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:
parent
02cea246b5
commit
d977552d8f
|
@ -37,7 +37,7 @@ namespace Nuclex.Support.Packing {
|
||||||
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>
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace Nuclex.Support.Packing {
|
||||||
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>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user