navmesh
Version:
A library for fast pathfinding using navigation meshes in JS
115 lines (98 loc) • 3.2 kB
text/typescript
// Mostly sourced from PatrolJS at the moment. TODO: come back and reimplement this as an incomplete
// funnel algorithm so astar checks can be more accurate.
import Vector2 from "./math/vector-2";
import { triarea2 } from "./utils";
export interface Portal {
left: Vector2;
right: Vector2;
}
/**
* @private
*/
export default class Channel {
public path: Vector2[];
private portals: Portal[];
constructor() {
this.portals = [];
this.path = [];
}
push(p1: Vector2, p2?: Vector2) {
if (p2 === undefined) p2 = p1;
this.portals.push({
left: p1,
right: p2,
});
}
stringPull() {
const portals = this.portals;
const pts: Vector2[] = [];
// Init scan state
let apexIndex = 0;
let leftIndex = 0;
let rightIndex = 0;
let portalApex = portals[0].left;
let portalLeft = portals[0].left;
let portalRight = portals[0].right;
// Add start point.
pts.push(portalApex);
for (var i = 1; i < portals.length; i++) {
// Find the next portal vertices
const left = portals[i].left;
const right = portals[i].right;
// Update right vertex.
if (triarea2(portalApex, portalRight, right) <= 0.0) {
if (portalApex.equals(portalRight) || triarea2(portalApex, portalLeft, right) > 0.0) {
// Tighten the funnel.
portalRight = right;
rightIndex = i;
} else {
// Right vertex just crossed over the left vertex, so the left vertex should
// now be part of the path.
pts.push(portalLeft);
// Restart scan from portal left point.
// Make current left the new apex.
portalApex = portalLeft;
apexIndex = leftIndex;
// Reset portal
portalLeft = portalApex;
portalRight = portalApex;
leftIndex = apexIndex;
rightIndex = apexIndex;
// Restart scan
i = apexIndex;
continue;
}
}
// Update left vertex.
if (triarea2(portalApex, portalLeft, left) >= 0.0) {
if (portalApex.equals(portalLeft) || triarea2(portalApex, portalRight, left) < 0.0) {
// Tighten the funnel.
portalLeft = left;
leftIndex = i;
} else {
// Left vertex just crossed over the right vertex, so the right vertex should
// now be part of the path
pts.push(portalRight);
// Restart scan from portal right point.
// Make current right the new apex.
portalApex = portalRight;
apexIndex = rightIndex;
// Reset portal
portalLeft = portalApex;
portalRight = portalApex;
leftIndex = apexIndex;
rightIndex = apexIndex;
// Restart scan
i = apexIndex;
continue;
}
}
}
if (pts.length === 0 || !pts[pts.length - 1].equals(portals[portals.length - 1].left)) {
// Append last point to path.
pts.push(portals[portals.length - 1].left);
}
this.path = pts;
return pts;
}
}