Compare commits
8 Commits
93a8981d0a
...
a548d5329a
| Author | SHA1 | Date | |
|---|---|---|---|
| a548d5329a | |||
| 07012033c7 | |||
| 92b17b2963 | |||
| b6ee04f038 | |||
| 8ffdacd6c0 | |||
| ccd402c50f | |||
| b1e872577c | |||
| 9903478d3e |
@@ -66,8 +66,15 @@ namespace OpenNest
|
|||||||
if (item.Quantity > 0 && best.Count > item.Quantity)
|
if (item.Quantity > 0 && best.Count > item.Quantity)
|
||||||
best = ShrinkFiller.TrimToCount(best, item.Quantity, ShrinkAxis.Width);
|
best = ShrinkFiller.TrimToCount(best, item.Quantity, ShrinkAxis.Width);
|
||||||
|
|
||||||
ReportProgress(progress, WinnerPhase, PlateNumber, best, workArea, BuildProgressSummary(),
|
ReportProgress(progress, new ProgressReport
|
||||||
isOverallBest: true);
|
{
|
||||||
|
Phase = WinnerPhase,
|
||||||
|
PlateNumber = PlateNumber,
|
||||||
|
Parts = best,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = BuildProgressSummary(),
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
|
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
@@ -94,8 +101,15 @@ namespace OpenNest
|
|||||||
|
|
||||||
Debug.WriteLine($"[Fill(groupParts,Box)] Linear pattern: {best?.Count ?? 0} parts | WorkArea: {workArea.Width:F1}x{workArea.Length:F1}");
|
Debug.WriteLine($"[Fill(groupParts,Box)] Linear pattern: {best?.Count ?? 0} parts | WorkArea: {workArea.Width:F1}x{workArea.Length:F1}");
|
||||||
|
|
||||||
ReportProgress(progress, NestPhase.Linear, PlateNumber, best, workArea, BuildProgressSummary(),
|
ReportProgress(progress, new ProgressReport
|
||||||
isOverallBest: true);
|
{
|
||||||
|
Phase = NestPhase.Linear,
|
||||||
|
PlateNumber = PlateNumber,
|
||||||
|
Parts = best,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = BuildProgressSummary(),
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
|
|
||||||
return best ?? new List<Part>();
|
return best ?? new List<Part>();
|
||||||
}
|
}
|
||||||
@@ -151,9 +165,15 @@ namespace OpenNest
|
|||||||
|
|
||||||
if (context.CurrentBest != null && context.CurrentBest.Count > 0)
|
if (context.CurrentBest != null && context.CurrentBest.Count > 0)
|
||||||
{
|
{
|
||||||
ReportProgress(context.Progress, context.WinnerPhase, PlateNumber,
|
ReportProgress(context.Progress, new ProgressReport
|
||||||
context.CurrentBest, context.WorkArea, BuildProgressSummary(),
|
{
|
||||||
isOverallBest: true);
|
Phase = context.WinnerPhase,
|
||||||
|
PlateNumber = PlateNumber,
|
||||||
|
Parts = context.CurrentBest,
|
||||||
|
WorkArea = context.WorkArea,
|
||||||
|
Description = BuildProgressSummary(),
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ namespace OpenNest.Engine.Fill
|
|||||||
combined.AddRange(previousParts);
|
combined.AddRange(previousParts);
|
||||||
combined.AddRange(value.BestParts);
|
combined.AddRange(value.BestParts);
|
||||||
value.BestParts = combined;
|
value.BestParts = combined;
|
||||||
value.BestPartCount = combined.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner.Report(value);
|
inner.Report(value);
|
||||||
|
|||||||
@@ -7,74 +7,30 @@ namespace OpenNest
|
|||||||
public static bool FindFrom2(double length1, double length2, double overallLength, out int count1, out int count2)
|
public static bool FindFrom2(double length1, double length2, double overallLength, out int count1, out int count2)
|
||||||
{
|
{
|
||||||
overallLength += Tolerance.Epsilon;
|
overallLength += Tolerance.Epsilon;
|
||||||
|
count1 = 0;
|
||||||
if (length1 > overallLength)
|
|
||||||
{
|
|
||||||
if (length2 > overallLength)
|
|
||||||
{
|
|
||||||
count1 = 0;
|
|
||||||
count2 = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
count1 = 0;
|
|
||||||
count2 = (int)System.Math.Floor(overallLength / length2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length2 > overallLength)
|
|
||||||
{
|
|
||||||
count1 = (int)System.Math.Floor(overallLength / length1);
|
|
||||||
count2 = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxCountLength1 = (int)System.Math.Floor(overallLength / length1);
|
|
||||||
|
|
||||||
count1 = maxCountLength1;
|
|
||||||
count2 = 0;
|
count2 = 0;
|
||||||
|
|
||||||
var remnant = overallLength - maxCountLength1 * length1;
|
var maxCount1 = (int)System.Math.Floor(overallLength / length1);
|
||||||
|
var bestRemnant = overallLength + 1;
|
||||||
|
|
||||||
if (remnant.IsEqualTo(0))
|
for (var c1 = 0; c1 <= maxCount1; c1++)
|
||||||
return true;
|
|
||||||
|
|
||||||
for (int countLength1 = 0; countLength1 <= maxCountLength1; ++countLength1)
|
|
||||||
{
|
{
|
||||||
var remnant1 = overallLength - countLength1 * length1;
|
var remaining = overallLength - c1 * length1;
|
||||||
|
var c2 = (int)System.Math.Floor(remaining / length2);
|
||||||
|
var remnant = remaining - c2 * length2;
|
||||||
|
|
||||||
if (remnant1 >= length2)
|
if (!(remnant < bestRemnant))
|
||||||
{
|
continue;
|
||||||
var countLength2 = (int)System.Math.Floor(remnant1 / length2);
|
|
||||||
var remnant2 = remnant1 - length2 * countLength2;
|
|
||||||
|
|
||||||
if (!(remnant2 < remnant))
|
count1 = c1;
|
||||||
continue;
|
count2 = c2;
|
||||||
|
bestRemnant = remnant;
|
||||||
|
|
||||||
count1 = countLength1;
|
if (remnant.IsEqualTo(0))
|
||||||
count2 = countLength2;
|
break;
|
||||||
|
|
||||||
if (remnant2.IsEqualTo(0))
|
|
||||||
break;
|
|
||||||
|
|
||||||
remnant = remnant2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(remnant1 < remnant))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
count1 = countLength1;
|
|
||||||
count2 = 0;
|
|
||||||
|
|
||||||
if (remnant1.IsEqualTo(0))
|
|
||||||
break;
|
|
||||||
|
|
||||||
remnant = remnant1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return count1 > 0 || count2 > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,18 +36,36 @@ namespace OpenNest.Engine.Fill
|
|||||||
if (column.Count == 0)
|
if (column.Count == 0)
|
||||||
return new List<Part>();
|
return new List<Part>();
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Extents, plateNumber,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
column, workArea, $"Extents: initial column {column.Count} parts");
|
{
|
||||||
|
Phase = NestPhase.Extents,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = column,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"Extents: initial column {column.Count} parts",
|
||||||
|
});
|
||||||
|
|
||||||
var adjusted = AdjustColumn(pair.Value, column, token);
|
var adjusted = AdjustColumn(pair.Value, column, token);
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Extents, plateNumber,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
adjusted, workArea, $"Extents: adjusted column {adjusted.Count} parts");
|
{
|
||||||
|
Phase = NestPhase.Extents,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = adjusted,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"Extents: adjusted column {adjusted.Count} parts",
|
||||||
|
});
|
||||||
|
|
||||||
var result = RepeatColumns(adjusted, token);
|
var result = RepeatColumns(adjusted, token);
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Extents, plateNumber,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
result, workArea, $"Extents: {result.Count} parts total");
|
{
|
||||||
|
Phase = NestPhase.Extents,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = result,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"Extents: {result.Count} parts total",
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
|
namespace OpenNest.Engine.Fill;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks evaluated grid configurations so duplicate pattern/direction/workArea
|
||||||
|
/// combinations can be skipped across fill strategies.
|
||||||
|
/// </summary>
|
||||||
|
public class GridDedup
|
||||||
|
{
|
||||||
|
public const string SharedStateKey = "GridDedup";
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<GridKey, byte> _seen = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if this configuration has NOT been seen before (i.e., should be evaluated).
|
||||||
|
/// Returns false if it's a duplicate.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryAdd(Box patternBox, Box workArea, NestDirection dir)
|
||||||
|
{
|
||||||
|
var key = new GridKey(patternBox, workArea, dir);
|
||||||
|
return _seen.TryAdd(key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _seen.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or creates a GridDedup from FillContext.SharedState.
|
||||||
|
/// </summary>
|
||||||
|
public static GridDedup GetOrCreate(System.Collections.Generic.Dictionary<string, object> sharedState)
|
||||||
|
{
|
||||||
|
if (sharedState.TryGetValue(SharedStateKey, out var existing))
|
||||||
|
return (GridDedup)existing;
|
||||||
|
|
||||||
|
var dedup = new GridDedup();
|
||||||
|
sharedState[SharedStateKey] = dedup;
|
||||||
|
return dedup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct GridKey : IEquatable<GridKey>
|
||||||
|
{
|
||||||
|
private readonly int _patternW, _patternL, _workW, _workL, _dir;
|
||||||
|
|
||||||
|
public GridKey(Box patternBox, Box workArea, NestDirection dir)
|
||||||
|
{
|
||||||
|
_patternW = (int)System.Math.Round(patternBox.Width * 10);
|
||||||
|
_patternL = (int)System.Math.Round(patternBox.Length * 10);
|
||||||
|
_workW = (int)System.Math.Round(workArea.Width * 10);
|
||||||
|
_workL = (int)System.Math.Round(workArea.Length * 10);
|
||||||
|
_dir = (int)dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(GridKey other) =>
|
||||||
|
_patternW == other._patternW && _patternL == other._patternL &&
|
||||||
|
_workW == other._workW && _workL == other._workL &&
|
||||||
|
_dir == other._dir;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is GridKey other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
var hash = _patternW;
|
||||||
|
hash = hash * 397 ^ _patternL;
|
||||||
|
hash = hash * 397 ^ _workW;
|
||||||
|
hash = hash * 397 ^ _workL;
|
||||||
|
hash = hash * 397 ^ _dir;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,8 @@ namespace OpenNest.Engine.Fill
|
|||||||
double spacing,
|
double spacing,
|
||||||
CancellationToken token = default,
|
CancellationToken token = default,
|
||||||
IProgress<NestProgress> progress = null,
|
IProgress<NestProgress> progress = null,
|
||||||
int plateNumber = 0)
|
int plateNumber = 0,
|
||||||
|
Func<NestItem, Box, List<Part>> widthFillFunc = null)
|
||||||
{
|
{
|
||||||
if (items == null || items.Count == 0)
|
if (items == null || items.Count == 0)
|
||||||
return new IterativeShrinkResult();
|
return new IterativeShrinkResult();
|
||||||
@@ -72,6 +73,8 @@ namespace OpenNest.Engine.Fill
|
|||||||
// include them in progress reports.
|
// include them in progress reports.
|
||||||
var placedSoFar = new List<Part>();
|
var placedSoFar = new List<Part>();
|
||||||
|
|
||||||
|
var wFillFunc = widthFillFunc ?? fillFunc;
|
||||||
|
|
||||||
Func<NestItem, Box, List<Part>> shrinkWrapper = (ni, box) =>
|
Func<NestItem, Box, List<Part>> shrinkWrapper = (ni, box) =>
|
||||||
{
|
{
|
||||||
var target = ni.Quantity > 0 ? ni.Quantity : 0;
|
var target = ni.Quantity > 0 ? ni.Quantity : 0;
|
||||||
@@ -84,7 +87,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
Parallel.Invoke(
|
Parallel.Invoke(
|
||||||
() => heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token,
|
() => heightResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Height, token,
|
||||||
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar),
|
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar),
|
||||||
() => widthResult = ShrinkFiller.Shrink(fillFunc, ni, box, spacing, ShrinkAxis.Width, token,
|
() => widthResult = ShrinkFiller.Shrink(wFillFunc, ni, box, spacing, ShrinkAxis.Width, token,
|
||||||
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar)
|
targetCount: target, progress: progress, plateNumber: plateNumber, placedParts: placedSoFar)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -108,8 +111,15 @@ namespace OpenNest.Engine.Fill
|
|||||||
var allParts = new List<Part>(placedSoFar.Count + best.Count);
|
var allParts = new List<Part>(placedSoFar.Count + best.Count);
|
||||||
allParts.AddRange(placedSoFar);
|
allParts.AddRange(placedSoFar);
|
||||||
allParts.AddRange(best);
|
allParts.AddRange(best);
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Custom, plateNumber,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
allParts, box, $"Shrink: {best.Count} parts placed", isOverallBest: true);
|
{
|
||||||
|
Phase = NestPhase.Custom,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = allParts,
|
||||||
|
WorkArea = box,
|
||||||
|
Description = $"Shrink: {best.Count} parts placed",
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate for the next item's progress reports.
|
// Accumulate for the next item's progress reports.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using OpenNest.Engine;
|
using OpenNest.Engine;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Fill
|
namespace OpenNest.Engine.Fill
|
||||||
@@ -32,13 +33,15 @@ namespace OpenNest.Engine.Fill
|
|||||||
private readonly Size plateSize;
|
private readonly Size plateSize;
|
||||||
private readonly double partSpacing;
|
private readonly double partSpacing;
|
||||||
private readonly IFillComparer comparer;
|
private readonly IFillComparer comparer;
|
||||||
|
private readonly GridDedup dedup;
|
||||||
|
|
||||||
public PairFiller(Plate plate, IFillComparer comparer = null)
|
public PairFiller(Plate plate, IFillComparer comparer = null, GridDedup dedup = null)
|
||||||
{
|
{
|
||||||
this.plate = plate;
|
this.plate = plate;
|
||||||
this.plateSize = plate.Size;
|
this.plateSize = plate.Size;
|
||||||
this.partSpacing = plate.PartSpacing;
|
this.partSpacing = plate.PartSpacing;
|
||||||
this.comparer = comparer ?? new DefaultFillComparer();
|
this.comparer = comparer ?? new DefaultFillComparer();
|
||||||
|
this.dedup = dedup ?? new GridDedup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairFillResult Fill(NestItem item, Box workArea,
|
public PairFillResult Fill(NestItem item, Box workArea,
|
||||||
@@ -68,32 +71,60 @@ namespace OpenNest.Engine.Fill
|
|||||||
List<Part> best = null;
|
List<Part> best = null;
|
||||||
var sinceImproved = 0;
|
var sinceImproved = 0;
|
||||||
var effectiveWorkArea = workArea;
|
var effectiveWorkArea = workArea;
|
||||||
|
var batchSize = System.Math.Max(2, Environment.ProcessorCount);
|
||||||
|
|
||||||
|
var maxUtilization = candidates.Count > 0 ? candidates.Max(c => c.Utilization) : 1.0;
|
||||||
|
var partBox = drawing.Program.BoundingBox();
|
||||||
|
var partArea = System.Math.Max(partBox.Width * partBox.Length, 1);
|
||||||
|
|
||||||
|
FillStrategyRegistry.SetEnabled("Pairs", "RectBestFit", "Extents", "Linear");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (var i = 0; i < candidates.Count; i++)
|
for (var batchStart = 0; batchStart < candidates.Count; batchStart += batchSize)
|
||||||
{
|
{
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var filled = EvaluateCandidate(candidates[i], drawing, effectiveWorkArea, token);
|
var batchEnd = System.Math.Min(batchStart + batchSize, candidates.Count);
|
||||||
|
var batchCount = batchEnd - batchStart;
|
||||||
|
var batchWorkArea = effectiveWorkArea;
|
||||||
|
var minCountToBeat = best?.Count ?? 0;
|
||||||
|
|
||||||
if (comparer.IsBetter(filled, best, effectiveWorkArea))
|
var results = new List<Part>[batchCount];
|
||||||
|
Parallel.For(0, batchCount,
|
||||||
|
new ParallelOptions { CancellationToken = token },
|
||||||
|
j =>
|
||||||
|
{
|
||||||
|
results[j] = EvaluateCandidate(
|
||||||
|
candidates[batchStart + j], drawing, batchWorkArea,
|
||||||
|
minCountToBeat, maxUtilization, partArea, token);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var j = 0; j < batchCount; j++)
|
||||||
{
|
{
|
||||||
best = filled;
|
if (comparer.IsBetter(results[j], best, effectiveWorkArea))
|
||||||
sinceImproved = 0;
|
{
|
||||||
effectiveWorkArea = TryReduceWorkArea(filled, targetCount, workArea, effectiveWorkArea);
|
best = results[j];
|
||||||
}
|
sinceImproved = 0;
|
||||||
else
|
effectiveWorkArea = TryReduceWorkArea(best, targetCount, workArea, effectiveWorkArea);
|
||||||
{
|
}
|
||||||
sinceImproved++;
|
else
|
||||||
|
{
|
||||||
|
sinceImproved++;
|
||||||
|
}
|
||||||
|
|
||||||
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
|
{
|
||||||
|
Phase = NestPhase.Pairs,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = best,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"Pairs: {batchStart + j + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Pairs, plateNumber, best, workArea,
|
if (batchEnd >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
|
||||||
$"Pairs: {i + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts");
|
|
||||||
|
|
||||||
if (i + 1 >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
|
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"[PairFiller] Early exit at {i + 1}/{candidates.Count} — no improvement in last {sinceImproved} candidates");
|
Debug.WriteLine($"[PairFiller] Early exit at {batchEnd}/{candidates.Count} — no improvement in last {sinceImproved} candidates");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,6 +133,10 @@ namespace OpenNest.Engine.Fill
|
|||||||
{
|
{
|
||||||
Debug.WriteLine("[PairFiller] Cancelled mid-phase, using results so far");
|
Debug.WriteLine("[PairFiller] Cancelled mid-phase, using results so far");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
FillStrategyRegistry.SetEnabled(null);
|
||||||
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"[PairFiller] Best pair result: {best?.Count ?? 0} parts");
|
Debug.WriteLine($"[PairFiller] Best pair result: {best?.Count ?? 0} parts");
|
||||||
return best ?? new List<Part>();
|
return best ?? new List<Part>();
|
||||||
@@ -145,7 +180,8 @@ namespace OpenNest.Engine.Fill
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Part> EvaluateCandidate(BestFitResult candidate, Drawing drawing,
|
private List<Part> EvaluateCandidate(BestFitResult candidate, Drawing drawing,
|
||||||
Box workArea, CancellationToken token)
|
Box workArea, int minCountToBeat, double maxUtilization, double partArea,
|
||||||
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var pairParts = candidate.BuildParts(drawing);
|
var pairParts = candidate.BuildParts(drawing);
|
||||||
var angles = BuildTilingAngles(candidate);
|
var angles = BuildTilingAngles(candidate);
|
||||||
@@ -162,6 +198,9 @@ namespace OpenNest.Engine.Fill
|
|||||||
var engine = new FillLinear(workArea, partSpacing);
|
var engine = new FillLinear(workArea, partSpacing);
|
||||||
foreach (var dir in new[] { NestDirection.Horizontal, NestDirection.Vertical })
|
foreach (var dir in new[] { NestDirection.Horizontal, NestDirection.Vertical })
|
||||||
{
|
{
|
||||||
|
if (!dedup.TryAdd(pattern.BoundingBox, workArea, dir))
|
||||||
|
continue;
|
||||||
|
|
||||||
var gridParts = engine.Fill(pattern, dir);
|
var gridParts = engine.Fill(pattern, dir);
|
||||||
if (gridParts != null && gridParts.Count > 0)
|
if (gridParts != null && gridParts.Count > 0)
|
||||||
grids.Add((gridParts, dir));
|
grids.Add((gridParts, dir));
|
||||||
@@ -174,17 +213,34 @@ namespace OpenNest.Engine.Fill
|
|||||||
// Sort by count descending so we try the best grids first
|
// Sort by count descending so we try the best grids first
|
||||||
grids.Sort((a, b) => b.Parts.Count.CompareTo(a.Parts.Count));
|
grids.Sort((a, b) => b.Parts.Count.CompareTo(a.Parts.Count));
|
||||||
|
|
||||||
|
// Early abort: if the best grid + optimistic remnant can't beat the global best, skip Phase 2
|
||||||
|
if (minCountToBeat > 0)
|
||||||
|
{
|
||||||
|
var topCount = grids[0].Parts.Count;
|
||||||
|
var optimisticRemnant = EstimateRemnantUpperBound(
|
||||||
|
grids[0].Parts, workArea, maxUtilization, partArea);
|
||||||
|
if (topCount + optimisticRemnant <= minCountToBeat)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[PairFiller] Skipping candidate: grid {topCount} + estimate {optimisticRemnant} <= best {minCountToBeat}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 2: try remnant for each grid, skip if grid is too far behind
|
// Phase 2: try remnant for each grid, skip if grid is too far behind
|
||||||
List<Part> best = null;
|
List<Part> best = null;
|
||||||
var maxRemnantEstimate = EstimateMaxRemnantParts(drawing, workArea);
|
|
||||||
|
|
||||||
foreach (var (gridParts, dir) in grids)
|
foreach (var (gridParts, dir) in grids)
|
||||||
{
|
{
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// If this grid + max possible remnant can't beat current best, skip
|
// If this grid + max possible remnant can't beat current best, skip
|
||||||
if (best != null && gridParts.Count + maxRemnantEstimate <= best.Count)
|
if (best != null)
|
||||||
break; // sorted descending, so remaining are even smaller
|
{
|
||||||
|
var remnantBound = EstimateRemnantUpperBound(
|
||||||
|
gridParts, workArea, maxUtilization, partArea);
|
||||||
|
if (gridParts.Count + remnantBound <= best.Count)
|
||||||
|
break; // sorted descending, so remaining are even smaller
|
||||||
|
}
|
||||||
|
|
||||||
var remnantParts = FillRemnant(gridParts, drawing, workArea, token);
|
var remnantParts = FillRemnant(gridParts, drawing, workArea, token);
|
||||||
List<Part> total;
|
List<Part> total;
|
||||||
@@ -206,12 +262,20 @@ namespace OpenNest.Engine.Fill
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int EstimateMaxRemnantParts(Drawing drawing, Box workArea)
|
private int EstimateRemnantUpperBound(List<Part> gridParts, Box workArea,
|
||||||
|
double maxUtilization, double partArea)
|
||||||
{
|
{
|
||||||
var partBox = drawing.Program.BoundingBox();
|
var gridBox = ((IEnumerable<IBoundable>)gridParts).GetBoundingBox();
|
||||||
var partArea = System.Math.Max(partBox.Width * partBox.Length, 1);
|
|
||||||
var remnantArea = workArea.Area() * 0.3; // remnant is at most ~30% of work area
|
// L-shaped remnant: top strip (full width) + right strip (grid height only)
|
||||||
return (int)(remnantArea / partArea) + 1;
|
var topHeight = System.Math.Max(0, workArea.Top - gridBox.Top);
|
||||||
|
var rightWidth = System.Math.Max(0, workArea.Right - gridBox.Right);
|
||||||
|
|
||||||
|
var topArea = workArea.Width * topHeight;
|
||||||
|
var rightArea = rightWidth * System.Math.Min(gridBox.Top - workArea.Y, workArea.Length);
|
||||||
|
var remnantArea = topArea + rightArea;
|
||||||
|
|
||||||
|
return (int)(remnantArea * maxUtilization / partArea) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Part> FillRemnant(List<Part> gridParts, Drawing drawing,
|
private List<Part> FillRemnant(List<Part> gridParts, Drawing drawing,
|
||||||
@@ -257,28 +321,20 @@ namespace OpenNest.Engine.Fill
|
|||||||
return cachedResult;
|
return cachedResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
FillStrategyRegistry.SetEnabled("Pairs", "RectBestFit", "Extents", "Linear");
|
var remnantEngine = NestEngineRegistry.Create(plate);
|
||||||
try
|
var item = new NestItem { Drawing = drawing };
|
||||||
|
var parts = remnantEngine.Fill(item, remnantBox, null, token);
|
||||||
|
|
||||||
|
Debug.WriteLine($"[PairFiller] Remnant: {parts?.Count ?? 0} parts in " +
|
||||||
|
$"{remnantBox.Width:F2}x{remnantBox.Length:F2}");
|
||||||
|
|
||||||
|
if (parts != null && parts.Count > 0)
|
||||||
{
|
{
|
||||||
var remnantEngine = NestEngineRegistry.Create(plate);
|
FillResultCache.Store(drawing, remnantBox, partSpacing, parts);
|
||||||
var item = new NestItem { Drawing = drawing };
|
return parts;
|
||||||
var parts = remnantEngine.Fill(item, remnantBox, null, token);
|
|
||||||
|
|
||||||
Debug.WriteLine($"[PairFiller] Remnant: {parts?.Count ?? 0} parts in " +
|
|
||||||
$"{remnantBox.Width:F2}x{remnantBox.Length:F2}");
|
|
||||||
|
|
||||||
if (parts != null && parts.Count > 0)
|
|
||||||
{
|
|
||||||
FillResultCache.Store(drawing, remnantBox, partSpacing, parts);
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
FillStrategyRegistry.SetEnabled(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<double> BuildTilingAngles(BestFitResult candidate)
|
private static List<double> BuildTilingAngles(BestFitResult candidate)
|
||||||
|
|||||||
@@ -79,8 +79,14 @@ namespace OpenNest.Engine.Fill
|
|||||||
|
|
||||||
var desc = $"Shrink {axis}: {bestParts.Count} parts, dim={dim:F1}";
|
var desc = $"Shrink {axis}: {bestParts.Count} parts, dim={dim:F1}";
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Custom, plateNumber,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
allParts, workArea, desc);
|
{
|
||||||
|
Phase = NestPhase.Custom,
|
||||||
|
PlateNumber = plateNumber,
|
||||||
|
Parts = allParts,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = desc,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public class StripeFiller
|
|||||||
private readonly FillContext _context;
|
private readonly FillContext _context;
|
||||||
private readonly NestDirection _primaryAxis;
|
private readonly NestDirection _primaryAxis;
|
||||||
private readonly IFillComparer _comparer;
|
private readonly IFillComparer _comparer;
|
||||||
|
private readonly GridDedup _dedup;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When true, only complete stripes are placed — no partial rows/columns.
|
/// When true, only complete stripes are placed — no partial rows/columns.
|
||||||
@@ -38,6 +39,7 @@ public class StripeFiller
|
|||||||
_context = context;
|
_context = context;
|
||||||
_primaryAxis = primaryAxis;
|
_primaryAxis = primaryAxis;
|
||||||
_comparer = context.Policy?.Comparer ?? new DefaultFillComparer();
|
_comparer = context.Policy?.Comparer ?? new DefaultFillComparer();
|
||||||
|
_dedup = GridDedup.GetOrCreate(context.SharedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Part> Fill()
|
public List<Part> Fill()
|
||||||
@@ -93,9 +95,14 @@ public class StripeFiller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(_context.Progress, NestPhase.Custom,
|
NestEngineBase.ReportProgress(_context.Progress, new ProgressReport
|
||||||
_context.PlateNumber, bestParts, workArea,
|
{
|
||||||
$"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts");
|
Phase = NestPhase.Custom,
|
||||||
|
PlateNumber = _context.PlateNumber,
|
||||||
|
Parts = bestParts,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestParts ?? new List<Part>();
|
return bestParts ?? new List<Part>();
|
||||||
@@ -110,6 +117,10 @@ public class StripeFiller
|
|||||||
var rotatedPattern = FillHelpers.BuildRotatedPattern(pairParts, angle);
|
var rotatedPattern = FillHelpers.BuildRotatedPattern(pairParts, angle);
|
||||||
var perpDim = GetDimension(rotatedPattern.BoundingBox, perpAxis);
|
var perpDim = GetDimension(rotatedPattern.BoundingBox, perpAxis);
|
||||||
var stripeBox = MakeStripeBox(workArea, perpDim, primaryAxis);
|
var stripeBox = MakeStripeBox(workArea, perpDim, primaryAxis);
|
||||||
|
|
||||||
|
if (!_dedup.TryAdd(rotatedPattern.BoundingBox, workArea, primaryAxis))
|
||||||
|
return null;
|
||||||
|
|
||||||
var stripeEngine = new FillLinear(stripeBox, spacing);
|
var stripeEngine = new FillLinear(stripeBox, spacing);
|
||||||
var stripeParts = stripeEngine.Fill(rotatedPattern, primaryAxis);
|
var stripeParts = stripeEngine.Fill(rotatedPattern, primaryAxis);
|
||||||
|
|
||||||
|
|||||||
@@ -210,55 +210,26 @@ namespace OpenNest
|
|||||||
// --- Protected utilities ---
|
// --- Protected utilities ---
|
||||||
|
|
||||||
internal static void ReportProgress(
|
internal static void ReportProgress(
|
||||||
IProgress<NestProgress> progress,
|
IProgress<NestProgress> progress, ProgressReport report)
|
||||||
NestPhase phase,
|
|
||||||
int plateNumber,
|
|
||||||
List<Part> best,
|
|
||||||
Box workArea,
|
|
||||||
string description,
|
|
||||||
bool isOverallBest = false)
|
|
||||||
{
|
{
|
||||||
if (progress == null || best == null || best.Count == 0)
|
if (progress == null || report.Parts == null || report.Parts.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var score = FillScore.Compute(best, workArea);
|
var clonedParts = new List<Part>(report.Parts.Count);
|
||||||
var clonedParts = new List<Part>(best.Count);
|
foreach (var part in report.Parts)
|
||||||
var totalPartArea = 0.0;
|
|
||||||
|
|
||||||
foreach (var part in best)
|
|
||||||
{
|
|
||||||
clonedParts.Add((Part)part.Clone());
|
clonedParts.Add((Part)part.Clone());
|
||||||
totalPartArea += part.BaseDrawing.Area;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds = best.GetBoundingBox();
|
Debug.WriteLine($"[Progress] Phase={report.Phase}, Plate={report.PlateNumber}, " +
|
||||||
|
$"Parts={clonedParts.Count} | {report.Description}");
|
||||||
var msg = $"[Progress] Phase={phase}, Plate={plateNumber}, Parts={score.Count}, " +
|
|
||||||
$"Density={score.Density:P1}, Nested={bounds.Width:F1}x{bounds.Length:F1}, " +
|
|
||||||
$"PartArea={totalPartArea:F0}, Remnant={workArea.Area() - totalPartArea:F0}, " +
|
|
||||||
$"WorkArea={workArea.Width:F1}x{workArea.Length:F1} | {description}";
|
|
||||||
Debug.WriteLine(msg);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
System.IO.File.AppendAllText(
|
|
||||||
System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "nest-debug.log"),
|
|
||||||
$"{DateTime.Now:HH:mm:ss.fff} {msg}\n");
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
progress.Report(new NestProgress
|
progress.Report(new NestProgress
|
||||||
{
|
{
|
||||||
Phase = phase,
|
Phase = report.Phase,
|
||||||
PlateNumber = plateNumber,
|
PlateNumber = report.PlateNumber,
|
||||||
BestPartCount = score.Count,
|
|
||||||
BestDensity = score.Density,
|
|
||||||
NestedWidth = bounds.Width,
|
|
||||||
NestedLength = bounds.Length,
|
|
||||||
NestedArea = totalPartArea,
|
|
||||||
BestParts = clonedParts,
|
BestParts = clonedParts,
|
||||||
Description = description,
|
Description = report.Description,
|
||||||
ActiveWorkArea = workArea,
|
ActiveWorkArea = report.WorkArea,
|
||||||
IsOverallBest = isOverallBest,
|
IsOverallBest = report.IsOverallBest,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +241,7 @@ namespace OpenNest
|
|||||||
var parts = new List<string>(PhaseResults.Count);
|
var parts = new List<string>(PhaseResults.Count);
|
||||||
|
|
||||||
foreach (var r in PhaseResults)
|
foreach (var r in PhaseResults)
|
||||||
parts.Add($"{FormatPhaseName(r.Phase)}: {r.PartCount}");
|
parts.Add($"{r.Phase.ShortName()}: {r.PartCount}");
|
||||||
|
|
||||||
return string.Join(" | ", parts);
|
return string.Join(" | ", parts);
|
||||||
}
|
}
|
||||||
@@ -323,17 +294,5 @@ namespace OpenNest
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static string FormatPhaseName(NestPhase phase)
|
|
||||||
{
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case NestPhase.Pairs: return "Pairs";
|
|
||||||
case NestPhase.Linear: return "Linear";
|
|
||||||
case NestPhase.RectBestFit: return "BestFit";
|
|
||||||
case NestPhase.Extents: return "Extents";
|
|
||||||
case NestPhase.Custom: return "Custom";
|
|
||||||
default: return phase.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-12
@@ -1,16 +1,52 @@
|
|||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace OpenNest
|
namespace OpenNest
|
||||||
{
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
internal class ShortNameAttribute(string name) : Attribute
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
}
|
||||||
|
|
||||||
public enum NestPhase
|
public enum NestPhase
|
||||||
{
|
{
|
||||||
Linear,
|
[Description("Trying rotations..."), ShortName("Linear")] Linear,
|
||||||
RectBestFit,
|
[Description("Trying best fit..."), ShortName("BestFit")] RectBestFit,
|
||||||
Pairs,
|
[Description("Trying pairs..."), ShortName("Pairs")] Pairs,
|
||||||
Nfp,
|
[Description("Trying NFP..."), ShortName("NFP")] Nfp,
|
||||||
Extents,
|
[Description("Trying extents..."), ShortName("Extents")] Extents,
|
||||||
Custom
|
[Description("Custom"), ShortName("Custom")] Custom
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NestPhaseExtensions
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<NestPhase, string> DisplayNames = new();
|
||||||
|
private static readonly ConcurrentDictionary<NestPhase, string> ShortNames = new();
|
||||||
|
|
||||||
|
public static string DisplayName(this NestPhase phase)
|
||||||
|
{
|
||||||
|
return DisplayNames.GetOrAdd(phase, p =>
|
||||||
|
{
|
||||||
|
var field = typeof(NestPhase).GetField(p.ToString());
|
||||||
|
var attr = field?.GetCustomAttribute<DescriptionAttribute>();
|
||||||
|
return attr?.Description ?? p.ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ShortName(this NestPhase phase)
|
||||||
|
{
|
||||||
|
return ShortNames.GetOrAdd(phase, p =>
|
||||||
|
{
|
||||||
|
var field = typeof(NestPhase).GetField(p.ToString());
|
||||||
|
var attr = field?.GetCustomAttribute<ShortNameAttribute>();
|
||||||
|
return attr?.Name ?? p.ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PhaseResult
|
public class PhaseResult
|
||||||
@@ -34,18 +70,93 @@ namespace OpenNest
|
|||||||
public int PartCount { get; set; }
|
public int PartCount { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal readonly struct ProgressReport
|
||||||
|
{
|
||||||
|
public NestPhase Phase { get; init; }
|
||||||
|
public int PlateNumber { get; init; }
|
||||||
|
public List<Part> Parts { get; init; }
|
||||||
|
public Box WorkArea { get; init; }
|
||||||
|
public string Description { get; init; }
|
||||||
|
public bool IsOverallBest { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
public class NestProgress
|
public class NestProgress
|
||||||
{
|
{
|
||||||
public NestPhase Phase { get; set; }
|
public NestPhase Phase { get; set; }
|
||||||
public int PlateNumber { get; set; }
|
public int PlateNumber { get; set; }
|
||||||
public int BestPartCount { get; set; }
|
|
||||||
public double BestDensity { get; set; }
|
private List<Part> bestParts;
|
||||||
public double NestedWidth { get; set; }
|
public List<Part> BestParts
|
||||||
public double NestedLength { get; set; }
|
{
|
||||||
public double NestedArea { get; set; }
|
get => bestParts;
|
||||||
public List<Part> BestParts { get; set; }
|
set { bestParts = value; cachedParts = null; }
|
||||||
|
}
|
||||||
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public Box ActiveWorkArea { get; set; }
|
public Box ActiveWorkArea { get; set; }
|
||||||
public bool IsOverallBest { get; set; }
|
public bool IsOverallBest { get; set; }
|
||||||
|
|
||||||
|
public int BestPartCount => BestParts?.Count ?? 0;
|
||||||
|
|
||||||
|
private List<Part> cachedParts;
|
||||||
|
private Box cachedBounds;
|
||||||
|
private double cachedPartArea;
|
||||||
|
|
||||||
|
private void EnsureCache()
|
||||||
|
{
|
||||||
|
if (cachedParts == bestParts) return;
|
||||||
|
cachedParts = bestParts;
|
||||||
|
if (bestParts == null || bestParts.Count == 0)
|
||||||
|
{
|
||||||
|
cachedBounds = default;
|
||||||
|
cachedPartArea = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cachedBounds = bestParts.GetBoundingBox();
|
||||||
|
cachedPartArea = 0;
|
||||||
|
foreach (var p in bestParts)
|
||||||
|
cachedPartArea += p.BaseDrawing.Area;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double BestDensity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (BestParts == null || BestParts.Count == 0) return 0;
|
||||||
|
EnsureCache();
|
||||||
|
var bboxArea = cachedBounds.Width * cachedBounds.Length;
|
||||||
|
return bboxArea > 0 ? cachedPartArea / bboxArea : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double NestedWidth
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (BestParts == null || BestParts.Count == 0) return 0;
|
||||||
|
EnsureCache();
|
||||||
|
return cachedBounds.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double NestedLength
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (BestParts == null || BestParts.Count == 0) return 0;
|
||||||
|
EnsureCache();
|
||||||
|
return cachedBounds.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double NestedArea
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (BestParts == null || BestParts.Count == 0) return 0;
|
||||||
|
EnsureCache();
|
||||||
|
return cachedPartArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,8 +74,15 @@ namespace OpenNest.Engine.Nfp
|
|||||||
|
|
||||||
Debug.WriteLine($"[AutoNest] Result: {parts.Count} parts placed, {result.Iterations} SA iterations");
|
Debug.WriteLine($"[AutoNest] Result: {parts.Count} parts placed, {result.Iterations} SA iterations");
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Nfp, 0, parts, workArea,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
$"NFP: {parts.Count} parts, {result.Iterations} iterations", isOverallBest: true);
|
{
|
||||||
|
Phase = NestPhase.Nfp,
|
||||||
|
PlateNumber = 0,
|
||||||
|
Parts = parts,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"NFP: {parts.Count} parts, {result.Iterations} iterations",
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,8 +277,15 @@ namespace OpenNest.Engine.Nfp
|
|||||||
private static void ReportBest(IProgress<NestProgress> progress, List<Part> parts,
|
private static void ReportBest(IProgress<NestProgress> progress, List<Part> parts,
|
||||||
Box workArea, string description)
|
Box workArea, string description)
|
||||||
{
|
{
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Nfp, 0, parts, workArea,
|
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||||
description, isOverallBest: true);
|
{
|
||||||
|
Phase = NestPhase.Nfp,
|
||||||
|
PlateNumber = 0,
|
||||||
|
Parts = parts,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = description,
|
||||||
|
IsOverallBest = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,9 +47,14 @@ namespace OpenNest.Engine.Strategies
|
|||||||
best = result;
|
best = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(context.Progress, NestPhase.Linear,
|
NestEngineBase.ReportProgress(context.Progress, new ProgressReport
|
||||||
context.PlateNumber, best, workArea,
|
{
|
||||||
$"Linear: {ai + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts");
|
Phase = NestPhase.Linear,
|
||||||
|
PlateNumber = context.PlateNumber,
|
||||||
|
Parts = best,
|
||||||
|
WorkArea = workArea,
|
||||||
|
Description = $"Linear: {ai + 1}/{angles.Count} angles, {angleDeg:F0}° best = {best?.Count ?? 0} parts",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return best ?? new List<Part>();
|
return best ?? new List<Part>();
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ namespace OpenNest.Engine.Strategies
|
|||||||
public List<Part> Fill(FillContext context)
|
public List<Part> Fill(FillContext context)
|
||||||
{
|
{
|
||||||
var comparer = context.Policy?.Comparer;
|
var comparer = context.Policy?.Comparer;
|
||||||
var filler = new PairFiller(context.Plate, comparer);
|
var dedup = GridDedup.GetOrCreate(context.SharedState);
|
||||||
|
var filler = new PairFiller(context.Plate, comparer, dedup);
|
||||||
var result = filler.Fill(context.Item, context.WorkArea,
|
var result = filler.Fill(context.Item, context.WorkArea,
|
||||||
context.PlateNumber, context.Token, context.Progress);
|
context.PlateNumber, context.Token, context.Progress);
|
||||||
|
|
||||||
|
|||||||
@@ -77,17 +77,23 @@ namespace OpenNest
|
|||||||
// Phase 1: Iterative shrink-fill for multi-quantity items.
|
// Phase 1: Iterative shrink-fill for multi-quantity items.
|
||||||
if (fillItems.Count > 0)
|
if (fillItems.Count > 0)
|
||||||
{
|
{
|
||||||
// Pass progress through so the UI shows intermediate results
|
// Use direction-specific engines: height shrink benefits from
|
||||||
// during the initial BestFitCache computation and fill phases.
|
// minimizing Y-extent, width shrink from minimizing X-extent.
|
||||||
Func<NestItem, Box, List<Part>> fillFunc = (ni, b) =>
|
Func<NestItem, Box, List<Part>> heightFillFunc = (ni, b) =>
|
||||||
{
|
{
|
||||||
var inner = new DefaultNestEngine(Plate);
|
var inner = new HorizontalRemnantEngine(Plate);
|
||||||
|
return inner.Fill(ni, b, progress, token);
|
||||||
|
};
|
||||||
|
|
||||||
|
Func<NestItem, Box, List<Part>> widthFillFunc = (ni, b) =>
|
||||||
|
{
|
||||||
|
var inner = new VerticalRemnantEngine(Plate);
|
||||||
return inner.Fill(ni, b, progress, token);
|
return inner.Fill(ni, b, progress, token);
|
||||||
};
|
};
|
||||||
|
|
||||||
var shrinkResult = IterativeShrinkFiller.Fill(
|
var shrinkResult = IterativeShrinkFiller.Fill(
|
||||||
fillItems, workArea, fillFunc, Plate.PartSpacing, token,
|
fillItems, workArea, heightFillFunc, Plate.PartSpacing, token,
|
||||||
progress, PlateNumber);
|
progress, PlateNumber, widthFillFunc);
|
||||||
|
|
||||||
allParts.AddRange(shrinkResult.Parts);
|
allParts.AddRange(shrinkResult.Parts);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class AccumulatingProgressTests
|
|||||||
var accumulating = new AccumulatingProgress(inner, previous);
|
var accumulating = new AccumulatingProgress(inner, previous);
|
||||||
|
|
||||||
var newParts = new List<Part> { TestHelpers.MakePartAt(20, 0, 10) };
|
var newParts = new List<Part> { TestHelpers.MakePartAt(20, 0, 10) };
|
||||||
accumulating.Report(new NestProgress { BestParts = newParts, BestPartCount = 1 });
|
accumulating.Report(new NestProgress { BestParts = newParts });
|
||||||
|
|
||||||
Assert.NotNull(inner.Last);
|
Assert.NotNull(inner.Last);
|
||||||
Assert.Equal(2, inner.Last.BestParts.Count);
|
Assert.Equal(2, inner.Last.BestParts.Count);
|
||||||
@@ -32,7 +32,7 @@ public class AccumulatingProgressTests
|
|||||||
var accumulating = new AccumulatingProgress(inner, new List<Part>());
|
var accumulating = new AccumulatingProgress(inner, new List<Part>());
|
||||||
|
|
||||||
var newParts = new List<Part> { TestHelpers.MakePartAt(0, 0, 10) };
|
var newParts = new List<Part> { TestHelpers.MakePartAt(0, 0, 10) };
|
||||||
accumulating.Report(new NestProgress { BestParts = newParts, BestPartCount = 1 });
|
accumulating.Report(new NestProgress { BestParts = newParts });
|
||||||
|
|
||||||
Assert.NotNull(inner.Last);
|
Assert.NotNull(inner.Last);
|
||||||
Assert.Single(inner.Last.BestParts);
|
Assert.Single(inner.Last.BestParts);
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
namespace OpenNest.Tests;
|
||||||
|
|
||||||
|
public class BestCombinationTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BothFit_FindsZeroRemnant()
|
||||||
|
{
|
||||||
|
// 100 = 0*30 + 5*20 (algorithm iterates from countLength1=0, finds zero remnant first)
|
||||||
|
var result = BestCombination.FindFrom2(30, 20, 100, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0.0, 100.0 - (c1 * 30.0 + c2 * 20.0), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnlyLength1Fits_ReturnsMaxCount1()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(10, 200, 50, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(5, c1);
|
||||||
|
Assert.Equal(0, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnlyLength2Fits_ReturnsMaxCount2()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(200, 10, 50, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0, c1);
|
||||||
|
Assert.Equal(5, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NeitherFits_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(100, 200, 50, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(0, c1);
|
||||||
|
Assert.Equal(0, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Length1FillsExactly_ZeroRemnant()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(25, 10, 100, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0.0, 100.0 - (c1 * 25.0 + c2 * 10.0), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MixMinimizesRemnant()
|
||||||
|
{
|
||||||
|
// 7 and 3 into 20: best is 2*7 + 2*3 = 20 (zero remnant)
|
||||||
|
var result = BestCombination.FindFrom2(7, 3, 20, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(2, c1);
|
||||||
|
Assert.Equal(2, c2);
|
||||||
|
Assert.True(c1 * 7 + c2 * 3 <= 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PrefersLessRemnant_OverMoreOfLength1()
|
||||||
|
{
|
||||||
|
// 6 and 5 into 17:
|
||||||
|
// all length1: 2*6=12, remnant=5 -> actually 2*6+1*5=17 perfect
|
||||||
|
var result = BestCombination.FindFrom2(6, 5, 17, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0.0, 17.0 - (c1 * 6.0 + c2 * 5.0), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EqualLengths_FillsWithLength1()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(10, 10, 50, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(5, c1 + c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SmallLengths_LargeOverall()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(3, 7, 100, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
var used = c1 * 3.0 + c2 * 7.0;
|
||||||
|
Assert.True(used <= 100);
|
||||||
|
Assert.True(100 - used < 3); // remnant less than smallest piece
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Length2IsBetter_SoleCandidate()
|
||||||
|
{
|
||||||
|
// length1=9, length2=5, overall=10:
|
||||||
|
// length1 alone: 1*9=9 remnant=1
|
||||||
|
// length2 alone: 2*5=10 remnant=0
|
||||||
|
var result = BestCombination.FindFrom2(9, 5, 10, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0, c1);
|
||||||
|
Assert.Equal(2, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FractionalLengths_WorkCorrectly()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(2.5, 3.5, 12, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
var used = c1 * 2.5 + c2 * 3.5;
|
||||||
|
Assert.True(used <= 12.0 + 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OverallExactlyOneOfEach()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(40, 60, 100, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(1, c1);
|
||||||
|
Assert.Equal(1, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OverallSmallerThanEither_ReturnsFalse()
|
||||||
|
{
|
||||||
|
var result = BestCombination.FindFrom2(10, 20, 5, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Equal(0, c1);
|
||||||
|
Assert.Equal(0, c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ZeroRemnant_StopsEarly()
|
||||||
|
{
|
||||||
|
// 4 and 6 into 24: 0*4+4*6=24 or 3*4+2*6=24 or 6*4+0*6=24
|
||||||
|
// Algorithm iterates from 0 length1 upward, finds zero remnant and breaks
|
||||||
|
var result = BestCombination.FindFrom2(4, 6, 24, out var c1, out var c2);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(0.0, 24.0 - (c1 * 4.0 + c2 * 6.0), 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace OpenNest.Tests;
|
||||||
|
|
||||||
|
public class NestPhaseExtensionsTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(NestPhase.Linear, "Trying rotations...")]
|
||||||
|
[InlineData(NestPhase.RectBestFit, "Trying best fit...")]
|
||||||
|
[InlineData(NestPhase.Pairs, "Trying pairs...")]
|
||||||
|
[InlineData(NestPhase.Nfp, "Trying NFP...")]
|
||||||
|
[InlineData(NestPhase.Extents, "Trying extents...")]
|
||||||
|
[InlineData(NestPhase.Custom, "Custom")]
|
||||||
|
public void DisplayName_ReturnsDescription(NestPhase phase, string expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, phase.DisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(NestPhase.Linear, "Linear")]
|
||||||
|
[InlineData(NestPhase.RectBestFit, "BestFit")]
|
||||||
|
[InlineData(NestPhase.Pairs, "Pairs")]
|
||||||
|
[InlineData(NestPhase.Nfp, "NFP")]
|
||||||
|
[InlineData(NestPhase.Extents, "Extents")]
|
||||||
|
[InlineData(NestPhase.Custom, "Custom")]
|
||||||
|
public void ShortName_ReturnsShortLabel(NestPhase phase, string expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, phase.ShortName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests;
|
||||||
|
|
||||||
|
public class NestProgressTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void BestPartCount_NullParts_ReturnsZero()
|
||||||
|
{
|
||||||
|
var progress = new NestProgress { BestParts = null };
|
||||||
|
Assert.Equal(0, progress.BestPartCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BestPartCount_ReturnsBestPartsCount()
|
||||||
|
{
|
||||||
|
var parts = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(10, 0, 5),
|
||||||
|
};
|
||||||
|
var progress = new NestProgress { BestParts = parts };
|
||||||
|
Assert.Equal(2, progress.BestPartCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BestDensity_NullParts_ReturnsZero()
|
||||||
|
{
|
||||||
|
var progress = new NestProgress { BestParts = null };
|
||||||
|
Assert.Equal(0, progress.BestDensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BestDensity_MatchesFillScoreFormula()
|
||||||
|
{
|
||||||
|
var parts = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(5, 0, 5),
|
||||||
|
};
|
||||||
|
var workArea = new Box(0, 0, 100, 100);
|
||||||
|
var progress = new NestProgress { BestParts = parts, ActiveWorkArea = workArea };
|
||||||
|
Assert.Equal(1.0, progress.BestDensity, precision: 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NestedWidth_ReturnsPartsSpan()
|
||||||
|
{
|
||||||
|
var parts = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(10, 0, 5),
|
||||||
|
};
|
||||||
|
var progress = new NestProgress { BestParts = parts };
|
||||||
|
Assert.Equal(15, progress.NestedWidth, precision: 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NestedLength_ReturnsPartsSpan()
|
||||||
|
{
|
||||||
|
var parts = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(0, 10, 5),
|
||||||
|
};
|
||||||
|
var progress = new NestProgress { BestParts = parts };
|
||||||
|
Assert.Equal(15, progress.NestedLength, precision: 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NestedArea_ReturnsSumOfPartAreas()
|
||||||
|
{
|
||||||
|
var parts = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(10, 0, 5),
|
||||||
|
};
|
||||||
|
var progress = new NestProgress { BestParts = parts };
|
||||||
|
Assert.Equal(50, progress.NestedArea, precision: 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SettingBestParts_InvalidatesCache()
|
||||||
|
{
|
||||||
|
var parts1 = new List<Part> { TestHelpers.MakePartAt(0, 0, 5) };
|
||||||
|
var parts2 = new List<Part>
|
||||||
|
{
|
||||||
|
TestHelpers.MakePartAt(0, 0, 5),
|
||||||
|
TestHelpers.MakePartAt(10, 0, 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
var progress = new NestProgress { BestParts = parts1 };
|
||||||
|
Assert.Equal(1, progress.BestPartCount);
|
||||||
|
Assert.Equal(25, progress.NestedArea, precision: 4);
|
||||||
|
|
||||||
|
progress.BestParts = parts2;
|
||||||
|
Assert.Equal(2, progress.BestPartCount);
|
||||||
|
Assert.Equal(50, progress.NestedArea, precision: 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,16 +59,6 @@ namespace OpenNest.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDisplayName(NestPhase phase)
|
|
||||||
{
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case NestPhase.RectBestFit: return "BestFit";
|
|
||||||
case NestPhase.Nfp: return "NFP";
|
|
||||||
default: return phase.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e)
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPaint(e);
|
base.OnPaint(e);
|
||||||
@@ -134,7 +124,7 @@ namespace OpenNest.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
var label = GetDisplayName(phase);
|
var label = phase.ShortName();
|
||||||
var font = isVisited || isActive ? BoldLabelFont : LabelFont;
|
var font = isVisited || isActive ? BoldLabelFont : LabelFont;
|
||||||
var brush = isVisited || isActive ? activeTextBrush : pendingTextBrush;
|
var brush = isVisited || isActive ? activeTextBrush : pendingTextBrush;
|
||||||
var labelSize = g.MeasureString(label, font);
|
var labelSize = g.MeasureString(label, font);
|
||||||
|
|||||||
@@ -1098,23 +1098,16 @@ namespace OpenNest.Controls
|
|||||||
var bounds = parts.GetBoundingBox();
|
var bounds = parts.GetBoundingBox();
|
||||||
var center = bounds.Center;
|
var center = bounds.Center;
|
||||||
var anchor = bounds.Location;
|
var anchor = bounds.Location;
|
||||||
var rotatedPrograms = new HashSet<Program>();
|
|
||||||
|
|
||||||
for (int i = 0; i < SelectedParts.Count; ++i)
|
for (var i = 0; i < SelectedParts.Count; ++i)
|
||||||
{
|
{
|
||||||
var part = SelectedParts[i];
|
var part = SelectedParts[i];
|
||||||
var basePart = part.BasePart;
|
part.BasePart.Rotate(angle, center);
|
||||||
|
|
||||||
if (rotatedPrograms.Add(basePart.Program))
|
|
||||||
basePart.Program.Rotate(angle);
|
|
||||||
|
|
||||||
part.Location = part.Location.Rotate(angle, center);
|
|
||||||
basePart.UpdateBounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var diff = anchor - parts.GetBoundingBox().Location;
|
var diff = anchor - parts.GetBoundingBox().Location;
|
||||||
|
|
||||||
for (int i = 0; i < SelectedParts.Count; ++i)
|
for (var i = 0; i < SelectedParts.Count; ++i)
|
||||||
SelectedParts[i].Offset(diff);
|
SelectedParts[i].Offset(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-32
@@ -85,13 +85,13 @@ namespace OpenNest.Forms
|
|||||||
resultsTable.Controls.Add(nestedAreaLabel, 0, 2);
|
resultsTable.Controls.Add(nestedAreaLabel, 0, 2);
|
||||||
resultsTable.Controls.Add(nestedAreaValue, 1, 2);
|
resultsTable.Controls.Add(nestedAreaValue, 1, 2);
|
||||||
resultsTable.Dock = System.Windows.Forms.DockStyle.Top;
|
resultsTable.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
resultsTable.Location = new System.Drawing.Point(14, 29);
|
resultsTable.Location = new System.Drawing.Point(14, 33);
|
||||||
resultsTable.Name = "resultsTable";
|
resultsTable.Name = "resultsTable";
|
||||||
resultsTable.RowCount = 3;
|
resultsTable.RowCount = 3;
|
||||||
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
resultsTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
resultsTable.Size = new System.Drawing.Size(422, 57);
|
resultsTable.Size = new System.Drawing.Size(422, 69);
|
||||||
resultsTable.TabIndex = 1;
|
resultsTable.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// partsLabel
|
// partsLabel
|
||||||
@@ -102,7 +102,7 @@ namespace OpenNest.Forms
|
|||||||
partsLabel.Location = new System.Drawing.Point(0, 3);
|
partsLabel.Location = new System.Drawing.Point(0, 3);
|
||||||
partsLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
partsLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
partsLabel.Name = "partsLabel";
|
partsLabel.Name = "partsLabel";
|
||||||
partsLabel.Size = new System.Drawing.Size(36, 13);
|
partsLabel.Size = new System.Drawing.Size(43, 17);
|
||||||
partsLabel.TabIndex = 0;
|
partsLabel.TabIndex = 0;
|
||||||
partsLabel.Text = "Parts:";
|
partsLabel.Text = "Parts:";
|
||||||
//
|
//
|
||||||
@@ -110,10 +110,10 @@ namespace OpenNest.Forms
|
|||||||
//
|
//
|
||||||
partsValue.AutoSize = true;
|
partsValue.AutoSize = true;
|
||||||
partsValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
partsValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
||||||
partsValue.Location = new System.Drawing.Point(80, 3);
|
partsValue.Location = new System.Drawing.Point(90, 3);
|
||||||
partsValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
partsValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||||
partsValue.Name = "partsValue";
|
partsValue.Name = "partsValue";
|
||||||
partsValue.Size = new System.Drawing.Size(13, 13);
|
partsValue.Size = new System.Drawing.Size(13, 15);
|
||||||
partsValue.TabIndex = 1;
|
partsValue.TabIndex = 1;
|
||||||
partsValue.Text = "�";
|
partsValue.Text = "�";
|
||||||
//
|
//
|
||||||
@@ -122,10 +122,10 @@ namespace OpenNest.Forms
|
|||||||
densityLabel.AutoSize = true;
|
densityLabel.AutoSize = true;
|
||||||
densityLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
densityLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
||||||
densityLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
densityLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
||||||
densityLabel.Location = new System.Drawing.Point(0, 22);
|
densityLabel.Location = new System.Drawing.Point(0, 26);
|
||||||
densityLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
densityLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
densityLabel.Name = "densityLabel";
|
densityLabel.Name = "densityLabel";
|
||||||
densityLabel.Size = new System.Drawing.Size(49, 13);
|
densityLabel.Size = new System.Drawing.Size(59, 17);
|
||||||
densityLabel.TabIndex = 2;
|
densityLabel.TabIndex = 2;
|
||||||
densityLabel.Text = "Density:";
|
densityLabel.Text = "Density:";
|
||||||
//
|
//
|
||||||
@@ -134,10 +134,10 @@ namespace OpenNest.Forms
|
|||||||
densityPanel.AutoSize = true;
|
densityPanel.AutoSize = true;
|
||||||
densityPanel.Controls.Add(densityValue);
|
densityPanel.Controls.Add(densityValue);
|
||||||
densityPanel.Controls.Add(densityBar);
|
densityPanel.Controls.Add(densityBar);
|
||||||
densityPanel.Location = new System.Drawing.Point(80, 19);
|
densityPanel.Location = new System.Drawing.Point(90, 23);
|
||||||
densityPanel.Margin = new System.Windows.Forms.Padding(0);
|
densityPanel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
densityPanel.Name = "densityPanel";
|
densityPanel.Name = "densityPanel";
|
||||||
densityPanel.Size = new System.Drawing.Size(311, 19);
|
densityPanel.Size = new System.Drawing.Size(262, 21);
|
||||||
densityPanel.TabIndex = 3;
|
densityPanel.TabIndex = 3;
|
||||||
densityPanel.WrapContents = false;
|
densityPanel.WrapContents = false;
|
||||||
//
|
//
|
||||||
@@ -148,7 +148,7 @@ namespace OpenNest.Forms
|
|||||||
densityValue.Location = new System.Drawing.Point(0, 3);
|
densityValue.Location = new System.Drawing.Point(0, 3);
|
||||||
densityValue.Margin = new System.Windows.Forms.Padding(0, 3, 8, 3);
|
densityValue.Margin = new System.Windows.Forms.Padding(0, 3, 8, 3);
|
||||||
densityValue.Name = "densityValue";
|
densityValue.Name = "densityValue";
|
||||||
densityValue.Size = new System.Drawing.Size(13, 13);
|
densityValue.Size = new System.Drawing.Size(13, 15);
|
||||||
densityValue.TabIndex = 0;
|
densityValue.TabIndex = 0;
|
||||||
densityValue.Text = "�";
|
densityValue.Text = "�";
|
||||||
//
|
//
|
||||||
@@ -157,7 +157,7 @@ namespace OpenNest.Forms
|
|||||||
densityBar.Location = new System.Drawing.Point(21, 5);
|
densityBar.Location = new System.Drawing.Point(21, 5);
|
||||||
densityBar.Margin = new System.Windows.Forms.Padding(0, 5, 0, 0);
|
densityBar.Margin = new System.Windows.Forms.Padding(0, 5, 0, 0);
|
||||||
densityBar.Name = "densityBar";
|
densityBar.Name = "densityBar";
|
||||||
densityBar.Size = new System.Drawing.Size(290, 8);
|
densityBar.Size = new System.Drawing.Size(241, 8);
|
||||||
densityBar.TabIndex = 1;
|
densityBar.TabIndex = 1;
|
||||||
densityBar.Value = 0D;
|
densityBar.Value = 0D;
|
||||||
//
|
//
|
||||||
@@ -166,10 +166,10 @@ namespace OpenNest.Forms
|
|||||||
nestedAreaLabel.AutoSize = true;
|
nestedAreaLabel.AutoSize = true;
|
||||||
nestedAreaLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
nestedAreaLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
||||||
nestedAreaLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
nestedAreaLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
||||||
nestedAreaLabel.Location = new System.Drawing.Point(0, 41);
|
nestedAreaLabel.Location = new System.Drawing.Point(0, 49);
|
||||||
nestedAreaLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
nestedAreaLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
nestedAreaLabel.Name = "nestedAreaLabel";
|
nestedAreaLabel.Name = "nestedAreaLabel";
|
||||||
nestedAreaLabel.Size = new System.Drawing.Size(47, 13);
|
nestedAreaLabel.Size = new System.Drawing.Size(55, 17);
|
||||||
nestedAreaLabel.TabIndex = 4;
|
nestedAreaLabel.TabIndex = 4;
|
||||||
nestedAreaLabel.Text = "Nested:";
|
nestedAreaLabel.Text = "Nested:";
|
||||||
//
|
//
|
||||||
@@ -177,10 +177,10 @@ namespace OpenNest.Forms
|
|||||||
//
|
//
|
||||||
nestedAreaValue.AutoSize = true;
|
nestedAreaValue.AutoSize = true;
|
||||||
nestedAreaValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
nestedAreaValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
||||||
nestedAreaValue.Location = new System.Drawing.Point(80, 41);
|
nestedAreaValue.Location = new System.Drawing.Point(90, 49);
|
||||||
nestedAreaValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
nestedAreaValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||||
nestedAreaValue.Name = "nestedAreaValue";
|
nestedAreaValue.Name = "nestedAreaValue";
|
||||||
nestedAreaValue.Size = new System.Drawing.Size(13, 13);
|
nestedAreaValue.Size = new System.Drawing.Size(13, 15);
|
||||||
nestedAreaValue.TabIndex = 5;
|
nestedAreaValue.TabIndex = 5;
|
||||||
nestedAreaValue.Text = "�";
|
nestedAreaValue.Text = "�";
|
||||||
//
|
//
|
||||||
@@ -193,7 +193,7 @@ namespace OpenNest.Forms
|
|||||||
resultsHeader.Location = new System.Drawing.Point(14, 10);
|
resultsHeader.Location = new System.Drawing.Point(14, 10);
|
||||||
resultsHeader.Name = "resultsHeader";
|
resultsHeader.Name = "resultsHeader";
|
||||||
resultsHeader.Padding = new System.Windows.Forms.Padding(0, 0, 0, 4);
|
resultsHeader.Padding = new System.Windows.Forms.Padding(0, 0, 0, 4);
|
||||||
resultsHeader.Size = new System.Drawing.Size(56, 19);
|
resultsHeader.Size = new System.Drawing.Size(65, 23);
|
||||||
resultsHeader.TabIndex = 0;
|
resultsHeader.TabIndex = 0;
|
||||||
resultsHeader.Text = "RESULTS";
|
resultsHeader.Text = "RESULTS";
|
||||||
//
|
//
|
||||||
@@ -203,7 +203,7 @@ namespace OpenNest.Forms
|
|||||||
statusPanel.Controls.Add(statusTable);
|
statusPanel.Controls.Add(statusTable);
|
||||||
statusPanel.Controls.Add(statusHeader);
|
statusPanel.Controls.Add(statusHeader);
|
||||||
statusPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
statusPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
statusPanel.Location = new System.Drawing.Point(0, 165);
|
statusPanel.Location = new System.Drawing.Point(0, 180);
|
||||||
statusPanel.Name = "statusPanel";
|
statusPanel.Name = "statusPanel";
|
||||||
statusPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
|
statusPanel.Padding = new System.Windows.Forms.Padding(14, 10, 14, 10);
|
||||||
statusPanel.Size = new System.Drawing.Size(450, 115);
|
statusPanel.Size = new System.Drawing.Size(450, 115);
|
||||||
@@ -222,13 +222,13 @@ namespace OpenNest.Forms
|
|||||||
statusTable.Controls.Add(descriptionLabel, 0, 2);
|
statusTable.Controls.Add(descriptionLabel, 0, 2);
|
||||||
statusTable.Controls.Add(descriptionValue, 1, 2);
|
statusTable.Controls.Add(descriptionValue, 1, 2);
|
||||||
statusTable.Dock = System.Windows.Forms.DockStyle.Top;
|
statusTable.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
statusTable.Location = new System.Drawing.Point(14, 29);
|
statusTable.Location = new System.Drawing.Point(14, 33);
|
||||||
statusTable.Name = "statusTable";
|
statusTable.Name = "statusTable";
|
||||||
statusTable.RowCount = 3;
|
statusTable.RowCount = 3;
|
||||||
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
statusTable.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
statusTable.Size = new System.Drawing.Size(422, 57);
|
statusTable.Size = new System.Drawing.Size(422, 69);
|
||||||
statusTable.TabIndex = 1;
|
statusTable.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// plateLabel
|
// plateLabel
|
||||||
@@ -239,7 +239,7 @@ namespace OpenNest.Forms
|
|||||||
plateLabel.Location = new System.Drawing.Point(0, 3);
|
plateLabel.Location = new System.Drawing.Point(0, 3);
|
||||||
plateLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
plateLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
plateLabel.Name = "plateLabel";
|
plateLabel.Name = "plateLabel";
|
||||||
plateLabel.Size = new System.Drawing.Size(36, 13);
|
plateLabel.Size = new System.Drawing.Size(43, 17);
|
||||||
plateLabel.TabIndex = 0;
|
plateLabel.TabIndex = 0;
|
||||||
plateLabel.Text = "Plate:";
|
plateLabel.Text = "Plate:";
|
||||||
//
|
//
|
||||||
@@ -247,10 +247,10 @@ namespace OpenNest.Forms
|
|||||||
//
|
//
|
||||||
plateValue.AutoSize = true;
|
plateValue.AutoSize = true;
|
||||||
plateValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
plateValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
||||||
plateValue.Location = new System.Drawing.Point(80, 3);
|
plateValue.Location = new System.Drawing.Point(90, 3);
|
||||||
plateValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
plateValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||||
plateValue.Name = "plateValue";
|
plateValue.Name = "plateValue";
|
||||||
plateValue.Size = new System.Drawing.Size(13, 13);
|
plateValue.Size = new System.Drawing.Size(13, 15);
|
||||||
plateValue.TabIndex = 1;
|
plateValue.TabIndex = 1;
|
||||||
plateValue.Text = "�";
|
plateValue.Text = "�";
|
||||||
//
|
//
|
||||||
@@ -259,10 +259,10 @@ namespace OpenNest.Forms
|
|||||||
elapsedLabel.AutoSize = true;
|
elapsedLabel.AutoSize = true;
|
||||||
elapsedLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
elapsedLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
||||||
elapsedLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
elapsedLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
||||||
elapsedLabel.Location = new System.Drawing.Point(0, 22);
|
elapsedLabel.Location = new System.Drawing.Point(0, 26);
|
||||||
elapsedLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
elapsedLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
elapsedLabel.Name = "elapsedLabel";
|
elapsedLabel.Name = "elapsedLabel";
|
||||||
elapsedLabel.Size = new System.Drawing.Size(50, 13);
|
elapsedLabel.Size = new System.Drawing.Size(59, 17);
|
||||||
elapsedLabel.TabIndex = 2;
|
elapsedLabel.TabIndex = 2;
|
||||||
elapsedLabel.Text = "Elapsed:";
|
elapsedLabel.Text = "Elapsed:";
|
||||||
//
|
//
|
||||||
@@ -270,10 +270,10 @@ namespace OpenNest.Forms
|
|||||||
//
|
//
|
||||||
elapsedValue.AutoSize = true;
|
elapsedValue.AutoSize = true;
|
||||||
elapsedValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
elapsedValue.Font = new System.Drawing.Font("Consolas", 9.75F);
|
||||||
elapsedValue.Location = new System.Drawing.Point(80, 22);
|
elapsedValue.Location = new System.Drawing.Point(90, 26);
|
||||||
elapsedValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
elapsedValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||||
elapsedValue.Name = "elapsedValue";
|
elapsedValue.Name = "elapsedValue";
|
||||||
elapsedValue.Size = new System.Drawing.Size(31, 13);
|
elapsedValue.Size = new System.Drawing.Size(35, 15);
|
||||||
elapsedValue.TabIndex = 3;
|
elapsedValue.TabIndex = 3;
|
||||||
elapsedValue.Text = "0:00";
|
elapsedValue.Text = "0:00";
|
||||||
//
|
//
|
||||||
@@ -282,10 +282,10 @@ namespace OpenNest.Forms
|
|||||||
descriptionLabel.AutoSize = true;
|
descriptionLabel.AutoSize = true;
|
||||||
descriptionLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
descriptionLabel.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold);
|
||||||
descriptionLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
descriptionLabel.ForeColor = System.Drawing.Color.FromArgb(51, 51, 51);
|
||||||
descriptionLabel.Location = new System.Drawing.Point(0, 41);
|
descriptionLabel.Location = new System.Drawing.Point(0, 49);
|
||||||
descriptionLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
descriptionLabel.Margin = new System.Windows.Forms.Padding(0, 3, 5, 3);
|
||||||
descriptionLabel.Name = "descriptionLabel";
|
descriptionLabel.Name = "descriptionLabel";
|
||||||
descriptionLabel.Size = new System.Drawing.Size(40, 13);
|
descriptionLabel.Size = new System.Drawing.Size(49, 17);
|
||||||
descriptionLabel.TabIndex = 4;
|
descriptionLabel.TabIndex = 4;
|
||||||
descriptionLabel.Text = "Detail:";
|
descriptionLabel.Text = "Detail:";
|
||||||
//
|
//
|
||||||
@@ -293,10 +293,10 @@ namespace OpenNest.Forms
|
|||||||
//
|
//
|
||||||
descriptionValue.AutoSize = true;
|
descriptionValue.AutoSize = true;
|
||||||
descriptionValue.Font = new System.Drawing.Font("Segoe UI", 9.75F);
|
descriptionValue.Font = new System.Drawing.Font("Segoe UI", 9.75F);
|
||||||
descriptionValue.Location = new System.Drawing.Point(80, 41);
|
descriptionValue.Location = new System.Drawing.Point(90, 49);
|
||||||
descriptionValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
descriptionValue.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||||
descriptionValue.Name = "descriptionValue";
|
descriptionValue.Name = "descriptionValue";
|
||||||
descriptionValue.Size = new System.Drawing.Size(18, 13);
|
descriptionValue.Size = new System.Drawing.Size(20, 17);
|
||||||
descriptionValue.TabIndex = 5;
|
descriptionValue.TabIndex = 5;
|
||||||
descriptionValue.Text = "�";
|
descriptionValue.Text = "�";
|
||||||
//
|
//
|
||||||
@@ -309,7 +309,7 @@ namespace OpenNest.Forms
|
|||||||
statusHeader.Location = new System.Drawing.Point(14, 10);
|
statusHeader.Location = new System.Drawing.Point(14, 10);
|
||||||
statusHeader.Name = "statusHeader";
|
statusHeader.Name = "statusHeader";
|
||||||
statusHeader.Padding = new System.Windows.Forms.Padding(0, 0, 0, 4);
|
statusHeader.Padding = new System.Windows.Forms.Padding(0, 0, 0, 4);
|
||||||
statusHeader.Size = new System.Drawing.Size(50, 19);
|
statusHeader.Size = new System.Drawing.Size(59, 23);
|
||||||
statusHeader.TabIndex = 0;
|
statusHeader.TabIndex = 0;
|
||||||
statusHeader.Text = "STATUS";
|
statusHeader.Text = "STATUS";
|
||||||
//
|
//
|
||||||
@@ -320,7 +320,7 @@ namespace OpenNest.Forms
|
|||||||
buttonPanel.Controls.Add(acceptButton);
|
buttonPanel.Controls.Add(acceptButton);
|
||||||
buttonPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
buttonPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
buttonPanel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
buttonPanel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||||
buttonPanel.Location = new System.Drawing.Point(0, 265);
|
buttonPanel.Location = new System.Drawing.Point(0, 295);
|
||||||
buttonPanel.Name = "buttonPanel";
|
buttonPanel.Name = "buttonPanel";
|
||||||
buttonPanel.Padding = new System.Windows.Forms.Padding(9, 6, 9, 6);
|
buttonPanel.Padding = new System.Windows.Forms.Padding(9, 6, 9, 6);
|
||||||
buttonPanel.Size = new System.Drawing.Size(450, 45);
|
buttonPanel.Size = new System.Drawing.Size(450, 45);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace OpenNest.Forms
|
|||||||
|
|
||||||
descriptionValue.Text = !string.IsNullOrEmpty(progress.Description)
|
descriptionValue.Text = !string.IsNullOrEmpty(progress.Description)
|
||||||
? progress.Description
|
? progress.Description
|
||||||
: FormatPhase(progress.Phase);
|
: progress.Phase.DisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowCompleted()
|
public void ShowCompleted()
|
||||||
@@ -196,18 +196,5 @@ namespace OpenNest.Forms
|
|||||||
return DensityMidColor;
|
return DensityMidColor;
|
||||||
return DensityHighColor;
|
return DensityHighColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatPhase(NestPhase phase)
|
|
||||||
{
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case NestPhase.Linear: return "Trying rotations...";
|
|
||||||
case NestPhase.RectBestFit: return "Trying best fit...";
|
|
||||||
case NestPhase.Pairs: return "Trying pairs...";
|
|
||||||
case NestPhase.Extents: return "Trying extents...";
|
|
||||||
case NestPhase.Nfp: return "Trying NFP...";
|
|
||||||
default: return phase.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user