NavisworksTransport/src/Commands/VoxelGridSDFTestCommand.cs
2025-10-21 18:48:27 +08:00

267 lines
9.6 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.Threading;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using NavisworksTransport.PathPlanning;
namespace NavisworksTransport.Commands
{
/// <summary>
/// 体素网格 SDF 测试命令
/// 使用 geometry4Sharp 的 MeshSignedDistanceGrid 进行精确体素化
/// 用于对比性能:射线投射法 vs 签名距离场法
/// </summary>
public class VoxelGridSDFTestCommand : CommandBase
{
private readonly Document _document;
private readonly double _cellSize;
private readonly int _samplingRate;
private readonly double _vehicleRadius;
private readonly double _vehicleHeight;
public VoxelGridSDFTestCommand(
Document document,
double cellSize,
int samplingRate,
double vehicleRadius,
double vehicleHeight)
: base("VoxelGridSDFTest", "体素网格SDF测试", "使用签名距离场进行精确体素化")
{
_document = document ?? throw new ArgumentNullException(nameof(document));
_cellSize = cellSize;
_samplingRate = samplingRate;
_vehicleRadius = vehicleRadius;
_vehicleHeight = vehicleHeight;
}
protected override PathPlanningResult ValidateParameters()
{
if (_document == null)
{
return PathPlanningResult.ValidationFailure("Navisworks 文档对象为空");
}
if (_cellSize <= 0)
{
return PathPlanningResult.ValidationFailure("体素大小必须大于0");
}
return PathPlanningResult.Success("参数验证通过");
}
protected override async Task<PathPlanningResult> ExecuteInternalAsync(CancellationToken cancellationToken)
{
try
{
LogInfo("=== 开始体素网格 SDF 测试 ===");
LogInfo($"体素大小: {_cellSize}米");
LogInfo($"车辆半径: {_vehicleRadius}米");
LogInfo($"车辆高度: {_vehicleHeight}米");
LogInfo($"采样率: {_samplingRate}");
UpdateProgress(5, "正在获取模型空间范围...");
// 步骤1: 获取整个模型空间的包围盒
BoundingBox3D spaceBounds = GetSpaceBounds(_document);
if (spaceBounds == null)
{
return PathPlanningResult.Failure("无法获取模型空间范围");
}
LogInfo($"空间范围: Min={spaceBounds.Min}, Max={spaceBounds.Max}");
UpdateProgress(15, "正在收集场景中的所有模型对象...");
// 步骤2: 收集场景中所有带几何信息的对象
var allModelItems = GetAllModelItems(_document);
LogInfo($"场景中共有 {allModelItems.Count} 个带几何信息的模型对象");
if (allModelItems.Count == 0)
{
return PathPlanningResult.Failure("场景中没有找到任何带几何信息的模型对象");
}
UpdateProgress(25, "正在使用 MeshSignedDistanceGrid 生成体素网格...");
// 步骤3: 使用 SDF 方法生成体素网格
var generator = new VoxelGridGenerator();
var voxelGrid = generator.GenerateFromBIMWithSDF(
spaceBounds,
_cellSize,
_vehicleRadius,
_vehicleHeight,
allModelItems);
LogInfo($"体素网格生成完成: {voxelGrid.SizeX}×{voxelGrid.SizeY}×{voxelGrid.SizeZ} = {voxelGrid.TotalVoxels} 个体素");
UpdateProgress(70, "正在统计体素分布...");
// 步骤4: 统计结果
var (total, passable, obstacle) = voxelGrid.GetStatistics();
LogInfo($"体素统计: 总数={total}, 自由空间={passable} ({passable*100.0/total:F1}%), 障碍物={obstacle} ({obstacle*100.0/total:F1}%)");
UpdateProgress(75, "正在生成可视化数据...");
// 步骤5: 生成可视化
var voxelPoints = VoxelGridVisualizer.VisualizeAsPoints(
voxelGrid,
samplingRate: _samplingRate,
showPassableOnly: false
);
LogInfo($"生成了 {voxelPoints.Count} 个可视化点(采样率: {_samplingRate}");
// 步骤6: 转换为 PathRoute 以便使用现有渲染系统
var visualRoute = VoxelGridVisualizer.ConvertToPathRoute(
voxelPoints,
$"VoxelGrid_SDF_{DateTime.Now:HHmmss}"
);
LogInfo($"可视化路径已创建: {visualRoute.Name}");
UpdateProgress(85, "正在渲染体素网格...");
// 步骤7: 调用 PathPointRenderPlugin 进行渲染
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
// 设置网格大小(体素大小),让渲染器根据体素大小计算球体半径
renderPlugin.SetGridSize(_cellSize);
LogInfo($"已设置体素网格大小: {_cellSize}米");
renderPlugin.RenderPointOnly(visualRoute);
LogInfo("✓ 体素网格已成功渲染到3D视图");
}
else
{
LogWarning("PathPointRenderPlugin 实例不可用,无法渲染体素网格");
}
UpdateProgress(95, "正在生成报告...");
// 步骤8: 生成文本报告
var report = VoxelGridVisualizer.GenerateVisualizationReport(voxelGrid);
LogInfo("=== 体素网格 SDF 测试报告 ===");
LogInfo(report);
UpdateProgress(100, "体素网格 SDF 测试完成");
return PathPlanningResult.Success($"体素网格 SDF 测试完成\n\n{report}");
}
catch (Exception ex)
{
LogError($"体素网格 SDF 测试失败: {ex.Message}", ex);
return PathPlanningResult.Failure($"体素网格 SDF 测试失败: {ex.Message}", ex);
}
}
#region
/// <summary>
/// 获取整个模型空间的包围盒
/// </summary>
private BoundingBox3D GetSpaceBounds(Document document)
{
try
{
// 计算所有模型的总包围盒
LogInfo("正在计算所有模型的总包围盒...");
return CalculateBoundsFromAllModels(document);
}
catch (Exception ex)
{
LogError($"获取空间范围失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 计算所有模型的总包围盒
/// </summary>
private BoundingBox3D CalculateBoundsFromAllModels(Document document)
{
BoundingBox3D totalBounds = null;
foreach (var model in document.Models)
{
if (model.RootItem != null)
{
var modelBounds = model.RootItem.BoundingBox();
if (modelBounds != null)
{
if (totalBounds == null)
{
totalBounds = modelBounds;
}
else
{
totalBounds = ExpandBounds(totalBounds, modelBounds);
}
}
}
}
return totalBounds;
}
/// <summary>
/// 扩展包围盒以包含另一个包围盒
/// </summary>
private BoundingBox3D ExpandBounds(BoundingBox3D bounds1, BoundingBox3D bounds2)
{
var min = new Point3D(
Math.Min(bounds1.Min.X, bounds2.Min.X),
Math.Min(bounds1.Min.Y, bounds2.Min.Y),
Math.Min(bounds1.Min.Z, bounds2.Min.Z)
);
var max = new Point3D(
Math.Max(bounds1.Max.X, bounds2.Max.X),
Math.Max(bounds1.Max.Y, bounds2.Max.Y),
Math.Max(bounds1.Max.Z, bounds2.Max.Z)
);
return new BoundingBox3D(min, max);
}
/// <summary>
/// 获取场景中所有带几何信息的模型对象
/// </summary>
private List<ModelItem> GetAllModelItems(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);
}
}
#endregion
}
}