Search in sources :

Example 11 with ElkEdge

use of org.eclipse.elk.graph.ElkEdge in project elk by eclipse.

the class GraphRenderingCanvas method calculateRequiredCanvasSizeAndBaseOffset.

/**
 * Sets size of the canvas by looking up the biggest distances between graph elements in x- and y-direction
 * and return a KVector with the required offset of the origin coordinates to fit all elements on the canvas.
 *
 * @param graph to be painted
 */
private KVector calculateRequiredCanvasSizeAndBaseOffset(final ElkNode graph) {
    if (graph != null) {
        double minX = Double.MAX_VALUE;
        double maxX = Double.MIN_VALUE;
        double minY = Double.MAX_VALUE;
        double maxY = Double.MIN_VALUE;
        // check all nodes for their coordinates
        for (ElkNode node : graph.getChildren()) {
            minX = Math.min(minX, node.getX());
            maxX = Math.max(maxX, node.getX() + node.getWidth());
            minY = Math.min(minY, node.getY());
            maxY = Math.max(maxY, node.getY() + node.getHeight());
            // check node labels
            for (ElkLabel nodeLabel : node.getLabels()) {
                minX = Math.min(minX, node.getX() + nodeLabel.getX());
                maxX = Math.max(maxX, node.getX() + nodeLabel.getX() + nodeLabel.getWidth());
                minY = Math.min(minY, node.getY() + nodeLabel.getY());
                maxY = Math.max(maxY, node.getY() + nodeLabel.getY() + nodeLabel.getHeight());
            }
        }
        for (ElkEdge edge : graph.getContainedEdges()) {
            // check all sections of the edges for their coordinates
            for (ElkEdgeSection edgeSection : edge.getSections()) {
                minX = Math.min(minX, edgeSection.getStartX());
                minY = Math.min(minY, edgeSection.getStartY());
                maxX = Math.max(maxX, edgeSection.getStartX());
                maxY = Math.max(maxY, edgeSection.getStartY());
                minX = Math.min(minX, edgeSection.getEndX());
                minY = Math.min(minY, edgeSection.getEndY());
                maxX = Math.max(maxX, edgeSection.getEndX());
                maxY = Math.max(maxY, edgeSection.getEndY());
                for (ElkBendPoint bendPoint : edgeSection.getBendPoints()) {
                    minX = Math.min(minX, bendPoint.getX());
                    minY = Math.min(minY, bendPoint.getY());
                    maxX = Math.max(maxX, bendPoint.getX());
                    maxY = Math.max(maxY, bendPoint.getY());
                }
            }
            // check edge labels
            for (ElkLabel edgeLabel : edge.getLabels()) {
                minX = Math.min(minX, edgeLabel.getX());
                maxX = Math.max(maxX, edgeLabel.getX() + edgeLabel.getWidth());
                minY = Math.min(minY, edgeLabel.getY());
                maxY = Math.max(maxY, edgeLabel.getY() + edgeLabel.getHeight());
            }
        }
        int x = ((int) (Math.max(graph.getWidth(), maxX) - Math.min(0, minX))) + 1;
        int y = ((int) (Math.max(graph.getHeight(), maxY) - Math.min(0, minY))) + 1;
        setSize(new Point(x, y));
        return new KVector((-Math.min(0, minX)), (-Math.min(0, minY)));
    }
    return new KVector();
}
Also used : ElkNode(org.eclipse.elk.graph.ElkNode) ElkLabel(org.eclipse.elk.graph.ElkLabel) ElkBendPoint(org.eclipse.elk.graph.ElkBendPoint) Point(org.eclipse.swt.graphics.Point) ElkBendPoint(org.eclipse.elk.graph.ElkBendPoint) KVector(org.eclipse.elk.core.math.KVector) ElkEdgeSection(org.eclipse.elk.graph.ElkEdgeSection) Point(org.eclipse.swt.graphics.Point) ElkBendPoint(org.eclipse.elk.graph.ElkBendPoint) ElkEdge(org.eclipse.elk.graph.ElkEdge)

Example 12 with ElkEdge

use of org.eclipse.elk.graph.ElkEdge in project elk by eclipse.

the class RecursiveGraphLayoutEngine method postProcessInsideSelfLoops.

/**
 * Post-processes self loops routed inside by offsetting their coordinates by the coordinates of
 * their parent node. The post processing is necessary since the self loop coordinates are
 * relative to their parent node's upper left corner since, at that point, the parent node's
 * final coordinates are not determined yet.
 *
 * @param insideSelfLoops
 *            list of inside self loops to post-process.
 */
protected void postProcessInsideSelfLoops(final List<ElkEdge> insideSelfLoops) {
    for (final ElkEdge selfLoop : insideSelfLoops) {
        // MIGRATE Adapt to hyperedges and make error-safe
        final ElkConnectableShape node = ElkGraphUtil.connectableShapeToNode(selfLoop.getSources().get(0));
        final double xOffset = node.getX();
        final double yOffset = node.getY();
        // Offset the edge coordinates by the node's position
        // MIGRATE Adapt to hyperedges. Also, what about multiple edge sections?
        ElkEdgeSection section = selfLoop.getSections().get(0);
        section.setStartLocation(section.getStartX() + xOffset, section.getStartY() + yOffset);
        section.setEndLocation(section.getEndX() + xOffset, section.getEndY() + yOffset);
        for (final ElkBendPoint bend : section.getBendPoints()) {
            bend.set(bend.getX() + xOffset, bend.getY() + yOffset);
        }
        // Offset junction points by the node position
        selfLoop.getProperty(CoreOptions.JUNCTION_POINTS).offset(xOffset, yOffset);
    }
}
Also used : ElkBendPoint(org.eclipse.elk.graph.ElkBendPoint) ElkConnectableShape(org.eclipse.elk.graph.ElkConnectableShape) ElkEdgeSection(org.eclipse.elk.graph.ElkEdgeSection) ElkEdge(org.eclipse.elk.graph.ElkEdge)

Example 13 with ElkEdge

use of org.eclipse.elk.graph.ElkEdge in project elk by eclipse.

the class RecursiveGraphLayoutEngine method layoutRecursively.

/**
 * Recursive function to enable layout of hierarchy. The leafs are laid out first to use their
 * layout information in the levels above.
 *
 * <p>This method returns self loops routed inside the given layout node. Those will have
 * coordinates relative to the node's top left corner, which is incorrect. Once the node's
 * final coordinates in its container are determined, any inside self loops will have to be offset
 * by the node's position.</p>
 *
 * @param layoutNode the node with children to be laid out
 * @param testController an optional test controller if this layout run is part of a unit test
 * @param progressMonitor monitor used to keep track of progress
 * @return list of self loops routed inside the node.
 */
protected List<ElkEdge> layoutRecursively(final ElkNode layoutNode, final TestController testController, final IElkProgressMonitor progressMonitor) {
    if (progressMonitor.isCanceled()) {
        return Collections.emptyList();
    }
    // Check if the node should be laid out at all
    if (layoutNode.getProperty(CoreOptions.NO_LAYOUT)) {
        return Collections.emptyList();
    }
    // We have to process the node if it has children...
    final boolean hasChildren = !layoutNode.getChildren().isEmpty();
    // ...or if inside self loop processing is enabled and it actually has inside self loops
    final List<ElkEdge> insideSelfLoops = gatherInsideSelfLoops(layoutNode);
    final boolean hasInsideSelfLoops = !insideSelfLoops.isEmpty();
    if (hasChildren || hasInsideSelfLoops) {
        // Fetch the layout algorithm that should be used to compute a layout for its content
        final LayoutAlgorithmData algorithmData = layoutNode.getProperty(CoreOptions.RESOLVED_ALGORITHM);
        if (algorithmData == null) {
            throw new UnsupportedConfigurationException("Resolved algorithm is not set;" + " apply a LayoutAlgorithmResolver before computing layout.");
        }
        final boolean supportsInsideSelfLoops = algorithmData.supportsFeature(GraphFeature.INSIDE_SELF_LOOPS);
        // Persist the Hierarchy Handling in the nodes by querying the parent node
        evaluateHierarchyHandlingInheritance(layoutNode);
        // algorithm doesn't actually support inside self loops, we cancel
        if (!hasChildren && hasInsideSelfLoops && !supportsInsideSelfLoops) {
            return Collections.emptyList();
        }
        // We collect inside self loops of children and post-process them later
        List<ElkEdge> childrenInsideSelfLoops = Lists.newArrayList();
        // If the layout provider supports hierarchy, it is expected to layout the node's compound
        // node children as well
        int nodeCount;
        if (layoutNode.getProperty(CoreOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN && (algorithmData.supportsFeature(GraphFeature.COMPOUND) || algorithmData.supportsFeature(GraphFeature.CLUSTERS))) {
            // The layout algorithm will compute a layout for multiple levels of hierarchy under the current one
            nodeCount = countNodesWithHierarchy(layoutNode);
            // Look for nodes that stop the hierarchy handling, evaluating the inheritance on the way
            final Queue<ElkNode> nodeQueue = Lists.newLinkedList();
            nodeQueue.addAll(layoutNode.getChildren());
            while (!nodeQueue.isEmpty()) {
                ElkNode node = nodeQueue.poll();
                // Persist the Hierarchy Handling in every case. (Won't hurt with nodes that are
                // evaluated in the next recursion)
                evaluateHierarchyHandlingInheritance(node);
                final boolean stopHierarchy = node.getProperty(CoreOptions.HIERARCHY_HANDLING) == HierarchyHandling.SEPARATE_CHILDREN;
                // In that case, a separate recursive call is used for child nodes
                if (stopHierarchy || (node.hasProperty(CoreOptions.ALGORITHM) && !algorithmData.equals(node.getProperty(CoreOptions.RESOLVED_ALGORITHM)))) {
                    List<ElkEdge> childLayoutSelfLoops = layoutRecursively(node, testController, progressMonitor);
                    childrenInsideSelfLoops.addAll(childLayoutSelfLoops);
                    // Explicitly disable hierarchical layout for the child node. Simplifies the
                    // handling of switching algorithms in the layouter.
                    node.setProperty(CoreOptions.HIERARCHY_HANDLING, HierarchyHandling.SEPARATE_CHILDREN);
                    // Apply the LayoutOptions.SCALE_FACTOR if present
                    ElkUtil.applyConfiguredNodeScaling(node);
                } else {
                    // Child should be included in current layout, possibly adding its own children
                    nodeQueue.addAll(node.getChildren());
                }
            }
        } else {
            // Layout each compound node contained in this node separately
            nodeCount = layoutNode.getChildren().size();
            for (ElkNode child : layoutNode.getChildren()) {
                List<ElkEdge> childLayoutSelfLoops = layoutRecursively(child, testController, progressMonitor);
                childrenInsideSelfLoops.addAll(childLayoutSelfLoops);
                // Apply the LayoutOptions.SCALE_FACTOR if present
                ElkUtil.applyConfiguredNodeScaling(child);
            }
        }
        if (progressMonitor.isCanceled()) {
            return Collections.emptyList();
        }
        // from being laid out again
        for (final ElkEdge selfLoop : childrenInsideSelfLoops) {
            selfLoop.setProperty(CoreOptions.NO_LAYOUT, true);
        }
        executeAlgorithm(layoutNode, algorithmData, testController, progressMonitor.subTask(nodeCount));
        // Post-process the inner self loops we collected
        postProcessInsideSelfLoops(childrenInsideSelfLoops);
        // Return our own inside self loops to be processed later
        if (hasInsideSelfLoops && supportsInsideSelfLoops) {
            return insideSelfLoops;
        } else {
            return Collections.emptyList();
        }
    } else {
        return Collections.emptyList();
    }
}
Also used : LayoutAlgorithmData(org.eclipse.elk.core.data.LayoutAlgorithmData) ElkNode(org.eclipse.elk.graph.ElkNode) ElkBendPoint(org.eclipse.elk.graph.ElkBendPoint) ElkEdge(org.eclipse.elk.graph.ElkEdge)

Example 14 with ElkEdge

use of org.eclipse.elk.graph.ElkEdge in project elk by eclipse.

the class AllowNonFlowPortsToSwitchSidesTest method testOnlySwitchPortSidesIfPermitted.

@Test
public void testOnlySwitchPortSidesIfPermitted() {
    final ElkNode graph = createSimpleGraph(portConstraints, node1MaySwitchPortSides, node2MaySwitchPortSides);
    final ElkEdge e1 = graph.getContainedEdges().get(0);
    final ElkEdge e2 = graph.getContainedEdges().get(1);
    layeredLayout.layout(graph, new NullElkProgressMonitor());
    assertEquals(edgesIntersect, intersect(e1, e2));
}
Also used : NullElkProgressMonitor(org.eclipse.elk.core.util.NullElkProgressMonitor) ElkNode(org.eclipse.elk.graph.ElkNode) ElkEdge(org.eclipse.elk.graph.ElkEdge) Test(org.junit.Test)

Example 15 with ElkEdge

use of org.eclipse.elk.graph.ElkEdge in project elk by eclipse.

the class NextToPortLabelPlacementTest method shouldLabelBePlacedNextToPort.

private boolean shouldLabelBePlacedNextToPort(final ElkPort port, final boolean insideLabelPlacement) {
    // Shortcut: if there is no incident edge, we can place the label next to the port
    if (port.getIncomingEdges().isEmpty() && port.getOutgoingEdges().isEmpty()) {
        return true;
    }
    // Iterate over all edges and check whether there are (i) edges that connect to the parent node's insides, and
    // (ii) edges that connect to somewhere else
    boolean edgesToInsides = false;
    boolean edgesToSomewhereElse = false;
    for (ElkEdge outEdge : port.getOutgoingEdges()) {
        boolean insideEdge = ElkGraphUtil.isDescendant(ElkGraphUtil.connectableShapeToNode(outEdge.getTargets().get(0)), port.getParent());
        edgesToInsides |= insideEdge;
        edgesToSomewhereElse |= !insideEdge;
    }
    for (ElkEdge inEdge : port.getIncomingEdges()) {
        boolean insideEdge = ElkGraphUtil.isDescendant(ElkGraphUtil.connectableShapeToNode(inEdge.getSources().get(0)), port.getParent());
        edgesToInsides |= insideEdge;
        edgesToSomewhereElse |= !insideEdge;
    }
    return (insideLabelPlacement && !edgesToInsides) || (!insideLabelPlacement && !edgesToSomewhereElse);
}
Also used : ElkEdge(org.eclipse.elk.graph.ElkEdge)

Aggregations

ElkEdge (org.eclipse.elk.graph.ElkEdge)73 ElkNode (org.eclipse.elk.graph.ElkNode)59 ElkPort (org.eclipse.elk.graph.ElkPort)27 KVector (org.eclipse.elk.core.math.KVector)23 ElkEdgeSection (org.eclipse.elk.graph.ElkEdgeSection)21 ElkLabel (org.eclipse.elk.graph.ElkLabel)20 ElkConnectableShape (org.eclipse.elk.graph.ElkConnectableShape)10 LinkedList (java.util.LinkedList)9 ElkBendPoint (org.eclipse.elk.graph.ElkBendPoint)9 ElkGraphElement (org.eclipse.elk.graph.ElkGraphElement)9 SizeConstraint (org.eclipse.elk.core.options.SizeConstraint)8 Test (org.junit.Test)8 ArrayList (java.util.ArrayList)7 List (java.util.List)6 ElkPadding (org.eclipse.elk.core.math.ElkPadding)6 KVectorChain (org.eclipse.elk.core.math.KVectorChain)5 HashSet (java.util.HashSet)4 Lists (com.google.common.collect.Lists)3 Collection (java.util.Collection)3 Set (java.util.Set)3