/// <summary>
        /// Layouts a connected graph with Multidimensional Scaling, using
        /// shortest-path distances as Euclidean target distances.
        /// </summary>
        /// <param name="geometryGraph">A graph.</param>
        /// <param name="settings">The settings for the algorithm.</param>
        /// <param name="x">Coordinate vector.</param>
        /// <param name="y">Coordinate vector.</param>
        internal static void LayoutGraphWithMds(GeometryGraph geometryGraph, MdsLayoutSettings settings, out double[] x, out double[] y)
        {
            x = new double[geometryGraph.Nodes.Count];
            y = new double[geometryGraph.Nodes.Count];
            if (geometryGraph.Nodes.Count == 0)
            {
                return;
            }
            if (geometryGraph.Nodes.Count == 1)
            {
                x[0] = y[0] = 0;
                return;
            }
            int    k        = Math.Min(settings.PivotNumber, geometryGraph.Nodes.Count);
            int    iter     = settings.GetNumberOfIterationsWithMajorization(geometryGraph.Nodes.Count);
            double exponent = settings.Exponent;

            var            pivotArray     = new int[k];
            PivotDistances pivotDistances = new PivotDistances(geometryGraph, false, pivotArray);

            pivotDistances.Run();
            double[][] c = pivotDistances.Result;
            MultidimensionalScaling.LandmarkClassicalScaling(c, out x, out y, pivotArray);
            ScaleToAverageEdgeLength(geometryGraph, x, y);

            if (iter > 0)
            {
                AllPairsDistances apd = new AllPairsDistances(geometryGraph, false);
                apd.Run();
                double[][] d = apd.Result;
                double[][] w = MultidimensionalScaling.ExponentialWeightMatrix(d, exponent);
                // MultidimensionalScaling.DistanceScaling(d, x, y, w, iter);
                MultidimensionalScaling.DistanceScalingSubset(d, x, y, w, iter);
            }
        }
        /// <summary>
        /// Default constructor
        /// </summary>
        public GViewer(){
            mdsLayoutSettings = new MdsLayoutSettings();
            sugiyamaSettings = new SugiyamaLayoutSettings();
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            BackwardEnabled = false;

            ForwardEnabled = false;

            toolbar.MouseMove += ToolBarMouseMoved;

            Assembly a = Assembly.GetExecutingAssembly();   
            foreach (string r in a.GetManifestResourceNames()){
                if (r.Contains("hmove.cur"))
                    panGrabCursor = new Cursor(a.GetManifestResourceStream(r));
                else if (r.Contains("oph.cur"))
                    panOpenCursor = new Cursor(a.GetManifestResourceStream(r));
            }

            originalCursor = Cursor;

            
            panButton.Pushed = false;
            windowZoomButton.Pushed = false;

            layoutSettingsButton.ToolTipText = "Configures the layout algorithm settings";

            undoButton.ToolTipText = "Undo layout editing";
            redoButton.ToolTipText = "Redo layout editing";
            forwardButton.ToolTipText = "Forward";
            panButton.ToolTipText = panButton.Pushed ? panButtonToolTipText : PanButtonDisabledToolTipText;
            windowZoomButton.ToolTipText = windowZoomButton.Pushed
                                               ? WindowZoomButtonToolTipText
                                               : windowZoomButtonDisabledToolTipText;

            InitDrawingLayoutEditor();

            toolbar.Invalidate();

            SuspendLayout();
            InitPanel();
            Controls.Add(toolbar);
            ResumeLayout();          
        }
Esempio n. 3
0
        /// <summary>
        /// Executes the algorithm.
        /// </summary>
        /// <summary>
        /// Executes the actual algorithm.
        /// </summary>
        protected override void RunInternal()
        {
            var g = new GeometryGraph();

            foreach (var v in graph.Nodes)
            {
                Debug.Assert(!(v is Cluster));
                var u = new Node(v.BoundaryCurve.Clone())
                {
                    UserData = v
                };
                v.AlgorithmData = new PivotMDSNodeWrap(u);
                g.Nodes.Add(u);
            }
            double avgLength = 0;

            foreach (var e in graph.Edges)
            {
                avgLength += e.Length;
                if (e.Source is Cluster || e.Target is Cluster)
                {
                    continue;
                }
                var u  = e.Source.AlgorithmData as PivotMDSNodeWrap;
                var v  = e.Target.AlgorithmData as PivotMDSNodeWrap;
                var ee = new Edge(u.node, v.node)
                {
                    Length = e.Length
                };
                g.Edges.Add(ee);
            }
            if (graph.Edges.Count != 0)
            {
                avgLength /= graph.Edges.Count;
            }
            else
            {
                avgLength = 100;
            }

            // create edges from the children of each parent cluster to the parent cluster node
            foreach (var c in graph.RootCluster.AllClustersDepthFirst())
            {
                if (c == graph.RootCluster)
                {
                    continue;
                }

                var u = new Node(CurveFactory.CreateRectangle(10, 10, new Point()));
                u.UserData      = c;
                c.AlgorithmData = new PivotMDSNodeWrap(u);
                g.Nodes.Add(u);

                foreach (var v in c.Nodes.Concat(from cc in c.Clusters select(Node) cc))
                {
                    var vv = v.AlgorithmData as PivotMDSNodeWrap;
                    g.Edges.Add(new Edge(u, vv.node)
                    {
                        Length = avgLength
                    });
                }
            }

            // create edges between clusters
            foreach (var e in graph.Edges)
            {
                if (e.Source is Cluster || e.Target is Cluster)
                {
                    var u  = e.Source.AlgorithmData as PivotMDSNodeWrap;
                    var v  = e.Target.AlgorithmData as PivotMDSNodeWrap;
                    var ee = new Edge(u.node, v.node)
                    {
                        Length = e.Length
                    };
                    g.Edges.Add(ee);
                }
            }

            // with 0 majorization iterations we just do PivotMDS
            MdsLayoutSettings settings = new MdsLayoutSettings
            {
                ScaleX = this.Scale,
                ScaleY = this.Scale,
                IterationsWithMajorization = 0,
                RemoveOverlaps             = false,
                AdjustScale = false
            };

            MdsGraphLayout mdsLayout = new MdsGraphLayout(settings, g);

            this.RunChildAlgorithm(mdsLayout, 1.0);

            g.UpdateBoundingBox();
            foreach (var v in graph.Nodes)
            {
                var m = v.AlgorithmData as PivotMDSNodeWrap;
                v.Center = m.node.Center;
            }
        }
Esempio n. 4
0
 MdsLayoutSettings GetMdsLayoutSettings() {
     var settings = new MdsLayoutSettings();
     settings.ScaleX *= 3;
     settings.ScaleY *= 3;
     settings.EdgeRoutingSettings.KeepOriginalSpline = true;
     settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Spline;
     settings.IterationsWithMajorization = _argsParser.OptionIsUsed(NoIterationsWithMajorization) ? 0 : 30;
     if (_argsParser.OptionIsUsed(AllowOverlapsInMds))
         settings.RemoveOverlaps = false;
     if (_argsParser.OptionIsUsed(SequentialRunOption))
         settings.RunInParallel = false;
     if (_argsParser.OptionIsUsed(StraightLineEdgesOption))
         settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.StraightLine;
     if (_argsParser.OptionIsUsed(NoEdgeRoutingOption))
         settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.None;
     //settings.AdjustScale = true;
     return settings;
 }
Esempio n. 5
0
        public static void MDS()
        {
            WriteMessage("Starting test...");

            WriteMessage("Create GeometryGraph");

            GeometryGraph graph = AbcdeGraph();

            //graph.Save("c:\\tmp\\saved.msagl");
            var settings = new MdsLayoutSettings();
            settings.RunInParallel = false;

            settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Rectilinear;

            LayoutHelpers.CalculateLayout(graph, settings, null);

            WriteMessage("Layout progressed");

            WriteMessage("");
            WriteMessage("Segments A->B");
            //OutputCurve(ab.Curve);

            WriteMessage("");
            WriteMessage("Segments B->C");
            //OutputCurve(bc.Curve);

            WriteMessage("");
            WriteMessage("Segments C->A");
            //OutputCurve(ca.Curve);

            foreach (var node in graph.Nodes)
            {
                WriteMessage(string.Format("{0}: {1} {2}", node.UserData, node.Center.X, node.Center.Y));
            }

            /*var canvas = HtmlContext.document.getElementById("drawing").As<HtmlCanvasElement>();
            var ctx = canvas.getContext("2d").As<CanvasRenderingContext2D>();

            var canvasHeight = canvas.height;

            var bounds = calcBounds(graph.Nodes);

            var xScale = canvas.width / bounds.Width;
            var yScale = canvas.height / bounds.Height;

            var xShift = -bounds.Left * xScale;
            var yShift = -(canvas.height - bounds.Top) * yScale;

            WriteMessage(string.Format("Scaling : {0} {1}", xScale, yScale));
            WriteMessage(string.Format("Shifting : {0} {1}", xShift, yShift));

            foreach (var msaglEdge in graph.Edges)
            {
                DrawEdge(ctx, msaglEdge, xShift, yShift, xScale, yScale, canvasHeight);
            }

            foreach (var msaglNode in graph.Nodes)
            {
                DrawNode(ctx, msaglNode, xShift, yShift, xScale, yScale, canvasHeight);
            }*/
        }
 void MDSLayout(MdsLayoutSettings settings, GeometryGraph component) {
     LayoutAlgorithmHelpers.ComputeDesiredEdgeLengths(settings.IdealEdgeLength, component);
     var layout = new MdsGraphLayout(settings, component);
     layout.Run(this.CancelToken);
     InitialLayoutHelpers.RouteEdges(component, settings, this.CancelToken);
     InitialLayoutHelpers.PlaceLabels(component, this.CancelToken);
     InitialLayoutHelpers.FixBoundingBox(component, settings);
 }
 /// <summary>
 /// Constructs the multidimensional scaling algorithm.
 /// </summary>
 public MdsGraphLayout(MdsLayoutSettings settings, GeometryGraph geometryGraph)
 {
     this.settings = settings;
     graph         = geometryGraph;
 }
Esempio n. 8
0
        static void Main(string[] args) {
#if DEBUG
            DisplayGeometryGraph.SetShowFunctions();
#endif
            ArgsParser.ArgsParser argsParser = SetArgsParser(args);

            if (argsParser.OptionIsUsed(PolygonDistanceTestOption))
                TestPolygonDistance();
            else if (argsParser.OptionIsUsed(TestCdtThreaderOption))
                TestCdtThreader();
            else if (argsParser.OptionIsUsed(RandomBundlingTest))
                RandomBundlingTests.RsmContent();


            bundling = argsParser.OptionIsUsed(BundlingOption);

            var gviewer = new GViewer();
            gviewer.MouseMove += Draw.GviewerMouseMove;
            if(argsParser.OptionIsUsed(FdOption)) {
                TestFD();
                gviewer.CurrentLayoutMethod = LayoutMethod.IcrementalLayout;
            }
            Form form = CreateForm(null, gviewer);
            if (argsParser.OptionIsUsed(AsyncLayoutOption))
                gviewer.AsyncLayout = true;

            string listOfFilesFile = argsParser.GetValueOfOptionWithAfterString(ListOfFilesOption);
            if (listOfFilesFile != null) {
                ProcessListOfFiles(listOfFilesFile, argsParser);
                return;
            }
            string fileName = argsParser.GetValueOfOptionWithAfterString(FileOption);
            string ext = Path.GetExtension(fileName);
            if (ext != null) {
                ext = ext.ToLower();
                if (ext == ".dot") {
                    ProcessDotFile(gviewer, argsParser, fileName);
                }
                else {
                    if (ext == ".geom") {
                        GeometryGraph geometryGraph = GeometryGraphReader.CreateFromFile(fileName);
                        geometryGraph.Margins = 10;

                        FixHookPorts(geometryGraph);
                        // if (argsParser.OptionIsUsed(BundlingOption)) {
                        for (int i = 0; i < 1; i++) {
#if DEBUG
                            /*DisplayGeometryGraph.ShowGraph(geometryGraph);
                                var l = new List<DebugCurve>(); l.AddRange(geometryGraph.Nodes.Select(n=>new DebugCurve(100,1,"black",n.BoundaryCurve)));
                                l.AddRange(geometryGraph.Edges.Select(e=>new DebugCurve(100,1,"black", new LineSegment(e.Source.Center,e.Target.Center))));
                                foreach (var cl in geometryGraph.RootCluster.AllClustersDepthFirst()) {
                                    l.Add(new DebugCurve(100,2,"blue",cl.BoundaryCurve));
                                    foreach (var node in cl.Nodes)
                                        l.Add(new DebugCurve(100, 2, "brown", node.BoundaryCurve));
                                  
                                    foreach (var e in cl.Edges)
                                        l.Add(new DebugCurve(100, 2, "pink", new LineSegment(e.Source.Center, e.Target.Center)));
                                  
                                }

                                DisplayGeometryGraph.ShowDebugCurves(l.ToArray());*/

#endif
                            BundlingSettings bs = GetBundlingSettings(argsParser);

                            double loosePadding;
                            double tightPadding = GetPaddings(argsParser, out loosePadding);
                            if (argsParser.OptionIsUsed(MdsOption)) {
                                var mdsLayoutSettings = new MdsLayoutSettings
                                                        {RemoveOverlaps = true, NodeSeparation = loosePadding*3};
                                var mdsLayout = new MdsGraphLayout(mdsLayoutSettings, geometryGraph);
                                mdsLayout.Run();
                            }
                            else {
                                if (argsParser.OptionIsUsed(FdOption)) {
                                    var settings = new FastIncrementalLayoutSettings {AvoidOverlaps = true};
                                    (new InitialLayout(geometryGraph, settings)).Run();
                                }
                            }
                            var splineRouter = new SplineRouter(geometryGraph, geometryGraph.Edges, tightPadding,
                                                                loosePadding,
                                                                Math.PI/6, bs);
                            splineRouter.Run();
                        }
#if DEBUG
                        DisplayGeometryGraph.ShowGraph(geometryGraph);
#endif
                        return;
                    }
                    else {
                        if (ext == ".msagl") {
                            Graph graph = Graph.Read(fileName);
                            //           DisplayGeometryGraph.ShowGraph(graph.GeometryGraph);
                            if (graph != null) {
                                if (argsParser.OptionIsUsed(BundlingOption)) {
                                    BundlingSettings bs = GetBundlingSettings(argsParser);

                                    double loosePadding;
                                    double tightPadding = GetPaddings(argsParser, out loosePadding);
                                    var br = new SplineRouter(graph.GeometryGraph, tightPadding, loosePadding, Math.PI/6,
                                                              bs);
                                    br.Run();
                                    //                 DisplayGeometryGraph.ShowGraph(graph.GeometryGraph);
                                }
                            }
                            gviewer.NeedToCalculateLayout = false;
                            gviewer.Graph = graph;
                            gviewer.NeedToCalculateLayout = true;
                        }
                    }
                }
            }
            else if (argsParser.OptionIsUsed(TestCdtOption)) {
                Triangulation(argsParser.OptionIsUsed(ReverseXOption));
                Environment.Exit(0);
            }
            else if (argsParser.OptionIsUsed(TestCdtOption0)) {
                TestTriangulationOnSmallGraph(argsParser);
                Environment.Exit(0);
            }
            else if (argsParser.OptionIsUsed(TestCdtOption2)) {
                TestTriangulationOnPolys();
                Environment.Exit(0);
            }
            else if (argsParser.OptionIsUsed(TestCdtOption1)) {
                ThreadThroughCdt();
                Environment.Exit(0);
            }
            else if (argsParser.OptionIsUsed(ConstraintsTestOption))
                TestGraphWithConstraints();
            
            Application.Run(form);
            
        }
Esempio n. 9
0
        static void LoadGeomFile(string s, EdgeRoutingSettings edgeRoutingSettings, bool show) {
            LayoutAlgorithmSettings settings;
            GeometryGraph geomGraph = GeometryGraphReader.CreateFromFile(s, out settings);
            if (geomGraph == null) {
                //Opens file "data.xml" and deserializes the object from it.
                var stream = File.Open(s, FileMode.Open);
                var formatter = new BinaryFormatter();
                geomGraph = (GeometryGraph) formatter.Deserialize(stream);
                geomGraph.RootCluster = new Cluster();
                stream.Close();
            }
            geomGraph.UpdateBoundingBox();
            if (FormStuff.initialLayout) {
                var l = new List<Cluster>();
                l.Add(geomGraph.RootCluster);
                var il = new InitialLayoutByCluster(geomGraph, new FastIncrementalLayoutSettings());
                il.Run();
            }
            else if (mds) {
                var mdsSettings = new MdsLayoutSettings
                { IterationsWithMajorization = 21,ScaleX = 1, ScaleY=1, RemoveOverlaps = true
                };
                var mdslayout = new MdsGraphLayout(mdsSettings, geomGraph);
                mdslayout.Run();
                var router = new SplineRouter(geomGraph, 1, 20, Math.PI / 6, edgeRoutingSettings.BundlingSettings);
                router.Run();
            }
            else {
                var sugiyamaSettings = (SugiyamaLayoutSettings) settings;
                if (edgeRoutingSettings.EdgeRoutingMode == EdgeRoutingMode.Rectilinear ||
                    edgeRoutingSettings.EdgeRoutingMode == EdgeRoutingMode.RectilinearToCenter) {
                    RouteRectEdgesOfGeomGraph(edgeRoutingSettings.EdgeRoutingMode, true, 
                                            edgeRoutingSettings.UseObstacleRectangles, edgeRoutingSettings.BendPenalty, geomGraph, sugiyamaSettings);
                } else {
                    const double angle = 30*Math.PI/180;
                    var router = new SplineRouter(geomGraph, 1, 20, angle, edgeRoutingSettings.BundlingSettings);
                    router.Run();

                    TestPadding(geomGraph);
                }
            }

#if DEBUG
            if (show) {
                geomGraph.UpdateBoundingBox();
                var b = geomGraph.BoundingBox;
                b.Pad(40);
                geomGraph.BoundingBox = b;
                DisplayGeometryGraph.ShowGraph(geomGraph);
            }
#endif
        }
        public void TreeGraphMdsLayout()
        {
            GeometryGraph treeGraph = GraphGenerator.GenerateTree(20);
            treeGraph.RootCluster = new Cluster(treeGraph.Nodes);
            var settings = new MdsLayoutSettings { ScaleX = 1, ScaleY = 1, RemoveOverlaps = true, PackingAspectRatio = 1.4 };
            settings.EdgeRoutingSettings = new EdgeRoutingSettings { EdgeRoutingMode = EdgeRoutingMode.Spline, ConeAngle = Math.PI / 6, Padding = settings.NodeSeparation / 2.1 };
            foreach (var v in treeGraph.Nodes)
            {
                v.BoundingBox = new Rectangle(0, 0, new Point(30, 30));
            }
            var layout = new InitialLayoutByCluster(treeGraph, settings);

            double progress = 0.0;

            EventHandler<ProgressChangedEventArgs> handler = (s, e) => progress = e.RatioComplete;

            try
            {
                layout.ProgressChanged += handler;
                layout.Run();
            }
            finally
            {
                layout.ProgressChanged -= handler;
            }

            EnableDebugViewer();
            ShowGraphInDebugViewer(treeGraph);

            const double EdgeLengthDelta = 0.5;

            foreach (var e in treeGraph.Edges)
            {
                Assert.IsNotNull(e.Curve, "Edge curves not populated");
                if (e.Source != e.Target)
                {
                    double actualLength = (e.Source.Center - e.Target.Center).Length;
                    double actualDesiredRatio = e.Length / actualLength;
                    Assert.AreEqual(1, actualDesiredRatio, EdgeLengthDelta, "Edge length is not correct");
                }
            }

            double aspectRatio = treeGraph.BoundingBox.Width / treeGraph.BoundingBox.Height;
            Assert.AreEqual(settings.PackingAspectRatio, aspectRatio, 1.4, "Aspect ratio too far from desired");

            Assert.AreEqual(1.0, progress, "Progress was never reported as 100%.  Last update was at " + progress + "%");
        }
        /// <summary>
        /// reads the layout algorithm settings
        /// </summary>
        LayoutAlgorithmSettings ReadLayoutAlgorithmSettings(XmlReader reader) {
            LayoutAlgorithmSettings layoutSettings = null;
            CheckToken(GeometryToken.LayoutAlgorithmSettings);
            if (reader.IsEmptyElement) {
                reader.Read();
                return null;
            }
            //reader.Read();

            var edgeRoutingMode =
                (EdgeRoutingMode) GetIntAttributeOrDefault(GeometryToken.EdgeRoutingMode, (int) EdgeRoutingMode.Spline);
            var str = GetAttribute(GeometryToken.LayoutAlgorithmType);
            if (XmlReader.NodeType == XmlNodeType.EndElement) {
                //todo - support fastincremental settings
                layoutSettings = new FastIncrementalLayoutSettings();
                EdgeRoutingSettings routingSettings = layoutSettings.EdgeRoutingSettings;
                routingSettings.EdgeRoutingMode = edgeRoutingMode;
            }
            else {
                if (str != null) {
                    var token =
                        (GeometryToken) Enum.Parse(typeof (GeometryToken), str, false);
                    if (token == GeometryToken.SugiyamaLayoutSettings) {
                        layoutSettings = ReadSugiyamaLayoutSettings(edgeRoutingMode);
                    }
                    else if (token == GeometryToken.MdsLayoutSettings) {
                        var mds = new MdsLayoutSettings();
                        EdgeRoutingSettings routingSettings = mds.EdgeRoutingSettings;
                        routingSettings.EdgeRoutingMode = edgeRoutingMode;

                        layoutSettings = mds;
                        if (XmlReader.IsStartElement(GeometryToken.Reporting.ToString())) {
#if REPORTING
                            mds.Reporting =
#endif
                                ReadBooleanElement(GeometryToken.Reporting);
                        }
                        mds.Exponent = ReadDoubleElement(reader);
                        mds.IterationsWithMajorization = ReadIntElement(GeometryToken.IterationsWithMajorization);
                        mds.PivotNumber = ReadIntElement(GeometryToken.PivotNumber);
                        mds.RotationAngle = ReadDoubleElement(reader);
                        mds.ScaleX = ReadDoubleElement(reader);
                        mds.ScaleY = ReadDoubleElement(reader);
                    }
                    else //todo - write a reader and a writer for FastIncrementalLayoutSettings 
                        throw new NotImplementedException();
                }
            }
            reader.ReadEndElement();

            return layoutSettings;
        }
        static LayoutAlgorithmSettings PickLayoutAlgorithmSettings(int edges, int nodes) {
            LayoutAlgorithmSettings settings;
            const int sugiaymaTreshold = 200;
            const double bundlingTreshold = 3.0;
            bool bundling = nodes != 0 && ((double)edges / nodes >= bundlingTreshold || edges>100);
            if (nodes < sugiaymaTreshold && edges<sugiaymaTreshold) {
                settings = new SugiyamaLayoutSettings();

                if (bundling)
                    settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.SplineBundling;
            } else {
                MdsLayoutSettings mdsSettings;
                settings = mdsSettings = new MdsLayoutSettings {
                    EdgeRoutingSettings = {
                        EdgeRoutingMode
                            =
                            bundling
                                ? EdgeRoutingMode.SplineBundling
                                : EdgeRoutingMode.Spline
                    }                   
                };
                if (bundling)
                    settings.EdgeRoutingSettings.BundlingSettings = new BundlingSettings();
                double scale = FigureOutScaleForMdsLayout(nodes);
                mdsSettings.ScaleX = scale;
                mdsSettings.ScaleY = scale;
            }
            return settings;
        }
        /// <summary>
        /// Executes the algorithm.
        /// </summary>
        /// <summary>
        /// Executes the actual algorithm.
        /// </summary>
        protected override void RunInternal()
        {
            var g = new GeometryGraph();
            foreach (var v in graph.Nodes)
            {
                Debug.Assert(!(v is Cluster));
                var u = new Node(v.BoundaryCurve.Clone())
                {
                    UserData = v
                };
                v.AlgorithmData = new PivotMDSNodeWrap(u);
                g.Nodes.Add(u);
            }
            double avgLength = 0;
            foreach (var e in graph.Edges)
            {
                avgLength += e.Length;
                if (e.Source is Cluster || e.Target is Cluster) continue;
                var u = e.Source.AlgorithmData as PivotMDSNodeWrap;
                var v = e.Target.AlgorithmData as PivotMDSNodeWrap;
                var ee = new Edge(u.node, v.node)
                {
                    Length = e.Length
                };
                g.Edges.Add(ee);
            }
            if (graph.Edges.Count != 0)
            {
                avgLength /= graph.Edges.Count;
            }
            else
            {
                avgLength = 100;
            }

            // create edges from the children of each parent cluster to the parent cluster node
            foreach (var c in graph.RootCluster.AllClustersDepthFirst())
            {
                if (c == graph.RootCluster) continue;

                var u = new Node(CurveFactory.CreateRectangle(10, 10, new Point()));
                u.UserData = c;
                c.AlgorithmData = new PivotMDSNodeWrap(u);
                g.Nodes.Add(u);
                    
                foreach (var v in c.Nodes.Concat(from cc in c.Clusters select (Node)cc))
                {
                    var vv = v.AlgorithmData as PivotMDSNodeWrap;
                    g.Edges.Add(new Edge(u, vv.node)
                    {
                        Length = avgLength
                    });
                }
            }

            // create edges between clusters
            foreach (var e in graph.Edges)
            {
                if (e.Source is Cluster || e.Target is Cluster)
                {
                    var u = e.Source.AlgorithmData as PivotMDSNodeWrap;
                    var v = e.Target.AlgorithmData as PivotMDSNodeWrap;
                    var ee = new Edge(u.node, v.node)
                    {
                        Length = e.Length
                    };
                    g.Edges.Add(ee);
                }
            }

            // with 0 majorization iterations we just do PivotMDS
            MdsLayoutSettings settings = new MdsLayoutSettings
            {
                ScaleX = this.Scale,
                ScaleY = this.Scale,
                IterationsWithMajorization = 0,
                RemoveOverlaps = false,
                AdjustScale = false
            };

            MdsGraphLayout mdsLayout = new MdsGraphLayout(settings, g);
            this.RunChildAlgorithm(mdsLayout, 1.0);

            g.UpdateBoundingBox();
            foreach (var v in graph.Nodes)
            {
                var m = v.AlgorithmData as PivotMDSNodeWrap;
                v.Center = m.node.Center;
            }
        }
 /// <summary>
 /// Constructs the multidimensional scaling algorithm.
 /// </summary>
 public MdsGraphLayout(MdsLayoutSettings settings, GeometryGraph geometryGraph)
 {
     this.settings = settings;
     graph = geometryGraph;
 }
        /// <summary>
        /// Layouts a connected graph with Multidimensional Scaling, using
        /// shortest-path distances as Euclidean target distances.
        /// </summary>
        /// <param name="geometryGraph">A graph.</param>
        /// <param name="settings">The settings for the algorithm.</param>
        /// <param name="x">Coordinate vector.</param>
        /// <param name="y">Coordinate vector.</param>
        internal static void LayoutGraphWithMds(GeometryGraph geometryGraph, MdsLayoutSettings settings, out double[] x, out double[] y) {            
            x = new double[geometryGraph.Nodes.Count];
            y = new double[geometryGraph.Nodes.Count];
            if (geometryGraph.Nodes.Count == 0) return;
            if (geometryGraph.Nodes.Count == 1)
            {
                x[0] = y[0] = 0;
                return;
            }
            int k = Math.Min(settings.PivotNumber, geometryGraph.Nodes.Count);
            int iter = settings.GetNumberOfIterationsWithMajorization(geometryGraph.Nodes.Count);
            double exponent = settings.Exponent;

            var pivotArray = new int[k];
            PivotDistances pivotDistances = new PivotDistances(geometryGraph, false, pivotArray);
            pivotDistances.Run();
            double[][] c = pivotDistances.Result;
            MultidimensionalScaling.LandmarkClassicalScaling(c, out x, out y, pivotArray);
            ScaleToAverageEdgeLength(geometryGraph, x, y);
            
            if (iter > 0) {
                AllPairsDistances apd = new AllPairsDistances(geometryGraph, false);
                apd.Run();
                double[][] d = apd.Result;
                double[][] w = MultidimensionalScaling.ExponentialWeightMatrix(d, exponent);
                // MultidimensionalScaling.DistanceScaling(d, x, y, w, iter);
                MultidimensionalScaling.DistanceScalingSubset(d, x, y, w, iter);
            }
        }