mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-15 15:55:36 -05:00
With a pierce diameter set, every internal contour whose configured pierce is inside the zone is resolved by flattening the cutout, finding its pole and eroding it, and the search emits a part's whole chain again on every expansion, so on parts with dozens of such holes almost all of the planning time went there: three 1,600-motion parts took 41 minutes and timed out in production. The resolution is a pure function of the hole, the entry, the configured lead-in, the zone and the turn, so each preparation now keeps it by contour ordinal and reuses it, unchanged, across its emissions and copies. The same plate plans in 148 s. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
66 lines
3.1 KiB
C#
66 lines
3.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.CNC.CuttingStrategy
|
|
{
|
|
/// <summary>
|
|
/// Remembers <see cref="ContourCuttingStrategy.ResolvePierceZone"/> for one preparation's
|
|
/// contours. The resolution reads only the cutout's own geometry, the entry, the configured
|
|
/// lead-in and the zone, so for a given contour it is a pure function of those, yet the
|
|
/// search emits every contour of a part's chain again on every expansion, and on a cutout
|
|
/// whose configured pierce is inside the zone each resolution flattens the cutout, finds its
|
|
/// pole and erodes it. One memo serves every emission of one <c>PreparedContours</c> and
|
|
/// its copies, which share the contours by ordinal. A lead-in a memo returns is shared and
|
|
/// must not be mutated.
|
|
/// </summary>
|
|
internal sealed class PierceZoneMemo
|
|
{
|
|
private readonly Dictionary<Key, LeadIn> resolved = new();
|
|
private readonly object gate = new();
|
|
|
|
/// <summary>How many resolutions the memo answered without computing; for tests.</summary>
|
|
internal int Hits { get; private set; }
|
|
|
|
internal LeadIn Resolve(int contour, Shape hole, Vector point, LeadIn leadIn, double normal,
|
|
double pierceRadius, RotationType turn)
|
|
{
|
|
if (KeyOf(leadIn) is not { } lead)
|
|
return ContourCuttingStrategy.ResolvePierceZone(hole, point, leadIn, normal, pierceRadius, turn);
|
|
var key = new Key(contour, point.X, point.Y, normal, pierceRadius, turn, lead);
|
|
lock (gate)
|
|
{
|
|
if (resolved.TryGetValue(key, out var known))
|
|
{
|
|
Hits++;
|
|
return known;
|
|
}
|
|
}
|
|
var result = ContourCuttingStrategy.ResolvePierceZone(hole, point, leadIn, normal, pierceRadius, turn);
|
|
lock (gate)
|
|
{
|
|
resolved[key] = result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// The configured lead-in by value: an equal lead-in resolves the same way whichever
|
|
// instance carries it. A lead-in type this does not know is resolved every time.
|
|
private static LeadKey? KeyOf(LeadIn leadIn) => leadIn switch
|
|
{
|
|
NoLeadIn => new(0, 0, 0, 0, 0),
|
|
LineLeadIn line => new(1, line.Length, line.ApproachAngle, 0, 0),
|
|
ArcLeadIn arc => new(2, arc.Radius, arc.SweepAngle, 0, 0),
|
|
LineArcLeadIn lineArc => new(3, lineArc.LineLength, lineArc.ApproachAngle, lineArc.ArcRadius, 0),
|
|
CleanHoleLeadIn cleanHole => new(4, cleanHole.LineLength, cleanHole.ArcRadius, cleanHole.Kerf, 0),
|
|
LineLineLeadIn lineLine => new(5, lineLine.Length1, lineLine.ApproachAngle1, lineLine.Length2, lineLine.ApproachAngle2),
|
|
_ => null,
|
|
};
|
|
|
|
private readonly record struct LeadKey(int Kind, double A, double B, double C, double D);
|
|
|
|
private readonly record struct Key(int Contour, double X, double Y, double Normal, double PierceRadius,
|
|
RotationType Turn, LeadKey Lead);
|
|
}
|
|
}
|