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
data/README.adoc
ADDED
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
= ElkRb
|
|
2
|
+
|
|
3
|
+
image:https://img.shields.io/gem/v/elkrb.svg[RubyGems Version]
|
|
4
|
+
image:https://img.shields.io/github/license/metanorma/elkrb.svg[License]
|
|
5
|
+
image:https://github.com/metanorma/elkrb/actions/workflows/test.yml/badge.svg["Build", link="https://github.com/metanorma/elkrb/actions/workflows/test.yml"]
|
|
6
|
+
|
|
7
|
+
== Purpose
|
|
8
|
+
|
|
9
|
+
ElkRb is a pure Ruby implementation of the Eclipse Layout Kernel (ELK) for
|
|
10
|
+
automatic layout of node-link diagrams. It implements ELK's layout algorithms
|
|
11
|
+
to compute positions and routes for graph elements.
|
|
12
|
+
|
|
13
|
+
The library is designed for box-based diagrams including:
|
|
14
|
+
|
|
15
|
+
* UML class diagrams
|
|
16
|
+
* EXPRESS-G data models
|
|
17
|
+
* Mermaid diagrams
|
|
18
|
+
* Flowcharts and data flow diagrams
|
|
19
|
+
* Organization charts
|
|
20
|
+
* Network diagrams
|
|
21
|
+
|
|
22
|
+
ElkRb is built on top of the `lutaml-model` serialization framework, providing
|
|
23
|
+
flexible YAML and JSON serialization with ELK JSON format compatibility.
|
|
24
|
+
|
|
25
|
+
ElkRb aims to be fully compatible with:
|
|
26
|
+
|
|
27
|
+
* Java-based ELK (https://www.eclipse.org/elk/)
|
|
28
|
+
* JavaScript-based elkjs (https://github.com/kieler/elkjs) (verifiable
|
|
29
|
+
compatible in specs)
|
|
30
|
+
|
|
31
|
+
== Features
|
|
32
|
+
|
|
33
|
+
Supports all 15 ELK layout algorithms from Java implementation:
|
|
34
|
+
|
|
35
|
+
Layered:: (Sugiyama) hierarchical diagrams with directed flow
|
|
36
|
+
Force:: organic, symmetric layouts using force simulation
|
|
37
|
+
Stress:: quality-focused layout with stress minimization
|
|
38
|
+
Box:: simple grid-based arrangement
|
|
39
|
+
Fixed:: preserve existing positions
|
|
40
|
+
Random:: random node placement
|
|
41
|
+
MRTree:: multi-rooted tree layout
|
|
42
|
+
Radial:: circular/radial node arrangement
|
|
43
|
+
RectPacking:: efficient rectangle packing
|
|
44
|
+
TopdownPacking:: grid-based treemap layout
|
|
45
|
+
Libavoid:: "A*" pathfinding connector routing
|
|
46
|
+
VertiFlex:: vertical flexible column layout
|
|
47
|
+
DISCO:: disconnected component layout
|
|
48
|
+
SPOrE Overlap:: overlap removal optimization
|
|
49
|
+
SPOrE Compaction:: whitespace compaction
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Advanced edge routing
|
|
53
|
+
|
|
54
|
+
** ORTHOGONAL: 90-degree bends
|
|
55
|
+
** POLYLINE: Straight segments
|
|
56
|
+
** SPLINES: Smooth Bezier curves
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
Advanced port constraints
|
|
60
|
+
|
|
61
|
+
* Port side specification (NORTH, SOUTH, EAST, WEST)
|
|
62
|
+
* Automatic side detection
|
|
63
|
+
* Port ordering within sides
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Self-loop support
|
|
67
|
+
|
|
68
|
+
* Edges connecting nodes to themselves
|
|
69
|
+
* Multiple self-loops per node
|
|
70
|
+
* Configurable routing styles
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Export formats
|
|
74
|
+
|
|
75
|
+
* ELK JSON format
|
|
76
|
+
* YAML format (Ruby-specific)
|
|
77
|
+
* Graphviz DOT format
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
Advanced features
|
|
81
|
+
|
|
82
|
+
* Edge routing with bend points and port awareness
|
|
83
|
+
* Hierarchical graph support with recursive layout
|
|
84
|
+
* Automatic label placement for nodes, edges, and ports
|
|
85
|
+
* EdgeSection model with orthogonal routing
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
Port-based node connections
|
|
89
|
+
|
|
90
|
+
* Explicit port definitions
|
|
91
|
+
* Port-to-port edge routing
|
|
92
|
+
* Port side detection for labels
|
|
93
|
+
|
|
94
|
+
ELK JSON format compatibility
|
|
95
|
+
|
|
96
|
+
* Full elkjs v0.11.0 compatibility
|
|
97
|
+
* YAML/JSON serialization via Lutaml::Model
|
|
98
|
+
|
|
99
|
+
Comprehensive layout options
|
|
100
|
+
|
|
101
|
+
* Option parsers (ElkPadding, KVector, KVectorChain)
|
|
102
|
+
* Algorithm-specific options
|
|
103
|
+
* Layout categories and metadata
|
|
104
|
+
|
|
105
|
+
Command-line interface
|
|
106
|
+
|
|
107
|
+
* Layout command with format options
|
|
108
|
+
* Algorithm listing and query
|
|
109
|
+
|
|
110
|
+
== Architecture
|
|
111
|
+
|
|
112
|
+
.ElkRb layout process
|
|
113
|
+
[source]
|
|
114
|
+
----
|
|
115
|
+
Input Graph (YAML/JSON/Hash)
|
|
116
|
+
│
|
|
117
|
+
▼
|
|
118
|
+
┌─────────┐
|
|
119
|
+
│ Graph │ (Lutaml::Model)
|
|
120
|
+
│ Model │
|
|
121
|
+
└────┬────┘
|
|
122
|
+
│
|
|
123
|
+
▼
|
|
124
|
+
┌──────────────┐
|
|
125
|
+
│Layout Engine │
|
|
126
|
+
└──────┬───────┘
|
|
127
|
+
│
|
|
128
|
+
▼
|
|
129
|
+
┌──────────────────┐
|
|
130
|
+
│Algorithm Registry│
|
|
131
|
+
└──────┬───────────┘
|
|
132
|
+
│
|
|
133
|
+
▼
|
|
134
|
+
Algorithm
|
|
135
|
+
(Layered, Force, etc.)
|
|
136
|
+
│
|
|
137
|
+
▼
|
|
138
|
+
┌─────────┐
|
|
139
|
+
│ Graph │ (with computed positions)
|
|
140
|
+
│ Model │
|
|
141
|
+
└────┬────┘
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
Output (YAML/JSON/Hash)
|
|
145
|
+
----
|
|
146
|
+
|
|
147
|
+
.Graph model structure
|
|
148
|
+
[source]
|
|
149
|
+
----
|
|
150
|
+
Graph
|
|
151
|
+
│
|
|
152
|
+
┌────────┴────────┐
|
|
153
|
+
│ │
|
|
154
|
+
Nodes Edges
|
|
155
|
+
│ │
|
|
156
|
+
┌───────┴───────┐ │
|
|
157
|
+
│ │ │
|
|
158
|
+
Ports Labels EdgeSections
|
|
159
|
+
│ │ │
|
|
160
|
+
┌───┴───┐ ┌───┴───┐ │
|
|
161
|
+
│ │ │ │ │
|
|
162
|
+
Position Size Text Position BendPoints
|
|
163
|
+
----
|
|
164
|
+
|
|
165
|
+
== Installation
|
|
166
|
+
|
|
167
|
+
Add this line to your application's Gemfile:
|
|
168
|
+
|
|
169
|
+
[source,ruby]
|
|
170
|
+
----
|
|
171
|
+
gem "elkrb"
|
|
172
|
+
----
|
|
173
|
+
|
|
174
|
+
And then execute:
|
|
175
|
+
|
|
176
|
+
[source,sh]
|
|
177
|
+
----
|
|
178
|
+
$ bundle install
|
|
179
|
+
----
|
|
180
|
+
|
|
181
|
+
Or install it yourself as:
|
|
182
|
+
|
|
183
|
+
[source,sh]
|
|
184
|
+
----
|
|
185
|
+
$ gem install elkrb
|
|
186
|
+
----
|
|
187
|
+
|
|
188
|
+
== Usage
|
|
189
|
+
|
|
190
|
+
=== Basic graph layout
|
|
191
|
+
|
|
192
|
+
A layout can be performed by defining a graph structure as a Ruby hash and
|
|
193
|
+
passing it to the `Elkrb::LayoutEngine`.
|
|
194
|
+
|
|
195
|
+
[source,ruby]
|
|
196
|
+
----
|
|
197
|
+
require "elkrb"
|
|
198
|
+
|
|
199
|
+
# Define a simple graph
|
|
200
|
+
graph = {
|
|
201
|
+
id: "root",
|
|
202
|
+
layoutOptions: {
|
|
203
|
+
"elk.algorithm" => "layered"
|
|
204
|
+
},
|
|
205
|
+
children: [
|
|
206
|
+
{ id: "n1", width: 100, height: 60 },
|
|
207
|
+
{ id: "n2", width: 100, height: 60 },
|
|
208
|
+
{ id: "n3", width: 100, height: 60 }
|
|
209
|
+
],
|
|
210
|
+
edges: [
|
|
211
|
+
{ id: "e1", sources: ["n1"], targets: ["n2"] },
|
|
212
|
+
{ id: "e2", sources: ["n1"], targets: ["n3"] }
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# Layout the graph
|
|
217
|
+
engine = Elkrb::LayoutEngine.new
|
|
218
|
+
result = engine.layout(graph)
|
|
219
|
+
|
|
220
|
+
# Access computed positions
|
|
221
|
+
puts result[:children][0][:x] # => 0.0
|
|
222
|
+
puts result[:children][0][:y] # => 0.0
|
|
223
|
+
puts result[:children][1][:x] # => 150.0
|
|
224
|
+
----
|
|
225
|
+
|
|
226
|
+
A more structured approach uses the model classes to create the graph.
|
|
227
|
+
|
|
228
|
+
[source,ruby]
|
|
229
|
+
----
|
|
230
|
+
require "elkrb"
|
|
231
|
+
|
|
232
|
+
# Create graph using model classes
|
|
233
|
+
graph = Elkrb::Graph::Graph.new(id: "root")
|
|
234
|
+
|
|
235
|
+
# Add nodes
|
|
236
|
+
node1 = Elkrb::Graph::Node.new(
|
|
237
|
+
id: "n1",
|
|
238
|
+
width: 100,
|
|
239
|
+
height: 60
|
|
240
|
+
)
|
|
241
|
+
node2 = Elkrb::Graph::Node.new(
|
|
242
|
+
id: "n2",
|
|
243
|
+
width: 100,
|
|
244
|
+
height: 60
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
graph.children = [node1, node2]
|
|
248
|
+
|
|
249
|
+
# Add edge
|
|
250
|
+
edge = Elkrb::Graph::Edge.new(
|
|
251
|
+
id: "e1",
|
|
252
|
+
sources: ["n1"],
|
|
253
|
+
targets: ["n2"]
|
|
254
|
+
)
|
|
255
|
+
graph.edges = [edge]
|
|
256
|
+
|
|
257
|
+
# Set layout options
|
|
258
|
+
graph.layout_options = Elkrb::Graph::LayoutOptions.new(
|
|
259
|
+
algorithm: "layered",
|
|
260
|
+
direction: "DOWN"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Layout
|
|
264
|
+
engine = Elkrb::LayoutEngine.new
|
|
265
|
+
result = engine.layout(graph)
|
|
266
|
+
|
|
267
|
+
# Serialize result
|
|
268
|
+
puts result.to_yaml
|
|
269
|
+
puts result.to_json
|
|
270
|
+
----
|
|
271
|
+
|
|
272
|
+
=== UML Class diagram layout
|
|
273
|
+
|
|
274
|
+
[source,ruby]
|
|
275
|
+
----
|
|
276
|
+
require "elkrb"
|
|
277
|
+
|
|
278
|
+
# UML class diagram with ports for connections
|
|
279
|
+
graph = {
|
|
280
|
+
id: "uml_diagram",
|
|
281
|
+
layoutOptions: {
|
|
282
|
+
"elk.algorithm" => "layered",
|
|
283
|
+
"elk.direction" => "DOWN",
|
|
284
|
+
"elk.spacing.nodeNode" => 50
|
|
285
|
+
},
|
|
286
|
+
children: [
|
|
287
|
+
{
|
|
288
|
+
id: "Person",
|
|
289
|
+
width: 120,
|
|
290
|
+
height: 80,
|
|
291
|
+
labels: [
|
|
292
|
+
{ text: "Person", width: 100, height: 20 }
|
|
293
|
+
],
|
|
294
|
+
ports: [
|
|
295
|
+
{ id: "p1", x: 60, y: 0 }, # Top center
|
|
296
|
+
{ id: "p2", x: 60, y: 80 } # Bottom center
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "Employee",
|
|
301
|
+
width: 120,
|
|
302
|
+
height: 80,
|
|
303
|
+
labels: [
|
|
304
|
+
{ text: "Employee", width: 100, height: 20 }
|
|
305
|
+
],
|
|
306
|
+
ports: [
|
|
307
|
+
{ id: "p3", x: 60, y: 0 }
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "Customer",
|
|
312
|
+
width: 120,
|
|
313
|
+
height: 80,
|
|
314
|
+
labels: [
|
|
315
|
+
{ text: "Customer", width: 100, height: 20 }
|
|
316
|
+
],
|
|
317
|
+
ports: [
|
|
318
|
+
{ id: "p4", x: 60, y: 0 }
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
edges: [
|
|
323
|
+
{
|
|
324
|
+
id: "inheritance1",
|
|
325
|
+
sources: ["Employee"],
|
|
326
|
+
targets: ["Person"],
|
|
327
|
+
layoutOptions: {
|
|
328
|
+
"elk.edgeRouting" => "ORTHOGONAL"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: "inheritance2",
|
|
333
|
+
sources: ["Customer"],
|
|
334
|
+
targets: ["Person"],
|
|
335
|
+
layoutOptions: {
|
|
336
|
+
"elk.edgeRouting" => "ORTHOGONAL"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
engine = Elkrb::LayoutEngine.new
|
|
343
|
+
result = engine.layout(graph)
|
|
344
|
+
----
|
|
345
|
+
|
|
346
|
+
=== EXPRESS-G diagram layout
|
|
347
|
+
|
|
348
|
+
[source,ruby]
|
|
349
|
+
----
|
|
350
|
+
require "elkrb"
|
|
351
|
+
|
|
352
|
+
# EXPRESS-G entity-relationship diagram
|
|
353
|
+
graph = {
|
|
354
|
+
id: "express_g",
|
|
355
|
+
layoutOptions: {
|
|
356
|
+
"elk.algorithm" => "layered",
|
|
357
|
+
"elk.direction" => "RIGHT",
|
|
358
|
+
"elk.edgeRouting" => "ORTHOGONAL"
|
|
359
|
+
},
|
|
360
|
+
children: [
|
|
361
|
+
{
|
|
362
|
+
id: "Entity1",
|
|
363
|
+
width: 100,
|
|
364
|
+
height: 60,
|
|
365
|
+
labels: [{ text: "Entity", width: 80, height: 15 }]
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "Attribute1",
|
|
369
|
+
width: 80,
|
|
370
|
+
height: 40,
|
|
371
|
+
labels: [{ text: "name", width: 60, height: 15 }]
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "Attribute2",
|
|
375
|
+
width: 80,
|
|
376
|
+
height: 40,
|
|
377
|
+
labels: [{ text: "value", width: 60, height: 15 }]
|
|
378
|
+
}
|
|
379
|
+
],
|
|
380
|
+
edges: [
|
|
381
|
+
{ id: "e1", sources: ["Entity1"], targets: ["Attribute1"] },
|
|
382
|
+
{ id: "e2", sources: ["Entity1"], targets: ["Attribute2"] }
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
engine = Elkrb::LayoutEngine.new
|
|
387
|
+
result = engine.layout(graph)
|
|
388
|
+
----
|
|
389
|
+
|
|
390
|
+
=== Layout from YAML File
|
|
391
|
+
|
|
392
|
+
[source,ruby]
|
|
393
|
+
----
|
|
394
|
+
require "elkrb"
|
|
395
|
+
|
|
396
|
+
# Load graph from YAML
|
|
397
|
+
yaml_content = File.read("diagram.yml")
|
|
398
|
+
graph = Elkrb::Graph::Graph.from_yaml(yaml_content)
|
|
399
|
+
|
|
400
|
+
# Layout
|
|
401
|
+
engine = Elkrb::LayoutEngine.new
|
|
402
|
+
result = engine.layout(graph)
|
|
403
|
+
|
|
404
|
+
# Save result
|
|
405
|
+
File.write("diagram_laid_out.yml", result.to_yaml)
|
|
406
|
+
----
|
|
407
|
+
|
|
408
|
+
== Layout algorithms
|
|
409
|
+
|
|
410
|
+
=== Layered layout
|
|
411
|
+
|
|
412
|
+
The layered (Sugiyama) algorithm is ELK's flagship layout, ideal for
|
|
413
|
+
hierarchical diagrams with a natural flow direction.
|
|
414
|
+
|
|
415
|
+
==== Use cases
|
|
416
|
+
|
|
417
|
+
* UML class diagrams (inheritance hierarchies)
|
|
418
|
+
* Flowcharts and process diagrams
|
|
419
|
+
* Data flow diagrams
|
|
420
|
+
* Organization charts
|
|
421
|
+
* Dependency graphs
|
|
422
|
+
|
|
423
|
+
==== Key options
|
|
424
|
+
|
|
425
|
+
[source,ruby]
|
|
426
|
+
----
|
|
427
|
+
layoutOptions: {
|
|
428
|
+
"elk.algorithm" => "layered",
|
|
429
|
+
"elk.direction" => "DOWN", # DOWN, UP, LEFT, RIGHT
|
|
430
|
+
"elk.spacing.nodeNode" => 50,
|
|
431
|
+
"elk.layered.crossingMinimization.strategy" => "LAYER_SWEEP",
|
|
432
|
+
"elk.layered.nodePlacement.strategy" => "NETWORK_SIMPLEX",
|
|
433
|
+
"elk.edgeRouting" => "ORTHOGONAL" # ORTHOGONAL, POLYLINE, SPLINES
|
|
434
|
+
}
|
|
435
|
+
----
|
|
436
|
+
|
|
437
|
+
=== Force-Directed Layout
|
|
438
|
+
|
|
439
|
+
Creates organic, symmetric layouts using force simulation.
|
|
440
|
+
|
|
441
|
+
Use cases:
|
|
442
|
+
|
|
443
|
+
* Network diagrams
|
|
444
|
+
* Social graphs
|
|
445
|
+
* Mind maps
|
|
446
|
+
* General undirected graphs
|
|
447
|
+
|
|
448
|
+
Options:
|
|
449
|
+
|
|
450
|
+
[source,ruby]
|
|
451
|
+
----
|
|
452
|
+
layoutOptions: {
|
|
453
|
+
"elk.algorithm" => "force",
|
|
454
|
+
"elk.force.repulsion" => 5.0,
|
|
455
|
+
"elk.force.temperature" => 0.001
|
|
456
|
+
}
|
|
457
|
+
----
|
|
458
|
+
|
|
459
|
+
=== Stress Minimization
|
|
460
|
+
|
|
461
|
+
Quality-focused layout optimizing edge lengths and crossings.
|
|
462
|
+
|
|
463
|
+
Use cases:
|
|
464
|
+
|
|
465
|
+
* High-quality graph visualization
|
|
466
|
+
* Research diagrams
|
|
467
|
+
* Publication-ready layouts
|
|
468
|
+
|
|
469
|
+
Options:
|
|
470
|
+
|
|
471
|
+
[source,ruby]
|
|
472
|
+
----
|
|
473
|
+
layoutOptions: {
|
|
474
|
+
"elk.algorithm" => "stress"
|
|
475
|
+
}
|
|
476
|
+
----
|
|
477
|
+
|
|
478
|
+
=== Box Layout
|
|
479
|
+
|
|
480
|
+
Simple rectangular packing of nodes.
|
|
481
|
+
|
|
482
|
+
Use cases:
|
|
483
|
+
|
|
484
|
+
* Simple container layouts
|
|
485
|
+
* Grid-like arrangements
|
|
486
|
+
* Dashboards
|
|
487
|
+
|
|
488
|
+
Options:
|
|
489
|
+
|
|
490
|
+
[source,ruby]
|
|
491
|
+
----
|
|
492
|
+
layoutOptions: {
|
|
493
|
+
"elk.algorithm" => "box"
|
|
494
|
+
}
|
|
495
|
+
----
|
|
496
|
+
|
|
497
|
+
=== MRTree Layout
|
|
498
|
+
|
|
499
|
+
Multi-rooted tree layout for forest structures.
|
|
500
|
+
|
|
501
|
+
Use cases:
|
|
502
|
+
|
|
503
|
+
* Multiple inheritance hierarchies
|
|
504
|
+
* Forest data structures
|
|
505
|
+
* Parallel tree structures
|
|
506
|
+
* Multi-root taxonomies
|
|
507
|
+
|
|
508
|
+
Options:
|
|
509
|
+
|
|
510
|
+
[source,ruby]
|
|
511
|
+
----
|
|
512
|
+
layoutOptions: {
|
|
513
|
+
"elk.algorithm" => "mrtree",
|
|
514
|
+
"elk.direction" => "DOWN",
|
|
515
|
+
"elk.spacing.nodeNode" => 30
|
|
516
|
+
}
|
|
517
|
+
----
|
|
518
|
+
|
|
519
|
+
=== Radial Layout
|
|
520
|
+
|
|
521
|
+
Arranges nodes in a circular pattern around the center.
|
|
522
|
+
|
|
523
|
+
Use cases:
|
|
524
|
+
|
|
525
|
+
* Network diagrams
|
|
526
|
+
* Circular visualizations
|
|
527
|
+
* Hub-and-spoke diagrams
|
|
528
|
+
* Cyclic structures
|
|
529
|
+
|
|
530
|
+
Options:
|
|
531
|
+
|
|
532
|
+
[source,ruby]
|
|
533
|
+
----
|
|
534
|
+
layoutOptions: {
|
|
535
|
+
"elk.algorithm" => "radial",
|
|
536
|
+
"elk.spacing.nodeNode" => 50
|
|
537
|
+
}
|
|
538
|
+
----
|
|
539
|
+
|
|
540
|
+
=== RectPacking Layout
|
|
541
|
+
|
|
542
|
+
Efficient rectangle packing using shelf algorithms.
|
|
543
|
+
|
|
544
|
+
Use cases:
|
|
545
|
+
|
|
546
|
+
* Dashboards
|
|
547
|
+
* Tile layouts
|
|
548
|
+
* Space-efficient arrangements
|
|
549
|
+
* Component grids
|
|
550
|
+
|
|
551
|
+
Options:
|
|
552
|
+
|
|
553
|
+
[source,ruby]
|
|
554
|
+
----
|
|
555
|
+
layoutOptions: {
|
|
556
|
+
"elk.algorithm" => "rectpacking",
|
|
557
|
+
"elk.spacing.nodeNode" => 15
|
|
558
|
+
}
|
|
559
|
+
----
|
|
560
|
+
|
|
561
|
+
=== DISCO Layout
|
|
562
|
+
|
|
563
|
+
Disconnected component layout - identifies and arranges separate graph components.
|
|
564
|
+
|
|
565
|
+
Use cases:
|
|
566
|
+
|
|
567
|
+
* Graphs with multiple disconnected parts
|
|
568
|
+
* Component-based visualizations
|
|
569
|
+
* Separate module layouts
|
|
570
|
+
* Multi-cluster diagrams
|
|
571
|
+
|
|
572
|
+
Options:
|
|
573
|
+
|
|
574
|
+
[source,ruby]
|
|
575
|
+
----
|
|
576
|
+
layoutOptions: {
|
|
577
|
+
"elk.algorithm" => "disco",
|
|
578
|
+
"disco.componentCompaction.strategy" => "NONE", # NONE, ROW, COLUMN, GRID
|
|
579
|
+
"disco.componentCompaction.componentLayoutAlgorithm" => "layered",
|
|
580
|
+
"disco.spacing.componentComponent" => 30
|
|
581
|
+
}
|
|
582
|
+
----
|
|
583
|
+
|
|
584
|
+
=== SPOrE Overlap Removal
|
|
585
|
+
|
|
586
|
+
Removes overlaps between nodes while preserving the overall structure.
|
|
587
|
+
|
|
588
|
+
Use cases:
|
|
589
|
+
|
|
590
|
+
* Post-processing for force-directed layouts
|
|
591
|
+
* Cleaning up manually positioned diagrams
|
|
592
|
+
* Overlap resolution
|
|
593
|
+
* Layout refinement
|
|
594
|
+
|
|
595
|
+
Options:
|
|
596
|
+
|
|
597
|
+
[source,ruby]
|
|
598
|
+
----
|
|
599
|
+
layoutOptions: {
|
|
600
|
+
"elk.algorithm" => "sporeOverlap",
|
|
601
|
+
"elk.spacing.nodeNode" => 10,
|
|
602
|
+
"sporeOverlap.maxIterations" => 100
|
|
603
|
+
}
|
|
604
|
+
----
|
|
605
|
+
|
|
606
|
+
=== SPOrE Compaction
|
|
607
|
+
|
|
608
|
+
Compacts layouts by removing whitespace while maintaining structure.
|
|
609
|
+
|
|
610
|
+
Use cases:
|
|
611
|
+
|
|
612
|
+
* Minimizing diagram size
|
|
613
|
+
* Reducing whitespace
|
|
614
|
+
* Creating compact layouts
|
|
615
|
+
* Space optimization
|
|
616
|
+
|
|
617
|
+
Options:
|
|
618
|
+
|
|
619
|
+
[source,ruby]
|
|
620
|
+
----
|
|
621
|
+
layoutOptions: {
|
|
622
|
+
"elk.algorithm" => "sporeCompaction",
|
|
623
|
+
"sporeCompaction.compactionStrategy" => "BOTH", # HORIZONTAL, VERTICAL, BOTH
|
|
624
|
+
"elk.spacing.nodeNode" => 10,
|
|
625
|
+
"sporeCompaction.normalize" => true
|
|
626
|
+
}
|
|
627
|
+
----
|
|
628
|
+
|
|
629
|
+
== Advanced features
|
|
630
|
+
|
|
631
|
+
=== Edge routing
|
|
632
|
+
|
|
633
|
+
ElkRb supports advanced edge routing with bend points and port awareness.
|
|
634
|
+
|
|
635
|
+
[source,ruby]
|
|
636
|
+
----
|
|
637
|
+
# Enable edge routing with orthogonal bend points
|
|
638
|
+
graph = {
|
|
639
|
+
layoutOptions: {
|
|
640
|
+
"elk.algorithm" => "layered",
|
|
641
|
+
"elk.edgeRouting" => "ORTHOGONAL"
|
|
642
|
+
},
|
|
643
|
+
# ... nodes and edges
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
result = Elkrb.layout(graph)
|
|
647
|
+
|
|
648
|
+
# Access edge sections with bend points
|
|
649
|
+
edge_section = result[:edges][0][:sections][0]
|
|
650
|
+
puts edge_section[:startPoint] # { x: 50.0, y: 60.0 }
|
|
651
|
+
puts edge_section[:endPoint] # { x: 150.0, y: 60.0 }
|
|
652
|
+
puts edge_section[:bendPoints] # [{ x: 100.0, y: 30.0 }, ...]
|
|
653
|
+
----
|
|
654
|
+
|
|
655
|
+
=== Hierarchical graphs
|
|
656
|
+
|
|
657
|
+
Nested node structures are automatically handled with recursive layout.
|
|
658
|
+
|
|
659
|
+
[source,ruby]
|
|
660
|
+
----
|
|
661
|
+
graph = {
|
|
662
|
+
layoutOptions: {
|
|
663
|
+
"elk.algorithm" => "layered",
|
|
664
|
+
"hierarchical" => true
|
|
665
|
+
},
|
|
666
|
+
children: [
|
|
667
|
+
{
|
|
668
|
+
id: "parent",
|
|
669
|
+
width: 300,
|
|
670
|
+
height: 200,
|
|
671
|
+
children: [
|
|
672
|
+
{ id: "child1", width: 80, height: 60 },
|
|
673
|
+
{ id: "child2", width: 80, height: 60 }
|
|
674
|
+
],
|
|
675
|
+
edges: [
|
|
676
|
+
{ sources: ["child1"], targets: ["child2"] }
|
|
677
|
+
]
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
result = Elkrb.layout(graph)
|
|
683
|
+
# Parent bounds automatically calculated to contain children
|
|
684
|
+
# Child nodes recursively laid out within parent
|
|
685
|
+
----
|
|
686
|
+
|
|
687
|
+
=== Label placement
|
|
688
|
+
|
|
689
|
+
Automatic label positioning for nodes, edges, and ports.
|
|
690
|
+
|
|
691
|
+
[source,ruby]
|
|
692
|
+
----
|
|
693
|
+
graph = {
|
|
694
|
+
children: [
|
|
695
|
+
{
|
|
696
|
+
id: "n1",
|
|
697
|
+
width: 100,
|
|
698
|
+
height: 60,
|
|
699
|
+
labels: [
|
|
700
|
+
{ text: "Node Label", width: 80, height: 20 }
|
|
701
|
+
],
|
|
702
|
+
layoutOptions: {
|
|
703
|
+
"node.label.placement" => "INSIDE CENTER" # or OUTSIDE TOP, etc.
|
|
704
|
+
},
|
|
705
|
+
ports: [
|
|
706
|
+
{
|
|
707
|
+
id: "p1",
|
|
708
|
+
x: 0,
|
|
709
|
+
y: 30,
|
|
710
|
+
labels: [
|
|
711
|
+
{ text: "Port", width: 30, height: 15 }
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
]
|
|
715
|
+
}
|
|
716
|
+
],
|
|
717
|
+
edges: [
|
|
718
|
+
{
|
|
719
|
+
sources: ["n1"],
|
|
720
|
+
targets: ["n2"],
|
|
721
|
+
labels: [
|
|
722
|
+
{ text: "Edge Label", width: 60, height: 15 }
|
|
723
|
+
]
|
|
724
|
+
}
|
|
725
|
+
]
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
result = Elkrb.layout(graph)
|
|
729
|
+
# Labels automatically positioned based on placement options
|
|
730
|
+
# Port labels placed according to port side detection
|
|
731
|
+
# Edge labels placed at center of edge path
|
|
732
|
+
----
|
|
733
|
+
|
|
734
|
+
Label placement options:
|
|
735
|
+
|
|
736
|
+
* `node.label.placement` - INSIDE/OUTSIDE + TOP/BOTTOM/LEFT/RIGHT/CENTER
|
|
737
|
+
* `port.label.placement` - Same format as node labels
|
|
738
|
+
* `label.padding` - Internal spacing within labels
|
|
739
|
+
* `label.margin` - External spacing around labels
|
|
740
|
+
* `label.placement.disabled` - Disable automatic placement
|
|
741
|
+
|
|
742
|
+
== Advanced features
|
|
743
|
+
|
|
744
|
+
=== Spline Edge Routing
|
|
745
|
+
|
|
746
|
+
Use smooth Bezier curves for aesthetically pleasing edge routing:
|
|
747
|
+
|
|
748
|
+
[source,ruby]
|
|
749
|
+
----
|
|
750
|
+
graph.layout_options = Elkrb::Graph::LayoutOptions.new(
|
|
751
|
+
algorithm: "layered",
|
|
752
|
+
edge_routing: "SPLINES",
|
|
753
|
+
spline_curvature: 0.5
|
|
754
|
+
)
|
|
755
|
+
----
|
|
756
|
+
|
|
757
|
+
// See link:SPLINE_ROUTING_GUIDE.md[Spline Routing Guide] for details.
|
|
758
|
+
|
|
759
|
+
=== Advanced Port Constraints
|
|
760
|
+
|
|
761
|
+
Control port placement and ordering on nodes:
|
|
762
|
+
|
|
763
|
+
[source,ruby]
|
|
764
|
+
----
|
|
765
|
+
port = Elkrb::Graph::Port.new(
|
|
766
|
+
id: "p1",
|
|
767
|
+
side: "WEST",
|
|
768
|
+
index: 0
|
|
769
|
+
)
|
|
770
|
+
----
|
|
771
|
+
|
|
772
|
+
// See link:PORT_CONSTRAINTS_GUIDE.md[Port Constraints Guide] for details.
|
|
773
|
+
|
|
774
|
+
=== Self-loop Support
|
|
775
|
+
|
|
776
|
+
Create edges connecting nodes to themselves:
|
|
777
|
+
|
|
778
|
+
[source,ruby]
|
|
779
|
+
----
|
|
780
|
+
edge = Elkrb::Graph::Edge.new(
|
|
781
|
+
id: "loop",
|
|
782
|
+
sources: ["n1"],
|
|
783
|
+
targets: ["n1"]
|
|
784
|
+
)
|
|
785
|
+
----
|
|
786
|
+
|
|
787
|
+
// See link:SELF_LOOP_GUIDE.md[Self-loop Guide] for details.
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
=== Graphviz DOT Export
|
|
791
|
+
|
|
792
|
+
NOTE: DOT export requires Graphviz installed on your system. ElkRb does not
|
|
793
|
+
include Graphviz itself, only the DOT serialization functionality.
|
|
794
|
+
|
|
795
|
+
Export layouts to DOT format:
|
|
796
|
+
|
|
797
|
+
[source,ruby]
|
|
798
|
+
----
|
|
799
|
+
result = Elkrb.layout(graph)
|
|
800
|
+
dot_string = Elkrb.export_dot(result)
|
|
801
|
+
File.write("output.dot", dot_string)
|
|
802
|
+
----
|
|
803
|
+
|
|
804
|
+
// See link:DOT_SERIALIZER_GUIDE.md[DOT Export Guide] for details.
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
== Performance
|
|
808
|
+
|
|
809
|
+
ElkRb provides production-ready performance for most use cases.
|
|
810
|
+
|
|
811
|
+
TODO: See link:docs/PERFORMANCE.adoc[Performance Benchmarks] for detailed
|
|
812
|
+
comparisons with elkjs and Java ELK.
|
|
813
|
+
|
|
814
|
+
Quick overview of algorithm performance:
|
|
815
|
+
|
|
816
|
+
* Fast algorithms (< 10ms): Box, Radial, VertiFlex
|
|
817
|
+
* Medium algorithms (10-50ms): Layered, Stress, MRTree
|
|
818
|
+
* Complex algorithms (50-500ms): Force, RectPacking, Libavoid
|
|
819
|
+
|
|
820
|
+
Suitable for:
|
|
821
|
+
|
|
822
|
+
* Real-time layout of small to medium graphs (< 100 nodes)
|
|
823
|
+
* Batch processing of large graphs
|
|
824
|
+
* Interactive diagram editors
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
== Migration from elkjs
|
|
828
|
+
|
|
829
|
+
If you're migrating from elkjs, see
|
|
830
|
+
link:docs/MIGRATION_FROM_ELKJS.adoc[Migration Guide] for a smooth transition.
|
|
831
|
+
|
|
832
|
+
Key differences:
|
|
833
|
+
|
|
834
|
+
* Ruby API instead of JavaScript Promise-based API
|
|
835
|
+
* Additional algorithms (TopdownPacking, Libavoid, VertiFlex)
|
|
836
|
+
* YAML serialization support
|
|
837
|
+
|
|
838
|
+
== Data Model
|
|
839
|
+
|
|
840
|
+
=== Graph
|
|
841
|
+
|
|
842
|
+
The root container for nodes and edges.
|
|
843
|
+
|
|
844
|
+
`id`:: Unique identifier
|
|
845
|
+
`x, y`:: Position coordinates
|
|
846
|
+
`width, height`:: Dimensions
|
|
847
|
+
`children`:: Array of Node objects
|
|
848
|
+
`edges`:: Array of Edge objects
|
|
849
|
+
`layout_options`:: LayoutOptions object
|
|
850
|
+
`properties`:: Custom properties hash
|
|
851
|
+
|
|
852
|
+
=== Node
|
|
853
|
+
|
|
854
|
+
Represents a graph node (box, shape, entity).
|
|
855
|
+
|
|
856
|
+
`id`:: Unique identifier
|
|
857
|
+
`x, y`:: Position coordinates
|
|
858
|
+
`width, height`:: Required dimensions
|
|
859
|
+
`labels`:: Array of Label objects
|
|
860
|
+
`ports`:: Array of Port objects
|
|
861
|
+
`children`:: Nested nodes (for hierarchical graphs)
|
|
862
|
+
`edges`:: Local edges
|
|
863
|
+
`layout_options`:: Node-specific layout options
|
|
864
|
+
`properties`:: Custom properties
|
|
865
|
+
|
|
866
|
+
=== Edge
|
|
867
|
+
|
|
868
|
+
Represents connections between nodes.
|
|
869
|
+
|
|
870
|
+
`id`:: Unique identifier
|
|
871
|
+
`sources`:: Array of source node/port IDs
|
|
872
|
+
`targets`:: Array of target node/port IDs
|
|
873
|
+
`labels`:: Array of Label objects
|
|
874
|
+
`sections`:: Computed routing (EdgeSection objects)
|
|
875
|
+
`layout_options`:: Edge-specific layout options
|
|
876
|
+
`properties`:: Custom properties
|
|
877
|
+
|
|
878
|
+
=== Port
|
|
879
|
+
|
|
880
|
+
Explicit attachment points on node borders.
|
|
881
|
+
|
|
882
|
+
`id`:: Unique identifier
|
|
883
|
+
`x, y`:: Position relative to node
|
|
884
|
+
`width, height`:: Port dimensions
|
|
885
|
+
`labels`:: Array of Label objects
|
|
886
|
+
`layout_options`:: Port-specific layout options
|
|
887
|
+
|
|
888
|
+
=== Label
|
|
889
|
+
|
|
890
|
+
Text labels for nodes, edges, or ports.
|
|
891
|
+
|
|
892
|
+
`id`:: Unique identifier
|
|
893
|
+
`text`:: Label text
|
|
894
|
+
`x, y`:: Position coordinates
|
|
895
|
+
`width, height`:: Label dimensions
|
|
896
|
+
|
|
897
|
+
=== LayoutOptions
|
|
898
|
+
|
|
899
|
+
Configuration for layout algorithms.
|
|
900
|
+
|
|
901
|
+
`algorithm`:: Algorithm name (layered, force, stress, etc.)
|
|
902
|
+
`direction`:: Layout direction (DOWN, UP, LEFT, RIGHT)
|
|
903
|
+
`spacing_node_node`:: Node-to-node spacing
|
|
904
|
+
`spacing_edge_node`:: Edge-to-node spacing
|
|
905
|
+
`edge_routing`:: Edge routing style (ORTHOGONAL, POLYLINE, SPLINES)
|
|
906
|
+
`properties`:: Additional algorithm-specific options
|
|
907
|
+
|
|
908
|
+
== Command-Line Interface
|
|
909
|
+
|
|
910
|
+
The `elkrb` command provides utilities for layout operations.
|
|
911
|
+
|
|
912
|
+
=== Layout Command
|
|
913
|
+
|
|
914
|
+
[source,sh]
|
|
915
|
+
----
|
|
916
|
+
# Layout a graph from file
|
|
917
|
+
$ elkrb layout input.yml --output result.yml
|
|
918
|
+
|
|
919
|
+
# Specify algorithm
|
|
920
|
+
$ elkrb layout input.yml --algorithm layered --output result.yml
|
|
921
|
+
|
|
922
|
+
# Output JSON
|
|
923
|
+
$ elkrb layout input.yml --format json --output result.json
|
|
924
|
+
----
|
|
925
|
+
|
|
926
|
+
=== Available Algorithms
|
|
927
|
+
|
|
928
|
+
[source,sh]
|
|
929
|
+
----
|
|
930
|
+
$ elkrb algorithms
|
|
931
|
+
|
|
932
|
+
Available Layout Algorithms:
|
|
933
|
+
|
|
934
|
+
box
|
|
935
|
+
Name: Box
|
|
936
|
+
Description: Arranges nodes in a grid pattern
|
|
937
|
+
|
|
938
|
+
fixed
|
|
939
|
+
Name: Fixed
|
|
940
|
+
Description: Keeps nodes at their current positions
|
|
941
|
+
|
|
942
|
+
force
|
|
943
|
+
Name: Force-Directed
|
|
944
|
+
Description: Physics-based layout using attractive and repulsive forces
|
|
945
|
+
Category: force
|
|
946
|
+
|
|
947
|
+
layered
|
|
948
|
+
Name: Layered (Sugiyama)
|
|
949
|
+
Description: Hierarchical layout using the Sugiyama framework
|
|
950
|
+
Category: hierarchical
|
|
951
|
+
Supports Hierarchy: Yes
|
|
952
|
+
|
|
953
|
+
random
|
|
954
|
+
Name: Random
|
|
955
|
+
Description: Places nodes at random positions
|
|
956
|
+
|
|
957
|
+
stress
|
|
958
|
+
Name: Stress Minimization
|
|
959
|
+
Description: High-quality layout using stress majorization
|
|
960
|
+
Category: force
|
|
961
|
+
----
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
== Benchmarking
|
|
965
|
+
|
|
966
|
+
ElkRb provides production-ready performance.
|
|
967
|
+
|
|
968
|
+
ElkRb can be run in a benchmark mode to compare its performance against
|
|
969
|
+
elkjs and Java ELK (TODO).
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
[source,sh]
|
|
973
|
+
----
|
|
974
|
+
# Run all ElkRb benchmarks and generate report
|
|
975
|
+
$ rake benchmark:all
|
|
976
|
+
|
|
977
|
+
# Generate test graphs
|
|
978
|
+
$ rake benchmark:generate_graphs
|
|
979
|
+
|
|
980
|
+
# Run ElkRb benchmarks only
|
|
981
|
+
$ rake benchmark:elkrb
|
|
982
|
+
|
|
983
|
+
# Run elkjs benchmarks (requires Node.js and elkjs)
|
|
984
|
+
$ rake benchmark:elkjs
|
|
985
|
+
|
|
986
|
+
# Generate performance report
|
|
987
|
+
$ rake benchmark:report
|
|
988
|
+
----
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
== Development
|
|
992
|
+
|
|
993
|
+
=== Running tests
|
|
994
|
+
|
|
995
|
+
[source,sh]
|
|
996
|
+
----
|
|
997
|
+
$ bundle exec rake spec
|
|
998
|
+
----
|
|
999
|
+
|
|
1000
|
+
=== Code Style
|
|
1001
|
+
|
|
1002
|
+
[source,sh]
|
|
1003
|
+
----
|
|
1004
|
+
$ bundle exec rubocop
|
|
1005
|
+
$ bundle exec rubocop -A # Auto-correct
|
|
1006
|
+
----
|
|
1007
|
+
|
|
1008
|
+
== References
|
|
1009
|
+
|
|
1010
|
+
Other libraries that implement ELK:
|
|
1011
|
+
|
|
1012
|
+
* https://www.eclipse.org/elk/[Eclipse Layout Kernel]
|
|
1013
|
+
* https://github.com/kieler/elkjs[elkjs - JavaScript port]
|
|
1014
|
+
|
|
1015
|
+
ELK Documentation:
|
|
1016
|
+
|
|
1017
|
+
* https://www.eclipse.org/elk/reference.html[ELK Layout Options Reference]
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
== Copyright
|
|
1021
|
+
|
|
1022
|
+
Copyright https://www.ribose.com[Ribose Inc.]
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
== License
|
|
1026
|
+
|
|
1027
|
+
The gem is available as open source under the terms of the
|
|
1028
|
+
https://opensource.org/licenses/BSD-2-Clause[2-Clause BSD License].
|