Skip to content

Commit 752d225

Browse files
committed
SVG!NodesOfDirectedMultiGraph: Expose the Sugiyama and Eiglsperger
layout algorithms and their layering. [Feature]
1 parent b9bdd58 commit 752d225

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

modules/SVG.tla

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ NodeOfRingNetwork(cx, cy, r, n, m) ==
155155
(**************************************************************************)
156156
[ x |-> 0, y |-> 0 ]
157157

158-
NodesOfDirectedMultiGraph(nodes, edges, width, height) ==
158+
NodesOfDirectedMultiGraph(nodes, edges, options) ==
159159
(**************************************************************************)
160160
(* Example to layout a graph with the given Nodes and Edges: *)
161161
(* *)
162162
(* Nodes == {"v1", "v2", "v3"} \* v3 is not connected *)
163163
(* Edges == {<<"v1", "v2">>, <<"v2", "v1">>} *)
164-
(* Graph == NodesOfDirectedMultiGraph(Nodes, Edges, 23, 42) *)
164+
(* Graph == NodesOfDirectedMultiGraph(Nodes, Edges, [algo |-> ...]) *)
165165
(* *)
166166
(* RN[ n \in Nodes ] == *)
167167
(* LET c == Graph[n] *)

modules/tlc2/overrides/SVG.java

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,16 @@
3232
import org.jgrapht.graph.DefaultGraphType;
3333
import org.jgrapht.graph.builder.GraphTypeBuilder;
3434
import org.jgrapht.util.SupplierUtil;
35+
import org.jungrapht.visualization.layout.algorithms.EiglspergerLayoutAlgorithm;
36+
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
37+
import org.jungrapht.visualization.layout.algorithms.SugiyamaLayoutAlgorithm;
3538
import org.jungrapht.visualization.layout.algorithms.TreeLayoutAlgorithm;
39+
import org.jungrapht.visualization.layout.algorithms.sugiyama.Layering;
3640
import org.jungrapht.visualization.layout.model.LayoutModel;
3741
import org.jungrapht.visualization.layout.model.Point;
3842
import org.jungrapht.visualization.layout.model.Rectangle;
3943

44+
import tlc2.tool.EvalControl;
4045
import tlc2.value.impl.FcnRcdValue;
4146
import tlc2.value.impl.IntValue;
4247
import tlc2.value.impl.RecordValue;
@@ -151,11 +156,13 @@ public static Value ringNetwork(IntValue cx, IntValue cy, IntValue r, IntValue n
151156
}
152157

153158
@TLAPlusOperator(identifier = "NodesOfDirectedMultiGraph", module = "SVG", warn = false)
154-
public static Value directedMultiGraph(final SetEnumValue nodes, final SetEnumValue edges, final IntValue width, final IntValue height) throws Exception {
159+
public static Value directedMultiGraph(final SetEnumValue nodes, final SetEnumValue edges, final RecordValue opts)
160+
throws Exception {
155161
// https://jgrapht.org/guide/UserOverview#graph-structures
156-
final Graph<Value, Integer> graph = GraphTypeBuilder.<Value, Integer>forGraphType(DefaultGraphType.directedMultigraph())
162+
final Graph<Value, Integer> graph = GraphTypeBuilder
163+
.<Value, Integer>forGraphType(DefaultGraphType.directedMultigraph())
157164
.edgeSupplier(SupplierUtil.createIntegerSupplier()).allowingSelfLoops(false).buildGraph();
158-
165+
159166
ValueVec elems = nodes.elems;
160167
for (int i = 0; i < elems.size(); i++) {
161168
graph.addVertex(elems.elementAt(i));
@@ -166,22 +173,50 @@ public static Value directedMultiGraph(final SetEnumValue nodes, final SetEnumVa
166173
graph.addEdge(tuple.elems[0], tuple.elems[1]);
167174
}
168175

169-
final LayoutModel<Value> layoutModel = LayoutModel.<Value>builder().size(1000, 1000).graph(graph).build();
170-
TreeLayoutAlgorithm<Value> layoutAlgo = TreeLayoutAlgorithm.<Value>builder()
171-
.vertexBoundsFunction(v -> Rectangle.of(-5, -5, width.val, height.val)).build();
172-
layoutAlgo.visit(layoutModel);
173-
final Map<Value, Point> locations = layoutModel.getLocations();
176+
// Layout
177+
final IntValue viewH = (IntValue) opts.apply(new StringValue("view_width"), EvalControl.Clear);
178+
final IntValue viewW = (IntValue) opts.apply(new StringValue("view_height"), EvalControl.Clear);
179+
final LayoutModel<Value> layoutModel = LayoutModel.<Value>builder().size(viewW.val, viewH.val).graph(graph)
180+
.build();
181+
182+
// Algorithm
183+
final StringValue algo = (StringValue) opts.apply(new StringValue("algo"), EvalControl.Clear);
184+
getAlgo(algo.val.toString(), opts).visit(layoutModel);
185+
186+
// Get the node's coordinates from the algorithm.
187+
final Map <Value, Point> locations = layoutModel.getLocations();
174188

189+
// convert to TLC values.
175190
return new FcnRcdValue(locations.entrySet().stream()
176-
.collect(Collectors.toMap(
177-
entry -> entry.getKey(),
178-
entry -> point2Value(entry.getValue()))));
191+
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> point2Value(entry.getValue()))));
179192
}
180-
193+
181194
private static Value point2Value(final Point p) {
182195
final int x = ((Double) p.x).intValue();
183196
final int y = ((Double) p.y).intValue();
184197
return new RecordValue(new UniqueString[] { UniqueString.of("x"), UniqueString.of("y") },
185198
new Value[] { IntValue.gen(x), IntValue.gen(y) }, false);
186199
}
200+
201+
private static LayoutAlgorithm<Value> getAlgo(final String algo, final RecordValue opts) {
202+
final IntValue nodeH = (IntValue) opts.apply(new StringValue("node_width"), EvalControl.Clear);
203+
final IntValue nodeW = (IntValue) opts.apply(new StringValue("node_height"), EvalControl.Clear);
204+
205+
switch (algo) {
206+
case "Sugiyama":
207+
// https://github.com/tomnelson/jungrapht-visualization/blob/afd155bf0246e5185f054ba1429bbcfbd429292a/jungrapht-layout/src/main/java/org/jungrapht/visualization/layout/algorithms/sugiyama/Layering.java#L4-L7
208+
String l = ((StringValue) opts.apply(new StringValue("layering"), EvalControl.Clear)).val.toString();
209+
return SugiyamaLayoutAlgorithm.<Value, Integer>edgeAwareBuilder()
210+
.layering(Layering.valueOf(l)).vertexBoundsFunction(v -> Rectangle.of(-5, -5, nodeW.val, nodeH.val)).threaded(false)
211+
.build();
212+
case "Eiglsperger":
213+
l = ((StringValue) opts.apply(new StringValue("layering"), EvalControl.Clear)).val.toString();
214+
return EiglspergerLayoutAlgorithm.<Value, Integer>edgeAwareBuilder()
215+
.layering(Layering.valueOf(l)).vertexBoundsFunction(v -> Rectangle.of(-5, -5, nodeW.val, nodeH.val)).threaded(false)
216+
.build();
217+
default:
218+
return TreeLayoutAlgorithm.<Value>builder()
219+
.vertexBoundsFunction(v -> Rectangle.of(-5, -5, nodeW.val, nodeH.val)).build();
220+
}
221+
}
187222
}

0 commit comments

Comments
 (0)