Флаг associate у drawing_add_diametral_dimension/drawing_add_radial_dimension: - associate=false (по умолчанию) — прежний свободный размер (без изменений); - associate=true — (xc,yc,radius) как ключ поиска спроецированной окружности (IDrawingContainer.Circles), привязка через BaseObject; значение Ø/R читается из геометрии и обновляется с моделью. - CircularObjectMatch.SelectMatchIndex (чистый отбор по центру+радиусу, допуск 1 мм; бросает при отсутствии/неоднозначности; концентрические разделяются радиусом) - RequireViewContainers (вид → ISymbols2DContainer + IDrawingContainer), CollectCircles, FindBaseCircle - 5 unit + 6 интеграционных тестов (всего 278 зелёных), сборка Release чистая Спайк подтвердил: BaseObject=circle без задания Xc/Yc/Radius → Valid, NominalValue из геометрии (Ø=20/R=10 для R10). Объём — только окружности (Circles); дуги/угловой/линейный — далее. Ревью Codex спека учтено (оба контейнера, guard вокруг блока, доказательство ассоциативности тестом radius=10.3→Ø20, регресс обоих свободных путей). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Дизайн: ассоциативная привязка диаметрального и радиального размеров к геометрии вида (API7)
Дата: 2026-05-27
Статус: дизайн согласован (вариант A — флаг associate), спайк проведён, ревью Codex спека учтено — к реализации
Правки по ревью Codex (спек)
- #3/#4 объём сужен до окружностей (
IDrawingContainer.Circles): спайк подтвердил привязку только к окружности. Дуги (Arcs) — следующий инкремент со своим спайком (привязкаIArcкакBaseObjectдля диам./радиального не проверена; дуги одного родителя неотличимы по центру+радиусу). - #1 вид резолвится один раз и приводится к обоим контейнерам:
ISymbols2DContainer(создание размера) ИIDrawingContainer(поиск окружности). Новый helperRequireViewContainers(viewNumber)→(IView, ISymbols2DContainer, IDrawingContainer). - #2 весь блок после
…Dimensions.Add()(присвоениеBaseObject/Angle/AutoNominalValue,Update,Valid, read-back) — внутри того жеtry/catchсDeleteпри любом сбое (как свободный путь). - #5 тест доказывает ассоциативность: ключ поиска
radius=10.3(в допуске находит окружность R10) → associate вернёт Ø=20 (из геометрии), свободный дал бы 2·10.3=20.6;Value≈20(не 20.6) доказывает чтение из геометрии. + диагностический read-backBaseObject != null. - #6 обе ветки (свободная/associate) конвертируют
angleчерезDimensionAngles.ToRadians(angleDeg)перед присвоениемdim.Angle. - #7 пост-инвариант значения (
value > 0 && double.IsFinite) сохраняется и в associate-ветке (иначе откат). - #8 регресс-тесты свободного пути (
associate=false) для ОБОИХ — диаметрального и радиального.
Цель
Веха 2D-ЧЕРТЁЖ, инкремент 7. Размеры пока «свободные» (значение из заданных координат). Ассоциативная
привязка делает диаметральный/радиальный размер связанным с проекцией геометрии: значение (Ø/R)
читается из спроецированной окружности/дуги вида через BaseObject и обновляется вместе с моделью.
Первый шаг к «умному» чертежу.
Объём (обоснован рефлексией interop)
Только диаметральный и радиальный, привязка только к окружности (IDrawingContainer.Circles)
— у IDiametralDimension/IRadialDimension есть свойство BaseObject; _Circle реализует
IDrawingObject, поэтому объект из коллекции присваивается dim.BaseObject (подтверждено спайком).
У ILineDimension BaseObject НЕТ; угловой требует BaseObject1/2; привязка к дуге (Arcs) не
проверена — всё это отдельные инкременты (вне объёма).
Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (_SpikeAssocDim, прогнан на реальном КОМПАС, затем удалён): цилиндр (окружность R10, выдавлен
на 20) → стандартные виды. Один из видов содержит спроецированную окружность.
IDrawingContainer dc = (IDrawingContainer)view; // QI от вида (как ISymbols2DContainer)
ICircles circles = dc.Circles; // Count=1; circles[0] as ICircle → Xc=0,Yc=0,R=10
IDiametralDimension dia = symbols.DiametralDimensions.Add();
dia.BaseObject = circles[0]; // IDrawingObject; Xc/Yc/Radius НЕ задаём
dia.Angle = π/4; // направление выноски (размещение)
dia.Update(); // True, Valid=True
double v = ((IDimensionText)dia).NominalValue; // == 20 — ДИАМЕТР, прочитан из геометрии!
Проверено: BaseObject=circle без задания координат → Update=True, Valid=True,
диаметральный NominalValue=20 (=2·R геометрии), радиальный NominalValue=10 (=R). BaseObject
остаётся непустым после. Геометрия вида: IDrawingContainer.Circles (ICircle: Xc/Yc/Radius),
.Arcs (IArc: Xc/Yc/Radius/Angle1/Angle2), .LineSegments. Нет «find by point» — выбор
перебором коллекции.
MCP-инструменты (расширение существующих, вариант A)
К drawing_add_diametral_dimension и drawing_add_radial_dimension добавляется параметр
associate=false:
associate |
Поведение |
|---|---|
false (по умолчанию) |
Текущее «свободное»: Xc/Yc/Radius задаются напрямую (без изменений, обратная совместимость). |
true |
(xc,yc,radius) — ключ поиска: найти в виде окружность/дугу с центром ≈ (xc,yc) и радиусом ≈ radius, привязать BaseObject; значение (Ø/R) читается из геометрии. |
Координаты ключа — view-local (= координаты модели при масштабе 1:1; при ином масштабе — масштабированные,
как у прочих размеров). angle — направление выноски (°), как сейчас. viewNumber — как сейчас.
Выбор объекта
В IDrawingContainer нет «find by point». Перебираем Circles, собираем кандидатов (Xc, Yc, Radius),
ищем подходящий по ключу (xc, yc, radius):
- кандидат подходит, если
dist(центр, ключ) ≤ tolИ|R − radius| ≤ tol(tol = 1.0 мм, константа); - ровно 1 подходящий → привязываем; 0 → ошибка «окружность не найдена рядом с (xc,yc) R≈radius»;
1 → ошибка «неоднозначно — уточните координаты/радиус».
Радиус в ключе снимает неоднозначность концентрических окружностей (цековка R5/R10 — ключ R10 выберет только внешнюю). Логика отбора — чистая (unit-тест), перечисление COM — в сервисе.
Архитектура
DrawingService.AddDiametralDimensionAsync(viewNumber, xc, yc, radius, angleDeg, associate, ct)иAddRadialDimensionAsync(..., associate, ct)— добавленassociate(по умолчаниюfalse). Приfalse— текущая ветка без изменений. Обе ветки:angleRad = DimensionAngles.ToRadians(angleDeg).- При
associate=true:RequireViewContainers(viewNumber)→ собрать кандидатов изCircles(CollectCircles) →CircularObjectMatch.SelectMatchIndex(...)→ взятьIDrawingObjectпо индексу →symbols.{Diametral|Radial}Dimensions.Add()→ внутриtry/catch+Delete:dim.BaseObject = obj,dim.Angle = angleRad,AutoNominalValue=true→Update()(FALSE→откат) →Valid(false→откат) →value = NominalValue(из геометрии);value<=0 || !IsFinite→ откат →DrawingDimensionResult. - Новый helper
RequireViewContainers(viewNumber)→(IView View, ISymbols2DContainer Symbols, IDrawingContainer Drawing)— резолвит вид один раз, оба QI с раздельной диагностикой (переиспользуетFindView). - Новый файл
src/Kompas.Mcp.Core/Drawings/CircularObjectMatch.cs: чистый статическийrecord CircleCandidate(double Xc, double Yc, double Radius)+int SelectMatchIndex(IReadOnlyList<CircleCandidate> candidates, double keyX, double keyY, double keyR, double tol)— возвращает индекс единственного совпадения;InvalidOperationExceptionпри 0 (нет) / >1 (неоднозначно). - Сбор кандидатов из COM (Circles в список с сохранением ссылки на
IDrawingObject+ геометрии) — в сервисе (private helperCollectCircles(IDrawingContainer)→List<(IDrawingObject Obj, CircleCandidate Geom)>). DrawingDimensionResultпереиспользуется (значение из геометрии). Свободный путь не трогаем.
Валидация
- Оба пути:
RequireFiniteCoords(xc, yc, angleDeg)+RequirePositiveRadius(radius)(есть). В associate-режиме radius — положительный ключ поиска. CircularObjectMatch.SelectMatchIndex— чистая логика отбора (unit).
Тестирование
Unit (CircularObjectMatchTests)
- Единственное совпадение по центру+радиусу → индекс.
- Нет кандидатов в допуске →
InvalidOperationException(нет). - Два совпадения в допуске →
InvalidOperationException(неоднозначно). - Концентрические (один центр, разный R): ключ по R выбирает нужный (не неоднозначно).
- Пустой список →
InvalidOperationException.
Integration (DrawingAssociativeDimensionTests; модель — цилиндр R10, как в спайке)
- Диаметральный associative + доказательство геометрии: ключ
(0,0,10.3)(radius в допуске находит окружность R10) →Value ≈ 20(Ø из геометрии, НЕ 2·10.3=20.6 — доказывает чтение из геометрии); попадание в вид (DiametralDimensions.Count+1). - Радиальный associative:
(0,0,10.3)→Value ≈ 10(R из геометрии, не 10.3). - Нет окружности рядом: ключ далеко от геометрии →
InvalidOperationException; счётчик не вырос (откат). - Свободный путь не сломан — диаметральный
associate=false(xc,yc,15)→Value ≈ 30. - Свободный путь не сломан — радиальный
associate=false(xc,yc,12)→Value ≈ 12(регресс обоих). - Нет видов (associate) → понятная ошибка.
Дальнейшее (вне спека)
- Ассоциативная привязка углового (
BaseObject1/2— пара отрезков) и шероховатости (IRough.BaseObject); линейный —BaseObjectотсутствует (нужен иной механизм). Рамка/формат листа; выноски/базы/допуски формы.