From e2fb22efe644622381c18b31b53ff52368fe2261 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 21 Jan 2026 21:05:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E8=87=AA=E5=8A=A8=E8=A7=84=E5=88=92?= =?UTF-8?q?=E5=87=A0=E4=BD=95=E4=BD=93=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8DFS=E9=81=8D?= =?UTF-8?q?=E5=8E=86=E5=92=8C=E8=BF=87=E6=BB=A4=EF=BC=8C=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/PathPlanning/GridMapGenerator.cs | 128 +--------------- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 28 +++- src/Utils/ModelItemAnalysisHelper.cs | 142 ++++++++++++++++++ 3 files changed, 165 insertions(+), 133 deletions(-) diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index 8ed1f87..f7a7681 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -1180,131 +1180,7 @@ namespace NavisworksTransport.PathPlanning } /// - /// 使用Search API一次性获取所有有几何体的模型项 - /// 这比逐项调用HasGeometry快得多 - /// - private List GetGeometryItemsUsingSearchAPI() - { - try - { - LogManager.Info("[Search API优化] 开始批量获取几何体项目"); - var startTime = DateTime.Now; - - // 统计模型总数 - var totalModelItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf.Count(); - LogManager.Info($"[Search API优化] 模型总数统计: {totalModelItems} 个模型项"); - - var search = new Search(); - - // Search API阶段:批量筛选基础条件 - search.SearchConditions.Add( - SearchCondition.HasCategoryByName(PropertyCategoryNames.Geometry)); - - search.Selection.SelectAll(); - search.Locations = SearchLocations.DescendantsAndSelf; - - // 一次性获取所有有几何体的项目 - var geometryItems = search.FindAll(Application.ActiveDocument, false); - - var searchElapsed = (DateTime.Now - startTime).TotalMilliseconds; - var filteredByGeometry = totalModelItems - geometryItems.Count(); - var geometryFilterRatio = (double)filteredByGeometry / totalModelItems; - - LogManager.Info($"[Search API优化] Search API阶段完成: 获取 {geometryItems.Count()} 个几何体项目,耗时: {searchElapsed:F1}ms"); - LogManager.Info($"[Search API优化] 几何体筛选效果: 过滤掉 {filteredByGeometry} 个无几何体项目,筛选率: {geometryFilterRatio:P1}"); - - // 🔥 关键优化:使用PrimitiveTypes精确过滤线性几何体 - // 基于多模型测试验证,LINQ筛选条件(IsCollection, IsComposite, Children.Any)都返回0项 - LogManager.Info("[Search API优化] 开始使用PrimitiveTypes过滤线性几何体"); - var primitiveFilterStart = DateTime.Now; - - int triangleCount = 0; - int lineOnlyCount = 0; - int pointOnlyCount = 0; - int snapPointOnlyCount = 0; - int textOnlyCount = 0; - int mixedCount = 0; - int noGeometryCount = 0; - - var filteredItems = geometryItems.Where(item => - { - try - { - var geometry = item.Geometry; - if (geometry == null) - { - noGeometryCount++; - return false; - } - - var primitiveTypes = geometry.PrimitiveTypes; - - // 统计各种图元类型 - if (primitiveTypes.HasFlag(PrimitiveTypes.Triangles)) - { - if (primitiveTypes == PrimitiveTypes.Triangles) - triangleCount++; - else - mixedCount++; - return true; // 包含三角形的几何体都保留 - } - else if (primitiveTypes == PrimitiveTypes.Lines) - { - lineOnlyCount++; - return false; // 纯线条几何体过滤掉 - } - else if (primitiveTypes == PrimitiveTypes.Points) - { - pointOnlyCount++; - return false; // 纯点几何体过滤掉 - } - else if (primitiveTypes == PrimitiveTypes.SnapPoints) - { - snapPointOnlyCount++; - return false; // 纯捕捉点几何体过滤掉 - } - else if (primitiveTypes == PrimitiveTypes.Text) - { - textOnlyCount++; - return false; // 纯文本几何体过滤掉 - } - else - { - mixedCount++; - return true; // 其他混合类型保留 - } - } - catch - { - return false; // 出错时跳过该项目 - } - }).ToList(); - - var primitiveFilterElapsed = (DateTime.Now - primitiveFilterStart).TotalMilliseconds; - var filteredByPrimitives = geometryItems.Count() - filteredItems.Count; - - LogManager.Info($"[Search API优化] PrimitiveTypes过滤完成: 过滤掉 {filteredByPrimitives} 个线性几何体,耗时: {primitiveFilterElapsed:F1}ms"); - LogManager.Info($"[Search API优化] 图元类型统计 - 纯三角形: {triangleCount}, 纯线条: {lineOnlyCount}, 纯点: {pointOnlyCount}, 纯捕捉点: {snapPointOnlyCount}, 纯文本: {textOnlyCount}, 混合类型: {mixedCount}, 无几何体: {noGeometryCount}"); - - var totalElapsed = (DateTime.Now - startTime).TotalMilliseconds; - var totalFilteredCount = totalModelItems - filteredItems.Count; - var totalFilterRatio = (double)totalFilteredCount / totalModelItems; - - LogManager.Info($"[Search API优化] 优化完成,总耗时: {totalElapsed:F1}ms"); - LogManager.Info($"[Search API优化] 优化效果: 总模型项 {totalModelItems} → 几何体项目 {filteredItems.Count}"); - LogManager.Info($"[Search API优化] 总优化率: 减少 {totalFilteredCount} 项 ({totalFilterRatio:P1})"); - - return filteredItems; - } - catch (Exception ex) - { - LogManager.Error($"[Search API优化] 筛选失败: {ex.Message}"); - throw; - } - } - - /// - /// 轻量级后处理:只对Search API预筛选的几何体项目进行必要的属性提取 + /// 轻量级后处理:只对预筛选的几何体项目进行必要的属性提取 /// 大幅减少API调用次数 /// private Dictionary PostProcessGeometryItems( @@ -1406,7 +1282,7 @@ namespace NavisworksTransport.PathPlanning var traversableItemsSet = channelItems ?? new HashSet(); // 阶段1:Search API预筛选(一次性批量获取几何体项目) - var geometryItems = GetGeometryItemsUsingSearchAPI(); + var geometryItems = ModelItemAnalysisHelper.GetAllVisibleGeometryItems(); LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {traversableItemsSet.Count}"); diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 5040a6c..0b2e2d1 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -1595,14 +1595,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels { await SafeExecuteAsync(async () => { - if (!_hasStartPoint || !_hasEndPoint) - { - UpdateMainStatus("请先选择起点和终点"); - return; - } + // 保存当前光标 + var oldCursor = Mouse.OverrideCursor; - UpdateMainStatus("正在计算最优路径...", -1, true); - LogManager.Info("=== 开始执行自动路径规划 ==="); + try + { + if (!_hasStartPoint || !_hasEndPoint) + { + UpdateMainStatus("请先选择起点和终点"); + return; + } + + // 设置等待光标 + Mouse.OverrideCursor = Cursors.Wait; + + UpdateMainStatus("正在计算最优路径...", -1, true); + LogManager.Info("=== 开始执行自动路径规划 ==="); // 确保在开始自动路径规划前清理任何残留的事件订阅 CleanupAutoPathEventSubscriptions(); @@ -1709,6 +1717,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } } + } + finally + { + // 恢复光标 + Mouse.OverrideCursor = oldCursor; + } }, "自动路径规划"); } diff --git a/src/Utils/ModelItemAnalysisHelper.cs b/src/Utils/ModelItemAnalysisHelper.cs index ad698eb..39ba9ec 100644 --- a/src/Utils/ModelItemAnalysisHelper.cs +++ b/src/Utils/ModelItemAnalysisHelper.cs @@ -344,6 +344,148 @@ namespace NavisworksTransport.Utils } #endregion + + #region 几何体获取方法 + + /// + /// 获取所有可见的几何体项目(DFS遍历 + 隐藏项过滤 + 几何体类型过滤) + /// 统一的几何体获取接口,供自动路径规划和动画检测预计算共同使用 + /// + /// 可见的几何体项目列表(只保留包含 Triangles 的几何体) + public static List GetAllVisibleGeometryItems() + { + try + { + LogManager.Info("[几何体获取] 开始获取所有可见几何体项目"); + var startTime = DateTime.Now; + + // 统计模型总数 + var totalModelItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf.Count(); + LogManager.Info($"[几何体获取] 模型总数统计: {totalModelItems} 个模型项"); + + // 使用 Stack 模拟递归避免栈溢出 + // Stack 初始容量设为 1000 以减少扩容 + var stack = new Stack(1000); + var visibleItems = new List(); + + // 将所有模型的根节点压入栈 + foreach (var model in Application.ActiveDocument.Models) + { + stack.Push(model.RootItem); + } + + // 统计信息 + int hiddenSkipped = 0; // 跳过的隐藏节点(包括其子节点) + int visibleCount = 0; // 可见节点总数 + int triangleCount = 0; // 纯三角形几何体 + int lineOnlyCount = 0; // 纯线条几何体(过滤) + int pointOnlyCount = 0; // 纯点几何体(过滤) + int snapPointOnlyCount = 0; // 纯捕捉点几何体(过滤) + int textOnlyCount = 0; // 纯文本几何体(过滤) + int mixedCount = 0; // 混合类型几何体 + int noGeometryCount = 0; // 无几何体节点 + + while (stack.Count > 0) + { + var item = stack.Pop(); + + // 关键剪枝:如果节点显式隐藏,则跳过其整个分支(它的所有子节点都不可见) + if (item.IsHidden) + { + hiddenSkipped++; + continue; + } + + visibleCount++; + + // 🔥 优化:只提取复合对象(IsComposite = true)或独立实体节点(HasGeometry) + // 原因:复合对象(如窗户)包含多个实体节点(玻璃、框架、把手) + // 我们只需要检测复合对象,不需要检测其子节点 + bool isComposite = item.IsComposite; + bool hasGeometry = item.HasGeometry; + + if (isComposite || hasGeometry) + { + // 复合对象或独立实体节点:检查几何体类型 + var geometry = item.Geometry; + + if (geometry != null) + { + var primitiveTypes = geometry.PrimitiveTypes; + + // 统计各种图元类型 + if (primitiveTypes.HasFlag(PrimitiveTypes.Triangles)) + { + if (primitiveTypes == PrimitiveTypes.Triangles) + triangleCount++; + else + mixedCount++; + visibleItems.Add(item); // 只保留包含三角形的几何体 + } + else if (primitiveTypes == PrimitiveTypes.Lines) + { + lineOnlyCount++; + // 不添加:纯线条几何体过滤掉 + } + else if (primitiveTypes == PrimitiveTypes.Points) + { + pointOnlyCount++; + // 不添加:纯点几何体过滤掉 + } + else if (primitiveTypes == PrimitiveTypes.SnapPoints) + { + snapPointOnlyCount++; + // 不添加:纯捕捉点几何体过滤掉 + } + else if (primitiveTypes == PrimitiveTypes.Text) + { + textOnlyCount++; + // 不添加:纯文本几何体过滤掉 + } + else + { + mixedCount++; + visibleItems.Add(item); // 其他混合类型保留 + } + } + else + { + noGeometryCount++; + // 不添加:无几何体数据 + } + + // 复合对象:不继续遍历子节点(Clash Detective API 会自动处理) + // 独立实体节点:也没有子节点 + } + else + { + // 空节点:继续遍历子节点 + foreach (var child in item.Children) + { + stack.Push(child); + } + } + } + + var elapsed = (DateTime.Now - startTime).TotalMilliseconds; + + // 输出详细统计信息 + LogManager.Info($"[几何体获取] DFS遍历完成,总耗时: {elapsed:F1}ms"); + LogManager.Info($"[几何体获取] 隐藏项过滤效果: 跳过 {hiddenSkipped} 个隐藏节点(包括其子节点)"); + LogManager.Info($"[几何体获取] 可见节点统计: {visibleCount} 个可见节点"); + LogManager.Info($"[几何体获取] 图元类型统计 - 纯三角形: {triangleCount}, 纯线条: {lineOnlyCount}, 纯点: {pointOnlyCount}, 纯捕捉点: {snapPointOnlyCount}, 纯文本: {textOnlyCount}, 混合类型: {mixedCount}, 无几何体: {noGeometryCount}"); + LogManager.Info($"[几何体获取] 最终结果: {visibleItems.Count} 个几何体项目(只保留包含 Triangles 的几何体)"); + + return visibleItems; + } + catch (Exception ex) + { + LogManager.Error($"[几何体获取] 获取失败: {ex.Message}"); + throw; + } + } + + #endregion } #region 数据结构定义