feat: отверстие (hole) через API7 — IHoles3D/IHole3D + IHoleDisposal
- HoleService (Core/Modeling): простое цилиндрическое отверстие (ksHTBase), сквозное/глухое - размещение: BaseSurface=грань (FindObjectsByPoint) + AssociationVertex=Points3D.Add() (точка по мировым координатам ksPParamCoord+X/Y/Z); эскиз размещения не нужен - в API5 определения отверстия НЕТ (как draft) — реализовано на API7 (прецедент: move_face) - авто-направление в тело с повтором; DI-регистрация; инструмент hole в FeatureTools - интеграционные тесты: сквозное (Ø10 удаляет π·25·20) + глухое (Ø8×10); spike зелёный с первого раза - 103 теста зелёных; спек docs/superpowers/specs/2026-05-27-hole-design.md
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# Дизайн: отверстие (hole) через API7
|
||||
|
||||
**Дата:** 2026-05-27
|
||||
**Статус:** реализовано и проверено (spike зелёный); на ревью (ревьюер — Codex)
|
||||
|
||||
## Цель
|
||||
|
||||
Добавить параметрическую операцию **«Отверстие»** (простое цилиндрическое, ksHTBase) — сквозное
|
||||
или глухое, размещённое на грани по мировой точке. Приоритет 2 из плана.
|
||||
|
||||
## Ключевой факт: отверстие — только API7
|
||||
|
||||
В **API5 определения отверстия НЕТ** (по рефлексии `Kompas6API5.dll` нет ни одного типа с `Hole`;
|
||||
есть лишь `Obj3dType.o3d_holeOperation=52` без интерфейса — как было с draft). В **API7** —
|
||||
богатая поддержка: `IModelContainer.Holes3D` (`IHoles3D`) → `Add()` → `IHole3D`, размещение через
|
||||
`IHoleDisposal`. Прецедент API7 в слое моделирования уже есть — `move_face` (`FaceEditService`).
|
||||
Поэтому отдельный сервис `HoleService` (API7), а не метод `PartModeler` (API5).
|
||||
|
||||
## Подтверждённый workflow (spike зелёный)
|
||||
|
||||
```
|
||||
IPart7 top = (IPart7)doc3d.TopPart;
|
||||
IFace face = top.FindObjectsByPoint(x,y,z,true) → первый IFace; // как в move_face
|
||||
IModelContainer c = (IModelContainer)top; // COM-QI
|
||||
|
||||
// Точка размещения центра отверстия в МИРОВЫХ координатах:
|
||||
IPoint3D pt = c.Points3D.Add();
|
||||
pt.ParameterType = ksPParamCoord; pt.X=x; pt.Y=y; pt.Z=z; pt.Update();
|
||||
|
||||
IHole3D hole = c.Holes3D.Add();
|
||||
hole.HoleType = ksHTBase; // простое цилиндрическое
|
||||
hole.Diameter = diameter; // мм
|
||||
hole.DepthType = throughAll ? ksDTReachThrough : ksDTValue;
|
||||
if (!throughAll) hole.Depth = depth; // мм
|
||||
hole.EndFaceType = ksEFFlat; hole.Axis=false; hole.ShowThread=false;
|
||||
|
||||
IHoleDisposal d = (IHoleDisposal)hole; // QI
|
||||
d.BaseSurface = (IModelObject)face; // обязательна грань
|
||||
d.Perpendicular = true;
|
||||
d.AssociationVertex = (IModelObject)pt; // центр = созданная точка
|
||||
d.Direction = true; // в тело; при FALSE — пробуем обратное
|
||||
hole.Update(); // повтор с Direction=false при отказе
|
||||
doc3d.RebuildDocument();
|
||||
```
|
||||
|
||||
**Размещение** (главная неочевидность, решена): у `IHole3D` нет свойства позиции — оно в
|
||||
`IHoleDisposal` (`BaseSurface` = грань + `AssociationVertex` = точка/вершина). Самый надёжный
|
||||
способ задать центр по мировым координатам — создать `IPoint3D` (`ksPParamCoord` + X/Y/Z) и
|
||||
передать его как `AssociationVertex`. Эскиз размещения НЕ требуется (в отличие от `extrude_cut`).
|
||||
|
||||
Единицы: `Diameter`/`Depth`/`X`/`Y`/`Z` — мм. `EndFaceType=ksEFFlat` (плоский торец) для глухого.
|
||||
|
||||
## MCP-инструмент (группа Feature)
|
||||
|
||||
| Инструмент | Параметры | Поведение |
|
||||
|---|---|---|
|
||||
| `hole` | `x,y,z: double`, `diameter: double`, `depth=0`, `throughAll=false` | Просверлить цилиндрическое отверстие на грани, найденной по точке (x,y,z — центр). `throughAll` — сквозное (depth не нужен), иначе глухое на `depth`. Направление в тело — авто (как move_face). |
|
||||
|
||||
**Решения (YAGNI):** на первом этапе только `ksHTBase` (простое цилиндрическое); зенковка/
|
||||
цековка/коническое (`ksHTCounterbore/Countersinking/Conic` + параметрические подынтерфейсы) и
|
||||
резьба (`ShowThread`/`IThread`) — расширения позже. Глубина «до объекта» (`ksDTObject`) не
|
||||
выставляется. Отверстие не регистрируется в реестре `_features` (он для API5-операций) — массивы/
|
||||
зеркало пакета C к нему неприменимы; контракт инструмента возвращает подтверждение без id.
|
||||
|
||||
## Реализация
|
||||
|
||||
- **`src/Kompas.Mcp.Core/Modeling/HoleService.cs`** (новый): `HoleAsync(x,y,z,diameter,depth,throughAll,ct)`
|
||||
на STA-потоке; валидация `diameter>0`, `depth>0` (если не throughAll); `FindFaceAtPoint`
|
||||
(копия паттерна из `FaceEditService`); авто-направление с повтором.
|
||||
- **DI:** `AddSingleton<HoleService>()` в `Program.cs`.
|
||||
- **Инструмент** `hole` в `FeatureTools.cs` (конструктор получает `HoleService`).
|
||||
- Транзитные RCW не освобождаем точечно — консистентно (долг v2-2).
|
||||
|
||||
## Тестирование (Integration, `HoleTests`)
|
||||
|
||||
1. **Сквозное**: коробка 40×40×20 (центр 0,0; верх Z=20) → `hole(0,0,20, Ø10, throughAll)` →
|
||||
удалён цилиндр π·25·20 ≈ 1570.8 мм³; `InRange(before-after, ±5%)`. ✓
|
||||
2. **Глухое**: та же коробка → `hole(0,0,20, Ø8, depth=10)` → удалён π·16·10 ≈ 502.7 мм³;
|
||||
`InRange(before-after, ±10%)` (торец плоский, погрешность модели). ✓
|
||||
Reference in New Issue
Block a user