Files
kompas3d-mcp/docs/superpowers/specs/2026-05-27-draft-design.md
T
mikhail 205a4b02de feat: уклон граней (draft) через API5 — ksInclineDefinition o3d_incline=42
- ВОПРЕКИ прежнему предположению: уклон ЕСТЬ в API5 (операция Incline, не Draft) →
  стандартный паттерн PartModeler, регистрируется в _features (массивы/зеркало применимы)
- DraftAsync: FaceArray().Add(грани) + SetPlane(нейтральная коорд. плоскость) + angle + direction
- ЭМПИРИЧЕСКИ: direction=false=расширение, true=сужение (обратно справке) → маппинг !outward
- инструмент draft (faceIndices, neutralPlane, angle 0..90, outward); валидация угла и индексов
- интеграционный тест: 4 боковые грани коробки → усечённая пирамида (объём ±10%)
- 105 тестов; спек docs/superpowers/specs/2026-05-27-draft-design.md
2026-05-27 10:23:16 +03:00

68 lines
5.0 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.
# Дизайн: уклон граней (draft / incline) через API5
**Дата:** 2026-05-27
**Статус:** реализовано и проверено (тест зелёный); на ревью (ревьюер — Codex)
## Цель
Добавить операцию **«Уклон»** (draft) — наклонить грани на угол относительно нейтральной
(опорной) плоскости. Приоритет 1 текущей сессии.
## Ключевой факт: уклон ЕСТЬ в API5 (вопреки прежнему предположению)
Память `kompas-formative-features-api5` ошибочно считала draft недоступным в API5 — она искала
тип со словом **Draft**, но операция называется **«Уклон» = Incline**. Рефлексия подтвердила:
`Obj3dType.o3d_incline=42` + **`ksInclineDefinition`** есть в `Kompas6API5.dll`. Это стандартный
API5-паттерн (как shell/rib/fillet) — НЕ нужен API7. Плюс: операция регистрируется в реестре
`_features`, значит массивы/зеркало пакета C к ней применимы.
> `o3d_DraftFromEdges=644` (API7 `IDraftFromEdges`) — это ДРУГАЯ операция «уклон от базовой линии»
> (v22), без нейтральной плоскости. Для обычного уклона граней используем `IIncline`/`ksInclineDefinition`.
## Сигнатуры (рефлексия interop + справка)
```
Obj3dType.o3d_incline = 42
ksInclineDefinition:
double angle // угол уклона (градусы)
bool direction // ЭМПИРИЧЕСКИ: false=расширение, true=сужение (обратно справке!)
object FaceArray() // ksEntityCollection уклоняемых граней (Add по одной)
bool SetPlane(object) // нейтральная (опорная) плоскость — ksEntity
object GetPlane()
```
**Паттерн:** `NewEntity(o3d_incline)``GetDefinition()``FaceArray().Add(face)` (по граням) →
`SetPlane(нейтральная)``angle``direction``Create()` → регистрация в `_features`.
**Нейтральная плоскость** — сечение тела в ней остаётся неизменным; грани уклоняются вокруг
линии пересечения с ней. На первом этапе — базовая координатная плоскость (XOY/XOZ/YOZ).
**Направление (важно):** в interop `direction=false` даёт **расширение**, `true`**сужение**
(проверено объёмом; обратно тексту справки). Контракт инструмента: `outward` (true=расширение),
маппинг `def.direction = !outward`.
## MCP-инструмент (группа Feature)
| Инструмент | Параметры | Поведение |
|---|---|---|
| `draft` | `faceIndices: int[]`, `neutralPlane: "XOY"\|"XOZ"\|"YOZ"`, `angle: double`, `outward=false` | Уклон граней `faceIndices` (из list_faces) на `angle`° относительно нейтральной координатной плоскости. `outward` — расширение/сужение. Возвращает id. |
**Решения (YAGNI):** нейтральная плоскость — только координатная (как mirror_operation); уклон от
ребра (`IDraftFromEdges`) — отдельная задача позже. Грани — по стабильному индексу из list_faces.
## Реализация
- **Метод** `DraftAsync(IReadOnlyList<int> faceIndices, BasePlane neutralPlane, double angle, bool outward, ct)`
в `PartModeler.Features.cs`. Валидация: `angle` конечен и в (0; 90); `faceIndices` не пуст,
`Distinct`; диапазон всех индексов проверяется до мутации (`SelectFaceByIndex`).
- **Инструмент** `draft` в `FeatureTools.cs` (`neutralPlane` парсится `BasePlanes.Parse`).
- Транзитные RCW не освобождаем точечно — консистентно (долг v2-2).
- `Create()==FALSE``InvalidOperationException` (грань не граничит с нейтральной плоскостью?).
## Тестирование (Integration, `FeatureOpsTests`)
**Уклон**: коробка 40×40×20 (низ на Z=0) → 4 боковые грани (площадь 800) под уклон 10° внутрь
(`outward=false`) относительно XOY → усечённая пирамида: верх ≈ 402·20·tan10°=32.95;
`V = (20/3)(1600+1085.5+√(1600·1085.5)) ≈ 26689` мм³; `InRange(after, ±10%)` и `after < before`. ✓
Боковые грани выбираются из `ListFaces` по площади 800 (тип plane).