docs: спек drawing_create_standard_views — стандартные виды чертежа (API7)

Веха 2 (2D-ЧЕРТЁЖ), инкремент 1. Механизм подтверждён спайком: doc2d.
ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", {1,3,5}, ...);
object[]→SAFEARRAY VT_I4 ОК, 3 вида создаются (Count 1→4). Новый DrawingService.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 14:09:43 +03:00
parent cb1984f227
commit 46648f7635
@@ -0,0 +1,101 @@
# Дизайн: стандартные виды чертежа (drawing_create_standard_views) через API7
**Дата:** 2026-05-27
**Статус:** спайк зелёный (механизм подтверждён вживую); спроектировано; на ревью (Codex)
## Цель
Веха 2 — **2D-ЧЕРТЁЖ** (новый класс функционала после деталей и сборок). Инкремент 1: создать на
активном чертеже **стандартные ассоциативные виды** (спереди/сверху/слева) по сохранённой 3D-модели
`.m3d`/`.a3d`. Это фундамент чертежа — без видов лист пуст. Чертёж проверяется не объёмом, а
**числом созданных видов**.
## Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (`DrawingSpikeTests`, зелёный) подтвердил путь API7:
```
IKompasDocument2D doc2d = (IKompasDocument2D)app.ActiveDocument; // активный чертёж
IViews views = doc2d.ViewsAndLayersManager.Views; // COM-свойства (не QI)
object[] projTypes = { 1, 3, 5 }; // ksPtFront=1, ksPtUp=3, ksPtLeft=5 (SAFEARRAY VT_I4)
bool ok = views.AddStandartViews(path, "#Спереди", projTypes, X:100, Y:150, Scale:1.0, DX:20, DY:20);
int count = views.Count;
```
**Проверенные факты:**
- `object[] { 1, 3, 5 }` **маршалится в SAFEARRAY VT_I4 без проблем** (риск из ревью SDK снят).
- Новый чертёж стартует с `Views.Count == 1` (системный вид #0). После `AddStandartViews` с тремя
кодами проекций `Count == 4`**создано ровно 3 вида** (спереди/сверху/слева). `ok == True`.
- `ProjectionName = "#Спереди"` — ориентация главного вида (стандартное имя проекции 3D-модели).
- `IKompasDocument2D` напрямую (без QI к `IKompasDocument2D1`) даёт `ViewsAndLayersManager.Views`
(подтверждено рефлексией interop — отличие от ревью SDK).
- Явный rebuild/save после `AddStandartViews` НЕ нужен — виды уже в документе (`Count` сразу 4).
**Параметры `AddStandartViews(FileName, ProjectionName, ProjectionsTypes, X, Y, Scale, DX, DY)`:**
- `FileName` — путь к `.m3d`/`.a3d`.
- `ProjectionName` — ориентация главного вида (`"#Спереди"`).
- `ProjectionsTypes``SAFEARRAY VT_I4` кодов `ksRelativeProjectionTypeEnum`: `ksPtFront=1`,
`ksPtRear=2`, `ksPtUp=3`, `ksPtDown=4`, `ksPtLeft=5`, `ksPtRight=6`, `ksPtIsoXYZ=7`.
- `X, Y` — точка привязки главного вида на листе, мм.
- `Scale` — масштаб (1.0 = 1:1).
- `DX, DY` — зазоры между видами, мм. Возврат: `bool` (успех).
## Архитектура
Новый класс (как сборки — отдельный от `PartModeler`). Сервис `DrawingService` (API7), инструменты
`DrawingTools`, namespace `Kompas.Mcp.Core.Drawings`.
- `src/Kompas.Mcp.Core/Drawings/DrawingService.cs` — сервис.
- `src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs` — чистые static-валидаторы (unit).
- `src/Kompas.Mcp.Host/Tools/DrawingTools.cs` — MCP-инструмент.
- DI: `AddSingleton<DrawingService>()` в `Program.cs`.
## MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_create_standard_views` | `partFilePath: string`, `scale=1.0` | На активном чертеже создать стандартные ассоциативные виды (спереди/сверху/слева) сохранённой детали/сборки. Возвращает число созданных видов. |
Контракт: активный документ — чертёж (`ksDocumentDrawing`); файл модели существует. Набор видов
фиксирован (спереди+сверху+слева, 3 вида); позиция/зазоры — дефолты (X=100, Y=150, DX=20, DY=20 мм).
Выбор набора видов / позиции / изометрии — будущие инкременты (YAGNI).
## Валидация (чистые static, unit-тест)
`DrawingValidation`:
- `NormalizeModelPath(string? path)``Trim` → непустота → расширение `.m3d`/`.a3d``Path.GetFullPath`.
- `RequireScale(double scale)` — конечный и `> 0`.
`File.Exists` — в сервисе (`FileNotFoundException` с абсолютным путём).
## Проверка активного документа
`RequireActiveDrawing()` — раздельные ошибки: нет документа / тип != `ksDocumentDrawing` / не
приводится к `IKompasDocument2D`.
## Реализация
`CreateStandardViewsAsync(partFilePath, scale, ct)` — на STA-потоке: валидация (`NormalizeModelPath`,
`RequireScale`) → `File.Exists``RequireActiveDrawing``views.Count` (before) →
`AddStandartViews(path, "#Спереди", {1,3,5}, 100, 150, scale, 20, 20)` (результат проверяем: FALSE →
ошибка) → `created = views.Count - before` (ожидаем 3; если 0 — ошибка «виды не созданы»). Возврат —
число созданных видов. Транзитные RCW не освобождаем точечно (долг v2-2).
## Тестирование
### Unit (`DrawingValidationTests`)
- `NormalizeModelPath` — бросает на пустом/чужом расширении (`.step`); принимает `.m3d`/`.a3d`
(регистр, пробелы), возвращает абсолютный путь.
- `RequireScale` — бросает на 0/отрицательном/NaN/Infinity; принимает положительный.
### Integration (`DrawingTests`)
Паттерн: построить деталь 40×30×20 → `SaveAsAsync(.m3d)` → проверить файл → `CloseAsync` → создать
чертёж → `CreateStandardViewsAsync` → проверить → `finally CloseAsync` (база `IntegrationTestBase`).
1. **Создание видов**: `AddStandartViews` → возврат `== 3`; `Views.Count` вырос на 3 (с 1 до 4).
2. **Не-чертёж**: активный документ — деталь → `CreateStandardViewsAsync` бросает (понятная ошибка).
(Сценарий 1 = промотированный спайк; спайк-файл удаляется.)
## Дальнейшее (вне спека)
- Выбор набора видов (+изометрия, +сечения), позиция/масштаб листа, размеры/обозначения на видах,
рамка/основная надпись.