|
25 | 25 | ******************************************************************************/ |
26 | 26 | package tlc2.overrides; |
27 | 27 |
|
| 28 | +import java.util.Map; |
| 29 | +import java.util.stream.Collectors; |
| 30 | + |
| 31 | +import org.jgrapht.Graph; |
| 32 | +import org.jgrapht.graph.DefaultGraphType; |
| 33 | +import org.jgrapht.graph.builder.GraphTypeBuilder; |
| 34 | +import org.jgrapht.util.SupplierUtil; |
| 35 | +import org.jungrapht.visualization.layout.algorithms.TreeLayoutAlgorithm; |
| 36 | +import org.jungrapht.visualization.layout.model.LayoutModel; |
| 37 | +import org.jungrapht.visualization.layout.model.Point; |
| 38 | +import org.jungrapht.visualization.layout.model.Rectangle; |
| 39 | + |
28 | 40 | import tlc2.value.impl.FcnRcdValue; |
29 | 41 | import tlc2.value.impl.IntValue; |
30 | 42 | import tlc2.value.impl.RecordValue; |
| 43 | +import tlc2.value.impl.SetEnumValue; |
31 | 44 | import tlc2.value.impl.StringValue; |
32 | 45 | import tlc2.value.impl.TupleValue; |
33 | 46 | import tlc2.value.impl.Value; |
| 47 | +import tlc2.value.impl.ValueVec; |
34 | 48 | import util.UniqueString; |
35 | 49 |
|
36 | 50 | public final class SVG { |
@@ -135,4 +149,39 @@ public static Value ringNetwork(IntValue cx, IntValue cy, IntValue r, IntValue n |
135 | 149 | final int y = (int) (cy.val + r.val * Math.sin(angle)); |
136 | 150 | return new RecordValue(new UniqueString[] {UniqueString.of("x"), UniqueString.of("y")}, new Value[] {IntValue.gen(x), IntValue.gen(y)}, false); |
137 | 151 | } |
| 152 | + |
| 153 | + @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 { |
| 155 | + // https://jgrapht.org/guide/UserOverview#graph-structures |
| 156 | + final Graph<Value, Integer> graph = GraphTypeBuilder.<Value, Integer>forGraphType(DefaultGraphType.directedMultigraph()) |
| 157 | + .edgeSupplier(SupplierUtil.createIntegerSupplier()).allowingSelfLoops(false).buildGraph(); |
| 158 | + |
| 159 | + ValueVec elems = nodes.elems; |
| 160 | + for (int i = 0; i < elems.size(); i++) { |
| 161 | + graph.addVertex(elems.elementAt(i)); |
| 162 | + } |
| 163 | + elems = edges.elems; |
| 164 | + for (int i = 0; i < elems.size(); i++) { |
| 165 | + TupleValue tuple = (TupleValue) elems.elementAt(i); |
| 166 | + graph.addEdge(tuple.elems[0], tuple.elems[1]); |
| 167 | + } |
| 168 | + |
| 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(); |
| 174 | + |
| 175 | + return new FcnRcdValue(locations.entrySet().stream() |
| 176 | + .collect(Collectors.toMap( |
| 177 | + entry -> entry.getKey(), |
| 178 | + entry -> point2Value(entry.getValue())))); |
| 179 | + } |
| 180 | + |
| 181 | + private static Value point2Value(final Point p) { |
| 182 | + final int x = ((Double) p.x).intValue(); |
| 183 | + final int y = ((Double) p.y).intValue(); |
| 184 | + return new RecordValue(new UniqueString[] { UniqueString.of("x"), UniqueString.of("y") }, |
| 185 | + new Value[] { IntValue.gen(x), IntValue.gen(y) }, false); |
| 186 | + } |
138 | 187 | } |
0 commit comments