Box constructor and derived properties (Right, Top, Center, Translate, Offset) had Width and Length swapped — Length is X axis, Width is Y axis. Corrected across Core geometry, plate bounding box, rectangle packing, fill algorithms, tests, and UI renderers. Added FillSpiral with center remnant detection and recursive FillBest on the gap between the 4 spiral quadrants. RectFill.FillBest now compares spiral+center vs full best-fit fairly. BestCombination returns a CombinationResult record instead of out params. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
123 lines
2.8 KiB
C#
123 lines
2.8 KiB
C#
using OpenNest.Geometry;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace OpenNest.RectanglePacking
|
|
{
|
|
internal class FirstFitDecreasing : PackEngine
|
|
{
|
|
private readonly List<Level> levels;
|
|
|
|
public FirstFitDecreasing(Bin bin)
|
|
: base(bin)
|
|
{
|
|
levels = new List<Level>();
|
|
}
|
|
|
|
public override void Pack(List<Item> items)
|
|
{
|
|
items = items.OrderBy(i => -i.Width).ToList();
|
|
|
|
foreach (var item in items)
|
|
{
|
|
if (item.Width > Bin.Width)
|
|
continue;
|
|
|
|
var level = FindLevel(item);
|
|
|
|
if (level == null)
|
|
continue;
|
|
|
|
level.AddItem(item);
|
|
}
|
|
}
|
|
|
|
private Level FindLevel(Item item)
|
|
{
|
|
foreach (var level in levels)
|
|
{
|
|
if (level.Height < item.Width)
|
|
continue;
|
|
|
|
if (level.RemainingLength < item.Length)
|
|
continue;
|
|
|
|
return level;
|
|
}
|
|
|
|
return CreateNewLevel(item);
|
|
}
|
|
|
|
private Level CreateNewLevel(Item item)
|
|
{
|
|
var y = Bin.Y;
|
|
var lastLevel = levels.LastOrDefault();
|
|
|
|
if (lastLevel != null)
|
|
y = lastLevel.Y + lastLevel.Height;
|
|
|
|
var remaining = Bin.Top - y;
|
|
|
|
if (remaining < item.Width)
|
|
return null;
|
|
|
|
var level = new Level(Bin);
|
|
level.Y = y;
|
|
level.Height = item.Width;
|
|
|
|
levels.Add(level);
|
|
|
|
return level;
|
|
}
|
|
|
|
private class Level
|
|
{
|
|
public Level(Bin parent)
|
|
{
|
|
Parent = parent;
|
|
NextItemLocation = parent.Location;
|
|
}
|
|
|
|
public Bin Parent { get; set; }
|
|
|
|
private Vector NextItemLocation;
|
|
|
|
public double X
|
|
{
|
|
get { return Parent.X; }
|
|
}
|
|
|
|
public double Y
|
|
{
|
|
get { return NextItemLocation.Y; }
|
|
set { NextItemLocation.Y = value; }
|
|
}
|
|
|
|
public double LevelLength
|
|
{
|
|
get { return Parent.Length; }
|
|
}
|
|
|
|
public double Height { get; set; }
|
|
|
|
public double Top
|
|
{
|
|
get { return Y + Height; }
|
|
}
|
|
|
|
public double RemainingLength
|
|
{
|
|
get { return X + LevelLength - NextItemLocation.X; }
|
|
}
|
|
|
|
public void AddItem(Item item)
|
|
{
|
|
item.Location = NextItemLocation;
|
|
Parent.Items.Add(item);
|
|
|
|
NextItemLocation = new Vector(NextItemLocation.X + item.Length, NextItemLocation.Y);
|
|
}
|
|
}
|
|
}
|
|
}
|