q5 1.9.2 → 1.9.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.
Files changed (4) hide show
  1. package/README.md +135 -81
  2. package/package.json +1 -1
  3. package/q5.js +424 -547
  4. package/q5.min.js +1 -1
package/q5.js CHANGED
@@ -10,31 +10,30 @@
10
10
  */
11
11
  function Q5(scope, parent) {
12
12
  let preloadCnt = 0;
13
- if (typeof scope == 'undefined') {
13
+ if (!scope) {
14
14
  scope = 'global';
15
15
  preloadCnt++;
16
16
  setTimeout(() => preloadCnt--, 32);
17
17
  }
18
18
  if (scope == 'auto') {
19
- if (typeof window.setup == 'undefined') return;
19
+ if (!(window.setup || window.draw)) return;
20
20
  else scope = 'global';
21
21
  }
22
- if (arguments.length == 1 && typeof scope != 'string' && typeof scope != 'function') {
23
- parent = arguments[0];
24
- scope = null;
25
- }
26
22
  if (scope == 'global') Q5._hasGlobal = true;
27
23
 
24
+ // CANVAS
25
+
28
26
  let $ = this;
29
- $.canvas = document.createElement('canvas');
30
- let ctx = ($._ctx = $.canvas.getContext('2d'));
31
- $.canvas.classList.add('p5Canvas', 'q5Canvas');
32
- $.canvas.id = 'defaultCanvas' + Q5._instanceCount++;
33
-
34
- $.width = 100;
35
- $.height = 100;
36
- $.canvas.width = $.width;
37
- $.canvas.height = $.height;
27
+ if (scope == 'image' || scope == 'graphics') {
28
+ $.canvas = new OffscreenCanvas(100, 100);
29
+ } else {
30
+ $.canvas = document.createElement('canvas');
31
+ $.canvas.id = 'defaultCanvas' + Q5._instanceCount++;
32
+ $.canvas.classList.add('p5Canvas', 'q5Canvas');
33
+ }
34
+
35
+ $.canvas.width = $.width = 100;
36
+ $.canvas.height = $.height = 100;
38
37
  $._windowResizedFn = () => {};
39
38
 
40
39
  if (scope != 'graphics' && scope != 'image') {
@@ -42,6 +41,9 @@ function Q5(scope, parent) {
42
41
  $._resize = () => {
43
42
  if ($.frameCount > 1) $._shouldResize = true;
44
43
  };
44
+ if (parent && typeof parent == 'string') {
45
+ parent = document.getElementById(parent);
46
+ }
45
47
  $.canvas.parent = (el) => {
46
48
  if (typeof el == 'string') el = document.getElementById(el);
47
49
  el.append($.canvas);
@@ -63,21 +65,27 @@ function Q5(scope, parent) {
63
65
  $.canvas.parent(parent);
64
66
  }
65
67
  if (document.body) appendCanvas();
66
- else window.addEventListener('load', appendCanvas);
68
+ else document.addEventListener('DOMContentLoaded', appendCanvas);
67
69
  }
68
70
 
69
- defaultStyle();
70
-
71
- $.MAGIC = 0x9a0ce55;
71
+ $._q5 = true;
72
72
  $.pixels = [];
73
73
  let imgData = null;
74
-
75
- $.createCanvas = function (width, height) {
76
- $.width = width;
77
- $.height = height;
78
- $.canvas.width = width;
79
- $.canvas.height = height;
74
+ let ctx;
75
+ $.ctx = $.drawingContext = null;
76
+
77
+ $.createCanvas = function (width, height, renderer, options) {
78
+ if (renderer == 'webgl') throw `webgl renderer is not supported in q5, use '2d'`;
79
+ $.width = $.canvas.width = width;
80
+ $.height = $.canvas.height = height;
81
+ $.canvas.renderer = '2d';
82
+ let opt = Object.assign({}, Q5.canvasOptions);
83
+ if (options) Object.assign(opt, options);
84
+
85
+ ctx = $.ctx = $.drawingContext = $.canvas.getContext('2d', opt);
86
+ Object.assign($.canvas, opt);
80
87
  defaultStyle();
88
+ ctx.save();
81
89
  if (scope != 'image') {
82
90
  let pd = $.displayDensity();
83
91
  if (scope == 'graphics') pd = this._pixelDensity;
@@ -87,9 +95,7 @@ function Q5(scope, parent) {
87
95
  };
88
96
  $._createCanvas = $.createCanvas;
89
97
 
90
- //================================================================
91
98
  // IMAGE
92
- //================================================================
93
99
 
94
100
  $.loadPixels = () => {
95
101
  imgData = ctx.getImageData(0, 0, $.canvas.width, $.canvas.height);
@@ -99,7 +105,7 @@ function Q5(scope, parent) {
99
105
  if (imgData != null) ctx.putImageData(imgData, 0, 0);
100
106
  };
101
107
 
102
- let filterImpl = {};
108
+ let filterImpl = [];
103
109
  filterImpl[$.THRESHOLD] = (data, thresh) => {
104
110
  if (thresh === undefined) thresh = 127.5;
105
111
  else thresh *= 255;
@@ -138,7 +144,7 @@ function Q5(scope, parent) {
138
144
  filterImpl[$.DILATE] = (data) => {
139
145
  makeTmpBuf();
140
146
  tmpBuf.set(data);
141
- let [w, h] = [ctx.canvas.width, ctx.canvas.height];
147
+ let [w, h] = [$.canvas.width, $.canvas.height];
142
148
  for (let i = 0; i < h; i++) {
143
149
  for (let j = 0; j < w; j++) {
144
150
  let l = 4 * Math.max(j - 1, 0);
@@ -165,7 +171,7 @@ function Q5(scope, parent) {
165
171
  filterImpl[$.ERODE] = (data) => {
166
172
  makeTmpBuf();
167
173
  tmpBuf.set(data);
168
- let [w, h] = [ctx.canvas.width, ctx.canvas.height];
174
+ let [w, h] = [$.canvas.width, $.canvas.height];
169
175
  for (let i = 0; i < h; i++) {
170
176
  for (let j = 0; j < w; j++) {
171
177
  let l = 4 * Math.max(j - 1, 0);
@@ -189,7 +195,6 @@ function Q5(scope, parent) {
189
195
  }
190
196
  }
191
197
  };
192
-
193
198
  filterImpl[$.BLUR] = (data, rad) => {
194
199
  rad = rad || 1;
195
200
  rad = Math.floor(rad * $._pixelDensity);
@@ -211,7 +216,7 @@ function Q5(scope, parent) {
211
216
  }
212
217
 
213
218
  let kern = gauss1d(ksize);
214
- let [w, h] = [ctx.canvas.width, ctx.canvas.height];
219
+ let [w, h] = [$.canvas.width, $.canvas.height];
215
220
  for (let i = 0; i < h; i++) {
216
221
  for (let j = 0; j < w; j++) {
217
222
  let s0 = 0,
@@ -258,22 +263,27 @@ function Q5(scope, parent) {
258
263
  };
259
264
 
260
265
  function makeTmpCtx(w, h) {
266
+ h ??= w || $.canvas.height;
267
+ w ??= $.canvas.width;
261
268
  if (tmpCtx == null) {
262
- tmpCtx = document.createElement('canvas').getContext('2d');
269
+ tmpCtx = new OffscreenCanvas(w, h).getContext('2d', {
270
+ colorSpace: $.canvas.colorSpace
271
+ });
263
272
  }
264
- h ??= w || ctx.canvas.height;
265
- w ??= ctx.canvas.width;
266
273
  if (tmpCtx.canvas.width != w || tmpCtx.canvas.height != h) {
267
274
  tmpCtx.canvas.width = w;
268
275
  tmpCtx.canvas.height = h;
269
276
  }
270
277
  }
278
+
271
279
  function makeTmpCt2(w, h) {
280
+ h ??= w || $.canvas.height;
281
+ w ??= $.canvas.width;
272
282
  if (tmpCt2 == null) {
273
- tmpCt2 = document.createElement('canvas').getContext('2d');
283
+ tmpCt2 = new OffscreenCanvas(w, h).getContext('2d', {
284
+ colorSpace: $.canvas.colorSpace
285
+ });
274
286
  }
275
- h ??= w || ctx.canvas.height;
276
- w ??= ctx.canvas.width;
277
287
  if (tmpCt2.canvas.width != w || tmpCt2.canvas.height != h) {
278
288
  tmpCt2.canvas.width = w;
279
289
  tmpCt2.canvas.height = h;
@@ -281,7 +291,7 @@ function Q5(scope, parent) {
281
291
  }
282
292
 
283
293
  function makeTmpBuf() {
284
- let l = ctx.canvas.width * ctx.canvas.height * 4;
294
+ let l = $.canvas.width * $.canvas.height * 4;
285
295
  if (!tmpBuf || l != tmpBuf.length) {
286
296
  tmpBuf = new Uint8ClampedArray(l);
287
297
  }
@@ -290,60 +300,60 @@ function Q5(scope, parent) {
290
300
  function nativeFilter(filtstr) {
291
301
  tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
292
302
  tmpCtx.filter = filtstr;
293
- tmpCtx.drawImage(ctx.canvas, 0, 0);
303
+ tmpCtx.drawImage($.canvas, 0, 0);
294
304
  ctx.save();
295
305
  ctx.resetTransform();
296
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
306
+ ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
297
307
  ctx.drawImage(tmpCtx.canvas, 0, 0);
298
308
  ctx.restore();
299
309
  }
300
310
 
311
+ function softFilter(typ, x) {
312
+ let imgData = ctx.getImageData(0, 0, $.canvas.width, $.canvas.height);
313
+ filterImpl[typ](imgData.data, x);
314
+ ctx.putImageData(imgData, 0, 0);
315
+ }
316
+
301
317
  $.filter = (typ, x) => {
302
- let support = $.HARDWARE_FILTERS && ctx.filter != undefined;
303
- if (support) {
304
- makeTmpCtx();
305
- if (typ == $.THRESHOLD) {
306
- x ??= 0.5;
307
- x = Math.max(x, 0.00001);
308
- let b = Math.floor((0.5 / x) * 100);
309
- nativeFilter(`saturate(0%) brightness(${b}%) contrast(1000000%)`);
310
- } else if (typ == $.GRAY) {
311
- nativeFilter(`saturate(0%)`);
312
- } else if (typ == $.OPAQUE) {
313
- tmpCtx.fillStyle = 'black';
314
- tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
315
- tmpCtx.drawImage(ctx.canvas, 0, 0);
316
- ctx.save();
317
- ctx.resetTransform();
318
- ctx.drawImage(tmpCtx.canvas, 0, 0);
319
- ctx.restore();
320
- } else if (typ == $.INVERT) {
321
- nativeFilter(`invert(100%)`);
322
- } else if (typ == $.BLUR) {
323
- nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`);
324
- } else {
325
- let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
326
- filterImpl[typ](imgData.data, x);
327
- ctx.putImageData(imgData, 0, 0);
328
- }
318
+ if (!ctx.filter) return softFilter(typ, x);
319
+ makeTmpCtx();
320
+ if (typeof typ == 'string') {
321
+ nativeFilter(typ);
322
+ } else if (typ == $.THRESHOLD) {
323
+ x ??= 0.5;
324
+ x = Math.max(x, 0.00001);
325
+ let b = Math.floor((0.5 / x) * 100);
326
+ nativeFilter(`saturate(0%) brightness(${b}%) contrast(1000000%)`);
327
+ } else if (typ == $.GRAY) {
328
+ nativeFilter(`saturate(0%)`);
329
+ } else if (typ == $.OPAQUE) {
330
+ tmpCtx.fillStyle = 'black';
331
+ tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
332
+ tmpCtx.drawImage($.canvas, 0, 0);
333
+ ctx.save();
334
+ ctx.resetTransform();
335
+ ctx.drawImage(tmpCtx.canvas, 0, 0);
336
+ ctx.restore();
337
+ } else if (typ == $.INVERT) {
338
+ nativeFilter(`invert(100%)`);
339
+ } else if (typ == $.BLUR) {
340
+ nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`);
329
341
  } else {
330
- let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
331
- filterImpl[typ](imgData.data, x);
332
- ctx.putImageData(imgData, 0, 0);
342
+ softFilter(typ, x);
333
343
  }
334
344
  };
335
345
 
336
346
  $.resize = (w, h) => {
337
347
  makeTmpCtx();
338
- tmpCtx.drawImage(ctx.canvas, 0, 0);
348
+ tmpCtx.drawImage($.canvas, 0, 0);
339
349
  $.width = w;
340
350
  $.height = h;
341
- ctx.canvas.width = w * $._pixelDensity;
342
- ctx.canvas.height = h * $._pixelDensity;
351
+ $.canvas.width = w * $._pixelDensity;
352
+ $.canvas.height = h * $._pixelDensity;
343
353
  ctx.save();
344
354
  ctx.resetTransform();
345
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
346
- ctx.drawImage(tmpCtx.canvas, 0, 0, ctx.canvas.width, ctx.canvas.height);
355
+ ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
356
+ ctx.drawImage(tmpCtx.canvas, 0, 0, $.canvas.width, $.canvas.height);
347
357
  ctx.restore();
348
358
  };
349
359
 
@@ -351,7 +361,7 @@ function Q5(scope, parent) {
351
361
  let pd = $._pixelDensity || 1;
352
362
  if (x !== undefined && w === undefined) {
353
363
  let c = ctx.getImageData(x * pd, y * pd, 1, 1).data;
354
- return new Q5.Color(c[0], c[1], c[2], c[3] / 255);
364
+ return new $.Color(c[0], c[1], c[2], c[3] / 255);
355
365
  }
356
366
  x = (x || 0) * pd;
357
367
  y = (y || 0) * pd;
@@ -361,7 +371,7 @@ function Q5(scope, parent) {
361
371
  h *= pd;
362
372
  let img = $.createImage(w, h);
363
373
  let imgData = ctx.getImageData(x, y, w, h);
364
- img.canvas.getContext('2d').putImageData(imgData, 0, 0);
374
+ img.ctx.putImageData(imgData, 0, 0);
365
375
  img._pixelDensity = pd;
366
376
  img.width = _w;
367
377
  img.height = _h;
@@ -369,28 +379,29 @@ function Q5(scope, parent) {
369
379
  };
370
380
 
371
381
  $.set = (x, y, c) => {
372
- if (c.MAGIC == $.MAGIC) {
382
+ if (c._q5) {
373
383
  let old = $._tint;
374
384
  $._tint = null;
375
385
  $.image(c, x, y);
376
386
  $._tint = old;
377
387
  return;
378
388
  }
389
+ if (!$.pixels.length) $.loadPixels();
379
390
  let mod = $._pixelDensity || 1;
380
391
  for (let i = 0; i < mod; i++) {
381
392
  for (let j = 0; j < mod; j++) {
382
- let idx = 4 * ((y * mod + i) * ctx.canvas.width + x * mod + j);
383
- $.pixels[idx] = c._r;
384
- $.pixels[idx + 1] = c._g;
385
- $.pixels[idx + 2] = c._b;
386
- $.pixels[idx + 3] = c._a * 255;
393
+ let idx = 4 * ((y * mod + i) * $.canvas.width + x * mod + j);
394
+ $.pixels[idx] = c.r ?? c.l;
395
+ $.pixels[idx + 1] = c.g ?? c.c;
396
+ $.pixels[idx + 2] = c.b ?? c.h;
397
+ $.pixels[idx + 3] = c.a;
387
398
  }
388
399
  }
389
400
  };
390
401
 
391
402
  $.tinted = function (col) {
392
- let alpha = col._a;
393
- col._a = 1;
403
+ let alpha = col.a;
404
+ col.a = 255;
394
405
  makeTmpCtx();
395
406
  tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
396
407
  tmpCtx.fillStyle = col;
@@ -407,7 +418,7 @@ function Q5(scope, parent) {
407
418
  ctx.globalCompositeOperation = old;
408
419
  ctx.restore();
409
420
 
410
- tmpCtx.globalAlpha = alpha;
421
+ tmpCtx.globalAlpha = alpha / 255;
411
422
  tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
412
423
  tmpCtx.drawImage(ctx.canvas, 0, 0);
413
424
  tmpCtx.globalAlpha = 1;
@@ -418,8 +429,8 @@ function Q5(scope, parent) {
418
429
  ctx.drawImage(tmpCtx.canvas, 0, 0);
419
430
  ctx.restore();
420
431
  };
421
- $.tint = function () {
422
- $._tint = $.color(...Array.from(arguments));
432
+ $.tint = function (c) {
433
+ $._tint = c._q5Color ? c : $.color(...arguments);
423
434
  };
424
435
  $.noTint = () => ($._tint = null);
425
436
 
@@ -458,7 +469,7 @@ function Q5(scope, parent) {
458
469
  if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
459
470
  c = b;
460
471
  b = a;
461
- a = ctx.canvas;
472
+ a = $.canvas;
462
473
  }
463
474
  if (c) return $._save(a, b, c);
464
475
  if (b) {
@@ -469,9 +480,8 @@ function Q5(scope, parent) {
469
480
  $.canvas.save = $.save;
470
481
  $.saveCanvas = $.save;
471
482
 
472
- //================================================================
473
483
  // PRIVATE VARS
474
- //================================================================
484
+
475
485
  let looper = null;
476
486
  let firstVertex = true;
477
487
  let curveBuff = [];
@@ -481,36 +491,18 @@ function Q5(scope, parent) {
481
491
  let tmpCt2 = null;
482
492
  let tmpBuf = null;
483
493
 
484
- if (scope == 'image') return;
485
-
486
- $.remove = () => {
487
- $.noLoop();
488
- $.canvas.remove();
489
- };
490
-
491
- //================================================================
492
494
  // CONSTANTS
493
- //================================================================
494
495
 
495
- $.RGB = 0;
496
- $.HSV = 1;
497
- $.HSB = 1;
498
-
499
- $.CHORD = 0;
500
- $.PIE = 1;
501
- $.OPEN = 2;
502
-
503
- $.RADIUS = 'radius';
504
- $.CORNER = 'corner';
505
- $.CORNERS = 'corners';
506
-
507
- $.ROUND = 'round';
508
- $.SQUARE = 'butt';
509
- $.PROJECT = 'square';
510
- $.MITER = 'miter';
511
- $.BEVEL = 'bevel';
496
+ $.THRESHOLD = 1;
497
+ $.GRAY = 2;
498
+ $.OPAQUE = 3;
499
+ $.INVERT = 4;
500
+ $.POSTERIZE = 5;
501
+ $.DILATE = 6;
502
+ $.ERODE = 7;
503
+ $.BLUR = 8;
512
504
 
513
- $.CLOSE = 1;
505
+ if (scope == 'image') return;
514
506
 
515
507
  $.BLEND = 'source-over';
516
508
  $.REMOVE = 'destination-out';
@@ -529,6 +521,26 @@ function Q5(scope, parent) {
529
521
  $.DODGE = 'color-dodge';
530
522
  $.BURN = 'color-burn';
531
523
 
524
+ $.RGB = 'rgb';
525
+ $.RGBA = 'rgb';
526
+ $.HSB = 'hsb';
527
+
528
+ $.CHORD = 0;
529
+ $.PIE = 1;
530
+ $.OPEN = 2;
531
+
532
+ $.RADIUS = 'radius';
533
+ $.CORNER = 'corner';
534
+ $.CORNERS = 'corners';
535
+
536
+ $.ROUND = 'round';
537
+ $.SQUARE = 'butt';
538
+ $.PROJECT = 'square';
539
+ $.MITER = 'miter';
540
+ $.BEVEL = 'bevel';
541
+
542
+ $.CLOSE = 1;
543
+
532
544
  $.NORMAL = 'normal';
533
545
  $.ITALIC = 'italic';
534
546
  $.BOLD = 'bold';
@@ -568,15 +580,6 @@ function Q5(scope, parent) {
568
580
  $.TAU = Math.PI * 2;
569
581
  $.TWO_PI = Math.PI * 2;
570
582
 
571
- $.THRESHOLD = 1;
572
- $.GRAY = 2;
573
- $.OPAQUE = 3;
574
- $.INVERT = 4;
575
- $.POSTERIZE = 5;
576
- $.DILATE = 6;
577
- $.ERODE = 7;
578
- $.BLUR = 8;
579
-
580
583
  $.ARROW = 'default';
581
584
  $.CROSS = 'crosshair';
582
585
  $.HAND = 'pointer';
@@ -589,18 +592,17 @@ function Q5(scope, parent) {
589
592
  $.SHR3 = 1;
590
593
  $.LCG = 2;
591
594
 
592
- $.HARDWARE_FILTERS = true;
593
595
  $.hint = (prop, val) => {
594
596
  $[prop] = val;
595
597
  };
596
598
 
597
- //================================================================
598
599
  // PUBLIC PROPERTIES
599
- //================================================================
600
+
600
601
  $.frameCount = 0;
601
602
  $.deltaTime = 16;
602
603
  $.mouseX = 0;
603
604
  $.mouseY = 0;
605
+ $.touches = [];
604
606
  $.mouseButton = null;
605
607
  $.keyIsPressed = false;
606
608
  $.mouseIsPressed = false;
@@ -615,7 +617,6 @@ function Q5(scope, parent) {
615
617
  $.relRotationX = 0;
616
618
  $.relRotationY = 0;
617
619
  $.relRotationZ = 0;
618
-
619
620
  $.pmouseX = 0;
620
621
  $.pmouseY = 0;
621
622
  $.pAccelerationX = 0;
@@ -628,55 +629,40 @@ function Q5(scope, parent) {
628
629
  $.pRelRotationY = 0;
629
630
  $.pRelRotationZ = 0;
630
631
 
631
- $.touches = [];
632
+ Object.defineProperty($, 'deviceOrientation', {
633
+ get: () => window.screen?.orientation?.type
634
+ });
635
+ Object.defineProperty($, 'windowWidth', {
636
+ get: () => window.innerWidth
637
+ });
638
+ Object.defineProperty($, 'windowHeight', {
639
+ get: () => window.innerHeight
640
+ });
641
+
642
+ // PRIVATE PROPERTIES
632
643
 
633
- $._colorMode = $.RGB;
644
+ $._colorMode = 'rgb';
634
645
  $._doStroke = true;
635
646
  $._doFill = true;
636
647
  $._strokeSet = false;
637
648
  $._fillSet = false;
649
+ $._tint = null;
638
650
  $._ellipseMode = $.CENTER;
639
651
  $._rectMode = $.CORNER;
640
652
  $._curveDetail = 20;
641
653
  $._curveAlpha = 0.0;
642
654
  $._loop = true;
643
-
644
655
  $._textFont = 'sans-serif';
645
656
  $._textSize = 12;
646
657
  $._textLeading = 15;
647
658
  $._textLeadDiff = 3;
648
659
  $._textStyle = 'normal';
649
-
650
660
  $._pixelDensity = 1;
651
661
  $._lastFrameTime = 0;
652
662
  $._targetFrameRate = null;
653
663
  $._frameRate = $._fps = 60;
654
664
 
655
- $._tint = null;
656
-
657
- //================================================================
658
- // ALIAS PROPERTIES
659
- //================================================================
660
-
661
- Object.defineProperty($, 'deviceOrientation', {
662
- get: () => window.screen?.orientation?.type
663
- });
664
-
665
- Object.defineProperty($, 'windowWidth', {
666
- get: () => window.innerWidth
667
- });
668
-
669
- Object.defineProperty($, 'windowHeight', {
670
- get: () => window.innerHeight
671
- });
672
-
673
- Object.defineProperty($, 'drawingContext', {
674
- get: () => ctx
675
- });
676
-
677
- //================================================================
678
665
  // CANVAS
679
- //================================================================
680
666
 
681
667
  function cloneCtx() {
682
668
  let c = {};
@@ -687,25 +673,25 @@ function Q5(scope, parent) {
687
673
  return c;
688
674
  }
689
675
 
690
- $.resizeCanvas = (width, height) => {
691
- $.width = width;
692
- $.height = height;
676
+ $.resizeCanvas = (w, h) => {
677
+ $.width = w;
678
+ $.height = h;
693
679
  let c = cloneCtx();
694
- $.canvas.width = width * $._pixelDensity;
695
- $.canvas.height = height * $._pixelDensity;
696
- ctx = $._ctx = $.canvas.getContext('2d');
697
- for (let prop in c) $._ctx[prop] = c[prop];
680
+ $.canvas.width = w * $._pixelDensity;
681
+ $.canvas.height = h * $._pixelDensity;
682
+ for (let prop in c) $.ctx[prop] = c[prop];
698
683
  if (scope != 'image') $.pixelDensity($._pixelDensity);
699
684
  };
700
685
 
701
- $.createGraphics = function (width, height) {
686
+ $.createGraphics = function (w, h) {
702
687
  let g = new Q5('graphics');
703
- g._createCanvas.call($, width, height);
688
+ g._createCanvas.call($, w, h);
704
689
  return g;
705
690
  };
706
- $.createImage = (width, height) => {
707
- return new Q5.Image(width, height);
691
+ $.createImage = (w, h) => {
692
+ return new Q5.Image(w, h);
708
693
  };
694
+
709
695
  $.displayDensity = () => window.devicePixelRatio;
710
696
  $.pixelDensity = (n) => {
711
697
  if (n === undefined) return $._pixelDensity;
@@ -716,16 +702,13 @@ function Q5(scope, parent) {
716
702
  $.canvas.height = Math.ceil($.height * n);
717
703
  $.canvas.style.width = $.width + 'px';
718
704
  $.canvas.style.height = $.height + 'px';
719
- ctx = $._ctx = $.canvas.getContext('2d');
720
- for (let prop in c) $._ctx[prop] = c[prop];
705
+ for (let prop in c) $.ctx[prop] = c[prop];
721
706
 
722
707
  ctx.scale($._pixelDensity, $._pixelDensity);
723
708
  return $._pixelDensity;
724
709
  };
725
710
 
726
- //================================================================
727
711
  // MATH
728
- //================================================================
729
712
 
730
713
  $.map = (value, istart, istop, ostart, ostop, clamp) => {
731
714
  let val = ostart + (ostop - ostart) * (((value - istart) * 1.0) / (istop - istart));
@@ -741,11 +724,9 @@ function Q5(scope, parent) {
741
724
  $.lerp = (a, b, t) => a * (1 - t) + b * t;
742
725
  $.constrain = (x, lo, hi) => Math.min(Math.max(x, lo), hi);
743
726
  $.dist = function () {
744
- if (arguments.length == 4) {
745
- return Math.hypot(arguments[0] - arguments[2], arguments[1] - arguments[3]);
746
- } else {
747
- return Math.hypot(arguments[0] - arguments[3], arguments[1] - arguments[4], arguments[2] - arguments[5]);
748
- }
727
+ let a = arguments;
728
+ if (a.length == 4) return Math.hypot(a[0] - a[2], a[1] - a[3]);
729
+ else return Math.hypot(a[0] - a[3], a[1] - a[4], a[2] - a[5]);
749
730
  };
750
731
  $.norm = (value, start, stop) => $.map(value, start, stop, 0, 1);
751
732
  $.sq = (x) => x * x;
@@ -812,10 +793,7 @@ function Q5(scope, parent) {
812
793
  };
813
794
  $.createVector = (x, y, z) => new Q5.Vector(x, y, z, $);
814
795
 
815
- //================================================================
816
- // CURVE QUERY
817
- //================================================================
818
- //https://github.com/processing/p5.js/blob/1.1.9/src/core/shape/curves.js
796
+ // CURVES
819
797
 
820
798
  $.curvePoint = (a, b, c, d, t) => {
821
799
  const t3 = t * t * t,
@@ -855,13 +833,21 @@ function Q5(scope, parent) {
855
833
  );
856
834
  };
857
835
 
858
- //================================================================
859
- // COLORS
860
- //================================================================
836
+ // COLOR
837
+
838
+ $.Color = Q5.ColorRGBA_P3;
861
839
 
862
- $.Color = Q5.Color;
863
840
  $.colorMode = (mode) => {
864
841
  $._colorMode = mode;
842
+ if (mode == 'oklch') {
843
+ $.Color = Q5.ColorOKLCH;
844
+ } else if (mode == 'rgb') {
845
+ if ($.canvas.colorSpace == 'srgb') $.Color = Q5.ColorRGBA;
846
+ else $.Color = Q5.ColorRGBA_P3;
847
+ } else if (mode == 'srgb') {
848
+ $.Color = Q5.ColorRGBA;
849
+ $._colorMode = 'rgb';
850
+ }
865
851
  };
866
852
 
867
853
  let basicColors = {
@@ -897,113 +883,64 @@ function Q5(scope, parent) {
897
883
  yellow: [255, 255, 0]
898
884
  };
899
885
 
900
- $.color = function () {
886
+ $.color = function (c0, c1, c2, c3) {
887
+ let C = $.Color;
888
+ if (c0._q5Color) return new C(...c0.levels);
901
889
  let args = arguments;
902
890
  if (args.length == 1) {
903
- if (typeof args[0] == 'string') {
904
- if (args[0][0] == '#') {
905
- return new Q5.Color(
906
- parseInt(args[0].slice(1, 3), 16),
907
- parseInt(args[0].slice(3, 5), 16),
908
- parseInt(args[0].slice(5, 7), 16),
909
- 1
891
+ if (typeof c0 == 'string') {
892
+ if (c0[0] == '#') {
893
+ return new C(
894
+ parseInt(c0.slice(1, 3), 16),
895
+ parseInt(c0.slice(3, 5), 16),
896
+ parseInt(c0.slice(5, 7), 16),
897
+ c0.length != 9 ? null : parseInt(c0.slice(7, 9), 16)
910
898
  );
911
- } else {
912
- if (basicColors[args[0]]) {
913
- return new Q5.Color(...basicColors[args[0]], 1);
914
- }
915
- return new Q5.Color(0, 0, 0, 1);
916
- }
917
- }
918
- if (typeof args[0] != 'number' && args[0].MAGIC == 0xc010a) {
919
- return args[0];
920
- }
899
+ } else if (basicColors[c0]) return new C(...basicColors[c0]);
900
+ else return new C(0, 0, 0);
901
+ } else if (Array.isArray(c0)) return new C(...c0);
921
902
  }
922
- if ($._colorMode == $.RGB) {
923
- if (args.length == 1) {
924
- return new Q5.Color(args[0], args[0], args[0], 1);
925
- } else if (args.length == 2) {
926
- return new Q5.Color(args[0], args[0], args[0], args[1] / 255);
927
- } else if (args.length == 3) {
928
- return new Q5.Color(args[0], args[1], args[2], 1);
929
- } else if (args.length == 4) {
930
- return new Q5.Color(args[0], args[1], args[2], args[3] / 255);
931
- }
932
- } else {
933
- if (args.length == 1) {
934
- return new Q5.Color(...Q5.Color._hsv2rgb(0, 0, args[0] / 100), 1);
935
- } else if (args.length == 2) {
936
- return new Q5.Color(...Q5.Color._hsv2rgb(0, 0, args[0] / 100), args[1] / 255);
937
- } else if (args.length == 3) {
938
- return new Q5.Color(...Q5.Color._hsv2rgb(args[0], args[1] / 100, args[2] / 100), 1);
939
- } else if (args.length == 4) {
940
- return new Q5.Color(...Q5.Color._hsv2rgb(args[0], args[1] / 100, args[2] / 100), args[3]);
941
- }
903
+ if ($._colorMode == 'rgb') {
904
+ if (args.length == 1) return new C(c0, c0, c0);
905
+ else if (args.length == 2) return new C(c0, c0, c0, c1);
906
+ else if (args.length == 3) return new C(c0, c1, c2);
907
+ else if (args.length == 4) return new C(c0, c1, c2, c3);
942
908
  }
943
- return null;
944
909
  };
945
910
 
946
- $.red = (c) => {
947
- return c._r;
948
- };
949
- $.green = (c) => {
950
- return c._g;
951
- };
952
- $.blue = (c) => {
953
- return c._b;
954
- };
955
- $.alpha = (c) => {
956
- return c._a * 255;
957
- };
958
- $.hue = (c) => {
959
- c._inferHSV();
960
- return c._h;
961
- };
962
- $.saturation = (c) => {
963
- c._inferHSV();
964
- return c._s;
965
- };
966
- $.brightness = (c) => {
967
- c._inferHSV();
968
- return c._v;
969
- };
911
+ $.red = (c) => c.r;
912
+ $.green = (c) => c.g;
913
+ $.blue = (c) => c.b;
914
+ $.alpha = (c) => c.a;
970
915
  $.lightness = (c) => {
971
- return ((0.2126 * c._r + 0.7152 * c._g + 0.0722 * c._b) * 100) / 255;
916
+ return ((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100) / 255;
972
917
  };
973
918
 
974
- function lerpHue(h0, h1, t) {
975
- var methods = [
976
- [Math.abs(h1 - h0), $.map(t, 0, 1, h0, h1)],
977
- [Math.abs(h1 + 360 - h0), $.map(t, 0, 1, h0, h1 + 360)],
978
- [Math.abs(h1 - 360 - h0), $.map(t, 0, 1, h0, h1 - 360)]
979
- ];
980
- methods.sort((x, y) => x[0] - y[0]);
981
- return (methods[0][1] + 720) % 360;
982
- }
983
-
984
919
  $.lerpColor = (a, b, t) => {
985
- if ($._colorMode == $.RGB) {
986
- return new Q5.Color(
987
- $.constrain($.lerp(a._r, b._r, t), 0, 255),
988
- $.constrain($.lerp(a._g, b._g, t), 0, 255),
989
- $.constrain($.lerp(a._b, b._b, t), 0, 255),
990
- $.constrain($.lerp(a._a, b._a, t), 0, 1)
920
+ if ($._colorMode == 'rgb') {
921
+ return new $.Color(
922
+ $.constrain($.lerp(a.r, b.r, t), 0, 255),
923
+ $.constrain($.lerp(a.g, b.g, t), 0, 255),
924
+ $.constrain($.lerp(a.b, b.b, t), 0, 255),
925
+ $.constrain($.lerp(a.a, b.a, t), 0, 255)
991
926
  );
992
927
  } else {
993
- a._inferHSV();
994
- b._inferHSV();
995
- return new Q5.Color(
996
- $.constrain(lerpHue(a._h, b._h, t), 0, 360),
997
- $.constrain($.lerp(a._s, b._s, t), 0, 100),
998
- $.constrain($.lerp(a._v, b._v, t), 0, 100),
999
- $.constrain($.lerp(a._a, b._a, t), 0, 1)
928
+ let deltaH = b.h - a.h;
929
+ if (deltaH > 180) deltaH -= 360;
930
+ if (deltaH < -180) deltaH += 360;
931
+ let h = a.h + t * deltaH;
932
+ if (h < 0) h += 360;
933
+ if (h > 360) h -= 360;
934
+ return new $.Color(
935
+ $.constrain($.lerp(a.l, b.l, t), 0, 100),
936
+ $.constrain($.lerp(a.c, b.c, t), 0, 100),
937
+ h,
938
+ $.constrain($.lerp(a.a, b.a, t), 0, 255)
1000
939
  );
1001
940
  }
1002
941
  };
1003
942
 
1004
- //================================================================
1005
- // DRAWING SETTING
1006
- //================================================================
943
+ // DRAWING SETTINGS
1007
944
 
1008
945
  function defaultStyle() {
1009
946
  ctx.fillStyle = 'white';
@@ -1017,34 +954,20 @@ function Q5(scope, parent) {
1017
954
  if (!n) $._doStroke = false;
1018
955
  ctx.lineWidth = n || 0.0001;
1019
956
  };
1020
- $.stroke = function () {
957
+ $.stroke = function (c) {
1021
958
  $._doStroke = true;
1022
959
  $._strokeSet = true;
1023
- if (typeof arguments[0] == 'string') {
1024
- ctx.strokeStyle = arguments[0];
1025
- return;
1026
- }
1027
- let col = $.color(...arguments);
1028
- if (col._a <= 0) {
1029
- $._doStroke = false;
1030
- return;
1031
- }
1032
- ctx.strokeStyle = col;
960
+ if (!c._q5Color) c = $.color(...arguments);
961
+ if (c.a <= 0) return ($._doStroke = false);
962
+ ctx.strokeStyle = c;
1033
963
  };
1034
964
  $.noStroke = () => ($._doStroke = false);
1035
- $.fill = function () {
965
+ $.fill = function (c) {
1036
966
  $._doFill = true;
1037
967
  $._fillSet = true;
1038
- if (typeof arguments[0] == 'string') {
1039
- ctx.fillStyle = arguments[0];
1040
- return;
1041
- }
1042
- let col = $.color(...arguments);
1043
- if (col._a <= 0) {
1044
- $._doFill = false;
1045
- return;
1046
- }
1047
- ctx.fillStyle = col;
968
+ if (!c._q5Color) c = $.color(...arguments);
969
+ if (c.a <= 0) return ($._doFill = false);
970
+ ctx.fillStyle = c;
1048
971
  };
1049
972
  $.noFill = () => ($._doFill = false);
1050
973
  $.smooth = () => ($._smooth = true);
@@ -1058,25 +981,18 @@ function Q5(scope, parent) {
1058
981
  $.curveAlpha = (x) => ($._curveAlpha = x);
1059
982
  $.curveTightness = (x) => ($._curveAlpha = x);
1060
983
 
1061
- //================================================================
1062
984
  // DRAWING
1063
- //================================================================
1064
985
 
1065
986
  $.clear = () => {
1066
987
  ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
1067
988
  };
1068
989
 
1069
- $.background = function () {
1070
- if (arguments[0] && arguments[0].MAGIC == $.MAGIC) {
1071
- return $.image(arguments[0], 0, 0, $.width, $.height);
1072
- }
990
+ $.background = function (c) {
991
+ if (c._q5) return $.image(c, 0, 0, $.width, $.height);
1073
992
  ctx.save();
1074
993
  ctx.resetTransform();
1075
- if (typeof arguments[0] == 'string') {
1076
- ctx.fillStyle = arguments[0];
1077
- } else {
1078
- ctx.fillStyle = $.color(...Array.from(arguments));
1079
- }
994
+ if (!c._q5color) c = $.color(...arguments);
995
+ ctx.fillStyle = c;
1080
996
  ctx.fillRect(0, 0, $.canvas.width, $.canvas.height);
1081
997
  ctx.restore();
1082
998
  };
@@ -1284,7 +1200,6 @@ function Q5(scope, parent) {
1284
1200
  if ($._doFill) ctx.fill();
1285
1201
  if ($._doStroke) ctx.stroke();
1286
1202
  if (!$._doFill && !$._doStroke) {
1287
- // eh.
1288
1203
  ctx.save();
1289
1204
  ctx.fillStyle = 'none';
1290
1205
  ctx.fill();
@@ -1292,7 +1207,6 @@ function Q5(scope, parent) {
1292
1207
  }
1293
1208
  };
1294
1209
  function catmullRomSpline(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, numPts, alpha) {
1295
- //https://en.wikipedia.org/wiki/Centripetal_Catmull–Rom_spline
1296
1210
  function catmullromSplineGetT(t, p0x, p0y, p1x, p1y, alpha) {
1297
1211
  let a = Math.pow(p1x - p0x, 2.0) + Math.pow(p1y - p0y, 2.0);
1298
1212
  let b = Math.pow(a, alpha * 0.5);
@@ -1378,30 +1292,22 @@ function Q5(scope, parent) {
1378
1292
  $.curveVertex(x4, y4);
1379
1293
  $.endShape();
1380
1294
  };
1295
+ $.opacity = (a) => (ctx.globalAlpha = a);
1381
1296
 
1382
- //================================================================
1383
1297
  // DRAWING MATRIX
1384
- //================================================================
1298
+
1385
1299
  $.translate = (x, y) => ctx.translate(x, y);
1386
1300
  $.rotate = (r) => {
1387
1301
  if ($._angleMode == 'degrees') r = $.radians(r);
1388
1302
  ctx.rotate(r);
1389
1303
  };
1390
-
1391
1304
  $.scale = (x, y) => {
1392
1305
  y ??= x;
1393
1306
  ctx.scale(x, y);
1394
1307
  };
1395
- $.applyMatrix = (a, b, c, d, e, f) => {
1396
- ctx.transform(a, b, c, d, e, f);
1397
- };
1398
- $.shearX = (ang) => {
1399
- ctx.transform(1, 0, $.tan(ang), 1, 0, 0);
1400
- };
1401
- $.shearY = (ang) => {
1402
- ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
1403
- };
1404
-
1308
+ $.applyMatrix = (a, b, c, d, e, f) => ctx.transform(a, b, c, d, e, f);
1309
+ $.shearX = (ang) => ctx.transform(1, 0, $.tan(ang), 1, 0, 0);
1310
+ $.shearY = (ang) => ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
1405
1311
  $.resetMatrix = () => {
1406
1312
  ctx.resetTransform();
1407
1313
  ctx.scale($._pixelDensity, $._pixelDensity);
@@ -1425,51 +1331,41 @@ function Q5(scope, parent) {
1425
1331
  '_textStyle',
1426
1332
  '_textWrap'
1427
1333
  ];
1428
-
1429
- $._ctxStyleNames = ['strokeStyle', 'fillStyle', 'lineWidth', 'lineCap', 'lineJoin'];
1430
-
1431
1334
  $._styles = [];
1432
- $._ctxStyles = [];
1433
1335
 
1434
- $.pushMatrix = $.push = () => {
1336
+ $.push = $.pushMatrix = () => {
1435
1337
  ctx.save();
1436
1338
  let styles = {};
1437
1339
  for (let s of $._styleNames) styles[s] = $[s];
1438
1340
  $._styles.push(styles);
1439
- let ctxStyles = {};
1440
- for (let s of $._ctxStyleNames) ctxStyles[s] = ctx[s];
1441
- $._ctxStyles.push(ctxStyles);
1442
1341
  };
1443
- $.popMatrix = $.pop = () => {
1342
+ $.pop = $.popMatrix = () => {
1444
1343
  ctx.restore();
1445
1344
  let styles = $._styles.pop();
1446
1345
  for (let s of $._styleNames) $[s] = styles[s];
1447
- let ctxStyles = $._ctxStyles.pop();
1448
- for (let s of $._ctxStyleNames) ctx[s] = ctxStyles[s];
1449
1346
  };
1450
1347
 
1451
- //================================================================
1452
1348
  // IMAGING
1453
- //================================================================
1454
- $.imageMode = (mode) => ($._imageMode = mode); // TODO
1349
+
1350
+ $.imageMode = (mode) => ($._imageMode = mode);
1455
1351
  $.image = (img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) => {
1456
- let drawable = img.MAGIC == $.MAGIC ? img.canvas : img;
1352
+ let drawable = img._q5 ? img.canvas : img;
1457
1353
  function reset() {
1458
- if (img.MAGIC != $.MAGIC || !$._tint) return;
1459
- let c = img.canvas.getContext('2d');
1354
+ if (!img._q5 || !$._tint) return;
1355
+ let c = img.ctx;
1460
1356
  c.save();
1461
1357
  c.resetTransform();
1462
1358
  c.clearRect(0, 0, c.canvas.width, c.canvas.height);
1463
1359
  c.drawImage(tmpCt2.canvas, 0, 0);
1464
1360
  c.restore();
1465
1361
  }
1466
- if (img.MAGIC == $.MAGIC && $._tint != null) {
1362
+ if (img._q5 && $._tint != null) {
1467
1363
  makeTmpCt2(img.canvas.width, img.canvas.height);
1468
1364
  tmpCt2.drawImage(img.canvas, 0, 0);
1469
1365
  img.tinted($._tint);
1470
1366
  }
1471
1367
  if (!dWidth) {
1472
- if (img.MAGIC == $.MAGIC || img.width) {
1368
+ if (img._q5 || img.width) {
1473
1369
  dWidth = img.width;
1474
1370
  dHeight = img.height;
1475
1371
  } else {
@@ -1498,7 +1394,7 @@ function Q5(scope, parent) {
1498
1394
  $.loadImage = (url, cb) => {
1499
1395
  preloadCnt++;
1500
1396
  let g = $.createImage(1, 1);
1501
- let c = g.canvas.getContext('2d');
1397
+ let c = g.ctx;
1502
1398
  let img = new window.Image();
1503
1399
  img.src = url;
1504
1400
  img.crossOrigin = 'Anonymous';
@@ -1523,9 +1419,7 @@ function Q5(scope, parent) {
1523
1419
  tmpBuf = null;
1524
1420
  };
1525
1421
 
1526
- //================================================================
1527
1422
  // TYPOGRAPHY
1528
- //================================================================
1529
1423
 
1530
1424
  $.loadFont = (url, cb) => {
1531
1425
  preloadCnt++;
@@ -1660,7 +1554,7 @@ function Q5(scope, parent) {
1660
1554
  return;
1661
1555
  }
1662
1556
  tg = $.createGraphics.call($, 1, 1);
1663
- c = tg._ctx;
1557
+ c = tg.ctx;
1664
1558
  pd = $._pixelDensity;
1665
1559
  }
1666
1560
  c.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
@@ -1708,11 +1602,8 @@ function Q5(scope, parent) {
1708
1602
  $._imageMode = og;
1709
1603
  };
1710
1604
 
1711
- //================================================================
1712
1605
  // RANDOM
1713
- //================================================================
1714
1606
 
1715
- //https://github.com/processing/p5.js/blob/1.1.9/src/math/noise.js
1716
1607
  var PERLIN_YWRAPB = 4;
1717
1608
  var PERLIN_YWRAP = 1 << PERLIN_YWRAPB;
1718
1609
  var PERLIN_ZWRAPB = 8;
@@ -1856,7 +1747,7 @@ function Q5(scope, parent) {
1856
1747
  return rng1.rand() * a;
1857
1748
  }
1858
1749
  } else {
1859
- return a[~~(a.length * rng1.rand())];
1750
+ return a[Math.trunc(a.length * rng1.rand())];
1860
1751
  }
1861
1752
  };
1862
1753
  $.randomGenerator = (method) => {
@@ -1866,7 +1757,6 @@ function Q5(scope, parent) {
1866
1757
  };
1867
1758
 
1868
1759
  var ziggurat = new (function () {
1869
- //http://ziggurat.glitch.me/
1870
1760
  var iz;
1871
1761
  var jz;
1872
1762
  var kn = new Array(128);
@@ -2004,29 +1894,12 @@ function Q5(scope, parent) {
2004
1894
  return ziggurat.REXP();
2005
1895
  };
2006
1896
 
2007
- //================================================================
2008
- // ENVIRONMENT
2009
- //================================================================
1897
+ // DOM
2010
1898
 
2011
- $.print = console.log;
2012
- $.cursor = (name, x, y) => {
2013
- let pfx = '';
2014
- if (name.includes('.')) {
2015
- name = `url("${name}")`;
2016
- pfx = ', auto';
2017
- }
2018
- if (x !== undefined) {
2019
- name += ' ' + x + ' ' + y;
2020
- }
2021
- $.canvas.style.cursor = name + pfx;
2022
- };
2023
- $.noCursor = () => {
2024
- $.canvas.style.cursor = 'none';
1899
+ $.Element = function (a) {
1900
+ this.elt = a;
2025
1901
  };
2026
-
2027
- //================================================================
2028
- // DOM
2029
- //================================================================
1902
+ $._elements = [];
2030
1903
 
2031
1904
  $.createCapture = (x) => {
2032
1905
  var vid = document.createElement('video');
@@ -2042,6 +1915,11 @@ function Q5(scope, parent) {
2042
1915
  return vid;
2043
1916
  };
2044
1917
 
1918
+ // ENVIRONMENT
1919
+
1920
+ $.print = console.log;
1921
+ $.describe = () => {};
1922
+
2045
1923
  function _draw() {
2046
1924
  let pre = performance.now();
2047
1925
  if ($._loop) {
@@ -2077,7 +1955,6 @@ function Q5(scope, parent) {
2077
1955
  $.pmouseX = $.mouseX;
2078
1956
  $.pmouseY = $.mouseY;
2079
1957
  }
2080
-
2081
1958
  $.noLoop = () => {
2082
1959
  $._loop = false;
2083
1960
  looper = null;
@@ -2087,6 +1964,11 @@ function Q5(scope, parent) {
2087
1964
  if (looper == null) _draw();
2088
1965
  };
2089
1966
  $.redraw = () => _draw();
1967
+ $.remove = () => {
1968
+ $.noLoop();
1969
+ $.canvas.remove();
1970
+ };
1971
+
2090
1972
  $.frameRate = (fps) => {
2091
1973
  if (fps) $._targetFrameRate = fps;
2092
1974
  return $._frameRate;
@@ -2094,26 +1976,31 @@ function Q5(scope, parent) {
2094
1976
  $.getFrameRate = () => $._frameRate;
2095
1977
  $.getFPS = () => $._fps;
2096
1978
 
2097
- $._updateMouse = function (e) {
2098
- let $ = this;
1979
+ $.storeItem = localStorage.setItem;
1980
+ $.getItem = localStorage.getItem;
1981
+ $.removeItem = localStorage.removeItem;
1982
+ $.clearStorage = localStorage.clear;
1983
+
1984
+ // USER INPUT
1985
+
1986
+ $._updateMouse = (e) => {
2099
1987
  let rect = $.canvas.getBoundingClientRect();
2100
1988
  let sx = $.canvas.scrollWidth / $.width || 1;
2101
1989
  let sy = $.canvas.scrollHeight / $.height || 1;
2102
1990
  $.mouseX = (e.clientX - rect.left) / sx;
2103
1991
  $.mouseY = (e.clientY - rect.top) / sy;
2104
- }.bind($);
2105
-
2106
- $._onmousemove = function (e) {
2107
- $._updateMouse(e);
2108
- if (this.mouseIsPressed) this._mouseDraggedFn(e);
2109
- else this._mouseMovedFn(e);
2110
- }.bind($);
1992
+ };
2111
1993
  $._onmousedown = (e) => {
2112
1994
  $._updateMouse(e);
2113
1995
  $.mouseIsPressed = true;
2114
1996
  $.mouseButton = [$.LEFT, $.CENTER, $.RIGHT][e.button];
2115
1997
  $._mousePressedFn(e);
2116
1998
  };
1999
+ $._onmousemove = (e) => {
2000
+ $._updateMouse(e);
2001
+ if ($.mouseIsPressed) $._mouseDraggedFn(e);
2002
+ else $._mouseMovedFn(e);
2003
+ };
2117
2004
  $._onmouseup = (e) => {
2118
2005
  $._updateMouse(e);
2119
2006
  $.mouseIsPressed = false;
@@ -2125,6 +2012,21 @@ function Q5(scope, parent) {
2125
2012
  $._mouseClickedFn(e);
2126
2013
  $.mouseIsPressed = false;
2127
2014
  };
2015
+ $.cursor = (name, x, y) => {
2016
+ let pfx = '';
2017
+ if (name.includes('.')) {
2018
+ name = `url("${name}")`;
2019
+ pfx = ', auto';
2020
+ }
2021
+ if (x !== undefined) {
2022
+ name += ' ' + x + ' ' + y;
2023
+ }
2024
+ $.canvas.style.cursor = name + pfx;
2025
+ };
2026
+ $.noCursor = () => {
2027
+ $.canvas.style.cursor = 'none';
2028
+ };
2029
+
2128
2030
  $._onkeydown = (e) => {
2129
2031
  if (e.repeat) return;
2130
2032
  $.keyIsPressed = true;
@@ -2194,6 +2096,8 @@ function Q5(scope, parent) {
2194
2096
  $.canvas.ontouchmove = (e) => $._ontouchmove(e);
2195
2097
  $.canvas.ontouchcancel = $.canvas.ontouchend = (e) => $._ontouchend(e);
2196
2098
 
2099
+ // SENSORS
2100
+
2197
2101
  $.hasSensorPermission =
2198
2102
  (!window.DeviceOrientationEvent && !window.DeviceMotionEvent) ||
2199
2103
  !(DeviceOrientationEvent.requestPermission || DeviceMotionEvent.requestPermission);
@@ -2217,11 +2121,6 @@ function Q5(scope, parent) {
2217
2121
  }
2218
2122
  };
2219
2123
 
2220
- //================================================================
2221
- // SENSORS
2222
- //================================================================
2223
-
2224
- // 3d transformation helpers
2225
2124
  let ROTX = (a) => [1, 0, 0, 0, 0, $.cos(a), -$.sin(a), 0, 0, $.sin(a), $.cos(a), 0, 0, 0, 0, 1];
2226
2125
  let ROTY = (a) => [$.cos(a), 0, $.sin(a), 0, 0, 1, 0, 0, -$.sin(a), 0, $.cos(a), 0, 0, 0, 0, 1];
2227
2126
  let MULT = (A, B) => [
@@ -2248,41 +2147,34 @@ function Q5(scope, parent) {
2248
2147
  (A[8] * v[0] + A[9] * v[1] + A[10] * v[2] + A[11]) / (A[12] * v[0] + A[13] * v[1] + A[14] * v[2] + A[15])
2249
2148
  ];
2250
2149
 
2251
- if (typeof window !== 'undefined') {
2252
- window.ondeviceorientation = (e) => {
2253
- $.pRotationX = $.rotationX;
2254
- $.pRotationY = $.rotationY;
2255
- $.pRotationZ = $.rotationZ;
2256
- $.pRelRotationX = $.relRotationX;
2257
- $.pRelRotationY = $.relRotationY;
2258
- $.pRelRotationZ = $.relRotationZ;
2259
-
2260
- $.rotationX = e.beta * (Math.PI / 180.0);
2261
- $.rotationY = e.gamma * (Math.PI / 180.0);
2262
- $.rotationZ = e.alpha * (Math.PI / 180.0);
2263
- $.relRotationX = [-$.rotationY, -$.rotationX, $.rotationY][~~(window.orientation / 90) + 1];
2264
- $.relRotationY = [-$.rotationX, $.rotationY, $.rotationX][~~(window.orientation / 90) + 1];
2265
- $.relRotationZ = $.rotationZ;
2266
- };
2267
- window.ondevicemotion = (e) => {
2268
- $.pAccelerationX = $.accelerationX;
2269
- $.pAccelerationY = $.accelerationY;
2270
- $.pAccelerationZ = $.accelerationZ;
2271
- if (!e.acceleration) {
2272
- // devices that don't support plain acceleration
2273
- // compute gravitational acceleration's component on X Y Z axes based on gyroscope
2274
- // g = ~ 9.80665
2275
- let grav = TRFM(MULT(ROTY($.rotationY), ROTX($.rotationX)), [0, 0, -9.80665]);
2276
- $.accelerationX = e.accelerationIncludingGravity.x + grav[0];
2277
- $.accelerationY = e.accelerationIncludingGravity.y + grav[1];
2278
- $.accelerationZ = e.accelerationIncludingGravity.z - grav[2];
2279
- }
2280
- };
2281
- }
2150
+ window.ondeviceorientation = (e) => {
2151
+ $.pRotationX = $.rotationX;
2152
+ $.pRotationY = $.rotationY;
2153
+ $.pRotationZ = $.rotationZ;
2154
+ $.pRelRotationX = $.relRotationX;
2155
+ $.pRelRotationY = $.relRotationY;
2156
+ $.pRelRotationZ = $.relRotationZ;
2157
+
2158
+ $.rotationX = e.beta * (Math.PI / 180.0);
2159
+ $.rotationY = e.gamma * (Math.PI / 180.0);
2160
+ $.rotationZ = e.alpha * (Math.PI / 180.0);
2161
+ $.relRotationX = [-$.rotationY, -$.rotationX, $.rotationY][Math.trunc(window.orientation / 90) + 1];
2162
+ $.relRotationY = [-$.rotationX, $.rotationY, $.rotationX][Math.trunc(window.orientation / 90) + 1];
2163
+ $.relRotationZ = $.rotationZ;
2164
+ };
2165
+ window.ondevicemotion = (e) => {
2166
+ $.pAccelerationX = $.accelerationX;
2167
+ $.pAccelerationY = $.accelerationY;
2168
+ $.pAccelerationZ = $.accelerationZ;
2169
+ if (!e.acceleration) {
2170
+ let grav = TRFM(MULT(ROTY($.rotationY), ROTX($.rotationX)), [0, 0, -9.80665]);
2171
+ $.accelerationX = e.accelerationIncludingGravity.x + grav[0];
2172
+ $.accelerationY = e.accelerationIncludingGravity.y + grav[1];
2173
+ $.accelerationZ = e.accelerationIncludingGravity.z - grav[2];
2174
+ }
2175
+ };
2282
2176
 
2283
- //================================================================
2284
2177
  // TIME
2285
- //================================================================
2286
2178
 
2287
2179
  $.year = () => new Date().getFullYear();
2288
2180
  $.day = () => new Date().getDay();
@@ -2291,10 +2183,7 @@ function Q5(scope, parent) {
2291
2183
  $.second = () => new Date().getSeconds();
2292
2184
  $.millis = () => performance.now() - millisStart;
2293
2185
 
2294
- $.storeItem = localStorage.setItem;
2295
- $.getItem = localStorage.getItem;
2296
- $.removeItem = localStorage.removeItem;
2297
- $.clearStorage = localStorage.clear;
2186
+ // LOAD FILES
2298
2187
 
2299
2188
  $._loadFile = (path, cb, type) => {
2300
2189
  preloadCnt++;
@@ -2314,7 +2203,6 @@ function Q5(scope, parent) {
2314
2203
 
2315
2204
  $.loadStrings = (path, cb) => $._loadFile(path, cb, 'text');
2316
2205
  $.loadJSON = (path, cb) => $._loadFile(path, cb, 'json');
2317
-
2318
2206
  $.loadSound = (path, cb) => {
2319
2207
  preloadCnt++;
2320
2208
  let a = new Audio(path);
@@ -2328,17 +2216,10 @@ function Q5(scope, parent) {
2328
2216
  return a;
2329
2217
  };
2330
2218
 
2331
- $.Element = function (a) {
2332
- this.elt = a;
2333
- };
2334
- $._elements = [];
2219
+ // INIT
2335
2220
 
2336
2221
  if (scope == 'global') {
2337
- // delete $.name;
2338
- // delete $.length;
2339
2222
  Object.assign(Q5, $);
2340
- // $.name = '';
2341
- // $.length = 0;
2342
2223
  delete Q5.Q5;
2343
2224
  }
2344
2225
  Q5.Image ??= _Q5Image;
@@ -2403,8 +2284,8 @@ function Q5(scope, parent) {
2403
2284
  millisStart = performance.now();
2404
2285
  function _start() {
2405
2286
  if (preloadCnt > 0) return requestAnimationFrame(_start);
2406
- ctx.save();
2407
2287
  $._setupFn();
2288
+ if (!ctx) $.createCanvas(100, 100);
2408
2289
  $._setupDone = true;
2409
2290
  ctx.restore();
2410
2291
  $.resetMatrix();
@@ -2420,127 +2301,100 @@ function Q5(scope, parent) {
2420
2301
  else requestAnimationFrame(_init);
2421
2302
  }
2422
2303
 
2304
+ // COLOR CLASSES
2305
+
2423
2306
  Q5.Color = class {
2307
+ constructor() {
2308
+ this._q5Color = true;
2309
+ }
2310
+ };
2311
+ Q5.ColorOKLCH = class extends Q5.Color {
2312
+ constructor(l, c, h, a) {
2313
+ super();
2314
+ this.l = l;
2315
+ this.c = c;
2316
+ this.h = h;
2317
+ this.a = a ?? 1;
2318
+ }
2319
+ toString() {
2320
+ return `color(oklch ${this.l} ${this.c} ${this.h} / ${this.a})`;
2321
+ }
2322
+ };
2323
+ Q5.ColorRGBA = class extends Q5.Color {
2424
2324
  constructor(r, g, b, a) {
2425
- this.MAGIC = 0xc010a;
2426
- this._r = r;
2427
- this._g = g;
2428
- this._b = b;
2429
- this._a = a;
2430
- this._h = 0;
2431
- this._s = 0;
2432
- this._v = 0;
2433
- this._hsvInferred = false;
2325
+ super();
2326
+ this.r = r;
2327
+ this.g = g;
2328
+ this.b = b;
2329
+ this.a = a ?? 255;
2434
2330
  }
2435
-
2436
- setRed(x) {
2437
- this._r = x;
2438
- this._hsvInferred = false;
2331
+ setRed(v) {
2332
+ this.r = v;
2439
2333
  }
2440
- setGreen(x) {
2441
- this._g = x;
2442
- this._hsvInferred = false;
2334
+ setGreen(v) {
2335
+ this.g = v;
2443
2336
  }
2444
- setBlue(x) {
2445
- this._b = x;
2446
- this._hsvInferred = false;
2337
+ setBlue(v) {
2338
+ this.b = v;
2447
2339
  }
2448
- setAlpha(x) {
2449
- this._a = x / 255;
2450
- this._hsvInferred = false;
2340
+ setAlpha(v) {
2341
+ this.a = v;
2451
2342
  }
2452
2343
  get levels() {
2453
- return [this._r, this._g, this._b, this._a * 255];
2454
- }
2455
- _inferHSV() {
2456
- if (!this._hsvInferred) {
2457
- [this._h, this._s, this._v] = Q5.Color._rgb2hsv(this._r, this._g, this._b);
2458
- this._hsvInferred = true;
2459
- }
2344
+ return [this.r, this.g, this.b, this.a];
2460
2345
  }
2461
2346
  toString() {
2462
- return `rgba(${Math.round(this._r)},${Math.round(this._g)},${Math.round(this._b)},${~~(this._a * 1000) / 1000})`;
2347
+ return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
2463
2348
  }
2464
2349
  };
2465
- Q5._instanceCount = 0;
2466
- Q5.Color._rgb2hsv = (r, g, b) => {
2467
- //https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
2468
- let rgbMin, rgbMax;
2469
- let h, s, v;
2470
- rgbMin = r < g ? (r < b ? r : b) : g < b ? g : b;
2471
- rgbMax = r > g ? (r > b ? r : b) : g > b ? g : b;
2472
- v = (rgbMax * 100) / 255;
2473
- if (v == 0) {
2474
- h = 0;
2475
- s = 0;
2476
- return [h, s, v];
2477
- }
2478
- s = (100 * (rgbMax - rgbMin)) / rgbMax;
2479
- if (s == 0) {
2480
- h = 0;
2481
- return [h, s, v];
2482
- }
2483
- if (rgbMax == r) h = 0 + (60 * (g - b)) / (rgbMax - rgbMin);
2484
- else if (rgbMax == g) h = 120 + (60 * (b - r)) / (rgbMax - rgbMin);
2485
- else h = 240 + (60 * (r - g)) / (rgbMax - rgbMin);
2486
- return [h, s, v];
2487
- };
2488
- Q5.Color._hsv2rgb = (h, s, v) => {
2489
- //https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
2490
- let r, g, b;
2491
- let hh, i, ff, p, q, t;
2492
- if (s == 0) {
2493
- r = v;
2494
- g = v;
2495
- b = v;
2496
- return [r * 255, g * 255, b * 255];
2497
- }
2498
- hh = h;
2499
- if (hh > 360) hh = 0;
2500
- hh /= 60;
2501
- i = ~~hh;
2502
- ff = hh - i;
2503
- p = v * (1.0 - s);
2504
- q = v * (1.0 - s * ff);
2505
- t = v * (1.0 - s * (1.0 - ff));
2506
- switch (i) {
2507
- case 0:
2508
- r = v;
2509
- g = t;
2510
- b = p;
2511
- break;
2512
- case 1:
2513
- r = q;
2514
- g = v;
2515
- b = p;
2516
- break;
2517
- case 2:
2518
- r = p;
2519
- g = v;
2520
- b = t;
2521
- break;
2522
- case 3:
2523
- r = p;
2524
- g = q;
2525
- b = v;
2526
- break;
2527
- case 4:
2528
- r = t;
2529
- g = p;
2530
- b = v;
2531
- break;
2532
- default:
2533
- r = v;
2534
- g = p;
2535
- b = q;
2536
- break;
2537
- }
2538
- return [r * 255, g * 255, b * 255];
2350
+ Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
2351
+ constructor(r, g, b, a) {
2352
+ super(r, g, b, a);
2353
+ this._edited = true;
2354
+ }
2355
+ get r() {
2356
+ return this._r;
2357
+ }
2358
+ set r(v) {
2359
+ this._r = v;
2360
+ this._edited = true;
2361
+ }
2362
+ get g() {
2363
+ return this._g;
2364
+ }
2365
+ set g(v) {
2366
+ this._g = v;
2367
+ this._edited = true;
2368
+ }
2369
+ get b() {
2370
+ return this._b;
2371
+ }
2372
+ set b(v) {
2373
+ this._b = v;
2374
+ this._edited = true;
2375
+ }
2376
+ get a() {
2377
+ return this._a;
2378
+ }
2379
+ set a(v) {
2380
+ this._a = v;
2381
+ this._edited = true;
2382
+ }
2383
+ toString() {
2384
+ if (this._edited) {
2385
+ let r = (this._r / 255).toFixed(3);
2386
+ let g = (this._g / 255).toFixed(3);
2387
+ let b = (this._b / 255).toFixed(3);
2388
+ let a = (this._a / 255).toFixed(3);
2389
+ this._css = `color(display-p3 ${r} ${g} ${b} / ${a})`;
2390
+ this._edited = false;
2391
+ }
2392
+ return this._css;
2393
+ }
2539
2394
  };
2540
2395
 
2541
- //================================================================
2542
2396
  // VECTOR
2543
- //================================================================
2397
+
2544
2398
  Q5.Vector = class {
2545
2399
  constructor(_x, _y, _z, _$) {
2546
2400
  this.x = _x || 0;
@@ -2550,7 +2404,6 @@ Q5.Vector = class {
2550
2404
  this._cn = null;
2551
2405
  this._cnsq = null;
2552
2406
  }
2553
-
2554
2407
  set(_x, _y, _z) {
2555
2408
  this.x = _x || 0;
2556
2409
  this.y = _y || 0;
@@ -2764,10 +2617,12 @@ for (let k of ['fromAngle', 'fromAngles', 'random2D', 'random3D']) {
2764
2617
  Q5.Vector[k] = (u, v, t) => new Q5.Vector()[k](u, v, t);
2765
2618
  }
2766
2619
 
2620
+ // IMAGE CLASS
2621
+
2767
2622
  class _Q5Image extends Q5 {
2768
- constructor(width, height) {
2623
+ constructor(w, h) {
2769
2624
  super('image');
2770
- this.createCanvas(width, height);
2625
+ this.createCanvas(w, h);
2771
2626
  delete this.createCanvas;
2772
2627
  this._loop = false;
2773
2628
  }
@@ -2779,9 +2634,32 @@ class _Q5Image extends Q5 {
2779
2634
  }
2780
2635
  }
2781
2636
 
2637
+ // Q5
2638
+
2639
+ if (typeof window == 'undefined') {
2640
+ throw 'q5 requires you to define a window object.';
2641
+ }
2642
+
2643
+ Q5.canvasOptions = {
2644
+ alpha: false,
2645
+ desynchronized: true,
2646
+ colorSpace: 'display-p3'
2647
+ };
2648
+
2649
+ if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
2650
+ Q5.canvasOptions.colorSpace = 'srgb';
2651
+ } else Q5.supportsHDR = true;
2652
+
2653
+ window.OffscreenCanvas ??= function () {
2654
+ return document.createElement('canvas');
2655
+ };
2656
+
2657
+ Q5._instanceCount = 0;
2782
2658
  Q5._friendlyError = (msg, func) => {
2783
2659
  throw func + ': ' + msg;
2784
2660
  };
2661
+ Q5._validateParameters = () => true;
2662
+
2785
2663
  Q5.prototype._methods = {
2786
2664
  init: [],
2787
2665
  pre: [],
@@ -2790,7 +2668,6 @@ Q5.prototype._methods = {
2790
2668
  };
2791
2669
  Q5.prototype.registerMethod = (m, fn) => Q5.prototype._methods[m].push(fn);
2792
2670
  Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.prototype[n] = fn[n]);
2793
- Q5._validateParameters = () => true;
2794
2671
 
2795
2672
  if (typeof module != 'undefined') module.exports = Q5;
2796
2673
  else window.p5 ??= Q5;