diff --git a/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs b/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs index aa42adbd..26049c85 100644 --- a/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs +++ b/OpenNest.WinForms.Tests/PlateOverlapOverlayTests.cs @@ -343,47 +343,41 @@ public class PlateOverlapOverlayTests }); [Fact] - public void ClearPlateDrawsAGreenCheckInsteadOfTheLabelButStatusKeepsTheWords() => RunSta(() => + public void ClearPlateDrawsNoLabelButStatusKeepsTheWords() => RunSta(() => { using var run = new OverlayRun(); run.View.Plate.Parts.RemoveAt(1); run.Finish(); - Assert.True(run.View.OverlapOverlay.ShowsAllClear); + Assert.True(run.View.OverlapOverlay.ShowsNoLabel); Assert.Equal("No material overlaps detected", run.View.Status); - Assert.True(CheckIsDrawn(run)); + Assert.True(CornerIsEmpty(run)); run.View.Plate.Parts.Add(Rectangle(1)); run.Finish(); - Assert.False(run.View.OverlapOverlay.ShowsAllClear); - Assert.False(CheckIsDrawn(run)); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); + Assert.False(CornerIsEmpty(run)); }); [Fact] - public void ClearPlateKeepsAGrayCheckWhileRechecking() => RunSta(() => + public void ClearPlateStaysEmptyWhileRechecking() => RunSta(() => { using var run = new OverlayRun(); run.View.Plate.Parts.RemoveAt(1); run.EnableAutoCheck(); var overlay = run.View.OverlapOverlay; + Assert.True(overlay.ShowsNoLabel); // Before the first result too. var task = run.AutoTick(); - run.Next().Complete(); + var work = run.Next(); + Assert.True(overlay.ShowsNoLabel); + work.Complete(); run.Pump(task); - Assert.True(overlay.ShowsAllClear); + Assert.True(overlay.ShowsNoLabel); run.View.Plate.Parts[0].Offset(1, 0); run.Paint(); Assert.True(overlay.IsAutoCheckScheduled); - Assert.True(overlay.ShowsPendingCheck); - Assert.False(overlay.ShowsAllClear); - Assert.False(CheckIsDrawn(run)); // Gray, not green: this layout is unconfirmed. - - task = run.AutoTick(); - var work = run.Next(); - Assert.True(overlay.ShowsPendingCheck); - work.Complete(); - run.Pump(task); - Assert.False(overlay.ShowsPendingCheck); - Assert.True(overlay.ShowsAllClear); + Assert.True(overlay.ShowsNoLabel); + Assert.True(CornerIsEmpty(run)); }); [Fact] @@ -397,37 +391,60 @@ public class PlateOverlapOverlayTests run.View.Plate.Parts[1].Offset(0.5, 0); run.Paint(); - Assert.False(run.View.OverlapOverlay.ShowsPendingCheck); - Assert.False(run.View.OverlapOverlay.ShowsAllClear); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); }); - [Fact] - public void StaleWithoutAutoCheckStillAsksForACheck() => RunSta(() => + [Theory] + [InlineData("failed")] + [InlineData("canceled")] + public void FailedOrCanceledCheckIsNeverAnEmptyCorner(string outcome) => RunSta(() => { using var run = new OverlayRun(); run.View.Plate.Parts.RemoveAt(1); + run.EnableAutoCheck(); + var task = run.AutoTick(); + var work = run.Next(); + if (outcome == "failed") + work.Fail(); + else + { + run.View.CancelOverlapCheck(); + work.Complete(); + } + run.Pump(task); + run.Paint(); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); + }); + + [Fact] + public void WithoutAutoCheckPendingStatesKeepTheirText() => RunSta(() => + { + using var run = new OverlayRun(); + run.View.Plate.Parts.RemoveAt(1); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); // Not checked. run.Finish(); run.View.Plate.Parts[0].Offset(1, 0); run.Paint(); Assert.Equal(OverlapCheckStatus.Stale, run.View.OverlapStatus); - Assert.False(run.View.OverlapOverlay.ShowsPendingCheck); - Assert.False(run.View.OverlapOverlay.ShowsAllClear); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); + var task = run.Start(); + var work = run.Next(); + Assert.False(run.View.OverlapOverlay.ShowsNoLabel); // Checking. + work.Complete(); + run.Pump(task); }); - private static bool CheckIsDrawn(OverlayRun run) + private static bool CornerIsEmpty(OverlayRun run) { using var image = new Bitmap(64, 64); using var graphics = Graphics.FromImage(image); graphics.Clear(Color.White); run.View.OverlapOverlay.Draw(graphics); for (var x = 0; x < 64; x++) - for (var y = 0; y < 64; y++) - { - var pixel = image.GetPixel(x, y); - if (pixel.G > pixel.R + 60 && pixel.G > pixel.B + 40) - return true; - } - return false; + for (var y = 0; y < 32; y++) + if (image.GetPixel(x, y).ToArgb() != Color.White.ToArgb()) + return false; + return true; } [Fact] diff --git a/OpenNest/Controls/OverlapOverlayController.cs b/OpenNest/Controls/OverlapOverlayController.cs index c532f538..66d2c1de 100644 --- a/OpenNest/Controls/OverlapOverlayController.cs +++ b/OpenNest/Controls/OverlapOverlayController.cs @@ -80,13 +80,16 @@ internal sealed class OverlapOverlayController : IDisposable ? "Overlaps: check pending…" : state.Message; - /// The canvas shows a green check instead of the label: a pending recheck is not clear. - internal bool ShowsAllClear => state.IsAllClear && !IsSettling; + /// + /// The corner stays empty unless something is wrong: on a clear result, and while an + /// automatic recheck settles after one or before the first. Views without automatic rechecks + /// keep their pending text, because nothing will arrive unless the user asks. + /// + internal bool ShowsNoLabel => IsSettling + ? state.SettledMessage == null || state.SettledClear + : state.IsAllClear; - /// A gray check while a recheck settles: unconfirmed, and steady in the corner. - internal bool ShowsPendingCheck => IsSettling && (state.SettledMessage == null || state.SettledClear); - - private bool IsSettling => autoCheck.IsWaiting || state.IsRunning; + private bool IsSettling => autoCheckUnits != null && (autoCheck.IsWaiting || state.IsRunning); /// /// Recheck automatically after layout edits settle. Units are read when each request @@ -687,41 +690,14 @@ internal sealed class OverlapOverlayController : IDisposable } } - // Same corner as the label; Message keeps the words for the status bar. - private void DrawCheck(Graphics graphics, Color color) - { - var scale = view.DeviceDpi / 96f; - var box = 24 * scale; - using var background = new SolidBrush(Color.FromArgb(235, Color.White)); - graphics.FillRectangle(background, 6, 6, box, box); - using var pen = new Pen(color, 3 * scale) - { - StartCap = LineCap.Round, - EndCap = LineCap.Round, - LineJoin = LineJoin.Round, - }; - var smoothing = graphics.SmoothingMode; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - graphics.DrawLines(pen, new[] - { - new PointF(6 + 5 * scale, 6 + 13 * scale), - new PointF(6 + 10 * scale, 6 + 18 * scale), - new PointF(6 + 19 * scale, 6 + 7 * scale), - }); - graphics.SmoothingMode = smoothing; - } - private void DrawStateLabel(Graphics graphics) { var saved = graphics.Save(); try { graphics.ResetTransform(); - if (ShowsAllClear || ShowsPendingCheck) - { - DrawCheck(graphics, ShowsAllClear ? Color.FromArgb(0, 140, 60) : Color.FromArgb(150, 150, 150)); + if (ShowsNoLabel) return; - } // While settling, the last findings stay put, dimmed, instead of status text flickering. var settling = IsSettling; var text = (settling ? state.SettledMessage : Message) diff --git a/docs/geometry/visual-overlap-check.md b/docs/geometry/visual-overlap-check.md index 47d59614..de57678d 100644 --- a/docs/geometry/visual-overlap-check.md +++ b/docs/geometry/visual-overlap-check.md @@ -29,11 +29,12 @@ Small positive areas use significant-figure formatting rather than rounding to zero. Selection and dragging remain ordinary plate operations, not overlay interactions. -A current, fully checked report with no finding of any kind draws a green check in -the corner instead of words; the status bar still uses the wording below. While a -recheck is pending or running the corner stays quiet: a gray check after a clear -result or when there is no result yet, or the last findings dimmed in gray, neither -of which says anything about the edited layout. Every +A current, fully checked report with no finding of any kind leaves the corner empty; +the status bar still uses the wording below. In a nest window the corner also stays +empty while a recheck is pending or running after a clear result or before the first, +and earlier findings stay dimmed in gray, which says nothing about the edited layout. +Failed, canceled and incomplete checks always show text, and hosts without automatic +rechecks keep their pending text, so an empty corner there is a finished clear check. Every other state keeps a persistent label, which distinguishes unchecked, checking, current with findings, incomplete, stale, canceled, and failed checks. Only a completed, current, fully checked report can