mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-17 17:21:38 -04:00
feat(desktop): show clamps to pull and their time
Slice 3b task 3 (D9 + display). OpenNest.Core: TimingInfo gains ClampRemovalMinutes (included in +, -, * so every aggregation stays consistent); Timing.GetTimingInfo(Plate) adds plate.ClampRemovalMinutes once per physical sheet, before the quantity multiply so it scales with everything else cut on that sheet; CalculateTime adds it to the estimate as a flat number of minutes regardless of the cut parameters' unit. A plate with nothing removed is unaffected (zero). OpenNest.Data: ClampSettings and ClampRailEditor carry RemovalMinutes through the same three boundaries as clamp size - the rail/constructor, the settings file (missing key falls back to ClampRail.DefaultRemovalMinutes; present but negative/non-numeric/null is ignored and reported Partial, same as width/depth), and ToMachineDefaults - except it is a time, not a length, so ConvertedTo never scales it between inches and millimeters. Desktop: ClampsForm gets a 'Minutes to remove and replace one clamp' field wired to the editor; PlateRenderer.DrawRemovedClamps draws each clamp Plate.RemovedClamps lists (resolved from the full rail, since a removed clamp is absent from Plate.ClampZones) as a dashed grey outline with a small 'remove' marker, distinct from an active clamp's solid hatch and still display-only - no hit test, selection or sequencing; PlateView calls it right after the active-zone pass. MainForm's Auto Nest completion flow lists every committed sheet with a removal as 'Sheet N: remove clamp-X (+5 min)', using Plate.RemovedClamps/ClampRemovalMinutes from Slice 3b tasks 1-2. Tests: 10 new Data (ClampSettingsTests, ClampRailEditorTests) covering round-trip/default/invalid-value handling and the never-scaled-by-units rule; 4 new Core (TimingTests) covering the per-sheet add, the quantity scaling, CalculateTime, and nest-level aggregation across plates. Desktop changes (dialog field, renderer, Auto Nest summary) are compile-verified only (-p:EnableWindowsTargeting=true), matching the project's existing WinForms testing bar - no automated UI test exists for the sibling DrawClampZones either. Gates: Release OpenNest.Tests 3898 passed/31 skipped, OpenNest.IO.Tests 105 passed, OpenNest.Engine.Tests 878 passed/2 skipped; desktop and OpenNest.WinForms.Tests cross-builds clean; scoped dotnet format --verify-no-changes clean. This completes Slice 3b (D8/D9) on top of Slice 3's rail-following plates. Not published; master and other feature/clamp-zones-* branches untouched. Windows runtime/visual acceptance not claimed - the plan's click-through checklist (after slices 3-5) still applies.
This commit is contained in:
1 parent
83c77269a1
commit
87849e5326
11 files changed
+280
-6
No files matched your search
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenNest.Api;
|
||||
using OpenNest.CNC;
|
||||
@@ -43,6 +43,10 @@ namespace OpenNest
|
||||
pos = endpt;
|
||||
}
|
||||
|
||||
// D9: once per physical sheet, before the quantity multiply below scales it with
|
||||
// everything else cut on that sheet.
|
||||
info.ClampRemovalMinutes = plate.ClampRemovalMinutes;
|
||||
|
||||
info *= plate.Quantity;
|
||||
|
||||
return info;
|
||||
@@ -91,6 +95,9 @@ namespace OpenNest
|
||||
|
||||
time += TimeSpan.FromTicks(info.PierceCount * cutParams.PierceTime.Ticks);
|
||||
|
||||
// D9: a flat per-removed-clamp time, already in minutes regardless of cutParams.Units.
|
||||
time += TimeSpan.FromMinutes(info.ClampRemovalMinutes);
|
||||
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,13 @@ namespace OpenNest
|
||||
|
||||
public double CutDistance;
|
||||
|
||||
/// <summary>
|
||||
/// Minutes added by last-resort clamp removal (D9): once per physical sheet, for
|
||||
/// every clamp <see cref="Plate.RemovedClamps"/> lists. Zero for a plate with no
|
||||
/// removal, so existing timing is unaffected.
|
||||
/// </summary>
|
||||
public double ClampRemovalMinutes;
|
||||
|
||||
public static TimingInfo operator +(TimingInfo info1, TimingInfo info2)
|
||||
{
|
||||
return new TimingInfo
|
||||
@@ -18,6 +25,7 @@ namespace OpenNest
|
||||
IntersectionCount = info1.IntersectionCount + info2.IntersectionCount,
|
||||
TravelDistance = info1.TravelDistance + info2.TravelDistance,
|
||||
PierceCount = info1.PierceCount + info2.PierceCount,
|
||||
ClampRemovalMinutes = info1.ClampRemovalMinutes + info2.ClampRemovalMinutes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +37,7 @@ namespace OpenNest
|
||||
IntersectionCount = info1.IntersectionCount - info2.IntersectionCount,
|
||||
TravelDistance = info1.TravelDistance - info2.TravelDistance,
|
||||
PierceCount = info1.PierceCount - info2.PierceCount,
|
||||
ClampRemovalMinutes = info1.ClampRemovalMinutes - info2.ClampRemovalMinutes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,6 +49,7 @@ namespace OpenNest
|
||||
IntersectionCount = info1.IntersectionCount * info2.IntersectionCount,
|
||||
TravelDistance = info1.TravelDistance * info2.TravelDistance,
|
||||
PierceCount = info1.PierceCount * info2.PierceCount,
|
||||
ClampRemovalMinutes = info1.ClampRemovalMinutes * info2.ClampRemovalMinutes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,6 +61,7 @@ namespace OpenNest
|
||||
IntersectionCount = info1.IntersectionCount * factor,
|
||||
TravelDistance = info1.TravelDistance * factor,
|
||||
PierceCount = info1.PierceCount * factor,
|
||||
ClampRemovalMinutes = info1.ClampRemovalMinutes * factor,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ public sealed class ClampRailEditor
|
||||
var initial = ClampSettings.FromRail(rail, units, applyToNewNests);
|
||||
Width = initial.Width;
|
||||
Depth = initial.Depth;
|
||||
RemovalMinutes = initial.RemovalMinutes;
|
||||
_centers.AddRange(initial.Centers);
|
||||
}
|
||||
|
||||
@@ -30,6 +31,10 @@ public sealed class ClampRailEditor
|
||||
/// <summary>Clamp intrusion into the sheet (Y).</summary>
|
||||
public double Depth { get; set; }
|
||||
|
||||
/// <summary>Minutes to remove one clamp and put it back after cutting (D9); not a
|
||||
/// length, so it is never converted between units.</summary>
|
||||
public double RemovalMinutes { get; set; }
|
||||
|
||||
/// <summary>Whether saved machine defaults give new nests these clamps.</summary>
|
||||
public bool ApplyToNewNests { get; set; }
|
||||
|
||||
@@ -76,6 +81,7 @@ public sealed class ClampRailEditor
|
||||
var converted = settings.ConvertedTo(Units);
|
||||
Width = converted.Width;
|
||||
Depth = converted.Depth;
|
||||
RemovalMinutes = converted.RemovalMinutes;
|
||||
ApplyToNewNests = settings.Enabled;
|
||||
_centers.Clear();
|
||||
_centers.AddRange(converted.Centers.Distinct().OrderBy(c => c));
|
||||
@@ -83,10 +89,11 @@ public sealed class ClampRailEditor
|
||||
|
||||
/// <summary>
|
||||
/// The edited rail, or null when no clamp is listed. Throws
|
||||
/// <see cref="ArgumentOutOfRangeException"/> for a non-positive width or depth.
|
||||
/// <see cref="ArgumentOutOfRangeException"/> for a non-positive width or depth, or a
|
||||
/// negative/non-finite removal time.
|
||||
/// </summary>
|
||||
public ClampRail? BuildRail() =>
|
||||
_centers.Count == 0 ? null : new ClampRail(Width, Depth, _centers);
|
||||
_centers.Count == 0 ? null : new ClampRail(Width, Depth, _centers, RemovalMinutes);
|
||||
|
||||
/// <summary>The dialog's OK: makes the edited rail the nest's, re-resolving every plate.</summary>
|
||||
public void ApplyTo(Nest nest)
|
||||
@@ -107,12 +114,18 @@ public sealed class ClampRailEditor
|
||||
nameof(Width),
|
||||
"Clamp width and depth must be positive."
|
||||
);
|
||||
if (!double.IsFinite(RemovalMinutes) || RemovalMinutes < 0)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(RemovalMinutes),
|
||||
"Clamp removal time must be finite and zero or more."
|
||||
);
|
||||
return new ClampSettings
|
||||
{
|
||||
Enabled = ApplyToNewNests && _centers.Count > 0,
|
||||
Units = Units,
|
||||
Width = Width,
|
||||
Depth = Depth,
|
||||
RemovalMinutes = RemovalMinutes,
|
||||
Centers = _centers.ToArray(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ public sealed class ClampSettings
|
||||
|
||||
public double Depth { get; set; } = DefaultDepth;
|
||||
|
||||
/// <summary>
|
||||
/// Minutes to remove one clamp and put it back after cutting (D9), stored and edited in
|
||||
/// the machine clamp settings alongside the rail's physical size.
|
||||
/// </summary>
|
||||
public double RemovalMinutes { get; set; } = ClampRail.DefaultRemovalMinutes;
|
||||
|
||||
/// <summary>Clamp centers along X, measured from the plate origin.</summary>
|
||||
public IReadOnlyList<double> Centers { get; set; } = Array.Empty<double>();
|
||||
|
||||
@@ -82,13 +88,14 @@ public sealed class ClampSettings
|
||||
Units = units,
|
||||
Width = rail?.Width ?? ConvertDefault(DefaultWidth, units),
|
||||
Depth = rail?.Depth ?? ConvertDefault(DefaultDepth, units),
|
||||
RemovalMinutes = rail?.RemovalMinutes ?? ClampRail.DefaultRemovalMinutes,
|
||||
Centers = rail?.Centers.ToArray() ?? Array.Empty<double>(),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The rail a new nest in <paramref name="units"/> receives, or null when clamps are
|
||||
/// disabled or no center is configured. Values are converted between inches and
|
||||
/// millimeters as needed.
|
||||
/// millimeters as needed; <see cref="RemovalMinutes"/> is a time, so it is never scaled.
|
||||
/// </summary>
|
||||
public ClampRail? RailFor(Units units)
|
||||
{
|
||||
@@ -96,12 +103,13 @@ public sealed class ClampSettings
|
||||
return null;
|
||||
|
||||
var converted = ConvertedTo(units);
|
||||
return new ClampRail(converted.Width, converted.Depth, converted.Centers);
|
||||
return new ClampRail(converted.Width, converted.Depth, converted.Centers, converted.RemovalMinutes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A copy with width, depth and centers expressed in <paramref name="units"/>
|
||||
/// (inches and millimeters convert at 25.4); <see cref="Enabled"/> is kept.
|
||||
/// (inches and millimeters convert at 25.4); <see cref="Enabled"/> and
|
||||
/// <see cref="RemovalMinutes"/> (a time, not a length) are kept unscaled.
|
||||
/// </summary>
|
||||
public ClampSettings ConvertedTo(Units units)
|
||||
{
|
||||
@@ -112,6 +120,7 @@ public sealed class ClampSettings
|
||||
Units = units,
|
||||
Width = Width * scale,
|
||||
Depth = Depth * scale,
|
||||
RemovalMinutes = RemovalMinutes,
|
||||
Centers = Centers.Select(c => c * scale).ToArray(),
|
||||
};
|
||||
}
|
||||
@@ -203,6 +212,14 @@ public sealed class ClampSettings
|
||||
ignored = true;
|
||||
}
|
||||
|
||||
if (values.TryGetValue("removalMinutes", out var removalMinutes))
|
||||
{
|
||||
if (TryLength(removalMinutes, out var value) && value >= 0)
|
||||
settings.RemovalMinutes = value;
|
||||
else
|
||||
ignored = true;
|
||||
}
|
||||
|
||||
if (values.TryGetValue("centers", out var centers))
|
||||
{
|
||||
if (centers.ValueKind == JsonValueKind.Array)
|
||||
@@ -240,6 +257,7 @@ public sealed class ClampSettings
|
||||
units = Units.ToString().ToLowerInvariant(),
|
||||
width = Width,
|
||||
depth = Depth,
|
||||
removalMinutes = RemovalMinutes,
|
||||
centers = Centers,
|
||||
};
|
||||
var json = JsonSerializer.Serialize(dto, JsonOptions);
|
||||
|
||||
@@ -168,4 +168,47 @@ public class ClampRailEditorTests
|
||||
Assert.Equal(1.5, inches.Width); // the source is not modified
|
||||
Assert.Equal(1.5, inches.ConvertedTo(Units.Inches).Width);
|
||||
}
|
||||
|
||||
// D9: the editor carries removal minutes through the same three boundaries as size.
|
||||
|
||||
[Fact]
|
||||
public void RemovalMinutesStartsFromTheRailOrMachineDefault()
|
||||
{
|
||||
var rail = new ClampRail(1.5, 0.625, MachineCenters, removalMinutes: 7);
|
||||
Assert.Equal(7, new ClampRailEditor(rail, Units.Inches, false).RemovalMinutes);
|
||||
Assert.Equal(ClampRail.DefaultRemovalMinutes,
|
||||
new ClampRailEditor(null, Units.Inches, false).RemovalMinutes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovalMinutesIsNeverConvertedBetweenUnitsOnLoadOrBuild()
|
||||
{
|
||||
var editor = new ClampRailEditor(null, Units.Millimeters, applyToNewNests: true);
|
||||
editor.RemovalMinutes = 8;
|
||||
editor.TryAddCenter(150, out _);
|
||||
|
||||
Assert.Equal(8, editor.ToMachineDefaults().RemovalMinutes);
|
||||
Assert.Equal(8, editor.BuildRail()!.RemovalMinutes);
|
||||
|
||||
var defaults = new ClampSettings
|
||||
{
|
||||
Enabled = true,
|
||||
Units = Units.Inches,
|
||||
RemovalMinutes = 9,
|
||||
Centers = new[] { 6.0 },
|
||||
};
|
||||
editor.LoadMachineDefaults(defaults);
|
||||
Assert.Equal(9, editor.RemovalMinutes); // a time, so unaffected by the unit change
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidRemovalMinutesIsRefusedBeforeSaving()
|
||||
{
|
||||
var editor = new ClampRailEditor(null, Units.Inches, applyToNewNests: true);
|
||||
editor.TryAddCenter(6, out _);
|
||||
editor.RemovalMinutes = -1;
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => editor.ToMachineDefaults());
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => editor.BuildRail());
|
||||
}
|
||||
}
|
||||
@@ -322,6 +322,71 @@ public class ClampSettingsTests : IDisposable
|
||||
AssertFallback(none);
|
||||
}
|
||||
|
||||
// D9: minutes to remove one clamp, stored alongside the rail's physical size.
|
||||
|
||||
[Fact]
|
||||
public void RemovalMinutesDefaultsToThePlannerValue()
|
||||
{
|
||||
Assert.Equal(ClampRail.DefaultRemovalMinutes, new ClampSettings().RemovalMinutes);
|
||||
Assert.Equal(ClampRail.DefaultRemovalMinutes, ClampSettings.Fallback.RemovalMinutes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovalMinutesRoundTripsAndIsNeverScaledByUnits()
|
||||
{
|
||||
var settings = Machine();
|
||||
settings.RemovalMinutes = 8;
|
||||
settings.Save(_path);
|
||||
|
||||
var loaded = ClampSettings.Load(_path, out var status);
|
||||
Assert.Equal(ClampSettingsStatus.Ok, status);
|
||||
Assert.Equal(8, loaded.RemovalMinutes);
|
||||
using var json = JsonDocument.Parse(File.ReadAllText(_path));
|
||||
Assert.Equal(8, json.RootElement.GetProperty("removalMinutes").GetDouble());
|
||||
|
||||
// Width/depth/centers scale between units; removal time (not a length) never does.
|
||||
var metric = loaded.ConvertedTo(Units.Millimeters);
|
||||
Assert.Equal(8, metric.RemovalMinutes);
|
||||
Assert.Equal(8, loaded.RailFor(Units.Millimeters)!.RemovalMinutes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingRemovalMinutesDefaultsOnLoad()
|
||||
{
|
||||
File.WriteAllText(_path, """{ "enabled": true, "centers": [6] }""");
|
||||
|
||||
var loaded = ClampSettings.Load(_path, out var status);
|
||||
|
||||
Assert.Equal(ClampSettingsStatus.Ok, status);
|
||||
Assert.Equal(ClampRail.DefaultRemovalMinutes, loaded.RemovalMinutes);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("""{ "enabled": true, "centers": [6], "removalMinutes": -1 }""")]
|
||||
[InlineData("""{ "enabled": true, "centers": [6], "removalMinutes": "five" }""")]
|
||||
[InlineData("""{ "enabled": true, "centers": [6], "removalMinutes": null }""")]
|
||||
public void InvalidRemovalMinutesFallsBackAndIsReported(string json)
|
||||
{
|
||||
File.WriteAllText(_path, json);
|
||||
|
||||
var loaded = ClampSettings.Load(_path, out var status);
|
||||
|
||||
Assert.Equal(ClampSettingsStatus.Partial, status);
|
||||
Assert.Equal(ClampRail.DefaultRemovalMinutes, loaded.RemovalMinutes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZeroRemovalMinutesIsValid()
|
||||
{
|
||||
var settings = Machine();
|
||||
settings.RemovalMinutes = 0;
|
||||
settings.Save(_path);
|
||||
|
||||
var loaded = ClampSettings.Load(_path, out var status);
|
||||
Assert.Equal(ClampSettingsStatus.Ok, status);
|
||||
Assert.Equal(0, loaded.RemovalMinutes);
|
||||
}
|
||||
|
||||
private static ClampSettingsStatus LoadStatus(string path)
|
||||
{
|
||||
ClampSettings.Load(path, out var status);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using OpenNest.Api;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// D9: a sheet's cut-time estimate gains the rail's removal minutes once per removed clamp,
|
||||
/// scaled by the plate's cut quantity like every other timing field; a plate with nothing
|
||||
/// removed is unaffected.
|
||||
/// </summary>
|
||||
public class TimingTests
|
||||
{
|
||||
private static Plate PlateWithOneRemovedClamp(double removalMinutes, int quantity = 1)
|
||||
{
|
||||
var plate = new Plate(3, 23) { Quantity = quantity, EdgeSpacing = new Spacing(0, 0, 0, 0) };
|
||||
var rail = new ClampRail(1.5, 0.625, new[] { 6.0, 21.0 }, removalMinutes);
|
||||
plate.SetClampRail(rail, new[] { "clamp-1" });
|
||||
return plate;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlateWithNoRemovedClampsAddsNoTime()
|
||||
{
|
||||
var plate = new Plate(48, 96) { Quantity = 3 };
|
||||
|
||||
var info = Timing.GetTimingInfo(plate);
|
||||
|
||||
Assert.Equal(0, info.ClampRemovalMinutes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemovedClampMinutesAreAddedOncePerPhysicalSheet()
|
||||
{
|
||||
var plate = PlateWithOneRemovedClamp(removalMinutes: 5, quantity: 3);
|
||||
|
||||
var info = Timing.GetTimingInfo(plate);
|
||||
|
||||
Assert.Equal(15, info.ClampRemovalMinutes); // 5 minutes x 3 physical sheets
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateTimeAddsTheRemovalMinutesToTheEstimate()
|
||||
{
|
||||
var info = new TimingInfo { ClampRemovalMinutes = 7 };
|
||||
|
||||
var time = Timing.CalculateTime(info, CutParameters.Default);
|
||||
|
||||
Assert.Equal(TimeSpan.FromMinutes(7), time);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestTotalsSumRemovalMinutesAcrossPlates()
|
||||
{
|
||||
var nest = new Nest();
|
||||
nest.Plates.Add(PlateWithOneRemovedClamp(removalMinutes: 5));
|
||||
nest.Plates.Add(new Plate(48, 96)); // ordinary plate, no clamps
|
||||
|
||||
var info = Timing.GetTimingInfo(nest);
|
||||
|
||||
Assert.Equal(5, info.ClampRemovalMinutes);
|
||||
}
|
||||
}
|
||||
@@ -205,6 +205,43 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
// D8: a clamp Auto Nest pulled as a last resort. Distinct from an active clamp - a
|
||||
// dashed grey outline and a small "remove" marker, still display + exclusion only
|
||||
// (no hit test, selection or sequencing).
|
||||
private static readonly Color RemovedClampOutlineColor = Color.FromArgb(200, 110, 110, 110);
|
||||
|
||||
public void DrawRemovedClamps(Graphics g)
|
||||
{
|
||||
var plate = view.DisplayPlate;
|
||||
if (plate?.ClampRail == null || plate.RemovedClamps.Count == 0)
|
||||
return;
|
||||
|
||||
var left = plate.Quadrant is 2 or 3 ? -plate.Size.Length : 0;
|
||||
var bottom = plate.Quadrant is 3 or 4 ? -plate.Size.Width : 0;
|
||||
|
||||
using var pen = new Pen(RemovedClampOutlineColor, 1.5f) { DashStyle = DashStyle.Dash };
|
||||
using var font = new Font(FontFamily.GenericSansSerif, 7f);
|
||||
using var textBrush = new SolidBrush(RemovedClampOutlineColor);
|
||||
|
||||
foreach (var zone in plate.ClampRail.ResolveFor(plate.Size, plate.Quadrant))
|
||||
{
|
||||
if (!plate.RemovedClamps.Contains(zone.Id))
|
||||
continue;
|
||||
|
||||
var box = zone.Bounds.Translate(left, bottom);
|
||||
var rect = new RectangleF
|
||||
{
|
||||
Location = view.PointWorldToGraph(box.Location),
|
||||
Width = view.LengthWorldToGui(box.Length),
|
||||
Height = view.LengthWorldToGui(box.Width),
|
||||
};
|
||||
rect.Y -= rect.Height;
|
||||
|
||||
g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
|
||||
g.DrawString("remove", font, textBrush, rect.X, rect.Y - font.GetHeight(g));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawCutOffs(Graphics g)
|
||||
{
|
||||
var plate = view.DisplayPlate;
|
||||
|
||||
@@ -712,6 +712,7 @@ namespace OpenNest.Controls
|
||||
renderer.DrawPlate(e.Graphics);
|
||||
renderer.DrawParts(e.Graphics);
|
||||
renderer.DrawClampZones(e.Graphics);
|
||||
renderer.DrawRemovedClamps(e.Graphics);
|
||||
if (!HasProposalDisplay)
|
||||
{
|
||||
renderer.DrawCutOffs(e.Graphics);
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed class ClampsForm : Form
|
||||
private readonly string settingsPath;
|
||||
private readonly NumericUpDown widthBox;
|
||||
private readonly NumericUpDown depthBox;
|
||||
private readonly NumericUpDown removalMinutesBox;
|
||||
private readonly NumericUpDown centerBox;
|
||||
private readonly ListBox centersList;
|
||||
private readonly Button addButton;
|
||||
@@ -73,8 +74,10 @@ public sealed class ClampsForm : Form
|
||||
var size = new TableLayoutPanel { AutoSize = true, ColumnCount = 3, Dock = DockStyle.Top };
|
||||
widthBox = NewLengthBox(Editor.Width, minimum: 0.0001m);
|
||||
depthBox = NewLengthBox(Editor.Depth, minimum: 0.0001m);
|
||||
removalMinutesBox = NewLengthBox(Editor.RemovalMinutes, minimum: 0m);
|
||||
AddRow(size, "Clamp width (along the edge):", widthBox, unit);
|
||||
AddRow(size, "Clamp depth (into the plate):", depthBox, unit);
|
||||
AddRow(size, "Minutes to remove and replace one clamp:", removalMinutesBox, "min");
|
||||
layout.Controls.Add(size);
|
||||
|
||||
var centersGroup = new GroupBox
|
||||
@@ -206,6 +209,7 @@ public sealed class ClampsForm : Form
|
||||
{
|
||||
Editor.Width = (double)widthBox.Value;
|
||||
Editor.Depth = (double)depthBox.Value;
|
||||
Editor.RemovalMinutes = (double)removalMinutesBox.Value;
|
||||
Editor.ApplyToNewNests = applyToNewNestsBox.Checked;
|
||||
}
|
||||
|
||||
@@ -244,6 +248,7 @@ public sealed class ClampsForm : Form
|
||||
Editor.LoadMachineDefaults(machineDefaults);
|
||||
widthBox.Value = ToBoxValue(widthBox, Editor.Width);
|
||||
depthBox.Value = ToBoxValue(depthBox, Editor.Depth);
|
||||
removalMinutesBox.Value = ToBoxValue(removalMinutesBox, Editor.RemovalMinutes);
|
||||
applyToNewNestsBox.Checked = Editor.ApplyToNewNests;
|
||||
RefreshCenters();
|
||||
}
|
||||
|
||||
@@ -1702,6 +1702,18 @@ namespace OpenNest.Forms
|
||||
target.OverlapDisplay = OverlapDisplayMode.Both;
|
||||
await target.CheckOverlapsAsync();
|
||||
}
|
||||
// D8/D9: name every clamp Auto Nest pulled as a last resort and its added time.
|
||||
var removalNotices = applied
|
||||
.Where(plate => plate.RemovedClamps.Count > 0)
|
||||
.Select(plate => $"Sheet {target.Nest.Plates.IndexOf(plate) + 1}: remove "
|
||||
+ $"{string.Join(", ", plate.RemovedClamps.OrderBy(id => id, StringComparer.Ordinal))} "
|
||||
+ $"(+{plate.ClampRemovalMinutes:0.#} min)")
|
||||
.ToList();
|
||||
if (removalNotices.Count > 0)
|
||||
MessageBox.Show(this,
|
||||
"Auto Nest removed clamps as a last resort so a part that would not "
|
||||
+ "otherwise fit could be nested:\n\n" + string.Join("\n", removalNotices),
|
||||
"Auto Nest", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
if (costs?.Basis == "supplied-cost")
|
||||
MessageBox.Show(this, $"Gross sheet cost: {costs.GrossTotal:G}\nEstimated salvage credit: {costs.SalvageCredit:G}\nNet score: {costs.NetScore:G}",
|
||||
"Auto Nest Cost", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
|
||||
Reference in new issue
Block a user