diff --git a/OpenNest.Core/Splitting/AutoSplitCalculator.cs b/OpenNest.Core/Splitting/AutoSplitCalculator.cs new file mode 100644 index 0000000..bebced2 --- /dev/null +++ b/OpenNest.Core/Splitting/AutoSplitCalculator.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using OpenNest.Geometry; + +namespace OpenNest; + +public static class AutoSplitCalculator +{ + public static List FitToPlate(Box partBounds, double plateWidth, double plateHeight, + double edgeSpacing, double featureOverhang) + { + var usableWidth = plateWidth - 2 * edgeSpacing - featureOverhang; + var usableHeight = plateHeight - 2 * edgeSpacing - featureOverhang; + + var lines = new List(); + + var verticalSplits = usableWidth > 0 ? (int)System.Math.Ceiling(partBounds.Width / usableWidth) - 1 : 0; + var horizontalSplits = usableHeight > 0 ? (int)System.Math.Ceiling(partBounds.Length / usableHeight) - 1 : 0; + + if (verticalSplits < 0) verticalSplits = 0; + if (horizontalSplits < 0) horizontalSplits = 0; + + if (verticalSplits > 0) + { + var spacing = partBounds.Width / (verticalSplits + 1); + for (var i = 1; i <= verticalSplits; i++) + lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical)); + } + + if (horizontalSplits > 0) + { + var spacing = partBounds.Length / (horizontalSplits + 1); + for (var i = 1; i <= horizontalSplits; i++) + lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal)); + } + + return lines; + } + + public static List SplitByCount(Box partBounds, int horizontalPieces, int verticalPieces) + { + var lines = new List(); + + if (verticalPieces > 1) + { + var spacing = partBounds.Width / verticalPieces; + for (var i = 1; i < verticalPieces; i++) + lines.Add(new SplitLine(partBounds.X + spacing * i, CutOffAxis.Vertical)); + } + + if (horizontalPieces > 1) + { + var spacing = partBounds.Length / horizontalPieces; + for (var i = 1; i < horizontalPieces; i++) + lines.Add(new SplitLine(partBounds.Y + spacing * i, CutOffAxis.Horizontal)); + } + + return lines; + } +} diff --git a/OpenNest.Tests/Splitting/SplitLineTests.cs b/OpenNest.Tests/Splitting/SplitLineTests.cs index 1abb55e..69b7065 100644 --- a/OpenNest.Tests/Splitting/SplitLineTests.cs +++ b/OpenNest.Tests/Splitting/SplitLineTests.cs @@ -1,3 +1,5 @@ +using OpenNest.Geometry; + namespace OpenNest.Tests.Splitting; public class SplitLineTests @@ -29,3 +31,57 @@ public class SplitLineTests Assert.Equal(2, p.SpikePairCount); } } + +public class AutoSplitCalculatorTests +{ + [Fact] + public void FitToPlate_SingleAxis_CalculatesCorrectSplits() + { + var partBounds = new Box(0, 0, 100, 50); + var lines = AutoSplitCalculator.FitToPlate(partBounds, 60, 60, 1.0, 0); + + Assert.Single(lines); + Assert.Equal(CutOffAxis.Vertical, lines[0].Axis); + Assert.Equal(50.0, lines[0].Position, 1); + } + + [Fact] + public void FitToPlate_BothAxes_GeneratesGrid() + { + var partBounds = new Box(0, 0, 200, 200); + var lines = AutoSplitCalculator.FitToPlate(partBounds, 60, 60, 0, 0); + + var verticals = lines.Where(l => l.Axis == CutOffAxis.Vertical).ToList(); + var horizontals = lines.Where(l => l.Axis == CutOffAxis.Horizontal).ToList(); + Assert.Equal(3, verticals.Count); + Assert.Equal(3, horizontals.Count); + } + + [Fact] + public void FitToPlate_AlreadyFits_ReturnsEmpty() + { + var partBounds = new Box(0, 0, 50, 50); + var lines = AutoSplitCalculator.FitToPlate(partBounds, 60, 60, 1.0, 0); + Assert.Empty(lines); + } + + [Fact] + public void SplitByCount_SingleAxis_EvenlySpaced() + { + var partBounds = new Box(0, 0, 100, 50); + var lines = AutoSplitCalculator.SplitByCount(partBounds, horizontalPieces: 1, verticalPieces: 3); + + Assert.Equal(2, lines.Count); + Assert.All(lines, l => Assert.Equal(CutOffAxis.Vertical, l.Axis)); + Assert.Equal(33.333, lines[0].Position, 2); + Assert.Equal(66.667, lines[1].Position, 2); + } + + [Fact] + public void FitToPlate_AccountsForFeatureOverhang() + { + var partBounds = new Box(0, 0, 100, 50); + var lines = AutoSplitCalculator.FitToPlate(partBounds, 60, 60, 1.0, 0.5); + Assert.Single(lines); + } +}