Some minor cosmetic fixes; improved several variable names; added more commentation

git-svn-id: file:///srv/devel/repo-conversion/nusu@27 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2007-05-28 18:11:41 +00:00
parent 02cea246b5
commit d977552d8f
5 changed files with 49 additions and 37 deletions

View File

@ -36,8 +36,8 @@ namespace Nuclex.Support.Packing {
[Test] [Test]
public void TestSpaceEfficiency() { public void TestSpaceEfficiency() {
float efficiency = CalculateEfficiency(new ArevaloRectanglePacker(70, 70)); float efficiency = CalculateEfficiency(new ArevaloRectanglePacker(70, 70));
Assert.GreaterOrEqual(efficiency, 0.75, "Packer achieves 75% efficiency"); Assert.GreaterOrEqual(efficiency, 0.75f, "Packer achieves 75% efficiency");
} }
/// <summary>Tests the packer's stability by running a complete benchmark</summary> /// <summary>Tests the packer's stability by running a complete benchmark</summary>

View File

@ -147,7 +147,7 @@ namespace Nuclex.Support.Packing {
// positions of the new rectangle // positions of the new rectangle
{ {
// The anchor is only removed if the placement optimization didn't // The anchor is only removed if the placement optimization didn't
// move the rectangle so far that the anchor isn't used at all // move the rectangle so far that the anchor isn't blocked anymore
bool blocksAnchor = bool blocksAnchor =
((placement.X + rectangleWidth) > this.anchors[anchorIndex].X) && ((placement.X + rectangleWidth) > this.anchors[anchorIndex].X) &&
((placement.Y + rectangleHeight) > this.anchors[anchorIndex].Y); ((placement.Y + rectangleHeight) > this.anchors[anchorIndex].Y);
@ -165,8 +165,8 @@ namespace Nuclex.Support.Packing {
new Rectangle(placement.X, placement.Y, rectangleWidth, rectangleHeight) new Rectangle(placement.X, placement.Y, rectangleWidth, rectangleHeight)
); );
return true; return true;
} }
/// <summary> /// <summary>
@ -209,7 +209,8 @@ namespace Nuclex.Support.Packing {
} }
/// <summary> /// <summary>
/// Searches for a free anchor and enlarges the packing area if none can be found /// Searches for a free anchor and recursively enlarges the packing area
/// if none can be found.
/// </summary> /// </summary>
/// <param name="rectangleWidth">Width of the rectangle to be placed</param> /// <param name="rectangleWidth">Width of the rectangle to be placed</param>
/// <param name="rectangleHeight">Height of the rectangle to be placed</param> /// <param name="rectangleHeight">Height of the rectangle to be placed</param>
@ -217,7 +218,7 @@ namespace Nuclex.Support.Packing {
/// <param name="testedPackingAreaHeight">Height of the tested packing area</param> /// <param name="testedPackingAreaHeight">Height of the tested packing area</param>
/// <returns> /// <returns>
/// Index of the anchor the rectangle is to be placed at or -1 if the rectangle /// Index of the anchor the rectangle is to be placed at or -1 if the rectangle
/// does not fit in the packing area anymore /// does not fit in the packing area anymore.
/// </returns> /// </returns>
private int selectAnchorRecursive( private int selectAnchorRecursive(
int rectangleWidth, int rectangleHeight, int rectangleWidth, int rectangleHeight,
@ -248,13 +249,13 @@ namespace Nuclex.Support.Packing {
// any further in its width and in its height // any further in its width and in its height
bool canEnlargeWidth = (testedPackingAreaWidth < PackingAreaWidth); bool canEnlargeWidth = (testedPackingAreaWidth < PackingAreaWidth);
bool canEnlargeHeight = (testedPackingAreaHeight < PackingAreaHeight); bool canEnlargeHeight = (testedPackingAreaHeight < PackingAreaHeight);
// Try to enlarge the smaller of the two dimensions first (unless the smaller // Try to enlarge the smaller of the two dimensions first (unless the smaller
// dimension is already at its maximum size) // dimension is already at its maximum size)
if( if(
canEnlargeHeight && ( canEnlargeHeight && (
(testedPackingAreaHeight < testedPackingAreaWidth) || !canEnlargeWidth (testedPackingAreaHeight < testedPackingAreaWidth) || !canEnlargeWidth
) )
) { ) {
// Try to double the height of the packing area // Try to double the height of the packing area
@ -284,12 +285,12 @@ namespace Nuclex.Support.Packing {
/// <summary>Locates the first free anchor at which the rectangle fits</summary> /// <summary>Locates the first free anchor at which the rectangle fits</summary>
/// <param name="rectangleWidth">Width of the rectangle to be placed</param> /// <param name="rectangleWidth">Width of the rectangle to be placed</param>
/// <param name="rectangleHeight">Height of the rectangle to be placed</param> /// <param name="rectangleHeight">Height of the rectangle to be placed</param>
/// <param name="packingAreaWidth">Total width of the packing area</param> /// <param name="testedPackingAreaWidth">Total width of the packing area</param>
/// <param name="packingAreaHeight">Total height of the packing area</param> /// <param name="testedPackingAreaHeight">Total height of the packing area</param>
/// <returns>The index of the first free anchor or -1 if none is found</returns> /// <returns>The index of the first free anchor or -1 if none is found</returns>
private int findFirstFreeAnchor( private int findFirstFreeAnchor(
int rectangleWidth, int rectangleHeight, int rectangleWidth, int rectangleHeight,
int packingAreaWidth, int packingAreaHeight int testedPackingAreaWidth, int testedPackingAreaHeight
) { ) {
Rectangle potentialLocation = new Rectangle( Rectangle potentialLocation = new Rectangle(
0, 0, rectangleWidth, rectangleHeight 0, 0, rectangleWidth, rectangleHeight
@ -303,7 +304,7 @@ namespace Nuclex.Support.Packing {
potentialLocation.Y = this.anchors[index].Y; potentialLocation.Y = this.anchors[index].Y;
// See if the rectangle would fit in at this anchor point // See if the rectangle would fit in at this anchor point
if(isFree(ref potentialLocation, packingAreaWidth, packingAreaHeight)) if(isFree(ref potentialLocation, testedPackingAreaWidth, testedPackingAreaHeight))
return index; return index;
} }
@ -316,11 +317,11 @@ namespace Nuclex.Support.Packing {
/// at its current location. /// at its current location.
/// </summary> /// </summary>
/// <param name="rectangle">Rectangle whose position to check</param> /// <param name="rectangle">Rectangle whose position to check</param>
/// <param name="packingAreaWidth">Total width of the packing area</param> /// <param name="testedPackingAreaWidth">Total width of the packing area</param>
/// <param name="packingAreaHeight">Total height of the packing area</param> /// <param name="testedPackingAreaHeight">Total height of the packing area</param>
/// <returns>True if the rectangle can be placed at its current position</returns> /// <returns>True if the rectangle can be placed at its current position</returns>
private bool isFree( private bool isFree(
ref Rectangle rectangle, int packingAreaWidth, int packingAreaHeight ref Rectangle rectangle, int testedPackingAreaWidth, int testedPackingAreaHeight
) { ) {
// If the rectangle is partially or completely outside of the packing // If the rectangle is partially or completely outside of the packing
@ -328,8 +329,8 @@ namespace Nuclex.Support.Packing {
bool leavesPackingArea = bool leavesPackingArea =
(rectangle.X < 0) || (rectangle.X < 0) ||
(rectangle.Y < 0) || (rectangle.Y < 0) ||
(rectangle.Right >= packingAreaWidth) || (rectangle.Right >= testedPackingAreaWidth) ||
(rectangle.Bottom >= packingAreaHeight); (rectangle.Bottom >= testedPackingAreaHeight);
if(leavesPackingArea) if(leavesPackingArea)
return false; return false;

View File

@ -28,15 +28,20 @@ namespace Nuclex.Support.Packing {
/// <remarks> /// <remarks>
/// <para> /// <para>
/// Algorithm conceived by Markus Ewald (cygon at nuclex dot org), thought /// Algorithm conceived by Markus Ewald (cygon at nuclex dot org), thought
/// I'm quite sure I'm not the first one to invent this algorithm :) /// I'm quite sure I'm not the first one to come up with it :)
/// </para> /// </para>
/// <para> /// <para>
/// This algorithm always places rectangles as low as possible. So, for any /// The algorithm always places rectangles as low as possible in the packing
/// new rectangle that is to be added into the packing area, the packer has /// area. So, for any new rectangle that is to be added into the packing area,
/// to determine the X coordinate at which the rectangle has the lowest height. /// the packer has to determine the X coordinate at which the rectangle can have
/// To quickly discover these locations, the packer keeps a dynamically updated /// lowest overall height without overlapping any other rectangles.
/// list of "height slices" which store the silhouette of the rectangles that /// </para>
/// have been placed so far. /// <para>
/// To quickly discover these locations, the packer uses a sophisticated
/// data structure that stores the upper silhouette of the packing area. When
/// a new rectangle needs to be added, only the silouette edges need to be
/// analyzed to find the position where the rectangle would achieve the lowest
/// placement possible in the packing area.
/// </para> /// </para>
/// </remarks> /// </remarks>
public class CygonRectanglePacker : RectanglePacker { public class CygonRectanglePacker : RectanglePacker {
@ -91,7 +96,11 @@ namespace Nuclex.Support.Packing {
return false; return false;
} }
bool fits = findBestPosition(rectangleWidth, rectangleHeight, out placement); // Determine the placement for the new rectangle
bool fits = findBestPlacement(rectangleWidth, rectangleHeight, out placement);
// If a place for the rectangle could be found, update the height slice table to
// mark the region of the rectangle as being taken.
if(fits) if(fits)
integrateRectangle(placement.X, rectangleWidth, placement.Y + rectangleHeight); integrateRectangle(placement.X, rectangleWidth, placement.Y + rectangleHeight);
@ -101,8 +110,8 @@ namespace Nuclex.Support.Packing {
/// <summary>Finds the best position for a rectangle of the given width</summary> /// <summary>Finds the best position for a rectangle of the given width</summary>
/// <param name="rectangleWidth">Width of the rectangle to find a position for</param> /// <param name="rectangleWidth">Width of the rectangle to find a position for</param>
/// <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 of the specified dimensions</returns>
private bool findBestPosition( private bool findBestPlacement(
int rectangleWidth, int rectangleHeight, out Point placement int rectangleWidth, int rectangleHeight, out Point placement
) { ) {
@ -136,6 +145,7 @@ namespace Nuclex.Support.Packing {
if(this.heightSlices[index].Y > highest) if(this.heightSlices[index].Y > highest)
highest = this.heightSlices[index].Y; highest = this.heightSlices[index].Y;
// Only process this position if it doesn't leave the packing area
if((highest + rectangleHeight < PackingAreaHeight)) { if((highest + rectangleHeight < PackingAreaHeight)) {
int score = highest; int score = highest;
@ -151,7 +161,7 @@ namespace Nuclex.Support.Packing {
if(leftSliceIndex >= this.heightSlices.Count) if(leftSliceIndex >= this.heightSlices.Count)
break; break;
// Advance the ending slice until we're on the right slice again, given the new // Advance the ending slice until we're on the proper slice again, given the new
// starting position of the rectangle. // starting position of the rectangle.
int rightRectangleEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth; int rightRectangleEnd = this.heightSlices[leftSliceIndex].X + rectangleWidth;
for(; rightSliceIndex <= this.heightSlices.Count; ++rightSliceIndex) { for(; rightSliceIndex <= this.heightSlices.Count; ++rightSliceIndex) {
@ -171,10 +181,11 @@ namespace Nuclex.Support.Packing {
if(rightSliceIndex > this.heightSlices.Count) if(rightSliceIndex > this.heightSlices.Count)
break; break;
} } // while rightSliceIndex <= this.heightSlices.Count
// Return the index of the best slice we found for this rectangle. If the rectangle // Return the best placement we found for this rectangle. If the rectangle
// didn't fit, this variable will still have its initialization value of -1. // didn't fit anywhere, the slice index will still have its initialization value
// of -1 and we can report that no placement could be found.
if(bestSliceIndex == -1) { if(bestSliceIndex == -1) {
placement = Point.Zero; placement = Point.Zero;
return false; return false;
@ -258,9 +269,9 @@ namespace Nuclex.Support.Packing {
if(right < PackingAreaWidth) if(right < PackingAreaWidth)
this.heightSlices.Insert(startSlice, new Point(right, returnHeight)); this.heightSlices.Insert(startSlice, new Point(right, returnHeight));
} } // if endSlice > 0
} } // if startSlice >= this.heightSlices.Count
} }

View File

@ -80,7 +80,7 @@ namespace Nuclex.Support.Packing {
RectanglePacker packer = buildPacker(); RectanglePacker packer = buildPacker();
// Try to cramp as many rectangles into the packing area as possible // Try to cramp as many rectangles into the packing area as possible
for(;; ++rectanglesPacked) { for(; ; ++rectanglesPacked) {
Point placement; Point placement;
int width = dimensionGenerator.Next(16, 64); int width = dimensionGenerator.Next(16, 64);

View File

@ -36,8 +36,8 @@ namespace Nuclex.Support.Packing {
[Test] [Test]
public void TestSpaceEfficiency() { public void TestSpaceEfficiency() {
float efficiency = CalculateEfficiency(new SimpleRectanglePacker(70, 70)); float efficiency = CalculateEfficiency(new SimpleRectanglePacker(70, 70));
Assert.GreaterOrEqual(efficiency, 0.75, "Packer achieves 75% efficiency"); Assert.GreaterOrEqual(efficiency, 0.75f, "Packer achieves 75% efficiency");
} }
/// <summary>Tests the packer's stability by running a complete benchmark</summary> /// <summary>Tests the packer's stability by running a complete benchmark</summary>