mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 10:15:07 -05:00
On an enclosed machine a cutout slug that tips once cut free can stand up above the sheet and catch the enclosure door when the pallet changes. The head already keeps off cut cutouts, so this is about clearing the door, not the head. Cutting Settings > Cutouts > "Slice slugs the support grid can't hold level" (CuttingParameters.SliceTippingSlugs, off by default, saved per machine thickness and written only when on) makes Plan Cutting cut each such slug along the bed's contact lines (grid rows and columns, slat center lines) before the cutout, so no piece keeps a contact and the pieces drop between the supports. Each slice stops a kerf short of the cutout, so the pieces stay attached until the cutout's own kerf frees them together. A slug the bed holds level, or one on no contact, is left whole; a cutout holding another part or another of its part's cuts is never sliced. Slices are break cuts emitted right after the drawing's own break cuts, pierced on themselves and led straight back to their start. The released-contour proof now leaves out straight break cuts lying wholly inside one of the part's closed holes, since the grid cells they free are scrap; break cuts in material still take part. The plan fingerprint only changes when the setting is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
309 lines
12 KiB
C#
309 lines
12 KiB
C#
using System;
|
|
using System.Text.Json;
|
|
using OpenNest.CNC.CuttingStrategy;
|
|
using static OpenNest.IO.CuttingParametersDto;
|
|
|
|
namespace OpenNest.IO;
|
|
|
|
public static class CuttingParametersSerializer
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
WriteIndented = false,
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
};
|
|
|
|
/// <summary>Writes desktop settings, retaining the legacy null-tab width fallback.</summary>
|
|
public static string Serialize(CuttingParameters parameters)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(parameters);
|
|
var dto = ToDto(parameters);
|
|
if (parameters.TabConfig == null)
|
|
dto.TabConfig = null;
|
|
return JsonSerializer.Serialize(dto, JsonOptions);
|
|
}
|
|
|
|
/// <summary>Reads desktop settings with their historical defaults and angle fallback.</summary>
|
|
public static CuttingParameters Deserialize(string json)
|
|
{
|
|
var dto = JsonSerializer.Deserialize<CuttingParametersDto>(json, JsonOptions);
|
|
var parameters = FromDto(dto) ?? new CuttingParameters();
|
|
if (parameters.LeadInAngleIncrement <= 0)
|
|
parameters.LeadInAngleIncrement = 5.0;
|
|
return parameters;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads settings a caller supplied explicitly (a file or tool argument). A JSON null is
|
|
/// refused rather than read as the defaults, so missing settings are never invented.
|
|
/// </summary>
|
|
public static CuttingParameters DeserializeExplicit(string json)
|
|
{
|
|
if (JsonSerializer.Deserialize<CuttingParametersDto>(json, JsonOptions) == null)
|
|
throw new JsonException("Cutting settings must be a JSON object.");
|
|
return Deserialize(json);
|
|
}
|
|
|
|
/// <summary>Captures an owned snapshot. Null stays null for absent plate parameters.</summary>
|
|
public static CuttingParametersDto ToDto(CuttingParameters parameters)
|
|
{
|
|
if (parameters == null)
|
|
return null;
|
|
|
|
return new CuttingParametersDto
|
|
{
|
|
Id = parameters.Id,
|
|
MachineName = parameters.MachineName,
|
|
MaterialName = parameters.MaterialName,
|
|
Grade = parameters.Grade,
|
|
Thickness = parameters.Thickness,
|
|
Kerf = parameters.Kerf,
|
|
PartSpacing = parameters.PartSpacing,
|
|
ExternalLeadIn = ToLeadInDto(parameters.ExternalLeadIn),
|
|
ExternalLeadOut = ToLeadOutDto(parameters.ExternalLeadOut),
|
|
InternalLeadIn = ToLeadInDto(parameters.InternalLeadIn),
|
|
InternalLeadOut = ToLeadOutDto(parameters.InternalLeadOut),
|
|
TabsEnabled = parameters.TabsEnabled,
|
|
TabWidth = parameters.TabConfig?.Size ?? 0.25,
|
|
TabConfig = ToTabDto(parameters.TabConfig),
|
|
PierceClearance = parameters.PierceClearance,
|
|
PierceDiameter = parameters.PierceDiameter,
|
|
RoundLeadInAngles = parameters.RoundLeadInAngles,
|
|
LeadInAngleIncrement = parameters.LeadInAngleIncrement,
|
|
SupportTabs = parameters.SupportTabs,
|
|
TabBySize = parameters.TabBySize,
|
|
NoBedTabMinWidth = parameters.TabSizeMinWidth,
|
|
NoBedTabMaxWidth = parameters.TabSizeMaxWidth,
|
|
TabHeldMaxLength = parameters.TabHeldMaxLength,
|
|
SliceTippingSlugs = parameters.SliceTippingSlugs,
|
|
Sequencing = ToSequenceDto(parameters.Sequencing),
|
|
Assignment = ToAssignmentDto(parameters.Assignment),
|
|
};
|
|
}
|
|
|
|
/// <summary>Restores an owned snapshot without the desktop settings' value normalization.</summary>
|
|
public static CuttingParameters FromDto(CuttingParametersDto dto)
|
|
{
|
|
if (dto == null)
|
|
return null;
|
|
|
|
return new CuttingParameters
|
|
{
|
|
Id = dto.Id,
|
|
MachineName = dto.MachineName,
|
|
MaterialName = dto.MaterialName,
|
|
Grade = dto.Grade,
|
|
Thickness = dto.Thickness,
|
|
Kerf = dto.Kerf,
|
|
PartSpacing = dto.PartSpacing,
|
|
ExternalLeadIn = FromLeadInDto(dto.ExternalLeadIn),
|
|
ExternalLeadOut = FromLeadOutDto(dto.ExternalLeadOut),
|
|
InternalLeadIn = FromLeadInDto(dto.InternalLeadIn),
|
|
InternalLeadOut = FromLeadOutDto(dto.InternalLeadOut),
|
|
TabsEnabled = dto.TabsEnabled,
|
|
TabConfig = dto.TabConfig == null
|
|
? new NormalTab { Size = dto.TabWidth }
|
|
: FromTabDto(dto.TabConfig),
|
|
PierceClearance = dto.PierceClearance,
|
|
PierceDiameter = dto.PierceDiameter,
|
|
RoundLeadInAngles = dto.RoundLeadInAngles,
|
|
LeadInAngleIncrement = dto.LeadInAngleIncrement,
|
|
SupportTabs = dto.SupportTabs,
|
|
TabBySize = dto.TabBySize,
|
|
TabSizeMinWidth = dto.NoBedTabMinWidth,
|
|
TabSizeMaxWidth = dto.NoBedTabMaxWidth,
|
|
TabHeldMaxLength = dto.TabHeldMaxLength,
|
|
SliceTippingSlugs = dto.SliceTippingSlugs,
|
|
Sequencing = FromSequenceDto(dto.Sequencing),
|
|
Assignment = FromAssignmentDto(dto.Assignment),
|
|
};
|
|
}
|
|
|
|
internal static LeadInDto ToLeadInDto(LeadIn leadIn) => leadIn switch
|
|
{
|
|
LineLeadIn line => new LeadInDto
|
|
{
|
|
Type = "Line",
|
|
Length = line.Length,
|
|
ApproachAngle = line.ApproachAngle,
|
|
},
|
|
ArcLeadIn arc => new LeadInDto { Type = "Arc", Radius = arc.Radius, SweepAngle = arc.SweepAngle },
|
|
LineArcLeadIn lineArc => new LeadInDto
|
|
{
|
|
Type = "LineArc",
|
|
LineLength = lineArc.LineLength,
|
|
ArcRadius = lineArc.ArcRadius,
|
|
ApproachAngle = lineArc.ApproachAngle,
|
|
},
|
|
CleanHoleLeadIn cleanHole => new LeadInDto
|
|
{
|
|
Type = "CleanHole",
|
|
LineLength = cleanHole.LineLength,
|
|
ArcRadius = cleanHole.ArcRadius,
|
|
Kerf = cleanHole.Kerf,
|
|
},
|
|
LineLineLeadIn lineLine => new LeadInDto
|
|
{
|
|
Type = "LineLine",
|
|
Length1 = lineLine.Length1,
|
|
Angle1 = lineLine.ApproachAngle1,
|
|
Length2 = lineLine.Length2,
|
|
Angle2 = lineLine.ApproachAngle2,
|
|
},
|
|
_ => new LeadInDto { Type = "None" },
|
|
};
|
|
|
|
internal static LeadIn FromLeadInDto(LeadInDto dto) => dto?.Type switch
|
|
{
|
|
"Line" => new LineLeadIn { Length = dto.Length, ApproachAngle = dto.ApproachAngle },
|
|
"Arc" => new ArcLeadIn { Radius = dto.Radius, SweepAngle = dto.SweepAngle },
|
|
"LineArc" => new LineArcLeadIn
|
|
{
|
|
LineLength = dto.LineLength,
|
|
ArcRadius = dto.ArcRadius,
|
|
ApproachAngle = dto.ApproachAngle,
|
|
},
|
|
"CleanHole" => new CleanHoleLeadIn
|
|
{
|
|
LineLength = dto.LineLength,
|
|
ArcRadius = dto.ArcRadius,
|
|
Kerf = dto.Kerf,
|
|
},
|
|
"LineLine" => new LineLineLeadIn
|
|
{
|
|
Length1 = dto.Length1,
|
|
ApproachAngle1 = dto.Angle1,
|
|
Length2 = dto.Length2,
|
|
ApproachAngle2 = dto.Angle2,
|
|
},
|
|
_ => new NoLeadIn(),
|
|
};
|
|
|
|
internal static LeadOutDto ToLeadOutDto(LeadOut leadOut) => leadOut switch
|
|
{
|
|
LineLeadOut line => new LeadOutDto
|
|
{
|
|
Type = "Line",
|
|
Length = line.Length,
|
|
ApproachAngle = line.ApproachAngle,
|
|
},
|
|
ArcLeadOut arc => new LeadOutDto { Type = "Arc", Radius = arc.Radius },
|
|
_ => new LeadOutDto { Type = "None" },
|
|
};
|
|
|
|
internal static LeadOut FromLeadOutDto(LeadOutDto dto) => dto?.Type switch
|
|
{
|
|
"Line" => new LineLeadOut { Length = dto.Length, ApproachAngle = dto.ApproachAngle },
|
|
"Arc" => new ArcLeadOut { Radius = dto.Radius },
|
|
_ => new NoLeadOut(),
|
|
};
|
|
|
|
internal static TabDto ToTabDto(Tab tab)
|
|
{
|
|
var dto = tab switch
|
|
{
|
|
NormalTab normal => new TabDto
|
|
{
|
|
Type = "Normal",
|
|
CutoutMinWidth = normal.CutoutMinWidth,
|
|
CutoutMinHeight = normal.CutoutMinHeight,
|
|
CutoutMaxWidth = normal.CutoutMaxWidth,
|
|
CutoutMaxHeight = normal.CutoutMaxHeight,
|
|
},
|
|
MachineTab machine => new TabDto { Type = "Machine", MachineTabId = machine.MachineTabId },
|
|
BreakerTab breaker => new TabDto
|
|
{
|
|
Type = "Breaker",
|
|
BreakerDepth = breaker.BreakerDepth,
|
|
BreakerLeadInLength = breaker.BreakerLeadInLength,
|
|
BreakerAngle = breaker.BreakerAngle,
|
|
},
|
|
_ => new TabDto { Type = "None" },
|
|
};
|
|
if (tab != null)
|
|
{
|
|
dto.Size = tab.Size;
|
|
dto.TabLeadIn = tab.TabLeadIn == null ? null : ToLeadInDto(tab.TabLeadIn);
|
|
dto.TabLeadOut = tab.TabLeadOut == null ? null : ToLeadOutDto(tab.TabLeadOut);
|
|
}
|
|
return dto;
|
|
}
|
|
|
|
internal static Tab FromTabDto(TabDto dto)
|
|
{
|
|
var tab = dto.Type switch
|
|
{
|
|
"Normal" => (Tab)new NormalTab
|
|
{
|
|
CutoutMinWidth = dto.CutoutMinWidth,
|
|
CutoutMinHeight = dto.CutoutMinHeight,
|
|
CutoutMaxWidth = dto.CutoutMaxWidth,
|
|
CutoutMaxHeight = dto.CutoutMaxHeight,
|
|
},
|
|
"Machine" => new MachineTab { MachineTabId = dto.MachineTabId },
|
|
"Breaker" => new BreakerTab
|
|
{
|
|
BreakerDepth = dto.BreakerDepth,
|
|
BreakerLeadInLength = dto.BreakerLeadInLength,
|
|
BreakerAngle = dto.BreakerAngle,
|
|
},
|
|
_ => null,
|
|
};
|
|
if (tab != null)
|
|
{
|
|
tab.Size = dto.Size;
|
|
tab.TabLeadIn = dto.TabLeadIn == null ? null : FromLeadInDto(dto.TabLeadIn);
|
|
tab.TabLeadOut = dto.TabLeadOut == null ? null : FromLeadOutDto(dto.TabLeadOut);
|
|
}
|
|
return tab;
|
|
}
|
|
|
|
private static SequenceDto ToSequenceDto(SequenceParameters parameters) => parameters == null
|
|
? null
|
|
: new SequenceDto
|
|
{
|
|
Method = parameters.Method,
|
|
SmallCutoutWidth = parameters.SmallCutoutWidth,
|
|
SmallCutoutHeight = parameters.SmallCutoutHeight,
|
|
MediumCutoutWidth = parameters.MediumCutoutWidth,
|
|
MediumCutoutHeight = parameters.MediumCutoutHeight,
|
|
DistanceMediumSmall = parameters.DistanceMediumSmall,
|
|
AlternateRowsColumns = parameters.AlternateRowsColumns,
|
|
AlternateCutoutsWithinRowColumn = parameters.AlternateCutoutsWithinRowColumn,
|
|
MinDistanceBetweenRowsColumns = parameters.MinDistanceBetweenRowsColumns,
|
|
};
|
|
|
|
private static SequenceParameters FromSequenceDto(SequenceDto dto) => dto == null
|
|
? null
|
|
: new SequenceParameters
|
|
{
|
|
Method = dto.Method,
|
|
SmallCutoutWidth = dto.SmallCutoutWidth,
|
|
SmallCutoutHeight = dto.SmallCutoutHeight,
|
|
MediumCutoutWidth = dto.MediumCutoutWidth,
|
|
MediumCutoutHeight = dto.MediumCutoutHeight,
|
|
DistanceMediumSmall = dto.DistanceMediumSmall,
|
|
AlternateRowsColumns = dto.AlternateRowsColumns,
|
|
AlternateCutoutsWithinRowColumn = dto.AlternateCutoutsWithinRowColumn,
|
|
MinDistanceBetweenRowsColumns = dto.MinDistanceBetweenRowsColumns,
|
|
};
|
|
|
|
private static AssignmentDto ToAssignmentDto(AssignmentParameters parameters) => parameters == null
|
|
? null
|
|
: new AssignmentDto
|
|
{
|
|
Method = parameters.Method,
|
|
Preference = parameters.Preference,
|
|
MinGeometryLength = parameters.MinGeometryLength,
|
|
};
|
|
|
|
private static AssignmentParameters FromAssignmentDto(AssignmentDto dto) => dto == null
|
|
? null
|
|
: new AssignmentParameters
|
|
{
|
|
Method = dto.Method,
|
|
Preference = dto.Preference,
|
|
MinGeometryLength = dto.MinGeometryLength,
|
|
};
|
|
}
|