Files
react-diagrams/lib-routing/src/engine/PathFinding.ts
Dylan Vorster fddec93370 more generics
2019-07-26 13:37:30 +02:00

128 lines
3.3 KiB
TypeScript

import * as PF from 'pathfinding';
import { PathFindingLinkFactory } from '../link/PathFindingLinkFactory';
import { PointModel } from "@projectstorm/react-diagrams-core";
/*
it can be very expensive to calculate routes when every single pixel on the canvas
is individually represented. Using the factor below, we combine values in order
to achieve the best trade-off between accuracy and performance.
*/
const pathFinderInstance = new PF.JumpPointFinder({
heuristic: PF.Heuristic.manhattan,
diagonalMovement: PF.DiagonalMovement.Never
});
export default class PathFinding {
instance: any;
factory: PathFindingLinkFactory;
constructor(factory: PathFindingLinkFactory) {
this.instance = pathFinderInstance;
this.factory = factory;
}
/**
* Taking as argument a fully unblocked walking matrix, this method
* finds a direct path from point A to B.
*/
calculateDirectPath(
from: PointModel,
to: PointModel
): number[][] {
const matrix = this.factory.getCanvasMatrix();
const grid = new PF.Grid(matrix);
return pathFinderInstance.findPath(
this.factory.translateRoutingX(Math.floor(from.getX() / this.factory.ROUTING_SCALING_FACTOR)),
this.factory.translateRoutingY(Math.floor(from.getY() / this.factory.ROUTING_SCALING_FACTOR)),
this.factory.translateRoutingX(Math.floor(to.getX() / this.factory.ROUTING_SCALING_FACTOR)),
this.factory.translateRoutingY(Math.floor(to.getY() / this.factory.ROUTING_SCALING_FACTOR)),
grid
);
}
/**
* Using @link{#calculateDirectPath}'s result as input, we here
* determine the first walkable point found in the matrix that includes
* blocked paths.
*/
calculateLinkStartEndCoords(
matrix: number[][],
path: number[][]
): {
start: {
x: number;
y: number;
};
end: {
x: number;
y: number;
};
pathToStart: number[][];
pathToEnd: number[][];
} {
const startIndex = path.findIndex(point => matrix[point[1]][point[0]] === 0);
const endIndex =
path.length -
1 -
path
.slice()
.reverse()
.findIndex(point => matrix[point[1]][point[0]] === 0);
// are we trying to create a path exclusively through blocked areas?
// if so, let's fallback to the linear routing
if (startIndex === -1 || endIndex === -1) {
return undefined;
}
const pathToStart = path.slice(0, startIndex);
const pathToEnd = path.slice(endIndex);
return {
start: {
x: path[startIndex][0],
y: path[startIndex][1]
},
end: {
x: path[endIndex][0],
y: path[endIndex][1]
},
pathToStart,
pathToEnd
};
}
/**
* Puts everything together: merges the paths from/to the centre of the ports,
* with the path calculated around other elements.
*/
calculateDynamicPath(
routingMatrix: number[][],
start: {
x: number;
y: number;
},
end: {
x: number;
y: number;
},
pathToStart: number[][],
pathToEnd: number[][]
) {
// generate the path based on the matrix with obstacles
const grid = new PF.Grid(routingMatrix);
const dynamicPath = pathFinderInstance.findPath(start.x, start.y, end.x, end.y, grid);
// aggregate everything to have the calculated path ready for rendering
const pathCoords = pathToStart
.concat(dynamicPath, pathToEnd)
.map(coords => [
this.factory.translateRoutingX(coords[0], true),
this.factory.translateRoutingY(coords[1], true)
]);
return PF.Util.compressPath(pathCoords);
}
}