style: apply CSharpier formatting to all C# sources
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests pass after reformat, full solution builds 0 errors. Added .csharpierignore so csproj/config XML keeps its existing layout (CSharpier's XML wrapping churns attributes with zero benefit). Formatting is now enforceable: dotnet csharpier check . passes.
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
@@ -15,9 +15,12 @@ namespace OpenNest.Engine.Nfp
|
||||
/// </summary>
|
||||
public static class AutoNester
|
||||
{
|
||||
public static List<Part> Nest(List<NestItem> items, Plate plate,
|
||||
public static List<Part> Nest(
|
||||
List<NestItem> items,
|
||||
Plate plate,
|
||||
IProgress<NestProgress> progress = null,
|
||||
CancellationToken cancellation = default)
|
||||
CancellationToken cancellation = default
|
||||
)
|
||||
{
|
||||
var workArea = plate.WorkArea();
|
||||
var halfSpacing = plate.PartSpacing / 2.0;
|
||||
@@ -36,7 +39,9 @@ namespace OpenNest.Engine.Nfp
|
||||
|
||||
if (perimeterPolygon == null)
|
||||
{
|
||||
Debug.WriteLine($"[AutoNest] Skipping drawing '{drawing.Name}': no valid perimeter");
|
||||
Debug.WriteLine(
|
||||
$"[AutoNest] Skipping drawing '{drawing.Name}': no valid perimeter"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -58,11 +63,20 @@ namespace OpenNest.Engine.Nfp
|
||||
// Pre-compute all NFPs.
|
||||
nfpCache.PreComputeAll();
|
||||
|
||||
Debug.WriteLine($"[AutoNest] NFP cache: {nfpCache.Count} entries for {candidateRotations.Count} drawings");
|
||||
Debug.WriteLine(
|
||||
$"[AutoNest] NFP cache: {nfpCache.Count} entries for {candidateRotations.Count} drawings"
|
||||
);
|
||||
|
||||
// Run simulated annealing optimizer.
|
||||
var optimizer = new SimulatedAnnealing();
|
||||
var result = optimizer.Optimize(items, workArea, nfpCache, candidateRotations, progress, cancellation);
|
||||
var result = optimizer.Optimize(
|
||||
items,
|
||||
workArea,
|
||||
nfpCache,
|
||||
candidateRotations,
|
||||
progress,
|
||||
cancellation
|
||||
);
|
||||
|
||||
if (result.Sequence == null || result.Sequence.Count == 0)
|
||||
return new List<Part>();
|
||||
@@ -72,17 +86,22 @@ namespace OpenNest.Engine.Nfp
|
||||
var placedParts = blf.Fill(result.Sequence);
|
||||
var parts = BottomLeftFill.ToNestParts(placedParts);
|
||||
|
||||
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, new ProgressReport
|
||||
{
|
||||
Phase = NestPhase.Nfp,
|
||||
PlateNumber = 0,
|
||||
Parts = parts,
|
||||
WorkArea = workArea,
|
||||
Description = $"NFP: {parts.Count} parts, {result.Iterations} iterations",
|
||||
IsOverallBest = true,
|
||||
});
|
||||
NestEngineBase.ReportProgress(
|
||||
progress,
|
||||
new ProgressReport
|
||||
{
|
||||
Phase = NestPhase.Nfp,
|
||||
PlateNumber = 0,
|
||||
Parts = parts,
|
||||
WorkArea = workArea,
|
||||
Description = $"NFP: {parts.Count} parts, {result.Iterations} iterations",
|
||||
IsOverallBest = true,
|
||||
}
|
||||
);
|
||||
|
||||
return parts;
|
||||
}
|
||||
@@ -147,7 +166,9 @@ namespace OpenNest.Engine.Nfp
|
||||
// Only use the NFP result if it kept all parts and improved density.
|
||||
if (optimized.Count < parts.Count)
|
||||
{
|
||||
Debug.WriteLine($"[AutoNest.Optimize] Rejected: placed {optimized.Count}/{parts.Count} parts");
|
||||
Debug.WriteLine(
|
||||
$"[AutoNest.Optimize] Rejected: placed {optimized.Count}/{parts.Count} parts"
|
||||
);
|
||||
return parts;
|
||||
}
|
||||
|
||||
@@ -163,24 +184,32 @@ namespace OpenNest.Engine.Nfp
|
||||
|
||||
if (optimizedScore > originalScore)
|
||||
{
|
||||
Debug.WriteLine($"[AutoNest.Optimize] Improved: density {originalScore.Density:P1} -> {optimizedScore.Density:P1}");
|
||||
Debug.WriteLine(
|
||||
$"[AutoNest.Optimize] Improved: density {originalScore.Density:P1} -> {optimizedScore.Density:P1}"
|
||||
);
|
||||
return optimized;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[AutoNest.Optimize] No improvement: {originalScore.Density:P1} >= {optimizedScore.Density:P1}");
|
||||
Debug.WriteLine(
|
||||
$"[AutoNest.Optimize] No improvement: {originalScore.Density:P1} >= {optimizedScore.Density:P1}"
|
||||
);
|
||||
return parts;
|
||||
}
|
||||
|
||||
private static bool AllPartsInBounds(List<Part> parts, Box workArea)
|
||||
{
|
||||
var logPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "nest-debug.log");
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
"nest-debug.log"
|
||||
);
|
||||
|
||||
var allInBounds = true;
|
||||
|
||||
// Append to the log that BLF already started
|
||||
using var log = new StreamWriter(logPath, true);
|
||||
log.WriteLine($"\n[Bounds] workArea: X={workArea.X} Y={workArea.Y} W={workArea.Width} H={workArea.Length} Right={workArea.Right} Top={workArea.Top}");
|
||||
log.WriteLine(
|
||||
$"\n[Bounds] workArea: X={workArea.X} Y={workArea.Y} W={workArea.Width} H={workArea.Length} Right={workArea.Right} Top={workArea.Top}"
|
||||
);
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
@@ -193,7 +222,9 @@ namespace OpenNest.Engine.Nfp
|
||||
|
||||
if (oob)
|
||||
{
|
||||
log.WriteLine($"[Bounds] OOB DrawingId={part.BaseDrawing.Id} \"{part.BaseDrawing.Name}\" loc=({part.Location.X:F4},{part.Location.Y:F4}) rot={part.Rotation:F3} bb=({bb.Left:F4},{bb.Bottom:F4})-({bb.Right:F4},{bb.Top:F4}) violations: {(outLeft ? "LEFT " : "")}{(outBottom ? "BOTTOM " : "")}{(outRight ? "RIGHT " : "")}{(outTop ? "TOP " : "")}");
|
||||
log.WriteLine(
|
||||
$"[Bounds] OOB DrawingId={part.BaseDrawing.Id} \"{part.BaseDrawing.Name}\" loc=({part.Location.X:F4},{part.Location.Y:F4}) rot={part.Rotation:F3} bb=({bb.Left:F4},{bb.Bottom:F4})-({bb.Right:F4},{bb.Top:F4}) violations: {(outLeft ? "LEFT " : "")}{(outBottom ? "BOTTOM " : "")}{(outRight ? "RIGHT " : "")}{(outTop ? "TOP " : "")}"
|
||||
);
|
||||
allInBounds = false;
|
||||
}
|
||||
}
|
||||
@@ -215,8 +246,11 @@ namespace OpenNest.Engine.Nfp
|
||||
/// <summary>
|
||||
/// Computes candidate rotation angles for a drawing.
|
||||
/// </summary>
|
||||
private static List<double> ComputeCandidateRotations(NestItem item,
|
||||
Polygon perimeterPolygon, Box workArea)
|
||||
private static List<double> ComputeCandidateRotations(
|
||||
NestItem item,
|
||||
Polygon perimeterPolygon,
|
||||
Box workArea
|
||||
)
|
||||
{
|
||||
var rotations = new List<double> { 0 };
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using OpenNest.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Clipper2Lib;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
@@ -14,7 +14,9 @@ namespace OpenNest.Engine.Nfp
|
||||
public class BottomLeftFill
|
||||
{
|
||||
private static readonly string DebugLogPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "nest-debug.log");
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
"nest-debug.log"
|
||||
);
|
||||
|
||||
private readonly Box workArea;
|
||||
private readonly NfpCache nfpCache;
|
||||
@@ -34,7 +36,9 @@ namespace OpenNest.Engine.Nfp
|
||||
var placedParts = new List<PlacedPart>();
|
||||
|
||||
using var log = new StreamWriter(DebugLogPath, false);
|
||||
log.WriteLine($"[BLF] {DateTime.Now:HH:mm:ss.fff} workArea: X={workArea.X} Y={workArea.Y} W={workArea.Width} H={workArea.Length} Right={workArea.Right} Top={workArea.Top}");
|
||||
log.WriteLine(
|
||||
$"[BLF] {DateTime.Now:HH:mm:ss.fff} workArea: X={workArea.X} Y={workArea.Y} W={workArea.Width} H={workArea.Length} Right={workArea.Right} Top={workArea.Top}"
|
||||
);
|
||||
log.WriteLine($"[BLF] Sequence count: {sequence.Count}");
|
||||
|
||||
foreach (var entry in sequence)
|
||||
@@ -43,13 +47,22 @@ namespace OpenNest.Engine.Nfp
|
||||
|
||||
if (ifp.Vertices.Count < 3)
|
||||
{
|
||||
log.WriteLine($"[BLF] DrawingId={entry.DrawingId} rot={entry.Rotation:F3} SKIPPED (IFP has {ifp.Vertices.Count} verts)");
|
||||
log.WriteLine(
|
||||
$"[BLF] DrawingId={entry.DrawingId} rot={entry.Rotation:F3} SKIPPED (IFP has {ifp.Vertices.Count} verts)"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
log.WriteLine($"[BLF] DrawingId={entry.DrawingId} rot={entry.Rotation:F3} IFP verts={ifp.Vertices.Count} bounds=({ifp.BoundingBox.X:F2},{ifp.BoundingBox.Y:F2},{ifp.BoundingBox.Width:F2},{ifp.BoundingBox.Length:F2})");
|
||||
log.WriteLine(
|
||||
$"[BLF] DrawingId={entry.DrawingId} rot={entry.Rotation:F3} IFP verts={ifp.Vertices.Count} bounds=({ifp.BoundingBox.X:F2},{ifp.BoundingBox.Y:F2},{ifp.BoundingBox.Width:F2},{ifp.BoundingBox.Length:F2})"
|
||||
);
|
||||
|
||||
var nfpPaths = ComputeNfpPaths(placedParts, entry.DrawingId, entry.Rotation, ifp.BoundingBox);
|
||||
var nfpPaths = ComputeNfpPaths(
|
||||
placedParts,
|
||||
entry.DrawingId,
|
||||
entry.Rotation,
|
||||
ifp.BoundingBox
|
||||
);
|
||||
var feasible = InnerFitPolygon.ComputeFeasibleRegion(ifp, nfpPaths);
|
||||
var point = InnerFitPolygon.FindBottomLeftPoint(feasible);
|
||||
|
||||
@@ -63,17 +76,22 @@ namespace OpenNest.Engine.Nfp
|
||||
var ifpBb = ifp.BoundingBox;
|
||||
point = new Vector(
|
||||
System.Math.Max(ifpBb.X, System.Math.Min(ifpBb.Right, point.X)),
|
||||
System.Math.Max(ifpBb.Y, System.Math.Min(ifpBb.Top, point.Y)));
|
||||
System.Math.Max(ifpBb.Y, System.Math.Min(ifpBb.Top, point.Y))
|
||||
);
|
||||
|
||||
log.WriteLine($"[BLF] -> placed at ({point.X:F4}, {point.Y:F4}) nfpPaths={nfpPaths.Count} feasibleVerts={feasible.Vertices.Count}");
|
||||
log.WriteLine(
|
||||
$"[BLF] -> placed at ({point.X:F4}, {point.Y:F4}) nfpPaths={nfpPaths.Count} feasibleVerts={feasible.Vertices.Count}"
|
||||
);
|
||||
|
||||
placedParts.Add(new PlacedPart
|
||||
{
|
||||
DrawingId = entry.DrawingId,
|
||||
Rotation = entry.Rotation,
|
||||
Position = point,
|
||||
Drawing = entry.Drawing
|
||||
});
|
||||
placedParts.Add(
|
||||
new PlacedPart
|
||||
{
|
||||
DrawingId = entry.DrawingId,
|
||||
Rotation = entry.Rotation,
|
||||
Position = point,
|
||||
Drawing = entry.Drawing,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
log.WriteLine($"[BLF] Total placed: {placedParts.Count}/{sequence.Count}");
|
||||
@@ -106,7 +124,12 @@ namespace OpenNest.Engine.Nfp
|
||||
/// returned as Clipper paths with translations applied.
|
||||
/// Filters NFPs that don't intersect the target IFP.
|
||||
/// </summary>
|
||||
private PathsD ComputeNfpPaths(List<PlacedPart> placedParts, int drawingId, double rotation, Box ifpBounds)
|
||||
private PathsD ComputeNfpPaths(
|
||||
List<PlacedPart> placedParts,
|
||||
int drawingId,
|
||||
double rotation,
|
||||
Box ifpBounds
|
||||
)
|
||||
{
|
||||
var nfpPaths = new PathsD(placedParts.Count);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
@@ -33,9 +33,13 @@ namespace OpenNest.Engine.Nfp
|
||||
/// </summary>
|
||||
public interface INestOptimizer
|
||||
{
|
||||
OptimizationResult Optimize(List<NestItem> items, Box workArea, NfpCache cache,
|
||||
OptimizationResult Optimize(
|
||||
List<NestItem> items,
|
||||
Box workArea,
|
||||
NfpCache cache,
|
||||
Dictionary<int, List<double>> candidateRotations,
|
||||
IProgress<NestProgress> progress = null,
|
||||
CancellationToken cancellation = default);
|
||||
CancellationToken cancellation = default
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenNest.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
@@ -12,10 +12,10 @@ namespace OpenNest.Engine.Nfp
|
||||
public class NfpCache
|
||||
{
|
||||
private readonly Dictionary<NfpKey, Polygon> cache = new Dictionary<NfpKey, Polygon>();
|
||||
private readonly Dictionary<int, Dictionary<double, Polygon>> polygonCache
|
||||
= new Dictionary<int, Dictionary<double, Polygon>>();
|
||||
private readonly Dictionary<(int drawingId, double rotation), Polygon> ifpCache
|
||||
= new Dictionary<(int drawingId, double rotation), Polygon>();
|
||||
private readonly Dictionary<int, Dictionary<double, Polygon>> polygonCache =
|
||||
new Dictionary<int, Dictionary<double, Polygon>>();
|
||||
private readonly Dictionary<(int drawingId, double rotation), Polygon> ifpCache =
|
||||
new Dictionary<(int drawingId, double rotation), Polygon>();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a pre-computed polygon for a drawing at a specific rotation.
|
||||
@@ -107,8 +107,12 @@ namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
for (var j = 0; j < entries.Count; j++)
|
||||
{
|
||||
Get(entries[i].drawingId, entries[i].rotation,
|
||||
entries[j].drawingId, entries[j].rotation);
|
||||
Get(
|
||||
entries[i].drawingId,
|
||||
entries[i].rotation,
|
||||
entries[j].drawingId,
|
||||
entries[j].rotation
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,9 +140,9 @@ namespace OpenNest.Engine.Nfp
|
||||
public bool Equals(NfpKey other)
|
||||
{
|
||||
return DrawingIdA == other.DrawingIdA
|
||||
&& RotationA == other.RotationA
|
||||
&& DrawingIdB == other.DrawingIdB
|
||||
&& RotationB == other.RotationB;
|
||||
&& RotationA == other.RotationA
|
||||
&& DrawingIdB == other.DrawingIdB
|
||||
&& RotationB == other.RotationB;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is NfpKey key && Equals(key);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.Nfp
|
||||
{
|
||||
@@ -18,10 +18,14 @@ namespace OpenNest.Engine.Nfp
|
||||
private const double DefaultMinTemperature = 0.1;
|
||||
private const int DefaultMaxNoImprovement = 500;
|
||||
|
||||
public OptimizationResult Optimize(List<NestItem> items, Box workArea, NfpCache cache,
|
||||
public OptimizationResult Optimize(
|
||||
List<NestItem> items,
|
||||
Box workArea,
|
||||
NfpCache cache,
|
||||
Dictionary<int, List<double>> candidateRotations,
|
||||
IProgress<NestProgress> progress = null,
|
||||
CancellationToken cancellation = default)
|
||||
CancellationToken cancellation = default
|
||||
)
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
@@ -30,7 +34,12 @@ namespace OpenNest.Engine.Nfp
|
||||
var sequence = BuildInitialSequence(items, candidateRotations);
|
||||
|
||||
if (sequence.Count == 0)
|
||||
return new OptimizationResult { Sequence = sequence, Score = default, Iterations = 0 };
|
||||
return new OptimizationResult
|
||||
{
|
||||
Sequence = sequence,
|
||||
Score = default,
|
||||
Iterations = 0,
|
||||
};
|
||||
|
||||
// Evaluate initial solution.
|
||||
var blf = new BottomLeftFill(workArea, cache);
|
||||
@@ -42,20 +51,33 @@ namespace OpenNest.Engine.Nfp
|
||||
var currentScore = bestScore;
|
||||
|
||||
// Calibrate initial temperature so ~80% of worse moves are accepted.
|
||||
var initialTemp = CalibrateTemperature(currentSequence, workArea, cache,
|
||||
candidateRotations, random);
|
||||
var initialTemp = CalibrateTemperature(
|
||||
currentSequence,
|
||||
workArea,
|
||||
cache,
|
||||
candidateRotations,
|
||||
random
|
||||
);
|
||||
var temperature = initialTemp;
|
||||
var noImprovement = 0;
|
||||
var iteration = 0;
|
||||
|
||||
Debug.WriteLine($"[SA] Initial: {bestScore.Count} parts, density={bestScore.Density:P1}, temp={initialTemp:F2}");
|
||||
Debug.WriteLine(
|
||||
$"[SA] Initial: {bestScore.Count} parts, density={bestScore.Density:P1}, temp={initialTemp:F2}"
|
||||
);
|
||||
|
||||
ReportBest(progress, BottomLeftFill.ToNestParts(bestPlaced), workArea,
|
||||
$"NFP: initial {bestScore.Count} parts, density={bestScore.Density:P1}");
|
||||
ReportBest(
|
||||
progress,
|
||||
BottomLeftFill.ToNestParts(bestPlaced),
|
||||
workArea,
|
||||
$"NFP: initial {bestScore.Count} parts, density={bestScore.Density:P1}"
|
||||
);
|
||||
|
||||
while (temperature > DefaultMinTemperature
|
||||
&& noImprovement < DefaultMaxNoImprovement
|
||||
&& !cancellation.IsCancellationRequested)
|
||||
while (
|
||||
temperature > DefaultMinTemperature
|
||||
&& noImprovement < DefaultMaxNoImprovement
|
||||
&& !cancellation.IsCancellationRequested
|
||||
)
|
||||
{
|
||||
iteration++;
|
||||
|
||||
@@ -63,7 +85,10 @@ namespace OpenNest.Engine.Nfp
|
||||
Mutate(candidate, candidateRotations, random);
|
||||
|
||||
var candidatePlaced = blf.Fill(candidate);
|
||||
var candidateScore = FillScore.Compute(BottomLeftFill.ToNestParts(candidatePlaced), workArea);
|
||||
var candidateScore = FillScore.Compute(
|
||||
BottomLeftFill.ToNestParts(candidatePlaced),
|
||||
workArea
|
||||
);
|
||||
|
||||
var delta = candidateScore.CompareTo(currentScore);
|
||||
|
||||
@@ -79,10 +104,16 @@ namespace OpenNest.Engine.Nfp
|
||||
bestSequence = new List<SequenceEntry>(currentSequence);
|
||||
noImprovement = 0;
|
||||
|
||||
Debug.WriteLine($"[SA] New best at iter {iteration}: {bestScore.Count} parts, density={bestScore.Density:P1}");
|
||||
Debug.WriteLine(
|
||||
$"[SA] New best at iter {iteration}: {bestScore.Count} parts, density={bestScore.Density:P1}"
|
||||
);
|
||||
|
||||
ReportBest(progress, BottomLeftFill.ToNestParts(candidatePlaced), workArea,
|
||||
$"NFP: iter {iteration}, {bestScore.Count} parts, density={bestScore.Density:P1}");
|
||||
ReportBest(
|
||||
progress,
|
||||
BottomLeftFill.ToNestParts(candidatePlaced),
|
||||
workArea,
|
||||
$"NFP: iter {iteration}, {bestScore.Count} parts, density={bestScore.Density:P1}"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,13 +142,15 @@ namespace OpenNest.Engine.Nfp
|
||||
temperature *= DefaultCoolingRate;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[SA] Done: {iteration} iters, best={bestScore.Count} parts, density={bestScore.Density:P1}");
|
||||
Debug.WriteLine(
|
||||
$"[SA] Done: {iteration} iters, best={bestScore.Count} parts, density={bestScore.Density:P1}"
|
||||
);
|
||||
|
||||
return new OptimizationResult
|
||||
{
|
||||
Sequence = bestSequence,
|
||||
Score = bestScore,
|
||||
Iterations = iteration
|
||||
Iterations = iteration,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -126,7 +159,9 @@ namespace OpenNest.Engine.Nfp
|
||||
/// Each NestItem is expanded by its quantity.
|
||||
/// </summary>
|
||||
private static List<SequenceEntry> BuildInitialSequence(
|
||||
List<NestItem> items, Dictionary<int, List<double>> candidateRotations)
|
||||
List<NestItem> items,
|
||||
Dictionary<int, List<double>> candidateRotations
|
||||
)
|
||||
{
|
||||
var sequence = new List<SequenceEntry>();
|
||||
|
||||
@@ -138,7 +173,10 @@ namespace OpenNest.Engine.Nfp
|
||||
var qty = item.Quantity > 0 ? item.Quantity : 1;
|
||||
var rotation = 0.0;
|
||||
|
||||
if (candidateRotations.TryGetValue(item.Drawing.Id, out var rotations) && rotations.Count > 0)
|
||||
if (
|
||||
candidateRotations.TryGetValue(item.Drawing.Id, out var rotations)
|
||||
&& rotations.Count > 0
|
||||
)
|
||||
rotation = rotations[0];
|
||||
|
||||
for (var i = 0; i < qty; i++)
|
||||
@@ -151,8 +189,11 @@ namespace OpenNest.Engine.Nfp
|
||||
/// <summary>
|
||||
/// Applies a random mutation to the sequence.
|
||||
/// </summary>
|
||||
private static void Mutate(List<SequenceEntry> sequence,
|
||||
Dictionary<int, List<double>> candidateRotations, Random random)
|
||||
private static void Mutate(
|
||||
List<SequenceEntry> sequence,
|
||||
Dictionary<int, List<double>> candidateRotations,
|
||||
Random random
|
||||
)
|
||||
{
|
||||
if (sequence.Count < 2)
|
||||
return;
|
||||
@@ -190,13 +231,19 @@ namespace OpenNest.Engine.Nfp
|
||||
/// <summary>
|
||||
/// Changes a random part's rotation to another candidate angle.
|
||||
/// </summary>
|
||||
private static void MutateRotate(List<SequenceEntry> sequence,
|
||||
Dictionary<int, List<double>> candidateRotations, Random random)
|
||||
private static void MutateRotate(
|
||||
List<SequenceEntry> sequence,
|
||||
Dictionary<int, List<double>> candidateRotations,
|
||||
Random random
|
||||
)
|
||||
{
|
||||
var idx = random.Next(sequence.Count);
|
||||
var entry = sequence[idx];
|
||||
|
||||
if (!candidateRotations.TryGetValue(entry.DrawingId, out var rotations) || rotations.Count <= 1)
|
||||
if (
|
||||
!candidateRotations.TryGetValue(entry.DrawingId, out var rotations)
|
||||
|| rotations.Count <= 1
|
||||
)
|
||||
return;
|
||||
|
||||
var newRotation = rotations[random.Next(rotations.Count)];
|
||||
@@ -229,8 +276,11 @@ namespace OpenNest.Engine.Nfp
|
||||
/// </summary>
|
||||
private static double CalibrateTemperature(
|
||||
List<SequenceEntry> sequence,
|
||||
Box workArea, NfpCache cache,
|
||||
Dictionary<int, List<double>> candidateRotations, Random random)
|
||||
Box workArea,
|
||||
NfpCache cache,
|
||||
Dictionary<int, List<double>> candidateRotations,
|
||||
Random random
|
||||
)
|
||||
{
|
||||
const int samples = 20;
|
||||
var deltas = new List<double>();
|
||||
@@ -274,18 +324,25 @@ namespace OpenNest.Engine.Nfp
|
||||
return countDiff * 10.0 + densityDiff;
|
||||
}
|
||||
|
||||
private static void ReportBest(IProgress<NestProgress> progress, List<Part> parts,
|
||||
Box workArea, string description)
|
||||
private static void ReportBest(
|
||||
IProgress<NestProgress> progress,
|
||||
List<Part> parts,
|
||||
Box workArea,
|
||||
string description
|
||||
)
|
||||
{
|
||||
NestEngineBase.ReportProgress(progress, new ProgressReport
|
||||
{
|
||||
Phase = NestPhase.Nfp,
|
||||
PlateNumber = 0,
|
||||
Parts = parts,
|
||||
WorkArea = workArea,
|
||||
Description = description,
|
||||
IsOverallBest = true,
|
||||
});
|
||||
NestEngineBase.ReportProgress(
|
||||
progress,
|
||||
new ProgressReport
|
||||
{
|
||||
Phase = NestPhase.Nfp,
|
||||
PlateNumber = 0,
|
||||
Parts = parts,
|
||||
WorkArea = workArea,
|
||||
Description = description,
|
||||
IsOverallBest = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user