/// <summary> /// Deep-copy the SolverParameters. /// </summary> /// <returns></returns> public object Clone() { OverlapRemovalParameters newParams = (OverlapRemovalParameters)this.MemberwiseClone(); newParams.SolverParameters = (Parameters)this.SolverParameters.Clone(); return(newParams); }
/// <summary> /// Generates and solves the constraints. /// </summary> /// <param name="solver">The solver to generate into and solve. May be null, in which case one /// is created by the method.</param> /// <param name="parameters">Parameters to OverlapRemoval and ProjectionSolver.Solver.Solve().</param> /// <param name="doGenerate">Generate constraints before solving; if false, solver is assumed to /// have already been populated by this.Generate().</param> /// <returns>The set of OverlapRemoval.Constraints that were unsatisfiable, or NULL.</returns> public ProjectionSolver.Solution Solve(ProjectionSolver.Solver solver, OverlapRemovalParameters parameters, bool doGenerate) { if (null == solver) { solver = new ProjectionSolver.Solver(); } if (null == parameters) { parameters = new OverlapRemovalParameters(); } if (doGenerate) { Generate(solver, parameters); } ProjectionSolver.Solution solverSolution = solver.Solve(parameters.SolverParameters); foreach (OverlapRemovalCluster cluster in this.clusterHierarchies) { cluster.UpdateFromVariable(); // "recursively" processes all child clusters } return(solverSolution); }
/// <summary> /// Generate the necessary constraints to ensure there is no overlap (unless we're doing /// a horizontal pass and deferring some movement, which would be smaller, to the vertical pass). /// </summary> /// <param name="solver">The solver to generate into.</param> /// <param name="parameters">Parameters to OverlapRemoval and ProjectionSolver.Solver.Solve().</param> public void Generate(ProjectionSolver.Solver solver, OverlapRemovalParameters parameters) { ValidateArg.IsNotNull(solver, "solver"); if (null == parameters) { parameters = new OverlapRemovalParameters(); } foreach (OverlapRemovalCluster cluster in this.clusterHierarchies) { cluster.Generate(solver, parameters, this.IsHorizontal); } // For Clusters we reposition their "fake border" variables between the constraint-generation // and solving phases, so we need to tell the solver to do this. solver.UpdateVariables(); // @@PERF: Not needed if no clusters were created. }
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); } }
} // end GetLeftNeighbours List<OverlapRemovalNode> GetRightNeighbours(OverlapRemovalParameters parameters, ScanLine scanLine, OverlapRemovalNode currentNode, bool isHorizontal) { var lstNeighbours = new List<OverlapRemovalNode>(); OverlapRemovalNode nextNode = scanLine.NextRight(currentNode); for (; null != nextNode; nextNode = scanLine.NextRight(nextNode)) { // AddNeighbor returns false if we are done adding them. if (!AddNeighbour(parameters, currentNode, nextNode, lstNeighbours, false /* isLeftNeighbor */ , isHorizontal)) { if (!nextNode.DeferredRightNeighborToV) { break; } } } // endfor NextLeft return lstNeighbours; } // end GetRightNeighbours
} // end GetRightNeighbours bool AddNeighbour(OverlapRemovalParameters parameters, OverlapRemovalNode currentNode, OverlapRemovalNode nextNode, List<OverlapRemovalNode> neighbors, bool isLeftNeighbor, bool isHorizontal) { // Sanity check to be sure that the borders are past all other nodes. Debug.Assert(currentNode != (isLeftNeighbor ? this.LeftBorderNode : this.RightBorderNode), "currentNode must != BorderNode"); double overlap = Overlap(currentNode, nextNode, NodePadding); if (overlap <= 0) { // This is the first node encountered on this neighbour-traversal that did not // overlap within the required padding. Add it to the list and we're done with // this traversal, unless this is a vertical pass and it is not an overlap on // the horizontal axis; in that case, pretend we never saw it and return true // so the next non-overlapping node will be found. (See below for more information // on why this is necessary). if (!isHorizontal && (OverlapP(currentNode, nextNode, NodePaddingP) <= parameters.SolverParameters.GapTolerance)) { #if VERBOSE Console.WriteLine(" V {0}nbourHTolSkipNO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; } neighbors.Add(nextNode); #if VERBOSE Console.WriteLine("{0}nbourAddNO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return false; } if (isHorizontal) { if (parameters.AllowDeferToVertical) { // We are doing horizontal constraints so examine the vertical overlap and see which // is the smallest (normalized by total node size in that orientation) such that the // least amount of movement required. this.padding is currently the same in both // directions; if this changes, we'll have to add different padding values here for // each direction. @@DCR: consider adding weights to the defer-to-vertical calculation; // this would allow two nodes to pop up/down if they're being squeezed, rather than // force apart the borders (which happens regardless of their weight). double overlapP = OverlapP(currentNode, nextNode, NodePaddingP); bool isOverlapping = parameters.ConsiderProportionalOverlap ? overlap / (currentNode.Size + nextNode.Size) > overlapP / (currentNode.SizeP + nextNode.SizeP) : overlap > overlapP; if (isOverlapping) { // Don't skip if either of these is a border node. if ((currentNode != this.LeftBorderNode) && (currentNode != this.RightBorderNode) && (nextNode != this.LeftBorderNode) && (nextNode != this.RightBorderNode)) { // Moving in the horizontal direction requires more movement than in the vertical // direction to remove the overlap, so skip this node on horizontal constraint // generation and we'll pick it up on vertical constraint generation. Return true // to keep looking for more overlapping nodes. // Note: it is still possible that we'll pick up a constraint in both directions, // due to either or both of this.padding and the "create a constraint to the first // non-overlapping node" logic. This is expected and the latter helps retain stability. #if VERBOSE Console.WriteLine("{0}nbourDeferToV: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE // We need to track whether we skipped these so that we don't have a broken transition chain. // See Test_OverlapRemoval.cs, Test_DeferToV_Causing_Missing_Cst() for more information. if (isLeftNeighbor) { currentNode.DeferredLeftNeighborToV = true; nextNode.DeferredRightNeighborToV = true; } else { currentNode.DeferredRightNeighborToV = true; nextNode.DeferredLeftNeighborToV = true; } return true; } } // endif Overlap is greater than OverlapP } // endif AllowDeferToVertical } else { // We're on the vertical pass so make sure we match up with the Solver's tolerance in the // scanline direction, because it is possible that there was a horizontal constraint between // these nodes that was within the Solver's tolerance and thus was not enforced. In that // case, we could spuriously add a vertical constraint here that would result in undesired // and possibly huge vertical movement. There is a corresponding Assert during constraint // generation when the node is Closed. We have to do this here rather than at runtime because // doing it then may skip a Neighbour that replaced other Neighbors by transitivity. if (OverlapP(currentNode, nextNode, NodePaddingP) <= parameters.SolverParameters.GapTolerance) { #if VERBOSE Console.WriteLine(" V {0}nbourHTolSkipO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; } } // Add this overlapping neighbour and return true to keep looking for more overlapping neighbours. neighbors.Add(nextNode); #if VERBOSE Console.WriteLine("{0}nbourAddO: {1}", isLeftNeighbor ? "L" : "R", nextNode); #endif // VERBOSE return true; }
void GenerateFromEvents(Solver solver, OverlapRemovalParameters parameters, List<Event> events, bool isHorizontal) { // First, sort the events on the perpendicular coordinate of the event // (e.g. for horizontal constraint generation, order on vertical position). events.Sort(); #if VERBOSE Console.WriteLine("Events:"); foreach (Event evt in events) { Console.WriteLine(" {0}", evt); } #endif // VERBOSE var scanLine = new ScanLine(); foreach (Event evt in events) { OverlapRemovalNode currentNode = evt.Node; if (evt.IsForOpen) { // Insert the current node into the scan line. scanLine.Insert(currentNode); #if VERBOSE Console.WriteLine("ScanAdd: {0}", currentNode); #endif // VERBOSE // Find the nodes that are currently open to either side of it and are either overlapping // nodes or the first non-overlapping node in that direction. currentNode.LeftNeighbors = GetLeftNeighbours(parameters, scanLine, currentNode, isHorizontal); currentNode.RightNeighbors = GetRightNeighbours(parameters, scanLine, currentNode, isHorizontal); // Use counts for indexing for performance (rather than foreach, and hoist the count-control // variable out of the loop so .Count isn't checked on each iteration, since we know it's // not going to be changed). int numLeftNeighbors = currentNode.LeftNeighbors.Count; int numRightNeighbors = currentNode.RightNeighbors.Count; // If there is currently a non-overlap constraint between any two nodes across the // two neighbour lists we've just created, we can remove them because they will be // transitively enforced by the constraints we'll create for the current node. // I.e., we can remove the specification for the constraint // leftNeighborNode + gap + padding <= rightNeighborNode // because it is implied by the constraints we'll create for // leftNeighborNode + gap + padding <= node // node + gap + padding <= rightNeighborNode // We must also add the current node as a neighbour in the appropriate direction. // @@PERF: List<T>.Remove is a sequential search so averages 1/2 the size of the // lists. We currently don't expect the neighbour lists to be very large or Remove // to be a frequent operation, and using HashSets would incur the GetEnumerator overhead // on the outer and inner loops; but .Remove creates an inner-inner loop so do some // timing runs to compare performance. // @@PERF: handles the case where we are node c and have added node b as a lnbour // and node d as rnbour, where those nodes are already nbours. But it does not handle // the case where we add node b and node a as lnbours, and node b already has node a // as an lnbour. To do this I think we'd just want to skip adding the node-a lnbour, // but that forms a new inner loop (iterating all lnbours before adding a new one) // unless we develop different storage for nbours. for (int ii = 0; ii < numLeftNeighbors; ++ii) { OverlapRemovalNode leftNeighborNode = currentNode.LeftNeighbors[ii]; for (int jj = 0; jj < numRightNeighbors; ++jj) { // TODOunit: test this OverlapRemovalNode nodeToRemove = currentNode.RightNeighbors[jj]; if (leftNeighborNode.RightNeighbors.Remove(nodeToRemove)) { #if VERBOSE Console.WriteLine(" {0} RnbourRem {1} --> {2}", isHorizontal ? "H" : "V", leftNeighborNode, nodeToRemove); #endif // VERBOSE } } leftNeighborNode.RightNeighbors.Add(currentNode); } for (int ii = 0; ii < numRightNeighbors; ++ii) { // TODOunit: test this OverlapRemovalNode rightNeighborNode = currentNode.RightNeighbors[ii]; for (int jj = 0; jj < numLeftNeighbors; ++jj) { OverlapRemovalNode nodeToRemove = currentNode.LeftNeighbors[jj]; if (rightNeighborNode.LeftNeighbors.Remove(nodeToRemove)) { #if VERBOSE Console.WriteLine(" {0} LnbourRem {1} --> {2}", isHorizontal ? "H" : "V", nodeToRemove, rightNeighborNode); #endif // VERBOSE } } rightNeighborNode.LeftNeighbors.Add(currentNode); } } // endif evt.IsForOpen else { // This is a close event, so generate the constraints and remove the closing node // from its neighbours lists. If we're closing we should have left neighbours so // this is null then we've likely got some sort of internal calculation error. if (null == currentNode.LeftNeighbors) { Debug.Assert(null != currentNode.LeftNeighbors, "LeftNeighbors should not be null for a Close event"); continue; } // currentNode is the current node; if it's a cluster, translate it to the node that // should be involved in the constraint (if it's the left neighbour then use its // right border as the constraint variable, and vice-versa). OverlapRemovalNode currentLeftNode = GetLeftConstraintNode(currentNode); OverlapRemovalNode currentRightNode = GetRightConstraintNode(currentNode); // LeftNeighbors must end before the current node... int cLeftNeighbours = currentNode.LeftNeighbors.Count; for (int ii = 0; ii < cLeftNeighbours; ++ii) { // Keep track of the original Node; it may be the base of a Cluster, in which // case it will have the active neighbours list, not leftNeighborNode (which will // be the left border "fake Node"). OverlapRemovalNode origLeftNeighborNode = currentNode.LeftNeighbors[ii]; origLeftNeighborNode.RightNeighbors.Remove(currentNode); OverlapRemovalNode leftNeighborNode = GetLeftConstraintNode(origLeftNeighborNode); Debug.Assert(leftNeighborNode.OpenP == origLeftNeighborNode.OpenP, "leftNeighborNode.OpenP must == origLeftNeighborNode.OpenP"); // This assert verifies we match the Solver.ViolationTolerance check in AddNeighbor. // We are closing the node here so use an alternative to OverlapP for additional // consistency verification. Allow a little rounding error. Debug.Assert(isHorizontal || ((currentNode.CloseP + NodePaddingP - leftNeighborNode.OpenP) > (parameters.SolverParameters.GapTolerance - 1e-6)), "LeftNeighbors: unexpected close/open overlap"); double p = leftNeighborNode == LeftBorderNode || currentRightNode == RightBorderNode ? ClusterPadding : NodePadding; double separation = ((leftNeighborNode.Size + currentRightNode.Size) / 2) + p; if (TranslateChildren) { separation = Math.Max(separation, currentRightNode.Position - leftNeighborNode.Position); } Constraint cst = solver.AddConstraint(leftNeighborNode.Variable, currentRightNode.Variable, separation); Debug.Assert(null != cst, "LeftNeighbors: unexpected null cst"); #if VERBOSE Console.WriteLine(" {0} LnbourCst {1} -> {2} g {3:F5}", isHorizontal ? "H" : "V" , cst.Left.Name, cst.Right.Name, cst.Gap); #endif // VERBOSE } // ... and RightNeighbors must start after the current node. int cRightNeighbours = currentNode.RightNeighbors.Count; for (int ii = 0; ii < cRightNeighbours; ++ii) { // Keep original node, which may be a cluster; see comments in LeftNeighbors above. OverlapRemovalNode origRightNeighborNode = currentNode.RightNeighbors[ii]; origRightNeighborNode.LeftNeighbors.Remove(currentNode); OverlapRemovalNode rightNeighborNode = GetRightConstraintNode(origRightNeighborNode); // This assert verifies we match the Solver.ViolationTolerance check in AddNeighbor. // Allow a little rounding error. Debug.Assert(isHorizontal || ((currentNode.CloseP + NodePaddingP - rightNeighborNode.OpenP) > (parameters.SolverParameters.GapTolerance - 1e-6)), "RightNeighbors: unexpected close/open overlap"); double p = currentLeftNode == LeftBorderNode || rightNeighborNode == RightBorderNode ? ClusterPadding : NodePadding; double separation = ((currentLeftNode.Size + rightNeighborNode.Size) / 2) + p; if (TranslateChildren) { separation = Math.Max(separation, rightNeighborNode.Position - currentLeftNode.Position); } Constraint cst = solver.AddConstraint(currentLeftNode.Variable, rightNeighborNode.Variable, separation); Debug.Assert(null != cst, "RightNeighbors: unexpected null cst"); #if VERBOSE Console.WriteLine(" {0} RnbourCst {1} -> {2} g {3:F5}", isHorizontal ? "H" : "V" , cst.Left.Name, cst.Right.Name, cst.Gap); #endif // VERBOSE } // Note: although currentNode is closed, there may still be open nodes in its // Neighbour lists; these will subsequently be processed (and removed from // currentNode.*Neighbour) when those Neighbors are closed. scanLine.Remove(currentNode); #if VERBOSE Console.WriteLine("ScanRem: {0}", currentNode); #endif // VERBOSE } // endelse !evt.IsForOpen // @@PERF: Set Node.Left/RightNeighbors null to let the GC know we're not using them // anymore, unless we can reasonably assume a short lifetime for the ConstraintGenerator. } // endforeach Event }
// Returns false if the cluster is empty; this handles nested clusters of empty clusters. // TODOunit: several of the test files cover this but add a specific test for it. bool GenerateWorker(Solver solver, OverlapRemovalParameters parameters, bool isHorizontal) { // @@DCR "Precalculate Cluster Sizes": if we are solving per-cluster to calculate best sizes before // generating constraints, then solver would be passed in as null and we'd create one here. // Variables to calculate our boundaries. Top and Bottom refer to the perpendicular direction; // for vertical, read Top <-> Left and Bottom <-> Right. var boundaryRect = new Rectangle { //Left = // this.OpenBorderInfo.IsFixedPosition && this.TranslateChildren // ? this.OpenBorderInfo.FixedPosition // : double.MaxValue, //Right = // this.CloseBorderInfo.IsFixedPosition && this.TranslateChildren // ? this.CloseBorderInfo.FixedPosition // : double.MinValue, //Bottom = // this.OpenBorderInfoP.IsFixedPosition && this.TranslateChildren // ? this.OpenBorderInfoP.FixedPosition // : double.MaxValue, //Top = // this.CloseBorderInfoP.IsFixedPosition && this.TranslateChildren // ? this.CloseBorderInfoP.FixedPosition // : double.MinValue Left = double.MaxValue, Right = double.MinValue, Bottom = double.MaxValue, Top = double.MinValue }; if (IsEmpty) { // Nothing to generate. return false; } // The list of open/close events, which will be sorted on the perpendicular coordinate of the event // (e.g. for horizontal constraint generation, order on vertical position). var events = this.CreateEvents(solver, ref boundaryRect); // If we added no events, we're either Fixed (so continue) or empty (so return). if (0 == events.Count && !TranslateChildren) { return false; } // Top/Bottom are considered the secondary (Perpendicular) axis here. double leftBorderWidth = DefaultBorderWidth; double rightBorderWidth = DefaultBorderWidth; if (!this.IsRootCluster) { CalculateBorderWidths(solver, events, boundaryRect, out leftBorderWidth, out rightBorderWidth); #if VERBOSE Console.WriteLine(" {0} After CalculateBorderWidths: p {1:F5} s {2:F5} pP {3:F5} sP {4:F5}" , this.Name, this.Size, this.Position, this.Size, this.SizeP); #endif } GenerateFromEvents(solver, parameters, events, isHorizontal); if (!this.IsRootCluster) { // Non-fixed borders are moved later by SqueezeNonFixedBorderPositions(). this.AdjustFixedBorderPositions(solver, leftBorderWidth, rightBorderWidth, isHorizontal); } return true; }
internal void Generate(Solver solver, OverlapRemovalParameters parameters, bool isHorizontal) { ProcessClusterHierarchy(this, cluster => cluster.IsInSolver = cluster.GenerateWorker(solver, parameters, isHorizontal)); ProcessClusterHierarchy(this, cluster => cluster.SqueezeNonFixedBorderPositions()); }
/// <summary> /// Generates and solves the constraints. /// </summary> /// <param name="solver">The solver to generate into and solve. May be null, in which case one /// is created by the method.</param> /// <param name="parameters">Parameters to OverlapRemoval and ProjectionSolver.Solver.Solve().</param> /// <param name="doGenerate">Generate constraints before solving; if false, solver is assumed to /// have already been populated by this.Generate().</param> /// <returns>The set of OverlapRemoval.Constraints that were unsatisfiable, or NULL.</returns> public ProjectionSolver.Solution Solve(ProjectionSolver.Solver solver, OverlapRemovalParameters parameters, bool doGenerate) { if (null == solver) { solver = new ProjectionSolver.Solver(); } if (null == parameters) { parameters = new OverlapRemovalParameters(); } if (doGenerate) { Generate(solver, parameters); } ProjectionSolver.Solution solverSolution = solver.Solve(parameters.SolverParameters); foreach (OverlapRemovalCluster cluster in this.clusterHierarchies) { cluster.UpdateFromVariable(); // "recursively" processes all child clusters } return solverSolution; }