Files
OpenNest/OpenNest.Engine/CuttingPlanning/DrawingPinCapture.cs
T
ajandClaude Opus 5.5 7949d41b6d feat(core): pinned contour preparation
PreparedContours.Capture(program, parameters, pins) takes CutPinResolver
results for the same contours and fixes every resolved pin's contour: its
one and only entry is the pin's point, and it is emitted through the
existing emitter with the pin's own lead-in, lead-out and tab (angle
rounding off, so the entry never moves). Every catalogue returns the pinned
entry alone and emission refuses any other entry for that contour, so no
search path can relocate it. The certified tab size is the pinned outside's
own, so a pinned tab gap is genuine and replay certifies it. Unpinned
contours keep the shared settings and automatic selection; without pins the
existing preparation is untouched (5.1 digests unchanged).

Capture now resolves a regenerated part's drawing pins. A pin that does not
resolve (not on a contour, on several, sharing one, or unplaceable) is never
guessed: its contour stays automatic and the plate gets a FixedLead warning
naming the pin, making the proposal UNVERIFIED. A tab pinned on an internal
contour cannot be cut as pinned (only the outside is tabbed) and warns the
same way. Pins-off, locked parts with pins and freshness follow in 5.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 15:33:24 -04:00

55 lines
2.7 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OpenNest.CNC;
using OpenNest.CNC.CuttingPlanning;
using OpenNest.CNC.CuttingStrategy;
using OpenNest.Diagnostics;
namespace OpenNest.Engine.CuttingPlanning;
/// <summary>
/// Capture-time preparation of a regenerated part's contours with its drawing's pins (D1: the
/// drawing wins). A pin that does not resolve exactly is never guessed: its contour is planned
/// from the plate settings and the plate gets a warning, so its proposal is UNVERIFIED.
/// </summary>
internal static class DrawingPinCapture
{
internal static PreparedContours Prepare(Part source, int ordinal, Program ownedClean,
CuttingParameters parameters, List<CuttingPlanFinding> warnings, CancellationToken token)
{
var prepared = PreparedContours.Capture(ownedClean, parameters, token);
if (source.BaseDrawing.CutPins is not { Count: > 0 })
return prepared;
// The clean program is the drawing's own program rotated by the part's rotation, so the
// resolver maps the source-frame pins onto exactly these contours.
var resolutions = CutPinResolver.Resolve(source.BaseDrawing, source.Rotation, prepared, token);
var pinned = PreparedContours.Capture(ownedClean, parameters, resolutions, token);
foreach (var resolution in resolutions.Where(r => !r.IsResolved))
warnings.Add(Warning(source, ordinal, resolution, Unresolved(resolution)));
foreach (var rejected in pinned.RejectedPins)
warnings.Add(Warning(source, ordinal, rejected, $"Drawing pin {rejected.PinIndex + 1} puts a tab on an "
+ "internal contour, but only the outside can be tabbed; planned with nest settings."));
return pinned;
}
private static string Unresolved(CutPinResolution resolution)
{
var pin = resolution.PinIndex + 1;
return resolution.Status switch
{
CutPinStatus.NotOnContour => $"Drawing pin {pin} no longer matches the geometry; planned with nest settings.",
CutPinStatus.OnSeveralContours => $"Drawing pin {pin} touches more than one contour; planned with nest settings.",
CutPinStatus.ContourPinnedTwice => $"Drawing pin {pin} shares its contour with another pin, so neither is used; "
+ "planned with nest settings.",
_ => $"Drawing pin {pin} cannot be placed on this drawing's program; planned with nest settings.",
};
}
private static CuttingPlanFinding Warning(Part source, int ordinal, CutPinResolution resolution, string message) =>
new(ordinal, source, null, null, PostVerificationKind.FixedLead, message)
{
FixedLead = new(null, resolution.PinIndex + 1, null)
};
}