Files
OpenNest/OpenNest.Engine/BestFit/RotationSlideStrategy.cs

132 lines
4.9 KiB
C#

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<PairCandidate> GenerateCandidates(Drawing drawing, double spacing, double stepSize)
{
var candidates = new List<PairCandidate>();
// 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<PairCandidate> 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;
}
}
}
}