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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +364 -0
  3. data/LICENSE +25 -0
  4. data/README.adoc +695 -557
  5. data/exe/elkrb +36 -2
  6. data/lib/elkrb/atomic_destination.rb +53 -0
  7. data/lib/elkrb/best_effort_write.rb +49 -0
  8. data/lib/elkrb/cli/layout_flag_options.rb +54 -0
  9. data/lib/elkrb/cli.rb +155 -82
  10. data/lib/elkrb/command_resolver.rb +186 -0
  11. data/lib/elkrb/commands/batch_command.rb +28 -5
  12. data/lib/elkrb/commands/convert_command.rb +22 -52
  13. data/lib/elkrb/commands/diagram_command.rb +234 -102
  14. data/lib/elkrb/commands/options_command.rb +87 -0
  15. data/lib/elkrb/commands/render_command.rb +6 -1
  16. data/lib/elkrb/commands/validate_command.rb +146 -91
  17. data/lib/elkrb/errors.rb +20 -1
  18. data/lib/elkrb/format_sniffer.rb +362 -0
  19. data/lib/elkrb/geometry/bezier.rb +14 -4
  20. data/lib/elkrb/geometry/point.rb +5 -6
  21. data/lib/elkrb/geometry/rectangle.rb +2 -2
  22. data/lib/elkrb/graph/deep_stringify_keys.rb +30 -0
  23. data/lib/elkrb/graph/edge.rb +89 -3
  24. data/lib/elkrb/graph/graph.rb +43 -15
  25. data/lib/elkrb/graph/label.rb +16 -2
  26. data/lib/elkrb/graph/node.rb +13 -2
  27. data/lib/elkrb/graph/node_constraints.rb +139 -8
  28. data/lib/elkrb/graph/normalize_option_keys.rb +39 -0
  29. data/lib/elkrb/graph/port.rb +29 -21
  30. data/lib/elkrb/graph/read_only_mapping.rb +21 -0
  31. data/lib/elkrb/graphviz_wrapper.rb +152 -44
  32. data/lib/elkrb/layout/algorithm_registry.rb +65 -8
  33. data/lib/elkrb/layout/algorithms/base_algorithm.rb +74 -74
  34. data/lib/elkrb/layout/algorithms/box.rb +47 -20
  35. data/lib/elkrb/layout/algorithms/disco.rb +150 -61
  36. data/lib/elkrb/layout/algorithms/fixed.rb +70 -10
  37. data/lib/elkrb/layout/algorithms/force.rb +145 -86
  38. data/lib/elkrb/layout/algorithms/layered/bk/aligner.rb +104 -0
  39. data/lib/elkrb/layout/algorithms/layered/bk/compactor.rb +87 -0
  40. data/lib/elkrb/layout/algorithms/layered/bk/view.rb +103 -0
  41. data/lib/elkrb/layout/algorithms/layered/bk_node_placer.rb +170 -0
  42. data/lib/elkrb/layout/algorithms/layered/components.rb +170 -0
  43. data/lib/elkrb/layout/algorithms/layered/crossing_minimizer.rb +306 -0
  44. data/lib/elkrb/layout/algorithms/layered/cycle_breaker.rb +118 -49
  45. data/lib/elkrb/layout/algorithms/layered/discovery_order.rb +75 -0
  46. data/lib/elkrb/layout/algorithms/layered/dummy_slot.rb +31 -0
  47. data/lib/elkrb/layout/algorithms/layered/layer_assigner.rb +188 -48
  48. data/lib/elkrb/layout/algorithms/layered/layer_constraints.rb +82 -0
  49. data/lib/elkrb/layout/algorithms/layered/layer_sweep.rb +195 -0
  50. data/lib/elkrb/layout/algorithms/layered/node_placer.rb +214 -36
  51. data/lib/elkrb/layout/algorithms/layered/orthogonal/gap_solver.rb +219 -0
  52. data/lib/elkrb/layout/algorithms/layered/orthogonal/hyper_edge_segment.rb +152 -0
  53. data/lib/elkrb/layout/algorithms/layered/orthogonal/junction_points.rb +58 -0
  54. data/lib/elkrb/layout/algorithms/layered/orthogonal/segment_order.rb +134 -0
  55. data/lib/elkrb/layout/algorithms/layered/orthogonal_router.rb +253 -0
  56. data/lib/elkrb/layout/algorithms/layered/port_order.rb +188 -0
  57. data/lib/elkrb/layout/algorithms/layered/port_spread.rb +42 -0
  58. data/lib/elkrb/layout/algorithms/layered.rb +548 -13
  59. data/lib/elkrb/layout/algorithms/libavoid.rb +951 -187
  60. data/lib/elkrb/layout/algorithms/mrtree.rb +585 -65
  61. data/lib/elkrb/layout/algorithms/radial.rb +188 -29
  62. data/lib/elkrb/layout/algorithms/random.rb +131 -17
  63. data/lib/elkrb/layout/algorithms/rectpacking.rb +28 -40
  64. data/lib/elkrb/layout/algorithms/spore_compaction.rb +57 -100
  65. data/lib/elkrb/layout/algorithms/spore_overlap.rb +78 -84
  66. data/lib/elkrb/layout/algorithms/stress.rb +391 -84
  67. data/lib/elkrb/layout/algorithms/topdown_packing.rb +95 -31
  68. data/lib/elkrb/layout/algorithms/vertiflex.rb +12 -47
  69. data/lib/elkrb/layout/constraints/alignment_constraint.rb +49 -12
  70. data/lib/elkrb/layout/constraints/base_constraint.rb +3 -15
  71. data/lib/elkrb/layout/constraints/constraint_processor.rb +9 -23
  72. data/lib/elkrb/layout/constraints/fixed_position_constraint.rb +22 -13
  73. data/lib/elkrb/layout/constraints/layer_constraint.rb +10 -33
  74. data/lib/elkrb/layout/constraints/relative_position_constraint.rb +94 -50
  75. data/lib/elkrb/layout/edge_router.rb +533 -337
  76. data/lib/elkrb/layout/hierarchical_processor.rb +19 -276
  77. data/lib/elkrb/layout/java_random.rb +59 -0
  78. data/lib/elkrb/layout/label_placer.rb +165 -157
  79. data/lib/elkrb/layout/layout_engine.rb +46 -23
  80. data/lib/elkrb/layout/node_index.rb +149 -0
  81. data/lib/elkrb/layout/polyomino/component_compactor.rb +138 -0
  82. data/lib/elkrb/layout/polyomino/footprint.rb +94 -0
  83. data/lib/elkrb/layout/polyomino/grid.rb +106 -0
  84. data/lib/elkrb/layout/polyomino/packer.rb +95 -0
  85. data/lib/elkrb/layout/polyomino/piece.rb +52 -0
  86. data/lib/elkrb/layout/port_constraint_processor.rb +131 -44
  87. data/lib/elkrb/layout/spore/body.rb +65 -0
  88. data/lib/elkrb/layout/spore/depth_first_compaction.rb +127 -0
  89. data/lib/elkrb/layout/spore/edge_cost.rb +69 -0
  90. data/lib/elkrb/layout/spore/gap.rb +76 -0
  91. data/lib/elkrb/layout/spore/geometry.rb +104 -0
  92. data/lib/elkrb/layout/spore/overlap_sweep.rb +90 -0
  93. data/lib/elkrb/layout/spore/placement.rb +57 -0
  94. data/lib/elkrb/layout/spore/rays.rb +119 -0
  95. data/lib/elkrb/layout/spore/spanning_tree.rb +53 -0
  96. data/lib/elkrb/layout/spore/triangulation.rb +159 -0
  97. data/lib/elkrb/layout_flags.rb +69 -0
  98. data/lib/elkrb/options/decimal.rb +50 -0
  99. data/lib/elkrb/options/element_options.rb +43 -0
  100. data/lib/elkrb/options/elk_padding.rb +20 -14
  101. data/lib/elkrb/options/k_vector.rb +6 -7
  102. data/lib/elkrb/options/k_vector_chain.rb +12 -20
  103. data/lib/elkrb/options/option_map.rb +85 -0
  104. data/lib/elkrb/options/registry.rb +335 -0
  105. data/lib/elkrb/options/resolver.rb +151 -0
  106. data/lib/elkrb/options/spellings.rb +62 -0
  107. data/lib/elkrb/options/unhonoured_report.rb +194 -0
  108. data/lib/elkrb/parsers/elkt/lexer.rb +309 -0
  109. data/lib/elkrb/parsers/elkt/parser.rb +424 -0
  110. data/lib/elkrb/parsers/elkt/resolver.rb +142 -0
  111. data/lib/elkrb/parsers/elkt/token.rb +40 -0
  112. data/lib/elkrb/parsers/elkt_parser.rb +32 -230
  113. data/lib/elkrb/serializers/dot_serializer.rb +342 -54
  114. data/lib/elkrb/serializers/elkt_serializer.rb +386 -155
  115. data/lib/elkrb/version.rb +1 -1
  116. data/lib/elkrb.rb +60 -61
  117. metadata +71 -49
  118. data/.rspec +0 -3
  119. data/.rubocop.yml +0 -11
  120. data/Gemfile +0 -13
  121. data/Rakefile +0 -64
  122. data/benchmarks/README.md +0 -172
  123. data/benchmarks/elkjs_benchmark.js +0 -140
  124. data/benchmarks/elkrb_benchmark.rb +0 -145
  125. data/benchmarks/fixtures/graphs.json +0 -10777
  126. data/benchmarks/generate_report.rb +0 -241
  127. data/benchmarks/generate_test_graphs.rb +0 -154
  128. data/benchmarks/results/elkrb_results.json +0 -280
  129. data/benchmarks/results/elkrb_summary.json +0 -285
  130. data/elkrb.gemspec +0 -39
  131. data/examples/dot_export_demo.rb +0 -133
  132. data/examples/hierarchical_graph.rb +0 -19
  133. data/examples/layout_constraints_demo.rb +0 -272
  134. data/examples/port_constraints_demo.rb +0 -291
  135. data/examples/self_loop_demo.rb +0 -391
  136. data/examples/simple_graph.rb +0 -50
  137. data/examples/spline_routing_demo.rb +0 -235
  138. data/lib/elkrb/graph/layout_options.rb +0 -247
  139. data/sig/elkrb/constraints.rbs +0 -114
  140. data/sig/elkrb/geometry.rbs +0 -61
  141. data/sig/elkrb/graph.rbs +0 -112
  142. data/sig/elkrb/layout.rbs +0 -107
  143. data/sig/elkrb/options.rbs +0 -81
  144. data/sig/elkrb.rbs +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19c43adfbca7a5e7f9680b6363c6a350f9b4f9c2933229c48437398c0d665ebd
4
- data.tar.gz: a33dbcef5f165982417f9caffd84137ba21f02594634ba27260593ea46676247
3
+ metadata.gz: 8699f197aff817a1de6a193b1fb421d59e5df023ed0bbb39f7cfa75cedefcf82
4
+ data.tar.gz: cfa580618758f94d81542c86cf4bb87014375057c3271ee00031b962f2faa598
5
5
  SHA512:
6
- metadata.gz: 5b7491f8cd40a33fd457a46aef7947501a47cbe3bad2289ecfdd815ac28685216c526d2ed2a5113eac393a167d5399981ba1e19da2b6640c5e6f494a324dd1d4
7
- data.tar.gz: 2814564220196fe977b08bc607f3dc5d34b97bb260e7103935de85f7cc90e574ca417ddcb08a417040b47eb14d500c1e53428ab601e2361bb2a4156537cff24d
6
+ metadata.gz: 19b997af148da717eae04d0c30274276a654999f8d3bac9254bf3e7098f9f0fedfaba00744961e734f54f8dfa0db009620be9c3cd7d7891b5886400af1c4ee6c
7
+ data.tar.gz: c7ec92a9b09af0c7797fac7c567b869a0fe0faeacdcae1ad5d7c2312c0051c5f91272f22beba34789b0856ac3bb656c3ceeb10eebd08831855de9e0fb90e113f
data/CHANGELOG.adoc ADDED
@@ -0,0 +1,364 @@
1
+ = Changelog
2
+
3
+ All notable changes to Elkrb are recorded here.
4
+
5
+ == 2.0.0 (2026-10-10)
6
+
7
+ === Breaking changes
8
+
9
+ * `NodeConstraints` now writes YAML in snake_case, like every other model in
10
+ the gem. `fixedPosition`, `alignGroup`, `alignDirection`, `relativeTo`,
11
+ `relativeOffset` and `positionPriority` are still read, so old documents keep
12
+ parsing, but they are no longer produced. Code that reads the camelCase keys
13
+ out of generated YAML must be updated. JSON and the other key-value formats
14
+ are unchanged and stay camelCase.
15
+
16
+ * `Elkrb.layout` and `Elkrb::Layout::LayoutEngine.layout` now raise
17
+ `ArgumentError` when the graph is neither a `Hash` nor an
18
+ `Elkrb::Graph::Graph`. Before, other objects were accepted and failed later
19
+ with a `NoMethodError`, or silently did nothing. Anything passing a wrapper
20
+ or a duck-typed graph has to pass a real `Graph` now.
21
+
22
+ * Ruby 3.3 or newer is required. Older Rubies cannot load `lutaml-model` 0.8.
23
+
24
+ * `Elkrb::Graph::LayoutOptions` and its typed getters are gone. `layoutOptions`
25
+ is an open map read by id, so pass a plain String-keyed Hash. Writing a
26
+ Symbol key into the returned map in place now stores the Symbol as given;
27
+ use String keys, or assign the whole map through the setter, which still
28
+ normalises. (#4, #10)
29
+ +
30
+ [source,ruby]
31
+ ----
32
+ # before
33
+ Elkrb::Graph::LayoutOptions.new(algorithm: "layered")
34
+ # after
35
+ { "elk.algorithm" => "layered" }
36
+ ----
37
+
38
+ * Unparseable ELKT raises `Elkrb::ParseError` with a line and column. It used
39
+ to return an empty graph. Rescue `Elkrb::ParseError` where you relied on
40
+ the empty graph. (#17)
41
+ +
42
+ [source,ruby]
43
+ ----
44
+ Elkrb::Parsers::ElktParser.parse("<html><body>not elkt</body></html>")
45
+ # before: {"id":"root","layoutOptions":{},"children":[],"edges":[]}
46
+ # after: Elkrb::ParseError: Unexpected character "<" at line 1, column 1
47
+ ----
48
+
49
+ * Layered defaults to direction `RIGHT` with a 20px gap between layers, and
50
+ cross-axis positions are centred. Set `"elk.direction": "DOWN"` and
51
+ `"elk.layered.spacing.nodeNodeBetweenLayers": 60` to approximate 1.x.
52
+ Layered reads the direction from `elk.direction` and, as a registered alias,
53
+ from the bare `direction` key. (#58)
54
+ +
55
+ Two nodes joined by one edge, default options:
56
+ +
57
+ [source,text]
58
+ ----
59
+ before: a (12, 12) b (12, 102)
60
+ after: a (12, 12) b (62, 12)
61
+ ----
62
+
63
+ * Edge sections start and end on node borders instead of node centres, section
64
+ ids change from `<edge>_section_0` to `<edge>_s0`, and sections gain
65
+ `incomingShape` and `outgoingShape`. Renderers that clipped centre-based
66
+ lines themselves should stop doing so. (#67)
67
+ +
68
+ [source,text]
69
+ ----
70
+ before: {"id":"e_section_0","startPoint":{"x":27.0,"y":27.0},"endPoint":{"x":27.0,"y":117.0}}
71
+ after: {"id":"e_s0","startPoint":{"x":42.0,"y":27.0},"endPoint":{"x":62.0,"y":27.0},
72
+ "incomingShape":"a","outgoingShape":"b"}
73
+ ----
74
+
75
+ * Label `x` and `y` are relative to their owner (node, port or edge) instead of
76
+ absolute, so add the owner's position when drawing. A node label with no
77
+ `elk.nodeLabels.placement` stays at (0, 0); set
78
+ `"elk.nodeLabels.placement": "[H_CENTER,V_CENTER,INSIDE]"` to keep it
79
+ centred. (#65)
80
+ +
81
+ A 60x40 node at (12,12) with a 10x10 label:
82
+ +
83
+ [source,text]
84
+ ----
85
+ before: {"text":"A","x":37.0,"y":27.0}
86
+ after, no placement option: {"text":"A","x":0.0,"y":0.0}
87
+ after, placement as above: {"text":"A","x":25.0,"y":15.0}
88
+ ----
89
+
90
+ * The `_constraint_*` and `_assigned_layer` scratch keys no longer appear in
91
+ output `properties`, and constraint violations go through `Elkrb.logger`
92
+ instead of `Kernel#warn`. `ConstraintProcessor.new` takes `spacing:` and
93
+ `BaseConstraint#applies_to?` is removed. Stop reading the scratch keys.
94
+ (#66)
95
+ +
96
+ [source,text]
97
+ ----
98
+ before: {"id":"a","constraints":{"fixedPosition":false,"layer":1,"positionPriority":0},
99
+ "properties":{"_constraint_layer":1}}
100
+ after: {"id":"a","constraints":{"fixedPosition":false,"layer":1,"positionPriority":0}}
101
+ ----
102
+
103
+ * mrtree and radial coordinates follow ELK's geometry. mrtree honours
104
+ `elk.direction`, and radial reads the graph's edges and centres the root.
105
+ There is nothing to migrate; the old coordinates did not match ELK. (#55)
106
+ +
107
+ Root `r` with children `c1` and `c2`, 30x30 nodes, mrtree:
108
+ +
109
+ [source,text]
110
+ ----
111
+ before: r (12, 12) c1 (12, 102) c2 (62, 102)
112
+ after: r (45, 20) c1 (20, 70) c2 (70, 70)
113
+ ----
114
+
115
+ * Force layout runs a deterministic Fruchterman-Reingold simulation using node
116
+ sizes, with ELK's 50px default padding; explicit padding still wins. Every
117
+ force coordinate changes. (#69)
118
+ +
119
+ The same graph with `"elk.algorithm": "force"`:
120
+ +
121
+ [source,text]
122
+ ----
123
+ before: r (12, 12) c1 (12, 102) c2 (62, 102)
124
+ after: r (115.648, 106.874) c1 (50, 163.785) c2 (181.253, 50)
125
+ ----
126
+
127
+ * libavoid keeps nodes that already have a position and places only the
128
+ unplaced ones, in a grid to the right of the placed nodes. To get a full
129
+ re-layout, position every node or none. (#60)
130
+ +
131
+ `a` at (5, 7), `b` at (200, 9) and an unplaced `c`:
132
+ +
133
+ [source,text]
134
+ ----
135
+ before: a (12, 12) b (62, 12) c (112, 12)
136
+ after: a (12, 12) b (207, 14) c (257, 12)
137
+ ----
138
+
139
+ * An edge written with the elkjs primitive keys `source` and `target` is now a
140
+ real edge, so the graph lays out differently. (#16)
141
+ +
142
+ [source,text]
143
+ ----
144
+ before: {"id":"e"}
145
+ after: {"id":"e","sources":["a"],"targets":["b"],
146
+ "sections":[{"id":"e_s0","startPoint":{"x":42.0,"y":27.0},
147
+ "endPoint":{"x":62.0,"y":27.0},
148
+ "incomingShape":"a","outgoingShape":"b"}]}
149
+ ----
150
+
151
+ * Ports are placed on their node's border. Each port now carries `x`, `y` and
152
+ `side`, and `offset` is filled in, where before a port had only `index` and
153
+ `offset: 0`. Edge sections that start or end at a port move to match. Code
154
+ that computed port positions itself should read these fields. (#70)
155
+ +
156
+ Node `a` with a 6x6 port `p`, edge `p` to `b`:
157
+ +
158
+ [source,text]
159
+ ----
160
+ before: port {"id":"p","index":0,"offset":0.0}
161
+ section start (12.0, 12.0)
162
+ after: port {"id":"p","x":30.0,"y":12.0,"side":"EAST","index":0,"offset":12.0}
163
+ section start (48.0, 27.0)
164
+ ----
165
+
166
+ * Layered orders the nodes within a layer by a barycenter sweep, so cross-axis
167
+ coordinates move whenever a layer holds more than one node.
168
+ `elk.layered.crossingMinimization.strategy` and
169
+ `elk.layered.considerModelOrder.strategy` are now honoured. Set
170
+ `elk.layered.crossingMinimization.strategy` to `NONE` to get insertion order
171
+ back. (#72)
172
+ +
173
+ Edges `a` to `d` and `b` to `c`, default options:
174
+ +
175
+ [source,text]
176
+ ----
177
+ before: a (12, 12) b (12, 62) c (62, 12) d (62, 62)
178
+ after: a (12, 12) b (12, 62) c (62, 62) d (62, 12)
179
+ after, NONE: a (12, 12) b (12, 62) c (62, 12) d (62, 62)
180
+ ----
181
+
182
+ * Self-loops leave and enter the node border and are routed outside the node
183
+ instead of retracing themselves, and their section id changes from
184
+ `<edge>_section_0` to `<edge>_s0`. An edge between two ports of the same node
185
+ is now routed as a loop too. Code that matches the old section id or the old
186
+ geometry must accept the new one. (#74)
187
+ +
188
+ Node `a` (30x30) with an edge from `a` to `a`:
189
+ +
190
+ [source,text]
191
+ ----
192
+ before: {"id":"e_section_0","startPoint":{"x":42.0,"y":27.0},"endPoint":{"x":42.0,"y":37.0},
193
+ "bendPoints":[{"x":74.0,"y":27.0},{"x":74.0,"y":-5.0},{"x":74.0,"y":59.0},{"x":42.0,"y":32.0}]}
194
+ after: {"id":"e_s0","startPoint":{"x":42.0,"y":22.0},"endPoint":{"x":42.0,"y":32.0},
195
+ "bendPoints":[{"x":74.0,"y":22.0},{"x":74.0,"y":32.0}],
196
+ "incomingShape":"a","outgoingShape":"a"}
197
+ ----
198
+
199
+ * `elkrb layout` rejects an unknown switch with `Unknown switches "--bogus"`
200
+ and a `Try: elkrb help layout` line. It used to read the switch as the input
201
+ file and fail with "no such file". `layout --output out.yml` now writes YAML,
202
+ where it wrote JSON; pass `--format json` to keep JSON. (#62)
203
+ +
204
+ `elkrb layout --bogus graph.json`:
205
+ +
206
+ [source,text]
207
+ ----
208
+ before: ERROR: "elkrb layout" was called with arguments ["--bogus", "graph.json"]
209
+ after: Unknown switches "--bogus"
210
+ Try: elkrb help layout
211
+ ----
212
+
213
+ * `sig/` is deleted and `rbs` is no longer a runtime dependency; the
214
+ signatures did not load. The gem ships an explicit file list (`lib`, `exe`,
215
+ `README.adoc`, `LICENSE`, `CHANGELOG.adoc`) instead of every tracked file,
216
+ so benchmarks, examples, docs and dotfiles are no longer packaged. (#77, #84)
217
+
218
+ * Layered routes an edge that spans more than one layer around the nodes in
219
+ between, through a dummy lane with bend points. Nodes in the spanned layers
220
+ can move on the cross axis to make room. Nothing to migrate: the old route
221
+ could cross straight through a node. (#76)
222
+ +
223
+ Edges a->b, b->c and a->c, default options:
224
+ +
225
+ [source,text]
226
+ ----
227
+ before: b (62, 12) a->c bends []
228
+ after: b (62, 37) a->c bends [(77, 2)]
229
+ ----
230
+
231
+ * A graph's own `elk.algorithm` now wins over the `algorithm:` keyword of
232
+ `Elkrb.layout`. Before, the keyword won and the graph's value was ignored.
233
+ The keyword is now the fallback for graphs that name no algorithm. To force
234
+ an algorithm on a graph that already names one, set `elk.algorithm` on the
235
+ graph. (#50)
236
+ +
237
+ A graph with `"elk.algorithm": "box"`, two 30x30 nodes, called with
238
+ `algorithm: "force"`:
239
+ +
240
+ [source,text]
241
+ ----
242
+ before: runs force a (12, 12) b (23.09, 14.34)
243
+ after: runs box a (15, 15) b (60, 15)
244
+ ----
245
+
246
+ * A duplicate node id, or a duplicate port id, within one level raises
247
+ `Elkrb::ValidationError` (`duplicate id: a`). Before, a duplicate node
248
+ failed with a `NoMethodError` and a duplicate port was silently accepted.
249
+ The same id may still appear at different levels. Duplicate edge ids are
250
+ not checked. (#5)
251
+
252
+ * Layered raises `Elkrb::UnsupportedConfigurationException` for an edge with
253
+ more than one source or more than one target (`layered does not support
254
+ hyperedges (edge e)`); it used to lay the graph out.
255
+ Layered also no longer rewrites your edges to break cycles, so a back edge
256
+ keeps its source and target and `properties["reversed"]` is never written.
257
+ Stop reading that key.
258
+ +
259
+ [source,text]
260
+ ----
261
+ cycle a->b, b->c, c->a
262
+ before: e3 {"sources":["a"],"targets":["c"],"properties":{"reversed":true}}
263
+ after: e3 {"sources":["c"],"targets":["a"]}
264
+ ----
265
+
266
+ * The `hierarchical` option does nothing. Hierarchy is detected from nodes that
267
+ have `children`, as before when the option was unset, and `hierarchical`
268
+ is now reported as an unknown option under `strict: true`. A compound node
269
+ is laid out before its parent level, so the parent level places its siblings
270
+ against the compound's computed size instead of the `width` and `height` it
271
+ declared. A compound that names its own `elk.algorithm` is still laid out by
272
+ it, but an unknown name now raises `Elkrb::AlgorithmNotFoundError` where
273
+ the parent's algorithm used to be used quietly. (#63)
274
+ +
275
+ Compound `c` declaring 500x500 and holding two 20x20 nodes, with a sibling
276
+ `z` linked from `c`:
277
+ +
278
+ [source,text]
279
+ ----
280
+ before: c 84x44 z (532, 252)
281
+ after: c 84x44 z (116, 24)
282
+ ----
283
+
284
+ * `fixed` keeps each node where its `x` and `y` put it, and reads
285
+ `elk.position`. It used to move the nodes so the top-left one sat at the
286
+ padding origin and to ignore `elk.position`. `box` uses a different packing,
287
+ so box coordinates and the graph size change. (#59)
288
+ +
289
+ [source,text]
290
+ ----
291
+ fixed, a at (100, 50) and b at (200, 80):
292
+ before: a (12, 12) b (112, 42)
293
+ after: a (100, 50) b (200, 80)
294
+
295
+ box, four 30x30 nodes:
296
+ before: 154x104 n0 (12, 12) n1 (62, 12) n2 (112, 12) n3 (12, 62)
297
+ after: 105x105 n0 (15, 15) n1 (60, 15) n2 (15, 60) n3 (60, 60)
298
+ ----
299
+
300
+ === Added
301
+
302
+ * Edges read the elkjs primitive endpoint keys `source`, `target`,
303
+ `sourcePort` and `targetPort` in every format, YAML included. A blank port
304
+ value is ignored, so a real `source` or `target` still wins. Output stays
305
+ `sources` and `targets` only.
306
+
307
+ * `junctionPoints`, `container`, `incomingSections` and `outgoingSections` on
308
+ edges, and `properties` on labels, are no longer dropped when parsing.
309
+
310
+ * `elkrb options [ALGORITHM]` lists every layout option with its type,
311
+ default, status, aliases and the algorithms that read it; `--json` prints
312
+ the same as JSON. `elkrb algorithms --json` is new. `batch` and `diagram`
313
+ accept the same layout flags as `layout`, including `--spacing`
314
+ and `--layer-spacing`. (#62)
315
+
316
+ * A `LICENSE` file (BSD-2-Clause, as the gemspec already declared) ships with
317
+ the gem. (#77)
318
+
319
+ === Fixed
320
+
321
+ * `Rectangle#position` and `Rectangle#center` no longer raise.
322
+
323
+ * `Bezier.calculate_curve` with fewer than two sample points returns copies of
324
+ the two endpoints instead of the caller's own `Point` objects. Its fifth
325
+ parameter is now named `sample_count`, which is what it always counted.
326
+
327
+ * Validation now checks nested container scopes, collection shapes, finite
328
+ dimensions and option enum values. It rejects ambiguous hierarchical edge
329
+ endpoints, and the `HORIZONTAL` and `VERTICAL` direction aliases produce
330
+ valid layouts. Validation output remains readable on US-ASCII terminals.
331
+
332
+ * Failed conversions and Graphviz renders preserve an existing destination
333
+ instead of replacing it with a partial result.
334
+
335
+ * ELKT output preserves representable graph geometry, edge sections, ports and
336
+ hierarchy. DOT output keeps same-named nodes in different hierarchy scopes
337
+ distinct and routes scoped and hierarchical port endpoints without creating
338
+ phantom nodes.
339
+
340
+ * `elk.selfLoopOffset` is honoured when it is carried by an edge or its node.
341
+
342
+ === Changed
343
+
344
+ * `lutaml-model` is now capped below 0.9.
345
+
346
+ * Layered layout uses ELK-style Brandes-Kopf placement, layer sweeps, discovery
347
+ and port ordering, per-gap orthogonal routing with junction points, and
348
+ connected-component packing. Node coordinates and edge routes can differ
349
+ from earlier releases.
350
+
351
+ * DisCo defaults to ELK's polyomino component compaction. Set the component
352
+ arrangement or compaction strategy to `ROW` to retain row arrangement.
353
+
354
+ * SPOrE overlap removal and compaction follow Java ELK's triangulation and tree
355
+ algorithms and honour their registered processing options.
356
+
357
+ * Stress layout uses ELK-compatible deterministic seeding and the weighted
358
+ stress objective. Large line-metric graphs with the default iteration limit
359
+ use an exact projection, avoiding the previous quadratic timeout.
360
+
361
+ === Security
362
+
363
+ * Release automation uses SHA-pinned actions, least-privilege jobs and
364
+ RubyGems OIDC trusted publishing instead of a long-lived API key.
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) Ribose Inc.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.