Files
CutList/SawCut/Nesting/BestFitEngine.cs
AJ 703efd528a Encapsulate mutable collections in Bin and Result
Replace public mutable collection fields/properties with private
backing fields and expose them as IReadOnlyList. Add proper methods
for mutation (AddItem, AddItems, RemoveItem, SortItems).

Changes:
- Bin.Items: Now private with AddItem/AddItems/RemoveItem/SortItems
- Result.ItemsNotUsed: Now readonly with Add methods
- Result.Bins: Now readonly with Add methods
- Updated all engine classes to use new encapsulated APIs

This improves encapsulation and prevents external code from
bypassing business logic by directly manipulating collections.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 16:02:48 -05:00

111 lines
2.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace SawCut.Nesting
{
public class BestFitEngine : IEngine
{
public double StockLength { get; set; }
public double Spacing { get; set; }
public int MaxBinCount { get; set; } = int.MaxValue;
private List<BinItem> Items { get; set; }
public Result Pack(List<BinItem> items)
{
if (StockLength <= 0)
throw new Exception("Stock length must be greater than 0");
Items = items.OrderByDescending(i => i.Length).ToList();
var result = new Result();
var itemsTooLarge = Items.Where(i => i.Length > StockLength).ToList();
result.AddItemsNotUsed(itemsTooLarge);
foreach (var item in itemsTooLarge)
{
Items.Remove(item);
}
var bins = GetBins();
result.AddBins(bins);
foreach (var bin in bins)
{
foreach (var item in bin.Items)
{
Items.Remove(item);
}
}
result.AddItemsNotUsed(Items);
return result;
}
private List<Bin> GetBins()
{
var bins = new List<Bin>();
foreach (var item in Items)
{
Bin best_bin;
if (!FindBin(bins.ToArray(), item.Length, out best_bin))
{
if (item.Length > StockLength)
continue;
if (bins.Count < MaxBinCount)
{
best_bin = CreateBin();
bins.Add(best_bin);
}
}
if (best_bin != null)
best_bin.AddItem(item);
}
return bins
.OrderByDescending(b => b.Utilization)
.ThenBy(b => b.Items.Count)
.ToList();
}
private Bin CreateBin()
{
var length = StockLength;
return new Bin(length)
{
Spacing = Spacing
};
}
private static bool FindBin(IEnumerable<Bin> bins, double length, out Bin found)
{
found = null;
foreach (var bin in bins)
{
if (bin.RemainingLength < length)
continue;
if (found == null)
found = bin;
if (bin.RemainingLength < found.RemainingLength)
found = bin;
}
return (found != null);
}
}
}