iotAR小程序
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.
 

478 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.y - b.y) * (a.y - b.y));
export const minScale = 0.1;
export const maxScale = 10;
export default class MAPAPP {
events = new Events();
mall = mall;
groups = [];
x = null;
y = null;
intervals = [];
shown = false;
theta2s = [];
scale = 0.5;
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();
if (map2dData) 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) {
if (!this.theta2s) this.theta2s = [rag];
else this.theta2s.push(rag);
if (this.theta2s.length > 20) this.theta2s.shift();
const { x, y } = this.theta2s.reduce(
({ x, y }, nxt) => ({
x: x + Math.cos(nxt),
y: y + Math.sin(nxt),
}),
{
x: 0,
y: 0,
}
);
this.theta2 = Math.atan2(y, x);
}
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, theta2 } = this;
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.rotate(-theta2);
ctx.drawImage(arrow, -16, -16, 32, 32);
ctx.restore();
}
drawPath() {
const { linePath, ctx } = this;
const start = linePath[0];
const end = linePath[linePath.length - 1];
ctx.fillStyle = "#518cf7";
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 = "#518cf7";
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 = "#437af7";
// 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 = "#437af7";
// 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 minDistance = 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 < minDistance) {
minDistance = d;
crossPoint = cross;
index = k;
if (d < 0.5) break;
}
}
let distance2End = 0;
for (let i = index; i < positions.length; i++) {
let point1 = i === index ? crossPoint : positions[i - 1];
let point2 = positions[i];
distance2End += getDistance(point1, point2);
}
return [crossPoint, minDistance, distance2End];
}
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],
y: 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 && getApp().globalData.floors[path[index].split("_")[0]];
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 },
searchType
) {
const { graph, graphDt, graphFt } = this;
searchType = [0, 1, 2].includes(searchType) ? searchType : this.searchType;
try {
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 this.shortestPath(
{ floorOrder: floorOrder1, NavPoint: NavPoint1 },
{ floorOrder: floorOrder2, NavPoint: NavPoint2 },
0
);
}
}
getNearestPoint({ x, y, floorOrder }) {
const { points } = this;
const [nearest] = points[floorOrder].reduce(
([last, min], nxt) => {
if (last === null) {
return [
nxt,
getDistance(
{
x,
y,
},
{
x: nxt.position[0],
y: nxt.position[2],
}
),
];
}
const dis = getDistance(
{
x,
y,
},
{
x: nxt.position[0],
y: 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;
}
}