public override void OptimizeAfterLayering(LayoutGraph graph, ILayers layers, ILayoutDataProvider ldp, IItemFactory itemFactory) { if (coreOptimizer != null) { coreOptimizer.OptimizeAfterLayering(graph, layers, ldp, itemFactory); } }
public Preview( Func <ISample> getNewTarget, ILayers layers, IPreviewSettings settings, Func <Texture2D> getTexture, float dotSize = 5 ) : base(() => new PreviewSample(getNewTarget())) { drawer = new Drawer(getNewTarget, () => settings.PoolingStepMultiplier); drawer.onStartDrawing += () => fixFactor = true; drawer.onFinishDrawing += () => fixFactor = false; this.layers = layers; layers.onChange += OnLayersChange; this.dotSize = dotSize; this.settings = settings; this.getTexture = getTexture; }
protected override void OptimizeAfterSequencing(IComparer <object> inEdgeOrder, IComparer <object> outEdgeOrder, LayoutGraph graph, ILayers layers, ILayoutDataProvider ldp, IItemFactory itemFactory) { edge2LaneCrossing = Maps.CreateHashedEdgeMap(); node2LaneAlignment = Maps.CreateHashedNodeMap(); var criticalEdges = Maps.CreateHashedEdgeMap(); // determine whether an edge crosses a swim lane border and if so in which direction foreach (var edge in graph.Edges) { var originalEdge = GetOriginalEdge(edge, ldp); // now we have a 'real' edge with valid valid source and target nodes var originalSourceId = GetLaneId(originalEdge.Source, ldp); var originalTargetId = GetLaneId(originalEdge.Target, ldp); LaneCrossing crossing = LaneCrossing.None; if (originalSourceId != originalTargetId) { // check if we need to flip the sides because edge and original edge have different directions var flipSides = edge.Source != originalEdge.Source; var sourceId = flipSides ? originalTargetId : originalSourceId; var targetId = flipSides ? originalSourceId : originalTargetId; crossing = sourceId > targetId ? LaneCrossing.ToWest : LaneCrossing.ToEast; } edge2LaneCrossing.Set(edge, crossing); } // determine basic node alignment foreach (var n in graph.Nodes) { LaneAlignment alignment = CalculateLaneAlignment(n); node2LaneAlignment.Set(n, alignment); } foreach (var n in graph.Nodes) { // sort the edges with the provided comparer n.SortInEdges(inEdgeOrder); n.SortOutEdges(outEdgeOrder); // calculate 'critical' in and out-edges whose nodes should be aligned in flow var bestInEdge = n.InDegree > 0 ? GetBestFlowEdge(n.InEdges, ldp, graph) : null; var bestOutEdge = n.OutDegree > 0 ? GetBestFlowEdge(n.OutEdges, ldp, graph) : null; if (bestInEdge != null) { criticalEdges.SetDouble(bestInEdge, criticalEdges.GetDouble(bestInEdge) + 0.5); } if (bestOutEdge != null) { criticalEdges.SetDouble(bestOutEdge, criticalEdges.GetDouble(bestOutEdge) + 0.5); } if (n.Degree <= 4) { // should usually be the case and we can distribute each edge to its own side // remember which node side is already taken by an in- or out-edge bool westTakenByInEdge = false; bool eastTakenByInEdge = false; bool westTakenByOutEdge = false; bool eastTakenByOutEdge = false; if (n.InDegree > 0 && n.OutDegree < 3) { // if there are at least three out-edges, we distribute those first, otherwise we start with the in-edges var firstInEdge = n.FirstInEdge; var lastInEdge = n.LastInEdge; if (GetLaneCrossing(firstInEdge) == LaneCrossing.ToEast && (n.InDegree > 1 || IsSameLayerEdge(firstInEdge, ldp))) { // the first in-edge comes from west and is either a same layer edge or there are other in-edges ConstrainWest(firstInEdge, false, itemFactory); westTakenByInEdge = true; } if (!westTakenByInEdge || n.OutDegree < 2) { // don't use west and east side for in-edges if there are at least 2 out-edges if (GetLaneCrossing(lastInEdge) == LaneCrossing.ToWest && (n.InDegree > 1 || IsSameLayerEdge(lastInEdge, ldp))) { // the last in-edge comes from east and is either // a same-layer edge or there are other in-edges ConstrainEast(lastInEdge, false, itemFactory); eastTakenByInEdge = true; } } } if (n.OutDegree > 0) { var firstOutEdge = n.FirstOutEdge; var lastOutEdge = n.LastOutEdge; if (!westTakenByInEdge) { // the west side is still free if (BpmnLayout.IsBoundaryInterrupting(firstOutEdge, graph) || (GetLaneCrossing(firstOutEdge) == LaneCrossing.ToWest) && (n.OutDegree > 1 || IsSameLayerEdge(firstOutEdge, ldp))) { // the first out-edge is either boundary interrupting or goes to west and // is either a same layer edge or there are other out-edges ConstrainWest(firstOutEdge, true, itemFactory); westTakenByOutEdge = true; } else if (eastTakenByInEdge && n.OutDegree >= 2 && !IsSameLayerEdge(firstOutEdge.NextOutEdge, ldp)) { // the east side is already taken but we have more then one out edge. // if the second out edge is a same layer edge, constraining the firstOutEdge could lead to // no in-flow edge ConstrainWest(firstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } if (!eastTakenByInEdge) { // the east side is still free if (GetLaneCrossing(lastOutEdge) == LaneCrossing.ToEast && (n.OutDegree > 1 || IsSameLayerEdge(lastOutEdge, ldp))) { // the last out-edge goes to east and // is either a same layer edge or there are other out-edges ConstrainEast(lastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } else if (westTakenByInEdge && n.OutDegree >= 2 && !IsSameLayerEdge(lastOutEdge.PrevOutEdge, ldp)) { // the west side is already taken but we have more then one out edge. // if the second last out edge is a same layer edge, constraining the lastOutEdge could lead to // no in-flow edge ConstrainEast(lastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } } } // distribute remaining in-edges if (n.InDegree == 2 && !(eastTakenByInEdge || westTakenByInEdge)) { // two in-edges but none distributed, yet if (bestInEdge == n.FirstInEdge && !eastTakenByOutEdge) { // first in-edge is in-flow edge and east side is still free ConstrainEast(n.LastInEdge, false, itemFactory); eastTakenByInEdge = true; } else if (bestInEdge == n.LastInEdge && !westTakenByOutEdge) { // last in-edge is in-flow edge and west side is still free ConstrainWest(n.FirstInEdge, false, itemFactory); westTakenByInEdge = true; } } else if (n.InDegree == 3 && !(eastTakenByInEdge && westTakenByInEdge) && !(IsSameLayerEdge(n.FirstInEdge.NextInEdge, ldp))) { // three in-edges but not both sides taken, yet and the middle edge is no same layer edge if (!eastTakenByOutEdge) { // if not already taken, constraint the last in-edge to east ConstrainEast(n.LastInEdge, false, itemFactory); eastTakenByInEdge = true; } if (!westTakenByOutEdge) { // if not already taken, constraint the first in-edge to west ConstrainWest(n.FirstInEdge, false, itemFactory); westTakenByInEdge = true; } } // distribute remaining out-edges if (n.OutDegree == 2 && !(eastTakenByOutEdge || westTakenByOutEdge)) { // two out-edges but none distributed, yet if (bestOutEdge == n.FirstOutEdge && !eastTakenByInEdge) { // first out-edge is in-flow edge and east side is still free ConstrainEast(n.LastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } else if (bestOutEdge == n.LastOutEdge && !westTakenByInEdge) { // last out-edge is in-flow edge and west side is still free ConstrainWest(n.FirstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } else if (n.OutDegree == 3 && !(eastTakenByOutEdge && westTakenByOutEdge) && !(IsSameLayerEdge(n.FirstOutEdge.NextOutEdge, ldp))) { // three out-edges but not both sides taken, yet and the middle edge is no same layer edge if (!eastTakenByInEdge) { // if not already taken, constraint the last out-edge to east ConstrainEast(n.LastOutEdge, true, itemFactory); eastTakenByOutEdge = true; } if (!westTakenByInEdge) { // if not already taken, constraint the first out-edge to west ConstrainWest(n.FirstOutEdge, true, itemFactory); westTakenByOutEdge = true; } } } } // register the data provider for critical edge paths. It is deregistered again by BpmnLayout itself graph.AddDataProvider(HierarchicLayout.CriticalEdgePriorityDpKey, criticalEdges); sameLayerData = null; edge2LaneCrossing = null; node2LaneAlignment = null; }
protected override SameLayerData InsertSameLayerStructures(LayoutGraph graph, ILayers layers, ILayoutDataProvider ldp, IItemFactory itemFactory) { // store the SameLayerData for later use sameLayerData = base.InsertSameLayerStructures(graph, layers, ldp, itemFactory); return(sameLayerData); }
/// <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; }
public Map() { Layers = new ILayers(); }