/// <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); }; } }