修改了膨胀算法,识别多层的边界进行膨胀。给每层增加了IsWalkable。
This commit is contained in:
parent
a4eaf46723
commit
9ea89aa8d0
@ -3453,20 +3453,21 @@ namespace NavisworksTransport
|
||||
double layerZ = layer.Z;
|
||||
double passableHeight = layer.PassableHeight.GetSpan();
|
||||
|
||||
// 判断高度是否足够车辆通行
|
||||
bool isHeightSufficient = passableHeight >= vehicleHeightInModelUnits;
|
||||
// 判断该层是否可通行(考虑膨胀影响和高度限制)
|
||||
bool isLayerWalkable = layer.IsWalkable; // 膨胀影响
|
||||
bool isHeightSufficient = passableHeight >= vehicleHeightInModelUnits; // 高度限制
|
||||
|
||||
// 调试:输出高度不足的层
|
||||
if (!isHeightSufficient && multiLayerCells < 5) // 只输出前5个网格的详情
|
||||
// 调试:输出不可通行的层
|
||||
if ((!isLayerWalkable || !isHeightSufficient) && multiLayerCells < 5) // 只输出前5个网格的详情
|
||||
{
|
||||
LogManager.Debug($"[高度判断] 网格({x},{y}) Layer Z={layerZ:F2}, PassableH={passableHeight:F2}, 车辆H={vehicleHeightInModelUnits:F2}, 高度足够={isHeightSufficient}");
|
||||
LogManager.Debug($"[层通行性] 网格({x},{y}) Layer Z={layerZ:F2}, IsWalkable={isLayerWalkable}, PassableH={passableHeight:F2}, 车辆H={vehicleHeightInModelUnits:F2}, 高度足够={isHeightSufficient}");
|
||||
}
|
||||
|
||||
PathRoute targetRoute = null;
|
||||
string gridTypeName = "";
|
||||
|
||||
// 根据高度是否足够和网格类型决定渲染方式
|
||||
if (isHeightSufficient)
|
||||
// 根据层可通行性、高度是否足够和网格类型决定渲染方式
|
||||
if (isLayerWalkable && isHeightSufficient)
|
||||
{
|
||||
// 高度足够 - 按原类型渲染
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道)
|
||||
@ -3499,11 +3500,25 @@ namespace NavisworksTransport
|
||||
}
|
||||
else
|
||||
{
|
||||
// 高度不足 - 渲染为障碍物(灰色)
|
||||
// 层不可通行(被膨胀影响或高度不足)- 渲染为障碍物(灰色)
|
||||
if (_showObstacleGrid)
|
||||
{
|
||||
targetRoute = obstacleRoute;
|
||||
gridTypeName = "障碍_高度不足"; // 必须包含"障碍"关键词才能渲染为灰色
|
||||
|
||||
// 区分不可通行的原因
|
||||
if (!isLayerWalkable && !isHeightSufficient)
|
||||
{
|
||||
gridTypeName = "障碍_膨胀+高度不足";
|
||||
}
|
||||
else if (!isLayerWalkable)
|
||||
{
|
||||
gridTypeName = "障碍_膨胀影响";
|
||||
}
|
||||
else
|
||||
{
|
||||
gridTypeName = "障碍_高度不足";
|
||||
}
|
||||
|
||||
heightInsufficientLayers++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,6 +168,10 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var layer = cell.HeightLayers[li];
|
||||
|
||||
// 检查该层是否可通行(膨胀影响)
|
||||
if (!layer.IsWalkable)
|
||||
continue;
|
||||
|
||||
// 楼梯/电梯区域跳过层0(下方楼板,不是可行走表面)
|
||||
if (li == 0 && (layer.Type == CategoryAttributeManager.LogisticsElementType.楼梯 ||
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.电梯))
|
||||
|
||||
@ -851,6 +851,19 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
public CategoryAttributeManager.LogisticsElementType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否是边界层(用于膨胀计算)
|
||||
/// 边界定义:当前网格层数 > 邻居网格层数,且层间高度差超过阈值
|
||||
/// </summary>
|
||||
public bool IsBoundary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 该层是否可通行(用于多层膨胀)
|
||||
/// true: 该层可以通行
|
||||
/// false: 该层被膨胀影响,不可通行
|
||||
/// </summary>
|
||||
public bool IsWalkable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
@ -861,6 +874,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
SourceItem = sourceItem;
|
||||
SpeedLimit = speedLimit;
|
||||
Type = type;
|
||||
IsBoundary = false; // 默认不是边界
|
||||
IsWalkable = true; // 默认可通行,通过膨胀算法修改
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
public class GridMapGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 边界检测的最大高度差(米)
|
||||
/// 与AutoPathFinder中的MAX_HEIGHT_DIFF_METERS保持一致
|
||||
/// 用于判断层间是否构成边界(超过此阈值则标记为边界)
|
||||
/// </summary>
|
||||
private const double MAX_HEIGHT_DIFF_METERS = 0.35;
|
||||
|
||||
private readonly CategoryAttributeManager _categoryManager;
|
||||
private readonly ChannelBasedGridBuilder _channelBuilder;
|
||||
|
||||
@ -145,6 +152,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
ProcessDoorElements(channelCoverage.GridMap, allTraversableItems);
|
||||
LogManager.Info($"【阶段2.6完成】门元素处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 2.7. 标记边界层(用于膨胀计算)
|
||||
LogManager.Info("【生成网格地图】步骤2.7: 标记边界层");
|
||||
double maxHeightDiffInModelUnits = MAX_HEIGHT_DIFF_METERS * metersToModelUnitsConversionFactor;
|
||||
LogManager.Info($"【生成网格地图】边界高度差阈值: {MAX_HEIGHT_DIFF_METERS}米 = {maxHeightDiffInModelUnits:F2}模型单位");
|
||||
MarkBoundaryLayers(channelCoverage.GridMap, maxHeightDiffInModelUnits);
|
||||
LogManager.Info($"【阶段2.7完成】边界层标记后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 3. 应用车辆尺寸膨胀(如果需要)
|
||||
if (vehicleRadius > 0 || safetyMargin > 0)
|
||||
{
|
||||
@ -410,6 +424,111 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"【高度约束设置】其中楼梯/电梯下方层: {stairBottomLayersProcessed} 个(使用实际可通行高度)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记边界层(用于膨胀计算)
|
||||
/// 边界定义:当前网格层数 > 邻居网格层数(梯度≥1),且层间高度差超过阈值
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="maxHeightDiffInModelUnits">最大允许高度差(模型单位)</param>
|
||||
private void MarkBoundaryLayers(GridMap gridMap, double maxHeightDiffInModelUnits)
|
||||
{
|
||||
LogManager.Info($"【边界检测】开始标记边界层,高度差阈值: {maxHeightDiffInModelUnits:F2}模型单位");
|
||||
|
||||
int totalBoundaryLayers = 0;
|
||||
int totalCheckedCells = 0;
|
||||
|
||||
// 4个方向:上下左右
|
||||
int[] dx = { 0, 0, -1, 1 };
|
||||
int[] dy = { -1, 1, 0, 0 };
|
||||
string[] dirNames = { "下", "上", "左", "右" };
|
||||
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 只检查可通行且有高度层的网格
|
||||
if (!cell.IsWalkable || cell.HeightLayers == null || cell.HeightLayers.Count == 0)
|
||||
continue;
|
||||
|
||||
totalCheckedCells++;
|
||||
int currentLayerCount = cell.HeightLayers.Count;
|
||||
|
||||
// 检查4个方向的邻居
|
||||
for (int dir = 0; dir < 4; dir++)
|
||||
{
|
||||
int nx = x + dx[dir];
|
||||
int ny = y + dy[dir];
|
||||
|
||||
// 边界检查
|
||||
if (nx < 0 || nx >= gridMap.Width || ny < 0 || ny >= gridMap.Height)
|
||||
continue;
|
||||
|
||||
var neighbor = gridMap.Cells[nx, ny];
|
||||
int neighborLayerCount = (neighbor.HeightLayers != null) ? neighbor.HeightLayers.Count : 0;
|
||||
|
||||
// 层数梯度检测:当前层数 > 邻居层数
|
||||
if (currentLayerCount <= neighborLayerCount)
|
||||
continue;
|
||||
|
||||
int gradient = currentLayerCount - neighborLayerCount;
|
||||
|
||||
// 检查每个梯度层是否构成边界
|
||||
// 例如:当前2层,邻居1层,梯度=1,检查当前Layer[1]是否是边界
|
||||
// 例如:当前3层,邻居1层,梯度=2,检查Layer[1]和Layer[2]
|
||||
for (int layerOffset = 1; layerOffset <= gradient; layerOffset++)
|
||||
{
|
||||
int currentLayerIndex = neighborLayerCount + layerOffset - 1;
|
||||
|
||||
if (currentLayerIndex >= currentLayerCount)
|
||||
continue;
|
||||
|
||||
var currentLayer = cell.HeightLayers[currentLayerIndex];
|
||||
|
||||
// 获取对比层的Z值
|
||||
double currentZ = currentLayer.Z;
|
||||
double neighborZ = 0;
|
||||
|
||||
if (neighborLayerCount > 0)
|
||||
{
|
||||
// 邻居有层:取邻居最高层的Z值
|
||||
neighborZ = neighbor.HeightLayers[neighborLayerCount - 1].Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 邻居无层(Unknown):使用网格底部Z值
|
||||
neighborZ = gridMap.Bounds.Min.Z;
|
||||
}
|
||||
|
||||
// 计算层间高度差
|
||||
double heightDiff = Math.Abs(currentZ - neighborZ);
|
||||
|
||||
// 高度差判定
|
||||
if (heightDiff > maxHeightDiffInModelUnits)
|
||||
{
|
||||
// 高度差超过阈值,标记为边界
|
||||
if (!currentLayer.IsBoundary)
|
||||
{
|
||||
currentLayer.IsBoundary = true;
|
||||
cell.HeightLayers[currentLayerIndex] = currentLayer;
|
||||
totalBoundaryLayers++;
|
||||
|
||||
LogManager.Debug($"【边界检测】网格({x},{y}) Layer{currentLayerIndex} 标记为边界," +
|
||||
$"方向={dirNames[dir]}, 当前Z={currentZ:F2}, 邻居Z={neighborZ:F2}, " +
|
||||
$"高度差={heightDiff:F2}, 梯度={gradient}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gridMap.Cells[x, y] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【边界检测】完成,检查了 {totalCheckedCells} 个网格,标记了 {totalBoundaryLayers} 个边界层");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从物流元素集合中过滤出门类型的元素
|
||||
/// </summary>
|
||||
@ -699,13 +818,222 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的车辆膨胀算法 - 使用正确的8连通距离变换方法
|
||||
/// 时间复杂度: O(n*m) 而不是 O(n*m*r^2)
|
||||
/// 多层膨胀算法 - 按高度层分别处理膨胀
|
||||
/// 核心原则:边界层只影响同层索引的邻居层
|
||||
/// </summary>
|
||||
private void ApplyVehicleInflationOptimized(GridMap gridMap, int inflationRadius)
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
LogManager.Info($"【多层膨胀】开始按层处理膨胀,网格大小: {gridMap.Width}x{gridMap.Height}, 膨胀半径: {inflationRadius}");
|
||||
|
||||
// 统计每个层索引的处理情况
|
||||
var layerStats = new Dictionary<int, (int boundary, int inflated)>();
|
||||
int maxLayerIndex = 0;
|
||||
|
||||
// 第一步:找出最大层索引
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
|
||||
{
|
||||
maxLayerIndex = Math.Max(maxLayerIndex, cell.HeightLayers.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【多层膨胀】检测到最大层索引: {maxLayerIndex}");
|
||||
|
||||
// 第二步:对每个层索引分别处理膨胀
|
||||
for (int layerIndex = 0; layerIndex <= maxLayerIndex; layerIndex++)
|
||||
{
|
||||
int boundaryCount = 0;
|
||||
int inflatedCount = 0;
|
||||
|
||||
// 为这一层创建距离图
|
||||
var distanceMap = new double[gridMap.Width, gridMap.Height];
|
||||
const double SQRT2 = 1.414213562373095;
|
||||
|
||||
// 初始化:标记障碍物、Unknown和边界层
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 默认为无穷大
|
||||
distanceMap[x, y] = double.MaxValue;
|
||||
|
||||
// 1. 整个网格是障碍物 → 距离为0
|
||||
if (!cell.IsWalkable)
|
||||
{
|
||||
distanceMap[x, y] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Unknown网格 → 距离为0
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
{
|
||||
distanceMap[x, y] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. 该层索引存在且是边界 → 距离为0
|
||||
if (cell.HeightLayers != null && layerIndex < cell.HeightLayers.Count)
|
||||
{
|
||||
var layer = cell.HeightLayers[layerIndex];
|
||||
if (layer.IsBoundary)
|
||||
{
|
||||
distanceMap[x, y] = 0.0;
|
||||
boundaryCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (boundaryCount == 0)
|
||||
{
|
||||
LogManager.Debug($"【多层膨胀】Layer{layerIndex} 无边界,跳过");
|
||||
continue;
|
||||
}
|
||||
|
||||
LogManager.Info($"【多层膨胀】Layer{layerIndex} 开始膨胀,边界数量: {boundaryCount}");
|
||||
|
||||
// 距离变换 - 正向扫描
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
if (distanceMap[x, y] == double.MaxValue)
|
||||
{
|
||||
double minDist = double.MaxValue;
|
||||
|
||||
if (x > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y] + 1.0);
|
||||
if (y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y - 1] + 1.0);
|
||||
if (x > 0 && y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y - 1] + SQRT2);
|
||||
if (x > 0 && y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y + 1] + SQRT2);
|
||||
|
||||
if (minDist != double.MaxValue)
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 距离变换 - 反向扫描
|
||||
for (int x = gridMap.Width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = gridMap.Height - 1; y >= 0; y--)
|
||||
{
|
||||
if (distanceMap[x, y] != 0.0)
|
||||
{
|
||||
double minDist = distanceMap[x, y];
|
||||
|
||||
if (x < gridMap.Width - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y] + 1.0);
|
||||
if (y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y + 1] + 1.0);
|
||||
if (x < gridMap.Width - 1 && y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y + 1] + SQRT2);
|
||||
if (x < gridMap.Width - 1 && y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y - 1] + SQRT2);
|
||||
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 应用膨胀结果:设置该层的IsWalkable
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 检查是否需要膨胀这一层
|
||||
if (cell.HeightLayers != null && layerIndex < cell.HeightLayers.Count)
|
||||
{
|
||||
var layer = cell.HeightLayers[layerIndex];
|
||||
|
||||
// 门保护:不膨胀门类型的层
|
||||
if (layer.Type == CategoryAttributeManager.LogisticsElementType.门)
|
||||
continue;
|
||||
|
||||
// 膨胀条件:距离 < 膨胀半径
|
||||
// 边界网格距离=0,算在膨胀半径内
|
||||
// 例如:半径=1 → 只膨胀边界本身(距离0)
|
||||
// 半径=2 → 膨胀边界+向内1格(距离0,1)
|
||||
// 半径=3 → 膨胀边界+向内2格(距离0,1,2)
|
||||
bool shouldInflate = false;
|
||||
|
||||
if (distanceMap[x, y] < inflationRadius)
|
||||
{
|
||||
shouldInflate = true;
|
||||
}
|
||||
|
||||
if (shouldInflate)
|
||||
{
|
||||
// 设置该层为不可通行
|
||||
layer.IsWalkable = false;
|
||||
cell.HeightLayers[layerIndex] = layer;
|
||||
inflatedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
gridMap.Cells[x, y] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
layerStats[layerIndex] = (boundaryCount, inflatedCount);
|
||||
LogManager.Info($"【多层膨胀】Layer{layerIndex} 完成,边界: {boundaryCount}, 膨胀: {inflatedCount}");
|
||||
}
|
||||
|
||||
// 第三步:更新GridCell.IsWalkable(至少有一层可通行)
|
||||
int cellsBecameUnwalkable = 0;
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
bool wasWalkable = cell.IsWalkable;
|
||||
|
||||
// 检查是否至少有一层可通行
|
||||
bool hasWalkableLayer = false;
|
||||
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
|
||||
{
|
||||
hasWalkableLayer = cell.HeightLayers.Any(layer => layer.IsWalkable);
|
||||
}
|
||||
|
||||
cell.IsWalkable = hasWalkableLayer;
|
||||
gridMap.Cells[x, y] = cell;
|
||||
|
||||
if (wasWalkable && !hasWalkableLayer)
|
||||
cellsBecameUnwalkable++;
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
LogManager.Info($"【多层膨胀】完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【多层膨胀】网格变为不可通行: {cellsBecameUnwalkable}个");
|
||||
|
||||
foreach (var kvp in layerStats.OrderBy(k => k.Key))
|
||||
{
|
||||
LogManager.Info($"【多层膨胀】Layer{kvp.Key}: 边界{kvp.Value.boundary}个, 膨胀{kvp.Value.inflated}层");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 旧的2D膨胀算法(已废弃,保留用于参考)
|
||||
/// </summary>
|
||||
private void ApplyVehicleInflationOptimized_Old2D(GridMap gridMap, int inflationRadius)
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
LogManager.Info($"【8连通膨胀】使用正确的8连通距离变换算法,网格大小: {gridMap.Width}x{gridMap.Height}");
|
||||
|
||||
// 使用浮点数距离矩阵支持√2对角线距离
|
||||
@ -715,6 +1043,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 初始化距离矩阵
|
||||
int obstacleCount = 0;
|
||||
int unknownCount = 0;
|
||||
int boundaryCount = 0;
|
||||
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
@ -722,7 +1051,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 障碍物和Unknown网格作为膨胀源点
|
||||
// 膨胀源点:1.障碍物 2.Unknown网格 3.边界层网格
|
||||
if (!cell.IsWalkable)
|
||||
{
|
||||
distanceMap[x, y] = 0.0;
|
||||
@ -733,6 +1062,12 @@ namespace NavisworksTransport.PathPlanning
|
||||
distanceMap[x, y] = 0.0;
|
||||
unknownCount++;
|
||||
}
|
||||
else if (cell.HeightLayers != null && cell.HeightLayers.Any(layer => layer.IsBoundary))
|
||||
{
|
||||
// 包含边界层的网格作为膨胀源点
|
||||
distanceMap[x, y] = 0.0;
|
||||
boundaryCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceMap[x, y] = double.MaxValue;
|
||||
@ -740,7 +1075,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【8连通膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}");
|
||||
LogManager.Info($"【8连通膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}, 边界: {boundaryCount}");
|
||||
|
||||
// 使用标准的距离变换算法 - 正向扫描
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user