Esempio n. 1
0
        /// <summary>
        /// Adds a mapper with a <see cref="PreferredPlacementDescriptor"/> that matches the given settings to the mapper
        /// registry of the given graph. In addition, sets the label model of all edge labels to free since that model
        /// can realizes any label placement calculated by a layout algorithm.
        /// </summary>
        public LayoutData CreateLabelingLayoutData(
            IGraph graph, EnumLabelPlacementAlongEdge placeAlongEdge, EnumLabelPlacementSideOfEdge sideOfEdge,
            EnumLabelPlacementOrientation orientation, double distanceToEdge)
        {
            var descriptor = CreatePreferredPlacementDescriptor(
                placeAlongEdge,
                sideOfEdge,
                orientation,
                distanceToEdge
                );

            // change to a free edge label model to support integrated edge labeling
            var model = new FreeEdgeLabelModel();

            foreach (var label in graph.GetEdgeLabels())
            {
                if (!(label.LayoutParameter.Model  is FreeEdgeLabelModel))
                {
                    graph.SetLabelLayoutParameter(label, model.FindBestParameter(label, model, label.GetLayout()));
                }
            }

            var layoutData = new GenericLayoutData();

            layoutData.AddItemMapping(LayoutGraphAdapter.EdgeLabelLayoutPreferredPlacementDescriptorDpKey,
                                      new ItemMapping <ILabel, PreferredPlacementDescriptor>(label => descriptor));

            return(layoutData);
        }
        protected override LayoutData CreateConfiguredLayoutData(GraphControl graphControl, ILayoutAlgorithm layout)
        {
            var layoutData     = new BusRouterData();
            var graph          = graphControl.Graph;
            var graphSelection = graphControl.Selection;
            var scopePartial   = ScopeItem == EnumScope.Partial;

            var busIds = layoutData.EdgeDescriptors.Mapper;

            foreach (var edge in graph.Edges)
            {
                var isFixed = scopePartial &&
                              !graphSelection.IsSelected(edge.GetSourceNode()) &&
                              !graphSelection.IsSelected(edge.GetTargetNode());
                var id         = GetBusId(edge, BusesItem);
                var descriptor = new BusDescriptor(id, isFixed)
                {
                    RoutingPolicy = RoutingPolicyItem
                };
                busIds[edge] = descriptor;
            }

            HashSet <object> selectedIds;

            switch (ScopeItem)
            {
            case EnumScope.Subset:
                layoutData.AffectedEdges.Delegate = graphSelection.IsSelected;
                break;

            case EnumScope.SubsetBus:
                selectedIds = new HashSet <object>(graphSelection
                                                   .SelectedEdges
                                                   .Select(edge => busIds[edge].BusId));
                layoutData.AffectedEdges.Delegate = edge => selectedIds.Contains(busIds[edge].BusId);
                break;

            case EnumScope.Partial:
                selectedIds = new HashSet <object>(graphSelection
                                                   .SelectedNodes
                                                   .SelectMany(node => graph.EdgesAt(node))
                                                   .Select(edge => busIds[edge].BusId));

                layoutData.AffectedEdges.Delegate = edge => selectedIds.Contains(busIds[edge].BusId);

                var hideNonOrthogonalEdgesLayoutData = new GenericLayoutData();
                hideNonOrthogonalEdgesLayoutData.AddItemCollection(HideNonOrthogonalEdgesStage.SelectedNodesDpKey).Source =
                    graphSelection.SelectedNodes;

                return(layoutData.CombineWith(hideNonOrthogonalEdgesLayoutData));
            }

            return(layoutData);
        }
        private static void AddNodeHalos(GenericLayoutData data, IGraph graph, ISelectionModel <IModelItem> selection, bool layoutOnlySelection)
        {
            var nodeHalos = new DictionaryMapper <INode, NodeHalo>();

            foreach (var node in graph.Nodes)
            {
                var top    = 0.0;
                var left   = 0.0;
                var bottom = 0.0;
                var right  = 0.0;

                // for each port with an EventPortStyle extend the node halo to cover the ports render size
                foreach (var port in node.Ports)
                {
                    var eventPortStyle = port.Style as EventPortStyle;
                    if (eventPortStyle != null)
                    {
                        var renderSize = eventPortStyle.RenderSize;
                        var location   = port.GetLocation();
                        top    = Math.Max(top, node.Layout.Y - location.Y - renderSize.Height / 2);
                        left   = Math.Max(left, node.Layout.X - location.X - renderSize.Width / 2);
                        bottom = Math.Max(bottom, location.Y + renderSize.Height / 2 - node.Layout.GetMaxY());
                        right  = Math.Max(right, location.X + renderSize.Width / 2 - node.Layout.GetMaxX());
                    }
                }

                // for each node without incoming or outgoing edges reserve space for laid out exterior labels
                if (graph.InDegree(node) == 0 || graph.OutDegree(node) == 0)
                {
                    foreach (var label in node.Labels)
                    {
                        if (IsNodeLabelAffected(graph, selection, label, layoutOnlySelection))
                        {
                            var labelBounds = label.GetLayout().GetBounds();
                            if (graph.InDegree(node) == 0)
                            {
                                left = Math.Max(left, labelBounds.Width);
                                top  = Math.Max(top, labelBounds.Height);
                            }
                            if (graph.OutDegree(node) == 0)
                            {
                                right  = Math.Max(right, labelBounds.Width);
                                bottom = Math.Max(bottom, labelBounds.Height);
                            }
                        }
                    }
                }

                nodeHalos[node] = NodeHalo.Create(top, left, bottom, right);
            }
            data.AddItemMapping(NodeHalo.NodeHaloDpKey).Mapper = nodeHalos;
        }
        private static void AddEdgeLabelPlacementDescriptors(GenericLayoutData data)
        {
            var atSourceDescriptor = new PreferredPlacementDescriptor {
                PlaceAlongEdge = LabelPlacements.AtSourcePort,
                SideOfEdge     = LabelPlacements.LeftOfEdge | LabelPlacements.RightOfEdge,
            };

            data.AddItemMapping(LayoutGraphAdapter.EdgeLabelLayoutPreferredPlacementDescriptorDpKey).Delegate =
                label => {
                var edgeType = ((BpmnEdgeStyle)((IEdge)label.Owner).Style).Type;
                if (edgeType == EdgeType.SequenceFlow || edgeType == EdgeType.DefaultFlow || edgeType == EdgeType.ConditionalFlow)
                {
                    // labels on sequence, default and conditional flow edges should be placed at the source side.
                    return(atSourceDescriptor);
                }
                return(null);
            };
        }
        public LayoutData Create(IGraph graph, ISelectionModel <IModelItem> selection, Scope layoutScope)
        {
            var data = new GenericLayoutData();
            var hierarchicLayoutData = new HierarchicLayoutData();

            // check if only selected elements should be laid out
            var layoutOnlySelection = layoutScope == Scope.SelectedElements;

            // mark 'flow' edges, i.e. sequence flows, default flows and conditional flows
            data.AddItemCollection(BpmnLayout.SequenceFlowEdgesDpKey).Delegate = IsSequenceFlow;

            // mark boundary interrupting edges for the BalancingPortOptimizer
            data.AddItemCollection(BpmnLayout.BoundaryInterruptingEdgesDpKey).Delegate = edge => edge.SourcePort.Style is EventPortStyle;



            // mark conversations, events and gateways so their port locations are adjusted
            data.AddItemCollection(PortLocationAdjuster.AffectedNodesDpKey).Delegate = (INode node) =>
                                                                                       (node.Style is ConversationNodeStyle || node.Style is EventNodeStyle || node.Style is GatewayNodeStyle);

            // add NodeHalos around nodes with event ports or specific exterior labels so the layout keeps space for the event ports and labels as well
            AddNodeHalos(data, graph, selection, layoutOnlySelection);

            // add PreferredPlacementDescriptors for labels on sequence, default or conditional flows to place them at source side
            AddEdgeLabelPlacementDescriptors(data);

            // mark nodes, edges and labels as either fixed or affected by the layout and configure port constraints and incremental hints
            MarkFixedAndAffectedItems(data, hierarchicLayoutData, selection, layoutOnlySelection);

            // mark associations and message flows as undirected so they have less impact on layering
            hierarchicLayoutData.EdgeDirectedness.Delegate = edge => (IsMessageFlow(edge) || IsAssociation(edge)) ? 0 : 1;

            // add layer constraints for start events, sub processes and message flows
            AddLayerConstraints(graph, hierarchicLayoutData);

            // add EdgeLayoutDescriptor to specify minimum edge length for edges
            AddMinimumEdgeLength(MinimumEdgeLength, hierarchicLayoutData);


            return(data.CombineWith(hierarchicLayoutData));
        }
        private static void MarkFixedAndAffectedItems(GenericLayoutData data, HierarchicLayoutData hierarchicLayoutData, ISelectionModel <IModelItem> graphSelection,
                                                      bool layoutOnlySelection)
        {
            if (layoutOnlySelection)
            {
                var affectedEdges = Mappers.FromDelegate((IEdge edge) =>
                                                         graphSelection.IsSelected(edge) ||
                                                         graphSelection.IsSelected(edge.GetSourceNode()) ||
                                                         graphSelection.IsSelected(edge.GetTargetNode()));
                data.AddItemCollection(LayoutKeys.AffectedEdgesDpKey).Mapper = affectedEdges;

                // fix ports of unselected edges and edges at event ports
                data.AddItemMapping(PortConstraintKeys.SourcePortConstraintDpKey).Delegate =
                    edge =>
                    (!affectedEdges[edge] || edge.SourcePort.Style is EventPortStyle)
              ? PortConstraint.Create(GetSide(edge, true))
              : null;
                data.AddItemMapping(PortConstraintKeys.TargetPortConstraintDpKey).Delegate =
                    edge => !affectedEdges[edge] ? PortConstraint.Create(GetSide(edge, false)) : null;

                // give core layout hints that selected nodes and edges should be incremental
                hierarchicLayoutData.IncrementalHints.ContextDelegate = (item, factory) => {
                    if (item is INode && graphSelection.IsSelected(item))
                    {
                        return(factory.CreateLayerIncrementallyHint(item));
                    }
                    else if (item is IEdge && affectedEdges[(IEdge)item])
                    {
                        return(factory.CreateSequenceIncrementallyHint(item));
                    }
                    return(null);
                };
                data.AddItemCollection(BpmnLayout.AffectedLabelsDpKey).Delegate = label => {
                    var edge = label.Owner as IEdge;
                    if (edge != null)
                    {
                        return(affectedEdges[edge]);
                    }
                    var node = label.Owner as INode;
                    if (node != null)
                    {
                        var  isInnerLabel   = node.Layout.Contains(label.GetLayout().GetCenter());
                        bool isPool         = node.Style is PoolNodeStyle;
                        bool isChoreography = node.Style is ChoreographyNodeStyle;
                        return(!isInnerLabel && !isPool && !isChoreography && graphSelection.IsSelected(node));
                    }
                    return(false);
                };
            }
            else
            {
                // fix source port of edges at event ports
                data.AddItemMapping(PortConstraintKeys.SourcePortConstraintDpKey).Delegate =
                    edge => edge.SourcePort.Style is EventPortStyle?PortConstraint.Create(GetSide(edge, true)) : null;

                data.AddItemCollection(BpmnLayout.AffectedLabelsDpKey).Delegate = label => {
                    if (label.Owner is IEdge)
                    {
                        return(true);
                    }
                    var node = label.Owner as INode;
                    if (node != null)
                    {
                        var  isInnerLabel   = node.Layout.Contains(label.GetLayout().GetCenter());
                        bool isPool         = node.Style is PoolNodeStyle;
                        bool isChoreography = node.Style is ChoreographyNodeStyle;
                        return(!isInnerLabel && !isPool && !isChoreography);
                    }
                    return(false);
                };
            }
        }