mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-10 16:44:13 -04:00
Build paths in the drawing-local frame and translate both ordinary and split cut/lead paths once, without changing CNC programs. Add native Windows regressions using the operator's synthetic G90 fixture and incremental controls. Four absolute-mode cases fail before the fix; all seven pass after.
156 lines
6.3 KiB
C#
156 lines
6.3 KiB
C#
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Controls;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.IO;
|
|
|
|
namespace OpenNest.WinForms.Tests;
|
|
|
|
public class GraphicsHelperPlacementTests
|
|
{
|
|
[Theory]
|
|
[InlineData(Mode.Absolute, false)]
|
|
[InlineData(Mode.Absolute, true)]
|
|
[InlineData(Mode.Incremental, false)]
|
|
[InlineData(Mode.Incremental, true)]
|
|
public void PlacementTranslatesEveryOutlineAndLeadPointWithoutChangingProgram(Mode mode, bool splitPaths)
|
|
{
|
|
var program = new CNC.Program(Mode.Absolute);
|
|
program.Codes.AddRange(new ICode[]
|
|
{
|
|
new RapidMove(-1, 0),
|
|
new LinearMove(0, 0) { Layer = LayerType.Leadin },
|
|
new LinearMove(0, 4),
|
|
new LinearMove(3, 4),
|
|
new ArcMove(4, 3, 3, 3, RotationType.CW),
|
|
new LinearMove(4, 0),
|
|
new LinearMove(0, 0),
|
|
new ArcMove(-1, -1, -1, 0, RotationType.CW) { Layer = LayerType.Leadout },
|
|
new RapidMove(3, 2),
|
|
new ArcMove(3, 2, 2, 2),
|
|
});
|
|
program.Mode = mode;
|
|
var before = NestWriter.GetProgramText(program);
|
|
var instructions = program.Codes.ToArray();
|
|
using var local = program.GetGraphicsPath();
|
|
program.GetGraphicsPaths(Vector.Zero, out var localCut, out var localLead);
|
|
using (localCut)
|
|
using (localLead)
|
|
{
|
|
Assert.True(local.PointCount > 0);
|
|
Assert.True(localCut.PointCount > 0);
|
|
Assert.True(localLead.PointCount > 0);
|
|
foreach (var origin in new[] { new Vector(10.25, 6.5), new Vector(-7.5, 12.25), Vector.Zero })
|
|
{
|
|
if (!splitPaths)
|
|
{
|
|
using var placed = program.GetGraphicsPath(origin);
|
|
AssertTranslated(local, placed, origin);
|
|
}
|
|
else
|
|
{
|
|
program.GetGraphicsPaths(origin, out var cut, out var lead);
|
|
using (cut)
|
|
using (lead)
|
|
{
|
|
AssertTranslated(localCut, cut, origin);
|
|
AssertTranslated(localLead, lead, origin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Assert.Equal(before, NestWriter.GetProgramText(program));
|
|
Assert.Equal(instructions, program.Codes);
|
|
Assert.Equal(mode, program.Mode);
|
|
}
|
|
|
|
[Fact]
|
|
public void IncrementalSubprogramOffsetsStayRelativeToProgramOrigin()
|
|
{
|
|
var hole = new CNC.Program(Mode.Incremental);
|
|
hole.Codes.AddRange(new ICode[]
|
|
{
|
|
new RapidMove(1, 0),
|
|
new ArcMove(0, 0, -1, 0),
|
|
});
|
|
var program = new CNC.Program(Mode.Incremental);
|
|
program.Codes.Add(new SubProgramCall(hole, 0) { Offset = new Vector(3, 4) });
|
|
program.Codes.Add(new SubProgramCall(hole, 0) { Offset = new Vector(9, 4) });
|
|
var before = NestWriter.GetSubProgramsText(program);
|
|
using var local = program.GetGraphicsPath();
|
|
using var placed = program.GetGraphicsPath(new Vector(20, 30));
|
|
AssertTranslated(local, placed, new Vector(20, 30));
|
|
Assert.Equal(new RectangleF(22, 33, 8, 2), placed.GetBounds());
|
|
program.GetGraphicsPaths(new Vector(20, 30), out var cut, out var lead);
|
|
using (cut)
|
|
using (lead)
|
|
{
|
|
AssertTranslated(local, cut, new Vector(20, 30));
|
|
Assert.Equal(0, lead.PointCount);
|
|
}
|
|
Assert.Same(hole, ((SubProgramCall)program.Codes[0]).Program);
|
|
Assert.Same(hole, ((SubProgramCall)program.Codes[1]).Program);
|
|
Assert.Equal(before, NestWriter.GetSubProgramsText(program));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void SavedAbsolutePartsFollowRepeatedMovesAndViewTransform(bool splitPaths) => StaTestThread.Run(() =>
|
|
{
|
|
var fixture = Path.Combine(AppContext.BaseDirectory, "Fixtures", "absolute-coordinate-parts.nest");
|
|
var nest = new NestReader(fixture).Read();
|
|
Assert.Equal(2, nest.Plates.Count);
|
|
Assert.Equal(3, nest.Plates.Sum(plate => plate.Parts.Count));
|
|
using var view = new PlateView();
|
|
view.Matrix.Reset();
|
|
view.Matrix.Scale(2, -2);
|
|
view.Matrix.Translate(40, 80, MatrixOrder.Append);
|
|
foreach (var part in nest.Plates.SelectMany(plate => plate.Parts))
|
|
{
|
|
Assert.Equal(Mode.Absolute, part.Program.Mode);
|
|
part.HasManualLeadIns = splitPaths;
|
|
var before = NestWriter.GetProgramText(part.Program);
|
|
var initial = part.Location;
|
|
var layout = LayoutPart.Create(part, view);
|
|
try
|
|
{
|
|
foreach (var location in new[] { initial, new Vector(4.25, 10.75), new Vector(15.5, 3.25), initial })
|
|
{
|
|
part.Location = location;
|
|
layout.Path.Dispose();
|
|
layout.Update(view);
|
|
using var expected = part.Program.GetGraphicsPath();
|
|
using var translation = new Matrix();
|
|
translation.Translate((float)location.X, (float)location.Y);
|
|
expected.Transform(translation);
|
|
expected.Transform(view.Matrix);
|
|
AssertTranslated(expected, layout.Path, Vector.Zero);
|
|
if (splitPaths)
|
|
Assert.Equal(0, layout.LeadInPath.PointCount);
|
|
Assert.Equal(before, NestWriter.GetProgramText(part.Program));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
layout.Path.Dispose();
|
|
layout.LeadInPath?.Dispose();
|
|
}
|
|
}
|
|
}, TimeSpan.FromMinutes(1), "Absolute-coordinate placement test did not complete.");
|
|
|
|
private static void AssertTranslated(GraphicsPath local, GraphicsPath placed, Vector offset)
|
|
{
|
|
Assert.Equal(local.PointCount, placed.PointCount);
|
|
Assert.Equal(local.PathTypes, placed.PathTypes);
|
|
var expected = local.PathPoints;
|
|
var actual = placed.PathPoints;
|
|
for (var i = 0; i < expected.Length; i++)
|
|
{
|
|
Assert.InRange(actual[i].X - (expected[i].X + offset.X), -0.00001, 0.00001);
|
|
Assert.InRange(actual[i].Y - (expected[i].Y + offset.Y), -0.00001, 0.00001);
|
|
}
|
|
}
|
|
}
|