@xtia/grid 0.0.10 → 0.0.12
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 +16 -8
- package/lib/grid.d.ts +4 -2
- package/lib/grid.js +52 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ const mask = (x: number, y: number) => {
|
|
|
91
91
|
const dist = Math.hypot(x - centre.x, y - centre.y);
|
|
92
92
|
return dist > 12 && dist < 18; // mountain ring
|
|
93
93
|
};
|
|
94
|
-
world.fill("mountain"
|
|
94
|
+
world.writeMask(mask).fill("mountain");
|
|
95
95
|
|
|
96
96
|
// get a view of just the interesting area
|
|
97
97
|
const lakeRegion = world.region(25, 25, 30, 30);
|
|
@@ -203,9 +203,8 @@ const visibility = start.createVisibilityMap(
|
|
|
203
203
|
cell => cell.value === "grass" // anything except grass blocks vision
|
|
204
204
|
);
|
|
205
205
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
screenGrid.paste(spriteMap, 0, 0, visibility);
|
|
206
|
+
// writeMask() creates a live view that only allows writes at positions allowed by the mask Source2D/function
|
|
207
|
+
screenGrid.writeMask(visibility).paste(spriteMap, 0, 0);
|
|
209
208
|
```
|
|
210
209
|
|
|
211
210
|
### Reusing path data
|
|
@@ -234,8 +233,17 @@ We can perform batched updates, suppressing the 'change' event until a process c
|
|
|
234
233
|
Pipe2D makes it easy to save and restore grid data:
|
|
235
234
|
|
|
236
235
|
```ts
|
|
237
|
-
const
|
|
236
|
+
const snapshot = world.pipe.stash();
|
|
238
237
|
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
const restored = Grid.from(snapshot);
|
|
239
|
+
// or paste it to an existing grid
|
|
240
|
+
world.paste(snapshot);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
For persistent storage or transmission, Pipe2D can export as array:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const saved = snapshot.toFlatArrayXY(); // ["mountain", "forest", "grass", ...]
|
|
247
|
+
|
|
248
|
+
const restoredSnapshot = Pipe2D.fromFlatArrayXY(saved);
|
|
249
|
+
```
|
package/lib/grid.d.ts
CHANGED
|
@@ -64,8 +64,9 @@ export declare class Grid<T> {
|
|
|
64
64
|
get(x: number, y: number): T;
|
|
65
65
|
set(x: number, y: number, value: T): void;
|
|
66
66
|
trySet(x: number, y: number, value: T): boolean;
|
|
67
|
-
fill(value: T
|
|
68
|
-
paste(source: Source2D<T>, x?: number, y?: number
|
|
67
|
+
fill(value: T): void;
|
|
68
|
+
paste(source: Source2D<T>, x?: number, y?: number): void;
|
|
69
|
+
writeMask(mask: Source2D<boolean> | GetXYFunc<boolean>): Grid<T>;
|
|
69
70
|
[Symbol.iterator](): IterableIterator<{
|
|
70
71
|
x: number;
|
|
71
72
|
y: number;
|
|
@@ -75,6 +76,7 @@ export declare class Grid<T> {
|
|
|
75
76
|
combine<U, R>(aux: Source2D<U>, cb: (source: T, aux: U) => R): GridBase<R>;
|
|
76
77
|
map<U>(read: (initial: T, x: number, y: number) => U, write: (local: U, x: number, y: number) => T): Grid<U>;
|
|
77
78
|
region(x: number, y: number, width: number, height: number): Grid<T>;
|
|
79
|
+
scroll(xDelta: number, yDelta: number, fill: T): this;
|
|
78
80
|
static init<T>(width: number, height: number, initCell: (x: number, y: number) => T): GridBase<T>;
|
|
79
81
|
static from<T>(source: Source2D<T>): GridBase<T>;
|
|
80
82
|
static solid<T>(width: number, height: number, fillValue: T): GridBase<T>;
|
package/lib/grid.js
CHANGED
|
@@ -218,17 +218,29 @@ export class Grid {
|
|
|
218
218
|
return new Cell(x, y, this);
|
|
219
219
|
}).strict().withCache();
|
|
220
220
|
this.pipe = new Pipe2D(this);
|
|
221
|
+
if (!Number.isInteger(width) || !Number.isInteger(height)) {
|
|
222
|
+
throw new Error(`Grid width & height must be integer; got (${width} x ${height})`);
|
|
223
|
+
}
|
|
224
|
+
if (width < 0 || height < 0) {
|
|
225
|
+
throw new Error(`Grid width/height may not be less than 0; got (${width} x ${height})`);
|
|
226
|
+
}
|
|
221
227
|
}
|
|
222
228
|
batchUpdate(fn) {
|
|
223
229
|
this.batch(fn);
|
|
224
230
|
}
|
|
225
231
|
get(x, y) {
|
|
232
|
+
if (!Number.isInteger(x) || !Number.isInteger(y)) {
|
|
233
|
+
throw new Error(`Coordinates must be integer; got (${x}, ${y})`);
|
|
234
|
+
}
|
|
226
235
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
|
227
236
|
throw new RangeError("Coordinates out of bounds");
|
|
228
237
|
}
|
|
229
238
|
return this.parentGet(x, y);
|
|
230
239
|
}
|
|
231
240
|
set(x, y, value) {
|
|
241
|
+
if (!Number.isInteger(x) || !Number.isInteger(y)) {
|
|
242
|
+
throw new Error(`Coordinates must be integer; got (${x}, ${y})`);
|
|
243
|
+
}
|
|
232
244
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
|
233
245
|
throw new RangeError("Coordinates out of bounds");
|
|
234
246
|
}
|
|
@@ -245,41 +257,36 @@ export class Grid {
|
|
|
245
257
|
this.set(x, y, value);
|
|
246
258
|
return true;
|
|
247
259
|
}
|
|
248
|
-
fill(value
|
|
249
|
-
const maskSource = typeof mask == "function" ? {
|
|
250
|
-
width: this.width,
|
|
251
|
-
height: this.height,
|
|
252
|
-
get: mask
|
|
253
|
-
} : mask;
|
|
260
|
+
fill(value) {
|
|
254
261
|
this.batchUpdate(() => {
|
|
255
262
|
for (let y = 0; y < this.height; y++) {
|
|
256
263
|
for (let x = 0; x < this.width; x++) {
|
|
257
|
-
if (maskSource && !maskSource.get(x, y))
|
|
258
|
-
continue;
|
|
259
264
|
this.trySet(x, y, value);
|
|
260
265
|
}
|
|
261
266
|
}
|
|
262
267
|
});
|
|
263
268
|
}
|
|
264
|
-
paste(source, x = 0, y = 0
|
|
265
|
-
const
|
|
266
|
-
width: this.width,
|
|
267
|
-
height: this.height,
|
|
268
|
-
get: mask
|
|
269
|
-
} : mask;
|
|
270
|
-
const cachedSource = new GridBase(source);
|
|
269
|
+
paste(source, x = 0, y = 0) {
|
|
270
|
+
const cachedSource = new Pipe2D(source).stash();
|
|
271
271
|
this.batchUpdate(() => {
|
|
272
272
|
for (let oy = 0; oy < source.height; oy++) {
|
|
273
273
|
for (let ox = 0; ox < source.width; ox++) {
|
|
274
|
-
|
|
275
|
-
const ty = oy + y;
|
|
276
|
-
if (maskSource && !maskSource.get(tx, ty))
|
|
277
|
-
continue;
|
|
278
|
-
this.trySet(tx, ty, cachedSource.get(ox, oy));
|
|
274
|
+
this.trySet(ox + x, oy + y, cachedSource.get(ox, oy));
|
|
279
275
|
}
|
|
280
276
|
}
|
|
281
277
|
});
|
|
282
278
|
}
|
|
279
|
+
writeMask(mask) {
|
|
280
|
+
const getMask = typeof mask == "function"
|
|
281
|
+
? mask
|
|
282
|
+
: (x, y) => mask.get(x, y);
|
|
283
|
+
return new Grid(this.width, this.height, this.parentGet, (x, y, v) => {
|
|
284
|
+
const writable = getMask(x, y);
|
|
285
|
+
if (writable) {
|
|
286
|
+
this.set(x, y, v);
|
|
287
|
+
}
|
|
288
|
+
}, cb => this.batch(cb));
|
|
289
|
+
}
|
|
283
290
|
*[Symbol.iterator]() {
|
|
284
291
|
for (let y = 0; y < this.height; y++) {
|
|
285
292
|
for (let x = 0; x < this.width; x++) {
|
|
@@ -309,12 +316,37 @@ export class Grid {
|
|
|
309
316
|
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));
|
|
310
317
|
}
|
|
311
318
|
region(x, y, width, height) {
|
|
319
|
+
if (!Number.isInteger(x) || !Number.isInteger(y) || !Number.isInteger(width) || !Number.isInteger(height)) {
|
|
320
|
+
throw new Error(`Region boundaries must be integer; got (${x}, ${y}), (${width} x ${height})`);
|
|
321
|
+
}
|
|
322
|
+
if (x < 0 || y < 0 || x + width > this.width || y + height > this.height) {
|
|
323
|
+
throw new Error("Requested region exceeds the parent's bounds");
|
|
324
|
+
}
|
|
312
325
|
return new Grid(width, height, (tx, ty) => {
|
|
313
326
|
return this.parentGet(tx + x, ty + y);
|
|
314
327
|
}, (tx, ty, value) => {
|
|
315
328
|
this.parentSet(tx + x, ty + y, value);
|
|
316
329
|
}, fn => this.batch(fn));
|
|
317
330
|
}
|
|
331
|
+
scroll(xDelta, yDelta, fill) {
|
|
332
|
+
if (!Number.isInteger(xDelta) || !Number.isInteger(yDelta)) {
|
|
333
|
+
throw new Error(`Scroll offsets must be integer; got (${xDelta}, ${yDelta})`);
|
|
334
|
+
}
|
|
335
|
+
this.paste(this, xDelta, yDelta);
|
|
336
|
+
if (xDelta > 0) {
|
|
337
|
+
this.region(0, 0, xDelta, this.height).fill(fill);
|
|
338
|
+
}
|
|
339
|
+
else if (xDelta < 0) {
|
|
340
|
+
this.region(this.width + xDelta, 0, -xDelta, this.height).fill(fill);
|
|
341
|
+
}
|
|
342
|
+
if (yDelta > 0) {
|
|
343
|
+
this.region(0, 0, this.width, yDelta).fill(fill);
|
|
344
|
+
}
|
|
345
|
+
else if (yDelta < 0) {
|
|
346
|
+
this.region(0, this.height + yDelta, this.width, -yDelta).fill(fill);
|
|
347
|
+
}
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
318
350
|
static init(width, height, initCell) {
|
|
319
351
|
return new GridBase({
|
|
320
352
|
width,
|
package/package.json
CHANGED