docs: спек drawing_add_linear_dimension — линейный размер на виде (API7)

Веха 2D-ЧЕРТЁЖ, инкремент 3. Спайк подтвердил: (ISymbols2DContainer)view QI →
LineDimensions.Add → X1..Y3 (локальная СК вида) + Orientation + Update; авто-значение
((IDimensionText)dim).NominalValue == расстояние (проверено 40). Плюс: DrawingViewsResult
+= ViewNumbers (адресация видов, закрывает замечание Codex из ревью видов).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 17:49:18 +03:00
parent d6a129b0d1
commit ca3d91ae68
@@ -0,0 +1,104 @@
# Дизайн: линейный размер на виде чертежа (drawing_add_linear_dimension) через API7
**Дата:** 2026-05-27
**Статус:** спайк зелёный (механизм + авто-значение подтверждены); спроектировано; на ревью (Codex)
## Цель
Веха 2D-ЧЕРТЁЖ, инкремент 3 (размеры). После видов и основной надписи — **линейный размер** на
ассоциативном виде: между двумя точками (в локальной СК вида) с авто-измерением длины. Размеры —
самый ценный элемент оформления (несут размерную информацию). Начинаем с линейного
(горизонтальный/вертикальный/параллельный); диаметральные/радиальные/угловые — далее.
## Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (`DrawingDimensionSpikeTests`, зелёный) подтвердил путь API7:
```
IView view = первый вид с Number != 0; // системный вид — #0, пропускаем
ISymbols2DContainer symbols = (ISymbols2DContainer)view; // COM-QI ОТ ВИДА (не от документа!)
ILineDimension dim = symbols.LineDimensions.Add();
dim.X1=x1; dim.Y1=y1; dim.X2=x2; dim.Y2=y2; // выносные точки (ЛОКАЛЬНАЯ СК вида, мм)
dim.X3=x3; dim.Y3=y3; // положение размерной линии
dim.Orientation = ksLinDHorizontal; // 0=Parallel / 1=Horizontal / 2=Vertical
bool ok = dim.Update();
double value = ((IDimensionText)dim).NominalValue; // авто-измеренное значение (мм)
```
**Проверено (вид спереди коробки 40×30×20):** размер (0,0)-(40,0) горизонтальный →
`Update()=True`, `Valid=True`, `NominalValue == 40` (авто `|X2-X1|`), `view.ObjectCount` вырос.
**Подтверждённые факты:**
- **`ISymbols2DContainer` берётся COM-QI ОТ КОНКРЕТНОГО `IView`** — размер попадает именно в этот
вид (не нужен `Current=true`). (Аналогично 3D: `part``IModelContainer`.)
- **Координаты X1..Y3 — ЛОКАЛЬНАЯ СК вида** (= координаты модели на плоскости вида, при масштабе
1:1 совпадают с мм модели). НЕ координаты листа.
- **`AutoNominalValue=true` по умолчанию** → `NominalValue` = расстояние между точками
(горизонтальный: `|X2-X1|`, вертикальный: `|Y2-Y1|`, параллельный: реальная длина). Значение
читается `((IDimensionText)dim).NominalValue` (QI).
- `Orientation` (`ksLineDimensionOrientationEnum`): `ksLinDParallel=0`, `ksLinDHorizontal=1`,
`ksLinDVertical=2``Kompas6Constants`).
- `Update()` обязателен; перестроение документа не нужно.
## Адресация вида (часть инкремента)
Чтобы указать, НА КАКОМ виде ставить размер, нужен его `Number`. Сейчас
`drawing_create_standard_views` возвращает только `{Created, Total}`. **Расширяем `DrawingViewsResult`
полем `ViewNumbers` (номера созданных видов)** — уже собираются как `newViews` в
`CreateStandardViewsCore` (тривиально: `newViews.Select(v => v.Number)`). Это закрывает замечание
Codex из ревью видов (#10: «нет ссылок на созданные виды») и делает размеры адресуемыми.
## MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_add_linear_dimension` | `x1,y1,x2,y2` (выносные точки), `x3,y3` (положение размерной линии), `orientation="horizontal"`, `viewNumber=0` | Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) в ЛОКАЛЬНОЙ СК вида (мм). `orientation`: horizontal/vertical/parallel. `viewNumber`: номер вида (0 = первый ассоциативный/главный). Значение измеряется автоматически. Возвращает измеренное значение и номер вида. |
Контракт: активный документ — чертёж; на чертеже есть хотя бы один ассоциативный вид. Координаты —
локальные вида (см. выше). `drawing_create_standard_views` возвращает `ViewNumbers` для адресации.
## Архитектура
В существующем `DrawingService`. Свой enum `DimensionOrientation` (паттерн `MateType`/`StampField`).
- `src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs` — enum + static `DimensionOrientations`
(`Parse(string)`, `ToKompas``ksLineDimensionOrientationEnum`).
- `DrawingService.AddLinearDimensionAsync(viewNumber, x1,y1,x2,y2,x3,y3, DimensionOrientation, ct)`.
- `DrawingViewsResult` += `IReadOnlyList<int> ViewNumbers`.
- Инструмент `drawing_add_linear_dimension` в `DrawingTools` (orientation строкой → Parse).
## Валидация (чистые static, unit-тест)
- `DimensionOrientations.Parse` — horizontal/vertical/parallel (+рус.) → enum; иначе `ArgumentException`.
- `DimensionOrientations.ToKompas` — enum → `ksLineDimensionOrientationEnum` (0/1/2).
- `DrawingValidation.RequireFiniteCoords(params)` — координаты конечны (NaN/Infinity → ошибка).
## Реализация
`AddLinearDimensionCore`: валидация координат + orientation → `RequireActiveDrawing` → найти вид
(`viewNumber<=0` → первый с `Number!=0`; иначе по `Number`, ошибка если нет) → `(ISymbols2DContainer)view`
`LineDimensions.Add()` → задать X1..Y3 + Orientation → `Update()` (FALSE → откат `dim.Delete()` +
ошибка) → проверить `dim.Valid` (false → откат + ошибка) → вернуть `AddedDimension { Value =
((IDimensionText)dim).NominalValue, ViewNumber = view.Number }`. RCW точечно не освобождаем (v2-2).
## Тестирование
### Unit (`DimensionOrientationsTests`)
- `Parse` — horizontal/vertical/parallel и рус.-синонимы → enum; бросает на мусоре.
- `ToKompas` — каждый → 1/2/0.
- `RequireFiniteCoords` — бросает на NaN/Infinity; пропускает конечные.
### Integration (`DrawingDimensionTests`)
Паттерн: построить деталь → сохранить → создать чертёж → стандартные виды → размер → проверить →
`finally CloseAsync` (база `IntegrationTestBase`).
1. **Горизонтальный** (0,0)-(40,0) → `Value ≈ 40`; `view.ObjectCount` вырос.
2. **Вертикальный** (0,0)-(0,20) → `Value ≈ 20`.
3. **Адресация вида**: `CreateStandardViewsAsync` вернул `ViewNumbers` (3 шт); размер с конкретным
`viewNumber` ставится в этот вид.
4. **Нет видов**: чертёж без видов → понятная ошибка.
(Сценарий 1 = промотированный спайк; спайк-файл удаляется.)
## Дальнейшее (вне спека)
- Диаметральные/радиальные/угловые размеры; привязка к геометрии вида (ассоциативно, `BaseObject`);
допуски/обозначения; рамка/формат листа.