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:
2026-05-26 09:57:48 +03:00
parent 01f5f37c67
commit 5a689ad461
5 changed files with 126 additions and 7 deletions
@@ -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(() =>
+24
View File
@@ -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()
+9
View File
@@ -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)