267 lines
9.6 KiB
C#
267 lines
9.6 KiB
C#
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
|
||
}
|
||
}
|