增加了几何体缓存文件支持

This commit is contained in:
tian 2025-10-20 10:39:21 +08:00
parent 55fbcbdb48
commit dba1f76f0a
6 changed files with 744 additions and 16 deletions

View File

@ -291,6 +291,7 @@
<Compile Include="src\Utils\FloorDetector.cs" />
<Compile Include="src\Utils\ModelItemAnalysisHelper.cs" />
<Compile Include="src\Utils\GeometryHelper.cs" />
<Compile Include="src\Utils\GeometryCacheManager.cs" />
<Compile Include="src\Utils\LogManager.cs" />
<Compile Include="src\Utils\NavisworksApiHelper.cs" />
<Compile Include="src\Utils\NavisworksSelectionHelper.cs" />
@ -298,6 +299,7 @@
<Compile Include="src\Utils\UnitsConverter.cs" />
<Compile Include="src\Utils\VisibilityHelper.cs" />
<Compile Include="src\Utils\ModelItemTransformHelper.cs" />
<Compile Include="src\Utils\CachedTriangle3D.cs" />
<!-- Assembly Info -->
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -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<Triangle3D>();
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");

View File

@ -0,0 +1,82 @@
using System;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport
{
/// <summary>
/// 可序列化的点结构体
/// </summary>
[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})";
}
}
/// <summary>
/// 可序列化的三角形结构体
/// </summary>
[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}]";
}
}
}

View File

@ -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
{
/// <summary>
/// 几何体缓存管理器
/// 用于缓存模型文档中的几何体信息,提高访问性能
/// </summary>
public class GeometryCacheManager
{
// 缓存根节点
private PathTreeNode cacheRoot;
// 当前文档路径
private string documentPath;
// 缓存文件路径
private string cacheFilePath;
// 缓存加载状态
private bool _isCacheLoaded = false;
private bool _cacheLoadResult = false;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="docPath">文档路径</param>
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}");
}
/// <summary>
/// 从模型项列表提取所有片段并构建缓存
/// </summary>
/// <param name="modelItems">模型项列表</param>
public void BuildCache(IEnumerable<ModelItem> 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);
}
}
}
/// <summary>
/// 将片段插入到树形结构中
/// </summary>
/// <param name="pathKey">片段路径</param>
/// <param name="fragment">片段数据</param>
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}");
}
}
/// <summary>
/// 获取指定ModelItem的所有三角形数据
/// 如果缓存不存在,则自动生成缓存
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>三角形列表</returns>
public List<Triangle3D> 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<Triangle3D>();
// 获取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;
}
/// <summary>
/// 获取指定ModelItem的所有三角形数据自动缓存版本
/// 如果缓存不存在,则自动生成缓存
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>三角形列表</returns>
public List<Triangle3D> GetTrianglesForModelItemWithAutoCache(ModelItem modelItem)
{
// 检查缓存是否存在,如果不存在则自动生成
if (!CacheExists())
{
LogManager.Info("[几何体缓存] 缓存不存在,开始自动生成缓存...");
GenerateCacheAutomatically();
}
// 如果缓存仍然不存在或加载失败,则使用传统方法
if (!CacheExists() || !LoadCache())
{
LogManager.Warning("[几何体缓存] 无法加载缓存,使用传统方法提取几何体");
return GeometryHelper.ExtractTriangles(new[] { modelItem });
}
return GetTrianglesForModelItem(modelItem);
}
/// <summary>
/// 递归统计缓存中的片段数量
/// </summary>
/// <param name="node">当前节点</param>
/// <returns>片段总数</returns>
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;
}
/// <summary>
/// 获取ModelItem的所有路径
/// </summary>
/// <param name="modelItem">模型项</param>
/// <returns>路径列表</returns>
private List<string> GetModelItemPaths(ModelItem modelItem)
{
var paths = new List<string>();
// 转换为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<int>();
var pathKey = string.Join(",", pathArray);
paths.Add(pathKey);
}
return paths;
}
/// <summary>
/// 获取指定路径下的所有片段
/// </summary>
/// <param name="targetPath">目标路径</param>
/// <returns>片段列表</returns>
private List<CachedFragmentData> GetFragmentsForPath(string targetPath)
{
var result = new List<CachedFragmentData>();
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;
}
/// <summary>
/// 保存缓存到文件
/// </summary>
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}");
}
}
/// <summary>
/// 从文件加载缓存
/// </summary>
/// <returns>是否加载成功</returns>
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;
}
}
/// <summary>
/// 递归统计缓存中的节点和片段数量
/// </summary>
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);
}
}
}
/// <summary>
/// 检查缓存文件是否存在
/// </summary>
/// <returns>是否存在</returns>
public bool CacheExists()
{
bool exists = File.Exists(cacheFilePath);
LogManager.Info($"[几何体缓存] 检查缓存文件是否存在: {cacheFilePath}, 结果: {(exists ? "" : "")}");
return exists;
}
/// <summary>
/// 自动生成缓存文件
/// </summary>
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}");
}
}
/// <summary>
/// 获取场景中所有带几何信息的模型对象
/// </summary>
private List<ModelItem> GetAllModelItems(Autodesk.Navisworks.Api.Document document)
{
var items = new List<ModelItem>();
// 遍历文档中的所有模型
foreach (var model in document.Models)
{
if (model.RootItem != null)
{
CollectAllItems(model.RootItem, items);
}
}
return items;
}
/// <summary>
/// 递归收集所有带几何信息的对象
/// </summary>
private void CollectAllItems(ModelItem item, List<ModelItem> result)
{
// 如果当前对象有几何信息,加入结果
if (item.HasGeometry)
{
result.Add(item);
}
// 递归处理所有子对象
foreach (var child in item.Children)
{
CollectAllItems(child, result);
}
}
}
/// <summary>
/// 路径树节点
/// </summary>
[Serializable]
public class PathTreeNode
{
public string NodeKey { get; set; }
public string FullPath { get; set; }
public List<CachedFragmentData> Fragments { get; set; }
// 使用List存储子节点避免Dictionary序列化问题
public List<PathTreeNode> 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<PathTreeNode>();
var existingChild = FindChild(nodeKey);
if (existingChild != null)
return existingChild;
var newChild = new PathTreeNode
{
NodeKey = nodeKey,
FullPath = fullPath,
Fragments = new List<CachedFragmentData>(),
Children = new List<PathTreeNode>()
};
Children.Add(newChild);
return newChild;
}
public PathTreeNode()
{
Fragments = new List<CachedFragmentData>();
Children = new List<PathTreeNode>();
}
public PathTreeNode(string nodeKey, string fullPath)
{
NodeKey = nodeKey;
FullPath = fullPath;
Fragments = new List<CachedFragmentData>();
Children = new List<PathTreeNode>();
}
}
/// <summary>
/// 缓存片段数据
/// </summary>
[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<CachedTriangle3D> Triangles { get; set; }
public CachedFragmentData()
{
Triangles = new List<CachedTriangle3D>();
}
}
/// <summary>
/// 三角形比较器,用于检测重复的三角形数据
/// </summary>
public class CachedTriangleComparer : IEqualityComparer<CachedTriangle3D>
{
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();
}
}
}

View File

@ -10,7 +10,7 @@ using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
namespace NavisworksTransport
{
/// <summary>
/// 几何提取器 - 使用改进的算法和性能优化
/// 几何提取器
/// </summary>
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)

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using g4;
namespace NavisworksTransport