mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 15:01:07 -04:00
A display perimeter written without its own tag reloaded as an ordinary cut, so a saved blank would come back with its outline to be cut. Programs now carry it as :DISPLAYPERIMETER and the reader maps it back, and stored source entities keep the DISPLAY PERIMETER layer so editing the drawing rebuilds the same never-cut outline. Builds older than this read the unknown tag as a cut. Open nests with display perimeters only in builds that include this change.
201 lines
7.4 KiB
C#
201 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using OpenNest.Converters;
|
|
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,
|
|
IReadOnlyDictionary<Guid, EntityRole> roles = null
|
|
)
|
|
{
|
|
return new EntitySetDto
|
|
{
|
|
Entities = entities.Select(ToEntityDto).ToList(),
|
|
Suppressed = suppressed.Select(id => id.ToString()).ToList(),
|
|
Roles = roles is { Count: > 0 }
|
|
? roles.OrderBy(r => r.Key).ToDictionary(r => r.Key.ToString(), r => RoleName(r.Value))
|
|
: null,
|
|
};
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Role overrides of <paramref name="dto"/>. Unknown role names and malformed IDs are
|
|
/// skipped, so those entities keep their layer's role.
|
|
/// </summary>
|
|
public static Dictionary<Guid, EntityRole> RolesFromDto(EntitySetDto dto)
|
|
{
|
|
var roles = new Dictionary<Guid, EntityRole>();
|
|
if (dto.Roles == null)
|
|
return roles;
|
|
foreach (var (id, name) in dto.Roles)
|
|
{
|
|
if (Guid.TryParse(id, out var guid) && TryParseRole(name, out var role))
|
|
roles[guid] = role;
|
|
}
|
|
return roles;
|
|
}
|
|
|
|
private static string RoleName(EntityRole role) =>
|
|
role switch
|
|
{
|
|
EntityRole.Etch => "etch",
|
|
EntityRole.Display => "display",
|
|
_ => "cut",
|
|
};
|
|
|
|
private static bool TryParseRole(string name, out EntityRole role)
|
|
{
|
|
switch (name?.ToLowerInvariant())
|
|
{
|
|
case "cut":
|
|
role = EntityRole.Cut;
|
|
return true;
|
|
case "etch":
|
|
role = EntityRole.Etch;
|
|
return true;
|
|
case "display":
|
|
role = EntityRole.Display;
|
|
return true;
|
|
default:
|
|
role = EntityRole.Cut;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (string.Equals(name, SpecialLayers.DisplayPerimeter.Name, StringComparison.OrdinalIgnoreCase))
|
|
return SpecialLayers.DisplayPerimeter;
|
|
|
|
return new Layer(name);
|
|
}
|
|
}
|
|
}
|