/// <summary> /// Initializes the graph instance setting default styles /// and creating a small sample graph. /// </summary> protected virtual void InitializeGraph() { IGraph graph = Graph; // load a sample graph new GraphMLIOHandler().Read(graph, "Resources/sample.graphml"); // set some defaults graph.NodeDefaults.Style = Enumerable.First(graph.Nodes).Style; graph.NodeDefaults.ShareStyleInstance = true; // we start with a simple run of OrganicLayout to get a good starting result // the algorithm is optimized to "unfold" graphs quicker than // interactive organic, so we use this result as a starting solution var initialLayout = new OrganicLayout { MinimumNodeDistance = 50 }; graph.ApplyLayout(initialLayout); // center the initial graph GraphControl.FitGraphBounds(); movedNodes = new List <INode>(); // we wrap the PositionHandler for nodes so that we always have the collection of nodes // that are currently being moved available in "movedNodes". // this way we do not need to know how the node is moved and do not have to guess // what elements are currently being moved based upon selection, etc. graph.GetDecorator().NodeDecorator.PositionHandlerDecorator.SetImplementationWrapper( (item, implementation) => new CollectingPositionHandlerWrapper(item, movedNodes, implementation)); // create a copy of the graph for the layout algorithm LayoutGraphAdapter adapter = new LayoutGraphAdapter(graphControl.Graph); copiedLayoutGraph = adapter.CreateCopiedLayoutGraph(); // create and start the layout algorithm layout = StartLayout(); WakeUp(); // register a listener so that structure updates are handled automatically graph.NodeCreated += delegate(object source, ItemEventArgs <INode> args) { if (layout != null) { var center = args.Item.Layout.GetCenter(); layout.SyncStructure(true); //we nail down all newly created nodes var copiedNode = copiedLayoutGraph.GetCopiedNode(args.Item); layout.SetCenter(copiedNode, center.X, center.Y); layout.SetInertia(copiedNode, 1); layout.SetStress(copiedNode, 0); layout.WakeUp(); } }; graph.NodeRemoved += OnStructureChanged; graph.EdgeCreated += OnStructureChanged; graph.EdgeRemoved += OnStructureChanged; }
/// <summary> /// Executes the module on the given graph using the provided context. /// </summary> /// <remarks> /// The layout will be calculated <see cref="RunInBackground">optionally</see> /// in a separate thread in method <see cref="RunModuleAsync"/>. /// </remarks> /// <param name="graph">The graph to execute on.</param> /// <param name="newContext">The context to use. This method will query a <c>ISelectionModel<IModelItem></c></param> /// for the selected nodes and edges and the <c>GraphControl</c> to morph the layout. protected virtual async Task StartWithIGraph(IGraph graph, ILookup newContext) { this.graph = graph; if (ShouldConfigureTableLayout()) { PrepareTableLayout(); } ISelectionModel <IModelItem> selectionModel = newContext.Lookup <ISelectionModel <IModelItem> >(); LayoutGraphAdapter adapter = new LayoutGraphAdapter(graph, selectionModel); this.layoutGraph = adapter.CreateCopiedLayoutGraph(); ILookup additionalLookup = Lookups.Single(layoutGraph, typeof(LayoutGraph)); ILookup wrappedLookup = Lookups.Wrapped(newContext, additionalLookup); try { ICompoundEdit compoundEdit = graph.BeginEdit("Layout", "Layout"); CheckReentrant(wrappedLookup); ConfigureModule(); if (RunInBackground) { // without the LayoutExecutor helper class on the layout graph side of things, we register the aborthandler // to the layout graph with the utility method provided by AbortHandler var abortHandler = AbortHandler.CreateForGraph(layoutGraph); // now create the dialog that controls the abort handler abortDialog = new AbortDialog { AbortHandler = abortHandler, Owner = Application.Current.MainWindow }; // start the layout in another thread. var layoutThread = new Thread(async() => await RunModuleAsync(wrappedLookup, graph, compoundEdit)); // now if we are not doing a quick layout - and if it takes more than a few seconds, we open the dialog to // enable the user to stop or cancel the execution var showDialogTimer = new DispatcherTimer(DispatcherPriority.Normal, abortDialog.Dispatcher) { Interval = TimeSpan.FromSeconds(2) }; showDialogTimer.Tick += delegate { // it could be that the layout is already done - so check whether we still // need to open the dialog var dialogInstance = abortDialog; if (dialogInstance != null) { // open the abort dialog dialogInstance.Show(); } // we only want to let it go off once - so stop the timer showDialogTimer.Stop(); }; // kick-off the timer and the layout showDialogTimer.Start(); layoutThread.Start(); } else { await RunModuleAsync(wrappedLookup, graph, compoundEdit); } } catch (Exception e) { FreeReentrant(); TableLayoutConfigurator.CleanUp(graph); OnDone(new LayoutEventArgs(e)); //optionally do something here... } }