private ESection ProcessClusterOrConstraints(string currentLine, ESection currentSection, ref EClusterState currentClusterState) { if (currentLine.StartsWith( TestFileStrings.BeginCluster, StringComparison.OrdinalIgnoreCase)) { currentSection = ESection.Cluster; currentClusterState = EClusterState.Id; this.currentClusterDef = new ClusterDef(this.MinClusterSizeX, this.MinClusterSizeY); return(currentSection); } if (currentLine.StartsWith( TestFileStrings.BeginConstraintsX, StringComparison.OrdinalIgnoreCase)) { currentSection = ESection.Constraints; this.currentConstraintDefs = this.ConstraintDefsX; return(currentSection); } if (currentLine.StartsWith( TestFileStrings.BeginConstraintsY, StringComparison.OrdinalIgnoreCase)) { currentSection = ESection.Constraints; this.currentConstraintDefs = this.ConstraintsDefY; return(currentSection); } if (currentLine.StartsWith( TestFileStrings.BeginConstraints, StringComparison.OrdinalIgnoreCase)) { currentSection = ESection.Constraints; this.currentConstraintDefs = this.ConstraintDefsX; return(currentSection); } return(currentSection); }
private void InitializeMembers() { SolverX = null; SolverY = null; SolutionX = null; SolutionY = null; MinPaddingX = InitialMinPaddingX; MinPaddingY = InitialMinPaddingY; AllowDeferToVertical = InitialAllowDeferToVertical; ClusterDef.Reset(); }
internal void AddClusterDef(ClusterDef clusDef) { this.clusterDefs.Add(clusDef); clusDef.ParentClusterDef = this; }
private void VerifyClustersDoNotOverlapWithNonChildNodesInTheirOwnHierarchy(double epsilon, KeyValuePair <List <ClusterDef>, List <VariableDef> > kvpCurHier, ClusterDef[] localClusDefs, ref bool succeeded) { int idxStartVar = 0; foreach (ClusterDef clusCur in localClusDefs) { if (clusCur.IsEmpty) { continue; } VariableDef[] localVarDefs = kvpCurHier.Value.OrderBy(varDef => varDef.Top).ToArray(); for (int jj = idxStartVar; jj < localVarDefs.Length; ++jj) { VariableDef varCheck = localVarDefs[jj]; // Minimize variable-list traversal. if (varCheck.Top < (clusCur.Top - epsilon)) { idxStartVar = jj; } // If the variable ends before the cluster starts, there's no overlap. if ((clusCur.Top - varCheck.Bottom - this.MinPaddingY) > -epsilon) { continue; } // Rounding error may leave these calculations slightly greater or less than zero. // Since margin is calculated only for inner edges and here we are testing for // sibling rather than nested nodes, we don't use margin here. // Name is <relativeToVarCur><RelativeToVarCheck> double bottomTopOverlap = varCheck.Top - clusCur.Bottom - this.MinPaddingY; if (bottomTopOverlap >= -epsilon) { // Out of range of clusCur's size, so we're done with clusCur. break; } // Does varCheck's left or right border overlap? Negative overlap means yes. // Again, margins are only cluster-internal and we're testing external boundaries // here; so the cluster size should have been calculated large enough and we only // look at padding. double xa = varCheck.Left - clusCur.Right - this.MinPaddingX; double xb = clusCur.Left - varCheck.Right - this.MinPaddingX; if ((xa < -epsilon) && (xb < -epsilon)) { // Let's see if it's an ancestor. bool hasSideOverlap = true; foreach (ClusterDef clusDefParent in varCheck.ParentClusters) { for (ClusterDef clusDefAncestor = clusDefParent; null != clusDefAncestor; clusDefAncestor = clusDefAncestor.ParentClusterDef) { if (clusDefAncestor == clusCur) { hasSideOverlap = false; break; } } if (!hasSideOverlap) { break; } } if (hasSideOverlap) { // Uh oh. this.WriteLine("Error {0}: Overlap exists between Cluster '{1}' and non-child Node '{2}'", FailTag("OlapClusNode"), clusCur.ClusterId, varCheck.IdString); this.WriteLine(" Cluster {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusCur.ClusterId, clusCur.Left, clusCur.Right, clusCur.Top, clusCur.Bottom); this.WriteLine(" Node {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", varCheck.IdString, varCheck.Left, varCheck.Right, varCheck.Top, varCheck.Bottom); succeeded = false; } } // endif overlap within epsilon } // endfor each non-child variable } }
private void VerifyClustersDoNotOverlapWithNonParentClustersInTheirOwnHierarchy(double epsilon, out ClusterDef[] localClusDefs, KeyValuePair <List <ClusterDef>, List <VariableDef> > kvpCurHier, ref bool succeeded) { localClusDefs = kvpCurHier.Key.OrderBy(clusDef => clusDef.Top).ToArray(); for (int ii = 0; ii < localClusDefs.Length; ++ii) { ClusterDef clusCur = localClusDefs[ii]; if (clusCur.IsEmpty || clusCur.IsNewHierarchy) { continue; } for (int jj = ii + 1; jj < localClusDefs.Length; ++jj) { ClusterDef clusCheck = localClusDefs[jj]; if (clusCheck.IsEmpty || clusCheck.IsNewHierarchy) { continue; } // Rounding error may leave these calculations slightly greater or less than zero. // Since margin is calculated only for inner edges and here we are testing for // sibling rather than nested nodes, we don't use margin here. // Name is <relativeToVarCur><RelativeToVarCheck> double bottomTopOverlap = clusCheck.Top - clusCur.Bottom - this.MinPaddingY; if (bottomTopOverlap >= -epsilon) { // Out of range of clusCur's size, so we're done with clusCur. break; } // Does clusCheck's left or right border overlap? Negative overlap means yes. // Again, margins are only cluster-internal and we're testing external boundaries // here; so the cluster size should have been calculated large enough and we only // look at padding. double xa = clusCheck.Left - clusCur.Right - this.MinPaddingX; double xb = clusCur.Left - clusCheck.Right - this.MinPaddingX; if ((xa < -epsilon) && (xb < -epsilon)) { // Let's see if it's a parent. bool hasSideOverlap = true; for (ClusterDef clusDefParent = clusCheck.ParentClusterDef; null != clusDefParent; clusDefParent = clusDefParent.ParentClusterDef) { if (clusDefParent == clusCur) { hasSideOverlap = false; break; } } // Note: This test may fail if clusCheck is a parent of clusCur, but in that case // clusCheck should be outside clusCur - which means we had another error before this, // that cluster {clusCheck} is outside the bounds of parent cluster {clusCur}. if (hasSideOverlap) { // Uh oh. this.WriteLine("Error {0}: Overlap exists between sibling Clusters '{1}' and '{2}'", FailTag("OlapSibClus"), clusCur.ClusterId, clusCheck.ClusterId); this.WriteLine(" Cluster {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusCur.ClusterId, clusCur.Left, clusCur.Right, clusCur.Top, clusCur.Bottom); this.WriteLine(" Cluster {0}: L/R T/B {1:F5}/{2:F5} {3:F5}/{4:F5}", clusCheck.ClusterId, clusCheck.Left, clusCheck.Right, clusCheck.Top, clusCheck.Bottom); succeeded = false; } } // endif overlap within epsilon } // endfor localClusDefs[jj] } }
private static void UnflattenEachClusterHierarchy(IEnumerable <ClusterDef> iterClusterDefs, IEnumerable <VariableDef> iterVariableDefs, Dictionary <int, KeyValuePair <List <ClusterDef>, List <VariableDef> > > dictHierarchies) { var kvpCurrentHierarchy = new KeyValuePair <List <ClusterDef>, List <VariableDef> >(new List <ClusterDef>(), new List <VariableDef>()); dictHierarchies[0] = kvpCurrentHierarchy; foreach (VariableDef varDef in iterVariableDefs) { // If no parent, or a child of cluster 0 (which is implicit and not added to a test datafile's // cluster specifications), add it to the DefaultClusterHierarchy. if (0 == varDef.ParentClusters.Count) { kvpCurrentHierarchy.Value.Add(varDef); } else { foreach (ClusterDef clusDef in varDef.ParentClusters) { if (0 == clusDef.ClusterId) { kvpCurrentHierarchy.Value.Add(varDef); } } } } // Now if there are explicit clusters and/or hierarchies, add them and their variables to their hierarchy. if (null != iterClusterDefs) { foreach (ClusterDef clusDef in iterClusterDefs) { Validate.IsTrue(clusDef.ClusterId > 0, "Explicit clusters must have ID > 0"); if (clusDef.IsNewHierarchy) { // Create a new hierarchy. kvpCurrentHierarchy = new KeyValuePair <List <ClusterDef>, List <VariableDef> >(new List <ClusterDef>(), new List <VariableDef>()); dictHierarchies[clusDef.ClusterId] = kvpCurrentHierarchy; } else { // Find the root cluster of the hierarchy. Default to the DefaultClusterHierarchy. int rootId = 0; for (ClusterDef clusDefParent = clusDef.ParentClusterDef; null != clusDefParent; clusDefParent = clusDefParent.ParentClusterDef) { if (clusDefParent.IsNewHierarchy) { rootId = clusDefParent.ClusterId; Validate.IsNull(clusDefParent.ParentClusterDef, "IsNewHierarchy cluster's parent must be null"); break; } } kvpCurrentHierarchy = dictHierarchies[rootId]; kvpCurrentHierarchy.Key.Add(clusDef); } // endifelse IsNewHierarchy foreach (VariableDef varDef in clusDef.Variables) { kvpCurrentHierarchy.Value.Add(varDef); } } // endfor each cluster } }
private ESection ProcessCluster(int lineNumber, string currentLine, ESection currentSection, ref EClusterState currentClusterState) { if (currentLine.StartsWith( TestFileStrings.EndCluster, StringComparison.OrdinalIgnoreCase)) { if (EClusterState.Variable != currentClusterState) { Validate.Fail(string.Format("Unexpected END CLUSTER line {0}: {1}", lineNumber, currentLine)); } currentSection = ESection.PreClusterOrConstraints; this.ClusterDefs.Add(this.currentClusterDef); this.currentClusterDef = null; return(currentSection); } if (EClusterState.Id == currentClusterState) { Match m = TestFileStrings.ParseClusterId.Match(currentLine); if (m.Success) { // Verify the Clusters in the file are sorted on ID. This makes it easier for the results // reading to be in sync, as we'll index ClusterDefs by [Parent - 1]. var id = int.Parse(m.Groups["id"].ToString()); Validate.IsTrue(this.currentClusterDef.ClusterId == id, "Out of order CLUSTER id"); } else { Validate.Fail(string.Format("Unparsable CLUSTER ID line {0}: {1}", lineNumber, currentLine)); } currentClusterState = EClusterState.Parent; } else if (EClusterState.Parent == currentClusterState) { Match m = TestFileStrings.ParseClusterParent.Match(currentLine); if (m.Success) { int parentId = int.Parse(m.Groups["parent"].ToString()); // Cluster IDs are 1-based because we use 0 for the "root cluster". if (0 != parentId) { ClusterDef clusParent = this.ClusterDefs[parentId - 1]; Validate.AreEqual(clusParent.ClusterId, parentId, "clusParent.ClusterId mismatch with idParent"); clusParent.AddClusterDef(this.currentClusterDef); } } else { Validate.Fail(string.Format("Unparsable CLUSTER Parent line {0}: {1}", lineNumber, currentLine)); } currentClusterState = EClusterState.LeftBorder; } else if (EClusterState.LeftBorder == currentClusterState) { // Older files didn't have MinSize. Match m = TestFileStrings.ParseClusterMinSize.Match(currentLine); if (m.Success) { this.currentClusterDef.MinimumSizeX = double.Parse(m.Groups["X"].ToString()); this.currentClusterDef.MinimumSizeY = double.Parse(m.Groups["Y"].ToString()); return(currentSection); } if (0 == string.Compare("NewHierarchy", currentLine, StringComparison.OrdinalIgnoreCase)) { // NewHierarchy is optional. this.currentClusterDef.IsNewHierarchy = true; return(currentSection); } this.currentClusterDef.LeftBorderInfo = ParseBorderInfo("Left", currentLine, lineNumber); currentClusterState = EClusterState.RightBorder; } else if (EClusterState.RightBorder == currentClusterState) { this.currentClusterDef.RightBorderInfo = ParseBorderInfo("Right", currentLine, lineNumber); currentClusterState = EClusterState.TopBorder; } else if (EClusterState.TopBorder == currentClusterState) { this.currentClusterDef.TopBorderInfo = ParseBorderInfo("Top", currentLine, lineNumber); currentClusterState = EClusterState.BottomBorder; } else if (EClusterState.BottomBorder == currentClusterState) { this.currentClusterDef.BottomBorderInfo = ParseBorderInfo("Bottom", currentLine, lineNumber); currentClusterState = EClusterState.Variable; } else if (EClusterState.Variable == currentClusterState) { Match m = TestFileStrings.ParseClusterVariable.Match(currentLine); if (m.Success) { int variableId = int.Parse(m.Groups["var"].ToString()); this.currentClusterDef.AddVariableDef(this.VariableDefs[variableId]); } else { Validate.Fail(string.Format("Unparsable CLUSTER Variable line {0}: {1}", lineNumber, currentLine)); } } return(currentSection); }
internal static void WriteFile(int seed, double maxWeightToGenerate, Solver solverX, Solver solverY, Solution solutionX, Solution solutionY, double minPaddingX, double minPaddingY, double minClusterSizeX, double minClusterSizeY, double maxMargin, List <VariableDef> lstVarDefs, List <ClusterDef> lstClusDefs, StreamWriter outputFileWriter) { // TODO_cleanup: make shared strings; regenerate test files to verify // Add the summary information as comments. @@ (high-level) and @# (low-level) allow // findstr etc. scans of the file metadata; @[@#] gets both. outputFileWriter.WriteLine("// @@Variables: {0}", lstVarDefs.Count); outputFileWriter.WriteLine("// @@Clusters: {0}", (null == lstClusDefs) ? 0 : lstClusDefs.Count); outputFileWriter.WriteLine("// @@Constraints_X: {0}", solverX.Constraints.Count()); outputFileWriter.WriteLine("// @@Constraints_Y: {0}", solverY.Constraints.Count()); outputFileWriter.WriteLine(); // Values we want to read back in. outputFileWriter.WriteLine("Seed 0x{0}", seed.ToString("X")); outputFileWriter.WriteLine("Weight {0:F5}", maxWeightToGenerate); outputFileWriter.WriteLine("Padding {0:F5} {1:F5}", minPaddingX, minPaddingY); outputFileWriter.WriteLine("MinClusterSize {0:F5} {1:F5}", minClusterSizeX, minClusterSizeY); outputFileWriter.WriteLine("Margin {0}", maxMargin); outputFileWriter.WriteLine("Goal {0} {1}", solutionX.GoalFunctionValue, solutionY.GoalFunctionValue); outputFileWriter.WriteLine(); outputFileWriter.WriteLine(TestFileStrings.BeginVariables); for (int idxVar = 0; idxVar < lstVarDefs.Count; ++idxVar) { VariableDef varDef = lstVarDefs[idxVar]; outputFileWriter.WriteLine( TestFileStrings.WriteVariable2D, idxVar, varDef.DesiredPosX, varDef.DesiredPosY, varDef.SizeX, varDef.SizeY, varDef.WeightX, varDef.WeightY); } outputFileWriter.WriteLine(TestFileStrings.EndVariables); outputFileWriter.WriteLine(); outputFileWriter.Flush(); if (null != lstClusDefs) { // Write out the clusters, starting at 1 to skip the root. Since we populate the // clusterdefs left-to-right we'll always print out the parents before the children. foreach (ClusterDef clusDef in lstClusDefs) { outputFileWriter.WriteLine(TestFileStrings.BeginCluster); // Write the current cluster definition. outputFileWriter.WriteLine(TestFileStrings.WriteClusterId, clusDef.ClusterId); outputFileWriter.WriteLine( TestFileStrings.WriteClusterParent, null == clusDef.ParentClusterDef ? 0 : clusDef.ParentClusterDef.ClusterId); outputFileWriter.WriteLine( TestFileStrings.WriteClusterMinSize, clusDef.MinimumSizeX, clusDef.MinimumSizeY); if (clusDef.IsNewHierarchy) { outputFileWriter.WriteLine("NewHierarchy"); } outputFileWriter.WriteLine( TestFileStrings.WriteClusterBorder, "Left", clusDef.LeftBorderInfo.InnerMargin, ClusterDef.IsFixedString(clusDef.LeftBorderInfo), clusDef.LeftBorderInfo.Weight); outputFileWriter.WriteLine( TestFileStrings.WriteClusterBorder, "Right", clusDef.RightBorderInfo.InnerMargin, ClusterDef.IsFixedString(clusDef.RightBorderInfo), clusDef.RightBorderInfo.Weight); outputFileWriter.WriteLine( TestFileStrings.WriteClusterBorder, "Top", clusDef.TopBorderInfo.InnerMargin, ClusterDef.IsFixedString(clusDef.TopBorderInfo), clusDef.TopBorderInfo.Weight); outputFileWriter.WriteLine( TestFileStrings.WriteClusterBorder, "Bottom", clusDef.BottomBorderInfo.InnerMargin, ClusterDef.IsFixedString(clusDef.BottomBorderInfo), clusDef.BottomBorderInfo.Weight); outputFileWriter.WriteLine("// @#ClusterVars: {0}", clusDef.Variables.Count); foreach (VariableDef varDef in clusDef.Variables) { outputFileWriter.WriteLine(TestFileStrings.WriteClusterVariable, varDef.IdString); } outputFileWriter.WriteLine(TestFileStrings.EndCluster); outputFileWriter.WriteLine(); } outputFileWriter.Flush(); } // endif clusters exist // Write the constraints. // TODOclus: This is outputting vars Lnn Rnn in DEBUG and an empty string in RELEASE; consider making the file // output (with clusters, anyway) run in DEBUG-only and have TestFileReader.cs know how to decode them. outputFileWriter.WriteLine(TestFileStrings.BeginConstraintsX); foreach (Constraint cst in solverX.Constraints.OrderBy(cst => cst)) { // There are no equality constraints in OverlapRemoval so pass an empty string. outputFileWriter.WriteLine( TestFileStrings.WriteConstraint, ((OverlapRemovalNode)cst.Left.UserData).UserData, ((OverlapRemovalNode)cst.Right.UserData).UserData, string.Empty, cst.Gap); } outputFileWriter.WriteLine(TestFileStrings.EndConstraints); outputFileWriter.WriteLine(); outputFileWriter.WriteLine(TestFileStrings.BeginConstraintsY); foreach (Constraint cst in solverY.Constraints.OrderBy(cst => cst)) { // There are no equality constraints in OverlapRemoval so pass an empty string. outputFileWriter.WriteLine( TestFileStrings.WriteConstraint, ((OverlapRemovalNode)cst.Left.UserData).UserData, ((OverlapRemovalNode)cst.Right.UserData).UserData, string.Empty, cst.Gap); } outputFileWriter.WriteLine(TestFileStrings.EndConstraints); outputFileWriter.WriteLine(); // Now write the results. outputFileWriter.WriteLine(TestFileStrings.BeginResults); foreach (VariableDef varDef in lstVarDefs) { outputFileWriter.WriteLine(TestFileStrings.WriteResults2D, varDef.IdString, varDef.VariableX.ActualPos, varDef.VariableY.ActualPos); } // endforeach varDef outputFileWriter.WriteLine(TestFileStrings.EndResults); if (null != lstClusDefs) { outputFileWriter.WriteLine(); outputFileWriter.WriteLine(TestFileStrings.BeginClusterResults); outputFileWriter.WriteLine("// (includes only clusters that are not IsNewHierarchy)"); foreach (ClusterDef clusDef in lstClusDefs) { // Clusters at the root of a hierarchy have no borders. if (!clusDef.IsNewHierarchy) { outputFileWriter.WriteLine( TestFileStrings.WriteClusterResults, clusDef.ClusterId, clusDef.Left, clusDef.Right, clusDef.Top, clusDef.Bottom); } } outputFileWriter.WriteLine(TestFileStrings.EndClusterResults); } // Done. outputFileWriter.Flush(); outputFileWriter.Close(); }