Files
OpenNest/OpenNest.IO/CutPinFormat.cs
T
ajandClaude Opus 5.5 0f06748f28 feat(io): persist pins and pins-off
Drawing cut pins and the per-placement pins-off flag now survive
save/reload and travel in drawing packages.

- `DrawingDto.cutPins` reuses the plate cutting-parameter lead and tab
  DTOs. Pins are read strictly per entry: an unknown lead or tab type is
  never replaced by a default, because a pin that cut differently from
  what was saved is worse than no pin. A bad entry is skipped with a
  reader warning naming the drawing and pin, and the rest load; a value
  that is not a list loads no pins, with a warning. A nest never fails to
  open over pins.
- `PartDto.drawingPinsOff` is written only when true. Anything other than
  a boolean loads as pins on (the drawing wins, D1) with a warning naming
  the plate and part, so an operator's override is never lost silently.
- Packages carry pins as drawing content, so pinning a drawing changes
  its package hash and the newer-revision notice can tell geometry/pins
  changes from detail-only ones.
- `Part.DrawingPinsOff` holds the flag; nothing plans with it yet.

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

72 lines
2.9 KiB
C#

using System;
using System.Collections.Generic;
using OpenNest.CNC.CuttingStrategy;
using OpenNest.Geometry;
using static OpenNest.IO.CuttingParametersDto;
using static OpenNest.IO.NestFormat;
namespace OpenNest.IO;
/// <summary>
/// Maps drawing cut pins to and from the nest file. Reading is strict per pin: unlike desktop
/// settings, an unknown lead or tab type is not replaced by a default, because a pin that cut
/// differently from what was saved would be worse than no pin.
/// </summary>
internal static class CutPinFormat
{
private static readonly HashSet<string> LeadInTypes = new(StringComparer.Ordinal)
{ "None", "Line", "Arc", "LineArc", "CleanHole", "LineLine" };
private static readonly HashSet<string> LeadOutTypes = new(StringComparer.Ordinal) { "None", "Line", "Arc" };
private static readonly HashSet<string> TabTypes = new(StringComparer.Ordinal) { "Normal", "Machine", "Breaker" };
internal static CutPinDto ToDto(ContourCutPin pin) => new()
{
X = pin.Point.X,
Y = pin.Point.Y,
LeadIn = CuttingParametersSerializer.ToLeadInDto(pin.LeadIn),
LeadOut = CuttingParametersSerializer.ToLeadOutDto(pin.LeadOut),
Tab = pin.HasTab ? CuttingParametersSerializer.ToTabDto(pin.Tab) : null,
};
/// <summary>Builds the pin, or returns null with the reason it cannot be trusted.</summary>
internal static ContourCutPin Read(CutPinDto dto, out string problem)
{
problem = null;
if (dto == null || dto.Malformed)
return Fail("it is not a readable pin", out problem);
if (!IsLeadIn(dto.LeadIn))
return Fail("its lead-in is missing or of an unknown type", out problem);
if (!IsLeadOut(dto.LeadOut))
return Fail("its lead-out is missing or of an unknown type", out problem);
if (dto.Tab != null && (!TabTypes.Contains(dto.Tab.Type ?? "")
|| dto.Tab.TabLeadIn != null && !IsLeadIn(dto.Tab.TabLeadIn)
|| dto.Tab.TabLeadOut != null && !IsLeadOut(dto.Tab.TabLeadOut)))
return Fail("its tab is of an unknown type", out problem);
try
{
return new ContourCutPin(
new Vector(dto.X, dto.Y),
CuttingParametersSerializer.FromLeadInDto(dto.LeadIn),
CuttingParametersSerializer.FromLeadOutDto(dto.LeadOut),
dto.Tab == null ? null : CuttingParametersSerializer.FromTabDto(dto.Tab));
}
catch (Exception ex) when (ex is ArgumentException || ex is NotSupportedException)
{
return Fail(ex.Message, out problem);
}
}
private static bool IsLeadIn(LeadInDto dto) => dto != null && LeadInTypes.Contains(dto.Type ?? "");
private static bool IsLeadOut(LeadOutDto dto) => dto != null && LeadOutTypes.Contains(dto.Type ?? "");
private static ContourCutPin Fail(string reason, out string problem)
{
problem = reason;
return null;
}
}