q5 2.18.1 → 2.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/q5.d.ts +96 -38
  4. package/q5.js +41 -25
  5. package/q5.min.js +1 -1
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  ## Visit [q5js.org](https://q5js.org)! 🌟
4
4
 
5
- [q5.js](https://q5js.org) is a spiritual successor to the [p5.js][] and [Processing Java][] graphics libraries.
5
+ [q5.js](https://q5js.org) is a sequel to the [p5.js][] and [Processing Java][] graphics libraries.
6
6
 
7
7
  - performance optimized for interactive art 🚀
8
8
  - includes a brand new renderer powered by WebGPU 💪
9
9
  - up to 32x faster than p5.js 🏎️
10
- - HDR color support 🌈
10
+ - beginner friendly API and documentation 📚
11
11
  - compatible with popular addons, including [p5.sound][] and [p5play][] 🎮
12
- - no dependencies, less than 100kb minified 📦
12
+ - no dependencies, ~100kb minified 📦
13
13
  - LGPL licensed (just like p5.js) 🆓
14
14
 
15
15
  q5.js was designed to make creative coding fun and accessible for a new generation of artists, designers, educators, and beginners. 🤝
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.18.1",
3
+ "version": "2.18.3",
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
@@ -823,12 +823,12 @@ rect(20, 20, 60, 60);
823
823
  /** 💻
824
824
  * Customize how your canvas is presented.
825
825
  * @param {string} mode
826
- * - "normal": (default) no styling to canvas or its parent element
827
- * - "centered": canvas will be centered horizontally and vertically within its parent and if the window is smaller than the canvas, the canvas will be resized to avoid clipping
828
- * - "maxed": canvas will fill the parent element, with letterboxing if necessary to preserve its aspect ratio
826
+ * - "normal": (default) canvas is not repositioned
827
+ * - "centered": canvas is moved to the center of its parent
828
+ * - "maxed": canvas will be scaled to fill the parent element, with letterboxing if necessary to preserve its aspect ratio
829
829
  * @param {string} renderQuality
830
- * - "smooth": (default) no change to the default render quality
831
- * - "pixelated": runs `pixelDensity(1)` and `noSmooth()` then sets the canvas CSS styles `image-rendering: pixelated` and `font-smooth: never`
830
+ * - "smooth": (default) smooth upscaling if the canvas is scaled
831
+ * - "pixelated": pixels rendered as sharp squares
832
832
  * @param {number} scale can also be given as a string (for example "x2")
833
833
  * @example
834
834
  createCanvas(50, 25);
@@ -1131,13 +1131,15 @@ point(125, 50);
1131
1131
 
1132
1132
  // 📑 dom
1133
1133
 
1134
+ /** 📑
1135
+ * The Document Object Model (DOM) is an interface for
1136
+ * creating and editing web pages with JavaScript.
1137
+ */
1138
+
1134
1139
  /** 📑
1135
1140
  * Creates a new HTML element and adds it to the page. `createEl` is
1136
1141
  * an alias.
1137
1142
  *
1138
- * The element is part of the DOM (Document Object Model), an interface for
1139
- * creating and editing web pages with JavaScript.
1140
- *
1141
1143
  * Modify the element's CSS [`style`](https://developer.mozilla.org/docs/Web/API/HTMLElement/style) to change its appearance.
1142
1144
  *
1143
1145
  * Use [`addEventListener`](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) to respond to events such as:
@@ -1255,6 +1257,9 @@ let img = createImg('/assets/p5play_logo.webp')
1255
1257
  *
1256
1258
  * Use the `value` property to get or set the input's value.
1257
1259
  *
1260
+ * Use the `placeholder` property to set label text that appears
1261
+ * inside the input when it's empty.
1262
+ *
1258
1263
  * See MDN's [input documentation](https://developer.mozilla.org/docs/Web/HTML/Element/input#input_types) for the full list of input types.
1259
1264
  * @param {string} [value] initial value
1260
1265
  * @param {string} [type] text input type, can be 'text', 'password', 'email', 'number', 'range', 'search', 'tel', 'url'
@@ -1781,9 +1786,9 @@ text(info, 12, 30, 20, 6);
1781
1786
  *
1782
1787
  * In q5 WebGPU, fonts must be in MSDF format with the file ending
1783
1788
  * "-msdf.json". If no font is loaded before `text` is run, then
1784
- * the default font, Microsoft YaHei, is loaded:
1785
- * https://q5js.org/fonts/YaHei-msdf.json
1786
- * https://q5js.org/fonts/YaHei.png
1789
+ * the default sans serif font, Microsoft YaHei, is loaded:
1790
+ * https://q5js.org/fonts/sans-serif-msdf.json
1791
+ * https://q5js.org/fonts/sans-serif.png
1787
1792
  * @param {string} url uRL of the font to load
1788
1793
  * @param {(font: FontFace) => void} [cb] optional callback function that receives the font name as an argument once the font is loaded
1789
1794
  * @returns {FontFace} font
@@ -2296,6 +2301,53 @@ noCursor();
2296
2301
 
2297
2302
  // 🧮 math
2298
2303
 
2304
+ /** 🧮
2305
+ * Generates random numbers. If no arguments are provided, returns a random number between 0 and 1.
2306
+ * If one number argument is provided, returns a random number between 0 and the provided value.
2307
+ * If two number arguments are provided, returns a random number between the two values.
2308
+ * If an array is provided, returns a random element from the array.
2309
+ * @param {number | any[]} [low] lower bound (inclusive) or an array
2310
+ * @param {number} [high] upper bound (exclusive)
2311
+ * @returns {number | any} a random number or element
2312
+ * @example
2313
+ createCanvas(200);
2314
+ background(200);
2315
+ frameRate(5);
2316
+
2317
+ function draw() {
2318
+ circle(100, 100, random(200));
2319
+ }
2320
+ */
2321
+ function random(low?: number | any[], high?: number): number | any;
2322
+
2323
+ /** 🧮
2324
+ * Generates a random number within the range of the canvas width.
2325
+ * @param {number} [margin] distance to extend (positive) or contract (negative) the range from canvas edges
2326
+ * @returns {number} random x value
2327
+ * @example
2328
+ createCanvas(200);
2329
+ background(200);
2330
+
2331
+ function draw() {
2332
+ circle(randomX(), 100, random(50));
2333
+ }
2334
+ */
2335
+ function randomX(margin?: number): number;
2336
+
2337
+ /** 🧮
2338
+ * Generates a random number within the range of the canvas height.
2339
+ * @param {number} [margin] distance to extend (positive) or contract (negative) the range from canvas edges
2340
+ * @returns {number} random y value
2341
+ * @example
2342
+ createCanvas(200);
2343
+ background(200);
2344
+
2345
+ function draw() {
2346
+ circle(100, randomY(), random(50));
2347
+ }
2348
+ */
2349
+ function randomY(margin?: number): number;
2350
+
2299
2351
  /** 🧮
2300
2352
  * Calculates the distance between two points.
2301
2353
  *
@@ -2386,17 +2438,6 @@ noCursor();
2386
2438
  */
2387
2439
  function randomSeed(seed: number): void;
2388
2440
 
2389
- /** 🧮
2390
- * Generates random numbers. If no arguments are provided, returns a random number between 0 and 1.
2391
- * If one number argument is provided, returns a random number between 0 and the provided value.
2392
- * If two number arguments are provided, returns a random number between the two values.
2393
- * If an array is provided, returns a random element from the array.
2394
- * @param {number | any[]} [a] lower bound (inclusive) or an array
2395
- * @param {number} [b] upper bound (exclusive)
2396
- * @returns {number | any} a random number or element
2397
- */
2398
- function random(a?: number | any[], b?: number): number | any;
2399
-
2400
2441
  /** 🧮
2401
2442
  * Sets the random number generation method.
2402
2443
  * @param {any} method method to use for random number generation
@@ -2419,16 +2460,13 @@ noCursor();
2419
2460
 
2420
2461
  /** 🧮
2421
2462
  * Sets the noise generation mode.
2463
+ *
2464
+ * Only the default mode, "perlin", is included in q5.js. Use of the
2465
+ * other modes requires the q5-noiser module.
2422
2466
  * @param {'perlin' | 'simplex' | 'blocky'} mode noise generation mode
2423
2467
  */
2424
2468
  function noiseMode(mode: 'perlin' | 'simplex' | 'blocky'): void;
2425
2469
 
2426
- /** 🧮
2427
- * Sets the seed value for noise generation.
2428
- * @param {number} seed seed value
2429
- */
2430
- function noiseSeed(seed: number): void;
2431
-
2432
2470
  /** 🧮
2433
2471
  * Generates a noise value based on the x, y, and z inputs.
2434
2472
  * @param {number} [x] x-coordinate input
@@ -2438,6 +2476,12 @@ noCursor();
2438
2476
  */
2439
2477
  function noise(x?: number, y?: number, z?: number): number;
2440
2478
 
2479
+ /** 🧮
2480
+ * Sets the seed value for noise generation.
2481
+ * @param {number} seed seed value
2482
+ */
2483
+ function noiseSeed(seed: number): void;
2484
+
2441
2485
  /** 🧮
2442
2486
  * Sets the level of detail for noise generation.
2443
2487
  * @param {number} lod level of detail (number of octaves)
@@ -2466,23 +2510,30 @@ noCursor();
2466
2510
  // 🎞️ record
2467
2511
 
2468
2512
  /** 🎞️
2469
- * Creates a recorder. Simply hit record to start recording!
2513
+ * q5.js has a built-in canvas recorder powered by
2514
+ * [`MediaRecorder`](https://developer.mozilla.org/docs/Web/API/MediaRecorder/MediaRecorder). It's 5-10x faster than p5.capture.
2470
2515
  *
2471
2516
  * Recording large canvases is an intensive process, so your
2472
2517
  * computer may not be able to do it in real time. That's okay,
2473
2518
  * the resulting video will playback at your sketch's target
2474
2519
  * frame rate.
2475
2520
  *
2476
- * In cases where real time interaction is a priority, consider
2477
- * reducing the canvas' size, frame rate, and/or recording
2478
- * quality.
2521
+ * If real time interaction while recording is a priority,
2522
+ * consider reducing the canvas' size, frame rate, and/or
2523
+ * recording quality.
2524
+ *
2525
+ * HDR video encoding is not yet supported by any web browser.
2526
+ * For that and other advanced recording features, consider using
2527
+ * a screen capture tool like [OBS Studio](https://obsproject.com).
2528
+ */
2529
+
2530
+ /** 🎞️
2531
+ * Creates a recorder. Simply hit record to start recording!
2479
2532
  *
2480
2533
  * `format` and `quality` properties are set
2481
2534
  * automatically based on the height of the canvas. They can be
2482
2535
  * changed via the UI or programmatically.
2483
2536
  *
2484
- * The recorder uses the [`MediaRecorder`](https://developer.mozilla.org/docs/Web/API/MediaRecorder/MediaRecorder) API, which
2485
- * unfortunately can't encode HDR video.
2486
2537
  * @returns {HTMLElement} a recorder, q5 DOM element
2487
2538
  * @example
2488
2539
  createCanvas(200);
@@ -2490,7 +2541,7 @@ createCanvas(200);
2490
2541
  createRecorder();
2491
2542
 
2492
2543
  function draw() {
2493
- circle(mouseX, random(canvas.h), 10);
2544
+ circle(mouseX, random(height), 10);
2494
2545
  }
2495
2546
  */
2496
2547
  function createRecorder(): HTMLElement;
@@ -2517,7 +2568,7 @@ function draw() {
2517
2568
  * @param {string} fileName
2518
2569
  * @example
2519
2570
  function draw() {
2520
- square(mouseX, random(canvas.h), 10);
2571
+ square(mouseX, random(height), 10);
2521
2572
  }
2522
2573
 
2523
2574
  function mousePressed() {
@@ -2535,8 +2586,15 @@ function mousePressed() {
2535
2586
  // 🔊 sound
2536
2587
 
2537
2588
  /** 🔊
2538
- * Loads audio data from a file and returns a `Q5.Sound` object that
2539
- * provides low latency sound mixing powered by WebAudio.
2589
+ * q5.js includes low latency sound playback and basic mixing powered
2590
+ * by WebAudio.
2591
+ *
2592
+ * For audio filtering, synthesis, and analysis, consider using
2593
+ * [p5.sound](https://p5js.org/reference/p5.sound/).
2594
+ */
2595
+
2596
+ /** 🔊
2597
+ * Loads audio data from a file and returns a `Q5.Sound` object.
2540
2598
  *
2541
2599
  * Use functions like `play`, `pause`, and `stop` to
2542
2600
  * control playback. Note that sounds can only be played after the
package/q5.js CHANGED
@@ -223,6 +223,8 @@ function Q5(scope, parent, renderer) {
223
223
  $.postProcess ??= () => {};
224
224
 
225
225
  let userFns = [
226
+ 'setup',
227
+ 'postProcess',
226
228
  'mouseMoved',
227
229
  'mousePressed',
228
230
  'mouseReleased',
@@ -2082,8 +2084,8 @@ Q5.modules.ai = ($) => {
2082
2084
  let parts = errFile.split(':');
2083
2085
  let lineNum = parseInt(parts.at(-2));
2084
2086
  if (askAI) lineNum++;
2085
- parts[3] = parts[3].split(')')[0];
2086
- let fileUrl = parts.slice(0, 2).join(':');
2087
+ parts[parts.length - 1] = parts.at(-1).split(')')[0];
2088
+ let fileUrl = parts.slice(0, -2).join(':');
2087
2089
  let fileBase = fileUrl.split('/').at(-1);
2088
2090
 
2089
2091
  try {
@@ -2107,7 +2109,7 @@ Q5.modules.ai = ($) => {
2107
2109
  askAI && e.message.length > 10 ? e.message.slice(10) : 'Whats+wrong+with+this+line%3F+short+answer';
2108
2110
 
2109
2111
  let url =
2110
- 'https://chatgpt.com/?q=q5.js+' +
2112
+ 'https://chatgpt.com/?q=using+q5.js+not+p5.js+' +
2111
2113
  question +
2112
2114
  (askAI ? '' : '%0A%0A' + encodeURIComponent(e.name + ': ' + e.message)) +
2113
2115
  '%0A%0ALine%3A+' +
@@ -3167,6 +3169,14 @@ Q5.modules.math = ($, q) => {
3167
3169
  }
3168
3170
  };
3169
3171
 
3172
+ if ($._renderer == 'c2d' && !$._webgpuFallback) {
3173
+ $.randomX = (v = 0) => $.random(-v, $.canvas.w + v);
3174
+ $.randomY = (v = 0) => $.random(-v, $.canvas.h + v);
3175
+ } else {
3176
+ $.randomX = (v = 0) => $.random(-$.canvas.hw - v, $.canvas.hw + v);
3177
+ $.randomY = (v = 0) => $.random(-$.canvas.hh - v, $.canvas.hh + v);
3178
+ }
3179
+
3170
3180
  $.randomGenerator = (method) => {
3171
3181
  if (method == $.LCG) rng1 = lcg();
3172
3182
  else if (method == $.SHR3) rng1 = shr3();
@@ -3455,7 +3465,7 @@ Q5.PerlinNoise = class extends Q5.Noise {
3455
3465
  return (total / maxAmp + 1) / 2;
3456
3466
  }
3457
3467
  };
3458
- Q5.modules.record = ($) => {
3468
+ Q5.modules.record = ($, q) => {
3459
3469
  let rec, btn0, btn1, timer, formatSelect, qualitySelect;
3460
3470
 
3461
3471
  $.recording = false;
@@ -3549,26 +3559,27 @@ Q5.modules.record = ($) => {
3549
3559
  }
3550
3560
  }
3551
3561
 
3552
- formatSelect = $.createSelect();
3562
+ formatSelect = $.createSelect('format');
3553
3563
  for (const name in rec.formats) {
3554
3564
  formatSelect.option(name, rec.formats[name]);
3555
3565
  }
3566
+ formatSelect.title = 'Video Format';
3556
3567
  rec.append(formatSelect);
3557
3568
 
3558
- // prettier-ignore
3559
- rec.qualityPresets = {
3560
- SD: 10000000, // 10 Mbps
3561
- HD: 16000000, // 16 Mbps
3562
- FHD: 22000000, // 22 Mbps
3563
- QHD: 28000000, // 28 Mbps
3564
- '4K': 48000000, // 48 Mbps
3565
- '8K': 75000000 // 75 Mbps
3569
+ let qMult = {
3570
+ min: 0.1,
3571
+ low: 0.25,
3572
+ mid: 0.5,
3573
+ high: 0.75,
3574
+ ultra: 0.9,
3575
+ max: 1
3566
3576
  };
3567
3577
 
3568
- qualitySelect = $.createSelect();
3569
- for (let name in rec.qualityPresets) {
3570
- qualitySelect.option(name);
3578
+ qualitySelect = $.createSelect('quality');
3579
+ for (let name in qMult) {
3580
+ qualitySelect.option(name, qMult[name]);
3571
3581
  }
3582
+ qualitySelect.title = 'Video Quality';
3572
3583
  rec.append(qualitySelect);
3573
3584
 
3574
3585
  rec.encoderSettings = {};
@@ -3578,7 +3589,7 @@ Q5.modules.record = ($) => {
3578
3589
  }
3579
3590
 
3580
3591
  function changeQuality() {
3581
- rec.encoderSettings.videoBitsPerSecond = rec.qualityPresets[qualitySelect.value];
3592
+ rec.encoderSettings.videoBitsPerSecond = maxVideoBitRate * qualitySelect.value;
3582
3593
  }
3583
3594
 
3584
3595
  formatSelect.addEventListener('change', changeFormat);
@@ -3587,8 +3598,8 @@ Q5.modules.record = ($) => {
3587
3598
  Object.defineProperty(rec, 'quality', {
3588
3599
  get: () => qualitySelect.selected,
3589
3600
  set: (v) => {
3590
- v = v.toUpperCase();
3591
- if (rec.qualityPresets[v]) {
3601
+ v = v.toLowerCase();
3602
+ if (qMult[v]) {
3592
3603
  qualitySelect.selected = v;
3593
3604
  changeQuality();
3594
3605
  }
@@ -3607,11 +3618,15 @@ Q5.modules.record = ($) => {
3607
3618
  });
3608
3619
 
3609
3620
  let h = $.canvas.height;
3610
- rec.quality = h >= 4320 ? '8K' : h >= 2160 ? '4K' : h >= 1440 ? 'QHD' : h >= 1080 ? 'FHD' : h >= 720 ? 'HD' : 'SD';
3611
3621
 
3612
3622
  if (h >= 1440 && rec.formats.VP9) rec.format = 'VP9';
3613
3623
  else rec.format = 'H.264';
3614
3624
 
3625
+ let maxVideoBitRate =
3626
+ (h >= 4320 ? 128 : h >= 2160 ? 75 : h >= 1440 ? 36 : h >= 1080 ? 28 : h >= 720 ? 22 : 16) * 1000000;
3627
+
3628
+ rec.quality = 'high';
3629
+
3615
3630
  btn0.addEventListener('click', () => {
3616
3631
  if (!$.recording) start();
3617
3632
  else if (!rec.paused) $.pauseRecording();
@@ -3652,7 +3667,7 @@ Q5.modules.record = ($) => {
3652
3667
  });
3653
3668
 
3654
3669
  rec.mediaRecorder.start();
3655
- $.recording = true;
3670
+ q.recording = true;
3656
3671
  rec.paused = false;
3657
3672
  rec.classList.add('recording');
3658
3673
 
@@ -3673,7 +3688,7 @@ Q5.modules.record = ($) => {
3673
3688
 
3674
3689
  rec.resetTimer();
3675
3690
  rec.mediaRecorder.stop();
3676
- $.recording = false;
3691
+ q.recording = false;
3677
3692
  rec.paused = false;
3678
3693
  rec.classList.remove('recording');
3679
3694
  }
@@ -3745,7 +3760,7 @@ Q5.modules.record = ($) => {
3745
3760
  $.deleteRecording = () => {
3746
3761
  stop();
3747
3762
  resetUI();
3748
- $.recording = false;
3763
+ q.recording = false;
3749
3764
  };
3750
3765
 
3751
3766
  $.saveRecording = async (fileName) => {
@@ -3770,7 +3785,8 @@ Q5.modules.record = ($) => {
3770
3785
  a.target = iframe.name;
3771
3786
  a.href = dataUrl;
3772
3787
  fileName ??=
3773
- 'recording ' +
3788
+ document.title +
3789
+ ' ' +
3774
3790
  new Date()
3775
3791
  .toLocaleString(undefined, { hour12: false })
3776
3792
  .replace(',', ' at')
@@ -3788,7 +3804,7 @@ Q5.modules.record = ($) => {
3788
3804
 
3789
3805
  setTimeout(() => URL.revokeObjectURL(dataUrl), 1000);
3790
3806
  resetUI();
3791
- $.recording = false;
3807
+ q.recording = false;
3792
3808
  };
3793
3809
  };
3794
3810
  Q5.modules.sound = ($, q) => {
package/q5.min.js CHANGED
@@ -5,4 +5,4 @@
5
5
  * @license LGPL-3.0
6
6
  * @class Q5
7
7
  */
8
- function Q5(scope,parent,renderer){let $=this;$._q5=true;$._parent=parent;if(renderer=="webgpu-fallback"){$._webgpuFallback=true;$._renderer="c2d"}else{$._renderer=renderer||Q5.render}$._preloadCount=0;let autoLoaded=scope=="auto";scope??="global";if(scope=="auto"){if(!(window.setup||window.update||window.draw))return;scope="global"}$._scope=scope;let globalScope;if(scope=="global"){Q5._hasGlobal=$._isGlobal=true;globalScope=Q5._esm?globalThis:!Q5._server?window:global}let q=new Proxy($,{set:(t,p,v)=>{$[p]=v;if($._isGlobal)globalScope[p]=v;return true}});$.canvas=$.ctx=$.drawingContext=null;$.pixels=[];let looper=null;$.frameCount=0;$.deltaTime=16;$._targetFrameRate=0;$._targetFrameDuration=16.666666666666668;$._frameRate=$._fps=60;$._loop=true;$._hooks={postCanvas:[],preRender:[],postRender:[]};let millisStart=0;$.millis=()=>performance.now()-millisStart;$.noCanvas=()=>{if($.canvas?.remove)$.canvas.remove();$.canvas=0;q.ctx=q.drawingContext=0};if(window){$.windowWidth=window.innerWidth;$.windowHeight=window.innerHeight;$.deviceOrientation=window.screen?.orientation?.type}$._incrementPreload=()=>q._preloadCount++;$._decrementPreload=()=>q._preloadCount--;$.disablePreloadSystem=()=>$._disablePreload=true;$._draw=timestamp=>{let ts=timestamp||performance.now();$._lastFrameTime??=ts-$._targetFrameDuration;if($._didResize){$.windowResized();$._didResize=false}if($._loop)looper=raf($._draw);else if($.frameCount&&!$._redraw)return;if(looper&&$.frameCount){let time_since_last=ts-$._lastFrameTime;if(time_since_last<$._targetFrameDuration-4)return}q.deltaTime=ts-$._lastFrameTime;$._frameRate=1e3/$.deltaTime;q.frameCount++;let pre=performance.now();$.resetMatrix();if($._beginRender)$._beginRender();for(let m of Q5.methods.pre)m.call($);try{$.draw()}catch(e){if(!Q5.disableFriendlyErrors&&$._askAI)$._askAI(e);if(!Q5.errorTolerant)$.noLoop();throw e}for(let m of Q5.methods.post)m.call($);if($._render)$._render();if($._finishRender)$._finishRender();$.postProcess();q.pmouseX=$.mouseX;q.pmouseY=$.mouseY;q.moveX=q.moveY=0;$._lastFrameTime=ts;let post=performance.now();$._fps=Math.round(1e3/(post-pre))};$.noLoop=()=>{$._loop=false;looper=null};$.loop=()=>{$._loop=true;if($._setupDone&&looper==null)$._draw()};$.isLooping=()=>$._loop;$.redraw=(n=1)=>{$._redraw=true;for(let i=0;i<n;i++){$._draw()}$._redraw=false};$.remove=()=>{$.noLoop();$.canvas.remove()};$.frameRate=hz=>{if(hz){$._targetFrameRate=hz;$._targetFrameDuration=1e3/hz}return $._frameRate};$.getTargetFrameRate=()=>$._targetFrameRate||60;$.getFPS=()=>$._fps;$.Element=function(a){this.elt=a};$._elements=[];$.describe=()=>{};$.log=$.print=console.log;for(let m in Q5.modules){Q5.modules[m]($,q)}let r=Q5.renderers[$._renderer];for(let m in r){r[m]($,q)}for(let k in Q5){if(k[1]!="_"&&k[1]==k[1].toUpperCase()){$[k]=Q5[k]}}if(scope=="graphics")return;if(scope=="global"){Object.assign(Q5,$);delete Q5.Q5}if($._webgpuFallback)$.colorMode("rgb",1);for(let m of Q5.methods.init){m.call($)}for(let[n,fn]of Object.entries(Q5.prototype)){if(n[0]!="_"&&typeof $[n]=="function")$[n]=fn.bind($)}if(scope=="global"){let props=Object.getOwnPropertyNames($);for(let p of props){if(p[0]!="_")globalScope[p]=$[p]}}if(typeof scope=="function")scope($);Q5._instanceCount++;let raf=window.requestAnimationFrame||function(cb){const idealFrameTime=$._lastFrameTime+$._targetFrameDuration;return setTimeout((()=>{cb(idealFrameTime)}),idealFrameTime-performance.now())};let t=globalScope||$;$._isTouchAware=t.touchStarted||t.touchMoved||t.mouseReleased;if($._isGlobal){$.preload=t.preload;$.setup=t.setup;$.draw=t.draw;$.postProcess=t.postProcess}$.preload??=()=>{};$.setup??=()=>{};$.draw??=()=>{};$.postProcess??=()=>{};let userFns=["mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","mouseWheel","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let k of userFns){if(!t[k])$[k]=()=>{};else if($._isGlobal){$[k]=event=>{try{return t[k](event)}catch(e){if($._askAI)$._askAI(e);throw e}}}}async function _setup(){$._startDone=true;if($._preloadCount>0)return raf(_setup);millisStart=performance.now();await $.setup();$._setupDone=true;if($.frameCount)return;if($.ctx===null)$.createCanvas(200,200);raf($._draw)}function _start(){try{$.preload();if(!$._startDone)_setup()}catch(e){if($._askAI)$._askAI(e);throw e}}if(autoLoaded)_start();else setTimeout(_start,32)}Q5.render="c2d";Q5.renderers={};Q5.modules={};Q5._server=typeof process=="object";Q5._esm=this===undefined;Q5._instanceCount=0;Q5._friendlyError=(msg,func)=>{if(!Q5.disableFriendlyErrors)console.error(func+": "+msg)};Q5._validateParameters=()=>true;Q5.methods={init:[],pre:[],post:[],remove:[]};Q5.prototype.registerMethod=(m,fn)=>Q5.methods[m].push(fn);Q5.prototype.registerPreloadMethod=(n,fn)=>Q5.prototype[n]=fn[n];if(Q5._server)global.p5??=global.Q5=Q5;if(typeof window=="object")window.p5??=window.Q5=Q5;else global.window=0;function createCanvas(w,h,opt){if(!Q5._hasGlobal){let q=new Q5;q.createCanvas(w,h,opt)}}Q5.version=Q5.VERSION="2.18";if(typeof document=="object"){document.addEventListener("DOMContentLoaded",(()=>{if(!Q5._hasGlobal)new Q5("auto")}))}Q5.modules.canvas=($,q)=>{$._OffscreenCanvas=window.OffscreenCanvas||function(){return document.createElement("canvas")};if(Q5._server){if(Q5._createServerCanvas){q.canvas=Q5._createServerCanvas(100,100)}}else if($._scope=="image"||$._scope=="graphics"){q.canvas=new $._OffscreenCanvas(100,100)}if(!$.canvas){if(typeof document=="object"){q.canvas=document.createElement("canvas");$.canvas.id="q5Canvas"+Q5._instanceCount;$.canvas.classList.add("q5Canvas")}else $.noCanvas()}let c=$.canvas;$.width=200;$.height=200;$._pixelDensity=1;$.displayDensity=()=>window.devicePixelRatio||1;if(c){c.width=200;c.height=200;if($._scope!="image"){c.renderer=$._renderer;c[$._renderer]=true;$._pixelDensity=Math.ceil($.displayDensity())}}$._adjustDisplay=()=>{if(c.style){c.style.width=c.w+"px";c.style.height=c.h+"px"}};$.createCanvas=function(w,h,options){if(typeof w=="object"){options=w;w=null}options??=arguments[3];let opt=Object.assign({},Q5.canvasOptions);if(typeof options=="object")Object.assign(opt,options);if($._scope!="image"){if($._scope=="graphics")$._pixelDensity=this._pixelDensity;else if(window.IntersectionObserver){let wasObserved=false;new IntersectionObserver((e=>{c.visible=e[0].isIntersecting;if(!wasObserved){$._wasLooping=$._loop;wasObserved=true}if(c.visible){if($._wasLooping&&!$._loop)$.loop()}else{$._wasLooping=$._loop;$.noLoop()}})).observe(c)}}$._setCanvasSize(w,h);Object.assign(c,opt);let rend=$._createCanvas(c.w,c.h,opt);if($._hooks){for(let m of $._hooks.postCanvas)m()}if($._beginRender)$._beginRender();return rend};$.createGraphics=function(w,h,opt){let g=new Q5("graphics");opt??={};opt.alpha??=true;opt.colorSpace??=$.canvas.colorSpace;g.createCanvas.call($,w,h,opt);g.defaultWidth=w;g.defaultHeight=h;return g};async function saveFile(data,name,ext){name=name||"untitled";ext=ext||"png";if(ext=="jpg"||ext=="png"||ext=="webp"){if(data instanceof OffscreenCanvas){const blob=await data.convertToBlob({type:"image/"+ext});data=await new Promise((resolve=>{const reader=new FileReader;reader.onloadend=()=>resolve(reader.result);reader.readAsDataURL(blob)}))}else{data=data.toDataURL("image/"+ext)}}else{let type="text/plain";if(ext=="json"){if(typeof data!="string")data=JSON.stringify(data);type="text/json"}data=new Blob([data],{type:type});data=URL.createObjectURL(data)}let a=document.createElement("a");a.href=data;a.download=name+"."+ext;a.click();URL.revokeObjectURL(a.href)}$.save=(a,b,c)=>{if(!a||typeof a=="string"&&(!b||!c&&b.length<5)){c=b;b=a;a=$.canvas}if(c)return saveFile(a,b,c);if(b){b=b.split(".");saveFile(a,b[0],b.at(-1))}else saveFile(a)};$._setCanvasSize=(w,h)=>{if(!w)h??=window.innerHeight;else h??=w;w??=window.innerWidth;$.defaultWidth=c.w=w=Math.ceil(w);$.defaultHeight=c.h=h=Math.ceil(h);c.hw=w/2;c.hh=h/2;c.width=Math.ceil(w*$._pixelDensity);c.height=Math.ceil(h*$._pixelDensity);if(!$._da){q.width=w;q.height=h}else $.flexibleCanvas($._dau);if($.displayMode&&!c.displayMode)$.displayMode();else $._adjustDisplay()};$._setImageSize=(w,h)=>{q.width=c.w=w;q.height=c.h=h;c.hw=w/2;c.hh=h/2;c.width=Math.ceil(w*$._pixelDensity);c.height=Math.ceil(h*$._pixelDensity)};$.defaultImageScale=scale=>{if(!scale)return $._defaultImageScale;return $._defaultImageScale=scale};$.defaultImageScale(.5);if($._scope=="image")return;if(c&&$._scope!="graphics"){c.parent=el=>{if(c.parentElement)c.parentElement.removeChild(c);if(typeof el=="string")el=document.getElementById(el);el.append(c);function parentResized(){if($.frameCount>1){$._didResize=true;$._adjustDisplay()}}if(typeof ResizeObserver=="function"){if($._ro)$._ro.disconnect();$._ro=new ResizeObserver(parentResized);$._ro.observe(el)}else if(!$.frameCount){window.addEventListener("resize",parentResized)}};function addCanvas(){let el=$._parent;el??=document.getElementsByTagName("main")[0];if(!el){el=document.createElement("main");document.body.append(el)}c.parent(el)}if(document.body)addCanvas();else document.addEventListener("DOMContentLoaded",addCanvas)}$.resizeCanvas=(w,h)=>{if(!$.ctx)return $.createCanvas(w,h);if(w==c.w&&h==c.h)return;$._resizeCanvas(w,h)};if(c&&!Q5._createServerCanvas){c.resize=$.resizeCanvas;c.save=$.saveCanvas=$.save}$.pixelDensity=v=>{if(!v||v==$._pixelDensity)return $._pixelDensity;$._pixelDensity=v;$._setCanvasSize(c.w,c.h);return v};$.flexibleCanvas=(unit=400)=>{if(unit){$._da=c.width/(unit*$._pixelDensity);q.width=$._dau=unit;q.height=c.h/c.w*unit}else $._da=0};$._styleNames=["_fill","_stroke","_strokeWeight","_doStroke","_doFill","_strokeSet","_fillSet","_shadow","_doShadow","_shadowOffsetX","_shadowOffsetY","_shadowBlur","_tint","_imageMode","_rectMode","_ellipseMode","_textSize","_textAlign","_textBaseline"];$._styles=[];$.pushStyles=()=>{let styles={};for(let s of $._styleNames)styles[s]=$[s];$._styles.push(styles)};$.popStyles=()=>{let styles=$._styles.pop();for(let s of $._styleNames)$[s]=styles[s]};if(window&&$._scope!="graphics"){window.addEventListener("resize",(()=>{$._didResize=true;q.windowWidth=window.innerWidth;q.windowHeight=window.innerHeight;q.deviceOrientation=window.screen?.orientation?.type}))}};Q5.CENTER="center";Q5.LEFT="left";Q5.RIGHT="right";Q5.TOP="top";Q5.BOTTOM="bottom";Q5.BASELINE="alphabetic";Q5.MIDDLE="middle";Q5.NORMAL="normal";Q5.ITALIC="italic";Q5.BOLD="bold";Q5.BOLDITALIC="italic bold";Q5.ROUND="round";Q5.SQUARE="butt";Q5.PROJECT="square";Q5.MITER="miter";Q5.BEVEL="bevel";Q5.CHORD_OPEN=0;Q5.PIE_OPEN=1;Q5.PIE=2;Q5.CHORD=3;Q5.RADIUS="radius";Q5.CORNER="corner";Q5.CORNERS="corners";Q5.OPEN=0;Q5.CLOSE=1;Q5.LANDSCAPE="landscape";Q5.PORTRAIT="portrait";Q5.BLEND="source-over";Q5.REMOVE="destination-out";Q5.ADD="lighter";Q5.DARKEST="darken";Q5.LIGHTEST="lighten";Q5.DIFFERENCE="difference";Q5.SUBTRACT="subtract";Q5.EXCLUSION="exclusion";Q5.MULTIPLY="multiply";Q5.SCREEN="screen";Q5.REPLACE="copy";Q5.OVERLAY="overlay";Q5.HARD_LIGHT="hard-light";Q5.SOFT_LIGHT="soft-light";Q5.DODGE="color-dodge";Q5.BURN="color-burn";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.SEPIA=9;Q5.BRIGHTNESS=10;Q5.SATURATION=11;Q5.CONTRAST=12;Q5.HUE_ROTATE=13;Q5.P2D="2d";Q5.WEBGL="webgl";Q5.canvasOptions={alpha:false,colorSpace:"display-p3"};if(!window.matchMedia||!matchMedia("(dynamic-range: high) and (color-gamut: p3)").matches){Q5.canvasOptions.colorSpace="srgb"}else Q5.supportsHDR=true;Q5.renderers.c2d={};Q5.renderers.c2d.canvas=($,q)=>{let c=$.canvas;if($.colorMode){$.colorMode(Q5.canvasOptions.colorSpace!="srgb"?"rgb":"srgb",255)}$._createCanvas=function(w,h,options){if(!c){console.error("q5 canvas could not be created. skia-canvas and jsdom packages not found.");return}q.ctx=q.drawingContext=c.getContext("2d",options);if($._scope!="image"){$.ctx.fillStyle=$._fill="white";$.ctx.strokeStyle=$._stroke="black";$.ctx.lineCap="round";$.ctx.lineJoin="miter";$.ctx.textAlign="left";$._strokeWeight=1}$.ctx.scale($._pixelDensity,$._pixelDensity);$.ctx.save();return c};$.clear=()=>{$.ctx.save();$.ctx.resetTransform();$.ctx.clearRect(0,0,$.canvas.width,$.canvas.height);$.ctx.restore()};if($._scope=="image")return;$._resizeCanvas=(w,h)=>{let t={};for(let prop in $.ctx){if(typeof $.ctx[prop]!="function")t[prop]=$.ctx[prop]}delete t.canvas;let o;if($.frameCount>1){o=new $._OffscreenCanvas(c.width,c.height);o.w=c.w;o.h=c.h;let oCtx=o.getContext("2d");oCtx.drawImage(c,0,0)}$._setCanvasSize(w,h);for(let prop in t)$.ctx[prop]=t[prop];$.scale($._pixelDensity);if(o)$.ctx.drawImage(o,0,0,o.w,o.h)};$.fill=function(c){$._doFill=$._fillSet=true;if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}if(c.a<=0)return $._doFill=false}$.ctx.fillStyle=$._fill=c.toString()};$.stroke=function(c){$._doStroke=$._strokeSet=true;if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}if(c.a<=0)return $._doStroke=false}$.ctx.strokeStyle=$._stroke=c.toString()};$.strokeWeight=n=>{if(!n)$._doStroke=false;if($._da)n*=$._da;$.ctx.lineWidth=$._strokeWeight=n||1e-4};$.noFill=()=>$._doFill=false;$.noStroke=()=>$._doStroke=false;$.opacity=a=>$.ctx.globalAlpha=a;$._doShadow=false;$._shadowOffsetX=$._shadowOffsetY=$._shadowBlur=10;$.shadow=function(c){if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}}$.ctx.shadowColor=$._shadow=c.toString();$._doShadow=true;$.ctx.shadowOffsetX||=$._shadowOffsetX;$.ctx.shadowOffsetY||=$._shadowOffsetY;$.ctx.shadowBlur||=$._shadowBlur};$.shadowBox=(offsetX,offsetY,blur)=>{$.ctx.shadowOffsetX=$._shadowOffsetX=offsetX;$.ctx.shadowOffsetY=$._shadowOffsetY=offsetY||offsetX;$.ctx.shadowBlur=$._shadowBlur=blur||0};$.noShadow=()=>{$._doShadow=false;$.ctx.shadowOffsetX=$.ctx.shadowOffsetY=$.ctx.shadowBlur=0};$.translate=(x,y)=>{if($._da){x*=$._da;y*=$._da}$.ctx.translate(x,y)};$.rotate=r=>{if($._angleMode)r=$.radians(r);$.ctx.rotate(r)};$.scale=(x,y)=>{if(x.x){y=x.y;x=x.x}y??=x;$.ctx.scale(x,y)};$.applyMatrix=(a,b,c,d,e,f)=>$.ctx.transform(a,b,c,d,e,f);$.shearX=ang=>$.ctx.transform(1,0,$.tan(ang),1,0,0);$.shearY=ang=>$.ctx.transform(1,$.tan(ang),0,1,0,0);$.resetMatrix=()=>{if($.ctx){$.ctx.resetTransform();$.scale($._pixelDensity);if($._webgpuFallback)$.translate($.canvas.hw,$.canvas.hh)}};$.pushMatrix=()=>$.ctx.save();$.popMatrix=()=>$.ctx.restore();let _popStyles=$.popStyles;$.popStyles=()=>{_popStyles();$.ctx.fillStyle=$._fill;$.ctx.strokeStyle=$._stroke;$.ctx.lineWidth=$._strokeWeight;$.ctx.shadowColor=$._shadow;$.ctx.shadowOffsetX=$._doShadow?$._shadowOffsetX:0;$.ctx.shadowOffsetY=$._doShadow?$._shadowOffsetY:0;$.ctx.shadowBlur=$._doShadow?$._shadowBlur:0};$.push=()=>{$.ctx.save();$.pushStyles()};$.pop=()=>{$.ctx.restore();_popStyles()};$.createCapture=x=>{var vid=document.createElement("video");vid.playsinline="playsinline";vid.autoplay="autoplay";navigator.mediaDevices.getUserMedia(x).then((stream=>{vid.srcObject=stream}));vid.style.position="absolute";vid.style.opacity=1e-5;vid.style.zIndex=-1e3;document.body.append(vid);return vid}};Q5.renderers.c2d.drawing=$=>{$._doStroke=true;$._doFill=true;$._strokeSet=false;$._fillSet=false;$._ellipseMode=Q5.CENTER;$._rectMode=Q5.CORNER;$._curveDetail=20;$._curveAlpha=0;let firstVertex=true;let curveBuff=[];function ink(){if($._doFill)$.ctx.fill();if($._doStroke)$.ctx.stroke()}$.blendMode=x=>$.ctx.globalCompositeOperation=x;$.strokeCap=x=>$.ctx.lineCap=x;$.strokeJoin=x=>$.ctx.lineJoin=x;$.ellipseMode=x=>$._ellipseMode=x;$.rectMode=x=>$._rectMode=x;$.curveDetail=x=>$._curveDetail=x;$.curveAlpha=x=>$._curveAlpha=x;$.curveTightness=x=>$._curveAlpha=x;$.background=function(c){$.ctx.save();$.ctx.resetTransform();$.ctx.globalAlpha=1;if(c.canvas)$.image(c,0,0,$.canvas.width,$.canvas.height);else{if(Q5.Color&&!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}$.ctx.fillStyle=c.toString();$.ctx.fillRect(0,0,$.canvas.width,$.canvas.height)}$.ctx.restore()};$.line=(x0,y0,x1,y1)=>{if($._doStroke){$._da&&(x0*=$._da,y0*=$._da,x1*=$._da,y1*=$._da);$.ctx.beginPath();$.ctx.moveTo(x0,y0);$.ctx.lineTo(x1,y1);$.ctx.stroke()}};const TAU=Math.PI*2;function arc(x,y,w,h,lo,hi,mode){if($._angleMode){lo=$.radians(lo);hi=$.radians(hi)}lo%=TAU;hi%=TAU;if(lo<0)lo+=TAU;if(hi<0)hi+=TAU;if(lo>hi)hi+=TAU;if(lo==hi)return $.ellipse(x,y,w,h);w/=2;h/=2;if(!$._doFill&&mode==$.PIE_OPEN)mode=$.CHORD_OPEN;$.ctx.beginPath();$.ctx.ellipse(x,y,w,h,0,lo,hi);if(mode==$.PIE||mode==$.PIE_OPEN)$.ctx.lineTo(x,y);if($._doFill)$.ctx.fill();if($._doStroke){if(mode==$.PIE||mode==$.CHORD)$.ctx.closePath();if(mode!=$.PIE_OPEN)return $.ctx.stroke();$.ctx.beginPath();$.ctx.ellipse(x,y,w,h,0,lo,hi);$.ctx.stroke()}}$.arc=(x,y,w,h,start,stop,mode)=>{if(start==stop)return $.ellipse(x,y,w,h);if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}mode??=$.PIE_OPEN;if($._ellipseMode==$.CENTER){arc(x,y,w,h,start,stop,mode)}else if($._ellipseMode==$.RADIUS){arc(x,y,w*2,h*2,start,stop,mode)}else if($._ellipseMode==$.CORNER){arc(x+w/2,y+h/2,w,h,start,stop,mode)}else if($._ellipseMode==$.CORNERS){arc((x+w)/2,(y+h)/2,w-x,h-y,start,stop,mode)}};function ellipse(x,y,w,h){$.ctx.beginPath();$.ctx.ellipse(x,y,w/2,h/2,0,0,TAU);ink()}$.ellipse=(x,y,w,h)=>{h??=w;if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}if($._ellipseMode==$.CENTER){ellipse(x,y,w,h)}else if($._ellipseMode==$.RADIUS){ellipse(x,y,w*2,h*2)}else if($._ellipseMode==$.CORNER){ellipse(x+w/2,y+h/2,w,h)}else if($._ellipseMode==$.CORNERS){ellipse((x+w)/2,(y+h)/2,w-x,h-y)}};$.circle=(x,y,d)=>{if($._ellipseMode==$.CENTER){if($._da){x*=$._da;y*=$._da;d*=$._da}$.ctx.beginPath();$.ctx.arc(x,y,d/2,0,TAU);ink()}else $.ellipse(x,y,d,d)};$.point=(x,y)=>{if($._doStroke){if(x.x){y=x.y;x=x.x}if($._da){x*=$._da;y*=$._da}$.ctx.beginPath();$.ctx.moveTo(x,y);$.ctx.lineTo(x,y);$.ctx.stroke()}};function rect(x,y,w,h){if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}$.ctx.beginPath();$.ctx.rect(x,y,w,h);ink()}function roundedRect(x,y,w,h,tl,tr,br,bl){if(tl===undefined){return rect(x,y,w,h)}if(tr===undefined){return roundedRect(x,y,w,h,tl,tl,tl,tl)}if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da;tl*=$._da;tr*=$._da;bl*=$._da;br*=$._da}$.ctx.roundRect(x,y,w,h,[tl,tr,br,bl]);ink()}$.rect=(x,y,w,h=w,tl,tr,br,bl)=>{if($._rectMode==$.CENTER){roundedRect(x-w/2,y-h/2,w,h,tl,tr,br,bl)}else if($._rectMode==$.RADIUS){roundedRect(x-w,y-h,w*2,h*2,tl,tr,br,bl)}else if($._rectMode==$.CORNER){roundedRect(x,y,w,h,tl,tr,br,bl)}else if($._rectMode==$.CORNERS){roundedRect(x,y,w-x,h-y,tl,tr,br,bl)}};$.square=(x,y,s,tl,tr,br,bl)=>$.rect(x,y,s,s,tl,tr,br,bl);$.beginShape=()=>{curveBuff=[];$.ctx.beginPath();firstVertex=true};$.beginContour=()=>{$.ctx.closePath();curveBuff=[];firstVertex=true};$.endContour=()=>{curveBuff=[];firstVertex=true};$.vertex=(x,y)=>{if($._da){x*=$._da;y*=$._da}curveBuff=[];if(firstVertex){$.ctx.moveTo(x,y)}else{$.ctx.lineTo(x,y)}firstVertex=false};$.bezierVertex=(cp1x,cp1y,cp2x,cp2y,x,y)=>{if($._da){cp1x*=$._da;cp1y*=$._da;cp2x*=$._da;cp2y*=$._da;x*=$._da;y*=$._da}curveBuff=[];$.ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)};$.quadraticVertex=(cp1x,cp1y,x,y)=>{if($._da){cp1x*=$._da;cp1y*=$._da;x*=$._da;y*=$._da}curveBuff=[];$.ctx.quadraticCurveTo(cp1x,cp1y,x,y)};$.bezier=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.bezierVertex(x2,y2,x3,y3,x4,y4);$.endShape()};$.triangle=(x1,y1,x2,y2,x3,y3)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.endShape($.CLOSE)};$.quad=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.vertex(x4,y4);$.endShape($.CLOSE)};$.endShape=close=>{curveBuff=[];if(close)$.ctx.closePath();ink()};$.curveVertex=(x,y)=>{if($._da){x*=$._da;y*=$._da}curveBuff.push([x,y]);if(curveBuff.length<4)return;let p0=curveBuff.at(-4),p1=curveBuff.at(-3),p2=curveBuff.at(-2),p3=curveBuff.at(-1);let cp1x=p1[0]+(p2[0]-p0[0])/6,cp1y=p1[1]+(p2[1]-p0[1])/6,cp2x=p2[0]-(p3[0]-p1[0])/6,cp2y=p2[1]-(p3[1]-p1[1])/6;if(firstVertex){$.ctx.moveTo(p1[0],p1[1]);firstVertex=false}$.ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,p2[0],p2[1])};$.curve=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.curveVertex(x1,y1);$.curveVertex(x2,y2);$.curveVertex(x3,y3);$.curveVertex(x4,y4);$.endShape()};$.curvePoint=(a,b,c,d,t)=>{const t3=t*t*t,t2=t*t,f1=-.5*t3+t2-.5*t,f2=1.5*t3-2.5*t2+1,f3=-1.5*t3+2*t2+.5*t,f4=.5*t3-.5*t2;return a*f1+b*f2+c*f3+d*f4};$.bezierPoint=(a,b,c,d,t)=>{const adjustedT=1-t;return Math.pow(adjustedT,3)*a+3*Math.pow(adjustedT,2)*t*b+3*adjustedT*Math.pow(t,2)*c+Math.pow(t,3)*d};$.curveTangent=(a,b,c,d,t)=>{const t2=t*t,f1=-3*t2/2+2*t-.5,f2=9*t2/2-5*t,f3=-9*t2/2+4*t+.5,f4=3*t2/2-t;return a*f1+b*f2+c*f3+d*f4};$.bezierTangent=(a,b,c,d,t)=>{const adjustedT=1-t;return 3*d*Math.pow(t,2)-3*c*Math.pow(t,2)+6*c*adjustedT*t-6*b*adjustedT*t+3*b*Math.pow(adjustedT,2)-3*a*Math.pow(adjustedT,2)};$.erase=function(fillAlpha=255,strokeAlpha=255){$.ctx.save();$.ctx.globalCompositeOperation="destination-out";$.ctx.fillStyle=`rgba(0, 0, 0, ${fillAlpha/255})`;$.ctx.strokeStyle=`rgba(0, 0, 0, ${strokeAlpha/255})`};$.noErase=function(){$.ctx.globalCompositeOperation="source-over";$.ctx.restore()};$.inFill=(x,y)=>{const pd=$._pixelDensity;return $.ctx.isPointInPath(x*pd,y*pd)};$.inStroke=(x,y)=>{const pd=$._pixelDensity;return $.ctx.isPointInStroke(x*pd,y*pd)}};Q5.renderers.c2d.image=($,q)=>{class Q5Image{constructor(w,h,opt){let $=this;$._scope="image";$.canvas=$.ctx=$.drawingContext=null;$.pixels=[];Q5.modules.canvas($,$);let r=Q5.renderers.c2d;for(let m of["canvas","image","soft_filters"]){if(r[m])r[m]($,$)}$._pixelDensity=opt.pixelDensity||1;$.createCanvas(w,h,opt);delete $.createCanvas;$._loop=false}get w(){return this.width}get h(){return this.height}}Q5.Image??=Q5Image;$._tint=null;let imgData=null;$.createImage=(w,h,opt)=>{opt??={};opt.alpha??=true;opt.colorSpace??=$.canvas.colorSpace||Q5.canvasOptions.colorSpace;let img=new Q5.Image(w,h,opt);img.defaultWidth=w*$._defaultImageScale;img.defaultHeight=h*$._defaultImageScale;return img};$.loadImage=function(url,cb,opt){if(url.canvas)return url;if(url.slice(-3).toLowerCase()=="gif"){throw new Error(`q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84`)}q._preloadCount++;let last=[...arguments].at(-1);if(typeof last=="object"){opt=last;cb=null}else opt=null;let g=$.createImage(1,1,opt);let pd=g._pixelDensity=opt?.pixelDensity||1;let img=new window.Image;img.crossOrigin="Anonymous";g._loader=new Promise(((resolve,reject)=>{img.onload=()=>{img._pixelDensity=pd;g.defaultWidth=img.width*$._defaultImageScale;g.defaultHeight=img.height*$._defaultImageScale;g.naturalWidth=img.naturalWidth||img.width;g.naturalHeight=img.naturalHeight||img.height;g._setImageSize(Math.ceil(g.naturalWidth/pd),Math.ceil(g.naturalHeight/pd));g.ctx.drawImage(img,0,0);q._preloadCount--;if(cb)cb(g);delete g._loader;resolve(g)};img.onerror=e=>{q._preloadCount--;reject(e)}}));img.src=url;if($._disablePreload)return g._loader;return g};$.imageMode=mode=>$._imageMode=mode;$.image=(img,dx,dy,dw,dh,sx=0,sy=0,sw,sh)=>{if(!img)return;let drawable=img.canvas||img;dw??=img.defaultWidth||drawable.width||img.videoWidth;dh??=img.defaultHeight||drawable.height||img.videoHeight;if($._imageMode=="center"){dx-=dw*.5;dy-=dh*.5}if($._da){dx*=$._da;dy*=$._da;dw*=$._da;dh*=$._da;sx*=$._da;sy*=$._da;sw*=$._da;sh*=$._da}let pd=img._pixelDensity||1;if(!sw){sw=drawable.width||drawable.videoWidth}else sw*=pd;if(!sh){sh=drawable.height||drawable.videoHeight}else sh*=pd;if($._tint){if(img._retint||img._tint!=$._tint){img._tintImg??=$.createImage(img.w,img.h,{pixelDensity:pd});if(img._tintImg.width!=img.width||img._tintImg.height!=img.height){img._tintImg.resize(img.w,img.h)}let tnt=img._tintImg.ctx;tnt.globalCompositeOperation="copy";tnt.fillStyle=$._tint;tnt.fillRect(0,0,img.width,img.height);if(img.canvas.alpha){tnt.globalCompositeOperation="destination-in";tnt.drawImage(drawable,0,0,img.width,img.height)}tnt.globalCompositeOperation="multiply";tnt.drawImage(drawable,0,0,img.width,img.height);img._tint=$._tint;img._retint=false}drawable=img._tintImg.canvas}$.ctx.drawImage(drawable,sx*pd,sy*pd,sw,sh,dx,dy,dw,dh)};$.filter=(type,value)=>{$.ctx.save();let f="";if($.ctx.filter){if(typeof type=="string"){f=type}else if(type==Q5.GRAY){f=`saturate(0%)`}else if(type==Q5.INVERT){f=`invert(100%)`}else if(type==Q5.BLUR){let r=Math.ceil(value*$._pixelDensity)||1;f=`blur(${r}px)`}else if(type==Q5.THRESHOLD){value??=.5;let b=Math.floor(.5/Math.max(value,1e-5)*100);f=`saturate(0%) brightness(${b}%) contrast(1000000%)`}else if(type==Q5.SEPIA){f=`sepia(${value??1})`}else if(type==Q5.BRIGHTNESS){f=`brightness(${value??1})`}else if(type==Q5.SATURATION){f=`saturate(${value??1})`}else if(type==Q5.CONTRAST){f=`contrast(${value??1})`}else if(type==Q5.HUE_ROTATE){let unit=$._angleMode==0?"rad":"deg";f=`hue-rotate(${value}${unit})`}if(f){$.ctx.filter=f;if($.ctx.filter=="none"){throw new Error(`Invalid filter format: ${type}`)}}}if(!f)$._softFilter(type,value);$.ctx.globalCompositeOperation="source-over";$.ctx.drawImage($.canvas,0,0,$.canvas.w,$.canvas.h);$.ctx.restore();$._retint=true};if($._scope=="image"){$.resize=(w,h)=>{let c=$.canvas;let o=new $._OffscreenCanvas(c.width,c.height);let tmpCtx=o.getContext("2d",{colorSpace:c.colorSpace});tmpCtx.drawImage(c,0,0);$._setImageSize(w,h);$.defaultWidth=c.width*$._defaultImageScale;$.defaultHeight=c.height*$._defaultImageScale;$.ctx.clearRect(0,0,c.width,c.height);$.ctx.drawImage(o,0,0,c.width,c.height);$._retint=true}}$._getImageData=(x,y,w,h)=>$.ctx.getImageData(x,y,w,h,{colorSpace:$.canvas.colorSpace});$.trim=()=>{let pd=$._pixelDensity||1;let w=$.canvas.width;let h=$.canvas.height;let data=$._getImageData(0,0,w,h).data;let left=w,right=0,top=h,bottom=0;let i=3;for(let y=0;y<h;y++){for(let x=0;x<w;x++){if(data[i]!==0){if(x<left)left=x;if(x>right)right=x;if(y<top)top=y;if(y>bottom)bottom=y}i+=4}}top=Math.floor(top/pd);bottom=Math.floor(bottom/pd);left=Math.floor(left/pd);right=Math.floor(right/pd);return $.get(left,top,right-left+1,bottom-top+1)};$.mask=img=>{$.ctx.save();$.ctx.resetTransform();let old=$.ctx.globalCompositeOperation;$.ctx.globalCompositeOperation="destination-in";$.ctx.drawImage(img.canvas,0,0);$.ctx.globalCompositeOperation=old;$.ctx.restore();$._retint=true};$.inset=(x,y,w,h,dx,dy,dw,dh)=>{let pd=$._pixelDensity||1;$.ctx.drawImage($.canvas,x*pd,y*pd,w*pd,h*pd,dx,dy,dw,dh);$._retint=true};$.copy=()=>$.get();$.get=(x,y,w,h)=>{let pd=$._pixelDensity||1;if(x!==undefined&&w===undefined){let c=$._getImageData(x*pd,y*pd,1,1).data;return[c[0],c[1],c[2],c[3]/255]}x=Math.floor(x||0)*pd;y=Math.floor(y||0)*pd;let _w=w=w||$.width;let _h=h=h||$.height;w*=pd;h*=pd;let img=$.createImage(w,h);img.ctx.drawImage($.canvas,x,y,w,h,0,0,w,h);img._pixelDensity=pd;img.width=_w;img.height=_h;return img};$.set=(x,y,c)=>{x=Math.floor(x);y=Math.floor(y);$._retint=true;if(c.canvas){let old=$._tint;$._tint=null;$.image(c,x,y);$._tint=old;return}if(!$.pixels.length)$.loadPixels();let mod=$._pixelDensity||1;for(let i=0;i<mod;i++){for(let j=0;j<mod;j++){let idx=4*((y*mod+i)*$.canvas.width+x*mod+j);$.pixels[idx]=c.r;$.pixels[idx+1]=c.g;$.pixels[idx+2]=c.b;$.pixels[idx+3]=c.a}}};$.loadPixels=()=>{imgData=$._getImageData(0,0,$.canvas.width,$.canvas.height);q.pixels=imgData.data};$.updatePixels=()=>{if(imgData!=null){$.ctx.putImageData(imgData,0,0);$._retint=true}};$.smooth=()=>$.ctx.imageSmoothingEnabled=true;$.noSmooth=()=>$.ctx.imageSmoothingEnabled=false;if($._scope=="image")return;$.tint=function(c){$._tint=(c._q5Color?c:$.color(...arguments)).toString()};$.noTint=()=>$._tint=null};Q5.renderers.c2d.soft_filters=$=>{let u=null;function ensureBuf(){let l=$.canvas.width*$.canvas.height*4;if(!u||u.length!=l)u=new Uint8ClampedArray(l)}function initSoftFilters(){$._filters=[];$._filters[Q5.THRESHOLD]=(d,thresh)=>{if(thresh===undefined)thresh=127.5;else thresh*=255;for(let i=0;i<d.length;i+=4){const gray=.2126*d[i]+.7152*d[i+1]+.0722*d[i+2];d[i]=d[i+1]=d[i+2]=gray>=thresh?255:0}};$._filters[Q5.GRAY]=d=>{for(let i=0;i<d.length;i+=4){const gray=.2126*d[i]+.7152*d[i+1]+.0722*d[i+2];d[i]=d[i+1]=d[i+2]=gray}};$._filters[Q5.OPAQUE]=d=>{for(let i=0;i<d.length;i+=4){d[i+3]=255}};$._filters[Q5.INVERT]=d=>{for(let i=0;i<d.length;i+=4){d[i]=255-d[i];d[i+1]=255-d[i+1];d[i+2]=255-d[i+2]}};$._filters[Q5.POSTERIZE]=(d,lvl=4)=>{let lvl1=lvl-1;for(let i=0;i<d.length;i+=4){d[i]=(d[i]*lvl>>8)*255/lvl1;d[i+1]=(d[i+1]*lvl>>8)*255/lvl1;d[i+2]=(d[i+2]*lvl>>8)*255/lvl1}};$._filters[Q5.DILATE]=(d,func)=>{func??=Math.max;ensureBuf();u.set(d);let[w,h]=[$.canvas.width,$.canvas.height];for(let i=0;i<h;i++){for(let j=0;j<w;j++){let l=4*Math.max(j-1,0);let r=4*Math.min(j+1,w-1);let t=4*Math.max(i-1,0)*w;let b=4*Math.min(i+1,h-1)*w;let oi=4*i*w;let oj=4*j;for(let k=0;k<4;k++){let kt=k+t;let kb=k+b;let ko=k+oi;d[oi+oj+k]=func(u[kt+oj],u[ko+l],u[ko+oj],u[ko+r],u[kb+oj])}}}};$._filters[Q5.ERODE]=d=>{$._filters[Q5.DILATE](d,Math.min)};$._filters[Q5.BLUR]=(d,r)=>{r=r||1;r=Math.floor(r*$._pixelDensity);ensureBuf();u.set(d);let ksize=r*2+1;function gauss(ksize){let im=new Float32Array(ksize);let sigma=.3*r+.8;let ss2=sigma*sigma*2;for(let i=0;i<ksize;i++){let x=i-ksize/2;let z=Math.exp(-(x*x)/ss2)/(2.5066282746*sigma);im[i]=z}return im}let kern=gauss(ksize);let[w,h]=[$.canvas.width,$.canvas.height];for(let i=0;i<h;i++){for(let j=0;j<w;j++){let s0=0,s1=0,s2=0,s3=0;for(let k=0;k<ksize;k++){let jk=Math.min(Math.max(j-r+k,0),w-1);let idx=4*(i*w+jk);s0+=u[idx]*kern[k];s1+=u[idx+1]*kern[k];s2+=u[idx+2]*kern[k];s3+=u[idx+3]*kern[k]}let idx=4*(i*w+j);d[idx]=s0;d[idx+1]=s1;d[idx+2]=s2;d[idx+3]=s3}}u.set(d);for(let i=0;i<h;i++){for(let j=0;j<w;j++){let s0=0,s1=0,s2=0,s3=0;for(let k=0;k<ksize;k++){let ik=Math.min(Math.max(i-r+k,0),h-1);let idx=4*(ik*w+j);s0+=u[idx]*kern[k];s1+=u[idx+1]*kern[k];s2+=u[idx+2]*kern[k];s3+=u[idx+3]*kern[k]}let idx=4*(i*w+j);d[idx]=s0;d[idx+1]=s1;d[idx+2]=s2;d[idx+3]=s3}}}}$._softFilter=(typ,x)=>{if(!$._filters)initSoftFilters();let imgData=$._getImageData(0,0,$.canvas.width,$.canvas.height);$._filters[typ](imgData.data,x);$.ctx.putImageData(imgData,0,0)}};Q5.renderers.c2d.text=($,q)=>{$._textAlign="left";$._textBaseline="alphabetic";$._textSize=12;let font="sans-serif",leadingSet=false,leading=15,leadDiff=3,emphasis="normal",fontMod=false,styleHash=0,styleHashes=[],useCache=false,genTextImage=false,cacheSize=0,cacheMax=12e3;let cache=$._textCache={};$.loadFont=(url,cb)=>{q._preloadCount++;let name=url.split("/").pop().split(".")[0].replace(" ","");let f=new FontFace(name,`url(${url})`);document.fonts.add(f);f._loader=(async()=>{let err;try{await f.load()}catch(e){err=e}q._preloadCount--;delete f._loader;if(err)throw err;if(cb)cb(f);return f})();$.textFont(name);if($._disablePreload)return f._loader;return f};$.textFont=x=>{if(typeof x!="string")x=x.family;if(!x||x==font)return font;font=x;fontMod=true;styleHash=-1};$.textSize=x=>{if(x==undefined||x==$._textSize)return $._textSize;if($._da)x*=$._da;$._textSize=x;fontMod=true;styleHash=-1;if(!leadingSet){leading=x*1.25;leadDiff=leading-x}};$.textStyle=x=>{if(!x||x==emphasis)return emphasis;emphasis=x;fontMod=true;styleHash=-1};$.textLeading=x=>{if(x==undefined)return leading||$._textSize*1.25;leadingSet=true;if(x==leading)return leading;if($._da)x*=$._da;leading=x;leadDiff=x-$._textSize;styleHash=-1};$.textAlign=(horiz,vert)=>{$.ctx.textAlign=$._textAlign=horiz;if(vert){$.ctx.textBaseline=$._textBaseline=vert==$.CENTER?"middle":vert}};const updateFont=()=>{$.ctx.font=`${emphasis} ${$._textSize}px ${font}`;fontMod=false};$.textWidth=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).width};$.textAscent=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).actualBoundingBoxAscent};$.textDescent=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).actualBoundingBoxDescent};$.textFill=$.fill;$.textStroke=$.stroke;let updateStyleHash=()=>{let styleString=font+$._textSize+emphasis+leading;let hash=5381;for(let i=0;i<styleString.length;i++){hash=hash*33^styleString.charCodeAt(i)}styleHash=hash>>>0};$.textCache=(enable,maxSize)=>{if(maxSize)cacheMax=maxSize;if(enable!==undefined)useCache=enable;return useCache};$.createTextImage=(str,w,h)=>{genTextImage=true;img=$.text(str,0,0,w,h);genTextImage=false;return img};let lines=[];$.text=(str,x,y,w,h)=>{if(str===undefined||!$._doFill&&!$._doStroke)return;str=str.toString();if($._da){x*=$._da;y*=$._da}let ctx=$.ctx;let img,tX,tY;if(fontMod){ctx.font=`${emphasis} ${$._textSize}px ${font}`;fontMod=false}if(useCache||genTextImage){if(styleHash==-1)updateStyleHash();img=cache[str];if(img)img=img[styleHash];if(img){if(img._fill==$._fill&&img._stroke==$._stroke&&img._strokeWeight==$._strokeWeight){if(genTextImage)return img;return $.textImage(img,x,y)}else img.clear()}}if(str.indexOf("\n")==-1)lines[0]=str;else lines=str.split("\n");if(str.length>w){let wrapped=[];for(let line of lines){let i=0;while(i<line.length){let max=i+w;if(max>=line.length){wrapped.push(line.slice(i));break}let end=line.lastIndexOf(" ",max);if(end===-1||end<i)end=max;wrapped.push(line.slice(i,end));i=end+1}}lines=wrapped}if(!useCache&&!genTextImage){tX=x;tY=y}else{tX=0;tY=leading*lines.length;if(!img){let measure=ctx.measureText(" ");let ascent=measure.fontBoundingBoxAscent;let descent=measure.fontBoundingBoxDescent;img=$.createImage.call($,Math.ceil(ctx.measureText(str).width),Math.ceil(tY+descent),{pixelDensity:$._pixelDensity});img._ascent=ascent;img._descent=descent;img._top=descent+leadDiff;img._middle=img._top+ascent*.5;img._bottom=img._top+ascent;img._leading=leading}img._fill=$._fill;img._stroke=$._stroke;img._strokeWeight=$._strokeWeight;img.modified=true;ctx=img.ctx;ctx.font=$.ctx.font;ctx.fillStyle=$._fill;ctx.strokeStyle=$._stroke;ctx.lineWidth=$.ctx.lineWidth}let ogFill;if(!$._fillSet){ogFill=ctx.fillStyle;ctx.fillStyle="black"}let lineAmount=0;for(let line of lines){if($._doStroke&&$._strokeSet)ctx.strokeText(line,tX,tY);if($._doFill)ctx.fillText(line,tX,tY);tY+=leading;lineAmount++;if(lineAmount>=h)break}lines=[];if(!$._fillSet)ctx.fillStyle=ogFill;if(useCache||genTextImage){styleHashes.push(styleHash);(cache[str]??={})[styleHash]=img;cacheSize++;if(cacheSize>cacheMax){let half=Math.ceil(cacheSize/2);let hashes=styleHashes.splice(0,half);for(let s in cache){s=cache[s];for(let h of hashes)delete s[h]}cacheSize-=half}if(genTextImage)return img;$.textImage(img,x,y)}};$.textImage=(img,x,y)=>{if(typeof img=="string")img=$.createTextImage(img);let og=$._imageMode;$._imageMode="corner";let ta=$._textAlign;if(ta=="center")x-=img.canvas.hw;else if(ta=="right")x-=img.width;let bl=$._textBaseline;if(bl=="alphabetic")y-=img._leading;else if(bl=="middle")y-=img._middle;else if(bl=="bottom")y-=img._bottom;else if(bl=="top")y-=img._top;$.image(img,x,y);$._imageMode=og};$.nf=(n,l,r)=>{let neg=n<0;n=Math.abs(n);let parts=n.toFixed(r).split(".");parts[0]=parts[0].padStart(l,"0");let s=parts.join(".");if(neg)s="-"+s;return s}};Q5.modules.ai=$=>{$.askAI=(question="")=>{Q5.disableFriendlyErrors=false;throw Error("Ask AI ✨ "+question)};$._askAI=async e=>{let askAI=e.message?.includes("Ask AI ✨");let stackLines=e.stack?.split("\n");if(!e.stack||stackLines.length<=1)return;let idx=1;let sep="(";if(navigator.userAgent.indexOf("Chrome")==-1){idx=0;sep="@"}while(stackLines[idx].indexOf("q5")>=0)idx++;let errFile=stackLines[idx].split(sep).at(-1);if(errFile.startsWith("blob:"))errFile=errFile.slice(5);let parts=errFile.split(":");let lineNum=parseInt(parts.at(-2));if(askAI)lineNum++;parts[3]=parts[3].split(")")[0];let fileUrl=parts.slice(0,2).join(":");let fileBase=fileUrl.split("/").at(-1);try{let res=await(await fetch(fileUrl)).text();let lines=res.split("\n");let errLine=lines[lineNum-1].trim();let context="";let i=1;while(context.length<1600){if(lineNum-i>=0){context=lines[lineNum-i].trim()+"\n"+context}if(lineNum+i<lines.length){context+=lines[lineNum+i].trim()+"\n"}else break;i++}let question=askAI&&e.message.length>10?e.message.slice(10):"Whats+wrong+with+this+line%3F+short+answer";let url="https://chatgpt.com/?q=q5.js+"+question+(askAI?"":"%0A%0A"+encodeURIComponent(e.name+": "+e.message))+"%0A%0ALine%3A+"+encodeURIComponent(errLine)+"%0A%0AExcerpt+for+context%3A%0A%0A"+encodeURIComponent(context);console.warn("Error in "+fileBase+" on line "+lineNum+":\n\n"+errLine);console.warn("Ask AI ✨ "+url);if(askAI)return window.open(url,"_blank")}catch(err){}}};Q5.modules.color=($,q)=>{$.RGB=$.RGBA=$._colorMode="rgb";$.SRGB="srgb";$.OKLCH="oklch";$.colorMode=(mode,format)=>{$._colorMode=mode;let srgb=$.canvas.colorSpace=="srgb"||mode=="srgb";format??=srgb?"integer":"float";$._colorFormat=format=="float"||format==1?1:255;if(mode=="oklch"){q.Color=Q5.ColorOKLCH}else{if($._colorFormat==255){q.Color=srgb?Q5.ColorRGBA_8:Q5.ColorRGBA_P3_8}else{q.Color=srgb?Q5.ColorRGBA:Q5.ColorRGBA_P3}$._colorMode="rgb"}};$._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]};$.color=(c0,c1,c2,c3)=>{let C=$.Color;if(c0._q5Color)return new C(...c0.levels);if(c1==undefined){if(typeof c0=="string"){if(c0[0]=="#"){if(c0.length<=5){if(c0.length>4)c3=parseInt(c0[4]+c0[4],16);c2=parseInt(c0[3]+c0[3],16);c1=parseInt(c0[2]+c0[2],16);c0=parseInt(c0[1]+c0[1],16)}else{if(c0.length>7)c3=parseInt(c0.slice(7,9),16);c2=parseInt(c0.slice(5,7),16);c1=parseInt(c0.slice(3,5),16);c0=parseInt(c0.slice(1,3),16)}}else if($._namedColors[c0]){[c0,c1,c2,c3]=$._namedColors[c0]}else{let c=new C(0,0,0);c._css=c0;c.toString=function(){return this._css};return c}if($._colorFormat==1){c0/=255;if(c1)c1/=255;if(c2)c2/=255;if(c3)c3/=255}}if(Array.isArray(c0))[c0,c1,c2,c3]=c0}if(c2==undefined){if($._colorMode==Q5.OKLCH)return new C(c0,0,0,c1);return new C(c0,c0,c0,c1)}return new C(c0,c1,c2,c3)};$.red=c=>c.r;$.green=c=>c.g;$.blue=c=>c.b;$.alpha=c=>c.a;$.lightness=c=>{if(c.l)return c.l;return(.2126*c.r+.7152*c.g+.0722*c.b)*100/255};$.hue=c=>{if(c.h)return c.h;let r=c.r;let g=c.g;let b=c.b;if($._colorFormat==255){r/=255;g/=255;b/=255}let max=Math.max(r,g,b);let min=Math.min(r,g,b);let h;if(max==min)h=0;else if(max==r)h=60*(g-b)/(max-min);else if(max==g)h=60*(b-r)/(max-min)+120;else h=60*(r-g)/(max-min)+240;if(h<0)h+=360;return h};$.lerpColor=(a,b,t)=>{t=Math.max(0,Math.min(1,t));if($._colorMode=="rgb"){return new $.Color($.lerp(a.r,b.r,t),$.lerp(a.g,b.g,t),$.lerp(a.b,b.b,t),$.lerp(a.a,b.a,t))}else{let deltaH=b.h-a.h;if(deltaH>180)deltaH-=360;if(deltaH<-180)deltaH+=360;let h=a.h+t*deltaH;if(h<0)h+=360;if(h>360)h-=360;return new $.Color($.lerp(a.l,b.l,t),$.lerp(a.c,b.c,t),h,$.lerp(a.a,b.a,t))}}};Q5.Color=class{constructor(){this._q5Color=true}};Q5.ColorOKLCH=class extends Q5.Color{constructor(l,c,h,a){super();this.l=l;this.c=c;this.h=h;this.a=a??1}get levels(){return[this.l,this.c,this.h,this.a]}equals(c){return c&&this.l==c.l&&this.c==c.c&&this.h==c.h&&this.a==c.a}isSameColor(c){return c&&this.l==c.l&&this.c==c.c&&this.h==c.h}toString(){return`oklch(${this.l} ${this.c} ${this.h} / ${this.a})`}get lightness(){return this.l}set lightness(v){this.l=v}get chroma(){return this.c}set chroma(v){this.c=v}get hue(){return this.h}set hue(v){this.h=v}get alpha(){return this.a}set alpha(v){this.a=v}};Q5.ColorRGBA=class extends Q5.Color{constructor(r,g,b,a){super();this.r=r;this.g=g;this.b=b;this.a=a??1}get levels(){return[this.r,this.g,this.b,this.a]}equals(c){return c&&this.r==c.r&&this.g==c.g&&this.b==c.b&&this.a==c.a}isSameColor(c){return c&&this.r==c.r&&this.g==c.g&&this.b==c.b}toString(){return`color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`}get red(){return this.r}set red(v){this.r=v}get green(){return this.g}set green(v){this.g=v}get blue(){return this.b}set blue(v){this.b=v}get alpha(){return this.a}set alpha(v){this.a=v}};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(r,g,b,a){super(r,g,b,a??255)}setRed(v){this.r=v}setGreen(v){this.g=v}setBlue(v){this.b=v}setAlpha(v){this.a=v}toString(){return`rgb(${this.r} ${this.g} ${this.b} / ${this.a/255})`}};Q5.ColorRGBA_P3_8=class extends Q5.ColorRGBA{constructor(r,g,b,a){super(r,g,b,a??255);this._edited=true}get r(){return this._r}set r(v){this._r=v;this._edited=true}get g(){return this._g}set g(v){this._g=v;this._edited=true}get b(){return this._b}set b(v){this._b=v;this._edited=true}get a(){return this._a}set a(v){this._a=v;this._edited=true}toString(){if(this._edited){let r=(this._r/255).toFixed(3);let g=(this._g/255).toFixed(3);let b=(this._b/255).toFixed(3);let a=(this._a/255).toFixed(3);this._css=`color(display-p3 ${r} ${g} ${b} / ${a})`;this._edited=false}return this._css}};Q5.modules.display=$=>{if(!$.canvas||$._scope=="graphics")return;let c=$.canvas;$.CENTERED="centered";$.FULLSCREEN="fullscreen";$.MAXED="maxed";$.PIXELATED="pixelated";if(Q5._instanceCount==0&&!Q5._server){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>`)}$._adjustDisplay=()=>{let s=c.style;let p=c.parentElement;if(!s||!p||!c.displayMode)return;if(c.renderQuality=="pixelated"){c.classList.add("q5-pixelated");$.pixelDensity(1);$.defaultImageScale(1);if($.noSmooth)$.noSmooth();if($.textFont)$.textFont("monospace")}if(c.displayMode=="default"||c.displayMode=="normal"){p.classList.remove("q5-centered","q5-maxed","q5-fullscreen");s.width=c.w*c.displayScale+"px";s.height=c.h*c.displayScale+"px"}else{p.classList.add("q5-"+c.displayMode);p=p.getBoundingClientRect();if(c.w/c.h>p.width/p.height){if(c.displayMode=="centered"){s.width=c.w*c.displayScale+"px";s.maxWidth="100%"}else s.width="100%";s.height="auto";s.maxHeight=""}else{s.width="auto";s.maxWidth="";if(c.displayMode=="centered"){s.height=c.h*c.displayScale+"px";s.maxHeight="100%"}else s.height="100%"}}};$.displayMode=(displayMode="normal",renderQuality="smooth",displayScale=1)=>{if(typeof displayScale=="string"){displayScale=parseFloat(displayScale.slice(1))}if(displayMode=="center")displayMode="centered";Object.assign(c,{displayMode:displayMode,renderQuality:renderQuality,displayScale:displayScale});if($.ctx)$.pushStyles();$._adjustDisplay();if($.ctx)$.popStyles()};$.fullscreen=v=>{if(v===undefined)return document.fullscreenElement;if(v)document.body.requestFullscreen();else document.body.exitFullscreen()}};Q5.modules.dom=$=>{$.elementMode=mode=>$._elementMode=mode;$.createElement=(tag,content)=>{let el=document.createElement(tag);if($._elementMode=="center"){el.style.transform="translate(-50%, -50%)"}if(content)el.innerHTML=content;Object.defineProperty(el,"x",{get:()=>el._x,set:v=>{let pos=el.style.position;if(!pos||pos=="relative"){el.style.position="absolute"}let x=$.canvas.offsetLeft+v;el.style.left=x+"px";el._x=x}});Object.defineProperty(el,"y",{get:()=>el._y,set:v=>{let pos=el.style.position;if(!pos||pos=="relative"){el.style.position="absolute"}let y=$.canvas.offsetTop+v;el.style.top=y+"px";el._y=y}});Object.defineProperty(el,"width",{get:()=>el.style.width,set:v=>el.style.width=v+"px"});Object.defineProperty(el,"height",{get:()=>el.style.height,set:v=>el.style.height=v+"px"});el.position=(x,y,scheme)=>{if(scheme)el.style.position=scheme;el.x=x;el.y=y;return el};Object.defineProperty(el,"size",{writable:true});el.size=(w,h)=>{el.width=w;el.height=h;return el};el.center=()=>{el.style.position="absolute";el.x=$.canvas.hw;el.y=$.canvas.hh;return el};el.show=()=>{el.style.display="";return el};el.hide=()=>{el.style.display="none";return el};el.parent=parent=>{parent.append(el);return el};$._elements.push(el);$.canvas.parentElement.append(el);return el};$.createEl=$.createElement;$.createA=(href,content,newTab)=>{let el=$.createEl("a",content);el.href=href;el.target=newTab?"_blank":"_self";return el};$.createButton=content=>$.createEl("button",content);$.createCheckbox=(label="",checked=false)=>{let el=$.createEl("input");el.type="checkbox";el.checked=checked;let lbl=$.createEl("label",label);lbl.addEventListener("click",(()=>{el.checked=!el.checked;el.dispatchEvent(new Event("input",{bubbles:true}));el.dispatchEvent(new Event("change",{bubbles:true}))}));el.insertAdjacentElement("afterend",lbl);el.label=lbl;return el};$.createColorPicker=(value="#ffffff")=>{let el=$.createEl("input");el.type="color";el.value=value.toString();return el};$.createDiv=content=>$.createEl("div",content);$.createImg=src=>{let el=$.createEl("img");el.crossOrigin="anonymous";el.src=src;return el};$.createInput=(value="",type="text")=>{let el=$.createEl("input");el.value=value;el.type=type;el.style.boxSizing="border-box";return el};$.createP=content=>$.createEl("p",content);let radioCount=0;$.createRadio=name=>{let el=$.createEl("div");el.name=name||"radio"+radioCount++;el.buttons=[];Object.defineProperty(el,"value",{get:()=>el.selected?.value,set:v=>{let btn=el.buttons.find((b=>b.value==v));if(btn){btn.checked=true;el.selected=btn}}});el.option=(label,value)=>{let btn=$.createEl("input");btn.type="radio";btn.name=el.name;btn.value=value||label;btn.addEventListener("input",(()=>el.selected=btn));let lbl=$.createEl("label",label);lbl.addEventListener("click",(()=>{btn.checked=true;el.selected=btn;btn.dispatchEvent(new Event("input",{bubbles:true}));btn.dispatchEvent(new Event("change",{bubbles:true}))}));btn.label=lbl;el.append(btn);el.append(lbl);el.buttons.push(btn);return el};return el};$.createSelect=placeholder=>{let el=$.createEl("select");if(placeholder){let opt=$.createEl("option",placeholder);opt.disabled=true;opt.selected=true;el.append(opt)}Object.defineProperty(el,"selected",{get:()=>{if(el.multiple){return Array.from(el.selectedOptions).map((opt=>opt.textContent))}return el.selectedOptions[0]?.textContent},set:v=>{if(el.multiple){Array.from(el.options).forEach((opt=>{opt.selected=v.includes(opt.textContent)}))}else{const option=Array.from(el.options).find((opt=>opt.textContent===v));if(option)option.selected=true}}});Object.defineProperty(el,"value",{get:()=>{if(el.multiple){return Array.from(el.selectedOptions).map((o=>o.value))}return el.selectedOptions[0]?.value},set:v=>{if(el.multiple){el.options.forEach((o=>o.selected=v.includes(o.value)))}else{let opt;for(let i=0;i<el.options.length;i++){if(el.options[i].value==v){opt=el.options[i];break}}if(opt)opt.selected=true}}});el.option=(label,value)=>{let opt=$.createEl("option",label);opt.value=value||label;el.append(opt);return el};return el};$.createSlider=(min,max,value,step)=>{let el=$.createEl("input");el.type="range";el.min=min;el.max=max;el.value=value;el.step=step;el.val=()=>parseFloat(el.value);return el};$.createSpan=content=>$.createEl("span",content);$.createVideo=src=>{let el=$.createEl("video");el.crossOrigin="anonymous";el.src=src;return el};$.findElement=selector=>document.querySelector(selector);$.findElements=selector=>document.querySelectorAll(selector)};Q5.modules.input=($,q)=>{if($._scope=="graphics")return;$.mouseX=0;$.mouseY=0;$.pmouseX=0;$.pmouseY=0;$.touches=[];$.mouseButton="";$.keyIsPressed=false;$.mouseIsPressed=false;$.key="";$.keyCode=0;$.UP_ARROW=38;$.DOWN_ARROW=40;$.LEFT_ARROW=37;$.RIGHT_ARROW=39;$.SHIFT=16;$.TAB=9;$.BACKSPACE=8;$.ENTER=$.RETURN=13;$.ALT=$.OPTION=18;$.CONTROL=17;$.DELETE=46;$.ESCAPE=27;$.ARROW="default";$.CROSS="crosshair";$.HAND="pointer";$.MOVE="move";$.TEXT="text";let keysHeld={};let mouseBtns=[Q5.LEFT,Q5.CENTER,Q5.RIGHT];let c=$.canvas;$._startAudio=()=>{if(!Q5.aud||Q5.aud?.state!="running")$.userStartAudio()};$._updateMouse=e=>{if(e.changedTouches)return;if(c){let rect=c.getBoundingClientRect();let sx=c.scrollWidth/$.width||1;let sy=c.scrollHeight/$.height||1;q.mouseX=(e.clientX-rect.left)/sx;q.mouseY=(e.clientY-rect.top)/sy;if(c.renderer=="webgpu"){q.mouseX-=c.hw;q.mouseY-=c.hh}}else{q.mouseX=e.clientX;q.mouseY=e.clientY}q.moveX=e.movementX;q.moveY=e.movementY};let pressedInCanvas=0;$._onmousedown=e=>{pressedInCanvas++;$._startAudio();$._updateMouse(e);q.mouseIsPressed=true;q.mouseButton=mouseBtns[e.button];$.mousePressed(e)};$._onmousemove=e=>{$._updateMouse(e);if($.mouseIsPressed)$.mouseDragged(e);else $.mouseMoved(e)};$._onmouseup=e=>{$._updateMouse(e);q.mouseIsPressed=false;$.mouseReleased(e)};$._onclick=e=>{$._updateMouse(e);q.mouseIsPressed=true;$.mouseClicked(e);q.mouseIsPressed=false};$._onwheel=e=>{$._updateMouse(e);e.delta=e.deltaY;if($.mouseWheel(e)==false)e.preventDefault()};$.cursor=(name,x,y)=>{let pfx="";if(name.includes(".")){name=`url("${name}")`;pfx=", auto"}if(x!==undefined){name+=" "+x+" "+y}$.canvas.style.cursor=name+pfx};$.noCursor=()=>{$.canvas.style.cursor="none"};if(window){$.lockMouse=document.body?.requestPointerLock;$.unlockMouse=document.exitPointerLock}$._onkeydown=e=>{if(e.repeat)return;$._startAudio();q.keyIsPressed=true;q.key=e.key;q.keyCode=e.keyCode;keysHeld[$.keyCode]=keysHeld[$.key.toLowerCase()]=true;$.keyPressed(e);if(e.key.length==1)$.keyTyped(e)};$._onkeyup=e=>{q.keyIsPressed=false;q.key=e.key;q.keyCode=e.keyCode;keysHeld[$.keyCode]=keysHeld[$.key.toLowerCase()]=false;$.keyReleased(e)};$.keyIsDown=v=>!!keysHeld[typeof v=="string"?v.toLowerCase():v];function getTouchInfo(touch){const rect=$.canvas.getBoundingClientRect();const sx=$.canvas.scrollWidth/$.width||1;const sy=$.canvas.scrollHeight/$.height||1;return{x:(touch.clientX-rect.left)/sx,y:(touch.clientY-rect.top)/sy,id:touch.identifier}}$._ontouchstart=e=>{$._startAudio();q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware){q.mouseX=$.touches[0].x;q.mouseY=$.touches[0].y;q.mouseIsPressed=true;q.mouseButton=$.LEFT;$.mousePressed(e)}$.touchStarted(e)};$._ontouchmove=e=>{q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware){q.mouseX=$.touches[0].x;q.mouseY=$.touches[0].y;if(!$.mouseDragged(e))e.preventDefault()}if(!$.touchMoved(e))e.preventDefault()};$._ontouchend=e=>{q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware&&!$.touches.length){q.mouseIsPressed=false;if(!$.mouseReleased(e))e.preventDefault()}if(!$.touchEnded(e))e.preventDefault()};if(c){let l=c.addEventListener.bind(c);l("mousedown",(e=>$._onmousedown(e)));l("wheel",(e=>$._onwheel(e)));l("click",(e=>$._onclick(e)));l("touchstart",(e=>$._ontouchstart(e)));l("touchmove",(e=>$._ontouchmove(e)));l("touchend",(e=>$._ontouchend(e)));l("touchcancel",(e=>$._ontouchend(e)))}if(window){let l=window.addEventListener;l("keydown",(e=>$._onkeydown(e)),false);l("keyup",(e=>$._onkeyup(e)),false);if(!c){l("mousedown",(e=>$._onmousedown(e)));l("wheel",(e=>$._onwheel(e)));l("click",(e=>$._onclick(e)))}l("mousemove",(e=>$._onmousemove(e)),false);l("mouseup",(e=>{if(pressedInCanvas>0){pressedInCanvas--;$._onmouseup(e)}}))}};Q5.modules.math=($,q)=>{$.RADIANS=0;$.DEGREES=1;$.PI=Math.PI;$.HALF_PI=Math.PI/2;$.QUARTER_PI=Math.PI/4;$.TWO_PI=$.TAU=Math.PI*2;$.abs=Math.abs;$.ceil=Math.ceil;$.exp=Math.exp;$.floor=$.int=Math.floor;$.loge=Math.log;$.mag=Math.hypot;$.max=Math.max;$.min=Math.min;$.round=Math.round;$.pow=Math.pow;$.sqrt=Math.sqrt;$.SHR3=1;$.LCG=2;let angleMode=$._angleMode=0;$.angleMode=mode=>{angleMode=$._angleMode=mode==0||mode=="radians"?0:1;return!angleMode?"radians":"degrees"};let DEGTORAD=$._DEGTORAD=Math.PI/180;let RADTODEG=$._RADTODEG=180/Math.PI;$.degrees=x=>x*$._RADTODEG;$.radians=x=>x*$._DEGTORAD;$.map=Q5.prototype.map=(value,istart,istop,ostart,ostop,clamp)=>{let val=ostart+(ostop-ostart)*((value-istart)*1/(istop-istart));if(!clamp){return val}if(ostart<ostop){return Math.min(Math.max(val,ostart),ostop)}else{return Math.min(Math.max(val,ostop),ostart)}};$.dist=function(){let a=arguments;if(a.length==2)return Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y);if(a.length==4)return Math.hypot(a[0]-a[2],a[1]-a[3]);return Math.hypot(a[0]-a[3],a[1]-a[4],a[2]-a[5])};$.lerp=(a,b,t)=>a*(1-t)+b*t;$.constrain=(x,lo,hi)=>Math.min(Math.max(x,lo),hi);$.norm=(value,start,stop)=>$.map(value,start,stop,0,1);$.sq=x=>x*x;$.fract=x=>x-Math.floor(x);$.sin=a=>Math.sin(!angleMode?a:a*DEGTORAD);$.cos=a=>Math.cos(!angleMode?a:a*DEGTORAD);$.tan=a=>Math.tan(!angleMode?a:a*DEGTORAD);$.asin=x=>{let a=Math.asin(x);return!angleMode?a:a*RADTODEG};$.acos=x=>{let a=Math.acos(x);return!angleMode?a:a*RADTODEG};$.atan=x=>{let a=Math.atan(x);return!angleMode?a:a*RADTODEG};$.atan2=(y,x)=>{let a=Math.atan2(y,x);return!angleMode?a:a*RADTODEG};function lcg(){const m=4294967296;const a=1664525;const c=1013904223;let seed,z;return{setSeed(val){z=seed=(val==null?Math.random()*m:val)>>>0},getSeed(){return seed},rand(){z=(a*z+c)%m;return z/m}}}function shr3(){let jsr,seed;let m=4294967295;return{setSeed(val){jsr=seed=(val==null?Math.random()*m:val)>>>0},getSeed(){return seed},rand(){jsr^=jsr<<17;jsr^=jsr>>13;jsr^=jsr<<5;return(jsr>>>0)/m}}}let rng1=shr3();rng1.setSeed();$.randomSeed=seed=>rng1.setSeed(seed);$.random=(a,b)=>{if(a===undefined)return rng1.rand();if(typeof a=="number"){if(b!==undefined){return rng1.rand()*(b-a)+a}else{return rng1.rand()*a}}else{return a[Math.trunc(a.length*rng1.rand())]}};$.randomGenerator=method=>{if(method==$.LCG)rng1=lcg();else if(method==$.SHR3)rng1=shr3();rng1.setSeed()};var ziggurat=new function(){var iz;var jz;var kn=new Array(128);var ke=new Array(256);var hz;var wn=new Array(128);var fn=new Array(128);var we=new Array(256);var fe=new Array(256);var SHR3=()=>rng1.rand()*4294967296-2147483648;var UNI=()=>.5+(SHR3()<<0)*2.328306e-10;var RNOR=()=>{hz=SHR3();iz=hz&127;return Math.abs(hz)<kn[iz]?hz*wn[iz]:nfix()};var REXP=()=>{jz=SHR3()>>>0;iz=jz&255;return jz<kn[iz]?jz*we[iz]:efix()};var nfix=()=>{var r=3.44262;var x,y;var u1,u2;for(;;){x=hz*wn[iz];if(iz==0){do{u1=UNI();u2=UNI();x=-Math.log(u1)*.2904764;y=-Math.log(u2)}while(y+y<x*x);return hz>0?r+x:-r-x}if(fn[iz]+UNI()*(fn[iz-1]-fn[iz])<Math.exp(-.5*x*x)){return x}hz=SHR3();iz=hz&127;if(Math.abs(hz)<kn[iz]){return hz*wn[iz]}}};var efix=()=>{var x;for(;;){if(iz==0){return 7.69711-Math.log(UNI())}x=jz*we[iz];if(fe[iz]+UNI()*(fe[iz-1]-fe[iz])<Math.exp(-x)){return x}jz=SHR3();iz=jz&255;if(jz<ke[iz]){return jz*we[iz]}}};var zigset=()=>{var m1=2147483648;var m2=4294967296;var dn=3.442619855899;var tn=dn;var vn=.00991256303526217;var q;var de=7.697117470131487;var te=de;var ve=.003949659822581572;var i;q=vn/Math.exp(-.5*dn*dn);kn[0]=Math.floor(dn/q*m1);kn[1]=0;wn[0]=q/m1;wn[127]=dn/m1;fn[0]=1;fn[127]=Math.exp(-.5*dn*dn);for(i=126;i>=1;i--){dn=Math.sqrt(-2*Math.log(vn/dn+Math.exp(-.5*dn*dn)));kn[i+1]=Math.floor(dn/tn*m1);tn=dn;fn[i]=Math.exp(-.5*dn*dn);wn[i]=dn/m1}q=ve/Math.exp(-de);ke[0]=Math.floor(de/q*m2);ke[1]=0;we[0]=q/m2;we[255]=de/m2;fe[0]=1;fe[255]=Math.exp(-de);for(i=254;i>=1;i--){de=-Math.log(ve/de+Math.exp(-de));ke[i+1]=Math.floor(de/te*m2);te=de;fe[i]=Math.exp(-de);we[i]=de/m2}};this.SHR3=SHR3;this.UNI=UNI;this.RNOR=RNOR;this.REXP=REXP;this.zigset=zigset};ziggurat.hasInit=false;$.randomGaussian=(mean,std)=>{if(!ziggurat.hasInit){ziggurat.zigset();ziggurat.hasInit=true}return ziggurat.RNOR()*std+mean};$.randomExponential=()=>{if(!ziggurat.hasInit){ziggurat.zigset();ziggurat.hasInit=true}return ziggurat.REXP()};$.PERLIN="perlin";$.SIMPLEX="simplex";$.BLOCKY="blocky";$.Noise=Q5.PerlinNoise;let _noise;$.noiseMode=mode=>{q.Noise=Q5[mode[0].toUpperCase()+mode.slice(1)+"Noise"];_noise=null};$.noiseSeed=seed=>{_noise=new $.Noise(seed)};$.noise=(x=0,y=0,z=0)=>{_noise??=new $.Noise;return _noise.noise(x,y,z)};$.noiseDetail=(lod,falloff)=>{_noise??=new $.Noise;if(lod>0)_noise.octaves=lod;if(falloff>0)_noise.falloff=falloff}};Q5.Noise=class{};Q5.PerlinNoise=class extends Q5.Noise{constructor(seed){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;if(seed==undefined){this.p=Array.from({length:256},(()=>Math.floor(Math.random()*256)))}else{this.p=this.seedPermutation(seed)}this.p=this.p.concat(this.p)}seedPermutation(seed){let p=[];for(let i=0;i<256;i++){p[i]=i}let n,q;for(let i=255;i>0;i--){seed=seed*16807%2147483647;n=seed%(i+1);q=p[i];p[i]=p[n];p[n]=q}return p}dot(g,x,y,z){return g[0]*x+g[1]*y+g[2]*z}mix(a,b,t){return(1-t)*a+t*b}fade(t){return t*t*t*(t*(t*6-15)+10)}noise(x,y,z){let t=this;let total=0;let freq=1;let amp=1;let maxAmp=0;for(let i=0;i<t.octaves;i++){const X=Math.floor(x*freq)&255;const Y=Math.floor(y*freq)&255;const Z=Math.floor(z*freq)&255;const xf=x*freq-Math.floor(x*freq);const yf=y*freq-Math.floor(y*freq);const zf=z*freq-Math.floor(z*freq);const u=t.fade(xf);const v=t.fade(yf);const w=t.fade(zf);const A=t.p[X]+Y;const AA=t.p[A]+Z;const AB=t.p[A+1]+Z;const B=t.p[X+1]+Y;const BA=t.p[B]+Z;const BB=t.p[B+1]+Z;const lerp1=t.mix(t.dot(t.grad3[t.p[AA]%12],xf,yf,zf),t.dot(t.grad3[t.p[BA]%12],xf-1,yf,zf),u);const lerp2=t.mix(t.dot(t.grad3[t.p[AB]%12],xf,yf-1,zf),t.dot(t.grad3[t.p[BB]%12],xf-1,yf-1,zf),u);const lerp3=t.mix(t.dot(t.grad3[t.p[AA+1]%12],xf,yf,zf-1),t.dot(t.grad3[t.p[BA+1]%12],xf-1,yf,zf-1),u);const lerp4=t.mix(t.dot(t.grad3[t.p[AB+1]%12],xf,yf-1,zf-1),t.dot(t.grad3[t.p[BB+1]%12],xf-1,yf-1,zf-1),u);const mix1=t.mix(lerp1,lerp2,v);const mix2=t.mix(lerp3,lerp4,v);total+=t.mix(mix1,mix2,w)*amp;maxAmp+=amp;amp*=t.falloff;freq*=2}return(total/maxAmp+1)/2}};Q5.modules.record=$=>{let rec,btn0,btn1,timer,formatSelect,qualitySelect;$.recording=false;function initRecorder(opt={}){document.head.insertAdjacentHTML("beforeend",`<style>\n.rec {\n\tdisplay: flex;\n\tz-index: 1000;\n\tgap: 6px;\n\tbackground: #1a1b1d;\n\tpadding: 6px 8px;\n\tborder-radius: 21px;\n\tbox-shadow: #0000001a 0px 4px 12px;\n\tborder: 2px solid transparent; \n\topacity: 0.6;\n\ttransition: all 0.3s;\n\twidth: 134px;\n\toverflow: hidden;\n}\n\n.rec:hover {\n\twidth: unset;\n\topacity: 0.96;\n}\n\n.rec.recording { border-color: #cc3e44; }\n\n.rec button,\n.rec select { cursor: pointer; }\n\n.rec button,\n.rec select,\n.rec .record-timer {\n\tfont-family: sans-serif;\n\tfont-size: 14px;\n\tpadding: 2px 10px;\n\tborder-radius: 18px;\n\toutline: none;\n\tbackground-color: #232529;\n\tcolor: #d4dae6;\n\tbox-shadow: #0000001a 0px 4px 12px;\n\tborder: 1px solid #46494e;\n\tvertical-align: middle;\n\tline-height: 18px;\n\ttransition: all 0.3s;\n}\n\n.rec .record-button { \n\tcolor: #cc3e44;\n\tfont-size: 18px;\n}\n\n.rec select:hover,\n.rec button:hover { background-color: #292b30; }\n\n.rec button:disabled {\n\topacity: 0.5;\n\tcolor: #969ba5;\n\tcursor: not-allowed;\n}\n</style>`);rec=$.createEl("div");rec.className="rec";rec.innerHTML=`\n<button class="record-button"></button>\n<span class="record-timer"></span>\n<button></button>\n`;[btn0,timer,btn1]=rec.children;rec.x=rec.y=8;rec.resetTimer=()=>rec.time={hours:0,minutes:0,seconds:0,frames:0};rec.resetTimer();rec.formats=opt.formats||{"H.264":'video/mp4; codecs="avc1.42E01E"',VP9:"video/mp4; codecs=vp9"};for(let format in rec.formats){if(!MediaRecorder.isTypeSupported(rec.formats[format])){delete rec.formats[format]}}formatSelect=$.createSelect();for(const name in rec.formats){formatSelect.option(name,rec.formats[name])}rec.append(formatSelect);rec.qualityPresets={SD:1e7,HD:16e6,FHD:22e6,QHD:28e6,"4K":48e6,"8K":75e6};qualitySelect=$.createSelect();for(let name in rec.qualityPresets){qualitySelect.option(name)}rec.append(qualitySelect);rec.encoderSettings={};function changeFormat(){rec.encoderSettings.mimeType=formatSelect.value}function changeQuality(){rec.encoderSettings.videoBitsPerSecond=rec.qualityPresets[qualitySelect.value]}formatSelect.addEventListener("change",changeFormat);qualitySelect.addEventListener("change",changeQuality);Object.defineProperty(rec,"quality",{get:()=>qualitySelect.selected,set:v=>{v=v.toUpperCase();if(rec.qualityPresets[v]){qualitySelect.selected=v;changeQuality()}}});Object.defineProperty(rec,"format",{get:()=>formatSelect.selected,set:v=>{v=v.toUpperCase();if(rec.formats[v]){formatSelect.selected=v;changeFormat()}}});let h=$.canvas.height;rec.quality=h>=4320?"8K":h>=2160?"4K":h>=1440?"QHD":h>=1080?"FHD":h>=720?"HD":"SD";if(h>=1440&&rec.formats.VP9)rec.format="VP9";else rec.format="H.264";btn0.addEventListener("click",(()=>{if(!$.recording)start();else if(!rec.paused)$.pauseRecording();else resumeRecording()}));btn1.addEventListener("click",(()=>{if(rec.paused)$.saveRecording();else $.deleteRecording()}));resetUI();$.registerMethod("post",updateTimer)}function start(){if($.recording)return;if(!rec.stream){rec.frameRate??=$.getTargetFrameRate();let canvasStream=$.canvas.captureStream(rec.frameRate);rec.stream=canvasStream}try{rec.mediaRecorder=new MediaRecorder(rec.stream,rec.encoderSettings)}catch(e){console.error("Failed to initialize MediaRecorder: ",e);return}rec.chunks=[];rec.mediaRecorder.addEventListener("dataavailable",(e=>{if(e.data.size>0)rec.chunks.push(e.data)}));rec.mediaRecorder.start();$.recording=true;rec.paused=false;rec.classList.add("recording");rec.resetTimer();resetUI(true)}function resumeRecording(){if(!$.recording||!rec.paused)return;rec.mediaRecorder.resume();rec.paused=false;resetUI(true)}function stop(){if(!$.recording)return;rec.resetTimer();rec.mediaRecorder.stop();$.recording=false;rec.paused=false;rec.classList.remove("recording")}function resetUI(r){btn0.textContent=r?"⏸":"⏺";btn0.title=(r?"Pause":"Start")+" Recording";btn1.textContent=r?"🗑️":"💾";btn1.title=(r?"Delete":"Save")+" Recording";btn1.disabled=!r}function updateTimer(){if($.recording&&!rec.paused){rec.time.frames++;let fr=$.getTargetFrameRate();if(rec.time.frames>=fr){rec.time.seconds+=Math.floor(rec.time.frames/fr);rec.time.frames%=fr;if(rec.time.seconds>=60){rec.time.minutes+=Math.floor(rec.time.seconds/60);rec.time.seconds%=60;if(rec.time.minutes>=60){rec.time.hours+=Math.floor(rec.time.minutes/60);rec.time.minutes%=60}}}}timer.textContent=formatTime()}function formatTime(){let{hours:hours,minutes:minutes,seconds:seconds,frames:frames}=rec.time;return`${String(hours).padStart(2,"0")}:${String(minutes).padStart(2,"0")}:${String(seconds).padStart(2,"0")}:${String(frames).padStart(2,"0")}`}$.createRecorder=opt=>{if(!rec)initRecorder(opt);return rec};$.record=opt=>{if(!rec){initRecorder(opt);rec.hide()}if(!$.recording)start();else if(rec.paused)resumeRecording()};$.pauseRecording=()=>{if(!$.recording||rec.paused)return;rec.mediaRecorder.pause();rec.paused=true;resetUI();btn0.title="Resume Recording";btn1.disabled=false};$.deleteRecording=()=>{stop();resetUI();$.recording=false};$.saveRecording=async fileName=>{if(!$.recording)return;await new Promise((resolve=>{rec.mediaRecorder.onstop=resolve;stop()}));let type=rec.encoderSettings.mimeType,extension=type.slice(6,type.indexOf(";")),dataUrl=URL.createObjectURL(new Blob(rec.chunks,{type:type})),iframe=document.createElement("iframe"),a=document.createElement("a");iframe.style.display="none";iframe.name="download_"+Date.now();document.body.append(iframe);a.target=iframe.name;a.href=dataUrl;fileName??="recording "+(new Date).toLocaleString(undefined,{hour12:false}).replace(","," at").replaceAll("/","-").replaceAll(":","_");a.download=`${fileName}.${extension}`;await new Promise((resolve=>{iframe.onload=()=>{document.body.removeChild(iframe);resolve()};a.click()}));setTimeout((()=>URL.revokeObjectURL(dataUrl)),1e3);resetUI();$.recording=false}};Q5.modules.sound=($,q)=>{$.Sound=Q5.Sound;let sounds=[];$.loadSound=(url,cb)=>{q._preloadCount++;let s=new Q5.Sound;sounds.push(s);s._loader=(async()=>{let err;try{await s.load(url)}catch(e){err=e}q._preloadCount--;delete s._loader;if(err)throw err;if(cb)cb(s);return s})();if($._disablePreload)return s._loader;return s};$.loadAudio=(url,cb)=>{q._preloadCount++;let a=new Audio(url);a.crossOrigin="Anonymous";a.addEventListener("canplaythrough",(()=>{if(!a.loaded){q._preloadCount--;a.loaded=true;if(cb)cb(a)}}));let preloadSkip=()=>{a._preloadSkip=true;q._preloadCount--};a.addEventListener("suspend",preloadSkip);a.addEventListener("error",(e=>{preloadSkip();throw e}));return a};$.getAudioContext=()=>Q5.aud;$.userStartAudio=()=>{if(window.AudioContext){if(Q5._offlineAudio){Q5._offlineAudio=false;Q5.aud=new window.AudioContext;for(let s of sounds)s.init()}return Q5.aud.resume()}}};if(window.OfflineAudioContext){Q5.aud=new window.OfflineAudioContext(2,1,44100);Q5._offlineAudio=true}Q5.Sound=class{constructor(){this.sources=new Set;this.loaded=this.paused=false}async load(url){this.url=url;let res=await fetch(url);this.buffer=await res.arrayBuffer();this.buffer=await Q5.aud.decodeAudioData(this.buffer)}init(){this.gainNode=Q5.aud.createGain();this.pannerNode=Q5.aud.createStereoPanner();this.gainNode.connect(this.pannerNode);this.pannerNode.connect(Q5.aud.destination);this.loaded=true;if(this._volume)this.volume=this._volume;if(this._pan)this.pan=this._pan}_newSource(offset,duration){let source=Q5.aud.createBufferSource();source.buffer=this.buffer;source.connect(this.gainNode);source.loop=this._loop;source._startedAt=Q5.aud.currentTime;source._offset=offset;source._duration=duration;source.start(0,source._offset,source._duration);this.sources.add(source);source.onended=()=>{if(!this.paused){this.ended=true;this.sources.delete(source)}}}play(time=0,duration){if(!this.loaded)return;if(!this.paused){this._newSource(time,duration)}else{let timings=[];for(let source of this.sources){timings.push(source._offset,source._duration);this.sources.delete(source)}for(let i=0;i<timings.length;i+=2){this._newSource(timings[i],timings[i+1])}}this.paused=this.ended=false}pause(){if(!this.isPlaying())return;for(let source of this.sources){source.stop();let timePassed=Q5.aud.currentTime-source._startedAt;source._offset+=timePassed;if(source._duration)source._duration-=timePassed}this.paused=true}stop(){for(let source of this.sources){source.stop();this.sources.delete(source)}this.paused=false;this.ended=true}get volume(){return this._volume}set volume(level){if(this.loaded)this.gainNode.gain.value=level;this._volume=level}get pan(){return this._pan}set pan(value){if(this.loaded)this.pannerNode.pan.value=value;this._pan=value}get loop(){return this._loop}set loop(value){this.sources.forEach((source=>source.loop=value));this._loop=value}get playing(){return!this.paused&&this.sources.size>0}setVolume(level){this.volume=level}setPan(value){this.pan=value}setLoop(loop){this.loop=loop}isLoaded(){return this.loaded}isPlaying(){return this.playing}isPaused(){return this.paused}isLooping(){return this._loop}};Q5.modules.util=($,q)=>{$._loadFile=(url,cb,type)=>{q._preloadCount++;let ret={};ret._loader=new Promise(((resolve,reject)=>{fetch(url).then((res=>{if(!res.ok){reject("error loading file");return null}if(type=="json")return res.json();return res.text()})).then((f=>{if(type=="csv")f=$.CSV.parse(f);if(typeof f=="string")ret.text=f;else Object.assign(ret,f);delete ret._loader;if(cb)cb(f);q._preloadCount--;resolve(f)}))}));return ret};$.loadText=(url,cb)=>$._loadFile(url,cb,"text");$.loadJSON=(url,cb)=>$._loadFile(url,cb,"json");$.loadCSV=(url,cb)=>$._loadFile(url,cb,"csv");$.load=function(...urls){if(Array.isArray(urls[0]))urls=urls[0];let loaders=[];for(let url of urls){let ext=url.split(".").pop().toLowerCase();let obj;if(ext=="json"&&!url.includes("-msdf.")){obj=$.loadJSON(url)}else if(ext=="csv"){obj=$.loadCSV(url)}else if(/(jpe?g|png|gif|webp|avif|svg)/.test(ext)){obj=$.loadImage(url)}else if(/(ttf|otf|woff2?|eot|json)/i.test(ext)){obj=$.loadFont(url)}else if(/(wav|flac|mp3|ogg|m4a|aac|aiff|weba)/.test(ext)){obj=$.loadSound(url)}else{obj=$.loadText(url)}loaders.push(obj._loader)}if(urls.length==1)return loaders[0];return Promise.all(loaders)};$.CSV={};$.CSV.parse=(csv,sep=",",lineSep="\n")=>{if(!csv.length)return[];let a=[],lns=csv.split(lineSep),headers=lns[0].split(sep).map((h=>h.replaceAll('"',"")));for(let i=1;i<lns.length;i++){let o={},ln=lns[i].split(sep);headers.forEach(((h,i)=>o[h]=JSON.parse(ln[i])));a.push(o)}return a};if(typeof localStorage=="object"){$.storeItem=localStorage.setItem;$.getItem=localStorage.getItem;$.removeItem=localStorage.removeItem;$.clearStorage=localStorage.clear}$.year=()=>(new Date).getFullYear();$.day=()=>(new Date).getDay();$.hour=()=>(new Date).getHours();$.minute=()=>(new Date).getMinutes();$.second=()=>(new Date).getSeconds()};Q5.modules.vector=$=>{$.createVector=(x,y,z)=>new Q5.Vector(x,y,z,$)};Q5.Vector=class{constructor(x,y,z,$){this.x=x||0;this.y=y||0;this.z=z||0;this._$=$||window;this._cn=null;this._cnsq=null}set(x,y,z){this.x=x?.x||x||0;this.y=x?.y||y||0;this.z=x?.z||z||0;return this}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(x,y,z){if(x?.x!==undefined)return x;if(y!==undefined){return{x:x,y:y,z:z||0}}return{x:x,y:x,z:x}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z;this._cn=Math.sqrt(this._cnsq)}add(){let u=this._arg2v(...arguments);this.x+=u.x;this.y+=u.y;this.z+=u.z;return this}rem(){let u=this._arg2v(...arguments);this.x%=u.x;this.y%=u.y;this.z%=u.z;return this}sub(){let u=this._arg2v(...arguments);this.x-=u.x;this.y-=u.y;this.z-=u.z;return this}mult(){let u=this._arg2v(...arguments);this.x*=u.x;this.y*=u.y;this.z*=u.z;return this}div(){let u=this._arg2v(...arguments);if(u.x)this.x/=u.x;else this.x=0;if(u.y)this.y/=u.y;else this.y=0;if(u.z)this.z/=u.z;else this.z=0;return this}mag(){this._calcNorm();return this._cn}magSq(){this._calcNorm();return this._cnsq}dot(){let u=this._arg2v(...arguments);return this.x*u.x+this.y*u.y+this.z*u.z}dist(){let u=this._arg2v(...arguments);let x=this.x-u.x;let y=this.y-u.y;let z=this.z-u.z;return Math.sqrt(x*x+y*y+z*z)}cross(){let u=this._arg2v(...arguments);let x=this.y*u.z-this.z*u.y;let y=this.z*u.x-this.x*u.z;let z=this.x*u.y-this.y*u.x;this.x=x;this.y=y;this.z=z;return this}normalize(){this._calcNorm();let n=this._cn;if(n!=0){this.x/=n;this.y/=n;this.z/=n}this._cn=1;this._cnsq=1;return this}limit(m){this._calcNorm();let n=this._cn;if(n>m){let t=m/n;this.x*=t;this.y*=t;this.z*=t;this._cn=m;this._cnsq=m*m}return this}setMag(m){this._calcNorm();let n=this._cn;let t=m/n;this.x*=t;this.y*=t;this.z*=t;this._cn=m;this._cnsq=m*m;return this}heading(){return this._$.atan2(this.y,this.x)}setHeading(ang){let mag=this.mag();this.x=mag*this._$.cos(ang);this.y=mag*this._$.sin(ang);return this}rotate(ang){let costh=this._$.cos(ang);let sinth=this._$.sin(ang);let vx=this.x*costh-this.y*sinth;let vy=this.x*sinth+this.y*costh;this.x=vx;this.y=vy;return this}angleBetween(){let u=this._arg2v(...arguments);let o=Q5.Vector.cross(this,u);let ang=this._$.atan2(o.mag(),this.dot(u));return ang*Math.sign(o.z||1)}lerp(){let args=[...arguments];let amt=args.at(-1);if(amt==0)return this;let u=this._arg2v(...args.slice(0,-1));this.x+=(u.x-this.x)*amt;this.y+=(u.y-this.y)*amt;this.z+=(u.z-this.z)*amt;return this}slerp(){let args=[...arguments];let amt=args.at(-1);if(amt==0)return this;let u=this._arg2v(...args.slice(0,-1));if(amt==1)return this.set(u);let v0Mag=this.mag();let v1Mag=u.mag();if(v0Mag==0||v1Mag==0){return this.mult(1-amt).add(u.mult(amt))}let axis=Q5.Vector.cross(this,u);let axisMag=axis.mag();let theta=Math.atan2(axisMag,this.dot(u));if(axisMag>0){axis.div(axisMag)}else if(theta<this._$.HALF_PI){return this.mult(1-amt).add(u.mult(amt))}else{if(this.z==0&&u.z==0)axis.set(0,0,1);else if(this.x!=0)axis.set(this.y,-this.x,0).normalize();else axis.set(1,0,0)}let ey=axis.cross(this);let lerpedMagFactor=1-amt+amt*v1Mag/v0Mag;let cosMultiplier=lerpedMagFactor*Math.cos(amt*theta);let sinMultiplier=lerpedMagFactor*Math.sin(amt*theta);this.x=this.x*cosMultiplier+ey.x*sinMultiplier;this.y=this.y*cosMultiplier+ey.y*sinMultiplier;this.z=this.z*cosMultiplier+ey.z*sinMultiplier;return this}reflect(n){n.normalize();return this.sub(n.mult(2*this.dot(n)))}array(){return[this.x,this.y,this.z]}equals(u,epsilon){epsilon??=Number.EPSILON||0;return Math.abs(u.x-this.x)<epsilon&&Math.abs(u.y-this.y)<epsilon&&Math.abs(u.z-this.z)<epsilon}fromAngle(th,l){if(l===undefined)l=1;this._cn=l;this._cnsq=l*l;this.x=l*this._$.cos(th);this.y=l*this._$.sin(th);this.z=0;return this}fromAngles(th,ph,l){if(l===undefined)l=1;this._cn=l;this._cnsq=l*l;const cosph=this._$.cos(ph);const sinph=this._$.sin(ph);const costh=this._$.cos(th);const sinth=this._$.sin(th);this.x=l*sinth*sinph;this.y=-l*costh;this.z=l*sinth*cosph;return this}random2D(){this._cn=this._cnsq=1;return this.fromAngle(Math.random()*Math.PI*2)}random3D(){this._cn=this._cnsq=1;return this.fromAngles(Math.random()*Math.PI*2,Math.random()*Math.PI*2)}toString(){return`[${this.x}, ${this.y}, ${this.z}]`}};Q5.Vector.add=(v,u)=>v.copy().add(u);Q5.Vector.cross=(v,u)=>v.copy().cross(u);Q5.Vector.dist=(v,u)=>Math.hypot(v.x-u.x,v.y-u.y,v.z-u.z);Q5.Vector.div=(v,u)=>v.copy().div(u);Q5.Vector.dot=(v,u)=>v.copy().dot(u);Q5.Vector.equals=(v,u,epsilon)=>v.equals(u,epsilon);Q5.Vector.lerp=(v,u,amt)=>v.copy().lerp(u,amt);Q5.Vector.slerp=(v,u,amt)=>v.copy().slerp(u,amt);Q5.Vector.limit=(v,m)=>v.copy().limit(m);Q5.Vector.heading=v=>this._$.atan2(v.y,v.x);Q5.Vector.magSq=v=>v.x*v.x+v.y*v.y+v.z*v.z;Q5.Vector.mag=v=>Math.sqrt(Q5.Vector.magSq(v));Q5.Vector.mult=(v,u)=>v.copy().mult(u);Q5.Vector.normalize=v=>v.copy().normalize();Q5.Vector.rem=(v,u)=>v.copy().rem(u);Q5.Vector.sub=(v,u)=>v.copy().sub(u);for(let k of["fromAngle","fromAngles","random2D","random3D"]){Q5.Vector[k]=(u,v,t)=>(new Q5.Vector)[k](u,v,t)}Q5.renderers.webgpu={};Q5.renderers.webgpu.canvas=($,q)=>{let c=$.canvas;c.width=$.width=500;c.height=$.height=500;$._g=$.createGraphics(1,1);if($.colorMode)$.colorMode("rgb",1);let pass,mainView,colorIndex=1,colorStackIndex=8;$._pipelineConfigs=[];$._pipelines=[];let drawStack=$.drawStack=[];let colorStack=$.colorStack=new Float32Array(1e6);colorStack.set([0,0,0,1,1,1,1,1]);let mainLayout=Q5.device.createBindGroupLayout({label:"mainLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]});$.bindGroupLayouts=[mainLayout];let uniformBuffer=Q5.device.createBuffer({size:8,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let createMainView=()=>{mainView=Q5.device.createTexture({size:[$.canvas.width,$.canvas.height],sampleCount:4,format:"bgra8unorm",usage:GPUTextureUsage.RENDER_ATTACHMENT}).createView()};$._createCanvas=(w,h,opt)=>{q.ctx=q.drawingContext=c.getContext("webgpu");opt.format??=navigator.gpu.getPreferredCanvasFormat();opt.device??=Q5.device;if(opt.alpha)opt.alphaMode="premultiplied";$.ctx.configure(opt);Q5.device.queue.writeBuffer(uniformBuffer,0,new Float32Array([$.canvas.hw,$.canvas.hh]));createMainView();return c};$._resizeCanvas=(w,h)=>{$._setCanvasSize(w,h);createMainView()};$.pixelDensity=v=>{if(!v||v==$._pixelDensity)return $._pixelDensity;$._pixelDensity=v;$._setCanvasSize(c.w,c.h);createMainView();return v};let addColor=(r,g,b,a=1)=>{if(typeof r=="string")r=$.color(r);else if(b==undefined){a=g??1;g=b=r}if(r._q5Color){a=r.a;b=r.b;g=r.g;r=r.r}let cs=colorStack,i=colorStackIndex;cs[i++]=r;cs[i++]=g;cs[i++]=b;cs[i++]=a;colorStackIndex=i;colorIndex++};$._stroke=0;$._fill=$._tint=$._globalAlpha=1;$._doFill=$._doStroke=true;$.fill=(r,g,b,a)=>{addColor(r,g,b,a);$._doFill=$._fillSet=true;$._fill=colorIndex};$.stroke=(r,g,b,a)=>{addColor(r,g,b,a);$._doStroke=$._strokeSet=true;$._stroke=colorIndex};$.tint=(r,g,b,a)=>{addColor(r,g,b,a);$._tint=colorIndex};$.opacity=a=>$._globalAlpha=a;$.noFill=()=>$._doFill=false;$.noStroke=()=>$._doStroke=false;$.noTint=()=>$._tint=1;$._strokeWeight=1;$._hsw=.5;$._scaledSW=1;$.strokeWeight=v=>{v=Math.abs(v);$._strokeWeight=v;$._scaledSW=v*$._scale;$._hsw=v/2};const MAX_TRANSFORMS=1e7,MATRIX_SIZE=16,transforms=new Float32Array(MAX_TRANSFORMS*MATRIX_SIZE);let matrix,matrices=[],matricesIndexStack=[];$._matrixDirty=false;matrices.push([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);transforms.set(matrices[0]);$.resetMatrix=()=>{matrix=matrices[0].slice();$._matrixIndex=0};$.resetMatrix();$.translate=(x,y,z)=>{if(!x&&!y&&!z)return;matrix[12]+=x;matrix[13]-=y;matrix[14]+=z||0;$._matrixDirty=true};$.rotate=a=>{if(!a)return;if($._angleMode)a*=$._DEGTORAD;let cosR=Math.cos(a),sinR=Math.sin(a),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];if(m0==1&&!m1&&!m4&&m5==1){m[0]=cosR;m[1]=-sinR;m[4]=sinR;m[5]=cosR}else{m[0]=m0*cosR+m1*sinR;m[1]=m1*cosR-m0*sinR;m[4]=m4*cosR+m5*sinR;m[5]=m5*cosR-m4*sinR}$._matrixDirty=true};$._scale=1;$.scale=(x=1,y,z=1)=>{y??=x;$._scale=Math.max(Math.abs(x),Math.abs(y));$._scaledSW=$._strokeWeight*$._scale;let m=matrix;m[0]*=x;m[1]*=x;m[2]*=x;m[3]*=x;m[4]*=y;m[5]*=y;m[6]*=y;m[7]*=y;m[8]*=z;m[9]*=z;m[10]*=z;m[11]*=z;$._matrixDirty=true};$.shearX=ang=>{if(!ang)return;if($._angleMode)ang*=$._DEGTORAD;let tanAng=Math.tan(ang),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];m[0]=m0+m4*tanAng;m[1]=m1+m5*tanAng;$._matrixDirty=true};$.shearY=ang=>{if(!ang)return;if($._angleMode)ang*=$._DEGTORAD;let tanAng=Math.tan(ang),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];m[4]=m4+m0*tanAng;m[5]=m5+m1*tanAng;$._matrixDirty=true};$.applyMatrix=(...args)=>{let m;if(args.length==1)m=args[0];else m=args;if(m.length==9){m=[m[0],m[1],0,m[2],m[3],m[4],0,m[5],0,0,1,0,m[6],m[7],0,m[8]]}else if(m.length!=16){throw new Error("Matrix must be a 3x3 or 4x4 array.")}matrix=m.slice();$._matrixDirty=true};$._saveMatrix=()=>{transforms.set(matrix,matrices.length*MATRIX_SIZE);$._matrixIndex=matrices.length;matrices.push(matrix.slice());$._matrixDirty=false};$.pushMatrix=()=>{if($._matrixDirty)$._saveMatrix();matricesIndexStack.push($._matrixIndex)};$.popMatrix=()=>{if(!matricesIndexStack.length){return console.warn("Matrix index stack is empty!")}let idx=matricesIndexStack.pop();matrix=matrices[idx].slice();$._matrixIndex=idx;$._matrixDirty=false};let _pushStyles=$.pushStyles;$.pushStyles=()=>{_pushStyles();$.strokeWeight($._strokeWeight)};$.push=()=>{$.pushMatrix();$.pushStyles()};$.pop=()=>{$.popMatrix();$.popStyles()};$._calcBox=(x,y,w,h,mode)=>{let hw=w/2;let hh=h/2;let l,r,t,b;if(!mode||mode=="corner"){l=x;r=x+w;t=-y;b=-(y+h)}else if(mode=="center"){l=x-hw;r=x+hw;t=-(y-hh);b=-(y+hh)}else{l=x;r=w;t=-y;b=-h}return[l,r,t,b]};let blendFactors=["zero","one","src-alpha","one-minus-src-alpha","dst","dst-alpha","one-minus-dst-alpha","one-minus-src"];let blendOps=["add","subtract","reverse-subtract","min","max"];const blendModes={normal:[2,3,0,2,3,0],additive:[1,1,0,1,1,0]};$.blendConfigs={};for(const[name,mode]of Object.entries(blendModes)){$.blendConfigs[name]={color:{srcFactor:blendFactors[mode[0]],dstFactor:blendFactors[mode[1]],operation:blendOps[mode[2]]},alpha:{srcFactor:blendFactors[mode[3]],dstFactor:blendFactors[mode[4]],operation:blendOps[mode[5]]}}}$._blendMode="normal";$.blendMode=mode=>{if(mode==$._blendMode)return;if(mode=="source-over")mode="normal";if(mode=="lighter")mode="additive";mode=mode.toLowerCase().replace(/[ -]/g,"_");$._blendMode=mode;for(let i=0;i<$._pipelines.length;i++){$._pipelineConfigs[i].fragment.targets[0].blend=$.blendConfigs[mode];$._pipelines[i]=Q5.device.createRenderPipeline($._pipelineConfigs[i])}};$.clear=()=>{};$._beginRender=()=>{$.encoder=Q5.device.createCommandEncoder();pass=q.pass=$.encoder.beginRenderPass({label:"q5-webgpu",colorAttachments:[{view:mainView,resolveTarget:$.ctx.getCurrentTexture().createView(),loadOp:"clear",storeOp:"store",clearValue:[0,0,0,0]}]})};$._render=()=>{let transformBuffer=Q5.device.createBuffer({size:matrices.length*MATRIX_SIZE*4,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0,matrices.length*MATRIX_SIZE));transformBuffer.unmap();let colorsBuffer=Q5.device.createBuffer({size:colorStackIndex*4,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0,colorStackIndex));colorsBuffer.unmap();let mainBindGroup=Q5.device.createBindGroup({layout:mainLayout,entries:[{binding:0,resource:{buffer:uniformBuffer}},{binding:1,resource:{buffer:transformBuffer}},{binding:2,resource:{buffer:colorsBuffer}}]});pass.setBindGroup(0,mainBindGroup);for(let m of $._hooks.preRender)m();let drawVertOffset=0,imageVertOffset=0,textCharOffset=0,curPipelineIndex=-1;for(let i=0;i<drawStack.length;i+=2){let v=drawStack[i+1];if(curPipelineIndex!=drawStack[i]){curPipelineIndex=drawStack[i];pass.setPipeline($._pipelines[curPipelineIndex])}if(curPipelineIndex==0){pass.draw(v,1,drawVertOffset);drawVertOffset+=v}else if(curPipelineIndex==1){pass.setBindGroup(1,$._textureBindGroups[v]);pass.draw(4,1,imageVertOffset);imageVertOffset+=4}else if(curPipelineIndex==2){let o=drawStack[i+2];pass.setBindGroup(1,$._fonts[o].bindGroup);pass.setBindGroup(2,$._textBindGroup);pass.draw(4,v,0,textCharOffset);textCharOffset+=v;i++}}};$._finishRender=()=>{pass.end();let commandBuffer=$.encoder.finish();Q5.device.queue.submit([commandBuffer]);q.pass=$.encoder=null;$.drawStack=drawStack=[];colorIndex=1;colorStackIndex=8;matrices=[matrices[0]];matricesIndexStack=[];for(let m of $._hooks.postRender)m()}};Q5.initWebGPU=async()=>{if(!navigator.gpu){console.warn("q5 WebGPU not supported on this browser! Use Google Chrome or Edge.");return false}if(!Q5.device){let adapter=await navigator.gpu.requestAdapter();if(!adapter){console.warn("q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.");return false}Q5.device=await adapter.requestDevice()}return true};Q5.webgpu=async function(scope,parent){if(!scope||scope=="global")Q5._hasGlobal=true;if(!await Q5.initWebGPU()){return new Q5(scope,parent,"webgpu-fallback")}return new Q5(scope,parent,"webgpu")};Q5.renderers.webgpu.drawing=($,q)=>{let c=$.canvas,drawStack=$.drawStack,vertexStack=new Float32Array(1e7),vertIndex=0;const TAU=Math.PI*2;const HALF_PI=Math.PI/2;let drawingShader=Q5.device.createShaderModule({label:"drawingShader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@location(0) pos: vec2f,\n\t@location(1) colorIndex: f32,\n\t@location(2) matrixIndex: f32\n}\nstruct FragmentParams {\n\t@builtin(position) position: vec4f,\n\t@location(0) color: vec4f\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@vertex\nfn vertexMain(v: VertexParams) -> FragmentParams {\n\tvar vert = vec4f(v.pos, 0.0, 1.0);\n\tvert = transforms[i32(v.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f: FragmentParams;\n\tf.position = vert;\n\tf.color = colors[i32(v.colorIndex)];\n\treturn f;\n}\n\n@fragment\nfn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {\n\treturn color;\n}\n`});let vertexBufferLayout={arrayStride:16,attributes:[{format:"float32x2",offset:0,shaderLocation:0},{format:"float32",offset:8,shaderLocation:1},{format:"float32",offset:12,shaderLocation:2}]};let pipelineLayout=Q5.device.createPipelineLayout({label:"drawingPipelineLayout",bindGroupLayouts:$.bindGroupLayouts});$._pipelineConfigs[0]={label:"drawingPipeline",layout:pipelineLayout,vertex:{module:drawingShader,entryPoint:"vertexMain",buffers:[vertexBufferLayout]},fragment:{module:drawingShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[0]=Q5.device.createRenderPipeline($._pipelineConfigs[0]);const addVert=(x,y,ci,ti)=>{let v=vertexStack,i=vertIndex;v[i++]=x;v[i++]=y;v[i++]=ci;v[i++]=ti;vertIndex=i};const addRect=(x1,y1,x2,y2,x3,y3,x4,y4,ci,ti)=>{let v=vertexStack,i=vertIndex;v[i++]=x1;v[i++]=y1;v[i++]=ci;v[i++]=ti;v[i++]=x2;v[i++]=y2;v[i++]=ci;v[i++]=ti;v[i++]=x4;v[i++]=y4;v[i++]=ci;v[i++]=ti;v[i++]=x3;v[i++]=y3;v[i++]=ci;v[i++]=ti;vertIndex=i;drawStack.push(0,4)};const addArc=(x,y,a,b,startAngle,endAngle,n,ci,ti)=>{let angleRange=endAngle-startAngle;let angleIncrement=angleRange/n;let t=startAngle;let v=vertexStack,i=vertIndex;for(let j=0;j<=n;j++){v[i++]=x;v[i++]=y;v[i++]=ci;v[i++]=ti;let vx=x+a*Math.cos(t);let vy=y+b*Math.sin(t);v[i++]=vx;v[i++]=vy;v[i++]=ci;v[i++]=ti;t+=angleIncrement}vertIndex=i;drawStack.push(0,(n+1)*2)};const addArcStroke=(x,y,outerA,outerB,innerA,innerB,startAngle,endAngle,n,ci,ti)=>{let angleRange=endAngle-startAngle;let angleIncrement=angleRange/n;let t=startAngle;let v=vertexStack,i=vertIndex;for(let j=0;j<=n;j++){let vxOuter=x+outerA*Math.cos(t);let vyOuter=y+outerB*Math.sin(t);let vxInner=x+innerA*Math.cos(t);let vyInner=y+innerB*Math.sin(t);v[i++]=vxOuter;v[i++]=vyOuter;v[i++]=ci;v[i++]=ti;v[i++]=vxInner;v[i++]=vyInner;v[i++]=ci;v[i++]=ti;t+=angleIncrement}vertIndex=i;drawStack.push(0,(n+1)*2)};$.rectMode=x=>$._rectMode=x;$.rect=(x,y,w,h,rr=0)=>{let[l,r,t,b]=$._calcBox(x,y,w,h,$._rectMode);let ci,ti;if($._matrixDirty)$._saveMatrix();ti=$._matrixIndex;if(!rr){if($._doFill){ci=$._fill;addRect(l,t,r,t,r,b,l,b,ci,ti)}if($._doStroke){ci=$._stroke;let sw=$._strokeWeight/2;let lsw=l-sw,rsw=r+sw,tsw=t+sw,bsw=b-sw,lpsw=l+sw,rpsw=r-sw,tpsw=t-sw,bpsw=b+sw;addRect(lsw,tpsw,rsw,tpsw,rsw,tsw,lsw,tsw,ci,ti);addRect(lsw,bsw,rsw,bsw,rsw,bpsw,lsw,bpsw,ci,ti);tsw=t-sw;bsw=b+sw;addRect(lsw,tsw,lpsw,tsw,lpsw,bsw,lsw,bsw,ci,ti);addRect(rpsw,tsw,rsw,tsw,rsw,bsw,rpsw,bsw,ci,ti)}return}l+=rr;r-=rr;t-=rr;b+=rr;rr=Math.min(rr,Math.min(w,h)/2);let n=getArcSegments(rr*$._scale);let trr=t+rr,brr=b-rr,lrr=l-rr,rrr=r+rr;if($._doFill){ci=$._fill;addArc(r,b,rr,rr,-HALF_PI,0,n,ci,ti);addArc(l,b,rr,rr,-Math.PI,-HALF_PI,n,ci,ti);addArc(l,t,rr,rr,Math.PI,HALF_PI,n,ci,ti);addArc(r,t,rr,rr,0,HALF_PI,n,ci,ti);addRect(l,trr,r,trr,r,brr,l,brr,ci,ti);addRect(l,t,lrr,t,lrr,b,l,b,ci,ti);addRect(rrr,t,r,t,r,b,rrr,b,ci,ti)}if($._doStroke){ci=$._stroke;let hsw=$._hsw;let outerA=rr+hsw,outerB=rr+hsw,innerA=rr-hsw,innerB=rr-hsw;addArcStroke(r,b,outerA,outerB,innerA,innerB,-HALF_PI,0,n,ci,ti);addArcStroke(l,b,outerA,outerB,innerA,innerB,-Math.PI,-HALF_PI,n,ci,ti);addArcStroke(l,t,outerA,outerB,innerA,innerB,Math.PI,HALF_PI,n,ci,ti);addArcStroke(r,t,outerA,outerB,innerA,innerB,0,HALF_PI,n,ci,ti);let lrrMin=lrr-hsw,lrrMax=lrr+hsw,rrrMin=rrr-hsw,rrrMax=rrr+hsw,trrMin=trr-hsw,trrMax=trr+hsw,brrMin=brr-hsw,brrMax=brr+hsw;addRect(lrrMin,t,lrrMax,t,lrrMax,b,lrrMin,b,ci,ti);addRect(rrrMin,t,rrrMax,t,rrrMax,b,rrrMin,b,ci,ti);addRect(l,trrMin,r,trrMin,r,trrMax,l,trrMax,ci,ti);addRect(l,brrMin,r,brrMin,r,brrMax,l,brrMax,ci,ti)}};$.square=(x,y,s)=>$.rect(x,y,s,s);const getArcSegments=d=>d<4?6:d<6?8:d<10?10:d<16?12:d<20?14:d<22?16:d<24?18:d<28?20:d<34?22:d<42?24:d<48?26:d<56?28:d<64?30:d<72?32:d<84?34:d<96?36:d<98?38:d<113?40:d<149?44:d<199?48:d<261?52:d<353?56:d<461?60:d<585?64:d<1200?70:d<1800?80:d<2400?90:100;$._ellipseMode=Q5.CENTER;$.ellipseMode=x=>$._ellipseMode=x;$.ellipse=(x,y,w,h)=>{let n=getArcSegments(Math.max(Math.abs(w),Math.abs(h))*$._scale);let a=w/2;let b=w==h?a:h/2;if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex;if($._doFill){addArc(x,-y,a,b,0,TAU,n,$._fill,ti)}if($._doStroke){let sw=$._strokeWeight/2;addArcStroke(x,-y,a+sw,b+sw,a-sw,b-sw,0,TAU,n,$._stroke,ti)}};$.circle=(x,y,d)=>$.ellipse(x,y,d,d);$.arc=(x,y,w,h,start,stop)=>{if(start===stop)return $.ellipse(x,y,w,h);if($._angleMode){start=$.radians(start);stop=$.radians(stop)}start%=TAU;stop%=TAU;if(start<0)start+=TAU;if(stop<0)stop+=TAU;if(start>stop)stop+=TAU;if(start==stop)return $.ellipse(x,y,w,h);let a,b;if($._ellipseMode==$.CENTER){a=w/2;b=h/2}else if($._ellipseMode==$.RADIUS){a=w;b=h}else if($._ellipseMode==$.CORNER){x+=w/2;y+=h/2;a=w/2;b=h/2}else if($._ellipseMode==$.CORNERS){x=(x+w)/2;y=(y+h)/2;a=(w-x)/2;b=(h-y)/2}let ti=$._matrixIndex;if($._matrixDirty)$._saveMatrix();let n=getArcSegments(Math.max(Math.abs(w),Math.abs(h))*$._scale);if($._doFill){addArc(x,-y,a,b,start,stop,n,$._fill,ti)}if($._doStroke){let sw=$._strokeWeight;addArcStroke(x,-y,a+sw,b+sw,a-sw,b-sw,start,stop,n,$._stroke,ti)}};$.point=(x,y)=>{if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex,ci=$._stroke,sw=$._strokeWeight;if($._scaledSW<2){let[l,r,t,b]=$._calcBox(x,y,sw,sw,"corner");addRect(l,t,r,t,r,b,l,b,ci,ti)}else{let n=getArcSegments($._scaledSW);sw/=2;addArc(x,-y,sw,sw,0,TAU,n,ci,ti)}};$._strokeJoin="round";$.strokeJoin=x=>{$._strokeJoin=x};$.line=(x1,y1,x2,y2)=>{if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex,ci=$._stroke,sw=$._strokeWeight,hsw=$._hsw;let dx=x2-x1,dy=y2-y1,length=Math.hypot(dx,dy);let px=-(dy/length)*hsw,py=dx/length*hsw;addRect(x1+px,-y1-py,x1-px,-y1+py,x2-px,-y2+py,x2+px,-y2-py,ci,ti);if($._scaledSW>2&&$._strokeJoin!="none"){let n=getArcSegments($._scaledSW);addArc(x1,-y1,hsw,hsw,0,TAU,n,ci,ti);addArc(x2,-y2,hsw,hsw,0,TAU,n,ci,ti)}};let shapeVertCount;let sv=[];let curveVertices=[];$.beginShape=()=>{shapeVertCount=0;sv=[];curveVertices=[]};$.vertex=(x,y)=>{if($._matrixDirty)$._saveMatrix();sv.push(x,-y,$._fill,$._matrixIndex);shapeVertCount++};$.curveVertex=(x,y)=>{if($._matrixDirty)$._saveMatrix();curveVertices.push({x:x,y:-y})};$.endShape=close=>{if(curveVertices.length>0){let points=[...curveVertices];if(points.length<4){while(points.length<4){points.unshift(points[0]);points.push(points[points.length-1])}}for(let i=0;i<points.length-3;i++){let p0=points[i];let p1=points[i+1];let p2=points[i+2];let p3=points[i+3];for(let t=0;t<=1;t+=.1){let t2=t*t;let t3=t2*t;let x=.5*(2*p1.x+(-p0.x+p2.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*t2+(-p0.x+3*p1.x-3*p2.x+p3.x)*t3);let y=.5*(2*p1.y+(-p0.y+p2.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*t2+(-p0.y+3*p1.y-3*p2.y+p3.y)*t3);sv.push(x,y,$._fill,$._matrixIndex);shapeVertCount++}}}if(shapeVertCount<3){throw new Error("A shape must have at least 3 vertices.")}if(close){let firstIndex=0;let lastIndex=(shapeVertCount-1)*4;let firstX=sv[firstIndex];let firstY=sv[firstIndex+1];let lastX=sv[lastIndex];let lastY=sv[lastIndex+1];if(firstX!==lastX||firstY!==lastY){sv.push(firstX,firstY,sv[firstIndex+2],sv[firstIndex+3]);shapeVertCount++}}if($._doFill){if(shapeVertCount==5){addVert(sv[0],sv[1],sv[2],sv[3]);addVert(sv[4],sv[5],sv[6],sv[7]);addVert(sv[12],sv[13],sv[14],sv[15]);addVert(sv[8],sv[9],sv[10],sv[11]);drawStack.push(0,4)}else{for(let i=1;i<shapeVertCount-1;i++){let v0=0;let v1=i*4;let v2=(i+1)*4;addVert(sv[v0],sv[v0+1],sv[v0+2],sv[v0+3]);addVert(sv[v1],sv[v1+1],sv[v1+2],sv[v1+3]);addVert(sv[v2],sv[v2+1],sv[v2+2],sv[v2+3])}drawStack.push(0,(shapeVertCount-2)*3)}}if($._doStroke){let hsw=$._hsw,n=getArcSegments($._scaledSW),ti=$._matrixIndex,ogStrokeJoin=$._strokeJoin;$._strokeJoin="none";for(let i=0;i<shapeVertCount-1;i++){let v1=i*4;let v2=(i+1)*4;$.line(sv[v1],-sv[v1+1],sv[v2],-sv[v2+1]);addArc(sv[v1],sv[v1+1],hsw,hsw,0,TAU,n,$._stroke,ti)}if(close){let v1=(shapeVertCount-1)*4;let v2=0;$.line(sv[v1],-sv[v1+1],sv[v2],-sv[v2+1])}$._strokeJoin=ogStrokeJoin}shapeVertCount=0;sv=[];curveVertices=[]};$.triangle=(x1,y1,x2,y2,x3,y3)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.endShape(true)};$.quad=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.vertex(x4,y4);$.endShape(true)};$.background=(r,g,b,a)=>{$.push();$.resetMatrix();if(r.canvas){let img=r;$._imageMode="corner";$.image(img,-c.hw,-c.hh,c.w,c.h)}else{$._rectMode="corner";$.fill(r,g,b,a);$._doStroke=false;$.rect(-c.hw,-c.hh,c.w,c.h)}$.pop()};$._hooks.preRender.push((()=>{$.pass.setPipeline($._pipelines[0]);let vertexBuffer=Q5.device.createBuffer({size:vertIndex*4,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0,vertIndex));vertexBuffer.unmap();$.pass.setVertexBuffer(0,vertexBuffer)}));$._hooks.postRender.push((()=>{drawStack=$.drawStack;vertIndex=0}))};Q5.renderers.webgpu.image=($,q)=>{let vertexStack=new Float32Array(1e7),vertIndex=0;let imageShader=Q5.device.createShaderModule({label:"imageShader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@location(0) pos: vec2f,\n\t@location(1) texCoord: vec2f,\n\t@location(2) tintIndex: f32,\n\t@location(3) matrixIndex: f32,\n\t@location(4) globalAlpha: f32\n}\nstruct FragmentParams {\n\t@builtin(position) position: vec4f,\n\t@location(0) texCoord: vec2f,\n\t@location(1) tintIndex: f32,\n\t@location(2) globalAlpha: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@group(1) @binding(0) var samp: sampler;\n@group(1) @binding(1) var texture: texture_2d<f32>;\n\n@vertex\nfn vertexMain(v: VertexParams) -> FragmentParams {\n\tvar vert = vec4f(v.pos, 0.0, 1.0);\n\tvert = transforms[i32(v.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f: FragmentParams;\n\tf.position = vert;\n\tf.texCoord = v.texCoord;\n\tf.tintIndex = v.tintIndex;\n\tf.globalAlpha = v.globalAlpha;\n\treturn f;\n}\n\n@fragment\nfn fragmentMain(f: FragmentParams) -> @location(0) vec4f {\n\t\tlet texColor = textureSample(texture, samp, f.texCoord);\n\t\tlet tintColor = colors[i32(f.tintIndex)];\n\t\t\n\t\t// Mix original and tinted colors using tint alpha as blend factor\n\t\tlet tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);\n\t\treturn mix(texColor, tinted, tintColor.a);\n}\n\t`});$._textureBindGroups=[];let textureLayout=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 vertexBufferLayout={arrayStride:28,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"},{shaderLocation:2,offset:16,format:"float32"},{shaderLocation:3,offset:20,format:"float32"},{shaderLocation:4,offset:24,format:"float32"}]};const pipelineLayout=Q5.device.createPipelineLayout({label:"imagePipelineLayout",bindGroupLayouts:[...$.bindGroupLayouts,textureLayout]});$._pipelineConfigs[1]={label:"imagePipeline",layout:pipelineLayout,vertex:{module:imageShader,entryPoint:"vertexMain",buffers:[{arrayStride:0,attributes:[]},vertexBufferLayout]},fragment:{module:imageShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[1]=Q5.device.createRenderPipeline($._pipelineConfigs[1]);let sampler;let makeSampler=filter=>{sampler=Q5.device.createSampler({magFilter:filter,minFilter:filter})};$.smooth=()=>makeSampler("linear");$.noSmooth=()=>makeSampler("nearest");$.smooth();let MAX_TEXTURES=12e3;$._textures=[];let tIdx=0;$._createTexture=img=>{if(img.canvas)img=img.canvas;let textureSize=[img.width,img.height,1];let texture=Q5.device.createTexture({size:textureSize,format:"bgra8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:img},{texture:texture,colorSpace:$.canvas.colorSpace},textureSize);$._textures[tIdx]=texture;img.textureIndex=tIdx;const textureBindGroup=Q5.device.createBindGroup({layout:textureLayout,entries:[{binding:0,resource:sampler},{binding:1,resource:texture.createView()}]});$._textureBindGroups[tIdx]=textureBindGroup;tIdx=(tIdx+1)%MAX_TEXTURES;if($._textures[tIdx]){$._textures[tIdx].destroy();delete $._textures[tIdx];delete $._textureBindGroups[tIdx]}};$.loadImage=(src,cb)=>{q._preloadCount++;let g=$._g.loadImage(src,(img=>{g.defaultWidth=img.width*$._defaultImageScale;g.defaultHeight=img.height*$._defaultImageScale;$._createTexture(img);q._preloadCount--;if(cb)cb(g)}));return g};$.imageMode=x=>$._imageMode=x;const addVert=(x,y,u,v,ci,ti,ga)=>{let s=vertexStack,i=vertIndex;s[i++]=x;s[i++]=y;s[i++]=u;s[i++]=v;s[i++]=ci;s[i++]=ti;s[i++]=ga;vertIndex=i};$.image=(img,dx=0,dy=0,dw,dh,sx=0,sy=0,sw,sh)=>{let g=img;if(img.canvas)img=img.canvas;if(img.textureIndex==undefined)return;if($._matrixDirty)$._saveMatrix();let w=img.width,h=img.height,pd=g._pixelDensity||1;dw??=g.defaultWidth;dh??=g.defaultHeight;sw??=w;sh??=h;dw*=pd;dh*=pd;let[l,r,t,b]=$._calcBox(dx,dy,dw,dh,$._imageMode);let u0=sx/w,v0=sy/h,u1=(sx+sw)/w,v1=(sy+sh)/h,ti=$._matrixIndex,ci=$._tint,ga=$._globalAlpha;addVert(l,t,u0,v0,ci,ti,ga);addVert(r,t,u1,v0,ci,ti,ga);addVert(l,b,u0,v1,ci,ti,ga);addVert(r,b,u1,v1,ci,ti,ga);$.drawStack.push(1,img.textureIndex)};$._hooks.preRender.push((()=>{if(!$._textureBindGroups.length)return;$.pass.setPipeline($._pipelines[1]);let vertexBuffer=Q5.device.createBuffer({size:vertIndex*5,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0,vertIndex));vertexBuffer.unmap();$.pass.setVertexBuffer(1,vertexBuffer)}));$._hooks.postRender.push((()=>{vertIndex=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=($,q)=>{let textShader=Q5.device.createShaderModule({label:"MSDF text shader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@builtin(vertex_index) vertex : u32,\n\t@builtin(instance_index) instance : u32\n}\nstruct FragmentParams {\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\tmatrixIndex: f32,\n\tfillIndex: f32,\n\tstrokeIndex: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@group(1) @binding(0) var fontTexture: texture_2d<f32>;\n@group(1) @binding(1) var fontSampler: sampler;\n@group(1) @binding(2) var<storage> fontChars: array<Char>;\n\n@group(2) @binding(0) var<storage> textChars: array<vec4f>;\n@group(2) @binding(1) var<storage> textMetadata: array<Text>;\n\nconst quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));\n\n@vertex\nfn vertexMain(v : VertexParams) -> FragmentParams {\n\tlet char = textChars[v.instance];\n\n\tlet text = textMetadata[i32(char.w)];\n\n\tlet fontChar = fontChars[i32(char.z)];\n\n\tlet charPos = ((quad[v.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.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f : FragmentParams;\n\tf.position = vert;\n\tf.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;\n\tf.fillColor = colors[i32(text.fillIndex)];\n\treturn f;\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(f : FragmentParams) -> @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(f.texCoord.x), dpdyFine(f.texCoord.x)));\n\tlet dy = sz.y*length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));\n\tlet toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);\n\tlet sigDist = sampleMsdf(f.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(f.fillColor.rgb, f.fillColor.a * alpha);\n}\n`});let textBindGroupLayout=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"}}]});let fontSampler=Q5.device.createSampler({minFilter:"linear",magFilter:"linear",mipmapFilter:"linear",maxAnisotropy:16});let fontBindGroupLayout=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"}}]});let fontPipelineLayout=Q5.device.createPipelineLayout({bindGroupLayouts:[...$.bindGroupLayouts,fontBindGroupLayout,textBindGroupLayout]});$._pipelineConfigs[2]={label:"msdf font pipeline",layout:fontPipelineLayout,vertex:{module:textShader,entryPoint:"vertexMain"},fragment:{module:textShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[2]=Q5.device.createRenderPipeline($._pipelineConfigs[2]);class MsdfFont{constructor(bindGroup,lineHeight,chars,kernings){this.bindGroup=bindGroup;this.lineHeight=lineHeight;this.chars=chars;this.kernings=kernings;let charArray=Object.values(chars);this.charCount=charArray.length;this.defaultChar=charArray[0]}getChar(charCode){return this.chars[charCode]??this.defaultChar}getXAdvance(charCode,nextCharCode=-1){let char=this.getChar(charCode);if(nextCharCode>=0){let kerning=this.kernings.get(charCode);if(kerning){return char.xadvance+(kerning.get(nextCharCode)??0)}}return char.xadvance}}$._fonts=[];let fonts={};let createFont=async(fontJsonUrl,fontName,cb)=>{q._preloadCount++;let res=await fetch(fontJsonUrl);if(res.status==404){q._preloadCount--;return""}let atlas=await res.json();let slashIdx=fontJsonUrl.lastIndexOf("/");let baseUrl=slashIdx!=-1?fontJsonUrl.substring(0,slashIdx+1):"";res=await fetch(baseUrl+atlas.pages[0]);let img=await createImageBitmap(await res.blob());let imgSize=[img.width,img.height,1];let texture=Q5.device.createTexture({label:`MSDF ${fontName}`,size:imgSize,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:img},{texture:texture},imgSize);if(typeof atlas.chars=="string"){atlas.chars=$.CSV.parse(atlas.chars," ");atlas.kernings=$.CSV.parse(atlas.kernings," ")}let charCount=atlas.chars.length;let charsBuffer=Q5.device.createBuffer({size:charCount*32,usage:GPUBufferUsage.STORAGE,mappedAtCreation:true});let fontChars=new Float32Array(charsBuffer.getMappedRange());let u=1/atlas.common.scaleW;let v=1/atlas.common.scaleH;let chars={};let o=0;for(let[i,char]of atlas.chars.entries()){chars[char.id]=char;chars[char.id].charIndex=i;fontChars[o]=char.x*u;fontChars[o+1]=char.y*v;fontChars[o+2]=char.width*u;fontChars[o+3]=char.height*v;fontChars[o+4]=char.width;fontChars[o+5]=char.height;fontChars[o+6]=char.xoffset;fontChars[o+7]=-char.yoffset;o+=8}charsBuffer.unmap();let fontBindGroup=Q5.device.createBindGroup({label:"msdf font bind group",layout:fontBindGroupLayout,entries:[{binding:0,resource:texture.createView()},{binding:1,resource:fontSampler},{binding:2,resource:{buffer:charsBuffer}}]});let kernings=new Map;if(atlas.kernings){for(let kerning of atlas.kernings){let charKerning=kernings.get(kerning.first);if(!charKerning){charKerning=new Map;kernings.set(kerning.first,charKerning)}charKerning.set(kerning.second,kerning.amount)}}$._font=new MsdfFont(fontBindGroup,atlas.common.lineHeight,chars,kernings);$._font.index=$._fonts.length;$._fonts.push($._font);fonts[fontName]=$._font;q._preloadCount--;if(cb)cb(fontName)};$._g.colorMode($.RGB,1);$.loadFont=(url,cb)=>{let ext=url.slice(url.lastIndexOf(".")+1);if(ext!="json")return $._g.loadFont(url,cb);let fontName=url.slice(url.lastIndexOf("/")+1,url.lastIndexOf("-"));let f={family:fontName};f._loader=createFont(url,fontName,(()=>{delete f._loader;if(cb)cb(f)}));if($._disablePreload)return f._loader;return f};$._loadDefaultFont=fontName=>{fonts[fontName]=null;if(navigator.onLine){$.loadFont(`https://q5js.org/fonts/${fontName}-msdf.json`)}else{$.loadFont(`/node_modules/q5/builtinFonts/${fontName}-msdf.json`)}};$._textSize=18;$._textAlign="left";$._textBaseline="alphabetic";let leadingSet=false,leading=22.5,leadDiff=4.5,leadPercent=1.25;$.textFont=fontName=>{if(typeof fontName!="string")fontName=fontName.family;let font=fonts[fontName];if(font)$._font=font;else if(font===undefined)$._loadDefaultFont(fontName)};$.textSize=size=>{$._textSize=size;if(!leadingSet){leading=size*leadPercent;leadDiff=leading-size}};$.textLeading=lineHeight=>{$._font.lineHeight=leading=lineHeight;leadDiff=leading-$._textSize;leadPercent=leading/$._textSize;leadingSet=true};$.textAlign=(horiz,vert)=>{$._textAlign=horiz;if(vert)$._textBaseline=vert};let charStack=[],textStack=[];let measureText=(font,text,charCallback)=>{let maxWidth=0,offsetX=0,offsetY=0,line=0,printedCharCount=0,lineWidths=[],nextCharCode=text.charCodeAt(0);for(let i=0;i<text.length;++i){let charCode=nextCharCode;nextCharCode=i<text.length-1?text.charCodeAt(i+1):-1;switch(charCode){case 10:lineWidths.push(offsetX);line++;maxWidth=Math.max(maxWidth,offsetX);offsetX=0;offsetY-=font.lineHeight*leadPercent;break;case 13:break;case 32:offsetX+=font.getXAdvance(charCode);break;case 9:offsetX+=font.getXAdvance(charCode)*2;break;default:if(charCallback){charCallback(offsetX,offsetY,line,font.getChar(charCode))}offsetX+=font.getXAdvance(charCode,nextCharCode);printedCharCount++}}lineWidths.push(offsetX);maxWidth=Math.max(maxWidth,offsetX);return{width:maxWidth,height:lineWidths.length*font.lineHeight*leadPercent,lineWidths:lineWidths,printedCharCount:printedCharCount}};$.text=(str,x,y,w,h)=>{if(!$._font){if($._font!==null)$.textFont("sans-serif");return}let type=typeof str;if(type!="string"){if(type=="object")str=str.toString();else str=str+""}if(str.length>w){let wrapped=[];let i=0;while(i<str.length&&wrapped.length<h){let max=i+w;if(max>=str.length){wrapped.push(str.slice(i));break}let end=str.lastIndexOf(" ",max);if(end==-1||end<i)end=max;wrapped.push(str.slice(i,end));i=end+1}str=wrapped.join("\n")}let spaces=0,hasNewline;for(let i=0;i<str.length;i++){let c=str[i];switch(c){case"\n":hasNewline=true;case"\r":case"\t":case" ":spaces++}}let charsData=[];let ta=$._textAlign,tb=$._textBaseline,textIndex=textStack.length,o=0,measurements;if(ta=="left"&&!hasNewline){measurements=measureText($._font,str,((textX,textY,line,char)=>{charsData[o]=textX;charsData[o+1]=textY;charsData[o+2]=char.charIndex;charsData[o+3]=textIndex;o+=4}));if(tb=="alphabetic")y-=$._textSize;else if(tb=="center")y-=$._textSize*.5;else if(tb=="bottom")y-=leading}else{measurements=measureText($._font,str);let offsetY=0;if(tb=="alphabetic")y-=$._textSize;else if(tb=="center")offsetY=measurements.height*.5;else if(tb=="bottom")offsetY=measurements.height;measureText($._font,str,((textX,textY,line,char)=>{let offsetX=0;if(ta=="center"){offsetX=measurements.width*-.5-(measurements.width-measurements.lineWidths[line])*-.5}else if(ta=="right"){offsetX=-measurements.lineWidths[line]}charsData[o]=textX+offsetX;charsData[o+1]=textY+offsetY;charsData[o+2]=char.charIndex;charsData[o+3]=textIndex;o+=4}))}charStack.push(charsData);let txt=[];if($._matrixDirty)$._saveMatrix();txt[0]=x;txt[1]=-y;txt[2]=$._textSize/44;txt[3]=$._matrixIndex;txt[4]=$._fillSet?$._fill:0;txt[5]=$._stroke;textStack.push(txt);$.drawStack.push(2,measurements.printedCharCount,$._font.index)};$.textWidth=str=>{if(!$._font)return 0;return measureText($._font,str).width};$.createTextImage=(str,w,h)=>{$._g.textSize($._textSize);if($._doFill){let fi=$._fill*4;$._g.fill(colorStack.slice(fi,fi+4))}if($._doStroke){let si=$._stroke*4;$._g.stroke(colorStack.slice(si,si+4))}let img=$._g.createTextImage(str,w,h);if(img.canvas.textureIndex==undefined){$._createTexture(img)}else if(img.modified){let cnv=img.canvas;let textureSize=[cnv.width,cnv.height,1];let texture=$._textures[cnv.textureIndex];Q5.device.queue.copyExternalImageToTexture({source:cnv},{texture:texture,colorSpace:$.canvas.colorSpace},textureSize);img.modified=false}return img};$.textImage=(img,x,y)=>{if(typeof img=="string")img=$.createTextImage(img);let og=$._imageMode;$._imageMode="corner";let ta=$._textAlign;if(ta=="center")x-=img.canvas.hw;else if(ta=="right")x-=img.width;let bl=$._textBaseline;if(bl=="alphabetic")y-=img._leading;else if(bl=="center")y-=img._middle;else if(bl=="bottom")y-=img._bottom;else if(bl=="top")y-=img._top;$.image(img,x,y);$._imageMode=og};$._hooks.preRender.push((()=>{if(!charStack.length)return;let totalTextSize=0;for(let charsData of charStack){totalTextSize+=charsData.length*4}let charBuffer=Q5.device.createBuffer({size:totalTextSize,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(charBuffer.getMappedRange()).set(charStack.flat());charBuffer.unmap();let totalMetadataSize=textStack.length*6*4;let textBuffer=Q5.device.createBuffer({label:"textBuffer",size:totalMetadataSize,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(textBuffer.getMappedRange()).set(textStack.flat());textBuffer.unmap();$._textBindGroup=Q5.device.createBindGroup({label:"msdf text bind group",layout:textBindGroupLayout,entries:[{binding:0,resource:{buffer:charBuffer}},{binding:1,resource:{buffer:textBuffer}}]})}));$._hooks.postRender.push((()=>{charStack=[];textStack=[]}))};
8
+ function Q5(scope,parent,renderer){let $=this;$._q5=true;$._parent=parent;if(renderer=="webgpu-fallback"){$._webgpuFallback=true;$._renderer="c2d"}else{$._renderer=renderer||Q5.render}$._preloadCount=0;let autoLoaded=scope=="auto";scope??="global";if(scope=="auto"){if(!(window.setup||window.update||window.draw))return;scope="global"}$._scope=scope;let globalScope;if(scope=="global"){Q5._hasGlobal=$._isGlobal=true;globalScope=Q5._esm?globalThis:!Q5._server?window:global}let q=new Proxy($,{set:(t,p,v)=>{$[p]=v;if($._isGlobal)globalScope[p]=v;return true}});$.canvas=$.ctx=$.drawingContext=null;$.pixels=[];let looper=null;$.frameCount=0;$.deltaTime=16;$._targetFrameRate=0;$._targetFrameDuration=16.666666666666668;$._frameRate=$._fps=60;$._loop=true;$._hooks={postCanvas:[],preRender:[],postRender:[]};let millisStart=0;$.millis=()=>performance.now()-millisStart;$.noCanvas=()=>{if($.canvas?.remove)$.canvas.remove();$.canvas=0;q.ctx=q.drawingContext=0};if(window){$.windowWidth=window.innerWidth;$.windowHeight=window.innerHeight;$.deviceOrientation=window.screen?.orientation?.type}$._incrementPreload=()=>q._preloadCount++;$._decrementPreload=()=>q._preloadCount--;$.disablePreloadSystem=()=>$._disablePreload=true;$._draw=timestamp=>{let ts=timestamp||performance.now();$._lastFrameTime??=ts-$._targetFrameDuration;if($._didResize){$.windowResized();$._didResize=false}if($._loop)looper=raf($._draw);else if($.frameCount&&!$._redraw)return;if(looper&&$.frameCount){let time_since_last=ts-$._lastFrameTime;if(time_since_last<$._targetFrameDuration-4)return}q.deltaTime=ts-$._lastFrameTime;$._frameRate=1e3/$.deltaTime;q.frameCount++;let pre=performance.now();$.resetMatrix();if($._beginRender)$._beginRender();for(let m of Q5.methods.pre)m.call($);try{$.draw()}catch(e){if(!Q5.disableFriendlyErrors&&$._askAI)$._askAI(e);if(!Q5.errorTolerant)$.noLoop();throw e}for(let m of Q5.methods.post)m.call($);if($._render)$._render();if($._finishRender)$._finishRender();$.postProcess();q.pmouseX=$.mouseX;q.pmouseY=$.mouseY;q.moveX=q.moveY=0;$._lastFrameTime=ts;let post=performance.now();$._fps=Math.round(1e3/(post-pre))};$.noLoop=()=>{$._loop=false;looper=null};$.loop=()=>{$._loop=true;if($._setupDone&&looper==null)$._draw()};$.isLooping=()=>$._loop;$.redraw=(n=1)=>{$._redraw=true;for(let i=0;i<n;i++){$._draw()}$._redraw=false};$.remove=()=>{$.noLoop();$.canvas.remove()};$.frameRate=hz=>{if(hz){$._targetFrameRate=hz;$._targetFrameDuration=1e3/hz}return $._frameRate};$.getTargetFrameRate=()=>$._targetFrameRate||60;$.getFPS=()=>$._fps;$.Element=function(a){this.elt=a};$._elements=[];$.describe=()=>{};$.log=$.print=console.log;for(let m in Q5.modules){Q5.modules[m]($,q)}let r=Q5.renderers[$._renderer];for(let m in r){r[m]($,q)}for(let k in Q5){if(k[1]!="_"&&k[1]==k[1].toUpperCase()){$[k]=Q5[k]}}if(scope=="graphics")return;if(scope=="global"){Object.assign(Q5,$);delete Q5.Q5}if($._webgpuFallback)$.colorMode("rgb",1);for(let m of Q5.methods.init){m.call($)}for(let[n,fn]of Object.entries(Q5.prototype)){if(n[0]!="_"&&typeof $[n]=="function")$[n]=fn.bind($)}if(scope=="global"){let props=Object.getOwnPropertyNames($);for(let p of props){if(p[0]!="_")globalScope[p]=$[p]}}if(typeof scope=="function")scope($);Q5._instanceCount++;let raf=window.requestAnimationFrame||function(cb){const idealFrameTime=$._lastFrameTime+$._targetFrameDuration;return setTimeout((()=>{cb(idealFrameTime)}),idealFrameTime-performance.now())};let t=globalScope||$;$._isTouchAware=t.touchStarted||t.touchMoved||t.mouseReleased;if($._isGlobal){$.preload=t.preload;$.setup=t.setup;$.draw=t.draw;$.postProcess=t.postProcess}$.preload??=()=>{};$.setup??=()=>{};$.draw??=()=>{};$.postProcess??=()=>{};let userFns=["setup","postProcess","mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","mouseWheel","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let k of userFns){if(!t[k])$[k]=()=>{};else if($._isGlobal){$[k]=event=>{try{return t[k](event)}catch(e){if($._askAI)$._askAI(e);throw e}}}}async function _setup(){$._startDone=true;if($._preloadCount>0)return raf(_setup);millisStart=performance.now();await $.setup();$._setupDone=true;if($.frameCount)return;if($.ctx===null)$.createCanvas(200,200);raf($._draw)}function _start(){try{$.preload();if(!$._startDone)_setup()}catch(e){if($._askAI)$._askAI(e);throw e}}if(autoLoaded)_start();else setTimeout(_start,32)}Q5.render="c2d";Q5.renderers={};Q5.modules={};Q5._server=typeof process=="object";Q5._esm=this===undefined;Q5._instanceCount=0;Q5._friendlyError=(msg,func)=>{if(!Q5.disableFriendlyErrors)console.error(func+": "+msg)};Q5._validateParameters=()=>true;Q5.methods={init:[],pre:[],post:[],remove:[]};Q5.prototype.registerMethod=(m,fn)=>Q5.methods[m].push(fn);Q5.prototype.registerPreloadMethod=(n,fn)=>Q5.prototype[n]=fn[n];if(Q5._server)global.p5??=global.Q5=Q5;if(typeof window=="object")window.p5??=window.Q5=Q5;else global.window=0;function createCanvas(w,h,opt){if(!Q5._hasGlobal){let q=new Q5;q.createCanvas(w,h,opt)}}Q5.version=Q5.VERSION="2.18";if(typeof document=="object"){document.addEventListener("DOMContentLoaded",(()=>{if(!Q5._hasGlobal)new Q5("auto")}))}Q5.modules.canvas=($,q)=>{$._OffscreenCanvas=window.OffscreenCanvas||function(){return document.createElement("canvas")};if(Q5._server){if(Q5._createServerCanvas){q.canvas=Q5._createServerCanvas(100,100)}}else if($._scope=="image"||$._scope=="graphics"){q.canvas=new $._OffscreenCanvas(100,100)}if(!$.canvas){if(typeof document=="object"){q.canvas=document.createElement("canvas");$.canvas.id="q5Canvas"+Q5._instanceCount;$.canvas.classList.add("q5Canvas")}else $.noCanvas()}let c=$.canvas;$.width=200;$.height=200;$._pixelDensity=1;$.displayDensity=()=>window.devicePixelRatio||1;if(c){c.width=200;c.height=200;if($._scope!="image"){c.renderer=$._renderer;c[$._renderer]=true;$._pixelDensity=Math.ceil($.displayDensity())}}$._adjustDisplay=()=>{if(c.style){c.style.width=c.w+"px";c.style.height=c.h+"px"}};$.createCanvas=function(w,h,options){if(typeof w=="object"){options=w;w=null}options??=arguments[3];let opt=Object.assign({},Q5.canvasOptions);if(typeof options=="object")Object.assign(opt,options);if($._scope!="image"){if($._scope=="graphics")$._pixelDensity=this._pixelDensity;else if(window.IntersectionObserver){let wasObserved=false;new IntersectionObserver((e=>{c.visible=e[0].isIntersecting;if(!wasObserved){$._wasLooping=$._loop;wasObserved=true}if(c.visible){if($._wasLooping&&!$._loop)$.loop()}else{$._wasLooping=$._loop;$.noLoop()}})).observe(c)}}$._setCanvasSize(w,h);Object.assign(c,opt);let rend=$._createCanvas(c.w,c.h,opt);if($._hooks){for(let m of $._hooks.postCanvas)m()}if($._beginRender)$._beginRender();return rend};$.createGraphics=function(w,h,opt){let g=new Q5("graphics");opt??={};opt.alpha??=true;opt.colorSpace??=$.canvas.colorSpace;g.createCanvas.call($,w,h,opt);g.defaultWidth=w;g.defaultHeight=h;return g};async function saveFile(data,name,ext){name=name||"untitled";ext=ext||"png";if(ext=="jpg"||ext=="png"||ext=="webp"){if(data instanceof OffscreenCanvas){const blob=await data.convertToBlob({type:"image/"+ext});data=await new Promise((resolve=>{const reader=new FileReader;reader.onloadend=()=>resolve(reader.result);reader.readAsDataURL(blob)}))}else{data=data.toDataURL("image/"+ext)}}else{let type="text/plain";if(ext=="json"){if(typeof data!="string")data=JSON.stringify(data);type="text/json"}data=new Blob([data],{type:type});data=URL.createObjectURL(data)}let a=document.createElement("a");a.href=data;a.download=name+"."+ext;a.click();URL.revokeObjectURL(a.href)}$.save=(a,b,c)=>{if(!a||typeof a=="string"&&(!b||!c&&b.length<5)){c=b;b=a;a=$.canvas}if(c)return saveFile(a,b,c);if(b){b=b.split(".");saveFile(a,b[0],b.at(-1))}else saveFile(a)};$._setCanvasSize=(w,h)=>{if(!w)h??=window.innerHeight;else h??=w;w??=window.innerWidth;$.defaultWidth=c.w=w=Math.ceil(w);$.defaultHeight=c.h=h=Math.ceil(h);c.hw=w/2;c.hh=h/2;c.width=Math.ceil(w*$._pixelDensity);c.height=Math.ceil(h*$._pixelDensity);if(!$._da){q.width=w;q.height=h}else $.flexibleCanvas($._dau);if($.displayMode&&!c.displayMode)$.displayMode();else $._adjustDisplay()};$._setImageSize=(w,h)=>{q.width=c.w=w;q.height=c.h=h;c.hw=w/2;c.hh=h/2;c.width=Math.ceil(w*$._pixelDensity);c.height=Math.ceil(h*$._pixelDensity)};$.defaultImageScale=scale=>{if(!scale)return $._defaultImageScale;return $._defaultImageScale=scale};$.defaultImageScale(.5);if($._scope=="image")return;if(c&&$._scope!="graphics"){c.parent=el=>{if(c.parentElement)c.parentElement.removeChild(c);if(typeof el=="string")el=document.getElementById(el);el.append(c);function parentResized(){if($.frameCount>1){$._didResize=true;$._adjustDisplay()}}if(typeof ResizeObserver=="function"){if($._ro)$._ro.disconnect();$._ro=new ResizeObserver(parentResized);$._ro.observe(el)}else if(!$.frameCount){window.addEventListener("resize",parentResized)}};function addCanvas(){let el=$._parent;el??=document.getElementsByTagName("main")[0];if(!el){el=document.createElement("main");document.body.append(el)}c.parent(el)}if(document.body)addCanvas();else document.addEventListener("DOMContentLoaded",addCanvas)}$.resizeCanvas=(w,h)=>{if(!$.ctx)return $.createCanvas(w,h);if(w==c.w&&h==c.h)return;$._resizeCanvas(w,h)};if(c&&!Q5._createServerCanvas){c.resize=$.resizeCanvas;c.save=$.saveCanvas=$.save}$.pixelDensity=v=>{if(!v||v==$._pixelDensity)return $._pixelDensity;$._pixelDensity=v;$._setCanvasSize(c.w,c.h);return v};$.flexibleCanvas=(unit=400)=>{if(unit){$._da=c.width/(unit*$._pixelDensity);q.width=$._dau=unit;q.height=c.h/c.w*unit}else $._da=0};$._styleNames=["_fill","_stroke","_strokeWeight","_doStroke","_doFill","_strokeSet","_fillSet","_shadow","_doShadow","_shadowOffsetX","_shadowOffsetY","_shadowBlur","_tint","_imageMode","_rectMode","_ellipseMode","_textSize","_textAlign","_textBaseline"];$._styles=[];$.pushStyles=()=>{let styles={};for(let s of $._styleNames)styles[s]=$[s];$._styles.push(styles)};$.popStyles=()=>{let styles=$._styles.pop();for(let s of $._styleNames)$[s]=styles[s]};if(window&&$._scope!="graphics"){window.addEventListener("resize",(()=>{$._didResize=true;q.windowWidth=window.innerWidth;q.windowHeight=window.innerHeight;q.deviceOrientation=window.screen?.orientation?.type}))}};Q5.CENTER="center";Q5.LEFT="left";Q5.RIGHT="right";Q5.TOP="top";Q5.BOTTOM="bottom";Q5.BASELINE="alphabetic";Q5.MIDDLE="middle";Q5.NORMAL="normal";Q5.ITALIC="italic";Q5.BOLD="bold";Q5.BOLDITALIC="italic bold";Q5.ROUND="round";Q5.SQUARE="butt";Q5.PROJECT="square";Q5.MITER="miter";Q5.BEVEL="bevel";Q5.CHORD_OPEN=0;Q5.PIE_OPEN=1;Q5.PIE=2;Q5.CHORD=3;Q5.RADIUS="radius";Q5.CORNER="corner";Q5.CORNERS="corners";Q5.OPEN=0;Q5.CLOSE=1;Q5.LANDSCAPE="landscape";Q5.PORTRAIT="portrait";Q5.BLEND="source-over";Q5.REMOVE="destination-out";Q5.ADD="lighter";Q5.DARKEST="darken";Q5.LIGHTEST="lighten";Q5.DIFFERENCE="difference";Q5.SUBTRACT="subtract";Q5.EXCLUSION="exclusion";Q5.MULTIPLY="multiply";Q5.SCREEN="screen";Q5.REPLACE="copy";Q5.OVERLAY="overlay";Q5.HARD_LIGHT="hard-light";Q5.SOFT_LIGHT="soft-light";Q5.DODGE="color-dodge";Q5.BURN="color-burn";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.SEPIA=9;Q5.BRIGHTNESS=10;Q5.SATURATION=11;Q5.CONTRAST=12;Q5.HUE_ROTATE=13;Q5.P2D="2d";Q5.WEBGL="webgl";Q5.canvasOptions={alpha:false,colorSpace:"display-p3"};if(!window.matchMedia||!matchMedia("(dynamic-range: high) and (color-gamut: p3)").matches){Q5.canvasOptions.colorSpace="srgb"}else Q5.supportsHDR=true;Q5.renderers.c2d={};Q5.renderers.c2d.canvas=($,q)=>{let c=$.canvas;if($.colorMode){$.colorMode(Q5.canvasOptions.colorSpace!="srgb"?"rgb":"srgb",255)}$._createCanvas=function(w,h,options){if(!c){console.error("q5 canvas could not be created. skia-canvas and jsdom packages not found.");return}q.ctx=q.drawingContext=c.getContext("2d",options);if($._scope!="image"){$.ctx.fillStyle=$._fill="white";$.ctx.strokeStyle=$._stroke="black";$.ctx.lineCap="round";$.ctx.lineJoin="miter";$.ctx.textAlign="left";$._strokeWeight=1}$.ctx.scale($._pixelDensity,$._pixelDensity);$.ctx.save();return c};$.clear=()=>{$.ctx.save();$.ctx.resetTransform();$.ctx.clearRect(0,0,$.canvas.width,$.canvas.height);$.ctx.restore()};if($._scope=="image")return;$._resizeCanvas=(w,h)=>{let t={};for(let prop in $.ctx){if(typeof $.ctx[prop]!="function")t[prop]=$.ctx[prop]}delete t.canvas;let o;if($.frameCount>1){o=new $._OffscreenCanvas(c.width,c.height);o.w=c.w;o.h=c.h;let oCtx=o.getContext("2d");oCtx.drawImage(c,0,0)}$._setCanvasSize(w,h);for(let prop in t)$.ctx[prop]=t[prop];$.scale($._pixelDensity);if(o)$.ctx.drawImage(o,0,0,o.w,o.h)};$.fill=function(c){$._doFill=$._fillSet=true;if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}if(c.a<=0)return $._doFill=false}$.ctx.fillStyle=$._fill=c.toString()};$.stroke=function(c){$._doStroke=$._strokeSet=true;if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}if(c.a<=0)return $._doStroke=false}$.ctx.strokeStyle=$._stroke=c.toString()};$.strokeWeight=n=>{if(!n)$._doStroke=false;if($._da)n*=$._da;$.ctx.lineWidth=$._strokeWeight=n||1e-4};$.noFill=()=>$._doFill=false;$.noStroke=()=>$._doStroke=false;$.opacity=a=>$.ctx.globalAlpha=a;$._doShadow=false;$._shadowOffsetX=$._shadowOffsetY=$._shadowBlur=10;$.shadow=function(c){if(Q5.Color){if(!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}}$.ctx.shadowColor=$._shadow=c.toString();$._doShadow=true;$.ctx.shadowOffsetX||=$._shadowOffsetX;$.ctx.shadowOffsetY||=$._shadowOffsetY;$.ctx.shadowBlur||=$._shadowBlur};$.shadowBox=(offsetX,offsetY,blur)=>{$.ctx.shadowOffsetX=$._shadowOffsetX=offsetX;$.ctx.shadowOffsetY=$._shadowOffsetY=offsetY||offsetX;$.ctx.shadowBlur=$._shadowBlur=blur||0};$.noShadow=()=>{$._doShadow=false;$.ctx.shadowOffsetX=$.ctx.shadowOffsetY=$.ctx.shadowBlur=0};$.translate=(x,y)=>{if($._da){x*=$._da;y*=$._da}$.ctx.translate(x,y)};$.rotate=r=>{if($._angleMode)r=$.radians(r);$.ctx.rotate(r)};$.scale=(x,y)=>{if(x.x){y=x.y;x=x.x}y??=x;$.ctx.scale(x,y)};$.applyMatrix=(a,b,c,d,e,f)=>$.ctx.transform(a,b,c,d,e,f);$.shearX=ang=>$.ctx.transform(1,0,$.tan(ang),1,0,0);$.shearY=ang=>$.ctx.transform(1,$.tan(ang),0,1,0,0);$.resetMatrix=()=>{if($.ctx){$.ctx.resetTransform();$.scale($._pixelDensity);if($._webgpuFallback)$.translate($.canvas.hw,$.canvas.hh)}};$.pushMatrix=()=>$.ctx.save();$.popMatrix=()=>$.ctx.restore();let _popStyles=$.popStyles;$.popStyles=()=>{_popStyles();$.ctx.fillStyle=$._fill;$.ctx.strokeStyle=$._stroke;$.ctx.lineWidth=$._strokeWeight;$.ctx.shadowColor=$._shadow;$.ctx.shadowOffsetX=$._doShadow?$._shadowOffsetX:0;$.ctx.shadowOffsetY=$._doShadow?$._shadowOffsetY:0;$.ctx.shadowBlur=$._doShadow?$._shadowBlur:0};$.push=()=>{$.ctx.save();$.pushStyles()};$.pop=()=>{$.ctx.restore();_popStyles()};$.createCapture=x=>{var vid=document.createElement("video");vid.playsinline="playsinline";vid.autoplay="autoplay";navigator.mediaDevices.getUserMedia(x).then((stream=>{vid.srcObject=stream}));vid.style.position="absolute";vid.style.opacity=1e-5;vid.style.zIndex=-1e3;document.body.append(vid);return vid}};Q5.renderers.c2d.drawing=$=>{$._doStroke=true;$._doFill=true;$._strokeSet=false;$._fillSet=false;$._ellipseMode=Q5.CENTER;$._rectMode=Q5.CORNER;$._curveDetail=20;$._curveAlpha=0;let firstVertex=true;let curveBuff=[];function ink(){if($._doFill)$.ctx.fill();if($._doStroke)$.ctx.stroke()}$.blendMode=x=>$.ctx.globalCompositeOperation=x;$.strokeCap=x=>$.ctx.lineCap=x;$.strokeJoin=x=>$.ctx.lineJoin=x;$.ellipseMode=x=>$._ellipseMode=x;$.rectMode=x=>$._rectMode=x;$.curveDetail=x=>$._curveDetail=x;$.curveAlpha=x=>$._curveAlpha=x;$.curveTightness=x=>$._curveAlpha=x;$.background=function(c){$.ctx.save();$.ctx.resetTransform();$.ctx.globalAlpha=1;if(c.canvas)$.image(c,0,0,$.canvas.width,$.canvas.height);else{if(Q5.Color&&!c._q5Color){if(typeof c!="string")c=$.color(...arguments);else if($._namedColors[c])c=$.color(...$._namedColors[c])}$.ctx.fillStyle=c.toString();$.ctx.fillRect(0,0,$.canvas.width,$.canvas.height)}$.ctx.restore()};$.line=(x0,y0,x1,y1)=>{if($._doStroke){$._da&&(x0*=$._da,y0*=$._da,x1*=$._da,y1*=$._da);$.ctx.beginPath();$.ctx.moveTo(x0,y0);$.ctx.lineTo(x1,y1);$.ctx.stroke()}};const TAU=Math.PI*2;function arc(x,y,w,h,lo,hi,mode){if($._angleMode){lo=$.radians(lo);hi=$.radians(hi)}lo%=TAU;hi%=TAU;if(lo<0)lo+=TAU;if(hi<0)hi+=TAU;if(lo>hi)hi+=TAU;if(lo==hi)return $.ellipse(x,y,w,h);w/=2;h/=2;if(!$._doFill&&mode==$.PIE_OPEN)mode=$.CHORD_OPEN;$.ctx.beginPath();$.ctx.ellipse(x,y,w,h,0,lo,hi);if(mode==$.PIE||mode==$.PIE_OPEN)$.ctx.lineTo(x,y);if($._doFill)$.ctx.fill();if($._doStroke){if(mode==$.PIE||mode==$.CHORD)$.ctx.closePath();if(mode!=$.PIE_OPEN)return $.ctx.stroke();$.ctx.beginPath();$.ctx.ellipse(x,y,w,h,0,lo,hi);$.ctx.stroke()}}$.arc=(x,y,w,h,start,stop,mode)=>{if(start==stop)return $.ellipse(x,y,w,h);if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}mode??=$.PIE_OPEN;if($._ellipseMode==$.CENTER){arc(x,y,w,h,start,stop,mode)}else if($._ellipseMode==$.RADIUS){arc(x,y,w*2,h*2,start,stop,mode)}else if($._ellipseMode==$.CORNER){arc(x+w/2,y+h/2,w,h,start,stop,mode)}else if($._ellipseMode==$.CORNERS){arc((x+w)/2,(y+h)/2,w-x,h-y,start,stop,mode)}};function ellipse(x,y,w,h){$.ctx.beginPath();$.ctx.ellipse(x,y,w/2,h/2,0,0,TAU);ink()}$.ellipse=(x,y,w,h)=>{h??=w;if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}if($._ellipseMode==$.CENTER){ellipse(x,y,w,h)}else if($._ellipseMode==$.RADIUS){ellipse(x,y,w*2,h*2)}else if($._ellipseMode==$.CORNER){ellipse(x+w/2,y+h/2,w,h)}else if($._ellipseMode==$.CORNERS){ellipse((x+w)/2,(y+h)/2,w-x,h-y)}};$.circle=(x,y,d)=>{if($._ellipseMode==$.CENTER){if($._da){x*=$._da;y*=$._da;d*=$._da}$.ctx.beginPath();$.ctx.arc(x,y,d/2,0,TAU);ink()}else $.ellipse(x,y,d,d)};$.point=(x,y)=>{if($._doStroke){if(x.x){y=x.y;x=x.x}if($._da){x*=$._da;y*=$._da}$.ctx.beginPath();$.ctx.moveTo(x,y);$.ctx.lineTo(x,y);$.ctx.stroke()}};function rect(x,y,w,h){if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da}$.ctx.beginPath();$.ctx.rect(x,y,w,h);ink()}function roundedRect(x,y,w,h,tl,tr,br,bl){if(tl===undefined){return rect(x,y,w,h)}if(tr===undefined){return roundedRect(x,y,w,h,tl,tl,tl,tl)}if($._da){x*=$._da;y*=$._da;w*=$._da;h*=$._da;tl*=$._da;tr*=$._da;bl*=$._da;br*=$._da}$.ctx.roundRect(x,y,w,h,[tl,tr,br,bl]);ink()}$.rect=(x,y,w,h=w,tl,tr,br,bl)=>{if($._rectMode==$.CENTER){roundedRect(x-w/2,y-h/2,w,h,tl,tr,br,bl)}else if($._rectMode==$.RADIUS){roundedRect(x-w,y-h,w*2,h*2,tl,tr,br,bl)}else if($._rectMode==$.CORNER){roundedRect(x,y,w,h,tl,tr,br,bl)}else if($._rectMode==$.CORNERS){roundedRect(x,y,w-x,h-y,tl,tr,br,bl)}};$.square=(x,y,s,tl,tr,br,bl)=>$.rect(x,y,s,s,tl,tr,br,bl);$.beginShape=()=>{curveBuff=[];$.ctx.beginPath();firstVertex=true};$.beginContour=()=>{$.ctx.closePath();curveBuff=[];firstVertex=true};$.endContour=()=>{curveBuff=[];firstVertex=true};$.vertex=(x,y)=>{if($._da){x*=$._da;y*=$._da}curveBuff=[];if(firstVertex){$.ctx.moveTo(x,y)}else{$.ctx.lineTo(x,y)}firstVertex=false};$.bezierVertex=(cp1x,cp1y,cp2x,cp2y,x,y)=>{if($._da){cp1x*=$._da;cp1y*=$._da;cp2x*=$._da;cp2y*=$._da;x*=$._da;y*=$._da}curveBuff=[];$.ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)};$.quadraticVertex=(cp1x,cp1y,x,y)=>{if($._da){cp1x*=$._da;cp1y*=$._da;x*=$._da;y*=$._da}curveBuff=[];$.ctx.quadraticCurveTo(cp1x,cp1y,x,y)};$.bezier=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.bezierVertex(x2,y2,x3,y3,x4,y4);$.endShape()};$.triangle=(x1,y1,x2,y2,x3,y3)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.endShape($.CLOSE)};$.quad=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.vertex(x4,y4);$.endShape($.CLOSE)};$.endShape=close=>{curveBuff=[];if(close)$.ctx.closePath();ink()};$.curveVertex=(x,y)=>{if($._da){x*=$._da;y*=$._da}curveBuff.push([x,y]);if(curveBuff.length<4)return;let p0=curveBuff.at(-4),p1=curveBuff.at(-3),p2=curveBuff.at(-2),p3=curveBuff.at(-1);let cp1x=p1[0]+(p2[0]-p0[0])/6,cp1y=p1[1]+(p2[1]-p0[1])/6,cp2x=p2[0]-(p3[0]-p1[0])/6,cp2y=p2[1]-(p3[1]-p1[1])/6;if(firstVertex){$.ctx.moveTo(p1[0],p1[1]);firstVertex=false}$.ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,p2[0],p2[1])};$.curve=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.curveVertex(x1,y1);$.curveVertex(x2,y2);$.curveVertex(x3,y3);$.curveVertex(x4,y4);$.endShape()};$.curvePoint=(a,b,c,d,t)=>{const t3=t*t*t,t2=t*t,f1=-.5*t3+t2-.5*t,f2=1.5*t3-2.5*t2+1,f3=-1.5*t3+2*t2+.5*t,f4=.5*t3-.5*t2;return a*f1+b*f2+c*f3+d*f4};$.bezierPoint=(a,b,c,d,t)=>{const adjustedT=1-t;return Math.pow(adjustedT,3)*a+3*Math.pow(adjustedT,2)*t*b+3*adjustedT*Math.pow(t,2)*c+Math.pow(t,3)*d};$.curveTangent=(a,b,c,d,t)=>{const t2=t*t,f1=-3*t2/2+2*t-.5,f2=9*t2/2-5*t,f3=-9*t2/2+4*t+.5,f4=3*t2/2-t;return a*f1+b*f2+c*f3+d*f4};$.bezierTangent=(a,b,c,d,t)=>{const adjustedT=1-t;return 3*d*Math.pow(t,2)-3*c*Math.pow(t,2)+6*c*adjustedT*t-6*b*adjustedT*t+3*b*Math.pow(adjustedT,2)-3*a*Math.pow(adjustedT,2)};$.erase=function(fillAlpha=255,strokeAlpha=255){$.ctx.save();$.ctx.globalCompositeOperation="destination-out";$.ctx.fillStyle=`rgba(0, 0, 0, ${fillAlpha/255})`;$.ctx.strokeStyle=`rgba(0, 0, 0, ${strokeAlpha/255})`};$.noErase=function(){$.ctx.globalCompositeOperation="source-over";$.ctx.restore()};$.inFill=(x,y)=>{const pd=$._pixelDensity;return $.ctx.isPointInPath(x*pd,y*pd)};$.inStroke=(x,y)=>{const pd=$._pixelDensity;return $.ctx.isPointInStroke(x*pd,y*pd)}};Q5.renderers.c2d.image=($,q)=>{class Q5Image{constructor(w,h,opt){let $=this;$._scope="image";$.canvas=$.ctx=$.drawingContext=null;$.pixels=[];Q5.modules.canvas($,$);let r=Q5.renderers.c2d;for(let m of["canvas","image","soft_filters"]){if(r[m])r[m]($,$)}$._pixelDensity=opt.pixelDensity||1;$.createCanvas(w,h,opt);delete $.createCanvas;$._loop=false}get w(){return this.width}get h(){return this.height}}Q5.Image??=Q5Image;$._tint=null;let imgData=null;$.createImage=(w,h,opt)=>{opt??={};opt.alpha??=true;opt.colorSpace??=$.canvas.colorSpace||Q5.canvasOptions.colorSpace;let img=new Q5.Image(w,h,opt);img.defaultWidth=w*$._defaultImageScale;img.defaultHeight=h*$._defaultImageScale;return img};$.loadImage=function(url,cb,opt){if(url.canvas)return url;if(url.slice(-3).toLowerCase()=="gif"){throw new Error(`q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84`)}q._preloadCount++;let last=[...arguments].at(-1);if(typeof last=="object"){opt=last;cb=null}else opt=null;let g=$.createImage(1,1,opt);let pd=g._pixelDensity=opt?.pixelDensity||1;let img=new window.Image;img.crossOrigin="Anonymous";g._loader=new Promise(((resolve,reject)=>{img.onload=()=>{img._pixelDensity=pd;g.defaultWidth=img.width*$._defaultImageScale;g.defaultHeight=img.height*$._defaultImageScale;g.naturalWidth=img.naturalWidth||img.width;g.naturalHeight=img.naturalHeight||img.height;g._setImageSize(Math.ceil(g.naturalWidth/pd),Math.ceil(g.naturalHeight/pd));g.ctx.drawImage(img,0,0);q._preloadCount--;if(cb)cb(g);delete g._loader;resolve(g)};img.onerror=e=>{q._preloadCount--;reject(e)}}));img.src=url;if($._disablePreload)return g._loader;return g};$.imageMode=mode=>$._imageMode=mode;$.image=(img,dx,dy,dw,dh,sx=0,sy=0,sw,sh)=>{if(!img)return;let drawable=img.canvas||img;dw??=img.defaultWidth||drawable.width||img.videoWidth;dh??=img.defaultHeight||drawable.height||img.videoHeight;if($._imageMode=="center"){dx-=dw*.5;dy-=dh*.5}if($._da){dx*=$._da;dy*=$._da;dw*=$._da;dh*=$._da;sx*=$._da;sy*=$._da;sw*=$._da;sh*=$._da}let pd=img._pixelDensity||1;if(!sw){sw=drawable.width||drawable.videoWidth}else sw*=pd;if(!sh){sh=drawable.height||drawable.videoHeight}else sh*=pd;if($._tint){if(img._retint||img._tint!=$._tint){img._tintImg??=$.createImage(img.w,img.h,{pixelDensity:pd});if(img._tintImg.width!=img.width||img._tintImg.height!=img.height){img._tintImg.resize(img.w,img.h)}let tnt=img._tintImg.ctx;tnt.globalCompositeOperation="copy";tnt.fillStyle=$._tint;tnt.fillRect(0,0,img.width,img.height);if(img.canvas.alpha){tnt.globalCompositeOperation="destination-in";tnt.drawImage(drawable,0,0,img.width,img.height)}tnt.globalCompositeOperation="multiply";tnt.drawImage(drawable,0,0,img.width,img.height);img._tint=$._tint;img._retint=false}drawable=img._tintImg.canvas}$.ctx.drawImage(drawable,sx*pd,sy*pd,sw,sh,dx,dy,dw,dh)};$.filter=(type,value)=>{$.ctx.save();let f="";if($.ctx.filter){if(typeof type=="string"){f=type}else if(type==Q5.GRAY){f=`saturate(0%)`}else if(type==Q5.INVERT){f=`invert(100%)`}else if(type==Q5.BLUR){let r=Math.ceil(value*$._pixelDensity)||1;f=`blur(${r}px)`}else if(type==Q5.THRESHOLD){value??=.5;let b=Math.floor(.5/Math.max(value,1e-5)*100);f=`saturate(0%) brightness(${b}%) contrast(1000000%)`}else if(type==Q5.SEPIA){f=`sepia(${value??1})`}else if(type==Q5.BRIGHTNESS){f=`brightness(${value??1})`}else if(type==Q5.SATURATION){f=`saturate(${value??1})`}else if(type==Q5.CONTRAST){f=`contrast(${value??1})`}else if(type==Q5.HUE_ROTATE){let unit=$._angleMode==0?"rad":"deg";f=`hue-rotate(${value}${unit})`}if(f){$.ctx.filter=f;if($.ctx.filter=="none"){throw new Error(`Invalid filter format: ${type}`)}}}if(!f)$._softFilter(type,value);$.ctx.globalCompositeOperation="source-over";$.ctx.drawImage($.canvas,0,0,$.canvas.w,$.canvas.h);$.ctx.restore();$._retint=true};if($._scope=="image"){$.resize=(w,h)=>{let c=$.canvas;let o=new $._OffscreenCanvas(c.width,c.height);let tmpCtx=o.getContext("2d",{colorSpace:c.colorSpace});tmpCtx.drawImage(c,0,0);$._setImageSize(w,h);$.defaultWidth=c.width*$._defaultImageScale;$.defaultHeight=c.height*$._defaultImageScale;$.ctx.clearRect(0,0,c.width,c.height);$.ctx.drawImage(o,0,0,c.width,c.height);$._retint=true}}$._getImageData=(x,y,w,h)=>$.ctx.getImageData(x,y,w,h,{colorSpace:$.canvas.colorSpace});$.trim=()=>{let pd=$._pixelDensity||1;let w=$.canvas.width;let h=$.canvas.height;let data=$._getImageData(0,0,w,h).data;let left=w,right=0,top=h,bottom=0;let i=3;for(let y=0;y<h;y++){for(let x=0;x<w;x++){if(data[i]!==0){if(x<left)left=x;if(x>right)right=x;if(y<top)top=y;if(y>bottom)bottom=y}i+=4}}top=Math.floor(top/pd);bottom=Math.floor(bottom/pd);left=Math.floor(left/pd);right=Math.floor(right/pd);return $.get(left,top,right-left+1,bottom-top+1)};$.mask=img=>{$.ctx.save();$.ctx.resetTransform();let old=$.ctx.globalCompositeOperation;$.ctx.globalCompositeOperation="destination-in";$.ctx.drawImage(img.canvas,0,0);$.ctx.globalCompositeOperation=old;$.ctx.restore();$._retint=true};$.inset=(x,y,w,h,dx,dy,dw,dh)=>{let pd=$._pixelDensity||1;$.ctx.drawImage($.canvas,x*pd,y*pd,w*pd,h*pd,dx,dy,dw,dh);$._retint=true};$.copy=()=>$.get();$.get=(x,y,w,h)=>{let pd=$._pixelDensity||1;if(x!==undefined&&w===undefined){let c=$._getImageData(x*pd,y*pd,1,1).data;return[c[0],c[1],c[2],c[3]/255]}x=Math.floor(x||0)*pd;y=Math.floor(y||0)*pd;let _w=w=w||$.width;let _h=h=h||$.height;w*=pd;h*=pd;let img=$.createImage(w,h);img.ctx.drawImage($.canvas,x,y,w,h,0,0,w,h);img._pixelDensity=pd;img.width=_w;img.height=_h;return img};$.set=(x,y,c)=>{x=Math.floor(x);y=Math.floor(y);$._retint=true;if(c.canvas){let old=$._tint;$._tint=null;$.image(c,x,y);$._tint=old;return}if(!$.pixels.length)$.loadPixels();let mod=$._pixelDensity||1;for(let i=0;i<mod;i++){for(let j=0;j<mod;j++){let idx=4*((y*mod+i)*$.canvas.width+x*mod+j);$.pixels[idx]=c.r;$.pixels[idx+1]=c.g;$.pixels[idx+2]=c.b;$.pixels[idx+3]=c.a}}};$.loadPixels=()=>{imgData=$._getImageData(0,0,$.canvas.width,$.canvas.height);q.pixels=imgData.data};$.updatePixels=()=>{if(imgData!=null){$.ctx.putImageData(imgData,0,0);$._retint=true}};$.smooth=()=>$.ctx.imageSmoothingEnabled=true;$.noSmooth=()=>$.ctx.imageSmoothingEnabled=false;if($._scope=="image")return;$.tint=function(c){$._tint=(c._q5Color?c:$.color(...arguments)).toString()};$.noTint=()=>$._tint=null};Q5.renderers.c2d.soft_filters=$=>{let u=null;function ensureBuf(){let l=$.canvas.width*$.canvas.height*4;if(!u||u.length!=l)u=new Uint8ClampedArray(l)}function initSoftFilters(){$._filters=[];$._filters[Q5.THRESHOLD]=(d,thresh)=>{if(thresh===undefined)thresh=127.5;else thresh*=255;for(let i=0;i<d.length;i+=4){const gray=.2126*d[i]+.7152*d[i+1]+.0722*d[i+2];d[i]=d[i+1]=d[i+2]=gray>=thresh?255:0}};$._filters[Q5.GRAY]=d=>{for(let i=0;i<d.length;i+=4){const gray=.2126*d[i]+.7152*d[i+1]+.0722*d[i+2];d[i]=d[i+1]=d[i+2]=gray}};$._filters[Q5.OPAQUE]=d=>{for(let i=0;i<d.length;i+=4){d[i+3]=255}};$._filters[Q5.INVERT]=d=>{for(let i=0;i<d.length;i+=4){d[i]=255-d[i];d[i+1]=255-d[i+1];d[i+2]=255-d[i+2]}};$._filters[Q5.POSTERIZE]=(d,lvl=4)=>{let lvl1=lvl-1;for(let i=0;i<d.length;i+=4){d[i]=(d[i]*lvl>>8)*255/lvl1;d[i+1]=(d[i+1]*lvl>>8)*255/lvl1;d[i+2]=(d[i+2]*lvl>>8)*255/lvl1}};$._filters[Q5.DILATE]=(d,func)=>{func??=Math.max;ensureBuf();u.set(d);let[w,h]=[$.canvas.width,$.canvas.height];for(let i=0;i<h;i++){for(let j=0;j<w;j++){let l=4*Math.max(j-1,0);let r=4*Math.min(j+1,w-1);let t=4*Math.max(i-1,0)*w;let b=4*Math.min(i+1,h-1)*w;let oi=4*i*w;let oj=4*j;for(let k=0;k<4;k++){let kt=k+t;let kb=k+b;let ko=k+oi;d[oi+oj+k]=func(u[kt+oj],u[ko+l],u[ko+oj],u[ko+r],u[kb+oj])}}}};$._filters[Q5.ERODE]=d=>{$._filters[Q5.DILATE](d,Math.min)};$._filters[Q5.BLUR]=(d,r)=>{r=r||1;r=Math.floor(r*$._pixelDensity);ensureBuf();u.set(d);let ksize=r*2+1;function gauss(ksize){let im=new Float32Array(ksize);let sigma=.3*r+.8;let ss2=sigma*sigma*2;for(let i=0;i<ksize;i++){let x=i-ksize/2;let z=Math.exp(-(x*x)/ss2)/(2.5066282746*sigma);im[i]=z}return im}let kern=gauss(ksize);let[w,h]=[$.canvas.width,$.canvas.height];for(let i=0;i<h;i++){for(let j=0;j<w;j++){let s0=0,s1=0,s2=0,s3=0;for(let k=0;k<ksize;k++){let jk=Math.min(Math.max(j-r+k,0),w-1);let idx=4*(i*w+jk);s0+=u[idx]*kern[k];s1+=u[idx+1]*kern[k];s2+=u[idx+2]*kern[k];s3+=u[idx+3]*kern[k]}let idx=4*(i*w+j);d[idx]=s0;d[idx+1]=s1;d[idx+2]=s2;d[idx+3]=s3}}u.set(d);for(let i=0;i<h;i++){for(let j=0;j<w;j++){let s0=0,s1=0,s2=0,s3=0;for(let k=0;k<ksize;k++){let ik=Math.min(Math.max(i-r+k,0),h-1);let idx=4*(ik*w+j);s0+=u[idx]*kern[k];s1+=u[idx+1]*kern[k];s2+=u[idx+2]*kern[k];s3+=u[idx+3]*kern[k]}let idx=4*(i*w+j);d[idx]=s0;d[idx+1]=s1;d[idx+2]=s2;d[idx+3]=s3}}}}$._softFilter=(typ,x)=>{if(!$._filters)initSoftFilters();let imgData=$._getImageData(0,0,$.canvas.width,$.canvas.height);$._filters[typ](imgData.data,x);$.ctx.putImageData(imgData,0,0)}};Q5.renderers.c2d.text=($,q)=>{$._textAlign="left";$._textBaseline="alphabetic";$._textSize=12;let font="sans-serif",leadingSet=false,leading=15,leadDiff=3,emphasis="normal",fontMod=false,styleHash=0,styleHashes=[],useCache=false,genTextImage=false,cacheSize=0,cacheMax=12e3;let cache=$._textCache={};$.loadFont=(url,cb)=>{q._preloadCount++;let name=url.split("/").pop().split(".")[0].replace(" ","");let f=new FontFace(name,`url(${url})`);document.fonts.add(f);f._loader=(async()=>{let err;try{await f.load()}catch(e){err=e}q._preloadCount--;delete f._loader;if(err)throw err;if(cb)cb(f);return f})();$.textFont(name);if($._disablePreload)return f._loader;return f};$.textFont=x=>{if(typeof x!="string")x=x.family;if(!x||x==font)return font;font=x;fontMod=true;styleHash=-1};$.textSize=x=>{if(x==undefined||x==$._textSize)return $._textSize;if($._da)x*=$._da;$._textSize=x;fontMod=true;styleHash=-1;if(!leadingSet){leading=x*1.25;leadDiff=leading-x}};$.textStyle=x=>{if(!x||x==emphasis)return emphasis;emphasis=x;fontMod=true;styleHash=-1};$.textLeading=x=>{if(x==undefined)return leading||$._textSize*1.25;leadingSet=true;if(x==leading)return leading;if($._da)x*=$._da;leading=x;leadDiff=x-$._textSize;styleHash=-1};$.textAlign=(horiz,vert)=>{$.ctx.textAlign=$._textAlign=horiz;if(vert){$.ctx.textBaseline=$._textBaseline=vert==$.CENTER?"middle":vert}};const updateFont=()=>{$.ctx.font=`${emphasis} ${$._textSize}px ${font}`;fontMod=false};$.textWidth=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).width};$.textAscent=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).actualBoundingBoxAscent};$.textDescent=str=>{if(fontMod)updateFont();return $.ctx.measureText(str).actualBoundingBoxDescent};$.textFill=$.fill;$.textStroke=$.stroke;let updateStyleHash=()=>{let styleString=font+$._textSize+emphasis+leading;let hash=5381;for(let i=0;i<styleString.length;i++){hash=hash*33^styleString.charCodeAt(i)}styleHash=hash>>>0};$.textCache=(enable,maxSize)=>{if(maxSize)cacheMax=maxSize;if(enable!==undefined)useCache=enable;return useCache};$.createTextImage=(str,w,h)=>{genTextImage=true;img=$.text(str,0,0,w,h);genTextImage=false;return img};let lines=[];$.text=(str,x,y,w,h)=>{if(str===undefined||!$._doFill&&!$._doStroke)return;str=str.toString();if($._da){x*=$._da;y*=$._da}let ctx=$.ctx;let img,tX,tY;if(fontMod){ctx.font=`${emphasis} ${$._textSize}px ${font}`;fontMod=false}if(useCache||genTextImage){if(styleHash==-1)updateStyleHash();img=cache[str];if(img)img=img[styleHash];if(img){if(img._fill==$._fill&&img._stroke==$._stroke&&img._strokeWeight==$._strokeWeight){if(genTextImage)return img;return $.textImage(img,x,y)}else img.clear()}}if(str.indexOf("\n")==-1)lines[0]=str;else lines=str.split("\n");if(str.length>w){let wrapped=[];for(let line of lines){let i=0;while(i<line.length){let max=i+w;if(max>=line.length){wrapped.push(line.slice(i));break}let end=line.lastIndexOf(" ",max);if(end===-1||end<i)end=max;wrapped.push(line.slice(i,end));i=end+1}}lines=wrapped}if(!useCache&&!genTextImage){tX=x;tY=y}else{tX=0;tY=leading*lines.length;if(!img){let measure=ctx.measureText(" ");let ascent=measure.fontBoundingBoxAscent;let descent=measure.fontBoundingBoxDescent;img=$.createImage.call($,Math.ceil(ctx.measureText(str).width),Math.ceil(tY+descent),{pixelDensity:$._pixelDensity});img._ascent=ascent;img._descent=descent;img._top=descent+leadDiff;img._middle=img._top+ascent*.5;img._bottom=img._top+ascent;img._leading=leading}img._fill=$._fill;img._stroke=$._stroke;img._strokeWeight=$._strokeWeight;img.modified=true;ctx=img.ctx;ctx.font=$.ctx.font;ctx.fillStyle=$._fill;ctx.strokeStyle=$._stroke;ctx.lineWidth=$.ctx.lineWidth}let ogFill;if(!$._fillSet){ogFill=ctx.fillStyle;ctx.fillStyle="black"}let lineAmount=0;for(let line of lines){if($._doStroke&&$._strokeSet)ctx.strokeText(line,tX,tY);if($._doFill)ctx.fillText(line,tX,tY);tY+=leading;lineAmount++;if(lineAmount>=h)break}lines=[];if(!$._fillSet)ctx.fillStyle=ogFill;if(useCache||genTextImage){styleHashes.push(styleHash);(cache[str]??={})[styleHash]=img;cacheSize++;if(cacheSize>cacheMax){let half=Math.ceil(cacheSize/2);let hashes=styleHashes.splice(0,half);for(let s in cache){s=cache[s];for(let h of hashes)delete s[h]}cacheSize-=half}if(genTextImage)return img;$.textImage(img,x,y)}};$.textImage=(img,x,y)=>{if(typeof img=="string")img=$.createTextImage(img);let og=$._imageMode;$._imageMode="corner";let ta=$._textAlign;if(ta=="center")x-=img.canvas.hw;else if(ta=="right")x-=img.width;let bl=$._textBaseline;if(bl=="alphabetic")y-=img._leading;else if(bl=="middle")y-=img._middle;else if(bl=="bottom")y-=img._bottom;else if(bl=="top")y-=img._top;$.image(img,x,y);$._imageMode=og};$.nf=(n,l,r)=>{let neg=n<0;n=Math.abs(n);let parts=n.toFixed(r).split(".");parts[0]=parts[0].padStart(l,"0");let s=parts.join(".");if(neg)s="-"+s;return s}};Q5.modules.ai=$=>{$.askAI=(question="")=>{Q5.disableFriendlyErrors=false;throw Error("Ask AI ✨ "+question)};$._askAI=async e=>{let askAI=e.message?.includes("Ask AI ✨");let stackLines=e.stack?.split("\n");if(!e.stack||stackLines.length<=1)return;let idx=1;let sep="(";if(navigator.userAgent.indexOf("Chrome")==-1){idx=0;sep="@"}while(stackLines[idx].indexOf("q5")>=0)idx++;let errFile=stackLines[idx].split(sep).at(-1);if(errFile.startsWith("blob:"))errFile=errFile.slice(5);let parts=errFile.split(":");let lineNum=parseInt(parts.at(-2));if(askAI)lineNum++;parts[parts.length-1]=parts.at(-1).split(")")[0];let fileUrl=parts.slice(0,-2).join(":");let fileBase=fileUrl.split("/").at(-1);try{let res=await(await fetch(fileUrl)).text();let lines=res.split("\n");let errLine=lines[lineNum-1].trim();let context="";let i=1;while(context.length<1600){if(lineNum-i>=0){context=lines[lineNum-i].trim()+"\n"+context}if(lineNum+i<lines.length){context+=lines[lineNum+i].trim()+"\n"}else break;i++}let question=askAI&&e.message.length>10?e.message.slice(10):"Whats+wrong+with+this+line%3F+short+answer";let url="https://chatgpt.com/?q=using+q5.js+not+p5.js+"+question+(askAI?"":"%0A%0A"+encodeURIComponent(e.name+": "+e.message))+"%0A%0ALine%3A+"+encodeURIComponent(errLine)+"%0A%0AExcerpt+for+context%3A%0A%0A"+encodeURIComponent(context);console.warn("Error in "+fileBase+" on line "+lineNum+":\n\n"+errLine);console.warn("Ask AI ✨ "+url);if(askAI)return window.open(url,"_blank")}catch(err){}}};Q5.modules.color=($,q)=>{$.RGB=$.RGBA=$._colorMode="rgb";$.SRGB="srgb";$.OKLCH="oklch";$.colorMode=(mode,format)=>{$._colorMode=mode;let srgb=$.canvas.colorSpace=="srgb"||mode=="srgb";format??=srgb?"integer":"float";$._colorFormat=format=="float"||format==1?1:255;if(mode=="oklch"){q.Color=Q5.ColorOKLCH}else{if($._colorFormat==255){q.Color=srgb?Q5.ColorRGBA_8:Q5.ColorRGBA_P3_8}else{q.Color=srgb?Q5.ColorRGBA:Q5.ColorRGBA_P3}$._colorMode="rgb"}};$._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]};$.color=(c0,c1,c2,c3)=>{let C=$.Color;if(c0._q5Color)return new C(...c0.levels);if(c1==undefined){if(typeof c0=="string"){if(c0[0]=="#"){if(c0.length<=5){if(c0.length>4)c3=parseInt(c0[4]+c0[4],16);c2=parseInt(c0[3]+c0[3],16);c1=parseInt(c0[2]+c0[2],16);c0=parseInt(c0[1]+c0[1],16)}else{if(c0.length>7)c3=parseInt(c0.slice(7,9),16);c2=parseInt(c0.slice(5,7),16);c1=parseInt(c0.slice(3,5),16);c0=parseInt(c0.slice(1,3),16)}}else if($._namedColors[c0]){[c0,c1,c2,c3]=$._namedColors[c0]}else{let c=new C(0,0,0);c._css=c0;c.toString=function(){return this._css};return c}if($._colorFormat==1){c0/=255;if(c1)c1/=255;if(c2)c2/=255;if(c3)c3/=255}}if(Array.isArray(c0))[c0,c1,c2,c3]=c0}if(c2==undefined){if($._colorMode==Q5.OKLCH)return new C(c0,0,0,c1);return new C(c0,c0,c0,c1)}return new C(c0,c1,c2,c3)};$.red=c=>c.r;$.green=c=>c.g;$.blue=c=>c.b;$.alpha=c=>c.a;$.lightness=c=>{if(c.l)return c.l;return(.2126*c.r+.7152*c.g+.0722*c.b)*100/255};$.hue=c=>{if(c.h)return c.h;let r=c.r;let g=c.g;let b=c.b;if($._colorFormat==255){r/=255;g/=255;b/=255}let max=Math.max(r,g,b);let min=Math.min(r,g,b);let h;if(max==min)h=0;else if(max==r)h=60*(g-b)/(max-min);else if(max==g)h=60*(b-r)/(max-min)+120;else h=60*(r-g)/(max-min)+240;if(h<0)h+=360;return h};$.lerpColor=(a,b,t)=>{t=Math.max(0,Math.min(1,t));if($._colorMode=="rgb"){return new $.Color($.lerp(a.r,b.r,t),$.lerp(a.g,b.g,t),$.lerp(a.b,b.b,t),$.lerp(a.a,b.a,t))}else{let deltaH=b.h-a.h;if(deltaH>180)deltaH-=360;if(deltaH<-180)deltaH+=360;let h=a.h+t*deltaH;if(h<0)h+=360;if(h>360)h-=360;return new $.Color($.lerp(a.l,b.l,t),$.lerp(a.c,b.c,t),h,$.lerp(a.a,b.a,t))}}};Q5.Color=class{constructor(){this._q5Color=true}};Q5.ColorOKLCH=class extends Q5.Color{constructor(l,c,h,a){super();this.l=l;this.c=c;this.h=h;this.a=a??1}get levels(){return[this.l,this.c,this.h,this.a]}equals(c){return c&&this.l==c.l&&this.c==c.c&&this.h==c.h&&this.a==c.a}isSameColor(c){return c&&this.l==c.l&&this.c==c.c&&this.h==c.h}toString(){return`oklch(${this.l} ${this.c} ${this.h} / ${this.a})`}get lightness(){return this.l}set lightness(v){this.l=v}get chroma(){return this.c}set chroma(v){this.c=v}get hue(){return this.h}set hue(v){this.h=v}get alpha(){return this.a}set alpha(v){this.a=v}};Q5.ColorRGBA=class extends Q5.Color{constructor(r,g,b,a){super();this.r=r;this.g=g;this.b=b;this.a=a??1}get levels(){return[this.r,this.g,this.b,this.a]}equals(c){return c&&this.r==c.r&&this.g==c.g&&this.b==c.b&&this.a==c.a}isSameColor(c){return c&&this.r==c.r&&this.g==c.g&&this.b==c.b}toString(){return`color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`}get red(){return this.r}set red(v){this.r=v}get green(){return this.g}set green(v){this.g=v}get blue(){return this.b}set blue(v){this.b=v}get alpha(){return this.a}set alpha(v){this.a=v}};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(r,g,b,a){super(r,g,b,a??255)}setRed(v){this.r=v}setGreen(v){this.g=v}setBlue(v){this.b=v}setAlpha(v){this.a=v}toString(){return`rgb(${this.r} ${this.g} ${this.b} / ${this.a/255})`}};Q5.ColorRGBA_P3_8=class extends Q5.ColorRGBA{constructor(r,g,b,a){super(r,g,b,a??255);this._edited=true}get r(){return this._r}set r(v){this._r=v;this._edited=true}get g(){return this._g}set g(v){this._g=v;this._edited=true}get b(){return this._b}set b(v){this._b=v;this._edited=true}get a(){return this._a}set a(v){this._a=v;this._edited=true}toString(){if(this._edited){let r=(this._r/255).toFixed(3);let g=(this._g/255).toFixed(3);let b=(this._b/255).toFixed(3);let a=(this._a/255).toFixed(3);this._css=`color(display-p3 ${r} ${g} ${b} / ${a})`;this._edited=false}return this._css}};Q5.modules.display=$=>{if(!$.canvas||$._scope=="graphics")return;let c=$.canvas;$.CENTERED="centered";$.FULLSCREEN="fullscreen";$.MAXED="maxed";$.PIXELATED="pixelated";if(Q5._instanceCount==0&&!Q5._server){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>`)}$._adjustDisplay=()=>{let s=c.style;let p=c.parentElement;if(!s||!p||!c.displayMode)return;if(c.renderQuality=="pixelated"){c.classList.add("q5-pixelated");$.pixelDensity(1);$.defaultImageScale(1);if($.noSmooth)$.noSmooth();if($.textFont)$.textFont("monospace")}if(c.displayMode=="default"||c.displayMode=="normal"){p.classList.remove("q5-centered","q5-maxed","q5-fullscreen");s.width=c.w*c.displayScale+"px";s.height=c.h*c.displayScale+"px"}else{p.classList.add("q5-"+c.displayMode);p=p.getBoundingClientRect();if(c.w/c.h>p.width/p.height){if(c.displayMode=="centered"){s.width=c.w*c.displayScale+"px";s.maxWidth="100%"}else s.width="100%";s.height="auto";s.maxHeight=""}else{s.width="auto";s.maxWidth="";if(c.displayMode=="centered"){s.height=c.h*c.displayScale+"px";s.maxHeight="100%"}else s.height="100%"}}};$.displayMode=(displayMode="normal",renderQuality="smooth",displayScale=1)=>{if(typeof displayScale=="string"){displayScale=parseFloat(displayScale.slice(1))}if(displayMode=="center")displayMode="centered";Object.assign(c,{displayMode:displayMode,renderQuality:renderQuality,displayScale:displayScale});if($.ctx)$.pushStyles();$._adjustDisplay();if($.ctx)$.popStyles()};$.fullscreen=v=>{if(v===undefined)return document.fullscreenElement;if(v)document.body.requestFullscreen();else document.body.exitFullscreen()}};Q5.modules.dom=$=>{$.elementMode=mode=>$._elementMode=mode;$.createElement=(tag,content)=>{let el=document.createElement(tag);if($._elementMode=="center"){el.style.transform="translate(-50%, -50%)"}if(content)el.innerHTML=content;Object.defineProperty(el,"x",{get:()=>el._x,set:v=>{let pos=el.style.position;if(!pos||pos=="relative"){el.style.position="absolute"}let x=$.canvas.offsetLeft+v;el.style.left=x+"px";el._x=x}});Object.defineProperty(el,"y",{get:()=>el._y,set:v=>{let pos=el.style.position;if(!pos||pos=="relative"){el.style.position="absolute"}let y=$.canvas.offsetTop+v;el.style.top=y+"px";el._y=y}});Object.defineProperty(el,"width",{get:()=>el.style.width,set:v=>el.style.width=v+"px"});Object.defineProperty(el,"height",{get:()=>el.style.height,set:v=>el.style.height=v+"px"});el.position=(x,y,scheme)=>{if(scheme)el.style.position=scheme;el.x=x;el.y=y;return el};Object.defineProperty(el,"size",{writable:true});el.size=(w,h)=>{el.width=w;el.height=h;return el};el.center=()=>{el.style.position="absolute";el.x=$.canvas.hw;el.y=$.canvas.hh;return el};el.show=()=>{el.style.display="";return el};el.hide=()=>{el.style.display="none";return el};el.parent=parent=>{parent.append(el);return el};$._elements.push(el);$.canvas.parentElement.append(el);return el};$.createEl=$.createElement;$.createA=(href,content,newTab)=>{let el=$.createEl("a",content);el.href=href;el.target=newTab?"_blank":"_self";return el};$.createButton=content=>$.createEl("button",content);$.createCheckbox=(label="",checked=false)=>{let el=$.createEl("input");el.type="checkbox";el.checked=checked;let lbl=$.createEl("label",label);lbl.addEventListener("click",(()=>{el.checked=!el.checked;el.dispatchEvent(new Event("input",{bubbles:true}));el.dispatchEvent(new Event("change",{bubbles:true}))}));el.insertAdjacentElement("afterend",lbl);el.label=lbl;return el};$.createColorPicker=(value="#ffffff")=>{let el=$.createEl("input");el.type="color";el.value=value.toString();return el};$.createDiv=content=>$.createEl("div",content);$.createImg=src=>{let el=$.createEl("img");el.crossOrigin="anonymous";el.src=src;return el};$.createInput=(value="",type="text")=>{let el=$.createEl("input");el.value=value;el.type=type;el.style.boxSizing="border-box";return el};$.createP=content=>$.createEl("p",content);let radioCount=0;$.createRadio=name=>{let el=$.createEl("div");el.name=name||"radio"+radioCount++;el.buttons=[];Object.defineProperty(el,"value",{get:()=>el.selected?.value,set:v=>{let btn=el.buttons.find((b=>b.value==v));if(btn){btn.checked=true;el.selected=btn}}});el.option=(label,value)=>{let btn=$.createEl("input");btn.type="radio";btn.name=el.name;btn.value=value||label;btn.addEventListener("input",(()=>el.selected=btn));let lbl=$.createEl("label",label);lbl.addEventListener("click",(()=>{btn.checked=true;el.selected=btn;btn.dispatchEvent(new Event("input",{bubbles:true}));btn.dispatchEvent(new Event("change",{bubbles:true}))}));btn.label=lbl;el.append(btn);el.append(lbl);el.buttons.push(btn);return el};return el};$.createSelect=placeholder=>{let el=$.createEl("select");if(placeholder){let opt=$.createEl("option",placeholder);opt.disabled=true;opt.selected=true;el.append(opt)}Object.defineProperty(el,"selected",{get:()=>{if(el.multiple){return Array.from(el.selectedOptions).map((opt=>opt.textContent))}return el.selectedOptions[0]?.textContent},set:v=>{if(el.multiple){Array.from(el.options).forEach((opt=>{opt.selected=v.includes(opt.textContent)}))}else{const option=Array.from(el.options).find((opt=>opt.textContent===v));if(option)option.selected=true}}});Object.defineProperty(el,"value",{get:()=>{if(el.multiple){return Array.from(el.selectedOptions).map((o=>o.value))}return el.selectedOptions[0]?.value},set:v=>{if(el.multiple){el.options.forEach((o=>o.selected=v.includes(o.value)))}else{let opt;for(let i=0;i<el.options.length;i++){if(el.options[i].value==v){opt=el.options[i];break}}if(opt)opt.selected=true}}});el.option=(label,value)=>{let opt=$.createEl("option",label);opt.value=value||label;el.append(opt);return el};return el};$.createSlider=(min,max,value,step)=>{let el=$.createEl("input");el.type="range";el.min=min;el.max=max;el.value=value;el.step=step;el.val=()=>parseFloat(el.value);return el};$.createSpan=content=>$.createEl("span",content);$.createVideo=src=>{let el=$.createEl("video");el.crossOrigin="anonymous";el.src=src;return el};$.findElement=selector=>document.querySelector(selector);$.findElements=selector=>document.querySelectorAll(selector)};Q5.modules.input=($,q)=>{if($._scope=="graphics")return;$.mouseX=0;$.mouseY=0;$.pmouseX=0;$.pmouseY=0;$.touches=[];$.mouseButton="";$.keyIsPressed=false;$.mouseIsPressed=false;$.key="";$.keyCode=0;$.UP_ARROW=38;$.DOWN_ARROW=40;$.LEFT_ARROW=37;$.RIGHT_ARROW=39;$.SHIFT=16;$.TAB=9;$.BACKSPACE=8;$.ENTER=$.RETURN=13;$.ALT=$.OPTION=18;$.CONTROL=17;$.DELETE=46;$.ESCAPE=27;$.ARROW="default";$.CROSS="crosshair";$.HAND="pointer";$.MOVE="move";$.TEXT="text";let keysHeld={};let mouseBtns=[Q5.LEFT,Q5.CENTER,Q5.RIGHT];let c=$.canvas;$._startAudio=()=>{if(!Q5.aud||Q5.aud?.state!="running")$.userStartAudio()};$._updateMouse=e=>{if(e.changedTouches)return;if(c){let rect=c.getBoundingClientRect();let sx=c.scrollWidth/$.width||1;let sy=c.scrollHeight/$.height||1;q.mouseX=(e.clientX-rect.left)/sx;q.mouseY=(e.clientY-rect.top)/sy;if(c.renderer=="webgpu"){q.mouseX-=c.hw;q.mouseY-=c.hh}}else{q.mouseX=e.clientX;q.mouseY=e.clientY}q.moveX=e.movementX;q.moveY=e.movementY};let pressedInCanvas=0;$._onmousedown=e=>{pressedInCanvas++;$._startAudio();$._updateMouse(e);q.mouseIsPressed=true;q.mouseButton=mouseBtns[e.button];$.mousePressed(e)};$._onmousemove=e=>{$._updateMouse(e);if($.mouseIsPressed)$.mouseDragged(e);else $.mouseMoved(e)};$._onmouseup=e=>{$._updateMouse(e);q.mouseIsPressed=false;$.mouseReleased(e)};$._onclick=e=>{$._updateMouse(e);q.mouseIsPressed=true;$.mouseClicked(e);q.mouseIsPressed=false};$._onwheel=e=>{$._updateMouse(e);e.delta=e.deltaY;if($.mouseWheel(e)==false)e.preventDefault()};$.cursor=(name,x,y)=>{let pfx="";if(name.includes(".")){name=`url("${name}")`;pfx=", auto"}if(x!==undefined){name+=" "+x+" "+y}$.canvas.style.cursor=name+pfx};$.noCursor=()=>{$.canvas.style.cursor="none"};if(window){$.lockMouse=document.body?.requestPointerLock;$.unlockMouse=document.exitPointerLock}$._onkeydown=e=>{if(e.repeat)return;$._startAudio();q.keyIsPressed=true;q.key=e.key;q.keyCode=e.keyCode;keysHeld[$.keyCode]=keysHeld[$.key.toLowerCase()]=true;$.keyPressed(e);if(e.key.length==1)$.keyTyped(e)};$._onkeyup=e=>{q.keyIsPressed=false;q.key=e.key;q.keyCode=e.keyCode;keysHeld[$.keyCode]=keysHeld[$.key.toLowerCase()]=false;$.keyReleased(e)};$.keyIsDown=v=>!!keysHeld[typeof v=="string"?v.toLowerCase():v];function getTouchInfo(touch){const rect=$.canvas.getBoundingClientRect();const sx=$.canvas.scrollWidth/$.width||1;const sy=$.canvas.scrollHeight/$.height||1;return{x:(touch.clientX-rect.left)/sx,y:(touch.clientY-rect.top)/sy,id:touch.identifier}}$._ontouchstart=e=>{$._startAudio();q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware){q.mouseX=$.touches[0].x;q.mouseY=$.touches[0].y;q.mouseIsPressed=true;q.mouseButton=$.LEFT;$.mousePressed(e)}$.touchStarted(e)};$._ontouchmove=e=>{q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware){q.mouseX=$.touches[0].x;q.mouseY=$.touches[0].y;if(!$.mouseDragged(e))e.preventDefault()}if(!$.touchMoved(e))e.preventDefault()};$._ontouchend=e=>{q.touches=[...e.touches].map(getTouchInfo);if(!$._isTouchAware&&!$.touches.length){q.mouseIsPressed=false;if(!$.mouseReleased(e))e.preventDefault()}if(!$.touchEnded(e))e.preventDefault()};if(c){let l=c.addEventListener.bind(c);l("mousedown",(e=>$._onmousedown(e)));l("wheel",(e=>$._onwheel(e)));l("click",(e=>$._onclick(e)));l("touchstart",(e=>$._ontouchstart(e)));l("touchmove",(e=>$._ontouchmove(e)));l("touchend",(e=>$._ontouchend(e)));l("touchcancel",(e=>$._ontouchend(e)))}if(window){let l=window.addEventListener;l("keydown",(e=>$._onkeydown(e)),false);l("keyup",(e=>$._onkeyup(e)),false);if(!c){l("mousedown",(e=>$._onmousedown(e)));l("wheel",(e=>$._onwheel(e)));l("click",(e=>$._onclick(e)))}l("mousemove",(e=>$._onmousemove(e)),false);l("mouseup",(e=>{if(pressedInCanvas>0){pressedInCanvas--;$._onmouseup(e)}}))}};Q5.modules.math=($,q)=>{$.RADIANS=0;$.DEGREES=1;$.PI=Math.PI;$.HALF_PI=Math.PI/2;$.QUARTER_PI=Math.PI/4;$.TWO_PI=$.TAU=Math.PI*2;$.abs=Math.abs;$.ceil=Math.ceil;$.exp=Math.exp;$.floor=$.int=Math.floor;$.loge=Math.log;$.mag=Math.hypot;$.max=Math.max;$.min=Math.min;$.round=Math.round;$.pow=Math.pow;$.sqrt=Math.sqrt;$.SHR3=1;$.LCG=2;let angleMode=$._angleMode=0;$.angleMode=mode=>{angleMode=$._angleMode=mode==0||mode=="radians"?0:1;return!angleMode?"radians":"degrees"};let DEGTORAD=$._DEGTORAD=Math.PI/180;let RADTODEG=$._RADTODEG=180/Math.PI;$.degrees=x=>x*$._RADTODEG;$.radians=x=>x*$._DEGTORAD;$.map=Q5.prototype.map=(value,istart,istop,ostart,ostop,clamp)=>{let val=ostart+(ostop-ostart)*((value-istart)*1/(istop-istart));if(!clamp){return val}if(ostart<ostop){return Math.min(Math.max(val,ostart),ostop)}else{return Math.min(Math.max(val,ostop),ostart)}};$.dist=function(){let a=arguments;if(a.length==2)return Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y);if(a.length==4)return Math.hypot(a[0]-a[2],a[1]-a[3]);return Math.hypot(a[0]-a[3],a[1]-a[4],a[2]-a[5])};$.lerp=(a,b,t)=>a*(1-t)+b*t;$.constrain=(x,lo,hi)=>Math.min(Math.max(x,lo),hi);$.norm=(value,start,stop)=>$.map(value,start,stop,0,1);$.sq=x=>x*x;$.fract=x=>x-Math.floor(x);$.sin=a=>Math.sin(!angleMode?a:a*DEGTORAD);$.cos=a=>Math.cos(!angleMode?a:a*DEGTORAD);$.tan=a=>Math.tan(!angleMode?a:a*DEGTORAD);$.asin=x=>{let a=Math.asin(x);return!angleMode?a:a*RADTODEG};$.acos=x=>{let a=Math.acos(x);return!angleMode?a:a*RADTODEG};$.atan=x=>{let a=Math.atan(x);return!angleMode?a:a*RADTODEG};$.atan2=(y,x)=>{let a=Math.atan2(y,x);return!angleMode?a:a*RADTODEG};function lcg(){const m=4294967296;const a=1664525;const c=1013904223;let seed,z;return{setSeed(val){z=seed=(val==null?Math.random()*m:val)>>>0},getSeed(){return seed},rand(){z=(a*z+c)%m;return z/m}}}function shr3(){let jsr,seed;let m=4294967295;return{setSeed(val){jsr=seed=(val==null?Math.random()*m:val)>>>0},getSeed(){return seed},rand(){jsr^=jsr<<17;jsr^=jsr>>13;jsr^=jsr<<5;return(jsr>>>0)/m}}}let rng1=shr3();rng1.setSeed();$.randomSeed=seed=>rng1.setSeed(seed);$.random=(a,b)=>{if(a===undefined)return rng1.rand();if(typeof a=="number"){if(b!==undefined){return rng1.rand()*(b-a)+a}else{return rng1.rand()*a}}else{return a[Math.trunc(a.length*rng1.rand())]}};if($._renderer=="c2d"&&!$._webgpuFallback){$.randomX=(v=0)=>$.random(-v,$.canvas.w+v);$.randomY=(v=0)=>$.random(-v,$.canvas.h+v)}else{$.randomX=(v=0)=>$.random(-$.canvas.hw-v,$.canvas.hw+v);$.randomY=(v=0)=>$.random(-$.canvas.hh-v,$.canvas.hh+v)}$.randomGenerator=method=>{if(method==$.LCG)rng1=lcg();else if(method==$.SHR3)rng1=shr3();rng1.setSeed()};var ziggurat=new function(){var iz;var jz;var kn=new Array(128);var ke=new Array(256);var hz;var wn=new Array(128);var fn=new Array(128);var we=new Array(256);var fe=new Array(256);var SHR3=()=>rng1.rand()*4294967296-2147483648;var UNI=()=>.5+(SHR3()<<0)*2.328306e-10;var RNOR=()=>{hz=SHR3();iz=hz&127;return Math.abs(hz)<kn[iz]?hz*wn[iz]:nfix()};var REXP=()=>{jz=SHR3()>>>0;iz=jz&255;return jz<kn[iz]?jz*we[iz]:efix()};var nfix=()=>{var r=3.44262;var x,y;var u1,u2;for(;;){x=hz*wn[iz];if(iz==0){do{u1=UNI();u2=UNI();x=-Math.log(u1)*.2904764;y=-Math.log(u2)}while(y+y<x*x);return hz>0?r+x:-r-x}if(fn[iz]+UNI()*(fn[iz-1]-fn[iz])<Math.exp(-.5*x*x)){return x}hz=SHR3();iz=hz&127;if(Math.abs(hz)<kn[iz]){return hz*wn[iz]}}};var efix=()=>{var x;for(;;){if(iz==0){return 7.69711-Math.log(UNI())}x=jz*we[iz];if(fe[iz]+UNI()*(fe[iz-1]-fe[iz])<Math.exp(-x)){return x}jz=SHR3();iz=jz&255;if(jz<ke[iz]){return jz*we[iz]}}};var zigset=()=>{var m1=2147483648;var m2=4294967296;var dn=3.442619855899;var tn=dn;var vn=.00991256303526217;var q;var de=7.697117470131487;var te=de;var ve=.003949659822581572;var i;q=vn/Math.exp(-.5*dn*dn);kn[0]=Math.floor(dn/q*m1);kn[1]=0;wn[0]=q/m1;wn[127]=dn/m1;fn[0]=1;fn[127]=Math.exp(-.5*dn*dn);for(i=126;i>=1;i--){dn=Math.sqrt(-2*Math.log(vn/dn+Math.exp(-.5*dn*dn)));kn[i+1]=Math.floor(dn/tn*m1);tn=dn;fn[i]=Math.exp(-.5*dn*dn);wn[i]=dn/m1}q=ve/Math.exp(-de);ke[0]=Math.floor(de/q*m2);ke[1]=0;we[0]=q/m2;we[255]=de/m2;fe[0]=1;fe[255]=Math.exp(-de);for(i=254;i>=1;i--){de=-Math.log(ve/de+Math.exp(-de));ke[i+1]=Math.floor(de/te*m2);te=de;fe[i]=Math.exp(-de);we[i]=de/m2}};this.SHR3=SHR3;this.UNI=UNI;this.RNOR=RNOR;this.REXP=REXP;this.zigset=zigset};ziggurat.hasInit=false;$.randomGaussian=(mean,std)=>{if(!ziggurat.hasInit){ziggurat.zigset();ziggurat.hasInit=true}return ziggurat.RNOR()*std+mean};$.randomExponential=()=>{if(!ziggurat.hasInit){ziggurat.zigset();ziggurat.hasInit=true}return ziggurat.REXP()};$.PERLIN="perlin";$.SIMPLEX="simplex";$.BLOCKY="blocky";$.Noise=Q5.PerlinNoise;let _noise;$.noiseMode=mode=>{q.Noise=Q5[mode[0].toUpperCase()+mode.slice(1)+"Noise"];_noise=null};$.noiseSeed=seed=>{_noise=new $.Noise(seed)};$.noise=(x=0,y=0,z=0)=>{_noise??=new $.Noise;return _noise.noise(x,y,z)};$.noiseDetail=(lod,falloff)=>{_noise??=new $.Noise;if(lod>0)_noise.octaves=lod;if(falloff>0)_noise.falloff=falloff}};Q5.Noise=class{};Q5.PerlinNoise=class extends Q5.Noise{constructor(seed){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;if(seed==undefined){this.p=Array.from({length:256},(()=>Math.floor(Math.random()*256)))}else{this.p=this.seedPermutation(seed)}this.p=this.p.concat(this.p)}seedPermutation(seed){let p=[];for(let i=0;i<256;i++){p[i]=i}let n,q;for(let i=255;i>0;i--){seed=seed*16807%2147483647;n=seed%(i+1);q=p[i];p[i]=p[n];p[n]=q}return p}dot(g,x,y,z){return g[0]*x+g[1]*y+g[2]*z}mix(a,b,t){return(1-t)*a+t*b}fade(t){return t*t*t*(t*(t*6-15)+10)}noise(x,y,z){let t=this;let total=0;let freq=1;let amp=1;let maxAmp=0;for(let i=0;i<t.octaves;i++){const X=Math.floor(x*freq)&255;const Y=Math.floor(y*freq)&255;const Z=Math.floor(z*freq)&255;const xf=x*freq-Math.floor(x*freq);const yf=y*freq-Math.floor(y*freq);const zf=z*freq-Math.floor(z*freq);const u=t.fade(xf);const v=t.fade(yf);const w=t.fade(zf);const A=t.p[X]+Y;const AA=t.p[A]+Z;const AB=t.p[A+1]+Z;const B=t.p[X+1]+Y;const BA=t.p[B]+Z;const BB=t.p[B+1]+Z;const lerp1=t.mix(t.dot(t.grad3[t.p[AA]%12],xf,yf,zf),t.dot(t.grad3[t.p[BA]%12],xf-1,yf,zf),u);const lerp2=t.mix(t.dot(t.grad3[t.p[AB]%12],xf,yf-1,zf),t.dot(t.grad3[t.p[BB]%12],xf-1,yf-1,zf),u);const lerp3=t.mix(t.dot(t.grad3[t.p[AA+1]%12],xf,yf,zf-1),t.dot(t.grad3[t.p[BA+1]%12],xf-1,yf,zf-1),u);const lerp4=t.mix(t.dot(t.grad3[t.p[AB+1]%12],xf,yf-1,zf-1),t.dot(t.grad3[t.p[BB+1]%12],xf-1,yf-1,zf-1),u);const mix1=t.mix(lerp1,lerp2,v);const mix2=t.mix(lerp3,lerp4,v);total+=t.mix(mix1,mix2,w)*amp;maxAmp+=amp;amp*=t.falloff;freq*=2}return(total/maxAmp+1)/2}};Q5.modules.record=($,q)=>{let rec,btn0,btn1,timer,formatSelect,qualitySelect;$.recording=false;function initRecorder(opt={}){document.head.insertAdjacentHTML("beforeend",`<style>\n.rec {\n\tdisplay: flex;\n\tz-index: 1000;\n\tgap: 6px;\n\tbackground: #1a1b1d;\n\tpadding: 6px 8px;\n\tborder-radius: 21px;\n\tbox-shadow: #0000001a 0px 4px 12px;\n\tborder: 2px solid transparent; \n\topacity: 0.6;\n\ttransition: all 0.3s;\n\twidth: 134px;\n\toverflow: hidden;\n}\n\n.rec:hover {\n\twidth: unset;\n\topacity: 0.96;\n}\n\n.rec.recording { border-color: #cc3e44; }\n\n.rec button,\n.rec select { cursor: pointer; }\n\n.rec button,\n.rec select,\n.rec .record-timer {\n\tfont-family: sans-serif;\n\tfont-size: 14px;\n\tpadding: 2px 10px;\n\tborder-radius: 18px;\n\toutline: none;\n\tbackground-color: #232529;\n\tcolor: #d4dae6;\n\tbox-shadow: #0000001a 0px 4px 12px;\n\tborder: 1px solid #46494e;\n\tvertical-align: middle;\n\tline-height: 18px;\n\ttransition: all 0.3s;\n}\n\n.rec .record-button { \n\tcolor: #cc3e44;\n\tfont-size: 18px;\n}\n\n.rec select:hover,\n.rec button:hover { background-color: #292b30; }\n\n.rec button:disabled {\n\topacity: 0.5;\n\tcolor: #969ba5;\n\tcursor: not-allowed;\n}\n</style>`);rec=$.createEl("div");rec.className="rec";rec.innerHTML=`\n<button class="record-button"></button>\n<span class="record-timer"></span>\n<button></button>\n`;[btn0,timer,btn1]=rec.children;rec.x=rec.y=8;rec.resetTimer=()=>rec.time={hours:0,minutes:0,seconds:0,frames:0};rec.resetTimer();rec.formats=opt.formats||{"H.264":'video/mp4; codecs="avc1.42E01E"',VP9:"video/mp4; codecs=vp9"};for(let format in rec.formats){if(!MediaRecorder.isTypeSupported(rec.formats[format])){delete rec.formats[format]}}formatSelect=$.createSelect("format");for(const name in rec.formats){formatSelect.option(name,rec.formats[name])}formatSelect.title="Video Format";rec.append(formatSelect);let qMult={min:.1,low:.25,mid:.5,high:.75,ultra:.9,max:1};qualitySelect=$.createSelect("quality");for(let name in qMult){qualitySelect.option(name,qMult[name])}qualitySelect.title="Video Quality";rec.append(qualitySelect);rec.encoderSettings={};function changeFormat(){rec.encoderSettings.mimeType=formatSelect.value}function changeQuality(){rec.encoderSettings.videoBitsPerSecond=maxVideoBitRate*qualitySelect.value}formatSelect.addEventListener("change",changeFormat);qualitySelect.addEventListener("change",changeQuality);Object.defineProperty(rec,"quality",{get:()=>qualitySelect.selected,set:v=>{v=v.toLowerCase();if(qMult[v]){qualitySelect.selected=v;changeQuality()}}});Object.defineProperty(rec,"format",{get:()=>formatSelect.selected,set:v=>{v=v.toUpperCase();if(rec.formats[v]){formatSelect.selected=v;changeFormat()}}});let h=$.canvas.height;if(h>=1440&&rec.formats.VP9)rec.format="VP9";else rec.format="H.264";let maxVideoBitRate=(h>=4320?128:h>=2160?75:h>=1440?36:h>=1080?28:h>=720?22:16)*1e6;rec.quality="high";btn0.addEventListener("click",(()=>{if(!$.recording)start();else if(!rec.paused)$.pauseRecording();else resumeRecording()}));btn1.addEventListener("click",(()=>{if(rec.paused)$.saveRecording();else $.deleteRecording()}));resetUI();$.registerMethod("post",updateTimer)}function start(){if($.recording)return;if(!rec.stream){rec.frameRate??=$.getTargetFrameRate();let canvasStream=$.canvas.captureStream(rec.frameRate);rec.stream=canvasStream}try{rec.mediaRecorder=new MediaRecorder(rec.stream,rec.encoderSettings)}catch(e){console.error("Failed to initialize MediaRecorder: ",e);return}rec.chunks=[];rec.mediaRecorder.addEventListener("dataavailable",(e=>{if(e.data.size>0)rec.chunks.push(e.data)}));rec.mediaRecorder.start();q.recording=true;rec.paused=false;rec.classList.add("recording");rec.resetTimer();resetUI(true)}function resumeRecording(){if(!$.recording||!rec.paused)return;rec.mediaRecorder.resume();rec.paused=false;resetUI(true)}function stop(){if(!$.recording)return;rec.resetTimer();rec.mediaRecorder.stop();q.recording=false;rec.paused=false;rec.classList.remove("recording")}function resetUI(r){btn0.textContent=r?"⏸":"⏺";btn0.title=(r?"Pause":"Start")+" Recording";btn1.textContent=r?"🗑️":"💾";btn1.title=(r?"Delete":"Save")+" Recording";btn1.disabled=!r}function updateTimer(){if($.recording&&!rec.paused){rec.time.frames++;let fr=$.getTargetFrameRate();if(rec.time.frames>=fr){rec.time.seconds+=Math.floor(rec.time.frames/fr);rec.time.frames%=fr;if(rec.time.seconds>=60){rec.time.minutes+=Math.floor(rec.time.seconds/60);rec.time.seconds%=60;if(rec.time.minutes>=60){rec.time.hours+=Math.floor(rec.time.minutes/60);rec.time.minutes%=60}}}}timer.textContent=formatTime()}function formatTime(){let{hours:hours,minutes:minutes,seconds:seconds,frames:frames}=rec.time;return`${String(hours).padStart(2,"0")}:${String(minutes).padStart(2,"0")}:${String(seconds).padStart(2,"0")}:${String(frames).padStart(2,"0")}`}$.createRecorder=opt=>{if(!rec)initRecorder(opt);return rec};$.record=opt=>{if(!rec){initRecorder(opt);rec.hide()}if(!$.recording)start();else if(rec.paused)resumeRecording()};$.pauseRecording=()=>{if(!$.recording||rec.paused)return;rec.mediaRecorder.pause();rec.paused=true;resetUI();btn0.title="Resume Recording";btn1.disabled=false};$.deleteRecording=()=>{stop();resetUI();q.recording=false};$.saveRecording=async fileName=>{if(!$.recording)return;await new Promise((resolve=>{rec.mediaRecorder.onstop=resolve;stop()}));let type=rec.encoderSettings.mimeType,extension=type.slice(6,type.indexOf(";")),dataUrl=URL.createObjectURL(new Blob(rec.chunks,{type:type})),iframe=document.createElement("iframe"),a=document.createElement("a");iframe.style.display="none";iframe.name="download_"+Date.now();document.body.append(iframe);a.target=iframe.name;a.href=dataUrl;fileName??=document.title+" "+(new Date).toLocaleString(undefined,{hour12:false}).replace(","," at").replaceAll("/","-").replaceAll(":","_");a.download=`${fileName}.${extension}`;await new Promise((resolve=>{iframe.onload=()=>{document.body.removeChild(iframe);resolve()};a.click()}));setTimeout((()=>URL.revokeObjectURL(dataUrl)),1e3);resetUI();q.recording=false}};Q5.modules.sound=($,q)=>{$.Sound=Q5.Sound;let sounds=[];$.loadSound=(url,cb)=>{q._preloadCount++;let s=new Q5.Sound;sounds.push(s);s._loader=(async()=>{let err;try{await s.load(url)}catch(e){err=e}q._preloadCount--;delete s._loader;if(err)throw err;if(cb)cb(s);return s})();if($._disablePreload)return s._loader;return s};$.loadAudio=(url,cb)=>{q._preloadCount++;let a=new Audio(url);a.crossOrigin="Anonymous";a.addEventListener("canplaythrough",(()=>{if(!a.loaded){q._preloadCount--;a.loaded=true;if(cb)cb(a)}}));let preloadSkip=()=>{a._preloadSkip=true;q._preloadCount--};a.addEventListener("suspend",preloadSkip);a.addEventListener("error",(e=>{preloadSkip();throw e}));return a};$.getAudioContext=()=>Q5.aud;$.userStartAudio=()=>{if(window.AudioContext){if(Q5._offlineAudio){Q5._offlineAudio=false;Q5.aud=new window.AudioContext;for(let s of sounds)s.init()}return Q5.aud.resume()}}};if(window.OfflineAudioContext){Q5.aud=new window.OfflineAudioContext(2,1,44100);Q5._offlineAudio=true}Q5.Sound=class{constructor(){this.sources=new Set;this.loaded=this.paused=false}async load(url){this.url=url;let res=await fetch(url);this.buffer=await res.arrayBuffer();this.buffer=await Q5.aud.decodeAudioData(this.buffer)}init(){this.gainNode=Q5.aud.createGain();this.pannerNode=Q5.aud.createStereoPanner();this.gainNode.connect(this.pannerNode);this.pannerNode.connect(Q5.aud.destination);this.loaded=true;if(this._volume)this.volume=this._volume;if(this._pan)this.pan=this._pan}_newSource(offset,duration){let source=Q5.aud.createBufferSource();source.buffer=this.buffer;source.connect(this.gainNode);source.loop=this._loop;source._startedAt=Q5.aud.currentTime;source._offset=offset;source._duration=duration;source.start(0,source._offset,source._duration);this.sources.add(source);source.onended=()=>{if(!this.paused){this.ended=true;this.sources.delete(source)}}}play(time=0,duration){if(!this.loaded)return;if(!this.paused){this._newSource(time,duration)}else{let timings=[];for(let source of this.sources){timings.push(source._offset,source._duration);this.sources.delete(source)}for(let i=0;i<timings.length;i+=2){this._newSource(timings[i],timings[i+1])}}this.paused=this.ended=false}pause(){if(!this.isPlaying())return;for(let source of this.sources){source.stop();let timePassed=Q5.aud.currentTime-source._startedAt;source._offset+=timePassed;if(source._duration)source._duration-=timePassed}this.paused=true}stop(){for(let source of this.sources){source.stop();this.sources.delete(source)}this.paused=false;this.ended=true}get volume(){return this._volume}set volume(level){if(this.loaded)this.gainNode.gain.value=level;this._volume=level}get pan(){return this._pan}set pan(value){if(this.loaded)this.pannerNode.pan.value=value;this._pan=value}get loop(){return this._loop}set loop(value){this.sources.forEach((source=>source.loop=value));this._loop=value}get playing(){return!this.paused&&this.sources.size>0}setVolume(level){this.volume=level}setPan(value){this.pan=value}setLoop(loop){this.loop=loop}isLoaded(){return this.loaded}isPlaying(){return this.playing}isPaused(){return this.paused}isLooping(){return this._loop}};Q5.modules.util=($,q)=>{$._loadFile=(url,cb,type)=>{q._preloadCount++;let ret={};ret._loader=new Promise(((resolve,reject)=>{fetch(url).then((res=>{if(!res.ok){reject("error loading file");return null}if(type=="json")return res.json();return res.text()})).then((f=>{if(type=="csv")f=$.CSV.parse(f);if(typeof f=="string")ret.text=f;else Object.assign(ret,f);delete ret._loader;if(cb)cb(f);q._preloadCount--;resolve(f)}))}));return ret};$.loadText=(url,cb)=>$._loadFile(url,cb,"text");$.loadJSON=(url,cb)=>$._loadFile(url,cb,"json");$.loadCSV=(url,cb)=>$._loadFile(url,cb,"csv");$.load=function(...urls){if(Array.isArray(urls[0]))urls=urls[0];let loaders=[];for(let url of urls){let ext=url.split(".").pop().toLowerCase();let obj;if(ext=="json"&&!url.includes("-msdf.")){obj=$.loadJSON(url)}else if(ext=="csv"){obj=$.loadCSV(url)}else if(/(jpe?g|png|gif|webp|avif|svg)/.test(ext)){obj=$.loadImage(url)}else if(/(ttf|otf|woff2?|eot|json)/i.test(ext)){obj=$.loadFont(url)}else if(/(wav|flac|mp3|ogg|m4a|aac|aiff|weba)/.test(ext)){obj=$.loadSound(url)}else{obj=$.loadText(url)}loaders.push(obj._loader)}if(urls.length==1)return loaders[0];return Promise.all(loaders)};$.CSV={};$.CSV.parse=(csv,sep=",",lineSep="\n")=>{if(!csv.length)return[];let a=[],lns=csv.split(lineSep),headers=lns[0].split(sep).map((h=>h.replaceAll('"',"")));for(let i=1;i<lns.length;i++){let o={},ln=lns[i].split(sep);headers.forEach(((h,i)=>o[h]=JSON.parse(ln[i])));a.push(o)}return a};if(typeof localStorage=="object"){$.storeItem=localStorage.setItem;$.getItem=localStorage.getItem;$.removeItem=localStorage.removeItem;$.clearStorage=localStorage.clear}$.year=()=>(new Date).getFullYear();$.day=()=>(new Date).getDay();$.hour=()=>(new Date).getHours();$.minute=()=>(new Date).getMinutes();$.second=()=>(new Date).getSeconds()};Q5.modules.vector=$=>{$.createVector=(x,y,z)=>new Q5.Vector(x,y,z,$)};Q5.Vector=class{constructor(x,y,z,$){this.x=x||0;this.y=y||0;this.z=z||0;this._$=$||window;this._cn=null;this._cnsq=null}set(x,y,z){this.x=x?.x||x||0;this.y=x?.y||y||0;this.z=x?.z||z||0;return this}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(x,y,z){if(x?.x!==undefined)return x;if(y!==undefined){return{x:x,y:y,z:z||0}}return{x:x,y:x,z:x}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z;this._cn=Math.sqrt(this._cnsq)}add(){let u=this._arg2v(...arguments);this.x+=u.x;this.y+=u.y;this.z+=u.z;return this}rem(){let u=this._arg2v(...arguments);this.x%=u.x;this.y%=u.y;this.z%=u.z;return this}sub(){let u=this._arg2v(...arguments);this.x-=u.x;this.y-=u.y;this.z-=u.z;return this}mult(){let u=this._arg2v(...arguments);this.x*=u.x;this.y*=u.y;this.z*=u.z;return this}div(){let u=this._arg2v(...arguments);if(u.x)this.x/=u.x;else this.x=0;if(u.y)this.y/=u.y;else this.y=0;if(u.z)this.z/=u.z;else this.z=0;return this}mag(){this._calcNorm();return this._cn}magSq(){this._calcNorm();return this._cnsq}dot(){let u=this._arg2v(...arguments);return this.x*u.x+this.y*u.y+this.z*u.z}dist(){let u=this._arg2v(...arguments);let x=this.x-u.x;let y=this.y-u.y;let z=this.z-u.z;return Math.sqrt(x*x+y*y+z*z)}cross(){let u=this._arg2v(...arguments);let x=this.y*u.z-this.z*u.y;let y=this.z*u.x-this.x*u.z;let z=this.x*u.y-this.y*u.x;this.x=x;this.y=y;this.z=z;return this}normalize(){this._calcNorm();let n=this._cn;if(n!=0){this.x/=n;this.y/=n;this.z/=n}this._cn=1;this._cnsq=1;return this}limit(m){this._calcNorm();let n=this._cn;if(n>m){let t=m/n;this.x*=t;this.y*=t;this.z*=t;this._cn=m;this._cnsq=m*m}return this}setMag(m){this._calcNorm();let n=this._cn;let t=m/n;this.x*=t;this.y*=t;this.z*=t;this._cn=m;this._cnsq=m*m;return this}heading(){return this._$.atan2(this.y,this.x)}setHeading(ang){let mag=this.mag();this.x=mag*this._$.cos(ang);this.y=mag*this._$.sin(ang);return this}rotate(ang){let costh=this._$.cos(ang);let sinth=this._$.sin(ang);let vx=this.x*costh-this.y*sinth;let vy=this.x*sinth+this.y*costh;this.x=vx;this.y=vy;return this}angleBetween(){let u=this._arg2v(...arguments);let o=Q5.Vector.cross(this,u);let ang=this._$.atan2(o.mag(),this.dot(u));return ang*Math.sign(o.z||1)}lerp(){let args=[...arguments];let amt=args.at(-1);if(amt==0)return this;let u=this._arg2v(...args.slice(0,-1));this.x+=(u.x-this.x)*amt;this.y+=(u.y-this.y)*amt;this.z+=(u.z-this.z)*amt;return this}slerp(){let args=[...arguments];let amt=args.at(-1);if(amt==0)return this;let u=this._arg2v(...args.slice(0,-1));if(amt==1)return this.set(u);let v0Mag=this.mag();let v1Mag=u.mag();if(v0Mag==0||v1Mag==0){return this.mult(1-amt).add(u.mult(amt))}let axis=Q5.Vector.cross(this,u);let axisMag=axis.mag();let theta=Math.atan2(axisMag,this.dot(u));if(axisMag>0){axis.div(axisMag)}else if(theta<this._$.HALF_PI){return this.mult(1-amt).add(u.mult(amt))}else{if(this.z==0&&u.z==0)axis.set(0,0,1);else if(this.x!=0)axis.set(this.y,-this.x,0).normalize();else axis.set(1,0,0)}let ey=axis.cross(this);let lerpedMagFactor=1-amt+amt*v1Mag/v0Mag;let cosMultiplier=lerpedMagFactor*Math.cos(amt*theta);let sinMultiplier=lerpedMagFactor*Math.sin(amt*theta);this.x=this.x*cosMultiplier+ey.x*sinMultiplier;this.y=this.y*cosMultiplier+ey.y*sinMultiplier;this.z=this.z*cosMultiplier+ey.z*sinMultiplier;return this}reflect(n){n.normalize();return this.sub(n.mult(2*this.dot(n)))}array(){return[this.x,this.y,this.z]}equals(u,epsilon){epsilon??=Number.EPSILON||0;return Math.abs(u.x-this.x)<epsilon&&Math.abs(u.y-this.y)<epsilon&&Math.abs(u.z-this.z)<epsilon}fromAngle(th,l){if(l===undefined)l=1;this._cn=l;this._cnsq=l*l;this.x=l*this._$.cos(th);this.y=l*this._$.sin(th);this.z=0;return this}fromAngles(th,ph,l){if(l===undefined)l=1;this._cn=l;this._cnsq=l*l;const cosph=this._$.cos(ph);const sinph=this._$.sin(ph);const costh=this._$.cos(th);const sinth=this._$.sin(th);this.x=l*sinth*sinph;this.y=-l*costh;this.z=l*sinth*cosph;return this}random2D(){this._cn=this._cnsq=1;return this.fromAngle(Math.random()*Math.PI*2)}random3D(){this._cn=this._cnsq=1;return this.fromAngles(Math.random()*Math.PI*2,Math.random()*Math.PI*2)}toString(){return`[${this.x}, ${this.y}, ${this.z}]`}};Q5.Vector.add=(v,u)=>v.copy().add(u);Q5.Vector.cross=(v,u)=>v.copy().cross(u);Q5.Vector.dist=(v,u)=>Math.hypot(v.x-u.x,v.y-u.y,v.z-u.z);Q5.Vector.div=(v,u)=>v.copy().div(u);Q5.Vector.dot=(v,u)=>v.copy().dot(u);Q5.Vector.equals=(v,u,epsilon)=>v.equals(u,epsilon);Q5.Vector.lerp=(v,u,amt)=>v.copy().lerp(u,amt);Q5.Vector.slerp=(v,u,amt)=>v.copy().slerp(u,amt);Q5.Vector.limit=(v,m)=>v.copy().limit(m);Q5.Vector.heading=v=>this._$.atan2(v.y,v.x);Q5.Vector.magSq=v=>v.x*v.x+v.y*v.y+v.z*v.z;Q5.Vector.mag=v=>Math.sqrt(Q5.Vector.magSq(v));Q5.Vector.mult=(v,u)=>v.copy().mult(u);Q5.Vector.normalize=v=>v.copy().normalize();Q5.Vector.rem=(v,u)=>v.copy().rem(u);Q5.Vector.sub=(v,u)=>v.copy().sub(u);for(let k of["fromAngle","fromAngles","random2D","random3D"]){Q5.Vector[k]=(u,v,t)=>(new Q5.Vector)[k](u,v,t)}Q5.renderers.webgpu={};Q5.renderers.webgpu.canvas=($,q)=>{let c=$.canvas;c.width=$.width=500;c.height=$.height=500;$._g=$.createGraphics(1,1);if($.colorMode)$.colorMode("rgb",1);let pass,mainView,colorIndex=1,colorStackIndex=8;$._pipelineConfigs=[];$._pipelines=[];let drawStack=$.drawStack=[];let colorStack=$.colorStack=new Float32Array(1e6);colorStack.set([0,0,0,1,1,1,1,1]);let mainLayout=Q5.device.createBindGroupLayout({label:"mainLayout",entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]});$.bindGroupLayouts=[mainLayout];let uniformBuffer=Q5.device.createBuffer({size:8,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let createMainView=()=>{mainView=Q5.device.createTexture({size:[$.canvas.width,$.canvas.height],sampleCount:4,format:"bgra8unorm",usage:GPUTextureUsage.RENDER_ATTACHMENT}).createView()};$._createCanvas=(w,h,opt)=>{q.ctx=q.drawingContext=c.getContext("webgpu");opt.format??=navigator.gpu.getPreferredCanvasFormat();opt.device??=Q5.device;if(opt.alpha)opt.alphaMode="premultiplied";$.ctx.configure(opt);Q5.device.queue.writeBuffer(uniformBuffer,0,new Float32Array([$.canvas.hw,$.canvas.hh]));createMainView();return c};$._resizeCanvas=(w,h)=>{$._setCanvasSize(w,h);createMainView()};$.pixelDensity=v=>{if(!v||v==$._pixelDensity)return $._pixelDensity;$._pixelDensity=v;$._setCanvasSize(c.w,c.h);createMainView();return v};let addColor=(r,g,b,a=1)=>{if(typeof r=="string")r=$.color(r);else if(b==undefined){a=g??1;g=b=r}if(r._q5Color){a=r.a;b=r.b;g=r.g;r=r.r}let cs=colorStack,i=colorStackIndex;cs[i++]=r;cs[i++]=g;cs[i++]=b;cs[i++]=a;colorStackIndex=i;colorIndex++};$._stroke=0;$._fill=$._tint=$._globalAlpha=1;$._doFill=$._doStroke=true;$.fill=(r,g,b,a)=>{addColor(r,g,b,a);$._doFill=$._fillSet=true;$._fill=colorIndex};$.stroke=(r,g,b,a)=>{addColor(r,g,b,a);$._doStroke=$._strokeSet=true;$._stroke=colorIndex};$.tint=(r,g,b,a)=>{addColor(r,g,b,a);$._tint=colorIndex};$.opacity=a=>$._globalAlpha=a;$.noFill=()=>$._doFill=false;$.noStroke=()=>$._doStroke=false;$.noTint=()=>$._tint=1;$._strokeWeight=1;$._hsw=.5;$._scaledSW=1;$.strokeWeight=v=>{v=Math.abs(v);$._strokeWeight=v;$._scaledSW=v*$._scale;$._hsw=v/2};const MAX_TRANSFORMS=1e7,MATRIX_SIZE=16,transforms=new Float32Array(MAX_TRANSFORMS*MATRIX_SIZE);let matrix,matrices=[],matricesIndexStack=[];$._matrixDirty=false;matrices.push([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);transforms.set(matrices[0]);$.resetMatrix=()=>{matrix=matrices[0].slice();$._matrixIndex=0};$.resetMatrix();$.translate=(x,y,z)=>{if(!x&&!y&&!z)return;matrix[12]+=x;matrix[13]-=y;matrix[14]+=z||0;$._matrixDirty=true};$.rotate=a=>{if(!a)return;if($._angleMode)a*=$._DEGTORAD;let cosR=Math.cos(a),sinR=Math.sin(a),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];if(m0==1&&!m1&&!m4&&m5==1){m[0]=cosR;m[1]=-sinR;m[4]=sinR;m[5]=cosR}else{m[0]=m0*cosR+m1*sinR;m[1]=m1*cosR-m0*sinR;m[4]=m4*cosR+m5*sinR;m[5]=m5*cosR-m4*sinR}$._matrixDirty=true};$._scale=1;$.scale=(x=1,y,z=1)=>{y??=x;$._scale=Math.max(Math.abs(x),Math.abs(y));$._scaledSW=$._strokeWeight*$._scale;let m=matrix;m[0]*=x;m[1]*=x;m[2]*=x;m[3]*=x;m[4]*=y;m[5]*=y;m[6]*=y;m[7]*=y;m[8]*=z;m[9]*=z;m[10]*=z;m[11]*=z;$._matrixDirty=true};$.shearX=ang=>{if(!ang)return;if($._angleMode)ang*=$._DEGTORAD;let tanAng=Math.tan(ang),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];m[0]=m0+m4*tanAng;m[1]=m1+m5*tanAng;$._matrixDirty=true};$.shearY=ang=>{if(!ang)return;if($._angleMode)ang*=$._DEGTORAD;let tanAng=Math.tan(ang),m=matrix,m0=m[0],m1=m[1],m4=m[4],m5=m[5];m[4]=m4+m0*tanAng;m[5]=m5+m1*tanAng;$._matrixDirty=true};$.applyMatrix=(...args)=>{let m;if(args.length==1)m=args[0];else m=args;if(m.length==9){m=[m[0],m[1],0,m[2],m[3],m[4],0,m[5],0,0,1,0,m[6],m[7],0,m[8]]}else if(m.length!=16){throw new Error("Matrix must be a 3x3 or 4x4 array.")}matrix=m.slice();$._matrixDirty=true};$._saveMatrix=()=>{transforms.set(matrix,matrices.length*MATRIX_SIZE);$._matrixIndex=matrices.length;matrices.push(matrix.slice());$._matrixDirty=false};$.pushMatrix=()=>{if($._matrixDirty)$._saveMatrix();matricesIndexStack.push($._matrixIndex)};$.popMatrix=()=>{if(!matricesIndexStack.length){return console.warn("Matrix index stack is empty!")}let idx=matricesIndexStack.pop();matrix=matrices[idx].slice();$._matrixIndex=idx;$._matrixDirty=false};let _pushStyles=$.pushStyles;$.pushStyles=()=>{_pushStyles();$.strokeWeight($._strokeWeight)};$.push=()=>{$.pushMatrix();$.pushStyles()};$.pop=()=>{$.popMatrix();$.popStyles()};$._calcBox=(x,y,w,h,mode)=>{let hw=w/2;let hh=h/2;let l,r,t,b;if(!mode||mode=="corner"){l=x;r=x+w;t=-y;b=-(y+h)}else if(mode=="center"){l=x-hw;r=x+hw;t=-(y-hh);b=-(y+hh)}else{l=x;r=w;t=-y;b=-h}return[l,r,t,b]};let blendFactors=["zero","one","src-alpha","one-minus-src-alpha","dst","dst-alpha","one-minus-dst-alpha","one-minus-src"];let blendOps=["add","subtract","reverse-subtract","min","max"];const blendModes={normal:[2,3,0,2,3,0],additive:[1,1,0,1,1,0]};$.blendConfigs={};for(const[name,mode]of Object.entries(blendModes)){$.blendConfigs[name]={color:{srcFactor:blendFactors[mode[0]],dstFactor:blendFactors[mode[1]],operation:blendOps[mode[2]]},alpha:{srcFactor:blendFactors[mode[3]],dstFactor:blendFactors[mode[4]],operation:blendOps[mode[5]]}}}$._blendMode="normal";$.blendMode=mode=>{if(mode==$._blendMode)return;if(mode=="source-over")mode="normal";if(mode=="lighter")mode="additive";mode=mode.toLowerCase().replace(/[ -]/g,"_");$._blendMode=mode;for(let i=0;i<$._pipelines.length;i++){$._pipelineConfigs[i].fragment.targets[0].blend=$.blendConfigs[mode];$._pipelines[i]=Q5.device.createRenderPipeline($._pipelineConfigs[i])}};$.clear=()=>{};$._beginRender=()=>{$.encoder=Q5.device.createCommandEncoder();pass=q.pass=$.encoder.beginRenderPass({label:"q5-webgpu",colorAttachments:[{view:mainView,resolveTarget:$.ctx.getCurrentTexture().createView(),loadOp:"clear",storeOp:"store",clearValue:[0,0,0,0]}]})};$._render=()=>{let transformBuffer=Q5.device.createBuffer({size:matrices.length*MATRIX_SIZE*4,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0,matrices.length*MATRIX_SIZE));transformBuffer.unmap();let colorsBuffer=Q5.device.createBuffer({size:colorStackIndex*4,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0,colorStackIndex));colorsBuffer.unmap();let mainBindGroup=Q5.device.createBindGroup({layout:mainLayout,entries:[{binding:0,resource:{buffer:uniformBuffer}},{binding:1,resource:{buffer:transformBuffer}},{binding:2,resource:{buffer:colorsBuffer}}]});pass.setBindGroup(0,mainBindGroup);for(let m of $._hooks.preRender)m();let drawVertOffset=0,imageVertOffset=0,textCharOffset=0,curPipelineIndex=-1;for(let i=0;i<drawStack.length;i+=2){let v=drawStack[i+1];if(curPipelineIndex!=drawStack[i]){curPipelineIndex=drawStack[i];pass.setPipeline($._pipelines[curPipelineIndex])}if(curPipelineIndex==0){pass.draw(v,1,drawVertOffset);drawVertOffset+=v}else if(curPipelineIndex==1){pass.setBindGroup(1,$._textureBindGroups[v]);pass.draw(4,1,imageVertOffset);imageVertOffset+=4}else if(curPipelineIndex==2){let o=drawStack[i+2];pass.setBindGroup(1,$._fonts[o].bindGroup);pass.setBindGroup(2,$._textBindGroup);pass.draw(4,v,0,textCharOffset);textCharOffset+=v;i++}}};$._finishRender=()=>{pass.end();let commandBuffer=$.encoder.finish();Q5.device.queue.submit([commandBuffer]);q.pass=$.encoder=null;$.drawStack=drawStack=[];colorIndex=1;colorStackIndex=8;matrices=[matrices[0]];matricesIndexStack=[];for(let m of $._hooks.postRender)m()}};Q5.initWebGPU=async()=>{if(!navigator.gpu){console.warn("q5 WebGPU not supported on this browser! Use Google Chrome or Edge.");return false}if(!Q5.device){let adapter=await navigator.gpu.requestAdapter();if(!adapter){console.warn("q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.");return false}Q5.device=await adapter.requestDevice()}return true};Q5.webgpu=async function(scope,parent){if(!scope||scope=="global")Q5._hasGlobal=true;if(!await Q5.initWebGPU()){return new Q5(scope,parent,"webgpu-fallback")}return new Q5(scope,parent,"webgpu")};Q5.renderers.webgpu.drawing=($,q)=>{let c=$.canvas,drawStack=$.drawStack,vertexStack=new Float32Array(1e7),vertIndex=0;const TAU=Math.PI*2;const HALF_PI=Math.PI/2;let drawingShader=Q5.device.createShaderModule({label:"drawingShader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@location(0) pos: vec2f,\n\t@location(1) colorIndex: f32,\n\t@location(2) matrixIndex: f32\n}\nstruct FragmentParams {\n\t@builtin(position) position: vec4f,\n\t@location(0) color: vec4f\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@vertex\nfn vertexMain(v: VertexParams) -> FragmentParams {\n\tvar vert = vec4f(v.pos, 0.0, 1.0);\n\tvert = transforms[i32(v.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f: FragmentParams;\n\tf.position = vert;\n\tf.color = colors[i32(v.colorIndex)];\n\treturn f;\n}\n\n@fragment\nfn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {\n\treturn color;\n}\n`});let vertexBufferLayout={arrayStride:16,attributes:[{format:"float32x2",offset:0,shaderLocation:0},{format:"float32",offset:8,shaderLocation:1},{format:"float32",offset:12,shaderLocation:2}]};let pipelineLayout=Q5.device.createPipelineLayout({label:"drawingPipelineLayout",bindGroupLayouts:$.bindGroupLayouts});$._pipelineConfigs[0]={label:"drawingPipeline",layout:pipelineLayout,vertex:{module:drawingShader,entryPoint:"vertexMain",buffers:[vertexBufferLayout]},fragment:{module:drawingShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[0]=Q5.device.createRenderPipeline($._pipelineConfigs[0]);const addVert=(x,y,ci,ti)=>{let v=vertexStack,i=vertIndex;v[i++]=x;v[i++]=y;v[i++]=ci;v[i++]=ti;vertIndex=i};const addRect=(x1,y1,x2,y2,x3,y3,x4,y4,ci,ti)=>{let v=vertexStack,i=vertIndex;v[i++]=x1;v[i++]=y1;v[i++]=ci;v[i++]=ti;v[i++]=x2;v[i++]=y2;v[i++]=ci;v[i++]=ti;v[i++]=x4;v[i++]=y4;v[i++]=ci;v[i++]=ti;v[i++]=x3;v[i++]=y3;v[i++]=ci;v[i++]=ti;vertIndex=i;drawStack.push(0,4)};const addArc=(x,y,a,b,startAngle,endAngle,n,ci,ti)=>{let angleRange=endAngle-startAngle;let angleIncrement=angleRange/n;let t=startAngle;let v=vertexStack,i=vertIndex;for(let j=0;j<=n;j++){v[i++]=x;v[i++]=y;v[i++]=ci;v[i++]=ti;let vx=x+a*Math.cos(t);let vy=y+b*Math.sin(t);v[i++]=vx;v[i++]=vy;v[i++]=ci;v[i++]=ti;t+=angleIncrement}vertIndex=i;drawStack.push(0,(n+1)*2)};const addArcStroke=(x,y,outerA,outerB,innerA,innerB,startAngle,endAngle,n,ci,ti)=>{let angleRange=endAngle-startAngle;let angleIncrement=angleRange/n;let t=startAngle;let v=vertexStack,i=vertIndex;for(let j=0;j<=n;j++){let vxOuter=x+outerA*Math.cos(t);let vyOuter=y+outerB*Math.sin(t);let vxInner=x+innerA*Math.cos(t);let vyInner=y+innerB*Math.sin(t);v[i++]=vxOuter;v[i++]=vyOuter;v[i++]=ci;v[i++]=ti;v[i++]=vxInner;v[i++]=vyInner;v[i++]=ci;v[i++]=ti;t+=angleIncrement}vertIndex=i;drawStack.push(0,(n+1)*2)};$.rectMode=x=>$._rectMode=x;$.rect=(x,y,w,h,rr=0)=>{let[l,r,t,b]=$._calcBox(x,y,w,h,$._rectMode);let ci,ti;if($._matrixDirty)$._saveMatrix();ti=$._matrixIndex;if(!rr){if($._doFill){ci=$._fill;addRect(l,t,r,t,r,b,l,b,ci,ti)}if($._doStroke){ci=$._stroke;let sw=$._strokeWeight/2;let lsw=l-sw,rsw=r+sw,tsw=t+sw,bsw=b-sw,lpsw=l+sw,rpsw=r-sw,tpsw=t-sw,bpsw=b+sw;addRect(lsw,tpsw,rsw,tpsw,rsw,tsw,lsw,tsw,ci,ti);addRect(lsw,bsw,rsw,bsw,rsw,bpsw,lsw,bpsw,ci,ti);tsw=t-sw;bsw=b+sw;addRect(lsw,tsw,lpsw,tsw,lpsw,bsw,lsw,bsw,ci,ti);addRect(rpsw,tsw,rsw,tsw,rsw,bsw,rpsw,bsw,ci,ti)}return}l+=rr;r-=rr;t-=rr;b+=rr;rr=Math.min(rr,Math.min(w,h)/2);let n=getArcSegments(rr*$._scale);let trr=t+rr,brr=b-rr,lrr=l-rr,rrr=r+rr;if($._doFill){ci=$._fill;addArc(r,b,rr,rr,-HALF_PI,0,n,ci,ti);addArc(l,b,rr,rr,-Math.PI,-HALF_PI,n,ci,ti);addArc(l,t,rr,rr,Math.PI,HALF_PI,n,ci,ti);addArc(r,t,rr,rr,0,HALF_PI,n,ci,ti);addRect(l,trr,r,trr,r,brr,l,brr,ci,ti);addRect(l,t,lrr,t,lrr,b,l,b,ci,ti);addRect(rrr,t,r,t,r,b,rrr,b,ci,ti)}if($._doStroke){ci=$._stroke;let hsw=$._hsw;let outerA=rr+hsw,outerB=rr+hsw,innerA=rr-hsw,innerB=rr-hsw;addArcStroke(r,b,outerA,outerB,innerA,innerB,-HALF_PI,0,n,ci,ti);addArcStroke(l,b,outerA,outerB,innerA,innerB,-Math.PI,-HALF_PI,n,ci,ti);addArcStroke(l,t,outerA,outerB,innerA,innerB,Math.PI,HALF_PI,n,ci,ti);addArcStroke(r,t,outerA,outerB,innerA,innerB,0,HALF_PI,n,ci,ti);let lrrMin=lrr-hsw,lrrMax=lrr+hsw,rrrMin=rrr-hsw,rrrMax=rrr+hsw,trrMin=trr-hsw,trrMax=trr+hsw,brrMin=brr-hsw,brrMax=brr+hsw;addRect(lrrMin,t,lrrMax,t,lrrMax,b,lrrMin,b,ci,ti);addRect(rrrMin,t,rrrMax,t,rrrMax,b,rrrMin,b,ci,ti);addRect(l,trrMin,r,trrMin,r,trrMax,l,trrMax,ci,ti);addRect(l,brrMin,r,brrMin,r,brrMax,l,brrMax,ci,ti)}};$.square=(x,y,s)=>$.rect(x,y,s,s);const getArcSegments=d=>d<4?6:d<6?8:d<10?10:d<16?12:d<20?14:d<22?16:d<24?18:d<28?20:d<34?22:d<42?24:d<48?26:d<56?28:d<64?30:d<72?32:d<84?34:d<96?36:d<98?38:d<113?40:d<149?44:d<199?48:d<261?52:d<353?56:d<461?60:d<585?64:d<1200?70:d<1800?80:d<2400?90:100;$._ellipseMode=Q5.CENTER;$.ellipseMode=x=>$._ellipseMode=x;$.ellipse=(x,y,w,h)=>{let n=getArcSegments(Math.max(Math.abs(w),Math.abs(h))*$._scale);let a=w/2;let b=w==h?a:h/2;if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex;if($._doFill){addArc(x,-y,a,b,0,TAU,n,$._fill,ti)}if($._doStroke){let sw=$._strokeWeight/2;addArcStroke(x,-y,a+sw,b+sw,a-sw,b-sw,0,TAU,n,$._stroke,ti)}};$.circle=(x,y,d)=>$.ellipse(x,y,d,d);$.arc=(x,y,w,h,start,stop)=>{if(start===stop)return $.ellipse(x,y,w,h);if($._angleMode){start=$.radians(start);stop=$.radians(stop)}start%=TAU;stop%=TAU;if(start<0)start+=TAU;if(stop<0)stop+=TAU;if(start>stop)stop+=TAU;if(start==stop)return $.ellipse(x,y,w,h);let a,b;if($._ellipseMode==$.CENTER){a=w/2;b=h/2}else if($._ellipseMode==$.RADIUS){a=w;b=h}else if($._ellipseMode==$.CORNER){x+=w/2;y+=h/2;a=w/2;b=h/2}else if($._ellipseMode==$.CORNERS){x=(x+w)/2;y=(y+h)/2;a=(w-x)/2;b=(h-y)/2}let ti=$._matrixIndex;if($._matrixDirty)$._saveMatrix();let n=getArcSegments(Math.max(Math.abs(w),Math.abs(h))*$._scale);if($._doFill){addArc(x,-y,a,b,start,stop,n,$._fill,ti)}if($._doStroke){let sw=$._strokeWeight;addArcStroke(x,-y,a+sw,b+sw,a-sw,b-sw,start,stop,n,$._stroke,ti)}};$.point=(x,y)=>{if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex,ci=$._stroke,sw=$._strokeWeight;if($._scaledSW<2){let[l,r,t,b]=$._calcBox(x,y,sw,sw,"corner");addRect(l,t,r,t,r,b,l,b,ci,ti)}else{let n=getArcSegments($._scaledSW);sw/=2;addArc(x,-y,sw,sw,0,TAU,n,ci,ti)}};$._strokeJoin="round";$.strokeJoin=x=>{$._strokeJoin=x};$.line=(x1,y1,x2,y2)=>{if($._matrixDirty)$._saveMatrix();let ti=$._matrixIndex,ci=$._stroke,sw=$._strokeWeight,hsw=$._hsw;let dx=x2-x1,dy=y2-y1,length=Math.hypot(dx,dy);let px=-(dy/length)*hsw,py=dx/length*hsw;addRect(x1+px,-y1-py,x1-px,-y1+py,x2-px,-y2+py,x2+px,-y2-py,ci,ti);if($._scaledSW>2&&$._strokeJoin!="none"){let n=getArcSegments($._scaledSW);addArc(x1,-y1,hsw,hsw,0,TAU,n,ci,ti);addArc(x2,-y2,hsw,hsw,0,TAU,n,ci,ti)}};let shapeVertCount;let sv=[];let curveVertices=[];$.beginShape=()=>{shapeVertCount=0;sv=[];curveVertices=[]};$.vertex=(x,y)=>{if($._matrixDirty)$._saveMatrix();sv.push(x,-y,$._fill,$._matrixIndex);shapeVertCount++};$.curveVertex=(x,y)=>{if($._matrixDirty)$._saveMatrix();curveVertices.push({x:x,y:-y})};$.endShape=close=>{if(curveVertices.length>0){let points=[...curveVertices];if(points.length<4){while(points.length<4){points.unshift(points[0]);points.push(points[points.length-1])}}for(let i=0;i<points.length-3;i++){let p0=points[i];let p1=points[i+1];let p2=points[i+2];let p3=points[i+3];for(let t=0;t<=1;t+=.1){let t2=t*t;let t3=t2*t;let x=.5*(2*p1.x+(-p0.x+p2.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*t2+(-p0.x+3*p1.x-3*p2.x+p3.x)*t3);let y=.5*(2*p1.y+(-p0.y+p2.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*t2+(-p0.y+3*p1.y-3*p2.y+p3.y)*t3);sv.push(x,y,$._fill,$._matrixIndex);shapeVertCount++}}}if(shapeVertCount<3){throw new Error("A shape must have at least 3 vertices.")}if(close){let firstIndex=0;let lastIndex=(shapeVertCount-1)*4;let firstX=sv[firstIndex];let firstY=sv[firstIndex+1];let lastX=sv[lastIndex];let lastY=sv[lastIndex+1];if(firstX!==lastX||firstY!==lastY){sv.push(firstX,firstY,sv[firstIndex+2],sv[firstIndex+3]);shapeVertCount++}}if($._doFill){if(shapeVertCount==5){addVert(sv[0],sv[1],sv[2],sv[3]);addVert(sv[4],sv[5],sv[6],sv[7]);addVert(sv[12],sv[13],sv[14],sv[15]);addVert(sv[8],sv[9],sv[10],sv[11]);drawStack.push(0,4)}else{for(let i=1;i<shapeVertCount-1;i++){let v0=0;let v1=i*4;let v2=(i+1)*4;addVert(sv[v0],sv[v0+1],sv[v0+2],sv[v0+3]);addVert(sv[v1],sv[v1+1],sv[v1+2],sv[v1+3]);addVert(sv[v2],sv[v2+1],sv[v2+2],sv[v2+3])}drawStack.push(0,(shapeVertCount-2)*3)}}if($._doStroke){let hsw=$._hsw,n=getArcSegments($._scaledSW),ti=$._matrixIndex,ogStrokeJoin=$._strokeJoin;$._strokeJoin="none";for(let i=0;i<shapeVertCount-1;i++){let v1=i*4;let v2=(i+1)*4;$.line(sv[v1],-sv[v1+1],sv[v2],-sv[v2+1]);addArc(sv[v1],sv[v1+1],hsw,hsw,0,TAU,n,$._stroke,ti)}if(close){let v1=(shapeVertCount-1)*4;let v2=0;$.line(sv[v1],-sv[v1+1],sv[v2],-sv[v2+1])}$._strokeJoin=ogStrokeJoin}shapeVertCount=0;sv=[];curveVertices=[]};$.triangle=(x1,y1,x2,y2,x3,y3)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.endShape(true)};$.quad=(x1,y1,x2,y2,x3,y3,x4,y4)=>{$.beginShape();$.vertex(x1,y1);$.vertex(x2,y2);$.vertex(x3,y3);$.vertex(x4,y4);$.endShape(true)};$.background=(r,g,b,a)=>{$.push();$.resetMatrix();if(r.canvas){let img=r;$._imageMode="corner";$.image(img,-c.hw,-c.hh,c.w,c.h)}else{$._rectMode="corner";$.fill(r,g,b,a);$._doStroke=false;$.rect(-c.hw,-c.hh,c.w,c.h)}$.pop()};$._hooks.preRender.push((()=>{$.pass.setPipeline($._pipelines[0]);let vertexBuffer=Q5.device.createBuffer({size:vertIndex*4,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0,vertIndex));vertexBuffer.unmap();$.pass.setVertexBuffer(0,vertexBuffer)}));$._hooks.postRender.push((()=>{drawStack=$.drawStack;vertIndex=0}))};Q5.renderers.webgpu.image=($,q)=>{let vertexStack=new Float32Array(1e7),vertIndex=0;let imageShader=Q5.device.createShaderModule({label:"imageShader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@location(0) pos: vec2f,\n\t@location(1) texCoord: vec2f,\n\t@location(2) tintIndex: f32,\n\t@location(3) matrixIndex: f32,\n\t@location(4) globalAlpha: f32\n}\nstruct FragmentParams {\n\t@builtin(position) position: vec4f,\n\t@location(0) texCoord: vec2f,\n\t@location(1) tintIndex: f32,\n\t@location(2) globalAlpha: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@group(1) @binding(0) var samp: sampler;\n@group(1) @binding(1) var texture: texture_2d<f32>;\n\n@vertex\nfn vertexMain(v: VertexParams) -> FragmentParams {\n\tvar vert = vec4f(v.pos, 0.0, 1.0);\n\tvert = transforms[i32(v.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f: FragmentParams;\n\tf.position = vert;\n\tf.texCoord = v.texCoord;\n\tf.tintIndex = v.tintIndex;\n\tf.globalAlpha = v.globalAlpha;\n\treturn f;\n}\n\n@fragment\nfn fragmentMain(f: FragmentParams) -> @location(0) vec4f {\n\t\tlet texColor = textureSample(texture, samp, f.texCoord);\n\t\tlet tintColor = colors[i32(f.tintIndex)];\n\t\t\n\t\t// Mix original and tinted colors using tint alpha as blend factor\n\t\tlet tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);\n\t\treturn mix(texColor, tinted, tintColor.a);\n}\n\t`});$._textureBindGroups=[];let textureLayout=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 vertexBufferLayout={arrayStride:28,attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"},{shaderLocation:2,offset:16,format:"float32"},{shaderLocation:3,offset:20,format:"float32"},{shaderLocation:4,offset:24,format:"float32"}]};const pipelineLayout=Q5.device.createPipelineLayout({label:"imagePipelineLayout",bindGroupLayouts:[...$.bindGroupLayouts,textureLayout]});$._pipelineConfigs[1]={label:"imagePipeline",layout:pipelineLayout,vertex:{module:imageShader,entryPoint:"vertexMain",buffers:[{arrayStride:0,attributes:[]},vertexBufferLayout]},fragment:{module:imageShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[1]=Q5.device.createRenderPipeline($._pipelineConfigs[1]);let sampler;let makeSampler=filter=>{sampler=Q5.device.createSampler({magFilter:filter,minFilter:filter})};$.smooth=()=>makeSampler("linear");$.noSmooth=()=>makeSampler("nearest");$.smooth();let MAX_TEXTURES=12e3;$._textures=[];let tIdx=0;$._createTexture=img=>{if(img.canvas)img=img.canvas;let textureSize=[img.width,img.height,1];let texture=Q5.device.createTexture({size:textureSize,format:"bgra8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:img},{texture:texture,colorSpace:$.canvas.colorSpace},textureSize);$._textures[tIdx]=texture;img.textureIndex=tIdx;const textureBindGroup=Q5.device.createBindGroup({layout:textureLayout,entries:[{binding:0,resource:sampler},{binding:1,resource:texture.createView()}]});$._textureBindGroups[tIdx]=textureBindGroup;tIdx=(tIdx+1)%MAX_TEXTURES;if($._textures[tIdx]){$._textures[tIdx].destroy();delete $._textures[tIdx];delete $._textureBindGroups[tIdx]}};$.loadImage=(src,cb)=>{q._preloadCount++;let g=$._g.loadImage(src,(img=>{g.defaultWidth=img.width*$._defaultImageScale;g.defaultHeight=img.height*$._defaultImageScale;$._createTexture(img);q._preloadCount--;if(cb)cb(g)}));return g};$.imageMode=x=>$._imageMode=x;const addVert=(x,y,u,v,ci,ti,ga)=>{let s=vertexStack,i=vertIndex;s[i++]=x;s[i++]=y;s[i++]=u;s[i++]=v;s[i++]=ci;s[i++]=ti;s[i++]=ga;vertIndex=i};$.image=(img,dx=0,dy=0,dw,dh,sx=0,sy=0,sw,sh)=>{let g=img;if(img.canvas)img=img.canvas;if(img.textureIndex==undefined)return;if($._matrixDirty)$._saveMatrix();let w=img.width,h=img.height,pd=g._pixelDensity||1;dw??=g.defaultWidth;dh??=g.defaultHeight;sw??=w;sh??=h;dw*=pd;dh*=pd;let[l,r,t,b]=$._calcBox(dx,dy,dw,dh,$._imageMode);let u0=sx/w,v0=sy/h,u1=(sx+sw)/w,v1=(sy+sh)/h,ti=$._matrixIndex,ci=$._tint,ga=$._globalAlpha;addVert(l,t,u0,v0,ci,ti,ga);addVert(r,t,u1,v0,ci,ti,ga);addVert(l,b,u0,v1,ci,ti,ga);addVert(r,b,u1,v1,ci,ti,ga);$.drawStack.push(1,img.textureIndex)};$._hooks.preRender.push((()=>{if(!$._textureBindGroups.length)return;$.pass.setPipeline($._pipelines[1]);let vertexBuffer=Q5.device.createBuffer({size:vertIndex*5,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0,vertIndex));vertexBuffer.unmap();$.pass.setVertexBuffer(1,vertexBuffer)}));$._hooks.postRender.push((()=>{vertIndex=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=($,q)=>{let textShader=Q5.device.createShaderModule({label:"MSDF text shader",code:`\nstruct Uniforms {\n\thalfWidth: f32,\n\thalfHeight: f32\n}\nstruct VertexParams {\n\t@builtin(vertex_index) vertex : u32,\n\t@builtin(instance_index) instance : u32\n}\nstruct FragmentParams {\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\tmatrixIndex: f32,\n\tfillIndex: f32,\n\tstrokeIndex: f32\n}\n\n@group(0) @binding(0) var<uniform> uniforms: Uniforms;\n@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;\n@group(0) @binding(2) var<storage> colors : array<vec4f>;\n\n@group(1) @binding(0) var fontTexture: texture_2d<f32>;\n@group(1) @binding(1) var fontSampler: sampler;\n@group(1) @binding(2) var<storage> fontChars: array<Char>;\n\n@group(2) @binding(0) var<storage> textChars: array<vec4f>;\n@group(2) @binding(1) var<storage> textMetadata: array<Text>;\n\nconst quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));\n\n@vertex\nfn vertexMain(v : VertexParams) -> FragmentParams {\n\tlet char = textChars[v.instance];\n\n\tlet text = textMetadata[i32(char.w)];\n\n\tlet fontChar = fontChars[i32(char.z)];\n\n\tlet charPos = ((quad[v.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.matrixIndex)] * vert;\n\tvert.x /= uniforms.halfWidth;\n\tvert.y /= uniforms.halfHeight;\n\n\tvar f : FragmentParams;\n\tf.position = vert;\n\tf.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;\n\tf.fillColor = colors[i32(text.fillIndex)];\n\treturn f;\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(f : FragmentParams) -> @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(f.texCoord.x), dpdyFine(f.texCoord.x)));\n\tlet dy = sz.y*length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));\n\tlet toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);\n\tlet sigDist = sampleMsdf(f.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(f.fillColor.rgb, f.fillColor.a * alpha);\n}\n`});let textBindGroupLayout=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"}}]});let fontSampler=Q5.device.createSampler({minFilter:"linear",magFilter:"linear",mipmapFilter:"linear",maxAnisotropy:16});let fontBindGroupLayout=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"}}]});let fontPipelineLayout=Q5.device.createPipelineLayout({bindGroupLayouts:[...$.bindGroupLayouts,fontBindGroupLayout,textBindGroupLayout]});$._pipelineConfigs[2]={label:"msdf font pipeline",layout:fontPipelineLayout,vertex:{module:textShader,entryPoint:"vertexMain"},fragment:{module:textShader,entryPoint:"fragmentMain",targets:[{format:"bgra8unorm",blend:$.blendConfigs.normal}]},primitive:{topology:"triangle-strip",stripIndexFormat:"uint32"},multisample:{count:4}};$._pipelines[2]=Q5.device.createRenderPipeline($._pipelineConfigs[2]);class MsdfFont{constructor(bindGroup,lineHeight,chars,kernings){this.bindGroup=bindGroup;this.lineHeight=lineHeight;this.chars=chars;this.kernings=kernings;let charArray=Object.values(chars);this.charCount=charArray.length;this.defaultChar=charArray[0]}getChar(charCode){return this.chars[charCode]??this.defaultChar}getXAdvance(charCode,nextCharCode=-1){let char=this.getChar(charCode);if(nextCharCode>=0){let kerning=this.kernings.get(charCode);if(kerning){return char.xadvance+(kerning.get(nextCharCode)??0)}}return char.xadvance}}$._fonts=[];let fonts={};let createFont=async(fontJsonUrl,fontName,cb)=>{q._preloadCount++;let res=await fetch(fontJsonUrl);if(res.status==404){q._preloadCount--;return""}let atlas=await res.json();let slashIdx=fontJsonUrl.lastIndexOf("/");let baseUrl=slashIdx!=-1?fontJsonUrl.substring(0,slashIdx+1):"";res=await fetch(baseUrl+atlas.pages[0]);let img=await createImageBitmap(await res.blob());let imgSize=[img.width,img.height,1];let texture=Q5.device.createTexture({label:`MSDF ${fontName}`,size:imgSize,format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});Q5.device.queue.copyExternalImageToTexture({source:img},{texture:texture},imgSize);if(typeof atlas.chars=="string"){atlas.chars=$.CSV.parse(atlas.chars," ");atlas.kernings=$.CSV.parse(atlas.kernings," ")}let charCount=atlas.chars.length;let charsBuffer=Q5.device.createBuffer({size:charCount*32,usage:GPUBufferUsage.STORAGE,mappedAtCreation:true});let fontChars=new Float32Array(charsBuffer.getMappedRange());let u=1/atlas.common.scaleW;let v=1/atlas.common.scaleH;let chars={};let o=0;for(let[i,char]of atlas.chars.entries()){chars[char.id]=char;chars[char.id].charIndex=i;fontChars[o]=char.x*u;fontChars[o+1]=char.y*v;fontChars[o+2]=char.width*u;fontChars[o+3]=char.height*v;fontChars[o+4]=char.width;fontChars[o+5]=char.height;fontChars[o+6]=char.xoffset;fontChars[o+7]=-char.yoffset;o+=8}charsBuffer.unmap();let fontBindGroup=Q5.device.createBindGroup({label:"msdf font bind group",layout:fontBindGroupLayout,entries:[{binding:0,resource:texture.createView()},{binding:1,resource:fontSampler},{binding:2,resource:{buffer:charsBuffer}}]});let kernings=new Map;if(atlas.kernings){for(let kerning of atlas.kernings){let charKerning=kernings.get(kerning.first);if(!charKerning){charKerning=new Map;kernings.set(kerning.first,charKerning)}charKerning.set(kerning.second,kerning.amount)}}$._font=new MsdfFont(fontBindGroup,atlas.common.lineHeight,chars,kernings);$._font.index=$._fonts.length;$._fonts.push($._font);fonts[fontName]=$._font;q._preloadCount--;if(cb)cb(fontName)};$._g.colorMode($.RGB,1);$.loadFont=(url,cb)=>{let ext=url.slice(url.lastIndexOf(".")+1);if(ext!="json")return $._g.loadFont(url,cb);let fontName=url.slice(url.lastIndexOf("/")+1,url.lastIndexOf("-"));let f={family:fontName};f._loader=createFont(url,fontName,(()=>{delete f._loader;if(cb)cb(f)}));if($._disablePreload)return f._loader;return f};$._loadDefaultFont=fontName=>{fonts[fontName]=null;if(navigator.onLine){$.loadFont(`https://q5js.org/fonts/${fontName}-msdf.json`)}else{$.loadFont(`/node_modules/q5/builtinFonts/${fontName}-msdf.json`)}};$._textSize=18;$._textAlign="left";$._textBaseline="alphabetic";let leadingSet=false,leading=22.5,leadDiff=4.5,leadPercent=1.25;$.textFont=fontName=>{if(typeof fontName!="string")fontName=fontName.family;let font=fonts[fontName];if(font)$._font=font;else if(font===undefined)$._loadDefaultFont(fontName)};$.textSize=size=>{$._textSize=size;if(!leadingSet){leading=size*leadPercent;leadDiff=leading-size}};$.textLeading=lineHeight=>{$._font.lineHeight=leading=lineHeight;leadDiff=leading-$._textSize;leadPercent=leading/$._textSize;leadingSet=true};$.textAlign=(horiz,vert)=>{$._textAlign=horiz;if(vert)$._textBaseline=vert};let charStack=[],textStack=[];let measureText=(font,text,charCallback)=>{let maxWidth=0,offsetX=0,offsetY=0,line=0,printedCharCount=0,lineWidths=[],nextCharCode=text.charCodeAt(0);for(let i=0;i<text.length;++i){let charCode=nextCharCode;nextCharCode=i<text.length-1?text.charCodeAt(i+1):-1;switch(charCode){case 10:lineWidths.push(offsetX);line++;maxWidth=Math.max(maxWidth,offsetX);offsetX=0;offsetY-=font.lineHeight*leadPercent;break;case 13:break;case 32:offsetX+=font.getXAdvance(charCode);break;case 9:offsetX+=font.getXAdvance(charCode)*2;break;default:if(charCallback){charCallback(offsetX,offsetY,line,font.getChar(charCode))}offsetX+=font.getXAdvance(charCode,nextCharCode);printedCharCount++}}lineWidths.push(offsetX);maxWidth=Math.max(maxWidth,offsetX);return{width:maxWidth,height:lineWidths.length*font.lineHeight*leadPercent,lineWidths:lineWidths,printedCharCount:printedCharCount}};$.text=(str,x,y,w,h)=>{if(!$._font){if($._font!==null)$.textFont("sans-serif");return}let type=typeof str;if(type!="string"){if(type=="object")str=str.toString();else str=str+""}if(str.length>w){let wrapped=[];let i=0;while(i<str.length&&wrapped.length<h){let max=i+w;if(max>=str.length){wrapped.push(str.slice(i));break}let end=str.lastIndexOf(" ",max);if(end==-1||end<i)end=max;wrapped.push(str.slice(i,end));i=end+1}str=wrapped.join("\n")}let spaces=0,hasNewline;for(let i=0;i<str.length;i++){let c=str[i];switch(c){case"\n":hasNewline=true;case"\r":case"\t":case" ":spaces++}}let charsData=[];let ta=$._textAlign,tb=$._textBaseline,textIndex=textStack.length,o=0,measurements;if(ta=="left"&&!hasNewline){measurements=measureText($._font,str,((textX,textY,line,char)=>{charsData[o]=textX;charsData[o+1]=textY;charsData[o+2]=char.charIndex;charsData[o+3]=textIndex;o+=4}));if(tb=="alphabetic")y-=$._textSize;else if(tb=="center")y-=$._textSize*.5;else if(tb=="bottom")y-=leading}else{measurements=measureText($._font,str);let offsetY=0;if(tb=="alphabetic")y-=$._textSize;else if(tb=="center")offsetY=measurements.height*.5;else if(tb=="bottom")offsetY=measurements.height;measureText($._font,str,((textX,textY,line,char)=>{let offsetX=0;if(ta=="center"){offsetX=measurements.width*-.5-(measurements.width-measurements.lineWidths[line])*-.5}else if(ta=="right"){offsetX=-measurements.lineWidths[line]}charsData[o]=textX+offsetX;charsData[o+1]=textY+offsetY;charsData[o+2]=char.charIndex;charsData[o+3]=textIndex;o+=4}))}charStack.push(charsData);let txt=[];if($._matrixDirty)$._saveMatrix();txt[0]=x;txt[1]=-y;txt[2]=$._textSize/44;txt[3]=$._matrixIndex;txt[4]=$._fillSet?$._fill:0;txt[5]=$._stroke;textStack.push(txt);$.drawStack.push(2,measurements.printedCharCount,$._font.index)};$.textWidth=str=>{if(!$._font)return 0;return measureText($._font,str).width};$.createTextImage=(str,w,h)=>{$._g.textSize($._textSize);if($._doFill){let fi=$._fill*4;$._g.fill(colorStack.slice(fi,fi+4))}if($._doStroke){let si=$._stroke*4;$._g.stroke(colorStack.slice(si,si+4))}let img=$._g.createTextImage(str,w,h);if(img.canvas.textureIndex==undefined){$._createTexture(img)}else if(img.modified){let cnv=img.canvas;let textureSize=[cnv.width,cnv.height,1];let texture=$._textures[cnv.textureIndex];Q5.device.queue.copyExternalImageToTexture({source:cnv},{texture:texture,colorSpace:$.canvas.colorSpace},textureSize);img.modified=false}return img};$.textImage=(img,x,y)=>{if(typeof img=="string")img=$.createTextImage(img);let og=$._imageMode;$._imageMode="corner";let ta=$._textAlign;if(ta=="center")x-=img.canvas.hw;else if(ta=="right")x-=img.width;let bl=$._textBaseline;if(bl=="alphabetic")y-=img._leading;else if(bl=="center")y-=img._middle;else if(bl=="bottom")y-=img._bottom;else if(bl=="top")y-=img._top;$.image(img,x,y);$._imageMode=og};$._hooks.preRender.push((()=>{if(!charStack.length)return;let totalTextSize=0;for(let charsData of charStack){totalTextSize+=charsData.length*4}let charBuffer=Q5.device.createBuffer({size:totalTextSize,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(charBuffer.getMappedRange()).set(charStack.flat());charBuffer.unmap();let totalMetadataSize=textStack.length*6*4;let textBuffer=Q5.device.createBuffer({label:"textBuffer",size:totalMetadataSize,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST,mappedAtCreation:true});new Float32Array(textBuffer.getMappedRange()).set(textStack.flat());textBuffer.unmap();$._textBindGroup=Q5.device.createBindGroup({label:"msdf text bind group",layout:textBindGroupLayout,entries:[{binding:0,resource:{buffer:charBuffer}},{binding:1,resource:{buffer:textBuffer}}]})}));$._hooks.postRender.push((()=>{charStack=[];textStack=[]}))};