Compare commits
21 Commits
5949c3ca1f
...
d65f3460a9
| Author | SHA1 | Date | |
|---|---|---|---|
| d65f3460a9 | |||
| ede06b1bf6 | |||
| 51eea6d1e6 | |||
| 3d23ad8073 | |||
| 107fd86066 | |||
| d12f0cee3e | |||
| d93b69c524 | |||
| a65598615e | |||
| ed082a6799 | |||
| c9b17619ef | |||
| f78cc78a65 | |||
| 37130e8a28 | |||
| 6f19fe1822 | |||
| 81c167320d | |||
| 981188f65e | |||
| ffd060bf61 | |||
| a360452da3 | |||
| b3e9e5e28b | |||
| 7380a43349 | |||
| 59e00cd707 | |||
| 44cb6e4a2b |
@@ -12,8 +12,32 @@ namespace OpenNest
|
||||
public class Drawing
|
||||
{
|
||||
private static int nextId;
|
||||
private static int nextColorIndex;
|
||||
private Program program;
|
||||
|
||||
public static readonly Color[] PartColors = new Color[]
|
||||
{
|
||||
Color.FromArgb(205, 92, 92), // Indian Red
|
||||
Color.FromArgb(148, 103, 189), // Medium Purple
|
||||
Color.FromArgb(75, 180, 175), // Teal
|
||||
Color.FromArgb(210, 190, 75), // Goldenrod
|
||||
Color.FromArgb(190, 85, 175), // Orchid
|
||||
Color.FromArgb(185, 115, 85), // Sienna
|
||||
Color.FromArgb(120, 100, 190), // Slate Blue
|
||||
Color.FromArgb(200, 100, 140), // Rose
|
||||
Color.FromArgb(80, 175, 155), // Sea Green
|
||||
Color.FromArgb(195, 160, 85), // Dark Khaki
|
||||
Color.FromArgb(175, 95, 160), // Plum
|
||||
Color.FromArgb(215, 130, 130), // Light Coral
|
||||
};
|
||||
|
||||
public static Color GetNextColor()
|
||||
{
|
||||
var color = PartColors[nextColorIndex % PartColors.Length];
|
||||
nextColorIndex++;
|
||||
return color;
|
||||
}
|
||||
|
||||
public Drawing()
|
||||
: this(string.Empty, new Program())
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using OpenNest.Collections;
|
||||
using OpenNest.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenNest
|
||||
{
|
||||
@@ -51,6 +52,10 @@ namespace OpenNest
|
||||
|
||||
public PlateSettings PlateDefaults { get; set; }
|
||||
|
||||
public List<PlateOption> PlateOptions { get; set; } = new();
|
||||
|
||||
public double SalvageRate { get; set; } = 0.5;
|
||||
|
||||
public Plate CreatePlate()
|
||||
{
|
||||
var plate = PlateDefaults.CreateNew();
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
using OpenNest.Collections;
|
||||
using System;
|
||||
|
||||
namespace OpenNest
|
||||
{
|
||||
public class PlateChangedEventArgs : EventArgs
|
||||
{
|
||||
public Plate Plate { get; }
|
||||
public int Index { get; }
|
||||
|
||||
public PlateChangedEventArgs(Plate plate, int index)
|
||||
{
|
||||
Plate = plate;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlateManager : IDisposable
|
||||
{
|
||||
private readonly Nest nest;
|
||||
private bool disposed;
|
||||
private bool suppressNavigation;
|
||||
private bool batching;
|
||||
private Plate subscribedLast;
|
||||
private Plate subscribedSecondToLast;
|
||||
|
||||
public event EventHandler<PlateChangedEventArgs> CurrentPlateChanged;
|
||||
public event EventHandler PlateListChanged;
|
||||
|
||||
public PlateManager(Nest nest)
|
||||
{
|
||||
this.nest = nest;
|
||||
nest.Plates.ItemAdded += OnPlateAdded;
|
||||
nest.Plates.ItemRemoved += OnPlateRemoved;
|
||||
}
|
||||
|
||||
public int CurrentIndex { get; private set; }
|
||||
|
||||
public Plate CurrentPlate => nest.Plates.Count > 0 ? nest.Plates[CurrentIndex] : null;
|
||||
|
||||
public int Count => nest.Plates.Count;
|
||||
|
||||
public bool IsFirst => Count == 0 || CurrentIndex <= 0;
|
||||
|
||||
public bool IsLast => CurrentIndex + 1 >= Count;
|
||||
|
||||
public bool CanRemoveCurrent => Count > 1 && CurrentPlate != null && CurrentPlate.Parts.Count > 0;
|
||||
|
||||
public void LoadFirst()
|
||||
{
|
||||
if (Count == 0)
|
||||
return;
|
||||
|
||||
CurrentIndex = 0;
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
|
||||
public void LoadLast()
|
||||
{
|
||||
if (Count == 0)
|
||||
return;
|
||||
|
||||
CurrentIndex = Count - 1;
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
|
||||
public bool LoadNext()
|
||||
{
|
||||
if (CurrentIndex + 1 >= Count)
|
||||
return false;
|
||||
|
||||
CurrentIndex++;
|
||||
FireCurrentPlateChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool LoadPrevious()
|
||||
{
|
||||
if (Count == 0 || CurrentIndex - 1 < 0)
|
||||
return false;
|
||||
|
||||
CurrentIndex--;
|
||||
FireCurrentPlateChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void LoadAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
return;
|
||||
|
||||
CurrentIndex = index;
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
|
||||
public void EnsureSentinel()
|
||||
{
|
||||
suppressNavigation = true;
|
||||
try
|
||||
{
|
||||
if (Count == 0 || nest.Plates[^1].Parts.Count > 0)
|
||||
nest.CreatePlate();
|
||||
|
||||
while (Count > 1
|
||||
&& nest.Plates[^1].Parts.Count == 0
|
||||
&& nest.Plates[^2].Parts.Count == 0)
|
||||
{
|
||||
nest.Plates.RemoveAt(Count - 1);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
suppressNavigation = false;
|
||||
}
|
||||
|
||||
SubscribeToTailPlates();
|
||||
}
|
||||
|
||||
public void BeginBatch()
|
||||
{
|
||||
batching = true;
|
||||
}
|
||||
|
||||
public void EndBatch()
|
||||
{
|
||||
batching = false;
|
||||
EnsureSentinel();
|
||||
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
|
||||
public Plate GetOrCreateEmpty()
|
||||
{
|
||||
for (var i = Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (nest.Plates[i].Parts.Count == 0)
|
||||
return nest.Plates[i];
|
||||
}
|
||||
|
||||
return nest.CreatePlate();
|
||||
}
|
||||
|
||||
public void RemoveCurrent()
|
||||
{
|
||||
if (Count < 2)
|
||||
return;
|
||||
|
||||
nest.Plates.RemoveAt(CurrentIndex);
|
||||
}
|
||||
|
||||
private void SubscribeToTailPlates()
|
||||
{
|
||||
UnsubscribeFromTailPlates();
|
||||
|
||||
if (Count > 0)
|
||||
{
|
||||
subscribedLast = nest.Plates[^1];
|
||||
subscribedLast.PartAdded += OnTailPartAdded;
|
||||
subscribedLast.PartRemoved += OnTailPartRemoved;
|
||||
}
|
||||
|
||||
if (Count > 1)
|
||||
{
|
||||
subscribedSecondToLast = nest.Plates[^2];
|
||||
subscribedSecondToLast.PartAdded += OnTailPartAdded;
|
||||
subscribedSecondToLast.PartRemoved += OnTailPartRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromTailPlates()
|
||||
{
|
||||
if (subscribedLast != null)
|
||||
{
|
||||
subscribedLast.PartAdded -= OnTailPartAdded;
|
||||
subscribedLast.PartRemoved -= OnTailPartRemoved;
|
||||
subscribedLast = null;
|
||||
}
|
||||
|
||||
if (subscribedSecondToLast != null)
|
||||
{
|
||||
subscribedSecondToLast.PartAdded -= OnTailPartAdded;
|
||||
subscribedSecondToLast.PartRemoved -= OnTailPartRemoved;
|
||||
subscribedSecondToLast = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTailPartAdded(object sender, ItemAddedEventArgs<Part> e)
|
||||
{
|
||||
if (!batching)
|
||||
EnsureSentinel();
|
||||
}
|
||||
|
||||
private void OnTailPartRemoved(object sender, ItemRemovedEventArgs<Part> e)
|
||||
{
|
||||
if (!batching)
|
||||
EnsureSentinel();
|
||||
}
|
||||
|
||||
private void OnPlateAdded(object sender, ItemAddedEventArgs<Plate> e)
|
||||
{
|
||||
if (!suppressNavigation && !batching)
|
||||
EnsureSentinel();
|
||||
|
||||
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
if (!suppressNavigation)
|
||||
{
|
||||
CurrentIndex = Count - 1;
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlateRemoved(object sender, ItemRemovedEventArgs<Plate> e)
|
||||
{
|
||||
if (CurrentIndex >= Count && Count > 0)
|
||||
CurrentIndex = Count - 1;
|
||||
|
||||
if (!suppressNavigation && !batching)
|
||||
EnsureSentinel();
|
||||
|
||||
PlateListChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
if (!suppressNavigation)
|
||||
FireCurrentPlateChanged();
|
||||
}
|
||||
|
||||
private void FireCurrentPlateChanged()
|
||||
{
|
||||
CurrentPlateChanged?.Invoke(this, new PlateChangedEventArgs(CurrentPlate, CurrentIndex));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
UnsubscribeFromTailPlates();
|
||||
nest.Plates.ItemAdded -= OnPlateAdded;
|
||||
nest.Plates.ItemRemoved -= OnPlateRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenNest
|
||||
{
|
||||
public class PlateOptimizerResult
|
||||
{
|
||||
public List<Part> Parts { get; set; } = new();
|
||||
public PlateOption ChosenSize { get; set; }
|
||||
public double NetCost { get; set; }
|
||||
public double Utilization { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace OpenNest
|
||||
{
|
||||
public class PlateOption
|
||||
{
|
||||
public double Width { get; set; }
|
||||
public double Length { get; set; }
|
||||
public double Cost { get; set; }
|
||||
|
||||
public double Area => Width * Length;
|
||||
}
|
||||
}
|
||||
@@ -139,28 +139,63 @@ namespace OpenNest
|
||||
var bestFits = BestFitCache.GetOrCompute(
|
||||
drawing, Plate.Size.Length, Plate.Size.Width, Plate.PartSpacing);
|
||||
|
||||
var best = bestFits.FirstOrDefault(r => r.Keep);
|
||||
var best = SelectBestFitPair(bestFits);
|
||||
if (best == null)
|
||||
return null;
|
||||
|
||||
var parts = best.BuildParts(drawing);
|
||||
// BuildParts produces landscape orientation (Width >= Height).
|
||||
// Try both landscape and portrait (90° rotated) and let the
|
||||
// engine's comparer pick the better orientation.
|
||||
var landscape = best.BuildParts(drawing);
|
||||
var portrait = RotatePair90(landscape);
|
||||
|
||||
// BuildParts positions at origin — offset to work area.
|
||||
var lFits = TryOffsetToWorkArea(landscape, workArea);
|
||||
var pFits = TryOffsetToWorkArea(portrait, workArea);
|
||||
|
||||
if (!lFits && !pFits)
|
||||
return null;
|
||||
if (lFits && pFits)
|
||||
return IsBetterFill(portrait, landscape, workArea) ? portrait : landscape;
|
||||
return lFits ? landscape : portrait;
|
||||
}
|
||||
|
||||
private static List<Part> RotatePair90(List<Part> parts)
|
||||
{
|
||||
var rotated = new List<Part>(parts.Count);
|
||||
foreach (var p in parts)
|
||||
rotated.Add((Part)p.Clone());
|
||||
|
||||
var bbox = ((IEnumerable<IBoundable>)rotated).GetBoundingBox();
|
||||
var center = bbox.Center;
|
||||
|
||||
foreach (var p in rotated)
|
||||
p.Rotate(-Angle.HalfPI, center);
|
||||
|
||||
var newBbox = ((IEnumerable<IBoundable>)rotated).GetBoundingBox();
|
||||
var offset = new Vector(-newBbox.Left, -newBbox.Bottom);
|
||||
foreach (var p in rotated)
|
||||
{
|
||||
p.Offset(offset);
|
||||
p.UpdateBounds();
|
||||
}
|
||||
|
||||
return rotated;
|
||||
}
|
||||
|
||||
private static bool TryOffsetToWorkArea(List<Part> parts, Box workArea)
|
||||
{
|
||||
var bbox = ((IEnumerable<IBoundable>)parts).GetBoundingBox();
|
||||
if (bbox.Width > workArea.Width + Tolerance.Epsilon ||
|
||||
bbox.Length > workArea.Length + Tolerance.Epsilon)
|
||||
return false;
|
||||
|
||||
var offset = workArea.Location - bbox.Location;
|
||||
foreach (var p in parts)
|
||||
{
|
||||
p.Offset(offset);
|
||||
p.UpdateBounds();
|
||||
}
|
||||
|
||||
// Verify pair fits in work area.
|
||||
bbox = ((IEnumerable<IBoundable>)parts).GetBoundingBox();
|
||||
if (bbox.Width > workArea.Width + Tolerance.Epsilon ||
|
||||
bbox.Length > workArea.Length + Tolerance.Epsilon)
|
||||
return null;
|
||||
|
||||
return parts;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -309,14 +344,18 @@ namespace OpenNest
|
||||
// during progress reporting.
|
||||
PhaseResults.Add(phaseResult);
|
||||
|
||||
if (context.Policy.Comparer.IsBetter(result, context.CurrentBest, context.WorkArea))
|
||||
// FillContext.ReportProgress updates CurrentBest during the
|
||||
// strategy's angle sweep. This catches strategies that return a
|
||||
// result without reporting it (e.g. RectBestFit).
|
||||
var improved = context.Policy.Comparer.IsBetter(result, context.CurrentBest, context.WorkArea);
|
||||
if (improved)
|
||||
{
|
||||
context.CurrentBest = result;
|
||||
context.CurrentBestScore = FillScore.Compute(result, context.WorkArea);
|
||||
context.WinnerPhase = strategy.Phase;
|
||||
}
|
||||
|
||||
if (context.CurrentBest != null && context.CurrentBest.Count > 0)
|
||||
if (improved && context.CurrentBest != null && context.CurrentBest.Count > 0)
|
||||
{
|
||||
ReportProgress(context.Progress, new ProgressReport
|
||||
{
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace OpenNest.Engine.Fill
|
||||
// rectangular obstacle boundary. Without this, gaps between
|
||||
// individual bounding boxes cause the next drawing to fill
|
||||
// into inter-row spaces, producing an interleaved layout.
|
||||
if (placed.Count > 1)
|
||||
if (placed.Count > 2)
|
||||
RemoveTopmostPart(placed);
|
||||
|
||||
allParts.AddRange(placed);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.Engine;
|
||||
using OpenNest.Engine.BestFit;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
@@ -26,6 +27,20 @@ namespace OpenNest
|
||||
|
||||
public override ShrinkAxis TrimAxis => ShrinkAxis.Length;
|
||||
|
||||
protected override BestFitResult SelectBestFitPair(List<BestFitResult> results)
|
||||
{
|
||||
BestFitResult best = null;
|
||||
|
||||
foreach (var r in results)
|
||||
{
|
||||
if (!r.Keep) continue;
|
||||
if (best == null || r.BoundingHeight < best.BoundingHeight)
|
||||
best = r;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
public override List<double> BuildAngles(NestItem item, ClassificationResult classification, Box workArea)
|
||||
{
|
||||
var baseAngles = new List<double> { classification.PrimaryAngle, classification.PrimaryAngle + Angle.HalfPI };
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace OpenNest
|
||||
|
||||
protected FillPolicy BuildPolicy() => new FillPolicy(Comparer, PreferredDirection);
|
||||
|
||||
protected virtual BestFitResult SelectBestFitPair(List<BestFitResult> results)
|
||||
{
|
||||
return results.FirstOrDefault(r => r.Keep);
|
||||
}
|
||||
|
||||
// --- Virtual methods (side-effect-free, return parts) ---
|
||||
|
||||
public virtual List<Part> Fill(NestItem item, Box workArea,
|
||||
@@ -333,7 +338,7 @@ namespace OpenNest
|
||||
|
||||
var bestFits = BestFitCache.GetOrCompute(
|
||||
item.Drawing, Plate.Size.Length, Plate.Size.Width, Plate.PartSpacing);
|
||||
var bestFit = bestFits.FirstOrDefault(r => r.Keep);
|
||||
var bestFit = SelectBestFitPair(bestFits);
|
||||
if (bestFit == null) continue;
|
||||
|
||||
var parts = bestFit.BuildParts(item.Drawing);
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
using OpenNest.Engine;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenNest
|
||||
{
|
||||
public static class PlateOptimizer
|
||||
{
|
||||
public static PlateOptimizerResult Optimize(
|
||||
List<NestItem> items,
|
||||
List<PlateOption> plateOptions,
|
||||
double salvageRate,
|
||||
Plate templatePlate,
|
||||
IProgress<NestProgress> progress = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (items == null || items.Count == 0 || plateOptions == null || plateOptions.Count == 0)
|
||||
return null;
|
||||
|
||||
// Find the minimum dimension needed to fit the largest part.
|
||||
var minPartWidth = 0.0;
|
||||
var minPartLength = 0.0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Quantity <= 0) continue;
|
||||
var bb = item.Drawing.Program.BoundingBox();
|
||||
var shortSide = System.Math.Min(bb.Width, bb.Length);
|
||||
var longSide = System.Math.Max(bb.Width, bb.Length);
|
||||
if (shortSide > minPartWidth) minPartWidth = shortSide;
|
||||
if (longSide > minPartLength) minPartLength = longSide;
|
||||
}
|
||||
|
||||
// Sort candidates by cost ascending — try cheapest first.
|
||||
var candidates = plateOptions
|
||||
.Where(o => FitsPart(o, minPartWidth, minPartLength, templatePlate.EdgeSpacing))
|
||||
.OrderBy(o => o.Cost)
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
return null;
|
||||
|
||||
PlateOptimizerResult best = null;
|
||||
|
||||
foreach (var option in candidates)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
var result = TryPlateSize(option, items, salvageRate, templatePlate, progress, token);
|
||||
if (result == null)
|
||||
continue;
|
||||
|
||||
if (IsBetter(result, best))
|
||||
best = result;
|
||||
|
||||
// Early exit: when salvage is zero, cheapest plate that fits everything wins.
|
||||
// With salvage > 0, larger plates may have lower net cost, so keep searching.
|
||||
if (salvageRate <= 0)
|
||||
{
|
||||
var allPlaced = items.All(i => i.Quantity <= 0 ||
|
||||
result.Parts.Count(p => p.BaseDrawing.Name == i.Drawing.Name) >= i.Quantity);
|
||||
if (allPlaced)
|
||||
{
|
||||
Debug.WriteLine($"[PlateOptimizer] Early exit: {option.Width}x{option.Length} placed all items");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
private static bool FitsPart(PlateOption option, double minWidth, double minLength, Spacing edgeSpacing)
|
||||
{
|
||||
var workW = option.Width - edgeSpacing.Left - edgeSpacing.Right;
|
||||
var workL = option.Length - edgeSpacing.Top - edgeSpacing.Bottom;
|
||||
|
||||
// Part fits in either orientation.
|
||||
var fitsNormal = workW >= minWidth - Tolerance.Epsilon && workL >= minLength - Tolerance.Epsilon;
|
||||
var fitsRotated = workW >= minLength - Tolerance.Epsilon && workL >= minWidth - Tolerance.Epsilon;
|
||||
return fitsNormal || fitsRotated;
|
||||
}
|
||||
|
||||
private static PlateOptimizerResult TryPlateSize(
|
||||
PlateOption option,
|
||||
List<NestItem> items,
|
||||
double salvageRate,
|
||||
Plate templatePlate,
|
||||
IProgress<NestProgress> progress,
|
||||
CancellationToken token)
|
||||
{
|
||||
// Create a temporary plate with candidate size + settings from template.
|
||||
var tempPlate = new Plate(option.Width, option.Length)
|
||||
{
|
||||
PartSpacing = templatePlate.PartSpacing,
|
||||
EdgeSpacing = new Spacing
|
||||
{
|
||||
Left = templatePlate.EdgeSpacing.Left,
|
||||
Right = templatePlate.EdgeSpacing.Right,
|
||||
Top = templatePlate.EdgeSpacing.Top,
|
||||
Bottom = templatePlate.EdgeSpacing.Bottom,
|
||||
},
|
||||
};
|
||||
|
||||
// Clone items so the dry run doesn't mutate originals.
|
||||
var clonedItems = items.Select(i => new NestItem
|
||||
{
|
||||
Drawing = i.Drawing, // share Drawing reference for BestFitCache compatibility
|
||||
Priority = i.Priority,
|
||||
Quantity = i.Quantity,
|
||||
StepAngle = i.StepAngle,
|
||||
RotationStart = i.RotationStart,
|
||||
RotationEnd = i.RotationEnd,
|
||||
}).ToList();
|
||||
|
||||
var engine = NestEngineRegistry.Create(tempPlate);
|
||||
var parts = engine.Nest(clonedItems, progress, token);
|
||||
|
||||
if (parts == null || parts.Count == 0)
|
||||
return null;
|
||||
|
||||
var workArea = tempPlate.WorkArea();
|
||||
var plateArea = workArea.Width * workArea.Length;
|
||||
var partsArea = 0.0;
|
||||
foreach (var part in parts)
|
||||
partsArea += part.BoundingBox.Area();
|
||||
|
||||
var remnantArea = plateArea - partsArea;
|
||||
var costPerSqUnit = option.Cost / option.Area;
|
||||
var netCost = option.Cost - (remnantArea * costPerSqUnit * salvageRate);
|
||||
|
||||
Debug.WriteLine($"[PlateOptimizer] {option.Width}x{option.Length} ${option.Cost}: " +
|
||||
$"{parts.Count} parts, util={partsArea / plateArea:P1}, net=${netCost:F2}");
|
||||
|
||||
return new PlateOptimizerResult
|
||||
{
|
||||
Parts = parts,
|
||||
ChosenSize = option,
|
||||
NetCost = netCost,
|
||||
Utilization = plateArea > 0 ? partsArea / plateArea : 0,
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsBetter(PlateOptimizerResult candidate, PlateOptimizerResult current)
|
||||
{
|
||||
if (current == null) return true;
|
||||
|
||||
// 1. More parts placed is always better.
|
||||
if (candidate.Parts.Count != current.Parts.Count)
|
||||
return candidate.Parts.Count > current.Parts.Count;
|
||||
|
||||
// 2. Lower net cost.
|
||||
if (!candidate.NetCost.IsEqualTo(current.NetCost))
|
||||
return candidate.NetCost < current.NetCost;
|
||||
|
||||
// 3. Smaller plate area as tiebreak.
|
||||
return candidate.ChosenSize.Area < current.ChosenSize.Area;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ public class ColumnFillStrategy : IFillStrategy
|
||||
|
||||
public List<Part> Fill(FillContext context)
|
||||
{
|
||||
if (context.PartType == PartType.Rectangle)
|
||||
return null;
|
||||
|
||||
var filler = new StripeFiller(context, NestDirection.Vertical) { CompleteStripesOnly = true };
|
||||
return filler.Fill();
|
||||
}
|
||||
|
||||
@@ -32,16 +32,29 @@ namespace OpenNest.Engine.Strategies
|
||||
/// <summary>
|
||||
/// Standard progress reporting for strategies and fillers. Reports intermediate
|
||||
/// results using the current ActivePhase, PlateNumber, and WorkArea.
|
||||
/// When the reported parts beat the current pipeline best, promotes the
|
||||
/// result to IsOverallBest so the UI updates immediately.
|
||||
/// </summary>
|
||||
public void ReportProgress(List<Part> parts, string description)
|
||||
{
|
||||
var isNewBest = parts != null && parts.Count > 0
|
||||
&& Policy.Comparer.IsBetter(parts, CurrentBest, WorkArea);
|
||||
|
||||
if (isNewBest)
|
||||
{
|
||||
CurrentBest = parts;
|
||||
CurrentBestScore = FillScore.Compute(parts, WorkArea);
|
||||
WinnerPhase = ActivePhase;
|
||||
}
|
||||
|
||||
NestEngineBase.ReportProgress(Progress, new ProgressReport
|
||||
{
|
||||
Phase = ActivePhase,
|
||||
PlateNumber = PlateNumber,
|
||||
Parts = parts,
|
||||
Parts = isNewBest ? parts : CurrentBest,
|
||||
WorkArea = WorkArea,
|
||||
Description = description,
|
||||
IsOverallBest = isNewBest,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ public class RowFillStrategy : IFillStrategy
|
||||
|
||||
public List<Part> Fill(FillContext context)
|
||||
{
|
||||
if (context.PartType == PartType.Rectangle)
|
||||
return null;
|
||||
|
||||
var filler = new StripeFiller(context, NestDirection.Horizontal) { CompleteStripesOnly = true };
|
||||
return filler.Fill();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.Engine;
|
||||
using OpenNest.Engine.BestFit;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
@@ -24,6 +25,20 @@ namespace OpenNest
|
||||
|
||||
public override NestDirection? PreferredDirection => NestDirection.Horizontal;
|
||||
|
||||
protected override BestFitResult SelectBestFitPair(List<BestFitResult> results)
|
||||
{
|
||||
BestFitResult best = null;
|
||||
|
||||
foreach (var r in results)
|
||||
{
|
||||
if (!r.Keep) continue;
|
||||
if (best == null || r.BoundingHeight < best.BoundingHeight)
|
||||
best = r;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
public override List<double> BuildAngles(NestItem item, ClassificationResult classification, Box workArea)
|
||||
{
|
||||
var baseAngles = new List<double> { classification.PrimaryAngle, classification.PrimaryAngle + Angle.HalfPI };
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace OpenNest.IO
|
||||
public PlateDefaultsDto PlateDefaults { get; init; } = new();
|
||||
public List<DrawingDto> Drawings { get; init; } = new();
|
||||
public List<PlateDto> Plates { get; init; } = new();
|
||||
public List<PlateOptionDto> PlateOptions { get; init; } = new();
|
||||
public double SalvageRate { get; init; } = 0.5;
|
||||
}
|
||||
|
||||
public record PlateDefaultsDto
|
||||
@@ -153,6 +155,13 @@ namespace OpenNest.IO
|
||||
public string NoteText { get; init; } = "";
|
||||
}
|
||||
|
||||
public record PlateOptionDto
|
||||
{
|
||||
public double Width { get; init; }
|
||||
public double Length { get; init; }
|
||||
public double Cost { get; init; }
|
||||
}
|
||||
|
||||
public record BestFitSetDto
|
||||
{
|
||||
public double PlateWidth { get; init; }
|
||||
|
||||
@@ -192,6 +192,18 @@ namespace OpenNest.IO
|
||||
nest.PlateDefaults.PartSpacing = pd.PartSpacing;
|
||||
nest.PlateDefaults.EdgeSpacing = new Spacing(pd.EdgeSpacing.Left, pd.EdgeSpacing.Bottom, pd.EdgeSpacing.Right, pd.EdgeSpacing.Top);
|
||||
|
||||
// Plate optimizer settings
|
||||
nest.SalvageRate = dto.SalvageRate;
|
||||
if (dto.PlateOptions != null)
|
||||
{
|
||||
nest.PlateOptions = dto.PlateOptions.Select(o => new PlateOption
|
||||
{
|
||||
Width = o.Width,
|
||||
Length = o.Length,
|
||||
Cost = o.Cost,
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// Drawings
|
||||
foreach (var d in drawingMap.OrderBy(k => k.Key))
|
||||
nest.Drawings.Add(d.Value);
|
||||
|
||||
@@ -88,7 +88,14 @@ namespace OpenNest.IO
|
||||
},
|
||||
PlateDefaults = BuildPlateDefaultsDto(),
|
||||
Drawings = BuildDrawingDtos(),
|
||||
Plates = BuildPlateDtos()
|
||||
Plates = BuildPlateDtos(),
|
||||
PlateOptions = nest.PlateOptions?.Select(o => new PlateOptionDto
|
||||
{
|
||||
Width = o.Width,
|
||||
Length = o.Length,
|
||||
Cost = o.Cost,
|
||||
}).ToList() ?? new(),
|
||||
SalvageRate = nest.SalvageRate,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -168,9 +175,15 @@ namespace OpenNest.IO
|
||||
private List<PlateDto> BuildPlateDtos()
|
||||
{
|
||||
var list = new List<PlateDto>();
|
||||
var id = 0;
|
||||
for (var i = 0; i < nest.Plates.Count; i++)
|
||||
{
|
||||
var plate = nest.Plates[i];
|
||||
|
||||
if (plate.Parts.Count(p => !p.BaseDrawing.IsCutOff) == 0 && plate.CutOffs.Count == 0)
|
||||
continue;
|
||||
|
||||
id++;
|
||||
var parts = new List<PartDto>();
|
||||
foreach (var part in plate.Parts.Where(p => !p.BaseDrawing.IsCutOff))
|
||||
{
|
||||
@@ -201,7 +214,7 @@ namespace OpenNest.IO
|
||||
|
||||
list.Add(new PlateDto
|
||||
{
|
||||
Id = i + 1,
|
||||
Id = id,
|
||||
Size = new SizeDto { Width = plate.Size.Width, Length = plate.Size.Length },
|
||||
Quadrant = plate.Quadrant,
|
||||
Quantity = plate.Quantity,
|
||||
|
||||
@@ -112,6 +112,7 @@ namespace OpenNest.Mcp.Tools
|
||||
|
||||
var drawingName = name ?? Path.GetFileNameWithoutExtension(path);
|
||||
var drawing = new Drawing(drawingName, pgm);
|
||||
drawing.Color = Drawing.GetNextColor();
|
||||
_session.Drawings.Add(drawing);
|
||||
|
||||
var bbox = pgm.BoundingBox();
|
||||
@@ -155,6 +156,7 @@ namespace OpenNest.Mcp.Tools
|
||||
if (pgm == null)
|
||||
return "Error: failed to parse G-code";
|
||||
var gcodeDrawing = new Drawing(name, pgm);
|
||||
gcodeDrawing.Color = Drawing.GetNextColor();
|
||||
_session.Drawings.Add(gcodeDrawing);
|
||||
var gcodeBbox = pgm.BoundingBox();
|
||||
return $"Created drawing '{name}': bbox={gcodeBbox.Width:F2} x {gcodeBbox.Length:F2}";
|
||||
@@ -164,6 +166,7 @@ namespace OpenNest.Mcp.Tools
|
||||
}
|
||||
|
||||
var drawing = shapeDef.GetDrawing();
|
||||
drawing.Color = Drawing.GetNextColor();
|
||||
_session.Drawings.Add(drawing);
|
||||
|
||||
var bbox = drawing.Program.BoundingBox();
|
||||
|
||||
@@ -13,6 +13,9 @@ public class NestResponsePersistenceTests
|
||||
{
|
||||
var nest = new Nest("test-nest");
|
||||
var plate = new Plate(new Size(60, 120));
|
||||
var drawing = new Drawing("test-part");
|
||||
nest.Drawings.Add(drawing);
|
||||
plate.Parts.Add(new Part(drawing));
|
||||
nest.Plates.Add(plate);
|
||||
|
||||
var request = new NestRequest
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests.Engine;
|
||||
|
||||
public class PlateOptimizerTests
|
||||
{
|
||||
private static Drawing MakeRectDrawing(double w, double h, string name = "rect")
|
||||
{
|
||||
var pgm = new OpenNest.CNC.Program();
|
||||
pgm.Codes.Add(new OpenNest.CNC.RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, 0)));
|
||||
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(w, h)));
|
||||
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, h)));
|
||||
pgm.Codes.Add(new OpenNest.CNC.LinearMove(new Vector(0, 0)));
|
||||
return new Drawing(name, pgm);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PicksCheapestPlateThatFitsParts()
|
||||
{
|
||||
var options = new List<PlateOption>
|
||||
{
|
||||
new() { Width = 20, Length = 20, Cost = 100 },
|
||||
new() { Width = 40, Length = 40, Cost = 400 },
|
||||
};
|
||||
|
||||
var templatePlate = new Plate(40, 40) { PartSpacing = 0 };
|
||||
var items = new List<NestItem>
|
||||
{
|
||||
new() { Drawing = MakeRectDrawing(10, 10), Quantity = 1 }
|
||||
};
|
||||
|
||||
var result = PlateOptimizer.Optimize(items, options, 0.0, templatePlate);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(20, result.ChosenSize.Width);
|
||||
Assert.True(result.Parts.Count >= 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrefersMorePartsOverCheaperPlate()
|
||||
{
|
||||
var options = new List<PlateOption>
|
||||
{
|
||||
new() { Width = 12, Length = 12, Cost = 50 },
|
||||
new() { Width = 24, Length = 12, Cost = 100 },
|
||||
};
|
||||
|
||||
var templatePlate = new Plate(24, 12) { PartSpacing = 0 };
|
||||
var items = new List<NestItem>
|
||||
{
|
||||
new() { Drawing = MakeRectDrawing(10, 10), Quantity = 2 }
|
||||
};
|
||||
|
||||
var result = PlateOptimizer.Optimize(items, options, 0.0, templatePlate);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(24, result.ChosenSize.Width);
|
||||
Assert.Equal(2, result.Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SalvageRateReducesNetCost()
|
||||
{
|
||||
// Small: 20x20=400sqin, cost $400. Part=10x10=100sqin. Remnant=300.
|
||||
// Net = 400 - 300*(400/400)*1.0 = 400-300 = 100
|
||||
// Large: 40x40=1600sqin, cost $800. Part=10x10=100sqin. Remnant=1500.
|
||||
// Net = 800 - 1500*(800/1600)*1.0 = 800-750 = 50
|
||||
var options = new List<PlateOption>
|
||||
{
|
||||
new() { Width = 20, Length = 20, Cost = 400 },
|
||||
new() { Width = 40, Length = 40, Cost = 800 },
|
||||
};
|
||||
|
||||
var templatePlate = new Plate(40, 40) { PartSpacing = 0 };
|
||||
templatePlate.EdgeSpacing = new Spacing();
|
||||
var items = new List<NestItem>
|
||||
{
|
||||
new() { Drawing = MakeRectDrawing(10, 10), Quantity = 1 }
|
||||
};
|
||||
|
||||
var result = PlateOptimizer.Optimize(items, options, 1.0, templatePlate);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(40, result.ChosenSize.Width);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkipsPlatesThatAreTooSmall()
|
||||
{
|
||||
var options = new List<PlateOption>
|
||||
{
|
||||
new() { Width = 20, Length = 20, Cost = 100 },
|
||||
new() { Width = 40, Length = 40, Cost = 400 },
|
||||
};
|
||||
|
||||
var templatePlate = new Plate(40, 40) { PartSpacing = 0 };
|
||||
var items = new List<NestItem>
|
||||
{
|
||||
new() { Drawing = MakeRectDrawing(30, 30), Quantity = 1 }
|
||||
};
|
||||
|
||||
var result = PlateOptimizer.Optimize(items, options, 0.0, templatePlate);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(40, result.ChosenSize.Width);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsNullWhenNoPlatesFit()
|
||||
{
|
||||
var options = new List<PlateOption>
|
||||
{
|
||||
new() { Width = 10, Length = 10, Cost = 50 },
|
||||
};
|
||||
|
||||
var templatePlate = new Plate(10, 10) { PartSpacing = 0 };
|
||||
var items = new List<NestItem>
|
||||
{
|
||||
new() { Drawing = MakeRectDrawing(20, 20), Quantity = 1 }
|
||||
};
|
||||
|
||||
var result = PlateOptimizer.Optimize(items, options, 0.0, templatePlate);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Collections;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests;
|
||||
|
||||
public class PlateManagerTests
|
||||
{
|
||||
private static Nest CreateNest()
|
||||
{
|
||||
var nest = new Nest("test");
|
||||
return nest;
|
||||
}
|
||||
|
||||
private static Part MakePart()
|
||||
{
|
||||
var pgm = new Program();
|
||||
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(10, 0)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
|
||||
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
var drawing = new Drawing("test", pgm);
|
||||
return new Part(drawing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_EmptyNest_CurrentIndexZero()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_NestWithPlates_CurrentIndexZero()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CurrentPlate_ReturnsPlateAtCurrentIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.Same(plate, mgr.CurrentPlate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CurrentPlate_EmptyNest_ReturnsNull()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.Null(mgr.CurrentPlate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Count_DelegatesToNestPlates()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.Equal(2, mgr.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadFirst_SetsCurrentIndexToZero()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadLast();
|
||||
mgr.LoadFirst();
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadLast_SetsCurrentIndexToLastPlate()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadLast();
|
||||
Assert.Equal(2, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadNext_AdvancesIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
var result = mgr.LoadNext();
|
||||
Assert.True(result);
|
||||
Assert.Equal(1, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadNext_AtEnd_ReturnsFalse()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
var result = mgr.LoadNext();
|
||||
Assert.False(result);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadPrevious_DecrementsIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadLast();
|
||||
var result = mgr.LoadPrevious();
|
||||
Assert.True(result);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadPrevious_AtStart_ReturnsFalse()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
var result = mgr.LoadPrevious();
|
||||
Assert.False(result);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadAt_SetsExactIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadAt(2);
|
||||
Assert.Equal(2, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsFirst_WhenAtStart_ReturnsTrue()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.True(mgr.IsFirst);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsLast_WhenAtEnd_ReturnsTrue()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
Assert.True(mgr.IsLast);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsFirst_WhenNotAtStart_ReturnsFalse()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadLast();
|
||||
Assert.False(mgr.IsFirst);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Navigation_FiresCurrentPlateChanged()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
PlateChangedEventArgs received = null;
|
||||
mgr.CurrentPlateChanged += (s, e) => received = e;
|
||||
mgr.LoadNext();
|
||||
Assert.NotNull(received);
|
||||
Assert.Equal(1, received.Index);
|
||||
Assert.Same(nest.Plates[1], received.Plate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_UnsubscribesFromPlateEvents()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
using var mgr = new PlateManager(nest);
|
||||
var eventFired = false;
|
||||
mgr.PlateListChanged += (s, e) => eventFired = true;
|
||||
mgr.Dispose();
|
||||
nest.CreatePlate();
|
||||
Assert.False(eventFired);
|
||||
}
|
||||
|
||||
// Task 3: Sentinel plate invariant
|
||||
|
||||
[Fact]
|
||||
public void EnsureSentinel_EmptyNest_CreatesOnePlate()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(1, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[0].Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureSentinel_LastPlateHasParts_CreatesNewEmpty()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureSentinel_TwoTrailingEmpties_TrimsToOne()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(1, nest.Plates.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureSentinel_PreservesCurrentIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
Assert.Same(plate, mgr.CurrentPlate);
|
||||
}
|
||||
|
||||
// Task 4: Reactive tail-plate subscriptions
|
||||
|
||||
[Fact]
|
||||
public void PartAddedToLastPlate_CreatesSentinel()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
var initialCount = nest.Plates.Count;
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
Assert.Equal(initialCount + 1, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PartRemovedFromSecondToLast_TrimsExtraEmpty()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
nest.Plates[0].Parts.RemoveAt(0);
|
||||
Assert.Equal(1, nest.Plates.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReactiveSubscription_ResubscribesAfterPlateListChange()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
plate1.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
Assert.Equal(3, nest.Plates.Count);
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
Assert.Equal(4, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
}
|
||||
|
||||
// Task 5: Batch mode and plate operations
|
||||
|
||||
[Fact]
|
||||
public void BeginBatch_DefersSentinelEnforcement()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
|
||||
mgr.BeginBatch();
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndBatch_EnforcesSentinelAndFiresEvents()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
|
||||
mgr.BeginBatch();
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
|
||||
var listChangedCount = 0;
|
||||
mgr.PlateListChanged += (s, e) => listChangedCount++;
|
||||
|
||||
mgr.EndBatch();
|
||||
|
||||
Assert.Equal(3, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
Assert.True(listChangedCount > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreateEmpty_ReturnsSentinelWhenEmpty()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
plate1.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
|
||||
var result = mgr.GetOrCreateEmpty();
|
||||
Assert.Same(nest.Plates[^1], result);
|
||||
Assert.Equal(0, result.Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreateEmpty_NoEmptyPlate_CreatesNew()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
plate1.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
|
||||
var result = mgr.GetOrCreateEmpty();
|
||||
Assert.Equal(0, result.Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveCurrent_RemovesPlateAndClampsIndex()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
plate1.Parts.Add(MakePart());
|
||||
var plate2 = nest.CreatePlate();
|
||||
plate2.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.LoadLast();
|
||||
|
||||
mgr.RemoveCurrent();
|
||||
|
||||
// plate1 + sentinel (created by reactive sentinel enforcement)
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
Assert.Same(plate1, nest.Plates[0]);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
Assert.Equal(0, mgr.CurrentIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRemoveCurrent_OnlyIfMultiplePlatesAndHasParts()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
using var mgr = new PlateManager(nest);
|
||||
|
||||
Assert.False(mgr.CanRemoveCurrent);
|
||||
|
||||
plate1.Parts.Add(MakePart());
|
||||
Assert.False(mgr.CanRemoveCurrent);
|
||||
|
||||
nest.CreatePlate();
|
||||
// Auto-navigated to plate2 (empty), go back to plate1 (has parts)
|
||||
mgr.LoadFirst();
|
||||
Assert.True(mgr.CanRemoveCurrent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveEmptyPlates_SentinelIsRestored()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate = nest.CreatePlate();
|
||||
plate.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
|
||||
// RemoveEmptyPlates removes the sentinel
|
||||
nest.Plates.RemoveEmptyPlates();
|
||||
|
||||
// Sentinel should be restored reactively by OnPlateRemoved
|
||||
Assert.Equal(2, nest.Plates.Count);
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlateRemoval_RefreshesTailSubscriptions()
|
||||
{
|
||||
var nest = CreateNest();
|
||||
var plate1 = nest.CreatePlate();
|
||||
plate1.Parts.Add(MakePart());
|
||||
var plate2 = nest.CreatePlate();
|
||||
plate2.Parts.Add(MakePart());
|
||||
using var mgr = new PlateManager(nest);
|
||||
mgr.EnsureSentinel();
|
||||
// plates: [plate1(parts), plate2(parts), sentinel(empty)]
|
||||
|
||||
// Remove plate2 — tail subscriptions should refresh
|
||||
nest.Plates.Remove(plate2);
|
||||
// plates: [plate1(parts), sentinel(empty)]
|
||||
|
||||
// Verify reactive subscriptions still work on the new tail
|
||||
nest.Plates[^1].Parts.Add(MakePart());
|
||||
Assert.Equal(0, nest.Plates[^1].Parts.Count);
|
||||
}
|
||||
}
|
||||
@@ -79,21 +79,7 @@ int RunDataCollection(string dir, string dbPath, string saveDir, double s, strin
|
||||
Console.WriteLine($"Using template: {template}");
|
||||
}
|
||||
|
||||
var PartColors = new[]
|
||||
{
|
||||
Color.FromArgb(205, 92, 92),
|
||||
Color.FromArgb(148, 103, 189),
|
||||
Color.FromArgb(75, 180, 175),
|
||||
Color.FromArgb(210, 190, 75),
|
||||
Color.FromArgb(190, 85, 175),
|
||||
Color.FromArgb(185, 115, 85),
|
||||
Color.FromArgb(120, 100, 190),
|
||||
Color.FromArgb(200, 100, 140),
|
||||
Color.FromArgb(80, 175, 155),
|
||||
Color.FromArgb(195, 160, 85),
|
||||
Color.FromArgb(175, 95, 160),
|
||||
Color.FromArgb(215, 130, 130),
|
||||
};
|
||||
var PartColors = Drawing.PartColors;
|
||||
|
||||
var sheetSuite = new[]
|
||||
{
|
||||
|
||||
+1
-15
@@ -13,21 +13,7 @@ namespace OpenNest
|
||||
private Color edgeSpacingColor;
|
||||
private Color previewPartColor;
|
||||
|
||||
public static readonly Color[] PartColors = new Color[]
|
||||
{
|
||||
Color.FromArgb(205, 92, 92), // Indian Red
|
||||
Color.FromArgb(148, 103, 189), // Medium Purple
|
||||
Color.FromArgb(75, 180, 175), // Teal
|
||||
Color.FromArgb(210, 190, 75), // Goldenrod
|
||||
Color.FromArgb(190, 85, 175), // Orchid
|
||||
Color.FromArgb(185, 115, 85), // Sienna
|
||||
Color.FromArgb(120, 100, 190), // Slate Blue
|
||||
Color.FromArgb(200, 100, 140), // Rose
|
||||
Color.FromArgb(80, 175, 155), // Sea Green
|
||||
Color.FromArgb(195, 160, 85), // Dark Khaki
|
||||
Color.FromArgb(175, 95, 160), // Plum
|
||||
Color.FromArgb(215, 130, 130), // Light Coral
|
||||
};
|
||||
public static Color[] PartColors => Drawing.PartColors;
|
||||
|
||||
public static readonly ColorScheme Default = new ColorScheme
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Drawing;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenNest.Controls
|
||||
@@ -22,8 +23,25 @@ namespace OpenNest.Controls
|
||||
|
||||
imageSize = new Size(ItemHeight, ItemHeight - 10);
|
||||
nameFont = new Font(Font.FontFamily, 10, FontStyle.Bold);
|
||||
|
||||
var menu = new ContextMenuStrip();
|
||||
var deleteItem = new ToolStripMenuItem("Delete");
|
||||
deleteItem.Click += (s, e) =>
|
||||
{
|
||||
if (SelectedItem is Drawing drawing)
|
||||
DeleteRequested?.Invoke(this, drawing);
|
||||
};
|
||||
menu.Opening += (s, e) =>
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
e.Cancel = true;
|
||||
};
|
||||
menu.Items.Add(deleteItem);
|
||||
ContextMenuStrip = menu;
|
||||
}
|
||||
|
||||
public event EventHandler<Drawing> DeleteRequested;
|
||||
|
||||
public Units Units { get; set; }
|
||||
|
||||
public bool HideDepletedParts { get; set; }
|
||||
@@ -43,15 +61,30 @@ namespace OpenNest.Controls
|
||||
var isSelected = (e.State & DrawItemState.Selected) != 0;
|
||||
Brush bgBrush;
|
||||
|
||||
if (isSelected)
|
||||
bgBrush = SystemBrushes.Highlight;
|
||||
else if (!HideQuantity && dwg.Quantity.Nested > 0 && dwg.Quantity.Remaining == 0)
|
||||
if (!HideQuantity && dwg.Quantity.Nested > 0 && dwg.Quantity.Remaining == 0)
|
||||
bgBrush = SystemBrushes.Info;
|
||||
else
|
||||
bgBrush = Brushes.White;
|
||||
|
||||
e.Graphics.FillRectangle(bgBrush, e.Bounds);
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
var borderRect = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width - 1, e.Bounds.Height - 1);
|
||||
using var borderPen = new Pen(SystemColors.Highlight, 2);
|
||||
e.Graphics.DrawRectangle(borderPen, borderRect);
|
||||
}
|
||||
|
||||
if (!HideQuantity && dwg.Quantity.Required > 0)
|
||||
{
|
||||
var barWidth = 4;
|
||||
var barColor = dwg.Quantity.Nested >= dwg.Quantity.Required
|
||||
? Color.FromArgb(76, 175, 80)
|
||||
: Color.FromArgb(255, 152, 0);
|
||||
using var barBrush = new SolidBrush(barColor);
|
||||
e.Graphics.FillRectangle(barBrush, e.Bounds.X, e.Bounds.Y, barWidth, e.Bounds.Height);
|
||||
}
|
||||
|
||||
var pt = new PointF(5, e.Bounds.Y + 5);
|
||||
|
||||
var brush = new SolidBrush(dwg.Color);
|
||||
@@ -66,8 +99,8 @@ namespace OpenNest.Controls
|
||||
|
||||
pt.X += imageSize.Width + 10;
|
||||
|
||||
var textBrush = isSelected ? SystemBrushes.HighlightText : Brushes.Black;
|
||||
var detailBrush = isSelected ? SystemBrushes.HighlightText : Brushes.Gray;
|
||||
var textBrush = Brushes.Black;
|
||||
var detailBrush = Brushes.Gray;
|
||||
|
||||
e.Graphics.DrawString(dwg.Name, nameFont, textBrush, pt);
|
||||
|
||||
|
||||
@@ -152,22 +152,14 @@ namespace OpenNest.Controls
|
||||
UpdateScrollBar();
|
||||
}
|
||||
|
||||
protected override bool IsInputKey(Keys keyData)
|
||||
public void ProcessArrowKey(Keys keyData)
|
||||
{
|
||||
if (keyData == Keys.Up || keyData == Keys.Down)
|
||||
return true;
|
||||
return base.IsInputKey(keyData);
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
if (items.Count == 0) return;
|
||||
|
||||
var newIndex = selectedIndex;
|
||||
if (e.KeyCode == Keys.Down)
|
||||
if (keyData == Keys.Down)
|
||||
newIndex = System.Math.Min(selectedIndex + 1, items.Count - 1);
|
||||
else if (e.KeyCode == Keys.Up)
|
||||
else if (keyData == Keys.Up)
|
||||
newIndex = System.Math.Max(selectedIndex - 1, 0);
|
||||
else
|
||||
return;
|
||||
@@ -179,7 +171,23 @@ namespace OpenNest.Controls
|
||||
Invalidate();
|
||||
SelectedIndexChanged?.Invoke(this, selectedIndex);
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
protected override bool IsInputKey(Keys keyData)
|
||||
{
|
||||
if (keyData == Keys.Up || keyData == Keys.Down)
|
||||
return true;
|
||||
return base.IsInputKey(keyData);
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)
|
||||
{
|
||||
ProcessArrowKey(e.KeyCode);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
|
||||
@@ -412,6 +412,16 @@ namespace OpenNest.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessEscapeKey()
|
||||
{
|
||||
if (currentAction.IsBusy())
|
||||
currentAction.CancelAction();
|
||||
else if (currentAction is ActionSelect && previousAction != null)
|
||||
RestorePreviousAction();
|
||||
else
|
||||
SetAction(typeof(ActionSelect));
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
// Only handle TAB, RETURN, ESC, and ARROW KEYS here.
|
||||
@@ -420,12 +430,7 @@ namespace OpenNest.Controls
|
||||
switch (keyData)
|
||||
{
|
||||
case Keys.Escape:
|
||||
if (currentAction.IsBusy())
|
||||
currentAction.CancelAction();
|
||||
else if (currentAction is ActionSelect && previousAction != null)
|
||||
RestorePreviousAction();
|
||||
else
|
||||
SetAction(typeof(ActionSelect));
|
||||
ProcessEscapeKey();
|
||||
break;
|
||||
|
||||
case Keys.Left:
|
||||
@@ -791,9 +796,11 @@ namespace OpenNest.Controls
|
||||
progressForm.UpdateProgress(p);
|
||||
|
||||
if (p.IsOverallBest)
|
||||
{
|
||||
progressForm.UpdatePreview(p.BestParts);
|
||||
SetActiveParts(p.BestParts);
|
||||
}
|
||||
|
||||
SetActiveParts(p.BestParts);
|
||||
ActiveWorkArea = p.ActiveWorkArea;
|
||||
});
|
||||
|
||||
@@ -837,6 +844,7 @@ namespace OpenNest.Controls
|
||||
ActiveWorkArea = null;
|
||||
progressForm.Close();
|
||||
cts.Dispose();
|
||||
Focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using OpenNest.IO;
|
||||
using OpenNest.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
@@ -12,7 +12,9 @@ namespace OpenNest
|
||||
|
||||
public string LastSavePath { get; private set; }
|
||||
|
||||
public Units Units { get; set; }
|
||||
public string Name => Nest?.Name;
|
||||
|
||||
public bool HasSavePath => File.Exists(LastSavePath);
|
||||
|
||||
public void SaveAs(string path)
|
||||
{
|
||||
|
||||
Generated
+217
-77
@@ -1,16 +1,9 @@
|
||||
namespace OpenNest.Forms
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
partial class AutoNestForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
@@ -22,106 +15,253 @@
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
||||
this.bottomPanel1 = new OpenNest.Controls.BottomPanel();
|
||||
this.acceptButton = new System.Windows.Forms.Button();
|
||||
this.engineLabel = new System.Windows.Forms.Label();
|
||||
this.engineComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.partsGroup = new System.Windows.Forms.GroupBox();
|
||||
this.partsGrid = new System.Windows.Forms.DataGridView();
|
||||
this.summaryLabel = new System.Windows.Forms.Label();
|
||||
this.optionsGroup = new System.Windows.Forms.GroupBox();
|
||||
this.createNewPlatesAsNeededBox = new System.Windows.Forms.CheckBox();
|
||||
this.plateOptimizerGroup = new System.Windows.Forms.GroupBox();
|
||||
this.optimizePlateSizeBox = new System.Windows.Forms.CheckBox();
|
||||
this.plateGrid = new System.Windows.Forms.DataGridView();
|
||||
this.salvageRateLabel = new System.Windows.Forms.Label();
|
||||
this.salvageRateBox = new System.Windows.Forms.TextBox();
|
||||
this.salvageRatePercentLabel = new System.Windows.Forms.Label();
|
||||
this.buttonPanel = new System.Windows.Forms.Panel();
|
||||
this.acceptButton = new System.Windows.Forms.Button();
|
||||
this.cancelButton = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
||||
this.bottomPanel1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.partsGrid)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.plateGrid)).BeginInit();
|
||||
this.partsGroup.SuspendLayout();
|
||||
this.optionsGroup.SuspendLayout();
|
||||
this.plateOptimizerGroup.SuspendLayout();
|
||||
this.buttonPanel.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// dataGridView1
|
||||
//
|
||||
this.dataGridView1.AllowUserToDeleteRows = false;
|
||||
this.dataGridView1.AllowUserToOrderColumns = true;
|
||||
this.dataGridView1.AllowUserToResizeRows = false;
|
||||
this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.dataGridView1.Location = new System.Drawing.Point(0, 0);
|
||||
this.dataGridView1.Name = "dataGridView1";
|
||||
this.dataGridView1.RowHeadersVisible = false;
|
||||
this.dataGridView1.Size = new System.Drawing.Size(545, 385);
|
||||
this.dataGridView1.TabIndex = 0;
|
||||
//
|
||||
// bottomPanel1
|
||||
//
|
||||
this.bottomPanel1.Controls.Add(this.acceptButton);
|
||||
this.bottomPanel1.Controls.Add(this.createNewPlatesAsNeededBox);
|
||||
this.bottomPanel1.Controls.Add(this.cancelButton);
|
||||
this.bottomPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.bottomPanel1.Location = new System.Drawing.Point(0, 335);
|
||||
this.bottomPanel1.Name = "bottomPanel1";
|
||||
this.bottomPanel1.Size = new System.Drawing.Size(545, 50);
|
||||
this.bottomPanel1.TabIndex = 9;
|
||||
//
|
||||
// acceptButton
|
||||
//
|
||||
this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.acceptButton.Location = new System.Drawing.Point(344, 9);
|
||||
this.acceptButton.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.acceptButton.Name = "acceptButton";
|
||||
this.acceptButton.Size = new System.Drawing.Size(90, 28);
|
||||
this.acceptButton.TabIndex = 6;
|
||||
this.acceptButton.Text = "Accept";
|
||||
this.acceptButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
//
|
||||
// engineLabel
|
||||
//
|
||||
this.engineLabel.AutoSize = true;
|
||||
this.engineLabel.Location = new System.Drawing.Point(12, 15);
|
||||
this.engineLabel.Name = "engineLabel";
|
||||
this.engineLabel.Size = new System.Drawing.Size(82, 16);
|
||||
this.engineLabel.TabIndex = 0;
|
||||
this.engineLabel.Text = "Nest Engine:";
|
||||
//
|
||||
// engineComboBox
|
||||
//
|
||||
this.engineComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.engineComboBox.Location = new System.Drawing.Point(100, 12);
|
||||
this.engineComboBox.Name = "engineComboBox";
|
||||
this.engineComboBox.Size = new System.Drawing.Size(200, 24);
|
||||
this.engineComboBox.TabIndex = 1;
|
||||
//
|
||||
// partsGroup
|
||||
//
|
||||
this.partsGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.partsGroup.Controls.Add(this.partsGrid);
|
||||
this.partsGroup.Controls.Add(this.summaryLabel);
|
||||
this.partsGroup.Location = new System.Drawing.Point(12, 42);
|
||||
this.partsGroup.Name = "partsGroup";
|
||||
this.partsGroup.Size = new System.Drawing.Size(556, 210);
|
||||
this.partsGroup.TabIndex = 2;
|
||||
this.partsGroup.TabStop = false;
|
||||
this.partsGroup.Text = "Parts";
|
||||
//
|
||||
// partsGrid
|
||||
//
|
||||
this.partsGrid.AllowUserToAddRows = false;
|
||||
this.partsGrid.AllowUserToDeleteRows = false;
|
||||
this.partsGrid.AllowUserToResizeRows = false;
|
||||
this.partsGrid.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.partsGrid.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.partsGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.partsGrid.Location = new System.Drawing.Point(10, 22);
|
||||
this.partsGrid.Name = "partsGrid";
|
||||
this.partsGrid.RowHeadersVisible = false;
|
||||
this.partsGrid.AutoGenerateColumns = false;
|
||||
this.partsGrid.Size = new System.Drawing.Size(536, 160);
|
||||
this.partsGrid.TabIndex = 0;
|
||||
//
|
||||
// summaryLabel
|
||||
//
|
||||
this.summaryLabel.AutoSize = true;
|
||||
this.summaryLabel.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
this.summaryLabel.Location = new System.Drawing.Point(10, 188);
|
||||
this.summaryLabel.Name = "summaryLabel";
|
||||
this.summaryLabel.Size = new System.Drawing.Size(0, 16);
|
||||
this.summaryLabel.TabIndex = 1;
|
||||
//
|
||||
// optionsGroup
|
||||
//
|
||||
this.optionsGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.optionsGroup.Controls.Add(this.createNewPlatesAsNeededBox);
|
||||
this.optionsGroup.Location = new System.Drawing.Point(12, 258);
|
||||
this.optionsGroup.Name = "optionsGroup";
|
||||
this.optionsGroup.Size = new System.Drawing.Size(556, 48);
|
||||
this.optionsGroup.TabIndex = 3;
|
||||
this.optionsGroup.TabStop = false;
|
||||
this.optionsGroup.Text = "Options";
|
||||
//
|
||||
// createNewPlatesAsNeededBox
|
||||
//
|
||||
this.createNewPlatesAsNeededBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
//
|
||||
this.createNewPlatesAsNeededBox.AutoSize = true;
|
||||
this.createNewPlatesAsNeededBox.Location = new System.Drawing.Point(12, 15);
|
||||
this.createNewPlatesAsNeededBox.Location = new System.Drawing.Point(10, 22);
|
||||
this.createNewPlatesAsNeededBox.Name = "createNewPlatesAsNeededBox";
|
||||
this.createNewPlatesAsNeededBox.Size = new System.Drawing.Size(202, 20);
|
||||
this.createNewPlatesAsNeededBox.TabIndex = 8;
|
||||
this.createNewPlatesAsNeededBox.TabIndex = 0;
|
||||
this.createNewPlatesAsNeededBox.Text = "Create new plates as needed";
|
||||
this.createNewPlatesAsNeededBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
//
|
||||
// plateOptimizerGroup
|
||||
//
|
||||
this.plateOptimizerGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.plateOptimizerGroup.Controls.Add(this.optimizePlateSizeBox);
|
||||
this.plateOptimizerGroup.Controls.Add(this.plateGrid);
|
||||
this.plateOptimizerGroup.Controls.Add(this.salvageRateLabel);
|
||||
this.plateOptimizerGroup.Controls.Add(this.salvageRateBox);
|
||||
this.plateOptimizerGroup.Controls.Add(this.salvageRatePercentLabel);
|
||||
this.plateOptimizerGroup.Location = new System.Drawing.Point(12, 312);
|
||||
this.plateOptimizerGroup.Name = "plateOptimizerGroup";
|
||||
this.plateOptimizerGroup.Size = new System.Drawing.Size(556, 188);
|
||||
this.plateOptimizerGroup.TabIndex = 4;
|
||||
this.plateOptimizerGroup.TabStop = false;
|
||||
this.plateOptimizerGroup.Text = " Plate Optimizer";
|
||||
//
|
||||
// optimizePlateSizeBox
|
||||
//
|
||||
this.optimizePlateSizeBox.AutoSize = true;
|
||||
this.optimizePlateSizeBox.Location = new System.Drawing.Point(10, 0);
|
||||
this.optimizePlateSizeBox.Name = "optimizePlateSizeBox";
|
||||
this.optimizePlateSizeBox.Size = new System.Drawing.Size(15, 14);
|
||||
this.optimizePlateSizeBox.TabIndex = 0;
|
||||
this.optimizePlateSizeBox.UseVisualStyleBackColor = true;
|
||||
this.optimizePlateSizeBox.CheckedChanged += new System.EventHandler(this.optimizePlateSizeBox_CheckedChanged);
|
||||
//
|
||||
// plateGrid
|
||||
//
|
||||
this.plateGrid.AllowUserToResizeRows = false;
|
||||
this.plateGrid.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.plateGrid.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.plateGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.plateGrid.Location = new System.Drawing.Point(10, 22);
|
||||
this.plateGrid.Name = "plateGrid";
|
||||
this.plateGrid.RowHeadersVisible = false;
|
||||
this.plateGrid.AutoGenerateColumns = false;
|
||||
this.plateGrid.Size = new System.Drawing.Size(536, 130);
|
||||
this.plateGrid.TabIndex = 1;
|
||||
//
|
||||
// salvageRateLabel
|
||||
//
|
||||
this.salvageRateLabel.AutoSize = true;
|
||||
this.salvageRateLabel.Location = new System.Drawing.Point(10, 162);
|
||||
this.salvageRateLabel.Name = "salvageRateLabel";
|
||||
this.salvageRateLabel.Size = new System.Drawing.Size(96, 16);
|
||||
this.salvageRateLabel.TabIndex = 2;
|
||||
this.salvageRateLabel.Text = "Salvage Rate:";
|
||||
//
|
||||
// salvageRateBox
|
||||
//
|
||||
this.salvageRateBox.Location = new System.Drawing.Point(108, 159);
|
||||
this.salvageRateBox.Name = "salvageRateBox";
|
||||
this.salvageRateBox.Size = new System.Drawing.Size(50, 22);
|
||||
this.salvageRateBox.TabIndex = 3;
|
||||
this.salvageRateBox.Text = "50";
|
||||
//
|
||||
// salvageRatePercentLabel
|
||||
//
|
||||
this.salvageRatePercentLabel.AutoSize = true;
|
||||
this.salvageRatePercentLabel.Location = new System.Drawing.Point(160, 162);
|
||||
this.salvageRatePercentLabel.Name = "salvageRatePercentLabel";
|
||||
this.salvageRatePercentLabel.Size = new System.Drawing.Size(21, 16);
|
||||
this.salvageRatePercentLabel.TabIndex = 4;
|
||||
this.salvageRatePercentLabel.Text = "%";
|
||||
//
|
||||
// buttonPanel
|
||||
//
|
||||
this.buttonPanel.Controls.Add(this.acceptButton);
|
||||
this.buttonPanel.Controls.Add(this.cancelButton);
|
||||
this.buttonPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.buttonPanel.Location = new System.Drawing.Point(0, 506);
|
||||
this.buttonPanel.Name = "buttonPanel";
|
||||
this.buttonPanel.Size = new System.Drawing.Size(580, 50);
|
||||
this.buttonPanel.TabIndex = 5;
|
||||
//
|
||||
// acceptButton
|
||||
//
|
||||
this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.acceptButton.Location = new System.Drawing.Point(376, 12);
|
||||
this.acceptButton.Name = "acceptButton";
|
||||
this.acceptButton.Size = new System.Drawing.Size(90, 28);
|
||||
this.acceptButton.TabIndex = 0;
|
||||
this.acceptButton.Text = "Accept";
|
||||
this.acceptButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cancelButton
|
||||
//
|
||||
//
|
||||
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.cancelButton.Location = new System.Drawing.Point(442, 9);
|
||||
this.cancelButton.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.cancelButton.Location = new System.Drawing.Point(474, 12);
|
||||
this.cancelButton.Name = "cancelButton";
|
||||
this.cancelButton.Size = new System.Drawing.Size(90, 28);
|
||||
this.cancelButton.TabIndex = 7;
|
||||
this.cancelButton.TabIndex = 1;
|
||||
this.cancelButton.Text = "Cancel";
|
||||
this.cancelButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
//
|
||||
// AutoNestForm
|
||||
//
|
||||
//
|
||||
this.AcceptButton = this.acceptButton;
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
|
||||
this.ClientSize = new System.Drawing.Size(545, 385);
|
||||
this.Controls.Add(this.bottomPanel1);
|
||||
this.Controls.Add(this.dataGridView1);
|
||||
this.CancelButton = this.cancelButton;
|
||||
this.ClientSize = new System.Drawing.Size(580, 556);
|
||||
this.Controls.Add(this.engineLabel);
|
||||
this.Controls.Add(this.engineComboBox);
|
||||
this.Controls.Add(this.partsGroup);
|
||||
this.Controls.Add(this.optionsGroup);
|
||||
this.Controls.Add(this.plateOptimizerGroup);
|
||||
this.Controls.Add(this.buttonPanel);
|
||||
this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "AutoNestForm";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "AutoNest";
|
||||
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
|
||||
this.bottomPanel1.ResumeLayout(false);
|
||||
this.bottomPanel1.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.partsGrid)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.plateGrid)).EndInit();
|
||||
this.partsGroup.ResumeLayout(false);
|
||||
this.partsGroup.PerformLayout();
|
||||
this.optionsGroup.ResumeLayout(false);
|
||||
this.optionsGroup.PerformLayout();
|
||||
this.plateOptimizerGroup.ResumeLayout(false);
|
||||
this.plateOptimizerGroup.PerformLayout();
|
||||
this.buttonPanel.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
this.PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.DataGridView dataGridView1;
|
||||
private System.Windows.Forms.Label engineLabel;
|
||||
private System.Windows.Forms.ComboBox engineComboBox;
|
||||
private System.Windows.Forms.GroupBox partsGroup;
|
||||
private System.Windows.Forms.DataGridView partsGrid;
|
||||
private System.Windows.Forms.Label summaryLabel;
|
||||
private System.Windows.Forms.GroupBox optionsGroup;
|
||||
private System.Windows.Forms.CheckBox createNewPlatesAsNeededBox;
|
||||
private System.Windows.Forms.GroupBox plateOptimizerGroup;
|
||||
private System.Windows.Forms.CheckBox optimizePlateSizeBox;
|
||||
private System.Windows.Forms.DataGridView plateGrid;
|
||||
private System.Windows.Forms.Label salvageRateLabel;
|
||||
private System.Windows.Forms.TextBox salvageRateBox;
|
||||
private System.Windows.Forms.Label salvageRatePercentLabel;
|
||||
private System.Windows.Forms.Panel buttonPanel;
|
||||
private System.Windows.Forms.Button acceptButton;
|
||||
private System.Windows.Forms.Button cancelButton;
|
||||
private System.Windows.Forms.CheckBox createNewPlatesAsNeededBox;
|
||||
private Controls.BottomPanel bottomPanel1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+262
-16
@@ -1,17 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Engine;
|
||||
|
||||
namespace OpenNest.Forms
|
||||
{
|
||||
public partial class AutoNestForm : Form
|
||||
{
|
||||
private static readonly Regex SizePattern = new(@"^(\d+\.?\d*)\s*[xX×]\s*(\d+\.?\d*)$");
|
||||
|
||||
public AutoNestForm(Nest nest)
|
||||
{
|
||||
InitializeComponent();
|
||||
SetupPartsGrid();
|
||||
SetupPlateGrid();
|
||||
LoadEngines();
|
||||
LoadDrawings(nest);
|
||||
LoadDefaultPlateOptions();
|
||||
SetPlateOptimizerVisible(false);
|
||||
|
||||
dataGridView1.DataError += dataGridView1_DataError;
|
||||
partsGrid.DataError += PartsGrid_DataError;
|
||||
}
|
||||
|
||||
public string EngineName
|
||||
{
|
||||
get { return engineComboBox.SelectedItem as string; }
|
||||
set { engineComboBox.SelectedItem = value; }
|
||||
}
|
||||
|
||||
public bool AllowPlateCreation
|
||||
@@ -20,21 +37,119 @@ namespace OpenNest.Forms
|
||||
set { createNewPlatesAsNeededBox.Checked = value; }
|
||||
}
|
||||
|
||||
public bool OptimizePlateSize
|
||||
{
|
||||
get { return optimizePlateSizeBox.Checked; }
|
||||
set { optimizePlateSizeBox.Checked = value; }
|
||||
}
|
||||
|
||||
public double SalvageRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (double.TryParse(salvageRateBox.Text, out var val))
|
||||
return System.Math.Clamp(val / 100.0, 0, 1);
|
||||
return 0.5;
|
||||
}
|
||||
set { salvageRateBox.Text = (value * 100).ToString("F0"); }
|
||||
}
|
||||
|
||||
private void LoadEngines()
|
||||
{
|
||||
foreach (var engine in NestEngineRegistry.AvailableEngines)
|
||||
engineComboBox.Items.Add(engine.Name);
|
||||
engineComboBox.SelectedItem = NestEngineRegistry.ActiveEngineName;
|
||||
}
|
||||
|
||||
private void SetupPartsGrid()
|
||||
{
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "DrawingName",
|
||||
HeaderText = "Drawing Name",
|
||||
Width = 160,
|
||||
ReadOnly = true,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "Quantity",
|
||||
HeaderText = "Qty",
|
||||
Width = 50,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "Priority",
|
||||
HeaderText = "Priority",
|
||||
Width = 55,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "RotationStart",
|
||||
HeaderText = "Rot Start",
|
||||
Width = 65,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "RotationEnd",
|
||||
HeaderText = "Rot End",
|
||||
Width = 60,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
partsGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "StepAngle",
|
||||
HeaderText = "Step",
|
||||
Width = 55,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
|
||||
partsGrid.CellValueChanged += PartsGrid_CellValueChanged;
|
||||
partsGrid.CurrentCellDirtyStateChanged += (s, e) =>
|
||||
{
|
||||
if (partsGrid.IsCurrentCellDirty)
|
||||
partsGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupPlateGrid()
|
||||
{
|
||||
plateGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "Size",
|
||||
HeaderText = "Size",
|
||||
Width = 120,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
plateGrid.Columns.Add(new DataGridViewTextBoxColumn
|
||||
{
|
||||
DataPropertyName = "Cost",
|
||||
HeaderText = "Cost",
|
||||
Width = 70,
|
||||
AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
|
||||
});
|
||||
|
||||
plateGrid.CellValidating += PlateGrid_CellValidating;
|
||||
}
|
||||
|
||||
private void LoadDrawings(Nest nest)
|
||||
{
|
||||
var items = new List<DataGridViewItem>();
|
||||
dataGridView1.Rows.Clear();
|
||||
|
||||
foreach (var drawing in nest.Drawings)
|
||||
items.Add(GetDataGridViewItem(drawing));
|
||||
|
||||
dataGridView1.DataSource = items;
|
||||
partsGrid.DataSource = items;
|
||||
UpdateSummary();
|
||||
}
|
||||
|
||||
public List<NestItem> GetNestItems()
|
||||
{
|
||||
var nestItems = new List<NestItem>();
|
||||
var gridItems = dataGridView1.DataSource as List<DataGridViewItem>;
|
||||
var gridItems = partsGrid.DataSource as List<DataGridViewItem>;
|
||||
|
||||
if (gridItems == null)
|
||||
return nestItems;
|
||||
@@ -58,6 +173,139 @@ namespace OpenNest.Forms
|
||||
return nestItems;
|
||||
}
|
||||
|
||||
public List<PlateOption> GetPlateOptions()
|
||||
{
|
||||
var result = new List<PlateOption>();
|
||||
var gridItems = plateGrid.DataSource as List<PlateOptionItem>;
|
||||
if (gridItems == null) return result;
|
||||
|
||||
foreach (var item in gridItems)
|
||||
{
|
||||
if (!TryParseSize(item.Size, out var width, out var length))
|
||||
continue;
|
||||
if (width <= 0 || length <= 0)
|
||||
continue;
|
||||
|
||||
result.Add(new PlateOption
|
||||
{
|
||||
Width = width,
|
||||
Length = length,
|
||||
Cost = item.Cost,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void LoadPlateOptions(List<PlateOption> options, double salvageRate)
|
||||
{
|
||||
if (options != null && options.Count > 0)
|
||||
{
|
||||
var items = options.Select(o => new PlateOptionItem
|
||||
{
|
||||
Size = FormatSize(o.Width, o.Length),
|
||||
Cost = o.Cost,
|
||||
}).ToList();
|
||||
plateGrid.DataSource = items;
|
||||
optimizePlateSizeBox.Checked = true;
|
||||
}
|
||||
SalvageRate = salvageRate;
|
||||
}
|
||||
|
||||
private void LoadDefaultPlateOptions()
|
||||
{
|
||||
var items = new List<PlateOptionItem>
|
||||
{
|
||||
new() { Size = "48 x 96", Cost = 0 },
|
||||
new() { Size = "48 x 120", Cost = 0 },
|
||||
new() { Size = "48 x 144", Cost = 0 },
|
||||
new() { Size = "60 x 96", Cost = 0 },
|
||||
new() { Size = "60 x 120", Cost = 0 },
|
||||
new() { Size = "60 x 144", Cost = 0 },
|
||||
new() { Size = "72 x 96", Cost = 0 },
|
||||
new() { Size = "72 x 120", Cost = 0 },
|
||||
new() { Size = "72 x 144", Cost = 0 },
|
||||
};
|
||||
plateGrid.DataSource = items;
|
||||
}
|
||||
|
||||
private void optimizePlateSizeBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetPlateOptimizerVisible(optimizePlateSizeBox.Checked);
|
||||
}
|
||||
|
||||
private void SetPlateOptimizerVisible(bool visible)
|
||||
{
|
||||
plateGrid.Visible = visible;
|
||||
salvageRateLabel.Visible = visible;
|
||||
salvageRateBox.Visible = visible;
|
||||
salvageRatePercentLabel.Visible = visible;
|
||||
}
|
||||
|
||||
private void UpdateSummary()
|
||||
{
|
||||
var gridItems = partsGrid.DataSource as List<DataGridViewItem>;
|
||||
if (gridItems == null)
|
||||
{
|
||||
summaryLabel.Text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var totalQty = gridItems.Sum(i => System.Math.Max(0, i.Quantity));
|
||||
var drawingCount = gridItems.Count(i => i.Quantity > 0);
|
||||
summaryLabel.Text = $"{totalQty} parts across {drawingCount} drawings";
|
||||
}
|
||||
|
||||
private void PartsGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (e.RowIndex < 0) return;
|
||||
if (partsGrid.Columns[e.ColumnIndex].DataPropertyName == "Quantity")
|
||||
UpdateSummary();
|
||||
}
|
||||
|
||||
private void PlateGrid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
|
||||
{
|
||||
if (plateGrid.Columns[e.ColumnIndex].DataPropertyName != "Size")
|
||||
return;
|
||||
|
||||
var value = e.FormattedValue?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return;
|
||||
|
||||
if (!TryParseSize(value, out _, out _))
|
||||
{
|
||||
e.Cancel = true;
|
||||
plateGrid.Rows[e.RowIndex].ErrorText = "Enter size as W x L (e.g. 48 x 96)";
|
||||
}
|
||||
else
|
||||
{
|
||||
plateGrid.Rows[e.RowIndex].ErrorText = "";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseSize(string value, out double width, out double length)
|
||||
{
|
||||
width = 0;
|
||||
length = 0;
|
||||
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||
var match = SizePattern.Match(value.Trim());
|
||||
if (!match.Success) return false;
|
||||
width = double.Parse(match.Groups[1].Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||
length = double.Parse(match.Groups[2].Value, System.Globalization.CultureInfo.InvariantCulture);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string FormatSize(double width, double length)
|
||||
{
|
||||
return $"{width:G} x {length:G}";
|
||||
}
|
||||
|
||||
private void PartsGrid_DataError(object sender, DataGridViewDataErrorEventArgs e)
|
||||
{
|
||||
MessageBox.Show("Invalid input. Expected input type is " +
|
||||
partsGrid[e.ColumnIndex, e.RowIndex].ValueType.Name);
|
||||
}
|
||||
|
||||
private DataGridViewItem GetDataGridViewItem(Drawing dwg)
|
||||
{
|
||||
var item = new DataGridViewItem();
|
||||
@@ -71,11 +319,6 @@ namespace OpenNest.Forms
|
||||
return item;
|
||||
}
|
||||
|
||||
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
|
||||
{
|
||||
MessageBox.Show("Invalid input. Expected input type is " + dataGridView1[e.ColumnIndex, e.RowIndex].ValueType.Name);
|
||||
}
|
||||
|
||||
private class DataGridViewItem
|
||||
{
|
||||
internal Drawing RefDrawing { get; set; }
|
||||
@@ -92,17 +335,20 @@ namespace OpenNest.Forms
|
||||
|
||||
public int Priority { get; set; }
|
||||
|
||||
[Browsable(false)] // hide until implemented
|
||||
[DisplayName("Rotation Start")]
|
||||
[DisplayName("Rot Start")]
|
||||
public double RotationStart { get; set; }
|
||||
|
||||
[Browsable(false)] // hide until implemented
|
||||
[DisplayName("Rotation End")]
|
||||
[DisplayName("Rot End")]
|
||||
public double RotationEnd { get; set; }
|
||||
|
||||
[Browsable(false)] // hide until implemented
|
||||
[DisplayName("Step Angle")]
|
||||
[DisplayName("Step")]
|
||||
public double StepAngle { get; set; }
|
||||
}
|
||||
|
||||
private class PlateOptionItem
|
||||
{
|
||||
public string Size { get; set; }
|
||||
public double Cost { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +420,7 @@ namespace OpenNest.Forms
|
||||
|
||||
var drawingName = Path.GetFileNameWithoutExtension(part.DxfPath);
|
||||
var drawing = new Drawing(drawingName);
|
||||
drawing.Color = Drawing.GetNextColor();
|
||||
drawing.Source.Path = part.DxfPath;
|
||||
drawing.Quantity.Required = part.Qty ?? 1;
|
||||
drawing.Material = new Material(material);
|
||||
|
||||
@@ -19,10 +19,21 @@ namespace OpenNest.Forms
|
||||
{
|
||||
public partial class CadConverterForm : Form
|
||||
{
|
||||
private static int colorIndex;
|
||||
|
||||
private SimplifierViewerForm simplifierViewer;
|
||||
private bool staleProgram = true;
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
if ((keyData == Keys.Up || keyData == Keys.Down) && !fileList.Focused)
|
||||
{
|
||||
fileList.ProcessArrowKey(keyData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
public CadConverterForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -611,7 +622,7 @@ namespace OpenNest.Forms
|
||||
continue;
|
||||
|
||||
var drawing = new Drawing(item.Name);
|
||||
drawing.Color = GetNextColor();
|
||||
drawing.Color = Drawing.GetNextColor();
|
||||
drawing.Customer = item.Customer;
|
||||
drawing.Source.Path = item.Path;
|
||||
drawing.Quantity.Required = item.Quantity;
|
||||
@@ -660,12 +671,7 @@ namespace OpenNest.Forms
|
||||
}
|
||||
|
||||
|
||||
private static Color GetNextColor()
|
||||
{
|
||||
var color = ColorScheme.PartColors[colorIndex % ColorScheme.PartColors.Length];
|
||||
colorIndex++;
|
||||
return color;
|
||||
}
|
||||
private static Color GetNextColor() => Drawing.GetNextColor();
|
||||
|
||||
private static bool IsDirectoryWritable(string path)
|
||||
{
|
||||
|
||||
Generated
+222
-169
@@ -28,224 +28,271 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditNestForm));
|
||||
this.splitContainer = new System.Windows.Forms.SplitContainer();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.platesListView = new System.Windows.Forms.ListView();
|
||||
this.plateNumColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.sizeColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.qtyColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
|
||||
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.drawingListBox1 = new OpenNest.Controls.DrawingListBox();
|
||||
this.toolStrip2 = new System.Windows.Forms.ToolStrip();
|
||||
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
|
||||
this.splitContainer.Panel1.SuspendLayout();
|
||||
this.splitContainer.SuspendLayout();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.toolStrip1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.toolStrip2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
splitContainer = new System.Windows.Forms.SplitContainer();
|
||||
tabControl1 = new System.Windows.Forms.TabControl();
|
||||
tabPage1 = new System.Windows.Forms.TabPage();
|
||||
platesListView = new System.Windows.Forms.ListView();
|
||||
plateNumColumn = new System.Windows.Forms.ColumnHeader();
|
||||
sizeColumn = new System.Windows.Forms.ColumnHeader();
|
||||
qtyColumn = new System.Windows.Forms.ColumnHeader();
|
||||
partsColumn = new System.Windows.Forms.ColumnHeader();
|
||||
utilColumn = new System.Windows.Forms.ColumnHeader();
|
||||
toolStrip1 = new System.Windows.Forms.ToolStrip();
|
||||
toolStripLabel1 = new System.Windows.Forms.ToolStripButton();
|
||||
toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
|
||||
toolStripLabel2 = new System.Windows.Forms.ToolStripButton();
|
||||
tabPage2 = new System.Windows.Forms.TabPage();
|
||||
drawingListBox1 = new OpenNest.Controls.DrawingListBox();
|
||||
toolStrip2 = new System.Windows.Forms.ToolStrip();
|
||||
toolStripButton2 = new System.Windows.Forms.ToolStripButton();
|
||||
toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
toolStripButton3 = new System.Windows.Forms.ToolStripButton();
|
||||
toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
hideNestedButton = new System.Windows.Forms.ToolStripButton();
|
||||
((System.ComponentModel.ISupportInitialize)splitContainer).BeginInit();
|
||||
splitContainer.Panel1.SuspendLayout();
|
||||
splitContainer.SuspendLayout();
|
||||
tabControl1.SuspendLayout();
|
||||
tabPage1.SuspendLayout();
|
||||
toolStrip1.SuspendLayout();
|
||||
tabPage2.SuspendLayout();
|
||||
toolStrip2.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// splitContainer
|
||||
//
|
||||
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
|
||||
this.splitContainer.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitContainer.Name = "splitContainer";
|
||||
splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
splitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
|
||||
splitContainer.Location = new System.Drawing.Point(0, 0);
|
||||
splitContainer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
splitContainer.Name = "splitContainer";
|
||||
//
|
||||
// splitContainer.Panel1
|
||||
//
|
||||
this.splitContainer.Panel1.Controls.Add(this.tabControl1);
|
||||
this.splitContainer.Size = new System.Drawing.Size(735, 396);
|
||||
this.splitContainer.SplitterDistance = 241;
|
||||
this.splitContainer.TabIndex = 8;
|
||||
splitContainer.Panel1.Controls.Add(tabControl1);
|
||||
splitContainer.Size = new System.Drawing.Size(858, 457);
|
||||
splitContainer.SplitterDistance = 281;
|
||||
splitContainer.SplitterWidth = 5;
|
||||
splitContainer.TabIndex = 8;
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tabControl1.ItemSize = new System.Drawing.Size(100, 22);
|
||||
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(241, 396);
|
||||
this.tabControl1.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
|
||||
this.tabControl1.TabIndex = 1;
|
||||
tabControl1.Controls.Add(tabPage1);
|
||||
tabControl1.Controls.Add(tabPage2);
|
||||
tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
tabControl1.ItemSize = new System.Drawing.Size(100, 22);
|
||||
tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||
tabControl1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
tabControl1.Name = "tabControl1";
|
||||
tabControl1.SelectedIndex = 0;
|
||||
tabControl1.Size = new System.Drawing.Size(281, 457);
|
||||
tabControl1.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
|
||||
tabControl1.TabIndex = 1;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
this.tabPage1.Controls.Add(this.platesListView);
|
||||
this.tabPage1.Controls.Add(this.toolStrip1);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(233, 370);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Plates";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
tabPage1.Controls.Add(platesListView);
|
||||
tabPage1.Controls.Add(toolStrip1);
|
||||
tabPage1.Location = new System.Drawing.Point(4, 26);
|
||||
tabPage1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
tabPage1.Name = "tabPage1";
|
||||
tabPage1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
tabPage1.Size = new System.Drawing.Size(273, 427);
|
||||
tabPage1.TabIndex = 0;
|
||||
tabPage1.Text = "Plates";
|
||||
tabPage1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// platesListView
|
||||
//
|
||||
this.platesListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.platesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.plateNumColumn,
|
||||
this.sizeColumn,
|
||||
this.qtyColumn});
|
||||
this.platesListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.platesListView.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.platesListView.FullRowSelect = true;
|
||||
this.platesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
this.platesListView.HideSelection = false;
|
||||
this.platesListView.Location = new System.Drawing.Point(3, 34);
|
||||
this.platesListView.MultiSelect = false;
|
||||
this.platesListView.Name = "platesListView";
|
||||
this.platesListView.Size = new System.Drawing.Size(227, 333);
|
||||
this.platesListView.TabIndex = 1;
|
||||
this.platesListView.UseCompatibleStateImageBehavior = false;
|
||||
this.platesListView.View = System.Windows.Forms.View.Details;
|
||||
this.platesListView.DoubleClick += new System.EventHandler(this.EditSelectedPlate_Click);
|
||||
platesListView.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
platesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { plateNumColumn, sizeColumn, qtyColumn, partsColumn, utilColumn });
|
||||
platesListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
platesListView.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
|
||||
platesListView.FullRowSelect = true;
|
||||
platesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
||||
platesListView.Location = new System.Drawing.Point(4, 30);
|
||||
platesListView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
platesListView.MultiSelect = false;
|
||||
platesListView.Name = "platesListView";
|
||||
platesListView.Size = new System.Drawing.Size(265, 394);
|
||||
platesListView.TabIndex = 1;
|
||||
platesListView.UseCompatibleStateImageBehavior = false;
|
||||
platesListView.View = System.Windows.Forms.View.Details;
|
||||
platesListView.DoubleClick += EditSelectedPlate_Click;
|
||||
//
|
||||
// plateNumColumn
|
||||
//
|
||||
this.plateNumColumn.Text = "";
|
||||
this.plateNumColumn.Width = 30;
|
||||
plateNumColumn.Text = "";
|
||||
plateNumColumn.Width = 30;
|
||||
//
|
||||
// sizeColumn
|
||||
//
|
||||
this.sizeColumn.Text = "Size";
|
||||
this.sizeColumn.Width = 80;
|
||||
sizeColumn.Text = "Size";
|
||||
sizeColumn.Width = 80;
|
||||
//
|
||||
// qtyColumn
|
||||
//
|
||||
this.qtyColumn.Text = "Qty";
|
||||
qtyColumn.Text = "Qty";
|
||||
//
|
||||
// partsColumn
|
||||
//
|
||||
partsColumn.Text = "Parts";
|
||||
partsColumn.Width = 45;
|
||||
//
|
||||
// utilColumn
|
||||
//
|
||||
utilColumn.Text = "Util";
|
||||
utilColumn.Width = 45;
|
||||
//
|
||||
// toolStrip1
|
||||
//
|
||||
this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripButton1});
|
||||
this.toolStrip1.Location = new System.Drawing.Point(3, 3);
|
||||
this.toolStrip1.Name = "toolStrip1";
|
||||
this.toolStrip1.Size = new System.Drawing.Size(227, 31);
|
||||
this.toolStrip1.TabIndex = 2;
|
||||
this.toolStrip1.Text = "toolStrip1";
|
||||
toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||
toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripLabel1, toolStripSeparator3, toolStripLabel2 });
|
||||
toolStrip1.Location = new System.Drawing.Point(4, 3);
|
||||
toolStrip1.Name = "toolStrip1";
|
||||
toolStrip1.Size = new System.Drawing.Size(265, 27);
|
||||
toolStrip1.TabIndex = 2;
|
||||
toolStrip1.Text = "toolStrip1";
|
||||
//
|
||||
// toolStripButton1
|
||||
// toolStripLabel1
|
||||
//
|
||||
toolStripLabel1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
toolStripLabel1.Image = Properties.Resources.plus;
|
||||
toolStripLabel1.Name = "toolStripLabel1";
|
||||
toolStripLabel1.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
toolStripLabel1.Size = new System.Drawing.Size(34, 24);
|
||||
toolStripLabel1.Text = "Remove Selected Plate";
|
||||
toolStripLabel1.Click += toolStripLabel1_Click;
|
||||
//
|
||||
// toolStripSeparator3
|
||||
//
|
||||
toolStripSeparator3.Name = "toolStripSeparator3";
|
||||
toolStripSeparator3.Size = new System.Drawing.Size(6, 27);
|
||||
//
|
||||
// toolStripLabel2
|
||||
//
|
||||
toolStripLabel2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
toolStripLabel2.Image = Properties.Resources.delete;
|
||||
toolStripLabel2.Name = "toolStripLabel2";
|
||||
toolStripLabel2.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
toolStripLabel2.Size = new System.Drawing.Size(34, 24);
|
||||
toolStripLabel2.Text = "Remove Selected";
|
||||
toolStripLabel2.Click += toolStripLabel2_Click;
|
||||
//
|
||||
this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
this.toolStripButton1.Image = global::OpenNest.Properties.Resources.clock;
|
||||
this.toolStripButton1.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
|
||||
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.toolStripButton1.Name = "toolStripButton1";
|
||||
this.toolStripButton1.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.toolStripButton1.Size = new System.Drawing.Size(38, 28);
|
||||
this.toolStripButton1.Text = "Calculate Cut Time";
|
||||
this.toolStripButton1.Click += new System.EventHandler(this.CalculateSelectedPlateCutTime_Click);
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.drawingListBox1);
|
||||
this.tabPage2.Controls.Add(this.toolStrip2);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 26);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(233, 366);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Drawings";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
tabPage2.Controls.Add(drawingListBox1);
|
||||
tabPage2.Controls.Add(toolStrip2);
|
||||
tabPage2.Location = new System.Drawing.Point(4, 26);
|
||||
tabPage2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
tabPage2.Name = "tabPage2";
|
||||
tabPage2.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
tabPage2.Size = new System.Drawing.Size(273, 427);
|
||||
tabPage2.TabIndex = 1;
|
||||
tabPage2.Text = "Drawings";
|
||||
tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// drawingListBox1
|
||||
//
|
||||
this.drawingListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
this.drawingListBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.drawingListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
|
||||
this.drawingListBox1.FormattingEnabled = true;
|
||||
this.drawingListBox1.ItemHeight = 85;
|
||||
this.drawingListBox1.Location = new System.Drawing.Point(3, 34);
|
||||
this.drawingListBox1.Name = "drawingListBox1";
|
||||
this.drawingListBox1.Size = new System.Drawing.Size(227, 329);
|
||||
this.drawingListBox1.TabIndex = 1;
|
||||
this.drawingListBox1.Units = OpenNest.Units.Inches;
|
||||
this.drawingListBox1.Click += new System.EventHandler(this.drawingListBox1_Click);
|
||||
this.drawingListBox1.DoubleClick += new System.EventHandler(this.drawingListBox1_DoubleClick);
|
||||
drawingListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
|
||||
drawingListBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
drawingListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
|
||||
drawingListBox1.FormattingEnabled = true;
|
||||
drawingListBox1.HideDepletedParts = false;
|
||||
drawingListBox1.HideQuantity = false;
|
||||
drawingListBox1.ItemHeight = 85;
|
||||
drawingListBox1.Location = new System.Drawing.Point(4, 30);
|
||||
drawingListBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
drawingListBox1.Name = "drawingListBox1";
|
||||
drawingListBox1.Size = new System.Drawing.Size(265, 394);
|
||||
drawingListBox1.TabIndex = 1;
|
||||
drawingListBox1.Units = Units.Inches;
|
||||
drawingListBox1.Click += drawingListBox1_Click;
|
||||
drawingListBox1.DoubleClick += drawingListBox1_DoubleClick;
|
||||
//
|
||||
// toolStrip2
|
||||
//
|
||||
this.toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||
this.toolStrip2.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripButton2,
|
||||
this.toolStripSeparator1,
|
||||
this.toolStripButton3});
|
||||
this.toolStrip2.Location = new System.Drawing.Point(3, 3);
|
||||
this.toolStrip2.Name = "toolStrip2";
|
||||
this.toolStrip2.Size = new System.Drawing.Size(227, 31);
|
||||
this.toolStrip2.TabIndex = 3;
|
||||
this.toolStrip2.Text = "toolStrip2";
|
||||
toolStrip2.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||
toolStrip2.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
toolStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripButton2, toolStripSeparator1, toolStripButton3, toolStripSeparator2, hideNestedButton });
|
||||
toolStrip2.Location = new System.Drawing.Point(4, 3);
|
||||
toolStrip2.Name = "toolStrip2";
|
||||
toolStrip2.Size = new System.Drawing.Size(265, 27);
|
||||
toolStrip2.TabIndex = 3;
|
||||
toolStrip2.Text = "toolStrip2";
|
||||
//
|
||||
// toolStripButton2
|
||||
//
|
||||
this.toolStripButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
this.toolStripButton2.Image = global::OpenNest.Properties.Resources.import;
|
||||
this.toolStripButton2.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
|
||||
this.toolStripButton2.Name = "toolStripButton2";
|
||||
this.toolStripButton2.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.toolStripButton2.RightToLeft = System.Windows.Forms.RightToLeft.No;
|
||||
this.toolStripButton2.Size = new System.Drawing.Size(38, 28);
|
||||
this.toolStripButton2.Text = "Import Drawings";
|
||||
this.toolStripButton2.Click += new System.EventHandler(this.ImportDrawings_Click);
|
||||
toolStripButton2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
toolStripButton2.Image = (System.Drawing.Image)resources.GetObject("toolStripButton2.Image");
|
||||
toolStripButton2.Name = "toolStripButton2";
|
||||
toolStripButton2.Overflow = System.Windows.Forms.ToolStripItemOverflow.Never;
|
||||
toolStripButton2.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
toolStripButton2.RightToLeft = System.Windows.Forms.RightToLeft.No;
|
||||
toolStripButton2.Size = new System.Drawing.Size(34, 24);
|
||||
toolStripButton2.Text = "Import Drawings";
|
||||
toolStripButton2.Click += ImportDrawings_Click;
|
||||
//
|
||||
// toolStripSeparator1
|
||||
//
|
||||
this.toolStripSeparator1.Name = "toolStripSeparator1";
|
||||
this.toolStripSeparator1.Size = new System.Drawing.Size(6, 31);
|
||||
toolStripSeparator1.Name = "toolStripSeparator1";
|
||||
toolStripSeparator1.Size = new System.Drawing.Size(6, 27);
|
||||
//
|
||||
// toolStripButton3
|
||||
//
|
||||
this.toolStripButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
this.toolStripButton3.Image = global::OpenNest.Properties.Resources.clear;
|
||||
this.toolStripButton3.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
|
||||
this.toolStripButton3.Name = "toolStripButton3";
|
||||
this.toolStripButton3.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.toolStripButton3.RightToLeft = System.Windows.Forms.RightToLeft.No;
|
||||
this.toolStripButton3.Size = new System.Drawing.Size(38, 28);
|
||||
this.toolStripButton3.Text = "Cleanup unused Drawings";
|
||||
this.toolStripButton3.Click += new System.EventHandler(this.CleanUnusedDrawings_Click);
|
||||
toolStripButton3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
toolStripButton3.Image = (System.Drawing.Image)resources.GetObject("toolStripButton3.Image");
|
||||
toolStripButton3.Name = "toolStripButton3";
|
||||
toolStripButton3.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
toolStripButton3.RightToLeft = System.Windows.Forms.RightToLeft.No;
|
||||
toolStripButton3.Size = new System.Drawing.Size(34, 24);
|
||||
toolStripButton3.Text = "Cleanup unused Drawings";
|
||||
toolStripButton3.Click += CleanUnusedDrawings_Click;
|
||||
//
|
||||
// toolStripSeparator2
|
||||
//
|
||||
toolStripSeparator2.Name = "toolStripSeparator2";
|
||||
toolStripSeparator2.Size = new System.Drawing.Size(6, 27);
|
||||
//
|
||||
// hideNestedButton
|
||||
//
|
||||
hideNestedButton.CheckOnClick = true;
|
||||
hideNestedButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
hideNestedButton.Image = (System.Drawing.Image)resources.GetObject("hideNestedButton.Image");
|
||||
hideNestedButton.Name = "hideNestedButton";
|
||||
hideNestedButton.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
hideNestedButton.Size = new System.Drawing.Size(34, 24);
|
||||
hideNestedButton.Text = "Hide fully nested drawings";
|
||||
hideNestedButton.CheckedChanged += HideNestedButton_CheckedChanged;
|
||||
//
|
||||
// EditNestForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(735, 396);
|
||||
this.Controls.Add(this.splitContainer);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MinimumSize = new System.Drawing.Size(200, 198);
|
||||
this.Name = "EditNestForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "EditForm";
|
||||
this.splitContainer.Panel1.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
|
||||
this.splitContainer.ResumeLayout(false);
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage1.PerformLayout();
|
||||
this.toolStrip1.ResumeLayout(false);
|
||||
this.toolStrip1.PerformLayout();
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.tabPage2.PerformLayout();
|
||||
this.toolStrip2.ResumeLayout(false);
|
||||
this.toolStrip2.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
ClientSize = new System.Drawing.Size(858, 457);
|
||||
Controls.Add(splitContainer);
|
||||
Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon");
|
||||
Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
MinimumSize = new System.Drawing.Size(231, 222);
|
||||
Name = "EditNestForm";
|
||||
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
Text = "EditForm";
|
||||
splitContainer.Panel1.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)splitContainer).EndInit();
|
||||
splitContainer.ResumeLayout(false);
|
||||
tabControl1.ResumeLayout(false);
|
||||
tabPage1.ResumeLayout(false);
|
||||
tabPage1.PerformLayout();
|
||||
toolStrip1.ResumeLayout(false);
|
||||
toolStrip1.PerformLayout();
|
||||
tabPage2.ResumeLayout(false);
|
||||
tabPage2.PerformLayout();
|
||||
toolStrip2.ResumeLayout(false);
|
||||
toolStrip2.PerformLayout();
|
||||
ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
@@ -258,13 +305,19 @@
|
||||
private System.Windows.Forms.ColumnHeader sizeColumn;
|
||||
private System.Windows.Forms.ColumnHeader qtyColumn;
|
||||
private System.Windows.Forms.ToolStrip toolStrip1;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton1;
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private Controls.DrawingListBox drawingListBox1;
|
||||
private System.Windows.Forms.ColumnHeader plateNumColumn;
|
||||
private System.Windows.Forms.ColumnHeader partsColumn;
|
||||
private System.Windows.Forms.ColumnHeader utilColumn;
|
||||
private System.Windows.Forms.ToolStrip toolStrip2;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton2;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton3;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
|
||||
private System.Windows.Forms.ToolStripButton hideNestedButton;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
|
||||
private System.Windows.Forms.ToolStripButton toolStripLabel1;
|
||||
private System.Windows.Forms.ToolStripButton toolStripLabel2;
|
||||
}
|
||||
}
|
||||
+129
-142
@@ -23,15 +23,18 @@ namespace OpenNest.Forms
|
||||
{
|
||||
public event EventHandler PlateChanged;
|
||||
|
||||
public readonly Nest Nest;
|
||||
public readonly Document Document;
|
||||
public readonly PlateView PlateView;
|
||||
public readonly PlateManager PlateManager;
|
||||
|
||||
public Nest Nest => Document.Nest;
|
||||
|
||||
private readonly Timer updateDrawingListTimer;
|
||||
private bool updatingPlateList;
|
||||
|
||||
private Panel plateHeaderPanel;
|
||||
private Label plateInfoLabel;
|
||||
private Button btnFirstPlate;
|
||||
private Button btnRemovePlate;
|
||||
|
||||
private Button btnPreviousPlate;
|
||||
private Button btnNextPlate;
|
||||
@@ -67,13 +70,10 @@ namespace OpenNest.Forms
|
||||
|
||||
platesListView.SelectedIndexChanged += (sender, e) =>
|
||||
{
|
||||
if (platesListView.SelectedIndices.Count == 0)
|
||||
if (updatingPlateList || platesListView.SelectedIndices.Count == 0)
|
||||
return;
|
||||
|
||||
CurrentPlateIndex = platesListView.SelectedIndices[0];
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
PlateView.ZoomToFit();
|
||||
FirePlateChanged(false);
|
||||
PlateManager.LoadAt(platesListView.SelectedIndices[0]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,16 +100,16 @@ namespace OpenNest.Forms
|
||||
var btnSize = new System.Drawing.Size(28, 28);
|
||||
|
||||
btnFirstPlate = CreateNavButton(Resources.move_first);
|
||||
btnFirstPlate.Click += (s, e) => LoadFirstPlate();
|
||||
btnFirstPlate.Click += (s, e) => PlateManager.LoadFirst();
|
||||
|
||||
btnPreviousPlate = CreateNavButton(Resources.move_previous);
|
||||
btnPreviousPlate.Click += (s, e) => LoadPreviousPlate();
|
||||
btnPreviousPlate.Click += (s, e) => PlateManager.LoadPrevious();
|
||||
|
||||
btnNextPlate = CreateNavButton(Resources.move_next);
|
||||
btnNextPlate.Click += (s, e) => LoadNextPlate();
|
||||
btnNextPlate.Click += (s, e) => PlateManager.LoadNext();
|
||||
|
||||
btnLastPlate = CreateNavButton(Resources.move_last);
|
||||
btnLastPlate.Click += (s, e) => LoadLastPlate();
|
||||
btnLastPlate.Click += (s, e) => PlateManager.LoadLast();
|
||||
|
||||
// Panel that holds the nav buttons and centers itself in the header
|
||||
var navPanel = new Panel
|
||||
@@ -126,18 +126,8 @@ namespace OpenNest.Forms
|
||||
|
||||
navPanel.Controls.AddRange(new Control[] { btnFirstPlate, btnPreviousPlate, btnNextPlate, btnLastPlate });
|
||||
|
||||
btnRemovePlate = CreateNavButton(Resources.remove);
|
||||
btnRemovePlate.Dock = DockStyle.Right;
|
||||
btnRemovePlate.Click += (s, e) => RemoveCurrentPlate();
|
||||
|
||||
var btnAddPlate = CreateNavButton(Resources.add);
|
||||
btnAddPlate.Dock = DockStyle.Right;
|
||||
btnAddPlate.Click += (s, e) => Nest.CreatePlate();
|
||||
|
||||
plateHeaderPanel.Controls.Add(navPanel);
|
||||
plateHeaderPanel.Controls.Add(plateInfoLabel);
|
||||
plateHeaderPanel.Controls.Add(btnRemovePlate);
|
||||
plateHeaderPanel.Controls.Add(btnAddPlate);
|
||||
|
||||
// Center the nav panel on resize
|
||||
CenterNavPanel(navPanel);
|
||||
@@ -210,94 +200,32 @@ namespace OpenNest.Forms
|
||||
};
|
||||
updateDrawingListTimer.Elapsed += drawingListUpdateTimer_Elapsed;
|
||||
|
||||
Nest = nest;
|
||||
Nest.Plates.ItemAdded += Plates_PlateAdded;
|
||||
Nest.Plates.ItemRemoved += Plates_PlateRemoved;
|
||||
Document = new Document { Nest = nest };
|
||||
|
||||
if (Nest.Plates.Count == 0)
|
||||
Nest.CreatePlate();
|
||||
PlateManager = new PlateManager(nest);
|
||||
PlateManager.CurrentPlateChanged += PlateManager_CurrentPlateChanged;
|
||||
PlateManager.PlateListChanged += PlateManager_PlateListChanged;
|
||||
|
||||
PlateManager.EnsureSentinel();
|
||||
|
||||
UpdatePlateList();
|
||||
UpdateDrawingList();
|
||||
UpdateRemovePlateButton();
|
||||
|
||||
LoadFirstPlate();
|
||||
PlateManager.LoadFirst();
|
||||
|
||||
Text = Nest.Name;
|
||||
drawingListBox1.Units = Nest.Units;
|
||||
drawingListBox1.DeleteRequested += drawingListBox1_DeleteRequested;
|
||||
}
|
||||
|
||||
public string LastSavePath { get; private set; }
|
||||
|
||||
public DateTime LastSaveDate { get; private set; }
|
||||
|
||||
public int CurrentPlateIndex { get; private set; }
|
||||
|
||||
public int PlateCount
|
||||
{
|
||||
get { return Nest.Plates.Count; }
|
||||
}
|
||||
|
||||
public void LoadFirstPlate()
|
||||
{
|
||||
if (Nest.Plates.Count > 0)
|
||||
{
|
||||
CurrentPlateIndex = 0;
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
PlateView.ZoomToFit();
|
||||
FirePlateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadLastPlate()
|
||||
{
|
||||
if (Nest.Plates.Count > 0)
|
||||
{
|
||||
CurrentPlateIndex = Nest.Plates.Count - 1;
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
PlateView.ZoomToFit();
|
||||
FirePlateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool LoadNextPlate()
|
||||
{
|
||||
if (CurrentPlateIndex + 1 >= Nest.Plates.Count)
|
||||
return false;
|
||||
|
||||
CurrentPlateIndex++;
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
PlateView.ZoomToFit();
|
||||
FirePlateChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool LoadPreviousPlate()
|
||||
{
|
||||
if (Nest.Plates.Count == 0 || CurrentPlateIndex - 1 < 0)
|
||||
return false;
|
||||
|
||||
CurrentPlateIndex--;
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
PlateView.ZoomToFit();
|
||||
FirePlateChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFirstPlate()
|
||||
{
|
||||
return (Nest.Plates.Count == 0 || (CurrentPlateIndex - 1) < 0);
|
||||
}
|
||||
|
||||
public bool IsLastPlate()
|
||||
{
|
||||
return CurrentPlateIndex + 1 >= Nest.Plates.Count;
|
||||
}
|
||||
|
||||
public void UpdatePlateList()
|
||||
{
|
||||
updatingPlateList = true;
|
||||
var focused = ContainsFocus ? GetFocusedControl() : null;
|
||||
|
||||
platesListView.BeginUpdate();
|
||||
platesListView.Items.Clear();
|
||||
|
||||
var items = new ListViewItem[Nest.Plates.Count];
|
||||
@@ -310,6 +238,23 @@ namespace OpenNest.Forms
|
||||
}
|
||||
|
||||
platesListView.Items.AddRange(items);
|
||||
|
||||
if (PlateManager.CurrentIndex < platesListView.Items.Count)
|
||||
platesListView.Items[PlateManager.CurrentIndex].Selected = true;
|
||||
|
||||
platesListView.EndUpdate();
|
||||
updatingPlateList = false;
|
||||
|
||||
if (focused != null && focused != platesListView)
|
||||
focused.Focus();
|
||||
}
|
||||
|
||||
private Control GetFocusedControl()
|
||||
{
|
||||
var ctrl = this;
|
||||
while (ctrl is ContainerControl container && container.ActiveControl != null)
|
||||
return container.ActiveControl;
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
public void UpdateDrawingList()
|
||||
@@ -317,13 +262,18 @@ namespace OpenNest.Forms
|
||||
drawingListBox1.Items.Clear();
|
||||
|
||||
foreach (var dwg in Nest.Drawings.OrderBy(d => d.Name).ToList())
|
||||
{
|
||||
if (hideNestedButton.Checked && dwg.Quantity.Required > 0 && dwg.Quantity.Remaining == 0)
|
||||
continue;
|
||||
|
||||
drawingListBox1.Items.Add(dwg);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (File.Exists(LastSavePath))
|
||||
SaveAs(LastSavePath);
|
||||
if (Document.HasSavePath)
|
||||
SaveAs(Document.LastSavePath);
|
||||
else
|
||||
SaveAs();
|
||||
}
|
||||
@@ -345,14 +295,8 @@ namespace OpenNest.Forms
|
||||
|
||||
public void SaveAs(string path)
|
||||
{
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
Nest.Name = name;
|
||||
Text = name;
|
||||
LastSaveDate = DateTime.Now;
|
||||
LastSavePath = path;
|
||||
|
||||
var writer = new NestWriter(Nest);
|
||||
writer.Write(path);
|
||||
Document.SaveAs(path);
|
||||
Text = Document.Name;
|
||||
}
|
||||
|
||||
public void SaveTemplate(string path)
|
||||
@@ -397,7 +341,7 @@ namespace OpenNest.Forms
|
||||
"Image as displayed (*.jpg)|*.jpg|" +
|
||||
"Locations and rotations (*.txt)|*.txt";
|
||||
|
||||
dlg.FileName = string.Format("{0}-P{1}", Nest.Name, CurrentPlateIndex + 1);
|
||||
dlg.FileName = string.Format("{0}-P{1}", Nest.Name, PlateManager.CurrentIndex + 1);
|
||||
dlg.AddExtension = true;
|
||||
dlg.DefaultExt = ".";
|
||||
|
||||
@@ -454,13 +398,13 @@ namespace OpenNest.Forms
|
||||
|
||||
public void ExportAll()
|
||||
{
|
||||
LoadFirstPlate();
|
||||
PlateManager.LoadFirst();
|
||||
|
||||
do
|
||||
{
|
||||
if (!Export()) return;
|
||||
}
|
||||
while (LoadNextPlate());
|
||||
while (PlateManager.LoadNext());
|
||||
}
|
||||
|
||||
public void RotateCw()
|
||||
@@ -581,7 +525,7 @@ namespace OpenNest.Forms
|
||||
public void OpenCurrentPlate()
|
||||
{
|
||||
var plate = PlateView.Plate;
|
||||
var name = string.Format("{0}-P{1}.dxf", Nest.Name, CurrentPlateIndex + 1);
|
||||
var name = string.Format("{0}-P{1}.dxf", Nest.Name, PlateManager.CurrentIndex + 1);
|
||||
var path = Path.Combine(Path.GetTempPath(), name);
|
||||
var exporter = new DxfExporter();
|
||||
exporter.ExportPlate(plate, path);
|
||||
@@ -591,10 +535,7 @@ namespace OpenNest.Forms
|
||||
|
||||
public void RemoveCurrentPlate()
|
||||
{
|
||||
if (Nest.Plates.Count < 2)
|
||||
return;
|
||||
|
||||
Nest.Plates.RemoveAt(CurrentPlateIndex);
|
||||
PlateManager.RemoveCurrent();
|
||||
}
|
||||
|
||||
public void AutoSequenceCurrentPlate()
|
||||
@@ -672,33 +613,31 @@ namespace OpenNest.Forms
|
||||
|
||||
private void FirePlateChanged(bool updateListView = true)
|
||||
{
|
||||
if (updateListView)
|
||||
platesListView.Items[CurrentPlateIndex].Selected = true;
|
||||
if (updateListView && PlateManager.CurrentIndex < platesListView.Items.Count)
|
||||
platesListView.Items[PlateManager.CurrentIndex].Selected = true;
|
||||
|
||||
UpdatePlateHeader();
|
||||
|
||||
if (PlateChanged != null)
|
||||
PlateChanged.Invoke(this, EventArgs.Empty);
|
||||
PlateChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void UpdatePlateHeader()
|
||||
{
|
||||
var plate = Nest.Plates.Count > 0 ? Nest.Plates[CurrentPlateIndex] : null;
|
||||
var plate = PlateManager.CurrentPlate;
|
||||
|
||||
if (plate != null)
|
||||
{
|
||||
plateInfoLabel.Text = string.Format("Plate {0} of {1} | {2}",
|
||||
CurrentPlateIndex + 1, Nest.Plates.Count, plate.Size);
|
||||
PlateManager.CurrentIndex + 1, PlateManager.Count, plate.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
plateInfoLabel.Text = "No plates";
|
||||
}
|
||||
|
||||
btnFirstPlate.Enabled = !IsFirstPlate();
|
||||
btnPreviousPlate.Enabled = !IsFirstPlate();
|
||||
btnNextPlate.Enabled = !IsLastPlate();
|
||||
btnLastPlate.Enabled = !IsLastPlate();
|
||||
btnFirstPlate.Enabled = !PlateManager.IsFirst;
|
||||
btnPreviousPlate.Enabled = !PlateManager.IsFirst;
|
||||
btnNextPlate.Enabled = !PlateManager.IsLast;
|
||||
btnLastPlate.Enabled = !PlateManager.IsLast;
|
||||
}
|
||||
|
||||
#region Overrides
|
||||
@@ -722,6 +661,8 @@ namespace OpenNest.Forms
|
||||
{
|
||||
base.OnClosing(e);
|
||||
|
||||
PlateManager.Dispose();
|
||||
|
||||
Settings.Default.PlateViewDrawBounds = PlateView.DrawBounds;
|
||||
Settings.Default.PlateViewDrawRapid = PlateView.DrawRapid;
|
||||
Settings.Default.SplitterDistance = splitContainer.SplitterDistance;
|
||||
@@ -926,34 +867,34 @@ namespace OpenNest.Forms
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plate Collection Events
|
||||
|
||||
private void Plates_PlateRemoved(object sender, ItemRemovedEventArgs<Plate> e)
|
||||
private void HideNestedButton_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (Nest.Plates.Count <= CurrentPlateIndex)
|
||||
LoadLastPlate();
|
||||
else
|
||||
PlateView.Plate = Nest.Plates[CurrentPlateIndex];
|
||||
|
||||
UpdatePlateList();
|
||||
UpdateRemovePlateButton();
|
||||
PlateView.ZoomToFit();
|
||||
UpdateDrawingList();
|
||||
}
|
||||
|
||||
private void Plates_PlateAdded(object sender, ItemAddedEventArgs<Plate> e)
|
||||
#endregion
|
||||
|
||||
#region PlateManager Events
|
||||
|
||||
private void PlateManager_CurrentPlateChanged(object sender, PlateChangedEventArgs e)
|
||||
{
|
||||
PlateView.Plate = PlateManager.CurrentPlate;
|
||||
PlateView.ZoomToFit();
|
||||
UpdatePlateHeader();
|
||||
PlateChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void PlateManager_PlateListChanged(object sender, EventArgs e)
|
||||
{
|
||||
tabControl1.SelectedIndex = 0;
|
||||
UpdatePlateList();
|
||||
UpdatePlateHeader();
|
||||
UpdateRemovePlateButton();
|
||||
LoadLastPlate();
|
||||
PlateView.ZoomToFit();
|
||||
}
|
||||
|
||||
private void UpdateRemovePlateButton()
|
||||
{
|
||||
btnRemovePlate.Enabled = Nest.Plates.Count > 1;
|
||||
toolStripLabel2.Enabled = PlateManager.CanRemoveCurrent;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -964,6 +905,13 @@ namespace OpenNest.Forms
|
||||
item.Text = id.ToString();
|
||||
item.SubItems.Add(plate.Size.ToString());
|
||||
item.SubItems.Add(plate.Quantity.ToString());
|
||||
|
||||
var partCount = plate.Parts.Count(p => !p.BaseDrawing.IsCutOff);
|
||||
item.SubItems.Add(partCount.ToString());
|
||||
|
||||
var util = plate.Utilization();
|
||||
item.SubItems.Add(partCount > 0 ? $"{util:P0}" : "");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -986,7 +934,10 @@ namespace OpenNest.Forms
|
||||
|
||||
drawingListBox1.Invoke(new MethodInvoker(() =>
|
||||
{
|
||||
drawingListBox1.Refresh();
|
||||
if (hideNestedButton.Checked)
|
||||
UpdateDrawingList();
|
||||
else
|
||||
drawingListBox1.Refresh();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1013,6 +964,32 @@ namespace OpenNest.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void drawingListBox1_DeleteRequested(object sender, Drawing drawing)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"Delete drawing '{drawing.Name}' and all its parts from every plate?",
|
||||
"Delete Drawing",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning,
|
||||
MessageBoxDefaultButton.Button2);
|
||||
|
||||
if (result != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
foreach (var plate in Nest.Plates)
|
||||
{
|
||||
for (var i = plate.Parts.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (plate.Parts[i].BaseDrawing == drawing)
|
||||
plate.Parts.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
Nest.Drawings.Remove(drawing);
|
||||
UpdateDrawingList();
|
||||
PlateView.Invalidate();
|
||||
}
|
||||
|
||||
private void drawingListBox1_Click(object sender, EventArgs e)
|
||||
{
|
||||
addPart = true;
|
||||
@@ -1032,5 +1009,15 @@ namespace OpenNest.Forms
|
||||
|
||||
addPart = false;
|
||||
}
|
||||
|
||||
private void toolStripLabel2_Click(object sender, EventArgs e)
|
||||
{
|
||||
RemoveSelectedPlate_Click(sender, e);
|
||||
}
|
||||
|
||||
private void toolStripLabel1_Click(object sender, EventArgs e)
|
||||
{
|
||||
AddPlate_Click(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -124,6 +124,611 @@
|
||||
<value>122, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
vAAADrwBlbxySQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABb8SURBVHhe7d15
|
||||
zG1XWQbwpxQ6MojBaCj4h9GAQplCJCpRKAUSoy1ECiYiYBogxhAEJMaEUCiJQ5iDpS1gxYJaUJBJZRQQ
|
||||
RCBKVAwWJUaZEgglDGIotGiWPVe+Lta99xvOsPd+f7/kSZre3u/sffbqu55zvjMkAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAA7M4ZSc5Ncn6SC1dp/3z31Z8BAAtwepKHJ7kiybVJbkzyP8dJ+7P231ye5GFJTut/
|
||||
GAAwbXdO8sIk1w02+v2m/d0XJLlT/8MBgGm5XZIXJ7l+sKEfNu1ntSJw2/7GAIDde2iSzw428HXlM0ke
|
||||
3N8oALAbpyS59CS/319X2m1csrpNAGBHTk1y1WCj3nResbptAGDL2qPwthH3m/O2crVnAgBg+9rT/v2m
|
||||
vO08sz8oAGBzzktyw2BD3nbaawIe0h8cALB+7e14m3y1/0Hz6SS36Q8SAFivFw024V3n+f1BAgDr0z6V
|
||||
b50f8rOufD3JOf3BAgDrMcVH/8fiWQAA2ID25TxfGGy8U0n77oD25UMAwBq1b/XrN92p5YL+oAGAo7ly
|
||||
sOFOLZf1Bw0AHM21gw13avlYf9AAwOGduaUv+zlq2ocTndEfPABwOOcONtup5m79wQMAh/OgwUY71Tyg
|
||||
P3gA4HAuHGy0U413AgDAmlw02GinmnasAMAaKAAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJAC
|
||||
AAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAA
|
||||
UJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAF
|
||||
KQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJAC
|
||||
AAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAA
|
||||
UJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAF
|
||||
KQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJAC
|
||||
AAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAA
|
||||
UJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAF
|
||||
KQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJAC
|
||||
AAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAA
|
||||
UJACAAAFKQAAUJACAAAFKQAAUJACAAAFKQAAUJACAMDinZPkUUmeleRVSd6c5B1J3prkmiTPTfLEJPdM
|
||||
cov+Ly+UAjA9be3da7UWn5fkNUnetlqrbc22tXtJkkcmuWP/lwG4yQ8leXaSawcbyolyXZKrkjwoySn9
|
||||
D10QBWAa2hp7cJJXJvni4NxPlH9Zldof7H8oQEX3SfL6JDcOBuZB89Ekj17oswIKwG61NfWYJP88ON+D
|
||||
pq311yW5d38jABV8d5Ir17Tx9/ngqlgsiQKwO/dN8uHBeR41be1fkeT2/Q0CLNX9kvz7YCCuM99cPd26
|
||||
lGcDFIDta0/3PznJ9YNzXGc+meT+/Y0DLM3jVptzPwQ3lfaiwdP7g5ghBWC72pp57eDcNpVvJHlsfxAA
|
||||
S/GUJN8aDL9N551Jzu4PZmYUgO1pa6Wtmf68Np32/8av9gcDMHftxXm72PyP5X1Jbt0f1IwoANtxVpJ3
|
||||
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
|
||||
BlC5AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
3Sz9f1mSMXfnTj0pAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="hideNestedButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
</value>
|
||||
</data>
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAUAQEAAAAEAIAAoQgAAVgAAADAwAAABACAAqCUAAH5CAAAgIAAAAQAgAKgQAAAmaAAAGBgAAAEA
|
||||
|
||||
+87
-30
@@ -29,6 +29,17 @@ namespace OpenNest.Forms
|
||||
private const float ZoomInFactor = 1.5f;
|
||||
private const float ZoomOutFactor = 1.0f / ZoomInFactor;
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
if (keyData == Keys.Escape && activeForm?.PlateView != null)
|
||||
{
|
||||
activeForm.PlateView.ProcessEscapeKey();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -109,6 +120,7 @@ namespace OpenNest.Forms
|
||||
{
|
||||
NavigationEnableCheck();
|
||||
UpdatePlateStatus();
|
||||
mnuPlateRemove.Enabled = activeForm?.PlateManager.CanRemoveCurrent ?? false;
|
||||
};
|
||||
editForm.WindowState = windowState;
|
||||
editForm.Show();
|
||||
@@ -179,10 +191,10 @@ namespace OpenNest.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
mnuNestPreviousPlate.Enabled = !activeForm.IsFirstPlate();
|
||||
mnuNestNextPlate.Enabled = !activeForm.IsLastPlate();
|
||||
mnuNestFirstPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsFirstPlate();
|
||||
mnuNestLastPlate.Enabled = activeForm.PlateCount > 0 && !activeForm.IsLastPlate();
|
||||
mnuNestPreviousPlate.Enabled = !activeForm.PlateManager.IsFirst;
|
||||
mnuNestNextPlate.Enabled = !activeForm.PlateManager.IsLast;
|
||||
mnuNestFirstPlate.Enabled = activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsFirst;
|
||||
mnuNestLastPlate.Enabled = activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsLast;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,10 +207,10 @@ namespace OpenNest.Forms
|
||||
mnuPlate.Enabled = !locked;
|
||||
|
||||
// Lock plate navigation
|
||||
mnuNestPreviousPlate.Enabled = !locked && activeForm != null && !activeForm.IsFirstPlate();
|
||||
mnuNestNextPlate.Enabled = !locked && activeForm != null && !activeForm.IsLastPlate();
|
||||
mnuNestFirstPlate.Enabled = !locked && activeForm != null && activeForm.PlateCount > 0 && !activeForm.IsFirstPlate();
|
||||
mnuNestLastPlate.Enabled = !locked && activeForm != null && activeForm.PlateCount > 0 && !activeForm.IsLastPlate();
|
||||
mnuNestPreviousPlate.Enabled = !locked && activeForm != null && !activeForm.PlateManager.IsFirst;
|
||||
mnuNestNextPlate.Enabled = !locked && activeForm != null && !activeForm.PlateManager.IsLast;
|
||||
mnuNestFirstPlate.Enabled = !locked && activeForm != null && activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsFirst;
|
||||
mnuNestLastPlate.Enabled = !locked && activeForm != null && activeForm.PlateManager.Count > 0 && !activeForm.PlateManager.IsLast;
|
||||
}
|
||||
|
||||
private void UpdateLocationStatus()
|
||||
@@ -227,8 +239,8 @@ namespace OpenNest.Forms
|
||||
|
||||
plateIndexStatusLabel.Text = string.Format(
|
||||
"Plate: {0} of {1}",
|
||||
activeForm.CurrentPlateIndex + 1,
|
||||
activeForm.PlateCount);
|
||||
activeForm.PlateManager.CurrentIndex + 1,
|
||||
activeForm.PlateManager.Count);
|
||||
|
||||
plateSizeStatusLabel.Text = string.Format(
|
||||
"Size: {0}",
|
||||
@@ -712,7 +724,7 @@ namespace OpenNest.Forms
|
||||
foreach (var part in result.Parts)
|
||||
plate.Parts.Add(part);
|
||||
|
||||
activeForm.LoadLastPlate();
|
||||
activeForm.PlateManager.LoadLast();
|
||||
}
|
||||
|
||||
activeForm.Nest.UpdateDrawingQuantities();
|
||||
@@ -832,25 +844,25 @@ namespace OpenNest.Forms
|
||||
private void LoadFirstPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null) return;
|
||||
activeForm.LoadFirstPlate();
|
||||
activeForm.PlateManager.LoadFirst();
|
||||
}
|
||||
|
||||
private void LoadLastPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null) return;
|
||||
activeForm.LoadLastPlate();
|
||||
activeForm.PlateManager.LoadLast();
|
||||
}
|
||||
|
||||
private void LoadPreviousPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null) return;
|
||||
activeForm.LoadPreviousPlate();
|
||||
activeForm.PlateManager.LoadPrevious();
|
||||
}
|
||||
|
||||
private void LoadNextPlate_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (activeForm == null) return;
|
||||
activeForm.LoadNextPlate();
|
||||
activeForm.PlateManager.LoadNext();
|
||||
}
|
||||
|
||||
private RemnantViewerForm remnantViewer;
|
||||
@@ -900,14 +912,33 @@ namespace OpenNest.Forms
|
||||
var form = new AutoNestForm(activeForm.Nest);
|
||||
form.AllowPlateCreation = true;
|
||||
|
||||
if (activeForm.Nest.PlateOptions.Count > 0)
|
||||
form.LoadPlateOptions(activeForm.Nest.PlateOptions, activeForm.Nest.SalvageRate);
|
||||
|
||||
if (form.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||
return;
|
||||
|
||||
if (form.EngineName != null)
|
||||
{
|
||||
NestEngineRegistry.ActiveEngineName = form.EngineName;
|
||||
engineComboBox.SelectedItem = form.EngineName;
|
||||
}
|
||||
|
||||
var items = form.GetNestItems();
|
||||
|
||||
if (!items.Any(it => it.Quantity > 0))
|
||||
return;
|
||||
|
||||
var optimizePlateSize = form.OptimizePlateSize;
|
||||
var plateOptions = optimizePlateSize ? form.GetPlateOptions() : null;
|
||||
var salvageRate = form.SalvageRate;
|
||||
|
||||
if (optimizePlateSize)
|
||||
{
|
||||
activeForm.Nest.PlateOptions = plateOptions;
|
||||
activeForm.Nest.SalvageRate = salvageRate;
|
||||
}
|
||||
|
||||
nestingCts = new CancellationTokenSource();
|
||||
var progressForm = new NestProgressForm(nestingCts, showPlateRow: true);
|
||||
progressForm.PreviewPlate = CreatePreviewPlate(activeForm.PlateView.Plate);
|
||||
@@ -928,7 +959,8 @@ namespace OpenNest.Forms
|
||||
|
||||
try
|
||||
{
|
||||
await RunAutoNestAsync(items, progressForm, progress, nestingCts.Token);
|
||||
await RunAutoNestAsync(items, progressForm, progress, nestingCts.Token,
|
||||
plateOptions, salvageRate);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -950,7 +982,9 @@ namespace OpenNest.Forms
|
||||
List<NestItem> items,
|
||||
NestProgressForm progressForm,
|
||||
IProgress<NestProgress> progress,
|
||||
CancellationToken token)
|
||||
CancellationToken token,
|
||||
List<PlateOption> plateOptions = null,
|
||||
double salvageRate = 0.5)
|
||||
{
|
||||
const int maxPlates = 100;
|
||||
|
||||
@@ -964,7 +998,8 @@ namespace OpenNest.Forms
|
||||
var plate = GetOrCreatePlate(progressForm);
|
||||
|
||||
var placed = await NestSinglePlateAsync(
|
||||
plate, plateIndex, remaining, progressForm, progress, token);
|
||||
plate, plateIndex, remaining, progressForm, progress, token,
|
||||
plateOptions, salvageRate);
|
||||
|
||||
if (!placed)
|
||||
break;
|
||||
@@ -976,13 +1011,8 @@ namespace OpenNest.Forms
|
||||
|
||||
private Plate GetOrCreatePlate(NestProgressForm progressForm)
|
||||
{
|
||||
var currentPlate = activeForm.PlateView.Plate;
|
||||
|
||||
if (currentPlate.Parts.Count == 0)
|
||||
return currentPlate;
|
||||
|
||||
var plate = activeForm.Nest.CreatePlate();
|
||||
activeForm.LoadLastPlate();
|
||||
var plate = activeForm.PlateManager.GetOrCreateEmpty();
|
||||
activeForm.PlateManager.LoadLast();
|
||||
progressForm.PreviewPlate = CreatePreviewPlate(plate);
|
||||
return plate;
|
||||
}
|
||||
@@ -993,13 +1023,40 @@ namespace OpenNest.Forms
|
||||
List<NestItem> items,
|
||||
NestProgressForm progressForm,
|
||||
IProgress<NestProgress> progress,
|
||||
CancellationToken token)
|
||||
CancellationToken token,
|
||||
List<PlateOption> plateOptions = null,
|
||||
double salvageRate = 0.5)
|
||||
{
|
||||
var engine = NestEngineRegistry.Create(plate);
|
||||
engine.PlateNumber = plateIndex;
|
||||
List<Part> nestParts;
|
||||
|
||||
var nestParts = await Task.Run(() =>
|
||||
engine.Nest(items, progress, token));
|
||||
if (plateOptions != null && plateOptions.Count > 0)
|
||||
{
|
||||
var result = await Task.Run(() =>
|
||||
PlateOptimizer.Optimize(items, plateOptions, salvageRate, plate, progress, token));
|
||||
|
||||
if (result == null || result.Parts.Count == 0 ||
|
||||
(token.IsCancellationRequested && !progressForm.Accepted))
|
||||
return false;
|
||||
|
||||
plate.Size = new Geometry.Size(result.ChosenSize.Width, result.ChosenSize.Length);
|
||||
nestParts = result.Parts;
|
||||
|
||||
// Deduct placed quantities — the optimizer clones items internally
|
||||
// so the originals are untouched after dry runs.
|
||||
foreach (var item in items)
|
||||
{
|
||||
var placed = nestParts.Count(p => p.BaseDrawing.Name == item.Drawing.Name);
|
||||
item.Quantity = System.Math.Max(0, item.Quantity - placed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var engine = NestEngineRegistry.Create(plate);
|
||||
engine.PlateNumber = plateIndex;
|
||||
|
||||
nestParts = await Task.Run(() =>
|
||||
engine.Nest(items, progress, token));
|
||||
}
|
||||
|
||||
activeForm.PlateView.ClearPreviewParts();
|
||||
|
||||
|
||||
+25
-5
@@ -19,7 +19,7 @@ namespace OpenNest.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
@@ -80,6 +80,16 @@ namespace OpenNest.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap clock {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("clock", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@@ -89,13 +99,13 @@ namespace OpenNest.Properties {
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap clock {
|
||||
internal static System.Drawing.Bitmap delete {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("clock", resourceCulture);
|
||||
object obj = ResourceManager.GetObject("delete", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
@@ -170,6 +180,16 @@ namespace OpenNest.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap plus {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("plus", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@@ -179,7 +199,7 @@ namespace OpenNest.Properties {
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
||||
@@ -118,67 +118,73 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="clear" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\clear.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="remove" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="clock" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\clock.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="doc_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\doc_new.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="doc_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\doc_open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="import" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\import.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_first" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_first.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_last" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_last.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_next" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_next.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_previous" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_previous.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="remove" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="rotate_ccw" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\rotate_ccw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="rotate_cw" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\rotate_cw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="save" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="save_as" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\save_as.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="zoom_in" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zoom_in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="watermark" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\watermark.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="clear" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\clear.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="save_as" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\save_as.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_last" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_last.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="import" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\import.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="zoom_all" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zoom_all.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="zoom_in" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zoom_in.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="move_first" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_first.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="save" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="zoom_out" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\zoom_out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="cutoff" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\cutoff.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
<data name="move_next" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_next.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="remnants" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\remnants.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="move_previous" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\move_previous.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="rotate_cw" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\rotate_cw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="rotate_ccw" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\rotate_ccw.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="cutoff" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\cutoff.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="doc_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\doc_open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="doc_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\doc_new.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="plus" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\plus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Reference in New Issue
Block a user