feat: add StripeFiller.ConvergeStripeAngle iterative convergence
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using OpenNest.Engine.Strategies;
|
using OpenNest.Engine.Strategies;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using OpenNest.Math;
|
using OpenNest.Math;
|
||||||
@@ -75,6 +76,57 @@ public class StripeFiller
|
|||||||
return bestAngle;
|
return bestAngle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Iteratively finds the rotation angle where N copies of the pattern
|
||||||
|
/// span the given dimension with minimal waste.
|
||||||
|
/// Returns (angle, waste, pairCount).
|
||||||
|
/// </summary>
|
||||||
|
public static (double Angle, double Waste, int Count) ConvergeStripeAngle(
|
||||||
|
List<Part> patternParts, double sheetSpan, double spacing,
|
||||||
|
NestDirection axis, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var currentAngle = 0.0;
|
||||||
|
var bestWaste = double.MaxValue;
|
||||||
|
var bestAngle = 0.0;
|
||||||
|
var bestCount = 0;
|
||||||
|
var tolerance = sheetSpan * 0.001;
|
||||||
|
|
||||||
|
for (var iteration = 0; iteration < MaxConvergenceIterations; iteration++)
|
||||||
|
{
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var rotated = FillHelpers.BuildRotatedPattern(patternParts, currentAngle);
|
||||||
|
var pairSpan = GetDimension(rotated.BoundingBox, axis);
|
||||||
|
|
||||||
|
if (pairSpan + spacing <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var n = (int)System.Math.Floor((sheetSpan + spacing) / (pairSpan + spacing));
|
||||||
|
if (n <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var usedSpan = n * (pairSpan + spacing) - spacing;
|
||||||
|
var remaining = sheetSpan - usedSpan;
|
||||||
|
|
||||||
|
if (remaining < bestWaste)
|
||||||
|
{
|
||||||
|
bestWaste = remaining;
|
||||||
|
bestAngle = currentAngle;
|
||||||
|
bestCount = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining <= tolerance)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var delta = remaining / n;
|
||||||
|
var targetSpan = pairSpan + delta;
|
||||||
|
|
||||||
|
currentAngle = FindAngleForTargetSpan(patternParts, targetSpan, axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bestAngle, bestWaste, bestCount);
|
||||||
|
}
|
||||||
|
|
||||||
private static double BisectForTarget(
|
private static double BisectForTarget(
|
||||||
List<Part> patternParts, double lo, double hi,
|
List<Part> patternParts, double lo, double hi,
|
||||||
double targetSpan, NestDirection axis)
|
double targetSpan, NestDirection axis)
|
||||||
|
|||||||
@@ -60,4 +60,36 @@ public class StripeFillerTests
|
|||||||
|
|
||||||
Assert.True(angle >= 0 && angle <= System.Math.PI / 2);
|
Assert.True(angle >= 0 && angle <= System.Math.PI / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConvergeStripeAngle_ReducesWaste()
|
||||||
|
{
|
||||||
|
var pattern = MakeRectPattern(20, 10);
|
||||||
|
var (angle, waste, count) = StripeFiller.ConvergeStripeAngle(
|
||||||
|
pattern.Parts, 120.0, 0.5, NestDirection.Horizontal);
|
||||||
|
|
||||||
|
Assert.True(count >= 5, $"Expected at least 5 pairs, got {count}");
|
||||||
|
Assert.True(waste < 18.0, $"Expected waste < 18, got {waste:F2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConvergeStripeAngle_HandlesExactFit()
|
||||||
|
{
|
||||||
|
var pattern = MakeRectPattern(10, 5);
|
||||||
|
var (angle, waste, count) = StripeFiller.ConvergeStripeAngle(
|
||||||
|
pattern.Parts, 100.0, 0.0, NestDirection.Horizontal);
|
||||||
|
|
||||||
|
Assert.Equal(10, count);
|
||||||
|
Assert.True(waste < 0.2, $"Expected near-zero waste, got {waste:F2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ConvergeStripeAngle_Vertical()
|
||||||
|
{
|
||||||
|
var pattern = MakeRectPattern(10, 20);
|
||||||
|
var (angle, waste, count) = StripeFiller.ConvergeStripeAngle(
|
||||||
|
pattern.Parts, 120.0, 0.5, NestDirection.Vertical);
|
||||||
|
|
||||||
|
Assert.True(count >= 5, $"Expected at least 5 pairs, got {count}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user