void addPrevSolutionIfSubstantial(Stack <ChunkSolutionRect> solution, ChunkSolutionRect prev) { if (solution == null) { throw new ArgumentNullException("solution"); } if (!prev.IsEmpty) { if (solution.Count == 0) { solution.Push(prev); } else { var threshold = this.SubstantialBlock; //SubstantialBlock.RoundMultiply(this.Area.Length); var test = prev; test.TrimOff(solution.Peek()); if (test.Count > threshold) { exclusivePush(solution, prev); } } } }
public bool IsOverlap(ChunkSolutionRect other) { if (this.Col > other.RightCol || this.RightCol < other.Col || this.Row > other.BottomRow || this.BottomRow < other.Row) { return(false); } return(true); }
bool tryFullColsOfFalseOnLeft(ref ChunkSolutionRect testsample, int fullcollength) { var sorter = this.Sorter; //full column scan, skim side var colsempty = sorter.Columns.TakeWhile(s => !s.Contains(true)).Count(); if (colsempty != 0) { testsample.ColWidth = colsempty; testsample.RowHeight = fullcollength; return(true); } return(false); }
bool tryFullRowsOfFalseOnTop(ref ChunkSolutionRect testsample, int fullrowlength) { var sorter = this.Sorter; //full row scan, skim top var rowsempty = sorter.Rows.TakeWhile(s => !s.Contains(true)).Count(); if (rowsempty != 0) { testsample.ColWidth = fullrowlength; testsample.RowHeight = rowsempty; return(true); } return(false); }
public void TrimOff(ChunkSolutionRect other) { if (other.HasAll(this)) { throw new ArgumentException("The other chunk completely encloses this one. Trimming off intersections, will leave nothing"); } if (this.IsOverlap(other)) { //how to do a set subtraction, based simply on x,y,w,h of 2 rect... //simply just find which edge intersects with this rectangle and "chop" off in direction of inside the rectangle //var clearright = this.RightCol < other.Col; //var clearbottom = this.BottomRow < other.Row; //var cleartop = this.Row > other.BottomRow; //var clearleft = this.Col > other.RightCol; var topinside = this.Row < other.Row && other.Row <= this.BottomRow; var bottominside = this.Row <= other.BottomRow && other.BottomRow < this.BottomRow; var leftinside = this.Col < other.Col && other.Col <= this.RightCol; var rightinside = this.Col <= other.RightCol && other.RightCol < this.RightCol; if (leftinside) //left edge of other is inside right border, so erase everything of this, right of other's border { this.ColWidth = this.Col - other.Col - 1; } if (topinside)//top edge of other is inside bottom border, so erase everything of this, bottom of other's border { this.RowHeight = this.Row - other.Row - 1; } if (rightinside) { var right = this.RightCol; this.ColWidth = this.RightCol - other.RightCol; this.Col = other.RightCol + 1; // other.Col - this.ColWidth - 1; System.Diagnostics.Debug.Assert(this.ColWidth > 0, "this is obvious, the width still has to be >0"); System.Diagnostics.Debug.Assert(right == this.RightCol, "these should still be the same"); System.Diagnostics.Debug.Assert(other.RightCol < this.Col, "the current left edge should be distinct from other's right"); } if (bottominside) { var bottom = this.BottomRow; this.RowHeight = this.BottomRow - other.BottomRow; this.Row = other.BottomRow + 1; System.Diagnostics.Debug.Assert(this.RowHeight > 0, "this is obvious, the height still has to be >0"); System.Diagnostics.Debug.Assert(bottom == this.BottomRow, "these should still be the same"); System.Diagnostics.Debug.Assert(other.BottomRow < this.Row, "the current top edge should be distinct from other's bottom"); } } }
void exclusivePush(Stack <ChunkSolutionRect> solution, ChunkSolutionRect entry) { if (!entry.IsEmpty) { if (solution.Count == 0) { solution.Push(entry); } else { var last = solution.Pop(); last.TrimOff(entry); // the popped off one, AND remove common records from new set entry.TrimOff(last); // now remove the common records from older one set, from latest set, to make them mutually exclusive solution.Push(last); solution.Push(entry); } } }
public int SubstantialBlock = 75; //100 bool ifSubstantial(ChunkSolutionRect current, ChunkSolutionRect prev) { if (!current.IsEmpty) { var threshold = this.SubstantialBlock; //SubstantialBlock.RoundMultiply(this.Area.Length); var test = prev; test.TrimOff(prev); if (test.Count > threshold) { return(true); } else { return(false); } } else { throw new ArgumentException("The current solution is empty, how is this a solution, much less a substantial one compared to previous"); } }
IEnumerable <ChunkSolutionRect> scanColendingForSolutions(int startrow, int[] colend, int colcount, ChunkSolutionRect lastsolution, int rowcount) { var sorter = this.Sorter; Stack <ChunkSolutionRect> solutions = new Stack <ChunkSolutionRect>(); var testsample = new ChunkSolutionRect() { Col = 0, Row = startrow }; var maxcount = 0; var bottomedgerow = int.MaxValue; var samplemax = default(ChunkSolutionRect); //skim the side, search for solutions in bottom bubble int compensate = 0; //the column heights were searched for before solutions were found. Now a place to store new column heights, if they were to be searched for now var lastsolutionbottom = lastsolution.BottomRow + 1; if (lastsolutionbottom > startrow) { compensate = lastsolutionbottom - startrow; //we just store the difference between the last solution row, and the where search started } if (startrow + compensate < rowcount) { testsample.Col = 0; testsample.Row = startrow + compensate; //obvious the solution has to start, forward compensated for the new solution start for (int i = 0; i < colcount; i++) { var rowheight = colend[i] - compensate; //and the height has to be backward compensated for new solution start if (rowheight <= 0) //backward compensation makes negative possible { break; } if (rowheight < bottomedgerow) { bottomedgerow = rowheight; } else if (rowheight > bottomedgerow) { rowheight = bottomedgerow; } testsample.ColWidth = i + 1; testsample.RowHeight = rowheight; var count = testsample.Count; //(i - startcol) * (j - startrow + 1); if (count > maxcount) { maxcount = count; if (samplemax.RowHeight != testsample.RowHeight) //don't bother doing the comprehensive test, if it doesn't pass this simple one b/c unless the height of sample changes, the testsample will always be superset of prev samplemax { addPrevSolutionIfSubstantial(solutions, samplemax); } samplemax = testsample; System.Diagnostics.Debug.WriteLine("max at each (row {0}) x (col {1}) = (count elements {2}) <= running max={3}, min right edge={4}", rowheight, i + 1, count, maxcount, bottomedgerow); #if DEBUG for (int y = testsample.Row; y <= testsample.BottomRow; y++) { for (int x = testsample.Col; x <= testsample.RightCol; x++) { if (sorter[y, x]) { System.Diagnostics.Debug.WriteLine(sorter[y, x], "Solution is misaligned. THe solution elements should all be false"); } } } #endif } } if (solutions.Count == 0 || samplemax != solutions.Peek()) { exclusivePush(solutions, samplemax); } } return(solutions); }
IEnumerable <ChunkSolutionRect> scanRowendingForSolutions(int startcol, int[] rowend, int rowcount) { var sorter = this.Sorter; Stack <ChunkSolutionRect> solutions = new Stack <ChunkSolutionRect>(); var testsample = new ChunkSolutionRect() { Col = startcol, Row = 0 }; var maxcount = 0; var rightedgecol = int.MaxValue; var samplemax = default(ChunkSolutionRect); for (int j = 0; j < rowcount; j++) { var colwidth = rowend[j]; // the rowend is actually a column index, or length to first true, or end of elements if (colwidth == 0) { break; } if (colwidth < rightedgecol) { rightedgecol = colwidth; } else if (colwidth > rightedgecol) { colwidth = rightedgecol; } testsample.ColWidth = colwidth; testsample.RowHeight = j + 1; var count = testsample.Count; //(i - startcol) * (j - startrow + 1); if (count > maxcount) { maxcount = count; if (samplemax.ColWidth != testsample.ColWidth) //don't bother doing the comprehensive test, if it doesn't pass this simple one b/c unless the width of sample changes, the testsample will always be superset of prev samplemax { addPrevSolutionIfSubstantial(solutions, samplemax); } samplemax = testsample; System.Diagnostics.Debug.WriteLine("max at each (row {0}) x (col {1}) = (count elements {2}) <= running max={3}, min right edge={4}", j + 1, colwidth, count, maxcount, rightedgecol); #if DEBUG for (int y = testsample.Row; y <= testsample.BottomRow; y++) { for (int x = testsample.Col; x <= testsample.RightCol; x++) { if (sorter[y, x]) { System.Diagnostics.Debug.WriteLine(sorter[y, x], "Solution is misaligned. THe solution elements should all be false"); } } } #endif } } if (solutions.Count == 0 || samplemax != solutions.Peek()) { exclusivePush(solutions, samplemax); } return(solutions); }
public IEnumerable <ArraySubblock> Chunkify(bool heavy) { if (this.Area == null) { this.Area = this.Sorter.Area; } var area = this.Area; var sorter = this.Sorter; while (true) { sorter.Sort(); if (sorter.IsAllEqual && sorter.IsAllTrue) { break; } //initialize program variables ChunkSolutionRect testsample = new ChunkSolutionRect() { Row = 0, Col = 0 }; var rowcount = sorter.RowCount; var colcount = sorter.ColumnCount; if (!sorter[0, 0]) { //full row scan, skim top if (tryFullRowsOfFalseOnTop(ref testsample, colcount)) { yield return(testsample.ToArraySubblock(sorter)); } //full column scan, skim side else if (tryFullColsOfFalseOnLeft(ref testsample, rowcount)) { yield return(testsample.ToArraySubblock(sorter)); } else { //look for biggest block testsample = getBiggestPossibleRectInUpperLeft(rowcount, colcount); yield return(testsample.ToArraySubblock(sorter)); } } else //sorter[0, 0] is true { //algorithm below, will actually work for sorter[0, 0] is false, but the above algorithm is much easier to understand //skim the top irregular shapes, bubbles in upper left //Stack<ChunkSolutionRect> solutions = new Stack<ChunkSolutionRect>(); //ChunkSolutionRect samplemax = default(ChunkSolutionRect); //var max = 0; //find first bubbles in top and left, set to startcol, startrow var startcol = getFirstFalseOnTopRow(colcount); var startrow = getFirstFalseOnLeftCol(rowcount); //run in parallel search for the first row or col with true int[] rowlen = null, collen = null; Parallel.For(0, 2, delegate(int branch) { if (branch == 1) { rowlen = getArrayOfFirstTrueInAllRowAfterCol(startcol, colcount, rowcount); } else { collen = getArrayOfFirstTrueInAllColumnAfterRow(startrow, colcount, rowcount); } }); //search for solutions in top bubble var lastsolution = default(ChunkSolutionRect); foreach (var solution in scanRowendingForSolutions(startcol, rowlen, rowcount).Reverse()) { lastsolution = solution; yield return(solution.ToArraySubblock(sorter)); } foreach (var solution in scanColendingForSolutions(startrow, collen, colcount, lastsolution, rowcount).Reverse()) { lastsolution = solution; yield return(solution.ToArraySubblock(sorter)); } //foreach (var solution in solutions.Reverse()) // yield return solution.ToArraySubblock(sorter); } } }
public bool HasAll(ChunkSolutionRect other) { return(other.Exists(this.Row, this.Col) && other.Exists(this.Row, this.RightCol) && other.Exists(this.BottomRow, this.Col) && other.Exists(this.BottomRow, this.RightCol)); }