@litecanvas/utils 0.3.1 → 0.4.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 +1 -0
- package/dist/all.js +214 -11
- package/dist/all.min.js +2 -1
- package/dist/camera.js +20 -16
- package/dist/camera.min.js +1 -1
- package/dist/grid.js +202 -0
- package/dist/grid.min.js +2 -0
- package/dist/vector.js +1 -1
- package/dist/vector.min.js +1 -1
- package/package.json +1 -1
- package/src/camera/index.js +26 -19
- package/src/collision/README.md +2 -0
- package/src/grid/README.md +45 -0
- package/src/grid/_web.js +3 -0
- package/src/grid/index.js +205 -57
- package/src/index.js +1 -0
- package/src/vector/index.js +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Small collection of tools for developing games with [Litecanvas](https://github.
|
|
|
6
6
|
|
|
7
7
|
- **Camera**: Move-, zoom- and rotatable camera with shake. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/camera)
|
|
8
8
|
- **Vector**: Modular 2D vector. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/vector)
|
|
9
|
+
- **Grid** class. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/grid)
|
|
9
10
|
- **Collision** utilities. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/collision)
|
|
10
11
|
- And [some math utilities](https://github.com/litecanvas/utils/tree/main/src/math)
|
|
11
12
|
|
package/dist/all.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
__export(src_exports, {
|
|
11
11
|
Camera: () => Camera,
|
|
12
12
|
DOWN: () => DOWN,
|
|
13
|
+
Grid: () => Grid,
|
|
13
14
|
LEFT: () => LEFT,
|
|
14
15
|
ONE: () => ONE,
|
|
15
16
|
RIGHT: () => RIGHT,
|
|
@@ -126,6 +127,26 @@
|
|
|
126
127
|
engine = engine || globalThis;
|
|
127
128
|
this._engine = engine;
|
|
128
129
|
this.size(engine.WIDTH || 0, engine.HEIGHT || 0);
|
|
130
|
+
this.x = this.width / 2;
|
|
131
|
+
this.y = this.height / 2;
|
|
132
|
+
}
|
|
133
|
+
size(width, height) {
|
|
134
|
+
this.width = width;
|
|
135
|
+
this.height = height;
|
|
136
|
+
}
|
|
137
|
+
start(clip = false) {
|
|
138
|
+
const centerX = this.width / 2, centerY = this.height / 2;
|
|
139
|
+
this._engine.push();
|
|
140
|
+
this._engine.translate(centerX, centerY);
|
|
141
|
+
this._engine.scale(this.scale);
|
|
142
|
+
this._engine.rotate(this.rotation);
|
|
143
|
+
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
|
|
144
|
+
if (clip) {
|
|
145
|
+
this._engine.cliprect(this.x, this.y, this.width, this.height);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
end() {
|
|
149
|
+
this._engine.pop();
|
|
129
150
|
}
|
|
130
151
|
/**
|
|
131
152
|
* @param {number} x
|
|
@@ -176,23 +197,205 @@
|
|
|
176
197
|
shaking() {
|
|
177
198
|
return this._shake.removeListener !== null;
|
|
178
199
|
}
|
|
179
|
-
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/grid/index.js
|
|
203
|
+
var Grid = class _Grid {
|
|
204
|
+
/** @type {number} The grid width */
|
|
205
|
+
_w;
|
|
206
|
+
/** @type {number} The grid height */
|
|
207
|
+
_h;
|
|
208
|
+
/** @type {any[]} The grid cells */
|
|
209
|
+
_c;
|
|
210
|
+
/**
|
|
211
|
+
* @static
|
|
212
|
+
* @param {number} width
|
|
213
|
+
* @param {number} height
|
|
214
|
+
* @param {any[]} values
|
|
215
|
+
*/
|
|
216
|
+
static fromArray(width, height, values) {
|
|
217
|
+
const grid = new _Grid(width, height);
|
|
218
|
+
for (let i = 0; i < values.length; i++) {
|
|
219
|
+
grid._c[i] = values[i];
|
|
220
|
+
}
|
|
221
|
+
return grid;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* @param {number} width The grid width
|
|
225
|
+
* @param {number} height The grid height
|
|
226
|
+
*/
|
|
227
|
+
constructor(width, height) {
|
|
180
228
|
this.width = width;
|
|
181
229
|
this.height = height;
|
|
230
|
+
this.clear();
|
|
182
231
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
this.
|
|
188
|
-
|
|
189
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Delete all cell values.
|
|
234
|
+
*/
|
|
235
|
+
clear() {
|
|
236
|
+
this._c = Array(this._w * this._h);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* @param {number} value
|
|
240
|
+
*/
|
|
241
|
+
set width(value) {
|
|
242
|
+
this._w = Math.max(1, ~~value);
|
|
243
|
+
}
|
|
244
|
+
get width() {
|
|
245
|
+
return this._w;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* @param {number} value
|
|
249
|
+
*/
|
|
250
|
+
set height(value) {
|
|
251
|
+
this._h = Math.max(1, ~~value);
|
|
252
|
+
}
|
|
253
|
+
get height() {
|
|
254
|
+
return this._h;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* The the value of a grid's cell.
|
|
258
|
+
*
|
|
259
|
+
* @param {number} x
|
|
260
|
+
* @param {number} y
|
|
261
|
+
* @param {any} value
|
|
262
|
+
*/
|
|
263
|
+
set(x, y, value) {
|
|
264
|
+
this._c[this.pointToIndex(x, y)] = value;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Returns the value of a grid's cell.
|
|
268
|
+
*
|
|
269
|
+
* @param {number} x
|
|
270
|
+
* @param {number} y
|
|
271
|
+
* @returns {any}
|
|
272
|
+
*/
|
|
273
|
+
get(x, y) {
|
|
274
|
+
return this._c[this.pointToIndex(x, y)];
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns true if the which cell has any value not equal to `null` or `undefined`.
|
|
278
|
+
*
|
|
279
|
+
* @param {number} x
|
|
280
|
+
* @param {number} y
|
|
281
|
+
* @returns {boolean}
|
|
282
|
+
*/
|
|
283
|
+
has(x, y) {
|
|
284
|
+
return this.get(x, y) != null;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Returns the total of cells.
|
|
288
|
+
*
|
|
289
|
+
* @returns {number}
|
|
290
|
+
*/
|
|
291
|
+
get length() {
|
|
292
|
+
return this._w * this._h;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Convert a grid point (X, Y) to a index.
|
|
296
|
+
*
|
|
297
|
+
* @param {number} x
|
|
298
|
+
* @param {number} y
|
|
299
|
+
* @returns {number} The index
|
|
300
|
+
*/
|
|
301
|
+
pointToIndex(x, y) {
|
|
302
|
+
return this.clampX(~~x) + this.clampY(~~y) * this._w;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Convert index to a grid point X.
|
|
306
|
+
*
|
|
307
|
+
* @param {number} index
|
|
308
|
+
* @returns {number}
|
|
309
|
+
*/
|
|
310
|
+
indexToPointX(index) {
|
|
311
|
+
return index % this._w;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Convert index to a grid point Y.
|
|
315
|
+
*
|
|
316
|
+
* @param {number} index
|
|
317
|
+
* @returns {number}
|
|
318
|
+
*/
|
|
319
|
+
indexToPointY(index) {
|
|
320
|
+
return Math.floor(index / this._w);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Loops over all grid cells.
|
|
324
|
+
*
|
|
325
|
+
* @callback GridForEachCallback
|
|
326
|
+
* @param {number} x
|
|
327
|
+
* @param {number} y
|
|
328
|
+
* @param {any} value
|
|
329
|
+
* @param {Grid} grid
|
|
330
|
+
* @returns {boolean?} returns `false` to stop/break the loop
|
|
331
|
+
*
|
|
332
|
+
* @param {GridForEachCallback} handler
|
|
333
|
+
* @param {boolean} [reverse=false]
|
|
334
|
+
*/
|
|
335
|
+
forEach(handler, reverse = false) {
|
|
336
|
+
let i = reverse ? this.length - 1 : 0, limit = reverse ? -1 : this.length, step = reverse ? -1 : 1;
|
|
337
|
+
while (i !== limit) {
|
|
338
|
+
const x = this.indexToPointX(i), y = this.indexToPointY(i), cellValue = this._c[i];
|
|
339
|
+
if (false === handler(x, y, cellValue, this)) break;
|
|
340
|
+
i += step;
|
|
190
341
|
}
|
|
191
342
|
}
|
|
192
|
-
|
|
193
|
-
|
|
343
|
+
/**
|
|
344
|
+
* @param {*} value
|
|
345
|
+
*/
|
|
346
|
+
fill(value) {
|
|
347
|
+
this.forEach((x, y) => {
|
|
348
|
+
this.set(x, y, value);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* @returns {Grid} the cloned grid
|
|
353
|
+
*/
|
|
354
|
+
clone() {
|
|
355
|
+
return _Grid.fromArray(this._w, this._h, this._c);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* @param {number} y
|
|
359
|
+
* @returns {number}
|
|
360
|
+
*/
|
|
361
|
+
clampX(x) {
|
|
362
|
+
return _clamp(x, 0, this._w - 1);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* @param {number} y
|
|
366
|
+
* @returns {number}
|
|
367
|
+
*/
|
|
368
|
+
clampY(y) {
|
|
369
|
+
return _clamp(y, 0, this._h - 1);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Returns the cell values in a single array.
|
|
373
|
+
*
|
|
374
|
+
* @returns {any[]}
|
|
375
|
+
*/
|
|
376
|
+
toArray() {
|
|
377
|
+
return this._c.slice(0);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* @param {string} separator
|
|
381
|
+
* @param {boolean} format
|
|
382
|
+
* @returns {string}
|
|
383
|
+
*/
|
|
384
|
+
toString(separator = " ", format = true) {
|
|
385
|
+
if (!format) return this._c.join(separator);
|
|
386
|
+
const rows = [];
|
|
387
|
+
this.forEach((x, y, value) => {
|
|
388
|
+
rows[y] = rows[y] || "";
|
|
389
|
+
rows[y] += value + separator;
|
|
390
|
+
});
|
|
391
|
+
return rows.join("\n");
|
|
194
392
|
}
|
|
195
393
|
};
|
|
394
|
+
function _clamp(value, min, max) {
|
|
395
|
+
if (value < min) return min;
|
|
396
|
+
if (value > max) return max;
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
196
399
|
|
|
197
400
|
// src/vector/index.js
|
|
198
401
|
var Vector = class {
|
|
@@ -220,7 +423,7 @@
|
|
|
220
423
|
if (isvector(x)) {
|
|
221
424
|
return veceq(v, x.x, x.y);
|
|
222
425
|
}
|
|
223
|
-
return v.x === x && v.y ===
|
|
426
|
+
return v.x === x && v.y === y;
|
|
224
427
|
};
|
|
225
428
|
var veccopy = (v) => vec(v.x, v.y);
|
|
226
429
|
var vecset = (v, x, y = x) => {
|
package/dist/all.min.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
(()=>{var
|
|
1
|
+
(()=>{var j=Object.defineProperty;var D=(e,t)=>{for(var s in t)j(e,s,{get:t[s],enumerable:!0})};var T={};D(T,{Camera:()=>f,DOWN:()=>Q,Grid:()=>p,LEFT:()=>tt,ONE:()=>C,RIGHT:()=>K,UP:()=>J,Vector:()=>u,ZERO:()=>B,diff:()=>I,fract:()=>L,intersection:()=>_,isvector:()=>c,resolve:()=>X,vec:()=>h,vecadd:()=>z,vecconfig:()=>k,veccopy:()=>G,veccross:()=>V,vecdir:()=>F,vecdist:()=>W,vecdist2:()=>$,vecdiv:()=>y,vecdot:()=>U,veceq:()=>q,veclerp:()=>Z,veclimit:()=>S,vecmag:()=>O,vecmag2:()=>b,vecmult:()=>M,vecnorm:()=>R,vecrand:()=>v,vecrot:()=>N,vecset:()=>A,vecsub:()=>H,wave:()=>P});var I=(e,t)=>Math.abs(t-e)||0;var L=e=>e%1||0;var P=(e,t,s,i=Math.sin)=>e+(i(s)+1)/2*(t-e);var _=(e,t,s,i,r,o,n,a)=>{let l=Math.max(e,r),E=Math.min(e+s,r+n)-l,d=Math.max(t,o),g=Math.min(t+i,o+a)-d;return[l,d,E,g]};var X=(e,t,s,i,r,o,n,a)=>{let[l,E,d,g]=_(e,t,s,i,r,o,n,a),x="",m=e,w=t;return d<g?e<r?(x="right",m=r-s):(x="left",m=r+n):t<o?(x="bottom",w=o-i):(x="top",w=o+a),{direction:x,x:m,y:w}};var f=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(t=null){t=t||globalThis,this._engine=t,this.size(t.WIDTH||0,t.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}size(t,s){this.width=t,this.height=s}start(t=!1){let s=this.width/2,i=this.height/2;this._engine.push(),this._engine.translate(s,i),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y),t&&this._engine.cliprect(this.x,this.y,this.width,this.height)}end(){this._engine.pop()}lookAt(t,s){this.x=t,this.y=s}move(t,s){this.x+=t,this.y+=s}zoom(t){this.scale*=t}zoomTo(t){this.scale=t}rotate(t){this.rotation+=t}rotateTo(t){this.rotation=t}shake(t=.3,s=1){this.shaking()||(this._shake.removeListener=this._engine.listen("update",i=>{this._shake.x=this._engine.randi(-s,s),this._shake.y=this._engine.randi(-s,s),t-=i,t<=0&&this.unshake()}))}unshake(){this.shaking()&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}shaking(){return this._shake.removeListener!==null}};var p=class e{_w;_h;_c;static fromArray(t,s,i){let r=new e(t,s);for(let o=0;o<i.length;o++)r._c[o]=i[o];return r}constructor(t,s){this.width=t,this.height=s,this.clear()}clear(){this._c=Array(this._w*this._h)}set width(t){this._w=Math.max(1,~~t)}get width(){return this._w}set height(t){this._h=Math.max(1,~~t)}get height(){return this._h}set(t,s,i){this._c[this.pointToIndex(t,s)]=i}get(t,s){return this._c[this.pointToIndex(t,s)]}has(t,s){return this.get(t,s)!=null}get length(){return this._w*this._h}pointToIndex(t,s){return this.clampX(~~t)+this.clampY(~~s)*this._w}indexToPointX(t){return t%this._w}indexToPointY(t){return Math.floor(t/this._w)}forEach(t,s=!1){let i=s?this.length-1:0,r=s?-1:this.length,o=s?-1:1;for(;i!==r;){let n=this.indexToPointX(i),a=this.indexToPointY(i),l=this._c[i];if(t(n,a,l,this)===!1)break;i+=o}}fill(t){this.forEach((s,i)=>{this.set(s,i,t)})}clone(){return e.fromArray(this._w,this._h,this._c)}clampX(t){return Y(t,0,this._w-1)}clampY(t){return Y(t,0,this._h-1)}toArray(){return this._c.slice(0)}toString(t=" ",s=!0){if(!s)return this._c.join(t);let i=[];return this.forEach((r,o,n)=>{i[o]=i[o]||"",i[o]+=n+t}),i.join(`
|
|
2
|
+
`)}};function Y(e,t,s){return e<t?t:e>s?s:e}var u=class{x;y;constructor(t=0,s=t){this.x=t,this.y=s}toString(){return`Vector (${this.x}, ${this.y})`}},h=(e=0,t=e)=>new u(e,t),q=(e,t,s=t)=>c(t)?q(e,t.x,t.y):e.x===t&&e.y===s,G=e=>h(e.x,e.y),A=(e,t,s=t)=>{c(t)?A(e,t.x,t.y):(e.x=t,e.y=s)},z=(e,t,s=t)=>{c(t)?z(e,t.x,t.y):(e.x+=t,e.y+=s)},H=(e,t,s=t)=>{c(t)?H(e,t.x,t.y):(e.x-=t,e.y-=s)},M=(e,t,s=t)=>{c(t)?M(e,t.x,t.y):(e.x*=t,e.y*=s)},y=(e,t,s=t)=>{c(t)?y(e,t.x,t.y):(e.x/=t,e.y/=s)},N=(e,t)=>{let s=Math.cos(t),i=Math.sin(t);e.x=s*e.x-i*e.y,e.y=i*e.x+s*e.y},O=e=>Math.sqrt(e.x*e.x+e.y*e.y),b=e=>e.x*e.x+e.y*e.y,R=e=>{let t=O(e);t>0&&y(e,t)},S=(e,t)=>{let s=b(e);s>t*t&&(y(e,Math.sqrt(s)),M(e,t))},W=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return Math.sqrt(s*s+i*i)},$=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return s*s+i*i},F=e=>Math.atan2(e.y,e.x),U=(e,t)=>e.x*t.x+e.y*t.y,V=(e,t)=>e.x*t.y-e.y*t.x,Z=(e,t,s)=>{e.x+=(t.x-e.x)*s||0,e.y+=(t.y-e.y)*s||0},v=(e=1,t=e)=>{let s=k.random()*2*Math.PI,i=k.random()*(t-e)+e;return h(Math.cos(s)*i,Math.sin(s)*i)},c=e=>e instanceof u,k={random:()=>globalThis.rand?rand():Math.random()},B=h(0,0),C=h(1,1),J=h(0,-1),K=h(1,0),Q=h(0,1),tt=h(-1,0);globalThis.utils=T;})();
|
package/dist/camera.js
CHANGED
|
@@ -27,6 +27,26 @@
|
|
|
27
27
|
engine = engine || globalThis;
|
|
28
28
|
this._engine = engine;
|
|
29
29
|
this.size(engine.WIDTH || 0, engine.HEIGHT || 0);
|
|
30
|
+
this.x = this.width / 2;
|
|
31
|
+
this.y = this.height / 2;
|
|
32
|
+
}
|
|
33
|
+
size(width, height) {
|
|
34
|
+
this.width = width;
|
|
35
|
+
this.height = height;
|
|
36
|
+
}
|
|
37
|
+
start(clip = false) {
|
|
38
|
+
const centerX = this.width / 2, centerY = this.height / 2;
|
|
39
|
+
this._engine.push();
|
|
40
|
+
this._engine.translate(centerX, centerY);
|
|
41
|
+
this._engine.scale(this.scale);
|
|
42
|
+
this._engine.rotate(this.rotation);
|
|
43
|
+
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
|
|
44
|
+
if (clip) {
|
|
45
|
+
this._engine.cliprect(this.x, this.y, this.width, this.height);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
end() {
|
|
49
|
+
this._engine.pop();
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* @param {number} x
|
|
@@ -77,22 +97,6 @@
|
|
|
77
97
|
shaking() {
|
|
78
98
|
return this._shake.removeListener !== null;
|
|
79
99
|
}
|
|
80
|
-
size(width, height) {
|
|
81
|
-
this.width = width;
|
|
82
|
-
this.height = height;
|
|
83
|
-
}
|
|
84
|
-
start(clip = false) {
|
|
85
|
-
this._engine.push();
|
|
86
|
-
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
|
|
87
|
-
this._engine.scale(this.scale);
|
|
88
|
-
this._engine.rotate(this.rotation);
|
|
89
|
-
if (clip) {
|
|
90
|
-
this._engine.cliprect(this.x, this.y, this.width, this.height);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
end() {
|
|
94
|
-
this._engine.pop();
|
|
95
|
-
}
|
|
96
100
|
};
|
|
97
101
|
|
|
98
102
|
// src/camera/_web.js
|
package/dist/camera.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var i=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(
|
|
1
|
+
(()=>{var i=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(t=null){t=t||globalThis,this._engine=t,this.size(t.WIDTH||0,t.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}size(t,s){this.width=t,this.height=s}start(t=!1){let s=this.width/2,h=this.height/2;this._engine.push(),this._engine.translate(s,h),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y),t&&this._engine.cliprect(this.x,this.y,this.width,this.height)}end(){this._engine.pop()}lookAt(t,s){this.x=t,this.y=s}move(t,s){this.x+=t,this.y+=s}zoom(t){this.scale*=t}zoomTo(t){this.scale=t}rotate(t){this.rotation+=t}rotateTo(t){this.rotation=t}shake(t=.3,s=1){this.shaking()||(this._shake.removeListener=this._engine.listen("update",h=>{this._shake.x=this._engine.randi(-s,s),this._shake.y=this._engine.randi(-s,s),t-=h,t<=0&&this.unshake()}))}unshake(){this.shaking()&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}shaking(){return this._shake.removeListener!==null}};globalThis.utils=Object.assign(globalThis.utils||{},{Camera:i});})();
|
package/dist/grid.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
// src/grid/index.js
|
|
3
|
+
var Grid = class _Grid {
|
|
4
|
+
/** @type {number} The grid width */
|
|
5
|
+
_w;
|
|
6
|
+
/** @type {number} The grid height */
|
|
7
|
+
_h;
|
|
8
|
+
/** @type {any[]} The grid cells */
|
|
9
|
+
_c;
|
|
10
|
+
/**
|
|
11
|
+
* @static
|
|
12
|
+
* @param {number} width
|
|
13
|
+
* @param {number} height
|
|
14
|
+
* @param {any[]} values
|
|
15
|
+
*/
|
|
16
|
+
static fromArray(width, height, values) {
|
|
17
|
+
const grid = new _Grid(width, height);
|
|
18
|
+
for (let i = 0; i < values.length; i++) {
|
|
19
|
+
grid._c[i] = values[i];
|
|
20
|
+
}
|
|
21
|
+
return grid;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @param {number} width The grid width
|
|
25
|
+
* @param {number} height The grid height
|
|
26
|
+
*/
|
|
27
|
+
constructor(width, height) {
|
|
28
|
+
this.width = width;
|
|
29
|
+
this.height = height;
|
|
30
|
+
this.clear();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Delete all cell values.
|
|
34
|
+
*/
|
|
35
|
+
clear() {
|
|
36
|
+
this._c = Array(this._w * this._h);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @param {number} value
|
|
40
|
+
*/
|
|
41
|
+
set width(value) {
|
|
42
|
+
this._w = Math.max(1, ~~value);
|
|
43
|
+
}
|
|
44
|
+
get width() {
|
|
45
|
+
return this._w;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @param {number} value
|
|
49
|
+
*/
|
|
50
|
+
set height(value) {
|
|
51
|
+
this._h = Math.max(1, ~~value);
|
|
52
|
+
}
|
|
53
|
+
get height() {
|
|
54
|
+
return this._h;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The the value of a grid's cell.
|
|
58
|
+
*
|
|
59
|
+
* @param {number} x
|
|
60
|
+
* @param {number} y
|
|
61
|
+
* @param {any} value
|
|
62
|
+
*/
|
|
63
|
+
set(x, y, value) {
|
|
64
|
+
this._c[this.pointToIndex(x, y)] = value;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the value of a grid's cell.
|
|
68
|
+
*
|
|
69
|
+
* @param {number} x
|
|
70
|
+
* @param {number} y
|
|
71
|
+
* @returns {any}
|
|
72
|
+
*/
|
|
73
|
+
get(x, y) {
|
|
74
|
+
return this._c[this.pointToIndex(x, y)];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns true if the which cell has any value not equal to `null` or `undefined`.
|
|
78
|
+
*
|
|
79
|
+
* @param {number} x
|
|
80
|
+
* @param {number} y
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
has(x, y) {
|
|
84
|
+
return this.get(x, y) != null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the total of cells.
|
|
88
|
+
*
|
|
89
|
+
* @returns {number}
|
|
90
|
+
*/
|
|
91
|
+
get length() {
|
|
92
|
+
return this._w * this._h;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Convert a grid point (X, Y) to a index.
|
|
96
|
+
*
|
|
97
|
+
* @param {number} x
|
|
98
|
+
* @param {number} y
|
|
99
|
+
* @returns {number} The index
|
|
100
|
+
*/
|
|
101
|
+
pointToIndex(x, y) {
|
|
102
|
+
return this.clampX(~~x) + this.clampY(~~y) * this._w;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convert index to a grid point X.
|
|
106
|
+
*
|
|
107
|
+
* @param {number} index
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
indexToPointX(index) {
|
|
111
|
+
return index % this._w;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert index to a grid point Y.
|
|
115
|
+
*
|
|
116
|
+
* @param {number} index
|
|
117
|
+
* @returns {number}
|
|
118
|
+
*/
|
|
119
|
+
indexToPointY(index) {
|
|
120
|
+
return Math.floor(index / this._w);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Loops over all grid cells.
|
|
124
|
+
*
|
|
125
|
+
* @callback GridForEachCallback
|
|
126
|
+
* @param {number} x
|
|
127
|
+
* @param {number} y
|
|
128
|
+
* @param {any} value
|
|
129
|
+
* @param {Grid} grid
|
|
130
|
+
* @returns {boolean?} returns `false` to stop/break the loop
|
|
131
|
+
*
|
|
132
|
+
* @param {GridForEachCallback} handler
|
|
133
|
+
* @param {boolean} [reverse=false]
|
|
134
|
+
*/
|
|
135
|
+
forEach(handler, reverse = false) {
|
|
136
|
+
let i = reverse ? this.length - 1 : 0, limit = reverse ? -1 : this.length, step = reverse ? -1 : 1;
|
|
137
|
+
while (i !== limit) {
|
|
138
|
+
const x = this.indexToPointX(i), y = this.indexToPointY(i), cellValue = this._c[i];
|
|
139
|
+
if (false === handler(x, y, cellValue, this)) break;
|
|
140
|
+
i += step;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @param {*} value
|
|
145
|
+
*/
|
|
146
|
+
fill(value) {
|
|
147
|
+
this.forEach((x, y) => {
|
|
148
|
+
this.set(x, y, value);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* @returns {Grid} the cloned grid
|
|
153
|
+
*/
|
|
154
|
+
clone() {
|
|
155
|
+
return _Grid.fromArray(this._w, this._h, this._c);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @param {number} y
|
|
159
|
+
* @returns {number}
|
|
160
|
+
*/
|
|
161
|
+
clampX(x) {
|
|
162
|
+
return _clamp(x, 0, this._w - 1);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* @param {number} y
|
|
166
|
+
* @returns {number}
|
|
167
|
+
*/
|
|
168
|
+
clampY(y) {
|
|
169
|
+
return _clamp(y, 0, this._h - 1);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Returns the cell values in a single array.
|
|
173
|
+
*
|
|
174
|
+
* @returns {any[]}
|
|
175
|
+
*/
|
|
176
|
+
toArray() {
|
|
177
|
+
return this._c.slice(0);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* @param {string} separator
|
|
181
|
+
* @param {boolean} format
|
|
182
|
+
* @returns {string}
|
|
183
|
+
*/
|
|
184
|
+
toString(separator = " ", format = true) {
|
|
185
|
+
if (!format) return this._c.join(separator);
|
|
186
|
+
const rows = [];
|
|
187
|
+
this.forEach((x, y, value) => {
|
|
188
|
+
rows[y] = rows[y] || "";
|
|
189
|
+
rows[y] += value + separator;
|
|
190
|
+
});
|
|
191
|
+
return rows.join("\n");
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
function _clamp(value, min, max) {
|
|
195
|
+
if (value < min) return min;
|
|
196
|
+
if (value > max) return max;
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/grid/_web.js
|
|
201
|
+
globalThis.utils = Object.assign(globalThis.utils || {}, { Grid });
|
|
202
|
+
})();
|
package/dist/grid.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(()=>{var e=class s{_w;_h;_c;static fromArray(t,i,h){let n=new s(t,i);for(let r=0;r<h.length;r++)n._c[r]=h[r];return n}constructor(t,i){this.width=t,this.height=i,this.clear()}clear(){this._c=Array(this._w*this._h)}set width(t){this._w=Math.max(1,~~t)}get width(){return this._w}set height(t){this._h=Math.max(1,~~t)}get height(){return this._h}set(t,i,h){this._c[this.pointToIndex(t,i)]=h}get(t,i){return this._c[this.pointToIndex(t,i)]}has(t,i){return this.get(t,i)!=null}get length(){return this._w*this._h}pointToIndex(t,i){return this.clampX(~~t)+this.clampY(~~i)*this._w}indexToPointX(t){return t%this._w}indexToPointY(t){return Math.floor(t/this._w)}forEach(t,i=!1){let h=i?this.length-1:0,n=i?-1:this.length,r=i?-1:1;for(;h!==n;){let o=this.indexToPointX(h),c=this.indexToPointY(h),_=this._c[h];if(t(o,c,_,this)===!1)break;h+=r}}fill(t){this.forEach((i,h)=>{this.set(i,h,t)})}clone(){return s.fromArray(this._w,this._h,this._c)}clampX(t){return l(t,0,this._w-1)}clampY(t){return l(t,0,this._h-1)}toArray(){return this._c.slice(0)}toString(t=" ",i=!0){if(!i)return this._c.join(t);let h=[];return this.forEach((n,r,o)=>{h[r]=h[r]||"",h[r]+=o+t}),h.join(`
|
|
2
|
+
`)}};function l(s,t,i){return s<t?t:s>i?i:s}globalThis.utils=Object.assign(globalThis.utils||{},{Grid:e});})();
|
package/dist/vector.js
CHANGED
package/dist/vector.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var M=Object.defineProperty;var g=(t,o)=>{for(var s in o)M(t,s,{get:o[s],enumerable:!0})};var i={};g(i,{DOWN:()=>G,LEFT:()=>H,ONE:()=>w,RIGHT:()=>F,UP:()=>D,Vector:()=>n,ZERO:()=>j,isvector:()=>r,vec:()=>c,vecadd:()=>l,vecconfig:()=>
|
|
1
|
+
(()=>{var M=Object.defineProperty;var g=(t,o)=>{for(var s in o)M(t,s,{get:o[s],enumerable:!0})};var i={};g(i,{DOWN:()=>G,LEFT:()=>H,ONE:()=>w,RIGHT:()=>F,UP:()=>D,Vector:()=>n,ZERO:()=>j,isvector:()=>r,vec:()=>c,vecadd:()=>l,vecconfig:()=>y,veccopy:()=>m,veccross:()=>R,vecdir:()=>N,vecdist:()=>E,vecdist2:()=>I,vecdiv:()=>x,vecdot:()=>P,veceq:()=>a,veclerp:()=>U,veclimit:()=>O,vecmag:()=>u,vecmag2:()=>f,vecmult:()=>p,vecnorm:()=>T,vecrand:()=>$,vecrot:()=>q,vecset:()=>d,vecsub:()=>h});var n=class{x;y;constructor(o=0,s=o){this.x=o,this.y=s}toString(){return`Vector (${this.x}, ${this.y})`}},c=(t=0,o=t)=>new n(t,o),a=(t,o,s=o)=>r(o)?a(t,o.x,o.y):t.x===o&&t.y===s,m=t=>c(t.x,t.y),d=(t,o,s=o)=>{r(o)?d(t,o.x,o.y):(t.x=o,t.y=s)},l=(t,o,s=o)=>{r(o)?l(t,o.x,o.y):(t.x+=o,t.y+=s)},h=(t,o,s=o)=>{r(o)?h(t,o.x,o.y):(t.x-=o,t.y-=s)},p=(t,o,s=o)=>{r(o)?p(t,o.x,o.y):(t.x*=o,t.y*=s)},x=(t,o,s=o)=>{r(o)?x(t,o.x,o.y):(t.x/=o,t.y/=s)},q=(t,o)=>{let s=Math.cos(o),e=Math.sin(o);t.x=s*t.x-e*t.y,t.y=e*t.x+s*t.y},u=t=>Math.sqrt(t.x*t.x+t.y*t.y),f=t=>t.x*t.x+t.y*t.y,T=t=>{let o=u(t);o>0&&x(t,o)},O=(t,o)=>{let s=f(t);s>o*o&&(x(t,Math.sqrt(s)),p(t,o))},E=(t,o)=>{let s=t.x-o.x,e=t.y-o.y;return Math.sqrt(s*s+e*e)},I=(t,o)=>{let s=t.x-o.x,e=t.y-o.y;return s*s+e*e},N=t=>Math.atan2(t.y,t.x),P=(t,o)=>t.x*o.x+t.y*o.y,R=(t,o)=>t.x*o.y-t.y*o.x,U=(t,o,s)=>{t.x+=(o.x-t.x)*s||0,t.y+=(o.y-t.y)*s||0},$=(t=1,o=t)=>{let s=y.random()*2*Math.PI,e=y.random()*(o-t)+t;return c(Math.cos(s)*e,Math.sin(s)*e)},r=t=>t instanceof n,y={random:()=>globalThis.rand?rand():Math.random()},j=c(0,0),w=c(1,1),D=c(0,-1),F=c(1,0),G=c(0,1),H=c(-1,0);globalThis.utils=Object.assign(globalThis.utils||{},i);})();
|
package/package.json
CHANGED
package/src/camera/index.js
CHANGED
|
@@ -29,6 +29,32 @@ export default class Camera {
|
|
|
29
29
|
engine = engine || globalThis
|
|
30
30
|
this._engine = engine
|
|
31
31
|
this.size(engine.WIDTH || 0, engine.HEIGHT || 0)
|
|
32
|
+
this.x = this.width / 2
|
|
33
|
+
this.y = this.height / 2
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
size(width, height) {
|
|
37
|
+
this.width = width
|
|
38
|
+
this.height = height
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
start(clip = false) {
|
|
42
|
+
const centerX = this.width / 2,
|
|
43
|
+
centerY = this.height / 2
|
|
44
|
+
|
|
45
|
+
this._engine.push()
|
|
46
|
+
this._engine.translate(centerX, centerY)
|
|
47
|
+
this._engine.scale(this.scale)
|
|
48
|
+
this._engine.rotate(this.rotation)
|
|
49
|
+
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y)
|
|
50
|
+
|
|
51
|
+
if (clip) {
|
|
52
|
+
this._engine.cliprect(this.x, this.y, this.width, this.height)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
end() {
|
|
57
|
+
this._engine.pop()
|
|
32
58
|
}
|
|
33
59
|
|
|
34
60
|
/**
|
|
@@ -89,23 +115,4 @@ export default class Camera {
|
|
|
89
115
|
shaking() {
|
|
90
116
|
return this._shake.removeListener !== null
|
|
91
117
|
}
|
|
92
|
-
|
|
93
|
-
size(width, height) {
|
|
94
|
-
this.width = width
|
|
95
|
-
this.height = height
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
start(clip = false) {
|
|
99
|
-
this._engine.push()
|
|
100
|
-
this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y)
|
|
101
|
-
this._engine.scale(this.scale)
|
|
102
|
-
this._engine.rotate(this.rotation)
|
|
103
|
-
if (clip) {
|
|
104
|
-
this._engine.cliprect(this.x, this.y, this.width, this.height)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
end() {
|
|
109
|
-
this._engine.pop()
|
|
110
|
-
}
|
|
111
118
|
}
|
package/src/collision/README.md
CHANGED
|
@@ -40,6 +40,8 @@ function draw() {
|
|
|
40
40
|
|
|
41
41
|
Syntax: `resolve(x1, y1, w1, h1, x2, y2, w2, h2): { direction: string, x: number, y: number }`
|
|
42
42
|
|
|
43
|
+
> Note: possible values for `direction` is `"top"`, `"botton"`, `"left"`, `"right"` or `""` (empty string, if no collision).
|
|
44
|
+
|
|
43
45
|
```js
|
|
44
46
|
import litecanvas from "litecanvas"
|
|
45
47
|
import { resolve } from "@litecanvas/utils"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Grid
|
|
2
|
+
|
|
3
|
+
**CDN**: https://unpkg.com/@litecanvas/utils/dist/grid.js
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Lets build an arena with [ASCII graphics](https://en.wikipedia.org/wiki/ASCII_art) like in classic roguelikes.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// make a grid 13x13
|
|
11
|
+
let grid = new utils.Grid(13, 13)
|
|
12
|
+
|
|
13
|
+
// fill the entire grid with "."
|
|
14
|
+
grid.fill(".")
|
|
15
|
+
|
|
16
|
+
// put a '@' in the middle
|
|
17
|
+
grid.set(grid.width / 2, grid.height / 2, "@")
|
|
18
|
+
|
|
19
|
+
// put '#' around the grid
|
|
20
|
+
grid.forEach((x, y, cellValue) => {
|
|
21
|
+
if (x === 0 || y === 0 || x === grid.width - 1 || y === grid.height - 1) {
|
|
22
|
+
grid.set(x, y, "#")
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
document.body.innerHTML = "<pre>" + grid.toString() + "</pre>"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The result:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# # # # # # # # # # # # #
|
|
33
|
+
# . . . . . . . . . . . #
|
|
34
|
+
# . . . . . . . . . . . #
|
|
35
|
+
# . . . . . . . . . . . #
|
|
36
|
+
# . . . . . . . . . . . #
|
|
37
|
+
# . . . . . . . . . . . #
|
|
38
|
+
# . . . . . @ . . . . . #
|
|
39
|
+
# . . . . . . . . . . . #
|
|
40
|
+
# . . . . . . . . . . . #
|
|
41
|
+
# . . . . . . . . . . . #
|
|
42
|
+
# . . . . . . . . . . . #
|
|
43
|
+
# . . . . . . . . . . . #
|
|
44
|
+
# # # # # # # # # # # # #
|
|
45
|
+
```
|
package/src/grid/_web.js
ADDED
package/src/grid/index.js
CHANGED
|
@@ -1,91 +1,239 @@
|
|
|
1
|
-
class Grid {
|
|
1
|
+
export default class Grid {
|
|
2
|
+
/** @type {number} The grid width */
|
|
3
|
+
_w
|
|
4
|
+
|
|
5
|
+
/** @type {number} The grid height */
|
|
6
|
+
_h
|
|
7
|
+
|
|
8
|
+
/** @type {any[]} The grid cells */
|
|
9
|
+
_c
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @static
|
|
13
|
+
* @param {number} width
|
|
14
|
+
* @param {number} height
|
|
15
|
+
* @param {any[]} values
|
|
16
|
+
*/
|
|
17
|
+
static fromArray(width, height, values) {
|
|
18
|
+
const grid = new Grid(width, height)
|
|
19
|
+
for (let i = 0; i < values.length; i++) {
|
|
20
|
+
grid._c[i] = values[i]
|
|
21
|
+
}
|
|
22
|
+
return grid
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {number} width The grid width
|
|
27
|
+
* @param {number} height The grid height
|
|
28
|
+
*/
|
|
2
29
|
constructor(width, height) {
|
|
3
|
-
this.
|
|
4
|
-
this.
|
|
30
|
+
this.width = width
|
|
31
|
+
this.height = height
|
|
5
32
|
this.clear()
|
|
6
33
|
}
|
|
7
34
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Delete all cell values.
|
|
37
|
+
*/
|
|
38
|
+
clear() {
|
|
39
|
+
this._c = Array(this._w * this._h)
|
|
11
40
|
}
|
|
12
41
|
|
|
13
|
-
|
|
14
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @param {number} value
|
|
44
|
+
*/
|
|
45
|
+
set width(value) {
|
|
46
|
+
this._w = Math.max(1, ~~value)
|
|
15
47
|
}
|
|
16
48
|
|
|
17
|
-
|
|
18
|
-
return this.
|
|
49
|
+
get width() {
|
|
50
|
+
return this._w
|
|
19
51
|
}
|
|
20
52
|
|
|
21
|
-
|
|
22
|
-
|
|
53
|
+
/**
|
|
54
|
+
* @param {number} value
|
|
55
|
+
*/
|
|
56
|
+
set height(value) {
|
|
57
|
+
this._h = Math.max(1, ~~value)
|
|
23
58
|
}
|
|
24
59
|
|
|
25
60
|
get height() {
|
|
26
|
-
return this.
|
|
61
|
+
return this._h
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The the value of a grid's cell.
|
|
66
|
+
*
|
|
67
|
+
* @param {number} x
|
|
68
|
+
* @param {number} y
|
|
69
|
+
* @param {any} value
|
|
70
|
+
*/
|
|
71
|
+
set(x, y, value) {
|
|
72
|
+
this._c[this.pointToIndex(x, y)] = value
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns the value of a grid's cell.
|
|
77
|
+
*
|
|
78
|
+
* @param {number} x
|
|
79
|
+
* @param {number} y
|
|
80
|
+
* @returns {any}
|
|
81
|
+
*/
|
|
82
|
+
get(x, y) {
|
|
83
|
+
return this._c[this.pointToIndex(x, y)]
|
|
27
84
|
}
|
|
28
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Returns true if the which cell has any value not equal to `null` or `undefined`.
|
|
88
|
+
*
|
|
89
|
+
* @param {number} x
|
|
90
|
+
* @param {number} y
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
has(x, y) {
|
|
94
|
+
return this.get(x, y) != null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Returns the total of cells.
|
|
99
|
+
*
|
|
100
|
+
* @returns {number}
|
|
101
|
+
*/
|
|
29
102
|
get length() {
|
|
30
|
-
return this.
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
103
|
+
return this._w * this._h
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Convert a grid point (X, Y) to a index.
|
|
108
|
+
*
|
|
109
|
+
* @param {number} x
|
|
110
|
+
* @param {number} y
|
|
111
|
+
* @returns {number} The index
|
|
112
|
+
*/
|
|
113
|
+
pointToIndex(x, y) {
|
|
114
|
+
return this.clampX(~~x) + this.clampY(~~y) * this._w
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Convert index to a grid point X.
|
|
119
|
+
*
|
|
120
|
+
* @param {number} index
|
|
121
|
+
* @returns {number}
|
|
122
|
+
*/
|
|
123
|
+
indexToPointX(index) {
|
|
124
|
+
return index % this._w
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Convert index to a grid point Y.
|
|
129
|
+
*
|
|
130
|
+
* @param {number} index
|
|
131
|
+
* @returns {number}
|
|
132
|
+
*/
|
|
133
|
+
indexToPointY(index) {
|
|
134
|
+
return Math.floor(index / this._w)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Loops over all grid cells.
|
|
139
|
+
*
|
|
140
|
+
* @callback GridForEachCallback
|
|
141
|
+
* @param {number} x
|
|
142
|
+
* @param {number} y
|
|
143
|
+
* @param {any} value
|
|
144
|
+
* @param {Grid} grid
|
|
145
|
+
* @returns {boolean?} returns `false` to stop/break the loop
|
|
146
|
+
*
|
|
147
|
+
* @param {GridForEachCallback} handler
|
|
148
|
+
* @param {boolean} [reverse=false]
|
|
149
|
+
*/
|
|
150
|
+
forEach(handler, reverse = false) {
|
|
151
|
+
let i = reverse ? this.length - 1 : 0,
|
|
152
|
+
limit = reverse ? -1 : this.length,
|
|
153
|
+
step = reverse ? -1 : 1
|
|
154
|
+
|
|
155
|
+
while (i !== limit) {
|
|
156
|
+
const x = this.indexToPointX(i),
|
|
157
|
+
y = this.indexToPointY(i),
|
|
158
|
+
cellValue = this._c[i]
|
|
159
|
+
|
|
160
|
+
if (false === handler(x, y, cellValue, this)) break
|
|
161
|
+
|
|
162
|
+
i += step
|
|
52
163
|
}
|
|
53
|
-
return this
|
|
54
164
|
}
|
|
55
165
|
|
|
56
|
-
|
|
57
|
-
|
|
166
|
+
/**
|
|
167
|
+
* @param {*} value
|
|
168
|
+
*/
|
|
169
|
+
fill(value) {
|
|
170
|
+
this.forEach((x, y) => {
|
|
171
|
+
this.set(x, y, value)
|
|
172
|
+
})
|
|
58
173
|
}
|
|
59
174
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
175
|
+
/**
|
|
176
|
+
* @returns {Grid} the cloned grid
|
|
177
|
+
*/
|
|
178
|
+
clone() {
|
|
179
|
+
return Grid.fromArray(this._w, this._h, this._c)
|
|
63
180
|
}
|
|
64
181
|
|
|
65
|
-
|
|
66
|
-
|
|
182
|
+
/**
|
|
183
|
+
* @param {number} y
|
|
184
|
+
* @returns {number}
|
|
185
|
+
*/
|
|
186
|
+
clampX(x) {
|
|
187
|
+
return _clamp(x, 0, this._w - 1)
|
|
67
188
|
}
|
|
68
189
|
|
|
69
|
-
|
|
70
|
-
|
|
190
|
+
/**
|
|
191
|
+
* @param {number} y
|
|
192
|
+
* @returns {number}
|
|
193
|
+
*/
|
|
194
|
+
clampY(y) {
|
|
195
|
+
return _clamp(y, 0, this._h - 1)
|
|
71
196
|
}
|
|
72
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Returns the cell values in a single array.
|
|
200
|
+
*
|
|
201
|
+
* @returns {any[]}
|
|
202
|
+
*/
|
|
73
203
|
toArray() {
|
|
74
|
-
return this.
|
|
204
|
+
return this._c.slice(0)
|
|
75
205
|
}
|
|
76
206
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
207
|
+
/**
|
|
208
|
+
* @param {string} separator
|
|
209
|
+
* @param {boolean} format
|
|
210
|
+
* @returns {string}
|
|
211
|
+
*/
|
|
212
|
+
toString(separator = " ", format = true) {
|
|
213
|
+
if (!format) return this._c.join(separator)
|
|
80
214
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
215
|
+
const rows = []
|
|
216
|
+
this.forEach((x, y, value) => {
|
|
217
|
+
rows[y] = rows[y] || ""
|
|
218
|
+
rows[y] += value + separator
|
|
219
|
+
})
|
|
87
220
|
|
|
88
|
-
|
|
89
|
-
return Math.min(Math.max(n, min), max)
|
|
221
|
+
return rows.join("\n")
|
|
90
222
|
}
|
|
91
223
|
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Constrains a number between `min` and `max`.
|
|
227
|
+
*
|
|
228
|
+
* @param {number} value
|
|
229
|
+
* @param {number} min
|
|
230
|
+
* @param {number} max
|
|
231
|
+
* @returns {number}
|
|
232
|
+
*/
|
|
233
|
+
function _clamp(value, min, max) {
|
|
234
|
+
if (value < min) return min
|
|
235
|
+
if (value > max) return max
|
|
236
|
+
return value
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function _fill(x, y) {}
|
package/src/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { default as wave } from "./math/wave.js"
|
|
|
4
4
|
export { default as resolve } from "./collision/resolve.js"
|
|
5
5
|
export { default as intersection } from "./collision/intersection.js"
|
|
6
6
|
export { default as Camera } from "./camera/index.js"
|
|
7
|
+
export { default as Grid } from "./grid/index.js"
|
|
7
8
|
export * from "./vector/index.js"
|