- 创建 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)或根据实际需求调整
377 lines
15 KiB
C#
377 lines
15 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|