From d0351ab765102c7a406c289c900d959324234b0b Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 16 Mar 2026 00:27:57 -0400 Subject: [PATCH] feat: add directional part sequencers (RightSide, LeftSide, BottomSide) Co-Authored-By: Claude Sonnet 4.6 --- .../Sequencing/BottomSideSequencer.cs | 17 +++++ .../Sequencing/LeftSideSequencer.cs | 17 +++++ .../Sequencing/RightSideSequencer.cs | 17 +++++ .../Sequencing/DirectionalSequencerTests.cs | 75 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 OpenNest.Engine/Sequencing/BottomSideSequencer.cs create mode 100644 OpenNest.Engine/Sequencing/LeftSideSequencer.cs create mode 100644 OpenNest.Engine/Sequencing/RightSideSequencer.cs create mode 100644 OpenNest.Tests/Sequencing/DirectionalSequencerTests.cs diff --git a/OpenNest.Engine/Sequencing/BottomSideSequencer.cs b/OpenNest.Engine/Sequencing/BottomSideSequencer.cs new file mode 100644 index 0000000..de06053 --- /dev/null +++ b/OpenNest.Engine/Sequencing/BottomSideSequencer.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Engine.Sequencing +{ + public class BottomSideSequencer : IPartSequencer + { + public List Sequence(IReadOnlyList parts, Plate plate) + { + return parts + .OrderBy(p => p.Location.Y) + .ThenBy(p => p.Location.X) + .Select(p => new SequencedPart { Part = p }) + .ToList(); + } + } +} diff --git a/OpenNest.Engine/Sequencing/LeftSideSequencer.cs b/OpenNest.Engine/Sequencing/LeftSideSequencer.cs new file mode 100644 index 0000000..ca0f20a --- /dev/null +++ b/OpenNest.Engine/Sequencing/LeftSideSequencer.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Engine.Sequencing +{ + public class LeftSideSequencer : IPartSequencer + { + public List Sequence(IReadOnlyList parts, Plate plate) + { + return parts + .OrderBy(p => p.Location.X) + .ThenBy(p => p.Location.Y) + .Select(p => new SequencedPart { Part = p }) + .ToList(); + } + } +} diff --git a/OpenNest.Engine/Sequencing/RightSideSequencer.cs b/OpenNest.Engine/Sequencing/RightSideSequencer.cs new file mode 100644 index 0000000..f804a38 --- /dev/null +++ b/OpenNest.Engine/Sequencing/RightSideSequencer.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Engine.Sequencing +{ + public class RightSideSequencer : IPartSequencer + { + public List Sequence(IReadOnlyList parts, Plate plate) + { + return parts + .OrderByDescending(p => p.Location.X) + .ThenBy(p => p.Location.Y) + .Select(p => new SequencedPart { Part = p }) + .ToList(); + } + } +} diff --git a/OpenNest.Tests/Sequencing/DirectionalSequencerTests.cs b/OpenNest.Tests/Sequencing/DirectionalSequencerTests.cs new file mode 100644 index 0000000..0a64a98 --- /dev/null +++ b/OpenNest.Tests/Sequencing/DirectionalSequencerTests.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using OpenNest.CNC; +using OpenNest.Engine.Sequencing; +using OpenNest.Geometry; +using Xunit; + +namespace OpenNest.Tests.Sequencing; + +public class DirectionalSequencerTests +{ + private static Part MakePartAt(double x, double y) => TestHelpers.MakePartAt(x, y); + private static Plate MakePlate(params Part[] parts) => TestHelpers.MakePlate(60, 120, parts); + + [Fact] + public void RightSide_SortsXDescending() + { + var a = MakePartAt(10, 5); + var b = MakePartAt(30, 5); + var c = MakePartAt(20, 5); + var plate = MakePlate(a, b, c); + + var sequencer = new RightSideSequencer(); + var result = sequencer.Sequence(plate.Parts.ToList(), plate); + + Assert.Same(b, result[0].Part); + Assert.Same(c, result[1].Part); + Assert.Same(a, result[2].Part); + } + + [Fact] + public void LeftSide_SortsXAscending() + { + var a = MakePartAt(10, 5); + var b = MakePartAt(30, 5); + var c = MakePartAt(20, 5); + var plate = MakePlate(a, b, c); + + var sequencer = new LeftSideSequencer(); + var result = sequencer.Sequence(plate.Parts.ToList(), plate); + + Assert.Same(a, result[0].Part); + Assert.Same(c, result[1].Part); + Assert.Same(b, result[2].Part); + } + + [Fact] + public void BottomSide_SortsYAscending() + { + var a = MakePartAt(5, 20); + var b = MakePartAt(5, 5); + var c = MakePartAt(5, 10); + var plate = MakePlate(a, b, c); + + var sequencer = new BottomSideSequencer(); + var result = sequencer.Sequence(plate.Parts.ToList(), plate); + + Assert.Same(b, result[0].Part); + Assert.Same(c, result[1].Part); + Assert.Same(a, result[2].Part); + } + + [Fact] + public void RightSide_TiesBrokenByPerpendicularAxis() + { + var a = MakePartAt(10, 20); + var b = MakePartAt(10, 5); + var plate = MakePlate(a, b); + + var sequencer = new RightSideSequencer(); + var result = sequencer.Sequence(plate.Parts.ToList(), plate); + + Assert.Same(b, result[0].Part); + Assert.Same(a, result[1].Part); + } +}