/// <summary> /// Creates the layout with an appropriate layout data which is selected by the layout combo box. /// </summary> private void CreateLayout() { switch (layoutComboBox.SelectedIndex) { default: case 0: layout = new HierarchicLayout(); var hierarchicLayoutData = new HierarchicLayoutData(); layoutData = hierarchicLayoutData; abortHandler = hierarchicLayoutData.AbortHandler; break; case 1: layout = new OrganicLayout { QualityTimeRatio = 1.0, MaximumDuration = 1200000, MultiThreadingAllowed = true }; var organicLayoutData = new OrganicLayoutData(); layoutData = organicLayoutData; abortHandler = organicLayoutData.AbortHandler; break; case 2: layout = new OrthogonalLayout { CrossingReduction = true }; var orthogonalLayoutData = new OrthogonalLayoutData(); layoutData = orthogonalLayoutData; abortHandler = orthogonalLayoutData.AbortHandler; break; } }
/// <summary> /// Applies the layout. /// </summary> /// <remarks> /// Uses an <see cref="HierarchicLayout"/>. If single graph /// items are created or removed, the incremental mode of this layout /// algorithm is used to keep most of the current layout of the graph /// unchanged. /// </remarks> /// <param name="incremental">if set to <see langword="true"/> [incremental].</param> /// <param name="incrementalNodes">The incremental nodes.</param> private async Task ApplyLayout(bool incremental, params EntityData[] incrementalNodes) { var layout = new HierarchicLayout { IntegratedEdgeLabeling = true, OrthogonalRouting = true }; HierarchicLayoutData layoutData = null; if (!incremental) { layout.LayoutMode = LayoutMode.FromScratch; } else { layout.LayoutMode = LayoutMode.Incremental; if (incrementalNodes.Any()) { // we need to add hints for incremental nodes layoutData = new HierarchicLayoutData { IncrementalHints = { IncrementalLayeringNodes = { Source = incrementalNodes.Select(graphSource.GetNode) } } }; } } await graphControl.MorphLayout(layout, TimeSpan.FromSeconds(1), layoutData); }
/// <summary> /// Creates the sample graph. /// </summary> private void CreateSampleGraph(IGraph graph) { INode n1, n2, n3, n4; layerIndices[n1 = graph.CreateNode()] = 0; layerIndices[n2 = graph.CreateNode()] = 1; layerIndices[n3 = graph.CreateNode()] = 2; layerIndices[n4 = graph.CreateNode()] = 2; graph.CreateEdge(n1, n2); graph.CreateEdge(n2, n3); graph.CreateEdge(n1, n4); // create an HierarchicLayout instance to provide an initial layout var hl = CreateLayout(); // use the GivenLayersLayerer to respect the above node to layer assignment hl.FromScratchLayerer = new GivenLayersLayerer(); // provide additional data to configure the layout // respect the above node to layer assignment var hlData = new HierarchicLayoutData { GivenLayersLayererIds = { Mapper = layerIndices } }; // run the layout graph.ApplyLayout(hl, hlData); // and update the layer visualization layerVisualCreator.UpdateLayers(graph, layerIndices); }
/// <summary> /// Creates a configured hierarchic layout data. /// </summary> public HierarchicLayoutData GetHierarchicLayoutData() { // use preferred placement descriptors to place the labels vertically on the edges var layoutData = new HierarchicLayoutData { EdgeLabelPreferredPlacement = new ItemMapping <ILabel, PreferredPlacementDescriptor> { Constant = GetPreferredLabelPlacement() }, IncrementalHints = new IncrementalHintItemMapping { ContextDelegate = (item, hintsFactory) => item is IEdge?hintsFactory.CreateSequenceIncrementallyHint(item) : null } }; return(layoutData); }
/// <summary> /// Core method that recalculates and updates the layout. /// </summary> private async void UpdateLayout(object sender, EventArgs e) { // make sure we are not reentrant if (updateLayout) { return; } updateLayout = true; // update the layers for moved nodes UpdateMovedNodes(); // create and configure the HierarchicLayout var layout = CreateLayout(); // rearrange only the incremental graph elements, the // remaining elements are not, or only slightly, changed layout.LayoutMode = LayoutMode.Incremental; // use the GivenLayersLayerer for all non-incremental nodes layout.FixedElementsLayerer = new GivenLayersLayerer(); // provide additional data to configure the HierarchicLayout var hierarchicLayoutData = new HierarchicLayoutData { // specify the layer of each non-incremental node GivenLayersLayererIds = { Mapper = layerIndices }, // retrieve the layer of each incremental node after the layout run to update the layer visualization LayerIndices = layerIndices, // specify port constrains for the source of each edge SourcePortConstraints = { Mapper = sourcePortConstraints }, // specify port constrains for the target of each edge TargetPortConstraints = { Mapper = targetPortConstraints }, IncrementalHints = { // specify the nodes to rearrange IncrementalLayeringNodes = { Source = incrementalNodes }, // specify the edges to rearrange IncrementalSequencingItems = { Source = incrementalEdges }, } }; await graphControl.MorphLayout(layout, TimeSpan.FromSeconds(1), hierarchicLayoutData); // forget the nodes and edges for the next run incrementalNodes.Clear(); incrementalEdges.Clear(); layerVisualCreator.UpdateLayers(graphControl.Graph, layerIndices); updateLayout = false; }
/// <summary> /// Creates and configures the <see cref="HierarchicLayout"/> and the <see cref="HierarchicLayoutData"/> /// such that node types are considered. /// </summary> private Sample CreateHierarchicSample() { // create hierarchic layout - no further settings on the algorithm necessary to support types var layout = new HierarchicLayout(); // the node types are specified as delegate on the nodeTypes property of the layout data var layoutData = new HierarchicLayoutData { NodeTypes = { Delegate = node => GetNodeType(node) } }; return(new Sample { Name = "Hierarchic", File = "hierarchic", Layout = layout, LayoutData = layoutData, IsDirected = true }); }
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 void AddLayerConstraints(IGraph graph, HierarchicLayoutData hierarchicLayoutData) { // use layer constraints via HierarchicLayoutData var layerConstraintData = hierarchicLayoutData.LayerConstraints; foreach (var edge in graph.Edges) { if (IsMessageFlow(edge) && !CompactMessageFlowLayering) { // message flow layering compaction is disabled, we add a 'weak' same layer constraint, i.e. source node shall be placed at // least 0 layers above target node layerConstraintData.PlaceAbove(edge.GetTargetNode(), edge.GetSourceNode(), 0, 1); } else if (IsSequenceFlow(edge)) { if ((IsSubprocess(edge.GetSourceNode()) && !(edge.SourcePort.Style is EventPortStyle)) || IsSubprocess(edge.GetTargetNode())) { // For edges to or from a subprocess that are not attached to an (interrupting) event port, the flow should be considered. // If the subprocess is a group node, any constraints to it are ignored so we have to add the constraints to the content nodes // of the subprocess AddAboveLayerConstraint(layerConstraintData, edge, graph); } } } // if start events should be pulled to the first layer, add PlaceNodeAtTop constraint. if (StartNodesFirst) { foreach (var node in graph.Nodes) { if (node.Style is EventNodeStyle && ((EventNodeStyle)node.Style).Characteristic == EventCharacteristic.Start && graph.InDegree(node) == 0 && (graph.GetParent(node) == null || graph.GetParent(node).Style is PoolNodeStyle)) { layerConstraintData.PlaceAtTop(node); } } } }
private void AddMinimumEdgeLength(double minimumEdgeLength, HierarchicLayoutData hierarchicLayoutData) { // each edge should have a minimum length so that all its labels can be placed on it one // after another with a minimum label-to-label distance hierarchicLayoutData.EdgeLayoutDescriptors.Delegate = edge => { var descriptor = new EdgeLayoutDescriptor { RoutingStyle = new RoutingStyle(EdgeRoutingStyle.Orthogonal) }; double minLength = 0; foreach (var label in edge.Labels) { var labelSize = label.GetLayout().GetBounds(); minLength += Math.Max(labelSize.Width, labelSize.Height); } if (edge.Labels.Count > 1) { minLength += (edge.Labels.Count - 1) * MinLabelToLabelDistance; } descriptor.MinimumLength = Math.Max(minLength, minimumEdgeLength); return(descriptor); }; }
private async Task ApplyLayout() { // create a pre-configured HierarchicLayout var hl = CreateHierarchicLayout(); // rearrange only the incremental graph elements, the // remaining elements are not, or only slightly, changed hl.LayoutMode = LayoutMode.Incremental; // provide additional data to configure the HierarchicLayout var hlData = new HierarchicLayoutData(); // specify the nodes to rearrange hlData.IncrementalHints.IncrementalLayeringNodes.Source = incrementalNodes; // specify the edges to rearrange hlData.IncrementalHints.IncrementalSequencingItems.Source = incrementalEdges; // append the FixNodeLocationStage to fix the position of the upper right corner // of the currently expanded or collapsed group node so that the mouse cursor // remains on the expand/collapse button during layout var fixNodeLocationStage = new FixNodeLocationStage(); hl.AppendStage(fixNodeLocationStage); // run the layout calculation and animate the result var executor = new LayoutExecutor(graphControl, hl) { AnimateViewport = false, EasedAnimation = true, RunInThread = true, UpdateContentRect = true, Duration = TimeSpan.FromMilliseconds(500), LayoutData = hlData }; // compose layout data from HierarchicLayoutData and FixNodeLayoutData await executor.Start(); }
private async Task DoLayout() { // layout starting, disable button layoutButton.IsEnabled = false; // create a new layout algorithm var hl = new HierarchicLayout { OrthogonalRouting = true, FromScratchLayeringStrategy = LayeringStrategy.HierarchicalTopmost, IntegratedEdgeLabeling = true }; // and layout data for it var hlData = new HierarchicLayoutData { ConstraintIncrementalLayererAdditionalEdgeWeights = { Delegate = GetEdgeWeight } }; // we provide the LayerConstraintData.Values directly as IComparable instead of using the Add* methods on LayerConstraintData var layerConstraintData = hlData.LayerConstraints; layerConstraintData.NodeComparables.Delegate = node => { var data = node.Tag as LayerConstraintsInfo; if (data != null && data.Constraints) { // the node shall be constrained so we use its Value as comparable return(data.Value); } // otherwise we don't add constraints for the node return(null); }; INode[] layerRep = new INode[8]; // additionally enforce all nodes with a LayerConstraintInfo.Value of 0 or 7 to be placed at top/bottom // and all nodes with the same Value in the same layer foreach (var node in graphControl.Graph.Nodes) { var data = node.Tag as LayerConstraintsInfo; if (data != null && data.Constraints) { if (data.Value == 0) { // add constraint to put this node at the top layerConstraintData.PlaceAtTop(node); } else if (data.Value == 7) { // add constraint to put this node at the bottom layerConstraintData.PlaceAtBottom(node); } if (layerRep[data.Value] == null) { // this is the first node found having this data.Value so we store it as representative layerRep[data.Value] = node; } else { // add constraint to put this node in the same layer as its representative layerConstraintData.PlaceInSameLayer(layerRep[data.Value], node); } } } // perform the layout operation await graphControl.MorphLayout(hl, TimeSpan.FromSeconds(1), hlData); // code is executed once the layout operation is finished // enable button again layoutButton.IsEnabled = true; }
/// <summary> /// Creates a new instance configured for the specified graph. /// </summary> /// <param name="graph">The graph to configure the layout data for</param> /// <param name="fromSketch"/> Whether the <see cref="HierarchicLayout"/> shall be run in incremental layout mode. public LayoutData(IGraph graph, bool fromSketch) { // set up port candidates for edges (edges should be attached to the left/right side of the corresponding node var candidates = new List <PortCandidate> { PortCandidate.CreateCandidate(PortDirections.West), PortCandidate.CreateCandidate(PortDirections.East) }; // configure the different sub group layout settings var leftToRightTreeLayout = new TreeReductionStage(); leftToRightTreeLayout.NonTreeEdgeRouter = leftToRightTreeLayout.CreateStraightLineRouter(); leftToRightTreeLayout.CoreLayout = new TreeLayout { LayoutOrientation = LayoutOrientation.LeftToRight, }; var rightToLeftTreeLayout = new TreeReductionStage(); rightToLeftTreeLayout.NonTreeEdgeRouter = rightToLeftTreeLayout.CreateStraightLineRouter(); rightToLeftTreeLayout.CoreLayout = new TreeLayout { LayoutOrientation = LayoutOrientation.RightToLeft, }; var view = graph.Lookup <IFoldingView>(); Items.Add(new RecursiveGroupLayoutData { SourcePortCandidates = { Constant = candidates }, TargetPortCandidates = { Constant = candidates }, // map each group node to the layout algorithm that should be used for its content GroupNodeLayouts = { Delegate = node => { switch (GetTierType(node, view, graph)) { case TierType.LeftTreeGroupNode: return(leftToRightTreeLayout); case TierType.RightTreeGroupNode: return(rightToLeftTreeLayout); default: return(null); } } } }); var hlData = new HierarchicLayoutData { NodeLayoutDescriptors = { Delegate = node => { // align tree group nodes within their layer switch (GetTierType(node, view, graph)) { case TierType.LeftTreeGroupNode: return(new NodeLayoutDescriptor { LayerAlignment = 1 }); case TierType.RightTreeGroupNode: return(new NodeLayoutDescriptor { LayerAlignment = 0 }); default: return(null); } } } }; if (!fromSketch) { // insert layer constraints to guarantee the desired placement for "left" and "right" group nodes var layerConstraintData = hlData.LayerConstraints; foreach (var node in graph.Nodes) { // align tree group nodes within their layer TierType type = GetTierType(node, view, graph); if (type == TierType.LeftTreeGroupNode) { layerConstraintData.PlaceAtTop(node); } else if (type == TierType.RightTreeGroupNode) { layerConstraintData.PlaceAtBottom(node); } } } Items.Add(hlData); }
protected override LayoutData CreateConfiguredLayoutData(GraphControl graphControl, ILayoutAlgorithm layout) { var layoutData = new HierarchicLayoutData(); var incrementalLayout = SelectedElementsIncrementallyItem; var selection = graphControl.Selection; var selectedElements = selection.SelectedEdges.Any() || selection.SelectedNodes.Any(); if (incrementalLayout && selectedElements) { // configure the mode var ihf = ((HierarchicLayout)layout).CreateIncrementalHintsFactory(); layoutData.IncrementalHints.Delegate = item => { // Return the correct hint type for each model item that appears in one of these sets if (item is INode && selection.IsSelected(item)) { return(ihf.CreateLayerIncrementallyHint(item)); } if (item is IEdge && selection.IsSelected(item)) { return(ihf.CreateSequenceIncrementallyHint(item)); } return(null); }; } if (RankingPolicyItem == LayeringStrategy.Bfs) { layoutData.BfsLayererCoreNodes.Delegate = selection.IsSelected; } if (GridEnabledItem) { var nld = ((HierarchicLayout)layout).NodeLayoutDescriptor; layoutData.NodeLayoutDescriptors.Delegate = node => { var descriptor = new NodeLayoutDescriptor(); descriptor.LayerAlignment = nld.LayerAlignment; descriptor.MinimumDistance = nld.MinimumDistance; descriptor.MinimumLayerHeight = nld.MinimumLayerHeight; descriptor.NodeLabelMode = nld.NodeLabelMode; // anchor nodes on grid according to their alignment within the layer descriptor.GridReference = new YPoint(0.0, (nld.LayerAlignment - 0.5) * node.Layout.Height); descriptor.PortAssignment = this.GridPortAssignmentItem; return(descriptor); }; } if (EdgeDirectednessItem) { layoutData.EdgeDirectedness.Delegate = edge => { if (edge.Style is IArrowOwner && !Equals(((IArrowOwner)edge.Style).TargetArrow, Arrows.None)) { return(1); } return(0); }; } if (EdgeThicknessItem) { layoutData.EdgeThickness.Delegate = edge => { var style = edge.Style as PolylineEdgeStyle; if (style != null) { return(style.Pen.Thickness); } return(1); }; } if (SubComponentsItem) { // layout all siblings with label 'TL' separately with tree layout var treeLayout = new TreeLayout { DefaultNodePlacer = new LeftRightNodePlacer() }; foreach (var listOfNodes in FindSubComponents(graphControl.Graph, "TL")) { layoutData.SubComponents.Add(treeLayout).Items = listOfNodes; } // layout all siblings with label 'HL' separately with hierarchical layout var hierarchicLayout = new HierarchicLayout { LayoutOrientation = LayoutOrientation.LeftToRight }; foreach (var listOfNodes in FindSubComponents(graphControl.Graph, "HL")) { layoutData.SubComponents.Add(hierarchicLayout).Items = listOfNodes; } // layout all siblings with label 'OL' separately with organic layout var organicLayout = new OrganicLayout { PreferredEdgeLength = 100, Deterministic = true }; foreach (var listOfNodes in FindSubComponents(graphControl.Graph, "OL")) { layoutData.SubComponents.Add(organicLayout).Items = listOfNodes; } } if (HighlightCriticalPath) { // highlight the longest path in the graph as critical path // since the longest path algorithm only works for acyclic graphs, // feedback edges and self loops have to be excluded here var feedbackEdgeSetResult = new FeedbackEdgeSet().Run(graphControl.Graph); var longestPath = new LongestPath { SubgraphEdges = { Excludes = { Delegate = edge => feedbackEdgeSetResult.FeedbackEdgeSet.Contains(edge) || edge.IsSelfloop() } } }.Run(graphControl.Graph); if (longestPath.Edges.Any()) { layoutData.CriticalEdgePriorities.Delegate = edge => { if (longestPath.Edges.Contains(edge)) { return(10); } return(1); }; } } if (AutomaticBusRouting) { var allBusNodes = new HashSet <INode>(); foreach (var node in graphControl.Graph.Nodes) { if (!graphControl.Graph.IsGroupNode(node) && !allBusNodes.Contains(node)) { // search for good opportunities for bus structures rooted at this node if (graphControl.Graph.InDegree(node) >= 4) { var busDescriptor = new BusDescriptor(); var busEdges = GetBusEdges(graphControl.Graph, node, allBusNodes, graphControl.Graph.InEdgesAt(node)); if (busEdges.Any()) { layoutData.Buses.Add(busDescriptor).Items = busEdges; } } if (graphControl.Graph.OutDegree(node) >= 4) { var busDescriptor = new BusDescriptor(); var busEdges = GetBusEdges(graphControl.Graph, node, allBusNodes, graphControl.Graph.OutEdgesAt(node)); if (busEdges.Any()) { layoutData.Buses.Add(busDescriptor).Items = busEdges; } } } } } return(layoutData.CombineWith( CreateLabelingLayoutData( graphControl.Graph, LabelPlacementAlongEdgeItem, LabelPlacementSideOfEdgeItem, LabelPlacementOrientationItem, LabelPlacementDistanceItem ) )); }
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); }; } }
protected override LayoutData CreateConfiguredLayoutData(GraphControl graphControl, ILayoutAlgorithm layout) { var layoutData = new HierarchicLayoutData(); var incrementalLayout = SelectedElementsIncrementallyItem; var selection = graphControl.Selection; var selectedElements = selection.SelectedEdges.Any() || selection.SelectedNodes.Any(); if (incrementalLayout && selectedElements) { // configure the mode var ihf = ((HierarchicLayout)layout).CreateIncrementalHintsFactory(); layoutData.IncrementalHints.Delegate = item => { // Return the correct hint type for each model item that appears in one of these sets if (item is INode && selection.IsSelected(item)) { return(ihf.CreateLayerIncrementallyHint(item)); } if (item is IEdge && selection.IsSelected(item)) { return(ihf.CreateSequenceIncrementallyHint(item)); } return(null); }; } if (RankingPolicyItem == LayeringStrategy.Bfs) { layoutData.BfsLayererCoreNodes.Delegate = selection.IsSelected; } if (GridEnabledItem) { var nld = ((HierarchicLayout)layout).NodeLayoutDescriptor; layoutData.NodeLayoutDescriptors.Delegate = node => { var descriptor = new NodeLayoutDescriptor(); descriptor.LayerAlignment = nld.LayerAlignment; descriptor.MinimumDistance = nld.MinimumDistance; descriptor.MinimumLayerHeight = nld.MinimumLayerHeight; descriptor.NodeLabelMode = nld.NodeLabelMode; // anchor nodes on grid according to their alignment within the layer descriptor.GridReference = new YPoint(0.0, (nld.LayerAlignment - 0.5) * node.Layout.Height); descriptor.PortAssignment = this.GridPortAssignmentItem; return(descriptor); }; } if (EdgeDirectednessItem) { layoutData.EdgeDirectedness.Delegate = edge => { if (edge.Style is IArrowOwner && !Equals(((IArrowOwner)edge.Style).TargetArrow, Arrows.None)) { return(1); } return(0); }; } if (EdgeThicknessItem) { layoutData.EdgeThickness.Delegate = edge => { var style = edge.Style as PolylineEdgeStyle; if (style != null) { return(style.Pen.Width); } return(1); }; } if (SubComponentsItem) { var treeLayout = new TreeLayout { DefaultNodePlacer = new LeftRightNodePlacer() }; layoutData.SubComponents.Add(treeLayout).Delegate = node => node.Labels.Any() && node.Labels.First().Text == "TL"; var hierarchicLayout = new HierarchicLayout { LayoutOrientation = LayoutOrientation.LeftToRight }; layoutData.SubComponents.Add(hierarchicLayout).Delegate = node => node.Labels.Any() && node.Labels.First().Text == "HL"; var organicLayout = new OrganicLayout { PreferredEdgeLength = 100, Deterministic = true }; layoutData.SubComponents.Add(organicLayout).Delegate = node => node.Labels.Any() && node.Labels.First().Text == "OL"; } if (BusesItem) { // Group edges ending at a node with the label "Bus" into a bus layoutData.Buses.Add(new BusDescriptor()).Delegate = edge => edge.GetTargetNode().Labels.Count > 0 && edge.GetTargetNode().Labels[0].Text == "Bus"; } return(layoutData); }
private async Task DoLayout() { // layout starting, disable button runButton.Enabled = false; // create a new layout algorithm var hl = new HierarchicLayout { OrthogonalRouting = true, FromScratchLayeringStrategy = LayeringStrategy.HierarchicalTopmost, IntegratedEdgeLabeling = true }; // and layout data for it var hlData = new HierarchicLayoutData { ConstraintIncrementalLayererAdditionalEdgeWeights = { Delegate = GetEdgeWeight } }; // we provide the LayerConstraintData.Values directly as IComparable instead of using the Add* methods on LayerConstraintData var layerConstraintData = hlData.LayerConstraints; layerConstraintData.NodeComparables.Delegate = node => { var data = node.Tag as LayerConstraintsInfo; if (data != null && data.Constraints) { // the node shall be constrained so we use its Value as comparable return(data.Value); } // otherwise we don't add constraints for the node return(null); }; INode[] layerRep = new INode[8]; // additionally enforce all nodes with a LayerConstraintInfo.Value of 0 or 7 to be placed at top/bottom // and register the value in the NodeComparables Mapper for all other constrained nodes foreach (var node in graphControl.Graph.Nodes) { var data = node.Tag as LayerConstraintsInfo; if (data != null && data.Constraints) { if (data.Value == 0) { // add constraint to put this node at the top layerConstraintData.PlaceAtTop(node); } else if (data.Value == 7) { // add constraint to put this node at the bottom layerConstraintData.PlaceAtBottom(node); } else { // for every node in between we record its value with the mapper, assuring that there // will be no layer with different values and monotonically growing values per layer layerConstraintData.NodeComparables.Mapper[node] = data.Value; } } } // perform the layout operation await graphControl.MorphLayout(hl, TimeSpan.FromSeconds(1), hlData); // code is executed once the layout operation is finished // enable button again runButton.Enabled = true; }