@xtia/grid 0.0.6 → 0.0.8
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 +7 -7
- package/lib/grid.d.ts +10 -9
- package/lib/grid.js +39 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `Grid`
|
|
2
2
|
|
|
3
|
-
**Alpha**:
|
|
3
|
+
**Alpha**: Pre-0.1.0 API details may change
|
|
4
4
|
|
|
5
5
|
## Summary
|
|
6
6
|
|
|
@@ -11,8 +11,8 @@ A mutable 2D grid with efficient region views, spatial operations, and seamless
|
|
|
11
11
|
- **Mutable 2D storage** with type safety
|
|
12
12
|
- **Zero-copy region views** - edit or provide subgrids and transformation layers without copying
|
|
13
13
|
- **Spatial operations** - fill, paste, flood fill, pathfinding
|
|
14
|
-
- **Pipe2D
|
|
15
|
-
- **Consistent API** - for grids and
|
|
14
|
+
- **First-class Pipe2D interoperability** – use grids as sources for pipes, or pipes as sources for grids
|
|
15
|
+
- **Consistent API** - for grids, regions, and your existing 2D structures
|
|
16
16
|
- **Mask support** - apply operations to arbitrary shapes
|
|
17
17
|
|
|
18
18
|
## Example
|
|
@@ -48,7 +48,7 @@ const grid = Grid.from(source);
|
|
|
48
48
|
|
|
49
49
|
### Wrapping existing structures
|
|
50
50
|
|
|
51
|
-
Use Grid's interface over any read/write 2D structure:
|
|
51
|
+
Use Grid's interface and features over any read/write 2D structure:
|
|
52
52
|
|
|
53
53
|
```ts
|
|
54
54
|
const gameMap = [
|
|
@@ -134,7 +134,7 @@ const mountainCells = world.cells.toFlatArrayXY()
|
|
|
134
134
|
|
|
135
135
|
## Cell interface
|
|
136
136
|
|
|
137
|
-
`grid.cells` provides a Pipe2D, for convenient transformation, of
|
|
137
|
+
`grid.cells` provides a Pipe2D, for convenient transformation, of Cell objects, each representing a live view into a grid location.
|
|
138
138
|
|
|
139
139
|
```ts
|
|
140
140
|
const topLeft = world.cells.get(0, 0);
|
|
@@ -210,9 +210,9 @@ Unlike visibility maps, which are lazily evaluated according to the supplied `is
|
|
|
210
210
|
|
|
211
211
|
`Grid` itself is an interface for reading and writing 2D data. `GridBase` - a subclass of `Grid` - maintains the actual storage of such data.
|
|
212
212
|
|
|
213
|
-
Although the Grid factory methods (`Grid.solid()`, `Grid.from()`, `Grid.init()`) return a `GridBase<T
|
|
213
|
+
Although the Grid factory methods (`Grid.solid<T>()`, `Grid.from<T>()`, `Grid.init<T>()`) belong to `Grid`, they return a `GridBase<T>`. `Grid.wrap<T>()` is an exception, returning `Grid<T>`, as it uses a storage layer provided by the user. The only API distinction is that `GridBase` provides a 'change' event, via `grid.on("change", handler)`.
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
We can perform batched updates, suppressing the 'change' event until a process concludes, with `grid.batchUpdate(callback)`.
|
|
216
216
|
|
|
217
217
|
## Save and load
|
|
218
218
|
|
package/lib/grid.d.ts
CHANGED
|
@@ -13,15 +13,16 @@ type PathFindOptions<T> = {
|
|
|
13
13
|
cell: Cell<T>;
|
|
14
14
|
cost: number;
|
|
15
15
|
}) => void;
|
|
16
|
-
shortcutMap?: Source2D<Cell<T>[] | undefined>;
|
|
16
|
+
shortcutMap?: Source2D<Cell<T>[] | undefined> | GetXYFunc<Cell<T>[]>;
|
|
17
17
|
};
|
|
18
18
|
type CostMapOptions<T> = PathFindOptions<T> & {
|
|
19
|
-
stopAt?:
|
|
19
|
+
stopAt?: LocationSpec<T>;
|
|
20
20
|
};
|
|
21
21
|
type VisibilityOptions = {
|
|
22
22
|
maxDistance?: number;
|
|
23
23
|
boundariesVisible?: boolean;
|
|
24
24
|
};
|
|
25
|
+
type LocationSpec<T> = Cell<T> | [number, number];
|
|
25
26
|
export declare enum TraversalType {
|
|
26
27
|
cardinal = 0,
|
|
27
28
|
diagonal = 1,
|
|
@@ -38,16 +39,16 @@ export declare class Cell<T> {
|
|
|
38
39
|
constructor(x: number, y: number, gridView: Grid<T>);
|
|
39
40
|
get value(): T;
|
|
40
41
|
set value(v: T);
|
|
42
|
+
private getSiblingCell;
|
|
43
|
+
private getSiblingXY;
|
|
41
44
|
getNeighbours(includeDiagonals?: boolean): Cell<T>[];
|
|
42
45
|
look(xDelta: number, yDelta: number): Cell<T> | undefined;
|
|
43
|
-
findPath(target:
|
|
44
|
-
getPathMap(getCost: CostFunc<T>, options?: PathFindOptions<T>): Pipe2D<Cell<T>[] | null>;
|
|
45
|
-
getCostMap(getCost: CostFunc<T>, options?: CostMapOptions<T>): Pipe2D<number>;
|
|
46
|
+
findPath(target: LocationSpec<T>, getCost: CostFunc<T> | Map<T, number>, options?: PathFindOptions<T>): Cell<T>[] | null;
|
|
47
|
+
getPathMap(getCost: CostFunc<T> | Map<T, number>, options?: PathFindOptions<T>): Pipe2D<Cell<T>[] | null>;
|
|
48
|
+
getCostMap(getCost: CostFunc<T> | Map<T, number>, options?: CostMapOptions<T>): Pipe2D<number>;
|
|
46
49
|
private calculateCosts;
|
|
47
|
-
getLineTo(target:
|
|
48
|
-
|
|
49
|
-
y: number;
|
|
50
|
-
} | [number, number]): IterableIterator<Cell<T>>;
|
|
50
|
+
getLineTo(target: LocationSpec<T>): IterableIterator<Cell<T>>;
|
|
51
|
+
getLineTo(x: number, y: number): IterableIterator<Cell<T>>;
|
|
51
52
|
createVisibilityMap(isClear: (cell: Cell<T>) => boolean, options?: VisibilityOptions): Pipe2D<boolean>;
|
|
52
53
|
}
|
|
53
54
|
export declare class Grid<T> {
|
package/lib/grid.js
CHANGED
|
@@ -18,6 +18,22 @@ export class Cell {
|
|
|
18
18
|
set value(v) {
|
|
19
19
|
this.gridView.set(this.x, this.y, v);
|
|
20
20
|
}
|
|
21
|
+
getSiblingCell(location) {
|
|
22
|
+
if (Array.isArray(location))
|
|
23
|
+
return this.gridView.cells.get(...location);
|
|
24
|
+
if (location.gridView !== this.gridView) {
|
|
25
|
+
throw new Error("Target cell belongs to a different Grid");
|
|
26
|
+
}
|
|
27
|
+
return location;
|
|
28
|
+
}
|
|
29
|
+
getSiblingXY(location) {
|
|
30
|
+
if (Array.isArray(location))
|
|
31
|
+
return location;
|
|
32
|
+
if (location.gridView !== this.gridView) {
|
|
33
|
+
throw new Error("Target cell belongs to a different Grid");
|
|
34
|
+
}
|
|
35
|
+
return [location.x, location.y];
|
|
36
|
+
}
|
|
21
37
|
getNeighbours(includeDiagonals = false) {
|
|
22
38
|
const offsets = includeDiagonals
|
|
23
39
|
? [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
|
|
@@ -34,15 +50,12 @@ export class Cell {
|
|
|
34
50
|
return this.gridView.cells.get(xDelta + this.x, yDelta + this.y);
|
|
35
51
|
}
|
|
36
52
|
findPath(target, getCost, options = {}) {
|
|
37
|
-
const targetXY = Array.isArray(target)
|
|
38
|
-
? target
|
|
39
|
-
: [target.x, target.y];
|
|
40
53
|
const pathMap = this.calculateCosts(getCost, {
|
|
41
|
-
stopAt:
|
|
54
|
+
stopAt: target,
|
|
42
55
|
allowDiagonal: options.allowDiagonal,
|
|
43
56
|
maxCost: options.maxCost,
|
|
44
57
|
}, true);
|
|
45
|
-
return pathMap === null || pathMap === void 0 ? void 0 : pathMap.get(...
|
|
58
|
+
return pathMap === null || pathMap === void 0 ? void 0 : pathMap.get(...this.getSiblingXY(target));
|
|
46
59
|
}
|
|
47
60
|
getPathMap(getCost, options = {}) {
|
|
48
61
|
return this.calculateCosts(getCost, options, true);
|
|
@@ -59,41 +72,43 @@ export class Cell {
|
|
|
59
72
|
const pathInfo = asPathMap
|
|
60
73
|
? new Map()
|
|
61
74
|
: null;
|
|
75
|
+
const getCostFn = getCost instanceof Map
|
|
76
|
+
? (cell) => { var _a; return (_a = getCost.get(cell.value)) !== null && _a !== void 0 ? _a : Infinity; }
|
|
77
|
+
: getCost;
|
|
62
78
|
costs.set(this, 0);
|
|
63
79
|
const queue = new OrderedQueue(cell => { var _a; return (_a = costs.get(cell)) !== null && _a !== void 0 ? _a : Infinity; }, this);
|
|
64
|
-
const
|
|
65
|
-
? this.gridView.cells.get(options.stopAt[0], options.stopAt[1])
|
|
66
|
-
: options.stopAt;
|
|
67
|
-
if (targetCell && targetCell.gridView !== this.gridView) {
|
|
68
|
-
throw new Error("Target cell belongs to a different Grid");
|
|
69
|
-
}
|
|
80
|
+
const stopAtCell = options.stopAt && this.getSiblingCell(options.stopAt);
|
|
70
81
|
while (queue.length > 0) {
|
|
71
82
|
const current = queue.take();
|
|
72
|
-
(_a = options.onVisit) === null || _a === void 0 ? void 0 : _a.call(options, { cell: current, cost: costs.get(current) });
|
|
73
83
|
if (visited.has(current))
|
|
74
84
|
continue;
|
|
75
85
|
visited.add(current);
|
|
76
|
-
|
|
86
|
+
(_a = options.onVisit) === null || _a === void 0 ? void 0 : _a.call(options, { cell: current, cost: costs.get(current) });
|
|
87
|
+
if (stopAtCell && current === stopAtCell) {
|
|
77
88
|
break;
|
|
78
89
|
}
|
|
79
90
|
const currentCost = costs.get(current);
|
|
80
91
|
const neighbours = current.getNeighbours(options.allowDiagonal).map(cell => ({
|
|
81
|
-
traversalType: cell.x == current.x && cell.y == current.y
|
|
92
|
+
traversalType: cell.x == current.x && cell.y == current.y
|
|
93
|
+
? TraversalType.cardinal
|
|
94
|
+
: TraversalType.diagonal,
|
|
82
95
|
cell
|
|
83
96
|
}));
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
const shortcutCells = typeof options.shortcutMap == "function"
|
|
98
|
+
? options.shortcutMap(current.x, current.y)
|
|
99
|
+
: (_b = options.shortcutMap) === null || _b === void 0 ? void 0 : _b.get(current.x, current.y);
|
|
100
|
+
if (shortcutCells) {
|
|
101
|
+
const shortcuts = shortcutCells.map(cell => ({
|
|
86
102
|
traversalType: TraversalType.shortcut,
|
|
87
103
|
cell
|
|
88
104
|
}));
|
|
89
|
-
|
|
90
|
-
neighbours.push(...shortcuts);
|
|
105
|
+
neighbours.push(...shortcuts);
|
|
91
106
|
}
|
|
92
107
|
neighbours.forEach(n => {
|
|
93
108
|
var _a;
|
|
94
109
|
if (visited.has(n.cell))
|
|
95
110
|
return;
|
|
96
|
-
const moveCost =
|
|
111
|
+
const moveCost = getCostFn(n.cell, {
|
|
97
112
|
traversalType: n.traversalType,
|
|
98
113
|
from: current,
|
|
99
114
|
});
|
|
@@ -135,10 +150,10 @@ export class Cell {
|
|
|
135
150
|
return this.gridView.cells.map(costs, () => Infinity)
|
|
136
151
|
.strict();
|
|
137
152
|
}
|
|
138
|
-
*getLineTo(
|
|
139
|
-
const [targetX, targetY] =
|
|
140
|
-
?
|
|
141
|
-
:
|
|
153
|
+
*getLineTo(targetOrX, _targetY) {
|
|
154
|
+
const [targetX, targetY] = typeof targetOrX == "number"
|
|
155
|
+
? [targetOrX, _targetY]
|
|
156
|
+
: this.getSiblingXY(targetOrX);
|
|
142
157
|
if (this.x === targetX && this.y === targetY) {
|
|
143
158
|
yield this;
|
|
144
159
|
return;
|
|
@@ -182,7 +197,7 @@ export class Cell {
|
|
|
182
197
|
return false;
|
|
183
198
|
}
|
|
184
199
|
}
|
|
185
|
-
for (const cell of this.getLineTo(
|
|
200
|
+
for (const cell of this.getLineTo([x, y])) {
|
|
186
201
|
if (isClear(cell))
|
|
187
202
|
continue;
|
|
188
203
|
return cell.x === x && cell.y === y
|
package/package.json
CHANGED