feat(query): перечисление граней + выбор грани по индексу

QueryService.ListFacesAsync: обход EntityCollection(o3d_face), для каждой грани —
индекс, тип поверхности (plane/cylinder/cone/sphere/torus/nurbs через ksFaceDefinition.Is*)
и площадь (GetArea, мм²). Запись FaceInfo.
PartModeler.OpenSketchOnFaceIndexAsync: эскиз на грани по индексу из list_faces —
надёжнее выбора по точке (агенту не нужно угадывать координаты).
Новые инструменты: list_faces, sketch_create_on_face_index.

Тест List_faces_and_sketch_by_index: цилиндр → 3 грани (2 plane + 1 cylinder),
площади торца ≈314 мм² и бока ≈1257 мм²; эскиз на торце по индексу → бобышка.
Презентация: 28 инструментов, 18 тестов.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@
This commit is contained in:
2026-05-26 10:08:20 +03:00
parent 8c778a449d
commit 8ac92095b6
7 changed files with 149 additions and 8 deletions
@@ -155,6 +155,46 @@ public sealed class ModelingTests
}
}
[Fact]
public async Task List_faces_and_sketch_by_index()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
// Цилиндр R10×H20: 3 грани — 2 плоские (торцы) + 1 цилиндрическая (бок).
var s1 = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddCircleAsync(s1, 0, 0, 10);
await _modeler.CloseSketchAsync(s1);
await _modeler.ExtrudeAsync(s1, depth: 20);
var faces = await _query.ListFacesAsync();
Assert.Equal(3, faces.Count);
Assert.Equal(2, faces.Count(f => f.Type == "plane"));
Assert.Equal(1, faces.Count(f => f.Type == "cylinder"));
// Площадь торца ≈ π·10² ≈ 314 мм²; боковой ≈ 2π·10·20 ≈ 1257 мм².
var planar = faces.First(f => f.Type == "plane");
Assert.InRange(planar.Area, Math.PI * 100 * 0.95, Math.PI * 100 * 1.05);
var side = faces.First(f => f.Type == "cylinder");
Assert.InRange(side.Area, 2 * Math.PI * 10 * 20 * 0.95, 2 * Math.PI * 10 * 20 * 1.05);
// Эскиз на плоской грани по индексу → бобышка (проверяем, что выбор по индексу работает).
var s2 = await _modeler.OpenSketchOnFaceIndexAsync(planar.Index);
await _modeler.AddCircleAsync(s2, 0, 0, 4);
await _modeler.CloseSketchAsync(s2);
var boss = await _modeler.ExtrudeAsync(s2, depth: 8, forward: false);
Assert.True(boss > 0);
await _modeler.RebuildAsync();
// Модель осталась валидной и опрашиваемой.
Assert.True((await _query.GetPartInfoAsync()).Volume > 0);
}
finally
{
await _docs.CloseAsync(save: false);
}
}
[Fact]
public async Task Reset_invalidates_sketch_ids()
{