feat(inspection): структурное «зрение» модели — осмотр без снимка
Новый слой «модель как данные» (приоритетный над model_snapshot): - describe_model — структурный «паспорт» детали одним вызовом (габарит, МЦХ, тела, сводка топологии, дерево построения с параметрами, переменные) - list_features / list_bodies / list_variables - describe_face / describe_edge (drill-down по индексу) - measure (расстояние/угол между гранями/рёбрами/вершинами) - model_snapshot понижен до fallback для визуально-пространственных вопросов Реализация: ModelInspectionService (COM) + чистый InspectionText (рендер, группировка, детект «импорт без истории») + record-модели; InspectionTools. Дерево читается через ksPart.GetFeature()->SubFeatureCollection; точный тип и параметры операций берутся из GetObject()->GetDefinition() (ksFeature.type отдаёт лишь coarse o3d_entity). Снимок дерева также включает слои conversion (STEP), editing (move_face/solid) и validation, развивавшиеся параллельно. Полный прогон: 61 тест зелёный, 43 MCP-инструмента. Документация (README, ARCHITECTURE, OPEN_QUESTIONS, CLAUDE.md, presentation.html) синхронизирована. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,53 @@ public sealed class QueryService
|
||||
}
|
||||
}, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Перечислить компоненты верхнего уровня активной сборки (детали/подсборки): имя,
|
||||
/// обозначение, файл, признак детали и габарит. Обход через API7 <c>TopPart → Parts</c>.
|
||||
/// </summary>
|
||||
public Task<IReadOnlyList<ComponentInfo>> ListComponentsAsync(CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync<IReadOnlyList<ComponentInfo>>(() =>
|
||||
{
|
||||
if (_session.Application.ActiveDocument is not KompasAPI7.IKompasDocument3D doc3d)
|
||||
throw new InvalidOperationException("Активный документ не является 3D-документом.");
|
||||
if (doc3d.TopPart is not KompasAPI7.IPart7 top)
|
||||
throw new InvalidOperationException("Не удалось получить TopPart активного документа.");
|
||||
|
||||
var parts = top.Parts;
|
||||
int count = parts.Count;
|
||||
var list = new List<ComponentInfo>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (parts.Part[i] is not KompasAPI7.IPart7 p) continue;
|
||||
double x1 = 0, y1 = 0, z1 = 0, x2 = 0, y2 = 0, z2 = 0;
|
||||
bool gab = false;
|
||||
try { gab = p.GetGabarit(false, false, out x1, out y1, out z1, out x2, out y2, out z2); }
|
||||
catch { /* габарит может быть недоступен у отдельных компонентов */ }
|
||||
list.Add(new ComponentInfo
|
||||
{
|
||||
Index = i,
|
||||
Name = SafeGet(() => p.Name),
|
||||
Marking = SafeGet(() => p.Marking),
|
||||
FileName = SafeGet(() => p.FileName),
|
||||
IsDetail = SafeBool(() => p.Detail),
|
||||
SizeX = gab ? Math.Abs(x2 - x1) : 0,
|
||||
SizeY = gab ? Math.Abs(y2 - y1) : 0,
|
||||
SizeZ = gab ? Math.Abs(z2 - z1) : 0,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, ct);
|
||||
|
||||
private static string SafeGet(Func<string?> get)
|
||||
{
|
||||
try { return get() ?? string.Empty; } catch { return string.Empty; }
|
||||
}
|
||||
|
||||
private static bool SafeBool(Func<bool> get)
|
||||
{
|
||||
try { return get(); } catch { return false; }
|
||||
}
|
||||
|
||||
// ST_MIX_MM (0x1) — площади граней в мм².
|
||||
private const uint MixMm = 0x1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user