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:
aj
2026-09-20 16:41:50 -04:00
parent 8e6fa677fb
commit aec0523062
476 changed files with 16592 additions and 7800 deletions
+59 -25
View File
@@ -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 };
+39 -16
View File
@@ -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);
+8 -4
View File
@@ -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
);
}
}
+14 -10
View File
@@ -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);
+96 -39
View File
@@ -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,
}
);
}
}
}