Search in sources :

Example 36 with LEdge

use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.

the class GreedyCycleBreaker method updateNeighbors.

/**
 * Updates indegree and outdegree values of the neighbors of the given node,
 * simulating its removal from the graph. the sources and sinks lists are
 * also updated.
 *
 * @param node node for which neighbors are updated
 */
private void updateNeighbors(final LNode node) {
    for (LPort port : node.getPorts()) {
        for (LEdge edge : port.getConnectedEdges()) {
            LPort connectedPort = edge.getSource() == port ? edge.getTarget() : edge.getSource();
            LNode endpoint = connectedPort.getNode();
            // exclude self-loops
            if (node == endpoint) {
                continue;
            }
            int priority = edge.getProperty(LayeredOptions.PRIORITY_DIRECTION);
            if (priority < 0) {
                priority = 0;
            }
            int index = endpoint.id;
            if (mark[index] == 0) {
                if (edge.getTarget() == connectedPort) {
                    indeg[index] -= priority + 1;
                    if (indeg[index] <= 0 && outdeg[index] > 0) {
                        sources.add(endpoint);
                    }
                } else {
                    outdeg[index] -= priority + 1;
                    if (outdeg[index] <= 0 && indeg[index] > 0) {
                        sinks.add(endpoint);
                    }
                }
            }
        }
    }
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode)

Example 37 with LEdge

use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.

the class InteractiveCycleBreaker method process.

@Override
public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) {
    monitor.begin("Interactive cycle breaking", 1);
    // gather edges that point to the wrong direction
    List<LEdge> revEdges = Lists.newArrayList();
    for (LNode source : layeredGraph.getLayerlessNodes()) {
        source.id = 1;
        double sourcex = source.getInteractiveReferencePoint().x;
        for (LPort port : source.getPorts(PortType.OUTPUT)) {
            for (LEdge edge : port.getOutgoingEdges()) {
                LNode target = edge.getTarget().getNode();
                if (target != source) {
                    double targetx = target.getInteractiveReferencePoint().x;
                    if (targetx < sourcex) {
                        revEdges.add(edge);
                    }
                }
            }
        }
    }
    // reverse the gathered edges
    for (LEdge edge : revEdges) {
        edge.reverse(layeredGraph, true);
    }
    // perform an additional check for cycles - maybe we missed something
    // (could happen if some nodes have the same horizontal position)
    revEdges.clear();
    for (LNode node : layeredGraph.getLayerlessNodes()) {
        // unvisited nodes have id = 1
        if (node.id > 0) {
            findCycles(node, revEdges);
        }
    }
    // again, reverse the edges that were marked
    for (LEdge edge : revEdges) {
        edge.reverse(layeredGraph, true);
    }
    revEdges.clear();
    monitor.done();
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode)

Example 38 with LEdge

use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.

the class CoffmanGrahamLayerer method dfs.

private void dfs(final LNode start, final LNode v) {
    if (nodeMark[v.id]) {
        return;
    }
    for (LEdge out : v.getOutgoingEdges()) {
        LNode w = out.getTarget().getNode();
        for (LEdge transitive : w.getIncomingEdges()) {
            if (transitive.getSource().getNode() == start) {
                edgeMark[transitive.id] = true;
            }
        }
        dfs(start, w);
    }
    nodeMark[v.id] = true;
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LNode(org.eclipse.elk.alg.layered.graph.LNode)

Example 39 with LEdge

use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.

the class ElkGraphImporter method transformEdge.

// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Edge Transformation
/**
 * Transforms the given edge if it's not a hyperedge. If it is a hyperedge, throws an exception.
 *
 * @param elkedge the edge to transform
 * @param elkparent the node in the original graph which currently gets transformed into {@code lgraph}
 * @param lgraph the layered graph
 * @return the transformed edge, or {@code null} if it cannot be transformed
 * @throws UnsupportedGraphException if the edge is a hyperedge.
 */
private LEdge transformEdge(final ElkEdge elkedge, final ElkNode elkparent, final LGraph lgraph) {
    checkEdgeValidity(elkedge);
    // Get a few basic information about the edge
    ElkConnectableShape elkSourceShape = elkedge.getSources().get(0);
    ElkConnectableShape elkTargetShape = elkedge.getTargets().get(0);
    ElkNode elkSourceNode = ElkGraphUtil.connectableShapeToNode(elkSourceShape);
    ElkNode elkTargetNode = ElkGraphUtil.connectableShapeToNode(elkTargetShape);
    ElkEdgeSection edgeSection = elkedge.getSections().isEmpty() ? null : elkedge.getSections().get(0);
    // Find the transformed source and target nodes
    LNode sourceLNode = (LNode) nodeAndPortMap.get(elkSourceNode);
    LNode targetLNode = (LNode) nodeAndPortMap.get(elkTargetNode);
    LPort sourceLPort = null;
    LPort targetLPort = null;
    // Find the transformed source port, if any
    if (elkSourceShape instanceof ElkPort) {
        // If the ElkPort is a regular port, it will map to an LPort; if it's an external port, it
        // will map to an LNode
        LGraphElement sourceElem = nodeAndPortMap.get(elkSourceShape);
        if (sourceElem instanceof LPort) {
            sourceLPort = (LPort) sourceElem;
        } else if (sourceElem instanceof LNode) {
            sourceLNode = (LNode) sourceElem;
            sourceLPort = sourceLNode.getPorts().get(0);
        }
    }
    // Find the transformed target port, if any
    if (elkTargetShape instanceof ElkPort) {
        // If the ElkPort is a regular port, it will map to an LPort; if it's an external port, it
        // will map to an LNode
        LGraphElement targetElem = nodeAndPortMap.get(elkTargetShape);
        if (targetElem instanceof LPort) {
            targetLPort = (LPort) targetElem;
        } else if (targetElem instanceof LNode) {
            targetLNode = (LNode) targetElem;
            targetLPort = targetLNode.getPorts().get(0);
        }
    }
    // reason, we back out
    if (sourceLNode == null || targetLNode == null) {
        throw new UnsupportedGraphException("The source or the target of edge " + elkedge + " could not be found. " + "This usually happens when an edge connects a node laid out by ELK Layered to a node in " + "another level of hierarchy laid out by either another instance of ELK Layered or another " + "layout algorithm alltogether. The former can be solved by setting the hierarchyHandling " + "option to INCLUDE_CHILDREN.");
    }
    // Create a layered edge
    LEdge ledge = new LEdge();
    ledge.copyProperties(elkedge);
    ledge.setProperty(InternalProperties.ORIGIN, elkedge);
    // Clear junction points, since they are recomputed from scratch
    ledge.setProperty(LayeredOptions.JUNCTION_POINTS, null);
    // If we have a self-loop, set the appropriate graph property
    Set<GraphProperties> graphProperties = lgraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
    if (sourceLNode == targetLNode) {
        graphProperties.add(GraphProperties.SELF_LOOPS);
    }
    // Create source and target ports if they do not exist yet
    if (sourceLPort == null) {
        PortType portType = PortType.OUTPUT;
        KVector sourcePoint = null;
        if (edgeSection != null && sourceLNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
            sourcePoint = new KVector(edgeSection.getStartX(), edgeSection.getStartY());
            // The coordinates need to be relative to us
            ElkUtil.toAbsolute(sourcePoint, elkedge.getContainingNode());
            ElkUtil.toRelative(sourcePoint, elkparent);
            // source), we may need to adjust the coordinates
            if (ElkGraphUtil.isDescendant(elkTargetNode, elkSourceNode)) {
                // External source port: put it on the west side
                portType = PortType.INPUT;
                sourcePoint.add(sourceLNode.getPosition());
            }
        }
        sourceLPort = LGraphUtil.createPort(sourceLNode, sourcePoint, portType, lgraph);
    }
    if (targetLPort == null) {
        PortType portType = PortType.INPUT;
        KVector targetPoint = null;
        if (edgeSection != null && targetLNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
            targetPoint = new KVector(edgeSection.getEndX(), edgeSection.getEndY());
            // Adjust the coordinates
            // MIGRATE Not sure yet if this really does what we want it to do
            ElkUtil.toAbsolute(targetPoint, elkedge.getContainingNode());
            ElkUtil.toRelative(targetPoint, elkparent);
        }
        targetLPort = LGraphUtil.createPort(targetLNode, targetPoint, portType, targetLNode.getGraph());
    }
    // Finally set the source and target of the edge
    ledge.setSource(sourceLPort);
    ledge.setTarget(targetLPort);
    // If the ports have multiple incoming or outgoing edges, the HYPEREDGE property needs to be set
    if (sourceLPort.getIncomingEdges().size() > 1 || sourceLPort.getOutgoingEdges().size() > 1 || targetLPort.getIncomingEdges().size() > 1 || targetLPort.getOutgoingEdges().size() > 1) {
        graphProperties.add(GraphProperties.HYPEREDGES);
    }
    // Transform the edge's labels
    for (ElkLabel elklabel : elkedge.getLabels()) {
        if (!elklabel.getProperty(LayeredOptions.NO_LAYOUT) && !Strings.isNullOrEmpty(elklabel.getText())) {
            LLabel llabel = transformLabel(elklabel);
            ledge.getLabels().add(llabel);
            // edge label placement is actually properly defined
            switch(llabel.getProperty(LayeredOptions.EDGE_LABELS_PLACEMENT)) {
                case HEAD:
                case TAIL:
                    graphProperties.add(GraphProperties.END_LABELS);
                    break;
                case CENTER:
                    graphProperties.add(GraphProperties.CENTER_LABELS);
                    llabel.setProperty(LayeredOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.CENTER);
            }
        }
    }
    // Copy the original bend points of the edge in case they are required
    CrossingMinimizationStrategy crossMinStrat = lgraph.getProperty(LayeredOptions.CROSSING_MINIMIZATION_STRATEGY);
    NodePlacementStrategy nodePlaceStrat = lgraph.getProperty(LayeredOptions.NODE_PLACEMENT_STRATEGY);
    boolean bendPointsRequired = crossMinStrat == CrossingMinimizationStrategy.INTERACTIVE || nodePlaceStrat == NodePlacementStrategy.INTERACTIVE;
    if (edgeSection != null && !edgeSection.getBendPoints().isEmpty() && bendPointsRequired) {
        KVectorChain originalBendpoints = ElkUtil.createVectorChain(edgeSection);
        KVectorChain importedBendpoints = new KVectorChain();
        // MIGRATE We may have to do some coordinate conversion here
        for (KVector point : originalBendpoints) {
            importedBendpoints.add(new KVector(point));
        }
        ledge.setProperty(InternalProperties.ORIGINAL_BENDPOINTS, importedBendpoints);
    }
    return ledge;
}
Also used : UnsupportedGraphException(org.eclipse.elk.core.UnsupportedGraphException) NodePlacementStrategy(org.eclipse.elk.alg.layered.options.NodePlacementStrategy) ElkNode(org.eclipse.elk.graph.ElkNode) LLabel(org.eclipse.elk.alg.layered.graph.LLabel) GraphProperties(org.eclipse.elk.alg.layered.options.GraphProperties) ElkPort(org.eclipse.elk.graph.ElkPort) LGraphElement(org.eclipse.elk.alg.layered.graph.LGraphElement) LEdge(org.eclipse.elk.alg.layered.graph.LEdge) KVectorChain(org.eclipse.elk.core.math.KVectorChain) ElkConnectableShape(org.eclipse.elk.graph.ElkConnectableShape) ElkEdgeSection(org.eclipse.elk.graph.ElkEdgeSection) ElkLabel(org.eclipse.elk.graph.ElkLabel) LPort(org.eclipse.elk.alg.layered.graph.LPort) CrossingMinimizationStrategy(org.eclipse.elk.alg.layered.options.CrossingMinimizationStrategy) LNode(org.eclipse.elk.alg.layered.graph.LNode) KVector(org.eclipse.elk.core.math.KVector) PortType(org.eclipse.elk.alg.layered.options.PortType)

Example 40 with LEdge

use of org.eclipse.elk.alg.layered.graph.LEdge in project elk by eclipse.

the class ElkGraphImporter method importHierarchicalGraph.

/**
 * Imports the graph hierarchy rooted at the given graph.
 *
 * @param elkgraph
 *            graph to import.
 * @param lgraph
 *            graph to add the direct children of the current hierarchy level to.
 */
private void importHierarchicalGraph(final ElkNode elkgraph, final LGraph lgraph) {
    final Queue<ElkNode> elkGraphQueue = Lists.newLinkedList();
    Direction parentGraphDirection = lgraph.getProperty(LayeredOptions.DIRECTION);
    // Model order index for nodes
    int index = 0;
    // Transform the node's children
    elkGraphQueue.addAll(elkgraph.getChildren());
    while (!elkGraphQueue.isEmpty()) {
        ElkNode elknode = elkGraphQueue.poll();
        if (elkgraph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE || elkgraph.getProperty(LayeredOptions.CYCLE_BREAKING_STRATEGY) == CycleBreakingStrategy.MODEL_ORDER) {
            // Assign a model order to the nodes as they are read
            elknode.setProperty(InternalProperties.MODEL_ORDER, index++);
        }
        // Check if the current node is to be laid out in the first place
        boolean isNodeToBeLaidOut = !elknode.getProperty(LayeredOptions.NO_LAYOUT);
        if (isNodeToBeLaidOut) {
            // Check if there has to be an LGraph for this node (which is the case if it has children or inside
            // self-loops, and if it does not have another layout algorithm configured)
            boolean hasChildren = !elknode.getChildren().isEmpty();
            boolean hasInsideSelfLoops = hasInsideSelfLoops(elknode);
            boolean hasHierarchyHandlingEnabled = elknode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
            boolean usesElkLayered = !elknode.hasProperty(CoreOptions.ALGORITHM) || elknode.getProperty(CoreOptions.ALGORITHM).equals(LayeredOptions.ALGORITHM_ID);
            LGraph nestedGraph = null;
            if (usesElkLayered && hasHierarchyHandlingEnabled && (hasChildren || hasInsideSelfLoops)) {
                nestedGraph = createLGraph(elknode);
                nestedGraph.setProperty(LayeredOptions.DIRECTION, parentGraphDirection);
                // Apply a spacing configuration, for details see comment int #importGraph(...)
                if (nestedGraph.hasProperty(LayeredOptions.SPACING_BASE_VALUE)) {
                    LayeredSpacings.withBaseValue(nestedGraph.getProperty(LayeredOptions.SPACING_BASE_VALUE)).apply(nestedGraph);
                }
                // if the size constraints are not empty
                if (shouldCalculateMinimumGraphSize(elknode)) {
                    final LGraph finalNestedGraph = nestedGraph;
                    elknode.getPorts().stream().forEach(elkport -> ensureDefinedPortSide(finalNestedGraph, elkport));
                    calculateMinimumGraphSize(elknode, nestedGraph);
                }
            }
            // Transform da node!!!
            LGraph parentLGraph = lgraph;
            LNode parentLNode = (LNode) nodeAndPortMap.get(elknode.getParent());
            if (parentLNode != null) {
                parentLGraph = parentLNode.getNestedGraph();
            }
            LNode lnode = transformNode(elknode, parentLGraph);
            // Setup hierarchical relationships
            if (nestedGraph != null) {
                lnode.setNestedGraph(nestedGraph);
                nestedGraph.setParentNode(lnode);
                elkGraphQueue.addAll(elknode.getChildren());
            }
        }
    }
    // Model order index for edges.
    index = 0;
    // Transform the edges
    elkGraphQueue.add(elkgraph);
    while (!elkGraphQueue.isEmpty()) {
        ElkNode elkGraphNode = elkGraphQueue.poll();
        for (ElkEdge elkedge : elkGraphNode.getContainedEdges()) {
            // We don't support hyperedges
            checkEdgeValidity(elkedge);
            if (elkgraph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE || elkgraph.getProperty(LayeredOptions.CYCLE_BREAKING_STRATEGY) == CycleBreakingStrategy.MODEL_ORDER) {
                // Assign a model order to the edges as they are read
                elkedge.setProperty(InternalProperties.MODEL_ORDER, index++);
            }
            ElkNode sourceNode = ElkGraphUtil.connectableShapeToNode(elkedge.getSources().get(0));
            ElkNode targetNode = ElkGraphUtil.connectableShapeToNode(elkedge.getTargets().get(0));
            // Don't bother if either the edge or at least one of its end points are excluded from layout
            if (elkedge.getProperty(LayeredOptions.NO_LAYOUT) || sourceNode.getProperty(LayeredOptions.NO_LAYOUT) || targetNode.getProperty(LayeredOptions.NO_LAYOUT)) {
                continue;
            }
            // Check if this edge is an inside self-loop
            boolean isInsideSelfLoop = elkedge.isSelfloop() && sourceNode.getProperty(LayeredOptions.INSIDE_SELF_LOOPS_ACTIVATE) && elkedge.getProperty(LayeredOptions.INSIDE_SELF_LOOPS_YO);
            // Find the graph the edge will be placed in. Basically, if the edge is an inside
            // self loop or connects one of its end points to a descendant, the edge will be
            // placed in the graph that represents that end point's insides. Otherwise, it will
            // be placed in the current graph.
            ElkNode parentElkGraph = elkGraphNode;
            if (isInsideSelfLoop || ElkGraphUtil.isDescendant(targetNode, sourceNode)) {
                parentElkGraph = sourceNode;
            } else if (ElkGraphUtil.isDescendant(sourceNode, targetNode)) {
                parentElkGraph = targetNode;
            }
            LGraph parentLGraph = lgraph;
            LNode parentLNode = (LNode) nodeAndPortMap.get(parentElkGraph);
            if (parentLNode != null) {
                parentLGraph = parentLNode.getNestedGraph();
            }
            // Transform the edge, finally...
            LEdge ledge = transformEdge(elkedge, parentElkGraph, parentLGraph);
            // Find the graph the edge's coordinates will have to be made relative to during export. This will only
            // do something if the edge containment inside ELK Layered differs from the edge containment in the
            // ELK graph
            ledge.setProperty(InternalProperties.COORDINATE_SYSTEM_ORIGIN, findCoordinateSystemOrigin(elkedge, elkgraph, lgraph));
        }
        // We may need to look at edges contained in the current graph node's children as well.
        // this is true unless either the current graph node does not have hierarchy handling
        // enabled, or a child has another layout algorithm configured
        boolean hasHierarchyHandlingEnabled = elkGraphNode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
        if (hasHierarchyHandlingEnabled) {
            for (ElkNode elkChildGraphNode : elkGraphNode.getChildren()) {
                boolean usesElkLayered = !elkChildGraphNode.hasProperty(CoreOptions.ALGORITHM) || elkChildGraphNode.getProperty(CoreOptions.ALGORITHM).equals(LayeredOptions.ALGORITHM_ID);
                boolean partOfSameLayoutRun = elkChildGraphNode.getProperty(LayeredOptions.HIERARCHY_HANDLING) == HierarchyHandling.INCLUDE_CHILDREN;
                if (usesElkLayered && partOfSameLayoutRun) {
                    elkGraphQueue.add(elkChildGraphNode);
                }
            }
        }
    }
}
Also used : ElkNode(org.eclipse.elk.graph.ElkNode) LEdge(org.eclipse.elk.alg.layered.graph.LEdge) LNode(org.eclipse.elk.alg.layered.graph.LNode) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) Direction(org.eclipse.elk.core.options.Direction) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint) ElkEdge(org.eclipse.elk.graph.ElkEdge)

Aggregations

LEdge (org.eclipse.elk.alg.layered.graph.LEdge)148 LNode (org.eclipse.elk.alg.layered.graph.LNode)107 LPort (org.eclipse.elk.alg.layered.graph.LPort)80 Layer (org.eclipse.elk.alg.layered.graph.Layer)41 KVector (org.eclipse.elk.core.math.KVector)34 KVectorChain (org.eclipse.elk.core.math.KVectorChain)20 LLabel (org.eclipse.elk.alg.layered.graph.LLabel)17 LGraph (org.eclipse.elk.alg.layered.graph.LGraph)16 PortSide (org.eclipse.elk.core.options.PortSide)11 ArrayList (java.util.ArrayList)9 List (java.util.List)9 InternalProperties (org.eclipse.elk.alg.layered.options.InternalProperties)8 Set (java.util.Set)7 LayeredOptions (org.eclipse.elk.alg.layered.options.LayeredOptions)7 Map (java.util.Map)6 IElkProgressMonitor (org.eclipse.elk.core.util.IElkProgressMonitor)6 Lists (com.google.common.collect.Lists)5 Iterator (java.util.Iterator)5 ElkRectangle (org.eclipse.elk.core.math.ElkRectangle)5 Pair (org.eclipse.elk.core.util.Pair)5