#4: отклонение нулевого NominalValue после Update (ориентация несовместима с точками, напр. horizontal на вертикали |X2-X1|=0) — откат dim.Delete + ошибка. #3: Parallel протестирован — работает БЕЗ явного Angle (КОМПАС авто-вычисляет угол по точкам; (0,0)-(30,40)→50). #7: rollback-ветка покрыта тестом нулевой проекции. #8: контракт «первый несистемный вид» — осознанный (задокументирован). Тесты: DrawingDimensionTests 5→7 (+parallel, +нулевая проекция/откат). Итого 131 unit + 72 integration = 203. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Дизайн: линейный размер на виде чертежа (drawing_add_linear_dimension) через API7
Дата: 2026-05-27 Статус: реализовано и проверено (7 интеграционных + unit, всего 203); ревью Codex спека и реализации учтено
Правки по ревью реализации Codex
- #4 нулевое значение (важно): после
Update+Validпроверяем|NominalValue| > 1e-6— ловит несовместимость ориентации с точками (horizontal для точек на одной вертикали:|X2-X1|=0), которуюRequireDistinctPointsне видит. При нуле — откат (dim.Delete) + ошибка. Тест добавлен. - #3 Parallel (важно): добавлен интеграционный тест — параллельный (0,0)-(30,40) → 50 (3-4-5).
Работает БЕЗ явного
Angle(КОМПАС авто-вычисляет угол по точкам) — Parallel оставлен. - #7 rollback (важно): тест нулевой проекции проходит через ветку
dim.Delete()(откат) — проверяет, что число размеров вида не изменилось. (Update==false/Valid==false без fault injection.) - #8 ассоциативность вида (минор): осознанный контракт «первый НЕсистемный вид» (задокументирован в описании инструмента и ошибке FindView).
Находка реализации: размеры НЕ в IView.ObjectCount
IView.ObjectCount считает геометрию (IDrawingContainer: линии/дуги), а размеры живут в
ISymbols2DContainer отдельно → после простановки размера ObjectCount НЕ меняется (проверено: 4→4).
Поэтому «размер попал в целевой вид» проверяем счётом ISymbols2DContainer.LineDimensions.Count
(helper GetViewLineDimensionCountAsync), а не ObjectCount. (Гарантия непустоты у стандартных видов
по ObjectCount остаётся верной — там именно геометрия.)
Правки по ревью Codex
- Нулевая длина (#1):
DrawingValidation.RequireDistinctPoints(x1,y1,x2,y2)— расстояние между точками > 1e-7 (иначе вырожденный размер); + проверкаdim.ValidпослеUpdate()(откат);- негативный тест.
- Контракт вида (#4): честно — «первый НЕСИСТЕМНЫЙ вид (Number != 0)» при
viewNumber<=0, без претензии на ассоциативность и без QI кIAssociationView(в наших чертежах иных видов нет). - null-check + порядок (#9):
LineDimensions.Add()проверять на null;AutoNominalValue=trueвыставлять явно;NominalValueчитать ТОЛЬКО послеUpdate()==true && Valid==true. - ViewNumbers (#7): поле
requiredвDrawingViewsResult; инициализатор и текст MCP-ответаdrawing_create_standard_viewsобновить (вернуть номера видов). - Тесты (#6): ObjectCount ЦЕЛЕВОГО вида до/после; адресация конкретного
viewNumber; негатив на совпадающие точки. viewNumber не найден (#2) → понятная ошибка. 3D-конвертер точек (#8) — будущее.
Цель
Веха 2D-ЧЕРТЁЖ, инкремент 3 (размеры). После видов и основной надписи — линейный размер на ассоциативном виде: между двумя точками (в локальной СК вида) с авто-измерением длины. Размеры — самый ценный элемент оформления (несут размерную информацию). Начинаем с линейного (горизонтальный/вертикальный/параллельный); диаметральные/радиальные/угловые — далее.
Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (DrawingDimensionSpikeTests, зелёный) подтвердил путь API7:
IView view = первый вид с Number != 0; // системный вид — #0, пропускаем
ISymbols2DContainer symbols = (ISymbols2DContainer)view; // COM-QI ОТ ВИДА (не от документа!)
ILineDimension dim = symbols.LineDimensions.Add();
dim.X1=x1; dim.Y1=y1; dim.X2=x2; dim.Y2=y2; // выносные точки (ЛОКАЛЬНАЯ СК вида, мм)
dim.X3=x3; dim.Y3=y3; // положение размерной линии
dim.Orientation = ksLinDHorizontal; // 0=Parallel / 1=Horizontal / 2=Vertical
bool ok = dim.Update();
double value = ((IDimensionText)dim).NominalValue; // авто-измеренное значение (мм)
Проверено (вид спереди коробки 40×30×20): размер (0,0)-(40,0) горизонтальный →
Update()=True, Valid=True, NominalValue == 40 (авто |X2-X1|), view.ObjectCount вырос.
Подтверждённые факты:
ISymbols2DContainerберётся COM-QI ОТ КОНКРЕТНОГОIView— размер попадает именно в этот вид (не нуженCurrent=true). (Аналогично 3D:part→IModelContainer.)- Координаты X1..Y3 — ЛОКАЛЬНАЯ СК вида (= координаты модели на плоскости вида, при масштабе 1:1 совпадают с мм модели). НЕ координаты листа.
AutoNominalValue=trueпо умолчанию →NominalValue= расстояние между точками (горизонтальный:|X2-X1|, вертикальный:|Y2-Y1|, параллельный: реальная длина). Значение читается((IDimensionText)dim).NominalValue(QI).Orientation(ksLineDimensionOrientationEnum):ksLinDParallel=0,ksLinDHorizontal=1,ksLinDVertical=2(вKompas6Constants).Update()обязателен; перестроение документа не нужно.
Адресация вида (часть инкремента)
Чтобы указать, НА КАКОМ виде ставить размер, нужен его Number. Сейчас
drawing_create_standard_views возвращает только {Created, Total}. Расширяем DrawingViewsResult
полем ViewNumbers (номера созданных видов) — уже собираются как newViews в
CreateStandardViewsCore (тривиально: newViews.Select(v => v.Number)). Это закрывает замечание
Codex из ревью видов (#10: «нет ссылок на созданные виды») и делает размеры адресуемыми.
MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
drawing_add_linear_dimension |
x1,y1,x2,y2 (выносные точки), x3,y3 (положение размерной линии), orientation="horizontal", viewNumber=0 |
Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) в ЛОКАЛЬНОЙ СК вида (мм). orientation: horizontal/vertical/parallel. viewNumber: номер вида (0 = первый ассоциативный/главный). Значение измеряется автоматически. Возвращает измеренное значение и номер вида. |
Контракт: активный документ — чертёж; на чертеже есть хотя бы один ассоциативный вид. Координаты —
локальные вида (см. выше). drawing_create_standard_views возвращает ViewNumbers для адресации.
Архитектура
В существующем DrawingService. Свой enum DimensionOrientation (паттерн MateType/StampField).
src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs— enum + staticDimensionOrientations(Parse(string),ToKompas→ksLineDimensionOrientationEnum).DrawingService.AddLinearDimensionAsync(viewNumber, x1,y1,x2,y2,x3,y3, DimensionOrientation, ct).DrawingViewsResult+=IReadOnlyList<int> ViewNumbers.- Инструмент
drawing_add_linear_dimensionвDrawingTools(orientation строкой → Parse).
Валидация (чистые static, unit-тест)
DimensionOrientations.Parse— horizontal/vertical/parallel (+рус.) → enum; иначеArgumentException.DimensionOrientations.ToKompas— enum →ksLineDimensionOrientationEnum(0/1/2).DrawingValidation.RequireFiniteCoords(params)— координаты конечны (NaN/Infinity → ошибка).
Реализация
AddLinearDimensionCore: валидация координат + orientation → RequireActiveDrawing → найти вид
(viewNumber<=0 → первый с Number!=0; иначе по Number, ошибка если нет) → (ISymbols2DContainer)view
→ LineDimensions.Add() → задать X1..Y3 + Orientation → Update() (FALSE → откат dim.Delete() +
ошибка) → проверить dim.Valid (false → откат + ошибка) → вернуть AddedDimension { Value = ((IDimensionText)dim).NominalValue, ViewNumber = view.Number }. RCW точечно не освобождаем (v2-2).
Тестирование
Unit (DimensionOrientationsTests)
Parse— horizontal/vertical/parallel и рус.-синонимы → enum; бросает на мусоре.ToKompas— каждый → 1/2/0.RequireFiniteCoords— бросает на NaN/Infinity; пропускает конечные.
Integration (DrawingDimensionTests)
Паттерн: построить деталь → сохранить → создать чертёж → стандартные виды → размер → проверить →
finally CloseAsync (база IntegrationTestBase).
- Горизонтальный (0,0)-(40,0) →
Value ≈ 40;view.ObjectCountвырос. - Вертикальный (0,0)-(0,20) →
Value ≈ 20. - Адресация вида:
CreateStandardViewsAsyncвернулViewNumbers(3 шт); размер с конкретнымviewNumberставится в этот вид. - Нет видов: чертёж без видов → понятная ошибка.
(Сценарий 1 = промотированный спайк; спайк-файл удаляется.)
Дальнейшее (вне спека)
- Диаметральные/радиальные/угловые размеры; привязка к геометрии вида (ассоциативно,
BaseObject); допуски/обозначения; рамка/формат листа.