Esempio n. 1
0
        private static IBend CreateBends([NotNull] IGraph graph, [NotNull] IEdge edge, int segmentIndex, double ratio,
                                         [NotNull] IListEnumerable <IPoint> pathPoints)
        {
            //Create 3 bends and adjust the neighbors
            //The first bend we need to touch is at startIndex
            var startIndex = segmentIndex * 3;

            //This holds the new coordinates left and right of the split point
            //We don't actually need all of them, but this keeps the algorithm more straightforward.
            var left  = new PointD[4];
            var right = new PointD[4];

            //Determine the new control points to cleanly split the curve

            GetCubicSplitPoints(ratio,
                                new[] {
                pathPoints[startIndex].ToPointD(), pathPoints[startIndex + 1].ToPointD(),
                pathPoints[startIndex + 2].ToPointD(), pathPoints[startIndex + 3].ToPointD()
            }, left, right);

            //Previous control point - does always exist as a bend, given our precondition
            var previousBend = edge.Bends[startIndex];
            //Next control point - also always exists given the precondition for bend counts (i.e. there have to be at least two)
            var nextBend = edge.Bends[startIndex + 1];

            //We create the three new bends between previous bend and next bend and adjust these two.
            //We don't have to adjust more bends, since we just have a cubic curve.
            IBend bendToMove;
            var   engine = graph.GetUndoEngine();

            //Wrap everything into a single compound edit, so that everything can be undone in a single unit
            using (var edit = graph.BeginEdit("Create Bezier Bend", "Create Bezier Bend")) {
                try {
                    //Adjust the previous bend - given the split algorithm, its coordinate is in left[1]
                    //(left[0] is actually kept unchanged from the initial value)
                    var oldPrevLocation = previousBend.Location.ToPointD();
                    var newPrevLocation = left[1];
                    graph.SetBendLocation(previousBend, newPrevLocation);
                    // Add unit to engine
                    graph.AddUndoUnit("Set bend location", "Set bend location",
                                      () => graph.SetBendLocation(previousBend, oldPrevLocation),
                                      () => graph.SetBendLocation(previousBend, newPrevLocation));

                    //Insert the new triple, using the values from left and right in order
                    graph.AddBend(edge, left[2], startIndex + 1);
                    bendToMove = graph.AddBend(edge, left[3], startIndex + 2);
                    //right[0] == left[3], so right[1] is the next new control point
                    graph.AddBend(edge, right[1], startIndex + 3);

                    //Adjust the next bend
                    var oldNextLocation = nextBend.Location.ToPointD();
                    var newNextLocation = right[2];
                    graph.SetBendLocation(nextBend, newNextLocation);
                    // Add unit to engine
                    graph.AddUndoUnit("Set bend location", "Set bend location",
                                      () => graph.SetBendLocation(nextBend, oldNextLocation),
                                      () => graph.SetBendLocation(nextBend, newNextLocation));
                } catch {
                    //Cancel the edit in case anything goes wrong.
                    edit.Cancel();
                    throw;
                }
            }

            return(bendToMove);
        }
        /// <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&lt;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...
            }
        }