diff --git a/docs/content/documentation/tooldevelopers/graphdatastructure.md b/docs/content/documentation/tooldevelopers/graphdatastructure.md index b47d6f569c..22e66890d5 100644 --- a/docs/content/documentation/tooldevelopers/graphdatastructure.md +++ b/docs/content/documentation/tooldevelopers/graphdatastructure.md @@ -118,6 +118,8 @@ Since ELK graphs are based on EMF, you can simply obtain an instance of the `Elk One method of particular value to layout algorithm developers is `firstEdgeSection(edge, reset, removeOthers)`, which returns the first edge section of the given edge, optionally resetting its layout data and removing all other edge sections. If the edge doesn't have any edge sections yet, one is created and added to it. This method is handy for applying layout results. * **Edge Containment:** Unless one uses one of the `create` methods to create edges, finding the node an edge should be contained in may be annoying (thus, don't). The `updateContainment(ElkEdge)` method automatically computes the best containment and puts the edge there. This requires at least one of the edge's end points to be set, since that is what determines the best containment. +Note that, when graphs are imported into ELK from JSON, `updateContainment()` is automatically called on each edge. Authors of graphs in JSON format are therefore not responsible for placing edges +inside their proper container nodes. While computing the containment is a little intricate (hence the utility method), the actual rule that determines the best containment is rather simple: it is the lowest common ancestor of all end points, where the ancestors of a node are defined as the sequence of nodes from the node itself to the graph's root node. Taking the graph at the top of this page as an example, the rule has the following implications for different types of edges: diff --git a/docs/content/documentation/tooldevelopers/graphdatastructure/coordinatesystem.md b/docs/content/documentation/tooldevelopers/graphdatastructure/coordinatesystem.md index 654baf6bf6..3e1016b19d 100644 --- a/docs/content/documentation/tooldevelopers/graphdatastructure/coordinatesystem.md +++ b/docs/content/documentation/tooldevelopers/graphdatastructure/coordinatesystem.md @@ -14,3 +14,51 @@ The coordinates of most elements are relative to their parent element. There are * Edge labels are relative to the coordinate system their edge is relative to. * Source points, bend points, and target points of edges are relative to the edge's containing node. Following the usual containment conventions (as implemented in `ElkGraphUtil`) results in what is displayed in the image above. + +## JSON + +One way of using ELK is to define a graph using JSON, and then +transfer the computed layout back to the initial JSON graph. +See [_JSON Format_]({{< relref "documentation/tooldevelopers/graphdatastructure/jsonformat.md" >}}). +When working this way, it is possible to control what type of coordinates you get, +using the `org.eclipse.elk.json.shapeCoords` and `org.eclipse.elk.json.edgeCoords` layout options. + +### Shape Coords + +This setting controls the coordinate system that will be used for all nodes, ports, and labels of nodes and ports. + +There are three possible values: + +* `INHERIT`: Inherit the value from the parent element. +* `PARENT`: Coordinates are relative to the parent element. +* `ROOT`: Coordinates are relative to the root node, i.e. global coordinates. + +The default value is `INHERIT`, except at the root node, where the default is `PARENT`. +Thus, the default behavior is as described in the image at the top of this page. +Setting `ROOT` allows you to instead use a "flat" coordinate system. + +### Edge Coords + +This setting controls the coordinate system that will be used for all source, bend, and target points of edges, +as well as all edge labels. + +There are four possible values: + +* `INHERIT`: Inherit the value from the parent element. +* `CONTAINER`: Coordinates are relative to the edge's proper container node (see below). +* `PARENT`: Coordinates are relative to the node in which the edge was defined in the given JSON. +* `ROOT`: Coordinates are relative to the root node, i.e. global coordinates. + +The default value is `INHERIT`, except at the root node, where the default is (for historical reasons) `CONTAINER`. +Thus, the default behavior is as described at the top of this page. + +The definition of an edge's proper container is given at the end of the page on the +[_Graph Data Structure_]({{< relref "documentation/tooldevelopers/graphdatastructure.md" >}}). +It involves lowest common ancestors, and can be tricky to compute correctly. +In particular, since you are allowed to define an edge under any node whatsoever in the input JSON, the edge's +*parent* node (the one it is defined under) may be different from its container. + +If you elect to work in `CONTAINER` mode, ELK helps you by writing a `container` property into each edge after layout, +giving the `id` of the edge's container node. This can then be used to interpret the coordinates. + +If you elect to work in `PARENT` or `ROOT` mode instead, then you do not need to know about edge containers at all. diff --git a/docs/content/documentation/tooldevelopers/graphdatastructure/jsonformat.md b/docs/content/documentation/tooldevelopers/graphdatastructure/jsonformat.md index f11c2554b9..3c30c95b9c 100644 --- a/docs/content/documentation/tooldevelopers/graphdatastructure/jsonformat.md +++ b/docs/content/documentation/tooldevelopers/graphdatastructure/jsonformat.md @@ -55,9 +55,12 @@ labels. Nodes can have an arbitrary number of ports. Edges can connect to a node either directly or through one of its ports. A node can also contain an arbitrary number of child nodes. A graph is actually nothing more than a simple node whose children are the top-level nodes of the graph. -Finally, a node can contain edges. These edges do not necessarily connect to the node, but will -usually connect its children. The edge coordinates will be interpreted relative to the upper -left corner of the node which contains it. +Finally, a node can contain edges. While it is common to define those edges under a given node +that connect that node's children, in fact any edge may be defined under any node, regardless of its +end points. This allows for flexibility when defining hierarchy-crossing edges, as well as for +alternative schemes, such as defining all edges at the root level. +See [_Coordinate System_]({{< relref "documentation/tooldevelopers/graphdatastructure/coordinatesystem.md" >}}) +for the rules for interpreting edge coordinates. ```json { diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk index d5b059bab5..4ae8418bcc 100644 --- a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk @@ -302,6 +302,29 @@ advanced option omitNodeMicroLayout: boolean { targets parents } +// --- JSON +group json { + + option shapeCoords: ShapeCoords { + label "Shape Coords" + description + "For layouts transferred into JSON graphs, specify the coordinate system + to be used for nodes, ports, and labels of nodes and ports." + default = ShapeCoords.INHERIT + targets parents + } + + option edgeCoords: EdgeCoords { + label "Edge Coords" + description + "For layouts transferred into JSON graphs, specify the coordinate system + to be used for edge route points and edge labels." + default = EdgeCoords.INHERIT + targets parents + } + +} + // --- SPACING group spacing { diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/EdgeCoords.java b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/EdgeCoords.java new file mode 100644 index 0000000000..b2945e8fe7 --- /dev/null +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/EdgeCoords.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2024 Kiel University and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.elk.core.options; + +/** + * Edge coordinate systems for JSON output. To be accessed using {@link CoreOptions#JSON_EDGE_COORDS}. + * Applies to edges, and to labels of edges. + */ +public enum EdgeCoords { + + /** + * Inherit the parent node's coordinate system. The root node has no parent node; here, this setting defaults to + * {@link #CONTAINER}. + */ + INHERIT, + /** relative to the edge's proper container node. */ + CONTAINER, + /** relative to the edge's JSON parent node. */ + PARENT, + /** relative to the root node, a.k.a. global coordinates. */ + ROOT; + +} diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/ShapeCoords.java b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/ShapeCoords.java new file mode 100644 index 0000000000..4d776bce69 --- /dev/null +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/options/ShapeCoords.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2024 Kiel University and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.elk.core.options; + +/** + * Shape coordinate systems for JSON output. To be accessed using {@link CoreOptions#JSON_SHAPE_COORDS}. + * Applies to nodes, ports, and labels of nodes and ports. + */ +public enum ShapeCoords { + + /** + * Inherit the parent shape's coordinate system. The root node has no parent shape; here, this setting defaults to + * {@link #PARENT}. + */ + INHERIT, + /** relative to the shape's JSON parent shape. */ + PARENT, + /** relative to the root node, a.k.a. global coordinates. */ + ROOT; + +} diff --git a/plugins/org.eclipse.elk.graph.json/src/org/eclipse/elk/graph/json/JsonImporter.xtend b/plugins/org.eclipse.elk.graph.json/src/org/eclipse/elk/graph/json/JsonImporter.xtend index 7990e0f7ff..99dfb64188 100644 --- a/plugins/org.eclipse.elk.graph.json/src/org/eclipse/elk/graph/json/JsonImporter.xtend +++ b/plugins/org.eclipse.elk.graph.json/src/org/eclipse/elk/graph/json/JsonImporter.xtend @@ -17,6 +17,8 @@ import com.google.common.collect.Multimap import java.util.Map import org.eclipse.elk.core.data.LayoutMetaDataService import org.eclipse.elk.core.options.CoreOptions +import org.eclipse.elk.core.options.EdgeCoords +import org.eclipse.elk.core.options.ShapeCoords import org.eclipse.elk.core.util.IndividualSpacings import org.eclipse.elk.graph.ElkEdge import org.eclipse.elk.graph.ElkEdgeSection @@ -58,6 +60,13 @@ final class JsonImporter { val Map edgeJsonMap = Maps.newHashMap val Map edgeSectionJsonMap = Maps.newHashMap val Map labelJsonMap = Maps.newHashMap + + /* Maps to help in adjusting coordinates */ + val Map edgeOriginalParentMap = Maps.newHashMap + val Map globalXMap = Maps.newHashMap + val Map globalYMap = Maps.newHashMap + val Map shapeCoordsMap = Maps.newHashMap + val Map edgeCoordsMap = Maps.newHashMap var Object inputModel @@ -91,10 +100,18 @@ final class JsonImporter { portIdMap.clear edgeIdMap.clear edgeSectionIdMap.clear + nodeJsonMap.clear portJsonMap.clear edgeJsonMap.clear - edgeSectionJsonMap.clear + edgeSectionJsonMap.clear + labelJsonMap.clear + + edgeOriginalParentMap.clear + globalXMap.clear + globalYMap.clear + shapeCoordsMap.clear + edgeCoordsMap.clear } private def transformChildNodes(Object jsonNodeA, ElkNode parent) { @@ -525,37 +542,45 @@ final class JsonImporter { * Transfer the layout back to the formerly imported graph, using {@link #transform(Object)}. */ def transferLayout(ElkNode graph) { - // transfer layout of all elements (including root) + // First pass handles nodes and ports. ElkGraphUtil.propertiesSkippingIteratorFor(graph, true).forEach [ element | - element.transferLayoutInt + element.transferLayoutInt1 + ] + + // Second pass handles edges and labels. + ElkGraphUtil.propertiesSkippingIteratorFor(graph, true).forEach [ element | + element.transferLayoutInt2 ] } - private def dispatch transferLayoutInt(ElkNode node) { + private def dispatch transferLayoutInt1(ElkNode node) { val jsonObj = nodeJsonMap.get(node) if (jsonObj === null) { throw formatError("Node did not exist in input.") } - // transfer positions and dimension + node.recordGlobalCoords + node.recordCoordinateModes node.transferShapeLayout(jsonObj) } - private def dispatch transferLayoutInt(ElkPort port) { + private def dispatch transferLayoutInt1(ElkPort port) { val jsonObj = portJsonMap.get(port) if (jsonObj === null) { throw formatError("Port did not exist in input.") } - - // transfer positions and dimension + port.recordGlobalCoords + port.recordCoordinateModes port.transferShapeLayout(jsonObj) } - private def dispatch transferLayoutInt(ElkEdge edge) { + private def dispatch transferLayoutInt2(ElkEdge edge) { val jsonObj = edgeJsonMap.get(edge).toJsonObject if (jsonObj === null) { throw formatError("Edge did not exist in input.") } + edge.recordCoordinateModes + val edgeId = jsonObj.id // what we need to transfer are the edge sections @@ -576,14 +601,14 @@ final class JsonImporter { // Start Point val startPoint = newJsonObject - startPoint.addJsonObj("x", elkSection.startX) - startPoint.addJsonObj("y", elkSection.startY) + startPoint.addJsonObj("x", edge.adjustX(elkSection.startX)) + startPoint.addJsonObj("y", edge.adjustY(elkSection.startY)) jsonSection.addJsonObj("startPoint", startPoint) // End Point val endPoint = newJsonObject - endPoint.addJsonObj("x", elkSection.endX) - endPoint.addJsonObj("y", elkSection.endY) + endPoint.addJsonObj("x", edge.adjustX(elkSection.endX)) + endPoint.addJsonObj("y", edge.adjustY(elkSection.endY)) jsonSection.addJsonObj("endPoint", endPoint) // Bend Points @@ -591,8 +616,8 @@ final class JsonImporter { val bendPoints = newJsonArray elkSection.bendPoints.forEach [ pnt | val jsonPnt = newJsonObject - jsonPnt.addJsonObj("x", pnt.x) - jsonPnt.addJsonObj("y", pnt.y) + jsonPnt.addJsonObj("x", edge.adjustX(pnt.x)) + jsonPnt.addJsonObj("y", edge.adjustY(pnt.y)) bendPoints.addJsonArr(jsonPnt) ] jsonSection.addJsonObj("bendPoints", bendPoints) @@ -637,37 +662,78 @@ final class JsonImporter { val jsonJPs = newJsonArray jps.forEach[ jp | val jsonPnt = newJsonObject - jsonPnt.addJsonObj("x", jp.x) - jsonPnt.addJsonObj("y", jp.y) + jsonPnt.addJsonObj("x", edge.adjustX(jp.x)) + jsonPnt.addJsonObj("y", edge.adjustY(jp.y)) jsonJPs.addJsonArr(jsonPnt) ] jsonObj.addJsonObj("junctionPoints", jsonJPs) } } - jsonObj.addJsonObj("container", edge.getContainingNode().identifier) + if (edge.originalParent.edgeCoordsMode === EdgeCoords.CONTAINER) { + jsonObj.addJsonObj("container", edge.getContainingNode.identifier) + } } - private def dispatch transferLayoutInt(ElkLabel label) { + private def dispatch transferLayoutInt2(ElkLabel label) { val jsonObj = labelJsonMap.get(label) - - // transfer positions and dimension + label.recordGlobalCoords + label.recordCoordinateModes label.transferShapeLayout(jsonObj) } - private def dispatch transferLayoutInt(Object obj) { + private def dispatch transferLayoutInt1(Object obj) { + // don't care about the rest + } + + private def dispatch transferLayoutInt2(Object obj) { // don't care about the rest } private def transferShapeLayout(ElkShape shape, Object jsonObjA) { val jsonObj = jsonObjA.toJsonObject + val parent = shape.jsonParent // pos and dimension - jsonObj.addJsonObj("x", shape.x) - jsonObj.addJsonObj("y", shape.y) + jsonObj.addJsonObj("x", parent?.adjustX(shape.x) ?: shape.x) + jsonObj.addJsonObj("y", parent?.adjustY(shape.y) ?: shape.y) jsonObj.addJsonObj("width", shape.width) jsonObj.addJsonObj("height", shape.height) } + private def dispatch Double adjustX(ElkEdge edge, Double x) { + val mode = edge.originalParent.edgeCoordsMode + return switch mode { + case EdgeCoords.ROOT: x + edge.getContainingNode.globalX + case EdgeCoords.PARENT: x + edge.getContainingNode.globalX - edge.originalParent.globalX + default: x + } + } + + private def dispatch Double adjustX(ElkShape shape, Double x) { + val mode = shape.shapeCoordsMode + return switch mode { + case ShapeCoords.ROOT: x + shape.globalX + default: x + } + } + + private def dispatch Double adjustY(ElkEdge edge, Double y) { + val mode = edge.originalParent.edgeCoordsMode + return switch mode { + case EdgeCoords.ROOT: y + edge.getContainingNode.globalY + case EdgeCoords.PARENT: y + edge.getContainingNode.globalY - edge.originalParent.globalY + default: y + } + } + + private def dispatch Double adjustY(ElkShape shape, Double y) { + val mode = shape.shapeCoordsMode + return switch mode { + case ShapeCoords.ROOT: y + shape.globalY + default: y + } + } + private def dispatch idByElement(ElkNode node) { return nodeIdMap.inverse.get(node) } @@ -708,6 +774,7 @@ final class JsonImporter { edgeIdMap.put(id, edge) edgeJsonMap.put(edge, obj) + edgeOriginalParentMap.put(edge, edge.getContainingNode) return edge } @@ -720,5 +787,74 @@ final class JsonImporter { return edgeSection } + + private def recordCoordinateModes(ElkGraphElement element) { + val parent = element.jsonParent + + var scm = element.getProperty(CoreOptions.JSON_SHAPE_COORDS) ?: ShapeCoords.INHERIT + if (scm === ShapeCoords.INHERIT) { + scm = parent.shapeCoordsMode ?: ShapeCoords.PARENT + } + shapeCoordsMap.put(element, scm) + + var ecm = element.getProperty(CoreOptions.JSON_EDGE_COORDS) ?: EdgeCoords.INHERIT + if (ecm === EdgeCoords.INHERIT) { + ecm = parent.edgeCoordsMode ?: EdgeCoords.CONTAINER + } + edgeCoordsMap.put(element, ecm) + } + + private def recordGlobalCoords(ElkShape shape) { + val parent = shape.jsonParent + val shapeA = parent?.shapeAncestor + val dx = shapeA.globalX ?: 0 + val dy = shapeA.globalY ?: 0 + globalXMap.put(shape, shape.x + dx) + globalYMap.put(shape, shape.y + dy) + } + + private def dispatch shapeAncestor(ElkEdge edge) { + return edge.originalParent + } + + private def dispatch shapeAncestor(ElkShape shape) { + return shape + } + + private def dispatch jsonParent(ElkNode node) { + return node.getParent + } + + private def dispatch jsonParent(ElkPort port) { + return port.getParent + } + + private def dispatch jsonParent(ElkEdge edge) { + return edge.originalParent + } + + private def dispatch jsonParent(ElkLabel label) { + return label.getParent + } + + private def ElkNode originalParent(ElkEdge edge) { + return edgeOriginalParentMap.get(edge) + } + + private def Double globalX(ElkShape shape) { + return globalXMap.get(shape) + } + + private def Double globalY(ElkShape shape) { + return globalYMap.get(shape) + } + + private def ShapeCoords shapeCoordsMode(ElkGraphElement element) { + return shapeCoordsMap.get(element) + } + + private def EdgeCoords edgeCoordsMode(ElkGraphElement element) { + return edgeCoordsMap.get(element) + } } diff --git a/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/EdgeCoordsTest.xtend b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/EdgeCoordsTest.xtend new file mode 100644 index 0000000000..7a1d2a05f1 --- /dev/null +++ b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/EdgeCoordsTest.xtend @@ -0,0 +1,1376 @@ +/******************************************************************************* + * Copyright (c) 2024 Kiel University and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.elk.graph.json.test + +import com.google.gson.JsonParser +import org.eclipse.elk.alg.test.PlainJavaInitialization +import org.eclipse.elk.core.RecursiveGraphLayoutEngine +import org.eclipse.elk.core.util.BasicProgressMonitor +import org.eclipse.elk.core.util.Maybe +import org.eclipse.elk.graph.json.ElkGraphJson +import org.eclipse.elk.graph.json.JsonImporter +import org.junit.BeforeClass +import org.junit.Test + +import static org.junit.Assert.* + +/** + */ +class EdgeCoordsTest { + + @BeforeClass + static def void init() { + PlainJavaInitialization.initializePlainJavaLayout + } + val graph = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN" + }, + "children": [ + { "id": "A", + "children": [ + { "id": "x", "width": 50, "height": 90 }, + { "id": "B", + "labels": [ { "text": "B", "width": 10, "height": 12 } ], + "ports": [ + { "id": "p", "width": 10, "height": 10, + "labels": [ { "text": "p", "width": 10, "height": 12 } ] + } + ], + "children": [ + { "id": "y", "width": 50, "height": 90 }, + { "id": "z", "width": 50, "height": 90 } + ], + "edges": [ + { "id": "e1", "sources": [ "y" ], "targets": [ "z" ] }, + { "id": "e2", "sources": [ "x" ], "targets": [ "z" ], + "labels": [ { "text": "e2", "width": 20, "height": 12 } ] + }, + { "id": "e3", "sources": [ "x" ], "targets": [ "p" ] }, + { "id": "e4", "sources": [ "p" ], "targets": [ "y" ] } + ] + } + ] + } + ] + } + ''' + + @Test + def void edgeCoordsTest() { + + val cases = #[ + #['PARENT', 'CONTAINER', outputPC], + #['PARENT', 'PARENT', outputPP], + #['PARENT', 'ROOT', outputPR], + #['ROOT', 'CONTAINER', outputRC], + #['ROOT', 'PARENT', outputRP], + #['ROOT', 'ROOT', outputRR] + ] + + for (c : cases) { + val scm = c.get(0) + val ecm = c.get(1) + val expectedOutputString = c.get(2) + + val jsonGraph = JsonParser.parseString(graph).asJsonObject + + val obj = jsonGraph.get("properties").asJsonObject + obj.addProperty( + "org.eclipse.elk.json.shapeCoords", scm + ) + obj.addProperty( + "org.eclipse.elk.json.edgeCoords", ecm + ) + + val mby = new Maybe + val root = ElkGraphJson.forGraph(jsonGraph) + .rememberImporter(mby) + .toElk + + new RecursiveGraphLayoutEngine().layout(root, new BasicProgressMonitor) + + mby.get.transferLayout(root) + + val computedOutput = jsonGraph.asJsonObject + + val expectedOutput = JsonParser.parseString(expectedOutputString).asJsonObject + + assertEquals(expectedOutput, computedOutput) + } + + } + + val outputPC = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "PARENT", + "org.eclipse.elk.json.edgeCoords": "CONTAINER" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 12, + "y": 39 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 0, + "y": 0 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": -11, + "y": -13 + } + ], + "x": -10, + "y": 52 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 12, + "y": 12 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 82, + "y": 27 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 62, + "y": 57 + }, + "endPoint": { + "x": 82, + "y": 57 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ], + "container": "B" + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": 82, + "y": 102 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": 62, + "y": 99 + }, + "endPoint": { + "x": 225, + "y": 99 + }, + "bendPoints": [ + { + "x": 112, + "y": 99 + }, + { + "x": 112, + "y": 124 + }, + { + "x": 215, + "y": 124 + }, + { + "x": 215, + "y": 99 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ], + "container": "A" + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": 62, + "y": 69 + }, + "endPoint": { + "x": 133, + "y": 69 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ], + "container": "A" + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": -10, + "y": 57 + }, + "endPoint": { + "x": 12, + "y": 57 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ], + "container": "B" + } + ], + "x": 143, + "y": 12, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + ''' + + val outputPP = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "PARENT", + "org.eclipse.elk.json.edgeCoords": "PARENT" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 12, + "y": 39 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 0, + "y": 0 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": -11, + "y": -13 + } + ], + "x": -10, + "y": 52 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 12, + "y": 12 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 82, + "y": 27 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 62, + "y": 57 + }, + "endPoint": { + "x": 82, + "y": 57 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ] + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": -61, + "y": 90 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": -81, + "y": 87 + }, + "endPoint": { + "x": 82, + "y": 87 + }, + "bendPoints": [ + { + "x": -31, + "y": 87 + }, + { + "x": -31, + "y": 112 + }, + { + "x": 72, + "y": 112 + }, + { + "x": 72, + "y": 87 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ] + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": -81, + "y": 57 + }, + "endPoint": { + "x": -10, + "y": 57 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ] + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": -10, + "y": 57 + }, + "endPoint": { + "x": 12, + "y": 57 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ] + } + ], + "x": 143, + "y": 12, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + ''' + + val outputPR = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "PARENT", + "org.eclipse.elk.json.edgeCoords": "ROOT" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 12, + "y": 39 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 0, + "y": 0 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": -11, + "y": -13 + } + ], + "x": -10, + "y": 52 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 12, + "y": 12 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 82, + "y": 27 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 217, + "y": 81 + }, + "endPoint": { + "x": 237, + "y": 81 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ] + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": 94, + "y": 114 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": 74, + "y": 111 + }, + "endPoint": { + "x": 237, + "y": 111 + }, + "bendPoints": [ + { + "x": 124, + "y": 111 + }, + { + "x": 124, + "y": 136 + }, + { + "x": 227, + "y": 136 + }, + { + "x": 227, + "y": 111 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ] + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": 74, + "y": 81 + }, + "endPoint": { + "x": 145, + "y": 81 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ] + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": 145, + "y": 81 + }, + "endPoint": { + "x": 167, + "y": 81 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ] + } + ], + "x": 143, + "y": 12, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + ''' + + val outputRC = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "ROOT", + "org.eclipse.elk.json.edgeCoords": "CONTAINER" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 24, + "y": 51 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 155, + "y": 24 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": 134, + "y": 63 + } + ], + "x": 145, + "y": 76 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 167, + "y": 36 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 237, + "y": 51 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 62, + "y": 57 + }, + "endPoint": { + "x": 82, + "y": 57 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ], + "container": "B" + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": 82, + "y": 102 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": 62, + "y": 99 + }, + "endPoint": { + "x": 225, + "y": 99 + }, + "bendPoints": [ + { + "x": 112, + "y": 99 + }, + { + "x": 112, + "y": 124 + }, + { + "x": 215, + "y": 124 + }, + { + "x": 215, + "y": 99 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ], + "container": "A" + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": 62, + "y": 69 + }, + "endPoint": { + "x": 133, + "y": 69 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ], + "container": "A" + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": -10, + "y": 57 + }, + "endPoint": { + "x": 12, + "y": 57 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ], + "container": "B" + } + ], + "x": 155, + "y": 24, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + ''' + + val outputRP = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "ROOT", + "org.eclipse.elk.json.edgeCoords": "PARENT" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 24, + "y": 51 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 155, + "y": 24 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": 134, + "y": 63 + } + ], + "x": 145, + "y": 76 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 167, + "y": 36 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 237, + "y": 51 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 62, + "y": 57 + }, + "endPoint": { + "x": 82, + "y": 57 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ] + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": -61, + "y": 90 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": -81, + "y": 87 + }, + "endPoint": { + "x": 82, + "y": 87 + }, + "bendPoints": [ + { + "x": -31, + "y": 87 + }, + { + "x": -31, + "y": 112 + }, + { + "x": 72, + "y": 112 + }, + { + "x": 72, + "y": 87 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ] + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": -81, + "y": 57 + }, + "endPoint": { + "x": -10, + "y": 57 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ] + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": -10, + "y": 57 + }, + "endPoint": { + "x": 12, + "y": 57 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ] + } + ], + "x": 155, + "y": 24, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + + ''' + + val outputRR = ''' + { + "id": "root", + "properties": { + "algorithm": "layered", + "org.eclipse.elk.hierarchyHandling": "INCLUDE_CHILDREN", + "org.eclipse.elk.json.shapeCoords": "ROOT", + "org.eclipse.elk.json.edgeCoords": "ROOT" + }, + "children": [ + { + "id": "A", + "children": [ + { + "id": "x", + "width": 50, + "height": 90, + "x": 24, + "y": 51 + }, + { + "id": "B", + "labels": [ + { + "text": "B", + "width": 10, + "height": 12, + "x": 155, + "y": 24 + } + ], + "ports": [ + { + "id": "p", + "width": 10, + "height": 10, + "labels": [ + { + "text": "p", + "width": 10, + "height": 12, + "x": 134, + "y": 63 + } + ], + "x": 145, + "y": 76 + } + ], + "children": [ + { + "id": "y", + "width": 50, + "height": 90, + "x": 167, + "y": 36 + }, + { + "id": "z", + "width": 50, + "height": 90, + "x": 237, + "y": 51 + } + ], + "edges": [ + { + "id": "e1", + "sources": [ + "y" + ], + "targets": [ + "z" + ], + "sections": [ + { + "id": "e1_s0", + "startPoint": { + "x": 217, + "y": 81 + }, + "endPoint": { + "x": 237, + "y": 81 + }, + "incomingShape": "y", + "outgoingShape": "z" + } + ] + }, + { + "id": "e2", + "sources": [ + "x" + ], + "targets": [ + "z" + ], + "labels": [ + { + "text": "e2", + "width": 20, + "height": 12, + "x": 94, + "y": 114 + } + ], + "sections": [ + { + "id": "e2_s0", + "startPoint": { + "x": 74, + "y": 111 + }, + "endPoint": { + "x": 237, + "y": 111 + }, + "bendPoints": [ + { + "x": 124, + "y": 111 + }, + { + "x": 124, + "y": 136 + }, + { + "x": 227, + "y": 136 + }, + { + "x": 227, + "y": 111 + } + ], + "incomingShape": "x", + "outgoingShape": "z" + } + ] + }, + { + "id": "e3", + "sources": [ + "x" + ], + "targets": [ + "p" + ], + "sections": [ + { + "id": "e3_s0", + "startPoint": { + "x": 74, + "y": 81 + }, + "endPoint": { + "x": 145, + "y": 81 + }, + "incomingShape": "x", + "outgoingShape": "p" + } + ] + }, + { + "id": "e4", + "sources": [ + "p" + ], + "targets": [ + "y" + ], + "sections": [ + { + "id": "e4_s0", + "startPoint": { + "x": 145, + "y": 81 + }, + "endPoint": { + "x": 167, + "y": 81 + }, + "incomingShape": "p", + "outgoingShape": "y" + } + ] + } + ], + "x": 155, + "y": 24, + "width": 144, + "height": 129 + } + ], + "x": 12, + "y": 12, + "width": 299, + "height": 153 + } + ], + "x": 0, + "y": 0, + "width": 323, + "height": 177 + } + ''' +} diff --git a/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/SectionsTest.xtend b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/SectionsTest.xtend index 4528cf3662..aa5d953b53 100644 --- a/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/SectionsTest.xtend +++ b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/SectionsTest.xtend @@ -100,8 +100,7 @@ class SectionsTest { // string -> json - val parser = new JsonParser - val json = parser.parse(graph).asJsonObject + val json = JsonParser.parseString(graph).asJsonObject // json -> elk val importer = new Maybe() @@ -159,8 +158,8 @@ class SectionsTest { */ private def toJsonGraphAndBackToString(String graph) { // string -> json - val parser = new JsonParser - val json = parser.parse(graph).asJsonObject + val json = JsonParser.parseString(graph).asJsonObject + // json -> elk val importer = new Maybe() val root = ElkGraphJson.forGraph(json).rememberImporter(importer).toElk diff --git a/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/TransferLayoutTest.xtend b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/TransferLayoutTest.xtend index 1c3def9dbf..08787d15bc 100644 --- a/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/TransferLayoutTest.xtend +++ b/test/org.eclipse.elk.graph.json.test/src/org/eclipse/elk/graph/json/test/TransferLayoutTest.xtend @@ -63,8 +63,7 @@ class TransferLayoutTest { @Test def void transferLayoutEdgeSectionExistsTest() { - val parser = new JsonParser() - val jsonGraph = parser.parse(graph).asJsonObject + val jsonGraph = JsonParser.parseString(graph).asJsonObject val mby = new Maybe val root = ElkGraphJson.forGraph(jsonGraph)