From f13e60c979cfa250e118405bd48ee7b1beea0c6a Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 6 Aug 2026 11:09:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20ClashDetectiveIntegration=20?= =?UTF-8?q?=E5=87=A0=E4=BD=95=E7=BC=93=E5=AD=98=E6=9E=84=E5=BB=BA=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=20ModelItemTreeWalker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuildNonHidddenGeometryItemsCache 栈式 DFS → ModelItemTreeWalker.WalkModels: - IsHidden 剪枝 / IsComposite、HasGeometry 收集 / 空节点深入 / visibleCount 统计 / 双重检查锁 / 异常空缓存 全部语义保持 业务等价验证(Ground 虚拟物体碰撞测试,干净 NW): - 新旧版 totalCollisions 均为 12(stash 前后对比,对象集合一致) - 消费方(GetNonChannelGeometryItemsCache → SpatialHashGrid)为集合语义,顺序无关 - 集成测试 25/25(含 3 个碰撞测试) 注:脏 NW 状态(集成测试残留)下碰撞数会波动(5 vs 12)——非迁移引入,重启后稳定 --- .../Collision/ClashDetectiveIntegration.cs | 72 +++++++------------ 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs index 3211e45..1df4de3 100644 --- a/src/Core/Collision/ClashDetectiveIntegration.cs +++ b/src/Core/Collision/ClashDetectiveIntegration.cs @@ -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(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");