diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj
index 45e9265..30f4315 100644
--- a/NavisworksTransportPlugin.csproj
+++ b/NavisworksTransportPlugin.csproj
@@ -194,7 +194,8 @@
-
+
+
LogisticsControlPanel.xaml
diff --git a/src/PathPlanning/VoxelGridGenerator.cs b/src/PathPlanning/VoxelGridGenerator.cs
new file mode 100644
index 0000000..48524b8
--- /dev/null
+++ b/src/PathPlanning/VoxelGridGenerator.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Autodesk.Navisworks.Api;
+using NavisworksTransport.Utils;
+using static NavisworksTransport.CategoryAttributeManager;
+
+namespace NavisworksTransport.PathPlanning
+{
+ ///
+ /// 体素网格生成器 - 从 Navisworks BIM 模型生成 3D 体素网格
+ /// 阶段 1 原型版本:使用简单的包围盒方法进行体素化
+ ///
+ public class VoxelGridGenerator
+ {
+ ///
+ /// 从 BIM 模型生成体素网格(简化版 - 使用包围盒)
+ ///
+ /// 网格边界(世界坐标,模型单位)
+ /// 体素尺寸(米)
+ /// 车辆半径(米),用于膨胀障碍物
+ /// 车辆高度(米),用于检测通行空间
+ /// BIM 模型元素列表
+ /// 生成的体素网格
+ public VoxelGrid GenerateFromBIM(
+ BoundingBox3D bounds,
+ double voxelSizeMeters,
+ double vehicleRadiusMeters,
+ double vehicleHeightMeters,
+ IEnumerable modelItems)
+ {
+ var stopwatch = Stopwatch.StartNew();
+ LogManager.Info("=== 开始体素网格生成(简单包围盒方法) ===");
+
+ // 第一步:单位转换(米 → 模型单位)
+ double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(
+ Autodesk.Navisworks.Api.Application.ActiveDocument.Units);
+
+ double voxelSizeInModelUnits = voxelSizeMeters * metersToModelUnits;
+ double vehicleRadiusInModelUnits = vehicleRadiusMeters * metersToModelUnits;
+ double vehicleHeightInModelUnits = vehicleHeightMeters * metersToModelUnits;
+
+ LogManager.Info($"单位转换系数: {metersToModelUnits:F4}");
+ LogManager.Info($"体素尺寸: {voxelSizeMeters}米 = {voxelSizeInModelUnits:F2}模型单位");
+ LogManager.Info($"车辆半径: {vehicleRadiusMeters}米 = {vehicleRadiusInModelUnits:F2}模型单位");
+ LogManager.Info($"车辆高度: {vehicleHeightMeters}米 = {vehicleHeightInModelUnits:F2}模型单位");
+
+ // 第二步:创建体素网格
+ var voxelGrid = new VoxelGrid(bounds, voxelSizeInModelUnits);
+ LogManager.Info($"创建体素网格: {voxelGrid.SizeX} × {voxelGrid.SizeY} × {voxelGrid.SizeZ} = {voxelGrid.TotalVoxels:N0} 个体素");
+
+ // 第三步:提取模型元素并分类
+ var obstacleItems = new List<(ModelItem item, BoundingBox3D bbox, LogisticsElementType type)>();
+ int processedCount = 0;
+ int skippedCount = 0;
+
+ foreach (var item in modelItems)
+ {
+ processedCount++;
+
+ // 跳过没有几何体的元素
+ if (!item.HasGeometry)
+ {
+ skippedCount++;
+ continue;
+ }
+
+ // 获取包围盒
+ var bbox = item.BoundingBox();
+ if (bbox == null)
+ {
+ skippedCount++;
+ continue;
+ }
+
+ // 获取物流类型(从自定义属性或使用默认值)
+ var elementType = GetLogisticsElementType(item);
+
+ obstacleItems.Add((item, bbox, elementType));
+ }
+
+ LogManager.Info($"模型元素统计: 总数={processedCount}, 有效={obstacleItems.Count}, 跳过={skippedCount}");
+
+ // 第四步:体素化 - 使用包围盒标记障碍物
+ int obstacleVoxelCount = 0;
+ int passableVoxelCount = 0;
+
+ foreach (var (item, bbox, elementType) in obstacleItems)
+ {
+ // 判断是否为障碍物
+ bool isObstacle = IsObstacleType(elementType);
+
+ // 体素化该包围盒
+ int markedCount = VoxelizeBoundingBox(
+ voxelGrid,
+ bbox,
+ isObstacle,
+ elementType,
+ item,
+ vehicleRadiusInModelUnits);
+
+ if (isObstacle)
+ obstacleVoxelCount += markedCount;
+ else
+ passableVoxelCount += markedCount;
+ }
+
+ LogManager.Info($"体素标记完成: 障碍物体素={obstacleVoxelCount:N0}, 可通行体素={passableVoxelCount:N0}");
+
+ // 第五步:统计信息
+ var (total, passable, obstacle) = voxelGrid.GetStatistics();
+ double passableRatio = (double)passable / total * 100.0;
+
+ stopwatch.Stop();
+ LogManager.Info($"=== 体素网格生成完成 ===");
+ LogManager.Info($"总体素数: {total:N0}");
+ LogManager.Info($"可通行体素: {passable:N0} ({passableRatio:F1}%)");
+ LogManager.Info($"障碍物体素: {obstacle:N0} ({100 - passableRatio:F1}%)");
+ LogManager.Info($"生成耗时: {stopwatch.ElapsedMilliseconds} ms ({stopwatch.Elapsed.TotalSeconds:F2} 秒)");
+
+ return voxelGrid;
+ }
+
+ ///
+ /// 使用包围盒标记体素
+ ///
+ /// 体素网格
+ /// 包围盒(模型单位)
+ /// 是否为障碍物
+ /// 元素类型
+ /// 源模型元素
+ /// 膨胀半径(模型单位),用于障碍物
+ /// 标记的体素数量
+ private int VoxelizeBoundingBox(
+ VoxelGrid voxelGrid,
+ BoundingBox3D bbox,
+ bool isObstacle,
+ LogisticsElementType elementType,
+ ModelItem sourceItem,
+ double inflationRadius)
+ {
+ int markedCount = 0;
+
+ // 膨胀包围盒(仅对障碍物)
+ BoundingBox3D inflatedBbox = bbox;
+ if (isObstacle && inflationRadius > 0)
+ {
+ double inflation = inflationRadius;
+ inflatedBbox = new BoundingBox3D(
+ new Point3D(bbox.Min.X - inflation, bbox.Min.Y - inflation, bbox.Min.Z - inflation),
+ new Point3D(bbox.Max.X + inflation, bbox.Max.Y + inflation, bbox.Max.Z + inflation)
+ );
+ }
+
+ // 计算包围盒覆盖的体素范围
+ var (minX, minY, minZ) = voxelGrid.WorldToVoxel(inflatedBbox.Min);
+ var (maxX, maxY, maxZ) = voxelGrid.WorldToVoxel(inflatedBbox.Max);
+
+ // 遍历该范围内的所有体素
+ for (int x = Math.Max(0, minX); x <= Math.Min(voxelGrid.SizeX - 1, maxX); x++)
+ {
+ for (int y = Math.Max(0, minY); y <= Math.Min(voxelGrid.SizeY - 1, maxY); y++)
+ {
+ for (int z = Math.Max(0, minZ); z <= Math.Min(voxelGrid.SizeZ - 1, maxZ); z++)
+ {
+ // 检查体素中心是否在膨胀后的包围盒内
+ Point3D voxelCenter = voxelGrid.VoxelToWorldCenter(x, y, z);
+
+ if (IsPointInBoundingBox(voxelCenter, inflatedBbox))
+ {
+ var cell = voxelGrid.GetCell(x, y, z);
+
+ // 标记体素
+ if (isObstacle)
+ {
+ // 障碍物:标记为不可通行
+ cell.SetAsObstacle(sourceItem);
+ cell.Type = elementType;
+ }
+ else
+ {
+ // 可通行元素(门、通道等):保持可通行,但设置类型
+ // 注意:不覆盖已经标记为障碍物的体素
+ if (cell.IsPassable)
+ {
+ cell.Type = elementType;
+ cell.SourceItem = sourceItem;
+
+ // 设置速度限制(根据类型)
+ if (elementType == LogisticsElementType.门)
+ {
+ cell.SpeedLimit = 1.0; // 门的速度限制 1 m/s
+ }
+ else if (elementType == LogisticsElementType.楼梯)
+ {
+ cell.SpeedLimit = 0.5; // 楼梯速度限制 0.5 m/s
+ }
+ }
+ }
+
+ markedCount++;
+ }
+ }
+ }
+ }
+
+ return markedCount;
+ }
+
+ ///
+ /// 检查点是否在包围盒内
+ ///
+ private bool IsPointInBoundingBox(Point3D point, BoundingBox3D bbox)
+ {
+ return point.X >= bbox.Min.X && point.X <= bbox.Max.X &&
+ point.Y >= bbox.Min.Y && point.Y <= bbox.Max.Y &&
+ point.Z >= bbox.Min.Z && point.Z <= bbox.Max.Z;
+ }
+
+ ///
+ /// 获取模型元素的物流类型
+ ///
+ private LogisticsElementType GetLogisticsElementType(ModelItem item)
+ {
+ // 尝试从自定义属性读取
+ try
+ {
+ var categories = item.PropertyCategories;
+ foreach (var category in categories)
+ {
+ if (category.DisplayName == LogisticsCategories.LOGISTICS ||
+ category.Name == LogisticsCategories.CATEGORY_INTERNAL_NAME)
+ {
+ foreach (var prop in category.Properties)
+ {
+ if (prop.DisplayName == LogisticsProperties.TYPE)
+ {
+ string typeStr = prop.Value.ToDisplayString();
+ if (Enum.TryParse(typeStr, out var parsedType))
+ {
+ return parsedType;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Debug($"读取物流属性失败: {ex.Message}");
+ }
+
+ // 默认:根据元素名称推断类型
+ string displayName = item.DisplayName?.ToLower() ?? "";
+
+ if (displayName.Contains("门") || displayName.Contains("door"))
+ return LogisticsElementType.门;
+ else if (displayName.Contains("楼梯") || displayName.Contains("stair"))
+ return LogisticsElementType.楼梯;
+ else if (displayName.Contains("电梯") || displayName.Contains("elevator"))
+ return LogisticsElementType.电梯;
+ else if (displayName.Contains("通道") || displayName.Contains("corridor"))
+ return LogisticsElementType.通道;
+ else
+ return LogisticsElementType.障碍物; // 默认为障碍物
+ }
+
+ ///
+ /// 判断物流类型是否为障碍物
+ ///
+ private bool IsObstacleType(LogisticsElementType type)
+ {
+ switch (type)
+ {
+ case LogisticsElementType.障碍物:
+ return true;
+
+ case LogisticsElementType.门:
+ case LogisticsElementType.通道:
+ case LogisticsElementType.楼梯:
+ case LogisticsElementType.电梯:
+ case LogisticsElementType.装卸区:
+ case LogisticsElementType.停车位:
+ return false;
+
+ default:
+ return true; // 未知类型默认为障碍物
+ }
+ }
+
+ ///
+ /// 快速测试方法 - 生成简单的测试场景体素网格
+ ///
+ /// 测试用体素网格
+ public static VoxelGrid CreateTestGrid()
+ {
+ // 创建 10m × 10m × 3m 的测试房间
+ double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(
+ Autodesk.Navisworks.Api.Application.ActiveDocument.Units);
+
+ var bounds = new BoundingBox3D(
+ new Point3D(0, 0, 0),
+ new Point3D(10 * metersToModelUnits, 10 * metersToModelUnits, 3 * metersToModelUnits)
+ );
+
+ double voxelSize = 0.5 * metersToModelUnits; // 0.5米体素
+
+ var grid = new VoxelGrid(bounds, voxelSize);
+
+ LogManager.Info($"创建测试网格: {grid}");
+
+ // 添加一个简单的障碍物(中心位置的柱子)
+ int centerX = grid.SizeX / 2;
+ int centerY = grid.SizeY / 2;
+
+ for (int z = 0; z < grid.SizeZ; z++)
+ {
+ for (int dx = -1; dx <= 1; dx++)
+ {
+ for (int dy = -1; dy <= 1; dy++)
+ {
+ int x = centerX + dx;
+ int y = centerY + dy;
+
+ if (grid.IsValidIndex(x, y, z))
+ {
+ var cell = grid.GetCell(x, y, z);
+ cell.SetAsObstacle();
+ }
+ }
+ }
+ }
+
+ var (total, passable, obstacle) = grid.GetStatistics();
+ LogManager.Info($"测试网格统计: 总={total}, 可通行={passable}, 障碍={obstacle}");
+
+ return grid;
+ }
+ }
+}