feat: зеркало операций и тела (mirror_operation, mirror_body)

- MirrorOperationAsync: ksMirrorCopyDefinition o3d_mirrorOperation, SetPlane + GetOperationArray
- MirrorBodyAsync: ksMirrorCopyAllDefinition o3d_mirrorAllOperation, SetPlane +
  ChooseBodies().ChooseBodiesType=ksAllBodies (отражает все тела, оригинал сохраняется)
- инструменты mirror_operation (featureIds, plane), mirror_body (plane)
- интеграционные тесты: mirror_operation на ДВУХ источниках (коллекция), mirror_body — оба ±5%
- пакет C завершён: 62 инструмента, 100 тестов (60 unit + 40 integration)
This commit is contained in:
2026-05-27 00:11:02 +03:00
parent be6a99b9b6
commit 2aeed5ba3d
3 changed files with 143 additions and 0 deletions
@@ -419,6 +419,67 @@ public sealed partial class PartModeler
return id;
}, ct);
/// <summary>Зеркальная копия операций featureIds относительно координатной плоскости plane.
/// Возвращает id операции.</summary>
public Task<int> MirrorOperationAsync(IReadOnlyList<int> featureIds, BasePlane plane, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
ArgumentNullException.ThrowIfNull(featureIds);
var ids = featureIds.Distinct().ToList();
if (ids.Count == 0) throw new ArgumentException("Нужна хотя бы одна операция для зеркала.", nameof(featureIds));
// Собираем источники до мутации модели (проверка существования всех id).
var features = ids.Select(RequireFeature).ToList();
var part = GetTopPart();
var planeEntity = part.GetDefaultEntity((short)BasePlanes.ToObj3dType(plane)) as ksEntity
?? throw new InvalidOperationException("Не удалось получить координатную плоскость.");
var entity = part.NewEntity((short)Obj3dType.o3d_mirrorOperation) as ksEntity
?? throw new InvalidOperationException("NewEntity(o3d_mirrorOperation) вернул null.");
var def = entity.GetDefinition() as ksMirrorCopyDefinition
?? throw new InvalidOperationException("GetDefinition() зеркала вернул не ksMirrorCopyDefinition.");
def.SetPlane(planeEntity);
var arr = def.GetOperationArray() as ksEntityCollection
?? throw new InvalidOperationException("GetOperationArray() зеркала вернул не ksEntityCollection.");
foreach (var f in features) arr.Add(f);
if (!entity.Create())
throw new InvalidOperationException(
"Create() зеркальной копии вернул FALSE (копия пересекает исходную геометрию?).");
var id = _nextId++;
_features[id] = entity;
return id;
}, ct);
/// <summary>Зеркально отразить всё тело относительно координатной плоскости plane (получить
/// симметричную деталь: оригинал + зеркало). Возвращает id операции.</summary>
public Task<int> MirrorBodyAsync(BasePlane plane, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var part = GetTopPart();
var planeEntity = part.GetDefaultEntity((short)BasePlanes.ToObj3dType(plane)) as ksEntity
?? throw new InvalidOperationException("Не удалось получить координатную плоскость.");
var entity = part.NewEntity((short)Obj3dType.o3d_mirrorAllOperation) as ksEntity
?? throw new InvalidOperationException("NewEntity(o3d_mirrorAllOperation) вернул null.");
var def = entity.GetDefinition() as ksMirrorCopyAllDefinition
?? throw new InvalidOperationException("GetDefinition() зеркала тела вернул не ksMirrorCopyAllDefinition.");
def.SetPlane(planeEntity);
// Отражаем все тела детали.
if (def.ChooseBodies() is ksChooseBodies chooser)
chooser.ChooseBodiesType = (int)ksChooseBodiesType.ksAllBodies;
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(() =>
+21
View File
@@ -163,6 +163,27 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler)
return $"Круговой массив создан ({count} экз. вокруг {axis}, шаг {step}°), id={id}.";
}
[McpServerTool(Name = "mirror_operation")]
[Description("Зеркальная копия операций: отразить ранее созданные операции featureIds относительно координатной плоскости plane (XOY|XOZ|YOZ). Добавляет зеркальные копии (оригинал сохраняется). Возвращает id операции.")]
public async Task<string> MirrorOperation(
[Description("Id операций-источников")] int[] featureIds,
[Description("Плоскость симметрии: XOY|XOZ|YOZ")] string plane)
{
await session.ConnectAsync();
var id = await modeler.MirrorOperationAsync(featureIds, BasePlanes.Parse(plane));
return $"Зеркальная копия операций создана (плоскость {plane}), id={id}.";
}
[McpServerTool(Name = "mirror_body")]
[Description("Зеркально отразить всё тело детали относительно координатной плоскости plane (XOY|XOZ|YOZ) — получить симметричную деталь (оригинал + зеркало). Удобно для моделирования половины с последующим отражением. Возвращает id операции.")]
public async Task<string> MirrorBody(
[Description("Плоскость симметрии: XOY|XOZ|YOZ")] string plane)
{
await session.ConnectAsync();
var id = await modeler.MirrorBodyAsync(BasePlanes.Parse(plane));
return $"Тело зеркально отражено (плоскость {plane}), id={id}.";
}
[McpServerTool(Name = "rebuild")]
[Description("Перестроить активный документ.")]
public async Task<string> Rebuild()