Search in sources :

Example 21 with LGraph

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

the class ComponentsProcessor method split.

/**
 * Split the given graph into its connected components.
 *
 * @param graph an input graph with layerless nodes
 * @return a list of components that can be processed one by one
 */
public List<LGraph> split(final LGraph graph) {
    List<LGraph> result;
    // Default to the simple graph placer
    graphPlacer = simpleRowGraphPlacer;
    // Whether separate components processing is requested
    Boolean separateProperty = graph.getProperty(LayeredOptions.SEPARATE_CONNECTED_COMPONENTS);
    boolean separate = separateProperty == null || separateProperty.booleanValue();
    // Whether the graph contains external ports
    boolean extPorts = graph.getProperty(InternalProperties.GRAPH_PROPERTIES).contains(GraphProperties.EXTERNAL_PORTS);
    // The graph's external port constraints
    PortConstraints extPortConstraints = graph.getProperty(LayeredOptions.PORT_CONSTRAINTS);
    boolean compatiblePortConstraints = !extPortConstraints.isOrderFixed();
    // FREE or FIXED_SIDES.
    if (separate && (compatiblePortConstraints || !extPorts)) {
        // Set id of all nodes to 0
        for (LNode node : graph.getLayerlessNodes()) {
            node.id = 0;
        }
        // Perform DFS starting on each node, collecting connected components
        result = Lists.newArrayList();
        for (LNode node : graph.getLayerlessNodes()) {
            Pair<List<LNode>, Set<PortSide>> componentData = dfs(node, null);
            if (componentData != null) {
                LGraph newGraph = new LGraph();
                newGraph.copyProperties(graph);
                newGraph.setProperty(InternalProperties.EXT_PORT_CONNECTIONS, componentData.getSecond());
                newGraph.getPadding().copy(graph.getPadding());
                // If a minimum size was set on the original graph, setting it on the seperated graphs as well
                // might enlarge them although their combined area might not actually have fallen below the minimum
                // size; thus, remove the minimum size
                newGraph.setProperty(LayeredOptions.NODE_SIZE_MINIMUM, null);
                for (LNode n : componentData.getFirst()) {
                    newGraph.getLayerlessNodes().add(n);
                    n.setGraph(newGraph);
                }
                result.add(newGraph);
            }
        }
        if (extPorts) {
            // With external port connections, we want to use the more complex components
            // placement algorithm
            graphPlacer = componentGroupGraphPlacer;
        }
    } else {
        result = Arrays.asList(graph);
    }
    // The component with the node with the smallest order should be first.
    if (graph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) != OrderingStrategy.NONE) {
        Collections.sort(result, (g1, g2) -> {
            int g1Order = Integer.MAX_VALUE;
            for (LNode node : g1.getLayerlessNodes()) {
                if (node.hasProperty(InternalProperties.MODEL_ORDER)) {
                    g1Order = Math.min(g1Order, node.getProperty(InternalProperties.MODEL_ORDER));
                }
            }
            int g2Order = Integer.MAX_VALUE;
            for (LNode node : g2.getLayerlessNodes()) {
                if (node.hasProperty(InternalProperties.MODEL_ORDER)) {
                    g2Order = Math.min(g2Order, node.getProperty(InternalProperties.MODEL_ORDER));
                }
            }
            return Integer.compare(g1Order, g2Order);
        });
    }
    return result;
}
Also used : Set(java.util.Set) EnumSet(java.util.EnumSet) LNode(org.eclipse.elk.alg.layered.graph.LNode) List(java.util.List) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) PortConstraints(org.eclipse.elk.core.options.PortConstraints)

Example 22 with LGraph

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

the class SimpleRowGraphPlacer method combine.

@Override
public void combine(final List<LGraph> components, final LGraph target) {
    if (components.size() == 1) {
        LGraph source = components.get(0);
        if (source != target) {
            target.getLayerlessNodes().clear();
            moveGraph(target, source, 0, 0);
            target.copyProperties(source);
            target.getPadding().copy(source.getPadding());
            target.getSize().x = source.getSize().x;
            target.getSize().y = source.getSize().y;
        }
        return;
    } else if (components.isEmpty()) {
        target.getLayerlessNodes().clear();
        target.getSize().x = 0;
        target.getSize().y = 0;
        return;
    }
    assert !components.contains(target);
    // assign priorities
    for (LGraph graph : components) {
        int priority = 0;
        for (LNode node : graph.getLayerlessNodes()) {
            priority += node.getProperty(LayeredOptions.PRIORITY);
        }
        graph.id = priority;
    }
    // sort the components by their priority and size.
    // If preserve order is set, we do not consider the size.
    Collections.sort(components, new Comparator<LGraph>() {

        public int compare(final LGraph graph1, final LGraph graph2) {
            int prio = graph2.id - graph1.id;
            if (prio == 0) {
                if (graph1.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) == OrderingStrategy.NONE) {
                    double size1 = graph1.getSize().x * graph1.getSize().y;
                    double size2 = graph2.getSize().x * graph2.getSize().y;
                    return Double.compare(size1, size2);
                }
            }
            return prio;
        }
    });
    LGraph firstComponent = components.get(0);
    target.getLayerlessNodes().clear();
    target.copyProperties(firstComponent);
    // determine the maximal row width by the maximal box width and the total area
    double maxRowWidth = 0.0f;
    double totalArea = 0.0f;
    for (LGraph graph : components) {
        KVector size = graph.getSize();
        maxRowWidth = Math.max(maxRowWidth, size.x);
        totalArea += size.x * size.y;
    }
    maxRowWidth = Math.max(maxRowWidth, (float) Math.sqrt(totalArea) * target.getProperty(LayeredOptions.ASPECT_RATIO));
    double componentSpacing = target.getProperty(LayeredOptions.SPACING_COMPONENT_COMPONENT);
    // place nodes iteratively into rows
    double xpos = 0, ypos = 0, highestBox = 0, broadestRow = componentSpacing;
    for (LGraph graph : components) {
        KVector size = graph.getSize();
        if (xpos + size.x > maxRowWidth) {
            // place the graph into the next row
            xpos = 0;
            ypos += highestBox + componentSpacing;
            highestBox = 0;
        }
        KVector offset = graph.getOffset();
        offsetGraph(graph, xpos + offset.x, ypos + offset.y);
        offset.reset();
        broadestRow = Math.max(broadestRow, xpos + size.x);
        highestBox = Math.max(highestBox, size.y);
        xpos += size.x + componentSpacing;
    }
    target.getSize().x = broadestRow;
    target.getSize().y = ypos + highestBox;
    // if compaction is desired, do so!
    if (firstComponent.getProperty(LayeredOptions.COMPACTION_CONNECTED_COMPONENTS)) {
        ComponentsCompactor compactor = new ComponentsCompactor();
        compactor.compact(components, target.getSize(), componentSpacing);
        // therefore we have to use the final drawing's offset
        for (LGraph h : components) {
            h.getOffset().reset().add(compactor.getOffset());
        }
        // set the new graph size
        target.getSize().reset().add(compactor.getGraphSize());
    }
    // finally move the components to the combined graph
    moveGraphs(target, components, 0, 0);
}
Also used : LNode(org.eclipse.elk.alg.layered.graph.LNode) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) KVector(org.eclipse.elk.core.math.KVector)

Example 23 with LGraph

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

the class CompoundGraphPostprocessor method process.

@Override
public void process(final LGraph graph, final IElkProgressMonitor monitor) {
    monitor.begin("Compound graph postprocessor", 1);
    // whether bend points should be added whenever crossing a hierarchy boundary
    boolean addUnnecessaryBendpoints = graph.getProperty(LayeredOptions.UNNECESSARY_BENDPOINTS);
    // restore the cross-hierarchy map that was built by the preprocessor
    Multimap<LEdge, CrossHierarchyEdge> crossHierarchyMap = graph.getProperty(InternalProperties.CROSS_HIERARCHY_MAP);
    // remember all dummy edges we encounter; these need to be removed at the end
    Set<LEdge> dummyEdges = Sets.newHashSet();
    // iterate over all original edges
    for (LEdge origEdge : crossHierarchyMap.keySet()) {
        // find all cross-hierarchy edges the original edge was split into, and sort them from source to target
        List<CrossHierarchyEdge> crossHierarchyEdges = new ArrayList<CrossHierarchyEdge>(crossHierarchyMap.get(origEdge));
        Collections.sort(crossHierarchyEdges, new CrossHierarchyEdgeComparator(graph));
        // find the original source and target ports for the original edge
        LPort sourcePort = crossHierarchyEdges.get(0).getActualSource();
        LPort targetPort = crossHierarchyEdges.get(crossHierarchyEdges.size() - 1).getActualTarget();
        // determine the reference graph for all bend points
        LNode referenceNode = sourcePort.getNode();
        LGraph referenceGraph;
        if (LGraphUtil.isDescendant(targetPort.getNode(), referenceNode)) {
            referenceGraph = referenceNode.getNestedGraph();
        } else {
            referenceGraph = referenceNode.getGraph();
        }
        // check whether there are any junction points
        KVectorChain junctionPoints = clearJunctionPoints(origEdge, crossHierarchyEdges);
        // reset bend points (we have computed new ones anyway)
        origEdge.getBendPoints().clear();
        // apply the computed layouts to the cross-hierarchy edge
        KVector lastPoint = null;
        for (CrossHierarchyEdge chEdge : crossHierarchyEdges) {
            // transform all coordinates from the graph of the dummy edge to the reference graph
            KVector offset = new KVector();
            LGraphUtil.changeCoordSystem(offset, chEdge.getGraph(), referenceGraph);
            LEdge ledge = chEdge.getEdge();
            KVectorChain bendPoints = new KVectorChain();
            bendPoints.addAllAsCopies(0, ledge.getBendPoints());
            bendPoints.offset(offset);
            // Note: if an NPE occurs here, that means ELK Layered has replaced the original edge
            KVector sourcePoint = new KVector(ledge.getSource().getAbsoluteAnchor());
            KVector targetPoint = new KVector(ledge.getTarget().getAbsoluteAnchor());
            sourcePoint.add(offset);
            targetPoint.add(offset);
            if (lastPoint != null) {
                KVector nextPoint;
                if (bendPoints.isEmpty()) {
                    nextPoint = targetPoint;
                } else {
                    nextPoint = bendPoints.getFirst();
                }
                // we add the source point as a bend point to properly connect the hierarchy levels
                // either if the last point of the previous hierarchy edge segment is a certain
                // level of tolerance away or if we are required to add unnecessary bend points
                boolean xDiffEnough = Math.abs(lastPoint.x - nextPoint.x) > OrthogonalRoutingGenerator.TOLERANCE;
                boolean yDiffEnough = Math.abs(lastPoint.y - nextPoint.y) > OrthogonalRoutingGenerator.TOLERANCE;
                if ((!addUnnecessaryBendpoints && xDiffEnough && yDiffEnough) || (addUnnecessaryBendpoints && (xDiffEnough || yDiffEnough))) {
                    origEdge.getBendPoints().add(sourcePoint);
                }
            }
            origEdge.getBendPoints().addAll(bendPoints);
            if (bendPoints.isEmpty()) {
                lastPoint = sourcePoint;
            } else {
                lastPoint = bendPoints.getLast();
            }
            // copy junction points
            copyJunctionPoints(ledge, junctionPoints, offset);
            // add offset to target port with a special property
            if (chEdge.getActualTarget() == targetPort) {
                if (targetPort.getNode().getGraph() != chEdge.getGraph()) {
                    // the target port is in a different coordinate system -- recompute the offset
                    offset = new KVector();
                    LGraphUtil.changeCoordSystem(offset, targetPort.getNode().getGraph(), referenceGraph);
                }
                origEdge.setProperty(InternalProperties.TARGET_OFFSET, offset);
            }
            // copy labels back to the original edge
            copyLabelsBack(ledge, origEdge, referenceGraph);
            // remember the dummy edge for later removal (dummy edges may be in use by several
            // different original edges, which is why we cannot just go and remove it now)
            dummyEdges.add(ledge);
        }
        // restore the original source port and target port
        origEdge.setSource(sourcePort);
        origEdge.setTarget(targetPort);
    }
    // remove the dummy edges from the graph (dummy ports and dummy nodes are retained)
    for (LEdge dummyEdge : dummyEdges) {
        dummyEdge.setSource(null);
        dummyEdge.setTarget(null);
    }
    monitor.done();
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) KVectorChain(org.eclipse.elk.core.math.KVectorChain) ArrayList(java.util.ArrayList) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode) KVector(org.eclipse.elk.core.math.KVector)

Example 24 with LGraph

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

the class ElkGraphLayoutTransferrer method applyLayout.

/**
 * Applies the layout information contained in the given LGraph to the ElkGraph elements it was
 * created from. All source ElkGraph elements are expected to be accessible through their LGraph
 * counterparts through the {@link InternalProperties#ORIGIN} property.
 *
 * @param lgraph the LGraph whose layout information to apply.
 */
public void applyLayout(final LGraph lgraph) {
    Object graphOrigin = lgraph.getProperty(InternalProperties.ORIGIN);
    if (!(graphOrigin instanceof ElkNode)) {
        return;
    }
    // The ElkNode that represents this graph in the original ElkGraph
    ElkNode parentElkNode = (ElkNode) graphOrigin;
    // The LNode that represents this graph in the upper hierarchy level, if any
    LNode parentLNode = (LNode) lgraph.getParentNode();
    // Get the offset to be added to all coordinates
    KVector offset = new KVector(lgraph.getOffset());
    // Adjust offset (and with it the positions) by the requested padding
    LPadding lPadding = lgraph.getPadding();
    offset.x += lPadding.left;
    offset.y += lPadding.top;
    // Set node padding, if it was computed during layout
    final EnumSet<SizeOptions> sizeOptions = parentElkNode.getProperty(LayeredOptions.NODE_SIZE_OPTIONS);
    if (sizeOptions.contains(SizeOptions.COMPUTE_PADDING)) {
        ElkPadding padding = parentElkNode.getProperty(LayeredOptions.PADDING);
        padding.setBottom(lPadding.bottom);
        padding.setTop(lPadding.top);
        padding.setLeft(lPadding.left);
        padding.setRight(lPadding.right);
    }
    // Along the way, we collect the list of edges to be processed later
    List<LEdge> edgeList = Lists.newArrayList();
    // Process the nodes
    for (LNode lnode : lgraph.getLayerlessNodes()) {
        if (representsNode(lnode)) {
            applyNodeLayout(lnode, offset);
        } else if (representsExternalPort(lnode) && parentLNode == null) {
            // We have an external port here on the top-most hierarchy level of the current (possibly
            // hierarchical) layout run; set its position
            ElkPort elkport = (ElkPort) lnode.getProperty(InternalProperties.ORIGIN);
            KVector portPosition = LGraphUtil.getExternalPortPosition(lgraph, lnode, elkport.getWidth(), elkport.getHeight());
            elkport.setLocation(portPosition.x, portPosition.y);
        }
        // correctly)
        for (LPort port : lnode.getPorts()) {
            port.getOutgoingEdges().stream().filter(edge -> !LGraphUtil.isDescendant(edge.getTarget().getNode(), lnode)).forEach(edge -> edgeList.add(edge));
        }
    }
    // Collect edges that go from the current graph's representing LNode down into its descendants
    if (parentLNode != null) {
        for (LPort port : parentLNode.getPorts()) {
            port.getOutgoingEdges().stream().filter(edge -> LGraphUtil.isDescendant(edge.getTarget().getNode(), parentLNode)).forEach(edge -> edgeList.add(edge));
        }
    }
    // Iterate through all edges
    EdgeRouting routing = parentElkNode.getProperty(LayeredOptions.EDGE_ROUTING);
    for (LEdge ledge : edgeList) {
        applyEdgeLayout(ledge, routing, offset, lPadding);
    }
    // Setup the parent node
    applyParentNodeLayout(lgraph);
    // Process nested subgraphs
    for (LNode lnode : lgraph.getLayerlessNodes()) {
        LGraph nestedGraph = lnode.getNestedGraph();
        if (nestedGraph != null) {
            applyLayout(nestedGraph);
        }
    }
}
Also used : GraphProperties(org.eclipse.elk.alg.layered.options.GraphProperties) LLabel(org.eclipse.elk.alg.layered.graph.LLabel) LEdge(org.eclipse.elk.alg.layered.graph.LEdge) ElkNode(org.eclipse.elk.graph.ElkNode) ElkPort(org.eclipse.elk.graph.ElkPort) PortLabelPlacement(org.eclipse.elk.core.options.PortLabelPlacement) LayeredOptions(org.eclipse.elk.alg.layered.options.LayeredOptions) KVectorChain(org.eclipse.elk.core.math.KVectorChain) ElkUtil(org.eclipse.elk.core.util.ElkUtil) Lists(com.google.common.collect.Lists) LabelDummySwitcher(org.eclipse.elk.alg.layered.intermediate.LabelDummySwitcher) InternalProperties(org.eclipse.elk.alg.layered.options.InternalProperties) ElkPadding(org.eclipse.elk.core.math.ElkPadding) EnumSet(java.util.EnumSet) NodePlacementStrategy(org.eclipse.elk.alg.layered.options.NodePlacementStrategy) NodeFlexibility(org.eclipse.elk.alg.layered.options.NodeFlexibility) PortConstraints(org.eclipse.elk.core.options.PortConstraints) ElkLabel(org.eclipse.elk.graph.ElkLabel) KVector(org.eclipse.elk.core.math.KVector) SizeOptions(org.eclipse.elk.core.options.SizeOptions) Set(java.util.Set) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint) LGraphUtil(org.eclipse.elk.alg.layered.graph.LGraphUtil) LPadding(org.eclipse.elk.alg.layered.graph.LPadding) List(java.util.List) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) ElkEdgeSection(org.eclipse.elk.graph.ElkEdgeSection) LPort(org.eclipse.elk.alg.layered.graph.LPort) EdgeRouting(org.eclipse.elk.core.options.EdgeRouting) LNode(org.eclipse.elk.alg.layered.graph.LNode) ElkEdge(org.eclipse.elk.graph.ElkEdge) ElkGraphUtil(org.eclipse.elk.graph.util.ElkGraphUtil) ElkNode(org.eclipse.elk.graph.ElkNode) SizeOptions(org.eclipse.elk.core.options.SizeOptions) LEdge(org.eclipse.elk.alg.layered.graph.LEdge) ElkPort(org.eclipse.elk.graph.ElkPort) LPadding(org.eclipse.elk.alg.layered.graph.LPadding) EdgeRouting(org.eclipse.elk.core.options.EdgeRouting) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode) KVector(org.eclipse.elk.core.math.KVector) ElkPadding(org.eclipse.elk.core.math.ElkPadding)

Example 25 with LGraph

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

the class ElkGraphLayoutTransferrer method calculateHierarchicalOffset.

/**
 * If the coordinates of an edge must be relative to a different node than they are in the algorithm, this
 * method returns the correct offset to translate from the algorithm's coordinate system to the necessary
 * target coordinate system.
 *
 * @return the offset vector, which may simply be zero (but not {@code null}). Must not be modified.
 */
private KVector calculateHierarchicalOffset(final LEdge ledge) {
    LGraph targetCoordinateSystem = ledge.getProperty(InternalProperties.COORDINATE_SYSTEM_ORIGIN);
    if (targetCoordinateSystem != null) {
        KVector result = new KVector();
        // Edges this method is called on are always in the coordinate system of their source
        LGraph currentGraph = ledge.getSource().getNode().getGraph();
        while (currentGraph != targetCoordinateSystem) {
            // The current graph should always have an upper level if we have not reached the target graph yet;
            LNode representingNode = currentGraph.getParentNode();
            currentGraph = representingNode.getGraph();
            result.add(representingNode.getPosition()).add(currentGraph.getOffset()).add(currentGraph.getPadding().left, currentGraph.getPadding().top);
        }
        return result;
    }
    // No coordinate system conversion is required
    return ZERO_OFFSET;
}
Also used : LNode(org.eclipse.elk.alg.layered.graph.LNode) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) KVector(org.eclipse.elk.core.math.KVector)

Aggregations

LGraph (org.eclipse.elk.alg.layered.graph.LGraph)115 LNode (org.eclipse.elk.alg.layered.graph.LNode)90 LPort (org.eclipse.elk.alg.layered.graph.LPort)49 Layer (org.eclipse.elk.alg.layered.graph.Layer)38 Test (org.junit.Test)36 TestAfterProcessor (org.eclipse.elk.alg.test.framework.annotations.TestAfterProcessor)19 LEdge (org.eclipse.elk.alg.layered.graph.LEdge)17 KVector (org.eclipse.elk.core.math.KVector)15 InternalProperties (org.eclipse.elk.alg.layered.options.InternalProperties)14 LayeredOptions (org.eclipse.elk.alg.layered.options.LayeredOptions)13 List (java.util.List)11 ILayoutProcessor (org.eclipse.elk.core.alg.ILayoutProcessor)11 IElkProgressMonitor (org.eclipse.elk.core.util.IElkProgressMonitor)10 NodeType (org.eclipse.elk.alg.layered.graph.LNode.NodeType)9 Lists (com.google.common.collect.Lists)7 Set (java.util.Set)7 GraphProperties (org.eclipse.elk.alg.layered.options.GraphProperties)7 GraphInfoHolder (org.eclipse.elk.alg.layered.p3order.GraphInfoHolder)7 PortSide (org.eclipse.elk.core.options.PortSide)7 ElkNode (org.eclipse.elk.graph.ElkNode)7