using System.Collections.Generic;
using System.Linq;
using OpenNest.Converters;
using OpenNest.Geometry;
namespace OpenNest
{
///
/// Pre-computed offset boundary polygons for a part's geometry.
/// Polygons are stored at program-local origin (no location applied)
/// and can be efficiently translated to any location when extracting lines.
///
public class PartBoundary
{
private const double ChordTolerance = 0.01;
private readonly List _polygons;
public PartBoundary(Part part, double spacing)
{
var entities = ConvertProgram.ToGeometry(part.Program);
var shapes = Helper.GetShapes(entities.Where(e => e.Layer != SpecialLayers.Rapid));
_polygons = new List();
foreach (var shape in shapes)
{
var offsetEntity = shape.OffsetEntity(spacing + ChordTolerance, OffsetSide.Left) as Shape;
if (offsetEntity == null)
continue;
var polygon = offsetEntity.ToPolygonWithTolerance(ChordTolerance);
polygon.RemoveSelfIntersections();
_polygons.Add(polygon);
}
}
///
/// Returns offset boundary lines translated to the given location,
/// filtered to edges whose outward normal faces the specified direction.
///
public List GetLines(Vector location, PushDirection facingDirection)
{
var lines = new List();
foreach (var polygon in _polygons)
lines.AddRange(TranslateDirectionalLines(polygon, location, facingDirection));
return lines;
}
///
/// Returns all offset boundary lines translated to the given location.
///
public List GetLines(Vector location)
{
var lines = new List();
foreach (var polygon in _polygons)
{
var verts = polygon.Vertices;
if (verts.Count < 2)
continue;
var last = verts[0].Offset(location);
for (var i = 1; i < verts.Count; i++)
{
var current = verts[i].Offset(location);
lines.Add(new Line(last, current));
last = current;
}
}
return lines;
}
private static List TranslateDirectionalLines(
Polygon polygon, Vector location, PushDirection facingDirection)
{
var verts = polygon.Vertices;
if (verts.Count < 3)
{
var fallback = new List();
if (verts.Count >= 2)
{
var last = verts[0].Offset(location);
for (var i = 1; i < verts.Count; i++)
{
var current = verts[i].Offset(location);
fallback.Add(new Line(last, current));
last = current;
}
}
return fallback;
}
var sign = polygon.RotationDirection() == RotationType.CCW ? 1.0 : -1.0;
var result = new List();
var prev = verts[0].Offset(location);
for (var i = 1; i < verts.Count; i++)
{
var current = verts[i].Offset(location);
// Use un-translated deltas — translation doesn't affect direction.
var dx = verts[i].X - verts[i - 1].X;
var dy = verts[i].Y - verts[i - 1].Y;
bool keep;
switch (facingDirection)
{
case PushDirection.Left: keep = -sign * dy > 0; break;
case PushDirection.Right: keep = sign * dy > 0; break;
case PushDirection.Up: keep = -sign * dx > 0; break;
case PushDirection.Down: keep = sign * dx > 0; break;
default: keep = true; break;
}
if (keep)
result.Add(new Line(prev, current));
prev = current;
}
return result;
}
}
}