using OpenNest.CNC.CuttingStrategy; using OpenNest.Math; using System.Collections.Generic; using System.Linq; namespace OpenNest.Engine.Sequencing { public class AdvancedSequencer : IPartSequencer { private readonly SequenceParameters _parameters; public AdvancedSequencer(SequenceParameters parameters) { _parameters = parameters; } public List Sequence(IReadOnlyList parts, Plate plate) { if (parts.Count == 0) return new List(); var exit = PlateHelper.GetExitPoint(plate); // Group parts into rows by Y proximity var rows = GroupIntoRows(parts, _parameters.MinDistanceBetweenRowsColumns); // Sort rows bottom-to-top (ascending Y) rows.Sort((a, b) => a.RowY.CompareTo(b.RowY)); // Determine initial direction based on exit point var leftToRight = exit.X > plate.Size.Length * 0.5; var result = new List(parts.Count); foreach (var row in rows) { var sorted = leftToRight ? row.Parts.OrderBy(p => p.BoundingBox.Center.X).ToList() : row.Parts.OrderByDescending(p => p.BoundingBox.Center.X).ToList(); foreach (var p in sorted) result.Add(new SequencedPart { Part = p }); if (_parameters.AlternateRowsColumns) leftToRight = !leftToRight; } return result; } private static List GroupIntoRows(IReadOnlyList parts, double minDistance) { // Sort parts by Y center var sorted = parts .OrderBy(p => p.BoundingBox.Center.Y) .ToList(); var rows = new List(); foreach (var part in sorted) { var y = part.BoundingBox.Center.Y; var placed = false; foreach (var row in rows) { if (System.Math.Abs(y - row.RowY) <= minDistance + Tolerance.Epsilon) { row.Parts.Add(part); placed = true; break; } } if (!placed) { var row = new PartRow(y); row.Parts.Add(part); rows.Add(row); } } return rows; } private class PartRow { public double RowY { get; } public List Parts { get; } = new List(); public PartRow(double rowY) { RowY = rowY; } } } }