Merge feat/drawing-diametral-dimension: диаметральный размер на виде (инкремент 4)

drawing_add_diametral_dimension — Ø окружности на виде через API7
(ISymbols2DContainer.DiametralDimensions; значение=диаметр 2R, авто). Извлечён
общий RequireSymbols2DContainer. 76 инструментов, 218 тестов (141 unit + 77
integration). Спек + ревью Codex (спека и реализации) учтены.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 18:44:42 +03:00
13 changed files with 371 additions and 27 deletions
+3 -3
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -39,7 +39,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
}
```
## Инструменты (75 инструментов)
## Инструменты (76 инструментов)
| Группа | Инструменты |
|---|---|
@@ -54,7 +54,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
| Variables | `create_variable`, `set_variable`, `delete_variable` |
| Conversion | `import_step`, `export_step` |
| Assembly | `assembly_add_component`, `assembly_add_mate` |
| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension` |
| Drawing | `drawing_create_standard_views`, `drawing_fill_title_block`, `drawing_add_linear_dimension`, `drawing_add_diametral_dimension` |
| Validation | `validate_part` |
### Типовой сценарий
@@ -73,7 +73,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe
```
src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели
src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов
tests/Kompas.Mcp.Tests/ 203 теста: unit + integration (integration требуют КОМПАС)
tests/Kompas.Mcp.Tests/ 218 тестов: unit + integration (integration требуют КОМПАС)
libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common)
docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/)
usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d
+6 -5
View File
@@ -1,6 +1,6 @@
# Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант)
> Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`hole_counterbore`+`hole_countersink`+`hole_conic`+`draft`+`assembly_add_component`+`assembly_add_mate`+`drawing_create_standard_views`+`drawing_fill_title_block`+`drawing_add_linear_dimension` (веха «2D-чертёж», инкремент 3)). Актуализировано по коду.
> Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`hole_counterbore`+`hole_countersink`+`hole_conic`+`draft`+`assembly_add_component`+`assembly_add_mate`+`drawing_create_standard_views`+`drawing_fill_title_block`+`drawing_add_linear_dimension`+`drawing_add_diametral_dimension` (веха «2D-чертёж», инкремент 4)). Актуализировано по коду.
> Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md).
> Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (навык `kompas-sdk-research`).
@@ -113,7 +113,7 @@ MCP client ──stdio──> Host (поток-пул, async)
|---|---|
| **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI (в т.ч. `ConversionService`), логи в stderr, старт и владение STA-потоком, graceful shutdown. Определения инструментов по категориям (System / Documents / Sketch / Features / Query / Conversion). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. |
| **Kompas.Mcp.Core** | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов и операций, `ConversionService` (STEP импорт/экспорт), `QueryService` (МЦХ, грани, рёбра, компоненты), `ModelInspectionService` (дерево операций, тела, переменные, drill-down, измерения), `AssemblyService` (вставка компонентов в сборку, namespace `Kompas.Mcp.Core.Assemblies`), `DrawingService` (стандартные виды чертежа, namespace `Kompas.Mcp.Core.Drawings`), снимок. Все паттерны API5/API7 живут здесь. |
| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 203 теста (131 unit + 72 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. |
| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 218 тестов (141 unit + 77 integration). Все интеграционные классы наследуют `IntegrationTestBase` (`IAsyncLifetime`), который вызывает `DocumentService.CloseAllAsync` после каждого теста. |
Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики.
@@ -121,7 +121,7 @@ MCP client ──stdio──> Host (поток-пул, async)
---
## 5. Карта инструментов (75 инструментов)
## 5. Карта инструментов (76 инструментов)
Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`.
@@ -212,6 +212,7 @@ MCP client ──stdio──> Host (поток-пул, async)
- `drawing_create_standard_views(partFilePath, scale, x, y)` — создать стандартные ассоциативные виды (спереди/сверху/слева, `ksPtFront/ksPtUp/ksPtLeft`) сохранённой 3D-модели (.m3d/.a3d) на активном чертеже. Реализовано через API7 `IKompasDocument2D.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", object[]{1,3,5}, x, y, scale, dx=20, dy=20)``object[]` передаётся как SAFEARRAY VT_I4. Проверки: `created==3` строго; непустота — сумма `IView.ObjectCount` новых видов (по разнице `IView.Number`) `>0`, иначе ошибка + откат (`IView.Delete`). Возвращает `DrawingViewsResult{Created,Total,ViewNumbers}``ViewNumbers` содержит номера созданных видов (для адресации при простановке размеров). Модель должна быть сохранена до вызова.
- `drawing_fill_title_block(designation?, name?, material?)` — заполнить графы основной надписи (штампа) активного чертежа. `designation` = обозначение документа (графа 2), `name` = наименование изделия (графа 1), `material` = материал (графа 3); нужно хотя бы одно поле. Реализовано: `doc2d.LayoutSheets.ItemByNumber[1].Stamp` (`IStamp`) → `stamp.Text[columnId].Str = text` (индексированное свойство; `IText.Str` замещает содержимое без `Clear`) → `stamp.Update()`. Номера граф `ksStampEnum`: обозначение=2, наименование=1, материал=3. Свой enum `StampField` + `StampFields.ColumnId/Collect` в `src/Kompas.Mcp.Core/Drawings/StampField.cs`. Возвращает число заполненных граф.
- `drawing_add_linear_dimension(x1,y1,x2,y2,x3,y3, orientation, viewNumber)` — поставить линейный размер на виде активного чертежа. Координаты (x1,y1)-(x2,y2) — выносные точки в **локальной СК вида** (мм); (x3,y3) — положение размерной линии; `orientation`: `horizontal`|`vertical`|`parallel`; `viewNumber` — номер вида (0=первый). Значение измеряется автоматически (`AutoNominalValue=true`). Реализовано: `(ISymbols2DContainer)view` (COM-QI от конкретного `IView`, не от листа — размер попадает именно в этот вид) → `LineDimensions.Add()``ILineDimension``Update()``Valid``((IDimensionText)dim).NominalValue`. Ключевая находка: **размеры не входят в `IView.ObjectCount`** (он считает геометрию `IDrawingContainer`); проверять через `ISymbols2DContainer.LineDimensions.Count`. Откат через `dim.Delete()` при ошибке или нулевом значении. Свой enum `DimensionOrientation {Horizontal, Vertical, Parallel}` + `DimensionOrientations.Parse/ToKompas`. Валидаторы `RequireFiniteCoords` + `RequireDistinctPoints`. Новые файлы: `DimensionOrientation.cs`, `DrawingDimensionResult.cs`; инструмент в `DrawingTools.cs`.
- `drawing_add_diametral_dimension(xc,yc,radius, angle, viewNumber)` — поставить диаметральный размер (Ø) окружности на виде активного чертежа. `xc,yc` — центр, `radius` — радиус (мм, локальная СК вида); `angle` — направление выноски (градусы → радианы через `DimensionAngles.ToRadians`); `viewNumber` — номер вида (0=первый). Значение (диаметр = 2·radius) измеряется автоматически. Реализовано: общий хелпер `RequireSymbols2DContainer(viewNumber)` (переиспользуется с линейным размером) → `DiametralDimensions.Add()``IDiametralDimension` (`Xc`, `Yc`, `Radius`, `Angle` в радианах, `AutoNominalValue=true`) → `Update()``Valid``((IDimensionText)dim).NominalValue`. Размер «свободный» (без `BaseObject`). Валидатор `DrawingValidation.RequirePositiveRadius`. `DimensionAngles.ToRadians` добавлен в `DimensionOrientation.cs`.
**Vision — визуальная обратная связь (fallback)**
- `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**,
@@ -346,14 +347,14 @@ dotnet build -c Release -r win-x64 # сборка
## 10. Дорожная карта
**Реализовано (v1+v2+STEP/assembly+direct-edit+inspection+package-A+package-B+package-C+package-D start+package-E start+hole+hole_counterbore+hole_countersink+hole_conic+draft+assembly_add_component+assembly_add_mate+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension):** документы, эскизы (полный набор 2D-примитивов: линия, окружность, дуга, дуга по 3 точкам, прямоугольник, эллипс, ломаная, правильный многоугольник, сплайн NURBS, точка), **вспомогательная геометрия (`sketch_create_on_offset_plane`)**, выдавливание/вырез, вращение, скругление/фаска, **оболочка (`shell`)**, **ребро жёсткости (`rib`)**, **кинематическая операция (`sweep`)**, **операция по сечениям (`loft`)**, **линейный/круговой массив и зеркало (`linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`)**, **отверстие (`hole`, API7)**, **цековка (`hole_counterbore`, API7)**, **зенковка (`hole_countersink`, API7)**, **коническое отверстие (`hole_conic`, API7)**, **уклон (`draft`, API5 `ksInclineDefinition`)**, снимок; `get_part_info`, `get_bounding_box`, `list_faces`, `list_edges`; `import_step`, `export_step`, `list_components`; **`move_face`**, **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`** (прямое редактирование и булевы операции); **`describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure`** (структурный осмотр модели); **`create_variable`, `set_variable`, `delete_variable`** (управление переменными, package D start); **`assembly_add_component`** (вставка компонента в сборку, API7, `AssemblyService`); **`assembly_add_mate`** (сопряжения `coincidence`/`distance`, API7, `IMateConstraints3D`); **`drawing_create_standard_views`** (стандартные ассоциативные виды чертежа, API7, `DrawingService`); **`drawing_fill_title_block`** (заполнение основной надписи/штампа: обозначение, наименование, материал; `IStamp.Text[id].Str`, API7, `DrawingService`); **`drawing_add_linear_dimension`** (линейный размер на виде чертежа: `ISymbols2DContainer`/`LineDimensions.Add`/`ILineDimension`, координаты в локальной СК вида, API7, `DrawingService`).
**Реализовано (v1+v2+STEP/assembly+direct-edit+inspection+package-A+package-B+package-C+package-D start+package-E start+hole+hole_counterbore+hole_countersink+hole_conic+draft+assembly_add_component+assembly_add_mate+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension):** документы, эскизы (полный набор 2D-примитивов: линия, окружность, дуга, дуга по 3 точкам, прямоугольник, эллипс, ломаная, правильный многоугольник, сплайн NURBS, точка), **вспомогательная геометрия (`sketch_create_on_offset_plane`)**, выдавливание/вырез, вращение, скругление/фаска, **оболочка (`shell`)**, **ребро жёсткости (`rib`)**, **кинематическая операция (`sweep`)**, **операция по сечениям (`loft`)**, **линейный/круговой массив и зеркало (`linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`)**, **отверстие (`hole`, API7)**, **цековка (`hole_counterbore`, API7)**, **зенковка (`hole_countersink`, API7)**, **коническое отверстие (`hole_conic`, API7)**, **уклон (`draft`, API5 `ksInclineDefinition`)**, снимок; `get_part_info`, `get_bounding_box`, `list_faces`, `list_edges`; `import_step`, `export_step`, `list_components`; **`move_face`**, **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`** (прямое редактирование и булевы операции); **`describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure`** (структурный осмотр модели); **`create_variable`, `set_variable`, `delete_variable`** (управление переменными, package D start); **`assembly_add_component`** (вставка компонента в сборку, API7, `AssemblyService`); **`assembly_add_mate`** (сопряжения `coincidence`/`distance`, API7, `IMateConstraints3D`); **`drawing_create_standard_views`** (стандартные ассоциативные виды чертежа, API7, `DrawingService`); **`drawing_fill_title_block`** (заполнение основной надписи/штампа: обозначение, наименование, материал; `IStamp.Text[id].Str`, API7, `DrawingService`); **`drawing_add_linear_dimension`** (линейный размер на виде чертежа: `ISymbols2DContainer`/`LineDimensions.Add`/`ILineDimension`, координаты в локальной СК вида, API7, `DrawingService`); **`drawing_add_diametral_dimension`** (диаметральный размер Ø: `ISymbols2DContainer.DiametralDimensions.Add`/`IDiametralDimension`, Xc/Yc/Radius/Angle в радианах, API7, `DrawingService`).
**Следующие приоритеты:**
1. Пакет D «параметрика» (продолжение) — связь размеров эскизов с переменными (параметрические эскизы через API2D). ⚠️ **Исследовано**: `ksCDimWithVariable` недоступен из внешней автоматизации — см. `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`.
2. Пакет E «вспомогательная геометрия» (продолжение) — ось, точка, плоскость по трём точкам / по углу.
3. Рассечение/перемещение тела как MCP-инструменты (`SplitSolids`/`BodyRepositions`) — механика есть, продуктизация не закончена.
4. Свойства документа: `IPropertyMng` / `IPropertyKeeper`.
5. **2D-чертёж (продолжение):** диаметральные/радиальные/угловые размеры, рамка/формат, штриховки, тексты. Стандартные виды реализованы (`drawing_create_standard_views`); основная надпись/штамп реализована (`drawing_fill_title_block`); линейные размеры реализованы (`drawing_add_linear_dimension`).
5. **2D-чертёж (продолжение):** радиальные/угловые размеры, рамка/формат, штриховки, тексты. Стандартные виды реализованы (`drawing_create_standard_views`); основная надпись/штамп реализована (`drawing_fill_title_block`); линейные размеры реализованы (`drawing_add_linear_dimension`); диаметральный размер реализован (`drawing_add_diametral_dimension`).
6. Сборки — продолжение: дополнительные типы сопряжений (`parallel`, `perpendicular`, `concentric`, `angle`, `tangency`; `coincidence`/`distance` реализованы); авто-позиционирование компонентов; спецификации (`ISpecification`).
7. Транспорт **HTTP/SSE** — как опция для удалённых клиентов.
+3
View File
@@ -197,3 +197,6 @@ stateful-сессии? Пока полагаемся на последовате
### ✅ Линейные размеры на видах чертежа — реализованы (инкремент 3 вехи «2D-чертёж»)
**Решено.** Новый инструмент `drawing_add_linear_dimension(x1,y1,x2,y2,x3,y3, orientation, viewNumber)` — ставит линейный размер на виде активного чертежа. Паттерн API7: получить вид по `viewNumber` → COM-QI `(ISymbols2DContainer)view` (размер адресуется конкретному виду, а не листу) → `LineDimensions.Add()``ILineDimension``X1,Y1,X2,Y2` (выносные точки в **локальной СК вида**, мм), `X3,Y3` (положение размерной линии), `Orientation` (`ksLineDimensionOrientationEnum`: `ksLinDParallel=0`/`ksLinDHorizontal=1`/`ksLinDVertical=2`), `AutoNominalValue=true``Update()``Valid` → значение `((IDimensionText)dim).NominalValue`. Откат `dim.Delete()` при ошибке или нулевом значении. **Ключевая находка: размеры НЕ входят в `IView.ObjectCount`** (тот считает геометрию `IDrawingContainer`); размеры живут в `ISymbols2DContainer` отдельно — попадание размера в вид проверяется через `ISymbols2DContainer.LineDimensions.Count`. `DrawingViewsResult` расширен полем `ViewNumbers` (номера созданных видов — для адресации при простановке размеров). Свой enum `DimensionOrientation {Horizontal, Vertical, Parallel}` + `DimensionOrientations.Parse/ToKompas`. Валидаторы `DrawingValidation.RequireFiniteCoords` + `RequireDistinctPoints`. Новые файлы: `DimensionOrientation.cs`, `DrawingDimensionResult.cs`; инструмент в `DrawingTools.cs`. Итог: **75 инструментов, 203 теста (131 unit + 72 integration).** Следующий шаг: диаметральные/радиальные/угловые размеры, рамка/формат.
### ✅ Диаметральный размер на виде чертежа — реализован (инкремент 4 вехи «2D-чертёж»)
**Решено.** Новый инструмент `drawing_add_diametral_dimension(xc,yc,radius, angle, viewNumber)` — ставит диаметральный размер (Ø) на виде активного чертежа. Паттерн API7: общий хелпер `RequireSymbols2DContainer(viewNumber)` (переиспользуется с линейным путём) → `DiametralDimensions.Add()``IDiametralDimension` (`Xc`, `Yc`, `Radius` в локальной СК вида, `Angle` в **радианах** через `DimensionAngles.ToRadians`, `AutoNominalValue=true`) → `Update()``Valid``((IDimensionText)dim).NominalValue` = диаметр (2·Radius). Размер «свободный» (без `BaseObject` — ассоциативная привязка отложена). Валидатор `DrawingValidation.RequirePositiveRadius`. `DimensionAngles.ToRadians` (с проверкой переполнения) добавлен в `DimensionOrientation.cs`; `RequirePositiveRadius` — в `DrawingValidation.cs`; инструмент в `DrawingTools.cs`. Итог: **76 инструментов, 218 тестов (141 unit + 77 integration).** Следующий шаг: радиальные/угловые размеры, рамка/формат.
+9 -8
View File
@@ -128,7 +128,7 @@
<!-- HERO -->
<header class="hero">
<div class="wrap">
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+hole_counterbore+hole_countersink+hole_conic+draft+переменные+assembly_add_component+assembly_add_mate+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension (75 инструментов)</span>
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+hole_counterbore+hole_countersink+hole_conic+draft+переменные+assembly_add_component+assembly_add_mate+drawing_create_standard_views+drawing_fill_title_block+drawing_add_linear_dimension+drawing_add_diametral_dimension (76 инструментов)</span>
<h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1>
<p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов,
эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом —
@@ -261,7 +261,7 @@
впереди.</p>
<div class="prog">
<div class="progrow"><span>Общий прогресс</span><span>v3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · hole_counterbore · hole_countersink · hole_conic · draft (API5) · assembly_add_component · assembly_add_mate · drawing_create_standard_views · drawing_fill_title_block · drawing_add_linear_dimension · навык kompas-3d · 75 инструментов · 203 теста</span></div>
<div class="progrow"><span>Общий прогресс</span><span>v3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · hole_counterbore · hole_countersink · hole_conic · draft (API5) · assembly_add_component · assembly_add_mate · drawing_create_standard_views · drawing_fill_title_block · drawing_add_linear_dimension · drawing_add_diametral_dimension · навык kompas-3d · 76 инструментов · 218 тестов</span></div>
<div class="bar"><i style="width:97%"></i></div>
</div>
@@ -297,7 +297,7 @@
<!-- TOOLS -->
<div class="panel">
<h3>Инструменты v3+ <span class="tag done">ГОТОВО</span></h3>
<p class="meta">75 инструментов, отдаются по MCP-протоколу</p>
<p class="meta">76 инструментов, отдаются по MCP-протоколу</p>
<ul class="list">
<li class="muted"><span class="mark ok"></span><span class="t"><code>System</code>: connect · status · set_visible</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Documents</code>: create · open · save · save_as · close · active</span></li>
@@ -310,8 +310,8 @@
<li class="muted"><span class="mark ok"></span><span class="t"><code>Variables</code>: <b>create_variable</b> · <b>set_variable</b> · <b>delete_variable</b> (пакет D)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Conversion</code>: import_step · export_step</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Assembly</code>: <b>assembly_add_component</b> (вставка компонента .m3d/.a3d в сборку, API7) · <b>assembly_add_mate</b> (сопряжения coincidence/distance, API7)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Drawing</code>: <b>drawing_create_standard_views</b> (стандартные ассоциативные виды чертежа, API7) · <b>drawing_fill_title_block</b> (основная надпись/штамп: обозначение, наименование, материал, API7) · <b>drawing_add_linear_dimension</b> (линейный размер на виде, ISymbols2DContainer, API7)</span></li>
<li><span class="mark wip"></span><span>2D-чертёж: диаметральные/радиальные/угловые размеры, рамка/формат · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — <b>исследовано, недоступно через COM API</b> · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t"><code>Drawing</code>: <b>drawing_create_standard_views</b> (стандартные ассоциативные виды чертежа, API7) · <b>drawing_fill_title_block</b> (основная надпись/штамп: обозначение, наименование, материал, API7) · <b>drawing_add_linear_dimension</b> (линейный размер на виде, ISymbols2DContainer, API7) · <b>drawing_add_diametral_dimension</b> (диаметральный размер Ø, IDiametralDimension, API7)</span></li>
<li><span class="mark wip"></span><span>2D-чертёж: радиальные/угловые размеры, рамка/формат · пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — <b>исследовано, недоступно через COM API</b> · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency)</span></li>
</ul>
</div>
@@ -322,7 +322,7 @@
<ul class="list">
<li class="muted"><span class="mark ok"></span><span class="t">Unit-тесты — без COM: диспетчер, enum-маппинг, StepFormat, InspectionText</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Итого 203 теста зелёных (131 unit + 72 integration)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Итого 218 тестов зелёных (141 unit + 77 integration)</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Сборка <code>kompas-mcp.exe</code>, README с конфигом клиента</span></li>
<li class="muted"><span class="mark ok"></span><span class="t">Навык <code>kompas-3d</code> (методика) + полигон <code>usecases/</code></span></li>
<li><span class="mark todo"></span><span>In-process тест MCP-клиента, релизный publish</span></li>
@@ -352,8 +352,9 @@
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки start · готово ✓</div><h4>Вставка компонентов</h4><p><b>assembly_add_component</b> (API7 IParts7.AddFromFile + IPlacement3D)</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">сборки инкремент 2 · готово ✓</div><h4>Сопряжения</h4><p><b>assembly_add_mate</b> (coincidence/distance, API7 IMateConstraints3D) · IntegrationTestBase · 72 инструмента · 156 тестов</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 2 ✓</div><h4>Стандартные виды + основная надпись</h4><p><b>drawing_create_standard_views</b> (AddStandartViews, API7) · <b>drawing_fill_title_block</b> (IStamp.Text[id].Str, API7) · 74 инструмента · 188 тестов</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 3 ✓</div><h4>Линейные размеры</h4><p><b>drawing_add_linear_dimension</b> (ISymbols2DContainer/LineDimensions.Add/ILineDimension, локальная СК вида, API7) · ViewNumbers в результате видов · 75 инструментов · 203 теста · далее: диаметральные/радиальные размеры, рамка/формат</p></div>
<div class="tl cur"><div class="ph">далее · в работе ▶</div><h4>Оформление чертежа</h4><p>Диаметральные/радиальные/угловые размеры · рамка/формат · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 3 ✓</div><h4>Линейные размеры</h4><p><b>drawing_add_linear_dimension</b> (ISymbols2DContainer/LineDimensions.Add/ILineDimension, локальная СК вида, API7) · ViewNumbers в результате видов · 75 инструментов · 203 теста</p></div>
<div class="tl" style="border-color:var(--green)"><div class="ph" style="color:var(--green)">2D-чертёж · инкремент 4 ✓</div><h4>Диаметральный размер</h4><p><b>drawing_add_diametral_dimension</b> (ISymbols2DContainer.DiametralDimensions.Add/IDiametralDimension, Xc/Yc/Radius/Angle рад, API7) · RequireSymbols2DContainer общий хелпер · DimensionAngles.ToRadians · 76 инструментов · 218 тестов · далее: радиальные/угловые размеры, рамка/формат</p></div>
<div class="tl cur"><div class="ph">далее · в работе ▶</div><h4>Оформление чертежа</h4><p>Радиальные/угловые размеры · рамка/формат · пакет E (ось, угловая плоскость) · прочие типы сопряжений (parallel/perpendicular/concentric/angle/tangency) · пакет D параметрика — параметрические эскизы недоступны через COM API</p></div>
</div>
</div>
</section>
@@ -0,0 +1,108 @@
# Дизайн: диаметральный размер на виде чертежа (drawing_add_diametral_dimension) через API7
**Дата:** 2026-05-27
**Статус:** реализовано и проверено (5 интеграционных + unit, всего 218); ревью Codex спека и реализации учтено
## Правки по ревью реализации Codex
- **#2** инвариант диаметра: пост-проверка `value <= 0 || !double.IsFinite(value)` (а не `Abs<1e-6`).
- **#3** сообщение `drawing_create_standard_views` обобщено: «номера видов для инструментов размеров».
- **#4a** `DimensionAngles.ToRadians` — проверка конечности результата (защита от переполнения).
- **#5d** тест `viewNumber: 999` (виды есть, номер не найден) — покрывает ветку FindView.
- **#6** комментарий `DimensionType` переформулирован (просто COM-дефолт, без претензии на «стиль»).
- **#4b** минимальный радиус — отклонён (`>0` честный контракт). **#5a/#5b/#5c** (реальная окружность,
read-back угла, пост-Add откат) — отклонены (инструмент geometry-agnostic; ToRadians покрыт unit;
пост-Add сбой для radius>0 не форсировать). Транзитный сбой пакета (нестабильность КОМПАС после
большого прогона) не воспроизводится — повторный прогон 5/5 зелёный.
## Правки по ревью Codex
- **#5** `((IDimensionText)dim).AutoNominalValue = true` выставляем явно (консистентно с линейным).
- **#7** извлечь `RequireSymbols2DContainer(viewNumber)` (= `(ISymbols2DContainer)FindView(...)`) —
переиспользуют диаметральный путь и счётчики (радиальный — следующий, тот же паттерн).
- **#4** конверсия °→рад вынесена в чистый `DimensionAngles.ToRadians(deg)` + unit-тест (значение от
угла не зависит → математику проверяем отдельно).
- **#2** `DimensionType` НЕ задаём — принимаем COM-дефолт (стандартный стиль; полярность bool не
подтверждена, спайк с дефолтом дал `Valid` + верное значение). Зафиксировано комментарием.
- **#1** размер «свободный» (по Xc/Yc/Radius, без `BaseObject`) — как у линейного; LLM задаёт верные
параметры окружности. Ассоциативная привязка — будущее. Отражено в описании инструмента.
- **#3** `angle` — любой конечный (направление выноски, КОМПАС нормализует); валидируем лишь
конечность. **#6** откат после `Add()` для диаметрального не форсируется (radius>0 → всегда Valid,
value=2R>0); код отката идентичен протестированному линейному (zero-projection тест).
## Цель
Веха 2D-ЧЕРТЁЖ, инкремент 4. После линейного размера — **диаметральный** размер окружности на виде
(Ø для отверстий/цилиндров — частый случай конструкторского чертежа). Радиальный (с ветвями) и
угловой — следующие инкременты.
## Спайк: механизм подтверждён (НЕ разучивать)
Та же модель, что у линейного размера (`ISymbols2DContainer` QI от вида). Спайк
(`DrawingDiametralSpikeTests`, зелёный):
```
ISymbols2DContainer symbols = (ISymbols2DContainer)view; // QI от конкретного IView
IDiametralDimension dim = symbols.DiametralDimensions.Add();
dim.Xc = xc; dim.Yc = yc; // центр окружности (ЛОКАЛЬНАЯ СК вида, мм)
dim.Radius = radius; // радиус окружности (мм)
dim.Angle = angleRad; // направление выноски (РАДИАНЫ)
bool ok = dim.Update();
double value = ((IDimensionText)dim).NominalValue; // = ДИАМЕТР (2·Radius)
```
**Проверено:** `Xc=10,Yc=10,Radius=15,Angle=0.785``Update=True`, `Valid=True`,
`NominalValue == 30` (= диаметр = 2·Radius). `Angle` — в **радианах** (конвенция 2D-API КОМПАС;
для размера задаёт лишь направление выноски, на значение не влияет).
**Координаты** — локальная СК вида (как у линейного). Размер «свободный» (по Xc/Yc/Radius), без
ассоциативной привязки к геометрии (`BaseObject` не задаём — будущее).
## MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_add_diametral_dimension` | `xc,yc` (центр), `radius`, `angle=0` (°), `viewNumber=0` | Поставить диаметральный размер (Ø) окружности с центром (xc,yc) и радиусом `radius` (ЛОКАЛЬНАЯ СК вида, мм) на виде. `angle` — направление выноски в ГРАДУСАХ (внутри → радианы). `viewNumber`: 0 = первый/главный вид. Значение (диаметр = 2·radius) измеряется автоматически. Возвращает диаметр и номер вида. |
`angle` экспонируется в **градусах** (LLM-дружелюбно), внутри умножается на π/180.
## Архитектура
В существующем `DrawingService` (рядом с `AddLinearDimensionAsync`); переиспользуем `FindView`,
`DrawingDimensionResult`, паттерн отката.
- `DrawingService.AddDiametralDimensionAsync(viewNumber, xc, yc, radius, angleDeg, ct)`.
- Инструмент `drawing_add_diametral_dimension` в `DrawingTools`.
- Helper `GetViewDiametralDimensionCountAsync` (для теста — попадание в вид; размеры в
`ISymbols2DContainer`, не в `ObjectCount` — как выяснено для линейных).
## Валидация (чистые static, unit-тест)
- `DrawingValidation.RequireFiniteCoords(xc, yc, angleDeg)` (уже есть).
- `DrawingValidation.RequirePositiveRadius(double radius)` (новый) — конечный и `> 0`.
## Реализация
`AddDiametralDimensionCore`: валидация (coords + radius>0) → `FindView``(ISymbols2DContainer)view`
`DiametralDimensions.Add()` (null-check) → `Xc/Yc/Radius`, `Angle = angleDeg·π/180``Update()`
(FALSE → откат `dim.Delete` + ошибка) → `Valid` (false → откат) → `value = ((IDimensionText)dim).
NominalValue` (диаметр); если `≤ 0` → откат + ошибка → вернуть `DrawingDimensionResult { Value, ViewNumber }`.
RCW точечно не освобождаем (v2-2).
## Тестирование
### Unit (`DrawingValidationTests`)
- `RequirePositiveRadius` — бросает на 0/отрицательном/NaN/Infinity; пропускает положительный.
### Integration (`DrawingDiametralTests`)
1. **Значение = диаметр**: Radius=15 → `Value ≈ 30`.
2. **Попадание в вид**: `*DiametralDimensions.Count` целевого вида +1 (адресация по `viewNumber` из
`ViewNumbers`).
3. **Радиус ≤ 0**`ArgumentOutOfRangeException` (откат не нужен — до `Add`).
4. **Нет видов** → понятная ошибка.
(Сценарий 1 = промотированный спайк; спайк-файл удаляется.)
## Дальнейшее (вне спека)
- Радиальный размер (`RadialDimensions`, ветви), угловой; ассоциативная привязка к окружности вида
(`BaseObject`); рамка/формат листа.
@@ -13,6 +13,21 @@ public enum DimensionOrientation
Parallel,
}
/// <summary>Конверсии углов размеров (2D-API КОМПАС использует радианы).</summary>
public static class DimensionAngles
{
/// <summary>Градусы → радианы (угол выноски в API задаётся в радианах).</summary>
public static double ToRadians(double degrees)
{
if (!double.IsFinite(degrees))
throw new ArgumentException("Угол должен быть конечным числом.", nameof(degrees));
var result = degrees * (Math.PI / 180.0);
if (!double.IsFinite(result)) // защита от переполнения на экстремальных degrees
throw new ArgumentOutOfRangeException(nameof(degrees), "Угол слишком велик (переполнение при переводе в радианы).");
return result;
}
}
/// <summary>Маппинг ориентации размера ↔ перечисление КОМПАС и разбор строки.</summary>
public static class DimensionOrientations
{
+58 -7
View File
@@ -100,9 +100,7 @@ public sealed class DrawingService
DrawingValidation.RequireFiniteCoords(x1, y1, x2, y2, x3, y3);
DrawingValidation.RequireDistinctPoints(x1, y1, x2, y2);
var view = FindView(viewNumber);
var symbols = view as ISymbols2DContainer
?? throw new InvalidOperationException("Вид не приводится к ISymbols2DContainer.");
var (view, symbols) = RequireSymbols2DContainer(viewNumber);
var dim = symbols.LineDimensions?.Add()
?? throw new InvalidOperationException("LineDimensions.Add вернул null.");
try
@@ -136,11 +134,64 @@ public sealed class DrawingService
/// <summary>Число линейных размеров на виде по номеру (для проверки/диагностики). Размеры живут
/// в ISymbols2DContainer вида, а НЕ в IView.ObjectCount (тот считает геометрию).</summary>
public Task<int> GetViewLineDimensionCountAsync(int viewNumber, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
=> _dispatcher.InvokeAsync(() => RequireSymbols2DContainer(viewNumber).Symbols.LineDimensions?.Count ?? 0, ct);
/// <summary>
/// Поставить диаметральный размер (Ø) окружности с центром (xc,yc) и радиусом <paramref name="radius"/>
/// (ЛОКАЛЬНАЯ СК вида, мм) на виде. <paramref name="angleDeg"/> — направление выноски (градусы).
/// Значение (диаметр = 2·radius) измеряется автоматически. Возвращает диаметр и номер вида.
/// </summary>
public Task<DrawingDimensionResult> AddDiametralDimensionAsync(int viewNumber, double xc, double yc,
double radius, double angleDeg, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() => AddDiametralDimensionCore(viewNumber, xc, yc, radius, angleDeg), ct);
private DrawingDimensionResult AddDiametralDimensionCore(int viewNumber, double xc, double yc,
double radius, double angleDeg)
{
DrawingValidation.RequireFiniteCoords(xc, yc);
DrawingValidation.RequirePositiveRadius(radius);
var angleRad = DimensionAngles.ToRadians(angleDeg);
var (view, symbols) = RequireSymbols2DContainer(viewNumber);
var dim = symbols.DiametralDimensions?.Add()
?? throw new InvalidOperationException("DiametralDimensions.Add вернул null.");
try
{
var symbols = (ISymbols2DContainer)FindView(viewNumber);
return symbols.LineDimensions?.Count ?? 0;
}, ct);
dim.Xc = xc; dim.Yc = yc; dim.Radius = radius; dim.Angle = angleRad;
// DimensionType (стиль размерной линии) не задаём — оставляем COM-дефолт (полярность bool
// не подтверждена; дефолт даёт валидный размер с верным значением — проверено спайком).
((IDimensionText)dim).AutoNominalValue = true;
if (!dim.Update())
throw new InvalidOperationException("IDiametralDimension.Update вернул FALSE — размер не построен.");
if (!dim.Valid)
throw new InvalidOperationException("Диаметральный размер построен невалидным.");
var value = ((IDimensionText)dim).NominalValue; // диаметр = 2·radius
if (value <= 0 || !double.IsFinite(value)) // инвариант диаметра: строго положительный
throw new InvalidOperationException($"Диаметральный размер измерил некорректное значение ({value}).");
return new DrawingDimensionResult { Value = value, ViewNumber = view.Number };
}
catch
{
try { dim.Delete(); } catch { /* лучшее усилие */ }
throw;
}
}
/// <summary>Число диаметральных размеров на виде (для проверки попадания в вид — они в
/// ISymbols2DContainer, а не в IView.ObjectCount).</summary>
public Task<int> GetViewDiametralDimensionCountAsync(int viewNumber, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() => RequireSymbols2DContainer(viewNumber).Symbols.DiametralDimensions?.Count ?? 0, ct);
/// <summary>Вид + его ISymbols2DContainer (QI от вида). Общий для линейных/диаметральных/… размеров.</summary>
private (IView View, ISymbols2DContainer Symbols) RequireSymbols2DContainer(int viewNumber)
{
var view = FindView(viewNumber);
var symbols = view as ISymbols2DContainer
?? throw new InvalidOperationException("Вид не приводится к ISymbols2DContainer.");
return (view, symbols);
}
/// <summary>Найти вид: при viewNumber ≤ 0 — первый несистемный (Number != 0); иначе по номеру.</summary>
private IView FindView(int viewNumber)
@@ -41,6 +41,13 @@ public static class DrawingValidation
throw new ArgumentException("Координаты должны быть конечными числами (не NaN/Infinity).");
}
/// <summary>Проверить радиус: конечный и строго положительный.</summary>
public static void RequirePositiveRadius(double radius)
{
if (!double.IsFinite(radius) || radius <= 0)
throw new ArgumentOutOfRangeException(nameof(radius), "Радиус должен быть конечным и > 0.");
}
/// <summary>Минимальное расстояние между выносными точками линейного размера (мм).</summary>
private const double MinPointDistance = 1e-7;
+19 -1
View File
@@ -26,7 +26,7 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing)
var r = await drawing.CreateStandardViewsAsync(partFilePath, scale, x, y);
var numbers = string.Join(", ", r.ViewNumbers);
return $"Создано {r.Created} стандартных вида на чертеже (всего видов: {r.Total}, масштаб {scale}). " +
$"Номера созданных видов: {numbers} (для drawing_add_linear_dimension).";
$"Номера созданных видов: {numbers} (для инструментов простановки размеров — параметр viewNumber).";
}
[McpServerTool(Name = "drawing_fill_title_block")]
@@ -65,4 +65,22 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing)
var r = await drawing.AddLinearDimensionAsync(viewNumber, x1, y1, x2, y2, x3, y3, o);
return $"Линейный размер ({orientation}) поставлен на виде {r.ViewNumber}, значение {r.Value:0.###} мм.";
}
[McpServerTool(Name = "drawing_add_diametral_dimension")]
[Description("Поставить диаметральный размер (Ø) окружности на виде активного чертежа. Окружность " +
"задаётся центром (xc,yc) и радиусом radius в ЛОКАЛЬНОЙ СК вида (мм; при масштабе 1:1 = координаты " +
"модели на плоскости вида) — задайте параметры реальной окружности вида. angle — направление выноски " +
"в градусах. viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный). " +
"Значение (диаметр = 2·radius) измеряется автоматически. Возвращает диаметр и номер вида.")]
public async Task<string> AddDiametralDimension(
[Description("X центра окружности (СК вида, мм)")] double xc,
[Description("Y центра окружности")] double yc,
[Description("Радиус окружности, мм")] double radius,
[Description("Направление выноски, градусы")] double angle = 0,
[Description("Номер вида (0 = первый/главный)")] int viewNumber = 0)
{
await session.ConnectAsync();
var r = await drawing.AddDiametralDimensionAsync(viewNumber, xc, yc, radius, angle);
return $"Диаметральный размер поставлен на виде {r.ViewNumber}, Ø{r.Value:0.###} мм.";
}
}
@@ -21,4 +21,16 @@ public sealed class DimensionOrientationsTests
// ToKompas (→ ksLineDimensionOrientationEnum) намеренно НЕ покрыт unit-тестом: тестовый проект
// не ссылается на Kompas6Constants (граница проекта). Маппинг тривиален и проверяется интеграцией
// (horizontal → |X2-X1|, vertical → |Y2-Y1|).
[Theory]
[InlineData(0, 0)]
[InlineData(90, Math.PI / 2)]
[InlineData(180, Math.PI)]
[InlineData(-45, -Math.PI / 4)]
public void ToRadians_converts_degrees(double degrees, double expected)
=> Assert.Equal(expected, DimensionAngles.ToRadians(degrees), 9);
[Fact]
public void ToRadians_throws_on_non_finite()
=> Assert.Throws<ArgumentException>(() => DimensionAngles.ToRadians(double.NaN));
}
@@ -68,4 +68,16 @@ public sealed class DrawingValidationTests
Assert.Throws<ArgumentException>(() => DrawingValidation.RequireDistinctPoints(10, 20, 10, 20));
Assert.Null(Record.Exception(() => DrawingValidation.RequireDistinctPoints(0, 0, 40, 0)));
}
[Theory]
[InlineData(0)]
[InlineData(-5)]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
public void RequirePositiveRadius_throws_on_non_positive_or_non_finite(double radius)
=> Assert.Throws<ArgumentOutOfRangeException>(() => DrawingValidation.RequirePositiveRadius(radius));
[Fact]
public void RequirePositiveRadius_accepts_positive()
=> Assert.Null(Record.Exception(() => DrawingValidation.RequirePositiveRadius(15)));
}
@@ -0,0 +1,116 @@
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Drawings;
using Kompas.Mcp.Core.Modeling;
namespace Kompas.Mcp.Tests.Integration;
/// <summary>Интеграция: диаметральный размер на виде (API7 ISymbols2DContainer.DiametralDimensions).</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class DrawingDiametralTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PartModeler _modeler;
private readonly DrawingService _drawing;
public DrawingDiametralTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_drawing = new DrawingService(fx.Session, fx.Dispatcher);
}
private async Task<string> BuildAndSaveBoxAsync()
{
var path = TestPaths.NewFile(".m3d");
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddRectangleAsync(s, 0, 0, 40, 30);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: 20);
await _modeler.RebuildAsync();
await _docs.SaveAsAsync(path);
}
finally { await _docs.CloseAsync(save: false); }
return path;
}
[Fact]
public async Task Diametral_dimension_value_is_diameter()
{
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
// Радиус 15 → диаметр 30.
var r = await _drawing.AddDiametralDimensionAsync(viewNumber: 0, xc: 10, yc: 10, radius: 15, angleDeg: 45);
Assert.InRange(r.Value, 29.9, 30.1);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Targets_specific_view_and_increments_diametral_count()
{
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
var targetNumber = views.ViewNumbers[1];
var before = await _drawing.GetViewDiametralDimensionCountAsync(targetNumber);
var r = await _drawing.AddDiametralDimensionAsync(targetNumber, xc: 5, yc: 5, radius: 8, angleDeg: 30);
var after = await _drawing.GetViewDiametralDimensionCountAsync(targetNumber);
Assert.Equal(targetNumber, r.ViewNumber);
Assert.Equal(before + 1, after);
Assert.InRange(r.Value, 15.9, 16.1); // Ø = 2·8
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Non_positive_radius_throws()
{
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
_drawing.AddDiametralDimensionAsync(viewNumber: 0, xc: 0, yc: 0, radius: 0, angleDeg: 0));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task No_views_throws()
{
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_drawing.AddDiametralDimensionAsync(viewNumber: 0, xc: 0, yc: 0, radius: 10, angleDeg: 0));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Unknown_view_number_throws()
{
var partFile = await BuildAndSaveBoxAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
// Виды есть, но номер 999 не существует → ветка FindView "вид не найден".
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_drawing.AddDiametralDimensionAsync(viewNumber: 999, xc: 0, yc: 0, radius: 10, angleDeg: 0));
}
finally { await _docs.CloseAsync(save: false); }
}
}