/// <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); } }