diff --git a/src/Kompas.Mcp.Core/Modeling/HoleService.cs b/src/Kompas.Mcp.Core/Modeling/HoleService.cs index 7aa6a09..6201c2c 100644 --- a/src/Kompas.Mcp.Core/Modeling/HoleService.cs +++ b/src/Kompas.Mcp.Core/Modeling/HoleService.cs @@ -71,6 +71,23 @@ public sealed class HoleService }); }, ct); + /// Коническое отверстие (ksHTConic): конусность задаётся углом conicAngle (градусы). + /// diameter — диаметр у дна, конус расширяется к поверхности. + public Task ConicHoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll, + double conicAngle, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + if (!double.IsFinite(conicAngle) || conicAngle <= 0 || conicAngle >= 180) + throw new ArgumentOutOfRangeException(nameof(conicAngle), "Угол конуса должен быть в (0; 180) градусов."); + HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTConic, hole => + { + var p = hole.HoleParameters as IConicHoleParameters + ?? throw new InvalidOperationException("HoleParameters не приводится к IConicHoleParameters."); + p.ConicType = ksConicTypeEnum.ksCNAngle; + p.ConicAngle = conicAngle; + }); + }, ct); + private void HoleCore(double x, double y, double z, double diameter, double depth, bool throughAll, ksHoleTypeEnum holeType, Action? configure) { diff --git a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs index 5f7af1a..2e56a22 100644 --- a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs +++ b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs @@ -244,6 +244,21 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol return $"Отверстие с зенковкой Ø{diameter} (зенковка Ø{sinkDiameter}, угол {sinkAngle}°, {kind}) создано в точке ({x}, {y}, {z})."; } + [McpServerTool(Name = "hole_conic")] + [Description("Коническое (конусное) отверстие на грани в точке (x,y,z — центр). diameter — диаметр у дна (мм). conicAngle — угол конуса (градусы, строго 0 < conicAngle < 180); отверстие расширяется к поверхности. depth/throughAll — как у hole. Направление в тело — авто.")] + public async Task HoleConic( + double x, double y, double z, + [Description("Диаметр у дна, мм")] double diameter, + [Description("Угол конуса, градусы (0 < conicAngle < 180)")] double conicAngle, + [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0, + [Description("Сквозное отверстие")] bool throughAll = false) + { + await session.ConnectAsync(); + await holes.ConicHoleAsync(x, y, z, diameter, depth, throughAll, conicAngle); + var kind = throughAll ? "сквозное" : $"глубина {depth} мм"; + return $"Коническое отверстие Ø{diameter} (угол {conicAngle}°, {kind}) создано в точке ({x}, {y}, {z})."; + } + [McpServerTool(Name = "rebuild")] [Description("Перестроить активный документ.")] public async Task Rebuild() diff --git a/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs b/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs index 9f7d00e..c2eac0c 100644 --- a/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs +++ b/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs @@ -147,4 +147,28 @@ public sealed class HoleTests } finally { await _docs.CloseAsync(save: false); } } + + [Fact] + public async Task Conic_hole_removes_tapered_volume() + { + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + 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; + + // Коническое Ø10 у дна, угол 20°, глухое на 15 мм по центру верхней грани. + await _holes.ConicHoleAsync(0, 0, 20, diameter: 10, depth: 15, throughAll: false, conicAngle: 20); + var removed = before - (await _query.GetPartInfoAsync()).Volume; + + // Конус расширяется к поверхности → объём больше цилиндра Ø10×15 (π·25·15≈1178) и в разумных пределах. + var baseCyl = Math.PI * 25 * 15; + Assert.True(removed > baseCyl, "Конусное отверстие должно удалить больше цилиндра Ø10."); + Assert.True(removed < baseCyl * 4, "Объём конуса в разумных пределах."); + } + finally { await _docs.CloseAsync(save: false); } + } }