Files
OpenNest/OpenNest.Core/TimingInfo.cs
T
aj 87849e5326 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.
2026-10-13 18:15:49 -04:00

69 lines
2.6 KiB
C#

namespace OpenNest
{
public class TimingInfo
{
public int PierceCount;
public int IntersectionCount;
public double TravelDistance;
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
{
CutDistance = info1.CutDistance + info2.CutDistance,
IntersectionCount = info1.IntersectionCount + info2.IntersectionCount,
TravelDistance = info1.TravelDistance + info2.TravelDistance,
PierceCount = info1.PierceCount + info2.PierceCount,
ClampRemovalMinutes = info1.ClampRemovalMinutes + info2.ClampRemovalMinutes,
};
}
public static TimingInfo operator -(TimingInfo info1, TimingInfo info2)
{
return new TimingInfo
{
CutDistance = info1.CutDistance - info2.CutDistance,
IntersectionCount = info1.IntersectionCount - info2.IntersectionCount,
TravelDistance = info1.TravelDistance - info2.TravelDistance,
PierceCount = info1.PierceCount - info2.PierceCount,
ClampRemovalMinutes = info1.ClampRemovalMinutes - info2.ClampRemovalMinutes,
};
}
public static TimingInfo operator *(TimingInfo info1, TimingInfo info2)
{
return new TimingInfo
{
CutDistance = info1.CutDistance * info2.CutDistance,
IntersectionCount = info1.IntersectionCount * info2.IntersectionCount,
TravelDistance = info1.TravelDistance * info2.TravelDistance,
PierceCount = info1.PierceCount * info2.PierceCount,
ClampRemovalMinutes = info1.ClampRemovalMinutes * info2.ClampRemovalMinutes,
};
}
public static TimingInfo operator *(TimingInfo info1, int factor)
{
return new TimingInfo
{
CutDistance = info1.CutDistance * factor,
IntersectionCount = info1.IntersectionCount * factor,
TravelDistance = info1.TravelDistance * factor,
PierceCount = info1.PierceCount * factor,
ClampRemovalMinutes = info1.ClampRemovalMinutes * factor,
};
}
}
}