/// <summary> /// Creates a layout data suiting the <paramref name="resizeState"/>. /// </summary> /// <param name="resizeState">The current state of the gesture</param> /// <returns>A layout data suiting the <paramref name="resizeState"/>.</returns> private LayoutData CreateLayoutData(ResizeState resizeState) { var layoutData = new CompositeLayoutData(resetToOriginalGraphStageData); if (resizeState == ResizeState.Shrinking) { layoutData.Items.Add(new FillAreaLayoutData { FixedNodes = { Items = nodes } }); if (state == GestureState.Finishing) { // only route edges for the final layout layoutData.Items.Add(new EdgeRouterData { AffectedNodes = { Items = nodes } }); } } else { if (resizeState == ResizeState.Both) { layoutData.Items.Add(new FillAreaLayoutData { FixedNodes = { Items = nodes } }); } layoutData.Items.Add(new ClearAreaLayoutData { AreaNodes = { Items = nodes } }); } return(layoutData); }
/// <summary> /// A <see cref="LayoutExecutor"/> that is used during the drag and drop operation. /// </summary> /// <remarks> /// First, all nodes and edges are pushed back into place before the drag started. Then space /// is made for the component at its current position. The animation morphs all elements to the /// calculated positions. /// </remarks> private LayoutExecutor CreateDraggingLayoutExecutor() { var layout = new GivenCoordinatesStage( clearAreaLayout = new ClearAreaLayout { ComponentAssignmentStrategy = ComponentAssignmentStrategy.Customized, ClearAreaStrategy = ClearAreaStrategy.PreserveShapes }); var layoutData = new CompositeLayoutData( resetToOriginalGraphStageData, new ClearAreaLayoutData { ComponentIds = { Mapper = components } }); var items = new List <IModelItem>(); items.AddRange(Component.Nodes); items.AddRange(Component.Edges); clearAreaLayout.ConfigureAreaOutline(items, 10); return(new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, RunInThread = true, Duration = TimeSpan.FromMilliseconds(150) }); }
/// <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> /// A <see cref="LayoutExecutor"/> that is used after the drag and drop operation is finished. /// </summary> /// <remarks> /// All nodes and edges are pushed back into place before the drag started. Then space is made /// for the component that has been dropped. /// </remarks> private LayoutExecutor CreateFinishedLayoutExecutor() { var layout = new GivenCoordinatesStage( new ClearAreaLayout { ComponentAssignmentStrategy = ComponentAssignmentStrategy.Customized, ClearAreaStrategy = ClearAreaStrategy.PreserveShapes }); var layoutData = new CompositeLayoutData( resetToOriginalGraphStageData, new ClearAreaLayoutData { AreaNodes = { Source = Component.Nodes }, ComponentIds = { Mapper = components } }); return(new LayoutExecutor(graphControl, layout) { LayoutData = layoutData, RunInThread = true, Duration = TimeSpan.FromMilliseconds(150) }); }
/// <summary> /// Lays out the graph with nodes that are laid out incrementally. /// </summary> /// <param name="incrementalNodes">Nodes that should be inserted into the graph incrementally.</param> private async Task ApplyLayout(HashSet <INode> incrementalNodes) { toolStrip.Enabled = false; // We'll use an OrganicLayout with OrganicEdgeRouter to get a pleasing image for most databases var layout = new OrganicLayout { NodeEdgeOverlapAvoided = true, NodeOverlapsAllowed = false, PreferredEdgeLength = 180, MinimumNodeDistance = 80, StarSubstructureStyle = StarSubstructureStyle.Radial, StarSubstructureSize = 3, ConsiderNodeSizes = true, CycleSubstructureStyle = CycleSubstructureStyle.Circular, StarSubstructureTypeSeparation = false, LabelingEnabled = true, ParallelEdgeRouterEnabled = true, Scope = incrementalNodes.Count > 0 ? Scope.MainlySubset : Scope.All }; layout.PrependStage(new OrganicEdgeRouter() { ConsiderExistingBends = false, KeepExistingBends = false, EdgeNodeOverlapAllowed = false, RouteAllEdges = false, MinimumDistance = 10 }); // GenericLabeling ensures that labels don't overlap layout.PrependStage(new GenericLabeling { RemoveNodeOverlaps = true }); // CurveFittingLayoutStage adds Bezier control points so we can use BezierEdgeStyle to render the edges layout.PrependStage(new CurveFittingLayoutStage()); var layoutData = new CompositeLayoutData( new OrganicLayoutData { NodeTypes = { Delegate = node => ((INeo4jNode)node.Tag).Labels[0] }, AffectedNodes = { Source = incrementalNodes }, }, new LabelingData { EdgeLabelPreferredPlacement = { Constant = new PreferredPlacementDescriptor { AngleReference = LabelAngleReferences.RelativeToEdgeFlow, PlaceAlongEdge = LabelPlacements.AtSource, SideOfEdge = LabelPlacements.RightOfEdge | LabelPlacements.LeftOfEdge } } } ); await graphControl.MorphLayout(layout, TimeSpan.FromMilliseconds(700), layoutData); toolStrip.Enabled = true; }