littlejsengine 1.12.6 → 1.13.4
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 +15 -12
- package/dist/littlejs.d.ts +466 -236
- package/dist/littlejs.esm.js +6126 -5370
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +6093 -5359
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5614 -4907
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/gameObjects.js +8 -7
- package/examples/box2d/index.html +1 -1
- package/examples/box2d/scenes.js +8 -8
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/README.md +46 -43
- package/examples/breakoutTutorial/game.js +3 -3
- package/examples/breakoutTutorial/index.html +1 -1
- package/examples/electron/build.bat +7 -0
- package/examples/electron/build.js +124 -0
- package/examples/electron/electron.js +35 -0
- package/examples/electron/game.js +140 -0
- package/examples/electron/index.html +13 -0
- package/examples/electron/package.json +12 -0
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +1 -0
- package/examples/empty/index.html +1 -1
- package/examples/htmlMenu/game.js +1 -1
- package/examples/htmlMenu/index.html +1 -1
- package/examples/index.html +486 -182
- package/examples/module/game.js +6 -3
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +12 -3
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameCharacter.js +6 -6
- package/examples/platformer/gameEffects.js +74 -57
- package/examples/platformer/gameLevel.js +61 -70
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/shorts/base.html +23 -3
- package/examples/shorts/blending.js +9 -5
- package/examples/shorts/box2d.js +1 -1
- package/examples/shorts/box2dCar.js +9 -13
- package/examples/shorts/clock.js +1 -1
- package/examples/shorts/colors.js +2 -2
- package/examples/shorts/flappyGame.js +52 -0
- package/examples/shorts/helloWorld.js +6 -3
- package/examples/shorts/hillGlideGame.js +56 -0
- package/examples/shorts/landerGame.js +32 -0
- package/examples/shorts/maze.js +47 -0
- package/examples/shorts/medals.js +42 -0
- package/examples/shorts/nineSlice.js +21 -0
- package/examples/shorts/piano.js +52 -0
- package/examples/shorts/platformer.js +24 -20
- package/examples/shorts/{pong.js → pongGame.js} +1 -1
- package/examples/shorts/raycasting.js +71 -0
- package/examples/shorts/shapes.js +9 -9
- package/examples/shorts/slidingPuzzle.js +42 -0
- package/examples/shorts/spaceGame.js +56 -0
- package/examples/shorts/starfield.js +15 -0
- package/examples/shorts/systemFont.js +1 -1
- package/examples/shorts/tileLayer.js +3 -5
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +8 -7
- package/examples/shorts/topDown.js +18 -26
- package/examples/shorts/uiSystem.js +5 -7
- package/examples/starter/build.bat +6 -1
- package/examples/starter/build.js +9 -8
- package/examples/starter/game.js +7 -4
- package/examples/starter/index.html +23 -13
- package/examples/stress/index.html +7 -8
- package/examples/style.css +139 -0
- package/examples/typescript/build.js +2 -3
- package/examples/typescript/game.js +4 -4
- package/examples/typescript/game.ts +6 -3
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +10 -25
- package/examples/uiSystem/index.html +1 -1
- package/package.json +2 -2
- package/plugins/box2d.js +22 -97
- package/plugins/drawUtilities.js +132 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +7 -1
- package/plugins/postProcess.js +9 -2
- package/plugins/uiSystem.js +155 -67
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +56 -30
- package/src/engineAudio.js +15 -6
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +20 -5
- package/src/engineDebug.js +55 -28
- package/src/engineDraw.js +321 -177
- package/src/engineExport.js +27 -9
- package/src/engineInput.js +110 -38
- package/src/engineMedals.js +4 -4
- package/src/engineObject.js +71 -70
- package/src/engineParticles.js +33 -9
- package/src/engineSettings.js +69 -23
- package/src/engineTileLayer.js +312 -153
- package/src/engineUtilities.js +187 -135
- package/src/engineWebGL.js +70 -39
package/src/engineUtilities.js
CHANGED
|
@@ -22,19 +22,17 @@ const PI = Math.PI;
|
|
|
22
22
|
* @memberof Utilities */
|
|
23
23
|
function abs(value) { return Math.abs(value); }
|
|
24
24
|
|
|
25
|
-
/** Returns lowest
|
|
26
|
-
* @param {number}
|
|
27
|
-
* @param {number} valueB
|
|
25
|
+
/** Returns lowest value passed in
|
|
26
|
+
* @param {...number} values
|
|
28
27
|
* @return {number}
|
|
29
28
|
* @memberof Utilities */
|
|
30
|
-
function min(
|
|
29
|
+
function min(...values) { return Math.min(...values); }
|
|
31
30
|
|
|
32
|
-
/** Returns highest
|
|
33
|
-
* @param {number}
|
|
34
|
-
* @param {number} valueB
|
|
31
|
+
/** Returns highest value passed in
|
|
32
|
+
* @param {...number} values
|
|
35
33
|
* @return {number}
|
|
36
34
|
* @memberof Utilities */
|
|
37
|
-
function max(
|
|
35
|
+
function max(...values) { return Math.max(...values); }
|
|
38
36
|
|
|
39
37
|
/** Returns the sign of value passed in
|
|
40
38
|
* @param {number} value
|
|
@@ -67,12 +65,17 @@ function percent(value, valueA, valueB)
|
|
|
67
65
|
{ return (valueB-=valueA) ? clamp((value-valueA)/valueB) : 0; }
|
|
68
66
|
|
|
69
67
|
/** Linearly interpolates between values passed in using percent
|
|
70
|
-
* @param {number} percent
|
|
71
68
|
* @param {number} valueA
|
|
72
69
|
* @param {number} valueB
|
|
70
|
+
* @param {number} percent
|
|
73
71
|
* @return {number}
|
|
74
72
|
* @memberof Utilities */
|
|
75
|
-
function lerp(
|
|
73
|
+
function lerp(valueA, valueB, percent)
|
|
74
|
+
{
|
|
75
|
+
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
76
|
+
console.warn('lerp() parameter order changed! use lerp(start, end, p)');
|
|
77
|
+
return valueA + clamp(percent) * (valueB-valueA);
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
/** Returns signed wrapped distance between the two values passed in
|
|
78
81
|
* @param {number} valueA
|
|
@@ -84,14 +87,18 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
84
87
|
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
85
88
|
|
|
86
89
|
/** Linearly interpolates between values passed in with wrapping
|
|
87
|
-
* @param {number} percent
|
|
88
90
|
* @param {number} valueA
|
|
89
91
|
* @param {number} valueB
|
|
92
|
+
* @param {number} percent
|
|
90
93
|
* @param {number} [wrapSize]
|
|
91
94
|
* @returns {number}
|
|
92
95
|
* @memberof Utilities */
|
|
93
|
-
function lerpWrap(
|
|
94
|
-
{
|
|
96
|
+
function lerpWrap(valueA, valueB, percent, wrapSize=1)
|
|
97
|
+
{
|
|
98
|
+
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
99
|
+
console.warn('lerpWrap() parameter order changed! use lerpWrap(start, end, p)');
|
|
100
|
+
return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize);
|
|
101
|
+
}
|
|
95
102
|
|
|
96
103
|
/** Returns signed wrapped distance between the two angles passed in
|
|
97
104
|
* @param {number} angleA
|
|
@@ -101,12 +108,12 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
101
108
|
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
102
109
|
|
|
103
110
|
/** Linearly interpolates between the angles passed in with wrapping
|
|
104
|
-
* @param {number} percent
|
|
105
111
|
* @param {number} angleA
|
|
106
112
|
* @param {number} angleB
|
|
113
|
+
* @param {number} percent
|
|
107
114
|
* @returns {number}
|
|
108
115
|
* @memberof Utilities */
|
|
109
|
-
function lerpAngle(
|
|
116
|
+
function lerpAngle(angleA, angleB, percent) { return lerpWrap(angleA, angleB, percent, 2*PI); }
|
|
110
117
|
|
|
111
118
|
/** Applies smoothstep function to the percentage value
|
|
112
119
|
* @param {number} percent
|
|
@@ -114,17 +121,24 @@ function lerpAngle(percent, angleA, angleB) { return lerpWrap(percent, angleA, a
|
|
|
114
121
|
* @memberof Utilities */
|
|
115
122
|
function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
116
123
|
|
|
124
|
+
/** Checks if the value passed in is a power of two
|
|
125
|
+
* @param {number} value
|
|
126
|
+
* @return {boolean}
|
|
127
|
+
* @memberof Utilities */
|
|
128
|
+
function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
129
|
+
|
|
117
130
|
/** Returns the nearest power of two not less then the value
|
|
118
131
|
* @param {number} value
|
|
119
132
|
* @return {number}
|
|
120
133
|
* @memberof Utilities */
|
|
121
134
|
function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
122
135
|
|
|
123
|
-
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
136
|
+
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
137
|
+
* this can be used for simple collision detection between objects
|
|
124
138
|
* @param {Vector2} posA - Center of box A
|
|
125
139
|
* @param {Vector2} sizeA - Size of box A
|
|
126
140
|
* @param {Vector2} posB - Center of box B
|
|
127
|
-
* @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
|
|
141
|
+
* @param {Vector2} [sizeB=(0,0)] - Size of box B, uses a point if undefined
|
|
128
142
|
* @return {boolean} - True if overlapping
|
|
129
143
|
* @memberof Utilities */
|
|
130
144
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
@@ -178,10 +192,11 @@ function isIntersecting(start, end, pos, size)
|
|
|
178
192
|
* @param {number} [frequency] - Frequency of the wave in Hz
|
|
179
193
|
* @param {number} [amplitude] - Amplitude (max height) of the wave
|
|
180
194
|
* @param {number} [t=time] - Value to use for time of the wave
|
|
195
|
+
* @param {number} [offset] - Value to use for time offset of the wave
|
|
181
196
|
* @return {number} - Value waving between 0 and amplitude
|
|
182
197
|
* @memberof Utilities */
|
|
183
|
-
function wave(frequency=1, amplitude=1, t=time)
|
|
184
|
-
{ return amplitude/2 * (1 - Math.cos(t*frequency*2*PI)); }
|
|
198
|
+
function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
199
|
+
{ return amplitude/2 * (1 - Math.cos(offset + t*frequency*2*PI)); }
|
|
185
200
|
|
|
186
201
|
/** Formats seconds to mm:ss style for display purposes
|
|
187
202
|
* @param {number} t - time in seconds
|
|
@@ -199,6 +214,14 @@ async function fetchJSON(url)
|
|
|
199
214
|
return response.json();
|
|
200
215
|
}
|
|
201
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Check if object is a valid number, not NaN or undefined, but it may be infinite
|
|
219
|
+
* @param {any} n
|
|
220
|
+
* @return {boolean}
|
|
221
|
+
* @memberof Utilities
|
|
222
|
+
*/
|
|
223
|
+
function isNumber(n) { return typeof n == 'number' && !isNaN(n); }
|
|
224
|
+
|
|
202
225
|
///////////////////////////////////////////////////////////////////////////////
|
|
203
226
|
|
|
204
227
|
/** Random global functions
|
|
@@ -219,6 +242,12 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
219
242
|
* @memberof Random */
|
|
220
243
|
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
221
244
|
|
|
245
|
+
/** Randomly returns true or false given the chance of true passed in
|
|
246
|
+
* @param {number} [chance]
|
|
247
|
+
* @return {boolean}
|
|
248
|
+
* @memberof Random */
|
|
249
|
+
function randBool(chance=.5) { return rand() < chance; }
|
|
250
|
+
|
|
222
251
|
/** Randomly returns either -1 or 1
|
|
223
252
|
* @return {number}
|
|
224
253
|
* @memberof Random */
|
|
@@ -228,7 +257,7 @@ function randSign() { return randInt(2) * 2 - 1; }
|
|
|
228
257
|
* @param {number} [length]
|
|
229
258
|
* @return {Vector2}
|
|
230
259
|
* @memberof Random */
|
|
231
|
-
function
|
|
260
|
+
function randVec2(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
232
261
|
|
|
233
262
|
/** Returns a random Vector2 within a circular shape
|
|
234
263
|
* @param {number} [radius]
|
|
@@ -236,7 +265,7 @@ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length
|
|
|
236
265
|
* @return {Vector2}
|
|
237
266
|
* @memberof Random */
|
|
238
267
|
function randInCircle(radius=1, minRadius=0)
|
|
239
|
-
{ return radius > 0 ?
|
|
268
|
+
{ return radius > 0 ? randVec2(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
240
269
|
|
|
241
270
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
242
271
|
* @param {Color} [colorA=(1,1,1,1)]
|
|
@@ -265,8 +294,8 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
265
294
|
class RandomGenerator
|
|
266
295
|
{
|
|
267
296
|
/** Create a random number generator with the seed passed in
|
|
268
|
-
* @param {number} seed - Starting seed */
|
|
269
|
-
constructor(seed)
|
|
297
|
+
* @param {number} [seed] - Starting seed or engine default seed */
|
|
298
|
+
constructor(seed = 123456789)
|
|
270
299
|
{
|
|
271
300
|
/** @property {number} - random seed */
|
|
272
301
|
this.seed = seed;
|
|
@@ -291,6 +320,11 @@ class RandomGenerator
|
|
|
291
320
|
* @return {number} */
|
|
292
321
|
int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
|
|
293
322
|
|
|
323
|
+
/** Randomly returns true or false given the chance of true passed in
|
|
324
|
+
* @param {number} [chance]
|
|
325
|
+
* @return {boolean} */
|
|
326
|
+
bool(chance=.5) { return this.float() < chance; }
|
|
327
|
+
|
|
294
328
|
/** Randomly returns either -1 or 1 deterministically
|
|
295
329
|
* @return {number} */
|
|
296
330
|
sign() { return this.float() > .5 ? 1 : -1; }
|
|
@@ -300,11 +334,22 @@ class RandomGenerator
|
|
|
300
334
|
* @param {number} [valueB]
|
|
301
335
|
* @return {number} */
|
|
302
336
|
floatSign(valueA=1, valueB=0) { return this.float(valueA, valueB) * this.sign(); }
|
|
337
|
+
|
|
338
|
+
/** Returns a random angle between -PI and PI
|
|
339
|
+
* @return {number} */
|
|
340
|
+
angle() { return this.float(-PI, PI); }
|
|
341
|
+
|
|
342
|
+
/** Returns a seeded vec2 with size between the two values passed in
|
|
343
|
+
* @param {number} valueA
|
|
344
|
+
* @param {number} [valueB]
|
|
345
|
+
* @return {Vector2} */
|
|
346
|
+
vec2(valueA=1, valueB=0)
|
|
347
|
+
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
303
348
|
}
|
|
304
349
|
|
|
305
350
|
///////////////////////////////////////////////////////////////////////////////
|
|
306
351
|
|
|
307
|
-
/**
|
|
352
|
+
/**
|
|
308
353
|
* Create a 2d vector, can take 1 or 2 scalar values
|
|
309
354
|
* @param {number} [x]
|
|
310
355
|
* @param {number} [y] - if y is undefined, x is used for both
|
|
@@ -315,7 +360,7 @@ class RandomGenerator
|
|
|
315
360
|
* b = vec2(); // set b to (0, 0)
|
|
316
361
|
* @memberof Utilities
|
|
317
362
|
*/
|
|
318
|
-
function vec2(x=0, y) { return new Vector2(x, y
|
|
363
|
+
function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
|
|
319
364
|
|
|
320
365
|
/**
|
|
321
366
|
* Check if object is a valid Vector2
|
|
@@ -325,6 +370,15 @@ function vec2(x=0, y) { return new Vector2(x, y == undefined? x : y); }
|
|
|
325
370
|
*/
|
|
326
371
|
function isVector2(v) { return v instanceof Vector2; }
|
|
327
372
|
|
|
373
|
+
// vector2 asserts
|
|
374
|
+
function ASSERT_VECTOR2_VALID(v) { ASSERT(isVector2(v) && v.isValid(), 'Vector2 is invalid.', v); }
|
|
375
|
+
function ASSERT_NUMBER_VALID(n) { ASSERT(isNumber(n), 'Number is invalid.', n); }
|
|
376
|
+
function ASSERT_VECTOR2_NORMAL(v)
|
|
377
|
+
{
|
|
378
|
+
ASSERT_VECTOR2_VALID(v);
|
|
379
|
+
ASSERT(abs(v.lengthSquared()-1) < .01, 'Vector2 is not normal.', v);
|
|
380
|
+
}
|
|
381
|
+
|
|
328
382
|
/**
|
|
329
383
|
* 2D Vector object with vector math library
|
|
330
384
|
* - Functions do not change this so they can be chained together
|
|
@@ -345,7 +399,7 @@ class Vector2
|
|
|
345
399
|
this.x = x;
|
|
346
400
|
/** @property {number} - Y axis location */
|
|
347
401
|
this.y = y;
|
|
348
|
-
ASSERT(this.isValid());
|
|
402
|
+
ASSERT(this.isValid(), 'Constructed Vector2 is invalid.', this);
|
|
349
403
|
}
|
|
350
404
|
|
|
351
405
|
/** Sets values of this vector and returns self
|
|
@@ -356,7 +410,6 @@ class Vector2
|
|
|
356
410
|
{
|
|
357
411
|
this.x = x;
|
|
358
412
|
this.y = y;
|
|
359
|
-
ASSERT(this.isValid());
|
|
360
413
|
return this;
|
|
361
414
|
}
|
|
362
415
|
|
|
@@ -367,47 +420,27 @@ class Vector2
|
|
|
367
420
|
/** Returns a copy of this vector plus the vector passed in
|
|
368
421
|
* @param {Vector2} v - other vector
|
|
369
422
|
* @return {Vector2} */
|
|
370
|
-
add(v)
|
|
371
|
-
{
|
|
372
|
-
ASSERT(isVector2(v));
|
|
373
|
-
return new Vector2(this.x + v.x, this.y + v.y);
|
|
374
|
-
}
|
|
423
|
+
add(v) { return new Vector2(this.x + v.x, this.y + v.y);}
|
|
375
424
|
|
|
376
425
|
/** Returns a copy of this vector minus the vector passed in
|
|
377
426
|
* @param {Vector2} v - other vector
|
|
378
427
|
* @return {Vector2} */
|
|
379
|
-
subtract(v)
|
|
380
|
-
{
|
|
381
|
-
ASSERT(isVector2(v));
|
|
382
|
-
return new Vector2(this.x - v.x, this.y - v.y);
|
|
383
|
-
}
|
|
428
|
+
subtract(v) { return new Vector2(this.x - v.x, this.y - v.y); }
|
|
384
429
|
|
|
385
430
|
/** Returns a copy of this vector times the vector passed in
|
|
386
431
|
* @param {Vector2} v - other vector
|
|
387
432
|
* @return {Vector2} */
|
|
388
|
-
multiply(v)
|
|
389
|
-
{
|
|
390
|
-
ASSERT(isVector2(v));
|
|
391
|
-
return new Vector2(this.x * v.x, this.y * v.y);
|
|
392
|
-
}
|
|
433
|
+
multiply(v) { return new Vector2(this.x * v.x, this.y * v.y); }
|
|
393
434
|
|
|
394
435
|
/** Returns a copy of this vector divided by the vector passed in
|
|
395
436
|
* @param {Vector2} v - other vector
|
|
396
437
|
* @return {Vector2} */
|
|
397
|
-
divide(v)
|
|
398
|
-
{
|
|
399
|
-
ASSERT(isVector2(v));
|
|
400
|
-
return new Vector2(this.x / v.x, this.y / v.y);
|
|
401
|
-
}
|
|
438
|
+
divide(v) { return new Vector2(this.x / v.x, this.y / v.y); }
|
|
402
439
|
|
|
403
440
|
/** Returns a copy of this vector scaled by the vector passed in
|
|
404
441
|
* @param {number} s - scale
|
|
405
442
|
* @return {Vector2} */
|
|
406
|
-
scale(s)
|
|
407
|
-
{
|
|
408
|
-
ASSERT(!isVector2(s));
|
|
409
|
-
return new Vector2(this.x * s, this.y * s);
|
|
410
|
-
}
|
|
443
|
+
scale(s) { return new Vector2(this.x * s, this.y * s); }
|
|
411
444
|
|
|
412
445
|
/** Returns the length of this vector
|
|
413
446
|
* @return {number} */
|
|
@@ -420,20 +453,12 @@ class Vector2
|
|
|
420
453
|
/** Returns the distance from this vector to vector passed in
|
|
421
454
|
* @param {Vector2} v - other vector
|
|
422
455
|
* @return {number} */
|
|
423
|
-
distance(v)
|
|
424
|
-
{
|
|
425
|
-
ASSERT(isVector2(v));
|
|
426
|
-
return this.distanceSquared(v)**.5;
|
|
427
|
-
}
|
|
456
|
+
distance(v) { return this.distanceSquared(v)**.5; }
|
|
428
457
|
|
|
429
458
|
/** Returns the distance squared from this vector to vector passed in
|
|
430
459
|
* @param {Vector2} v - other vector
|
|
431
460
|
* @return {number} */
|
|
432
|
-
distanceSquared(v)
|
|
433
|
-
{
|
|
434
|
-
ASSERT(isVector2(v));
|
|
435
|
-
return (this.x - v.x)**2 + (this.y - v.y)**2;
|
|
436
|
-
}
|
|
461
|
+
distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
|
|
437
462
|
|
|
438
463
|
/** Returns a new vector in same direction as this one with the length passed in
|
|
439
464
|
* @param {number} [length]
|
|
@@ -456,20 +481,19 @@ class Vector2
|
|
|
456
481
|
/** Returns the dot product of this and the vector passed in
|
|
457
482
|
* @param {Vector2} v - other vector
|
|
458
483
|
* @return {number} */
|
|
459
|
-
dot(v)
|
|
460
|
-
{
|
|
461
|
-
ASSERT(isVector2(v));
|
|
462
|
-
return this.x*v.x + this.y*v.y;
|
|
463
|
-
}
|
|
484
|
+
dot(v) { return this.x*v.x + this.y*v.y; }
|
|
464
485
|
|
|
465
486
|
/** Returns the cross product of this and the vector passed in
|
|
466
487
|
* @param {Vector2} v - other vector
|
|
467
488
|
* @return {number} */
|
|
468
|
-
cross(v)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
489
|
+
cross(v) { return this.x*v.y - this.y*v.x; }
|
|
490
|
+
|
|
491
|
+
/** Returns a copy this vector reflected by the surface normal
|
|
492
|
+
* @param {Vector2} normal - surface normal (should be normalized)
|
|
493
|
+
* @param {number} restitution - how much to bounce, 1 is perfect bounce, 0 is no bounce
|
|
494
|
+
* @return {Vector2} */
|
|
495
|
+
reflect(normal, restitution=1)
|
|
496
|
+
{ return this.subtract(normal.scale((1+restitution)*this.dot(normal))); }
|
|
473
497
|
|
|
474
498
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
475
499
|
* @return {number} */
|
|
@@ -481,6 +505,8 @@ class Vector2
|
|
|
481
505
|
* @return {Vector2} */
|
|
482
506
|
setAngle(angle=0, length=1)
|
|
483
507
|
{
|
|
508
|
+
ASSERT_NUMBER_VALID(angle);
|
|
509
|
+
ASSERT_NUMBER_VALID(length);
|
|
484
510
|
this.x = length*Math.sin(angle);
|
|
485
511
|
this.y = length*Math.cos(angle);
|
|
486
512
|
return this;
|
|
@@ -490,7 +516,8 @@ class Vector2
|
|
|
490
516
|
* @param {number} angle
|
|
491
517
|
* @return {Vector2} */
|
|
492
518
|
rotate(angle)
|
|
493
|
-
{
|
|
519
|
+
{
|
|
520
|
+
ASSERT_NUMBER_VALID(angle);
|
|
494
521
|
const c = Math.cos(-angle), s = Math.sin(-angle);
|
|
495
522
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
496
523
|
}
|
|
@@ -500,8 +527,11 @@ class Vector2
|
|
|
500
527
|
* @param {number} [length] */
|
|
501
528
|
setDirection(direction, length=1)
|
|
502
529
|
{
|
|
530
|
+
ASSERT_NUMBER_VALID(direction);
|
|
531
|
+
ASSERT_NUMBER_VALID(length);
|
|
503
532
|
direction = mod(direction, 4);
|
|
504
|
-
ASSERT(direction==0 || direction==1 || direction==2 || direction==3
|
|
533
|
+
ASSERT(direction==0 || direction==1 || direction==2 || direction==3,
|
|
534
|
+
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
505
535
|
return vec2(direction%2 ? direction-1 ? -length : length : 0,
|
|
506
536
|
direction%2 ? 0 : direction ? -length : length);
|
|
507
537
|
}
|
|
@@ -515,10 +545,20 @@ class Vector2
|
|
|
515
545
|
* @return {Vector2} */
|
|
516
546
|
invert() { return new Vector2(this.y, -this.x); }
|
|
517
547
|
|
|
548
|
+
/** Returns a copy of this vector absolute values
|
|
549
|
+
* @return {Vector2} */
|
|
550
|
+
abs() { return new Vector2(abs(this.x), abs(this.y)); }
|
|
551
|
+
|
|
518
552
|
/** Returns a copy of this vector with each axis floored
|
|
519
553
|
* @return {Vector2} */
|
|
520
554
|
floor() { return new Vector2(Math.floor(this.x), Math.floor(this.y)); }
|
|
521
555
|
|
|
556
|
+
/** Returns new vec2 with modded values
|
|
557
|
+
* @param {number} [divisor]
|
|
558
|
+
* @return {Vector2} */
|
|
559
|
+
mod(divisor=1)
|
|
560
|
+
{ return new Vector2(mod(this.x, divisor), mod(this.y, divisor)); }
|
|
561
|
+
|
|
522
562
|
/** Returns the area this vector covers as a rectangle
|
|
523
563
|
* @return {number} */
|
|
524
564
|
area() { return abs(this.x * this.y); }
|
|
@@ -529,35 +569,36 @@ class Vector2
|
|
|
529
569
|
* @return {Vector2} */
|
|
530
570
|
lerp(v, percent)
|
|
531
571
|
{
|
|
532
|
-
|
|
533
|
-
|
|
572
|
+
ASSERT_VECTOR2_VALID(v);
|
|
573
|
+
ASSERT_NUMBER_VALID(percent);
|
|
574
|
+
const p = clamp(percent);
|
|
575
|
+
return new Vector2(v.x*p + this.x*(1-p), v.y*p + this.y*(1-p));
|
|
534
576
|
}
|
|
535
577
|
|
|
536
578
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
537
579
|
* @param {Vector2} arraySize
|
|
538
580
|
* @return {boolean} */
|
|
539
581
|
arrayCheck(arraySize)
|
|
540
|
-
{
|
|
541
|
-
ASSERT(isVector2(arraySize));
|
|
542
|
-
return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y;
|
|
543
|
-
}
|
|
582
|
+
{ return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
544
583
|
|
|
545
584
|
/** Returns this vector expressed as a string
|
|
546
585
|
* @param {number} digits - precision to display
|
|
547
586
|
* @return {string} */
|
|
548
587
|
toString(digits=3)
|
|
549
588
|
{
|
|
589
|
+
ASSERT_NUMBER_VALID(digits);
|
|
550
590
|
if (debug)
|
|
551
|
-
|
|
591
|
+
{
|
|
592
|
+
if (this.isValid())
|
|
593
|
+
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
594
|
+
else
|
|
595
|
+
return `(${this.x}, ${this.y})`;
|
|
596
|
+
}
|
|
552
597
|
}
|
|
553
598
|
|
|
554
599
|
/** Checks if this is a valid vector
|
|
555
600
|
* @return {boolean} */
|
|
556
|
-
isValid()
|
|
557
|
-
{
|
|
558
|
-
return typeof this.x == 'number' && !isNaN(this.x)
|
|
559
|
-
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
560
|
-
}
|
|
601
|
+
isValid() { return isNumber(this.x) && isNumber(this.y); }
|
|
561
602
|
}
|
|
562
603
|
|
|
563
604
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -592,13 +633,16 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
|
592
633
|
*/
|
|
593
634
|
function isColor(c) { return c instanceof Color; }
|
|
594
635
|
|
|
636
|
+
// color asserts
|
|
637
|
+
function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c) && c.isValid(), 'Color is invalid.', c); }
|
|
638
|
+
|
|
595
639
|
/**
|
|
596
640
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
597
641
|
* @example
|
|
598
642
|
* let a = new Color; // white
|
|
599
643
|
* let b = new Color(1, 0, 0); // red
|
|
600
644
|
* let c = new Color(0, 0, 0, 0); // transparent black
|
|
601
|
-
* let d = rgb(0, 0, 1);
|
|
645
|
+
* let d = rgb(0, 0, 1); // blue using rgb color
|
|
602
646
|
* let e = hsl(.3, 1, .5); // green using hsl color
|
|
603
647
|
*/
|
|
604
648
|
class Color
|
|
@@ -618,7 +662,7 @@ class Color
|
|
|
618
662
|
this.b = b;
|
|
619
663
|
/** @property {number} - Alpha */
|
|
620
664
|
this.a = a;
|
|
621
|
-
ASSERT(this.isValid());
|
|
665
|
+
ASSERT(this.isValid(), 'Constructed Color is invalid.', this);
|
|
622
666
|
}
|
|
623
667
|
|
|
624
668
|
/** Sets values of this color and returns self
|
|
@@ -633,7 +677,6 @@ class Color
|
|
|
633
677
|
this.g = g;
|
|
634
678
|
this.b = b;
|
|
635
679
|
this.a = a;
|
|
636
|
-
ASSERT(this.isValid());
|
|
637
680
|
return this;
|
|
638
681
|
}
|
|
639
682
|
|
|
@@ -644,38 +687,22 @@ class Color
|
|
|
644
687
|
/** Returns a copy of this color plus the color passed in
|
|
645
688
|
* @param {Color} c - other color
|
|
646
689
|
* @return {Color} */
|
|
647
|
-
add(c)
|
|
648
|
-
{
|
|
649
|
-
ASSERT(isColor(c));
|
|
650
|
-
return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a);
|
|
651
|
-
}
|
|
690
|
+
add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
|
|
652
691
|
|
|
653
692
|
/** Returns a copy of this color minus the color passed in
|
|
654
693
|
* @param {Color} c - other color
|
|
655
694
|
* @return {Color} */
|
|
656
|
-
subtract(c)
|
|
657
|
-
{
|
|
658
|
-
ASSERT(isColor(c));
|
|
659
|
-
return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a);
|
|
660
|
-
}
|
|
695
|
+
subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
|
|
661
696
|
|
|
662
697
|
/** Returns a copy of this color times the color passed in
|
|
663
698
|
* @param {Color} c - other color
|
|
664
699
|
* @return {Color} */
|
|
665
|
-
multiply(c)
|
|
666
|
-
{
|
|
667
|
-
ASSERT(isColor(c));
|
|
668
|
-
return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a);
|
|
669
|
-
}
|
|
700
|
+
multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
|
|
670
701
|
|
|
671
702
|
/** Returns a copy of this color divided by the color passed in
|
|
672
703
|
* @param {Color} c - other color
|
|
673
704
|
* @return {Color} */
|
|
674
|
-
divide(c)
|
|
675
|
-
{
|
|
676
|
-
ASSERT(isColor(c));
|
|
677
|
-
return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a);
|
|
678
|
-
}
|
|
705
|
+
divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
|
|
679
706
|
|
|
680
707
|
/** Returns a copy of this color scaled by the value passed in, alpha can be scaled separately
|
|
681
708
|
* @param {number} scale
|
|
@@ -694,8 +721,14 @@ class Color
|
|
|
694
721
|
* @return {Color} */
|
|
695
722
|
lerp(c, percent)
|
|
696
723
|
{
|
|
697
|
-
|
|
698
|
-
|
|
724
|
+
ASSERT_COLOR_VALID(c);
|
|
725
|
+
ASSERT_NUMBER_VALID(percent);
|
|
726
|
+
const p = clamp(percent);
|
|
727
|
+
return new Color(
|
|
728
|
+
c.r*p + this.r*(1-p),
|
|
729
|
+
c.g*p + this.g*(1-p),
|
|
730
|
+
c.b*p + this.b*(1-p),
|
|
731
|
+
c.a*p + this.a*(1-p));
|
|
699
732
|
}
|
|
700
733
|
|
|
701
734
|
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
@@ -718,7 +751,7 @@ class Color
|
|
|
718
751
|
this.g = f(p, q, h);
|
|
719
752
|
this.b = f(p, q, h - 1/3);
|
|
720
753
|
this.a = a;
|
|
721
|
-
|
|
754
|
+
ASSERT_COLOR_VALID(this);
|
|
722
755
|
return this;
|
|
723
756
|
}
|
|
724
757
|
|
|
@@ -730,20 +763,19 @@ class Color
|
|
|
730
763
|
const g = clamp(this.g);
|
|
731
764
|
const b = clamp(this.b);
|
|
732
765
|
const a = clamp(this.a);
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
const l = (
|
|
736
|
-
|
|
766
|
+
const maxC = max(r, g, b);
|
|
767
|
+
const minC = min(r, g, b);
|
|
768
|
+
const l = (maxC + minC) / 2;
|
|
737
769
|
let h = 0, s = 0;
|
|
738
|
-
if (
|
|
770
|
+
if (maxC != minC)
|
|
739
771
|
{
|
|
740
|
-
let d =
|
|
741
|
-
s = l > .5 ? d / (2 -
|
|
742
|
-
if (r ==
|
|
772
|
+
let d = maxC - minC;
|
|
773
|
+
s = l > .5 ? d / (2 - maxC - minC) : d / (maxC + minC);
|
|
774
|
+
if (r == maxC)
|
|
743
775
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
744
|
-
else if (g ==
|
|
776
|
+
else if (g == maxC)
|
|
745
777
|
h = (b - r) / d + 2;
|
|
746
|
-
else if (b ==
|
|
778
|
+
else if (b == maxC)
|
|
747
779
|
h = (r - g) / d + 4;
|
|
748
780
|
}
|
|
749
781
|
return [h / 6, s, l, a];
|
|
@@ -755,6 +787,8 @@ class Color
|
|
|
755
787
|
* @return {Color} */
|
|
756
788
|
mutate(amount=.05, alphaAmount=0)
|
|
757
789
|
{
|
|
790
|
+
ASSERT_NUMBER_VALID(amount);
|
|
791
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
758
792
|
return new Color
|
|
759
793
|
(
|
|
760
794
|
this.r + rand(amount, -amount),
|
|
@@ -768,7 +802,10 @@ class Color
|
|
|
768
802
|
* @param {boolean} [useAlpha] - if alpha should be included in result
|
|
769
803
|
* @return {string} */
|
|
770
804
|
toString(useAlpha = true)
|
|
771
|
-
{
|
|
805
|
+
{
|
|
806
|
+
ASSERT(typeof useAlpha == 'boolean', 'Use alpha boolean is invalid.', useAlpha);
|
|
807
|
+
if (debug && !this.isValid())
|
|
808
|
+
return `#000`;
|
|
772
809
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
773
810
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
774
811
|
}
|
|
@@ -778,7 +815,7 @@ class Color
|
|
|
778
815
|
* @return {Color} */
|
|
779
816
|
setHex(hex)
|
|
780
817
|
{
|
|
781
|
-
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
818
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#', 'Color hex code must be a string starting with #');
|
|
782
819
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
783
820
|
|
|
784
821
|
if (hex.length < 6)
|
|
@@ -798,7 +835,7 @@ class Color
|
|
|
798
835
|
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
799
836
|
}
|
|
800
837
|
|
|
801
|
-
|
|
838
|
+
ASSERT_COLOR_VALID(this);
|
|
802
839
|
return this;
|
|
803
840
|
}
|
|
804
841
|
|
|
@@ -816,12 +853,7 @@ class Color
|
|
|
816
853
|
/** Checks if this is a valid color
|
|
817
854
|
* @return {boolean} */
|
|
818
855
|
isValid()
|
|
819
|
-
{
|
|
820
|
-
return typeof this.r == 'number' && !isNaN(this.r)
|
|
821
|
-
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
822
|
-
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
823
|
-
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
824
|
-
}
|
|
856
|
+
{ return isNumber(this.r) && isNumber(this.g) && isNumber(this.b) && isNumber(this.a); }
|
|
825
857
|
}
|
|
826
858
|
|
|
827
859
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -832,11 +864,21 @@ class Color
|
|
|
832
864
|
* @memberof Utilities */
|
|
833
865
|
const WHITE = rgb();
|
|
834
866
|
|
|
867
|
+
/** Color - Clear White #ffffff with 0 alpha
|
|
868
|
+
* @type {Color}
|
|
869
|
+
* @memberof Utilities */
|
|
870
|
+
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
871
|
+
|
|
835
872
|
/** Color - Black #000000
|
|
836
873
|
* @type {Color}
|
|
837
874
|
* @memberof Utilities */
|
|
838
875
|
const BLACK = rgb(0,0,0);
|
|
839
876
|
|
|
877
|
+
/** Color - Clear Black #000000 with 0 alpha
|
|
878
|
+
* @type {Color}
|
|
879
|
+
* @memberof Utilities */
|
|
880
|
+
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
881
|
+
|
|
840
882
|
/** Color - Gray #808080
|
|
841
883
|
* @type {Color}
|
|
842
884
|
* @memberof Utilities */
|
|
@@ -897,18 +939,28 @@ class Timer
|
|
|
897
939
|
{
|
|
898
940
|
/** Create a timer object set time passed in
|
|
899
941
|
* @param {number} [timeLeft] - How much time left before the timer elapses in seconds */
|
|
900
|
-
constructor(timeLeft)
|
|
942
|
+
constructor(timeLeft)
|
|
943
|
+
{
|
|
944
|
+
ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
|
|
945
|
+
this.time = timeLeft === undefined ? undefined : time + timeLeft;
|
|
946
|
+
this.setTime = timeLeft;
|
|
947
|
+
}
|
|
901
948
|
|
|
902
949
|
/** Set the timer with seconds passed in
|
|
903
950
|
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
904
|
-
set(timeLeft=0)
|
|
951
|
+
set(timeLeft=0)
|
|
952
|
+
{
|
|
953
|
+
ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
|
|
954
|
+
this.time = time + timeLeft;
|
|
955
|
+
this.setTime = timeLeft;
|
|
956
|
+
}
|
|
905
957
|
|
|
906
958
|
/** Unset the timer */
|
|
907
959
|
unset() { this.time = undefined; }
|
|
908
960
|
|
|
909
961
|
/** Returns true if set
|
|
910
962
|
* @return {boolean} */
|
|
911
|
-
isSet() { return this.time
|
|
963
|
+
isSet() { return this.time !== undefined; }
|
|
912
964
|
|
|
913
965
|
/** Returns true if set and has not elapsed
|
|
914
966
|
* @return {boolean} */
|
|
@@ -932,5 +984,5 @@ class Timer
|
|
|
932
984
|
|
|
933
985
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
934
986
|
* @return {number} */
|
|
935
|
-
valueOf()
|
|
987
|
+
valueOf() { return this.get(); }
|
|
936
988
|
}
|