Files
OpenNest/OpenNest.WinForms.Tests/Controls/CutSimulationOverlayTests.cs
T
ajandClaude Opus 5.5 e5d9e12df9 test(desktop): share the square and rectangle part programs
Ten desktop test classes drew the same squares and the same 4 x 2
rectangle in private helpers, in two equivalent styles. They now use
TestPrograms.Square, ClockwiseSquare and Rectangle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:53:24 -05:00

139 lines
5.2 KiB
C#

using System.Drawing;
using OpenNest.CNC;
using OpenNest.Controls;
using OpenNest.Geometry;
namespace OpenNest.WinForms.Tests.Controls;
public class CutSimulationOverlayTests
{
private static readonly CutParameters Parameters = new()
{
Feedrate = 100,
RapidTravelRate = 600,
PierceTime = TimeSpan.FromSeconds(1),
Units = Units.Inches,
};
private static Plate TwoSquares()
{
var plate = new Plate(48, 96);
plate.Parts.Add(new Part(new Drawing("a", TestPrograms.Square(10)), new Vector(5, 5)));
plate.Parts.Add(new Part(new Drawing("b", TestPrograms.Square(10)), new Vector(25, 5)));
return plate;
}
[Fact]
public void PartsAheadOfTheTorchArePending() => RunSta(() =>
{
using var view = new PlateView { Plate = TwoSquares() };
var overlay = view.CutSimulation;
var sim = CutSimulation.Build(view.Plate, Parameters);
overlay.Load(sim);
Assert.True(overlay.IsPartPending(0)); // still traveling to the first part
var firstCut = sim.Steps.First(s => s.Kind == CutSimulationStepKind.Cut);
overlay.Time = firstCut.StartTime + firstCut.Duration / 2;
Assert.False(overlay.IsPartPending(0));
Assert.True(overlay.IsPartPending(1));
overlay.Time = sim.TotalTime;
Assert.False(overlay.IsPartPending(1));
});
[Fact]
public void FinishedCutsAreDrawnOnlyBehindThePlayhead() => RunSta(() =>
{
using var view = new PlateView { Plate = TwoSquares() };
var sim = CutSimulation.Build(view.Plate, Parameters);
view.CutSimulation.Load(sim);
var secondPartCut = sim.Steps.First(s => s.PartIndex == 1 && s.Kind == CutSimulationStepKind.Cut);
view.CutSimulation.Time = secondPartCut.StartTime;
using var bitmap = new Bitmap(400, 400);
using var graphics = Graphics.FromImage(bitmap);
graphics.TranslateTransform(20, 380);
graphics.Clear(Color.Black);
view.CutSimulation.Draw(graphics);
// Bottom edge midpoints of each square, in graph coordinates.
var cut = Pixel(view, bitmap, new Vector(10, 5));
var uncut = Pixel(view, bitmap, new Vector(35, 15));
Assert.True(cut.R > 200 && cut.G < 160, $"first part trail missing: {cut}");
Assert.Equal(Color.Black.ToArgb(), uncut.ToArgb());
});
[Fact]
public void ZoomedInTrailIsDrawnWherePannedTo() => RunSta(() =>
{
using var view = new PlateView { Plate = TwoSquares() };
var sim = CutSimulation.Build(view.Plate, Parameters);
view.CutSimulation.Load(sim);
view.CutSimulation.Time = sim.TotalTime;
view.ZoomToPoint(new Vector(10, 5), 100 / view.ViewScale, false); // trail far larger than the screen
using var bitmap = new Bitmap(400, 400);
using var graphics = Graphics.FromImage(bitmap);
foreach (var target in new[] { new Vector(10, 5), new Vector(35, 15) })
{
var point = view.PointWorldToGraph(target);
graphics.ResetTransform();
graphics.Clear(Color.Black);
graphics.TranslateTransform(200 - point.X, 200 - point.Y);
view.CutSimulation.Draw(graphics);
var pixel = bitmap.GetPixel(200, 200);
Assert.True(pixel.R > 200 && pixel.G < 160, $"trail missing at {target}: {pixel}");
}
});
[Fact]
public void RetimingKeepsTheTorchWhereItWas() => RunSta(() =>
{
using var view = new PlateView { Plate = TwoSquares() };
var overlay = view.CutSimulation;
var slow = CutSimulation.Build(view.Plate, Parameters);
overlay.Load(slow);
var cut = slow.Steps.Last(s => s.Kind == CutSimulationStepKind.Cut);
overlay.Time = cut.StartTime + cut.Duration * 0.4;
var before = slow.ToolPositionAt(overlay.Time);
var fast = CutSimulation.Build(view.Plate, new CutParameters
{
Feedrate = 100,
RapidTravelRate = 6000,
PierceTime = TimeSpan.FromSeconds(1),
Units = Units.Inches,
});
overlay.Load(fast);
Assert.True(overlay.Time < cut.StartTime); // faster rapids moved it earlier
var after = fast.ToolPositionAt(overlay.Time);
Assert.Equal(before.X, after.X, 6);
Assert.Equal(before.Y, after.Y, 6);
});
[Fact]
public void PlateSwitchClearsTheSimulation() => RunSta(() =>
{
using var view = new PlateView { Plate = TwoSquares() };
view.CutSimulation.Load(CutSimulation.Build(view.Plate, Parameters));
view.CutSimulation.Play();
Assert.True(view.CutSimulation.IsPlaying);
view.Plate = new Plate();
Assert.False(view.CutSimulation.IsActive);
Assert.False(view.CutSimulation.IsPlaying);
});
private static Color Pixel(PlateView view, Bitmap bitmap, Vector world)
{
var point = view.PointWorldToGraph(world);
return bitmap.GetPixel((int)System.Math.Round(point.X + 20), (int)System.Math.Round(point.Y + 380));
}
private static void RunSta(System.Action action) =>
StaTestThread.Run(action, TimeSpan.FromSeconds(30), "Cut simulation overlay test timed out.");
}