@xtia/grid 0.0.4 → 0.0.6

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 CHANGED
@@ -22,7 +22,7 @@ npm i @xtia/grid
22
22
  ```
23
23
 
24
24
  ```ts
25
- import { Grid } from "@xtia/grid";
25
+ import { Grid } from "@xtia/grid"; // ~4.8kb gzipped
26
26
 
27
27
  // initialise a 30x20 Grid<number> of 0's
28
28
  const numGrid = Grid.solid(30, 20, 0);
@@ -46,6 +46,34 @@ const source = imagePipe
46
46
  const grid = Grid.from(source);
47
47
  ```
48
48
 
49
+ ### Wrapping existing structures
50
+
51
+ Use Grid's interface over any read/write 2D structure:
52
+
53
+ ```ts
54
+ const gameMap = [
55
+ [0, 1, -1, 2],
56
+ [1, 0, 1, 0],
57
+ [-1, 1, 0, 2],
58
+ [2, 0, 2, -1]
59
+ ];
60
+
61
+ // create a *live view* Grid over gameMap
62
+ const grid = Grid.wrap(
63
+ gameMap[0].length, // width
64
+ gameMap.length, // height
65
+ (x, y) => gameMap[y][x], // get
66
+ (x, y, value) => gameMap[y][x] = value // set
67
+ );
68
+
69
+ // reading the grid = reading the source
70
+ gameMap[0][0] = 50;
71
+ console.log(grid.get(0, 0)); // 50
72
+ // writing to the grid = writing to the source
73
+ grid.set(3, 3, 100);
74
+ console.log(gameMap[3][3]); // 100
75
+ ```
76
+
49
77
  ## Regions
50
78
 
51
79
  Use `grid.region(x, y, w, h)` to define a subgrid. The subgrid is **zero-copy view** into the parent; changes to the subgrid affect the parent and vice-versa.
@@ -74,12 +102,12 @@ const lakeGrid = Grid.from(lakeRegion);
74
102
 
75
103
  ## Transformation Layers
76
104
 
77
- `grid.map(read, write)` creates a **view** that reads and writes the parent through the provided transformation functions.
105
+ `grid.map(read, write)` creates a **zero-copy view** that reads and writes the parent through the provided transformation functions.
78
106
 
79
107
  ```ts
80
108
  const spriteMap = world.map(
81
109
  type => type + ".png",
82
- sprite => sprite.replace(/\.*/, '')
110
+ sprite => sprite.replace(/\..*/, '')
83
111
  );
84
112
 
85
113
  world.set(0, 0, "mountain"); // modify the parent
@@ -90,6 +118,20 @@ console.log(world.get(1, 0)); // read the parent: "forest"
90
118
 
91
119
  For one-way read-mapping, use `grid.pipe` - a [Pipe2D](https://github.com/tiadrop/pipe2d) view into the grid's data.
92
120
 
121
+ ```ts
122
+ // create a one-way, player-centred sprite map
123
+ const viewport = world.pipe
124
+ .oob("mountain")
125
+ .map(t => t + ".png")
126
+ .crop(playerX - 5, playerY - 5, 10, 10);
127
+
128
+ // or get a list of locations of cells with mountains
129
+ const mountainCells = world.cells.toFlatArrayXY()
130
+ .filter(cell => cell.value === "mountain")
131
+ .map(cell => [cell.x, cell.y]);
132
+
133
+ ```
134
+
93
135
  ## Cell interface
94
136
 
95
137
  `grid.cells` provides a Pipe2D, for convenient transformation, of live-view interfaces relating to positions in the grid.
@@ -98,12 +140,12 @@ For one-way read-mapping, use `grid.pipe` - a [Pipe2D](https://github.com/tiadro
98
140
  const topLeft = world.cells.get(0, 0);
99
141
  console.log(topLeft.value); // "mountain"
100
142
 
101
- topLeft.value = "forest; // modifies the underlying data
143
+ topLeft.value = "forest"; // modifies the underlying data
102
144
  console.log(spriteMap.get(0, 0)); // now "forest.png";
103
145
 
104
146
  // navigate by offset
105
147
  const adjacentCell = topLeft.look(1, 0);
106
- console.log(adjacentCell.x, adjacentCell.y); // 1, 0
148
+ console.log(adjacentCell?.x, adjacentCell?.y); // 1, 0
107
149
  ```
108
150
 
109
151
  Cells provide methods for locational utilities such as pathfinding and visibility mapping.
@@ -149,9 +191,6 @@ const visibility = start.createVisibilityMap(
149
191
  // any 2d source, including visibility maps, can be used as a mask for paste/fill operations
150
192
  // paste(source: Source2D<T>, mask?: Source2D<boolean>)
151
193
  screenGrid.paste(spriteMap, visibility);
152
-
153
- // using pipe2d's convenience
154
- const mountainCells = world.cells.toFlatArrayXY().filter(c => c.value === "mountain");
155
194
  ```
156
195
 
157
196
  ### Reusing path data
@@ -165,7 +204,7 @@ const pathToCentre = pathMap.get(50, 50);
165
204
  const pathToCorner = pathMap.get(99, 99);
166
205
  ```
167
206
 
168
- Unlike visibility maps, which are lazily evaluated according to the supplied `isWall` function *when the map is queried*, path maps necessarily reflect the underlying data *when the map is created*. This distinction is hinted through the `create*` vs `get*` naming.
207
+ Unlike visibility maps, which are lazily evaluated according to the supplied `isClear` function *when the map is queried* (but can be easily cached with `visMap.withCache()`), path maps use a relatively expensive traversal map that's created *when the map is created*. This distinction is hinted through the `create*` vs `get*` naming.
169
208
 
170
209
  ## The storage layer
171
210
 
@@ -173,4 +212,15 @@ Unlike visibility maps, which are lazily evaluated according to the supplied `is
173
212
 
174
213
  Although the Grid factory methods (`Grid.solid()`, `Grid.from()`, `Grid.init()`) return a `GridBase<T>`, the mental model is that we're simply working with `Grid`s, therefore those factory methods live on `Grid`. The only API distinction is that `GridBase` provides a 'change' event, via `grid.on("change", handler)`.
175
214
 
176
- You can perform batched updates, holding off the 'change' event until the batch process concludes, with `grid.batchUpdate(callback)`.
215
+ You can perform batched updates, holding off the 'change' event until the batch process concludes, with `grid.batchUpdate(callback)`.
216
+
217
+ ## Save and load
218
+
219
+ Pipe2D makes it easy to save and restore grid data:
220
+
221
+ ```ts
222
+ const saved = world.pipe.toFlatArrayXY(); // ["mountain", "forest", "grass", ...]
223
+
224
+ const restoredPipe = Pipe2D.fromFlatArrayXY(saved);
225
+ const restored = Grid.from(restoredPipe);
226
+ ```
package/lib/grid.d.ts CHANGED
@@ -77,6 +77,7 @@ export declare class Grid<T> {
77
77
  static init<T>(width: number, height: number, initCell: (x: number, y: number) => T): GridBase<T>;
78
78
  static from<T>(source: Source2D<T>): GridBase<T>;
79
79
  static solid<T>(width: number, height: number, fillValue: T): GridBase<T>;
80
+ static wrap<T>(width: number, height: number, get: (x: number, y: number) => T, set: (x: number, y: number, value: T) => void, batch?: (callback: () => void) => void): Grid<T>;
80
81
  }
81
82
  export declare class GridBase<T> extends Grid<T> {
82
83
  private data;
package/lib/grid.js CHANGED
@@ -132,7 +132,7 @@ export class Cell {
132
132
  return path;
133
133
  });
134
134
  }
135
- return this.gridView.cells.map((c => { var _a; return (_a = costs.get(c)) !== null && _a !== void 0 ? _a : Infinity; }))
135
+ return this.gridView.cells.map(costs, () => Infinity)
136
136
  .strict();
137
137
  }
138
138
  *getLineTo(target) {
@@ -314,6 +314,9 @@ export class Grid {
314
314
  static solid(width, height, fillValue) {
315
315
  return new GridBase(Pipe2D.solid(fillValue, width, height));
316
316
  }
317
+ static wrap(width, height, get, set, batch = cb => cb()) {
318
+ return new Grid(width, height, get, set, batch);
319
+ }
317
320
  }
318
321
  export class GridBase extends Grid {
319
322
  triggerEvent(name, data) {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.4",
2
+ "version": "0.0.6",
3
3
  "description": "2D store and utilities, with zero-copy regions and transform layers, pathfinding, Pipe2D interop",
4
4
  "name": "@xtia/grid",
5
5
  "exports": {
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "./lib/*": null
11
11
  },
12
+ "keywords": ["grid", "game-dev", "pathfinding", "cellular-automata"],
12
13
  "scripts": {
13
14
  "build": "npx tsc",
14
15
  "test": "npx vitest run"