perf: replace linear sweep with iterative halving in RotationSlideStrategy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,13 +53,15 @@ namespace OpenNest.Engine.BestFit
|
|||||||
double spacing, double stepSize, PushDirection pushDir,
|
double spacing, double stepSize, PushDirection pushDir,
|
||||||
List<PairCandidate> candidates, ref int testNumber)
|
List<PairCandidate> candidates, ref int testNumber)
|
||||||
{
|
{
|
||||||
|
const int CoarseMultiplier = 16;
|
||||||
|
const int MaxRegions = 5;
|
||||||
|
|
||||||
var bbox1 = part1.BoundingBox;
|
var bbox1 = part1.BoundingBox;
|
||||||
var bbox2 = part2Template.BoundingBox;
|
var bbox2 = part2Template.BoundingBox;
|
||||||
var halfSpacing = spacing / 2;
|
var halfSpacing = spacing / 2;
|
||||||
|
|
||||||
var isHorizontalPush = pushDir == PushDirection.Left || pushDir == PushDirection.Right;
|
var isHorizontalPush = pushDir == PushDirection.Left || pushDir == PushDirection.Right;
|
||||||
|
|
||||||
// Perpendicular range: part2 slides across the full extent of part1
|
|
||||||
double perpMin, perpMax, pushStartOffset;
|
double perpMin, perpMax, pushStartOffset;
|
||||||
|
|
||||||
if (isHorizontalPush)
|
if (isHorizontalPush)
|
||||||
@@ -75,33 +77,102 @@ namespace OpenNest.Engine.BestFit
|
|||||||
pushStartOffset = bbox1.Length + bbox2.Length + spacing * 2;
|
pushStartOffset = bbox1.Length + bbox2.Length + spacing * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute part1's offset lines (half-spacing outward)
|
|
||||||
var part1Lines = Helper.GetOffsetPartLines(part1, halfSpacing);
|
var part1Lines = Helper.GetOffsetPartLines(part1, halfSpacing);
|
||||||
|
|
||||||
// Align sweep start to a multiple of stepSize so that offset=0 is always
|
// Start with the full range as a single region.
|
||||||
// included. This ensures perfect grid arrangements (side-by-side, stacked)
|
var regions = new List<(double min, double max)> { (perpMin, perpMax) };
|
||||||
// are generated for rectangular parts.
|
var currentStep = stepSize * CoarseMultiplier;
|
||||||
var alignedStart = System.Math.Ceiling(perpMin / stepSize) * stepSize;
|
|
||||||
|
|
||||||
for (var offset = alignedStart; offset <= perpMax; offset += stepSize)
|
// Iterative halving: coarse sweep, select top regions, narrow, repeat.
|
||||||
|
while (currentStep > stepSize)
|
||||||
{
|
{
|
||||||
var (slideDist, finalPosition) = ComputeSlideResult(
|
var hits = new List<(double offset, double slideDist)>();
|
||||||
part2Template, part1Lines, halfSpacing,
|
|
||||||
offset, pushStartOffset, isHorizontalPush, pushDir);
|
|
||||||
|
|
||||||
if (slideDist >= double.MaxValue || slideDist < 0)
|
foreach (var (regionMin, regionMax) in regions)
|
||||||
continue;
|
|
||||||
|
|
||||||
candidates.Add(new PairCandidate
|
|
||||||
{
|
{
|
||||||
Drawing = drawing,
|
var alignedStart = System.Math.Ceiling(regionMin / currentStep) * currentStep;
|
||||||
Part1Rotation = 0,
|
|
||||||
Part2Rotation = Part2Rotation,
|
for (var offset = alignedStart; offset <= regionMax; offset += currentStep)
|
||||||
Part2Offset = finalPosition,
|
{
|
||||||
StrategyType = Type,
|
var slideDist = ComputeSlideDistance(
|
||||||
TestNumber = testNumber++,
|
part2Template, part1Lines, halfSpacing,
|
||||||
Spacing = spacing
|
offset, pushStartOffset, isHorizontalPush, pushDir);
|
||||||
});
|
|
||||||
|
if (slideDist >= double.MaxValue || slideDist < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hits.Add((offset, slideDist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Select top regions by tightest fit, deduplicating nearby hits.
|
||||||
|
hits.Sort((a, b) => a.slideDist.CompareTo(b.slideDist));
|
||||||
|
|
||||||
|
var selectedOffsets = new List<double>();
|
||||||
|
|
||||||
|
foreach (var (offset, _) in hits)
|
||||||
|
{
|
||||||
|
var tooClose = false;
|
||||||
|
|
||||||
|
foreach (var selected in selectedOffsets)
|
||||||
|
{
|
||||||
|
if (System.Math.Abs(offset - selected) < currentStep)
|
||||||
|
{
|
||||||
|
tooClose = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tooClose)
|
||||||
|
{
|
||||||
|
selectedOffsets.Add(offset);
|
||||||
|
|
||||||
|
if (selectedOffsets.Count >= MaxRegions)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build narrowed regions around selected offsets.
|
||||||
|
regions = new List<(double min, double max)>();
|
||||||
|
|
||||||
|
foreach (var offset in selectedOffsets)
|
||||||
|
{
|
||||||
|
var regionMin = System.Math.Max(perpMin, offset - currentStep);
|
||||||
|
var regionMax = System.Math.Min(perpMax, offset + currentStep);
|
||||||
|
regions.Add((regionMin, regionMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentStep /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final pass: sweep refined regions at stepSize, generating candidates.
|
||||||
|
foreach (var (regionMin, regionMax) in regions)
|
||||||
|
{
|
||||||
|
var alignedStart = System.Math.Ceiling(regionMin / stepSize) * stepSize;
|
||||||
|
|
||||||
|
for (var offset = alignedStart; offset <= regionMax; offset += stepSize)
|
||||||
|
{
|
||||||
|
var (slideDist, finalPosition) = ComputeSlideResult(
|
||||||
|
part2Template, part1Lines, halfSpacing,
|
||||||
|
offset, pushStartOffset, isHorizontalPush, pushDir);
|
||||||
|
|
||||||
|
if (slideDist >= double.MaxValue || slideDist < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidates.Add(new PairCandidate
|
||||||
|
{
|
||||||
|
Drawing = drawing,
|
||||||
|
Part1Rotation = 0,
|
||||||
|
Part2Rotation = Part2Rotation,
|
||||||
|
Part2Offset = finalPosition,
|
||||||
|
StrategyType = Type,
|
||||||
|
TestNumber = testNumber++,
|
||||||
|
Spacing = spacing
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user