/// <summary> /// Perform a hierarchic layout that also configures the tables /// </summary> /// <remarks>Table support is automatically enabled in <see cref="LayoutExecutor"/>. The layout will: /// <list type="bullet"> /// <item> /// Arrange all leaf nodes in a hierarchic layout inside their respective table cells /// </item> /// <item>Resize all table cells to encompass their child nodes. Optionally, <see cref="TableLayoutConfigurator.Compaction"/> allows to shrink table cells, other wise, table cells /// can only grow.</item> /// </list> /// </remarks> private async void LayoutButton_Click(object sender, EventArgs e) { var hl = new HierarchicLayout { ComponentLayoutEnabled = false, LayoutOrientation = LayoutOrientation.LeftToRight, OrthogonalRouting = true, RecursiveGroupLayering = false }; ((SimplexNodePlacer)hl.NodePlacer).BarycenterMode = true; //We use Layout executor convenience method that already sets up the whole layout pipeline correctly var layoutExecutor = new LayoutExecutor(graphControl, hl) { //Table layout is enabled by default already... ConfigureTableLayout = true, Duration = TimeSpan.FromMilliseconds(500), AnimateViewport = true, UpdateContentRect = true, TargetBoundsInsets = graphEditorInputMode.ContentRectMargins, RunInThread = true, //Table cells may only grow by an automatic layout. TableLayoutConfigurator = { Compaction = false } }; await layoutExecutor.Start(); }
///<summary> /// Execute the layout algorithm. /// </summary> private async Task ApplyLayout() { var treeLayout = new TreeLayout(); var executor = new LayoutExecutor(graphControl, treeLayout) { AnimateViewport = true, Duration = TimeSpan.FromMilliseconds(500), UpdateContentRect = true, LayoutData = new TreeLayoutData { NodePlacers = { Mapper = placers }, AssistantNodes = { Mapper = assistants }, OutEdgeComparers = { Constant = (edge1, edge2) => { var value1 = GetOrdinal(edge1); int value2 = GetOrdinal(edge2); return(value1 - value2); } } } }; try { await executor.Start(); } catch (Exception e) { MessageBox.Show(this, "Layout did not complete successfully.\n" + e.Message); } }
/// <summary> /// Routes the edges given in the list according to the current /// EdgeRouter settings stored in the PolylineEdgeRouterCustomConfig /// </summary> private async Task RouteAffectedEdges(IEnumerable <IEdge> edges) { try { // route the affected edges edgeRouterConfig.ScopeItem = Scope.RouteAffectedEdges; // get the layout algorithm with current settings var layoutAlgorithm = GetLayoutAlgorithm(); // create a layout executor with 0.5 seconds execution time and the affected edges var layoutExecutor = new LayoutExecutor(graphControl, fullGraph, layoutAlgorithm) { LayoutData = new EdgeRouterData { AffectedEdges = { Source = edges } }, Duration = TimeSpan.FromSeconds(0.5), AnimateViewport = true, EasedAnimation = true, UpdateContentRect = true }; await layoutExecutor.Start(); } catch (Exception e) { MessageBox.Show(this, "Layout did not complete successfully.\n" + e.Message); } }
/// <summary> /// Applies this configuration to the given <see cref="GraphControl"/>. /// </summary> /// <remarks> /// This is the main method of this class. Typically, it calls <see cref="CreateConfiguredLayout"/> to create and /// configure a layout and <see cref="CreateConfiguredLayoutData"/> to get a suitable <see cref="LayoutData"/> instance /// for the layout. /// </remarks> /// <param name="graphControl">The <c>GraphControl</c> to apply the configuration on.</param> /// <param name="doneHandler">A callback that is called after the configuration is applied. Can be <see langword="null"/></param> public virtual async Task Apply(GraphControl graphControl, Action doneHandler) { if (layoutRunning) { await graphControl.Dispatcher.BeginInvoke(DispatcherPriority.Background, doneHandler); return; } var layout = CreateConfiguredLayout(graphControl); if (layout == null) { await graphControl.Dispatcher.BeginInvoke(DispatcherPriority.Background, doneHandler); return; } var layoutData = CreateConfiguredLayoutData(graphControl, layout); // configure the LayoutExecutor var layoutExecutor = new LayoutExecutor(graphControl, new MinimumNodeSizeStage(layout)) { Duration = TimeSpan.FromSeconds(0.75), AnimateViewport = true, EasedAnimation = true, PortAdjustmentPolicy = PortAdjustmentPolicy.Lengthen }; // set the cancel duration for the layout computation to 20s layoutExecutor.AbortHandler.CancelDuration = TimeSpan.FromSeconds(20); // set the layout data to the LayoutExecutor if (layoutData != null) { layoutExecutor.LayoutData = layoutData; } // start the LayoutExecutor try { await layoutExecutor.Start(); layoutRunning = false; } catch (AlgorithmAbortedException) { MessageBox.Show( "The layout computation has been canceled because the maximum configured runtime of 20 seconds has been exceeded."); } catch (Exception e) { MessageBox.Show( "Running the layout has encountered an error: " + e.Message); } finally { PostProcess(graphControl); // clean up mapperRegistry graphControl.Graph.MapperRegistry.RemoveMapper( LayoutGraphAdapter.EdgeLabelLayoutPreferredPlacementDescriptorDpKey ); doneHandler(); } }
/// <summary> /// Prepares the layout execution. /// </summary> public void InitializeLayout() { // prepare undo/redo oldClearRect = clearRect.ToRectD(); layoutEdit = Graph.BeginEdit("Clear Area", "Clear Area"); resetToOriginalGraphStageData = CreateGivenCoordinateStageData(); executor = CreateDraggingLayoutExecutor(); }
/// <summary> /// Initializes the layout calculation. /// </summary> public void InitializeLayout() { resetToOriginalGraphStageData = CreateGivenCoordinatesStageData(n => !nodes.Contains(n), e => !IsSubgraphEdge(e)); // hide edge path for any edge between a node in 'nodes' and a node not in 'nodes' HideInterEdges(); executor = GetDragLayoutExecutor(); state = GestureState.Dragging; }
/// <summary> /// Called before the a layout run starts. /// </summary> private void OnLayoutStarting() { switch (state) { case GestureState.Initializing: resetToOriginalGraphStageData = CreateGivenCoordinatesStageData(n => true, e => true); executor = CreateInitializingLayoutExecutor(); // highlight the parent of the current subtree and make the connection to the root invisible if (subtree.Parent != null) { canvasObjectEdge = graphControl.GraphModelManager.GetCanvasObject(subtree.ParentToRootEdge); canvasObjectEdge.Visible = false; subtree.NewParent = subtree.Parent; UpdateComponents(); graphControl.HighlightIndicatorManager.AddHighlight(subtree.NewParent); } break; case GestureState.Cancelling: // make root edge visible if (canvasObjectEdge != null) { canvasObjectEdge.Visible = true; } // remove highlight if (subtree.NewParent != null) { graphControl.HighlightIndicatorManager.RemoveHighlight(subtree.NewParent); } // reset to original graph layout executor = CreateCanceledLayoutExecutor(); break; case GestureState.Finishing: // before the last run starts, we have to reparent the subtree if (ApplyNewParent()) { canvasObjectEdge = graphControl.GraphModelManager.GetCanvasObject(subtree.ParentToRootEdge); canvasObjectEdge.Visible = false; } // last layout run also includes subtree executor = CreateFinishedLayoutExecutor(); // remove highlight if (subtree.NewParent != null) { graphControl.HighlightIndicatorManager.RemoveHighlight(subtree.NewParent); } state = GestureState.Finished; break; } }
/// <summary> /// Called before the a layout run starts. /// </summary> private void OnExecutorStarting() { if (canceled) { // use an executor that resets the graph to original layout executor = CreateCanceledLayoutExecutor(); } else { clearAreaLayout.Area = clearRect.ToRectD().ToYRectangle(); } }
/// <summary> /// Called before the a layout run starts. /// </summary> private void OnExecutorStarting() { if (canceled) { // reset to original graph layout executor = CreateCanceledLayoutExecutor(); } else { clearAreaLayout.Area = ClearRectangle.ToYRectangle(); } }
/// <summary> /// Applies this configuration to the given <see cref="GraphControl"/>. /// </summary> /// <remarks> /// This is the main method of this class. Typically, it calls <see cref="CreateConfiguredLayout"/> to create and /// configure a layout and <see cref="CreateConfiguredLayoutData"/> to get a suitable <see cref="LayoutData"/> instance /// for the layout. /// </remarks> /// <param name="graphControl">The <c>GraphControl</c> to apply the configuration on.</param> /// <param name="doneHandler">A callback that is called after the configuration is applied. Can be <see langword="null"/></param> public virtual async Task Apply(GraphControl graphControl, Action doneHandler) { if (layoutRunning) { graphControl.BeginInvoke(doneHandler); return; } var layout = CreateConfiguredLayout(graphControl); if (layout == null) { graphControl.BeginInvoke(doneHandler); return; } var layoutData = CreateConfiguredLayoutData(graphControl, layout); // configure the LayoutExecutor var layoutExecutor = new LayoutExecutor(graphControl, new MinimumNodeSizeStage(layout)) { Duration = TimeSpan.FromSeconds(1), AnimateViewport = true }; // set the cancel duration for the layout computation to 20s layoutExecutor.AbortHandler.CancelDuration = TimeSpan.FromSeconds(20); // set the layout data to the LayoutExecutor if (layoutData != null) { layoutExecutor.LayoutData = layoutData; } // start the LayoutExecutor try { await layoutExecutor.Start(); layoutRunning = false; } catch (AlgorithmAbortedException) { MessageBox.Show( "The layout computation has been canceled because the maximum configured runtime of 20 seconds has been exceeded."); } catch (Exception e) { MessageBox.Show( "Running the layout has encountered an error: " + e.Message); } finally { PostProcess(graphControl); doneHandler(); } }
private async Task RunLayout(ILayoutAlgorithm layout, LayoutData layoutData) { var executor = new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, Duration = TimeSpan.FromMilliseconds(500), AnimateViewport = true }; try { await executor.Start(); } catch (Exception e) { MessageBox.Show(this, "Layout did not complete successfully.\n" + e.Message); } EnableButtons(); }
/// <summary> /// Prepares the <see cref="LayoutExecutor"/> that is called after the selection is deleted. /// </summary> private void OnDeletingSelection(object sender, SelectionEventArgs <IModelItem> e) { // determine the bounds of the selection var rect = GetBounds(e.Selection); // configure the layout that will fill free space var layout = new FillAreaLayout { ComponentAssignmentStrategy = ComponentAssignmentStrategy, Area = rect.ToYRectangle() }; // configure the LayoutExecutor that will perform the layout and morph the result executor = new LayoutExecutor(GraphControl, layout) { Duration = TimeSpan.FromMilliseconds(100) }; }
/// <summary> /// Invokes a layout specified by the current layout type. /// </summary> /// <remarks> /// If there is a fixed node, the layout is calculated incrementally. /// </remarks> /// <param name="fixedNode">If defined, the layout will be incrementally and this node remains at its location.</param> public async Task RunLayout(INode fixedNode = null) { if (layoutRunning) { return; } layoutRunning = true; var incremental = fixedNode != null; // configure layout var layout = layoutType == LayoutHierarchic ? (ILayoutAlgorithm)GetHierarchicLayout(incremental) : GetOrthogonalLayout(); var layoutData = layoutType == LayoutHierarchic ? (LayoutData)GetHierarchicLayoutData() : GetOrthogonalLayoutData(); if (incremental) { // fixate the location of the given fixed node layout = new FixNodeLayoutStage(layout) { FixPointPolicy = FixPointPolicy.LowerLeft }; layoutData = new CompositeLayoutData(layoutData, new FixNodeLayoutData { FixedNodes = new ItemCollection <INode> { Item = fixedNode } } ); } EnableUI(false); // configure layout execution to not move the view port var executor = new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, AnimateViewport = !incremental, Duration = TimeSpan.FromMilliseconds(500), UpdateContentRect = true, }; // start layout await executor.Start(); layoutRunning = false; EnableUI(true); }
/// <summary> /// Perform the layout operation /// </summary> private async Task ApplyLayout(ILayoutAlgorithm layout, LayoutData layoutData, bool animateViewport) { // layout starting, disable button hlLayoutButton.IsEnabled = false; orthoEdgeRouterButton.IsEnabled = false; // do the layout var executor = new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, Duration = TimeSpan.FromSeconds(1), AnimateViewport = animateViewport }; await executor.Start(); // layout finished, enable layout button again hlLayoutButton.IsEnabled = true; orthoEdgeRouterButton.IsEnabled = true; }
/// <summary> /// Does the label placement using the generic labeling algorithm. Before this, the model of the labels is /// set according to the option handlers settings. /// </summary> private async Task DoLayout(bool fitViewToContent) { // fix node layout stage is used to keep the bounding box of the graph in the view port var layoutExecutor = new LayoutExecutor(graphControl, new FixNodeLayoutStage(layoutAlgorithm)) { Duration = TimeSpan.FromMilliseconds(500), AnimateViewport = fitViewToContent, LayoutData = new CompositeLayoutData( new FixNodeLayoutData { FixedNodes = { Delegate = node => true } }, new LabelingData { EdgeLabelPreferredPlacement = { Mapper = descriptorMapper } }) }; await layoutExecutor.Start(); }
/// <summary> /// Called before the a layout run starts. /// </summary> private void OnLayoutStarting() { switch (state) { case GestureState.Dragging: executor = GetDragLayoutExecutor(); fillLayout?.ConfigureAreaOutline(nodes); break; case GestureState.Cancelling: executor = CreateCancelLayoutExecutor(); state = GestureState.Cancelled; break; case GestureState.Finishing: executor = CreateFinishLayoutExecutor(); fillLayout?.ConfigureAreaOutline(nodes); state = GestureState.Finished; break; } }
/// <summary> /// Helper method that refreshes the layout after children or parent nodes have been added /// or removed. /// </summary> private async Task RefreshLayout(int count, INode centerNode) { if (doingLayout) { return; } doingLayout = true; if (count != hiddenNodesSet.Count) { // tell our filter to refresh the graph filteredGraphWrapper.NodePredicateChanged(); // the commands CanExecute state might have changed - suggest a requery. CommandManager.InvalidateRequerySuggested(); // now layout the graph in animated fashion IGraph tree = graphControl.Graph; // configure the layout data var layoutData = CreateLayoutData(tree, centerNode); // create the layout algorithm (with a stage that fixes the center node in the coordinate system var layout = new BendDuplicatorStage(new FixNodeLayoutStage(new TreeLayout())); // configure a LayoutExecutor var executor = new LayoutExecutor(graphControl, layout) { AnimateViewport = centerNode == null, EasedAnimation = true, RunInThread = true, UpdateContentRect = true, LayoutData = layoutData, Duration = TimeSpan.FromMilliseconds(500) }; await executor.Start(); graphControl.Graph.MapperRegistry.RemoveMapper("CenterNode"); doingLayout = false; LimitViewport(); } }
private async Task RunLayout(bool animateViewport) { if (currentLayout != null) { // provide additional data to configure the FixNodeLayoutStage FixNodeLayoutData fixNodeLayoutData = new FixNodeLayoutData(); // specify the node whose position is to be fixed during layout fixNodeLayoutData.FixedNodes.Item = toggledNode; // run the layout and animate the result var layoutExecutor = new LayoutExecutor(graphControl, currentLayout) { UpdateContentRect = true, AnimateViewport = animateViewport, Duration = TimeSpan.FromSeconds(0.3d), LayoutData = fixNodeLayoutData }; await layoutExecutor.Start(); toggledNode = null; } }
/// <summary> /// Calculates a layout taking the node types into account. /// </summary> private async Task ApplyLayout(bool animate) { var sample = (Sample)SampleGraphComboBox.SelectedItem; var considerTypes = ConsiderTypes.IsChecked == true; var layout = sample.Layout; var layoutData = considerTypes ? sample.LayoutData : null; var layoutExecutor = new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, Duration = animate ? TimeSpan.FromMilliseconds(700) : TimeSpan.Zero, PortAdjustmentPolicy = PortAdjustmentPolicy.Always, RunInThread = false, AnimateViewport = true }; EnableUi(false); await layoutExecutor.Start(); EnableUi(true); }
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(); }
/// <summary> /// Starts a layout calculation if none is already running. /// </summary> public async void RunLayout() { if (layoutIsRunning) { // if another layout is running: request a new layout and exit layoutPending = true; return; } do { // prevent other layouts from running layoutIsRunning = true; // clear the pending flag: the requested layout will run now layoutPending = false; // start the layout if (canceled) { // reset to original graph layout executor = CreateCanceledLayoutExecutor(); } else if (finished) { // calculate the final layout executor = CreateFinishedLayoutExecutor(); } else { // update the location of the components outline UpdateOutline(); } await executor.Start(); // free the executor for the next layout layoutIsRunning = false; // repeat if another layout has been requested in the meantime } while (layoutPending); }
///<summary> /// Runs either the table or the three tiers layout depending on the selected scenario. ///</summary> private async Task RunLayout() { DisableButtons(); var layoutData = new PartialLayoutData { AffectedEdges = { Mapper = partialEdgesMapper }, AffectedNodes = { Mapper = partialNodesMapper } }; var executor = new LayoutExecutor( graphControl, CreateConfiguredPartialLayout()) { Duration = TimeSpan.FromMilliseconds(500), AnimateViewport = true, LayoutData = layoutData }; try { await executor.Start(); } catch (Exception e) { MessageBox.Show(this, "Layout did not complete successfully.\n" + e.Message); } EnableButtons(); }
/// <summary> /// Called after the a layout run finished. /// </summary> private void OnLayoutFinished() { switch (state) { case GestureState.Initializing: resetToWorkingGraphStageData = CreateGivenCoordinatesStageData(n => !subtree.Nodes.Contains(n), e => !subtree.Edges.Contains(e)); executor = CreateDraggingLayoutExecutor(); state = GestureState.Dragging; break; case GestureState.Dragging: // check for a new parent candidate UpdateNewParent(); break; case GestureState.Finished: // make root edge visible if (subtree.Parent == subtree.NewParent && canvasObjectEdge != null) { canvasObjectEdge.Visible = true; } break; } }
/// <summary> /// Does the label placement using the generic labeling algorithm. Before this, the model and size of the labels is /// set according to the option handlers settings. /// </summary> private async Task DoLabelPlacement() { if (inLayout) { return; } inLayout = true; toolStrip.Enabled = false; //desired label model ILabelModel labelModel = LabelModels[labelModelComboBox.SelectedItem.ToString()]; //desired label size int size = Convert.ToInt32(sizeNumericUpDown.NumericUpDownControl.Text); foreach (var label in graphControl.Graph.Labels) { if (label.Owner is INode) { // only update the label model parameter if the label model changed if (labelModel != graphControl.Graph.NodeDefaults.Labels.LayoutParameter.Model) { graphControl.Graph.SetLabelLayoutParameter(label, labelModel.CreateDefaultParameter()); } var cityLabelStyle = label.Style as CityLabelStyle; if (cityLabelStyle != null && cityLabelStyle.InnerLabelStyle is DefaultLabelStyle) { var font = ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).Font; ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).Font = new Font(font.FontFamily, size); } graphControl.Graph.AdjustLabelPreferredSize(label); } } { // set as default label model parameter graphControl.Graph.NodeDefaults.Labels.LayoutParameter = labelModel.CreateDefaultParameter(); var cityLabelStyle = graphControl.Graph.NodeDefaults.Labels.Style as CityLabelStyle; if (cityLabelStyle != null && cityLabelStyle.InnerLabelStyle is DefaultLabelStyle) { var font = ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).Font; ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).Font = new Font(font.FontFamily, size); } } graphControl.Invalidate(); // configure and run the layout algorithm var labelingAlgorithm = new GenericLabeling { MaximumDuration = 0, OptimizationStrategy = OptimizationStrategy.Balanced, PlaceEdgeLabels = false, PlaceNodeLabels = true, ReduceLabelOverlaps = true, ProfitModel = new ExtendedLabelCandidateProfitModel(), }; var layoutExecutor = new LayoutExecutor(graphControl, graphControl.Graph, labelingAlgorithm) { Duration = TimeSpan.FromMilliseconds(500), EasedAnimation = true, AnimateViewport = false, UpdateContentRect = true }; await layoutExecutor.Start(); toolStrip.Enabled = true; inLayout = false; }
/// <summary> /// Does the label placement using the generic labeling algorithm. Before this, the model and size of the labels is /// set according to the option handlers settings. /// </summary> private async Task DoLabelPlacement() { if (inLayout) { return; } inLayout = true; toolBar.IsEnabled = false; editorControl.IsEnabled = false; //desired label model ILabelModel labelModel = LabelModels[(string)handler[LABEL_MODEL].Value]; int size = (int)handler[LABEL_SIZE].Value; foreach (var label in graphControl.Graph.Labels) { if (label.Owner is INode) { // only update the label model parameter if the label model changed if (labelModel != graphControl.Graph.NodeDefaults.Labels.LayoutParameter.Model) { graphControl.Graph.SetLabelLayoutParameter(label, labelModel.CreateDefaultParameter()); } var cityLabelStyle = label.Style as CityLabelStyle; if (cityLabelStyle != null && cityLabelStyle.InnerLabelStyle is DefaultLabelStyle) { ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).TextSize = size; } graphControl.Graph.AdjustLabelPreferredSize(label); } } { // set as default label model parameter graphControl.Graph.NodeDefaults.Labels.LayoutParameter = labelModel.CreateDefaultParameter(); var cityLabelStyle = graphControl.Graph.NodeDefaults.Labels.Style as CityLabelStyle; if (cityLabelStyle != null && cityLabelStyle.InnerLabelStyle is DefaultLabelStyle) { ((DefaultLabelStyle)cityLabelStyle.InnerLabelStyle).TextSize = size; } } graphControl.Invalidate(); // configure and run the layout algorithm var labelingAlgorithm = new GenericLabeling { MaximumDuration = 0, OptimizationStrategy = OptimizationStrategy.Balanced, PlaceEdgeLabels = false, PlaceNodeLabels = true, ReduceLabelOverlaps = true, ProfitModel = new ExtendedLabelCandidateProfitModel(), }; var layoutExecutor = new LayoutExecutor(graphControl, graphControl.Graph, labelingAlgorithm) { Duration = TimeSpan.FromMilliseconds(500), EasedAnimation = true, AnimateViewport = false, UpdateContentRect = true }; await layoutExecutor.Start(); toolBar.IsEnabled = true; editorControl.IsEnabled = true; inLayout = false; }
/// <summary> /// Prepares the layout execution. /// </summary> public void InitializeLayout() { layoutEdit = Graph.BeginEdit("Clear Area", "Clear Area"); resetToOriginalGraphStageData = CreateGivenCoordinateStageData(); executor = CreateDraggingLayoutExecutor(); }
/// <summary> /// Prepares the layout execution. /// </summary> public void InitializeLayout() { resetToOriginalGraphStageData = CreateGivenCoordinateStageData(); executor = CreateDraggingLayoutExecutor(); }