diff --git a/docs/superpowers/specs/2026-05-26-shell-design.md b/docs/superpowers/specs/2026-05-26-shell-design.md new file mode 100644 index 0000000..a0e32e9 --- /dev/null +++ b/docs/superpowers/specs/2026-05-26-shell-design.md @@ -0,0 +1,80 @@ +# Дизайн: пакет 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 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 и не пуст. Возврат `Create()==FALSE` → `InvalidOperationException` + с понятным текстом (толщина велика для геометрии?). + +## Тестирование + +**Integration** (`Category=Integration`, `KompasFixture`, в `SketchPrimitivesTests` или новом +`FeatureOpsTests`): коробка 40×30×20 (`AddRectangle` → `Extrude`) → `list_faces` → найти +верхнюю грань (нормаль +Z или по площади) → `ShellAsync([верхняя], thickness=2, outward=false)` +→ объём существенно уменьшается (тело стало полым с открытым верхом) и остаётся > 0. +Проверка: `after < before` и `after ∈ (before·0.2, before·0.5)` (стенки 2 мм у коробки +40×30×20 дают ≈30% исходного объёма). + +Unit отдельный не вводим: маппинг `thinType=!outward` тривиален и инлайнится; валидация +параметров — в стиле существующих операций (`extrude`/`revolve` тоже покрыты интеграционно). + +## Влияние на документацию + +После реализации (через `docs-delegate`): счётчик инструментов +1 (→51), тестов +1; +добавить `shell` в описание формообразующих в `CLAUDE.md`/`README.md`/`docs/ARCHITECTURE.md`/ +`docs/presentation.html`; отметить начало пакета B.