@
feat(features): операция вращения (revolve_boss / revolve_cut) 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 <noreply@anthropic.com> @
This commit is contained in:
@@ -125,7 +125,7 @@
|
||||
<!-- HERO -->
|
||||
<header class="hero">
|
||||
<div class="wrap">
|
||||
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v1 работает — цикл эскиз→выдавливание→снимок через MCP (20 инструментов)</span>
|
||||
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v1 работает — цикл эскиз→выдавливание/вращение→снимок через MCP (23 инструмента)</span>
|
||||
<h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1>
|
||||
<p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов,
|
||||
эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом —
|
||||
@@ -246,8 +246,8 @@
|
||||
впереди.</p>
|
||||
|
||||
<div class="prog">
|
||||
<div class="progrow"><span>Общий прогресс</span><span>v1 + эскиз на грани · 15 тестов зелёные · доки и ревью</span></div>
|
||||
<div class="bar"><i style="width:76%"></i></div>
|
||||
<div class="progrow"><span>Общий прогресс</span><span>v1 + эскиз на грани + вращение · 16 тестов зелёные · доки и ревью</span></div>
|
||||
<div class="bar"><i style="width:78%"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="todo-grid" style="margin-top:26px">
|
||||
@@ -282,12 +282,12 @@
|
||||
<!-- TOOLS -->
|
||||
<div class="panel">
|
||||
<h3>Инструменты v1 <span class="tag done">ГОТОВО</span></h3>
|
||||
<p class="meta">20 инструментов, отдаются по MCP-протоколу</p>
|
||||
<p class="meta">23 инструмента, отдаются по MCP-протоколу</p>
|
||||
<ul class="list">
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>System</code>: connect · status · set_visible</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Documents</code>: create · open · save · save_as · close · active</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Sketch</code>: create · create_on_face 🆕 · add line/circle/rectangle · close</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Features</code>: extrude_boss · extrude_cut · rebuild</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Sketch</code>: create · create_on_face · add line/circle/rectangle/axis · close</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Features</code>: extrude_boss/cut · revolve_boss/cut 🆕 · rebuild</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Vision</code>: model_snapshot 👁️</span></li>
|
||||
<li class="muted"><span class="mark ok">✓</span><span class="t"><code>Query</code>: get_part_info (объём/масса/площадь/центр масс)</span></li>
|
||||
<li><span class="mark todo"></span><span><code>Query</code>: select_face · list_bodies (далее)</span></li>
|
||||
@@ -319,7 +319,7 @@
|
||||
черчению и сборкам.</p>
|
||||
<div class="timeline">
|
||||
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">v1 · готово ✓</div><h4>Цикл моделирования</h4><p>Документы, эскизы, выдавливание/вырез, снимок модели — работает</p></div>
|
||||
<div class="tl cur"><div class="ph">v2 · в работе</div><h4>Операции и выборка</h4><p>Эскиз на грани ✓ · далее: вращение, скругление/фаска, массивы, список тел</p></div>
|
||||
<div class="tl cur"><div class="ph">v2 · в работе</div><h4>Операции и выборка</h4><p>Эскиз на грани ✓ · вращение ✓ · далее: скругление/фаска, массивы, список тел</p></div>
|
||||
<div class="tl"><div class="ph">v3</div><h4>Параметрика</h4><p>Переменные <code>IVariable7</code>, свойства <code>IPropertyMng</code>, формулы</p></div>
|
||||
<div class="tl"><div class="ph">v4</div><h4>2D-черчение</h4><p>Виды, размеры, тексты, штриховки; спецификации</p></div>
|
||||
<div class="tl"><div class="ph">v5</div><h4>Сборки и экспорт</h4><p>Компоненты, сопряжения, конвертеры (STEP), HTTP/SSE-транспорт</p></div>
|
||||
|
||||
@@ -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);
|
||||
|
||||
/// <summary>Добавить осевую линию (стиль «осевая») в эскиз — ось для операции вращения.</summary>
|
||||
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);
|
||||
|
||||
/// <summary>Завершить редактирование эскиза.</summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Вращать эскиз вокруг его осевой линии (добавьте её через AddAxis). <paramref name="cut"/> —
|
||||
/// вырезание вместо добавления материала. Угол <paramref name="angle"/> — в градусах.
|
||||
/// Возвращает id операции.
|
||||
/// </summary>
|
||||
public Task<int> 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);
|
||||
|
||||
/// <summary>Перестроить документ.</summary>
|
||||
public Task RebuildAsync(CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
|
||||
@@ -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<string> 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<string> 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<string> Rebuild()
|
||||
|
||||
@@ -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<string> 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<string> AddRectangle(int sketchId, double x1, double y1, double x2, double y2)
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user