feat(console): improve training data collection and best-fit persistence
- Add verbose per-file and per-sheet-size console output during collection - Skip already-processed parts at the sheet-size level instead of all-or-nothing - Precompute best-fits once per part and reuse across all sheet sizes - Clear best-fit cache after each part to prevent memory growth - Save best-fits in separate bestfits/ zip entries instead of embedding in nest.json - Filter to Keep=true results only and scope to plate sizes in the nest - Set nest name to match filename (includes sheet size and part count) - Add TrainingDatabase with per-run skip logic and SQLite schema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,93 @@ namespace OpenNest.Engine.BestFit
|
||||
}
|
||||
}
|
||||
|
||||
public static void ComputeForSizes(
|
||||
Drawing drawing, double spacing,
|
||||
IEnumerable<(double Width, double Height)> plateSizes)
|
||||
{
|
||||
// Skip sizes that are already cached.
|
||||
var needed = new List<(double Width, double Height)>();
|
||||
foreach (var size in plateSizes)
|
||||
{
|
||||
var key = new CacheKey(drawing, size.Width, size.Height, spacing);
|
||||
if (!_cache.ContainsKey(key))
|
||||
needed.Add(size);
|
||||
}
|
||||
|
||||
if (needed.Count == 0)
|
||||
return;
|
||||
|
||||
// Find the largest plate to use for the initial computation — this
|
||||
// keeps the filter maximally permissive so we don't discard results
|
||||
// that a smaller plate might still use after re-filtering.
|
||||
var maxWidth = 0.0;
|
||||
var maxHeight = 0.0;
|
||||
foreach (var size in needed)
|
||||
{
|
||||
if (size.Width > maxWidth) maxWidth = size.Width;
|
||||
if (size.Height > maxHeight) maxHeight = size.Height;
|
||||
}
|
||||
|
||||
IPairEvaluator evaluator = null;
|
||||
ISlideComputer slideComputer = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (CreateEvaluator != null)
|
||||
{
|
||||
try { evaluator = CreateEvaluator(drawing, spacing); }
|
||||
catch { /* fall back to default evaluator */ }
|
||||
}
|
||||
|
||||
if (CreateSlideComputer != null)
|
||||
{
|
||||
try { slideComputer = CreateSlideComputer(); }
|
||||
catch { /* fall back to CPU slide computation */ }
|
||||
}
|
||||
|
||||
// Compute candidates and evaluate once with the largest plate.
|
||||
var finder = new BestFitFinder(maxWidth, maxHeight, evaluator, slideComputer);
|
||||
var baseResults = finder.FindBestFits(drawing, spacing, StepSize);
|
||||
|
||||
// Cache a filtered copy for each plate size.
|
||||
foreach (var size in needed)
|
||||
{
|
||||
var filter = new BestFitFilter
|
||||
{
|
||||
MaxPlateWidth = size.Width,
|
||||
MaxPlateHeight = size.Height
|
||||
};
|
||||
|
||||
var copy = new List<BestFitResult>(baseResults.Count);
|
||||
for (var i = 0; i < baseResults.Count; i++)
|
||||
{
|
||||
var r = baseResults[i];
|
||||
copy.Add(new BestFitResult
|
||||
{
|
||||
Candidate = r.Candidate,
|
||||
RotatedArea = r.RotatedArea,
|
||||
BoundingWidth = r.BoundingWidth,
|
||||
BoundingHeight = r.BoundingHeight,
|
||||
OptimalRotation = r.OptimalRotation,
|
||||
TrueArea = r.TrueArea,
|
||||
HullAngles = r.HullAngles,
|
||||
Keep = r.Keep,
|
||||
Reason = r.Reason
|
||||
});
|
||||
}
|
||||
|
||||
filter.Apply(copy);
|
||||
|
||||
var key = new CacheKey(drawing, size.Width, size.Height, spacing);
|
||||
_cache.TryAdd(key, copy);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
(evaluator as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Invalidate(Drawing drawing)
|
||||
{
|
||||
foreach (var key in _cache.Keys)
|
||||
@@ -63,6 +150,25 @@ namespace OpenNest.Engine.BestFit
|
||||
}
|
||||
}
|
||||
|
||||
public static void Populate(Drawing drawing, double plateWidth, double plateHeight,
|
||||
double spacing, List<BestFitResult> results)
|
||||
{
|
||||
var key = new CacheKey(drawing, plateWidth, plateHeight, spacing);
|
||||
_cache.TryAdd(key, results);
|
||||
}
|
||||
|
||||
public static Dictionary<(double PlateWidth, double PlateHeight, double Spacing), List<BestFitResult>>
|
||||
GetAllForDrawing(Drawing drawing)
|
||||
{
|
||||
var result = new Dictionary<(double, double, double), List<BestFitResult>>();
|
||||
foreach (var kvp in _cache)
|
||||
{
|
||||
if (ReferenceEquals(kvp.Key.Drawing, drawing))
|
||||
result[(kvp.Key.PlateWidth, kvp.Key.PlateHeight, kvp.Key.Spacing)] = kvp.Value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
_cache.Clear();
|
||||
|
||||
Reference in New Issue
Block a user