New "Cygon" Packer is working and promptly achieved a new space efficiency record

git-svn-id: file:///srv/devel/repo-conversion/nusu@22 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2007-05-20 21:03:21 +00:00
parent 5756ed94b3
commit 4fd0680ae7
2 changed files with 71 additions and 55 deletions

View File

@ -103,76 +103,95 @@ namespace Nuclex.Support.Packing {
/// <param name="rectangleHeight">Height of the rectangle to find a position for</param>
/// <returns>The best position for a rectangle with the specified width</returns>
private int findBestPosition(int rectangleWidth, int rectangleHeight) {
// Index and score of the best slice we could find for the rectangle
int bestSliceIndex = -1;
int bestScore = MaxPackingAreaWidth * MaxPackingAreaHeight; // lower == better!
// This is the counter for the currently checked position. The search works by
// skipping from slice to slice, determining the suitability of the location for the
// placement of the rectangle.
int leftSliceIndex = 0;
// Determine the slice in which the right end of the rectangle is located
int rightSliceIndex = this.heightSlices.BinarySearch(
new Point(rectangleWidth, 0), SliceStartComparer.Default
);
if(rightSliceIndex < 0)
rightSliceIndex = ~rightSliceIndex;
int leastWastedSliceIndex = -1;
int leastWastedArea = MaxPackingAreaWidth * MaxPackingAreaHeight;
while(rightSliceIndex <= this.heightSlices.Count) {
// final time (this is the special case where the rectangle is attempted
// to be placed at the rightmost end of the packing area)
/*
// Determine the highest slice at this position. We cannot put the rectangle
// any lower than this without colliding into other rectangles
int highest = this.heightSlices[leftSliceIndex].Y;
for(int index = leftSliceIndex + 1; index < rightSliceIndex; ++index)
highest = Math.Max(highest, this.heightSlices[index].Y);
// Determine the highest slice within the slices covered by the rectangle at
// its current placement. We cannot put the rectangle any lower than this without
// overlapping the other rectangles.
int highest = this.heightSlices[leftSliceIndex].Y;
for(int index = leftSliceIndex + 1; index < rightSliceIndex; ++index)
if(this.heightSlices[index].Y > highest)
highest = this.heightSlices[index].Y;
// Calculate the amount of space that would go to waste if the rectangle
// would be placed at this location
int wastedArea = 0;
for(int index = leftSliceIndex; index < rightSliceIndex - 1; ++index) {
int sliceWidth = this.heightSlices[index + 1].X - this.heightSlices[index].X;
wastedArea += (highest - this.heightSlices[index].Y) * sliceWidth;
}
wastedArea +=
(highest - this.heightSlices[rightSliceIndex - 1].Y) *
(
(this.heightSlices[leftSliceIndex].X + rectangleWidth) -
this.heightSlices[rightSliceIndex - 1].X
);
if((highest + rectangleHeight < MaxPackingAreaHeight)) {
int score = highest;
// If this beats the current record for the least wasted area, remember this as
// being the best position found so far
if(
(wastedArea < leastWastedArea) &&
(this.heightSlices[leftSliceIndex].Y + rectangleHeight < MaxPackingAreaHeight)
) {
leastWastedArea = wastedArea;
leastWastedSliceIndex = leftSliceIndex;
// TESTING --------------------------------------------------
/*
// Calculate the amount of space that would go to waste if the rectangle
// would be placed at this location
int wastedArea = 0;
for(int index = leftSliceIndex; index < rightSliceIndex - 1; ++index) {
int sliceWidth = this.heightSlices[index + 1].X - this.heightSlices[index].X;
wastedArea += (highest - this.heightSlices[index].Y) * sliceWidth;
}
wastedArea +=
(highest - this.heightSlices[rightSliceIndex - 1].Y) *
(
(this.heightSlices[leftSliceIndex].X + rectangleWidth) -
this.heightSlices[rightSliceIndex - 1].X
);
// No sense looking any further if we found the perfect place!
if(leastWastedArea == 0)
break;
}
*/
//score += Math.Sign(wastedArea);
//score += (int)Math.Sqrt((double)wastedArea);
//score = wastedArea;
*/
// TESTING --------------------------------------------------
// If this already was the loop after the final slice, terminate it now!
if(rightSliceIndex == this.heightSlices.Count)
break;
if(score < bestScore) {
bestSliceIndex = leftSliceIndex;
bestScore = score;
}
}
// Advance the starting slice to the next slice start
++leftSliceIndex;
int rightEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth;
if(leftSliceIndex >= this.heightSlices.Count)
break;
// Advance the ending slice to where the rectangle ends now
while(rightEnd > this.heightSlices[rightSliceIndex].X) {
++rightSliceIndex;
// If the end is reached, stop shifting and make the outer loop run one final time
// Advance the ending slice until we're on the right slice again, given the new
// starting position of the rectangle.
int rightRectangleEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth;
for(; rightSliceIndex <= this.heightSlices.Count; ++rightSliceIndex) {
int rightSliceStart;
if(rightSliceIndex == this.heightSlices.Count)
rightSliceStart = MaxPackingAreaWidth;
else
rightSliceStart = this.heightSlices[rightSliceIndex].X;
// Is this the slice we're looking for?
if(rightSliceStart > rightRectangleEnd)
break;
}
// If we crossed the end of the slice array, the rectangle's right end has left
// the packing area, and thus, our search ends.
if(rightSliceIndex > this.heightSlices.Count)
break;
}
return leastWastedSliceIndex;
// Return the index of the best slice we found for this rectangle. If the rectangle
// didn't fit, this variable will still have its initialization value of -1.
return bestSliceIndex;
}
/// <summary>Integrates a new rectangle into the height slice table</summary>
@ -213,8 +232,8 @@ namespace Nuclex.Support.Packing {
if(startSlice >= this.heightSlices.Count) {
// If the slice ends within the last slice (usual case, unless it has the
// exact same with the packing area has), add another slice to return to the
// original height at the end of the rectangle.
// exact same width the packing area has), add another slice to return to
// the original height at the end of the rectangle.
if(right < MaxPackingAreaWidth)
this.heightSlices.Add(new Point(right, firstSliceOriginalHeight));
@ -242,9 +261,8 @@ namespace Nuclex.Support.Packing {
else
returnHeight = this.heightSlices[endSlice - 1].Y;
// Remove all slices covered by the rectangle and began a new slice at
// its end to return to the height the slice in which the rectangle ends
// has had.
// Remove all slices covered by the rectangle and begin a new slice at its end
// to return back to the height of the slice on which the rectangle ends.
this.heightSlices.RemoveRange(startSlice, endSlice - startSlice);
if(right < MaxPackingAreaWidth)
this.heightSlices.Insert(startSlice, new Point(right, returnHeight));

View File

@ -54,8 +54,6 @@ namespace Nuclex.Support.Packing {
return (float)areaCovered / 4900.0f;
}
}
} // namespace Nuclex.Support.Packing