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
					
				
					 5 changed files with 49 additions and 37 deletions
				
			
		|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue