Files
OpenNest/OpenNest.Core/Diagnostics/OverlapReportState.cs
T
aj 29953ea601 feat(overlap): auto-recheck the active plate after layout edits settle
Nest windows now rerun the overlap check once the layout has been
unchanged for 0.5 s, instead of leaving 'Overlaps: not checked'. Edits
show 'Overlaps: check pending...'; drags are caught by the paint-time
pose stamp, collection edits by their events. The check waits while a
mouse button, modal dialog or fill is active, supersedes a running check
when the layout moves again, and does not retry a canceled or failed
layout until it changes. Rechecks use the incremental analyzer, so only
the moved parts' neighbors are recomputed.

Automatic results update only the canvas label (the status bar keeps the
last command's message) and keep Display > Off. Check Active Plate still
runs immediately. InvalidateOverlapCheck now also drops cached material.
2026-09-29 09:31:51 -04:00

112 lines
3.8 KiB
C#

using System.Collections.Generic;
namespace OpenNest.Diagnostics;
public enum OverlapDisplayMode { Off, Areas, Centroids, Both }
public enum OverlapCheckStatus { NotChecked, Checking, Current, Incomplete, Failed, Canceled, Stale }
/// <summary>UI-thread lifecycle policy, independent of workers, GDI and view transforms.</summary>
public sealed class OverlapReportState
{
private OverlapGeometryStamp stamp;
private int uncheckedPartCount;
public long Generation { get; private set; }
public OverlapCheckStatus Status { get; private set; } = OverlapCheckStatus.NotChecked;
public OverlapDisplayMode DisplayMode { get; set; } = OverlapDisplayMode.Areas;
public PlateOverlapReport Report { get; private set; }
public bool IsRunning => Status == OverlapCheckStatus.Checking;
public string Message => Status switch
{
OverlapCheckStatus.Checking => "Checking overlaps…",
OverlapCheckStatus.Current => Report.Pairs.Count == 0
? "No material overlaps detected" : $"Overlaps: {Report.Pairs.Count} pairs",
OverlapCheckStatus.Incomplete => $"Overlap check incomplete: {Report.Pairs.Count} overlapping pairs; "
+ $"{uncheckedPartCount} parts could not be checked",
OverlapCheckStatus.Failed => "Overlap check failed — run Check Overlaps again",
OverlapCheckStatus.Canceled => "Overlap check canceled",
OverlapCheckStatus.Stale => "Overlap check out of date — run Check Overlaps again",
_ => "Overlaps: not checked"
};
/// <summary>
/// Starts a request. A manual check from Off shows Areas; an automatic recheck keeps
/// the user's display choice, including Off.
/// </summary>
public long Begin(Plate plate, bool automatic = false)
{
Clear(OverlapCheckStatus.Checking);
stamp = OverlapGeometryStamp.Capture(plate);
if (!automatic && DisplayMode == OverlapDisplayMode.Off)
DisplayMode = OverlapDisplayMode.Areas;
return Generation;
}
public bool TryPublish(long generation, Plate plate, PlateOverlapReport report)
{
if (!CanComplete(generation, plate))
return false;
Report = report;
uncheckedPartCount = CountUncheckedParts(report.Issues);
Status = report.IsComplete ? OverlapCheckStatus.Current : OverlapCheckStatus.Incomplete;
return true;
}
public bool TryFail(long generation, Plate plate)
{
if (!CanComplete(generation, plate))
return false;
Clear(OverlapCheckStatus.Failed);
return true;
}
private bool CanComplete(long generation, Plate plate) =>
generation == Generation && IsRunning && EnsureFresh(plate);
public bool EnsureFresh(Plate plate)
{
if (stamp == null)
return false;
if (stamp.Matches(plate))
return true;
Invalidate();
return false;
}
public void Invalidate()
{
if (Status is OverlapCheckStatus.Checking or OverlapCheckStatus.Current or OverlapCheckStatus.Incomplete)
Clear(OverlapCheckStatus.Stale);
}
public void Cancel()
{
if (IsRunning)
Clear(OverlapCheckStatus.Canceled);
}
public void Reset() => Clear(OverlapCheckStatus.NotChecked);
private void Clear(OverlapCheckStatus status)
{
Generation++;
Report = null;
uncheckedPartCount = 0;
stamp = null;
Status = status;
}
public static int CountUncheckedParts(IEnumerable<PlateOverlapIssue> issues)
{
var ids = new HashSet<int>();
foreach (var issue in issues)
{
ids.Add(issue.PartAId);
if (issue.PartBId.HasValue)
ids.Add(issue.PartBId.Value);
}
return ids.Count;
}
}