litecanvas 0.401.0 → 0.402.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,15 @@
115
115
  var assert = (condition, message = "Assertion failed") => {
116
116
  if (!condition) throw new Error("[Litecanvas] " + message);
117
117
  };
118
- var version = "0.401.0";
118
+ var version = "0.402.0";
119
+ var mulberry32 = (seed) => {
120
+ return () => {
121
+ let t = (seed += 1831565813);
122
+ t = Math.imul(t ^ (t >>> 15), t | 1);
123
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
124
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
125
+ };
126
+ };
119
127
  function litecanvas(settings = {}) {
120
128
  const root = window,
121
129
  math = Math,
@@ -166,6 +174,7 @@
166
174
  _fontSize = 20,
167
175
  _fontLineHeight = 1.2,
168
176
  _rngSeed = Date.now(),
177
+ _rngNext = mulberry32(_rngSeed >>> 0),
169
178
  _currentPalette = defaultPalette,
170
179
  _lastPalette = defaultPalette,
171
180
  _colorPaletteState = [],
@@ -177,6 +186,32 @@
177
186
  T: 0,
178
187
  MX: -1,
179
188
  MY: -1,
189
+ rand: (min = 0, max = 1) => {
190
+ DEV: assert(isNumber(min), "rand() 1st argument must be a number");
191
+ DEV: assert(isNumber(max), "rand() 2nd argument must be a number");
192
+ DEV: assert(
193
+ max >= min,
194
+ "rand() the 1st argument must be less than the 2nd argument",
195
+ );
196
+ return min + _rngNext() * (max - min);
197
+ },
198
+ randi: (min = 0, max = 1) => {
199
+ DEV: assert(isNumber(min), "randi() 1st argument must be a number");
200
+ DEV: assert(isNumber(max), "randi() 2nd argument must be a number");
201
+ DEV: assert(
202
+ max >= min,
203
+ "randi() the 1st argument must be less than the 2nd argument",
204
+ );
205
+ return ~~instance.rand(min, max + 1);
206
+ },
207
+ rseed(value) {
208
+ DEV: assert(
209
+ isNumber(value) && value >= 0,
210
+ "rseed() 1st argument must be a non-negative integer",
211
+ );
212
+ _rngSeed = value;
213
+ _rngNext = mulberry32(_rngSeed >>> 0);
214
+ },
180
215
  TAU,
181
216
  lerp: (start, end, t) => {
182
217
  DEV: assert(isNumber(start), "lerp() 1st argument must be a number");
@@ -265,35 +300,6 @@
265
300
  );
266
301
  return instance.map(value, start, stop, 0, 1);
267
302
  },
268
- rand: (min = 0, max = 1) => {
269
- DEV: assert(isNumber(min), "rand() 1st argument must be a number");
270
- DEV: assert(isNumber(max), "rand() 2nd argument must be a number");
271
- DEV: assert(
272
- max >= min,
273
- "rand() the 1st argument must be less than the 2nd argument",
274
- );
275
- const a = 1664525;
276
- const c = 1013904223;
277
- const m = 4294967296;
278
- _rngSeed = (a * _rngSeed + c) % m;
279
- return (_rngSeed / m) * (max - min) + min;
280
- },
281
- randi: (min = 0, max = 1) => {
282
- DEV: assert(isNumber(min), "randi() 1st argument must be a number");
283
- DEV: assert(isNumber(max), "randi() 2nd argument must be a number");
284
- DEV: assert(
285
- max >= min,
286
- "randi() the 1st argument must be less than the 2nd argument",
287
- );
288
- return ~~instance.rand(min, max + 1);
289
- },
290
- rseed(value) {
291
- DEV: assert(
292
- isNumber(value) && value >= 0,
293
- "rseed() 1st argument must be a non-negative integer",
294
- );
295
- _rngSeed = ~~value;
296
- },
297
303
  cls(color) {
298
304
  DEV: assert(
299
305
  null == color ||
@@ -1221,3 +1227,16 @@
1221
1227
  }
1222
1228
  window.litecanvas = litecanvas;
1223
1229
  })();
1230
+ /**
1231
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
1232
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
1233
+ * The same seed always gives the same sequence.
1234
+ *
1235
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
1236
+ * @license CC0-1.0 (Public Domain)
1237
+ *
1238
+ * @param {number} seed an unsigned 32-bit integer
1239
+ * @param {number} t
1240
+ * @param {typeof Math.imul} i
1241
+ * @returns {() => number}
1242
+ */
package/dist/dist.js CHANGED
@@ -112,6 +112,14 @@
112
112
  };
113
113
  };
114
114
  var defaultPalette = ["#211e20", "#555568", "#a0a08b", "#e9efec"];
115
+ var mulberry32 = (seed) => {
116
+ return () => {
117
+ let t = (seed += 1831565813);
118
+ t = Math.imul(t ^ (t >>> 15), t | 1);
119
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
120
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
121
+ };
122
+ };
115
123
  function litecanvas(settings = {}) {
116
124
  const root = window,
117
125
  math = Math,
@@ -158,6 +166,7 @@
158
166
  _fontSize = 20,
159
167
  _fontLineHeight = 1.2,
160
168
  _rngSeed = Date.now(),
169
+ _rngNext = mulberry32(_rngSeed >>> 0),
161
170
  _currentPalette = defaultPalette,
162
171
  _lastPalette = defaultPalette,
163
172
  _colorPaletteState = [],
@@ -169,6 +178,16 @@
169
178
  T: 0,
170
179
  MX: -1,
171
180
  MY: -1,
181
+ rand: (min = 0, max = 1) => {
182
+ return min + _rngNext() * (max - min);
183
+ },
184
+ randi: (min = 0, max = 1) => {
185
+ return ~~instance.rand(min, max + 1);
186
+ },
187
+ rseed(value) {
188
+ _rngSeed = value;
189
+ _rngNext = mulberry32(_rngSeed >>> 0);
190
+ },
172
191
  TAU,
173
192
  lerp: (start, end, t) => {
174
193
  return start + t * (end - start);
@@ -208,19 +227,6 @@
208
227
  norm: (value, start, stop) => {
209
228
  return instance.map(value, start, stop, 0, 1);
210
229
  },
211
- rand: (min = 0, max = 1) => {
212
- const a = 1664525;
213
- const c = 1013904223;
214
- const m = 4294967296;
215
- _rngSeed = (a * _rngSeed + c) % m;
216
- return (_rngSeed / m) * (max - min) + min;
217
- },
218
- randi: (min = 0, max = 1) => {
219
- return ~~instance.rand(min, max + 1);
220
- },
221
- rseed(value) {
222
- _rngSeed = ~~value;
223
- },
224
230
  cls(color) {
225
231
  if (null == color) {
226
232
  _ctx.clearRect(0, 0, instance.W, instance.H);
@@ -754,3 +760,16 @@
754
760
  }
755
761
  window.litecanvas = litecanvas;
756
762
  })();
763
+ /**
764
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
765
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
766
+ * The same seed always gives the same sequence.
767
+ *
768
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
769
+ * @license CC0-1.0 (Public Domain)
770
+ *
771
+ * @param {number} seed an unsigned 32-bit integer
772
+ * @param {number} t
773
+ * @param {typeof Math.imul} i
774
+ * @returns {() => number}
775
+ */
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,n=window,l=Math,i=performance,o=2*l.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},c=(n.zzfxX=new AudioContext,n.zzfxV=1,(e=1,t=.05,a=220,n=0,l=0,i=.1,o=0,r=1,s=0,f=0,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=1,x=0,y=0,v=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,C=a*=(1-t+2*t*b.random(t=[]))*z/44100,E=0,P=0,I=0,T=1,L=0,S=0,A=0,M=v<0?-1:1,N=z*M*v*2/44100,W=b.cos(N),X=b.sin,H=X(N)/4,q=1+H,B=-2*W/q,V=(1-H)/q,D=(1+M*W)/2/q,O=-(M+W)/q,G=0,R=0,Y=0,$=0;for(n=44100*n+9,x*=44100,l*=44100,i*=44100,m*=44100,f*=500*z/85766121e6,h*=z/44100,c*=z/44100,d*=44100,p=44100*p|0,e*=.3*zzfxV,M=n+x+l+i+m|0;I<M;t[I++]=A*e)++S%(100*g|0)||(A=o?1<o?2<o?3<o?X(E*E):b.max(b.min(b.tan(E),1),-1):1-(2*E/z%2+2)%2:1-4*b.abs(b.round(E/z)-E/z):X(E),A=(p?1-y+y*X(z*I/p):1)*(A<0?-1:1)*b.abs(A)**r*(I<n?I/n:I<n+x?1-(I-n)/x*(1-w):I<n+x+l?w:I<M-m?(M-I-m)/i*w:0),A=m?A/2+(m>I?0:(I<M-m?1:(M-I)/m)*t[I-m|0]/2/e):A,v&&(A=$=D*G+O*(G=R)+D*(R=A)-V*Y-B*(Y=$))),E+=(N=(a+=s+=f)*b.cos(h*P++))+N*u*X(I**5),T&&++T>d&&(a+=c,C+=c,T=0),!p||++L%p||(a=C,s=k,T=T||1);(e=zzfxX.createBuffer(1,M,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),d=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p,u,h,g=1,m,w=.5,x=1,y,v=1e3/60,b,z=0,k=3,C="sans-serif",E=20,P=1.2,I=Date.now(),T=e,L=e,S=[],A=[.5,0,1750,,,.3,1,,,,600,.1],M={},N={W:0,H:0,T:0,MX:-1,MY:-1,TAU:o,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,mod:(e,t)=>(e%t+t)%t||0,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,n)=>l.hypot(a-e,n-t),wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,n,l,i){let o=(e-t)/(a-t)*(l-n)+n;return i?N.clamp(o,n,l):o},norm:(e,t,a)=>N.map(e,t,a,0,1),rand:(e=0,t=1)=>(I=(1664525*I+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~N.rand(e,t+1),rseed(e){I=~~e},cls(e){null==e?m.clearRect(0,0,N.W,N.H):N.rectfill(0,0,N.W,N.H,e)},rect(e,t,a,n,l,i){m.beginPath(),m[i?"roundRect":"rect"](~~e-w,~~t-w,~~a+2*w,~~n+2*w,i),N.stroke(l)},rectfill(e,t,a,n,l,i){m.beginPath(),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~n,i),N.fill(l)},oval(e,t,a,n,l){m.beginPath(),m.ellipse(~~e,~~t,~~a,~~n,0,0,o),N.stroke(l)},ovalfill(e,t,a,n,l){m.beginPath(),m.ellipse(~~e,~~t,~~a,~~n,0,0,o),N.fill(l)},circ(e,t,a,n){N.oval(e,t,a,a,n)},circfill(e,t,a,n){N.ovalfill(e,t,a,a,n)},shape(e){m.beginPath();for(let t=0;t<e.length;t+=2)t?m.lineTo(~~e[t],~~e[t+1]):m.moveTo(~~e[t],~~e[t+1]);m.lineTo(~~e[0],~~e[1])},line(e,t,a,n,l){m.beginPath();let i=w&&~~e==~~a?.5:0,o=w&&~~t==~~n?.5:0;m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~n+o),N.stroke(l)},linewidth(e){m.lineWidth=~~e,w=~~e%2?.5:0},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,n=k,l="normal"){m.font=`${l} ${E}px ${C}`,m.fillStyle=B(n);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)m.fillText(i[a],~~e,~~t+E*P*a)},textgap(e){P=e},textfont(e){C=e},textsize(e){E=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},spr(e,t,a){let n=a.replace(/[^\w.\n]/g,"").split("\n").filter(e=>e);for(let a=0;a<n.length;a++)for(let l=0;l<n[a].length;l++)"."!==n[a][l]&&N.rectfill(e+l,t+a,1,1,parseInt(n[a][l],36)||0)},paint(e,t,a,n={}){let l=n.canvas||new OffscreenCanvas(1,1),i=n.scale||1,o=m;return l.width=e*i,l.height=t*i,(m=l.getContext("2d")).scale(i,i),a(m),m=o,l.transferToImageBitmap()},ctx:e=>(e&&(m=e),m),push(e=0,t=e,a=0,n=1,l=n){m.save(),N.translate(e,t),N.rotate(a),N.scale(n,l)},pop(){m.restore()},translate(e,t){m.translate(~~e,~~t)},scale(e,t=e){m.scale(e,t)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=N.clamp(e,0,1)},fill(e){m.fillStyle=B(e),m.fill()},stroke(e){m.strokeStyle=B(e),m.stroke()},clip(e){m.beginPath(),e(m),m.clip()},sfx:(e,t,a)=>!!n.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=A,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),c.apply(0,e),e),volume(e){n.zzfxV=e},canvas:()=>h,use(e,t={}){var a=e,n=t;let l=a(N,n);for(let e in l)N.def(e,l[e])},resize(e,a=e,n){t.width=e,t.height=a,t.autoscale=null==n?t.autoscale:n,H()},listen:(e,t)=>{M[e=e.toLowerCase()]=M[e]||new Set,M[e].add(t)},unlisten:(e,t)=>{M[e=e.toLowerCase()]&&M[e].delete(t)},emit:(e,t,a,l,i)=>(p&&(q("before:"+(e=e.toLowerCase()),t,a,l,i),d||n[e]===N[e]||"function"!=typeof n[e]||n[e](t,a,l,i),q(e,t,a,l,i),q("after:"+e,t,a,l,i)),t),pal(e,t=3){null==e?T=L:(L=T,T=e),S=[],k=t,N.emit("pal",T,k)},palc(e,t){null==e?S=[]:S[e]=t},def(e,a){N[e]=a,t.global&&(n[e]=a)},timescale(e){x=e},framerate(e){v=1e3/~~e},stat:e=>[t,p,v/1e3,g,M,T,A,x,n.zzfxV,I,E,C,S,P][e],ispaused:()=>u,pause(){u||(u=!0,z=~~cancelAnimationFrame(z),N.emit("paused"))},resume(){p&&u&&(W(),u=!1,N.emit("resumed"))},quit(){for(let e of(N.emit("quit"),N.pause(),p=!1,M={},s))e();if(t.global){for(let e in N)delete n[e];delete n.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,min,max,pow,sqrt,sign".split(","))N[e]=l[e];function W(){z||(b=0,y=i.now(),z=r(X))}function X(){z=r(X);let e=i.now(),t=0,a=e-y;for(y=e,b+=a<100?a:v;b>=v;){t++,b-=v;let e=v/1e3*x;N.emit("update",e,t),N.def("T",N.T+e)}t&&(N.emit("draw",m),t>1&&(b=0))}function H(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(N.def("W",e),N.def("H",a),h.width=e,h.height=a,h.style="image-rendering:pixelated;touch-action:none",t.autoscale){let n=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),g=l.min(innerWidth/e,innerHeight/a),g=n>1&&g>n?n:g,h.style.width=e*g+"px",h.style.height=a*g+"px"}m.imageSmoothingEnabled=!1,N.textalign("start","top"),N.emit("resized",g)}function q(e,t,a,n,l){if(M[e])for(let i of M[e])i(t,a,n,l)}function B(e){return e>=0?T[~~(S[e]??e)%T.length]:e}if(t.global){if(n.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(n,N),n.ENGINE=N}if(a=document,m=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),f(h,"click",()=>focus()),h.parentNode||a.body.appendChild(h),h.oncontextmenu=()=>!1,H(),d)for(let e in d)d[e]&&N.listen(e,d[e]);return r(function(){if(t.autoscale&&f(n,"resize",H),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/g,(e.pageY-h.offsetTop)/g],t=new Map,a=(e,a,n)=>{let l={x:a,y:n,xi:a,yi:n,t:i.now()};return t.set(e,l),l},l=!1;f(h,"pointerdown",t=>{let[n,i]=e(t);if("mouse"===t.pointerType){if(t.button)return;l=!0}let o=t.pointerId;a(o,n,i),N.emit("tap",n,i,o),h.setPointerCapture(o)}),f(h,"pointermove",n=>{let[i,o]=e(n);if("mouse"===n.pointerType&&(N.def("MX",i),N.def("MY",o),!l))return;let r=n.pointerId;if(t.get(r)){let e;N.emit("tapping",i,o,r),(e=t.get(r)||a(r)).x=i,e.y=o}});let o=a=>{let n=a.pointerId,o=t.get(n),[r,s]=e(a);if(o){if("mouse"===a.pointerType){if(a.button)return;l=!1}o&&i.now()-o.t<=300&&N.emit("tapped",o.xi,o.yi,n),N.emit("untap",r,s,n),t.delete(n),h.releasePointerCapture(n)}};f(h,"pointerup",o),f(h,"pointercancel",o),f(n,"blur",()=>{for(let[e,a]of(l=!1,t)){N.emit("untap",a.x,a.y,e),t.delete(e);try{h.releasePointerCapture(e)}catch(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,l="";f(n,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n),l=" "===n?"space":n)}),f(n,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(n,"blur",()=>e.clear()),N.listen("after:update",()=>t.clear()),N.def("iskeydown",t=>a(e,t)),N.def("iskeypressed",e=>a(t,e)),N.def("lastkey",()=>l)}p=!0,N.emit("init",N),u||W()}),N}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"],t=e=>()=>{let t=e+=0x6d2b79f5;return t=Math.imul(t^t>>>15,1|t),(((t^=t+Math.imul(t^t>>>7,61|t))^t>>>14)>>>0)/0x100000000};window.litecanvas=function(a={}){let n,l=window,i=Math,o=performance,r=2*i.PI,s=requestAnimationFrame,f=[],d=(e,t,a)=>{e.addEventListener(t,a,!1),f.push(()=>e.removeEventListener(t,a,!1))},c=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,n=0,l=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,w=1,x=0,y=0,v=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,C=a*=(1-t+2*t*b.random(t=[]))*z/44100,E=0,P=0,I=0,T=1,L=0,M=0,S=0,A=v<0?-1:1,N=z*A*v*2/44100,W=b.cos(N),X=b.sin,H=X(N)/4,q=1+H,B=-2*W/q,V=(1-H)/q,D=(1+A*W)/2/q,O=-(A+W)/q,G=0,R=0,Y=0,$=0;for(n=44100*n+9,x*=44100,l*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,p=44100*p|0,e*=.3*zzfxV,A=n+x+l+i+g|0;I<A;t[I++]=S*e)++M%(100*m|0)||(S=o?1<o?2<o?3<o?X(E*E):b.max(b.min(b.tan(E),1),-1):1-(2*E/z%2+2)%2:1-4*b.abs(b.round(E/z)-E/z):X(E),S=(p?1-y+y*X(z*I/p):1)*(S<0?-1:1)*b.abs(S)**r*(I<n?I/n:I<n+x?1-(I-n)/x*(1-w):I<n+x+l?w:I<A-g?(A-I-g)/i*w:0),S=g?S/2+(g>I?0:(I<A-g?1:(A-I)/g)*t[I-g|0]/2/e):S,v&&(S=$=D*G+O*(G=R)+D*(R=S)-V*Y-B*(Y=$))),E+=(N=(a+=s+=f)*b.cos(h*P++))+N*u*X(I**5),T&&++T>c&&(a+=d,C+=d,T=0),!p||++L%p||(a=C,s=k,T=T||1);(e=zzfxX.createBuffer(1,A,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),p=(a=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},a)).loop,u,h,m,g=1,w,x=.5,y=1,v,b=1e3/60,z,k=0,C=3,E="sans-serif",P=20,I=1.2,T=Date.now(),L=t(T>>>0),M=e,S=e,A=[],N=[.5,0,1750,,,.3,1,,,,600,.1],W={},X={W:0,H:0,T:0,MX:-1,MY:-1,rand:(e=0,t=1)=>e+L()*(t-e),randi:(e=0,t=1)=>~~X.rand(e,t+1),rseed(e){L=t((T=e)>>>0)},TAU:r,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>i.PI/180*e,rad2deg:e=>180/i.PI*e,mod:(e,t)=>(e%t+t)%t||0,round:(e,t=0)=>{if(!t)return i.round(e);let a=10**t;return i.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,n)=>i.hypot(a-e,n-t),wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,n,l,i){let o=(e-t)/(a-t)*(l-n)+n;return i?X.clamp(o,n,l):o},norm:(e,t,a)=>X.map(e,t,a,0,1),cls(e){null==e?w.clearRect(0,0,X.W,X.H):X.rectfill(0,0,X.W,X.H,e)},rect(e,t,a,n,l,i){w.beginPath(),w[i?"roundRect":"rect"](~~e-x,~~t-x,~~a+2*x,~~n+2*x,i),X.stroke(l)},rectfill(e,t,a,n,l,i){w.beginPath(),w[i?"roundRect":"rect"](~~e,~~t,~~a,~~n,i),X.fill(l)},oval(e,t,a,n,l){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~n,0,0,r),X.stroke(l)},ovalfill(e,t,a,n,l){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~n,0,0,r),X.fill(l)},circ(e,t,a,n){X.oval(e,t,a,a,n)},circfill(e,t,a,n){X.ovalfill(e,t,a,a,n)},shape(e){w.beginPath();for(let t=0;t<e.length;t+=2)t?w.lineTo(~~e[t],~~e[t+1]):w.moveTo(~~e[t],~~e[t+1]);w.lineTo(~~e[0],~~e[1])},line(e,t,a,n,l){w.beginPath();let i=x&&~~e==~~a?.5:0,o=x&&~~t==~~n?.5:0;w.moveTo(~~e+i,~~t+o),w.lineTo(~~a+i,~~n+o),X.stroke(l)},linewidth(e){w.lineWidth=~~e,x=~~e%2?.5:0},linedash(e,t=0){w.setLineDash(e),w.lineDashOffset=t},text(e,t,a,n=C,l="normal"){w.font=`${l} ${P}px ${E}`,w.fillStyle=D(n);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)w.fillText(i[a],~~e,~~t+P*I*a)},textgap(e){I=e},textfont(e){E=e},textsize(e){P=e},textalign(e,t){e&&(w.textAlign=e),t&&(w.textBaseline=t)},image(e,t,a){w.drawImage(a,~~e,~~t)},spr(e,t,a){let n=a.replace(/[^\w.\n]/g,"").split("\n").filter(e=>e);for(let a=0;a<n.length;a++)for(let l=0;l<n[a].length;l++)"."!==n[a][l]&&X.rectfill(e+l,t+a,1,1,parseInt(n[a][l],36)||0)},paint(e,t,a,n={}){let l=n.canvas||new OffscreenCanvas(1,1),i=n.scale||1,o=w;return l.width=e*i,l.height=t*i,(w=l.getContext("2d")).scale(i,i),a(w),w=o,l.transferToImageBitmap()},ctx:e=>(e&&(w=e),w),push(e=0,t=e,a=0,n=1,l=n){w.save(),X.translate(e,t),X.rotate(a),X.scale(n,l)},pop(){w.restore()},translate(e,t){w.translate(~~e,~~t)},scale(e,t=e){w.scale(e,t)},rotate(e){w.rotate(e)},alpha(e){w.globalAlpha=X.clamp(e,0,1)},fill(e){w.fillStyle=D(e),w.fill()},stroke(e){w.strokeStyle=D(e),w.stroke()},clip(e){w.beginPath(),e(w),w.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=N,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),c.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>m,use(e,t={}){var a=e,n=t;let l=a(X,n);for(let e in l)X.def(e,l[e])},resize(e,t=e,n){a.width=e,a.height=t,a.autoscale=null==n?a.autoscale:n,B()},listen:(e,t)=>{W[e=e.toLowerCase()]=W[e]||new Set,W[e].add(t)},unlisten:(e,t)=>{W[e=e.toLowerCase()]&&W[e].delete(t)},emit:(e,t,a,n,i)=>(u&&(V("before:"+(e=e.toLowerCase()),t,a,n,i),p||l[e]===X[e]||"function"!=typeof l[e]||l[e](t,a,n,i),V(e,t,a,n,i),V("after:"+e,t,a,n,i)),t),pal(e,t=3){null==e?M=S:(S=M,M=e),A=[],C=t,X.emit("pal",M,C)},palc(e,t){null==e?A=[]:A[e]=t},def(e,t){X[e]=t,a.global&&(l[e]=t)},timescale(e){y=e},framerate(e){b=1e3/~~e},stat:e=>[a,u,b/1e3,g,W,M,N,y,l.zzfxV,T,P,E,A,I][e],ispaused:()=>h,pause(){h||(h=!0,k=~~cancelAnimationFrame(k),X.emit("paused"))},resume(){u&&h&&(H(),h=!1,X.emit("resumed"))},quit(){for(let e of(X.emit("quit"),X.pause(),u=!1,W={},f))e();if(a.global){for(let e in X)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,min,max,pow,sqrt,sign".split(","))X[e]=i[e];function H(){k||(z=0,v=o.now(),k=s(q))}function q(){k=s(q);let e=o.now(),t=0,a=e-v;for(v=e,z+=a<100?a:b;z>=b;){t++,z-=b;let e=b/1e3*y;X.emit("update",e,t),X.def("T",X.T+e)}t&&(X.emit("draw",w),t>1&&(z=0))}function B(){let e=a.width>0?a.width:innerWidth,t=a.width>0?a.height||a.width:innerHeight;if(X.def("W",e),X.def("H",t),m.width=e,m.height=t,m.style="image-rendering:pixelated;touch-action:none",a.autoscale){let n=+a.autoscale;m.style.display||(m.style.display="block",m.style.margin="auto"),g=i.min(innerWidth/e,innerHeight/t),g=n>1&&g>n?n:g,m.style.width=e*g+"px",m.style.height=t*g+"px"}w.imageSmoothingEnabled=!1,X.textalign("start","top"),X.emit("resized",g)}function V(e,t,a,n,l){if(W[e])for(let i of W[e])i(t,a,n,l)}function D(e){return e>=0?M[~~(A[e]??e)%M.length]:e}if(a.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,X),l.ENGINE=X}if(n=document,w=(m=(m="string"==typeof a.canvas?n.querySelector(a.canvas):a.canvas)||n.createElement("canvas")).getContext("2d"),d(m,"click",()=>focus()),m.parentNode||n.body.appendChild(m),m.oncontextmenu=()=>!1,B(),p)for(let e in p)p[e]&&X.listen(e,p[e]);return s(function(){if(a.autoscale&&d(l,"resize",B),a.tapEvents){let e=e=>[(e.pageX-m.offsetLeft)/g,(e.pageY-m.offsetTop)/g],t=new Map,a=(e,a,n)=>{let l={x:a,y:n,xi:a,yi:n,t:o.now()};return t.set(e,l),l},n=!1;d(m,"pointerdown",t=>{let[l,i]=e(t);if("mouse"===t.pointerType){if(t.button)return;n=!0}let o=t.pointerId;a(o,l,i),X.emit("tap",l,i,o),m.setPointerCapture(o)}),d(m,"pointermove",l=>{let[i,o]=e(l);if("mouse"===l.pointerType&&(X.def("MX",i),X.def("MY",o),!n))return;let r=l.pointerId;if(t.get(r)){let e;X.emit("tapping",i,o,r),(e=t.get(r)||a(r)).x=i,e.y=o}});let i=a=>{let l=a.pointerId,i=t.get(l),[r,s]=e(a);if(i){if("mouse"===a.pointerType){if(a.button)return;n=!1}i&&o.now()-i.t<=300&&X.emit("tapped",i.xi,i.yi,l),X.emit("untap",r,s,l),t.delete(l),m.releasePointerCapture(l)}};d(m,"pointerup",i),d(m,"pointercancel",i),d(l,"blur",()=>{for(let[e,a]of(n=!1,t)){X.emit("untap",a.x,a.y,e),t.delete(e);try{m.releasePointerCapture(e)}catch(e){}}})}if(a.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";d(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),d(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),d(l,"blur",()=>e.clear()),X.listen("after:update",()=>t.clear()),X.def("iskeydown",t=>a(e,t)),X.def("iskeypressed",e=>a(t,e)),X.def("lastkey",()=>n)}u=!0,X.emit("init",X),h||H()}),X}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.401.0",
3
+ "version": "0.402.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>",
@@ -32,10 +32,10 @@
32
32
  "creative coding"
33
33
  ],
34
34
  "devDependencies": {
35
- "@happy-dom/global-registrator": "^21.0.1",
35
+ "@happy-dom/global-registrator": "^21.0.3",
36
36
  "@size-limit/preset-small-lib": "^14.2.0",
37
37
  "@swc/core": "^1.16.18",
38
- "ava": "^8.0.1",
38
+ "ava": "^8.1.0",
39
39
  "esbuild": "^0.28.2",
40
40
  "genversion": "^3.2.0",
41
41
  "gzip-size": "^7.0.0",
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ import { setupZzFX } from './zzfx.js'
3
3
  import { defaultPalette } from './palette.js'
4
4
  import { assert } from './dev.js'
5
5
  import { version } from './version.js'
6
+ import { mulberry32 } from './mulberry32.js'
6
7
 
7
8
  /**
8
9
  * The litecanvas constructor
@@ -84,13 +85,15 @@ export default function litecanvas(settings = {}) {
84
85
  _fontLineHeight = 1.2,
85
86
  /** @type {number} */
86
87
  _rngSeed = Date.now(),
88
+ /** @type {() => number} */
89
+ _rngNext = mulberry32(_rngSeed >>> 0),
87
90
  /** @type {string[]} */
88
91
  _currentPalette = defaultPalette,
89
92
  /** @type {string[]} */
90
93
  _lastPalette = defaultPalette,
91
94
  /** @type {number[]} */
92
95
  _colorPaletteState = [],
93
- /** @type {number[]} */
96
+ /** @type {(number|undefined)[]} */
94
97
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
95
98
  /**
96
99
  * @type {Object<string,Set<Function>>} game event listeners
@@ -114,6 +117,54 @@ export default function litecanvas(settings = {}) {
114
117
  /** @type {number} */
115
118
  MY: -1,
116
119
 
120
+ /** RNG API */
121
+ /**
122
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
123
+ * using the Mulberry32 algorithm.
124
+ *
125
+ * @param {number} [min=0.0]
126
+ * @param {number} [max=1.0]
127
+ * @returns {number} the random number
128
+ */
129
+ rand: (min = 0.0, max = 1.0) => {
130
+ DEV: assert(isNumber(min), 'rand() 1st argument must be a number')
131
+ DEV: assert(isNumber(max), 'rand() 2nd argument must be a number')
132
+ DEV: assert(max >= min, 'rand() the 1st argument must be less than the 2nd argument')
133
+
134
+ return min + _rngNext() * (max - min)
135
+ },
136
+
137
+ /**
138
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
139
+ *
140
+ * @param {number} [min=0]
141
+ * @param {number} [max=1]
142
+ * @returns {number} the random number
143
+ */
144
+ randi: (min = 0, max = 1) => {
145
+ DEV: assert(isNumber(min), 'randi() 1st argument must be a number')
146
+ DEV: assert(isNumber(max), 'randi() 2nd argument must be a number')
147
+ DEV: assert(max >= min, 'randi() the 1st argument must be less than the 2nd argument')
148
+
149
+ return ~~instance.rand(min, max + 1)
150
+ },
151
+
152
+ /**
153
+ * Initializes the random number generator with an explicit seed value.
154
+ *
155
+ * Note: The seed should be a integer number greater than or equal to zero.
156
+ *
157
+ * @param {number} value
158
+ */
159
+ rseed(value) {
160
+ DEV: assert(
161
+ isNumber(value) && value >= 0,
162
+ 'rseed() 1st argument must be a non-negative integer'
163
+ )
164
+ _rngSeed = value
165
+ _rngNext = mulberry32(_rngSeed >>> 0)
166
+ },
167
+
117
168
  /** MATH API */
118
169
  /**
119
170
  * Twice the value of the mathematical constant PI (π).
@@ -306,60 +357,6 @@ export default function litecanvas(settings = {}) {
306
357
  return instance.map(value, start, stop, 0, 1)
307
358
  },
308
359
 
309
- /** RNG API */
310
- /**
311
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
312
- * using the Linear Congruential Generator (LCG) algorithm.
313
- *
314
- * @param {number} [min=0.0]
315
- * @param {number} [max=1.0]
316
- * @returns {number} the random number
317
- */
318
- rand: (min = 0.0, max = 1.0) => {
319
- DEV: assert(isNumber(min), 'rand() 1st argument must be a number')
320
- DEV: assert(isNumber(max), 'rand() 2nd argument must be a number')
321
- DEV: assert(max >= min, 'rand() the 1st argument must be less than the 2nd argument')
322
-
323
- const a = 1664525
324
- const c = 1013904223
325
- const m = 4294967296
326
-
327
- _rngSeed = (a * _rngSeed + c) % m
328
-
329
- return (_rngSeed / m) * (max - min) + min
330
- },
331
-
332
- /**
333
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
334
- *
335
- * @param {number} [min=0]
336
- * @param {number} [max=1]
337
- * @returns {number} the random number
338
- */
339
- randi: (min = 0, max = 1) => {
340
- DEV: assert(isNumber(min), 'randi() 1st argument must be a number')
341
- DEV: assert(isNumber(max), 'randi() 2nd argument must be a number')
342
- DEV: assert(max >= min, 'randi() the 1st argument must be less than the 2nd argument')
343
-
344
- return ~~instance.rand(min, max + 1)
345
- },
346
-
347
- /**
348
- * Initializes the random number generator with an explicit seed value.
349
- *
350
- * Note: The seed should be a integer number greater than or equal to zero.
351
- *
352
- * @param {number} value
353
- */
354
- rseed(value) {
355
- DEV: assert(
356
- isNumber(value) && value >= 0,
357
- 'rseed() 1st argument must be a non-negative integer'
358
- )
359
-
360
- _rngSeed = ~~value
361
- },
362
-
363
360
  /** BASIC GRAPHICS API */
364
361
  /**
365
362
  * Clear the game screen with an optional color.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
3
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
4
+ * The same seed always gives the same sequence.
5
+ *
6
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
7
+ * @license CC0-1.0 (Public Domain)
8
+ *
9
+ * @param {number} seed an unsigned 32-bit integer
10
+ * @param {number} t
11
+ * @param {typeof Math.imul} i
12
+ * @returns {() => number}
13
+ */
14
+ export const mulberry32 = (seed) => {
15
+ return () => {
16
+ let t = (seed += 0x6d2b79f5)
17
+ t = Math.imul(t ^ (t >>> 15), t | 1)
18
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
19
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296
20
+ }
21
+ }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.401.0'
2
+ export const version = '0.402.0'
package/types/global.d.ts CHANGED
@@ -186,7 +186,8 @@ declare global {
186
186
 
187
187
  /** RNG API */
188
188
  /**
189
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
189
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
190
+ * using the Mulberry32 algorithm.
190
191
  *
191
192
  * @param [min=0.0]
192
193
  * @param [max=1.0]
@@ -194,7 +195,7 @@ declare global {
194
195
  */
195
196
  function rand(min?: number, max?: number): number
196
197
  /**
197
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
198
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
198
199
  *
199
200
  * @param [min=0]
200
201
  * @param [max=1]
@@ -204,9 +205,7 @@ declare global {
204
205
  /**
205
206
  * Initializes the random number generator with an explicit seed value.
206
207
  *
207
- * Note: The seed should be a integer number greater than or equal to zero.
208
- *
209
- * @param value
208
+ * @param value the PRNG seed. Must be an non-negative number.
210
209
  */
211
210
  function rseed(value: number): void
212
211
 
package/types/types.d.ts CHANGED
@@ -180,7 +180,8 @@ type LitecanvasInstance = {
180
180
 
181
181
  /** RNG API */
182
182
  /**
183
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
183
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
184
+ * using the Mulberry32 algorithm.
184
185
  *
185
186
  * @param [min=0.0]
186
187
  * @param [max=1.0]
@@ -188,7 +189,7 @@ type LitecanvasInstance = {
188
189
  */
189
190
  rand(min?: number, max?: number): number
190
191
  /**
191
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
192
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive)
192
193
  *
193
194
  * @param [min=0]
194
195
  * @param [max=1]
@@ -196,11 +197,11 @@ type LitecanvasInstance = {
196
197
  */
197
198
  randi(min?: number, max?: number): number
198
199
  /**
199
- * Initializes the random number generator with an explicit seed value.
200
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
200
201
  *
201
- * Note: The seed should be a integer number greater than or equal to zero.
202
- *
203
- * @param value
202
+ * @param [min=0]
203
+ * @param [max=1]
204
+ * @returns the random number
204
205
  */
205
206
  rseed(value: number): void
206
207