/// <summary>
        /// Create variables, generate non-overlap constraints.
        /// </summary>
        /// <param name="hPad">horizontal node padding</param>
        /// <param name="vPad">vertical node padding</param>
        /// <param name="cHPad">horizontal cluster padding</param>
        /// <param name="cVPad">vertical cluster padding</param>
        /// <param name="nodeCenter"></param>
        internal void Initialize(double hPad, double vPad, double cHPad, double cVPad, InitialCenterDelegateType nodeCenter) {
            // For the Vertical ConstraintGenerator, Padding is vPad and PadddingP(erpendicular) is hPad.
            cg = new ConstraintGenerator(IsHorizontal
                                            , IsHorizontal ? hPad : vPad
                                            , IsHorizontal ? vPad : hPad
                                            , IsHorizontal ? cHPad : cVPad
                                            , IsHorizontal ? cVPad : cHPad);
            solver = new Solver();

            foreach (var filNode in nodes) {
                filNode.SetOlapNode(IsHorizontal,null);
            }
            // Calculate horizontal non-Overlap constraints.  
            if (avoidOverlaps && clusterHierarchies != null) {
                foreach (var c in clusterHierarchies) {
                    AddOlapClusters(cg, null /* OlapParentCluster */, c, nodeCenter);
                }
            }

            foreach (var filNode in nodes) {
                if (filNode.getOlapNode(IsHorizontal) == null) {
                    AddOlapNode(cg, cg.DefaultClusterHierarchy /* olapParentCluster */, filNode, nodeCenter);
                }
                filNode.getOlapNode(IsHorizontal).CreateVariable(solver);
            }
            if (avoidOverlaps && this.ConstraintLevel >= 2) {
                cg.Generate(solver, OverlapRemovalParameters);
            }
            AddStructuralConstraints();
        }
        private void DebugVerifyClusters(ConstraintGenerator generator, Cluster incCluster, Cluster root) {
            double dblEpsilon = 0.0001;

            // First verify that all nodes are within the cluster.
            Rectangle clusRect = incCluster.RectangularBoundary.rectangle;
            foreach (var v in incCluster.Nodes) {
                FiNode iiFilNode = (FiNode)v.AlgorithmData;
                Rectangle iiNodeRect = iiFilNode.mNode.BoundaryCurve.BoundingBox;

                if (IsHorizontal) {
                    // Don't check containment for the root ClusterHierarchy as there is no border for it.
                    if (incCluster != root) {
                        // This is horizontal so we've not yet calculated the Y-axis stuff.  The only thing we
                        // can do is verify we're within cluster X bounds.  If *Space is negative, there's overlap.
                        // Generator primary axis is horizontal so use its Padding.
                        double dblLboundSpace = iiNodeRect.Left - clusRect.Left - generator.Padding;
                        double dblRboundSpace = clusRect.Right - iiNodeRect.Right - generator.Padding;
                        Debug.Assert((dblLboundSpace >= -dblEpsilon) && (dblRboundSpace >= -dblEpsilon)
                                    , "Node is not within parent Cluster");
                    }
                } else {
                    // Don't check containment for the root ClusterHierarchy as there is no border for it.
                    if (incCluster != root) {
                        // This is vertical so we've calculated the Y-axis stuff and horizontal is Perpendicular.
                        DebugVerifyRectContains(clusRect, iiNodeRect
                                                , generator.PaddingP, generator.Padding, dblEpsilon);
                    }
                    // Make sure the node doesn't intersect any following nodes, or any clusters.
                    foreach (var u in incCluster.Nodes) {
                        if (u == v) continue;
                        FiNode jjFilNode = (FiNode)u.AlgorithmData;
                        Rectangle jjNodeRect = jjFilNode.mNode.BoundaryCurve.BoundingBox;

                        // We've already added the padding for the node so don't add it for the jjNode/Cluster.
                        DebugVerifyRectsDisjoint(iiNodeRect, jjNodeRect
                                                , generator.PaddingP, generator.Padding, dblEpsilon);
                    }
                    foreach (Cluster incClusComp in incCluster.Clusters)
                    {
                        DebugVerifyRectsDisjoint(iiNodeRect, incClusComp.RectangularBoundary.rectangle
                                            , generator.PaddingP, generator.Padding, dblEpsilon);
                    }
                } // endif isHorizontal
            } // endfor iiNode

            // Now verify the clusters are contained and don't overlap.
            foreach (var iiIncClus in incCluster.Clusters) {
                Rectangle iiClusRect = iiIncClus.RectangularBoundary.rectangle;

                if (IsHorizontal) {
                    // Don't check containment for the root ClusterHierarchy as there is no border for it.
                    if (incCluster != root) {
                        // This is horizontal so we've not yet calculated the Y-axis stuff.  The only thing we
                        // can do is verify we're within cluster X bounds.  If *Space is negative, there's overlap.
                        // Generator primary axis is horizontal so use its Padding.
                        double dblLboundSpace = iiClusRect.Left - clusRect.Left - generator.Padding;
                        double dblRboundSpace = clusRect.Right - iiClusRect.Right - generator.Padding;
                        Debug.Assert((dblLboundSpace >= -dblEpsilon) && (dblRboundSpace >= -dblEpsilon)
                                    , "Cluster is not within parent Cluster");
                    }
                } else {
                    // Don't check containment for the root ClusterHierarchy as there is no border for it.
                    if (incCluster != root) {
                        // This is vertical so we've calculated the Y-axis stuff and horizontal is Perpendicular.
                        DebugVerifyRectContains(clusRect, iiClusRect
                                                , generator.PaddingP, generator.Padding, dblEpsilon);
                    }
                    // Make sure the cluster doesn't intersect any following clusters.
                    foreach (var jjIncClus in incCluster.Clusters) {
                        if (jjIncClus == iiIncClus) continue;
                        Rectangle jjClusRect = jjIncClus.RectangularBoundary.rectangle;
                        DebugVerifyRectsDisjoint(iiClusRect, jjClusRect
                                                , generator.PaddingP, generator.Padding, dblEpsilon);
                    }
                } // endif isHorizontal

                // Now recurse.
                DebugVerifyClusters(generator, iiIncClus, root);
            } // endfor iiCluster
        }
        private void AddOlapNode(ConstraintGenerator generator, OverlapRemovalCluster olapParentCluster, FiNode filNode, InitialCenterDelegateType nodeCenter) {
            // If the node already has an mOlapNode, it's already in a cluster (in a different
            // hierarchy); we just add it to the new cluster.
            if (null != filNode.getOlapNode(IsHorizontal)) {
                generator.AddNodeToCluster(olapParentCluster, filNode.getOlapNode(IsHorizontal));
                return;
            }

            var center = nodeCenter(filNode);
            // We need to create a new Node in the Generator.
            if (IsHorizontal) {
                // Add the Generator node with the X-axis coords primary, Y-axis secondary.
                filNode.mOlapNodeX = generator.AddNode(olapParentCluster, filNode /* userData */
                                    , center.X, center.Y
                                    , filNode.Width, filNode.Height, filNode.stayWeight);
            } else {
                // Add the Generator node with the Y-axis coords primary, X-axis secondary.
                filNode.mOlapNodeY = generator.AddNode(olapParentCluster, filNode /* userData */
                                    , center.Y, center.X
                                    , filNode.Height, filNode.Width, filNode.stayWeight);
            }
        }
        private void AddOlapClusters(ConstraintGenerator generator, OverlapRemovalCluster olapParentCluster, Cluster incClus, InitialCenterDelegateType nodeCenter)
        {
            LayoutAlgorithmSettings settings = clusterSettings(incClus);
            double nodeSeparationH = settings.NodeSeparation;
            double nodeSeparationV = settings.NodeSeparation + 1e-4;
            double innerPaddingH = settings.ClusterMargin;
            double innerPaddingV = settings.ClusterMargin + 1e-4;

            // Creates the OverlapRemoval (Olap) Cluster/Node objects for our FastIncrementalLayout (FIL) objects.
            // If !isHorizontal this overwrites the Olap members of the Incremental.Clusters and Msagl.Nodes.

            // First create the olapCluster for the current incCluster.  If olapParentCluster is null, then
            // incCluster is the root of a new hierarchy.
            RectangularClusterBoundary rb = incClus.RectangularBoundary;
            if (IsHorizontal)
            {
                rb.olapCluster = generator.AddCluster(
                    olapParentCluster,
                    incClus /* userData */,
                    rb.MinWidth,
                    rb.MinHeight,
                    rb.LeftBorderInfo,
                    rb.RightBorderInfo,
                    rb.BottomBorderInfo,
                    rb.TopBorderInfo);
                rb.olapCluster.NodePadding = nodeSeparationH;
                rb.olapCluster.NodePaddingP = nodeSeparationV;
                rb.olapCluster.ClusterPadding = innerPaddingH;
                rb.olapCluster.ClusterPaddingP = innerPaddingV;
            }
            else
            {
                var postXLeftBorderInfo = new BorderInfo(rb.LeftBorderInfo.InnerMargin, rb.Rect.Left, rb.LeftBorderInfo.Weight);
                var postXRightBorderInfo = new BorderInfo(rb.RightBorderInfo.InnerMargin, rb.Rect.Right, rb.RightBorderInfo.Weight);
                rb.olapCluster = generator.AddCluster(
                    olapParentCluster,
                    incClus /* userData */,
                    rb.MinHeight,
                    rb.MinWidth,
                    rb.BottomBorderInfo,
                    rb.TopBorderInfo,
                    postXLeftBorderInfo,
                    postXRightBorderInfo);
                rb.olapCluster.NodePadding = nodeSeparationV;
                rb.olapCluster.NodePaddingP = nodeSeparationH;
                rb.olapCluster.ClusterPadding = innerPaddingV;
                rb.olapCluster.ClusterPaddingP = innerPaddingH;
            }
            rb.olapCluster.TranslateChildren = rb.GenerateFixedConstraints;
            // Note: Incremental.Cluster always creates child List<Cluster|Node> so we don't have to check for null here.
            // Add our child nodes.
            foreach (var filNode in incClus.Nodes)
            {
                AddOlapNode(generator, rb.olapCluster, (FiNode)filNode.AlgorithmData, nodeCenter);
            }

            // Now recurse through all child clusters.
            foreach (var incChildClus in incClus.Clusters)
            {
                AddOlapClusters(generator, rb.olapCluster, incChildClus, nodeCenter);
            }
        }
        private void VerifyAxisResults(uint rep, ConstraintGenerator generator, OverlapRemovalParameters olapParameters, ref bool succeeded)
        {
            var axisName = generator.IsHorizontal ? "X" : "Y";
            var solver = generator.IsHorizontal ? this.SolverX : this.SolverY;
            var solution = generator.IsHorizontal ? this.SolutionX : this.SolutionY;
            if (TestGlobals.VerboseLevel >= 3)
            {
                this.WriteLine("  {0} Nodes after solving Constraints:", axisName);
                foreach (Variable var in solver.Variables)
                {
                    var node = (OverlapRemovalNode)var.UserData;
                    Console.Write("    {0}", node);

                    const string Format = " - L/R T/B {0:F5}/{1:F5} {2:F5}/{3:F5}";
                    if (generator.IsHorizontal)
                    {
                        // X is Perpendicular here
                        this.WriteLine(Format, node.OpenP, node.CloseP, node.Open, node.Close);
                    }
                    else
                    {
                        // Y is Perpendicular here
                        this.WriteLine(Format, node.Open, node.Close, node.OpenP, node.CloseP);
                    }
                }
            }

            // We should never see unsatisfiable constraints since we created them ourselves.
            if (0 != solution.NumberOfUnsatisfiableConstraints)
            {
                succeeded = false;
                this.WriteLine(" *** Error! {0} unsatisfiable {1} constraints found ***",
                        solution.NumberOfUnsatisfiableConstraints, axisName);
                if ((TestGlobals.VerboseLevel >= 2) && (0 == rep))
                {
                    foreach (Constraint cst in solver.Constraints.Where(cst => cst.IsUnsatisfiable))
                    {
                        this.WriteLine("      {0}", cst);
                    }
                } // endif VerboseLevel
            } // endif unsatisfiable constraints

            bool violationSeen = false;
            foreach (Constraint cst in solver.Constraints)
            {
                if (!this.VerifyConstraint(olapParameters.SolverParameters, cst, generator.IsHorizontal, ref violationSeen))
                {
                    succeeded = false;
                }
            }

            if (solution.ExecutionLimitExceeded)
            {
                this.WriteLine(GetCutoffString(solution));
            }
        }
        private void AddNodesAndSolve(ConstraintGenerator generator,
                        IEnumerable<VariableDef> iterVariableDefs,
                        OverlapRemovalParameters olapParameters,
                        IEnumerable<ConstraintDef> iterConstraintDefs,
                        ref bool succeeded)
        {
            var axisName = generator.IsHorizontal ? "X" : "Y";
            var solver = generator.IsHorizontal ? this.SolverX : this.SolverY;
            foreach (VariableDef varDef in iterVariableDefs)
            {
                // Create the variable in its initial cluster.
                OverlapRemovalCluster olapClusParent = generator.DefaultClusterHierarchy;
                if (varDef.ParentClusters.Count > 0)
                {
                    olapClusParent = varDef.ParentClusters[0].Cluster;
                }
                OverlapRemovalNode newNode;
                if (generator.IsHorizontal)
                {
                    newNode = generator.AddNode(olapClusParent, varDef.IdString,
                                           varDef.DesiredPosX, varDef.DesiredPosY,
                                           varDef.SizeX, varDef.SizeY, varDef.WeightX);
                    varDef.VariableX = new OlapTestNode(newNode);
                }
                else 
                {
                    newNode = generator.AddNode(olapClusParent, varDef.IdString,
                                           varDef.DesiredPosY, varDef.VariableX.ActualPos,
                                           varDef.SizeY, varDef.SizeX, varDef.WeightY);
                    varDef.VariableY = new OlapTestNode(newNode);
                }

                // Add it to its other clusters.
                for (int ii = 1; ii < varDef.ParentClusters.Count; ++ii)
                {
                    olapClusParent = varDef.ParentClusters[ii].Cluster;
                    generator.AddNodeToCluster(olapClusParent, newNode);
                }
            }
            generator.Generate(solver, olapParameters);
            if (TestGlobals.VerboseLevel >= 3)
            {
                this.WriteLine("  {0} Constraints:", axisName);
                foreach (Constraint cst in solver.Constraints.OrderBy(cst => cst))
                {
                    this.WriteLine("    {0} {1} g {2:F5}", cst.Left.UserData, cst.Right.UserData, cst.Gap);
                }
            }

            if (null != iterConstraintDefs)
            {
                // TODO: Compare expected to actual constraints generated in solver
            }

            var solution = generator.Solve(solver, olapParameters, false /* doGenerate */);
            if (!this.VerifySolutionMembers(solution, /*iterNeighborDefs:*/ null))
            {
                succeeded = false;
            }
            if (generator.IsHorizontal)
            {
                this.SolutionX = solution;
            }
            else
            {
                this.SolutionY = solution;
            }
        }
        private void CreateSolversAndGetSolutions(Stopwatch sw, IEnumerable<ClusterDef> iterClusterDefs, IEnumerable<VariableDef> iterVariableDefs, IEnumerable<ConstraintDef> iterConstraintDefsX, IEnumerable<ConstraintDef> iterConstraintDefsY, ref bool succeeded)
        {
            var olapParameters = new OverlapRemovalParameters(this.SolverParameters)
                {
                    AllowDeferToVertical = this.AllowDeferToVertical
                };

            for (uint rep = 0; rep < TestGlobals.TestReps; ++rep)
            {
                sw.Start();

                // Load the Horizontal ProjectionSolver and ConstraintGenerator with variables.
                // We must Solve the X coordinates before generating the Y ones, so that the
                // Y generation, whose scan line uses X coordinates, uses the updated values.
                var generatorX = new ConstraintGenerator(true /* fIsHorizontal */, this.MinPaddingX, this.MinPaddingY);
                this.SolverX = new Solver();

                // First create the X Clusters.
                if (null != iterClusterDefs)
                {
                    foreach (ClusterDef clusDef in iterClusterDefs)
                    {
                        clusDef.ComputeInitialBorders();  // called for H only; does both H and V
                        clusDef.CreateCluster(generatorX);
                    }
                }
                this.AddNodesAndSolve(generatorX, iterVariableDefs, olapParameters, iterConstraintDefsX, ref succeeded);

                sw.Stop();

                this.VerifyAxisResults(rep, generatorX, olapParameters, ref succeeded);

                sw.Start();

                // Load the Vertical ProjectionSolver and ConstraintGenerator with variables.
                // This uses the X coordinate determined by the above solution for the perpendicular.
                var generatorY = new ConstraintGenerator(false /* fIsHorizontal */, this.MinPaddingY, this.MinPaddingX);
                this.SolverY = new Solver();

                // First create the Y Clusters.
                if (null != iterClusterDefs)
                {
                    // Clear out the ConGenX Clusters first, then create them in ConGenY.
                    foreach (ClusterDef clusDef in iterClusterDefs)
                    {
                        if (!clusDef.PostX())
                        {
                            succeeded = false;
                        }
                    }
                    foreach (ClusterDef clusDef in iterClusterDefs)
                    {
                        clusDef.CreateCluster(generatorY);
                    }
                }

                this.AddNodesAndSolve(generatorY, iterVariableDefs, olapParameters, iterConstraintDefsY, ref succeeded);

                sw.Stop();

                if (null != iterClusterDefs)
                {
                    foreach (ClusterDef clusDef in iterClusterDefs)
                    {
                        if (!clusDef.PostY())
                        {
                            succeeded = false;
                        }
                    }
                }

                this.VerifyAxisResults(rep, generatorY, olapParameters, ref succeeded);
            }
        }