From 5a689ad461434096bedc4dbf7f9d7a46dded9ace Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Tue, 26 May 2026 09:57:48 +0300 Subject: [PATCH] =?UTF-8?q?@=20feat(features):=20=D0=BE=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D1=80=D0=B0=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20(revolve=5Fboss=20/=20revolve=5Fcut)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PartModeler.RevolveAsync через API5 o3d_bossRotated/o3d_cutRotated: directionType=dtNormal, toroidShapeType=сфероид, SetThinParam(false), SetSideParam(forward, angle). Ось вращения — осевая линия в эскизе (системный стиль 3); добавляется sketch_add_axis. Новые MCP-инструменты: sketch_add_axis, revolve_boss, revolve_cut (угол в градусах, по умолчанию 360). Тест Revolve_rectangle_makes_tube: профиль [10..15]×[0..20] вокруг оси Y → труба (внеш. R15, внутр. R10, H20), объём π·2500 ≈ 7854 мм³ — проверено через get_part_info. Презентация: 23 инструмента, 16 тестов. Co-Authored-By: Claude Opus 4.7 @ --- docs/presentation.html | 14 ++--- src/Kompas.Mcp.Core/Modeling/PartModeler.cs | 58 +++++++++++++++++++ src/Kompas.Mcp.Host/Tools/FeatureTools.cs | 24 ++++++++ src/Kompas.Mcp.Host/Tools/SketchTools.cs | 9 +++ .../Integration/ModelingTests.cs | 28 +++++++++ 5 files changed, 126 insertions(+), 7 deletions(-) diff --git a/docs/presentation.html b/docs/presentation.html index 646ebb7..4e105ae 100644 --- a/docs/presentation.html +++ b/docs/presentation.html @@ -125,7 +125,7 @@
- Статус: v1 работает — цикл эскиз→выдавливание→снимок через MCP (20 инструментов) + Статус: v1 работает — цикл эскиз→выдавливание/вращение→снимок через MCP (23 инструмента)

КОМПАС-3D MCP-сервер
управление CAD языком LLM

MCP-сервер, который превращает операции КОМПАС-3D — создание документов, эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом — @@ -246,8 +246,8 @@ впереди.

-
Общий прогрессv1 + эскиз на грани · 15 тестов зелёные · доки и ревью
-
+
Общий прогрессv1 + эскиз на грани + вращение · 16 тестов зелёные · доки и ревью
+
@@ -282,12 +282,12 @@

Инструменты v1 ГОТОВО

-

20 инструментов, отдаются по MCP-протоколу

+

23 инструмента, отдаются по MCP-протоколу

  • System: connect · status · set_visible
  • Documents: create · open · save · save_as · close · active
  • -
  • Sketch: create · create_on_face 🆕 · add line/circle/rectangle · close
  • -
  • Features: extrude_boss · extrude_cut · rebuild
  • +
  • Sketch: create · create_on_face · add line/circle/rectangle/axis · close
  • +
  • Features: extrude_boss/cut · revolve_boss/cut 🆕 · rebuild
  • Vision: model_snapshot 👁️
  • Query: get_part_info (объём/масса/площадь/центр масс)
  • Query: select_face · list_bodies (далее)
  • @@ -319,7 +319,7 @@ черчению и сборкам.

    v1 · готово ✓

    Цикл моделирования

    Документы, эскизы, выдавливание/вырез, снимок модели — работает

    -
    v2 · в работе

    Операции и выборка

    Эскиз на грани ✓ · далее: вращение, скругление/фаска, массивы, список тел

    +
    v2 · в работе

    Операции и выборка

    Эскиз на грани ✓ · вращение ✓ · далее: скругление/фаска, массивы, список тел

    v3

    Параметрика

    Переменные IVariable7, свойства IPropertyMng, формулы

    v4

    2D-черчение

    Виды, размеры, тексты, штриховки; спецификации

    v5

    Сборки и экспорт

    Компоненты, сопряжения, конвертеры (STEP), HTTP/SSE-транспорт

    diff --git a/src/Kompas.Mcp.Core/Modeling/PartModeler.cs b/src/Kompas.Mcp.Core/Modeling/PartModeler.cs index 5a1e019..f208cba 100644 --- a/src/Kompas.Mcp.Core/Modeling/PartModeler.cs +++ b/src/Kompas.Mcp.Core/Modeling/PartModeler.cs @@ -15,6 +15,7 @@ namespace Kompas.Mcp.Core.Modeling; public sealed class PartModeler : IDisposable { private const int MainLineStyle = 1; // системный стиль «Основная» + private const int AxisLineStyle = 3; // системный стиль «Осевая» (ось вращения) private readonly KompasSession _session; private readonly KompasDispatcher _dispatcher; @@ -124,6 +125,15 @@ public sealed class PartModeler : IDisposable Seg(x1, y2, x1, y1); }, ct); + /// Добавить осевую линию (стиль «осевая») в эскиз — ось для операции вращения. + public Task AddAxisAsync(int sketchId, double x1, double y1, double x2, double y2, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + var editor = RequireOpenSketch(sketchId).Editor!; + if (editor.ksLineSeg(x1, y1, x2, y2, AxisLineStyle) == 0) + throw new InvalidOperationException("ksLineSeg (ось) вернул 0 (ось не создана)."); + }, ct); + /// Завершить редактирование эскиза. public Task CloseSketchAsync(int sketchId, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => CloseSketchCore(RequireSketch(sketchId)), ct); @@ -188,6 +198,54 @@ public sealed class PartModeler : IDisposable return id; }, ct); + /// + /// Вращать эскиз вокруг его осевой линии (добавьте её через AddAxis). — + /// вырезание вместо добавления материала. Угол — в градусах. + /// Возвращает id операции. + /// + public Task RevolveAsync( + int sketchId, double angle = 360, bool cut = false, bool forward = true, + CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + if (angle is <= 0 or > 360) + throw new ArgumentOutOfRangeException(nameof(angle), "Угол вращения должен быть в (0; 360]."); + var sketch = RequireSketch(sketchId); + CloseSketchCore(sketch); // эскиз должен быть закрыт перед операцией + + var part = GetTopPart(); + var type = cut ? Obj3dType.o3d_cutRotated : Obj3dType.o3d_bossRotated; + var entity = part.NewEntity((short)type) as ksEntity + ?? throw new InvalidOperationException("NewEntity операции вращения вернул null."); + + if (cut) + { + var def = entity.GetDefinition() as ksCutRotatedDefinition + ?? throw new InvalidOperationException("GetDefinition() выреза вращением вернул не ksCutRotatedDefinition."); + def.directionType = (short)Direction_Type.dtNormal; + def.SetThinParam(false, 0, 0, 0); + def.SetSideParam(forward, angle); + def.SetSketch(sketch.Entity); + } + else + { + var def = entity.GetDefinition() as ksBossRotatedDefinition + ?? throw new InvalidOperationException("GetDefinition() вращения вернул не ksBossRotatedDefinition."); + def.directionType = (short)Direction_Type.dtNormal; + def.toroidShapeType = false; // сфероид (сплошное тело), не тороид + def.SetThinParam(false, 0, 0, 0); + def.SetSideParam(forward, angle); + def.SetSketch(sketch.Entity); + } + + if (!entity.Create()) + throw new InvalidOperationException("Create() операции вращения вернул FALSE."); + + var id = _nextId++; + _features[id] = entity; + return id; + }, ct); + /// Перестроить документ. public Task RebuildAsync(CancellationToken ct = default) => _dispatcher.InvokeAsync(() => diff --git a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs index 6d6a27b..a1f94eb 100644 --- a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs +++ b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs @@ -31,6 +31,30 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler) return $"Вырез (cut) создан, id={id}."; } + [McpServerTool(Name = "revolve_boss")] + [Description("Вращать эскиз вокруг его осевой линии, добавив материал. Эскиз должен содержать профиль и осевую линию (sketch_add_axis). angle — угол в градусах (по умолчанию 360). Возвращает id операции.")] + public async Task RevolveBoss( + int sketchId, + [Description("Угол вращения, градусы (0..360]")] double angle = 360, + [Description("Прямое направление")] bool forward = true) + { + await session.ConnectAsync(); + var id = await modeler.RevolveAsync(sketchId, angle, cut: false, forward: forward); + return $"Вращение (boss) создано, id={id}."; + } + + [McpServerTool(Name = "revolve_cut")] + [Description("Вырезать вращением эскиза вокруг его осевой линии. Эскиз должен содержать профиль и осевую линию (sketch_add_axis). angle — угол в градусах (по умолчанию 360). Возвращает id операции.")] + public async Task RevolveCut( + int sketchId, + [Description("Угол вращения, градусы (0..360]")] double angle = 360, + [Description("Прямое направление")] bool forward = true) + { + await session.ConnectAsync(); + var id = await modeler.RevolveAsync(sketchId, angle, cut: true, forward: forward); + return $"Вырез вращением (cut) создан, id={id}."; + } + [McpServerTool(Name = "rebuild")] [Description("Перестроить активный документ.")] public async Task Rebuild() diff --git a/src/Kompas.Mcp.Host/Tools/SketchTools.cs b/src/Kompas.Mcp.Host/Tools/SketchTools.cs index 482ba5e..5895405 100644 --- a/src/Kompas.Mcp.Host/Tools/SketchTools.cs +++ b/src/Kompas.Mcp.Host/Tools/SketchTools.cs @@ -45,6 +45,15 @@ public sealed class SketchTools(KompasSession session, PartModeler modeler) return "Отрезок добавлен."; } + [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) diff --git a/tests/Kompas.Mcp.Tests/Integration/ModelingTests.cs b/tests/Kompas.Mcp.Tests/Integration/ModelingTests.cs index bafa373..15c611c 100644 --- a/tests/Kompas.Mcp.Tests/Integration/ModelingTests.cs +++ b/tests/Kompas.Mcp.Tests/Integration/ModelingTests.cs @@ -92,6 +92,34 @@ public sealed class ModelingTests } } + [Fact] + public async Task Revolve_rectangle_makes_tube() + { + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + // Профиль-прямоугольник [10..15]×[0..20] + осевая по локальной оси Y (x=0). + // Вращение 360° вокруг оси → труба: внеш. R15, внутр. R10, высота 20. + var s = await _modeler.OpenSketchAsync(BasePlane.XOY); + await _modeler.AddAxisAsync(s, 0, 0, 0, 20); + await _modeler.AddRectangleAsync(s, 10, 0, 15, 20); + await _modeler.CloseSketchAsync(s); + var rev = await _modeler.RevolveAsync(s, angle: 360); + Assert.True(rev > 0); + await _modeler.RebuildAsync(); + + var info = await _query.GetPartInfoAsync(); + + // Объём трубы = π·(15² − 10²)·20 = π·2500 ≈ 7853.98 мм³. + var expected = Math.PI * (15 * 15 - 10 * 10) * 20; + Assert.InRange(info.Volume, expected * 0.97, expected * 1.03); + } + finally + { + await _docs.CloseAsync(save: false); + } + } + [Fact] public async Task Reset_invalidates_sketch_ids() {