Инкремент 9. 83 инструмента, 331 тест. + заметка о ревью реализации в спеке (Codex + pi/glm-5.1 + pi/kimi-k2.6). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40 KiB
Открытые вопросы для проработки
Журнал спорных моментов и решений, принятых автономно (ночная сессия 2026-05-25/26). Помечено: ⚠️ = требует вашего решения; ✅ = решено по умолчанию, можно пересмотреть.
⚠️ 1. API5 vs API7 для построения 3D
Контекст. Примеры SDK строят 3D через API5 (ksPart.NewEntity(o3d_*) →
ksBossExtrusionDefinition → Create()). API7 предлагает IPart7 → IModelContainer →
IExtrusions.Add().
Решение по умолчанию (✅ в коде): в v1 строю 3D через API5 ksPart (надёжно, повторяет
рабочие примеры Step3d1). API7 использую для приложения/документов/версии.
На проработку: переводить ли операции на чистый API7 позже (плюс — единообразие, минус — риск).
✅ 2. Точка входа подключения — API5
Для построения 3D нужен KompasObject (API5), а для снимка — ksDocument3D (API5).
Поэтому KompasSession подключается через KOMPAS.Application.5 → KompasObject, затем
ksGetApplication7() → IApplication. Держит обе ссылки. (Ранее в спайке заходили через .7.)
✅ 3. Состав проектов v1
Вместо 4 проектов (Host/Tools/Core/Interop) сделано компактно: Core (COM + сервисы) +
Host (MCP + определения инструментов) + Tests. Interop — вендорские DLL в libs/.
Разнести на больше проектов можно позже.
✅ 4. Тестовые артефакты
Тестовые документы КОМПАС сохраняю в .scratch/ (gitignore). Снимки рендера — туда же,
для визуальной проверки.
Само-ревью кода (ночная сессия) — итоги
Исправлено сразу:
- C1/C2 — утечки COM и устаревшие id:
PartModelerосвобождает RCW и сбрасывает реестр (ResetAsync/Dispose),DocumentToolsсбрасывает реестр при create/open/close;DocumentServiceосвобождает транзитныеIDocuments/IKompasDocument. - M3 —
KompasSession.Dispose: ограниченное ожидание (5 c) вместо вечного блока. - M4 —
ExtrudeAsync: убранdynamic, конкретныеksBossExtrusionDefinition/ksCutExtrusionDefinition;throughAllразрешён только для выреза. - m7 —
extrude_cut: дефолтthroughAll=false, без молчаливой подменыdepth. - Регрессионный тест: после
ResetAsyncстарый id эскиза недействителен.
⚠️ Отложено на проработку (из ревью):
- M5 — отмена/насос сообщений:
CancellationTokenне прерывает уже идущий COM-вызов; зависший вызов (модальный диалог КОМПАС) блокирует всю очередь. Возможное решение — диспетчер с насосом сообщений + подавление диалогов. Связано с [конкурентностью MCP]. - m6 —
colorBPP=24для снимка: для GIF (палитра) может не подойти; снимок по умолчанию PNG. - m8 — приведения enum к
shortдляGetPart/NewEntity/GetDefaultEntity: на v24 работает (тесты зелёные), но стоит сверить точные типы параметров в interop. - m10 —
_app/_kompasчитаются изIsConnected/Status()вне STA-потока безvolatile(доброкачественное устаревшее чтение). - m11 — трансляция
COMException/HRESULT в понятные сообщения (сейчас понятны только нашиInvalidOperationException); добавить текст последней ошибки КОМПАС.
Ревью v2 (грань/вращение/скругление/фаска/габариты/список граней)
⚠️ Отложено:
- v2-1 — направление операций на грани: для бобышки/выреза на выбранной грани параметр
forwardзависит от ориентации нормали грани, которая для эскиза-на-грани оказывается направленной внутрь тела (boss сforward=trueуходит в материал — прироста нет; наружу даётforward=false). Сейчас выбор направления — на стороне агента (по снимку). На проработку: автоопределение «наружу» по нормали грани, либо опцияauto. - v2-2 — утечки транзитных RCW в
PartModeler:GetTopPart()и коллекцииEntityCollection(o3d_face/o3d_edge)не освобождаются (унаследованный паттерн;QueryServiceих освобождает,PartModeler— только зарегистрированные сущности вResetCore). Накапливается за длинную сессию. На проработку: единыйusing/release транзитов в построителе. Тот же паттерн у новых членов (SelectFaceByIndex,ShellAsync: транзитныеksShellDefinition/FaceArray()/коллекции граней не освобождаются — сознательно, ради консистентности сFillet/Chamfer; войдёт в общий рефакторинг release транзитов). - v2-3 — стабильность индексов граней (
list_faces→sketch_create_on_face_index): индекс валиден, пока геометрия не меняется между вызовами. После любой операции порядок/состав граней может измениться — переиспользовать старый индекс нельзя, нужно перезапроситьlist_faces. - v2-4 —
fillet/chamferстроятся по одному ребру (первое изSelectByPoint/коллекции);tangent=false. На проработку: набор рёбер, скругление по касательным, переменный радиус.
✅ Решено по ходу:
- Угол вращения
revolve_*— в градусах,directionType=dtNormal,toroidShapeType=сфероид,SetThinParam(false,…)для сплошного тела. Ось вращения — осевая линия (системный стиль 3). - Габариты —
ksPart.GetGabarit(full=false)(только тела), координаты в мм.
Журнал решений по ходу
(дополняется автоматически во время работы)
⚠️ Конкурентность вызовов MCP и состояние сессии
MCP-сервер обрабатывает запросы конкурентно. Цикл моделирования с реестром эскизов по id
(sketch_create → sketch_add_*) предполагает, что клиент ждёт ответа на каждый вызов перед
следующим (нормальное поведение LLM-клиентов). При «конвейерной» отправке зависимых вызовов
возможна гонка (id ещё не зарегистрирован). STA-диспетчер сериализует COM, но не порядок постановки задач.
На проработку: нужна ли серверная сериализация вызовов инструментов (очередь) для надёжности
stateful-сессии? Пока полагаемся на последовательное поведение клиента.
✅ Возврат изображения: ImageContentBlock.FromBytes
Поле ImageContentBlock.Data хранит base64-байты, а не сырые. Сырые байты PNG нужно
передавать через ImageContentBlock.FromBytes(bytes, mimeType) — он сам кодирует. Прямое
присваивание Data = rawBytes портит изображение на проводе. Исправлено в VisionTools.
✅ МЦХ (get_part_info): API5 CalcMassInertiaProperties, а не API7
Сначала пробовали API7 IMassInertiaParam7 (от TopPart). Две проблемы:
- Единицы — отдаёт в единицах отображения МЦХ документа (по умолчанию см³/см²/г), зависит от настроек.
- Залипающий кэш — после первого расчёта свойство
ActualостаётсяTRUEи не сбрасывается при изменении геометрии через API5;Calculate()возвращаетFALSEи не пересчитывает. Сценарий «запрос → достройка → запрос» отдавал устаревшее значение. Сброс черезRebuildModel, переключениеMassSettingMode— не помогли.
Решение (✅ в коде): считаем через API5 ksPart.CalcMassInertiaProperties(bitVector) →
ksMassInertiaParam (v/m/F/xc/yc/zc). Считает «по запросу» (без залипания), единицы
задаются битовым вектором: ST_MIX_MM (0x1) | ST_MIX_KG (0x10) → мм³, мм², кг, мм.
Согласовано с тем, что деталь строится через API5 ksPart. Проверено двумя тестами:
цилиндр R10×H20 (V≈6283 мм³, S≈1885 мм², m≈0.0494 кг, Zc=10 мм) и «запрос → штифт на грани → запрос»
(виден корректный прирост ≈785 мм³). Материал по умолчанию — сталь (ρ≈7.856 г/см³).
✅ Снимок 3D: рендер через файл, а не resultArrayBytes
ksDocument3D.SaveAsToRasterFormat(fileName, param) при непустом fileName возвращает TRUE,
пишет корректный PNG на диск, но param.resultArrayBytes остаётся пустым. Поэтому
SnapshotService рендерит во временный файл и читает байты обратно (надёжно проверено на v24 Home).
На проработку: пустое имя файла + returnResultAsArrayBytes=true для чистого in-memory —
не проверено; текущий путь через temp-файл работает.
✅ STEP импорт/экспорт и list_components — реализованы
import_step, export_step (ConversionService, ConversionTools), list_components (QueryService.ListComponentsAsync) добавлены в v2+.
Проверенные COM-приёмы:
- Импорт:
IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)(код формата, не путь к DLL) →IConverter.ConverterParameters(cmd)→IAdditionConvertParameters.Format=ksConverterFromSTEP→IKompasDocument3D1.ConvertFromAdditionFormat(path, prm). - Экспорт:
ConvertToAdditionFormatс форматами AP203/AP214/AP242 (StepFormatenum вCore/Conversion/StepFormat.cs). - Param-коклассы (AdditionConvertParameters) — не CoCreatable, только через фабрики.
- Обход сборки:
IKompasDocument3D.TopPart → IPart7.Parts (IParts7). - Извлечение детали:
prm.NeedCreateComponentsFiles=true+IPart7.OpenSourceDocument.
✅ Прямое редактирование импортированной B-rep — move_face реализован
Решено. Перемещение грани продуктизировано как инструмент move_face / сервис FaceEditService (src/Kompas.Mcp.Core/Editing/FaceEditService.cs). Зарегистрирован в DI (Program.cs); интеграционный тест — Integration/FaceEditTests.cs.
Проверенный паттерн:
- Объект грани берётся через
IPart7.FindObjectsByPoint(x, y, z, true)→ результат приводится кKompasAPI7.IFace. Не требуетEntityCollection/SelectByPointчерез API5. - Контейнеры операций не создаются отдельно:
(ISurfaceContainer)partи(IModelContainer)part— COM-QI отIPart7в рантайме. FaceMover:SetFaces(faces)+Offset(distance)+Direction(true/false = наружу/внутрь)+Update().- Работает как на параметрической геометрии, так и на импортированной B-rep (проверено: деталь top_spacer, высота 39.45 → 41.45 мм).
Полный «рассечь→раздвинуть→объединить» (SplitSolids/BodyRepositions) — механика есть и драйвится, но отдельными MCP-инструментами не оформлена (возможная будущая опция).
✅ Структурный осмотр модели — инспекция реализована
Решено. 7 инструментов (InspectionTools.cs, ModelInspectionService.cs, InspectionText.cs):
describe_model— структурный «паспорт» детали: габарит, МЦХ, тела, сводка топологии, дерево операций с параметрами, переменные. Предпочтителен передmodel_snapshot.list_features/list_bodies/list_variables— детальные срезы.describe_face/describe_edge— drill-down по индексу.measure— расстояние/угол между объектами (ksMeasurer).
Ключевые паттерны: дерево — ksPart.GetFeature() → SubFeatureCollection(true,false); тела — BodyCollection() → ksBody; переменные — VariableCollection() → ksVariable. Детект «импорт без истории»: bodyCount>0 && нет формообразующих операций && features.Count<=bodyCount+1. Итог: 62 инструмента, 101 тест.
✅ hole — реализован через API7
Решено. Инструмент hole добавлен через API7 — в API5 интерфейс ksHoleDefinition отсутствует в interop (o3d_holeOperation=52 есть, но определения нет). Новый сервис HoleService (src/Kompas.Mcp.Core/Modeling/HoleService.cs) не является методом PartModeler.
Паттерн размещения: (IModelContainer)part7 → Points3D.Add() (создаёт IPoint3D как центр) → Holes3D.Add() → IHole3D → (IHoleDisposal)hole (BaseSurface = грань по FindObjectsByPoint, Perpendicular=true, AssociationVertex=point) → Update() → RebuildDocument(). Эскиз размещения не нужен.
Ключевая тонкость — направление по объёму: Update() возвращает TRUE даже если сверлит «в воздух» снаружи тела (убыли материала нет). Решение: попытка Direction=true → Rebuild → сравниваем объём через API5 CalcMassInertiaProperties; если не убыл — пробуем Direction=false. Тот же класс проблем, что у boss/cut на грани (непредсказуемая ориентация нормали). При неуспехе обеих попыток — откат через IFeature7.Delete(). Добавлена валидация double.IsFinite (Infinity проходил мимо >0).
Отверстие не попадает в реестр _features API5 → возвращает подтверждение без id. +3 интеграционных теста (HoleTests.cs).
✅ draft (уклон) — реализован через API5 Incline, а не API7
Решено. Ранее считалось, что уклон недоступен в API5 и требует API7. Это было заблуждением: поиск вёлся по слову "Draft", тогда как операция в API5 называется Incline (ksInclineDefinition, o3d_incline=42). Реализация — стандартный API5-паттерн PartModeler (как shell/rib/loft): NewEntity(o3d_incline) → ksInclineDefinition → FaceArray() (грани по индексам) → SetPlane(координатная плоскость) → angle → direction → Create() → регистрация в _features.
Ключевая тонкость (эмпирика): direction=false=расширение (outward), direction=true=сужение — обратно справке. Маппинг в коде: def.direction = !outward. Тот же класс проблем с направлением, что у boss/cut (v2-1). Валидация угла вынесена в SketchGeometry.RequireDraftAngle(0<angle<90). +9 unit-тестов + 2 integration-теста. Итог: 64 инструмента, 115 тестов (69 unit + 46 integration).
Примечание: o3d_DraftFromEdges=644 / IDraftFromEdges — это другая операция «уклон от базовой линии» (API7), здесь не используется.
✅ Пакет D «параметрика» — CRUD переменных реализован
Решено (start). 3 новых инструмента: create_variable, set_variable, delete_variable — VariableService (src/Kompas.Mcp.Core/Modeling/VariableService.cs) + VariableTools (src/Kompas.Mcp.Host/Tools/VariableTools.cs). Исправлен list_variables в ModelInspectionService.ReadVariables. Итог (на тот момент): 67 инструментов, 116 тестов (69 unit + 47 integration).
Ключевые API-тонкости (эмпирика):
- Создание переменной — ТОЛЬКО через
ksPart.GetFeature().VariableCollection(свойство-коллекция на корневомksFeature).ksPart.VariableCollection()(метод) возвращает только внешние переменные и непригоден для создания пользовательских. - Expression — ведущее поле; Value — вычисленный результат. Изменяют значение через Expression.
- После
ksPart.RebuildModel()RCWksVariableзастревает на старом значении → перечитывать из свежей коллекции (ReadValueFresh). Зависимые переменные пересчитываются автоматически. RemoveVariableвозвращает FALSE при наличии зависимых переменных → удаление невозможно.list_variablesисправлен: ранее читалksPart.VariableCollection()(только внешние), теперьksPart.GetFeature().VariableCollection(все), с фолбэком на старый путь.
⚠️ Осознанное ограничение — геометрия не меняется: переменная управляет геометрией только в параметрической модели, где размеры эскизов привязаны к именам переменных. Наши эскизы строятся литеральными координатами — set_variable хранит и вычисляет значение, но геометрию НЕ меняет.
Связь размеров эскизов с переменными (параметрические эскизы через API2D) — отдельный пласт, не реализован. Это осознанно задокументировано; реализация — продолжение пакета D.
✅ Параметрические эскизы (пакет D продолжение) — исследовано, недоступно через COM-API
Исследовано и закрыто. ksCDimWithVariable (размер с переменной) не конструируется из внешней автоматизации — интерфейс недоступен в COM-клиентском режиме. Связать размеры эскиза с именем переменной через API2D невозможно без исполнения кода внутри процесса КОМПАС (plugin). Подробности — docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md.
✅ Отверстие с цековкой и зенковкой — реализовано
Решено. 2 новых инструмента: hole_counterbore (ksHTCounterbore, ISpotfacingHoleParameters) и hole_countersink (ksHTCountersinking, ICountersinkHoleParameters). HoleCore рефакторен под configure-колбэк. Итог: 69 инструментов, 118 тестов (69 unit + 49 integration).
✅ Коническое отверстие — реализовано
Решено. Новый инструмент hole_conic — коническое (конусное) отверстие, сужающееся вглубь. Параметры: x,y,z, diameter (диаметр у основания), conicAngle (угол конуса, 0<angle<180), depth=0, throughAll=false. Реализован через тот же HoleCore-паттерн (configure-колбэк): HoleType=ksHTConic + configure устанавливает (IConicHoleParameters)hole.HoleParameters (ConicType=ksCNAngle, ConicAngle). Завершает набор типов отверстий: простое (hole), цековка (hole_counterbore), зенковка (hole_countersink), коническое (hole_conic). Итог: 70 инструментов, 119 тестов (69 unit + 50 integration).
✅ Вставка компонента в сборку — реализована (начало класса «Сборки»)
Решено. Новый инструмент assembly_add_component(filePath, x, y, z) — первый инструмент, который пишет в сборку (ранее list_components только читал). Сервис AssemblyService (src/Kompas.Mcp.Core/Assemblies/), инструменты — AssemblyTools. Паттерн API7: IParts7.AddFromFile → IPlacement3D.SetOrigin → UpdatePlacement → RebuildModel(true). Ключевые тонкости: UpdatePlacement возвращает FALSE для вручную позиционируемого компонента — не ошибка; зафиксированный первый компонент двигается через SetOrigin+RebuildModel без разфиксации; RequireActiveAssembly проверяет тип документа до TopPart. Откат при неуспехе через IFeature7.Delete.
✅ Сопряжения в сборках — реализованы (coincidence/distance)
Решено (инкремент 2). Новый инструмент assembly_add_mate(mateType, x1,y1,z1, x2,y2,z2, value=0). Паттерн API7: top.MateConstraints (IMateConstraints3D) → Add(MateConstraintType) → BaseObject1/2 (грани через top.FindObjectsByPoint(x,y,z,FirstLevel=false)) → ParamValue → Update() → RebuildModel(true); проверка mate.Valid. Типы mc_Coincidence=0/mc_Distance=5 проверены вживую. Enum MateType + static Mates в src/Kompas.Mcp.Core/Assemblies/MateType.cs. Итог: 72 инструмента, 156 тестов (98 unit + 58 integration). Следующий шаг: прочие типы сопряжений (parallel, perpendicular, concentric, angle, tangency).
✅ Стандартные виды чертежа — реализованы (начало вехи «2D-чертёж»)
Решено. Новый инструмент drawing_create_standard_views(partFilePath, scale, x, y) — создаёт ассоциативные виды (спереди/сверху/слева) на активном чертеже. Реализовано через API7 DrawingService (namespace Kompas.Mcp.Core.Drawings), DrawingTools. Паттерн: IKompasDocument2D.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", object[]{1,3,5}, x, y, scale, 20, 20) — object[] как SAFEARRAY VT_I4. Проверено вживую: Views.Count 1→4, ObjectCount>0 сразу (rebuild не нужен).
✅ Основная надпись (штамп) чертежа — реализована (инкремент 2 вехи «2D-чертёж»)
Решено. Новый инструмент drawing_fill_title_block(designation?, name?, material?) — заполняет графы основной надписи активного чертежа. Паттерн API7: doc2d.LayoutSheets.ItemByNumber[1].Stamp (IStamp) → stamp.Text[columnId].Str = text → stamp.Update(). IText.Str замещает содержимое напрямую (повторная запись перезаписывает — Clear не нужен, подтверждено round-trip тестом). Номера граф ksStampEnum: обозначение=2, наименование=1, материал=3. Свой enum StampField + StampFields.ColumnId/Collect в src/Kompas.Mcp.Core/Drawings/StampField.cs; инструмент в DrawingTools.cs. Итог: 74 инструмента, 188 тестов (123 unit + 65 integration). Следующий шаг: размеры на видах.
✅ Линейные размеры на видах чертежа — реализованы (инкремент 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).
✅ Радиальный и угловой размеры на виде чертежа — реализованы (инкремент 5 вехи «2D-чертёж»)
Решено. Два новых инструмента завершают базовое семейство размеров:
drawing_add_radial_dimension(xc,yc,radius, angle=0, viewNumber) — радиальный размер (R). Паттерн API7: RequireSymbols2DContainer(viewNumber) → RadialDimensions.Add() → IRadialDimension (Xc, Yc, Radius, Angle в радианах, DimensionType=true, AutoNominalValue=true) → Update() → Valid → NominalValue = радиус. Переиспользует RequirePositiveRadius и DimensionAngles.ToRadians.
drawing_add_angular_dimension(xc,yc, x1,y1, x2,y2, x3,y3, angleType="min", viewNumber) — угловой размер. angleType = "min" (острый) | "max" (тупой) | "more" (рефлексный >180°). Паттерн API7: RequireSymbols2DContainer(viewNumber) → AngleDimensions.Add(ksDrADimension) → IAngleDimension (Xc,Yc,X1,Y1,X2,Y2,X3,Y3, DimensionType=ksAngleDimTypeEnum, AutoNominalValue=true) → Update() → Valid → NominalValue в градусах. Новый enum AngleDimensionType {Min,Max,More} + AngleDimensionTypes.Parse/ToKompas в DimensionOrientation.cs (+9 unit-тестов для маппинга).
Итог: 78 инструментов, 241 тест (150 unit + 91 integration). Базовое семейство размеров завершено — виды, штамп, размеры (линейный, диаметральный, радиальный, угловой). Нереализовано: текстовые обозначения (шероховатость, допуски формы, выноски, тех. требования), ассоциативная привязка, рамка/формат.
✅ Текстовые обозначения на чертеже — реализованы (инкремент 6 вехи «2D-чертёж»)
Решено. Три новых инструмента завершают класс «2D-чертёж» на уровне текстовых аннотаций:
drawing_add_rough(x,y, value?, signType, angle=0, viewNumber) — знак шероховатости на виде. Паттерн API7: ISymbols2DContainer.Roughs.Add() → IRough (BranchX0/Y0/Angle) → (IRoughParams)rough (SignType=ksRoughSignEnum, RoughParamText.Str = value) → Update() → Valid. Новый enum RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial} + RoughSignTypes.Parse/ToKompas в Drawings/RoughSignType.cs. Возвращает DrawingAnnotationResult { Value, ViewNumber }.
drawing_add_text(x,y, text, angle=0, viewNumber) — свободная текстовая надпись на виде. Паттерн API7: (IDrawingContainer)view.DrawingTexts.Add() → IDrawingText (X/Y/Angle) → (IText)dt.Str = text → Update() → Valid. ВАЖНО: текст живёт в IDrawingContainer, а не ISymbols2DContainer; проверяется через DrawingTexts.Count. Возвращает DrawingAnnotationResult { Value, ViewNumber }.
drawing_set_technical_requirements(text) — технические требования документа (уровень документа, не вида). Паттерн API7: (IDrawingDocument)doc.TechnicalDemand → td.Text.Str = text → td.Update(). Перезаписывает прежние. Возвращает число строк.
Итог: 81 инструмент, 267 тестов (163 unit + 104 integration). Нереализовано: ассоциативная привязка размеров и шероховатости к геометрии (BaseObject), рамка/формат листа, выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances).
✅ Ассоциативная привязка диаметрального/радиального к окружности — реализована (инкремент 7 вехи «2D-чертёж»)
Решено (частично). Параметр associate=true добавлен к drawing_add_diametral_dimension и drawing_add_radial_dimension. Реализация (API7, DrawingService): новый класс CircularObjectMatch — отбирает окружность-кандидата в IDrawingContainer.Circles по центру (xc,yc) + радиусу с допуском 1 мм; бросает при отсутствии или неоднозначности (концентрические окружности различаются по радиусу). Хелпер RequireViewContainers получает одновременно ISymbols2DContainer и IDrawingContainer для вида. _Circle реализует IDrawingObject, поэтому dim.BaseObject = circle без дополнительного приведения. associate=false — прежнее «свободное» поведение без изменений. Подтверждено спайком: BaseObject=circle без задания Xc/Yc/Radius → Valid, NominalValue из геометрии (цилиндр R10: Ø=20 / R=10). +5 unit-тестов (CircularObjectMatchTests) + 6 интеграционных (DrawingAssociativeDimensionTests). Итог: 81 инструмент, 278 тестов (168 unit + 110 integration).
Нереализовано (следующие инкременты): привязка к дуге (IDrawingContainer.Arcs), ассоциативный угловой размер (BaseObject1/2, отрезки), ассоциативная шероховатость (IRough.BaseObject); рамка/основная надпись по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов; выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances).
✅ Формат и ориентация листа чертежа — реализованы (инкремент 8 вехи «2D-чертёж»)
Решено. Новый инструмент drawing_set_sheet_format(format, landscape, width?, height?, sheetNumber=1) — задаёт формат и ориентацию активного листа чертежа. Паттерн API7: doc2d.LayoutSheets.ItemByNumber[sheetNumber] → ILayoutSheet.Format (ISheetFormat: Format=ksDocumentFormatEnum, VerticalOrientation=!landscape, FormatWidth/Height только для format=user) → sheet.Update(). Дефолтный лист нового чертежа — A4 книжный. Новые типы: enum PaperFormat {A0,A1,A2,A3,A4,A5,User} + PaperFormats.Parse/ToKompas/FromKompas; record SheetFormatResult; валидатор DrawingValidation.ValidateFormatDimensions; хелпер RequireLayoutSheet. Ключевая тонкость: для format=user КОМПАС выводит VerticalOrientation из соотношения сторон (height>width), флаг landscape в этом случае игнорируется — результат читается read-back из COM. Для стандартных форматов width/height запрещены (должны быть 0). +20 unit-тестов (PaperFormats.Parse, ValidateFormatDimensions) + 10 интеграционных (DrawingSheetFormatTests). Итог: 82 инструмента, 308 тестов (188 unit + 120 integration).
Нереализовано: рамка по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов.
✅ Линия-выноска с текстом — реализована (инкремент 9 вехи «2D-чертёж»)
Решено. Новый инструмент drawing_add_leader(x,y, textX,textY, text, shelfDirection, viewNumber) — ставит линию-выноску с надписью на полке на виде активного чертежа. Паттерн API7: ISymbols2DContainer.Leaders.Add(ksDrLeader) → IBaseLeader → (IBranchs)bl.AddBranchByPoint(0,x,y) (остриё, ответвление ответвлений — обязательно до Update, иначе RPC_E_SERVERFAULT) → SetBranchTextPosition(textX,textY) → (ILeader)bl.TextOnShelf.Str = text → опционально ShelfDirection → Update() → Valid. Новый enum ShelfDirection {Auto,Right,Left,Up,Down} + ShelfDirections.Parse/ToKompas в src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs; переиспользует DrawingAnnotationResult { Value, ViewNumber }. +13 unit-тестов (ShelfDirections.Parse) + 10 интеграционных (DrawingLeaderTests, включая параметризацию направлений полки). Итог: 83 инструмента, 331 тест (201 unit + 130 integration).
Нереализовано: обозначения баз (Bases), допуски формы (Tolerances); привязка к дуге (Arcs), ассоциативный угловой размер, ассоциативная шероховатость (IRough.BaseObject); рамка по ГОСТ-стилю; специальные выноски (позиция, клеймо, маркер).