mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-28 00:21:07 -04:00
A hole can now be declared a tabbed cutout in the CAD Converter: pick a circle or any edge of a closed hole, set the tab count and width, and drag the tabs around the hole. The definition persists on the drawing and in the nest file, and stays editable from the source entities. The builder replaces the hole with open cut pieces and uncut spans on a new Tab layer. Tabs are material, so area, nesting and overlap checks still see a closed hole, but they are never cut. Plan Cutting and Assign Lead-ins cut each piece from the slug with the internal lead-in and no lead-out. A lead that would reach back over a tab is mirrored, and an arc lead onto an arc pierces straight in. Planning accounts for each span between tabs and runs the open-cut proof, so its own tabbed cutouts certify; tabs followed by a lead-out are still refused. Pre-post review flags programs that still contain tab motions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
147 lines
5.5 KiB
C#
147 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using OpenNest.Geometry;
|
|
using static OpenNest.IO.NestFormat;
|
|
|
|
namespace OpenNest.IO
|
|
{
|
|
public static class EntitySerializer
|
|
{
|
|
public static EntitySetDto ToDto(List<Entity> entities, HashSet<Guid> suppressed)
|
|
{
|
|
return new EntitySetDto
|
|
{
|
|
Entities = entities.Select(ToEntityDto).ToList(),
|
|
Suppressed = suppressed.Select(id => id.ToString()).ToList(),
|
|
};
|
|
}
|
|
|
|
public static (List<Entity> entities, HashSet<Guid> suppressed) FromDto(EntitySetDto dto)
|
|
{
|
|
var entities = dto.Entities.Select(FromEntityDto).ToList();
|
|
var suppressed = new HashSet<Guid>(dto.Suppressed.Select(Guid.Parse));
|
|
return (entities, suppressed);
|
|
}
|
|
|
|
private static EntityDto ToEntityDto(Entity entity)
|
|
{
|
|
switch (entity.Type)
|
|
{
|
|
case EntityType.Line:
|
|
var line = (Line)entity;
|
|
return new EntityDto
|
|
{
|
|
Id = entity.Id.ToString(),
|
|
Type = "line",
|
|
Layer = entity.Layer?.Name ?? "",
|
|
LineType = entity.LineTypeName ?? "",
|
|
X1 = line.StartPoint.X,
|
|
Y1 = line.StartPoint.Y,
|
|
X2 = line.EndPoint.X,
|
|
Y2 = line.EndPoint.Y,
|
|
};
|
|
|
|
case EntityType.Arc:
|
|
var arc = (Arc)entity;
|
|
return new EntityDto
|
|
{
|
|
Id = entity.Id.ToString(),
|
|
Type = "arc",
|
|
Layer = entity.Layer?.Name ?? "",
|
|
LineType = entity.LineTypeName ?? "",
|
|
CX = arc.Center.X,
|
|
CY = arc.Center.Y,
|
|
R = arc.Radius,
|
|
StartAngle = arc.StartAngle,
|
|
EndAngle = arc.EndAngle,
|
|
Reversed = arc.IsReversed,
|
|
};
|
|
|
|
case EntityType.Circle:
|
|
var circle = (Circle)entity;
|
|
return new EntityDto
|
|
{
|
|
Id = entity.Id.ToString(),
|
|
Type = "circle",
|
|
Layer = entity.Layer?.Name ?? "",
|
|
LineType = entity.LineTypeName ?? "",
|
|
CX = circle.Center.X,
|
|
CY = circle.Center.Y,
|
|
R = circle.Radius,
|
|
Rotation = circle.Rotation == RotationType.CW ? "CW" : "CCW",
|
|
};
|
|
|
|
default:
|
|
throw new NotSupportedException(
|
|
$"Entity type {entity.Type} is not supported for serialization."
|
|
);
|
|
}
|
|
}
|
|
|
|
private static Entity FromEntityDto(EntityDto dto)
|
|
{
|
|
Entity entity;
|
|
|
|
switch (dto.Type)
|
|
{
|
|
case "line":
|
|
entity = new Line(new Vector(dto.X1, dto.Y1), new Vector(dto.X2, dto.Y2));
|
|
break;
|
|
|
|
case "arc":
|
|
entity = new Arc(
|
|
new Vector(dto.CX, dto.CY),
|
|
dto.R,
|
|
dto.StartAngle,
|
|
dto.EndAngle,
|
|
dto.Reversed
|
|
);
|
|
break;
|
|
|
|
case "circle":
|
|
var circle = new Circle(new Vector(dto.CX, dto.CY), dto.R);
|
|
circle.Rotation = dto.Rotation == "CW" ? RotationType.CW : RotationType.CCW;
|
|
entity = circle;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException(
|
|
$"Entity type '{dto.Type}' is not supported for deserialization."
|
|
);
|
|
}
|
|
|
|
entity.Id = Guid.Parse(dto.Id);
|
|
entity.Layer = ResolveLayer(dto.Layer);
|
|
entity.LineTypeName = dto.LineType;
|
|
|
|
return entity;
|
|
}
|
|
|
|
private static Layer ResolveLayer(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name) || name == "0")
|
|
return Layer.Default;
|
|
|
|
if (string.Equals(name, SpecialLayers.Cut.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Cut;
|
|
if (string.Equals(name, SpecialLayers.Rapid.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Rapid;
|
|
if (string.Equals(name, SpecialLayers.Display.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Display;
|
|
if (string.Equals(name, SpecialLayers.Leadin.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Leadin;
|
|
if (string.Equals(name, SpecialLayers.Leadout.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Leadout;
|
|
if (string.Equals(name, SpecialLayers.Scribe.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Scribe;
|
|
if (string.Equals(name, SpecialLayers.Break.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Break;
|
|
if (string.Equals(name, SpecialLayers.Tab.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.Tab;
|
|
|
|
return new Layer(name);
|
|
}
|
|
}
|
|
}
|