Files
OpenNest/OpenNest/Forms/NestProgressForm.cs

214 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
namespace OpenNest.Forms
{
public partial class NestProgressForm : Form
{
private static readonly Color DefaultFlashColor = Color.FromArgb(0, 160, 0);
private static readonly Color DensityLowColor = Color.FromArgb(200, 40, 40);
private static readonly Color DensityMidColor = Color.FromArgb(200, 160, 0);
private static readonly Color DensityHighColor = Color.FromArgb(0, 160, 0);
private const int FadeSteps = 20;
private const int FadeIntervalMs = 50;
private readonly CancellationTokenSource cts;
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
private readonly System.Windows.Forms.Timer elapsedTimer;
private readonly System.Windows.Forms.Timer fadeTimer;
private readonly Dictionary<Label, (int remaining, Color flashColor)> fadeCounters = new();
private bool hasReceivedProgress;
public bool Accepted { get; private set; }
public NestProgressForm(CancellationTokenSource cts, bool showPlateRow = true)
{
this.cts = cts;
InitializeComponent();
if (!showPlateRow)
{
plateLabel.Visible = false;
plateValue.Visible = false;
}
elapsedTimer = new System.Windows.Forms.Timer { Interval = 1000 };
elapsedTimer.Tick += (s, e) => UpdateElapsed();
elapsedTimer.Start();
fadeTimer = new System.Windows.Forms.Timer { Interval = FadeIntervalMs };
fadeTimer.Tick += FadeTimer_Tick;
}
public void UpdateProgress(NestProgress progress)
{
if (IsDisposed || !IsHandleCreated)
return;
if (!hasReceivedProgress)
{
hasReceivedProgress = true;
acceptButton.Enabled = true;
stopButton.Enabled = true;
}
phaseStepper.ActivePhase = progress.Phase;
SetValueWithFlash(plateValue, progress.PlateNumber.ToString());
SetValueWithFlash(partsValue, progress.BestPartCount.ToString());
var densityText = progress.BestDensity.ToString("P1");
var densityFlashColor = GetDensityColor(progress.BestDensity);
SetValueWithFlash(densityValue, densityText, densityFlashColor);
densityBar.Value = progress.BestDensity;
SetValueWithFlash(nestedAreaValue,
$"{progress.NestedWidth:F1} x {progress.NestedLength:F1} ({progress.NestedArea:F1} sq in)");
descriptionValue.Text = !string.IsNullOrEmpty(progress.Description)
? progress.Description
: FormatPhase(progress.Phase);
}
public void ShowCompleted()
{
if (IsDisposed || !IsHandleCreated)
return;
stopwatch.Stop();
elapsedTimer.Stop();
UpdateElapsed();
phaseStepper.IsComplete = true;
descriptionValue.Text = "\u2014";
acceptButton.Visible = false;
stopButton.Text = "Close";
stopButton.Enabled = true;
stopButton.Click -= StopButton_Click;
stopButton.Click += (s, e) => Close();
}
private void UpdateElapsed()
{
if (IsDisposed || !IsHandleCreated)
return;
var elapsed = stopwatch.Elapsed;
elapsedValue.Text = elapsed.TotalHours >= 1
? elapsed.ToString(@"h\:mm\:ss")
: elapsed.ToString(@"m\:ss");
}
private void AcceptButton_Click(object sender, EventArgs e)
{
Accepted = true;
cts.Cancel();
acceptButton.Enabled = false;
stopButton.Enabled = false;
acceptButton.Text = "Accepted";
stopButton.Text = "Stopping...";
}
private void StopButton_Click(object sender, EventArgs e)
{
cts.Cancel();
acceptButton.Enabled = false;
stopButton.Enabled = false;
stopButton.Text = "Stopping...";
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
fadeTimer.Stop();
fadeTimer.Dispose();
elapsedTimer.Stop();
elapsedTimer.Dispose();
stopwatch.Stop();
if (!cts.IsCancellationRequested)
cts.Cancel();
base.OnFormClosing(e);
}
private void SetValueWithFlash(Label label, string text, Color? flashColor = null)
{
if (label.Text == text)
return;
var color = flashColor ?? DefaultFlashColor;
label.Text = text;
label.ForeColor = color;
fadeCounters[label] = (FadeSteps, color);
if (!fadeTimer.Enabled)
fadeTimer.Start();
}
private void FadeTimer_Tick(object sender, EventArgs e)
{
if (IsDisposed || !IsHandleCreated)
{
fadeTimer.Stop();
return;
}
var defaultColor = SystemColors.ControlText;
var labels = fadeCounters.Keys.ToList();
foreach (var label in labels)
{
var (remaining, flashColor) = fadeCounters[label];
remaining--;
if (remaining <= 0)
{
label.ForeColor = defaultColor;
fadeCounters.Remove(label);
}
else
{
var ratio = (float)remaining / FadeSteps;
var r = (int)(defaultColor.R + (flashColor.R - defaultColor.R) * ratio);
var g = (int)(defaultColor.G + (flashColor.G - defaultColor.G) * ratio);
var b = (int)(defaultColor.B + (flashColor.B - defaultColor.B) * ratio);
label.ForeColor = Color.FromArgb(r, g, b);
fadeCounters[label] = (remaining, flashColor);
}
}
if (fadeCounters.Count == 0)
fadeTimer.Stop();
}
private static Color GetDensityColor(double density)
{
if (density < 0.5)
return DensityLowColor;
if (density < 0.7)
return DensityMidColor;
return DensityHighColor;
}
private static string FormatPhase(NestPhase phase)
{
switch (phase)
{
case NestPhase.Linear: return "Trying rotations...";
case NestPhase.RectBestFit: return "Trying best fit...";
case NestPhase.Pairs: return "Trying pairs...";
case NestPhase.Extents: return "Trying extents...";
case NestPhase.Nfp: return "Trying NFP...";
default: return phase.ToString();
}
}
}
}