From dba1f76f0a54bd1cef8a6c5abc8fe0469b2a1dad Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 20 Oct 2025 10:39:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=87=A0=E4=BD=95?= =?UTF-8?q?=E4=BD=93=E7=BC=93=E5=AD=98=E6=96=87=E4=BB=B6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NavisworksTransportPlugin.csproj | 2 + src/PathPlanning/VoxelGridGenerator.cs | 27 +- src/Utils/CachedTriangle3D.cs | 82 +++ src/Utils/GeometryCacheManager.cs | 631 +++++++++++++++++++++++ src/Utils/GeometryHelper.cs | 17 +- src/Utils/NavisworksToDMesh3Converter.cs | 1 - 6 files changed, 744 insertions(+), 16 deletions(-) create mode 100644 src/Utils/CachedTriangle3D.cs create mode 100644 src/Utils/GeometryCacheManager.cs diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index 7e31d43..0476d99 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -291,6 +291,7 @@ + @@ -298,6 +299,7 @@ + diff --git a/src/PathPlanning/VoxelGridGenerator.cs b/src/PathPlanning/VoxelGridGenerator.cs index bd38eb2..15325ce 100644 --- a/src/PathPlanning/VoxelGridGenerator.cs +++ b/src/PathPlanning/VoxelGridGenerator.cs @@ -145,7 +145,32 @@ namespace NavisworksTransport.PathPlanning LogManager.Info("开始提取障碍物几何体..."); var geometryStopwatch = Stopwatch.StartNew(); - DMesh3 obstacleMesh = NavisworksToDMesh3Converter.ConvertFromModelItems(filteredItems); + // 使用几何体缓存 + DMesh3 obstacleMesh = null; + var cacheManager = new GeometryCacheManager(Application.ActiveDocument.FileName); + try + { + LogManager.Info($"[体素网格] 从缓存中提取 {filteredItems.Count} 个过滤后项的几何体"); + var allTriangles = new List(); + + int processedItems = 0; + foreach (var item in filteredItems) + { + var itemTriangles = cacheManager.GetTrianglesForModelItem(item); + allTriangles.AddRange(itemTriangles); + processedItems++; + } + + LogManager.Info($"[体素网格] 从缓存中提取完成,总三角形数: {allTriangles.Count}"); + obstacleMesh = NavisworksToDMesh3Converter.Convert(allTriangles); + } + catch (Exception ex) + { + LogManager.Warning($"[体素网格] 使用几何体缓存失败: {ex.Message},回退到传统方法"); + + // 如果缓存不可用,则使用传统方法提取几何体 + obstacleMesh = NavisworksToDMesh3Converter.ConvertFromModelItems(filteredItems); + } geometryStopwatch.Stop(); LogManager.Info($"几何体提取完成,耗时: {geometryStopwatch.ElapsedMilliseconds} ms"); diff --git a/src/Utils/CachedTriangle3D.cs b/src/Utils/CachedTriangle3D.cs new file mode 100644 index 0000000..4c284ed --- /dev/null +++ b/src/Utils/CachedTriangle3D.cs @@ -0,0 +1,82 @@ +using System; +using Autodesk.Navisworks.Api; + +namespace NavisworksTransport +{ + /// + /// 可序列化的点结构体 + /// + [Serializable] + [System.Xml.Serialization.XmlType("CachedPoint3D")] + public struct CachedPoint3D + { + [System.Xml.Serialization.XmlAttribute("X")] + public double X { get; set; } + + [System.Xml.Serialization.XmlAttribute("Y")] + public double Y { get; set; } + + [System.Xml.Serialization.XmlAttribute("Z")] + public double Z { get; set; } + + public CachedPoint3D(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + public CachedPoint3D(Point3D point) + { + X = point.X; + Y = point.Y; + Z = point.Z; + } + + public Point3D ToNavisworksPoint3D() + { + return new Point3D(X, Y, Z); + } + + public override string ToString() + { + return $"Point3D({X:F3}, {Y:F3}, {Z:F3})"; + } + } + + /// + /// 可序列化的三角形结构体 + /// + [Serializable] + [System.Xml.Serialization.XmlType("CachedTriangle3D")] + public struct CachedTriangle3D + { + [System.Xml.Serialization.XmlElement("Point1")] + public CachedPoint3D Point1 { get; set; } + + [System.Xml.Serialization.XmlElement("Point2")] + public CachedPoint3D Point2 { get; set; } + + [System.Xml.Serialization.XmlElement("Point3")] + public CachedPoint3D Point3 { get; set; } + + public CachedTriangle3D(CachedPoint3D point1, CachedPoint3D point2, CachedPoint3D point3) + { + Point1 = point1; + Point2 = point2; + Point3 = point3; + } + + public CachedTriangle3D(Point3D point1, Point3D point2, Point3D point3) + { + Point1 = new CachedPoint3D(point1); + Point2 = new CachedPoint3D(point2); + Point3 = new CachedPoint3D(point3); + } + + public override string ToString() + { + return $"Triangle[{Point1}, {Point2}, {Point3}]"; + } + } +} \ No newline at end of file diff --git a/src/Utils/GeometryCacheManager.cs b/src/Utils/GeometryCacheManager.cs new file mode 100644 index 0000000..65c35b7 --- /dev/null +++ b/src/Utils/GeometryCacheManager.cs @@ -0,0 +1,631 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Autodesk.Navisworks.Api; +using ComApi = Autodesk.Navisworks.Api.Interop.ComApi; +using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge; + +namespace NavisworksTransport.Utils +{ + /// + /// 几何体缓存管理器 + /// 用于缓存模型文档中的几何体信息,提高访问性能 + /// + public class GeometryCacheManager + { + // 缓存根节点 + private PathTreeNode cacheRoot; + + // 当前文档路径 + private string documentPath; + + // 缓存文件路径 + private string cacheFilePath; + + // 缓存加载状态 + private bool _isCacheLoaded = false; + private bool _cacheLoadResult = false; + + /// + /// 构造函数 + /// + /// 文档路径 + public GeometryCacheManager(string docPath) + { + documentPath = docPath; + cacheRoot = new PathTreeNode("", ""); + + // 生成缓存文件路径 + var docDir = Path.GetDirectoryName(documentPath); + var docName = Path.GetFileNameWithoutExtension(documentPath); + cacheFilePath = Path.Combine(docDir, $"{docName}.geometrycache"); + + LogManager.Info($"[几何体缓存] 缓存文件路径: {cacheFilePath}"); + } + + /// + /// 从模型项列表提取所有片段并构建缓存 + /// + /// 模型项列表 + public void BuildCache(IEnumerable modelItems) + { + var itemCount = modelItems?.Count() ?? 0; + LogManager.Info($"[几何体缓存] 开始构建几何体缓存,模型项数量: {itemCount}"); + + // 清空现有缓存 + cacheRoot = new PathTreeNode("", ""); + + ComApi.InwOpSelection comSelection = null; + try + { + // 转换为 COM 选择 + var modelCollection = new ModelItemCollection(); + foreach (var item in modelItems) + { + modelCollection.Add(item); + } + + comSelection = ComApiBridge.ToInwOpSelection(modelCollection); + + // 获取所有片段 + var allFragments = GeometryHelper.GetAllFragments(comSelection); + + int processedFragments = 0; + int totalTriangles = 0; + int skippedDuplicates = 0; + + // 处理每个片段 + foreach (var fragmentInfo in allFragments) + { + try + { + var callback = new OptimizedGeometryCallback(fragmentInfo.TransformMatrix); + fragmentInfo.Fragment.GenerateSimplePrimitives( + ComApi.nwEVertexProperty.eNORMAL, + callback); + + var fragmentTriangles = callback.GetTriangles(); + totalTriangles += fragmentTriangles.Count; + + // 创建缓存片段数据 + var cachedFragment = new CachedFragmentData + { + PathKey = fragmentInfo.PathKey, + Triangles = fragmentTriangles.Select(t => new NavisworksTransport.CachedTriangle3D(t.Point1, t.Point2, t.Point3)).ToList() + }; + + // 插入到树形结构中 + InsertFragment(fragmentInfo.PathKey, cachedFragment); + + processedFragments++; + } + catch (Exception ex) + { + LogManager.Error($"[几何体缓存] 处理片段失败: {ex.Message}"); + } + } + + // 统计最终缓存中的片段数量 + int totalCachedFragments = CountFragments(cacheRoot); + LogManager.Info($"[几何体缓存] 几何体缓存构建完成,处理片段数: {processedFragments},总三角形数: {totalTriangles},最终缓存片段数: {totalCachedFragments},跳过重复: {skippedDuplicates}"); + } + finally + { + // 释放COM对象 + if (comSelection != null) + { + System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection); + } + } + } + + /// + /// 将片段插入到树形结构中 + /// + /// 片段路径 + /// 片段数据 + private void InsertFragment(string pathKey, CachedFragmentData fragment) + { + var pathParts = pathKey.Split(','); + var currentNode = cacheRoot; + + // 逐级创建或导航到目标节点 + for (int i = 0; i < pathParts.Length; i++) + { + var currentPath = string.Join(",", pathParts.Take(i + 1)); + var nodeKey = pathParts[i]; + + currentNode = currentNode.AddChild(nodeKey, currentPath); + } + + // 检查是否已存在相同PathKey的片段(避免完全重复) + bool isDuplicate = currentNode.Fragments.Any(f => f.PathKey == pathKey && + f.Triangles.Count == fragment.Triangles.Count && + f.Triangles.SequenceEqual(fragment.Triangles, new CachedTriangleComparer())); + + if (!isDuplicate) + { + // 只有当片段不完全相同时才添加 + currentNode.Fragments.Add(fragment); + } + else + { + LogManager.Debug($"[几何体缓存] 跳过重复片段,PathKey: {pathKey}"); + } + } + + /// + /// 获取指定ModelItem的所有三角形数据 + /// 如果缓存不存在,则自动生成缓存 + /// + /// 模型项 + /// 三角形列表 + public List GetTrianglesForModelItem(ModelItem modelItem) + { + // 确保缓存已加载(只加载一次) + if (!_isCacheLoaded) + { + if (CacheExists()) + { + _cacheLoadResult = LoadCache(); + } + else + { + LogManager.Info("[几何体缓存] 缓存不存在,开始自动生成缓存..."); + GenerateCacheAutomatically(); + // 重新尝试加载新生成的缓存 + if (CacheExists()) + { + _cacheLoadResult = LoadCache(); + } + } + _isCacheLoaded = true; + } + + // 如果缓存加载失败,则使用传统方法 + if (!_cacheLoadResult) + { + LogManager.Warning("[几何体缓存] 无法加载缓存,使用传统方法提取几何体"); + return GeometryHelper.ExtractTriangles(new[] { modelItem }); + } + + var result = new List(); + + // 获取ModelItem的所有路径 + var itemPaths = GetModelItemPaths(modelItem); + + // 收集所有路径下的片段 + foreach (var pathKey in itemPaths) + { + var fragments = GetFragmentsForPath(pathKey); + foreach (var fragment in fragments) + { + // 将CachedTriangle3D转换为Triangle3D + foreach (var cachedTriangle in fragment.Triangles) + { + result.Add(new Triangle3D( + cachedTriangle.Point1.ToNavisworksPoint3D(), + cachedTriangle.Point2.ToNavisworksPoint3D(), + cachedTriangle.Point3.ToNavisworksPoint3D() + )); + } + } + } + + return result; + } + + /// + /// 获取指定ModelItem的所有三角形数据(自动缓存版本) + /// 如果缓存不存在,则自动生成缓存 + /// + /// 模型项 + /// 三角形列表 + public List GetTrianglesForModelItemWithAutoCache(ModelItem modelItem) + { + // 检查缓存是否存在,如果不存在则自动生成 + if (!CacheExists()) + { + LogManager.Info("[几何体缓存] 缓存不存在,开始自动生成缓存..."); + GenerateCacheAutomatically(); + } + + // 如果缓存仍然不存在或加载失败,则使用传统方法 + if (!CacheExists() || !LoadCache()) + { + LogManager.Warning("[几何体缓存] 无法加载缓存,使用传统方法提取几何体"); + return GeometryHelper.ExtractTriangles(new[] { modelItem }); + } + + return GetTrianglesForModelItem(modelItem); + } + + /// + /// 递归统计缓存中的片段数量 + /// + /// 当前节点 + /// 片段总数 + private int CountFragments(PathTreeNode node) + { + if (node == null) return 0; + + int count = node.Fragments?.Count ?? 0; + + if (node.Children != null) + { + foreach (var child in node.Children) + { + count += CountFragments(child); + } + } + + return count; + } + + /// + /// 获取ModelItem的所有路径 + /// + /// 模型项 + /// 路径列表 + private List GetModelItemPaths(ModelItem modelItem) + { + var paths = new List(); + + // 转换为COM选择 + var modelCollection = new ModelItemCollection(); + modelCollection.Add(modelItem); + var comSelection = ComApiBridge.ToInwOpSelection(modelCollection); + + // 遍历所有路径 + foreach (ComApi.InwOaPath3 path in comSelection.Paths()) + { + var pathArray = ((Array)path.ArrayData).ToArray(); + var pathKey = string.Join(",", pathArray); + paths.Add(pathKey); + } + + return paths; + } + + /// + /// 获取指定路径下的所有片段 + /// + /// 目标路径 + /// 片段列表 + private List GetFragmentsForPath(string targetPath) + { + var result = new List(); + var pathParts = targetPath.Split(','); + var currentNode = cacheRoot; + + // 逐级导航到目标节点 + for (int i = 0; i < pathParts.Length; i++) + { + var nodeKey = pathParts[i]; + var childNode = currentNode.FindChild(nodeKey); + if (childNode != null) + { + currentNode = childNode; + } + else + { + // 路径不存在,返回空结果 + return result; + } + } + + // 只返回目标节点的片段 + if (currentNode.Fragments != null) + { + result.AddRange(currentNode.Fragments); + } + + return result; + } + + /// + /// 保存缓存到文件 + /// + public void SaveCache() + { + try + { + // 统计缓存中的数据量 + int fragmentCount = 0; + int nodeCount = 0; + CountCacheNodes(cacheRoot, ref fragmentCount, ref nodeCount); + + LogManager.Info($"[几何体缓存] 保存缓存到文件: {cacheFilePath},节点数: {nodeCount}, 片段数: {fragmentCount}"); + + // 使用XML序列化 + var serializer = new System.Xml.Serialization.XmlSerializer(typeof(PathTreeNode)); + using (var writer = new System.IO.StreamWriter(cacheFilePath, false, System.Text.Encoding.UTF8)) + { + serializer.Serialize(writer, cacheRoot); + } + + // 检查文件是否成功创建 + if (File.Exists(cacheFilePath)) + { + var fileInfo = new FileInfo(cacheFilePath); + LogManager.Info($"[几何体缓存] 缓存保存完成,文件大小: {fileInfo.Length} 字节"); + } + else + { + LogManager.Warning($"[几何体缓存] 缓存保存完成,但文件未找到: {cacheFilePath}"); + } + } + catch (Exception ex) + { + LogManager.Error($"[几何体缓存] 保存缓存失败: {ex.Message}"); + } + } + + /// + /// 从文件加载缓存 + /// + /// 是否加载成功 + public bool LoadCache() + { + try + { + if (!File.Exists(cacheFilePath)) + { + LogManager.Info($"[几何体缓存] 缓存文件不存在: {cacheFilePath}"); + return false; + } + + LogManager.Info($"[几何体缓存] 从文件加载缓存: {cacheFilePath}"); + + // 使用XML反序列化 + var serializer = new System.Xml.Serialization.XmlSerializer(typeof(PathTreeNode)); + using (var reader = new System.IO.StreamReader(cacheFilePath, System.Text.Encoding.UTF8)) + { + cacheRoot = (PathTreeNode)serializer.Deserialize(reader); + } + + // 统计缓存中的数据量 + int fragmentCount = 0; + int nodeCount = 0; + CountCacheNodes(cacheRoot, ref fragmentCount, ref nodeCount); + + LogManager.Info($"[几何体缓存] 缓存加载完成,节点数: {nodeCount}, 片段数: {fragmentCount}"); + return true; + } + catch (Exception ex) + { + LogManager.Error($"[几何体缓存] 加载缓存失败: {ex.Message}"); + return false; + } + } + + /// + /// 递归统计缓存中的节点和片段数量 + /// + private void CountCacheNodes(PathTreeNode node, ref int fragmentCount, ref int nodeCount) + { + if (node == null) return; + + nodeCount++; + fragmentCount += node.Fragments?.Count ?? 0; + + if (node.Children != null) + { + foreach (var child in node.Children) + { + CountCacheNodes(child, ref fragmentCount, ref nodeCount); + } + } + } + + /// + /// 检查缓存文件是否存在 + /// + /// 是否存在 + public bool CacheExists() + { + bool exists = File.Exists(cacheFilePath); + LogManager.Info($"[几何体缓存] 检查缓存文件是否存在: {cacheFilePath}, 结果: {(exists ? "存在" : "不存在")}"); + return exists; + } + + /// + /// 自动生成缓存文件 + /// + private void GenerateCacheAutomatically() + { + try + { + LogManager.Info("[几何体缓存] 开始自动生成缓存文件"); + + // 获取当前文档 + var document = Autodesk.Navisworks.Api.Application.ActiveDocument; + if (document == null) + { + LogManager.Error("[几何体缓存] 无法获取活动文档"); + return; + } + + // 开始进度条 + var progress = Autodesk.Navisworks.Api.Application.BeginProgress("生成几何体缓存", + $"正在为 {Path.GetFileName(document.FileName)} 生成几何体缓存..."); + + try + { + // 收集场景中所有带几何信息的对象 + var allModelItems = GetAllModelItems(document); + LogManager.Info($"[几何体缓存] 场景中共有 {allModelItems.Count} 个带几何信息的模型对象"); + + if (allModelItems.Count == 0) + { + LogManager.Warning("[几何体缓存] 场景中没有找到任何带几何信息的模型对象"); + return; + } + + // 更新进度 + progress.Update(0.25); // 只传递进度值,不传递消息 + + // 构建缓存 + BuildCache(allModelItems); + + // 更新进度 + progress.Update(0.75); // 只传递进度值,不传递消息 + + // 保存缓存到文件 + SaveCache(); + + // 更新进度 + progress.Update(1.0); // 只传递进度值,不传递消息 + + LogManager.Info("[几何体缓存] 几何体缓存自动生成完成"); + } + finally + { + // 确保进度条被关闭 + Autodesk.Navisworks.Api.Application.EndProgress(); + } + } + catch (Exception ex) + { + LogManager.Error($"[几何体缓存] 自动生成缓存失败: {ex.Message}"); + } + } + + /// + /// 获取场景中所有带几何信息的模型对象 + /// + private List GetAllModelItems(Autodesk.Navisworks.Api.Document document) + { + var items = new List(); + + // 遍历文档中的所有模型 + foreach (var model in document.Models) + { + if (model.RootItem != null) + { + CollectAllItems(model.RootItem, items); + } + } + + return items; + } + + /// + /// 递归收集所有带几何信息的对象 + /// + private void CollectAllItems(ModelItem item, List result) + { + // 如果当前对象有几何信息,加入结果 + if (item.HasGeometry) + { + result.Add(item); + } + + // 递归处理所有子对象 + foreach (var child in item.Children) + { + CollectAllItems(child, result); + } + } + } + + /// + /// 路径树节点 + /// + [Serializable] + public class PathTreeNode + { + public string NodeKey { get; set; } + public string FullPath { get; set; } + public List Fragments { get; set; } + + // 使用List存储子节点,避免Dictionary序列化问题 + public List Children { get; set; } + + // 用于查找子节点的辅助方法 + public PathTreeNode FindChild(string nodeKey) + { + return Children?.FirstOrDefault(child => child.NodeKey == nodeKey); + } + + // 用于添加子节点的辅助方法 + public PathTreeNode AddChild(string nodeKey, string fullPath) + { + if (Children == null) + Children = new List(); + + var existingChild = FindChild(nodeKey); + if (existingChild != null) + return existingChild; + + var newChild = new PathTreeNode + { + NodeKey = nodeKey, + FullPath = fullPath, + Fragments = new List(), + Children = new List() + }; + + Children.Add(newChild); + return newChild; + } + + public PathTreeNode() + { + Fragments = new List(); + Children = new List(); + } + + public PathTreeNode(string nodeKey, string fullPath) + { + NodeKey = nodeKey; + FullPath = fullPath; + Fragments = new List(); + Children = new List(); + } + } + + /// + /// 缓存片段数据 + /// + [Serializable] + [System.Xml.Serialization.XmlType("CachedFragmentData")] + public class CachedFragmentData + { + [System.Xml.Serialization.XmlAttribute("PathKey")] + public string PathKey { get; set; } + + [System.Xml.Serialization.XmlArray("Triangles")] + [System.Xml.Serialization.XmlArrayItem("Triangle")] + public List Triangles { get; set; } + + public CachedFragmentData() + { + Triangles = new List(); + } + } + + /// + /// 三角形比较器,用于检测重复的三角形数据 + /// + public class CachedTriangleComparer : IEqualityComparer + { + public bool Equals(CachedTriangle3D x, CachedTriangle3D y) + { + if (x.Point1.X != y.Point1.X || x.Point1.Y != y.Point1.Y || x.Point1.Z != y.Point1.Z) + return false; + if (x.Point2.X != y.Point2.X || x.Point2.Y != y.Point2.Y || x.Point2.Z != y.Point2.Z) + return false; + if (x.Point3.X != y.Point3.X || x.Point3.Y != y.Point3.Y || x.Point3.Z != y.Point3.Z) + return false; + return true; + } + + public int GetHashCode(CachedTriangle3D obj) + { + // 简单的哈希实现 + return obj.Point1.GetHashCode() ^ obj.Point2.GetHashCode() ^ obj.Point3.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Utils/GeometryHelper.cs b/src/Utils/GeometryHelper.cs index ca99981..e94da48 100644 --- a/src/Utils/GeometryHelper.cs +++ b/src/Utils/GeometryHelper.cs @@ -10,7 +10,7 @@ using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge; namespace NavisworksTransport { /// - /// 几何提取器 - 使用改进的算法和性能优化 + /// 几何提取器 /// public class GeometryHelper { @@ -291,14 +291,12 @@ namespace NavisworksTransport } } - // 使用递增索引作为唯一标识,确保每个片段都被处理 - var uniqueKey = $"fragment_{totalFragmentCount}"; - + // 使用实际的路径键而不是递增索引 fragmentList.Add(new FragmentInfo { Fragment = fragment, TransformMatrix = transformMatrix, - PathKey = uniqueKey // 使用唯一索引而不是pathKey + PathKey = pathKey // 使用实际的路径键 }); } catch (Exception ex) @@ -1058,15 +1056,6 @@ namespace NavisworksTransport public OptimizedGeometryCallback(double[] transformMatrix) { _transformMatrix = transformMatrix; - - if (_transformMatrix != null) - { - LogManager.Debug($"几何回调处理器使用预处理矩阵,元素数量: {_transformMatrix.Length}"); - } - else - { - LogManager.Debug("几何回调处理器使用单位矩阵(无变换)"); - } } public void Line(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2) diff --git a/src/Utils/NavisworksToDMesh3Converter.cs b/src/Utils/NavisworksToDMesh3Converter.cs index cebb994..f6bc32d 100644 --- a/src/Utils/NavisworksToDMesh3Converter.cs +++ b/src/Utils/NavisworksToDMesh3Converter.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; -using NavisworksTransport.Utils; using g4; namespace NavisworksTransport