docs(spec): дизайн операции Оболочка (shell) — пакет B шаг 1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 22:20:02 +03:00
parent 04e8d2be52
commit b6ee39a914
@@ -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<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 и не пуст. Возврат `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.