Simplify packing by reversing sort and growing in only 1 direction

This commit is contained in:
Jarrod Doyle 2024-08-28 22:03:48 +01:00
parent d55231178e
commit f6ef623bb4
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
2 changed files with 10 additions and 67 deletions

View File

@ -365,10 +365,8 @@ public partial class Mission : Node3D
{ {
var bounds = Timing.TimeStage("RectPack", () => var bounds = Timing.TimeStage("RectPack", () =>
{ {
var bounds = RectPacker.Pack(packingRects); return RectPacker.Pack(packingRects);
return bounds;
}); });
bounds = RectPacker.TightBounds(packingRects);
GD.Print($"Packed {packingRects.Length} rects in ({bounds.Width}, {bounds.Height})"); GD.Print($"Packed {packingRects.Length} rects in ({bounds.Width}, {bounds.Height})");
var lightmapFormat = Image.Format.Rgba8; var lightmapFormat = Image.Format.Rgba8;

View File

@ -13,21 +13,15 @@ public static class RectPacker
public ushort Height; public ushort Height;
} }
enum ExpandDir public static Rect Pack(Rect[] rectangles, ushort width = 1024, ushort minHeight = 512)
{
X,
Y,
}
public static Rect Pack(Rect[] rectangles, ushort minWidth = 512, ushort minHeight = 512)
{ {
ArgumentNullException.ThrowIfNull(rectangles); ArgumentNullException.ThrowIfNull(rectangles);
ArgumentOutOfRangeException.ThrowIfZero(minWidth); ArgumentOutOfRangeException.ThrowIfZero(width);
ArgumentOutOfRangeException.ThrowIfZero(minHeight); ArgumentOutOfRangeException.ThrowIfZero(minHeight);
var bounds = new Rect var bounds = new Rect
{ {
Width = minWidth, Width = width,
Height = minHeight Height = minHeight
}; };
if (rectangles.Length == 0) if (rectangles.Length == 0)
@ -35,51 +29,25 @@ public static class RectPacker
return bounds; return bounds;
} }
Array.Sort(rectangles, (a, b) => a.Height.CompareTo(b.Height)); Array.Sort(rectangles, (a, b) => b.Height.CompareTo(a.Height));
// Naively try to pack into the row, wrapping when we reach the end // Naively try to pack into the row, wrapping when we reach the end
// and expanding the bounds when needed // and expanding the bounds when needed
ushort rowStartX = 0; ushort rowMaxH = rectangles[0].Height;
ushort rowMaxH = 0;
ushort x = 0; ushort x = 0;
ushort y = 0; ushort y = 0;
var expandDir = ExpandDir.Y;
var rectCount = rectangles.Length; var rectCount = rectangles.Length;
for (var i = 0; i < rectCount; i++) for (var i = 0; i < rectCount; i++)
{ {
var rect = rectangles[i]; var rect = rectangles[i];
if (rect.Width == 0 || rect.Height == 0)
{
throw new ArgumentOutOfRangeException(nameof(rectangles), rectangles, $"{nameof(rectangles)} contains rect with width or height 0.");
}
// uh oh we've hit the edge, wrap to next row
if (x + rect.Width > bounds.Width) if (x + rect.Width > bounds.Width)
{ {
// we just loop to next row x = 0;
x = rowStartX;
y += rowMaxH; y += rowMaxH;
rowMaxH = 0; rowMaxH = rect.Height;
} if (y + rowMaxH > bounds.Height)
// uh oh we've hit the edge, expand
if (y + rect.Height > bounds.Height)
{
if (expandDir == ExpandDir.X)
{ {
// We can expand to the right
x = bounds.Width;
rowStartX = bounds.Width;
y = 0;
rowMaxH = 0;
bounds.Width *= 2;
expandDir = ExpandDir.Y;
}
else if (expandDir == ExpandDir.Y)
{
rowStartX = 0;
bounds.Height *= 2; bounds.Height *= 2;
expandDir = ExpandDir.X;
} }
} }
@ -88,32 +56,9 @@ public static class RectPacker
rectangles[i] = rect; rectangles[i] = rect;
x += rect.Width; x += rect.Width;
if (rect.Height > rowMaxH)
{
rowMaxH = rect.Height;
}
} }
return bounds; bounds.Height = (ushort)(y + rowMaxH);
}
public static Rect TightBounds(Rect[] rectangles)
{
var bounds = new Rect();
foreach (var rect in rectangles)
{
var w = rect.X + rect.Width;
if (w > bounds.Width)
{
bounds.Width = (ushort)w;
}
var h = rect.Y + rect.Height;
if (h > bounds.Height)
{
bounds.Height = (ushort)h;
}
}
return bounds; return bounds;
} }
} }