Files
kompas3d-mcp/docs/superpowers/specs/2026-05-27-drawing-text-annotations-design.md
T
mikhail 5272066bef feat(drawing): текстовые обозначения — шероховатость, текст, тех. требования
drawing_add_rough (знак шероховатости: ISymbols2DContainer.Roughs + IRoughParams,
SignType ksRoughSignEnum, значение Ra/Rz через RoughParamText.Str),
drawing_add_text (свободная надпись: IDrawingContainer.DrawingTexts + IText.Str),
drawing_set_technical_requirements (блок тех. требований уровня документа:
IDrawingDocument.TechnicalDemand.Text).

- DrawingService: AddRoughAsync/AddTextAsync/SetTechnicalRequirementsAsync +
  ReadTechnicalRequirementsAsync + счётчики; RequireDrawingContainer/RequireTechnicalDemand
- RoughSignType (enum + Parse/ToKompas), DrawingAnnotationResult (Value read-back из COM)
- RequireNonEmptyText в DrawingValidation
- 13 unit + 13 интеграционных тестов (всего 267 зелёных), сборка Release чистая

Спайк подтвердил: значение шероховатости через IRoughParams.RoughParamText.Str (round-trip);
текст НЕ в IView.ObjectCount (счёт по DrawingTexts.Count); тех. требования IsCreated False→True
при первом Text.Str+Update, многострочно через \n. Ревью Codex спека учтено (height убран —
это высота блока, не шрифт; guards; read-back возврат). Спек — docs/superpowers/specs/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:13:11 +03:00

168 lines
14 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.
# Дизайн: текстовые обозначения чертежа — шероховатость, текст, тех. требования (API7)
**Дата:** 2026-05-27
**Статус:** дизайн согласован (все три в одном инкременте), спайк проведён, ревью Codex спека учтено — к реализации
## Правки по ревью Codex (спек)
- **#4** параметр `height` у `drawing_add_text` **убран:** `IDrawingText.Height` — высота блока
форматирования, НЕ размер шрифта (шрифт задаётся на уровне `ITextItem`, вне объёма). Текст ставится
стилем по умолчанию. (Снимает и #10 — валидация height не нужна.)
- **#3/#11** сервис возвращает значение, **прочитанное обратно из COM** после `Update` (rough —
`RoughParamText.Str`; text — `((IText)dt).Str`) — как `NominalValue` у размеров. Тест `Value==вход`
доказывает round-trip. Пост-Add откат `Delete` есть в коде; не форсируется тестом (для валидных
параметров COM всегда `Valid` — как решено у диаметрального; путь идентичен протестированному угловому).
- **#1** `value` шероховатости нормализуется (`null`/whitespace → `""`); возвращается нормализованное
(read-back), контракт `Value` непустой не нарушается.
- **#2/#5/#7** добавлены null/QI-guards: `Roughs`/`(IRoughParams)`/`RoughParamText`;
`(IDrawingContainer)`/`DrawingTexts`/`(IText)`; `(IDrawingDocument)`/`TechnicalDemand`/`Text`
каждый с раздельной диагностикой (как `RequireStampCell`/`RequireSymbols2DContainer`).
- **#6** тесты текста дополнены: многострочный round-trip (`\n`), несуществующий `viewNumber>0`.
- **#8** добавлен helper `ReadTechnicalRequirementsAsync` — тест читает `td.Text.Str` и проверяет
перезапись (как `DrawingStampTests` перечитывают графы).
- **#9** подсчёт строк тех. требований нормализован: `text.Replace("\r\n","\n")`, хвостовые пустые
строки отбрасываются (`"a\n"` → 1 строка).
## Цель
Веха 2D-ЧЕРТЁЖ, инкремент 6. После завершения базового семейства размеров — **текстовые обозначения**:
знак шероховатости, свободная текстовая надпись, технические требования. Три инструмента покрывают
самые частые «не-размерные» элементы конструкторского чертежа.
## Спайк: все три механизма подтверждены вживую (НЕ разучивать)
Спайк (`_SpikeAnnotations`, прогнан на реальном КОМПАС v24, затем удалён) на коробке 40×30×20 с
тремя стандартными видами.
### Шероховатость — на виде (`ISymbols2DContainer.Roughs`)
```
IRough rough = symbols.Roughs.Add(); // без параметров
rough.BranchX0 = 20; rough.BranchY0 = 25; // положение знака (ЛОКАЛЬНАЯ СК вида, мм)
rough.Angle = 0; // угол наклона оси знака (градусы)
IRoughParams rp = (IRoughParams)rough; // QI (как IDimensionText у размеров)
rp.SignType = ksRoughSignEnum.ksDeleteMaterial; // тип знака
rp.RoughParamText.Str = "Ra 1.6"; // значение (Ra/Rz) — текст
rough.Update(); // True, rough.Valid == True
```
**Проверено:** `Update=True`, `Valid=True`, `RoughParamText.Str` round-trip = `"Ra 1.6"`, `Roughs.Count`
→ 1. `ksRoughSignEnum`: `ksNoProcessingType=0` (без указания обработки), `ksDeleteMaterial=1` (с
удалением слоя материала), `ksWithoutDeleteMaterial=2` (без удаления). Положение — свободные координаты
(`BranchX0/Y0`); `BaseObject` (привязка к контуру) не задаём — будущее.
### Свободный текст — на виде (`IDrawingContainer.DrawingTexts`, НЕ Symbols!)
```
IDrawingContainer dc = (IDrawingContainer)view; // ВНИМАНИЕ: текст в контейнере геометрии,
IDrawingText dt = dc.DrawingTexts.Add(); // а НЕ в ISymbols2DContainer
dt.X = 30; dt.Y = 45; dt.Angle = 0; // точка привязки (ЛОКАЛЬНАЯ СК вида, мм)
((IText)dt).Str = "Образец надписи"; // содержимое (QI к IText), \n — многострочно
dt.Update(); // True, dt.Valid == True
```
**Проверено:** `Update=True`, `Valid=True`, `Str` round-trip, `DrawingTexts.Count`**2** (вид уже
содержал 1 текст — авто-подпись вида; проверять по ДЕЛЬТЕ before+1, не по абсолюту). `ObjectCount`
4→4 — текст **НЕ** входит в `IView.ObjectCount` (как и размеры) → проверять `DrawingTexts.Count`.
### Технические требования — на уровне ДОКУМЕНТА (`IDrawingDocument.TechnicalDemand`)
```
IDrawingDocument dd = (IDrawingDocument)doc; // QI от активного IKompasDocument2D
ITechnicalDemand td = dd.TechnicalDemand; // единый блок на документ
td.Text.Str = "1. Общие допуски по ГОСТ 30893.1.\n2. Острые кромки притупить."; // \n — строки
td.Update(); // True
```
**Проверено:** `IsCreated` False→True (первый `Text.Str`+`Update()` создаёт блок), `Update=True`, текст
с `\n` сохранён построчно. Объект уровня документа (не вида), единственный, над основной надписью.
`Str` замещает содержимое (как у штампа) → инструмент `set` (перезапись).
## MCP-инструменты
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_add_rough` | `x,y` (положение знака), `value=""` (Ra/Rz, напр. "Ra 1.6"), `signType="delete"` (delete\|without\|none), `angle=0` (°), `viewNumber=0` | Поставить знак шероховатости на виде. Положение `x,y` в ЛСК вида (мм). `value` — текст параметра (пусто = знак без значения). Возвращает значение и номер вида. |
| `drawing_add_text` | `x,y` (точка привязки), `text`, `angle=0` (°), `viewNumber=0` | Поставить свободную текстовую надпись на виде стилем по умолчанию. `text` — содержимое (`\n` — многострочно). Возвращает текст (read-back) и номер вида. |
| `drawing_set_technical_requirements` | `text` | Задать технические требования активного чертежа (единый блок над штампом; перезаписывает прежние). `text` — строки через `\n`. Возвращает число строк. |
## Архитектура
В существующем `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`). Шероховатость/текст —
per-view; тех. требования — per-document.
- `AddRoughAsync(viewNumber, x, y, value, signType, angleDeg, ct)``DrawingAnnotationResult`.
Введём `record DrawingAnnotationResult { string Value; int ViewNumber }` (универсальный для rough/text —
`Value`=строка, прочитанная обратно из COM; `ViewNumber`). Тех. требования возвращают число строк (int).
- `AddTextAsync(viewNumber, x, y, text, angleDeg, ct)``DrawingAnnotationResult`.
- `SetTechnicalRequirementsAsync(text, ct)``int` (число строк).
- `ReadTechnicalRequirementsAsync(ct)``string` (для теста — `td.Text.Str`).
- Helpers: `RequireSymbols2DContainer` (есть, для rough), новый `RequireDrawingContainer(viewNumber)`
= guarded `(IDrawingContainer)FindView(...)` (для text), новый `RequireDrawingDocument()` =
guarded `(IDrawingDocument)RequireActiveDrawing()` (для тех. требований). Все QI с раздельной диагностикой.
- Счётчики для тестов: `GetViewRoughCountAsync`, `GetViewTextCountAsync`.
- Новый enum-файл `RoughSignType.cs`: `enum RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial}`
+ `RoughSignTypes.Parse(string)`/`ToKompas(...)` (→ `ksRoughSignEnum`), по образцу `AngleDimensionTypes`.
- Новый файл `DrawingAnnotationResult.cs`.
- Инструменты в `DrawingTools.cs`.
## Валидация (чистые static, unit-тест)
- `RequireFiniteCoords` (есть) — координаты rough/text.
- Новый `RequireNonEmptyText(string, paramName)` — для `text` (drawing_add_text, тех. требования).
`value` шероховатости НЕ обязателен (знак без значения допустим; `null`/whitespace → `""`).
- `RoughSignTypes.Parse` — разбор строки (unit-тест; `ToKompas``ksRoughSignEnum` НЕ покрываем unit —
граница проекта, как у `AngleDimensionTypes`/`DimensionOrientations`).
## Реализация
**`AddRoughCore`**: нормализация `value` (`null`/whitespace → `""`) → `RequireFiniteCoords(x,y,angleDeg)`
`RequireSymbols2DContainer``symbols.Roughs` (null-check) → `.Add()` (null-check) → `BranchX0/Y0/Angle`
`(IRoughParams)rough` (null-check QI) → `SignType=ToKompas`, `rp.RoughParamText` (null-check) `.Str = value`
`Update()` (FALSE → откат `Delete`) → `Valid` (false → откат) → читаем `rp.RoughParamText.Str` обратно
`DrawingAnnotationResult{Value=read-back, ViewNumber}`.
**`AddTextCore`**: `RequireNonEmptyText(text)` + `RequireFiniteCoords(x,y,angleDeg)`
`RequireDrawingContainer``dc.DrawingTexts` (null-check) → `.Add()` (null-check) → `X/Y/Angle`
`(IText)dt` (null-check QI) `.Str = text``Update()` (FALSE → откат `Delete`) → `Valid` (false → откат)
→ читаем `((IText)dt).Str` обратно → `DrawingAnnotationResult{Value=read-back, ViewNumber}`.
**`SetTechnicalRequirementsCore`**: `RequireNonEmptyText(text)` → нормализация `text.Replace("\r\n","\n")`
`RequireDrawingDocument()``dd.TechnicalDemand` (null-check) → `td.Text` (null-check) `.Str = text`
`td.Update()` (FALSE → ошибка; объект уровня документа — отката `Delete` НЕ делаем, перезапись идемпотентна)
→ вернуть число непустых-после-trim хвоста строк (`TrimEnd('\n').Split('\n').Length`).
**`RequireDrawingContainer(viewNumber)`** = `FindView``(IDrawingContainer)view` (null → «вид не приводится
к IDrawingContainer»). **`RequireDrawingDocument()`** = `RequireActiveDrawing()``(IDrawingDocument)doc`
(null → «чертёж не приводится к IDrawingDocument»).
RCW точечно не освобождаем (консистентно с остальным `DrawingService`; долг v2-2).
## Тестирование
### Unit (`RoughSignTypesTests`, `DrawingValidationTests`)
- `RoughSignTypes.Parse`: `delete/without/none` (+ рус. синонимы) → enum; неизвестное → `ArgumentException`; null → `ArgumentNullException`.
- `RequireNonEmptyText`: бросает на null/пусто/пробелы; пропускает непустое.
### Integration (`DrawingRoughTests`, `DrawingTextTests`, `DrawingTechReqTests`; наследуют `IntegrationTestBase`)
Шероховатость:
1. **Ставится + round-trip**: `value="Ra 1.6"`, `signType=delete``Value=="Ra 1.6"` (read-back из COM),
попадание в целевой вид (`Roughs.Count` +1, адресация по `viewNumber`).
2. **Знак без значения**: `value=""` → ставится (Valid), `Roughs.Count` +1.
3. **Нет видов** → понятная ошибка. 4. **Несуществующий `viewNumber>0`** → ошибка.
Текст:
5. **Ставится + round-trip**: `text``Value==text` (read-back), `DrawingTexts.Count` +1 (по ДЕЛЬТЕ —
вид уже мог содержать подпись), адресация по целевому `viewNumber`.
6. **Многострочный** (`"строка1\nстрока2"`) → `Value` сохраняет обе строки (round-trip `\n`).
7. **Пустой текст**`ArgumentException` (до Add). 8. **Нет видов** → ошибка. 9. **Несуществующий `viewNumber>0`** → ошибка.
Тех. требования:
10. **Задаются + round-trip**: многострочный текст → `lines==2`; `ReadTechnicalRequirementsAsync` возвращает
тот же текст; повторный вызов с другим текстом перезаписывает (read-back = новый, без накопления).
11. **Пустой текст**`ArgumentException`. 12. **Активный документ не чертёж** → понятная ошибка.
## Дальнейшее (вне спека)
- Привязка шероховатости/выносок к геометрии (`BaseObject`); выноски (`Leaders`), обозначения баз
(`Bases`), допуски формы (`Tolerances`); рамка/формат листа; ассоциативная привязка размеров.