Example #1
0
        /// <summary>
        /// Apply the appropriate layout to the specified cluster
        /// </summary>
        /// <param name="cluster">the root of the cluster hierarchy to lay out</param>
        /// <returns>list of edges external to the cluster</returns>
        void LayoutCluster(Cluster cluster)
        {
            ProgressStep();
            cluster.UnsetInitialLayoutState();
            FastIncrementalLayoutSettings settings = null;
            LayoutAlgorithmSettings       s        = clusterSettings(cluster);
            Directions layoutDirection             = Directions.None;

            if (s is SugiyamaLayoutSettings)
            {
                var ss = s as SugiyamaLayoutSettings;
                settings = ss.FallbackLayoutSettings != null
                    ? new FastIncrementalLayoutSettings((FastIncrementalLayoutSettings)ss.FallbackLayoutSettings)
                    : new FastIncrementalLayoutSettings();
                layoutDirection = LayeredLayoutEngine.GetLayoutDirection(ss);
            }
            else
            {
                settings = new FastIncrementalLayoutSettings((FastIncrementalLayoutSettings)s);
            }

            settings.ApplyForces          = true;
            settings.MinorIterations      = 10;
            settings.AvoidOverlaps        = true;
            settings.InterComponentForces = false;
            settings.IdealEdgeLength      = new IdealEdgeLengthSettings {
                EdgeDirectionConstraints  = layoutDirection,
                ConstrainedEdgeSeparation = 30
            };
            settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Spline;

            HashSet <Node> addedNodes;

            if (addedNodesByCluster.TryGetValue(cluster, out addedNodes))
            {
                // if the structure of the cluster has changed then we apply unconstrained layout first,
                // then introduce structural constraints, and then all constraints
                settings.MinConstraintLevel = 0;
                settings.MaxConstraintLevel = 2;
            }
            else
            {
                settings.MinConstraintLevel = 2;
            }

            GeometryGraph newGraph = GetShallowCopyGraphUnderCluster(cluster);

            LayoutAlgorithmHelpers.ComputeDesiredEdgeLengths(settings.IdealEdgeLength, newGraph);

            // orthogonal ordering constraints preserve the left-of, above-of relationships between existing nodes
            // (we do not apply these to the newly added nodes)
            GenerateOrthogonalOrderingConstraints(
                newGraph.Nodes.Where(v => !addedNodes.Contains(v.UserData as Node)).ToList(), settings);

            LayoutComponent(newGraph, settings);
            //LayoutAlgorithmSettings.ShowGraph(newGraph);
            InitialLayoutByCluster.FixOriginalGraph(newGraph, true);

            cluster.UpdateBoundary(newGraph.BoundingBox);
        }
Example #2
0
        internal void LayoutComponent(GeometryGraph component, FastIncrementalLayoutSettings settings)
        {
            // for small graphs (below 100 nodes) do extra iterations
            settings.MaxIterations = LayoutAlgorithmHelpers.NegativeLinearInterpolation(
                component.Nodes.Count,
                /*lowerThreshold:*/ 50, /*upperThreshold:*/ 500, /*minIterations:*/ 3, /*maxIterations:*/ 5);
            settings.MinorIterations = LayoutAlgorithmHelpers.NegativeLinearInterpolation(component.Nodes.Count,
                                                                                          /*lowerThreshold:*/ 50, /*upperThreshold:*/ 500, /*minIterations:*/ 2, /*maxIterations:*/ 10);

            FastIncrementalLayout fil = new FastIncrementalLayout(component, settings, settings.MinConstraintLevel,
                                                                  anyCluster => settings);

            Debug.Assert(settings.Iterations == 0);

            foreach (var level in Enumerable.Range(settings.MinConstraintLevel, settings.MaxConstraintLevel + 1))
            {
                if (level != fil.CurrentConstraintLevel)
                {
                    fil.CurrentConstraintLevel = level;
                    if (level == 2)
                    {
                        settings.MinorIterations = 1;
                        settings.ApplyForces     = false;
                    }
                }
                do
                {
                    fil.Run();
                } while (!settings.IsDone);
            }

            // Pad the graph with margins so the packing will be spaced out.
            component.Margins = settings.ClusterMargin;
            component.UpdateBoundingBox();
        }
Example #3
0
 public void CalculateIterationsForGraphSize()
 {
     const int LowerThreshold = 10;
     const int UpperThreshold = 50;
     const int MinIterations = 10;
     const int MaxIterations = 30;
     Assert.AreEqual(
         LayoutAlgorithmHelpers.NegativeLinearInterpolation(UpperThreshold + 1, LowerThreshold, UpperThreshold, MinIterations, MaxIterations), 
         MinIterations,
         "When nodeCount > upperThreshold number of iterations should be minIterations");
     Assert.AreEqual(
         LayoutAlgorithmHelpers.NegativeLinearInterpolation(UpperThreshold, LowerThreshold, UpperThreshold, MinIterations, MaxIterations),
         MinIterations,
         "When nodeCount == upperThreshold number of iterations should be minIterations");
     Assert.AreEqual(
         LayoutAlgorithmHelpers.NegativeLinearInterpolation(LowerThreshold - 1, LowerThreshold, UpperThreshold, MinIterations, MaxIterations),
         MaxIterations,
         "When nodeCount < lowerThreshold number of iterations should be maxIterations");
     Assert.AreEqual(
         LayoutAlgorithmHelpers.NegativeLinearInterpolation(LowerThreshold, LowerThreshold, UpperThreshold, MinIterations, MaxIterations),
         MaxIterations,
         "When nodeCount == lowerThreshold number of iterations should be maxIterations");
     Assert.AreEqual(
         LayoutAlgorithmHelpers.NegativeLinearInterpolation((UpperThreshold + LowerThreshold) / 2, LowerThreshold, UpperThreshold, MinIterations, MaxIterations),
         (MinIterations + MaxIterations) / 2,
         "When nodeCount is mid-way between lowerThreshold and upperThreshold number of iterations should be mid-way between minIterations and maxIterations");
 }
 void MDSLayout(MdsLayoutSettings settings, GeometryGraph component) {
     LayoutAlgorithmHelpers.ComputeDesiredEdgeLengths(settings.EdgeConstraints, 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);
 }
 void ForceDirectedLayout(FastIncrementalLayoutSettings settings, GeometryGraph component) {
     LayoutAlgorithmHelpers.ComputeDesiredEdgeLengths(settings.IdealEdgeLength, component);
     var layout = new InitialLayout(component, settings) { SingleComponent = true };
     layout.Run(this.CancelToken);
     InitialLayoutHelpers.RouteEdges(component, settings, this.CancelToken);
     InitialLayoutHelpers.PlaceLabels(component, this.CancelToken);
     InitialLayoutHelpers.FixBoundingBox(component, settings);
 }
Example #6
0
        private void LayoutComponent(GeometryGraph component)
        {
            if (component.Nodes.Count > 1 || component.RootCluster.Clusters.Any())
            {
                // for small graphs (below 100 nodes) do extra iterations
                settings.MaxIterations = LayoutAlgorithmHelpers.NegativeLinearInterpolation(
                    component.Nodes.Count,
                    /*lowerThreshold:*/ 50, /*upperThreshold:*/ 500, /*minIterations:*/ 5, /*maxIterations:*/ 10);
                settings.MinorIterations = LayoutAlgorithmHelpers.NegativeLinearInterpolation(component.Nodes.Count,
                                                                                              /*lowerThreshold:*/ 50, /*upperThreshold:*/ 500, /*minIterations:*/ 3, /*maxIterations:*/ 20);

                if (settings.MinConstraintLevel == 0)
                {
                    // run PivotMDS with a largish Scale so that the layout comes back oversized.
                    // subsequent incremental iterations do a better job of untangling when they're pulling it in
                    // rather than pushing it apart.
                    PivotMDS pivotMDS = new PivotMDS(component)
                    {
                        Scale = 2
                    };
                    this.RunChildAlgorithm(pivotMDS, 0.5 / componentCount);
                }
                FastIncrementalLayout fil = new FastIncrementalLayout(component, settings, settings.MinConstraintLevel, anyCluster => settings);
                Debug.Assert(settings.Iterations == 0);

                foreach (var level in GetConstraintLevels(component))
                {
                    if (level > settings.MaxConstraintLevel)
                    {
                        break;
                    }

                    if (level > settings.MinConstraintLevel)
                    {
                        fil.CurrentConstraintLevel = level;
                    }

                    do
                    {
                        fil.Run();
                    } while (!settings.IsDone);
                }
            }

            // Pad the graph with margins so the packing will be spaced out.
            component.Margins = settings.NodeSeparation;
            component.UpdateBoundingBox();

            // Zero the graph
            component.Translate(-component.BoundingBox.LeftBottom);
        }
        /// <summary>
        /// Create an abstraction of the graph by pairing edges with the shortest
        /// symmetric difference of neighbour sets.  In a perfect world the result
        /// would have half as many nodes as the input graph.  In practice we only
        /// process the edge list once, and don't pair edges whose ends are already
        /// paired so the output may have more than n/2 nodes.
        /// </summary>
        /// <param name="graph">The input graph</param>
        /// <param name="edgeLengthOffset">Initial edge length adjustment percent before proportional adjustments.</param>
        /// <param name="edgeLengthMultiplier">The percent length adjustment unit.</param>
        /// <returns>an abridged graph</returns>
        private GeometryGraph CreateAbridgedGraph(GeometryGraph graph, double edgeLengthOffset, double edgeLengthMultiplier)
        {
            LayoutAlgorithmHelpers.SetEdgeLengthsProportionalToSymmetricDifference(graph, edgeLengthOffset, edgeLengthMultiplier);
            var g2    = new GeometryGraph();
            var nodes = new Set <Node>(graph.Nodes);
            var rand  = new Random(1);
            // edges sorted by increasing weight, edges with the same weight shuffled
            var edges = (from e in graph.Edges
                         let r = rand.Next()
                                 orderby e.Length, r
                         select e).ToList();
            // mapping from nodes in graph to nodes in g2
            // each node in g2 may represent either 1 or 2 nodes from graph
            var nodeMap = new Dictionary <Node, Node>();

            // populate g2 with nodes representing pairs of nodes, paired across the shortest edges first
            foreach (var e in edges)
            {
                if (nodes.Contains(e.Source) && nodes.Contains(e.Target))
                {
                    var pairNode = new Node
                    {
                        UserData = e
                    };
                    nodeMap[e.Source] = pairNode;
                    nodeMap[e.Target] = pairNode;
                    g2.Nodes.Add(pairNode);
                    nodes.Remove(e.Source);
                    nodes.Remove(e.Target);
                }
            }
            // populate g2 with remaining singleton nodes from graph
            foreach (var u in nodes)
            {
                var v = new Node
                {
                    UserData = u
                };
                g2.Nodes.Add(v);
                nodeMap[u] = v;
            }
            // populate g2 with edges - no duplicates, reverse duplicates or self edges allowed
            var neighbours = new Set <KeyValuePair <Node, Node> >();

            foreach (var e in edges)
            {
                Node u = nodeMap[e.Source], v = nodeMap[e.Target];
                var  pair = new KeyValuePair <Node, Node>(u, v);
                var  raip = new KeyValuePair <Node, Node>(v, u);
                if (pair.Key != pair.Value &&
                    !neighbours.Contains(pair) &&
                    !neighbours.Contains(raip))
                {
                    var e2 = new Edge(u, v)
                    {
                        Length = e.Length
                    };
                    neighbours.Insert(pair);
                    g2.Edges.Add(e2);
                }
            }
            return(g2);
        }