refactor(engine): extract AngleCandidateBuilder.Build into focused helpers
Move known-good pruning check before sweep/ML to avoid wasted work, extract ContainsAngle, NeedsSweep, AddSweepAngles, ApplyMlPrediction, and BuildPrunedList so Build reads as a clear pipeline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,24 @@ namespace OpenNest.Engine.Fill
|
|||||||
|
|
||||||
public List<double> Build(NestItem item, double bestRotation, Box workArea)
|
public List<double> Build(NestItem item, double bestRotation, Box workArea)
|
||||||
{
|
{
|
||||||
var angles = new List<double> { bestRotation, bestRotation + Angle.HalfPI };
|
var baseAngles = new[] { bestRotation, bestRotation + Angle.HalfPI };
|
||||||
|
|
||||||
|
if (knownGoodAngles.Count > 0 && !ForceFullSweep)
|
||||||
|
return BuildPrunedList(baseAngles);
|
||||||
|
|
||||||
|
var angles = new List<double>(baseAngles);
|
||||||
|
|
||||||
|
if (NeedsSweep(item, bestRotation, workArea))
|
||||||
|
AddSweepAngles(angles);
|
||||||
|
|
||||||
|
if (!ForceFullSweep && angles.Count > 2)
|
||||||
|
angles = ApplyMlPrediction(item, workArea, baseAngles, angles);
|
||||||
|
|
||||||
|
return angles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool NeedsSweep(NestItem item, double bestRotation, Box workArea)
|
||||||
|
{
|
||||||
var testPart = new Part(item.Drawing);
|
var testPart = new Part(item.Drawing);
|
||||||
if (!bestRotation.IsEqualTo(0))
|
if (!bestRotation.IsEqualTo(0))
|
||||||
testPart.Rotate(bestRotation);
|
testPart.Rotate(bestRotation);
|
||||||
@@ -29,56 +45,57 @@ namespace OpenNest.Engine.Fill
|
|||||||
|
|
||||||
var partLongestSide = System.Math.Max(testPart.BoundingBox.Width, testPart.BoundingBox.Length);
|
var partLongestSide = System.Math.Max(testPart.BoundingBox.Width, testPart.BoundingBox.Length);
|
||||||
var workAreaShortSide = System.Math.Min(workArea.Width, workArea.Length);
|
var workAreaShortSide = System.Math.Min(workArea.Width, workArea.Length);
|
||||||
var needsSweep = workAreaShortSide < partLongestSide || ForceFullSweep;
|
return workAreaShortSide < partLongestSide || ForceFullSweep;
|
||||||
|
}
|
||||||
|
|
||||||
if (needsSweep)
|
private static void AddSweepAngles(List<double> angles)
|
||||||
{
|
{
|
||||||
var step = Angle.ToRadians(5);
|
var step = Angle.ToRadians(5);
|
||||||
for (var a = 0.0; a < System.Math.PI; a += step)
|
for (var a = 0.0; a < System.Math.PI; a += step)
|
||||||
{
|
{
|
||||||
if (!angles.Any(existing => existing.IsEqualTo(a)))
|
if (!ContainsAngle(angles, a))
|
||||||
angles.Add(a);
|
angles.Add(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ForceFullSweep && angles.Count > 2)
|
private static List<double> ApplyMlPrediction(
|
||||||
|
NestItem item, Box workArea, double[] baseAngles, List<double> fallback)
|
||||||
{
|
{
|
||||||
var features = FeatureExtractor.Extract(item.Drawing);
|
var features = FeatureExtractor.Extract(item.Drawing);
|
||||||
if (features != null)
|
if (features == null)
|
||||||
{
|
return fallback;
|
||||||
var predicted = AnglePredictor.PredictAngles(
|
|
||||||
features, workArea.Width, workArea.Length);
|
var predicted = AnglePredictor.PredictAngles(features, workArea.Width, workArea.Length);
|
||||||
|
if (predicted == null)
|
||||||
|
return fallback;
|
||||||
|
|
||||||
if (predicted != null)
|
|
||||||
{
|
|
||||||
var mlAngles = new List<double>(predicted);
|
var mlAngles = new List<double>(predicted);
|
||||||
|
foreach (var b in baseAngles)
|
||||||
if (!mlAngles.Any(a => a.IsEqualTo(bestRotation)))
|
|
||||||
mlAngles.Add(bestRotation);
|
|
||||||
if (!mlAngles.Any(a => a.IsEqualTo(bestRotation + Angle.HalfPI)))
|
|
||||||
mlAngles.Add(bestRotation + Angle.HalfPI);
|
|
||||||
|
|
||||||
Debug.WriteLine($"[AngleCandidateBuilder] ML: {angles.Count} angles -> {mlAngles.Count} predicted");
|
|
||||||
angles = mlAngles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (knownGoodAngles.Count > 0 && !ForceFullSweep)
|
|
||||||
{
|
{
|
||||||
var pruned = new List<double> { bestRotation, bestRotation + Angle.HalfPI };
|
if (!ContainsAngle(mlAngles, b))
|
||||||
|
mlAngles.Add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"[AngleCandidateBuilder] ML: {fallback.Count} angles -> {mlAngles.Count} predicted");
|
||||||
|
return mlAngles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<double> BuildPrunedList(double[] baseAngles)
|
||||||
|
{
|
||||||
|
var pruned = new List<double>(baseAngles);
|
||||||
foreach (var a in knownGoodAngles)
|
foreach (var a in knownGoodAngles)
|
||||||
{
|
{
|
||||||
if (!pruned.Any(existing => existing.IsEqualTo(a)))
|
if (!ContainsAngle(pruned, a))
|
||||||
pruned.Add(a);
|
pruned.Add(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"[AngleCandidateBuilder] Pruned: {angles.Count} -> {pruned.Count} angles (known-good)");
|
Debug.WriteLine($"[AngleCandidateBuilder] Pruned to {pruned.Count} angles (known-good)");
|
||||||
return pruned;
|
return pruned;
|
||||||
}
|
}
|
||||||
|
|
||||||
return angles;
|
private static bool ContainsAngle(List<double> angles, double angle)
|
||||||
|
{
|
||||||
|
return angles.Any(existing => existing.IsEqualTo(angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user