feat(query): перечисление рёбер + скругление/фаска по индексу

QueryService.ListEdgesAsync: обход EntityCollection(o3d_edge), для каждого ребра —
индекс, тип кривой (line/circle/arc/ellipse/nurbs через ksEdgeDefinition.Is*) и длина
(GetLength, мм). Запись EdgeInfo. Новый инструмент list_edges.
PartModeler: выделены ядра CreateFillet/CreateChamfer и хелпер SelectEdgeByIndex;
добавлены FilletEdgeIndexAsync/ChamferEdgeIndexAsync. Новые инструменты
fillet_edge_index, chamfer_edge_index — выбор ребра по индексу (надёжнее точки).

Тест List_edges_and_fillet_by_index: цилиндр → 2 круговых ребра ≈62.83 мм →
скругление ребра по индексу → объём уменьшается. Итого 31 инструмент, 19 тестов.
Документация синхронизирована через навык docs-delegate (Sonnet).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@
This commit is contained in:
2026-05-26 11:22:57 +03:00
parent 170e2aa4c0
commit 498eeaa847
9 changed files with 211 additions and 52 deletions
+45
View File
@@ -109,6 +109,51 @@ public sealed class QueryService
return "other";
}
/// <summary>Перечислить рёбра активной детали: индекс, тип кривой и длину (мм).</summary>
public Task<IReadOnlyList<EdgeInfo>> ListEdgesAsync(CancellationToken ct = default)
=> _dispatcher.InvokeAsync<IReadOnlyList<EdgeInfo>>(() =>
{
var doc3d = _session.Kompas.ActiveDocument3D() as ksDocument3D
?? throw new InvalidOperationException("Нет активного 3D-документа.");
var part = doc3d.GetPart((short)Part_Type.pTop_Part) as ksPart
?? throw new InvalidOperationException("Не удалось получить вершинный компонент (ksPart).");
try
{
var edges = part.EntityCollection((short)Obj3dType.o3d_edge) as ksEntityCollection
?? throw new InvalidOperationException("Не удалось получить коллекцию рёбер.");
var count = edges.GetCount();
var list = new List<EdgeInfo>(count);
for (int i = 0; i < count; i++)
{
if (edges.GetByIndex(i) is not ksEntity entity) continue;
if (entity.GetDefinition() is not ksEdgeDefinition def) continue;
list.Add(new EdgeInfo
{
Index = i,
Type = EdgeType(def),
Length = def.GetLength(MixMm),
});
}
return list;
}
finally
{
if (Marshal.IsComObject(part)) Marshal.ReleaseComObject(part);
}
}, ct);
private static string EdgeType(ksEdgeDefinition def)
{
if (def.IsLineSeg()) return "line";
if (def.IsCircle()) return "circle";
if (def.IsArc()) return "arc";
if (def.IsEllipseArc()) return "ellipse_arc";
if (def.IsEllipse()) return "ellipse";
if (def.IsNurbs()) return "nurbs";
return "other";
}
/// <summary>Габаритный параллелепипед активной детали (по телам), мм.</summary>
public Task<BoundingBox> GetBoundingBoxAsync(CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>