q5 2.9.23 → 2.9.26

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.9.23",
3
+ "version": "2.9.26",
4
4
  "description": "A sequel to p5.js that's optimized for interactive art",
5
5
  "author": "quinton-ashley",
6
6
  "contributors": [
package/q5.d.ts CHANGED
@@ -13,7 +13,9 @@ declare global {
13
13
  *
14
14
  * Running `new Q5()` enables the use of q5 functions and variables
15
15
  * anywhere in your code. You can also start Q5 in global mode by
16
- * running [`createCanvas`](https://q5js.org/learn/#canvas-createCanvas).
16
+ * running [`createCanvas`](https://q5js.org/learn/#canvas-createCanvas). q5 uses the q2d renderer, based on the CanvasRenderingContext2D, by default.
17
+ *
18
+ * To use the q5 WebGPU renderer, run `Q5.webgpu()` after the creation of any file level variables. For more information read the [q5-webgpu modules documentation](https://github.com/q5js/q5.js/blob/main/src/readme.md#webgpu-canvas).
17
19
  * @param {string | Function} [scope] -
18
20
  * - "global": (default) top-level global mode, adds q5 functions
19
21
  * and variables to the global scope
@@ -21,9 +23,9 @@ declare global {
21
23
  * - "instance": enables users to [assign a Q5 instance to a variable](https://github.com/q5js/q5.js/wiki/Instance-Mode), not to the global scope
22
24
  * @param {HTMLElement} [parent] - element that the canvas will be placed inside
23
25
  * @example
24
- * new Q5();
25
- * createCanvas(200, 100);
26
- * circle(100, 50, 80);
26
+ new Q5();
27
+ createCanvas(200, 100);
28
+ circle(100, 50, 80);
27
29
  */
28
30
  constructor(scope?: string | Function, parent?: HTMLElement);
29
31
 
@@ -950,6 +952,21 @@ function draw() {
950
952
  */
951
953
  function imageMode(mode: string): void;
952
954
 
955
+ /** 🌆
956
+ * Sets the default image scale, which is applied to images when
957
+ * they are drawn without a specified width or height.
958
+ *
959
+ * By default it is 0.5 so images appear at their actual size
960
+ * when pixel density is 2. Images will be drawn at a consistent
961
+ * default size relative to the canvas regardless of pixel density.
962
+ *
963
+ * This function must be called before images are loaded to
964
+ * have an effect.
965
+ * @param {number}
966
+ * @returns {number} default image scale
967
+ */
968
+ function defaultImageScale(scale: number): number;
969
+
953
970
  /** 🌆
954
971
  * Resizes the image.
955
972
  * @param {number} w - new width
@@ -973,19 +990,52 @@ function setup() {
973
990
  function trim(): Image;
974
991
 
975
992
  /** 🌆
976
- * Enables smooth image rendering.
993
+ * Enables smooth rendering of images displayed larger than
994
+ * their actual size. This is the default setting, so running this
995
+ * function only has an effect if `noSmooth` has been called.
996
+ * @example
997
+ createCanvas(200, 200);
998
+
999
+ let icon = loadImage('/q5js_icon.png');
1000
+
1001
+ function setup() {
1002
+ image(icon, 0, 0, 200, 200);
1003
+ }
977
1004
  */
978
1005
  function smooth(): void;
979
1006
 
980
1007
  /** 🌆
981
1008
  * Disables smooth image rendering for a pixelated look.
1009
+ * @example
1010
+ createCanvas(200, 200);
1011
+
1012
+ let icon = loadImage('/q5js_icon.png');
1013
+
1014
+ function setup() {
1015
+ noSmooth();
1016
+ image(icon, 0, 0, 200, 200);
1017
+ }
982
1018
  */
983
1019
  function noSmooth(): void;
984
1020
 
985
1021
  /** 🌆
986
1022
  * Applies a tint (color overlay) to the drawing.
1023
+ *
1024
+ * The alpha value of the tint color determines the
1025
+ * strength of the tint. To change an image's opacity,
1026
+ * use the `opacity` function.
1027
+ *
987
1028
  * Tinting affects all subsequent images drawn.
988
1029
  * @param {string | number} color - tint color
1030
+ * @example
1031
+ createCanvas(200, 200);
1032
+
1033
+ let logo = loadImage('/q5js_logo.webp');
1034
+
1035
+ function setup() {
1036
+ tint(255, 0, 0, 128);
1037
+ image(logo, 0, 0, 200, 200);
1038
+ }
989
1039
  */
990
1040
  function tint(color: string | number): void;
991
1041
 
@@ -1008,70 +1058,108 @@ function setup() {
1008
1058
  */
1009
1059
  function save(filename: string, extension: string, quality?: number): void;
1010
1060
 
1011
- /** 🌆
1012
- * Displays a region of the image on another region of the image.
1013
- * Can be used to create a detail inset, aka a magnifying glass effect.
1014
- * @param {number} sx - x-coordinate of the source region
1015
- * @param {number} sy - y-coordinate of the source region
1016
- * @param {number} sw - width of the source region
1017
- * @param {number} sh - height of the source region
1018
- * @param {number} dx - x-coordinate of the destination region
1019
- * @param {number} dy - y-coordinate of the destination region
1020
- * @param {number} dw - width of the destination region
1021
- * @param {number} dh - height of the destination region
1022
- * @example
1023
- let logo;
1024
- function preload() {
1025
- logo = loadImage('/q5js_logo.webp');
1026
- }
1027
- function setup() {
1028
- logo.inset(256, 256, 512, 512, 0, 0, 200, 200);
1029
- }
1030
- function draw() {
1031
-
1032
- }
1033
- */
1034
- function inset(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
1035
-
1036
- /** 🌆
1037
- * Returns a copy of the image.
1038
- * @returns {Image}
1039
- */
1040
- function copy(): Image;
1041
-
1042
1061
  /** 🌆
1043
1062
  * Retrieves a subsection of an image or canvas, as a q5 Image.
1044
1063
  * Or if width and height are both 1, returns the color of the pixel at the given coordinates in `[R, G, B, A]` array format.
1045
- * To edit the color value of multiple pixels, it's faster to use `loadPixels` and `updatePixels`.
1046
1064
  * @param {number} x
1047
1065
  * @param {number} y
1048
1066
  * @param {number} [w] - width of the area
1049
1067
  * @param {number} [h] - height of the area
1050
1068
  * @returns {Image | number[]}
1069
+ * @example
1070
+ createCanvas(200, 200);
1071
+
1072
+ let logo = loadImage('/q5js_logo.webp');
1073
+
1074
+ function setup() {
1075
+ let cropped = logo.get(256, 256, 512, 512);
1076
+ image(cropped, 0, 0, 200, 200);
1077
+ }
1051
1078
  */
1052
1079
  function get(x: number, y: number, w?: number, h?: number): Image | number[];
1053
1080
 
1054
1081
  /** 🌆
1055
1082
  * Sets a pixel's color in the image or canvas.
1056
- * Or if a canvas or image is provided, it's drawn on top of the destination image or canvas ignoring its tint setting.
1083
+ *
1084
+ * Or if a canvas or image is provided, it's drawn on top of the
1085
+ * destination image or canvas, ignoring its tint setting.
1086
+ *
1087
+ * Run `updatePixels` to apply the changes.
1057
1088
  * @param {number} x
1058
1089
  * @param {number} y
1059
1090
  * @param {any} c - color, canvas, or image
1091
+ * @example
1092
+ createCanvas(200, 200);
1093
+ let c = color('lime');
1094
+
1095
+ function draw() {
1096
+ set(random(width), random(height), c);
1097
+ updatePixels();
1098
+ }
1060
1099
  */
1061
1100
  function set(x: number, y: number, c: any): void;
1062
1101
 
1102
+ /** 🌆
1103
+ * Returns a copy of the image.
1104
+ * @returns {Image}
1105
+ */
1106
+ function copy(): Image;
1107
+
1108
+ /** 🌆
1109
+ * Displays a region of the image on another region of the image.
1110
+ * Can be used to create a detail inset, aka a magnifying glass effect.
1111
+ * @param {number} sx - x-coordinate of the source region
1112
+ * @param {number} sy - y-coordinate of the source region
1113
+ * @param {number} sw - width of the source region
1114
+ * @param {number} sh - height of the source region
1115
+ * @param {number} dx - x-coordinate of the destination region
1116
+ * @param {number} dy - y-coordinate of the destination region
1117
+ * @param {number} dw - width of the destination region
1118
+ * @param {number} dh - height of the destination region
1119
+ * @example
1120
+ createCanvas(200, 200);
1121
+
1122
+ let logo = loadImage('/q5js_logo.webp');
1123
+
1124
+ function setup() {
1125
+ logo.inset(256, 256, 512, 512, 0, 0, 256, 256);
1126
+ image(logo, 0, 0, 200, 200);
1127
+ }
1128
+ */
1129
+ function inset(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
1130
+
1063
1131
  /** 🌆
1064
1132
  * Array of pixels in the canvas or image. Use `loadPixels` to load the pixel data.
1065
1133
  */
1066
1134
  var pixels: number[];
1067
1135
 
1068
1136
  /** 🌆
1069
- * Loads pixel data into the image's `pixels` array.
1137
+ * Loads pixel data into the canvas' or image's `pixels` array.
1138
+ * @example
1139
+ createCanvas(200, 200);
1140
+ let icon = loadImage('/q5js_icon.png');
1141
+
1142
+ function setup() {
1143
+ icon.loadPixels();
1144
+ for (let i=0; i < 65536; i+=16) icon.pixels[i] = 255;
1145
+ icon.updatePixels();
1146
+ image(icon, 0, 0, 200, 200);
1147
+ }
1070
1148
  */
1071
1149
  function loadPixels(): void;
1072
1150
 
1073
1151
  /** 🌆
1074
- * Updates the image's `pixels` array to the canvas.
1152
+ * Applies changes in the `pixels` array to the canvas or image.
1153
+ * @example
1154
+ createCanvas(200, 200);
1155
+ function setup() {
1156
+ for (let x = 0; x < 200; x += 5) {
1157
+ for (let y = 0; y < 200; y += 5) {
1158
+ set(x, y, color('red'));
1159
+ }
1160
+ }
1161
+ updatePixels();
1162
+ }
1075
1163
  */
1076
1164
  function updatePixels(): void;
1077
1165
 
@@ -1333,11 +1421,22 @@ function draw() {
1333
1421
 
1334
1422
  /** 🖲️
1335
1423
  * Current X position of the mouse.
1424
+ * @example
1425
+ function draw() {
1426
+ background(200);
1427
+ textSize(64);
1428
+ text(round(mouseX), 50, 120);
1429
+ }
1336
1430
  */
1337
1431
  let mouseX: number;
1338
1432
 
1339
1433
  /** 🖲️
1340
1434
  * Current Y position of the mouse.
1435
+ * @example
1436
+ function draw() {
1437
+ background(200);
1438
+ circle(100, mouseY, 100);
1439
+ }
1341
1440
  */
1342
1441
  let mouseY: number;
1343
1442
 
@@ -1352,40 +1451,79 @@ function draw() {
1352
1451
  let pmouseY: number;
1353
1452
 
1354
1453
  /** 🖲️
1355
- * Array of current touches, each touch being an object with x, y, id, etc.
1454
+ * The current button being pressed: 'left', 'right', 'center').
1455
+ *
1456
+ * The default value is an empty string.
1457
+ * @example
1458
+ function draw() {
1459
+ background(200);
1460
+ textSize(64);
1461
+ text(mouseButton, 20, 120);
1462
+ }
1356
1463
  */
1357
- let touches: any[];
1464
+ let mouseButton: string;
1465
+
1466
+ /** 🖲️
1467
+ * True if the mouse is currently pressed, false otherwise.
1468
+ * @example
1469
+ function draw() {
1470
+ if (mouseIsPressed) background(0);
1471
+ else background(200);
1472
+ }
1473
+ */
1474
+ let mouseIsPressed: boolean;
1358
1475
 
1359
1476
  /** 🖲️
1360
- * The current button being pressed ('left', 'right', 'center'), or null if no button is pressed.
1477
+ * The name of the last key pressed.
1478
+ * @example
1479
+ function draw() {
1480
+ background(200);
1481
+ textSize(64);
1482
+ text(key, 20, 120);
1483
+ }
1361
1484
  */
1362
- let mouseButton: string;
1485
+ let key: string;
1363
1486
 
1364
1487
  /** 🖲️
1365
1488
  * True if a key is currently pressed, false otherwise.
1489
+ * @example
1490
+ function draw() {
1491
+ if (keyIsPressed) background(0);
1492
+ else background(200);
1493
+ }
1366
1494
  */
1367
1495
  let keyIsPressed: boolean;
1368
1496
 
1369
1497
  /** 🖲️
1370
- * True if the mouse is currently pressed, false otherwise.
1498
+ * Returns true if the user is pressing the specified key, false otherwise. Accepts case-insensitive key names.
1499
+ * @param {string} key - key to check
1500
+ * @returns {boolean} true if the key is pressed, false otherwise
1501
+ * @example
1502
+ function draw() {
1503
+ background(200);
1504
+
1505
+ if (keyIsDown('f') && keyIsDown('j')) {
1506
+ rect(50, 50, 100, 100);
1507
+ }
1508
+ }
1371
1509
  */
1372
- let mouseIsPressed: boolean;
1510
+ function keyIsDown(key: string): boolean;
1373
1511
 
1374
1512
  /** 🖲️
1375
- * The value of the last key pressed.
1513
+ * The keyCode of the last key pressed.
1514
+ * @deprecated
1376
1515
  */
1377
- let key: string;
1516
+ let keyCode: number;
1378
1517
 
1379
1518
  /** 🖲️
1380
- * The keyCode of the last key pressed.
1519
+ * Array of current touches, each touch being an object with x, y, id, etc.
1381
1520
  */
1382
- let keyCode: number;
1521
+ let touches: any[];
1383
1522
 
1384
1523
  /** 🖲️
1385
- * Sets the cursor to a specified type or image path.
1524
+ * Sets the cursor to a [CSS cursor type](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor) or image.
1386
1525
  * If an image is provided, optional x and y coordinates can
1387
1526
  * specify the active point of the cursor.
1388
- * https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
1389
1527
  * @param {string} name - name of the cursor or the path to an image
1390
1528
  * @param {number} [x] - x-coordinate of the cursor's hot spot
1391
1529
  * @param {number} [y] - y-coordinate of the cursor's hot spot
@@ -1393,7 +1531,10 @@ function draw() {
1393
1531
  function cursor(name: string, x?: number, y?: number): void;
1394
1532
 
1395
1533
  /** 🖲️
1396
- * Hides the cursor.
1534
+ * Hides the cursor within the bounds of the canvas.
1535
+ * @example
1536
+ createCanvas(200, 200);
1537
+ noCursor();
1397
1538
  */
1398
1539
  function noCursor(): void;
1399
1540
 
@@ -1407,13 +1548,6 @@ function draw() {
1407
1548
  */
1408
1549
  function exitPointerLock(): void;
1409
1550
 
1410
- /** 🖲️
1411
- * Returns true if the user is pressing the specified key, false otherwise. Accepts case-insensitive key names.
1412
- * @param {string} key - key to check
1413
- * @returns {boolean} true if the key is pressed, false otherwise
1414
- */
1415
- function keyIsDown(key: string): boolean;
1416
-
1417
1551
  // 🧮 math
1418
1552
 
1419
1553
  /** 🧮
package/q5.js CHANGED
@@ -13,7 +13,7 @@ function Q5(scope, parent, renderer) {
13
13
  $._webgpuFallback = true;
14
14
  $._renderer = 'q2d';
15
15
  } else {
16
- $._renderer = renderer || 'q2d';
16
+ $._renderer = renderer || Q5.render;
17
17
  }
18
18
  $._preloadCount = 0;
19
19
 
@@ -277,6 +277,8 @@ function Q5(scope, parent, renderer) {
277
277
  else setTimeout(_start, 32);
278
278
  }
279
279
 
280
+ Q5.render = 'q2d';
281
+
280
282
  Q5.renderers = {};
281
283
  Q5.modules = {};
282
284
 
@@ -1329,10 +1331,16 @@ Q5.renderers.q2d.image = ($, q) => {
1329
1331
  $.ctx.drawImage(drawable, sx * pd, sy * pd, sw, sh, dx, dy, dw, dh);
1330
1332
 
1331
1333
  if ($._tint) {
1334
+ $.ctx.shadowBlur = 0;
1335
+ $.ctx.shadowOffsetX = 0;
1336
+ $.ctx.shadowOffsetY = 0;
1337
+
1338
+ let fill = $.ctx.fillStyle;
1332
1339
  $.ctx.globalCompositeOperation = 'multiply';
1333
1340
  $.ctx.fillStyle = $._tint.toString();
1334
1341
  $.ctx.fillRect(dx, dy, dw, dh);
1335
1342
  $.ctx.globalCompositeOperation = 'source-over';
1343
+ $.ctx.fillStyle = fill;
1336
1344
  }
1337
1345
  };
1338
1346
 
@@ -1437,8 +1445,8 @@ Q5.renderers.q2d.image = ($, q) => {
1437
1445
  let c = $._getImageData(x * pd, y * pd, 1, 1).data;
1438
1446
  return [c[0], c[1], c[2], c[3] / 255];
1439
1447
  }
1440
- x = (x || 0) * pd;
1441
- y = (y || 0) * pd;
1448
+ x = Math.floor(x || 0) * pd;
1449
+ y = Math.floor(y || 0) * pd;
1442
1450
  let _w = (w = w || $.width);
1443
1451
  let _h = (h = h || $.height);
1444
1452
  w *= pd;
@@ -1452,6 +1460,8 @@ Q5.renderers.q2d.image = ($, q) => {
1452
1460
  };
1453
1461
 
1454
1462
  $.set = (x, y, c) => {
1463
+ x = Math.floor(x);
1464
+ y = Math.floor(y);
1455
1465
  if (c.canvas) {
1456
1466
  let old = $._tint;
1457
1467
  $._tint = null;
package/q5.min.js CHANGED
@@ -5,4 +5,4 @@
5
5
  * @license LGPL-3.0
6
6
  * @class Q5
7
7
  */
8
- function Q5(e,t,r){let a=this;a._q5=!0,a._parent=t,"webgpu-fallback"==r?(a._webgpuFallback=!0,a._renderer="q2d"):a._renderer=r||"q2d",a._preloadCount=0;let n,o="auto"==e;if(e??="global","auto"==e){if(!window.setup&&!window.draw)return;e="global"}a._scope=e,"global"==e&&(Q5._hasGlobal=a._isGlobal=!0,n=Q5._nodejs?global:window);let i=new Proxy(a,{set:(e,t,r)=>(a[t]=r,a._isGlobal&&(n[t]=r),!0)});a.canvas=a.ctx=a.drawingContext=null,a.pixels=[];let s=null;a.frameCount=0,a.deltaTime=16,a._targetFrameRate=0,a._targetFrameDuration=16.666666666666668,a._frameRate=a._fps=60,a._loop=!0,a._hooks={postCanvas:[],preRender:[],postRender:[]};let l=0;a.millis=()=>performance.now()-l,a.noCanvas=()=>{a.canvas?.remove&&a.canvas.remove(),a.canvas=0,i.ctx=i.drawingContext=0},window&&(a.windowWidth=window.innerWidth,a.windowHeight=window.innerHeight,a.deviceOrientation=window.screen?.orientation?.type),a._incrementPreload=()=>i._preloadCount++,a._decrementPreload=()=>i._preloadCount--,a._draw=e=>{let t=e||performance.now();if(a._lastFrameTime??=t-a._targetFrameDuration,a._didResize&&(a.windowResized(),a._didResize=!1),a._loop)s=c(a._draw);else if(a.frameCount&&!a._redraw)return;if(s&&a.frameCount){if(t-a._lastFrameTime<a._targetFrameDuration-4)return}i.deltaTime=t-a._lastFrameTime,a._frameRate=1e3/a.deltaTime,i.frameCount++;let r=performance.now();a.resetMatrix(),a._beginRender&&a._beginRender();for(let e of Q5.methods.pre)e.call(a);try{a.draw()}catch(e){throw!Q5.disableFriendlyErrors&&a._askAI&&a._askAI(e),Q5.errorTolerant||a.noLoop(),e}for(let e of Q5.methods.post)e.call(a);a._render&&a._render(),a._finishRender&&a._finishRender(),i.pmouseX=a.mouseX,i.pmouseY=a.mouseY,i.moveX=i.moveY=0,a._lastFrameTime=t;let n=performance.now();a._fps=Math.round(1e3/(n-r))},a.noLoop=()=>{a._loop=!1,s=null},a.loop=()=>{a._loop=!0,a._setupDone&&null==s&&a._draw()},a.isLooping=()=>a._loop,a.redraw=(e=1)=>{a._redraw=!0;for(let t=0;t<e;t++)a._draw();a._redraw=!1},a.remove=()=>{a.noLoop(),a.canvas.remove()},a.frameRate=e=>(e&&(a._targetFrameRate=e,a._targetFrameDuration=1e3/e),a._frameRate),a.getTargetFrameRate=()=>a._targetFrameRate||60,a.getFPS=()=>a._fps,a.Element=function(e){this.elt=e},a._elements=[],a.TWO_PI=a.TAU=2*Math.PI,a.log=a.print=console.log,a.describe=()=>{};for(let e in Q5.modules)Q5.modules[e](a,i);let d=Q5.renderers[a._renderer];for(let e in d)d[e](a,i);for(let e in Q5)"_"!=e[1]&&e[1]==e[1].toUpperCase()&&(a[e]=Q5[e]);if("graphics"==e)return;"global"==e&&(Object.assign(Q5,a),delete Q5.Q5),a._webgpuFallback&&(a.colorMode("rgb",1),a._beginRender=()=>a.translate(a.canvas.hw,a.canvas.hh));for(let e of Q5.methods.init)e.call(a);for(let[e,t]of Object.entries(Q5.prototype))"_"!=e[0]&&"function"==typeof a[e]&&(a[e]=t.bind(a));if("global"==e){let e=Object.getOwnPropertyNames(a);for(let t of e)"_"!=t[0]&&(n[t]=a[t])}"function"==typeof e&&e(a),Q5._instanceCount++;let c=window.requestAnimationFrame||function(e){const t=a._lastFrameTime+a._targetFrameDuration;return setTimeout((()=>{e(t)}),t-performance.now())},h=n||a;a._isTouchAware=h.touchStarted||h.touchMoved||h.mouseReleased,a._isGlobal&&(a.preload=h.preload,a.setup=h.setup,a.draw=h.draw),a.preload??=()=>{},a.setup??=()=>{},a.draw??=()=>{};let u=["mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","mouseWheel","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let e of u)h[e]?a._isGlobal&&(a[e]=t=>{try{return h[e](t)}catch(e){throw a._askAI&&a._askAI(e),e}}):a[e]=()=>{};async function p(){if(a._startDone=!0,a._preloadCount>0)return c(p);l=performance.now(),await a.setup(),a._setupDone=!0,a.frameCount||(null===a.ctx&&a.createCanvas(200,200),a.ctx&&a.resetMatrix(),c(a._draw))}function f(){try{a.preload(),a._startDone||p()}catch(e){throw a._askAI&&a._askAI(e),e}}o?f():setTimeout(f,32)}function createCanvas(e,t,r){if(!Q5._hasGlobal){(new Q5).createCanvas(e,t,r)}}Q5.renderers={},Q5.modules={},Q5._nodejs="object"==typeof process,Q5._instanceCount=0,Q5._friendlyError=(e,t)=>{Q5.disableFriendlyErrors||console.error(t+": "+e)},Q5._validateParameters=()=>!0,Q5.methods={init:[],pre:[],post:[],remove:[]},Q5.prototype.registerMethod=(e,t)=>Q5.methods[e].push(t),Q5.prototype.registerPreloadMethod=(e,t)=>Q5.prototype[e]=t[e],Q5._nodejs&&(global.p5??=global.Q5=Q5),"object"==typeof window?window.p5??=window.Q5=Q5:global.window=0,Q5.version=Q5.VERSION="2.9","object"==typeof document&&document.addEventListener("DOMContentLoaded",(()=>{Q5._hasGlobal||new Q5("auto")})),Q5.modules.canvas=(e,t)=>{e.CENTER="center",e.LEFT="left",e.RIGHT="right",e.TOP="top",e.BOTTOM="bottom",e.BASELINE="alphabetic",e.NORMAL="normal",e.ITALIC="italic",e.BOLD="bold",e.BOLDITALIC="italic bold",e.ROUND="round",e.SQUARE="butt",e.PROJECT="square",e.MITER="miter",e.BEVEL="bevel",e.CHORD_OPEN=0,e.PIE_OPEN=1,e.PIE=2,e.CHORD=3,e.RADIUS="radius",e.CORNER="corner",e.CORNERS="corners",e.OPEN=0,e.CLOSE=1,e.LANDSCAPE="landscape",e.PORTRAIT="portrait",e.BLEND="source-over",e.REMOVE="destination-out",e.ADD="lighter",e.DARKEST="darken",e.LIGHTEST="lighten",e.DIFFERENCE="difference",e.SUBTRACT="subtract",e.EXCLUSION="exclusion",e.MULTIPLY="multiply",e.SCREEN="screen",e.REPLACE="copy",e.OVERLAY="overlay",e.HARD_LIGHT="hard-light",e.SOFT_LIGHT="soft-light",e.DODGE="color-dodge",e.BURN="color-burn",e.P2D="2d",e.WEBGL="webgl",e._OffscreenCanvas=window.OffscreenCanvas||function(){return document.createElement("canvas")},Q5._nodejs?Q5._createNodeJSCanvas&&(t.canvas=Q5._createNodeJSCanvas(100,100)):"image"!=e._scope&&"graphics"!=e._scope||(t.canvas=new e._OffscreenCanvas(100,100)),e.canvas||("object"==typeof document?(t.canvas=document.createElement("canvas"),e.canvas.id="q5Canvas"+Q5._instanceCount,e.canvas.classList.add("q5Canvas")):e.noCanvas());let r=e.canvas;async function a(e,t,r){if(t=t||"untitled","jpg"==(r=r||"png")||"png"==r||"webp"==r)if(e instanceof OffscreenCanvas){const t=await e.convertToBlob({type:"image/"+r});e=await new Promise((e=>{const r=new FileReader;r.onloadend=()=>e(r.result),r.readAsDataURL(t)}))}else e=e.toDataURL("image/"+r);else{let t="text/plain";"json"==r&&("string"!=typeof e&&(e=JSON.stringify(e)),t="text/json"),e=new Blob([e],{type:t}),e=URL.createObjectURL(e)}let a=document.createElement("a");a.href=e,a.download=t+"."+r,a.click(),URL.revokeObjectURL(a.href)}if(r.width=e.width=100,r.height=e.height=100,e._pixelDensity=1,e.displayDensity=()=>window.devicePixelRatio||1,"image"!=e._scope&&(r.renderer=e._renderer,r[e._renderer]=!0,e._pixelDensity=Math.ceil(e.displayDensity())),e._adjustDisplay=()=>{r.style&&(r.style.width=r.w+"px",r.style.height=r.h+"px")},e.createCanvas=function(t,a,n){n??=arguments[3];let o=Object.assign({},Q5.canvasOptions);if("object"==typeof n&&Object.assign(o,n),"image"!=e._scope)if("graphics"==e._scope)e._pixelDensity=this._pixelDensity;else if(window.IntersectionObserver){let t=!1;new IntersectionObserver((a=>{r.visible=a[0].isIntersecting,t||(e._wasLooping=e._loop,t=!0),r.visible?e._wasLooping&&!e._loop&&e.loop():(e._wasLooping=e._loop,e.noLoop())})).observe(r)}e._setCanvasSize(t,a),Object.assign(r,o);let i=e._createCanvas(r.w,r.h,o);if(e._hooks)for(let t of e._hooks.postCanvas)t();return e._beginRender&&e._beginRender(),i},e.createGraphics=function(t,r,a){let n=new Q5("graphics");return a??={},a.alpha??=!0,a.colorSpace??=e.canvas.colorSpace,n.createCanvas.call(e,t,r,a),n.defaultWidth=t,n.defaultHeight=r,n},e.save=(t,r,n)=>{if((!t||"string"==typeof t&&(!r||!n&&r.length<5))&&(n=r,r=t,t=e.canvas),n)return a(t,r,n);r?a(t,(r=r.split("."))[0],r.at(-1)):a(t)},e._setCanvasSize=(a,n)=>{a??=window.innerWidth,n??=window.innerHeight,e.defaultWidth=r.w=a=Math.ceil(a),e.defaultHeight=r.h=n=Math.ceil(n),r.hw=a/2,r.hh=n/2,r.width=Math.ceil(a*e._pixelDensity),r.height=Math.ceil(n*e._pixelDensity),e._da?e.flexibleCanvas(e._dau):(t.width=a,t.height=n),e.displayMode&&!r.displayMode?e.displayMode():e._adjustDisplay()},e._setImageSize=(a,n)=>{t.width=r.w=a,t.height=r.h=n,r.hw=a/2,r.hh=n/2,r.width=Math.ceil(a*e._pixelDensity),r.height=Math.ceil(n*e._pixelDensity)},e.defaultImageScale=t=>t?e._defaultImageScale=t:e._defaultImageScale,e.defaultImageScale(.5),"image"!=e._scope){if(r&&"graphics"!=e._scope){function n(){let t=e._parent;t??=document.getElementsByTagName("main")[0],t||(t=document.createElement("main"),document.body.append(t)),r.parent(t)}r.parent=t=>{function a(){e.frameCount>1&&(e._didResize=!0,e._adjustDisplay())}r.parentElement&&r.parentElement.removeChild(r),"string"==typeof t&&(t=document.getElementById(t)),t.append(r),"function"==typeof ResizeObserver?(e._ro&&e._ro.disconnect(),e._ro=new ResizeObserver(a),e._ro.observe(t)):e.frameCount||window.addEventListener("resize",a)},document.body?n():document.addEventListener("DOMContentLoaded",n)}e.resizeCanvas=(t,a)=>{if(!e.ctx)return e.createCanvas(t,a);t==r.w&&a==r.h||e._resizeCanvas(t,a)},e.canvas.resize=e.resizeCanvas,e.canvas.save=e.saveCanvas=e.save,e.pixelDensity=t=>t&&t!=e._pixelDensity?(e._pixelDensity=t,e._setCanvasSize(r.w,r.h),t):e._pixelDensity,e.flexibleCanvas=(a=400)=>{a?(e._da=r.width/(a*e._pixelDensity),t.width=e._dau=a,t.height=r.h/r.w*a):e._da=0},e._styleNames=["_fill","_stroke","_doStroke","_doFill","_strokeSet","_fillSet","_tint","_imageMode","_rectMode","_ellipseMode","_textSize","_textAlign","_textBaseline"],e._styles=[],e.pushStyles=()=>{let t={};for(let r of e._styleNames)t[r]=e[r];e._styles.push(t)},e.popStyles=()=>{let t=e._styles.pop();for(let r of e._styleNames)e[r]=t[r]},window&&"graphics"!=e._scope&&window.addEventListener("resize",(()=>{e._didResize=!0,t.windowWidth=window.innerWidth,t.windowHeight=window.innerHeight,t.deviceOrientation=window.screen?.orientation?.type}))}},Q5.canvasOptions={alpha:!1,colorSpace:"display-p3"},window.matchMedia&&matchMedia("(dynamic-range: high) and (color-gamut: p3)").matches?Q5.supportsHDR=!0:Q5.canvasOptions.colorSpace="srgb",Q5.renderers.q2d={},Q5.renderers.q2d.canvas=(e,t)=>{let r=e.canvas;e.colorMode&&e.colorMode("rgb","integer"),e._createCanvas=function(a,n,o){return t.ctx=t.drawingContext=r.getContext("2d",o),"image"!=e._scope&&(e.ctx.fillStyle=e._fill="white",e.ctx.strokeStyle=e._stroke="black",e.ctx.lineCap="round",e.ctx.lineJoin="miter",e.ctx.textAlign="left"),e.ctx.scale(e._pixelDensity,e._pixelDensity),e.ctx.save(),r},e.clear=()=>{e.ctx.save(),e.ctx.resetTransform(),e.ctx.clearRect(0,0,e.canvas.width,e.canvas.height),e.ctx.restore()},"image"!=e._scope&&(e._resizeCanvas=(t,a)=>{let n,o={};for(let t in e.ctx)"function"!=typeof e.ctx[t]&&(o[t]=e.ctx[t]);if(delete o.canvas,e.frameCount>1){n=new e._OffscreenCanvas(r.width,r.height),n.w=r.w,n.h=r.h,n.getContext("2d").drawImage(r,0,0)}e._setCanvasSize(t,a);for(let t in o)e.ctx[t]=o[t];e.scale(e._pixelDensity),n&&e.ctx.drawImage(n,0,0,n.w,n.h)},e.fill=function(t){if(e._doFill=e._fillSet=!0,Q5.Color&&(t._q5Color||("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),t.a<=0))return e._doFill=!1;e.ctx.fillStyle=e._fill=t.toString()},e.stroke=function(t){if(e._doStroke=e._strokeSet=!0,Q5.Color&&(t._q5Color||("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),t.a<=0))return e._doStroke=!1;e.ctx.strokeStyle=e._stroke=t.toString()},e.strokeWeight=t=>{t||(e._doStroke=!1),e._da&&(t*=e._da),e.ctx.lineWidth=e._strokeWeight=t||1e-4},e.noFill=()=>e._doFill=!1,e.noStroke=()=>e._doStroke=!1,e.opacity=t=>e.ctx.globalAlpha=t,e.translate=(t,r)=>{e._da&&(t*=e._da,r*=e._da),e.ctx.translate(t,r)},e.rotate=t=>{e._angleMode&&(t=e.radians(t)),e.ctx.rotate(t)},e.scale=(t,r)=>{t.x&&(r=t.y,t=t.x),r??=t,e.ctx.scale(t,r)},e.applyMatrix=(t,r,a,n,o,i)=>e.ctx.transform(t,r,a,n,o,i),e.shearX=t=>e.ctx.transform(1,0,e.tan(t),1,0,0),e.shearY=t=>e.ctx.transform(1,e.tan(t),0,1,0,0),e.resetMatrix=()=>{e.ctx&&(e.ctx.resetTransform(),e.scale(e._pixelDensity))},e.pushMatrix=()=>e.ctx.save(),e.popMatrix=()=>e.ctx.restore(),e.popStyles=()=>{let t=e._styles.pop();for(let r of e._styleNames)e[r]=t[r];e.ctx.fillStyle=e._fill,e.ctx.strokeStyle=e._stroke,e.ctx.lineWidth=e._strokeWeight},e.push=()=>{e.ctx.save(),e.pushStyles()},e.pop=()=>{e.ctx.restore(),e.popStyles()},e.createCapture=e=>{var t=document.createElement("video");return t.playsinline="playsinline",t.autoplay="autoplay",navigator.mediaDevices.getUserMedia(e).then((e=>{t.srcObject=e})),t.style.position="absolute",t.style.opacity=1e-5,t.style.zIndex=-1e3,document.body.append(t),t})},Q5.renderers.q2d.drawing=e=>{e._doStroke=!0,e._doFill=!0,e._strokeSet=!1,e._fillSet=!1,e._ellipseMode=e.CENTER,e._rectMode=e.CORNER,e._curveDetail=20,e._curveAlpha=0;let t=!0,r=[];function a(){e._doFill&&e.ctx.fill(),e._doStroke&&e.ctx.stroke()}function n(t,r,a,n,o,i,s){e._angleMode&&(o=e.radians(o),i=e.radians(i));let l=e.TAU;if((o%=l)<0&&(o+=l),(i%=l)<0&&(i+=l),o>i&&(i+=l),o==i)return e.ellipse(t,r,a,n);if(a/=2,n/=2,e._doFill||s!=e.PIE_OPEN||(s=e.CHORD_OPEN),e.ctx.beginPath(),e.ctx.ellipse(t,r,a,n,0,o,i),s!=e.PIE&&s!=e.PIE_OPEN||e.ctx.lineTo(t,r),e._doFill&&e.ctx.fill(),e._doStroke){if(s!=e.PIE&&s!=e.CHORD||e.ctx.closePath(),s!=e.PIE_OPEN)return e.ctx.stroke();e.ctx.beginPath(),e.ctx.ellipse(t,r,a,n,0,o,i),e.ctx.stroke()}}function o(t,r,n,o){e.ctx.beginPath(),e.ctx.ellipse(t,r,n/2,o/2,0,0,e.TAU),a()}function i(t,r,n,o,s,l,d,c){return void 0===s?function(t,r,n,o){e._da&&(t*=e._da,r*=e._da,n*=e._da,o*=e._da),e.ctx.beginPath(),e.ctx.rect(t,r,n,o),a()}(t,r,n,o):void 0===l?i(t,r,n,o,s,s,s,s):(e._da&&(t*=e._da,r*=e._da,n*=e._da,o*=e._da,s*=e._da,l*=e._da,c*=e._da,d*=e._da),e.ctx.roundRect(t,r,n,o,[s,l,d,c]),void a())}e.blendMode=t=>e.ctx.globalCompositeOperation=t,e.strokeCap=t=>e.ctx.lineCap=t,e.strokeJoin=t=>e.ctx.lineJoin=t,e.ellipseMode=t=>e._ellipseMode=t,e.rectMode=t=>e._rectMode=t,e.curveDetail=t=>e._curveDetail=t,e.curveAlpha=t=>e._curveAlpha=t,e.curveTightness=t=>e._curveAlpha=t,e.background=function(t){e.ctx.save(),e.ctx.resetTransform(),e.ctx.globalAlpha=1,t.canvas?e.image(t,0,0,e.canvas.width,e.canvas.height):(Q5.Color&&!t._q5Color&&("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),e.ctx.fillStyle=t.toString(),e.ctx.fillRect(0,0,e.canvas.width,e.canvas.height)),e.ctx.restore()},e.line=(t,r,a,n)=>{e._doStroke&&(e._da&&(t*=e._da,r*=e._da,a*=e._da,n*=e._da),e.ctx.beginPath(),e.ctx.moveTo(t,r),e.ctx.lineTo(a,n),e.ctx.stroke())},e.arc=(t,r,a,o,i,s,l)=>{if(i==s)return e.ellipse(t,r,a,o);e._da&&(t*=e._da,r*=e._da,a*=e._da,o*=e._da),l??=e.PIE_OPEN,e._ellipseMode==e.CENTER?n(t,r,a,o,i,s,l):e._ellipseMode==e.RADIUS?n(t,r,2*a,2*o,i,s,l):e._ellipseMode==e.CORNER?n(t+a/2,r+o/2,a,o,i,s,l):e._ellipseMode==e.CORNERS&&n((t+a)/2,(r+o)/2,a-t,o-r,i,s,l)},e.ellipse=(t,r,a,n)=>{n??=a,e._da&&(t*=e._da,r*=e._da,a*=e._da,n*=e._da),e._ellipseMode==e.CENTER?o(t,r,a,n):e._ellipseMode==e.RADIUS?o(t,r,2*a,2*n):e._ellipseMode==e.CORNER?o(t+a/2,r+n/2,a,n):e._ellipseMode==e.CORNERS&&o((t+a)/2,(r+n)/2,a-t,n-r)},e.circle=(t,r,n)=>{e._ellipseMode==e.CENTER?(e._da&&(t*=e._da,r*=e._da,n*=e._da),e.ctx.beginPath(),e.ctx.arc(t,r,n/2,0,e.TAU),a()):e.ellipse(t,r,n,n)},e.point=(t,r)=>{e._doStroke&&(t.x&&(r=t.y,t=t.x),e._da&&(t*=e._da,r*=e._da),e.ctx.beginPath(),e.ctx.moveTo(t,r),e.ctx.lineTo(t,r),e.ctx.stroke())},e.rect=(t,r,a,n=a,o,s,l,d)=>{e._rectMode==e.CENTER?i(t-a/2,r-n/2,a,n,o,s,l,d):e._rectMode==e.RADIUS?i(t-a,r-n,2*a,2*n,o,s,l,d):e._rectMode==e.CORNER?i(t,r,a,n,o,s,l,d):e._rectMode==e.CORNERS&&i(t,r,a-t,n-r,o,s,l,d)},e.square=(t,r,a,n,o,i,s)=>e.rect(t,r,a,a,n,o,i,s),e.beginShape=()=>{r.length=0,e.ctx.beginPath(),t=!0},e.beginContour=()=>{e.ctx.closePath(),r.length=0,t=!0},e.endContour=()=>{r.length=0,t=!0},e.vertex=(a,n)=>{e._da&&(a*=e._da,n*=e._da),r.length=0,t?e.ctx.moveTo(a,n):e.ctx.lineTo(a,n),t=!1},e.bezierVertex=(t,a,n,o,i,s)=>{e._da&&(t*=e._da,a*=e._da,n*=e._da,o*=e._da,i*=e._da,s*=e._da),r.length=0,e.ctx.bezierCurveTo(t,a,n,o,i,s)},e.quadraticVertex=(t,a,n,o)=>{e._da&&(t*=e._da,a*=e._da,n*=e._da,o*=e._da),r.length=0,e.ctx.quadraticCurveTo(t,a,n,o)},e.bezier=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.bezierVertex(a,n,o,i,s,l),e.endShape()},e.triangle=(t,r,a,n,o,i)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.endShape(e.CLOSE)},e.quad=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.vertex(s,l),e.endShape(e.CLOSE)},e.endShape=t=>{r.length=0,t&&e.ctx.closePath(),a()},e.curveVertex=(a,n)=>{if(e._da&&(a*=e._da,n*=e._da),r.push([a,n]),r.length<4)return;let o=r.at(-4),i=r.at(-3),s=r.at(-2),l=r.at(-1),d=i[0]+(s[0]-o[0])/6,c=i[1]+(s[1]-o[1])/6,h=s[0]-(l[0]-i[0])/6,u=s[1]-(l[1]-i[1])/6;t&&(e.ctx.moveTo(i[0],i[1]),t=!1),e.ctx.bezierCurveTo(d,c,h,u,s[0],s[1])},e.curve=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.curveVertex(t,r),e.curveVertex(a,n),e.curveVertex(o,i),e.curveVertex(s,l),e.endShape()},e.curvePoint=(e,t,r,a,n)=>{const o=n*n*n,i=n*n;return e*(-.5*o+i-.5*n)+t*(1.5*o-2.5*i+1)+r*(-1.5*o+2*i+.5*n)+a*(.5*o-.5*i)},e.bezierPoint=(e,t,r,a,n)=>{const o=1-n;return Math.pow(o,3)*e+3*Math.pow(o,2)*n*t+3*o*Math.pow(n,2)*r+Math.pow(n,3)*a},e.curveTangent=(e,t,r,a,n)=>{const o=n*n;return e*(-3*o/2+2*n-.5)+t*(9*o/2-5*n)+r*(-9*o/2+4*n+.5)+a*(3*o/2-n)},e.bezierTangent=(e,t,r,a,n)=>{const o=1-n;return 3*a*Math.pow(n,2)-3*r*Math.pow(n,2)+6*r*o*n-6*t*o*n+3*t*Math.pow(o,2)-3*e*Math.pow(o,2)},e.erase=function(t=255,r=255){e.ctx.save(),e.ctx.globalCompositeOperation="destination-out",e.ctx.fillStyle=`rgba(0, 0, 0, ${t/255})`,e.ctx.strokeStyle=`rgba(0, 0, 0, ${r/255})`},e.noErase=function(){e.ctx.globalCompositeOperation="source-over",e.ctx.restore()},e.inFill=(t,r)=>{const a=e._pixelDensity;return e.ctx.isPointInPath(t*a,r*a)},e.inStroke=(t,r)=>{const a=e._pixelDensity;return e.ctx.isPointInStroke(t*a,r*a)}},Q5.renderers.q2d.image=(e,t)=>{Q5.Image??=class{constructor(e,t,r){let a=this;a._scope="image",a.canvas=a.ctx=a.drawingContext=null,a.pixels=[],Q5.modules.canvas(a,a);let n=Q5.renderers.q2d;for(let e of["canvas","image","soft_filters"])n[e]&&n[e](a,a);a._pixelDensity=r.pixelDensity||1,a.createCanvas(e,t,r),delete a.createCanvas,a._loop=!1}get w(){return this.width}get h(){return this.height}},e.createImage=(t,r,a)=>{a??={},a.alpha??=!0,a.colorSpace??=e.canvas.colorSpace||Q5.canvasOptions.colorSpace;let n=new Q5.Image(t,r,a);return n.defaultWidth=t*e._defaultImageScale,n.defaultHeight=r*e._defaultImageScale,n},e.loadImage=function(r,a,n){if(r.canvas)return r;if("gif"==r.slice(-3).toLowerCase())throw new Error("q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84");t._preloadCount++;let o=[...arguments].at(-1);n="object"==typeof o?o:null;let i=e.createImage(1,1,n),s=i._pixelDensity=n?.pixelDensity||1;function l(r){i.defaultWidth=r.width*e._defaultImageScale,i.defaultHeight=r.height*e._defaultImageScale,i.naturalWidth=r.naturalWidth,i.naturalHeight=r.naturalHeight,i._setImageSize(Math.ceil(i.naturalWidth/s),Math.ceil(i.naturalHeight/s)),i.ctx.drawImage(r,0,0),t._preloadCount--,a&&a(i)}if(Q5._nodejs&&global.CairoCanvas)global.CairoCanvas.loadImage(r).then(l).catch((e=>{throw t._preloadCount--,e}));else{let e=new window.Image;e.src=r,e.crossOrigin="Anonymous",e._pixelDensity=s,e.onload=()=>l(e),e.onerror=e=>{throw t._preloadCount--,e}}return i},e.imageMode=t=>e._imageMode=t,e.image=(t,r,a,n,o,i=0,s=0,l,d)=>{if(!t)return;let c=t?.canvas||t;Q5._createNodeJSCanvas&&(c=c.context.canvas),n??=t.defaultWidth||c.width||t.videoWidth,o??=t.defaultHeight||c.height||t.videoHeight,"center"==e._imageMode&&(r-=.5*n,a-=.5*o),e._da&&(r*=e._da,a*=e._da,n*=e._da,o*=e._da,i*=e._da,s*=e._da,l*=e._da,d*=e._da);let h=t._pixelDensity||1;l?l*=h:l=c.width||c.videoWidth,d?d*=h:d=c.height||c.videoHeight,e.ctx.drawImage(c,i*h,s*h,l,d,r,a,n,o),e._tint&&(e.ctx.globalCompositeOperation="multiply",e.ctx.fillStyle=e._tint.toString(),e.ctx.fillRect(r,a,n,o),e.ctx.globalCompositeOperation="source-over")},e._tint=null;let r=null;e._softFilter=()=>{throw new Error("Load q5-2d-soft-filters.js to use software filters.")},e.filter=(t,r)=>{if(!e.ctx.filter)return e._softFilter(t,r);if("string"==typeof t)f=t;else if(t==Q5.GRAY)f="saturate(0%)";else if(t==Q5.INVERT)f="invert(100%)";else if(t==Q5.BLUR){let t=Math.ceil(r*e._pixelDensity)||1;f=`blur(${t}px)`}else{if(t!=Q5.THRESHOLD)return e._softFilter(t,r);{r??=.5;let e=Math.floor(.5/Math.max(r,1e-5)*100);f=`saturate(0%) brightness(${e}%) contrast(1000000%)`}}e.ctx.filter=f,e.ctx.drawImage(e.canvas,0,0,e.canvas.w,e.canvas.h),e.ctx.filter="none"},"image"==e._scope&&(e.resize=(t,r)=>{let a=e.canvas,n=new e._OffscreenCanvas(a.width,a.height);n.getContext("2d",{colorSpace:a.colorSpace}).drawImage(a,0,0),e._setImageSize(t,r),e.defaultWidth=a.width*e._defaultImageScale,e.defaultHeight=a.height*e._defaultImageScale,e.ctx.clearRect(0,0,a.width,a.height),e.ctx.drawImage(n,0,0,a.width,a.height)}),e._getImageData=(t,r,a,n)=>e.ctx.getImageData(t,r,a,n,{colorSpace:e.canvas.colorSpace}),e.trim=()=>{let t=e._pixelDensity||1,r=e.canvas.width,a=e.canvas.height,n=e._getImageData(0,0,r,a).data,o=r,i=0,s=a,l=0,d=3;for(let e=0;e<a;e++)for(let t=0;t<r;t++)0!==n[d]&&(t<o&&(o=t),t>i&&(i=t),e<s&&(s=e),e>l&&(l=e)),d+=4;return s=Math.floor(s/t),l=Math.floor(l/t),o=Math.floor(o/t),i=Math.floor(i/t),e.get(o,s,i-o+1,l-s+1)},e.mask=t=>{e.ctx.save(),e.ctx.resetTransform();let r=e.ctx.globalCompositeOperation;e.ctx.globalCompositeOperation="destination-in",e.ctx.drawImage(t.canvas,0,0),e.ctx.globalCompositeOperation=r,e.ctx.restore()},e.inset=(t,r,a,n,o,i,s,l)=>{let d=e._pixelDensity||1;e.ctx.drawImage(e.canvas,t*d,r*d,a*d,n*d,o,i,s,l)},e.copy=()=>e.get(),e.get=(t,r,a,n)=>{let o=e._pixelDensity||1;if(void 0!==t&&void 0===a){let a=e._getImageData(t*o,r*o,1,1).data;return[a[0],a[1],a[2],a[3]/255]}t=(t||0)*o,r=(r||0)*o;let i=a=a||e.width,s=n=n||e.height;a*=o,n*=o;let l=e.createImage(a,n);return l.ctx.drawImage(e.canvas,t,r,a,n,0,0,a,n),l._pixelDensity=o,l.width=i,l.height=s,l},e.set=(t,r,a)=>{if(a.canvas){let n=e._tint;return e._tint=null,e.image(a,t,r),void(e._tint=n)}e.pixels.length||e.loadPixels();let n=e._pixelDensity||1;for(let o=0;o<n;o++)for(let i=0;i<n;i++){let s=4*((r*n+o)*e.canvas.width+t*n+i);e.pixels[s]=a.r,e.pixels[s+1]=a.g,e.pixels[s+2]=a.b,e.pixels[s+3]=a.a}},e.loadPixels=()=>{r=e._getImageData(0,0,e.canvas.width,e.canvas.height),t.pixels=r.data},e.updatePixels=()=>{null!=r&&e.ctx.putImageData(r,0,0)},e.smooth=()=>e.ctx.imageSmoothingEnabled=!0,e.noSmooth=()=>e.ctx.imageSmoothingEnabled=!1,"image"!=e._scope&&(e.tint=function(t){e._tint=t._q5Color?t:e.color(...arguments)},e.noTint=()=>e._tint=null)},Q5.THRESHOLD=1,Q5.GRAY=2,Q5.OPAQUE=3,Q5.INVERT=4,Q5.POSTERIZE=5,Q5.DILATE=6,Q5.ERODE=7,Q5.BLUR=8,Q5.renderers.q2d.text=(e,t)=>{e._textAlign="left",e._textBaseline="alphabetic",e._textSize=12;let r="sans-serif",a=!1,n=15,o=3,i="normal",s=!1,l=0,d=[],c=!1,h=!1,u=0,p=12e3,f=e._textCache={};e.loadFont=(e,r)=>{t._preloadCount++;let a=e.split("/").pop().split(".")[0].replace(" ",""),n=new FontFace(a,`url(${e})`);return document.fonts.add(n),n.load().then((()=>{t._preloadCount--,r&&r(a)})),a},e.textFont=e=>{if(!e||e==r)return r;r=e,s=!0,l=-1},e.textSize=t=>{if(null==t||t==e._textSize)return e._textSize;e._da&&(t*=e._da),e._textSize=t,s=!0,l=-1,a||(n=1.25*t,o=n-t)},e.textStyle=e=>{if(!e||e==i)return i;i=e,s=!0,l=-1},e.textLeading=t=>{if(a=!0,null==t||t==n)return n;e._da&&(t*=e._da),n=t,o=t-e._textSize,l=-1},e.textAlign=(t,r)=>{e.ctx.textAlign=e._textAlign=t,r&&(e.ctx.textBaseline=e._textBaseline=r==e.CENTER?"middle":r)},e.textWidth=t=>e.ctx.measureText(t).width,e.textAscent=t=>e.ctx.measureText(t).actualBoundingBoxAscent,e.textDescent=t=>e.ctx.measureText(t).actualBoundingBoxDescent,e.textFill=e.fill,e.textStroke=e.stroke;e.textCache=(e,t)=>(t&&(p=t),void 0!==e&&(c=e),c),e.createTextImage=(t,r,a)=>(h=!0,img=e.text(t,0,0,r,a),h=!1,img);let _=[];e.text=(t,a,g,x,m)=>{if(void 0===t||!e._doFill&&!e._doStroke)return;t=t.toString(),e._da&&(a*=e._da,g*=e._da);let v,y,w,b,S=e.ctx;if(s&&(S.font=`${i} ${e._textSize}px ${r}`,s=!1),(c||h)&&(-1==l&&(()=>{let t=r+e._textSize+i+n,a=5381;for(let e=0;e<t.length;e++)a=33*a^t.charCodeAt(e);l=a>>>0})(),v=f[t],v&&(v=v[l]),v)){if(v._fill==e._fill&&v._stroke==e._stroke&&v._strokeWeight==e._strokeWeight)return h?v:e.textImage(v,a,g);v.clear()}if(-1==t.indexOf("\n")?_[0]=t:_=t.split("\n"),t.length>x){let e=[];for(let t of _){let r=0;for(;r<t.length;){let a=r+x;if(a>=t.length){e.push(t.slice(r));break}let n=t.lastIndexOf(" ",a);(-1===n||n<r)&&(n=a),e.push(t.slice(r,n)),r=n+1}}_=e}if(c||h){if(y=0,w=n*_.length,!v){let r=S.measureText(" "),a=r.fontBoundingBoxAscent,i=r.fontBoundingBoxDescent;m??=w+i,v=e.createImage.call(e,Math.ceil(S.measureText(t).width),Math.ceil(m),{pixelDensity:e._pixelDensity}),v._ascent=a,v._descent=i,v._top=i+o,v._middle=v._top+.5*a,v._bottom=v._top+a,v._leading=n}v._fill=e._fill,v._stroke=e._stroke,v._strokeWeight=e._strokeWeight,v.modified=!0,S=v.ctx,S.font=e.ctx.font,S.fillStyle=e._fill,S.strokeStyle=e._stroke,S.lineWidth=e.ctx.lineWidth}else y=a,w=g;e._fillSet||(b=S.fillStyle,S.fillStyle="black");for(let t of _)if(e._doStroke&&e._strokeSet&&S.strokeText(t,y,w),e._doFill&&S.fillText(t,y,w),w+=n,w>m)break;if(_.length=0,e._fillSet||(S.fillStyle=b),c||h){if(d.push(l),(f[t]??={})[l]=v,u++,u>p){let e=Math.ceil(u/2),t=d.splice(0,e);for(let e in f){e=f[e];for(let r of t)delete e[r]}u-=e}if(h)return v;e.textImage(v,a,g)}},e.textImage=(t,r,a)=>{"string"==typeof t&&(t=e.createTextImage(t));let n=e._imageMode;e._imageMode="corner";let o=e._textAlign;"center"==o?r-=t.canvas.hw:"right"==o&&(r-=t.width);let i=e._textBaseline;"alphabetic"==i?a-=t._leading:"middle"==i?a-=t._middle:"bottom"==i?a-=t._bottom:"top"==i&&(a-=t._top),e.image(t,r,a),e._imageMode=n},e.nf=(e,t,r)=>{let a=e<0,n=(e=Math.abs(e)).toFixed(r).split(".");n[0]=n[0].padStart(t,"0");let o=n.join(".");return a&&(o="-"+o),o}},Q5.modules.ai=e=>{e.askAI=(e="")=>{throw Q5.disableFriendlyErrors=!1,Error("Ask AI ✨ "+e)},e._askAI=async e=>{let t=e.message?.includes("Ask AI ✨"),r=e.stack?.split("\n");if(!e.stack||r.length<=1)return;let a=1,n="(";for(-1==navigator.userAgent.indexOf("Chrome")&&(a=0,n="@");r[a].indexOf("q5")>=0;)a++;let o=r[a].split(n).at(-1);o.startsWith("blob:")&&(o=o.slice(5));let i=o.split(":"),s=parseInt(i.at(-2));t&&s++,i[3]=i[3].split(")")[0];let l=i.slice(0,2).join(":"),d=l.split("/").at(-1);try{let r=(await(await fetch(l)).text()).split("\n"),a=r[s-1].trim(),n="",o=1;for(;n.length<1600&&(s-o>=0&&(n=r[s-o].trim()+"\n"+n),s+o<r.length);)n+=r[s+o].trim()+"\n",o++;let i="https://chatgpt.com/?q=q5.js+"+(t&&e.message.length>10?e.message.slice(10):"Whats+wrong+with+this+line%3F+short+answer")+(t?"":"%0A%0A"+encodeURIComponent(e.name+": "+e.message))+"%0A%0ALine%3A+"+encodeURIComponent(a)+"%0A%0AExcerpt+for+context%3A%0A%0A"+encodeURIComponent(n);if(console.warn("Error in "+d+" on line "+s+":\n\n"+a),console.warn("Ask AI ✨ "+i),t)return window.open(i,"_blank")}catch(e){}}},Q5.modules.color=(e,t)=>{e.RGB=e.RGBA=e._colorMode="rgb",e.OKLCH="oklch",e.colorMode=(r,a)=>{e._colorMode=r;let n="srgb"==e.canvas.colorSpace||"srgb"==r;if(a??=n?"integer":"float",e._colorFormat="float"==a||1==a?1:255,"oklch"==r)t.Color=Q5.ColorOKLCH;else{let r="srgb"==e.canvas.colorSpace;255==e._colorFormat?t.Color=r?Q5.ColorRGBA_8:Q5.ColorRGBA_P3_8:t.Color=r?Q5.ColorRGBA:Q5.ColorRGBA_P3,e._colorMode="rgb"}},e._namedColors={aqua:[0,255,255],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],crimson:[220,20,60],cyan:[0,255,255],darkviolet:[148,0,211],gold:[255,215,0],green:[0,128,0],gray:[128,128,128],grey:[128,128,128],hotpink:[255,105,180],indigo:[75,0,130],khaki:[240,230,140],lightgreen:[144,238,144],lime:[0,255,0],magenta:[255,0,255],navy:[0,0,128],orange:[255,165,0],olive:[128,128,0],peachpuff:[255,218,185],pink:[255,192,203],purple:[128,0,128],red:[255,0,0],skyblue:[135,206,235],tan:[210,180,140],turquoise:[64,224,208],transparent:[0,0,0,0],white:[255,255,255],violet:[238,130,238],yellow:[255,255,0]},e.color=(t,r,a,n)=>{let o=e.Color;if(t._q5Color)return new o(...t.levels);if(null==r){if("string"==typeof t){if("#"==t[0])t.length<=5?(t.length>4&&(n=parseInt(t[4]+t[4],16)),a=parseInt(t[3]+t[3],16),r=parseInt(t[2]+t[2],16),t=parseInt(t[1]+t[1],16)):(t.length>7&&(n=parseInt(t.slice(7,9),16)),a=parseInt(t.slice(5,7),16),r=parseInt(t.slice(3,5),16),t=parseInt(t.slice(1,3),16));else{if(!e._namedColors[t])return console.error("q5 can't parse color: "+t+"\nOnly numeric input, hex, and common named colors are supported."),new o(0,0,0);[t,r,a,n]=e._namedColors[t]}1==e._colorFormat&&(t/=255,r&&(r/=255),a&&(a/=255),n&&(n/=255))}Array.isArray(t)&&([t,r,a,n]=t)}return null==a?new o(t,t,t,r):new o(t,r,a,n)},e.red=e=>e.r,e.green=e=>e.g,e.blue=e=>e.b,e.alpha=e=>e.a,e.lightness=e=>e.l?e.l:100*(.2126*e.r+.7152*e.g+.0722*e.b)/255,e.hue=t=>{if(t.h)return t.h;let r=t.r,a=t.g,n=t.b;255==e._colorFormat&&(r/=255,a/=255,n/=255);let o,i=Math.max(r,a,n),s=Math.min(r,a,n);return o=i==s?0:i==r?60*(a-n)/(i-s):i==a?60*(n-r)/(i-s)+120:60*(r-a)/(i-s)+240,o<0&&(o+=360),o},e.lerpColor=(t,r,a)=>{if(a=Math.max(0,Math.min(1,a)),"rgb"==e._colorMode)return new e.Color(e.lerp(t.r,r.r,a),e.lerp(t.g,r.g,a),e.lerp(t.b,r.b,a),e.lerp(t.a,r.a,a));{let n=r.h-t.h;n>180&&(n-=360),n<-180&&(n+=360);let o=t.h+a*n;return o<0&&(o+=360),o>360&&(o-=360),new e.Color(e.lerp(t.l,r.l,a),e.lerp(t.c,r.c,a),o,e.lerp(t.a,r.a,a))}}},Q5.Color=class{constructor(){this._q5Color=!0}},Q5.ColorOKLCH=class extends Q5.Color{constructor(e,t,r,a){super(),this.l=e,this.c=t,this.h=r,this.a=a??1}toString(){return`oklch(${this.l} ${this.c} ${this.h} / ${this.a})`}},Q5.ColorRGBA=class extends Q5.Color{constructor(e,t,r,a){super(),this.r=e,this.g=t,this.b=r,this.a=a??1}get levels(){return[this.r,this.g,this.b,this.a]}toString(){return`color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`}},Q5.ColorRGBA_P3=class extends Q5.ColorRGBA{toString(){return`color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`}},Q5.ColorRGBA_8=class extends Q5.ColorRGBA{constructor(e,t,r,a){super(e,t,r,a??255)}setRed(e){this.r=e}setGreen(e){this.g=e}setBlue(e){this.b=e}setAlpha(e){this.a=e}get levels(){return[this.r,this.g,this.b,this.a]}toString(){return`rgb(${this.r} ${this.g} ${this.b} / ${this.a/255})`}},Q5.ColorRGBA_P3_8=class extends Q5.ColorRGBA{constructor(e,t,r,a){super(e,t,r,a??255),this._edited=!0}get r(){return this._r}set r(e){this._r=e,this._edited=!0}get g(){return this._g}set g(e){this._g=e,this._edited=!0}get b(){return this._b}set b(e){this._b=e,this._edited=!0}get a(){return this._a}set a(e){this._a=e,this._edited=!0}toString(){if(this._edited){let e=(this._r/255).toFixed(3),t=(this._g/255).toFixed(3),r=(this._b/255).toFixed(3),a=(this._a/255).toFixed(3);this._css=`color(display-p3 ${e} ${t} ${r} / ${a})`,this._edited=!1}return this._css}},Q5.modules.display=e=>{if(!e.canvas||"graphics"==e._scope)return;let t=e.canvas;e.CENTERED="centered",e.FULLSCREEN="fullscreen",e.MAXED="maxed",e.PIXELATED="pixelated",0!=Q5._instanceCount||Q5._nodejs||document.head.insertAdjacentHTML("beforeend","<style>\nhtml, body {\n\tmargin: 0;\n\tpadding: 0;\n}\n.q5Canvas {\n\toutline: none;\n\t-webkit-touch-callout: none;\n\t-webkit-text-size-adjust: none;\n\t-webkit-user-select: none;\n\toverscroll-behavior: none;\n}\n.q5-pixelated {\n\timage-rendering: pixelated;\n\tfont-smooth: never;\n\t-webkit-font-smoothing: none;\n}\n.q5-centered,\n.q5-maxed,\n.q5-fullscreen {\n display: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\nmain.q5-centered,\nmain.q5-maxed,\n.q5-fullscreen {\n\theight: 100vh;\n}\nmain {\n\toverscroll-behavior: none;\n}\n</style>"),e._adjustDisplay=()=>{let r=t.style,a=t.parentElement;r&&a&&t.displayMode&&("pixelated"==t.renderQuality&&(t.classList.add("q5-pixelated"),e.pixelDensity(1),e.defaultImageScale(1),e.noSmooth&&e.noSmooth(),e.textFont&&e.textFont("monospace")),"default"==t.displayMode||"normal"==t.displayMode?(a.classList.remove("q5-centered","q5-maxed","q5-fullscreen"),r.width=t.w*t.displayScale+"px",r.height=t.h*t.displayScale+"px"):(a.classList.add("q5-"+t.displayMode),a=a.getBoundingClientRect(),t.w/t.h>a.width/a.height?("centered"==t.displayMode?(r.width=t.w*t.displayScale+"px",r.maxWidth="100%"):r.width="100%",r.height="auto",r.maxHeight=""):(r.width="auto",r.maxWidth="","centered"==t.displayMode?(r.height=t.h*t.displayScale+"px",r.maxHeight="100%"):r.height="100%")))},e.displayMode=(r="normal",a="smooth",n=1)=>{"string"==typeof n&&(n=parseFloat(n.slice(1))),"center"==r&&(r="centered"),Object.assign(t,{displayMode:r,renderQuality:a,displayScale:n}),e._adjustDisplay()},e.fullscreen=e=>{if(void 0===e)return document.fullscreenElement;e?document.body.requestFullscreen():document.body.exitFullscreen()}},Q5.modules.input=(e,t)=>{if("graphics"==e._scope)return;e.mouseX=0,e.mouseY=0,e.pmouseX=0,e.pmouseY=0,e.touches=[],e.mouseButton="",e.keyIsPressed=!1,e.mouseIsPressed=!1,e.key="",e.keyCode=0,e.UP_ARROW=38,e.DOWN_ARROW=40,e.LEFT_ARROW=37,e.RIGHT_ARROW=39,e.SHIFT=16,e.TAB=9,e.BACKSPACE=8,e.ENTER=e.RETURN=13,e.ALT=e.OPTION=18,e.CONTROL=17,e.DELETE=46,e.ESCAPE=27,e.ARROW="default",e.CROSS="crosshair",e.HAND="pointer",e.MOVE="move",e.TEXT="text";let r={},a=[e.LEFT,e.CENTER,e.RIGHT],n=e.canvas;function o(t){const r=e.canvas.getBoundingClientRect(),a=e.canvas.scrollWidth/e.width||1,n=e.canvas.scrollHeight/e.height||1;return{x:(t.clientX-r.left)/a,y:(t.clientY-r.top)/n,id:t.identifier}}if(e._startAudio=()=>{e.getAudioContext&&"suspended"==e.getAudioContext()?.state&&e.userStartAudio()},e._updateMouse=r=>{if(!r.changedTouches){if(n){let a=n.getBoundingClientRect(),o=n.scrollWidth/e.width||1,i=n.scrollHeight/e.height||1;t.mouseX=(r.clientX-a.left)/o,t.mouseY=(r.clientY-a.top)/i,"webgpu"==n.renderer&&(t.mouseX-=n.hw,t.mouseY-=n.hh)}else t.mouseX=r.clientX,t.mouseY=r.clientY;t.moveX=r.movementX,t.moveY=r.movementY}},e._onmousedown=r=>{e._startAudio(),e._updateMouse(r),t.mouseIsPressed=!0,t.mouseButton=a[r.button],e.mousePressed(r)},e._onmousemove=t=>{e._updateMouse(t),e.mouseIsPressed?e.mouseDragged(t):e.mouseMoved(t)},e._onmouseup=r=>{e._updateMouse(r),t.mouseIsPressed=!1,e.mouseReleased(r)},e._onclick=r=>{e._updateMouse(r),t.mouseIsPressed=!0,e.mouseClicked(r),t.mouseIsPressed=!1},e._onwheel=t=>{e._updateMouse(t),t.delta=t.deltaY,0==e.mouseWheel(t)&&t.preventDefault()},e.cursor=(t,r,a)=>{let n="";t.includes(".")&&(t=`url("${t}")`,n=", auto"),void 0!==r&&(t+=" "+r+" "+a),e.canvas.style.cursor=t+n},e.noCursor=()=>{e.canvas.style.cursor="none"},window&&(e.requestPointerLock=document.body?.requestPointerLock,e.exitPointerLock=document.exitPointerLock),e._onkeydown=a=>{a.repeat||(e._startAudio(),t.keyIsPressed=!0,t.key=a.key,t.keyCode=a.keyCode,r[e.keyCode]=r[e.key.toLowerCase()]=!0,e.keyPressed(a),1==a.key.length&&e.keyTyped(a))},e._onkeyup=a=>{t.keyIsPressed=!1,t.key=a.key,t.keyCode=a.keyCode,r[e.keyCode]=r[e.key.toLowerCase()]=!1,e.keyReleased(a)},e.keyIsDown=e=>!!r["string"==typeof e?e.toLowerCase():e],e._ontouchstart=r=>{e._startAudio(),t.touches=[...r.touches].map(o),e._isTouchAware||(t.mouseX=e.touches[0].x,t.mouseY=e.touches[0].y,t.mouseIsPressed=!0,t.mouseButton=e.LEFT,e.mousePressed(r)||r.preventDefault()),e.touchStarted(r)||r.preventDefault()},e._ontouchmove=r=>{t.touches=[...r.touches].map(o),e._isTouchAware||(t.mouseX=e.touches[0].x,t.mouseY=e.touches[0].y,e.mouseDragged(r)||r.preventDefault()),e.touchMoved(r)||r.preventDefault()},e._ontouchend=r=>{t.touches=[...r.touches].map(o),e._isTouchAware||e.touches.length||(t.mouseIsPressed=!1,e.mouseReleased(r)||r.preventDefault()),e.touchEnded(r)||r.preventDefault()},n&&(n.addEventListener("mousedown",(t=>e._onmousedown(t))),n.addEventListener("mouseup",(t=>e._onmouseup(t))),n.addEventListener("wheel",(t=>e._onwheel(t))),n.addEventListener("click",(t=>e._onclick(t))),n.addEventListener("touchstart",(t=>e._ontouchstart(t))),n.addEventListener("touchmove",(t=>e._ontouchmove(t))),n.addEventListener("touchcancel",(t=>e._ontouchend(t))),n.addEventListener("touchend",(t=>e._ontouchend(t)))),window){let t=window.addEventListener;t("mousemove",(t=>e._onmousemove(t)),!1),t("keydown",(t=>e._onkeydown(t)),!1),t("keyup",(t=>e._onkeyup(t)),!1)}},Q5.modules.math=(e,t)=>{e.RADIANS=0,e.DEGREES=1,e.PI=Math.PI,e.HALF_PI=Math.PI/2,e.QUARTER_PI=Math.PI/4,e.abs=Math.abs,e.ceil=Math.ceil,e.exp=Math.exp,e.floor=e.int=Math.floor,e.loge=Math.log,e.mag=Math.hypot,e.max=Math.max,e.min=Math.min,e.round=Math.round,e.pow=Math.pow,e.sqrt=Math.sqrt,e.SHR3=1,e.LCG=2;let r=e._angleMode=0;e.angleMode=t=>(r=e._angleMode=0==t||"radians"==t?0:1,r?"degrees":"radians");let a=e._DEGTORAD=Math.PI/180,n=e._RADTODEG=180/Math.PI;function o(){let e,t,r=4294967295;return{setSeed(a){e=t=(a??Math.random()*r)>>>0},getSeed:()=>t,rand:()=>(e^=e<<17,e^=e>>13,e^=e<<5,(e>>>0)/r)}}e.degrees=t=>t*e._RADTODEG,e.radians=t=>t*e._DEGTORAD,e.map=Q5.prototype.map=(e,t,r,a,n,o)=>{let i=a+1*(e-t)/(r-t)*(n-a);return o?a<n?Math.min(Math.max(i,a),n):Math.min(Math.max(i,n),a):i},e.dist=function(){let e=arguments;return 4==e.length?Math.hypot(e[0]-e[2],e[1]-e[3]):Math.hypot(e[0]-e[3],e[1]-e[4],e[2]-e[5])},e.lerp=(e,t,r)=>e*(1-r)+t*r,e.constrain=(e,t,r)=>Math.min(Math.max(e,t),r),e.norm=(t,r,a)=>e.map(t,r,a,0,1),e.sq=e=>e*e,e.fract=e=>e-Math.floor(e),e.sin=e=>Math.sin(r?e*a:e),e.cos=e=>Math.cos(r?e*a:e),e.tan=e=>Math.tan(r?e*a:e),e.asin=e=>{let t=Math.asin(e);return r?t*n:t},e.acos=e=>{let t=Math.acos(e);return r?t*n:t},e.atan=e=>{let t=Math.atan(e);return r?t*n:t},e.atan2=(e,t)=>{let a=Math.atan2(e,t);return r?a*n:a};let i=o();i.setSeed(),e.randomSeed=e=>i.setSeed(e),e.random=(e,t)=>void 0===e?i.rand():"number"==typeof e?void 0!==t?i.rand()*(t-e)+e:i.rand()*e:e[Math.trunc(e.length*i.rand())],e.randomGenerator=t=>{t==e.LCG?i=function(){const e=4294967296;let t,r;return{setSeed(a){r=t=(a??Math.random()*e)>>>0},getSeed:()=>t,rand:()=>(r=(1664525*r+1013904223)%e,r/e)}}():t==e.SHR3&&(i=o()),i.setSeed()};var s=new function(){var e,t,r,a=new Array(128),n=new Array(256),o=new Array(128),s=new Array(128),l=new Array(256),d=new Array(256),c=()=>4294967296*i.rand()-2147483648,h=()=>.5+2.328306e-10*(c()|0),u=()=>{for(var t,n,i,l,d=3.44262;;){if(t=r*o[e],0==e){do{i=h(),l=h(),t=.2904764*-Math.log(i),n=-Math.log(l)}while(n+n<t*t);return r>0?d+t:-d-t}if(s[e]+h()*(s[e-1]-s[e])<Math.exp(-.5*t*t))return t;if(r=c(),e=127&r,Math.abs(r)<a[e])return r*o[e]}},p=()=>{for(var r;;){if(0==e)return 7.69711-Math.log(h());if(r=t*l[e],d[e]+h()*(d[e-1]-d[e])<Math.exp(-r))return r;if((t=c())<n[e=255&t])return t*l[e]}};this.SHR3=c,this.UNI=h,this.RNOR=()=>(r=c(),e=127&r,Math.abs(r)<a[e]?r*o[e]:u()),this.REXP=()=>(t=c()>>>0)<a[e=255&t]?t*l[e]:p(),this.zigset=()=>{var e,t,r=2147483648,i=4294967296,c=3.442619855899,h=c,u=.00991256303526217,p=7.697117470131487,f=p,_=.003949659822581572;for(e=u/Math.exp(-.5*c*c),a[0]=Math.floor(c/e*r),a[1]=0,o[0]=e/r,o[127]=c/r,s[0]=1,s[127]=Math.exp(-.5*c*c),t=126;t>=1;t--)c=Math.sqrt(-2*Math.log(u/c+Math.exp(-.5*c*c))),a[t+1]=Math.floor(c/h*r),h=c,s[t]=Math.exp(-.5*c*c),o[t]=c/r;for(e=_/Math.exp(-p),n[0]=Math.floor(p/e*i),n[1]=0,l[0]=e/i,l[255]=p/i,d[0]=1,d[255]=Math.exp(-p),t=254;t>=1;t--)p=-Math.log(_/p+Math.exp(-p)),n[t+1]=Math.floor(p/f*i),f=p,d[t]=Math.exp(-p),l[t]=p/i}};let l;s.hasInit=!1,e.randomGaussian=(e,t)=>(s.hasInit||(s.zigset(),s.hasInit=!0),s.RNOR()*t+e),e.randomExponential=()=>(s.hasInit||(s.zigset(),s.hasInit=!0),s.REXP()),e.PERLIN="perlin",e.SIMPLEX="simplex",e.BLOCKY="blocky",e.Noise=Q5.PerlinNoise,e.noiseMode=e=>{t.Noise=Q5[e[0].toUpperCase()+e.slice(1)+"Noise"],l=null},e.noiseSeed=t=>{l=new e.Noise(t)},e.noise=(t=0,r=0,a=0)=>(l??=new e.Noise,l.noise(t,r,a)),e.noiseDetail=(t,r)=>{l??=new e.Noise,t>0&&(l.octaves=t),r>0&&(l.falloff=r)}},Q5.Noise=class{},Q5.PerlinNoise=class extends Q5.Noise{constructor(e){super(),this.grad3=[[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]],this.octaves=1,this.falloff=.5,this.p=null==e?Array.from({length:256},(()=>Math.floor(256*Math.random()))):this.seedPermutation(e),this.p=this.p.concat(this.p)}seedPermutation(e){let t,r,a=[];for(let e=0;e<256;e++)a[e]=e;for(let n=255;n>0;n--)t=(e=16807*e%2147483647)%(n+1),r=a[n],a[n]=a[t],a[t]=r;return a}dot(e,t,r,a){return e[0]*t+e[1]*r+e[2]*a}mix(e,t,r){return(1-r)*e+r*t}fade(e){return e*e*e*(e*(6*e-15)+10)}noise(e,t,r){let a=this,n=0,o=1,i=1,s=0;for(let l=0;l<a.octaves;l++){const l=255&Math.floor(e*o),d=255&Math.floor(t*o),c=255&Math.floor(r*o),h=e*o-Math.floor(e*o),u=t*o-Math.floor(t*o),p=r*o-Math.floor(r*o),f=a.fade(h),_=a.fade(u),g=a.fade(p),x=a.p[l]+d,m=a.p[x]+c,v=a.p[x+1]+c,y=a.p[l+1]+d,w=a.p[y]+c,b=a.p[y+1]+c,S=a.mix(a.dot(a.grad3[a.p[m]%12],h,u,p),a.dot(a.grad3[a.p[w]%12],h-1,u,p),f),M=a.mix(a.dot(a.grad3[a.p[v]%12],h,u-1,p),a.dot(a.grad3[a.p[b]%12],h-1,u-1,p),f),C=a.mix(a.dot(a.grad3[a.p[m+1]%12],h,u,p-1),a.dot(a.grad3[a.p[w+1]%12],h-1,u,p-1),f),R=a.mix(a.dot(a.grad3[a.p[v+1]%12],h,u-1,p-1),a.dot(a.grad3[a.p[b+1]%12],h-1,u-1,p-1),f),I=a.mix(S,M,_),E=a.mix(C,R,_);n+=a.mix(I,E,g)*i,s+=i,i*=a.falloff,o*=2}return(n/s+1)/2}},Q5.modules.sound=(e,t)=>{e.Sound=Q5.Sound,e.loadSound=(e,r)=>{t._preloadCount++,Q5.aud??=new window.AudioContext;let a=new Q5.Sound(e,r);return a.crossOrigin="Anonymous",a.addEventListener("canplaythrough",(()=>{t._preloadCount--,a.loaded=!0,r&&r(a)})),a},e.getAudioContext=()=>Q5.aud,e.userStartAudio=()=>Q5.aud.resume()},window.Audio&&(Q5.Sound=class extends Audio{constructor(e){super(e);let t=this;t.load(),t.panner=Q5.aud.createStereoPanner(),t.source=Q5.aud.createMediaElementSource(t),t.source.connect(t.panner),t.panner.connect(Q5.aud.destination),Object.defineProperty(t,"pan",{get:()=>t.panner.pan.value,set:e=>t.panner.pan.value=e})}setVolume(e){this.volume=e}setLoop(e){this.loop=e}setPan(e){this.pan=e}isLoaded(){return this.loaded}isPlaying(){return!this.paused}}),Q5.modules.util=(e,t)=>{e._loadFile=(r,a,n)=>{t._preloadCount++;let o={};return fetch(r).then((e=>"json"==n?e.json():e.text())).then((r=>{t._preloadCount--,"csv"==n&&(r=e.CSV.parse(r)),Object.assign(o,r),a&&a(r)})),o},e.loadText=(t,r)=>e._loadFile(t,r,"text"),e.loadJSON=(t,r)=>e._loadFile(t,r,"json"),e.loadCSV=(t,r)=>e._loadFile(t,r,"csv"),e.CSV={},e.CSV.parse=(e,t=",",r="\n")=>{let a=[],n=e.split(r),o=n[0].split(t);for(let e=1;e<n.length;e++){let r={},i=n[e].split(t);o.forEach(((e,t)=>r[e]=JSON.parse(i[t]))),a.push(r)}return a},"object"==typeof localStorage&&(e.storeItem=localStorage.setItem,e.getItem=localStorage.getItem,e.removeItem=localStorage.removeItem,e.clearStorage=localStorage.clear),e.year=()=>(new Date).getFullYear(),e.day=()=>(new Date).getDay(),e.hour=()=>(new Date).getHours(),e.minute=()=>(new Date).getMinutes(),e.second=()=>(new Date).getSeconds()},Q5.modules.vector=e=>{e.createVector=(t,r,a)=>new Q5.Vector(t,r,a,e)},Q5.Vector=class{constructor(e,t,r,a){this.x=e||0,this.y=t||0,this.z=r||0,this._$=a||window,this._cn=null,this._cnsq=null}set(e,t,r){return this.x=e?.x||e||0,this.y=e?.y||t||0,this.z=e?.z||r||0,this}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(e,t,r){return void 0!==e?.x?e:void 0!==t?{x:e,y:t,z:r||0}:{x:e,y:e,z:e}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z,this._cn=Math.sqrt(this._cnsq)}add(){let e=this._arg2v(...arguments);return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}rem(){let e=this._arg2v(...arguments);return this.x%=e.x,this.y%=e.y,this.z%=e.z,this}sub(){let e=this._arg2v(...arguments);return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}mult(){let e=this._arg2v(...arguments);return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}div(){let e=this._arg2v(...arguments);return e.x?this.x/=e.x:this.x=0,e.y?this.y/=e.y:this.y=0,e.z?this.z/=e.z:this.z=0,this}mag(){return this._calcNorm(),this._cn}magSq(){return this._calcNorm(),this._cnsq}dot(){let e=this._arg2v(...arguments);return this.x*e.x+this.y*e.y+this.z*e.z}dist(){let e=this._arg2v(...arguments),t=this.x-e.x,r=this.y-e.y,a=this.z-e.z;return Math.sqrt(t*t+r*r+a*a)}cross(){let e=this._arg2v(...arguments),t=this.y*e.z-this.z*e.y,r=this.z*e.x-this.x*e.z,a=this.x*e.y-this.y*e.x;return this.x=t,this.y=r,this.z=a,this}normalize(){this._calcNorm();let e=this._cn;return 0!=e&&(this.x/=e,this.y/=e,this.z/=e),this._cn=1,this._cnsq=1,this}limit(e){this._calcNorm();let t=this._cn;if(t>e){let r=e/t;this.x*=r,this.y*=r,this.z*=r,this._cn=e,this._cnsq=e*e}return this}setMag(e){this._calcNorm();let t=e/this._cn;return this.x*=t,this.y*=t,this.z*=t,this._cn=e,this._cnsq=e*e,this}heading(){return this._$.atan2(this.y,this.x)}setHeading(e){let t=this.mag();return this.x=t*this._$.cos(e),this.y=t*this._$.sin(e),this}rotate(e){let t=this._$.cos(e),r=this._$.sin(e),a=this.x*t-this.y*r,n=this.x*r+this.y*t;return this.x=a,this.y=n,this}angleBetween(){let e=this._arg2v(...arguments),t=Q5.Vector.cross(this,e);return this._$.atan2(t.mag(),this.dot(e))*Math.sign(t.z||1)}lerp(){let e=[...arguments],t=e.at(-1);if(0==t)return this;let r=this._arg2v(...e.slice(0,-1));return this.x+=(r.x-this.x)*t,this.y+=(r.y-this.y)*t,this.z+=(r.z-this.z)*t,this}slerp(){let e=[...arguments],t=e.at(-1);if(0==t)return this;let r=this._arg2v(...e.slice(0,-1));if(1==t)return this.set(r);let a=this.mag(),n=r.mag();if(0==a||0==n)return this.mult(1-t).add(r.mult(t));let o=Q5.Vector.cross(this,r),i=o.mag(),s=Math.atan2(i,this.dot(r));if(i>0)o.div(i);else{if(s<this._$.HALF_PI)return this.mult(1-t).add(r.mult(t));0==this.z&&0==r.z?o.set(0,0,1):0!=this.x?o.set(this.y,-this.x,0).normalize():o.set(1,0,0)}let l=o.cross(this),d=1-t+t*n/a,c=d*Math.cos(t*s),h=d*Math.sin(t*s);return this.x=this.x*c+l.x*h,this.y=this.y*c+l.y*h,this.z=this.z*c+l.z*h,this}reflect(e){return e.normalize(),this.sub(e.mult(2*this.dot(e)))}array(){return[this.x,this.y,this.z]}equals(e,t){return t??=Number.EPSILON||0,Math.abs(e.x-this.x)<t&&Math.abs(e.y-this.y)<t&&Math.abs(e.z-this.z)<t}fromAngle(e,t){return void 0===t&&(t=1),this._cn=t,this._cnsq=t*t,this.x=t*this._$.cos(e),this.y=t*this._$.sin(e),this.z=0,this}fromAngles(e,t,r){void 0===r&&(r=1),this._cn=r,this._cnsq=r*r;const a=this._$.cos(t),n=this._$.sin(t),o=this._$.cos(e),i=this._$.sin(e);return this.x=r*i*n,this.y=-r*o,this.z=r*i*a,this}random2D(){return this._cn=this._cnsq=1,this.fromAngle(Math.random()*Math.PI*2)}random3D(){return this._cn=this._cnsq=1,this.fromAngles(Math.random()*Math.PI*2,Math.random()*Math.PI*2)}toString(){return`[${this.x}, ${this.y}, ${this.z}]`}},Q5.Vector.add=(e,t)=>e.copy().add(t),Q5.Vector.cross=(e,t)=>e.copy().cross(t),Q5.Vector.dist=(e,t)=>Math.hypot(e.x-t.x,e.y-t.y,e.z-t.z),Q5.Vector.div=(e,t)=>e.copy().div(t),Q5.Vector.dot=(e,t)=>e.copy().dot(t),Q5.Vector.equals=(e,t,r)=>e.equals(t,r),Q5.Vector.lerp=(e,t,r)=>e.copy().lerp(t,r),Q5.Vector.slerp=(e,t,r)=>e.copy().slerp(t,r),Q5.Vector.limit=(e,t)=>e.copy().limit(t),Q5.Vector.heading=e=>this._$.atan2(e.y,e.x),Q5.Vector.magSq=e=>e.x*e.x+e.y*e.y+e.z*e.z,Q5.Vector.mag=e=>Math.sqrt(Q5.Vector.magSq(e)),Q5.Vector.mult=(e,t)=>e.copy().mult(t),Q5.Vector.normalize=e=>e.copy().normalize(),Q5.Vector.rem=(e,t)=>e.copy().rem(t),Q5.Vector.sub=(e,t)=>e.copy().sub(t);for(let e of["fromAngle","fromAngles","random2D","random3D"])Q5.Vector[e]=(t,r,a)=>(new Q5.Vector)[e](t,r,a);Q5.renderers.webgpu={},Q5.renderers.webgpu.canvas=(e,t)=>{let r=e.canvas;r.width=e.width=500,r.height=e.height=500,e.colorMode&&e.colorMode("rgb",1);let a,n,o,i=1,s=8;e._pipelineConfigs=[],e._pipelines=[];let l=e.drawStack=[],d=e.colorStack=new Float32Array(1e6);d.set([0,0,0,1,1,1,1,1]),e._transformLayout=Q5.device.createBindGroupLayout({label:"transformLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",hasDynamicOffset:!1}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage",hasDynamicOffset:!1}}]}),o=Q5.device.createBindGroupLayout({label:"colorsLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage",hasDynamicOffset:!1}}]}),e.bindGroupLayouts=[e._transformLayout,o];let c=Q5.device.createBuffer({size:8,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),h=()=>{n=Q5.device.createTexture({size:[e.canvas.width,e.canvas.height],sampleCount:4,format:"bgra8unorm",usage:GPUTextureUsage.RENDER_ATTACHMENT}).createView()};e._createCanvas=(a,n,o)=>(t.ctx=t.drawingContext=r.getContext("webgpu"),o.format??=navigator.gpu.getPreferredCanvasFormat(),o.device??=Q5.device,e.ctx.configure(o),Q5.device.queue.writeBuffer(c,0,new Float32Array([e.canvas.hw,e.canvas.hh])),h(),r),e._resizeCanvas=(t,r)=>{e._setCanvasSize(t,r),h()},e.pixelDensity=t=>t&&t!=e._pixelDensity?(e._pixelDensity=t,e._setCanvasSize(r.w,r.h),h(),t):e._pixelDensity;let u=(t,r,a,n=1)=>{"string"==typeof t?t=e.color(t):null==a&&(n=r??1,r=a=t),t._q5Color&&(n=t.a,a=t.b,r=t.g,t=t.r);let o=d,l=s;o[l++]=t,o[l++]=r,o[l++]=a,o[l++]=n,s=l,i++};e._fill=e._stroke=0,e._doFill=e._doStroke=!0,e.fill=(t,r,a,n)=>{u(t,r,a,n),e._doFill=e._fillSet=!0,e._fill=i},e.stroke=(t,r,a,n)=>{u(t,r,a,n),e._doStroke=e._strokeSet=!0,e._stroke=i},e.noFill=()=>e._doFill=!1,e.noStroke=()=>e._doStroke=!1,e._strokeWeight=1,e.strokeWeight=t=>e._strokeWeight=Math.abs(t),e.resetMatrix=()=>{e._matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],e._transformIndex=0},e.resetMatrix(),e._matrixDirty=!1;let p=[e._matrix.slice()];e._transformIndexStack=[],e.translate=(t,r,a)=>{(t||r||a)&&(e._matrix[12]+=t,e._matrix[13]-=r,e._matrix[14]+=a||0,e._matrixDirty=!0)},e.rotate=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.cos(t),a=Math.sin(t),n=e._matrix,o=n[0],i=n[1],s=n[4],l=n[5];1!=o||i||s||1!=l?(n[0]=o*r+i*a,n[1]=i*r-o*a,n[4]=s*r+l*a,n[5]=l*r-s*a):(n[0]=r,n[1]=-a,n[4]=a,n[5]=r),e._matrixDirty=!0},e.scale=(t=1,r,a=1)=>{r??=t;let n=e._matrix;n[0]*=t,n[1]*=t,n[2]*=t,n[3]*=t,n[4]*=r,n[5]*=r,n[6]*=r,n[7]*=r,n[8]*=a,n[9]*=a,n[10]*=a,n[11]*=a,e._matrixDirty=!0},e.shearX=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.tan(t),a=e._matrix[0],n=e._matrix[1],o=e._matrix[4],i=e._matrix[5];e._matrix[0]=a+o*r,e._matrix[1]=n+i*r,e._matrixDirty=!0},e.shearY=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.tan(t),a=e._matrix[0],n=e._matrix[1],o=e._matrix[4],i=e._matrix[5];e._matrix[4]=o+a*r,e._matrix[5]=i+n*r,e._matrixDirty=!0},e.applyMatrix=(...t)=>{let r;if(r=1==t.length?t[0]:t,9==r.length)r=[r[0],r[1],0,r[2],r[3],r[4],0,r[5],0,0,1,0,r[6],r[7],0,r[8]];else if(16!=r.length)throw new Error("Matrix must be a 3x3 or 4x4 array.");e._matrix=r.slice(),e._matrixDirty=!0},e._saveMatrix=()=>{p.push(e._matrix.slice()),e._transformIndex=p.length-1,e._matrixDirty=!1},e.pushMatrix=()=>{e._matrixDirty&&e._saveMatrix(),e._transformIndexStack.push(e._transformIndex)},e.popMatrix=()=>{if(!e._transformIndexStack.length)return console.warn("Matrix index stack is empty!");let t=e._transformIndexStack.pop();e._matrix=p[t].slice(),e._transformIndex=t,e._matrixDirty=!1},e.push=()=>{e.pushMatrix(),e.pushStyles()},e.pop=()=>{e.popMatrix(),e.popStyles()},e._calcBox=(e,t,r,a,n)=>{let o,i,s,l,d=r/2,c=a/2;return n&&"corner"!=n?"center"==n?(o=e-d,i=e+d,s=-(t-c),l=-(t+c)):(o=e,i=r,s=-t,l=-a):(o=e,i=e+r,s=-t,l=-(t+a)),[o,i,s,l]};let f=["zero","one","src-alpha","one-minus-src-alpha","dst","dst-alpha","one-minus-dst-alpha","one-minus-src"],_=["add","subtract","reverse-subtract","min","max"];const g={normal:[2,3,0,2,3,0],additive:[1,1,0,1,1,0]};e.blendConfigs={};for(const[t,r]of Object.entries(g))e.blendConfigs[t]={color:{srcFactor:f[r[0]],dstFactor:f[r[1]],operation:_[r[2]]},alpha:{srcFactor:f[r[3]],dstFactor:f[r[4]],operation:_[r[5]]}};e._blendMode="normal",e.blendMode=t=>{if(t!=e._blendMode){"source-over"==t&&(t="normal"),"lighter"==t&&(t="additive"),t=t.toLowerCase().replace(/[ -]/g,"_"),e._blendMode=t;for(let r=0;r<e._pipelines.length;r++)e._pipelineConfigs[r].fragment.targets[0].blend=e.blendConfigs[t],e._pipelines[r]=Q5.device.createRenderPipeline(e._pipelineConfigs[r])}},e.clear=()=>{},e._beginRender=()=>{e.encoder=Q5.device.createCommandEncoder(),a=t.pass=e.encoder.beginRenderPass({label:"q5-webgpu",colorAttachments:[{view:n,resolveTarget:e.ctx.getCurrentTexture().createView(),loadOp:"clear",storeOp:"store"}]})},e._render=()=>{if(p.length>1||!e._transformBindGroup){let t=Q5.device.createBuffer({size:64*p.length,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(p.flat()),t.unmap(),e._transformBindGroup=Q5.device.createBindGroup({layout:e._transformLayout,entries:[{binding:0,resource:{buffer:c}},{binding:1,resource:{buffer:t}}]})}a.setBindGroup(0,e._transformBindGroup);let t=Q5.device.createBuffer({size:4*s,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(d.slice(0,s)),t.unmap(),e._colorsBindGroup=Q5.device.createBindGroup({layout:o,entries:[{binding:0,resource:{buffer:t}}]}),e.pass.setBindGroup(1,e._colorsBindGroup);for(let t of e._hooks.preRender)t();let r=0,n=0,i=0,h=-1;for(let t=0;t<l.length;t+=2){let o=l[t+1];if(h!=l[t]&&(h=l[t],a.setPipeline(e._pipelines[h])),0==h)a.draw(o,1,r),r+=o;else if(1==h)-1!=o&&a.setBindGroup(2,e._textureBindGroups[o]),a.draw(4,1,n),n+=4;else if(2==h){let r=l[t+2];a.setBindGroup(2,e._fonts[r].bindGroup),a.setBindGroup(3,e._textBindGroup),a.draw(4,o,0,i),i+=o,t++}}for(let t of e._hooks.postRender)t()},e._finishRender=()=>{a.end();let r=e.encoder.finish();Q5.device.queue.submit([r]),t.pass=e.encoder=null,e.drawStack.length=0,i=1,s=8,rotation=0,p.length=1,e._transformIndexStack.length=0}},Q5.initWebGPU=async()=>{if(!navigator.gpu)return console.warn("q5 WebGPU not supported on this browser!"),!1;if(!Q5.device){let e=await navigator.gpu.requestAdapter();if(!e)throw new Error("No appropriate GPUAdapter found.");Q5.device=await e.requestDevice()}return!0},Q5.webgpu=async function(e,t){return e&&"global"!=e||(Q5._hasGlobal=!0),await Q5.initWebGPU()?new Q5(e,t,"webgpu"):new Q5(e,t,"webgpu-fallback")},Q5.renderers.webgpu.drawing=(e,t)=>{let r=e.canvas,a=e.drawStack,n=new Float32Array(1e7),o=0,i=Q5.device.createShaderModule({label:"drawingVertexShader",code:"\nstruct VertexInput {\n\t@location(0) pos: vec2f,\n\t@location(1) colorIndex: f32,\n\t@location(2) transformIndex: f32\n}\nstruct VertexOutput {\n\t@builtin(position) position: vec4f,\n\t@location(0) color: vec4f\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@group(1) @binding(0) var<storage> colors : array<vec4f>;\n\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n\tvar vert = vec4f(input.pos, 0.0, 1.0);\n\tvert = transforms[i32(input.transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output: VertexOutput;\n\toutput.position = vert;\n\toutput.color = colors[i32(input.colorIndex)];\n\treturn output;\n}\n"}),s=Q5.device.createShaderModule({label:"drawingFragmentShader",code:"\n@fragment\nfn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {\n\treturn color;\n}\n"}),l=Q5.device.createPipelineLayout({label:"drawingPipelineLayout",bindGroupLayouts:e.bindGroupLayouts});e._pipelineConfigs[0]={label:"drawingPipeline",layout:l,vertex:{module:i,entryPoint:"vertexMain",buffers:[{arrayStride:16,attributes:[{format:"float32x2",offset:0,shaderLocation:0},{format:"float32",offset:8,shaderLocation:1},{format:"float32",offset:12,shaderLocation:2}]}]},fragment:{module:s,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[0]=Q5.device.createRenderPipeline(e._pipelineConfigs[0]);const d=(e,t,r,a)=>{let i=n,s=o;i[s++]=e,i[s++]=t,i[s++]=r,i[s++]=a,o=s},c=(e,t,r,i,s,l,d,c,h,u)=>{let p=n,f=o;p[f++]=e,p[f++]=t,p[f++]=h,p[f++]=u,p[f++]=r,p[f++]=i,p[f++]=h,p[f++]=u,p[f++]=d,p[f++]=c,p[f++]=h,p[f++]=u,p[f++]=s,p[f++]=l,p[f++]=h,p[f++]=u,o=f,a.push(0,4)},h=(t,r,i,s,l,d,c)=>{r=-r;let h=0,u=e.TAU/l,p=n,f=o;for(let e=0;e<=l;e++){p[f++]=t,p[f++]=r,p[f++]=d,p[f++]=c;let e=t+i*Math.cos(h),a=r+s*Math.sin(h);p[f++]=e,p[f++]=a,p[f++]=d,p[f++]=c,h+=u}p[f++]=t,p[f++]=r,p[f++]=d,p[f++]=c,p[f++]=t+i,p[f++]=r,p[f++]=d,p[f++]=c,o=f,a.push(0,2*(l+1)+2)};e.rectMode=t=>e._rectMode=t,e.rect=(t,r,a,n)=>{let o,i,[s,l,d,h]=e._calcBox(t,r,a,n,e._rectMode);if(e._matrixDirty&&e._saveMatrix(),i=e._transformIndex,e._doStroke){o=e._stroke;let t=e._strokeWeight/2;if(e._doFill){let e=d+t,r=h-t,a=s-t,n=l+t;c(a,e,n,e,n,r,a,r,o,i),d-=t,h+=t,s+=t,l-=t}else{let e=s-t,r=l+t,a=d+t,n=h-t,u=s+t,p=l-t,f=d-t,_=h+t;c(e,f,r,f,r,a,e,a,o,i),c(e,n,r,n,r,_,e,_,o,i),c(e,a,u,a,u,n,e,n,o,i),c(p,a,r,a,r,n,p,n,o,i)}}e._doFill&&(o=e._fill,c(s,d,l,d,l,h,s,h,o,i))},e.square=(t,r,a)=>e.rect(t,r,a,a);const u=e=>e<4?6:e<6?8:e<10?10:e<16?12:e<20?14:e<22?16:e<24?18:e<28?20:e<34?22:e<42?24:e<48?26:e<56?28:e<64?30:e<72?32:e<84?34:e<96?36:e<98?38:e<113?40:e<149?44:e<199?48:e<261?52:e<353?56:e<461?60:e<585?64:e<1200?70:e<1800?80:e<2400?90:100;let p;e.ellipseMode=t=>e._ellipseMode=t,e.ellipse=(t,r,a,n)=>{let o=u(Math.max(a,n)),i=Math.max(a,1)/2,s=a==n?i:Math.max(n,1)/2;e._matrixDirty&&e._saveMatrix();let l=e._transformIndex;if(e._doStroke){let a=e._strokeWeight/2;h(t,r,i+a,s+a,o,e._stroke,l),i-=a,s-=a}e._doFill&&h(t,r,i,s,o,e._fill,l)},e.circle=(t,r,a)=>e.ellipse(t,r,a,a),e.point=(t,r)=>{e._matrixDirty&&e._saveMatrix();let a=e._transformIndex,n=e._stroke,o=e._strokeWeight;if(o<2){let[i,s,l,d]=e._calcBox(t,r,o,o,"corner");c(i,l,s,l,s,d,i,d,n,a)}else{let e=u(o);o/=2,h(t,r,o,o,e,n,a)}},e.stokeJoin=t=>{e.log("q5 WebGPU doesn't support changing stroke join style.")},e.line=(t,r,a,n)=>{e._matrixDirty&&e._saveMatrix();let o=e._transformIndex,i=e._stroke,s=e._strokeWeight,l=s/2,d=a-t,p=n-r,f=Math.hypot(d,p),_=-p/f*l,g=d/f*l;if(c(t+_,-r-g,t-_,-r+g,a-_,-n+g,a+_,-n-g,i,o),s>2){let e=u(s);h(t,r,l,l,e,i,o),h(a,n,l,l,e,i,o)}};let f=[],_=[];e.beginShape=()=>{p=0,f=[],_=[]},e.vertex=(t,r)=>{e._matrixDirty&&e._saveMatrix(),f.push(t,-r,e._fill,e._transformIndex),p++},e.curveVertex=(t,r)=>{e._matrixDirty&&e._saveMatrix(),_.push({x:t,y:-r})},e.endShape=t=>{if(_.length>0){let t=[..._];if(t.length<4)for(;t.length<4;)t.unshift(t[0]),t.push(t[t.length-1]);for(let r=0;r<t.length-3;r++){let a=t[r],n=t[r+1],o=t[r+2],i=t[r+3];for(let t=0;t<=1;t+=.1){let r=t*t,s=r*t,l=.5*(2*n.x+(-a.x+o.x)*t+(2*a.x-5*n.x+4*o.x-i.x)*r+(-a.x+3*n.x-3*o.x+i.x)*s),d=.5*(2*n.y+(-a.y+o.y)*t+(2*a.y-5*n.y+4*o.y-i.y)*r+(-a.y+3*n.y-3*o.y+i.y)*s);f.push(l,d,e._fill,e._transformIndex),p++}}}if(p<3)throw new Error("A shape must have at least 3 vertices.");if(t){let e=0,t=4*(p-1),r=f[e],a=f[e+1],n=f[t],o=f[t+1];r===n&&a===o||(f.push(r,a,f[e+2],f[e+3]),p++)}if(e._doFill){for(let e=1;e<p-1;e++){let t=0,r=4*e,a=4*(e+1);d(f[t],f[t+1],f[t+2],f[t+3]),d(f[r],f[r+1],f[r+2],f[r+3]),d(f[a],f[a+1],f[a+2],f[a+3])}a.push(0,3*(p-2))}if(e._doStroke){for(let t=0;t<p-1;t++){let r=4*t,a=4*(t+1);e.line(f[r],-f[r+1],f[a],-f[a+1])}if(t){let t=4*(p-1),r=0;e.line(f[t],-f[t+1],f[r],-f[r+1])}}p=0,f=[],_=[]},e.triangle=(t,r,a,n,o,i)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.endShape(!0)},e.quad=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.vertex(s,l),e.endShape(!0)},e.background=(t,a,n,o)=>{if(e.push(),e.resetMatrix(),e._doStroke=!1,t.src){let a=e._imageMode;e._imageMode="corner",e.image(t,-r.hw,-r.hh,r.w,r.h),e._imageMode=a}else{let i=e._rectMode;e._rectMode="corner",e.fill(t,a,n,o),e.rect(-r.hw,-r.hh,r.w,r.h),e._rectMode=i}e.pop(),e._fillSet||(e._fill=1)},e._hooks.preRender.push((()=>{e.pass.setPipeline(e._pipelines[0]);let t=Q5.device.createBuffer({size:4*o,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(n.slice(0,o)),t.unmap(),e.pass.setVertexBuffer(0,t)})),e._hooks.postRender.push((()=>{o=0}))},Q5.renderers.webgpu.image=(e,t)=>{e._textureBindGroups=[];let r=[],a=Q5.device.createShaderModule({label:"imageVertexShader",code:"\nstruct VertexOutput {\n\t@builtin(position) position: vec4f,\n\t@location(0) texCoord: vec2f\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@vertex\nfn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {\n\tvar vert = vec4f(pos, 0.0, 1.0);\n\tvert = transforms[i32(transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output: VertexOutput;\n\toutput.position = vert;\n\toutput.texCoord = texCoord;\n\treturn output;\n}\n\t"}),n=Q5.device.createShaderModule({label:"imageFragmentShader",code:"\n@group(2) @binding(0) var samp: sampler;\n@group(2) @binding(1) var texture: texture_2d<f32>;\n\n@fragment\nfn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {\n\t// Sample the texture using the interpolated texture coordinate\n\treturn textureSample(texture, samp, texCoord);\n}\n\t"}),o=Q5.device.createBindGroupLayout({label:"textureLayout",entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,sampler:{type:"filtering"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,texture:{viewDimension:"2d",sampleType:"float"}}]});const i=Q5.device.createPipelineLayout({label:"imagePipelineLayout",bindGroupLayouts:[...e.bindGroupLayouts,o]});let s;e._pipelineConfigs[1]={label:"imagePipeline",layout:i,vertex:{module:a,entryPoint:"vertexMain",buffers:[{arrayStride:0,attributes:[]},{arrayStride:20,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"},{shaderLocation:2,offset:16,format:"float32"}]}]},fragment:{module:n,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[1]=Q5.device.createRenderPipeline(e._pipelineConfigs[1]);let l=e=>{s=Q5.device.createSampler({magFilter:e,minFilter:e})};l("linear"),e.smooth=()=>{l("linear")},e.noSmooth=()=>{l("nearest")};e._textures=[];let d=0;e._createTexture=t=>{t.canvas&&(t=t.canvas);let r=[t.width,t.height,1],a=Q5.device.createTexture({size:r,format:"bgra8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:t},{texture:a,colorSpace:e.canvas.colorSpace},r),e._textures[d]=a,t.textureIndex=d;const n=Q5.device.createBindGroup({layout:o,entries:[{binding:0,resource:s},{binding:1,resource:a.createView()}]});e._textureBindGroups[d]=n,d=(d+1)%12e3,e._textures[d]&&(e._textures[d].destroy(),delete e._textures[d],delete e._textureBindGroups[d])},e.loadImage=(r,a)=>{t._preloadCount++;const n=new Image;return n.crossOrigin="Anonymous",n.onload=()=>{n.defaultWidth=n.width*e._defaultImageScale,n.defaultHeight=n.height*e._defaultImageScale,n.pixelDensity=1,e._createTexture(n),t._preloadCount--,a&&a(n)},n.src=r,n},e.imageMode=t=>e._imageMode=t,e.image=(t,a,n,o,i,s=0,l=0,d,c)=>{if(t.canvas&&(t=t.canvas),null==t.textureIndex)return;e._matrixDirty&&e._saveMatrix();let h=e._transformIndex,u=t.width,p=t.height;o??=t.defaultWidth,i??=t.defaultHeight,d??=u,c??=p;let f=t.pixelDensity||1;o*=f,i*=f;let[_,g,x,m]=e._calcBox(a,n,o,i,e._imageMode),v=s/u,y=l/p,w=(s+d)/u,b=(l+c)/p;r.push(_,x,v,y,h,g,x,w,y,h,_,m,v,b,h,g,m,w,b,h),e.drawStack.push(1,t.textureIndex)},e._hooks.preRender.push((()=>{if(!e._textureBindGroups.length)return;e.pass.setPipeline(e._pipelines[1]);const t=Q5.device.createBuffer({size:4*r.length,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(r),t.unmap(),e.pass.setVertexBuffer(1,t)})),e._hooks.postRender.push((()=>{r.length=0}))},Q5.THRESHOLD=1,Q5.GRAY=2,Q5.OPAQUE=3,Q5.INVERT=4,Q5.POSTERIZE=5,Q5.DILATE=6,Q5.ERODE=7,Q5.BLUR=8,Q5.renderers.webgpu.text=(e,t)=>{let r=Q5.device.createShaderModule({label:"MSDF text shader",code:"\n// Positions for simple quad geometry\nconst pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));\n\nstruct VertexInput {\n\t@builtin(vertex_index) vertex : u32,\n\t@builtin(instance_index) instance : u32\n}\nstruct VertexOutput {\n\t@builtin(position) position : vec4f,\n\t@location(0) texCoord : vec2f,\n\t@location(1) fillColor : vec4f\n}\nstruct Char {\n\ttexOffset: vec2f,\n\ttexExtent: vec2f,\n\tsize: vec2f,\n\toffset: vec2f,\n}\nstruct Text {\n\tpos: vec2f,\n\tscale: f32,\n\ttransformIndex: f32,\n\tfillIndex: f32,\n\tstrokeIndex: f32\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@group(1) @binding(0) var<storage> colors : array<vec4f>;\n\n@group(2) @binding(0) var fontTexture: texture_2d<f32>;\n@group(2) @binding(1) var fontSampler: sampler;\n@group(2) @binding(2) var<storage> fontChars: array<Char>;\n\n@group(3) @binding(0) var<storage> textChars: array<vec4f>;\n@group(3) @binding(1) var<storage> textMetadata: array<Text>;\n\n@vertex\nfn vertexMain(input : VertexInput) -> VertexOutput {\n\tlet char = textChars[input.instance];\n\n\tlet text = textMetadata[i32(char.w)];\n\n\tlet fontChar = fontChars[i32(char.z)];\n\n\tlet charPos = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;\n\n\tvar vert = vec4f(charPos, 0.0, 1.0);\n\tvert = transforms[i32(text.transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output : VertexOutput;\n\toutput.position = vert;\n\toutput.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;\n\toutput.fillColor = colors[i32(text.fillIndex)];\n\treturn output;\n}\n\nfn sampleMsdf(texCoord: vec2f) -> f32 {\n\tlet c = textureSample(fontTexture, fontSampler, texCoord);\n\treturn max(min(c.r, c.g), min(max(c.r, c.g), c.b));\n}\n\n@fragment\nfn fragmentMain(input : VertexOutput) -> @location(0) vec4f {\n\t// pxRange (AKA distanceRange) comes from the msdfgen tool,\n\t// uses the default which is 4.\n\tlet pxRange = 4.0;\n\tlet sz = vec2f(textureDimensions(fontTexture, 0));\n\tlet dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));\n\tlet dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));\n\tlet toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);\n\tlet sigDist = sampleMsdf(input.texCoord) - 0.5;\n\tlet pxDist = sigDist * toPixels;\n\tlet edgeWidth = 0.5;\n\tlet alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);\n\tif (alpha < 0.001) {\n\t\tdiscard;\n\t}\n\treturn vec4f(input.fillColor.rgb, input.fillColor.a * alpha);\n}\n"}),a=Q5.device.createBindGroupLayout({label:"MSDF text group layout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}},{binding:1,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]}),n=Q5.device.createSampler({minFilter:"linear",magFilter:"linear",mipmapFilter:"linear",maxAnisotropy:16}),o=Q5.device.createBindGroupLayout({label:"MSDF font group layout",entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{}},{binding:2,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}}]}),i=Q5.device.createPipelineLayout({bindGroupLayouts:[...e.bindGroupLayouts,o,a]});e._pipelineConfigs[2]={label:"msdf font pipeline",layout:i,vertex:{module:r,entryPoint:"vertexMain"},fragment:{module:r,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[2]=Q5.device.createRenderPipeline(e._pipelineConfigs[2]);class s{constructor(e,t,r,a){this.bindGroup=e,this.lineHeight=t,this.chars=r,this.kernings=a;let n=Object.values(r);this.charCount=n.length,this.defaultChar=n[0]}getChar(e){return this.chars[e]??this.defaultChar}getXAdvance(e,t=-1){let r=this.getChar(e);if(t>=0){let a=this.kernings.get(e);if(a)return r.xadvance+(a.get(t)??0)}return r.xadvance}}e._fonts=[];let l={},d=e.createGraphics(1,1);d.colorMode(e.RGB,1),e.loadFont=(r,a)=>{if("json"!=r.slice(r.lastIndexOf(".")+1))return d.loadFont(r,a);let i=r.slice(r.lastIndexOf("/")+1,r.lastIndexOf("-"));return(async(r,a,i)=>{t._preloadCount++;let d=await fetch(r);if(404==d.status)return t._preloadCount--,"";let c=await d.json(),h=r.lastIndexOf("/"),u=-1!=h?r.substring(0,h+1):"";d=await fetch(u+c.pages[0]);let p=await createImageBitmap(await d.blob()),f=[p.width,p.height,1],_=Q5.device.createTexture({label:`MSDF ${a}`,size:f,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:p},{texture:_},f),"string"==typeof c.chars&&(c.chars=e.CSV.parse(c.chars," "),c.kernings=e.CSV.parse(c.kernings," "));let g=c.chars.length,x=Q5.device.createBuffer({size:32*g,usage:GPUBufferUsage.STORAGE,mappedAtCreation:!0}),m=new Float32Array(x.getMappedRange()),v=1/c.common.scaleW,y=1/c.common.scaleH,w={},b=0;for(let[e,t]of c.chars.entries())w[t.id]=t,w[t.id].charIndex=e,m[b]=t.x*v,m[b+1]=t.y*y,m[b+2]=t.width*v,m[b+3]=t.height*y,m[b+4]=t.width,m[b+5]=t.height,m[b+6]=t.xoffset,m[b+7]=-t.yoffset,b+=8;x.unmap();let S=Q5.device.createBindGroup({label:"msdf font bind group",layout:o,entries:[{binding:0,resource:_.createView()},{binding:1,resource:n},{binding:2,resource:{buffer:x}}]}),M=new Map;if(c.kernings)for(let e of c.kernings){let t=M.get(e.first);t||(t=new Map,M.set(e.first,t)),t.set(e.second,e.amount)}e._font=new s(S,c.common.lineHeight,w,M),e._font.index=e._fonts.length,e._fonts.push(e._font),l[a]=e._font,t._preloadCount--,i&&i(a)})(r,i,a),i},e._textSize=18,e._textAlign="left",e._textBaseline="alphabetic";let c=!1,h=22.5,u=4.5,p=1.25;e.textFont=t=>{e._font=l[t]},e.textSize=t=>{e._textSize=t,c||(h=t*p,u=h-t)},e.textLeading=t=>{e._font.lineHeight=h=t,u=h-e._textSize,p=h/e._textSize,c=!0},e.textAlign=(t,r)=>{e._textAlign=t,r&&(e._textBaseline=r)},e._charStack=[],e._textStack=[];let f,_=(e,t,r)=>{let a=0,n=0,o=0,i=0,s=0,l=[],d=t.charCodeAt(0);for(let c=0;c<t.length;++c){let h=d;switch(d=c<t.length-1?t.charCodeAt(c+1):-1,h){case 10:l.push(n),i++,a=Math.max(a,n),n=0,o-=e.lineHeight*p;break;case 13:break;case 32:n+=e.getXAdvance(h);break;case 9:n+=2*e.getXAdvance(h);break;default:r&&r(n,o,i,e.getChar(h)),n+=e.getXAdvance(h,d),s++}}return l.push(n),a=Math.max(a,n),{width:a,height:l.length*e.lineHeight*p,lineWidths:l,printedCharCount:s}};e.text=(t,r,a,n,o)=>{if(!e._font)return void(navigator.onLine&&!f&&(f=!0,e.loadFont("https://q5js.org/fonts/YaHei-msdf.json")));if(t.length>n){let e=[],r=0;for(;r<t.length;){let a=r+n;if(a>=t.length){e.push(t.slice(r));break}let o=t.lastIndexOf(" ",a);(-1==o||o<r)&&(o=a),e.push(t.slice(r,o)),r=o+1}t=e.join("\n")}let i;for(let e=0;e<t.length;e++){switch(t[e]){case"\n":i=!0;case"\r":case"\t":case" ":0}}let s,l=[],d=e._textAlign,c=e._textBaseline,u=e._textStack.length,p=0;if("left"!=d||i){s=_(e._font,t);let r=0;"alphabetic"==c?a-=e._textSize:"center"==c?r=.5*s.height:"bottom"==c&&(r=s.height),_(e._font,t,((e,t,a,n)=>{let o=0;"center"==d?o=-.5*s.width- -.5*(s.width-s.lineWidths[a]):"right"==d&&(o=s.width-s.lineWidths[a]),l[p]=e+o,l[p+1]=t+r,l[p+2]=n.charIndex,l[p+3]=u,p+=4}))}else s=_(e._font,t,((e,t,r,a)=>{l[p]=e,l[p+1]=t,l[p+2]=a.charIndex,l[p+3]=u,p+=4})),"alphabetic"==c?a-=e._textSize:"center"==c?a-=.5*e._textSize:"bottom"==c&&(a-=h);e._charStack.push(l);let g=[];e._matrixDirty&&e._saveMatrix(),g[0]=r,g[1]=-a,g[2]=e._textSize/44,g[3]=e._transformIndex,g[4]=e._fillSet?e._fill:0,g[5]=e._stroke,e._textStack.push(g),e.drawStack.push(2,s.printedCharCount,e._font.index)},e.textWidth=t=>e._font?_(e._font,t).width:0,e.createTextImage=(t,r,a)=>{if(d.textSize(e._textSize),e._doFill){let t=4*e._fill;d.fill(colorStack.slice(t,t+4))}if(e._doStroke){let t=4*e._stroke;d.stroke(colorStack.slice(t,t+4))}let n=d.createTextImage(t,r,a);if(null==n.canvas.textureIndex)e._createTexture(n);else if(n.modified){let t=n.canvas,r=[t.width,t.height,1],a=e._textures[t.textureIndex];Q5.device.queue.copyExternalImageToTexture({source:t},{texture:a,colorSpace:e.canvas.colorSpace},r),n.modified=!1}return n},e.textImage=(t,r,a)=>{"string"==typeof t&&(t=e.createTextImage(t));let n=e._imageMode;e._imageMode="corner";let o=e._textAlign;"center"==o?r-=t.canvas.hw:"right"==o&&(r-=t.width);let i=e._textBaseline;"alphabetic"==i?a-=t._leading:"center"==i?a-=t._middle:"bottom"==i?a-=t._bottom:"top"==i&&(a-=t._top),e.image(t,r,a),e._imageMode=n},e._hooks.preRender.push((()=>{if(!e._charStack.length)return;let t=0;for(let r of e._charStack)t+=4*r.length;let r=Q5.device.createBuffer({size:t,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(r.getMappedRange()).set(e._charStack.flat()),r.unmap();let n=6*e._textStack.length*4,o=Q5.device.createBuffer({label:"textBuffer",size:n,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(o.getMappedRange()).set(e._textStack.flat()),o.unmap(),e._textBindGroup=Q5.device.createBindGroup({label:"msdf text bind group",layout:a,entries:[{binding:0,resource:{buffer:r}},{binding:1,resource:{buffer:o}}]})})),e._hooks.postRender.push((()=>{e._charStack.length=0,e._textStack.length=0}))};
8
+ function Q5(e,t,r){let a=this;a._q5=!0,a._parent=t,"webgpu-fallback"==r?(a._webgpuFallback=!0,a._renderer="q2d"):a._renderer=r||Q5.render,a._preloadCount=0;let n,o="auto"==e;if(e??="global","auto"==e){if(!window.setup&&!window.draw)return;e="global"}a._scope=e,"global"==e&&(Q5._hasGlobal=a._isGlobal=!0,n=Q5._nodejs?global:window);let i=new Proxy(a,{set:(e,t,r)=>(a[t]=r,a._isGlobal&&(n[t]=r),!0)});a.canvas=a.ctx=a.drawingContext=null,a.pixels=[];let s=null;a.frameCount=0,a.deltaTime=16,a._targetFrameRate=0,a._targetFrameDuration=16.666666666666668,a._frameRate=a._fps=60,a._loop=!0,a._hooks={postCanvas:[],preRender:[],postRender:[]};let l=0;a.millis=()=>performance.now()-l,a.noCanvas=()=>{a.canvas?.remove&&a.canvas.remove(),a.canvas=0,i.ctx=i.drawingContext=0},window&&(a.windowWidth=window.innerWidth,a.windowHeight=window.innerHeight,a.deviceOrientation=window.screen?.orientation?.type),a._incrementPreload=()=>i._preloadCount++,a._decrementPreload=()=>i._preloadCount--,a._draw=e=>{let t=e||performance.now();if(a._lastFrameTime??=t-a._targetFrameDuration,a._didResize&&(a.windowResized(),a._didResize=!1),a._loop)s=c(a._draw);else if(a.frameCount&&!a._redraw)return;if(s&&a.frameCount){if(t-a._lastFrameTime<a._targetFrameDuration-4)return}i.deltaTime=t-a._lastFrameTime,a._frameRate=1e3/a.deltaTime,i.frameCount++;let r=performance.now();a.resetMatrix(),a._beginRender&&a._beginRender();for(let e of Q5.methods.pre)e.call(a);try{a.draw()}catch(e){throw!Q5.disableFriendlyErrors&&a._askAI&&a._askAI(e),Q5.errorTolerant||a.noLoop(),e}for(let e of Q5.methods.post)e.call(a);a._render&&a._render(),a._finishRender&&a._finishRender(),i.pmouseX=a.mouseX,i.pmouseY=a.mouseY,i.moveX=i.moveY=0,a._lastFrameTime=t;let n=performance.now();a._fps=Math.round(1e3/(n-r))},a.noLoop=()=>{a._loop=!1,s=null},a.loop=()=>{a._loop=!0,a._setupDone&&null==s&&a._draw()},a.isLooping=()=>a._loop,a.redraw=(e=1)=>{a._redraw=!0;for(let t=0;t<e;t++)a._draw();a._redraw=!1},a.remove=()=>{a.noLoop(),a.canvas.remove()},a.frameRate=e=>(e&&(a._targetFrameRate=e,a._targetFrameDuration=1e3/e),a._frameRate),a.getTargetFrameRate=()=>a._targetFrameRate||60,a.getFPS=()=>a._fps,a.Element=function(e){this.elt=e},a._elements=[],a.TWO_PI=a.TAU=2*Math.PI,a.log=a.print=console.log,a.describe=()=>{};for(let e in Q5.modules)Q5.modules[e](a,i);let d=Q5.renderers[a._renderer];for(let e in d)d[e](a,i);for(let e in Q5)"_"!=e[1]&&e[1]==e[1].toUpperCase()&&(a[e]=Q5[e]);if("graphics"==e)return;"global"==e&&(Object.assign(Q5,a),delete Q5.Q5),a._webgpuFallback&&(a.colorMode("rgb",1),a._beginRender=()=>a.translate(a.canvas.hw,a.canvas.hh));for(let e of Q5.methods.init)e.call(a);for(let[e,t]of Object.entries(Q5.prototype))"_"!=e[0]&&"function"==typeof a[e]&&(a[e]=t.bind(a));if("global"==e){let e=Object.getOwnPropertyNames(a);for(let t of e)"_"!=t[0]&&(n[t]=a[t])}"function"==typeof e&&e(a),Q5._instanceCount++;let c=window.requestAnimationFrame||function(e){const t=a._lastFrameTime+a._targetFrameDuration;return setTimeout((()=>{e(t)}),t-performance.now())},h=n||a;a._isTouchAware=h.touchStarted||h.touchMoved||h.mouseReleased,a._isGlobal&&(a.preload=h.preload,a.setup=h.setup,a.draw=h.draw),a.preload??=()=>{},a.setup??=()=>{},a.draw??=()=>{};let u=["mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","mouseWheel","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let e of u)h[e]?a._isGlobal&&(a[e]=t=>{try{return h[e](t)}catch(e){throw a._askAI&&a._askAI(e),e}}):a[e]=()=>{};async function p(){if(a._startDone=!0,a._preloadCount>0)return c(p);l=performance.now(),await a.setup(),a._setupDone=!0,a.frameCount||(null===a.ctx&&a.createCanvas(200,200),a.ctx&&a.resetMatrix(),c(a._draw))}function f(){try{a.preload(),a._startDone||p()}catch(e){throw a._askAI&&a._askAI(e),e}}o?f():setTimeout(f,32)}function createCanvas(e,t,r){if(!Q5._hasGlobal){(new Q5).createCanvas(e,t,r)}}Q5.render="q2d",Q5.renderers={},Q5.modules={},Q5._nodejs="object"==typeof process,Q5._instanceCount=0,Q5._friendlyError=(e,t)=>{Q5.disableFriendlyErrors||console.error(t+": "+e)},Q5._validateParameters=()=>!0,Q5.methods={init:[],pre:[],post:[],remove:[]},Q5.prototype.registerMethod=(e,t)=>Q5.methods[e].push(t),Q5.prototype.registerPreloadMethod=(e,t)=>Q5.prototype[e]=t[e],Q5._nodejs&&(global.p5??=global.Q5=Q5),"object"==typeof window?window.p5??=window.Q5=Q5:global.window=0,Q5.version=Q5.VERSION="2.9","object"==typeof document&&document.addEventListener("DOMContentLoaded",(()=>{Q5._hasGlobal||new Q5("auto")})),Q5.modules.canvas=(e,t)=>{e.CENTER="center",e.LEFT="left",e.RIGHT="right",e.TOP="top",e.BOTTOM="bottom",e.BASELINE="alphabetic",e.NORMAL="normal",e.ITALIC="italic",e.BOLD="bold",e.BOLDITALIC="italic bold",e.ROUND="round",e.SQUARE="butt",e.PROJECT="square",e.MITER="miter",e.BEVEL="bevel",e.CHORD_OPEN=0,e.PIE_OPEN=1,e.PIE=2,e.CHORD=3,e.RADIUS="radius",e.CORNER="corner",e.CORNERS="corners",e.OPEN=0,e.CLOSE=1,e.LANDSCAPE="landscape",e.PORTRAIT="portrait",e.BLEND="source-over",e.REMOVE="destination-out",e.ADD="lighter",e.DARKEST="darken",e.LIGHTEST="lighten",e.DIFFERENCE="difference",e.SUBTRACT="subtract",e.EXCLUSION="exclusion",e.MULTIPLY="multiply",e.SCREEN="screen",e.REPLACE="copy",e.OVERLAY="overlay",e.HARD_LIGHT="hard-light",e.SOFT_LIGHT="soft-light",e.DODGE="color-dodge",e.BURN="color-burn",e.P2D="2d",e.WEBGL="webgl",e._OffscreenCanvas=window.OffscreenCanvas||function(){return document.createElement("canvas")},Q5._nodejs?Q5._createNodeJSCanvas&&(t.canvas=Q5._createNodeJSCanvas(100,100)):"image"!=e._scope&&"graphics"!=e._scope||(t.canvas=new e._OffscreenCanvas(100,100)),e.canvas||("object"==typeof document?(t.canvas=document.createElement("canvas"),e.canvas.id="q5Canvas"+Q5._instanceCount,e.canvas.classList.add("q5Canvas")):e.noCanvas());let r=e.canvas;async function a(e,t,r){if(t=t||"untitled","jpg"==(r=r||"png")||"png"==r||"webp"==r)if(e instanceof OffscreenCanvas){const t=await e.convertToBlob({type:"image/"+r});e=await new Promise((e=>{const r=new FileReader;r.onloadend=()=>e(r.result),r.readAsDataURL(t)}))}else e=e.toDataURL("image/"+r);else{let t="text/plain";"json"==r&&("string"!=typeof e&&(e=JSON.stringify(e)),t="text/json"),e=new Blob([e],{type:t}),e=URL.createObjectURL(e)}let a=document.createElement("a");a.href=e,a.download=t+"."+r,a.click(),URL.revokeObjectURL(a.href)}if(r.width=e.width=100,r.height=e.height=100,e._pixelDensity=1,e.displayDensity=()=>window.devicePixelRatio||1,"image"!=e._scope&&(r.renderer=e._renderer,r[e._renderer]=!0,e._pixelDensity=Math.ceil(e.displayDensity())),e._adjustDisplay=()=>{r.style&&(r.style.width=r.w+"px",r.style.height=r.h+"px")},e.createCanvas=function(t,a,n){n??=arguments[3];let o=Object.assign({},Q5.canvasOptions);if("object"==typeof n&&Object.assign(o,n),"image"!=e._scope)if("graphics"==e._scope)e._pixelDensity=this._pixelDensity;else if(window.IntersectionObserver){let t=!1;new IntersectionObserver((a=>{r.visible=a[0].isIntersecting,t||(e._wasLooping=e._loop,t=!0),r.visible?e._wasLooping&&!e._loop&&e.loop():(e._wasLooping=e._loop,e.noLoop())})).observe(r)}e._setCanvasSize(t,a),Object.assign(r,o);let i=e._createCanvas(r.w,r.h,o);if(e._hooks)for(let t of e._hooks.postCanvas)t();return e._beginRender&&e._beginRender(),i},e.createGraphics=function(t,r,a){let n=new Q5("graphics");return a??={},a.alpha??=!0,a.colorSpace??=e.canvas.colorSpace,n.createCanvas.call(e,t,r,a),n.defaultWidth=t,n.defaultHeight=r,n},e.save=(t,r,n)=>{if((!t||"string"==typeof t&&(!r||!n&&r.length<5))&&(n=r,r=t,t=e.canvas),n)return a(t,r,n);r?a(t,(r=r.split("."))[0],r.at(-1)):a(t)},e._setCanvasSize=(a,n)=>{a??=window.innerWidth,n??=window.innerHeight,e.defaultWidth=r.w=a=Math.ceil(a),e.defaultHeight=r.h=n=Math.ceil(n),r.hw=a/2,r.hh=n/2,r.width=Math.ceil(a*e._pixelDensity),r.height=Math.ceil(n*e._pixelDensity),e._da?e.flexibleCanvas(e._dau):(t.width=a,t.height=n),e.displayMode&&!r.displayMode?e.displayMode():e._adjustDisplay()},e._setImageSize=(a,n)=>{t.width=r.w=a,t.height=r.h=n,r.hw=a/2,r.hh=n/2,r.width=Math.ceil(a*e._pixelDensity),r.height=Math.ceil(n*e._pixelDensity)},e.defaultImageScale=t=>t?e._defaultImageScale=t:e._defaultImageScale,e.defaultImageScale(.5),"image"!=e._scope){if(r&&"graphics"!=e._scope){function n(){let t=e._parent;t??=document.getElementsByTagName("main")[0],t||(t=document.createElement("main"),document.body.append(t)),r.parent(t)}r.parent=t=>{function a(){e.frameCount>1&&(e._didResize=!0,e._adjustDisplay())}r.parentElement&&r.parentElement.removeChild(r),"string"==typeof t&&(t=document.getElementById(t)),t.append(r),"function"==typeof ResizeObserver?(e._ro&&e._ro.disconnect(),e._ro=new ResizeObserver(a),e._ro.observe(t)):e.frameCount||window.addEventListener("resize",a)},document.body?n():document.addEventListener("DOMContentLoaded",n)}e.resizeCanvas=(t,a)=>{if(!e.ctx)return e.createCanvas(t,a);t==r.w&&a==r.h||e._resizeCanvas(t,a)},e.canvas.resize=e.resizeCanvas,e.canvas.save=e.saveCanvas=e.save,e.pixelDensity=t=>t&&t!=e._pixelDensity?(e._pixelDensity=t,e._setCanvasSize(r.w,r.h),t):e._pixelDensity,e.flexibleCanvas=(a=400)=>{a?(e._da=r.width/(a*e._pixelDensity),t.width=e._dau=a,t.height=r.h/r.w*a):e._da=0},e._styleNames=["_fill","_stroke","_doStroke","_doFill","_strokeSet","_fillSet","_tint","_imageMode","_rectMode","_ellipseMode","_textSize","_textAlign","_textBaseline"],e._styles=[],e.pushStyles=()=>{let t={};for(let r of e._styleNames)t[r]=e[r];e._styles.push(t)},e.popStyles=()=>{let t=e._styles.pop();for(let r of e._styleNames)e[r]=t[r]},window&&"graphics"!=e._scope&&window.addEventListener("resize",(()=>{e._didResize=!0,t.windowWidth=window.innerWidth,t.windowHeight=window.innerHeight,t.deviceOrientation=window.screen?.orientation?.type}))}},Q5.canvasOptions={alpha:!1,colorSpace:"display-p3"},window.matchMedia&&matchMedia("(dynamic-range: high) and (color-gamut: p3)").matches?Q5.supportsHDR=!0:Q5.canvasOptions.colorSpace="srgb",Q5.renderers.q2d={},Q5.renderers.q2d.canvas=(e,t)=>{let r=e.canvas;e.colorMode&&e.colorMode("rgb","integer"),e._createCanvas=function(a,n,o){return t.ctx=t.drawingContext=r.getContext("2d",o),"image"!=e._scope&&(e.ctx.fillStyle=e._fill="white",e.ctx.strokeStyle=e._stroke="black",e.ctx.lineCap="round",e.ctx.lineJoin="miter",e.ctx.textAlign="left"),e.ctx.scale(e._pixelDensity,e._pixelDensity),e.ctx.save(),r},e.clear=()=>{e.ctx.save(),e.ctx.resetTransform(),e.ctx.clearRect(0,0,e.canvas.width,e.canvas.height),e.ctx.restore()},"image"!=e._scope&&(e._resizeCanvas=(t,a)=>{let n,o={};for(let t in e.ctx)"function"!=typeof e.ctx[t]&&(o[t]=e.ctx[t]);if(delete o.canvas,e.frameCount>1){n=new e._OffscreenCanvas(r.width,r.height),n.w=r.w,n.h=r.h,n.getContext("2d").drawImage(r,0,0)}e._setCanvasSize(t,a);for(let t in o)e.ctx[t]=o[t];e.scale(e._pixelDensity),n&&e.ctx.drawImage(n,0,0,n.w,n.h)},e.fill=function(t){if(e._doFill=e._fillSet=!0,Q5.Color&&(t._q5Color||("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),t.a<=0))return e._doFill=!1;e.ctx.fillStyle=e._fill=t.toString()},e.stroke=function(t){if(e._doStroke=e._strokeSet=!0,Q5.Color&&(t._q5Color||("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),t.a<=0))return e._doStroke=!1;e.ctx.strokeStyle=e._stroke=t.toString()},e.strokeWeight=t=>{t||(e._doStroke=!1),e._da&&(t*=e._da),e.ctx.lineWidth=e._strokeWeight=t||1e-4},e.noFill=()=>e._doFill=!1,e.noStroke=()=>e._doStroke=!1,e.opacity=t=>e.ctx.globalAlpha=t,e.translate=(t,r)=>{e._da&&(t*=e._da,r*=e._da),e.ctx.translate(t,r)},e.rotate=t=>{e._angleMode&&(t=e.radians(t)),e.ctx.rotate(t)},e.scale=(t,r)=>{t.x&&(r=t.y,t=t.x),r??=t,e.ctx.scale(t,r)},e.applyMatrix=(t,r,a,n,o,i)=>e.ctx.transform(t,r,a,n,o,i),e.shearX=t=>e.ctx.transform(1,0,e.tan(t),1,0,0),e.shearY=t=>e.ctx.transform(1,e.tan(t),0,1,0,0),e.resetMatrix=()=>{e.ctx&&(e.ctx.resetTransform(),e.scale(e._pixelDensity))},e.pushMatrix=()=>e.ctx.save(),e.popMatrix=()=>e.ctx.restore(),e.popStyles=()=>{let t=e._styles.pop();for(let r of e._styleNames)e[r]=t[r];e.ctx.fillStyle=e._fill,e.ctx.strokeStyle=e._stroke,e.ctx.lineWidth=e._strokeWeight},e.push=()=>{e.ctx.save(),e.pushStyles()},e.pop=()=>{e.ctx.restore(),e.popStyles()},e.createCapture=e=>{var t=document.createElement("video");return t.playsinline="playsinline",t.autoplay="autoplay",navigator.mediaDevices.getUserMedia(e).then((e=>{t.srcObject=e})),t.style.position="absolute",t.style.opacity=1e-5,t.style.zIndex=-1e3,document.body.append(t),t})},Q5.renderers.q2d.drawing=e=>{e._doStroke=!0,e._doFill=!0,e._strokeSet=!1,e._fillSet=!1,e._ellipseMode=e.CENTER,e._rectMode=e.CORNER,e._curveDetail=20,e._curveAlpha=0;let t=!0,r=[];function a(){e._doFill&&e.ctx.fill(),e._doStroke&&e.ctx.stroke()}function n(t,r,a,n,o,i,s){e._angleMode&&(o=e.radians(o),i=e.radians(i));let l=e.TAU;if((o%=l)<0&&(o+=l),(i%=l)<0&&(i+=l),o>i&&(i+=l),o==i)return e.ellipse(t,r,a,n);if(a/=2,n/=2,e._doFill||s!=e.PIE_OPEN||(s=e.CHORD_OPEN),e.ctx.beginPath(),e.ctx.ellipse(t,r,a,n,0,o,i),s!=e.PIE&&s!=e.PIE_OPEN||e.ctx.lineTo(t,r),e._doFill&&e.ctx.fill(),e._doStroke){if(s!=e.PIE&&s!=e.CHORD||e.ctx.closePath(),s!=e.PIE_OPEN)return e.ctx.stroke();e.ctx.beginPath(),e.ctx.ellipse(t,r,a,n,0,o,i),e.ctx.stroke()}}function o(t,r,n,o){e.ctx.beginPath(),e.ctx.ellipse(t,r,n/2,o/2,0,0,e.TAU),a()}function i(t,r,n,o,s,l,d,c){return void 0===s?function(t,r,n,o){e._da&&(t*=e._da,r*=e._da,n*=e._da,o*=e._da),e.ctx.beginPath(),e.ctx.rect(t,r,n,o),a()}(t,r,n,o):void 0===l?i(t,r,n,o,s,s,s,s):(e._da&&(t*=e._da,r*=e._da,n*=e._da,o*=e._da,s*=e._da,l*=e._da,c*=e._da,d*=e._da),e.ctx.roundRect(t,r,n,o,[s,l,d,c]),void a())}e.blendMode=t=>e.ctx.globalCompositeOperation=t,e.strokeCap=t=>e.ctx.lineCap=t,e.strokeJoin=t=>e.ctx.lineJoin=t,e.ellipseMode=t=>e._ellipseMode=t,e.rectMode=t=>e._rectMode=t,e.curveDetail=t=>e._curveDetail=t,e.curveAlpha=t=>e._curveAlpha=t,e.curveTightness=t=>e._curveAlpha=t,e.background=function(t){e.ctx.save(),e.ctx.resetTransform(),e.ctx.globalAlpha=1,t.canvas?e.image(t,0,0,e.canvas.width,e.canvas.height):(Q5.Color&&!t._q5Color&&("string"!=typeof t?t=e.color(...arguments):e._namedColors[t]&&(t=e.color(...e._namedColors[t]))),e.ctx.fillStyle=t.toString(),e.ctx.fillRect(0,0,e.canvas.width,e.canvas.height)),e.ctx.restore()},e.line=(t,r,a,n)=>{e._doStroke&&(e._da&&(t*=e._da,r*=e._da,a*=e._da,n*=e._da),e.ctx.beginPath(),e.ctx.moveTo(t,r),e.ctx.lineTo(a,n),e.ctx.stroke())},e.arc=(t,r,a,o,i,s,l)=>{if(i==s)return e.ellipse(t,r,a,o);e._da&&(t*=e._da,r*=e._da,a*=e._da,o*=e._da),l??=e.PIE_OPEN,e._ellipseMode==e.CENTER?n(t,r,a,o,i,s,l):e._ellipseMode==e.RADIUS?n(t,r,2*a,2*o,i,s,l):e._ellipseMode==e.CORNER?n(t+a/2,r+o/2,a,o,i,s,l):e._ellipseMode==e.CORNERS&&n((t+a)/2,(r+o)/2,a-t,o-r,i,s,l)},e.ellipse=(t,r,a,n)=>{n??=a,e._da&&(t*=e._da,r*=e._da,a*=e._da,n*=e._da),e._ellipseMode==e.CENTER?o(t,r,a,n):e._ellipseMode==e.RADIUS?o(t,r,2*a,2*n):e._ellipseMode==e.CORNER?o(t+a/2,r+n/2,a,n):e._ellipseMode==e.CORNERS&&o((t+a)/2,(r+n)/2,a-t,n-r)},e.circle=(t,r,n)=>{e._ellipseMode==e.CENTER?(e._da&&(t*=e._da,r*=e._da,n*=e._da),e.ctx.beginPath(),e.ctx.arc(t,r,n/2,0,e.TAU),a()):e.ellipse(t,r,n,n)},e.point=(t,r)=>{e._doStroke&&(t.x&&(r=t.y,t=t.x),e._da&&(t*=e._da,r*=e._da),e.ctx.beginPath(),e.ctx.moveTo(t,r),e.ctx.lineTo(t,r),e.ctx.stroke())},e.rect=(t,r,a,n=a,o,s,l,d)=>{e._rectMode==e.CENTER?i(t-a/2,r-n/2,a,n,o,s,l,d):e._rectMode==e.RADIUS?i(t-a,r-n,2*a,2*n,o,s,l,d):e._rectMode==e.CORNER?i(t,r,a,n,o,s,l,d):e._rectMode==e.CORNERS&&i(t,r,a-t,n-r,o,s,l,d)},e.square=(t,r,a,n,o,i,s)=>e.rect(t,r,a,a,n,o,i,s),e.beginShape=()=>{r.length=0,e.ctx.beginPath(),t=!0},e.beginContour=()=>{e.ctx.closePath(),r.length=0,t=!0},e.endContour=()=>{r.length=0,t=!0},e.vertex=(a,n)=>{e._da&&(a*=e._da,n*=e._da),r.length=0,t?e.ctx.moveTo(a,n):e.ctx.lineTo(a,n),t=!1},e.bezierVertex=(t,a,n,o,i,s)=>{e._da&&(t*=e._da,a*=e._da,n*=e._da,o*=e._da,i*=e._da,s*=e._da),r.length=0,e.ctx.bezierCurveTo(t,a,n,o,i,s)},e.quadraticVertex=(t,a,n,o)=>{e._da&&(t*=e._da,a*=e._da,n*=e._da,o*=e._da),r.length=0,e.ctx.quadraticCurveTo(t,a,n,o)},e.bezier=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.bezierVertex(a,n,o,i,s,l),e.endShape()},e.triangle=(t,r,a,n,o,i)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.endShape(e.CLOSE)},e.quad=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.vertex(s,l),e.endShape(e.CLOSE)},e.endShape=t=>{r.length=0,t&&e.ctx.closePath(),a()},e.curveVertex=(a,n)=>{if(e._da&&(a*=e._da,n*=e._da),r.push([a,n]),r.length<4)return;let o=r.at(-4),i=r.at(-3),s=r.at(-2),l=r.at(-1),d=i[0]+(s[0]-o[0])/6,c=i[1]+(s[1]-o[1])/6,h=s[0]-(l[0]-i[0])/6,u=s[1]-(l[1]-i[1])/6;t&&(e.ctx.moveTo(i[0],i[1]),t=!1),e.ctx.bezierCurveTo(d,c,h,u,s[0],s[1])},e.curve=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.curveVertex(t,r),e.curveVertex(a,n),e.curveVertex(o,i),e.curveVertex(s,l),e.endShape()},e.curvePoint=(e,t,r,a,n)=>{const o=n*n*n,i=n*n;return e*(-.5*o+i-.5*n)+t*(1.5*o-2.5*i+1)+r*(-1.5*o+2*i+.5*n)+a*(.5*o-.5*i)},e.bezierPoint=(e,t,r,a,n)=>{const o=1-n;return Math.pow(o,3)*e+3*Math.pow(o,2)*n*t+3*o*Math.pow(n,2)*r+Math.pow(n,3)*a},e.curveTangent=(e,t,r,a,n)=>{const o=n*n;return e*(-3*o/2+2*n-.5)+t*(9*o/2-5*n)+r*(-9*o/2+4*n+.5)+a*(3*o/2-n)},e.bezierTangent=(e,t,r,a,n)=>{const o=1-n;return 3*a*Math.pow(n,2)-3*r*Math.pow(n,2)+6*r*o*n-6*t*o*n+3*t*Math.pow(o,2)-3*e*Math.pow(o,2)},e.erase=function(t=255,r=255){e.ctx.save(),e.ctx.globalCompositeOperation="destination-out",e.ctx.fillStyle=`rgba(0, 0, 0, ${t/255})`,e.ctx.strokeStyle=`rgba(0, 0, 0, ${r/255})`},e.noErase=function(){e.ctx.globalCompositeOperation="source-over",e.ctx.restore()},e.inFill=(t,r)=>{const a=e._pixelDensity;return e.ctx.isPointInPath(t*a,r*a)},e.inStroke=(t,r)=>{const a=e._pixelDensity;return e.ctx.isPointInStroke(t*a,r*a)}},Q5.renderers.q2d.image=(e,t)=>{Q5.Image??=class{constructor(e,t,r){let a=this;a._scope="image",a.canvas=a.ctx=a.drawingContext=null,a.pixels=[],Q5.modules.canvas(a,a);let n=Q5.renderers.q2d;for(let e of["canvas","image","soft_filters"])n[e]&&n[e](a,a);a._pixelDensity=r.pixelDensity||1,a.createCanvas(e,t,r),delete a.createCanvas,a._loop=!1}get w(){return this.width}get h(){return this.height}},e.createImage=(t,r,a)=>{a??={},a.alpha??=!0,a.colorSpace??=e.canvas.colorSpace||Q5.canvasOptions.colorSpace;let n=new Q5.Image(t,r,a);return n.defaultWidth=t*e._defaultImageScale,n.defaultHeight=r*e._defaultImageScale,n},e.loadImage=function(r,a,n){if(r.canvas)return r;if("gif"==r.slice(-3).toLowerCase())throw new Error("q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84");t._preloadCount++;let o=[...arguments].at(-1);n="object"==typeof o?o:null;let i=e.createImage(1,1,n),s=i._pixelDensity=n?.pixelDensity||1;function l(r){i.defaultWidth=r.width*e._defaultImageScale,i.defaultHeight=r.height*e._defaultImageScale,i.naturalWidth=r.naturalWidth,i.naturalHeight=r.naturalHeight,i._setImageSize(Math.ceil(i.naturalWidth/s),Math.ceil(i.naturalHeight/s)),i.ctx.drawImage(r,0,0),t._preloadCount--,a&&a(i)}if(Q5._nodejs&&global.CairoCanvas)global.CairoCanvas.loadImage(r).then(l).catch((e=>{throw t._preloadCount--,e}));else{let e=new window.Image;e.src=r,e.crossOrigin="Anonymous",e._pixelDensity=s,e.onload=()=>l(e),e.onerror=e=>{throw t._preloadCount--,e}}return i},e.imageMode=t=>e._imageMode=t,e.image=(t,r,a,n,o,i=0,s=0,l,d)=>{if(!t)return;let c=t?.canvas||t;Q5._createNodeJSCanvas&&(c=c.context.canvas),n??=t.defaultWidth||c.width||t.videoWidth,o??=t.defaultHeight||c.height||t.videoHeight,"center"==e._imageMode&&(r-=.5*n,a-=.5*o),e._da&&(r*=e._da,a*=e._da,n*=e._da,o*=e._da,i*=e._da,s*=e._da,l*=e._da,d*=e._da);let h=t._pixelDensity||1;if(l?l*=h:l=c.width||c.videoWidth,d?d*=h:d=c.height||c.videoHeight,e.ctx.drawImage(c,i*h,s*h,l,d,r,a,n,o),e._tint){e.ctx.shadowBlur=0,e.ctx.shadowOffsetX=0,e.ctx.shadowOffsetY=0;let t=e.ctx.fillStyle;e.ctx.globalCompositeOperation="multiply",e.ctx.fillStyle=e._tint.toString(),e.ctx.fillRect(r,a,n,o),e.ctx.globalCompositeOperation="source-over",e.ctx.fillStyle=t}},e._tint=null;let r=null;e._softFilter=()=>{throw new Error("Load q5-2d-soft-filters.js to use software filters.")},e.filter=(t,r)=>{if(!e.ctx.filter)return e._softFilter(t,r);if("string"==typeof t)f=t;else if(t==Q5.GRAY)f="saturate(0%)";else if(t==Q5.INVERT)f="invert(100%)";else if(t==Q5.BLUR){let t=Math.ceil(r*e._pixelDensity)||1;f=`blur(${t}px)`}else{if(t!=Q5.THRESHOLD)return e._softFilter(t,r);{r??=.5;let e=Math.floor(.5/Math.max(r,1e-5)*100);f=`saturate(0%) brightness(${e}%) contrast(1000000%)`}}e.ctx.filter=f,e.ctx.drawImage(e.canvas,0,0,e.canvas.w,e.canvas.h),e.ctx.filter="none"},"image"==e._scope&&(e.resize=(t,r)=>{let a=e.canvas,n=new e._OffscreenCanvas(a.width,a.height);n.getContext("2d",{colorSpace:a.colorSpace}).drawImage(a,0,0),e._setImageSize(t,r),e.defaultWidth=a.width*e._defaultImageScale,e.defaultHeight=a.height*e._defaultImageScale,e.ctx.clearRect(0,0,a.width,a.height),e.ctx.drawImage(n,0,0,a.width,a.height)}),e._getImageData=(t,r,a,n)=>e.ctx.getImageData(t,r,a,n,{colorSpace:e.canvas.colorSpace}),e.trim=()=>{let t=e._pixelDensity||1,r=e.canvas.width,a=e.canvas.height,n=e._getImageData(0,0,r,a).data,o=r,i=0,s=a,l=0,d=3;for(let e=0;e<a;e++)for(let t=0;t<r;t++)0!==n[d]&&(t<o&&(o=t),t>i&&(i=t),e<s&&(s=e),e>l&&(l=e)),d+=4;return s=Math.floor(s/t),l=Math.floor(l/t),o=Math.floor(o/t),i=Math.floor(i/t),e.get(o,s,i-o+1,l-s+1)},e.mask=t=>{e.ctx.save(),e.ctx.resetTransform();let r=e.ctx.globalCompositeOperation;e.ctx.globalCompositeOperation="destination-in",e.ctx.drawImage(t.canvas,0,0),e.ctx.globalCompositeOperation=r,e.ctx.restore()},e.inset=(t,r,a,n,o,i,s,l)=>{let d=e._pixelDensity||1;e.ctx.drawImage(e.canvas,t*d,r*d,a*d,n*d,o,i,s,l)},e.copy=()=>e.get(),e.get=(t,r,a,n)=>{let o=e._pixelDensity||1;if(void 0!==t&&void 0===a){let a=e._getImageData(t*o,r*o,1,1).data;return[a[0],a[1],a[2],a[3]/255]}t=Math.floor(t||0)*o,r=Math.floor(r||0)*o;let i=a=a||e.width,s=n=n||e.height;a*=o,n*=o;let l=e.createImage(a,n);return l.ctx.drawImage(e.canvas,t,r,a,n,0,0,a,n),l._pixelDensity=o,l.width=i,l.height=s,l},e.set=(t,r,a)=>{if(t=Math.floor(t),r=Math.floor(r),a.canvas){let n=e._tint;return e._tint=null,e.image(a,t,r),void(e._tint=n)}e.pixels.length||e.loadPixels();let n=e._pixelDensity||1;for(let o=0;o<n;o++)for(let i=0;i<n;i++){let s=4*((r*n+o)*e.canvas.width+t*n+i);e.pixels[s]=a.r,e.pixels[s+1]=a.g,e.pixels[s+2]=a.b,e.pixels[s+3]=a.a}},e.loadPixels=()=>{r=e._getImageData(0,0,e.canvas.width,e.canvas.height),t.pixels=r.data},e.updatePixels=()=>{null!=r&&e.ctx.putImageData(r,0,0)},e.smooth=()=>e.ctx.imageSmoothingEnabled=!0,e.noSmooth=()=>e.ctx.imageSmoothingEnabled=!1,"image"!=e._scope&&(e.tint=function(t){e._tint=t._q5Color?t:e.color(...arguments)},e.noTint=()=>e._tint=null)},Q5.THRESHOLD=1,Q5.GRAY=2,Q5.OPAQUE=3,Q5.INVERT=4,Q5.POSTERIZE=5,Q5.DILATE=6,Q5.ERODE=7,Q5.BLUR=8,Q5.renderers.q2d.text=(e,t)=>{e._textAlign="left",e._textBaseline="alphabetic",e._textSize=12;let r="sans-serif",a=!1,n=15,o=3,i="normal",s=!1,l=0,d=[],c=!1,h=!1,u=0,p=12e3,f=e._textCache={};e.loadFont=(e,r)=>{t._preloadCount++;let a=e.split("/").pop().split(".")[0].replace(" ",""),n=new FontFace(a,`url(${e})`);return document.fonts.add(n),n.load().then((()=>{t._preloadCount--,r&&r(a)})),a},e.textFont=e=>{if(!e||e==r)return r;r=e,s=!0,l=-1},e.textSize=t=>{if(null==t||t==e._textSize)return e._textSize;e._da&&(t*=e._da),e._textSize=t,s=!0,l=-1,a||(n=1.25*t,o=n-t)},e.textStyle=e=>{if(!e||e==i)return i;i=e,s=!0,l=-1},e.textLeading=t=>{if(a=!0,null==t||t==n)return n;e._da&&(t*=e._da),n=t,o=t-e._textSize,l=-1},e.textAlign=(t,r)=>{e.ctx.textAlign=e._textAlign=t,r&&(e.ctx.textBaseline=e._textBaseline=r==e.CENTER?"middle":r)},e.textWidth=t=>e.ctx.measureText(t).width,e.textAscent=t=>e.ctx.measureText(t).actualBoundingBoxAscent,e.textDescent=t=>e.ctx.measureText(t).actualBoundingBoxDescent,e.textFill=e.fill,e.textStroke=e.stroke;e.textCache=(e,t)=>(t&&(p=t),void 0!==e&&(c=e),c),e.createTextImage=(t,r,a)=>(h=!0,img=e.text(t,0,0,r,a),h=!1,img);let _=[];e.text=(t,a,g,x,m)=>{if(void 0===t||!e._doFill&&!e._doStroke)return;t=t.toString(),e._da&&(a*=e._da,g*=e._da);let v,y,w,b,S=e.ctx;if(s&&(S.font=`${i} ${e._textSize}px ${r}`,s=!1),(c||h)&&(-1==l&&(()=>{let t=r+e._textSize+i+n,a=5381;for(let e=0;e<t.length;e++)a=33*a^t.charCodeAt(e);l=a>>>0})(),v=f[t],v&&(v=v[l]),v)){if(v._fill==e._fill&&v._stroke==e._stroke&&v._strokeWeight==e._strokeWeight)return h?v:e.textImage(v,a,g);v.clear()}if(-1==t.indexOf("\n")?_[0]=t:_=t.split("\n"),t.length>x){let e=[];for(let t of _){let r=0;for(;r<t.length;){let a=r+x;if(a>=t.length){e.push(t.slice(r));break}let n=t.lastIndexOf(" ",a);(-1===n||n<r)&&(n=a),e.push(t.slice(r,n)),r=n+1}}_=e}if(c||h){if(y=0,w=n*_.length,!v){let r=S.measureText(" "),a=r.fontBoundingBoxAscent,i=r.fontBoundingBoxDescent;m??=w+i,v=e.createImage.call(e,Math.ceil(S.measureText(t).width),Math.ceil(m),{pixelDensity:e._pixelDensity}),v._ascent=a,v._descent=i,v._top=i+o,v._middle=v._top+.5*a,v._bottom=v._top+a,v._leading=n}v._fill=e._fill,v._stroke=e._stroke,v._strokeWeight=e._strokeWeight,v.modified=!0,S=v.ctx,S.font=e.ctx.font,S.fillStyle=e._fill,S.strokeStyle=e._stroke,S.lineWidth=e.ctx.lineWidth}else y=a,w=g;e._fillSet||(b=S.fillStyle,S.fillStyle="black");for(let t of _)if(e._doStroke&&e._strokeSet&&S.strokeText(t,y,w),e._doFill&&S.fillText(t,y,w),w+=n,w>m)break;if(_.length=0,e._fillSet||(S.fillStyle=b),c||h){if(d.push(l),(f[t]??={})[l]=v,u++,u>p){let e=Math.ceil(u/2),t=d.splice(0,e);for(let e in f){e=f[e];for(let r of t)delete e[r]}u-=e}if(h)return v;e.textImage(v,a,g)}},e.textImage=(t,r,a)=>{"string"==typeof t&&(t=e.createTextImage(t));let n=e._imageMode;e._imageMode="corner";let o=e._textAlign;"center"==o?r-=t.canvas.hw:"right"==o&&(r-=t.width);let i=e._textBaseline;"alphabetic"==i?a-=t._leading:"middle"==i?a-=t._middle:"bottom"==i?a-=t._bottom:"top"==i&&(a-=t._top),e.image(t,r,a),e._imageMode=n},e.nf=(e,t,r)=>{let a=e<0,n=(e=Math.abs(e)).toFixed(r).split(".");n[0]=n[0].padStart(t,"0");let o=n.join(".");return a&&(o="-"+o),o}},Q5.modules.ai=e=>{e.askAI=(e="")=>{throw Q5.disableFriendlyErrors=!1,Error("Ask AI ✨ "+e)},e._askAI=async e=>{let t=e.message?.includes("Ask AI ✨"),r=e.stack?.split("\n");if(!e.stack||r.length<=1)return;let a=1,n="(";for(-1==navigator.userAgent.indexOf("Chrome")&&(a=0,n="@");r[a].indexOf("q5")>=0;)a++;let o=r[a].split(n).at(-1);o.startsWith("blob:")&&(o=o.slice(5));let i=o.split(":"),s=parseInt(i.at(-2));t&&s++,i[3]=i[3].split(")")[0];let l=i.slice(0,2).join(":"),d=l.split("/").at(-1);try{let r=(await(await fetch(l)).text()).split("\n"),a=r[s-1].trim(),n="",o=1;for(;n.length<1600&&(s-o>=0&&(n=r[s-o].trim()+"\n"+n),s+o<r.length);)n+=r[s+o].trim()+"\n",o++;let i="https://chatgpt.com/?q=q5.js+"+(t&&e.message.length>10?e.message.slice(10):"Whats+wrong+with+this+line%3F+short+answer")+(t?"":"%0A%0A"+encodeURIComponent(e.name+": "+e.message))+"%0A%0ALine%3A+"+encodeURIComponent(a)+"%0A%0AExcerpt+for+context%3A%0A%0A"+encodeURIComponent(n);if(console.warn("Error in "+d+" on line "+s+":\n\n"+a),console.warn("Ask AI ✨ "+i),t)return window.open(i,"_blank")}catch(e){}}},Q5.modules.color=(e,t)=>{e.RGB=e.RGBA=e._colorMode="rgb",e.OKLCH="oklch",e.colorMode=(r,a)=>{e._colorMode=r;let n="srgb"==e.canvas.colorSpace||"srgb"==r;if(a??=n?"integer":"float",e._colorFormat="float"==a||1==a?1:255,"oklch"==r)t.Color=Q5.ColorOKLCH;else{let r="srgb"==e.canvas.colorSpace;255==e._colorFormat?t.Color=r?Q5.ColorRGBA_8:Q5.ColorRGBA_P3_8:t.Color=r?Q5.ColorRGBA:Q5.ColorRGBA_P3,e._colorMode="rgb"}},e._namedColors={aqua:[0,255,255],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],crimson:[220,20,60],cyan:[0,255,255],darkviolet:[148,0,211],gold:[255,215,0],green:[0,128,0],gray:[128,128,128],grey:[128,128,128],hotpink:[255,105,180],indigo:[75,0,130],khaki:[240,230,140],lightgreen:[144,238,144],lime:[0,255,0],magenta:[255,0,255],navy:[0,0,128],orange:[255,165,0],olive:[128,128,0],peachpuff:[255,218,185],pink:[255,192,203],purple:[128,0,128],red:[255,0,0],skyblue:[135,206,235],tan:[210,180,140],turquoise:[64,224,208],transparent:[0,0,0,0],white:[255,255,255],violet:[238,130,238],yellow:[255,255,0]},e.color=(t,r,a,n)=>{let o=e.Color;if(t._q5Color)return new o(...t.levels);if(null==r){if("string"==typeof t){if("#"==t[0])t.length<=5?(t.length>4&&(n=parseInt(t[4]+t[4],16)),a=parseInt(t[3]+t[3],16),r=parseInt(t[2]+t[2],16),t=parseInt(t[1]+t[1],16)):(t.length>7&&(n=parseInt(t.slice(7,9),16)),a=parseInt(t.slice(5,7),16),r=parseInt(t.slice(3,5),16),t=parseInt(t.slice(1,3),16));else{if(!e._namedColors[t])return console.error("q5 can't parse color: "+t+"\nOnly numeric input, hex, and common named colors are supported."),new o(0,0,0);[t,r,a,n]=e._namedColors[t]}1==e._colorFormat&&(t/=255,r&&(r/=255),a&&(a/=255),n&&(n/=255))}Array.isArray(t)&&([t,r,a,n]=t)}return null==a?new o(t,t,t,r):new o(t,r,a,n)},e.red=e=>e.r,e.green=e=>e.g,e.blue=e=>e.b,e.alpha=e=>e.a,e.lightness=e=>e.l?e.l:100*(.2126*e.r+.7152*e.g+.0722*e.b)/255,e.hue=t=>{if(t.h)return t.h;let r=t.r,a=t.g,n=t.b;255==e._colorFormat&&(r/=255,a/=255,n/=255);let o,i=Math.max(r,a,n),s=Math.min(r,a,n);return o=i==s?0:i==r?60*(a-n)/(i-s):i==a?60*(n-r)/(i-s)+120:60*(r-a)/(i-s)+240,o<0&&(o+=360),o},e.lerpColor=(t,r,a)=>{if(a=Math.max(0,Math.min(1,a)),"rgb"==e._colorMode)return new e.Color(e.lerp(t.r,r.r,a),e.lerp(t.g,r.g,a),e.lerp(t.b,r.b,a),e.lerp(t.a,r.a,a));{let n=r.h-t.h;n>180&&(n-=360),n<-180&&(n+=360);let o=t.h+a*n;return o<0&&(o+=360),o>360&&(o-=360),new e.Color(e.lerp(t.l,r.l,a),e.lerp(t.c,r.c,a),o,e.lerp(t.a,r.a,a))}}},Q5.Color=class{constructor(){this._q5Color=!0}},Q5.ColorOKLCH=class extends Q5.Color{constructor(e,t,r,a){super(),this.l=e,this.c=t,this.h=r,this.a=a??1}toString(){return`oklch(${this.l} ${this.c} ${this.h} / ${this.a})`}},Q5.ColorRGBA=class extends Q5.Color{constructor(e,t,r,a){super(),this.r=e,this.g=t,this.b=r,this.a=a??1}get levels(){return[this.r,this.g,this.b,this.a]}toString(){return`color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`}},Q5.ColorRGBA_P3=class extends Q5.ColorRGBA{toString(){return`color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`}},Q5.ColorRGBA_8=class extends Q5.ColorRGBA{constructor(e,t,r,a){super(e,t,r,a??255)}setRed(e){this.r=e}setGreen(e){this.g=e}setBlue(e){this.b=e}setAlpha(e){this.a=e}get levels(){return[this.r,this.g,this.b,this.a]}toString(){return`rgb(${this.r} ${this.g} ${this.b} / ${this.a/255})`}},Q5.ColorRGBA_P3_8=class extends Q5.ColorRGBA{constructor(e,t,r,a){super(e,t,r,a??255),this._edited=!0}get r(){return this._r}set r(e){this._r=e,this._edited=!0}get g(){return this._g}set g(e){this._g=e,this._edited=!0}get b(){return this._b}set b(e){this._b=e,this._edited=!0}get a(){return this._a}set a(e){this._a=e,this._edited=!0}toString(){if(this._edited){let e=(this._r/255).toFixed(3),t=(this._g/255).toFixed(3),r=(this._b/255).toFixed(3),a=(this._a/255).toFixed(3);this._css=`color(display-p3 ${e} ${t} ${r} / ${a})`,this._edited=!1}return this._css}},Q5.modules.display=e=>{if(!e.canvas||"graphics"==e._scope)return;let t=e.canvas;e.CENTERED="centered",e.FULLSCREEN="fullscreen",e.MAXED="maxed",e.PIXELATED="pixelated",0!=Q5._instanceCount||Q5._nodejs||document.head.insertAdjacentHTML("beforeend","<style>\nhtml, body {\n\tmargin: 0;\n\tpadding: 0;\n}\n.q5Canvas {\n\toutline: none;\n\t-webkit-touch-callout: none;\n\t-webkit-text-size-adjust: none;\n\t-webkit-user-select: none;\n\toverscroll-behavior: none;\n}\n.q5-pixelated {\n\timage-rendering: pixelated;\n\tfont-smooth: never;\n\t-webkit-font-smoothing: none;\n}\n.q5-centered,\n.q5-maxed,\n.q5-fullscreen {\n display: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\nmain.q5-centered,\nmain.q5-maxed,\n.q5-fullscreen {\n\theight: 100vh;\n}\nmain {\n\toverscroll-behavior: none;\n}\n</style>"),e._adjustDisplay=()=>{let r=t.style,a=t.parentElement;r&&a&&t.displayMode&&("pixelated"==t.renderQuality&&(t.classList.add("q5-pixelated"),e.pixelDensity(1),e.defaultImageScale(1),e.noSmooth&&e.noSmooth(),e.textFont&&e.textFont("monospace")),"default"==t.displayMode||"normal"==t.displayMode?(a.classList.remove("q5-centered","q5-maxed","q5-fullscreen"),r.width=t.w*t.displayScale+"px",r.height=t.h*t.displayScale+"px"):(a.classList.add("q5-"+t.displayMode),a=a.getBoundingClientRect(),t.w/t.h>a.width/a.height?("centered"==t.displayMode?(r.width=t.w*t.displayScale+"px",r.maxWidth="100%"):r.width="100%",r.height="auto",r.maxHeight=""):(r.width="auto",r.maxWidth="","centered"==t.displayMode?(r.height=t.h*t.displayScale+"px",r.maxHeight="100%"):r.height="100%")))},e.displayMode=(r="normal",a="smooth",n=1)=>{"string"==typeof n&&(n=parseFloat(n.slice(1))),"center"==r&&(r="centered"),Object.assign(t,{displayMode:r,renderQuality:a,displayScale:n}),e._adjustDisplay()},e.fullscreen=e=>{if(void 0===e)return document.fullscreenElement;e?document.body.requestFullscreen():document.body.exitFullscreen()}},Q5.modules.input=(e,t)=>{if("graphics"==e._scope)return;e.mouseX=0,e.mouseY=0,e.pmouseX=0,e.pmouseY=0,e.touches=[],e.mouseButton="",e.keyIsPressed=!1,e.mouseIsPressed=!1,e.key="",e.keyCode=0,e.UP_ARROW=38,e.DOWN_ARROW=40,e.LEFT_ARROW=37,e.RIGHT_ARROW=39,e.SHIFT=16,e.TAB=9,e.BACKSPACE=8,e.ENTER=e.RETURN=13,e.ALT=e.OPTION=18,e.CONTROL=17,e.DELETE=46,e.ESCAPE=27,e.ARROW="default",e.CROSS="crosshair",e.HAND="pointer",e.MOVE="move",e.TEXT="text";let r={},a=[e.LEFT,e.CENTER,e.RIGHT],n=e.canvas;function o(t){const r=e.canvas.getBoundingClientRect(),a=e.canvas.scrollWidth/e.width||1,n=e.canvas.scrollHeight/e.height||1;return{x:(t.clientX-r.left)/a,y:(t.clientY-r.top)/n,id:t.identifier}}if(e._startAudio=()=>{e.getAudioContext&&"suspended"==e.getAudioContext()?.state&&e.userStartAudio()},e._updateMouse=r=>{if(!r.changedTouches){if(n){let a=n.getBoundingClientRect(),o=n.scrollWidth/e.width||1,i=n.scrollHeight/e.height||1;t.mouseX=(r.clientX-a.left)/o,t.mouseY=(r.clientY-a.top)/i,"webgpu"==n.renderer&&(t.mouseX-=n.hw,t.mouseY-=n.hh)}else t.mouseX=r.clientX,t.mouseY=r.clientY;t.moveX=r.movementX,t.moveY=r.movementY}},e._onmousedown=r=>{e._startAudio(),e._updateMouse(r),t.mouseIsPressed=!0,t.mouseButton=a[r.button],e.mousePressed(r)},e._onmousemove=t=>{e._updateMouse(t),e.mouseIsPressed?e.mouseDragged(t):e.mouseMoved(t)},e._onmouseup=r=>{e._updateMouse(r),t.mouseIsPressed=!1,e.mouseReleased(r)},e._onclick=r=>{e._updateMouse(r),t.mouseIsPressed=!0,e.mouseClicked(r),t.mouseIsPressed=!1},e._onwheel=t=>{e._updateMouse(t),t.delta=t.deltaY,0==e.mouseWheel(t)&&t.preventDefault()},e.cursor=(t,r,a)=>{let n="";t.includes(".")&&(t=`url("${t}")`,n=", auto"),void 0!==r&&(t+=" "+r+" "+a),e.canvas.style.cursor=t+n},e.noCursor=()=>{e.canvas.style.cursor="none"},window&&(e.requestPointerLock=document.body?.requestPointerLock,e.exitPointerLock=document.exitPointerLock),e._onkeydown=a=>{a.repeat||(e._startAudio(),t.keyIsPressed=!0,t.key=a.key,t.keyCode=a.keyCode,r[e.keyCode]=r[e.key.toLowerCase()]=!0,e.keyPressed(a),1==a.key.length&&e.keyTyped(a))},e._onkeyup=a=>{t.keyIsPressed=!1,t.key=a.key,t.keyCode=a.keyCode,r[e.keyCode]=r[e.key.toLowerCase()]=!1,e.keyReleased(a)},e.keyIsDown=e=>!!r["string"==typeof e?e.toLowerCase():e],e._ontouchstart=r=>{e._startAudio(),t.touches=[...r.touches].map(o),e._isTouchAware||(t.mouseX=e.touches[0].x,t.mouseY=e.touches[0].y,t.mouseIsPressed=!0,t.mouseButton=e.LEFT,e.mousePressed(r)||r.preventDefault()),e.touchStarted(r)||r.preventDefault()},e._ontouchmove=r=>{t.touches=[...r.touches].map(o),e._isTouchAware||(t.mouseX=e.touches[0].x,t.mouseY=e.touches[0].y,e.mouseDragged(r)||r.preventDefault()),e.touchMoved(r)||r.preventDefault()},e._ontouchend=r=>{t.touches=[...r.touches].map(o),e._isTouchAware||e.touches.length||(t.mouseIsPressed=!1,e.mouseReleased(r)||r.preventDefault()),e.touchEnded(r)||r.preventDefault()},n&&(n.addEventListener("mousedown",(t=>e._onmousedown(t))),n.addEventListener("mouseup",(t=>e._onmouseup(t))),n.addEventListener("wheel",(t=>e._onwheel(t))),n.addEventListener("click",(t=>e._onclick(t))),n.addEventListener("touchstart",(t=>e._ontouchstart(t))),n.addEventListener("touchmove",(t=>e._ontouchmove(t))),n.addEventListener("touchcancel",(t=>e._ontouchend(t))),n.addEventListener("touchend",(t=>e._ontouchend(t)))),window){let t=window.addEventListener;t("mousemove",(t=>e._onmousemove(t)),!1),t("keydown",(t=>e._onkeydown(t)),!1),t("keyup",(t=>e._onkeyup(t)),!1)}},Q5.modules.math=(e,t)=>{e.RADIANS=0,e.DEGREES=1,e.PI=Math.PI,e.HALF_PI=Math.PI/2,e.QUARTER_PI=Math.PI/4,e.abs=Math.abs,e.ceil=Math.ceil,e.exp=Math.exp,e.floor=e.int=Math.floor,e.loge=Math.log,e.mag=Math.hypot,e.max=Math.max,e.min=Math.min,e.round=Math.round,e.pow=Math.pow,e.sqrt=Math.sqrt,e.SHR3=1,e.LCG=2;let r=e._angleMode=0;e.angleMode=t=>(r=e._angleMode=0==t||"radians"==t?0:1,r?"degrees":"radians");let a=e._DEGTORAD=Math.PI/180,n=e._RADTODEG=180/Math.PI;function o(){let e,t,r=4294967295;return{setSeed(a){e=t=(a??Math.random()*r)>>>0},getSeed:()=>t,rand:()=>(e^=e<<17,e^=e>>13,e^=e<<5,(e>>>0)/r)}}e.degrees=t=>t*e._RADTODEG,e.radians=t=>t*e._DEGTORAD,e.map=Q5.prototype.map=(e,t,r,a,n,o)=>{let i=a+1*(e-t)/(r-t)*(n-a);return o?a<n?Math.min(Math.max(i,a),n):Math.min(Math.max(i,n),a):i},e.dist=function(){let e=arguments;return 4==e.length?Math.hypot(e[0]-e[2],e[1]-e[3]):Math.hypot(e[0]-e[3],e[1]-e[4],e[2]-e[5])},e.lerp=(e,t,r)=>e*(1-r)+t*r,e.constrain=(e,t,r)=>Math.min(Math.max(e,t),r),e.norm=(t,r,a)=>e.map(t,r,a,0,1),e.sq=e=>e*e,e.fract=e=>e-Math.floor(e),e.sin=e=>Math.sin(r?e*a:e),e.cos=e=>Math.cos(r?e*a:e),e.tan=e=>Math.tan(r?e*a:e),e.asin=e=>{let t=Math.asin(e);return r?t*n:t},e.acos=e=>{let t=Math.acos(e);return r?t*n:t},e.atan=e=>{let t=Math.atan(e);return r?t*n:t},e.atan2=(e,t)=>{let a=Math.atan2(e,t);return r?a*n:a};let i=o();i.setSeed(),e.randomSeed=e=>i.setSeed(e),e.random=(e,t)=>void 0===e?i.rand():"number"==typeof e?void 0!==t?i.rand()*(t-e)+e:i.rand()*e:e[Math.trunc(e.length*i.rand())],e.randomGenerator=t=>{t==e.LCG?i=function(){const e=4294967296;let t,r;return{setSeed(a){r=t=(a??Math.random()*e)>>>0},getSeed:()=>t,rand:()=>(r=(1664525*r+1013904223)%e,r/e)}}():t==e.SHR3&&(i=o()),i.setSeed()};var s=new function(){var e,t,r,a=new Array(128),n=new Array(256),o=new Array(128),s=new Array(128),l=new Array(256),d=new Array(256),c=()=>4294967296*i.rand()-2147483648,h=()=>.5+2.328306e-10*(c()|0),u=()=>{for(var t,n,i,l,d=3.44262;;){if(t=r*o[e],0==e){do{i=h(),l=h(),t=.2904764*-Math.log(i),n=-Math.log(l)}while(n+n<t*t);return r>0?d+t:-d-t}if(s[e]+h()*(s[e-1]-s[e])<Math.exp(-.5*t*t))return t;if(r=c(),e=127&r,Math.abs(r)<a[e])return r*o[e]}},p=()=>{for(var r;;){if(0==e)return 7.69711-Math.log(h());if(r=t*l[e],d[e]+h()*(d[e-1]-d[e])<Math.exp(-r))return r;if((t=c())<n[e=255&t])return t*l[e]}};this.SHR3=c,this.UNI=h,this.RNOR=()=>(r=c(),e=127&r,Math.abs(r)<a[e]?r*o[e]:u()),this.REXP=()=>(t=c()>>>0)<a[e=255&t]?t*l[e]:p(),this.zigset=()=>{var e,t,r=2147483648,i=4294967296,c=3.442619855899,h=c,u=.00991256303526217,p=7.697117470131487,f=p,_=.003949659822581572;for(e=u/Math.exp(-.5*c*c),a[0]=Math.floor(c/e*r),a[1]=0,o[0]=e/r,o[127]=c/r,s[0]=1,s[127]=Math.exp(-.5*c*c),t=126;t>=1;t--)c=Math.sqrt(-2*Math.log(u/c+Math.exp(-.5*c*c))),a[t+1]=Math.floor(c/h*r),h=c,s[t]=Math.exp(-.5*c*c),o[t]=c/r;for(e=_/Math.exp(-p),n[0]=Math.floor(p/e*i),n[1]=0,l[0]=e/i,l[255]=p/i,d[0]=1,d[255]=Math.exp(-p),t=254;t>=1;t--)p=-Math.log(_/p+Math.exp(-p)),n[t+1]=Math.floor(p/f*i),f=p,d[t]=Math.exp(-p),l[t]=p/i}};let l;s.hasInit=!1,e.randomGaussian=(e,t)=>(s.hasInit||(s.zigset(),s.hasInit=!0),s.RNOR()*t+e),e.randomExponential=()=>(s.hasInit||(s.zigset(),s.hasInit=!0),s.REXP()),e.PERLIN="perlin",e.SIMPLEX="simplex",e.BLOCKY="blocky",e.Noise=Q5.PerlinNoise,e.noiseMode=e=>{t.Noise=Q5[e[0].toUpperCase()+e.slice(1)+"Noise"],l=null},e.noiseSeed=t=>{l=new e.Noise(t)},e.noise=(t=0,r=0,a=0)=>(l??=new e.Noise,l.noise(t,r,a)),e.noiseDetail=(t,r)=>{l??=new e.Noise,t>0&&(l.octaves=t),r>0&&(l.falloff=r)}},Q5.Noise=class{},Q5.PerlinNoise=class extends Q5.Noise{constructor(e){super(),this.grad3=[[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]],this.octaves=1,this.falloff=.5,this.p=null==e?Array.from({length:256},(()=>Math.floor(256*Math.random()))):this.seedPermutation(e),this.p=this.p.concat(this.p)}seedPermutation(e){let t,r,a=[];for(let e=0;e<256;e++)a[e]=e;for(let n=255;n>0;n--)t=(e=16807*e%2147483647)%(n+1),r=a[n],a[n]=a[t],a[t]=r;return a}dot(e,t,r,a){return e[0]*t+e[1]*r+e[2]*a}mix(e,t,r){return(1-r)*e+r*t}fade(e){return e*e*e*(e*(6*e-15)+10)}noise(e,t,r){let a=this,n=0,o=1,i=1,s=0;for(let l=0;l<a.octaves;l++){const l=255&Math.floor(e*o),d=255&Math.floor(t*o),c=255&Math.floor(r*o),h=e*o-Math.floor(e*o),u=t*o-Math.floor(t*o),p=r*o-Math.floor(r*o),f=a.fade(h),_=a.fade(u),g=a.fade(p),x=a.p[l]+d,m=a.p[x]+c,v=a.p[x+1]+c,y=a.p[l+1]+d,w=a.p[y]+c,b=a.p[y+1]+c,S=a.mix(a.dot(a.grad3[a.p[m]%12],h,u,p),a.dot(a.grad3[a.p[w]%12],h-1,u,p),f),M=a.mix(a.dot(a.grad3[a.p[v]%12],h,u-1,p),a.dot(a.grad3[a.p[b]%12],h-1,u-1,p),f),C=a.mix(a.dot(a.grad3[a.p[m+1]%12],h,u,p-1),a.dot(a.grad3[a.p[w+1]%12],h-1,u,p-1),f),R=a.mix(a.dot(a.grad3[a.p[v+1]%12],h,u-1,p-1),a.dot(a.grad3[a.p[b+1]%12],h-1,u-1,p-1),f),I=a.mix(S,M,_),E=a.mix(C,R,_);n+=a.mix(I,E,g)*i,s+=i,i*=a.falloff,o*=2}return(n/s+1)/2}},Q5.modules.sound=(e,t)=>{e.Sound=Q5.Sound,e.loadSound=(e,r)=>{t._preloadCount++,Q5.aud??=new window.AudioContext;let a=new Q5.Sound(e,r);return a.crossOrigin="Anonymous",a.addEventListener("canplaythrough",(()=>{t._preloadCount--,a.loaded=!0,r&&r(a)})),a},e.getAudioContext=()=>Q5.aud,e.userStartAudio=()=>Q5.aud.resume()},window.Audio&&(Q5.Sound=class extends Audio{constructor(e){super(e);let t=this;t.load(),t.panner=Q5.aud.createStereoPanner(),t.source=Q5.aud.createMediaElementSource(t),t.source.connect(t.panner),t.panner.connect(Q5.aud.destination),Object.defineProperty(t,"pan",{get:()=>t.panner.pan.value,set:e=>t.panner.pan.value=e})}setVolume(e){this.volume=e}setLoop(e){this.loop=e}setPan(e){this.pan=e}isLoaded(){return this.loaded}isPlaying(){return!this.paused}}),Q5.modules.util=(e,t)=>{e._loadFile=(r,a,n)=>{t._preloadCount++;let o={};return fetch(r).then((e=>"json"==n?e.json():e.text())).then((r=>{t._preloadCount--,"csv"==n&&(r=e.CSV.parse(r)),Object.assign(o,r),a&&a(r)})),o},e.loadText=(t,r)=>e._loadFile(t,r,"text"),e.loadJSON=(t,r)=>e._loadFile(t,r,"json"),e.loadCSV=(t,r)=>e._loadFile(t,r,"csv"),e.CSV={},e.CSV.parse=(e,t=",",r="\n")=>{let a=[],n=e.split(r),o=n[0].split(t);for(let e=1;e<n.length;e++){let r={},i=n[e].split(t);o.forEach(((e,t)=>r[e]=JSON.parse(i[t]))),a.push(r)}return a},"object"==typeof localStorage&&(e.storeItem=localStorage.setItem,e.getItem=localStorage.getItem,e.removeItem=localStorage.removeItem,e.clearStorage=localStorage.clear),e.year=()=>(new Date).getFullYear(),e.day=()=>(new Date).getDay(),e.hour=()=>(new Date).getHours(),e.minute=()=>(new Date).getMinutes(),e.second=()=>(new Date).getSeconds()},Q5.modules.vector=e=>{e.createVector=(t,r,a)=>new Q5.Vector(t,r,a,e)},Q5.Vector=class{constructor(e,t,r,a){this.x=e||0,this.y=t||0,this.z=r||0,this._$=a||window,this._cn=null,this._cnsq=null}set(e,t,r){return this.x=e?.x||e||0,this.y=e?.y||t||0,this.z=e?.z||r||0,this}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(e,t,r){return void 0!==e?.x?e:void 0!==t?{x:e,y:t,z:r||0}:{x:e,y:e,z:e}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z,this._cn=Math.sqrt(this._cnsq)}add(){let e=this._arg2v(...arguments);return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}rem(){let e=this._arg2v(...arguments);return this.x%=e.x,this.y%=e.y,this.z%=e.z,this}sub(){let e=this._arg2v(...arguments);return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}mult(){let e=this._arg2v(...arguments);return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}div(){let e=this._arg2v(...arguments);return e.x?this.x/=e.x:this.x=0,e.y?this.y/=e.y:this.y=0,e.z?this.z/=e.z:this.z=0,this}mag(){return this._calcNorm(),this._cn}magSq(){return this._calcNorm(),this._cnsq}dot(){let e=this._arg2v(...arguments);return this.x*e.x+this.y*e.y+this.z*e.z}dist(){let e=this._arg2v(...arguments),t=this.x-e.x,r=this.y-e.y,a=this.z-e.z;return Math.sqrt(t*t+r*r+a*a)}cross(){let e=this._arg2v(...arguments),t=this.y*e.z-this.z*e.y,r=this.z*e.x-this.x*e.z,a=this.x*e.y-this.y*e.x;return this.x=t,this.y=r,this.z=a,this}normalize(){this._calcNorm();let e=this._cn;return 0!=e&&(this.x/=e,this.y/=e,this.z/=e),this._cn=1,this._cnsq=1,this}limit(e){this._calcNorm();let t=this._cn;if(t>e){let r=e/t;this.x*=r,this.y*=r,this.z*=r,this._cn=e,this._cnsq=e*e}return this}setMag(e){this._calcNorm();let t=e/this._cn;return this.x*=t,this.y*=t,this.z*=t,this._cn=e,this._cnsq=e*e,this}heading(){return this._$.atan2(this.y,this.x)}setHeading(e){let t=this.mag();return this.x=t*this._$.cos(e),this.y=t*this._$.sin(e),this}rotate(e){let t=this._$.cos(e),r=this._$.sin(e),a=this.x*t-this.y*r,n=this.x*r+this.y*t;return this.x=a,this.y=n,this}angleBetween(){let e=this._arg2v(...arguments),t=Q5.Vector.cross(this,e);return this._$.atan2(t.mag(),this.dot(e))*Math.sign(t.z||1)}lerp(){let e=[...arguments],t=e.at(-1);if(0==t)return this;let r=this._arg2v(...e.slice(0,-1));return this.x+=(r.x-this.x)*t,this.y+=(r.y-this.y)*t,this.z+=(r.z-this.z)*t,this}slerp(){let e=[...arguments],t=e.at(-1);if(0==t)return this;let r=this._arg2v(...e.slice(0,-1));if(1==t)return this.set(r);let a=this.mag(),n=r.mag();if(0==a||0==n)return this.mult(1-t).add(r.mult(t));let o=Q5.Vector.cross(this,r),i=o.mag(),s=Math.atan2(i,this.dot(r));if(i>0)o.div(i);else{if(s<this._$.HALF_PI)return this.mult(1-t).add(r.mult(t));0==this.z&&0==r.z?o.set(0,0,1):0!=this.x?o.set(this.y,-this.x,0).normalize():o.set(1,0,0)}let l=o.cross(this),d=1-t+t*n/a,c=d*Math.cos(t*s),h=d*Math.sin(t*s);return this.x=this.x*c+l.x*h,this.y=this.y*c+l.y*h,this.z=this.z*c+l.z*h,this}reflect(e){return e.normalize(),this.sub(e.mult(2*this.dot(e)))}array(){return[this.x,this.y,this.z]}equals(e,t){return t??=Number.EPSILON||0,Math.abs(e.x-this.x)<t&&Math.abs(e.y-this.y)<t&&Math.abs(e.z-this.z)<t}fromAngle(e,t){return void 0===t&&(t=1),this._cn=t,this._cnsq=t*t,this.x=t*this._$.cos(e),this.y=t*this._$.sin(e),this.z=0,this}fromAngles(e,t,r){void 0===r&&(r=1),this._cn=r,this._cnsq=r*r;const a=this._$.cos(t),n=this._$.sin(t),o=this._$.cos(e),i=this._$.sin(e);return this.x=r*i*n,this.y=-r*o,this.z=r*i*a,this}random2D(){return this._cn=this._cnsq=1,this.fromAngle(Math.random()*Math.PI*2)}random3D(){return this._cn=this._cnsq=1,this.fromAngles(Math.random()*Math.PI*2,Math.random()*Math.PI*2)}toString(){return`[${this.x}, ${this.y}, ${this.z}]`}},Q5.Vector.add=(e,t)=>e.copy().add(t),Q5.Vector.cross=(e,t)=>e.copy().cross(t),Q5.Vector.dist=(e,t)=>Math.hypot(e.x-t.x,e.y-t.y,e.z-t.z),Q5.Vector.div=(e,t)=>e.copy().div(t),Q5.Vector.dot=(e,t)=>e.copy().dot(t),Q5.Vector.equals=(e,t,r)=>e.equals(t,r),Q5.Vector.lerp=(e,t,r)=>e.copy().lerp(t,r),Q5.Vector.slerp=(e,t,r)=>e.copy().slerp(t,r),Q5.Vector.limit=(e,t)=>e.copy().limit(t),Q5.Vector.heading=e=>this._$.atan2(e.y,e.x),Q5.Vector.magSq=e=>e.x*e.x+e.y*e.y+e.z*e.z,Q5.Vector.mag=e=>Math.sqrt(Q5.Vector.magSq(e)),Q5.Vector.mult=(e,t)=>e.copy().mult(t),Q5.Vector.normalize=e=>e.copy().normalize(),Q5.Vector.rem=(e,t)=>e.copy().rem(t),Q5.Vector.sub=(e,t)=>e.copy().sub(t);for(let e of["fromAngle","fromAngles","random2D","random3D"])Q5.Vector[e]=(t,r,a)=>(new Q5.Vector)[e](t,r,a);Q5.renderers.webgpu={},Q5.renderers.webgpu.canvas=(e,t)=>{let r=e.canvas;r.width=e.width=500,r.height=e.height=500,e.colorMode&&e.colorMode("rgb",1);let a,n,o,i=1,s=8;e._pipelineConfigs=[],e._pipelines=[];let l=e.drawStack=[],d=e.colorStack=new Float32Array(1e6);d.set([0,0,0,1,1,1,1,1]),e._transformLayout=Q5.device.createBindGroupLayout({label:"transformLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",hasDynamicOffset:!1}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage",hasDynamicOffset:!1}}]}),o=Q5.device.createBindGroupLayout({label:"colorsLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage",hasDynamicOffset:!1}}]}),e.bindGroupLayouts=[e._transformLayout,o];let c=Q5.device.createBuffer({size:8,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),h=()=>{n=Q5.device.createTexture({size:[e.canvas.width,e.canvas.height],sampleCount:4,format:"bgra8unorm",usage:GPUTextureUsage.RENDER_ATTACHMENT}).createView()};e._createCanvas=(a,n,o)=>(t.ctx=t.drawingContext=r.getContext("webgpu"),o.format??=navigator.gpu.getPreferredCanvasFormat(),o.device??=Q5.device,e.ctx.configure(o),Q5.device.queue.writeBuffer(c,0,new Float32Array([e.canvas.hw,e.canvas.hh])),h(),r),e._resizeCanvas=(t,r)=>{e._setCanvasSize(t,r),h()},e.pixelDensity=t=>t&&t!=e._pixelDensity?(e._pixelDensity=t,e._setCanvasSize(r.w,r.h),h(),t):e._pixelDensity;let u=(t,r,a,n=1)=>{"string"==typeof t?t=e.color(t):null==a&&(n=r??1,r=a=t),t._q5Color&&(n=t.a,a=t.b,r=t.g,t=t.r);let o=d,l=s;o[l++]=t,o[l++]=r,o[l++]=a,o[l++]=n,s=l,i++};e._fill=e._stroke=0,e._doFill=e._doStroke=!0,e.fill=(t,r,a,n)=>{u(t,r,a,n),e._doFill=e._fillSet=!0,e._fill=i},e.stroke=(t,r,a,n)=>{u(t,r,a,n),e._doStroke=e._strokeSet=!0,e._stroke=i},e.noFill=()=>e._doFill=!1,e.noStroke=()=>e._doStroke=!1,e._strokeWeight=1,e.strokeWeight=t=>e._strokeWeight=Math.abs(t),e.resetMatrix=()=>{e._matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],e._transformIndex=0},e.resetMatrix(),e._matrixDirty=!1;let p=[e._matrix.slice()];e._transformIndexStack=[],e.translate=(t,r,a)=>{(t||r||a)&&(e._matrix[12]+=t,e._matrix[13]-=r,e._matrix[14]+=a||0,e._matrixDirty=!0)},e.rotate=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.cos(t),a=Math.sin(t),n=e._matrix,o=n[0],i=n[1],s=n[4],l=n[5];1!=o||i||s||1!=l?(n[0]=o*r+i*a,n[1]=i*r-o*a,n[4]=s*r+l*a,n[5]=l*r-s*a):(n[0]=r,n[1]=-a,n[4]=a,n[5]=r),e._matrixDirty=!0},e.scale=(t=1,r,a=1)=>{r??=t;let n=e._matrix;n[0]*=t,n[1]*=t,n[2]*=t,n[3]*=t,n[4]*=r,n[5]*=r,n[6]*=r,n[7]*=r,n[8]*=a,n[9]*=a,n[10]*=a,n[11]*=a,e._matrixDirty=!0},e.shearX=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.tan(t),a=e._matrix[0],n=e._matrix[1],o=e._matrix[4],i=e._matrix[5];e._matrix[0]=a+o*r,e._matrix[1]=n+i*r,e._matrixDirty=!0},e.shearY=t=>{if(!t)return;e._angleMode&&(t*=e._DEGTORAD);let r=Math.tan(t),a=e._matrix[0],n=e._matrix[1],o=e._matrix[4],i=e._matrix[5];e._matrix[4]=o+a*r,e._matrix[5]=i+n*r,e._matrixDirty=!0},e.applyMatrix=(...t)=>{let r;if(r=1==t.length?t[0]:t,9==r.length)r=[r[0],r[1],0,r[2],r[3],r[4],0,r[5],0,0,1,0,r[6],r[7],0,r[8]];else if(16!=r.length)throw new Error("Matrix must be a 3x3 or 4x4 array.");e._matrix=r.slice(),e._matrixDirty=!0},e._saveMatrix=()=>{p.push(e._matrix.slice()),e._transformIndex=p.length-1,e._matrixDirty=!1},e.pushMatrix=()=>{e._matrixDirty&&e._saveMatrix(),e._transformIndexStack.push(e._transformIndex)},e.popMatrix=()=>{if(!e._transformIndexStack.length)return console.warn("Matrix index stack is empty!");let t=e._transformIndexStack.pop();e._matrix=p[t].slice(),e._transformIndex=t,e._matrixDirty=!1},e.push=()=>{e.pushMatrix(),e.pushStyles()},e.pop=()=>{e.popMatrix(),e.popStyles()},e._calcBox=(e,t,r,a,n)=>{let o,i,s,l,d=r/2,c=a/2;return n&&"corner"!=n?"center"==n?(o=e-d,i=e+d,s=-(t-c),l=-(t+c)):(o=e,i=r,s=-t,l=-a):(o=e,i=e+r,s=-t,l=-(t+a)),[o,i,s,l]};let f=["zero","one","src-alpha","one-minus-src-alpha","dst","dst-alpha","one-minus-dst-alpha","one-minus-src"],_=["add","subtract","reverse-subtract","min","max"];const g={normal:[2,3,0,2,3,0],additive:[1,1,0,1,1,0]};e.blendConfigs={};for(const[t,r]of Object.entries(g))e.blendConfigs[t]={color:{srcFactor:f[r[0]],dstFactor:f[r[1]],operation:_[r[2]]},alpha:{srcFactor:f[r[3]],dstFactor:f[r[4]],operation:_[r[5]]}};e._blendMode="normal",e.blendMode=t=>{if(t!=e._blendMode){"source-over"==t&&(t="normal"),"lighter"==t&&(t="additive"),t=t.toLowerCase().replace(/[ -]/g,"_"),e._blendMode=t;for(let r=0;r<e._pipelines.length;r++)e._pipelineConfigs[r].fragment.targets[0].blend=e.blendConfigs[t],e._pipelines[r]=Q5.device.createRenderPipeline(e._pipelineConfigs[r])}},e.clear=()=>{},e._beginRender=()=>{e.encoder=Q5.device.createCommandEncoder(),a=t.pass=e.encoder.beginRenderPass({label:"q5-webgpu",colorAttachments:[{view:n,resolveTarget:e.ctx.getCurrentTexture().createView(),loadOp:"clear",storeOp:"store"}]})},e._render=()=>{if(p.length>1||!e._transformBindGroup){let t=Q5.device.createBuffer({size:64*p.length,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(p.flat()),t.unmap(),e._transformBindGroup=Q5.device.createBindGroup({layout:e._transformLayout,entries:[{binding:0,resource:{buffer:c}},{binding:1,resource:{buffer:t}}]})}a.setBindGroup(0,e._transformBindGroup);let t=Q5.device.createBuffer({size:4*s,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(d.slice(0,s)),t.unmap(),e._colorsBindGroup=Q5.device.createBindGroup({layout:o,entries:[{binding:0,resource:{buffer:t}}]}),e.pass.setBindGroup(1,e._colorsBindGroup);for(let t of e._hooks.preRender)t();let r=0,n=0,i=0,h=-1;for(let t=0;t<l.length;t+=2){let o=l[t+1];if(h!=l[t]&&(h=l[t],a.setPipeline(e._pipelines[h])),0==h)a.draw(o,1,r),r+=o;else if(1==h)-1!=o&&a.setBindGroup(2,e._textureBindGroups[o]),a.draw(4,1,n),n+=4;else if(2==h){let r=l[t+2];a.setBindGroup(2,e._fonts[r].bindGroup),a.setBindGroup(3,e._textBindGroup),a.draw(4,o,0,i),i+=o,t++}}for(let t of e._hooks.postRender)t()},e._finishRender=()=>{a.end();let r=e.encoder.finish();Q5.device.queue.submit([r]),t.pass=e.encoder=null,e.drawStack.length=0,i=1,s=8,rotation=0,p.length=1,e._transformIndexStack.length=0}},Q5.initWebGPU=async()=>{if(!navigator.gpu)return console.warn("q5 WebGPU not supported on this browser!"),!1;if(!Q5.device){let e=await navigator.gpu.requestAdapter();if(!e)throw new Error("No appropriate GPUAdapter found.");Q5.device=await e.requestDevice()}return!0},Q5.webgpu=async function(e,t){return e&&"global"!=e||(Q5._hasGlobal=!0),await Q5.initWebGPU()?new Q5(e,t,"webgpu"):new Q5(e,t,"webgpu-fallback")},Q5.renderers.webgpu.drawing=(e,t)=>{let r=e.canvas,a=e.drawStack,n=new Float32Array(1e7),o=0,i=Q5.device.createShaderModule({label:"drawingVertexShader",code:"\nstruct VertexInput {\n\t@location(0) pos: vec2f,\n\t@location(1) colorIndex: f32,\n\t@location(2) transformIndex: f32\n}\nstruct VertexOutput {\n\t@builtin(position) position: vec4f,\n\t@location(0) color: vec4f\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@group(1) @binding(0) var<storage> colors : array<vec4f>;\n\n@vertex\nfn vertexMain(input: VertexInput) -> VertexOutput {\n\tvar vert = vec4f(input.pos, 0.0, 1.0);\n\tvert = transforms[i32(input.transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output: VertexOutput;\n\toutput.position = vert;\n\toutput.color = colors[i32(input.colorIndex)];\n\treturn output;\n}\n"}),s=Q5.device.createShaderModule({label:"drawingFragmentShader",code:"\n@fragment\nfn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {\n\treturn color;\n}\n"}),l=Q5.device.createPipelineLayout({label:"drawingPipelineLayout",bindGroupLayouts:e.bindGroupLayouts});e._pipelineConfigs[0]={label:"drawingPipeline",layout:l,vertex:{module:i,entryPoint:"vertexMain",buffers:[{arrayStride:16,attributes:[{format:"float32x2",offset:0,shaderLocation:0},{format:"float32",offset:8,shaderLocation:1},{format:"float32",offset:12,shaderLocation:2}]}]},fragment:{module:s,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[0]=Q5.device.createRenderPipeline(e._pipelineConfigs[0]);const d=(e,t,r,a)=>{let i=n,s=o;i[s++]=e,i[s++]=t,i[s++]=r,i[s++]=a,o=s},c=(e,t,r,i,s,l,d,c,h,u)=>{let p=n,f=o;p[f++]=e,p[f++]=t,p[f++]=h,p[f++]=u,p[f++]=r,p[f++]=i,p[f++]=h,p[f++]=u,p[f++]=d,p[f++]=c,p[f++]=h,p[f++]=u,p[f++]=s,p[f++]=l,p[f++]=h,p[f++]=u,o=f,a.push(0,4)},h=(t,r,i,s,l,d,c)=>{r=-r;let h=0,u=e.TAU/l,p=n,f=o;for(let e=0;e<=l;e++){p[f++]=t,p[f++]=r,p[f++]=d,p[f++]=c;let e=t+i*Math.cos(h),a=r+s*Math.sin(h);p[f++]=e,p[f++]=a,p[f++]=d,p[f++]=c,h+=u}p[f++]=t,p[f++]=r,p[f++]=d,p[f++]=c,p[f++]=t+i,p[f++]=r,p[f++]=d,p[f++]=c,o=f,a.push(0,2*(l+1)+2)};e.rectMode=t=>e._rectMode=t,e.rect=(t,r,a,n)=>{let o,i,[s,l,d,h]=e._calcBox(t,r,a,n,e._rectMode);if(e._matrixDirty&&e._saveMatrix(),i=e._transformIndex,e._doStroke){o=e._stroke;let t=e._strokeWeight/2;if(e._doFill){let e=d+t,r=h-t,a=s-t,n=l+t;c(a,e,n,e,n,r,a,r,o,i),d-=t,h+=t,s+=t,l-=t}else{let e=s-t,r=l+t,a=d+t,n=h-t,u=s+t,p=l-t,f=d-t,_=h+t;c(e,f,r,f,r,a,e,a,o,i),c(e,n,r,n,r,_,e,_,o,i),c(e,a,u,a,u,n,e,n,o,i),c(p,a,r,a,r,n,p,n,o,i)}}e._doFill&&(o=e._fill,c(s,d,l,d,l,h,s,h,o,i))},e.square=(t,r,a)=>e.rect(t,r,a,a);const u=e=>e<4?6:e<6?8:e<10?10:e<16?12:e<20?14:e<22?16:e<24?18:e<28?20:e<34?22:e<42?24:e<48?26:e<56?28:e<64?30:e<72?32:e<84?34:e<96?36:e<98?38:e<113?40:e<149?44:e<199?48:e<261?52:e<353?56:e<461?60:e<585?64:e<1200?70:e<1800?80:e<2400?90:100;let p;e.ellipseMode=t=>e._ellipseMode=t,e.ellipse=(t,r,a,n)=>{let o=u(Math.max(a,n)),i=Math.max(a,1)/2,s=a==n?i:Math.max(n,1)/2;e._matrixDirty&&e._saveMatrix();let l=e._transformIndex;if(e._doStroke){let a=e._strokeWeight/2;h(t,r,i+a,s+a,o,e._stroke,l),i-=a,s-=a}e._doFill&&h(t,r,i,s,o,e._fill,l)},e.circle=(t,r,a)=>e.ellipse(t,r,a,a),e.point=(t,r)=>{e._matrixDirty&&e._saveMatrix();let a=e._transformIndex,n=e._stroke,o=e._strokeWeight;if(o<2){let[i,s,l,d]=e._calcBox(t,r,o,o,"corner");c(i,l,s,l,s,d,i,d,n,a)}else{let e=u(o);o/=2,h(t,r,o,o,e,n,a)}},e.stokeJoin=t=>{e.log("q5 WebGPU doesn't support changing stroke join style.")},e.line=(t,r,a,n)=>{e._matrixDirty&&e._saveMatrix();let o=e._transformIndex,i=e._stroke,s=e._strokeWeight,l=s/2,d=a-t,p=n-r,f=Math.hypot(d,p),_=-p/f*l,g=d/f*l;if(c(t+_,-r-g,t-_,-r+g,a-_,-n+g,a+_,-n-g,i,o),s>2){let e=u(s);h(t,r,l,l,e,i,o),h(a,n,l,l,e,i,o)}};let f=[],_=[];e.beginShape=()=>{p=0,f=[],_=[]},e.vertex=(t,r)=>{e._matrixDirty&&e._saveMatrix(),f.push(t,-r,e._fill,e._transformIndex),p++},e.curveVertex=(t,r)=>{e._matrixDirty&&e._saveMatrix(),_.push({x:t,y:-r})},e.endShape=t=>{if(_.length>0){let t=[..._];if(t.length<4)for(;t.length<4;)t.unshift(t[0]),t.push(t[t.length-1]);for(let r=0;r<t.length-3;r++){let a=t[r],n=t[r+1],o=t[r+2],i=t[r+3];for(let t=0;t<=1;t+=.1){let r=t*t,s=r*t,l=.5*(2*n.x+(-a.x+o.x)*t+(2*a.x-5*n.x+4*o.x-i.x)*r+(-a.x+3*n.x-3*o.x+i.x)*s),d=.5*(2*n.y+(-a.y+o.y)*t+(2*a.y-5*n.y+4*o.y-i.y)*r+(-a.y+3*n.y-3*o.y+i.y)*s);f.push(l,d,e._fill,e._transformIndex),p++}}}if(p<3)throw new Error("A shape must have at least 3 vertices.");if(t){let e=0,t=4*(p-1),r=f[e],a=f[e+1],n=f[t],o=f[t+1];r===n&&a===o||(f.push(r,a,f[e+2],f[e+3]),p++)}if(e._doFill){for(let e=1;e<p-1;e++){let t=0,r=4*e,a=4*(e+1);d(f[t],f[t+1],f[t+2],f[t+3]),d(f[r],f[r+1],f[r+2],f[r+3]),d(f[a],f[a+1],f[a+2],f[a+3])}a.push(0,3*(p-2))}if(e._doStroke){for(let t=0;t<p-1;t++){let r=4*t,a=4*(t+1);e.line(f[r],-f[r+1],f[a],-f[a+1])}if(t){let t=4*(p-1),r=0;e.line(f[t],-f[t+1],f[r],-f[r+1])}}p=0,f=[],_=[]},e.triangle=(t,r,a,n,o,i)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.endShape(!0)},e.quad=(t,r,a,n,o,i,s,l)=>{e.beginShape(),e.vertex(t,r),e.vertex(a,n),e.vertex(o,i),e.vertex(s,l),e.endShape(!0)},e.background=(t,a,n,o)=>{if(e.push(),e.resetMatrix(),e._doStroke=!1,t.src){let a=e._imageMode;e._imageMode="corner",e.image(t,-r.hw,-r.hh,r.w,r.h),e._imageMode=a}else{let i=e._rectMode;e._rectMode="corner",e.fill(t,a,n,o),e.rect(-r.hw,-r.hh,r.w,r.h),e._rectMode=i}e.pop(),e._fillSet||(e._fill=1)},e._hooks.preRender.push((()=>{e.pass.setPipeline(e._pipelines[0]);let t=Q5.device.createBuffer({size:4*o,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(n.slice(0,o)),t.unmap(),e.pass.setVertexBuffer(0,t)})),e._hooks.postRender.push((()=>{o=0}))},Q5.renderers.webgpu.image=(e,t)=>{e._textureBindGroups=[];let r=[],a=Q5.device.createShaderModule({label:"imageVertexShader",code:"\nstruct VertexOutput {\n\t@builtin(position) position: vec4f,\n\t@location(0) texCoord: vec2f\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@vertex\nfn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {\n\tvar vert = vec4f(pos, 0.0, 1.0);\n\tvert = transforms[i32(transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output: VertexOutput;\n\toutput.position = vert;\n\toutput.texCoord = texCoord;\n\treturn output;\n}\n\t"}),n=Q5.device.createShaderModule({label:"imageFragmentShader",code:"\n@group(2) @binding(0) var samp: sampler;\n@group(2) @binding(1) var texture: texture_2d<f32>;\n\n@fragment\nfn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {\n\t// Sample the texture using the interpolated texture coordinate\n\treturn textureSample(texture, samp, texCoord);\n}\n\t"}),o=Q5.device.createBindGroupLayout({label:"textureLayout",entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,sampler:{type:"filtering"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,texture:{viewDimension:"2d",sampleType:"float"}}]});const i=Q5.device.createPipelineLayout({label:"imagePipelineLayout",bindGroupLayouts:[...e.bindGroupLayouts,o]});let s;e._pipelineConfigs[1]={label:"imagePipeline",layout:i,vertex:{module:a,entryPoint:"vertexMain",buffers:[{arrayStride:0,attributes:[]},{arrayStride:20,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"},{shaderLocation:2,offset:16,format:"float32"}]}]},fragment:{module:n,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[1]=Q5.device.createRenderPipeline(e._pipelineConfigs[1]);let l=e=>{s=Q5.device.createSampler({magFilter:e,minFilter:e})};l("linear"),e.smooth=()=>{l("linear")},e.noSmooth=()=>{l("nearest")};e._textures=[];let d=0;e._createTexture=t=>{t.canvas&&(t=t.canvas);let r=[t.width,t.height,1],a=Q5.device.createTexture({size:r,format:"bgra8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:t},{texture:a,colorSpace:e.canvas.colorSpace},r),e._textures[d]=a,t.textureIndex=d;const n=Q5.device.createBindGroup({layout:o,entries:[{binding:0,resource:s},{binding:1,resource:a.createView()}]});e._textureBindGroups[d]=n,d=(d+1)%12e3,e._textures[d]&&(e._textures[d].destroy(),delete e._textures[d],delete e._textureBindGroups[d])},e.loadImage=(r,a)=>{t._preloadCount++;const n=new Image;return n.crossOrigin="Anonymous",n.onload=()=>{n.defaultWidth=n.width*e._defaultImageScale,n.defaultHeight=n.height*e._defaultImageScale,n.pixelDensity=1,e._createTexture(n),t._preloadCount--,a&&a(n)},n.src=r,n},e.imageMode=t=>e._imageMode=t,e.image=(t,a,n,o,i,s=0,l=0,d,c)=>{if(t.canvas&&(t=t.canvas),null==t.textureIndex)return;e._matrixDirty&&e._saveMatrix();let h=e._transformIndex,u=t.width,p=t.height;o??=t.defaultWidth,i??=t.defaultHeight,d??=u,c??=p;let f=t.pixelDensity||1;o*=f,i*=f;let[_,g,x,m]=e._calcBox(a,n,o,i,e._imageMode),v=s/u,y=l/p,w=(s+d)/u,b=(l+c)/p;r.push(_,x,v,y,h,g,x,w,y,h,_,m,v,b,h,g,m,w,b,h),e.drawStack.push(1,t.textureIndex)},e._hooks.preRender.push((()=>{if(!e._textureBindGroups.length)return;e.pass.setPipeline(e._pipelines[1]);const t=Q5.device.createBuffer({size:4*r.length,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(t.getMappedRange()).set(r),t.unmap(),e.pass.setVertexBuffer(1,t)})),e._hooks.postRender.push((()=>{r.length=0}))},Q5.THRESHOLD=1,Q5.GRAY=2,Q5.OPAQUE=3,Q5.INVERT=4,Q5.POSTERIZE=5,Q5.DILATE=6,Q5.ERODE=7,Q5.BLUR=8,Q5.renderers.webgpu.text=(e,t)=>{let r=Q5.device.createShaderModule({label:"MSDF text shader",code:"\n// Positions for simple quad geometry\nconst pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));\n\nstruct VertexInput {\n\t@builtin(vertex_index) vertex : u32,\n\t@builtin(instance_index) instance : u32\n}\nstruct VertexOutput {\n\t@builtin(position) position : vec4f,\n\t@location(0) texCoord : vec2f,\n\t@location(1) fillColor : vec4f\n}\nstruct Char {\n\ttexOffset: vec2f,\n\ttexExtent: vec2f,\n\tsize: vec2f,\n\toffset: vec2f,\n}\nstruct Text {\n\tpos: vec2f,\n\tscale: f32,\n\ttransformIndex: f32,\n\tfillIndex: f32,\n\tstrokeIndex: f32\n}\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n\n@group(1) @binding(0) var<storage> colors : array<vec4f>;\n\n@group(2) @binding(0) var fontTexture: texture_2d<f32>;\n@group(2) @binding(1) var fontSampler: sampler;\n@group(2) @binding(2) var<storage> fontChars: array<Char>;\n\n@group(3) @binding(0) var<storage> textChars: array<vec4f>;\n@group(3) @binding(1) var<storage> textMetadata: array<Text>;\n\n@vertex\nfn vertexMain(input : VertexInput) -> VertexOutput {\n\tlet char = textChars[input.instance];\n\n\tlet text = textMetadata[i32(char.w)];\n\n\tlet fontChar = fontChars[i32(char.z)];\n\n\tlet charPos = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;\n\n\tvar vert = vec4f(charPos, 0.0, 1.0);\n\tvert = transforms[i32(text.transformIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar output : VertexOutput;\n\toutput.position = vert;\n\toutput.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;\n\toutput.fillColor = colors[i32(text.fillIndex)];\n\treturn output;\n}\n\nfn sampleMsdf(texCoord: vec2f) -> f32 {\n\tlet c = textureSample(fontTexture, fontSampler, texCoord);\n\treturn max(min(c.r, c.g), min(max(c.r, c.g), c.b));\n}\n\n@fragment\nfn fragmentMain(input : VertexOutput) -> @location(0) vec4f {\n\t// pxRange (AKA distanceRange) comes from the msdfgen tool,\n\t// uses the default which is 4.\n\tlet pxRange = 4.0;\n\tlet sz = vec2f(textureDimensions(fontTexture, 0));\n\tlet dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));\n\tlet dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));\n\tlet toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);\n\tlet sigDist = sampleMsdf(input.texCoord) - 0.5;\n\tlet pxDist = sigDist * toPixels;\n\tlet edgeWidth = 0.5;\n\tlet alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);\n\tif (alpha < 0.001) {\n\t\tdiscard;\n\t}\n\treturn vec4f(input.fillColor.rgb, input.fillColor.a * alpha);\n}\n"}),a=Q5.device.createBindGroupLayout({label:"MSDF text group layout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}},{binding:1,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]}),n=Q5.device.createSampler({minFilter:"linear",magFilter:"linear",mipmapFilter:"linear",maxAnisotropy:16}),o=Q5.device.createBindGroupLayout({label:"MSDF font group layout",entries:[{binding:0,visibility:GPUShaderStage.FRAGMENT,texture:{}},{binding:1,visibility:GPUShaderStage.FRAGMENT,sampler:{}},{binding:2,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}}]}),i=Q5.device.createPipelineLayout({bindGroupLayouts:[...e.bindGroupLayouts,o,a]});e._pipelineConfigs[2]={label:"msdf font pipeline",layout:i,vertex:{module:r,entryPoint:"vertexMain"},fragment:{module:r,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:e.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}},e._pipelines[2]=Q5.device.createRenderPipeline(e._pipelineConfigs[2]);class s{constructor(e,t,r,a){this.bindGroup=e,this.lineHeight=t,this.chars=r,this.kernings=a;let n=Object.values(r);this.charCount=n.length,this.defaultChar=n[0]}getChar(e){return this.chars[e]??this.defaultChar}getXAdvance(e,t=-1){let r=this.getChar(e);if(t>=0){let a=this.kernings.get(e);if(a)return r.xadvance+(a.get(t)??0)}return r.xadvance}}e._fonts=[];let l={},d=e.createGraphics(1,1);d.colorMode(e.RGB,1),e.loadFont=(r,a)=>{if("json"!=r.slice(r.lastIndexOf(".")+1))return d.loadFont(r,a);let i=r.slice(r.lastIndexOf("/")+1,r.lastIndexOf("-"));return(async(r,a,i)=>{t._preloadCount++;let d=await fetch(r);if(404==d.status)return t._preloadCount--,"";let c=await d.json(),h=r.lastIndexOf("/"),u=-1!=h?r.substring(0,h+1):"";d=await fetch(u+c.pages[0]);let p=await createImageBitmap(await d.blob()),f=[p.width,p.height,1],_=Q5.device.createTexture({label:`MSDF ${a}`,size:f,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:p},{texture:_},f),"string"==typeof c.chars&&(c.chars=e.CSV.parse(c.chars," "),c.kernings=e.CSV.parse(c.kernings," "));let g=c.chars.length,x=Q5.device.createBuffer({size:32*g,usage:GPUBufferUsage.STORAGE,mappedAtCreation:!0}),m=new Float32Array(x.getMappedRange()),v=1/c.common.scaleW,y=1/c.common.scaleH,w={},b=0;for(let[e,t]of c.chars.entries())w[t.id]=t,w[t.id].charIndex=e,m[b]=t.x*v,m[b+1]=t.y*y,m[b+2]=t.width*v,m[b+3]=t.height*y,m[b+4]=t.width,m[b+5]=t.height,m[b+6]=t.xoffset,m[b+7]=-t.yoffset,b+=8;x.unmap();let S=Q5.device.createBindGroup({label:"msdf font bind group",layout:o,entries:[{binding:0,resource:_.createView()},{binding:1,resource:n},{binding:2,resource:{buffer:x}}]}),M=new Map;if(c.kernings)for(let e of c.kernings){let t=M.get(e.first);t||(t=new Map,M.set(e.first,t)),t.set(e.second,e.amount)}e._font=new s(S,c.common.lineHeight,w,M),e._font.index=e._fonts.length,e._fonts.push(e._font),l[a]=e._font,t._preloadCount--,i&&i(a)})(r,i,a),i},e._textSize=18,e._textAlign="left",e._textBaseline="alphabetic";let c=!1,h=22.5,u=4.5,p=1.25;e.textFont=t=>{e._font=l[t]},e.textSize=t=>{e._textSize=t,c||(h=t*p,u=h-t)},e.textLeading=t=>{e._font.lineHeight=h=t,u=h-e._textSize,p=h/e._textSize,c=!0},e.textAlign=(t,r)=>{e._textAlign=t,r&&(e._textBaseline=r)},e._charStack=[],e._textStack=[];let f,_=(e,t,r)=>{let a=0,n=0,o=0,i=0,s=0,l=[],d=t.charCodeAt(0);for(let c=0;c<t.length;++c){let h=d;switch(d=c<t.length-1?t.charCodeAt(c+1):-1,h){case 10:l.push(n),i++,a=Math.max(a,n),n=0,o-=e.lineHeight*p;break;case 13:break;case 32:n+=e.getXAdvance(h);break;case 9:n+=2*e.getXAdvance(h);break;default:r&&r(n,o,i,e.getChar(h)),n+=e.getXAdvance(h,d),s++}}return l.push(n),a=Math.max(a,n),{width:a,height:l.length*e.lineHeight*p,lineWidths:l,printedCharCount:s}};e.text=(t,r,a,n,o)=>{if(!e._font)return void(navigator.onLine&&!f&&(f=!0,e.loadFont("https://q5js.org/fonts/YaHei-msdf.json")));if(t.length>n){let e=[],r=0;for(;r<t.length;){let a=r+n;if(a>=t.length){e.push(t.slice(r));break}let o=t.lastIndexOf(" ",a);(-1==o||o<r)&&(o=a),e.push(t.slice(r,o)),r=o+1}t=e.join("\n")}let i;for(let e=0;e<t.length;e++){switch(t[e]){case"\n":i=!0;case"\r":case"\t":case" ":0}}let s,l=[],d=e._textAlign,c=e._textBaseline,u=e._textStack.length,p=0;if("left"!=d||i){s=_(e._font,t);let r=0;"alphabetic"==c?a-=e._textSize:"center"==c?r=.5*s.height:"bottom"==c&&(r=s.height),_(e._font,t,((e,t,a,n)=>{let o=0;"center"==d?o=-.5*s.width- -.5*(s.width-s.lineWidths[a]):"right"==d&&(o=s.width-s.lineWidths[a]),l[p]=e+o,l[p+1]=t+r,l[p+2]=n.charIndex,l[p+3]=u,p+=4}))}else s=_(e._font,t,((e,t,r,a)=>{l[p]=e,l[p+1]=t,l[p+2]=a.charIndex,l[p+3]=u,p+=4})),"alphabetic"==c?a-=e._textSize:"center"==c?a-=.5*e._textSize:"bottom"==c&&(a-=h);e._charStack.push(l);let g=[];e._matrixDirty&&e._saveMatrix(),g[0]=r,g[1]=-a,g[2]=e._textSize/44,g[3]=e._transformIndex,g[4]=e._fillSet?e._fill:0,g[5]=e._stroke,e._textStack.push(g),e.drawStack.push(2,s.printedCharCount,e._font.index)},e.textWidth=t=>e._font?_(e._font,t).width:0,e.createTextImage=(t,r,a)=>{if(d.textSize(e._textSize),e._doFill){let t=4*e._fill;d.fill(colorStack.slice(t,t+4))}if(e._doStroke){let t=4*e._stroke;d.stroke(colorStack.slice(t,t+4))}let n=d.createTextImage(t,r,a);if(null==n.canvas.textureIndex)e._createTexture(n);else if(n.modified){let t=n.canvas,r=[t.width,t.height,1],a=e._textures[t.textureIndex];Q5.device.queue.copyExternalImageToTexture({source:t},{texture:a,colorSpace:e.canvas.colorSpace},r),n.modified=!1}return n},e.textImage=(t,r,a)=>{"string"==typeof t&&(t=e.createTextImage(t));let n=e._imageMode;e._imageMode="corner";let o=e._textAlign;"center"==o?r-=t.canvas.hw:"right"==o&&(r-=t.width);let i=e._textBaseline;"alphabetic"==i?a-=t._leading:"center"==i?a-=t._middle:"bottom"==i?a-=t._bottom:"top"==i&&(a-=t._top),e.image(t,r,a),e._imageMode=n},e._hooks.preRender.push((()=>{if(!e._charStack.length)return;let t=0;for(let r of e._charStack)t+=4*r.length;let r=Q5.device.createBuffer({size:t,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(r.getMappedRange()).set(e._charStack.flat()),r.unmap();let n=6*e._textStack.length*4,o=Q5.device.createBuffer({label:"textBuffer",size:n,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:!0});new Float32Array(o.getMappedRange()).set(e._textStack.flat()),o.unmap(),e._textBindGroup=Q5.device.createBindGroup({label:"msdf text bind group",layout:a,entries:[{binding:0,resource:{buffer:r}},{binding:1,resource:{buffer:o}}]})})),e._hooks.postRender.push((()=>{e._charStack.length=0,e._textStack.length=0}))};
@@ -117,10 +117,16 @@ Q5.renderers.q2d.image = ($, q) => {
117
117
  $.ctx.drawImage(drawable, sx * pd, sy * pd, sw, sh, dx, dy, dw, dh);
118
118
 
119
119
  if ($._tint) {
120
+ $.ctx.shadowBlur = 0;
121
+ $.ctx.shadowOffsetX = 0;
122
+ $.ctx.shadowOffsetY = 0;
123
+
124
+ let fill = $.ctx.fillStyle;
120
125
  $.ctx.globalCompositeOperation = 'multiply';
121
126
  $.ctx.fillStyle = $._tint.toString();
122
127
  $.ctx.fillRect(dx, dy, dw, dh);
123
128
  $.ctx.globalCompositeOperation = 'source-over';
129
+ $.ctx.fillStyle = fill;
124
130
  }
125
131
  };
126
132
 
@@ -225,8 +231,8 @@ Q5.renderers.q2d.image = ($, q) => {
225
231
  let c = $._getImageData(x * pd, y * pd, 1, 1).data;
226
232
  return [c[0], c[1], c[2], c[3] / 255];
227
233
  }
228
- x = (x || 0) * pd;
229
- y = (y || 0) * pd;
234
+ x = Math.floor(x || 0) * pd;
235
+ y = Math.floor(y || 0) * pd;
230
236
  let _w = (w = w || $.width);
231
237
  let _h = (h = h || $.height);
232
238
  w *= pd;
@@ -240,6 +246,8 @@ Q5.renderers.q2d.image = ($, q) => {
240
246
  };
241
247
 
242
248
  $.set = (x, y, c) => {
249
+ x = Math.floor(x);
250
+ y = Math.floor(y);
243
251
  if (c.canvas) {
244
252
  let old = $._tint;
245
253
  $._tint = null;
package/src/q5-core.js CHANGED
@@ -13,7 +13,7 @@ function Q5(scope, parent, renderer) {
13
13
  $._webgpuFallback = true;
14
14
  $._renderer = 'q2d';
15
15
  } else {
16
- $._renderer = renderer || 'q2d';
16
+ $._renderer = renderer || Q5.render;
17
17
  }
18
18
  $._preloadCount = 0;
19
19
 
@@ -277,6 +277,8 @@ function Q5(scope, parent, renderer) {
277
277
  else setTimeout(_start, 32);
278
278
  }
279
279
 
280
+ Q5.render = 'q2d';
281
+
280
282
  Q5.renderers = {};
281
283
  Q5.modules = {};
282
284
 
package/src/readme.md CHANGED
@@ -10,11 +10,14 @@ These modules are included in the default "q5.js" bundle:
10
10
 
11
11
  ```html
12
12
  <script src="https://q5js.org/src/q5-core.js"></script>
13
+ <script src="https://q5js.org/src/q5-canvas.js"></script>
14
+
13
15
  <script src="https://q5js.org/src/q5-2d-canvas.js"></script>
14
16
  <script src="https://q5js.org/src/q5-2d-drawing.js"></script>
15
17
  <script src="https://q5js.org/src/q5-2d-image.js"></script>
16
18
  <script src="https://q5js.org/src/q5-2d-soft-filters.js"></script>
17
19
  <script src="https://q5js.org/src/q5-2d-text.js"></script>
20
+
18
21
  <script src="https://q5js.org/src/q5-ai.js"></script>
19
22
  <script src="https://q5js.org/src/q5-color.js"></script>
20
23
  <script src="https://q5js.org/src/q5-display.js"></script>
@@ -23,6 +26,11 @@ These modules are included in the default "q5.js" bundle:
23
26
  <script src="https://q5js.org/src/q5-sound.js"></script>
24
27
  <script src="https://q5js.org/src/q5-util.js"></script>
25
28
  <script src="https://q5js.org/src/q5-vector.js"></script>
29
+
30
+ <script src="https://q5js.org/src/q5-webgpu-canvas.js"></script>
31
+ <script src="https://q5js.org/src/q5-webgpu-drawing.js"></script>
32
+ <script src="https://q5js.org/src/q5-webgpu-image.js"></script>
33
+ <script src="https://q5js.org/src/q5-webgpu-text.js"></script>
26
34
  ```
27
35
 
28
36
  Additional modules:
@@ -33,15 +41,6 @@ Additional modules:
33
41
  <script src="https://q5js.org/src/q5-sensors.js"></script>
34
42
  ```
35
43
 
36
- WebGPU rendering modules are in development:
37
-
38
- ```html
39
- <script src="https://q5js.org/src/q5-webgpu-canvas.js"></script>
40
- <script src="https://q5js.org/src/q5-webgpu-drawing.js"></script>
41
- <script src="https://q5js.org/src/q5-webgpu-image.js"></script>
42
- <script src="https://q5js.org/src/q5-webgpu-text.js"></script>
43
- ```
44
-
45
44
  # Module Info
46
45
 
47
46
  - [q5.js Source Documentation](#q5js-source-documentation)
@@ -114,30 +113,32 @@ Image based features in this module require the q5-2d-image module.
114
113
 
115
114
  > ⚠️ Experimental features! ⚠️
116
115
 
117
- To use q5's WebGPU renderer, run `Q5.webgpu()` at the bottom of your sketch.
116
+ [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API) is the successor to WebGL. It's a modern graphics API that provides cross-platform, high-performance access to the GPU.
118
117
 
119
- ```js
120
- function setup() {
121
- createCanvas(200, 200);
122
- noStroke();
123
- }
118
+ When you intend for a sketch to use WebGPU, but WebGPU is not supported on a viewer's browser, q5 will put a warning in the console, apply a compatibility layer, and display the sketch with the fallback q2d renderer. As of November 2024, WebGPU is only supported in Google Chrome and Edge.
124
119
 
125
- function draw() {
126
- clear();
127
- rect(50, 50, 100, 100);
128
- }
120
+ To use the q5 WebGPU renderer, run `Q5.webgpu()` after the creation of any file level variables.
121
+
122
+ ```js
123
+ let x = 0;
124
+ let y = 0;
129
125
 
130
126
  Q5.webgpu();
127
+
128
+ function setup() {
129
+ createCanvas(200, 100);
130
+ circle(x, y, 80);
131
+ }
131
132
  ```
132
133
 
133
- WebGPU has different default settings compared to q5's q2d renderer and p5's P2D and WEBGL modes.
134
+ q5 WebGPU differences:
134
135
 
135
- - Explicit use of `createCanvas` is required before anything can be drawn.
136
- - The default color mode is RGB in 0-1 "float" format: `colorMode(RGB, 1)`.
137
136
  - The origin of the canvas (0, 0) is in the center, not the top left.
138
- - Mouse and touch coordinates correspond to canvas pixels (unlike in p5 WEBGL mode).
137
+ - The default color mode is RGB in 0-1 "float" format: `colorMode(RGB, 1)`.
138
+ - Strokes are implemented but `strokeJoin` is shimmed.
139
+ - `Q5.webgpu` is an async function, so enabling [top level global mode](https://github.com/q5js/q5.js/wiki/Top%E2%80%90Level-Global-Mode) is a bit more complex.
139
140
 
140
- The sketches you create with the q5-webgpu renderer will still display if WebGPU is not supported on a viewer's browser. q5 will put a warning in the console and apply a compatibility layer to display sketches with the fallback q2d renderer.
141
+ Note that unlike in p5's WebGL mode, mouse and touch coordinates align with canvas pixel values.
141
142
 
142
143
  ## webgpu-drawing
143
144
 
@@ -145,11 +146,11 @@ The sketches you create with the q5-webgpu renderer will still display if WebGPU
145
146
 
146
147
  q5's WebGPU renderer drawing functions like `rect` don't immediately draw on the canvas. Instead, they prepare vertex and color data to be sent to the GPU in bulk, which occurs after the user's `draw` function and any post-draw functions are run. This approach better utilizes the GPU, so it doesn't have to repeatedly wait for the CPU to send small chunks of data that describe each individual shape. It's the main reason why WebGPU is faster than Canvas2D.
147
148
 
148
- Rounded rectangles, stroke modes, and functions for drawing curves like `bezier` and `curve` are not implemented yet.
149
+ Rounded rectangles, stroke modes, and functions for drawing curves are not implemented yet.
149
150
 
150
151
  ## webgpu-image
151
152
 
152
- Using `image` to drawn a subsection of an image and most blending modes are not yet implemented.
153
+ Most filters and blending modes are not implemented yet.
153
154
 
154
155
  ## webgpu-text
155
156
 
@@ -184,6 +185,8 @@ You can choose a custom set of characters and convert fonts to MSDF format by us
184
185
  Fonts must be in MSDF format with the file ending "-msdf.json".
185
186
 
186
187
  ```js
188
+ Q5.webgpu();
189
+
187
190
  function preload() {
188
191
  loadFont('arial-msdf.json');
189
192
  }
@@ -196,8 +199,6 @@ function draw() {
196
199
  fill(0.71, 0.92, 1);
197
200
  text('Hello, World!', mouseX, mouseY);
198
201
  }
199
-
200
- Q5.webgpu();
201
202
  ```
202
203
 
203
204
  ### Displaying Emojis
@@ -205,6 +206,8 @@ Q5.webgpu();
205
206
  Full color emoji characters can't be rendered using the MSDF technique, so draw them using `textImage`.
206
207
 
207
208
  ```js
209
+ Q5.webgpu();
210
+
208
211
  function setup() {
209
212
  createCanvas(200, 200);
210
213
  textSize(100);
@@ -214,8 +217,6 @@ function draw() {
214
217
  textAlign(CENTER, CENTER);
215
218
  textImage('🐶', 0, 0);
216
219
  }
217
-
218
- Q5.webgpu();
219
220
  ```
220
221
 
221
222
  You can also use `createTextImage` and display it with `textImage`.
@@ -244,4 +245,4 @@ Adds additional noise functions to q5.
244
245
 
245
246
  `SimplexNoise` is a simplex noise implementation in JavaScript by Tezumie. Kevin Perlin's patent on simplex noise expired in 2022. Simplex noise is slightly faster but arguably less visually appealing than perlin noise.
246
247
 
247
- `BlockyNoise` is similar to p5's default `noise` function, which is a bit notorious in the gen art community for not actually being perlin noise, despite its claims to be. It looks closer to value noise but is not a standard implementation of that either. When visualized in 2d it's a bit blocky at 1 octave, hence the name. This algorithm is however, very good at outputting a variety of values from less inputs, even just a single param.
248
+ `BlockyNoise` is similar to p5's default `noise` function, which is a bit notorious in the gen art community for not actually being perlin noise, despite its claims to be. It looks closer to value noise but is not a standard implementation of that either. When visualized in 2d it's a bit blocky at 1 octave, hence the name. This algorithm is however, very good at outputting a variety of values from less than 3 inputs, even from a single parameter.