294 lines
11 KiB
TypeScript
294 lines
11 KiB
TypeScript
import SKDataUtil from "../gear/SKDataUtil";
|
||
|
||
export default class PathUtil {
|
||
|
||
static getAvailabelPoint(r: number, l: number, mapArr: any[], rows: number, lines: number): any {
|
||
if (r < 0) {
|
||
r = 0;
|
||
}
|
||
if (l < 0) {
|
||
l = 0;
|
||
}
|
||
if (r >= rows) {
|
||
r = rows - 1;
|
||
}
|
||
if (l >= lines) {
|
||
l = lines - 1;
|
||
}
|
||
let value = SKDataUtil.intOf2Array(mapArr, r, l, 0);
|
||
if (value != 0) {
|
||
return {
|
||
r: r,
|
||
l: l
|
||
};
|
||
}
|
||
let count = 1;
|
||
for (let i = 0; i < 10000000; i++) {
|
||
if (count > lines && count > rows) {
|
||
return {
|
||
r: -1,
|
||
l: -1
|
||
};
|
||
}
|
||
if (r + count < rows && mapArr[r + count][l] != 0) {
|
||
return {
|
||
r: r + count,
|
||
l: l
|
||
};
|
||
}
|
||
if (l + count < lines && mapArr[r][l + count] != 0) {
|
||
return {
|
||
r: r,
|
||
l: l + count
|
||
};
|
||
}
|
||
if (r >= count && mapArr[r - count][l] != 0) {
|
||
return {
|
||
r: r - count,
|
||
l: l
|
||
};
|
||
}
|
||
if (l >= count && mapArr[r][l - count] != 0) {
|
||
return {
|
||
r: r,
|
||
l: l - count
|
||
};
|
||
}
|
||
if (r + count < rows && l + count < lines && mapArr[r + count][l + count] != 0) {
|
||
return {
|
||
r: r + count,
|
||
l: l + count
|
||
};
|
||
}
|
||
if (r >= count && l >= count && mapArr[r - count][l - count] != 0) {
|
||
return {
|
||
r: r - count,
|
||
l: l - count
|
||
};
|
||
}
|
||
if (r >= count && l + count < lines && mapArr[r - count][l + count] != 0) {
|
||
return {
|
||
r: r - count,
|
||
l: l + count
|
||
};
|
||
}
|
||
if (l >= count && r + count < rows && mapArr[r + count][l - count] != 0) {
|
||
return {
|
||
r: r + count,
|
||
l: l - count
|
||
};
|
||
}
|
||
count++;
|
||
}
|
||
};
|
||
// 寻路
|
||
static searchRoad(start_r: number, start_l: number, end_r: number, end_l: number, mapArr: any[], rows: number, cols: number): any[] {
|
||
if (end_r == -1 && end_l == -1) {
|
||
return [];
|
||
}
|
||
let openList: any[] = [], //开启列表
|
||
closeObjList: any = {}, //关闭列表索引
|
||
openObjList: any = {}, //开启列表索引
|
||
result: any[] = [], //结果数组
|
||
result_index: number = 0; //终点位置
|
||
if (start_r == end_r && start_l == end_l) {
|
||
return result;
|
||
}
|
||
openList.push({
|
||
r: start_r,
|
||
l: start_l,
|
||
G: 0
|
||
}); //把当前点加入到开启列表中,并且G是0
|
||
openObjList[start_r * cols + start_l] = start_r * cols + start_l;
|
||
do {
|
||
var currentPoint = openList[0];
|
||
if (openList.length > 1) {
|
||
openList[0] = openList[openList.length - 1];
|
||
this.minheap_filterdown(openList, 0, openObjList, cols);
|
||
}
|
||
openList.splice(openList.length - 1, 1);
|
||
closeObjList[currentPoint.r * cols + currentPoint.l] = currentPoint;
|
||
delete openObjList[currentPoint.r * cols + currentPoint.l];
|
||
let surroundPoint = this.SurroundPoint(currentPoint);
|
||
for (let i in surroundPoint) {
|
||
let item: any = surroundPoint[i]; //获得周围的八个点
|
||
if (
|
||
item.r >= 0 && //判断是否在地图上
|
||
item.l >= 0 &&
|
||
item.r < rows &&
|
||
item.l < cols &&
|
||
mapArr[item.r][item.l] != 0 && //判断是否是障碍物
|
||
!this.existInCloseList(item, closeObjList, cols) && //判断是否在关闭列表中
|
||
mapArr[item.r][currentPoint.l] != 0 && //判断之间是否有障碍物,如果有障碍物是过不去的
|
||
mapArr[currentPoint.r][item.l] != 0) {
|
||
//g 到父节点的位置
|
||
//如果是上下左右位置的则g等于10,斜对角的就是14
|
||
var g = currentPoint.G + ((currentPoint.r - item.r) * (currentPoint.l - item.l) == 0 ? 10 : 14);
|
||
let temp: number = this.existInOpenList(item, openObjList, cols);
|
||
if (temp == null) { //如果不在开启列表中
|
||
//计算H,通过水平和垂直距离进行确定
|
||
item['H'] = Math.abs(end_r - item.r) * 10 + Math.abs(end_l - item.l) * 10;
|
||
item['G'] = g;
|
||
item['F'] = item.H + item.G;
|
||
item['parent'] = currentPoint;
|
||
openList.push(item);
|
||
openObjList[item.r * cols + item.l] = openList.length - 1;
|
||
if (item['H'] == 0) {
|
||
break;
|
||
}
|
||
this.minheap_filterup(openList, openList.length - 1, openObjList, cols);
|
||
} else { //存在在开启列表中,比较目前的g值和之前的g的大小
|
||
let index: number = temp;
|
||
//如果当前点的g更小
|
||
if (g < openList[index].G) {
|
||
openList[index].parent = currentPoint;
|
||
openList[index].G = g;
|
||
openList[index].F = g + openList[index].H;
|
||
}
|
||
this.minheap_filterup(openList, index, openObjList, cols);
|
||
}
|
||
}
|
||
}
|
||
//如果开启列表空了,没有通路,结果为空
|
||
if (openList.length == 0) {
|
||
break;
|
||
}
|
||
// openList.sort(this.sortF);//这一步是为了循环回去的时候,找出 F 值最小的, 将它从 "开启列表" 中移掉
|
||
} while (!(result_index = this.existInOpenList({
|
||
r: end_r,
|
||
l: end_l
|
||
}, openObjList, cols)));
|
||
|
||
//判断结果列表是否为空
|
||
if (!result_index) {
|
||
result = [];
|
||
} else {
|
||
var currentObj = openList[result_index];
|
||
do {
|
||
//把路劲节点添加到result当中
|
||
result.unshift({
|
||
r: currentObj.r,
|
||
l: currentObj.l
|
||
});
|
||
currentObj = currentObj.parent;
|
||
} while (currentObj.r != start_r || currentObj.l != start_l);
|
||
}
|
||
let alpha = 0.8;
|
||
let beta = 0.2;
|
||
let p = result.slice(0);
|
||
for (let i = 1; i <= 8; i++) {
|
||
for (let k = 2; k < p.length - 1; k++) {
|
||
let t = p[k];
|
||
let l = p[k - 1];
|
||
let n = p[k + 1];
|
||
t.l = t.l + alpha * (result[k].l - t.l) + beta * (l.l - 2 * t.l + n.l);
|
||
t.r = t.r + alpha * (result[k].r - t.r) + beta * (l.r - 2 * t.r + n.r);
|
||
}
|
||
}
|
||
return p;
|
||
// return result;
|
||
};
|
||
|
||
/**
|
||
* 小顶堆上升算法
|
||
* list 小顶堆列表
|
||
* pos 起始计算位置,即从改点开始上升
|
||
* indexlist 地图节点索引,节点地图坐标对应list中的位置
|
||
* cols 地图列数
|
||
*/
|
||
static minheap_filterup(list: any[], pos: number, indexlist: any, cols: number) {
|
||
let c = pos; // 当前节点(current)的位置
|
||
let p = Math.floor((c - 1) / 2); // 父(parent)结点的位置
|
||
let tmp = list[c]; // 当前节点(current)
|
||
while (c > 0) { // c>0 还未上升到顶部
|
||
if (list[p].F <= tmp.F) // 父节点比当前节点小,上升结束
|
||
break;
|
||
else {
|
||
list[c] = list[p]; // 父节点放到当前位置
|
||
indexlist[list[p].r * cols + list[p].l] = c; //设置父节点的索引位置
|
||
c = p; // 当前位置上升到父节点位置
|
||
p = Math.floor((p - 1) / 2); // 重新计算父节点位置
|
||
}
|
||
}
|
||
list[c] = tmp; // 把传入节点放到上升位置
|
||
indexlist[tmp.r * cols + tmp.l] = c; // 设置传入点的索引位置
|
||
};
|
||
|
||
/**
|
||
* 小顶堆下沉算法
|
||
* list 小顶堆列表
|
||
* pos 起始计算位置,即从改点开始上升
|
||
* indexlist 地图节点索引,节点地图坐标对应list中的位置
|
||
* cols 地图列数
|
||
*/
|
||
static minheap_filterdown(list: any[], pos: number, indexlist: any, cols: number) {
|
||
let c = pos; // 当前(current)节点的位置
|
||
let l = 2 * c + 1; // 左(left)孩子的位置
|
||
let tmp = list[c]; // 当前(current)节点
|
||
let end = list.length - 1; // 数组终点
|
||
while (l <= end) {
|
||
if (l < end && list[l].F > list[l + 1].F) // "l"是左孩子,"l+1"是右孩子
|
||
l++; // 左右两孩子中选择较小者,即list[l+1]
|
||
if (tmp.F <= list[l].F) // 当前节点比最小的子节点小,调整结束
|
||
break;
|
||
else {
|
||
list[c] = list[l];
|
||
indexlist[list[l].r * cols + list[l].l] = c;
|
||
c = l;
|
||
l = 2 * l + 1;
|
||
}
|
||
}
|
||
list[c] = tmp;
|
||
indexlist[tmp.r * cols + tmp.l] = c;
|
||
};
|
||
// 是否在开放列表中
|
||
static existInOpenList(point: any, list: any, cols: number): number {
|
||
let result: number = list[point.r * cols + point.l];
|
||
return result;
|
||
};
|
||
//获取周围八个点的值
|
||
static SurroundPoint(curPoint: any): any[] {
|
||
let r = curPoint.r,
|
||
l = curPoint.l;
|
||
return [{
|
||
r: r - 1,
|
||
l: l - 1
|
||
},
|
||
{
|
||
r: r,
|
||
l: l - 1
|
||
},
|
||
{
|
||
r: r + 1,
|
||
l: l - 1
|
||
},
|
||
{
|
||
r: r + 1,
|
||
l: l
|
||
},
|
||
{
|
||
r: r + 1,
|
||
l: l + 1
|
||
},
|
||
{
|
||
r: r,
|
||
l: l + 1
|
||
},
|
||
{
|
||
r: r - 1,
|
||
l: l + 1
|
||
},
|
||
{
|
||
r: r - 1,
|
||
l: l
|
||
}
|
||
];
|
||
};
|
||
// 是否在关闭列表中
|
||
static existInCloseList(point: any, list: any, cols: number): boolean {
|
||
if (list[point.r * cols + point.l]) {
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
} |