ConvertMode.ToIncremental skipped SubProgramCall codes entirely when computing deltas, so parent motions after a sub-call were encoded as if the tool never moved. Several traversal sites (ConvertProgram, GraphicsHelper, PlateRenderer, CutDirectionArrows, Program.BoundingBox) worked around this with save/restore hacks that treated sub-calls as transparent — but DrawRapids legitimately tracks actual tool position, so after the last hole the first perimeter rapid was applied to the wrong base, drifting the rendered perimeter past the plate edge by roughly the distance to the last hole. Fix the root cause: ToIncremental and ToAbsolute now walk sub-programs to compute where they leave the tool, and advance pos accordingly. The other traversals capture a frameOrigin at entry and compute sub-call placement as frameOrigin + Offset, letting pos advance naturally through the sub recursion. All the save/restore workarounds are removed. Program.BoundingBox also picks up the same frame-origin treatment, which corrects a latent bug where absolute-mode endpoints and nested sub-calls dropped the parent's frame origin. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
147 lines
4.7 KiB
C#
147 lines
4.7 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Math;
|
|
using System.Collections.Generic;
|
|
|
|
namespace OpenNest.Converters
|
|
{
|
|
public static class ConvertProgram
|
|
{
|
|
public static List<Entity> ToGeometry(Program pgm)
|
|
{
|
|
var geometry = new List<Entity>();
|
|
var curpos = new Vector();
|
|
var mode = Mode.Absolute;
|
|
|
|
AddProgram(pgm, ref mode, ref curpos, ref geometry);
|
|
|
|
return geometry;
|
|
}
|
|
|
|
private static void AddProgram(Program program, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
|
{
|
|
// Capture the frame origin at entry. Sub-program Offsets are relative
|
|
// to this fixed origin, not to the current tool position.
|
|
var frameOrigin = curpos;
|
|
mode = program.Mode;
|
|
|
|
for (int i = 0; i < program.Length; ++i)
|
|
{
|
|
var code = program[i];
|
|
|
|
switch (code.Type)
|
|
{
|
|
case CodeType.ArcMove:
|
|
AddArcMove((ArcMove)code, ref mode, ref curpos, ref geometry);
|
|
break;
|
|
|
|
case CodeType.LinearMove:
|
|
AddLinearMove((LinearMove)code, ref mode, ref curpos, ref geometry);
|
|
break;
|
|
|
|
case CodeType.RapidMove:
|
|
AddRapidMove((RapidMove)code, ref mode, ref curpos, ref geometry);
|
|
break;
|
|
|
|
case CodeType.SubProgramCall:
|
|
var subpgm = (SubProgramCall)code;
|
|
var savedMode = mode;
|
|
|
|
// The sub-program's frame origin in this program's frame is
|
|
// frameOrigin + Offset — independent of current tool position.
|
|
curpos = new Vector(frameOrigin.X + subpgm.Offset.X, frameOrigin.Y + subpgm.Offset.Y);
|
|
|
|
AddProgram(subpgm.Program, ref mode, ref curpos, ref geometry);
|
|
mode = savedMode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void AddLinearMove(LinearMove linearMove, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
|
{
|
|
var pt = linearMove.EndPoint;
|
|
|
|
if (mode == Mode.Incremental)
|
|
pt += curpos;
|
|
|
|
var layer = ConvertLayer(linearMove.Layer);
|
|
var line = new Line(curpos, pt)
|
|
{
|
|
Layer = layer,
|
|
Color = layer.Color
|
|
};
|
|
geometry.Add(line);
|
|
curpos = pt;
|
|
}
|
|
|
|
private static void AddRapidMove(RapidMove rapidMove, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
|
{
|
|
var pt = rapidMove.EndPoint;
|
|
|
|
if (mode == Mode.Incremental)
|
|
pt += curpos;
|
|
|
|
var line = new Line(curpos, pt)
|
|
{
|
|
Layer = SpecialLayers.Rapid,
|
|
Color = SpecialLayers.Rapid.Color
|
|
};
|
|
geometry.Add(line);
|
|
curpos = pt;
|
|
}
|
|
|
|
private static void AddArcMove(ArcMove arcMove, ref Mode mode, ref Vector curpos, ref List<Entity> geometry)
|
|
{
|
|
var center = arcMove.CenterPoint;
|
|
var endpt = arcMove.EndPoint;
|
|
|
|
if (mode == Mode.Incremental)
|
|
{
|
|
endpt += curpos;
|
|
center += curpos;
|
|
}
|
|
|
|
var startAngle = center.AngleTo(curpos);
|
|
var endAngle = center.AngleTo(endpt);
|
|
|
|
var dx = endpt.X - center.X;
|
|
var dy = endpt.Y - center.Y;
|
|
|
|
var radius = System.Math.Sqrt(dx * dx + dy * dy);
|
|
var layer = ConvertLayer(arcMove.Layer);
|
|
|
|
if (startAngle.IsEqualTo(endAngle))
|
|
geometry.Add(new Circle(center, radius) { Layer = layer, Color = layer.Color, Rotation = arcMove.Rotation });
|
|
else
|
|
geometry.Add(new Arc(center, radius, startAngle, endAngle, arcMove.Rotation == RotationType.CW) { Layer = layer, Color = layer.Color });
|
|
|
|
curpos = endpt;
|
|
}
|
|
|
|
private static Layer ConvertLayer(LayerType layer)
|
|
{
|
|
switch (layer)
|
|
{
|
|
case LayerType.Cut:
|
|
return SpecialLayers.Cut;
|
|
|
|
case LayerType.Display:
|
|
return SpecialLayers.Display;
|
|
|
|
case LayerType.Leadin:
|
|
return SpecialLayers.Leadin;
|
|
|
|
case LayerType.Leadout:
|
|
return SpecialLayers.Leadout;
|
|
|
|
case LayerType.Scribe:
|
|
return SpecialLayers.Scribe;
|
|
|
|
default:
|
|
return new Layer(layer.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|