/// <summary>
 /// Creates a new cluster with no minimum size within the specified parent cluster.  Clusters allow creating a subset of
 /// nodes that must be within a distinct rectangle.
 /// </summary>
 /// <param name="parentCluster">The cluster this cluster is to be a member of; if null, this is the root of a
 ///                             new hierarchy, otherwise must be non-NULL (perhaps DefaultClusterHierarchy).</param>
 /// <param name="userData">An object that is passed through.</param>
 /// <param name="openBorderInfo">Information about the Left (if isHorizontal, else Top) border.</param>
 /// <param name="closeBorderInfo">Information about the Right (if isHorizontal, else Bottom) border.</param>
 /// <param name="openBorderInfoP">Same as OpenBorder, but in the secondary (Perpendicular) axis.</param>
 /// <param name="closeBorderInfoP">Same as CloseBorder, but in the secondary (Perpendicular) axis.</param>
 /// <returns>The new Cluster.</returns>
 ///
 public OverlapRemovalCluster AddCluster(OverlapRemovalCluster parentCluster, object userData,
                                         BorderInfo openBorderInfo, BorderInfo closeBorderInfo,
                                         BorderInfo openBorderInfoP, BorderInfo closeBorderInfoP)
 {
     return(AddCluster(parentCluster, userData, 0.0 /*minSize*/, 0.0 /*minSizeP*/,
                       openBorderInfo, closeBorderInfo, openBorderInfoP, closeBorderInfoP));
 }
 /// <summary>
 /// Add a Cluster with default border information and specified minimum sizes.
 /// </summary>
 /// <param name="parentCluster">The cluster this cluster is to be a member of; if null, this is the root of a
 ///                             new hierarchy, otherwise must be non-NULL (perhaps DefaultClusterHierarchy).</param>
 /// <param name="userData">An object that is passed through.</param>
 /// <param name="minimumSize">Minimum cluster size along the primary axis.</param>
 /// <param name="minimumSizeP">Minimum cluster size along the perpendicular axis.</param>
 /// <returns>The new Cluster.</returns>
 public OverlapRemovalCluster AddCluster(OverlapRemovalCluster parentCluster, object userData,
                                         double minimumSize, double minimumSizeP)
 {
     return(AddCluster(parentCluster, userData, minimumSize, minimumSizeP,
                       new BorderInfo(0.0), new BorderInfo(0.0),
                       new BorderInfo(0.0), new BorderInfo(0.0))); // 0.0 margins
 }
        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);
            }
        }
Exemple #4
0
        private void VerifyClustersAreWithinParentClusterBounds(IEnumerable <ClusterDef> iterClusterDefs, double epsilon, ref bool succeeded)
        {
            foreach (ClusterDef clusDef in iterClusterDefs)
            {
                // Empty clusters have nothing to verify
                // Clusters at the root of a hierarchy have no borders.
                if (clusDef.IsEmpty)
                {
                    continue;
                }
                if (null != clusDef.ParentClusterDef)
                {
                    // Clusters at the root of a hierarchy have no borders.
                    if (clusDef.ParentClusterDef.IsNewHierarchy)
                    {
                        continue;
                    }

                    // Is varCheck's left or right border out of bounds?  Negative overlap means yes.
                    // We're testing for nested variables here so include cluster margin at the relevant border.
                    double leftOverlap = clusDef.Left - clusDef.ParentClusterDef.Left
                                         - OverlapRemovalCluster.CalcBorderWidth(clusDef.ParentClusterDef.LeftBorderInfo.InnerMargin)
                                         - this.MinPaddingX;
                    double rightOverlap = clusDef.ParentClusterDef.Right - clusDef.Right
                                          - OverlapRemovalCluster.CalcBorderWidth(clusDef.ParentClusterDef.RightBorderInfo.InnerMargin)
                                          - this.MinPaddingX;
                    double topOverlap = clusDef.Top - clusDef.ParentClusterDef.Top
                                        - OverlapRemovalCluster.CalcBorderWidth(clusDef.ParentClusterDef.TopBorderInfo.InnerMargin)
                                        - this.MinPaddingY;
                    double bottomOverlap = clusDef.ParentClusterDef.Bottom - clusDef.Bottom
                                           - OverlapRemovalCluster.CalcBorderWidth(clusDef.ParentClusterDef.BottomBorderInfo.InnerMargin)
                                           - this.MinPaddingY;
                    if ((leftOverlap < -epsilon) ||
                        (rightOverlap < -epsilon) ||
                        (topOverlap < -epsilon) ||
                        (bottomOverlap < -epsilon))
                    {
                        // Uh oh.
                        this.WriteLine("Error {0}: Cluster '{1}' is outside ParentCluster '{2}' bounds", FailTag("ClusParentClus"),
                                       clusDef.ClusterId, clusDef.ParentClusterDef.ClusterId);
                        this.WriteLine("   Cluster {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusDef.ClusterId,
                                       clusDef.Left, clusDef.Right, clusDef.Top, clusDef.Bottom);
                        this.WriteLine("   Parent  {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusDef.ParentClusterDef.ClusterId,
                                       clusDef.ParentClusterDef.Left, clusDef.ParentClusterDef.Right,
                                       clusDef.ParentClusterDef.Top, clusDef.ParentClusterDef.Bottom);
                        this.WriteLine("   Overlap  : L/R T/B {0:F5}/{1:F5} {2:F5}/{3:F5}",
                                       leftOverlap, rightOverlap, topOverlap, bottomOverlap);
                        succeeded = false;
                    }
                } // endif null != (object)varCheck.ParentClusterDef
            }
        }
        /// <summary>
        /// Add a new variable to the ConstraintGenerator.
        /// </summary>
        /// <param name="initialCluster">The cluster this node is to be a member of.  It may not be null; pass
        ///                     DefaultClusterHierarchy to create a node at the lowest level.  Subsequently a node
        ///                     may be added to additional clusters, but only to one cluster per hierarchy.</param>
        /// <param name="userData">An object that is passed through.</param>
        /// <param name="position">Position of the node in the primary axis; if isHorizontal, it contains horizontal
        ///                     position and size, else it contains vertical position and size.</param>
        /// <param name="size">Size of the node in the primary axis.</param>
        /// <param name="positionP">Position of the node in the secondary (Perpendicular) axis.</param>
        /// <param name="sizeP">Size of the node in the secondary (Perpendicular) axis.</param>
        /// <param name="weight">Weight of the node (indicates how freely it should move).</param>
        /// <returns>The created node.</returns>
        public OverlapRemovalNode AddNode(OverlapRemovalCluster initialCluster, object userData, double position,
                                          double positionP, double size, double sizeP, double weight)
        {
            ValidateArg.IsNotNull(initialCluster, "initialCluster");
            // @@PERF: Currently every node will have at least one constraint generated if there are any
            // other nodes along its line, regardless of whether the perpendicular coordinates result in overlap.
            // It might be worthwhile to add a check to avoid constraint generation in the case that there cannot
            // be such an overlap on a line, or if the nodes are separated by some amount of distance.
            Debug.Assert(null != initialCluster, "initialCluster must not be null");
            var nodNew = new OverlapRemovalNode(this.nextNodeId++, userData, position, positionP, size, sizeP, weight);

            initialCluster.AddNode(nodNew);
            return(nodNew);
        }
        public void AddNodeToCluster(OverlapRemovalCluster cluster, OverlapRemovalNode node)
        {
            // Node derives from Cluster so make sure we don't have this - the only way to create
            // cluster hierarchies is by AddCluster.
            ValidateArg.IsNotNull(cluster, "cluster");
            if (node is OverlapRemovalCluster)
            {
                throw new InvalidOperationException(
#if DEBUG
                          "Argument 'node' must not be a Cluster"
#endif // DEBUG
                          );
            }
            cluster.AddNode(node);
        }
Exemple #7
0
 internal OverlapRemovalCluster CreateCluster(ConstraintGenerator conGen)
 {
     if (null == this.Cluster)
     {
         // Ensure the parent Cluster is created as we must pass it as a parameter.
         // Don't call this.ParentCluster here because it's got an Assert that our
         // cluster has been created - and it hasn't, yet; that's what we're doing here.
         // clusParent remains null if this.IsNewHierarchy.
         OverlapRemovalCluster clusParent = null;
         if (!this.IsNewHierarchy)
         {
             clusParent = (null == this.ParentClusterDef)
                              ? conGen.DefaultClusterHierarchy
                              : this.ParentClusterDef.CreateCluster(conGen);
         }
         if (conGen.IsHorizontal)
         {
             this.Cluster = conGen.AddCluster(
                 clusParent,
                 this.ClusterId,
                 this.MinimumSizeX,
                 this.MinimumSizeY,
                 this.LeftBorderInfo,
                 this.RightBorderInfo,
                 this.TopBorderInfo,
                 this.BottomBorderInfo);
         }
         else
         {
             // Use horizontal PostX BorderInfos due to MinimumSize; see PostX().
             this.Cluster = conGen.AddCluster(
                 clusParent,
                 this.ClusterId,
                 this.MinimumSizeY,
                 this.MinimumSizeX,
                 this.TopBorderInfo,
                 this.BottomBorderInfo,
                 this.PostXLeftBorderInfo,
                 this.PostXRightBorderInfo);
         }
     }
     return(this.Cluster);
 }
        /// <summary>
        /// Creates a new cluster with a minimum size within the specified parent cluster.  Clusters allow creating a subset of
        /// nodes that must be within a distinct rectangle.
        /// </summary>
        /// <param name="parentCluster">The cluster this cluster is to be a member of; if null, this is the root of a
        ///                             new hierarchy, otherwise must be non-NULL (perhaps DefaultClusterHierarchy).</param>
        /// <param name="userData">An object that is passed through.</param>
        /// <param name="minimumSize">Minimum cluster size along the primary axis.</param>
        /// <param name="minimumSizeP">Minimum cluster size along the perpendicular axis.</param>
        /// <param name="openBorderInfo">Information about the Left (if isHorizontal, else Top) border.</param>
        /// <param name="closeBorderInfo">Information about the Right (if isHorizontal, else Bottom) border.</param>
        /// <param name="openBorderInfoP">Same as OpenBorder, but in the secondary (Perpendicular) axis.</param>
        /// <param name="closeBorderInfoP">Same as CloseBorder, but in the secondary (Perpendicular) axis.</param>
        /// <returns>The new Cluster.</returns>
        ///
        public OverlapRemovalCluster AddCluster(OverlapRemovalCluster parentCluster, object userData,
                                                double minimumSize, double minimumSizeP,
                                                BorderInfo openBorderInfo, BorderInfo closeBorderInfo,
                                                BorderInfo openBorderInfoP, BorderInfo closeBorderInfoP)
        {
            var newCluster = new OverlapRemovalCluster(this.nextNodeId, parentCluster, userData, minimumSize, minimumSizeP,
                                                       this.Padding, this.PaddingP, this.ClusterPadding, this.ClusterPaddingP,
                                                       openBorderInfo, closeBorderInfo, openBorderInfoP, closeBorderInfoP);

            this.nextNodeId += OverlapRemovalCluster.NumInternalNodes;
            if (null == parentCluster)
            {
                this.clusterHierarchies.Add(newCluster);
            }
            else
            {
                // @@DCR: Enforce that Clusters live in only one hierarchy - they can have only one parent, so add a
                //          Cluster.parentCluster to enforce this.
                parentCluster.AddNode(newCluster);
            }
            return(newCluster);
        }
 /// <summary>
 /// Add a Cluster with default border information and no minimum sizes.
 /// </summary>
 /// <param name="parentCluster">The cluster this cluster is to be a member of; if null, this is the root of a
 ///                             new hierarchy, otherwise must be non-NULL (perhaps DefaultClusterHierarchy).</param>
 /// <param name="userData">An object that is passed through.</param>
 /// <returns>The new Cluster.</returns>
 public OverlapRemovalCluster AddCluster(OverlapRemovalCluster parentCluster, object userData)
 {
     return(AddCluster(parentCluster, userData,
                       new BorderInfo(0.0), new BorderInfo(0.0),
                       new BorderInfo(0.0), new BorderInfo(0.0))); // 0.0 margins
 }
        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);
            }
        }
Exemple #11
0
        private void VerifyClustersAreTight(IEnumerable <ClusterDef> iterClusterDefs, double epsilon, ref bool succeeded)
        {
            // Verify that clusters have at least one node or cluster immediately adjacent to
            // the cluster borders (i.e. verify the clusters are tight).
            foreach (ClusterDef clusDef in iterClusterDefs)
            {
                // Empty clusters have nothing to verify
                if (clusDef.IsEmpty)
                {
                    continue;
                }

                // Clusters at the root of a hierarchy have no borders.
                if (clusDef.IsNewHierarchy)
                {
                    continue;
                }

                double minLeft   = double.MaxValue;
                double maxRight  = double.MinValue;
                double minTop    = double.MaxValue;
                double maxBottom = double.MinValue;

                foreach (VariableDef varChild in clusDef.Variables)
                {
                    minLeft   = Math.Min(minLeft, varChild.Left);
                    maxRight  = Math.Max(maxRight, varChild.Right);
                    minTop    = Math.Min(minTop, varChild.Top);
                    maxBottom = Math.Max(maxBottom, varChild.Bottom);
                }

                foreach (ClusterDef clusChild in clusDef.Clusters)
                {
                    if (clusChild.IsEmpty)
                    {
                        continue;
                    }
                    minLeft   = Math.Min(minLeft, clusChild.Left);
                    maxRight  = Math.Max(maxRight, clusChild.Right);
                    minTop    = Math.Min(minTop, clusChild.Top);
                    maxBottom = Math.Max(maxBottom, clusChild.Bottom);
                }

                // Are the cluster's borders tight?  Too big a positive gap means yes.
                // We're testing for children here so include cluster margin at the relevant border.
                double leftGap = minLeft - clusDef.Left
                                 - OverlapRemovalCluster.CalcBorderWidth(clusDef.LeftBorderInfo.InnerMargin)
                                 - this.MinPaddingX;
                double rightGap = clusDef.Right - maxRight
                                  - OverlapRemovalCluster.CalcBorderWidth(clusDef.RightBorderInfo.InnerMargin)
                                  - this.MinPaddingX;
                double topGap = minTop - clusDef.Top
                                - OverlapRemovalCluster.CalcBorderWidth(clusDef.TopBorderInfo.InnerMargin)
                                - this.MinPaddingY;
                double bottomGap = clusDef.Bottom - maxBottom
                                   - OverlapRemovalCluster.CalcBorderWidth(clusDef.BottomBorderInfo.InnerMargin)
                                   - this.MinPaddingY;

                // This is OK if the cluster is at its min size; assume it had to grow to meet the min size.
                bool badXgap = !clusDef.IsMinimumSizeX &&
                               (((leftGap > epsilon) && !clusDef.LeftBorderInfo.IsFixedPosition) ||
                                ((rightGap > epsilon) && !clusDef.RightBorderInfo.IsFixedPosition));
                bool badYgap = !clusDef.IsMinimumSizeY &&
                               (((topGap > epsilon) && !clusDef.TopBorderInfo.IsFixedPosition) ||
                                ((bottomGap > epsilon) && !clusDef.BottomBorderInfo.IsFixedPosition));
                if (badXgap || badYgap)
                {
                    // Uh oh.
                    this.WriteLine("Error {0}: Cluster '{1}' border is not tight (within {2})", FailTag("ClusTightBorder"),
                                   clusDef.ClusterId, epsilon);
                    this.WriteLine("   Cluster {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusDef.ClusterId,
                                   clusDef.Left, clusDef.Right, clusDef.Top, clusDef.Bottom);
                    this.WriteLine("   Gaps     : L/R T/B {0}/{1} {2}/{3}",
                                   clusDef.LeftBorderInfo.IsFixedPosition ? "fixed" : string.Format("{0:F5}", leftGap),
                                   clusDef.RightBorderInfo.IsFixedPosition ? "fixed" : string.Format("{0:F5}", rightGap),
                                   clusDef.TopBorderInfo.IsFixedPosition ? "fixed" : string.Format("{0:F5}", topGap),
                                   clusDef.BottomBorderInfo.IsFixedPosition ? "fixed" : string.Format("{0:F5}", bottomGap));
                    succeeded = false;
                }
            }
        }
Exemple #12
0
        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;
            }
        }