@guinetik/gcanvas 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/gcanvas.es.js +25656 -0
  2. package/dist/gcanvas.es.min.js +1 -0
  3. package/dist/gcanvas.umd.js +1 -0
  4. package/dist/gcanvas.umd.min.js +1 -0
  5. package/package.json +23 -6
  6. package/src/game/objects/index.js +1 -0
  7. package/src/game/objects/spritesheet.js +260 -0
  8. package/src/game/ui/theme.js +6 -0
  9. package/src/io/keys.js +9 -1
  10. package/src/math/boolean.js +481 -0
  11. package/src/math/index.js +1 -0
  12. package/.github/workflows/release.yaml +0 -70
  13. package/.jshintrc +0 -4
  14. package/.vscode/settings.json +0 -22
  15. package/CLAUDE.md +0 -310
  16. package/blackhole.jpg +0 -0
  17. package/demo.png +0 -0
  18. package/demos/CNAME +0 -1
  19. package/demos/animations.html +0 -31
  20. package/demos/basic.html +0 -38
  21. package/demos/baskara.html +0 -31
  22. package/demos/bezier.html +0 -35
  23. package/demos/beziersignature.html +0 -29
  24. package/demos/blackhole.html +0 -28
  25. package/demos/blob.html +0 -35
  26. package/demos/coordinates.html +0 -698
  27. package/demos/cube3d.html +0 -23
  28. package/demos/demos.css +0 -303
  29. package/demos/dino.html +0 -42
  30. package/demos/easing.html +0 -28
  31. package/demos/events.html +0 -195
  32. package/demos/fluent.html +0 -647
  33. package/demos/fluid-simple.html +0 -22
  34. package/demos/fluid.html +0 -37
  35. package/demos/fractals.html +0 -36
  36. package/demos/gameobjects.html +0 -626
  37. package/demos/genart.html +0 -26
  38. package/demos/gendream.html +0 -26
  39. package/demos/group.html +0 -36
  40. package/demos/home.html +0 -587
  41. package/demos/index.html +0 -376
  42. package/demos/isometric.html +0 -34
  43. package/demos/js/animations.js +0 -452
  44. package/demos/js/basic.js +0 -204
  45. package/demos/js/baskara.js +0 -751
  46. package/demos/js/bezier.js +0 -692
  47. package/demos/js/beziersignature.js +0 -241
  48. package/demos/js/blackhole/accretiondisk.obj.js +0 -379
  49. package/demos/js/blackhole/blackhole.obj.js +0 -318
  50. package/demos/js/blackhole/index.js +0 -409
  51. package/demos/js/blackhole/particle.js +0 -56
  52. package/demos/js/blackhole/starfield.obj.js +0 -218
  53. package/demos/js/blob.js +0 -2276
  54. package/demos/js/coordinates.js +0 -840
  55. package/demos/js/cube3d.js +0 -789
  56. package/demos/js/dino.js +0 -1420
  57. package/demos/js/easing.js +0 -477
  58. package/demos/js/fluent.js +0 -183
  59. package/demos/js/fluid-simple.js +0 -253
  60. package/demos/js/fluid.js +0 -527
  61. package/demos/js/fractals.js +0 -931
  62. package/demos/js/fractalworker.js +0 -93
  63. package/demos/js/gameobjects.js +0 -176
  64. package/demos/js/genart.js +0 -268
  65. package/demos/js/gendream.js +0 -209
  66. package/demos/js/group.js +0 -140
  67. package/demos/js/info-toggle.js +0 -25
  68. package/demos/js/isometric.js +0 -863
  69. package/demos/js/kerr.js +0 -1556
  70. package/demos/js/lavalamp.js +0 -590
  71. package/demos/js/layout.js +0 -354
  72. package/demos/js/mondrian.js +0 -285
  73. package/demos/js/opacity.js +0 -275
  74. package/demos/js/painter.js +0 -484
  75. package/demos/js/particles-showcase.js +0 -514
  76. package/demos/js/particles.js +0 -299
  77. package/demos/js/patterns.js +0 -397
  78. package/demos/js/penrose/artifact.js +0 -69
  79. package/demos/js/penrose/blackhole.js +0 -121
  80. package/demos/js/penrose/constants.js +0 -73
  81. package/demos/js/penrose/game.js +0 -943
  82. package/demos/js/penrose/lore.js +0 -278
  83. package/demos/js/penrose/penrosescene.js +0 -892
  84. package/demos/js/penrose/ship.js +0 -216
  85. package/demos/js/penrose/sounds.js +0 -211
  86. package/demos/js/penrose/voidparticle.js +0 -55
  87. package/demos/js/penrose/voidscene.js +0 -258
  88. package/demos/js/penrose/voidship.js +0 -144
  89. package/demos/js/penrose/wormhole.js +0 -46
  90. package/demos/js/pipeline.js +0 -555
  91. package/demos/js/plane3d.js +0 -256
  92. package/demos/js/platformer.js +0 -1579
  93. package/demos/js/scene.js +0 -304
  94. package/demos/js/scenes.js +0 -320
  95. package/demos/js/schrodinger.js +0 -410
  96. package/demos/js/schwarzschild.js +0 -1023
  97. package/demos/js/shapes.js +0 -628
  98. package/demos/js/space/alien.js +0 -171
  99. package/demos/js/space/boom.js +0 -98
  100. package/demos/js/space/boss.js +0 -353
  101. package/demos/js/space/buff.js +0 -73
  102. package/demos/js/space/bullet.js +0 -102
  103. package/demos/js/space/constants.js +0 -85
  104. package/demos/js/space/game.js +0 -1884
  105. package/demos/js/space/hud.js +0 -112
  106. package/demos/js/space/laserbeam.js +0 -179
  107. package/demos/js/space/lightning.js +0 -277
  108. package/demos/js/space/minion.js +0 -192
  109. package/demos/js/space/missile.js +0 -212
  110. package/demos/js/space/player.js +0 -430
  111. package/demos/js/space/powerup.js +0 -90
  112. package/demos/js/space/starfield.js +0 -58
  113. package/demos/js/space/starpower.js +0 -90
  114. package/demos/js/spacetime.js +0 -559
  115. package/demos/js/sphere3d.js +0 -229
  116. package/demos/js/sprite.js +0 -473
  117. package/demos/js/svgtween.js +0 -204
  118. package/demos/js/tde/accretiondisk.js +0 -471
  119. package/demos/js/tde/blackhole.js +0 -219
  120. package/demos/js/tde/blackholescene.js +0 -209
  121. package/demos/js/tde/config.js +0 -59
  122. package/demos/js/tde/index.js +0 -820
  123. package/demos/js/tde/jets.js +0 -290
  124. package/demos/js/tde/lensedstarfield.js +0 -154
  125. package/demos/js/tde/tdestar.js +0 -297
  126. package/demos/js/tde/tidalstream.js +0 -372
  127. package/demos/js/tde_old/blackhole.obj.js +0 -354
  128. package/demos/js/tde_old/debris.obj.js +0 -791
  129. package/demos/js/tde_old/flare.obj.js +0 -239
  130. package/demos/js/tde_old/index.js +0 -448
  131. package/demos/js/tde_old/star.obj.js +0 -812
  132. package/demos/js/tiles.js +0 -312
  133. package/demos/js/tweendemo.js +0 -79
  134. package/demos/js/visibility.js +0 -102
  135. package/demos/kerr.html +0 -28
  136. package/demos/lavalamp.html +0 -27
  137. package/demos/layouts.html +0 -37
  138. package/demos/logo.svg +0 -4
  139. package/demos/loop.html +0 -84
  140. package/demos/mondrian.html +0 -32
  141. package/demos/og_image.png +0 -0
  142. package/demos/opacity.html +0 -36
  143. package/demos/painter.html +0 -39
  144. package/demos/particles-showcase.html +0 -28
  145. package/demos/particles.html +0 -24
  146. package/demos/patterns.html +0 -33
  147. package/demos/penrose-game.html +0 -31
  148. package/demos/pipeline.html +0 -737
  149. package/demos/plane3d.html +0 -24
  150. package/demos/platformer.html +0 -43
  151. package/demos/scene.html +0 -33
  152. package/demos/scenes.html +0 -96
  153. package/demos/schrodinger.html +0 -27
  154. package/demos/schwarzschild.html +0 -27
  155. package/demos/shapes.html +0 -16
  156. package/demos/space.html +0 -85
  157. package/demos/spacetime.html +0 -27
  158. package/demos/sphere3d.html +0 -24
  159. package/demos/sprite.html +0 -18
  160. package/demos/svgtween.html +0 -29
  161. package/demos/tde.html +0 -28
  162. package/demos/tiles.html +0 -28
  163. package/demos/transforms.html +0 -400
  164. package/demos/tween.html +0 -45
  165. package/demos/visibility.html +0 -33
  166. package/docs/README.md +0 -230
  167. package/docs/api/FluidSystem.md +0 -173
  168. package/docs/concepts/architecture-overview.md +0 -204
  169. package/docs/concepts/coordinate-system.md +0 -384
  170. package/docs/concepts/lifecycle.md +0 -255
  171. package/docs/concepts/rendering-pipeline.md +0 -279
  172. package/docs/concepts/shapes-vs-gameobjects.md +0 -187
  173. package/docs/concepts/tde-zorder.md +0 -106
  174. package/docs/concepts/two-layer-architecture.md +0 -229
  175. package/docs/fluid-dynamics.md +0 -99
  176. package/docs/getting-started/first-game.md +0 -354
  177. package/docs/getting-started/hello-world.md +0 -269
  178. package/docs/getting-started/installation.md +0 -175
  179. package/docs/modules/collision/README.md +0 -453
  180. package/docs/modules/fluent/README.md +0 -1075
  181. package/docs/modules/game/README.md +0 -303
  182. package/docs/modules/isometric-camera.md +0 -210
  183. package/docs/modules/isometric.md +0 -275
  184. package/docs/modules/painter/README.md +0 -328
  185. package/docs/modules/particle/README.md +0 -559
  186. package/docs/modules/shapes/README.md +0 -221
  187. package/docs/modules/shapes/base/euclidian.md +0 -123
  188. package/docs/modules/shapes/base/geometry2d.md +0 -204
  189. package/docs/modules/shapes/base/renderable.md +0 -215
  190. package/docs/modules/shapes/base/shape.md +0 -262
  191. package/docs/modules/shapes/base/transformable.md +0 -243
  192. package/docs/modules/shapes/hierarchy.md +0 -218
  193. package/docs/modules/state/README.md +0 -577
  194. package/docs/modules/util/README.md +0 -99
  195. package/docs/modules/util/camera3d.md +0 -412
  196. package/docs/modules/util/scene3d.md +0 -395
  197. package/index.html +0 -17
  198. package/jsdoc.json +0 -50
  199. package/scripts/build-demo.js +0 -69
  200. package/scripts/bundle4llm.js +0 -276
  201. package/scripts/clearconsole.js +0 -48
  202. package/test/math/orbital.test.js +0 -61
  203. package/test/math/tensor.test.js +0 -114
  204. package/test/particle/emitter.test.js +0 -204
  205. package/test/particle/particle-system.test.js +0 -310
  206. package/test/particle/particle.test.js +0 -116
  207. package/test/particle/updaters.test.js +0 -386
  208. package/test/setup.js +0 -120
  209. package/test/shapes/euclidian.test.js +0 -44
  210. package/test/shapes/geometry.test.js +0 -86
  211. package/test/shapes/group.test.js +0 -86
  212. package/test/shapes/rectangle.test.js +0 -64
  213. package/test/shapes/transform.test.js +0 -379
  214. package/test/util/camera3d.test.js +0 -428
  215. package/test/util/scene3d.test.js +0 -352
  216. package/vite.config.js +0 -50
  217. package/vitest.config.js +0 -13
@@ -0,0 +1 @@
1
+ var _Grad,_grad3,_p,_perm,_gradP,_F2,_G2,_F3,_G3,_Noise_static,fade_fn,lerp_fn,_components,_dimension,_name,_signature,_coordinates,__colors,__effects,__img,__lines,__opacity,__shapes,__text,_Painter_static,checkInitialized_fn,_prevWidth,_prevHeight,_parent,_go,_refs,_state,_shapes,_motions,_FluentGO_instances,addShapeInstance_fn,normalizeShapeOpts_fn,processMotions_fn,applyMotion_fn,_parent2,_layerGO,_group,_refs2,_state2,_parent3,_scene,_refs3,_state3,_lastGO,_game,_scenes,_currentScene,_refs4,_state4,_plugins,_canvas,_FluentGame_instances,createCanvas_fn,createContext_fn,__ctx,__output,__ctx2,__output2,__ctx3,__analyzer,__dataArray,__frequencyData,__ctx4,__masterGain,__initialized,__defProp=Object.defineProperty,__typeError=t=>{throw TypeError(t)},__defNormalProp=(t,e,i)=>e in t?__defProp(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,__publicField=(t,e,i)=>__defNormalProp(t,"symbol"!=typeof e?e+"":e,i),__accessCheck=(t,e,i)=>e.has(t)||__typeError("Cannot "+i),__privateGet=(t,e,i)=>(__accessCheck(t,e,"read from private field"),i?i.call(t):e.get(t)),__privateAdd=(t,e,i)=>e.has(t)?__typeError("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,i),__privateSet=(t,e,i,s)=>(__accessCheck(t,e,"write to private field"),s?s.call(t,i):e.set(t,i),i),__privateMethod=(t,e,i)=>(__accessCheck(t,e,"access private method"),i);class ZOrderedCollection{constructor(t={}){this.children=[],this.sortByZIndex=t.sortByZIndex||!0,this._zOrderDirty=!1}add(t){return this.children.includes(t)?(console.warn("Object is already in this collection"),t):(this.children.push(t),t.parent=this._owner||this,this.sortByZIndex&&(this._zOrderDirty=!0,void 0!==t.zIndex&&null!==t.zIndex||(t.zIndex=this.children.length-1)),t)}remove(t){const e=this.children.indexOf(t);return-1!==e&&(this.children.splice(e,1),t.parent=null,!0)}clear(){this.children.forEach(t=>{t.parent=null}),this.children=[]}bringToFront(t){const e=this.children.indexOf(t);if(-1!==e)if(this.sortByZIndex){let e=!0;for(const i of this.children)if(i!==t&&(i.zIndex||0)>=(t.zIndex||0)){e=!1;break}e||(t.zIndex=Number.MAX_SAFE_INTEGER,this._zOrderDirty=!0,this._normalizeZIndices())}else e!==this.children.length-1&&(this.children.splice(e,1),this.children.push(t));else this.add(t)}sendToBack(t){const e=this.children.indexOf(t);if(-1===e)return this.children.unshift(t),void(t.parent=this._owner||this);if(this.sortByZIndex){let e=!0;for(const i of this.children)if(i!==t&&(i.zIndex||0)<=(t.zIndex||0)){e=!1;break}e||(t.zIndex=Number.MIN_SAFE_INTEGER,this._zOrderDirty=!0,this._normalizeZIndices())}else 0!==e&&(this.children.splice(e,1),this.children.unshift(t))}bringForward(t){const e=this.children.indexOf(t);if(-1!==e&&e!==this.children.length-1)if(this.sortByZIndex){const e=[...this.children].sort((t,e)=>(t.zIndex||0)-(e.zIndex||0)),i=e.indexOf(t);if(i<e.length-1){const s=e[i+1],n=s.zIndex||0,a=t.zIndex||0;n-a>1?t.zIndex=a+Math.floor((n-a)/2):(t.zIndex=n,s.zIndex=a),this._zOrderDirty=!0,this._normalizeZIndices()}}else{const i=this.children[e+1];this.children[e+1]=t,this.children[e]=i}}sendBackward(t){const e=this.children.indexOf(t);if(!(e<=0))if(this.sortByZIndex){const e=[...this.children].sort((t,e)=>(t.zIndex||0)-(e.zIndex||0)),i=e.indexOf(t);if(i>0){const s=e[i-1],n=s.zIndex||0,a=t.zIndex||0;a-n>1?t.zIndex=n+Math.floor((a-n)/2):(t.zIndex=n,s.zIndex=a),this._zOrderDirty=!0,this._normalizeZIndices()}}else{const i=this.children[e-1];this.children[e-1]=t,this.children[e]=i}}_normalizeZIndices(){if(this.children.length<=1)return;if(this.children.some(t=>(t.zIndex||0)>1e3||(t.zIndex||0)<-1e3)){[...this.children].sort((t,e)=>(t.zIndex||0)-(e.zIndex||0)).forEach((t,e)=>{t.zIndex=10*e}),this._zOrderDirty=!0}}getSortedChildren(){return this.sortByZIndex&&this._zOrderDirty&&(this.children.sort((t,e)=>(t.zIndex||0)-(e.zIndex||0)),this._zOrderDirty=!1),this.children}}const _Position=class t{static calculate(e,i,s,n=10,a=0,r=0){const o=i.width||0,h=i.height||0,l=s.width||0,c=s.height||0,u=s.x||0,d=s.y||0;let _,p,g,f;switch(e){case t.TOP_LEFT:_=u-l/2+n+o/2,p=d-c/2+n+h/2,g="left",f="top";break;case t.TOP_CENTER:_=u,p=d-c/2+n+h/2,g="center",f="top";break;case t.TOP_RIGHT:_=u+l/2-n-o/2,p=d-c/2+n+h/2,g="right",f="top";break;case t.CENTER_LEFT:_=u-l/2+n+o/2,p=d,g="left",f="middle";break;case t.CENTER:_=u,p=d,g="center",f="middle";break;case t.CENTER_RIGHT:_=u+l/2-n-o/2,p=d,g="right",f="middle";break;case t.BOTTOM_LEFT:_=u-l/2+n+o/2,p=d+c/2-n-h/2,g="left",f="bottom";break;case t.BOTTOM_CENTER:_=u,p=d+c/2-n-h/2,g="center",f="bottom";break;case t.BOTTOM_RIGHT:_=u+l/2-n-o/2,p=d+c/2-n-h/2,g="right",f="bottom";break;default:_=u-l/2+n+o/2,p=d-c/2+n+h/2,g="left",f="top"}return _+=a,p+=r,{x:_,y:p,align:g,baseline:f}}static calculateAbsolute(e,i,s,n=10,a=0,r=0){const o={width:s.width,height:s.height,x:s.width/2,y:s.height/2};return t.calculate(e,i,o,n,a,r)}};__publicField(_Position,"TOP_LEFT","top-left"),__publicField(_Position,"TOP_CENTER","top-center"),__publicField(_Position,"TOP_RIGHT","top-right"),__publicField(_Position,"CENTER_LEFT","center-left"),__publicField(_Position,"CENTER","center"),__publicField(_Position,"CENTER_RIGHT","center-right"),__publicField(_Position,"BOTTOM_LEFT","bottom-left"),__publicField(_Position,"BOTTOM_CENTER","bottom-center"),__publicField(_Position,"BOTTOM_RIGHT","bottom-right");let Position=_Position;function applyLayout(t,e,i={}){const s=i.offsetX??0,n=i.offsetY??0,a=i.transform;return t.forEach((t,i)=>{if(i<e.length){const r=e[i];if(a){const e=a(r);t.x=e.x+s,t.y=e.y+n}else t.x=r.x+s,t.y=r.y+n}}),t}function horizontalLayout(t,e={}){const i=e.spacing??10,s=e.padding??0,n=e.align??"start",a=e.centerItems??!0;let r=s,o=0;const h=[];for(const e of t)o=Math.max(o,e.height??0);for(let e=0;e<t.length;e++){const s=t[e],l=s.width??0,c=s.height??0,u=a?r+l/2:r;let d;switch(n){case"center":d=(o-c)/2;break;case"end":d=o-c;break;default:d=0}h.push({x:u,y:d}),r+=l,e<t.length-1&&(r+=i)}return{positions:h,width:r+s,height:o+2*s}}function verticalLayout(t,e={}){const i=e.spacing??10,s=e.padding??0,n=e.align??"start",a=e.centerItems??!0;let r=s,o=0;const h=[];for(const e of t)o=Math.max(o,e.width??0);for(let e=0;e<t.length;e++){const s=t[e],l=s.width??0,c=s.height??0,u=a?r+c/2:r;let d;switch(n){case"center":d=(o-l)/2;break;case"end":d=o-l;break;default:d=0}h.push({x:d,y:u}),r+=c,e<t.length-1&&(r+=i)}return{positions:h,width:o+2*s,height:r+s}}function tileLayout(t,e={}){if(0===t.length)return{positions:[],width:0,height:0};const i=e.columns??4,s=e.spacing??10,n=e.padding??0,a=e.centerItems??!0,r=t[0].width??0,o=t[0].height??0,h=Math.ceil(t.length/i),l=[],c=i*r+(i-1)*s+2*n,u=h*o+(h-1)*s+2*n;let d=n,_=n,p=0;for(let e=0;e<t.length;e++){const t=a?d+r/2:d,e=a?_+o/2:_;l.push({x:t,y:e}),p++,p<i?d+=r+s:(p=0,d=n,_+=o+s)}return{positions:l,width:c,height:u}}function gridLayout(t,e={}){if(0===t.length)return{positions:[],width:0,height:0};const i=e.columns??4,s=e.spacing??10,n=e.padding??0,a=e.centerItems??!0,r=new Array(i).fill(0),o=[];t.forEach((t,e)=>{const s=e%i,n=Math.floor(e/i),a=t.width??0,h=t.height??0;r[s]=Math.max(r[s],a),void 0===o[n]?o[n]=h:o[n]=Math.max(o[n],h)});const h=[];let l=n,c=n,u=0;for(let e=0;e<t.length;e++){const d=t[e],_=d.width??0,p=d.height??0,g=o[Math.floor(e/i)],f=a?l+_/2:l,m=a?c+p/2:c;h.push({x:f,y:m}),u++,u<i?l+=r[u-1]+s:(u=0,l=n,c+=g+s)}return{positions:h,width:2*n+r.reduce((t,e)=>t+e,0)+s*(i-1),height:2*n+o.reduce((t,e)=>t+e,0)+s*(o.length-1),cols:i,rows:o.length}}class TaskManager{constructor(t){this.worker=new Worker(t),this.nextTaskId=1,this.pendingTasks=new Map,this.worker.onmessage=this.handleMessage.bind(this)}handleMessage(t){const{taskId:e,status:i,result:s,error:n}=t.data;if(this.pendingTasks.has(e)){const{resolve:t,reject:a}=this.pendingTasks.get(e);"complete"===i?t(s):"error"===i&&a(new Error(n)),this.pendingTasks.delete(e)}}runTask(t,e){return new Promise((i,s)=>{const n=this.nextTaskId++;this.pendingTasks.set(n,{resolve:i,reject:s}),this.worker.postMessage({taskId:n,taskName:t,params:e})})}terminate(){this.worker.terminate()}}class Camera3D{constructor(t={}){this.rotationX=t.rotationX??0,this.rotationY=t.rotationY??0,this.rotationZ=t.rotationZ??0,this.x=t.x??0,this.y=t.y??0,this.z=t.z??0,this._initialRotationX=this.rotationX,this._initialRotationY=this.rotationY,this._initialRotationZ=this.rotationZ,this._initialX=this.x,this._initialY=this.y,this._initialZ=this.z,this.perspective=t.perspective??800,this.sensitivity=t.sensitivity??.005,this.minRotationX=t.minRotationX??-1.5,this.maxRotationX=t.maxRotationX??1.5,this.clampX=t.clampX??!0,this.autoRotate=t.autoRotate??!1,this.autoRotateSpeed=t.autoRotateSpeed??.5,this.autoRotateAxis=t.autoRotateAxis??"y",this.inertia=t.inertia??!1,this.friction=t.friction??.92,this.velocityScale=t.velocityScale??1,this._velocityX=0,this._velocityY=0,this._lastDeltaX=0,this._lastDeltaY=0,this._lastMoveTime=0,this._isDragging=!1,this._lastMouseX=0,this._lastMouseY=0,this._canvas=null,this._boundHandlers=null,this._followTarget=null,this._followOffset={x:0,y:0,z:0},this._followLookAt=!0,this._followLerp=.1,this._targetX=null,this._targetY=null,this._targetZ=null,this._targetRotationX=null,this._targetRotationY=null,this._positionLerp=.05}project(t,e,i){if(t-=this.x,e-=this.y,i-=this.z,0!==this.rotationZ){const i=Math.cos(this.rotationZ),s=Math.sin(this.rotationZ),n=t;t=n*i-e*s,e=n*s+e*i}const s=Math.cos(this.rotationY),n=Math.sin(this.rotationY),a=t*s-i*n,r=t*n+i*s,o=Math.cos(this.rotationX),h=Math.sin(this.rotationX),l=e*o-r*h,c=e*h+r*o,u=this.perspective/(this.perspective+c);return{x:a*u,y:l*u,z:c,scale:u}}projectAll(t){return t.map(t=>this.project(t.x,t.y,t.z))}update(t){var e,i,s;if(this._followTarget){const t=this._followTarget,n=(t.x??0)+this._followOffset.x,a=(t.y??0)+this._followOffset.y,r=(t.z??0)+this._followOffset.z;if(this.x+=(n-this.x)*this._followLerp,this.y+=(a-this.y)*this._followLerp,this.z+=(r-this.z)*this._followLerp,this._followLookAt){const t=(null==(e=this._followLookAtTarget)?void 0:e.x)??0,n=(null==(i=this._followLookAtTarget)?void 0:i.y)??0,a=(null==(s=this._followLookAtTarget)?void 0:s.z)??0,r=t-this.x,o=n-this.y,h=a-this.z,l=Math.sqrt(r*r+h*h),c=Math.atan2(r,h),u=Math.atan2(-o,l);this.rotationY+=this._angleDiff(this.rotationY,c)*this._followLerp,this.rotationX+=(u-this.rotationX)*this._followLerp}}else if(null!==this._targetX){const t=this._positionLerp;this.x+=(this._targetX-this.x)*t,this.y+=(this._targetY-this.y)*t,this.z+=(this._targetZ-this.z)*t,null!==this._targetRotationX&&(this.rotationX+=(this._targetRotationX-this.rotationX)*t),null!==this._targetRotationY&&(this.rotationY+=this._angleDiff(this.rotationY,this._targetRotationY)*t);Math.abs(this._targetX-this.x)+Math.abs(this._targetY-this.y)+Math.abs(this._targetZ-this.z)<.1&&(this.x=this._targetX,this.y=this._targetY,this.z=this._targetZ,this._targetX=null,this._targetY=null,this._targetZ=null,this._targetRotationX=null,this._targetRotationY=null)}if(!this.inertia||this._isDragging||this._followTarget||(Math.abs(this._velocityX)>1e-4||Math.abs(this._velocityY)>1e-4)&&(this.rotationY+=this._velocityY,this.rotationX+=this._velocityX,this.clampX&&(this.rotationX=Math.max(this.minRotationX,Math.min(this.maxRotationX,this.rotationX))),this._velocityX*=this.friction,this._velocityY*=this.friction,Math.abs(this._velocityX)<1e-4&&(this._velocityX=0),Math.abs(this._velocityY)<1e-4&&(this._velocityY=0)),this.autoRotate&&!this._isDragging&&!this._followTarget){if(!(Math.abs(this._velocityX)>.001||Math.abs(this._velocityY)>.001)){const e=this.autoRotateSpeed*t;switch(this.autoRotateAxis){case"x":this.rotationX+=e;break;case"y":this.rotationY+=e;break;case"z":this.rotationZ+=e}}}}_angleDiff(t,e){let i=e-t;for(;i>Math.PI;)i-=2*Math.PI;for(;i<-Math.PI;)i+=2*Math.PI;return i}enableMouseControl(t,e={}){this._canvas&&this.disableMouseControl(),this._canvas=t;const i=e.invertX?-1:1,s=e.invertY?-1:1;return this._boundHandlers={mousedown:t=>{this._isDragging=!0,this._lastMouseX=t.clientX,this._lastMouseY=t.clientY,this._lastMoveTime=performance.now(),this._velocityX=0,this._velocityY=0},mousemove:t=>{if(!this._isDragging)return;const e=t.clientX-this._lastMouseX,n=t.clientY-this._lastMouseY,a=e*this.sensitivity*i,r=n*this.sensitivity*s;this.rotationY+=a,this.rotationX+=r,this.clampX&&(this.rotationX=Math.max(this.minRotationX,Math.min(this.maxRotationX,this.rotationX))),this.inertia&&(this._lastDeltaX=r,this._lastDeltaY=a,this._lastMoveTime=performance.now()),this._lastMouseX=t.clientX,this._lastMouseY=t.clientY},mouseup:()=>{if(this.inertia&&this._isDragging){performance.now()-this._lastMoveTime<50&&(this._velocityX=this._lastDeltaX*this.velocityScale,this._velocityY=this._lastDeltaY*this.velocityScale)}this._isDragging=!1},mouseleave:()=>{if(this.inertia&&this._isDragging){performance.now()-this._lastMoveTime<50&&(this._velocityX=this._lastDeltaX*this.velocityScale,this._velocityY=this._lastDeltaY*this.velocityScale)}this._isDragging=!1},touchstart:t=>{1===t.touches.length&&(this._isDragging=!0,this._lastMouseX=t.touches[0].clientX,this._lastMouseY=t.touches[0].clientY,this._lastMoveTime=performance.now(),this._velocityX=0,this._velocityY=0)},touchmove:t=>{if(!this._isDragging||1!==t.touches.length)return;t.preventDefault();const e=t.touches[0].clientX-this._lastMouseX,n=t.touches[0].clientY-this._lastMouseY,a=e*this.sensitivity*i,r=n*this.sensitivity*s;this.rotationY+=a,this.rotationX+=r,this.clampX&&(this.rotationX=Math.max(this.minRotationX,Math.min(this.maxRotationX,this.rotationX))),this.inertia&&(this._lastDeltaX=r,this._lastDeltaY=a,this._lastMoveTime=performance.now()),this._lastMouseX=t.touches[0].clientX,this._lastMouseY=t.touches[0].clientY},touchend:()=>{if(this.inertia&&this._isDragging){performance.now()-this._lastMoveTime<50&&(this._velocityX=this._lastDeltaX*this.velocityScale,this._velocityY=this._lastDeltaY*this.velocityScale)}this._isDragging=!1},dblclick:()=>{this.reset()}},t.addEventListener("mousedown",this._boundHandlers.mousedown),t.addEventListener("mousemove",this._boundHandlers.mousemove),t.addEventListener("mouseup",this._boundHandlers.mouseup),t.addEventListener("mouseleave",this._boundHandlers.mouseleave),t.addEventListener("touchstart",this._boundHandlers.touchstart),t.addEventListener("touchmove",this._boundHandlers.touchmove,{passive:!1}),t.addEventListener("touchend",this._boundHandlers.touchend),t.addEventListener("dblclick",this._boundHandlers.dblclick),this}disableMouseControl(){return this._canvas&&this._boundHandlers&&(this._canvas.removeEventListener("mousedown",this._boundHandlers.mousedown),this._canvas.removeEventListener("mousemove",this._boundHandlers.mousemove),this._canvas.removeEventListener("mouseup",this._boundHandlers.mouseup),this._canvas.removeEventListener("mouseleave",this._boundHandlers.mouseleave),this._canvas.removeEventListener("touchstart",this._boundHandlers.touchstart),this._canvas.removeEventListener("touchmove",this._boundHandlers.touchmove),this._canvas.removeEventListener("touchend",this._boundHandlers.touchend),this._canvas.removeEventListener("dblclick",this._boundHandlers.dblclick)),this._canvas=null,this._boundHandlers=null,this}reset(){return this.rotationX=this._initialRotationX,this.rotationY=this._initialRotationY,this.rotationZ=this._initialRotationZ,this.x=this._initialX,this.y=this._initialY,this.z=this._initialZ,this._velocityX=0,this._velocityY=0,this._followTarget=null,this._targetX=null,this._targetY=null,this._targetZ=null,this}stopInertia(){return this._velocityX=0,this._velocityY=0,this}setPosition(t,e,i){return this.x=t,this.y=e,this.z=i,this}moveTo(t,e,i,s={}){return this._targetX=t,this._targetY=e,this._targetZ=i,this._targetRotationX=s.rotationX??null,this._targetRotationY=s.rotationY??null,this._positionLerp=s.lerp??.05,this}follow(t,e={}){return this._followTarget=t,this._followOffset={x:e.offsetX??0,y:e.offsetY??0,z:e.offsetZ??0},this._followLookAt=e.lookAt??!0,this._followLookAtTarget=e.lookAtTarget??null,this._followLerp=e.lerp??.1,this}unfollow(t=!1){return this._followTarget=null,t&&this.moveTo(this._initialX,this._initialY,this._initialZ,{rotationX:this._initialRotationX,rotationY:this._initialRotationY,lerp:.05}),this}isFollowing(){return null!==this._followTarget}setRotation(t,e,i=0){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i=0){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this.clampX&&(this.rotationX=Math.max(this.minRotationX,Math.min(this.maxRotationX,this.rotationX))),this}isDragging(){return this._isDragging}lookAt(t,e,i){const s=t-this.x,n=e-this.y,a=i-this.z,r=Math.sqrt(s*s+a*a);return this.rotationY=Math.atan2(s,a),this.rotationX=Math.atan2(-n,r),this}}class Camera2D{constructor(t={}){this.x=0,this.y=0,this.target=t.target??null,this.deadzone=t.deadzone??null,this.lerp=t.lerp??.1,this.zoom=t.zoom??1,this.bounds=t.bounds??null,this.viewportWidth=t.viewportWidth??800,this.viewportHeight=t.viewportHeight??600,this.offsetX=t.offsetX??0,this.offsetY=t.offsetY??0,this._shakeIntensity=0,this._shakeDuration=0,this._shakeTime=0,this._shakeOffsetX=0,this._shakeOffsetY=0,this._initialX=this.x,this._initialY=this.y}update(t){if(!this.target)return;const e=this.target.x+this.offsetX,i=this.target.y+this.offsetY,s=this.x+this.viewportWidth/2,n=this.y+this.viewportHeight/2;let a=this.x,r=this.y;if(this.deadzone){const t=this.deadzone.width/2,o=this.deadzone.height/2;e<s-t?a=e+t-this.viewportWidth/2:e>s+t&&(a=e-t-this.viewportWidth/2),i<n-o?r=i+o-this.viewportHeight/2:i>n+o&&(r=i-o-this.viewportHeight/2)}else a=e-this.viewportWidth/2,r=i-this.viewportHeight/2;if(this.x+=(a-this.x)*this.lerp,this.y+=(r-this.y)*this.lerp,this.bounds){const t=this.bounds.maxX-this.viewportWidth,e=this.bounds.maxY-this.viewportHeight;this.x=Math.max(this.bounds.minX,Math.min(this.x,t)),this.y=Math.max(this.bounds.minY,Math.min(this.y,e))}this._updateShake(t)}_updateShake(t){if(this._shakeDuration>0&&this._shakeTime<this._shakeDuration){this._shakeTime+=t;const e=1-this._shakeTime/this._shakeDuration;this._shakeOffsetX=2*(Math.random()-.5)*this._shakeIntensity*e,this._shakeOffsetY=2*(Math.random()-.5)*this._shakeIntensity*e,this._shakeTime>=this._shakeDuration&&(this._shakeOffsetX=0,this._shakeOffsetY=0,this._shakeDuration=0,this._shakeTime=0)}}getOffset(){return{x:this.x+this._shakeOffsetX,y:this.y+this._shakeOffsetY}}setPosition(t,e){return this.x=t,this.y=e,this}setTarget(t){return this.target=t,this}shake(t,e){return this._shakeIntensity=t,this._shakeDuration=e,this._shakeTime=0,this}stopShake(){return this._shakeIntensity=0,this._shakeDuration=0,this._shakeTime=0,this._shakeOffsetX=0,this._shakeOffsetY=0,this}isVisible(t){if(!t)return!1;const e=this.x+this.viewportWidth,i=this.y+this.viewportHeight,s=t.x+(t.width||0),n=t.y+(t.height||0);return!(t.x>e||s<this.x||t.y>i||n<this.y)}screenToWorld(t,e){return{x:t/this.zoom+this.x,y:e/this.zoom+this.y}}worldToScreen(t,e){return{x:(t-this.x)*this.zoom,y:(e-this.y)*this.zoom}}getWorldBounds(){return{x:this.x,y:this.y,width:this.viewportWidth/this.zoom,height:this.viewportHeight/this.zoom}}reset(){return this.x=this._initialX,this.y=this._initialY,this.stopShake(),this}isShaking(){return this._shakeDuration>0&&this._shakeTime<this._shakeDuration}}class IsometricCamera{constructor(t={}){this.angle=t.angle??0,this._targetAngle=this.angle,this.rotationStep=t.rotationStep??Math.PI/2,this.animationDuration=t.animationDuration??.4,this.easingType=t.easing??"easeInOutCubic",this._animating=!1,this._animationProgress=0,this._startAngle=0,this._onRotationStart=null,this._onRotationEnd=null}rotateRight(){return this._animating||this._startRotation(this._targetAngle+this.rotationStep),this}rotateLeft(){return this._animating||this._startRotation(this._targetAngle-this.rotationStep),this}rotateTo(t){return this._animating||this._startRotation(t),this}setAngle(t){return this.angle=t,this._targetAngle=t,this._animating=!1,this}_startRotation(t){this._startAngle=this.angle,this._targetAngle=t,this._animationProgress=0,this._animating=!0,this._onRotationStart&&this._onRotationStart(this._startAngle,this._targetAngle)}update(t){if(this._animating)if(this._animationProgress+=t/this.animationDuration,this._animationProgress>=1)this._animationProgress=1,this.angle=this._targetAngle,this._animating=!1,this._onRotationEnd&&this._onRotationEnd(this.angle);else{const t=this._ease(this._animationProgress);this.angle=this._startAngle+(this._targetAngle-this._startAngle)*t}}_ease(t){switch(this.easingType){case"linear":return t;case"easeInQuad":return t*t;case"easeOutQuad":return t*(2-t);case"easeInOutQuad":return t<.5?2*t*t:(4-2*t)*t-1;case"easeInCubic":return t*t*t;case"easeOutCubic":return--t*t*t+1;case"easeInOutCubic":default:return t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1;case"easeOutBack":const e=1.70158;return 1+(e+1)*Math.pow(t-1,3)+e*Math.pow(t-1,2)}}isAnimating(){return this._animating}getAngleDegrees(){return 180*this.angle/Math.PI%360}getNormalizedAngle(){let t=this.angle%(2*Math.PI);return t<0&&(t+=2*Math.PI),t}onRotationStart(t){return this._onRotationStart=t,this}onRotationEnd(t){return this._onRotationEnd=t,this}reset(){return this.setAngle(0),this}}class Random{static symmetric(t,e,i,s,n=1,a="topleft"){const r="centered"===a?e:e+s/2;return{x:("centered"===a?t:t+i/2)+(Math.random()-.5)*i*n,y:r+(Math.random()-.5)*s*n}}static pointInBox(t,e,i,s,n="topleft"){return"centered"===n?{x:t+(Math.random()-.5)*i,y:e+(Math.random()-.5)*s}:{x:t+Math.random()*i,y:e+Math.random()*s}}static centered(t,e,i,s,n=50,a="topleft"){const r="centered"===a?e:e+s/2;return{x:("centered"===a?t:t+i/2)+2*(Math.random()-.5)*n,y:r+2*(Math.random()-.5)*n}}static gaussian(t,e,i,s,n=40,a="topleft"){const r="centered"===a?e:e+s/2;return{x:("centered"===a?t:t+i/2)+Random._gaussian(0,n),y:r+Random._gaussian(0,n)}}static radial(t,e,i,s,n=100,a="topleft"){const r="centered"===a?t:t+i/2,o="centered"===a?e:e+s/2,h=Math.random()*Math.PI*2,l=Math.random()*n;return{x:r+Math.cos(h)*l,y:o+Math.sin(h)*l}}static pick(t){return t[Math.floor(Math.random()*t.length)]}static pickOther(t,e){const i=t.filter(t=>t!==e);if(0!==i.length)return Random.pick(i)}static float(t,e){return t+Math.random()*(e-t)}static int(t,e){return Math.floor(Random.float(t,e+1))}static chance(t=.5){return Math.random()<t}static coin(){return Math.random()<.5}static _gaussian(t=0,e=1){let i=1-Math.random(),s=1-Math.random();return t+e*Math.sqrt(-2*Math.log(i))*Math.cos(2*Math.PI*s)}}class Complex{constructor(t,e=0){this.real=t,this.imag=e}static fromPolar(t,e){return new Complex(t*Math.cos(e),t*Math.sin(e))}add(t){return new Complex(this.real+t.real,this.imag+t.imag)}subtract(t){return new Complex(this.real-t.real,this.imag-t.imag)}multiply(t){return new Complex(this.real*t.real-this.imag*t.imag,this.real*t.imag+this.imag*t.real)}divide(t){return new Complex(this.real/t,this.imag/t)}scale(t){return new Complex(this.real*t,this.imag*t)}abs(){return Math.sqrt(this.real*this.real+this.imag*this.imag)}}class Fractals{static applyColorScheme(t,e,i,s,n,a){const r=(null==e?void 0:e.data)||[];for(let e=0;e<t.length;e++){const o=t[e],h=4*e;switch(i){case"futuristic":{const i=t[e]/10,s={r:0,g:5,b:10},n={r:0,g:30,b:20};if(i>.7){const t=3.33*(i-.7);r[h]=Math.floor(s.r*(1-t)+n.r*t),r[h+1]=Math.floor(s.g*(1-t)+n.g*t),r[h+2]=Math.floor(s.b*(1-t)+n.b*t)}else{const t=1.43*i;r[h]=Math.floor(s.r*t),r[h+1]=Math.floor(s.g*t),r[h+2]=Math.floor(s.b*t)}r[h+3]=255}break;case"rainbow":if(0===o)r[h]=0,r[h+1]=0,r[h+2]=0,r[h+3]=255;else{const t=(10*o+n)%360,[e,i,s]=a(t,.8,.5);r[h]=e,r[h+1]=i,r[h+2]=s,r[h+3]=255}break;case"grayscale":{const t=0===o?0:255-255*o/s;r[h]=t,r[h+1]=t,r[h+2]=t,r[h+3]=255}break;case"binary":0!==o?(r[h]=0,r[h+1]=0,r[h+2]=0):(r[h]=255,r[h+1]=255,r[h+2]=255),r[h+3]=255;break;case"fire":if(0==o)r[h]=0,r[h+1]=0,r[h+2]=0;else{const t=o/s;if(t<.3){const e=t/.3;r[h]=Math.floor(255*e),r[h+1]=0,r[h+2]=0}else if(t<.6){const e=(t-.3)/.3;r[h]=255,r[h+1]=Math.floor(165*e),r[h+2]=0}else if(t<.9){const e=(t-.6)/.3;r[h]=255,r[h+1]=165+Math.floor(90*e),r[h+2]=Math.floor(255*e)}else r[h]=255,r[h+1]=255,r[h+2]=255}r[h+3]=255;break;case"ocean":if(0===o)r[h]=0,r[h+1]=20,r[h+2]=50;else{const t=o/s;r[h]=Math.floor(10+50*t),r[h+1]=Math.floor(50+150*t),r[h+2]=Math.floor(100+155*t)}r[h+3]=255;break;case"electric":if(0===o)r[h]=0,r[h+1]=0,r[h+2]=0;else{const t=(o+n)%3,e=o%20/20;0===t?(r[h]=Math.floor(255*(.5+.5*Math.sin(e*Math.PI*2))),r[h+1]=Math.floor(128*e),r[h+2]=Math.floor(255*e)):1===t?(r[h]=Math.floor(255*e),r[h+1]=Math.floor(255*(.5+.5*Math.sin(e*Math.PI*2))),r[h+2]=Math.floor(128*e)):(r[h]=Math.floor(128*e),r[h+1]=Math.floor(255*e),r[h+2]=Math.floor(255*(.5+.5*Math.sin(e*Math.PI*2))))}r[h+3]=255;break;case"topographic":if(0===o)r[h]=5,r[h+1]=15,r[h+2]=30;else{const t=o/s;if(t<.1){const e=t/.1;r[h]=Math.floor(5+20*e),r[h+1]=Math.floor(15+40*e),r[h+2]=Math.floor(30+50*e)}else if(t<.3){const e=(t-.1)/.2;r[h]=Math.floor(210+45*e),r[h+1]=Math.floor(180+40*e),r[h+2]=Math.floor(140+30*e)}else if(t<.7){const e=(t-.3)/.4;r[h]=Math.floor(50*(1-e)),r[h+1]=Math.floor(100+80*e),r[h+2]=Math.floor(50*(1-e))}else{const e=(t-.7)/.3;r[h]=Math.floor(150+105*e),r[h+1]=Math.floor(150+105*e),r[h+2]=Math.floor(150+105*e)}}r[h+3]=255;break;default:if(0===o)r[h]=0,r[h+1]=0,r[h+2]=0;else{const t=(o+n)%64;t<16?(r[h]=16*t,r[h+1]=0,r[h+2]=0):t<32?(r[h]=255,r[h+1]=16*(t-16),r[h+2]=0):t<48?(r[h]=255-16*(t-32),r[h+1]=255,r[h+2]=0):(r[h]=0,r[h+1]=255-16*(t-48),r[h+2]=16*(t-48))}r[h+3]=255}}return null!=e?e:r}static pythagorasTree(t,e,i=10,s=-2,n=2,a=-.5,r=3.5){const o=new Uint8Array(t*e),h=e=>Math.floor((e-s)*t/(n-s)),l=t=>Math.floor((t-a)*e/(r-a)),c=(i,s,n,a)=>{const r=h(i),c=l(s),u=h(n),d=l(a);let _=r,p=c;const g=Math.abs(u-r),f=Math.abs(d-c),m=r<u?1:-1,v=c<d?1:-1;let y=g-f;for(;_>=0&&_<t&&p>=0&&p<e&&(o[p*t+_]=255),_!==u||p!==d;){const t=2*y;t>-f&&(y-=f,_+=m),t<g&&(y+=g,p+=v)}},u=(t,e,i,s,n)=>{if(n<=0)return;const a=i-t,r=s-e,o=i+r,h=s-a,l=t+r,d=e-a;((t,e,i,s,n,a,r,o)=>{c(t,e,i,s),c(i,s,n,a),c(n,a,r,o),c(r,o,t,e)})(t,e,i,s,o,h,l,d);const _=Math.PI/4,p=.7*Math.sqrt(a*a+r*r),g=p*Math.cos(Math.atan2(r,a)-_),f=p*Math.sin(Math.atan2(r,a)-_),m=.7*Math.sqrt(a*a+r*r),v=l,y=d,x=v+m*Math.cos(Math.atan2(r,a)+_),b=y+m*Math.sin(Math.atan2(r,a)+_);u(o,h,o+g,h+f,n-1),u(v,y,x,b,n-1)},d=Math.min(i,12);return u(-.5,0,.5,0,d),o}static mandelbrot(t,e,i=100,s=-2.5,n=1,a=-1.5,r=1.5){const o=new Uint8Array(t*e),h=(n-s)/t,l=(r-a)/e;for(let n=0;n<e;n++){const e=n*t,r=a+n*l;for(let n=0;n<t;n++){const t=s+n*h;let a=0,l=0,c=0,u=0,d=0;do{l=2*a*l+r,a=c-u+t,c=a*a,u=l*l,d++}while(c+u<4&&d<i);o[e+n]=d<i?d%256:0}}return o}static julia(t,e,i=100,s=-.7,n=.27,a=1,r=0,o=0){const h=new Uint8Array(t*e),l=2/a,c=-l+r,u=-l+o,d=(l+r-c)/t,_=(l+o-u)/e;for(let a=0;a<e;a++){const e=a*t,r=u+a*_;for(let a=0;a<t;a++){let t=c+a*d,o=r,l=0,u=0,_=0;do{l=t*t,u=o*o;o=2*t*o+n,t=l-u+s,_++}while(l+u<4&&_<i);h[e+a]=_<i?_%256:0}}return h}static tricorn(t,e,i=100,s=-2.5,n=1.5,a=-1.5,r=1.5){const o=new Uint8Array(t*e),h=(n-s)/t,l=(r-a)/e;for(let n=0;n<e;n++){const e=n*t,r=a+n*l;for(let n=0;n<t;n++){const t=s+n*h;let a=0,l=0,c=0,u=0,d=0;do{l=-2*a*l+r,a=c-u+t,c=a*a,u=l*l,d++}while(c+u<4&&d<i);o[e+n]=d<i?d%256:0}}return o}static phoenix(t,e,i=100,s=.5,n=.5,a=-2,r=2,o=-2,h=2){const l=new Uint8Array(t*e),c=(r-a)/t,u=(h-o)/e;for(let r=0;r<e;r++){const e=r*t,h=o+r*u;for(let r=0;r<t;r++){const t=a+r*c;let o=0,u=0,d=0,_=0,p=0,g=0,f=0;do{const e=p-g+t+s*d+n,i=2*o*u+h+s*_;d=o,_=u,o=e,u=i,p=o*o,g=u*u,f++}while(p+g<4&&f<i);l[e+r]=f<i?f%256:0}}return l}static newton(t,e,i=100,s=1e-6,n=-2,a=2,r=-2,o=2){const h=new Uint8Array(t*e),l=s*s,c=a-n,u=o-r,d=new Float64Array(3),_=new Float64Array(3);for(let t=0;t<3;t++){const e=2*Math.PI*t/3;d[t]=Math.cos(e),_[t]=Math.sin(e)}const p=c/t,g=u/e;for(let s=0;s<e;s++){const e=s*t,a=r+s*g;for(let s=0;s<t;s++){let t=n+s*p,r=a,o=0,c=-1;for(;o<i&&c<0;){const e=t*t-r*r,i=2*t*r,s=e*t-i*r-1,n=e*r+i*t,a=3*e,h=3*i,u=a*a+h*h;if(u<l)break;const p=1/u,g=t-(s*a+n*h)*p,f=r-(n*a-s*h)*p;for(let t=0;t<3;t++){const e=g-d[t],i=f-_[t];if(e*e+i*i<l){c=t;break}}t=g,r=f,o++}if(c>=0){const t=1-Math.min(o/i,1),n=85*c;h[e+s]=Math.floor(n+85*t)}else h[e+s]=0}}return h}static sierpinski(t,e,i=6,s=0,n=1,a=0,r=1){const o=new Uint8Array(t*e).fill(1),h=Math.sqrt(3)/2,l=n-s,c=(r-a)/l;if(Math.abs(c-h)>1e-9){const t=(a+r)/2,e=l*h;a=t-e/2,r=t+e/2}const u=(1<<Math.min(i,32))-1,d=(n-s)/t,_=(r-a)/e,p=2/Math.sqrt(3);for(let i=0;i<e;++i){const e=a+i*_,n=Math.floor(e*p),r=.5*n;for(let e=0;e<t;++e){const a=s+e*d;0!==(Math.floor(a-r)&n&u)&&(o[i*t+e]=0)}}return o}static sierpinskiCarpet(t,e,i=5,s=0,n=1,a=0,r=1){const o=new Uint8Array(t*e).fill(1),h=n-s,l=r-a,c=Math.max(h,l),u=(s+n)/2,d=(a+r)/2;s=u-c/2,n=u+c/2,a=d-c/2,r=d+c/2;const _=Math.pow(3,i),p=(t,e)=>{let i=t,s=e;for(;i>0||s>0;){if(i%3==1&&s%3==1)return!0;i=Math.floor(i/3),s=Math.floor(s/3)}return!1};for(let i=0;i<e;++i){const h=(a+i/e*(r-a))*_,l=(Math.floor(h)%_+_)%_;for(let e=0;e<t;++e){const a=(s+e/t*(n-s))*_;p((Math.floor(a)%_+_)%_,l)&&(o[i*t+e]=0)}}return o}static barnsleyFern(t,e,i=1e5){const s=new Uint8Array(t*e).fill(0);let n=0,a=0;const r=Math.min(t,e)/10,o=t/2;for(let h=0;h<i;h++){const i=Math.random();let h,l;i<.01?(h=0,l=.16*a):i<.86?(h=.85*n+.04*a,l=-.04*n+.85*a+1.6):i<.93?(h=.2*n-.26*a,l=.23*n+.22*a+1.6):(h=-.15*n+.28*a,l=.26*n+.24*a+.44),n=h,a=l;const c=Math.floor(n*r+o),u=Math.floor(e-a*r);if(c>=0&&c<t&&u>=0&&u<e){const e=u*t+c;s[e]<255&&s[e]++}}return s}static lyapunov(t,e,i=1e3,s="AB",n=3.4,a=4,r=3.4,o=4){console.time("lyapunov");const h=(s=s.toUpperCase().replace(/[^AB]/g,"")||"AB").length,l=new Float32Array(t*e);let c=1/0,u=-1/0;for(let d=0;d<e;d++){const _=r+(o-r)*d/e;for(let e=0;e<t;e++){const r=n+(a-n)*e/t;let o=.5;for(let t=0;t<100;t++){o=("A"===s[t%h]?r:_)*o*(1-o)}let p=0,g=0;for(;g<i;){const t="A"===s[g%h]?r:_;o=t*o*(1-o);const e=Math.abs(t*(1-2*o));if(p+=Math.log(Math.max(e,1e-10)),g++,Math.abs(p/g)>10)break}const f=p/g;l[d*t+e]=f,f>-10&&f<10&&(f<c&&(c=f),f>u&&(u=f))}}c===u&&(c-=1,u+=1);const d=u-c,_=new Uint8Array(t*e);for(let t=0;t<l.length;t++){let e=l[t];e=Math.max(-10,Math.min(10,e));let i=(e-c)/d;_[t]=Math.floor(255*i)}return console.timeEnd("lyapunov"),_}static koch(t,e,i=4,s=-2,n=2,a=-2,r=2){const o=new Uint8Array(t*e),h=e=>Math.floor((e-s)*t/(n-s)),l=t=>Math.floor((t-a)*e/(r-a)),c=(i,s,n,a,r)=>{if(r<=0)return void((i,s,n,a)=>{const r=h(i),c=l(s),u=h(n),d=l(a);let _=r,p=c;const g=Math.abs(u-r),f=Math.abs(d-c),m=r<u?1:-1,v=c<d?1:-1;let y=g-f;for(;_>=0&&_<t&&p>=0&&p<e&&(o[p*t+_]=255),_!==u||p!==d;){const t=2*y;t>-f&&(y-=f,_+=m),t<g&&(y+=g,p+=v)}})(i,s,n,a);const u=(n-i)/3,d=(a-s)/3,_=i+u,p=s+d,g=i+2*u,f=s+2*d,m=Math.PI/3,v=_+u*Math.cos(m)-d*Math.sin(m),y=p+u*Math.sin(m)+d*Math.cos(m);c(i,s,_,p,r-1),c(_,p,v,y,r-1),c(v,y,g,f,r-1),c(g,f,n,a,r-1)},u=Math.min(i,10),d=3*Math.sqrt(3)/2,_=[0,-d/2+.5],p=[-1.5,d/2+.5],g=[1.5,d/2+.5];return c(_[0],_[1],p[0],p[1],u),c(p[0],p[1],g[0],g[1],u),c(g[0],g[1],_[0],_[1],u),o}}__publicField(Fractals,"types",{MANDELBROT:"mandelbrot",TRICORN:"tricorn",PHOENIX:"phoenix",JULIA:"julia",SIERPINSKI:"sierpinski",SCARPET:"sierpinskiCarpet",BARNSEY_FERN:"barnsleyFern",KOCH:"koch",PYTHAGORAS_TREE:"pythagorasTree",NEWTON:"newton",LYAPUNOV:"lyapunov"}),__publicField(Fractals,"colors",{FUTURISTIC:"futuristic",RAINBOW:"rainbow",GRAYSCALE:"grayscale",TOPOGRAPHIC:"topographic",FIRE:"fire",OCEAN:"ocean",ELECTRIC:"electric",BINARY:"binary",HISTORIC:"historic"});const _Noise=class{static seed(t){t>0&&t<1&&(t*=65536),(t=Math.floor(t))<256&&(t|=t<<8);for(let e=0;e<256;e++){let i;i=1&e?__privateGet(this,_p)[e]^255&t:__privateGet(this,_p)[e]^t>>8&255,__privateGet(this,_perm)[e]=__privateGet(this,_perm)[e+256]=i,__privateGet(this,_gradP)[e]=__privateGet(this,_gradP)[e+256]=__privateGet(this,_grad3)[i%12]}}static simplex2(t,e){let i,s,n;const a=(t+e)*__privateGet(this,_F2),r=Math.floor(t+a),o=Math.floor(e+a),h=(r+o)*__privateGet(this,_G2),l=t-r+h,c=e-o+h;let u,d;l>c?(u=1,d=0):(u=0,d=1);const _=l-u+__privateGet(this,_G2),p=c-d+__privateGet(this,_G2),g=l-1+2*__privateGet(this,_G2),f=c-1+2*__privateGet(this,_G2),m=255&r,v=255&o,y=__privateGet(this,_gradP)[m+__privateGet(this,_perm)[v]],x=__privateGet(this,_gradP)[m+u+__privateGet(this,_perm)[v+d]],b=__privateGet(this,_gradP)[m+1+__privateGet(this,_perm)[v+1]];let w=.5-l*l-c*c;w<0?i=0:(w*=w,i=w*w*y.dot2(l,c));let M=.5-_*_-p*p;M<0?s=0:(M*=M,s=M*M*x.dot2(_,p));let S=.5-g*g-f*f;return S<0?n=0:(S*=S,n=S*S*b.dot2(g,f)),70*(i+s+n)}static simplex3(t,e,i){let s,n,a,r;const o=(t+e+i)*__privateGet(this,_F3),h=Math.floor(t+o),l=Math.floor(e+o),c=Math.floor(i+o),u=(h+l+c)*__privateGet(this,_G3),d=t-h+u,_=e-l+u,p=i-c+u;let g,f,m,v,y,x;d>=_?_>=p?(g=1,f=0,m=0,v=1,y=1,x=0):d>=p?(g=1,f=0,m=0,v=1,y=0,x=1):(g=0,f=0,m=1,v=1,y=0,x=1):_<p?(g=0,f=0,m=1,v=0,y=1,x=1):d<p?(g=0,f=1,m=0,v=0,y=1,x=1):(g=0,f=1,m=0,v=1,y=1,x=0);const b=d-g+__privateGet(this,_G3),w=_-f+__privateGet(this,_G3),M=p-m+__privateGet(this,_G3),S=d-v+2*__privateGet(this,_G3),P=_-y+2*__privateGet(this,_G3),T=p-x+2*__privateGet(this,_G3),C=d-1+3*__privateGet(this,_G3),k=_-1+3*__privateGet(this,_G3),G=p-1+3*__privateGet(this,_G3),R=255&h,E=255&l,I=255&c,A=__privateGet(this,_gradP)[R+__privateGet(this,_perm)[E+__privateGet(this,_perm)[I]]],F=__privateGet(this,_gradP)[R+g+__privateGet(this,_perm)[E+f+__privateGet(this,_perm)[I+m]]],z=__privateGet(this,_gradP)[R+v+__privateGet(this,_perm)[E+y+__privateGet(this,_perm)[I+x]]],D=__privateGet(this,_gradP)[R+1+__privateGet(this,_perm)[E+1+__privateGet(this,_perm)[I+1]]];let O=.6-d*d-_*_-p*p;O<0?s=0:(O*=O,s=O*O*A.dot3(d,_,p));let B=.6-b*b-w*w-M*M;B<0?n=0:(B*=B,n=B*B*F.dot3(b,w,M));let L=.6-S*S-P*P-T*T;L<0?a=0:(L*=L,a=L*L*z.dot3(S,P,T));let X=.6-C*C-k*k-G*G;return X<0?r=0:(X*=X,r=X*X*D.dot3(C,k,G)),32*(s+n+a+r)}static perlin2(t,e){const i=Math.floor(t),s=Math.floor(e);t-=i,e-=s;const n=255&i,a=255&s,r=__privateGet(this,_gradP)[n+__privateGet(this,_perm)[a]].dot2(t,e),o=__privateGet(this,_gradP)[n+__privateGet(this,_perm)[a+1]].dot2(t,e-1),h=__privateGet(this,_gradP)[n+1+__privateGet(this,_perm)[a]].dot2(t-1,e),l=__privateGet(this,_gradP)[n+1+__privateGet(this,_perm)[a+1]].dot2(t-1,e-1),c=__privateMethod(this,_Noise_static,fade_fn).call(this,t);return __privateMethod(this,_Noise_static,lerp_fn).call(this,__privateMethod(this,_Noise_static,lerp_fn).call(this,r,h,c),__privateMethod(this,_Noise_static,lerp_fn).call(this,o,l,c),__privateMethod(this,_Noise_static,fade_fn).call(this,e))}static perlin3(t,e,i){const s=Math.floor(t),n=Math.floor(e),a=Math.floor(i);t-=s,e-=n,i-=a;const r=255&s,o=255&n,h=255&a,l=__privateGet(this,_gradP)[r+__privateGet(this,_perm)[o+__privateGet(this,_perm)[h]]].dot3(t,e,i),c=__privateGet(this,_gradP)[r+__privateGet(this,_perm)[o+__privateGet(this,_perm)[h+1]]].dot3(t,e,i-1),u=__privateGet(this,_gradP)[r+__privateGet(this,_perm)[o+1+__privateGet(this,_perm)[h]]].dot3(t,e-1,i),d=__privateGet(this,_gradP)[r+__privateGet(this,_perm)[o+1+__privateGet(this,_perm)[h+1]]].dot3(t,e-1,i-1),_=__privateGet(this,_gradP)[r+1+__privateGet(this,_perm)[o+__privateGet(this,_perm)[h]]].dot3(t-1,e,i),p=__privateGet(this,_gradP)[r+1+__privateGet(this,_perm)[o+__privateGet(this,_perm)[h+1]]].dot3(t-1,e,i-1),g=__privateGet(this,_gradP)[r+1+__privateGet(this,_perm)[o+1+__privateGet(this,_perm)[h]]].dot3(t-1,e-1,i),f=__privateGet(this,_gradP)[r+1+__privateGet(this,_perm)[o+1+__privateGet(this,_perm)[h+1]]].dot3(t-1,e-1,i-1),m=__privateMethod(this,_Noise_static,fade_fn).call(this,t),v=__privateMethod(this,_Noise_static,fade_fn).call(this,e),y=__privateMethod(this,_Noise_static,fade_fn).call(this,i);return __privateMethod(this,_Noise_static,lerp_fn).call(this,__privateMethod(this,_Noise_static,lerp_fn).call(this,__privateMethod(this,_Noise_static,lerp_fn).call(this,l,_,m),__privateMethod(this,_Noise_static,lerp_fn).call(this,c,p,m),y),__privateMethod(this,_Noise_static,lerp_fn).call(this,__privateMethod(this,_Noise_static,lerp_fn).call(this,u,g,m),__privateMethod(this,_Noise_static,lerp_fn).call(this,d,f,m),y),v)}};_Grad=new WeakMap,_grad3=new WeakMap,_p=new WeakMap,_perm=new WeakMap,_gradP=new WeakMap,_F2=new WeakMap,_G2=new WeakMap,_F3=new WeakMap,_G3=new WeakMap,_Noise_static=new WeakSet,fade_fn=function(t){return t*t*t*(t*(6*t-15)+10)},lerp_fn=function(t,e,i){return(1-i)*t+i*e},__privateAdd(_Noise,_Noise_static),__privateAdd(_Noise,_Grad,class{constructor(t,e,i){this.x=t,this.y=e,this.z=i}dot2(t,e){return this.x*t+this.y*e}dot3(t,e,i){return this.x*t+this.y*e+this.z*i}}),__privateAdd(_Noise,_grad3,[new(__privateGet(_Noise,_Grad))(1,1,0),new(__privateGet(_Noise,_Grad))(-1,1,0),new(__privateGet(_Noise,_Grad))(1,-1,0),new(__privateGet(_Noise,_Grad))(-1,-1,0),new(__privateGet(_Noise,_Grad))(1,0,1),new(__privateGet(_Noise,_Grad))(-1,0,1),new(__privateGet(_Noise,_Grad))(1,0,-1),new(__privateGet(_Noise,_Grad))(-1,0,-1),new(__privateGet(_Noise,_Grad))(0,1,1),new(__privateGet(_Noise,_Grad))(0,-1,1),new(__privateGet(_Noise,_Grad))(0,1,-1),new(__privateGet(_Noise,_Grad))(0,-1,-1)]),__privateAdd(_Noise,_p,[151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]),__privateAdd(_Noise,_perm,new Array(512)),__privateAdd(_Noise,_gradP,new Array(512)),__privateAdd(_Noise,_F2,.5*(Math.sqrt(3)-1)),__privateAdd(_Noise,_G2,(3-Math.sqrt(3))/6),__privateAdd(_Noise,_F3,1/3),__privateAdd(_Noise,_G3,1/6),_Noise.seed(0);let Noise=_Noise;function generatePenroseTilingPixels(t=800,e=800,i){const{divisions:s=5,zoomType:n="in",color1:a=[255,0,0,255],color2:r=[0,0,255,255],color3:o=[0,0,0,255],backgroundColor:h=[255,255,255,255]}=i||{},l=new Uint8ClampedArray(t*e*4);for(let t=0;t<l.length;t+=4)l[t]=h[0],l[t+1]=h[1],l[t+2]=h[2],l[t+3]=h[3]||255;const c="in"===n?1:2,u=Math.max(t,e),d=u/c,_=u/c,p=.5*c,g=.5*c,f=(Math.sqrt(5)+1)/2;let m=[];for(let t=0;t<10;t++){const e=Complex.fromPolar(1,(2*t-1)*Math.PI/10),i=Complex.fromPolar(1,(2*t+1)*Math.PI/10);t%2==0?m.push(["thin",new Complex(0),i,e]):m.push(["thin",new Complex(0),e,i])}for(let t=0;t<s;t++){const t=[];for(const[e,i,s,n]of m)if("thin"===e){const e=i.add(s.subtract(i).scale(1/f));t.push(["thin",n,e,s]),t.push(["thicc",e,n,i])}else{const e=s.add(i.subtract(s).scale(1/f)),a=s.add(n.subtract(s).scale(1/f));t.push(["thicc",a,n,i]),t.push(["thicc",e,a,s]),t.push(["thin",a,e,i])}m=t}function v(i){return{x:Math.floor((i.real*d+p*d)*t/u),y:Math.floor((i.imag*_+g*_)*e/u)}}for(const[i,s,n,o]of m){fillTriangle(l,v(s),v(n),v(o),"thin"===i?a:r,t,e)}if(o&&o[3]>0)for(const[i,s,n,a]of m){const i=v(s),r=v(n),h=v(a);drawLine(l,i,r,o,t,e),drawLine(l,r,h,o,t,e),drawLine(l,h,i,o,t,e)}return l}function fillTriangle(t,e,i,s,n,a,r){e.y>i.y&&([e,i]=[i,e]),e.y>s.y&&([e,s]=[s,e]),i.y>s.y&&([i,s]=[s,i]);const o=n[0],h=n[1],l=n[2],c=n[3]||255;if(i.y===s.y)fillFlatBottomTriangle(t,e,i,s,o,h,l,c,a,r);else if(e.y===i.y)fillFlatTopTriangle(t,e,i,s,o,h,l,c,a,r);else{const n={x:Math.floor(e.x+(i.y-e.y)/(s.y-e.y)*(s.x-e.x)),y:i.y};fillFlatBottomTriangle(t,e,i,n,o,h,l,c,a,r),fillFlatTopTriangle(t,i,n,s,o,h,l,c,a,r)}}function fillFlatBottomTriangle(t,e,i,s,n,a,r,o,h,l){const c=(i.x-e.x)/(i.y-e.y||1),u=(s.x-e.x)/(s.y-e.y||1);let d=e.x,_=e.x;for(let s=e.y;s<=i.y;s++){if(s>=0&&s<l){const e=Math.max(0,Math.min(Math.floor(d),h-1)),i=Math.max(0,Math.min(Math.floor(_),h-1));for(let l=Math.min(e,i);l<=Math.max(e,i);l++){const e=4*(s*h+l);e>=0&&e<t.length-3&&(t[e]=n,t[e+1]=a,t[e+2]=r,t[e+3]=o)}}d+=c,_+=u}}function fillFlatTopTriangle(t,e,i,s,n,a,r,o,h,l){const c=(s.x-e.x)/(s.y-e.y||1),u=(s.x-i.x)/(s.y-i.y||1);let d=s.x,_=s.x;for(let i=s.y;i>e.y;i--)if(i>=0&&i<l){d-=c,_-=u;const e=Math.max(0,Math.min(Math.floor(d),h-1)),s=Math.max(0,Math.min(Math.floor(_),h-1));for(let l=Math.min(e,s);l<=Math.max(e,s);l++){const e=4*(i*h+l);e>=0&&e<t.length-3&&(t[e]=n,t[e+1]=a,t[e+2]=r,t[e+3]=o)}}}function drawLine(t,e,i,s,n,a){const r=s[0],o=s[1],h=s[2],l=s[3]||255;let c=e.x,u=e.y,d=i.x,_=i.y;const p=Math.abs(d-c),g=Math.abs(_-u),f=c<d?1:-1,m=u<_?1:-1;let v=p-g;for(;;){if(c>=0&&c<n&&u>=0&&u<a){const e=4*(u*n+c);if(e>=0&&e<t.length-3){const i=l/255;t[e]=Math.round(t[e]*(1-i)+r*i),t[e+1]=Math.round(t[e+1]*(1-i)+o*i),t[e+2]=Math.round(t[e+2]*(1-i)+h*i),t[e+3]=255}}if(c===d&&u===_)break;const e=2*v;e>-g&&(v-=g,c+=f),e<p&&(v+=p,u+=m)}}class Patterns{static void(t,e,i={}){const{background:s=[255,255,255,255],foreground:n=[0,0,200,255]}=i,a=new Uint8ClampedArray(t*e*4);for(let t=0;t<a.length;t+=4)a[t]=s[0],a[t+1]=s[1],a[t+2]=s[2],a[t+3]=s[3];return a}static solidGrid(t,e,i={}){const{spacing:s=8,background:n=[0,0,0,0],foreground:a=[128,128,128,255]}=i,r=new Uint8ClampedArray(t*e*4);for(let i=0;i<e;i++){const e=i%s===0;for(let o=0;o<t;o++){const h=4*(i*t+o),l=o%s===0||e?a:n;r[h]=l[0],r[h+1]=l[1],r[h+2]=l[2],r[h+3]=l[3]}}return r}static checkerboard(t,e,i={}){const{cellSize:s=8,color1:n=[0,0,0,255],color2:a=[255,255,255,255]}=i,r=new Uint8ClampedArray(t*e*4);for(let i=0;i<e;i++){const e=Math.floor(i/s);for(let o=0;o<t;o++){const h=(Math.floor(o/s)+e)%2==0?n:a,l=4*(i*t+o);r.set(h,l)}}return r}static stripes(t,e,i={}){const{spacing:s=4,thickness:n=1,background:a=[0,0,0,0],foreground:r=[255,255,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let i=0;i<e;i++)for(let e=0;e<t;e++){const h=(e+i)%s<n,l=4*(i*t+e);o.set(h?r:a,l)}return o}static honeycomb(t,e,i={}){const{radius:s=10,lineWidth:n=1,foreground:a=[255,255,255,255],background:r=[0,0,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=r[0],o[t+1]=r[1],o[t+2]=r[2],o[t+3]=r[3];const h=Math.floor(t/2),l=Math.floor(e/2),c=(t,e,i,s,n)=>{const a=Math.abs(t-i),r=Math.abs(e-s),o=n*Math.sqrt(3)/2;return!(r>o)&&(!(a>n)&&n*o*2>=n*r*2+o*a)},u=s-n,d=s*Math.sqrt(3),_=Math.max(0,Math.floor(h-s-1)),p=Math.min(t-1,Math.ceil(h+s+1)),g=Math.max(0,Math.floor(l-d/2-1)),f=Math.min(e-1,Math.ceil(l+d/2+1));for(let e=g;e<=f;e++)for(let i=_;i<=p;i++){const n=c(i,e,h,l,s),r=u>0&&c(i,e,h,l,u);if(n&&!r){const s=4*(e*t+i);o[s]=a[0],o[s+1]=a[1],o[s+2]=a[2],o[s+3]=a[3]}}return o}static harlequin(t,e,i={}){const{size:s=20,spacing:n=0,background:a=[255,255,255,255],foreground:r=[0,0,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=a[0],o[t+1]=a[1],o[t+2]=a[2],o[t+3]=a[3];const h=2*s,l=2*s,c=h+n,u=l+n,d=(t,e,i,s)=>Math.abs(t-i)/(h/2)+Math.abs(e-s)/(l/2)<=1;for(let i=-1;i<e/u+1;i++)for(let s=-1;s<t/c+1;s++){const n=s*c+c/2,a=i*u+u/2;if(!((i+s)%2==0))continue;const _=Math.max(0,Math.floor(n-h/2)),p=Math.min(t-1,Math.ceil(n+h/2)),g=Math.max(0,Math.floor(a-l/2)),f=Math.min(e-1,Math.ceil(a+l/2));for(let e=g;e<=f;e++)for(let i=_;i<=p;i++)if(d(i,e,n,a)){const s=4*(e*t+i);o[s]=r[0],o[s+1]=r[1],o[s+2]=r[2],o[s+3]=r[3]}}return o}static circles(t,e,i={}){const{radius:s=10,lineWidth:n=2,spacing:a=5,background:r=[0,0,0,255],foreground:o=[255,255,255,255]}=i,h=new Uint8ClampedArray(t*e*4);for(let t=0;t<h.length;t+=4)h[t]=r[0],h[t+1]=r[1],h[t+2]=r[2],h[t+3]=r[3];const l=2*s+a,c=(t,e,i,s,n)=>{const a=t-i,r=e-s;return a*a+r*r<=n*n};for(let i=0;i<Math.ceil(e/l)+1;i++)for(let a=0;a<Math.ceil(t/l)+1;a++){const r=a*l+s,u=i*l+s;if(r<-s||r>t+s||u<-s||u>e+s)continue;const d=Math.max(0,Math.floor(r-s)),_=Math.min(t-1,Math.ceil(r+s)),p=Math.max(0,Math.floor(u-s)),g=Math.min(e-1,Math.ceil(u+s)),f=s-n;for(let e=p;e<=g;e++)for(let i=d;i<=_;i++){const n=c(i,e,r,u,s),a=c(i,e,r,u,f);if(n&&!a){const s=4*(e*t+i);h[s]=o[0],h[s+1]=o[1],h[s+2]=o[2],h[s+3]=o[3]}}}return h}static diamonds(t,e,i={}){const{size:s=16,squareSize:n=6,background:a=[255,255,255,255],foreground:r=[0,0,0,255],innerColor:o=[255,255,255,255]}=i,h=new Uint8ClampedArray(t*e*4);for(let t=0;t<h.length;t+=4)h[t]=a[0],h[t+1]=a[1],h[t+2]=a[2],h[t+3]=a[3];const l=s,c=(t,e,i,s,n)=>Math.abs(t-i)+Math.abs(e-s)<=n/2,u=(t,e,i,s,n)=>Math.abs(t-i)<=n/2&&Math.abs(e-s)<=n/2;for(let i=-1;i<e/l+1;i++)for(let s=-1;s<t/l+1;s++){const a=s*l+l/2,d=i*l+l/2;if(a<-l||a>t+l||d<-l||d>e+l)continue;const _=Math.max(0,Math.floor(a-l/2)),p=Math.min(t-1,Math.ceil(a+l/2)),g=Math.max(0,Math.floor(d-l/2)),f=Math.min(e-1,Math.ceil(d+l/2));for(let e=g;e<=f;e++)for(let i=_;i<=p;i++){const s=c(i,e,a,d,l),_=u(i,e,a,d,n);if(s){const s=4*(e*t+i);_?(h[s]=o[0],h[s+1]=o[1],h[s+2]=o[2],h[s+3]=o[3]):(h[s]=r[0],h[s+1]=r[1],h[s+2]=r[2],h[s+3]=r[3])}}}return h}static cubes(t,e,i={}){const{size:s=10,spacing:n=2,background:a=[0,0,0,255],foreground:r=[255,100,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=a[0],o[t+1]=a[1],o[t+2]=a[2],o[t+3]=a[3];const h=s+n;for(let i=0;i<Math.ceil(e/h)+1;i++)for(let n=0;n<Math.ceil(t/h)+1;n++){const a=n*h,l=i*h;if(!(a>=t||l>=e))for(let i=l;i<Math.min(l+s,e);i++)for(let e=a;e<Math.min(a+s,t);e++){const s=4*(i*t+e);o[s]=r[0],o[s+1]=r[1],o[s+2]=r[2],o[s+3]=r[3]}}return o}static cross(t,e,i={}){const{size:s=8,thickness:n=2,spacing:a=16,background:r=[255,255,255,255],foreground:o=[80,80,80,255]}=i,h=new Uint8ClampedArray(t*e*4);for(let t=0;t<h.length;t+=4)h[t]=r[0],h[t+1]=r[1],h[t+2]=r[2],h[t+3]=r[3];for(let i=0;i<Math.ceil(e/a)+1;i++)for(let r=0;r<Math.ceil(t/a)+1;r++){const l=r*a,c=i*a;if(l<-s||l>t+s||c<-s||c>e+s)continue;const u=l-s/2,d=l+s/2,_=c-n/2,p=c+n/2;for(let i=Math.max(0,Math.floor(_));i<Math.min(e,Math.ceil(p));i++)for(let e=Math.max(0,Math.floor(u));e<Math.min(t,Math.ceil(d));e++){const s=4*(i*t+e);h[s]=o[0],h[s+1]=o[1],h[s+2]=o[2],h[s+3]=o[3]}const g=l-n/2,f=l+n/2,m=c-s/2,v=c+s/2;for(let i=Math.max(0,Math.floor(m));i<Math.min(e,Math.ceil(v));i++)for(let e=Math.max(0,Math.floor(g));e<Math.min(t,Math.ceil(f));e++){const s=4*(i*t+e);h[s]=o[0],h[s+1]=o[1],h[s+2]=o[2],h[s+3]=o[3]}}return h}static mesh(t,e,i={}){const{spacing:s=20,lineWidth:n=2,background:a=[255,255,255,0],foreground:r=[0,0,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=a[0],o[t+1]=a[1],o[t+2]=a[2],o[t+3]=a[3];for(let i=0;i<e;i++)for(let a=0;a<t;a++){const h=(a+i)%s,l=(a-i+e)%s;if(h<n||h>s-n||(l<n||l>s-n)){const e=4*(i*t+a);o[e]=r[0],o[e+1]=r[1],o[e+2]=r[2],o[e+3]=r[3]}}return o}static isometric(t,e,i={}){const{cellSize:s=20,lineWidth:n=1,background:a=[0,0,0,0],foreground:r=[0,255,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=a[0],o[t+1]=a[1],o[t+2]=a[2],o[t+3]=a[3];const h=s,l=s/2;for(let i=0;i<e;i++)for(let e=0;e<t;e++){const s=e%h,a=i%l,c=a-s/2,u=a+s/2-l,d=Math.abs(c)<n/2,_=Math.abs(u)<n/2;if(d||_){const s=4*(i*t+e);o[s]=r[0],o[s+1]=r[1],o[s+2]=r[2],o[s+3]=r[3]}}return o}static weave(t,e,i={}){const{tileSize:s=40,lineWidth:n=2,background:a=[255,255,255,255],foreground:r=[0,0,0,255]}=i,o=new Uint8ClampedArray(t*e*4);for(let t=0;t<o.length;t+=4)o[t]=a[0],o[t+1]=a[1],o[t+2]=a[2],o[t+3]=a[3];for(let i=0;i<e;i++)for(let e=0;e<t;e++){const a=e%s,h=i%s,l=Math.abs((h+s/2)%s-s/2)<n/2,c=Math.abs((a+2*h+1.5*s)%s-s/2)<n/2,u=Math.abs((a-2*h+1.5*s)%s-s/2)<n/2;if(l||c||u){const s=4*(i*t+e);o[s]=r[0],o[s+1]=r[1],o[s+2]=r[2],o[s+3]=r[3]}}return o}static perlinNoise(t,e,i={}){const{background:s=[0,0,0,0],foreground:n=[255,255,255,255],scale:a=.1,octaves:r=4,persistence:o=.5,lacunarity:h=2,seed:l=65536*Math.random()}=i,c=new Uint8ClampedArray(t*e*4);Noise.seed(l);for(let i=0;i<e;i++)for(let e=0;e<t;e++){let l=1,u=1,d=0,_=0;for(let t=0;t<r;t++){const t=e*a*u,s=i*a*u;d+=Noise.perlin2(t,s)*l,_+=l,l*=o,u*=h}d/=_;const p=.5*(d+1),g=[Math.floor(s[0]+p*(n[0]-s[0])),Math.floor(s[1]+p*(n[1]-s[1])),Math.floor(s[2]+p*(n[2]-s[2])),Math.floor(s[3]+p*(n[3]-s[3]))],f=4*(i*t+e);c.set(g,f)}return c}static circularGradient(t,e,i={}){const{innerColor:s=[255,255,255,255],outerColor:n=[0,0,0,255],centerX:a=t/2,centerY:r=e/2,radius:o=Math.min(t,e)/2,fadeExponent:h=1}=i,l=new Uint8ClampedArray(t*e*4);for(let i=0;i<e;i++)for(let e=0;e<t;e++){const c=4*(i*t+e),u=e-a,d=i-r,_=Math.sqrt(u*u+d*d);let p=Math.min(_/o,1);p=Math.pow(p,h);const g=[Math.floor(s[0]+p*(n[0]-s[0])),Math.floor(s[1]+p*(n[1]-s[1])),Math.floor(s[2]+p*(n[2]-s[2])),Math.floor(s[3]+p*(n[3]-s[3]))];l.set(g,c)}return l}static noiseDisplacement(t,e,i={}){const{gridSpacing:s=16,gridColor:n=[255,255,255,255],background:a=[0,0,0,0],displacementScale:r=8,noiseScale:o=.05,gridThickness:h=1,seed:l=65536*Math.random()}=i,c=new Uint8ClampedArray(t*e*4);Noise.seed(l);for(let t=0;t<c.length;t+=4)c.set(a,t);for(let i=0;i<e;i++)for(let e=0;e<t;e++){const a=e+Noise.perlin2(e*o,i*o)*r,l=i+Noise.perlin2((e+31.416)*o,(i+27.182)*o)*r;if(a%s<h||a%s>s-h||(l%s<h||l%s>s-h)){const s=4*(i*t+e);c.set(n,s)}}return c}static dotPattern(t,e,i={}){const{dotSize:s=3,spacing:n=12,dotColor:a=[0,0,0,255],background:r=[255,255,255,255],useNoise:o=!1,noiseScale:h=.1,noiseDensity:l=.4,seed:c=65536*Math.random()}=i,u=new Uint8ClampedArray(t*e*4);o&&Noise.seed(c);for(let t=0;t<u.length;t+=4)u.set(r,t);if(o)for(let i=0;i<e;i++)for(let n=0;n<t;n++){if(.5*(Noise.perlin2(n*h,i*h)+1)>l)for(let r=-s;r<=s;r++)for(let o=-s;o<=s;o++){const h=n+o,l=i+r;if(h>=0&&h<t&&l>=0&&l<e){if(o*o+r*r<=s*s){const e=4*(l*t+h);u.set(a,e)}}}}else for(let i=Math.floor(n/2);i<e;i+=n)for(let r=Math.floor(n/2);r<t;r+=n)for(let n=-s;n<=s;n++)for(let o=-s;o<=s;o++){const h=r+o,l=i+n;if(h>=0&&h<t&&l>=0&&l<e){if(o*o+n*n<=s*s){const e=4*(l*t+h);u.set(a,e)}}}return u}static voronoi(t,e,i={}){const{cellCount:s=20,cellColors:n=null,edgeColor:a=[0,0,0,255],edgeThickness:r=1.5,seed:o=1e3*Math.random(),jitter:h=.5,baseColor:l=null,colorVariation:c=.3}=i,u=new Uint8ClampedArray(t*e*4);Noise.seed(o);const d=[],_=[],p=()=>{let t=1e4*Math.sin(.167*o+.423*d.length);return t-Math.floor(t)},g=Math.sqrt(s),f=t/g,m=e/g,v=t=>{if(l){const[e,i,s,n]=l,a=Math.max(e,i,s)/255,r=Math.min(e,i,s)/255,o=(a+r)/2;let h,u;if(a===r)h=u=0;else{const t=a-r;u=o>.5?t/(2-a-r):t/(a+r),h=a===e/255?(i/255-s/255)/t+(i/255<s/255?6:0):a===i/255?(s/255-e/255)/t+2:(e/255-i/255)/t+4,h/=6}const d=Noise.perlin2(.15*t,0)*c*.3,_=Noise.perlin2(0,.15*t)*c,g=Noise.perlin2(.15*t,.15*t)*c*.5;h=(h+d)%1,u=Math.min(1,Math.max(0,u*(1+_)));const f=Math.min(.9,Math.max(.1,o*(1+g)));let m,v,y;if(0===u)m=v=y=f;else{const t=(t,e,i)=>(i<0&&(i+=1),i>1&&(i-=1),i<1/6?t+6*(e-t)*i:i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t),e=f<.5?f*(1+u):f+u-f*u,i=2*f-e;m=t(i,e,h+1/3),v=t(i,e,h),y=t(i,e,h-1/3)}const x=.05,b=()=>(2*p()-1)*x;return[Math.min(255,Math.max(0,Math.floor(255*m*(1+b())))),Math.min(255,Math.max(0,Math.floor(255*v*(1+b())))),Math.min(255,Math.max(0,Math.floor(255*y*(1+b())))),n]}{let e,i,s;const n=6*(.618033988749895*t%1),a=Math.floor(n),r=n-a,o=.5,h=.5*(1-r),l=.5*(1-(1-r));switch(a%6){case 0:e=.5,i=l,s=o;break;case 1:e=h,i=.5,s=o;break;case 2:e=o,i=.5,s=l;break;case 3:e=o,i=h,s=.5;break;case 4:e=l,i=o,s=.5;break;case 5:e=.5,i=o,s=h}return[Math.floor(255*e+50+100*p()),Math.floor(255*i+50+100*p()),Math.floor(255*s+50+100*p()),255]}};for(let t=0;t<g;t++)for(let e=0;e<g&&!(d.length>=s);e++){const i=e*f+f/2,s=t*m+m/2,a=(2*p()-1)*h*f,r=(2*p()-1)*h*m;d.push({x:Math.floor(i+a),y:Math.floor(s+r)}),n&&d.length-1<n.length?_.push(n[d.length-1]):_.push(v(d.length-1))}const y=(i,s,n,a)=>{let r=Math.abs(i-n),o=Math.abs(s-a);r=Math.min(r,t-r),o=Math.min(o,e-o);return.8*Math.sqrt(r*r+o*o)+.2*(r+o)};for(let i=0;i<e;i++)for(let s=0;s<t;s++){const n=4*(i*t+s);let o=1/0,h=1/0,l=0;for(let t=0;t<d.length;t++){const e=y(s,i,d[t].x,d[t].y);e<o?(h=o,o=e,l=t):e<h&&(h=e)}for(let n=0;n<d.length;n++)for(let a=-1;a<=1;a++)for(let r=-1;r<=1;r++){if(0===a&&0===r)continue;const c=d[n].x+a*t,u=d[n].y+r*e,_=Math.sqrt(Math.pow(s-c,2)+Math.pow(i-u,2));_<o?(h=o,o=_,l=n):_<h&&(h=_)}h-o<r?u.set(a,n):u.set(_[l],n)}return u}static penrose(t,e,i={}){return generatePenroseTilingPixels(t,e,i)}}const _Tensor=class t{constructor(t,e={}){__privateAdd(this,_components),__privateAdd(this,_dimension),__privateAdd(this,_name),__privateAdd(this,_signature),__privateAdd(this,_coordinates),__privateSet(this,_components,t.map(t=>[...t])),__privateSet(this,_dimension,t.length),__privateSet(this,_name,e.name||""),__privateSet(this,_signature,e.signature||null),__privateSet(this,_coordinates,e.coordinates||null)}static minkowski(){return new t([[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],{name:"Minkowski",signature:[-1,1,1,1],coordinates:["t","x","y","z"]})}static schwarzschild(e,i,s=Math.PI/2){e<=i&&(e=1.001*i);const n=1-i/e,a=Math.sin(s);return new t([[-n,0,0,0],[0,1/n,0,0],[0,0,e*e,0],[0,0,0,e*e*a*a]],{name:"Schwarzschild",signature:[-1,1,1,1],coordinates:["t","r","θ","φ"]})}static schwarzschildContravariant(e,i,s=Math.PI/2){e<=i&&(e=1.001*i);const n=1-i/e,a=Math.sin(s);return new t([[-1/n,0,0,0],[0,n,0,0],[0,0,1/(e*e),0],[0,0,0,1/(e*e*a*a)]],{name:"Schwarzschild (Contravariant)",signature:[-1,1,1,1],coordinates:["t","r","θ","φ"]})}static kerr(e,i,s,n){const a=(n=Math.min(Math.abs(n),s))*n,r=e*e,o=Math.cos(i),h=Math.sin(i),l=h*h,c=o*o,u=r+a*c,d=r-2*s*e+a,_=s+Math.sqrt(Math.max(0,s*s-a));if(e<=_){const r=(e=1.001*_)*e,o=r+a*c,h=r-2*s*e+a;return t._buildKerrMetric(e,i,s,n,o,h,l)}return t._buildKerrMetric(e,i,s,n,u,d,l)}static kerrContravariant(e,i,s,n){const a=(n=Math.min(Math.abs(n),s))*n,r=e*e,o=Math.cos(i),h=Math.sin(i),l=h*h,c=r+a*o*o,u=r-2*s*e+a,d=s+Math.sqrt(Math.max(0,s*s-a));e<=d&&(e=1.001*d);const _=-(Math.pow(r+a,2)-a*u*l)/(c*u),p=-2*s*n*e/(c*u);return new t([[_,0,0,p],[0,u/c,0,0],[0,0,1/c,0],[p,0,0,(u-a*l)/(c*u*l)]],{name:"Kerr (Contravariant)",signature:[-1,1,1,1],coordinates:["t","r","θ","φ"]})}static _buildKerrMetric(e,i,s,n,a,r,o){const h=n*n,l=-2*s*n*e*o/a;return new t([[-(1-2*s*e/a),0,0,l],[0,a/r,0,0],[0,0,a,0],[l,0,0,(e*e+h+2*s*h*e*o/a)*o]],{name:"Kerr",signature:[-1,1,1,1],coordinates:["t","r","θ","φ"]})}static kerrHorizonRadius(t,e,i=!1){const s=t*t-e*e;if(s<0)return NaN;const n=Math.sqrt(s);return i?t-n:t+n}static kerrErgosphereRadius(t,e,i){const s=Math.cos(i),n=t*t-e*e*s*s;return n<0?NaN:t+Math.sqrt(n)}static kerrISCO(t,e,i=!0){const s=e/t,n=i?1:-1,a=1+Math.cbrt(1-s*s)*(Math.cbrt(1+s)+Math.cbrt(1-s)),r=Math.sqrt(3*s*s+a*a);return t*(3+r-n*Math.sqrt((3-a)*(3+a+2*r)))}static kerrFrameDraggingOmega(e,i,s,n){const a=t.kerr(e,i,s,n),r=a.get(0,3),o=a.get(3,3);return Math.abs(o)<1e-10?0:-r/o}static kerrEffectivePotential(t,e,i,s,n){if(n<=0)return 1/0;const a=n*n;return-t/n+(s*s-e*e*(i*i-1))/(2*a)+-t*Math.pow(s-e*i,2)/(a*n)}static diagonal(e,i={}){const s=e.length,n=[];for(let t=0;t<s;t++){n[t]=[];for(let i=0;i<s;i++)n[t][i]=t===i?e[t]:0}return new t(n,i)}static identity(e=4){const i=new Array(e).fill(1);return t.diagonal(i,{name:"Identity"})}static zero(e=4){const i=[];for(let t=0;t<e;t++)i[t]=new Array(e).fill(0);return new t(i,{name:"Zero"})}get(t,e){return __privateGet(this,_components)[t][e]}set(e,i,s){const n=__privateGet(this,_components).map(t=>[...t]);return n[e][i]=s,new t(n,{name:__privateGet(this,_name),signature:__privateGet(this,_signature),coordinates:__privateGet(this,_coordinates)})}getDiagonal(){const t=[];for(let e=0;e<__privateGet(this,_dimension);e++)t.push(__privateGet(this,_components)[e][e]);return t}get dimension(){return __privateGet(this,_dimension)}get name(){return __privateGet(this,_name)}get signature(){return __privateGet(this,_signature)}get coordinates(){return __privateGet(this,_coordinates)}add(e){const i=[];for(let t=0;t<__privateGet(this,_dimension);t++){i[t]=[];for(let s=0;s<__privateGet(this,_dimension);s++)i[t][s]=__privateGet(this,_components)[t][s]+e.get(t,s)}return new t(i)}subtract(e){const i=[];for(let t=0;t<__privateGet(this,_dimension);t++){i[t]=[];for(let s=0;s<__privateGet(this,_dimension);s++)i[t][s]=__privateGet(this,_components)[t][s]-e.get(t,s)}return new t(i)}scale(e){const i=[];for(let t=0;t<__privateGet(this,_dimension);t++){i[t]=[];for(let s=0;s<__privateGet(this,_dimension);s++)i[t][s]=__privateGet(this,_components)[t][s]*e}return new t(i)}multiply(e){const i=[];for(let t=0;t<__privateGet(this,_dimension);t++){i[t]=[];for(let s=0;s<__privateGet(this,_dimension);s++){let n=0;for(let i=0;i<__privateGet(this,_dimension);i++)n+=__privateGet(this,_components)[t][i]*e.get(i,s);i[t][s]=n}}return new t(i)}transpose(){const e=[];for(let t=0;t<__privateGet(this,_dimension);t++){e[t]=[];for(let i=0;i<__privateGet(this,_dimension);i++)e[t][i]=__privateGet(this,_components)[i][t]}return new t(e,{name:__privateGet(this,_name)?`${__privateGet(this,_name)}ᵀ`:"",signature:__privateGet(this,_signature),coordinates:__privateGet(this,_coordinates)})}inverse(){const e=__privateGet(this,_dimension);if(this.isDiagonal()){const e=this.getDiagonal().map(t=>{if(Math.abs(t)<1e-15)throw new Error("Diagonal matrix is singular, cannot compute inverse");return 1/t});return t.diagonal(e,{name:__privateGet(this,_name)?`${__privateGet(this,_name)}⁻¹`:"",signature:__privateGet(this,_signature),coordinates:__privateGet(this,_coordinates)})}const i=[];for(let t=0;t<e;t++){i[t]=[];for(let s=0;s<e;s++)i[t][s]=__privateGet(this,_components)[t][s];for(let s=0;s<e;s++)i[t][e+s]=t===s?1:0}for(let t=0;t<e;t++){let s=t;for(let n=t+1;n<e;n++)Math.abs(i[n][t])>Math.abs(i[s][t])&&(s=n);if([i[t],i[s]]=[i[s],i[t]],Math.abs(i[t][t])<1e-10)throw new Error("Matrix is singular, cannot compute inverse");const n=i[t][t];for(let s=0;s<2*e;s++)i[t][s]/=n;for(let s=0;s<e;s++)if(s!==t){const n=i[s][t];for(let a=0;a<2*e;a++)i[s][a]-=n*i[t][a]}}const s=[];for(let t=0;t<e;t++){s[t]=[];for(let n=0;n<e;n++)s[t][n]=i[t][e+n]}return new t(s,{name:__privateGet(this,_name)?`${__privateGet(this,_name)}⁻¹`:"",signature:__privateGet(this,_signature),coordinates:__privateGet(this,_coordinates)})}determinant(){const t=__privateGet(this,_dimension);if(this.isDiagonal())return this.getDiagonal().reduce((t,e)=>t*e,1);if(2===t)return __privateGet(this,_components)[0][0]*__privateGet(this,_components)[1][1]-__privateGet(this,_components)[0][1]*__privateGet(this,_components)[1][0];if(3===t){const t=__privateGet(this,_components);return t[0][0]*(t[1][1]*t[2][2]-t[1][2]*t[2][1])-t[0][1]*(t[1][0]*t[2][2]-t[1][2]*t[2][0])+t[0][2]*(t[1][0]*t[2][1]-t[1][1]*t[2][0])}const e=__privateGet(this,_components).map(t=>[...t]);let i=1,s=0;for(let n=0;n<t;n++){let a=n;for(let i=n+1;i<t;i++)Math.abs(e[i][n])>Math.abs(e[a][n])&&(a=i);if(a!==n&&([e[n],e[a]]=[e[a],e[n]],s++),Math.abs(e[n][n])<1e-10)return 0;i*=e[n][n];for(let i=n+1;i<t;i++){e[i][n]/=e[n][n];for(let s=n+1;s<t;s++)e[i][s]-=e[i][n]*e[n][s]}}return s%2==0?i:-i}trace(){let t=0;for(let e=0;e<__privateGet(this,_dimension);e++)t+=__privateGet(this,_components)[e][e];return t}isDiagonal(t=1e-10){for(let e=0;e<__privateGet(this,_dimension);e++)for(let i=0;i<__privateGet(this,_dimension);i++)if(e!==i&&Math.abs(__privateGet(this,_components)[e][i])>t)return!1;return!0}isSymmetric(t=1e-10){for(let e=0;e<__privateGet(this,_dimension);e++)for(let i=e+1;i<__privateGet(this,_dimension);i++)if(Math.abs(__privateGet(this,_components)[e][i]-__privateGet(this,_components)[i][e])>t)return!1;return!0}static christoffel(e,i,s=.001){const n=e(i);if("Schwarzschild"===n.name){const e=i._rs||2;return t.schwarzschildChristoffel(i[1],e,i[2])}const a=[],r=n.inverse(),o=[];for(let t=0;t<4;t++){const n=[...i],a=[...i];n[t]+=s,a[t]-=s;const r=e(n),h=e(a);o[t]=[];for(let e=0;e<4;e++){o[t][e]=[];for(let i=0;i<4;i++)o[t][e][i]=(r.get(e,i)-h.get(e,i))/(2*s)}}for(let t=0;t<4;t++){a[t]=[];for(let e=0;e<4;e++){a[t][e]=[];for(let i=0;i<4;i++){let s=0;for(let n=0;n<4;n++){const a=r.get(t,n);Math.abs(a)<1e-15||(s+=a*(o[e][i][n]+o[i][e][n]-o[n][e][i])/2)}a[t][e][i]=s}}}return a}static schwarzschildChristoffel(t,e,i){const s=[];for(let t=0;t<4;t++){s[t]=[];for(let e=0;e<4;e++)s[t][e]=new Array(4).fill(0)}const n=1-e/t;if(Math.abs(n)<1e-10)return s;const a=e/(2*t*t),r=1/Math.tan(i),o=Math.sin(i),h=Math.cos(i);return s[0][0][1]=s[0][1][0]=a/n,s[1][0][0]=e*n/(2*t*t),s[1][1][1]=-a/n,s[1][2][2]=-(t-e),s[1][3][3]=-(t-e)*o*o,s[2][1][2]=s[2][2][1]=1/t,s[2][3][3]=-o*h,s[3][1][3]=s[3][3][1]=1/t,s[3][2][3]=s[3][3][2]=r,s}static effectivePotential(t,e,i){if(i<=0)return 1/0;const s=e*e;return-t/i+s/(2*i*i)-t*s/(i*i*i)}static iscoRadius(t){return 3*t}static photonSphereRadius(t){return 1.5*t}toArray(){const t=[];for(let e=0;e<__privateGet(this,_dimension);e++)for(let i=0;i<__privateGet(this,_dimension);i++)t.push(__privateGet(this,_components)[e][i]);return t}toMatrix(){return __privateGet(this,_components).map(t=>[...t])}toString(t=3){return(__privateGet(this,_name)?`${__privateGet(this,_name)} Tensor:\n`:"")+__privateGet(this,_components).map(e=>`[ ${e.map(e=>e.toFixed(t).padStart(10)).join(" ")} ]`).join("\n")}toLatex(t=3){return`\\begin{pmatrix}\n${__privateGet(this,_components).map(e=>e.map(e=>e.toFixed(t)).join(" & ")).join(" \\\\\n")}\n\\end{pmatrix}`}};_components=new WeakMap,_dimension=new WeakMap,_name=new WeakMap,_signature=new WeakMap,_coordinates=new WeakMap;let Tensor=_Tensor;function flammEmbedding(t,e,i){return t<=e?0:Math.sqrt(8*i*(t-e))}function flammEmbeddingHeight(t,e,i,s,n){const a=flammEmbedding(Math.max(t,e+.01),e,i),r=flammEmbedding(s,e,i);return(r-a)*n/r}function cartesianToSpherical(t,e,i){const s=Math.sqrt(t*t+e*e+i*i);return{r:s,theta:s>0?Math.acos(i/s):0,phi:Math.atan2(e,t)}}function sphericalToCartesian(t,e,i){const s=Math.sin(e);return{x:t*s*Math.cos(i),y:t*s*Math.sin(i),z:t*Math.cos(e)}}function polarToCartesian(t,e){return{x:t*Math.cos(e),z:t*Math.sin(e)}}function cartesianToPolar(t,e){return{r:Math.sqrt(t*t+e*e),phi:Math.atan2(e,t)}}function gravitationalLensing(t,e,i,s,n=5){if(t<=n||t>=e)return 0;return Math.exp(-t*s)*i}function applyGravitationalLensing(t,e,i,s,n,a=5){const r=Math.sqrt(t*t+e*e);if(r<=a||r>=i)return{x:t,y:e,displacement:0};const o=gravitationalLensing(r,i,s,n,a),h=(r+o)/r;return{x:t*h,y:e*h,displacement:o}}function keplerianOmega(t,e,i=1,s=5){return t<=0?0:i*Math.sqrt(e)/Math.pow(t/s,1.5)}function schwarzschildPrecessionRate(t,e,i=1){return t<=e?0:i*(e/t)}function kerrPrecessionRate(t,e,i,s=1){const n=2*e;return t<=n?0:s*(n/t)*(1+i/e)}function orbitalRadius(t,e,i){if(e>=1)return t;return t*(1-e*e)/(1+e*Math.cos(i))}function orbitalRadiusSimple(t,e,i,s=2){return t+e*Math.sin(2*i)*s}function decayingOrbitalRadius(t,e,i){return t*Math.exp(-e*i)}function getTerminalTrajectory(t,e,i,s,n=null){const a=n?n(s):s;return{x:t*(1-a),y:e*(1-a),z:i*(1-a)}}function updateTrail(t,e,i=80){return t.unshift(e),t.length>i&&t.pop(),t}function createTrailPoint(t,e,i={}){return{x:Math.cos(e)*t,z:Math.sin(e)*t,r:t,...i}}function gaussianWavePacket(t,e,{amplitude:i,sigma:s,k:n,omega:a,velocity:r}){const o=t-r*e,h=i*Math.exp(-o*o/(4*s*s)),l=n*t-a*e;return{psi:Complex.fromPolar(h,l),envelope:h}}function planeWavePhase(t,e,i,s){return i*t-s*e}function gaussianEnvelope(t,e,i,s=1){const n=t-e;return s*Math.exp(-n*n/(4*i*i))}function probabilityDensity(t){return t.real*t.real+t.imag*t.imag}function deBroglieWavelength(t){return 2*Math.PI/t}function groupVelocity(t,e,i=.01){return e/t}class Easing{static lerp(t,e,i){return t+(e-t)*i}static linear(t){return t}static smoothstep(t){return t*t*(3-2*t)}static smootherstep(t){return t*t*t*(t*(6*t-15)+10)}static easeInQuad(t){return t*t}static easeOutQuad(t){return t*(2-t)}static easeInOutQuad(t){return t<.5?2*t*t:(4-2*t)*t-1}static easeInCubic(t){return t*t*t}static easeOutCubic(t){return--t*t*t+1}static easeInOutCubic(t){return t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1}static easeInQuart(t){return t*t*t*t}static easeOutQuart(t){return 1- --t*t*t*t}static easeInOutQuart(t){return t<.5?8*t*t*t*t:1-8*--t*t*t*t}static easeInSine(t){return 1-Math.cos(t*Math.PI/2)}static easeOutSine(t){return Math.sin(t*Math.PI/2)}static easeInOutSine(t){return-(Math.cos(Math.PI*t)-1)/2}static easeInExpo(t){return 0===t?0:Math.pow(2,10*(t-1))}static easeOutExpo(t){return 1===t?1:1-Math.pow(2,-10*t)}static easeInOutExpo(t){return 0===t||1===t?t:t<.5?.5*Math.pow(2,20*t-10):.5*(2-Math.pow(2,-20*t+10))}static easeInCirc(t){return 1-Math.sqrt(1-t*t)}static easeOutCirc(t){return Math.sqrt(1- --t*t)}static easeInOutCirc(t){return t<.5?.5*(1-Math.sqrt(1-4*t*t)):.5*(Math.sqrt(-(2*t-3)*(2*t-1))+1)}static easeInElastic(t,e=1,i=.3){if(0===t||1===t)return t;const s=i/(2*Math.PI)*Math.asin(1/e);return-e*Math.pow(2,10*(t-1))*Math.sin((t-1-s)*(2*Math.PI)/i)}static easeOutElastic(t,e=1,i=.3){if(0===t||1===t)return t;const s=i/(2*Math.PI)*Math.asin(1/e);return e*Math.pow(2,-10*t)*Math.sin((t-s)*(2*Math.PI)/i)+1}static easeInOutElastic(t,e=1,i=.3){if(0===t||1===t)return t;const s=i/(2*Math.PI)*Math.asin(1/e);return t<.5?e*Math.pow(2,10*(2*t-1))*Math.sin((2*t-1-s)*(2*Math.PI)/i)*-.5:e*Math.pow(2,-10*(2*t-1))*Math.sin((2*t-1-s)*(2*Math.PI)/i)*.5+1}static easeInBack(t,e=1.70158){return t*t*((e+1)*t-e)}static easeOutBack(t,e=1.70158){return--t*t*((e+1)*t+e)+1}static easeInOutBack(t,e=1.70158){const i=1.525*e;return t<.5?2*t*.5*(2*t)*(2*(i+1)*t-i):.5*((2*t-2)*(2*t-2)*((i+1)*(2*t-2)+i)+2)}static easeOutBounce(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}static easeInBounce(t){return 1-Easing.easeOutBounce(1-t)}static easeInOutBounce(t){return t<.5?.5*Easing.easeInBounce(2*t):.5*Easing.easeOutBounce(2*t-1)+.5}}function smoothstep(t,e,i){const s=Math.max(0,Math.min(1,(i-t)/(e-t)));return Easing.smoothstep(s)}function zoneTemperature(t,e,i){const{heatZone:s,coolZone:n,rate:a,heatMultiplier:r=1.5,coolMultiplier:o=1.5,middleMultiplier:h=.05,transitionWidth:l=.1}=i,c=smoothstep(s-l,s+.5*l,t),u=1-smoothstep(n-.5*l,n+l,t),d=1-c-u;let _=0;return c>0&&(_+=(1-e)*a*r*c),u>0&&(_+=(0-e)*a*o*u),d>0&&(_+=(t-e)*a*h*d),Math.max(0,Math.min(1,e+_))}function thermalBuoyancy(t,e,i){return(t-e)*i}function thermalGravity(t,e,i){return i*(t/e)}function heatTransfer(t,e,i,s,n){if(i>=s)return 0;return(e-t)*n}function heatTransferFalloff(t,e,i,s,n,a=1){if(i>=s)return 0;return(e-t)*n*Math.pow(1-i/s,a)}const CONFIG={kernel:{smoothingRadius:28},fluid:{restDensity:1.1,particleMass:1,pressureStiffness:1800,nearPressureStiffness:2.5,viscosity:.18,surfaceTension:0,maxForce:6e3},gas:{interactionRadius:34,pressure:12,diffusion:.08,drag:.04,buoyancy:260,neutralTemperature:.5,turbulence:16},external:{gravity:{x:0,y:820}}},EPS=1e-4;function mergeConfig(t={}){return{kernel:{...CONFIG.kernel,...t.kernel||{}},fluid:{...CONFIG.fluid,...t.fluid||{}},gas:{...CONFIG.gas,...t.gas||{}},external:{...CONFIG.external,...t.external||{}}}}const Kernels={poly6(t,e){const i=e*e;if(t>=i)return 0;const s=i-t;return 4/(Math.PI*Math.pow(e,8))*s*s*s},spikyPow2(t,e){if(t>=e)return 0;const i=e-t;return 6/(Math.PI*Math.pow(e,4))*i*i},spikyPow3(t,e){if(t>=e)return 0;const i=e-t;return 10/(Math.PI*Math.pow(e,5))*i*i*i},spikyPow2Derivative(t,e){if(0===t||t>=e)return 0;return-12/(Math.PI*Math.pow(e,4))*(e-t)},spikyPow3Derivative(t,e){if(0===t||t>=e)return 0;const i=e-t;return-30/(Math.PI*Math.pow(e,5))*i*i},spikyGradient(t,e){if(0===t||t>=e)return 0;return-30/(Math.PI*Math.pow(e,5))*((e-t)*(e-t))},viscosityLaplacian(t,e){if(t>=e)return 0;return 40/(Math.PI*Math.pow(e,5))*(e-t)}};function computeDensities(t,e={},i){const s=mergeConfig(e).kernel.smoothingRadius,n=s*s,a=i??buildSpatialHash(t,s),r=t.length,o=new Float32Array(r),h=new Float32Array(r);for(let e=0;e<r;e++)o[e]=Kernels.spikyPow2(0,s),h[e]=Kernels.spikyPow3(0,s),forEachNeighbor(e,t,a,n,(t,i,n,a)=>{const r=Math.sqrt(a);o[e]+=Kernels.spikyPow2(r,s),h[e]+=Kernels.spikyPow3(r,s)});return{densities:o,nearDensities:h}}function computePressures(t,e,i={}){const s=mergeConfig(i),n=t.length,a=new Float32Array(n),r=new Float32Array(n),{pressureStiffness:o,nearPressureStiffness:h,restDensity:l}=s.fluid;for(let i=0;i<n;i++)a[i]=(t[i]-l)*o,r[i]=e[i]*h;return{pressures:a,nearPressures:r}}function computeFluidForces(t,e={}){const i=mergeConfig(e),s=i.kernel.smoothingRadius,n=s*s,a=t.length,r=buildSpatialHash(t,s),{densities:o,nearDensities:h}=computeDensities(t,i,r),{pressures:l,nearPressures:c}=computePressures(o,h,i),u=new Array(a);for(let t=0;t<a;t++)u[t]={x:0,y:0};for(let e=0;e<a;e++){const a=t[e];Math.max(o[e],EPS),forEachNeighbor(e,t,r,n,(n,r,d,_)=>{if(n<=e)return;const p=Math.sqrt(_);if(p<EPS||p>=s)return;const g=t[n],f=1/p,m=-r*f,v=-d*f,y=Math.max(o[n],EPS),x=Math.max(h[n],EPS),b=.5*(l[e]+l[n]),w=.5*(c[e]+c[n]),M=b*Kernels.spikyPow2Derivative(p,s)/y+w*Kernels.spikyPow3Derivative(p,s)/x,S=m*M,P=v*M;u[e].x+=S,u[e].y+=P,u[n].x-=S,u[n].y-=P;const T=Kernels.poly6(_,s),C=i.fluid.viscosity*T,k=(g.vx-a.vx)*C,G=(g.vy-a.vy)*C;u[e].x+=k,u[e].y+=G,u[n].x-=k,u[n].y-=G})}return clampForces(u,i.fluid.maxForce),{forces:u,densities:o,pressures:l}}function computeThermalBuoyancy(t,e={}){var i;const s=mergeConfig(e),n=new Array(t.length),a=s.gas.neutralTemperature,r=s.gas.buoyancy;for(let e=0;e<t.length;e++){const o=t[e],h=((o.temperature??(null==(i=o.custom)?void 0:i.temperature)??s.gas.neutralTemperature)-a)*r;n[e]={x:0,y:-h}}return n}function computeGasForces(t,e={}){var i;const s=mergeConfig(e),n=s.gas.interactionRadius,a=n*n,r=t.length,o=buildSpatialHash(t,n),h=new Array(r);for(let t=0;t<r;t++)h[t]={x:0,y:0};for(let e=0;e<r;e++){const r=t[e],l=resolveMass(r,s),c=r.temperature??(null==(i=r.custom)?void 0:i.temperature)??s.gas.neutralTemperature;forEachNeighbor(e,t,o,a,(i,a,o,u)=>{var d;if(i<=e)return;if(0===u)return;const _=Math.sqrt(u),p=1/_,g=t[i],f=resolveMass(g,s),m=g.temperature??(null==(d=g.custom)?void 0:d.temperature)??s.gas.neutralTemperature,v=s.gas.pressure*(1-_/n),y=a*p*v,x=o*p*v;h[e].x+=y,h[e].y+=x,h[i].x-=y,h[i].y-=x;const b=s.gas.diffusion,w=(g.vx-r.vx)*b,M=(g.vy-r.vy)*b;h[e].x+=w*f,h[e].y+=M*f,h[i].x-=w*l,h[i].y-=M*l;const S=(c-m)*s.gas.buoyancy*.5;h[e].y-=S,h[i].y+=S})}for(let e=0;e<r;e++){const i=t[e],n=s.gas.drag;h[e].x+=-i.vx*n,h[e].y+=-i.vy*n,h[e].x+=(Math.random()-.5)*s.gas.turbulence,h[e].y+=(Math.random()-.5)*s.gas.turbulence}return clampForces(h,s.fluid.maxForce),{forces:h}}function integrateEuler(t,e,i,s={}){const n=mergeConfig(s),a=new Array(t.length);for(let s=0;s<t.length;s++){const r=t[s],o=e[s],h=resolveMass(r,n),l=o.x/h+n.external.gravity.x,c=o.y/h+n.external.gravity.y,u=r.vx+l*i,d=r.vy+c*i;a[s]={x:r.x+u*i,y:r.y+d*i,vx:u,vy:d}}return a}function blendForces(t,e,i){const s=Math.min(t.length,e.length),n=new Array(s);for(let a=0;a<s;a++)n[a]={x:Easing.lerp(t[a].x,e[a].x,i),y:Easing.lerp(t[a].y,e[a].y,i)};return n}function getDefaultFluidConfig(){return mergeConfig()}function resolveMass(t,e){return t.custom&&"number"==typeof t.custom.mass?t.custom.mass:t.mass?t.mass:t.size?.5*t.size+e.fluid.particleMass:e.fluid.particleMass}function clampForces(t,e){const i=e*e;for(let s=0;s<t.length;s++){const n=t[s],a=n.x*n.x+n.y*n.y;if(a>i){const t=1/Math.sqrt(a);n.x*=e*t,n.y*=e*t}}}function buildSpatialHash(t,e){const i=e,s=new Map;for(let e=0;e<t.length;e++){const n=t[e],a=`${Math.floor(n.x/i)},${Math.floor(n.y/i)}`;let r=s.get(a);r||(r=[],s.set(a,r)),r.push(e)}return{cellSize:i,buckets:s}}function forEachNeighbor(t,e,i,s,n){const a=e[t],r=i.cellSize,o=Math.floor(a.x/r),h=Math.floor(a.y/r);for(let r=-1;r<=1;r++)for(let l=-1;l<=1;l++){const c=`${o+r},${h+l}`,u=i.buckets.get(c);if(u)for(let i=0;i<u.length;i++){const r=u[i];if(r===t)continue;const o=e[r],h=a.x-o.x,l=a.y-o.y,c=h*h+l*l;c<s&&n(r,h,l,c)}}}const _Logger=class t{static disableAll(){t.enabledClasses=new Set,t.globalLevel=0}static disable(){t.enabled=!1}static enable(){t.enabled=!0}static setLevel(e){t.globalLevel=e}static enableFor(e){t.enabledClasses.add(e)}static disableFor(e){t.enabledClasses.delete(e)}static setOutput(e){t.output=e}constructor(t){this.className=t}static getLogger(e){return t.loggerz[e]||(t.loggerz[e]=new t(e)),t.loggerz[e]}_log(e,i,...s){t.enabled&&(t.globalLevel>=e||t.enabledClasses.has(this.className))&&t.output[i](`[${this.className}]`,...s)}log(...e){this._log(t.INFO,"log",...e)}warn(...e){this._log(t.WARN,"warn",...e)}error(...e){this._log(t.ERROR,"error",...e)}debug(...e){this._log(t.DEBUG,"log",...e)}table(...e){this._log(t.INFO,"table",...e)}groupCollapsed(e){t.enabled&&t.output.groupCollapsed(`[${this.className}] ${e}`)}groupEnd(){t.enabled&&t.output.groupEnd()}time(e){t.enabled&&t.output.time(`[${this.className}] ${e}`)}timeEnd(e){t.enabled&&t.output.timeEnd(`[${this.className}] ${e}`)}clear(){t.output.clear()}};__publicField(_Logger,"ERROR",1),__publicField(_Logger,"WARN",2),__publicField(_Logger,"INFO",3),__publicField(_Logger,"DEBUG",4),__publicField(_Logger,"globalLevel",_Logger.ERROR),__publicField(_Logger,"enabledClasses",new Set),__publicField(_Logger,"output",console),__publicField(_Logger,"enabled",!0),__publicField(_Logger,"loggerz",[]);let Logger=_Logger;class Loggable{constructor(t={}){this.name=t.name||this.constructor.name,this._logger=this.getLogger(t)}get logger(){return null==this._logger?this.getLogger():this._logger}trace(t="render"){this.logger.log(null==this.name?this.constructor.name:this.name,t,"x",this.x,"y",this.y,"w",this.width,"h",this.height,"opacity",this._opacity,"visible",this._visible,"active",this._active,"debug",this.debug)}getLogger(t){return Logger.getLogger(t.name||this.constructor.name)}}const _DebugTab=class t{static getInstance(){return t.instance||(t.instance=new t),t.instance}constructor(){this.createTab()}createTab(){this.tab=document.createElement("div"),Object.assign(this.tab.style,{position:"fixed",bottom:"0",left:"0",right:"0",height:"30px",backgroundColor:"#333",color:"#fff",padding:"5px",cursor:"pointer",fontFamily:"monospace",zIndex:"10000",display:"flex",justifyContent:"space-between",alignItems:"center"}),this.tab.innerText="Console";const t=document.createElement("div"),e=(t,e)=>{const i=document.createElement("button");return i.innerText=t,Object.assign(i.style,{marginLeft:"5px",padding:"2px 5px",fontFamily:"monospace",cursor:"pointer"}),i.onclick=e,i};this.paused=!1,this.scrollLock=!0,t.appendChild(e("Clear",()=>this.consoleArea.value="")),t.appendChild(e("Pause",()=>this.paused=!this.paused)),t.appendChild(e("Scroll Lock",()=>this.scrollLock=!this.scrollLock)),this.tab.appendChild(t),document.body.appendChild(this.tab),this.consoleArea=document.createElement("textarea"),Object.assign(this.consoleArea.style,{position:"fixed",bottom:"30px",left:"0",right:"0",height:"200px",display:"none",backgroundColor:"#111",color:"#0f0",fontFamily:"monospace",zIndex:"9999",padding:"10px",resize:"none"}),this.consoleArea.readOnly=!0,document.body.appendChild(this.consoleArea),this.tab.onclick=t=>{t.target===this.tab&&(this.consoleArea.style.display="none"===this.consoleArea.style.display?"block":"none")}}appendMessage(t,e,...i){if(this.paused)return;const s=`[${t.toUpperCase()}] ${e} ${i.join(" ")}\n`;this.consoleArea.value+=s,this.scrollLock&&(this.consoleArea.scrollTop=this.consoleArea.scrollHeight)}log(t,...e){this.appendMessage("log",t,...e)}warn(t,...e){this.appendMessage("warn",t,...e)}error(t,...e){this.appendMessage("error",t,...e)}table(t){const e=JSON.stringify(t,null,2);this.appendMessage("table",e)}groupCollapsed(t){this.appendMessage("group",`Group Start: ${t}`)}groupEnd(){this.appendMessage("group","Group End")}time(t){this[`time_${t}`]=performance.now()}timeEnd(t){const e=(performance.now()-this[`time_${t}`]).toFixed(2);this.appendMessage("time",`${t}: ${e} ms`)}};__publicField(_DebugTab,"instance");let DebugTab=_DebugTab;const _PainterEffects=class t{static dropShadow(t,e,i=0,s=0){Painter.ctx.shadowColor=t,Painter.ctx.shadowBlur=e,Painter.ctx.shadowOffsetX=i,Painter.ctx.shadowOffsetY=s}static clearShadow(){Painter.ctx.shadowColor="rgba(0, 0, 0, 0)",Painter.ctx.shadowBlur=0,Painter.ctx.shadowOffsetX=0,Painter.ctx.shadowOffsetY=0}static setAlpha(t){Painter.ctx.globalAlpha=t}static setBlendMode(t){Painter.ctx.globalCompositeOperation=t}static clipRect(t,e,i,s){Painter.ctx.beginPath(),Painter.ctx.rect(t,e,i,s),Painter.ctx.clip()}static clipCircle(t,e,i){Painter.ctx.beginPath(),Painter.shapes.arc(t,e,i,0,2*Math.PI),Painter.ctx.clip()}static blurRegion(t,e,i,s,n){const a=Painter.ctx.filter;Painter.ctx.filter=`blur(${n}px)`;const r=Painter.ctx.getImageData(t,e,i,s);Painter.ctx.putImageData(r,t,e),Painter.ctx.filter=a}static createGlow(e,i,s={}){const n="glow-"+Math.random().toString(36).substr(2,9),a={id:n,type:"glow",active:!0,time:0,color:e,blur:i,options:{...{pulseSpeed:0,pulseMin:.5*i,pulseMax:1.5*i,colorShift:0},...s},update(t){return Object.assign(this,t),this},stop(){return this.active=!1,t._activeEffects.delete(this.id),this},apply(){if(!this.active)return;let t=this.blur,e=this.color;if(this.options.pulseSpeed>0){const e=.5*Math.sin(this.time*this.options.pulseSpeed)+.5;t=this.options.pulseMin+e*(this.options.pulseMax-this.options.pulseMin)}return this.options.colorShift>0&&(e=e.replace("hue",this.time*this.options.colorShift%360)),Painter.ctx.shadowColor=e,Painter.ctx.shadowBlur=t,Painter.ctx.shadowOffsetX=0,Painter.ctx.shadowOffsetY=0,this.time+=1/60,this}};return t._activeEffects.set(n,a),t._startAnimationLoop(),a}static _startAnimationLoop(){if(null!==t._animationId)return;const e=()=>{if(t._activeEffects.forEach(t=>{t.active&&t.apply()}),0===t._activeEffects.size)return cancelAnimationFrame(t._animationId),void(t._animationId=null);t._animationId=requestAnimationFrame(e)};t._animationId=requestAnimationFrame(e)}static clearAllEffects(){t._activeEffects.forEach(t=>t.stop()),t._activeEffects.clear(),Painter.ctx.shadowColor="rgba(0, 0, 0, 0)",Painter.ctx.shadowBlur=0,Painter.ctx.shadowOffsetX=0,Painter.ctx.shadowOffsetY=0,Painter.ctx.filter="none",Painter.ctx.globalAlpha=1,Painter.ctx.globalCompositeOperation="source-over"}};__publicField(_PainterEffects,"_activeEffects",new Map),__publicField(_PainterEffects,"_animationId",null);let PainterEffects=_PainterEffects;class PainterImages{static draw(t,e=0,i=0,{width:s,height:n,crop:a=null,anchor:r="top‑left",rotation:o=0,scaleX:h=1,scaleY:l=1,flipX:c=!1,flipY:u=!1,alpha:d=1,smoothing:_=!0}={}){const p=Painter.ctx;if(!p||!t)return;const g=s??(a?a.sw:t.width??t.videoWidth),f=n??(a?a.sh:t.height??t.videoHeight),m=-g*({left:0,center:.5,right:1}[r.split("-").pop()]??0),v=-f*({top:0,center:.5,bottom:1}[r.split("-")[0]]??0);if(p.save(),p.imageSmoothingEnabled=_,p.globalAlpha*=d,p.translate(e,i),o&&p.rotate(o),(c||u)&&p.scale(c?-1:1,u?-1:1),p.scale(h,l),a){const{sx:e,sy:i,sw:s,sh:n}=a;p.drawImage(t,e,i,s,n,m,v,g,f)}else p.drawImage(t,m,v,g,f);p.restore()}static blit(t,e,i,s,n){this.draw(t,e,i,{width:s,height:n})}static createPattern(t,e="repeat"){return Painter.ctx.createPattern(t,e)}static fillPattern(t,e,i,s,n){const a=Painter.ctx;a.save(),a.fillStyle=t,a.fillRect(e,i,s,n),a.restore()}static createImageData(t,e){return Painter.ctx.createImageData(t,e)}static cloneImageData(t){return new ImageData(new Uint8ClampedArray(t.data),t.width,t.height)}static getImageData(t,e,i,s){return Painter.ctx.getImageData(t,e,i,s)}static putImageData(t,e,i,s=0,n=0,a=t.width,r=t.height){Painter.ctx.putImageData(t,e,i,s,n,a,r)}static mapPixels(t,e){const i=t.data;for(let t=0;t<i.length;t+=4){const s=t>>2,n=e(i[t],i[t+1],i[t+2],i[t+3],s);n&&([i[t],i[t+1],i[t+2],i[t+3]]=n)}return t}static setPixel(t,e,i,s,n,a,r=255){const o=4*(i*t.width+e),h=t.data;h[o]=s,h[o+1]=n,h[o+2]=a,h[o+3]=r}static async toBitmap({type:t="image/png",quality:e=.92}={}){const i=Painter.ctx.canvas,s=await i.convertToBlob({type:t,quality:e});return createImageBitmap(s)}static async createBitmap(t){return createImageBitmap(t)}static toImageData(t,e,i){if(t.length!==e*i*4)throw new Error("Invalid RGBA array size for given dimensions");return new ImageData(t,e,i)}static async createImageBitmapFromPixels(t,e,i){const s=this.toImageData(t,e,i);return await createImageBitmap(s)}static createPatternFromImageData(t,e="repeat"){const i=document.createElement("canvas");i.width=t.width,i.height=t.height;const s=i.getContext("2d");return s.putImageData(t,0,0),s.createPattern(i,e)}static createPatternFromPixels(t,e,i,s="repeat"){const n=this.toImageData(t,e,i);return this.createPatternFromImageData(n,s)}}class PainterLines{static path(t,e,i,s=1){const n=Painter.ctx;n.beginPath();for(const e of t){const[t,...i]=e;"M"===t?n.moveTo(...i):"L"===t?n.lineTo(...i):"C"===t?n.bezierCurveTo(...i):"Q"===t?n.quadraticCurveTo(...i):"Z"===t&&n.closePath()}e&&(n.fillStyle=e,Painter.colors.fill(e)),i&&(n.strokeStyle=i,n.lineWidth=s,Painter.colors.stroke())}static line(t,e,i,s,n,a){Painter.ctx.beginPath(),Painter.ctx.moveTo(t,e),Painter.ctx.lineTo(i,s),Painter.colors.stroke(n,a)}static beginPath(){Painter.ctx.beginPath()}static closePath(){Painter.ctx.closePath()}static moveTo(t,e){Painter.ctx.moveTo(t,e)}static lineTo(t,e){Painter.ctx.lineTo(t,e)}static bezierCurveTo(t,e,i,s,n,a){Painter.ctx.bezierCurveTo(t,e,i,s,n,a)}static dashedLine(t,e,i,s,n,a,r){Painter.ctx.beginPath(),a&&(Painter.ctx.strokeStyle=a),void 0!==r&&(Painter.ctx.lineWidth=r),Painter.ctx.setLineDash(n),Painter.ctx.moveTo(t,e),Painter.ctx.lineTo(i,s),Painter.colors.stroke(),Painter.ctx.setLineDash([])}static dottedLine(t,e,i,s,n=2,a=5,r){return Painter.lines.dashedLine(t,e,i,s,[n,a],r,n)}static setLineDash(t){Painter.ctx.setLineDash(t)}static resetLineDash(){Painter.ctx.setLineDash([])}static setLineWidth(t){Painter.ctx.lineWidth=t}static quadraticCurve(t,e,i,s,n,a,r,o){Painter.ctx.beginPath(),Painter.ctx.moveTo(t,e),Painter.ctx.quadraticCurveTo(i,s,n,a),r&&(Painter.ctx.strokeStyle=r),void 0!==o&&(Painter.ctx.lineWidth=o),Painter.colors.stroke()}}class PainterOpacity{static pushOpacity(t){const e=this._opacityStack[this._opacityStack.length-1]*t;this._opacityStack.push(e),Painter.logger.log("NEXT OPACITY WILL BE",e),Painter.effects.setAlpha(e)}static popOpacity(){if(this._opacityStack.length>1){this._opacityStack.pop();const t=this._opacityStack[this._opacityStack.length-1];Painter.logger.log("NEXT OPACITY WILL BE",t),Painter.effects.setAlpha(t)}}static _clone(){this._opacityStack=[...this._opacityStack]}static saveOpacityState(){this._opacityStateBackup=[...this._opacityStack]}static restoreOpacityState(){this._opacityStateBackup&&(this._opacityStack=this._opacityStateBackup,delete this._opacityStateBackup)}}__publicField(PainterOpacity,"_opacityStack",[1]);class PainterShapes{static rect(t,e,i,s,n){const a=Painter.ctx.fillStyle;Painter.colors.fill(n),Painter.ctx.fillRect(t,e,i,s),Painter.ctx.fillStyle=a}static outlineRect(t,e,i,s,n,a=1){const r=Painter.ctx.strokeStyle,o=Painter.ctx.lineWidth;Painter.ctx.strokeStyle=n,Painter.ctx.lineWidth=a,Painter.ctx.strokeRect(t,e,i,s),Painter.ctx.strokeStyle=r,Painter.ctx.lineWidth=o}static roundRect(t,e,i,s,n=0,a,r,o){let h;h="number"==typeof n?[n,n,n,n]:Array.isArray(n)?4===n.length?n:[n[0]||0,n[1]||n[0]||0,n[2]||n[0]||0,n[3]||n[1]||n[0]||0]:[0,0,0,0];const[l,c,u,d]=h,_=t+i,p=e+s;Painter.lines.beginPath(),Painter.lines.moveTo(t+l,e),Painter.lines.lineTo(_-c,e),this.arc(_-c,e+c,c,-Math.PI/2,0),Painter.lines.lineTo(_,p-u),this.arc(_-u,p-u,u,0,Math.PI/2),Painter.lines.lineTo(t+d,p),this.arc(t+d,p-d,d,Math.PI/2,Math.PI),Painter.lines.lineTo(t,e+l),this.arc(t+l,e+l,l,Math.PI,-Math.PI/2),Painter.lines.closePath(),a&&(Painter.fillStyle=a,Painter.colors.fill(a)),r&&Painter.colors.stroke(r,o)}static fillRoundRect(t,e,i,s,n=0,a){this.roundRect(t,e,i,s,n,a,null)}static strokeRoundRect(t,e,i,s,n=0,a,r){this.roundRect(t,e,i,s,n,null,a,r)}static fillCircle(t,e,i,s){Painter.logger.log("PainterShapes.fillCircle",t,e,i,s),Painter.lines.beginPath(),this.arc(t,e,i,0,2*Math.PI),Painter.colors.fill(s)}static arc(t,e,i,s,n,a){Painter.ctx.arc(t,e,i,s,n,a)}static strokeCircle(t,e,i,s,n){Painter.lines.beginPath(),this.arc(t,e,i,0,2*Math.PI),Painter.colors.stroke(s,n)}static fillEllipse(t,e,i,s,n=0,a){Painter.lines.beginPath(),this.ellipse(t,e,i,s,n,0,2*Math.PI),a&&(Painter.fillStyle=a),Painter.colors.fill(a)}static strokeEllipse(t,e,i,s,n=0,a,r){Painter.lines.beginPath(),this.ellipse(t,e,i,s,n,0,2*Math.PI),a&&(Painter.strokeStyle=a),void 0!==r&&(Painter.lineWidth=r),Painter.colors.stroke(a,r)}static ellipse(t,e,i,s,n,a,r,o){Painter.ctx.ellipse(t,e,i,s,n,a,r,o)}static polygon(t,e,i,s){if(!(t.length<2)){Painter.lines.beginPath(),Painter.lines.moveTo(t[0].x,t[0].y);for(let e=1;e<t.length;e++)Painter.lines.lineTo(t[e].x,t[e].y);Painter.lines.closePath(),e&&Painter.colors.fill(e),i&&Painter.colors.stroke(i,s)}}}class PainterText{static font(){return Painter.ctx.font}static setFont(t){Painter.ctx.font=t}static setTextAlign(t){Painter.ctx.textAlign=t}static setTextBaseline(t){Painter.ctx.textBaseline=t}static fillText(t,e,i,s,n){s&&(Painter.ctx.fillStyle=s),n&&(Painter.ctx.font=n),Painter.ctx.fillText(t,e,i)}static strokeText(t,e,i,s,n,a){s&&(Painter.ctx.strokeStyle=s),void 0!==n&&(Painter.ctx.lineWidth=n),a&&(Painter.ctx.font=a),Painter.ctx.strokeText(t,e,i)}static measureTextDimensions(t,e,i="start",s="alphabetic"){e&&(Painter.ctx.font=e);const n=Painter.ctx.measureText(t);let a=0;return"middle"===s&&(a=-1.5),{width:n.width,height:n.actualBoundingBoxAscent+n.actualBoundingBoxDescent,verticalAdjustment:a}}static measureTextWidth(t,e){return e&&(Painter.ctx.font=e),Painter.ctx.measureText(t).width}static outlinedText(t,e,i,s,n,a,r){r&&(Painter.ctx.font=r),Painter.ctx.strokeStyle=n,Painter.ctx.lineWidth=a,Painter.ctx.strokeText(t,e,i),Painter.ctx.fillStyle=s,Painter.ctx.fillText(t,e,i)}static wrappedText(t,e,i,s,n,a,r){a&&(Painter.ctx.fillStyle=a),r&&(Painter.ctx.font=r);const o=t.split(" ");let h="",l="",c=1;for(let t=0;t<o.length;t++){l=h+o[t]+" ";Painter.ctx.measureText(l).width>s&&t>0?(Painter.ctx.fillText(h,e,i),h=o[t]+" ",i+=n,c++):h=l}return Painter.ctx.fillText(h,e,i),c*n}static textOnPath(t,e,i,s,n=!1){if(e.length<2)return;i&&(Painter.ctx.fillStyle=i),s&&(Painter.ctx.font=s);const a=t.split(""),r=a.map(t=>Painter.ctx.measureText(t).width);n&&(a.reverse(),r.reverse(),e.reverse());let o=0;for(let t=1;t<e.length;t++){const i=e[t].x-e[t-1].x,s=e[t].y-e[t-1].y;o+=Math.sqrt(i*i+s*s)}let h=(o-r.reduce((t,e)=>t+e,0))/2;h<0&&(h=0);let l=h;for(let t=0;t<a.length;t++){const i=r[t],{x:s,y:n,angle:o}=getPositionOnPath(e,l);Painter.ctx.save(),Painter.ctx.translate(s,n),Painter.ctx.rotate(o),Painter.ctx.fillText(a[t],0,0),Painter.ctx.restore(),l+=i}}static getPositionOnPath(t,e){let i=0;for(let s=1;s<t.length;s++){const n=t[s-1],a=t[s],r=a.x-n.x,o=a.y-n.y,h=Math.sqrt(r*r+o*o);if(i+h>=e){const t=(e-i)/h;return{x:n.x+r*t,y:n.y+o*t,angle:Math.atan2(o,r)}}i+=h}const s=t[t.length-1],n=t[t.length-2],a=Math.atan2(s.y-n.y,s.x-n.x);return{x:s.x,y:s.y,angle:a}}}const _Painter=class t{static get colors(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"colors",__privateGet(this,__colors)),__privateGet(this,__colors)}static get effects(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"effects",__privateGet(this,__effects)),__privateGet(this,__effects)}static get img(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"img",__privateGet(this,__img)),__privateGet(this,__img)}static get lines(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"lines",__privateGet(this,__lines)),__privateGet(this,__lines)}static get opacity(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"opacity",__privateGet(this,__opacity)),__privateGet(this,__opacity)}static get shapes(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"shapes",__privateGet(this,__shapes)),__privateGet(this,__shapes)}static get text(){return __privateMethod(this,_Painter_static,checkInitialized_fn).call(this,"text",__privateGet(this,__text)),__privateGet(this,__text)}static set ctx(t){this._ctx=t}static get ctx(){if(!this._ctx)throw new Error("Cannot access Painter.ctx before initialization!");return this._ctx}static init(e){this._ctx=e,this.saveStack=[],__privateSet(this,__colors,PainterColors),__privateSet(this,__effects,PainterEffects),__privateSet(this,__img,PainterImages),__privateSet(this,__lines,PainterLines),__privateSet(this,__opacity,PainterOpacity),__privateSet(this,__shapes,PainterShapes),__privateSet(this,__text,PainterText),t.logger=Logger.getLogger("Painter"),t.saveStack=[]}static setContext(t){this._ctx=t}static save(){const e=((new Error).stack.split("\n")[2]||"").match(/at\s+(\w+)\.(\w+)/),i=e?`${e[1]}.${e[2]}`:"unknown";this.saveStack.push(i),this.logger.log(`Painter.save() by: ${i}`),this.ctx.save(),t.opacity.saveOpacityState()}static restore(){if(0===this.saveStack.length)return void console.error("PAINTER ERROR: restore() without matching save()!");const e=this.saveStack.pop();this.logger.log(`Painter.restore() balancing save from: ${e}`),this.ctx.restore(),t.opacity.restoreOpacityState()}static translateTo(t,e){(isNaN(t)||void 0===t)&&(t=0),(isNaN(e)||void 0===e)&&(e=0),this.logger.log("moveTo",t,e),this.ctx.translate(t,e)}static resetPosition(){this.logger.log("resetPosition");const t=this.ctx.getTransform();this.ctx.setTransform(t.a,t.b,t.c,t.d,0,0)}static withPosition(t,e,i){this.logger.log("withPosition",t,e),this.save(),this.translateTo(t,e),i(),this.restore()}static clear(e=0,i=0,s=t.ctx.canvas.width,n=t.ctx.canvas.height){t.ctx.clearRect(e,i,s,n)}static translate(e,i){t.ctx.translate(e,i)}static rotate(e){t.logger.log("Painter.rotate",e),t.ctx.rotate(e)}static scale(e,i){t.logger.log("Painter.scale",e,i),t.ctx.scale(e,i)}static useCtx(t,e={}){const i=this.ctx,{saveState:s=!1}=e;s&&this.save(),i.beginPath(),t(i),i.beginPath(),s&&this.restore()}};__colors=new WeakMap,__effects=new WeakMap,__img=new WeakMap,__lines=new WeakMap,__opacity=new WeakMap,__shapes=new WeakMap,__text=new WeakMap,_Painter_static=new WeakSet,checkInitialized_fn=function(t,e){if(!e)throw new Error(`Painter.${t} is not initialized. Call Painter.init(ctx) first.`)},__privateAdd(_Painter,_Painter_static),__privateAdd(_Painter,__colors,null),__privateAdd(_Painter,__effects,null),__privateAdd(_Painter,__img,null),__privateAdd(_Painter,__lines,null),__privateAdd(_Painter,__opacity,null),__privateAdd(_Painter,__shapes,null),__privateAdd(_Painter,__text,null),__publicField(_Painter,"logger");let Painter=_Painter;class PainterColors{static fill(t){Painter.logger.log("PainterColors.fill - before:",Painter.ctx.fillStyle,"setting to:",t),Painter.ctx.fillStyle,Painter.ctx.fillStyle=t,Painter.ctx.fill(),Painter.logger.log("PainterColors.fill - after:",Painter.ctx.fillStyle)}static strokeOptions(t){t.color&&(Painter.ctx.strokeStyle=t.color),void 0!==t.lineWidth&&(Painter.ctx.lineWidth=t.lineWidth),t.lineCap&&(Painter.ctx.lineCap=t.lineCap),t.lineJoin&&(Painter.ctx.lineJoin=t.lineJoin),t.strokeStyle&&(Painter.ctx.strokeStyle=t.strokeStyle)}static stroke(t,e){t&&(Painter.ctx.strokeStyle=t),void 0!==e&&(Painter.ctx.lineWidth=e),Painter.ctx.stroke()}static setFillColor(t){Painter.ctx.fillStyle=t}static setStrokeColor(t){Painter.ctx.strokeStyle=t}static randomColorRGB(){const t=Math.floor(360*Math.random()),e=70+Math.floor(30*Math.random()),i=50+Math.floor(20*Math.random());return Painter.colors.hslToRgb(t,e,i)}static randomColorRGBA(t=255){const[e,i,s]=this.randomColorRGB();return[e,i,s,t]}static randomColorHSL(){return`hsl(${360*Math.random()}, 100%, 50%)`}static randomColorHSL_RGBA(t=255){const e=360*Math.random(),i=60+40*Math.random(),s=40+40*Math.random(),[n,a,r]=Painter.colors.hslToRgb(e,i,s);return[n,a,r,t]}static randomColorHEX(){return"#"+(1048575*Math.random()*1e6).toString(16).slice(0,6)}static parseColorString(t){if((t=t.trim().toLowerCase()).startsWith("hsl")){const e=t.replace(/hsla?\(|\)/g,""),[i,s,n]=e.split(",").map(t=>t.trim()),a=parseFloat(i),r=parseFloat(s)/100,o=parseFloat(n)/100;return Painter.colors.hslToRgb(a,r,o)}if(t.startsWith("#"))return hexToRgb(t);if(t.startsWith("rgb")){const e=t.replace(/rgba?\(|\)/g,""),[i,s,n]=e.split(",").map(t=>parseInt(t.trim()));return[i,s,n]}return[0,0,0]}static rgbArrayToCSS([t,e,i]){return`rgb(${Math.round(t)}, ${Math.round(e)}, ${Math.round(i)})`}static hslToRgb(t,e,i){i/=100;const s=e=>(e+t/30)%12,n=(e/=100)*Math.min(i,1-i),a=t=>i-n*Math.max(-1,Math.min(s(t)-3,Math.min(9-s(t),1)));return[Math.round(255*a(0)),Math.round(255*a(8)),Math.round(255*a(4))]}static rgbToHsl(t,e,i){t/=255,e/=255,i/=255;const s=Math.max(t,e,i),n=Math.min(t,e,i),a=s-n;let r=0,o=0,h=(s+n)/2;if(0!==a)switch(o=a/(1-Math.abs(2*h-1)),s){case t:r=((e-i)/a+6)%6*60;break;case e:r=60*((i-t)/a+2);break;case i:r=60*((t-e)/a+4)}return[r%360,o,h]}static hexToRgb(t){const e=t.replace("#","");return[parseInt(e.substring(0,2),16),parseInt(e.substring(2,4),16),parseInt(e.substring(4,6),16)]}static linearGradient(t,e,i,s,n){const a=Painter.ctx.createLinearGradient(t,e,i,s);for(const t of n)a.addColorStop(t.offset,t.color);return a}static radialGradient(t,e,i,s,n,a,r){const o=Painter.ctx.createRadialGradient(t,e,i,s,n,a);for(const t of r)o.addColorStop(t.offset,t.color);return o}static verticalGradient(t,e,i,s,n){return Painter.colors.linearGradient(t,e,t,e+s,n)}static horizontalGradient(t,e,i,s,n){return Painter.colors.linearGradient(t,e,t+i,e,n)}static conicGradient(t,e,i,s){if("function"==typeof Painter.ctx.createConicGradient){const n=Painter.ctx.createConicGradient(i,t,e);for(const t of s)n.addColorStop(t.offset,t.color);return n}return null}static rgba(t,e,i,s=1){return`rgba(${Math.round(t)}, ${Math.round(e)}, ${Math.round(i)}, ${s})`}static hsl(t,e,i){return`hsl(${t}, ${e}%, ${i}%)`}static hsla(t,e,i,s){return`hsla(${t}, ${e}%, ${i}%, ${s})`}}class Euclidian extends Loggable{constructor(t={}){super(t),this._x="number"==typeof t.x?t.x:0,this._y="number"==typeof t.y?t.y:0,this._width="number"==typeof t.width?t.width:0,this._height="number"==typeof t.height?t.height:0,this.logger.log("Euclidian",this._x,this._y,this._width,this._height)}get x(){return this._x}set x(t){this.validateProp(t,"x"),this._x=t}get y(){return this._y}set y(t){this.validateProp(t,"y"),this._y=t}get width(){return this._width}set width(t){this.validateProp(t,"width"),this._width=Math.max(0,t)}get height(){return this._height}set height(t){this.validateProp(t,"height"),this._height=Math.max(0,t)}get debug(){return this._debug}set debug(t){this.validateProp(t,"debug"),this._debug=Boolean(t)}get debugColor(){return this._debugColor}set debugColor(t){this.validateProp(t,"debugColor"),this._debugColor=t}validateProp(t,e){if(null==t)throw new Error("Invalid property value: "+e+" "+t)}}class Geometry2d extends Euclidian{constructor(t={}){super(t),this._minX=t.minX,this._maxX=t.maxX,this._minY=t.minY,this._maxY=t.maxY,this._boundsDirty=!0,this._cachedBounds=null,this.crisp=t.crisp??!0,this.logger.log("Geometry2d",this.x,this.y,this.width,this.height)}update(){this.trace("Geometry2d.update"),this.applyConstraints(),this.getBounds()}get minX(){return this._minX}set minX(t){this._minX=t}get maxX(){return this._maxX}set maxX(t){this._maxX=t}get minY(){return this._minY}set minY(t){this._minY=t}get maxY(){return this._maxY}set maxY(t){this._maxY=t}get boundsDirty(){return this._boundsDirty}applyConstraints(){void 0!==this._minX&&(this.x=Math.max(this.x,this._minX)),void 0!==this._maxX&&(this.x=Math.min(this.x,this._maxX)),void 0!==this._minY&&(this.y=Math.max(this.y,this._minY)),void 0!==this._maxY&&(this.y=Math.min(this.y,this._maxY)),this.crisp&&(this.x=Math.round(this.x),this.y=Math.round(this.y),this.width=Math.round(this.width),this.height=Math.round(this.height))}getBounds(){return!this._boundsDirty&&this._cachedBounds||(this._cachedBounds=this.calculateBounds(),this._boundsDirty=!1),this._cachedBounds}calculateBounds(){return{width:this.width,height:this.height,x:this.x,y:this.y}}getLocalPosition(){let t=0,e=0;return this.parent&&(t=this.parent.x,e=this.parent.y),{x:this.x-t-this.width/2,y:this.y-e-this.height/2}}markBoundsDirty(){this._boundsDirty=!0}validateProp(t,e){super.validateProp(t,e);t!==this[e]&&this.markBoundsDirty()}setTopLeft(t,e){return this.x=t+this.width/2,this.y=e+this.height/2,this}setCenter(t,e){return this.x=t,this.y=e,this}}class Traceable extends Geometry2d{constructor(t={}){super(t),this._debug=Boolean(t.debug),this._debugColor="string"==typeof t.debugColor?t.debugColor:"#0f0",this.logger.log("Traceable",this.x,this.y,this.width,this.height)}drawDebug(){if(!this._debug)return;const t=this.getDebugBounds();this.logger.log(this.constructor.name,"drawDebug",t.x,t.y,t.width,t.height),Painter.shapes.outlineRect(t.x,t.y,t.width,t.height,this._debugColor,2)}getDebugBounds(){return{width:this.width,height:this.height,x:-this.width/2,y:-this.height/2}}trace(t="render"){this.logger.log(null==this.name?this.constructor.name:this.name,t,"x",this.x,"y",this.y,"w",this.width,"h",this.height,"opacity",this._opacity,"visible",this._visible,"active",this._active,"debug",this.debug)}}class Renderable extends Traceable{constructor(t={}){super(t),this._visible=!1!==t.visible,this._opacity="number"==typeof t.opacity?t.opacity:1,this._active=!1!==t.active,this.zIndex=t.zIndex??0,this._shadowColor=t.shadowColor??void 0,this._shadowBlur=t.shadowBlur??0,this._shadowOffsetX=t.shadowOffsetX??0,this._shadowOffsetY=t.shadowOffsetY??0,this._cacheRendering=t.cacheRendering??!1,this._cacheCanvas=null,this._cacheDirty=!0,this._cachePadding=t.cachePadding??2,this._tick=0,this.logger.log("Renderable",this.x,this.y,this.width,this.height)}render(){if(this._visible&&!(this._opacity<=0)){if(Painter.save(),Painter.effects.setBlendMode(this._blendMode),this.crisp?Painter.translateTo(Math.round(this.x),Math.round(this.y)):Painter.translateTo(this.x,this.y),this.applyShadow(Painter.ctx),this._cacheRendering&&"Renderable"!==this.constructor.name){const t="number"==typeof this.width?this.width:0,e="number"==typeof this.height?this.height:0,i=2*this._cachePadding,s=Math.ceil(t+i)||1,n=Math.ceil(e+i)||1;this._cacheCanvas&&this._cacheCanvas.width===s&&this._cacheCanvas.height===n||(this._cacheCanvas=document.createElement("canvas"),this._cacheCanvas.width=s,this._cacheCanvas.height=n,this._cacheDirty=!0),this._cacheDirty&&(this._renderToCache(s,n),this._cacheDirty=!1),Painter.opacity.pushOpacity(this._opacity);const a=this.rotation??0,r=this.scaleX??1,o=this.scaleY??1;Painter.img.draw(this._cacheCanvas,0,0,{width:s,height:n,rotation:a,scaleX:r,scaleY:o,anchor:"center"}),Painter.opacity.popOpacity()}else Painter.opacity.pushOpacity(this._opacity),this.draw(),Painter.opacity.popOpacity();Painter.restore()}}_renderToCache(t,e){const i=this._cacheCanvas.getContext("2d");i.clearRect(0,0,t,e);const s=Painter.ctx;Painter.ctx=i,this._isCaching=!0,i.save(),i.translate(t/2,e/2),this.draw(),i.restore(),this._isCaching=!1,Painter.ctx=s}invalidateCache(){this._cacheDirty=!0}draw(){this.drawDebug()}update(t){this.trace("Renderable.update"),this._tick+=t,super.update(t)}applyShadow(t){this._shadowColor&&(t.shadowColor=this._shadowColor,t.shadowBlur=this._shadowBlur,t.shadowOffsetX=this._shadowOffsetX,t.shadowOffsetY=this._shadowOffsetY)}get visible(){return this._visible}set visible(t){this._visible=Boolean(t)}get width(){return super.width}set width(t){super.width=t,this.invalidateCache()}get height(){return super.height}set height(t){super.height=t,this.invalidateCache()}get active(){return this._active}set active(t){this._active=Boolean(t)}get opacity(){return this._opacity}set opacity(t){this._opacity=Math.min(1,Math.max(0,"number"==typeof t?t:1))}get shadowColor(){return this._shadowColor}set shadowColor(t){this._shadowColor=t,this.invalidateCache()}get shadowBlur(){return this._shadowBlur}set shadowBlur(t){this._shadowBlur=t,this.invalidateCache()}get shadowOffsetX(){return this._shadowOffsetX}set shadowOffsetX(t){this._shadowOffsetX=t,this.invalidateCache()}get shadowOffsetY(){return this._shadowOffsetY}set shadowOffsetY(t){this._shadowOffsetY=t,this.invalidateCache()}get tick(){return this._tick}get cacheRendering(){return this._cacheRendering}set cacheRendering(t){this._cacheRendering=Boolean(t),t&&this.invalidateCache()}}const _Transform=class t{constructor(t){this._owner=t}get owner(){return this._owner}x(t){return this._owner._x=t,this._owner.markBoundsDirty(),this}y(t){return this._owner._y=t,this._owner.markBoundsDirty(),this}position(t,e){return this._owner._x=t,this._owner._y=e,this._owner.markBoundsDirty(),this}translateBy(t,e){return this._owner._x+=t,this._owner._y+=e,this._owner.markBoundsDirty(),this}width(t){var e,i;return this._owner._width=Math.max(0,t),this._owner.markBoundsDirty(),null==(i=(e=this._owner).invalidateCache)||i.call(e),this}height(t){var e,i;return this._owner._height=Math.max(0,t),this._owner.markBoundsDirty(),null==(i=(e=this._owner).invalidateCache)||i.call(e),this}size(t,e){var i,s;return this._owner._width=Math.max(0,t),this._owner._height=Math.max(0,e),this._owner.markBoundsDirty(),null==(s=(i=this._owner).invalidateCache)||s.call(i),this}rotation(t){return this._owner._rotation=t*Math.PI/180,this._owner.markBoundsDirty(),this}rotationRad(t){return this._owner._rotation=t,this._owner.markBoundsDirty(),this}rotateBy(t){return this._owner._rotation+=t*Math.PI/180,this._owner.markBoundsDirty(),this}scaleX(t){return this._owner._scaleX=t,this._owner.markBoundsDirty(),this}scaleY(t){return this._owner._scaleY=t,this._owner.markBoundsDirty(),this}scale(t){return this._owner._scaleX=t,this._owner._scaleY=t,this._owner.markBoundsDirty(),this}scaleBy(t){return this._owner._scaleX*=t,this._owner._scaleY*=t,this._owner.markBoundsDirty(),this}set(t){var e,i;let s=!1;return void 0!==t.x&&(this._owner._x=t.x),void 0!==t.y&&(this._owner._y=t.y),void 0!==t.width&&(this._owner._width=Math.max(0,t.width),s=!0),void 0!==t.height&&(this._owner._height=Math.max(0,t.height),s=!0),void 0!==t.rotation&&(this._owner._rotation=t.rotation*Math.PI/180),void 0!==t.scaleX&&(this._owner._scaleX=t.scaleX),void 0!==t.scaleY&&(this._owner._scaleY=t.scaleY),this._owner.markBoundsDirty(),s&&(null==(i=(e=this._owner).invalidateCache)||i.call(e)),this}reset(){return this._owner._rotation=0,this._owner._scaleX=1,this._owner._scaleY=1,this._owner.markBoundsDirty(),this}resetAll(){var t,e;return this._owner._x=0,this._owner._y=0,this._owner._width=0,this._owner._height=0,this._owner._rotation=0,this._owner._scaleX=1,this._owner._scaleY=1,this._owner.markBoundsDirty(),null==(e=(t=this._owner).invalidateCache)||e.call(t),this}toObject(){return{x:this._owner._x,y:this._owner._y,width:this._owner._width,height:this._owner._height,rotation:180*this._owner._rotation/Math.PI,scaleX:this._owner._scaleX,scaleY:this._owner._scaleY}}copyFrom(e){const i=e instanceof t?e.toObject():e;return this.set(i)}static handleDirectSet(e,i){if(t.strictMode)throw new Error(`Direct property assignment "${e} = ${i}" is disabled. Use shape.transform.${e}(${i}) instead. Set Transform.strictMode = false to allow direct assignment.`);console.warn(`[Deprecation] Direct assignment "${e} = ${i}" is deprecated. Use shape.transform.${e}(${i}) instead.`)}};__publicField(_Transform,"strictMode",!1);let Transform=_Transform;class Transformable extends Renderable{constructor(t={}){super(t),this._rotation=t.rotation*Math.PI/180,this._scaleX=t.scaleX??1,this._scaleY=t.scaleY??1,this.transform=new Transform(this),this.logger.log("Transformable",this.x,this.y,this.width,this.height)}draw(){this.applyTransforms(),this.drawDebug()}applyTransforms(){this._isCaching||(Painter.rotate(this._rotation),Painter.scale(this._scaleX,this._scaleY))}get rotation(){return this._rotation}set rotation(t){this._rotation=t*Math.PI/180,this.markBoundsDirty()}get scaleX(){return this._scaleX}set scaleX(t){this._scaleX=t,this.markBoundsDirty()}get scaleY(){return this._scaleY}set scaleY(t){this._scaleY=t,this.markBoundsDirty()}calculateBounds(){const t=this.width/2,e=this.height/2,i=[{x:-t,y:-e},{x:t,y:-e},{x:t,y:e},{x:-t,y:e}],s=Math.cos(this._rotation),n=Math.sin(this._rotation),a=i.map(({x:t,y:e})=>{t*=this._scaleX,e*=this._scaleY;const i=t*n+e*s;return{x:t*s-e*n+this.x,y:i+this.y}}),r=a.map(t=>t.x),o=a.map(t=>t.y),h=Math.min(...r),l=Math.max(...r),c=Math.min(...o),u=Math.max(...o);return{x:(h+l)/2,y:(c+u)/2,width:l-h,height:u-c}}}class Shape extends Transformable{constructor(t={}){super(t),this._color=t.color??null,this._stroke=t.stroke??null,this._lineWidth=t.lineWidth??1,this._lineJoin=t.lineJoin??"miter",this._lineCap=t.lineCap??"butt",this._miterLimit=t.miterLimit??10,this.logger.log("Shape",this.x,this.y,this.width,this.height)}get color(){return this._color}set color(t){this._color=t,this.invalidateCache()}get stroke(){return this._stroke}set stroke(t){this._stroke=t,this.invalidateCache()}get lineWidth(){return this._lineWidth}set lineWidth(t){this._lineWidth=Math.max(0,t),this.invalidateCache()}get lineJoin(){return this._lineJoin}set lineJoin(t){this._lineJoin=t,this.invalidateCache()}get lineCap(){return this._lineCap}set lineCap(t){this._lineCap=t,this.invalidateCache()}get miterLimit(){return this._miterLimit}set miterLimit(t){this._miterLimit=t,this.invalidateCache()}}class Group extends Transformable{constructor(t={}){super(t),this._collection=new ZOrderedCollection({sortByZIndex:t.sortByZIndex||!0}),this._collection._owner=this,this._childrenVersion=0,this._cachedBounds=null,t.width=Math.max(0,t.width||0),t.height=Math.max(0,t.height||0),this.userDefinedWidth=t.width,this.userDefinedHeight=t.height,this.userDefinedDimensions=void 0!==t.width&&void 0!==t.height&&(t.width>0||t.height>0)}add(t){if(null==t||null==t)throw new Error("Object is null or undefined");if(!(t instanceof Transformable))throw new TypeError("Group can only add Transformable instances");return t.parent=this,this._collection.add(t),this._childrenVersion++,this.markBoundsDirty(),this.invalidateCache(),t}remove(t){const e=this._collection.remove(t);return e&&(t.parent=null,this._childrenVersion++,this.markBoundsDirty(),this.invalidateCache()),e}clear(){this._collection.clear(),this._childrenVersion++,this.markBoundsDirty(),this.invalidateCache()}bringToFront(t){return this._collection.bringToFront(t)}sendToBack(t){return this._collection.sendToBack(t)}bringForward(t){return this._collection.bringForward(t)}sendBackward(t){return this._collection.sendBackward(t)}draw(){super.draw(),this.logger.log("Group.draw children:",this.children.length),this._renderChildren()}_renderChildren(){const t=this._collection.getSortedChildren();for(let e=0;e<t.length;e++){const i=t[e];i.visible&&(Painter.save(),i.render(),Painter.restore())}}update(t){this.logger.groupCollapsed("Group.update");const e=this._collection.getSortedChildren();for(let i=0;i<e.length;i++){const s=e[i];s.active&&"function"==typeof s.update&&s.update(t)}super.update(t),this.logger.groupEnd()}get children(){var t;return(null==(t=this._collection)?void 0:t.children)||[]}get width(){return this.userDefinedDimensions?this._width:this.getBounds().width}set width(t){const e=Math.max(0,t);this._width=e,this.userDefinedWidth=e,this.userDefinedDimensions=(this.userDefinedWidth>0||this.userDefinedHeight>0)&&void 0!==this.userDefinedWidth&&void 0!==this.userDefinedHeight,this.markBoundsDirty()}get height(){return this.userDefinedDimensions?this._height:this.getBounds().height}set height(t){const e=Math.max(0,t);this._height=e,this.userDefinedHeight=e,this.userDefinedDimensions=(this.userDefinedWidth>0||this.userDefinedHeight>0)&&void 0!==this.userDefinedWidth&&void 0!==this.userDefinedHeight,this.markBoundsDirty()}calculateBounds(){var t;if(this.userDefinedDimensions)return{x:this.x,y:this.y,width:this._width,height:this._height};if(!(null==(t=this.children)?void 0:t.length))return{x:this.x,y:this.y,width:0,height:0};let e=1/0,i=1/0,s=-1/0,n=-1/0;for(const t of this.children){const a=t.x,r=t.y,o=t.width,h=t.height,l=a-o/2,c=a+o/2,u=r-h/2,d=r+h/2;e=Math.min(e,l),s=Math.max(s,c),i=Math.min(i,u),n=Math.max(n,d)}const a=s-e,r=n-i;return{x:this.x,y:this.y,width:a,height:r}}getDebugBounds(){const t=this.calculateBounds();return{width:t.width,height:t.height,x:-t.width/2,y:-t.height/2}}forEachTransform(t){return this.children.forEach((e,i)=>{e.transform&&t(e.transform,e,i)}),this}translateChildren(t,e){return this.forEachTransform(i=>i.translateBy(t,e))}scaleChildren(t){return this.forEachTransform(e=>e.scaleBy(t))}rotateChildren(t){return this.forEachTransform(e=>e.rotateBy(t))}resetChildTransforms(){return this.forEachTransform(t=>t.reset())}}class Arc extends Shape{constructor(t,e,i,s={}){super(s),this.radius=t,this.startAngle=e,this.endAngle=i}draw(){super.draw(),Painter.lines.beginPath(),Painter.shapes.arc(0,0,this.radius,this.startAngle,this.endAngle,!1),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}getBounds(){const t=this.radius;return{x:this.x,y:this.y,width:2*t,height:2*t}}}class Circle extends Shape{constructor(t,e={}){super(e),this._radius=t,this.width=2*t,this.height=2*t}draw(){super.draw(),this.color&&Painter.shapes.fillCircle(0,0,this._radius,this.color),this.stroke&&Painter.shapes.strokeCircle(0,0,this._radius,this.stroke,this.lineWidth)}calculateBounds(){const t=2*this._radius;return this.trace("Circle.calculateBounds:"+t),{x:this.x,y:this.y,width:t,height:t}}get radius(){return this._radius}set radius(t){this.validateProp(t,"radius"),t!=this._radius&&(this._radius=t,this.width=2*t,this.height=2*t,this._boundsDirty=!0,this.calculateBounds())}}class Cloud extends Shape{constructor(t=40,e={}){super(e),this.size=t,this.width=2*t,this.height=2*t}draw(){super.draw();const t=this.size,e=Painter.ctx,i=[{x:.5*-t,y:0,r:.4*t},{x:.2*-t,y:.3*-t,r:.35*t},{x:.2*t,y:.35*-t,r:.4*t},{x:.5*t,y:0,r:.35*t},{x:0,y:.15*t,r:.5*t}];if(this.color){e.fillStyle=this.color;for(const t of i)e.beginPath(),e.arc(t.x,t.y,t.r,0,2*Math.PI),e.fill()}if(this.stroke){e.strokeStyle=this.stroke,e.lineWidth=this.lineWidth;for(const t of i)e.beginPath(),e.arc(t.x,t.y,t.r,0,2*Math.PI),e.stroke()}}getBounds(){const t=2*this.size;return{x:this.x,y:this.y,width:t,height:t}}}class BezierShape extends Shape{constructor(t=[],e={}){super(e),this.path=t}draw(){super.draw(),Painter.lines.path(this.path,this.color,this.stroke,this.lineWidth)}getBounds(){return{x:this.x,y:this.y,width:100,height:100}}}class Rectangle extends Shape{constructor(t={}){super(t)}draw(){super.draw(),this.drawRect()}drawRect(){const t=-this.width/2,e=-this.height/2;this.color&&Painter.shapes.rect(t,e,this.width,this.height,this.color),this.stroke&&Painter.shapes.outlineRect(t,e,this.width,this.height,this.stroke,this.lineWidth)}}class RoundedRectangle extends Shape{constructor(t=0,e={}){super(e),"number"==typeof t?this.radii=[t,t,t,t]:Array.isArray(t)?this.radii=4===t.length?t:[t[0]||0,t[1]||t[0]||0,t[2]||t[0]||0,t[3]||t[1]||t[0]||0]:this.radii=[0,0,0,0]}draw(){super.draw();const t=-this.width/2,e=-this.height/2;this.color&&this.stroke?Painter.shapes.roundRect(t,e,this.width,this.height,this.radii,this.color,this.stroke,this.lineWidth):this.color?Painter.shapes.fillRoundRect(t,e,this.width,this.height,this.radii,this.color):this.stroke&&Painter.shapes.strokeRoundRect(t,e,this.width,this.height,this.radii,this.stroke,this.lineWidth)}getBounds(){return{x:this.x,y:this.y,width:this.width,height:this.height}}}class PatternRectangle extends Shape{constructor(t=null,e="repeat",i={}){super(i),this.image=t,this.repetition=e,this.pattern=null,t&&this._tryCreatePattern(t)}_tryCreatePattern(t){t instanceof HTMLImageElement||"boolean"==typeof t.complete?t.complete?this._createPattern():t.addEventListener("load",()=>this._createPattern(),{once:!0}):this._createPattern()}_createPattern(){this.pattern=Painter.img.createPattern(this.image,this.repetition)}setImage(t,e){this.image=t,e&&(this.repetition=e),this.pattern=null,this._tryCreatePattern(t)}draw(){super.draw(),!this.pattern&&this.image&&this._tryCreatePattern(this.image);const t=-this.width/2,e=-this.height/2;this.pattern?Painter.img.fillPattern(this.pattern,t,e,this.width,this.height):this.strokeColor&&Painter.shapes.outlineRect(t,e,this.width,this.height,this.strokeColor,this.lineWidth)}getBounds(){return{x:this.x,y:this.y,width:this.width,height:this.height}}}class Square extends Rectangle{constructor(t,e={}){super(e),this.width=t,this.height=t}}class Cube extends Shape{constructor(t=50,e={}){super(e),this.size=t,this.faceTopColor=e.faceTopColor||"#eee",this.faceBottomColor=e.faceBottomColor||"#ccc",this.faceLeftColor=e.faceLeftColor||"#aaa",this.faceRightColor=e.faceRightColor||"#888",this.faceFrontColor=e.faceFrontColor||"#666",this.faceBackColor=e.faceBackColor||"#444",this.strokeColor=e.strokeColor||null,this.lineWidth=e.lineWidth||1,this.rotationX=e.rotationX||0,this.rotationY=e.rotationY||0,this.rotationZ=e.rotationZ||0,this.visibleFaces=e.visibleFaces||["top","left","right","front","back","bottom"]}setRotation(t,e,i){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this}draw(){super.draw();const t=this.size/2,e=(t,e,i)=>{let s=e,n=i;e=s*Math.cos(this.rotationX)-n*Math.sin(this.rotationX);let a=t;return n=i=s*Math.sin(this.rotationX)+n*Math.cos(this.rotationX),t=a*Math.cos(this.rotationY)+n*Math.sin(this.rotationY),i=-a*Math.sin(this.rotationY)+n*Math.cos(this.rotationY),a=t,s=e,{x:t=a*Math.cos(this.rotationZ)-s*Math.sin(this.rotationZ),y:e=a*Math.sin(this.rotationZ)+s*Math.cos(this.rotationZ),z:i}},i=(t,i,s)=>{const n=e(t,i,s);return{x:(n.x-n.y)*Math.cos(Math.PI/6),y:(n.x+n.y)*Math.sin(Math.PI/6)-n.z}},s={p0:i(-t,-t,-t),p1:i(t,-t,-t),p2:i(t,t,-t),p3:i(-t,t,-t),p4:i(-t,-t,t),p5:i(t,-t,t),p6:i(t,t,t),p7:i(-t,t,t)},n={top:{points:[s.p4,s.p5,s.p6,s.p7],color:this.faceTopColor,normal:[0,0,1]},bottom:{points:[s.p0,s.p1,s.p2,s.p3],color:this.faceBottomColor,normal:[0,0,-1]},left:{points:[s.p0,s.p4,s.p7,s.p3],color:this.faceLeftColor,normal:[-1,0,0]},right:{points:[s.p1,s.p5,s.p6,s.p2],color:this.faceRightColor,normal:[1,0,0]},front:{points:[s.p0,s.p1,s.p5,s.p4],color:this.faceFrontColor,normal:[0,-1,0]},back:{points:[s.p3,s.p2,s.p6,s.p7],color:this.faceBackColor,normal:[0,1,0]}};this.visibleFaces.map(t=>{const e=n[t];if(!e)return null;const i=e.points.reduce((t,e)=>({x:t.x+e.x,y:t.y+e.y}),{x:0,y:0});i.x/=e.points.length,i.y/=e.points.length;return{key:t,face:e,depth:i.x*i.x+i.y*i.y}}).filter(t=>null!==t).sort((t,e)=>e.depth-t.depth).forEach(({key:t,face:e})=>{(null==e?void 0:e.color)&&Painter.shapes.polygon(e.points,e.color,this.strokeColor,this.lineWidth)})}getBounds(){const t=1.5*this.size;return{x:this.x-t/2,y:this.y-t/2,width:t,height:t}}}class Cone extends Shape{constructor(t=50,e=100,i={}){super(i),this.radius=t,this.height=e||i.height||100,this.segments=i.segments||24,this.bottomColor=i.bottomColor||"#eee",this.sideColor=i.sideColor||"#aaa",this.stroke=i.stroke||null,this.lineWidth=i.lineWidth||1,this.rotationX=i.rotationX||0,this.rotationY=i.rotationY||0,this.rotationZ=i.rotationZ||0,this.visibleFaces=i.visibleFaces||["bottom","side"]}setRotation(t,e,i){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this}draw(){super.draw();const t=this.radius,e=this.height/2,i=(t,e,i)=>{let s=e,n=i;e=s*Math.cos(this.rotationX)-n*Math.sin(this.rotationX);let a=t;return n=i=s*Math.sin(this.rotationX)+n*Math.cos(this.rotationX),t=a*Math.cos(this.rotationY)+n*Math.sin(this.rotationY),i=-a*Math.sin(this.rotationY)+n*Math.cos(this.rotationY),a=t,s=e,{x:t=a*Math.cos(this.rotationZ)-s*Math.sin(this.rotationZ),y:e=a*Math.sin(this.rotationZ)+s*Math.cos(this.rotationZ),z:i}},s=(t,e,s)=>{const n=i(t,e,s);return{x:(n.x-n.y)*Math.cos(Math.PI/6),y:(n.x+n.y)*Math.sin(Math.PI/6)-n.z,z:n.z}},n=s(0,0,e),a=[],r=2*Math.PI/this.segments;for(let i=0;i<this.segments;i++){const n=i*r,o=Math.cos(n)*t,h=Math.sin(n)*t;a.push(s(o,h,-e))}const o=[];for(let t=0;t<this.segments;t++){const e=(t+1)%this.segments;o.push({points:[n,a[t],a[e]],z:(n.z+a[t].z+a[e].z)/3})}const h=[];this.visibleFaces.includes("bottom")&&h.push({type:"bottom",points:[...a].reverse(),z:-e}),this.visibleFaces.includes("side")&&h.push(...o.map(t=>({type:"side",points:t.points,z:t.z}))),h.sort((t,e)=>e.z-t.z);for(const t of h){const e="bottom"===t.type?this.bottomColor:this.sideColor;Painter.shapes.polygon(t.points,e,this.stroke,this.lineWidth)}}getBounds(){const t=1.5*Math.max(2*this.radius,this.height);return{x:this.x-t/2,y:this.y-t/2,width:t,height:t}}}class Prism extends Shape{constructor(t=100,e={}){super(e),this.depth=t,this.faceTopColor=e.faceTopColor||"#eee",this.faceBottomColor=e.faceBottomColor||"#ccc",this.faceLeftColor=e.faceLeftColor||"#aaa",this.faceRightColor=e.faceRightColor||"#888",this.faceFrontColor=e.faceFrontColor||"#666",this.faceBackColor=e.faceBackColor||"#444",this.stroke=e.stroke||null,this.lineWidth=e.lineWidth||1,this.rotationX=e.rotationX||0,this.rotationY=e.rotationY||0,this.rotationZ=e.rotationZ||0,this.visibleFaces=e.visibleFaces||["top","left","right","front","back","bottom"]}setRotation(t,e,i){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this}draw(){super.draw();const t=this.width/2,e=this.height/2,i=this.depth/2,s=(t,e,i)=>{let s=e,n=i;e=s*Math.cos(this.rotationX)-n*Math.sin(this.rotationX);let a=t;return n=i=s*Math.sin(this.rotationX)+n*Math.cos(this.rotationX),t=a*Math.cos(this.rotationY)+n*Math.sin(this.rotationY),i=-a*Math.sin(this.rotationY)+n*Math.cos(this.rotationY),a=t,s=e,{x:t=a*Math.cos(this.rotationZ)-s*Math.sin(this.rotationZ),y:e=a*Math.sin(this.rotationZ)+s*Math.cos(this.rotationZ),z:i}},n=(t,e,i)=>{const n=s(t,e,i);return{x:(n.x-n.y)*Math.cos(Math.PI/6),y:(n.x+n.y)*Math.sin(Math.PI/6)-n.z,z:n.z}},a={p0:n(-t,-i,-e),p1:n(t,-i,-e),p2:n(0,-i,e),p3:n(-t,i,-e),p4:n(t,i,-e),p5:n(0,i,e)},r={front:{points:[a.p0,a.p1,a.p2],color:this.faceFrontColor},back:{points:[a.p3,a.p4,a.p5],color:this.faceBackColor},bottom:{points:[a.p0,a.p1,a.p4,a.p3],color:this.faceBottomColor},right:{points:[a.p1,a.p2,a.p5,a.p4],color:this.faceRightColor},left:{points:[a.p0,a.p2,a.p5,a.p3],color:this.faceLeftColor}};this.visibleFaces.filter(t=>r[t]).map(t=>{const e=r[t],i=e.points.reduce((t,e)=>t+e.x,0)/e.points.length,s=e.points.reduce((t,e)=>t+e.y,0)/e.points.length,n=e.points.reduce((t,e)=>t+(e.z||0),0)/e.points.length;return{key:t,face:e,depth:i*i+s*s+n*n}}).sort((t,e)=>e.depth-t.depth).forEach(({key:t,face:e})=>{(null==e?void 0:e.color)&&Painter.shapes.polygon(e.points,e.color,this.stroke,this.lineWidth)})}getBounds(){const t=1.5*Math.max(this.width,this.height,this.depth);return{x:this.x-t/2,y:this.y-t/2,width:t,height:t}}}class Cylinder extends Shape{constructor(t=40,e={}){super(e),this.radius=t,this.height=e.height||80,this.segments=e.segments||24,this.topColor=e.topColor||"#FF00FF",this.bottomColor=e.bottomColor||"#FF0FFF",this.sideColor=e.sideColor||"#00FF00",this.stroke=e.stroke||"#000000",this.lineWidth=e.lineWidth||1,this.rotationX=e.rotationX||0,this.rotationY=e.rotationY||0,this.rotationZ=e.rotationZ||0,this.visibleFaces=e.visibleFaces||["top","bottom","side"]}setRotation(t,e,i){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this}draw(){super.draw();const t=this.radius,e=this.height/2,i=(t,e,i)=>{let s=e,n=i;e=s*Math.cos(this.rotationX)-n*Math.sin(this.rotationX);let a=t;return n=i=s*Math.sin(this.rotationX)+n*Math.cos(this.rotationX),t=a*Math.cos(this.rotationY)+n*Math.sin(this.rotationY),i=-a*Math.sin(this.rotationY)+n*Math.cos(this.rotationY),a=t,s=e,{x:t=a*Math.cos(this.rotationZ)-s*Math.sin(this.rotationZ),y:e=a*Math.sin(this.rotationZ)+s*Math.cos(this.rotationZ),z:i}},s=(t,e,s)=>{const n=i(t,e,s);return{x:(n.x-n.y)*Math.cos(Math.PI/6),y:(n.x+n.y)*Math.sin(Math.PI/6)-n.z,z:n.z}},n=[],a=[],r=2*Math.PI/this.segments;for(let i=0;i<this.segments;i++){const o=i*r,h=Math.cos(o)*t,l=Math.sin(o)*t;n.push(s(h,l,e)),a.push(s(h,l,-e))}const o=[];for(let t=0;t<this.segments;t++){const e=(t+1)%this.segments;o.push({points:[a[t],a[e],n[e],n[t]],z:(n[t].z+n[e].z+a[t].z+a[e].z)/4})}const h=[];this.visibleFaces.includes("top")&&h.push({type:"top",points:n,z:e}),this.visibleFaces.includes("bottom")&&h.push({type:"bottom",points:[...a].reverse(),z:-e}),this.visibleFaces.includes("side")&&h.push(...o.map(t=>({type:"side",points:t.points,z:t.z}))),h.sort((t,e)=>e.z-t.z);for(const t of h){let e;switch(t.type){case"top":e=this.topColor;break;case"bottom":e=this.bottomColor;break;case"side":e=this.sideColor}Painter.shapes.polygon(t.points,e,this.stroke,this.lineWidth)}}getBounds(){const t=1.5*Math.max(2*this.radius,this.height);return{x:this.x-t/2,y:this.y-t/2,width:t,height:t}}}class Diamond extends Shape{constructor(t={}){super(t)}draw(){super.draw();const t=this.width/2,e=this.height/2,i=[{x:0,y:-e},{x:t,y:0},{x:0,y:e},{x:-t,y:0}];Painter.shapes.polygon(i,this.color,this.stroke,this.lineWidth)}}class Line extends Shape{constructor(t=40,e={}){super(e),this.length=t}draw(){super.draw();const t=this.length/2;Painter.lines.line(-t,-t,t,t,this.stroke,this.lineWidth)}}class Triangle extends Shape{constructor(t=50,e={}){super(e),this.size=t}draw(){super.draw();const t=this.size/2,e=[{x:0,y:-t},{x:t,y:t},{x:-t,y:t}];Painter.shapes.polygon(e,this.color,this.stroke,this.lineWidth)}}class Star extends Shape{constructor(t=40,e=5,i=.5,s={}){super(s),this.radius=t,this.spikes=e,this.inset=i}draw(){super.draw();const t=Math.PI/this.spikes,e=-Math.PI/2;Painter.lines.beginPath();for(let i=0;i<2*this.spikes;i++){const s=i%2==0?this.radius:this.radius*this.inset,n=i*t+e,a=Math.cos(n)*s,r=Math.sin(n)*s;0===i?Painter.lines.moveTo(a,r):Painter.lines.lineTo(a,r)}Painter.lines.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}}class Sphere extends Shape{constructor(t=50,e={}){super(e),this.radius=t,this.hSegments=e.hSegments||16,this.vSegments=e.vSegments||12,this.color=e.color||"#6495ED",this.highlightColor=e.highlightColor||"#FFFFFF",this.wireframe=e.wireframe||!1,this.stroke=e.stroke||"#333333",this.lineWidth=e.lineWidth||1,this.rotationX=e.rotationX||0,this.rotationY=e.rotationY||0,this.rotationZ=e.rotationZ||0}setRotation(t,e,i){return this.rotationX=t,this.rotationY=e,this.rotationZ=i,this}rotate(t,e,i){return this.rotationX+=t,this.rotationY+=e,this.rotationZ+=i,this}calculateSurfaceColor(t,e,i){let s=t*(1/Math.sqrt(3))+e*(1/Math.sqrt(3))+i*(1/Math.sqrt(3));if(s=Math.max(.3,s),this.highlightColor){const t=this.hexToRgb(this.color),e=this.hexToRgb(this.highlightColor);return`rgb(${Math.round(t.r*(1-s)+e.r*s)}, ${Math.round(t.g*(1-s)+e.g*s)}, ${Math.round(t.b*(1-s)+e.b*s)})`}const n=this.hexToRgb(this.color);return`rgb(${Math.min(255,Math.round(n.r*s))}, ${Math.min(255,Math.round(n.g*s))}, ${Math.min(255,Math.round(n.b*s))})`}hexToRgb(t){const e={r:100,g:100,b:255};if(!t||"string"!=typeof t)return e;const i=t.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,(t,e,i,s)=>e+e+i+i+s+s),s=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(i);return s?{r:parseInt(s[1],16),g:parseInt(s[2],16),b:parseInt(s[3],16)}:e}draw(){super.draw();const t=this.radius,e=(t,e,i)=>{let s=e,n=i;e=s*Math.cos(this.rotationX)-n*Math.sin(this.rotationX);let a=t;return n=i=s*Math.sin(this.rotationX)+n*Math.cos(this.rotationX),t=a*Math.cos(this.rotationY)+n*Math.sin(this.rotationY),i=-a*Math.sin(this.rotationY)+n*Math.cos(this.rotationY),a=t,s=e,{x:t=a*Math.cos(this.rotationZ)-s*Math.sin(this.rotationZ),y:e=a*Math.sin(this.rotationZ)+s*Math.cos(this.rotationZ),z:i}},i=(i,s,n)=>{const a=e(i,s,n);return{x:(a.x-a.y)*Math.cos(Math.PI/6),y:(a.x+a.y)*Math.sin(Math.PI/6)-a.z,z:a.z,nx:a.x/t,ny:a.y/t,nz:a.z/t}},s=[];for(let e=0;e<=this.vSegments;e++){const n=[],a=e/this.vSegments,r=Math.PI*a-Math.PI/2;for(let e=0;e<=this.hSegments;e++){const s=e/this.hSegments,a=2*Math.PI*s,o=t*Math.cos(r)*Math.cos(a),h=t*Math.cos(r)*Math.sin(a),l=t*Math.sin(r);n.push(i(o,h,l))}s.push(n)}const n=[];for(let t=0;t<this.vSegments;t++)for(let e=0;e<this.hSegments;e++){const i=s[t][e],a=s[t][e+1],r=s[t+1][e],o=s[t+1][e+1],h=(i.z+a.z+r.z+o.z)/4,l=(i.nx+a.nx+r.nx+o.nx)/4,c=(i.ny+a.ny+r.ny+o.ny)/4,u=(i.nz+a.nz+r.nz+o.nz)/4;n.push({points:[i,a,o,r],z:h,color:this.calculateSurfaceColor(l,c,u)})}if(n.sort((t,e)=>e.z-t.z),this.wireframe)for(const t of n){const e=t.points;for(let t=0;t<e.length;t++){const i=(t+1)%e.length;Painter.lines.line(e[t].x,e[t].y,e[i].x,e[i].y,this.stroke,this.lineWidth)}}for(const t of n)Painter.shapes.polygon(t.points,t.color,this.stroke,this.lineWidth)}getBounds(){const t=2*this.radius*1.5;return{x:this.x-t/2,y:this.y-t/2,width:t,height:t}}}class WebGLRenderer{constructor(t,e){if(this.width=t,this.height=e,this.canvas=document.createElement("canvas"),this.canvas.width=t,this.canvas.height=e,this.gl=this.canvas.getContext("webgl",{alpha:!0,premultipliedAlpha:!0,antialias:!0,preserveDrawingBuffer:!0}),!this.gl)return console.warn("WebGL not available, falling back to Canvas 2D"),void(this.available=!1);this.available=!0;const i=this.gl;i.enable(i.BLEND),i.blendFunc(i.ONE,i.ONE_MINUS_SRC_ALPHA),i.viewport(0,0,t,e),this.programs=new Map,this.currentProgram=null,this.uniformLocations=new Map,this._needsAttributeRebind=!1,this._createQuad()}isAvailable(){return this.available}resize(t,e){this.width=t,this.height=e,this.canvas.width=t,this.canvas.height=e,this.gl&&(this.gl.viewport(0,0,t,e),this._needsAttributeRebind=!0)}_createQuad(){const t=this.gl,e=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),i=new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]);this.positionBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,e,t.STATIC_DRAW),this.uvBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.uvBuffer),t.bufferData(t.ARRAY_BUFFER,i,t.STATIC_DRAW)}_compileShader(t,e){const i=this.gl,s=i.createShader(t);return i.shaderSource(s,e),i.compileShader(s),i.getShaderParameter(s,i.COMPILE_STATUS)?s:(console.error("Shader compile error:",i.getShaderInfoLog(s)),console.error("Source:",e),i.deleteShader(s),null)}useProgram(t,e,i){if(!this.available)return null;const s=this.gl;if(this.programs.has(t)){const e=this.programs.get(t);return s.useProgram(e),this.currentProgram=t,this._needsAttributeRebind&&(this._bindAttributes(e),this._needsAttributeRebind=!1),e}const n=this._compileShader(s.VERTEX_SHADER,e),a=this._compileShader(s.FRAGMENT_SHADER,i);if(!n||!a)return null;const r=s.createProgram();return s.attachShader(r,n),s.attachShader(r,a),s.linkProgram(r),s.getProgramParameter(r,s.LINK_STATUS)?(this.programs.set(t,r),this.uniformLocations.set(t,new Map),s.useProgram(r),this.currentProgram=t,this._bindAttributes(r),r):(console.error("Program link error:",s.getProgramInfoLog(r)),s.deleteProgram(r),null)}_bindAttributes(t){const e=this.gl,i=e.getAttribLocation(t,"aPosition"),s=e.getAttribLocation(t,"aUv");-1!==i&&(e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(i),e.vertexAttribPointer(i,2,e.FLOAT,!1,0,0)),-1!==s&&(e.bindBuffer(e.ARRAY_BUFFER,this.uvBuffer),e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,0,0))}_getUniformLocation(t){const e=this.gl,i=this.programs.get(this.currentProgram),s=this.uniformLocations.get(this.currentProgram);return s.has(t)||s.set(t,e.getUniformLocation(i,t)),s.get(t)}setUniforms(t){if(!this.available||!this.currentProgram)return;const e=this.gl;for(const[i,s]of Object.entries(t)){const t=this._getUniformLocation(i);if(null!==t)if("number"==typeof s)e.uniform1f(t,s);else if(Array.isArray(s))switch(s.length){case 2:e.uniform2fv(t,s);break;case 3:e.uniform3fv(t,s);break;case 4:e.uniform4fv(t,s)}else s instanceof Float32Array&&(9===s.length?e.uniformMatrix3fv(t,!1,s):16===s.length&&e.uniformMatrix4fv(t,!1,s))}}setColorUniform(t,e){if(!this.available||!this.currentProgram)return;const i=e.replace("#",""),s=parseInt(i.substring(0,2),16)/255,n=parseInt(i.substring(2,4),16)/255,a=parseInt(i.substring(4,6),16)/255,r=this._getUniformLocation(t);null!==r&&this.gl.uniform3f(r,s,n,a)}clear(t=0,e=0,i=0,s=0){if(!this.available)return;const n=this.gl;n.clearColor(t,e,i,s),n.clear(n.COLOR_BUFFER_BIT)}render(){if(!this.available||!this.currentProgram)return;const t=this.gl;t.drawArrays(t.TRIANGLES,0,6)}compositeOnto(t,e,i,s,n){this.available&&t.drawImage(this.canvas,e,i,s??this.canvas.width,n??this.canvas.height)}getCanvas(){return this.canvas}destroy(){if(!this.available)return;const t=this.gl;for(const e of this.programs.values())t.deleteProgram(e);t.deleteBuffer(this.positionBuffer),t.deleteBuffer(this.uvBuffer),this.programs.clear(),this.uniformLocations.clear()}}const SPHERE_VERTEX="\nprecision highp float;\n\nattribute vec2 aPosition;\nattribute vec2 aUv;\n\nvarying vec2 vUv;\n\nvoid main() {\n vUv = aUv;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n",SPHERE_COMMON="\nprecision highp float;\n\nvarying vec2 vUv;\n\n// Uniforms common to all sphere shaders\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec3 uCameraRotation; // rotationX, rotationY, rotationZ\n\n// =============================================================================\n// NOISE FUNCTIONS\n// =============================================================================\n\nfloat hash(float n) {\n return fract(sin(n) * 43758.5453123);\n}\n\nfloat hash2(vec2 p) {\n return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\nfloat hash3(vec3 p) {\n return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453123);\n}\n\n// 3D Value noise\nfloat noise3D(vec3 x) {\n vec3 i = floor(x);\n vec3 f = fract(x);\n f = f * f * (3.0 - 2.0 * f);\n\n float n = dot(i, vec3(1.0, 57.0, 113.0));\n\n return mix(\n mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),\n mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),\n mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),\n mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z\n );\n}\n\n// FBM (Fractional Brownian Motion)\nfloat fbm(vec3 p, int octaves) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n\n for (int i = 0; i < 8; i++) {\n if (i >= octaves) break;\n value += amplitude * noise3D(p * frequency);\n frequency *= 2.0;\n amplitude *= 0.5;\n }\n\n return value;\n}\n\n// =============================================================================\n// RAY-SPHERE INTERSECTION\n// =============================================================================\n\n/**\n * Ray-sphere intersection\n * @param rayOrigin - Ray origin (camera position)\n * @param rayDir - Normalized ray direction\n * @param sphereCenter - Sphere center\n * @param sphereRadius - Sphere radius\n * @return t value for intersection, -1.0 if no hit\n */\nfloat raySphereIntersect(vec3 rayOrigin, vec3 rayDir, vec3 sphereCenter, float sphereRadius) {\n vec3 oc = rayOrigin - sphereCenter;\n float a = dot(rayDir, rayDir);\n float b = 2.0 * dot(oc, rayDir);\n float c = dot(oc, oc) - sphereRadius * sphereRadius;\n float discriminant = b * b - 4.0 * a * c;\n\n if (discriminant < 0.0) {\n return -1.0;\n }\n\n return (-b - sqrt(discriminant)) / (2.0 * a);\n}\n\n// =============================================================================\n// CAMERA AND ROTATION\n// =============================================================================\n\n/**\n * Create rotation matrix from Euler angles\n */\nmat3 rotationMatrix(vec3 rotation) {\n float cx = cos(rotation.x);\n float sx = sin(rotation.x);\n float cy = cos(rotation.y);\n float sy = sin(rotation.y);\n float cz = cos(rotation.z);\n float sz = sin(rotation.z);\n\n mat3 rx = mat3(\n 1.0, 0.0, 0.0,\n 0.0, cx, -sx,\n 0.0, sx, cx\n );\n\n mat3 ry = mat3(\n cy, 0.0, sy,\n 0.0, 1.0, 0.0,\n -sy, 0.0, cy\n );\n\n mat3 rz = mat3(\n cz, -sz, 0.0,\n sz, cz, 0.0,\n 0.0, 0.0, 1.0\n );\n\n return rz * ry * rx;\n}\n\n/**\n * Calculate ray direction from UV coordinates\n * Uses a simple pinhole camera model\n */\nvec3 getRayDirection(vec2 uv) {\n // Convert UV to normalized device coordinates (-1 to 1)\n vec2 ndc = uv * 2.0 - 1.0;\n // Field of view ~53 degrees (atan(0.5) * 2)\n return normalize(vec3(ndc * 0.5, 1.0));\n}\n\n// =============================================================================\n// LIGHTING\n// =============================================================================\n\n/**\n * Simple diffuse + ambient lighting\n */\nfloat lighting(vec3 normal, vec3 lightDir, float ambient) {\n float diffuse = max(0.0, dot(normal, lightDir));\n return ambient + (1.0 - ambient) * diffuse;\n}\n\n/**\n * Fresnel effect for rim lighting\n */\nfloat fresnel(vec3 normal, vec3 viewDir, float power) {\n return pow(1.0 - abs(dot(normal, viewDir)), power);\n}\n",STAR_FRAGMENT=`\n${SPHERE_COMMON}\n\nuniform vec3 uStarColor;\nuniform float uTemperature; // Kelvin, affects color\nuniform float uActivityLevel; // 0-1, affects turbulence\nuniform float uRotationSpeed; // Self-rotation speed (radians/second)\n\n// Tidal disruption uniforms\nuniform float uTidalStretch; // 0 = sphere, 1+ = elongated toward BH\nuniform float uStretchDirX; // Direction to black hole (X component)\nuniform float uStretchDirZ; // Direction to black hole (Z component)\nuniform float uStressLevel; // 0-1, surface chaos from tidal forces\nuniform float uBaseRadius; // Dynamic base radius for proper sizing\nuniform float uTidalFlare; // 0-1, sudden brightness burst at disruption start\nuniform float uTidalWobble; // 0-1, violent geometry wobble during trauma\n\n// =============================================================================\n// TIDAL DISTORTION - True Spaghettification via Ellipsoid Deformation\n// Uses ray-ellipsoid intersection for physically correct stretching\n// =============================================================================\n\n/**\n * Ray-Ellipsoid intersection\n * Ellipsoid defined by semi-axes (a, b, c) where:\n * - a = stretch along BH direction (in XZ plane)\n * - b = Y axis (slight compression)\n * - c = perpendicular to BH direction in XZ plane (compression)\n *\n * Technique: Transform ray into "unit sphere space" via inverse scaling\n */\nfloat rayEllipsoidIntersect(vec3 rayOrigin, vec3 rayDir, vec3 center, vec3 semiAxes) {\n // Scale ray into unit sphere space\n vec3 scaledOrigin = (rayOrigin - center) / semiAxes;\n vec3 scaledDir = rayDir / semiAxes;\n\n // Standard ray-sphere intersection in scaled space\n float a = dot(scaledDir, scaledDir);\n float b = 2.0 * dot(scaledOrigin, scaledDir);\n float c = dot(scaledOrigin, scaledOrigin) - 1.0;\n float discriminant = b * b - 4.0 * a * c;\n\n if (discriminant < 0.0) {\n return -1.0;\n }\n\n return (-b - sqrt(discriminant)) / (2.0 * a);\n}\n\n/**\n * Calculate ellipsoid normal at hit point\n * Normal = gradient of ellipsoid equation = 2*(x/a², y/b², z/c²)\n */\nvec3 ellipsoidNormal(vec3 hitPoint, vec3 center, vec3 semiAxes) {\n vec3 localPos = hitPoint - center;\n // Gradient of (x/a)² + (y/b)² + (z/c)² = 1\n vec3 grad = localPos / (semiAxes * semiAxes);\n return normalize(grad);\n}\n\n/**\n * Build tidal stretch axes from BH direction\n * Returns semi-axes (stretchAxis, Y, perpAxis) for ellipsoid\n * Includes violent wobble effect during trauma\n */\nvec3 tidalSemiAxes(float stretch, vec2 stretchDir, float baseRadius, float wobble, float time) {\n // Stretch factor along BH direction (elongation toward/away from BH)\n float stretchFactor = 1.0 + stretch * 0.8; // Up to 1.8x longer\n\n // Compression factor perpendicular (volume roughly conserved)\n float compressFactor = 1.0 / sqrt(stretchFactor); // Compress to conserve volume\n\n // Y axis gets slight compression too\n float yFactor = 1.0 - stretch * 0.15;\n\n // === TRAUMA WOBBLE ===\n // Violent, chaotic geometry distortion during tidal shock\n if (wobble > 0.01) {\n // Multiple frequency wobbles for organic chaos\n float wobble1 = sin(time * 12.0) * cos(time * 7.3);\n float wobble2 = sin(time * 19.0 + 1.5) * cos(time * 11.0);\n float wobble3 = sin(time * 8.0 + 3.0);\n \n // Asymmetric wobble - more violent on stretch axis\n float stretchWobble = wobble * (0.3 + wobble1 * 0.2 + wobble2 * 0.15);\n float yWobble = wobble * (wobble2 * 0.25 + wobble3 * 0.15);\n float perpWobble = wobble * (wobble3 * 0.2 + wobble1 * 0.1);\n \n stretchFactor *= (1.0 + stretchWobble);\n yFactor *= (1.0 + yWobble);\n compressFactor *= (1.0 + perpWobble);\n }\n\n return vec3(\n baseRadius * stretchFactor, // Stretch along BH radial\n baseRadius * yFactor, // Slight Y compression\n baseRadius * compressFactor // Compress perpendicular\n );\n}\n\n// =============================================================================\n// PLASMA NOISE with flowing distortion\n// =============================================================================\n\nfloat plasmaNoise(vec3 p, float time) {\n float value = 0.0;\n float amplitude = 1.0;\n float frequency = 1.0;\n float totalAmp = 0.0;\n\n for (int i = 0; i < 5; i++) {\n vec3 offset = vec3(\n sin(time * 0.1 + float(i)) * 0.5,\n cos(time * 0.15 + float(i) * 0.7) * 0.5,\n time * 0.05\n );\n value += amplitude * noise3D((p + offset) * frequency);\n totalAmp += amplitude;\n amplitude *= 0.5;\n frequency *= 2.0;\n }\n\n return value / totalAmp;\n}\n\n// =============================================================================\n// HOT BUBBLES - bright spots that appear and pop\n// =============================================================================\n\nfloat hotBubbles(vec3 p, float time) {\n // Large slow bubbles\n vec3 p1 = p * 5.0 + vec3(0.0, time * 0.06, 0.0);\n float b1 = noise3D(p1);\n b1 = smoothstep(0.3, 0.6, b1);\n\n // Medium bubbles, faster\n vec3 p2 = p * 9.0 + vec3(time * 0.04, time * 0.08, 0.0);\n float b2 = noise3D(p2);\n b2 = smoothstep(0.35, 0.65, b2);\n\n // Small rapid bubbles\n vec3 p3 = p * 16.0 + vec3(time * 0.1, 0.0, time * 0.12);\n float b3 = noise3D(p3);\n b3 = smoothstep(0.4, 0.7, b3);\n\n float bubbles = b1 * 0.5 + b2 * 0.35 + b3 * 0.15;\n float pulse = sin(time * 2.0 + p.x * 10.0) * 0.3 + 0.7;\n\n return bubbles * pulse;\n}\n\n// =============================================================================\n// BOILING TURBULENCE - fast chaotic movement\n// =============================================================================\n\nfloat boilingTurbulence(vec3 p, float time) {\n float turb = 0.0;\n float amp = 1.0;\n float freq = 4.0;\n\n for (int i = 0; i < 4; i++) {\n vec3 offset = vec3(\n sin(time * 0.3 + float(i) * 1.7) * 0.5,\n cos(time * 0.25 + float(i) * 2.3) * 0.5,\n time * 0.15 * (1.0 + float(i) * 0.3)\n );\n turb += amp * abs(noise3D(p * freq + offset));\n amp *= 0.5;\n freq *= 2.1;\n }\n return turb;\n}\n\n// =============================================================================\n// CORONA FLAMES - structures around the edge\n// =============================================================================\n\nfloat coronaFlames(float angle, float rimFactor, float time, float activity) {\n // Multiple flame frequencies\n float flames = 0.0;\n\n // Large slow flames\n float f1 = sin(angle * 5.0 + time * 0.5) * 0.5 + 0.5;\n f1 *= noise3D(vec3(angle * 2.0, time * 0.3, 0.0));\n\n // Medium flames\n float f2 = sin(angle * 12.0 + time * 0.8) * 0.5 + 0.5;\n f2 *= noise3D(vec3(angle * 4.0, time * 0.5, 5.0));\n\n // Small rapid flames\n float f3 = sin(angle * 25.0 + time * 1.5) * 0.5 + 0.5;\n f3 *= noise3D(vec3(angle * 8.0, time * 0.8, 10.0));\n\n flames = f1 * 0.5 + f2 * 0.3 + f3 * 0.2;\n\n // Flames only visible at rim\n flames *= pow(rimFactor, 1.5);\n flames *= 0.5 + activity * 0.5;\n\n return flames;\n}\n\n// =============================================================================\n// SELF ROTATION - rotate normal around Y axis\n// =============================================================================\n\nvec3 rotateY(vec3 v, float angle) {\n float c = cos(angle);\n float s = sin(angle);\n return vec3(v.x * c + v.z * s, v.y, -v.x * s + v.z * c);\n}\n\nvoid main() {\n // === CIRCULAR MASK - prevents square canvas artifacts ===\n vec2 center = vUv - 0.5;\n float distFromCenter = length(center) * 2.0;\n\n // Wider cutoff for stretched ellipsoid\n if (distFromCenter > 1.6) {\n gl_FragColor = vec4(0.0);\n return;\n }\n\n float circularMask = 1.0 - smoothstep(1.3, 1.6, distFromCenter);\n\n // Setup ray - camera looking at sphere from fixed position\n vec3 rayOrigin = vec3(0.0, 0.0, -2.5);\n vec3 rayDir = getRayDirection(vUv);\n\n float time = uTime;\n float selfRotation = time * uRotationSpeed;\n\n // === TIDAL ELLIPSOID SETUP ===\n // Direction toward black hole in XZ plane\n vec2 stretchDir2D = normalize(vec2(uStretchDirX, uStretchDirZ) + 0.0001);\n float stretch = uTidalStretch;\n\n // Build rotation matrix to align ellipsoid X-axis with stretch direction\n // This rotates the ellipsoid so its long axis points toward the BH\n float stretchAngle = atan(stretchDir2D.y, stretchDir2D.x);\n float cs = cos(stretchAngle);\n float sn = sin(stretchAngle);\n\n // Rotation matrix around Y axis (to align stretch in XZ plane)\n mat3 stretchRot = mat3(\n cs, 0.0, -sn,\n 0.0, 1.0, 0.0,\n sn, 0.0, cs\n );\n mat3 stretchRotInv = mat3(\n cs, 0.0, sn,\n 0.0, 1.0, 0.0,\n -sn, 0.0, cs\n );\n\n // Transform ray into ellipsoid-aligned space\n vec3 rotatedRayDir = stretchRotInv * rayDir;\n vec3 rotatedRayOrigin = stretchRotInv * rayOrigin;\n\n // Calculate ellipsoid semi-axes based on stretch\n // Use dynamic base radius passed from JS (scales with render texture size)\n // Falls back to 0.4 if uniform not set\n float baseRadius = uBaseRadius > 0.0 ? uBaseRadius : 0.4;\n vec3 semiAxes = tidalSemiAxes(stretch, stretchDir2D, baseRadius, uTidalWobble, time);\n\n // Ray-ellipsoid intersection for SURFACE\n float t = rayEllipsoidIntersect(rotatedRayOrigin, rotatedRayDir, vec3(0.0), semiAxes);\n\n // === NO CORONA - just render solid ellipsoid ===\n // If ray doesn't hit the surface, render transparent\n if (t < 0.0) {\n gl_FragColor = vec4(0.0);\n return;\n }\n\n // === SURFACE RENDERING ===\n // Calculate hit point in rotated (ellipsoid-aligned) space\n vec3 rotatedHitPoint = rotatedRayOrigin + rotatedRayDir * t;\n\n // Calculate ellipsoid normal (gradient of implicit surface)\n vec3 rotatedNormalRaw = ellipsoidNormal(rotatedHitPoint, vec3(0.0), semiAxes);\n\n // Transform hit point and normal back to world space\n vec3 hitPoint = stretchRot * rotatedHitPoint;\n vec3 normal = normalize(stretchRot * rotatedNormalRaw);\n\n // Apply inverse camera rotation to the normal (camera orbit)\n mat3 camRotMat = rotationMatrix(-uCameraRotation);\n vec3 rotatedNormal = camRotMat * normal;\n\n // Apply self-rotation to surface features\n rotatedNormal = rotateY(rotatedNormal, selfRotation);\n\n // === SPHERICAL DISTORTION for boiling effect ===\n vec2 sp = normal.xy;\n float r = dot(sp, sp);\n\n float brightness = 0.15 + (uTemperature / 10000.0) * 0.1;\n float distortStrength = 2.0 - brightness;\n\n vec2 warpedUV;\n if (r < 0.0001) {\n // At pole - use alternative coords\n float poleAngle = atan(rotatedNormal.y, rotatedNormal.x) + time * 0.15;\n float poleElev = acos(clamp(rotatedNormal.z, -1.0, 1.0));\n warpedUV = vec2(cos(poleAngle), sin(poleAngle)) * (poleElev / 3.14159) * distortStrength;\n } else {\n sp *= distortStrength;\n r = dot(sp, sp);\n float f = (1.0 - sqrt(abs(1.0 - r))) / (r + 0.001) + brightness * 0.5;\n warpedUV = sp * f + vec2(time * 0.05, 0.0);\n }\n\n // === PLASMA TEXTURE ===\n vec3 plasmaCoord = vec3(warpedUV * 3.0, time * 0.12);\n float plasma1 = plasmaNoise(plasmaCoord, time);\n float plasma2 = plasmaNoise(plasmaCoord * 1.3 + vec3(50.0), time * 1.2);\n float plasma = plasma1 * 0.6 + plasma2 * 0.4;\n plasma = plasma * 0.5 + 0.5;\n\n // === VIEW GEOMETRY ===\n float viewAngle = dot(normal, -rayDir);\n float edgeDist = 1.0 - viewAngle;\n float limbDarkening = pow(max(0.0, viewAngle), 0.4);\n\n // === TIDAL FACE INTENSITY ===\n // The side facing the black hole experiences more violent tidal forces\n // Calculate how much this surface point faces the BH direction\n vec3 bhDir3D = normalize(vec3(uStretchDirX, 0.0, uStretchDirZ));\n float facingBH = dot(normal, bhDir3D); // -1 to 1, positive = facing BH\n float tidalFace = smoothstep(-0.2, 0.8, facingBH); // Gradual transition\n tidalFace = tidalFace * tidalFace; // More concentrated on BH side\n \n // Tidal face boost - up to 3x more violent on the BH-facing side\n float tidalFaceBoost = 1.0 + tidalFace * uStressLevel * 2.0;\n\n // === MULTI-LAYER EFFECTS (stress-enhanced) ===\n // Stress amplifies all turbulent effects - star is being torn apart!\n // Much more violent - up to 5x chaos at max stress\n float stressBoost = 1.0 + uStressLevel * 4.0;\n \n // Combined boost: general stress + extra violence on BH-facing side\n float combinedBoost = stressBoost * tidalFaceBoost;\n\n float turbIntensity = boilingTurbulence(rotatedNormal, time * combinedBoost) * 0.6;\n turbIntensity *= combinedBoost;\n\n float bubbles = hotBubbles(rotatedNormal, time * combinedBoost);\n bubbles *= combinedBoost * 1.5; // More dramatic bubbles on tidal face\n\n // Granulation becomes violent under stress - larger and faster\n float gran = noise3D(rotatedNormal * 15.0 + time * 0.5 * combinedBoost);\n gran *= combinedBoost * 1.2;\n\n // === TIDAL FRACTURING ===\n // Stress causes visible cracks/tears - concentrated on BH-facing side\n float fractures = 0.0;\n if (uStressLevel > 0.15) { // Start fractures earlier (was 0.3)\n // Fractures are more intense on the tidal face\n float fractureBoost = 1.0 + tidalFace * 2.0; // Up to 3x on BH side\n float fractureNoise = noise3D(rotatedNormal * 6.0 + time * 0.8 * fractureBoost);\n float fractureThreshold = 1.0 - (uStressLevel - 0.15) * 1.2 * fractureBoost;\n fractures = smoothstep(fractureThreshold, fractureThreshold + 0.08, fractureNoise);\n fractures *= uStressLevel * 1.2 * fractureBoost; // More intense on BH side\n }\n\n // === PULSATION (amplified by stress) ===\n float pulse1 = cos(time * 0.5) * 0.5;\n float pulse2 = sin(time * 0.25) * 0.5;\n float pulseAmp = uActivityLevel * (1.0 + uStressLevel);\n float pulse = (pulse1 + pulse2) * 0.3 * pulseAmp;\n\n // === TIDAL HOTSPOT ===\n // Bright glowing region on the BH-facing side - like matter being pulled off\n float tidalHotspot = pow(tidalFace, 3.0) * uStressLevel;\n // Add some flickering/chaos to the hotspot\n tidalHotspot *= 0.7 + 0.3 * noise3D(rotatedNormal * 8.0 + time * 2.0);\n\n // === COMBINED INTENSITY ===\n float totalIntensity = plasma * 0.35 + turbIntensity * 0.25 + gran * 0.2;\n totalIntensity += bubbles * 0.4;\n totalIntensity += fractures * 0.5; // Fractures glow hot\n totalIntensity += tidalHotspot * 0.8; // Bright tidal hotspot\n totalIntensity *= 1.0 + pulse;\n\n // === 4-TIER COLOR SYSTEM ===\n vec3 baseColor = uStarColor;\n float maxComp = max(baseColor.r, max(baseColor.g, baseColor.b));\n if (maxComp > 0.01) baseColor = baseColor / maxComp * 0.85;\n\n // Temperature-based color blending\n float tempBlend = smoothstep(5000.0, 7500.0, uTemperature);\n\n vec3 hotColor = baseColor * vec3(1.6, 1.35, 1.2);\n vec3 coolColor = mix(baseColor * vec3(0.5, 0.3, 0.2), baseColor * vec3(0.7, 0.8, 0.95), tempBlend);\n vec3 warmColor = mix(baseColor * vec3(1.2, 1.0, 0.85), baseColor * vec3(1.0, 1.05, 1.2), tempBlend);\n vec3 blazingColor = mix(baseColor * vec3(2.0, 1.6, 1.3), baseColor * vec3(1.4, 1.5, 1.8), tempBlend);\n\n // Map intensity to color\n vec3 surfaceColor;\n if (totalIntensity < 0.35) {\n surfaceColor = mix(coolColor, warmColor, totalIntensity / 0.35);\n } else if (totalIntensity < 0.65) {\n surfaceColor = mix(warmColor, hotColor, (totalIntensity - 0.35) / 0.3);\n } else if (totalIntensity < 1.0) {\n surfaceColor = mix(hotColor, blazingColor, (totalIntensity - 0.65) / 0.35);\n } else {\n surfaceColor = blazingColor * (1.0 + (totalIntensity - 1.0) * 0.8);\n }\n\n // Bubble highlights\n float bubbleHighlight = pow(bubbles, 1.5) * turbIntensity;\n surfaceColor += blazingColor * bubbleHighlight * 0.6;\n\n // === LIMB DARKENING ===\n surfaceColor *= 0.75 + limbDarkening * 0.25;\n\n // === ORGANIC RIM GLOW ===\n float rimAngle = atan(normal.y, normal.x) + selfRotation;\n float rimNoise = noise3D(vec3(rimAngle * 3.0, edgeDist * 2.0, time * 0.2));\n rimNoise = rimNoise * 0.5 + 0.5;\n\n float rimIntensity = pow(edgeDist, 2.0) * (0.4 + rimNoise * 0.6);\n vec3 rimColor = baseColor * vec3(1.3, 0.95, 0.6);\n surfaceColor += rimColor * rimIntensity * 0.6 * uActivityLevel;\n\n // === EDGE GLOW (corona bleeding into surface) ===\n float edgeGlow = pow(edgeDist, 0.5) * 0.3 * uActivityLevel;\n surfaceColor += warmColor * edgeGlow;\n\n // === CENTER BOOST ===\n float centerBoost = pow(viewAngle, 1.5) * 0.2;\n surfaceColor += baseColor * centerBoost;\n\n // === SHIMMER ===\n float shimmer = sin(turbIntensity * 10.0 + time * 3.0) * 0.05 + 1.0;\n surfaceColor *= shimmer;\n\n // === TIDAL FLARE ===\n // Sudden brightness burst when disruption begins\n // Concentrated on the BH-facing side with violent flickering\n if (uTidalFlare > 0.01) {\n // Flare is brightest on the BH-facing side\n float flareFace = 0.3 + tidalFace * 0.7;\n \n // Violent flickering during the flare\n float flareFlicker = 0.7 + 0.3 * noise3D(rotatedNormal * 10.0 + time * 8.0);\n \n // White-hot flare color\n vec3 flareColor = vec3(1.0, 0.95, 0.8);\n \n // Additive flare - makes entire star brighter\n float flareIntensity = uTidalFlare * flareFace * flareFlicker * 2.0;\n surfaceColor += flareColor * flareIntensity;\n \n // Extra bloom at the BH-facing tip\n float tipFlare = pow(tidalFace, 4.0) * uTidalFlare * 1.5;\n surfaceColor += vec3(1.0, 0.9, 0.7) * tipFlare;\n }\n\n surfaceColor = clamp(surfaceColor, 0.0, 3.5); // Allow brighter for flare\n\n gl_FragColor = vec4(surfaceColor, 1.0);\n}\n`,BLACK_HOLE_FRAGMENT=`\n${SPHERE_COMMON}\n\nuniform float uAwakeningLevel; // 0 = dormant, 1 = fully active\nuniform float uFeedingPulse; // Temporary glow from feeding\nuniform float uRotation; // Black hole spin angle (Kerr rotation)\n\nvoid main() {\n vec2 uv = vUv;\n vec2 center = uv - 0.5;\n float dist = length(center) * 2.0; // 0 at center, 1 at edge\n float angle = atan(center.y, center.x);\n\n float time = uTime;\n float awakeFactor = uAwakeningLevel;\n float pulseFactor = uFeedingPulse;\n\n // Spin angle for rotating effects (frame dragging)\n // Using uTime since custom uniforms don't update properly\n float spinAngle = angle + uTime * 4.0; // Faster spin\n\n // === RADII (normalized to quad size) ===\n float eventHorizon = 0.42; // Slightly larger core\n float photonSphere = 0.54; // Tighten ring closer to horizon\n float shadowEdge = 0.5; // Shadow boundary\n\n // === CIRCULAR MASK ===\n if (dist > 1.5) {\n gl_FragColor = vec4(0.0);\n return;\n }\n\n // === NO INTERNAL STARFIELD ===\n // The real starfield is rendered separately in the scene\n // This shader just renders the dark void + subtle edge effects\n // True gravitational lensing would require render-to-texture of background\n\n // === EVENT HORIZON - Gradient from pure black to very dark edge ===\n // Edge color ~#110b06 = RGB(17,11,6) = vec3(0.067, 0.043, 0.024)\n if (dist < shadowEdge) {\n // Use shadowEdge (0.52) as outer boundary for smooth transition to ring\n float edgeT = dist / shadowEdge; // 0 at center, 1 at shadow edge\n\n // Very steep curve - stays pure black until very close to edge\n float glowFactor = pow(edgeT, 8.0);\n\n // Very dark brownish-black - NO yellow, matches #110b06\n vec3 edgeColor = vec3(0.067, 0.043, 0.024) * glowFactor;\n\n gl_FragColor = vec4(edgeColor, 1.0);\n return;\n }\n\n // === PHOTON SPHERE - Subtle ring with gentler spin asymmetry ===\n float photonRingWidth = 0.035;\n float photonDist = abs(dist - photonSphere);\n float photonRing = exp(-photonDist * photonDist / (photonRingWidth * photonRingWidth));\n\n // Softer Doppler asymmetry to avoid pointy highlights\n float doppler = 0.78 + 0.22 * cos(spinAngle); // narrower asymmetry\n photonRing *= 0.18 + doppler * 0.38; // 18%..56% brightness\n\n // Soft tip highlight to indicate spin without a spike\n float tipAlign = max(0.0, cos(spinAngle));\n float tipRadial = smoothstep(photonRingWidth * 1.2, 0.0, photonDist);\n float hotSpotGlow = tipAlign * tipAlign * tipRadial * 0.25;\n\n // Scale with awakening - more visible when feeding\n photonRing *= 0.15 + awakeFactor * 0.35;\n\n // === FEEDING PULSE - Subtle ripple when consuming ===\n float pulseRipple = 0.0;\n if (pulseFactor > 0.01) {\n float ripplePhase = fract(time * 1.5) * 0.3;\n float rippleRadius = shadowEdge + ripplePhase;\n float ripple = exp(-pow(dist - rippleRadius, 2.0) * 80.0);\n pulseRipple = ripple * pulseFactor * 0.15; // Subtle\n }\n\n // === EDGE GLOW - keep subtle; avoid wide smear\n float edgeGlow = 0.0;\n if (dist > shadowEdge && dist < photonSphere + 0.08) {\n float edgeFactor = smoothstep(shadowEdge, photonSphere, dist);\n edgeFactor *= smoothstep(photonSphere + 0.08, photonSphere, dist);\n edgeGlow = edgeFactor * pulseFactor * 0.06;\n }\n\n // === COMBINE EFFECTS ===\n vec3 color = vec3(0.0);\n\n // Photon sphere ring (warm orange-yellow)\n vec3 photonColor = vec3(1.0, 0.8, 0.45);\n color += photonColor * photonRing;\n\n // Soft tip highlight (spin indicator)\n vec3 hotSpotColor = vec3(1.0, 0.9, 0.65);\n color += hotSpotColor * hotSpotGlow;\n\n // Edge glow when feeding\n vec3 glowColor = vec3(1.0, 0.5, 0.2);\n color += glowColor * edgeGlow;\n\n // Feeding pulse ripple\n vec3 pulseColor = vec3(1.0, 0.6, 0.3);\n color += pulseColor * pulseRipple;\n\n // === OUTER FADE ===\n float outerFade = 1.0 - smoothstep(0.9, 1.25, dist);\n color *= outerFade;\n\n // === ALPHA ===\n // Event horizon and photon sphere are fully opaque to occlude background\n float alpha;\n if (dist < photonSphere) {\n alpha = 1.0;\n color = (dist < shadowEdge) ? vec3(0.0) : color; // keep core solid black\n } else {\n // Alpha based on visible content\n float contentBrightness = max(max(color.r, color.g), color.b);\n alpha = smoothstep(0.01, 0.06, contentBrightness);\n alpha = max(alpha, smoothstep(photonSphere + 0.12, shadowEdge, dist) * 0.25);\n alpha *= outerFade;\n }\n\n gl_FragColor = vec4(color, alpha);\n}\n`,ROCKY_PLANET_FRAGMENT=`\n${SPHERE_COMMON}\n\nuniform vec3 uBaseColor;\nuniform float uHasAtmosphere; // 0-1\nuniform float uSeed;\n\nvoid main() {\n // Setup ray - camera looking at sphere from fixed position\n vec3 rayOrigin = vec3(0.0, 0.0, -2.5);\n vec3 rayDir = getRayDirection(vUv);\n\n // Ray-sphere intersection (sphere at origin with radius 0.5)\n float t = raySphereIntersect(rayOrigin, rayDir, vec3(0.0), 0.5);\n\n if (t < 0.0) {\n // Atmosphere halo\n if (uHasAtmosphere > 0.0) {\n vec2 center = vUv - 0.5;\n float dist = length(center) * 2.0;\n float atmo = smoothstep(0.6, 0.5, dist) * smoothstep(0.45, 0.52, dist);\n atmo *= uHasAtmosphere * 0.4;\n vec3 atmoColor = vec3(0.5, 0.7, 1.0) * atmo;\n // Premultiplied alpha\n gl_FragColor = vec4(atmoColor * atmo, atmo);\n } else {\n gl_FragColor = vec4(0.0);\n }\n return;\n }\n\n // Calculate hit point and normal\n vec3 hitPoint = rayOrigin + rayDir * t;\n vec3 normal = normalize(hitPoint);\n\n // Apply inverse camera rotation for surface features\n mat3 rotMat = rotationMatrix(-uCameraRotation);\n vec3 rotatedNormal = rotMat * normal;\n\n // Seeded noise for consistent terrain\n vec3 noiseCoord = rotatedNormal * 4.0 + uSeed * 100.0;\n float terrain = fbm(noiseCoord, 5);\n\n // Height-based coloring\n vec3 lowColor = uBaseColor * 0.6; // Valleys/lowlands\n vec3 highColor = uBaseColor * 1.2; // Mountains/highlands\n vec3 surfaceColor = mix(lowColor, highColor, terrain);\n\n // Add some variation\n float variation = noise3D(rotatedNormal * 10.0 + uSeed * 50.0);\n surfaceColor *= 0.9 + variation * 0.2;\n\n // Lighting\n vec3 lightDir = normalize(vec3(1.0, 1.0, 0.5));\n float light = lighting(normal, lightDir, 0.3);\n surfaceColor *= light;\n\n // Atmosphere scattering at edges\n if (uHasAtmosphere > 0.0) {\n float rim = fresnel(normal, -rayDir, 3.0);\n vec3 atmoColor = vec3(0.5, 0.7, 1.0);\n surfaceColor = mix(surfaceColor, atmoColor, rim * uHasAtmosphere * 0.4);\n }\n\n gl_FragColor = vec4(surfaceColor, 1.0);\n}\n`,GAS_GIANT_FRAGMENT=`\n${SPHERE_COMMON}\n\nuniform vec3 uBaseColor;\nuniform float uSeed;\nuniform float uStormIntensity; // 0-1\nuniform float uRotationSpeed; // rotation speed multiplier (default ~0.1)\n\nvoid main() {\n // Setup ray - camera looking at sphere from fixed position\n vec3 rayOrigin = vec3(0.0, 0.0, -2.5);\n vec3 rayDir = getRayDirection(vUv);\n\n // Ray-sphere intersection (sphere at origin with radius 0.5)\n float t = raySphereIntersect(rayOrigin, rayDir, vec3(0.0), 0.5);\n\n if (t < 0.0) {\n gl_FragColor = vec4(0.0);\n return;\n }\n\n // Calculate hit point and normal\n vec3 hitPoint = rayOrigin + rayDir * t;\n vec3 normal = normalize(hitPoint);\n\n // Apply inverse camera rotation for surface features\n mat3 rotMat = rotationMatrix(-uCameraRotation);\n vec3 rotatedNormal = rotMat * normal;\n\n // Convert to spherical coordinates for banding (use rotated normal)\n float latitude = asin(rotatedNormal.y); // -PI/2 to PI/2\n float longitude = atan(rotatedNormal.z, rotatedNormal.x); // -PI to PI\n\n // Animated rotation (use uRotationSpeed, default to 0.1 if not set)\n float rotSpeed = uRotationSpeed > 0.0 ? uRotationSpeed : 0.1;\n float time = uTime * rotSpeed;\n\n // Create bands based on latitude\n float bands = sin(latitude * 15.0 + time) * 0.5 + 0.5;\n bands += sin(latitude * 25.0 - time * 0.5) * 0.25;\n bands += sin(latitude * 40.0 + time * 0.3) * 0.125;\n\n // Turbulent distortion of bands\n vec3 noiseCoord = vec3(longitude + time * 0.2, latitude * 3.0, uSeed);\n float turb = fbm(noiseCoord * 5.0, 4) * 0.3;\n bands += turb;\n\n // Color variation based on bands\n vec3 lightBand = uBaseColor * 1.3;\n vec3 darkBand = uBaseColor * 0.7;\n vec3 surfaceColor = mix(darkBand, lightBand, bands);\n\n // Add storm features\n if (uStormIntensity > 0.0) {\n // Great red spot style storm\n float stormLat = 0.3; // Storm latitude\n float stormLon = time * 0.5; // Storm drifts\n vec2 stormCenter = vec2(stormLon, stormLat);\n vec2 pos = vec2(longitude, latitude);\n float stormDist = length(pos - stormCenter);\n float storm = smoothstep(0.5, 0.2, stormDist);\n storm *= uStormIntensity;\n\n // Storm color and swirl\n vec3 stormColor = vec3(0.8, 0.3, 0.2);\n float swirl = sin(stormDist * 20.0 - time * 3.0) * 0.5 + 0.5;\n surfaceColor = mix(surfaceColor, stormColor * swirl, storm);\n }\n\n // Lighting with some subsurface scattering effect\n vec3 lightDir = normalize(vec3(1.0, 0.5, 0.3));\n float light = lighting(normal, lightDir, 0.4);\n surfaceColor *= light;\n\n // Limb darkening\n float viewAngle = dot(normal, -rayDir);\n surfaceColor *= 0.7 + max(0.0, viewAngle) * 0.3;\n\n gl_FragColor = vec4(surfaceColor, 1.0);\n}\n`,SPHERE_SHADERS={vertex:SPHERE_VERTEX,common:SPHERE_COMMON,star:STAR_FRAGMENT,blackHole:BLACK_HOLE_FRAGMENT,rockyPlanet:ROCKY_PLANET_FRAGMENT,gasGiant:GAS_GIANT_FRAGMENT},_Sphere3D=class t extends Shape{static _getGLRenderer(e,i){return t._glRenderer?t._glRendererSize.width===e&&t._glRendererSize.height===i||(t._glRenderer.resize(e,i),t._glRendererSize={width:e,height:i}):(t._glRenderer=new WebGLRenderer(e,i),t._glRendererSize={width:e,height:i}),t._glRenderer}constructor(t,e={}){super(e),this.radius=t,this.camera=e.camera??null,this.debug=e.debug??!1,this.segments=e.segments??20,this.useShader=e.useShader??!1,this.shaderType=e.shaderType??"star",this.shaderUniforms=e.shaderUniforms??{},this._shaderInitialized=!1,this.selfRotationX=e.selfRotationX??0,this.selfRotationY=e.selfRotationY??0,this.selfRotationZ=e.selfRotationZ??0,this._generateGeometry()}setCamera(t){return this.camera=t,this}setShaderUniforms(t){return Object.assign(this.shaderUniforms,t),this}_getFragmentShader(){switch(this.shaderType){case"star":default:return SPHERE_SHADERS.star;case"blackHole":return SPHERE_SHADERS.blackHole;case"rockyPlanet":return SPHERE_SHADERS.rockyPlanet;case"gasGiant":return SPHERE_SHADERS.gasGiant}}_initShader(e,i){const s=t._getGLRenderer(e,i);if(!s||!s.isAvailable())return void(this.useShader=!1);const n=`sphere_${this.shaderType}`;s.useProgram(n,SPHERE_SHADERS.vertex,this._getFragmentShader()),this._shaderInitialized=!0}_renderWithShader(e,i,s,n){var a,r,o,h;const l=n*(1+((null==(a=this.shaderUniforms)?void 0:a.uTidalStretch)??0)),c=Math.ceil(2*(n+l)),u=t._getGLRenderer(c,c);if(!u||!u.isAvailable())return!1;this._shaderInitialized||this._initShader(c,c);const d=`sphere_${this.shaderType}`;u.useProgram(d,SPHERE_SHADERS.vertex,this._getFragmentShader()),u.clear(0,0,0,0);const _=1.25*n/(c/2);u.setUniforms({uTime:performance.now()/1e3,uResolution:[c,c],uBaseRadius:_,uCameraRotation:[(null==(r=this.camera)?void 0:r.rotationX)??0,(null==(o=this.camera)?void 0:o.rotationY)??0,(null==(h=this.camera)?void 0:h.rotationZ)??0]}),u.setUniforms(this.shaderUniforms);for(const[t,e]of Object.entries(this.shaderUniforms))"string"==typeof e&&e.startsWith("#")&&u.setColorUniform(t,e);u.render();const p=i-c/2,g=s-c/2;return u.compositeOnto(e,p,g,c,c),!0}_generateGeometry(){this.vertices=[],this.faces=[];const t=this.segments,e=2*this.segments;for(let i=0;i<=t;i++){const s=i*Math.PI/t,n=Math.sin(s),a=Math.cos(s);for(let t=0;t<=e;t++){const i=2*t*Math.PI/e,s=Math.sin(i),r=Math.cos(i),o=this.radius*n*r,h=this.radius*a,l=this.radius*n*s;this.vertices.push({x:o,y:h,z:l,nx:n*r,ny:a,nz:n*s})}}for(let i=0;i<t;i++)for(let t=0;t<e;t++){const s=i*(e+1)+t,n=s+e+1;this.faces.push([s,n,s+1]),this.faces.push([n,n+1,s+1])}}_applySelfRotation(t,e,i){if(0!==this.selfRotationY){const e=Math.cos(this.selfRotationY),s=Math.sin(this.selfRotationY),n=t*s+i*e;t=t*e-i*s,i=n}if(0!==this.selfRotationX){const t=Math.cos(this.selfRotationX),s=Math.sin(this.selfRotationX),n=e*s+i*t;e=e*t-i*s,i=n}if(0!==this.selfRotationZ){const i=Math.cos(this.selfRotationZ),s=Math.sin(this.selfRotationZ),n=t*s+e*i;t=t*i-e*s,e=n}return{x:t,y:e,z:i}}_calculateLighting(t,e,i){const s=Math.sqrt(.99);let n=t*(.5/s)+e*(.7/s)+i*(.5/s);return n=.7*Math.max(0,n)+.3,n}_applyLighting(t,e){if(!t||"string"!=typeof t||!t.startsWith("#"))return t;const i=t.replace("#",""),s=parseInt(i.substring(0,2),16),n=parseInt(i.substring(2,4),16),a=parseInt(i.substring(4,6),16);return`rgb(${Math.round(s*e)}, ${Math.round(n*e)}, ${Math.round(a*e)})`}draw(){if(super.draw(),!this.camera)return this.color&&Painter.shapes.fillCircle(0,0,this.radius,this.color),void(this.debug&&this.stroke&&Painter.shapes.strokeCircle(0,0,this.radius,this.stroke,this.lineWidth));if(this.useShader&&!this.debug){const t=this.camera.project(this.x||0,this.y||0,this.z||0),e=this.camera.perspective/(this.camera.perspective+t.z),i=this.radius*e,s=Painter.ctx,n=s.getTransform(),a=n.e,r=n.f;s.save(),s.setTransform(1,0,0,1,0,0);const o=this._renderWithShader(s,a+t.x,r+t.y,i);if(s.restore(),o)return}const t=0!==this.selfRotationX||0!==this.selfRotationY||0!==this.selfRotationZ,e=this.vertices.map(e=>{let i=e.x,s=e.y,n=e.z,a=e.nx,r=e.ny,o=e.nz;if(t){const t=this._applySelfRotation(i,s,n);i=t.x,s=t.y,n=t.z;const e=this._applySelfRotation(a,r,o);a=e.x,r=e.y,o=e.z}const h=this.camera.project(i+(this.x||0),s+(this.y||0),n+(this.z||0));if(0!==this.camera.rotationZ){const t=Math.cos(this.camera.rotationZ),e=Math.sin(this.camera.rotationZ),i=a;a=i*t-r*e,r=i*e+r*t}const l=Math.cos(this.camera.rotationY),c=Math.sin(this.camera.rotationY),u=a*l-o*c,d=a*c+o*l,_=Math.cos(this.camera.rotationX),p=Math.sin(this.camera.rotationX);return{...h,nx:u,ny:r*_-d*p,nz:r*p+d*_}});this.debug&&this.trace("Sphere3D.draw: projected vertices",e.length);const i=[];for(const t of this.faces){const s=e[t[0]],n=e[t[1]],a=e[t[2]];if(s.z<10-this.camera.perspective||n.z<10-this.camera.perspective||a.z<10-this.camera.perspective)continue;const r=(s.z+n.z+a.z)/3,o=(s.nx+n.nx+a.nx)/3,h=(s.ny+n.ny+a.ny)/3,l=(s.nz+n.nz+a.nz)/3;if(l>.1)continue;const c=this._calculateLighting(o,h,l);i.push({vertices:[s,n,a],avgZ:r,intensity:c})}i.sort((t,e)=>e.avgZ-t.avgZ);for(const t of i){const e=t.vertices.map(t=>({x:t.x,y:t.y}));if(this.debug)Painter.ctx.beginPath(),Painter.ctx.moveTo(e[0].x,e[0].y),Painter.ctx.lineTo(e[1].x,e[1].y),Painter.ctx.lineTo(e[2].x,e[2].y),Painter.ctx.closePath(),this.stroke&&(Painter.ctx.strokeStyle=this.stroke,Painter.ctx.lineWidth=this.lineWidth??1,Painter.ctx.stroke());else if(this.color){const i=this._applyLighting(this.color,t.intensity);Painter.ctx.beginPath(),Painter.ctx.moveTo(e[0].x,e[0].y),Painter.ctx.lineTo(e[1].x,e[1].y),Painter.ctx.lineTo(e[2].x,e[2].y),Painter.ctx.closePath(),Painter.ctx.fillStyle=i,Painter.ctx.fill()}}}calculateBounds(){const t=2*this.radius;return{x:this.x,y:this.y,width:t,height:t}}};__publicField(_Sphere3D,"_glRenderer",null),__publicField(_Sphere3D,"_glRendererSize",{width:0,height:0});let Sphere3D=_Sphere3D;const PLANE_VERTEX="\nprecision highp float;\n\nattribute vec2 aPosition;\nattribute vec2 aUv;\n\nvarying vec2 vUv;\n\nvoid main() {\n vUv = aUv;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n",PLANE_COMMON="\nprecision highp float;\n\nvarying vec2 vUv;\n\nuniform float uTime;\nuniform vec2 uResolution;\n\n// =============================================================================\n// NOISE FUNCTIONS\n// =============================================================================\n\nfloat hash(float n) {\n return fract(sin(n) * 43758.5453123);\n}\n\nfloat hash2(vec2 p) {\n return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\n// 2D Value noise\nfloat noise2D(vec2 x) {\n vec2 i = floor(x);\n vec2 f = fract(x);\n f = f * f * (3.0 - 2.0 * f);\n\n float a = hash2(i);\n float b = hash2(i + vec2(1.0, 0.0));\n float c = hash2(i + vec2(0.0, 1.0));\n float d = hash2(i + vec2(1.0, 1.0));\n\n return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n}\n\n// FBM (Fractional Brownian Motion)\nfloat fbm2D(vec2 p, int octaves) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n\n for (int i = 0; i < 8; i++) {\n if (i >= octaves) break;\n value += amplitude * noise2D(p * frequency);\n frequency *= 2.0;\n amplitude *= 0.5;\n }\n\n return value;\n}\n",GRADIENT_FRAGMENT=`\n${PLANE_COMMON}\n\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform float uAngle;\n\nvoid main() {\n vec2 uv = vUv;\n\n // Calculate gradient direction from angle\n vec2 dir = vec2(cos(uAngle), sin(uAngle));\n\n // Project UV onto gradient direction\n // Center at 0.5 so gradient goes through middle\n float t = dot(uv - 0.5, dir) + 0.5;\n t = clamp(t, 0.0, 1.0);\n\n // Interpolate colors\n vec3 color = mix(uColor1, uColor2, t);\n\n gl_FragColor = vec4(color, 1.0);\n}\n`,GRID_FRAGMENT=`\n${PLANE_COMMON}\n\nuniform vec3 uLineColor;\nuniform vec3 uBackgroundColor;\nuniform float uGridSize;\nuniform float uLineWidth;\n\nvoid main() {\n vec2 uv = vUv;\n\n // Scale UV to grid space\n vec2 grid = fract(uv * uGridSize);\n\n // Calculate distance to nearest grid line\n float lineX = min(grid.x, 1.0 - grid.x);\n float lineY = min(grid.y, 1.0 - grid.y);\n\n // Smoothstep for anti-aliased lines\n float halfWidth = uLineWidth * 0.5;\n float edgeSmooth = 0.01;\n\n float lineAlphaX = 1.0 - smoothstep(halfWidth - edgeSmooth, halfWidth + edgeSmooth, lineX);\n float lineAlphaY = 1.0 - smoothstep(halfWidth - edgeSmooth, halfWidth + edgeSmooth, lineY);\n float lineAlpha = max(lineAlphaX, lineAlphaY);\n\n // Mix colors\n vec3 color = mix(uBackgroundColor, uLineColor, lineAlpha);\n\n gl_FragColor = vec4(color, 1.0);\n}\n`,CHECKERBOARD_FRAGMENT=`\n${PLANE_COMMON}\n\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform float uSize;\n\nvoid main() {\n vec2 uv = vUv;\n\n // Calculate which square we're in\n vec2 cell = floor(uv * uSize);\n\n // Alternating pattern based on cell coordinates\n float checker = mod(cell.x + cell.y, 2.0);\n\n // Select color\n vec3 color = mix(uColor1, uColor2, checker);\n\n gl_FragColor = vec4(color, 1.0);\n}\n`,NOISE_FRAGMENT=`\n${PLANE_COMMON}\n\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform float uNoiseScale;\nuniform float uAnimSpeed;\n\nvoid main() {\n vec2 uv = vUv;\n\n // Animated noise\n float n = fbm2D(uv * uNoiseScale + uTime * uAnimSpeed, 4);\n\n // Map noise to colors\n vec3 color = mix(uColor1, uColor2, n);\n\n gl_FragColor = vec4(color, 1.0);\n}\n`,PLANE_SHADERS={vertex:PLANE_VERTEX,gradient:GRADIENT_FRAGMENT,grid:GRID_FRAGMENT,checkerboard:CHECKERBOARD_FRAGMENT,noise:NOISE_FRAGMENT},_Plane3D=class t extends Shape{static _getGLRenderer(e,i){return t._glRenderer?t._glRendererSize.width===e&&t._glRendererSize.height===i||(t._glRenderer.resize(e,i),t._glRendererSize={width:e,height:i}):(t._glRenderer=new WebGLRenderer(e,i),t._glRendererSize={width:e,height:i}),t._glRenderer}constructor(t,e,i={}){super(i),this.planeWidth=t,this.planeHeight=e,this.x=i.x??0,this.y=i.y??0,this.z=i.z??0,this.camera=i.camera??null,this.debug=i.debug??!1,this.doubleSided=i.doubleSided??!0,this.texture=i.texture??null,this.selfRotationX=i.selfRotationX??0,this.selfRotationY=i.selfRotationY??0,this.selfRotationZ=i.selfRotationZ??0,this.useShader=i.useShader??!1,this.shaderType=i.shaderType??"gradient",this.shaderUniforms=i.shaderUniforms??{},this._shaderInitialized=!1,this._generateGeometry()}setCamera(t){return this.camera=t,this}setTexture(t){return this.texture=t,this}setShaderUniforms(t){return Object.assign(this.shaderUniforms,t),this}_generateGeometry(){const t=this.planeWidth/2,e=this.planeHeight/2;this.vertices=[{x:-t,y:-e,z:0,nx:0,ny:0,nz:-1,u:0,v:0},{x:t,y:-e,z:0,nx:0,ny:0,nz:-1,u:1,v:0},{x:t,y:e,z:0,nx:0,ny:0,nz:-1,u:1,v:1},{x:-t,y:e,z:0,nx:0,ny:0,nz:-1,u:0,v:1}],this.faces=[[0,1,2],[0,2,3]]}_getFragmentShader(){switch(this.shaderType){case"gradient":default:return PLANE_SHADERS.gradient;case"grid":return PLANE_SHADERS.grid;case"checkerboard":return PLANE_SHADERS.checkerboard;case"noise":return PLANE_SHADERS.noise}}_initShader(e,i){const s=t._getGLRenderer(e,i);if(!s||!s.isAvailable())return!1;const n=`plane_${this.shaderType}`,a=PLANE_SHADERS.vertex,r=this._getFragmentShader();try{return s.useProgram(n,a,r),this._shaderInitialized=!0,!0}catch(t){return console.warn("Plane3D shader init failed:",t),!1}}_renderWithShader(e,i,s,n,a){const r=Math.max(Math.ceil(Math.max(n,a)),16);if(!this._shaderInitialized&&!this._initShader(r,r))return!1;const o=t._getGLRenderer(r,r);if(!o||!o.isAvailable())return!1;const h=`plane_${this.shaderType}`;o.useProgram(h,PLANE_SHADERS.vertex,this._getFragmentShader());const l=performance.now()/1e3;o.setUniforms({uTime:l,uResolution:[r,r]}),o.setUniforms(this.shaderUniforms),o.render();const c=i-n/2,u=s-a/2;return o.compositeOnto(e,c,u,n,a),!0}_applySelfRotation(t,e,i){if(0!==this.selfRotationY){const e=Math.cos(this.selfRotationY),s=Math.sin(this.selfRotationY),n=t*s+i*e;t=t*e-i*s,i=n}if(0!==this.selfRotationX){const t=Math.cos(this.selfRotationX),s=Math.sin(this.selfRotationX),n=e*s+i*t;e=e*t-i*s,i=n}if(0!==this.selfRotationZ){const i=Math.cos(this.selfRotationZ),s=Math.sin(this.selfRotationZ),n=t*s+e*i;t=t*i-e*s,e=n}return{x:t,y:e,z:i}}_calculateLighting(t,e,i){const s=Math.sqrt(.99);let n=t*(.5/s)+e*(.7/s)+i*(.5/s);return n=.7*Math.max(0,n)+.3,n}_applyLighting(t,e){if(!t||"string"!=typeof t||!t.startsWith("#"))return t;const i=t.replace("#",""),s=parseInt(i.substring(0,2),16),n=parseInt(i.substring(2,4),16),a=parseInt(i.substring(4,6),16);return`rgb(${Math.round(s*e)}, ${Math.round(n*e)}, ${Math.round(a*e)})`}draw(){if(super.draw(),!this.camera)return void(this.color&&Painter.shapes.fillRect(-this.planeWidth/2,-this.planeHeight/2,this.planeWidth,this.planeHeight,this.color));const t=0!==this.selfRotationX||0!==this.selfRotationY||0!==this.selfRotationZ,e=this.vertices.map(e=>{let i=e.x,s=e.y,n=e.z,a=e.nx,r=e.ny,o=e.nz;if(t){const t=this._applySelfRotation(i,s,n);i=t.x,s=t.y,n=t.z;const e=this._applySelfRotation(a,r,o);a=e.x,r=e.y,o=e.z}const h=this.camera.project(i+this.x,s+this.y,n+this.z);if(0!==this.camera.rotationZ){const t=Math.cos(this.camera.rotationZ),e=Math.sin(this.camera.rotationZ),i=a;a=i*t-r*e,r=i*e+r*t}const l=Math.cos(this.camera.rotationY),c=Math.sin(this.camera.rotationY),u=a*l-o*c,d=a*c+o*l,_=Math.cos(this.camera.rotationX),p=Math.sin(this.camera.rotationX);return{...h,nx:u,ny:r*_-d*p,nz:r*p+d*_,u:e.u,v:e.v}}),i=(e[0].nz+e[1].nz+e[2].nz+e[3].nz)/4;if(!this.doubleSided&&i>.1)return;e[0].z,e[1].z,e[2].z,e[3].z;if(e.some(t=>t.z<10-this.camera.perspective))return;if(this.useShader&&!this.debug){const t=e.map(t=>t.x),i=e.map(t=>t.y),s=Math.min(...t),n=Math.max(...t),a=Math.min(...i),r=Math.max(...i),o=n-s,h=r-a,l=(s+n)/2,c=(a+r)/2,u=Painter.ctx,d=u.getTransform(),_=d.e,p=d.f;u.save(),u.setTransform(1,0,0,1,0,0);const g=this._renderWithShader(u,_+l,p+c,o,h);if(u.restore(),g)return}const s=Painter.ctx;let n=(e[0].nx+e[1].nx+e[2].nx+e[3].nx)/4,a=(e[0].ny+e[1].ny+e[2].ny+e[3].ny)/4,r=n,o=a,h=i;this.doubleSided&&i>0&&(r=-n,o=-a,h=-i);const l=this._calculateLighting(r,o,h);for(const t of this.faces){const i=e[t[0]],n=e[t[1]],a=e[t[2]];if(this.debug)s.beginPath(),s.moveTo(i.x,i.y),s.lineTo(n.x,n.y),s.lineTo(a.x,a.y),s.closePath(),this.stroke&&(s.strokeStyle=this.stroke,s.lineWidth=this.lineWidth??1,s.stroke());else if(this.texture)this._renderTexturedTriangle(s,i,n,a);else if(this.color){const t=this._applyLighting(this.color,l);s.beginPath(),s.moveTo(i.x,i.y),s.lineTo(n.x,n.y),s.lineTo(a.x,a.y),s.closePath(),s.fillStyle=t,s.fill()}}}_renderTexturedTriangle(t,e,i,s){if(!this.texture||!this.texture.complete)return;const n=this.texture,a=n.width,r=n.height,o=e.u*a,h=e.v*r,l=i.u*a,c=i.v*r,u=s.u*a,d=s.v*r,_=e.x,p=e.y,g=i.x,f=i.y,m=s.x,v=s.y,y=(l-o)*(d-h)-(u-o)*(c-h);if(Math.abs(y)<1e-4)return;const x=((g-_)*(d-h)-(m-_)*(c-h))/y,b=((m-_)*(l-o)-(g-_)*(u-o))/y,w=_-x*o-b*h,M=((f-p)*(d-h)-(v-p)*(c-h))/y,S=((v-p)*(l-o)-(f-p)*(u-o))/y,P=p-M*o-S*h;t.save(),t.beginPath(),t.moveTo(_,p),t.lineTo(g,f),t.lineTo(m,v),t.closePath(),t.clip(),t.setTransform(x,M,b,S,w,P),t.drawImage(n,0,0),t.restore()}getCenter(){return{x:this.x,y:this.y,z:this.z}}getBounds(){return{x:this.x-this.planeWidth/2,y:this.y-this.planeHeight/2,width:this.planeWidth,height:this.planeHeight}}};__publicField(_Plane3D,"_glRenderer",null),__publicField(_Plane3D,"_glRendererSize",{width:0,height:0});let Plane3D=_Plane3D;class Cube3D extends Shape{constructor(t,e={}){var i,s,n,a,r,o;super(e),this.size=t,this.x=e.x??0,this.y=e.y??0,this.z=e.z??0,this.camera=e.camera??null,this.debug=e.debug??!1,this.selfRotationX=e.selfRotationX??0,this.selfRotationY=e.selfRotationY??0,this.selfRotationZ=e.selfRotationZ??0,this.faceColors={front:(null==(i=e.faceColors)?void 0:i.front)??"#B71234",back:(null==(s=e.faceColors)?void 0:s.back)??"#FF5800",top:(null==(n=e.faceColors)?void 0:n.top)??"#FFFFFF",bottom:(null==(a=e.faceColors)?void 0:a.bottom)??"#FFD500",left:(null==(r=e.faceColors)?void 0:r.left)??"#009B48",right:(null==(o=e.faceColors)?void 0:o.right)??"#0046AD"},this.stroke=e.stroke??null,this.lineWidth=e.lineWidth??1,this.stickerMode=e.stickerMode??!1,this.stickerMargin=e.stickerMargin??.15,this.stickerBackgroundColor=e.stickerBackgroundColor??"#0A0A0A",this._faceConfigs=this._createFaceConfigs()}_createFaceConfigs(){const t=this.size/2;return{front:{localPos:{x:0,y:0,z:-t},faceRotX:0,faceRotY:0,color:this.faceColors.front},back:{localPos:{x:0,y:0,z:t},faceRotX:0,faceRotY:Math.PI,color:this.faceColors.back},top:{localPos:{x:0,y:-t,z:0},faceRotX:-Math.PI/2,faceRotY:0,color:this.faceColors.top},bottom:{localPos:{x:0,y:t,z:0},faceRotX:Math.PI/2,faceRotY:0,color:this.faceColors.bottom},left:{localPos:{x:-t,y:0,z:0},faceRotX:0,faceRotY:Math.PI/2,color:this.faceColors.left},right:{localPos:{x:t,y:0,z:0},faceRotX:0,faceRotY:-Math.PI/2,color:this.faceColors.right}}}setCamera(t){return this.camera=t,this}setFaceColors(t){Object.assign(this.faceColors,t);for(const[e,i]of Object.entries(this._faceConfigs))t[e]&&(i.color=t[e]);return this}_applySelfRotation(t,e,i){if(0!==this.selfRotationY){const e=Math.cos(this.selfRotationY),s=Math.sin(this.selfRotationY),n=t*s+i*e;t=t*e-i*s,i=n}if(0!==this.selfRotationX){const t=Math.cos(this.selfRotationX),s=Math.sin(this.selfRotationX),n=e*s+i*t;e=e*t-i*s,i=n}if(0!==this.selfRotationZ){const i=Math.cos(this.selfRotationZ),s=Math.sin(this.selfRotationZ),n=t*s+e*i;t=t*i-e*s,e=n}return{x:t,y:e,z:i}}_calculateLighting(t,e,i){const s=Math.sqrt(.99);let n=t*(.5/s)+e*(.7/s)+i*(.5/s);return n=.7*Math.max(0,n)+.3,n}_applyLighting(t,e){if(!t||"string"!=typeof t||!t.startsWith("#"))return t;const i=t.replace("#",""),s=parseInt(i.substring(0,2),16),n=parseInt(i.substring(2,4),16),a=parseInt(i.substring(4,6),16);return`rgb(${Math.round(s*e)}, ${Math.round(n*e)}, ${Math.round(a*e)})`}draw(){if(super.draw(),!this.camera){const t=this.size/2;return void(this.faceColors.front&&Painter.shapes.fillRect(-t,-t,this.size,this.size,this.faceColors.front))}const t=Painter.ctx;this.size;const e=0!==this.selfRotationX||0!==this.selfRotationY||0!==this.selfRotationZ,i=[];for(const[t,s]of Object.entries(this._faceConfigs)){let{x:n,y:a,z:r}=s.localPos;if(e){const t=this._applySelfRotation(n,a,r);n=t.x,a=t.y,r=t.z}const o=this.x+n,h=this.y+a,l=this.z+r;let c=0,u=0,d=0;if("front"===t?(c=0,u=0,d=-1):"back"===t?(c=0,u=0,d=1):"top"===t?(c=0,u=-1,d=0):"bottom"===t?(c=0,u=1,d=0):"left"===t?(c=-1,u=0,d=0):"right"===t&&(c=1,u=0,d=0),e){const t=this._applySelfRotation(c,u,d);c=t.x,u=t.y,d=t.z}let _=c,p=u,g=d;if(0!==this.camera.rotationZ){const t=Math.cos(this.camera.rotationZ),e=Math.sin(this.camera.rotationZ),i=_;_=i*t-p*e,p=i*e+p*t}const f=Math.cos(this.camera.rotationY),m=Math.sin(this.camera.rotationY),v=_*f-g*m,y=_*m+g*f,x=Math.cos(this.camera.rotationX),b=Math.sin(this.camera.rotationX),w=p*x-y*b,M=p*b+y*x;if(M>.01)continue;const S=this.camera.project(o,h,l);if(S.z<10-this.camera.perspective)continue;const P=this._calculateLighting(v,w,M),T=this._getFaceVertices(t,s,e);i.push({name:t,config:s,projected:S,vertices:T,depth:S.z,intensity:P,nx:v,ny:w,nz:M})}i.sort((t,e)=>e.depth-t.depth);for(const e of i)this._renderFace(t,e)}_getFaceVertices(t,e,i){const s=this.size/2;let n;switch(t){case"front":n=[{x:-s,y:-s,z:-s},{x:s,y:-s,z:-s},{x:s,y:s,z:-s},{x:-s,y:s,z:-s}];break;case"back":n=[{x:s,y:-s,z:s},{x:-s,y:-s,z:s},{x:-s,y:s,z:s},{x:s,y:s,z:s}];break;case"top":n=[{x:-s,y:-s,z:s},{x:s,y:-s,z:s},{x:s,y:-s,z:-s},{x:-s,y:-s,z:-s}];break;case"bottom":n=[{x:-s,y:s,z:-s},{x:s,y:s,z:-s},{x:s,y:s,z:s},{x:-s,y:s,z:s}];break;case"left":n=[{x:-s,y:-s,z:s},{x:-s,y:-s,z:-s},{x:-s,y:s,z:-s},{x:-s,y:s,z:s}];break;case"right":n=[{x:s,y:-s,z:-s},{x:s,y:-s,z:s},{x:s,y:s,z:s},{x:s,y:s,z:-s}]}return n.map(t=>{let{x:e,y:s,z:n}=t;if(i){const t=this._applySelfRotation(e,s,n);e=t.x,s=t.y,n=t.z}return e+=this.x,s+=this.y,n+=this.z,this.camera.project(e,s,n)})}_renderFace(t,e){const{vertices:i,config:s,intensity:n}=e,a=this._applyLighting(s.color,n);t.beginPath(),t.moveTo(i[0].x,i[0].y);for(let e=1;e<i.length;e++)t.lineTo(i[e].x,i[e].y);if(t.closePath(),this.debug)t.strokeStyle=this.stroke||"#fff",t.lineWidth=this.lineWidth??1,t.stroke();else if(this.stickerMode){const e=this._applyLighting(this.stickerBackgroundColor,n);t.fillStyle=e,t.fill(),this.stroke&&(t.strokeStyle=this.stroke,t.lineWidth=this.lineWidth??1,t.stroke());const s=this._getInsetVertices(i,this.stickerMargin);t.beginPath(),t.moveTo(s[0].x,s[0].y);for(let e=1;e<s.length;e++)t.lineTo(s[e].x,s[e].y);t.closePath(),t.fillStyle=a,t.fill()}else t.fillStyle=a,t.fill(),this.stroke&&(t.strokeStyle=this.stroke,t.lineWidth=this.lineWidth??1,t.stroke())}_getInsetVertices(t,e){let i=0,s=0;for(const e of t)i+=e.x,s+=e.y;i/=t.length,s/=t.length;const n=1-2*e;return t.map(t=>({x:i+(t.x-i)*n,y:s+(t.y-s)*n}))}getCenter(){return{x:this.x,y:this.y,z:this.z}}getBounds(){const t=this.size/2;return{x:this.x-t,y:this.y-t,width:this.size,height:this.size}}}class SVGShape extends Shape{constructor(t,e={}){console.log("SVGShape",e.x),super(e),this.scale=e.scale||1,this.centerPath=void 0===e.centerPath||e.centerPath,this.animationProgress=void 0!==e.animationProgress?e.animationProgress:1,this.svgPathData=t,this.pathCommands=this.parseSVGPath(t),this.centerPath?this.pathCommands=this.centerAndScalePath(this.pathCommands,this.scale):this.pathCommands=this.scalePath(this.pathCommands,this.scale),this.prevX=0,this.prevY=0,this.currentPoint={x:0,y:0}}parseSVGPath(t){const e=/M\s*([-\d.]+)[,\s]*([-\d.]+)/g,i=/L\s*([-\d.]+)[,\s]*([-\d.]+)/g,s=/C\s*([-\d.]+)[,\s]*([-\d.]+)\s*([-\d.]+)[,\s]*([-\d.]+)\s*([-\d.]+)[,\s]*([-\d.]+)/g,n=[];let a;for(;null!==(a=e.exec(t));)n.push(["M",parseFloat(a[1]),parseFloat(a[2])]);for(;null!==(a=i.exec(t));){const t=parseFloat(a[1]),e=parseFloat(a[2]);let i=0,s=0;for(let t=n.length-1;t>=0;t--){const e=n[t];if("M"===e[0]){i=e[1],s=e[2];break}if("C"===e[0]){i=e[5],s=e[6];break}}const r=i+(t-i)/3,o=s+(e-s)/3,h=i+2*(t-i)/3,l=s+2*(e-s)/3;n.push(["C",r,o,h,l,t,e])}for(;null!==(a=s.exec(t));)n.push(["C",parseFloat(a[1]),parseFloat(a[2]),parseFloat(a[3]),parseFloat(a[4]),parseFloat(a[5]),parseFloat(a[6])]);return/Z/g.test(t)&&n.push(["Z"]),n}centerAndScalePath(t,e){let i=1/0,s=1/0,n=-1/0,a=-1/0;for(const e of t)"M"===e[0]?(i=Math.min(i,e[1]),s=Math.min(s,e[2]),n=Math.max(n,e[1]),a=Math.max(a,e[2])):"C"===e[0]&&(i=Math.min(i,e[1],e[3],e[5]),s=Math.min(s,e[2],e[4],e[6]),n=Math.max(n,e[1],e[3],e[5]),a=Math.max(a,e[2],e[4],e[6]));const r=(i+n)/2,o=(s+a)/2;return this.originalWidth=(n-i)*e,this.originalHeight=(a-s)*e,t.map(t=>"M"===t[0]?["M",(t[1]-r)*e,(t[2]-o)*e]:"C"===t[0]?["C",(t[1]-r)*e,(t[2]-o)*e,(t[3]-r)*e,(t[4]-o)*e,(t[5]-r)*e,(t[6]-o)*e]:[...t])}scalePath(t,e){let i=1/0,s=1/0,n=-1/0,a=-1/0;for(const e of t)"M"===e[0]?(i=Math.min(i,e[1]),s=Math.min(s,e[2]),n=Math.max(n,e[1]),a=Math.max(a,e[2])):"C"===e[0]&&(i=Math.min(i,e[1],e[3],e[5]),s=Math.min(s,e[2],e[4],e[6]),n=Math.max(n,e[1],e[3],e[5]),a=Math.max(a,e[2],e[4],e[6]));return this.originalWidth=(n-i)*e,this.originalHeight=(a-s)*e,t.map(t=>"M"===t[0]?["M",t[1]*e,t[2]*e]:"C"===t[0]?["C",t[1]*e,t[2]*e,t[3]*e,t[4]*e,t[5]*e,t[6]*e]:[...t])}getBezierPoint(t,e){if("M"===t[0])return{x:t[1],y:t[2]};if("C"===t[0]){const i=this.prevX,s=this.prevY,n=t[1],a=t[2],r=t[3],o=t[4],h=t[5],l=t[6];return{x:Math.pow(1-e,3)*i+3*Math.pow(1-e,2)*e*n+3*(1-e)*Math.pow(e,2)*r+Math.pow(e,3)*h,y:Math.pow(1-e,3)*s+3*Math.pow(1-e,2)*e*a+3*(1-e)*Math.pow(e,2)*o+Math.pow(e,3)*l}}return{x:0,y:0}}getPartialPath(){const t=[];let e=this.pathCommands.length,i=Math.floor(this.animationProgress*e),s=this.animationProgress*e%1,n=!1;this.prevX=0,this.prevY=0;for(let e=0;e<i;e++){const i=this.pathCommands[e];t.push([...i]),"M"===i[0]?(this.prevX=i[1],this.prevY=i[2],n=!0):"C"===i[0]&&(this.prevX=i[5],this.prevY=i[6],n=!0)}if(i<e){const e=this.pathCommands[i];if("M"===e[0])t.push([...e]),this.prevX=e[1],this.prevY=e[2],this.currentPoint={x:e[1],y:e[2]},n=!0;else if("C"===e[0]){if(!n){for(let t=i-1;t>=0;t--)if("M"===this.pathCommands[t][0]){this.prevX=this.pathCommands[t][1],this.prevY=this.pathCommands[t][2],n=!0;break}n||(this.prevX=0,this.prevY=0)}const a=this.getBezierPoint(e,s);t.push(["C",e[1],e[2],e[3],e[4],a.x,a.y]),this.currentPoint=a}}return t}draw(){super.draw();const t=this.getPartialPath();Painter.lines.path(t,this.color,this.stroke,this.lineWidth)}getCurrentPoint(){return{x:this.currentPoint.x,y:this.currentPoint.y}}setAnimationProgress(t){this.animationProgress=Math.max(0,Math.min(1,t))}calculateBounds(){return{x:this.x,y:this.y,width:this.originalWidth||100,height:this.originalHeight||100}}}class StickFigure extends Shape{constructor(t=1,e={}){super(e),this.scale=t,this.stroke=e.stroke||"#000",this.headColor=e.headColor||this.stroke,this.jointColor=e.jointColor||this.stroke,this.lineWidth=e.lineWidth||2,this.showJoints=!1!==e.showJoints}draw(){super.draw();const t=this.scale,e=10*t,i=-30*t,s=i+e,n=s+40*t,a=s+10*t,r=15*t,o=10*t,h=n+40*t,l=3*t;if(Painter.shapes.fillCircle(0,i,e,this.headColor),Painter.shapes.strokeCircle(0,i,e,this.stroke,this.lineWidth),Painter.lines.line(0,s,0,n,this.stroke,this.lineWidth),Painter.lines.line(-r,a,r,a,this.stroke,this.lineWidth),Painter.lines.line(0,n,-o,h,this.stroke,this.lineWidth),Painter.lines.line(0,n,o,h,this.stroke,this.lineWidth),this.showJoints){[[0,s],[-r,a],[r,a],[0,n],[-o,h],[o,h]].forEach(([t,e])=>Painter.shapes.fillCircle(t,e,l,this.jointColor))}}getBounds(){const t=100*this.scale,e=40*this.scale;return{x:this.x,y:this.y,width:e,height:t}}}class Ring extends Shape{constructor(t,e,i={}){super(i),this.outerRadius=t,this.innerRadius=e}draw(){super.draw(),Painter.lines.beginPath(),Painter.shapes.arc(0,0,this.outerRadius,0,2*Math.PI),Painter.shapes.arc(0,0,this.innerRadius,0,2*Math.PI,!0),Painter.lines.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}}class Polygon extends Shape{constructor(t=6,e=40,i={}){super(i),this.sides=t,this.radius=e}draw(){super.draw();const t=[],e=2*Math.PI/this.sides;for(let i=0;i<this.sides;i++){const s=i*e;t.push({x:Math.cos(s)*this.radius,y:Math.sin(s)*this.radius})}Painter.shapes.polygon(t,this.color,this.stroke,this.lineWidth)}}class Arrow extends Shape{constructor(t,e={}){super(e),this.length=t}draw(){super.draw();const t=this.width/2,e=.4*this.length,i=this.length-e;Painter.lines.beginPath(),Painter.lines.moveTo(-i/2,-t),Painter.lines.lineTo(i/2,-t),Painter.lines.lineTo(i/2,-this.width),Painter.lines.lineTo(this.length/2,0),Painter.lines.lineTo(i/2,this.width),Painter.lines.lineTo(i/2,t),Painter.lines.lineTo(-i/2,t),Painter.lines.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}}class Pin extends Shape{constructor(t=20,e={}){super(e),this.radius=t}draw(){super.draw();const t=this.radius,e=2.5*t;Painter.lines.beginPath(),Painter.shapes.arc(0,0,t,Math.PI,0),Painter.lines.lineTo(t,0),Painter.lines.lineTo(0,e),Painter.lines.lineTo(-t,0),Painter.lines.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}getBounds(){return{x:this.x,y:this.y+.98*this.radius,width:2*this.radius,height:2.5*this.radius}}}class PieSlice extends Shape{constructor(t,e,i,s={}){super(s),this.radius=t,this.startAngle=e,this.endAngle=i}draw(){super.draw(),Painter.lines.beginPath(),Painter.lines.moveTo(0,0),Painter.shapes.arc(0,0,this.radius,this.startAngle,this.endAngle),Painter.lines.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}}class Hexagon extends Shape{constructor(t,e={}){super(e),this.radius=t}draw(){super.draw();const t=Array.from({length:6},(t,e)=>{const i=Math.PI/3*e;return{x:Math.cos(i)*this.radius,y:Math.sin(i)*this.radius}});Painter.shapes.polygon(t,this.color,this.stroke,this.lineWidth)}}class Heart extends Shape{constructor(t={}){super(t)}draw(){super.draw();const t=this.width,e=this.height,i=.3*e,s=Painter.lines;s.beginPath(),s.moveTo(0,i),s.bezierCurveTo(0,0,-t/2,0,-t/2,i),s.bezierCurveTo(-t/2,.8*e,0,e,0,e),s.bezierCurveTo(0,e,t/2,.8*e,t/2,i),s.bezierCurveTo(t/2,0,0,0,0,i),s.closePath(),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}getBounds(){return{x:this.x,y:this.y+this.height/2,width:this.width,height:this.height}}}class Cross extends Shape{constructor(t,e,i={}){super(i),this.size=t,this.thickness=e,this.diagonal=i.diagonal||!1}draw(){super.draw();const t=this.size/2,e=this.thickness/2;this.diagonal?(Painter.lines.beginPath(),Painter.lines.moveTo(-t,-t+e),Painter.lines.lineTo(-t+e,-t),Painter.lines.lineTo(0,-e),Painter.lines.lineTo(t-e,-t),Painter.lines.lineTo(t,-t+e),Painter.lines.lineTo(e,0),Painter.lines.lineTo(t,t-e),Painter.lines.lineTo(t-e,t),Painter.lines.lineTo(0,e),Painter.lines.lineTo(-t+e,t),Painter.lines.lineTo(-t,t-e),Painter.lines.lineTo(-e,0),Painter.lines.closePath()):(Painter.lines.beginPath(),Painter.lines.moveTo(-e,-t),Painter.lines.lineTo(e,-t),Painter.lines.lineTo(e,-e),Painter.lines.lineTo(t,-e),Painter.lines.lineTo(t,e),Painter.lines.lineTo(e,e),Painter.lines.lineTo(e,t),Painter.lines.lineTo(-e,t),Painter.lines.lineTo(-e,e),Painter.lines.lineTo(-t,e),Painter.lines.lineTo(-t,-e),Painter.lines.lineTo(-e,-e),Painter.lines.closePath()),this.color&&Painter.colors.fill(this.color),this.stroke&&Painter.colors.stroke(this.stroke,this.lineWidth)}}class TextShape extends Shape{constructor(t,e={}){super(e),this._text=t,this._font=e.font||"12px monospace",this._color=e.color||"yellow",this._align=e.align||"center",this._baseline=e.baseline||"middle",this._calculateBounds(),this._calculateAlignmentOffsets()}draw(){super.draw(),this.logger.log("draw",this.font,this.color,this.opacity),Painter.text.setFont(this.font),Painter.text.setTextAlign(this.align),Painter.text.setTextBaseline(this.baseline),Painter.text.fillText(this.text,0,0,this.color)}_calculateAlignmentOffsets(){if(!Painter.text)return;const t=Painter.text.measureTextDimensions(this.text,this.font);switch(this._align){case"left":this._centerOffsetX=t.width/2;break;case"center":this._centerOffsetX=0;break;case"right":this._centerOffsetX=-t.width/2-5}switch(this._baseline){case"top":this._centerOffsetY=t.height/4;break;case"middle":this._centerOffsetY=-2;break;case"bottom":this._centerOffsetY=-t.height}}getTextBounds(){if(Painter.text){const t=Painter.text.measureTextDimensions(this.text,this.font),e=2;return{x:this._centerOffsetX-t.width/2,y:this._centerOffsetY-t.height/2,width:t.width+2*e,height:t.height+2*e}}return{x:this._centerOffsetX,y:this._centerOffsetY,width:this._width,height:this._height}}_calculateBounds(){if(Painter.text){const t=Painter.text.measureTextDimensions(this.text,this.font);this._width=t.width,this._height=t.height,this._calculateAlignmentOffsets()}else this._width=this.text?8*this.text.length:0,this._height=16;this.trace("TextShape.calculateBounds: "+this._width+"x"+this._height)}getDebugBounds(){const t=this.getTextBounds();return{x:t.x,y:t.y,width:t.width,height:t.height}}checkDirty(t,e){t!==e&&(this._boundsDirty=!0,this._calculateBounds())}get text(){return this._text}set text(t){this.checkDirty(t,this._text),this._text=t}get font(){return this._font}set font(t){this.checkDirty(t,this._font),this._font=t}get color(){return this._color}set color(t){this._color=t}get align(){return this._align}set align(t){this.checkDirty(t,this._align),this._align=t}get baseline(){return this._baseline}set baseline(t){this.checkDirty(t,this._baseline),this._baseline=t}}class OutlinedText extends Shape{constructor(t,e,i,s={}){super(t,e,s),this.text=i,this.centered=s.centered||!1,this.color=s.color||"#000000",this.stroke=s.stroke||"#FFFFFF",this.lineWidth=s.lineWidth||1,this.font=s.font||null,this.align=s.align||"left",this.baseline=s.baseline||"alphabetic",this.calculateDimensions()}calculateDimensions(){if(!Painter.ctx)return console.warn("Painter context not initialized. Cannot calculate text dimensions."),this.width=0,void(this.height=0);const t=Painter.text.font();this.font&&Painter.text.setFont(this.font);const e=Painter.text.measureText(this.text);if(this.width=e.width,this.font){const t=parseInt(this.font);this.height=isNaN(t)?20:t}else this.height=e.actualBoundingBoxAscent+e.actualBoundingBoxDescent||20;this.width+=2*this.lineWidth,this.height+=2*this.lineWidth,Painter.text.setFont(t)}setText(t){this.text=t,this.calculateDimensions()}draw(){if(super.draw(),!Painter.ctx)return void console.warn("Painter context not initialized. Cannot draw text.");let t=0;this.font&&Painter.text.setFont(this.font),Painter.text.setTextAlign(this.align),Painter.text.setTextBaseline(this.baseline),this.centered&&("middle"===this.baseline||"alphabetic"===this.baseline?t=0:"top"===this.baseline?t=this.height/2:"bottom"===this.baseline&&(t=-this.height/2)),Painter.outlinedText(this.text,0,t,this.color,this.stroke,this.lineWidth,this.font)}getBounds(){if(!Painter.ctx)return super.getBounds();const t=Painter.text.font();Painter.text.setFont(this.font);const e=Painter.text.measureText(this.text),i=e.width,s=e.actualBoundingBoxAscent+e.actualBoundingBoxDescent||parseInt(this.font)||20;return Painter.text.setFont(t),this.width=i,this.height=s,{x:this.x,y:this.y,width:i,height:s}}}class WrappedText extends Shape{constructor(t,e,i,s,n=20,a={}){super(t,e,a),this.text=i,this.maxWidth=s,this.lineHeight=n,this.centered=a.centered||!1,this.color=a.color||"#000000",this.font=a.font||null,this.align=a.align||"left",this.baseline=a.baseline||"top",this.outlineColor=a.outlineColor||null,this.outlineWidth=a.outlineWidth||1,this.calculateDimensions()}calculateDimensions(){if(!Painter.ctx)return console.warn("Painter context not initialized. Cannot calculate text dimensions."),this.width=this.maxWidth,this.height=this.lineHeight,void(this.lines=[this.text]);const t=Painter.text.font(),e=Painter.text.textAlign(),i=Painter.text.textBaseline();this.font&&Painter.text.setFont(this.font),Painter.text.setTextAlign("left"),Painter.text.setTextBaseline("top");const s=this.text.split(" ");let n="",a="";this.lines=[],this.width=0;for(let t=0;t<s.length;t++){a=n+s[t]+" ";Painter.text.measureText(a).width>this.maxWidth&&t>0?(this.lines.push(n),this.width=Math.max(this.width,Painter.text.measureText(n).width),n=s[t]+" "):n=a}this.lines.push(n),this.width=Math.max(this.width,Painter.text.measureText(n).width),this.height=this.lines.length*this.lineHeight,Painter.text.setFont(t),Painter.text.setTextAlign(e),Painter.text.setTextBaseline(i)}setText(t){this.text=t,this.calculateDimensions()}draw(){if(super.draw(),!Painter.ctx)return void console.warn("Painter context not initialized. Cannot draw text.");let t=0,e=0;this.centered&&(t=-this.width/2,e=-this.height/2),this.font&&Painter.text.setFont(this.font),Painter.text.setTextAlign(this.align),Painter.text.setTextBaseline(this.baseline);let i=t;"center"===this.align?i=0:"right"===this.align&&(i=t+this.width);for(let t=0;t<this.lines.length;t++){const s=e+t*this.lineHeight;this.outlineColor?Painter.outlinedText(this.lines[t],i,s,this.color,this.outlineColor,this.outlineWidth,this.font):Painter.text.fillText(this.lines[t],i,s,this.color,this.font)}}getBounds(){return this.centered?{x:this.x,y:this.y,width:this.width,height:this.height}:{x:this.x+this.width/2,y:this.y+this.height/2,width:this.width,height:this.height}}}class ImageShape extends Shape{constructor(t,e={}){if(!t&&!e.width&&!e.height)throw new Error("ImageShape must be initialized with either a bitmap or width and height");super(e),this._bitmap=t??Painter.img.createImageData(e.width,e.height),this._width=e.width??(null==t?void 0:t.width)??0,this._height=e.height??(null==t?void 0:t.height)??0,this.anchor=e.anchor??"center",this._anchorX=.5,this._anchorY=.5,this._updateAnchorOffsets(),this.smoothing=!1!==e.smoothing,t instanceof ImageData&&this.buffer(t)}_updateAnchorOffsets(){var t;const e=(null==(t=this.anchor)?void 0:t.toLowerCase())??"center";e.includes("left")?this._anchorX=0:e.includes("right")?this._anchorX=1:this._anchorX=.5,e.includes("top")?this._anchorY=0:e.includes("bottom")?this._anchorY=1:this._anchorY=.5}get bitmap(){return this._bitmap}set bitmap(t){t&&(this._bitmap=t,!this._width&&t.width&&(this._width=t.width),!this._height&&t.height&&(this._height=t.height),t instanceof ImageData&&this.buffer(t))}buffer(t){if(!t)return;this._buffer||(this._buffer=document.createElement("canvas")),this._buffer.width===t.width&&this._buffer.height===t.height||(this._buffer.width=t.width,this._buffer.height=t.height);this._buffer.getContext("2d").putImageData(t,0,0)}reset(){this._buffer=null,this._bitmap=Painter.img.createImageData(this.width,this.height)}setAnchor(t){this.anchor=t,this._updateAnchorOffsets()}draw(){if(!this.visible)return;if(!this._bitmap&&!this._buffer)return;super.draw();let t=this._bitmap instanceof ImageData?this._buffer:this._bitmap;(!t||this._bitmap instanceof ImageData&&!this._buffer)&&(this._bitmap instanceof ImageData&&(this.buffer(this._bitmap),t=this._buffer),!t)||Painter.img.draw(t,0,0,{width:this.width,height:this.height,anchor:this.anchor,rotation:this.rotation,scaleX:this.scaleX,scaleY:this.scaleY,alpha:this.opacity,smoothing:this.smoothing,flipX:this.scaleX<0,flipY:this.scaleY<0})}calculateBounds(){return{x:-this._anchorX*this.width,y:-this._anchorY*this.height,width:this.width,height:this.height}}}class EventEmitter{constructor(){this.listeners={}}on(t,e){this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)}off(t,e){this.listeners[t]&&(this.listeners[t]=this.listeners[t].filter(t=>t!==e))}emit(t,e){this.listeners[t]&&this.listeners[t].forEach(t=>t(e))}}class Input{static init(t){Input.game=t,Input.x=0,Input.y=0,Input.down=!1,t.events.on("mousedown",e=>Input._onDown(e,t)),t.events.on("mouseup",e=>Input._onUp(e,t)),t.events.on("mousemove",e=>Input._onMove(e,t)),t.events.on("touchstart",e=>Input._onTouchStart(e,t)),t.events.on("touchend",e=>Input._onTouchEnd(e,t)),t.events.on("touchmove",e=>Input._onTouchMove(e,t))}static _scaleToCanvas(t,e,i){const s=t.canvas,n=s.getBoundingClientRect();return{x:e*(s.width/n.width),y:i*(s.height/n.height)}}static _setPosition(t,e){Input.x=t,Input.y=e}static _onDown(t,e){Input.down=!0;const i=Input._scaleToCanvas(e,t.offsetX,t.offsetY);Input._setPosition(i.x,i.y),Object.defineProperty(t,"x",{value:i.x,configurable:!0}),Object.defineProperty(t,"y",{value:i.y,configurable:!0}),e.events.emit("inputdown",t)}static _onUp(t,e){Input.down=!1;const i=Input._scaleToCanvas(e,t.offsetX,t.offsetY);Input._setPosition(i.x,i.y),Object.defineProperty(t,"x",{value:i.x,configurable:!0}),Object.defineProperty(t,"y",{value:i.y,configurable:!0}),e.events.emit("inputup",t)}static _onMove(t,e){const i=Input._scaleToCanvas(e,t.offsetX,t.offsetY);Input._setPosition(i.x,i.y),Object.defineProperty(t,"x",{value:i.x,configurable:!0}),Object.defineProperty(t,"y",{value:i.y,configurable:!0}),e.events.emit("inputmove",t)}static _onTouchStart(t,e){const i=t.touches[0],s=e.canvas.getBoundingClientRect();Input.down=!0;const n=i.clientX-s.left,a=i.clientY-s.top,r=Input._scaleToCanvas(e,n,a);Input._setPosition(r.x,r.y),Object.defineProperty(t,"x",{value:r.x,configurable:!0}),Object.defineProperty(t,"y",{value:r.y,configurable:!0}),e.events.emit("inputdown",t)}static _onTouchEnd(t,e){Input.down=!1,e.events.emit("inputup",t)}static _onTouchMove(t,e){const i=t.touches[0],s=e.canvas.getBoundingClientRect(),n=i.clientX-s.left,a=i.clientY-s.top,r=Input._scaleToCanvas(e,n,a);Input._setPosition(r.x,r.y),Object.defineProperty(t,"x",{value:r.x,configurable:!0}),Object.defineProperty(t,"y",{value:r.y,configurable:!0}),e.events.emit("inputmove",t)}}const _Mouse=class t{static init(e){t._gameMap.set(e.canvas,e),t.game=e,t.canvas=e.canvas,t.x=0,t.y=0,t.leftDown=!1,t.middleDown=!1,t.rightDown=!1,e.canvas.addEventListener("mousemove",t._onMove),e.canvas.addEventListener("mousedown",t._onDown),e.canvas.addEventListener("mouseup",t._onUp),e.canvas.addEventListener("click",t._onClick),e.canvas.addEventListener("wheel",t._onWheel)}static _getGameForEvent(e){const i=e.currentTarget;return t._gameMap.get(i)||t.game}static _updatePosition(e,i){const s=i.canvas,n=s.getBoundingClientRect(),a=e.clientX-n.left,r=e.clientY-n.top,o=s.width/n.width,h=s.height/n.height;t.x=a*o,t.y=r*h}};__publicField(_Mouse,"_gameMap",new Map),__publicField(_Mouse,"_onMove",t=>{const e=_Mouse._getGameForEvent(t);_Mouse._updatePosition(t,e),e.events.emit("mousemove",t)}),__publicField(_Mouse,"_onDown",t=>{const e=_Mouse._getGameForEvent(t);_Mouse._updatePosition(t,e),0===t.button&&(_Mouse.leftDown=!0),1===t.button&&(_Mouse.middleDown=!0),2===t.button&&(_Mouse.rightDown=!0),e.events.emit("mousedown",t)}),__publicField(_Mouse,"_onUp",t=>{const e=_Mouse._getGameForEvent(t);_Mouse._updatePosition(t,e),0===t.button&&(_Mouse.leftDown=!1),1===t.button&&(_Mouse.middleDown=!1),2===t.button&&(_Mouse.rightDown=!1),e.events.emit("mouseup",t)}),__publicField(_Mouse,"_onClick",t=>{const e=_Mouse._getGameForEvent(t);_Mouse._updatePosition(t,e),t.canvasX=_Mouse.x,t.canvasY=_Mouse.y,Object.defineProperty(t,"x",{value:_Mouse.x,writable:!1}),Object.defineProperty(t,"y",{value:_Mouse.y,writable:!1}),e.events.emit("click",t)}),__publicField(_Mouse,"_onWheel",t=>{const e=_Mouse._getGameForEvent(t);_Mouse._updatePosition(t,e),e.events.emit("wheel",t)});let Mouse=_Mouse;const _Keys=class t{static init(e){t.game=e,window.addEventListener("keydown",t._onKeyDown),window.addEventListener("keyup",t._onKeyUp)}static isDown(e){return t._down.has(e)}static _onKeyDown(e){const i=t._codeMap[e.code];i&&(t._down.has(i)||(t._down.add(i),t.game.events.emit(i,e))),t.game.events.emit(e.type,e)}static _onKeyUp(e){const i=t._codeMap[e.code];i&&t._down.has(i)&&(t._down.delete(i),t.game.events.emit(i+"_up",e)),t.game.events.emit(e.type,e)}};__publicField(_Keys,"W","W"),__publicField(_Keys,"A","A"),__publicField(_Keys,"S","S"),__publicField(_Keys,"D","D"),__publicField(_Keys,"Q","Q"),__publicField(_Keys,"E","E"),__publicField(_Keys,"R","R"),__publicField(_Keys,"F","F"),__publicField(_Keys,"Z","Z"),__publicField(_Keys,"C","C"),__publicField(_Keys,"UP","UP"),__publicField(_Keys,"DOWN","DOWN"),__publicField(_Keys,"LEFT","LEFT"),__publicField(_Keys,"RIGHT","RIGHT"),__publicField(_Keys,"SPACE","SPACE"),__publicField(_Keys,"SHIFT","SHIFT"),__publicField(_Keys,"ENTER","ENTER"),__publicField(_Keys,"ESC","ESC"),__publicField(_Keys,"_codeMap",{KeyW:_Keys.W,KeyA:_Keys.A,KeyS:_Keys.S,KeyD:_Keys.D,KeyQ:_Keys.Q,KeyE:_Keys.E,KeyR:_Keys.R,KeyF:_Keys.F,KeyZ:_Keys.Z,KeyC:_Keys.C,ArrowUp:_Keys.UP,ArrowDown:_Keys.DOWN,ArrowLeft:_Keys.LEFT,ArrowRight:_Keys.RIGHT,Space:_Keys.SPACE,ShiftLeft:_Keys.SHIFT,ShiftRight:_Keys.SHIFT,Enter:_Keys.ENTER,NumpadEnter:_Keys.ENTER,Escape:_Keys.ESC}),__publicField(_Keys,"_down",new Set),__publicField(_Keys,"game",null);let Keys=_Keys;const _Touch=class t{static init(e){t._gameMap.set(e.canvas,e),t.game=e,t.canvas=e.canvas,t.x=0,t.y=0,t.active=!1,e.canvas.addEventListener("touchstart",t._onStart),e.canvas.addEventListener("touchend",t._onEnd),e.canvas.addEventListener("touchmove",t._onMove)}static _getGameForEvent(e){const i=e.currentTarget;return t._gameMap.get(i)||t.game}static _updatePosition(e,i){const s=i.canvas,n=s.getBoundingClientRect(),a=e.clientX-n.left,r=e.clientY-n.top,o=s.width/n.width,h=s.height/n.height;t.x=a*o,t.y=r*h}};__publicField(_Touch,"_gameMap",new Map),__publicField(_Touch,"_onStart",t=>{if(t.touches.length>0){const e=_Touch._getGameForEvent(t);_Touch.active=!0,_Touch._updatePosition(t.touches[0],e),e.events.emit("touchstart",t)}}),__publicField(_Touch,"_onEnd",t=>{const e=_Touch._getGameForEvent(t);_Touch.active=!1,e.events.emit("touchend",t)}),__publicField(_Touch,"_onMove",t=>{if(t.touches.length>0){const e=_Touch._getGameForEvent(t);_Touch._updatePosition(t.touches[0],e),e.events.emit("touchmove",t)}});let Touch=_Touch;function applyAnchor(t,e={}){var i;if(!(t&&t instanceof GameObject))return console.warn("applyAnchor can only be applied to GameObject instances"),t;t._anchor={position:e.anchor??null,margin:e.anchorMargin??10,offsetX:e.anchorOffsetX??0,offsetY:e.anchorOffsetY??0,relative:e.anchorRelative??!1,setTextAlign:!1!==e.anchorSetTextAlign,lastUpdate:0};const s=null==(i=t.update)?void 0:i.bind(t);return t.update=function(e){const i=!0===t._anchor.relative&&t.parent?t.parent:t._anchor.relative;if(t._anchor.position&&(t.boundsDirty||i&&i.boundsDirty||t.parent&&t.parent.boundsDirty)){let e,s,a;if(i){const s={x:i.x,y:i.y,width:i.width,height:i.height};e=Position.calculate(t._anchor.position,t,s,t._anchor.margin,t._anchor.offsetX,t._anchor.offsetY)}else e=Position.calculateAbsolute(t._anchor.position,t,t.game,t._anchor.margin,t._anchor.offsetX,t._anchor.offsetY);!t.parent||(n=t).game&&n.game.pipeline&&n.game.pipeline.gameObjects&&n.game.pipeline.gameObjects.includes(n)?(s=e.x,a=e.y):i===t.parent?(s=e.x-i.x,a=e.y-i.y):(s=e.x-t.parent.x,a=e.y-t.parent.y),t.transform&&"function"==typeof t.transform.position?t.transform.position(s,a):(t.x=s,t.y=a),t._anchor.setTextAlign&&("align"in t&&(t.align=e.align),"baseline"in t&&(t.baseline=e.baseline)),t._anchor.lastUpdate=t.game?t.game.lastTime:Date.now()}var n;s&&s(e)},t}class GameObject extends Transformable{constructor(t,e={}){super(e),this.game=t,this.parent=null,this.events=new EventEmitter,this._interactive=e.interactive??!1,this._hovered=!1,e.anchor&&applyAnchor(this,e)}update(t){this.logger.groupCollapsed("GameObject.update: "+(null==this.name?this.constructor.name:this.name)),super.update(t),this.logger.groupEnd()}get interactive(){return this._interactive}set interactive(t){const e=Boolean(t);this._interactive!==e&&(this._interactive=e,!0===e?this._enableEvents():(this._disableEvents(),this._hovered&&(this._hovered=!1,this.events.emit("mouseout"))))}_enableEvents(){this.logger.log(`${this.constructor.name} is now interactive`)}_disableEvents(){this.logger.log(`${this.constructor.name} is no longer interactive`)}get hovered(){return this._hovered}set hovered(t){this._hovered=Boolean(t)}_setHovered(t){this._hovered=Boolean(t)}_hitTest(t,e){var i;if(!this._interactive)return!1;const s=null==(i=this.getBounds)?void 0:i.call(this);if(!s)return!1;let n=t,a=e;const r=[];let o=this;for(;o;)r.unshift(o),o=o.parent;for(const t of r){if(n-=t.x||0,a-=t.y||0,t.rotation){const e=Math.cos(-t.rotation),i=Math.sin(-t.rotation),s=n;n=s*e-a*i,a=s*i+a*e}void 0!==t.scaleX&&0!==t.scaleX&&(n/=t.scaleX),void 0!==t.scaleY&&0!==t.scaleY&&(a/=t.scaleY)}const h=(s.width||this.width||0)/2,l=(s.height||this.height||0)/2;return n>=-h&&n<=h&&a>=-l&&a<=l}on(t,e){this.events.on(t,e)}off(t,e){this.events.off(t,e)}emit(t,...e){this.events.emit(t,...e)}}function applyDraggable(t,e={}){const i=t.game;return t.dragging=!1,t.dragOffset={x:0,y:0},t._dragInputMoveHandler&&i.events.off("inputmove",t._dragInputMoveHandler),t._dragInputUpHandler&&i.events.off("inputup",t._dragInputUpHandler),"function"==typeof t.enableInteractivity?t.enableInteractivity(t):t.interactive=!0,t._dragInputDownHandler=i=>{t.dragging=!0,t.dragOffset.x=t.x-i.x,t.dragOffset.y=t.y-i.y,e.onDragStart&&e.onDragStart()},t._dragInputMoveHandler=e=>{t.dragging&&(t.x=e.x+t.dragOffset.x,t.y=e.y+t.dragOffset.y)},t._dragInputUpHandler=i=>{t.dragging&&(t.dragging=!1,e.onDragEnd&&e.onDragEnd())},t.on("inputdown",t._dragInputDownHandler),i.events.on("inputmove",t._dragInputMoveHandler),i.events.on("inputup",t._dragInputUpHandler),()=>{t.off("inputdown",t._dragInputDownHandler),i.events.off("inputmove",t._dragInputMoveHandler),i.events.off("inputup",t._dragInputUpHandler),delete t._dragInputDownHandler,delete t._dragInputMoveHandler,delete t._dragInputUpHandler,delete t.dragging,delete t.dragOffset}}class ShapeGOFactory{static create(t,e,i={}){const s={x:(null==e?void 0:e.x)??0,y:(null==e?void 0:e.y)??0,width:(null==e?void 0:e.width)??0,height:(null==e?void 0:e.height)??0,rotation:(null==e?void 0:e.rotation)??0,scaleX:(null==e?void 0:e.scaleX)??1,scaleY:(null==e?void 0:e.scaleY)??1,opacity:(null==e?void 0:e.opacity)??1,visible:(null==e?void 0:e.visible)??!0,active:!0,debug:(null==e?void 0:e.debug)??!1,color:(null==e?void 0:e.color)??null,stroke:(null==e?void 0:e.stroke)??null,lineWidth:(null==e?void 0:e.lineWidth)??1,lineJoin:(null==e?void 0:e.lineJoin)??"miter",lineCap:(null==e?void 0:e.lineCap)??"butt",miterLimit:(null==e?void 0:e.miterLimit)??10,...i,name:i.name??(null==e?void 0:e.constructor.name)??"ShapeWrapper"};return new GameObjectShapeWrapper(t,e,s)}}class GameObjectShapeWrapper extends GameObject{constructor(t,e,i={}){if(super(t,i),!e||null==e||null==e)throw new Error("GameObjectShapeWrapper requires a shape");this.shape=e,void 0!==i.color&&(e.color=i.color),void 0!==i.stroke&&(e.stroke=i.stroke),void 0!==i.lineWidth&&(e.lineWidth=i.lineWidth),void 0!==i.lineJoin&&(e.lineJoin=i.lineJoin),void 0!==i.lineCap&&(e.lineCap=i.lineCap),void 0!==i.miterLimit&&(e.miterLimit=i.miterLimit),this.syncPropertiesToShape(),this.logger.log(`Created GameObject(${this.constructor.name}):`,{x:this.x,y:this.y,width:this.width,height:this.height,color:this.color,stroke:this.stroke})}syncPropertiesToShape(){if(!this.shape)return;const t=["width","height","rotation","scaleX","scaleY","visible","debug","debugColor"];for(const e of t)e in this&&e in this.shape&&this[e]!==this.shape[e]&&(this.shape[e]=this[e])}get color(){return this.shape?this.shape.color:null}set color(t){this.shape&&(this.shape.color=t)}get stroke(){return this.shape?this.shape.stroke:null}set stroke(t){this.shape&&(this.shape.stroke=t)}get lineWidth(){return this.shape?this.shape.lineWidth:1}set lineWidth(t){this.shape&&(this.shape.lineWidth=t)}get lineJoin(){return this.shape?this.shape.lineJoin:"miter"}set lineJoin(t){this.shape&&(this.shape.lineJoin=t)}get lineCap(){return this.shape?this.shape.lineCap:"butt"}set lineCap(t){this.shape&&(this.shape.lineCap=t)}get miterLimit(){return this.shape?this.shape.miterLimit:10}set miterLimit(t){this.shape&&(this.shape.miterLimit=t)}update(t){var e;this.active&&(null==(e=this.onUpdate)||e.call(this,t),(this._boundsDirty||this.tweening)&&(this.syncPropertiesToShape(),this._boundsDirty=!1),super.update(t))}draw(){super.draw(),this.shape.render()}}class Scene extends GameObject{constructor(t,e={}){super(t,e),this._collection=new ZOrderedCollection({sortByZIndex:e.sortByZIndex||!0}),this._collection._owner=this,this._width=e.width??0,this._height=e.height??0,this.forceWidth=null,this.forceHeight=null,this._naturalWidth=null,this._naturalHeight=null,this.userDefinedDimensions=!1,null!=e.width&&null!=e.height&&(this.userDefinedWidth=e.width,this.userDefinedHeight=e.height,this.userDefinedDimensions=!0)}update(t){this.logger.groupCollapsed("Scene.update: "+(null==this.name?this.constructor.name:this.name));for(let e=0;e<this.children.length;e++){const i=this.children[e];i.active&&i.update&&i.update(t)}super.update(t),this.logger.groupEnd()}add(t){if(null==t||null==t)throw new Error("GameObject is null or undefined");return null!=t.parent&&console.warn("This GameObject already has a parent. Consider removing it first."),t.parent=this,this._collection.add(t),this.markBoundsDirty(),t.init&&t.init(),t}markBoundsDirty(){super.markBoundsDirty(),this.children.forEach(t=>{t.markBoundsDirty()})}remove(t){const e=this._collection.remove(t);return e&&(t.parent=null,this.markBoundsDirty()),e}draw(){super.draw(),this.logger.log("Scene.draw chilren:"),this._collection.getSortedChildren().filter(t=>t.visible).map(function(t){return Painter.save(),t.render(),Painter.restore(),t})}getDebugBounds(){return{width:this.width,height:this.height,x:-this.width/2,y:-this.height/2}}getBounds(){return{x:this.x,y:this.y,width:this._width||0,height:this._height||0}}bringToFront(t){return this._collection.bringToFront(t)}sendToBack(t){return this._collection.sendToBack(t)}bringForward(t){return this._collection.bringForward(t)}sendBackward(t){return this._collection.sendBackward(t)}clear(){return this._collection.children.forEach(t=>this.remove(t)),this._collection.clear()}get children(){return this._collection.children}}class Scene3D extends Scene{constructor(t,e={}){if(super(t,e),!e.camera)throw new Error("Scene3D requires a camera option");this.camera=e.camera,this.depthSort=e.depthSort??!0,this.scaleByDepth=e.scaleByDepth??!0}render(){if(!this.visible)return;Painter.save(),Painter.translateTo(this.x,this.y);const t=[];for(const e of this._collection.getSortedChildren()){if(!e.visible)continue;const i=e.z??0,s=this.camera.project(e.x,e.y,i);s.z<10-this.camera.perspective||t.push({child:e,x:s.x,y:s.y,z:s.z,scale:s.scale})}this.depthSort&&t.sort((t,e)=>{const i=t.child.zIndex??0,s=e.child.zIndex??0;return i!==s?i-s:e.z-t.z});for(const e of t)Painter.save(),Painter.translateTo(e.x,e.y),this.scaleByDepth&&Painter.ctx.scale(e.scale,e.scale),Painter.translateTo(-e.child.x,-e.child.y),e.child.render(),Painter.restore();Painter.restore()}}class IsometricScene extends Scene{constructor(t,e={}){super(t,e),this.tileWidth=e.tileWidth??64,this.tileHeight=e.tileHeight??this.tileWidth/2,this.depthSort=e.depthSort??!0,this.scaleByDepth=e.scaleByDepth??!1,this.gridSize=e.gridSize??10,this.elevationScale=e.elevationScale??1,this.camera=e.camera??null}setCamera(t){return this.camera=t,this}toIsometric(t,e,i=0){let s=t,n=e;if(this.camera){const i=this.camera.angle,a=Math.cos(i),r=Math.sin(i);s=t*a-e*r,n=t*r+e*a}return{x:(s-n)*(this.tileWidth/2),y:(s+n)*(this.tileHeight/2)-i*this.elevationScale,depth:s+n-.01*i}}fromIsometric(t,e){const i=this.tileWidth/2,s=this.tileHeight/2;let n=(t/i+e/s)/2,a=(e/s-t/i)/2;if(this.camera){const t=-this.camera.angle,e=Math.cos(t),i=Math.sin(t);return{x:n*e-a*i,y:n*i+a*e}}return{x:n,y:a}}update(t){super.update(t),this.camera&&this.camera.update(t)}getTileAt(t,e){const i=this.fromIsometric(t,e);return{x:Math.floor(i.x),y:Math.floor(i.y)}}getDepthScale(t){return.7+Math.abs(t)/this.gridSize*.6}render(){if(!this.visible)return;Painter.save(),Painter.translateTo(this.x,this.y);const t=[];for(const e of this._collection.getSortedChildren()){if(!e.visible)continue;let i;if(void 0!==e.isoDepth)i=e.isoDepth;else{const t=e.z??0;i=e.x+e.y+.05*t}t.push({child:e,depth:i})}this.depthSort&&t.sort((t,e)=>{const i=t.child.zIndex??0,s=e.child.zIndex??0;return i!==s?i-s:t.depth-e.depth});for(const e of t)Painter.save(),e.child.render(),Painter.restore();Painter.restore()}}class PlatformerScene extends Scene{constructor(t,e={}){super(t,e),this.player=e.player??null,this._layers=[],this.gravity=e.gravity??1200,this.groundY=e.groundY??null,this.moveSpeed=e.moveSpeed??300,this.jumpVelocity=e.jumpVelocity??-500,this.autoInput=e.autoInput??!0,this.autoGravity=e.autoGravity??!0,this._viewportWidth=e.viewportWidth??null,this._viewportHeight=e.viewportHeight??null,this.player&&(void 0===this.player.vx&&(this.player.vx=0),void 0===this.player.vy&&(this.player.vy=0),void 0===this.player._grounded&&(this.player._grounded=!0)),e.camera?this.camera=e.camera:this.player?this.camera=new Camera2D({target:this.player,viewportWidth:this._viewportWidth??t.width,viewportHeight:this._viewportHeight??t.height,lerp:.1}):this.camera=null}addLayer(t,e={}){const i={gameObject:t,speed:e.speed??1,offsetX:e.offsetX??0,offsetY:e.offsetY??0};return this._layers.push(i),this.add(t),t}removeLayer(t){const e=this._layers.findIndex(e=>e.gameObject===t);return-1!==e&&(this._layers.splice(e,1),this.remove(t),!0)}getLayers(){return[...this._layers]}isLayer(t){return this._layers.some(e=>e.gameObject===t)}applyGravity(t,e){t.vy=(t.vy||0)+this.gravity*e}applyInput(t,e){let i=0;Keys.isDown(Keys.LEFT)||Keys.isDown(Keys.A)?i=-1:(Keys.isDown(Keys.RIGHT)||Keys.isDown(Keys.D))&&(i=1),t.vx=i*this.moveSpeed;(Keys.isDown(Keys.SPACE)||Keys.isDown(Keys.W)||Keys.isDown(Keys.UP))&&this.isPlayerGrounded()&&(t.vy=this.jumpVelocity,t._grounded=!1)}updateCamera(t){this.camera&&this.camera.update(t)}getCameraOffset(){return this.camera?this.camera.getOffset():{x:0,y:0}}isPlayerGrounded(){var t;return!0===(null==(t=this.player)?void 0:t._grounded)}applyVelocity(t,e){t.x+=(t.vx||0)*e,t.y+=(t.vy||0)*e}handleGroundCollision(t){null!==this.groundY&&t.y>=this.groundY&&(t.y=this.groundY,t.vy=0,t._grounded=!0)}update(t){this.player&&(this.autoGravity&&this.applyGravity(this.player,t),this.autoInput&&this.applyInput(this.player,t),this.applyVelocity(this.player,t),this.handleGroundCollision(this.player)),this.updateCamera(t),super.update(t)}draw(){this.applyTransforms(),this.drawDebug();const t=this._viewportWidth??this.game.width,e=this._viewportHeight??this.game.height,i=this.getCameraOffset();Painter.save(),Painter.ctx.beginPath(),Painter.ctx.rect(-this.x,-this.y,t,e),Painter.ctx.clip(),Painter.ctx.beginPath(),Painter.ctx.translate(-this.x,-this.y);for(const t of this._layers){if(!t.gameObject.visible)continue;Painter.save();const e=i.x*t.speed+(t.offsetX||0),s=i.y*t.speed+(t.offsetY||0);Painter.ctx.translate(-e,-s),t.gameObject.render(),Painter.restore()}for(const t of this._collection.getSortedChildren())t.visible&&(this._layers.some(e=>e.gameObject===t)||(Painter.save(),Painter.ctx.translate(-i.x,-i.y),t.render(),Painter.restore()));Painter.restore()}shakeCamera(t,e){return this.camera&&this.camera.shake(t,e),this}setViewport(t,e){return this._viewportWidth=t,this._viewportHeight=e,this.camera&&(this.camera.viewportWidth=t,this.camera.viewportHeight=e),this}getViewport(){return{width:this._viewportWidth??this.game.width,height:this._viewportHeight??this.game.height}}}class LayoutScene extends Scene{constructor(t,e={}){super(t,e),this.spacing=e.spacing??10,this.padding=e.padding??0,this.autoSize=e.autoSize??!0,this.align=e.align??"start",this.debug=e.debug??!1,this._layoutDirty=!0,this.scrollable=e.scrollable??!1,this.scrollFriction=e.scrollFriction??.92,this.scrollBounce=e.scrollBounce??.3,this.scrollThreshold=e.scrollThreshold??.5,this._viewportWidth=e.viewportWidth??null,this._viewportHeight=e.viewportHeight??null,this._scrollOffset={x:0,y:0},this._scrollVelocity={x:0,y:0},this._scrollDragging=!1,this._scrollDragStart={x:0,y:0},this._scrollDragStartOffset={x:0,y:0},this._lastDragPosition={x:0,y:0},this._lastDragTime=0,this.scrollable&&this._setupScrollInteraction(),this._scrollInitialized=!1}calculateLayout(){throw new Error("Subclasses must implement calculateLayout()")}update(t){if(this._boundsDirty||this._layoutDirty){const t=this.width,e=this.height,i=this.calculateLayout();if(this.autoSize&&i){this._contentWidth=i.width,this._contentHeight=i.height;const t=this.getScrollAxis(),e=this._viewportWidth,s=this._viewportHeight;let n=i.width,a=i.height;this.scrollable&&null!==e&&t.horizontal&&(n=Math.min(i.width,e)),this.scrollable&&null!==s&&t.vertical&&(a=Math.min(i.height,s)),Math.abs(this.width-n)>.1&&(this.width=n),Math.abs(this.height-a)>.1&&(this.height=a)}if(i&&i.positions&&this.applyPositionsToChildren(i.positions),this.scrollable&&!this._scrollInitialized&&this._needsScrolling()){const t=this._getScrollBounds(),e=this.getScrollAxis();e.horizontal&&(this._scrollOffset.x=t.maxX),e.vertical&&(this._scrollOffset.y=t.maxY),this._scrollInitialized=!0}this._boundsDirty=!1,this._layoutDirty=!1,t===this.width&&e===this.height||this._updatingBoundsFromLayout||(this._updatingBoundsFromLayout=!0,Scene.prototype.markBoundsDirty.call(this),this._updatingBoundsFromLayout=!1)}this.scrollable&&!this._scrollDragging&&this._updateScrollMomentum(t),super.update(t)}markBoundsDirty(){this._updatingBoundsFromLayout?this._boundsDirty=!0:(super.markBoundsDirty(),this._layoutDirty=!0)}applyPositionsToChildren(t){applyLayout(this.children,t,this.getLayoutOffset())}getLayoutOffset(){return{offsetX:0,offsetY:0}}add(t){const e=super.add(t);return this._layoutDirty=!0,e}remove(t){const e=super.remove(t);return this._layoutDirty=!0,e}getScrollAxis(){return{horizontal:!1,vertical:!0}}calculateBounds(){if(this.scrollable&&(this._viewportWidth||this._viewportHeight)){const t=this._viewportWidth??this.width,e=this._viewportHeight??this.height;return{x:-t/2,y:-e/2,width:t,height:e}}return super.calculateBounds()}_getScrollBounds(){const t=this._contentWidth??this.width,e=this._contentHeight??this.height,i=this._viewportWidth??t,s=this._viewportHeight??e,n=Math.max(0,t-i),a=Math.max(0,e-s);return{minX:-n/2,maxX:n/2,minY:-a/2,maxY:a/2}}_clampScrollBounds(){const t=this._getScrollBounds(),e=this.getScrollAxis();e.horizontal&&(this._scrollOffset.x<t.minX?(this._scrollOffset.x+=(t.minX-this._scrollOffset.x)*this.scrollBounce,this._scrollVelocity.x=0):this._scrollOffset.x>t.maxX&&(this._scrollOffset.x+=(t.maxX-this._scrollOffset.x)*this.scrollBounce,this._scrollVelocity.x=0)),e.vertical&&(this._scrollOffset.y<t.minY?(this._scrollOffset.y+=(t.minY-this._scrollOffset.y)*this.scrollBounce,this._scrollVelocity.y=0):this._scrollOffset.y>t.maxY&&(this._scrollOffset.y+=(t.maxY-this._scrollOffset.y)*this.scrollBounce,this._scrollVelocity.y=0))}_updateScrollMomentum(t){const e=this.getScrollAxis();e.horizontal&&(this._scrollVelocity.x*=this.scrollFriction,Math.abs(this._scrollVelocity.x)<this.scrollThreshold&&(this._scrollVelocity.x=0),this._scrollOffset.x+=this._scrollVelocity.x*t*60),e.vertical&&(this._scrollVelocity.y*=this.scrollFriction,Math.abs(this._scrollVelocity.y)<this.scrollThreshold&&(this._scrollVelocity.y=0),this._scrollOffset.y+=this._scrollVelocity.y*t*60),this._clampScrollBounds()}_setupScrollInteraction(){this.interactive=!0,this._scrollInputDownHandler=t=>{this._isPointInViewport(t.x,t.y)&&this._onScrollDragStart(t)},this._scrollInputMoveHandler=t=>this._onScrollDragMove(t),this._scrollInputUpHandler=t=>this._onScrollDragEnd(t),this.game.events.on("inputdown",this._scrollInputDownHandler),this.game.events.on("inputmove",this._scrollInputMoveHandler),this.game.events.on("inputup",this._scrollInputUpHandler)}_isPointInViewport(t,e){let i=t-this.x,s=e-this.y,n=this.parent;for(;n;)i-=n.x||0,s-=n.y||0,n=n.parent;const a=(this._viewportWidth??this.width)/2,r=(this._viewportHeight??this.height)/2;return i>=-a&&i<=a&&s>=-r&&s<=r}_onScrollDragStart(t){this._scrollDragging=!0,this._scrollDragStart={x:t.x,y:t.y},this._scrollDragStartOffset={...this._scrollOffset},this._lastDragPosition={x:t.x,y:t.y},this._lastDragTime=performance.now(),this._scrollVelocity={x:0,y:0}}_onScrollDragMove(t){if(!this._scrollDragging)return;const e=this.getScrollAxis(),i=performance.now(),s=Math.max(1,i-this._lastDragTime)/1e3;if(e.horizontal){const e=t.x-this._scrollDragStart.x;this._scrollOffset.x=this._scrollDragStartOffset.x+e,this._scrollVelocity.x=(t.x-this._lastDragPosition.x)/(60*s)}if(e.vertical){const e=t.y-this._scrollDragStart.y;this._scrollOffset.y=this._scrollDragStartOffset.y+e,this._scrollVelocity.y=(t.y-this._lastDragPosition.y)/(60*s)}this._lastDragPosition={x:t.x,y:t.y},this._lastDragTime=i}_onScrollDragEnd(){this._scrollDragging=!1}_needsScrolling(){if(!this.scrollable)return!1;const t=this._contentWidth??this.width,e=this._contentHeight??this.height,i=this._viewportWidth??t,s=this._viewportHeight??e,n=this.getScrollAxis();return!!(n.horizontal&&t>i)||!!(n.vertical&&e>s)}draw(){this._needsScrolling()?this._drawScrollable():super.draw()}_drawScrollable(){this.applyTransforms(),this.drawDebug();const t=this.padding??0,e=(this._viewportWidth??this.width)-2*t,i=(this._viewportHeight??this.height)-2*t;Painter.save(),Painter.ctx.beginPath(),Painter.ctx.rect(-e/2,-i/2,e,i),Painter.ctx.clip(),Painter.ctx.beginPath(),Painter.ctx.translate(this._scrollOffset.x,this._scrollOffset.y),this._collection.getSortedChildren().filter(t=>t.visible).forEach(t=>{Painter.save(),t.render(),Painter.restore()}),Painter.restore()}scrollTo(t,e){const i=this.getScrollAxis();i.horizontal&&(this._scrollOffset.x=t),i.vertical&&(this._scrollOffset.y=e),this._scrollVelocity={x:0,y:0}}scrollBy(t,e){const i=this.getScrollAxis();i.horizontal&&(this._scrollOffset.x+=t),i.vertical&&(this._scrollOffset.y+=e)}getScrollPosition(){return{...this._scrollOffset}}resetScroll(){this._scrollOffset={x:0,y:0},this._scrollVelocity={x:0,y:0}}}class HorizontalLayout extends LayoutScene{getScrollAxis(){return{horizontal:!0,vertical:!1}}calculateLayout(){return horizontalLayout(this.children,{spacing:this.spacing,padding:this.padding,align:this.align,centerItems:!0})}getLayoutOffset(){return{offsetX:-(this._contentWidth??this.width)/2,offsetY:0}}}class VerticalLayout extends LayoutScene{calculateLayout(){return verticalLayout(this.children,{spacing:this.spacing,padding:this.padding,align:this.align,centerItems:!0})}getLayoutOffset(){return{offsetX:0,offsetY:-(this._contentHeight??this.height)/2}}}class TileLayout extends LayoutScene{constructor(t,e={}){super(t,e),this.columns=e.columns??4}calculateLayout(){return this.children.length?tileLayout(this.children,{columns:this.columns,spacing:this.spacing,padding:this.padding,centerItems:!0}):null}getLayoutOffset(){return{offsetX:-(this._contentWidth??this.width)/2,offsetY:-(this._contentHeight??this.height)/2}}}class GridLayout extends LayoutScene{constructor(t,e={}){super(t,e),this.columns=e.columns??4,this.debug=e.debug??!1}calculateLayout(){return this.children.length?gridLayout(this.children,{columns:this.columns,spacing:this.spacing,padding:this.padding,centerItems:this.centerItems,width:this.autoSize?void 0:this.width,height:this.autoSize?void 0:this.height}):null}getLayoutOffset(){return{offsetX:-(this._contentWidth??this.width)/2,offsetY:-(this._contentHeight??this.height)/2}}}class Sprite extends GameObject{constructor(t,e={}){super(t,e),this._frames=[],this._currentFrame=0,this._frameAccumulator=0,this._isPlaying=e.autoPlay||!1,this._loop=void 0===e.loop||e.loop,this._frameRate=e.frameRate||12,this._frameDuration=1/this._frameRate,this._animations=new Map,this._currentAnimation=null,e.frames&&Array.isArray(e.frames)&&e.frames.forEach(t=>this.addFrame(t))}addAnimation(t,e,i={}){if(!t||"string"!=typeof t)throw new Error("Sprite.addAnimation: name is required");if(!e||!Array.isArray(e)||0===e.length)throw new Error("Sprite.addAnimation: frames array is required");return e.forEach(t=>{t.parent=this}),this._animations.set(t,{frames:e,loop:void 0===i.loop||i.loop,frameRate:i.frameRate||null}),this}removeAnimation(t){const e=this._animations.get(t);return!!e&&(e.frames.forEach(t=>{t.parent=null}),this._animations.delete(t),this._currentAnimation===t&&(this._currentAnimation=null,this._frames=[]),!0)}playAnimation(t,e=!1){const i=this._animations.get(t);return i?(this._currentAnimation===t&&this._isPlaying&&!e||(this._currentAnimation=t,this._frames=i.frames,this._loop=i.loop,null!==i.frameRate&&(this._frameRate=i.frameRate,this._frameDuration=1/this._frameRate),this._currentFrame=0,this._frameAccumulator=0,this._isPlaying=!0),this):(console.warn(`Sprite.playAnimation: animation '${t}' not found`),this)}stopAnimation(t){const e=this._animations.get(t);return e?(this._currentAnimation=t,this._frames=e.frames,this._loop=e.loop,this._currentFrame=0,this._frameAccumulator=0,this._isPlaying=!1,this):(console.warn(`Sprite.stopAnimation: animation '${t}' not found`),this)}get currentAnimationName(){return this._currentAnimation}get animationNames(){return Array.from(this._animations.keys())}hasAnimation(t){return this._animations.has(t)}addFrame(t){if(!t)throw new Error("Sprite.addFrame: shape is required");return t.parent=this,this._frames.push(t),this.markBoundsDirty(),this._frames.length-1}removeFrame(t){if(t<0||t>=this._frames.length)return null;const e=this._frames.splice(t,1)[0];return e&&(e.parent=null,this.markBoundsDirty(),this._currentFrame>=this._frames.length&&this._frames.length>0&&(this._currentFrame=this._frames.length-1)),e}clearFrames(){this._frames.forEach(t=>{t.parent=null}),this._frames=[],this._currentFrame=0,this.markBoundsDirty()}get totalFrames(){return this._frames.length}get currentFrame(){return this._currentFrame}get currentShape(){return this._frames[this._currentFrame]||null}get frames(){return this._frames}get isPlaying(){return this._isPlaying}get loop(){return this._loop}set loop(t){this._loop=t}get frameRate(){return this._frameRate}set frameRate(t){if(t<=0)throw new Error("Sprite.frameRate must be greater than 0");this._frameRate=t,this._frameDuration=1/t}play(){return this._isPlaying=!0,this}pause(){return this._isPlaying=!1,this}stop(){return this._isPlaying=!1,this._currentFrame=0,this._frameAccumulator=0,this}rewind(){return this._currentFrame=0,this._frameAccumulator=0,this}goto(t){return 0===this._frames.length||(this._currentFrame=Math.max(0,Math.min(t,this._frames.length-1)),this._frameAccumulator=0),this}gotoAndStop(t){return this.goto(t),this.pause(),this}gotoAndPlay(t){return this.goto(t),this.play(),this}update(t){if(super.update(t),!this._isPlaying||0===this._frames.length)return;for(this._frameAccumulator+=t;this._frameAccumulator>=this._frameDuration;)this._frameAccumulator-=this._frameDuration,this._advanceFrame();const e=this.currentShape;e&&"function"==typeof e.update&&e.update(t)}_advanceFrame(){this._currentFrame++,this._currentFrame>=this._frames.length&&(this._loop?this._currentFrame=0:(this._currentFrame=this._frames.length-1,this._isPlaying=!1))}draw(){super.draw();const t=this.currentShape;t&&!1!==t.visible&&(Painter.save(),t.render(),Painter.restore())}calculateBounds(){if(0===this._frames.length)return{x:this.x,y:this.y,width:0,height:0};let t=1/0,e=1/0,i=-1/0,s=-1/0;return this._frames.forEach(n=>{const a=n.getBounds();t=Math.min(t,a.x),e=Math.min(e,a.y),i=Math.max(i,a.x+a.width),s=Math.max(s,a.y+a.height)}),{x:t+this.x,y:e+this.y,width:i-t,height:s-e}}toString(){return`[Sprite frames=${this.totalFrames} current=${this.currentFrame} playing=${this.isPlaying}]`}}class Text extends GameObjectShapeWrapper{constructor(t,e,i={}){super(t,new TextShape(e,{font:i.font||"16px monospace",color:i.color||"yellow",align:i.align||"left",baseline:i.baseline||"top",strokeColor:i.strokeColor||"#000",lineWidth:i.lineWidth||1,debugColor:i.debugColor||"yellow"}),i),this._textOptions={font:i.font||"16px monospace",color:i.color||"yellow",align:i.align||"left",baseline:i.baseline||"top"}}get text(){return this.shape.text}set text(t){this.shape.text=t,this.markBoundsDirty()}get font(){return this.shape.font}set font(t){this.shape.font=t,this._textOptions.font=t,this.markBoundsDirty()}get color(){return this.shape.color}set color(t){this.shape.color=t,this._textOptions.color=t}get align(){return this.shape.align}set align(t){this.shape.align=t,this._textOptions.align=t,this.markBoundsDirty()}get baseline(){return this.shape.baseline}set baseline(t){this.shape.baseline=t,this._textOptions.baseline=t,this.markBoundsDirty()}measureWidth(){if(!Painter.ctx)return 0;return Painter.text.measureTextWidth(this.text,this.font)}measureHeight(){if(!this.font)return 16;const t=parseInt(this.font);return isNaN(t)?16:t}getBounds(){const t=super.getBounds();if(this.shape.getTextBounds){const t=this.shape.getTextBounds();return{x:this.x,y:this.y,width:t.width,height:t.height}}return t}update(t){super.update(t),this.shape&&(this.width=this.shape.width||this.measureWidth(),this.height=this.shape.height||this.measureHeight())}}class ImageGo extends GameObjectShapeWrapper{constructor(t,e,i={}){super(t,e instanceof ImageShape?e:new ImageShape(e,i),i)}reset(){this.shape.reset()}}class Tween{static lerp(t,e,i){return t+(e-t)*i}static lerpAngle(t,e,i){let s=e-t;for(;s<-Math.PI;)s+=2*Math.PI;for(;s>Math.PI;)s-=2*Math.PI;return t+s*i}static tweenColor(t,e,i){return t.map((t,s)=>Tween.lerp(t,e[s],i))}static tweenGradient(t,e,i){let s=t[0],n=e[0];Math.abs(n-s)>180&&(s<n?s+=360:n+=360);return[Tween.lerp(s,n,i)%360,Tween.lerp(t[1],e[1],i),Tween.lerp(t[2],e[2],i)]}}function bounceV1(t,e,i,s,n,a=!1,r=null,o={},h=null){const{t:l,easedT:c,completed:u,state:d}=Motion._frame(s,n,a,r,o,h),_=1/(i+1),p=Math.min(Math.floor(c/_),i),g=c%_/_,f=t*Math.pow(.6,p),m=e-Math.sin(g*Math.PI)*(e-f);return Motion.animationResult({y:m,segment:p,bounceHeight:f},l,a,u,d)}function floatV1(t,e,i,s,n,a,r=!0,o=null,h={},l=null){if(i<=0)return Motion.animationResult({x:t.x,y:t.y,moving:!1},1,!1,!0);l||(l={initialX:t.x,initialY:t.y,started:!1,completed:!1,loopCount:0});const c=l.initialX,u=l.initialY,{t:d,easedT:_,completed:p,state:g}=Motion._frame(e,i,r,o,h,l);l={...l,...g};const f=e*s,m=Math.max(0,Math.min(1,n)),v=c+(Math.sin(.7*f)+.4*m*Math.sin(2.3*f+.5))*a,y=u+(Math.cos(.9*f)+.4*m*Math.cos(1.9*f+.7))*a,x=.7*Math.cos(.7*f)+.4*m*2.3*Math.cos(2.3*f+.5),b=-.9*Math.sin(.9*f)+.4*m*-1.9*Math.sin(1.9*f+.7),w=Math.sqrt(x*x+b*b),M=w>.8,S=Math.sqrt((v-c)*(v-c)+(y-u)*(y-u));return Motion.animationResult({x:v,y:y,centerX:c,centerY:u,offsetX:v-c,offsetY:y-u,distance:S,moving:M,velocity:w},d,r,p,l)}function followPath(t,e=!1,i,s,n=!1,a=null,r={},o=null){if(!t||t.length<2)return this._createResult({x:0,y:0},0,n,!1);const{t:h,easedT:l,completed:c,state:u}=Motion._frame(i,s,n,a,r,o);if(!o||!o.pathData){const i={segmentLengths:[],totalLength:0,points:[...t]};for(let e=0;e<t.length-1;e++){const s=t[e],n=t[e+1],a=n[0]-s[0],r=n[1]-s[1],o=Math.sqrt(a*a+r*r);i.segmentLengths.push(o),i.totalLength+=o}if(e){const e=t[t.length-1],s=t[0],n=s[0]-e[0],a=s[1]-e[1],r=Math.sqrt(n*n+a*a);i.segmentLengths.push(r),i.totalLength+=r}u.pathData=i}const{segmentLengths:d,totalLength:_,points:p}=u.pathData,g=l*_;let f=0,m=0;for(let t=0;t<d.length;t++){if(f+d[t]>=g){m=t;break}f+=d[t]}const v=(g-f)/d[m],y=p[m],x=m<p.length-1?p[m+1]:p[0],b=Tween.lerp(y[0],x[0],v),w=Tween.lerp(y[1],x[1],v),M=Math.atan2(x[1]-y[1],x[0]-y[0]);return Motion.animationResult({x:b,y:w,angle:M,segmentIndex:m,segmentProgress:v,pathProgress:l},h,n,c,u)}function orbitV1(t,e,i,s,n,a,r,o=!0,h=!0,l=null,c={},u=null){const{t:d,easedT:_,completed:p,state:g}=Motion._frame(a,r,o,l,c,u),f=n+(h?1:-1)*_*Math.PI*2,m=t+i*Math.cos(f),v=e+s*Math.sin(f);return Motion.animationResult({x:m,y:v,angle:f},d,o,p,g)}function oscillateV1(t,e,i,s,n=!0,a=null,r={},o=null){const{t:h,easedT:l,completed:c,state:u}=Motion._frame(i,s,n,a,r,o),d=(e-t)/2,_=t+d+d*Math.sin(l*Math.PI*2);return Motion.animationResult({value:_},h,n,c,u)}function parabolicV1(t,e,i,s,n,a=!1,r=!1,o=null,h={},l=null){l||(l={started:!1,loopCount:0,direction:1,lastDirection:1,completed:!1});let c=n>0?s/n:1,u=!1,d={...h};if(r||a)if(a)if(r){const t=2*n,e=s%t,i=Math.floor(s/t),a=e<n?1:-1;c=1===a?e/n:2-e/n,a!==l.direction&&(l.direction=a,1===l.direction&&d.onLoop&&d.onLoop(i)),i>l.loopCount&&(l.loopCount=i)}else{c%=1;const t=Math.floor(s/n);t>l.loopCount&&d.onLoop&&(d.onLoop(t),l.loopCount=t)}else r&&!a&&(c<=1?l.direction=1:c<=2?(c=2-c,l.direction=-1):(c=0,u=!0,l.direction=1));else c>=1&&(c=1,u=!0);!l.started&&d.onStart&&(d.onStart(),l.started=!0),u&&!l.completed&&d.onComplete&&(d.onComplete(),l.completed=!0);const _=o?o(c):c,p=(t+i-2*e)*_*_+2*(e-t)*_+t,g={...l,lastDirection:l.direction,completed:u||l.completed};return Motion.animationResult({value:p,direction:l.direction},c,a||r&&!u,u,g)}function patrolV1(t,e,i,s,n,a,r=!0,o=null){o||(o={currentX:t,currentY:e,targetX:t,targetY:e,isWaiting:!0,waitStartTime:0,moveStartTime:0,moveCount:0,direction:"idle"});const h=()=>Math.random();let l=o.isWaiting,c=o.currentX,u=o.currentY,d=o.direction;if(l){if(i-o.waitStartTime>=n){l=!1,o.moveStartTime=i;d=["up","down","left","right"][Math.floor(4*h())];let s=o.currentX,n=o.currentY;const r=a*(.2+.6*h());switch(d){case"up":n=o.currentY-r;break;case"down":n=o.currentY+r;break;case"left":s=o.currentX-r;break;case"right":s=o.currentX+r}Math.pow(s-t,2)+Math.pow(n-e,2)>a*a&&("up"===d||"down"===d?(n=e,d=o.currentY>e?"up":"down"):(s=t,d=o.currentX>t?"left":"right")),o.targetX=s,o.targetY=n,o.direction=d,o.moveCount++}}else{const t=(i-o.moveStartTime)/s;t>=1?(l=!0,o.waitStartTime=i,o.currentX=o.targetX,o.currentY=o.targetY,d="idle"):(c=o.currentX+(o.targetX-o.currentX)*t,u=o.currentY+(o.targetY-o.currentY)*t)}o.isWaiting=l,o.direction=d,l||(o.currentX=c,o.currentY=u);const _=s+n,p=i%_/_,g=Math.sqrt(Math.pow(c-t,2)+Math.pow(u-e,2));return Motion.animationResult({x:c,y:u,moving:!l,direction:d,distanceFromCenter:g},p,r,!1,o)}function pendulumV1(t,e,i,s,n=!0,a=!1,r=null,o={},h=null){const{t:l,easedT:c,completed:u,state:d}=Motion._frame(i,s,n,null,o,h),_=a&&!n?Math.exp(-4*l):1;let p=t+e*Math.cos(2*c*Math.PI)*_;if(r){p=t+r(((p-t)/(e*_)+1)/2)*e*_*2-e*_}return Motion.animationResult({angle:p},l,n,u,d)}function pulseV1(t,e,i,s,n=!0,a=!1,r=null,o={}){let h,l=i/s,c="forward";if(n){const t=Math.floor(l);l%=1,t>0&&o.onLoop&&o.onLoop(t)}else l>1&&(l=1);if(l>0&&i<=s&&o.onStart&&o.onStart(),a)if(l<.5){const i=2*l;h=t+(e-t)*(r?r(i):i),c="forward"}else{const i=2*(l-.5);h=e-(e-t)*(r?r(i):i),c="return",l>=.5&&l<.51&&o.onYoyoTurn&&o.onYoyoTurn()}else{const i=r?r(l):l;h=t+(e-t)*(i<.5?2*i:2-2*i)}const u=!n&&l>=1;return u&&o.onComplete&&o.onComplete(),Motion.animationResult({value:h,phase:c},l,n,u)}function hopV1(t,e,i,s,n=!0,a=!0,r=null,o={},h=null){const{t:l,easedT:c,completed:u,state:d}=Motion._frame(i,s,n,r,o,h,a);let _=0;_=n||a?a?Math.sin(c*Math.PI):Math.sin(Math.min(l,1)*Math.PI*.5):u?1:Math.sin(Math.min(l,1)*Math.PI*.5);const p=t-e*_;return Motion.animationResult({y:p},l,n,u,d)}function shakeV1(t,e,i,s,n,a,r,o,h=!1,l=null,c={},u=null){const{t:d,easedT:_,completed:p,state:g}=Motion._frame(r,o,h,l,c,u),f=Math.pow(1-_,a),m=_*Math.PI*2*n,v=_*Math.PI*2*n*1.3,y=f*i*(.6*Math.sin(m)+.3*Math.sin(2.5*m)+.1*Math.sin(5.6*m)),x=f*s*(.6*Math.cos(v)+.3*Math.cos(2.7*v)+.1*Math.cos(6.3*v));let b=t+y,w=e+x;if(_>.9){const i=(_-.9)/.1;b=t+y*(1-i),w=e+x*(1-i)}return Motion.animationResult({x:b,y:w,intensity:f},d,h,p,g)}function spiralV1(t,e,i,s,n,a,r,o,h=!1,l=!1,c=null,u={},d=null){d||(d={started:!1,loopCount:0,direction:1,lastDirection:1});let _=o>0?r/o:1,p=!1,g={...u};if(l||h)if(h)if(l){const t=2*o,e=r%t,i=Math.floor(r/t),s=e<o?1:-1;_=1===s?e/o:2-e/o,s!==d.direction&&(d.direction=s,1===d.direction&&g.onLoop&&g.onLoop(i)),i>d.loopCount&&(d.loopCount=i)}else{_%=1;const t=Math.floor(r/o);t>d.loopCount&&g.onLoop&&(g.onLoop(t),d.loopCount=t)}else l&&!h&&(_<=1?d.direction=1:_<=2?(_=2-_,d.direction=-1):(_=0,p=!0,d.direction=1));else _>=1&&(_=1,p=!0);!d.started&&g.onStart&&(g.onStart(),d.started=!0),p&&!d.completed&&g.onComplete&&(g.onComplete(),d.completed=!0);const f=c?c(_):_,m=Tween.lerp(i,s,f),v=n+f*a*Math.PI*2,y=t+m*Math.cos(v),x=e+m*Math.sin(v),b={...d,lastDirection:d.direction};return Motion.animationResult({x:y,y:x,radius:m,angle:v,direction:d.direction},_,h||l&&!p,p,b)}function springV1(t,e,i,s,n=!1,a=!1,r={},o={}){if(s<=0)return this.animationResult({value:e,velocity:0,done:!0,phase:"complete"},1,!1,!0);let h,l,c,u=i/s,d="forward",_=0;n?(_=Math.floor(u),u%=1,_>0&&o.onLoop&&o.onLoop(_)):u>1&&(u=1),u>0&&i<=s&&o.onStart&&o.onStart(),a?u>=.5?(h=t,l=e,c=2*(u-.5),d="return",u>=.5&&u<.51&&o.onYoyoTurn&&o.onYoyoTurn()):(h=e,l=t,c=2*u,d="forward"):(h=e,l=t,c=u);const p=void 0!==r.stiffness?r.stiffness:.3,g=void 0!==r.damping?r.damping:.6,f=Math.max(.1,1/(1.5*g)),m=Math.max(.1,.8/(1.5*p+.5));let v;if(c<.99)v=Easing.easeOutElastic(c,f,m);else{const t=(c-.99)/.01;v=Easing.easeOutElastic(.99,f,m)*(1-t)+1*t}const y=Tween.lerp(l,h,v),x=Math.min(c+.01,1);let b;if(x<.99)b=Easing.easeOutElastic(x,f,m);else{const t=(x-.99)/.01;b=Easing.easeOutElastic(.99,f,m)*(1-t)+1*t}const w=(Tween.lerp(l,h,b)-y)/.01*s,M=!n&&u>=1;return M&&o.onComplete&&o.onComplete(),Motion.animationResult({value:y,velocity:w,delta:"forward"===d?e-y:t-y,done:M,phase:d},u,n,M)}function swingV1(t,e,i,s,n,a=!0,r=!0,o=null,h={},l=null){const{t:c,easedT:u,completed:d,state:_}=Motion._frame(s,n,a,o,h,l),p=(r?Math.sin(u*Math.PI*2):Math.sin(u*Math.PI))*i;return Motion.animationResult({angle:p},c,a,d,_)}function waypointV1(t,e,i,s,n,a=!0,r={},o=null){if(!i||!Array.isArray(i)||i.length<2)return console.warn("Patrol animation requires at least 2 waypoints"),Motion._createResult({x:0,y:0,moving:!1,direction:"idle",waypoint:0},0,!1,!0);o||(o={currentWaypoint:0,nextWaypoint:1,isWaiting:!0,waitStartTime:0,lastWaypointTime:0,lastWaypointReached:-1,completed:!1});let h=0;for(let t=0;t<i.length;t++){const e=(t+1)%i.length;if(!a&&t===i.length-1)break;const s=i[e][0]-i[t][0],n=i[e][1]-i[t][1];h+=Math.abs(s)+Math.abs(n)}const l=h/s+n*i.length;let c=e;c=a?e%l:Math.min(e,l);const u=c/l;let d,_,p,g=c,f=0,m=1,v=!0,y=0,x=0,b=!1;if(g<n)y=g/n,f=0,m=1,v=!0;else{g-=n;for(let t=0;t<i.length;t++){if(!a&&t===i.length-1){f=t,m=t,v=!0,y=1,b=!0;break}const e=(t+1)%i.length,h=i[e][0]-i[t][0],l=i[e][1]-i[t][1],c=(Math.abs(h)+Math.abs(l))/s;if(g<c){f=t,m=e,v=!1,x=g/c;break}if(g-=c,g<n){f=e,m=(e+1)%i.length,v=!0,y=g/n,o.lastWaypointReached!==f&&(r.onWaypointReached&&r.onWaypointReached(f),r.onWaitStart&&r.onWaitStart(f),o.lastWaypointReached=f);break}g-=n}}if(v||b)d=i[f][0],_=i[f][1],p="idle",!o.isWaiting&&v&&r.onWaitEnd&&r.onWaitEnd(f);else{const t=i[f],e=i[m],s=e[0]-t[0],n=e[1]-t[1],a=Math.abs(s)+Math.abs(n),r=Math.abs(s)/a;if(x<=r&&0!==s){const e=x/r;d=t[0]+s*e,_=t[1],p=s>0?"right":"left"}else{const i=(x-r)/(1-r);d=e[0],_=t[1]+n*i,p=n>0?"down":"up"}}return o.currentWaypoint=f,o.nextWaypoint=m,o.isWaiting=v,!o.completed&&b&&r.onPatrolComplete&&(r.onPatrolComplete(),o.completed=!0),Motion.animationResult({x:d,y:_,moving:!v,waiting:v,waitProgress:v?y:0,direction:p,waypoint:f,nextWaypoint:m},u,a,b,o)}class Motion{static animationResult(t,e,i,s=!1,n=null){return{...t,t:e,progress:e,loop:i,completed:s,state:n}}static _step(t,e,i,s={},n={started:!1,loopCount:0}){let a=e>0?t/e:1,r=!1;if(!(n=n||{started:!1,loopCount:0}).started&&s.onStart&&(s.onStart(),n.started=!0),i){a%=1;const i=Math.floor(t/e);i>n.loopCount&&s.onLoop&&(s.onLoop(i),n.loopCount=i)}else a>=1&&(a=1,r=!0,!n.completed&&s.onComplete&&(s.onComplete(),n.completed=!0));return{t:a,completed:r,state:n}}static _frame(t,e,i,s=null,n={},a=null){const{t:r,completed:o,state:h}=this._step(t,e,i,n,a);return{t:r,easedT:s?s(r):r,completed:o,state:h}}static oscillate(t,e,i,s,n=!0,a=null,r={},o=null){return oscillateV1(t,e,i,s,n,a,r,o)}static parabolic(t,e,i,s,n,a=!1,r=!1,o=null,h={},l=null){return parabolicV1(t,e,i,s,n,a,r,o,h,l)}static float(t,e,i,s,n,a,r=!0,o=null,h={},l=null){return floatV1(t,e,i,s,n,a,r,o,h,l)}static spring(t,e,i,s,n=!1,a=!1,r={},o={}){return springV1(t,e,i,s,n,a,r,o)}static swing(t,e,i,s,n,a=!0,r=!0,o=null,h={},l=null){return swingV1(t,e,i,s,n,a,r,o,h,l)}static pendulum(t,e,i,s,n=!0,a=!1,r=null,o={},h=null){return pendulumV1(t,e,i,s,n,a,r,o,h)}static pulse(t,e,i,s,n=!0,a=!1,r=null,o={}){return pulseV1(t,e,i,s,n,a,r,{})}static spiral(t,e,i,s,n,a,r,o,h=!1,l=!1,c=null,u={},d=null){return spiralV1(t,e,i,s,n,a,r,o,h,l,c,u,d)}static orbit(t,e,i,s,n,a,r,o=!0,h=!0,l=null,c={},u=null){return orbitV1(t,e,i,s,n,a,r,o,h,l,c,u)}static bezier(t,e,i,s,n,a,r=!1,o=!1,h=null,l={},c=null){return bezierV1(t,e,i,s,n,a,r,o,h,l,c)}static bounce(t,e,i,s,n,a=!1,r=null,o={},h=null){return bounceV1(t,e,i,s,n,a,r,o,h)}static shake(t,e,i,s,n,a,r,o,h=!1,l=null,c={},u=null){return shakeV1(t,e,i,s,n,a,r,o,h,l,c,u)}static follow(t,e=!1,i,s,n=!1,a=null,r={},o=null){return followPath(t,e,i,s,n,a,r,o)}static waypoint(t,e,i,s,n,a=!0,r={},o=null){return waypointV1(t,e,i,s,n,a,r,o)}static patrol(t,e,i,s,n,a,r=!0,o=null){return patrolV1(t,e,i,s,n,a,r,o)}static hop(t,e,i,s,n=!0,a=!0,r=null,o={},h=null){return hopV1(t,e,i,s,n,a,r,o,h)}static group(t,e,i,s,n=!1,a=null,r={},o=null){o||(o={started:!1,loopCount:0,animationStates:Array(t.length).fill(null)});const{t:h,easedT:l,completed:c,state:u}=this._frame(i,s,n,a,r,o),d={};for(let o=0;o<t.length;o++){const h=t[o],l=[...e[o]];h===this.parabolic||h===this.oscillate||h===this.pulse?(l[3]=i,l[4]=s,l[5]=n,void 0===l[6]&&(l[6]=a)):h===this.spring?(l[2]=i,l[3]=s,l[4]=n):h===this.spiral||h===this.bezier?(l[6]=i,l[7]=s,l[8]=n,void 0===l[9]&&(l[9]=a)):h===this.orbit?(l[5]=i,l[6]=s,l[7]=n,void 0===l[9]&&(l[9]=a)):h===this.bounce||h===this.shake?(l[6]=i,l[7]=s,l[8]=n,void 0===l[9]&&(l[9]=a)):h===this.followPath&&(l[2]=i,l[3]=s,l[4]=n,void 0===l[5]&&(l[5]=a)),l.push(r),l.push(u.animationStates[o]);const c=h.apply(this,l);u.animationStates[o]=c.state;d[`anim${o}`]=c}return this.animationResult(d,h,n,c,u)}static sequence(t,e,i,s,n=!1,a=null,r={},o=null,h=null){if(!h){h={started:!1,loopCount:0,animationStates:Array(t.length).fill(null),currentAnim:0,animStartTimes:[0],totalDuration:0};let e=0;for(let t=0;t<i.length;t++)e+=i[t],t<i.length-1&&h.animStartTimes.push(e);h.totalDuration=e}let l=s;if(n&&h.totalDuration>0){l=s%h.totalDuration;const t=Math.floor(s/h.totalDuration);t>h.loopCount&&r.onLoop&&(r.onLoop(t),h.loopCount=t)}!h.started&&r.onStart&&(r.onStart(),h.started=!0);let c=0;for(let e=t.length-1;e>=0;e--)if(l>=h.animStartTimes[e]){c=e;break}h.currentAnim=c;const u=l-h.animStartTimes[c],d=i[c],_=t[c],p=[...e[c]];_===this.parabolic||_===this.oscillate||_===this.pulse?(p[3]=u,p[4]=d,p[5]=!1,a&&a[c]&&(p[6]=a[c])):_===this.spring?(p[2]=u,p[3]=d,p[4]=!1):_===this.spiral||_===this.bezier?(p[6]=u,p[7]=d,p[8]=!1,a&&a[c]&&(p[9]=a[c])):_===this.orbit?(p[5]=u,p[6]=d,p[7]=!1,a&&a[c]&&(p[9]=a[c])):_===this.bounce||_===this.shake?(p[6]=u,p[7]=d,p[8]=!1,a&&a[c]&&(p[9]=a[c])):_===this.followPath&&(p[2]=u,p[3]=d,p[4]=!1,a&&a[c]&&(p[5]=a[c]));const g=o&&o[c]?o[c]:{},f=_.apply(this,[...p,g,h.animationStates[c]]);h.animationStates[c]=f.state;const m=!n&&l>=h.totalDuration;return m&&!h.completed&&r.onComplete&&(r.onComplete(),h.completed=!0),this.animationResult({...f,currentAnim:c,totalAnimations:t.length,sequenceProgress:Math.min(l/h.totalDuration,1)},l/h.totalDuration,n,m,h)}}function bezierV1(t,e,i,s,n,a,r=!1,o=!1,h=null,l={},c=null){if(a<=0)return Motion.animationResult({x:s[0],y:s[1],phase:"complete"},1,!1,!0);let u=n/a,d="forward",_=0;r?(_=Math.floor(u),u%=1,_>0&&l.onLoop&&l.onLoop(_)):u>1&&(u=1),u>0&&n<=a&&l.onStart&&l.onStart();let p=h?h(u):u;o&&(u>=.5?(p=1-2*(u-.5),d="return",u>=.5&&u<.51&&l.onYoyoTurn&&l.onYoyoTurn()):(p=2*u,d="forward"),p=h?h(p):p);const g=3*(e[0]-t[0]),f=3*(i[0]-e[0])-g,m=s[0]-t[0]-g-f,v=3*(e[1]-t[1]),y=3*(i[1]-e[1])-v,x=s[1]-t[1]-v-y,b=m*Math.pow(p,3)+f*Math.pow(p,2)+g*p+t[0],w=x*Math.pow(p,3)+y*Math.pow(p,2)+v*p+t[1],M=!r&&u>=1;return M&&l.onComplete&&l.onComplete(),Motion.animationResult({x:b,y:w,phase:d},u,r,M,c)}class Tweenetik{constructor(t,e,i,s,n={}){this.target=t,this.toProps={...e},this.duration=i,this.easingFn=s||Easing.easeOutQuad,this.delay=n.delay||0,this.onStart=n.onStart||null,this.onComplete=n.onComplete||null,this.onUpdate=n.onUpdate||null,this._elapsed=0,this._started=!1,this._finished=!1,this._startProps={};for(const t in this.toProps)t in this.target&&(this._startProps[t]=this.target[t])}static to(t,e,i,s,n){const a=new Tweenetik(t,e,i,s,n);return Tweenetik._active.push(a),a}update(t){if(this._finished)return;if(this._elapsed+=t,this._elapsed<this.delay)return;const e=this._elapsed-this.delay,i=Math.min(e/this.duration,1);!this._started&&i>0&&(this._started=!0,this.onStart&&this.onStart());const s=this.easingFn(i);for(const t in this._startProps){const e=this._startProps[t],i=this.toProps[t];this.target[t]=Tween.lerp(e,i,s)}this.onUpdate&&this.onUpdate(),i>=1&&(this._finished=!0,this.onComplete&&this.onComplete())}static updateAll(t){for(const e of Tweenetik._active)e.update(t);Tweenetik._active=Tweenetik._active.filter(t=>!t._finished)}static killTarget(t){Tweenetik._active=Tweenetik._active.filter(e=>e.target!==t)}static killAll(){Tweenetik._active=[]}}class Pipeline extends Loggable{constructor(t){super(),this.game=t,this._collection=new ZOrderedCollection,this._collection._owner=this;["inputdown","inputup","inputmove","click"].forEach(t=>{this.game.events.on(t,e=>{this.dispatchInputEvent(t,e)})})}_hoverObject(t,e){if(!t.interactive||!t._hitTest)return;const i=t._hitTest(e.x,e.y);i&&!t._hovered?(t._hovered=!0,t.events.emit("mouseover",e)):!i&&t._hovered&&(t._hovered=!1,t.events.emit("mouseout",e))}_hoverScene(t,e){if(t.children&&t.children.length>0)for(let i=t.children.length-1;i>=0;i--){const s=t.children[i];s instanceof Scene?this._hoverScene(s,e):this._hoverObject(s,e)}this._hoverObject(t,e)}dispatchInputEvent(t,e){var i;for(let s=this.gameObjects.length-1;s>=0;s--){const n=this.gameObjects[s];if(n instanceof Scene){if(this._dispatchToScene(n,t,e))break}else if(n.interactive&&(null==(i=n._hitTest)?void 0:i.call(n,e.x,e.y))){n.events.emit(t,e);break}}"inputmove"===t&&this._dispatchHover(e)}_dispatchHover(t){for(let e=this.gameObjects.length-1;e>=0;e--){const i=this.gameObjects[e];i instanceof Scene?this._hoverScene(i,t):this._hoverObject(i,t)}}_dispatchToScene(t,e,i){var s,n;for(let n=t.children.length-1;n>=0;n--){const a=t.children[n];if(a instanceof Scene){if(this._dispatchToScene(a,e,i))return!0}else if(a.interactive&&(null==(s=a._hitTest)?void 0:s.call(a,i.x,i.y)))return a.events.emit(e,i),!0}return!(!t.interactive||!(null==(n=t._hitTest)?void 0:n.call(t,i.x,i.y)))&&(t.events.emit(e,i),!0)}add(t){t.parent=this.game;const e=this._collection.add(t);return e.init&&e.init(),e}remove(t){null!=t?this._collection.remove(t):this.logger.warn("Cannot remove undefined or null object",t)}bringToFront(t){return this._collection.bringToFront(t)}sendToBack(t){return this._collection.sendToBack(t)}bringForward(t){return this._collection.bringForward(t)}sendBackward(t){return this._collection.sendBackward(t)}clear(){return this._collection.clear()}get gameObjects(){return this._collection.children}update(t){this.logger.groupCollapsed("Pipeline.update"),this._collection.children.filter(t=>t.active).forEach(e=>e.update(t)),Tweenetik.updateAll(t),this.logger.groupEnd()}render(){this.logger.groupCollapsed("Pipeline.render"),this._collection.getSortedChildren().filter(t=>t.visible).filter(t=>t.active).forEach(t=>t.render()),this.logger.groupEnd()}}class Cursor extends GameObject{constructor(t,e,i=null,s={}){super(t,s),this.normalShape=e,this.pressedShape=i||e,this.active=!1,this.offsetX=0,this.offsetY=0,this.isDown=!1,this.game.events.on("inputmove",t=>{this.x=t.x,this.y=t.y}),this.game.events.on("inputdown",()=>{this.isDown=!0}),this.game.events.on("inputup",()=>{this.isDown=!1}),this.game.events.on("mouseover",()=>{this.visible=!1}),this.game.events.on("mouseout",()=>{this.visible=!0})}activate(){this.active=!0,this.game.canvas.style.cursor="none"}deactivate(){this.active=!1,this.game.canvas.style.cursor="default"}draw(){if(super.draw(),!this.active)return;const t=this.isDown&&this.pressedShape?this.pressedShape:this.normalShape;t&&t.render()}}class Game{constructor(t){__privateAdd(this,_prevWidth,0),__privateAdd(this,_prevHeight,0),this.canvas=t,this.ctx=t.getContext("2d"),this.events=new EventEmitter,this._cursor=null,this.lastTime=0,this.dt=0,this.running=!1,this._frame=0,this.pipeline=new Pipeline(this),Painter.init(this.ctx),this.targetFPS=60,this._frameInterval=1e3/this.targetFPS,this._accumulator=0,this._pauseOnBlur=!1,this._isPaused=!1,this._init=!1,this.initLogging()}setFPS(t){this.targetFPS=t,this._frameInterval=1e3/t}init(){this.initIO(),this.initMotion(),this._init=!0,this.logger.log("[Game] Initialized")}initMouse(){Mouse.init(this)}initTouch(){Touch.init(this)}initInput(){Input.init(this)}initKeyboard(){Keys.init(this)}initIO(){this.initMouse(),this.initTouch(),this.initInput(),this.initKeyboard()}initMotion(){Tweenetik._active=[]}initLogging(){this.logger=new Logger("Game"),Logger.setOutput(console),Logger.disableAll(),Logger.disable(),Logger.setLevel(Logger.INFO),this.logger.groupCollapsed("Initializing Game...")}enableLogging(){Logger.enable()}disableLogging(){Logger.disableAll(),Logger.disable()}markBoundsDirty(){this._boundsDirty=!0}get boundsDirty(){return this._boundsDirty}set boundsDirty(t){this._boundsDirty=t}enableFluidSize(t=window,e={}){const{top:i=0,right:s=0,bottom:n=0,left:a=0}=e;if(t===window){const t=()=>{var t;this.canvas.width=window.innerWidth-a-s,this.canvas.height=window.innerHeight-i-n,__privateGet(this,_prevWidth)===this.canvas.width&&__privateGet(this,_prevHeight)===this.canvas.height||(this.markBoundsDirty(),null==(t=this.onResize)||t.call(this)),__privateSet(this,_prevWidth,this.canvas.width),__privateSet(this,_prevHeight,this.canvas.height)};t(),window.addEventListener("resize",t),this._fluidResizeCleanup=()=>{window.removeEventListener("resize",t)}}else{if(!("ResizeObserver"in window))return void console.warn("ResizeObserver not supported in this browser.");const e=()=>{const e=t.getBoundingClientRect();this.canvas.width=e.width-a-s,this.canvas.height=e.height-i-n},r=new ResizeObserver(()=>{e()});r.observe(t),e(),this._fluidResizeCleanup=()=>r.disconnect()}}disableFluidSize(){this._fluidResizeCleanup&&(this._fluidResizeCleanup(),this._fluidResizeCleanup=null)}start(){if(this.logger.groupCollapsed("[Game] Starting..."),this.init(),!this._init)throw new Error("Game not initialized. Did you call init()? Remember to call super.init() in your subclass.");this.running=!0,this.loop=this.loop.bind(this),requestAnimationFrame(this.loop),this.logger.log("[Game] Started"),this.logger.groupEnd()}stop(){this.running=!1,this.logger.log("[Game] Stopped")}restart(){this.pipeline.clear(),this.init(),this.start(),this.logger.log("[Game] Restarted")}loop(t){if(!this.running)return;const e=t-this.lastTime;if(this.lastTime=t,this._accumulator+=e,this.actualFps=1e3/e,this._accumulator>=this._frameInterval){const t=this._frameInterval/1e3;this.dt=t,this._frame++,this.logger.groupCollapsed(`Frame #${this._frame}`),this.logger.time("render time"),this.update(t),this.render(),this.logger.timeEnd("render time"),this.logger.groupEnd(),this._accumulator-=this._frameInterval}this.boundsDirty&&(this.boundsDirty=!1),requestAnimationFrame(this.loop)}update(t){this.pipeline.update(t)}render(){Painter.setContext(this.ctx),this.running&&this.clear(),this.pipeline.render()}clear(){Painter.clear()}get width(){return this.canvas.width}get height(){return this.canvas.height}set backgroundColor(t){this.canvas.style.backgroundColor=t}set cursor(t){this._cursor&&(this._cursor.destroy(),this.pipeline.remove(this._cursor)),this._cursor=t,this._cursor.activate(),this.pipeline.add(t)}get cursor(){return this._cursor}resetCursor(){this._cursor&&(this._cursor.destroy(),this.pipeline.remove(this._cursor),this._cursor=null)}enablePauseOnBlur(t){this._pauseOnBlur=t,t?window.addEventListener("visibilitychange",this._handleVisibilityChange.bind(this),!1):window.removeEventListener("visibilitychange",this._handleVisibilityChange.bind(this),!1)}_handleVisibilityChange(){this.logger.log("Visibility change detected"),document.hidden?this._pauseOnBlur&&this.running&&(this._isPaused=!0,this.stop(),this.logger.log("Paused due to tab visibility change")):this._isPaused&&(this._isPaused=!1,this.start(),this.logger.log("Resumed after tab visibility change"))}}_prevWidth=new WeakMap,_prevHeight=new WeakMap;const UI_THEME={colors:{neonGreen:"#0f0",terminalGreen:"#16F529",cyanAccent:"#0ff",darkBg:"rgba(0, 0, 0, 0.85)",darkerBg:"rgba(0, 0, 0, 0.92)",hoverBg:"#0f0",pressedBg:"#0c0",activeBg:"rgba(0, 255, 0, 0.15)",lightText:"#0f0",darkText:"#000",dimText:"rgba(0, 255, 0, 0.7)",subtleBorder:"rgba(0, 255, 0, 0.4)",activeBorder:"#0f0",glowBorder:"rgba(0, 255, 0, 0.5)"},fonts:{primary:"monospace",small:"11px monospace",medium:"14px monospace",large:"18px monospace",heading:"bold 24px monospace"},spacing:{xs:4,sm:8,md:12,lg:16,xl:24},button:{default:{bg:"rgba(0, 0, 0, 0.85)",stroke:"rgba(0, 255, 0, 0.4)",text:"#0f0"},hover:{bg:"#0f0",stroke:"#0f0",text:"#000"},pressed:{bg:"#0c0",stroke:"#0f0",text:"#000"},active:{bg:"rgba(0, 255, 0, 0.15)",stroke:"#0f0",text:"#0f0"}},tooltip:{bg:"rgba(0, 0, 0, 0.92)",border:"rgba(0, 255, 0, 0.5)",text:"#0f0"}};class Button extends GameObject{constructor(t,e={}){super(t,e);const{x:i=0,y:s=0,width:n=120,height:a=40,text:r="Button",font:o="14px monospace",textColor:h="#000",textAlign:l="center",textBaseline:c="middle",shape:u=null,label:d=null,onClick:_=null,onHover:p=null,onPressed:g=null,onRelease:f=null,padding:m=10,colorDefaultBg:v=UI_THEME.button.default.bg,colorDefaultStroke:y=UI_THEME.button.default.stroke,colorDefaultText:x=UI_THEME.button.default.text,colorHoverBg:b=UI_THEME.button.hover.bg,colorHoverStroke:w=UI_THEME.button.hover.stroke,colorHoverText:M=UI_THEME.button.hover.text,colorPressedBg:S=UI_THEME.button.pressed.bg,colorPressedStroke:P=UI_THEME.button.pressed.stroke,colorPressedText:T=UI_THEME.button.pressed.text}=e;this.x=i,this.y=s,this.width=n,this.height=a,this.padding=m,this.textAlign=l,this.textBaseline=c,this.initColorScheme({colorDefaultBg:v,colorDefaultStroke:y,colorDefaultText:x,colorHoverBg:b,colorHoverStroke:w,colorHoverText:M,colorPressedBg:S,colorPressedStroke:P,colorPressedText:T}),this.initBackground(u),this.initLabel(r,o,h,d),this.initGroup(),this.initEvents(_,p,g,f),this.setState("default")}initColorScheme(t){this.colors={default:{bg:t.colorDefaultBg,stroke:t.colorDefaultStroke,text:t.colorDefaultText},hover:{bg:t.colorHoverBg,stroke:t.colorHoverStroke,text:t.colorHoverText},pressed:{bg:t.colorPressedBg,stroke:t.colorPressedStroke,text:t.colorPressedText}}}initBackground(t){this.bg=t??new Rectangle({width:this.width,height:this.height,color:this.colors.default.bg,stroke:this.colors.default.stroke,lineWidth:2})}initLabel(t,e,i,s){this.label=s??new TextShape(t,{font:e,color:i,align:this.textAlign,baseline:this.textBaseline}),this.alignText()}alignText(){if(!this.label)return;const t=this.width/2,e=this.height/2;switch(this.textAlign){case"left":this.label.x=-t+this.padding;break;case"right":this.label.x=t-this.padding;break;default:this.label.x=0}switch(this.textBaseline){case"top":this.label.y=-e+this.padding;break;case"bottom":this.label.y=e-this.padding;break;default:this.label.y=0}}initGroup(){this.group=new Group,this.group.add(this.bg),this.group.add(this.label)}initEvents(t,e,i,s){this.interactive=!0,this.onHover=e,this.onPressed=i,this.onRelease=s,this.on("mouseover",this.setState.bind(this,"hover")),this.on("mouseout",this.setState.bind(this,"default")),this.on("inputdown",this.setState.bind(this,"pressed")),this.on("inputup",()=>{"pressed"===this.state&&"function"==typeof t&&t(),this.setState("hover")})}setState(t){var e,i,s;if(this.state!==t)switch(this.state=t,t){case"default":this.game.cursor&&setTimeout(()=>{this.game.cursor.activate()},0),this.bg.color=this.colors.default.bg,this.bg.stroke=this.colors.default.stroke,this.label.color=this.colors.default.text,this.game.canvas.style.cursor="default",null==(e=this.onRelease)||e.call(this);break;case"hover":this.game.cursor&&this.game.cursor.deactivate(),this.bg.color=this.colors.hover.bg,this.bg.stroke=this.colors.hover.stroke,this.label.color=this.colors.hover.text,this.game.canvas.style.cursor="pointer",null==(i=this.onHover)||i.call(this);break;case"pressed":this.game.cursor&&this.game.cursor.deactivate(),this.bg.color=this.colors.pressed.bg,this.bg.stroke=this.colors.pressed.stroke,this.label.color=this.colors.pressed.text,this.game.canvas.style.cursor="pointer",null==(s=this.onPressed)||s.call(this)}}update(t){super.update(t),this._boundsDirty&&this.alignText()}get text(){return this.label.text}set text(t){this.label.text=t,this._boundsDirty=!0}setTextAlign(t){this.textAlign=t,this.label.align=t,this._boundsDirty=!0}setTextBaseline(t){this.textBaseline=t,this.label.baseline=t,this._boundsDirty=!0}setFont(t){this.label.font=t,this._boundsDirty=!0}resize(t,e){this.width=t,this.height=e,this.bg.width=t,this.bg.height=e,this._boundsDirty=!0}getBounds(){return{x:this.x,y:this.y,width:this.width,height:this.height}}draw(){super.draw(),this.group.render()}}class ToggleButton extends Button{constructor(t,e={}){const i=e.onClick;super(t,{...e,onClick:()=>{this.toggled=!this.toggled,"function"==typeof e.onToggle&&e.onToggle(this.toggled),"function"==typeof i&&i(),this.refreshToggleVisual()}}),this.colorActiveBg=e.colorActiveBg||UI_THEME.button.active.bg,this.colorActiveStroke=e.colorActiveStroke||UI_THEME.button.active.stroke,this.colorActiveText=e.colorActiveText||UI_THEME.button.active.text,this.toggled=!!e.startToggled,this.refreshToggleVisual()}toggle(t){this.toggled=t,this.refreshToggleVisual()}refreshToggleVisual(){this.toggled?(this.bg.fillColor=this.colorActiveBg,this.bg.strokeColor=this.colorActiveStroke,this.label.color=this.colorActiveText):(this.bg.fillColor=this.colors.default.bg,this.bg.strokeColor=this.colors.default.stroke,this.label.color=this.colors.default.text)}setState(t){super.setState(t),this.toggled&&(this.bg.fillColor=this.colorActiveBg,this.bg.strokeColor=this.colorActiveStroke,this.label.color=this.colorActiveText)}}class Tooltip extends GameObject{constructor(t,e={}){super(t,{...e,zIndex:9999}),this.font=e.font||UI_THEME.fonts.small,this.textColor=e.textColor||UI_THEME.tooltip.text,this.bgColor=e.bgColor||UI_THEME.tooltip.bg,this.borderColor=e.borderColor||UI_THEME.tooltip.border,this.padding=e.padding??8,this.offsetX=e.offsetX??15,this.offsetY=e.offsetY??15,this.maxWidth=e.maxWidth??300,this.lineHeightMultiplier=e.lineHeight??1.4,this._text="",this._lines=[],this._visible=!1,this.bg=new Rectangle({width:100,height:30,color:this.bgColor,stroke:this.borderColor,lineWidth:1}),this.lineShapes=[],this.group=new Group,this.group.add(this.bg),this.game.events.on("inputmove",t=>{this._visible&&this.updatePosition(t.x,t.y)})}wrapText(t){const e=this.game.ctx;e.font=this.font;const i=[],s=t.split("\n");for(const t of s){const s=t.split(" ");let n="";for(const t of s){const s=n?`${n} ${t}`:t;e.measureText(s).width>this.maxWidth&&n?(i.push(n),n=t):n=s}n?i.push(n):""===t&&i.push("")}return i}show(t,e,i){this._text=t,this._visible=!0,this._lines=this.wrapText(t),this.updateLineShapes(),this.updateSize(),void 0!==e&&void 0!==i&&this.updatePosition(e,i)}updateLineShapes(){for(const t of this.lineShapes)this.group.remove(t);this.lineShapes=this._lines.map(t=>new TextShape(t,{font:this.font,color:this.textColor,align:"left",baseline:"top"}));for(const t of this.lineShapes)this.group.add(t)}hide(){this._visible=!1}updatePosition(t,e){const i=this.bg.width,s=this.bg.height;let n=t+this.offsetX+i/2,a=e+this.offsetY+s/2;n+i/2>this.game.width&&(n=t-this.offsetX-i/2),a+s/2>this.game.height&&(a=e-this.offsetY-s/2),n-i/2<0&&(n=i/2+5),a-s/2<0&&(a=s/2+5),this.x=n,this.y=a}updateSize(){const t=this.game.ctx;t.font=this.font;let e=0;for(const i of this._lines){const s=t.measureText(i);e=Math.max(e,s.width)}const i=Math.min(e,this.maxWidth),s=parseInt(this.font)*this.lineHeightMultiplier,n=s*this._lines.length;this.bg.width=i+2*this.padding,this.bg.height=n+2*this.padding;const a=-this.bg.width/2+this.padding,r=-this.bg.height/2+this.padding;for(let t=0;t<this.lineShapes.length;t++)this.lineShapes[t].x=a,this.lineShapes[t].y=r+t*s}draw(){this._visible&&this.group.render()}}class FPSCounter extends Text{constructor(t,e={}){super(t,"0 FPS",{x:0,y:0,font:UI_THEME.fonts.small,color:UI_THEME.colors.neonGreen,align:"center",baseline:"middle",debug:!1,...e}),this.fps=0,this._frames=0,this._accum=0}update(t){const e=this.game.actualFps;e&&(this._frames++,this._accum+=t,this._accum>=.5&&(this.fps=Math.round(e),this.text=`${this.fps} FPS`,this._accum=0,this._frames=0),super.update(t))}getBounds(){if(this.shape&&this.shape.getTextBounds){const t=this.shape.getTextBounds();return{x:t.x,y:t.y,width:t.width,height:t.height}}return super.getBounds()}getDebugBounds(){if(this.shape&&this.shape.getDebugBounds){const t=this.shape.getDebugBounds();return{x:t.x,y:t.y,width:t.width,height:t.height}}return super.getDebugBounds()}}class Stepper extends GameObject{constructor(t,e={}){super(t,e);const{x:i=0,y:s=0,value:n=0,min:a=-1/0,max:r=1/0,step:o=1,buttonSize:h=32,valueWidth:l=60,height:c=32,gap:u=4,font:d=UI_THEME.fonts.medium,onChange:_=null,formatValue:p=null,label:g=""}=e;this.x=i,this.y=s,this.buttonSize=h,this.valueWidth=l,this.stepperHeight=c,this.gap=u,this.font=d,this._value=this.clamp(n,a,r),this.min=a,this.max=r,this.step=o,this.onChange=_,this.formatValue=p||(t=>String(t)),this.labelText=g,this.width=h+u+l+u+h,this.height=g?c+20:c,this.initComponents(),this.initEvents()}clamp(t,e,i){return Math.max(e,Math.min(i,t))}initComponents(){this.group=new Group;const t=this.width/2,e=this.buttonSize/2;this.valueWidth;const i=-t+e,s=t-e,n=this.labelText?8:0,a=this.labelText?-(this.stepperHeight/2+4):0;this.labelText&&(this.label=new TextShape(this.labelText,{font:UI_THEME.fonts.small,color:UI_THEME.colors.dimText,align:"center",baseline:"middle"}),this.label.y=a,this.group.add(this.label)),this.decrementBg=new Rectangle({width:this.buttonSize,height:this.stepperHeight,color:UI_THEME.button.default.bg,stroke:UI_THEME.button.default.stroke,lineWidth:1}),this.decrementBg.x=i,this.decrementBg.y=n,this.decrementText=new TextShape("−",{font:this.font,color:UI_THEME.button.default.text,align:"center",baseline:"middle"}),this.decrementText.x=i,this.decrementText.y=n,this.valueBg=new Rectangle({width:this.valueWidth,height:this.stepperHeight,color:UI_THEME.colors.darkerBg,stroke:UI_THEME.colors.subtleBorder,lineWidth:1}),this.valueBg.x=0,this.valueBg.y=n,this.valueText=new TextShape(this.formatValue(this._value),{font:this.font,color:UI_THEME.colors.neonGreen,align:"center",baseline:"middle"}),this.valueText.x=0,this.valueText.y=n,this.incrementBg=new Rectangle({width:this.buttonSize,height:this.stepperHeight,color:UI_THEME.button.default.bg,stroke:UI_THEME.button.default.stroke,lineWidth:1}),this.incrementBg.x=s,this.incrementBg.y=n,this.incrementText=new TextShape("+",{font:this.font,color:UI_THEME.button.default.text,align:"center",baseline:"middle"}),this.incrementText.x=s,this.incrementText.y=n,this.group.add(this.decrementBg),this.group.add(this.decrementText),this.group.add(this.valueBg),this.group.add(this.valueText),this.group.add(this.incrementBg),this.group.add(this.incrementText),this._decrementBounds={x:i-this.buttonSize/2,y:n-this.stepperHeight/2,width:this.buttonSize,height:this.stepperHeight},this._incrementBounds={x:s-this.buttonSize/2,y:n-this.stepperHeight/2,width:this.buttonSize,height:this.stepperHeight},this._decrementHover=!1,this._incrementHover=!1,this._decrementPressed=!1,this._incrementPressed=!1}initEvents(){this.interactive=!0,this._isMouseOver=!1,this.on("mouseover",()=>{this._isMouseOver=!0}),this.on("mouseout",()=>{this._isMouseOver=!1,this.handleMouseOut()}),this.game.events.on("inputmove",t=>{this._isMouseOver&&this.handleMouseMove(t)}),this.on("inputdown",t=>this.handleInputDown(t)),this.on("inputup",()=>this.handleInputUp())}screenToLocal(t,e){let i=t,s=e;const n=[];let a=this;for(;a;)n.unshift(a),a=a.parent;for(const t of n){if(i-=t.x||0,s-=t.y||0,t.rotation){const e=Math.cos(-t.rotation),n=Math.sin(-t.rotation),a=i;i=a*e-s*n,s=a*n+s*e}void 0!==t.scaleX&&0!==t.scaleX&&(i/=t.scaleX),void 0!==t.scaleY&&0!==t.scaleY&&(s/=t.scaleY)}return{x:i,y:s}}isPointInBounds(t,e,i){return t>=i.x&&t<=i.x+i.width&&e>=i.y&&e<=i.y+i.height}handleMouseMove(t){const e=this.screenToLocal(t.x,t.y),i=this._decrementHover,s=this._incrementHover;this._decrementHover=this.isPointInBounds(e.x,e.y,this._decrementBounds),this._incrementHover=this.isPointInBounds(e.x,e.y,this._incrementBounds),i===this._decrementHover&&s===this._incrementHover||this.updateButtonStates(),this._decrementHover||this._incrementHover?this.game.canvas.style.cursor="pointer":this.game.canvas.style.cursor="default"}handleMouseOut(){this._decrementHover=!1,this._incrementHover=!1,this._decrementPressed=!1,this._incrementPressed=!1,this.updateButtonStates(),this.game.canvas.style.cursor="default"}handleInputDown(t){const e=this.screenToLocal(t.x,t.y);this.isPointInBounds(e.x,e.y,this._decrementBounds)?(this._decrementPressed=!0,this.decrement()):this.isPointInBounds(e.x,e.y,this._incrementBounds)&&(this._incrementPressed=!0,this.increment()),this.updateButtonStates()}handleInputUp(){this._decrementPressed=!1,this._incrementPressed=!1,this.updateButtonStates()}updateButtonStates(){this._decrementPressed?(this.decrementBg.color=UI_THEME.button.pressed.bg,this.decrementBg.stroke=UI_THEME.button.pressed.stroke,this.decrementText.color=UI_THEME.button.pressed.text):this._decrementHover?(this.decrementBg.color=UI_THEME.button.hover.bg,this.decrementBg.stroke=UI_THEME.button.hover.stroke,this.decrementText.color=UI_THEME.button.hover.text):(this.decrementBg.color=UI_THEME.button.default.bg,this.decrementBg.stroke=UI_THEME.button.default.stroke,this.decrementText.color=UI_THEME.button.default.text),this._value<=this.min&&(this.decrementBg.stroke=UI_THEME.colors.subtleBorder,this.decrementText.color=UI_THEME.colors.dimText),this._incrementPressed?(this.incrementBg.color=UI_THEME.button.pressed.bg,this.incrementBg.stroke=UI_THEME.button.pressed.stroke,this.incrementText.color=UI_THEME.button.pressed.text):this._incrementHover?(this.incrementBg.color=UI_THEME.button.hover.bg,this.incrementBg.stroke=UI_THEME.button.hover.stroke,this.incrementText.color=UI_THEME.button.hover.text):(this.incrementBg.color=UI_THEME.button.default.bg,this.incrementBg.stroke=UI_THEME.button.default.stroke,this.incrementText.color=UI_THEME.button.default.text),this._value>=this.max&&(this.incrementBg.stroke=UI_THEME.colors.subtleBorder,this.incrementText.color=UI_THEME.colors.dimText)}increment(){this.value=this._value+this.step}decrement(){this.value=this._value-this.step}get value(){return this._value}set value(t){const e=this.clamp(t,this.min,this.max);e!==this._value&&(this._value=e,this.valueText.text=this.formatValue(this._value),this.updateButtonStates(),"function"==typeof this.onChange&&this.onChange(this._value))}setBounds(t,e){this.min=t,this.max=e,this.value=this._value}getBounds(){return{x:this.x,y:this.y,width:this.width,height:this.height}}draw(){super.draw(),this.group.render()}}class Particle{constructor(){this.x=0,this.y=0,this.z=0,this.vx=0,this.vy=0,this.vz=0,this.size=1,this.color={r:255,g:255,b:255,a:1},this.shape="circle",this.age=0,this.lifetime=1,this.alive=!0,this.custom={}}reset(){this.x=0,this.y=0,this.z=0,this.vx=0,this.vy=0,this.vz=0,this.size=1,this.color.r=255,this.color.g=255,this.color.b=255,this.color.a=1,this.shape="circle",this.age=0,this.lifetime=1,this.alive=!0;for(const t in this.custom)delete this.custom[t]}get progress(){return this.lifetime>0?this.age/this.lifetime:1}}class ParticleEmitter{constructor(t={}){this.rate=t.rate??10,this.position={x:0,y:0,z:0,...t.position},this.spread={x:0,y:0,z:0,...t.spread},this.velocity={x:0,y:0,z:0,...t.velocity},this.velocitySpread={x:0,y:0,z:0,...t.velocitySpread},this.lifetime={min:1,max:2,...t.lifetime},this.size={min:1,max:1,...t.size},this.color={r:255,g:255,b:255,a:1,...t.color},this.shape=t.shape??"circle",this.active=!1!==t.active,this._timer=0}_spread(t){return 2*(Math.random()-.5)*t}_range(t,e){return t+Math.random()*(e-t)}emit(t){t.x=this.position.x+this._spread(this.spread.x),t.y=this.position.y+this._spread(this.spread.y),t.z=this.position.z+this._spread(this.spread.z),t.vx=this.velocity.x+this._spread(this.velocitySpread.x),t.vy=this.velocity.y+this._spread(this.velocitySpread.y),t.vz=this.velocity.z+this._spread(this.velocitySpread.z),t.lifetime=this._range(this.lifetime.min,this.lifetime.max),t.age=0,t.alive=!0,t.size=this._range(this.size.min,this.size.max),t.color.r=this.color.r,t.color.g=this.color.g,t.color.b=this.color.b,t.color.a=this.color.a,t.shape=this.shape}update(t){if(!this.active||this.rate<=0)return 0;this._timer+=t;const e=1/this.rate;let i=0;for(;this._timer>=e;)this._timer-=e,i++;return i}reset(){this._timer=0}}const Updaters={velocity:(t,e)=>{t.x+=t.vx*e,t.y+=t.vy*e,t.z+=t.vz*e},lifetime:(t,e)=>{t.age+=e,t.age>=t.lifetime&&(t.alive=!1)},gravity:(t=200)=>(e,i)=>{e.vy+=t*i},rise:(t=100)=>(e,i)=>{e.vy-=t*i},damping:(t=.98)=>(e,i)=>{e.vx*=t,e.vy*=t,e.vz*=t},fadeOut:(t,e)=>{t.color.a=Math.max(0,1-t.progress)},fadeInOut:(t,e)=>{const i=t.progress;t.color.a=i<.5?2*i:2*(1-i)},shrink:(t=0)=>(e,i)=>{void 0===e.custom._initialSize&&(e.custom._initialSize=e.size),e.size=e.custom._initialSize*(1-e.progress*(1-t))},grow:(t=2)=>(e,i)=>{void 0===e.custom._initialSize&&(e.custom._initialSize=e.size),e.size=e.custom._initialSize*(1+e.progress*(t-1))},colorOverLife:(t,e)=>(i,s)=>{const n=i.progress;i.color.r=Math.floor(t.r+(e.r-t.r)*n),i.color.g=Math.floor(t.g+(e.g-t.g)*n),i.color.b=Math.floor(t.b+(e.b-t.b)*n)},wobble:(t=10)=>(e,i)=>{e.vx+=(Math.random()-.5)*t*i,e.vy+=(Math.random()-.5)*t*i},bounds:(t,e=.8)=>(i,s)=>{i.x<t.left?(i.x=t.left,i.vx=Math.abs(i.vx)*e):i.x>t.right&&(i.x=t.right,i.vx=-Math.abs(i.vx)*e),i.y<t.top?(i.y=t.top,i.vy=Math.abs(i.vy)*e):i.y>t.bottom&&(i.y=t.bottom,i.vy=-Math.abs(i.vy)*e)},attract:(t,e=100)=>(i,s)=>{const n=t.x-i.x,a=t.y-i.y,r=(t.z??0)-i.z,o=Math.sqrt(n*n+a*a+r*r);if(o>1){const t=e*s/o;i.vx+=n*t,i.vy+=a*t,i.vz+=r*t}}};class ParticleSystem extends GameObject{constructor(t,e={}){super(t,e),this.particles=[],this.pool=[],this.maxParticles=e.maxParticles??5e3,this.emitters=new Map,this.camera=e.camera??null,this.depthSort=e.depthSort??!1,this.updaters=e.updaters??[Updaters.velocity,Updaters.lifetime],this.blendMode=e.blendMode??"source-over",this.worldSpace=e.worldSpace??!1,this._particleCount=0}addEmitter(t,e){return this.emitters.set(t,e),this}removeEmitter(t){return this.emitters.delete(t),this}getEmitter(t){return this.emitters.get(t)}acquire(){return this.pool.length>0?this.pool.pop():new Particle}release(t){t.reset(),this.pool.push(t)}emit(t,e){for(let i=0;i<t&&this.particles.length<this.maxParticles;i++){const t=this.acquire();e.emit(t),this.particles.push(t)}}burst(t,e){const i="string"==typeof e?this.emitters.get(e):e;i&&this.emit(t,i)}update(t){super.update(t);for(const e of this.emitters.values())if(e.active){const i=e.update(t);this.emit(i,e)}for(let e=this.particles.length-1;e>=0;e--){const i=this.particles[e];for(const e of this.updaters)e(i,t,this);i.alive||(this.release(i),this.particles.splice(e,1))}this._particleCount=this.particles.length}render(){super.render(),0!==this.particles.length&&(this.camera&&this.depthSort?this.renderWithDepthSort():this.renderSimple())}renderSimple(){Painter.useCtx(t=>{t.globalCompositeOperation=this.blendMode;for(const e of this.particles)this.drawParticle(t,e,e.x,e.y,1);t.globalCompositeOperation="source-over"})}renderWithDepthSort(){const t=[];for(const e of this.particles){const i=this.camera.project(e.x,e.y,e.z);i.z<10-this.camera.perspective||t.push({p:e,x:i.x,y:i.y,z:i.z,scale:i.scale})}t.sort((t,e)=>e.z-t.z),Painter.useCtx(e=>{e.globalCompositeOperation=this.blendMode;const i=this.parent&&"Scene3D"===this.parent.constructor.name;this.worldSpace||i||(e.save(),e.translate(this.game.width/2,this.game.height/2));for(const i of t)this.drawParticle(e,i.p,i.x,i.y,i.scale);this.worldSpace||i||e.restore(),e.globalCompositeOperation="source-over"})}drawParticle(t,e,i,s,n){const{r:a,g:r,b:o,a:h}=e.color,l=e.size*n;if(l<.5||h<=0)return;t.fillStyle=`rgba(${Math.floor(a)},${Math.floor(r)},${Math.floor(o)},${h})`;const c=e.shape??"circle",u=l/2;t.beginPath(),"circle"===c?t.arc(i,s,u,0,2*Math.PI):"square"===c?t.rect(i-u,s-u,l,l):"triangle"===c&&(t.moveTo(i,s-u),t.lineTo(i+u,s+u),t.lineTo(i-u,s+u),t.closePath()),t.fill()}clear(){for(const t of this.particles)this.release(t);this.particles=[],this._particleCount=0}get particleCount(){return this._particleCount}get poolSize(){return this.pool.length}}class Collision{static rectRect(t,e){return t.x<e.x+e.width&&t.x+t.width>e.x&&t.y<e.y+e.height&&t.y+t.height>e.y}static intersects(t,e){return Collision.rectRect(t,e)}static pointRect(t,e,i){return t>=i.x&&t<=i.x+i.width&&e>=i.y&&e<=i.y+i.height}static circleCircle(t,e){const i=t.x-e.x,s=t.y-e.y,n=i*i+s*s,a=t.radius+e.radius;return n<=a*a}static getCircleOverlap(t,e){const i=t.x-e.x,s=t.y-e.y,n=i*i+s*s,a=t.radius+e.radius;if(n>=a*a||n<1e-4)return null;const r=Math.sqrt(n),o=1/r;return{overlap:a-r,nx:i*o,ny:s*o,dist:r}}static applyCircleSeparation(t,e,i={}){const s=i.strength??5e3,n=i.useSizeAsRadius??!0,a=t.length;for(let i=0;i<a;i++){const r=t[i],o=n?.5*r.size:r.radius??10;for(let h=i+1;h<a;h++){const a=t[h],l=n?.5*a.size:a.radius??10,c=Collision.getCircleOverlap({x:r.x,y:r.y,radius:o},{x:a.x,y:a.y,radius:l});if(c){const t=s*(c.overlap/(o+l)),n=c.nx*t,a=c.ny*t;e[i].x+=n,e[i].y+=a,e[h].x-=n,e[h].y-=a}}}}static pointCircle(t,e,i){const s=t-i.x,n=e-i.y;return s*s+n*n<=i.radius*i.radius}static circleRect(t,e){const i=Math.max(e.x,Math.min(t.x,e.x+e.width)),s=Math.max(e.y,Math.min(t.y,e.y+e.height)),n=t.x-i,a=t.y-s;return n*n+a*a<=t.radius*t.radius}static lineRect(t,e,i,s,n,a=0){const r=n.x-a/2,o=n.y-a/2,h=n.width+a,l=n.height+a;if(Collision.pointRect(t,e,{x:r,y:o,width:h,height:l})||Collision.pointRect(i,s,{x:r,y:o,width:h,height:l}))return!0;const c=Collision.lineLine(t,e,i,s,r,o,r,o+l),u=Collision.lineLine(t,e,i,s,r+h,o,r+h,o+l),d=Collision.lineLine(t,e,i,s,r,o,r+h,o),_=Collision.lineLine(t,e,i,s,r,o+l,r+h,o+l);return c||u||d||_}static lineLine(t,e,i,s,n,a,r,o){const h=(o-a)*(i-t)-(r-n)*(s-e);if(0===h)return!1;const l=((r-n)*(e-a)-(o-a)*(t-n))/h,c=((i-t)*(e-a)-(s-e)*(t-n))/h;return l>=0&&l<=1&&c>=0&&c<=1}static segmentsRect(t,e,i=0){for(const s of t)if(Collision.lineRect(s.x1,s.y1,s.x2,s.y2,e,i))return!0;return!1}static getOverlap(t,e){if(!Collision.rectRect(t,e))return null;return{x:Math.min(t.x+t.width,e.x+e.width)-Math.max(t.x,e.x),y:Math.min(t.y+t.height,e.y+e.height)-Math.max(t.y,e.y)}}static getMTV(t,e){const i=Collision.getOverlap(t,e);if(!i)return null;const s=t.x+t.width/2,n=t.y+t.height/2,a=e.x+e.width/2,r=e.y+e.height/2,o=s<a?-i.x:i.x,h=n<r?-i.y:i.y;return Math.abs(i.x)<Math.abs(i.y)?{x:o,y:0}:{x:0,y:h}}static sweep(t,e,i,s){const n=s.x-t.width/2,a=s.y-t.height/2,r=s.width+t.width,o=s.height+t.height,h=t.x+t.width/2,l=t.y+t.height/2;let c,u,d,_;0!==e?(c=(n-h)/e,u=(n+r-h)/e,c>u&&([c,u]=[u,c])):(c=h>=n&&h<=n+r?-1/0:1/0,u=h>=n&&h<=n+r?1/0:-1/0),0!==i?(d=(a-l)/i,_=(a+o-l)/i,d>_&&([d,_]=[_,d])):(d=l>=a&&l<=a+o?-1/0:1/0,_=l>=a&&l<=a+o?1/0:-1/0);const p=Math.max(c,d);if(p>Math.min(u,_)||p<0||p>1)return null;let g=0,f=0;return c>d?g=e>0?-1:1:f=i>0?-1:1,{time:p,normalX:g,normalY:f}}}class CollisionSystem{constructor(){this.groups=new Map,this.pairs=[],this.useQuadtree=!1}createGroup(t){return this.groups.has(t)||this.groups.set(t,new Set),this}add(t,e){const i=this.groups.get(t);if(!i)throw new Error(`Collision group '${t}' does not exist. Call createGroup('${t}') first.`);return i.add(e),this}remove(t,e){const i=this.groups.get(t);return!!i&&i.delete(e)}removeFromAll(t){for(const e of this.groups.values())e.delete(t)}clearGroup(t){const e=this.groups.get(t);e&&e.clear()}clearAll(){for(const t of this.groups.values())t.clear()}getGroup(t){const e=this.groups.get(t);return e?Array.from(e):[]}onCollision(t,e,i,s={}){return this.pairs.push({groupA:t,groupB:e,callback:i,once:s.once??!1}),this}offCollision(t,e){this.pairs=this.pairs.filter(i=>!(i.groupA===t&&i.groupB===e))}update(){for(const t of this.pairs)this._checkPair(t)}check(t,e){const i=this.groups.get(t),s=this.groups.get(e);if(!i||!s)return[];const n=[];for(const t of i){if(!this._isActive(t))continue;const e=this._getBounds(t);if(e)for(const i of s){if(t===i)continue;if(!this._isActive(i))continue;const s=this._getBounds(i);s&&(Collision.rectRect(e,s)&&n.push([t,i]))}}return n}checkAgainstGroup(t,e){const i=this.groups.get(e);if(!i)return null;const s=this._getBounds(t);if(!s)return null;for(const e of i){if(t===e)continue;if(!this._isActive(e))continue;const i=this._getBounds(e);if(i&&Collision.rectRect(s,i))return e}return null}checkAllAgainstGroup(t,e){const i=this.groups.get(e);if(!i)return[];const s=this._getBounds(t);if(!s)return[];const n=[];for(const e of i){if(t===e)continue;if(!this._isActive(e))continue;const i=this._getBounds(e);i&&(Collision.rectRect(s,i)&&n.push(e))}return n}_checkPair(t){const e=this.groups.get(t.groupA),i=this.groups.get(t.groupB);if(e&&i)for(const s of e){if(!this._isActive(s))continue;const e=this._getBounds(s);if(e)for(const n of i){if(s===n)continue;if(!this._isActive(n))continue;const i=this._getBounds(n);if(i&&(Collision.rectRect(e,i)&&(t.callback(s,n),t.once)))break}}}_getBounds(t){return"function"==typeof t.getBounds?t.getBounds():t.bounds?t.bounds:void 0!==t.x&&void 0!==t.y?{x:t.x-(t.width||0)/2,y:t.y-(t.height||0)/2,width:t.width||0,height:t.height||0}:null}_isActive(t){return!1!==t.active&&(!0!==t.destroyed&&!1!==t.alive)}}const DEFAULT_CONFIG={maxParticles:500,particleSize:20,particleColor:{r:100,g:180,b:255,a:.9},physics:"liquid",gravity:200,damping:.98,bounce:.3,maxSpeed:400,fluid:{smoothingRadius:null,restDensity:3,pressureStiffness:80,nearPressureStiffness:3,viscosity:.005,maxForce:5e3},gas:{interactionRadius:null,pressure:150,diffusion:.15,drag:.02,turbulence:50,buoyancy:300,sinking:200,repulsion:300},heat:{enabled:!1,heatZone:.88,coolZone:.25,rate:.03,heatMultiplier:1.5,coolMultiplier:2,middleMultiplier:.005,transitionWidth:.08,neutralTemp:.5,deadZone:.15,buoyancy:300,sinking:200},collision:{enabled:!0,strength:5e3},boundary:{enabled:!0,strength:4e3,radius:null},shake:{enabled:!0,sensitivity:2,maxForce:2500,damping:.8},blendMode:"source-over"};class FluidSystem extends ParticleSystem{constructor(t,e={}){var i,s,n,a;const r=FluidSystem._mergeConfig(e),o=(null==(i=e.bounds)?void 0:i.x)??0,h=(null==(s=e.bounds)?void 0:s.y)??0,l=e.width??(null==(n=e.bounds)?void 0:n.w)??0,c=e.height??(null==(a=e.bounds)?void 0:a.h)??0;super(t,{maxParticles:r.maxParticles,blendMode:r.blendMode,updaters:[Updaters.velocity,Updaters.lifetime],x:e.x??o+l/2,y:e.y??h+c/2,width:l,height:c,debug:e.debug??!1,debugColor:e.debugColor??"#0f0"}),this.config=r,this.bounds=e.bounds||null,this.gravityEnabled=e.gravityEnabled??!0,this.modeMix="gas"===r.physics?1:0,this._targetMode=this.modeMix,this._modeLerpSpeed=5,this._forces=[],this._shake={lastX:window.screenX,lastY:window.screenY,velocityX:0,velocityY:0,forceX:0,forceY:0},this._createEmitter()}static _mergeConfig(t){const e={...DEFAULT_CONFIG,...t},i=e.particleSize;return e.fluid={...DEFAULT_CONFIG.fluid,...t.fluid},e.gas={...DEFAULT_CONFIG.gas,...t.gas},e.heat={...DEFAULT_CONFIG.heat,...t.heat},e.collision={...DEFAULT_CONFIG.collision,...t.collision},e.boundary={...DEFAULT_CONFIG.boundary,...t.boundary},e.shake={...DEFAULT_CONFIG.shake,...t.shake},null===e.fluid.smoothingRadius&&(e.fluid.smoothingRadius=2*i),null===e.gas.interactionRadius&&(e.gas.interactionRadius=4*i),null===e.boundary.radius&&(e.boundary.radius=.8*i),e}_createEmitter(){const{particleSize:t,particleColor:e}=this.config,i=new ParticleEmitter({rate:0,position:{x:0,y:0},spread:{x:100,y:100},velocity:{x:0,y:0},velocitySpread:{x:10,y:10},size:{min:t,max:t+2},lifetime:{min:99999,max:99999},color:e,shape:"circle"});this.addEmitter("fluid",i)}spawn(t,e={}){const i=this.emitters.get("fluid");if(!i)return;let s=e.x,n=e.y,a=e.spreadX??100,r=e.spreadY??100;this.bounds&&void 0===s&&(s=this.bounds.x+this.bounds.w/2,n=this.bounds.y+.6*this.bounds.h,a=Math.min(.8*this.bounds.w,400),r=Math.min(.5*this.bounds.h,250)),i.position.x=s??this.game.width/2,i.position.y=n??this.game.height/2,i.spread.x=a,i.spread.y=r,this.burst(t,"fluid");for(const t of this.particles)t.custom.initialized||(t.custom.initialized=!0,t.custom.mass=1,t.custom.temperature=.5,t.vx=20*(Math.random()-.5),t.vy=20*(Math.random()-.5))}setBounds(t){this.bounds=t,this.x=t.x+t.w/2,this.y=t.y+t.h/2,this.width=t.w,this.height=t.h}update(t){t=Math.min(t,.033),this.modeMix=Easing.lerp(this.modeMix,this._targetMode,t*this._modeLerpSpeed);const e=this.particles;0!==e.length?(this._ensureForceArray(e.length),this._resetForces(),this._computePhysicsForces(e),this.modeMix>.5&&((this.config.heat.enabled||this.modeMix>.95)&&(this._updateTemperatures(e),this._applyThermalForces(e)),this.modeMix>.95&&this._applyGasRepulsion(e)),this.config.collision.enabled&&Collision.applyCircleSeparation(e,this._forces,{strength:this.config.collision.strength,useSizeAsRadius:!0}),this.bounds&&this.config.boundary.enabled&&this._applyBoundaryForces(e),this.config.shake.enabled&&(this._updateShakeForces(t),this._applyShakeForces()),this._integrateForces(e,t),super.update(t),this.bounds&&this._clampBounds(e)):super.update(t)}_ensureForceArray(t){for(;this._forces.length<t;)this._forces.push({x:0,y:0})}_resetForces(){for(let t=0;t<this._forces.length;t++)this._forces[t].x=0,this._forces[t].y=0}_computePhysicsForces(t){const{fluid:e,gas:i}=this.config;if(this.modeMix<.01){const i=computeFluidForces(t,{kernel:{smoothingRadius:e.smoothingRadius},fluid:{restDensity:e.restDensity,pressureStiffness:e.pressureStiffness,nearPressureStiffness:e.nearPressureStiffness,viscosity:e.viscosity,maxForce:e.maxForce}});this._accumulateForces(i.forces)}else if(this.modeMix>.95){const e=computeGasForces(t,{gas:{interactionRadius:i.interactionRadius,pressure:i.pressure,diffusion:i.diffusion,drag:i.drag,turbulence:i.turbulence,buoyancy:i.buoyancy}});this._accumulateForces(e.forces)}else{const s=computeFluidForces(t,{kernel:{smoothingRadius:e.smoothingRadius},fluid:e}),n=computeGasForces(t,{gas:i}),a=blendForces(s.forces,n.forces,this.modeMix);this._accumulateForces(a)}}_accumulateForces(t){const e=Math.min(t.length,this._forces.length);for(let i=0;i<e;i++)this._forces[i].x+=t[i].x,this._forces[i].y+=t[i].y}_applyBoundaryForces(t){const{x:e,y:i,w:s,h:n}=this.bounds,{radius:a,strength:r}=this.config.boundary,o=e,h=e+s,l=i,c=i+n;for(let e=0;e<t.length;e++){const i=t[e],s=.5*i.size,n=i.x-s-o,u=h-i.x-s,d=i.y-s-l,_=c-i.y-s;if(n<a){const t=Math.max(0,1-n/a);this._forces[e].x+=r*t*t}if(u<a){const t=Math.max(0,1-u/a);this._forces[e].x-=r*t*t}if(d<a){const t=Math.max(0,1-d/a);this._forces[e].y+=r*t*t}if(_<a){const t=Math.max(0,1-_/a);this._forces[e].y-=r*t*t}}}_updateTemperatures(t){if(!this.bounds)return;const{heat:e}=this.config,i=this.bounds.y,s=this.bounds.h;for(let n=0;n<t.length;n++){const a=t[n],r=a.custom.temperature??e.neutralTemp,o=zoneTemperature(Math.min(1,Math.max(0,(a.y-i)/s)),r,e);a.custom.temperature=Math.min(1,Math.max(0,o))}}_applyThermalForces(t){const{heat:e}=this.config,i=e.neutralTemp,s=e.deadZone;for(let n=0;n<t.length;n++){const a=(t[n].custom.temperature??i)-i;if(Math.abs(a)>s){let t=0;if(a>0){t=-(a-s)*e.buoyancy*2}else{t=(-a-s)*e.sinking*2}this._forces[n].y+=t}}}_applyGasRepulsion(t){const{gas:e}=this.config,i=e.interactionRadius,s=i*i,n=e.repulsion||200,a=t.length;for(let e=0;e<a;e++){const r=t[e];for(let o=e+1;o<a;o++){const a=t[o],h=r.x-a.x,l=r.y-a.y,c=h*h+l*l;if(c>=s||c<1)continue;const u=Math.sqrt(c),d=1-u/i,_=n*d*d*d,p=h/u*_,g=l/u*_;this._forces[e].x+=p,this._forces[e].y+=g,this._forces[o].x-=p,this._forces[o].y-=g}}}_updateShakeForces(t){const{shake:e}=this.config;if(!e.enabled)return;const i=window.screenX,s=window.screenY,n=i-this._shake.lastX,a=s-this._shake.lastY;t>0&&t<.1&&(this._shake.velocityX=n/t,this._shake.velocityY=a/t),this._shake.lastX=i,this._shake.lastY=s;const r=-this._shake.velocityX*e.sensitivity,o=-this._shake.velocityY*e.sensitivity;this._shake.forceX=Easing.lerp(this._shake.forceX,r,1-e.damping),this._shake.forceY=Easing.lerp(this._shake.forceY,o,1-e.damping);const h=Math.sqrt(this._shake.forceX*this._shake.forceX+this._shake.forceY*this._shake.forceY);if(h>e.maxForce){const t=e.maxForce/h;this._shake.forceX*=t,this._shake.forceY*=t}}_applyShakeForces(){const t=this._shake.forceX,e=this._shake.forceY;if(!(Math.abs(t)<1&&Math.abs(e)<1))for(let i=0;i<this._forces.length;i++)this._forces[i].x+=t,this._forces[i].y+=e}_integrateForces(t,e){const{gravity:i,damping:s,maxSpeed:n,heat:a}=this.config,r=n*n,o=Easing.lerp(s,.995,this.modeMix);for(let s=0;s<t.length;s++){const h=t[s],l=this._forces[s],c=h.custom.mass||1;let u,d;if(this.modeMix>.5){const t=h.custom.temperature??a.neutralTemp;u=c*Easing.lerp(1.5,.3,t),d=this.gravityEnabled?i*Easing.lerp(1.2,.6,t):0}else u=c,d=this.gravityEnabled?i:0;h.vx+=l.x/u*e,h.vy+=(l.y/u+d)*e,h.vx*=o,h.vy*=o;const _=h.vx*h.vx+h.vy*h.vy;if(_>r){const t=n/Math.sqrt(_);h.vx*=t,h.vy*=t}}}_clampBounds(t){const{x:e,y:i,w:s,h:n}=this.bounds,{bounce:a}=this.config;for(let r=0;r<t.length;r++){const o=t[r],h=.5*o.size,l=e+h,c=e+s-h,u=i+h,d=i+n-h;o.x<l?(o.x=l,o.vx=Math.abs(o.vx)*a):o.x>c&&(o.x=c,o.vx=-Math.abs(o.vx)*a),o.y<u?(o.y=u,o.vy=Math.abs(o.vy)*a):o.y>d&&(o.y=d,o.vy=-Math.abs(o.vy)*a)}}reset(){const t=this.particles.length;this.particles.length=0,this._forces.length=0,this.spawn(t)}toggleGravity(){return this.gravityEnabled=!this.gravityEnabled,this.gravityEnabled}setPhysicsMode(t,e=!1){let i;if("liquid"===t)i=0;else if("gas"===t)i=1;else{if("number"!=typeof t)return;i=Math.max(0,Math.min(1,t))}this._targetMode=i,e&&(this.modeMix=i)}getPhysicsMode(){return this.modeMix<.01?"liquid":this.modeMix>.99?"gas":"blending"}isHeatEnabled(){return this.config.heat.enabled||this.modeMix>.5}}const _FluentGO=class t{constructor(t,e,i,s){__privateAdd(this,_FluentGO_instances),__privateAdd(this,_parent),__privateAdd(this,_go),__privateAdd(this,_refs),__privateAdd(this,_state),__privateAdd(this,_shapes,[]),__privateAdd(this,_motions,[]),__privateSet(this,_parent,t),__privateSet(this,_go,e),__privateSet(this,_refs,i),__privateSet(this,_state,s)}circle(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=30,...s}=e,n=new Circle(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}rect(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),i=new Rectangle(e);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,i)}roundRect(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=10,...s}=e,n=new RoundedRectangle(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}square(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{size:i=50,...s}=e,n=new Square(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}star(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=40,points:s=5,inset:n=.5,...a}=e,r=new Star(i,s,n,a);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,r)}triangle(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{size:i=50,...s}=e,n=new Triangle(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}poly(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),i=new Polygon(e);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,i)}line(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{length:i=40,...s}=e,n=new Line(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}hexagon(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=30,...s}=e,n=new Hexagon(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}diamond(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),i=new Diamond(e);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,i)}heart(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{size:i,...s}=e;i&&!s.width&&(s.width=i),i&&!s.height&&(s.height=i);const n=new Heart(s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}arc(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=30,startAngle:s=0,endAngle:n=Math.PI,...a}=e,r=new Arc(i,s,n,a);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,r)}ring(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{outerRadius:i=40,innerRadius:s=20,...n}=e,a=new Ring(i,s,n);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,a)}arrow(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{length:i=50,...s}=e,n=new Arrow(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}cross(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{size:i=40,thickness:s=10,...n}=e,a=new Cross(i,s,n);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,a)}pin(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{radius:i=20,...s}=e,n=new Pin(i,s);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}cloud(t={}){const e=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,t),{size:i,width:s,height:n,...a}=e,r=i||Math.min(s||40,n||40),o=new Cloud(r,a);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,o)}text(t,e={}){const i=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,e);i.fillColor&&(i.color=i.fillColor,delete i.fillColor);const s=new TextShape(t,i);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,s)}image(t,e={}){const i=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,e);if("string"==typeof t){const s=new Image;s.src=t;const n=new ImageShape(s,i);return s.onload=()=>{n._bitmap=s,n._width=e.width??s.width,n._height=e.height??s.height},__privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,n)}{const e=new ImageShape(t,i);return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,e)}}svg(t,e={}){const i=__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,e),s=new SVGShape({path:t,...i});return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,s)}add(t,e={}){const i=new t(__privateMethod(this,_FluentGO_instances,normalizeShapeOpts_fn).call(this,e));return __privateMethod(this,_FluentGO_instances,addShapeInstance_fn).call(this,i)}oscillate(t={}){return this.motion("oscillate",t)}pulse(t={}){return this.motion("pulse",t)}orbit(t={}){return this.motion("orbit",t)}float(t={}){return this.motion("float",t)}shake(t={}){return this.motion("shake",t)}bounce(t={}){return this.motion("bounce",t)}spring(t={}){return this.motion("spring",t)}spiral(t={}){return this.motion("spiral",t)}pendulum(t={}){return this.motion("pendulum",t)}waypoint(t={}){return this.motion("waypoint",t)}motion(t,e={}){var i;if(__privateGet(this,_motions).push({type:t,opts:e}),!__privateGet(this,_go)._fluentMotions){__privateGet(this,_go)._fluentMotions=[],__privateGet(this,_go)._motionTime=0,__privateGet(this,_go)._baseX=__privateGet(this,_go).x,__privateGet(this,_go)._baseY=__privateGet(this,_go).y;const t=(null==(i=__privateGet(this,_go).update)?void 0:i.bind(__privateGet(this,_go)))||(()=>{}),e=this;__privateGet(this,_go).update=function(i){var s;t(i),this._motionTime+=i,__privateMethod(s=e,_FluentGO_instances,processMotions_fn).call(s,i)}}return __privateGet(this,_go)._fluentMotions.push({type:t,opts:e,state:null}),this}tween(t,e={}){const{duration:i=1,easing:s="easeOutQuad",delay:n=0,onComplete:a}=e;return n>0?setTimeout(()=>{Tweenetik.to(__privateGet(this,_go),t,i,s,{onComplete:a})},1e3*n):Tweenetik.to(__privateGet(this,_go),t,i,s,{onComplete:a}),this}child(e,i,s){let n,a,r;"function"==typeof e&&e.prototype?(n=e,"function"==typeof i?(a={},r=i):(a=i||{},r=s)):(n=GameObject,a=e||{},r=i);const o=new n(__privateGet(this,_go).game,a);__privateGet(this,_go).addChild(o),a.name&&(__privateGet(this,_refs)[a.name]=o);const h=new t(this,o,__privateGet(this,_refs),__privateGet(this,_state));return r?(r(h),this):h}on(t,e){const i={go:__privateGet(this,_go),shapes:__privateGet(this,_shapes),refs:__privateGet(this,_refs),state:__privateGet(this,_state)};return["click","mousedown","mouseup","mousemove","mouseover","mouseout","inputdown","inputup","inputmove"].includes(t)&&(__privateGet(this,_go).interactive=!0),__privateGet(this,_go).events&&__privateGet(this,_go).events.on(t,t=>e(i,t)),this}update(t){var e;const i=(null==(e=__privateGet(this,_go).update)?void 0:e.bind(__privateGet(this,_go)))||(()=>{}),s=__privateGet(this,_shapes),n=__privateGet(this,_refs),a=__privateGet(this,_state),r=__privateGet(this,_go);return __privateGet(this,_go).update=e=>{i(e),t(e,{go:r,shapes:s,refs:n,state:a})},this}pos(t,e){return __privateGet(this,_go).x=t,__privateGet(this,_go).y=e,this}scale(t,e){return __privateGet(this,_go).scaleX=t,__privateGet(this,_go).scaleY=e??t,this}rotate(t){return __privateGet(this,_go).rotation=t*(Math.PI/180),this}opacity(t){return __privateGet(this,_go).opacity=t,__privateGet(this,_go).renderable&&(__privateGet(this,_go).renderable.opacity=t),this}zIndex(t){return __privateGet(this,_go).zIndex=t,this}end(){return __privateGet(this,_parent)}go(t){var e;let i=__privateGet(this,_parent);for(;i&&!i.sceneInstance;)i=null==(e=i.end)?void 0:e.call(i);if(i&&i.go)return i.go(t);throw new Error("Cannot find scene context")}scene(t,e){var i;let s=__privateGet(this,_parent);for(;s&&!s.sceneInstance;)s=null==(i=s.end)?void 0:i.call(s);if(s&&s.scene)return s.scene(t,e);throw new Error("Cannot find game context")}start(){let t=__privateGet(this,_parent);for(;t&&t.end;){const e=t.end();if(e===t)break;t=e}return t.start()}get goInstance(){return __privateGet(this,_go)}get shapes(){return __privateGet(this,_shapes)}get refs(){return __privateGet(this,_refs)}get state(){return __privateGet(this,_state)}};_parent=new WeakMap,_go=new WeakMap,_refs=new WeakMap,_state=new WeakMap,_shapes=new WeakMap,_motions=new WeakMap,_FluentGO_instances=new WeakSet,addShapeInstance_fn=function(t){var e;if(__privateGet(this,_shapes).push(t),1===__privateGet(this,_shapes).length){__privateGet(this,_go)._fluentShape=t,__privateGet(this,_go).renderable=t;const i=(null==(e=__privateGet(this,_go).draw)?void 0:e.bind(__privateGet(this,_go)))||(()=>{});__privateGet(this,_go).draw=function(){i(),this._fluentShape&&this.visible&&this._fluentShape.render()};const s=__privateGet(this,_go);__privateGet(this,_go).getBounds=function(){return s._fluentShape&&s._fluentShape.getBounds?s._fluentShape.getBounds():null}}else{if(!(__privateGet(this,_go)._fluentShape instanceof Group)){const t=__privateGet(this,_shapes)[0],e=new Group;e.add(t),__privateGet(this,_go)._fluentShape=e,__privateGet(this,_go).renderable=e}__privateGet(this,_go)._fluentShape.add(t)}return this},normalizeShapeOpts_fn=function(t){const e={...t};return void 0!==t.fill&&(e.color=t.fill,e.fillColor=t.fill,delete e.fill),void 0!==t.stroke&&(e.strokeColor=t.stroke,delete e.stroke),e},processMotions_fn=function(t){for(const e of __privateGet(this,_go)._fluentMotions){const i=__privateMethod(this,_FluentGO_instances,applyMotion_fn).call(this,e,t);e.state=null==i?void 0:i.state}},applyMotion_fn=function(t,e){const{type:i,opts:s,state:n}=t,a=__privateGet(this,_go)._motionTime,r=__privateGet(this,_go);switch(i){case"oscillate":{const{prop:t="y",min:e=-50,max:i=50,duration:n=2}=s,o=Motion.oscillate(e,i,a,n,!0),h=`_base_${t}`;return void 0===r[h]&&(r[h]=r[t]),r[t]=r[h]+o.value,o}case"pulse":{const{prop:t="scale",min:e=.8,max:i=1.2,duration:n=1}=s,o=Motion.pulse(e,i,a,n,!0);return"scale"===t?(r.scaleX=o.value,r.scaleY=o.value):"opacity"===t&&r.renderable?r.renderable.opacity=o.value:r[t]=o.value,o}case"orbit":{const{centerX:t=r._baseX,centerY:e=r._baseY,radiusX:i=100,radiusY:n=100,duration:o=3,clockwise:h=!0}=s;r._orbitCenter||(r._orbitCenter={x:t,y:e});const l=Motion.orbit(r._orbitCenter.x,r._orbitCenter.y,i,n,0,a,o,!0,h);return r.x=l.x,r.y=l.y,l}case"float":{const{radius:t=20,speed:e=.5,randomness:i=.3,duration:n=5}=s;r._floatState||(r._floatState={baseX:r._baseX,baseY:r._baseY});const o=Motion.float(r._floatState,a,n,e,i,t,!0);return r.x=o.x,r.y=o.y,o}case"shake":{const{intensity:t=5,frequency:e=20,decay:i=.9,duration:n=.5}=s,o=Motion.shake(r._baseX,r._baseY,t,t,e,i,a,n,!0);return r.x=o.x,r.y=o.y,o}case"bounce":{const{height:t=100,bounces:e=3,duration:i=2}=s,n=Motion.bounce(t,r._baseY,e,a,i,!0);return r.y=n.y,n}case"spiral":{const{startRadius:t=50,endRadius:e=150,revolutions:i=3,duration:n=4}=s;r._spiralCenter||(r._spiralCenter={x:r._baseX,y:r._baseY});const o=Motion.spiral(r._spiralCenter.x,r._spiralCenter.y,t,e,0,i,a,n,!0);return r.x=o.x,r.y=o.y,o}case"pendulum":{const{amplitude:t=45,duration:e=2,damped:i=!1}=s,n=Motion.pendulum(0,t,a,e,!0,i);return r.rotation=n.value*(Math.PI/180),n}case"waypoint":{const{waypoints:t=[],speed:e=100,waitTime:i=0}=s;if(0===t.length)return{state:null};const o=Motion.waypoint(r,a,t,e,i,!0,null,n);return r.x=o.x??r.x,r.y=o.y??r.y,o}default:return console.warn(`Unknown motion type: ${i}`),{state:null}}};let FluentGO=_FluentGO;class FluentLayer{constructor(t,e,i,s,n){__privateAdd(this,_parent2),__privateAdd(this,_layerGO),__privateAdd(this,_group),__privateAdd(this,_refs2),__privateAdd(this,_state2),__privateSet(this,_parent2,t),__privateSet(this,_layerGO,e),__privateSet(this,_group,i),__privateSet(this,_refs2,s),__privateSet(this,_state2,n)}go(t={},e){var i;const{name:s,x:n=0,y:a=0,visible:r=!0,...o}=t,h=new GameObject({x:n,y:a,visible:r,...o});(null==(i=__privateGet(this,_parent2).parent)?void 0:i.game)&&(h.game=__privateGet(this,_parent2).parent.game),__privateGet(this,_group).add(h),s&&(h.name=s,__privateGet(this,_refs2)[s]=h);const l=new FluentGO(this,h,__privateGet(this,_refs2),__privateGet(this,_state2));return e?(e(l),this):l}visible(t){return __privateGet(this,_layerGO).visible=t,this}opacity(t){return __privateGet(this,_group).opacity=t,this}endLayer(){return __privateGet(this,_parent2)}end(){return __privateGet(this,_parent2)}scene(t,e){return __privateGet(this,_parent2).scene(t,e)}start(){return __privateGet(this,_parent2).start()}get layerGO(){return __privateGet(this,_layerGO)}get group(){return __privateGet(this,_group)}get refs(){return __privateGet(this,_refs2)}get state(){return __privateGet(this,_state2)}}_parent2=new WeakMap,_layerGO=new WeakMap,_group=new WeakMap,_refs2=new WeakMap,_state2=new WeakMap;class FluentScene{constructor(t,e,i,s){__privateAdd(this,_parent3),__privateAdd(this,_scene),__privateAdd(this,_refs3),__privateAdd(this,_state3),__privateAdd(this,_lastGO,null),__privateSet(this,_parent3,t),__privateSet(this,_scene,e),__privateSet(this,_refs3,i),__privateSet(this,_state3,s)}go(t,e,i){let s,n,a;"function"==typeof t&&t.prototype?(s=t,"function"==typeof e?(n={},a=e):(n=e||{},a=i)):(s=GameObject,n=t||{},a=e);const{name:r,x:o=0,y:h=0,visible:l=!0,...c}=n,u=new s(__privateGet(this,_parent3).game,{x:o,y:h,visible:l,...c});__privateGet(this,_scene).add(u),__privateSet(this,_lastGO,u),r&&(u.name=r,__privateGet(this,_refs3)[r]=u);const d=new FluentGO(this,u,__privateGet(this,_refs3),__privateGet(this,_state3));return a?(a(d),this):d}group(t,e){const i="string"==typeof t?t:null,s=[];return("function"==typeof t?t:e)({go:t=>{const e=this.go(t);return s.push(e.goInstance),e}}),i&&(__privateGet(this,_refs3)[i]=s),this}layer(t,e=0){var i;const s=new Group({zIndex:e});s.name=t;const n=new GameObject(__privateGet(this,_parent3).game,{x:0,y:0});n._fluentShape=s,n.renderable=s,n.zIndex=e;const a=(null==(i=n.draw)?void 0:i.bind(n))||(()=>{});return n.draw=function(){a(),this._fluentShape&&this.visible&&this._fluentShape.render()},__privateGet(this,_scene).add(n),t&&(__privateGet(this,_refs3)[`${__privateGet(this,_scene).name}_${t}`]=n),new FluentLayer(this,n,s,__privateGet(this,_refs3),__privateGet(this,_state3))}onEnter(t){return __privateGet(this,_scene)._onEnter=t,this}onExit(t){return __privateGet(this,_scene)._onExit=t,this}scene(t,e){return __privateGet(this,_parent3).scene(t,e)}end(){return __privateGet(this,_parent3)}start(){return __privateGet(this,_parent3).start()}stop(){return __privateGet(this,_parent3).stop()}on(t,e){return __privateGet(this,_parent3).on(t,e)}use(t){return __privateGet(this,_parent3).use(t)}state(t){return __privateGet(this,_parent3).state(t)}get sceneInstance(){return __privateGet(this,_scene)}get refs(){return __privateGet(this,_refs3)}get state(){return __privateGet(this,_state3)}get parent(){return __privateGet(this,_parent3)}}function gcanvas(t={}){return new FluentGame(t)}_parent3=new WeakMap,_scene=new WeakMap,_refs3=new WeakMap,_state3=new WeakMap,_lastGO=new WeakMap;class FluentGame{constructor(t={}){__privateAdd(this,_FluentGame_instances),__privateAdd(this,_game),__privateAdd(this,_scenes,new Map),__privateAdd(this,_currentScene,null),__privateAdd(this,_refs4,{}),__privateAdd(this,_state4,{}),__privateAdd(this,_plugins,[]),__privateAdd(this,_canvas,null);const{canvas:e,width:i=800,height:s=600,bg:n,fluid:a,container:r,fps:o=60,pixelRatio:h=("undefined"!=typeof window?window.devicePixelRatio:1)}=t;__privateSet(this,_canvas,e||__privateMethod(this,_FluentGame_instances,createCanvas_fn).call(this,i,s,r)),__privateSet(this,_game,new Game(__privateGet(this,_canvas))),__privateGet(this,_game).init(),n&&(__privateGet(this,_game).backgroundColor=n);(void 0!==a?a:!e)&&__privateGet(this,_game).enableFluidSize(),60!==o&&(__privateGet(this,_game).targetFPS=o),__privateGet(this,_game)._pixelRatio=h}scene(t,e,i){let s,n,a;"function"==typeof t?(n=t,s=n.name||"custom_scene",a=e||{}):"function"==typeof e?(s=t,n=e,a=i||{}):(s=t,n=Scene,a=e||{});const{zIndex:r=0,active:o=!0,onEnter:h,onExit:l,...c}=a;let u=__privateGet(this,_scenes).get(s);return u||(u=new n(__privateGet(this,_game),c),u.name=s,u.zIndex=r,u.visible=o,u._onEnter=h,u._onExit=l,__privateGet(this,_scenes).set(s,u),__privateGet(this,_game).pipeline.add(u)),__privateSet(this,_currentScene,u),new FluentScene(this,u,__privateGet(this,_refs4),__privateGet(this,_state4))}inScene(t){const e=__privateGet(this,_scenes).get(t);if(!e)throw new Error(`Scene '${t}' does not exist. Use .scene('${t}') to create it.`);return __privateSet(this,_currentScene,e),new FluentScene(this,e,__privateGet(this,_refs4),__privateGet(this,_state4))}go(t,e){return __privateGet(this,_currentScene)||this.scene("default"),new FluentScene(this,__privateGet(this,_currentScene),__privateGet(this,_refs4),__privateGet(this,_state4)).go(t,e)}showScene(t){var e;const i=__privateGet(this,_scenes).get(t);return i&&(i.visible=!0,null==(e=i._onEnter)||e.call(i,__privateMethod(this,_FluentGame_instances,createContext_fn).call(this))),this}hideScene(t){var e;const i=__privateGet(this,_scenes).get(t);return i&&(null==(e=i._onExit)||e.call(i,__privateMethod(this,_FluentGame_instances,createContext_fn).call(this)),i.visible=!1),this}transition(t,e,i={}){const{fade:s=0,onComplete:n}=i;return this.hideScene(t),this.showScene(e),null==n||n(),this}state(t){return Object.assign(__privateGet(this,_state4),t),this}getState(t){return __privateGet(this,_state4)[t]}setState(t,e){return __privateGet(this,_state4)[t]=e,this}on(t,e){const i=__privateMethod(this,_FluentGame_instances,createContext_fn).call(this);if("update"===t){const t=__privateGet(this,_game).update.bind(__privateGet(this,_game));__privateGet(this,_game).update=s=>{t(s),e(s,i)}}else if(t.startsWith("keydown:")){const s=t.split(":")[1];__privateGet(this,_game).events.on("keydown",t=>{var n,a;(null==(n=t.key)?void 0:n.toLowerCase())!==s.toLowerCase()&&(null==(a=t.code)?void 0:a.toLowerCase())!==s.toLowerCase()||e(i,t)})}else if(t.startsWith("keyup:")){const s=t.split(":")[1];__privateGet(this,_game).events.on("keyup",t=>{var n;(null==(n=t.key)?void 0:n.toLowerCase())===s.toLowerCase()&&e(i,t)})}else __privateGet(this,_game).events.on(t,t=>e(i,t));return this}use(t){return t(this),__privateGet(this,_plugins).push(t),this}start(){return __privateGet(this,_game).start(),this}stop(){return __privateGet(this,_game).stop(),this}restart(){return __privateGet(this,_game).restart(),this}get game(){return __privateGet(this,_game)}get refs(){return __privateGet(this,_refs4)}get scenes(){return __privateGet(this,_scenes)}get canvas(){return __privateGet(this,_canvas)}get width(){return __privateGet(this,_canvas).width}get height(){return __privateGet(this,_canvas).height}}function sketch(t=800,e=600,i="black"){const s=[];let n=null,a=null,r=null;const o={circle:(t,e,i,n="white")=>(s.push({type:"circle",x:t,y:e,radius:i,fill:n}),o),rect:(t,e,i,n,a="white")=>(s.push({type:"rect",x:t,y:e,width:i,height:n,fill:a}),o),square:(t,e,i,n="white")=>(s.push({type:"rect",x:t,y:e,width:i,height:i,fill:n}),o),star:(t,e,i,n,a="white")=>(s.push({type:"star",x:t,y:e,points:i,radius:n,fill:a}),o),triangle:(t,e,i,n="white")=>(s.push({type:"triangle",x:t,y:e,size:i,fill:n}),o),hexagon:(t,e,i,n="white")=>(s.push({type:"hexagon",x:t,y:e,radius:i,fill:n}),o),line:(t,e,i,n,a="white",r=1)=>(s.push({type:"line",x:t,y:e,x2:i,y2:n,stroke:a,lineWidth:r}),o),ring:(t,e,i,n,a="white")=>(s.push({type:"ring",x:t,y:e,innerRadius:i,outerRadius:n,fill:a}),o),text:(t,e,i,n={})=>(s.push({type:"text",content:t,x:e,y:i,...n}),o),grid:(t,e,i,s)=>{for(let n=0;n<t;n++)for(let t=0;t<e;t++){s(o,n*i+i/2,t*i+i/2,n,t)}return o},repeat:(t,e)=>{for(let i=0;i<t;i++)e(o,i,t);return o},radial:(t,e,i,s,n)=>{for(let a=0;a<s;a++){const r=a/s*Math.PI*2,h=t+Math.cos(r)*i,l=e+Math.sin(r)*i;n(o,h,l,r,a)}return o},setup:t=>(a=t,o),update:t=>(n=t,o),start:()=>{const h=gcanvas({width:t,height:e,bg:i});null==a||a(o);const l=[];let c=h.scene("default");if(s.forEach((t,e)=>{const i=c.go({x:t.x??0,y:t.y??0,name:`shape_${e}`});switch(t.type){case"circle":i.circle({radius:t.radius,fill:t.fill});break;case"rect":i.rect({width:t.width,height:t.height,fill:t.fill});break;case"star":i.star({points:t.points,radius:t.radius,fill:t.fill});break;case"triangle":i.triangle({size:t.size,fill:t.fill});break;case"hexagon":i.hexagon({radius:t.radius,fill:t.fill});break;case"line":i.line({x2:t.x2-t.x,y2:t.y2-t.y,stroke:t.stroke,lineWidth:t.lineWidth});break;case"ring":i.ring({innerRadius:t.innerRadius,outerRadius:t.outerRadius,fill:t.fill});break;case"text":i.text(t.content,{fill:t.fill??"white",font:t.font??"16px monospace"})}l.push(i.goInstance),c=i.end()}),n){let i=0,s=0;h.on("update",(a,r)=>{var o,h;i++,s+=a,n(a,{shapes:l,time:s,frame:i,width:t,height:e,mouse:{x:(null==(o=r.game.mouse)?void 0:o.x)??0,y:(null==(h=r.game.mouse)?void 0:h.y)??0},refs:r.refs,game:r.game})})}return r=h,h.start()},get width(){return t},get height(){return e},get game(){return r}};return o}_game=new WeakMap,_scenes=new WeakMap,_currentScene=new WeakMap,_refs4=new WeakMap,_state4=new WeakMap,_plugins=new WeakMap,_canvas=new WeakMap,_FluentGame_instances=new WeakSet,createCanvas_fn=function(t,e,i){const s=document.createElement("canvas");s.width=t,s.height=e,s.style.display="block";return(i||document.body).appendChild(s),s},createContext_fn=function(){return{refs:__privateGet(this,_refs4),state:__privateGet(this,_state4),scenes:Object.fromEntries(__privateGet(this,_scenes)),game:__privateGet(this,_game),width:__privateGet(this,_canvas).width,height:__privateGet(this,_canvas).height,showScene:t=>this.showScene(t),hideScene:t=>this.hideScene(t),transition:(t,e,i)=>this.transition(t,e,i)}};class SynthEnvelope{static applyADSR(t,e={}){const{attack:i=.01,decay:s=.1,sustain:n=.7,release:a=.2,startTime:r=0,duration:o=1,peakVolume:h=1}=e,l=h*n,c=Math.max(0,o-i-s);t.setValueAtTime(0,r),t.linearRampToValueAtTime(h,r+i),t.linearRampToValueAtTime(l,r+i+s),t.setValueAtTime(l,r+i+s+c),t.linearRampToValueAtTime(0,r+o+a)}static get presets(){return{pluck:{attack:.001,decay:.2,sustain:0,release:.1},pad:{attack:.5,decay:.3,sustain:.8,release:1},organ:{attack:.01,decay:0,sustain:1,release:.05},perc:{attack:.001,decay:.1,sustain:0,release:.05},string:{attack:.1,decay:.2,sustain:.7,release:.3},brass:{attack:.05,decay:.1,sustain:.8,release:.2},blip:{attack:.001,decay:.05,sustain:0,release:.02},laser:{attack:.001,decay:.15,sustain:0,release:.05},explosion:{attack:.001,decay:.3,sustain:.2,release:.5}}}}class SynthOscillators{static init(t,e){__privateSet(this,__ctx,t),__privateSet(this,__output,e)}static get ctx(){return __privateGet(this,__ctx)}static get now(){return __privateGet(this,__ctx).currentTime}static tone(t,e,i={}){const{type:s="sine",volume:n=.5,attack:a=.01,decay:r=.1,sustain:o=.7,release:h=.2,detune:l=0,startTime:c=__privateGet(this,__ctx).currentTime}=i,u=__privateGet(this,__ctx).createOscillator(),d=__privateGet(this,__ctx).createGain();return u.type=s,u.frequency.setValueAtTime(t,c),u.detune.setValueAtTime(l,c),SynthEnvelope.applyADSR(d.gain,{attack:a,decay:r,sustain:o,release:h,startTime:c,duration:e,peakVolume:n}),u.connect(d),d.connect(__privateGet(this,__output)),u.start(c),u.stop(c+e+h),u}static continuous(t={}){const{type:e="sine",frequency:i=440,volume:s=.5}=t,n=__privateGet(this,__ctx).createOscillator(),a=__privateGet(this,__ctx).createGain();n.type=e,n.frequency.value=i,a.gain.value=s,n.connect(a),a.connect(__privateGet(this,__output)),n.start();const r=__privateGet(this,__ctx);return{osc:n,gain:a,setFrequency:(t,e=0)=>{e>0?n.frequency.linearRampToValueAtTime(t,r.currentTime+e):n.frequency.setValueAtTime(t,r.currentTime)},setVolume:(t,e=0)=>{e>0?a.gain.linearRampToValueAtTime(t,r.currentTime+e):a.gain.setValueAtTime(t,r.currentTime)},stop:(t=0)=>{t>0?(a.gain.linearRampToValueAtTime(0,r.currentTime+t),n.stop(r.currentTime+t+.01)):n.stop()}}}static fm(t,e,i,s,n={}){const{volume:a=.5,startTime:r=__privateGet(this,__ctx).currentTime}=n,o=__privateGet(this,__ctx).createOscillator(),h=__privateGet(this,__ctx).createOscillator(),l=__privateGet(this,__ctx).createGain(),c=__privateGet(this,__ctx).createGain();return h.frequency.value=e,l.gain.value=i,o.frequency.value=t,c.gain.value=a,h.connect(l),l.connect(o.frequency),o.connect(c),c.connect(__privateGet(this,__output)),c.gain.setValueAtTime(a,r),c.gain.linearRampToValueAtTime(0,r+s),h.start(r),o.start(r),h.stop(r+s+.1),o.stop(r+s+.1),{carrier:o,modulator:h,outputGain:c}}static additive(t,e,i,s={}){const{volume:n=.5,startTime:a=__privateGet(this,__ctx).currentTime}=s,r=[],o=__privateGet(this,__ctx).createGain();return o.gain.value=n/e.length,o.connect(__privateGet(this,__output)),o.gain.setValueAtTime(n/e.length,a),o.gain.linearRampToValueAtTime(0,a+i),e.forEach((e,s)=>{if(e>0){const n=__privateGet(this,__ctx).createOscillator(),h=__privateGet(this,__ctx).createGain();n.frequency.value=t*(s+1),h.gain.value=e,n.connect(h),h.connect(o),n.start(a),n.stop(a+i+.1),r.push(n)}}),r}static sweep(t,e,i,s={}){const{type:n="sine",volume:a=.5,exponential:r=!0,startTime:o=__privateGet(this,__ctx).currentTime}=s,h=__privateGet(this,__ctx).createOscillator(),l=__privateGet(this,__ctx).createGain();return h.type=n,h.frequency.setValueAtTime(t,o),r&&e>0?h.frequency.exponentialRampToValueAtTime(e,o+i):h.frequency.linearRampToValueAtTime(e,o+i),l.gain.setValueAtTime(a,o),l.gain.linearRampToValueAtTime(0,o+i),h.connect(l),l.connect(__privateGet(this,__output)),h.start(o),h.stop(o+i+.01),h}static pulse(t,e,i=.5,s={}){const{volume:n=.5,startTime:a=__privateGet(this,__ctx).currentTime}=s,r=__privateGet(this,__ctx).createOscillator(),o=__privateGet(this,__ctx).createOscillator(),h=__privateGet(this,__ctx).createGain(),l=__privateGet(this,__ctx).createGain(),c=__privateGet(this,__ctx).createGain();return r.type="sawtooth",o.type="sawtooth",r.frequency.value=t,o.frequency.value=t,h.gain.value=.5,l.gain.value=-.5,c.gain.setValueAtTime(n,a),c.gain.linearRampToValueAtTime(0,a+e),r.connect(h),o.connect(l),h.connect(c),l.connect(c),c.connect(__privateGet(this,__output)),r.start(a),o.start(a),r.stop(a+e+.01),o.stop(a+e+.01),{osc1:r,osc2:o,output:c}}}__ctx=new WeakMap,__output=new WeakMap,__privateAdd(SynthOscillators,__ctx,null),__privateAdd(SynthOscillators,__output,null);class SynthEffects{static init(t,e){__privateSet(this,__ctx2,t),__privateSet(this,__output2,e)}static get ctx(){return __privateGet(this,__ctx2)}static filter(t="lowpass",e=1e3,i=1){const s=__privateGet(this,__ctx2).createBiquadFilter();return s.type=t,s.frequency.value=e,s.Q.value=i,s}static delay(t=.3,e=.4,i=.5){const s=__privateGet(this,__ctx2).createDelay(5),n=__privateGet(this,__ctx2).createGain(),a=__privateGet(this,__ctx2).createGain(),r=__privateGet(this,__ctx2).createGain(),o=__privateGet(this,__ctx2).createGain(),h=__privateGet(this,__ctx2).createGain();return s.delayTime.value=t,n.gain.value=e,a.gain.value=i,r.gain.value=1-i,o.connect(s),o.connect(r),s.connect(n),n.connect(s),s.connect(a),a.connect(h),r.connect(h),{input:o,output:h,setTime:t=>s.delayTime.setValueAtTime(t,__privateGet(this,__ctx2).currentTime),setFeedback:t=>n.gain.setValueAtTime(t,__privateGet(this,__ctx2).currentTime),setMix:t=>{a.gain.setValueAtTime(t,__privateGet(this,__ctx2).currentTime),r.gain.setValueAtTime(1-t,__privateGet(this,__ctx2).currentTime)}}}static reverb(t=2,e=2){const i=__privateGet(this,__ctx2).createConvolver(),s=__privateGet(this,__ctx2).sampleRate,n=s*t,a=__privateGet(this,__ctx2).createBuffer(2,n,s);for(let t=0;t<2;t++){const i=a.getChannelData(t);for(let t=0;t<n;t++)i[t]=(2*Math.random()-1)*Math.pow(1-t/n,e)}return i.buffer=a,i}static distortion(t=50){const e=__privateGet(this,__ctx2).createWaveShaper(),i=t,s=44100,n=new Float32Array(s);for(let t=0;t<s;t++){const e=2*t/s-1;n[t]=(3+i)*e*20*(Math.PI/180)/(Math.PI+i*Math.abs(e))}return e.curve=n,e.oversample="4x",e}static tremolo(t=5,e=.5){const i=__privateGet(this,__ctx2).createOscillator(),s=__privateGet(this,__ctx2).createGain(),n=__privateGet(this,__ctx2).createGain();return i.frequency.value=t,s.gain.value=.5*e,n.gain.value=1-.5*e,i.connect(s),s.connect(n.gain),i.start(),{input:n,output:n,lfo:i,setRate:t=>i.frequency.setValueAtTime(t,__privateGet(this,__ctx2).currentTime),setDepth:t=>s.gain.setValueAtTime(.5*t,__privateGet(this,__ctx2).currentTime),stop:()=>i.stop()}}static compressor(t={}){const{threshold:e=-24,knee:i=30,ratio:s=12,attack:n=.003,release:a=.25}=t,r=__privateGet(this,__ctx2).createDynamicsCompressor();return r.threshold.value=e,r.knee.value=i,r.ratio.value=s,r.attack.value=n,r.release.value=a,r}static panner(t=0){const e=__privateGet(this,__ctx2).createStereoPanner();return e.pan.value=t,e}static gain(t=1){const e=__privateGet(this,__ctx2).createGain();return e.gain.value=t,e}}__ctx2=new WeakMap,__output2=new WeakMap,__privateAdd(SynthEffects,__ctx2,null),__privateAdd(SynthEffects,__output2,null);class SynthNoise{static white(t,e){const i=t.sampleRate*e,s=t.createBuffer(1,i,t.sampleRate),n=s.getChannelData(0);for(let t=0;t<i;t++)n[t]=2*Math.random()-1;const a=t.createBufferSource();return a.buffer=s,a}static pink(t,e){const i=t.sampleRate*e,s=t.createBuffer(1,i,t.sampleRate),n=s.getChannelData(0);let a=0,r=0,o=0,h=0,l=0,c=0,u=0;for(let t=0;t<i;t++){const e=2*Math.random()-1;a=.99886*a+.0555179*e,r=.99332*r+.0750759*e,o=.969*o+.153852*e,h=.8665*h+.3104856*e,l=.55*l+.5329522*e,c=-.7616*c-.016898*e,n[t]=.11*(a+r+o+h+l+c+u+.5362*e),u=.115926*e}const d=t.createBufferSource();return d.buffer=s,d}static brown(t,e){const i=t.sampleRate*e,s=t.createBuffer(1,i,t.sampleRate),n=s.getChannelData(0);let a=0;for(let t=0;t<i;t++){const e=2*Math.random()-1;n[t]=(a+.02*e)/1.02,a=n[t],n[t]*=3.5}const r=t.createBufferSource();return r.buffer=s,r}}class SynthMusical{static noteToFreq(t){const e=t.match(/^([A-G][#b]?)(\d+)$/);if(!e)throw new Error(`Invalid note: ${t}`);const[,i,s]=e,n=this.NOTE_FREQUENCIES[i];if(void 0===n)throw new Error(`Unknown note: ${i}`);return n*Math.pow(2,parseInt(s))}static scale(t,e="major",i=1){const s=this.noteToFreq(t),n=this.SCALES[e];if(!n)throw new Error(`Unknown scale: ${e}`);const a=[];for(let t=0;t<i;t++)for(const e of n)a.push(s*Math.pow(2,(e+12*t)/12));return a}static chord(t,e="major"){const i=this.noteToFreq(t),s=this.CHORDS[e];if(!s)throw new Error(`Unknown chord type: ${e}`);return s.map(t=>i*Math.pow(2,t/12))}static mapToScale(t,e="C4",i="pentatonic",s=2){const n=this.scale(e,i,s),a=Math.max(0,Math.min(1,t));return n[Math.floor(a*n.length)%n.length]}static midiToFreq(t){return 440*Math.pow(2,(t-69)/12)}static freqToMidi(t){return Math.round(12*Math.log2(t/440)+69)}static randomNote(t="C4",e="pentatonic",i=2){const s=this.scale(t,e,i);return s[Math.floor(Math.random()*s.length)]}static detune(t,e){return t*Math.pow(2,e/1200)}}__publicField(SynthMusical,"NOTE_FREQUENCIES",{C:16.35,"C#":17.32,Db:17.32,D:18.35,"D#":19.45,Eb:19.45,E:20.6,F:21.83,"F#":23.12,Gb:23.12,G:24.5,"G#":25.96,Ab:25.96,A:27.5,"A#":29.14,Bb:29.14,B:30.87}),__publicField(SynthMusical,"SCALES",{major:[0,2,4,5,7,9,11],minor:[0,2,3,5,7,8,10],pentatonic:[0,2,4,7,9],pentatonicMinor:[0,3,5,7,10],blues:[0,3,5,6,7,10],dorian:[0,2,3,5,7,9,10],mixolydian:[0,2,4,5,7,9,10],chromatic:[0,1,2,3,4,5,6,7,8,9,10,11],wholeTone:[0,2,4,6,8,10],diminished:[0,2,3,5,6,8,9,11]}),__publicField(SynthMusical,"CHORDS",{major:[0,4,7],minor:[0,3,7],diminished:[0,3,6],augmented:[0,4,8],sus2:[0,2,7],sus4:[0,5,7],major7:[0,4,7,11],minor7:[0,3,7,10],dom7:[0,4,7,10],dim7:[0,3,6,9],add9:[0,4,7,14],power:[0,7]});class SynthAnalyzer{static init(t,e){__privateSet(this,__ctx3,t),__privateSet(this,__analyzer,t.createAnalyser()),__privateGet(this,__analyzer).fftSize=2048,e.connect(__privateGet(this,__analyzer)),__privateGet(this,__analyzer).connect(t.destination),__privateSet(this,__dataArray,new Uint8Array(__privateGet(this,__analyzer).frequencyBinCount)),__privateSet(this,__frequencyData,new Uint8Array(__privateGet(this,__analyzer).frequencyBinCount))}static get isInitialized(){return null!==__privateGet(this,__analyzer)}static get node(){return __privateGet(this,__analyzer)}static setFFTSize(t){__privateGet(this,__analyzer)&&(__privateGet(this,__analyzer).fftSize=t,__privateSet(this,__dataArray,new Uint8Array(__privateGet(this,__analyzer).frequencyBinCount)),__privateSet(this,__frequencyData,new Uint8Array(__privateGet(this,__analyzer).frequencyBinCount)))}static getWaveform(){return __privateGet(this,__analyzer)?(__privateGet(this,__analyzer).getByteTimeDomainData(__privateGet(this,__dataArray)),__privateGet(this,__dataArray)):new Uint8Array(0)}static getFrequency(){return __privateGet(this,__analyzer)?(__privateGet(this,__analyzer).getByteFrequencyData(__privateGet(this,__frequencyData)),__privateGet(this,__frequencyData)):new Uint8Array(0)}static getBands(t=8){const e=this.getFrequency();if(0===e.length)return new Array(t).fill(0);const i=Math.floor(e.length/t),s=[];for(let n=0;n<t;n++){let t=0;for(let s=0;s<i;s++)t+=e[n*i+s];s.push(t/(255*i))}return s}static getAmplitude(){const t=this.getWaveform();if(0===t.length)return 0;let e=0;for(let i=0;i<t.length;i++){const s=(t[i]-128)/128;e+=s*s}return Math.sqrt(e/t.length)}static getPeakFrequency(){if(!__privateGet(this,__analyzer)||!__privateGet(this,__ctx3))return 0;const t=this.getFrequency();let e=0,i=0;for(let s=0;s<t.length;s++)t[s]>i&&(i=t[s],e=s);return e*(__privateGet(this,__ctx3).sampleRate/2)/__privateGet(this,__analyzer).frequencyBinCount}static dispose(){__privateGet(this,__analyzer)&&(__privateGet(this,__analyzer).disconnect(),__privateSet(this,__analyzer,null)),__privateSet(this,__dataArray,null),__privateSet(this,__frequencyData,null)}}__ctx3=new WeakMap,__analyzer=new WeakMap,__dataArray=new WeakMap,__frequencyData=new WeakMap,__privateAdd(SynthAnalyzer,__ctx3,null),__privateAdd(SynthAnalyzer,__analyzer,null),__privateAdd(SynthAnalyzer,__dataArray,null),__privateAdd(SynthAnalyzer,__frequencyData,null);class Synth{static init(t={}){if(__privateGet(this,__initialized))return void console.warn("[Synth] Already initialized");const{masterVolume:e=.5,sampleRate:i=44100,enableAnalyzer:s=!1}=t;try{__privateSet(this,__ctx4,new(window.AudioContext||window.webkitAudioContext)({sampleRate:i})),__privateSet(this,__masterGain,__privateGet(this,__ctx4).createGain()),__privateGet(this,__masterGain).gain.value=e,__privateGet(this,__masterGain).connect(__privateGet(this,__ctx4).destination),SynthOscillators.init(__privateGet(this,__ctx4),__privateGet(this,__masterGain)),SynthEffects.init(__privateGet(this,__ctx4),__privateGet(this,__masterGain)),s&&SynthAnalyzer.init(__privateGet(this,__ctx4),__privateGet(this,__masterGain)),__privateSet(this,__initialized,!0),console.log("[Synth] Audio system initialized")}catch(t){console.error("[Synth] Failed to initialize audio:",t)}}static get isInitialized(){return __privateGet(this,__initialized)}static get ctx(){return __privateGet(this,__ctx4)}static get master(){return __privateGet(this,__masterGain)}static get osc(){return SynthOscillators}static get fx(){return SynthEffects}static get env(){return SynthEnvelope}static get noise(){return SynthNoise}static get music(){return SynthMusical}static get analyzer(){return SynthAnalyzer}static async resume(){__privateGet(this,__ctx4)&&"suspended"===__privateGet(this,__ctx4).state&&(await __privateGet(this,__ctx4).resume(),console.log("[Synth] Audio context resumed"))}static async suspend(){__privateGet(this,__ctx4)&&"running"===__privateGet(this,__ctx4).state&&await __privateGet(this,__ctx4).suspend()}static get now(){return __privateGet(this,__ctx4)?__privateGet(this,__ctx4).currentTime:0}static get state(){return __privateGet(this,__ctx4)?__privateGet(this,__ctx4).state:"closed"}static set volume(t){__privateGet(this,__masterGain)&&__privateGet(this,__masterGain).gain.setValueAtTime(Math.max(0,Math.min(1,t)),__privateGet(this,__ctx4).currentTime)}static get volume(){return __privateGet(this,__masterGain)?__privateGet(this,__masterGain).gain.value:0}static chain(...t){for(let e=0;e<t.length-1;e++)t[e].connect(t[e+1]);return{first:t[0],last:t[t.length-1],connectTo:e=>t[t.length-1].connect(e)}}static schedule(t,e){const i=Math.max(0,1e3*(e-this.now));return setTimeout(t,i)}static async close(){__privateGet(this,__ctx4)&&(SynthAnalyzer.dispose(),await __privateGet(this,__ctx4).close(),__privateSet(this,__ctx4,null),__privateSet(this,__masterGain,null),__privateSet(this,__initialized,!1),console.log("[Synth] Audio system closed"))}}__ctx4=new WeakMap,__masterGain=new WeakMap,__initialized=new WeakMap,__privateAdd(Synth,__ctx4,null),__privateAdd(Synth,__masterGain,null),__privateAdd(Synth,__initialized,!1);class Sound{static beep(t=440,e=.1,i={}){if(!Synth.isInitialized)return;const{volume:s=.3,type:n="sine"}=i;Synth.osc.tone(t,e,{type:n,volume:s,attack:.001,decay:.8*e,sustain:0,release:.2*e})}static click(t=.3){Synth.isInitialized&&Synth.osc.tone(1e3,.01,{type:"square",volume:t,attack:.001,decay:.009,sustain:0,release:.001})}static sweep(t,e,i,s={}){if(!Synth.isInitialized)return;const{volume:n=.3,type:a="sine"}=s;Synth.osc.sweep(t,e,i,{type:a,volume:n})}static fromValue(t,e={}){if(!Synth.isInitialized)return;const{root:i="C4",scale:s="pentatonic",octaves:n=2,duration:a=.2,volume:r=.3,type:o="sine"}=e,h=Synth.music.mapToScale(t,i,s,n);Synth.osc.tone(h,a,{volume:r,type:o})}static impact(t=.5){if(!Synth.isInitialized)return;const e=80+200*t,i=.05+.15*t;Synth.osc.tone(e,i,{type:"sine",volume:.4*t,attack:.001,decay:i,sustain:0,release:.02});const s=Synth.noise.white(Synth.ctx,.08),n=Synth.ctx.createGain();n.gain.setValueAtTime(.25*t,Synth.now),n.gain.exponentialRampToValueAtTime(.001,Synth.now+.08),s.connect(n),n.connect(Synth.master),s.start(),s.stop(Synth.now+.1)}static explosion(t=.7){if(!Synth.isInitialized)return;const e=.3+.4*t;Synth.osc.tone(50+30*t,e,{type:"sine",volume:.4*t,attack:.001,decay:.3*e,sustain:.3,release:.7*e});const i=Synth.noise.brown(Synth.ctx,e),s=Synth.ctx.createGain(),n=Synth.fx.filter("lowpass",800+400*t,1);s.gain.setValueAtTime(.5*t,Synth.now),s.gain.exponentialRampToValueAtTime(.001,Synth.now+e),i.connect(n),n.connect(s),s.connect(Synth.master),i.start(),i.stop(Synth.now+e+.1)}static laser(t={}){if(!Synth.isInitialized)return;const{startFreq:e=1200,endFreq:i=200,duration:s=.15,volume:n=.25,type:a="sawtooth"}=t;Synth.osc.sweep(e,i,s,{type:a,volume:n})}static powerUp(t={}){if(!Synth.isInitialized)return;const{startFreq:e=300,endFreq:i=1200,duration:s=.3,volume:n=.3}=t;Synth.osc.sweep(e,i,s,{type:"square",volume:n}),Synth.osc.sweep(1.5*e,1.5*i,s,{type:"sine",volume:.5*n})}static hurt(t=.5){Synth.isInitialized&&(Synth.osc.tone(80+40*t,.1,{type:"sine",volume:.4*t,attack:.001,decay:.08,sustain:0,release:.02}),Synth.osc.tone(200+100*t,.08,{type:"sawtooth",volume:.2*t,attack:.001,decay:.06,sustain:0,release:.02}))}static coin(t={}){if(!Synth.isInitialized)return;const{baseFreq:e=987.77,volume:i=.25}=t;Synth.osc.tone(e,.08,{type:"square",volume:i,attack:.001,decay:.05,sustain:.3,release:.02}),setTimeout(()=>{Synth.isInitialized&&Synth.osc.tone(1.5*e,.12,{type:"square",volume:i,attack:.001,decay:.08,sustain:.2,release:.04})},80)}static jump(t={}){if(!Synth.isInitialized)return;const{startFreq:e=150,endFreq:i=400,duration:s=.15,volume:n=.25}=t;Synth.osc.sweep(e,i,s,{type:"square",volume:n})}static select(t={}){if(!Synth.isInitialized)return;const{frequency:e=660,volume:i=.2}=t;Synth.osc.tone(e,.08,{type:"sine",volume:i,attack:.001,decay:.05,sustain:.3,release:.03})}static error(t={}){if(!Synth.isInitialized)return;const{volume:e=.25}=t;Synth.osc.tone(400,.1,{type:"square",volume:e,attack:.001,decay:.08,sustain:0,release:.02}),setTimeout(()=>{Synth.isInitialized&&Synth.osc.tone(300,.15,{type:"square",volume:e,attack:.001,decay:.12,sustain:0,release:.03})},100)}static drone(t="C2",e={}){if(!Synth.isInitialized)return null;const{volume:i=.2,richness:s=.5}=e,n=Synth.music.noteToFreq(t),a=[],r=[],o=[1,.5*s,.3*s,.2*s];return[n,1.5*n,2*n,3*n].forEach((t,e)=>{const s=Synth.ctx.createOscillator(),n=Synth.ctx.createGain();s.type="sine",s.frequency.value=t,n.gain.value=o[e]*i,s.connect(n),n.connect(Synth.master),s.start(),a.push(s),r.push(n)}),{stop:(t=.5)=>{const e=Synth.now;r.forEach(i=>{i.gain.linearRampToValueAtTime(0,e+t)}),setTimeout(()=>{a.forEach(t=>{try{t.stop()}catch(t){}})},1e3*t+100)},setVolume:t=>{r.forEach((e,i)=>{e.gain.linearRampToValueAtTime(o[i]*t,Synth.now+.1)})}}}static note(t,e=.5,i={}){if(!Synth.isInitialized)return;const{volume:s=.3,type:n="sine",envelope:a={}}=i,r=Synth.music.noteToFreq(t);Synth.osc.tone(r,e,{type:n,volume:s,...Synth.env.presets.pluck,...a})}static chord(t,e="major",i=.5,s={}){if(!Synth.isInitialized)return;const{volume:n=.2,type:a="sine",strum:r=0}=s,o=Synth.music.chord(t,e);o.forEach((t,e)=>{const s=r*e;Synth.osc.tone(t,i,{type:a,volume:n/o.length,attack:.01,decay:.1,sustain:.6,release:.2,startTime:Synth.now+s})})}static sequence(t,e=.2,i=0,s={}){if(!Synth.isInitialized)return;const{volume:n=.3,type:a="sine"}=s,r=e+i;t.forEach((t,i)=>{const s=Synth.now+i*r,o=Synth.music.noteToFreq(t);Synth.osc.tone(o,e,{type:a,volume:n,attack:.01,decay:.05,sustain:.5,release:.1,startTime:s})})}static win(){if(!Synth.isInitialized)return;this.sequence(["C5","E5","G5","C6"],.15,.05,{volume:.25,type:"square"})}static lose(){if(!Synth.isInitialized)return;this.sequence(["E4","D4","C4"],.25,0,{volume:.25,type:"sawtooth"})}}class StateMachine{constructor(t={}){this.states=t.states||{},this.currentState=null,this.previousState=null,this.stateTime=0,this.context=t.context||null,this.paused=!1,this.onStateChange=null,t.initial&&this.setState(t.initial)}get state(){return this.currentState}get currentStateConfig(){return this.currentState?this.states[this.currentState]:null}is(t){return this.currentState===t}isAny(...t){return t.includes(this.currentState)}addState(t,e){return this.states[t]=e,this}removeState(t){return delete this.states[t],this}setState(t,e){if(!this.states[t])return console.warn(`StateMachine: Unknown state '${t}'`),!1;if(this.currentState){const t=this.states[this.currentState];(null==t?void 0:t.exit)&&this._call(t.exit,e)}this.previousState=this.currentState,this.currentState=t,this.stateTime=0;const i=this.states[t];return(null==i?void 0:i.enter)&&this._call(i.enter,e),this.onStateChange&&this.onStateChange(t,this.previousState,e),!0}trigger(t,e){const i=this.currentStateConfig;if(!(null==i?void 0:i.on))return!1;const s=i.on[t];return!!s&&("string"==typeof s?this.setState(s,e):!(s.guard&&!this._call(s.guard,e))&&(s.action&&this._call(s.action,e),!!s.target&&this.setState(s.target,e)))}update(t){if(this.paused||!this.currentState)return;this.stateTime+=t;const e=this.states[this.currentState];e&&(e.update&&this._call(e.update,t),void 0!==e.duration&&this.stateTime>=e.duration&&(e.next?this.setState(e.next):e.onComplete&&this._call(e.onComplete)))}get progress(){const t=this.currentStateConfig;return(null==t?void 0:t.duration)?Math.min(1,this.stateTime/t.duration):0}get remaining(){const t=this.currentStateConfig;return(null==t?void 0:t.duration)?Math.max(0,t.duration-this.stateTime):1/0}get isTimed(){var t;return void 0!==(null==(t=this.currentStateConfig)?void 0:t.duration)}pause(){this.paused=!0}resume(){this.paused=!1}reset(t){if(this.stateTime=0,this.previousState=null,t)this.setState(t);else{const t=Object.keys(this.states)[0];t&&this.setState(t)}}_call(t,...e){if("function"==typeof t)return this.context?t.call(this.context,...e):t(...e)}static fromSequence(t,e={}){var i;const s={};for(let i=0;i<t.length;i++){const n=t[i],a=i===t.length-1,r=a?e.loop?t[0].name:null:t[i+1].name;s[n.name]={duration:n.duration,next:r,enter:n.enter,update:n.update,exit:n.exit,onComplete:a&&!e.loop?e.onComplete:void 0}}return new StateMachine({initial:null==(i=t[0])?void 0:i.name,states:s,context:e.context})}}export{Arc,Arrow,BezierShape,Button,Camera2D,Camera3D,Circle,Cloud,Collision,CollisionSystem,Complex,Cone,Cross,Cube,Cube3D,Cursor,Cylinder,DebugTab,Diamond,Easing,Euclidian,EventEmitter,FPSCounter,FluentGO,FluentGame,FluentLayer,FluentScene,FluidSystem,Fractals,Game,GameObject,GameObjectShapeWrapper,Geometry2d,GridLayout,Group,Heart,Hexagon,HorizontalLayout,ImageGo,ImageShape,Input,IsometricCamera,IsometricScene,Kernels,Keys,LayoutScene,Line,Loggable,Logger,Motion,Mouse,Noise,OutlinedText,Painter,PainterColors,PainterEffects,PainterImages,PainterLines,PainterOpacity,PainterShapes,PainterText,Particle,ParticleEmitter,ParticleSystem,PatternRectangle,Patterns,PieSlice,Pin,Pipeline,Plane3D,PlatformerScene,Polygon,Position,Prism,Random,Rectangle,Renderable,Ring,RoundedRectangle,SPHERE_SHADERS,SVGShape,Scene,Scene3D,Shape,ShapeGOFactory,Sound,Sphere,Sphere3D,Sprite,Square,Star,StateMachine,Stepper,StickFigure,Synth,SynthAnalyzer,SynthEffects,SynthEnvelope,SynthMusical,SynthNoise,SynthOscillators,TaskManager,Tensor,Text,TextShape,TileLayout,ToggleButton,Tooltip,Touch,Traceable,Transform,Transformable,Triangle,Tween,Tweenetik,UI_THEME,Updaters,VerticalLayout,WebGLRenderer,WrappedText,ZOrderedCollection,applyAnchor,applyDraggable,applyGravitationalLensing,applyLayout,bezierV1,blendForces,bounceV1,cartesianToPolar,cartesianToSpherical,computeDensities,computeFluidForces,computeGasForces,computePressures,computeThermalBuoyancy,createTrailPoint,deBroglieWavelength,decayingOrbitalRadius,flammEmbedding,flammEmbeddingHeight,floatV1,followPath,gaussianEnvelope,gaussianWavePacket,gcanvas,generatePenroseTilingPixels,getDefaultFluidConfig,getTerminalTrajectory,gravitationalLensing,gridLayout,groupVelocity,heatTransfer,heatTransferFalloff,hopV1,horizontalLayout,integrateEuler,keplerianOmega,kerrPrecessionRate,orbitV1,orbitalRadius,orbitalRadiusSimple,oscillateV1,parabolicV1,patrolV1,pendulumV1,planeWavePhase,polarToCartesian,probabilityDensity,pulseV1,schwarzschildPrecessionRate,shakeV1,sketch,sphericalToCartesian,spiralV1,springV1,swingV1,thermalBuoyancy,thermalGravity,tileLayout,updateTrail,verticalLayout,waypointV1,zoneTemperature};