增加了对门的处理,支持设置限宽
This commit is contained in:
parent
13bc16dd62
commit
739392ef7b
@ -3276,12 +3276,13 @@ namespace NavisworksTransport
|
||||
string gridTypeName = "";
|
||||
|
||||
// 根据网格类型和用户设置确定目标路径和名称
|
||||
if (cell.IsWalkable && cell.CellType == CategoryAttributeManager.LogisticsElementType.通道)
|
||||
if (cell.IsWalkable && (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道
|
||||
|| cell.CellType == CategoryAttributeManager.LogisticsElementType.门))
|
||||
{
|
||||
if (_showWalkableGrid) // 检查可通行网格开关
|
||||
{
|
||||
targetRoute = channelRoute;
|
||||
gridTypeName = "通道";
|
||||
gridTypeName = cell.CellType == CategoryAttributeManager.LogisticsElementType.门 ? "门" : "通道";
|
||||
channelCells++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1175,6 +1175,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定点在给定高度约束下是否可通行
|
||||
/// </summary>
|
||||
/// <param name="point">检查点</param>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="vehicleHeight">车辆高度</param>
|
||||
/// <returns>是否可通行</returns>
|
||||
/// <summary>
|
||||
/// 检查指定点在给定高度约束下是否可通行
|
||||
/// </summary>
|
||||
@ -1187,8 +1194,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
try
|
||||
{
|
||||
// 将世界坐标转换为网格坐标
|
||||
int gridX = (int)Math.Floor((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Floor((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
int gridX = (int)Math.Round((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Round((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
|
||||
LogManager.Info($"[高度检查] 检查点 ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格坐标 ({gridX}, {gridY})");
|
||||
|
||||
@ -1300,8 +1307,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
try
|
||||
{
|
||||
// 将世界坐标转换为网格坐标
|
||||
int gridX = (int)Math.Floor((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Floor((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
int gridX = (int)Math.Round((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Round((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
if (gridX < 0 || gridX >= gridMap.Width || gridY < 0 || gridY >= gridMap.Height)
|
||||
|
||||
@ -181,7 +181,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
double gridX = (worldPosition.X - Origin.X) / CellSize;
|
||||
double gridY = (worldPosition.Y - Origin.Y) / CellSize;
|
||||
|
||||
return new GridPoint2D((int)Math.Floor(gridX), (int)Math.Floor(gridY));
|
||||
return new GridPoint2D((int)Math.Round(gridX), (int)Math.Round(gridY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -212,48 +212,53 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("[包围盒2.5D模式] 开始生成网格地图");
|
||||
LogManager.Info("【包围盒2.5D模式】开始生成网格地图");
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
// 重要:将米制网格大小转换为模型单位
|
||||
double metersToModelUnitsConversionFactor = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
|
||||
double cellSizeInModelUnits = cellSize * metersToModelUnitsConversionFactor;
|
||||
LogManager.Info($"[包围盒2.5D模式] 网格大小单位转换: {cellSize}米 → {cellSizeInModelUnits:F2}模型单位 (转换系数: {metersToModelUnitsConversionFactor:F2})");
|
||||
LogManager.Info($"【包围盒2.5D模式】网格大小单位转换: {cellSize}米 → {cellSizeInModelUnits:F2}模型单位 (转换系数: {metersToModelUnitsConversionFactor:F2})");
|
||||
|
||||
// 1. 使用通道构建器构建基础通道覆盖网格
|
||||
LogManager.Info("[包围盒2.5D模式] 步骤1: 构建通道覆盖网格");
|
||||
LogManager.Info("【包围盒2.5D模式】步骤1: 构建通道覆盖网格");
|
||||
var channelCoverage = _channelBuilder.BuildChannelCoverage(cellSizeInModelUnits, document);
|
||||
|
||||
LogManager.Info($"[包围盒2.5D模式] 通道覆盖构建完成: {channelCoverage.GetStatistics()}");
|
||||
LogManager.Info($"[阶段1完成] 通道覆盖网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"【包围盒2.5D模式】通道覆盖构建完成: {channelCoverage.GetStatistics()}");
|
||||
LogManager.Info($"【阶段1完成】通道覆盖网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 1.5. 使用智能收集策略收集所有通道相关节点
|
||||
LogManager.Info("[包围盒2.5D模式] 步骤1.5: 智能收集通道相关节点");
|
||||
LogManager.Info("【包围盒2.5D模式】步骤1.5: 智能收集通道相关节点");
|
||||
|
||||
// 直接获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
LogManager.Info($"[包围盒2.5D模式] 直接获取到 {allChannelItems.Count} 个可通行物流元素");
|
||||
LogManager.Info($"【包围盒2.5D模式】直接获取到 {allChannelItems.Count} 个可通行物流元素");
|
||||
|
||||
// 智能收集通道相关节点(包括父节点、同级节点等)
|
||||
var channelRelatedItems = CollectChannelRelatedItems(allChannelItems.ToList());
|
||||
LogManager.Info($"[包围盒2.5D模式] 智能收集到 {channelRelatedItems.Count} 个通道相关节点");
|
||||
LogManager.Info($"【包围盒2.5D模式】智能收集到 {channelRelatedItems.Count} 个通道相关节点");
|
||||
|
||||
// 2. 直接遍历处理障碍物(包围盒检测) - 使用高性能优化版本
|
||||
LogManager.Info("[包围盒2.5D模式] 步骤2: 高性能包围盒遍历处理障碍物");
|
||||
LogManager.Info("【包围盒2.5D模式】步骤2: 高性能包围盒遍历处理障碍物");
|
||||
double scanHeight = vehicleHeight + safetyMargin; // 扫描高度 = 车辆高度 + 安全间隙
|
||||
ProcessObstaclesWithBoundingBoxOptimized(document, channelCoverage.GridMap, channelRelatedItems, scanHeight);
|
||||
|
||||
LogManager.Info($"[阶段2完成] 障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"【阶段2完成】障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 2.5. 单独处理门元素,设置为可通行
|
||||
LogManager.Info("【包围盒2.5D模式】步骤2.5: 处理门元素");
|
||||
ProcessDoorElements(channelCoverage.GridMap, allChannelItems);
|
||||
LogManager.Info($"【阶段2.5完成】门元素处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 3. 应用车辆尺寸膨胀(如果需要)
|
||||
if (vehicleRadius > 0 || safetyMargin > 0)
|
||||
{
|
||||
LogManager.Info("[包围盒2.5D模式] 步骤3: 应用车辆膨胀");
|
||||
LogManager.Info("【包围盒2.5D模式】步骤3: 应用车辆膨胀");
|
||||
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
|
||||
double totalInflationRadius = (vehicleRadius + safetyMargin) * metersToModelUnits;
|
||||
ApplyVehicleInflation(channelCoverage.GridMap, totalInflationRadius);
|
||||
LogManager.Info($"[包围盒2.5D模式] 车辆膨胀完成,膨胀半径: {totalInflationRadius:F2}模型单位");
|
||||
LogManager.Info($"[阶段3完成] 车辆膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"【包围盒2.5D模式】车辆膨胀完成,膨胀半径: {totalInflationRadius:F2}模型单位");
|
||||
LogManager.Info($"【阶段3完成】车辆膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
}
|
||||
|
||||
// 注意:边界膨胀已集成到车辆膨胀中,无需单独处理
|
||||
@ -273,26 +278,125 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (channelRelatedItems != null && channelRelatedItems.Any())
|
||||
{
|
||||
channelCoverage.GridMap.ChannelItems = channelRelatedItems.ToList();
|
||||
LogManager.Info($"[包围盒2.5D模式] 已将 {channelRelatedItems.Count} 个通道数据设置到GridMap中");
|
||||
LogManager.Info($"【包围盒2.5D模式】已将 {channelRelatedItems.Count} 个通道数据设置到GridMap中");
|
||||
}
|
||||
else if (channelCoverage.ChannelItems != null && channelCoverage.ChannelItems.Any())
|
||||
{
|
||||
channelCoverage.GridMap.ChannelItems = new List<ModelItem>(channelCoverage.ChannelItems);
|
||||
LogManager.Info($"[包围盒2.5D模式] 已将 {channelCoverage.ChannelItems.Count} 个通道数据设置到GridMap中");
|
||||
LogManager.Info($"【包围盒2.5D模式】已将 {channelCoverage.ChannelItems.Count} 个通道数据设置到GridMap中");
|
||||
}
|
||||
|
||||
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
LogManager.Info($"[包围盒2.5D模式] 网格地图生成完成: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"[包围盒2.5D模式] 总耗时: {elapsed:F1}ms");
|
||||
LogManager.Info($"【包围盒2.5D模式】网格地图生成完成: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"【包围盒2.5D模式】总耗时: {elapsed:F1}ms");
|
||||
return channelCoverage.GridMap;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[包围盒2.5D模式] 生成失败: {ex.Message}");
|
||||
LogManager.Error($"【包围盒2.5D模式】生成失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理门元素,将其设置为可通行网格
|
||||
/// </summary>
|
||||
private void ProcessDoorElements(GridMap gridMap, ICollection<ModelItem> allChannelItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 过滤出门类型的元素
|
||||
var doorItems = FilterDoorItems(allChannelItems);
|
||||
LogManager.Info($"【门元素处理】找到 {doorItems.Count} 个门元素");
|
||||
|
||||
if (!doorItems.Any())
|
||||
{
|
||||
LogManager.Info("【门元素处理】没有找到门元素,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
int processedCount = 0;
|
||||
int skippedCount = 0;
|
||||
foreach (var doorItem in doorItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取门元素的包围盒
|
||||
var bbox = doorItem.BoundingBox();
|
||||
if (bbox == null)
|
||||
{
|
||||
LogManager.Debug($"【门元素处理】门元素无有效包围盒,跳过: {doorItem.DisplayName}");
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 使用限宽计算门的实际开口区域
|
||||
var coveredCells = CalculateDoorOpeningCoverage(doorItem, bbox, gridMap);
|
||||
|
||||
if (!coveredCells.Any())
|
||||
{
|
||||
LogManager.Warning($"【门元素处理】门元素 {doorItem.DisplayName} 无法计算有效开口区域,跳过处理");
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 设置开口区域的网格为可通行的门类型
|
||||
foreach (var (x, y) in coveredCells)
|
||||
{
|
||||
var gridPos = new GridPoint2D(x, y);
|
||||
// 使用现有的SetCell方法,让它自动计算门的权重(1.2)
|
||||
gridMap.SetCell(gridPos, true, 0, CategoryAttributeManager.LogisticsElementType.门);
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
LogManager.Debug($"【门元素处理】已处理门元素: {doorItem.DisplayName},开口区域覆盖网格数量: {coveredCells.Count}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"【门元素处理】处理门元素失败: {doorItem.DisplayName}, 错误: {ex.Message}");
|
||||
skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【门元素处理】完成,已处理 {processedCount} 个门元素,跳过 {skippedCount} 个");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"【门元素处理】处理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从物流元素集合中过滤出门类型的元素
|
||||
/// </summary>
|
||||
private List<ModelItem> FilterDoorItems(ICollection<ModelItem> items)
|
||||
{
|
||||
var doorItems = new List<ModelItem>();
|
||||
|
||||
if (items == null || !items.Any())
|
||||
{
|
||||
return doorItems;
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
|
||||
if (logisticsType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
{
|
||||
doorItems.Add(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"【门元素过滤】获取物流类型失败: {item.DisplayName}, 错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return doorItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成网格点坐标用于垂直扫描
|
||||
/// </summary>
|
||||
@ -362,6 +466,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将高度区间信息集成到网格地图中
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="heightIntervals">高度区间数据</param>
|
||||
/// <summary>
|
||||
/// 将高度区间信息集成到网格地图中
|
||||
/// </summary>
|
||||
@ -379,8 +488,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
var intervals = kvp.Value;
|
||||
|
||||
// 将世界坐标转换为网格坐标
|
||||
int gridX = (int)Math.Floor((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Floor((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
int gridX = (int)Math.Round((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize);
|
||||
int gridY = (int)Math.Round((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize);
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
if (gridX >= 0 && gridX < gridMap.Width && gridY >= 0 && gridY < gridMap.Height)
|
||||
@ -548,7 +657,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
LogManager.Info($"[高效膨胀] 使用距离变换算法,网格大小: {gridMap.Width}x{gridMap.Height}");
|
||||
LogManager.Info($"【高效膨胀】使用距离变换算法,网格大小: {gridMap.Width}x{gridMap.Height}");
|
||||
|
||||
// 创建距离矩阵
|
||||
var distanceMap = new int[gridMap.Width, gridMap.Height];
|
||||
@ -583,9 +692,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[统一膨胀] 膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}");
|
||||
LogManager.Info($"【统一膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}");
|
||||
|
||||
LogManager.Info($"[高效膨胀] 距离矩阵初始化完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【高效膨胀】距离矩阵初始化完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 正向扫描(从左上到右下)
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
@ -628,7 +737,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[高效膨胀] 正向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【高效膨胀】正向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 反向扫描(从右下到左上)
|
||||
for (int x = gridMap.Width - 1; x >= 0; x--)
|
||||
@ -671,15 +780,16 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[高效膨胀] 反向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【高效膨胀】反向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 应用膨胀结果
|
||||
int inflatedCells = 0;
|
||||
int processedCells = 0;
|
||||
int totalCells = gridMap.Width * gridMap.Height;
|
||||
int skippedCells = 0;
|
||||
int protectedDoorCells = 0;
|
||||
|
||||
LogManager.Info($"[高效膨胀] 开始应用膨胀结果,总单元格数: {totalCells}");
|
||||
LogManager.Info($"【高效膨胀】开始应用膨胀结果,总单元格数: {totalCells}");
|
||||
|
||||
try
|
||||
{
|
||||
@ -693,21 +803,31 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (processedCells % 500000 == 0)
|
||||
{
|
||||
double progress = (double)processedCells / totalCells * 100;
|
||||
LogManager.Info($"[高效膨胀] 应用进度: {progress:F1}% ({processedCells}/{totalCells})");
|
||||
LogManager.Info($"【高效膨胀】应用进度: {progress:F1}% ({processedCells}/{totalCells})");
|
||||
}
|
||||
|
||||
var gridPos = new GridPoint2D(x, y);
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 只有满足以下所有条件才进行膨胀:
|
||||
// 1. 当前位置原本是可通行的
|
||||
// 2. 距离值不是无穷大(有有效的距离计算)
|
||||
// 3. 距离小于等于膨胀半径
|
||||
// 4. 距离值大于0(不是原始障碍物)
|
||||
// 5. 不是门类型(门保护)
|
||||
if (gridMap.IsWalkable(gridPos) &&
|
||||
distanceMap[x, y] != int.MaxValue &&
|
||||
distanceMap[x, y] > 0 &&
|
||||
distanceMap[x, y] <= inflationRadius)
|
||||
{
|
||||
// 门元素保护:跳过门类型的网格,不将其膨胀为障碍物
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
{
|
||||
protectedDoorCells++;
|
||||
// 跳过门网格,保持其可通行状态
|
||||
continue;
|
||||
}
|
||||
|
||||
gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物);
|
||||
inflatedCells++;
|
||||
}
|
||||
@ -720,7 +840,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[高效膨胀] 应用膨胀结果时发生错误: {ex.Message},已处理{processedCells}/{totalCells}个单元格");
|
||||
LogManager.Error($"【高效膨胀】应用膨胀结果时发生错误: {ex.Message},已处理{processedCells}/{totalCells}个单元格");
|
||||
throw;
|
||||
}
|
||||
|
||||
@ -728,7 +848,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
int boundaryInflatedCells = ApplyBoundaryInflationPost(gridMap, inflationRadius);
|
||||
|
||||
stopwatch.Stop();
|
||||
LogManager.Info($"[高效膨胀] 车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 边界膨胀={boundaryInflatedCells}个, 跳过={skippedCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【高效膨胀】车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 边界膨胀={boundaryInflatedCells}个, 跳过={skippedCells}个, 保护门网格={protectedDoorCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1171,6 +1291,160 @@ namespace NavisworksTransport.PathPlanning
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算门开口区域在网格上覆盖的单元坐标(考虑限宽)
|
||||
/// </summary>
|
||||
/// <param name="doorItem">门模型项</param>
|
||||
/// <param name="bbox">门的包围盒</param>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <returns>开口区域覆盖的网格单元坐标列表</returns>
|
||||
private List<(int x, int y)> CalculateDoorOpeningCoverage(ModelItem doorItem, BoundingBox3D bbox, GridMap gridMap)
|
||||
{
|
||||
var coveredCells = new List<(int x, int y)>();
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 获取门的限宽属性
|
||||
string widthLimitStr = CategoryAttributeManager.GetLogisticsPropertyValue(doorItem, CategoryAttributeManager.LogisticsProperties.WIDTH_LIMIT);
|
||||
|
||||
if (string.IsNullOrEmpty(widthLimitStr))
|
||||
{
|
||||
LogManager.Warning($"【门开口计算】门元素 {doorItem.DisplayName} 没有设置限宽属性,跳过处理");
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
// 2. 解析限宽属性值(支持带单位的格式)
|
||||
double limitWidthMeters = ParseWidthLimitValue(widthLimitStr);
|
||||
|
||||
if (limitWidthMeters <= 0)
|
||||
{
|
||||
LogManager.Warning($"【门开口计算】门元素 {doorItem.DisplayName} 限宽属性解析失败: {widthLimitStr}");
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
// 3. 转换限宽到模型单位
|
||||
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
|
||||
double limitWidthInModelUnits = limitWidthMeters * metersToModelUnits;
|
||||
|
||||
// 4. 分析门的方向:哪个数大,哪个就是门的宽度
|
||||
double width = bbox.Max.X - bbox.Min.X; // X方向宽度
|
||||
double depth = bbox.Max.Y - bbox.Min.Y; // Y方向深度
|
||||
double doorWidth = Math.Max(width, depth); // 门宽(较大的那个方向)
|
||||
bool isDoorWidthInXDirection = width > depth; // 门宽是否在X方向
|
||||
|
||||
LogManager.Debug($"【门开口计算】{doorItem.DisplayName} - 尺寸: {width:F2}x{depth:F2}, 门宽: {doorWidth:F2}, 门宽方向: {(isDoorWidthInXDirection ? "X" : "Y")}, 限宽: {limitWidthMeters}m");
|
||||
|
||||
// 5. 限宽约束:限宽不能超过门宽
|
||||
double effectiveWidth = Math.Min(limitWidthInModelUnits, doorWidth);
|
||||
|
||||
if (limitWidthInModelUnits > doorWidth)
|
||||
{
|
||||
LogManager.Info($"【门开口计算】{doorItem.DisplayName} 限宽({limitWidthMeters}m)超过门宽({doorWidth / metersToModelUnits:F2}m),按门宽处理");
|
||||
}
|
||||
|
||||
// 6. 直接计算开口区域坐标范围
|
||||
double minX, maxX, minY, maxY;
|
||||
|
||||
if (isDoorWidthInXDirection)
|
||||
{
|
||||
// 门宽在X方向:在X方向应用限宽,Y方向保持完整
|
||||
double centerX = (bbox.Min.X + bbox.Max.X) / 2;
|
||||
double halfWidth = effectiveWidth / 2;
|
||||
minX = centerX - halfWidth;
|
||||
maxX = centerX + halfWidth;
|
||||
minY = bbox.Min.Y;
|
||||
maxY = bbox.Max.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 门宽在Y方向:在Y方向应用限宽,X方向保持完整
|
||||
double centerY = (bbox.Min.Y + bbox.Max.Y) / 2;
|
||||
double halfWidth = effectiveWidth / 2;
|
||||
minX = bbox.Min.X;
|
||||
maxX = bbox.Max.X;
|
||||
minY = centerY - halfWidth;
|
||||
maxY = centerY + halfWidth;
|
||||
}
|
||||
|
||||
LogManager.Debug($"【门开口计算】{doorItem.DisplayName} 开口区域坐标: [{minX:F2},{minY:F2}]-[{maxX:F2},{maxY:F2}]");
|
||||
|
||||
// 7. 将开口区域坐标直接转换为网格坐标
|
||||
var minGridPos = gridMap.WorldToGrid(new Point3D(minX, minY, 0));
|
||||
var maxGridPos = gridMap.WorldToGrid(new Point3D(maxX, maxY, 0));
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
int gridMinX = Math.Max(0, Math.Min(minGridPos.X, maxGridPos.X));
|
||||
int gridMinY = Math.Max(0, Math.Min(minGridPos.Y, maxGridPos.Y));
|
||||
int gridMaxX = Math.Min(gridMap.Width - 1, Math.Max(minGridPos.X, maxGridPos.X));
|
||||
int gridMaxY = Math.Min(gridMap.Height - 1, Math.Max(minGridPos.Y, maxGridPos.Y));
|
||||
|
||||
// 确保至少有1个网格可通行
|
||||
if (gridMinX >= gridMaxX)
|
||||
{
|
||||
int centerX = (gridMinX + gridMaxX) / 2;
|
||||
gridMinX = Math.Max(0, centerX);
|
||||
gridMaxX = Math.Min(gridMap.Width - 1, centerX);
|
||||
}
|
||||
if (gridMinY >= gridMaxY)
|
||||
{
|
||||
int centerY = (gridMinY + gridMaxY) / 2;
|
||||
gridMinY = Math.Max(0, centerY);
|
||||
gridMaxY = Math.Min(gridMap.Height - 1, centerY);
|
||||
}
|
||||
|
||||
// 8. 生成覆盖的网格单元列表
|
||||
for (int x = gridMinX; x <= gridMaxX; x++)
|
||||
{
|
||||
for (int y = gridMinY; y <= gridMaxY; y++)
|
||||
{
|
||||
coveredCells.Add((x, y));
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【门开口计算】{doorItem.DisplayName} 成功处理 - 门宽: {doorWidth / metersToModelUnits:F2}m, 限宽: {limitWidthMeters}m, 开口覆盖: {coveredCells.Count}个网格 (范围: [{gridMinX},{gridMinY}]-[{gridMaxX},{gridMaxY}])");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"【门开口计算】计算失败: {doorItem.DisplayName}, 错误: {ex.Message}");
|
||||
// 出错时返回空列表,不影响其他门的处理
|
||||
}
|
||||
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析限宽属性值,支持带单位的字符串(如 "3.00 m")
|
||||
/// </summary>
|
||||
/// <param name="widthLimitStr">限宽属性字符串</param>
|
||||
/// <returns>解析出的数值(米),-1表示解析失败</returns>
|
||||
private double ParseWidthLimitValue(string widthLimitStr)
|
||||
{
|
||||
if (string.IsNullOrEmpty(widthLimitStr))
|
||||
return -1;
|
||||
|
||||
try
|
||||
{
|
||||
// 移除单位后缀和多余空格
|
||||
string cleanValue = widthLimitStr.Trim()
|
||||
.Replace(" m", "") // 移除 " m"
|
||||
.Replace("m", "") // 移除 "m"
|
||||
.Replace(" ", ""); // 移除所有空格
|
||||
|
||||
if (double.TryParse(cleanValue, out double value) && value > 0)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
LogManager.Debug($"【限宽解析】无法解析数值: '{widthLimitStr}' -> '{cleanValue}'");
|
||||
return -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"【限宽解析】解析异常: '{widthLimitStr}', 错误: {ex.Message}");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查包围盒是否在扫描高度范围内
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user