Files
mikhail 115fcb8759 feat(feature): операция Оболочка (shell)
ShellAsync (o3d_shellOperation): удаление граней по индексам + толщина стенки,
thinType=!outward. SelectFaceByIndex в ядре, инструмент shell в FeatureTools.
Валидация: thickness>0, дедупликация индексов, проверка диапазона до мутации.
Integration-тест: коробка 40x30x20 → оболочка 2мм → 7152 мм³ (±5%).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 22:28:03 +03:00

6.7 KiB
Raw Permalink Blame History

Дизайн: пакет B (шаг 1) — операция «Оболочка» (shell)

Дата: 2026-05-26 Статус: на ревью (автономный режим; ревьюер — Codex) Контекст: kompas3d-mcp — MCP-сервер для КОМПАС-3D через COM API (.NET 8, C#).

Цель

Добавить формообразующую операцию «Оболочка» (придать стенкам толщину, удалив выбранные грани) — первый шаг пакета B «формообразующие». Самая востребованная операция этого пакета; опирается на уже готовый выбор грани по индексу из list_faces, без мульти-эскизной оркестрации (нужной для loft/sweep). Один MCP-инструмент shell.

Не входит в объём (YAGNI)

  • Остальные операции пакета B (rib, draft, loft, sweep, hole) — отдельные шаги.
  • Переменная толщина по граням (ksShellDefinition задаёт одну общую thickness).
  • Закрытая оболочка без удаляемых граней — КОМПАС требует ≥1 удаляемую грань.

Сигнатуры COM (подтверждены: рефлексия interop + SDK-база + пример Step3d1.cs)

Obj3dType.o3d_shellOperation = 43
ksShellDefinition:
    double  thickness         // толщина стенки, мм
    bool    thinType          // true = внутрь (габарит сохраняется), false = наружу
    object  FaceArray()       // ksEntityCollection удаляемых (открываемых) граней; нужна >=1

Паттерн (из Samples/CSharp.zip → Step3d1.cs):

ksEntity ent = part.NewEntity(o3d_shellOperation)
ksShellDefinition def = ent.GetDefinition()
ksEntityCollection faces = def.FaceArray()
faces.Add(<грань>)                 // одна или несколько удаляемых граней
def.thickness = t; def.thinType = !outward
ent.Create()

Порядок: сначала заполнить FaceArray, затем thickness/thinType, затем Create().

Новый MCP-инструмент (группа Feature)

Инструмент Параметры Поведение
shell faceIndices: int[], thickness: double, outward: bool = false Превратить тело в оболочку толщиной thickness мм, удалив (открыв) грани с указанными индексами из list_faces. outward=false — толщина внутрь (габарит сохраняется), true — наружу.

Маппинг: thinType = !outward.

Выбор граней: по стабильному индексу из list_faces (как sketch_create_on_face_index, fillet_edge_index). Индексы валидны, пока геометрия не менялась — берутся из свежего list_faces. Несколько граней → несколько FaceArray.Add.

Реализация

  • Метод модели ShellAsync(IReadOnlyList<int> faceIndices, double thickness, bool outward, CancellationToken) в PartModeler.Features.cs (формообразующая). Возвращает id операции (как extrude/revolve).
  • Helper SelectFaceByIndex(ksPart, int) в ядре PartModeler.cs — аналог существующего SelectEdgeByIndex (диапазон-проверка + EntityCollection(o3d_face).GetByIndex).
  • Инструмент shell в FeatureTools.cs (как extrude_boss/revolve_boss).
  • Валидация (inline, как ExtrudeAsync/RevolveAsync): thickness > 0; faceIndices не null и не пуст; каждый индекс проверяется на диапазон до мутации модели (SelectFaceByIndex бросает ArgumentOutOfRangeException, как SelectEdgeByIndex); дубли индексов отбрасываются (Distinct), чтобы не добавлять грань в FaceArray дважды; FaceArray() проверяется на null (as ksEntityCollection ?? throw). Возврат Create()==FALSEInvalidOperationException с пояснением вероятных причин (толщина больше локального радиуса/толщины стенки; несовместимая топология).
  • Известное ограничение (документируем, не обрабатываем): на телах со скруглениями/фасками и на вогнутых/криволинейных гранях оболочка может не построиться (Create()==FALSE) даже при формально корректных входных данных — это ограничение ядра КОМПАС, причину API не сообщает.

Тестирование

Integration (Category=Integration, KompasFixture, в SketchPrimitivesTests или новом FeatureOpsTests): коробка 40×30×20 (AddRectangleExtrude) → list_faces → найти верхнюю грань (нормаль +Z или по площади) → ShellAsync([верхняя], thickness=2, outward=false) → объём существенно уменьшается (тело стало полым с открытым верхом) и остаётся > 0. Точная проверка: оболочка 2 мм с открытым верхом = 24000 36×26×18 = 24000 16848 = 7152 мм³. Утверждение: after < before и after ∈ (7152·0.95, 7152·1.05) (≈6794…7510 мм³). Если КОМПАС считает полость иначе — скорректировать ожидание по фактическому замеру, сохранив узкий допуск.

Unit отдельный не вводим: маппинг thinType=!outward тривиален и инлайнится; валидация параметров — в стиле существующих операций (extrude/revolve тоже покрыты интеграционно).

Влияние на документацию

После реализации (через docs-delegate): счётчик инструментов +1 (→51), тестов +1; добавить shell в описание формообразующих в CLAUDE.md/README.md/docs/ARCHITECTURE.md/ docs/presentation.html; отметить начало пакета B.