NavisworksTransport/src/PathPlanning/MeshSDFTester.cs
tian f64e79d372 feat(voxel): 阶段1.4 - 测试 geometry4Sharp 的 MeshSignedDistanceGrid
- 创建 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)或根据实际需求调整
2025-10-12 13:20:29 +08:00

377 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using g4;
namespace NavisworksTransport.PathPlanning
{
/// <summary>
/// Mesh SDF 测试器 - 测试 geometry4Sharp 的 MeshSignedDistanceGrid 功能
/// 阶段 1.4:验证 Navisworks 网格到 DMesh3 的转换和 SDF 计算
/// 注意这是原型版本部分API需要根据实际geometry4Sharp文档调整
/// </summary>
public class MeshSDFTester
{
/// <summary>
/// 简单测试:验证 geometry4Sharp 是否可用
/// </summary>
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}");
}
}
/// <summary>
/// 将 Navisworks 三角形转换为 geometry4Sharp DMesh3
/// </summary>
/// <param name="triangles">Navisworks 三角形集合</param>
/// <returns>DMesh3 对象</returns>
public static DMesh3 ConvertToDMesh3(List<Triangle3D> triangles)
{
var stopwatch = Stopwatch.StartNew();
LogManager.Info("=== 开始转换 Navisworks 网格到 DMesh3 ===");
var mesh = new DMesh3(MeshComponents.None); // 不需要法线、颜色等额外组件
try
{
// 第一步:添加所有顶点(使用字典去重)
var vertexMap = new Dictionary<string, int>();
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;
}
/// <summary>
/// 添加顶点(如果不存在)
/// </summary>
private static void AddVertexIfNotExists(DMesh3 mesh, Point3D point, Dictionary<string, int> 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++;
}
}
/// <summary>
/// 获取顶点索引
/// </summary>
private static int GetVertexIndex(Point3D point, Dictionary<string, int> vertexMap)
{
string key = GetVertexKey(point);
return vertexMap.TryGetValue(key, out int index) ? index : -1;
}
/// <summary>
/// 生成顶点键(用于去重)
/// </summary>
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}";
}
/// <summary>
/// 计算签名距离场Signed Distance Field
/// </summary>
/// <param name="mesh">DMesh3 网格</param>
/// <param name="cellSize">网格单元尺寸(模型单位)</param>
/// <returns>MeshSignedDistanceGrid 对象</returns>
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;
}
/// <summary>
/// 分析 SDF 统计信息(简化版本)
/// </summary>
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}");
}
}
/// <summary>
/// 完整测试流程:从 ModelItem 到 SDF
/// </summary>
/// <param name="modelItem">Navisworks 模型元素</param>
/// <param name="cellSize">SDF 网格单元尺寸(模型单位)</param>
/// <returns>MeshSignedDistanceGrid 对象</returns>
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;
}
/// <summary>
/// 验证 SDF 正确性(简化版本)
/// </summary>
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}");
}
}
/// <summary>
/// 创建简单测试网格(手动构建立方体)
/// </summary>
/// <param name="size">立方体尺寸</param>
/// <returns>DMesh3 立方体网格</returns>
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;
}
}
}