namespace OpenNest.Diagnostics; public enum OverlapAutoCheckStep { None, Wait, Check } /// /// UI-thread debounce policy for automatic overlap rechecks; the host owns the timer. /// Restart the quiet-period timer whenever returns true, and call /// when it fires. A check starts only after the ordered layout stamp /// has stayed unchanged for a whole quiet period with no interaction in progress. /// A canceled or failed request is not retried until the layout differs from the one it /// started from, so cancellation is respected and a failing layout cannot loop. /// public sealed class OverlapAutoCheckScheduler { private OverlapGeometryStamp pending; private OverlapGeometryStamp lastRequest; public bool IsWaiting => pending != null; /// /// Call after the report state's freshness check. True means (re)start the quiet-period /// timer; false means leave it as it is. /// public bool Observe(Plate plate, OverlapReportState state) { if (plate == null || !NeedsCheck(plate, state)) { pending = null; return false; } if (pending != null && pending.Matches(plate)) return false; pending = OverlapGeometryStamp.Capture(plate); return true; } /// /// Call when the quiet period ends. means restart the /// timer (the layout moved or an interaction is still running); only /// starts a request. /// public OverlapAutoCheckStep Elapsed(Plate plate, OverlapReportState state, bool interactionActive) { if (pending == null) return OverlapAutoCheckStep.None; if (plate == null || !NeedsCheck(plate, state)) { pending = null; return OverlapAutoCheckStep.None; } if (interactionActive || !pending.Matches(plate)) { pending = OverlapGeometryStamp.Capture(plate); return OverlapAutoCheckStep.Wait; } pending = null; return OverlapAutoCheckStep.Check; } /// Record every request start, manual or automatic. public void Started(Plate plate) { pending = null; lastRequest = OverlapGeometryStamp.Capture(plate); } /// Forget pending and previous requests (plate switch, handle loss, disable). public void Reset() { pending = null; lastRequest = null; } private bool NeedsCheck(Plate plate, OverlapReportState state) => state.Status switch { OverlapCheckStatus.NotChecked or OverlapCheckStatus.Stale => true, OverlapCheckStatus.Canceled or OverlapCheckStatus.Failed => lastRequest?.Matches(plate) != true, _ => false }; }