Search in sources :

Example 1 with Edge

use of org.knime.workbench.ui.layout.Graph.Edge in project knime-core by knime.

the class SimpleLayeredLayouter method doLayout.

/**
 * computes an hierarchical layout of the given graph. If nodes are fixed by
 * means of the given map, they will end up on the first or last layer,
 * ordered by their original y-coordinate. Precondition: g must be a
 * directed acyclic graph!
 *
 * @param g the graph to perform layout on
 * @param fixedNodes node map containing true if the respective node should
 *            be fixed (only sources and sinks allowed)
 * @throws RuntimeException
 */
public void doLayout(final Graph g, final Map<Node, Boolean> fixedNodes) throws RuntimeException {
    // create lists for fixed sources and sinks if necessary
    ArrayList<Node> fixedSources = null;
    ArrayList<Node> fixedSinks = null;
    if (fixedNodes != null) {
        fixedSources = new ArrayList<Graph.Node>();
        fixedSinks = new ArrayList<Graph.Node>();
        for (Node n : g.nodes()) {
            if (fixedNodes.get(n)) {
                if (n.inDegree() == 0) {
                    fixedSources.add(n);
                } else if (n.outDegree() == 0) {
                    fixedSinks.add(n);
                }
            }
        }
        if (fixedSources.isEmpty()) {
            fixedSources = null;
        }
        if (fixedSinks.isEmpty()) {
            fixedSinks = null;
        }
    }
    // get layering of the graph
    Map<Node, Integer> nodeLayer = g.createIntNodeMap();
    ArrayList<ArrayList<Node>> layers = Layerer.assignLayers(g, nodeLayer, fixedSources, fixedSinks);
    // add dummy vertices for edges spanning several layers
    ArrayList<Edge> hiddenEdges = new ArrayList<Graph.Edge>();
    ArrayList<Node> dummyNodes = new ArrayList<Graph.Node>();
    ArrayList<Edge> dummyEdges = new ArrayList<Graph.Edge>();
    HashMap<Edge, ArrayList<Node>> hiddenEdgeToDummyVertices = new HashMap<Graph.Edge, ArrayList<Node>>();
    for (Edge e : g.edges()) {
        int startLayer = nodeLayer.get(e.source()).intValue();
        int endLayer = nodeLayer.get(e.target()).intValue();
        int span = endLayer - startLayer;
        if (span > 1) {
            hiddenEdges.add(e);
        }
    }
    // concurrent modification due to iterator
    for (Edge e : hiddenEdges) {
        // list for this edges dummy nodes
        ArrayList<Node> eDummyNodes = new ArrayList<Graph.Node>();
        int startLayer = nodeLayer.get(e.source()).intValue();
        int endLayer = nodeLayer.get(e.target()).intValue();
        int span = endLayer - startLayer;
        Node last = e.source();
        for (int i = 1; i < span; i++) {
            Node current = g.createNode("bend " + e + ", " + i, startLayer + i, g.getY(last));
            // add dummy to its layer
            nodeLayer.put(current, startLayer + i);
            layers.get(startLayer + i).add(current);
            // add dummy edge to graph
            Edge dEdge = g.createEdge(last, current);
            dummyEdges.add(dEdge);
            // add dummy vertex to the list of dummies for the original edge
            eDummyNodes.add(current);
            // proceed
            last = current;
        }
        // add last dummy edge
        g.createEdge(last, e.target());
        // store list of dummy nodes for original edge
        hiddenEdgeToDummyVertices.put(e, eDummyNodes);
        // add this edges dummy Nodes to the list of all dummy nodes
        dummyNodes.addAll(eDummyNodes);
    }
    // remove hidden edges
    for (Edge e : hiddenEdges) {
        g.removeEdge(e);
    }
    // set initial coordinates by layer
    int layer = 0;
    for (ArrayList<Node> currentLayer : layers) {
        // sort first and last layer by y-coordinate if fixed
        if (layer == 0 && fixedSources != null) {
            Collections.sort(currentLayer, new Util.NodeByYComparator(g));
        } else if (layer == layers.size() - 1 && fixedSinks != null) {
            Collections.sort(currentLayer, new Util.NodeByYComparator(g));
        } else {
            // here the ordering is shuffled, could also be done several
            // times in the crossing minimization phase.
            // I.e., every execution of the algorithm potentially yields
            // another result!
            Collections.shuffle(currentLayer, m_rnd);
        // ordering could also be initialized by the current ordering
        // from y-coordinates.
        // Collections.sort(currentLayer, new
        // Util.NodeByYComparator(g));
        }
        // set coordinates from 0,1,...,size of layer
        int verticalCoord = 0;
        for (Node n : currentLayer) {
            g.setCoordinates(n, layer, verticalCoord);
            verticalCoord++;
        }
        layer++;
    }
    /* Do crossing minimization */
    CrossingMinimizer cm = new CrossingMinimizer(g, layers, fixedSources, fixedSinks);
    cm.run();
    /* Do vertical placement */
    VerticalCoordinateAssigner vca = new VerticalCoordinateAssigner(g, layers, dummyNodes, dummyEdges);
    vca.setBalanceBranchings(m_balanceBranching);
    vca.run();
    /*
         * Reinsert hidden edges with bendpoints, and remove dummy nodes and
         * edges
         */
    for (Edge hEdge : hiddenEdges) {
        Edge e = g.reinsert(hEdge);
        for (Node n : hiddenEdgeToDummyVertices.get(hEdge)) {
            g.addBend(e, g.getX(n), g.getY(n));
            // also removes dummy edges!
            g.removeNode(n);
        }
    }
    // clean up unnecessary bend-points
    g.cleanBends();
}
Also used : HashMap(java.util.HashMap) Node(org.knime.workbench.ui.layout.Graph.Node) ArrayList(java.util.ArrayList) Graph(org.knime.workbench.ui.layout.Graph) Edge(org.knime.workbench.ui.layout.Graph.Edge)

Example 2 with Edge

use of org.knime.workbench.ui.layout.Graph.Edge in project knime-core by knime.

the class VerticalCoordinateAssigner method markConflicts.

/*
     * Functions needed for first phase
     */
/**
 * mark conflicting edges.
 */
private void markConflicts() {
    if (m_layers.size() < 4) {
        // no conflicts possible since there cannot be any inner segments
        return;
    }
    // next-to-last and last layer
    for (int i = 1; i < m_layers.size() - 2; i++) {
        int k0 = 0;
        int l = 0;
        for (int l1 = 0; l1 < m_layers.get(i + 1).size(); l1++) {
            Node vl1 = m_layers.get(i + 1).get(l1);
            Edge innerSegment = getInnerSegmentIncidentTo(vl1);
            if (l1 == m_layers.get(i + 1).size() - 1 || innerSegment != null) {
                int k1 = m_layers.get(i).size() - 1;
                if (innerSegment != null) {
                    k1 = m_layers.get(i).indexOf(innerSegment.opposite(vl1));
                }
                while (l <= l1) {
                    Node vl = m_layers.get(i + 1).get(l);
                    for (Edge e : m_g.inEdges(vl)) {
                        Node vk = e.opposite(vl);
                        int k = m_layers.get(i).indexOf(vk);
                        if (k < k0 || k > k1) {
                            m_marked.put(e, true);
                        }
                    }
                    l++;
                }
                k0 = k1;
            }
        }
    }
}
Also used : Node(org.knime.workbench.ui.layout.Graph.Node) Edge(org.knime.workbench.ui.layout.Graph.Edge)

Example 3 with Edge

use of org.knime.workbench.ui.layout.Graph.Edge in project knime-core by knime.

the class VerticalCoordinateAssigner method getNeighbors.

/**
 * get either left or right neighbors of a node, sorted by their current
 * y-coordinate.
 *
 * @param n
 * @param left true if left neighbors should be returned, false otherwise
 * @return
 */
private ArrayList<Node> getNeighbors(final Node n, final boolean left) {
    ArrayList<Node> neighbors = new ArrayList<Graph.Node>();
    Iterable<Edge> incidentEdges;
    if (left) {
        incidentEdges = m_g.inEdges(n);
    } else {
        incidentEdges = m_g.outEdges(n);
    }
    for (Edge e : incidentEdges) {
        neighbors.add(e.opposite(n));
    }
    // sort by order in layer
    Collections.sort(neighbors, new Util.NodeByYComparator(m_g));
    return neighbors;
}
Also used : Graph(org.knime.workbench.ui.layout.Graph) Node(org.knime.workbench.ui.layout.Graph.Node) ArrayList(java.util.ArrayList) Edge(org.knime.workbench.ui.layout.Graph.Edge)

Example 4 with Edge

use of org.knime.workbench.ui.layout.Graph.Edge in project knime-core by knime.

the class Layerer method updateSources.

/**
 * check the outgoing edges of a given node n for becoming a new source
 * after n is processed.
 *
 * @param g
 * @param n
 * @param sources
 * @param residualDegree
 */
private static void updateSources(final Graph g, Node n, ArrayList<Node> sources, Map<Node, Integer> residualDegree) {
    for (Edge e : g.outEdges(n)) {
        Node t = e.target();
        int newDegree = residualDegree.get(t).intValue() - 1;
        residualDegree.put(t, newDegree);
        if (newDegree == 0)
            sources.add(t);
    }
}
Also used : Node(org.knime.workbench.ui.layout.Graph.Node) Edge(org.knime.workbench.ui.layout.Graph.Edge)

Example 5 with Edge

use of org.knime.workbench.ui.layout.Graph.Edge in project knime-core by knime.

the class LayoutManager method doLayout.

/**
 * @param nodes the nodes that should be laid out. If null, all nodes of the
 *            workflow manager passed to the constructor are laid out.
 */
public void doLayout(final Collection<NodeContainerUI> nodes) {
    int X_STRETCH = 100;
    int Y_STRETCH = 120;
    if (WorkflowEditor.getActiveEditorSnapToGrid()) {
        if (WorkflowEditor.getActiveEditorGridX() >= 70) {
            X_STRETCH = WorkflowEditor.getActiveEditorGridX();
        } else {
            X_STRETCH = WorkflowEditor.getActiveEditorGridXOffset(X_STRETCH);
        }
        Y_STRETCH = WorkflowEditor.getActiveEditorGridYOffset(Y_STRETCH);
    }
    // add all nodes that should be laid out to the graph
    Collection<NodeContainerUI> allNodes = nodes;
    if (allNodes == null || allNodes.size() <= 1) {
        allNodes = m_wfm.getNodeContainers();
    }
    // keep the left upper corner of the node cluster.
    // Nodes laid out are placed right and below
    int minX = Integer.MAX_VALUE;
    int minY = Integer.MAX_VALUE;
    // add all nodes that are to be laid out
    for (NodeContainerUI nc : allNodes) {
        Node gNode = createGraphNodeForNC(nc);
        m_workbenchToGraphNodes.put(nc, gNode);
        NodeUIInformation ui = nc.getUIInformation();
        minX = (ui.getBounds()[0] < minX) ? ui.getBounds()[0] : minX;
        minY = (ui.getBounds()[1] < minY) ? ui.getBounds()[1] : minY;
        if (WorkflowEditor.getActiveEditorSnapToGrid()) {
            Point nextGridLocation = WorkflowEditor.getActiveEditorNextGridLocation(new Point(minX, minY));
            minX = nextGridLocation.x;
            minY = nextGridLocation.y;
        }
    }
    // find all connections that connect from/to our nodes,
    // keep a flag that states: isClusterInternal
    HashMap<ConnectionContainerUI, Boolean> allConns = new HashMap<ConnectionContainerUI, Boolean>();
    for (ConnectionContainerUI conn : m_wfm.getConnectionContainers()) {
        Node src = null;
        if (!conn.getSource().equals(m_wfm.getID())) {
            // if it's not a meta node incoming connection
            src = m_workbenchToGraphNodes.get(m_wfm.getNodeContainer(conn.getSource()));
        }
        Node dest = null;
        if (!conn.getDest().equals(m_wfm.getID())) {
            // if it is not a meta node outgoing connection
            dest = m_workbenchToGraphNodes.get(m_wfm.getNodeContainer(conn.getDest()));
        }
        boolean isInternal = (src != null && dest != null);
        // if at least one node is auto laid out we need the connection
        if (src != null || dest != null) {
            allConns.put(conn, isInternal);
        }
    }
    // Add all connections (internal and leading in/out the cluster)
    // to the graph
    Edge gEdge;
    for (ConnectionContainerUI conn : allConns.keySet()) {
        Node srcGraphNode;
        Node destGraphNode;
        if (conn.getSource().equals(m_wfm.getID())) {
            // it connects to a meta node input port:
            int portIdx = conn.getSourcePort();
            srcGraphNode = m_workbenchWFMInports.get(portIdx);
            if (srcGraphNode == null) {
                srcGraphNode = m_g.createNode("Incoming " + portIdx, 0, portIdx * Y_STRETCH);
                m_workbenchWFMInports.put(portIdx, srcGraphNode);
            }
        } else {
            NodeContainerUI s = m_wfm.getNodeContainer(conn.getSource());
            srcGraphNode = m_workbenchToGraphNodes.get(s);
            if (srcGraphNode == null) {
                // then it connects to an "outside" node
                srcGraphNode = m_workbenchIncomingNodes.get(s);
                if (srcGraphNode == null) {
                    srcGraphNode = createGraphNodeForNC(s);
                    m_workbenchIncomingNodes.put(s, srcGraphNode);
                }
            }
        // else it is a connection inside the layout cluster
        }
        if (conn.getDest().equals(m_wfm.getID())) {
            // it connects to a meta node output port
            int portIdx = conn.getDestPort();
            destGraphNode = m_workbenchWFMOutports.get(portIdx);
            if (destGraphNode == null) {
                destGraphNode = m_g.createNode("Outgoing " + portIdx, 250, portIdx * Y_STRETCH);
                m_workbenchWFMOutports.put(portIdx, destGraphNode);
            }
        } else {
            NodeContainerUI d = m_wfm.getNodeContainer(conn.getDest());
            destGraphNode = m_workbenchToGraphNodes.get(d);
            if (destGraphNode == null) {
                // then it connects to an "outside" node
                destGraphNode = m_workbenchOutgoingNodes.get(d);
                if (destGraphNode == null) {
                    destGraphNode = createGraphNodeForNC(d);
                    m_workbenchOutgoingNodes.put(d, destGraphNode);
                }
            }
        // else it is a connection within the layout cluster
        }
        gEdge = m_g.createEdge(srcGraphNode, destGraphNode);
        if (gEdge != null) {
            m_workbenchToGraphEdges.put(conn, gEdge);
            m_parallelConns.put(gEdge, new LinkedList<ConnectionContainerUI>(Collections.singletonList(conn)));
        } else {
            // a connection between these node already exists in the graph
            Edge graphEdge = srcGraphNode.getEdge(destGraphNode);
            assert graphEdge != null;
            // add the connection to list of parallel connections.
            m_parallelConns.get(graphEdge).add(conn);
        }
    }
    // AFTER creating all nodes, mark the incoming/outgoing nodes as fixed
    boolean anchorsExist = false;
    Map<Node, Boolean> anchorNodes = m_g.createBoolNodeMap();
    for (Node n : m_workbenchIncomingNodes.values()) {
        anchorsExist = true;
        anchorNodes.put(n, Boolean.TRUE);
    }
    for (Node n : m_workbenchOutgoingNodes.values()) {
        anchorsExist = true;
        anchorNodes.put(n, Boolean.TRUE);
    }
    for (Node n : m_workbenchWFMInports.values()) {
        anchorsExist = true;
        anchorNodes.put(n, Boolean.TRUE);
    }
    for (Node n : m_workbenchWFMOutports.values()) {
        anchorsExist = true;
        anchorNodes.put(n, Boolean.TRUE);
    }
    SimpleLayeredLayouter layouter = new SimpleLayeredLayouter(m_initPlacementSeed);
    layouter.setBalanceBranchings(!WorkflowEditor.getActiveEditorSnapToGrid());
    if (anchorsExist) {
        layouter.doLayout(m_g, anchorNodes);
    } else {
        layouter.doLayout(m_g, null);
    }
    // preserver the old stuff for undoers
    m_oldBendpoints = new HashMap<ConnectionID, ConnectionUIInformation>();
    m_oldCoordinates = new HashMap<NodeID, NodeUIInformation>();
    // transfer new coordinates back to nodes
    // with fixed nodes (lots of) the new coordinates of the nodes may not
    // start at 0.
    double coordOffsetX = Integer.MAX_VALUE;
    double coordOffsetY = Integer.MAX_VALUE;
    for (NodeContainerUI nc : allNodes) {
        Node gNode = m_workbenchToGraphNodes.get(nc);
        coordOffsetX = Math.min(coordOffsetX, m_g.getX(gNode));
        coordOffsetY = Math.min(coordOffsetY, m_g.getY(gNode));
    }
    for (NodeContainerUI nc : allNodes) {
        NodeUIInformation uiInfo = nc.getUIInformation();
        if (uiInfo != null) {
            Node gNode = m_workbenchToGraphNodes.get(nc);
            int[] b = uiInfo.getBounds();
            int x = (int) Math.round((m_g.getX(gNode) - coordOffsetX) * X_STRETCH) + minX;
            int y = (int) Math.round((m_g.getY(gNode) - coordOffsetY) * Y_STRETCH) + minY;
            NodeUIInformation newCoord = NodeUIInformation.builder().setNodeLocation(x, y, b[2], b[3]).setHasAbsoluteCoordinates(uiInfo.hasAbsoluteCoordinates()).setSnapToGrid(WorkflowEditor.getActiveEditorSnapToGrid()).build();
            LOGGER.debug("Node " + nc + " gets auto-layout coordinates " + newCoord);
            // save old coordinates for undo
            m_oldCoordinates.put(nc.getID(), uiInfo);
            // triggers gui update
            nc.setUIInformation(newCoord);
        }
    }
    // delete old bendpoints - transfer new ones
    for (ConnectionContainerUI conn : allConns.keySet()) {
        // store old bendpoint for undo
        ConnectionUIInformation ui = conn.getUIInfo();
        if (ui != null) {
            m_oldBendpoints.put(conn.getID(), ui);
        } else {
            m_oldBendpoints.put(conn.getID(), null);
        }
        ConnectionUIInformation.Builder newUIBuilder = ConnectionUIInformation.builder();
        Edge e = m_workbenchToGraphEdges.get(conn);
        if (e == null) {
            // a parallel connection not represented by the edge
            continue;
        }
        List<ConnectionContainerUI> conns = m_parallelConns.get(e);
        assert conns.size() > 0;
        // that is how we created it!
        assert conns.get(0) == conn;
        ArrayList<Point2D> newBends = m_g.bends(e);
        if (newBends != null && !newBends.isEmpty()) {
            // half the node icon size...
            int extraX = 16;
            int extraY = 24;
            for (int i = 0; i < newBends.size(); i++) {
                Point2D b = newBends.get(i);
                newUIBuilder.addBendpoint((int) Math.round((b.getX() - coordOffsetX) * X_STRETCH) + extraX + minX, (int) Math.round((b.getY() - coordOffsetY) * Y_STRETCH) + extraY + minY, i);
            }
        }
        ConnectionUIInformation newUI = newUIBuilder.build();
        conn.setUIInfo(newUI);
        // compute bendpoints for parallel connections (slightly offset)
        for (int i = 1; i < conns.size(); i++) {
            // idx 0 == conn!
            ConnectionContainerUI parConn = conns.get(i);
            // destination port determines offset
            int yOffset = (parConn.getDestPort() - conn.getDestPort()) * 10;
            ConnectionUIInformation parUI = ConnectionUIInformation.builder(newUI).translate(new int[] { 0, yOffset }).build();
            parConn.setUIInfo(parUI);
        }
    }
}
Also used : NodeContainerUI(org.knime.core.ui.node.workflow.NodeContainerUI) HashMap(java.util.HashMap) Node(org.knime.workbench.ui.layout.Graph.Node) Point2D(java.awt.geom.Point2D) NodeID(org.knime.core.node.workflow.NodeID) SimpleLayeredLayouter(org.knime.workbench.ui.layout.layeredlayout.SimpleLayeredLayouter) Point(org.eclipse.draw2d.geometry.Point) Point(org.eclipse.draw2d.geometry.Point) ConnectionContainerUI(org.knime.core.ui.node.workflow.ConnectionContainerUI) ConnectionID(org.knime.core.node.workflow.ConnectionID) NodeUIInformation(org.knime.core.node.workflow.NodeUIInformation) ConnectionUIInformation(org.knime.core.node.workflow.ConnectionUIInformation) Edge(org.knime.workbench.ui.layout.Graph.Edge)

Aggregations

Edge (org.knime.workbench.ui.layout.Graph.Edge)5 Node (org.knime.workbench.ui.layout.Graph.Node)5 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 Graph (org.knime.workbench.ui.layout.Graph)2 Point2D (java.awt.geom.Point2D)1 Point (org.eclipse.draw2d.geometry.Point)1 ConnectionID (org.knime.core.node.workflow.ConnectionID)1 ConnectionUIInformation (org.knime.core.node.workflow.ConnectionUIInformation)1 NodeID (org.knime.core.node.workflow.NodeID)1 NodeUIInformation (org.knime.core.node.workflow.NodeUIInformation)1 ConnectionContainerUI (org.knime.core.ui.node.workflow.ConnectionContainerUI)1 NodeContainerUI (org.knime.core.ui.node.workflow.NodeContainerUI)1 SimpleLayeredLayouter (org.knime.workbench.ui.layout.layeredlayout.SimpleLayeredLayouter)1