Files
kompas3d-mcp/docs/superpowers/specs/2026-05-26-shell-design.md
T
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

89 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Дизайн: пакет 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()==FALSE``InvalidOperationException` с пояснением вероятных причин (толщина больше
локального радиуса/толщины стенки; несовместимая топология).
- **Известное ограничение** (документируем, не обрабатываем): на телах со скруглениями/фасками и
на вогнутых/криволинейных гранях оболочка может не построиться (`Create()==FALSE`) даже при
формально корректных входных данных — это ограничение ядра КОМПАС, причину API не сообщает.
## Тестирование
**Integration** (`Category=Integration`, `KompasFixture`, в `SketchPrimitivesTests` или новом
`FeatureOpsTests`): коробка 40×30×20 (`AddRectangle``Extrude`) → `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.