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;
+ }
+ }
+}