From f64e79d37264511c84997cfa911850e681ba173b Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 12 Oct 2025 13:20:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(voxel):=20=E9=98=B6=E6=AE=B51.4=20-=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=20geometry4Sharp=20=E7=9A=84=20MeshSignedDis?= =?UTF-8?q?tanceGrid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 MeshSDFTester.cs: 测试 geometry4Sharp 库的可用性和功能 - 实现 Navisworks Triangle3D 到 DMesh3 的转换 - 实现 MeshSignedDistanceGrid (SDF) 计算功能 - 添加库可用性快速测试方法 - 手动构建测试立方体网格 特性(原型版本): - QuickLibraryTest(): 验证 geometry4Sharp 库是否正常工作 - ConvertToDMesh3(): 将 Navisworks 三角形转换为 DMesh3 格式 - ComputeSDF(): 计算签名距离场(使用 DMeshAABBTree3 加速) - TestFullPipeline(): 完整测试流程(ModelItem → 三角形 → DMesh3 → SDF) - CreateTestCubeMesh(): 手动创建测试立方体 注意: - 这是原型版本,部分 SDF 距离查询 API 需要进一步研究 - 验证了 DMesh3, DMeshAABBTree3, MeshSignedDistanceGrid 基本可用 - 与 GeometryHelper.ExtractTriangles() 集成 下一步: 体素可视化验证(阶段1.5)或根据实际需求调整 --- NavisworksTransportPlugin.csproj | 1 + src/PathPlanning/MeshSDFTester.cs | 376 ++++++++++++++++++++++++++++++ 2 files changed, 377 insertions(+) create mode 100644 src/PathPlanning/MeshSDFTester.cs diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index 30f4315..91e6054 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -195,6 +195,7 @@ + diff --git a/src/PathPlanning/MeshSDFTester.cs b/src/PathPlanning/MeshSDFTester.cs new file mode 100644 index 0000000..eeb22cd --- /dev/null +++ b/src/PathPlanning/MeshSDFTester.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Autodesk.Navisworks.Api; +using NavisworksTransport.Utils; +using g4; + +namespace NavisworksTransport.PathPlanning +{ + /// + /// Mesh SDF 测试器 - 测试 geometry4Sharp 的 MeshSignedDistanceGrid 功能 + /// 阶段 1.4:验证 Navisworks 网格到 DMesh3 的转换和 SDF 计算 + /// 注意:这是原型版本,部分API需要根据实际geometry4Sharp文档调整 + /// + public class MeshSDFTester + { + /// + /// 简单测试:验证 geometry4Sharp 是否可用 + /// + public static void QuickLibraryTest() + { + LogManager.Info("=== geometry4Sharp 库可用性测试 ==="); + + try + { + // 测试创建 DMesh3 + var mesh = new DMesh3(MeshComponents.None); + LogManager.Info($"✅ DMesh3 创建成功"); + + // 添加简单的三角形 + int v0 = mesh.AppendVertex(new Vector3d(0, 0, 0)); + int v1 = mesh.AppendVertex(new Vector3d(1, 0, 0)); + int v2 = mesh.AppendVertex(new Vector3d(0, 1, 0)); + int t0 = mesh.AppendTriangle(v0, v1, v2); + + LogManager.Info($"✅ 顶点和三角形添加成功: {mesh.VertexCount} 顶点, {mesh.TriangleCount} 三角形"); + + // 测试 AABB 树 + var spatial = new DMeshAABBTree3(mesh, autoBuild: true); + LogManager.Info($"✅ DMeshAABBTree3 创建成功"); + + LogManager.Info("=== geometry4Sharp 库测试通过 ==="); + } + catch (Exception ex) + { + LogManager.Error($"❌ geometry4Sharp 库测试失败: {ex.Message}"); + LogManager.Error($"堆栈跟踪: {ex.StackTrace}"); + } + } + /// + /// 将 Navisworks 三角形转换为 geometry4Sharp DMesh3 + /// + /// Navisworks 三角形集合 + /// DMesh3 对象 + public static DMesh3 ConvertToDMesh3(List triangles) + { + var stopwatch = Stopwatch.StartNew(); + LogManager.Info("=== 开始转换 Navisworks 网格到 DMesh3 ==="); + + var mesh = new DMesh3(MeshComponents.None); // 不需要法线、颜色等额外组件 + + try + { + // 第一步:添加所有顶点(使用字典去重) + var vertexMap = new Dictionary(); + int vertexCount = 0; + + foreach (var triangle in triangles) + { + AddVertexIfNotExists(mesh, triangle.Point1, vertexMap, ref vertexCount); + AddVertexIfNotExists(mesh, triangle.Point2, vertexMap, ref vertexCount); + AddVertexIfNotExists(mesh, triangle.Point3, vertexMap, ref vertexCount); + } + + LogManager.Info($"顶点添加完成: {mesh.VertexCount} 个顶点"); + + // 第二步:添加所有三角形面 + int triangleAddedCount = 0; + int triangleFailedCount = 0; + + foreach (var triangle in triangles) + { + try + { + int v1 = GetVertexIndex(triangle.Point1, vertexMap); + int v2 = GetVertexIndex(triangle.Point2, vertexMap); + int v3 = GetVertexIndex(triangle.Point3, vertexMap); + + if (v1 >= 0 && v2 >= 0 && v3 >= 0 && v1 != v2 && v2 != v3 && v1 != v3) + { + int tid = mesh.AppendTriangle(v1, v2, v3); + if (tid >= 0) + { + triangleAddedCount++; + } + else + { + triangleFailedCount++; + } + } + else + { + triangleFailedCount++; + } + } + catch (Exception ex) + { + triangleFailedCount++; + LogManager.Debug($"添加三角形失败: {ex.Message}"); + } + } + + LogManager.Info($"三角形添加完成: 成功={triangleAddedCount}, 失败={triangleFailedCount}"); + + // 第三步:网格验证和修复 + if (!mesh.IsClosed()) + { + LogManager.Warning("网格不闭合,可能影响 SDF 计算准确性"); + } + + // 计算边界框 + var bounds = mesh.CachedBounds; + LogManager.Info($"网格边界: Min=({bounds.Min.x:F2}, {bounds.Min.y:F2}, {bounds.Min.z:F2}), " + + $"Max=({bounds.Max.x:F2}, {bounds.Max.y:F2}, {bounds.Max.z:F2})"); + + stopwatch.Stop(); + LogManager.Info($"=== DMesh3 转换完成 ==="); + LogManager.Info($"转换耗时: {stopwatch.ElapsedMilliseconds} ms ({stopwatch.Elapsed.TotalSeconds:F2} 秒)"); + LogManager.Info($"网格统计: 顶点={mesh.VertexCount}, 三角形={mesh.TriangleCount}, 边={mesh.EdgeCount}"); + } + catch (Exception ex) + { + LogManager.Error($"转换到 DMesh3 失败: {ex.Message}"); + LogManager.Error($"堆栈跟踪: {ex.StackTrace}"); + } + + return mesh; + } + + /// + /// 添加顶点(如果不存在) + /// + private static void AddVertexIfNotExists(DMesh3 mesh, Point3D point, Dictionary vertexMap, ref int vertexCount) + { + string key = GetVertexKey(point); + + if (!vertexMap.ContainsKey(key)) + { + int vid = mesh.AppendVertex(new Vector3d(point.X, point.Y, point.Z)); + vertexMap[key] = vid; + vertexCount++; + } + } + + /// + /// 获取顶点索引 + /// + private static int GetVertexIndex(Point3D point, Dictionary vertexMap) + { + string key = GetVertexKey(point); + return vertexMap.TryGetValue(key, out int index) ? index : -1; + } + + /// + /// 生成顶点键(用于去重) + /// + private static string GetVertexKey(Point3D point, double tolerance = 0.0001) + { + // 使用较高精度避免过度合并 + return $"{Math.Round(point.X / tolerance) * tolerance:F6}," + + $"{Math.Round(point.Y / tolerance) * tolerance:F6}," + + $"{Math.Round(point.Z / tolerance) * tolerance:F6}"; + } + + /// + /// 计算签名距离场(Signed Distance Field) + /// + /// DMesh3 网格 + /// 网格单元尺寸(模型单位) + /// MeshSignedDistanceGrid 对象 + public static MeshSignedDistanceGrid ComputeSDF(DMesh3 mesh, double cellSize) + { + var stopwatch = Stopwatch.StartNew(); + LogManager.Info("=== 开始计算签名距离场 (SDF) ==="); + + MeshSignedDistanceGrid sdf = null; + + try + { + // 获取网格边界 + var bounds = mesh.CachedBounds; + LogManager.Info($"网格边界: {bounds.Min} ~ {bounds.Max}"); + LogManager.Info($"网格尺寸: {bounds.Width:F2} × {bounds.Height:F2} × {bounds.Depth:F2} 模型单位"); + + // 计算网格分辨率 + int nx = (int)Math.Ceiling(bounds.Width / cellSize); + int ny = (int)Math.Ceiling(bounds.Height / cellSize); + int nz = (int)Math.Ceiling(bounds.Depth / cellSize); + + LogManager.Info($"SDF 网格分辨率: {nx} × {ny} × {nz} = {nx * ny * nz:N0} 个单元"); + LogManager.Info($"单元尺寸: {cellSize:F4} 模型单位"); + + // 创建 DMeshAABBTree3(空间索引树,加速距离查询) + LogManager.Info("构建 AABB 树..."); + var spatialStopwatch = Stopwatch.StartNew(); + var spatial = new DMeshAABBTree3(mesh, autoBuild: true); + spatialStopwatch.Stop(); + LogManager.Info($"AABB 树构建完成: {spatialStopwatch.ElapsedMilliseconds} ms"); + + // 计算 SDF + LogManager.Info("开始计算距离场..."); + sdf = new MeshSignedDistanceGrid(mesh, cellSize, spatial); + sdf.Compute(); + + stopwatch.Stop(); + LogManager.Info($"=== SDF 计算完成 ==="); + LogManager.Info($"计算耗时: {stopwatch.ElapsedMilliseconds} ms ({stopwatch.Elapsed.TotalSeconds:F2} 秒)"); + + // 统计距离场信息 + AnalyzeSDF(sdf); + } + catch (Exception ex) + { + LogManager.Error($"计算 SDF 失败: {ex.Message}"); + LogManager.Error($"堆栈跟踪: {ex.StackTrace}"); + } + + return sdf; + } + + /// + /// 分析 SDF 统计信息(简化版本) + /// + private static void AnalyzeSDF(MeshSignedDistanceGrid sdf) + { + try + { + var dims = sdf.Dimensions; + LogManager.Info($"SDF 维度: {dims.x} × {dims.y} × {dims.z}"); + LogManager.Info("SDF 创建成功,具体距离查询API需要进一步研究"); + } + catch (Exception ex) + { + LogManager.Warning($"分析 SDF 失败: {ex.Message}"); + } + } + + /// + /// 完整测试流程:从 ModelItem 到 SDF + /// + /// Navisworks 模型元素 + /// SDF 网格单元尺寸(模型单位) + /// MeshSignedDistanceGrid 对象 + public static MeshSignedDistanceGrid TestFullPipeline(ModelItem modelItem, double cellSize) + { + var stopwatch = Stopwatch.StartNew(); + LogManager.Info("=== 开始完整 SDF 测试流程 ==="); + LogManager.Info($"模型元素: {modelItem.DisplayName}"); + LogManager.Info($"SDF 单元尺寸: {cellSize:F4} 模型单位"); + + MeshSignedDistanceGrid sdf = null; + + try + { + // 步骤 1:提取三角形 + LogManager.Info("[步骤 1/3] 提取三角形网格..."); + var triangles = GeometryHelper.ExtractTriangles(modelItem); + + if (triangles.Count == 0) + { + LogManager.Error("未提取到三角形数据"); + return null; + } + + LogManager.Info($"提取到 {triangles.Count} 个三角形"); + + // 步骤 2:转换为 DMesh3 + LogManager.Info("[步骤 2/3] 转换到 DMesh3 格式..."); + var mesh = ConvertToDMesh3(triangles); + + if (mesh.TriangleCount == 0) + { + LogManager.Error("DMesh3 网格为空"); + return null; + } + + LogManager.Info($"DMesh3: {mesh.VertexCount} 顶点, {mesh.TriangleCount} 三角形"); + + // 步骤 3:计算 SDF + LogManager.Info("[步骤 3/3] 计算签名距离场..."); + sdf = ComputeSDF(mesh, cellSize); + + if (sdf == null) + { + LogManager.Error("SDF 计算失败"); + return null; + } + + stopwatch.Stop(); + LogManager.Info($"=== 完整测试流程完成 ==="); + LogManager.Info($"总耗时: {stopwatch.ElapsedMilliseconds} ms ({stopwatch.Elapsed.TotalSeconds:F2} 秒)"); + + // 验证 SDF 正确性 + VerifySDFCorrectness(sdf, mesh); + } + catch (Exception ex) + { + LogManager.Error($"完整测试流程失败: {ex.Message}"); + LogManager.Error($"堆栈跟踪: {ex.StackTrace}"); + } + + return sdf; + } + + /// + /// 验证 SDF 正确性(简化版本) + /// + private static void VerifySDFCorrectness(MeshSignedDistanceGrid sdf, DMesh3 mesh) + { + LogManager.Info("=== 验证 SDF 正确性(简化版本) ==="); + + try + { + var bounds = mesh.CachedBounds; + LogManager.Info($"网格边界: {bounds}"); + LogManager.Info("✅ SDF 创建成功,距离查询API需要进一步研究"); + } + catch (Exception ex) + { + LogManager.Error($"SDF 验证失败: {ex.Message}"); + } + } + + /// + /// 创建简单测试网格(手动构建立方体) + /// + /// 立方体尺寸 + /// DMesh3 立方体网格 + public static DMesh3 CreateTestCubeMesh(double size = 1.0) + { + LogManager.Info($"创建测试立方体网格: 尺寸={size}"); + + var mesh = new DMesh3(MeshComponents.None); + + // 手动创建立方体的8个顶点 + double h = size / 2.0; + int v0 = mesh.AppendVertex(new Vector3d(-h, -h, -h)); + int v1 = mesh.AppendVertex(new Vector3d(h, -h, -h)); + int v2 = mesh.AppendVertex(new Vector3d(h, h, -h)); + int v3 = mesh.AppendVertex(new Vector3d(-h, h, -h)); + int v4 = mesh.AppendVertex(new Vector3d(-h, -h, h)); + int v5 = mesh.AppendVertex(new Vector3d(h, -h, h)); + int v6 = mesh.AppendVertex(new Vector3d(h, h, h)); + int v7 = mesh.AppendVertex(new Vector3d(-h, h, h)); + + // 12个三角形(每个面2个三角形) + mesh.AppendTriangle(v0, v2, v1); // 底面 + mesh.AppendTriangle(v0, v3, v2); + mesh.AppendTriangle(v4, v5, v6); // 顶面 + mesh.AppendTriangle(v4, v6, v7); + mesh.AppendTriangle(v0, v1, v5); // 前面 + mesh.AppendTriangle(v0, v5, v4); + mesh.AppendTriangle(v2, v3, v7); // 后面 + mesh.AppendTriangle(v2, v7, v6); + mesh.AppendTriangle(v0, v4, v7); // 左面 + mesh.AppendTriangle(v0, v7, v3); + mesh.AppendTriangle(v1, v2, v6); // 右面 + mesh.AppendTriangle(v1, v6, v5); + + LogManager.Info($"测试网格: {mesh.VertexCount} 顶点, {mesh.TriangleCount} 三角形"); + + return mesh; + } + } +}