From 143877c6ccde4a30949db57d89922503206cb7b7 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 10:28:23 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20d?= =?UTF-8?q?raft=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20Codex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - угол уклона вынесен в чистый SketchGeometry.RequireDraftAngle (0 DraftAsync(IReadOnlyList faceIndices, BasePlane neutralPlane, double angle, bool outward = false, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => { - if (!double.IsFinite(angle) || angle <= 0 || angle >= 90) - throw new ArgumentOutOfRangeException(nameof(angle), "Угол уклона должен быть в (0; 90) градусов."); + SketchGeometry.RequireDraftAngle(angle, nameof(angle)); ArgumentNullException.ThrowIfNull(faceIndices); var indices = faceIndices.Distinct().ToList(); if (indices.Count == 0) throw new ArgumentException("Нужна хотя бы одна грань для уклона.", nameof(faceIndices)); diff --git a/src/Kompas.Mcp.Core/Modeling/SketchGeometry.cs b/src/Kompas.Mcp.Core/Modeling/SketchGeometry.cs index 1907fc6..a8c36d2 100644 --- a/src/Kompas.Mcp.Core/Modeling/SketchGeometry.cs +++ b/src/Kompas.Mcp.Core/Modeling/SketchGeometry.cs @@ -41,6 +41,13 @@ public static class SketchGeometry throw new ArgumentOutOfRangeException(paramName, value, $"Значение должно быть >= {min}."); } + /// Требовать угол уклона: конечный и строго в диапазоне (0; 90) градусов. + public static void RequireDraftAngle(double angle, string paramName) + { + if (!double.IsFinite(angle) || angle <= 0 || angle >= 90) + throw new ArgumentOutOfRangeException(paramName, angle, "Угол уклона должен быть в (0; 90) градусов."); + } + /// Валидировать список точек: не null, минимум , все координаты конечны. public static void RequirePoints(IReadOnlyList<(double x, double y)> points, int min, string paramName) { diff --git a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs index 6e196cd..e280357 100644 --- a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs +++ b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs @@ -199,11 +199,11 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol } [McpServerTool(Name = "draft")] - [Description("Уклон граней: наклонить грани faceIndices (из list_faces) на угол angle (градусы, 0..90) относительно нейтральной (опорной) координатной плоскости neutralPlane (XOY|XOZ|YOZ) — сечение тела в этой плоскости остаётся неизменным, грани уклоняются вокруг линии пересечения с ней. outward=true — расширение (материал добавляется), false — сужение. Грани должны граничить/пересекаться с нейтральной плоскостью. Возвращает id операции.")] + [Description("Уклон граней: наклонить грани faceIndices (из list_faces) на угол angle (градусы, строго 0 < angle < 90) относительно нейтральной (опорной) координатной плоскости neutralPlane (XOY|XOZ|YOZ) — сечение тела в этой плоскости остаётся неизменным, грани уклоняются вокруг линии пересечения с ней. outward=true — расширение (материал добавляется), false — сужение. Грани должны граничить/пересекаться с нейтральной плоскостью. Возвращает id операции.")] public async Task Draft( [Description("Индексы уклоняемых граней из list_faces")] int[] faceIndices, [Description("Нейтральная (опорная) плоскость: XOY|XOZ|YOZ")] string neutralPlane, - [Description("Угол уклона, градусы (0..90)")] double angle, + [Description("Угол уклона, градусы (0 < angle < 90)")] double angle, [Description("Расширение (true) или сужение (false)")] bool outward = false) { await session.ConnectAsync(); diff --git a/tests/Kompas.Mcp.Tests/Integration/FeatureOpsTests.cs b/tests/Kompas.Mcp.Tests/Integration/FeatureOpsTests.cs index a9f7a74..4d01397 100644 --- a/tests/Kompas.Mcp.Tests/Integration/FeatureOpsTests.cs +++ b/tests/Kompas.Mcp.Tests/Integration/FeatureOpsTests.cs @@ -314,6 +314,37 @@ public sealed class FeatureOpsTests finally { await _docs.CloseAsync(save: false); } } + [Fact] + public async Task Draft_outward_expands_box() + { + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + // Та же коробка 40×40×20 (низ на Z=0). + var s = await _modeler.OpenSketchAsync(BasePlane.XOY); + await _modeler.AddRectangleAsync(s, -20, -20, 20, 20); + await _modeler.CloseSketchAsync(s); + await _modeler.ExtrudeAsync(s, depth: 20); + var before = (await _query.GetPartInfoAsync()).Volume; // 32000 + + // 4 боковые грани под уклон 10° НАРУЖУ (outward=true) → верх расширяется, объём растёт. + // Страхует маппинг направления (def.direction = !outward). + var faces = await _query.ListFacesAsync(); + var sides = faces.Where(f => f.Type == "plane" && Math.Abs(f.Area - 800) < 1).Select(f => f.Index).ToArray(); + var id = await _modeler.DraftAsync(sides, BasePlane.XOY, angle: 10, outward: true); + Assert.True(id > 0); + await _modeler.RebuildAsync(); + + // Верх ≈ 40 + 2·20·tan10° = 47.05; V = (20/3)(1600 + 47.05² + √(1600·47.05²)) ≈ 37970. + var after = (await _query.GetPartInfoAsync()).Volume; + var top = 40 + 2 * 20 * Math.Tan(10 * Math.PI / 180); + var expected = (20.0 / 3) * (1600 + top * top + Math.Sqrt(1600 * top * top)); + Assert.True(after > before, "Расширяющий уклон должен увеличить объём (маппинг outward)."); + Assert.InRange(after, expected * 0.9, expected * 1.1); + } + finally { await _docs.CloseAsync(save: false); } + } + [Fact] public async Task Loft_two_squares_makes_solid() { diff --git a/tests/Kompas.Mcp.Tests/SketchGeometryTests.cs b/tests/Kompas.Mcp.Tests/SketchGeometryTests.cs index a5e41ef..ddcd243 100644 --- a/tests/Kompas.Mcp.Tests/SketchGeometryTests.cs +++ b/tests/Kompas.Mcp.Tests/SketchGeometryTests.cs @@ -53,6 +53,23 @@ public sealed class SketchGeometryTests Assert.Null(Record.Exception(() => SketchGeometry.RequireMin(5, 2, "count"))); } + [Theory] + [InlineData(0)] + [InlineData(90)] + [InlineData(-5)] + [InlineData(120)] + [InlineData(double.NaN)] + [InlineData(double.PositiveInfinity)] + public void RequireDraftAngle_rejects_out_of_range(double angle) + => Assert.Throws(() => SketchGeometry.RequireDraftAngle(angle, "angle")); + + [Theory] + [InlineData(0.1)] + [InlineData(45)] + [InlineData(89.9)] + public void RequireDraftAngle_passes_in_range(double angle) + => Assert.Null(Record.Exception(() => SketchGeometry.RequireDraftAngle(angle, "angle"))); + [Fact] public void RequirePoints_throws_on_null() => Assert.Throws(