- описание (x3,y3) и сообщение об ошибке: угол выбирает angleType, не точка дуги - тест angleType=Max (лучи 0°/45° → 135° тупой супплемент): min=45/max=135/more=315 - спек дополнен находкой о трёх типах угла 241 тест зелёный, сборка Release чистая. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
16 KiB
Дизайн: радиальный (R) и угловой размеры на виде чертежа через API7
Дата: 2026-05-27 Статус: реализовано и проверено (5 радиальных + 8 угловых интеграционных + 9 unit; всего 240 тестов зелёных); ревью Codex спека учтено
Уточнение по реализации (важно)
Спайк изначально решил, что измеряемый сектор выбирает положение дуги (x3,y3). При реализации
выяснилось иначе: какой угол измерять, выбирает angleType (ksADMinAngle/MaxAngle/MoreAngle),
а (x3,y3) лишь позиционирует размерную дугу и задаёт её радиус. (270° в спайке возникал потому, что
X3/Y3 не были заданы = совпадали с вершиной → вырождение.)
Три типа дают три разных угла (проверено на лучах 0° и 45°):
Min(ksADMinAngle) → 45° (острый);Max(ksADMaxAngle) → 135° (тупой супплемент, 180−45);More(ksADMoreAngle) → 315° (рефлексный, 360−45).
Тесты (#8 More→270 на 90°, Max→135 на 45°) и описания инструмента/сервиса исправлены под это.
Правки по ревью Codex (реализация)
- #1 описание параметра
x3,y3в инструменте переформулировано (точка позиционирует дугу; угол выбираетangleType, не она). - #2 сообщение пост-проверки углового больше не винит
(x3,y3)— указывает на коллинеарность сторон. - #3 добавлен интеграционный тест
angleType=Max(лучи 0°/45° → 135°) — покрыл маппингksADMaxAngle, отличный отMin/More. - #4 (откат радиального не тестируется) — отклонено с обоснованием: как у диаметрального
(
radius>0→ всегдаValid,value=radius>0; детерминированного пост-Add сбоя нет), код отката структурно идентичен протестированному угловому (коллинеарный путь, счётчик не меняется).
Правки по ревью 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): angleType=More на геометрии 90° → Value ≈ 270; angleType=Max
на лучах 0°/45° → Value ≈ 135 (тупой супплемент). Какой угол измерять, выбирает 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).