diff --git a/OpenNest.Core/Plate.cs b/OpenNest.Core/Plate.cs
index 674a1159..2c1aa62c 100644
--- a/OpenNest.Core/Plate.cs
+++ b/OpenNest.Core/Plate.cs
@@ -710,6 +710,14 @@ namespace OpenNest
return box;
}
+ ///
+ /// The offset that centers the parts' combined bounds in the ,
+ /// so uneven edge spacing (such as a wider clamp-side margin) shifts them with it.
+ /// Zero when the plate has no parts.
+ ///
+ public Vector OffsetToCenterPartsInWorkArea() =>
+ Parts.Count == 0 ? new Vector() : WorkArea().Center - Parts.GetBoundingBox().Center;
+
///
/// Automatically sizes the plate to fit the parts.
///
diff --git a/OpenNest.Tests/PlateCenterPartsTests.cs b/OpenNest.Tests/PlateCenterPartsTests.cs
new file mode 100644
index 00000000..51d76ad4
--- /dev/null
+++ b/OpenNest.Tests/PlateCenterPartsTests.cs
@@ -0,0 +1,50 @@
+using OpenNest.CNC;
+using OpenNest.Geometry;
+
+namespace OpenNest.Tests;
+
+public class PlateCenterPartsTests
+{
+ private static Part MakeSquare(Vector location)
+ {
+ var pgm = new Program();
+ pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
+ pgm.Codes.Add(new LinearMove(new Vector(10, 0)));
+ pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
+ pgm.Codes.Add(new LinearMove(new Vector(0, 10)));
+ pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
+ return new Part(new Drawing("square", pgm), location);
+ }
+
+ [Fact]
+ public void CentersPartsInWorkAreaNotPlate()
+ {
+ var plate = new Plate(100, 100)
+ {
+ // Wider clamp-side margin at the bottom.
+ EdgeSpacing = new Spacing(left: 0.25, bottom: 0.75, right: 0.25, top: 0.25),
+ };
+ var part = MakeSquare(new Vector(5, 5));
+ plate.Parts.Add(part);
+
+ part.Offset(plate.OffsetToCenterPartsInWorkArea());
+
+ var box = part.BoundingBox;
+ // Work area is X 0.25..99.75, Y 0.75..99.75, so the centre sits 0.25 above the plate's.
+ Assert.Equal(50, box.Center.X, 6);
+ Assert.Equal(50.25, box.Center.Y, 6);
+ Assert.Equal(box.Left - 0.25, 99.75 - box.Right, 6);
+ Assert.Equal(box.Bottom - 0.75, 99.75 - box.Top, 6);
+ }
+
+ [Fact]
+ public void EmptyPlateNeedsNoOffset()
+ {
+ var plate = new Plate(100, 100) { EdgeSpacing = new Spacing(0.25, 0.75, 0.25, 0.25) };
+
+ var offset = plate.OffsetToCenterPartsInWorkArea();
+
+ Assert.Equal(0, offset.X);
+ Assert.Equal(0, offset.Y);
+ }
+}
diff --git a/OpenNest/Forms/MainForm.Tools.cs b/OpenNest/Forms/MainForm.Tools.cs
index 30837a40..2a846af6 100644
--- a/OpenNest/Forms/MainForm.Tools.cs
+++ b/OpenNest/Forms/MainForm.Tools.cs
@@ -229,10 +229,7 @@ namespace OpenNest.Forms
if (view?.Plate == null || view.Parts.Count == 0)
return;
- var plateCenter = view.Plate.BoundingBox(false).Center;
- var partsCenter = view.Parts.GetBoundingBox().Center;
-
- var offset = plateCenter - partsCenter;
+ var offset = view.Plate.OffsetToCenterPartsInWorkArea();
foreach (var part in view.Parts)
part.Offset(offset);