feat: коническое отверстие (hole_conic) — ksHTConic + IConicHoleParameters

- ConicHoleAsync (ksHTConic + IConicHoleParameters: ConicType=ksCNAngle, ConicAngle)
- инструмент hole_conic (diameter у дна, conicAngle 0..180); тот же HoleCore-паттерн
- интеграционный тест (конус удаляет больше цилиндра Ø10); типы отверстий завершены
- 70 инструментов, 119 тестов
This commit is contained in:
2026-05-27 12:17:28 +03:00
parent b8dbe48fad
commit 675e43160e
3 changed files with 56 additions and 0 deletions
@@ -71,6 +71,23 @@ public sealed class HoleService
});
}, ct);
/// <summary>Коническое отверстие (ksHTConic): конусность задаётся углом conicAngle (градусы).
/// diameter — диаметр у дна, конус расширяется к поверхности.</summary>
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<IHole3D>? configure)
{
+15
View File
@@ -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<string> 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<string> Rebuild()
@@ -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); }
}
}