mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-06 14:35:44 -05:00
N26-295's PT42 pairs saved spacing in slice 1 but every shared edge was still cut twice, costing a pass per edge and risking a freed part tipping under the head on the second pass. Plan Cutting now cuts a plate's common lines once unless the plate is set to cut them twice. Each group is cut member by member, owner before partner (a captured dependency that search and replay enforce, so a kept order cutting the partner first is a constraint conflict): the member's holes, then the shared cuts it owns as open CommonLine centerline cuts entered through the notch on the span's axis, then its remainder pieces, each ramped in from the kerf the shared cut opened. Capture marks the shared spans as tabs on the owned clean copy, so replay's contour accounting proves each perimeter is cut exactly once; a lead that fits at neither end refuses as UnsupportedGeometry naming the span, never shortened. Material still comes from the drawing, so utilization, quantity and overlap results do not change. Members are never support-tabbed; locked or excluded members and broken groups refuse. The CI Fiber post writes shared cuts in the cut-off form with a lead-in and no G41/G42, and remainders in the contour form on their perimeter's compensation side, named by a remainder comment. Cincinnati CL (whose PreApplied mode offsets nothing) and Gravograph refuse such nests, as do the desktop and console for any post without ICommonLinePostProcessor; nothing falls back to a silent double cut. Plate > Arrange > Cut Common Lines Twice keeps slice 1's behavior, persists with the plate and makes an open plan stale. Pre-post review now checks each span is cut once, on its centerline, by the member cut first, and the rapid check treats a finished member's remainders as an obstacle. Compensation start in void and cancel in kerf are unconfirmed on the CI FIBER 4020; release waits on a 3/16 stainless coupon trial. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
3.9 KiB
C#
72 lines
3.9 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,
|
|
CommonLinePerimeter commonLine = null)
|
|
{
|
|
var prepared = PreparedContours.Capture(ownedClean, parameters, token, commonLine);
|
|
// Pins switched off for this placement (D1's escape hatch) leave it to the plate settings.
|
|
if (!source.HasActiveDrawingPins)
|
|
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, commonLine);
|
|
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,
|
|
pinned.IsCommonLinePerimeter && rejected.ContourOrdinal == pinned.PerimeterOrdinal
|
|
? $"Drawing pin {rejected.PinIndex + 1} is on the outside, which a common line cuts from its "
|
|
+ "shared spans; planned with nest settings."
|
|
: $"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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A part with active pins whose program is kept: locked lead-ins override the pins (they
|
|
/// are kept exactly, never refused), and an unlocked part a caller left ineligible is not
|
|
/// regenerated at all.
|
|
/// </summary>
|
|
internal static CuttingPlanFinding NotApplied(Part source, int ordinal) =>
|
|
new(ordinal, source, null, null, PostVerificationKind.FixedLead, source.LeadInsLocked
|
|
? $"Locked lead-ins override drawing pins on part {ordinal + 1}; use Plate > Remove Lead-ins, then Plan Cutting, to apply the pins."
|
|
: $"Drawing pins on part {ordinal + 1} are not applied: the part is not regenerated.")
|
|
{ FixedLead = new(null, null, null) };
|
|
|
|
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)
|
|
};
|
|
}
|