# Дизайн: пакет A — богаче эскизы (примитивы 2D)

**Дата:** 2026-05-26
**Статус:** одобрен к реализации
**Контекст:** kompas3d-mcp — MCP-сервер для КОМПАС-3D через COM API (.NET 8, C#).

## Цель

Расширить набор примитивов эскиза. Сейчас профиль ограничен отрезком, окружностью,
прямоугольником и осевой линией (`sketch_add_line/circle/rectangle/axis`) — без дуг и
кривых недоступна масса реальных профилей. Добавляем **7 инструментов**: дугу (двумя
способами), эллипс, ломаную, правильный многоугольник, сплайн и точку.

Это первый из приоритизированных пакетов расширения MCP (см. анализ функций от 2026-05-26:
пакеты A — эскизы, B — формообразующие, C — массивы, D — параметрика). Выбран пакет A.

## Не входит в объём (YAGNI)

- Параметрические ограничения и размеры эскиза (отдельный крупный пакет).
- Скругление/фаска углов эскиза (`pCorner` у многоугольника не используем).
- Выставление порядка сплайна наружу — фиксируем кубический (порядок 4).
- Дополнительные кривые (`ksArcByPoint`, дуга эллипса, Bezier, trim) — в backlog «пакет A2».

## Сигнатуры COM (Automation — по `docs/Kompas3D_SDK/`; типы — по рефлексии interop)

Все методы — на интерфейсе `ksDocument2D` (API5), вызываются на редакторе открытого
эскиза (`def.BeginEdit()` → `ksDocument2D`). Углы **в градусах**. Стиль линии `1` =
«основная» (`ksCSNormal`). Каждый метод возвращает `long`: `0` — ошибка.

```
long ksArcBy3Points(double x1,y1, x2,y2, x3,y3, long style)
long ksArcByAngle(double xc, yc, rad, f1_град, f2_град, short direction(1=CCW|-1=CW), long style)
long ksEllipse(LPDISPATCH ksEllipseParam)            // interop-свойства: xc,yc,A,B,angle(град),style
long ksRegularPolygon(LPDISPATCH ksRegularPolygonParam, short centre=0)
                                                     // count,xc,yc,ang(град),radius,describe(BOOL),style
long ksNurbs(short degree, BOOL close, long style)   // degree = ПОРЯДОК (степень+1); 4 = кубический
long ksNurbsPoint(LPDISPATCH ksNurbsPointParam)      // x,y,weight=1.0 — на каждый узел
long ksEndObj()                                      // завершить NURBS
long ksPoint(double x, y, long style)                // style=0
long ksLineSeg(double x1,y1, x2,y2, long style)      // уже используется — для polyline
```

> **Источники истины.** Automation-семантика методов — из `docs/Kompas3D_SDK/`. Точные
> C#-имена/типы свойств — из рефлексии по `libs/kompas-interop/`. Где interop расходится со
> справкой, побеждает interop (компиляция регистрозависима): в частности, `ksEllipseParam`
> имеет свойства **`A`/`B`** с заглавной (в справке — `a`/`b`). NURBS `degree` — это *порядок*
> кривой (степень+1), кубический сплайн = порядок **4**.

**Param-структуры** (`ellipse`/`polygon`/`spline`) берутся через
`KompasObject.GetParamStruct(...)` (`_session.Kompas`), заполняются, передаются в метод и
**освобождаются** `ComHelper.Release` — по дисциплине COM-lifetime проекта. Это новый для
`PartModeler` паттерн (раньше — только прямые методы вроде `ksLineSeg`/`ksCircle`).

Стиль точки: `0` (системный стиль точки, см. SDK `pstyles`).

## Новые MCP-инструменты (группа Sketch)

Контракт инструментов (обёртки в `SketchTools`, как существующие `sketch_add_*`;
возвращают строку-подтверждение, id не возвращают):

| Инструмент | Параметры | COM |
|---|---|---|
| `sketch_add_arc_3points` | `sketchId, x1,y1, x2,y2, x3,y3` | `ksArcBy3Points(..., 1)` |
| `sketch_add_arc` | `sketchId, centerX, centerY, radius, startAngle, endAngle, counterClockwise=true` | `ksArcByAngle(xc,yc,rad,f1,f2,dir,1)` |
| `sketch_add_ellipse` | `sketchId, centerX, centerY, semiMajor, semiMinor, angle=0` | `ksEllipse(param)` |
| `sketch_add_polyline` | `sketchId, points:[{x,y}…], closed=false` | цепочка `ksLineSeg(...,1)` |
| `sketch_add_polygon` | `sketchId, centerX, centerY, vertexCount, radius, inscribed=true, angle=0` | `ksRegularPolygon(param, 0)` |
| `sketch_add_spline` | `sketchId, points:[{x,y}…], closed=false` | `ksNurbs(4,close,1)` → `ksNurbsPoint`×N → `ksEndObj()` |
| `sketch_add_point` | `sketchId, x, y` | `ksPoint(x,y,0)` |

### Маппинги контракт → COM

- `counterClockwise=true → direction=1`, `false → direction=-1`.
- `inscribed=true → describe=false` (вершины на окружности `radius`);
  `inscribed=false → describe=true` (стороны касаются окружности).
- `closed → ksNurbs(close=TRUE)` / для polyline замыкающий `ksLineSeg` от последней к первой.
- Сплайн: `order=4` (кубический), на каждый узел `ksNurbsPoint` с `weight=1.0`; узловой вектор
  не задаём (КОМПАС строит автоматически).

### Формат списка точек

`points` — массив объектов `{x, y}` (double). JSON-имена зафиксированы lowercase через
`[JsonPropertyName("x"/"y")]` на record `SketchPoint(double X, double Y)`, чтобы схема
инструмента совпадала с контрактом. Координаты — в плоскости эскиза, мм.

## Валидация (до COM-вызова)

Вынесена в чистые статические guard'ы `SketchGeometry` (unit-тестируемы без COM); бросают
`ArgumentException`/`ArgumentOutOfRangeException`/`ArgumentNullException`:
- `radius > 0`, `semiMajor > 0`, `semiMinor > 0`;
- `vertexCount >= 3`;
- список точек: не `null`, все координаты конечны (`double.IsFinite`), и минимум:
  `polyline` — 2 (а при `closed=true` — 3, иначе площадь не образуется); `spline` — 2.

Возврат COM-метода `0` (или `ksEndObj() == 0` для сплайна) → `InvalidOperationException`
с понятным текстом (паттерн `ksLineSeg вернул 0`).

## Структура кода

`PartModeler` (сейчас 443 строки) совмещает эскизы и операции; 7 новых методов раздуют
эскизную часть. Разбиваем на **partial class**, сохраняя публичный класс, DI и общий
реестр id:

- `PartModeler.cs` — поля, реестры `_sketches`/`_features`, helpers (`GetTopPart`,
  `RequireSketch`/`RequireOpenSketch`, `SelectEdgeBy*`), `ResetCore`/`Dispose`.
- `PartModeler.Sketch.cs` — создание эскиза (`OpenSketch*`, `CreateSketchOn`), все
  примитивы (`AddLine/Circle/Rectangle/Axis` + 7 новых), `CloseSketch*`.
- `PartModeler.Features.cs` — `ExtrudeAsync`/`RevolveAsync`/`CreateFillet`/`CreateChamfer`/
  `RebuildAsync`.

Операции по-прежнему берут `sketch.Entity` из общего `_sketches` — реестр не делится.
Это targeted-улучшение «по ходу работы», контракт не меняется.

## Тестирование

**Unit** (`Category=Unit`, без COM): guard'ы `SketchGeometry` (`RequirePositive`,
`RequireVertexCount`, `RequirePoints` — включая null и не-finite координаты) и маппинги
`ArcDirection` (`counterClockwise→direction`), `PolygonDescribe` (`inscribed→describe`).
(Координат вершин многоугольника вручную не считаем — метод нативный.)

**Integration** (`Category=Integration`, `KompasFixture`): для каждого профилирующего
примитива — открыть эскиз → добавить примитив(ы), образующие замкнутый контур →
`extrude_boss` → проверить правдоподобный объём:
- `polygon`: правильный 6-угольник, `radius=10`, `extrude=5` → объём > 0, согласуется с
  площадью правильного шестиугольника.
- `ellipse`: `semiMajor=10, semiMinor=5, extrude=4` → V ≈ π·a·b·h.
- `arc_3points` / `arc`: дуга + замыкающий отрезок (хорда) → замкнутый сегмент → V > 0.
- `polyline`: треугольник из 3 точек, `closed=true` → V > 0.
- `spline`: замкнутый сплайн по 4+ точкам → V > 0.
- `point`: вызов не падает, эскиз остаётся валидным (точка контур не образует — без
  проверки объёма).

## Влияние на документацию

После реализации обновить (через навык `docs-delegate`): счётчики инструментов/тестов и
перечни в `CLAUDE.md`, `README.md`, `docs/ARCHITECTURE.md`; пометить пакет A выполненным в
анализе пробелов. Память `kompas-step-and-assembly-api` не затрагивается.
