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); }
+ }
}