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:
parent
5756ed94b3
commit
4fd0680ae7
|
@ -103,76 +103,95 @@ namespace Nuclex.Support.Packing {
|
||||||
/// <param name="rectangleHeight">Height of the rectangle to find a position for</param>
|
/// <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>
|
/// <returns>The best position for a rectangle with the specified width</returns>
|
||||||
private int findBestPosition(int rectangleWidth, int rectangleHeight) {
|
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;
|
int leftSliceIndex = 0;
|
||||||
|
|
||||||
|
// Determine the slice in which the right end of the rectangle is located
|
||||||
int rightSliceIndex = this.heightSlices.BinarySearch(
|
int rightSliceIndex = this.heightSlices.BinarySearch(
|
||||||
new Point(rectangleWidth, 0), SliceStartComparer.Default
|
new Point(rectangleWidth, 0), SliceStartComparer.Default
|
||||||
);
|
);
|
||||||
if(rightSliceIndex < 0)
|
if(rightSliceIndex < 0)
|
||||||
rightSliceIndex = ~rightSliceIndex;
|
rightSliceIndex = ~rightSliceIndex;
|
||||||
|
|
||||||
int leastWastedSliceIndex = -1;
|
|
||||||
int leastWastedArea = MaxPackingAreaWidth * MaxPackingAreaHeight;
|
|
||||||
|
|
||||||
while(rightSliceIndex <= this.heightSlices.Count) {
|
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);
|
|
||||||
|
|
||||||
// Calculate the amount of space that would go to waste if the rectangle
|
// Determine the highest slice within the slices covered by the rectangle at
|
||||||
// would be placed at this location
|
// its current placement. We cannot put the rectangle any lower than this without
|
||||||
int wastedArea = 0;
|
// overlapping the other rectangles.
|
||||||
for(int index = leftSliceIndex; index < rightSliceIndex - 1; ++index) {
|
int highest = this.heightSlices[leftSliceIndex].Y;
|
||||||
int sliceWidth = this.heightSlices[index + 1].X - this.heightSlices[index].X;
|
for(int index = leftSliceIndex + 1; index < rightSliceIndex; ++index)
|
||||||
wastedArea += (highest - this.heightSlices[index].Y) * sliceWidth;
|
if(this.heightSlices[index].Y > highest)
|
||||||
}
|
highest = this.heightSlices[index].Y;
|
||||||
wastedArea +=
|
|
||||||
(highest - this.heightSlices[rightSliceIndex - 1].Y) *
|
|
||||||
(
|
|
||||||
(this.heightSlices[leftSliceIndex].X + rectangleWidth) -
|
|
||||||
this.heightSlices[rightSliceIndex - 1].X
|
|
||||||
);
|
|
||||||
|
|
||||||
// If this beats the current record for the least wasted area, remember this as
|
if((highest + rectangleHeight < MaxPackingAreaHeight)) {
|
||||||
// being the best position found so far
|
int score = highest;
|
||||||
if(
|
|
||||||
(wastedArea < leastWastedArea) &&
|
|
||||||
(this.heightSlices[leftSliceIndex].Y + rectangleHeight < MaxPackingAreaHeight)
|
|
||||||
) {
|
|
||||||
leastWastedArea = wastedArea;
|
|
||||||
leastWastedSliceIndex = leftSliceIndex;
|
|
||||||
|
|
||||||
// No sense looking any further if we found the perfect place!
|
// TESTING --------------------------------------------------
|
||||||
if(leastWastedArea == 0)
|
/*
|
||||||
break;
|
// 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
|
||||||
|
);
|
||||||
|
|
||||||
|
//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(score < bestScore) {
|
||||||
if(rightSliceIndex == this.heightSlices.Count)
|
bestSliceIndex = leftSliceIndex;
|
||||||
break;
|
bestScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Advance the starting slice to the next slice start
|
// Advance the starting slice to the next slice start
|
||||||
++leftSliceIndex;
|
++leftSliceIndex;
|
||||||
int rightEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth;
|
if(leftSliceIndex >= this.heightSlices.Count)
|
||||||
|
break;
|
||||||
|
|
||||||
// Advance the ending slice to where the rectangle ends now
|
// Advance the ending slice until we're on the right slice again, given the new
|
||||||
while(rightEnd > this.heightSlices[rightSliceIndex].X) {
|
// starting position of the rectangle.
|
||||||
++rightSliceIndex;
|
int rightRectangleEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth;
|
||||||
|
for(; rightSliceIndex <= this.heightSlices.Count; ++rightSliceIndex) {
|
||||||
// If the end is reached, stop shifting and make the outer loop run one final time
|
int rightSliceStart;
|
||||||
if(rightSliceIndex == this.heightSlices.Count)
|
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;
|
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>
|
/// <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(startSlice >= this.heightSlices.Count) {
|
||||||
|
|
||||||
// If the slice ends within the last slice (usual case, unless it has the
|
// 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
|
// exact same width the packing area has), add another slice to return to
|
||||||
// original height at the end of the rectangle.
|
// the original height at the end of the rectangle.
|
||||||
if(right < MaxPackingAreaWidth)
|
if(right < MaxPackingAreaWidth)
|
||||||
this.heightSlices.Add(new Point(right, firstSliceOriginalHeight));
|
this.heightSlices.Add(new Point(right, firstSliceOriginalHeight));
|
||||||
|
|
||||||
|
@ -242,9 +261,8 @@ namespace Nuclex.Support.Packing {
|
||||||
else
|
else
|
||||||
returnHeight = this.heightSlices[endSlice - 1].Y;
|
returnHeight = this.heightSlices[endSlice - 1].Y;
|
||||||
|
|
||||||
// Remove all slices covered by the rectangle and began a new slice at
|
// Remove all slices covered by the rectangle and begin a new slice at its end
|
||||||
// its end to return to the height the slice in which the rectangle ends
|
// to return back to the height of the slice on which the rectangle ends.
|
||||||
// has had.
|
|
||||||
this.heightSlices.RemoveRange(startSlice, endSlice - startSlice);
|
this.heightSlices.RemoveRange(startSlice, endSlice - startSlice);
|
||||||
if(right < MaxPackingAreaWidth)
|
if(right < MaxPackingAreaWidth)
|
||||||
this.heightSlices.Insert(startSlice, new Point(right, returnHeight));
|
this.heightSlices.Insert(startSlice, new Point(right, returnHeight));
|
||||||
|
|
|
@ -54,8 +54,6 @@ namespace Nuclex.Support.Packing {
|
||||||
return (float)areaCovered / 4900.0f;
|
return (float)areaCovered / 4900.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Packing
|
} // namespace Nuclex.Support.Packing
|
||||||
|
|
Loading…
Reference in New Issue
Block a user