You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
450 lines
12 KiB
450 lines
12 KiB
import { getCrossPoint } from "./util";
|
|
import Events from "./events";
|
|
|
|
import dijkstra from "dijkstrajs";
|
|
import { getMapData, mall } from "../../getMapData";
|
|
|
|
const getDistance = (a, b) =>
|
|
Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.z - b.z) * (a.z - b.z));
|
|
export const minScale = 0.3;
|
|
export const maxScale = 1;
|
|
|
|
export default class MAPAPP {
|
|
events = new Events();
|
|
mall = mall;
|
|
groups = [];
|
|
x = null;
|
|
y = null;
|
|
intervals = [];
|
|
shown = false;
|
|
theta2s = [];
|
|
scale = minScale;
|
|
constructor(query, component) {
|
|
this.searchType = component.data.searchType;
|
|
const canvas = query.node;
|
|
const ctx = canvas.getContext("2d");
|
|
const dpr = wx.getSystemInfoSync().pixelRatio;
|
|
canvas.width = query.width * dpr;
|
|
canvas.height = query.height * dpr;
|
|
const start = canvas.createImage();
|
|
start.src = "/pages/map2d/start.png";
|
|
const end = canvas.createImage();
|
|
end.src = "/pages/map2d/end.png";
|
|
const arrow = canvas.createImage();
|
|
arrow.src = "/pages/map2d/arrow.png";
|
|
ctx.scale(dpr, dpr);
|
|
Object.assign(this, {
|
|
canvas,
|
|
ctx,
|
|
start,
|
|
end,
|
|
arrow,
|
|
width: query.width,
|
|
height: query.height,
|
|
});
|
|
this.init();
|
|
}
|
|
|
|
on(event, cb) {
|
|
return this.events.on(event, cb);
|
|
}
|
|
once(event, cb) {
|
|
return this.events.once(event, cb);
|
|
}
|
|
off(event, cb) {
|
|
return this.events.off(event, cb);
|
|
}
|
|
dispose() {
|
|
console.log("map interval disposed");
|
|
this.intervals && this.intervals.forEach(clearInterval);
|
|
this.intervals = [];
|
|
}
|
|
|
|
async init() {
|
|
const data = await getMapData();
|
|
Object.assign(this, data);
|
|
const { floors } = this.mall;
|
|
const { canvas } = this;
|
|
const groups = floors.map(([_, name, map2dData]) => {
|
|
const floor = canvas.createImage();
|
|
floor.src = map2dData.data;
|
|
return floor;
|
|
});
|
|
this.groups = groups;
|
|
this.events.dispatch("loaded", this);
|
|
this.intervals.push(
|
|
setInterval(() => {
|
|
this.animate();
|
|
}, 1000 / 30)
|
|
);
|
|
}
|
|
get shops() {
|
|
return !this.serverShopInfo
|
|
? []
|
|
: this.serverShopInfo[this.floorOrder].shopList;
|
|
}
|
|
get currentFloorObject() {
|
|
return this.groups[this.floorOrder];
|
|
}
|
|
updateTheta2(rag) {
|
|
this.theta2 = rag;
|
|
}
|
|
setXY(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
scaleX(number) {
|
|
const { x, width, scale } = this;
|
|
return (number - x) * scale + width / 2;
|
|
}
|
|
scaleY(number) {
|
|
const { y, height, scale } = this;
|
|
return (number - y) * scale + height / 2;
|
|
}
|
|
scaleLen(number) {
|
|
const { scale } = this;
|
|
return number * scale;
|
|
}
|
|
animate() {
|
|
const { shown, ctx, x, y, width, height } = this;
|
|
if (!shown) return;
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.save();
|
|
// ctx.translate(this.scaleX(-x) + width / 2, this.scaleY(-y) + height / 2);
|
|
this.drawFloor();
|
|
this.drawLines();
|
|
ctx.restore();
|
|
this.drawArrow();
|
|
}
|
|
drawArrow() {
|
|
const { ctx, arrow, width, height } = this;
|
|
ctx.save();
|
|
ctx.translate(width / 2, height / 2);
|
|
ctx.drawImage(arrow, -36, -(82 - 36), 72, 82);
|
|
ctx.restore();
|
|
}
|
|
drawPath() {
|
|
const { linePath, ctx } = this;
|
|
const start = linePath[0];
|
|
const end = linePath[linePath.length - 1];
|
|
ctx.fillStyle = "#fec210";
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(start[0]),
|
|
this.scaleY(start[1]),
|
|
// this.scaleLen(10),
|
|
10,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(end[0]),
|
|
this.scaleY(end[1]),
|
|
// this.scaleLen(10),
|
|
10,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
linePath.forEach(([x, y], i) => {
|
|
if (i === 0) {
|
|
ctx.moveTo(this.scaleX(x), this.scaleY(y));
|
|
} else {
|
|
ctx.lineTo(this.scaleX(x), this.scaleY(y));
|
|
}
|
|
});
|
|
}
|
|
drawLines() {
|
|
const { linePath, ctx } = this;
|
|
if (!linePath || !linePath.length) return;
|
|
this.drawPath();
|
|
ctx.lineCap = "round";
|
|
ctx.lineJoin = "round";
|
|
// ctx.lineWidth = this.scaleLen(11);
|
|
ctx.lineWidth = 11;
|
|
ctx.strokeStyle = "#fec210";
|
|
ctx.stroke();
|
|
for (let i = 9; i >= 1; i -= 1) {
|
|
ctx.lineCap = "butt";
|
|
ctx.lineJoin = "butt";
|
|
// ctx.lineWidth = this.scaleLen(i);
|
|
ctx.lineWidth = i;
|
|
ctx.strokeStyle = "#fff";
|
|
// ctx.setLineDash([this.scaleLen(4), this.scaleLen(13)]);
|
|
ctx.setLineDash([4, 13]);
|
|
// ctx.lineDashOffset = this.scaleLen(i - 9);
|
|
ctx.lineDashOffset = i - 9;
|
|
ctx.stroke();
|
|
ctx.strokeStyle = "#ed912b";
|
|
// ctx.setLineDash([0, this.scaleLen(4), this.scaleLen(13), 0]);
|
|
ctx.setLineDash([0, 4, 13, 0]);
|
|
// ctx.lineDashOffset = this.scaleLen(i - 9);
|
|
ctx.lineDashOffset = i - 9;
|
|
ctx.stroke();
|
|
}
|
|
const start = linePath[0];
|
|
const end = linePath[linePath.length - 1];
|
|
ctx.fillStyle = "#ed912b";
|
|
// ctx.lineWidth = this.scaleLen(1);
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(start[0]),
|
|
this.scaleY(start[1]),
|
|
// this.scaleLen(9),
|
|
9,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(end[0]),
|
|
this.scaleY(end[1]),
|
|
// this.scaleLen(9),
|
|
9,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
ctx.fillStyle = "#fff";
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(start[0]),
|
|
this.scaleY(start[1]),
|
|
// this.scaleLen(4.5),
|
|
4.5,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(
|
|
this.scaleX(end[0]),
|
|
this.scaleY(end[1]),
|
|
// this.scaleLen(4.5),
|
|
4.5,
|
|
0,
|
|
2 * Math.PI
|
|
);
|
|
ctx.fill();
|
|
}
|
|
|
|
drawFloor() {
|
|
const { floorOrder, groups, ctx } = this;
|
|
const floor = groups[floorOrder];
|
|
ctx.save();
|
|
ctx.drawImage(
|
|
floor,
|
|
this.scaleX(-floor.width / 2),
|
|
this.scaleY(-floor.height / 2),
|
|
this.scaleLen(floor.width),
|
|
this.scaleLen(floor.height)
|
|
);
|
|
ctx.restore();
|
|
}
|
|
|
|
async changeFloor(num) {
|
|
this.floorOrder = num;
|
|
this.x = null;
|
|
this.y = null;
|
|
this.events.dispatch("floorchange", Number(num));
|
|
return;
|
|
}
|
|
|
|
cross({ x, y }) {
|
|
const point = { x, y };
|
|
const { positions } = this;
|
|
if (!(positions && positions.length)) return point;
|
|
let minDisance = Infinity;
|
|
let crossPoint = null;
|
|
let index = 1;
|
|
for (let k = 1; k < positions.length; k++) {
|
|
let point1 = positions[k - 1];
|
|
let point2 = positions[k];
|
|
let cross = getCrossPoint(point, point1, point2);
|
|
let d = Math.sqrt(
|
|
(point.x - cross.x) * (point.x - cross.x) +
|
|
(point.y - cross.y) * (point.y - cross.y)
|
|
);
|
|
|
|
if (d < minDisance) {
|
|
minDisance = d;
|
|
crossPoint = cross;
|
|
index = k;
|
|
if (d < 0.5) break;
|
|
}
|
|
}
|
|
return crossPoint;
|
|
}
|
|
requestRoute(start, end, draw = true) {
|
|
if (!(start && end)) return;
|
|
const startFloorId = start[2];
|
|
const startFloorOrder = getApp().globalData.floorIdFloorOrderMap[
|
|
startFloorId
|
|
];
|
|
const { name: sname } = this.getNearestPoint({
|
|
x: start[0],
|
|
z: start[1],
|
|
floorOrder: startFloorOrder,
|
|
});
|
|
const startFloor = startFloorOrder;
|
|
const startPoint = Number(sname);
|
|
|
|
const endFloor = end.floorOrder;
|
|
const endPoint = end.navPoint;
|
|
|
|
let path = this.shortestPath(
|
|
{
|
|
floorOrder: startFloor,
|
|
NavPoint: startPoint,
|
|
},
|
|
{
|
|
floorOrder: endFloor,
|
|
NavPoint: endPoint,
|
|
}
|
|
);
|
|
if (!path) {
|
|
console.log("寻路失败", "start", start, "end", end);
|
|
return { naviData: null, nextFloorId: null };
|
|
}
|
|
let floorIdPoints = path.map((str) => str.split("_"));
|
|
const byFirstDiffFloor = ([floorId], i) =>
|
|
i === 0 ? false : floorIdPoints[i - 1][0] !== floorId;
|
|
const index = floorIdPoints.findIndex(byFirstDiffFloor);
|
|
console.log("index", index);
|
|
const isFloorChange = index !== -1;
|
|
const currentFloorOrder = Number(floorIdPoints[0][0]);
|
|
const nextFloorOrder = !isFloorChange
|
|
? null
|
|
: Number(floorIdPoints[index][0]);
|
|
const fac = !isFloorChange ? null : this.facilityLiftMap[path[index - 1]];
|
|
let naviData = floorIdPoints.slice(0, index === -1 ? undefined : index);
|
|
naviData = naviData.reduce((acc, [floorOrder, point]) => {
|
|
const last = acc[acc.length - 1];
|
|
const x = this.points[floorOrder][point].position[0];
|
|
const y = this.points[floorOrder][point].position[2];
|
|
const floorId = startFloorId;
|
|
if (last && Math.abs(last.y - y) <= 3 && Math.abs(last.x - x) <= 3)
|
|
return acc;
|
|
else return [...acc, { x, y, floorId }];
|
|
}, []);
|
|
|
|
if (naviData.length === 1) {
|
|
naviData.unshift({
|
|
x: start[0],
|
|
y: start[1],
|
|
flooorId: naviData[0].flooorId,
|
|
});
|
|
}
|
|
naviData.forEach((data, i) => {
|
|
data.pointType =
|
|
i === 0
|
|
? 0
|
|
: i !== naviData.length - 1
|
|
? 1
|
|
: !isFloorChange
|
|
? 6
|
|
: nextFloorOrder > currentFloorOrder
|
|
? fac.Type == 5
|
|
? 2
|
|
: 4
|
|
: fac.Type == 5
|
|
? 3
|
|
: 5;
|
|
});
|
|
this.positions = naviData;
|
|
console.log("originalNaviData", naviData);
|
|
if (draw && naviData.length) {
|
|
this.linePath = naviData.map(({ x, y }) => [x, y]);
|
|
}
|
|
const nextFloorId = !isFloorChange
|
|
? null
|
|
: getApp().globalData.floors[path[index].split("_")[0]].floorId;
|
|
console.log("nextFloorId", nextFloorId);
|
|
return {
|
|
naviData,
|
|
nextFloorId,
|
|
};
|
|
}
|
|
shortestPath(
|
|
{ floorOrder: floorOrder1, NavPoint: NavPoint1 },
|
|
{ floorOrder: floorOrder2, NavPoint: NavPoint2 }
|
|
) {
|
|
try {
|
|
const { graph, graphDt, graphFt, searchType = 0 } = this;
|
|
let currentGraph =
|
|
searchType === 0 ? graph : searchType === 1 ? graphFt : graphDt;
|
|
const s = floorOrder1 + "_" + NavPoint1;
|
|
const d = floorOrder2 + "_" + NavPoint2;
|
|
if (currentGraph && currentGraph[s] && currentGraph[d]) {
|
|
let path = dijkstra.find_path(currentGraph, s, d);
|
|
return path;
|
|
} else return null;
|
|
} catch (e) {
|
|
console.log(e);
|
|
return null;
|
|
}
|
|
}
|
|
getNearestPoint({ x, z, floorOrder }) {
|
|
const { points } = this;
|
|
const [nearest] = points[floorOrder].reduce(
|
|
([last, min], nxt) => {
|
|
if (last === null) {
|
|
return [
|
|
nxt,
|
|
getDistance(
|
|
{
|
|
x,
|
|
z,
|
|
},
|
|
{
|
|
x: nxt.position[0],
|
|
z: nxt.position[2],
|
|
}
|
|
),
|
|
];
|
|
}
|
|
const dis = getDistance(
|
|
{
|
|
x,
|
|
z,
|
|
},
|
|
{
|
|
x: nxt.position[0],
|
|
z: nxt.position[2],
|
|
}
|
|
);
|
|
if (dis < min) return [nxt, dis];
|
|
else return [last, min];
|
|
},
|
|
[null, Infinity]
|
|
);
|
|
return nearest;
|
|
}
|
|
getFloorHeightByFloorOrder(floorOrder) {
|
|
const { config, groups } = this;
|
|
const floor = groups[floorOrder];
|
|
const { floorHeights } = config;
|
|
return floorOrder in floorHeights
|
|
? floorHeights[floorOrder]
|
|
: floor.floorHeight;
|
|
}
|
|
getBoxHeightByFloorOrder(floorOrder) {
|
|
const { groups } = this;
|
|
const floor = groups[floorOrder];
|
|
return floor.boxHeight;
|
|
}
|
|
getNavIconY(floorOrder) {
|
|
const { groups } = this;
|
|
const floor = groups[floorOrder];
|
|
if (!floor) return this.getNavIconY(this.floorOrder);
|
|
const boxHeight = this.getBoxHeightByFloorOrder(floorOrder);
|
|
const radius = PathLine.getPathLineRadius(this, floor);
|
|
return boxHeight + radius * 5;
|
|
}
|
|
}
|
|
|