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>
|
||||
/// <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);
|
||||
|
||||
// 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
|
||||
);
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
if((highest + rectangleHeight < MaxPackingAreaHeight)) {
|
||||
int score = highest;
|
||||
|
||||
// No sense looking any further if we found the perfect place!
|
||||
if(leastWastedArea == 0)
|
||||
break;
|
||||
}
|
||||
*/
|
||||
// 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
|
||||
);
|
||||
|
||||
//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));
|
||||
|
|
|
@ -54,8 +54,6 @@ namespace Nuclex.Support.Packing {
|
|||
return (float)areaCovered / 4900.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Packing
|
||||
|
|
Loading…
Reference in New Issue
Block a user