using System.Collections.Generic;
using System.Linq;
using OpenNest.Geometry;
namespace OpenNest.Diagnostics;
/// An owned diagnostic result. An empty Pairs list is clear only if IsComplete is true.
public sealed class PlateOverlapReport
{
internal PlateOverlapReport(List pairs, List issues,
PlateOverlapSnapshot snapshot)
{
Pairs = pairs.AsReadOnly();
Issues = issues.AsReadOnly();
Snapshot = snapshot;
}
/// The owned input this report was computed from; the baseline for incremental rechecks.
internal PlateOverlapSnapshot Snapshot { get; }
public IReadOnlyList Pairs { get; }
public IReadOnlyList Issues { get; }
public bool IsComplete => Issues.Count == 0;
public double ChordTolerance => PlateOverlapAnalyzer.ChordTolerance;
}
/// Shared material for two input positions, ordered by zero-based input index.
public sealed class PlateOverlapPair
{
private readonly Box bounds;
private PlateOverlapPair(PlateOverlapPair source, int partAId, int partBId, string partAName,
string partBName)
{
PartAId = partAId;
PartBId = partBId;
PartAName = partAName;
PartBName = partBName;
Regions = source.Regions;
Area = source.Area;
Centroid = source.Centroid;
bounds = source.bounds;
}
/// The same immutable geometry under new input positions and captured names.
internal PlateOverlapPair Renumber(int partAId, int partBId, string partAName, string partBName) =>
new(this, partAId, partBId, partAName, partBName);
internal PlateOverlapPair(int partAId, int partBId, string partAName, string partBName,
List regions, Vector centroid)
{
PartAId = partAId;
PartBId = partBId;
PartAName = partAName;
PartBName = partBName;
Regions = regions.AsReadOnly();
Area = regions.Sum(region => region.Area);
Centroid = centroid;
var points = regions.SelectMany(region => region.Vertices).ToArray();
var left = points.Min(point => point.X);
var bottom = points.Min(point => point.Y);
bounds = new Box(left, bottom, points.Max(point => point.X) - left,
points.Max(point => point.Y) - bottom);
}
public int PartAId { get; }
public int PartBId { get; }
public string PartAName { get; }
public string PartBName { get; }
/// Convex fragments, not connected islands; no mutable kernel polygons are exposed.
public IReadOnlyList Regions { get; }
public double Area { get; }
///
/// Finite world-coordinate area centroid of all shared material, after hole subtraction.
/// This can lie outside disconnected or concave shared material. Returned by value.
///
public Vector Centroid { get; }
/// A fresh world-coordinate bounds copy.
public Box Bounds => new(bounds.X, bounds.Y, bounds.Length, bounds.Width);
}
/// A positive-area, hole-subtracted convex polygon in world coordinates.
public sealed class PlateOverlapRegion
{
internal PlateOverlapRegion(IEnumerable vertices, double area)
{
Vertices = System.Array.AsReadOnly(vertices.ToArray());
Area = area;
}
/// Read-only vertices with an exactly repeated closing vertex.
public IReadOnlyList Vertices { get; }
public double Area { get; }
}
/// An input or pair that could not be checked. IDs are zero-based input positions.
public sealed record PlateOverlapIssue(int PartAId, int? PartBId, string Message);
///
/// Owned clean geometry and poses. Capture while inputs are stable, then analyze on a worker.
/// No live Part, Drawing, Program, or subprogram is retained.
///
public sealed class PlateOverlapSnapshot
{
internal PlateOverlapSnapshot(List parts, List issues)
{
Parts = parts.AsReadOnly();
Issues = issues.AsReadOnly();
}
internal IReadOnlyList Parts { get; }
internal IReadOnlyList Issues { get; }
}
internal sealed record CapturedOverlapPart(int Id, string Name, OverlapSource Source,
double Rotation, Vector Location);