From 8e0ab374af2b4fd96316b953df10dc767bfd8660 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 18:01:02 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20drawing=5Fadd=5Flinear=5Fdimension=20?= =?UTF-8?q?=E2=80=94=20=D0=BB=D0=B8=D0=BD=D0=B5=D0=B9=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=20=D0=BD=D0=B0=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B4=D0=B5=20(API7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Веха 2D-ЧЕРТЁЖ, инкремент 3. DrawingService.AddLinearDimensionAsync: (ISymbols2D Container)view (QI) → LineDimensions.Add → X1..Y3 (локальная СК вида) + Orientation + AutoNominalValue → Update → проверка Valid → ((IDimensionText)dim).NominalValue. Свой enum DimensionOrientation (horizontal/vertical/parallel). Новый инструмент drawing_add_linear_dimension. DrawingViewsResult += ViewNumbers (адресация видов). Находка: размеры НЕ в IView.ObjectCount (он про геометрию IDrawingContainer) — проверка попадания в вид через ISymbols2DContainer.LineDimensions.Count. Ревью Codex спека учтено: RequireDistinctPoints (нулевая длина) + Valid после Update; контракт «первый несистемный вид»; null-check Add; AutoNominalValue явно; ViewNumbers required + текст ответа create_standard_views; тесты адресации/негатив. Тесты: +8 unit (DimensionOrientations, DrawingValidation), +5 integration (DrawingDimensionTests). Итого 131 unit + 70 integration = 201. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...6-05-27-drawing-linear-dimension-design.md | 24 +++- .../Drawings/DimensionOrientation.cs | 41 ++++++ .../Drawings/DrawingDimensionResult.cs | 11 ++ .../Drawings/DrawingService.cs | 80 +++++++++++- .../Drawings/DrawingValidation.cs | 22 ++++ .../Drawings/DrawingViewsResult.cs | 3 + src/Kompas.Mcp.Host/Tools/DrawingTools.cs | 26 +++- .../DimensionOrientationsTests.cs | 24 ++++ .../DrawingValidationTests.cs | 15 +++ .../Integration/DrawingDimensionTests.cs | 122 ++++++++++++++++++ 10 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs create mode 100644 src/Kompas.Mcp.Core/Drawings/DrawingDimensionResult.cs create mode 100644 tests/Kompas.Mcp.Tests/DimensionOrientationsTests.cs create mode 100644 tests/Kompas.Mcp.Tests/Integration/DrawingDimensionTests.cs diff --git a/docs/superpowers/specs/2026-05-27-drawing-linear-dimension-design.md b/docs/superpowers/specs/2026-05-27-drawing-linear-dimension-design.md index 984462d..29171eb 100644 --- a/docs/superpowers/specs/2026-05-27-drawing-linear-dimension-design.md +++ b/docs/superpowers/specs/2026-05-27-drawing-linear-dimension-design.md @@ -1,7 +1,29 @@ # Дизайн: линейный размер на виде чертежа (drawing_add_linear_dimension) через API7 **Дата:** 2026-05-27 -**Статус:** спайк зелёный (механизм + авто-значение подтверждены); спроектировано; на ревью (Codex) +**Статус:** реализовано и проверено (5 интеграционных + unit, всего 201); ревью Codex спека учтено + +## Находка реализации: размеры НЕ в 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) — будущее. ## Цель diff --git a/src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs b/src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs new file mode 100644 index 0000000..29e6e8f --- /dev/null +++ b/src/Kompas.Mcp.Core/Drawings/DimensionOrientation.cs @@ -0,0 +1,41 @@ +using Kompas6Constants; + +namespace Kompas.Mcp.Core.Drawings; + +/// Ориентация линейного размера (обёртка над ). +public enum DimensionOrientation +{ + /// Горизонтальный (измеряет |X2−X1|). + Horizontal, + /// Вертикальный (измеряет |Y2−Y1|). + Vertical, + /// Параллельный отрезку P1→P2 (реальная длина). + Parallel, +} + +/// Маппинг ориентации размера ↔ перечисление КОМПАС и разбор строки. +public static class DimensionOrientations +{ + public static ksLineDimensionOrientationEnum ToKompas(DimensionOrientation orientation) => orientation switch + { + DimensionOrientation.Horizontal => ksLineDimensionOrientationEnum.ksLinDHorizontal, + DimensionOrientation.Vertical => ksLineDimensionOrientationEnum.ksLinDVertical, + DimensionOrientation.Parallel => ksLineDimensionOrientationEnum.ksLinDParallel, + _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, "Неизвестная ориентация размера."), + }; + + /// Разобрать строку ориентации (англ./рус.) в . + public static DimensionOrientation Parse(string value) + { + ArgumentNullException.ThrowIfNull(value); + return value.Trim().ToLowerInvariant() switch + { + "horizontal" or "h" or "горизонтальный" or "горизонтальная" => DimensionOrientation.Horizontal, + "vertical" or "v" or "вертикальный" or "вертикальная" => DimensionOrientation.Vertical, + "parallel" or "p" or "параллельный" or "параллельная" => DimensionOrientation.Parallel, + _ => throw new ArgumentException( + $"Неизвестная ориентация размера '{value}'. Допустимо: horizontal, vertical, parallel.", + nameof(value)), + }; + } +} diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingDimensionResult.cs b/src/Kompas.Mcp.Core/Drawings/DrawingDimensionResult.cs new file mode 100644 index 0000000..3ccc082 --- /dev/null +++ b/src/Kompas.Mcp.Core/Drawings/DrawingDimensionResult.cs @@ -0,0 +1,11 @@ +namespace Kompas.Mcp.Core.Drawings; + +/// Результат простановки размера на чертеже. +public sealed record DrawingDimensionResult +{ + /// Измеренное значение размера (мм). + public required double Value { get; init; } + + /// Номер вида, на котором поставлен размер. + public required int ViewNumber { get; init; } +} diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs index 7a2d6b8..2e39f68 100644 --- a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs +++ b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs @@ -84,6 +84,79 @@ public sealed class DrawingService ?? throw new InvalidOperationException("У листа оформления нет основной надписи (Stamp)."); } + /// + /// Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) в ЛОКАЛЬНОЙ + /// СК вида (мм); размерная линия проходит через (x3,y3). — номер вида + /// (≤0 = первый несистемный/главный). Значение измеряется автоматически. Возвращает значение и номер вида. + /// + public Task AddLinearDimensionAsync(int viewNumber, double x1, double y1, + double x2, double y2, double x3, double y3, DimensionOrientation orientation, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + AddLinearDimensionCore(viewNumber, x1, y1, x2, y2, x3, y3, orientation), ct); + + private DrawingDimensionResult AddLinearDimensionCore(int viewNumber, double x1, double y1, + double x2, double y2, double x3, double y3, DimensionOrientation orientation) + { + DrawingValidation.RequireFiniteCoords(x1, y1, x2, y2, x3, y3); + DrawingValidation.RequireDistinctPoints(x1, y1, x2, y2); + + var view = FindView(viewNumber); + var symbols = view as ISymbols2DContainer + ?? throw new InvalidOperationException("Вид не приводится к ISymbols2DContainer."); + var dim = symbols.LineDimensions?.Add() + ?? throw new InvalidOperationException("LineDimensions.Add вернул null."); + try + { + dim.X1 = x1; dim.Y1 = y1; dim.X2 = x2; dim.Y2 = y2; dim.X3 = x3; dim.Y3 = y3; + dim.Orientation = DimensionOrientations.ToKompas(orientation); + ((IDimensionText)dim).AutoNominalValue = true; // авто-измерение значения (по умолчанию, явно) + + if (!dim.Update()) + throw new InvalidOperationException("ILineDimension.Update вернул FALSE — размер не построен."); + if (!dim.Valid) + throw new InvalidOperationException("Размер построен невалидным (проверьте точки/ориентацию)."); + + // NominalValue читаем ТОЛЬКО после успешного Update и Valid. + var value = ((IDimensionText)dim).NominalValue; + return new DrawingDimensionResult { Value = value, ViewNumber = view.Number }; + } + catch + { + try { dim.Delete(); } catch { /* лучшее усилие */ } + throw; + } + } + + /// Число линейных размеров на виде по номеру (для проверки/диагностики). Размеры живут + /// в ISymbols2DContainer вида, а НЕ в IView.ObjectCount (тот считает геометрию). + public Task GetViewLineDimensionCountAsync(int viewNumber, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + var symbols = (ISymbols2DContainer)FindView(viewNumber); + return symbols.LineDimensions?.Count ?? 0; + }, ct); + + /// Найти вид: при viewNumber ≤ 0 — первый несистемный (Number != 0); иначе по номеру. + private IView FindView(int viewNumber) + { + var views = RequireActiveDrawingViews(); + foreach (var o in views) + { + if (o is not IView v) continue; + if (viewNumber <= 0) + { + if (v.Number != 0) return v; // первый несистемный + } + else if (v.Number == viewNumber) + { + return v; + } + } + throw new InvalidOperationException(viewNumber <= 0 + ? "На чертеже нет несистемного вида. Сначала создайте виды: drawing_create_standard_views." + : $"Вид с номером {viewNumber} не найден на чертеже."); + } + // Стандартная тройка проекций: ksPtFront=1, ksPtUp=3, ksPtLeft=5 (ksRelativeProjectionTypeEnum). private static readonly object[] StandardProjections = { 1, 3, 5 }; private const int ExpectedCreated = 3; @@ -122,7 +195,12 @@ public sealed class DrawingService throw new InvalidOperationException( "Стандартные виды созданы, но пусты (модель не спроецировалась). Проверьте файл модели."); - return new DrawingViewsResult { Created = newViews.Count, Total = views.Count }; + return new DrawingViewsResult + { + Created = newViews.Count, + Total = views.Count, + ViewNumbers = newViews.Select(v => v.Number).ToArray(), + }; } catch { diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs b/src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs index bb9d0c3..032a2bc 100644 --- a/src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs +++ b/src/Kompas.Mcp.Core/Drawings/DrawingValidation.cs @@ -32,4 +32,26 @@ public static class DrawingValidation if (!double.IsFinite(scale) || scale <= 0) throw new ArgumentOutOfRangeException(nameof(scale), "Масштаб должен быть конечным и > 0."); } + + /// Проверить, что все координаты конечны (отсекает NaN/Infinity). + public static void RequireFiniteCoords(params double[] coords) + { + foreach (var c in coords) + if (!double.IsFinite(c)) + throw new ArgumentException("Координаты должны быть конечными числами (не NaN/Infinity)."); + } + + /// Минимальное расстояние между выносными точками линейного размера (мм). + private const double MinPointDistance = 1e-7; + + /// + /// Проверить, что выносные точки размера различны (иначе размер вырожден — нулевая длина). + /// + public static void RequireDistinctPoints(double x1, double y1, double x2, double y2) + { + var dx = x2 - x1; + var dy = y2 - y1; + if (Math.Sqrt(dx * dx + dy * dy) < MinPointDistance) + throw new ArgumentException("Выносные точки размера совпадают — нельзя поставить размер нулевой длины."); + } } diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingViewsResult.cs b/src/Kompas.Mcp.Core/Drawings/DrawingViewsResult.cs index 336cec7..fb36be9 100644 --- a/src/Kompas.Mcp.Core/Drawings/DrawingViewsResult.cs +++ b/src/Kompas.Mcp.Core/Drawings/DrawingViewsResult.cs @@ -8,4 +8,7 @@ public sealed record DrawingViewsResult /// Общее число видов на чертеже после операции (включая системный вид). public required int Total { get; init; } + + /// Номера созданных видов (для адресации при простановке размеров). + public required IReadOnlyList ViewNumbers { get; init; } } diff --git a/src/Kompas.Mcp.Host/Tools/DrawingTools.cs b/src/Kompas.Mcp.Host/Tools/DrawingTools.cs index 9ca2ad8..9341593 100644 --- a/src/Kompas.Mcp.Host/Tools/DrawingTools.cs +++ b/src/Kompas.Mcp.Host/Tools/DrawingTools.cs @@ -24,7 +24,9 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing) { await session.ConnectAsync(); var r = await drawing.CreateStandardViewsAsync(partFilePath, scale, x, y); - return $"Создано {r.Created} стандартных вида на чертеже (всего видов: {r.Total}, масштаб {scale})."; + var numbers = string.Join(", ", r.ViewNumbers); + return $"Создано {r.Created} стандартных вида на чертеже (всего видов: {r.Total}, масштаб {scale}). " + + $"Номера созданных видов: {numbers} (для drawing_add_linear_dimension)."; } [McpServerTool(Name = "drawing_fill_title_block")] @@ -41,4 +43,26 @@ public sealed class DrawingTools(KompasSession session, DrawingService drawing) var filled = await drawing.FillTitleBlockAsync(designation, name, material); return $"Заполнено граф основной надписи: {filled}."; } + + [McpServerTool(Name = "drawing_add_linear_dimension")] + [Description("Поставить линейный размер на виде активного чертежа между точками (x1,y1)-(x2,y2) " + + "в ЛОКАЛЬНОЙ СК вида (мм; при масштабе 1:1 = координаты модели на плоскости вида). Размерная " + + "линия проходит через (x3,y3). orientation: horizontal (|X2-X1|) | vertical (|Y2-Y1|) | parallel " + + "(длина отрезка). viewNumber — номер вида из drawing_create_standard_views (0 = первый/главный вид). " + + "Значение измеряется автоматически. Возвращает измеренное значение и номер вида.")] + public async Task AddLinearDimension( + [Description("X первой выносной точки (СК вида, мм)")] double x1, + [Description("Y первой выносной точки")] double y1, + [Description("X второй выносной точки")] double x2, + [Description("Y второй выносной точки")] double y2, + [Description("X положения размерной линии")] double x3, + [Description("Y положения размерной линии")] double y3, + [Description("Ориентация: horizontal | vertical | parallel")] string orientation = "horizontal", + [Description("Номер вида (0 = первый/главный)")] int viewNumber = 0) + { + await session.ConnectAsync(); + var o = DimensionOrientations.Parse(orientation); + var r = await drawing.AddLinearDimensionAsync(viewNumber, x1, y1, x2, y2, x3, y3, o); + return $"Линейный размер ({orientation}) поставлен на виде {r.ViewNumber}, значение {r.Value:0.###} мм."; + } } diff --git a/tests/Kompas.Mcp.Tests/DimensionOrientationsTests.cs b/tests/Kompas.Mcp.Tests/DimensionOrientationsTests.cs new file mode 100644 index 0000000..5cdeaeb --- /dev/null +++ b/tests/Kompas.Mcp.Tests/DimensionOrientationsTests.cs @@ -0,0 +1,24 @@ +using Kompas.Mcp.Core.Drawings; + +namespace Kompas.Mcp.Tests; + +[Trait("Category", "Unit")] +public sealed class DimensionOrientationsTests +{ + [Theory] + [InlineData("horizontal", DimensionOrientation.Horizontal)] + [InlineData("vertical", DimensionOrientation.Vertical)] + [InlineData("parallel", DimensionOrientation.Parallel)] + [InlineData("ГОРИЗОНТАЛЬНЫЙ", DimensionOrientation.Horizontal)] + [InlineData(" vertical ", DimensionOrientation.Vertical)] + public void Parse_maps_known(string value, DimensionOrientation expected) + => Assert.Equal(expected, DimensionOrientations.Parse(value)); + + [Fact] + public void Parse_throws_on_unknown() + => Assert.Throws(() => DimensionOrientations.Parse("diagonal")); + + // ToKompas (→ ksLineDimensionOrientationEnum) намеренно НЕ покрыт unit-тестом: тестовый проект + // не ссылается на Kompas6Constants (граница проекта). Маппинг тривиален и проверяется интеграцией + // (horizontal → |X2-X1|, vertical → |Y2-Y1|). +} diff --git a/tests/Kompas.Mcp.Tests/DrawingValidationTests.cs b/tests/Kompas.Mcp.Tests/DrawingValidationTests.cs index 0b5ee7b..ab20534 100644 --- a/tests/Kompas.Mcp.Tests/DrawingValidationTests.cs +++ b/tests/Kompas.Mcp.Tests/DrawingValidationTests.cs @@ -53,4 +53,19 @@ public sealed class DrawingValidationTests [InlineData(2.0)] public void RequireScale_accepts_positive(double scale) => Assert.Null(Record.Exception(() => DrawingValidation.RequireScale(scale))); + + [Fact] + public void RequireFiniteCoords_throws_on_non_finite() + { + Assert.Throws(() => DrawingValidation.RequireFiniteCoords(0, double.NaN)); + Assert.Throws(() => DrawingValidation.RequireFiniteCoords(double.PositiveInfinity)); + Assert.Null(Record.Exception(() => DrawingValidation.RequireFiniteCoords(0, 1, -5, 40))); + } + + [Fact] + public void RequireDistinctPoints_throws_on_coincident() + { + Assert.Throws(() => DrawingValidation.RequireDistinctPoints(10, 20, 10, 20)); + Assert.Null(Record.Exception(() => DrawingValidation.RequireDistinctPoints(0, 0, 40, 0))); + } } diff --git a/tests/Kompas.Mcp.Tests/Integration/DrawingDimensionTests.cs b/tests/Kompas.Mcp.Tests/Integration/DrawingDimensionTests.cs new file mode 100644 index 0000000..4e4cbdb --- /dev/null +++ b/tests/Kompas.Mcp.Tests/Integration/DrawingDimensionTests.cs @@ -0,0 +1,122 @@ +using Kompas.Mcp.Core.Documents; +using Kompas.Mcp.Core.Drawings; +using Kompas.Mcp.Core.Modeling; + +namespace Kompas.Mcp.Tests.Integration; + +/// Интеграция: линейный размер на виде чертежа (API7 ISymbols2DContainer.LineDimensions). +[Trait("Category", "Integration")] +[Collection(KompasCollection.Name)] +public sealed class DrawingDimensionTests : IntegrationTestBase +{ + private readonly DocumentService _docs; + private readonly PartModeler _modeler; + private readonly DrawingService _drawing; + + public DrawingDimensionTests(KompasFixture fx) : base(fx) + { + _docs = new DocumentService(fx.Session, fx.Dispatcher); + _modeler = new PartModeler(fx.Session, fx.Dispatcher); + _drawing = new DrawingService(fx.Session, fx.Dispatcher); + } + + private async Task BuildAndSaveBoxAsync() + { + var path = TestPaths.NewFile(".m3d"); + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + var s = await _modeler.OpenSketchAsync(BasePlane.XOY); + await _modeler.AddRectangleAsync(s, 0, 0, 40, 30); + await _modeler.CloseSketchAsync(s); + await _modeler.ExtrudeAsync(s, depth: 20); + await _modeler.RebuildAsync(); + await _docs.SaveAsAsync(path); + } + finally { await _docs.CloseAsync(save: false); } + return path; + } + + [Fact] + public async Task Horizontal_dimension_auto_measures_distance() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + Assert.Equal(3, views.ViewNumbers.Count); + + var r = await _drawing.AddLinearDimensionAsync( + viewNumber: 0, x1: 0, y1: 0, x2: 40, y2: 0, x3: 20, y3: -10, DimensionOrientation.Horizontal); + + Assert.InRange(r.Value, 39.9, 40.1); // авто-значение = |X2-X1| + Assert.Contains(r.ViewNumber, views.ViewNumbers); // размер попал в один из видов + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Vertical_dimension_measures_y_span() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + var r = await _drawing.AddLinearDimensionAsync( + viewNumber: 0, x1: 0, y1: 0, x2: 0, y2: 20, x3: -10, y3: 10, DimensionOrientation.Vertical); + Assert.InRange(r.Value, 19.9, 20.1); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Targets_specific_view_by_number_and_increments_its_object_count() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + var targetNumber = views.ViewNumbers[1]; // второй созданный вид (напр. сверху) + + // Размеры живут в ISymbols2DContainer вида — считаем именно их у ЦЕЛЕВОГО вида. + var before = await _drawing.GetViewLineDimensionCountAsync(targetNumber); + var r = await _drawing.AddLinearDimensionAsync( + viewNumber: targetNumber, x1: 0, y1: 0, x2: 40, y2: 0, x3: 20, y3: -10, DimensionOrientation.Horizontal); + var after = await _drawing.GetViewLineDimensionCountAsync(targetNumber); + + Assert.Equal(targetNumber, r.ViewNumber); + Assert.Equal(before + 1, after); // размер попал именно в целевой вид + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Coincident_points_throw() + { + var partFile = await BuildAndSaveBoxAsync(); + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150); + await Assert.ThrowsAsync(() => _drawing.AddLinearDimensionAsync( + viewNumber: 0, x1: 10, y1: 10, x2: 10, y2: 10, x3: 0, y3: 0, DimensionOrientation.Horizontal)); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task No_views_throws() + { + // Чертёж без ассоциативных видов: только системный вид → понятная ошибка. + await _docs.CreateAsync(KompasDocumentType.Drawing); + try + { + await Assert.ThrowsAsync(() => _drawing.AddLinearDimensionAsync( + viewNumber: 0, x1: 0, y1: 0, x2: 40, y2: 0, x3: 20, y3: -10, DimensionOrientation.Horizontal)); + } + finally { await _docs.CloseAsync(save: false); } + } +}