drawing_add_radial_dimension (R окружности/дуги; NominalValue = радиус) и drawing_add_angular_dimension (угол между двумя сторонами; angleType min/max/more выбирает измеряемый сектор) через API7 ISymbols2DContainer. Завершают базовое семейство размеров чертежа (после линейного и диаметрального). - DrawingService: AddRadialDimensionAsync/AddAngularDimensionAsync + счётчики - AngleDimensionType (enum + Parse/ToKompas, по образцу DimensionOrientations) - DrawingDimensionResult.Value — единице-нейтральный (мм / градусы) - 9 unit + 13 интеграционных тестов (всего 240 зелёных) Спайк подтвердил: радиальный NominalValue = РАДИУС (не диаметр, вопреки справке); у углового измеряемый угол выбирает angleType (min=90°/more=270°), а (x3,y3) лишь позиционирует дугу. Спек и ревью Codex — docs/superpowers/specs/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
Дизайн: радиальный (R) и угловой размеры на виде чертежа через API7
Дата: 2026-05-27 Статус: реализовано и проверено (5 радиальных + 8 угловых интеграционных + 9 unit; всего 240 тестов зелёных); ревью Codex спека учтено
Уточнение по реализации (важно)
Спайк изначально решил, что измеряемый сектор выбирает положение дуги (x3,y3). При реализации
выяснилось иначе: сектор (90° vs 270°) выбирает angleType (ksADMinAngle/MaxAngle/MoreAngle),
а (x3,y3) лишь позиционирует размерную дугу и задаёт её радиус. (270° в спайке возникал потому, что
X3/Y3 не были заданы = совпадали с вершиной → вырождение.) Проверено: angleType=Min→90°,
angleType=More→270° на одной геометрии. Тест #8 и описания инструмента/сервиса исправлены под это.
Правки по ревью Codex (спек)
- #1 угловой: добавлен
RequireDistinctPoints(x1,y1,x2,y2)(совпавшие точки сторон → одинаковые лучи → вырождение ещё доAdd). - #3 добавлен тест отката после
Add: коллинеарные сонаправленные лучи (вершина(0,0),(10,0),(20,0)) → угол ≈0 → пост-проверкаvalue<=0→ откатDelete+ ошибка; счётчик угловых не изменился. - #2 поведение
x3,y3(выбор сектора) задокументировано в описании инструмента; добавлен интеграционный тест выбора сектора (та же геометрия,x3,y3в дополнительном секторе → ~270°). Большой/тупой угол НЕ отвергаем — это легитимный результат (рефлексный угол черезmore/положение дуги). - #4 добавлены тесты на несуществующий
viewNumber>0(веткаFindView«вид не найден») для обоих. - #5
DrawingDimensionResult.Value— XML-док станет единице-нейтральным («значение размера»); ответdrawing_add_angular_dimensionформатируется в градусах (°), не «мм». - #6 радиальный использует
DimensionAngles.ToRadians(angleDeg)(как диаметральный), не ручное·π/180.
Цель
Веха 2D-ЧЕРТЁЖ, инкремент 5. После линейного и диаметрального размеров — радиальный (R) и
угловой размеры на виде. Завершают базовое семейство размеров чертежа. Оба переиспользуют
проверенный путь ISymbols2DContainer (QI от IView) и паттерн «Add → set → Update → проверка
Valid → чтение NominalValue → откат Delete при ошибке».
Спайк: поведение подтверждено вживую (НЕ разучивать)
Спайк (_SpikeDimensions, прогнан на реальном КОМПАС, затем удалён) на коробке 40×30×20 с
тремя стандартными видами. Контейнер размеров — (ISymbols2DContainer)view.
Радиальный
IRadialDimension rd = symbols.RadialDimensions.Add();
rd.Xc = 10; rd.Yc = 10; rd.Radius = 15; rd.Angle = π/4; // Angle — РАДИАНЫ (направление выноски)
rd.DimensionType = true; // BOOL: на значение НЕ влияет (стиль)
((IDimensionText)rd).AutoNominalValue = true;
rd.Update(); // True, rd.Valid == True
double value = ((IDimensionText)rd).NominalValue; // == 15 — РАДИУС (не диаметр!)
Ключевой факт (расходится со справкой SDK): у радиального NominalValue возвращает радиус
(Radius=15 → NominalValue=15), а НЕ диаметр. Проверено для DimensionType и true, и false —
оба Valid, значение одинаковое (BOOL влияет лишь на стиль «от центра / нет», не на величину).
Координаты — локальная СК вида, мм. Размер «свободный» (по Xc/Yc/Radius, без BaseObject).
Угловой
IAngleDimension ad = symbols.AngleDimensions.Add(DrawingObjectTypeEnum.ksDrADimension); // =10
ad.Xc = 0; ad.Yc = 0; // вершина угла
ad.X1 = 10; ad.Y1 = 0; // точка на стороне 1 → луч (Xc,Yc)→(X1,Y1)
ad.X2 = 0; ad.Y2 = 10; // точка на стороне 2 → луч (Xc,Yc)→(X2,Y2)
ad.X3 = 5; ad.Y3 = 5; // положение размерной дуги — ЗАДАЁТ ИЗМЕРЯЕМЫЙ СЕКТОР + радиус дуги
ad.DimensionType = ksADMinAngle; // 0=min / 1=max / 2=more180
((IDimensionText)ad).AutoNominalValue = true;
ad.Update(); // True, ad.Valid == True
double value = ((IDimensionText)ad).NominalValue; // == 90 — ГРАДУСЫ
Проверено:
- вершина(0,0), точки (10,0)&(0,10),
X3/Y3=(5,5) → 90° ✓; - вершина(0,0), точки (10,0)&(10,10),
X3/Y3=(8,4) → 45° ✓. X3/Y3критичен: без него тот же угол измерился как 270° (дополнительный сектор). Точка положения дуги задаёт, какой из двух секторов меряется; заодно радиус дуги пересчитывается вdist(вершина, X3/Y3)(заданныйRadiusперетирается → его не экспонируем).- Подход через
Angle1/Angle2(углы наклона сторон) — вырожденный (точки схлопываются к вершине,X1≈0.001), не используем. Геометрию ведут только координаты точек. NominalValue— в градусах.
Add(DrawingObjectTypeEnum) требует код типа: ksDrADimension = 10 (обычный угловой),
ksDrABreakDimension = 39 (с обрывом — вне объёма).
MCP-инструменты
| Инструмент | Параметры | Поведение |
|---|---|---|
drawing_add_radial_dimension |
xc,yc (центр окружности), radius, angle=0 (°), viewNumber=0 |
Радиальный размер (R) окружности/дуги с центром (xc,yc) и радиусом radius (ЛСК вида, мм). angle — направление выноски в ГРАДУСАХ (внутри → радианы). viewNumber: 0 = первый/главный. Значение (= радиус) измеряется автоматически. Возвращает радиус и номер вида. |
drawing_add_angular_dimension |
xc,yc (вершина), x1,y1 (точка на стороне 1), x2,y2 (точка на стороне 2), x3,y3 (положение размерной дуги — задаёт сектор), angleType="min", viewNumber=0 |
Угловой размер между лучами (xc,yc)→(x1,y1) и (xc,yc)→(x2,y2) (ЛСК вида, мм). x3,y3 должны лежать в измеряемом секторе. angleType: min/max/more. Значение (градусы) измеряется автоматически. Возвращает угол (°) и номер вида. |
Углы выноски (angle) экспонируются в градусах (LLM-дружелюбно), внутри ·π/180.
Архитектура
В существующем DrawingService (рядом с AddDiametralDimensionAsync); переиспользуем
RequireSymbols2DContainer(viewNumber), DrawingDimensionResult, паттерн отката.
DrawingService.AddRadialDimensionAsync(viewNumber, xc, yc, radius, angleDeg, ct).DrawingService.AddAngularDimensionAsync(viewNumber, xc, yc, x1, y1, x2, y2, x3, y3, angleType, ct).- Инструменты
drawing_add_radial_dimension,drawing_add_angular_dimensionвDrawingTools. - Helpers
GetViewRadialDimensionCountAsync,GetViewAngularDimensionCountAsync(для тестов — попадание в вид; размеры вISymbols2DContainer.RadialDimensions/AngleDimensions.Count, не вIView.ObjectCount). - Новый файл
src/Kompas.Mcp.Core/Drawings/AngleDimensionType.cs:enum AngleDimensionType {Min,Max,More}- static
AngleDimensionTypes.Parse(string)/ToKompas(...)(→ksADMinAngle/ksADMaxAngle/ksADMoreAngle), по образцуDimensionOrientations.
- static
DrawingDimensionResult (Value + ViewNumber) переиспользуется: радиальный Value=радиус, угловой
Value=градусы.
Валидация (чистые static, unit-тест)
- Радиальный:
RequireFiniteCoords(xc, yc, angleDeg)+RequirePositiveRadius(radius)— уже есть. - Угловой:
RequireFiniteCoords(xc,yc,x1,y1,x2,y2,x3,y3)(8 координат) +RequireDistinctPointsчетырежды: вершина≠(x1,y1), вершина≠(x2,y2), вершина≠(x3,y3) (иначе дуга нулевого радиуса), (x1,y1)≠(x2,y2) (#1: совпавшие точки сторон → одинаковые лучи). Все уже есть; новых валидаторов не требуется.
Реализация
AddRadialDimensionCore (зеркало диаметрального, но значение = радиус): валидация → RequireSymbols2DContainer
→ RadialDimensions.Add() (null-check) → Xc/Yc/Radius, Angle=DimensionAngles.ToRadians(angleDeg) (#6), DimensionType=true
(стиль R от центра, явно), AutoNominalValue=true → Update() (FALSE → откат Delete+ошибка) →
Valid (false → откат) → value = NominalValue (радиус); value<=0 || !IsFinite → откат+ошибка →
DrawingDimensionResult{Value, ViewNumber}.
AddAngularDimensionCore: валидация → RequireSymbols2DContainer → AngleDimensions.Add(ksDrADimension)
(null-check) → Xc/Yc/X1/Y1/X2/Y2/X3/Y3, DimensionType=AngleDimensionTypes.ToKompas(angleType),
AutoNominalValue=true → Update() (FALSE → откат) → Valid (false → откат) → value=NominalValue
(°); value<=0 || !IsFinite → откат+ошибка с подсказкой (вырожденный угол: стороны коллинеарны или
x3,y3 вне сектора) → DrawingDimensionResult{Value, ViewNumber}. Угол в (0,360); защиту по верхней
границе не форсируем (КОМПАС нормализует, more может дать >180).
RCW точечно не освобождаем (консистентно с линейным/диаметральным; долг v2-2).
Тестирование
Unit (DrawingDimensionTypeTests — новый, либо в DrawingValidationTests)
AngleDimensionTypes.Parse:min/max/more(+ рус. синонимы) → enum; неизвестное →ArgumentException.AngleDimensionTypes.ToKompas: маппинг трёх значений →ksADMinAngle/ksADMaxAngle/ksADMoreAngle.- (
ToRadians,RequirePositiveRadius, координаты — уже покрыты.)
Integration (DrawingRadialTests, DrawingAngularTests; наследуют IntegrationTestBase)
Радиальный:
- Значение = радиус:
radius=12 → Value ≈ 12(промотированный спайк). - Попадание в вид:
RadialDimensions.Countцелевого вида +1 (адресация поviewNumber). - Радиус ≤ 0 →
ArgumentOutOfRangeException(доAdd). - Нет видов → понятная ошибка.
Угловой:
5. Измеряет 90°: вершина(0,0), (10,0)&(0,10), x3/y3=(5,5) → Value ≈ 90.
6. Измеряет 45°: вершина(0,0), (10,0)&(10,10), x3/y3=(8,4) → Value ≈ 45.
7. Попадание в вид: AngleDimensions.Count целевого вида +1.
8. Выбор угла типом (#2): та же геометрия 90°, но angleType=More → Value ≈ 270
(документированное поведение: какой из двух углов измерять, выбирает angleType, не x3,y3).
9. Вырожденный угол до Add (вершина совпадает с точкой стороны) → ArgumentException.
10. Откат после Add (#3): коллинеарные сонаправленные лучи (0,0),(10,0),(20,0) → угол ≈0 →
InvalidOperationException; AngleDimensions.Count целевого вида НЕ изменился (откат Delete).
11. Несуществующий viewNumber>0 (#4) → понятная ошибка (ветка FindView).
12. Нет видов → понятная ошибка.
(Радиальный — аналогично добавлен тест на несуществующий viewNumber>0, #4.)
Дальнейшее (вне спека)
- Ассоциативная привязка размеров к геометрии вида (
BaseObject/BaseObject1/2); рамка/формат листа; текстовые обозначения (шероховатостьRoughs, допуски формыTolerances, выноскиLeaders, тех. требования); радиальный с изломом (BreakRadialDimensions), угловой с обрывом (ksDrABreakDimension).