/// <summary>
        /// Returns the best suited edge in <paramref name="edges"/> for use as in-flow edge or <see langword="null"/>
        /// if no such edge could be found.
        /// </summary>
        private Edge GetBestFlowEdge(IEnumerable <Edge> edges, ILayoutDataProvider ldp, LayoutGraph graph)
        {
            List <Edge> weakCandidates = new List <Edge>();
            List <Edge> candidates     = new List <Edge>();

            foreach (var edge in edges)
            {
                var originalEdge = GetOriginalEdge(edge, ldp);
                if ((LaneCrossing)edge2LaneCrossing.Get(edge) != LaneCrossing.None ||
                    BpmnLayout.IsBoundaryInterrupting(originalEdge, graph) ||
                    IsSameLayerEdge(originalEdge, ldp) ||
                    edge.SelfLoop)
                {
                    // an edge should not be aligned if:
                    // - it crosses stripe borders
                    // - it is boundary interrupting
                    // - it is a same-layer edge
                    // - it is a self-loop
                    continue;
                }
                if (ldp.GetEdgeData(edge).Reversed ||
                    !BpmnLayout.IsSequenceFlow(originalEdge, graph))
                {
                    // it is only a weak candidate if:
                    // - it is reversed
                    // - it is no sequence flow
                    weakCandidates.Add(edge);
                }
                else
                {
                    candidates.Add(edge);
                }
            }
            if (candidates.Count > 0)
            {
                // if there are several candidates, choose the one that would keep the LaneAlignment
                // of its source and target node consistent
                candidates.Sort((edge1, edge2) => {
                    var ac1 = GetAlignmentConsistency(edge1);
                    var ac2 = GetAlignmentConsistency(edge2);
                    return(ac2 - ac1);
                });
                return(candidates[0]);
            }
            if (weakCandidates.Count > 0)
            {
                return(weakCandidates[(int)Math.Floor(weakCandidates.Count / 2.0)]);
            }
            return(null);
        }
예제 #2
0
        /// <inheritdoc/>
        public override void AssignLayers(LayoutGraph graph, ILayers layers, ILayoutDataProvider ldp)
        {
            // get core layer assignment
            base.AssignLayers(graph, layers, ldp);

            // Hide all edges that are no sequence flows
            var graphHider = new LayoutGraphHider(graph);

            foreach (var edge in graph.GetEdgeArray())
            {
                if (!BpmnLayout.IsSequenceFlow(edge, graph))
                {
                    graphHider.Hide(edge);
                }
            }

            // determine current layer of all nodes
            currentLayers = new int[graph.NodeCount];
            for (int i = 0; i < layers.Size(); i++)
            {
                for (INodeCursor nc = layers.GetLayer(i).List.Nodes(); nc.Ok; nc.Next())
                {
                    currentLayers[nc.Node.Index] = i;
                }
            }

            // mark nodes on a back-loop and candidates that may be on a back loop if other back-loop nodes are reassigned
            nodeStates = new NodeState[graph.NodeCount];
            NodeList candidates    = new NodeList();
            NodeList backLoopNodes = new NodeList();

            for (int i = layers.Size() - 1; i >= 0; i--)
            {
                // check from last to first layer to detect candidates as well
                NodeList nodes = layers.GetLayer(i).List;
                UpdateNodeStates(nodes, backLoopNodes, candidates);
            }

            // swap layer for back-loop nodes
            while (backLoopNodes.Count > 0)
            {
                for (INodeCursor nc = backLoopNodes.Nodes(); nc.Ok; nc.Next())
                {
                    Node node         = nc.Node;
                    int  currentLayer = currentLayers[node.Index];
                    // the target layer is the next layer after the highest fixed target node layer
                    int targetLayer = 0;
                    for (Edge edge = node.FirstOutEdge; edge != null; edge = edge.NextOutEdge)
                    {
                        int targetNodeIndex = edge.Target.Index;
                        if (nodeStates[targetNodeIndex] == NodeState.Fixed)
                        {
                            targetLayer = Math.Max(targetLayer, currentLayers[targetNodeIndex] + 1);
                        }
                    }
                    if (targetLayer == 0)
                    {
                        // no fixed target found, so all targets must be candidates
                        // -> we skip the node as we don't know where the candidates will be placed at the end
                        continue;
                    }
                    if (targetLayer < currentLayer)
                    {
                        layers.GetLayer(currentLayer).Remove(node);
                        layers.GetLayer(targetLayer).Add(node);
                        currentLayers[node.Index] = targetLayer;
                        nodeStates[node.Index]    = NodeState.Fixed;
                    }
                }
                backLoopNodes.Clear();

                // update states of the candidates
                candidates = UpdateNodeStates(candidates, backLoopNodes, new NodeList());
            }

            // remove empty layers
            for (int i = layers.Size() - 1; i >= 0; i--)
            {
                if (layers.GetLayer(i).List.Count == 0)
                {
                    layers.Remove(i);
                }
            }

            // cleanup
            graphHider.UnhideAll();
            nodeStates    = null;
            currentLayers = null;
        }