@xtia/grid 0.1.0 → 0.2.0
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 +11 -13
- package/lib/grid.d.ts +2 -8
- package/lib/grid.js +15 -32
- package/package.json +1 -1
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"; // ~4.8kb gzipped
|
|
25
|
+
import { Grid } from "@xtia/grid"; // ~4.8kb gzipped (2.2kb + Pipe2D dep)
|
|
26
26
|
|
|
27
27
|
// initialise a 30x20 Grid<number> of 0's
|
|
28
28
|
const numGrid = Grid.solid(30, 20, 0);
|
|
@@ -32,7 +32,7 @@ numGrid.set(3, 3, 50);
|
|
|
32
32
|
|
|
33
33
|
// initialise a chessboard
|
|
34
34
|
const chessGrid = Grid.init(8, 8, (x, y) =>
|
|
35
|
-
|
|
35
|
+
(x + y) % 2 === 0 ? 'black' : 'white'
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
// read a value
|
|
@@ -43,7 +43,7 @@ const source = imagePipe
|
|
|
43
43
|
.crop(10, 10, 64, 64)
|
|
44
44
|
.scale(.5)
|
|
45
45
|
.rotateLeft();
|
|
46
|
-
const grid = Grid.
|
|
46
|
+
const grid = Grid.init(source);
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
### Wrapping existing structures
|
|
@@ -97,7 +97,7 @@ world.writeMask(mask).fill("mountain");
|
|
|
97
97
|
const lakeRegion = world.region(25, 25, 30, 30);
|
|
98
98
|
|
|
99
99
|
// do we want a distinct, self-contained copy of the region?
|
|
100
|
-
const lakeGrid = Grid.
|
|
100
|
+
const lakeGrid = Grid.init(lakeRegion);
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
## Transformation Layers
|
|
@@ -106,12 +106,11 @@ const lakeGrid = Grid.from(lakeRegion);
|
|
|
106
106
|
|
|
107
107
|
```ts
|
|
108
108
|
const spriteMap = world.map(
|
|
109
|
-
type => type + ".png",
|
|
110
|
-
sprite => sprite.replace(/\..*/, '')
|
|
109
|
+
type => type + ".png", // add .png on parent-read
|
|
110
|
+
sprite => sprite.replace(/\..*/, '') // remove it on parent-write
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
console.log(spriteMap.get(0, 0)); // read the transformed view: "mountain.png"
|
|
113
|
+
console.log(spriteMap.get(0, 0)); // read the transformed view: "grass.png"
|
|
115
114
|
spriteMap.set(1, 0, "forest.png"); // modify the view
|
|
116
115
|
console.log(world.get(1, 0)); // read the parent: "forest"
|
|
117
116
|
```
|
|
@@ -144,7 +143,6 @@ const minimap = world.pipe
|
|
|
144
143
|
.floorCoordinates()
|
|
145
144
|
.crop(viewX, viewY, vw, vh)
|
|
146
145
|
.stretch(canvas.width, canvas.height);
|
|
147
|
-
|
|
148
146
|
```
|
|
149
147
|
|
|
150
148
|
## Cell interface
|
|
@@ -227,9 +225,9 @@ Unlike visibility maps, which are lazily evaluated according to the supplied `is
|
|
|
227
225
|
|
|
228
226
|
## The storage layer
|
|
229
227
|
|
|
230
|
-
`Grid` itself is an interface
|
|
228
|
+
`Grid` itself is an interface that sits on top of a synchronous 2D read/write mechanism. `GridBase` is a subclass of `Grid` with built-in storage.
|
|
231
229
|
|
|
232
|
-
Although the
|
|
230
|
+
Although the factory methods `Grid.solid<T>()` and `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)`.
|
|
233
231
|
|
|
234
232
|
We can perform batched updates, suppressing the 'change' event until a process concludes, with `grid.batchUpdate(callback)`.
|
|
235
233
|
|
|
@@ -240,7 +238,7 @@ Pipe2D makes it easy to save and restore grid data:
|
|
|
240
238
|
```ts
|
|
241
239
|
const snapshot = world.pipe.stash();
|
|
242
240
|
|
|
243
|
-
const restored = Grid.
|
|
241
|
+
const restored = Grid.init(snapshot);
|
|
244
242
|
// or paste it to an existing grid
|
|
245
243
|
world.paste(snapshot);
|
|
246
244
|
```
|
|
@@ -248,7 +246,7 @@ world.paste(snapshot);
|
|
|
248
246
|
For persistent storage or transmission, Pipe2D can export as array:
|
|
249
247
|
|
|
250
248
|
```ts
|
|
251
|
-
const saved = snapshot.toFlatArrayXY(); // ["
|
|
249
|
+
const saved = snapshot.toFlatArrayXY(); // ["grass", "forest", "grass", ...]
|
|
252
250
|
|
|
253
251
|
const restoredSnapshot = Pipe2D.fromFlatArrayXY(saved);
|
|
254
252
|
```
|
package/lib/grid.d.ts
CHANGED
|
@@ -67,18 +67,13 @@ export declare class Grid<T> {
|
|
|
67
67
|
fill(value: T): void;
|
|
68
68
|
paste(source: Source2D<T>, x?: number, y?: number): void;
|
|
69
69
|
writeMask(mask: Source2D<boolean> | GetXYFunc<boolean>): Grid<T>;
|
|
70
|
-
[Symbol.iterator](): IterableIterator<
|
|
71
|
-
x: number;
|
|
72
|
-
y: number;
|
|
73
|
-
value: T;
|
|
74
|
-
}>;
|
|
70
|
+
[Symbol.iterator](): IterableIterator<T>;
|
|
75
71
|
forEach(callback: (value: T, x: number, y: number) => void): void;
|
|
76
|
-
combine<U, R>(aux: Source2D<U>, cb: (source: T, aux: U) => R): GridBase<R>;
|
|
77
72
|
map<U>(read: (initial: T, x: number, y: number) => U, write: (local: U, x: number, y: number) => T): Grid<U>;
|
|
78
73
|
region(x: number, y: number, width: number, height: number): Grid<T>;
|
|
79
74
|
scroll(xDelta: number, yDelta: number, fill: T): this;
|
|
80
75
|
static init<T>(width: number, height: number, initCell: (x: number, y: number) => T): GridBase<T>;
|
|
81
|
-
static
|
|
76
|
+
static init<T>(source: Source2D<T>): GridBase<T>;
|
|
82
77
|
static solid<T>(width: number, height: number, fillValue: T): GridBase<T>;
|
|
83
78
|
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>;
|
|
84
79
|
}
|
|
@@ -90,7 +85,6 @@ export declare class GridBase<T> extends Grid<T> {
|
|
|
90
85
|
batchUpdate(fn: () => void): void;
|
|
91
86
|
constructor(source: Source2D<T>);
|
|
92
87
|
on<K extends keyof EventMap<T>>(eventName: K, handler: (data: EventMap<T>[K]) => void): () => void;
|
|
93
|
-
private xyToIndex;
|
|
94
88
|
private _set;
|
|
95
89
|
private triggerChange;
|
|
96
90
|
}
|
package/lib/grid.js
CHANGED
|
@@ -215,6 +215,9 @@ export class Grid {
|
|
|
215
215
|
this.parentSet = parentSet;
|
|
216
216
|
this.batch = batch;
|
|
217
217
|
this.cells = new Pipe2D(this.width, this.height, (x, y) => {
|
|
218
|
+
if (x < 0 || x >= this.width || y < 0 || y >= this.height || !Number.isInteger(x) || !Number.isInteger(y)) {
|
|
219
|
+
throw new RangeError(`Cell coordinates must be bounded integers; got (${x}, ${y})`);
|
|
220
|
+
}
|
|
218
221
|
return new Cell(x, y, this);
|
|
219
222
|
}).strict().withCache();
|
|
220
223
|
this.pipe = new Pipe2D(this);
|
|
@@ -293,7 +296,7 @@ export class Grid {
|
|
|
293
296
|
*[Symbol.iterator]() {
|
|
294
297
|
for (let y = 0; y < this.height; y++) {
|
|
295
298
|
for (let x = 0; x < this.width; x++) {
|
|
296
|
-
yield
|
|
299
|
+
yield this.get(x, y);
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
302
|
}
|
|
@@ -304,22 +307,11 @@ export class Grid {
|
|
|
304
307
|
}
|
|
305
308
|
}
|
|
306
309
|
}
|
|
307
|
-
combine(aux, cb) {
|
|
308
|
-
const width = Math.min(this.width, aux.width);
|
|
309
|
-
const height = Math.min(this.height, aux.height);
|
|
310
|
-
return new GridBase({
|
|
311
|
-
width,
|
|
312
|
-
height,
|
|
313
|
-
get: (x, y) => {
|
|
314
|
-
return cb(this.get(x, y), aux.get(x, y));
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
310
|
map(read, write) {
|
|
319
311
|
return new Grid(this.width, this.height, (x, y) => read(this.parentGet(x, y), x, y), (x, y, value) => this.parentSet(x, y, write(value, x, y)), fn => this.batch(fn));
|
|
320
312
|
}
|
|
321
313
|
region(x, y, width, height) {
|
|
322
|
-
if (!Number.isInteger(x) || !Number.isInteger(y)
|
|
314
|
+
if (!Number.isInteger(x) || !Number.isInteger(y)) {
|
|
323
315
|
throw new Error(`Region boundaries must be integer; got (${x}, ${y}), (${width} x ${height})`);
|
|
324
316
|
}
|
|
325
317
|
if (x < 0 || y < 0 || x + width > this.width || y + height > this.height) {
|
|
@@ -350,16 +342,15 @@ export class Grid {
|
|
|
350
342
|
}
|
|
351
343
|
return this;
|
|
352
344
|
}
|
|
353
|
-
static init(
|
|
345
|
+
static init(widthOrSource, height, initCell) {
|
|
346
|
+
if (typeof widthOrSource == "object")
|
|
347
|
+
return new GridBase(widthOrSource);
|
|
354
348
|
return new GridBase({
|
|
355
|
-
width,
|
|
356
|
-
height,
|
|
349
|
+
width: widthOrSource,
|
|
350
|
+
height: height,
|
|
357
351
|
get: initCell
|
|
358
352
|
});
|
|
359
353
|
}
|
|
360
|
-
static from(source) {
|
|
361
|
-
return new GridBase(source);
|
|
362
|
-
}
|
|
363
354
|
static solid(width, height, fillValue) {
|
|
364
355
|
return new GridBase(Pipe2D.solid(fillValue, width, height));
|
|
365
356
|
}
|
|
@@ -387,11 +378,12 @@ export class GridBase extends Grid {
|
|
|
387
378
|
if (this.batchState.level == 0) {
|
|
388
379
|
const changed = this.batchState.changedCells;
|
|
389
380
|
this.batchState = null;
|
|
390
|
-
|
|
381
|
+
if (changed.size > 0)
|
|
382
|
+
this.triggerChange(changed);
|
|
391
383
|
}
|
|
392
384
|
}
|
|
393
385
|
constructor(source) {
|
|
394
|
-
super(source.width, source.height, (x, y) => this.data[this.
|
|
386
|
+
super(source.width, source.height, (x, y) => this.data[y * this.width + x], (x, y, value) => this._set(x, y, value), fn => this.batchUpdate(fn));
|
|
395
387
|
this.eventHandlers = {};
|
|
396
388
|
this.batchState = null;
|
|
397
389
|
const sourcePipe = source instanceof Pipe2D
|
|
@@ -411,20 +403,11 @@ export class GridBase extends Grid {
|
|
|
411
403
|
this.eventHandlers[eventName].splice(idx, 1);
|
|
412
404
|
};
|
|
413
405
|
}
|
|
414
|
-
xyToIndex(x, y) {
|
|
415
|
-
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
|
416
|
-
throw new Error(`Coordinates out of bounds: (${x}, ${y})`);
|
|
417
|
-
}
|
|
418
|
-
if (!Number.isInteger(x) || !Number.isInteger(y)) {
|
|
419
|
-
throw new Error(`Coordinates must be integer; got (${x}, ${y})`);
|
|
420
|
-
}
|
|
421
|
-
return y * this.width + x;
|
|
422
|
-
}
|
|
423
406
|
_set(x, y, value) {
|
|
424
|
-
const idx = this.
|
|
407
|
+
const idx = y * this.width + x;
|
|
425
408
|
if (this.data[idx] === value)
|
|
426
409
|
return;
|
|
427
|
-
this.data[
|
|
410
|
+
this.data[idx] = value;
|
|
428
411
|
if (this.batchState) {
|
|
429
412
|
this.batchState.changedCells.add(this.cells.get(x, y));
|
|
430
413
|
return;
|
package/package.json
CHANGED