diff --git a/README.md b/README.md index 1788b5a..3d98dca 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ # -q5.js is a drop-in replacement for [p5.js][]. It supports all of p5's 2D drawing APIs, math functionality, and some other utilities. +**q5.js** implements all of [p5][]'s 2D drawing, math, and user input functionality. -q5.min.js (42kb) is 23x smaller than p5.min.js (977kb)! It also has better performance, which is especially important on mobile devices. +It's a drop-in replacement that's performance optimized and 23x smaller than p5. q5 even has a few exclusive features: top-level global mode, HDR color support, namespace mode, and text image caching. -q5 doesn't include any friendly error messages to help you code though. Its mainly for people who are already familiar with p5.js or JS programming in general. If you're a beginner, stick with p5 while developing a sketch, then use q5 to share your work. +But q5 doesn't include any friendly error messages, so its mainly for people who are already familiar with p5.js or JS programming in general. If you're a beginner, stick with p5 while developing a sketch, then use q5 to share your work. ## Usage -q5 should work with your existing p5.js sketches, no modifications required! If you have any problems though, please [make an issue report.][] +q5 should work with your existing p5.js sketches, no modifications required! If you have any problems though, please [make an issue report][]. -Try out the [q5.js template sketch](https://editor.p5js.org/quinton-ashley/sketches/8SEtLEDl9) for the online p5.js Web Editor. - -Or you can use q5.js in your own project by adding this line to your HTML file: +Use q5.js in your own project by adding this line to your HTML file: ```html @@ -24,6 +22,8 @@ q5 is also available on [npm](https://www.npmjs.com/package/q5)! npm install q5 ``` +Or try out the [q5.js template sketch](https://editor.p5js.org/quinton-ashley/sketches/8SEtLEDl9) for the online p5.js Web Editor. + ## Support this project 🤝 q5 is open source and [multi-licensed](https://github.com/quinton-ashley/p5play-web/blob/main/LICENSING.md). Anyone can use q5 for free under the terms of the AGPLv3. 🎉 @@ -34,7 +34,7 @@ If you can't afford to pay, you can apply for the free [p5play Novice License](h ## Using p5 Addon Libraries -q5.js is compatible with popular p5 addons and projects that use p5, such as p5play, because it aliases `Q5` to `p5`. +q5.js is compatible with popular p5 addons and projects that use p5, such as [p5play][], because it aliases `Q5` to `p5`. To use addons, simply load them after q5.js: @@ -47,11 +47,11 @@ To use addons, simply load them after q5.js: ## New Features: Top-Level Global Mode -There are some extra features in q5 that aren't in p5, but using them is totally optional. +> q5.js includes some exclusive features that aren't available in p5.js. Using them is optional! -**q5.js** has an automatic global mode, which is enabled by default. This means existing p5.js sketches can be run without any modification. +In **p5**, p5 functions can't be used on the file level. Also you must declare a `setup` or `draw` function on the file level for p5 to start running in global mode. -But with q5, you could do away with the preload and setup functions all together. Just write the initialization routine `new Q5()` or `new Q5('global')` at the top of your sketch. +**q5** can automatically run in global mode as well, so existing sketches don't require any modification. But if you initialize Q5 at the top of your sketch, the `preload` and `setup` functions become optional. ```js new Q5(); @@ -62,82 +62,86 @@ fill(c); rect(15, 15, 35, 70); ``` -You could even use your own animation loop in place of `draw()`. But this would cause problems with addons that rely on `draw()`, such as p5play. - -```js -new Q5(); - -fill(255, 0, 0); +This is great because you don't have to declare variables on the file level and then define them in `preload` or `setup`. You can declare and define them at the same time! -function myLoop() { - requestAnimationFrame(myLoop); - rect(15, 15, 35, 70); -} -myLoop(); -``` +## New Features: HDR Color Support -## New Features: Namespace Mode +Most modern devices support the "display-p3" HDR color space. If a device doesn't support it, q5 will fall back to "srgb". -In **p5.js**, all p5 functions are in the global namespace, unless you use "instance" mode, like this: +**q5** now supports the [oklch](https://oklch.com/#63.65,0.2872,16.57,100) color format which is capable of representing HDR colors. ```js -let sketch = function (p) { - p.setup = function () { - p.createCanvas(100, 100); - }; - p.draw = function () { - p.background(0); - }; -}; +colorMode('oklch'); -let myp5 = new p5(sketch); +// (lightness, chroma, hue, alpha) +let c = color(0.637, 0.287, 16.57, 1); ``` -This does solve the problem of global namespace pollution, but there're still some inconveniences: +Support for the HSV color format was removed in q5 v1.9.3 because color experts thought HSV was flawed, outdated, and ought to be abandoned way back in 1997! -- The extra wrapping of the `sketch` function makes code look complex. (Flat is better than nested!) -- Variables inside `sketch` can no longer be accessed via browser console, which makes it less convenient for debugging. +The `color` function does accept strings but only hex strings in "#RRGGBB" or "#RRGGBBAA" format. It also does not accept percentages so you'll have to convert those to decimal values. -**q5** introduces "namespace" mode, in addition to global/instance modes: +`colorMode` accepts 'rgb', 'oklch', or 'srgb'. The default mode is 'rgb', which upgrades rgb colors to HDR on supported displays. Specifying 'srgb' enables sRGB gamut correction for rgb colors on HDR displays. -```js -let q5 = new Q5('namespace'); +## New Features: Customize Canvas Context Attributes -q5.setup = function () { - q5.createCanvas(100, 100); -}; +In **p5**, you're stuck with the default [canvas context attributes][], which can't be changed. So the canvas must have an alpha layer, even if you don't need one. p5 also doesn't support HDR color spaces or desynchronized rendering. -q5.draw = function () { - q5.background(0); +But **q5** has its own defaults: + +```js +Q5.canvasOptions = { + alpha: false, + desynchronized: true, + colorSpace: 'display-p3' }; ``` -You can call the namespace whatever you like. You can even get multiple instances of q5 running on the same page easily. +The `Q5.canvasOptions` object can be overridden, which will effect all q5 instances.You can also override any of these defaults by passing an options object as the fourth parameter to the `createCanvas()` function: ```js -let q5 = new Q5('namespace'); -let q6 = new Q5('namespace'); +createCanvas(400, 400, '2d', { + alpha: true +}); +``` -q5.setup = function () { - q5.createCanvas(400, 400); -}; +## New Features: Namespace Mode -q5.draw = function () { - q5.background(100); -}; +**p5**'s [instance mode][] enables multiple sketches to run on one page. To avoid needing to preface every p5 function with `p.` you can use a JS [with statement][]. -q6.setup = function () { - q6.createCanvas(400, 400); +```js +let sketch = (p) => { + with (p) { + p.setup = () => { + createCanvas(400, 400); + }; + p.draw = () => { + background(100); + }; + } }; -q6.draw = function () { - q6.background(200); -}; +let myp5 = new p5(sketch); +``` + +**q5** introduces "namespace" mode, in addition to the global and instance modes. You can call the namespace variable whatever you like. + +```js +let q = new Q5('namespace'); + +with (q) { + q.setup = () => { + createCanvas(400, 400); + }; + q.draw = () => { + background(100); + }; +} ``` ## Motivation: Part 1 -_This section was written by @LingDong-, co-creator of q5_ +> This section was written by @LingDong-, co-creator of q5. After having used many graphics libraries across many different languages, I have found that the Processing/p5.js/Openframeworks system has one huge advantage over others: @@ -153,55 +157,66 @@ In fact, its not uncommon for successful software systems to have multiple imple ## Motivation: Part 2 -_This section was written by @quinton-ashley, co-creator of q5_ +> This section was written by @quinton-ashley, co-creator of q5. I thought @LingDong-'s work on q5 and the idea itself had great potential. So I decided to upgrade its compatibility with p5.js. My main goal was to make it work with [p5play](https://p5play.org)! An increase in performance of even a few frames per second can make a significant difference in the user experience of a work of interactive art or a game, especially on mobile devices. -I was also interested in working on q5 because for a lot of p5.js users, the library itself is a black box. Even as an expert JS programmer and someone who teaches CS for a living, I still find myself scratching my head when I look at the p5.js source code. p5 was initially released 10 years ago and I think some bad design choices were made due to JS limitations at the time. It's also become an absolutely massive library, with literally over 100,000 lines of code and documentation! p5.js is 4.3 MB un-minified, q5.js is just 70kb. +I was also interested in working on q5 because for a lot of p5.js users, the library itself is a black box. Even as an expert JS programmer and someone who teaches CS for a living, I still find myself scratching my head when I look at the p5.js source code. p5 was initially released 10 years ago and I think some bad design choices were made due to JS limitations at the time. It's also become an absolutely massive library, with literally over 100,000 lines of code and documentation! p5.js is 4.3 MB un-minified, q5.js is under 70kb. I think it'd be better if the canvas mode, webgl mode, Friendly Error System, and accessibility features of p5 were offered in separate files. Yet, the powers that be at the Processing Foundation have made it clear that they don't want to do that. Instead they insist on adding more accessibility features to the base library, which the majority of people just don't need. So q5 is a good alternative that trims out the fat. Thanks in large part to @LingDong-'s design, q5 is well organized, concise, and utilizes many modern JS features! I think even without documentation, the source code is easier for experienced JS programmers to comprehend. -## More extra features +## More exclusive features q5.js provides some other features that are not in p5.js: - `textCache(true)`: Text image caching is enabled by default. Rotated text is only rendered once, and then cached as an image. This can result in ridiculously high 90x performance boosts for text-heavy sketches. Users don't need to change their code, the `text` function can be used as normal, q5 takes care of everything behind the scenes. - `loadSound()`: Basic sound support in q5.js, returns a Web Audio object. Not as powerful as p5.sound, but it's good enough for simple sketches. Includes `sound.setVolume()`. +- `ctx`: an alias for `drawingContext` - `randomExponential()` in addition to `randomGaussian()`: a random distribution that resembles exponential decay. - `curveAlpha()`: manipulate the `α` parameter of Catmull-Rom curves. - `relRotationX`, `relRotationY` and `relRotationZ`: Similar to `rotationX/Y/Z`, but are relative to the orientation of the mobile device. ## Cutting room floor -**p5.js** has some pretty extensive parsing capabilities. For example, it can parse out a color from strings like `color('hsl(160, 100%, 50%)')` or `color("lightblue")`. Functions behave sightly differently when under different "modes" (e.g. `hue`), and some have secret default settings (e.g. `arc` and `text`). +**p5.js** has some pretty extensive parsing capabilities. For example, it can parse out a color from strings like `color('hsl(160, 100%, 50%)')`. Functions behave sightly differently when under different "modes" and some have secret default settings, such as `arc` and `text`. -**q5.js** will only do things when you communicate the command in the simplest way. This means that functions mainly just take numeric inputs. Any behavior needs to be explicitly triggered. q5 has almost no overhead between digesting your parameters and putting them into use. +**q5.js** will only do things when you communicate the command in the simplest way. This means that functions mainly just take numeric inputs. q5 has almost no overhead between digesting your parameters and putting them into use. -## Known Issues +## Size Comparison -- `curveTightness()` sets the 'alpha' parameter of Catmull-Rom curve, and is NOT identical to p5.js counterpart. As this might change in the future, please call `curveAlpha()` directly. +Unminified: -## Size Comparison +- p5.js **4300kb** ⚠️ +- p5.sound.js 488kb +- q5.js 66kb -- p5.min.js 977kb +Minified: + +- p5.min.js 1000kb - p5.sound.min.js 200kb +- q5.min.js **42kb** 🎉 -- q5.min.js 42kb +## Benchmarks -- planck.min.js 209kb -- p5play.min.js 93kb +q5.js has a significant speed advantage in imaging operations because it uses hardware accelerated Canvas APIs whenever possible, instead of going pixel by pixel. Most other functionalities have very marginal speed improvements (or none at all when parameter validation overhead is negligible). The operations with important performance differences are listed below. -## Benchmarks +The following benchmarks are generated with Google Chrome 120, on a MacBook Air M1 2020. q5.js v1.9.3 vs p5.js v1.9.0. -q5.js has significant speed advantage in imaging operations because it uses hardware accelerated Canvas APIs directly whenever possible, instead of going over pixel by pixel. Most other functionalities have very marginal speed improvements (or none at all when parameter validation overhead is negligible). The operations with important performance differences are listed below. +Less time (milliseconds) is better. -The following benchmarks are generated with Google Chrome 84, on an old-ish MacBook Pro 2015 (with lots of apps and tabs running); Performance varies depending on software and hardware. +| Task | p5.js | q5.js | +| -------------------------------------------------- | ----- | ----- | +| Generate 100,000 random colors with `color(r,g,b)` | 168ms | 12ms | -p5.js version used is **1.1.9**. +## Older Benchmarks + +The following benchmarks are generated with Google Chrome 84, on an old-ish MacBook Pro 2015 (with lots of apps and tabs running); Performance varies depending on software and hardware. p5.js version used is v1.1.9. + +Higher FPS (frames per second) is better. | Operation on 1024x1024 image | p5.js | q5.js | | ---------------------------- | ----- | -------- | @@ -213,7 +228,7 @@ p5.js version used is **1.1.9**. | opaque | 20FPS | 60FPS | | erode/dilate | 5FPS | 9FPS | -| Misc | p5.js | q5.js | +| Task | p5.js | q5.js | | --------------------------------------------------- | ----- | ----- | | Generating 10,000 `randomGaussian()` sample | 10FPS | 20FPS | | Calling `noiseSeed()` 1,000 times | 10FPS | 60FPS | @@ -222,7 +237,27 @@ p5.js version used is **1.1.9**. \* Only for browsers that support CanvasRenderingContext2D.filter ([75% of all](https://caniuse.com/#feat=mdn-api_canvasrenderingcontext2d_filter) as of Aug 2020, including Chrome, Firefox and Edge). For those that don't, performance is similar to p5.js, as identical implementations are usually used as fallbacks. +## Contributing + Speed is a goal for q5.js, and we would very much like to see the above list grow. If you know how to make something faster, advice/pull requests are very welcome! -[p5.js]: https://p5js.org -[make an issue report.]: https://github.com/quinton-ashley/q5.js/issues +## Credits + +catmullRomSpline: +https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline + +ziggurat: +http://ziggurat.glitch.me/ + +random: +https://github.com/processing/p5.js/blob/1.1.9/src/math/noise.js + +Curve query: +https://github.com/processing/p5.js/blob/1.1.9/src/core/shape/curves.js + +[p5]: https://p5js.org +[p5play]: https://p5play.org +[instance mode]: https://p5js.org/examples/instance-mode-instantiation.html +[with statement]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with +[make an issue report]: https://github.com/quinton-ashley/q5.js/issues +[context attributes]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext#contextattributes diff --git a/package.json b/package.json index 6519282..1707688 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "quinton-ashley", "name": "q5", - "version": "1.9.2", + "version": "1.9.3", "description": "An implementation of the p5.js 2D API that's smaller and faster", "main": "q5.js", "scripts": { diff --git a/q5.js b/q5.js index 88d762a..4e28541 100644 --- a/q5.js +++ b/q5.js @@ -10,26 +10,23 @@ */ function Q5(scope, parent) { let preloadCnt = 0; - if (typeof scope == 'undefined') { + if (!scope) { scope = 'global'; preloadCnt++; setTimeout(() => preloadCnt--, 32); } if (scope == 'auto') { - if (typeof window.setup == 'undefined') return; + if (typeof window != 'object' || !(window.setup || window.draw)) return; else scope = 'global'; } - if (arguments.length == 1 && typeof scope != 'string' && typeof scope != 'function') { - parent = arguments[0]; - scope = null; - } if (scope == 'global') Q5._hasGlobal = true; + // CANVAS + let $ = this; $.canvas = document.createElement('canvas'); - let ctx = ($._ctx = $.canvas.getContext('2d')); - $.canvas.classList.add('p5Canvas', 'q5Canvas'); $.canvas.id = 'defaultCanvas' + Q5._instanceCount++; + $.canvas.classList.add('p5Canvas', 'q5Canvas'); $.width = 100; $.height = 100; @@ -42,6 +39,9 @@ function Q5(scope, parent) { $._resize = () => { if ($.frameCount > 1) $._shouldResize = true; }; + if (parent && typeof parent == 'string') { + parent = document.getElementById(parent); + } $.canvas.parent = (el) => { if (typeof el == 'string') el = document.getElementById(el); el.append($.canvas); @@ -63,21 +63,29 @@ function Q5(scope, parent) { $.canvas.parent(parent); } if (document.body) appendCanvas(); - else window.addEventListener('load', appendCanvas); + else document.addEventListener('DOMContentLoaded', appendCanvas); } - defaultStyle(); - - $.MAGIC = 0x9a0ce55; + $._q5 = true; $.pixels = []; let imgData = null; + let ctx; - $.createCanvas = function (width, height) { + $.createCanvas = function (width, height, renderer, options) { + if (renderer == 'webgl') throw `webgl renderer is not supported in q5, use '2d'`; $.width = width; $.height = height; $.canvas.width = width; $.canvas.height = height; + $.canvas.renderer = '2d'; + let opt = Object.assign({}, Q5.canvasOptions); + if (options) Object.assign(opt, options); + + ctx = $.ctx = $.drawingContext = $.canvas.getContext('2d', opt); + if (scope == 'global') window.ctx = window.drawingContext = ctx; + Object.assign($.canvas, opt); defaultStyle(); + ctx.save(); if (scope != 'image') { let pd = $.displayDensity(); if (scope == 'graphics') pd = this._pixelDensity; @@ -87,9 +95,7 @@ function Q5(scope, parent) { }; $._createCanvas = $.createCanvas; - //================================================================ // IMAGE - //================================================================ $.loadPixels = () => { imgData = ctx.getImageData(0, 0, $.canvas.width, $.canvas.height); @@ -189,7 +195,6 @@ function Q5(scope, parent) { } } }; - filterImpl[$.BLUR] = (data, rad) => { rad = rad || 1; rad = Math.floor(rad * $._pixelDensity); @@ -268,6 +273,7 @@ function Q5(scope, parent) { tmpCtx.canvas.height = h; } } + function makeTmpCt2(w, h) { if (tmpCt2 == null) { tmpCt2 = document.createElement('canvas').getContext('2d'); @@ -298,38 +304,38 @@ function Q5(scope, parent) { ctx.restore(); } + function softFilter(typ, x) { + let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); + filterImpl[typ](imgData.data, x); + ctx.putImageData(imgData, 0, 0); + } + $.filter = (typ, x) => { - let support = $.HARDWARE_FILTERS && ctx.filter != undefined; - if (support) { - makeTmpCtx(); - if (typ == $.THRESHOLD) { - x ??= 0.5; - x = Math.max(x, 0.00001); - let b = Math.floor((0.5 / x) * 100); - nativeFilter(`saturate(0%) brightness(${b}%) contrast(1000000%)`); - } else if (typ == $.GRAY) { - nativeFilter(`saturate(0%)`); - } else if (typ == $.OPAQUE) { - tmpCtx.fillStyle = 'black'; - tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); - tmpCtx.drawImage(ctx.canvas, 0, 0); - ctx.save(); - ctx.resetTransform(); - ctx.drawImage(tmpCtx.canvas, 0, 0); - ctx.restore(); - } else if (typ == $.INVERT) { - nativeFilter(`invert(100%)`); - } else if (typ == $.BLUR) { - nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`); - } else { - let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - filterImpl[typ](imgData.data, x); - ctx.putImageData(imgData, 0, 0); - } + if (!ctx.filter) return softFilter(typ, x); + makeTmpCtx(); + if (typeof typ == 'string') { + nativeFilter(typ); + } else if (typ == $.THRESHOLD) { + x ??= 0.5; + x = Math.max(x, 0.00001); + let b = Math.floor((0.5 / x) * 100); + nativeFilter(`saturate(0%) brightness(${b}%) contrast(1000000%)`); + } else if (typ == $.GRAY) { + nativeFilter(`saturate(0%)`); + } else if (typ == $.OPAQUE) { + tmpCtx.fillStyle = 'black'; + tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); + tmpCtx.drawImage(ctx.canvas, 0, 0); + ctx.save(); + ctx.resetTransform(); + ctx.drawImage(tmpCtx.canvas, 0, 0); + ctx.restore(); + } else if (typ == $.INVERT) { + nativeFilter(`invert(100%)`); + } else if (typ == $.BLUR) { + nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`); } else { - let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - filterImpl[typ](imgData.data, x); - ctx.putImageData(imgData, 0, 0); + softFilter(typ, x); } }; @@ -351,7 +357,7 @@ function Q5(scope, parent) { let pd = $._pixelDensity || 1; if (x !== undefined && w === undefined) { let c = ctx.getImageData(x * pd, y * pd, 1, 1).data; - return new Q5.Color(c[0], c[1], c[2], c[3] / 255); + return new $.Color(c[0], c[1], c[2], c[3] / 255); } x = (x || 0) * pd; y = (y || 0) * pd; @@ -369,7 +375,7 @@ function Q5(scope, parent) { }; $.set = (x, y, c) => { - if (c.MAGIC == $.MAGIC) { + if (c._q5) { let old = $._tint; $._tint = null; $.image(c, x, y); @@ -418,8 +424,8 @@ function Q5(scope, parent) { ctx.drawImage(tmpCtx.canvas, 0, 0); ctx.restore(); }; - $.tint = function () { - $._tint = $.color(...Array.from(arguments)); + $.tint = (c) => { + $._tint = c._q5Color ? c : $.color(...arguments); }; $.noTint = () => ($._tint = null); @@ -469,9 +475,8 @@ function Q5(scope, parent) { $.canvas.save = $.save; $.saveCanvas = $.save; - //================================================================ // PRIVATE VARS - //================================================================ + let looper = null; let firstVertex = true; let curveBuff = []; @@ -483,18 +488,11 @@ function Q5(scope, parent) { if (scope == 'image') return; - $.remove = () => { - $.noLoop(); - $.canvas.remove(); - }; - - //================================================================ // CONSTANTS - //================================================================ - $.RGB = 0; - $.HSV = 1; - $.HSB = 1; + $.RGB = 'rgb'; + $.RGBA = 'rgb'; + $.HSB = 'hsb'; $.CHORD = 0; $.PIE = 1; @@ -589,18 +587,17 @@ function Q5(scope, parent) { $.SHR3 = 1; $.LCG = 2; - $.HARDWARE_FILTERS = true; $.hint = (prop, val) => { $[prop] = val; }; - //================================================================ // PUBLIC PROPERTIES - //================================================================ + $.frameCount = 0; $.deltaTime = 16; $.mouseX = 0; $.mouseY = 0; + $.touches = []; $.mouseButton = null; $.keyIsPressed = false; $.mouseIsPressed = false; @@ -615,7 +612,6 @@ function Q5(scope, parent) { $.relRotationX = 0; $.relRotationY = 0; $.relRotationZ = 0; - $.pmouseX = 0; $.pmouseY = 0; $.pAccelerationX = 0; @@ -628,55 +624,40 @@ function Q5(scope, parent) { $.pRelRotationY = 0; $.pRelRotationZ = 0; - $.touches = []; + Object.defineProperty($, 'deviceOrientation', { + get: () => window.screen?.orientation?.type + }); + Object.defineProperty($, 'windowWidth', { + get: () => window.innerWidth + }); + Object.defineProperty($, 'windowHeight', { + get: () => window.innerHeight + }); + + // PRIVATE PROPERTIES - $._colorMode = $.RGB; + $._colorMode = 'rgb'; $._doStroke = true; $._doFill = true; $._strokeSet = false; $._fillSet = false; + $._tint = null; $._ellipseMode = $.CENTER; $._rectMode = $.CORNER; $._curveDetail = 20; $._curveAlpha = 0.0; $._loop = true; - $._textFont = 'sans-serif'; $._textSize = 12; $._textLeading = 15; $._textLeadDiff = 3; $._textStyle = 'normal'; - $._pixelDensity = 1; $._lastFrameTime = 0; $._targetFrameRate = null; $._frameRate = $._fps = 60; - $._tint = null; - - //================================================================ - // ALIAS PROPERTIES - //================================================================ - - Object.defineProperty($, 'deviceOrientation', { - get: () => window.screen?.orientation?.type - }); - - Object.defineProperty($, 'windowWidth', { - get: () => window.innerWidth - }); - - Object.defineProperty($, 'windowHeight', { - get: () => window.innerHeight - }); - - Object.defineProperty($, 'drawingContext', { - get: () => ctx - }); - - //================================================================ // CANVAS - //================================================================ function cloneCtx() { let c = {}; @@ -693,8 +674,7 @@ function Q5(scope, parent) { let c = cloneCtx(); $.canvas.width = width * $._pixelDensity; $.canvas.height = height * $._pixelDensity; - ctx = $._ctx = $.canvas.getContext('2d'); - for (let prop in c) $._ctx[prop] = c[prop]; + for (let prop in c) $.ctx[prop] = c[prop]; if (scope != 'image') $.pixelDensity($._pixelDensity); }; @@ -706,6 +686,7 @@ function Q5(scope, parent) { $.createImage = (width, height) => { return new Q5.Image(width, height); }; + $.displayDensity = () => window.devicePixelRatio; $.pixelDensity = (n) => { if (n === undefined) return $._pixelDensity; @@ -716,16 +697,13 @@ function Q5(scope, parent) { $.canvas.height = Math.ceil($.height * n); $.canvas.style.width = $.width + 'px'; $.canvas.style.height = $.height + 'px'; - ctx = $._ctx = $.canvas.getContext('2d'); - for (let prop in c) $._ctx[prop] = c[prop]; + for (let prop in c) $.ctx[prop] = c[prop]; ctx.scale($._pixelDensity, $._pixelDensity); return $._pixelDensity; }; - //================================================================ // MATH - //================================================================ $.map = (value, istart, istop, ostart, ostop, clamp) => { let val = ostart + (ostop - ostart) * (((value - istart) * 1.0) / (istop - istart)); @@ -812,10 +790,7 @@ function Q5(scope, parent) { }; $.createVector = (x, y, z) => new Q5.Vector(x, y, z, $); - //================================================================ - // CURVE QUERY - //================================================================ - //https://github.com/processing/p5.js/blob/1.1.9/src/core/shape/curves.js + // CURVES $.curvePoint = (a, b, c, d, t) => { const t3 = t * t * t, @@ -855,13 +830,21 @@ function Q5(scope, parent) { ); }; - //================================================================ - // COLORS - //================================================================ + // COLOR + + $.Color = Q5.ColorRGBA_P3; - $.Color = Q5.Color; $.colorMode = (mode) => { $._colorMode = mode; + if (mode == 'oklch') { + $.Color = Q5.ColorOKLCH; + } else if (mode == 'rgb') { + if ($.canvas.colorSpace == 'srgb') $.Color = Q5.ColorRGBA; + else $.Color = Q5.ColorRGBA_P3; + } else if (mode == 'srgb') { + $.Color = Q5.ColorRGBA; + $._colorMode = 'rgb'; + } }; let basicColors = { @@ -897,113 +880,64 @@ function Q5(scope, parent) { yellow: [255, 255, 0] }; - $.color = function () { + $.color = function (c0, c1, c2, c3) { + let C = $.Color; + if (c0._q5Color) return new C(...c0.levels); let args = arguments; if (args.length == 1) { - if (typeof args[0] == 'string') { - if (args[0][0] == '#') { - return new Q5.Color( - parseInt(args[0].slice(1, 3), 16), - parseInt(args[0].slice(3, 5), 16), - parseInt(args[0].slice(5, 7), 16), - 1 + if (typeof c0 == 'string') { + if (c0[0] == '#') { + return new C( + parseInt(c0.slice(1, 3), 16), + parseInt(c0.slice(3, 5), 16), + parseInt(c0.slice(5, 7), 16), + c0.length != 9 ? null : parseInt(c0.slice(7, 9), 16) ); - } else { - if (basicColors[args[0]]) { - return new Q5.Color(...basicColors[args[0]], 1); - } - return new Q5.Color(0, 0, 0, 1); - } - } - if (typeof args[0] != 'number' && args[0].MAGIC == 0xc010a) { - return args[0]; - } + } else if (basicColors[c0]) return new C(...basicColors[c0]); + else return new C(0, 0, 0); + } else if (Array.isArray(c0)) return new C(...c0); } - if ($._colorMode == $.RGB) { - if (args.length == 1) { - return new Q5.Color(args[0], args[0], args[0], 1); - } else if (args.length == 2) { - return new Q5.Color(args[0], args[0], args[0], args[1] / 255); - } else if (args.length == 3) { - return new Q5.Color(args[0], args[1], args[2], 1); - } else if (args.length == 4) { - return new Q5.Color(args[0], args[1], args[2], args[3] / 255); - } - } else { - if (args.length == 1) { - return new Q5.Color(...Q5.Color._hsv2rgb(0, 0, args[0] / 100), 1); - } else if (args.length == 2) { - return new Q5.Color(...Q5.Color._hsv2rgb(0, 0, args[0] / 100), args[1] / 255); - } else if (args.length == 3) { - return new Q5.Color(...Q5.Color._hsv2rgb(args[0], args[1] / 100, args[2] / 100), 1); - } else if (args.length == 4) { - return new Q5.Color(...Q5.Color._hsv2rgb(args[0], args[1] / 100, args[2] / 100), args[3]); - } + if ($._colorMode == 'rgb') { + if (args.length == 1) return new C(c0, c0, c0); + else if (args.length == 2) return new C(c0, c0, c0, c1); + else if (args.length == 3) return new C(c0, c1, c2); + else if (args.length == 4) return new C(c0, c1, c2, c3); } - return null; }; - $.red = (c) => { - return c._r; - }; - $.green = (c) => { - return c._g; - }; - $.blue = (c) => { - return c._b; - }; - $.alpha = (c) => { - return c._a * 255; - }; - $.hue = (c) => { - c._inferHSV(); - return c._h; - }; - $.saturation = (c) => { - c._inferHSV(); - return c._s; - }; - $.brightness = (c) => { - c._inferHSV(); - return c._v; - }; + $.red = (c) => c._r; + $.green = (c) => c._g; + $.blue = (c) => c._b; + $.alpha = (c) => c._a; $.lightness = (c) => { return ((0.2126 * c._r + 0.7152 * c._g + 0.0722 * c._b) * 100) / 255; }; - function lerpHue(h0, h1, t) { - var methods = [ - [Math.abs(h1 - h0), $.map(t, 0, 1, h0, h1)], - [Math.abs(h1 + 360 - h0), $.map(t, 0, 1, h0, h1 + 360)], - [Math.abs(h1 - 360 - h0), $.map(t, 0, 1, h0, h1 - 360)] - ]; - methods.sort((x, y) => x[0] - y[0]); - return (methods[0][1] + 720) % 360; - } - $.lerpColor = (a, b, t) => { - if ($._colorMode == $.RGB) { - return new Q5.Color( - $.constrain($.lerp(a._r, b._r, t), 0, 255), - $.constrain($.lerp(a._g, b._g, t), 0, 255), - $.constrain($.lerp(a._b, b._b, t), 0, 255), - $.constrain($.lerp(a._a, b._a, t), 0, 1) + if ($._colorMode == 'rgb') { + return new $.Color( + $.constrain($.lerp(a.r, b.r, t), 0, 255), + $.constrain($.lerp(a.g, b.g, t), 0, 255), + $.constrain($.lerp(a.b, b.b, t), 0, 255), + $.constrain($.lerp(a.a, b.a, t), 0, 255) ); } else { - a._inferHSV(); - b._inferHSV(); - return new Q5.Color( - $.constrain(lerpHue(a._h, b._h, t), 0, 360), - $.constrain($.lerp(a._s, b._s, t), 0, 100), - $.constrain($.lerp(a._v, b._v, t), 0, 100), - $.constrain($.lerp(a._a, b._a, t), 0, 1) + 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( + $.constrain($.lerp(a.l, b.l, t), 0, 100), + $.constrain($.lerp(a.c, b.c, t), 0, 100), + h, + $.constrain($.lerp(a.a, b.a, t), 0, 255) ); } }; - //================================================================ - // DRAWING SETTING - //================================================================ + // DRAWING SETTINGS function defaultStyle() { ctx.fillStyle = 'white'; @@ -1017,34 +951,20 @@ function Q5(scope, parent) { if (!n) $._doStroke = false; ctx.lineWidth = n || 0.0001; }; - $.stroke = function () { + $.stroke = function (c) { $._doStroke = true; $._strokeSet = true; - if (typeof arguments[0] == 'string') { - ctx.strokeStyle = arguments[0]; - return; - } - let col = $.color(...arguments); - if (col._a <= 0) { - $._doStroke = false; - return; - } - ctx.strokeStyle = col; + if (!c._q5Color) c = $.color(...arguments); + if (c._a <= 0) return ($._doStroke = false); + ctx.strokeStyle = c; }; $.noStroke = () => ($._doStroke = false); - $.fill = function () { + $.fill = function (c) { $._doFill = true; $._fillSet = true; - if (typeof arguments[0] == 'string') { - ctx.fillStyle = arguments[0]; - return; - } - let col = $.color(...arguments); - if (col._a <= 0) { - $._doFill = false; - return; - } - ctx.fillStyle = col; + if (!c._q5Color) c = $.color(...arguments); + if (c._a <= 0) return ($._doFill = false); + ctx.fillStyle = c; }; $.noFill = () => ($._doFill = false); $.smooth = () => ($._smooth = true); @@ -1058,25 +978,18 @@ function Q5(scope, parent) { $.curveAlpha = (x) => ($._curveAlpha = x); $.curveTightness = (x) => ($._curveAlpha = x); - //================================================================ // DRAWING - //================================================================ $.clear = () => { ctx.clearRect(0, 0, $.canvas.width, $.canvas.height); }; - $.background = function () { - if (arguments[0] && arguments[0].MAGIC == $.MAGIC) { - return $.image(arguments[0], 0, 0, $.width, $.height); - } + $.background = function (c) { + if (c._q5) return $.image(c, 0, 0, $.width, $.height); ctx.save(); ctx.resetTransform(); - if (typeof arguments[0] == 'string') { - ctx.fillStyle = arguments[0]; - } else { - ctx.fillStyle = $.color(...Array.from(arguments)); - } + if (!c._q5color) c = $.color(...arguments); + ctx.fillStyle = c; ctx.fillRect(0, 0, $.canvas.width, $.canvas.height); ctx.restore(); }; @@ -1284,7 +1197,6 @@ function Q5(scope, parent) { if ($._doFill) ctx.fill(); if ($._doStroke) ctx.stroke(); if (!$._doFill && !$._doStroke) { - // eh. ctx.save(); ctx.fillStyle = 'none'; ctx.fill(); @@ -1292,7 +1204,6 @@ function Q5(scope, parent) { } }; function catmullRomSpline(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, numPts, alpha) { - //https://en.wikipedia.org/wiki/Centripetal_Catmull–Rom_spline function catmullromSplineGetT(t, p0x, p0y, p1x, p1y, alpha) { let a = Math.pow(p1x - p0x, 2.0) + Math.pow(p1y - p0y, 2.0); let b = Math.pow(a, alpha * 0.5); @@ -1378,30 +1289,22 @@ function Q5(scope, parent) { $.curveVertex(x4, y4); $.endShape(); }; + $.opacity = (a) => (ctx.globalAlpha = a); - //================================================================ // DRAWING MATRIX - //================================================================ + $.translate = (x, y) => ctx.translate(x, y); $.rotate = (r) => { if ($._angleMode == 'degrees') r = $.radians(r); ctx.rotate(r); }; - $.scale = (x, y) => { 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); - }; - + $.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 = () => { ctx.resetTransform(); ctx.scale($._pixelDensity, $._pixelDensity); @@ -1425,37 +1328,27 @@ function Q5(scope, parent) { '_textStyle', '_textWrap' ]; - - $._ctxStyleNames = ['strokeStyle', 'fillStyle', 'lineWidth', 'lineCap', 'lineJoin']; - $._styles = []; - $._ctxStyles = []; - $.pushMatrix = $.push = () => { + $.push = $.pushMatrix = () => { ctx.save(); let styles = {}; for (let s of $._styleNames) styles[s] = $[s]; $._styles.push(styles); - let ctxStyles = {}; - for (let s of $._ctxStyleNames) ctxStyles[s] = ctx[s]; - $._ctxStyles.push(ctxStyles); }; - $.popMatrix = $.pop = () => { + $.pop = $.popMatrix = () => { ctx.restore(); let styles = $._styles.pop(); for (let s of $._styleNames) $[s] = styles[s]; - let ctxStyles = $._ctxStyles.pop(); - for (let s of $._ctxStyleNames) ctx[s] = ctxStyles[s]; }; - //================================================================ // IMAGING - //================================================================ - $.imageMode = (mode) => ($._imageMode = mode); // TODO + + $.imageMode = (mode) => ($._imageMode = mode); $.image = (img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) => { - let drawable = img.MAGIC == $.MAGIC ? img.canvas : img; + let drawable = img._q5 ? img.canvas : img; function reset() { - if (img.MAGIC != $.MAGIC || !$._tint) return; + if (!img._q5 || !$._tint) return; let c = img.canvas.getContext('2d'); c.save(); c.resetTransform(); @@ -1463,13 +1356,13 @@ function Q5(scope, parent) { c.drawImage(tmpCt2.canvas, 0, 0); c.restore(); } - if (img.MAGIC == $.MAGIC && $._tint != null) { + if (img._q5 && $._tint != null) { makeTmpCt2(img.canvas.width, img.canvas.height); tmpCt2.drawImage(img.canvas, 0, 0); img.tinted($._tint); } if (!dWidth) { - if (img.MAGIC == $.MAGIC || img.width) { + if (img._q5 || img.width) { dWidth = img.width; dHeight = img.height; } else { @@ -1523,9 +1416,7 @@ function Q5(scope, parent) { tmpBuf = null; }; - //================================================================ // TYPOGRAPHY - //================================================================ $.loadFont = (url, cb) => { preloadCnt++; @@ -1660,7 +1551,7 @@ function Q5(scope, parent) { return; } tg = $.createGraphics.call($, 1, 1); - c = tg._ctx; + c = tg.ctx; pd = $._pixelDensity; } c.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`; @@ -1708,11 +1599,8 @@ function Q5(scope, parent) { $._imageMode = og; }; - //================================================================ // RANDOM - //================================================================ - //https://github.com/processing/p5.js/blob/1.1.9/src/math/noise.js var PERLIN_YWRAPB = 4; var PERLIN_YWRAP = 1 << PERLIN_YWRAPB; var PERLIN_ZWRAPB = 8; @@ -1856,7 +1744,7 @@ function Q5(scope, parent) { return rng1.rand() * a; } } else { - return a[~~(a.length * rng1.rand())]; + return a[Math.trunc(a.length * rng1.rand())]; } }; $.randomGenerator = (method) => { @@ -1866,7 +1754,6 @@ function Q5(scope, parent) { }; var ziggurat = new (function () { - //http://ziggurat.glitch.me/ var iz; var jz; var kn = new Array(128); @@ -2004,29 +1891,12 @@ function Q5(scope, parent) { return ziggurat.REXP(); }; - //================================================================ - // ENVIRONMENT - //================================================================ + // DOM - $.print = console.log; - $.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'; + $.Element = function (a) { + this.elt = a; }; - - //================================================================ - // DOM - //================================================================ + $._elements = []; $.createCapture = (x) => { var vid = document.createElement('video'); @@ -2042,6 +1912,10 @@ function Q5(scope, parent) { return vid; }; + // ENVIRONMENT + + $.print = console.log; + function _draw() { let pre = performance.now(); if ($._loop) { @@ -2077,7 +1951,6 @@ function Q5(scope, parent) { $.pmouseX = $.mouseX; $.pmouseY = $.mouseY; } - $.noLoop = () => { $._loop = false; looper = null; @@ -2087,6 +1960,11 @@ function Q5(scope, parent) { if (looper == null) _draw(); }; $.redraw = () => _draw(); + $.remove = () => { + $.noLoop(); + $.canvas.remove(); + }; + $.frameRate = (fps) => { if (fps) $._targetFrameRate = fps; return $._frameRate; @@ -2094,26 +1972,31 @@ function Q5(scope, parent) { $.getFrameRate = () => $._frameRate; $.getFPS = () => $._fps; - $._updateMouse = function (e) { - let $ = this; + $.storeItem = localStorage.setItem; + $.getItem = localStorage.getItem; + $.removeItem = localStorage.removeItem; + $.clearStorage = localStorage.clear; + + // USER INPUT + + $._updateMouse = (e) => { let rect = $.canvas.getBoundingClientRect(); let sx = $.canvas.scrollWidth / $.width || 1; let sy = $.canvas.scrollHeight / $.height || 1; $.mouseX = (e.clientX - rect.left) / sx; $.mouseY = (e.clientY - rect.top) / sy; - }.bind($); - - $._onmousemove = function (e) { - $._updateMouse(e); - if (this.mouseIsPressed) this._mouseDraggedFn(e); - else this._mouseMovedFn(e); - }.bind($); + }; $._onmousedown = (e) => { $._updateMouse(e); $.mouseIsPressed = true; $.mouseButton = [$.LEFT, $.CENTER, $.RIGHT][e.button]; $._mousePressedFn(e); }; + $._onmousemove = (e) => { + $._updateMouse(e); + if ($.mouseIsPressed) $._mouseDraggedFn(e); + else $._mouseMovedFn(e); + }; $._onmouseup = (e) => { $._updateMouse(e); $.mouseIsPressed = false; @@ -2125,6 +2008,21 @@ function Q5(scope, parent) { $._mouseClickedFn(e); $.mouseIsPressed = false; }; + $.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'; + }; + $._onkeydown = (e) => { if (e.repeat) return; $.keyIsPressed = true; @@ -2194,6 +2092,8 @@ function Q5(scope, parent) { $.canvas.ontouchmove = (e) => $._ontouchmove(e); $.canvas.ontouchcancel = $.canvas.ontouchend = (e) => $._ontouchend(e); + // SENSORS + $.hasSensorPermission = (!window.DeviceOrientationEvent && !window.DeviceMotionEvent) || !(DeviceOrientationEvent.requestPermission || DeviceMotionEvent.requestPermission); @@ -2217,11 +2117,6 @@ function Q5(scope, parent) { } }; - //================================================================ - // SENSORS - //================================================================ - - // 3d transformation helpers let ROTX = (a) => [1, 0, 0, 0, 0, $.cos(a), -$.sin(a), 0, 0, $.sin(a), $.cos(a), 0, 0, 0, 0, 1]; let ROTY = (a) => [$.cos(a), 0, $.sin(a), 0, 0, 1, 0, 0, -$.sin(a), 0, $.cos(a), 0, 0, 0, 0, 1]; let MULT = (A, B) => [ @@ -2248,7 +2143,7 @@ function Q5(scope, parent) { (A[8] * v[0] + A[9] * v[1] + A[10] * v[2] + A[11]) / (A[12] * v[0] + A[13] * v[1] + A[14] * v[2] + A[15]) ]; - if (typeof window !== 'undefined') { + if (typeof window != 'undefined') { window.ondeviceorientation = (e) => { $.pRotationX = $.rotationX; $.pRotationY = $.rotationY; @@ -2260,8 +2155,8 @@ function Q5(scope, parent) { $.rotationX = e.beta * (Math.PI / 180.0); $.rotationY = e.gamma * (Math.PI / 180.0); $.rotationZ = e.alpha * (Math.PI / 180.0); - $.relRotationX = [-$.rotationY, -$.rotationX, $.rotationY][~~(window.orientation / 90) + 1]; - $.relRotationY = [-$.rotationX, $.rotationY, $.rotationX][~~(window.orientation / 90) + 1]; + $.relRotationX = [-$.rotationY, -$.rotationX, $.rotationY][Math.trunc(window.orientation / 90) + 1]; + $.relRotationY = [-$.rotationX, $.rotationY, $.rotationX][Math.trunc(window.orientation / 90) + 1]; $.relRotationZ = $.rotationZ; }; window.ondevicemotion = (e) => { @@ -2269,9 +2164,6 @@ function Q5(scope, parent) { $.pAccelerationY = $.accelerationY; $.pAccelerationZ = $.accelerationZ; if (!e.acceleration) { - // devices that don't support plain acceleration - // compute gravitational acceleration's component on X Y Z axes based on gyroscope - // g = ~ 9.80665 let grav = TRFM(MULT(ROTY($.rotationY), ROTX($.rotationX)), [0, 0, -9.80665]); $.accelerationX = e.accelerationIncludingGravity.x + grav[0]; $.accelerationY = e.accelerationIncludingGravity.y + grav[1]; @@ -2280,9 +2172,7 @@ function Q5(scope, parent) { }; } - //================================================================ // TIME - //================================================================ $.year = () => new Date().getFullYear(); $.day = () => new Date().getDay(); @@ -2291,10 +2181,7 @@ function Q5(scope, parent) { $.second = () => new Date().getSeconds(); $.millis = () => performance.now() - millisStart; - $.storeItem = localStorage.setItem; - $.getItem = localStorage.getItem; - $.removeItem = localStorage.removeItem; - $.clearStorage = localStorage.clear; + // LOAD FILES $._loadFile = (path, cb, type) => { preloadCnt++; @@ -2314,7 +2201,6 @@ function Q5(scope, parent) { $.loadStrings = (path, cb) => $._loadFile(path, cb, 'text'); $.loadJSON = (path, cb) => $._loadFile(path, cb, 'json'); - $.loadSound = (path, cb) => { preloadCnt++; let a = new Audio(path); @@ -2328,17 +2214,10 @@ function Q5(scope, parent) { return a; }; - $.Element = function (a) { - this.elt = a; - }; - $._elements = []; + // INIT if (scope == 'global') { - // delete $.name; - // delete $.length; Object.assign(Q5, $); - // $.name = ''; - // $.length = 0; delete Q5.Q5; } Q5.Image ??= _Q5Image; @@ -2403,8 +2282,8 @@ function Q5(scope, parent) { millisStart = performance.now(); function _start() { if (preloadCnt > 0) return requestAnimationFrame(_start); - ctx.save(); $._setupFn(); + if (!ctx) $.createCanvas(100, 100); $._setupDone = true; ctx.restore(); $.resetMatrix(); @@ -2420,127 +2299,100 @@ function Q5(scope, parent) { else requestAnimationFrame(_init); } +// COLOR CLASSES + 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; + } + toString() { + return `color(oklch ${this.l} ${this.c} ${this.h} / ${this.a})`; + } +}; +Q5.ColorRGBA = class extends Q5.Color { constructor(r, g, b, a) { - this.MAGIC = 0xc010a; - this._r = r; - this._g = g; - this._b = b; - this._a = a; - this._h = 0; - this._s = 0; - this._v = 0; - this._hsvInferred = false; + super(); + this.r = r; + this.g = g; + this.b = b; + this.a = a ?? 255; } - - setRed(x) { - this._r = x; - this._hsvInferred = false; + setRed(v) { + this.r = v; } - setGreen(x) { - this._g = x; - this._hsvInferred = false; + setGreen(v) { + this.g = v; } - setBlue(x) { - this._b = x; - this._hsvInferred = false; + setBlue(v) { + this.b = v; } - setAlpha(x) { - this._a = x / 255; - this._hsvInferred = false; + setAlpha(v) { + this.a = v; } get levels() { - return [this._r, this._g, this._b, this._a * 255]; - } - _inferHSV() { - if (!this._hsvInferred) { - [this._h, this._s, this._v] = Q5.Color._rgb2hsv(this._r, this._g, this._b); - this._hsvInferred = true; - } + return [this.r, this.g, this.b, this.a]; } toString() { - return `rgba(${Math.round(this._r)},${Math.round(this._g)},${Math.round(this._b)},${~~(this._a * 1000) / 1000})`; + return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`; } }; -Q5._instanceCount = 0; -Q5.Color._rgb2hsv = (r, g, b) => { - //https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both - let rgbMin, rgbMax; - let h, s, v; - rgbMin = r < g ? (r < b ? r : b) : g < b ? g : b; - rgbMax = r > g ? (r > b ? r : b) : g > b ? g : b; - v = (rgbMax * 100) / 255; - if (v == 0) { - h = 0; - s = 0; - return [h, s, v]; - } - s = (100 * (rgbMax - rgbMin)) / rgbMax; - if (s == 0) { - h = 0; - return [h, s, v]; - } - if (rgbMax == r) h = 0 + (60 * (g - b)) / (rgbMax - rgbMin); - else if (rgbMax == g) h = 120 + (60 * (b - r)) / (rgbMax - rgbMin); - else h = 240 + (60 * (r - g)) / (rgbMax - rgbMin); - return [h, s, v]; -}; -Q5.Color._hsv2rgb = (h, s, v) => { - //https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both - let r, g, b; - let hh, i, ff, p, q, t; - if (s == 0) { - r = v; - g = v; - b = v; - return [r * 255, g * 255, b * 255]; - } - hh = h; - if (hh > 360) hh = 0; - hh /= 60; - i = ~~hh; - ff = hh - i; - p = v * (1.0 - s); - q = v * (1.0 - s * ff); - t = v * (1.0 - s * (1.0 - ff)); - switch (i) { - case 0: - r = v; - g = t; - b = p; - break; - case 1: - r = q; - g = v; - b = p; - break; - case 2: - r = p; - g = v; - b = t; - break; - case 3: - r = p; - g = q; - b = v; - break; - case 4: - r = t; - g = p; - b = v; - break; - default: - r = v; - g = p; - b = q; - break; - } - return [r * 255, g * 255, b * 255]; +Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA { + constructor(r, g, b, a) { + super(r, g, b, a); + 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; + } }; -//================================================================ // VECTOR -//================================================================ + Q5.Vector = class { constructor(_x, _y, _z, _$) { this.x = _x || 0; @@ -2550,7 +2402,6 @@ Q5.Vector = class { this._cn = null; this._cnsq = null; } - set(_x, _y, _z) { this.x = _x || 0; this.y = _y || 0; @@ -2764,6 +2615,8 @@ for (let k of ['fromAngle', 'fromAngles', 'random2D', 'random3D']) { Q5.Vector[k] = (u, v, t) => new Q5.Vector()[k](u, v, t); } +// IMAGE CLASS + class _Q5Image extends Q5 { constructor(width, height) { super('image'); @@ -2779,9 +2632,24 @@ class _Q5Image extends Q5 { } } +// Q5 + +Q5.canvasOptions = { + alpha: false, + desynchronized: true, + colorSpace: 'display-p3' +}; + +if (typeof matchMedia == 'undefined' || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) { + Q5.canvasOptions.colorSpace = 'srgb'; +} + +Q5._instanceCount = 0; Q5._friendlyError = (msg, func) => { throw func + ': ' + msg; }; +Q5._validateParameters = () => true; + Q5.prototype._methods = { init: [], pre: [], @@ -2790,7 +2658,6 @@ Q5.prototype._methods = { }; Q5.prototype.registerMethod = (m, fn) => Q5.prototype._methods[m].push(fn); Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.prototype[n] = fn[n]); -Q5._validateParameters = () => true; if (typeof module != 'undefined') module.exports = Q5; else window.p5 ??= Q5; diff --git a/q5.min.js b/q5.min.js index 37c6f38..c51a9a3 100644 --- a/q5.min.js +++ b/q5.min.js @@ -4,4 +4,4 @@ * @author quinton-ashley and LingDong- * @license AGPL-3.0 */ -function Q5(e,t){let a=0;if(void 0===e&&(e="global",a++,setTimeout((()=>a--),32)),"auto"==e){if(void 0===window.setup)return;e="global"}1==arguments.length&&"string"!=typeof e&&"function"!=typeof e&&(t=arguments[0],e=null),"global"==e&&(Q5._hasGlobal=!0);let n=this;n.canvas=document.createElement("canvas");let o=n._ctx=n.canvas.getContext("2d");if(n.canvas.classList.add("p5Canvas","q5Canvas"),n.canvas.id="defaultCanvas"+Q5._instanceCount++,n.width=100,n.height=100,n.canvas.width=n.width,n.canvas.height=n.height,n._windowResizedFn=()=>{},"graphics"!=e&&"image"!=e){function i(){t??=document.getElementsByTagName("main")[0],t||(t=document.createElement("main"),document.body.append(t)),n.canvas.parent(t)}n._setupDone=!1,n._resize=()=>{n.frameCount>1&&(n._shouldResize=!0)},n.canvas.parent=e=>{"string"==typeof e&&(e=document.getElementById(e)),e.append(n.canvas),"undefined"!=typeof ResizeObserver?(n._ro&&n._ro.disconnect(),n._ro=new ResizeObserver(n._resize),n._ro.observe(t)):0==n.frameCount&&addEventListener("resize",n._resize)},document.body?i():window.addEventListener("load",i)}M(),n.MAGIC=161533525,n.pixels=[];let r=null;n.createCanvas=function(t,a){if(n.width=t,n.height=a,n.canvas.width=t,n.canvas.height=a,M(),"image"!=e){let t=n.displayDensity();"graphics"==e&&(t=this._pixelDensity),n.pixelDensity(Math.ceil(t))}else this._pixelDensity=1;return n.canvas},n._createCanvas=n.createCanvas,n.loadPixels=()=>{r=o.getImageData(0,0,n.canvas.width,n.canvas.height),n.pixels=r.data},n.updatePixels=()=>{null!=r&&o.putImageData(r,0,0)};let s={};function l(e,t){null==p&&(p=document.createElement("canvas").getContext("2d")),t??=e||o.canvas.height,e??=o.canvas.width,p.canvas.width==e&&p.canvas.height==t||(p.canvas.width=e,p.canvas.height=t)}function h(){let e=o.canvas.width*o.canvas.height*4;v&&e==v.length||(v=new Uint8ClampedArray(e))}function c(e){p.clearRect(0,0,p.canvas.width,p.canvas.height),p.filter=e,p.drawImage(o.canvas,0,0),o.save(),o.resetTransform(),o.clearRect(0,0,o.canvas.width,o.canvas.height),o.drawImage(p.canvas,0,0),o.restore()}s[n.THRESHOLD]=(e,t)=>{void 0===t?t=127.5:t*=255;for(let a=0;a=t?255:0}},s[n.GRAY]=e=>{for(let t=0;t{for(let t=0;t{for(let t=0;t{let a=t-1;for(let n=0;n>8)/a,e[n+1]=255*(e[n+1]*t>>8)/a,e[n+2]=255*(e[n+2]*t>>8)/a},s[n.DILATE]=e=>{h(),v.set(e);let[t,a]=[o.canvas.width,o.canvas.height];for(let n=0;n{h(),v.set(e);let[t,a]=[o.canvas.width,o.canvas.height];for(let n=0;n{t=t||1,t=Math.floor(t*n._pixelDensity),h(),v.set(e);let a=2*t+1;let i=function(e){let a=new Float32Array(e),n=.3*t+.8,o=n*n*2;for(let t=0;t{if(n.HARDWARE_FILTERS&&null!=o.filter)if(l(),e==n.THRESHOLD){t??=.5,t=Math.max(t,1e-5),c(`saturate(0%) brightness(${Math.floor(.5/t*100)}%) contrast(1000000%)`)}else if(e==n.GRAY)c("saturate(0%)");else if(e==n.OPAQUE)p.fillStyle="black",p.fillRect(0,0,p.canvas.width,p.canvas.height),p.drawImage(o.canvas,0,0),o.save(),o.resetTransform(),o.drawImage(p.canvas,0,0),o.restore();else if(e==n.INVERT)c("invert(100%)");else if(e==n.BLUR)c(`blur(${Math.ceil(t*n._pixelDensity/1)||1}px)`);else{let a=o.getImageData(0,0,o.canvas.width,o.canvas.height);s[e](a.data,t),o.putImageData(a,0,0)}else{let a=o.getImageData(0,0,o.canvas.width,o.canvas.height);s[e](a.data,t),o.putImageData(a,0,0)}},n.resize=(e,t)=>{l(),p.drawImage(o.canvas,0,0),n.width=e,n.height=t,o.canvas.width=e*n._pixelDensity,o.canvas.height=t*n._pixelDensity,o.save(),o.resetTransform(),o.clearRect(0,0,o.canvas.width,o.canvas.height),o.drawImage(p.canvas,0,0,o.canvas.width,o.canvas.height),o.restore()},n.get=(e,t,a,i)=>{let r=n._pixelDensity||1;if(void 0!==e&&void 0===a){let a=o.getImageData(e*r,t*r,1,1).data;return new Q5.Color(a[0],a[1],a[2],a[3]/255)}e=(e||0)*r,t=(t||0)*r;let s=a=a||n.width,l=i=i||n.height;a*=r,i*=r;let h=n.createImage(a,i),c=o.getImageData(e,t,a,i);return h.canvas.getContext("2d").putImageData(c,0,0),h._pixelDensity=r,h.width=s,h.height=l,h},n.set=(e,t,a)=>{if(a.MAGIC==n.MAGIC){let o=n._tint;return n._tint=null,n.image(a,e,t),void(n._tint=o)}let i=n._pixelDensity||1;for(let r=0;rn._tint=null,n.mask=e=>{o.save(),o.resetTransform();let t=o.globalCompositeOperation;o.globalCompositeOperation="destination-in",o.drawImage(e.canvas,0,0),o.globalCompositeOperation=t,o.restore()},n._save=(e,t,a)=>{if(t=t||"untitled","jpg"==(a=a||"png")||"png"==a)e=e.toDataURL();else{let t="text/plain";"json"==a&&("string"!=typeof e&&(e=JSON.stringify(e)),t="text/json"),e=new Blob([e],{type:t}),e=URL.createObjectURL(e)}let n=document.createElement("a");n.href=e,n.download=t+"."+a,document.body.append(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(n.href)},n.save=(e,t,a)=>{if((!e||"string"==typeof e&&(!t||!a&&t.length<5))&&(a=t,t=e,e=o.canvas),a)return n._save(e,t,a);t?(t=t.split("."),n._save(e,t[0],t.at(-1))):n._save(e)},n.canvas.save=n.save,n.saveCanvas=n.save;let d=null,u=!0,_=[],g={},m=0,p=null,f=null,v=null;if("image"==e)return;function x(){let e={};for(let t in o)"function"!=typeof o[t]&&(e[t]=o[t]);return delete e.canvas,e}n.remove=()=>{n.noLoop(),n.canvas.remove()},n.RGB=0,n.HSV=1,n.HSB=1,n.CHORD=0,n.PIE=1,n.OPEN=2,n.RADIUS="radius",n.CORNER="corner",n.CORNERS="corners",n.ROUND="round",n.SQUARE="butt",n.PROJECT="square",n.MITER="miter",n.BEVEL="bevel",n.CLOSE=1,n.BLEND="source-over",n.REMOVE="destination-out",n.ADD="lighter",n.DARKEST="darken",n.LIGHTEST="lighten",n.DIFFERENCE="difference",n.SUBTRACT="subtract",n.EXCLUSION="exclusion",n.MULTIPLY="multiply",n.SCREEN="screen",n.REPLACE="copy",n.OVERLAY="overlay",n.HARD_LIGHT="hard-light",n.SOFT_LIGHT="soft-light",n.DODGE="color-dodge",n.BURN="color-burn",n.NORMAL="normal",n.ITALIC="italic",n.BOLD="bold",n.BOLDITALIC="italic bold",n.CENTER="center",n.LEFT="left",n.RIGHT="right",n.TOP="top",n.BOTTOM="bottom",n.BASELINE="alphabetic",n.LANDSCAPE="landscape",n.PORTRAIT="portrait",n.ALT=18,n.BACKSPACE=8,n.CONTROL=17,n.DELETE=46,n.DOWN_ARROW=40,n.ENTER=13,n.ESCAPE=27,n.LEFT_ARROW=37,n.OPTION=18,n.RETURN=13,n.RIGHT_ARROW=39,n.SHIFT=16,n.TAB=9,n.UP_ARROW=38,n.DEGREES="degrees",n.RADIANS="radians",n.HALF_PI=Math.PI/2,n.PI=Math.PI,n.QUARTER_PI=Math.PI/4,n.TAU=2*Math.PI,n.TWO_PI=2*Math.PI,n.THRESHOLD=1,n.GRAY=2,n.OPAQUE=3,n.INVERT=4,n.POSTERIZE=5,n.DILATE=6,n.ERODE=7,n.BLUR=8,n.ARROW="default",n.CROSS="crosshair",n.HAND="pointer",n.MOVE="move",n.TEXT="text",n.VIDEO={video:!0,audio:!1},n.AUDIO={video:!1,audio:!0},n.SHR3=1,n.LCG=2,n.HARDWARE_FILTERS=!0,n.hint=(e,t)=>{n[e]=t},n.frameCount=0,n.deltaTime=16,n.mouseX=0,n.mouseY=0,n.mouseButton=null,n.keyIsPressed=!1,n.mouseIsPressed=!1,n.key=null,n.keyCode=null,n.accelerationX=0,n.accelerationY=0,n.accelerationZ=0,n.rotationX=0,n.rotationY=0,n.rotationZ=0,n.relRotationX=0,n.relRotationY=0,n.relRotationZ=0,n.pmouseX=0,n.pmouseY=0,n.pAccelerationX=0,n.pAccelerationY=0,n.pAccelerationZ=0,n.pRotationX=0,n.pRotationY=0,n.pRotationZ=0,n.pRelRotationX=0,n.pRelRotationY=0,n.pRelRotationZ=0,n.touches=[],n._colorMode=n.RGB,n._doStroke=!0,n._doFill=!0,n._strokeSet=!1,n._fillSet=!1,n._ellipseMode=n.CENTER,n._rectMode=n.CORNER,n._curveDetail=20,n._curveAlpha=0,n._loop=!0,n._textFont="sans-serif",n._textSize=12,n._textLeading=15,n._textLeadDiff=3,n._textStyle="normal",n._pixelDensity=1,n._lastFrameTime=0,n._targetFrameRate=null,n._frameRate=n._fps=60,n._tint=null,Object.defineProperty(n,"deviceOrientation",{get:()=>window.screen?.orientation?.type}),Object.defineProperty(n,"windowWidth",{get:()=>window.innerWidth}),Object.defineProperty(n,"windowHeight",{get:()=>window.innerHeight}),Object.defineProperty(n,"drawingContext",{get:()=>o}),n.resizeCanvas=(t,a)=>{n.width=t,n.height=a;let i=x();n.canvas.width=t*n._pixelDensity,n.canvas.height=a*n._pixelDensity,o=n._ctx=n.canvas.getContext("2d");for(let e in i)n._ctx[e]=i[e];"image"!=e&&n.pixelDensity(n._pixelDensity)},n.createGraphics=function(e,t){let a=new Q5("graphics");return a._createCanvas.call(n,e,t),a},n.createImage=(e,t)=>new Q5.Image(e,t),n.displayDensity=()=>window.devicePixelRatio,n.pixelDensity=e=>{if(void 0===e)return n._pixelDensity;n._pixelDensity=e;let t=x();n.canvas.width=Math.ceil(n.width*e),n.canvas.height=Math.ceil(n.height*e),n.canvas.style.width=n.width+"px",n.canvas.style.height=n.height+"px",o=n._ctx=n.canvas.getContext("2d");for(let e in t)n._ctx[e]=t[e];return o.scale(n._pixelDensity,n._pixelDensity),n._pixelDensity},n.map=(e,t,a,n,o,i)=>{let r=n+1*(e-t)/(a-t)*(o-n);return i?ne*(1-a)+t*a,n.constrain=(e,t,a)=>Math.min(Math.max(e,t),a),n.dist=function(){return 4==arguments.length?Math.hypot(arguments[0]-arguments[2],arguments[1]-arguments[3]):Math.hypot(arguments[0]-arguments[3],arguments[1]-arguments[4],arguments[2]-arguments[5])},n.norm=(e,t,a)=>n.map(e,t,a,0,1),n.sq=e=>e*e,n.fract=e=>e-Math.floor(e),n.angleMode=e=>n._angleMode=e,n._DEGTORAD=Math.PI/180,n._RADTODEG=180/Math.PI,n.degrees=e=>e*n._RADTODEG,n.radians=e=>e*n._DEGTORAD,n.abs=Math.abs,n.ceil=Math.ceil,n.exp=Math.exp,n.floor=Math.floor,n.log=Math.log,n.mag=Math.hypot,n.max=Math.max,n.min=Math.min,n.round=Math.round,n.pow=Math.pow,n.sqrt=Math.sqrt,n.sin=e=>("degrees"==n._angleMode&&(e=n.radians(e)),Math.sin(e)),n.cos=e=>("degrees"==n._angleMode&&(e=n.radians(e)),Math.cos(e)),n.tan=e=>("degrees"==n._angleMode&&(e=n.radians(e)),Math.tan(e)),n.asin=e=>{let t=Math.asin(e);return"degrees"==n._angleMode&&(t=n.degrees(t)),t},n.acos=e=>{let t=Math.acos(e);return"degrees"==n._angleMode&&(t=n.degrees(t)),t},n.atan=e=>{let t=Math.atan(e);return"degrees"==n._angleMode&&(t=n.degrees(t)),t},n.atan2=(e,t)=>{let a=Math.atan2(e,t);return"degrees"==n._angleMode&&(a=n.degrees(a)),a},n.nf=(e,t,a)=>{let n=e<0,o=e.toString();return n&&(o=o.slice(1)),o=o.padStart(t,"0"),a>0&&(-1==o.indexOf(".")&&(o+="."),o=o.padEnd(t+1+a,"0")),n&&(o="-"+o),o},n.createVector=(e,t,a)=>new Q5.Vector(e,t,a,n),n.curvePoint=(e,t,a,n,o)=>{const i=o*o*o,r=o*o;return e*(-.5*i+r-.5*o)+t*(1.5*i-2.5*r+1)+a*(-1.5*i+2*r+.5*o)+n*(.5*i-.5*r)},n.bezierPoint=(e,t,a,n,o)=>{const i=1-o;return Math.pow(i,3)*e+3*Math.pow(i,2)*o*t+3*i*Math.pow(o,2)*a+Math.pow(o,3)*n},n.curveTangent=(e,t,a,n,o)=>{const i=o*o;return e*(-3*i/2+2*o-.5)+t*(9*i/2-5*o)+a*(-9*i/2+4*o+.5)+n*(3*i/2-o)},n.bezierTangent=(e,t,a,n,o)=>{const i=1-o;return 3*n*Math.pow(o,2)-3*a*Math.pow(o,2)+6*a*i*o-6*t*i*o+3*t*Math.pow(i,2)-3*e*Math.pow(i,2)},n.Color=Q5.Color,n.colorMode=e=>{n._colorMode=e};let y={aqua:[0,255,255],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],crimson:[220,20,60],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]};function M(){o.fillStyle="white",o.strokeStyle="black",o.lineCap="round",o.lineJoin="miter",o.textAlign="left"}function w(e){let t=n._angleMode==n.DEGREES?180:Math.PI,a=2*t;if(0<=e&&e<=a)return e;for(;e<0;)e+=a;for(;e>=t;)e-=a;return e}function R(e,t,a,i,r,s,l,h){if(!n._doFill&&!n._doStroke)return;let c=w(r),d=w(s);if(c>d&&([c,d]=[d,c]),0==c){if(0==d)return;if(n._angleMode==n.DEGREES&&360==d||d==n.TAU)return n.ellipse(e,t,a,i)}o.beginPath();for(let r=0;re._r,n.green=e=>e._g,n.blue=e=>e._b,n.alpha=e=>255*e._a,n.hue=e=>(e._inferHSV(),e._h),n.saturation=e=>(e._inferHSV(),e._s),n.brightness=e=>(e._inferHSV(),e._v),n.lightness=e=>100*(.2126*e._r+.7152*e._g+.0722*e._b)/255,n.lerpColor=(e,t,a)=>n._colorMode==n.RGB?new Q5.Color(n.constrain(n.lerp(e._r,t._r,a),0,255),n.constrain(n.lerp(e._g,t._g,a),0,255),n.constrain(n.lerp(e._b,t._b,a),0,255),n.constrain(n.lerp(e._a,t._a,a),0,1)):(e._inferHSV(),t._inferHSV(),new Q5.Color(n.constrain(function(e,t,a){var o=[[Math.abs(t-e),n.map(a,0,1,e,t)],[Math.abs(t+360-e),n.map(a,0,1,e,t+360)],[Math.abs(t-360-e),n.map(a,0,1,e,t-360)]];return o.sort(((e,t)=>e[0]-t[0])),(o[0][1]+720)%360}(e._h,t._h,a),0,360),n.constrain(n.lerp(e._s,t._s,a),0,100),n.constrain(n.lerp(e._v,t._v,a),0,100),n.constrain(n.lerp(e._a,t._a,a),0,1))),n.strokeWeight=e=>{e||(n._doStroke=!1),o.lineWidth=e||1e-4},n.stroke=function(){if(n._doStroke=!0,n._strokeSet=!0,"string"==typeof arguments[0])return void(o.strokeStyle=arguments[0]);let e=n.color(...arguments);e._a<=0?n._doStroke=!1:o.strokeStyle=e},n.noStroke=()=>n._doStroke=!1,n.fill=function(){if(n._doFill=!0,n._fillSet=!0,"string"==typeof arguments[0])return void(o.fillStyle=arguments[0]);let e=n.color(...arguments);e._a<=0?n._doFill=!1:o.fillStyle=e},n.noFill=()=>n._doFill=!1,n.smooth=()=>n._smooth=!0,n.noSmooth=()=>n._smooth=!1,n.blendMode=e=>o.globalCompositeOperation=e,n.strokeCap=e=>o.lineCap=e,n.strokeJoin=e=>o.lineJoin=e,n.ellipseMode=e=>n._ellipseMode=e,n.rectMode=e=>n._rectMode=e,n.curveDetail=e=>n._curveDetail=e,n.curveAlpha=e=>n._curveAlpha=e,n.curveTightness=e=>n._curveAlpha=e,n.clear=()=>{o.clearRect(0,0,n.canvas.width,n.canvas.height)},n.background=function(){if(arguments[0]&&arguments[0].MAGIC==n.MAGIC)return n.image(arguments[0],0,0,n.width,n.height);o.save(),o.resetTransform(),o.fillStyle="string"==typeof arguments[0]?arguments[0]:n.color(...Array.from(arguments)),o.fillRect(0,0,n.canvas.width,n.canvas.height),o.restore()},n.line=(e,t,a,i)=>{n._doStroke&&(o.beginPath(),o.moveTo(e,t),o.lineTo(a,i),o.stroke())},n.arc=(e,t,a,o,i,r,s,l)=>{if(i==r)return n.ellipse(e,t,a,o);l??=25,s??=n.PIE,n._ellipseMode==n.CENTER?R(e,t,a,o,i,r,s,l):n._ellipseMode==n.RADIUS?R(e,t,2*a,2*o,i,r,s,l):n._ellipseMode==n.CORNER?R(e+a/2,t+o/2,a,o,i,r,s,l):n._ellipseMode==n.CORNERS&&R((e+a)/2,(t+o)/2,a-e,o-t,i,r,s,l)},n.ellipse=(e,t,a,o)=>{o??=a,n._ellipseMode==n.CENTER?S(e,t,a,o):n._ellipseMode==n.RADIUS?S(e,t,2*a,2*o):n._ellipseMode==n.CORNER?S(e+a/2,t+o/2,a,o):n._ellipseMode==n.CORNERS&&S((e+a)/2,(t+o)/2,a-e,o-t)},n.circle=(e,t,a)=>n.ellipse(e,t,a,a),n.point=(e,t)=>{e.x&&(t=e.y,e=e.x),o.beginPath(),o.ellipse(e,t,.4,.4,0,0,n.TAU),o.stroke()},n.rect=(e,t,a,o,i,r,s,l)=>{n._rectMode==n.CENTER?C(e-a/2,t-o/2,a,o,i,r,s,l):n._rectMode==n.RADIUS?C(e-a,t-o,2*a,2*o,i,r,s,l):n._rectMode==n.CORNER?C(e,t,a,o,i,r,s,l):n._rectMode==n.CORNERS&&C(e,t,a-e,o-t,i,r,s,l)},n.square=(e,t,a,o,i,r,s)=>n.rect(e,t,a,a,o,i,r,s),n.beginShape=()=>{I(),o.beginPath(),u=!0},n.beginContour=()=>{o.closePath(),I(),u=!0},n.endContour=()=>{I(),u=!0},n.vertex=(e,t)=>{I(),u?o.moveTo(e,t):o.lineTo(e,t),u=!1},n.bezierVertex=(e,t,a,n,i,r)=>{I(),o.bezierCurveTo(e,t,a,n,i,r)},n.quadraticVertex=(e,t,a,n)=>{I(),o.quadraticCurveTo(e,t,a,n)},n.bezier=(e,t,a,o,i,r,s,l)=>{n.beginShape(),n.vertex(e,t),n.bezierVertex(a,o,i,r,s,l),n.endShape()},n.triangle=(e,t,a,o,i,r)=>{n.beginShape(),n.vertex(e,t),n.vertex(a,o),n.vertex(i,r),n.endShape(n.CLOSE)},n.quad=(e,t,a,o,i,r,s,l)=>{n.beginShape(),n.vertex(e,t),n.vertex(a,o),n.vertex(i,r),n.vertex(s,l),n.endShape(n.CLOSE)},n.endShape=e=>{I(),e&&o.closePath(),n._doFill&&o.fill(),n._doStroke&&o.stroke(),n._doFill||n._doStroke||(o.save(),o.fillStyle="none",o.fill(),o.restore())},n.curveVertex=(e,t)=>{if(_.push([e,t]),_.length<4)return;let a=function(e,t,a,n,o,i,r,s,l,h){function c(e,t,a,n,o,i){let r=Math.pow(n-t,2)+Math.pow(o-a,2);return Math.pow(r,.5*i)+e}let d=[],u=c(0,e,t,a,n,h),_=c(u,a,n,o,i,h),g=c(_,o,i,r,s,h);for(let h=0;h0?(m[e]=1,m[e+1]=0):(m[e]=0,m[e+1]=1));let p=e*m[0]+a*m[1],f=t*m[0]+n*m[1],v=a*m[2]+o*m[3],x=n*m[2]+i*m[3],y=o*m[4]+r*m[5],M=i*m[4]+s*m[5],w=p*m[6]+v*m[7],R=f*m[6]+x*m[7],S=v*m[8]+y*m[9],C=x*m[8]+M*m[9],I=w*m[2]+S*m[3],E=R*m[2]+C*m[3];d.push([I,E])}return d}(..._[_.length-4],..._[_.length-3],..._[_.length-2],..._[_.length-1],n._curveDetail,n._curveAlpha);for(let e=0;e{n.beginShape(),n.curveVertex(e,t),n.curveVertex(a,o),n.curveVertex(i,r),n.curveVertex(s,l),n.endShape()},n.translate=(e,t)=>o.translate(e,t),n.rotate=e=>{"degrees"==n._angleMode&&(e=n.radians(e)),o.rotate(e)},n.scale=(e,t)=>{t??=e,o.scale(e,t)},n.applyMatrix=(e,t,a,n,i,r)=>{o.transform(e,t,a,n,i,r)},n.shearX=e=>{o.transform(1,0,n.tan(e),1,0,0)},n.shearY=e=>{o.transform(1,n.tan(e),0,1,0,0)},n.resetMatrix=()=>{o.resetTransform(),o.scale(n._pixelDensity,n._pixelDensity)},n._styleNames=["_doStroke","_doFill","_strokeSet","_fillSet","_tint","_imageMode","_rectMode","_ellipseMode","_textFont","_textLeading","_leadingSet","_textSize","_textAlign","_textBaseline","_textStyle","_textWrap"],n._ctxStyleNames=["strokeStyle","fillStyle","lineWidth","lineCap","lineJoin"],n._styles=[],n._ctxStyles=[],n.pushMatrix=n.push=()=>{o.save();let e={};for(let t of n._styleNames)e[t]=n[t];n._styles.push(e);let t={};for(let e of n._ctxStyleNames)t[e]=o[e];n._ctxStyles.push(t)},n.popMatrix=n.pop=()=>{o.restore();let e=n._styles.pop();for(let t of n._styleNames)n[t]=e[t];let t=n._ctxStyles.pop();for(let e of n._ctxStyleNames)o[e]=t[e]},n.imageMode=e=>n._imageMode=e,n.image=(e,t,a,i,r,s,l,h,c)=>{let d=e.MAGIC==n.MAGIC?e.canvas:e;var u,_;e.MAGIC==n.MAGIC&&null!=n._tint&&(u=e.canvas.width,_=e.canvas.height,null==f&&(f=document.createElement("canvas").getContext("2d")),_??=u||o.canvas.height,u??=o.canvas.width,f.canvas.width==u&&f.canvas.height==_||(f.canvas.width=u,f.canvas.height=_),f.drawImage(e.canvas,0,0),e.tinted(n._tint)),i||(e.MAGIC==n.MAGIC||e.width?(i=e.width,r=e.height):(i=e.videoWidth,r=e.videoHeight)),"center"==n._imageMode&&(t-=.5*i,a-=.5*r);let g=e._pixelDensity;s??=0,l??=0,h?h*=g:h=d.width,c?c*=g:c=d.height,o.drawImage(d,s*g,l*g,h,c,t,a,i,r),function(){if(e.MAGIC!=n.MAGIC||!n._tint)return;let t=e.canvas.getContext("2d");t.save(),t.resetTransform(),t.clearRect(0,0,t.canvas.width,t.canvas.height),t.drawImage(f.canvas,0,0),t.restore()}()},n._incrementPreload=()=>a++,n._decrementPreload=()=>a--,n.loadImage=(e,t)=>{a++;let o=n.createImage(1,1),i=o.canvas.getContext("2d"),r=new window.Image;return r.src=e,r.crossOrigin="Anonymous",r._pixelDensity=1,r.onload=()=>{o.width=i.canvas.width=r.naturalWidth,o.height=i.canvas.height=r.naturalHeight,i.drawImage(r,0,0),a--,t&&t(o)},r.onerror=e=>{throw a--,e},o},n._clearTemporaryBuffers=()=>{p=null,f=null,v=null},n.loadFont=(e,t)=>{a++;let n=e.split("/"),o=n[n.length-1].split(".")[0].replace(" ",""),i=new FontFace(o,"url("+e+")");return document.fonts.add(i),i.load().then((()=>{a--,t&&t(o)})),o},n.textFont=e=>n._textFont=e,n.textSize=e=>{if(void 0===e)return n._textSize;n._textSize=e,n._leadingSet||(n._textLeading=1.25*e,n._textLeadDiff=n._textLeading-e)},n.textLeading=e=>{if(void 0===e)return n._textLeading;n._textLeading=e,n._textLeadDiff=e-n._textSize,n._leadingSet=!0},n.textStyle=e=>n._textStyle=e,n.textAlign=(e,t)=>{o.textAlign=e,t&&(o.textBaseline=t==n.CENTER?"middle":t)},n.textWidth=e=>(o.font=`${n._textStyle} ${n._textSize}px ${n._textFont}`,o.measureText(e).width),n.textAscent=e=>(o.font=`${n._textStyle} ${n._textSize}px ${n._textFont}`,o.measureText(e).actualBoundingBoxAscent),n.textDescent=e=>(o.font=`${n._textStyle} ${n._textSize}px ${n._textFont}`,o.measureText(e).actualBoundingBoxDescent),n._textCache=!0,n._TimedCache=class extends Map{constructor(){super(),this.maxSize=500}set(e,t){t.lastAccessed=Date.now(),super.set(e,t),this.size>this.maxSize&&this.gc()}get(e){const t=super.get(e);return t&&(t.lastAccessed=Date.now()),t}gc(){let e,t=1/0,a=0;for(const[n,o]of this.entries())o.lastAccessed(t&&(n._tic.maxSize=t),void 0!==e&&(n._textCache=e),n._textCache),n.createTextImage=(e,t,a)=>{let o=n._textCache;n._textCache=!0,n._useCache=!0,n.text(e,0,0,t,a),n._useCache=!1;let i=E(e,t,a);return n._textCache=o,n._tic.get(i)},n.text=(e,t,a,i,r)=>{if(void 0===e)return;if(e=e.toString(),!n._doFill&&!n._doStroke)return;let s,l,h,c,d,u,_,g,m=1,p=o.getTransform(),f=n._useCache||n._textCache&&(0!=p.b||0!=p.c);if(f){if(c=E(e,i,r),l=n._tic.get(c),l)return void n.textImage(l,t,a);h=n.createGraphics.call(n,1,1),s=h._ctx,m=n._pixelDensity}else s=o,d=t,u=a;s.font=`${n._textStyle} ${n._textSize}px ${n._textFont}`;let v=e.split("\n");if(f){d=0,u=n._textLeading*v.length;let t=s.measureText(" ");_=t.fontBoundingBoxAscent,g=t.fontBoundingBoxDescent,r??=u+g,h.resizeCanvas(Math.ceil(s.measureText(e).width),Math.ceil(r)),s.fillStyle=o.fillStyle,s.strokeStyle=o.strokeStyle,s.lineWidth=o.lineWidth}let x=s.fillStyle;n._fillSet||(s.fillStyle="black");for(let e=0;er));e++);n._fillSet||(s.fillStyle=x),f&&(l=h.get(),l._ascent=_,l._descent=g,n._tic.set(c,l),n.textImage(l,t,a))},n.textImage=(e,t,a)=>{let i=n._imageMode;n._imageMode="corner","center"==o.textAlign?t-=.5*e.width:"right"==o.textAlign&&(t-=e.width),"alphabetic"==o.textBaseline&&(a-=n._textLeading),"middle"==o.textBaseline?a-=e._descent+.5*e._ascent+n._textLeadDiff:"bottom"==o.textBaseline?a-=e._ascent+e._descent+n._textLeadDiff:"top"==o.textBaseline&&(a-=e._descent+n._textLeadDiff),n.image(e,t,a),n._imageMode=i};var b,D=4095,A=4,T=.5,k=e=>.5*(1-Math.cos(e*Math.PI));n.noise=(e,t,a)=>{if(t??=0,a??=0,null==b){b=new Array(4096);for(var n=0;n<4096;n++)b[n]=Math.random()}e<0&&(e=-e),t<0&&(t=-t),a<0&&(a=-a);for(var o,i,r,s,l,h=Math.floor(e),c=Math.floor(t),d=Math.floor(a),u=e-h,_=t-c,g=a-d,m=0,p=.5,f=0;f=1&&(h++,u--),(_*=2)>=1&&(c++,_--),(g*=2)>=1&&(d++,g--)}return m},n.noiseDetail=(e,t)=>{e>0&&(A=e),t>0&&(T=t)};const P=()=>{let e,t,a=4294967295;return{setSeed(n){e=t=(null==n?Math.random()*a:n)>>>0},getSeed:()=>t,rand:()=>(e^=e<<17,e^=e>>13,e^=e<<5,(e>>>0)/a)}};let O=P();O.setSeed(),n.noiseSeed=e=>{let t=void 0===e?4294967295*Math.random():e;b||(b=new Float32Array(4096));for(var a=0;a<4096;a++)t^=t<<17,t^=t>>13,t^=t<<5,b[a]=(t>>>0)/4294967295},n.randomSeed=e=>O.setSeed(e),n.random=(e,t)=>void 0===e?O.rand():"number"==typeof e?void 0!==t?O.rand()*(t-e)+e:O.rand()*e:e[~~(e.length*O.rand())],n.randomGenerator=e=>{e==n.LCG?O=(()=>{const e=4294967296;let t,a;return{setSeed(n){a=t=(null==n?Math.random()*e:n)>>>0},getSeed:()=>t,rand:()=>(a=(1664525*a+1013904223)%e,a/e)}})():e==n.SHR3&&(O=P()),O.setSeed()};var z=new function(){var e,t,a,n=new Array(128),o=new Array(256),i=new Array(128),r=new Array(128),s=new Array(256),l=new Array(256),h=()=>4294967296*O.rand()-2147483648,c=()=>.5+2.328306e-10*(h()<<0),d=()=>{for(var t,o,s,l,d=3.44262;;){if(t=a*i[e],0==e){do{s=c(),l=c(),t=.2904764*-Math.log(s),o=-Math.log(l)}while(o+o0?d+t:-d-t}if(r[e]+c()*(r[e-1]-r[e]){for(var a;;){if(0==e)return 7.69711-Math.log(c());if(a=t*s[e],l[e]+c()*(l[e-1]-l[e])(a=h(),e=127&a,Math.abs(a)(t=h()>>>0){var e,t,a=2147483648,h=4294967296,c=3.442619855899,d=c,u=.00991256303526217,_=7.697117470131487,g=_,m=.003949659822581572;for(e=u/Math.exp(-.5*c*c),n[0]=Math.floor(c/e*a),n[1]=0,i[0]=e/a,i[127]=c/a,r[0]=1,r[127]=Math.exp(-.5*c*c),t=126;t>=1;t--)c=Math.sqrt(-2*Math.log(u/c+Math.exp(-.5*c*c))),n[t+1]=Math.floor(c/d*a),d=c,r[t]=Math.exp(-.5*c*c),i[t]=c/a;for(e=m/Math.exp(-_),o[0]=Math.floor(_/e*h),o[1]=0,s[0]=e/h,s[255]=_/h,l[0]=1,l[255]=Math.exp(-_),t=254;t>=1;t--)_=-Math.log(m/_+Math.exp(-_)),o[t+1]=Math.floor(_/g*h),g=_,l[t]=Math.exp(-_),s[t]=_/h}};function F(){let e=performance.now();if(n._loop)d=n._targetFrameRate?setTimeout(F,1e3/n._targetFrameRate):requestAnimationFrame(F);else if(n.frameCount>0)return;if(d&&0!=n.frameCount){if(e-n._lastFrameTime<1e3/(n._targetFrameRate||60)-5)return}n.deltaTime=e-n._lastFrameTime,n._frameRate=1e3/n.deltaTime,n.frameCount++,n._shouldResize&&(n._windowResizedFn(),n._shouldResize=!1);for(let e of Q5.prototype._methods.pre)e.call(n);I(),u=!0,o.save(),n._drawFn();for(let e of Q5.prototype._methods.post)e.call(n);o.restore(),n.resetMatrix();let t=performance.now();n._fps=Math.round(1e3/(t-e)),n._lastFrameTime=e,n.pmouseX=n.mouseX,n.pmouseY=n.mouseY}function Q(e){const t=n.canvas.getBoundingClientRect(),a=n.canvas.scrollWidth/n.width||1,o=n.canvas.scrollHeight/n.height||1;return{x:(e.clientX-t.left)/a,y:(e.clientY-t.top)/o,id:e.identifier}}function L(){return n._touchStartedFn.isPlaceHolder&&n._touchMovedFn.isPlaceHolder&&n._touchEndedFn.isPlaceHolder}z.hasInit=!1,n.randomGaussian=(e,t)=>(z.hasInit||(z.zigset(),z.hasInit=!0),z.RNOR()*t+e),n.randomExponential=()=>(z.hasInit||(z.zigset(),z.hasInit=!0),z.REXP()),n.print=console.log,n.cursor=(e,t,a)=>{let o="";e.includes(".")&&(e=`url("${e}")`,o=", auto"),void 0!==t&&(e+=" "+t+" "+a),n.canvas.style.cursor=e+o},n.noCursor=()=>{n.canvas.style.cursor="none"},n.createCapture=e=>{var t=document.createElement("video");return t.playsinline="playsinline",t.autoplay="autoplay",navigator.mediaDevices.getUserMedia(e).then((e=>{t.srcObject=e})),t.style.position="absolute",t.style.opacity=1e-5,t.style.zIndex=-1e3,document.body.append(t),t},n.noLoop=()=>{n._loop=!1,d=null},n.loop=()=>{n._loop=!0,null==d&&F()},n.redraw=()=>F(),n.frameRate=e=>(e&&(n._targetFrameRate=e),n._frameRate),n.getFrameRate=()=>n._frameRate,n.getFPS=()=>n._fps,n._updateMouse=function(e){let t=this,a=t.canvas.getBoundingClientRect(),n=t.canvas.scrollWidth/t.width||1,o=t.canvas.scrollHeight/t.height||1;t.mouseX=(e.clientX-a.left)/n,t.mouseY=(e.clientY-a.top)/o}.bind(n),n._onmousemove=function(e){n._updateMouse(e),this.mouseIsPressed?this._mouseDraggedFn(e):this._mouseMovedFn(e)}.bind(n),n._onmousedown=e=>{n._updateMouse(e),n.mouseIsPressed=!0,n.mouseButton=[n.LEFT,n.CENTER,n.RIGHT][e.button],n._mousePressedFn(e)},n._onmouseup=e=>{n._updateMouse(e),n.mouseIsPressed=!1,n._mouseReleasedFn(e)},n._onclick=e=>{n._updateMouse(e),n.mouseIsPressed=!0,n._mouseClickedFn(e),n.mouseIsPressed=!1},n._onkeydown=e=>{e.repeat||(n.keyIsPressed=!0,n.key=e.key,n.keyCode=e.keyCode,g[n.keyCode]=!0,n._keyPressedFn(e),1==e.key.length&&n._keyTypedFn(e))},n._onkeyup=e=>{n.keyIsPressed=!1,n.key=e.key,n.keyCode=e.keyCode,g[n.keyCode]=!1,n._keyReleasedFn(e)},n.canvas.onmousedown=e=>n._onmousedown(e),n.canvas.onmouseup=e=>n._onmouseup(e),n.canvas.onclick=e=>n._onclick(e),n.keyIsDown=e=>!!g[e],n._ontouchstart=e=>{n.touches=[...e.touches].map(Q),L()&&(n.mouseX=n.touches[0].x,n.mouseY=n.touches[0].y,n.mouseIsPressed=!0,n.mouseButton=n.LEFT,n._mousePressedFn(e)||e.preventDefault()),n._touchStartedFn(e)||e.preventDefault()},n._ontouchmove=e=>{n.touches=[...e.touches].map(Q),L()&&(n.mouseX=n.touches[0].x,n.mouseY=n.touches[0].y,n._mouseDraggedFn(e)||e.preventDefault()),n._touchMovedFn(e)||e.preventDefault()},n._ontouchend=e=>{n.touches=[...e.touches].map(Q),L()&&!n.touches.length&&(n.mouseIsPressed=!1,n._mouseReleasedFn(e)||e.preventDefault()),n._touchEndedFn(e)||e.preventDefault()},n.canvas.ontouchstart=e=>n._ontouchstart(e),n.canvas.ontouchmove=e=>n._ontouchmove(e),n.canvas.ontouchcancel=n.canvas.ontouchend=e=>n._ontouchend(e),n.hasSensorPermission=!window.DeviceOrientationEvent&&!window.DeviceMotionEvent||!(DeviceOrientationEvent.requestPermission||DeviceMotionEvent.requestPermission),n.requestSensorPermissions=()=>{DeviceOrientationEvent.requestPermission&&DeviceOrientationEvent.requestPermission().then((e=>{"granted"==e&&DeviceMotionEvent.requestPermission&&DeviceMotionEvent.requestPermission().then((e=>{"granted"==e&&(n.hasSensorPermission=!0)})).catch(alert)})).catch(alert)};"undefined"!=typeof window&&(window.ondeviceorientation=e=>{n.pRotationX=n.rotationX,n.pRotationY=n.rotationY,n.pRotationZ=n.rotationZ,n.pRelRotationX=n.relRotationX,n.pRelRotationY=n.relRotationY,n.pRelRotationZ=n.relRotationZ,n.rotationX=e.beta*(Math.PI/180),n.rotationY=e.gamma*(Math.PI/180),n.rotationZ=e.alpha*(Math.PI/180),n.relRotationX=[-n.rotationY,-n.rotationX,n.rotationY][1+~~(window.orientation/90)],n.relRotationY=[-n.rotationX,n.rotationY,n.rotationX][1+~~(window.orientation/90)],n.relRotationZ=n.rotationZ},window.ondevicemotion=e=>{if(n.pAccelerationX=n.accelerationX,n.pAccelerationY=n.accelerationY,n.pAccelerationZ=n.accelerationZ,!e.acceleration){let i=((e,t)=>[(e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]),(e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]),(e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15])])((o=n.rotationY,t=[n.cos(o),0,n.sin(o),0,0,1,0,0,-n.sin(o),0,n.cos(o),0,0,0,0,1],a=(e=>[1,0,0,0,0,n.cos(e),-n.sin(e),0,0,n.sin(e),n.cos(e),0,0,0,0,1])(n.rotationX),[t[0]*a[0]+t[1]*a[4]+t[2]*a[8]+t[3]*a[12],t[0]*a[1]+t[1]*a[5]+t[2]*a[9]+t[3]*a[13],t[0]*a[2]+t[1]*a[6]+t[2]*a[10]+t[3]*a[14],t[0]*a[3]+t[1]*a[7]+t[2]*a[11]+t[3]*a[15],t[4]*a[0]+t[5]*a[4]+t[6]*a[8]+t[7]*a[12],t[4]*a[1]+t[5]*a[5]+t[6]*a[9]+t[7]*a[13],t[4]*a[2]+t[5]*a[6]+t[6]*a[10]+t[7]*a[14],t[4]*a[3]+t[5]*a[7]+t[6]*a[11]+t[7]*a[15],t[8]*a[0]+t[9]*a[4]+t[10]*a[8]+t[11]*a[12],t[8]*a[1]+t[9]*a[5]+t[10]*a[9]+t[11]*a[13],t[8]*a[2]+t[9]*a[6]+t[10]*a[10]+t[11]*a[14],t[8]*a[3]+t[9]*a[7]+t[10]*a[11]+t[11]*a[15],t[12]*a[0]+t[13]*a[4]+t[14]*a[8]+t[15]*a[12],t[12]*a[1]+t[13]*a[5]+t[14]*a[9]+t[15]*a[13],t[12]*a[2]+t[13]*a[6]+t[14]*a[10]+t[15]*a[14],t[12]*a[3]+t[13]*a[7]+t[14]*a[11]+t[15]*a[15]]),[0,0,-9.80665]);n.accelerationX=e.accelerationIncludingGravity.x+i[0],n.accelerationY=e.accelerationIncludingGravity.y+i[1],n.accelerationZ=e.accelerationIncludingGravity.z-i[2]}var t,a,o}),n.year=()=>(new Date).getFullYear(),n.day=()=>(new Date).getDay(),n.hour=()=>(new Date).getHours(),n.minute=()=>(new Date).getMinutes(),n.second=()=>(new Date).getSeconds(),n.millis=()=>performance.now()-m,n.storeItem=localStorage.setItem,n.getItem=localStorage.getItem,n.removeItem=localStorage.removeItem,n.clearStorage=localStorage.clear,n._loadFile=(e,t,n)=>{a++;let o={};return fetch(e).then((e=>"json"==n?e.json():"text"==n?e.text():void 0)).then((e=>{a--,Object.assign(o,e),t&&t(e)})),o},n.loadStrings=(e,t)=>n._loadFile(e,t,"text"),n.loadJSON=(e,t)=>n._loadFile(e,t,"json"),n.loadSound=(e,t)=>{a++;let n=new Audio(e);return n.addEventListener("canplaythrough",(()=>{a--,t&&t(n)})),n.load(),n.setVolume=e=>n.volume=e,n.setLoop=e=>n.loop=e,n},n.Element=function(e){this.elt=e},n._elements=[],"global"==e&&(Object.assign(Q5,n),delete Q5.Q5),Q5.Image??=_Q5Image;for(let G of Q5.prototype._methods.init)G.call(n);for(let[V,q]of Object.entries(Q5.prototype))"_"!=V[0]&&"function"==typeof n[V]&&(n[V]=q.bind(n));if("global"==e){let B=Object.getOwnPropertyNames(n);for(let H of B)"function"==typeof n[H]?window[H]=n[H]:Object.defineProperty(window,H,{get:()=>n[H],set:e=>n[H]=e})}function N(){let t="global"==e?window:n,i=["setup","draw","preload","mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let e of i){let a="_"+e+"Fn";n[a]=()=>{},n[a].isPlaceHolder=!0,t[e]?n[a]=t[e]:Object.defineProperty(n,e,{set:e=>{n[a]=e}})}if("graphics"!=e||"image"!=e){n._preloadFn(),m=performance.now(),function e(){if(a>0)return requestAnimationFrame(e);o.save(),n._setupFn(),n._setupDone=!0,o.restore(),n.resetMatrix(),requestAnimationFrame(F)}()}addEventListener("mousemove",(e=>n._onmousemove(e)),!1),addEventListener("keydown",(e=>n._onkeydown(e)),!1),addEventListener("keyup",(e=>n._onkeyup(e)),!1)}"function"==typeof e&&e(n),"global"==e?N():requestAnimationFrame(N)}Q5.Color=class{constructor(e,t,a,n){this.MAGIC=786698,this._r=e,this._g=t,this._b=a,this._a=n,this._h=0,this._s=0,this._v=0,this._hsvInferred=!1}setRed(e){this._r=e,this._hsvInferred=!1}setGreen(e){this._g=e,this._hsvInferred=!1}setBlue(e){this._b=e,this._hsvInferred=!1}setAlpha(e){this._a=e/255,this._hsvInferred=!1}get levels(){return[this._r,this._g,this._b,255*this._a]}_inferHSV(){this._hsvInferred||([this._h,this._s,this._v]=Q5.Color._rgb2hsv(this._r,this._g,this._b),this._hsvInferred=!0)}toString(){return`rgba(${Math.round(this._r)},${Math.round(this._g)},${Math.round(this._b)},${~~(1e3*this._a)/1e3})`}},Q5._instanceCount=0,Q5.Color._rgb2hsv=(e,t,a)=>{let n,o,i,r,s;return n=et?e>a?e:a:t>a?t:a,s=100*o/255,0==s?(i=0,r=0,[i,r,s]):(r=100*(o-n)/o,0==r?(i=0,[i,r,s]):(i=o==e?0+60*(t-a)/(o-n):o==t?120+60*(a-e)/(o-n):240+60*(e-t)/(o-n),[i,r,s]))},Q5.Color._hsv2rgb=(e,t,a)=>{let n,o,i,r,s,l,h,c,d;if(0==t)return n=a,o=a,i=a,[255*n,255*o,255*i];switch(r=e,r>360&&(r=0),r/=60,s=~~r,l=r-s,h=a*(1-t),c=a*(1-t*l),d=a*(1-t*(1-l)),s){case 0:n=a,o=d,i=h;break;case 1:n=c,o=a,i=h;break;case 2:n=h,o=a,i=d;break;case 3:n=h,o=c,i=a;break;case 4:n=d,o=h,i=a;break;default:n=a,o=h,i=c}return[255*n,255*o,255*i]},Q5.Vector=class{constructor(e,t,a,n){this.x=e||0,this.y=t||0,this.z=a||0,this._$=n||window,this._cn=null,this._cnsq=null}set(e,t,a){this.x=e||0,this.y=t||0,this.z=a||0}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(e,t,a){return void 0!==e.x?e:void 0!==t?{x:e,y:t,z:a||0}:{x:e,y:e,z:e}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z,this._cn=Math.sqrt(this._cnsq)}add(){let e=this._arg2v(...arguments);return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}rem(){let e=this._arg2v(...arguments);return this.x%=e.x,this.y%=e.y,this.z%=e.z,this}sub(){let e=this._arg2v(...arguments);return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}mult(){let e=this._arg2v(...arguments);return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}div(){let e=this._arg2v(...arguments);return e.x?this.x/=e.x:this.x=0,e.y?this.y/=e.y:this.y=0,e.z?this.z/=e.z:this.z=0,this}mag(){return this._calcNorm(),this._cn}magSq(){return this._calcNorm(),this._cnsq}dot(){let e=this._arg2v(...arguments);return this.x*e.x+this.y*e.y+this.z*e.z}dist(){let e=this._arg2v(...arguments),t=this.x-e.x,a=this.y-e.y,n=this.z-e.z;return Math.sqrt(t*t+a*a+n*n)}cross(){let e=this._arg2v(...arguments),t=this.y*e.z-this.z*e.y,a=this.z*e.x-this.x*e.z,n=this.x*e.y-this.y*e.x;return this.x=t,this.y=a,this.z=n,this}normalize(){this._calcNorm();let e=this._cn;return 0!=e&&(this.x/=e,this.y/=e,this.z/=e),this._cn=1,this._cnsq=1,this}limit(e){this._calcNorm();let t=this._cn;if(t>e){let a=e/t;this.x*=a,this.y*=a,this.z*=a,this._cn=e,this._cnsq=e*e}return this}setMag(e){this._calcNorm();let t=e/this._cn;return this.x*=t,this.y*=t,this.z*=t,this._cn=e,this._cnsq=e*e,this}heading(){return this._$.atan2(this.y,this.x)}rotate(e){let t=this._$.cos(e),a=this._$.sin(e),n=this.x*t-this.y*a,o=this.x*a+this.y*t;return this.x=n,this.y=o,this}angleBetween(){let e=this._arg2v(...arguments),t=Q5.Vector.cross(this,e);return this._$.atan2(t.mag(),this.dot(e))*Math.sign(t.z||1)}lerp(){let e=[...arguments],t=this._arg2v(...e.slice(0,-1)),a=e[e.length-1];return this.x+=(t.x-this.x)*a,this.y+=(t.y-this.y)*a,this.z+=(t.z-this.z)*a,this}reflect(e){return e.normalize(),this.sub(e.mult(2*this.dot(e)))}array(){return[this.x,this.y,this.z]}equals(e,t){return t??=Number.EPSILON||0,Math.abs(e.x-this.x)e.copy().add(t),Q5.Vector.cross=(e,t)=>e.copy().cross(t),Q5.Vector.dist=(e,t)=>Math.hypot(e.x-t.x,e.y-t.y,e.z-t.z),Q5.Vector.div=(e,t)=>e.copy().div(t),Q5.Vector.dot=(e,t)=>e.copy().dot(t),Q5.Vector.equals=(e,t,a)=>e.equals(t,a),Q5.Vector.lerp=(e,t,a)=>e.copy().lerp(t,a),Q5.Vector.limit=(e,t)=>e.copy().limit(t),Q5.Vector.heading=e=>this._$.atan2(e.y,e.x),Q5.Vector.magSq=e=>e.x*e.x+e.y*e.y+e.z*e.z,Q5.Vector.mag=e=>Math.sqrt(Q5.Vector.magSq(e)),Q5.Vector.mult=(e,t)=>e.copy().mult(t),Q5.Vector.normalize=e=>e.copy().normalize(),Q5.Vector.rem=(e,t)=>e.copy().rem(t),Q5.Vector.sub=(e,t)=>e.copy().sub(t);for(let e of["fromAngle","fromAngles","random2D","random3D"])Q5.Vector[e]=(t,a,n)=>(new Q5.Vector)[e](t,a,n);class _Q5Image extends Q5{constructor(e,t){super("image"),this.createCanvas(e,t),delete this.createCanvas,this._loop=!1}get w(){return this.width}get h(){return this.height}}Q5._friendlyError=(e,t)=>{throw t+": "+e},Q5.prototype._methods={init:[],pre:[],post:[],remove:[]},Q5.prototype.registerMethod=(e,t)=>Q5.prototype._methods[e].push(t),Q5.prototype.registerPreloadMethod=(e,t)=>Q5.prototype[e]=t[e],Q5._validateParameters=()=>!0,"undefined"!=typeof module?module.exports=Q5:window.p5??=Q5,document.addEventListener("DOMContentLoaded",(()=>{Q5._hasGlobal||new Q5("auto")})); +function Q5(e,t){let a=0;if(e||(e="global",a++,setTimeout((()=>a--),32)),"auto"==e){if("object"!=typeof window||!window.setup&&!window.draw)return;e="global"}"global"==e&&(Q5._hasGlobal=!0);let o=this;if(o.canvas=document.createElement("canvas"),o.canvas.id="defaultCanvas"+Q5._instanceCount++,o.canvas.classList.add("p5Canvas","q5Canvas"),o.width=100,o.height=100,o.canvas.width=o.width,o.canvas.height=o.height,o._windowResizedFn=()=>{},"graphics"!=e&&"image"!=e){function n(){t??=document.getElementsByTagName("main")[0],t||(t=document.createElement("main"),document.body.append(t)),o.canvas.parent(t)}o._setupDone=!1,o._resize=()=>{o.frameCount>1&&(o._shouldResize=!0)},t&&"string"==typeof t&&(t=document.getElementById(t)),o.canvas.parent=e=>{"string"==typeof e&&(e=document.getElementById(e)),e.append(o.canvas),"undefined"!=typeof ResizeObserver?(o._ro&&o._ro.disconnect(),o._ro=new ResizeObserver(o._resize),o._ro.observe(t)):0==o.frameCount&&addEventListener("resize",o._resize)},document.body?n():document.addEventListener("DOMContentLoaded",n)}o._q5=!0,o.pixels=[];let i,r=null;o.createCanvas=function(t,a,n,r){if("webgl"==n)throw"webgl renderer is not supported in q5, use '2d'";o.width=t,o.height=a,o.canvas.width=t,o.canvas.height=a,o.canvas.renderer="2d";let s=Object.assign({},Q5.canvasOptions);if(r&&Object.assign(s,r),i=o.ctx=o.drawingContext=o.canvas.getContext("2d",s),"global"==e&&(window.ctx=window.drawingContext=i),Object.assign(o.canvas,s),i.fillStyle="white",i.strokeStyle="black",i.lineCap="round",i.lineJoin="miter",i.textAlign="left",i.save(),"image"!=e){let t=o.displayDensity();"graphics"==e&&(t=this._pixelDensity),o.pixelDensity(Math.ceil(t))}else this._pixelDensity=1;return o.canvas},o._createCanvas=o.createCanvas,o.loadPixels=()=>{r=i.getImageData(0,0,o.canvas.width,o.canvas.height),o.pixels=r.data},o.updatePixels=()=>{null!=r&&i.putImageData(r,0,0)};let s={};function l(e,t){null==f&&(f=document.createElement("canvas").getContext("2d")),t??=e||i.canvas.height,e??=i.canvas.width,f.canvas.width==e&&f.canvas.height==t||(f.canvas.width=e,f.canvas.height=t)}function h(){let e=i.canvas.width*i.canvas.height*4;x&&e==x.length||(x=new Uint8ClampedArray(e))}function c(e){f.clearRect(0,0,f.canvas.width,f.canvas.height),f.filter=e,f.drawImage(i.canvas,0,0),i.save(),i.resetTransform(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.drawImage(f.canvas,0,0),i.restore()}function d(e,t){let a=i.getImageData(0,0,i.canvas.width,i.canvas.height);s[e](a.data,t),i.putImageData(a,0,0)}s[o.THRESHOLD]=(e,t)=>{void 0===t?t=127.5:t*=255;for(let a=0;a=t?255:0}},s[o.GRAY]=e=>{for(let t=0;t{for(let t=0;t{for(let t=0;t{let a=t-1;for(let o=0;o>8)/a,e[o+1]=255*(e[o+1]*t>>8)/a,e[o+2]=255*(e[o+2]*t>>8)/a},s[o.DILATE]=e=>{h(),x.set(e);let[t,a]=[i.canvas.width,i.canvas.height];for(let o=0;o{h(),x.set(e);let[t,a]=[i.canvas.width,i.canvas.height];for(let o=0;o{t=t||1,t=Math.floor(t*o._pixelDensity),h(),x.set(e);let a=2*t+1;let n=function(e){let a=new Float32Array(e),o=.3*t+.8,n=o*o*2;for(let t=0;t{if(!i.filter)return d(e,t);if(l(),"string"==typeof e)c(e);else if(e==o.THRESHOLD){t??=.5,t=Math.max(t,1e-5),c(`saturate(0%) brightness(${Math.floor(.5/t*100)}%) contrast(1000000%)`)}else e==o.GRAY?c("saturate(0%)"):e==o.OPAQUE?(f.fillStyle="black",f.fillRect(0,0,f.canvas.width,f.canvas.height),f.drawImage(i.canvas,0,0),i.save(),i.resetTransform(),i.drawImage(f.canvas,0,0),i.restore()):e==o.INVERT?c("invert(100%)"):e==o.BLUR?c(`blur(${Math.ceil(t*o._pixelDensity/1)||1}px)`):d(e,t)},o.resize=(e,t)=>{l(),f.drawImage(i.canvas,0,0),o.width=e,o.height=t,i.canvas.width=e*o._pixelDensity,i.canvas.height=t*o._pixelDensity,i.save(),i.resetTransform(),i.clearRect(0,0,i.canvas.width,i.canvas.height),i.drawImage(f.canvas,0,0,i.canvas.width,i.canvas.height),i.restore()},o.get=(e,t,a,n)=>{let r=o._pixelDensity||1;if(void 0!==e&&void 0===a){let a=i.getImageData(e*r,t*r,1,1).data;return new o.Color(a[0],a[1],a[2],a[3]/255)}e=(e||0)*r,t=(t||0)*r;let s=a=a||o.width,l=n=n||o.height;a*=r,n*=r;let h=o.createImage(a,n),c=i.getImageData(e,t,a,n);return h.canvas.getContext("2d").putImageData(c,0,0),h._pixelDensity=r,h.width=s,h.height=l,h},o.set=(e,t,a)=>{if(a._q5){let n=o._tint;return o._tint=null,o.image(a,e,t),void(o._tint=n)}let n=o._pixelDensity||1;for(let r=0;r{o._tint=e._q5Color?e:o.color(...arguments)},o.noTint=()=>o._tint=null,o.mask=e=>{i.save(),i.resetTransform();let t=i.globalCompositeOperation;i.globalCompositeOperation="destination-in",i.drawImage(e.canvas,0,0),i.globalCompositeOperation=t,i.restore()},o._save=(e,t,a)=>{if(t=t||"untitled","jpg"==(a=a||"png")||"png"==a)e=e.toDataURL();else{let t="text/plain";"json"==a&&("string"!=typeof e&&(e=JSON.stringify(e)),t="text/json"),e=new Blob([e],{type:t}),e=URL.createObjectURL(e)}let o=document.createElement("a");o.href=e,o.download=t+"."+a,document.body.append(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(o.href)},o.save=(e,t,a)=>{if((!e||"string"==typeof e&&(!t||!a&&t.length<5))&&(a=t,t=e,e=i.canvas),a)return o._save(e,t,a);t?(t=t.split("."),o._save(e,t[0],t.at(-1))):o._save(e)},o.canvas.save=o.save,o.saveCanvas=o.save;let u=null,_=!0,g=[],p={},m=0,f=null,v=null,x=null;if("image"==e)return;function y(){let e={};for(let t in i)"function"!=typeof i[t]&&(e[t]=i[t]);return delete e.canvas,e}o.RGB="rgb",o.RGBA="rgb",o.HSB="hsb",o.CHORD=0,o.PIE=1,o.OPEN=2,o.RADIUS="radius",o.CORNER="corner",o.CORNERS="corners",o.ROUND="round",o.SQUARE="butt",o.PROJECT="square",o.MITER="miter",o.BEVEL="bevel",o.CLOSE=1,o.BLEND="source-over",o.REMOVE="destination-out",o.ADD="lighter",o.DARKEST="darken",o.LIGHTEST="lighten",o.DIFFERENCE="difference",o.SUBTRACT="subtract",o.EXCLUSION="exclusion",o.MULTIPLY="multiply",o.SCREEN="screen",o.REPLACE="copy",o.OVERLAY="overlay",o.HARD_LIGHT="hard-light",o.SOFT_LIGHT="soft-light",o.DODGE="color-dodge",o.BURN="color-burn",o.NORMAL="normal",o.ITALIC="italic",o.BOLD="bold",o.BOLDITALIC="italic bold",o.CENTER="center",o.LEFT="left",o.RIGHT="right",o.TOP="top",o.BOTTOM="bottom",o.BASELINE="alphabetic",o.LANDSCAPE="landscape",o.PORTRAIT="portrait",o.ALT=18,o.BACKSPACE=8,o.CONTROL=17,o.DELETE=46,o.DOWN_ARROW=40,o.ENTER=13,o.ESCAPE=27,o.LEFT_ARROW=37,o.OPTION=18,o.RETURN=13,o.RIGHT_ARROW=39,o.SHIFT=16,o.TAB=9,o.UP_ARROW=38,o.DEGREES="degrees",o.RADIANS="radians",o.HALF_PI=Math.PI/2,o.PI=Math.PI,o.QUARTER_PI=Math.PI/4,o.TAU=2*Math.PI,o.TWO_PI=2*Math.PI,o.THRESHOLD=1,o.GRAY=2,o.OPAQUE=3,o.INVERT=4,o.POSTERIZE=5,o.DILATE=6,o.ERODE=7,o.BLUR=8,o.ARROW="default",o.CROSS="crosshair",o.HAND="pointer",o.MOVE="move",o.TEXT="text",o.VIDEO={video:!0,audio:!1},o.AUDIO={video:!1,audio:!0},o.SHR3=1,o.LCG=2,o.hint=(e,t)=>{o[e]=t},o.frameCount=0,o.deltaTime=16,o.mouseX=0,o.mouseY=0,o.touches=[],o.mouseButton=null,o.keyIsPressed=!1,o.mouseIsPressed=!1,o.key=null,o.keyCode=null,o.accelerationX=0,o.accelerationY=0,o.accelerationZ=0,o.rotationX=0,o.rotationY=0,o.rotationZ=0,o.relRotationX=0,o.relRotationY=0,o.relRotationZ=0,o.pmouseX=0,o.pmouseY=0,o.pAccelerationX=0,o.pAccelerationY=0,o.pAccelerationZ=0,o.pRotationX=0,o.pRotationY=0,o.pRotationZ=0,o.pRelRotationX=0,o.pRelRotationY=0,o.pRelRotationZ=0,Object.defineProperty(o,"deviceOrientation",{get:()=>window.screen?.orientation?.type}),Object.defineProperty(o,"windowWidth",{get:()=>window.innerWidth}),Object.defineProperty(o,"windowHeight",{get:()=>window.innerHeight}),o._colorMode="rgb",o._doStroke=!0,o._doFill=!0,o._strokeSet=!1,o._fillSet=!1,o._tint=null,o._ellipseMode=o.CENTER,o._rectMode=o.CORNER,o._curveDetail=20,o._curveAlpha=0,o._loop=!0,o._textFont="sans-serif",o._textSize=12,o._textLeading=15,o._textLeadDiff=3,o._textStyle="normal",o._pixelDensity=1,o._lastFrameTime=0,o._targetFrameRate=null,o._frameRate=o._fps=60,o.resizeCanvas=(t,a)=>{o.width=t,o.height=a;let n=y();o.canvas.width=t*o._pixelDensity,o.canvas.height=a*o._pixelDensity;for(let e in n)o.ctx[e]=n[e];"image"!=e&&o.pixelDensity(o._pixelDensity)},o.createGraphics=function(e,t){let a=new Q5("graphics");return a._createCanvas.call(o,e,t),a},o.createImage=(e,t)=>new Q5.Image(e,t),o.displayDensity=()=>window.devicePixelRatio,o.pixelDensity=e=>{if(void 0===e)return o._pixelDensity;o._pixelDensity=e;let t=y();o.canvas.width=Math.ceil(o.width*e),o.canvas.height=Math.ceil(o.height*e),o.canvas.style.width=o.width+"px",o.canvas.style.height=o.height+"px";for(let e in t)o.ctx[e]=t[e];return i.scale(o._pixelDensity,o._pixelDensity),o._pixelDensity},o.map=(e,t,a,o,n,i)=>{let r=o+1*(e-t)/(a-t)*(n-o);return i?oe*(1-a)+t*a,o.constrain=(e,t,a)=>Math.min(Math.max(e,t),a),o.dist=function(){return 4==arguments.length?Math.hypot(arguments[0]-arguments[2],arguments[1]-arguments[3]):Math.hypot(arguments[0]-arguments[3],arguments[1]-arguments[4],arguments[2]-arguments[5])},o.norm=(e,t,a)=>o.map(e,t,a,0,1),o.sq=e=>e*e,o.fract=e=>e-Math.floor(e),o.angleMode=e=>o._angleMode=e,o._DEGTORAD=Math.PI/180,o._RADTODEG=180/Math.PI,o.degrees=e=>e*o._RADTODEG,o.radians=e=>e*o._DEGTORAD,o.abs=Math.abs,o.ceil=Math.ceil,o.exp=Math.exp,o.floor=Math.floor,o.log=Math.log,o.mag=Math.hypot,o.max=Math.max,o.min=Math.min,o.round=Math.round,o.pow=Math.pow,o.sqrt=Math.sqrt,o.sin=e=>("degrees"==o._angleMode&&(e=o.radians(e)),Math.sin(e)),o.cos=e=>("degrees"==o._angleMode&&(e=o.radians(e)),Math.cos(e)),o.tan=e=>("degrees"==o._angleMode&&(e=o.radians(e)),Math.tan(e)),o.asin=e=>{let t=Math.asin(e);return"degrees"==o._angleMode&&(t=o.degrees(t)),t},o.acos=e=>{let t=Math.acos(e);return"degrees"==o._angleMode&&(t=o.degrees(t)),t},o.atan=e=>{let t=Math.atan(e);return"degrees"==o._angleMode&&(t=o.degrees(t)),t},o.atan2=(e,t)=>{let a=Math.atan2(e,t);return"degrees"==o._angleMode&&(a=o.degrees(a)),a},o.nf=(e,t,a)=>{let o=e<0,n=e.toString();return o&&(n=n.slice(1)),n=n.padStart(t,"0"),a>0&&(-1==n.indexOf(".")&&(n+="."),n=n.padEnd(t+1+a,"0")),o&&(n="-"+n),n},o.createVector=(e,t,a)=>new Q5.Vector(e,t,a,o),o.curvePoint=(e,t,a,o,n)=>{const i=n*n*n,r=n*n;return e*(-.5*i+r-.5*n)+t*(1.5*i-2.5*r+1)+a*(-1.5*i+2*r+.5*n)+o*(.5*i-.5*r)},o.bezierPoint=(e,t,a,o,n)=>{const i=1-n;return Math.pow(i,3)*e+3*Math.pow(i,2)*n*t+3*i*Math.pow(n,2)*a+Math.pow(n,3)*o},o.curveTangent=(e,t,a,o,n)=>{const i=n*n;return e*(-3*i/2+2*n-.5)+t*(9*i/2-5*n)+a*(-9*i/2+4*n+.5)+o*(3*i/2-n)},o.bezierTangent=(e,t,a,o,n)=>{const i=1-n;return 3*o*Math.pow(n,2)-3*a*Math.pow(n,2)+6*a*i*n-6*t*i*n+3*t*Math.pow(i,2)-3*e*Math.pow(i,2)},o.Color=Q5.ColorRGBA_P3,o.colorMode=e=>{o._colorMode=e,"oklch"==e?o.Color=Q5.ColorOKLCH:"rgb"==e?"srgb"==o.canvas.colorSpace?o.Color=Q5.ColorRGBA:o.Color=Q5.ColorRGBA_P3:"srgb"==e&&(o.Color=Q5.ColorRGBA,o._colorMode="rgb")};let M={aqua:[0,255,255],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],crimson:[220,20,60],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]};function w(e){let t=o._angleMode==o.DEGREES?180:Math.PI,a=2*t;if(0<=e&&e<=a)return e;for(;e<0;)e+=a;for(;e>=t;)e-=a;return e}function R(e,t,a,n,r,s,l,h){if(!o._doFill&&!o._doStroke)return;let c=w(r),d=w(s);if(c>d&&([c,d]=[d,c]),0==c){if(0==d)return;if(o._angleMode==o.DEGREES&&360==d||d==o.TAU)return o.ellipse(e,t,a,n)}i.beginPath();for(let r=0;re._r,o.green=e=>e._g,o.blue=e=>e._b,o.alpha=e=>e._a,o.lightness=e=>100*(.2126*e._r+.7152*e._g+.0722*e._b)/255,o.lerpColor=(e,t,a)=>{if("rgb"==o._colorMode)return new o.Color(o.constrain(o.lerp(e.r,t.r,a),0,255),o.constrain(o.lerp(e.g,t.g,a),0,255),o.constrain(o.lerp(e.b,t.b,a),0,255),o.constrain(o.lerp(e.a,t.a,a),0,255));{let n=t.h-e.h;n>180&&(n-=360),n<-180&&(n+=360);let i=e.h+a*n;return i<0&&(i+=360),i>360&&(i-=360),new o.Color(o.constrain(o.lerp(e.l,t.l,a),0,100),o.constrain(o.lerp(e.c,t.c,a),0,100),i,o.constrain(o.lerp(e.a,t.a,a),0,255))}},o.strokeWeight=e=>{e||(o._doStroke=!1),i.lineWidth=e||1e-4},o.stroke=function(e){if(o._doStroke=!0,o._strokeSet=!0,e._q5Color||(e=o.color(...arguments)),e._a<=0)return o._doStroke=!1;i.strokeStyle=e},o.noStroke=()=>o._doStroke=!1,o.fill=function(e){if(o._doFill=!0,o._fillSet=!0,e._q5Color||(e=o.color(...arguments)),e._a<=0)return o._doFill=!1;i.fillStyle=e},o.noFill=()=>o._doFill=!1,o.smooth=()=>o._smooth=!0,o.noSmooth=()=>o._smooth=!1,o.blendMode=e=>i.globalCompositeOperation=e,o.strokeCap=e=>i.lineCap=e,o.strokeJoin=e=>i.lineJoin=e,o.ellipseMode=e=>o._ellipseMode=e,o.rectMode=e=>o._rectMode=e,o.curveDetail=e=>o._curveDetail=e,o.curveAlpha=e=>o._curveAlpha=e,o.curveTightness=e=>o._curveAlpha=e,o.clear=()=>{i.clearRect(0,0,o.canvas.width,o.canvas.height)},o.background=function(e){if(e._q5)return o.image(e,0,0,o.width,o.height);i.save(),i.resetTransform(),e._q5color||(e=o.color(...arguments)),i.fillStyle=e,i.fillRect(0,0,o.canvas.width,o.canvas.height),i.restore()},o.line=(e,t,a,n)=>{o._doStroke&&(i.beginPath(),i.moveTo(e,t),i.lineTo(a,n),i.stroke())},o.arc=(e,t,a,n,i,r,s,l)=>{if(i==r)return o.ellipse(e,t,a,n);l??=25,s??=o.PIE,o._ellipseMode==o.CENTER?R(e,t,a,n,i,r,s,l):o._ellipseMode==o.RADIUS?R(e,t,2*a,2*n,i,r,s,l):o._ellipseMode==o.CORNER?R(e+a/2,t+n/2,a,n,i,r,s,l):o._ellipseMode==o.CORNERS&&R((e+a)/2,(t+n)/2,a-e,n-t,i,r,s,l)},o.ellipse=(e,t,a,n)=>{n??=a,o._ellipseMode==o.CENTER?S(e,t,a,n):o._ellipseMode==o.RADIUS?S(e,t,2*a,2*n):o._ellipseMode==o.CORNER?S(e+a/2,t+n/2,a,n):o._ellipseMode==o.CORNERS&&S((e+a)/2,(t+n)/2,a-e,n-t)},o.circle=(e,t,a)=>o.ellipse(e,t,a,a),o.point=(e,t)=>{e.x&&(t=e.y,e=e.x),i.beginPath(),i.ellipse(e,t,.4,.4,0,0,o.TAU),i.stroke()},o.rect=(e,t,a,n,i,r,s,l)=>{o._rectMode==o.CENTER?C(e-a/2,t-n/2,a,n,i,r,s,l):o._rectMode==o.RADIUS?C(e-a,t-n,2*a,2*n,i,r,s,l):o._rectMode==o.CORNER?C(e,t,a,n,i,r,s,l):o._rectMode==o.CORNERS&&C(e,t,a-e,n-t,i,r,s,l)},o.square=(e,t,a,n,i,r,s)=>o.rect(e,t,a,a,n,i,r,s),o.beginShape=()=>{b(),i.beginPath(),_=!0},o.beginContour=()=>{i.closePath(),b(),_=!0},o.endContour=()=>{b(),_=!0},o.vertex=(e,t)=>{b(),_?i.moveTo(e,t):i.lineTo(e,t),_=!1},o.bezierVertex=(e,t,a,o,n,r)=>{b(),i.bezierCurveTo(e,t,a,o,n,r)},o.quadraticVertex=(e,t,a,o)=>{b(),i.quadraticCurveTo(e,t,a,o)},o.bezier=(e,t,a,n,i,r,s,l)=>{o.beginShape(),o.vertex(e,t),o.bezierVertex(a,n,i,r,s,l),o.endShape()},o.triangle=(e,t,a,n,i,r)=>{o.beginShape(),o.vertex(e,t),o.vertex(a,n),o.vertex(i,r),o.endShape(o.CLOSE)},o.quad=(e,t,a,n,i,r,s,l)=>{o.beginShape(),o.vertex(e,t),o.vertex(a,n),o.vertex(i,r),o.vertex(s,l),o.endShape(o.CLOSE)},o.endShape=e=>{b(),e&&i.closePath(),o._doFill&&i.fill(),o._doStroke&&i.stroke(),o._doFill||o._doStroke||(i.save(),i.fillStyle="none",i.fill(),i.restore())},o.curveVertex=(e,t)=>{if(g.push([e,t]),g.length<4)return;let a=function(e,t,a,o,n,i,r,s,l,h){function c(e,t,a,o,n,i){let r=Math.pow(o-t,2)+Math.pow(n-a,2);return Math.pow(r,.5*i)+e}let d=[],u=c(0,e,t,a,o,h),_=c(u,a,o,n,i,h),g=c(_,n,i,r,s,h);for(let h=0;h0?(p[e]=1,p[e+1]=0):(p[e]=0,p[e+1]=1));let m=e*p[0]+a*p[1],f=t*p[0]+o*p[1],v=a*p[2]+n*p[3],x=o*p[2]+i*p[3],y=n*p[4]+r*p[5],M=i*p[4]+s*p[5],w=m*p[6]+v*p[7],R=f*p[6]+x*p[7],S=v*p[8]+y*p[9],C=x*p[8]+M*p[9],b=w*p[2]+S*p[3],E=R*p[2]+C*p[3];d.push([b,E])}return d}(...g[g.length-4],...g[g.length-3],...g[g.length-2],...g[g.length-1],o._curveDetail,o._curveAlpha);for(let e=0;e{o.beginShape(),o.curveVertex(e,t),o.curveVertex(a,n),o.curveVertex(i,r),o.curveVertex(s,l),o.endShape()},o.opacity=e=>i.globalAlpha=e,o.translate=(e,t)=>i.translate(e,t),o.rotate=e=>{"degrees"==o._angleMode&&(e=o.radians(e)),i.rotate(e)},o.scale=(e,t)=>{t??=e,i.scale(e,t)},o.applyMatrix=(e,t,a,o,n,r)=>i.transform(e,t,a,o,n,r),o.shearX=e=>i.transform(1,0,o.tan(e),1,0,0),o.shearY=e=>i.transform(1,o.tan(e),0,1,0,0),o.resetMatrix=()=>{i.resetTransform(),i.scale(o._pixelDensity,o._pixelDensity)},o._styleNames=["_doStroke","_doFill","_strokeSet","_fillSet","_tint","_imageMode","_rectMode","_ellipseMode","_textFont","_textLeading","_leadingSet","_textSize","_textAlign","_textBaseline","_textStyle","_textWrap"],o._styles=[],o.push=o.pushMatrix=()=>{i.save();let e={};for(let t of o._styleNames)e[t]=o[t];o._styles.push(e)},o.pop=o.popMatrix=()=>{i.restore();let e=o._styles.pop();for(let t of o._styleNames)o[t]=e[t]},o.imageMode=e=>o._imageMode=e,o.image=(e,t,a,n,r,s,l,h,c)=>{let d=e._q5?e.canvas:e;var u,_;e._q5&&null!=o._tint&&(u=e.canvas.width,_=e.canvas.height,null==v&&(v=document.createElement("canvas").getContext("2d")),_??=u||i.canvas.height,u??=i.canvas.width,v.canvas.width==u&&v.canvas.height==_||(v.canvas.width=u,v.canvas.height=_),v.drawImage(e.canvas,0,0),e.tinted(o._tint)),n||(e._q5||e.width?(n=e.width,r=e.height):(n=e.videoWidth,r=e.videoHeight)),"center"==o._imageMode&&(t-=.5*n,a-=.5*r);let g=e._pixelDensity;s??=0,l??=0,h?h*=g:h=d.width,c?c*=g:c=d.height,i.drawImage(d,s*g,l*g,h,c,t,a,n,r),function(){if(!e._q5||!o._tint)return;let t=e.canvas.getContext("2d");t.save(),t.resetTransform(),t.clearRect(0,0,t.canvas.width,t.canvas.height),t.drawImage(v.canvas,0,0),t.restore()}()},o._incrementPreload=()=>a++,o._decrementPreload=()=>a--,o.loadImage=(e,t)=>{a++;let n=o.createImage(1,1),i=n.canvas.getContext("2d"),r=new window.Image;return r.src=e,r.crossOrigin="Anonymous",r._pixelDensity=1,r.onload=()=>{n.width=i.canvas.width=r.naturalWidth,n.height=i.canvas.height=r.naturalHeight,i.drawImage(r,0,0),a--,t&&t(n)},r.onerror=e=>{throw a--,e},n},o._clearTemporaryBuffers=()=>{f=null,v=null,x=null},o.loadFont=(e,t)=>{a++;let o=e.split("/"),n=o[o.length-1].split(".")[0].replace(" ",""),i=new FontFace(n,"url("+e+")");return document.fonts.add(i),i.load().then((()=>{a--,t&&t(n)})),n},o.textFont=e=>o._textFont=e,o.textSize=e=>{if(void 0===e)return o._textSize;o._textSize=e,o._leadingSet||(o._textLeading=1.25*e,o._textLeadDiff=o._textLeading-e)},o.textLeading=e=>{if(void 0===e)return o._textLeading;o._textLeading=e,o._textLeadDiff=e-o._textSize,o._leadingSet=!0},o.textStyle=e=>o._textStyle=e,o.textAlign=(e,t)=>{i.textAlign=e,t&&(i.textBaseline=t==o.CENTER?"middle":t)},o.textWidth=e=>(i.font=`${o._textStyle} ${o._textSize}px ${o._textFont}`,i.measureText(e).width),o.textAscent=e=>(i.font=`${o._textStyle} ${o._textSize}px ${o._textFont}`,i.measureText(e).actualBoundingBoxAscent),o.textDescent=e=>(i.font=`${o._textStyle} ${o._textSize}px ${o._textFont}`,i.measureText(e).actualBoundingBoxDescent),o._textCache=!0,o._TimedCache=class extends Map{constructor(){super(),this.maxSize=500}set(e,t){t.lastAccessed=Date.now(),super.set(e,t),this.size>this.maxSize&&this.gc()}get(e){const t=super.get(e);return t&&(t.lastAccessed=Date.now()),t}gc(){let e,t=1/0,a=0;for(const[o,n]of this.entries())n.lastAccessed(t&&(o._tic.maxSize=t),void 0!==e&&(o._textCache=e),o._textCache),o.createTextImage=(e,t,a)=>{let n=o._textCache;o._textCache=!0,o._useCache=!0,o.text(e,0,0,t,a),o._useCache=!1;let i=E(e,t,a);return o._textCache=n,o._tic.get(i)},o.text=(e,t,a,n,r)=>{if(void 0===e)return;if(e=e.toString(),!o._doFill&&!o._doStroke)return;let s,l,h,c,d,u,_,g,p=1,m=i.getTransform(),f=o._useCache||o._textCache&&(0!=m.b||0!=m.c);if(f){if(c=E(e,n,r),l=o._tic.get(c),l)return void o.textImage(l,t,a);h=o.createGraphics.call(o,1,1),s=h.ctx,p=o._pixelDensity}else s=i,d=t,u=a;s.font=`${o._textStyle} ${o._textSize}px ${o._textFont}`;let v=e.split("\n");if(f){d=0,u=o._textLeading*v.length;let t=s.measureText(" ");_=t.fontBoundingBoxAscent,g=t.fontBoundingBoxDescent,r??=u+g,h.resizeCanvas(Math.ceil(s.measureText(e).width),Math.ceil(r)),s.fillStyle=i.fillStyle,s.strokeStyle=i.strokeStyle,s.lineWidth=i.lineWidth}let x=s.fillStyle;o._fillSet||(s.fillStyle="black");for(let e=0;er));e++);o._fillSet||(s.fillStyle=x),f&&(l=h.get(),l._ascent=_,l._descent=g,o._tic.set(c,l),o.textImage(l,t,a))},o.textImage=(e,t,a)=>{let n=o._imageMode;o._imageMode="corner","center"==i.textAlign?t-=.5*e.width:"right"==i.textAlign&&(t-=e.width),"alphabetic"==i.textBaseline&&(a-=o._textLeading),"middle"==i.textBaseline?a-=e._descent+.5*e._ascent+o._textLeadDiff:"bottom"==i.textBaseline?a-=e._ascent+e._descent+o._textLeadDiff:"top"==i.textBaseline&&(a-=e._descent+o._textLeadDiff),o.image(e,t,a),o._imageMode=n};var D,I=4095,T=4,A=.5,O=e=>.5*(1-Math.cos(e*Math.PI));o.noise=(e,t,a)=>{if(t??=0,a??=0,null==D){D=new Array(4096);for(var o=0;o<4096;o++)D[o]=Math.random()}e<0&&(e=-e),t<0&&(t=-t),a<0&&(a=-a);for(var n,i,r,s,l,h=Math.floor(e),c=Math.floor(t),d=Math.floor(a),u=e-h,_=t-c,g=a-d,p=0,m=.5,f=0;f=1&&(h++,u--),(_*=2)>=1&&(c++,_--),(g*=2)>=1&&(d++,g--)}return p},o.noiseDetail=(e,t)=>{e>0&&(T=e),t>0&&(A=t)};const P=()=>{let e,t,a=4294967295;return{setSeed(o){e=t=(null==o?Math.random()*a:o)>>>0},getSeed:()=>t,rand:()=>(e^=e<<17,e^=e>>13,e^=e<<5,(e>>>0)/a)}};let k=P();k.setSeed(),o.noiseSeed=e=>{let t=void 0===e?4294967295*Math.random():e;D||(D=new Float32Array(4096));for(var a=0;a<4096;a++)t^=t<<17,t^=t>>13,t^=t<<5,D[a]=(t>>>0)/4294967295},o.randomSeed=e=>k.setSeed(e),o.random=(e,t)=>void 0===e?k.rand():"number"==typeof e?void 0!==t?k.rand()*(t-e)+e:k.rand()*e:e[Math.trunc(e.length*k.rand())],o.randomGenerator=e=>{e==o.LCG?k=(()=>{const e=4294967296;let t,a;return{setSeed(o){a=t=(null==o?Math.random()*e:o)>>>0},getSeed:()=>t,rand:()=>(a=(1664525*a+1013904223)%e,a/e)}})():e==o.SHR3&&(k=P()),k.setSeed()};var z=new function(){var e,t,a,o=new Array(128),n=new Array(256),i=new Array(128),r=new Array(128),s=new Array(256),l=new Array(256),h=()=>4294967296*k.rand()-2147483648,c=()=>.5+2.328306e-10*(h()<<0),d=()=>{for(var t,n,s,l,d=3.44262;;){if(t=a*i[e],0==e){do{s=c(),l=c(),t=.2904764*-Math.log(s),n=-Math.log(l)}while(n+n0?d+t:-d-t}if(r[e]+c()*(r[e-1]-r[e]){for(var a;;){if(0==e)return 7.69711-Math.log(c());if(a=t*s[e],l[e]+c()*(l[e-1]-l[e])(a=h(),e=127&a,Math.abs(a)(t=h()>>>0){var e,t,a=2147483648,h=4294967296,c=3.442619855899,d=c,u=.00991256303526217,_=7.697117470131487,g=_,p=.003949659822581572;for(e=u/Math.exp(-.5*c*c),o[0]=Math.floor(c/e*a),o[1]=0,i[0]=e/a,i[127]=c/a,r[0]=1,r[127]=Math.exp(-.5*c*c),t=126;t>=1;t--)c=Math.sqrt(-2*Math.log(u/c+Math.exp(-.5*c*c))),o[t+1]=Math.floor(c/d*a),d=c,r[t]=Math.exp(-.5*c*c),i[t]=c/a;for(e=p/Math.exp(-_),n[0]=Math.floor(_/e*h),n[1]=0,s[0]=e/h,s[255]=_/h,l[0]=1,l[255]=Math.exp(-_),t=254;t>=1;t--)_=-Math.log(p/_+Math.exp(-_)),n[t+1]=Math.floor(_/g*h),g=_,l[t]=Math.exp(-_),s[t]=_/h}};function F(){let e=performance.now();if(o._loop)u=o._targetFrameRate?setTimeout(F,1e3/o._targetFrameRate):requestAnimationFrame(F);else if(o.frameCount>0)return;if(u&&0!=o.frameCount){if(e-o._lastFrameTime<1e3/(o._targetFrameRate||60)-5)return}o.deltaTime=e-o._lastFrameTime,o._frameRate=1e3/o.deltaTime,o.frameCount++,o._shouldResize&&(o._windowResizedFn(),o._shouldResize=!1);for(let e of Q5.prototype._methods.pre)e.call(o);b(),_=!0,i.save(),o._drawFn();for(let e of Q5.prototype._methods.post)e.call(o);i.restore(),o.resetMatrix();let t=performance.now();o._fps=Math.round(1e3/(t-e)),o._lastFrameTime=e,o.pmouseX=o.mouseX,o.pmouseY=o.mouseY}function L(e){const t=o.canvas.getBoundingClientRect(),a=o.canvas.scrollWidth/o.width||1,n=o.canvas.scrollHeight/o.height||1;return{x:(e.clientX-t.left)/a,y:(e.clientY-t.top)/n,id:e.identifier}}function Q(){return o._touchStartedFn.isPlaceHolder&&o._touchMovedFn.isPlaceHolder&&o._touchEndedFn.isPlaceHolder}z.hasInit=!1,o.randomGaussian=(e,t)=>(z.hasInit||(z.zigset(),z.hasInit=!0),z.RNOR()*t+e),o.randomExponential=()=>(z.hasInit||(z.zigset(),z.hasInit=!0),z.REXP()),o.Element=function(e){this.elt=e},o._elements=[],o.createCapture=e=>{var t=document.createElement("video");return t.playsinline="playsinline",t.autoplay="autoplay",navigator.mediaDevices.getUserMedia(e).then((e=>{t.srcObject=e})),t.style.position="absolute",t.style.opacity=1e-5,t.style.zIndex=-1e3,document.body.append(t),t},o.print=console.log,o.noLoop=()=>{o._loop=!1,u=null},o.loop=()=>{o._loop=!0,null==u&&F()},o.redraw=()=>F(),o.remove=()=>{o.noLoop(),o.canvas.remove()},o.frameRate=e=>(e&&(o._targetFrameRate=e),o._frameRate),o.getFrameRate=()=>o._frameRate,o.getFPS=()=>o._fps,o.storeItem=localStorage.setItem,o.getItem=localStorage.getItem,o.removeItem=localStorage.removeItem,o.clearStorage=localStorage.clear,o._updateMouse=e=>{let t=o.canvas.getBoundingClientRect(),a=o.canvas.scrollWidth/o.width||1,n=o.canvas.scrollHeight/o.height||1;o.mouseX=(e.clientX-t.left)/a,o.mouseY=(e.clientY-t.top)/n},o._onmousedown=e=>{o._updateMouse(e),o.mouseIsPressed=!0,o.mouseButton=[o.LEFT,o.CENTER,o.RIGHT][e.button],o._mousePressedFn(e)},o._onmousemove=e=>{o._updateMouse(e),o.mouseIsPressed?o._mouseDraggedFn(e):o._mouseMovedFn(e)},o._onmouseup=e=>{o._updateMouse(e),o.mouseIsPressed=!1,o._mouseReleasedFn(e)},o._onclick=e=>{o._updateMouse(e),o.mouseIsPressed=!0,o._mouseClickedFn(e),o.mouseIsPressed=!1},o.cursor=(e,t,a)=>{let n="";e.includes(".")&&(e=`url("${e}")`,n=", auto"),void 0!==t&&(e+=" "+t+" "+a),o.canvas.style.cursor=e+n},o.noCursor=()=>{o.canvas.style.cursor="none"},o._onkeydown=e=>{e.repeat||(o.keyIsPressed=!0,o.key=e.key,o.keyCode=e.keyCode,p[o.keyCode]=!0,o._keyPressedFn(e),1==e.key.length&&o._keyTypedFn(e))},o._onkeyup=e=>{o.keyIsPressed=!1,o.key=e.key,o.keyCode=e.keyCode,p[o.keyCode]=!1,o._keyReleasedFn(e)},o.canvas.onmousedown=e=>o._onmousedown(e),o.canvas.onmouseup=e=>o._onmouseup(e),o.canvas.onclick=e=>o._onclick(e),o.keyIsDown=e=>!!p[e],o._ontouchstart=e=>{o.touches=[...e.touches].map(L),Q()&&(o.mouseX=o.touches[0].x,o.mouseY=o.touches[0].y,o.mouseIsPressed=!0,o.mouseButton=o.LEFT,o._mousePressedFn(e)||e.preventDefault()),o._touchStartedFn(e)||e.preventDefault()},o._ontouchmove=e=>{o.touches=[...e.touches].map(L),Q()&&(o.mouseX=o.touches[0].x,o.mouseY=o.touches[0].y,o._mouseDraggedFn(e)||e.preventDefault()),o._touchMovedFn(e)||e.preventDefault()},o._ontouchend=e=>{o.touches=[...e.touches].map(L),Q()&&!o.touches.length&&(o.mouseIsPressed=!1,o._mouseReleasedFn(e)||e.preventDefault()),o._touchEndedFn(e)||e.preventDefault()},o.canvas.ontouchstart=e=>o._ontouchstart(e),o.canvas.ontouchmove=e=>o._ontouchmove(e),o.canvas.ontouchcancel=o.canvas.ontouchend=e=>o._ontouchend(e),o.hasSensorPermission=!window.DeviceOrientationEvent&&!window.DeviceMotionEvent||!(DeviceOrientationEvent.requestPermission||DeviceMotionEvent.requestPermission),o.requestSensorPermissions=()=>{DeviceOrientationEvent.requestPermission&&DeviceOrientationEvent.requestPermission().then((e=>{"granted"==e&&DeviceMotionEvent.requestPermission&&DeviceMotionEvent.requestPermission().then((e=>{"granted"==e&&(o.hasSensorPermission=!0)})).catch(alert)})).catch(alert)};"undefined"!=typeof window&&(window.ondeviceorientation=e=>{o.pRotationX=o.rotationX,o.pRotationY=o.rotationY,o.pRotationZ=o.rotationZ,o.pRelRotationX=o.relRotationX,o.pRelRotationY=o.relRotationY,o.pRelRotationZ=o.relRotationZ,o.rotationX=e.beta*(Math.PI/180),o.rotationY=e.gamma*(Math.PI/180),o.rotationZ=e.alpha*(Math.PI/180),o.relRotationX=[-o.rotationY,-o.rotationX,o.rotationY][Math.trunc(window.orientation/90)+1],o.relRotationY=[-o.rotationX,o.rotationY,o.rotationX][Math.trunc(window.orientation/90)+1],o.relRotationZ=o.rotationZ},window.ondevicemotion=e=>{if(o.pAccelerationX=o.accelerationX,o.pAccelerationY=o.accelerationY,o.pAccelerationZ=o.accelerationZ,!e.acceleration){let i=((e,t)=>[(e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]),(e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]),(e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11])/(e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15])])((n=o.rotationY,t=[o.cos(n),0,o.sin(n),0,0,1,0,0,-o.sin(n),0,o.cos(n),0,0,0,0,1],a=(e=>[1,0,0,0,0,o.cos(e),-o.sin(e),0,0,o.sin(e),o.cos(e),0,0,0,0,1])(o.rotationX),[t[0]*a[0]+t[1]*a[4]+t[2]*a[8]+t[3]*a[12],t[0]*a[1]+t[1]*a[5]+t[2]*a[9]+t[3]*a[13],t[0]*a[2]+t[1]*a[6]+t[2]*a[10]+t[3]*a[14],t[0]*a[3]+t[1]*a[7]+t[2]*a[11]+t[3]*a[15],t[4]*a[0]+t[5]*a[4]+t[6]*a[8]+t[7]*a[12],t[4]*a[1]+t[5]*a[5]+t[6]*a[9]+t[7]*a[13],t[4]*a[2]+t[5]*a[6]+t[6]*a[10]+t[7]*a[14],t[4]*a[3]+t[5]*a[7]+t[6]*a[11]+t[7]*a[15],t[8]*a[0]+t[9]*a[4]+t[10]*a[8]+t[11]*a[12],t[8]*a[1]+t[9]*a[5]+t[10]*a[9]+t[11]*a[13],t[8]*a[2]+t[9]*a[6]+t[10]*a[10]+t[11]*a[14],t[8]*a[3]+t[9]*a[7]+t[10]*a[11]+t[11]*a[15],t[12]*a[0]+t[13]*a[4]+t[14]*a[8]+t[15]*a[12],t[12]*a[1]+t[13]*a[5]+t[14]*a[9]+t[15]*a[13],t[12]*a[2]+t[13]*a[6]+t[14]*a[10]+t[15]*a[14],t[12]*a[3]+t[13]*a[7]+t[14]*a[11]+t[15]*a[15]]),[0,0,-9.80665]);o.accelerationX=e.accelerationIncludingGravity.x+i[0],o.accelerationY=e.accelerationIncludingGravity.y+i[1],o.accelerationZ=e.accelerationIncludingGravity.z-i[2]}var t,a,n}),o.year=()=>(new Date).getFullYear(),o.day=()=>(new Date).getDay(),o.hour=()=>(new Date).getHours(),o.minute=()=>(new Date).getMinutes(),o.second=()=>(new Date).getSeconds(),o.millis=()=>performance.now()-m,o._loadFile=(e,t,o)=>{a++;let n={};return fetch(e).then((e=>"json"==o?e.json():"text"==o?e.text():void 0)).then((e=>{a--,Object.assign(n,e),t&&t(e)})),n},o.loadStrings=(e,t)=>o._loadFile(e,t,"text"),o.loadJSON=(e,t)=>o._loadFile(e,t,"json"),o.loadSound=(e,t)=>{a++;let o=new Audio(e);return o.addEventListener("canplaythrough",(()=>{a--,t&&t(o)})),o.load(),o.setVolume=e=>o.volume=e,o.setLoop=e=>o.loop=e,o},"global"==e&&(Object.assign(Q5,o),delete Q5.Q5),Q5.Image??=_Q5Image;for(let N of Q5.prototype._methods.init)N.call(o);for(let[B,$]of Object.entries(Q5.prototype))"_"!=B[0]&&"function"==typeof o[B]&&(o[B]=$.bind(o));if("global"==e){let V=Object.getOwnPropertyNames(o);for(let G of V)"function"==typeof o[G]?window[G]=o[G]:Object.defineProperty(window,G,{get:()=>o[G],set:e=>o[G]=e})}function q(){let t="global"==e?window:o,n=["setup","draw","preload","mouseMoved","mousePressed","mouseReleased","mouseDragged","mouseClicked","keyPressed","keyReleased","keyTyped","touchStarted","touchMoved","touchEnded","windowResized"];for(let e of n){let a="_"+e+"Fn";o[a]=()=>{},o[a].isPlaceHolder=!0,t[e]?o[a]=t[e]:Object.defineProperty(o,e,{set:e=>{o[a]=e}})}if("graphics"!=e||"image"!=e){o._preloadFn(),m=performance.now(),function e(){if(a>0)return requestAnimationFrame(e);o._setupFn(),i||o.createCanvas(100,100),o._setupDone=!0,i.restore(),o.resetMatrix(),requestAnimationFrame(F)}()}addEventListener("mousemove",(e=>o._onmousemove(e)),!1),addEventListener("keydown",(e=>o._onkeydown(e)),!1),addEventListener("keyup",(e=>o._onkeyup(e)),!1)}"function"==typeof e&&e(o),"global"==e?q():requestAnimationFrame(q)}Q5.Color=class{constructor(){this._q5Color=!0}},Q5.ColorOKLCH=class extends Q5.Color{constructor(e,t,a,o){super(),this.l=e,this.c=t,this.h=a,this.a=o??1}toString(){return`color(oklch ${this.l} ${this.c} ${this.h} / ${this.a})`}},Q5.ColorRGBA=class extends Q5.Color{constructor(e,t,a,o){super(),this.r=e,this.g=t,this.b=a,this.a=o??255}setRed(e){this.r=e}setGreen(e){this.g=e}setBlue(e){this.b=e}setAlpha(e){this.a=e}get levels(){return[this.r,this.g,this.b,this.a]}toString(){return`rgb(${this.r} ${this.g} ${this.b} / ${this.a/255})`}},Q5.ColorRGBA_P3=class extends Q5.ColorRGBA{constructor(e,t,a,o){super(e,t,a,o),this._edited=!0}get r(){return this._r}set r(e){this._r=e,this._edited=!0}get g(){return this._g}set g(e){this._g=e,this._edited=!0}get b(){return this._b}set b(e){this._b=e,this._edited=!0}get a(){return this._a}set a(e){this._a=e,this._edited=!0}toString(){if(this._edited){let e=(this._r/255).toFixed(3),t=(this._g/255).toFixed(3),a=(this._b/255).toFixed(3),o=(this._a/255).toFixed(3);this._css=`color(display-p3 ${e} ${t} ${a} / ${o})`,this._edited=!1}return this._css}},Q5.Vector=class{constructor(e,t,a,o){this.x=e||0,this.y=t||0,this.z=a||0,this._$=o||window,this._cn=null,this._cnsq=null}set(e,t,a){this.x=e||0,this.y=t||0,this.z=a||0}copy(){return new Q5.Vector(this.x,this.y,this.z)}_arg2v(e,t,a){return void 0!==e.x?e:void 0!==t?{x:e,y:t,z:a||0}:{x:e,y:e,z:e}}_calcNorm(){this._cnsq=this.x*this.x+this.y*this.y+this.z*this.z,this._cn=Math.sqrt(this._cnsq)}add(){let e=this._arg2v(...arguments);return this.x+=e.x,this.y+=e.y,this.z+=e.z,this}rem(){let e=this._arg2v(...arguments);return this.x%=e.x,this.y%=e.y,this.z%=e.z,this}sub(){let e=this._arg2v(...arguments);return this.x-=e.x,this.y-=e.y,this.z-=e.z,this}mult(){let e=this._arg2v(...arguments);return this.x*=e.x,this.y*=e.y,this.z*=e.z,this}div(){let e=this._arg2v(...arguments);return e.x?this.x/=e.x:this.x=0,e.y?this.y/=e.y:this.y=0,e.z?this.z/=e.z:this.z=0,this}mag(){return this._calcNorm(),this._cn}magSq(){return this._calcNorm(),this._cnsq}dot(){let e=this._arg2v(...arguments);return this.x*e.x+this.y*e.y+this.z*e.z}dist(){let e=this._arg2v(...arguments),t=this.x-e.x,a=this.y-e.y,o=this.z-e.z;return Math.sqrt(t*t+a*a+o*o)}cross(){let e=this._arg2v(...arguments),t=this.y*e.z-this.z*e.y,a=this.z*e.x-this.x*e.z,o=this.x*e.y-this.y*e.x;return this.x=t,this.y=a,this.z=o,this}normalize(){this._calcNorm();let e=this._cn;return 0!=e&&(this.x/=e,this.y/=e,this.z/=e),this._cn=1,this._cnsq=1,this}limit(e){this._calcNorm();let t=this._cn;if(t>e){let a=e/t;this.x*=a,this.y*=a,this.z*=a,this._cn=e,this._cnsq=e*e}return this}setMag(e){this._calcNorm();let t=e/this._cn;return this.x*=t,this.y*=t,this.z*=t,this._cn=e,this._cnsq=e*e,this}heading(){return this._$.atan2(this.y,this.x)}rotate(e){let t=this._$.cos(e),a=this._$.sin(e),o=this.x*t-this.y*a,n=this.x*a+this.y*t;return this.x=o,this.y=n,this}angleBetween(){let e=this._arg2v(...arguments),t=Q5.Vector.cross(this,e);return this._$.atan2(t.mag(),this.dot(e))*Math.sign(t.z||1)}lerp(){let e=[...arguments],t=this._arg2v(...e.slice(0,-1)),a=e[e.length-1];return this.x+=(t.x-this.x)*a,this.y+=(t.y-this.y)*a,this.z+=(t.z-this.z)*a,this}reflect(e){return e.normalize(),this.sub(e.mult(2*this.dot(e)))}array(){return[this.x,this.y,this.z]}equals(e,t){return t??=Number.EPSILON||0,Math.abs(e.x-this.x)e.copy().add(t),Q5.Vector.cross=(e,t)=>e.copy().cross(t),Q5.Vector.dist=(e,t)=>Math.hypot(e.x-t.x,e.y-t.y,e.z-t.z),Q5.Vector.div=(e,t)=>e.copy().div(t),Q5.Vector.dot=(e,t)=>e.copy().dot(t),Q5.Vector.equals=(e,t,a)=>e.equals(t,a),Q5.Vector.lerp=(e,t,a)=>e.copy().lerp(t,a),Q5.Vector.limit=(e,t)=>e.copy().limit(t),Q5.Vector.heading=e=>this._$.atan2(e.y,e.x),Q5.Vector.magSq=e=>e.x*e.x+e.y*e.y+e.z*e.z,Q5.Vector.mag=e=>Math.sqrt(Q5.Vector.magSq(e)),Q5.Vector.mult=(e,t)=>e.copy().mult(t),Q5.Vector.normalize=e=>e.copy().normalize(),Q5.Vector.rem=(e,t)=>e.copy().rem(t),Q5.Vector.sub=(e,t)=>e.copy().sub(t);for(let e of["fromAngle","fromAngles","random2D","random3D"])Q5.Vector[e]=(t,a,o)=>(new Q5.Vector)[e](t,a,o);class _Q5Image extends Q5{constructor(e,t){super("image"),this.createCanvas(e,t),delete this.createCanvas,this._loop=!1}get w(){return this.width}get h(){return this.height}}Q5.canvasOptions={alpha:!1,desynchronized:!0,colorSpace:"display-p3"},"undefined"!=typeof matchMedia&&matchMedia("(dynamic-range: high) and (color-gamut: p3)").matches||(Q5.canvasOptions.colorSpace="srgb"),Q5._instanceCount=0,Q5._friendlyError=(e,t)=>{throw t+": "+e},Q5._validateParameters=()=>!0,Q5.prototype._methods={init:[],pre:[],post:[],remove:[]},Q5.prototype.registerMethod=(e,t)=>Q5.prototype._methods[e].push(t),Q5.prototype.registerPreloadMethod=(e,t)=>Q5.prototype[e]=t[e],"undefined"!=typeof module?module.exports=Q5:window.p5??=Q5,document.addEventListener("DOMContentLoaded",(()=>{Q5._hasGlobal||new Q5("auto")}));