using System.Collections.Generic; using OpenNest.Geometry; using OpenNest.Math; namespace OpenNest.Engine.BestFit { public class RotationSlideStrategy : IBestFitStrategy { public RotationSlideStrategy(double part2Rotation, int type, string description) { Part2Rotation = part2Rotation; Type = type; Description = description; } public double Part2Rotation { get; } public int Type { get; } public string Description { get; } public List GenerateCandidates(Drawing drawing, double spacing, double stepSize) { var candidates = new List(); // Build part1 at origin var part1 = new Part(drawing); var bbox1 = part1.Program.BoundingBox(); part1.Offset(-bbox1.Location.X, -bbox1.Location.Y); part1.UpdateBounds(); // Build part2 template with rotation, normalized to origin var part2Template = new Part(drawing); if (!Part2Rotation.IsEqualTo(0)) part2Template.Rotate(Part2Rotation); var bbox2 = part2Template.Program.BoundingBox(); part2Template.Offset(-bbox2.Location.X, -bbox2.Location.Y); part2Template.UpdateBounds(); var testNumber = 0; // Try pushing left (horizontal slide) GenerateCandidatesForAxis( part1, part2Template, drawing, spacing, stepSize, PushDirection.Left, candidates, ref testNumber); // Try pushing down (vertical slide) GenerateCandidatesForAxis( part1, part2Template, drawing, spacing, stepSize, PushDirection.Down, candidates, ref testNumber); return candidates; } private void GenerateCandidatesForAxis( Part part1, Part part2Template, Drawing drawing, double spacing, double stepSize, PushDirection pushDir, List candidates, ref int testNumber) { var bbox1 = part1.BoundingBox; var bbox2 = part2Template.BoundingBox; var halfSpacing = spacing / 2; var isHorizontalPush = pushDir == PushDirection.Left || pushDir == PushDirection.Right; // Perpendicular range: part2 slides across the full extent of part1 double perpMin, perpMax, pushStartOffset; if (isHorizontalPush) { perpMin = -(bbox2.Height + spacing); perpMax = bbox1.Height + bbox2.Height + spacing; pushStartOffset = bbox1.Width + bbox2.Width + spacing * 2; } else { perpMin = -(bbox2.Width + spacing); perpMax = bbox1.Width + bbox2.Width + spacing; pushStartOffset = bbox1.Height + bbox2.Height + spacing * 2; } // Pre-compute part1's offset lines (half-spacing outward) var part1Lines = Helper.GetOffsetPartLines(part1, halfSpacing); for (var offset = perpMin; offset <= perpMax; offset += stepSize) { var part2 = (Part)part2Template.Clone(); // Place part2 far away along push axis, at perpendicular offset if (isHorizontalPush) part2.Offset(pushStartOffset, offset); else part2.Offset(offset, pushStartOffset); // Get part2's offset lines (half-spacing outward) var part2Lines = Helper.GetOffsetPartLines(part2, halfSpacing); // Find contact distance var slideDist = Helper.DirectionalDistance(part2Lines, part1Lines, pushDir); if (slideDist >= double.MaxValue || slideDist < 0) continue; // Move part2 to contact position var pushVector = GetPushVector(pushDir, slideDist); var finalPosition = part2.Location + pushVector; candidates.Add(new PairCandidate { Drawing = drawing, Part1Rotation = 0, Part2Rotation = Part2Rotation, Part2Offset = finalPosition, StrategyType = Type, TestNumber = testNumber++, Spacing = spacing }); } } private static Vector GetPushVector(PushDirection direction, double distance) { switch (direction) { case PushDirection.Left: return new Vector(-distance, 0); case PushDirection.Right: return new Vector(distance, 0); case PushDirection.Down: return new Vector(0, -distance); case PushDirection.Up: return new Vector(0, distance); default: return Vector.Zero; } } } }