mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 01:05:07 -05:00
The console overlap check and the MCP check_overlaps tool counted crossing points, so a part contained in or stacked on another reported no overlap. Both now list pairs from PartOverlapChecker.FindOverlappingPairs, which uses the Intersects verdict and skips cut-off parts. Plate.HasOverlappingParts stays for now: two test files outside this change still call it. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
147 lines
5.5 KiB
C#
147 lines
5.5 KiB
C#
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using ModelContextProtocol.Server;
|
|
using OpenNest.Engine.Fill;
|
|
using OpenNest.Math;
|
|
|
|
namespace OpenNest.Mcp.Tools
|
|
{
|
|
[McpServerToolType]
|
|
public class InspectionTools
|
|
{
|
|
private readonly NestSession _session;
|
|
|
|
public InspectionTools(NestSession session)
|
|
{
|
|
_session = session;
|
|
}
|
|
|
|
[McpServerTool(Name = "get_plate_info")]
|
|
[Description(
|
|
"Get detailed information about a plate including dimensions, part count, utilization, remnants, and drawing breakdown."
|
|
)]
|
|
public string GetPlateInfo([Description("Index of the plate")] int plateIndex)
|
|
{
|
|
var plate = _session.GetPlate(plateIndex);
|
|
if (plate == null)
|
|
return $"Error: plate {plateIndex} not found";
|
|
|
|
var work = plate.WorkArea();
|
|
var remnants = RemnantFinder.FromPlate(plate).FindRemnants();
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine($"Plate {plateIndex}:");
|
|
sb.AppendLine($" Size: {plate.Size.Width:F1} x {plate.Size.Length:F1}");
|
|
sb.AppendLine($" Quadrant: {plate.Quadrant}");
|
|
sb.AppendLine($" Thickness: {_session.Nest?.Thickness:F2}");
|
|
sb.AppendLine($" Material: {_session.Nest?.Material?.Name}");
|
|
sb.AppendLine($" Part spacing: {plate.PartSpacing:F2}");
|
|
sb.AppendLine(
|
|
$" Edge spacing: L={plate.EdgeSpacing.Left:F2} B={plate.EdgeSpacing.Bottom:F2} R={plate.EdgeSpacing.Right:F2} T={plate.EdgeSpacing.Top:F2}"
|
|
);
|
|
sb.AppendLine($" Work area: {work.X:F1},{work.Y:F1} {work.Width:F1}x{work.Length:F1}");
|
|
sb.AppendLine($" Parts: {plate.Parts.Count}");
|
|
sb.AppendLine($" Utilization: {plate.Utilization():P1}");
|
|
sb.AppendLine($" Quantity: {plate.Quantity}");
|
|
|
|
// Drawing breakdown
|
|
if (plate.Parts.Count > 0)
|
|
{
|
|
sb.AppendLine(" Drawings:");
|
|
var groups = plate.Parts.GroupBy(p => p.BaseDrawing.Name);
|
|
foreach (var group in groups)
|
|
sb.AppendLine($" {group.Key}: {group.Count()}");
|
|
}
|
|
|
|
// Remnants
|
|
sb.AppendLine($" Remnants: {remnants.Count}");
|
|
for (var i = 0; i < remnants.Count; i++)
|
|
{
|
|
var r = remnants[i];
|
|
sb.AppendLine(
|
|
$" Remnant {i}: ({r.X:F1},{r.Y:F1}) {r.Width:F1}x{r.Length:F1}, area={r.Area():F1}"
|
|
);
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
[McpServerTool(Name = "get_parts")]
|
|
[Description(
|
|
"List placed parts on a plate with index, drawing name, location, rotation, and bounding box."
|
|
)]
|
|
public string GetParts(
|
|
[Description("Index of the plate")] int plateIndex,
|
|
[Description("Maximum number of parts to list (default 50)")] int limit = 50
|
|
)
|
|
{
|
|
var plate = _session.GetPlate(plateIndex);
|
|
if (plate == null)
|
|
return $"Error: plate {plateIndex} not found";
|
|
|
|
if (plate.Parts.Count == 0)
|
|
return $"Plate {plateIndex} has no parts";
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine($"Plate {plateIndex}: {plate.Parts.Count} parts (showing up to {limit})");
|
|
|
|
var count = System.Math.Min(plate.Parts.Count, limit);
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var part = plate.Parts[i];
|
|
var bbox = part.BoundingBox;
|
|
var rotDeg = Angle.ToDegrees(part.Rotation);
|
|
|
|
sb.AppendLine(
|
|
$" [{i}] {part.BaseDrawing.Name}: "
|
|
+ $"loc=({part.Location.X:F2},{part.Location.Y:F2}), "
|
|
+ $"rot={rotDeg:F1} deg, "
|
|
+ $"bbox=({bbox.X:F2},{bbox.Y:F2} {bbox.Width:F2}x{bbox.Length:F2})"
|
|
);
|
|
}
|
|
|
|
if (plate.Parts.Count > limit)
|
|
sb.AppendLine($" ... and {plate.Parts.Count - limit} more");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
[McpServerTool(Name = "check_overlaps")]
|
|
[Description("Check a plate for overlapping parts. Reports the overlapping pairs by part index if any.")]
|
|
public string CheckOverlaps([Description("Index of the plate")] int plateIndex)
|
|
{
|
|
var plate = _session.GetPlate(plateIndex);
|
|
if (plate == null)
|
|
return $"Error: plate {plateIndex} not found";
|
|
|
|
if (plate.Parts.Count < 2)
|
|
return $"Plate {plateIndex}: no overlaps possible (fewer than 2 parts)";
|
|
|
|
var pairs = PartOverlapChecker.FindOverlappingPairs(plate.Parts);
|
|
|
|
if (pairs.Count == 0)
|
|
return $"Plate {plateIndex}: no overlapping parts detected";
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine($"Plate {plateIndex}: {pairs.Count} overlapping pair(s) detected!");
|
|
|
|
var limit = System.Math.Min(pairs.Count, 20);
|
|
|
|
for (var i = 0; i < limit; i++)
|
|
{
|
|
var (first, second) = pairs[i];
|
|
sb.AppendLine(
|
|
$" Part {plate.Parts.IndexOf(first)} overlaps part {plate.Parts.IndexOf(second)}"
|
|
);
|
|
}
|
|
|
|
if (pairs.Count > limit)
|
|
sb.AppendLine($" ... and {pairs.Count - limit} more overlapping pairs");
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
}
|