litecanvas 0.207.2 → 0.208.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/dist/dist.dev.js CHANGED
@@ -115,7 +115,7 @@
115
115
  var assert = (condition, message = "Assertion failed") => {
116
116
  if (!condition) throw new Error("[Litecanvas] " + message);
117
117
  };
118
- var version = "0.207.2";
118
+ var version = "0.208.0";
119
119
  function litecanvas(settings = {}) {
120
120
  const root = window,
121
121
  math = Math,
@@ -146,7 +146,7 @@
146
146
  };
147
147
  DEV: assert(
148
148
  null == settings || "object" === typeof settings,
149
- "litecanvas() 1st parameter must be a object or null",
149
+ "litecanvas() 1st parameter must be a object",
150
150
  );
151
151
  settings = Object.assign(defaults, settings);
152
152
  let _loop = settings.loop,
@@ -192,11 +192,19 @@
192
192
  DEV: assert(isNumber(rads), "rad2deg() 1st parameter must be a number");
193
193
  return (180 / math.PI) * rads;
194
194
  },
195
+ mod(a, b) {
196
+ DEV: assert(isNumber(a), "mod() 1st parameter must be a number");
197
+ DEV: assert(
198
+ isNumber(b) && b >= 0,
199
+ "mod() 2nd parameter must be a non-negative number",
200
+ );
201
+ return ((a % b) + b) % b || 0;
202
+ },
195
203
  round: (n, precision = 0) => {
196
204
  DEV: assert(isNumber(n), "round() 1st parameter must be a number");
197
205
  DEV: assert(
198
206
  isNumber(precision) && precision >= 0,
199
- "round() 2nd parameter must be a positive number or zero",
207
+ "round() 2nd parameter must be a non-negative number",
200
208
  );
201
209
  if (!precision) {
202
210
  return math.round(n);
@@ -282,14 +290,14 @@
282
290
  rseed(value) {
283
291
  DEV: assert(
284
292
  isNumber(value) && value >= 0,
285
- "rseed() 1st parameter must be a positive integer or zero",
293
+ "rseed() 1st parameter must be a non-negative integer",
286
294
  );
287
295
  _rngSeed = ~~value;
288
296
  },
289
297
  cls(color) {
290
298
  DEV: assert(
291
299
  null == color || (isNumber(color) && color >= 0),
292
- "cls() 1st parameter must be a positive number or zero or undefined",
300
+ "cls() 1st parameter must be a non-negative number",
293
301
  );
294
302
  if (null == color) {
295
303
  _ctx.clearRect(0, 0, instance.W, instance.H);
@@ -306,11 +314,11 @@
306
314
  );
307
315
  DEV: assert(
308
316
  isNumber(height) && height >= 0,
309
- "rect() 4th parameter must be a positive number or zero",
317
+ "rect() 4th parameter must be a non-negative number",
310
318
  );
311
319
  DEV: assert(
312
320
  null == color || (isNumber(color) && color >= 0),
313
- "rect() 5th parameter must be a positive number or zero",
321
+ "rect() 5th parameter must be a non-negative number",
314
322
  );
315
323
  DEV: assert(
316
324
  null == radii ||
@@ -333,15 +341,15 @@
333
341
  DEV: assert(isNumber(y), "rectfill() 2nd parameter must be a number");
334
342
  DEV: assert(
335
343
  isNumber(width) && width >= 0,
336
- "rectfill() 3rd parameter must be a positive number or zero",
344
+ "rectfill() 3rd parameter must be a non-negative number",
337
345
  );
338
346
  DEV: assert(
339
347
  isNumber(height) && height >= 0,
340
- "rectfill() 4th parameter must be a positive number or zero",
348
+ "rectfill() 4th parameter must be a non-negative number",
341
349
  );
342
350
  DEV: assert(
343
351
  null == color || (isNumber(color) && color >= 0),
344
- "rectfill() 5th parameter must be a positive number or zero",
352
+ "rectfill() 5th parameter must be a non-negative number",
345
353
  );
346
354
  DEV: assert(
347
355
  null == radii ||
@@ -358,15 +366,15 @@
358
366
  DEV: assert(isNumber(y), "oval() 2nd parameter must be a number");
359
367
  DEV: assert(
360
368
  isNumber(radiusX) && radiusX >= 0,
361
- "oval() 3rd parameter must be a positive number or zero",
369
+ "oval() 3rd parameter must be a non-negative number",
362
370
  );
363
371
  DEV: assert(
364
372
  isNumber(radiusY) && radiusY >= 0,
365
- "oval() 4th parameter must be a positive number or zero",
373
+ "oval() 4th parameter must be a non-negative number",
366
374
  );
367
375
  DEV: assert(
368
376
  null == color || (isNumber(color) && color >= 0),
369
- "oval() 5th parameter must be a positive number or zero",
377
+ "oval() 5th parameter must be a non-negative number",
370
378
  );
371
379
  beginPath(_ctx);
372
380
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
@@ -377,15 +385,15 @@
377
385
  DEV: assert(isNumber(y), "ovalfill() 2nd parameter must be a number");
378
386
  DEV: assert(
379
387
  isNumber(radiusX) && radiusX >= 0,
380
- "ovalfill() 3rd parameter must be a positive number or zero",
388
+ "ovalfill() 3rd parameter must be a non-negative number",
381
389
  );
382
390
  DEV: assert(
383
391
  isNumber(radiusY) && radiusY >= 0,
384
- "ovalfill() 4th parameter must be a positive number or zero",
392
+ "ovalfill() 4th parameter must be a non-negative number",
385
393
  );
386
394
  DEV: assert(
387
395
  null == color || (isNumber(color) && color >= 0),
388
- "ovalfill() 5th parameter must be a positive number or zero",
396
+ "ovalfill() 5th parameter must be a non-negative number",
389
397
  );
390
398
  beginPath(_ctx);
391
399
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
@@ -396,11 +404,11 @@
396
404
  DEV: assert(isNumber(y), "circ() 2nd parameter must be a number");
397
405
  DEV: assert(
398
406
  isNumber(radius) && radius >= 0,
399
- "circ() 3rd parameter must be a positive number or zero",
407
+ "circ() 3rd parameter must be a non-negative number",
400
408
  );
401
409
  DEV: assert(
402
410
  null == color || (isNumber(color) && color >= 0),
403
- "circ() 4th parameter must be a positive number or zero",
411
+ "circ() 4th parameter must be a non-negative number",
404
412
  );
405
413
  instance.oval(x, y, radius, radius, color);
406
414
  },
@@ -409,11 +417,11 @@
409
417
  DEV: assert(isNumber(y), "circfill() 2nd parameter must be a number");
410
418
  DEV: assert(
411
419
  isNumber(radius) && radius >= 0,
412
- "circfill() 3rd parameter must be a positive number or zero",
420
+ "circfill() 3rd parameter must be a non-negative number",
413
421
  );
414
422
  DEV: assert(
415
423
  null == color || (isNumber(color) && color >= 0),
416
- "circfill() 4th parameter must be a positive number or zero",
424
+ "circfill() 4th parameter must be a non-negative number",
417
425
  );
418
426
  instance.ovalfill(x, y, radius, radius, color);
419
427
  },
@@ -428,10 +436,10 @@
428
436
  );
429
437
  beginPath(_ctx);
430
438
  for (let i = 0; i < points.length; i += 2) {
431
- if (0 === i) {
432
- _ctx.moveTo(~~points[i], ~~points[i + 1]);
433
- } else {
439
+ if (i) {
434
440
  _ctx.lineTo(~~points[i], ~~points[i + 1]);
441
+ } else {
442
+ _ctx.moveTo(~~points[i], ~~points[i + 1]);
435
443
  }
436
444
  }
437
445
  _ctx.lineTo(~~points[0], ~~points[1]);
@@ -441,19 +449,19 @@
441
449
  DEV: assert(isNumber(y1), "line() 2nd parameter must be a number");
442
450
  DEV: assert(
443
451
  isNumber(x2),
444
- "line() 3rd parameter must be a positive number or zero",
452
+ "line() 3rd parameter must be a non-negative number",
445
453
  );
446
454
  DEV: assert(
447
455
  isNumber(y2),
448
- "line() 4th parameter must be a positive number or zero",
456
+ "line() 4th parameter must be a non-negative number",
449
457
  );
450
458
  DEV: assert(
451
459
  null == color || (isNumber(color) && color >= 0),
452
- "line() 5th parameter must be a positive number or zero",
460
+ "line() 5th parameter must be a non-negative number",
453
461
  );
454
462
  beginPath(_ctx);
455
- let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0;
456
- let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0;
463
+ let xfix = _outline_fix && ~~x1 === ~~x2 ? 0.5 : 0;
464
+ let yfix = _outline_fix && ~~y1 === ~~y2 ? 0.5 : 0;
457
465
  _ctx.moveTo(~~x1 + xfix, ~~y1 + yfix);
458
466
  _ctx.lineTo(~~x2 + xfix, ~~y2 + yfix);
459
467
  instance.stroke(color);
@@ -461,10 +469,10 @@
461
469
  linewidth(value) {
462
470
  DEV: assert(
463
471
  isNumber(value) && value >= 0,
464
- "linewidth() 1st parameter must be a positive number or zero",
472
+ "linewidth() 1st parameter must be a non-negative integer",
465
473
  );
466
474
  _ctx.lineWidth = ~~value;
467
- _outline_fix = 0 === ~~value % 2 ? 0 : 0.5;
475
+ _outline_fix = ~~value % 2 ? 0.5 : 0;
468
476
  },
469
477
  linedash(segments, offset = 0) {
470
478
  DEV: assert(
@@ -483,7 +491,7 @@
483
491
  DEV: assert(isNumber(y), "text() 2nd parameter must be a number");
484
492
  DEV: assert(
485
493
  null == color || (isNumber(color) && color >= 0),
486
- "text() 4th parameter must be a positive number or zero",
494
+ "text() 4th parameter must be a non-negative number",
487
495
  );
488
496
  DEV: assert(
489
497
  "string" === typeof fontStyle,
@@ -617,8 +625,31 @@
617
625
  }
618
626
  return _ctx;
619
627
  },
620
- push() {
628
+ push(
629
+ translateX = 0,
630
+ translateY = translateX,
631
+ rotation = 0,
632
+ scaleX = 1,
633
+ scaleY = scaleX,
634
+ ) {
635
+ DEV: assert(
636
+ isNumber(translateX),
637
+ "push() 1st parameter must be a number",
638
+ );
639
+ DEV: assert(
640
+ isNumber(translateY),
641
+ "push() 2nd parameter must be a number",
642
+ );
643
+ DEV: assert(
644
+ isNumber(rotation),
645
+ "push() 3rd parameter must be a number",
646
+ );
647
+ DEV: assert(isNumber(scaleX), "push() 4th parameter must be a number");
648
+ DEV: assert(isNumber(scaleY), "push() 5th parameter must be a number");
621
649
  _ctx.save();
650
+ instance.translate(translateX, translateY);
651
+ instance.rotate(rotation);
652
+ instance.scale(scaleX, scaleY);
622
653
  },
623
654
  pop() {
624
655
  _ctx.restore();
@@ -647,7 +678,7 @@
647
678
  fill(color) {
648
679
  DEV: assert(
649
680
  null == color || (isNumber(color) && color >= 0),
650
- "fill() 1st parameter must be a positive number or zero",
681
+ "fill() 1st parameter must be a non-negative number",
651
682
  );
652
683
  _ctx.fillStyle = getColor(color);
653
684
  _ctx.fill();
@@ -655,7 +686,7 @@
655
686
  stroke(color) {
656
687
  DEV: assert(
657
688
  null == color || (isNumber(color) && color >= 0),
658
- "stroke() 1st parameter must be a positive number or zero",
689
+ "stroke() 1st parameter must be a non-negative number",
659
690
  );
660
691
  _ctx.strokeStyle = getColor(color);
661
692
  _ctx.stroke();
@@ -700,7 +731,7 @@
700
731
  volume(value) {
701
732
  DEV: assert(
702
733
  isNumber(value) && value >= 0,
703
- "volume() 1st parameter must be a positive number or zero",
734
+ "volume() 1st parameter must be a non-negative number",
704
735
  );
705
736
  root.zzfxV = value;
706
737
  },
@@ -770,7 +801,7 @@
770
801
  );
771
802
  DEV: assert(
772
803
  isNumber(textColor) && textColor >= 0,
773
- "pal() 2nd parameter must be a positive number or zero",
804
+ "pal() 2nd parameter must be a non-negative number",
774
805
  );
775
806
  _colorPalette = colors || defaultPalette;
776
807
  _colorPaletteState = [];
@@ -786,7 +817,7 @@
786
817
  isNumber(a) ? isNumber(b) && b >= 0 : null == b,
787
818
  "palc() 2nd parameter must be a positive number",
788
819
  );
789
- if (a == null) {
820
+ if (null == a) {
790
821
  _colorPaletteState = [];
791
822
  } else {
792
823
  _colorPaletteState[a] = b;
@@ -810,7 +841,7 @@
810
841
  timescale(value) {
811
842
  DEV: assert(
812
843
  isNumber(value) && value >= 0,
813
- "timescale() 1st parameter must be a positive number or zero",
844
+ "timescale() 1st parameter must be a non-negative number",
814
845
  );
815
846
  _timeScale = value;
816
847
  },
@@ -921,7 +952,7 @@
921
952
  _checkTapped = (tap) => tap && perf.now() - tap.t <= 300;
922
953
  let _pressingMouse = false;
923
954
  on(_canvas, "mousedown", (ev) => {
924
- if (ev.button === 0) {
955
+ if (!ev.button) {
925
956
  preventDefault(ev);
926
957
  const [x, y] = _getXY(ev);
927
958
  instance.emit("tap", x, y, 0);
@@ -930,7 +961,7 @@
930
961
  }
931
962
  });
932
963
  on(_canvas, "mouseup", (ev) => {
933
- if (ev.button === 0) {
964
+ if (!ev.button) {
934
965
  preventDefault(ev);
935
966
  const tap = _taps.get(0);
936
967
  const [x, y] = _getXY(ev);
package/dist/dist.js CHANGED
@@ -179,6 +179,9 @@
179
179
  rad2deg: (rads) => {
180
180
  return (180 / math.PI) * rads;
181
181
  },
182
+ mod(a, b) {
183
+ return ((a % b) + b) % b || 0;
184
+ },
182
185
  round: (n, precision = 0) => {
183
186
  if (!precision) {
184
187
  return math.round(n);
@@ -260,25 +263,25 @@
260
263
  shape(points) {
261
264
  beginPath(_ctx);
262
265
  for (let i = 0; i < points.length; i += 2) {
263
- if (0 === i) {
264
- _ctx.moveTo(~~points[i], ~~points[i + 1]);
265
- } else {
266
+ if (i) {
266
267
  _ctx.lineTo(~~points[i], ~~points[i + 1]);
268
+ } else {
269
+ _ctx.moveTo(~~points[i], ~~points[i + 1]);
267
270
  }
268
271
  }
269
272
  _ctx.lineTo(~~points[0], ~~points[1]);
270
273
  },
271
274
  line(x1, y1, x2, y2, color) {
272
275
  beginPath(_ctx);
273
- let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0;
274
- let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0;
276
+ let xfix = _outline_fix && ~~x1 === ~~x2 ? 0.5 : 0;
277
+ let yfix = _outline_fix && ~~y1 === ~~y2 ? 0.5 : 0;
275
278
  _ctx.moveTo(~~x1 + xfix, ~~y1 + yfix);
276
279
  _ctx.lineTo(~~x2 + xfix, ~~y2 + yfix);
277
280
  instance.stroke(color);
278
281
  },
279
282
  linewidth(value) {
280
283
  _ctx.lineWidth = ~~value;
281
- _outline_fix = 0 === ~~value % 2 ? 0 : 0.5;
284
+ _outline_fix = ~~value % 2 ? 0.5 : 0;
282
285
  },
283
286
  linedash(segments, offset = 0) {
284
287
  _ctx.setLineDash(segments);
@@ -348,8 +351,17 @@
348
351
  }
349
352
  return _ctx;
350
353
  },
351
- push() {
354
+ push(
355
+ translateX = 0,
356
+ translateY = translateX,
357
+ rotation = 0,
358
+ scaleX = 1,
359
+ scaleY = scaleX,
360
+ ) {
352
361
  _ctx.save();
362
+ instance.translate(translateX, translateY);
363
+ instance.rotate(rotation);
364
+ instance.scale(scaleX, scaleY);
353
365
  },
354
366
  pop() {
355
367
  _ctx.restore();
@@ -436,7 +448,7 @@
436
448
  instance.emit("pal", _colorPalette, _defaultTextColor);
437
449
  },
438
450
  palc(a, b) {
439
- if (a == null) {
451
+ if (null == a) {
440
452
  _colorPaletteState = [];
441
453
  } else {
442
454
  _colorPaletteState[a] = b;
@@ -541,7 +553,7 @@
541
553
  _checkTapped = (tap) => tap && perf.now() - tap.t <= 300;
542
554
  let _pressingMouse = false;
543
555
  on(_canvas, "mousedown", (ev) => {
544
- if (ev.button === 0) {
556
+ if (!ev.button) {
545
557
  preventDefault(ev);
546
558
  const [x, y] = _getXY(ev);
547
559
  instance.emit("tap", x, y, 0);
@@ -550,7 +562,7 @@
550
562
  }
551
563
  });
552
564
  on(_canvas, "mouseup", (ev) => {
553
- if (ev.button === 0) {
565
+ if (!ev.button) {
554
566
  preventDefault(ev);
555
567
  const tap = _taps.get(0);
556
568
  const [x, y] = _getXY(ev);
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,l=0,n=0,i=.1,o=0,r=1,s=0,f=0,d=0,c=0,p=0,u=0,h=0,m=0,g=0,v=1,x=0,w=0,y=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,E=a*=(1-t+2*t*b.random(t=[]))*z/44100,T=0,P=0,C=0,I=1,L=0,D=0,A=0,S=y<0?-1:1,H=z*S*y*2/44100,M=b.cos(H),N=b.sin,W=N(H)/4,X=1+W,q=-2*M/X,B=(1-W)/X,V=(1+S*M)/2/X,O=-(S+M)/X,R=0,F=0,G=0,Y=0;for(l=44100*l+9,x*=44100,n*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,p=44100*p|0,e*=.3*zzfxV,S=l+x+n+i+g|0;C<S;t[C++]=A*e)++D%(100*m|0)||(A=o?1<o?2<o?3<o?N(T*T):b.max(b.min(b.tan(T),1),-1):1-(2*T/z%2+2)%2:1-4*b.abs(b.round(T/z)-T/z):N(T),A=(p?1-w+w*N(z*C/p):1)*(A<0?-1:1)*b.abs(A)**r*(C<l?C/l:C<l+x?1-(C-l)/x*(1-v):C<l+x+n?v:C<S-g?(S-C-g)/i*v:0),A=g?A/2+(g>C?0:(C<S-g?1:(S-C)/g)*t[C-g|0]/2/e):A,y&&(A=Y=V*R+O*(R=F)+V*(F=A)-B*G-q*(G=Y))),T+=(H=(a+=s+=f)*b.cos(h*P++))+H*u*N(C**5),I&&++I>c&&(a+=d,E+=d,I=0),!p||++L%p||(a=E,s=k,I=I||1);(e=zzfxX.createBuffer(1,S,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),c=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p=!1,u,h,m=1,g,v=.5,x=1,w,y=1e3/60,b,z=0,k=3,E="sans-serif",T=20,P=1.2,C=Date.now(),I=e,L=[],D=[.5,0,1750,,,.3,1,,,,600,.1],A={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?S.clamp(o,l,n):o},norm:(e,t,a)=>S.map(e,t,a,0,1),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){C=~~e},cls(e){null==e?g.clearRect(0,0,S.W,S.H):S.rectfill(0,0,S.W,S.H,e)},rect(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),S.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.fill(n)},circ(e,t,a,l){S.oval(e,t,a,a,l)},circfill(e,t,a,l){S.ovalfill(e,t,a,a,l)},shape(e){g.beginPath();for(let t=0;t<e.length;t+=2)0===t?g.moveTo(~~e[t],~~e[t+1]):g.lineTo(~~e[t],~~e[t+1]);g.lineTo(~~e[0],~~e[1])},line(e,t,a,l,n){g.beginPath();let i=.5*(0!==v&&~~e==~~a),o=.5*(0!==v&&~~t==~~l);g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,v=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${T}px ${E}`,g.fillStyle=X(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+T*P*a)},textgap(e){P=e},textfont(e){E=e},textsize(e){T=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a){let l=a.trim().split("\n");for(let a=0;a<l.length;a++){let n=l[a].trim();for(let l=0;l<n.length;l++){let i=n[l];"."!==i&&" "!==i&&S.rectfill(e+l,t+a,1,1,parseInt(i,36)||0)}}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(){g.save()},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t=e){g.scale(e,t)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=X(e),g.fill()},stroke(e){g.strokeStyle=X(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=D,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>h,use(e,t={}){var a=e,l=t;let n=a(S,l);for(let e in n)S.def(e,n[e])},listen:(e,t)=>{A[e=e.toLowerCase()]=A[e]||new Set,A[e].add(t)},unlisten:(e,t)=>{A[e=e.toLowerCase()]&&A[e].delete(t)},emit:(e,t,a,n,i)=>(p&&(W("before:"+(e=e.toLowerCase()),t,a,n,i),c||l[e]===S[e]||"function"!=typeof l[e]||l[e](t,a,n,i),W(e,t,a,n,i),W("after:"+e,t,a,n,i)),t),pal(t,a=3){I=t||e,L=[],k=a,S.emit("pal",I,k)},palc(e,t){null==e?L=[]:L[e]=t},def(e,a){S[e]=a,t.global&&(l[e]=a)},timescale(e){x=e},framerate(e){y=1e3/~~e},stat:e=>[t,p,y/1e3,m,A,I,D,x,l.zzfxV,C,T,E,L,P][e],pause(){u||(u=!0,z=~~cancelAnimationFrame(z),S.emit("paused"))},resume(){p&&u&&(H(),u=!1,S.emit("resumed"))},ispaused:()=>u,quit(){for(let e of(S.emit("quit"),S.pause(),p=!1,A={},s))e();if(t.global){for(let e in S)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))S[e]=n[e];function H(){z||(b=0,w=i.now(),z=r(M))}function M(){z=r(M);let e=i.now(),t=0,a=e-w;for(w=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*x;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.emit("draw",g),t>1&&(b=0))}function N(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(S.def("W",e),S.def("H",a),h.width=e,h.height=a,t.autoscale){let l=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=n.min(innerWidth/e,innerHeight/a),m=l>1&&m>l?l:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",m)}function W(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function X(e){return I[~~(L[e]??e)%I.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,S),l.ENGINE=S}if(a=document,g=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),f(h,"click",()=>focus()),N(),h.parentNode||a.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,c)for(let e in c)c[e]&&S.listen(e,c[e]);return r(function(){if(t.autoscale&&f(l,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(h,"mousedown",t=>{if(0===t.button){t.preventDefault();let[l,n]=e(t);S.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(h,"mouseup",a=>{if(0===a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&S.emit("tapped",l.xi,l.yi,0),S.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);S.def("MX",a),S.def("MY",l),r&&(S.emit("tapping",a,l,0),n(0,a,l))}),f(h,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(h,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);S.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{e.preventDefault();let a=[];for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(o(l)&&S.emit("tapped",l.xi,l.yi,e),S.emit("untap",l.x,l.y,e),t.delete(e))};f(h,"touchend",s),f(h,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))S.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>a(e,t)),S.def("iskeypressed",e=>a(t,e)),S.def("lastkey",()=>n)}p=!0,S.emit("init",S),u||H()}),S}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,l=0,n=0,i=.1,o=0,r=1,s=0,f=0,d=0,c=0,p=0,u=0,h=0,m=0,g=0,v=1,x=0,w=0,y=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,E=a*=(1-t+2*t*b.random(t=[]))*z/44100,T=0,P=0,C=0,I=1,L=0,D=0,A=0,S=y<0?-1:1,H=z*S*y*2/44100,M=b.cos(H),N=b.sin,W=N(H)/4,X=1+W,q=-2*M/X,B=(1-W)/X,V=(1+S*M)/2/X,O=-(S+M)/X,R=0,F=0,G=0,Y=0;for(l=44100*l+9,x*=44100,n*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,p=44100*p|0,e*=.3*zzfxV,S=l+x+n+i+g|0;C<S;t[C++]=A*e)++D%(100*m|0)||(A=o?1<o?2<o?3<o?N(T*T):b.max(b.min(b.tan(T),1),-1):1-(2*T/z%2+2)%2:1-4*b.abs(b.round(T/z)-T/z):N(T),A=(p?1-w+w*N(z*C/p):1)*(A<0?-1:1)*b.abs(A)**r*(C<l?C/l:C<l+x?1-(C-l)/x*(1-v):C<l+x+n?v:C<S-g?(S-C-g)/i*v:0),A=g?A/2+(g>C?0:(C<S-g?1:(S-C)/g)*t[C-g|0]/2/e):A,y&&(A=Y=V*R+O*(R=F)+V*(F=A)-B*G-q*(G=Y))),T+=(H=(a+=s+=f)*b.cos(h*P++))+H*u*N(C**5),I&&++I>c&&(a+=d,E+=d,I=0),!p||++L%p||(a=E,s=k,I=I||1);(e=zzfxX.createBuffer(1,S,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),c=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p=!1,u,h,m=1,g,v=.5,x=1,w,y=1e3/60,b,z=0,k=3,E="sans-serif",T=20,P=1.2,C=Date.now(),I=e,L=[],D=[.5,0,1750,,,.3,1,,,,600,.1],A={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,mod:(e,t)=>(e%t+t)%t||0,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?S.clamp(o,l,n):o},norm:(e,t,a)=>S.map(e,t,a,0,1),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){C=~~e},cls(e){null==e?g.clearRect(0,0,S.W,S.H):S.rectfill(0,0,S.W,S.H,e)},rect(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),S.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.fill(n)},circ(e,t,a,l){S.oval(e,t,a,a,l)},circfill(e,t,a,l){S.ovalfill(e,t,a,a,l)},shape(e){g.beginPath();for(let t=0;t<e.length;t+=2)t?g.lineTo(~~e[t],~~e[t+1]):g.moveTo(~~e[t],~~e[t+1]);g.lineTo(~~e[0],~~e[1])},line(e,t,a,l,n){g.beginPath();let i=v&&~~e==~~a?.5:0,o=v&&~~t==~~l?.5:0;g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,v=~~e%2?.5:0},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${T}px ${E}`,g.fillStyle=X(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+T*P*a)},textgap(e){P=e},textfont(e){E=e},textsize(e){T=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a){let l=a.trim().split("\n");for(let a=0;a<l.length;a++){let n=l[a].trim();for(let l=0;l<n.length;l++){let i=n[l];"."!==i&&" "!==i&&S.rectfill(e+l,t+a,1,1,parseInt(i,36)||0)}}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(e=0,t=e,a=0,l=1,n=l){g.save(),S.translate(e,t),S.rotate(a),S.scale(l,n)},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t=e){g.scale(e,t)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=X(e),g.fill()},stroke(e){g.strokeStyle=X(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=D,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>h,use(e,t={}){var a=e,l=t;let n=a(S,l);for(let e in n)S.def(e,n[e])},listen:(e,t)=>{A[e=e.toLowerCase()]=A[e]||new Set,A[e].add(t)},unlisten:(e,t)=>{A[e=e.toLowerCase()]&&A[e].delete(t)},emit:(e,t,a,n,i)=>(p&&(W("before:"+(e=e.toLowerCase()),t,a,n,i),c||l[e]===S[e]||"function"!=typeof l[e]||l[e](t,a,n,i),W(e,t,a,n,i),W("after:"+e,t,a,n,i)),t),pal(t,a=3){I=t||e,L=[],k=a,S.emit("pal",I,k)},palc(e,t){null==e?L=[]:L[e]=t},def(e,a){S[e]=a,t.global&&(l[e]=a)},timescale(e){x=e},framerate(e){y=1e3/~~e},stat:e=>[t,p,y/1e3,m,A,I,D,x,l.zzfxV,C,T,E,L,P][e],pause(){u||(u=!0,z=~~cancelAnimationFrame(z),S.emit("paused"))},resume(){p&&u&&(H(),u=!1,S.emit("resumed"))},ispaused:()=>u,quit(){for(let e of(S.emit("quit"),S.pause(),p=!1,A={},s))e();if(t.global){for(let e in S)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))S[e]=n[e];function H(){z||(b=0,w=i.now(),z=r(M))}function M(){z=r(M);let e=i.now(),t=0,a=e-w;for(w=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*x;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.emit("draw",g),t>1&&(b=0))}function N(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(S.def("W",e),S.def("H",a),h.width=e,h.height=a,t.autoscale){let l=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=n.min(innerWidth/e,innerHeight/a),m=l>1&&m>l?l:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",m)}function W(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function X(e){return I[~~(L[e]??e)%I.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,S),l.ENGINE=S}if(a=document,g=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),f(h,"click",()=>focus()),N(),h.parentNode||a.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,c)for(let e in c)c[e]&&S.listen(e,c[e]);return r(function(){if(t.autoscale&&f(l,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(h,"mousedown",t=>{if(!t.button){t.preventDefault();let[l,n]=e(t);S.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(h,"mouseup",a=>{if(!a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&S.emit("tapped",l.xi,l.yi,0),S.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);S.def("MX",a),S.def("MY",l),r&&(S.emit("tapping",a,l,0),n(0,a,l))}),f(h,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(h,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);S.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{e.preventDefault();let a=[];for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(o(l)&&S.emit("tapped",l.xi,l.yi,e),S.emit("untap",l.x,l.y,e),t.delete(e))};f(h,"touchend",s),f(h,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))S.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>a(e,t)),S.def("iskeypressed",e=>a(t,e)),S.def("lastkey",()=>n)}p=!0,S.emit("init",S),u||H()}),S}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.207.2",
3
+ "version": "0.208.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and p5.js/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
package/src/index.js CHANGED
@@ -45,7 +45,7 @@ export default function litecanvas(settings = {}) {
45
45
 
46
46
  DEV: assert(
47
47
  null == settings || 'object' === typeof settings,
48
- 'litecanvas() 1st parameter must be a object or null'
48
+ 'litecanvas() 1st parameter must be a object'
49
49
  )
50
50
 
51
51
  // setup the settings default values
@@ -173,6 +173,25 @@ export default function litecanvas(settings = {}) {
173
173
  return (180 / math.PI) * rads
174
174
  },
175
175
 
176
+ /**
177
+ * Modulus (Euclidean division).
178
+ *
179
+ * Note: When `b == 0` returns `0`, rather than `NaN`.
180
+ *
181
+ * @param {number} a dividend
182
+ * @param {number} b divisor
183
+ * @returns {number} the remainder
184
+ * @example
185
+ * mod(-1, 5) // => 4
186
+ * -1 % 5 // => -1
187
+ */
188
+ mod(a, b) {
189
+ DEV: assert(isNumber(a), 'mod() 1st parameter must be a number')
190
+ DEV: assert(isNumber(b) && b >= 0, 'mod() 2nd parameter must be a non-negative number')
191
+
192
+ return ((a % b) + b) % b || 0
193
+ },
194
+
176
195
  /**
177
196
  * Returns the rounded value of an number to optional precision (number of digits after the decimal point).
178
197
  *
@@ -186,7 +205,7 @@ export default function litecanvas(settings = {}) {
186
205
  DEV: assert(isNumber(n), 'round() 1st parameter must be a number')
187
206
  DEV: assert(
188
207
  isNumber(precision) && precision >= 0,
189
- 'round() 2nd parameter must be a positive number or zero'
208
+ 'round() 2nd parameter must be a non-negative number'
190
209
  )
191
210
 
192
211
  if (!precision) {
@@ -347,7 +366,7 @@ export default function litecanvas(settings = {}) {
347
366
  rseed(value) {
348
367
  DEV: assert(
349
368
  isNumber(value) && value >= 0,
350
- 'rseed() 1st parameter must be a positive integer or zero'
369
+ 'rseed() 1st parameter must be a non-negative integer'
351
370
  )
352
371
 
353
372
  _rngSeed = ~~value
@@ -355,14 +374,14 @@ export default function litecanvas(settings = {}) {
355
374
 
356
375
  /** BASIC GRAPHICS API */
357
376
  /**
358
- * Clear the game screen with an optional color
377
+ * Clear the game screen with an optional color.
359
378
  *
360
379
  * @param {number} [color] The background color (index) or null/undefined (for transparent)
361
380
  */
362
381
  cls(color) {
363
382
  DEV: assert(
364
383
  null == color || (isNumber(color) && color >= 0),
365
- 'cls() 1st parameter must be a positive number or zero or undefined'
384
+ 'cls() 1st parameter must be a non-negative number'
366
385
  )
367
386
 
368
387
  if (null == color) {
@@ -393,11 +412,11 @@ export default function litecanvas(settings = {}) {
393
412
  )
394
413
  DEV: assert(
395
414
  isNumber(height) && height >= 0,
396
- 'rect() 4th parameter must be a positive number or zero'
415
+ 'rect() 4th parameter must be a non-negative number'
397
416
  )
398
417
  DEV: assert(
399
418
  null == color || (isNumber(color) && color >= 0),
400
- 'rect() 5th parameter must be a positive number or zero'
419
+ 'rect() 5th parameter must be a non-negative number'
401
420
  )
402
421
  DEV: assert(
403
422
  null == radii || isNumber(radii) || (Array.isArray(radii) && radii.length >= 1),
@@ -405,6 +424,7 @@ export default function litecanvas(settings = {}) {
405
424
  )
406
425
 
407
426
  beginPath(_ctx)
427
+
408
428
  _ctx[radii ? 'roundRect' : 'rect'](
409
429
  ~~x - _outline_fix,
410
430
  ~~y - _outline_fix,
@@ -430,15 +450,15 @@ export default function litecanvas(settings = {}) {
430
450
  DEV: assert(isNumber(y), 'rectfill() 2nd parameter must be a number')
431
451
  DEV: assert(
432
452
  isNumber(width) && width >= 0,
433
- 'rectfill() 3rd parameter must be a positive number or zero'
453
+ 'rectfill() 3rd parameter must be a non-negative number'
434
454
  )
435
455
  DEV: assert(
436
456
  isNumber(height) && height >= 0,
437
- 'rectfill() 4th parameter must be a positive number or zero'
457
+ 'rectfill() 4th parameter must be a non-negative number'
438
458
  )
439
459
  DEV: assert(
440
460
  null == color || (isNumber(color) && color >= 0),
441
- 'rectfill() 5th parameter must be a positive number or zero'
461
+ 'rectfill() 5th parameter must be a non-negative number'
442
462
  )
443
463
  DEV: assert(
444
464
  null == radii || isNumber(radii) || (Array.isArray(radii) && radii.length >= 1),
@@ -447,6 +467,7 @@ export default function litecanvas(settings = {}) {
447
467
  )
448
468
 
449
469
  beginPath(_ctx)
470
+
450
471
  _ctx[radii ? 'roundRect' : 'rect'](~~x, ~~y, ~~width, ~~height, radii)
451
472
  instance.fill(color)
452
473
  },
@@ -465,18 +486,19 @@ export default function litecanvas(settings = {}) {
465
486
  DEV: assert(isNumber(y), 'oval() 2nd parameter must be a number')
466
487
  DEV: assert(
467
488
  isNumber(radiusX) && radiusX >= 0,
468
- 'oval() 3rd parameter must be a positive number or zero'
489
+ 'oval() 3rd parameter must be a non-negative number'
469
490
  )
470
491
  DEV: assert(
471
492
  isNumber(radiusY) && radiusY >= 0,
472
- 'oval() 4th parameter must be a positive number or zero'
493
+ 'oval() 4th parameter must be a non-negative number'
473
494
  )
474
495
  DEV: assert(
475
496
  null == color || (isNumber(color) && color >= 0),
476
- 'oval() 5th parameter must be a positive number or zero'
497
+ 'oval() 5th parameter must be a non-negative number'
477
498
  )
478
499
 
479
500
  beginPath(_ctx)
501
+
480
502
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
481
503
  instance.stroke(color)
482
504
  },
@@ -495,18 +517,19 @@ export default function litecanvas(settings = {}) {
495
517
  DEV: assert(isNumber(y), 'ovalfill() 2nd parameter must be a number')
496
518
  DEV: assert(
497
519
  isNumber(radiusX) && radiusX >= 0,
498
- 'ovalfill() 3rd parameter must be a positive number or zero'
520
+ 'ovalfill() 3rd parameter must be a non-negative number'
499
521
  )
500
522
  DEV: assert(
501
523
  isNumber(radiusY) && radiusY >= 0,
502
- 'ovalfill() 4th parameter must be a positive number or zero'
524
+ 'ovalfill() 4th parameter must be a non-negative number'
503
525
  )
504
526
  DEV: assert(
505
527
  null == color || (isNumber(color) && color >= 0),
506
- 'ovalfill() 5th parameter must be a positive number or zero'
528
+ 'ovalfill() 5th parameter must be a non-negative number'
507
529
  )
508
530
 
509
531
  beginPath(_ctx)
532
+
510
533
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
511
534
  instance.fill(color)
512
535
  },
@@ -524,11 +547,11 @@ export default function litecanvas(settings = {}) {
524
547
  DEV: assert(isNumber(y), 'circ() 2nd parameter must be a number')
525
548
  DEV: assert(
526
549
  isNumber(radius) && radius >= 0,
527
- 'circ() 3rd parameter must be a positive number or zero'
550
+ 'circ() 3rd parameter must be a non-negative number'
528
551
  )
529
552
  DEV: assert(
530
553
  null == color || (isNumber(color) && color >= 0),
531
- 'circ() 4th parameter must be a positive number or zero'
554
+ 'circ() 4th parameter must be a non-negative number'
532
555
  )
533
556
 
534
557
  instance.oval(x, y, radius, radius, color)
@@ -547,11 +570,11 @@ export default function litecanvas(settings = {}) {
547
570
  DEV: assert(isNumber(y), 'circfill() 2nd parameter must be a number')
548
571
  DEV: assert(
549
572
  isNumber(radius) && radius >= 0,
550
- 'circfill() 3rd parameter must be a positive number or zero'
573
+ 'circfill() 3rd parameter must be a non-negative number'
551
574
  )
552
575
  DEV: assert(
553
576
  null == color || (isNumber(color) && color >= 0),
554
- 'circfill() 4th parameter must be a positive number or zero'
577
+ 'circfill() 4th parameter must be a non-negative number'
555
578
  )
556
579
 
557
580
  instance.ovalfill(x, y, radius, radius, color)
@@ -567,15 +590,16 @@ export default function litecanvas(settings = {}) {
567
590
  DEV: assert(Array.isArray(points), 'shape() 1st parameter must be an array of numbers')
568
591
  DEV: assert(
569
592
  points.length >= 6,
570
-
571
593
  'shape() 1st parameter must be an array with at least 6 numbers (3 points)'
572
594
  )
595
+
573
596
  beginPath(_ctx)
597
+
574
598
  for (let i = 0; i < points.length; i += 2) {
575
- if (0 === i) {
576
- _ctx.moveTo(~~points[i], ~~points[i + 1])
577
- } else {
599
+ if (i) {
578
600
  _ctx.lineTo(~~points[i], ~~points[i + 1])
601
+ } else {
602
+ _ctx.moveTo(~~points[i], ~~points[i + 1])
579
603
  }
580
604
  }
581
605
  _ctx.lineTo(~~points[0], ~~points[1])
@@ -593,17 +617,17 @@ export default function litecanvas(settings = {}) {
593
617
  line(x1, y1, x2, y2, color) {
594
618
  DEV: assert(isNumber(x1), 'line() 1st parameter must be a number')
595
619
  DEV: assert(isNumber(y1), 'line() 2nd parameter must be a number')
596
- DEV: assert(isNumber(x2), 'line() 3rd parameter must be a positive number or zero')
597
- DEV: assert(isNumber(y2), 'line() 4th parameter must be a positive number or zero')
620
+ DEV: assert(isNumber(x2), 'line() 3rd parameter must be a non-negative number')
621
+ DEV: assert(isNumber(y2), 'line() 4th parameter must be a non-negative number')
598
622
  DEV: assert(
599
623
  null == color || (isNumber(color) && color >= 0),
600
- 'line() 5th parameter must be a positive number or zero'
624
+ 'line() 5th parameter must be a non-negative number'
601
625
  )
602
626
 
603
627
  beginPath(_ctx)
604
628
 
605
- let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0
606
- let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0
629
+ let xfix = _outline_fix && ~~x1 === ~~x2 ? 0.5 : 0
630
+ let yfix = _outline_fix && ~~y1 === ~~y2 ? 0.5 : 0
607
631
 
608
632
  _ctx.moveTo(~~x1 + xfix, ~~y1 + yfix)
609
633
  _ctx.lineTo(~~x2 + xfix, ~~y2 + yfix)
@@ -620,11 +644,11 @@ export default function litecanvas(settings = {}) {
620
644
  linewidth(value) {
621
645
  DEV: assert(
622
646
  isNumber(value) && value >= 0,
623
- 'linewidth() 1st parameter must be a positive number or zero'
647
+ 'linewidth() 1st parameter must be a non-negative integer'
624
648
  )
625
649
 
626
650
  _ctx.lineWidth = ~~value
627
- _outline_fix = 0 === ~~value % 2 ? 0 : 0.5
651
+ _outline_fix = ~~value % 2 ? 0.5 : 0
628
652
  },
629
653
 
630
654
  /**
@@ -661,7 +685,7 @@ export default function litecanvas(settings = {}) {
661
685
  DEV: assert(isNumber(y), 'text() 2nd parameter must be a number')
662
686
  DEV: assert(
663
687
  null == color || (isNumber(color) && color >= 0),
664
- 'text() 4th parameter must be a positive number or zero'
688
+ 'text() 4th parameter must be a non-negative number'
665
689
  )
666
690
  DEV: assert('string' === typeof fontStyle, 'text() 5th parameter must be a string')
667
691
 
@@ -844,16 +868,32 @@ export default function litecanvas(settings = {}) {
844
868
  if (context) {
845
869
  _ctx = context
846
870
  }
871
+
847
872
  return _ctx
848
873
  },
849
874
 
850
875
  /**
851
- * saves the current drawing style settings and transformations
876
+ * Saves the current drawing style settings and, optionally, transforms (translate/rotate/scale) the canvas.
877
+ *
878
+ * @param {number} [translateX]
879
+ * @param {number} [translateY]
880
+ * @param {number} [rotation] in radians
881
+ * @param {number} [scaleX]
882
+ * @param {number} [scaleY]
852
883
  *
853
884
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
854
885
  */
855
- push() {
886
+ push(translateX = 0, translateY = translateX, rotation = 0, scaleX = 1, scaleY = scaleX) {
887
+ DEV: assert(isNumber(translateX), 'push() 1st parameter must be a number')
888
+ DEV: assert(isNumber(translateY), 'push() 2nd parameter must be a number')
889
+ DEV: assert(isNumber(rotation), 'push() 3rd parameter must be a number')
890
+ DEV: assert(isNumber(scaleX), 'push() 4th parameter must be a number')
891
+ DEV: assert(isNumber(scaleY), 'push() 5th parameter must be a number')
892
+
856
893
  _ctx.save()
894
+ instance.translate(translateX, translateY)
895
+ instance.rotate(rotation)
896
+ instance.scale(scaleX, scaleY)
857
897
  },
858
898
 
859
899
  /**
@@ -922,7 +962,7 @@ export default function litecanvas(settings = {}) {
922
962
  fill(color) {
923
963
  DEV: assert(
924
964
  null == color || (isNumber(color) && color >= 0),
925
- 'fill() 1st parameter must be a positive number or zero'
965
+ 'fill() 1st parameter must be a non-negative number'
926
966
  )
927
967
 
928
968
  _ctx.fillStyle = getColor(color)
@@ -937,7 +977,7 @@ export default function litecanvas(settings = {}) {
937
977
  stroke(color) {
938
978
  DEV: assert(
939
979
  null == color || (isNumber(color) && color >= 0),
940
- 'stroke() 1st parameter must be a positive number or zero'
980
+ 'stroke() 1st parameter must be a non-negative number'
941
981
  )
942
982
 
943
983
  _ctx.strokeStyle = getColor(color)
@@ -1017,7 +1057,7 @@ export default function litecanvas(settings = {}) {
1017
1057
  volume(value) {
1018
1058
  DEV: assert(
1019
1059
  isNumber(value) && value >= 0,
1020
- 'volume() 1st parameter must be a positive number or zero'
1060
+ 'volume() 1st parameter must be a non-negative number'
1021
1061
  )
1022
1062
 
1023
1063
  root.zzfxV = value
@@ -1137,7 +1177,7 @@ export default function litecanvas(settings = {}) {
1137
1177
  )
1138
1178
  DEV: assert(
1139
1179
  isNumber(textColor) && textColor >= 0,
1140
- 'pal() 2nd parameter must be a positive number or zero'
1180
+ 'pal() 2nd parameter must be a non-negative number'
1141
1181
  )
1142
1182
 
1143
1183
  _colorPalette = colors || defaultPalette
@@ -1167,7 +1207,7 @@ export default function litecanvas(settings = {}) {
1167
1207
  'palc() 2nd parameter must be a positive number'
1168
1208
  )
1169
1209
 
1170
- if (a == null) {
1210
+ if (null == a) {
1171
1211
  _colorPaletteState = []
1172
1212
  } else {
1173
1213
  _colorPaletteState[a] = b
@@ -1209,7 +1249,7 @@ export default function litecanvas(settings = {}) {
1209
1249
  timescale(value) {
1210
1250
  DEV: assert(
1211
1251
  isNumber(value) && value >= 0,
1212
- 'timescale() 1st parameter must be a positive number or zero'
1252
+ 'timescale() 1st parameter must be a non-negative number'
1213
1253
  )
1214
1254
 
1215
1255
  _timeScale = value
@@ -1391,6 +1431,9 @@ export default function litecanvas(settings = {}) {
1391
1431
  (ev.pageX - _canvas.offsetLeft) / _canvasScale,
1392
1432
  (ev.pageY - _canvas.offsetTop) / _canvasScale,
1393
1433
  ],
1434
+ /**
1435
+ * @type {Map<number,{x:number, y:number, xi: number, yi: number, t:number}>}
1436
+ */
1394
1437
  _taps = new Map(),
1395
1438
  _registerTap =
1396
1439
  /**
@@ -1440,7 +1483,7 @@ export default function litecanvas(settings = {}) {
1440
1483
  * @param {MouseEvent} ev
1441
1484
  */
1442
1485
  (ev) => {
1443
- if (ev.button === 0) {
1486
+ if (!ev.button) {
1444
1487
  preventDefault(ev)
1445
1488
  const [x, y] = _getXY(ev)
1446
1489
  instance.emit('tap', x, y, 0)
@@ -1457,7 +1500,7 @@ export default function litecanvas(settings = {}) {
1457
1500
  * @param {MouseEvent} ev
1458
1501
  */
1459
1502
  (ev) => {
1460
- if (ev.button === 0) {
1503
+ if (!ev.button) {
1461
1504
  preventDefault(ev)
1462
1505
  const tap = _taps.get(0)
1463
1506
  const [x, y] = _getXY(ev)
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.207.2'
2
+ export const version = '0.208.0'
package/types/global.d.ts CHANGED
@@ -59,6 +59,19 @@ declare global {
59
59
  * @returns the value in degrees
60
60
  */
61
61
  function rad2deg(rads: number): number
62
+ /**
63
+ * Modulus (Euclidean division).
64
+ *
65
+ * Note: When `b == 0` returns `0`, rather than `NaN`.
66
+ *
67
+ * @param a dividend
68
+ * @param b divisor
69
+ * @returns the remainder
70
+ * @example
71
+ * mod(-1, 5) // => 4
72
+ * -1 % 5 // => -1
73
+ */
74
+ function mod(a: number, b: number): number
62
75
  /**
63
76
  * Returns the rounded value of an number to optional precision (number of digits after the decimal point).
64
77
  *
@@ -430,10 +443,23 @@ declare global {
430
443
  context?: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
431
444
  ): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
432
445
  /**
433
- * saves the current drawing style settings and transformations
446
+ * Saves the current drawing style settings and, optionally, transforms (translate/rotate/scale) the canvas.
447
+ *
448
+ * @param [translateX]
449
+ * @param [translateY]
450
+ * @param [rotation] in radians
451
+ * @param [scaleX]
452
+ * @param [scaleY]
453
+ *
434
454
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
435
455
  */
436
- function push(): void
456
+ function push(
457
+ translateX?: number,
458
+ translateY?: number,
459
+ rotation?: number,
460
+ scaleX?: number,
461
+ scaleY?: number
462
+ ): void
437
463
  /**
438
464
  * restores the drawing style settings and transformations
439
465
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
package/types/types.d.ts CHANGED
@@ -5,9 +5,9 @@ type LitecanvasInstance = {
5
5
  H: number
6
6
  /** the amount of time (in seconds) since the game started */
7
7
  T: number
8
- /** The current mouse's horizontal (X) position or -1 (if the mouse was not used or detected) */
8
+ /** The current mouse's horizontal (X) position or `-1` (if the mouse was not used or detected) */
9
9
  MX: number
10
- /** The current mouse's vertical (Y) position or -1 (if the mouse was not used or detected) */
10
+ /** The current mouse's vertical (Y) position or `-1` (if the mouse was not used or detected) */
11
11
  MY: number
12
12
 
13
13
  /** MATH API */
@@ -53,6 +53,19 @@ type LitecanvasInstance = {
53
53
  * @returns the value in degrees
54
54
  */
55
55
  rad2deg(rads: number): number
56
+ /**
57
+ * Modulus (Euclidean division).
58
+ *
59
+ * Note: When `b == 0` returns `0`, rather than `NaN`.
60
+ *
61
+ * @param a dividend
62
+ * @param b divisor
63
+ * @returns the remainder
64
+ * @example
65
+ * mod(-1, 5) // => 4
66
+ * -1 % 5 // => -1
67
+ */
68
+ mod(a: number, b: number): number
56
69
  /**
57
70
  * Returns the rounded value of an number to optional precision (number of digits after the decimal point).
58
71
  *
@@ -418,10 +431,23 @@ type LitecanvasInstance = {
418
431
  context?: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
419
432
  ): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
420
433
  /**
421
- * saves the current drawing style settings and transformations
434
+ * Saves the current drawing style settings and, optionally, transforms (translate/rotate/scale) the canvas.
435
+ *
436
+ * @param [translateX]
437
+ * @param [translateY]
438
+ * @param [rotation] in radians
439
+ * @param [scaleX]
440
+ * @param [scaleY]
441
+ *
422
442
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
423
443
  */
424
- push(): void
444
+ push(
445
+ translateX?: number,
446
+ translateY?: number,
447
+ rotation?: number,
448
+ scaleX?: number,
449
+ scaleY?: number
450
+ ): void
425
451
  /**
426
452
  * restores the drawing style settings and transformations
427
453
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore