mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-03 17:01:38 -05:00
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>
139 lines
5.2 KiB
C#
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.");
|
|
}
|