461e731857
Имя (name) прокидывается до создания объекта: API5 — ksEntity.name перед Create(), API7-пути (примитив, отверстие, булева, рез, перенос грани) — IModelObject.Name перед Update(). Читается назад через ksFeature.name, который только на чтение. Без имени дерево состоит из «Эскиз:1» и «Элемент выдавливания:3». fillet_edge/chamfer_edge принимают список точек: на теле с сотнями рёбер list_edges неподъёмен, а выбор по точке брал одно ребро — четыре угла давали четыре операции. Ненайденные рёбра перечисляются ВСЕ сразу, модель при этом не мутируется.
89 lines
4.1 KiB
C#
89 lines
4.1 KiB
C#
using Kompas.Mcp.Core.Documents;
|
|
using Kompas.Mcp.Core.Modeling;
|
|
using Kompas.Mcp.Core.Query;
|
|
|
|
namespace Kompas.Mcp.Tests.Integration;
|
|
|
|
/// <summary>
|
|
/// Интеграция: скругление пакетом по ТОЧКАМ. На теле с сотнями рёбер (надпись!) список из
|
|
/// list_edges неподъёмен по объёму ответа, а выбор по точке раньше брал ровно одно ребро —
|
|
/// четыре угла давали четыре операции в дереве. Теперь это одна операция.
|
|
/// </summary>
|
|
[Trait("Category", "Integration")]
|
|
[Collection(KompasCollection.Name)]
|
|
public sealed class FilletByPointsTests : IntegrationTestBase
|
|
{
|
|
private readonly DocumentService _docs;
|
|
private readonly PartModeler _modeler;
|
|
private readonly ModelInspectionService _inspection;
|
|
private readonly QueryService _query;
|
|
|
|
public FilletByPointsTests(KompasFixture fx) : base(fx)
|
|
{
|
|
_docs = new DocumentService(fx.Session, fx.Dispatcher);
|
|
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
|
|
_inspection = new ModelInspectionService(fx.Session, fx.Dispatcher);
|
|
_query = new QueryService(fx.Session, fx.Dispatcher);
|
|
}
|
|
|
|
/// <summary>Плита 20×10×3 от начала координат: вертикальные рёбра — в её четырёх углах.</summary>
|
|
private async Task PlateAsync()
|
|
{
|
|
await _docs.CreateAsync(KompasDocumentType.Part);
|
|
var s = await _modeler.OpenSketchAsync(BasePlane.XOY, name: "Контур плиты");
|
|
await _modeler.AddRectangleAsync(s, 0, 0, 20, 10);
|
|
await _modeler.CloseSketchAsync(s);
|
|
await _modeler.ExtrudeAsync(s, depth: 3, name: "Плита");
|
|
await _modeler.RebuildAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Four_corners_become_one_fillet_in_the_tree()
|
|
{
|
|
await PlateAsync();
|
|
try
|
|
{
|
|
var before = (await _query.GetPartInfoAsync()).Volume;
|
|
|
|
await _modeler.FilletEdgesByPointsAsync(
|
|
[
|
|
new Point3d(0, 0, 1.5), new Point3d(20, 0, 1.5),
|
|
new Point3d(20, 10, 1.5), new Point3d(0, 10, 1.5),
|
|
], radius: 2, name: "Скругления углов плиты");
|
|
await _modeler.RebuildAsync();
|
|
|
|
var tree = (await _inspection.DescribeModelAsync(InspectionText.ModelSections.Tree)).Features;
|
|
Assert.Single(tree, f => f.Name == "Скругления углов плиты");
|
|
|
|
// Каждый угол теряет (4 − π)·r²/4 · h материала — проверяем, что скруглены все четыре.
|
|
var expected = before - 4 * (4 - Math.PI) / 4 * 2 * 2 * 3;
|
|
Assert.InRange((await _query.GetPartInfoAsync()).Volume, expected * 0.99, expected * 1.01);
|
|
}
|
|
finally { await _docs.CloseAsync(save: false); }
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Points_without_an_edge_are_all_listed_at_once()
|
|
{
|
|
await PlateAsync();
|
|
try
|
|
{
|
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _modeler.FilletEdgesByPointsAsync(
|
|
[
|
|
new Point3d(0, 0, 1.5), // ребро есть
|
|
new Point3d(100, 100, 100), // пусто
|
|
new Point3d(-50, -50, -50), // пусто
|
|
], radius: 1));
|
|
|
|
// Обе промахнувшиеся точки названы в ОДНОМ сообщении: иначе агент выясняет их по одной.
|
|
Assert.Contains("[1]", ex.Message, StringComparison.Ordinal);
|
|
Assert.Contains("[2]", ex.Message, StringComparison.Ordinal);
|
|
|
|
// Модель не тронута — операция не создавалась.
|
|
var tree = (await _inspection.DescribeModelAsync(InspectionText.ModelSections.Tree)).Features;
|
|
Assert.DoesNotContain(tree, f => f.Name.Contains("Скругление", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
finally { await _docs.CloseAsync(save: false); }
|
|
}
|
|
}
|