refactor: ClashDetectiveIntegration 几何缓存构建迁移 ModelItemTreeWalker

BuildNonHidddenGeometryItemsCache 栈式 DFS → ModelItemTreeWalker.WalkModels:
- IsHidden 剪枝 / IsComposite、HasGeometry 收集 / 空节点深入 / visibleCount 统计 /
  双重检查锁 / 异常空缓存 全部语义保持

业务等价验证(Ground 虚拟物体碰撞测试,干净 NW):
- 新旧版 totalCollisions 均为 12(stash 前后对比,对象集合一致)
- 消费方(GetNonChannelGeometryItemsCache → SpatialHashGrid)为集合语义,顺序无关
- 集成测试 25/25(含 3 个碰撞测试)

注:脏 NW 状态(集成测试残留)下碰撞数会波动(5 vs 12)——非迁移引入,重启后稳定
This commit is contained in:
tian 2026-08-06 11:09:17 +08:00
parent 818a5e0ad7
commit f13e60c979

View File

@ -1818,60 +1818,38 @@ namespace NavisworksTransport
{
// 优化方案:递归遍历 (Top-Down Traversal)
// 原因Search API 的 Hidden 属性不检查父级可见性,且 IsHidden 属性检查自身。
// 解决方案:使用堆栈进行深度优先搜索(DFS),如果遭遇 Hidden 节点则停止深入该分支。
// 解决方案:统一走 ModelItemTreeWalker 深度遍历,如果遭遇 Hidden 节点则停止深入该分支。
// 这天然地利用了 Navisworks 的层级可见性规则。
_allGeometryItemsCache = new ModelItemCollection();
int visibleCount = 0;
// 使用 Stack 模拟递归避免栈溢出,并保持对 .NET 版本的兼容性
// Stack 初始容量设为 1000 以减少扩容
var stack = new Stack<ModelItem>(1000);
// 将所有模型的根节点压入栈
foreach (var model in Application.ActiveDocument.Models)
{
stack.Push(model.RootItem);
}
while (stack.Count > 0)
{
var item = stack.Pop();
// 关键剪枝:如果节点显式隐藏,则跳过其整个分支(它的所有子节点都不可见)
if (item.IsHidden)
continue;
// 🔥 优化移除:不再在构建时排除运动物体
// 原因:运动物体排除移至查询阶段,通过 _animatedObjectAndDescendants 动态过滤
// 这样基础缓存保持稳定,切换运动物体时无需重建
// if (_animatedObject != null && item.Equals(_animatedObject))
// continue;
// 🔥 优化只提取复合对象IsComposite = true
// 原因:复合对象(如窗户)包含多个实体节点(玻璃、框架、把手)
// 我们只需要检测复合对象,不需要检测其子节点
// Clash Detective API 会自动处理复合对象的子节点几何体
if (item.IsComposite)
ModelItemTreeWalker.WalkModels(
Application.ActiveDocument.Models,
item =>
{
// 复合对象:添加到缓存,不继续遍历子节点
_allGeometryItemsCache.Add(item);
visibleCount++;
}
else if (item.HasGeometry)
{
// 非复合对象但有几何数据(独立实体节点):添加到缓存
_allGeometryItemsCache.Add(item);
visibleCount++;
}
else
{
// 空节点:继续遍历子节点
foreach (var child in item.Children)
// 🔥 优化只提取复合对象IsComposite = true或独立实体节点HasGeometry
// 原因:复合对象(如窗户)包含多个实体节点(玻璃、框架、把手)
// 我们只需要检测复合对象,不需要检测其子节点
// Clash Detective API 会自动处理复合对象的子节点几何体
if (item.IsComposite)
{
stack.Push(child);
// 复合对象:添加到缓存,不继续遍历子节点
_allGeometryItemsCache.Add(item);
visibleCount++;
return ModelItemWalkAction.Prune;
}
}
}
if (item.HasGeometry)
{
// 非复合对象但有几何数据(独立实体节点):添加到缓存
_allGeometryItemsCache.Add(item);
visibleCount++;
return ModelItemWalkAction.Prune;
}
// 空节点:继续遍历子节点
return ModelItemWalkAction.Continue;
});
cacheStopwatch.Stop();
LogManager.Info($"复合对象列表缓存构建完成 (DFS遍历 + 自动剪枝),耗时: {cacheStopwatch.ElapsedMilliseconds}ms");