/// <summary> /// Creates a <see cref="LayoutData"/> used while dragging and finishing the gesture. /// </summary> private LayoutData CreateClearAreaLayoutData() { // force the router to let edges leave the nodes at the center of the south side // and to let enter the nodes in the center of the north side var sourcePortCandidate = PortCandidate.CreateCandidate(0, 0, PortDirections.South); var targetPortCandidate = PortCandidate.CreateCandidate(0, 0, PortDirections.North); return(new CompositeLayoutData( resetToWorkingGraphStageData, new ClearAreaLayoutData { AreaNodes = { Source = subtree.Nodes }, ComponentIds = { Mapper = components }, SourcePortCandidates = { Constant = new List <PortCandidate> { sourcePortCandidate } }, TargetPortCandidates = { Constant = new List <PortCandidate> { targetPortCandidate } } })); }
static TableLayout() { // 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 layout algorithms var rowLayout = new RowLayout(); // used for laying out the nodes (rows) within the group nodes (tables) layoutData = new RecursiveGroupLayoutData { SourcePortCandidates = { Constant = candidates }, TargetPortCandidates = { Constant = candidates }, // map each group node to its corresponding layout algorithm; // in this case each group node shall be laid out using the RowLayout GroupNodeLayouts = { Constant = rowLayout } }; }
protected override LayoutData CreateConfiguredLayoutData(GraphControl graphControl, ILayoutAlgorithm layout) { var layoutData = new EdgeRouterData(); layoutData.EdgeLayoutDescriptors.Delegate = edge => { var descriptor = new EdgeLayoutDescriptor { MinimumEdgeToEdgeDistance = MinimumEdgeToEdgeDistanceItem, MinimumNodeCornerDistance = MinimumNodeCornerDistanceItem, MinimumFirstSegmentLength = MinimumFirstSegmentLengthItem, MinimumLastSegmentLength = MinimumLastSegmentLengthItem, PreferredOctilinearSegmentLength = PreferredOctilinearSegmentLengthItem, MaximumOctilinearSegmentRatio = PreferredPolylineSegmentRatioItem, SourceCurveConnectionStyle = SourceConnectionStyleItem, TargetCurveConnectionStyle = TargetConnectionStyleItem, CurveUTurnSymmetry = CurveUTurnSymmetryItem, CurveShortcuts = CurveShortcutsItem, RoutingStyle = EdgeRoutingStyleItem, RoutingPolicy = RoutingPolicyItem, }; if (OptimizationStrategyItem == EnumStrategies.Balanced) { descriptor.PenaltySettings = PenaltySettings.OptimizationBalanced; } else if (OptimizationStrategyItem == EnumStrategies.MinimizeBends) { descriptor.PenaltySettings = PenaltySettings.OptimizationEdgeBends; } else if (OptimizationStrategyItem == EnumStrategies.MinimizeEdgeLength) { descriptor.PenaltySettings = PenaltySettings.OptimizationEdgeLengths; } else { descriptor.PenaltySettings = PenaltySettings.OptimizationEdgeCrossings; } if (MonotonicRestrictionItem == EnumMonotonyFlags.Horizontal) { descriptor.MonotonicPathRestriction = MonotonicPathRestriction.Horizontal; } else if (MonotonicRestrictionItem == EnumMonotonyFlags.Vertical) { descriptor.MonotonicPathRestriction = MonotonicPathRestriction.Vertical; } else if (MonotonicRestrictionItem == EnumMonotonyFlags.Both) { descriptor.MonotonicPathRestriction = MonotonicPathRestriction.Both; } else { descriptor.MonotonicPathRestriction = MonotonicPathRestriction.None; } descriptor.MinimumEdgeToEdgeDistance = MinimumEdgeToEdgeDistanceItem; descriptor.MinimumNodeCornerDistance = MinimumNodeCornerDistanceItem; descriptor.MinimumFirstSegmentLength = MinimumFirstSegmentLengthItem; descriptor.MinimumLastSegmentLength = MinimumLastSegmentLengthItem; if (UseIntermediatePointsItem) { descriptor.IntermediateRoutingPoints = edge.Bends .Select(b => new YPoint(b.Location.X, b.Location.Y)) .Cast <object>() .ToList(); } return(descriptor); }; var selection = graphControl.Selection; if (ScopeItem == Scope.RouteEdgesAtAffectedNodes) { layoutData.AffectedNodes.Source = selection.SelectedNodes; } else if (ScopeItem == Scope.RouteAffectedEdges) { layoutData.AffectedEdges.Source = selection.SelectedEdges; } else { layoutData.AffectedEdges.Delegate = edge => true; layoutData.AffectedNodes.Delegate = node => true; } if (PortSidesItem != PortSides.Any) { PortCandidate[] candidates; if (PortSidesItem == PortSides.LeftRight) { candidates = new[] { PortCandidate.CreateCandidate(PortDirections.East), PortCandidate.CreateCandidate(PortDirections.West) }; } else { candidates = new[] { PortCandidate.CreateCandidate(PortDirections.North), PortCandidate.CreateCandidate(PortDirections.South) }; } layoutData.SourcePortCandidates.Constant = candidates; layoutData.TargetPortCandidates.Constant = candidates; } switch (BusRoutingItem) { case EnumBusRouting.SingleBus: // All edges in a single bus layoutData.Buses.Add(CreateBusDescriptor()).Delegate = edge => true; break; case EnumBusRouting.ByLabel: var byLabel = new Dictionary <string, List <IEdge> >(); foreach (var edge in graphControl.Graph.Edges) { if (edge.Labels.Count > 0) { var label = edge.Labels[0].Text; List <IEdge> list; if (!byLabel.TryGetValue(label, out list)) { list = new List <IEdge>(); byLabel[label] = list; } list.Add(edge); } } foreach (var edges in byLabel.Values) { // Add a bus per label. Unlabeled edges don't get grouped into a bus layoutData.Buses.Add(CreateBusDescriptor()).Source = edges; } break; case EnumBusRouting.ByColor: var comparer = new ColorComparer(); var byColor = new Dictionary <Brush, List <IEdge> >(comparer); foreach (var edge in graphControl.Graph.Edges) { var brush = ((PolylineEdgeStyle)edge.Style).Pen.Brush; if (!comparer.Equals(brush, Brushes.Black)) { List <IEdge> list; if (!byColor.TryGetValue(brush, out list)) { list = new List <IEdge>(); byColor[brush] = list; } list.Add(edge); } } foreach (var edges in byColor.Values) { // Add a bus per color. Black edges don't get grouped into a bus layoutData.Buses.Add(CreateBusDescriptor()).Source = edges; } break; } return(layoutData.CombineWith( CreateLabelingLayoutData( graphControl.Graph, LabelPlacementAlongEdgeItem, LabelPlacementSideOfEdgeItem, LabelPlacementOrientationItem, LabelPlacementDistanceItem ) )); }
/// <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); }
/// <summary> /// Perform the layout operation /// </summary> private async Task ApplyLayout() { // layout starting, disable button layoutButton.IsEnabled = false; // create the layout algorithm var layout = new HierarchicLayout { OrthogonalRouting = true, LayoutOrientation = LayoutOrientation.TopToBottom }; // do the layout await graphControl.MorphLayout(layout, TimeSpan.FromSeconds(1), new HierarchicLayoutData { //Automatically determine port constraints for source and target NodePortCandidateSets = { Delegate = node => { var candidateSet = new PortCandidateSet(); // iterate the port descriptors var descriptors = PortDescriptor.CreatePortDescriptors(((FlowChartNodeStyle)node.Style).FlowChartType); foreach (var portDescriptor in descriptors) { PortCandidate candidate; // isn't a fixed port candidate (location is variable) if (portDescriptor.X == int.MaxValue) { // create a port candidate at the specified side (east, west, north, south) and apply a cost to it candidate = PortCandidate.CreateCandidate(portDescriptor.Side, portDescriptor.Cost); } else { // create a candidate at a fixed location and side var x = portDescriptor.X - node.Layout.Width / 2; var y = portDescriptor.Y - node.Layout.Height / 2; candidate = PortCandidate.CreateCandidate(x, y, portDescriptor.Side, portDescriptor.Cost); } candidateSet.Add(candidate, portDescriptor.Capacity); } return(candidateSet); } }, SourceGroupIds = { Delegate = edge => { // create bus-like edge groups for outgoing edges at Start nodes var sourceNode = edge.SourcePort.Owner as INode; if (sourceNode != null && ((((FlowChartNodeStyle)sourceNode.Style).FlowChartType)) == FlowChartType.Start) { return(sourceNode); } return(null); } }, TargetGroupIds = { Delegate = edge => { // create bus-like edge groups for incoming edges at Branch nodes var targetNode = edge.TargetPort.Owner as INode; if (targetNode != null && (((FlowChartNodeStyle)targetNode.Style).FlowChartType) == FlowChartType.Branch) { return(targetNode); } return(null); } } }); // enable button again layoutButton.IsEnabled = true; }