using System.ComponentModel;
using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Modeling;
using ModelContextProtocol.Server;
namespace Kompas.Mcp.Host.Tools;
/// Инструменты построения эскизов в активной детали.
[McpServerToolType]
public sealed class SketchTools(KompasSession session, PartModeler modeler)
{
[McpServerTool(Name = "sketch_create")]
[Description("Создать эскиз на базовой плоскости (XOY, XOZ или YOZ) активной детали и открыть его на редактирование. Возвращает id эскиза для последующих sketch_add_* и операций.")]
public async Task Create([Description("Плоскость: XOY, XOZ или YOZ")] string plane)
{
await session.ConnectAsync();
var id = await modeler.OpenSketchAsync(BasePlanes.Parse(plane));
return $"Эскиз создан, id={id}. Добавьте геометрию (sketch_add_*) и завершите sketch_close.";
}
[McpServerTool(Name = "sketch_create_on_face")]
[Description("Создать эскиз на плоской грани детали, выбранной по точке (мировые координаты модели x,y,z в мм), и открыть его на редактирование. Используйте для построения «на грани» поверх уже созданной геометрии: сделайте model_snapshot, прикиньте координаты точки на нужной грани, затем вызовите этот инструмент. Возвращает id эскиза.")]
public async Task CreateOnFace(double x, double y, double z)
{
await session.ConnectAsync();
var id = await modeler.OpenSketchOnFaceAsync(x, y, z);
return $"Эскиз создан на грани в точке ({x}, {y}, {z}), id={id}. Координаты геометрии — в локальной плоскости эскиза.";
}
[McpServerTool(Name = "sketch_create_on_offset_plane")]
[Description("Создать эскиз на плоскости, смещённой от базовой (XOY|XOZ|YOZ) на offset мм, и открыть на редактирование. Позволяет строить геометрию на произвольной высоте/смещении (например сечения для операции loft). direction — направление смещения от базовой плоскости. Возвращает id эскиза.")]
public async Task CreateOnOffsetPlane(
[Description("Базовая плоскость: XOY | XOZ | YOZ")] string basePlane,
[Description("Смещение от базовой плоскости, мм")] double offset,
[Description("Прямое направление смещения")] bool direction = true)
{
await session.ConnectAsync();
var id = await modeler.OpenSketchOnOffsetPlaneAsync(BasePlanes.Parse(basePlane), offset, direction);
return $"Эскиз создан на смещённой плоскости {basePlane}+{offset}, id={id}.";
}
[McpServerTool(Name = "sketch_add_circle")]
[Description("Добавить окружность в открытый эскиз. Координаты — в плоскости эскиза, мм.")]
public async Task AddCircle(int sketchId, double centerX, double centerY, double radius)
{
await session.ConnectAsync();
await modeler.AddCircleAsync(sketchId, centerX, centerY, radius);
return "Окружность добавлена.";
}
[McpServerTool(Name = "sketch_add_line")]
[Description("Добавить отрезок в открытый эскиз (две точки, мм).")]
public async Task AddLine(int sketchId, double x1, double y1, double x2, double y2)
{
await session.ConnectAsync();
await modeler.AddLineAsync(sketchId, x1, y1, x2, y2);
return "Отрезок добавлен.";
}
[McpServerTool(Name = "sketch_create_on_face_index")]
[Description("Создать эскиз на грани детали, заданной индексом из list_faces, и открыть на редактирование. Надёжнее выбора по точке (не нужно угадывать координаты). Возвращает id эскиза.")]
public async Task CreateOnFaceIndex([Description("Индекс грани из list_faces")] int faceIndex)
{
await session.ConnectAsync();
var id = await modeler.OpenSketchOnFaceIndexAsync(faceIndex);
return $"Эскиз создан на грани [{faceIndex}], id={id}.";
}
[McpServerTool(Name = "sketch_add_axis")]
[Description("Добавить осевую линию (ось вращения) в открытый эскиз — нужна для revolve_boss/revolve_cut. Две точки в плоскости эскиза, мм.")]
public async Task AddAxis(int sketchId, double x1, double y1, double x2, double y2)
{
await session.ConnectAsync();
await modeler.AddAxisAsync(sketchId, x1, y1, x2, y2);
return "Осевая линия добавлена.";
}
[McpServerTool(Name = "sketch_add_rectangle")]
[Description("Добавить прямоугольник по двум противоположным углам в открытый эскиз (мм).")]
public async Task AddRectangle(int sketchId, double x1, double y1, double x2, double y2)
{
await session.ConnectAsync();
await modeler.AddRectangleAsync(sketchId, x1, y1, x2, y2);
return "Прямоугольник добавлен.";
}
[McpServerTool(Name = "sketch_close")]
[Description("Завершить редактирование эскиза (перед операцией выдавливания).")]
public async Task Close(int sketchId)
{
await session.ConnectAsync();
await modeler.CloseSketchAsync(sketchId);
return "Эскиз закрыт.";
}
[McpServerTool(Name = "sketch_add_arc_3points")]
[Description("Добавить дугу по трём точкам (начало, промежуточная точка на дуге, конец) в открытый эскиз. Координаты — в плоскости эскиза, мм.")]
public async Task AddArc3Points(int sketchId, double x1, double y1, double x2, double y2, double x3, double y3)
{
await session.ConnectAsync();
await modeler.AddArc3PointsAsync(sketchId, x1, y1, x2, y2, x3, y3);
return "Дуга по 3 точкам добавлена.";
}
[McpServerTool(Name = "sketch_add_arc")]
[Description("Добавить дугу по центру, радиусу и углам (в градусах, от оси X) в открытый эскиз. counterClockwise=true — против часовой стрелки, false — по часовой. Координаты центра — в плоскости эскиза, мм.")]
public async Task AddArc(int sketchId, double centerX, double centerY, double radius, double startAngle, double endAngle, bool counterClockwise = true)
{
await session.ConnectAsync();
await modeler.AddArcByAngleAsync(sketchId, centerX, centerY, radius, startAngle, endAngle, counterClockwise);
return "Дуга добавлена.";
}
[McpServerTool(Name = "sketch_add_ellipse")]
[Description("Добавить эллипс в открытый эскиз: центр, большая полуось semiMajor, малая полуось semiMinor (мм), угол наклона большой оси к X (градусы, по умолчанию 0).")]
public async Task AddEllipse(int sketchId, double centerX, double centerY, double semiMajor, double semiMinor, double angle = 0)
{
await session.ConnectAsync();
await modeler.AddEllipseAsync(sketchId, centerX, centerY, semiMajor, semiMinor, angle);
return "Эллипс добавлен.";
}
[McpServerTool(Name = "sketch_add_polyline")]
[Description("Добавить ломаную — цепочку прямых отрезков по списку точек — в открытый эскиз. closed=true замыкает последнюю точку с первой (тогда нужно минимум 3 точки). Координаты — в плоскости эскиза, мм.")]
public async Task AddPolyline(int sketchId, SketchPoint[] points, bool closed = false)
{
await session.ConnectAsync();
await modeler.AddPolylineAsync(sketchId, Map(points), closed);
return $"Ломаная из {points.Length} точек добавлена{(closed ? " (замкнута)" : "")}.";
}
[McpServerTool(Name = "sketch_add_polygon")]
[Description("Добавить правильный многоугольник в открытый эскиз: центр, vertexCount вершин (>=3), radius (мм). inscribed=true — вершины лежат на окружности радиуса (вписанный в окружность); false — стороны касаются окружности (описанный). angle — поворот первой вершины (градусы, по умолчанию 0).")]
public async Task AddPolygon(int sketchId, double centerX, double centerY, int vertexCount, double radius, bool inscribed = true, double angle = 0)
{
await session.ConnectAsync();
await modeler.AddPolygonAsync(sketchId, centerX, centerY, vertexCount, radius, inscribed, angle);
return $"Правильный многоугольник ({vertexCount} вершин) добавлен.";
}
[McpServerTool(Name = "sketch_add_spline")]
[Description("Добавить сплайн (кубический NURBS) через список точек в открытый эскиз. closed=true замыкает кривую. Координаты — в плоскости эскиза, мм.")]
public async Task AddSpline(int sketchId, SketchPoint[] points, bool closed = false)
{
await session.ConnectAsync();
await modeler.AddSplineAsync(sketchId, Map(points), closed);
return $"Сплайн по {points.Length} точкам добавлен{(closed ? " (замкнут)" : "")}.";
}
[McpServerTool(Name = "sketch_add_point")]
[Description("Добавить точку в открытый эскиз (опорная точка для построений). Координаты — в плоскости эскиза, мм.")]
public async Task AddPoint(int sketchId, double x, double y)
{
await session.ConnectAsync();
await modeler.AddPointAsync(sketchId, x, y);
return "Точка добавлена.";
}
private static IReadOnlyList<(double x, double y)> Map(SketchPoint[] points)
{
ArgumentNullException.ThrowIfNull(points);
return points.Select(p => (p.X, p.Y)).ToList();
}
}