@flare-engine/pathfinding 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/index.d.mts +1 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
# @flare-engine/pathfinding
|
|
2
2
|
|
|
3
|
-
Grid pathfinding — A* (`findPath`), Jump-Point Search (`findPathJPS`) for uniform-cost grids, and a polygon navmesh fallback (`findNavMeshPath`). Pure data, no classes. Heuristics (`manhattanDistance`, `octileDistance`, `chebyshevDistance`, `euclideanDistance`) are exported as named functions for tree-shake.
|
|
3
|
+
Grid pathfinding — A* (`findPath`), Jump-Point Search (`findPathJPS`) for uniform-cost grids (a weighted grid delegates to A*, see below), and a polygon navmesh fallback (`findNavMeshPath`). Pure data, no classes. Heuristics (`manhattanDistance`, `octileDistance`, `chebyshevDistance`, `euclideanDistance`) are exported as named functions for tree-shake.
|
|
4
|
+
|
|
5
|
+
> **Status: beta — no production consumer yet.** Every other `@flare-engine/*` package is exercised
|
|
6
|
+
> by a shipping game; this one is not. The API is covered by tests and the algorithms are correct
|
|
7
|
+
> for the cases below, but it has not been through the shakedown that real use provides. Treat
|
|
8
|
+
> signatures as stable and behaviour as less battle-tested than the rest of the scope.
|
|
9
|
+
|
|
10
|
+
## `findPath` and `findPathJPS` disagree about corner cutting
|
|
11
|
+
|
|
12
|
+
They are **not** drop-in replacements for each other. Given a diagonal step whose two shared
|
|
13
|
+
cardinal neighbours are both blocked:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
. X . start (0,0), goal (1,1)
|
|
17
|
+
X . . findPath → null (refuses to squeeze the corner)
|
|
18
|
+
. . . findPathJPS → [{x:0,y:0},{x:1,y:1}] (takes the diagonal)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`findPath` requires both cardinal neighbours of a diagonal to be walkable; `findPathJPS` does not.
|
|
22
|
+
Swapping one for the other to gain speed therefore changes movement semantics, and on a map with
|
|
23
|
+
tight diagonal gaps it changes which routes exist at all. Pinned by tests in
|
|
24
|
+
`tests/jps.test.ts`. If your game's collision shape is a circle or box wider than a point, you
|
|
25
|
+
almost certainly want `findPath`'s rule.
|
|
26
|
+
|
|
27
|
+
## `findPathJPS` on weighted grids
|
|
28
|
+
|
|
29
|
+
JPS pruning is only correct when every walkable tile costs 1. If any tile's cost is above 1,
|
|
30
|
+
`findPathJPS` detects it with one linear scan of `grid.data` and delegates the call to `findPath`
|
|
31
|
+
with `diagonal: true`, forwarding `maxIterations` and `heuristic`. The result is a cost-correct
|
|
32
|
+
weighted A* path — with A*'s corner-cutting rule above, not JPS's — never a JPS path that ignored
|
|
33
|
+
the costs. A grid whose tiles all share a cost above 1 (`createGrid({ defaultCost: 2 })`) delegates
|
|
34
|
+
on every call too: same path as JPS, `cost` doubled to the true weighted total, and no JPS speed-up.
|
|
35
|
+
Keep every cost at 1 to get the JPS speed-up.
|
|
4
36
|
|
|
5
37
|
## Install
|
|
6
38
|
|
package/dist/index.d.mts
CHANGED
|
@@ -64,9 +64,7 @@ interface NavMeshPolygon {
|
|
|
64
64
|
readonly id: number;
|
|
65
65
|
/** Convex polygon vertices in winding order. */
|
|
66
66
|
readonly vertices: readonly Vec2[];
|
|
67
|
-
/**
|
|
68
|
-
readonly neighbors: readonly number[];
|
|
69
|
-
/** Shared edge per neighbor: [neighborId] → { a, b } portal endpoints. */
|
|
67
|
+
/** One `Portal` per adjacent polygon: the shared edge plus that neighbour's id. */
|
|
70
68
|
readonly portals: readonly Portal[];
|
|
71
69
|
}
|
|
72
70
|
interface Portal {
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"mappings":";;;;;;UAKiB;WACN;WACA;WACA,MAAM;;UAGA;EACf;EACA;;EAEA,OAAO;;EAEP;;iBAGc,WAAW,QAAQ,aAAa;iBAsBhC,SAAS,MAAM,MAAM,WAAW;iBAIhC,WAAW,MAAM,MAAM,WAAW;iBAKlC,QAAQ,MAAM,MAAM,WAAW;iBAK/B,YAAY,MAAM,MAAM,WAAW,WAAW;iBAK9C,QAAQ,MAAM,MAAM,WAAW,WAAW;;;UCrDzC;;EAEf;IAAQ;IAAW;;;EAEnB;;EAEA;;UAGe;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;EAEjD;;iBAOc,kBAAkB,YAAY,YAAY,YAAY;iBAItD,eAAe,YAAY,YAAY,YAAY;iBAMnD,kBAAkB,YAAY,YAAY,YAAY;iBAItD,kBAAkB,YAAY,YAAY,YAAY;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"mappings":";;;;;;UAKiB;WACN;WACA;WACA,MAAM;;UAGA;EACf;EACA;;EAEA,OAAO;;EAEP;;iBAGc,WAAW,QAAQ,aAAa;iBAsBhC,SAAS,MAAM,MAAM,WAAW;iBAIhC,WAAW,MAAM,MAAM,WAAW;iBAKlC,QAAQ,MAAM,MAAM,WAAW;iBAK/B,YAAY,MAAM,MAAM,WAAW,WAAW;iBAK9C,QAAQ,MAAM,MAAM,WAAW,WAAW;;;UCrDzC;;EAEf;IAAQ;IAAW;;;EAEnB;;EAEA;;UAGe;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;EAEjD;;iBAOc,kBAAkB,YAAY,YAAY,YAAY;iBAItD,eAAe,YAAY,YAAY,YAAY;iBAMnD,kBAAkB,YAAY,YAAY,YAAY;iBAItD,kBAAkB,YAAY,YAAY,YAAY;iBAwGtD,SACd,MAAM,MACN,gBACA,gBACA,cACA,cACA,UAAU,cACT;;;UC/Cc;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;iBAGnC,YACd,MAAM,MACN,gBACA,gBACA,cACA,cACA,UAAU,aACT;;;UChHc;WACN;;WAEA,mBAAmB;;WAEnB,kBAAkB;;UAGZ;;EAEf;;EAEA,GAAG;;EAEH,GAAG;;UAGY;WACN,mBAAmB;;UAGb;;EAEf,MAAM;;EAEN;;iBAKc,cAAc,UAAU,mBAAmB;iBAuB3C,cAAc,MAAM,SAAS,WAAW;iBAkCxC,gBAAgB,MAAM,SAAS,OAAO,MAAM,KAAK,OAAO"}
|
package/dist/index.d.ts
CHANGED
|
@@ -64,9 +64,7 @@ interface NavMeshPolygon {
|
|
|
64
64
|
readonly id: number;
|
|
65
65
|
/** Convex polygon vertices in winding order. */
|
|
66
66
|
readonly vertices: readonly Vec2[];
|
|
67
|
-
/**
|
|
68
|
-
readonly neighbors: readonly number[];
|
|
69
|
-
/** Shared edge per neighbor: [neighborId] → { a, b } portal endpoints. */
|
|
67
|
+
/** One `Portal` per adjacent polygon: the shared edge plus that neighbour's id. */
|
|
70
68
|
readonly portals: readonly Portal[];
|
|
71
69
|
}
|
|
72
70
|
interface Portal {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"mappings":";;;;;;UAKiB;WACN;WACA;WACA,MAAM;;UAGA;EACf;EACA;;EAEA,OAAO;;EAEP;;iBAGc,WAAW,QAAQ,aAAa;iBAsBhC,SAAS,MAAM,MAAM,WAAW;iBAIhC,WAAW,MAAM,MAAM,WAAW;iBAKlC,QAAQ,MAAM,MAAM,WAAW;iBAK/B,YAAY,MAAM,MAAM,WAAW,WAAW;iBAK9C,QAAQ,MAAM,MAAM,WAAW,WAAW;;;UCrDzC;;EAEf;IAAQ;IAAW;;;EAEnB;;EAEA;;UAGe;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;EAEjD;;iBAOc,kBAAkB,YAAY,YAAY,YAAY;iBAItD,eAAe,YAAY,YAAY,YAAY;iBAMnD,kBAAkB,YAAY,YAAY,YAAY;iBAItD,kBAAkB,YAAY,YAAY,YAAY;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"mappings":";;;;;;UAKiB;WACN;WACA;WACA,MAAM;;UAGA;EACf;EACA;;EAEA,OAAO;;EAEP;;iBAGc,WAAW,QAAQ,aAAa;iBAsBhC,SAAS,MAAM,MAAM,WAAW;iBAIhC,WAAW,MAAM,MAAM,WAAW;iBAKlC,QAAQ,MAAM,MAAM,WAAW;iBAK/B,YAAY,MAAM,MAAM,WAAW,WAAW;iBAK9C,QAAQ,MAAM,MAAM,WAAW,WAAW;;;UCrDzC;;EAEf;IAAQ;IAAW;;;EAEnB;;EAEA;;UAGe;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;EAEjD;;iBAOc,kBAAkB,YAAY,YAAY,YAAY;iBAItD,eAAe,YAAY,YAAY,YAAY;iBAMnD,kBAAkB,YAAY,YAAY,YAAY;iBAItD,kBAAkB,YAAY,YAAY,YAAY;iBAwGtD,SACd,MAAM,MACN,gBACA,gBACA,cACA,cACA,UAAU,cACT;;;UC/Cc;;EAEf;;EAEA,aAAa,YAAY,YAAY,YAAY;;iBAGnC,YACd,MAAM,MACN,gBACA,gBACA,cACA,cACA,UAAU,aACT;;;UChHc;WACN;;WAEA,mBAAmB;;WAEnB,kBAAkB;;UAGZ;;EAEf;;EAEA,GAAG;;EAEH,GAAG;;UAGY;WACN,mBAAmB;;UAGb;;EAEf,MAAM;;EAEN;;iBAKc,cAAc,UAAU,mBAAmB;iBAuB3C,cAAc,MAAM,SAAS,WAAW;iBAkCxC,gBAAgB,MAAM,SAAS,OAAO,MAAM,KAAK,OAAO"}
|
package/dist/index.js
CHANGED
|
@@ -64,7 +64,27 @@ function heapCreate$1(capacity) {
|
|
|
64
64
|
size: 0
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Double the heap's backing arrays.
|
|
69
|
+
*
|
|
70
|
+
* Typed arrays do not throw on an out-of-range write — the store is silently
|
|
71
|
+
* dropped — so without this a push past capacity increments `size`, writes
|
|
72
|
+
* nothing, and every later comparison reads `undefined`. The open set then
|
|
73
|
+
* degrades without any error, and the search returns a valid-looking but
|
|
74
|
+
* non-optimal path. Growth is amortized O(1): allocation happens only when
|
|
75
|
+
* capacity is reached, never per push.
|
|
76
|
+
*/
|
|
77
|
+
function heapGrow$1(h) {
|
|
78
|
+
const next = h.f.length === 0 ? 16 : h.f.length * 2;
|
|
79
|
+
const f = new Float64Array(next);
|
|
80
|
+
f.set(h.f);
|
|
81
|
+
const idx = new Int32Array(next);
|
|
82
|
+
idx.set(h.idx);
|
|
83
|
+
h.f = f;
|
|
84
|
+
h.idx = idx;
|
|
85
|
+
}
|
|
67
86
|
function heapPush$1(h, f, nodeIdx) {
|
|
87
|
+
if (h.size === h.f.length) heapGrow$1(h);
|
|
68
88
|
let i = h.size++;
|
|
69
89
|
h.f[i] = f;
|
|
70
90
|
h.idx[i] = nodeIdx;
|
|
@@ -239,7 +259,27 @@ function heapCreate(capacity) {
|
|
|
239
259
|
size: 0
|
|
240
260
|
};
|
|
241
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Double the heap's backing arrays.
|
|
264
|
+
*
|
|
265
|
+
* Typed arrays do not throw on an out-of-range write — the store is silently
|
|
266
|
+
* dropped — so without this a push past capacity increments `size`, writes
|
|
267
|
+
* nothing, and every later comparison reads `undefined`. The open set then
|
|
268
|
+
* degrades without any error, and the search returns a valid-looking but
|
|
269
|
+
* non-optimal path. Growth is amortized O(1): allocation happens only when
|
|
270
|
+
* capacity is reached, never per push.
|
|
271
|
+
*/
|
|
272
|
+
function heapGrow(h) {
|
|
273
|
+
const next = h.f.length === 0 ? 16 : h.f.length * 2;
|
|
274
|
+
const f = new Float64Array(next);
|
|
275
|
+
f.set(h.f);
|
|
276
|
+
const idx = new Int32Array(next);
|
|
277
|
+
idx.set(h.idx);
|
|
278
|
+
h.f = f;
|
|
279
|
+
h.idx = idx;
|
|
280
|
+
}
|
|
242
281
|
function heapPush(h, f, nodeIdx) {
|
|
282
|
+
if (h.size === h.f.length) heapGrow(h);
|
|
243
283
|
let i = h.size++;
|
|
244
284
|
h.f[i] = f;
|
|
245
285
|
h.idx[i] = nodeIdx;
|
|
@@ -281,6 +321,11 @@ function heapPop(h) {
|
|
|
281
321
|
return result;
|
|
282
322
|
}
|
|
283
323
|
const SQRT2 = Math.SQRT2;
|
|
324
|
+
function hasWeightedCost(grid) {
|
|
325
|
+
const data = grid.data;
|
|
326
|
+
for (let i = 0; i < data.length; i++) if (data[i] > 1) return true;
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
284
329
|
function findPathJPS(grid, startX, startY, endX, endY, options) {
|
|
285
330
|
const sx = startX | 0;
|
|
286
331
|
const sy = startY | 0;
|
|
@@ -295,6 +340,10 @@ function findPathJPS(grid, startX, startY, endX, endY, options) {
|
|
|
295
340
|
cost: 0,
|
|
296
341
|
explored: 0
|
|
297
342
|
};
|
|
343
|
+
if (hasWeightedCost(grid)) return findPath(grid, sx, sy, ex, ey, {
|
|
344
|
+
...options,
|
|
345
|
+
diagonal: true
|
|
346
|
+
});
|
|
298
347
|
const w = grid.width;
|
|
299
348
|
const h = grid.height;
|
|
300
349
|
const total = w * h;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["SQRT2","heapCreate","heapPush","heapPop"],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"sourcesContent":["/**\n * Grid data structure for tile-based pathfinding.\n * Uses a flat Uint8Array: 0 = blocked, 1+ = walkable (value = movement cost).\n */\n\nexport interface Grid {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\nexport interface GridConfig {\n width: number;\n height: number;\n /** Pre-filled grid data. Length must equal width * height. */\n data?: Uint8Array;\n /** Default cost for all cells when `data` is not provided. 0 = blocked, 1+ = walkable cost. Default 1. */\n defaultCost?: number;\n}\n\nexport function createGrid(config: GridConfig): Grid {\n const { width, height, defaultCost = 1 } = config;\n if (width <= 0 || height <= 0) {\n throw new RangeError('Grid dimensions must be positive');\n }\n const w = width | 0;\n const h = height | 0;\n let data: Uint8Array;\n if (config.data) {\n if (config.data.length !== w * h) {\n throw new RangeError(`Grid data length ${config.data.length} does not match ${w}x${h}`);\n }\n data = config.data;\n } else {\n data = new Uint8Array(w * h);\n if (defaultCost > 0) {\n data.fill(defaultCost);\n }\n }\n return { width: w, height: h, data };\n}\n\nexport function inBounds(grid: Grid, x: number, y: number): boolean {\n return x >= 0 && y >= 0 && x < grid.width && y < grid.height;\n}\n\nexport function isWalkable(grid: Grid, x: number, y: number): boolean {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return false;\n return grid.data[(y | 0) * grid.width + (x | 0)]! > 0;\n}\n\nexport function getCost(grid: Grid, x: number, y: number): number {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return 0;\n return grid.data[(y | 0) * grid.width + (x | 0)]!;\n}\n\nexport function setWalkable(grid: Grid, x: number, y: number, walkable: boolean): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = walkable ? 1 : 0;\n}\n\nexport function setCost(grid: Grid, x: number, y: number, cost: number): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = cost > 255 ? 255 : cost < 0 ? 0 : cost | 0;\n}\n","/**\n * A* pathfinding on a Grid. Supports 4-directional and 8-directional movement\n * with weighted costs. Uses a binary min-heap for the open set.\n */\n\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\nexport interface PathResult {\n /** Grid coordinates from start to end, inclusive. */\n path: { x: number; y: number }[];\n /** Total movement cost. */\n cost: number;\n /** Number of nodes explored. */\n explored: number;\n}\n\nexport interface PathOptions {\n /** Allow diagonal movement. Default: true. */\n diagonal?: boolean;\n /** Custom heuristic. Default: octile distance (diagonal) or Manhattan (4-dir). */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n}\n\n// ─── Heuristics ──────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport function manhattanDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.abs(ax - bx) + Math.abs(ay - by);\n}\n\nexport function octileDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = Math.abs(ax - bx);\n const dy = Math.abs(ay - by);\n return dx > dy ? dx + (SQRT2 - 1) * dy : dy + (SQRT2 - 1) * dx;\n}\n\nexport function chebyshevDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.max(Math.abs(ax - bx), Math.abs(ay - by));\n}\n\nexport function euclideanDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// ─── Binary min-heap ─────────────────────────────────────────────\n\n// Indices into the heap are stored as flat integers; each entry is (f, node-index).\n// We use two parallel Float64Arrays to avoid object allocation.\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n // Bubble up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n // Swap\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n // Sift down\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── Neighbors (pre-computed offsets) ────────────────────────────\n\n// 4-directional: N, E, S, W\nconst DIR4_X = [0, 1, 0, -1];\nconst DIR4_Y = [-1, 0, 1, 0];\n\n// 8-directional: N, NE, E, SE, S, SW, W, NW\nconst DIR8_X = [0, 1, 1, 1, 0, -1, -1, -1];\nconst DIR8_Y = [-1, -1, 0, 1, 1, 1, 0, -1];\nconst DIR8_COST = [1, SQRT2, 1, SQRT2, 1, SQRT2, 1, SQRT2];\n\n// ─── A* ──────────────────────────────────────────────────────────\n\nexport function findPath(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: PathOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n // Quick rejects\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const diagonal = options?.diagonal !== false;\n const maxIter = options?.maxIterations ?? grid.width * grid.height;\n const heuristic = options?.heuristic ?? (diagonal ? octileDistance : manhattanDistance);\n\n const w = grid.width;\n const total = w * grid.height;\n\n // gScore and closed set\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n const dirX = diagonal ? DIR8_X : DIR4_X;\n const dirY = diagonal ? DIR8_Y : DIR4_Y;\n const dirCost = diagonal ? DIR8_COST : null;\n const dirCount = dirX.length;\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return reconstructPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n const currentG = g[currentIdx]!;\n\n for (let d = 0; d < dirCount; d++) {\n const nx = cx + dirX[d]!;\n const ny = cy + dirY[d]!;\n if (nx < 0 || ny < 0 || nx >= w || ny >= grid.height) continue;\n\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const tileCost = grid.data[nIdx]!;\n if (tileCost === 0) continue;\n\n // For diagonal movement, check that both cardinal neighbors are walkable (corner-cutting)\n if (diagonal && d & 1) {\n const dx = dirX[d]!;\n const dy = dirY[d]!;\n if (!isWalkable(grid, cx + dx, cy) || !isWalkable(grid, cx, cy + dy)) continue;\n }\n\n const moveCost = dirCost ? dirCost[d]! * tileCost : tileCost;\n const tentativeG = currentG + moveCost;\n\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n const f = tentativeG + heuristic(nx, ny, ex, ey);\n heapPush(heap, f, nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction reconstructPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n const path: { x: number; y: number }[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n path.push({ x: idx % w, y: (idx / w) | 0 });\n idx = cameFrom[idx]!;\n }\n path.reverse();\n return { path, cost, explored };\n}\n","/**\n * Jump Point Search — an optimization over A* for uniform-cost grids.\n * Only works when all walkable tiles have cost 1. Falls back to A* behavior\n * if weighted costs are detected.\n *\n * JPS prunes symmetric paths by \"jumping\" along directions until hitting\n * a wall, the goal, or a forced neighbor.\n */\n\nimport type { PathResult } from './astar.js';\nimport { octileDistance } from './astar.js';\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\n// ─── Binary min-heap (duplicated from astar for tree-shakeability) ─\n\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── JPS ─────────────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport interface JpsOptions {\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n /** Custom heuristic. Default: octile distance. */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n}\n\nexport function findPathJPS(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: JpsOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const w = grid.width;\n const h = grid.height;\n const total = w * h;\n const maxIter = options?.maxIterations ?? total;\n const heuristic = options?.heuristic ?? octileDistance;\n\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return buildPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n\n const neighbors = identifySuccessors(grid, cx, cy, cameFrom, w, h, ex, ey);\n for (let i = 0; i < neighbors.length; i += 2) {\n const nx = neighbors[i]!;\n const ny = neighbors[i + 1]!;\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const dx = Math.abs(nx - cx);\n const dy = Math.abs(ny - cy);\n const diag = Math.min(dx, dy);\n const straight = Math.abs(dx - dy);\n const dist = diag * SQRT2 + straight;\n const tentativeG = g[currentIdx]! + dist;\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n heapPush(heap, tentativeG + heuristic(nx, ny, ex, ey), nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction identifySuccessors(\n grid: Grid,\n cx: number,\n cy: number,\n cameFrom: Int32Array,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number[] {\n const results: number[] = [];\n const parentIdx = cameFrom[cy * w + cx]!;\n\n if (parentIdx === -1) {\n // Start node: explore all 8 directions\n for (let dx = -1; dx <= 1; dx++) {\n for (let dy = -1; dy <= 1; dy++) {\n if (dx === 0 && dy === 0) continue;\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) {\n results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n } else {\n const px = parentIdx % w;\n const py = (parentIdx / w) | 0;\n const dx = clampDir(cx - px);\n const dy = clampDir(cy - py);\n\n if (dx !== 0 && dy !== 0) {\n // Diagonal move: check straight and diagonal successors\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy + dy)) {\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n // Forced neighbors\n if (!isWalkable(grid, cx - dx, cy) && isWalkable(grid, cx - dx, cy + dy)) {\n const jp = jump(grid, cx, cy, -dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - dy) && isWalkable(grid, cx + dx, cy - dy)) {\n const jp = jump(grid, cx, cy, dx, -dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else if (dx !== 0) {\n // Horizontal move\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy + 1) && isWalkable(grid, cx + dx, cy + 1)) {\n const jp = jump(grid, cx, cy, dx, 1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - 1) && isWalkable(grid, cx + dx, cy - 1)) {\n const jp = jump(grid, cx, cy, dx, -1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else {\n // Vertical move\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx + 1, cy) && isWalkable(grid, cx + 1, cy + dy)) {\n const jp = jump(grid, cx, cy, 1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx - 1, cy) && isWalkable(grid, cx - 1, cy + dy)) {\n const jp = jump(grid, cx, cy, -1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n\n return results;\n}\n\nfunction jump(\n grid: Grid,\n cx: number,\n cy: number,\n dx: number,\n dy: number,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number {\n const nx = cx + dx;\n const ny = cy + dy;\n\n if (!isWalkable(grid, nx, ny)) return -1;\n if (nx === ex && ny === ey) return ny * w + nx;\n\n // Forced neighbor check\n if (dx !== 0 && dy !== 0) {\n // Diagonal\n if (\n (!isWalkable(grid, nx - dx, ny) && isWalkable(grid, nx - dx, ny + dy)) ||\n (!isWalkable(grid, nx, ny - dy) && isWalkable(grid, nx + dx, ny - dy))\n ) {\n return ny * w + nx;\n }\n // Recurse in cardinal directions\n if (jump(grid, nx, ny, dx, 0, w, h, ex, ey) !== -1) return ny * w + nx;\n if (jump(grid, nx, ny, 0, dy, w, h, ex, ey) !== -1) return ny * w + nx;\n } else if (dx !== 0) {\n // Horizontal\n if (\n (!isWalkable(grid, nx, ny + 1) && isWalkable(grid, nx + dx, ny + 1)) ||\n (!isWalkable(grid, nx, ny - 1) && isWalkable(grid, nx + dx, ny - 1))\n ) {\n return ny * w + nx;\n }\n } else {\n // Vertical\n if (\n (!isWalkable(grid, nx + 1, ny) && isWalkable(grid, nx + 1, ny + dy)) ||\n (!isWalkable(grid, nx - 1, ny) && isWalkable(grid, nx - 1, ny + dy))\n ) {\n return ny * w + nx;\n }\n }\n\n // Keep jumping (iterative for cardinal, recursive for diagonal via identifySuccessors)\n return jump(grid, nx, ny, dx, dy, w, h, ex, ey);\n}\n\nfunction clampDir(v: number): number {\n return v > 0 ? 1 : v < 0 ? -1 : 0;\n}\n\nfunction buildPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n // JPS produces jump points — we interpolate the full path\n const jumpPoints: number[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n jumpPoints.push(idx);\n idx = cameFrom[idx]!;\n }\n jumpPoints.reverse();\n\n const path: { x: number; y: number }[] = [];\n for (let i = 0; i < jumpPoints.length; i++) {\n const jpIdx = jumpPoints[i]!;\n const jx = jpIdx % w;\n const jy = (jpIdx / w) | 0;\n if (i === 0) {\n path.push({ x: jx, y: jy });\n } else {\n const prev = path[path.length - 1]!;\n // Interpolate from prev to current jump point\n let cx = prev.x;\n let cy = prev.y;\n while (cx !== jx || cy !== jy) {\n if (cx < jx) cx++;\n else if (cx > jx) cx--;\n if (cy < jy) cy++;\n else if (cy > jy) cy--;\n path.push({ x: cx, y: cy });\n }\n }\n }\n\n return { path, cost, explored };\n}\n","/**\n * Lightweight navmesh pathfinding for polygon-based environments.\n * Supports convex polygon navigation with A* over the polygon graph\n * and the Simple Stupid Funnel Algorithm for path smoothing.\n */\n\nimport type { Vec2 } from '@flare-engine/math';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface NavMeshPolygon {\n readonly id: number;\n /** Convex polygon vertices in winding order. */\n readonly vertices: readonly Vec2[];\n /** Ids of neighboring polygons (sharing an edge). */\n readonly neighbors: readonly number[];\n /** Shared edge per neighbor: [neighborId] → { a, b } portal endpoints. */\n readonly portals: readonly Portal[];\n}\n\nexport interface Portal {\n /** Neighbor polygon id. */\n neighborId: number;\n /** Left endpoint of the shared edge (from the perspective of travel). */\n a: Vec2;\n /** Right endpoint of the shared edge. */\n b: Vec2;\n}\n\nexport interface NavMesh {\n readonly polygons: readonly NavMeshPolygon[];\n}\n\nexport interface NavMeshPathResult {\n /** Smoothed world-space waypoints. */\n path: Vec2[];\n /** Polygon ids traversed. */\n corridor: number[];\n}\n\n// ─── Construction ────────────────────────────────────────────────\n\nexport function createNavMesh(polygons: NavMeshPolygon[]): NavMesh {\n return { polygons };\n}\n\n// ─── Point-in-polygon ────────────────────────────────────────────\n\nfunction pointInConvexPolygon(verts: readonly Vec2[], px: number, py: number): boolean {\n const n = verts.length;\n if (n < 3) return false;\n let sign = 0;\n for (let i = 0; i < n; i++) {\n const a = verts[i]!;\n const b = verts[(i + 1) % n]!;\n const cross = (b.x - a.x) * (py - a.y) - (b.y - a.y) * (px - a.x);\n if (cross !== 0) {\n const s = cross > 0 ? 1 : -1;\n if (sign === 0) sign = s;\n else if (sign !== s) return false;\n }\n }\n return true;\n}\n\nexport function findPolygonAt(mesh: NavMesh, x: number, y: number): number {\n for (let i = 0; i < mesh.polygons.length; i++) {\n if (pointInConvexPolygon(mesh.polygons[i]!.vertices, x, y)) {\n return mesh.polygons[i]!.id;\n }\n }\n return -1;\n}\n\n// ─── Polygon centroid ────────────────────────────────────────────\n\nfunction centroid(verts: readonly Vec2[]): Vec2 {\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < verts.length; i++) {\n cx += verts[i]!.x;\n cy += verts[i]!.y;\n }\n const n = verts.length;\n return { x: cx / n, y: cy / n };\n}\n\n// ─── A* over polygon graph ───────────────────────────────────────\n\nfunction distSq(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return dx * dx + dy * dy;\n}\n\nfunction dist(ax: number, ay: number, bx: number, by: number): number {\n return Math.sqrt(distSq(ax, ay, bx, by));\n}\n\nexport function findNavMeshPath(mesh: NavMesh, start: Vec2, end: Vec2): NavMeshPathResult | null {\n const startPolyId = findPolygonAt(mesh, start.x, start.y);\n const endPolyId = findPolygonAt(mesh, end.x, end.y);\n if (startPolyId === -1 || endPolyId === -1) return null;\n if (startPolyId === endPolyId) {\n return {\n path: [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ],\n corridor: [startPolyId],\n };\n }\n\n // Build index: id → polygon\n const byId = new Map<number, NavMeshPolygon>();\n for (let i = 0; i < mesh.polygons.length; i++) {\n const p = mesh.polygons[i]!;\n byId.set(p.id, p);\n }\n\n // A* on polygon graph\n const gScore = new Map<number, number>();\n const fScore = new Map<number, number>();\n const cameFrom = new Map<number, number>();\n\n gScore.set(startPolyId, 0);\n\n const endPoly = byId.get(endPolyId)!;\n const endCenter = centroid(endPoly.vertices);\n const startPoly = byId.get(startPolyId)!;\n const startCenter = centroid(startPoly.vertices);\n\n fScore.set(startPolyId, dist(startCenter.x, startCenter.y, endCenter.x, endCenter.y));\n\n // Simple array-based open set (fine for typical navmeshes with <1000 polygons)\n const open: number[] = [startPolyId];\n const inOpen = new Set<number>([startPolyId]);\n const closed = new Set<number>();\n\n while (open.length > 0) {\n // Find lowest f-score\n let bestI = 0;\n let bestF = fScore.get(open[0]!) ?? Number.POSITIVE_INFINITY;\n for (let i = 1; i < open.length; i++) {\n const f = fScore.get(open[i]!) ?? Number.POSITIVE_INFINITY;\n if (f < bestF) {\n bestF = f;\n bestI = i;\n }\n }\n const currentId = open[bestI]!;\n\n if (currentId === endPolyId) {\n // Reconstruct corridor\n const corridor: number[] = [];\n let id: number | undefined = endPolyId;\n while (id !== undefined) {\n corridor.push(id);\n id = cameFrom.get(id);\n }\n corridor.reverse();\n const path = funnelSmooth(mesh, byId, corridor, start, end);\n return { path, corridor };\n }\n\n // Remove from open\n open[bestI] = open[open.length - 1]!;\n open.length--;\n inOpen.delete(currentId);\n closed.add(currentId);\n\n const current = byId.get(currentId)!;\n const currentCenter = centroid(current.vertices);\n const currentG = gScore.get(currentId)!;\n\n for (let i = 0; i < current.portals.length; i++) {\n const portal = current.portals[i]!;\n const neighborId = portal.neighborId;\n if (closed.has(neighborId)) continue;\n\n const neighbor = byId.get(neighborId);\n if (!neighbor) continue;\n\n const portalMidX = (portal.a.x + portal.b.x) * 0.5;\n const portalMidY = (portal.a.y + portal.b.y) * 0.5;\n const tentativeG = currentG + dist(currentCenter.x, currentCenter.y, portalMidX, portalMidY);\n\n const prevG = gScore.get(neighborId);\n if (prevG !== undefined && tentativeG >= prevG) continue;\n\n gScore.set(neighborId, tentativeG);\n cameFrom.set(neighborId, currentId);\n\n const neighborCenter = centroid(neighbor.vertices);\n fScore.set(\n neighborId,\n tentativeG + dist(neighborCenter.x, neighborCenter.y, endCenter.x, endCenter.y),\n );\n\n if (!inOpen.has(neighborId)) {\n open.push(neighborId);\n inOpen.add(neighborId);\n }\n }\n }\n\n return null;\n}\n\n// ─── Simple Stupid Funnel Algorithm ──────────────────────────────\n\nfunction funnelSmooth(\n _mesh: NavMesh,\n byId: Map<number, NavMeshPolygon>,\n corridor: number[],\n start: Vec2,\n end: Vec2,\n): Vec2[] {\n if (corridor.length <= 1) {\n return [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ];\n }\n\n // Build portal list from corridor\n const portalsLeft: Vec2[] = [];\n const portalsRight: Vec2[] = [];\n\n portalsLeft.push({ x: start.x, y: start.y });\n portalsRight.push({ x: start.x, y: start.y });\n\n for (let i = 0; i < corridor.length - 1; i++) {\n const poly = byId.get(corridor[i]!)!;\n const nextId = corridor[i + 1]!;\n // Find the portal to the next polygon\n let found = false;\n for (let j = 0; j < poly.portals.length; j++) {\n if (poly.portals[j]!.neighborId === nextId) {\n portalsLeft.push(poly.portals[j]!.a);\n portalsRight.push(poly.portals[j]!.b);\n found = true;\n break;\n }\n }\n if (!found) {\n // Fallback: straight line through centroids\n const c = centroid(poly.vertices);\n portalsLeft.push(c);\n portalsRight.push(c);\n }\n }\n\n portalsLeft.push({ x: end.x, y: end.y });\n portalsRight.push({ x: end.x, y: end.y });\n\n // Simple Stupid Funnel Algorithm (Mononen)\n // Walk the portal list and maintain a funnel (apex, left, right).\n const path: Vec2[] = [];\n\n let apexPt = portalsLeft[0]!; // start point\n let leftPt = portalsLeft[0]!;\n let rightPt = portalsRight[0]!;\n let apexIdx = 0;\n let leftIdx = 0;\n let rightIdx = 0;\n\n path.push({ x: apexPt.x, y: apexPt.y });\n\n const n = portalsLeft.length;\n\n for (let i = 1; i < n; i++) {\n const pL = portalsLeft[i]!;\n const pR = portalsRight[i]!;\n\n // Try to tighten the right side\n if (triArea2(apexPt, rightPt, pR) <= 0) {\n if (vEqual(apexPt, rightPt) || triArea2(apexPt, leftPt, pR) > 0) {\n // Tighten\n rightPt = pR;\n rightIdx = i;\n } else {\n // Right crossed over left → left becomes new apex\n addUnique(path, leftPt);\n apexPt = leftPt;\n apexIdx = leftIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n continue;\n }\n }\n\n // Try to tighten the left side\n if (triArea2(apexPt, leftPt, pL) >= 0) {\n if (vEqual(apexPt, leftPt) || triArea2(apexPt, rightPt, pL) < 0) {\n // Tighten\n leftPt = pL;\n leftIdx = i;\n } else {\n // Left crossed over right → right becomes new apex\n addUnique(path, rightPt);\n apexPt = rightPt;\n apexIdx = rightIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n }\n }\n }\n\n addUnique(path, end);\n\n return path;\n}\n\nfunction addUnique(path: Vec2[], p: Vec2): void {\n const last = path[path.length - 1];\n if (!last || last.x !== p.x || last.y !== p.y) {\n path.push({ x: p.x, y: p.y });\n }\n}\n\n/** Twice the signed area of the triangle (a, b, c). Positive = CCW. */\nfunction triArea2(a: Vec2, b: Vec2, c: Vec2): number {\n return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);\n}\n\nfunction vEqual(a: Vec2, b: Vec2): boolean {\n return a.x === b.x && a.y === b.y;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["SQRT2","heapCreate","heapGrow","heapPush","heapPop"],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"sourcesContent":["/**\n * Grid data structure for tile-based pathfinding.\n * Uses a flat Uint8Array: 0 = blocked, 1+ = walkable (value = movement cost).\n */\n\nexport interface Grid {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\nexport interface GridConfig {\n width: number;\n height: number;\n /** Pre-filled grid data. Length must equal width * height. */\n data?: Uint8Array;\n /** Default cost for all cells when `data` is not provided. 0 = blocked, 1+ = walkable cost. Default 1. */\n defaultCost?: number;\n}\n\nexport function createGrid(config: GridConfig): Grid {\n const { width, height, defaultCost = 1 } = config;\n if (width <= 0 || height <= 0) {\n throw new RangeError('Grid dimensions must be positive');\n }\n const w = width | 0;\n const h = height | 0;\n let data: Uint8Array;\n if (config.data) {\n if (config.data.length !== w * h) {\n throw new RangeError(`Grid data length ${config.data.length} does not match ${w}x${h}`);\n }\n data = config.data;\n } else {\n data = new Uint8Array(w * h);\n if (defaultCost > 0) {\n data.fill(defaultCost);\n }\n }\n return { width: w, height: h, data };\n}\n\nexport function inBounds(grid: Grid, x: number, y: number): boolean {\n return x >= 0 && y >= 0 && x < grid.width && y < grid.height;\n}\n\nexport function isWalkable(grid: Grid, x: number, y: number): boolean {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return false;\n return grid.data[(y | 0) * grid.width + (x | 0)]! > 0;\n}\n\nexport function getCost(grid: Grid, x: number, y: number): number {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return 0;\n return grid.data[(y | 0) * grid.width + (x | 0)]!;\n}\n\nexport function setWalkable(grid: Grid, x: number, y: number, walkable: boolean): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = walkable ? 1 : 0;\n}\n\nexport function setCost(grid: Grid, x: number, y: number, cost: number): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = cost > 255 ? 255 : cost < 0 ? 0 : cost | 0;\n}\n","/**\n * A* pathfinding on a Grid. Supports 4-directional and 8-directional movement\n * with weighted costs. Uses a binary min-heap for the open set.\n */\n\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\nexport interface PathResult {\n /** Grid coordinates from start to end, inclusive. */\n path: { x: number; y: number }[];\n /** Total movement cost. */\n cost: number;\n /** Number of nodes explored. */\n explored: number;\n}\n\nexport interface PathOptions {\n /** Allow diagonal movement. Default: true. */\n diagonal?: boolean;\n /** Custom heuristic. Default: octile distance (diagonal) or Manhattan (4-dir). */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n}\n\n// ─── Heuristics ──────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport function manhattanDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.abs(ax - bx) + Math.abs(ay - by);\n}\n\nexport function octileDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = Math.abs(ax - bx);\n const dy = Math.abs(ay - by);\n return dx > dy ? dx + (SQRT2 - 1) * dy : dy + (SQRT2 - 1) * dx;\n}\n\nexport function chebyshevDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.max(Math.abs(ax - bx), Math.abs(ay - by));\n}\n\nexport function euclideanDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// ─── Binary min-heap ─────────────────────────────────────────────\n\n// Indices into the heap are stored as flat integers; each entry is (f, node-index).\n// We use two parallel Float64Arrays to avoid object allocation.\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\n/**\n * Double the heap's backing arrays.\n *\n * Typed arrays do not throw on an out-of-range write — the store is silently\n * dropped — so without this a push past capacity increments `size`, writes\n * nothing, and every later comparison reads `undefined`. The open set then\n * degrades without any error, and the search returns a valid-looking but\n * non-optimal path. Growth is amortized O(1): allocation happens only when\n * capacity is reached, never per push.\n */\nfunction heapGrow(h: Heap): void {\n const next = h.f.length === 0 ? 16 : h.f.length * 2;\n const f = new Float64Array(next);\n f.set(h.f);\n const idx = new Int32Array(next);\n idx.set(h.idx);\n h.f = f;\n h.idx = idx;\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n if (h.size === h.f.length) heapGrow(h);\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n // Bubble up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n // Swap\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n // Sift down\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── Neighbors (pre-computed offsets) ────────────────────────────\n\n// 4-directional: N, E, S, W\nconst DIR4_X = [0, 1, 0, -1];\nconst DIR4_Y = [-1, 0, 1, 0];\n\n// 8-directional: N, NE, E, SE, S, SW, W, NW\nconst DIR8_X = [0, 1, 1, 1, 0, -1, -1, -1];\nconst DIR8_Y = [-1, -1, 0, 1, 1, 1, 0, -1];\nconst DIR8_COST = [1, SQRT2, 1, SQRT2, 1, SQRT2, 1, SQRT2];\n\n// ─── A* ──────────────────────────────────────────────────────────\n\nexport function findPath(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: PathOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n // Quick rejects\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const diagonal = options?.diagonal !== false;\n const maxIter = options?.maxIterations ?? grid.width * grid.height;\n const heuristic = options?.heuristic ?? (diagonal ? octileDistance : manhattanDistance);\n\n const w = grid.width;\n const total = w * grid.height;\n\n // gScore and closed set\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n const dirX = diagonal ? DIR8_X : DIR4_X;\n const dirY = diagonal ? DIR8_Y : DIR4_Y;\n const dirCost = diagonal ? DIR8_COST : null;\n const dirCount = dirX.length;\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return reconstructPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n const currentG = g[currentIdx]!;\n\n for (let d = 0; d < dirCount; d++) {\n const nx = cx + dirX[d]!;\n const ny = cy + dirY[d]!;\n if (nx < 0 || ny < 0 || nx >= w || ny >= grid.height) continue;\n\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const tileCost = grid.data[nIdx]!;\n if (tileCost === 0) continue;\n\n // For diagonal movement, check that both cardinal neighbors are walkable (corner-cutting)\n if (diagonal && d & 1) {\n const dx = dirX[d]!;\n const dy = dirY[d]!;\n if (!isWalkable(grid, cx + dx, cy) || !isWalkable(grid, cx, cy + dy)) continue;\n }\n\n const moveCost = dirCost ? dirCost[d]! * tileCost : tileCost;\n const tentativeG = currentG + moveCost;\n\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n const f = tentativeG + heuristic(nx, ny, ex, ey);\n heapPush(heap, f, nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction reconstructPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n const path: { x: number; y: number }[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n path.push({ x: idx % w, y: (idx / w) | 0 });\n idx = cameFrom[idx]!;\n }\n path.reverse();\n return { path, cost, explored };\n}\n","/**\n * Jump Point Search — an optimization over A* for uniform-cost grids.\n * JPS pruning is only correct when every walkable tile costs 1, so a grid with\n * any tile cost above 1 is delegated to weighted A* (`findPath`, diagonal moves)\n * after one linear scan of `grid.data`.\n *\n * JPS prunes symmetric paths by \"jumping\" along directions until hitting\n * a wall, the goal, or a forced neighbor.\n */\n\nimport type { PathResult } from './astar.js';\nimport { findPath, octileDistance } from './astar.js';\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\n// ─── Binary min-heap (duplicated from astar for tree-shakeability) ─\n\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\n/**\n * Double the heap's backing arrays.\n *\n * Typed arrays do not throw on an out-of-range write — the store is silently\n * dropped — so without this a push past capacity increments `size`, writes\n * nothing, and every later comparison reads `undefined`. The open set then\n * degrades without any error, and the search returns a valid-looking but\n * non-optimal path. Growth is amortized O(1): allocation happens only when\n * capacity is reached, never per push.\n */\nfunction heapGrow(h: Heap): void {\n const next = h.f.length === 0 ? 16 : h.f.length * 2;\n const f = new Float64Array(next);\n f.set(h.f);\n const idx = new Int32Array(next);\n idx.set(h.idx);\n h.f = f;\n h.idx = idx;\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n if (h.size === h.f.length) heapGrow(h);\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── JPS ─────────────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\n// One pass per call, never per node: JPS pruning assumes every walkable tile costs 1.\nfunction hasWeightedCost(grid: Grid): boolean {\n const data = grid.data;\n for (let i = 0; i < data.length; i++) {\n if (data[i]! > 1) return true;\n }\n return false;\n}\n\nexport interface JpsOptions {\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n /** Custom heuristic. Default: octile distance. */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n}\n\nexport function findPathJPS(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: JpsOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n if (hasWeightedCost(grid)) {\n return findPath(grid, sx, sy, ex, ey, { ...options, diagonal: true });\n }\n\n const w = grid.width;\n const h = grid.height;\n const total = w * h;\n const maxIter = options?.maxIterations ?? total;\n const heuristic = options?.heuristic ?? octileDistance;\n\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return buildPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n\n const neighbors = identifySuccessors(grid, cx, cy, cameFrom, w, h, ex, ey);\n for (let i = 0; i < neighbors.length; i += 2) {\n const nx = neighbors[i]!;\n const ny = neighbors[i + 1]!;\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const dx = Math.abs(nx - cx);\n const dy = Math.abs(ny - cy);\n const diag = Math.min(dx, dy);\n const straight = Math.abs(dx - dy);\n const dist = diag * SQRT2 + straight;\n const tentativeG = g[currentIdx]! + dist;\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n heapPush(heap, tentativeG + heuristic(nx, ny, ex, ey), nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction identifySuccessors(\n grid: Grid,\n cx: number,\n cy: number,\n cameFrom: Int32Array,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number[] {\n const results: number[] = [];\n const parentIdx = cameFrom[cy * w + cx]!;\n\n if (parentIdx === -1) {\n // Start node: explore all 8 directions\n for (let dx = -1; dx <= 1; dx++) {\n for (let dy = -1; dy <= 1; dy++) {\n if (dx === 0 && dy === 0) continue;\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) {\n results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n } else {\n const px = parentIdx % w;\n const py = (parentIdx / w) | 0;\n const dx = clampDir(cx - px);\n const dy = clampDir(cy - py);\n\n if (dx !== 0 && dy !== 0) {\n // Diagonal move: check straight and diagonal successors\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy + dy)) {\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n // Forced neighbors\n if (!isWalkable(grid, cx - dx, cy) && isWalkable(grid, cx - dx, cy + dy)) {\n const jp = jump(grid, cx, cy, -dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - dy) && isWalkable(grid, cx + dx, cy - dy)) {\n const jp = jump(grid, cx, cy, dx, -dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else if (dx !== 0) {\n // Horizontal move\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy + 1) && isWalkable(grid, cx + dx, cy + 1)) {\n const jp = jump(grid, cx, cy, dx, 1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - 1) && isWalkable(grid, cx + dx, cy - 1)) {\n const jp = jump(grid, cx, cy, dx, -1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else {\n // Vertical move\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx + 1, cy) && isWalkable(grid, cx + 1, cy + dy)) {\n const jp = jump(grid, cx, cy, 1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx - 1, cy) && isWalkable(grid, cx - 1, cy + dy)) {\n const jp = jump(grid, cx, cy, -1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n\n return results;\n}\n\nfunction jump(\n grid: Grid,\n cx: number,\n cy: number,\n dx: number,\n dy: number,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number {\n const nx = cx + dx;\n const ny = cy + dy;\n\n if (!isWalkable(grid, nx, ny)) return -1;\n if (nx === ex && ny === ey) return ny * w + nx;\n\n // Forced neighbor check\n if (dx !== 0 && dy !== 0) {\n // Diagonal\n if (\n (!isWalkable(grid, nx - dx, ny) && isWalkable(grid, nx - dx, ny + dy)) ||\n (!isWalkable(grid, nx, ny - dy) && isWalkable(grid, nx + dx, ny - dy))\n ) {\n return ny * w + nx;\n }\n // Recurse in cardinal directions\n if (jump(grid, nx, ny, dx, 0, w, h, ex, ey) !== -1) return ny * w + nx;\n if (jump(grid, nx, ny, 0, dy, w, h, ex, ey) !== -1) return ny * w + nx;\n } else if (dx !== 0) {\n // Horizontal\n if (\n (!isWalkable(grid, nx, ny + 1) && isWalkable(grid, nx + dx, ny + 1)) ||\n (!isWalkable(grid, nx, ny - 1) && isWalkable(grid, nx + dx, ny - 1))\n ) {\n return ny * w + nx;\n }\n } else {\n // Vertical\n if (\n (!isWalkable(grid, nx + 1, ny) && isWalkable(grid, nx + 1, ny + dy)) ||\n (!isWalkable(grid, nx - 1, ny) && isWalkable(grid, nx - 1, ny + dy))\n ) {\n return ny * w + nx;\n }\n }\n\n // Keep jumping (iterative for cardinal, recursive for diagonal via identifySuccessors)\n return jump(grid, nx, ny, dx, dy, w, h, ex, ey);\n}\n\nfunction clampDir(v: number): number {\n return v > 0 ? 1 : v < 0 ? -1 : 0;\n}\n\nfunction buildPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n // JPS produces jump points — we interpolate the full path\n const jumpPoints: number[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n jumpPoints.push(idx);\n idx = cameFrom[idx]!;\n }\n jumpPoints.reverse();\n\n const path: { x: number; y: number }[] = [];\n for (let i = 0; i < jumpPoints.length; i++) {\n const jpIdx = jumpPoints[i]!;\n const jx = jpIdx % w;\n const jy = (jpIdx / w) | 0;\n if (i === 0) {\n path.push({ x: jx, y: jy });\n } else {\n const prev = path[path.length - 1]!;\n // Interpolate from prev to current jump point\n let cx = prev.x;\n let cy = prev.y;\n while (cx !== jx || cy !== jy) {\n if (cx < jx) cx++;\n else if (cx > jx) cx--;\n if (cy < jy) cy++;\n else if (cy > jy) cy--;\n path.push({ x: cx, y: cy });\n }\n }\n }\n\n return { path, cost, explored };\n}\n","/**\n * Lightweight navmesh pathfinding for polygon-based environments.\n * Supports convex polygon navigation with A* over the polygon graph\n * and the Simple Stupid Funnel Algorithm for path smoothing.\n */\n\nimport type { Vec2 } from '@flare-engine/math';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface NavMeshPolygon {\n readonly id: number;\n /** Convex polygon vertices in winding order. */\n readonly vertices: readonly Vec2[];\n /** One `Portal` per adjacent polygon: the shared edge plus that neighbour's id. */\n readonly portals: readonly Portal[];\n}\n\nexport interface Portal {\n /** Neighbor polygon id. */\n neighborId: number;\n /** Left endpoint of the shared edge (from the perspective of travel). */\n a: Vec2;\n /** Right endpoint of the shared edge. */\n b: Vec2;\n}\n\nexport interface NavMesh {\n readonly polygons: readonly NavMeshPolygon[];\n}\n\nexport interface NavMeshPathResult {\n /** Smoothed world-space waypoints. */\n path: Vec2[];\n /** Polygon ids traversed. */\n corridor: number[];\n}\n\n// ─── Construction ────────────────────────────────────────────────\n\nexport function createNavMesh(polygons: NavMeshPolygon[]): NavMesh {\n return { polygons };\n}\n\n// ─── Point-in-polygon ────────────────────────────────────────────\n\nfunction pointInConvexPolygon(verts: readonly Vec2[], px: number, py: number): boolean {\n const n = verts.length;\n if (n < 3) return false;\n let sign = 0;\n for (let i = 0; i < n; i++) {\n const a = verts[i]!;\n const b = verts[(i + 1) % n]!;\n const cross = (b.x - a.x) * (py - a.y) - (b.y - a.y) * (px - a.x);\n if (cross !== 0) {\n const s = cross > 0 ? 1 : -1;\n if (sign === 0) sign = s;\n else if (sign !== s) return false;\n }\n }\n return true;\n}\n\nexport function findPolygonAt(mesh: NavMesh, x: number, y: number): number {\n for (let i = 0; i < mesh.polygons.length; i++) {\n if (pointInConvexPolygon(mesh.polygons[i]!.vertices, x, y)) {\n return mesh.polygons[i]!.id;\n }\n }\n return -1;\n}\n\n// ─── Polygon centroid ────────────────────────────────────────────\n\nfunction centroid(verts: readonly Vec2[]): Vec2 {\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < verts.length; i++) {\n cx += verts[i]!.x;\n cy += verts[i]!.y;\n }\n const n = verts.length;\n return { x: cx / n, y: cy / n };\n}\n\n// ─── A* over polygon graph ───────────────────────────────────────\n\nfunction distSq(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return dx * dx + dy * dy;\n}\n\nfunction dist(ax: number, ay: number, bx: number, by: number): number {\n return Math.sqrt(distSq(ax, ay, bx, by));\n}\n\nexport function findNavMeshPath(mesh: NavMesh, start: Vec2, end: Vec2): NavMeshPathResult | null {\n const startPolyId = findPolygonAt(mesh, start.x, start.y);\n const endPolyId = findPolygonAt(mesh, end.x, end.y);\n if (startPolyId === -1 || endPolyId === -1) return null;\n if (startPolyId === endPolyId) {\n return {\n path: [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ],\n corridor: [startPolyId],\n };\n }\n\n // Build index: id → polygon\n const byId = new Map<number, NavMeshPolygon>();\n for (let i = 0; i < mesh.polygons.length; i++) {\n const p = mesh.polygons[i]!;\n byId.set(p.id, p);\n }\n\n // A* on polygon graph\n const gScore = new Map<number, number>();\n const fScore = new Map<number, number>();\n const cameFrom = new Map<number, number>();\n\n gScore.set(startPolyId, 0);\n\n const endPoly = byId.get(endPolyId)!;\n const endCenter = centroid(endPoly.vertices);\n const startPoly = byId.get(startPolyId)!;\n const startCenter = centroid(startPoly.vertices);\n\n fScore.set(startPolyId, dist(startCenter.x, startCenter.y, endCenter.x, endCenter.y));\n\n // Simple array-based open set (fine for typical navmeshes with <1000 polygons)\n const open: number[] = [startPolyId];\n const inOpen = new Set<number>([startPolyId]);\n const closed = new Set<number>();\n\n while (open.length > 0) {\n // Find lowest f-score\n let bestI = 0;\n let bestF = fScore.get(open[0]!) ?? Number.POSITIVE_INFINITY;\n for (let i = 1; i < open.length; i++) {\n const f = fScore.get(open[i]!) ?? Number.POSITIVE_INFINITY;\n if (f < bestF) {\n bestF = f;\n bestI = i;\n }\n }\n const currentId = open[bestI]!;\n\n if (currentId === endPolyId) {\n // Reconstruct corridor\n const corridor: number[] = [];\n let id: number | undefined = endPolyId;\n while (id !== undefined) {\n corridor.push(id);\n id = cameFrom.get(id);\n }\n corridor.reverse();\n const path = funnelSmooth(mesh, byId, corridor, start, end);\n return { path, corridor };\n }\n\n // Remove from open\n open[bestI] = open[open.length - 1]!;\n open.length--;\n inOpen.delete(currentId);\n closed.add(currentId);\n\n const current = byId.get(currentId)!;\n const currentCenter = centroid(current.vertices);\n const currentG = gScore.get(currentId)!;\n\n for (let i = 0; i < current.portals.length; i++) {\n const portal = current.portals[i]!;\n const neighborId = portal.neighborId;\n if (closed.has(neighborId)) continue;\n\n const neighbor = byId.get(neighborId);\n if (!neighbor) continue;\n\n const portalMidX = (portal.a.x + portal.b.x) * 0.5;\n const portalMidY = (portal.a.y + portal.b.y) * 0.5;\n const tentativeG = currentG + dist(currentCenter.x, currentCenter.y, portalMidX, portalMidY);\n\n const prevG = gScore.get(neighborId);\n if (prevG !== undefined && tentativeG >= prevG) continue;\n\n gScore.set(neighborId, tentativeG);\n cameFrom.set(neighborId, currentId);\n\n const neighborCenter = centroid(neighbor.vertices);\n fScore.set(\n neighborId,\n tentativeG + dist(neighborCenter.x, neighborCenter.y, endCenter.x, endCenter.y),\n );\n\n if (!inOpen.has(neighborId)) {\n open.push(neighborId);\n inOpen.add(neighborId);\n }\n }\n }\n\n return null;\n}\n\n// ─── Simple Stupid Funnel Algorithm ──────────────────────────────\n\nfunction funnelSmooth(\n _mesh: NavMesh,\n byId: Map<number, NavMeshPolygon>,\n corridor: number[],\n start: Vec2,\n end: Vec2,\n): Vec2[] {\n if (corridor.length <= 1) {\n return [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ];\n }\n\n // Build portal list from corridor\n const portalsLeft: Vec2[] = [];\n const portalsRight: Vec2[] = [];\n\n portalsLeft.push({ x: start.x, y: start.y });\n portalsRight.push({ x: start.x, y: start.y });\n\n for (let i = 0; i < corridor.length - 1; i++) {\n const poly = byId.get(corridor[i]!)!;\n const nextId = corridor[i + 1]!;\n // Find the portal to the next polygon\n let found = false;\n for (let j = 0; j < poly.portals.length; j++) {\n if (poly.portals[j]!.neighborId === nextId) {\n portalsLeft.push(poly.portals[j]!.a);\n portalsRight.push(poly.portals[j]!.b);\n found = true;\n break;\n }\n }\n if (!found) {\n // Fallback: straight line through centroids\n const c = centroid(poly.vertices);\n portalsLeft.push(c);\n portalsRight.push(c);\n }\n }\n\n portalsLeft.push({ x: end.x, y: end.y });\n portalsRight.push({ x: end.x, y: end.y });\n\n // Simple Stupid Funnel Algorithm (Mononen)\n // Walk the portal list and maintain a funnel (apex, left, right).\n const path: Vec2[] = [];\n\n let apexPt = portalsLeft[0]!; // start point\n let leftPt = portalsLeft[0]!;\n let rightPt = portalsRight[0]!;\n let apexIdx = 0;\n let leftIdx = 0;\n let rightIdx = 0;\n\n path.push({ x: apexPt.x, y: apexPt.y });\n\n const n = portalsLeft.length;\n\n for (let i = 1; i < n; i++) {\n const pL = portalsLeft[i]!;\n const pR = portalsRight[i]!;\n\n // Try to tighten the right side\n if (triArea2(apexPt, rightPt, pR) <= 0) {\n if (vEqual(apexPt, rightPt) || triArea2(apexPt, leftPt, pR) > 0) {\n // Tighten\n rightPt = pR;\n rightIdx = i;\n } else {\n // Right crossed over left → left becomes new apex\n addUnique(path, leftPt);\n apexPt = leftPt;\n apexIdx = leftIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n continue;\n }\n }\n\n // Try to tighten the left side\n if (triArea2(apexPt, leftPt, pL) >= 0) {\n if (vEqual(apexPt, leftPt) || triArea2(apexPt, rightPt, pL) < 0) {\n // Tighten\n leftPt = pL;\n leftIdx = i;\n } else {\n // Left crossed over right → right becomes new apex\n addUnique(path, rightPt);\n apexPt = rightPt;\n apexIdx = rightIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n }\n }\n }\n\n addUnique(path, end);\n\n return path;\n}\n\nfunction addUnique(path: Vec2[], p: Vec2): void {\n const last = path[path.length - 1];\n if (!last || last.x !== p.x || last.y !== p.y) {\n path.push({ x: p.x, y: p.y });\n }\n}\n\n/** Twice the signed area of the triangle (a, b, c). Positive = CCW. */\nfunction triArea2(a: Vec2, b: Vec2, c: Vec2): number {\n return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);\n}\n\nfunction vEqual(a: Vec2, b: Vec2): boolean {\n return a.x === b.x && a.y === b.y;\n}\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -63,7 +63,27 @@ function heapCreate$1(capacity) {
|
|
|
63
63
|
size: 0
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Double the heap's backing arrays.
|
|
68
|
+
*
|
|
69
|
+
* Typed arrays do not throw on an out-of-range write — the store is silently
|
|
70
|
+
* dropped — so without this a push past capacity increments `size`, writes
|
|
71
|
+
* nothing, and every later comparison reads `undefined`. The open set then
|
|
72
|
+
* degrades without any error, and the search returns a valid-looking but
|
|
73
|
+
* non-optimal path. Growth is amortized O(1): allocation happens only when
|
|
74
|
+
* capacity is reached, never per push.
|
|
75
|
+
*/
|
|
76
|
+
function heapGrow$1(h) {
|
|
77
|
+
const next = h.f.length === 0 ? 16 : h.f.length * 2;
|
|
78
|
+
const f = new Float64Array(next);
|
|
79
|
+
f.set(h.f);
|
|
80
|
+
const idx = new Int32Array(next);
|
|
81
|
+
idx.set(h.idx);
|
|
82
|
+
h.f = f;
|
|
83
|
+
h.idx = idx;
|
|
84
|
+
}
|
|
66
85
|
function heapPush$1(h, f, nodeIdx) {
|
|
86
|
+
if (h.size === h.f.length) heapGrow$1(h);
|
|
67
87
|
let i = h.size++;
|
|
68
88
|
h.f[i] = f;
|
|
69
89
|
h.idx[i] = nodeIdx;
|
|
@@ -238,7 +258,27 @@ function heapCreate(capacity) {
|
|
|
238
258
|
size: 0
|
|
239
259
|
};
|
|
240
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Double the heap's backing arrays.
|
|
263
|
+
*
|
|
264
|
+
* Typed arrays do not throw on an out-of-range write — the store is silently
|
|
265
|
+
* dropped — so without this a push past capacity increments `size`, writes
|
|
266
|
+
* nothing, and every later comparison reads `undefined`. The open set then
|
|
267
|
+
* degrades without any error, and the search returns a valid-looking but
|
|
268
|
+
* non-optimal path. Growth is amortized O(1): allocation happens only when
|
|
269
|
+
* capacity is reached, never per push.
|
|
270
|
+
*/
|
|
271
|
+
function heapGrow(h) {
|
|
272
|
+
const next = h.f.length === 0 ? 16 : h.f.length * 2;
|
|
273
|
+
const f = new Float64Array(next);
|
|
274
|
+
f.set(h.f);
|
|
275
|
+
const idx = new Int32Array(next);
|
|
276
|
+
idx.set(h.idx);
|
|
277
|
+
h.f = f;
|
|
278
|
+
h.idx = idx;
|
|
279
|
+
}
|
|
241
280
|
function heapPush(h, f, nodeIdx) {
|
|
281
|
+
if (h.size === h.f.length) heapGrow(h);
|
|
242
282
|
let i = h.size++;
|
|
243
283
|
h.f[i] = f;
|
|
244
284
|
h.idx[i] = nodeIdx;
|
|
@@ -280,6 +320,11 @@ function heapPop(h) {
|
|
|
280
320
|
return result;
|
|
281
321
|
}
|
|
282
322
|
const SQRT2 = Math.SQRT2;
|
|
323
|
+
function hasWeightedCost(grid) {
|
|
324
|
+
const data = grid.data;
|
|
325
|
+
for (let i = 0; i < data.length; i++) if (data[i] > 1) return true;
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
283
328
|
function findPathJPS(grid, startX, startY, endX, endY, options) {
|
|
284
329
|
const sx = startX | 0;
|
|
285
330
|
const sy = startY | 0;
|
|
@@ -294,6 +339,10 @@ function findPathJPS(grid, startX, startY, endX, endY, options) {
|
|
|
294
339
|
cost: 0,
|
|
295
340
|
explored: 0
|
|
296
341
|
};
|
|
342
|
+
if (hasWeightedCost(grid)) return findPath(grid, sx, sy, ex, ey, {
|
|
343
|
+
...options,
|
|
344
|
+
diagonal: true
|
|
345
|
+
});
|
|
297
346
|
const w = grid.width;
|
|
298
347
|
const h = grid.height;
|
|
299
348
|
const total = w * h;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["SQRT2","heapCreate","heapPush","heapPop"],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"sourcesContent":["/**\n * Grid data structure for tile-based pathfinding.\n * Uses a flat Uint8Array: 0 = blocked, 1+ = walkable (value = movement cost).\n */\n\nexport interface Grid {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\nexport interface GridConfig {\n width: number;\n height: number;\n /** Pre-filled grid data. Length must equal width * height. */\n data?: Uint8Array;\n /** Default cost for all cells when `data` is not provided. 0 = blocked, 1+ = walkable cost. Default 1. */\n defaultCost?: number;\n}\n\nexport function createGrid(config: GridConfig): Grid {\n const { width, height, defaultCost = 1 } = config;\n if (width <= 0 || height <= 0) {\n throw new RangeError('Grid dimensions must be positive');\n }\n const w = width | 0;\n const h = height | 0;\n let data: Uint8Array;\n if (config.data) {\n if (config.data.length !== w * h) {\n throw new RangeError(`Grid data length ${config.data.length} does not match ${w}x${h}`);\n }\n data = config.data;\n } else {\n data = new Uint8Array(w * h);\n if (defaultCost > 0) {\n data.fill(defaultCost);\n }\n }\n return { width: w, height: h, data };\n}\n\nexport function inBounds(grid: Grid, x: number, y: number): boolean {\n return x >= 0 && y >= 0 && x < grid.width && y < grid.height;\n}\n\nexport function isWalkable(grid: Grid, x: number, y: number): boolean {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return false;\n return grid.data[(y | 0) * grid.width + (x | 0)]! > 0;\n}\n\nexport function getCost(grid: Grid, x: number, y: number): number {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return 0;\n return grid.data[(y | 0) * grid.width + (x | 0)]!;\n}\n\nexport function setWalkable(grid: Grid, x: number, y: number, walkable: boolean): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = walkable ? 1 : 0;\n}\n\nexport function setCost(grid: Grid, x: number, y: number, cost: number): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = cost > 255 ? 255 : cost < 0 ? 0 : cost | 0;\n}\n","/**\n * A* pathfinding on a Grid. Supports 4-directional and 8-directional movement\n * with weighted costs. Uses a binary min-heap for the open set.\n */\n\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\nexport interface PathResult {\n /** Grid coordinates from start to end, inclusive. */\n path: { x: number; y: number }[];\n /** Total movement cost. */\n cost: number;\n /** Number of nodes explored. */\n explored: number;\n}\n\nexport interface PathOptions {\n /** Allow diagonal movement. Default: true. */\n diagonal?: boolean;\n /** Custom heuristic. Default: octile distance (diagonal) or Manhattan (4-dir). */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n}\n\n// ─── Heuristics ──────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport function manhattanDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.abs(ax - bx) + Math.abs(ay - by);\n}\n\nexport function octileDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = Math.abs(ax - bx);\n const dy = Math.abs(ay - by);\n return dx > dy ? dx + (SQRT2 - 1) * dy : dy + (SQRT2 - 1) * dx;\n}\n\nexport function chebyshevDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.max(Math.abs(ax - bx), Math.abs(ay - by));\n}\n\nexport function euclideanDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// ─── Binary min-heap ─────────────────────────────────────────────\n\n// Indices into the heap are stored as flat integers; each entry is (f, node-index).\n// We use two parallel Float64Arrays to avoid object allocation.\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n // Bubble up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n // Swap\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n // Sift down\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── Neighbors (pre-computed offsets) ────────────────────────────\n\n// 4-directional: N, E, S, W\nconst DIR4_X = [0, 1, 0, -1];\nconst DIR4_Y = [-1, 0, 1, 0];\n\n// 8-directional: N, NE, E, SE, S, SW, W, NW\nconst DIR8_X = [0, 1, 1, 1, 0, -1, -1, -1];\nconst DIR8_Y = [-1, -1, 0, 1, 1, 1, 0, -1];\nconst DIR8_COST = [1, SQRT2, 1, SQRT2, 1, SQRT2, 1, SQRT2];\n\n// ─── A* ──────────────────────────────────────────────────────────\n\nexport function findPath(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: PathOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n // Quick rejects\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const diagonal = options?.diagonal !== false;\n const maxIter = options?.maxIterations ?? grid.width * grid.height;\n const heuristic = options?.heuristic ?? (diagonal ? octileDistance : manhattanDistance);\n\n const w = grid.width;\n const total = w * grid.height;\n\n // gScore and closed set\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n const dirX = diagonal ? DIR8_X : DIR4_X;\n const dirY = diagonal ? DIR8_Y : DIR4_Y;\n const dirCost = diagonal ? DIR8_COST : null;\n const dirCount = dirX.length;\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return reconstructPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n const currentG = g[currentIdx]!;\n\n for (let d = 0; d < dirCount; d++) {\n const nx = cx + dirX[d]!;\n const ny = cy + dirY[d]!;\n if (nx < 0 || ny < 0 || nx >= w || ny >= grid.height) continue;\n\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const tileCost = grid.data[nIdx]!;\n if (tileCost === 0) continue;\n\n // For diagonal movement, check that both cardinal neighbors are walkable (corner-cutting)\n if (diagonal && d & 1) {\n const dx = dirX[d]!;\n const dy = dirY[d]!;\n if (!isWalkable(grid, cx + dx, cy) || !isWalkable(grid, cx, cy + dy)) continue;\n }\n\n const moveCost = dirCost ? dirCost[d]! * tileCost : tileCost;\n const tentativeG = currentG + moveCost;\n\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n const f = tentativeG + heuristic(nx, ny, ex, ey);\n heapPush(heap, f, nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction reconstructPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n const path: { x: number; y: number }[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n path.push({ x: idx % w, y: (idx / w) | 0 });\n idx = cameFrom[idx]!;\n }\n path.reverse();\n return { path, cost, explored };\n}\n","/**\n * Jump Point Search — an optimization over A* for uniform-cost grids.\n * Only works when all walkable tiles have cost 1. Falls back to A* behavior\n * if weighted costs are detected.\n *\n * JPS prunes symmetric paths by \"jumping\" along directions until hitting\n * a wall, the goal, or a forced neighbor.\n */\n\nimport type { PathResult } from './astar.js';\nimport { octileDistance } from './astar.js';\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\n// ─── Binary min-heap (duplicated from astar for tree-shakeability) ─\n\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── JPS ─────────────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport interface JpsOptions {\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n /** Custom heuristic. Default: octile distance. */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n}\n\nexport function findPathJPS(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: JpsOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const w = grid.width;\n const h = grid.height;\n const total = w * h;\n const maxIter = options?.maxIterations ?? total;\n const heuristic = options?.heuristic ?? octileDistance;\n\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return buildPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n\n const neighbors = identifySuccessors(grid, cx, cy, cameFrom, w, h, ex, ey);\n for (let i = 0; i < neighbors.length; i += 2) {\n const nx = neighbors[i]!;\n const ny = neighbors[i + 1]!;\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const dx = Math.abs(nx - cx);\n const dy = Math.abs(ny - cy);\n const diag = Math.min(dx, dy);\n const straight = Math.abs(dx - dy);\n const dist = diag * SQRT2 + straight;\n const tentativeG = g[currentIdx]! + dist;\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n heapPush(heap, tentativeG + heuristic(nx, ny, ex, ey), nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction identifySuccessors(\n grid: Grid,\n cx: number,\n cy: number,\n cameFrom: Int32Array,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number[] {\n const results: number[] = [];\n const parentIdx = cameFrom[cy * w + cx]!;\n\n if (parentIdx === -1) {\n // Start node: explore all 8 directions\n for (let dx = -1; dx <= 1; dx++) {\n for (let dy = -1; dy <= 1; dy++) {\n if (dx === 0 && dy === 0) continue;\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) {\n results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n } else {\n const px = parentIdx % w;\n const py = (parentIdx / w) | 0;\n const dx = clampDir(cx - px);\n const dy = clampDir(cy - py);\n\n if (dx !== 0 && dy !== 0) {\n // Diagonal move: check straight and diagonal successors\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy + dy)) {\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n // Forced neighbors\n if (!isWalkable(grid, cx - dx, cy) && isWalkable(grid, cx - dx, cy + dy)) {\n const jp = jump(grid, cx, cy, -dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - dy) && isWalkable(grid, cx + dx, cy - dy)) {\n const jp = jump(grid, cx, cy, dx, -dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else if (dx !== 0) {\n // Horizontal move\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy + 1) && isWalkable(grid, cx + dx, cy + 1)) {\n const jp = jump(grid, cx, cy, dx, 1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - 1) && isWalkable(grid, cx + dx, cy - 1)) {\n const jp = jump(grid, cx, cy, dx, -1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else {\n // Vertical move\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx + 1, cy) && isWalkable(grid, cx + 1, cy + dy)) {\n const jp = jump(grid, cx, cy, 1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx - 1, cy) && isWalkable(grid, cx - 1, cy + dy)) {\n const jp = jump(grid, cx, cy, -1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n\n return results;\n}\n\nfunction jump(\n grid: Grid,\n cx: number,\n cy: number,\n dx: number,\n dy: number,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number {\n const nx = cx + dx;\n const ny = cy + dy;\n\n if (!isWalkable(grid, nx, ny)) return -1;\n if (nx === ex && ny === ey) return ny * w + nx;\n\n // Forced neighbor check\n if (dx !== 0 && dy !== 0) {\n // Diagonal\n if (\n (!isWalkable(grid, nx - dx, ny) && isWalkable(grid, nx - dx, ny + dy)) ||\n (!isWalkable(grid, nx, ny - dy) && isWalkable(grid, nx + dx, ny - dy))\n ) {\n return ny * w + nx;\n }\n // Recurse in cardinal directions\n if (jump(grid, nx, ny, dx, 0, w, h, ex, ey) !== -1) return ny * w + nx;\n if (jump(grid, nx, ny, 0, dy, w, h, ex, ey) !== -1) return ny * w + nx;\n } else if (dx !== 0) {\n // Horizontal\n if (\n (!isWalkable(grid, nx, ny + 1) && isWalkable(grid, nx + dx, ny + 1)) ||\n (!isWalkable(grid, nx, ny - 1) && isWalkable(grid, nx + dx, ny - 1))\n ) {\n return ny * w + nx;\n }\n } else {\n // Vertical\n if (\n (!isWalkable(grid, nx + 1, ny) && isWalkable(grid, nx + 1, ny + dy)) ||\n (!isWalkable(grid, nx - 1, ny) && isWalkable(grid, nx - 1, ny + dy))\n ) {\n return ny * w + nx;\n }\n }\n\n // Keep jumping (iterative for cardinal, recursive for diagonal via identifySuccessors)\n return jump(grid, nx, ny, dx, dy, w, h, ex, ey);\n}\n\nfunction clampDir(v: number): number {\n return v > 0 ? 1 : v < 0 ? -1 : 0;\n}\n\nfunction buildPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n // JPS produces jump points — we interpolate the full path\n const jumpPoints: number[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n jumpPoints.push(idx);\n idx = cameFrom[idx]!;\n }\n jumpPoints.reverse();\n\n const path: { x: number; y: number }[] = [];\n for (let i = 0; i < jumpPoints.length; i++) {\n const jpIdx = jumpPoints[i]!;\n const jx = jpIdx % w;\n const jy = (jpIdx / w) | 0;\n if (i === 0) {\n path.push({ x: jx, y: jy });\n } else {\n const prev = path[path.length - 1]!;\n // Interpolate from prev to current jump point\n let cx = prev.x;\n let cy = prev.y;\n while (cx !== jx || cy !== jy) {\n if (cx < jx) cx++;\n else if (cx > jx) cx--;\n if (cy < jy) cy++;\n else if (cy > jy) cy--;\n path.push({ x: cx, y: cy });\n }\n }\n }\n\n return { path, cost, explored };\n}\n","/**\n * Lightweight navmesh pathfinding for polygon-based environments.\n * Supports convex polygon navigation with A* over the polygon graph\n * and the Simple Stupid Funnel Algorithm for path smoothing.\n */\n\nimport type { Vec2 } from '@flare-engine/math';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface NavMeshPolygon {\n readonly id: number;\n /** Convex polygon vertices in winding order. */\n readonly vertices: readonly Vec2[];\n /** Ids of neighboring polygons (sharing an edge). */\n readonly neighbors: readonly number[];\n /** Shared edge per neighbor: [neighborId] → { a, b } portal endpoints. */\n readonly portals: readonly Portal[];\n}\n\nexport interface Portal {\n /** Neighbor polygon id. */\n neighborId: number;\n /** Left endpoint of the shared edge (from the perspective of travel). */\n a: Vec2;\n /** Right endpoint of the shared edge. */\n b: Vec2;\n}\n\nexport interface NavMesh {\n readonly polygons: readonly NavMeshPolygon[];\n}\n\nexport interface NavMeshPathResult {\n /** Smoothed world-space waypoints. */\n path: Vec2[];\n /** Polygon ids traversed. */\n corridor: number[];\n}\n\n// ─── Construction ────────────────────────────────────────────────\n\nexport function createNavMesh(polygons: NavMeshPolygon[]): NavMesh {\n return { polygons };\n}\n\n// ─── Point-in-polygon ────────────────────────────────────────────\n\nfunction pointInConvexPolygon(verts: readonly Vec2[], px: number, py: number): boolean {\n const n = verts.length;\n if (n < 3) return false;\n let sign = 0;\n for (let i = 0; i < n; i++) {\n const a = verts[i]!;\n const b = verts[(i + 1) % n]!;\n const cross = (b.x - a.x) * (py - a.y) - (b.y - a.y) * (px - a.x);\n if (cross !== 0) {\n const s = cross > 0 ? 1 : -1;\n if (sign === 0) sign = s;\n else if (sign !== s) return false;\n }\n }\n return true;\n}\n\nexport function findPolygonAt(mesh: NavMesh, x: number, y: number): number {\n for (let i = 0; i < mesh.polygons.length; i++) {\n if (pointInConvexPolygon(mesh.polygons[i]!.vertices, x, y)) {\n return mesh.polygons[i]!.id;\n }\n }\n return -1;\n}\n\n// ─── Polygon centroid ────────────────────────────────────────────\n\nfunction centroid(verts: readonly Vec2[]): Vec2 {\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < verts.length; i++) {\n cx += verts[i]!.x;\n cy += verts[i]!.y;\n }\n const n = verts.length;\n return { x: cx / n, y: cy / n };\n}\n\n// ─── A* over polygon graph ───────────────────────────────────────\n\nfunction distSq(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return dx * dx + dy * dy;\n}\n\nfunction dist(ax: number, ay: number, bx: number, by: number): number {\n return Math.sqrt(distSq(ax, ay, bx, by));\n}\n\nexport function findNavMeshPath(mesh: NavMesh, start: Vec2, end: Vec2): NavMeshPathResult | null {\n const startPolyId = findPolygonAt(mesh, start.x, start.y);\n const endPolyId = findPolygonAt(mesh, end.x, end.y);\n if (startPolyId === -1 || endPolyId === -1) return null;\n if (startPolyId === endPolyId) {\n return {\n path: [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ],\n corridor: [startPolyId],\n };\n }\n\n // Build index: id → polygon\n const byId = new Map<number, NavMeshPolygon>();\n for (let i = 0; i < mesh.polygons.length; i++) {\n const p = mesh.polygons[i]!;\n byId.set(p.id, p);\n }\n\n // A* on polygon graph\n const gScore = new Map<number, number>();\n const fScore = new Map<number, number>();\n const cameFrom = new Map<number, number>();\n\n gScore.set(startPolyId, 0);\n\n const endPoly = byId.get(endPolyId)!;\n const endCenter = centroid(endPoly.vertices);\n const startPoly = byId.get(startPolyId)!;\n const startCenter = centroid(startPoly.vertices);\n\n fScore.set(startPolyId, dist(startCenter.x, startCenter.y, endCenter.x, endCenter.y));\n\n // Simple array-based open set (fine for typical navmeshes with <1000 polygons)\n const open: number[] = [startPolyId];\n const inOpen = new Set<number>([startPolyId]);\n const closed = new Set<number>();\n\n while (open.length > 0) {\n // Find lowest f-score\n let bestI = 0;\n let bestF = fScore.get(open[0]!) ?? Number.POSITIVE_INFINITY;\n for (let i = 1; i < open.length; i++) {\n const f = fScore.get(open[i]!) ?? Number.POSITIVE_INFINITY;\n if (f < bestF) {\n bestF = f;\n bestI = i;\n }\n }\n const currentId = open[bestI]!;\n\n if (currentId === endPolyId) {\n // Reconstruct corridor\n const corridor: number[] = [];\n let id: number | undefined = endPolyId;\n while (id !== undefined) {\n corridor.push(id);\n id = cameFrom.get(id);\n }\n corridor.reverse();\n const path = funnelSmooth(mesh, byId, corridor, start, end);\n return { path, corridor };\n }\n\n // Remove from open\n open[bestI] = open[open.length - 1]!;\n open.length--;\n inOpen.delete(currentId);\n closed.add(currentId);\n\n const current = byId.get(currentId)!;\n const currentCenter = centroid(current.vertices);\n const currentG = gScore.get(currentId)!;\n\n for (let i = 0; i < current.portals.length; i++) {\n const portal = current.portals[i]!;\n const neighborId = portal.neighborId;\n if (closed.has(neighborId)) continue;\n\n const neighbor = byId.get(neighborId);\n if (!neighbor) continue;\n\n const portalMidX = (portal.a.x + portal.b.x) * 0.5;\n const portalMidY = (portal.a.y + portal.b.y) * 0.5;\n const tentativeG = currentG + dist(currentCenter.x, currentCenter.y, portalMidX, portalMidY);\n\n const prevG = gScore.get(neighborId);\n if (prevG !== undefined && tentativeG >= prevG) continue;\n\n gScore.set(neighborId, tentativeG);\n cameFrom.set(neighborId, currentId);\n\n const neighborCenter = centroid(neighbor.vertices);\n fScore.set(\n neighborId,\n tentativeG + dist(neighborCenter.x, neighborCenter.y, endCenter.x, endCenter.y),\n );\n\n if (!inOpen.has(neighborId)) {\n open.push(neighborId);\n inOpen.add(neighborId);\n }\n }\n }\n\n return null;\n}\n\n// ─── Simple Stupid Funnel Algorithm ──────────────────────────────\n\nfunction funnelSmooth(\n _mesh: NavMesh,\n byId: Map<number, NavMeshPolygon>,\n corridor: number[],\n start: Vec2,\n end: Vec2,\n): Vec2[] {\n if (corridor.length <= 1) {\n return [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ];\n }\n\n // Build portal list from corridor\n const portalsLeft: Vec2[] = [];\n const portalsRight: Vec2[] = [];\n\n portalsLeft.push({ x: start.x, y: start.y });\n portalsRight.push({ x: start.x, y: start.y });\n\n for (let i = 0; i < corridor.length - 1; i++) {\n const poly = byId.get(corridor[i]!)!;\n const nextId = corridor[i + 1]!;\n // Find the portal to the next polygon\n let found = false;\n for (let j = 0; j < poly.portals.length; j++) {\n if (poly.portals[j]!.neighborId === nextId) {\n portalsLeft.push(poly.portals[j]!.a);\n portalsRight.push(poly.portals[j]!.b);\n found = true;\n break;\n }\n }\n if (!found) {\n // Fallback: straight line through centroids\n const c = centroid(poly.vertices);\n portalsLeft.push(c);\n portalsRight.push(c);\n }\n }\n\n portalsLeft.push({ x: end.x, y: end.y });\n portalsRight.push({ x: end.x, y: end.y });\n\n // Simple Stupid Funnel Algorithm (Mononen)\n // Walk the portal list and maintain a funnel (apex, left, right).\n const path: Vec2[] = [];\n\n let apexPt = portalsLeft[0]!; // start point\n let leftPt = portalsLeft[0]!;\n let rightPt = portalsRight[0]!;\n let apexIdx = 0;\n let leftIdx = 0;\n let rightIdx = 0;\n\n path.push({ x: apexPt.x, y: apexPt.y });\n\n const n = portalsLeft.length;\n\n for (let i = 1; i < n; i++) {\n const pL = portalsLeft[i]!;\n const pR = portalsRight[i]!;\n\n // Try to tighten the right side\n if (triArea2(apexPt, rightPt, pR) <= 0) {\n if (vEqual(apexPt, rightPt) || triArea2(apexPt, leftPt, pR) > 0) {\n // Tighten\n rightPt = pR;\n rightIdx = i;\n } else {\n // Right crossed over left → left becomes new apex\n addUnique(path, leftPt);\n apexPt = leftPt;\n apexIdx = leftIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n continue;\n }\n }\n\n // Try to tighten the left side\n if (triArea2(apexPt, leftPt, pL) >= 0) {\n if (vEqual(apexPt, leftPt) || triArea2(apexPt, rightPt, pL) < 0) {\n // Tighten\n leftPt = pL;\n leftIdx = i;\n } else {\n // Left crossed over right → right becomes new apex\n addUnique(path, rightPt);\n apexPt = rightPt;\n apexIdx = rightIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n }\n }\n }\n\n addUnique(path, end);\n\n return path;\n}\n\nfunction addUnique(path: Vec2[], p: Vec2): void {\n const last = path[path.length - 1];\n if (!last || last.x !== p.x || last.y !== p.y) {\n path.push({ x: p.x, y: p.y });\n }\n}\n\n/** Twice the signed area of the triangle (a, b, c). Positive = CCW. */\nfunction triArea2(a: Vec2, b: Vec2, c: Vec2): number {\n return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);\n}\n\nfunction vEqual(a: Vec2, b: Vec2): boolean {\n return a.x === b.x && a.y === b.y;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["SQRT2","heapCreate","heapGrow","heapPush","heapPop"],"sources":["../src/grid.ts","../src/astar.ts","../src/jps.ts","../src/navmesh.ts"],"sourcesContent":["/**\n * Grid data structure for tile-based pathfinding.\n * Uses a flat Uint8Array: 0 = blocked, 1+ = walkable (value = movement cost).\n */\n\nexport interface Grid {\n readonly width: number;\n readonly height: number;\n readonly data: Uint8Array;\n}\n\nexport interface GridConfig {\n width: number;\n height: number;\n /** Pre-filled grid data. Length must equal width * height. */\n data?: Uint8Array;\n /** Default cost for all cells when `data` is not provided. 0 = blocked, 1+ = walkable cost. Default 1. */\n defaultCost?: number;\n}\n\nexport function createGrid(config: GridConfig): Grid {\n const { width, height, defaultCost = 1 } = config;\n if (width <= 0 || height <= 0) {\n throw new RangeError('Grid dimensions must be positive');\n }\n const w = width | 0;\n const h = height | 0;\n let data: Uint8Array;\n if (config.data) {\n if (config.data.length !== w * h) {\n throw new RangeError(`Grid data length ${config.data.length} does not match ${w}x${h}`);\n }\n data = config.data;\n } else {\n data = new Uint8Array(w * h);\n if (defaultCost > 0) {\n data.fill(defaultCost);\n }\n }\n return { width: w, height: h, data };\n}\n\nexport function inBounds(grid: Grid, x: number, y: number): boolean {\n return x >= 0 && y >= 0 && x < grid.width && y < grid.height;\n}\n\nexport function isWalkable(grid: Grid, x: number, y: number): boolean {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return false;\n return grid.data[(y | 0) * grid.width + (x | 0)]! > 0;\n}\n\nexport function getCost(grid: Grid, x: number, y: number): number {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return 0;\n return grid.data[(y | 0) * grid.width + (x | 0)]!;\n}\n\nexport function setWalkable(grid: Grid, x: number, y: number, walkable: boolean): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = walkable ? 1 : 0;\n}\n\nexport function setCost(grid: Grid, x: number, y: number, cost: number): void {\n if (x < 0 || y < 0 || x >= grid.width || y >= grid.height) return;\n grid.data[(y | 0) * grid.width + (x | 0)] = cost > 255 ? 255 : cost < 0 ? 0 : cost | 0;\n}\n","/**\n * A* pathfinding on a Grid. Supports 4-directional and 8-directional movement\n * with weighted costs. Uses a binary min-heap for the open set.\n */\n\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\nexport interface PathResult {\n /** Grid coordinates from start to end, inclusive. */\n path: { x: number; y: number }[];\n /** Total movement cost. */\n cost: number;\n /** Number of nodes explored. */\n explored: number;\n}\n\nexport interface PathOptions {\n /** Allow diagonal movement. Default: true. */\n diagonal?: boolean;\n /** Custom heuristic. Default: octile distance (diagonal) or Manhattan (4-dir). */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n}\n\n// ─── Heuristics ──────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\nexport function manhattanDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.abs(ax - bx) + Math.abs(ay - by);\n}\n\nexport function octileDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = Math.abs(ax - bx);\n const dy = Math.abs(ay - by);\n return dx > dy ? dx + (SQRT2 - 1) * dy : dy + (SQRT2 - 1) * dx;\n}\n\nexport function chebyshevDistance(ax: number, ay: number, bx: number, by: number): number {\n return Math.max(Math.abs(ax - bx), Math.abs(ay - by));\n}\n\nexport function euclideanDistance(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n// ─── Binary min-heap ─────────────────────────────────────────────\n\n// Indices into the heap are stored as flat integers; each entry is (f, node-index).\n// We use two parallel Float64Arrays to avoid object allocation.\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\n/**\n * Double the heap's backing arrays.\n *\n * Typed arrays do not throw on an out-of-range write — the store is silently\n * dropped — so without this a push past capacity increments `size`, writes\n * nothing, and every later comparison reads `undefined`. The open set then\n * degrades without any error, and the search returns a valid-looking but\n * non-optimal path. Growth is amortized O(1): allocation happens only when\n * capacity is reached, never per push.\n */\nfunction heapGrow(h: Heap): void {\n const next = h.f.length === 0 ? 16 : h.f.length * 2;\n const f = new Float64Array(next);\n f.set(h.f);\n const idx = new Int32Array(next);\n idx.set(h.idx);\n h.f = f;\n h.idx = idx;\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n if (h.size === h.f.length) heapGrow(h);\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n // Bubble up\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n // Swap\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n // Sift down\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── Neighbors (pre-computed offsets) ────────────────────────────\n\n// 4-directional: N, E, S, W\nconst DIR4_X = [0, 1, 0, -1];\nconst DIR4_Y = [-1, 0, 1, 0];\n\n// 8-directional: N, NE, E, SE, S, SW, W, NW\nconst DIR8_X = [0, 1, 1, 1, 0, -1, -1, -1];\nconst DIR8_Y = [-1, -1, 0, 1, 1, 1, 0, -1];\nconst DIR8_COST = [1, SQRT2, 1, SQRT2, 1, SQRT2, 1, SQRT2];\n\n// ─── A* ──────────────────────────────────────────────────────────\n\nexport function findPath(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: PathOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n // Quick rejects\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n\n const diagonal = options?.diagonal !== false;\n const maxIter = options?.maxIterations ?? grid.width * grid.height;\n const heuristic = options?.heuristic ?? (diagonal ? octileDistance : manhattanDistance);\n\n const w = grid.width;\n const total = w * grid.height;\n\n // gScore and closed set\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n const dirX = diagonal ? DIR8_X : DIR4_X;\n const dirY = diagonal ? DIR8_Y : DIR4_Y;\n const dirCost = diagonal ? DIR8_COST : null;\n const dirCount = dirX.length;\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return reconstructPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n const currentG = g[currentIdx]!;\n\n for (let d = 0; d < dirCount; d++) {\n const nx = cx + dirX[d]!;\n const ny = cy + dirY[d]!;\n if (nx < 0 || ny < 0 || nx >= w || ny >= grid.height) continue;\n\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const tileCost = grid.data[nIdx]!;\n if (tileCost === 0) continue;\n\n // For diagonal movement, check that both cardinal neighbors are walkable (corner-cutting)\n if (diagonal && d & 1) {\n const dx = dirX[d]!;\n const dy = dirY[d]!;\n if (!isWalkable(grid, cx + dx, cy) || !isWalkable(grid, cx, cy + dy)) continue;\n }\n\n const moveCost = dirCost ? dirCost[d]! * tileCost : tileCost;\n const tentativeG = currentG + moveCost;\n\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n const f = tentativeG + heuristic(nx, ny, ex, ey);\n heapPush(heap, f, nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction reconstructPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n const path: { x: number; y: number }[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n path.push({ x: idx % w, y: (idx / w) | 0 });\n idx = cameFrom[idx]!;\n }\n path.reverse();\n return { path, cost, explored };\n}\n","/**\n * Jump Point Search — an optimization over A* for uniform-cost grids.\n * JPS pruning is only correct when every walkable tile costs 1, so a grid with\n * any tile cost above 1 is delegated to weighted A* (`findPath`, diagonal moves)\n * after one linear scan of `grid.data`.\n *\n * JPS prunes symmetric paths by \"jumping\" along directions until hitting\n * a wall, the goal, or a forced neighbor.\n */\n\nimport type { PathResult } from './astar.js';\nimport { findPath, octileDistance } from './astar.js';\nimport type { Grid } from './grid.js';\nimport { isWalkable } from './grid.js';\n\n// ─── Binary min-heap (duplicated from astar for tree-shakeability) ─\n\ninterface Heap {\n f: Float64Array;\n idx: Int32Array;\n size: number;\n}\n\nfunction heapCreate(capacity: number): Heap {\n return {\n f: new Float64Array(capacity),\n idx: new Int32Array(capacity),\n size: 0,\n };\n}\n\n/**\n * Double the heap's backing arrays.\n *\n * Typed arrays do not throw on an out-of-range write — the store is silently\n * dropped — so without this a push past capacity increments `size`, writes\n * nothing, and every later comparison reads `undefined`. The open set then\n * degrades without any error, and the search returns a valid-looking but\n * non-optimal path. Growth is amortized O(1): allocation happens only when\n * capacity is reached, never per push.\n */\nfunction heapGrow(h: Heap): void {\n const next = h.f.length === 0 ? 16 : h.f.length * 2;\n const f = new Float64Array(next);\n f.set(h.f);\n const idx = new Int32Array(next);\n idx.set(h.idx);\n h.f = f;\n h.idx = idx;\n}\n\nfunction heapPush(h: Heap, f: number, nodeIdx: number): void {\n if (h.size === h.f.length) heapGrow(h);\n let i = h.size++;\n h.f[i] = f;\n h.idx[i] = nodeIdx;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n if (h.f[parent]! <= h.f[i]!) break;\n const tf = h.f[parent]!;\n const ti = h.idx[parent]!;\n h.f[parent] = h.f[i]!;\n h.idx[parent] = h.idx[i]!;\n h.f[i] = tf;\n h.idx[i] = ti;\n i = parent;\n }\n}\n\nfunction heapPop(h: Heap): number {\n const result = h.idx[0]!;\n h.size--;\n if (h.size > 0) {\n h.f[0] = h.f[h.size]!;\n h.idx[0] = h.idx[h.size]!;\n let i = 0;\n for (;;) {\n let smallest = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n if (left < h.size && h.f[left]! < h.f[smallest]!) smallest = left;\n if (right < h.size && h.f[right]! < h.f[smallest]!) smallest = right;\n if (smallest === i) break;\n const tf = h.f[i]!;\n const ti = h.idx[i]!;\n h.f[i] = h.f[smallest]!;\n h.idx[i] = h.idx[smallest]!;\n h.f[smallest] = tf;\n h.idx[smallest] = ti;\n i = smallest;\n }\n }\n return result;\n}\n\n// ─── JPS ─────────────────────────────────────────────────────────\n\nconst SQRT2 = Math.SQRT2;\n\n// One pass per call, never per node: JPS pruning assumes every walkable tile costs 1.\nfunction hasWeightedCost(grid: Grid): boolean {\n const data = grid.data;\n for (let i = 0; i < data.length; i++) {\n if (data[i]! > 1) return true;\n }\n return false;\n}\n\nexport interface JpsOptions {\n /** Max nodes to explore before giving up. Default: width * height. */\n maxIterations?: number;\n /** Custom heuristic. Default: octile distance. */\n heuristic?: (ax: number, ay: number, bx: number, by: number) => number;\n}\n\nexport function findPathJPS(\n grid: Grid,\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n options?: JpsOptions,\n): PathResult | null {\n const sx = startX | 0;\n const sy = startY | 0;\n const ex = endX | 0;\n const ey = endY | 0;\n\n if (!isWalkable(grid, sx, sy) || !isWalkable(grid, ex, ey)) return null;\n if (sx === ex && sy === ey) return { path: [{ x: sx, y: sy }], cost: 0, explored: 0 };\n if (hasWeightedCost(grid)) {\n return findPath(grid, sx, sy, ex, ey, { ...options, diagonal: true });\n }\n\n const w = grid.width;\n const h = grid.height;\n const total = w * h;\n const maxIter = options?.maxIterations ?? total;\n const heuristic = options?.heuristic ?? octileDistance;\n\n const g = new Float64Array(total);\n g.fill(Number.POSITIVE_INFINITY);\n const closed = new Uint8Array(total);\n const cameFrom = new Int32Array(total);\n cameFrom.fill(-1);\n\n const startIdx = sy * w + sx;\n const endIdx = ey * w + ex;\n g[startIdx] = 0;\n\n const heap = heapCreate(Math.min(total, 4096));\n heapPush(heap, heuristic(sx, sy, ex, ey), startIdx);\n\n let explored = 0;\n\n while (heap.size > 0 && explored < maxIter) {\n const currentIdx = heapPop(heap);\n if (currentIdx === endIdx) {\n return buildPath(cameFrom, w, currentIdx, g[currentIdx]!, explored);\n }\n if (closed[currentIdx]) continue;\n closed[currentIdx] = 1;\n explored++;\n\n const cx = currentIdx % w;\n const cy = (currentIdx / w) | 0;\n\n const neighbors = identifySuccessors(grid, cx, cy, cameFrom, w, h, ex, ey);\n for (let i = 0; i < neighbors.length; i += 2) {\n const nx = neighbors[i]!;\n const ny = neighbors[i + 1]!;\n const nIdx = ny * w + nx;\n if (closed[nIdx]) continue;\n\n const dx = Math.abs(nx - cx);\n const dy = Math.abs(ny - cy);\n const diag = Math.min(dx, dy);\n const straight = Math.abs(dx - dy);\n const dist = diag * SQRT2 + straight;\n const tentativeG = g[currentIdx]! + dist;\n if (tentativeG < g[nIdx]!) {\n g[nIdx] = tentativeG;\n cameFrom[nIdx] = currentIdx;\n heapPush(heap, tentativeG + heuristic(nx, ny, ex, ey), nIdx);\n }\n }\n }\n\n return null;\n}\n\nfunction identifySuccessors(\n grid: Grid,\n cx: number,\n cy: number,\n cameFrom: Int32Array,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number[] {\n const results: number[] = [];\n const parentIdx = cameFrom[cy * w + cx]!;\n\n if (parentIdx === -1) {\n // Start node: explore all 8 directions\n for (let dx = -1; dx <= 1; dx++) {\n for (let dy = -1; dy <= 1; dy++) {\n if (dx === 0 && dy === 0) continue;\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) {\n results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n } else {\n const px = parentIdx % w;\n const py = (parentIdx / w) | 0;\n const dx = clampDir(cx - px);\n const dy = clampDir(cy - py);\n\n if (dx !== 0 && dy !== 0) {\n // Diagonal move: check straight and diagonal successors\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (isWalkable(grid, cx + dx, cy + dy)) {\n const jp = jump(grid, cx, cy, dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n // Forced neighbors\n if (!isWalkable(grid, cx - dx, cy) && isWalkable(grid, cx - dx, cy + dy)) {\n const jp = jump(grid, cx, cy, -dx, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - dy) && isWalkable(grid, cx + dx, cy - dy)) {\n const jp = jump(grid, cx, cy, dx, -dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else if (dx !== 0) {\n // Horizontal move\n if (isWalkable(grid, cx + dx, cy)) {\n const jp = jump(grid, cx, cy, dx, 0, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy + 1) && isWalkable(grid, cx + dx, cy + 1)) {\n const jp = jump(grid, cx, cy, dx, 1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx, cy - 1) && isWalkable(grid, cx + dx, cy - 1)) {\n const jp = jump(grid, cx, cy, dx, -1, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n } else {\n // Vertical move\n if (isWalkable(grid, cx, cy + dy)) {\n const jp = jump(grid, cx, cy, 0, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx + 1, cy) && isWalkable(grid, cx + 1, cy + dy)) {\n const jp = jump(grid, cx, cy, 1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n if (!isWalkable(grid, cx - 1, cy) && isWalkable(grid, cx - 1, cy + dy)) {\n const jp = jump(grid, cx, cy, -1, dy, w, h, ex, ey);\n if (jp !== -1) results.push(jp % w, (jp / w) | 0);\n }\n }\n }\n\n return results;\n}\n\nfunction jump(\n grid: Grid,\n cx: number,\n cy: number,\n dx: number,\n dy: number,\n w: number,\n h: number,\n ex: number,\n ey: number,\n): number {\n const nx = cx + dx;\n const ny = cy + dy;\n\n if (!isWalkable(grid, nx, ny)) return -1;\n if (nx === ex && ny === ey) return ny * w + nx;\n\n // Forced neighbor check\n if (dx !== 0 && dy !== 0) {\n // Diagonal\n if (\n (!isWalkable(grid, nx - dx, ny) && isWalkable(grid, nx - dx, ny + dy)) ||\n (!isWalkable(grid, nx, ny - dy) && isWalkable(grid, nx + dx, ny - dy))\n ) {\n return ny * w + nx;\n }\n // Recurse in cardinal directions\n if (jump(grid, nx, ny, dx, 0, w, h, ex, ey) !== -1) return ny * w + nx;\n if (jump(grid, nx, ny, 0, dy, w, h, ex, ey) !== -1) return ny * w + nx;\n } else if (dx !== 0) {\n // Horizontal\n if (\n (!isWalkable(grid, nx, ny + 1) && isWalkable(grid, nx + dx, ny + 1)) ||\n (!isWalkable(grid, nx, ny - 1) && isWalkable(grid, nx + dx, ny - 1))\n ) {\n return ny * w + nx;\n }\n } else {\n // Vertical\n if (\n (!isWalkable(grid, nx + 1, ny) && isWalkable(grid, nx + 1, ny + dy)) ||\n (!isWalkable(grid, nx - 1, ny) && isWalkable(grid, nx - 1, ny + dy))\n ) {\n return ny * w + nx;\n }\n }\n\n // Keep jumping (iterative for cardinal, recursive for diagonal via identifySuccessors)\n return jump(grid, nx, ny, dx, dy, w, h, ex, ey);\n}\n\nfunction clampDir(v: number): number {\n return v > 0 ? 1 : v < 0 ? -1 : 0;\n}\n\nfunction buildPath(\n cameFrom: Int32Array,\n w: number,\n endIdx: number,\n cost: number,\n explored: number,\n): PathResult {\n // JPS produces jump points — we interpolate the full path\n const jumpPoints: number[] = [];\n let idx = endIdx;\n while (idx !== -1) {\n jumpPoints.push(idx);\n idx = cameFrom[idx]!;\n }\n jumpPoints.reverse();\n\n const path: { x: number; y: number }[] = [];\n for (let i = 0; i < jumpPoints.length; i++) {\n const jpIdx = jumpPoints[i]!;\n const jx = jpIdx % w;\n const jy = (jpIdx / w) | 0;\n if (i === 0) {\n path.push({ x: jx, y: jy });\n } else {\n const prev = path[path.length - 1]!;\n // Interpolate from prev to current jump point\n let cx = prev.x;\n let cy = prev.y;\n while (cx !== jx || cy !== jy) {\n if (cx < jx) cx++;\n else if (cx > jx) cx--;\n if (cy < jy) cy++;\n else if (cy > jy) cy--;\n path.push({ x: cx, y: cy });\n }\n }\n }\n\n return { path, cost, explored };\n}\n","/**\n * Lightweight navmesh pathfinding for polygon-based environments.\n * Supports convex polygon navigation with A* over the polygon graph\n * and the Simple Stupid Funnel Algorithm for path smoothing.\n */\n\nimport type { Vec2 } from '@flare-engine/math';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface NavMeshPolygon {\n readonly id: number;\n /** Convex polygon vertices in winding order. */\n readonly vertices: readonly Vec2[];\n /** One `Portal` per adjacent polygon: the shared edge plus that neighbour's id. */\n readonly portals: readonly Portal[];\n}\n\nexport interface Portal {\n /** Neighbor polygon id. */\n neighborId: number;\n /** Left endpoint of the shared edge (from the perspective of travel). */\n a: Vec2;\n /** Right endpoint of the shared edge. */\n b: Vec2;\n}\n\nexport interface NavMesh {\n readonly polygons: readonly NavMeshPolygon[];\n}\n\nexport interface NavMeshPathResult {\n /** Smoothed world-space waypoints. */\n path: Vec2[];\n /** Polygon ids traversed. */\n corridor: number[];\n}\n\n// ─── Construction ────────────────────────────────────────────────\n\nexport function createNavMesh(polygons: NavMeshPolygon[]): NavMesh {\n return { polygons };\n}\n\n// ─── Point-in-polygon ────────────────────────────────────────────\n\nfunction pointInConvexPolygon(verts: readonly Vec2[], px: number, py: number): boolean {\n const n = verts.length;\n if (n < 3) return false;\n let sign = 0;\n for (let i = 0; i < n; i++) {\n const a = verts[i]!;\n const b = verts[(i + 1) % n]!;\n const cross = (b.x - a.x) * (py - a.y) - (b.y - a.y) * (px - a.x);\n if (cross !== 0) {\n const s = cross > 0 ? 1 : -1;\n if (sign === 0) sign = s;\n else if (sign !== s) return false;\n }\n }\n return true;\n}\n\nexport function findPolygonAt(mesh: NavMesh, x: number, y: number): number {\n for (let i = 0; i < mesh.polygons.length; i++) {\n if (pointInConvexPolygon(mesh.polygons[i]!.vertices, x, y)) {\n return mesh.polygons[i]!.id;\n }\n }\n return -1;\n}\n\n// ─── Polygon centroid ────────────────────────────────────────────\n\nfunction centroid(verts: readonly Vec2[]): Vec2 {\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < verts.length; i++) {\n cx += verts[i]!.x;\n cy += verts[i]!.y;\n }\n const n = verts.length;\n return { x: cx / n, y: cy / n };\n}\n\n// ─── A* over polygon graph ───────────────────────────────────────\n\nfunction distSq(ax: number, ay: number, bx: number, by: number): number {\n const dx = ax - bx;\n const dy = ay - by;\n return dx * dx + dy * dy;\n}\n\nfunction dist(ax: number, ay: number, bx: number, by: number): number {\n return Math.sqrt(distSq(ax, ay, bx, by));\n}\n\nexport function findNavMeshPath(mesh: NavMesh, start: Vec2, end: Vec2): NavMeshPathResult | null {\n const startPolyId = findPolygonAt(mesh, start.x, start.y);\n const endPolyId = findPolygonAt(mesh, end.x, end.y);\n if (startPolyId === -1 || endPolyId === -1) return null;\n if (startPolyId === endPolyId) {\n return {\n path: [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ],\n corridor: [startPolyId],\n };\n }\n\n // Build index: id → polygon\n const byId = new Map<number, NavMeshPolygon>();\n for (let i = 0; i < mesh.polygons.length; i++) {\n const p = mesh.polygons[i]!;\n byId.set(p.id, p);\n }\n\n // A* on polygon graph\n const gScore = new Map<number, number>();\n const fScore = new Map<number, number>();\n const cameFrom = new Map<number, number>();\n\n gScore.set(startPolyId, 0);\n\n const endPoly = byId.get(endPolyId)!;\n const endCenter = centroid(endPoly.vertices);\n const startPoly = byId.get(startPolyId)!;\n const startCenter = centroid(startPoly.vertices);\n\n fScore.set(startPolyId, dist(startCenter.x, startCenter.y, endCenter.x, endCenter.y));\n\n // Simple array-based open set (fine for typical navmeshes with <1000 polygons)\n const open: number[] = [startPolyId];\n const inOpen = new Set<number>([startPolyId]);\n const closed = new Set<number>();\n\n while (open.length > 0) {\n // Find lowest f-score\n let bestI = 0;\n let bestF = fScore.get(open[0]!) ?? Number.POSITIVE_INFINITY;\n for (let i = 1; i < open.length; i++) {\n const f = fScore.get(open[i]!) ?? Number.POSITIVE_INFINITY;\n if (f < bestF) {\n bestF = f;\n bestI = i;\n }\n }\n const currentId = open[bestI]!;\n\n if (currentId === endPolyId) {\n // Reconstruct corridor\n const corridor: number[] = [];\n let id: number | undefined = endPolyId;\n while (id !== undefined) {\n corridor.push(id);\n id = cameFrom.get(id);\n }\n corridor.reverse();\n const path = funnelSmooth(mesh, byId, corridor, start, end);\n return { path, corridor };\n }\n\n // Remove from open\n open[bestI] = open[open.length - 1]!;\n open.length--;\n inOpen.delete(currentId);\n closed.add(currentId);\n\n const current = byId.get(currentId)!;\n const currentCenter = centroid(current.vertices);\n const currentG = gScore.get(currentId)!;\n\n for (let i = 0; i < current.portals.length; i++) {\n const portal = current.portals[i]!;\n const neighborId = portal.neighborId;\n if (closed.has(neighborId)) continue;\n\n const neighbor = byId.get(neighborId);\n if (!neighbor) continue;\n\n const portalMidX = (portal.a.x + portal.b.x) * 0.5;\n const portalMidY = (portal.a.y + portal.b.y) * 0.5;\n const tentativeG = currentG + dist(currentCenter.x, currentCenter.y, portalMidX, portalMidY);\n\n const prevG = gScore.get(neighborId);\n if (prevG !== undefined && tentativeG >= prevG) continue;\n\n gScore.set(neighborId, tentativeG);\n cameFrom.set(neighborId, currentId);\n\n const neighborCenter = centroid(neighbor.vertices);\n fScore.set(\n neighborId,\n tentativeG + dist(neighborCenter.x, neighborCenter.y, endCenter.x, endCenter.y),\n );\n\n if (!inOpen.has(neighborId)) {\n open.push(neighborId);\n inOpen.add(neighborId);\n }\n }\n }\n\n return null;\n}\n\n// ─── Simple Stupid Funnel Algorithm ──────────────────────────────\n\nfunction funnelSmooth(\n _mesh: NavMesh,\n byId: Map<number, NavMeshPolygon>,\n corridor: number[],\n start: Vec2,\n end: Vec2,\n): Vec2[] {\n if (corridor.length <= 1) {\n return [\n { x: start.x, y: start.y },\n { x: end.x, y: end.y },\n ];\n }\n\n // Build portal list from corridor\n const portalsLeft: Vec2[] = [];\n const portalsRight: Vec2[] = [];\n\n portalsLeft.push({ x: start.x, y: start.y });\n portalsRight.push({ x: start.x, y: start.y });\n\n for (let i = 0; i < corridor.length - 1; i++) {\n const poly = byId.get(corridor[i]!)!;\n const nextId = corridor[i + 1]!;\n // Find the portal to the next polygon\n let found = false;\n for (let j = 0; j < poly.portals.length; j++) {\n if (poly.portals[j]!.neighborId === nextId) {\n portalsLeft.push(poly.portals[j]!.a);\n portalsRight.push(poly.portals[j]!.b);\n found = true;\n break;\n }\n }\n if (!found) {\n // Fallback: straight line through centroids\n const c = centroid(poly.vertices);\n portalsLeft.push(c);\n portalsRight.push(c);\n }\n }\n\n portalsLeft.push({ x: end.x, y: end.y });\n portalsRight.push({ x: end.x, y: end.y });\n\n // Simple Stupid Funnel Algorithm (Mononen)\n // Walk the portal list and maintain a funnel (apex, left, right).\n const path: Vec2[] = [];\n\n let apexPt = portalsLeft[0]!; // start point\n let leftPt = portalsLeft[0]!;\n let rightPt = portalsRight[0]!;\n let apexIdx = 0;\n let leftIdx = 0;\n let rightIdx = 0;\n\n path.push({ x: apexPt.x, y: apexPt.y });\n\n const n = portalsLeft.length;\n\n for (let i = 1; i < n; i++) {\n const pL = portalsLeft[i]!;\n const pR = portalsRight[i]!;\n\n // Try to tighten the right side\n if (triArea2(apexPt, rightPt, pR) <= 0) {\n if (vEqual(apexPt, rightPt) || triArea2(apexPt, leftPt, pR) > 0) {\n // Tighten\n rightPt = pR;\n rightIdx = i;\n } else {\n // Right crossed over left → left becomes new apex\n addUnique(path, leftPt);\n apexPt = leftPt;\n apexIdx = leftIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n continue;\n }\n }\n\n // Try to tighten the left side\n if (triArea2(apexPt, leftPt, pL) >= 0) {\n if (vEqual(apexPt, leftPt) || triArea2(apexPt, rightPt, pL) < 0) {\n // Tighten\n leftPt = pL;\n leftIdx = i;\n } else {\n // Left crossed over right → right becomes new apex\n addUnique(path, rightPt);\n apexPt = rightPt;\n apexIdx = rightIdx;\n // Reset funnel\n leftPt = apexPt;\n rightPt = apexPt;\n leftIdx = apexIdx;\n rightIdx = apexIdx;\n // Restart scan from the portal after the new apex\n i = apexIdx;\n }\n }\n }\n\n addUnique(path, end);\n\n return path;\n}\n\nfunction addUnique(path: Vec2[], p: Vec2): void {\n const last = path[path.length - 1];\n if (!last || last.x !== p.x || last.y !== p.y) {\n path.push({ x: p.x, y: p.y });\n }\n}\n\n/** Twice the signed area of the triangle (a, b, c). Positive = CCW. */\nfunction triArea2(a: Vec2, b: Vec2, c: Vec2): number {\n return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);\n}\n\nfunction vEqual(a: Vec2, b: Vec2): boolean {\n return a.x === b.x && a.y === b.y;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flare-engine/pathfinding",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A* grid pathfinding, Jump Point Search, and navmesh for 2D scenes and apps",
|
|
5
5
|
"keywords": ["flare-engine", "pathfinding", "astar", "jps", "navmesh", "grid"],
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"typecheck": "tsc --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@flare-engine/math": "^0.
|
|
40
|
+
"@flare-engine/math": "^0.4.1"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=20"
|