{"version":3,"sources":["jsdelivr-header.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/global-this.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/fails.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/descriptors.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/function-bind-native.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/function-call.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-property-is-enumerable.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/create-property-descriptor.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/function-uncurry-this.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/classof-raw.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/indexed-object.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-null-or-undefined.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/require-object-coercible.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-indexed-object.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-callable.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-object.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/get-built-in.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-is-prototype-of.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/environment-user-agent.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/environment-v8-version.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/symbol-constructor-detection.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/use-symbol-as-uid.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-symbol.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/try-to-string.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/a-callable.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/get-method.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/ordinary-to-primitive.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-pure.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/define-global-property.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/shared-store.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/shared.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-object.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/has-own-property.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/uid.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/well-known-symbol.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-primitive.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-property-key.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/document-create-element.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/ie8-dom-define.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-get-own-property-descriptor.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/v8-prototype-define-bug.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/an-object.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-define-property.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/create-non-enumerable-property.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/function-name.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/inspect-source.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/weak-map-basic-detection.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/shared-key.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/hidden-keys.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/internal-state.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/make-built-in.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/define-built-in.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/math-trunc.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-integer-or-infinity.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-absolute-index.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-length.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/length-of-array-like.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/array-includes.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-keys-internal.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/enum-bug-keys.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-get-own-property-names.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/object-get-own-property-symbols.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/own-keys.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/copy-constructor-properties.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/is-forced.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/export.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/es.global-this.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/esnext.global-this.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/proposals/global-this.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-string-tag-support.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/classof.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/to-string.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/whitespaces.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/string-trim.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/string-trim-forced.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/string-trim-start.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/es.string.trim-left.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/es.string.trim-start.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/entry-unbind.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/es/string/trim-start.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/internals/string-trim-end.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/es.string.trim-right.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/modules/es.string.trim-end.js","/node_modules/.pnpm/core-js@3.49.0/node_modules/core-js/es/string/trim-end.js","/node_modules/.pnpm/howler@2.2.4/node_modules/howler/dist/howler.js","/npm/melonjs@20.1.0/src/polyfill/index.ts","/npm/melonjs@20.1.0/src/polyfill/console.ts","/npm/melonjs@20.1.0/src/polyfill/ellipse.ts","/npm/melonjs@20.1.0/src/polyfill/roundrect.ts","/npm/melonjs@20.1.0/src/const.ts","/npm/melonjs@20.1.0/src/system/pool.ts","/npm/melonjs@20.1.0/src/math/math.ts","/npm/melonjs@20.1.0/src/math/vector2d.ts","/npm/melonjs@20.1.0/src/geometries/point.ts","/npm/melonjs@20.1.0/src/physics/bounds.ts","/npm/melonjs@20.1.0/src/geometries/earcut.ts","/npm/melonjs@20.1.0/src/geometries/polygon.ts","/npm/melonjs@20.1.0/src/geometries/rectangle.ts","/npm/melonjs@20.1.0/src/system/device.ts","/npm/melonjs@20.1.0/src/utils/agent.ts","/npm/melonjs@20.1.0/src/utils/string.ts","/npm/melonjs@20.1.0/src/system/platform.ts","/npm/melonjs@20.1.0/src/system/dom.ts","/npm/melonjs@20.1.0/src/system/event.ts","/npm/melonjs@20.1.0/src/system/eventEmitter.ts","/npm/melonjs@20.1.0/src/utils/utils.ts","/npm/melonjs@20.1.0/src/utils/semver.ts","/npm/melonjs@20.1.0/src/utils/array.ts","/npm/melonjs@20.1.0/src/utils/file.ts","/npm/melonjs@20.1.0/src/utils/function.ts","/npm/melonjs@20.1.0/src/system/save.ts","/npm/melonjs@20.1.0/src/system/fullscreen.ts","/npm/melonjs@20.1.0/src/math/color.ts","/npm/melonjs@20.1.0/src/video/canvas_factory.js","/npm/melonjs@20.1.0/src/video/rendertarget/rendertarget.ts","/npm/melonjs@20.1.0/src/video/rendertarget/canvasrendertarget.js","/npm/melonjs@20.1.0/src/video/gradient.js","/npm/melonjs@20.1.0/src/geometries/toarccanvas.ts","/npm/melonjs@20.1.0/src/geometries/path2d.ts","/npm/melonjs@20.1.0/src/math/matrix2d.ts","/npm/melonjs@20.1.0/src/math/vector3d.ts","/npm/melonjs@20.1.0/src/math/matrix3d.ts","/npm/melonjs@20.1.0/src/video/renderstate.js","/npm/melonjs@20.1.0/src/video/renderer.js","/npm/melonjs@20.1.0/src/utils/array-multimap.js","/npm/melonjs@20.1.0/src/video/gpu/textureslots.js","/npm/melonjs@20.1.0/src/loader/loader.js","/npm/melonjs@20.1.0/src/audio/audio.ts","/npm/melonjs@20.1.0/src/audio/backend.ts","/npm/melonjs@20.1.0/src/audio/playback.ts","/npm/melonjs@20.1.0/src/audio/procedural.ts","/npm/melonjs@20.1.0/src/lang/console.js","/npm/melonjs@20.1.0/src/system/timer.ts","/npm/melonjs@20.1.0/src/tweens/easing.ts","/npm/melonjs@20.1.0/src/tweens/interpolation.ts","/npm/melonjs@20.1.0/src/tweens/tween.ts","/npm/melonjs@20.1.0/src/camera/effects/camera_effect.ts","/npm/melonjs@20.1.0/src/camera/effects/fade_effect.ts","/npm/melonjs@20.1.0/src/geometries/ellipse.ts","/npm/melonjs@20.1.0/src/camera/effects/mask_effect.ts","/npm/melonjs@20.1.0/src/math/color_matrix.ts","/npm/melonjs@20.1.0/src/geometries/observablePoint.ts","/npm/melonjs@20.1.0/src/input/input.ts","/npm/melonjs@20.1.0/src/input/keyboard.ts","/npm/melonjs@20.1.0/src/input/gamepad.ts","/npm/melonjs@20.1.0/src/input/key.ts","/npm/melonjs@20.1.0/src/math/observableVector3d.ts","/npm/melonjs@20.1.0/src/math/vertex.ts","/npm/melonjs@20.1.0/src/physics/broadphase/aabb3d.ts","/npm/melonjs@20.1.0/src/geometries/box3d.ts","/npm/melonjs@20.1.0/src/geometries/line.ts","/npm/melonjs@20.1.0/src/geometries/roundrect.ts","/npm/melonjs@20.1.0/src/renderable/text/glyph.ts","/npm/melonjs@20.1.0/src/renderable/text/bitmaptextdata.ts","/npm/melonjs@20.1.0/src/pool.ts","/npm/melonjs@20.1.0/src/system/legacy_pool.js","/npm/melonjs@20.1.0/src/physics/collision.js","/npm/melonjs@20.1.0/src/physics/builtin/body.js","/npm/melonjs@20.1.0/src/renderable/renderable.js","/npm/melonjs@20.1.0/src/video/texture/texture2d.ts","/npm/melonjs@20.1.0/src/video/webgl/utils/attributes.js","/npm/melonjs@20.1.0/src/video/webgl/utils/precision.js","/npm/melonjs@20.1.0/src/video/webgl/utils/program.js","/npm/melonjs@20.1.0/src/video/webgl/utils/string.js","/npm/melonjs@20.1.0/src/video/webgl/utils/uniforms.js","/npm/melonjs@20.1.0/src/video/webgl/glshader.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/quad.vert","/npm/melonjs@20.1.0/src/video/effects/glsl_realization.js","/npm/melonjs@20.1.0/src/video/effects/wgsl/layout.js","/npm/melonjs@20.1.0/src/video/effects/wgsl/parse.js","/npm/melonjs@20.1.0/src/video/effects/wgsl/scaffold.js","/npm/melonjs@20.1.0/src/video/effects/wgsl_realization.js","/npm/melonjs@20.1.0/src/video/effects/shadereffect.js","/npm/melonjs@20.1.0/src/video/effects/colorMatrix.js","/npm/melonjs@20.1.0/src/camera/effects/shake_effect.ts","/npm/melonjs@20.1.0/src/camera/camera2d.ts","/npm/melonjs@20.1.0/src/renderable/anchorPoint.ts","/npm/melonjs@20.1.0/src/renderable/animation.ts","/npm/melonjs@20.1.0/src/renderable/frameAnimation.js","/npm/melonjs@20.1.0/src/renderable/sprite.js","/npm/melonjs@20.1.0/src/state/stage.ts","/npm/melonjs@20.1.0/src/loader/loadingscreen.js","/npm/melonjs@20.1.0/src/state/state.ts","/npm/melonjs@20.1.0/src/loader/cache.js","/npm/melonjs@20.1.0/src/loader/parsers/fetchdata.js","/npm/melonjs@20.1.0/src/loader/parsers/aseprite.js","/npm/melonjs@20.1.0/src/loader/parsers/binary.js","/npm/melonjs@20.1.0/src/loader/parsers/fontface.js","/npm/melonjs@20.1.0/src/lighting/light3d.ts","/npm/melonjs@20.1.0/src/renderable/groundshadow.js","/npm/melonjs@20.1.0/src/camera/frustum.ts","/npm/melonjs@20.1.0/src/camera/camera3d.ts","/npm/melonjs@20.1.0/src/video/gpu/instancerecord.ts","/npm/melonjs@20.1.0/src/loader/parsers/pbr.ts","/npm/melonjs@20.1.0/src/renderable/mesh.js","/npm/melonjs@20.1.0/src/renderable/instanced_mesh.js","/npm/melonjs@20.1.0/src/renderable/container.js","/npm/melonjs@20.1.0/src/level/gltf/gltf_sampler.js","/npm/melonjs@20.1.0/src/level/gltf/srgb.js","/npm/melonjs@20.1.0/src/level/gltf/GLTFModel.js","/npm/melonjs@20.1.0/src/level/gltf/GLTFScene.js","/npm/melonjs@20.1.0/src/level/tiled/constants.js","/npm/melonjs@20.1.0/src/level/tiled/TMXTile.js","/npm/melonjs@20.1.0/src/level/tiled/TMXUtils.js","/npm/melonjs@20.1.0/src/utils/decode.ts","/npm/melonjs@20.1.0/src/utils/xml.ts","/npm/melonjs@20.1.0/src/level/tiled/TMXLayer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXHexagonalRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXIsometricRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXOrthogonalRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXObliqueRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/TMXStaggeredRenderer.js","/npm/melonjs@20.1.0/src/level/tiled/renderer/autodetect.js","/npm/melonjs@20.1.0/src/level/tiled/TMXObject.js","/npm/melonjs@20.1.0/src/level/tiled/TMXGroup.js","/npm/melonjs@20.1.0/src/level/tiled/factories/shape.js","/npm/melonjs@20.1.0/src/renderable/text/textstyle.js","/npm/melonjs@20.1.0/src/renderable/text/text.js","/npm/melonjs@20.1.0/src/renderable/text/textmetrics.js","/npm/melonjs@20.1.0/src/renderable/text/bitmaptext.js","/npm/melonjs@20.1.0/src/level/tiled/factories/text.js","/npm/melonjs@20.1.0/src/level/tiled/factories/tile.js","/npm/melonjs@20.1.0/src/level/tiled/TMXObjectFactory.js","/npm/melonjs@20.1.0/src/level/tiled/TMXTileset.js","/npm/melonjs@20.1.0/src/level/tiled/TMXTilesetGroup.js","/npm/melonjs@20.1.0/src/level/tiled/TMXTileMap.js","/npm/melonjs@20.1.0/src/level/level.js","/npm/melonjs@20.1.0/src/loader/parsers/gltf.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/parseDDS.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/parseKTX.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/parseKTX2.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/parsePKM.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/parsePVR.js","/npm/melonjs@20.1.0/src/loader/parsers/compressed_textures/compressed_image.js","/npm/melonjs@20.1.0/src/loader/parsers/image.js","/npm/melonjs@20.1.0/src/loader/parsers/json.js","/npm/melonjs@20.1.0/src/loader/parsers/mtl.js","/npm/melonjs@20.1.0/src/loader/parsers/obj.js","/npm/melonjs@20.1.0/src/loader/parsers/script.js","/npm/melonjs@20.1.0/src/loader/parsers/shader.js","/npm/melonjs@20.1.0/src/loader/parsers/tmx.js","/npm/melonjs@20.1.0/src/loader/parsers/video.js","/npm/melonjs@20.1.0/src/video/texture/parser/aseprite.js","/npm/melonjs@20.1.0/src/video/texture/parser/spritesheet.js","/npm/melonjs@20.1.0/src/video/texture/parser/texturepacker.js","/npm/melonjs@20.1.0/src/video/texture/atlas.js","/npm/melonjs@20.1.0/src/video/texture/cache.js","/npm/melonjs@20.1.0/src/video/texture/frametexture.js","/npm/melonjs@20.1.0/src/video/canvas/canvas_renderer.js","/npm/melonjs@20.1.0/src/video/buffer/vertex.js","/npm/melonjs@20.1.0/src/video/gpu/batcher.js","/npm/melonjs@20.1.0/src/video/gpu/vertexformat.ts","/npm/melonjs@20.1.0/src/video/buffer/index.js","/npm/melonjs@20.1.0/src/video/webgl/buffer/index.js","/npm/melonjs@20.1.0/src/video/webgl/buffer/vertexstate.js","/npm/melonjs@20.1.0/src/video/gpu/topology.ts","/npm/melonjs@20.1.0/src/video/webgl/utils/topology.js","/npm/melonjs@20.1.0/src/video/webgl/utils/vertexformat.js","/npm/melonjs@20.1.0/src/video/webgl/batchers/batcher.js","/npm/melonjs@20.1.0/src/video/gpu/primitives.ts","/npm/melonjs@20.1.0/src/video/webgl/shaders/primitive.frag","/npm/melonjs@20.1.0/src/video/webgl/shaders/primitive.vert","/npm/melonjs@20.1.0/src/video/webgl/batchers/primitive_batcher.js","/npm/melonjs@20.1.0/src/video/gpu/quadcorners.ts","/npm/melonjs@20.1.0/src/video/webgl/shaders/multitexture.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/quad-multi.vert","/npm/melonjs@20.1.0/src/video/webgl/batchers/material_batcher.js","/npm/melonjs@20.1.0/src/video/webgl/batchers/quad_batcher.js","/npm/melonjs@20.1.0/src/video/effects/radialGradient.js","/npm/melonjs@20.1.0/src/video/rendertarget/render_target_pool.js","/npm/melonjs@20.1.0/src/video/rendertarget/webglrendertarget.js","/npm/melonjs@20.1.0/src/video/utils/dash.js","/npm/melonjs@20.1.0/src/video/utils/tessellation.js","/npm/melonjs@20.1.0/src/video/gpu/meshvertex.ts","/npm/melonjs@20.1.0/src/video/webgl/buffer/uniformblock.js","/npm/melonjs@20.1.0/src/video/webgl/lighting/constants.ts","/npm/melonjs@20.1.0/src/video/webgl/lighting/pack3d.ts","/npm/melonjs@20.1.0/src/video/webgl/lighting/std140.ts","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh-lit.frag","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh-lit.vert","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh-lit-instanced.vert","/npm/melonjs@20.1.0/src/video/gpu/meshchunk.ts","/npm/melonjs@20.1.0/src/video/webgl/buffer/instance_buffer.js","/npm/melonjs@20.1.0/src/video/webgl/buffer/retained_geometry.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh.frag","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh.vert","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh-instanced.vert","/npm/melonjs@20.1.0/src/video/webgl/shaders/mesh-shadow-instanced.vert","/npm/melonjs@20.1.0/src/video/webgl/batchers/mesh_batcher.js","/npm/melonjs@20.1.0/src/video/webgl/batchers/lit_mesh_batcher.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/multitexture-lit.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/quad-multi-lit.vert","/npm/melonjs@20.1.0/src/video/gpu/texturestore.js","/npm/melonjs@20.1.0/src/video/webgl/texture/store.js","/npm/melonjs@20.1.0/src/video/webgl/batchers/lit_quad_batcher.js","/npm/melonjs@20.1.0/src/video/webgl/lighting/pack.ts","/npm/melonjs@20.1.0/src/video/texture/resource.js","/npm/melonjs@20.1.0/src/video/webgl/shaders/orthogonal-tmxlayer.frag","/npm/melonjs@20.1.0/src/video/webgl/shaders/orthogonal-tmxlayer.vert","/npm/melonjs@20.1.0/src/video/webgl/renderers/tmxlayer/orthogonal.js","/npm/melonjs@20.1.0/src/video/webgl/utils/maxtextures.js","/npm/melonjs@20.1.0/src/video/webgl/utils/samplercache.js","/npm/melonjs@20.1.0/src/video/webgl/webgl_renderer.js","/npm/melonjs@20.1.0/src/lang/deprecated.js","/npm/melonjs@20.1.0/src/input/pointer.ts","/npm/melonjs@20.1.0/src/input/pointerevent.ts","/npm/melonjs@20.1.0/src/geometries/sphere.ts","/npm/melonjs@20.1.0/src/physics/broadphase/octree.ts","/npm/melonjs@20.1.0/src/physics/broadphase/quadtree.ts","/npm/melonjs@20.1.0/src/physics/broadphase/factory.ts","/npm/melonjs@20.1.0/src/physics/response.js","/npm/melonjs@20.1.0/src/physics/builtin/raycast.ts","/npm/melonjs@20.1.0/src/physics/builtin/sat.js","/npm/melonjs@20.1.0/src/physics/builtin/sat3d.js","/npm/melonjs@20.1.0/src/physics/builtin/detector.js","/npm/melonjs@20.1.0/src/physics/builtin/builtin-adapter.ts","/npm/melonjs@20.1.0/src/physics/world.js","/npm/melonjs@20.1.0/src/lighting/light2d.ts","/npm/melonjs@20.1.0/src/particles/particle.ts","/npm/melonjs@20.1.0/src/renderable/collectable.js","/npm/melonjs@20.1.0/src/renderable/colorlayer.js","/npm/melonjs@20.1.0/src/renderable/entity/entity.js","/npm/melonjs@20.1.0/src/renderable/imagelayer.js","/npm/melonjs@20.1.0/src/renderable/nineslicesprite.js","/npm/melonjs@20.1.0/src/renderable/trigger.js","/npm/melonjs@20.1.0/src/version.ts","/npm/melonjs@20.1.0/src/system/bootstrap.ts","/npm/melonjs@20.1.0/src/video/utils/autodetect.js","/npm/melonjs@20.1.0/src/video/rendertarget/webgpurendertarget.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/mesh-instanced.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/mesh-lit.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/buffer/instance_buffer.js","/npm/melonjs@20.1.0/src/video/webgpu/buffer/retained_geometry.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/mesh.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/shaders/mesh-shadow-instanced.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/batchers/webgpu_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/batchers/mesh_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/batchers/lit_mesh_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/quad-lit.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/effect_binding.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/blit.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/shaders/clear.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/shaders/primitive.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/shaders/quad.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/pipeline/bindgroups.js","/npm/melonjs@20.1.0/src/video/webgpu/pipeline/cache.js","/npm/melonjs@20.1.0/src/video/webgpu/batchers/quad_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/batchers/lit_quad_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/batchers/primitive_batcher.js","/npm/melonjs@20.1.0/src/video/webgpu/buffer/arena.js","/npm/melonjs@20.1.0/src/video/webgpu/buffer/uniformring.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/tmxlayer.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/renderers/tmxlayer/orthogonal.js","/npm/melonjs@20.1.0/src/video/webgpu/texture/compressed.js","/npm/melonjs@20.1.0/src/video/webgpu/texture/frametexture.js","/npm/melonjs@20.1.0/src/video/webgpu/shaders/mipblit.wgsl","/npm/melonjs@20.1.0/src/video/webgpu/texture/store.js","/npm/melonjs@20.1.0/src/video/webgpu/webgpu_renderer.js","/npm/melonjs@20.1.0/src/application/scaleMethods.ts","/npm/melonjs@20.1.0/src/application/defaultApplicationSettings.ts","/npm/melonjs@20.1.0/src/application/header.ts","/npm/melonjs@20.1.0/src/application/resize.ts","/npm/melonjs@20.1.0/src/application/application.ts","/npm/melonjs@20.1.0/src/particles/settings.ts","/npm/melonjs@20.1.0/src/particles/emitter.ts","/npm/melonjs@20.1.0/src/plugin/plugin.ts","/npm/melonjs@20.1.0/src/renderable/draggable.js","/npm/melonjs@20.1.0/src/renderable/dragndrop.js","/npm/melonjs@20.1.0/src/renderable/sprite3d.js","/npm/melonjs@20.1.0/src/renderable/trail.js","/npm/melonjs@20.1.0/src/renderable/ui/uibaseelement.ts","/npm/melonjs@20.1.0/src/renderable/ui/uispriteelement.ts","/npm/melonjs@20.1.0/src/renderable/ui/uitextbutton.ts","/npm/melonjs@20.1.0/src/video/effects/blur.js","/npm/melonjs@20.1.0/src/video/effects/chromaticAberration.js","/npm/melonjs@20.1.0/src/video/effects/desaturate.js","/npm/melonjs@20.1.0/src/video/effects/dissolve.js","/npm/melonjs@20.1.0/src/video/effects/dropShadow.js","/npm/melonjs@20.1.0/src/video/effects/flash.js","/npm/melonjs@20.1.0/src/video/effects/glow.js","/npm/melonjs@20.1.0/src/video/effects/hologram.js","/npm/melonjs@20.1.0/src/video/effects/invert.js","/npm/melonjs@20.1.0/src/video/effects/outline.js","/npm/melonjs@20.1.0/src/video/effects/pixelate.js","/npm/melonjs@20.1.0/src/video/effects/scanline.js","/npm/melonjs@20.1.0/src/video/effects/sepia.js","/npm/melonjs@20.1.0/src/video/effects/shine.js","/npm/melonjs@20.1.0/src/video/effects/tintPulse.js","/npm/melonjs@20.1.0/src/video/effects/vignette.js","/npm/melonjs@20.1.0/src/video/effects/wave.js","/npm/melonjs@20.1.0/src/math/noise.ts","/npm/melonjs@20.1.0/src/video/texture/noise_texture2d.js","/npm/melonjs@20.1.0/src/math/observableVector2d.ts","/npm/melonjs@20.1.0/src/video/video.js","/npm/melonjs@20.1.0/src/index.ts"],"names":[],"mappings":"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","file":"/npm/melonjs@20.1.0/build/index.js/+esm","sourceRoot":"","sourcesContent":["/**\n * Bundled by jsDelivr using Rollup v4.62.2 and esbuild v0.28.1.\n * Original file: /npm/melonjs@20.1.0/build/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\nvar check = function (it) {\n  return it && it.Math === Math && it;\n};\n\n// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028\nmodule.exports =\n  // eslint-disable-next-line es/no-global-this -- safe\n  check(typeof globalThis == 'object' && globalThis) ||\n  check(typeof window == 'object' && window) ||\n  // eslint-disable-next-line no-restricted-globals -- safe\n  check(typeof self == 'object' && self) ||\n  check(typeof global == 'object' && global) ||\n  check(typeof this == 'object' && this) ||\n  // eslint-disable-next-line no-new-func -- fallback\n  (function () { return this; })() || Function('return this')();\n","'use strict';\nmodule.exports = function (exec) {\n  try {\n    return !!exec();\n  } catch (error) {\n    return true;\n  }\n};\n","'use strict';\nvar fails = require('../internals/fails');\n\n// Detect IE8's incomplete defineProperty implementation\nmodule.exports = !fails(function () {\n  // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n  return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] !== 7;\n});\n","'use strict';\nvar fails = require('../internals/fails');\n\nmodule.exports = !fails(function () {\n  // eslint-disable-next-line es/no-function-prototype-bind -- safe\n  var test = function () { /* empty */ }.bind();\n  // eslint-disable-next-line no-prototype-builtins -- safe\n  return typeof test != 'function' || test.hasOwnProperty('prototype');\n});\n","'use strict';\nvar NATIVE_BIND = require('../internals/function-bind-native');\n\nvar call = Function.prototype.call;\n// eslint-disable-next-line es/no-function-prototype-bind -- safe\nmodule.exports = NATIVE_BIND ? call.bind(call) : function () {\n  return call.apply(call, arguments);\n};\n","'use strict';\nvar $propertyIsEnumerable = {}.propertyIsEnumerable;\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n\n// Nashorn ~ JDK8 bug\nvar NASHORN_BUG = getOwnPropertyDescriptor && !$propertyIsEnumerable.call({ 1: 2 }, 1);\n\n// `Object.prototype.propertyIsEnumerable` method implementation\n// https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable\nexports.f = NASHORN_BUG ? function propertyIsEnumerable(V) {\n  var descriptor = getOwnPropertyDescriptor(this, V);\n  return !!descriptor && descriptor.enumerable;\n} : $propertyIsEnumerable;\n","'use strict';\nmodule.exports = function (bitmap, value) {\n  return {\n    enumerable: !(bitmap & 1),\n    configurable: !(bitmap & 2),\n    writable: !(bitmap & 4),\n    value: value\n  };\n};\n","'use strict';\nvar NATIVE_BIND = require('../internals/function-bind-native');\n\nvar FunctionPrototype = Function.prototype;\nvar call = FunctionPrototype.call;\n// eslint-disable-next-line es/no-function-prototype-bind -- safe\nvar uncurryThisWithBind = NATIVE_BIND && FunctionPrototype.bind.bind(call, call);\n\nmodule.exports = NATIVE_BIND ? uncurryThisWithBind : function (fn) {\n  return function () {\n    return call.apply(fn, arguments);\n  };\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\n\nvar toString = uncurryThis({}.toString);\nvar stringSlice = uncurryThis(''.slice);\n\nmodule.exports = function (it) {\n  return stringSlice(toString(it), 8, -1);\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar fails = require('../internals/fails');\nvar classof = require('../internals/classof-raw');\n\nvar $Object = Object;\nvar split = uncurryThis(''.split);\n\n// fallback for non-array-like ES3 and non-enumerable old V8 strings\nmodule.exports = fails(function () {\n  // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346\n  // eslint-disable-next-line no-prototype-builtins -- safe\n  return !$Object('z').propertyIsEnumerable(0);\n}) ? function (it) {\n  return classof(it) === 'String' ? split(it, '') : $Object(it);\n} : $Object;\n","'use strict';\n// we can't use just `it == null` since of `document.all` special case\n// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec\nmodule.exports = function (it) {\n  return it === null || it === undefined;\n};\n","'use strict';\nvar isNullOrUndefined = require('../internals/is-null-or-undefined');\n\nvar $TypeError = TypeError;\n\n// `RequireObjectCoercible` abstract operation\n// https://tc39.es/ecma262/#sec-requireobjectcoercible\nmodule.exports = function (it) {\n  if (isNullOrUndefined(it)) throw new $TypeError(\"Can't call method on \" + it);\n  return it;\n};\n","'use strict';\n// toObject with fallback for non-array-like ES3 strings\nvar IndexedObject = require('../internals/indexed-object');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\n\nmodule.exports = function (it) {\n  return IndexedObject(requireObjectCoercible(it));\n};\n","'use strict';\n// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot\nvar documentAll = typeof document == 'object' && document.all;\n\n// `IsCallable` abstract operation\n// https://tc39.es/ecma262/#sec-iscallable\n// eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing\nmodule.exports = typeof documentAll == 'undefined' && documentAll !== undefined ? function (argument) {\n  return typeof argument == 'function' || argument === documentAll;\n} : function (argument) {\n  return typeof argument == 'function';\n};\n","'use strict';\nvar isCallable = require('../internals/is-callable');\n\nmodule.exports = function (it) {\n  return typeof it == 'object' ? it !== null : isCallable(it);\n};\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar isCallable = require('../internals/is-callable');\n\nvar aFunction = function (argument) {\n  return isCallable(argument) ? argument : undefined;\n};\n\nmodule.exports = function (namespace, method) {\n  return arguments.length < 2 ? aFunction(globalThis[namespace]) : globalThis[namespace] && globalThis[namespace][method];\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\n\nmodule.exports = uncurryThis({}.isPrototypeOf);\n","'use strict';\nvar globalThis = require('../internals/global-this');\n\nvar navigator = globalThis.navigator;\nvar userAgent = navigator && navigator.userAgent;\n\nmodule.exports = userAgent ? String(userAgent) : '';\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar userAgent = require('../internals/environment-user-agent');\n\nvar process = globalThis.process;\nvar Deno = globalThis.Deno;\nvar versions = process && process.versions || Deno && Deno.version;\nvar v8 = versions && versions.v8;\nvar match, version;\n\nif (v8) {\n  match = v8.split('.');\n  // in old Chrome, versions of V8 isn't V8 = Chrome / 10\n  // but their correct versions are not interesting for us\n  version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]);\n}\n\n// BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0`\n// so check `userAgent` even if `.v8` exists, but 0\nif (!version && userAgent) {\n  match = userAgent.match(/Edge\\/(\\d+)/);\n  if (!match || match[1] >= 74) {\n    match = userAgent.match(/Chrome\\/(\\d+)/);\n    if (match) version = +match[1];\n  }\n}\n\nmodule.exports = version;\n","'use strict';\n/* eslint-disable es/no-symbol -- required for testing */\nvar V8_VERSION = require('../internals/environment-v8-version');\nvar fails = require('../internals/fails');\nvar globalThis = require('../internals/global-this');\n\nvar $String = globalThis.String;\n\n// eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing\nmodule.exports = !!Object.getOwnPropertySymbols && !fails(function () {\n  var symbol = Symbol('symbol detection');\n  // Chrome 38 Symbol has incorrect toString conversion\n  // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances\n  // nb: Do not call `String` directly to avoid this being optimized out to `symbol+''` which will,\n  // of course, fail.\n  return !$String(symbol) || !(Object(symbol) instanceof Symbol) ||\n    // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances\n    !Symbol.sham && V8_VERSION && V8_VERSION < 41;\n});\n","'use strict';\n/* eslint-disable es/no-symbol -- required for testing */\nvar NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');\n\nmodule.exports = NATIVE_SYMBOL &&\n  !Symbol.sham &&\n  typeof Symbol.iterator == 'symbol';\n","'use strict';\nvar getBuiltIn = require('../internals/get-built-in');\nvar isCallable = require('../internals/is-callable');\nvar isPrototypeOf = require('../internals/object-is-prototype-of');\nvar USE_SYMBOL_AS_UID = require('../internals/use-symbol-as-uid');\n\nvar $Object = Object;\n\nmodule.exports = USE_SYMBOL_AS_UID ? function (it) {\n  return typeof it == 'symbol';\n} : function (it) {\n  var $Symbol = getBuiltIn('Symbol');\n  return isCallable($Symbol) && isPrototypeOf($Symbol.prototype, $Object(it));\n};\n","'use strict';\nvar $String = String;\n\nmodule.exports = function (argument) {\n  try {\n    return $String(argument);\n  } catch (error) {\n    return 'Object';\n  }\n};\n","'use strict';\nvar isCallable = require('../internals/is-callable');\nvar tryToString = require('../internals/try-to-string');\n\nvar $TypeError = TypeError;\n\n// `Assert: IsCallable(argument) is true`\nmodule.exports = function (argument) {\n  if (isCallable(argument)) return argument;\n  throw new $TypeError(tryToString(argument) + ' is not a function');\n};\n","'use strict';\nvar aCallable = require('../internals/a-callable');\nvar isNullOrUndefined = require('../internals/is-null-or-undefined');\n\n// `GetMethod` abstract operation\n// https://tc39.es/ecma262/#sec-getmethod\nmodule.exports = function (V, P) {\n  var func = V[P];\n  return isNullOrUndefined(func) ? undefined : aCallable(func);\n};\n","'use strict';\nvar call = require('../internals/function-call');\nvar isCallable = require('../internals/is-callable');\nvar isObject = require('../internals/is-object');\n\nvar $TypeError = TypeError;\n\n// `OrdinaryToPrimitive` abstract operation\n// https://tc39.es/ecma262/#sec-ordinarytoprimitive\nmodule.exports = function (input, pref) {\n  var fn, val;\n  if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val;\n  if (isCallable(fn = input.valueOf) && !isObject(val = call(fn, input))) return val;\n  if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val;\n  throw new $TypeError(\"Can't convert object to primitive value\");\n};\n","'use strict';\nmodule.exports = false;\n","'use strict';\nvar globalThis = require('../internals/global-this');\n\n// eslint-disable-next-line es/no-object-defineproperty -- safe\nvar defineProperty = Object.defineProperty;\n\nmodule.exports = function (key, value) {\n  try {\n    defineProperty(globalThis, key, { value: value, configurable: true, writable: true });\n  } catch (error) {\n    globalThis[key] = value;\n  } return value;\n};\n","'use strict';\nvar IS_PURE = require('../internals/is-pure');\nvar globalThis = require('../internals/global-this');\nvar defineGlobalProperty = require('../internals/define-global-property');\n\nvar SHARED = '__core-js_shared__';\nvar store = module.exports = globalThis[SHARED] || defineGlobalProperty(SHARED, {});\n\n(store.versions || (store.versions = [])).push({\n  version: '3.49.0',\n  mode: IS_PURE ? 'pure' : 'global',\n  copyright: '© 2013–2025 Denis Pushkarev (zloirock.ru), 2025–2026 CoreJS Company (core-js.io). All rights reserved.',\n  license: 'https://github.com/zloirock/core-js/blob/v3.49.0/LICENSE',\n  source: 'https://github.com/zloirock/core-js'\n});\n","'use strict';\nvar store = require('../internals/shared-store');\n\nmodule.exports = function (key, value) {\n  return store[key] || (store[key] = value || {});\n};\n","'use strict';\nvar requireObjectCoercible = require('../internals/require-object-coercible');\n\nvar $Object = Object;\n\n// `ToObject` abstract operation\n// https://tc39.es/ecma262/#sec-toobject\nmodule.exports = function (argument) {\n  return $Object(requireObjectCoercible(argument));\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar toObject = require('../internals/to-object');\n\nvar hasOwnProperty = uncurryThis({}.hasOwnProperty);\n\n// `HasOwnProperty` abstract operation\n// https://tc39.es/ecma262/#sec-hasownproperty\n// eslint-disable-next-line es/no-object-hasown -- safe\nmodule.exports = Object.hasOwn || function hasOwn(it, key) {\n  return hasOwnProperty(toObject(it), key);\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\n\nvar id = 0;\nvar postfix = Math.random();\nvar toString = uncurryThis(1.1.toString);\n\nmodule.exports = function (key) {\n  return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36);\n};\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar shared = require('../internals/shared');\nvar hasOwn = require('../internals/has-own-property');\nvar uid = require('../internals/uid');\nvar NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');\nvar USE_SYMBOL_AS_UID = require('../internals/use-symbol-as-uid');\n\nvar Symbol = globalThis.Symbol;\nvar WellKnownSymbolsStore = shared('wks');\nvar createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol['for'] || Symbol : Symbol && Symbol.withoutSetter || uid;\n\nmodule.exports = function (name) {\n  if (!hasOwn(WellKnownSymbolsStore, name)) {\n    WellKnownSymbolsStore[name] = NATIVE_SYMBOL && hasOwn(Symbol, name)\n      ? Symbol[name]\n      : createWellKnownSymbol('Symbol.' + name);\n  } return WellKnownSymbolsStore[name];\n};\n","'use strict';\nvar call = require('../internals/function-call');\nvar isObject = require('../internals/is-object');\nvar isSymbol = require('../internals/is-symbol');\nvar getMethod = require('../internals/get-method');\nvar ordinaryToPrimitive = require('../internals/ordinary-to-primitive');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar $TypeError = TypeError;\nvar TO_PRIMITIVE = wellKnownSymbol('toPrimitive');\n\n// `ToPrimitive` abstract operation\n// https://tc39.es/ecma262/#sec-toprimitive\nmodule.exports = function (input, pref) {\n  if (!isObject(input) || isSymbol(input)) return input;\n  var exoticToPrim = getMethod(input, TO_PRIMITIVE);\n  var result;\n  if (exoticToPrim) {\n    if (pref === undefined) pref = 'default';\n    result = call(exoticToPrim, input, pref);\n    if (!isObject(result) || isSymbol(result)) return result;\n    throw new $TypeError(\"Can't convert object to primitive value\");\n  }\n  if (pref === undefined) pref = 'number';\n  return ordinaryToPrimitive(input, pref);\n};\n","'use strict';\nvar toPrimitive = require('../internals/to-primitive');\nvar isSymbol = require('../internals/is-symbol');\n\n// `ToPropertyKey` abstract operation\n// https://tc39.es/ecma262/#sec-topropertykey\nmodule.exports = function (argument) {\n  var key = toPrimitive(argument, 'string');\n  return isSymbol(key) ? key : key + '';\n};\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar isObject = require('../internals/is-object');\n\nvar document = globalThis.document;\n// typeof document.createElement is 'object' in old IE\nvar EXISTS = isObject(document) && isObject(document.createElement);\n\nmodule.exports = function (it) {\n  return EXISTS ? document.createElement(it) : {};\n};\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\nvar createElement = require('../internals/document-create-element');\n\n// Thanks to IE8 for its funny defineProperty\nmodule.exports = !DESCRIPTORS && !fails(function () {\n  // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n  return Object.defineProperty(createElement('div'), 'a', {\n    get: function () { return 7; }\n  }).a !== 7;\n});\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar call = require('../internals/function-call');\nvar propertyIsEnumerableModule = require('../internals/object-property-is-enumerable');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar toPropertyKey = require('../internals/to-property-key');\nvar hasOwn = require('../internals/has-own-property');\nvar IE8_DOM_DEFINE = require('../internals/ie8-dom-define');\n\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n\n// `Object.getOwnPropertyDescriptor` method\n// https://tc39.es/ecma262/#sec-object.getownpropertydescriptor\nexports.f = DESCRIPTORS ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {\n  O = toIndexedObject(O);\n  P = toPropertyKey(P);\n  if (IE8_DOM_DEFINE) try {\n    return $getOwnPropertyDescriptor(O, P);\n  } catch (error) { /* empty */ }\n  if (hasOwn(O, P)) return createPropertyDescriptor(!call(propertyIsEnumerableModule.f, O, P), O[P]);\n};\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\n\n// V8 ~ Chrome 36-\n// https://bugs.chromium.org/p/v8/issues/detail?id=3334\nmodule.exports = DESCRIPTORS && fails(function () {\n  // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n  return Object.defineProperty(function () { /* empty */ }, 'prototype', {\n    value: 42,\n    writable: false\n  }).prototype !== 42;\n});\n","'use strict';\nvar isObject = require('../internals/is-object');\n\nvar $String = String;\nvar $TypeError = TypeError;\n\n// `Assert: Type(argument) is Object`\nmodule.exports = function (argument) {\n  if (isObject(argument)) return argument;\n  throw new $TypeError($String(argument) + ' is not an object');\n};\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar IE8_DOM_DEFINE = require('../internals/ie8-dom-define');\nvar V8_PROTOTYPE_DEFINE_BUG = require('../internals/v8-prototype-define-bug');\nvar anObject = require('../internals/an-object');\nvar toPropertyKey = require('../internals/to-property-key');\n\nvar $TypeError = TypeError;\n// eslint-disable-next-line es/no-object-defineproperty -- safe\nvar $defineProperty = Object.defineProperty;\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nvar ENUMERABLE = 'enumerable';\nvar CONFIGURABLE = 'configurable';\nvar WRITABLE = 'writable';\n\n// `Object.defineProperty` method\n// https://tc39.es/ecma262/#sec-object.defineproperty\nexports.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) {\n  anObject(O);\n  P = toPropertyKey(P);\n  anObject(Attributes);\n  if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) {\n    var current = $getOwnPropertyDescriptor(O, P);\n    if (current && current[WRITABLE]) {\n      O[P] = Attributes.value;\n      Attributes = {\n        configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE],\n        enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE],\n        writable: false\n      };\n    }\n  } return $defineProperty(O, P, Attributes);\n} : $defineProperty : function defineProperty(O, P, Attributes) {\n  anObject(O);\n  P = toPropertyKey(P);\n  anObject(Attributes);\n  if (IE8_DOM_DEFINE) try {\n    return $defineProperty(O, P, Attributes);\n  } catch (error) { /* empty */ }\n  if ('get' in Attributes || 'set' in Attributes) throw new $TypeError('Accessors not supported');\n  if ('value' in Attributes) O[P] = Attributes.value;\n  return O;\n};\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar definePropertyModule = require('../internals/object-define-property');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\n\nmodule.exports = DESCRIPTORS ? function (object, key, value) {\n  return definePropertyModule.f(object, key, createPropertyDescriptor(1, value));\n} : function (object, key, value) {\n  object[key] = value;\n  return object;\n};\n","'use strict';\nvar DESCRIPTORS = require('../internals/descriptors');\nvar hasOwn = require('../internals/has-own-property');\n\nvar FunctionPrototype = Function.prototype;\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar getDescriptor = DESCRIPTORS && Object.getOwnPropertyDescriptor;\n\nvar EXISTS = hasOwn(FunctionPrototype, 'name');\n// additional protection from minified / mangled / dropped function names\nvar PROPER = EXISTS && function something() { /* empty */ }.name === 'something';\nvar CONFIGURABLE = EXISTS && (!DESCRIPTORS || (DESCRIPTORS && getDescriptor(FunctionPrototype, 'name').configurable));\n\nmodule.exports = {\n  EXISTS: EXISTS,\n  PROPER: PROPER,\n  CONFIGURABLE: CONFIGURABLE\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar isCallable = require('../internals/is-callable');\nvar store = require('../internals/shared-store');\n\nvar functionToString = uncurryThis(Function.toString);\n\n// this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper\nif (!isCallable(store.inspectSource)) {\n  store.inspectSource = function (it) {\n    return functionToString(it);\n  };\n}\n\nmodule.exports = store.inspectSource;\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar isCallable = require('../internals/is-callable');\n\nvar WeakMap = globalThis.WeakMap;\n\nmodule.exports = isCallable(WeakMap) && /native code/.test(String(WeakMap));\n","'use strict';\nvar shared = require('../internals/shared');\nvar uid = require('../internals/uid');\n\nvar keys = shared('keys');\n\nmodule.exports = function (key) {\n  return keys[key] || (keys[key] = uid(key));\n};\n","'use strict';\nmodule.exports = {};\n","'use strict';\nvar NATIVE_WEAK_MAP = require('../internals/weak-map-basic-detection');\nvar globalThis = require('../internals/global-this');\nvar isObject = require('../internals/is-object');\nvar createNonEnumerableProperty = require('../internals/create-non-enumerable-property');\nvar hasOwn = require('../internals/has-own-property');\nvar shared = require('../internals/shared-store');\nvar sharedKey = require('../internals/shared-key');\nvar hiddenKeys = require('../internals/hidden-keys');\n\nvar OBJECT_ALREADY_INITIALIZED = 'Object already initialized';\nvar TypeError = globalThis.TypeError;\nvar WeakMap = globalThis.WeakMap;\nvar set, get, has;\n\nvar enforce = function (it) {\n  return has(it) ? get(it) : set(it, {});\n};\n\nvar getterFor = function (TYPE) {\n  return function (it) {\n    var state;\n    if (!isObject(it) || (state = get(it)).type !== TYPE) {\n      throw new TypeError('Incompatible receiver, ' + TYPE + ' required');\n    } return state;\n  };\n};\n\nif (NATIVE_WEAK_MAP || shared.state) {\n  var store = shared.state || (shared.state = new WeakMap());\n  /* eslint-disable no-self-assign -- prototype methods protection */\n  store.get = store.get;\n  store.has = store.has;\n  store.set = store.set;\n  /* eslint-enable no-self-assign -- prototype methods protection */\n  set = function (it, metadata) {\n    if (store.has(it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);\n    metadata.facade = it;\n    store.set(it, metadata);\n    return metadata;\n  };\n  get = function (it) {\n    return store.get(it) || {};\n  };\n  has = function (it) {\n    return store.has(it);\n  };\n} else {\n  var STATE = sharedKey('state');\n  hiddenKeys[STATE] = true;\n  set = function (it, metadata) {\n    if (hasOwn(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);\n    metadata.facade = it;\n    createNonEnumerableProperty(it, STATE, metadata);\n    return metadata;\n  };\n  get = function (it) {\n    return hasOwn(it, STATE) ? it[STATE] : {};\n  };\n  has = function (it) {\n    return hasOwn(it, STATE);\n  };\n}\n\nmodule.exports = {\n  set: set,\n  get: get,\n  has: has,\n  enforce: enforce,\n  getterFor: getterFor\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar fails = require('../internals/fails');\nvar isCallable = require('../internals/is-callable');\nvar hasOwn = require('../internals/has-own-property');\nvar DESCRIPTORS = require('../internals/descriptors');\nvar CONFIGURABLE_FUNCTION_NAME = require('../internals/function-name').CONFIGURABLE;\nvar inspectSource = require('../internals/inspect-source');\nvar InternalStateModule = require('../internals/internal-state');\n\nvar enforceInternalState = InternalStateModule.enforce;\nvar getInternalState = InternalStateModule.get;\nvar $String = String;\n// eslint-disable-next-line es/no-object-defineproperty -- safe\nvar defineProperty = Object.defineProperty;\nvar stringSlice = uncurryThis(''.slice);\nvar replace = uncurryThis(''.replace);\nvar join = uncurryThis([].join);\n\nvar CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function () {\n  return defineProperty(function () { /* empty */ }, 'length', { value: 8 }).length !== 8;\n});\n\nvar TEMPLATE = String(String).split('String');\n\nvar makeBuiltIn = module.exports = function (value, name, options) {\n  if (stringSlice($String(name), 0, 7) === 'Symbol(') {\n    name = '[' + replace($String(name), /^Symbol\\(([^)]*)\\).*$/, '$1') + ']';\n  }\n  if (options && options.getter) name = 'get ' + name;\n  if (options && options.setter) name = 'set ' + name;\n  if (!hasOwn(value, 'name') || (CONFIGURABLE_FUNCTION_NAME && value.name !== name)) {\n    if (DESCRIPTORS) defineProperty(value, 'name', { value: name, configurable: true });\n    else value.name = name;\n  }\n  if (CONFIGURABLE_LENGTH && options && hasOwn(options, 'arity') && value.length !== options.arity) {\n    defineProperty(value, 'length', { value: options.arity });\n  }\n  try {\n    if (options && hasOwn(options, 'constructor') && options.constructor) {\n      if (DESCRIPTORS) defineProperty(value, 'prototype', { writable: false });\n    // in V8 ~ Chrome 53, prototypes of some methods, like `Array.prototype.values`, are non-writable\n    } else if (value.prototype) value.prototype = undefined;\n  } catch (error) { /* empty */ }\n  var state = enforceInternalState(value);\n  if (!hasOwn(state, 'source')) {\n    state.source = join(TEMPLATE, typeof name == 'string' ? name : '');\n  } return value;\n};\n\n// add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative\n// eslint-disable-next-line no-extend-native -- required\nFunction.prototype.toString = makeBuiltIn(function toString() {\n  return isCallable(this) && getInternalState(this).source || inspectSource(this);\n}, 'toString');\n","'use strict';\nvar isCallable = require('../internals/is-callable');\nvar definePropertyModule = require('../internals/object-define-property');\nvar makeBuiltIn = require('../internals/make-built-in');\nvar defineGlobalProperty = require('../internals/define-global-property');\n\nmodule.exports = function (O, key, value, options) {\n  if (!options) options = {};\n  var simple = options.enumerable;\n  var name = options.name !== undefined ? options.name : key;\n  if (isCallable(value)) makeBuiltIn(value, name, options);\n  if (options.global) {\n    if (simple) O[key] = value;\n    else defineGlobalProperty(key, value);\n  } else {\n    try {\n      if (!options.unsafe) delete O[key];\n      else if (O[key]) simple = true;\n    } catch (error) { /* empty */ }\n    if (simple) O[key] = value;\n    else definePropertyModule.f(O, key, {\n      value: value,\n      enumerable: false,\n      configurable: !options.nonConfigurable,\n      writable: !options.nonWritable\n    });\n  } return O;\n};\n","'use strict';\nvar ceil = Math.ceil;\nvar floor = Math.floor;\n\n// `Math.trunc` method\n// https://tc39.es/ecma262/#sec-math.trunc\n// eslint-disable-next-line es/no-math-trunc -- safe\nmodule.exports = Math.trunc || function trunc(x) {\n  var n = +x;\n  return (n > 0 ? floor : ceil)(n);\n};\n","'use strict';\nvar trunc = require('../internals/math-trunc');\n\n// `ToIntegerOrInfinity` abstract operation\n// https://tc39.es/ecma262/#sec-tointegerorinfinity\nmodule.exports = function (argument) {\n  var number = +argument;\n  // eslint-disable-next-line no-self-compare -- NaN check\n  return number !== number || number === 0 ? 0 : trunc(number);\n};\n","'use strict';\nvar toIntegerOrInfinity = require('../internals/to-integer-or-infinity');\n\nvar max = Math.max;\nvar min = Math.min;\n\n// Helper for a popular repeating case of the spec:\n// Let integer be ? ToInteger(index).\n// If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).\nmodule.exports = function (index, length) {\n  var integer = toIntegerOrInfinity(index);\n  return integer < 0 ? max(integer + length, 0) : min(integer, length);\n};\n","'use strict';\nvar toIntegerOrInfinity = require('../internals/to-integer-or-infinity');\n\nvar min = Math.min;\n\n// `ToLength` abstract operation\n// https://tc39.es/ecma262/#sec-tolength\nmodule.exports = function (argument) {\n  var len = toIntegerOrInfinity(argument);\n  return len > 0 ? min(len, 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991\n};\n","'use strict';\nvar toLength = require('../internals/to-length');\n\n// `LengthOfArrayLike` abstract operation\n// https://tc39.es/ecma262/#sec-lengthofarraylike\nmodule.exports = function (obj) {\n  return toLength(obj.length);\n};\n","'use strict';\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar toAbsoluteIndex = require('../internals/to-absolute-index');\nvar lengthOfArrayLike = require('../internals/length-of-array-like');\n\n// `Array.prototype.{ indexOf, includes }` methods implementation\nvar createMethod = function (IS_INCLUDES) {\n  return function ($this, el, fromIndex) {\n    var O = toIndexedObject($this);\n    var length = lengthOfArrayLike(O);\n    if (length === 0) return !IS_INCLUDES && -1;\n    var index = toAbsoluteIndex(fromIndex, length);\n    var value;\n    // Array#includes uses SameValueZero equality algorithm\n    // eslint-disable-next-line no-self-compare -- NaN check\n    if (IS_INCLUDES && el !== el) while (length > index) {\n      value = O[index++];\n      // eslint-disable-next-line no-self-compare -- NaN check\n      if (value !== value) return true;\n    // Array#indexOf ignores holes, Array#includes - not\n    } else for (;length > index; index++) {\n      if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;\n    } return !IS_INCLUDES && -1;\n  };\n};\n\nmodule.exports = {\n  // `Array.prototype.includes` method\n  // https://tc39.es/ecma262/#sec-array.prototype.includes\n  includes: createMethod(true),\n  // `Array.prototype.indexOf` method\n  // https://tc39.es/ecma262/#sec-array.prototype.indexof\n  indexOf: createMethod(false)\n};\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar hasOwn = require('../internals/has-own-property');\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar indexOf = require('../internals/array-includes').indexOf;\nvar hiddenKeys = require('../internals/hidden-keys');\n\nvar push = uncurryThis([].push);\n\nmodule.exports = function (object, names) {\n  var O = toIndexedObject(object);\n  var i = 0;\n  var result = [];\n  var key;\n  for (key in O) !hasOwn(hiddenKeys, key) && hasOwn(O, key) && push(result, key);\n  // Don't enum bug & hidden keys\n  while (names.length > i) if (hasOwn(O, key = names[i++])) {\n    ~indexOf(result, key) || push(result, key);\n  }\n  return result;\n};\n","'use strict';\n// IE8- don't enum bug keys\nmodule.exports = [\n  'constructor',\n  'hasOwnProperty',\n  'isPrototypeOf',\n  'propertyIsEnumerable',\n  'toLocaleString',\n  'toString',\n  'valueOf'\n];\n","'use strict';\nvar internalObjectKeys = require('../internals/object-keys-internal');\nvar enumBugKeys = require('../internals/enum-bug-keys');\n\nvar hiddenKeys = enumBugKeys.concat('length', 'prototype');\n\n// `Object.getOwnPropertyNames` method\n// https://tc39.es/ecma262/#sec-object.getownpropertynames\n// eslint-disable-next-line es/no-object-getownpropertynames -- safe\nexports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {\n  return internalObjectKeys(O, hiddenKeys);\n};\n","'use strict';\n// eslint-disable-next-line es/no-object-getownpropertysymbols -- safe\nexports.f = Object.getOwnPropertySymbols;\n","'use strict';\nvar getBuiltIn = require('../internals/get-built-in');\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar getOwnPropertyNamesModule = require('../internals/object-get-own-property-names');\nvar getOwnPropertySymbolsModule = require('../internals/object-get-own-property-symbols');\nvar anObject = require('../internals/an-object');\n\nvar concat = uncurryThis([].concat);\n\n// all object keys, includes non-enumerable and symbols\nmodule.exports = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {\n  var keys = getOwnPropertyNamesModule.f(anObject(it));\n  var getOwnPropertySymbols = getOwnPropertySymbolsModule.f;\n  return getOwnPropertySymbols ? concat(keys, getOwnPropertySymbols(it)) : keys;\n};\n","'use strict';\nvar hasOwn = require('../internals/has-own-property');\nvar ownKeys = require('../internals/own-keys');\nvar getOwnPropertyDescriptorModule = require('../internals/object-get-own-property-descriptor');\nvar definePropertyModule = require('../internals/object-define-property');\n\nmodule.exports = function (target, source, exceptions) {\n  var keys = ownKeys(source);\n  var defineProperty = definePropertyModule.f;\n  var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f;\n  for (var i = 0; i < keys.length; i++) {\n    var key = keys[i];\n    if (!hasOwn(target, key) && !(exceptions && hasOwn(exceptions, key))) {\n      defineProperty(target, key, getOwnPropertyDescriptor(source, key));\n    }\n  }\n};\n","'use strict';\nvar fails = require('../internals/fails');\nvar isCallable = require('../internals/is-callable');\n\nvar replacement = /#|\\.prototype\\./;\n\nvar isForced = function (feature, detection) {\n  var value = data[normalize(feature)];\n  return value === POLYFILL ? true\n    : value === NATIVE ? false\n    : isCallable(detection) ? fails(detection)\n    : !!detection;\n};\n\nvar normalize = isForced.normalize = function (string) {\n  return String(string).replace(replacement, '.').toLowerCase();\n};\n\nvar data = isForced.data = {};\nvar NATIVE = isForced.NATIVE = 'N';\nvar POLYFILL = isForced.POLYFILL = 'P';\n\nmodule.exports = isForced;\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor').f;\nvar createNonEnumerableProperty = require('../internals/create-non-enumerable-property');\nvar defineBuiltIn = require('../internals/define-built-in');\nvar defineGlobalProperty = require('../internals/define-global-property');\nvar copyConstructorProperties = require('../internals/copy-constructor-properties');\nvar isForced = require('../internals/is-forced');\n\n/*\n  options.target         - name of the target object\n  options.global         - target is the global object\n  options.stat           - export as static methods of target\n  options.proto          - export as prototype methods of target\n  options.real           - real prototype method for the `pure` version\n  options.forced         - export even if the native feature is available\n  options.bind           - bind methods to the target, required for the `pure` version\n  options.wrap           - wrap constructors to preventing global pollution, required for the `pure` version\n  options.unsafe         - use the simple assignment of property instead of delete + defineProperty\n  options.sham           - add a flag to not completely full polyfills\n  options.enumerable     - export as enumerable property\n  options.dontCallGetSet - prevent calling a getter on target\n  options.name           - the .name of the function if it does not match the key\n*/\nmodule.exports = function (options, source) {\n  var TARGET = options.target;\n  var GLOBAL = options.global;\n  var STATIC = options.stat;\n  var FORCED, target, key, targetProperty, sourceProperty, descriptor;\n  if (GLOBAL) {\n    target = globalThis;\n  } else if (STATIC) {\n    target = globalThis[TARGET] || defineGlobalProperty(TARGET, {});\n  } else {\n    target = globalThis[TARGET] && globalThis[TARGET].prototype;\n  }\n  if (target) for (key in source) {\n    sourceProperty = source[key];\n    if (options.dontCallGetSet) {\n      descriptor = getOwnPropertyDescriptor(target, key);\n      targetProperty = descriptor && descriptor.value;\n    } else targetProperty = target[key];\n    FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);\n    // contained in target\n    if (!FORCED && targetProperty !== undefined) {\n      if (typeof sourceProperty == typeof targetProperty) continue;\n      copyConstructorProperties(sourceProperty, targetProperty);\n    }\n    // add a flag to not completely full polyfills\n    if (options.sham || (targetProperty && targetProperty.sham)) {\n      createNonEnumerableProperty(sourceProperty, 'sham', true);\n    }\n    defineBuiltIn(target, key, sourceProperty, options);\n  }\n};\n","'use strict';\nvar $ = require('../internals/export');\nvar globalThis = require('../internals/global-this');\n\n// `globalThis` object\n// https://tc39.es/ecma262/#sec-globalthis\n$({ global: true, forced: globalThis.globalThis !== globalThis }, {\n  globalThis: globalThis\n});\n","'use strict';\n// TODO: Remove from `core-js@4`\nrequire('../modules/es.global-this');\n","'use strict';\n// https://github.com/tc39/proposal-global\nrequire('../modules/esnext.global-this');\nvar globalThis = require('../internals/global-this');\n\nmodule.exports = globalThis;\n","'use strict';\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar TO_STRING_TAG = wellKnownSymbol('toStringTag');\nvar test = {};\n// eslint-disable-next-line unicorn/no-immediate-mutation -- ES3 syntax limitation\ntest[TO_STRING_TAG] = 'z';\n\nmodule.exports = String(test) === '[object z]';\n","'use strict';\nvar TO_STRING_TAG_SUPPORT = require('../internals/to-string-tag-support');\nvar isCallable = require('../internals/is-callable');\nvar classofRaw = require('../internals/classof-raw');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar TO_STRING_TAG = wellKnownSymbol('toStringTag');\nvar $Object = Object;\n\n// ES3 wrong here\nvar CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) === 'Arguments';\n\n// fallback for IE11 Script Access Denied error\nvar tryGet = function (it, key) {\n  try {\n    return it[key];\n  } catch (error) { /* empty */ }\n};\n\n// getting tag from ES6+ `Object.prototype.toString`\nmodule.exports = TO_STRING_TAG_SUPPORT ? classofRaw : function (it) {\n  var O, tag, result;\n  return it === undefined ? 'Undefined' : it === null ? 'Null'\n    // @@toStringTag case\n    : typeof (tag = tryGet(O = $Object(it), TO_STRING_TAG)) == 'string' ? tag\n    // builtinTag case\n    : CORRECT_ARGUMENTS ? classofRaw(O)\n    // ES3 arguments fallback\n    : (result = classofRaw(O)) === 'Object' && isCallable(O.callee) ? 'Arguments' : result;\n};\n","'use strict';\nvar classof = require('../internals/classof');\n\nvar $String = String;\n\nmodule.exports = function (argument) {\n  if (classof(argument) === 'Symbol') throw new TypeError('Cannot convert a Symbol value to a string');\n  return $String(argument);\n};\n","'use strict';\n// a string of all valid unicode whitespaces\nmodule.exports = '\\u0009\\u000A\\u000B\\u000C\\u000D\\u0020\\u00A0\\u1680\\u2000\\u2001\\u2002' +\n  '\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200A\\u202F\\u205F\\u3000\\u2028\\u2029\\uFEFF';\n","'use strict';\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\nvar toString = require('../internals/to-string');\nvar whitespaces = require('../internals/whitespaces');\n\nvar replace = uncurryThis(''.replace);\nvar ltrim = RegExp('^[' + whitespaces + ']+');\nvar rtrim = RegExp('(^|[^' + whitespaces + '])[' + whitespaces + ']+$');\n\n// `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation\nvar createMethod = function (TYPE) {\n  return function ($this) {\n    var string = toString(requireObjectCoercible($this));\n    if (TYPE & 1) string = replace(string, ltrim, '');\n    if (TYPE & 2) string = replace(string, rtrim, '$1');\n    return string;\n  };\n};\n\nmodule.exports = {\n  // `String.prototype.{ trimLeft, trimStart }` methods\n  // https://tc39.es/ecma262/#sec-string.prototype.trimstart\n  start: createMethod(1),\n  // `String.prototype.{ trimRight, trimEnd }` methods\n  // https://tc39.es/ecma262/#sec-string.prototype.trimend\n  end: createMethod(2),\n  // `String.prototype.trim` method\n  // https://tc39.es/ecma262/#sec-string.prototype.trim\n  trim: createMethod(3)\n};\n","'use strict';\nvar PROPER_FUNCTION_NAME = require('../internals/function-name').PROPER;\nvar fails = require('../internals/fails');\nvar whitespaces = require('../internals/whitespaces');\n\nvar non = '\\u200B\\u0085\\u180E';\n\n// check that a method works with the correct list\n// of whitespaces and has a correct name\nmodule.exports = function (METHOD_NAME) {\n  return fails(function () {\n    return !!whitespaces[METHOD_NAME]()\n      || non[METHOD_NAME]() !== non\n      || (PROPER_FUNCTION_NAME && whitespaces[METHOD_NAME].name !== METHOD_NAME);\n  });\n};\n","'use strict';\nvar $trimStart = require('../internals/string-trim').start;\nvar forcedStringTrimMethod = require('../internals/string-trim-forced');\n\n// `String.prototype.{ trimStart, trimLeft }` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimstart\n// https://tc39.es/ecma262/#String.prototype.trimleft\nmodule.exports = forcedStringTrimMethod('trimStart') ? function trimStart() {\n  return $trimStart(this);\n// eslint-disable-next-line es/no-string-prototype-trimstart-trimend -- safe\n} : ''.trimStart;\n","'use strict';\nvar $ = require('../internals/export');\nvar trimStart = require('../internals/string-trim-start');\n\n// `String.prototype.trimLeft` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimleft\n// eslint-disable-next-line es/no-string-prototype-trimleft-trimright -- safe\n$({ target: 'String', proto: true, name: 'trimStart', forced: ''.trimLeft !== trimStart }, {\n  trimLeft: trimStart\n});\n","'use strict';\n// TODO: Remove this line from `core-js@4`\nrequire('../modules/es.string.trim-left');\nvar $ = require('../internals/export');\nvar trimStart = require('../internals/string-trim-start');\n\n// `String.prototype.trimStart` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimstart\n// eslint-disable-next-line es/no-string-prototype-trimstart-trimend -- safe\n$({ target: 'String', proto: true, name: 'trimStart', forced: ''.trimStart !== trimStart }, {\n  trimStart: trimStart\n});\n","'use strict';\nvar globalThis = require('../internals/global-this');\nvar uncurryThis = require('../internals/function-uncurry-this');\n\nmodule.exports = function (CONSTRUCTOR, METHOD) {\n  return uncurryThis(globalThis[CONSTRUCTOR].prototype[METHOD]);\n};\n","'use strict';\nrequire('../../modules/es.string.trim-start');\nvar entryUnbind = require('../../internals/entry-unbind');\n\nmodule.exports = entryUnbind('String', 'trimLeft');\n","'use strict';\nvar $trimEnd = require('../internals/string-trim').end;\nvar forcedStringTrimMethod = require('../internals/string-trim-forced');\n\n// `String.prototype.{ trimEnd, trimRight }` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimend\n// https://tc39.es/ecma262/#String.prototype.trimright\nmodule.exports = forcedStringTrimMethod('trimEnd') ? function trimEnd() {\n  return $trimEnd(this);\n// eslint-disable-next-line es/no-string-prototype-trimstart-trimend -- safe\n} : ''.trimEnd;\n","'use strict';\nvar $ = require('../internals/export');\nvar trimEnd = require('../internals/string-trim-end');\n\n// `String.prototype.trimRight` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimend\n// eslint-disable-next-line es/no-string-prototype-trimleft-trimright -- safe\n$({ target: 'String', proto: true, name: 'trimEnd', forced: ''.trimRight !== trimEnd }, {\n  trimRight: trimEnd\n});\n","'use strict';\n// TODO: Remove this line from `core-js@4`\nrequire('../modules/es.string.trim-right');\nvar $ = require('../internals/export');\nvar trimEnd = require('../internals/string-trim-end');\n\n// `String.prototype.trimEnd` method\n// https://tc39.es/ecma262/#sec-string.prototype.trimend\n// eslint-disable-next-line es/no-string-prototype-trimstart-trimend -- safe\n$({ target: 'String', proto: true, name: 'trimEnd', forced: ''.trimEnd !== trimEnd }, {\n  trimEnd: trimEnd\n});\n","'use strict';\nrequire('../../modules/es.string.trim-end');\nvar entryUnbind = require('../../internals/entry-unbind');\n\nmodule.exports = entryUnbind('String', 'trimRight');\n","/*!\n *  howler.js v2.2.4\n *  howlerjs.com\n *\n *  (c) 2013-2020, James Simpson of GoldFire Studios\n *  goldfirestudios.com\n *\n *  MIT License\n */\n\n(function() {\n\n  'use strict';\n\n  /** Global Methods **/\n  /***************************************************************************/\n\n  /**\n   * Create the global controller. All contained methods and properties apply\n   * to all sounds that are currently playing or will be in the future.\n   */\n  var HowlerGlobal = function() {\n    this.init();\n  };\n  HowlerGlobal.prototype = {\n    /**\n     * Initialize the global Howler object.\n     * @return {Howler}\n     */\n    init: function() {\n      var self = this || Howler;\n\n      // Create a global ID counter.\n      self._counter = 1000;\n\n      // Pool of unlocked HTML5 Audio objects.\n      self._html5AudioPool = [];\n      self.html5PoolSize = 10;\n\n      // Internal properties.\n      self._codecs = {};\n      self._howls = [];\n      self._muted = false;\n      self._volume = 1;\n      self._canPlayEvent = 'canplaythrough';\n      self._navigator = (typeof window !== 'undefined' && window.navigator) ? window.navigator : null;\n\n      // Public properties.\n      self.masterGain = null;\n      self.noAudio = false;\n      self.usingWebAudio = true;\n      self.autoSuspend = true;\n      self.ctx = null;\n\n      // Set to false to disable the auto audio unlocker.\n      self.autoUnlock = true;\n\n      // Setup the various state values for global tracking.\n      self._setup();\n\n      return self;\n    },\n\n    /**\n     * Get/set the global volume for all sounds.\n     * @param  {Float} vol Volume from 0.0 to 1.0.\n     * @return {Howler/Float}     Returns self or current volume.\n     */\n    volume: function(vol) {\n      var self = this || Howler;\n      vol = parseFloat(vol);\n\n      // If we don't have an AudioContext created yet, run the setup.\n      if (!self.ctx) {\n        setupAudioContext();\n      }\n\n      if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {\n        self._volume = vol;\n\n        // Don't update any of the nodes if we are muted.\n        if (self._muted) {\n          return self;\n        }\n\n        // When using Web Audio, we just need to adjust the master gain.\n        if (self.usingWebAudio) {\n          self.masterGain.gain.setValueAtTime(vol, Howler.ctx.currentTime);\n        }\n\n        // Loop through and change volume for all HTML5 audio nodes.\n        for (var i=0; i<self._howls.length; i++) {\n          if (!self._howls[i]._webAudio) {\n            // Get all of the sounds in this Howl group.\n            var ids = self._howls[i]._getSoundIds();\n\n            // Loop through all sounds and change the volumes.\n            for (var j=0; j<ids.length; j++) {\n              var sound = self._howls[i]._soundById(ids[j]);\n\n              if (sound && sound._node) {\n                sound._node.volume = sound._volume * vol;\n              }\n            }\n          }\n        }\n\n        return self;\n      }\n\n      return self._volume;\n    },\n\n    /**\n     * Handle muting and unmuting globally.\n     * @param  {Boolean} muted Is muted or not.\n     */\n    mute: function(muted) {\n      var self = this || Howler;\n\n      // If we don't have an AudioContext created yet, run the setup.\n      if (!self.ctx) {\n        setupAudioContext();\n      }\n\n      self._muted = muted;\n\n      // With Web Audio, we just need to mute the master gain.\n      if (self.usingWebAudio) {\n        self.masterGain.gain.setValueAtTime(muted ? 0 : self._volume, Howler.ctx.currentTime);\n      }\n\n      // Loop through and mute all HTML5 Audio nodes.\n      for (var i=0; i<self._howls.length; i++) {\n        if (!self._howls[i]._webAudio) {\n          // Get all of the sounds in this Howl group.\n          var ids = self._howls[i]._getSoundIds();\n\n          // Loop through all sounds and mark the audio node as muted.\n          for (var j=0; j<ids.length; j++) {\n            var sound = self._howls[i]._soundById(ids[j]);\n\n            if (sound && sound._node) {\n              sound._node.muted = (muted) ? true : sound._muted;\n            }\n          }\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Handle stopping all sounds globally.\n     */\n    stop: function() {\n      var self = this || Howler;\n\n      // Loop through all Howls and stop them.\n      for (var i=0; i<self._howls.length; i++) {\n        self._howls[i].stop();\n      }\n\n      return self;\n    },\n\n    /**\n     * Unload and destroy all currently loaded Howl objects.\n     * @return {Howler}\n     */\n    unload: function() {\n      var self = this || Howler;\n\n      for (var i=self._howls.length-1; i>=0; i--) {\n        self._howls[i].unload();\n      }\n\n      // Create a new AudioContext to make sure it is fully reset.\n      if (self.usingWebAudio && self.ctx && typeof self.ctx.close !== 'undefined') {\n        self.ctx.close();\n        self.ctx = null;\n        setupAudioContext();\n      }\n\n      return self;\n    },\n\n    /**\n     * Check for codec support of specific extension.\n     * @param  {String} ext Audio file extention.\n     * @return {Boolean}\n     */\n    codecs: function(ext) {\n      return (this || Howler)._codecs[ext.replace(/^x-/, '')];\n    },\n\n    /**\n     * Setup various state values for global tracking.\n     * @return {Howler}\n     */\n    _setup: function() {\n      var self = this || Howler;\n\n      // Keeps track of the suspend/resume state of the AudioContext.\n      self.state = self.ctx ? self.ctx.state || 'suspended' : 'suspended';\n\n      // Automatically begin the 30-second suspend process\n      self._autoSuspend();\n\n      // Check if audio is available.\n      if (!self.usingWebAudio) {\n        // No audio is available on this system if noAudio is set to true.\n        if (typeof Audio !== 'undefined') {\n          try {\n            var test = new Audio();\n\n            // Check if the canplaythrough event is available.\n            if (typeof test.oncanplaythrough === 'undefined') {\n              self._canPlayEvent = 'canplay';\n            }\n          } catch(e) {\n            self.noAudio = true;\n          }\n        } else {\n          self.noAudio = true;\n        }\n      }\n\n      // Test to make sure audio isn't disabled in Internet Explorer.\n      try {\n        var test = new Audio();\n        if (test.muted) {\n          self.noAudio = true;\n        }\n      } catch (e) {}\n\n      // Check for supported codecs.\n      if (!self.noAudio) {\n        self._setupCodecs();\n      }\n\n      return self;\n    },\n\n    /**\n     * Check for browser support for various codecs and cache the results.\n     * @return {Howler}\n     */\n    _setupCodecs: function() {\n      var self = this || Howler;\n      var audioTest = null;\n\n      // Must wrap in a try/catch because IE11 in server mode throws an error.\n      try {\n        audioTest = (typeof Audio !== 'undefined') ? new Audio() : null;\n      } catch (err) {\n        return self;\n      }\n\n      if (!audioTest || typeof audioTest.canPlayType !== 'function') {\n        return self;\n      }\n\n      var mpegTest = audioTest.canPlayType('audio/mpeg;').replace(/^no$/, '');\n\n      // Opera version <33 has mixed MP3 support, so we need to check for and block it.\n      var ua = self._navigator ? self._navigator.userAgent : '';\n      var checkOpera = ua.match(/OPR\\/(\\d+)/g);\n      var isOldOpera = (checkOpera && parseInt(checkOpera[0].split('/')[1], 10) < 33);\n      var checkSafari = ua.indexOf('Safari') !== -1 && ua.indexOf('Chrome') === -1;\n      var safariVersion = ua.match(/Version\\/(.*?) /);\n      var isOldSafari = (checkSafari && safariVersion && parseInt(safariVersion[1], 10) < 15);\n\n      self._codecs = {\n        mp3: !!(!isOldOpera && (mpegTest || audioTest.canPlayType('audio/mp3;').replace(/^no$/, ''))),\n        mpeg: !!mpegTest,\n        opus: !!audioTest.canPlayType('audio/ogg; codecs=\"opus\"').replace(/^no$/, ''),\n        ogg: !!audioTest.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/, ''),\n        oga: !!audioTest.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/, ''),\n        wav: !!(audioTest.canPlayType('audio/wav; codecs=\"1\"') || audioTest.canPlayType('audio/wav')).replace(/^no$/, ''),\n        aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),\n        caf: !!audioTest.canPlayType('audio/x-caf;').replace(/^no$/, ''),\n        m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n        m4b: !!(audioTest.canPlayType('audio/x-m4b;') || audioTest.canPlayType('audio/m4b;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n        mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n        weba: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs=\"vorbis\"').replace(/^no$/, '')),\n        webm: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs=\"vorbis\"').replace(/^no$/, '')),\n        dolby: !!audioTest.canPlayType('audio/mp4; codecs=\"ec-3\"').replace(/^no$/, ''),\n        flac: !!(audioTest.canPlayType('audio/x-flac;') || audioTest.canPlayType('audio/flac;')).replace(/^no$/, '')\n      };\n\n      return self;\n    },\n\n    /**\n     * Some browsers/devices will only allow audio to be played after a user interaction.\n     * Attempt to automatically unlock audio on the first user interaction.\n     * Concept from: http://paulbakaus.com/tutorials/html5/web-audio-on-ios/\n     * @return {Howler}\n     */\n    _unlockAudio: function() {\n      var self = this || Howler;\n\n      // Only run this if Web Audio is supported and it hasn't already been unlocked.\n      if (self._audioUnlocked || !self.ctx) {\n        return;\n      }\n\n      self._audioUnlocked = false;\n      self.autoUnlock = false;\n\n      // Some mobile devices/platforms have distortion issues when opening/closing tabs and/or web views.\n      // Bugs in the browser (especially Mobile Safari) can cause the sampleRate to change from 44100 to 48000.\n      // By calling Howler.unload(), we create a new AudioContext with the correct sampleRate.\n      if (!self._mobileUnloaded && self.ctx.sampleRate !== 44100) {\n        self._mobileUnloaded = true;\n        self.unload();\n      }\n\n      // Scratch buffer for enabling iOS to dispose of web audio buffers correctly, as per:\n      // http://stackoverflow.com/questions/24119684\n      self._scratchBuffer = self.ctx.createBuffer(1, 1, 22050);\n\n      // Call this method on touch start to create and play a buffer,\n      // then check if the audio actually played to determine if\n      // audio has now been unlocked on iOS, Android, etc.\n      var unlock = function(e) {\n        // Create a pool of unlocked HTML5 Audio objects that can\n        // be used for playing sounds without user interaction. HTML5\n        // Audio objects must be individually unlocked, as opposed\n        // to the WebAudio API which only needs a single activation.\n        // This must occur before WebAudio setup or the source.onended\n        // event will not fire.\n        while (self._html5AudioPool.length < self.html5PoolSize) {\n          try {\n            var audioNode = new Audio();\n\n            // Mark this Audio object as unlocked to ensure it can get returned\n            // to the unlocked pool when released.\n            audioNode._unlocked = true;\n\n            // Add the audio node to the pool.\n            self._releaseHtml5Audio(audioNode);\n          } catch (e) {\n            self.noAudio = true;\n            break;\n          }\n        }\n\n        // Loop through any assigned audio nodes and unlock them.\n        for (var i=0; i<self._howls.length; i++) {\n          if (!self._howls[i]._webAudio) {\n            // Get all of the sounds in this Howl group.\n            var ids = self._howls[i]._getSoundIds();\n\n            // Loop through all sounds and unlock the audio nodes.\n            for (var j=0; j<ids.length; j++) {\n              var sound = self._howls[i]._soundById(ids[j]);\n\n              if (sound && sound._node && !sound._node._unlocked) {\n                sound._node._unlocked = true;\n                sound._node.load();\n              }\n            }\n          }\n        }\n\n        // Fix Android can not play in suspend state.\n        self._autoResume();\n\n        // Create an empty buffer.\n        var source = self.ctx.createBufferSource();\n        source.buffer = self._scratchBuffer;\n        source.connect(self.ctx.destination);\n\n        // Play the empty buffer.\n        if (typeof source.start === 'undefined') {\n          source.noteOn(0);\n        } else {\n          source.start(0);\n        }\n\n        // Calling resume() on a stack initiated by user gesture is what actually unlocks the audio on Android Chrome >= 55.\n        if (typeof self.ctx.resume === 'function') {\n          self.ctx.resume();\n        }\n\n        // Setup a timeout to check that we are unlocked on the next event loop.\n        source.onended = function() {\n          source.disconnect(0);\n\n          // Update the unlocked state and prevent this check from happening again.\n          self._audioUnlocked = true;\n\n          // Remove the touch start listener.\n          document.removeEventListener('touchstart', unlock, true);\n          document.removeEventListener('touchend', unlock, true);\n          document.removeEventListener('click', unlock, true);\n          document.removeEventListener('keydown', unlock, true);\n\n          // Let all sounds know that audio has been unlocked.\n          for (var i=0; i<self._howls.length; i++) {\n            self._howls[i]._emit('unlock');\n          }\n        };\n      };\n\n      // Setup a touch start listener to attempt an unlock in.\n      document.addEventListener('touchstart', unlock, true);\n      document.addEventListener('touchend', unlock, true);\n      document.addEventListener('click', unlock, true);\n      document.addEventListener('keydown', unlock, true);\n\n      return self;\n    },\n\n    /**\n     * Get an unlocked HTML5 Audio object from the pool. If none are left,\n     * return a new Audio object and throw a warning.\n     * @return {Audio} HTML5 Audio object.\n     */\n    _obtainHtml5Audio: function() {\n      var self = this || Howler;\n\n      // Return the next object from the pool if one exists.\n      if (self._html5AudioPool.length) {\n        return self._html5AudioPool.pop();\n      }\n\n      //.Check if the audio is locked and throw a warning.\n      var testPlay = new Audio().play();\n      if (testPlay && typeof Promise !== 'undefined' && (testPlay instanceof Promise || typeof testPlay.then === 'function')) {\n        testPlay.catch(function() {\n          console.warn('HTML5 Audio pool exhausted, returning potentially locked audio object.');\n        });\n      }\n\n      return new Audio();\n    },\n\n    /**\n     * Return an activated HTML5 Audio object to the pool.\n     * @return {Howler}\n     */\n    _releaseHtml5Audio: function(audio) {\n      var self = this || Howler;\n\n      // Don't add audio to the pool if we don't know if it has been unlocked.\n      if (audio._unlocked) {\n        self._html5AudioPool.push(audio);\n      }\n\n      return self;\n    },\n\n    /**\n     * Automatically suspend the Web Audio AudioContext after no sound has played for 30 seconds.\n     * This saves processing/energy and fixes various browser-specific bugs with audio getting stuck.\n     * @return {Howler}\n     */\n    _autoSuspend: function() {\n      var self = this;\n\n      if (!self.autoSuspend || !self.ctx || typeof self.ctx.suspend === 'undefined' || !Howler.usingWebAudio) {\n        return;\n      }\n\n      // Check if any sounds are playing.\n      for (var i=0; i<self._howls.length; i++) {\n        if (self._howls[i]._webAudio) {\n          for (var j=0; j<self._howls[i]._sounds.length; j++) {\n            if (!self._howls[i]._sounds[j]._paused) {\n              return self;\n            }\n          }\n        }\n      }\n\n      if (self._suspendTimer) {\n        clearTimeout(self._suspendTimer);\n      }\n\n      // If no sound has played after 30 seconds, suspend the context.\n      self._suspendTimer = setTimeout(function() {\n        if (!self.autoSuspend) {\n          return;\n        }\n\n        self._suspendTimer = null;\n        self.state = 'suspending';\n\n        // Handle updating the state of the audio context after suspending.\n        var handleSuspension = function() {\n          self.state = 'suspended';\n\n          if (self._resumeAfterSuspend) {\n            delete self._resumeAfterSuspend;\n            self._autoResume();\n          }\n        };\n\n        // Either the state gets suspended or it is interrupted.\n        // Either way, we need to update the state to suspended.\n        self.ctx.suspend().then(handleSuspension, handleSuspension);\n      }, 30000);\n\n      return self;\n    },\n\n    /**\n     * Automatically resume the Web Audio AudioContext when a new sound is played.\n     * @return {Howler}\n     */\n    _autoResume: function() {\n      var self = this;\n\n      if (!self.ctx || typeof self.ctx.resume === 'undefined' || !Howler.usingWebAudio) {\n        return;\n      }\n\n      if (self.state === 'running' && self.ctx.state !== 'interrupted' && self._suspendTimer) {\n        clearTimeout(self._suspendTimer);\n        self._suspendTimer = null;\n      } else if (self.state === 'suspended' || self.state === 'running' && self.ctx.state === 'interrupted') {\n        self.ctx.resume().then(function() {\n          self.state = 'running';\n\n          // Emit to all Howls that the audio has resumed.\n          for (var i=0; i<self._howls.length; i++) {\n            self._howls[i]._emit('resume');\n          }\n        });\n\n        if (self._suspendTimer) {\n          clearTimeout(self._suspendTimer);\n          self._suspendTimer = null;\n        }\n      } else if (self.state === 'suspending') {\n        self._resumeAfterSuspend = true;\n      }\n\n      return self;\n    }\n  };\n\n  // Setup the global audio controller.\n  var Howler = new HowlerGlobal();\n\n  /** Group Methods **/\n  /***************************************************************************/\n\n  /**\n   * Create an audio group controller.\n   * @param {Object} o Passed in properties for this group.\n   */\n  var Howl = function(o) {\n    var self = this;\n\n    // Throw an error if no source is provided.\n    if (!o.src || o.src.length === 0) {\n      console.error('An array of source files must be passed with any new Howl.');\n      return;\n    }\n\n    self.init(o);\n  };\n  Howl.prototype = {\n    /**\n     * Initialize a new Howl group object.\n     * @param  {Object} o Passed in properties for this group.\n     * @return {Howl}\n     */\n    init: function(o) {\n      var self = this;\n\n      // If we don't have an AudioContext created yet, run the setup.\n      if (!Howler.ctx) {\n        setupAudioContext();\n      }\n\n      // Setup user-defined default properties.\n      self._autoplay = o.autoplay || false;\n      self._format = (typeof o.format !== 'string') ? o.format : [o.format];\n      self._html5 = o.html5 || false;\n      self._muted = o.mute || false;\n      self._loop = o.loop || false;\n      self._pool = o.pool || 5;\n      self._preload = (typeof o.preload === 'boolean' || o.preload === 'metadata') ? o.preload : true;\n      self._rate = o.rate || 1;\n      self._sprite = o.sprite || {};\n      self._src = (typeof o.src !== 'string') ? o.src : [o.src];\n      self._volume = o.volume !== undefined ? o.volume : 1;\n      self._xhr = {\n        method: o.xhr && o.xhr.method ? o.xhr.method : 'GET',\n        headers: o.xhr && o.xhr.headers ? o.xhr.headers : null,\n        withCredentials: o.xhr && o.xhr.withCredentials ? o.xhr.withCredentials : false,\n      };\n\n      // Setup all other default properties.\n      self._duration = 0;\n      self._state = 'unloaded';\n      self._sounds = [];\n      self._endTimers = {};\n      self._queue = [];\n      self._playLock = false;\n\n      // Setup event listeners.\n      self._onend = o.onend ? [{fn: o.onend}] : [];\n      self._onfade = o.onfade ? [{fn: o.onfade}] : [];\n      self._onload = o.onload ? [{fn: o.onload}] : [];\n      self._onloaderror = o.onloaderror ? [{fn: o.onloaderror}] : [];\n      self._onplayerror = o.onplayerror ? [{fn: o.onplayerror}] : [];\n      self._onpause = o.onpause ? [{fn: o.onpause}] : [];\n      self._onplay = o.onplay ? [{fn: o.onplay}] : [];\n      self._onstop = o.onstop ? [{fn: o.onstop}] : [];\n      self._onmute = o.onmute ? [{fn: o.onmute}] : [];\n      self._onvolume = o.onvolume ? [{fn: o.onvolume}] : [];\n      self._onrate = o.onrate ? [{fn: o.onrate}] : [];\n      self._onseek = o.onseek ? [{fn: o.onseek}] : [];\n      self._onunlock = o.onunlock ? [{fn: o.onunlock}] : [];\n      self._onresume = [];\n\n      // Web Audio or HTML5 Audio?\n      self._webAudio = Howler.usingWebAudio && !self._html5;\n\n      // Automatically try to enable audio.\n      if (typeof Howler.ctx !== 'undefined' && Howler.ctx && Howler.autoUnlock) {\n        Howler._unlockAudio();\n      }\n\n      // Keep track of this Howl group in the global controller.\n      Howler._howls.push(self);\n\n      // If they selected autoplay, add a play event to the load queue.\n      if (self._autoplay) {\n        self._queue.push({\n          event: 'play',\n          action: function() {\n            self.play();\n          }\n        });\n      }\n\n      // Load the source file unless otherwise specified.\n      if (self._preload && self._preload !== 'none') {\n        self.load();\n      }\n\n      return self;\n    },\n\n    /**\n     * Load the audio file.\n     * @return {Howler}\n     */\n    load: function() {\n      var self = this;\n      var url = null;\n\n      // If no audio is available, quit immediately.\n      if (Howler.noAudio) {\n        self._emit('loaderror', null, 'No audio support.');\n        return;\n      }\n\n      // Make sure our source is in an array.\n      if (typeof self._src === 'string') {\n        self._src = [self._src];\n      }\n\n      // Loop through the sources and pick the first one that is compatible.\n      for (var i=0; i<self._src.length; i++) {\n        var ext, str;\n\n        if (self._format && self._format[i]) {\n          // If an extension was specified, use that instead.\n          ext = self._format[i];\n        } else {\n          // Make sure the source is a string.\n          str = self._src[i];\n          if (typeof str !== 'string') {\n            self._emit('loaderror', null, 'Non-string found in selected audio sources - ignoring.');\n            continue;\n          }\n\n          // Extract the file extension from the URL or base64 data URI.\n          ext = /^data:audio\\/([^;,]+);/i.exec(str);\n          if (!ext) {\n            ext = /\\.([^.]+)$/.exec(str.split('?', 1)[0]);\n          }\n\n          if (ext) {\n            ext = ext[1].toLowerCase();\n          }\n        }\n\n        // Log a warning if no extension was found.\n        if (!ext) {\n          console.warn('No file extension was found. Consider using the \"format\" property or specify an extension.');\n        }\n\n        // Check if this extension is available.\n        if (ext && Howler.codecs(ext)) {\n          url = self._src[i];\n          break;\n        }\n      }\n\n      if (!url) {\n        self._emit('loaderror', null, 'No codec support for selected audio sources.');\n        return;\n      }\n\n      self._src = url;\n      self._state = 'loading';\n\n      // If the hosting page is HTTPS and the source isn't,\n      // drop down to HTML5 Audio to avoid Mixed Content errors.\n      if (window.location.protocol === 'https:' && url.slice(0, 5) === 'http:') {\n        self._html5 = true;\n        self._webAudio = false;\n      }\n\n      // Create a new sound object and add it to the pool.\n      new Sound(self);\n\n      // Load and decode the audio data for playback.\n      if (self._webAudio) {\n        loadBuffer(self);\n      }\n\n      return self;\n    },\n\n    /**\n     * Play a sound or resume previous playback.\n     * @param  {String/Number} sprite   Sprite name for sprite playback or sound id to continue previous.\n     * @param  {Boolean} internal Internal Use: true prevents event firing.\n     * @return {Number}          Sound ID.\n     */\n    play: function(sprite, internal) {\n      var self = this;\n      var id = null;\n\n      // Determine if a sprite, sound id or nothing was passed\n      if (typeof sprite === 'number') {\n        id = sprite;\n        sprite = null;\n      } else if (typeof sprite === 'string' && self._state === 'loaded' && !self._sprite[sprite]) {\n        // If the passed sprite doesn't exist, do nothing.\n        return null;\n      } else if (typeof sprite === 'undefined') {\n        // Use the default sound sprite (plays the full audio length).\n        sprite = '__default';\n\n        // Check if there is a single paused sound that isn't ended.\n        // If there is, play that sound. If not, continue as usual.\n        if (!self._playLock) {\n          var num = 0;\n          for (var i=0; i<self._sounds.length; i++) {\n            if (self._sounds[i]._paused && !self._sounds[i]._ended) {\n              num++;\n              id = self._sounds[i]._id;\n            }\n          }\n\n          if (num === 1) {\n            sprite = null;\n          } else {\n            id = null;\n          }\n        }\n      }\n\n      // Get the selected node, or get one from the pool.\n      var sound = id ? self._soundById(id) : self._inactiveSound();\n\n      // If the sound doesn't exist, do nothing.\n      if (!sound) {\n        return null;\n      }\n\n      // Select the sprite definition.\n      if (id && !sprite) {\n        sprite = sound._sprite || '__default';\n      }\n\n      // If the sound hasn't loaded, we must wait to get the audio's duration.\n      // We also need to wait to make sure we don't run into race conditions with\n      // the order of function calls.\n      if (self._state !== 'loaded') {\n        // Set the sprite value on this sound.\n        sound._sprite = sprite;\n\n        // Mark this sound as not ended in case another sound is played before this one loads.\n        sound._ended = false;\n\n        // Add the sound to the queue to be played on load.\n        var soundId = sound._id;\n        self._queue.push({\n          event: 'play',\n          action: function() {\n            self.play(soundId);\n          }\n        });\n\n        return soundId;\n      }\n\n      // Don't play the sound if an id was passed and it is already playing.\n      if (id && !sound._paused) {\n        // Trigger the play event, in order to keep iterating through queue.\n        if (!internal) {\n          self._loadQueue('play');\n        }\n\n        return sound._id;\n      }\n\n      // Make sure the AudioContext isn't suspended, and resume it if it is.\n      if (self._webAudio) {\n        Howler._autoResume();\n      }\n\n      // Determine how long to play for and where to start playing.\n      var seek = Math.max(0, sound._seek > 0 ? sound._seek : self._sprite[sprite][0] / 1000);\n      var duration = Math.max(0, ((self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000) - seek);\n      var timeout = (duration * 1000) / Math.abs(sound._rate);\n      var start = self._sprite[sprite][0] / 1000;\n      var stop = (self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000;\n      sound._sprite = sprite;\n\n      // Mark the sound as ended instantly so that this async playback\n      // doesn't get grabbed by another call to play while this one waits to start.\n      sound._ended = false;\n\n      // Update the parameters of the sound.\n      var setParams = function() {\n        sound._paused = false;\n        sound._seek = seek;\n        sound._start = start;\n        sound._stop = stop;\n        sound._loop = !!(sound._loop || self._sprite[sprite][2]);\n      };\n\n      // End the sound instantly if seek is at the end.\n      if (seek >= stop) {\n        self._ended(sound);\n        return;\n      }\n\n      // Begin the actual playback.\n      var node = sound._node;\n      if (self._webAudio) {\n        // Fire this when the sound is ready to play to begin Web Audio playback.\n        var playWebAudio = function() {\n          self._playLock = false;\n          setParams();\n          self._refreshBuffer(sound);\n\n          // Setup the playback params.\n          var vol = (sound._muted || self._muted) ? 0 : sound._volume;\n          node.gain.setValueAtTime(vol, Howler.ctx.currentTime);\n          sound._playStart = Howler.ctx.currentTime;\n\n          // Play the sound using the supported method.\n          if (typeof node.bufferSource.start === 'undefined') {\n            sound._loop ? node.bufferSource.noteGrainOn(0, seek, 86400) : node.bufferSource.noteGrainOn(0, seek, duration);\n          } else {\n            sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration);\n          }\n\n          // Start a new timer if none is present.\n          if (timeout !== Infinity) {\n            self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n          }\n\n          if (!internal) {\n            setTimeout(function() {\n              self._emit('play', sound._id);\n              self._loadQueue();\n            }, 0);\n          }\n        };\n\n        if (Howler.state === 'running' && Howler.ctx.state !== 'interrupted') {\n          playWebAudio();\n        } else {\n          self._playLock = true;\n\n          // Wait for the audio context to resume before playing.\n          self.once('resume', playWebAudio);\n\n          // Cancel the end timer.\n          self._clearTimer(sound._id);\n        }\n      } else {\n        // Fire this when the sound is ready to play to begin HTML5 Audio playback.\n        var playHtml5 = function() {\n          node.currentTime = seek;\n          node.muted = sound._muted || self._muted || Howler._muted || node.muted;\n          node.volume = sound._volume * Howler.volume();\n          node.playbackRate = sound._rate;\n\n          // Some browsers will throw an error if this is called without user interaction.\n          try {\n            var play = node.play();\n\n            // Support older browsers that don't support promises, and thus don't have this issue.\n            if (play && typeof Promise !== 'undefined' && (play instanceof Promise || typeof play.then === 'function')) {\n              // Implements a lock to prevent DOMException: The play() request was interrupted by a call to pause().\n              self._playLock = true;\n\n              // Set param values immediately.\n              setParams();\n\n              // Releases the lock and executes queued actions.\n              play\n                .then(function() {\n                  self._playLock = false;\n                  node._unlocked = true;\n                  if (!internal) {\n                    self._emit('play', sound._id);\n                  } else {\n                    self._loadQueue();\n                  }\n                })\n                .catch(function() {\n                  self._playLock = false;\n                  self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +\n                    'on mobile devices and Chrome where playback was not within a user interaction.');\n\n                  // Reset the ended and paused values.\n                  sound._ended = true;\n                  sound._paused = true;\n                });\n            } else if (!internal) {\n              self._playLock = false;\n              setParams();\n              self._emit('play', sound._id);\n            }\n\n            // Setting rate before playing won't work in IE, so we set it again here.\n            node.playbackRate = sound._rate;\n\n            // If the node is still paused, then we can assume there was a playback issue.\n            if (node.paused) {\n              self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +\n                'on mobile devices and Chrome where playback was not within a user interaction.');\n              return;\n            }\n\n            // Setup the end timer on sprites or listen for the ended event.\n            if (sprite !== '__default' || sound._loop) {\n              self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n            } else {\n              self._endTimers[sound._id] = function() {\n                // Fire ended on this audio node.\n                self._ended(sound);\n\n                // Clear this listener.\n                node.removeEventListener('ended', self._endTimers[sound._id], false);\n              };\n              node.addEventListener('ended', self._endTimers[sound._id], false);\n            }\n          } catch (err) {\n            self._emit('playerror', sound._id, err);\n          }\n        };\n\n        // If this is streaming audio, make sure the src is set and load again.\n        if (node.src === 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA') {\n          node.src = self._src;\n          node.load();\n        }\n\n        // Play immediately if ready, or wait for the 'canplaythrough'e vent.\n        var loadedNoReadyState = (window && window.ejecta) || (!node.readyState && Howler._navigator.isCocoonJS);\n        if (node.readyState >= 3 || loadedNoReadyState) {\n          playHtml5();\n        } else {\n          self._playLock = true;\n          self._state = 'loading';\n\n          var listener = function() {\n            self._state = 'loaded';\n            \n            // Begin playback.\n            playHtml5();\n\n            // Clear this listener.\n            node.removeEventListener(Howler._canPlayEvent, listener, false);\n          };\n          node.addEventListener(Howler._canPlayEvent, listener, false);\n\n          // Cancel the end timer.\n          self._clearTimer(sound._id);\n        }\n      }\n\n      return sound._id;\n    },\n\n    /**\n     * Pause playback and save current position.\n     * @param  {Number} id The sound ID (empty to pause all in group).\n     * @return {Howl}\n     */\n    pause: function(id) {\n      var self = this;\n\n      // If the sound hasn't loaded or a play() promise is pending, add it to the load queue to pause when capable.\n      if (self._state !== 'loaded' || self._playLock) {\n        self._queue.push({\n          event: 'pause',\n          action: function() {\n            self.pause(id);\n          }\n        });\n\n        return self;\n      }\n\n      // If no id is passed, get all ID's to be paused.\n      var ids = self._getSoundIds(id);\n\n      for (var i=0; i<ids.length; i++) {\n        // Clear the end timer.\n        self._clearTimer(ids[i]);\n\n        // Get the sound.\n        var sound = self._soundById(ids[i]);\n\n        if (sound && !sound._paused) {\n          // Reset the seek position.\n          sound._seek = self.seek(ids[i]);\n          sound._rateSeek = 0;\n          sound._paused = true;\n\n          // Stop currently running fades.\n          self._stopFade(ids[i]);\n\n          if (sound._node) {\n            if (self._webAudio) {\n              // Make sure the sound has been created.\n              if (!sound._node.bufferSource) {\n                continue;\n              }\n\n              if (typeof sound._node.bufferSource.stop === 'undefined') {\n                sound._node.bufferSource.noteOff(0);\n              } else {\n                sound._node.bufferSource.stop(0);\n              }\n\n              // Clean up the buffer source.\n              self._cleanBuffer(sound._node);\n            } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) {\n              sound._node.pause();\n            }\n          }\n        }\n\n        // Fire the pause event, unless `true` is passed as the 2nd argument.\n        if (!arguments[1]) {\n          self._emit('pause', sound ? sound._id : null);\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Stop playback and reset to start.\n     * @param  {Number} id The sound ID (empty to stop all in group).\n     * @param  {Boolean} internal Internal Use: true prevents event firing.\n     * @return {Howl}\n     */\n    stop: function(id, internal) {\n      var self = this;\n\n      // If the sound hasn't loaded, add it to the load queue to stop when capable.\n      if (self._state !== 'loaded' || self._playLock) {\n        self._queue.push({\n          event: 'stop',\n          action: function() {\n            self.stop(id);\n          }\n        });\n\n        return self;\n      }\n\n      // If no id is passed, get all ID's to be stopped.\n      var ids = self._getSoundIds(id);\n\n      for (var i=0; i<ids.length; i++) {\n        // Clear the end timer.\n        self._clearTimer(ids[i]);\n\n        // Get the sound.\n        var sound = self._soundById(ids[i]);\n\n        if (sound) {\n          // Reset the seek position.\n          sound._seek = sound._start || 0;\n          sound._rateSeek = 0;\n          sound._paused = true;\n          sound._ended = true;\n\n          // Stop currently running fades.\n          self._stopFade(ids[i]);\n\n          if (sound._node) {\n            if (self._webAudio) {\n              // Make sure the sound's AudioBufferSourceNode has been created.\n              if (sound._node.bufferSource) {\n                if (typeof sound._node.bufferSource.stop === 'undefined') {\n                  sound._node.bufferSource.noteOff(0);\n                } else {\n                  sound._node.bufferSource.stop(0);\n                }\n\n                // Clean up the buffer source.\n                self._cleanBuffer(sound._node);\n              }\n            } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) {\n              sound._node.currentTime = sound._start || 0;\n              sound._node.pause();\n\n              // If this is a live stream, stop download once the audio is stopped.\n              if (sound._node.duration === Infinity) {\n                self._clearSound(sound._node);\n              }\n            }\n          }\n\n          if (!internal) {\n            self._emit('stop', sound._id);\n          }\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Mute/unmute a single sound or all sounds in this Howl group.\n     * @param  {Boolean} muted Set to true to mute and false to unmute.\n     * @param  {Number} id    The sound ID to update (omit to mute/unmute all).\n     * @return {Howl}\n     */\n    mute: function(muted, id) {\n      var self = this;\n\n      // If the sound hasn't loaded, add it to the load queue to mute when capable.\n      if (self._state !== 'loaded'|| self._playLock) {\n        self._queue.push({\n          event: 'mute',\n          action: function() {\n            self.mute(muted, id);\n          }\n        });\n\n        return self;\n      }\n\n      // If applying mute/unmute to all sounds, update the group's value.\n      if (typeof id === 'undefined') {\n        if (typeof muted === 'boolean') {\n          self._muted = muted;\n        } else {\n          return self._muted;\n        }\n      }\n\n      // If no id is passed, get all ID's to be muted.\n      var ids = self._getSoundIds(id);\n\n      for (var i=0; i<ids.length; i++) {\n        // Get the sound.\n        var sound = self._soundById(ids[i]);\n\n        if (sound) {\n          sound._muted = muted;\n\n          // Cancel active fade and set the volume to the end value.\n          if (sound._interval) {\n            self._stopFade(sound._id);\n          }\n\n          if (self._webAudio && sound._node) {\n            sound._node.gain.setValueAtTime(muted ? 0 : sound._volume, Howler.ctx.currentTime);\n          } else if (sound._node) {\n            sound._node.muted = Howler._muted ? true : muted;\n          }\n\n          self._emit('mute', sound._id);\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Get/set the volume of this sound or of the Howl group. This method can optionally take 0, 1 or 2 arguments.\n     *   volume() -> Returns the group's volume value.\n     *   volume(id) -> Returns the sound id's current volume.\n     *   volume(vol) -> Sets the volume of all sounds in this Howl group.\n     *   volume(vol, id) -> Sets the volume of passed sound id.\n     * @return {Howl/Number} Returns self or current volume.\n     */\n    volume: function() {\n      var self = this;\n      var args = arguments;\n      var vol, id;\n\n      // Determine the values based on arguments.\n      if (args.length === 0) {\n        // Return the value of the groups' volume.\n        return self._volume;\n      } else if (args.length === 1 || args.length === 2 && typeof args[1] === 'undefined') {\n        // First check if this is an ID, and if not, assume it is a new volume.\n        var ids = self._getSoundIds();\n        var index = ids.indexOf(args[0]);\n        if (index >= 0) {\n          id = parseInt(args[0], 10);\n        } else {\n          vol = parseFloat(args[0]);\n        }\n      } else if (args.length >= 2) {\n        vol = parseFloat(args[0]);\n        id = parseInt(args[1], 10);\n      }\n\n      // Update the volume or return the current volume.\n      var sound;\n      if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {\n        // If the sound hasn't loaded, add it to the load queue to change volume when capable.\n        if (self._state !== 'loaded'|| self._playLock) {\n          self._queue.push({\n            event: 'volume',\n            action: function() {\n              self.volume.apply(self, args);\n            }\n          });\n\n          return self;\n        }\n\n        // Set the group volume.\n        if (typeof id === 'undefined') {\n          self._volume = vol;\n        }\n\n        // Update one or all volumes.\n        id = self._getSoundIds(id);\n        for (var i=0; i<id.length; i++) {\n          // Get the sound.\n          sound = self._soundById(id[i]);\n\n          if (sound) {\n            sound._volume = vol;\n\n            // Stop currently running fades.\n            if (!args[2]) {\n              self._stopFade(id[i]);\n            }\n\n            if (self._webAudio && sound._node && !sound._muted) {\n              sound._node.gain.setValueAtTime(vol, Howler.ctx.currentTime);\n            } else if (sound._node && !sound._muted) {\n              sound._node.volume = vol * Howler.volume();\n            }\n\n            self._emit('volume', sound._id);\n          }\n        }\n      } else {\n        sound = id ? self._soundById(id) : self._sounds[0];\n        return sound ? sound._volume : 0;\n      }\n\n      return self;\n    },\n\n    /**\n     * Fade a currently playing sound between two volumes (if no id is passed, all sounds will fade).\n     * @param  {Number} from The value to fade from (0.0 to 1.0).\n     * @param  {Number} to   The volume to fade to (0.0 to 1.0).\n     * @param  {Number} len  Time in milliseconds to fade.\n     * @param  {Number} id   The sound id (omit to fade all sounds).\n     * @return {Howl}\n     */\n    fade: function(from, to, len, id) {\n      var self = this;\n\n      // If the sound hasn't loaded, add it to the load queue to fade when capable.\n      if (self._state !== 'loaded' || self._playLock) {\n        self._queue.push({\n          event: 'fade',\n          action: function() {\n            self.fade(from, to, len, id);\n          }\n        });\n\n        return self;\n      }\n\n      // Make sure the to/from/len values are numbers.\n      from = Math.min(Math.max(0, parseFloat(from)), 1);\n      to = Math.min(Math.max(0, parseFloat(to)), 1);\n      len = parseFloat(len);\n\n      // Set the volume to the start position.\n      self.volume(from, id);\n\n      // Fade the volume of one or all sounds.\n      var ids = self._getSoundIds(id);\n      for (var i=0; i<ids.length; i++) {\n        // Get the sound.\n        var sound = self._soundById(ids[i]);\n\n        // Create a linear fade or fall back to timeouts with HTML5 Audio.\n        if (sound) {\n          // Stop the previous fade if no sprite is being used (otherwise, volume handles this).\n          if (!id) {\n            self._stopFade(ids[i]);\n          }\n\n          // If we are using Web Audio, let the native methods do the actual fade.\n          if (self._webAudio && !sound._muted) {\n            var currentTime = Howler.ctx.currentTime;\n            var end = currentTime + (len / 1000);\n            sound._volume = from;\n            sound._node.gain.setValueAtTime(from, currentTime);\n            sound._node.gain.linearRampToValueAtTime(to, end);\n          }\n\n          self._startFadeInterval(sound, from, to, len, ids[i], typeof id === 'undefined');\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Starts the internal interval to fade a sound.\n     * @param  {Object} sound Reference to sound to fade.\n     * @param  {Number} from The value to fade from (0.0 to 1.0).\n     * @param  {Number} to   The volume to fade to (0.0 to 1.0).\n     * @param  {Number} len  Time in milliseconds to fade.\n     * @param  {Number} id   The sound id to fade.\n     * @param  {Boolean} isGroup   If true, set the volume on the group.\n     */\n    _startFadeInterval: function(sound, from, to, len, id, isGroup) {\n      var self = this;\n      var vol = from;\n      var diff = to - from;\n      var steps = Math.abs(diff / 0.01);\n      var stepLen = Math.max(4, (steps > 0) ? len / steps : len);\n      var lastTick = Date.now();\n\n      // Store the value being faded to.\n      sound._fadeTo = to;\n\n      // Update the volume value on each interval tick.\n      sound._interval = setInterval(function() {\n        // Update the volume based on the time since the last tick.\n        var tick = (Date.now() - lastTick) / len;\n        lastTick = Date.now();\n        vol += diff * tick;\n\n        // Round to within 2 decimal points.\n        vol = Math.round(vol * 100) / 100;\n\n        // Make sure the volume is in the right bounds.\n        if (diff < 0) {\n          vol = Math.max(to, vol);\n        } else {\n          vol = Math.min(to, vol);\n        }\n\n        // Change the volume.\n        if (self._webAudio) {\n          sound._volume = vol;\n        } else {\n          self.volume(vol, sound._id, true);\n        }\n\n        // Set the group's volume.\n        if (isGroup) {\n          self._volume = vol;\n        }\n\n        // When the fade is complete, stop it and fire event.\n        if ((to < from && vol <= to) || (to > from && vol >= to)) {\n          clearInterval(sound._interval);\n          sound._interval = null;\n          sound._fadeTo = null;\n          self.volume(to, sound._id);\n          self._emit('fade', sound._id);\n        }\n      }, stepLen);\n    },\n\n    /**\n     * Internal method that stops the currently playing fade when\n     * a new fade starts, volume is changed or the sound is stopped.\n     * @param  {Number} id The sound id.\n     * @return {Howl}\n     */\n    _stopFade: function(id) {\n      var self = this;\n      var sound = self._soundById(id);\n\n      if (sound && sound._interval) {\n        if (self._webAudio) {\n          sound._node.gain.cancelScheduledValues(Howler.ctx.currentTime);\n        }\n\n        clearInterval(sound._interval);\n        sound._interval = null;\n        self.volume(sound._fadeTo, id);\n        sound._fadeTo = null;\n        self._emit('fade', id);\n      }\n\n      return self;\n    },\n\n    /**\n     * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments.\n     *   loop() -> Returns the group's loop value.\n     *   loop(id) -> Returns the sound id's loop value.\n     *   loop(loop) -> Sets the loop value for all sounds in this Howl group.\n     *   loop(loop, id) -> Sets the loop value of passed sound id.\n     * @return {Howl/Boolean} Returns self or current loop value.\n     */\n    loop: function() {\n      var self = this;\n      var args = arguments;\n      var loop, id, sound;\n\n      // Determine the values for loop and id.\n      if (args.length === 0) {\n        // Return the grou's loop value.\n        return self._loop;\n      } else if (args.length === 1) {\n        if (typeof args[0] === 'boolean') {\n          loop = args[0];\n          self._loop = loop;\n        } else {\n          // Return this sound's loop value.\n          sound = self._soundById(parseInt(args[0], 10));\n          return sound ? sound._loop : false;\n        }\n      } else if (args.length === 2) {\n        loop = args[0];\n        id = parseInt(args[1], 10);\n      }\n\n      // If no id is passed, get all ID's to be looped.\n      var ids = self._getSoundIds(id);\n      for (var i=0; i<ids.length; i++) {\n        sound = self._soundById(ids[i]);\n\n        if (sound) {\n          sound._loop = loop;\n          if (self._webAudio && sound._node && sound._node.bufferSource) {\n            sound._node.bufferSource.loop = loop;\n            if (loop) {\n              sound._node.bufferSource.loopStart = sound._start || 0;\n              sound._node.bufferSource.loopEnd = sound._stop;\n\n              // If playing, restart playback to ensure looping updates.\n              if (self.playing(ids[i])) {\n                self.pause(ids[i], true);\n                self.play(ids[i], true);\n              }\n            }\n          }\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Get/set the playback rate of a sound. This method can optionally take 0, 1 or 2 arguments.\n     *   rate() -> Returns the first sound node's current playback rate.\n     *   rate(id) -> Returns the sound id's current playback rate.\n     *   rate(rate) -> Sets the playback rate of all sounds in this Howl group.\n     *   rate(rate, id) -> Sets the playback rate of passed sound id.\n     * @return {Howl/Number} Returns self or the current playback rate.\n     */\n    rate: function() {\n      var self = this;\n      var args = arguments;\n      var rate, id;\n\n      // Determine the values based on arguments.\n      if (args.length === 0) {\n        // We will simply return the current rate of the first node.\n        id = self._sounds[0]._id;\n      } else if (args.length === 1) {\n        // First check if this is an ID, and if not, assume it is a new rate value.\n        var ids = self._getSoundIds();\n        var index = ids.indexOf(args[0]);\n        if (index >= 0) {\n          id = parseInt(args[0], 10);\n        } else {\n          rate = parseFloat(args[0]);\n        }\n      } else if (args.length === 2) {\n        rate = parseFloat(args[0]);\n        id = parseInt(args[1], 10);\n      }\n\n      // Update the playback rate or return the current value.\n      var sound;\n      if (typeof rate === 'number') {\n        // If the sound hasn't loaded, add it to the load queue to change playback rate when capable.\n        if (self._state !== 'loaded' || self._playLock) {\n          self._queue.push({\n            event: 'rate',\n            action: function() {\n              self.rate.apply(self, args);\n            }\n          });\n\n          return self;\n        }\n\n        // Set the group rate.\n        if (typeof id === 'undefined') {\n          self._rate = rate;\n        }\n\n        // Update one or all volumes.\n        id = self._getSoundIds(id);\n        for (var i=0; i<id.length; i++) {\n          // Get the sound.\n          sound = self._soundById(id[i]);\n\n          if (sound) {\n            // Keep track of our position when the rate changed and update the playback\n            // start position so we can properly adjust the seek position for time elapsed.\n            if (self.playing(id[i])) {\n              sound._rateSeek = self.seek(id[i]);\n              sound._playStart = self._webAudio ? Howler.ctx.currentTime : sound._playStart;\n            }\n            sound._rate = rate;\n\n            // Change the playback rate.\n            if (self._webAudio && sound._node && sound._node.bufferSource) {\n              sound._node.bufferSource.playbackRate.setValueAtTime(rate, Howler.ctx.currentTime);\n            } else if (sound._node) {\n              sound._node.playbackRate = rate;\n            }\n\n            // Reset the timers.\n            var seek = self.seek(id[i]);\n            var duration = ((self._sprite[sound._sprite][0] + self._sprite[sound._sprite][1]) / 1000) - seek;\n            var timeout = (duration * 1000) / Math.abs(sound._rate);\n\n            // Start a new end timer if sound is already playing.\n            if (self._endTimers[id[i]] || !sound._paused) {\n              self._clearTimer(id[i]);\n              self._endTimers[id[i]] = setTimeout(self._ended.bind(self, sound), timeout);\n            }\n\n            self._emit('rate', sound._id);\n          }\n        }\n      } else {\n        sound = self._soundById(id);\n        return sound ? sound._rate : self._rate;\n      }\n\n      return self;\n    },\n\n    /**\n     * Get/set the seek position of a sound. This method can optionally take 0, 1 or 2 arguments.\n     *   seek() -> Returns the first sound node's current seek position.\n     *   seek(id) -> Returns the sound id's current seek position.\n     *   seek(seek) -> Sets the seek position of the first sound node.\n     *   seek(seek, id) -> Sets the seek position of passed sound id.\n     * @return {Howl/Number} Returns self or the current seek position.\n     */\n    seek: function() {\n      var self = this;\n      var args = arguments;\n      var seek, id;\n\n      // Determine the values based on arguments.\n      if (args.length === 0) {\n        // We will simply return the current position of the first node.\n        if (self._sounds.length) {\n          id = self._sounds[0]._id;\n        }\n      } else if (args.length === 1) {\n        // First check if this is an ID, and if not, assume it is a new seek position.\n        var ids = self._getSoundIds();\n        var index = ids.indexOf(args[0]);\n        if (index >= 0) {\n          id = parseInt(args[0], 10);\n        } else if (self._sounds.length) {\n          id = self._sounds[0]._id;\n          seek = parseFloat(args[0]);\n        }\n      } else if (args.length === 2) {\n        seek = parseFloat(args[0]);\n        id = parseInt(args[1], 10);\n      }\n\n      // If there is no ID, bail out.\n      if (typeof id === 'undefined') {\n        return 0;\n      }\n\n      // If the sound hasn't loaded, add it to the load queue to seek when capable.\n      if (typeof seek === 'number' && (self._state !== 'loaded' || self._playLock)) {\n        self._queue.push({\n          event: 'seek',\n          action: function() {\n            self.seek.apply(self, args);\n          }\n        });\n\n        return self;\n      }\n\n      // Get the sound.\n      var sound = self._soundById(id);\n\n      if (sound) {\n        if (typeof seek === 'number' && seek >= 0) {\n          // Pause the sound and update position for restarting playback.\n          var playing = self.playing(id);\n          if (playing) {\n            self.pause(id, true);\n          }\n\n          // Move the position of the track and cancel timer.\n          sound._seek = seek;\n          sound._ended = false;\n          self._clearTimer(id);\n\n          // Update the seek position for HTML5 Audio.\n          if (!self._webAudio && sound._node && !isNaN(sound._node.duration)) {\n            sound._node.currentTime = seek;\n          }\n\n          // Seek and emit when ready.\n          var seekAndEmit = function() {\n            // Restart the playback if the sound was playing.\n            if (playing) {\n              self.play(id, true);\n            }\n\n            self._emit('seek', id);\n          };\n\n          // Wait for the play lock to be unset before emitting (HTML5 Audio).\n          if (playing && !self._webAudio) {\n            var emitSeek = function() {\n              if (!self._playLock) {\n                seekAndEmit();\n              } else {\n                setTimeout(emitSeek, 0);\n              }\n            };\n            setTimeout(emitSeek, 0);\n          } else {\n            seekAndEmit();\n          }\n        } else {\n          if (self._webAudio) {\n            var realTime = self.playing(id) ? Howler.ctx.currentTime - sound._playStart : 0;\n            var rateSeek = sound._rateSeek ? sound._rateSeek - sound._seek : 0;\n            return sound._seek + (rateSeek + realTime * Math.abs(sound._rate));\n          } else {\n            return sound._node.currentTime;\n          }\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not.\n     * @param  {Number}  id The sound id to check. If none is passed, the whole sound group is checked.\n     * @return {Boolean} True if playing and false if not.\n     */\n    playing: function(id) {\n      var self = this;\n\n      // Check the passed sound ID (if any).\n      if (typeof id === 'number') {\n        var sound = self._soundById(id);\n        return sound ? !sound._paused : false;\n      }\n\n      // Otherwise, loop through all sounds and check if any are playing.\n      for (var i=0; i<self._sounds.length; i++) {\n        if (!self._sounds[i]._paused) {\n          return true;\n        }\n      }\n\n      return false;\n    },\n\n    /**\n     * Get the duration of this sound. Passing a sound id will return the sprite duration.\n     * @param  {Number} id The sound id to check. If none is passed, return full source duration.\n     * @return {Number} Audio duration in seconds.\n     */\n    duration: function(id) {\n      var self = this;\n      var duration = self._duration;\n\n      // If we pass an ID, get the sound and return the sprite length.\n      var sound = self._soundById(id);\n      if (sound) {\n        duration = self._sprite[sound._sprite][1] / 1000;\n      }\n\n      return duration;\n    },\n\n    /**\n     * Returns the current loaded state of this Howl.\n     * @return {String} 'unloaded', 'loading', 'loaded'\n     */\n    state: function() {\n      return this._state;\n    },\n\n    /**\n     * Unload and destroy the current Howl object.\n     * This will immediately stop all sound instances attached to this group.\n     */\n    unload: function() {\n      var self = this;\n\n      // Stop playing any active sounds.\n      var sounds = self._sounds;\n      for (var i=0; i<sounds.length; i++) {\n        // Stop the sound if it is currently playing.\n        if (!sounds[i]._paused) {\n          self.stop(sounds[i]._id);\n        }\n\n        // Remove the source or disconnect.\n        if (!self._webAudio) {\n          // Set the source to 0-second silence to stop any downloading (except in IE).\n          self._clearSound(sounds[i]._node);\n\n          // Remove any event listeners.\n          sounds[i]._node.removeEventListener('error', sounds[i]._errorFn, false);\n          sounds[i]._node.removeEventListener(Howler._canPlayEvent, sounds[i]._loadFn, false);\n          sounds[i]._node.removeEventListener('ended', sounds[i]._endFn, false);\n\n          // Release the Audio object back to the pool.\n          Howler._releaseHtml5Audio(sounds[i]._node);\n        }\n\n        // Empty out all of the nodes.\n        delete sounds[i]._node;\n\n        // Make sure all timers are cleared out.\n        self._clearTimer(sounds[i]._id);\n      }\n\n      // Remove the references in the global Howler object.\n      var index = Howler._howls.indexOf(self);\n      if (index >= 0) {\n        Howler._howls.splice(index, 1);\n      }\n\n      // Delete this sound from the cache (if no other Howl is using it).\n      var remCache = true;\n      for (i=0; i<Howler._howls.length; i++) {\n        if (Howler._howls[i]._src === self._src || self._src.indexOf(Howler._howls[i]._src) >= 0) {\n          remCache = false;\n          break;\n        }\n      }\n\n      if (cache && remCache) {\n        delete cache[self._src];\n      }\n\n      // Clear global errors.\n      Howler.noAudio = false;\n\n      // Clear out `self`.\n      self._state = 'unloaded';\n      self._sounds = [];\n      self = null;\n\n      return null;\n    },\n\n    /**\n     * Listen to a custom event.\n     * @param  {String}   event Event name.\n     * @param  {Function} fn    Listener to call.\n     * @param  {Number}   id    (optional) Only listen to events for this sound.\n     * @param  {Number}   once  (INTERNAL) Marks event to fire only once.\n     * @return {Howl}\n     */\n    on: function(event, fn, id, once) {\n      var self = this;\n      var events = self['_on' + event];\n\n      if (typeof fn === 'function') {\n        events.push(once ? {id: id, fn: fn, once: once} : {id: id, fn: fn});\n      }\n\n      return self;\n    },\n\n    /**\n     * Remove a custom event. Call without parameters to remove all events.\n     * @param  {String}   event Event name.\n     * @param  {Function} fn    Listener to remove. Leave empty to remove all.\n     * @param  {Number}   id    (optional) Only remove events for this sound.\n     * @return {Howl}\n     */\n    off: function(event, fn, id) {\n      var self = this;\n      var events = self['_on' + event];\n      var i = 0;\n\n      // Allow passing just an event and ID.\n      if (typeof fn === 'number') {\n        id = fn;\n        fn = null;\n      }\n\n      if (fn || id) {\n        // Loop through event store and remove the passed function.\n        for (i=0; i<events.length; i++) {\n          var isId = (id === events[i].id);\n          if (fn === events[i].fn && isId || !fn && isId) {\n            events.splice(i, 1);\n            break;\n          }\n        }\n      } else if (event) {\n        // Clear out all events of this type.\n        self['_on' + event] = [];\n      } else {\n        // Clear out all events of every type.\n        var keys = Object.keys(self);\n        for (i=0; i<keys.length; i++) {\n          if ((keys[i].indexOf('_on') === 0) && Array.isArray(self[keys[i]])) {\n            self[keys[i]] = [];\n          }\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Listen to a custom event and remove it once fired.\n     * @param  {String}   event Event name.\n     * @param  {Function} fn    Listener to call.\n     * @param  {Number}   id    (optional) Only listen to events for this sound.\n     * @return {Howl}\n     */\n    once: function(event, fn, id) {\n      var self = this;\n\n      // Setup the event listener.\n      self.on(event, fn, id, 1);\n\n      return self;\n    },\n\n    /**\n     * Emit all events of a specific type and pass the sound id.\n     * @param  {String} event Event name.\n     * @param  {Number} id    Sound ID.\n     * @param  {Number} msg   Message to go with event.\n     * @return {Howl}\n     */\n    _emit: function(event, id, msg) {\n      var self = this;\n      var events = self['_on' + event];\n\n      // Loop through event store and fire all functions.\n      for (var i=events.length-1; i>=0; i--) {\n        // Only fire the listener if the correct ID is used.\n        if (!events[i].id || events[i].id === id || event === 'load') {\n          setTimeout(function(fn) {\n            fn.call(this, id, msg);\n          }.bind(self, events[i].fn), 0);\n\n          // If this event was setup with `once`, remove it.\n          if (events[i].once) {\n            self.off(event, events[i].fn, events[i].id);\n          }\n        }\n      }\n\n      // Pass the event type into load queue so that it can continue stepping.\n      self._loadQueue(event);\n\n      return self;\n    },\n\n    /**\n     * Queue of actions initiated before the sound has loaded.\n     * These will be called in sequence, with the next only firing\n     * after the previous has finished executing (even if async like play).\n     * @return {Howl}\n     */\n    _loadQueue: function(event) {\n      var self = this;\n\n      if (self._queue.length > 0) {\n        var task = self._queue[0];\n\n        // Remove this task if a matching event was passed.\n        if (task.event === event) {\n          self._queue.shift();\n          self._loadQueue();\n        }\n\n        // Run the task if no event type is passed.\n        if (!event) {\n          task.action();\n        }\n      }\n\n      return self;\n    },\n\n    /**\n     * Fired when playback ends at the end of the duration.\n     * @param  {Sound} sound The sound object to work with.\n     * @return {Howl}\n     */\n    _ended: function(sound) {\n      var self = this;\n      var sprite = sound._sprite;\n\n      // If we are using IE and there was network latency we may be clipping\n      // audio before it completes playing. Lets check the node to make sure it\n      // believes it has completed, before ending the playback.\n      if (!self._webAudio && sound._node && !sound._node.paused && !sound._node.ended && sound._node.currentTime < sound._stop) {\n        setTimeout(self._ended.bind(self, sound), 100);\n        return self;\n      }\n\n      // Should this sound loop?\n      var loop = !!(sound._loop || self._sprite[sprite][2]);\n\n      // Fire the ended event.\n      self._emit('end', sound._id);\n\n      // Restart the playback for HTML5 Audio loop.\n      if (!self._webAudio && loop) {\n        self.stop(sound._id, true).play(sound._id);\n      }\n\n      // Restart this timer if on a Web Audio loop.\n      if (self._webAudio && loop) {\n        self._emit('play', sound._id);\n        sound._seek = sound._start || 0;\n        sound._rateSeek = 0;\n        sound._playStart = Howler.ctx.currentTime;\n\n        var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate);\n        self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n      }\n\n      // Mark the node as paused.\n      if (self._webAudio && !loop) {\n        sound._paused = true;\n        sound._ended = true;\n        sound._seek = sound._start || 0;\n        sound._rateSeek = 0;\n        self._clearTimer(sound._id);\n\n        // Clean up the buffer source.\n        self._cleanBuffer(sound._node);\n\n        // Attempt to auto-suspend AudioContext if no sounds are still playing.\n        Howler._autoSuspend();\n      }\n\n      // When using a sprite, end the track.\n      if (!self._webAudio && !loop) {\n        self.stop(sound._id, true);\n      }\n\n      return self;\n    },\n\n    /**\n     * Clear the end timer for a sound playback.\n     * @param  {Number} id The sound ID.\n     * @return {Howl}\n     */\n    _clearTimer: function(id) {\n      var self = this;\n\n      if (self._endTimers[id]) {\n        // Clear the timeout or remove the ended listener.\n        if (typeof self._endTimers[id] !== 'function') {\n          clearTimeout(self._endTimers[id]);\n        } else {\n          var sound = self._soundById(id);\n          if (sound && sound._node) {\n            sound._node.removeEventListener('ended', self._endTimers[id], false);\n          }\n        }\n\n        delete self._endTimers[id];\n      }\n\n      return self;\n    },\n\n    /**\n     * Return the sound identified by this ID, or return null.\n     * @param  {Number} id Sound ID\n     * @return {Object}    Sound object or null.\n     */\n    _soundById: function(id) {\n      var self = this;\n\n      // Loop through all sounds and find the one with this ID.\n      for (var i=0; i<self._sounds.length; i++) {\n        if (id === self._sounds[i]._id) {\n          return self._sounds[i];\n        }\n      }\n\n      return null;\n    },\n\n    /**\n     * Return an inactive sound from the pool or create a new one.\n     * @return {Sound} Sound playback object.\n     */\n    _inactiveSound: function() {\n      var self = this;\n\n      self._drain();\n\n      // Find the first inactive node to recycle.\n      for (var i=0; i<self._sounds.length; i++) {\n        if (self._sounds[i]._ended) {\n          return self._sounds[i].reset();\n        }\n      }\n\n      // If no inactive node was found, create a new one.\n      return new Sound(self);\n    },\n\n    /**\n     * Drain excess inactive sounds from the pool.\n     */\n    _drain: function() {\n      var self = this;\n      var limit = self._pool;\n      var cnt = 0;\n      var i = 0;\n\n      // If there are less sounds than the max pool size, we are done.\n      if (self._sounds.length < limit) {\n        return;\n      }\n\n      // Count the number of inactive sounds.\n      for (i=0; i<self._sounds.length; i++) {\n        if (self._sounds[i]._ended) {\n          cnt++;\n        }\n      }\n\n      // Remove excess inactive sounds, going in reverse order.\n      for (i=self._sounds.length - 1; i>=0; i--) {\n        if (cnt <= limit) {\n          return;\n        }\n\n        if (self._sounds[i]._ended) {\n          // Disconnect the audio source when using Web Audio.\n          if (self._webAudio && self._sounds[i]._node) {\n            self._sounds[i]._node.disconnect(0);\n          }\n\n          // Remove sounds until we have the pool size.\n          self._sounds.splice(i, 1);\n          cnt--;\n        }\n      }\n    },\n\n    /**\n     * Get all ID's from the sounds pool.\n     * @param  {Number} id Only return one ID if one is passed.\n     * @return {Array}    Array of IDs.\n     */\n    _getSoundIds: function(id) {\n      var self = this;\n\n      if (typeof id === 'undefined') {\n        var ids = [];\n        for (var i=0; i<self._sounds.length; i++) {\n          ids.push(self._sounds[i]._id);\n        }\n\n        return ids;\n      } else {\n        return [id];\n      }\n    },\n\n    /**\n     * Load the sound back into the buffer source.\n     * @param  {Sound} sound The sound object to work with.\n     * @return {Howl}\n     */\n    _refreshBuffer: function(sound) {\n      var self = this;\n\n      // Setup the buffer source for playback.\n      sound._node.bufferSource = Howler.ctx.createBufferSource();\n      sound._node.bufferSource.buffer = cache[self._src];\n\n      // Connect to the correct node.\n      if (sound._panner) {\n        sound._node.bufferSource.connect(sound._panner);\n      } else {\n        sound._node.bufferSource.connect(sound._node);\n      }\n\n      // Setup looping and playback rate.\n      sound._node.bufferSource.loop = sound._loop;\n      if (sound._loop) {\n        sound._node.bufferSource.loopStart = sound._start || 0;\n        sound._node.bufferSource.loopEnd = sound._stop || 0;\n      }\n      sound._node.bufferSource.playbackRate.setValueAtTime(sound._rate, Howler.ctx.currentTime);\n\n      return self;\n    },\n\n    /**\n     * Prevent memory leaks by cleaning up the buffer source after playback.\n     * @param  {Object} node Sound's audio node containing the buffer source.\n     * @return {Howl}\n     */\n    _cleanBuffer: function(node) {\n      var self = this;\n      var isIOS = Howler._navigator && Howler._navigator.vendor.indexOf('Apple') >= 0;\n\n      if (!node.bufferSource) {\n        return self;\n      }\n\n      if (Howler._scratchBuffer && node.bufferSource) {\n        node.bufferSource.onended = null;\n        node.bufferSource.disconnect(0);\n        if (isIOS) {\n          try { node.bufferSource.buffer = Howler._scratchBuffer; } catch(e) {}\n        }\n      }\n      node.bufferSource = null;\n\n      return self;\n    },\n\n    /**\n     * Set the source to a 0-second silence to stop any downloading (except in IE).\n     * @param  {Object} node Audio node to clear.\n     */\n    _clearSound: function(node) {\n      var checkIE = /MSIE |Trident\\//.test(Howler._navigator && Howler._navigator.userAgent);\n      if (!checkIE) {\n        node.src = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA';\n      }\n    }\n  };\n\n  /** Single Sound Methods **/\n  /***************************************************************************/\n\n  /**\n   * Setup the sound object, which each node attached to a Howl group is contained in.\n   * @param {Object} howl The Howl parent group.\n   */\n  var Sound = function(howl) {\n    this._parent = howl;\n    this.init();\n  };\n  Sound.prototype = {\n    /**\n     * Initialize a new Sound object.\n     * @return {Sound}\n     */\n    init: function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Setup the default parameters.\n      self._muted = parent._muted;\n      self._loop = parent._loop;\n      self._volume = parent._volume;\n      self._rate = parent._rate;\n      self._seek = 0;\n      self._paused = true;\n      self._ended = true;\n      self._sprite = '__default';\n\n      // Generate a unique ID for this sound.\n      self._id = ++Howler._counter;\n\n      // Add itself to the parent's pool.\n      parent._sounds.push(self);\n\n      // Create the new node.\n      self.create();\n\n      return self;\n    },\n\n    /**\n     * Create and setup a new sound object, whether HTML5 Audio or Web Audio.\n     * @return {Sound}\n     */\n    create: function() {\n      var self = this;\n      var parent = self._parent;\n      var volume = (Howler._muted || self._muted || self._parent._muted) ? 0 : self._volume;\n\n      if (parent._webAudio) {\n        // Create the gain node for controlling volume (the source will connect to this).\n        self._node = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();\n        self._node.gain.setValueAtTime(volume, Howler.ctx.currentTime);\n        self._node.paused = true;\n        self._node.connect(Howler.masterGain);\n      } else if (!Howler.noAudio) {\n        // Get an unlocked Audio object from the pool.\n        self._node = Howler._obtainHtml5Audio();\n\n        // Listen for errors (http://dev.w3.org/html5/spec-author-view/spec.html#mediaerror).\n        self._errorFn = self._errorListener.bind(self);\n        self._node.addEventListener('error', self._errorFn, false);\n\n        // Listen for 'canplaythrough' event to let us know the sound is ready.\n        self._loadFn = self._loadListener.bind(self);\n        self._node.addEventListener(Howler._canPlayEvent, self._loadFn, false);\n\n        // Listen for the 'ended' event on the sound to account for edge-case where\n        // a finite sound has a duration of Infinity.\n        self._endFn = self._endListener.bind(self);\n        self._node.addEventListener('ended', self._endFn, false);\n\n        // Setup the new audio node.\n        self._node.src = parent._src;\n        self._node.preload = parent._preload === true ? 'auto' : parent._preload;\n        self._node.volume = volume * Howler.volume();\n\n        // Begin loading the source.\n        self._node.load();\n      }\n\n      return self;\n    },\n\n    /**\n     * Reset the parameters of this sound to the original state (for recycle).\n     * @return {Sound}\n     */\n    reset: function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Reset all of the parameters of this sound.\n      self._muted = parent._muted;\n      self._loop = parent._loop;\n      self._volume = parent._volume;\n      self._rate = parent._rate;\n      self._seek = 0;\n      self._rateSeek = 0;\n      self._paused = true;\n      self._ended = true;\n      self._sprite = '__default';\n\n      // Generate a new ID so that it isn't confused with the previous sound.\n      self._id = ++Howler._counter;\n\n      return self;\n    },\n\n    /**\n     * HTML5 Audio error listener callback.\n     */\n    _errorListener: function() {\n      var self = this;\n\n      // Fire an error event and pass back the code.\n      self._parent._emit('loaderror', self._id, self._node.error ? self._node.error.code : 0);\n\n      // Clear the event listener.\n      self._node.removeEventListener('error', self._errorFn, false);\n    },\n\n    /**\n     * HTML5 Audio canplaythrough listener callback.\n     */\n    _loadListener: function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Round up the duration to account for the lower precision in HTML5 Audio.\n      parent._duration = Math.ceil(self._node.duration * 10) / 10;\n\n      // Setup a sprite if none is defined.\n      if (Object.keys(parent._sprite).length === 0) {\n        parent._sprite = {__default: [0, parent._duration * 1000]};\n      }\n\n      if (parent._state !== 'loaded') {\n        parent._state = 'loaded';\n        parent._emit('load');\n        parent._loadQueue();\n      }\n\n      // Clear the event listener.\n      self._node.removeEventListener(Howler._canPlayEvent, self._loadFn, false);\n    },\n\n    /**\n     * HTML5 Audio ended listener callback.\n     */\n    _endListener: function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Only handle the `ended`` event if the duration is Infinity.\n      if (parent._duration === Infinity) {\n        // Update the parent duration to match the real audio duration.\n        // Round up the duration to account for the lower precision in HTML5 Audio.\n        parent._duration = Math.ceil(self._node.duration * 10) / 10;\n\n        // Update the sprite that corresponds to the real duration.\n        if (parent._sprite.__default[1] === Infinity) {\n          parent._sprite.__default[1] = parent._duration * 1000;\n        }\n\n        // Run the regular ended method.\n        parent._ended(self);\n      }\n\n      // Clear the event listener since the duration is now correct.\n      self._node.removeEventListener('ended', self._endFn, false);\n    }\n  };\n\n  /** Helper Methods **/\n  /***************************************************************************/\n\n  var cache = {};\n\n  /**\n   * Buffer a sound from URL, Data URI or cache and decode to audio source (Web Audio API).\n   * @param  {Howl} self\n   */\n  var loadBuffer = function(self) {\n    var url = self._src;\n\n    // Check if the buffer has already been cached and use it instead.\n    if (cache[url]) {\n      // Set the duration from the cache.\n      self._duration = cache[url].duration;\n\n      // Load the sound into this Howl.\n      loadSound(self);\n\n      return;\n    }\n\n    if (/^data:[^;]+;base64,/.test(url)) {\n      // Decode the base64 data URI without XHR, since some browsers don't support it.\n      var data = atob(url.split(',')[1]);\n      var dataView = new Uint8Array(data.length);\n      for (var i=0; i<data.length; ++i) {\n        dataView[i] = data.charCodeAt(i);\n      }\n\n      decodeAudioData(dataView.buffer, self);\n    } else {\n      // Load the buffer from the URL.\n      var xhr = new XMLHttpRequest();\n      xhr.open(self._xhr.method, url, true);\n      xhr.withCredentials = self._xhr.withCredentials;\n      xhr.responseType = 'arraybuffer';\n\n      // Apply any custom headers to the request.\n      if (self._xhr.headers) {\n        Object.keys(self._xhr.headers).forEach(function(key) {\n          xhr.setRequestHeader(key, self._xhr.headers[key]);\n        });\n      }\n\n      xhr.onload = function() {\n        // Make sure we get a successful response back.\n        var code = (xhr.status + '')[0];\n        if (code !== '0' && code !== '2' && code !== '3') {\n          self._emit('loaderror', null, 'Failed loading audio file with status: ' + xhr.status + '.');\n          return;\n        }\n\n        decodeAudioData(xhr.response, self);\n      };\n      xhr.onerror = function() {\n        // If there is an error, switch to HTML5 Audio.\n        if (self._webAudio) {\n          self._html5 = true;\n          self._webAudio = false;\n          self._sounds = [];\n          delete cache[url];\n          self.load();\n        }\n      };\n      safeXhrSend(xhr);\n    }\n  };\n\n  /**\n   * Send the XHR request wrapped in a try/catch.\n   * @param  {Object} xhr XHR to send.\n   */\n  var safeXhrSend = function(xhr) {\n    try {\n      xhr.send();\n    } catch (e) {\n      xhr.onerror();\n    }\n  };\n\n  /**\n   * Decode audio data from an array buffer.\n   * @param  {ArrayBuffer} arraybuffer The audio data.\n   * @param  {Howl}        self\n   */\n  var decodeAudioData = function(arraybuffer, self) {\n    // Fire a load error if something broke.\n    var error = function() {\n      self._emit('loaderror', null, 'Decoding audio data failed.');\n    };\n\n    // Load the sound on success.\n    var success = function(buffer) {\n      if (buffer && self._sounds.length > 0) {\n        cache[self._src] = buffer;\n        loadSound(self, buffer);\n      } else {\n        error();\n      }\n    };\n\n    // Decode the buffer into an audio source.\n    if (typeof Promise !== 'undefined' && Howler.ctx.decodeAudioData.length === 1) {\n      Howler.ctx.decodeAudioData(arraybuffer).then(success).catch(error);\n    } else {\n      Howler.ctx.decodeAudioData(arraybuffer, success, error);\n    }\n  }\n\n  /**\n   * Sound is now loaded, so finish setting everything up and fire the loaded event.\n   * @param  {Howl} self\n   * @param  {Object} buffer The decoded buffer sound source.\n   */\n  var loadSound = function(self, buffer) {\n    // Set the duration.\n    if (buffer && !self._duration) {\n      self._duration = buffer.duration;\n    }\n\n    // Setup a sprite if none is defined.\n    if (Object.keys(self._sprite).length === 0) {\n      self._sprite = {__default: [0, self._duration * 1000]};\n    }\n\n    // Fire the loaded event.\n    if (self._state !== 'loaded') {\n      self._state = 'loaded';\n      self._emit('load');\n      self._loadQueue();\n    }\n  };\n\n  /**\n   * Setup the audio context when available, or switch to HTML5 Audio mode.\n   */\n  var setupAudioContext = function() {\n    // If we have already detected that Web Audio isn't supported, don't run this step again.\n    if (!Howler.usingWebAudio) {\n      return;\n    }\n\n    // Check if we are using Web Audio and setup the AudioContext if we are.\n    try {\n      if (typeof AudioContext !== 'undefined') {\n        Howler.ctx = new AudioContext();\n      } else if (typeof webkitAudioContext !== 'undefined') {\n        Howler.ctx = new webkitAudioContext();\n      } else {\n        Howler.usingWebAudio = false;\n      }\n    } catch(e) {\n      Howler.usingWebAudio = false;\n    }\n\n    // If the audio context creation still failed, set using web audio to false.\n    if (!Howler.ctx) {\n      Howler.usingWebAudio = false;\n    }\n\n    // Check if a webview is being used on iOS8 or earlier (rather than the browser).\n    // If it is, disable Web Audio as it causes crashing.\n    var iOS = (/iP(hone|od|ad)/.test(Howler._navigator && Howler._navigator.platform));\n    var appVersion = Howler._navigator && Howler._navigator.appVersion.match(/OS (\\d+)_(\\d+)_?(\\d+)?/);\n    var version = appVersion ? parseInt(appVersion[1], 10) : null;\n    if (iOS && version && version < 9) {\n      var safari = /safari/.test(Howler._navigator && Howler._navigator.userAgent.toLowerCase());\n      if (Howler._navigator && !safari) {\n        Howler.usingWebAudio = false;\n      }\n    }\n\n    // Create and expose the master GainNode when using Web Audio (useful for plugins or advanced usage).\n    if (Howler.usingWebAudio) {\n      Howler.masterGain = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();\n      Howler.masterGain.gain.setValueAtTime(Howler._muted ? 0 : Howler._volume, Howler.ctx.currentTime);\n      Howler.masterGain.connect(Howler.ctx.destination);\n    }\n\n    // Re-run the setup on Howler.\n    Howler._setup();\n  };\n\n  // Add support for AMD (Asynchronous Module Definition) libraries such as require.js.\n  if (typeof define === 'function' && define.amd) {\n    define([], function() {\n      return {\n        Howler: Howler,\n        Howl: Howl\n      };\n    });\n  }\n\n  // Add support for CommonJS libraries such as browserify.\n  if (typeof exports !== 'undefined') {\n    exports.Howler = Howler;\n    exports.Howl = Howl;\n  }\n\n  // Add to global in Node.js (for testing, etc).\n  if (typeof global !== 'undefined') {\n    global.HowlerGlobal = HowlerGlobal;\n    global.Howler = Howler;\n    global.Howl = Howl;\n    global.Sound = Sound;\n  } else if (typeof window !== 'undefined') {  // Define globally in case AMD is not available or unused.\n    window.HowlerGlobal = HowlerGlobal;\n    window.Howler = Howler;\n    window.Howl = Howl;\n    window.Sound = Sound;\n  }\n})();\n\n\n/*!\n *  Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported.\n *  \n *  howler.js v2.2.4\n *  howlerjs.com\n *\n *  (c) 2013-2020, James Simpson of GoldFire Studios\n *  goldfirestudios.com\n *\n *  MIT License\n */\n\n(function() {\n\n  'use strict';\n\n  // Setup default properties.\n  HowlerGlobal.prototype._pos = [0, 0, 0];\n  HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0];\n\n  /** Global Methods **/\n  /***************************************************************************/\n\n  /**\n   * Helper method to update the stereo panning position of all current Howls.\n   * Future Howls will not use this value unless explicitly set.\n   * @param  {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.\n   * @return {Howler/Number}     Self or current stereo panning value.\n   */\n  HowlerGlobal.prototype.stereo = function(pan) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self.ctx || !self.ctx.listener) {\n      return self;\n    }\n\n    // Loop through all Howls and update their stereo panning.\n    for (var i=self._howls.length-1; i>=0; i--) {\n      self._howls[i].stereo(pan);\n    }\n\n    return self;\n  };\n\n  /**\n   * Get/set the position of the listener in 3D cartesian space. Sounds using\n   * 3D position will be relative to the listener's position.\n   * @param  {Number} x The x-position of the listener.\n   * @param  {Number} y The y-position of the listener.\n   * @param  {Number} z The z-position of the listener.\n   * @return {Howler/Array}   Self or current listener position.\n   */\n  HowlerGlobal.prototype.pos = function(x, y, z) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self.ctx || !self.ctx.listener) {\n      return self;\n    }\n\n    // Set the defaults for optional 'y' & 'z'.\n    y = (typeof y !== 'number') ? self._pos[1] : y;\n    z = (typeof z !== 'number') ? self._pos[2] : z;\n\n    if (typeof x === 'number') {\n      self._pos = [x, y, z];\n\n      if (typeof self.ctx.listener.positionX !== 'undefined') {\n        self.ctx.listener.positionX.setTargetAtTime(self._pos[0], Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.positionY.setTargetAtTime(self._pos[1], Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.positionZ.setTargetAtTime(self._pos[2], Howler.ctx.currentTime, 0.1);\n      } else {\n        self.ctx.listener.setPosition(self._pos[0], self._pos[1], self._pos[2]);\n      }\n    } else {\n      return self._pos;\n    }\n\n    return self;\n  };\n\n  /**\n   * Get/set the direction the listener is pointing in the 3D cartesian space.\n   * A front and up vector must be provided. The front is the direction the\n   * face of the listener is pointing, and up is the direction the top of the\n   * listener is pointing. Thus, these values are expected to be at right angles\n   * from each other.\n   * @param  {Number} x   The x-orientation of the listener.\n   * @param  {Number} y   The y-orientation of the listener.\n   * @param  {Number} z   The z-orientation of the listener.\n   * @param  {Number} xUp The x-orientation of the top of the listener.\n   * @param  {Number} yUp The y-orientation of the top of the listener.\n   * @param  {Number} zUp The z-orientation of the top of the listener.\n   * @return {Howler/Array}     Returns self or the current orientation vectors.\n   */\n  HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self.ctx || !self.ctx.listener) {\n      return self;\n    }\n\n    // Set the defaults for optional 'y' & 'z'.\n    var or = self._orientation;\n    y = (typeof y !== 'number') ? or[1] : y;\n    z = (typeof z !== 'number') ? or[2] : z;\n    xUp = (typeof xUp !== 'number') ? or[3] : xUp;\n    yUp = (typeof yUp !== 'number') ? or[4] : yUp;\n    zUp = (typeof zUp !== 'number') ? or[5] : zUp;\n\n    if (typeof x === 'number') {\n      self._orientation = [x, y, z, xUp, yUp, zUp];\n\n      if (typeof self.ctx.listener.forwardX !== 'undefined') {\n        self.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1);\n        self.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1);\n      } else {\n        self.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp);\n      }\n    } else {\n      return or;\n    }\n\n    return self;\n  };\n\n  /** Group Methods **/\n  /***************************************************************************/\n\n  /**\n   * Add new properties to the core init.\n   * @param  {Function} _super Core init method.\n   * @return {Howl}\n   */\n  Howl.prototype.init = (function(_super) {\n    return function(o) {\n      var self = this;\n\n      // Setup user-defined default properties.\n      self._orientation = o.orientation || [1, 0, 0];\n      self._stereo = o.stereo || null;\n      self._pos = o.pos || null;\n      self._pannerAttr = {\n        coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : 360,\n        coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : 360,\n        coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : 0,\n        distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : 'inverse',\n        maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : 10000,\n        panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : 'HRTF',\n        refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : 1,\n        rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : 1\n      };\n\n      // Setup event listeners.\n      self._onstereo = o.onstereo ? [{fn: o.onstereo}] : [];\n      self._onpos = o.onpos ? [{fn: o.onpos}] : [];\n      self._onorientation = o.onorientation ? [{fn: o.onorientation}] : [];\n\n      // Complete initilization with howler.js core's init function.\n      return _super.call(this, o);\n    };\n  })(Howl.prototype.init);\n\n  /**\n   * Get/set the stereo panning of the audio source for this sound or all in the group.\n   * @param  {Number} pan  A value of -1.0 is all the way left and 1.0 is all the way right.\n   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.\n   * @return {Howl/Number}    Returns self or the current stereo panning value.\n   */\n  Howl.prototype.stereo = function(pan, id) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self._webAudio) {\n      return self;\n    }\n\n    // If the sound hasn't loaded, add it to the load queue to change stereo pan when capable.\n    if (self._state !== 'loaded') {\n      self._queue.push({\n        event: 'stereo',\n        action: function() {\n          self.stereo(pan, id);\n        }\n      });\n\n      return self;\n    }\n\n    // Check for PannerStereoNode support and fallback to PannerNode if it doesn't exist.\n    var pannerType = (typeof Howler.ctx.createStereoPanner === 'undefined') ? 'spatial' : 'stereo';\n\n    // Setup the group's stereo panning if no ID is passed.\n    if (typeof id === 'undefined') {\n      // Return the group's stereo panning if no parameters are passed.\n      if (typeof pan === 'number') {\n        self._stereo = pan;\n        self._pos = [pan, 0, 0];\n      } else {\n        return self._stereo;\n      }\n    }\n\n    // Change the streo panning of one or all sounds in group.\n    var ids = self._getSoundIds(id);\n    for (var i=0; i<ids.length; i++) {\n      // Get the sound.\n      var sound = self._soundById(ids[i]);\n\n      if (sound) {\n        if (typeof pan === 'number') {\n          sound._stereo = pan;\n          sound._pos = [pan, 0, 0];\n\n          if (sound._node) {\n            // If we are falling back, make sure the panningModel is equalpower.\n            sound._pannerAttr.panningModel = 'equalpower';\n\n            // Check if there is a panner setup and create a new one if not.\n            if (!sound._panner || !sound._panner.pan) {\n              setupPanner(sound, pannerType);\n            }\n\n            if (pannerType === 'spatial') {\n              if (typeof sound._panner.positionX !== 'undefined') {\n                sound._panner.positionX.setValueAtTime(pan, Howler.ctx.currentTime);\n                sound._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime);\n                sound._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime);\n              } else {\n                sound._panner.setPosition(pan, 0, 0);\n              }\n            } else {\n              sound._panner.pan.setValueAtTime(pan, Howler.ctx.currentTime);\n            }\n          }\n\n          self._emit('stereo', sound._id);\n        } else {\n          return sound._stereo;\n        }\n      }\n    }\n\n    return self;\n  };\n\n  /**\n   * Get/set the 3D spatial position of the audio source for this sound or group relative to the global listener.\n   * @param  {Number} x  The x-position of the audio source.\n   * @param  {Number} y  The y-position of the audio source.\n   * @param  {Number} z  The z-position of the audio source.\n   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.\n   * @return {Howl/Array}    Returns self or the current 3D spatial position: [x, y, z].\n   */\n  Howl.prototype.pos = function(x, y, z, id) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self._webAudio) {\n      return self;\n    }\n\n    // If the sound hasn't loaded, add it to the load queue to change position when capable.\n    if (self._state !== 'loaded') {\n      self._queue.push({\n        event: 'pos',\n        action: function() {\n          self.pos(x, y, z, id);\n        }\n      });\n\n      return self;\n    }\n\n    // Set the defaults for optional 'y' & 'z'.\n    y = (typeof y !== 'number') ? 0 : y;\n    z = (typeof z !== 'number') ? -0.5 : z;\n\n    // Setup the group's spatial position if no ID is passed.\n    if (typeof id === 'undefined') {\n      // Return the group's spatial position if no parameters are passed.\n      if (typeof x === 'number') {\n        self._pos = [x, y, z];\n      } else {\n        return self._pos;\n      }\n    }\n\n    // Change the spatial position of one or all sounds in group.\n    var ids = self._getSoundIds(id);\n    for (var i=0; i<ids.length; i++) {\n      // Get the sound.\n      var sound = self._soundById(ids[i]);\n\n      if (sound) {\n        if (typeof x === 'number') {\n          sound._pos = [x, y, z];\n\n          if (sound._node) {\n            // Check if there is a panner setup and create a new one if not.\n            if (!sound._panner || sound._panner.pan) {\n              setupPanner(sound, 'spatial');\n            }\n\n            if (typeof sound._panner.positionX !== 'undefined') {\n              sound._panner.positionX.setValueAtTime(x, Howler.ctx.currentTime);\n              sound._panner.positionY.setValueAtTime(y, Howler.ctx.currentTime);\n              sound._panner.positionZ.setValueAtTime(z, Howler.ctx.currentTime);\n            } else {\n              sound._panner.setPosition(x, y, z);\n            }\n          }\n\n          self._emit('pos', sound._id);\n        } else {\n          return sound._pos;\n        }\n      }\n    }\n\n    return self;\n  };\n\n  /**\n   * Get/set the direction the audio source is pointing in the 3D cartesian coordinate\n   * space. Depending on how direction the sound is, based on the `cone` attributes,\n   * a sound pointing away from the listener can be quiet or silent.\n   * @param  {Number} x  The x-orientation of the source.\n   * @param  {Number} y  The y-orientation of the source.\n   * @param  {Number} z  The z-orientation of the source.\n   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.\n   * @return {Howl/Array}    Returns self or the current 3D spatial orientation: [x, y, z].\n   */\n  Howl.prototype.orientation = function(x, y, z, id) {\n    var self = this;\n\n    // Stop right here if not using Web Audio.\n    if (!self._webAudio) {\n      return self;\n    }\n\n    // If the sound hasn't loaded, add it to the load queue to change orientation when capable.\n    if (self._state !== 'loaded') {\n      self._queue.push({\n        event: 'orientation',\n        action: function() {\n          self.orientation(x, y, z, id);\n        }\n      });\n\n      return self;\n    }\n\n    // Set the defaults for optional 'y' & 'z'.\n    y = (typeof y !== 'number') ? self._orientation[1] : y;\n    z = (typeof z !== 'number') ? self._orientation[2] : z;\n\n    // Setup the group's spatial orientation if no ID is passed.\n    if (typeof id === 'undefined') {\n      // Return the group's spatial orientation if no parameters are passed.\n      if (typeof x === 'number') {\n        self._orientation = [x, y, z];\n      } else {\n        return self._orientation;\n      }\n    }\n\n    // Change the spatial orientation of one or all sounds in group.\n    var ids = self._getSoundIds(id);\n    for (var i=0; i<ids.length; i++) {\n      // Get the sound.\n      var sound = self._soundById(ids[i]);\n\n      if (sound) {\n        if (typeof x === 'number') {\n          sound._orientation = [x, y, z];\n\n          if (sound._node) {\n            // Check if there is a panner setup and create a new one if not.\n            if (!sound._panner) {\n              // Make sure we have a position to setup the node with.\n              if (!sound._pos) {\n                sound._pos = self._pos || [0, 0, -0.5];\n              }\n\n              setupPanner(sound, 'spatial');\n            }\n\n            if (typeof sound._panner.orientationX !== 'undefined') {\n              sound._panner.orientationX.setValueAtTime(x, Howler.ctx.currentTime);\n              sound._panner.orientationY.setValueAtTime(y, Howler.ctx.currentTime);\n              sound._panner.orientationZ.setValueAtTime(z, Howler.ctx.currentTime);\n            } else {\n              sound._panner.setOrientation(x, y, z);\n            }\n          }\n\n          self._emit('orientation', sound._id);\n        } else {\n          return sound._orientation;\n        }\n      }\n    }\n\n    return self;\n  };\n\n  /**\n   * Get/set the panner node's attributes for a sound or group of sounds.\n   * This method can optionall take 0, 1 or 2 arguments.\n   *   pannerAttr() -> Returns the group's values.\n   *   pannerAttr(id) -> Returns the sound id's values.\n   *   pannerAttr(o) -> Set's the values of all sounds in this Howl group.\n   *   pannerAttr(o, id) -> Set's the values of passed sound id.\n   *\n   *   Attributes:\n   *     coneInnerAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,\n   *                      inside of which there will be no volume reduction.\n   *     coneOuterAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,\n   *                      outside of which the volume will be reduced to a constant value of `coneOuterGain`.\n   *     coneOuterGain - (0 by default) A parameter for directional audio sources, this is the gain outside of the\n   *                     `coneOuterAngle`. It is a linear value in the range `[0, 1]`.\n   *     distanceModel - ('inverse' by default) Determines algorithm used to reduce volume as audio moves away from\n   *                     listener. Can be `linear`, `inverse` or `exponential.\n   *     maxDistance - (10000 by default) The maximum distance between source and listener, after which the volume\n   *                   will not be reduced any further.\n   *     refDistance - (1 by default) A reference distance for reducing volume as source moves further from the listener.\n   *                   This is simply a variable of the distance model and has a different effect depending on which model\n   *                   is used and the scale of your coordinates. Generally, volume will be equal to 1 at this distance.\n   *     rolloffFactor - (1 by default) How quickly the volume reduces as source moves from listener. This is simply a\n   *                     variable of the distance model and can be in the range of `[0, 1]` with `linear` and `[0, ∞]`\n   *                     with `inverse` and `exponential`.\n   *     panningModel - ('HRTF' by default) Determines which spatialization algorithm is used to position audio.\n   *                     Can be `HRTF` or `equalpower`.\n   *\n   * @return {Howl/Object} Returns self or current panner attributes.\n   */\n  Howl.prototype.pannerAttr = function() {\n    var self = this;\n    var args = arguments;\n    var o, id, sound;\n\n    // Stop right here if not using Web Audio.\n    if (!self._webAudio) {\n      return self;\n    }\n\n    // Determine the values based on arguments.\n    if (args.length === 0) {\n      // Return the group's panner attribute values.\n      return self._pannerAttr;\n    } else if (args.length === 1) {\n      if (typeof args[0] === 'object') {\n        o = args[0];\n\n        // Set the grou's panner attribute values.\n        if (typeof id === 'undefined') {\n          if (!o.pannerAttr) {\n            o.pannerAttr = {\n              coneInnerAngle: o.coneInnerAngle,\n              coneOuterAngle: o.coneOuterAngle,\n              coneOuterGain: o.coneOuterGain,\n              distanceModel: o.distanceModel,\n              maxDistance: o.maxDistance,\n              refDistance: o.refDistance,\n              rolloffFactor: o.rolloffFactor,\n              panningModel: o.panningModel\n            };\n          }\n\n          self._pannerAttr = {\n            coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== 'undefined' ? o.pannerAttr.coneInnerAngle : self._coneInnerAngle,\n            coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== 'undefined' ? o.pannerAttr.coneOuterAngle : self._coneOuterAngle,\n            coneOuterGain: typeof o.pannerAttr.coneOuterGain !== 'undefined' ? o.pannerAttr.coneOuterGain : self._coneOuterGain,\n            distanceModel: typeof o.pannerAttr.distanceModel !== 'undefined' ? o.pannerAttr.distanceModel : self._distanceModel,\n            maxDistance: typeof o.pannerAttr.maxDistance !== 'undefined' ? o.pannerAttr.maxDistance : self._maxDistance,\n            refDistance: typeof o.pannerAttr.refDistance !== 'undefined' ? o.pannerAttr.refDistance : self._refDistance,\n            rolloffFactor: typeof o.pannerAttr.rolloffFactor !== 'undefined' ? o.pannerAttr.rolloffFactor : self._rolloffFactor,\n            panningModel: typeof o.pannerAttr.panningModel !== 'undefined' ? o.pannerAttr.panningModel : self._panningModel\n          };\n        }\n      } else {\n        // Return this sound's panner attribute values.\n        sound = self._soundById(parseInt(args[0], 10));\n        return sound ? sound._pannerAttr : self._pannerAttr;\n      }\n    } else if (args.length === 2) {\n      o = args[0];\n      id = parseInt(args[1], 10);\n    }\n\n    // Update the values of the specified sounds.\n    var ids = self._getSoundIds(id);\n    for (var i=0; i<ids.length; i++) {\n      sound = self._soundById(ids[i]);\n\n      if (sound) {\n        // Merge the new values into the sound.\n        var pa = sound._pannerAttr;\n        pa = {\n          coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : pa.coneInnerAngle,\n          coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : pa.coneOuterAngle,\n          coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : pa.coneOuterGain,\n          distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : pa.distanceModel,\n          maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : pa.maxDistance,\n          refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : pa.refDistance,\n          rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : pa.rolloffFactor,\n          panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : pa.panningModel\n        };\n\n        // Create a new panner node if one doesn't already exist.\n        var panner = sound._panner;\n        if (!panner) {\n          // Make sure we have a position to setup the node with.\n          if (!sound._pos) {\n            sound._pos = self._pos || [0, 0, -0.5];\n          }\n\n          // Create a new panner node.\n          setupPanner(sound, 'spatial');\n          panner = sound._panner\n        }\n\n        // Update the panner values or create a new panner if none exists.\n        panner.coneInnerAngle = pa.coneInnerAngle;\n        panner.coneOuterAngle = pa.coneOuterAngle;\n        panner.coneOuterGain = pa.coneOuterGain;\n        panner.distanceModel = pa.distanceModel;\n        panner.maxDistance = pa.maxDistance;\n        panner.refDistance = pa.refDistance;\n        panner.rolloffFactor = pa.rolloffFactor;\n        panner.panningModel = pa.panningModel;\n      }\n    }\n\n    return self;\n  };\n\n  /** Single Sound Methods **/\n  /***************************************************************************/\n\n  /**\n   * Add new properties to the core Sound init.\n   * @param  {Function} _super Core Sound init method.\n   * @return {Sound}\n   */\n  Sound.prototype.init = (function(_super) {\n    return function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Setup user-defined default properties.\n      self._orientation = parent._orientation;\n      self._stereo = parent._stereo;\n      self._pos = parent._pos;\n      self._pannerAttr = parent._pannerAttr;\n\n      // Complete initilization with howler.js core Sound's init function.\n      _super.call(this);\n\n      // If a stereo or position was specified, set it up.\n      if (self._stereo) {\n        parent.stereo(self._stereo);\n      } else if (self._pos) {\n        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);\n      }\n    };\n  })(Sound.prototype.init);\n\n  /**\n   * Override the Sound.reset method to clean up properties from the spatial plugin.\n   * @param  {Function} _super Sound reset method.\n   * @return {Sound}\n   */\n  Sound.prototype.reset = (function(_super) {\n    return function() {\n      var self = this;\n      var parent = self._parent;\n\n      // Reset all spatial plugin properties on this sound.\n      self._orientation = parent._orientation;\n      self._stereo = parent._stereo;\n      self._pos = parent._pos;\n      self._pannerAttr = parent._pannerAttr;\n\n      // If a stereo or position was specified, set it up.\n      if (self._stereo) {\n        parent.stereo(self._stereo);\n      } else if (self._pos) {\n        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);\n      } else if (self._panner) {\n        // Disconnect the panner.\n        self._panner.disconnect(0);\n        self._panner = undefined;\n        parent._refreshBuffer(self);\n      }\n\n      // Complete resetting of the sound.\n      return _super.call(this);\n    };\n  })(Sound.prototype.reset);\n\n  /** Helper Methods **/\n  /***************************************************************************/\n\n  /**\n   * Create a new panner node and save it on the sound.\n   * @param  {Sound} sound Specific sound to setup panning on.\n   * @param {String} type Type of panner to create: 'stereo' or 'spatial'.\n   */\n  var setupPanner = function(sound, type) {\n    type = type || 'spatial';\n\n    // Create the new panner node.\n    if (type === 'spatial') {\n      sound._panner = Howler.ctx.createPanner();\n      sound._panner.coneInnerAngle = sound._pannerAttr.coneInnerAngle;\n      sound._panner.coneOuterAngle = sound._pannerAttr.coneOuterAngle;\n      sound._panner.coneOuterGain = sound._pannerAttr.coneOuterGain;\n      sound._panner.distanceModel = sound._pannerAttr.distanceModel;\n      sound._panner.maxDistance = sound._pannerAttr.maxDistance;\n      sound._panner.refDistance = sound._pannerAttr.refDistance;\n      sound._panner.rolloffFactor = sound._pannerAttr.rolloffFactor;\n      sound._panner.panningModel = sound._pannerAttr.panningModel;\n\n      if (typeof sound._panner.positionX !== 'undefined') {\n        sound._panner.positionX.setValueAtTime(sound._pos[0], Howler.ctx.currentTime);\n        sound._panner.positionY.setValueAtTime(sound._pos[1], Howler.ctx.currentTime);\n        sound._panner.positionZ.setValueAtTime(sound._pos[2], Howler.ctx.currentTime);\n      } else {\n        sound._panner.setPosition(sound._pos[0], sound._pos[1], sound._pos[2]);\n      }\n\n      if (typeof sound._panner.orientationX !== 'undefined') {\n        sound._panner.orientationX.setValueAtTime(sound._orientation[0], Howler.ctx.currentTime);\n        sound._panner.orientationY.setValueAtTime(sound._orientation[1], Howler.ctx.currentTime);\n        sound._panner.orientationZ.setValueAtTime(sound._orientation[2], Howler.ctx.currentTime);\n      } else {\n        sound._panner.setOrientation(sound._orientation[0], sound._orientation[1], sound._orientation[2]);\n      }\n    } else {\n      sound._panner = Howler.ctx.createStereoPanner();\n      sound._panner.pan.setValueAtTime(sound._stereo, Howler.ctx.currentTime);\n    }\n\n    sound._panner.connect(sound._node);\n\n    // Update the connections.\n    if (!sound._paused) {\n      sound._parent.pause(sound._id, true).play(sound._id, true);\n    }\n  };\n})();\n","// https://github.com/melonjs/melonJS/issues/1092\nimport \"core-js/proposals/global-this\";\n\n// es10 string trim functions\nimport \"core-js/es/string/trim-start\";\nimport \"core-js/es/string/trim-end\";\n\n// \"built-in\" polyfills\nimport \"./console.ts\";\nimport \"./ellipse.ts\";\nimport \"./roundrect.js\";\n","if (typeof globalThis !== \"undefined\") {\n\tif (typeof globalThis.console === \"undefined\") {\n\t\tglobalThis.console = {} as Console;\n\t\tglobalThis.console.log = function () {};\n\t\tglobalThis.console.assert = function () {};\n\t\tglobalThis.console.warn = function () {};\n\t\tglobalThis.console.error = function (...args: any[]) {\n\t\t\talert(Array.prototype.slice.call(args).join(\", \"));\n\t\t};\n\t}\n}\n","/*\n * CanvasRenderingContext2D.ellipse() polyfill\n * Uses cubic bezier curves to approximate the ellipse when native support is unavailable.\n */\n\n/** @ignore */\nfunction ellipse(\n\tthis: CanvasRenderingContext2D | Path2D,\n\tx: number,\n\ty: number,\n\tradiusX: number,\n\tradiusY: number,\n\trotation: number,\n\tstartAngle: number,\n\tendAngle: number,\n\tcounterclockwise: boolean = false,\n) {\n\tif (radiusX < 0 || radiusY < 0) {\n\t\tthrow new RangeError(\"Radius values must be non-negative.\");\n\t}\n\n\t// bail out on non-finite inputs to avoid infinite loops\n\tif (\n\t\t!Number.isFinite(x) ||\n\t\t!Number.isFinite(y) ||\n\t\t!Number.isFinite(radiusX) ||\n\t\t!Number.isFinite(radiusY) ||\n\t\t!Number.isFinite(rotation) ||\n\t\t!Number.isFinite(startAngle) ||\n\t\t!Number.isFinite(endAngle)\n\t) {\n\t\treturn;\n\t}\n\n\tconst ctx = this as CanvasRenderingContext2D;\n\n\t// for a full, unrotated ellipse use the fast bezier path\n\tconst isFullEllipse =\n\t\trotation === 0 &&\n\t\tstartAngle === 0 &&\n\t\t(Math.abs(endAngle - Math.PI * 2) < 1e-6 ||\n\t\t\tMath.abs(endAngle - 360) < 1e-6);\n\n\tif (isFullEllipse && !counterclockwise) {\n\t\t// kappa constant for cubic bezier approximation of a quarter circle\n\t\tconst kappa = 0.5522848;\n\t\tconst kx = radiusX * kappa;\n\t\tconst ky = radiusY * kappa;\n\n\t\t// use lineTo for the first point to continue existing subpaths\n\t\t// (matches native ellipse/arc behavior)\n\t\tctx.lineTo(x + radiusX, y);\n\t\tctx.bezierCurveTo(x + radiusX, y + ky, x + kx, y + radiusY, x, y + radiusY);\n\t\tctx.bezierCurveTo(x - kx, y + radiusY, x - radiusX, y + ky, x - radiusX, y);\n\t\tctx.bezierCurveTo(x - radiusX, y - ky, x - kx, y - radiusY, x, y - radiusY);\n\t\tctx.bezierCurveTo(x + kx, y - radiusY, x + radiusX, y - ky, x + radiusX, y);\n\t} else {\n\t\t// general case: approximate with line segments\n\t\tconst step = Math.PI / 36; // 5-degree increments\n\t\tconst dir = counterclockwise ? -1 : 1;\n\t\tlet end = endAngle;\n\n\t\tif (counterclockwise && endAngle > startAngle) {\n\t\t\tend -= Math.PI * 2;\n\t\t} else if (!counterclockwise && endAngle < startAngle) {\n\t\t\tend += Math.PI * 2;\n\t\t}\n\n\t\t// cap iterations to prevent runaway loops\n\t\tconst maxSegments = Math.ceil(Math.abs(end - startAngle) / step) + 1;\n\n\t\tconst cos = Math.cos(rotation);\n\t\tconst sin = Math.sin(rotation);\n\n\t\tfor (let i = 0; i <= maxSegments; i++) {\n\t\t\tconst angle = i === maxSegments ? end : startAngle + i * step * dir;\n\t\t\t// stop if we've passed the end angle\n\t\t\tif ((dir > 0 && angle > end) || (dir < 0 && angle < end)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst px =\n\t\t\t\tx + radiusX * Math.cos(angle) * cos - radiusY * Math.sin(angle) * sin;\n\t\t\tconst py =\n\t\t\t\ty + radiusX * Math.cos(angle) * sin + radiusY * Math.sin(angle) * cos;\n\t\t\t// lineTo continues existing subpaths (matches native behavior)\n\t\t\tctx.lineTo(px, py);\n\t\t}\n\n\t\t// ensure we hit the exact end angle\n\t\tconst px =\n\t\t\tx +\n\t\t\tradiusX * Math.cos(endAngle) * cos -\n\t\t\tradiusY * Math.sin(endAngle) * sin;\n\t\tconst py =\n\t\t\ty +\n\t\t\tradiusX * Math.cos(endAngle) * sin +\n\t\t\tradiusY * Math.sin(endAngle) * cos;\n\t\tctx.lineTo(px, py);\n\t}\n}\n\nif (globalThis.CanvasRenderingContext2D) {\n\tif (\n\t\ttypeof globalThis.CanvasRenderingContext2D.prototype.ellipse === \"undefined\"\n\t) {\n\t\tglobalThis.CanvasRenderingContext2D.prototype.ellipse = ellipse;\n\t}\n}\nif (globalThis.OffscreenCanvasRenderingContext2D) {\n\tif (\n\t\ttypeof globalThis.OffscreenCanvasRenderingContext2D.prototype.ellipse ===\n\t\t\"undefined\"\n\t) {\n\t\tglobalThis.OffscreenCanvasRenderingContext2D.prototype.ellipse = ellipse;\n\t}\n}\n","/*\n * based on https://www.npmjs.com/package/canvas-roundrect-polyfill\n * @version 0.0.1\n */\n\n/** @ignore */\nfunction roundRect(\n\tthis: CanvasRenderingContext2D,\n\tx: number,\n\ty: number,\n\tw: number,\n\th: number,\n\tradii: unknown,\n) {\n\tif (\n\t\t![x, y, w, h].every((input) => {\n\t\t\treturn Number.isFinite(input);\n\t\t})\n\t) {\n\t\treturn;\n\t}\n\n\tconst parsedRadii = parseRadiiArgument(radii);\n\n\tlet upperLeft;\n\tlet upperRight;\n\tlet lowerRight;\n\tlet lowerLeft;\n\n\tif (parsedRadii.length === 4) {\n\t\tupperLeft = toCornerPoint(parsedRadii[0]);\n\t\tupperRight = toCornerPoint(parsedRadii[1]);\n\t\tlowerRight = toCornerPoint(parsedRadii[2]);\n\t\tlowerLeft = toCornerPoint(parsedRadii[3]);\n\t} else if (parsedRadii.length === 3) {\n\t\tupperLeft = toCornerPoint(parsedRadii[0]);\n\t\tupperRight = toCornerPoint(parsedRadii[1]);\n\t\tlowerLeft = toCornerPoint(parsedRadii[1]);\n\t\tlowerRight = toCornerPoint(parsedRadii[2]);\n\t} else if (parsedRadii.length === 2) {\n\t\tupperLeft = toCornerPoint(parsedRadii[0]);\n\t\tlowerRight = toCornerPoint(parsedRadii[0]);\n\t\tupperRight = toCornerPoint(parsedRadii[1]);\n\t\tlowerLeft = toCornerPoint(parsedRadii[1]);\n\t} else if (parsedRadii.length === 1) {\n\t\tupperLeft = toCornerPoint(parsedRadii[0]);\n\t\tupperRight = toCornerPoint(parsedRadii[0]);\n\t\tlowerRight = toCornerPoint(parsedRadii[0]);\n\t\tlowerLeft = toCornerPoint(parsedRadii[0]);\n\t} else {\n\t\tthrow new Error(\n\t\t\t`${parsedRadii.length} is not a valid size for radii sequence.`,\n\t\t);\n\t}\n\n\tconst corners = [upperLeft, upperRight, lowerRight, lowerLeft];\n\tconst negativeCorner = corners.find(({ x, y }) => {\n\t\treturn x < 0 || y < 0;\n\t});\n\t//const negativeValue = negativeCorner?.x < 0 ? negativeCorner.x : negativeCorner?.y\n\n\tif (\n\t\tcorners.some(({ x, y }) => {\n\t\t\treturn !Number.isFinite(x) || !Number.isFinite(y);\n\t\t})\n\t) {\n\t\treturn;\n\t}\n\n\tif (negativeCorner) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions\n\t\tthrow new Error(`Radius value ${negativeCorner} is negative.`);\n\t}\n\n\tfixOverlappingCorners(corners);\n\n\tif (w < 0 && h < 0) {\n\t\tthis.moveTo(x - upperLeft.x, y);\n\t\tthis.ellipse(\n\t\t\tx + w + upperRight.x,\n\t\t\ty - upperRight.y,\n\t\t\tupperRight.x,\n\t\t\tupperRight.y,\n\t\t\t0,\n\t\t\t-Math.PI * 1.5,\n\t\t\t-Math.PI,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + w + lowerRight.x,\n\t\t\ty + h + lowerRight.y,\n\t\t\tlowerRight.x,\n\t\t\tlowerRight.y,\n\t\t\t0,\n\t\t\t-Math.PI,\n\t\t\t-Math.PI / 2,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx - lowerLeft.x,\n\t\t\ty + h + lowerLeft.y,\n\t\t\tlowerLeft.x,\n\t\t\tlowerLeft.y,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\t0,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx - upperLeft.x,\n\t\t\ty - upperLeft.y,\n\t\t\tupperLeft.x,\n\t\t\tupperLeft.y,\n\t\t\t0,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t);\n\t} else if (w < 0) {\n\t\tthis.moveTo(x - upperLeft.x, y);\n\t\tthis.ellipse(\n\t\t\tx + w + upperRight.x,\n\t\t\ty + upperRight.y,\n\t\t\tupperRight.x,\n\t\t\tupperRight.y,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\t-Math.PI,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + w + lowerRight.x,\n\t\t\ty + h - lowerRight.y,\n\t\t\tlowerRight.x,\n\t\t\tlowerRight.y,\n\t\t\t0,\n\t\t\t-Math.PI,\n\t\t\t-Math.PI * 1.5,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx - lowerLeft.x,\n\t\t\ty + h - lowerLeft.y,\n\t\t\tlowerLeft.x,\n\t\t\tlowerLeft.y,\n\t\t\t0,\n\t\t\tMath.PI / 2,\n\t\t\t0,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx - upperLeft.x,\n\t\t\ty + upperLeft.y,\n\t\t\tupperLeft.x,\n\t\t\tupperLeft.y,\n\t\t\t0,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\ttrue,\n\t\t);\n\t} else if (h < 0) {\n\t\tthis.moveTo(x + upperLeft.x, y);\n\t\tthis.ellipse(\n\t\t\tx + w - upperRight.x,\n\t\t\ty - upperRight.y,\n\t\t\tupperRight.x,\n\t\t\tupperRight.y,\n\t\t\t0,\n\t\t\tMath.PI / 2,\n\t\t\t0,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + w - lowerRight.x,\n\t\t\ty + h + lowerRight.y,\n\t\t\tlowerRight.x,\n\t\t\tlowerRight.y,\n\t\t\t0,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + lowerLeft.x,\n\t\t\ty + h + lowerLeft.y,\n\t\t\tlowerLeft.x,\n\t\t\tlowerLeft.y,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\t-Math.PI,\n\t\t\ttrue,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + upperLeft.x,\n\t\t\ty - upperLeft.y,\n\t\t\tupperLeft.x,\n\t\t\tupperLeft.y,\n\t\t\t0,\n\t\t\t-Math.PI,\n\t\t\t-Math.PI * 1.5,\n\t\t\ttrue,\n\t\t);\n\t} else {\n\t\tthis.moveTo(x + upperLeft.x, y);\n\t\tthis.ellipse(\n\t\t\tx + w - upperRight.x,\n\t\t\ty + upperRight.y,\n\t\t\tupperRight.x,\n\t\t\tupperRight.y,\n\t\t\t0,\n\t\t\t-Math.PI / 2,\n\t\t\t0,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + w - lowerRight.x,\n\t\t\ty + h - lowerRight.y,\n\t\t\tlowerRight.x,\n\t\t\tlowerRight.y,\n\t\t\t0,\n\t\t\t0,\n\t\t\tMath.PI / 2,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + lowerLeft.x,\n\t\t\ty + h - lowerLeft.y,\n\t\t\tlowerLeft.x,\n\t\t\tlowerLeft.y,\n\t\t\t0,\n\t\t\tMath.PI / 2,\n\t\t\tMath.PI,\n\t\t);\n\t\tthis.ellipse(\n\t\t\tx + upperLeft.x,\n\t\t\ty + upperLeft.y,\n\t\t\tupperLeft.x,\n\t\t\tupperLeft.y,\n\t\t\t0,\n\t\t\tMath.PI,\n\t\t\tMath.PI * 1.5,\n\t\t);\n\t}\n\n\tthis.closePath();\n\tthis.moveTo(x, y);\n\n\t/** @ignore */\n\tfunction toDOMPointInit(value: {\n\t\tx: number;\n\t\ty: number;\n\t\tz: number;\n\t\tw: number;\n\t}) {\n\t\tconst { x, y, z, w } = value;\n\t\treturn { x, y, z, w };\n\t}\n\n\t/** @ignore */\n\tfunction parseRadiiArgument(\n\t\tvalue: any,\n\t):\n\t\t| [number, number, number, number]\n\t\t| [number, number, number]\n\t\t| [number, number]\n\t\t| [number]\n\t\t| unknown[] {\n\t\t// https://webidl.spec.whatwg.org/#es-union\n\t\t// with 'optional (unrestricted double or DOMPointInit\n\t\t//   or sequence<(unrestricted double or DOMPointInit)>) radii = 0'\n\t\tconst type = typeof value;\n\n\t\tif (type === \"undefined\" || value === null) {\n\t\t\treturn [0];\n\t\t}\n\t\tif (type === \"function\") {\n\t\t\treturn [NaN];\n\t\t}\n\t\tif (type === \"object\") {\n\t\t\tif (typeof value[Symbol.iterator] === \"function\") {\n\t\t\t\treturn [...value].map((elem) => {\n\t\t\t\t\t// https://webidl.spec.whatwg.org/#es-union\n\t\t\t\t\t// with '(unrestricted double or DOMPointInit)'\n\t\t\t\t\tconst elemType = typeof elem;\n\t\t\t\t\tif (elemType === \"undefined\" || elem === null) {\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (elemType === \"function\") {\n\t\t\t\t\t\treturn NaN;\n\t\t\t\t\t}\n\t\t\t\t\tif (elemType === \"object\") {\n\t\t\t\t\t\treturn toDOMPointInit(elem);\n\t\t\t\t\t}\n\t\t\t\t\treturn toUnrestrictedNumber(elem);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn [toDOMPointInit(value)];\n\t\t}\n\n\t\treturn [toUnrestrictedNumber(value)];\n\t}\n\n\t/** @ignore */\n\tfunction toUnrestrictedNumber(value: any) {\n\t\treturn +value;\n\t}\n\n\t/** @ignore */\n\tfunction toCornerPoint(value: any) {\n\t\tconst asNumber = toUnrestrictedNumber(value);\n\t\tif (Number.isFinite(asNumber)) {\n\t\t\treturn {\n\t\t\t\tx: asNumber,\n\t\t\t\ty: asNumber,\n\t\t\t};\n\t\t}\n\t\tif (Object(value) === value) {\n\t\t\treturn {\n\t\t\t\tx: toUnrestrictedNumber(value.x || 0),\n\t\t\t\ty: toUnrestrictedNumber(value.y || 0),\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tx: NaN,\n\t\t\ty: NaN,\n\t\t};\n\t}\n\n\t/** @ignore */\n\tfunction fixOverlappingCorners(corners: any) {\n\t\tconst [upperLeft, upperRight, lowerRight, lowerLeft] = corners;\n\t\tconst factors = [\n\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\tMath.abs(w) / (upperLeft.x + upperRight.x),\n\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\tMath.abs(h) / (upperRight.y + lowerRight.y),\n\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\tMath.abs(w) / (lowerRight.x + lowerLeft.x),\n\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\tMath.abs(h) / (upperLeft.y + lowerLeft.y),\n\t\t];\n\t\tconst minFactor = Math.min(...factors);\n\t\tif (minFactor <= 1) {\n\t\t\tcorners.forEach((radii: any) => {\n\t\t\t\tradii.x *= minFactor;\n\t\t\t\tradii.y *= minFactor;\n\t\t\t});\n\t\t}\n\t}\n}\n\nif (globalThis.CanvasRenderingContext2D) {\n\tif (typeof globalThis.Path2D.prototype.roundRect === \"undefined\") {\n\t\tglobalThis.Path2D.prototype.roundRect = roundRect;\n\t}\n}\nif (globalThis.CanvasRenderingContext2D) {\n\tif (\n\t\ttypeof globalThis.CanvasRenderingContext2D.prototype.roundRect ===\n\t\t\"undefined\"\n\t) {\n\t\tglobalThis.CanvasRenderingContext2D.prototype.roundRect = roundRect;\n\t}\n}\nif (globalThis.OffscreenCanvasRenderingContext2D) {\n\tif (\n\t\ttypeof globalThis.OffscreenCanvasRenderingContext2D.prototype.roundRect ===\n\t\t\"undefined\"\n\t) {\n\t\tglobalThis.OffscreenCanvasRenderingContext2D.prototype.roundRect =\n\t\t\troundRect;\n\t}\n}\n","/**\n * Select the HTML5 Canvas renderer. Lower performance and no\n * programmable pipeline, but supported on every browser including\n * environments where the GPU backends are unavailable (some embedded\n * webviews, stripped-down kiosk browsers, GPU blocklisted by driver\n * policy).\n *\n * Use when the example / game uses only 2D sprites + primitives and you\n * want the broadest possible reach. Anything depending on the GPU\n * backends degrades or disables here: `ShaderEffect` and `Light2d`\n * shading stay inert, GPU TMX tile rendering falls back to the per-tile\n * path, and `Mesh` / `Camera3d` lose the depth-buffer path (meshes only\n * render CPU-projected under a 2D camera — `supportsDepthBuffer` is\n * `false`).\n */\nexport const CANVAS = 0;\n\n/**\n * Require the WebGL renderer. **`app.init()` rejects if WebGL 2 is\n * unavailable** (WebGL-1-only device, driver-blocklisted GPU, software\n * fallback failing the `failIfMajorPerformanceCaveat` check, etc.) —\n * it does NOT silently fall back to Canvas.\n *\n * Use this when your scene needs a GPU backend (Camera3d, Mesh,\n * ShaderEffect, Light2d, GPU tilemap) pinned to WebGL specifically and\n * you'd rather fail fast with a clear error than have the engine render\n * a stuck blank canvas.\n *\n * If falling back is acceptable, use {@link AUTO} instead (WebGPU →\n * WebGL 2 → Canvas).\n */\nexport const WEBGL = 1;\n\n/**\n * Auto-select the renderer: try WebGPU first (when the browser exposes it\n * and an adapter/device negotiates successfully), fall back to WebGL 2,\n * then to Canvas. `await app.init()` always resolves — a failed candidate\n * falls through to the next rather than rejecting.\n *\n * The WebGPU attempt is a full backend initialization (support can only be\n * proven by negotiating a device), so on WebGPU-capable browsers `init()`\n * settles after the adapter handshake; browsers without `navigator.gpu`\n * skip straight to the synchronous WebGL probe.\n *\n * Note: subsystems that require a GPU backend (Camera3d, Mesh,\n * ShaderEffect, Light2d, GPU tilemap) will silently stop working under\n * the terminal Canvas fallback — if your scene depends on any of those,\n * use {@link WEBGL} (or {@link WEBGPU}) so the failure surfaces at\n * startup instead of as a black canvas at runtime.\n *\n * A specific backend can still be forced per-run with the `#webgpu`,\n * `#webgl` or `#canvas` URI fragments.\n */\nexport const AUTO = 2;\n\n/**\n * Require the WebGPU renderer. The backend covers the full rendering\n * contract of the WebGL renderer — the 2D tier (sprites, text,\n * primitives, blend modes, masks and clipping, patterns, ShaderEffect /\n * post effects in WGSL, 2D lights and normal maps, frame captures,\n * gradient fills, compressed textures, GPU tile layers) and the 3D tier\n * (retained and accumulated meshes, lit and unlit, Camera3d, Light3d,\n * glTF scenes and models, Sprite3d billboards).\n *\n * `app.init()` rejects when WebGPU is unavailable in the environment;\n * like {@link WEBGL} it fails loudly rather than falling back, so a\n * missing capability surfaces at startup. Use {@link AUTO} if fallback\n * to WebGL / Canvas is acceptable — AUTO already prefers WebGPU when\n * the browser can negotiate a device.\n */\nexport const WEBGPU = 3;\n\nexport type RendererType =\n\t| typeof CANVAS\n\t| typeof WEBGL\n\t| typeof AUTO\n\t| typeof WEBGPU;\n","const takeFromSet = <T>(set: Set<T>) => {\n\tfor (const value of set) {\n\t\tset.delete(value);\n\t\treturn value;\n\t}\n};\n\nexport interface Pool<T, A extends unknown[]> {\n\tget(...args: A): T;\n\trelease(object: T): void;\n\tpurge(): void;\n\tsize(): number;\n\tused(): number;\n}\n\ntype Reset<A extends unknown[]> = ((...args: A) => void) | undefined;\ntype Release = (() => void) | undefined;\n\nexport interface CreatePoolOptions<T, A extends unknown[]> {\n\tinstance: T;\n\treset?: Reset<A>;\n\trelease?: Release;\n}\n\n// Pool registry for centralized access via getPool/getTotalPoolSize\nconst pools: Record<string, Pool<any, any[]>> = {};\n\n/**\n * Register a pool instance to the pool registry.\n * @ignore\n */\nexport const registerPool = (key: string, pool: Pool<any, any[]>) => {\n\tpools[key] = pool;\n};\n\n/**\n * @ignore\n */\nexport const getRegisteredPools = () => pools;\n\nexport const createPool = <T, A extends unknown[]>(\n\toptions: (...args: A) => CreatePoolOptions<T, A>,\n): Pool<T, A> => {\n\tconst available = new Set<T>();\n\tconst instanceResetMethods = new Map<T, Reset<A>>();\n\tconst instanceReleaseMethods = new Map<T, Release>();\n\tlet inUse: number = 0;\n\n\treturn {\n\t\t/**\n\t\t * release an object back to the pool\n\t\t * @param instance The object to release.\n\t\t */\n\t\trelease: (instance: T) => {\n\t\t\tif (available.has(instance)) {\n\t\t\t\tthrow new Error(\"Instance is already in pool.\");\n\t\t\t}\n\t\t\tconst release = instanceReleaseMethods.get(instance);\n\t\t\trelease?.();\n\t\t\tavailable.add(instance);\n\t\t\tinUse--;\n\t\t},\n\t\t/**\n\t\t * get an instance from the pool\n\t\t * @param args The arguments for creating the instance.\n\t\t */\n\t\tget: (...args) => {\n\t\t\tconst object = takeFromSet(available);\n\t\t\tif (object) {\n\t\t\t\tconst reset = instanceResetMethods.get(object);\n\t\t\t\treset?.(...args);\n\t\t\t\tinUse++;\n\t\t\t\treturn object;\n\t\t\t} else {\n\t\t\t\tconst { instance, reset, release } = options(...args);\n\t\t\t\tinstanceResetMethods.set(instance, reset);\n\t\t\t\tinstanceReleaseMethods.set(instance, release);\n\t\t\t\tinUse++;\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t},\n\t\t/**\n\t\t * purge the pool\n\t\t */\n\t\tpurge: () => {\n\t\t\tavailable.clear();\n\t\t\tinUse = 0;\n\t\t},\n\t\t/**\n\t\t * get the current size of the pool (how many objects are available)\n\t\t */\n\t\tsize: () => {\n\t\t\treturn available.size;\n\t\t},\n\t\t/**\n\t\t * get the number of objects currently in use\n\t\t */\n\t\tused: () => {\n\t\t\treturn inUse;\n\t\t},\n\t};\n};\n","/**\n * a collection of math utility functions\n * @namespace math\n */\n\n/**\n * constant to convert from degrees to radians\n */\nexport const DEG_TO_RAD = Math.PI / 180.0;\n\n/**\n * constant to convert from radians to degrees\n */\nexport const RAD_TO_DEG = 180.0 / Math.PI;\n\n/**\n * constant equals to 2 times pi\n */\nexport const TAU = Math.PI * 2;\n\n/**\n * constant equals to half pi\n */\nexport const ETA = Math.PI * 0.5;\n\n/**\n * the difference between 1 and the smallest floating point number greater than 1\n */\nexport const EPSILON = 0.000001;\n\n/**\n * returns true if the given value is a power of two\n * @param val number to test\n * @returns true if number is a power of two\n */\nexport function isPowerOfTwo(val: number) {\n\treturn (val & (val - 1)) === 0;\n}\n\n/**\n * returns true if the given value is a power of four\n * @param val number to test\n * @returns true if number is a power of four\n */\nexport function isPowerOfFour(val: number) {\n\tif (val === 0 || val === 2 || val === 3) {\n\t\treturn false;\n\t}\n\tif (val === 1) {\n\t\treturn true;\n\t}\n\n\tif ((val & (val - 1)) === 0) {\n\t\tif ((val & 0xaaaaaaaa) === 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * returns the next power of two for the given value\n * @param val any number\n * @returns next power of two relative to the given number\n */\nexport function nextPowerOfTwo(val: number) {\n\tval--;\n\tval |= val >> 1;\n\tval |= val >> 2;\n\tval |= val >> 4;\n\tval |= val >> 8;\n\tval |= val >> 16;\n\tval++;\n\treturn val;\n}\n\n/**\n * Converts an angle in degrees to an angle in radians\n * @param angle - angle in degrees\n * @returns corresponding angle in radians\n * @example\n * // convert a specific angle\n * math.degToRad(60); // return 1.0471...\n */\nexport function degToRad(angle: number) {\n\treturn angle * DEG_TO_RAD;\n}\n\n/**\n * Converts an angle in radians to an angle in degrees.\n * @param radians - angle in radians\n * @returns corresponding angle in degrees\n * @example\n * // convert a specific angle\n * math.radToDeg(1.0471975511965976); // return 60\n */\nexport function radToDeg(radians: number) {\n\treturn radians * RAD_TO_DEG;\n}\n\n/**\n * clamp the given value\n * @param val - the value to clamp\n * @param low - lower limit\n * @param high - higher limit\n * @returns clamped value\n */\nexport function clamp(val: number, low: number, high: number) {\n\treturn val < low ? low : val > high ? high : val;\n}\n\n/**\n * return a random integer between min (included) and max (excluded)\n * @param min - minimum value.\n * @param max - maximum value.\n * @returns random value\n * @example\n * // Print a random number; one of 5, 6, 7, 8, 9\n * console.log(math.random(5, 10));\n */\nexport function random(min: number, max: number) {\n\treturn ~~(Math.random() * (max - min)) + min;\n}\n\n/**\n * return a random float between min, max (exclusive)\n * @param min - minimum value.\n * @param max - maximum value.\n * @returns random value\n * @example\n * // Print a random number; one of 5, 6, 7, 8, 9\n * console.log(math.randomFloat(5, 10) );\n */\nexport function randomFloat(min: number, max: number) {\n\treturn Math.random() * (max - min) + min;\n}\n\n/**\n * return a weighted random between min, max (exclusive)\n * @param min - minimum value.\n * @param max - maximum value.\n * @returns random value\n * @example\n * // Print a random number; one of 5, 6, 7, 8, 9\n * console.log(math.weightedRandom(5, 10) );\n */\nexport function weightedRandom(min: number, max: number) {\n\treturn ~~(Math.pow(Math.random(), 2) * (max - min)) + min;\n}\n\n/**\n * round a value to the specified number of digit\n * @param num - value to be rounded.\n * @param [dec] - number of decimal digit to be rounded to.\n * @returns rounded value\n * @example\n * // round a specific value to 2 digits\n * math.round(10.33333, 2); // return 10.33\n */\nexport function round(num: number, dec = 0) {\n\t// if only one argument use the object value\n\tconst powres = Math.pow(10, dec);\n\treturn ~~(0.5 + num * powres) / powres;\n}\n\n/**\n * check if the given value is close to the expected one\n * @param expected - value to be compared with.\n * @param actual - actual value to compare\n * @param [precision] - float precision for the comparison\n * @returns if close to\n * @example\n * // test if the given value is close to 10\n * if (math.toBeCloseTo(10, value)) {\n *     // do something\n * }\n */\nexport function toBeCloseTo(expected: number, actual: number, precision = 2) {\n\treturn Math.abs(expected - actual) < Math.pow(10, -precision) / 2;\n}\n\n/**\n * Calculates the power of a number.\n * @param n - The number to be raised to the power of 2.\n * @returns The result of raising the number to the power of 2.\n */\nexport function pow(n: number) {\n\treturn Math.pow(n, 2);\n}\n\n/**\n * Linear interpolation between two scalar values.\n * `t = 0` returns `a`, `t = 1` returns `b`. Values of `t` outside\n * `[0, 1]` extrapolate — the result is NOT clamped.\n *\n * **Don't use this for smooth follow at a fixed alpha per frame.**\n * `x = lerp(x, target, 0.1)` per frame converges 2× faster at 60 fps\n * than at 30 fps — the visual feel changes with the frame rate. Use\n * {@link damp} instead for frame-rate-independent smoothing.\n * @param a - the value at `t = 0`\n * @param b - the value at `t = 1`\n * @param t - the interpolation parameter (typically `0..1`, but not clamped)\n * @returns `a + (b - a) * t`\n * @example\n * math.lerp(0, 10, 0.5);  // 5\n * math.lerp(0, 10, 1.5);  // 15 (extrapolation past `b`)\n */\nexport function lerp(a: number, b: number, t: number): number {\n\treturn a + (b - a) * t;\n}\n\n/**\n * Frame-rate-independent exponential damping toward a target.\n *\n * After total elapsed time `t_total` (in seconds), the result\n * satisfies\n * `result = current + (target - current) * (1 - exp(-lambda * t_total))`\n * regardless of how `t_total` was split across `dt` calls — the standard\n * frame-rate-independent exponential `damp` formulation.\n *\n * `lambda` is the decay rate in `1/seconds` — higher = snappier\n * convergence. A rule of thumb: `lambda = 5` reaches ≈ 99% of the\n * target in one second; `lambda = 10` reaches ≈ 99% in ~0.5 s.\n *\n * Use this — NOT `lerp(current, target, alpha)` per frame — whenever\n * the smoothing should feel the same regardless of frame rate\n * (camera follow, value tracking, input smoothing).\n * @param current - the current value (carries forward across frames)\n * @param target - the value to approach\n * @param lambda - decay rate in 1/seconds. Higher = snappier.\n * @param dt - delta time in **seconds** (note: melonJS engine `dt` is in ms; divide by 1000)\n * @returns the new value, one step closer to `target`\n * @example\n * // frame-rate independent smooth follow:\n * cameraX = math.damp(cameraX, target.x, 5, dt / 1000);\n */\nexport function damp(\n\tcurrent: number,\n\ttarget: number,\n\tlambda: number,\n\tdt: number,\n): number {\n\treturn current + (target - current) * (1 - Math.exp(-lambda * dt));\n}\n\n/**\n * Linearly interpolate a value from an array at the given position.\n * The array is treated as evenly spaced samples along the 0–1 range.\n * @param values - array of values to interpolate between\n * @param position - position along the array (0.0–1.0). Values above 1.0 clamp to the last element. Values below 0.0 extrapolate before the first element.\n * @returns interpolated value\n * @example\n * // interpolate halfway through a curve\n * math.lerpArray([0, 10, 20], 0.5); // returns 10\n * math.lerpArray([1, 0], 0.75);     // returns 0.25\n */\nexport function lerpArray(values: number[], position: number) {\n\tconst last = values.length - 1;\n\tif (last <= 0) return values[0];\n\tconst pos = position * last;\n\tconst idx = ~~pos;\n\t// clamp index to valid segment range without Math.min\n\tif (idx >= last) return values[last];\n\tconst frac = pos - idx;\n\treturn values[idx] + (values[idx + 1] - values[idx]) * frac;\n}\n","import { createPool } from \"../system/pool.ts\";\nimport { XYPoint } from \"../utils/types.ts\";\nimport { clamp, damp, lerp } from \"./math.ts\";\n\n/**\n * a generic 2D Vector Object\n * @category Math\n */\nexport class Vector2d {\n\tx: number;\n\ty: number;\n\n\t/**\n\t * @param [x] - x value of the vector\n\t * @param [y] - y value of the vector\n\t */\n\tconstructor(x: number | undefined = 0, y: number | undefined = 0) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\t/**\n\t * @param [x] - x value of the vector\n\t * @param [y] - y value of the vector\n\t * @ignore\n\t */\n\tonResetEvent(x = 0, y = 0) {\n\t\t/**\n\t\t * x value of the vector\n\t\t */\n\t\tthis.x = x;\n\t\t/**\n\t\t * y value of the vector\n\t\t */\n\t\tthis.y = y;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to the given values\n\t * @param x - x component of the vector\n\t * @param y - y component of the vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tset(x: number, y: number) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to 0\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetZero() {\n\t\treturn this.set(0, 0);\n\t}\n\n\t/**\n\t * set the Vector x and y properties using the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetV(v: Vector2d) {\n\t\treturn this.set(v.x, v.y);\n\t}\n\n\t/**\n\t * Add the passed vector to this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tadd(v: Vector2d) {\n\t\treturn this.set(this.x + v.x, this.y + v.y);\n\t}\n\n\t/**\n\t * Subtract the passed vector from this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsub(v: Vector2d) {\n\t\treturn this.set(this.x - v.x, this.y - v.y);\n\t}\n\n\t/**\n\t * Multiply this vector values by the given scalar\n\t * @param x x scale value\n\t * @param [y] y scale value, if not passed, it uses the x value\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x) {\n\t\treturn this.set(this.x * x, this.y * y);\n\t}\n\n\t/**\n\t * Convert this vector into isometric coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\ttoIso() {\n\t\treturn this.set(this.x - this.y, (this.x + this.y) * 0.5);\n\t}\n\n\t/**\n\t * Convert this vector into 2d coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\tto2d() {\n\t\treturn this.set(this.y + this.x / 2, this.y - this.x / 2);\n\t}\n\n\t/**\n\t * Multiply this vector values by the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector2d) {\n\t\treturn this.set(this.x * v.x, this.y * v.y);\n\t}\n\n\t/**\n\t * Divide this vector values by the passed value\n\t * @param n - the value to divide the vector by\n\t * @returns Reference to this object for method chaining\n\t */\n\tdiv(n: number) {\n\t\treturn this.set(this.x / n, this.y / n);\n\t}\n\n\t/**\n\t * Update this vector values to absolute values\n\t * @returns Reference to this object for method chaining\n\t */\n\tabs() {\n\t\treturn this.set(\n\t\t\tthis.x < 0 ? -this.x : this.x,\n\t\t\tthis.y < 0 ? -this.y : this.y,\n\t\t);\n\t}\n\n\t/**\n\t * Clamp the vector value within the specified value range\n\t * @param low minimum component value\n\t * @param high maximum component value\n\t * @returns new me.Vector2d\n\t */\n\tclamp(low: number, high: number) {\n\t\treturn new Vector2d(clamp(this.x, low, high), clamp(this.y, low, high));\n\t}\n\n\t/**\n\t * Clamp this vector value within the specified value range\n\t * @param low minimum component value\n\t * @param high maximum component value\n\t * @returns Reference to this object for method chaining\n\t */\n\tclampSelf(low: number, high: number) {\n\t\treturn this.set(clamp(this.x, low, high), clamp(this.y, low, high));\n\t}\n\n\t/**\n\t * Update this vector with the minimum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tminV(v: Vector2d) {\n\t\treturn this.set(this.x < v.x ? this.x : v.x, this.y < v.y ? this.y : v.y);\n\t}\n\n\t/**\n\t * Update this vector with the maximum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tmaxV(v: Vector2d) {\n\t\treturn this.set(this.x > v.x ? this.x : v.x, this.y > v.y ? this.y : v.y);\n\t}\n\n\t/**\n\t * Floor the vector values\n\t * @returns new Vector2d\n\t */\n\tfloor() {\n\t\treturn new Vector2d(Math.floor(this.x), Math.floor(this.y));\n\t}\n\n\t/**\n\t * Floor this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tfloorSelf() {\n\t\treturn this.set(Math.floor(this.x), Math.floor(this.y));\n\t}\n\n\t/**\n\t * Ceil the vector values\n\t * @returns new Vector2d\n\t */\n\tceil() {\n\t\treturn new Vector2d(Math.ceil(this.x), Math.ceil(this.y));\n\t}\n\n\t/**\n\t * Ceil this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tceilSelf() {\n\t\treturn this.set(Math.ceil(this.x), Math.ceil(this.y));\n\t}\n\n\t/**\n\t * Negate the vector values\n\t * @returns new Vector2d\n\t */\n\tnegate() {\n\t\treturn new Vector2d(-this.x, -this.y);\n\t}\n\n\t/**\n\t * Negate this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tnegateSelf() {\n\t\treturn this.set(-this.x, -this.y);\n\t}\n\n\t/**\n\t * Copy the x,y values of the passed vector to this one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(v: Vector2d) {\n\t\treturn this.set(v.x, v.y);\n\t}\n\n\t/**\n\t * Checks if this vector is equal to another vector or a pair of coordinates.\n\t * @param args - Either two numbers representing x and y coordinates or a single Vector2d object.\n\t * @returns True if the coordinates are equal, false otherwise.\n\t * @example\n\t * let v1 = new Vector2d(3, 4);\n\t * let v2 = new Vector2d(3, 4);\n\t * v1.equals(v2); // returns true\n\t * @example\n\t * let v = new Vector2d(3, 4);\n\t * v.equals(3, 4); // returns true\n\t */\n\tequals(...args: [number, number] | [Vector2d | XYPoint]) {\n\t\tif (args.length === 2) {\n\t\t\treturn this.x === args[0] && this.y === args[1];\n\t\t}\n\t\treturn this.x === args[0].x && this.y === args[0].y;\n\t}\n\n\t/**\n\t * normalize this vector (scale the vector so that its magnitude is 1)\n\t * @returns Reference to this object for method chaining\n\t */\n\tnormalize() {\n\t\treturn this.div(this.length() || 1);\n\t}\n\n\t/**\n\t * change this vector to be perpendicular to what it was before.<br>\n\t * (Effectively rotates it 90 degrees in a clockwise direction)\n\t * @returns Reference to this object for method chaining\n\t */\n\tperp() {\n\t\treturn this.set(this.y, -this.x);\n\t}\n\n\t/**\n\t * Rotate this vector (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: XYPoint) {\n\t\tlet cx = 0;\n\t\tlet cy = 0;\n\n\t\tif (v) {\n\t\t\tcx = v.x;\n\t\t\tcy = v.y;\n\t\t}\n\n\t\tconst x = this.x - cx;\n\t\tconst y = this.y - cy;\n\n\t\tconst c = Math.cos(angle);\n\t\tconst s = Math.sin(angle);\n\n\t\treturn this.set(x * c - y * s + cx, x * s + y * c + cy);\n\t}\n\n\t/**\n\t * return the dot product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The dot product.\n\t */\n\tdot(v: Vector2d) {\n\t\treturn this.x * v.x + this.y * v.y;\n\t}\n\n\t/**\n\t * return the cross product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The cross product.\n\t */\n\tcross(v: Vector2d) {\n\t\treturn this.x * v.y - this.y * v.x;\n\t}\n\n\t/**\n\t * return the square length of this vector\n\t * @returns The length^2 of this vector.\n\t */\n\tlength2() {\n\t\treturn this.dot(this);\n\t}\n\n\t/**\n\t * return the length (magnitude) of this vector\n\t * @returns the length of this vector\n\t */\n\tlength() {\n\t\treturn Math.sqrt(this.length2());\n\t}\n\n\t/**\n\t * Linearly interpolate between this vector and the given one.\n\t * Frame-rate dependent at a fixed alpha — for smooth tracking\n\t * use {@link Vector2d.damp} instead.\n\t * @param v - other vector\n\t * @param alpha - distance along the line (alpha = 0 → this vector, alpha = 1 → `v`)\n\t * @returns Reference to this object for method chaining\n\t */\n\tlerp(v: Vector2d, alpha: number) {\n\t\tthis.x = lerp(this.x, v.x, alpha);\n\t\tthis.y = lerp(this.y, v.y, alpha);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Frame-rate independent exponential damping toward `target`.\n\t * Wrapper around {@link damp} applied per component.\n\t * @param target - vector to approach\n\t * @param lambda - decay rate in `1/seconds` (higher = snappier)\n\t * @param dt - delta time in **seconds**\n\t * @returns Reference to this object for method chaining\n\t * @example\n\t * // smooth-follow a target point at a frame-rate independent rate:\n\t * this.pos.damp(target.pos, 5, dt / 1000);\n\t */\n\tdamp(target: Vector2d, lambda: number, dt: number) {\n\t\tthis.x = damp(this.x, target.x, lambda, dt);\n\t\tthis.y = damp(this.y, target.y, lambda, dt);\n\t\treturn this;\n\t}\n\n\t/**\n\t * interpolate the position of this vector towards the given one by the given maximum step.\n\t * @param target vector to rotate towards\n\t * @param step - the maximum step per iteration (Negative values will push the vector away from the target)\n\t * @returns Reference to this object for method chaining\n\t */\n\tmoveTowards(target: Vector2d, step: number) {\n\t\tconst distance = this.distance(target);\n\n\t\t// within one step (and not fleeing): land exactly on the target —\n\t\t// and always mutate/return `this`, never the caller's target\n\t\tif (distance === 0 || (step >= 0 && distance <= step)) {\n\t\t\tthis.x = target.x;\n\t\t\tthis.y = target.y;\n\t\t\treturn this;\n\t\t}\n\n\t\tconst angle = Math.atan2(target.y - this.y, target.x - this.x);\n\t\tthis.x += Math.cos(angle) * step;\n\t\tthis.y += Math.sin(angle) * step;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calculates the Euclidean distance between this vector and another vector.\n\t * @param v - The vector to which the distance is calculated.\n\t * @returns The Euclidean distance between this vector and the given vector.\n\t * @example\n\t * let v1 = new Vector2d(3, 4);\n\t * let v2 = new Vector2d(6, 8);\n\t * v1.distance(v2); // returns 5\n\t */\n\tdistance(v: Vector2d) {\n\t\tconst dx = this.x - v.x;\n\t\tconst dy = this.y - v.y;\n\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t}\n\n\t/**\n\t * return the angle between this vector and the passed one\n\t * @param v other vector\n\t * @returns angle in radians\n\t */\n\tangle(v: Vector2d) {\n\t\treturn Math.acos(clamp(this.dot(v) / (this.length() * v.length()), -1, 1));\n\t}\n\n\t/**\n\t * project this vector on to another vector.\n\t * @param v - The vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tproject(v: Vector2d) {\n\t\treturn this.scale(this.dot(v) / v.length2());\n\t}\n\n\t/**\n\t * Project this vector onto a vector of unit length.<br>\n\t * This is slightly more efficient than `project` when dealing with unit vectors.\n\t * @param v - The unit vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tprojectN(v: Vector2d) {\n\t\treturn this.scale(this.dot(v));\n\t}\n\n\t/**\n\t * return a clone copy of this vector\n\t * @returns new Vector2d\n\t */\n\tclone() {\n\t\treturn vector2dPool.get(this.x, this.y);\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns stringified representation of this vector\n\t */\n\ttoString() {\n\t\treturn `x:${this.x},y:${this.y}` as const;\n\t}\n}\n\nexport const vector2dPool = createPool<\n\tVector2d,\n\t[x?: number | undefined, y?: number | undefined]\n>((x, y) => {\n\tconst vector = new Vector2d(x, y);\n\n\treturn {\n\t\tinstance: vector,\n\t\treset(x = 0, y = 0) {\n\t\t\tvector.x = x;\n\t\t\tvector.y = y;\n\t\t},\n\t};\n});\n","import { createPool } from \"../system/pool\";\n\n/**\n * represents a point in a 2d space\n */\nexport class Point {\n\t/**\n\t * the position of the point on the horizontal axis\n\t * @default 0\n\t */\n\tx: number;\n\n\t/**\n\t * the position of the point on the vertical axis\n\t * @default 0\n\t */\n\ty: number;\n\n\t/**\n\t * the shape type (used internally)\n\t * @default \"Point\"\n\t */\n\ttype: \"Point\";\n\n\tconstructor(x?: number, y?: number) {\n\t\tthis.x = x ?? 0;\n\t\tthis.y = y ?? 0;\n\t\tthis.type = \"Point\";\n\t}\n\n\t/**\n\t * set the Point x and y properties to the given values\n\t * @param x horizontal coordinate\n\t * @param y vertical coordinate\n\t * @returns Reference to this object for method chaining\n\t */\n\tset(x = 0, y = 0) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\treturn this;\n\t}\n\n\t/**\n\t * return true if this point is equal to the given point\n\t * @param x horizontal coordinate\n\t * @param [y] vertical coordinate\n\t * @param args\n\t * @returns\n\t */\n\tequals(x: number, y: number): boolean;\n\tequals(point: Point): boolean;\n\tequals(xOrPoint: number | Point, y?: number) {\n\t\tif (typeof xOrPoint === \"object\") {\n\t\t\treturn this.x === xOrPoint.x && this.y === xOrPoint.y;\n\t\t}\n\t\treturn this.x === xOrPoint && this.y === y;\n\t}\n\n\t/**\n\t * clone this Point\n\t * @returns new Point\n\t */\n\tclone() {\n\t\treturn new Point(this.x, this.y);\n\t}\n}\n\nexport const pointPool = createPool<\n\tPoint,\n\t[x?: number | undefined, y?: number | undefined]\n>((x, y) => {\n\tconst instance = new Point(x, y);\n\n\treturn {\n\t\tinstance: instance,\n\t\treset(x = 0, y = 0) {\n\t\t\tinstance.x = x;\n\t\t\tinstance.y = y;\n\t\t},\n\t};\n});\n","import { Point } from \"../geometries/point.ts\";\nimport { Matrix2d } from \"../math/matrix2d.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { XYPoint } from \"../utils/types.ts\";\n\n// Module-level scratch reused by `Bounds.addFrame` for the matrix\n// path. Avoids a `pointPool.get`/`release` round-trip on every call —\n// `addFrame` is hit once per renderable per frame, so the round-trip\n// adds up. Safe to share across instances: JS is single-threaded and\n// `addPoint` cannot reenter `addFrame`.\nconst _addFrameScratch = new Point();\n\n/**\n * a bound object contains methods for creating and manipulating axis-aligned bounding boxes (AABB).\n * @category Geometry\n */\nexport class Bounds {\n\t_center: Vector2d;\n\ttype: string;\n\tmin: XYPoint;\n\tmax: XYPoint;\n\n\t/**\n\t * Constructs a Bounds object with optional vertices.\n\t * @param [vertices] - An array of Vector2d or Point to initialize the bounds.\n\t */\n\tconstructor(vertices?: Vector2d[] | Point[] | XYPoint[]) {\n\t\tthis._center = new Vector2d();\n\t\tthis.type = \"Bounds\";\n\t\tthis.min = { x: Infinity, y: Infinity };\n\t\tthis.max = { x: -Infinity, y: -Infinity };\n\t\tif (vertices) {\n\t\t\tthis.update(vertices);\n\t\t}\n\t}\n\n\t/**\n\t * Resets the bounds to its initial state.\n\t */\n\tclear() {\n\t\tthis.setMinMax(Infinity, Infinity, -Infinity, -Infinity);\n\t}\n\n\t/**\n\t * Sets the bounds to the given minimum and maximum values.\n\t * @param minX - The minimum x value.\n\t * @param minY - The minimum y value.\n\t * @param maxX - The maximum x value.\n\t * @param maxY - The maximum y value.\n\t */\n\tsetMinMax(minX: number, minY: number, maxX: number, maxY: number) {\n\t\tthis.min.x = minX;\n\t\tthis.min.y = minY;\n\n\t\tthis.max.x = maxX;\n\t\tthis.max.y = maxY;\n\t}\n\n\t/**\n\t * Gets the x position of the bounds.\n\t * @returns The x position.\n\t */\n\tget x() {\n\t\treturn this.min.x;\n\t}\n\n\t/**\n\t * Sets the x position of the bounds.\n\t * @param value - The new x position.\n\t */\n\tset x(value) {\n\t\tconst deltaX = this.max.x - this.min.x;\n\t\tthis.min.x = value;\n\t\tthis.max.x = value + deltaX;\n\t}\n\n\t/**\n\t * Gets the y position of the bounds.\n\t * @returns The y position.\n\t */\n\tget y() {\n\t\treturn this.min.y;\n\t}\n\n\t/**\n\t * Sets the y position of the bounds.\n\t * @param value - The new y position.\n\t */\n\tset y(value) {\n\t\tconst deltaY = this.max.y - this.min.y;\n\n\t\tthis.min.y = value;\n\t\tthis.max.y = value + deltaY;\n\t}\n\n\t/**\n\t * Gets the width of the bounds.\n\t * @returns The width.\n\t */\n\tget width() {\n\t\treturn this.max.x - this.min.x;\n\t}\n\n\t/**\n\t * Sets the width of the bounds.\n\t * @param value - The new width.\n\t */\n\tset width(value) {\n\t\tthis.max.x = this.min.x + value;\n\t}\n\n\t/**\n\t * Gets the height of the bounds.\n\t * @returns The height.\n\t */\n\tget height() {\n\t\treturn this.max.y - this.min.y;\n\t}\n\n\t/**\n\t * Sets the height of the bounds.\n\t * @param value - The new height.\n\t */\n\tset height(value) {\n\t\tthis.max.y = this.min.y + value;\n\t}\n\n\t/**\n\t * Gets the left coordinate of the bounds.\n\t * @returns The left coordinate.\n\t */\n\tget left() {\n\t\treturn this.min.x;\n\t}\n\n\t/**\n\t * Gets the right coordinate of the bounds.\n\t * @returns The right coordinate.\n\t */\n\tget right() {\n\t\treturn this.max.x;\n\t}\n\n\t/**\n\t * Gets the top coordinate of the bounds.\n\t * @returns The top coordinate.\n\t */\n\tget top() {\n\t\treturn this.min.y;\n\t}\n\n\t/**\n\t * Gets the bottom coordinate of the bounds.\n\t * @returns The bottom coordinate.\n\t */\n\tget bottom() {\n\t\treturn this.max.y;\n\t}\n\n\t/**\n\t * Gets the center position of the bounds on the x axis.\n\t * @returns The center x coordinate.\n\t */\n\tget centerX() {\n\t\treturn this.min.x + this.width / 2;\n\t}\n\n\t/**\n\t * Gets the center position of the bounds on the y axis.\n\t * @returns The center y coordinate.\n\t */\n\tget centerY() {\n\t\treturn this.min.y + this.height / 2;\n\t}\n\n\t/**\n\t * Gets the center position of the bounds.\n\t * @returns The center position as a Vector2d.\n\t */\n\tget center() {\n\t\treturn this._center.set(this.centerX, this.centerY);\n\t}\n\n\t/**\n\t * Centers the bounds position around the given coordinates.\n\t * @param x - The x coordinate to center around.\n\t * @param y - The y coordinate to center around.\n\t * @returns The current Bounds instance for method chaining.\n\t */\n\tcenterOn(x: number, y: number) {\n\t\tthis.shift(x - this.width / 2, y - this.height / 2);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Updates the bounds using the given vertices.\n\t * @param vertices - An array of Vector2d or Point to update the bounds.\n\t */\n\tupdate(vertices: Vector2d[] | Point[] | XYPoint[]) {\n\t\tthis.add(vertices, true);\n\t}\n\n\t/**\n\t * Adds the given vertices to the bounds definition.\n\t * @param vertices - An array of Vector2d or Point to add to the bounds.\n\t * @param [clear] - Whether to reset the bounds before adding the new vertices. Defaults to false.\n\t */\n\tadd(vertices: Vector2d[] | Point[] | XYPoint[], clear = false) {\n\t\tif (clear) {\n\t\t\tthis.clear();\n\t\t}\n\t\tfor (const vertex of vertices) {\n\t\t\tif (vertex.x > this.max.x) {\n\t\t\t\tthis.max.x = vertex.x;\n\t\t\t}\n\t\t\tif (vertex.x < this.min.x) {\n\t\t\t\tthis.min.x = vertex.x;\n\t\t\t}\n\t\t\tif (vertex.y > this.max.y) {\n\t\t\t\tthis.max.y = vertex.y;\n\t\t\t}\n\t\t\tif (vertex.y < this.min.y) {\n\t\t\t\tthis.min.y = vertex.y;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Adds the given bounds to the bounds definition.\n\t * @param bounds - The bounds to add.\n\t * @param [clear] - Whether to reset the bounds before adding the new bounds.\n\t */\n\taddBounds(bounds: Bounds, clear = false) {\n\t\tif (clear) {\n\t\t\tthis.max.x = bounds.max.x;\n\t\t\tthis.min.x = bounds.min.x;\n\t\t\tthis.max.y = bounds.max.y;\n\t\t\tthis.min.y = bounds.min.y;\n\t\t} else {\n\t\t\tif (bounds.max.x > this.max.x) {\n\t\t\t\tthis.max.x = bounds.max.x;\n\t\t\t}\n\t\t\tif (bounds.min.x < this.min.x) {\n\t\t\t\tthis.min.x = bounds.min.x;\n\t\t\t}\n\t\t\tif (bounds.max.y > this.max.y) {\n\t\t\t\tthis.max.y = bounds.max.y;\n\t\t\t}\n\t\t\tif (bounds.min.y < this.min.y) {\n\t\t\t\tthis.min.y = bounds.min.y;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Expand this bounds to include every shape in `shapes`. Each shape\n\t * contributes its own `.getBounds()` (so `Rect`, `Polygon`, `Ellipse`,\n\t * `Bounds`, and anything else implementing the same getter all work);\n\t * shapes without `.getBounds()` are silently ignored. Use `clear=true`\n\t * to compute a fresh union (matches the `add` / `addBounds` shape).\n\t *\n\t * Useful for sizing a renderable from its `bodyDef.shapes` before\n\t * the underlying physics body has been constructed — used by the\n\t * TMX shape factory and {@link Trigger}.\n\t * @param shapes - one shape or an iterable of shapes\n\t * @param [clear] - reset the bounds before unioning\n\t */\n\taddShapes(\n\t\tshapes:\n\t\t\t| { getBounds?: () => Bounds }\n\t\t\t| Iterable<{ getBounds?: () => Bounds }>,\n\t\tclear = false,\n\t) {\n\t\tif (clear) {\n\t\t\tthis.clear();\n\t\t}\n\t\tconst iterable: Iterable<{ getBounds?: () => Bounds }> =\n\t\t\ttypeof (shapes as { [Symbol.iterator]?: unknown })[Symbol.iterator] ===\n\t\t\t\"function\"\n\t\t\t\t? (shapes as Iterable<{ getBounds?: () => Bounds }>)\n\t\t\t\t: [shapes as { getBounds?: () => Bounds }];\n\t\tfor (const s of iterable) {\n\t\t\tif (typeof s?.getBounds === \"function\") {\n\t\t\t\tthis.addBounds(s.getBounds());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Adds the given point to the bounds definition.\n\t * @param point - The point to add to the bounds.\n\t * @param [m] - An optional transform to apply to the given point.\n\t */\n\taddPoint(point: Vector2d | Point, m?: Matrix2d | Matrix3d) {\n\t\tconst p = m ? m.apply(point) : point;\n\t\tthis.min.x = Math.min(this.min.x, p.x);\n\t\tthis.max.x = Math.max(this.max.x, p.x);\n\t\tthis.min.y = Math.min(this.min.y, p.y);\n\t\tthis.max.y = Math.max(this.max.y, p.y);\n\t}\n\n\t/**\n\t * Expands this bounds to include the axis-aligned bounding box of\n\t * the given rect's four corners, optionally transformed through `m`\n\t * first. With a non-identity `m` (rotation, scale, etc.) the result\n\t * is the AABB of the transformed quad, not a transformed AABB.\n\t * @param x0 - The left x coordinate of the quad.\n\t * @param y0 - The top y coordinate of the quad.\n\t * @param x1 - The right x coordinate of the quad.\n\t * @param y1 - The bottom y coordinate of the quad.\n\t * @param [m] - An optional transform applied to each corner before inclusion.\n\t */\n\taddFrame(\n\t\tx0: number,\n\t\ty0: number,\n\t\tx1: number,\n\t\ty1: number,\n\t\tm?: Matrix2d | Matrix3d,\n\t) {\n\t\tif (m === undefined || m.isIdentity()) {\n\t\t\t// no transform (or a no-op transform): fold the 4 corners'\n\t\t\t// min/max directly into the AABB without any Point or\n\t\t\t// `m.apply` work. Mirrors `addPoint` for each corner —\n\t\t\t// using Math.min/max so the caller may pass swapped corners\n\t\t\t// (x1 < x0 etc.) without breaking.\n\t\t\tconst minX = Math.min(x0, x1);\n\t\t\tconst maxX = Math.max(x0, x1);\n\t\t\tconst minY = Math.min(y0, y1);\n\t\t\tconst maxY = Math.max(y0, y1);\n\t\t\tif (minX < this.min.x) {\n\t\t\t\tthis.min.x = minX;\n\t\t\t}\n\t\t\tif (maxX > this.max.x) {\n\t\t\t\tthis.max.x = maxX;\n\t\t\t}\n\t\t\tif (minY < this.min.y) {\n\t\t\t\tthis.min.y = minY;\n\t\t\t}\n\t\t\tif (maxY > this.max.y) {\n\t\t\t\tthis.max.y = maxY;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// transformed: walk the 4 corners through `m` via `addPoint`.\n\t\t// Reuse the module-level scratch — `m.apply` mutates in place\n\t\t// and `addPoint` reads x/y immediately after, so the same Point\n\t\t// can shuttle through all four corners.\n\t\tconst v = _addFrameScratch;\n\t\tthis.addPoint(v.set(x0, y0), m);\n\t\tthis.addPoint(v.set(x1, y0), m);\n\t\tthis.addPoint(v.set(x0, y1), m);\n\t\tthis.addPoint(v.set(x1, y1), m);\n\t}\n\n\t/**\n\t * Returns true if the bounds contain the given point or vector.\n\t * @param xOrVectorOrBounds - The x coordinate, vector, or bounds to check.\n\t * @param [y] - The y coordinate if the first parameter is a number.\n\t * @returns True if the bounds contain the point or vector, otherwise false.\n\t */\n\tcontains(x: number, y: number): boolean;\n\tcontains(vector: Vector2d | Bounds): boolean;\n\tcontains(xOrVectorOrBounds: Bounds | Vector2d | number, y?: number) {\n\t\tlet _x1: number;\n\t\tlet _x2: number;\n\t\tlet _y1: number;\n\t\tlet _y2: number;\n\t\tif (xOrVectorOrBounds instanceof Vector2d) {\n\t\t\t_x1 = _x2 = xOrVectorOrBounds.x;\n\t\t\t_y1 = _y2 = xOrVectorOrBounds.y;\n\t\t} else if (xOrVectorOrBounds instanceof Bounds) {\n\t\t\t_x1 = xOrVectorOrBounds.min.x;\n\t\t\t_x2 = xOrVectorOrBounds.max.x;\n\t\t\t_y1 = xOrVectorOrBounds.min.y;\n\t\t\t_y2 = xOrVectorOrBounds.max.y;\n\t\t} else {\n\t\t\t_x1 = _x2 = xOrVectorOrBounds;\n\t\t\t_y1 = _y2 = y!;\n\t\t}\n\n\t\treturn (\n\t\t\t_x1 >= this.min.x &&\n\t\t\t_x2 <= this.max.x &&\n\t\t\t_y1 >= this.min.y &&\n\t\t\t_y2 <= this.max.y\n\t\t);\n\t}\n\n\t/**\n\t * Returns true if the two bounds intersect.\n\t * @param bounds - The bounds to check for intersection.\n\t * @returns True if the bounds overlap, otherwise false.\n\t */\n\toverlaps(bounds: Bounds) {\n\t\treturn !(\n\t\t\tthis.right < bounds.left ||\n\t\t\tthis.left > bounds.right ||\n\t\t\tthis.bottom < bounds.top ||\n\t\t\tthis.top > bounds.bottom\n\t\t);\n\t}\n\n\t/**\n\t * Determines whether all coordinates of this bounds are finite numbers.\n\t * @returns False if any coordinates are positive or negative Infinity or NaN; otherwise, true.\n\t */\n\tisFinite() {\n\t\treturn (\n\t\t\tisFinite(this.min.x) &&\n\t\t\tisFinite(this.max.x) &&\n\t\t\tisFinite(this.min.y) &&\n\t\t\tisFinite(this.max.y)\n\t\t);\n\t}\n\n\t/**\n\t * Translates the bounds by the given point or vector.\n\t * @param xOrVector - The x coordinate or vector to translate by.\n\t * @param [y] - The y coordinate if the first parameter is a number.\n\t */\n\ttranslate(x: number, y: number): void;\n\ttranslate(vector: Vector2d): void;\n\ttranslate(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\t\tthis.min.x += _x;\n\t\tthis.max.x += _x;\n\t\tthis.min.y += _y;\n\t\tthis.max.y += _y;\n\t}\n\n\t/**\n\t * Shifts the bounds to the given x, y position or vector.\n\t * @param xOrVector - The x coordinate or vector to shift to.\n\t * @param [y] - The y coordinate if the first parameter is a number.\n\t */\n\tshift(x: number, y: number): void;\n\tshift(vector: Vector2d): void;\n\tshift(xOrVector: number | Vector2d, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\tconst deltaX = this.max.x - this.min.x;\n\t\tconst deltaY = this.max.y - this.min.y;\n\n\t\tthis.min.x = _x;\n\t\tthis.max.x = _x + deltaX;\n\t\tthis.min.y = _y;\n\t\tthis.max.y = _y + deltaY;\n\t}\n\n\t/**\n\t * Creates a clone of this bounds.\n\t * @returns A new Bounds object that is a copy of this bounds.\n\t */\n\tclone() {\n\t\tconst bounds = new Bounds();\n\t\tbounds.addBounds(this);\n\t\treturn bounds;\n\t}\n}\n\n/**\n * A pool for creating and reusing Bounds objects.\n */\nexport const boundsPool = createPool<\n\tBounds,\n\t[vertices?: Vector2d[] | Point[] | undefined]\n>((vertices) => {\n\tconst instance = new Bounds(vertices);\n\treturn {\n\t\tinstance,\n\t\treset(vertices) {\n\t\t\tinstance.clear();\n\t\t\tif (vertices) {\n\t\t\t\tinstance.update(vertices);\n\t\t\t}\n\t\t},\n\t};\n});\n","export function earcut(\n\tdata: number[],\n\tholeIndices?: number[] | null,\n\tdim: number | undefined = 2,\n): number[] {\n\tconst hasHoles = holeIndices && holeIndices.length;\n\tconst outerLen = hasHoles ? holeIndices[0] * dim : data.length;\n\tlet outerNode = linkedList(data, 0, outerLen, dim, true);\n\tconst triangles: number[] = [];\n\n\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive guard from earcut algorithm\n\tif (!outerNode || outerNode.next === outerNode.prev) return triangles;\n\n\tlet minX, minY, invSize;\n\n\tif (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);\n\n\t// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox\n\tif (data.length > 80 * dim) {\n\t\tminX = Infinity;\n\t\tminY = Infinity;\n\t\tlet maxX = -Infinity;\n\t\tlet maxY = -Infinity;\n\n\t\tfor (let i = dim; i < outerLen; i += dim) {\n\t\t\tconst x = data[i];\n\t\t\tconst y = data[i + 1];\n\t\t\tif (x < minX) minX = x;\n\t\t\tif (y < minY) minY = y;\n\t\t\tif (x > maxX) maxX = x;\n\t\t\tif (y > maxY) maxY = y;\n\t\t}\n\n\t\t// minX, minY and invSize are later used to transform coords into integers for z-order calculation\n\t\tinvSize = Math.max(maxX - minX, maxY - minY);\n\t\tinvSize = invSize !== 0 ? 32767 / invSize : 0;\n\t}\n\n\tearcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);\n\n\treturn triangles;\n}\n\n// create a circular doubly linked list from polygon points in the specified winding order\nfunction linkedList(\n\tdata: number[],\n\tstart: number,\n\tend: number,\n\tdim: number,\n\tclockwise: boolean,\n) {\n\tlet last: Node | null = null;\n\n\tif (clockwise === signedArea(data, start, end, dim) > 0) {\n\t\tfor (let i = start; i < end; i += dim) {\n\t\t\tlast = insertNode((i / dim) | 0, data[i], data[i + 1], last);\n\t\t}\n\t} else {\n\t\tfor (let i = end - dim; i >= start; i -= dim) {\n\t\t\tlast = insertNode((i / dim) | 0, data[i], data[i + 1], last);\n\t\t}\n\t}\n\n\tif (last && equals(last, last.next)) {\n\t\tremoveNode(last);\n\t\tlast = last.next;\n\t}\n\n\treturn last!;\n}\n\n// eliminate colinear or duplicate points\nfunction filterPoints(start: null, end?: Node | null): null;\nfunction filterPoints(start: Node, end?: Node | null): Node;\nfunction filterPoints(start: Node | null, end?: Node | null) {\n\tif (!start) return start;\n\tif (!end) end = start;\n\n\tlet p = start,\n\t\tagain;\n\tdo {\n\t\tagain = false;\n\n\t\tif (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {\n\t\t\tremoveNode(p);\n\t\t\tp = end = p.prev;\n\t\t\tif (p === p.next) break;\n\t\t\tagain = true;\n\t\t} else {\n\t\t\tp = p.next;\n\t\t}\n\t} while (again || p !== end);\n\n\treturn end;\n}\n\n// main ear slicing loop which triangulates a polygon (given as a linked list)\nfunction earcutLinked(\n\tear: Node,\n\ttriangles: number[],\n\tdim: number,\n\tminX: number | undefined,\n\tminY: number | undefined,\n\tinvSize: number | undefined,\n\tpass: number,\n) {\n\t// interlink polygon nodes in z-order\n\tif (!pass && invSize) indexCurve(ear, minX!, minY!, invSize);\n\n\tlet stop = ear;\n\n\t// iterate through ears, slicing them one by one\n\twhile (ear.prev !== ear.next) {\n\t\tconst prev = ear.prev;\n\t\tconst next = ear.next;\n\n\t\tif (invSize ? isEarHashed(ear, minX!, minY!, invSize) : isEar(ear)) {\n\t\t\ttriangles.push(prev.i, ear.i, next.i); // cut off the triangle\n\n\t\t\tremoveNode(ear);\n\n\t\t\t// skipping the next vertex leads to less sliver triangles\n\t\t\tear = next.next;\n\t\t\tstop = next.next;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tear = next;\n\n\t\t// if we looped through the whole remaining polygon and can't find any more ears\n\t\tif (ear === stop) {\n\t\t\t// try filtering points and slicing again\n\t\t\tif (!pass) {\n\t\t\t\tearcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);\n\n\t\t\t\t// if this didn't work, try curing all small self-intersections locally\n\t\t\t} else if (pass === 1) {\n\t\t\t\tear = cureLocalIntersections(filterPoints(ear), triangles);\n\t\t\t\tearcutLinked(ear, triangles, dim, minX, minY, invSize, 2);\n\n\t\t\t\t// as a last resort, try splitting the remaining polygon into two\n\t\t\t} else if (pass === 2) {\n\t\t\t\tsplitEarcut(ear, triangles, dim, minX, minY, invSize);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n// check whether a polygon node forms a valid ear with adjacent nodes\nfunction isEar(ear: Node) {\n\tconst a = ear.prev,\n\t\tb = ear,\n\t\tc = ear.next;\n\n\tif (area(a, b, c) >= 0) return false; // reflex, can't be an ear\n\n\t// now make sure we don't have other points inside the potential ear\n\tconst ax = a.x,\n\t\tbx = b.x,\n\t\tcx = c.x,\n\t\tay = a.y,\n\t\tby = b.y,\n\t\tcy = c.y;\n\n\t// triangle bbox; min & max are calculated like this for speed\n\tconst x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx,\n\t\ty0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy,\n\t\tx1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx,\n\t\ty1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy;\n\n\tlet p = c.next;\n\twhile (p !== a) {\n\t\tif (\n\t\t\tp.x >= x0 &&\n\t\t\tp.x <= x1 &&\n\t\t\tp.y >= y0 &&\n\t\t\tp.y <= y1 &&\n\t\t\tpointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&\n\t\t\tarea(p.prev, p, p.next) >= 0\n\t\t)\n\t\t\treturn false;\n\t\tp = p.next;\n\t}\n\n\treturn true;\n}\n\nfunction isEarHashed(ear: Node, minX: number, minY: number, invSize: number) {\n\tconst a = ear.prev,\n\t\tb = ear,\n\t\tc = ear.next;\n\n\tif (area(a, b, c) >= 0) return false; // reflex, can't be an ear\n\n\tconst ax = a.x,\n\t\tbx = b.x,\n\t\tcx = c.x,\n\t\tay = a.y,\n\t\tby = b.y,\n\t\tcy = c.y;\n\n\t// triangle bbox; min & max are calculated like this for speed\n\tconst x0 = ax < bx ? (ax < cx ? ax : cx) : bx < cx ? bx : cx,\n\t\ty0 = ay < by ? (ay < cy ? ay : cy) : by < cy ? by : cy,\n\t\tx1 = ax > bx ? (ax > cx ? ax : cx) : bx > cx ? bx : cx,\n\t\ty1 = ay > by ? (ay > cy ? ay : cy) : by > cy ? by : cy;\n\n\t// z-order range for the current triangle bbox;\n\tconst minZ = zOrder(x0, y0, minX, minY, invSize),\n\t\tmaxZ = zOrder(x1, y1, minX, minY, invSize);\n\n\tlet p = ear.prevZ,\n\t\tn = ear.nextZ;\n\n\t// look for points inside the triangle in both directions\n\twhile (p && p.z >= minZ && n && n.z <= maxZ) {\n\t\tif (\n\t\t\tp.x >= x0 &&\n\t\t\tp.x <= x1 &&\n\t\t\tp.y >= y0 &&\n\t\t\tp.y <= y1 &&\n\t\t\tp !== a &&\n\t\t\tp !== c &&\n\t\t\tpointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&\n\t\t\tarea(p.prev, p, p.next) >= 0\n\t\t)\n\t\t\treturn false;\n\t\tp = p.prevZ;\n\n\t\tif (\n\t\t\tn.x >= x0 &&\n\t\t\tn.x <= x1 &&\n\t\t\tn.y >= y0 &&\n\t\t\tn.y <= y1 &&\n\t\t\tn !== a &&\n\t\t\tn !== c &&\n\t\t\tpointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) &&\n\t\t\tarea(n.prev, n, n.next) >= 0\n\t\t)\n\t\t\treturn false;\n\t\tn = n.nextZ;\n\t}\n\n\t// look for remaining points in decreasing z-order\n\twhile (p && p.z >= minZ) {\n\t\tif (\n\t\t\tp.x >= x0 &&\n\t\t\tp.x <= x1 &&\n\t\t\tp.y >= y0 &&\n\t\t\tp.y <= y1 &&\n\t\t\tp !== a &&\n\t\t\tp !== c &&\n\t\t\tpointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&\n\t\t\tarea(p.prev, p, p.next) >= 0\n\t\t)\n\t\t\treturn false;\n\t\tp = p.prevZ;\n\t}\n\n\t// look for remaining points in increasing z-order\n\twhile (n && n.z <= maxZ) {\n\t\tif (\n\t\t\tn.x >= x0 &&\n\t\t\tn.x <= x1 &&\n\t\t\tn.y >= y0 &&\n\t\t\tn.y <= y1 &&\n\t\t\tn !== a &&\n\t\t\tn !== c &&\n\t\t\tpointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) &&\n\t\t\tarea(n.prev, n, n.next) >= 0\n\t\t)\n\t\t\treturn false;\n\t\tn = n.nextZ;\n\t}\n\n\treturn true;\n}\n\n// go through all polygon nodes and cure small local self-intersections\nfunction cureLocalIntersections(start: Node, triangles: number[]) {\n\tlet p = start;\n\tdo {\n\t\tconst a = p.prev,\n\t\t\tb = p.next.next;\n\n\t\tif (\n\t\t\t!equals(a, b) &&\n\t\t\tintersects(a, p, p.next, b) &&\n\t\t\tlocallyInside(a, b) &&\n\t\t\tlocallyInside(b, a)\n\t\t) {\n\t\t\ttriangles.push(a.i, p.i, b.i);\n\n\t\t\t// remove two nodes involved\n\t\t\tremoveNode(p);\n\t\t\tremoveNode(p.next);\n\n\t\t\tp = start = b;\n\t\t}\n\t\tp = p.next;\n\t} while (p !== start);\n\n\treturn filterPoints(p);\n}\n\n// try splitting polygon into two and triangulate them independently\nfunction splitEarcut(\n\tstart: Node,\n\ttriangles: number[],\n\tdim: number,\n\tminX: number | undefined,\n\tminY: number | undefined,\n\tinvSize: number | undefined,\n) {\n\t// look for a valid diagonal that divides the polygon into two\n\tlet a = start;\n\tdo {\n\t\tlet b = a.next.next;\n\t\twhile (b !== a.prev) {\n\t\t\tif (a.i !== b.i && isValidDiagonal(a, b)) {\n\t\t\t\t// split the polygon in two by the diagonal\n\t\t\t\tlet c = splitPolygon(a, b);\n\n\t\t\t\t// filter colinear points around the cuts\n\t\t\t\ta = filterPoints(a, a.next);\n\t\t\t\tc = filterPoints(c, c.next);\n\n\t\t\t\t// run earcut on each half\n\t\t\t\tearcutLinked(a, triangles, dim, minX, minY, invSize, 0);\n\t\t\t\tearcutLinked(c, triangles, dim, minX, minY, invSize, 0);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tb = b.next;\n\t\t}\n\t\ta = a.next;\n\t} while (a !== start);\n}\n\n// link every hole into the outer loop, producing a single-ring polygon without holes\nfunction eliminateHoles(\n\tdata: number[],\n\tholeIndices: number[],\n\touterNode: Node,\n\tdim: number,\n) {\n\tconst queue = [];\n\n\tfor (let i = 0, len = holeIndices.length; i < len; i++) {\n\t\tconst start = holeIndices[i] * dim;\n\t\tconst end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;\n\t\tconst list = linkedList(data, start, end, dim, false);\n\t\tif (list === list.next) list.steiner = true;\n\t\tqueue.push(getLeftmost(list));\n\t}\n\n\tqueue.sort(compareX);\n\n\t// process holes from left to right\n\tfor (let i = 0; i < queue.length; i++) {\n\t\touterNode = eliminateHole(queue[i], outerNode);\n\t}\n\n\treturn outerNode;\n}\n\nfunction compareX(a: Node, b: Node) {\n\treturn a.x - b.x;\n}\n\n// find a bridge between vertices that connects hole with an outer ring and and link it\nfunction eliminateHole(hole: Node, outerNode: Node) {\n\tconst bridge = findHoleBridge(hole, outerNode);\n\tif (!bridge) {\n\t\treturn outerNode;\n\t}\n\n\tconst bridgeReverse = splitPolygon(bridge, hole);\n\n\t// filter collinear points around the cuts\n\tfilterPoints(bridgeReverse, bridgeReverse.next);\n\treturn filterPoints(bridge, bridge.next);\n}\n\n// David Eberly's algorithm for finding a bridge between hole and outer polygon\nfunction findHoleBridge(hole: Node, outerNode: Node) {\n\tlet p = outerNode;\n\tconst hx = hole.x;\n\tconst hy = hole.y;\n\tlet qx = -Infinity;\n\tlet m;\n\n\t// find a segment intersected by a ray from the hole's leftmost point to the left;\n\t// segment's endpoint with lesser x will be potential connection point\n\tdo {\n\t\tif (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {\n\t\t\tconst x = p.x + ((hy - p.y) * (p.next.x - p.x)) / (p.next.y - p.y);\n\t\t\tif (x <= hx && x > qx) {\n\t\t\t\tqx = x;\n\t\t\t\tm = p.x < p.next.x ? p : p.next;\n\t\t\t\tif (x === hx) return m; // hole touches outer segment; pick leftmost endpoint\n\t\t\t}\n\t\t}\n\t\tp = p.next;\n\t} while (p !== outerNode);\n\n\tif (!m) return null;\n\n\t// look for points inside the triangle of hole point, segment intersection and endpoint;\n\t// if there are no points found, we have a valid connection;\n\t// otherwise choose the point of the minimum angle with the ray as connection point\n\n\tconst stop = m;\n\tconst mx = m.x;\n\tconst my = m.y;\n\tlet tanMin = Infinity;\n\n\tp = m;\n\n\tdo {\n\t\tif (\n\t\t\thx >= p.x &&\n\t\t\tp.x >= mx &&\n\t\t\thx !== p.x &&\n\t\t\tpointInTriangle(\n\t\t\t\thy < my ? hx : qx,\n\t\t\t\thy,\n\t\t\t\tmx,\n\t\t\t\tmy,\n\t\t\t\thy < my ? qx : hx,\n\t\t\t\thy,\n\t\t\t\tp.x,\n\t\t\t\tp.y,\n\t\t\t)\n\t\t) {\n\t\t\tconst tan = Math.abs(hy - p.y) / (hx - p.x); // tangential\n\n\t\t\tif (\n\t\t\t\tlocallyInside(p, hole) &&\n\t\t\t\t(tan < tanMin ||\n\t\t\t\t\t(tan === tanMin &&\n\t\t\t\t\t\t(p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))\n\t\t\t) {\n\t\t\t\tm = p;\n\t\t\t\ttanMin = tan;\n\t\t\t}\n\t\t}\n\n\t\tp = p.next;\n\t} while (p !== stop);\n\n\treturn m;\n}\n\n// whether sector in vertex m contains sector in vertex p in the same coordinates\nfunction sectorContainsSector(m: Node, p: Node) {\n\treturn area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;\n}\n\n// interlink polygon nodes in z-order\nfunction indexCurve(start: Node, minX: number, minY: number, invSize: number) {\n\tlet p = start;\n\tdo {\n\t\tif (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);\n\t\tp.prevZ = p.prev;\n\t\tp.nextZ = p.next;\n\t\tp = p.next;\n\t} while (p !== start);\n\n\tp.prevZ!.nextZ = null;\n\tp.prevZ = null;\n\n\tsortLinked(p);\n}\n\n// Simon Tatham's linked list merge sort algorithm\n// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html\nfunction sortLinked(head: Node) {\n\tlet numMerges;\n\tlet inSize = 1;\n\n\tlet list: Node | null = head;\n\n\tdo {\n\t\tlet p: Node | null = list;\n\t\tlet e: Node;\n\t\tlist = null;\n\t\tlet tail = null;\n\t\tnumMerges = 0;\n\n\t\twhile (p) {\n\t\t\tnumMerges++;\n\t\t\tlet q: Node | null = p;\n\t\t\tlet pSize = 0;\n\t\t\tfor (let i = 0; i < inSize; i++) {\n\t\t\t\tpSize++;\n\t\t\t\tq = q.nextZ;\n\t\t\t\tif (!q) break;\n\t\t\t}\n\t\t\tlet qSize = inSize;\n\n\t\t\twhile (pSize > 0 || (qSize > 0 && q)) {\n\t\t\t\tif (pSize !== 0 && (qSize === 0 || !q || p!.z <= q.z)) {\n\t\t\t\t\te = p!;\n\t\t\t\t\tp = p!.nextZ;\n\t\t\t\t\tpSize--;\n\t\t\t\t} else {\n\t\t\t\t\te = q!;\n\t\t\t\t\tq = q!.nextZ;\n\t\t\t\t\tqSize--;\n\t\t\t\t}\n\n\t\t\t\tif (tail) {\n\t\t\t\t\ttail.nextZ = e;\n\t\t\t\t} else {\n\t\t\t\t\tlist = e;\n\t\t\t\t}\n\n\t\t\t\te.prevZ = tail;\n\t\t\t\ttail = e;\n\t\t\t}\n\n\t\t\tp = q;\n\t\t}\n\n\t\ttail!.nextZ = null; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion\n\t\tinSize *= 2;\n\t} while (numMerges > 1);\n\n\treturn list;\n}\n\n// z-order of a point given coords and inverse of the longer side of data bbox\nfunction zOrder(\n\tx: number,\n\ty: number,\n\tminX: number,\n\tminY: number,\n\tinvSize: number,\n) {\n\t// coords are transformed into non-negative 15-bit integer range\n\tx = ((x - minX) * invSize) | 0;\n\ty = ((y - minY) * invSize) | 0;\n\n\tx = (x | (x << 8)) & 0x00ff00ff;\n\tx = (x | (x << 4)) & 0x0f0f0f0f;\n\tx = (x | (x << 2)) & 0x33333333;\n\tx = (x | (x << 1)) & 0x55555555;\n\n\ty = (y | (y << 8)) & 0x00ff00ff;\n\ty = (y | (y << 4)) & 0x0f0f0f0f;\n\ty = (y | (y << 2)) & 0x33333333;\n\ty = (y | (y << 1)) & 0x55555555;\n\n\treturn x | (y << 1);\n}\n\n// find the leftmost node of a polygon ring\nfunction getLeftmost(start: Node) {\n\tlet p = start,\n\t\tleftmost = start;\n\tdo {\n\t\tif (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y))\n\t\t\tleftmost = p;\n\t\tp = p.next;\n\t} while (p !== start);\n\n\treturn leftmost;\n}\n\n// check if a point lies within a convex triangle\nfunction pointInTriangle(\n\tax: number,\n\tay: number,\n\tbx: number,\n\tby: number,\n\tcx: number,\n\tcy: number,\n\tpx: number,\n\tpy: number,\n) {\n\treturn (\n\t\t(cx - px) * (ay - py) >= (ax - px) * (cy - py) &&\n\t\t(ax - px) * (by - py) >= (bx - px) * (ay - py) &&\n\t\t(bx - px) * (cy - py) >= (cx - px) * (by - py)\n\t);\n}\n\n// check if a diagonal between two polygon nodes is valid (lies in polygon interior)\nfunction isValidDiagonal(a: Node, b: Node) {\n\treturn (\n\t\ta.next.i !== b.i &&\n\t\ta.prev.i !== b.i &&\n\t\t!intersectsPolygon(a, b) && // dones't intersect other edges\n\t\t((locallyInside(a, b) &&\n\t\t\tlocallyInside(b, a) &&\n\t\t\tmiddleInside(a, b) && // locally visible\n\t\t\t(area(a.prev, a, b.prev) || area(a, b.prev, b))) || // does not create opposite-facing sectors\n\t\t\t(equals(a, b) &&\n\t\t\t\tarea(a.prev, a, a.next) > 0 &&\n\t\t\t\tarea(b.prev, b, b.next) > 0))\n\t); // special zero-length case\n}\n\n// signed area of a triangle\nfunction area(p: Node, q: Node, r: Node) {\n\treturn (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);\n}\n\n// check if two points are equal\nfunction equals(p1: Node, p2: Node) {\n\treturn p1.x === p2.x && p1.y === p2.y;\n}\n\n// check if two segments intersect\nfunction intersects(p1: Node, q1: Node, p2: Node, q2: Node) {\n\tconst o1 = sign(area(p1, q1, p2));\n\tconst o2 = sign(area(p1, q1, q2));\n\tconst o3 = sign(area(p2, q2, p1));\n\tconst o4 = sign(area(p2, q2, q1));\n\n\tif (o1 !== o2 && o3 !== o4) return true; // general case\n\n\tif (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1\n\tif (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1\n\tif (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2\n\tif (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2\n\n\treturn false;\n}\n\n// for collinear points p, q, r, check if point q lies on segment pr\nfunction onSegment(p: Node, q: Node, r: Node) {\n\treturn (\n\t\tq.x <= Math.max(p.x, r.x) &&\n\t\tq.x >= Math.min(p.x, r.x) &&\n\t\tq.y <= Math.max(p.y, r.y) &&\n\t\tq.y >= Math.min(p.y, r.y)\n\t);\n}\n\nfunction sign(num: number) {\n\treturn num > 0 ? 1 : num < 0 ? -1 : 0;\n}\n\n// check if a polygon diagonal intersects any polygon segments\nfunction intersectsPolygon(a: Node, b: Node) {\n\tlet p = a;\n\tdo {\n\t\tif (\n\t\t\tp.i !== a.i &&\n\t\t\tp.next.i !== a.i &&\n\t\t\tp.i !== b.i &&\n\t\t\tp.next.i !== b.i &&\n\t\t\tintersects(p, p.next, a, b)\n\t\t)\n\t\t\treturn true;\n\t\tp = p.next;\n\t} while (p !== a);\n\n\treturn false;\n}\n\n// check if a polygon diagonal is locally inside the polygon\nfunction locallyInside(a: Node, b: Node) {\n\treturn area(a.prev, a, a.next) < 0\n\t\t? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0\n\t\t: area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;\n}\n\n// check if the middle point of a polygon diagonal is inside the polygon\nfunction middleInside(a: Node, b: Node) {\n\tlet p = a;\n\tlet inside = false;\n\tconst px = (a.x + b.x) / 2;\n\tconst py = (a.y + b.y) / 2;\n\tdo {\n\t\tif (\n\t\t\tp.y > py !== p.next.y > py &&\n\t\t\tp.next.y !== p.y &&\n\t\t\tpx < ((p.next.x - p.x) * (py - p.y)) / (p.next.y - p.y) + p.x\n\t\t)\n\t\t\tinside = !inside;\n\t\tp = p.next;\n\t} while (p !== a);\n\n\treturn inside;\n}\n\n// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;\n// if one belongs to the outer ring and another to a hole, it merges it into a single ring\nfunction splitPolygon(a: Node, b: Node) {\n\tconst a2 = createNode(a.i, a.x, a.y),\n\t\tb2 = createNode(b.i, b.x, b.y),\n\t\tan = a.next,\n\t\tbp = b.prev;\n\n\ta.next = b;\n\tb.prev = a;\n\n\ta2.next = an;\n\tan.prev = a2 as Node;\n\n\tb2.next = a2 as Node;\n\ta2.prev = b2 as Node;\n\n\tbp.next = b2 as Node;\n\tb2.prev = bp;\n\n\treturn b2 as Node;\n}\n\n// create a node and optionally link it with previous one (in a circular doubly linked list)\nfunction insertNode(i: number, x: number, y: number, last: Node | null) {\n\tconst p = createNode(i, x, y) as Node;\n\n\tif (!last) {\n\t\tp.prev = p;\n\t\tp.next = p;\n\t} else {\n\t\tp.next = last.next;\n\t\tp.prev = last;\n\t\tlast.next.prev = p;\n\t\tlast.next = p;\n\t}\n\treturn p;\n}\n\nfunction removeNode(p: Node) {\n\tp.next.prev = p.prev;\n\tp.prev.next = p.next;\n\n\tif (p.prevZ) p.prevZ.nextZ = p.nextZ;\n\tif (p.nextZ) p.nextZ.prevZ = p.prevZ;\n}\n\ninterface LooseNode {\n\ti: number;\n\tx: number;\n\ty: number;\n\tprev: Node | null;\n\tnext: Node | null;\n\tz: number;\n\tprevZ: Node | null;\n\tnextZ: Node | null;\n\tsteiner: boolean;\n}\n\ninterface Node extends LooseNode {\n\tprev: Node;\n\tnext: Node;\n}\n\nfunction createNode(i: number, x: number, y: number): LooseNode {\n\treturn {\n\t\ti, // vertex index in coordinates array\n\t\tx,\n\t\ty, // vertex coordinates\n\t\tprev: null, // previous and next vertex nodes in a polygon ring\n\t\tnext: null,\n\t\tz: 0, // z-order curve value\n\t\tprevZ: null, // previous and next nodes in z-order\n\t\tnextZ: null,\n\t\tsteiner: false, // indicates whether this is a steiner point\n\t};\n}\n\nexport function signedArea(\n\tdata: number[],\n\tstart: number,\n\tend: number,\n\tdim: number,\n) {\n\tlet sum = 0;\n\tfor (let i = start, j = end - dim; i < end; i += dim) {\n\t\tsum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);\n\t\tj = i;\n\t}\n\treturn sum;\n}\n","import { Matrix2d } from \"../math/matrix2d.ts\";\nimport { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\nimport { Bounds, boundsPool } from \"../physics/bounds.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { XYPoint } from \"../utils/types.ts\";\nimport { earcut } from \"./earcut.ts\";\n\nexport type PolygonVertices =\n\t| [XYPoint, XYPoint, XYPoint, ...XYPoint[]]\n\t| [Vector2d, Vector2d, Vector2d, ...Vector2d[]]\n\t| [number, number, number, number, number, number, ...number[]];\n\nexport type LineVertices =\n\t| [XYPoint, XYPoint]\n\t| [Vector2d, Vector2d]\n\t| [number, number, number, number];\n\n/**\n * a polygon Object.<br>\n * Please do note that melonJS implements a simple Axis-Aligned Boxes collision algorithm, which requires all polygons used for collision to be convex with all vertices defined with clockwise winding.\n * A polygon is convex when all line segments connecting two points in the interior do not cross any edge of the polygon\n * (which means that all angles are less than 180 degrees), as described here below : <br>\n * <center><img src=\"images/convex_polygon.png\"/></center><br>\n * A polygon's `winding` is clockwise if its vertices (points) are declared turning to the right. The image above shows COUNTERCLOCKWISE winding.\n * @category Geometry\n */\nexport class Polygon {\n\t/**\n\t * origin point of the Polygon\n\t */\n\tpos: Vector2d;\n\n\t/**\n\t * Array of points defining the Polygon <br>\n\t * Note: If you manually change `points`, you **must** call `recalc`afterwards so that the changes get applied correctly.\n\t */\n\tpoints: Vector2d[];\n\n\t/**\n\t * The edges here are the direction of the `n`th edge of the polygon, relative to\n\t * the `n`th point. If you want to draw a given edge from the edge value, you must\n\t * first translate to the position of the starting point.\n\t */\n\tedges: Vector2d[];\n\n\t/**\n\t * a list of indices for all vertices composing this polygon\n\t */\n\tindices: number[];\n\n\t/**\n\t * The normals here are the direction of the normal for the `n`th edge of the polygon, relative\n\t * to the position of the `n`th point. If you want to draw an edge normal, you must first\n\t * translate to the position of the starting point.\n\t * @ignore\n\t */\n\tnormals: Vector2d[];\n\n\t/**\n\t * The bounding rectangle for this shape\n\t * @ignore\n\t */\n\tprivate _bounds: Bounds;\n\n\t/**\n\t * the shape type (used internally)\n\t */\n\ttype = \"Polygon\";\n\n\t/**\n\t * @param [x=0] - origin point of the Polygon\n\t * @param [y=0] - origin point of the Polygon\n\t * @param vertices - array of vector defining the Polygon\n\t */\n\tconstructor(x = 0, y = 0, vertices: PolygonVertices | LineVertices) {\n\t\tthis.pos = vector2dPool.get(x, y);\n\t\tthis.points = [];\n\t\tthis.edges = [];\n\t\tthis.indices = [];\n\t\tthis.normals = [];\n\t\tthis._bounds = boundsPool.get();\n\t\tthis.setVertices(vertices);\n\t}\n\n\t/**\n\t * set new value to the Polygon\n\t * @param x - position of the Polygon\n\t * @param y - position of the Polygon\n\t * @param points - array of vector or vertices defining the Polygon\n\t * @returns this instance for object chaining\n\t */\n\tsetShape(x: number, y: number, points: PolygonVertices | LineVertices) {\n\t\tthis.pos.set(x, y);\n\t\tthis.setVertices(points);\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the vertices defining this Polygon\n\t * @param vertices - array of vector or vertices defining the Polygon\n\t * @returns this instance for object chaining\n\t */\n\tsetVertices(vertices: PolygonVertices | LineVertices) {\n\t\tif (!Array.isArray(vertices)) {\n\t\t\treturn this;\n\t\t}\n\n\t\tif (vertices.length < 2) {\n\t\t\tthrow new Error(\"You must provide at least two vertices\");\n\t\t}\n\n\t\tif (typeof vertices[0] === \"number\") {\n\t\t\tthis.points = [];\n\t\t\tfor (let p = 0; p < vertices.length; p += 2) {\n\t\t\t\tthis.points.push(\n\t\t\t\t\tnew Vector2d(\n\t\t\t\t\t\t(vertices as number[])[p],\n\t\t\t\t\t\t(vertices as number[])[p + 1],\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (vertices[0] instanceof Vector2d) {\n\t\t\tthis.points = vertices as Vector2d[];\n\t\t} else {\n\t\t\tthis.points = [];\n\t\t\tfor (const vertex of vertices as XYPoint[]) {\n\t\t\t\tthis.points.push(new Vector2d(vertex.x, vertex.y));\n\t\t\t}\n\t\t}\n\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t\treturn this;\n\t}\n\n\t/**\n\t * apply the given transformation matrix to this Polygon\n\t * @param m - the transformation matrix\n\t * @returns Reference to this object for method chaining\n\t */\n\ttransform(m: Matrix2d) {\n\t\tconst points = this.points;\n\t\tconst len = points.length;\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tm.apply(points[i]);\n\t\t}\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t\treturn this;\n\t}\n\n\t/**\n\t * apply an isometric projection to this shape\n\t * @returns Reference to this object for method chaining\n\t */\n\ttoIso() {\n\t\treturn this.rotate(Math.PI / 4).scale(Math.SQRT2, Math.SQRT1_2);\n\t}\n\n\t/**\n\t * apply a 2d projection to this shape\n\t * @returns Reference to this object for method chaining\n\t */\n\tto2d() {\n\t\treturn this.scale(Math.SQRT1_2, Math.SQRT2).rotate(-Math.PI / 4);\n\t}\n\n\t/**\n\t * Rotate this Polygon (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: Vector2d | XYPoint) {\n\t\tif (angle !== 0) {\n\t\t\tconst points = this.points;\n\t\t\tconst len = points.length;\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\n\t\t\tif (v) {\n\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\tconst x = points[i].x - v.x;\n\t\t\t\t\tconst y = points[i].y - v.y;\n\t\t\t\t\tpoints[i].x = x * cos - y * sin + v.x;\n\t\t\t\t\tpoints[i].y = x * sin + y * cos + v.y;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\tconst x = points[i].x;\n\t\t\t\t\tconst y = points[i].y;\n\t\t\t\t\tpoints[i].x = x * cos - y * sin;\n\t\t\t\t\tpoints[i].y = x * sin + y * cos;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.recalc();\n\t\t\tthis.updateBounds();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Scales the polygon by the given factors along the x and y axes.\n\t * @param x - The factor by which to scale the polygon along the x-axis.\n\t * @param [y=x] - The factor by which to scale the polygon along the y-axis. Defaults to the value of x.\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x) {\n\t\tconst points = this.points;\n\t\tconst len = points.length;\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tpoints[i].scale(x, y);\n\t\t}\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Scales the polygon by the given vector.\n\t * @param v - A vector containing the scaling factors for the x and y axes.\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector2d) {\n\t\treturn this.scale(v.x, v.y);\n\t}\n\n\t/**\n\t * Computes the calculated collision polygon.\n\t * This **must** be called if the `points` array, `angle`, or `offset` is modified manually.\n\t * @returns Reference to this object for method chaining\n\t */\n\trecalc() {\n\t\tconst edges = this.edges;\n\t\tconst normals = this.normals;\n\t\tconst indices = this.indices;\n\n\t\t// Copy the original points array and apply the offset/angle\n\t\tconst points = this.points;\n\t\tconst len = points.length;\n\n\t\tif (len < 3) {\n\t\t\tthrow new Error(\"Requires at least 3 points\");\n\t\t}\n\n\t\t// Calculate the edges/normals\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tlet edge = edges[i];\n\t\t\tif (edge === undefined) {\n\t\t\t\tedge = edges[i] = vector2dPool.get();\n\t\t\t}\n\n\t\t\tconst next = i + 1 < len ? i + 1 : 0;\n\t\t\tconst ex = points[next].x - points[i].x;\n\t\t\tconst ey = points[next].y - points[i].y;\n\t\t\tedge.x = ex;\n\t\t\tedge.y = ey;\n\n\t\t\tlet normal = normals[i];\n\t\t\tif (normal === undefined) {\n\t\t\t\tnormal = normals[i] = vector2dPool.get();\n\t\t\t}\n\n\t\t\t// perp of (ex, ey) is (ey, -ex), then normalize\n\t\t\tconst nlen = Math.sqrt(ey * ey + ex * ex);\n\t\t\tnormal.x = ey / nlen;\n\t\t\tnormal.y = -ex / nlen;\n\t\t}\n\n\t\t// Release any existing Vector2d objects back to the pool\n\t\tfor (let i = len; i < edges.length; i++) {\n\t\t\tvector2dPool.release(edges[i]);\n\t\t\tvector2dPool.release(normals[i]);\n\t\t}\n\n\t\t// trunc array\n\t\tedges.length = len;\n\t\tnormals.length = len;\n\n\t\t// do not do anything here, indices will be computed by\n\t\t// getIndices if array is empty upon function call\n\t\tindices.length = 0;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * returns a list of indices for all triangles defined in this polygon\n\t * @returns an array of vertex indices for all triangles forming this polygon.\n\t */\n\tgetIndices() {\n\t\tif (this.indices.length === 0) {\n\t\t\tconst points = this.points;\n\t\t\tconst len = points.length;\n\t\t\tconst flat = new Array(len * 2);\n\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\tflat[i * 2] = points[i].x;\n\t\t\t\tflat[i * 2 + 1] = points[i].y;\n\t\t\t}\n\t\t\tthis.indices = earcut(flat);\n\t\t}\n\t\treturn this.indices;\n\t}\n\n\t/**\n\t * Returns true if the vertices composing this polygon form a convex shape (vertices must be in clockwise order).\n\t * @returns true if the vertices are convex, false if not, null if not computable\n\t */\n\tisConvex() {\n\t\t// http://paulbourke.net/geometry/polygonmesh/\n\t\t// Copyright (c) Paul Bourke (use permitted)\n\n\t\tlet flag = 0;\n\t\tconst vertices = this.points;\n\t\tconst n = vertices.length;\n\n\t\tif (n < 3) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst j = (i + 1) % n;\n\t\t\tconst k = (i + 2) % n;\n\t\t\tlet z = (vertices[j].x - vertices[i].x) * (vertices[k].y - vertices[j].y);\n\t\t\tz -= (vertices[j].y - vertices[i].y) * (vertices[k].x - vertices[j].x);\n\n\t\t\tif (z < 0) {\n\t\t\t\tflag |= 1;\n\t\t\t} else if (z > 0) {\n\t\t\t\tflag |= 2;\n\t\t\t}\n\n\t\t\tif (flag === 3) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (flag !== 0) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Translates the Polygon by the specified offset.\n\t * @param x - The x offset or a vector point to translate by.\n\t * @param [y] - The y offset. This parameter is required if the first parameter is a number.\n\t * @returns Reference to this object for method chaining\n\t * @example\n\t * polygon.translate(10, 10);\n\t * // or\n\t * polygon.translate(myVector2d);\n\t */\n\ttranslate(x: number, y: number): Polygon;\n\ttranslate(vector: Vector2d): Polygon;\n\ttranslate(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\tthis.pos.x += _x;\n\t\tthis.pos.y += _y;\n\t\tthis.getBounds().translate(_x, _y);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Shifts the Polygon to the given position vector.\n\t * @param x - The x coordinate or a vector point to shift to.\n\t * @param [y] - The y coordinate. This parameter is required if the first parameter is a number.\n\t * @example\n\t * polygon.shift(10, 10);\n\t * // or\n\t * polygon.shift(myVector2d);\n\t */\n\tshift(x: number, y: number): void;\n\tshift(vector: Vector2d): void;\n\tshift(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x;\n\t\tlet _y;\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\t\tthis.pos.x = _x;\n\t\tthis.pos.y = _y;\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * Returns true if the polygon contains the given point. <br>\n\t * (Note: it is highly recommended to first do a hit test on the corresponding <br>\n\t *  bounding rect, as the function can be highly consuming with complex shapes)\n\t * @param x -  x coordinate or a vector point to check\n\t * @param [y] - y coordinate\n\t * @param args\n\t * @returns True if the polygon contain the point, otherwise false\n\t * @example\n\t * if (polygon.contains(10, 10)) {\n\t *   // do something\n\t * }\n\t * // or\n\t * if (polygon.contains(myVector2d)) {\n\t *   // do something\n\t * }\n\t */\n\tcontains(x: number, y: number): boolean;\n\tcontains(vector: Vector2d): boolean;\n\tcontains(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\tlet intersects = false;\n\t\tconst posx = this.pos.x;\n\t\tconst posy = this.pos.y;\n\t\tconst points = this.points;\n\t\tconst len = points.length;\n\n\t\t//http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html\n\t\tfor (let i = 0, j = len - 1; i < len; j = i++) {\n\t\t\tconst iy = points[i].y + posy;\n\t\t\tconst ix = points[i].x + posx;\n\t\t\tconst jy = points[j].y + posy;\n\t\t\tconst jx = points[j].x + posx;\n\t\t\tif (\n\t\t\t\tiy > _y !== jy > _y &&\n\t\t\t\t_x < ((jx - ix) * (_y - iy)) / (jy - iy) + ix\n\t\t\t) {\n\t\t\t\tintersects = !intersects;\n\t\t\t}\n\t\t}\n\t\treturn intersects;\n\t}\n\n\t/**\n\t * returns the bounding box for this shape, the smallest Rectangle object completely containing this shape.\n\t * @returns this shape bounding box Rectangle object\n\t */\n\tgetBounds() {\n\t\treturn this._bounds;\n\t}\n\n\t/**\n\t * update the bounding box for this shape.\n\t * @returns this shape bounding box Rectangle object\n\t */\n\tupdateBounds() {\n\t\tconst bounds = this.getBounds();\n\n\t\tbounds.update(this.points);\n\t\tbounds.translate(this.pos);\n\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * clone this Polygon\n\t * @returns new Polygon\n\t */\n\tclone() {\n\t\treturn polygonPool.get(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.points.map((point) => point.clone()) as [\n\t\t\t\tVector2d,\n\t\t\t\tVector2d,\n\t\t\t\tVector2d,\n\t\t\t\t...Vector2d[],\n\t\t\t],\n\t\t);\n\t}\n}\n\nexport const polygonPool = createPool<\n\tPolygon,\n\t[x: number, y: number, vertices: PolygonVertices]\n>((x, y, vertices) => {\n\tconst polygon = new Polygon(x, y, vertices);\n\treturn {\n\t\tinstance: polygon,\n\t\treset(x, y, points) {\n\t\t\tpolygon.pos.set(x, y);\n\t\t\tpolygon.setVertices(points);\n\t\t},\n\t};\n});\n","import { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { Polygon, PolygonVertices, polygonPool } from \"./polygon.ts\";\n\n/**\n * A rectangle object.\n * @category Geometry\n */\nexport class Rect extends Polygon {\n\t/**\n\t * The shape type (used internally).\n\t */\n\toverride type = \"Rectangle\";\n\n\t/**\n\t * Creates a new rectangle.\n\t * @param x - The x position of the rectangle.\n\t * @param y - The y position of the rectangle.\n\t * @param w - The width of the rectangle.\n\t * @param h - The height of the rectangle.\n\t */\n\tconstructor(x: number, y: number, w: number, h: number) {\n\t\tsuper(x, y, [\n\t\t\tvector2dPool.get(0, 0), // 0, 0\n\t\t\tvector2dPool.get(w, 0), // 1, 0\n\t\t\tvector2dPool.get(w, h), // 1, 1\n\t\t\tvector2dPool.get(0, h), // 0, 1\n\t\t]);\n\t}\n\n\t/**\n\t * Set new dimensions for the rectangle.\n\t * @param width - The new width of the rectangle.\n\t * @param height - The new height of the rectangle.\n\t */\n\tsetSize(width: number, height: number) {\n\t\tthis.points[0].set(0, 0); // 0, 0\n\t\tthis.points[1].set(width, 0); // 1, 0\n\t\tthis.points[2].set(width, height); // 1, 1\n\t\tthis.points[3].set(0, height); // 0, 1\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t\treturn this;\n\t}\n\n\t/**\n\t * The left coordinate of the Rectangle.\n\t */\n\tget left() {\n\t\treturn this.pos.x;\n\t}\n\n\t/**\n\t * right coordinate of the Rectangle\n\t */\n\tget right() {\n\t\treturn this.left + this.width;\n\t}\n\n\t/**\n\t * top coordinate of the Rectangle\n\t */\n\tget top() {\n\t\treturn this.pos.y;\n\t}\n\n\t/**\n\t * bottom coordinate of the Rectangle\n\t */\n\tget bottom() {\n\t\treturn this.top + this.height;\n\t}\n\n\t/**\n\t * width of the Rectangle\n\t */\n\tget width() {\n\t\treturn this.points[2].x;\n\t}\n\tset width(value) {\n\t\tthis.points[1].x = this.points[2].x = value;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * height of the Rectangle\n\t */\n\tget height() {\n\t\treturn this.points[2].y;\n\t}\n\tset height(value) {\n\t\tthis.points[2].y = this.points[3].y = value;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * absolute center of this rectangle on the horizontal axis\n\t */\n\tget centerX() {\n\t\tif (isFinite(this.width)) {\n\t\t\treturn this.left + this.width / 2;\n\t\t} else {\n\t\t\treturn this.width;\n\t\t}\n\t}\n\tset centerX(value) {\n\t\tthis.pos.x = value - this.width / 2;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * absolute center of this rectangle on the vertical axis\n\t */\n\tget centerY() {\n\t\tif (isFinite(this.height)) {\n\t\t\treturn this.top + this.height / 2;\n\t\t} else {\n\t\t\treturn this.height;\n\t\t}\n\t}\n\tset centerY(value) {\n\t\tthis.pos.y = value - this.height / 2;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * center the rectangle position around the given coordinates\n\t * @param x - the x coordinate around which to center this rectangle\n\t * @param y - the y coordinate around which to center this rectangle\n\t * @returns this rectangle\n\t */\n\tcenterOn(x: number, y: number) {\n\t\tthis.centerX = x;\n\t\tthis.centerY = y;\n\t\treturn this;\n\t}\n\n\t/**\n\t * resize the rectangle\n\t * @param w - new width of the rectangle\n\t * @param h - new height of the rectangle\n\t * @returns this rectangle\n\t */\n\tresize(w: number, h: number) {\n\t\tthis.width = w;\n\t\tthis.height = h;\n\t\treturn this;\n\t}\n\n\t/**\n\t * scale the rectangle\n\t * @param x - a number representing the abscissa of the scaling vector.\n\t * @param [y=x] - a number representing the ordinate of the scaling vector.\n\t * @returns this rectangle\n\t */\n\toverride scale(x: number, y = x) {\n\t\tthis.width *= x;\n\t\tthis.height *= y;\n\t\treturn this;\n\t}\n\n\t/**\n\t * clone this rectangle\n\t * @returns new rectangle\n\t */\n\toverride clone() {\n\t\treturn new Rect(this.left, this.top, this.width, this.height);\n\t}\n\n\t/**\n\t * copy the position and size of the given rectangle into this one\n\t * @param rect - Source rectangle\n\t * @returns new rectangle\n\t */\n\tcopy(rect: Rect) {\n\t\tthis.pos.set(rect.pos.x, rect.pos.y);\n\t\tthis.setSize(rect.width, rect.height);\n\t\treturn this;\n\t}\n\n\t/**\n\t * merge this rectangle with another one\n\t * @param rect - other rectangle to union with\n\t * @returns the union(ed) rectangle\n\t */\n\tunion(rect: Rect) {\n\t\tconst x1 = Math.min(this.left, rect.left);\n\t\tconst y1 = Math.min(this.top, rect.top);\n\n\t\tthis.resize(\n\t\t\tMath.max(this.right, rect.right) - x1,\n\t\t\tMath.max(this.bottom, rect.bottom) - y1,\n\t\t);\n\n\t\tthis.pos.set(x1, y1);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * check if this rectangle is intersecting with the specified one\n\t * @param rect Other rectangle.\n\t * @returns true if overlaps\n\t */\n\toverlaps(rect: Rect) {\n\t\treturn (\n\t\t\tthis.left < rect.right &&\n\t\t\trect.left < this.right &&\n\t\t\tthis.top < rect.bottom &&\n\t\t\trect.top < this.bottom\n\t\t);\n\t}\n\n\toverride contains(x: number, y: number): boolean;\n\toverride contains(vector: Vector2d): boolean;\n\toverride contains(xOrVector: Vector2d | number, y?: number): boolean {\n\t\tlet _x1: number;\n\t\tlet _x2: number;\n\t\tlet _y1: number;\n\t\tlet _y2: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x1 = _x2 = xOrVector.x;\n\t\t\t_y1 = _y2 = xOrVector.y;\n\t\t} else {\n\t\t\t_x1 = _x2 = xOrVector;\n\t\t\t_y1 = _y2 = y!;\n\t\t}\n\t\treturn (\n\t\t\t_x1 >= this.left &&\n\t\t\t_x2 <= this.right &&\n\t\t\t_y1 >= this.top &&\n\t\t\t_y2 <= this.bottom\n\t\t);\n\t}\n\n\t/**\n\t * Returns true if the rectangle contains the given rectangle\n\t * @param rectangle - rectangle to test\n\t * @returns True if the rectangle contain the given rectangle, otherwise false\n\t * @example\n\t * if (rect.containsRectangle(myRect)) {\n\t *   // do something\n\t * }\n\t */\n\tcontainsRectangle(rectangle: Rect) {\n\t\treturn (\n\t\t\trectangle.left >= this.left &&\n\t\t\trectangle.right <= this.right &&\n\t\t\trectangle.top >= this.top &&\n\t\t\trectangle.bottom <= this.bottom\n\t\t);\n\t}\n\n\t/**\n\t * Check if this rectangle is identical to the specified one.\n\t * @param rect Other rectangle.\n\t * @returns true if equals\n\t */\n\tequals(rect: Rect) {\n\t\treturn (\n\t\t\trect.left === this.left &&\n\t\t\trect.right === this.right &&\n\t\t\trect.top === this.top &&\n\t\t\trect.bottom === this.bottom\n\t\t);\n\t}\n\n\t/**\n\t * Determines whether all coordinates of this rectangle are finite numbers.\n\t * @returns false if all coordinates are positive or negative Infinity or NaN; otherwise, true.\n\t */\n\tisFinite() {\n\t\treturn (\n\t\t\tisFinite(this.left) &&\n\t\t\tisFinite(this.top) &&\n\t\t\tisFinite(this.width) &&\n\t\t\tisFinite(this.height)\n\t\t);\n\t}\n\n\t/**\n\t * Returns a polygon whose edges are the same as this box.\n\t * @returns a new Polygon that represents this rectangle.\n\t */\n\ttoPolygon() {\n\t\t// clone the vertices: Polygon.setVertices stores a Vector2d[] by\n\t\t// reference, and handing out this rect's live points would let any\n\t\t// transform on the \"new\" polygon silently corrupt the rectangle\n\t\treturn polygonPool.get(\n\t\t\tthis.left,\n\t\t\tthis.top,\n\t\t\tthis.points.map((p) => {\n\t\t\t\treturn p.clone();\n\t\t\t}) as PolygonVertices,\n\t\t);\n\t}\n}\n\nexport const rectPool = createPool<\n\tRect,\n\t[x: number, y: number, width: number, height: number]\n>((x, y, width, height) => {\n\tconst rectangle = new Rect(x, y, width, height);\n\treturn {\n\t\tinstance: rectangle,\n\t\treset(x, y, width, height) {\n\t\t\trectangle.pos.set(x, y);\n\t\t\trectangle.setSize(width, height);\n\t\t},\n\t};\n});\n","import { prefixed } from \"./../utils/agent.ts\";\nimport { DOMContentLoaded } from \"./dom.ts\";\nimport { BLUR, emit, FOCUS } from \"./event.ts\";\nimport * as device_platform from \"./platform.ts\";\nimport save from \"./save.ts\";\n\n// Narrow local typings for non-standard / legacy browser surfaces. The\n// `lib.dom.d.ts` shipped with TypeScript omits vendor-prefixed properties\n// and a handful of deprecated APIs that the engine still has to probe at\n// runtime (Safari mobile, old Firefox / WebKit fullscreen, IE-era language\n// fallbacks, iOS-only DeviceOrientationEvent.requestPermission, etc.).\ntype NavigatorLegacy = Navigator & {\n\tbrowserLanguage?: string;\n\tuserLanguage?: string;\n\tstandalone?: boolean;\n};\ntype DeviceOrientationEventCtor = typeof DeviceOrientationEvent & {\n\trequestPermission?: () => Promise<\"granted\" | \"denied\" | \"default\">;\n};\ntype ScreenLegacy = Screen & {\n\tmozOrientation?: string;\n\tmsOrientation?: string;\n};\n\n/**\n * device type and capabilities\n * @namespace device\n */\n\nlet accelInitialized = false;\nlet deviceOrientationInitialized = false;\n// swipe utility fn & flag\nlet swipeEnabled = true;\n// a reusable cached rect for getElementBounds() fallback. Plain object\n// literal (not `new DOMRect()`) so module load doesn't ReferenceError in\n// Node / SSR environments where the DOMRect constructor isn't defined.\n// All fields are mutable here; cast to `DOMRect` at the return site.\nconst domRect = {\n\tleft: 0,\n\ttop: 0,\n\tx: 0,\n\ty: 0,\n\twidth: 0,\n\theight: 0,\n\tright: 0,\n\tbottom: 0,\n};\n\n// supported videoCodecs lookup (lazily populated on first hasVideoFormat call)\nlet videoCodecs: Record<string, string> | undefined;\n\n// internal flag to avoid rechecking for support\nlet WebGLSupport = -1;\n\nfunction disableSwipeFn(e: Event) {\n\te.preventDefault();\n\tif (typeof globalThis.scroll === \"function\") {\n\t\tglobalThis.scroll(0, 0);\n\t}\n\treturn false;\n}\n\nfunction hasLocalStorage() {\n\ttry {\n\t\treturn !!globalThis.localStorage;\n\t} catch {\n\t\t// the above generates an exception when cookies are blocked\n\t\treturn false;\n\t}\n}\n\nfunction hasOffscreenCanvas() {\n\ttry {\n\t\t// some browser (e.g. Safari) implements WebGL1 and WebGL2 contexts only\n\t\t// https://bugzilla.mozilla.org/show_bug.cgi?id=801176\n\t\treturn (\n\t\t\ttypeof globalThis.OffscreenCanvas !== \"undefined\" &&\n\t\t\tnew globalThis.OffscreenCanvas(0, 0).getContext(\"2d\") !== null\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * used by [un]watchAccelerometer()\n * @ignore\n */\nfunction onDeviceMotion(e: DeviceMotionEvent) {\n\tconst accel = e.accelerationIncludingGravity;\n\tif (accel === null) {\n\t\treturn;\n\t}\n\taccelerationX = accel.x ?? 0;\n\taccelerationY = accel.y ?? 0;\n\taccelerationZ = accel.z ?? 0;\n}\n\n/**\n * used by [un]watchDeviceOrientation()\n * @ignore\n */\nexport function onDeviceRotate(e: DeviceOrientationEvent) {\n\tgamma = e.gamma ?? 0;\n\tbeta = e.beta ?? 0;\n\talpha = e.alpha ?? 0;\n}\n\n/**\n * the device platform type\n */\nexport const platform = device_platform;\n\n/**\n * True if the browser supports Touch Events\n */\nexport const touchEvent = \"ontouchstart\" in globalThis;\n\n/**\n * True if the browser supports Pointer Events\n */\nexport const pointerEvent = !!globalThis.PointerEvent;\n\n/**\n * Touch capabilities (support either Touch or Pointer events)\n */\nexport const touch =\n\ttouchEvent || (pointerEvent && globalThis.navigator.maxTouchPoints > 0);\n\n/**\n * the maximum number of simultaneous touch contact points are supported by the current device.\n * @example\n * if (me.device.maxTouchPoints > 1) {\n *     // device supports multi-touch\n * }\n */\nexport const maxTouchPoints = touch\n\t? pointerEvent\n\t\t? globalThis.navigator.maxTouchPoints || 1\n\t\t: 10\n\t: 1;\n\n/**\n * W3C standard wheel events\n */\nexport const wheel =\n\ttypeof globalThis.document !== \"undefined\" &&\n\t\"onwheel\" in globalThis.document.createElement(\"div\");\n\n/**\n * Browser pointerlock api support\n */\nexport const hasPointerLockSupport =\n\ttypeof globalThis.document !== \"undefined\" &&\n\ttypeof globalThis.document.pointerLockElement !== \"undefined\";\n\n/**\n * Browser device orientation\n */\nexport const hasDeviceOrientation = !!globalThis.DeviceOrientationEvent;\n\n/**\n * Supports the ScreenOrientation API\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/onchange\n */\nexport const screenOrientation =\n\ttypeof screen !== \"undefined\" && typeof screen.orientation !== \"undefined\";\n\n/**\n * Browser accelerometer capabilities\n */\nexport const hasAccelerometer = !!globalThis.DeviceMotionEvent;\n\n// `hasFullscreenSupport` + `isFullscreen` live in `system/fullscreen.ts` —\n// factored out so `lang/deprecated.js` can use them without depending on\n// `device.ts` (would create a circular dep with the deprecated-wrapper\n// re-exports below). Re-exported here so `me.device.hasFullscreenSupport`\n// / `me.device.isFullscreen` stay unchanged for consumers.\nexport { hasFullscreenSupport, isFullscreen } from \"./fullscreen.ts\";\n\n/**\n * Device WebAudio Support\n */\nexport const hasWebAudio = !!(\n\tglobalThis.AudioContext ||\n\t(globalThis as { webkitAudioContext?: typeof AudioContext })\n\t\t.webkitAudioContext\n);\n\n/**\n * Device HTML5Audio Support\n */\nexport const hasHTML5Audio = typeof globalThis.Audio !== \"undefined\";\n\n/**\n * Returns true if the browser/device has audio capabilities.\n */\nexport const sound = hasWebAudio || hasHTML5Audio;\n\n/**\n * Device Video Support\n */\nexport const hasVideo =\n\ttypeof globalThis.document !== \"undefined\" &&\n\t!!globalThis.document.createElement(\"video\").canPlayType;\n\n/**\n * Browser Local Storage capabilities <br>\n * (this flag will be set to false if cookies are blocked)\n */\nexport const localStorage = hasLocalStorage();\n\n/**\n * equals to true if the device browser supports OffScreenCanvas.\n */\nexport const offscreenCanvas = hasOffscreenCanvas();\n\n/**\n * Browser Base64 decoding capability\n */\nexport const nativeBase64 = typeof globalThis.atob === \"function\";\n\n/**\n * a string representing the preferred language of the user, usually the language of the browser UI.\n * (will default to \"en\" if the information is not available)\n * @see http://www.w3schools.com/tags/ref_language_codes.asp\n */\nexport const language =\n\ttypeof globalThis.navigator !== \"undefined\"\n\t\t? globalThis.navigator.language ||\n\t\t\t(globalThis.navigator as NavigatorLegacy).browserLanguage ||\n\t\t\t(globalThis.navigator as NavigatorLegacy).userLanguage ||\n\t\t\t\"en\"\n\t\t: \"en\";\n\n/**\n * Ratio of the resolution in physical pixels to the resolution in CSS pixels for the current display device.\n */\nexport const devicePixelRatio = globalThis.devicePixelRatio || 1;\n\n/**\n * equals to true if a mobile device.\n * (Android | iPhone | iPad | iPod | any UA matching `Mobi`)\n */\nexport const isMobile = platform.isMobile;\n\n/**\n * contains the g-force acceleration along the x-axis.\n * @see device.watchAccelerometer\n */\nexport let accelerationX = 0;\n\n/**\n * contains the g-force acceleration along the y-axis.\n * @see device.watchAccelerometer\n */\nexport let accelerationY = 0;\n\n/**\n * contains the g-force acceleration along the z-axis.\n * @see device.watchAccelerometer\n */\nexport let accelerationZ = 0;\n\n/**\n * Device orientation Gamma property. Gives angle on tilting a portrait held phone left or right\n * @see device.watchDeviceOrientation\n */\nexport let gamma = 0;\n\n/**\n * Device orientation Beta property. Gives angle on tilting a portrait held phone forward or backward\n * @see device.watchDeviceOrientation\n */\nexport let beta = 0;\n\n/**\n * Device orientation Alpha property. Gives angle based on the rotation of the phone around its z axis.\n * The z-axis is perpendicular to the phone, facing out from the center of the screen.\n * @see device.watchDeviceOrientation\n */\nexport let alpha = 0;\n\n/**\n * Specify whether to automatically bring the window to the front.\n *\n * Read this binding to inspect the current value; the write surface\n * is {@link setAutoFocus} — direct assignment via the ESM namespace\n * (`me.device.autoFocus = false`) always threw a `TypeError`, since\n * namespace imports are read-only externally regardless of `let` /\n * `const`. The setter is the only way to mutate the flag from\n * user code.\n * @default true\n */\nexport let autoFocus = true;\n\n/**\n * Update the {@link autoFocus} flag.\n * Mirrors the `enableSwipe` / similar function-setter shape used\n * elsewhere in this module — the engine's \"bring the window to\n * the front on visibility restore\" behaviour can be toggled via\n * `me.device.setAutoFocus(false)`.\n * @param enable - `true` to keep the default focus-on-restore behaviour, `false` to opt out\n * @example\n * // disable the auto-focus-on-restore behaviour\n * me.device.setAutoFocus(false);\n */\nexport function setAutoFocus(enable: boolean): void {\n\tautoFocus = enable;\n}\n\n/**\n * specify a function to execute when the Device is fully loaded and ready\n * @param fn - the function to be executed\n * @example\n * // the modern equivalent is simply to await the Application\n * const app = new Application(640, 480, { parent: \"screen\" });\n * await app.init();\n * @deprecated since 18.3.0 — no longer needed when using {@link Application} as entry point.\n * @category Application\n */\nexport function onReady(fn: () => void) {\n\tDOMContentLoaded(fn);\n}\n\n/**\n * Register blur/focus and visibility change event handlers.\n * Called once during boot to emit BLUR/FOCUS events when the\n * window or tab gains/loses focus.\n * @ignore\n */\nexport function initVisibilityEvents() {\n\tif (typeof globalThis.addEventListener === \"function\") {\n\t\tglobalThis.addEventListener(\n\t\t\t\"blur\",\n\t\t\t() => {\n\t\t\t\temit(BLUR);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t\tglobalThis.addEventListener(\n\t\t\t\"focus\",\n\t\t\t() => {\n\t\t\t\temit(FOCUS);\n\t\t\t\tif (autoFocus) {\n\t\t\t\t\tfocus();\n\t\t\t\t}\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t}\n\tif (globalThis.document?.addEventListener) {\n\t\tglobalThis.document.addEventListener(\n\t\t\t\"visibilitychange\",\n\t\t\t() => {\n\t\t\t\tif (globalThis.document.visibilityState === \"visible\") {\n\t\t\t\t\temit(FOCUS);\n\t\t\t\t\tif (autoFocus) {\n\t\t\t\t\t\tfocus();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\temit(BLUR);\n\t\t\t\t}\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t}\n}\n\n/**\n * enable/disable swipe on WebView.\n * @param [enable=true] - enable or disable swipe.\n * @category Application\n */\nexport function enableSwipe(enable?: boolean) {\n\tconst moveEvent = pointerEvent\n\t\t? \"pointermove\"\n\t\t: touchEvent\n\t\t\t? \"touchmove\"\n\t\t\t: \"mousemove\";\n\tif (enable !== false) {\n\t\tif (!swipeEnabled) {\n\t\t\tglobalThis.document.removeEventListener(moveEvent, disableSwipeFn);\n\t\t\tswipeEnabled = true;\n\t\t}\n\t} else if (swipeEnabled) {\n\t\tglobalThis.document.addEventListener(moveEvent, disableSwipeFn, {\n\t\t\tpassive: false,\n\t\t});\n\t\tswipeEnabled = false;\n\t}\n}\n\n// `requestFullscreen` / `exitFullscreen` were deprecated in 19.7.0 — the\n// implementations live with the rest of the engine's deprecation surface in\n// `lang/deprecated.js`. Re-exported here so they stay reachable as\n// `me.device.requestFullscreen` / `me.device.exitFullscreen` for backwards\n// compat; the disable below covers the re-export of `@deprecated` symbols\n// (the whole point — consumers still get the JSDoc + runtime warning when\n// they call them).\n// eslint-disable-next-line @typescript-eslint/no-deprecated\nexport { exitFullscreen, requestFullscreen } from \"../lang/deprecated.js\";\n\n/**\n * Return a string representing the orientation of the device screen.\n * It can be \"any\", \"natural\", \"landscape\", \"portrait\", \"portrait-primary\", \"portrait-secondary\", \"landscape-primary\", \"landscape-secondary\"\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation\n * @returns the screen orientation\n * @category Application\n */\nexport function getScreenOrientation(): string {\n\tconst PORTRAIT = \"portrait\";\n\tconst LANDSCAPE = \"landscape\";\n\n\tconst screen = globalThis.screen;\n\n\t// first try using \"standard\" values\n\tif (screenOrientation) {\n\t\tconst orientation = prefixed(\n\t\t\t\"orientation\",\n\t\t\tscreen as unknown as Record<string, unknown>,\n\t\t);\n\t\tif (\n\t\t\ttypeof orientation === \"object\" &&\n\t\t\torientation !== null &&\n\t\t\ttypeof (orientation as ScreenOrientation).type === \"string\"\n\t\t) {\n\t\t\t// Screen Orientation API specification\n\t\t\treturn (orientation as ScreenOrientation).type;\n\t\t} else if (typeof orientation === \"string\") {\n\t\t\t// moz/ms-orientation are strings\n\t\t\treturn orientation;\n\t\t}\n\t}\n\n\t// check using the deprecated API\n\tif (\n\t\ttypeof (globalThis as { orientation?: number }).orientation === \"number\"\n\t) {\n\t\treturn Math.abs((globalThis as { orientation: number }).orientation) === 90\n\t\t\t? LANDSCAPE\n\t\t\t: PORTRAIT;\n\t}\n\n\t// fallback to window size check\n\treturn globalThis.outerWidth > globalThis.outerHeight ? LANDSCAPE : PORTRAIT;\n}\n\n/**\n * locks the device screen into the specified orientation.<br>\n * This method only works for installed Web apps or for Web pages in full-screen mode.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Screen/lockOrientation\n * @param orientation - The orientation into which to lock the screen.\n * @returns true if the orientation was unsuccessfully locked\n * @category Application\n */\nexport function lockOrientation(orientation: string | string[]) {\n\tconst screen = globalThis.screen as ScreenLegacy | undefined;\n\tif (typeof screen !== \"undefined\") {\n\t\tconst _lockOrientation = prefixed(\n\t\t\t\"lockOrientation\",\n\t\t\tscreen as unknown as Record<string, unknown>,\n\t\t) as ((orientation: string | string[]) => boolean) | undefined;\n\t\tif (typeof _lockOrientation !== \"undefined\") {\n\t\t\treturn _lockOrientation(orientation);\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * unlocks the device screen into the specified orientation.<br>\n * This method only works for installed Web apps or for Web pages in full-screen mode.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Screen/lockOrientation\n * @returns true if the orientation was unsuccessfully unlocked\n * @category Application\n */\nexport function unlockOrientation() {\n\tconst screen = globalThis.screen as ScreenLegacy | undefined;\n\tif (typeof screen !== \"undefined\") {\n\t\tconst _unlockOrientation = prefixed(\n\t\t\t\"unlockOrientation\",\n\t\t\tscreen as unknown as Record<string, unknown>,\n\t\t) as (() => boolean) | undefined;\n\t\tif (typeof _unlockOrientation !== \"undefined\") {\n\t\t\treturn _unlockOrientation();\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * return true if the device screen orientation is in Portrait mode\n * @category Application\n */\nexport function isPortrait() {\n\treturn getScreenOrientation().includes(\"portrait\");\n}\n\n/**\n * return true if the device screen orientation is in Portrait mode\n * @category Application\n */\nexport function isLandscape() {\n\treturn getScreenOrientation().includes(\"landscape\");\n}\n\n/**\n * return the device storage\n * @see save\n * @param [type=\"local\"] - storage type (currently only `\"local\"` is supported)\n * @returns a reference to the device storage\n * @category Application\n */\nexport function getStorage(type: string = \"local\") {\n\tswitch (type) {\n\t\tcase \"local\":\n\t\t\treturn save;\n\n\t\tdefault:\n\t\t\tthrow new Error(`storage type ${type} not supported`);\n\t}\n}\n\n/**\n * return the parent DOM element for the given parent name or HTMLElement object\n * @param element - the parent element name or a HTMLElement object\n * @returns the parent Element\n * @category Application\n */\nexport function getParentElement(element: string | HTMLElement) {\n\tlet target: HTMLElement | (Node & ParentNode) = getElement(element);\n\n\tif (target.parentNode !== null) {\n\t\ttarget = target.parentNode;\n\t}\n\n\treturn target as HTMLElement;\n}\n\n/**\n * return the DOM element for the given element name or HTMLElement object\n * @param element - the parent element name or a HTMLElement object\n * @returns the corresponding DOM Element (falls back to `document.body` when the lookup fails or the input isn't an HTMLElement)\n * @category Application\n */\nexport function getElement(element: string | HTMLElement): HTMLElement {\n\tlet target: HTMLElement | null = null;\n\n\tif (typeof element !== \"undefined\") {\n\t\tif (typeof element === \"string\") {\n\t\t\ttarget = globalThis.document.getElementById(element);\n\t\t} else if (\n\t\t\ttypeof element === \"object\" &&\n\t\t\telement.nodeType === Node.ELEMENT_NODE\n\t\t) {\n\t\t\ttarget = element;\n\t\t}\n\t}\n\n\t// fallback, if invalid target or non HTMLElement object\n\tif (!target) {\n\t\t//default to document.body\n\t\ttarget = globalThis.document.body;\n\t}\n\n\treturn target;\n}\n\n/**\n * returns the size of the given HTMLElement and its position relative to the viewport\n * <br><img src=\"images/element-box-diagram.png\"/>\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect\n * @param element - an HTMLElement object\n * @returns the size and position of the element relatively to the viewport\n * @category Application\n */\nexport function getElementBounds(element: string | HTMLElement): DOMRect {\n\tif (\n\t\ttypeof element === \"object\" &&\n\t\telement !== globalThis.document.body &&\n\t\ttypeof element.getBoundingClientRect !== \"undefined\"\n\t) {\n\t\treturn element.getBoundingClientRect();\n\t} else {\n\t\tdomRect.width = domRect.right = globalThis.innerWidth;\n\t\tdomRect.height = domRect.bottom = globalThis.innerHeight;\n\t\treturn domRect as DOMRect;\n\t}\n}\n\n/**\n * returns the size of the given HTMLElement Parent and its position relative to the viewport\n * <br><img src=\"images/element-box-diagram.png\"/>\n * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect\n * @param element - an HTMLElement object\n * @returns the size and position of the given element parent relative to the viewport\n * @category Application\n */\nexport function getParentBounds(element: string | HTMLElement) {\n\treturn getElementBounds(getParentElement(element));\n}\n\n/**\n * Returns true if the device supports WebGL 2 — the version the WebGL\n * renderer requires since 20.0. This probes the same context type that\n * renderer construction requests, so the two can never disagree.\n *\n * When it returns false, `renderer: video.AUTO` falls back to the Canvas\n * renderer and `renderer: video.WEBGL` throws at construction time.\n * @param [options] - context creation options\n * @param [options.failIfMajorPerformanceCaveat=true] - if true, report WebGL as unsupported when the browser warns that a context would perform dramatically worse than a native application (e.g. a software rasterizer). Set false to accept WebGL on blocklisted drivers or software renderers.\n * @returns true if WebGL 2 is supported\n * @category Application\n */\nexport function isWebGLSupported(options?: {\n\tfailIfMajorPerformanceCaveat?: boolean;\n}): boolean {\n\tif (WebGLSupport === -1) {\n\t\ttry {\n\t\t\tconst canvas = globalThis.document.createElement(\"canvas\");\n\t\t\tconst ctxOptions: WebGLContextAttributes = {\n\t\t\t\tstencil: true,\n\t\t\t\t...(options?.failIfMajorPerformanceCaveat !== undefined && {\n\t\t\t\t\tfailIfMajorPerformanceCaveat: options.failIfMajorPerformanceCaveat,\n\t\t\t\t}),\n\t\t\t};\n\t\t\t// WebGL 2 only since 20.0 (#1509) — this gate must agree with\n\t\t\t// the context actually created by the WebGL renderer (which\n\t\t\t// requests \"webgl2\"), so AUTO falls back to Canvas exactly when\n\t\t\t// renderer construction would fail\n\t\t\tconst _supported = !!(\n\t\t\t\tglobalThis.WebGL2RenderingContext &&\n\t\t\t\tcanvas.getContext(\"webgl2\", ctxOptions)\n\t\t\t);\n\t\t\tWebGLSupport = _supported ? 1 : 0;\n\t\t} catch {\n\t\t\tWebGLSupport = 0;\n\t\t}\n\t}\n\treturn WebGLSupport === 1;\n}\n\n/**\n * Makes a request to bring this device window to the front.\n * @example\n *  if (clicked) {\n *    me.device.focus();\n *  }\n * @category Application\n */\nexport function focus() {\n\tif (typeof globalThis.focus === \"function\") {\n\t\tglobalThis.focus();\n\t}\n}\n\n/**\n * Enable monitor of the device accelerator to detect the amount of physical force of acceleration the device is receiving.\n * (one some device a first user gesture will be required before calling this function)\n * @see device.accelerationX\n * @see device.accelerationY\n * @see device.accelerationZ\n * @see http://www.mobilexweb.com/samples/ball.html\n * @see http://www.mobilexweb.com/blog/safari-ios-accelerometer-websockets-html5\n * @returns false if not supported or permission not granted by the user\n * @example\n * // try to enable device accelerometer event on user gesture\n * me.input.registerPointerEvent(\"pointerleave\", app.viewport, function() {\n *     if (me.device.watchAccelerometer() === true) {\n *         // Success\n *         me.input.releasePointerEvent(\"pointerleave\", app.viewport);\n *     } else {\n *         // ... fail at enabling the device accelerometer event\n *     }\n * });\n * @category Application\n */\nexport function watchAccelerometer() {\n\tif (hasAccelerometer && !accelInitialized) {\n\t\tconst DOECtor = DeviceOrientationEvent as DeviceOrientationEventCtor;\n\t\tif (DOECtor && typeof DOECtor.requestPermission === \"function\") {\n\t\t\tDOECtor.requestPermission()\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (response === \"granted\") {\n\t\t\t\t\t\t// add a listener for the devicemotion event\n\t\t\t\t\t\tglobalThis.addEventListener(\"devicemotion\", onDeviceMotion, false);\n\t\t\t\t\t\taccelInitialized = true;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(console.error);\n\t\t} else {\n\t\t\t// add a listener for the devicemotion event\n\t\t\tglobalThis.addEventListener(\"devicemotion\", onDeviceMotion, false);\n\t\t\taccelInitialized = true;\n\t\t}\n\t}\n\treturn accelInitialized;\n}\n\n/**\n * unwatch Accelerometer event\n * @category Application\n */\nexport function unwatchAccelerometer() {\n\tif (accelInitialized) {\n\t\t// remove the listener for the devicemotion event\n\t\tglobalThis.removeEventListener(\"devicemotion\", onDeviceMotion, false);\n\t\taccelInitialized = false;\n\t}\n}\n\n/**\n * Enable monitor of the device orientation to detect the current orientation of the device as compared to the Earth coordinate frame.\n * (one some device a first user gesture will be required before calling this function)\n * @see device.alpha\n * @see device.beta\n * @see device.gamma\n * @returns false if not supported or permission not granted by the user\n * @example\n * // try to enable device orientation event on user gesture\n * me.input.registerPointerEvent(\"pointerleave\", app.viewport, function() {\n *     if (me.device.watchDeviceOrientation() === true) {\n *         // Success\n *         me.input.releasePointerEvent(\"pointerleave\", app.viewport);\n *     } else {\n *         // ... fail at enabling the device orientation event\n *     }\n * });\n * @category Application\n */\nexport function watchDeviceOrientation() {\n\tif (hasDeviceOrientation && !deviceOrientationInitialized) {\n\t\tconst DOECtor = DeviceOrientationEvent as DeviceOrientationEventCtor;\n\t\tif (typeof DOECtor.requestPermission === \"function\") {\n\t\t\tDOECtor.requestPermission()\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (response === \"granted\") {\n\t\t\t\t\t\tglobalThis.addEventListener(\n\t\t\t\t\t\t\t\"deviceorientation\",\n\t\t\t\t\t\t\tonDeviceRotate,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tdeviceOrientationInitialized = true;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(console.error);\n\t\t} else {\n\t\t\tglobalThis.addEventListener(\"deviceorientation\", onDeviceRotate, false);\n\t\t\tdeviceOrientationInitialized = true;\n\t\t}\n\t}\n\treturn deviceOrientationInitialized;\n}\n\n/**\n * unwatch Device orientation event\n * @category Application\n */\nexport function unwatchDeviceOrientation() {\n\tif (deviceOrientationInitialized) {\n\t\tglobalThis.removeEventListener(\"deviceorientation\", onDeviceRotate, false);\n\t\tdeviceOrientationInitialized = false;\n\t}\n}\n\n/**\n * the vibrate method pulses the vibration hardware on the device, <br>\n * If the device doesn't support vibration, this method has no effect. <br>\n * If a vibration pattern is already in progress when this method is called,\n * the previous pattern is halted and the new one begins instead.\n * @param pattern - pattern of vibration and pause intervals\n * @example\n * // vibrate for 1000 ms\n * me.device.vibrate(1000);\n * // or alternatively\n * me.device.vibrate([1000]);\n * // vibrate for 50 ms, be still for 100 ms, and then vibrate for 150 ms:\n * me.device.vibrate([50, 100, 150]);\n * // cancel any existing vibrations\n * me.device.vibrate(0);\n * @category Application\n */\nexport function vibrate(pattern: number | number[]) {\n\tif (\n\t\ttypeof globalThis.navigator !== \"undefined\" &&\n\t\ttypeof globalThis.navigator.vibrate === \"function\"\n\t) {\n\t\tglobalThis.navigator.vibrate(pattern);\n\t}\n}\n\n/**\n * detect if the given video format is supported\n * @param codec - the video format to check for support\n * @returns return true if the given video format is supported\n * @category Application\n */\nexport function hasVideoFormat(\n\tcodec: \"h264\" | \"h265\" | \"ogg\" | \"mp4\" | \"m4v\" | \"webm\" | \"vp9\" | \"hls\",\n): boolean {\n\tlet result = false;\n\tif (hasVideo) {\n\t\tif (typeof videoCodecs === \"undefined\") {\n\t\t\t// check for support\n\t\t\tconst videoElement = globalThis.document.createElement(\"video\");\n\t\t\tvideoCodecs = {\n\t\t\t\th264: videoElement\n\t\t\t\t\t.canPlayType('video/mp4; codecs=\"avc1.42E01E\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\th265: videoElement\n\t\t\t\t\t.canPlayType('video/mp4; codecs=\"hev1\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\togg: videoElement\n\t\t\t\t\t.canPlayType('video/ogg; codecs=\"theora\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\tmp4: videoElement\n\t\t\t\t\t.canPlayType('video/mp4; codecs=\"avc1.42E01E\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\tm4v: videoElement.canPlayType(\"video/x-m4v\").replace(/^no$/, \"\"),\n\t\t\t\twebm: videoElement\n\t\t\t\t\t.canPlayType('video/webm; codecs=\"vp8, vorbis\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\tvp9: videoElement\n\t\t\t\t\t.canPlayType('video/webm; codecs=\"vp9\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t\thls: videoElement\n\t\t\t\t\t.canPlayType('application/x-mpegURL; codecs=\"avc1.42E01E\"')\n\t\t\t\t\t.replace(/^no$/, \"\"),\n\t\t\t};\n\t\t}\n\t\tresult = !!videoCodecs[codec];\n\t}\n\treturn result;\n}\n","import { capitalize } from \"./string.js\";\n\n/**\n * Known agent vendors\n */\nconst vendors = [\"ms\", \"MS\", \"moz\", \"webkit\", \"o\"];\n\n/**\n * Get a vendor-prefixed property\n * @param name - Property name\n * @param [obj] - Object or element reference to access\n * @returns Value of property\n */\nexport function prefixed(name: string, obj?: Record<string, unknown>) {\n\tobj = obj || globalThis;\n\tif (name in obj) {\n\t\treturn obj[name];\n\t}\n\n\tconst uc_name = capitalize(name);\n\n\tlet result;\n\tvendors.some((vendor) => {\n\t\tconst name = vendor + uc_name;\n\t\treturn (result = name in obj ? obj[name] : undefined);\n\t});\n\treturn result;\n}\n\n/**\n * Set a vendor-prefixed property\n * @param name - Property name\n * @param value - Property value\n * @param [obj] - Object or element reference to access\n * @returns true if one of the vendor-prefixed property was found\n */\nexport function setPrefixed(\n\tname: string,\n\tvalue: unknown,\n\tobj?: Record<string, unknown>,\n) {\n\tobj = obj || globalThis;\n\tif (name in obj) {\n\t\tobj[name] = value;\n\t\treturn;\n\t}\n\n\tconst uc_name = capitalize(name);\n\n\tvendors.some((vendor) => {\n\t\tconst name = vendor + uc_name;\n\t\tif (name in obj) {\n\t\t\tobj[name] = value;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t});\n\n\treturn false;\n}\n","/**\n * a collection of string utility functions\n */\n\n/**\n * converts the first character of the given string to uppercase\n * @param str - the string to be capitalized\n * @returns the capitalized string\n */\nexport function capitalize(str: string) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * returns true if the given string contains a numeric integer or float value\n * @param str - the string to be tested\n * @returns true if string contains only digits\n */\nexport function isNumeric(str: string) {\n\tif (typeof str === \"string\") {\n\t\tstr = str.trim();\n\t}\n\treturn !Number.isNaN(str) && /^[+-]?(\\d+(\\.\\d+)?|\\.\\d+)$/.test(str);\n}\n\n/**\n * returns true if the given string contains a true or false\n * @param str - the string to be tested\n * @returns true if the string is either true or false\n */\nexport function isBoolean(str: string) {\n\tconst trimmed = str.trim();\n\treturn trimmed === \"true\" || trimmed === \"false\";\n}\n\n/**\n * convert a string to the corresponding hexadecimal value\n * @param str - the string to be converted\n * @returns the converted hexadecimal value\n */\nexport function toHex(str: string) {\n\tlet res = \"\";\n\tlet c = 0;\n\twhile (c < str.length) {\n\t\tres += str.charCodeAt(c++).toString(16);\n\t}\n\treturn res;\n}\n\n/**\n * returns true if the given string is a data url in the `data:[<mediatype>][;base64],<data>` format.\n * (this will not test the validity of the Data or Base64 encoding)\n * @param str - the string (url) to be tested\n * @returns true if the string is a data url\n */\nexport function isDataUrl(str: string) {\n\treturn /^data:(.+);base64,(.+)$/.test(str);\n}\n","/**\n * The device platform type\n * @namespace platform\n * @memberof device\n * ua the user agent string for the current device\n * iOS `true` if the device is an iOS platform\n * android `true` if the device is an Android platform\n * android2 `true` if the device is an Android 2.x platform (deprecated)\n * linux `true` if the device is a Linux platform\n * chromeOS `true` if the device is running on ChromeOS.\n * wp `true` if the device is a Windows Phone platform (deprecated)\n * BlackBerry `true` if the device is a BlackBerry platform (deprecated)\n * Kindle `true` if the device is a Kindle platform (deprecated)\n * ejecta `true` if running under Ejecta\n * isWeixin `true` if running under Wechat\n * nodeJS `true` if running under node.js\n * isMobile `true` if a mobile device\n * webApp `true` if running as a standalone web app\n */\n\nexport const ua =\n\ttypeof globalThis.navigator !== \"undefined\"\n\t\t? globalThis.navigator.userAgent\n\t\t: \"\";\n\n// iPadOS 13+ (Sept 2019) ships Safari with the desktop Mac UA — no `iPad`\n// token. Feature-detect the iPad-on-Mac-UA case so `iOS` / `isMobile`\n// don't miss every modern iPad:\n//\n// - `navigator.platform === \"MacIntel\"` is the Mac identity Apple keeps\n//   frozen on Apple Silicon Macs/iPads for backwards compat (same trick\n//   as `Win32` on 64-bit Windows). NOT a CPU check — `MacIntel` persists\n//   on M1/M2/M3/M4.\n// - `maxTouchPoints > 1` excludes actual Macs (no touchscreens) and\n//   keeps real iPads (multi-touch digitizers).\n//\n/**\n * iPad-on-Mac-UA predicate. Exported so the spec file can assert the\n * SAME function the module evaluates at load time (no drift between\n * docs and implementation), but marked `@internal` because it's a\n * test-seam, not a stable public API — the engine reserves the right\n * to change / inline / rename it without a breaking-change bump.\n *\n * Parameter shape is `Partial<Pick<Navigator, ...>>` rather than a\n * named alias so no engine-defined type leaks into the emitted\n * `.d.ts` (`tsconfig.build.json` doesn't currently set\n * `stripInternal`).\n * @param nav - a `navigator`-shaped object (or `undefined` for Node/SSR)\n * @returns `true` when `nav` looks like an iPad reporting under the iPadOS-13+ desktop Mac UA\n * @internal\n */\nexport function isIPadOnMacUA(\n\tnav: Partial<Pick<Navigator, \"platform\" | \"maxTouchPoints\">> | undefined,\n): boolean {\n\treturn nav?.platform === \"MacIntel\" && (nav?.maxTouchPoints ?? 0) > 1;\n}\n\nconst _nav =\n\ttypeof globalThis.navigator !== \"undefined\"\n\t\t? globalThis.navigator\n\t\t: undefined;\n\nexport const iOS = /iPhone|iPad|iPod/i.test(ua) || isIPadOnMacUA(_nav);\nexport const android = /Android/i.test(ua);\n/**\n * @deprecated since 19.7.0 — Android 2.x predates 2012. Will be removed in 20.x.\n */\nexport const android2 = /Android 2/i.test(ua);\nexport const linux = /Linux/i.test(ua);\nexport const chromeOS = /CrOS/.test(ua);\n/**\n * @deprecated since 19.7.0 — Windows Phone was EOL'd by Microsoft in 2017. Will be removed in 20.x.\n */\nexport const wp = /Windows Phone/i.test(ua);\n/**\n * @deprecated since 19.7.0 — BlackBerry stopped shipping BB10 devices in 2016. Will be removed in 20.x.\n */\nexport const BlackBerry = /BlackBerry/i.test(ua);\n/**\n * @deprecated since 19.7.0 — Kindle has a negligible market share and behaves like Android. Will be removed in 20.x.\n */\nexport const Kindle = /Kindle|Silk.*Mobile Safari/i.test(ua);\nexport const ejecta = \"ejecta\" in globalThis;\nexport const isWeixin = /MicroMessenger/i.test(ua);\n// Node.js detection — `process.release.name === \"node\"` is the\n// official runtime identifier (set by V8 / Node.js itself). The cast\n// is contained in one variable so the rest of the check reads as plain\n// dotted access without repeating `as unknown as ...` three times.\nconst _proc = (globalThis as { process?: { release?: { name: string } } })\n\t.process;\nexport const nodeJS =\n\ttypeof _proc !== \"undefined\" &&\n\ttypeof _proc.release !== \"undefined\" &&\n\t_proc.release.name === \"node\";\n// `Mobi` substring matches Firefox + Chrome + Safari mobile UAs in\n// 2026 (MDN's recommended fallback); the `iOS || android` chain\n// catches the few outliers and the iPad-on-Mac-UA case. Dropped\n// `wp` / `BlackBerry` / `Kindle` — the underlying platforms are EOL\n// and the regexes were burning cycles on every page load for\n// hardware nobody ships.\nexport const isMobile = /Mobi/i.test(ua) || iOS || android;\nexport const webApp =\n\t(typeof globalThis.navigator !== \"undefined\" &&\n\t\t\"standalone\" in globalThis.navigator &&\n\t\tglobalThis.navigator.standalone === true) ||\n\t(typeof globalThis.matchMedia !== \"undefined\" &&\n\t\tglobalThis.matchMedia(\"(display-mode: standalone)\").matches);\n","import { nodeJS } from \"./platform.ts\";\n\n/**\n * Execute a callback when the DOM is ready.\n * In Node.js, when there is no DOM, or when the DOM is already parsed,\n * the callback is deferred to the next microtask.\n * @param fn - callback to execute when the DOM is ready\n * @ignore\n */\nexport const DOMContentLoaded = (fn: () => void) => {\n\tif (\n\t\tnodeJS ||\n\t\ttypeof globalThis.document === \"undefined\" ||\n\t\tglobalThis.document.readyState !== \"loading\"\n\t) {\n\t\t// DOM already ready (or no DOM environment), defer to next microtask\n\t\tvoid Promise.resolve().then(fn);\n\t} else {\n\t\tglobalThis.document.addEventListener(\"DOMContentLoaded\", fn, {\n\t\t\tonce: true,\n\t\t});\n\t}\n};\n","/**\n * an event system based on nodeJS EventEmitter interface\n */\n\nimport type Application from \"../application/application.ts\";\nimport Pointer from \"../input/pointer.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport { Draggable } from \"../renderable/draggable.js\";\nimport type Stage from \"../state/stage.ts\";\nimport Renderer from \"../video/renderer.js\";\nimport type { Texture2dSource } from \"../video/texture/texture2d.ts\";\nimport { EventEmitter } from \"./eventEmitter.js\";\n\n/**\n * event when the DOM is Ready is booting\n * @see event.on\n */\nexport const DOM_READY = \"dom_ready\";\n\n/**\n * event when the system is booting\n * @see event.on\n */\nexport const BOOT = \"me.boot\";\n\n/**\n * event generated when the system update the engine and the renderer by one step\n * @see event.on\n */\nexport const TICK = \"me.tick\";\n\n/**\n * event generated when the main browser or window is losing focus\n * @see event.on\n */\nexport const BLUR = \"me.blur\";\n\n/**\n * event generated when the main browser or window is gaining back focus\n * @see event.on\n */\nexport const FOCUS = \"me.focus\";\n\n/**\n * event when the game is paused <br>\n * Data passed : none <br>\n * @see event.on\n */\nexport const STATE_PAUSE = \"me.state.onPause\";\n\n/**\n * event for when the game is resumed <br>\n * Data passed : {number} time in ms the game was paused\n * @see event.on\n */\nexport const STATE_RESUME = \"me.state.onResume\";\n\n/**\n * event when the game is stopped <br>\n * Data passed : none <br>\n * @see event.on\n */\nexport const STATE_STOP = \"me.state.onStop\";\n\n/**\n * event for when the game is restarted <br>\n * Data passed : {number} time in ms the game was stopped\n * @see event.on\n */\nexport const STATE_RESTART = \"me.state.onRestart\";\n\n/**\n * event for when the changing to a different stage\n * @see event.on\n */\nexport const STATE_CHANGE = \"me.state.onChange\";\n\n/**\n * event for when a stage is resetted\n * @see event.on\n */\nexport const STAGE_RESET = \"me.stage.onReset\";\n\n/**\n * event for when the video is initialized<br>\n * Data passed : {Renderer} the renderer instance created\n * @see video.init\n * @see event.on\n */\nexport const VIDEO_INIT = \"me.video.onInit\";\n\n/**\n * event for when the game manager is initialized <br>\n * Data passed : none <br>\n * @see event.on\n */\nexport const GAME_INIT = \"me.game.onInit\";\n\n/**\n * event for when the game manager is resetted <br>\n * Data passed : none <br>\n * @see event.on\n */\nexport const GAME_RESET = \"me.game.onReset\";\n\n/**\n * event for when the engine is about to start a new game loop\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const GAME_BEFORE_UPDATE = \"me.game.beforeUpdate\";\n\n/**\n * event for the end of the update loop\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const GAME_AFTER_UPDATE = \"me.game.afterUpdate\";\n\n/**\n * Event for when the game is updated (will be impacted by frame skip, frame interpolation and pause/resume state) <br>\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const GAME_UPDATE = \"me.game.onUpdate\";\n\n/**\n * Event for the end of the draw loop\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const GAME_BEFORE_DRAW = \"me.game.beforeDraw\";\n\n/**\n * Event for the start of the draw loop\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const GAME_AFTER_DRAW = \"me.game.afterDraw\";\n\n/**\n * Event for when the physic world is updated\n * Data passed : {number} time the current time stamp\n * @see event.on\n */\nexport const WORLD_STEP = \"me.world.step\";\n\n/**\n * Event for when a level is loaded <br>\n * Data passed : {string} Level Name\n * @see event.on\n */\nexport const LEVEL_LOADED = \"me.game.onLevelLoaded\";\n\n/**\n * Event for when everything has loaded <br>\n * Data passed : none <br>\n * @see event.on\n */\nexport const LOADER_COMPLETE = \"me.loader.onload\";\n\n/**\n * Event for displaying a load progress indicator <br>\n * Data passed : {number} [0 .. 1], {Resource} resource object<br>\n * @see event.on\n */\nexport const LOADER_PROGRESS = \"me.loader.onProgress\";\n\n/**\n * Event for when an error occur during preloading <br>\n * Data passed : {Resource} resource object<br>\n * @see event.on\n */\nexport const LOADER_ERROR = \"me.loader.onError\";\n\n/**\n * Event for pressing a binded key <br>\n * Data passed : {string} user-defined action, {number} keyCode,\n * {boolean} edge state <br>\n * Edge-state is for detecting \"locked\" key bindings. When a locked key\n * is pressed and held, the first event will have the third argument\n * set true. Subsequent events will continue firing with the third\n * argument set false.\n * @see event.on\n * @example\n * me.input.bindKey(me.input.KEY.X, \"jump\", true); // Edge-triggered\n * me.input.bindKey(me.input.KEY.Z, \"shoot\"); // Level-triggered\n * me.event.on(me.event.KEYDOWN, (action, keyCode, edge) => {\n *   // Checking bound keys\n *   if (action === \"jump\") {\n *       if (edge) {\n *           this.doJump();\n *       }\n *\n *       // Make character fall slower when holding the jump key\n *       this.vel.y = this.body.gravity;\n *   }\n * });\n */\nexport const KEYDOWN = \"me.input.keydown\";\n\n/**\n * Event for releasing a binded key <br>\n * Data passed : {string} user-defined action, {number} keyCode\n * @see event.on\n * @example\n * me.event.on(me.event.KEYUP, (action, keyCode) => {\n *   // Checking unbound keys\n *   if (keyCode == me.input.KEY.ESC) {\n *       if (me.state.isPaused()) {\n *           me.state.resume();\n *       }\n *       else {\n *           me.state.pause();\n *       }\n *   }\n * });\n */\nexport const KEYUP = \"me.input.keyup\";\n\n/**\n * Event for when a gamepad is connected <br>\n * Data passed : {object} gamepad object\n * @see event.on\n */\nexport const GAMEPAD_CONNECTED = \"gamepad.connected\";\n\n/**\n * Event for when a gamepad is disconnected <br>\n * Data passed : {object} gamepad object\n * @see event.on\n */\nexport const GAMEPAD_DISCONNECTED = \"gamepad.disconnected\";\n\n/**\n * Event for when gamepad button/axis state is updated <br>\n * Data passed : {number} index <br>\n * Data passed : {string} type : \"axes\" or \"buttons\" <br>\n * Data passed : {number} button <br>\n * Data passed : {number} current.value <br>\n * Data passed : {boolean} current.pressed\n * @see event.on\n */\nexport const GAMEPAD_UPDATE = \"gamepad.update\";\n\n/**\n * Event for pointermove events on the screen area <br>\n * Data passed : {me.Pointer} a Pointer object\n * @see event.on\n */\nexport const POINTERMOVE = \"me.event.pointermove\";\n\n/**\n * Event for onPointerLockChange event <br>\n * Data passed : {boolean} pointer lock status (true/false)\n * @see event.on\n */\nexport const POINTERLOCKCHANGE = \"me.event.pointerlockChange\";\n\n/**\n * Event for dragstart events on a Draggable entity <br>\n * Data passed:\n * {object} the drag event <br>\n * {object} the Draggable entity\n * @see event.on\n */\nexport const DRAGSTART = \"me.game.dragstart\";\n\n/**\n * Event for dragend events on a Draggable entity <br>\n * Data passed:\n * {object} the drag event <br>\n * {object} the Draggable entity\n * @see event.on\n */\nexport const DRAGEND = \"me.game.dragend\";\n\n/**\n * Event for when the (browser) window is resized <br>\n * Data passed : {Event} Event object\n * @see event.on\n */\nexport const WINDOW_ONRESIZE = \"globalThis.onresize\";\n\n/**\n * Event for when the canvas is resized <br>\n * (this usually follows a WINDOW_ONRESIZE event).<br>\n * Data passed : {number} canvas width <br>\n * Data passed : {number} canvas height\n * @see event.on\n */\nexport const CANVAS_ONRESIZE = \"canvas.onresize\";\n\n/**\n * Event for when the viewport is resized <br>\n * (this usually follows a WINDOW_ONRESIZE event, when using the `flex` scaling mode is used and after the viewport was updated).<br>\n * Data passed : {number} viewport width <br>\n * Data passed : {number} viewport height <br>\n * Data passed : {Camera2d} a reference to the camera viewport being resized\n * @see event.on\n */\nexport const VIEWPORT_ONRESIZE = \"viewport.onresize\";\n\n/**\n * Event for when the device is rotated <br>\n * Data passed : {Event} Event object <br>\n * @see event.on\n */\nexport const WINDOW_ONORIENTATION_CHANGE = \"globalThis.orientationchange\";\n\n/**\n * Event for when the (browser) window is scrolled <br>\n * Data passed : {Event} Event object\n * @see event.on\n */\nexport const WINDOW_ONSCROLL = \"globalThis.onscroll\";\n\n/**\n * Event for when the viewport position is updated <br>\n * Data passed : {me.Vector2d} viewport position vector\n * @see event.on\n */\nexport const VIEWPORT_ONCHANGE = \"viewport.onchange\";\n\n/**\n * Event for when the current context is lost <br>\n * Data passed : {me.Renderer} the current renderer instance\n * @see event.on\n */\nexport const ONCONTEXT_LOST = \"renderer.contextlost\";\n\n/**\n * Event for when the current context is restored <br>\n * Data passed : {me.Renderer} the current renderer instance`\n * @see event.on\n */\nexport const ONCONTEXT_RESTORED = \"renderer.contextrestored\";\n\n/**\n * Emitted by the GPU texture cache after a unit-reassignment reset\n * (either because the shader's sampler range was exhausted, or via\n * explicit `resetUnitAssignments()`). Subscribers — typically batchers\n * — should drop any cached `texture → unit` tracking they hold so they\n * re-bind on first use. Without that, the `texture === boundTextures[unit]`\n * short-circuit in `bindTexture2D` lets a batcher skip the actual\n * `gl.bindTexture` call while a different texture sits at the unit on\n * the GPU. The historical symptom was mesh draws coming out as black\n * silhouettes (sampling a transparent HUD Text canvas) and\n * sprite/bullet draws turning pure white (sampling the multi-material\n * white-pixel fallback).\n *\n * Named GPU-prefixed rather than WEBGL- because the same contract\n * applies to any GPU backend that batches textures across draw calls\n * (WebGL today, WebGPU once it lands).\n *\n * Data passed: none\n * @see event.on\n */\nexport const GPU_TEXTURE_CACHE_RESET = \"renderer.gpu.texturecachereset\";\n\n/**\n * Fired by the WebGL renderer at the start of a new render pass on the\n * active target. The signal is \"begin new render pass — invalidate any\n * target-scoped state you've accumulated\", not strictly \"the framebuffer\n * identity changed\". Emitted from:\n *\n * - `clear()` — frame start: the active target's color/stencil were just\n *   wiped, depth wasn't (lazy), so a new pass begins now.\n * - `clearRenderTarget()` — a non-camera FBO was just bound and color-\n *   cleared, beginning a new pass on it.\n * - `endPostEffect()` — the parent target was rebound (a physical change\n *   of framebuffer identity), beginning a new pass on it.\n *\n * Subscribers with target-scoped state (depth-clear flag, instance buffers\n * keyed on the target, sort accumulators, etc.) invalidate it here.\n * Batchers without target-scoped state don't subscribe and pay nothing —\n * same shape as `GPU_TEXTURE_CACHE_RESET`.\n *\n * The future WebGPU renderer will emit this when it begins a new\n * `RenderPassEncoder` (clear loadOps replace `clear()` calls in WebGPU's\n * declarative pass descriptors).\n *\n * Data passed: the emitting renderer. Subscribers holding per-renderer\n * state should ignore broadcasts from other renderer instances (several\n * Applications can coexist on one page); a missing argument (legacy\n * emitters) should be treated as \"mine\".\n * @see event.on\n */\nexport const RENDER_TARGET_CHANGED = \"renderer.target.changed\";\n\n/**\n * Fired when a {@link Texture2d} asset is destroyed, with its backing\n * drawable source. GPU-side caches keyed by source image — e.g. the lit\n * batcher's per-image normal-map textures — subscribe to release the GL\n * texture and drop their entry, so destroying a texture (a per-level\n * {@link NoiseTexture2d}, for instance) doesn't leak GPU memory.\n *\n * Data passed: the destroyed texture's backing canvas/image\n * @see event.on\n */\nexport const TEXTURE2D_DESTROYED = \"texture2d.destroyed\";\n\ninterface Events {\n\t[DOM_READY]: () => void;\n\t[BOOT]: () => void;\n\t[TICK]: (time: number) => void;\n\t[BLUR]: () => void;\n\t[FOCUS]: () => void;\n\t[STATE_PAUSE]: () => void;\n\t[STATE_RESUME]: (time: number) => void;\n\t[STATE_STOP]: () => void;\n\t[STATE_RESTART]: (time: number) => void;\n\t[STATE_CHANGE]: () => void;\n\t[STAGE_RESET]: (stage: Stage) => void;\n\t[VIDEO_INIT]: (renderer: Renderer) => void;\n\t[GAME_INIT]: (app: Application) => void;\n\t[GAME_RESET]: () => void;\n\t[GAME_BEFORE_UPDATE]: (time: number) => void;\n\t[GAME_AFTER_UPDATE]: (time: number) => void;\n\t[GAME_UPDATE]: (time: number) => void;\n\t[GAME_BEFORE_DRAW]: (time: DOMHighResTimeStamp) => void;\n\t[GAME_AFTER_DRAW]: (time: DOMHighResTimeStamp) => void;\n\t[WORLD_STEP]: (dt: number) => void;\n\t[LEVEL_LOADED]: (levelId: string) => void;\n\t[LOADER_COMPLETE]: () => void;\n\t[LOADER_PROGRESS]: (progress: number, resource: unknown) => void;\n\t[LOADER_ERROR]: (resource: unknown) => void;\n\t[KEYDOWN]: (\n\t\taction: string | undefined,\n\t\tkeyCode: number,\n\t\tedge: boolean,\n\t) => void;\n\t[KEYUP]: (action: string | undefined, keyCode: number) => void;\n\t[GAMEPAD_CONNECTED]: (gamepad: Gamepad) => void;\n\t[GAMEPAD_DISCONNECTED]: (gamepad: Gamepad) => void;\n\t[GAMEPAD_UPDATE]: (\n\t\tindex: string,\n\t\ttype: \"buttons\" | \"axes\",\n\t\tbutton: number,\n\t\tcurrent: { value: number; pressed: boolean },\n\t) => void;\n\t[POINTERMOVE]: (pointer: Pointer) => void;\n\t[POINTERLOCKCHANGE]: (locked: boolean) => void;\n\t[DRAGSTART]: (event: unknown, draggable: Draggable) => void;\n\t[DRAGEND]: (event: unknown, draggable: Draggable) => void;\n\t[WINDOW_ONRESIZE]: (event: Event) => void;\n\t[WINDOW_ONORIENTATION_CHANGE]: (event: Event) => void;\n\t[WINDOW_ONSCROLL]: (event: Event) => void;\n\t[CANVAS_ONRESIZE]: (width: number, height: number) => void;\n\t[VIEWPORT_ONRESIZE]: (width: number, height: number) => void;\n\t[VIEWPORT_ONCHANGE]: (position: Vector2d) => void;\n\t[ONCONTEXT_LOST]: (renderer: Renderer) => void;\n\t[ONCONTEXT_RESTORED]: (renderer: Renderer) => void;\n\t[GPU_TEXTURE_CACHE_RESET]: () => void;\n\t[RENDER_TARGET_CHANGED]: (renderer?: object) => void;\n\t[TEXTURE2D_DESTROYED]: (source: Texture2dSource) => void;\n}\n\nconst eventEmitter = new EventEmitter<Events>();\n\n/**\n * Add a listener for a given event.\n * @param eventName - The event name.\n * @param listener - The listener function.\n * @param [context] - The context to invoke the listener with.\n * @example\n * me.event.on(\"event-name\", myFunction, this);\n */\nexport function on<E extends keyof Events>(\n\teventName: E,\n\tlistener: Events[E],\n\tcontext?: any,\n) {\n\treturn eventEmitter.addListener(eventName, listener, context);\n}\n\n/**\n * Add a one-time listener for a given event.\n * @param eventName - The event name.\n * @param listener - The listener function.\n * @param [context] - The context to invoke the listener with.\n * @example\n * me.event.once(\"event-name\", myFunction, this);\n */\nexport function once<E extends keyof Events>(\n\teventName: E,\n\tlistener: Events[E],\n\tcontext?: any,\n) {\n\teventEmitter.addListenerOnce(eventName, listener, context);\n}\n\n/**\n * remove the given listener for a given event.\n * @param eventName - The event name.\n * @param listener - The listener function.\n * @param [context] - The context that was used when registering the listener.\n * @example\n * me.event.off(\"event-name\", myFunction);\n */\nexport function off<E extends keyof Events>(\n\teventName: E,\n\tlistener: Events[E],\n\tcontext?: any,\n) {\n\teventEmitter.removeListener(eventName, listener, context);\n}\n\n/**\n * emit (trigger) the specified event.\n * @param eventName - The event name.\n * @param args - arguments to be passed to the listener functions.\n * @example\n * me.event.emit(\"event-name\", arg1, arg2);\n */\nexport function emit<E extends keyof Events>(\n\teventName: E,\n\t...args: Parameters<Events[E]>\n) {\n\teventEmitter.emit(eventName, ...args);\n}\n\n/**\n * Check if a listener is registered for a given event.\n * @param eventName - The event name.\n * @param listener - The listener function.\n * @param [context] - The context that was used when registering the listener.\n * @returns true if the listener is registered.\n * @example\n * if (me.event.has(\"event-name\", myFunction)) {\n *     // listener is registered\n * }\n */\nexport function has<E extends keyof Events>(\n\teventName: E,\n\tlistener: Events[E],\n\tcontext?: any,\n) {\n\treturn eventEmitter.hasListener(eventName, listener, context);\n}\n","interface EventsMap {\n\t[event: string]: any;\n}\n\ninterface DefaultEvents extends EventsMap {\n\t[event: string]: (...args: any) => void;\n}\n\ntype ListenerEntry<F> = { fn: F; ctx: any };\n\nexport class EventEmitter<Events extends EventsMap = DefaultEvents> {\n\tprivate eventListeners: Partial<{\n\t\t[E in keyof Events]: ListenerEntry<Events[E]>[];\n\t}>;\n\tprivate eventListenersOnce: Partial<{\n\t\t[E in keyof Events]: ListenerEntry<Events[E]>[];\n\t}>;\n\n\tconstructor() {\n\t\tthis.eventListeners = {};\n\t\tthis.eventListenersOnce = {};\n\t}\n\n\taddListener<E extends keyof Events>(\n\t\tevent: E,\n\t\tlistener: Events[E],\n\t\tcontext?: any,\n\t) {\n\t\tlet eventListenerList = this.eventListeners[event];\n\t\tif (!eventListenerList) {\n\t\t\teventListenerList = [];\n\t\t\tthis.eventListeners[event] = eventListenerList;\n\t\t}\n\t\teventListenerList.push({ fn: listener, ctx: context });\n\n\t\treturn () => {\n\t\t\tthis.removeListener(event, listener, context);\n\t\t};\n\t}\n\n\taddListenerOnce<E extends keyof Events>(\n\t\tevent: E,\n\t\tlistener: Events[E],\n\t\tcontext?: any,\n\t) {\n\t\tlet eventListenerList = this.eventListenersOnce[event];\n\t\tif (!eventListenerList) {\n\t\t\teventListenerList = [];\n\t\t\tthis.eventListenersOnce[event] = eventListenerList;\n\t\t}\n\t\teventListenerList.push({ fn: listener, ctx: context });\n\t}\n\n\tremoveAllListeners(event?: keyof Events) {\n\t\tif (event) {\n\t\t\tdelete this.eventListeners[event];\n\t\t\tdelete this.eventListenersOnce[event];\n\t\t} else {\n\t\t\tthis.eventListeners = {};\n\t\t\tthis.eventListenersOnce = {};\n\t\t}\n\t}\n\n\tremoveListener<E extends keyof Events>(\n\t\tevent: E,\n\t\tlistener: Events[E],\n\t\tcontext?: any,\n\t) {\n\t\tconst listeners = this.eventListeners[event];\n\t\tif (listeners) {\n\t\t\tconst idx = listeners.findIndex(\n\t\t\t\t(entry) => entry.fn === listener && entry.ctx === context,\n\t\t\t);\n\t\t\tif (idx !== -1) {\n\t\t\t\tlisteners.splice(idx, 1);\n\t\t\t}\n\t\t}\n\n\t\tconst listenersOnce = this.eventListenersOnce[event];\n\t\tif (listenersOnce) {\n\t\t\tconst idx = listenersOnce.findIndex(\n\t\t\t\t(entry) => entry.fn === listener && entry.ctx === context,\n\t\t\t);\n\t\t\tif (idx !== -1) {\n\t\t\t\tlistenersOnce.splice(idx, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\temit<E extends keyof Events>(event: E, ...args: Parameters<Events[E]>) {\n\t\tconst listeners = this.eventListeners[event];\n\t\tif (listeners) {\n\t\t\t// iterate over a copy so that removeListener during emit is safe\n\t\t\tconst snapshot = listeners.slice();\n\t\t\tfor (const entry of snapshot) {\n\t\t\t\tentry.fn.apply(entry.ctx, args);\n\t\t\t}\n\t\t}\n\n\t\tconst listenersOnce = this.eventListenersOnce[event];\n\t\tif (listenersOnce) {\n\t\t\t// clear before invoking so re-entrant addListenerOnce is safe\n\t\t\tthis.eventListenersOnce[event] = [];\n\t\t\tfor (const entry of listenersOnce) {\n\t\t\t\tentry.fn.apply(entry.ctx, args);\n\t\t\t}\n\t\t}\n\t}\n\n\thasListener<E extends keyof Events>(\n\t\tevent: E,\n\t\tlistener: Events[E],\n\t\tcontext?: any,\n\t) {\n\t\treturn (\n\t\t\t!!this.eventListeners[event]?.some(\n\t\t\t\t(entry) => entry.fn === listener && entry.ctx === context,\n\t\t\t) ||\n\t\t\t!!this.eventListenersOnce[event]?.some(\n\t\t\t\t(entry) => entry.fn === listener && entry.ctx === context,\n\t\t\t)\n\t\t);\n\t}\n}\n","import { compare } from \"./semver.js\";\nimport { toHex } from \"./string.js\";\n\nexport * as agent from \"./agent.js\";\nexport * as array from \"./array.js\";\nexport * as file from \"./file.js\";\nexport * as function from \"./function.js\";\nexport * as string from \"./string.js\";\n\n/**\n * a collection of utility functions\n * @namespace utils\n */\n\n// guid default value\nlet GUID_base = \"\";\nlet GUID_index = 0;\n\n/**\n * Compare two version strings\n * @param v1 - First version string to compare\n * @param v2 - second version string to compare\n * @returns Return 0 if v1 == v2, or 1 if v1 is greater, or -1 if v2 is greater\n * @example\n * if (me.utils.checkVersion(\"7.0.0\") > 0) {\n *     console.error(\n *         \"melonJS is too old. Expected: 7.0.0, Got: 6.3.0\"\n *     );\n * }\n */\nexport function checkVersion(v1: string, v2: string) {\n\t// Convert to proper \"x.y.z\" format if necessary\n\tif (/^\\d+$/.test(v1)) {\n\t\tv1 += \".0.0\";\n\t} else if (/^\\d+\\.\\d+$/.test(v1)) {\n\t\tv1 += \".0\";\n\t}\n\tif (/^\\d+$/.test(v2)) {\n\t\tv2 += \".0.0\";\n\t} else if (/^\\d+\\.\\d+$/.test(v2)) {\n\t\tv2 += \".0\";\n\t}\n\treturn compare(v1, v2);\n}\n\n/**\n * parse the fragment (hash) from a URL and returns them into\n * @param [url] - an optional params string or URL containing fragment (hash) params to be parsed\n * @returns an object representing the deserialized params string.\n * [hitbox=false] draw the hitbox in the debug panel (if enabled)\n * [velocity=false] draw the entities velocity in the debug panel (if enabled)\n * [quadtree=false] draw the quadtree in the debug panel (if enabled)\n * [webgl=false] force the renderer to WebGL\n * [canvas=false] force the renderer to Canvas\n * [webgpu=false] force the renderer to WebGPU (opt-in only; never selected by `video.AUTO`)\n * [debug=false] display the debug panel (if preloaded)\n * [debugToggleKey=\"s\"] show/hide the debug panel (if preloaded)\n * @example\n * // http://www.example.com/index.html#debug&hitbox=true&mytag=value\n * let UriFragment = me.utils.getUriFragment();\n * console.log(UriFragment[\"mytag\"]); //> \"value\"\n */\nexport function getUriFragment(url?: string) {\n\tconst hash: Record<string, string | boolean> = {};\n\n\tif (typeof url === \"undefined\") {\n\t\tif (typeof globalThis.document !== \"undefined\") {\n\t\t\tconst location = globalThis.document.location;\n\n\t\t\tif (location && location.hash) {\n\t\t\t\turl = location.hash;\n\t\t\t} else {\n\t\t\t\t// No \"document.location\" exist for Wechat mini game platform.\n\t\t\t\treturn hash;\n\t\t\t}\n\t\t} else {\n\t\t\t// \"document\" undefined on node.js\n\t\t\treturn hash;\n\t\t}\n\t} else {\n\t\t// never cache if a url is passed as parameter\n\t\tconst index = url.indexOf(\"#\");\n\t\tif (index !== -1) {\n\t\t\turl = url.slice(index, url.length);\n\t\t} else {\n\t\t\treturn hash;\n\t\t}\n\t}\n\n\t// parse the url — split on both \"&\" and \"?\" so engine flags coexist\n\t// with SPA hash routers (e.g. `#/some-route?webgpu` in the examples)\n\turl\n\t\t.slice(1)\n\t\t.split(/[&?]/)\n\t\t.filter((value) => {\n\t\t\treturn value !== \"\";\n\t\t})\n\t\t.forEach((value) => {\n\t\t\tconst kv = value.split(\"=\");\n\t\t\tconst k = kv.shift()!;\n\t\t\tconst v = kv.join(\"=\");\n\t\t\thash[k] = v || true;\n\t\t});\n\n\treturn hash;\n}\n\n/**\n * reset the GUID Base Name\n * the idea here being to have a unique ID\n * per level / object\n * @ignore\n */\nexport function resetGUID(base: string, index = 0) {\n\t// also ensure it's only 8bit ASCII characters\n\tGUID_base = toHex(base.toUpperCase());\n\tGUID_index = index;\n}\n\n/**\n * create and return a very simple GUID\n * Game Unique ID\n * @ignore\n */\nexport function createGUID(index?: number) {\n\t// `index` is the caller's own id where it has one (a Tiled object passes\n\t// its map id, so its GUID stays readable as `<levelBase>-<tiledId>`);\n\t// everything else falls through to the running counter, which\n\t// `resetGUID(levelId, nextobjectid)` seeds past every id in the map so the\n\t// two can never meet.\n\t//\n\t// The bug: the counter was advanced by `index` and then DISCARDED whenever\n\t// `index` was truthy, and `index` defaulted to 1 — so `createGUID()` with\n\t// no argument returned the literal string \"-1\" on every call. Every\n\t// renderable added to a container shared one GUID. The only consumer of\n\t// GUID is collision pair identity, so `Detector._pairKey` collapsed every\n\t// pair in the world onto a single key and the second simultaneous\n\t// collision had its `onCollisionStart` / `onCollisionActive` /\n\t// `onCollisionEnd` silently dropped.\n\t//\n\t// Advance by one and stop defaulting `index`, so an absent id genuinely\n\t// reaches the counter. Note this deliberately does NOT deduplicate ids the\n\t// caller supplies: two renderables sharing an explicit `id` still share a\n\t// GUID, exactly as before, since embedding the id is the point of that\n\t// branch.\n\tGUID_index++;\n\treturn `${GUID_base}-${index || GUID_index}`;\n}\n\nexport const isStringArray = (value: unknown): value is string[] => {\n\treturn (\n\t\tArray.isArray(value) &&\n\t\tvalue.every((element) => {\n\t\t\treturn typeof element === \"string\";\n\t\t})\n\t);\n};\n","const MAX_LENGTH = 256;\n\nconst compareIdentifiers = (a: number, b: number) => {\n\tif (a === b) {\n\t\treturn 0;\n\t} else if (a < b) {\n\t\treturn -1;\n\t} else {\n\t\treturn 1;\n\t}\n};\n\nconst SEMVER_REGEX = /(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)/;\n\nclass SemVer {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\n\tconstructor(version: string) {\n\t\tif (version.length > MAX_LENGTH) {\n\t\t\tthrow new TypeError(`version is longer than ${MAX_LENGTH} characters`);\n\t\t}\n\n\t\tconst m = version.trim().match(SEMVER_REGEX);\n\n\t\tif (!m) {\n\t\t\tthrow new TypeError(`Invalid Version: ${version}`);\n\t\t}\n\n\t\t// these are actually numbers\n\t\tthis.major = +m[1];\n\t\tthis.minor = +m[2];\n\t\tthis.patch = +m[3];\n\n\t\tif (this.major > Number.MAX_SAFE_INTEGER || this.major < 0) {\n\t\t\tthrow new TypeError(\"Invalid major version\");\n\t\t}\n\n\t\tif (this.minor > Number.MAX_SAFE_INTEGER || this.minor < 0) {\n\t\t\tthrow new TypeError(\"Invalid minor version\");\n\t\t}\n\n\t\tif (this.patch > Number.MAX_SAFE_INTEGER || this.patch < 0) {\n\t\t\tthrow new TypeError(\"Invalid patch version\");\n\t\t}\n\t}\n\n\tasString() {\n\t\treturn `${this.major}.${this.minor}.${this.patch}`;\n\t}\n\n\tcompare(other: SemVer) {\n\t\tif (other.asString() === this.asString()) {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn this.compareMain(other);\n\t}\n\n\tcompareMain(other: SemVer) {\n\t\treturn (\n\t\t\tcompareIdentifiers(this.major, other.major) ||\n\t\t\tcompareIdentifiers(this.minor, other.minor) ||\n\t\t\tcompareIdentifiers(this.patch, other.patch)\n\t\t);\n\t}\n}\n\nexport const compare = (a: string, b: string) => {\n\treturn new SemVer(a).compare(new SemVer(b));\n};\n","import {\n\trandom as mathRandom,\n\tweightedRandom as mathWeightedRandom,\n} from \"../math/math.js\";\n\n/**\n * a collection of array utility functions\n */\n\n/**\n * Remove the specified object from the given Array\n * @param arr - array from which to remove an object\n * @param obj - to be removed\n * @returns the modified Array\n * let arr = [ \"foo\", \"bar\", \"baz\" ];\n * // remove \"foo\" from the array\n * me.utils.array.remove(arr, \"foo\");\n */\nexport function remove<T>(arr: T[], obj: T): T[] {\n\tconst i = Array.prototype.indexOf.call(arr, obj);\n\tif (i !== -1) {\n\t\tArray.prototype.splice.call(arr, i, 1);\n\t}\n\treturn arr;\n}\n\n/**\n * return a random array element\n * @param arr - array to pick an element\n * @returns random member of array\n * @example\n * // Select a random array element\n * let arr = [ \"foo\", \"bar\", \"baz\" ];\n * console.log(me.utils.array.random(arr));\n */\nexport function random<T>(arr: T[]) {\n\treturn arr[mathRandom(0, arr.length)];\n}\n\n/**\n * return a weighted random array element, favoring the earlier entries\n * @param arr - array to pick an element\n * @returns random member of array\n */\nexport function weightedRandom<T>(arr: T[]) {\n\treturn arr[mathWeightedRandom(0, arr.length)];\n}\n","/**\n * a collection of file utility functions\n */\n\n// regexp to deal with file name & path\nconst PATH = /^.*(\\\\|\\/|:)/;\nconst EXT = /\\.[^.]*$/;\n\n/**\n * return the base name of the file without path info\n * @param path -  path containing the basename to extract\n * @returns the base name without path information.\n */\nexport function getBasename(path: string) {\n\treturn path.replace(PATH, \"\").replace(EXT, \"\");\n}\n\n/**\n * return the extension of the file in the given path\n * @param path - path containing the filename and extension to extract\n * @returns filename extension.\n */\nexport function getExtension(path: string) {\n\treturn path.substring(path.lastIndexOf(\".\") + 1, path.length);\n}\n","/**\n * a collection of utility functions\n */\n\n/**\n * Executes a function as soon as the interpreter is idle (stack empty).\n *\n * Generic over the target function's tail-args tuple so call sites pass\n * the function's actual parameters without lying to TypeScript via\n * `as unknown as (...args: unknown[]) => unknown` casts. The compiler\n * infers `TArgs` from the trailing rest args and constrains `func` to\n * accept them.\n * @param func - The function to be deferred.\n * @param thisArg - The value to be passed as the this parameter to the target function when the deferred function is called\n * @param args - Optional additional arguments to carry for the function.\n * @returns id that can be used to clear the deferred function using\n * clearTimeout\n * @example\n * // execute myFunc() when the stack is empty,\n * // with the current context and [1, 2, 3] as parameter\n * me.utils.function.defer(myFunc, this, 1, 2, 3);\n */\nexport function defer<TArgs extends unknown[]>(\n\tfunc: (...args: TArgs) => unknown,\n\tthisArg: unknown,\n\t...args: TArgs\n) {\n\treturn setTimeout(func.bind(thisArg), 0, ...args);\n}\n\n/**\n * returns a function that, when invoked will only be triggered at most once during a given window of time\n * @param fn - the function to be throttled.\n * @param [wait] - the delay in ms\n * @returns the function that will be throttled\n */\nexport const throttle = <T extends unknown[]>(\n\tfn: (...args: T) => void,\n\twait: number = 100,\n) => {\n\tlet inThrottle: boolean,\n\t\tlastFn: ReturnType<typeof setTimeout>,\n\t\tlastTime: number;\n\treturn (...args: T) => {\n\t\tif (!inThrottle) {\n\t\t\tfn.apply(this, args);\n\t\t\tlastTime = Date.now();\n\t\t\tinThrottle = true;\n\t\t} else {\n\t\t\tclearTimeout(lastFn);\n\t\t\tlastFn = setTimeout(\n\t\t\t\t() => {\n\t\t\t\t\tif (Date.now() - lastTime >= wait) {\n\t\t\t\t\t\tfn.apply(this, args);\n\t\t\t\t\t\tlastTime = Date.now();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tMath.max(wait - (Date.now() - lastTime), 0),\n\t\t\t);\n\t\t}\n\t};\n};\n","import { Jsonifiable } from \"type-fest\";\n/**\n * allow to access and manage the device localStorage\n * @example\n * // Initialize \"score\" and \"lives\" with default values\n * // This loads the properties from localStorage if they exist, else it sets the given defaults\n * me.save.add({ score : 0, lives : 3 });\n *\n * // Print all\n * // On first load, this prints { score : 0, lives : 3 }\n * // On further reloads, it prints { score : 31337, lives : 3, complexObject : ... }\n * // Because the following changes will be saved to localStorage\n * console.log(JSON.stringify(me.save));\n *\n * // Save score\n * me.save.score = 31337;\n *\n * // Also supports complex objects thanks to the JSON backend\n * me.save.add({ complexObject : {} })\n * me.save.complexObject = { a : \"b\", c : [ 1, 2, 3, \"d\" ], e : { f : [{}] } };\n *\n * // WARNING: Do not set any child properties of complex objects directly!\n * // Changes made that way will not save. Always set the entire object value at once.\n * // If you cannot live with this limitation, there's a workaround:\n * me.save.complexObject.c.push(\"foo\"); // Modify a child property\n * me.save.complexObject = me.save.complexObject; // Save the entire object!\n *\n * // Remove \"lives\" from localStorage\n * me.save.remove(\"lives\");\n * @namespace save\n */\n\nimport { isStringArray } from \"../utils/utils.js\";\nimport { BOOT, once } from \"./event.js\";\n\n// Variable to hold the object data\nconst data: Record<string, unknown> = {};\n\nlet hasLocalStorage = false;\n\ntry {\n\t// true if localStorage is supported\n\thasLocalStorage =\n\t\ttypeof globalThis !== \"undefined\" &&\n\t\ttypeof globalThis.localStorage !== \"undefined\";\n} catch {\n\t// the above generates an exception when cookies are blocked\n\thasLocalStorage = false;\n}\n\n/**\n * a function to check if the given key is a reserved word\n * @ignore\n */\nfunction isReserved(key: string) {\n\treturn key === \"add\" || key === \"remove\";\n}\n\n// Initialize me.save on Boot event\nonce(BOOT, () => {\n\t// Load previous data if local Storage is supported\n\tif (hasLocalStorage) {\n\t\tconst me_save_content = localStorage.getItem(\"me.save\");\n\n\t\tif (me_save_content !== null && me_save_content.length > 0) {\n\t\t\ttry {\n\t\t\t\tconst stored: unknown = JSON.parse(me_save_content);\n\t\t\t\tconst keys = isStringArray(stored) ? stored : [];\n\t\t\t\tfor (const key of keys) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst storageKey = `me.save.${key}`;\n\t\t\t\t\t\tconst stored = localStorage.getItem(storageKey);\n\t\t\t\t\t\tdata[key] = stored === null ? null : JSON.parse(stored);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// do nothing is invalid json\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// do nothing is invalid json\n\t\t\t}\n\t\t}\n\t}\n});\n\nconst save: Record<string, unknown> = {\n\t/**\n\t * Add new keys to localStorage and set them to the given default values if they do not exist\n\t * @param props - key and corresponding values\n\t * @example\n\t * // Initialize \"score\" and \"lives\" with default values\n\t * me.save.add({ score : 0, lives : 3 });\n\t * // get or set the value through me.save\n\t * me.save.score = 1000;\n\t */\n\tadd(props: Record<string, Jsonifiable>) {\n\t\tconst obj = save;\n\n\t\tfor (const key of Object.keys(props)) {\n\t\t\tif (isReserved(key)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tObject.defineProperty(obj, key, {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tget() {\n\t\t\t\t\treturn data[key];\n\t\t\t\t},\n\t\t\t\tset(value) {\n\t\t\t\t\tdata[key] = value;\n\t\t\t\t\tif (hasLocalStorage) {\n\t\t\t\t\t\tlocalStorage.setItem(`me.save.${key}`, JSON.stringify(value));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (!(key in data)) {\n\t\t\t\tsave[key] = props[key];\n\t\t\t}\n\t\t}\n\n\t\t// Save keys\n\t\tif (hasLocalStorage) {\n\t\t\tlocalStorage.setItem(\"me.save\", JSON.stringify(Object.keys(data)));\n\t\t}\n\t},\n\n\t/**\n\t * Remove a key from localStorage\n\t * @param key - key to be removed\n\t * @example\n\t * // Remove the \"score\" key from localStorage\n\t * me.save.remove(\"score\");\n\t */\n\tremove(key: string) {\n\t\tif (!isReserved(key)) {\n\t\t\tif (typeof data[key] !== \"undefined\") {\n\t\t\t\tdelete data[key];\n\t\t\t\tif (hasLocalStorage) {\n\t\t\t\t\tglobalThis.localStorage.removeItem(`me.save.${key}`);\n\t\t\t\t\tglobalThis.localStorage.setItem(\n\t\t\t\t\t\t\"me.save\",\n\t\t\t\t\t\tJSON.stringify(Object.keys(data)),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n};\n\nexport default save;\n","// Fullscreen-capability probes, factored out of `device.ts` so the\n// (small, leaf-level) `lang/deprecated.js` re-export wrappers can use them\n// without creating a circular import — device.ts re-exports these so\n// `me.device.hasFullscreenSupport` / `me.device.isFullscreen` stay\n// byte-for-byte unchanged for consumers.\n//\n// `lib.dom.d.ts` only carries the unprefixed `fullscreenEnabled` /\n// `fullscreenElement` fields; older WebKit / Gecko / IE-derived engines\n// still surface state via vendor-prefixed variants. The intersection type\n// below is local + un-exported; consumers cast through it.\ntype DocumentLegacy = Document & {\n\tmozFullScreenEnabled?: boolean;\n\tmozFullScreenElement?: Element;\n\twebkitFullscreenEnabled?: boolean;\n\twebkitFullscreenElement?: Element;\n\tmsFullscreenEnabled?: boolean;\n\tmsFullscreenElement?: Element;\n};\n\n/**\n * Browser full screen support — probed once at module load via the\n * unprefixed `fullscreenEnabled` field with vendor-prefixed fallbacks.\n */\nexport const hasFullscreenSupport =\n\ttypeof globalThis.document !== \"undefined\" &&\n\t!!(\n\t\tglobalThis.document.fullscreenEnabled ||\n\t\t(globalThis.document as DocumentLegacy).webkitFullscreenEnabled ||\n\t\t(globalThis.document as DocumentLegacy).mozFullScreenEnabled ||\n\t\t(globalThis.document as DocumentLegacy).msFullscreenEnabled\n\t);\n\n/**\n * Returns true if the browser/device is in full screen mode.\n *\n * Pure document-state probe — no Application context needed, since the\n * browser tracks exactly one fullscreen element per document regardless\n * of how many Applications are running.\n * @category Application\n * @returns true when any of the four vendor variants of `fullscreenElement` is non-null\n */\nexport function isFullscreen(): boolean {\n\tif (!hasFullscreenSupport) return false;\n\tconst doc = globalThis.document as DocumentLegacy;\n\treturn !!(\n\t\tdoc.fullscreenElement ||\n\t\tdoc.webkitFullscreenElement ||\n\t\tdoc.mozFullScreenElement ||\n\t\tdoc.msFullscreenElement\n\t);\n}\n","import { createPool } from \"../system/pool.ts\";\nimport { clamp, random } from \"./math.ts\";\n\n// pre-computed lookup table for byte-to-hex conversion (0–255 → \"00\"–\"FF\")\nconst hexLookup: string[] = new Array(256);\n{\n\tconst chars = \"0123456789ABCDEF\";\n\tfor (let i = 0; i < 256; i++) {\n\t\thexLookup[i] = chars[(i & 0xf0) >> 4] + chars[i & 0x0f];\n\t}\n}\n\nfunction toHex(component: number) {\n\treturn hexLookup[component];\n}\n\nfunction hue2rgb(p: number, q: number, t: number) {\n\tif (t < 0) {\n\t\tt += 1;\n\t}\n\tif (t > 1) {\n\t\tt -= 1;\n\t}\n\tif (t < 1 / 6) {\n\t\treturn p + (q - p) * 6 * t;\n\t}\n\tif (t < 1 / 2) {\n\t\treturn q;\n\t}\n\tif (t < 2 / 3) {\n\t\treturn p + (q - p) * (2 / 3 - t) * 6;\n\t}\n\treturn p;\n}\n\nconst rgbaRx = /^rgba?\\((\\d+), ?(\\d+), ?(\\d+)(, ?([\\d.]+))?\\)$/;\nconst hex3Rx = /^#([\\da-fA-F])([\\da-fA-F])([\\da-fA-F])$/;\nconst hex4Rx = /^#([\\da-fA-F])([\\da-fA-F])([\\da-fA-F])([\\da-fA-F])$/;\nconst hex6Rx = /^#([\\da-fA-F]{2})([\\da-fA-F]{2})([\\da-fA-F]{2})$/;\nconst hex8Rx =\n\t/^#([\\da-fA-F]{2})([\\da-fA-F]{2})([\\da-fA-F]{2})([\\da-fA-F]{2})$/;\n\nconst CSS_COLORS = [\n\t// CSS1\n\t[\"black\", [0, 0, 0]],\n\t[\"silver\", [192, 192, 192]],\n\t[\"gray\", [128, 128, 128]],\n\t[\"white\", [255, 255, 255]],\n\t[\"maroon\", [128, 0, 0]],\n\t[\"red\", [255, 0, 0]],\n\t[\"purple\", [128, 0, 128]],\n\t[\"fuchsia\", [255, 0, 255]],\n\t[\"green\", [0, 128, 0]],\n\t[\"lime\", [0, 255, 0]],\n\t[\"olive\", [128, 128, 0]],\n\t[\"yellow\", [255, 255, 0]],\n\t[\"navy\", [0, 0, 128]],\n\t[\"blue\", [0, 0, 255]],\n\t[\"teal\", [0, 128, 128]],\n\t[\"aqua\", [0, 255, 255]],\n\n\t// CSS2\n\t[\"orange\", [255, 165, 0]],\n\n\t// CSS3\n\t[\"aliceblue\", [240, 248, 255]],\n\t[\"antiquewhite\", [250, 235, 215]],\n\t[\"aquamarine\", [127, 255, 212]],\n\t[\"azure\", [240, 255, 255]],\n\t[\"beige\", [245, 245, 220]],\n\t[\"bisque\", [255, 228, 196]],\n\t[\"blanchedalmond\", [255, 235, 205]],\n\t[\"blueviolet\", [138, 43, 226]],\n\t[\"brown\", [165, 42, 42]],\n\t[\"burlywood\", [222, 184, 135]],\n\t[\"cadetblue\", [95, 158, 160]],\n\t[\"chartreuse\", [127, 255, 0]],\n\t[\"chocolate\", [210, 105, 30]],\n\t[\"coral\", [255, 127, 80]],\n\t[\"cornflowerblue\", [100, 149, 237]],\n\t[\"cornsilk\", [255, 248, 220]],\n\t[\"crimson\", [220, 20, 60]],\n\t[\"cyan\", [0, 255, 255]],\n\t[\"darkblue\", [0, 0, 139]],\n\t[\"darkcyan\", [0, 139, 139]],\n\t[\"darkgoldenrod\", [184, 134, 11]],\n\t[\"darkgray\", [169, 169, 169]],\n\t[\"darkgreen\", [0, 100, 0]],\n\t[\"darkgrey\", [169, 169, 169]],\n\t[\"darkkhaki\", [189, 183, 107]],\n\t[\"darkmagenta\", [139, 0, 139]],\n\t[\"darkolivegreen\", [85, 107, 47]],\n\t[\"darkorange\", [255, 140, 0]],\n\t[\"darkorchid\", [153, 50, 204]],\n\t[\"darkred\", [139, 0, 0]],\n\t[\"darksalmon\", [233, 150, 122]],\n\t[\"darkseagreen\", [143, 188, 143]],\n\t[\"darkslateblue\", [72, 61, 139]],\n\t[\"darkslategray\", [47, 79, 79]],\n\t[\"darkslategrey\", [47, 79, 79]],\n\t[\"darkturquoise\", [0, 206, 209]],\n\t[\"darkviolet\", [148, 0, 211]],\n\t[\"deeppink\", [255, 20, 147]],\n\t[\"deepskyblue\", [0, 191, 255]],\n\t[\"dimgray\", [105, 105, 105]],\n\t[\"dimgrey\", [105, 105, 105]],\n\t[\"dodgerblue\", [30, 144, 255]],\n\t[\"firebrick\", [178, 34, 34]],\n\t[\"floralwhite\", [255, 250, 240]],\n\t[\"forestgreen\", [34, 139, 34]],\n\t[\"gainsboro\", [220, 220, 220]],\n\t[\"ghostwhite\", [248, 248, 255]],\n\t[\"gold\", [255, 215, 0]],\n\t[\"goldenrod\", [218, 165, 32]],\n\t[\"greenyellow\", [173, 255, 47]],\n\t[\"grey\", [128, 128, 128]],\n\t[\"honeydew\", [240, 255, 240]],\n\t[\"hotpink\", [255, 105, 180]],\n\t[\"indianred\", [205, 92, 92]],\n\t[\"indigo\", [75, 0, 130]],\n\t[\"ivory\", [255, 255, 240]],\n\t[\"khaki\", [240, 230, 140]],\n\t[\"lavender\", [230, 230, 250]],\n\t[\"lavenderblush\", [255, 240, 245]],\n\t[\"lawngreen\", [124, 252, 0]],\n\t[\"lemonchiffon\", [255, 250, 205]],\n\t[\"lightblue\", [173, 216, 230]],\n\t[\"lightcoral\", [240, 128, 128]],\n\t[\"lightcyan\", [224, 255, 255]],\n\t[\"lightgoldenrodyellow\", [250, 250, 210]],\n\t[\"lightgray\", [211, 211, 211]],\n\t[\"lightgreen\", [144, 238, 144]],\n\t[\"lightgrey\", [211, 211, 211]],\n\t[\"lightpink\", [255, 182, 193]],\n\t[\"lightsalmon\", [255, 160, 122]],\n\t[\"lightseagreen\", [32, 178, 170]],\n\t[\"lightskyblue\", [135, 206, 250]],\n\t[\"lightslategray\", [119, 136, 153]],\n\t[\"lightslategrey\", [119, 136, 153]],\n\t[\"lightsteelblue\", [176, 196, 222]],\n\t[\"lightyellow\", [255, 255, 224]],\n\t[\"limegreen\", [50, 205, 50]],\n\t[\"linen\", [250, 240, 230]],\n\t[\"magenta\", [255, 0, 255]],\n\t[\"mediumaquamarine\", [102, 205, 170]],\n\t[\"mediumblue\", [0, 0, 205]],\n\t[\"mediumorchid\", [186, 85, 211]],\n\t[\"mediumpurple\", [147, 112, 219]],\n\t[\"mediumseagreen\", [60, 179, 113]],\n\t[\"mediumslateblue\", [123, 104, 238]],\n\t[\"mediumspringgreen\", [0, 250, 154]],\n\t[\"mediumturquoise\", [72, 209, 204]],\n\t[\"mediumvioletred\", [199, 21, 133]],\n\t[\"midnightblue\", [25, 25, 112]],\n\t[\"mintcream\", [245, 255, 250]],\n\t[\"mistyrose\", [255, 228, 225]],\n\t[\"moccasin\", [255, 228, 181]],\n\t[\"navajowhite\", [255, 222, 173]],\n\t[\"oldlace\", [253, 245, 230]],\n\t[\"olivedrab\", [107, 142, 35]],\n\t[\"orangered\", [255, 69, 0]],\n\t[\"orchid\", [218, 112, 214]],\n\t[\"palegoldenrod\", [238, 232, 170]],\n\t[\"palegreen\", [152, 251, 152]],\n\t[\"paleturquoise\", [175, 238, 238]],\n\t[\"palevioletred\", [219, 112, 147]],\n\t[\"papayawhip\", [255, 239, 213]],\n\t[\"peachpuff\", [255, 218, 185]],\n\t[\"peru\", [205, 133, 63]],\n\t[\"pink\", [255, 192, 203]],\n\t[\"plum\", [221, 160, 221]],\n\t[\"powderblue\", [176, 224, 230]],\n\t[\"rosybrown\", [188, 143, 143]],\n\t[\"royalblue\", [65, 105, 225]],\n\t[\"saddlebrown\", [139, 69, 19]],\n\t[\"salmon\", [250, 128, 114]],\n\t[\"sandybrown\", [244, 164, 96]],\n\t[\"seagreen\", [46, 139, 87]],\n\t[\"seashell\", [255, 245, 238]],\n\t[\"sienna\", [160, 82, 45]],\n\t[\"skyblue\", [135, 206, 235]],\n\t[\"slateblue\", [106, 90, 205]],\n\t[\"slategray\", [112, 128, 144]],\n\t[\"slategrey\", [112, 128, 144]],\n\t[\"snow\", [255, 250, 250]],\n\t[\"springgreen\", [0, 255, 127]],\n\t[\"steelblue\", [70, 130, 180]],\n\t[\"tan\", [210, 180, 140]],\n\t[\"thistle\", [216, 191, 216]],\n\t[\"tomato\", [255, 99, 71]],\n\t[\"turquoise\", [64, 224, 208]],\n\t[\"violet\", [238, 130, 238]],\n\t[\"wheat\", [245, 222, 179]],\n\t[\"whitesmoke\", [245, 245, 245]],\n\t[\"yellowgreen\", [154, 205, 50]],\n\n\t// CSS4\n\t[\"rebeccapurple\", [102, 51, 153]],\n] as const;\n\ntype ColorName = (typeof CSS_COLORS)[number][0];\n\nconst cssToRGB = new Map<ColorName, readonly [number, number, number]>();\nfor (const [name, rgb] of CSS_COLORS) {\n\tcssToRGB.set(name, rgb);\n}\n\n/**\n * A color manipulation object.\n * @category Math\n */\nexport class Color {\n\tprivate normalizedRGBA: Float32Array;\n\n\t/**\n\t * Creates a new Color instance.\n\t * @param r - A Color object or the red component [0 .. 255]. Defaults to 0.\n\t * @param g - The green component [0 .. 255]. Defaults to 0.\n\t * @param b - The blue component [0 .. 255]. Defaults to 0.\n\t * @param alpha - The alpha value [0.0 .. 1.0]. Defaults to 1.\n\t */\n\tconstructor(r: Color | string | number = 0, g = 0, b = 0, alpha = 1.0) {\n\t\tif (typeof r === \"number\") {\n\t\t\tthis.normalizedRGBA = new Float32Array([0, 0, 0, 1]);\n\t\t\tthis.setColor(r, g, b, alpha);\n\t\t} else if (typeof r === \"string\") {\n\t\t\tthis.normalizedRGBA = new Float32Array([0, 0, 0, 1]);\n\t\t\tthis.parseCSS(r);\n\t\t} else if (typeof r === \"object\") {\n\t\t\tthis.normalizedRGBA = r.normalizedRGBA.slice();\n\t\t} else {\n\t\t\tthrow new Error(\"Color: invalid parameter\");\n\t\t}\n\t}\n\n\t/**\n\t * Gets the red component of the color.\n\t * @returns The red component [0 .. 255].\n\t */\n\tget r() {\n\t\treturn ~~(this.normalizedRGBA[0] * 255);\n\t}\n\n\t/**\n\t * Sets the red component of the color.\n\t * @param value - The red component [0 .. 255].\n\t */\n\tset r(value) {\n\t\tthis.normalizedRGBA[0] = clamp(value, 0, 255) / 255.0;\n\t}\n\n\t/**\n\t * Gets the green component of the color.\n\t * @returns The green component [0 .. 255].\n\t */\n\tget g() {\n\t\treturn ~~(this.normalizedRGBA[1] * 255);\n\t}\n\n\t/**\n\t * Sets the green component of the color.\n\t * @param value - The green component [0 .. 255].\n\t */\n\tset g(value) {\n\t\tthis.normalizedRGBA[1] = clamp(value, 0, 255) / 255.0;\n\t}\n\n\t/**\n\t * Gets the blue component of the color.\n\t * @returns The blue component [0 .. 255].\n\t */\n\tget b() {\n\t\treturn ~~(this.normalizedRGBA[2] * 255);\n\t}\n\n\t/**\n\t * Sets the blue component of the color.\n\t * @param value - The blue component [0 .. 255].\n\t */\n\tset b(value) {\n\t\tthis.normalizedRGBA[2] = clamp(value, 0, 255) / 255.0;\n\t}\n\n\t/**\n\t * Gets the alpha component of the color.\n\t * @returns The alpha component [0.0 .. 1.0].\n\t */\n\tget alpha() {\n\t\treturn this.normalizedRGBA[3];\n\t}\n\n\t/**\n\t * Sets the alpha component of the color.\n\t * @param value - The alpha component [0.0 .. 1.0].\n\t */\n\tset alpha(value) {\n\t\tthis.normalizedRGBA[3] = clamp(value, 0, 1.0);\n\t}\n\n\t/**\n\t * Sets the color to the specified values.\n\t * @param r - The red component [0 .. 255].\n\t * @param g - The green component [0 .. 255].\n\t * @param b - The blue component [0 .. 255].\n\t * @param [alpha] - The alpha value [0.0 .. 1.0]. Defaults to 1.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetColor(r: number = 0, g: number = 0, b: number = 0, alpha = 1.0) {\n\t\tthis.r = r;\n\t\tthis.g = g;\n\t\tthis.b = b;\n\t\tthis.alpha = alpha;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the color to the specified normalized float values.\n\t * @param r - The red component [0.0 .. 1.0].\n\t * @param g - The green component [0.0 .. 1.0].\n\t * @param b - The blue component [0.0 .. 1.0].\n\t * @param [alpha=1.0] - The alpha value [0.0 .. 1.0]. Defaults to 1.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetFloat(r: number, g: number, b: number, alpha = 1.0) {\n\t\tconst a = this.normalizedRGBA;\n\t\ta[0] = clamp(r, 0, 1.0);\n\t\ta[1] = clamp(g, 0, 1.0);\n\t\ta[2] = clamp(b, 0, 1.0);\n\t\ta[3] = clamp(alpha, 0, 1.0);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the color to the specified HSV values.\n\t * @param h - The hue [0 .. 1].\n\t * @param s - The saturation [0 .. 1].\n\t * @param v - The value [0 .. 1].\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetHSV(h: number, s: number, v: number) {\n\t\tlet r: number;\n\t\tlet g: number;\n\t\tlet b: number;\n\n\t\tconst i = Math.floor(h * 6);\n\t\tconst f = h * 6 - i;\n\t\tconst p = v * (1 - s);\n\t\tconst q = v * (1 - f * s);\n\t\tconst t = v * (1 - (1 - f) * s);\n\n\t\tswitch ((i % 6) as 0 | 1 | 2 | 3 | 4 | 5) {\n\t\t\tcase 0:\n\t\t\t\tr = v;\n\t\t\t\tg = t;\n\t\t\t\tb = p;\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tr = q;\n\t\t\t\tg = v;\n\t\t\t\tb = p;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tr = p;\n\t\t\t\tg = v;\n\t\t\t\tb = t;\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tr = p;\n\t\t\t\tg = q;\n\t\t\t\tb = v;\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tr = t;\n\t\t\t\tg = p;\n\t\t\t\tb = v;\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\tr = v;\n\t\t\t\tg = p;\n\t\t\t\tb = q;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn this.setColor(r * 255, g * 255, b * 255);\n\t}\n\n\t/**\n\t * Sets the color from HSL values, **with hue in `[0..1]`** (the\n\t * GLSL / WebGL shader convention — `0 = red`, `1/3 = green`,\n\t * `2/3 = blue`, `1` = back to red). If you're coming from CSS /\n\t * Photoshop / D3 and have a hue in degrees (`0..360`), divide by\n\t * 360 first — or call {@link Color#setHSLDeg} which does it for you.\n\t *\n\t * Hue wraps at integer boundaries, so `h = 1` and `h = 0` produce\n\t * the same red. Saturation `0` short-circuits to a grey at the\n\t * requested lightness, ignoring hue. Lightness `0` or `1` produce\n\t * pure black or pure white regardless of the other two.\n\t * @param h - The hue, normalized to `[0..1]` (NOT degrees).\n\t * @param s - The saturation `[0..1]`.\n\t * @param l - The lightness `[0..1]`.\n\t * @returns Reference to this object for method chaining.\n\t * @see {@link Color#setHSLDeg} for the degrees-based variant.\n\t * @example\n\t * // pure green (hue 1/3, full saturation, mid lightness)\n\t * color.setHSL(1 / 3, 1, 0.5);\n\t * // CSS hsl(200deg, 80%, 60%) — divide hue by 360 first\n\t * color.setHSL(200 / 360, 0.8, 0.6);\n\t */\n\tsetHSL(h: number, s: number, l: number) {\n\t\tlet r;\n\t\tlet g;\n\t\tlet b;\n\n\t\tif (s === 0) {\n\t\t\tr = g = b = l; // achromatic\n\t\t} else {\n\t\t\tconst q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n\t\t\tconst p = 2 * l - q;\n\n\t\t\tr = hue2rgb(p, q, h + 1 / 3);\n\t\t\tg = hue2rgb(p, q, h);\n\t\t\tb = hue2rgb(p, q, h - 1 / 3);\n\t\t}\n\n\t\treturn this.setColor(r * 255, g * 255, b * 255);\n\t}\n\n\t/**\n\t * Sets the color from HSL values with **hue in degrees `[0..360]`**\n\t * — the CSS / Photoshop / D3 convention. Thin convenience wrapper\n\t * over {@link Color#setHSL}: divides `hDeg` by 360 and delegates.\n\t * Saturation and lightness stay in `[0..1]` (the same units CSS\n\t * percentages would normalize to).\n\t *\n\t * Use this when you're working from CSS color values, color-picker\n\t * output, or any standard color reference. Prefer {@link Color#setHSL}\n\t * when your hue is already normalized (e.g. from `Math.random()`,\n\t * a noise function, or a shader uniform).\n\t * @param hDeg - The hue in degrees `[0..360]`. Values outside the\n\t *   range are accepted (the underlying hue calc wraps).\n\t * @param s - The saturation `[0..1]`.\n\t * @param l - The lightness `[0..1]`.\n\t * @returns Reference to this object for method chaining.\n\t * @example\n\t * // CSS hsl(200deg, 80%, 60%)\n\t * color.setHSLDeg(200, 0.8, 0.6);\n\t * // 360° wraps back to red — same as setHSLDeg(0, ...)\n\t * color.setHSLDeg(360, 1, 0.5);\n\t */\n\tsetHSLDeg(hDeg: number, s: number, l: number) {\n\t\t// Normalize hDeg into `[0, 360)` before dividing. `setHSL`'s\n\t\t// internal `hue2rgb` adjusts by at most one turn (`t < 0 → +1`,\n\t\t// `t > 1 → -1`), so multi-turn inputs like `720` or `-360`\n\t\t// would otherwise leave the per-channel `t` value outside the\n\t\t// valid range and land in `hue2rgb`'s fallthrough (returning\n\t\t// `p`), which is wrong for any non-zero saturation. Modulo +\n\t\t// add-then-modulo keeps negatives well-defined.\n\t\tconst normalized = ((hDeg % 360) + 360) % 360;\n\t\treturn this.setHSL(normalized / 360, s, l);\n\t}\n\n\t/**\n\t * Creates a new copy of this color object.\n\t * @returns Reference to the newly cloned object.\n\t */\n\tclone() {\n\t\treturn colorPool.get(this);\n\t}\n\n\t/**\n\t * Copies a color object or CSS color into this one.\n\t * @param color - The color to copy.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tcopy(color: Color | string) {\n\t\tif (typeof color === \"string\") {\n\t\t\treturn this.parseCSS(color);\n\t\t} else {\n\t\t\tthis.normalizedRGBA.set(color.normalizedRGBA);\n\t\t\treturn this;\n\t\t}\n\t}\n\n\t/**\n\t * Blends this color with the given one using addition.\n\t * @param color - The color to blend with.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tadd(color: Color) {\n\t\tthis.normalizedRGBA[0] = clamp(\n\t\t\tthis.normalizedRGBA[0] + color.normalizedRGBA[0],\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t\tthis.normalizedRGBA[1] = clamp(\n\t\t\tthis.normalizedRGBA[1] + color.normalizedRGBA[1],\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t\tthis.normalizedRGBA[2] = clamp(\n\t\t\tthis.normalizedRGBA[2] + color.normalizedRGBA[2],\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t\tthis.normalizedRGBA[3] =\n\t\t\t(this.normalizedRGBA[3] + color.normalizedRGBA[3]) / 2;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Darkens this color value by a given scale.\n\t * @param scale - The scale to darken the color by [0 .. 1].\n\t * @returns Reference to this object for method chaining.\n\t */\n\tdarken(scale: number) {\n\t\tscale = clamp(scale, 0, 1);\n\t\tthis.normalizedRGBA[0] *= scale;\n\t\tthis.normalizedRGBA[1] *= scale;\n\t\tthis.normalizedRGBA[2] *= scale;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Linearly interpolates between this color and the given one.\n\t * @param color - The color to interpolate with.\n\t * @param alpha - The interpolation factor, with alpha = 0 being this color, and alpha = 1 being the given one.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tlerp(color: Color, alpha: number) {\n\t\talpha = clamp(alpha, 0, 1);\n\t\tthis.normalizedRGBA[0] +=\n\t\t\t(color.normalizedRGBA[0] - this.normalizedRGBA[0]) * alpha;\n\t\tthis.normalizedRGBA[1] +=\n\t\t\t(color.normalizedRGBA[1] - this.normalizedRGBA[1]) * alpha;\n\t\tthis.normalizedRGBA[2] +=\n\t\t\t(color.normalizedRGBA[2] - this.normalizedRGBA[2]) * alpha;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Lightens this color value by a given scale\n\t * @param scale - The scale to lighten the color by [0 .. 1].\n\t * @returns Reference to this object for method chaining.\n\t */\n\tlighten(scale: number) {\n\t\tscale = clamp(scale, 0, 1);\n\t\tthis.normalizedRGBA[0] = clamp(\n\t\t\tthis.normalizedRGBA[0] + (1 - this.normalizedRGBA[0]) * scale,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t\tthis.normalizedRGBA[1] = clamp(\n\t\t\tthis.normalizedRGBA[1] + (1 - this.normalizedRGBA[1]) * scale,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t\tthis.normalizedRGBA[2] = clamp(\n\t\t\tthis.normalizedRGBA[2] + (1 - this.normalizedRGBA[2]) * scale,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Generate random r,g,b values for this color object\n\t * @param [min] - minimum value for the random range\n\t * @param [max] - maxmium value for the random range\n\t * @returns Reference to this object for method chaining\n\t */\n\trandom(min = 0, max = 255) {\n\t\tif (min < 0) {\n\t\t\tmin = 0;\n\t\t}\n\t\tif (max > 255) {\n\t\t\tmax = 255;\n\t\t}\n\n\t\treturn this.setColor(\n\t\t\trandom(min, max),\n\t\t\trandom(min, max),\n\t\t\trandom(min, max),\n\t\t\tthis.alpha,\n\t\t);\n\t}\n\n\t/**\n\t * Checks if this color is equal to another.\n\t * @param color - The color to compare with.\n\t * @returns True if the colors are equal, otherwise false.\n\t */\n\tequals(color: Color) {\n\t\treturn (\n\t\t\tthis.normalizedRGBA[0] === color.normalizedRGBA[0] &&\n\t\t\tthis.normalizedRGBA[1] === color.normalizedRGBA[1] &&\n\t\t\tthis.normalizedRGBA[2] === color.normalizedRGBA[2] &&\n\t\t\tthis.normalizedRGBA[3] === color.normalizedRGBA[3]\n\t\t);\n\t}\n\n\t/**\n\t * Parse a CSS color name and set this color to the corresponding r,g,b values\n\t * @param cssColor - The CSS color name\n\t * @returns Reference to this object for method chaining\n\t */\n\tparseCSS(cssColor: string) {\n\t\tconst rgb = cssToRGB.get(cssColor as ColorName);\n\t\tif (!rgb) {\n\t\t\treturn this.parseRGB(cssColor);\n\t\t}\n\t\treturn this.setColor(...rgb);\n\t}\n\n\t/**\n\t * Parse an RGB or RGBA CSS color string\n\t * @param rgbColor - The RGB or RGBA color string to parse\n\t * @returns Reference to this object for method chaining\n\t */\n\tparseRGB(rgbColor: string) {\n\t\tconst match = rgbaRx.exec(rgbColor);\n\t\tif (!match) {\n\t\t\treturn this.parseHex(rgbColor as `#${string}`);\n\t\t}\n\t\treturn this.setColor(\n\t\t\t+match[1],\n\t\t\t+match[2],\n\t\t\t+match[3],\n\t\t\ttypeof match[5] !== \"undefined\" ? +match[5] : 1,\n\t\t);\n\t}\n\n\t/**\n\t * Parse a Hex color (\"#RGB\", \"#RGBA\" or \"#RRGGBB\", \"#RRGGBBAA\" format) and set this color to\n\t * the corresponding r,g,b,a values\n\t * @param hexColor - The Hex color string to parse\n\t * @param [argb] - true if format is #ARGB, or #AARRGGBB (as opposed to #RGBA or #RGGBBAA)\n\t * @returns Reference to this object for method chaining\n\t */\n\tparseHex(hexColor: `#${string}`, argb = false) {\n\t\tlet match: RegExpExecArray | null;\n\t\tif ((match = hex8Rx.exec(hexColor))) {\n\t\t\t// #AARRGGBB or #RRGGBBAA\n\t\t\treturn this.setColor(\n\t\t\t\tparseInt(match[!argb ? 1 : 2], 16), // r\n\t\t\t\tparseInt(match[!argb ? 2 : 3], 16), // g\n\t\t\t\tparseInt(match[!argb ? 3 : 4], 16), // b\n\t\t\t\tNumber(\n\t\t\t\t\t(clamp(parseInt(match[!argb ? 4 : 1], 16), 0, 255) / 255.0).toFixed(\n\t\t\t\t\t\t1,\n\t\t\t\t\t),\n\t\t\t\t), // a\n\t\t\t);\n\t\t}\n\n\t\tif ((match = hex6Rx.exec(hexColor))) {\n\t\t\t// #RRGGBB\n\t\t\treturn this.setColor(\n\t\t\t\tparseInt(match[1], 16),\n\t\t\t\tparseInt(match[2], 16),\n\t\t\t\tparseInt(match[3], 16),\n\t\t\t);\n\t\t}\n\n\t\tif ((match = hex4Rx.exec(hexColor))) {\n\t\t\t// #ARGB or #RGBA\n\t\t\tconst r = match[!argb ? 1 : 2];\n\t\t\tconst g = match[!argb ? 2 : 3];\n\t\t\tconst b = match[!argb ? 3 : 4];\n\t\t\tconst a = match[!argb ? 4 : 1];\n\t\t\treturn this.setColor(\n\t\t\t\tparseInt(r + r, 16), // r\n\t\t\t\tparseInt(g + g, 16), // g\n\t\t\t\tparseInt(b + b, 16), // b\n\t\t\t\tNumber((clamp(parseInt(a + a, 16), 0, 255) / 255.0).toFixed(1)), // a\n\t\t\t);\n\t\t}\n\n\t\tif ((match = hex3Rx.exec(hexColor))) {\n\t\t\t// #RGB\n\t\t\treturn this.setColor(\n\t\t\t\tparseInt(match[1] + match[1], 16),\n\t\t\t\tparseInt(match[2] + match[2], 16),\n\t\t\t\tparseInt(match[3] + match[3], 16),\n\t\t\t);\n\t\t}\n\n\t\tthrow new Error(`invalid parameter: ${hexColor}`);\n\t}\n\n\t/**\n\t * Pack this color RGB components into a Uint32 ARGB representation\n\t * @param [alpha] - alpha value [0.0 .. 1.0]\n\t * @returns A Uint32 ARGB representation of this color\n\t */\n\ttoUint32(alpha = 1.0) {\n\t\tconst a = this.normalizedRGBA;\n\n\t\tconst ur = (a[0] * 255) >> 0;\n\t\tconst ug = (a[1] * 255) >> 0;\n\t\tconst ub = (a[2] * 255) >> 0;\n\n\t\treturn (((alpha * 255) >> 0) << 24) | (ur << 16) | (ug << 8) | ub;\n\t}\n\n\t/**\n\t * return a Float Array representation of this object\n\t * @returns A Float Array representation of this color\n\t */\n\ttoArray() {\n\t\treturn this.normalizedRGBA;\n\t}\n\n\t/**\n\t * return the color in \"#RRGGBB\" format\n\t * @returns The color in \"#RRGGBB\" format\n\t */\n\ttoHex() {\n\t\tconst a = this.normalizedRGBA;\n\t\treturn `#${toHex((a[0] * 255) >> 0)}${toHex((a[1] * 255) >> 0)}${toHex((a[2] * 255) >> 0)}`;\n\t}\n\n\t/**\n\t * Get the color in \"#RRGGBBAA\" format\n\t * @param alpha - The alpha value [0.0 .. 1.0] to use in the output string.\n\t * @returns The color in \"#RRGGBBAA\" format\n\t */\n\ttoHex8(alpha = this.normalizedRGBA[3]) {\n\t\tconst a = this.normalizedRGBA;\n\t\treturn `#${toHex((a[0] * 255) >> 0)}${toHex((a[1] * 255) >> 0)}${toHex((a[2] * 255) >> 0)}${toHex((alpha * 255) >> 0)}`;\n\t}\n\n\t/**\n\t * Get the color in \"rgb(R,G,B)\" format\n\t * @returns The color in \"rgb(R,G,B)\" format\n\t */\n\ttoRGB() {\n\t\treturn `rgb(${this.r},${this.g},${this.b})` as const;\n\t}\n\n\t/**\n\t * Get the color in \"rgba(R,G,B,A)\" format\n\t * @param [alpha] - alpha value [0.0 .. 1.0]\n\t * @returns The color in \"rgba(R,G,B,A)\" format\n\t */\n\ttoRGBA(alpha = this.alpha) {\n\t\treturn `rgba(${this.r},${this.g},${this.b},${alpha})` as const;\n\t}\n}\n\nexport const colorPool = createPool<\n\tColor,\n\t[\n\t\tr?: number | string | Color | undefined,\n\t\tg?: number | undefined,\n\t\tb?: number | undefined,\n\t\talpha?: number | undefined,\n\t]\n>((r, g, b, alpha) => {\n\tconst color = new Color(r, g, b, alpha);\n\treturn {\n\t\tinstance: color,\n\t\treset(r, g, b, alpha) {\n\t\t\tif (arguments.length === 0) {\n\t\t\t\tcolor.setColor();\n\t\t\t} else {\n\t\t\t\tif (typeof r === \"number\") {\n\t\t\t\t\tcolor.setColor(r, g, b, alpha);\n\t\t\t\t} else {\n\t\t\t\t\tcolor.copy(r as Color | string);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n});\n","import * as device from \"../system/device.js\";\n\n/**\n * Create and return a new Canvas element (or `OffscreenCanvas` when\n * supported and `returnOffscreenCanvas` is true). The single low-level\n * allocator used by every renderer-side scratch / fallback / render-\n * target canvas in the engine.\n *\n * Lives in its own module (not on `Renderer` directly) to break the\n * circular dependency between `Renderer` and `CanvasRenderTarget` —\n * both can import this helper without referencing each other.\n * `Renderer.createCanvas` re-exposes it as a static method for the\n * public-facing API; internal callers can import it directly.\n *\n * Gates the OffscreenCanvas path on `device.offscreenCanvas` (the\n * vetted capability check — actually instantiates + verifies\n * `getContext(\"2d\")` works inside a try/catch, covering historical\n * Safari quirks).\n * @param {number} width - canvas width in pixels\n * @param {number} height - canvas height in pixels\n * @param {boolean} [returnOffscreenCanvas=false] - return an\n *   `OffscreenCanvas` if the platform supports it\n * @returns {HTMLCanvasElement|OffscreenCanvas} a new canvas of the given size\n * @ignore\n */\nexport function createCanvas(width, height, returnOffscreenCanvas = false) {\n\tif (width === 0 || height === 0) {\n\t\tthrow new Error(\n\t\t\t\"width or height was zero, Canvas could not be initialized !\",\n\t\t);\n\t}\n\tif (returnOffscreenCanvas === true && device.offscreenCanvas === true) {\n\t\tconst c = new globalThis.OffscreenCanvas(width, height);\n\t\t// stub `style` for compatibility — OffscreenCanvas is detached\n\t\t// from the DOM but downstream code may read it\n\t\tif (typeof c.style === \"undefined\") {\n\t\t\tc.style = {};\n\t\t}\n\t\treturn c;\n\t}\n\tconst c = globalThis.document.createElement(\"canvas\");\n\tc.width = width;\n\tc.height = height;\n\treturn c;\n}\n","/**\n * Abstract base class for offscreen render targets.\n * Provides a renderer-agnostic interface for binding, clearing, resizing,\n * and reading back pixel data from an offscreen surface.\n * Concrete implementations:\n * - {@link WebGLRenderTarget} — WebGL framebuffer object (FBO) for post-processing\n * - {@link CanvasRenderTarget} — 2D canvas surface for texture generation\n *\n * A future WebGPU implementation would extend this class with\n * GPUTexture / GPURenderPassDescriptor management.\n * @category Rendering\n */\nexport default abstract class RenderTarget {\n\t/**\n\t * The width of this render target in pixels.\n\t */\n\tdeclare width: number;\n\n\t/**\n\t * The height of this render target in pixels.\n\t */\n\tdeclare height: number;\n\n\t/**\n\t * Bind this render target as the active draw destination.\n\t * All subsequent draw calls will render into this target until {@link unbind} is called.\n\t * No-op by default — subclasses override for GPU render targets\n\t * (a WebGL FBO today, a `GPURenderPassEncoder` once the WebGPU\n\t * renderer lands).\n\t */\n\tbind(): void {}\n\n\t/**\n\t * Unbind this render target, restoring the default (screen) output.\n\t * No-op by default — subclasses override for GPU render targets.\n\t */\n\tunbind(): void {}\n\n\t/**\n\t * Resize the render target's backing storage to the given dimensions.\n\t * @param width - new width in pixels\n\t * @param height - new height in pixels\n\t */\n\tabstract resize(width: number, height: number): void;\n\n\t/**\n\t * Clear the render target contents.\n\t */\n\tabstract clear(): void;\n\n\t/**\n\t * Release all resources held by this render target.\n\t * The target must not be used after calling destroy.\n\t */\n\tabstract destroy(): void;\n\n\t/**\n\t * Read back pixel data from this render target.\n\t * @param x - x coordinate of the top-left corner (default 0)\n\t * @param y - y coordinate of the top-left corner (default 0)\n\t * @param width - width of the area to read (default full width)\n\t * @param height - height of the area to read (default full height)\n\t * @returns an ImageData object containing the pixel data\n\t */\n\tabstract getImageData(\n\t\tx?: number,\n\t\ty?: number,\n\t\twidth?: number,\n\t\theight?: number,\n\t): ImageData;\n\n\t/**\n\t * Creates a Blob object representing the image contained in this render target.\n\t * @param type - a string indicating the image format (default \"image/png\")\n\t * @param quality - a number between 0 and 1 for lossy formats (e.g. image/jpeg)\n\t * @returns a Promise resolving to a Blob\n\t */\n\ttoBlob(type = \"image/png\", quality?: number): Promise<Blob> {\n\t\tconst imageData = this.getImageData();\n\t\tif (typeof OffscreenCanvas !== \"undefined\") {\n\t\t\tconst canvas = new OffscreenCanvas(this.width, this.height);\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\t\t\tif (!ctx) {\n\t\t\t\treturn Promise.reject(new Error(\"Failed to get 2d context\"));\n\t\t\t}\n\t\t\tctx.putImageData(imageData, 0, 0);\n\t\t\tconst options: { type: string; quality?: number } = { type };\n\t\t\tif (typeof quality !== \"undefined\") {\n\t\t\t\toptions.quality = quality;\n\t\t\t}\n\t\t\treturn canvas.convertToBlob(options);\n\t\t}\n\t\tconst canvas = document.createElement(\"canvas\");\n\t\tcanvas.width = this.width;\n\t\tcanvas.height = this.height;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) {\n\t\t\treturn Promise.reject(new Error(\"Failed to get 2d context\"));\n\t\t}\n\t\tctx.putImageData(imageData, 0, 0);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tcanvas.toBlob(\n\t\t\t\t(blob) => {\n\t\t\t\t\tif (blob) {\n\t\t\t\t\t\tresolve(blob);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treject(new Error(`toBlob failed for type \"${type}\"`));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttype,\n\t\t\t\tquality,\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Creates an ImageBitmap object from the current contents of this render target.\n\t * @returns a Promise resolving to an ImageBitmap\n\t */\n\ttoImageBitmap(): Promise<ImageBitmap> {\n\t\tconst imageData = this.getImageData();\n\t\treturn globalThis.createImageBitmap(imageData);\n\t}\n\n\t/**\n\t * Returns a data URL containing a representation of the current contents.\n\t * @param type - a string indicating the image format (default \"image/png\")\n\t * @param quality - a number between 0 and 1 for lossy formats (e.g. image/jpeg)\n\t * @returns a Promise resolving to a data URL string\n\t */\n\ttoDataURL(type = \"image/png\", quality?: number): Promise<string> {\n\t\treturn this.toBlob(type, quality).then((blob) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treturn new Promise<string>((resolve, reject) => {\n\t\t\t\treader.onload = () => {\n\t\t\t\t\tresolve(reader.result as string);\n\t\t\t\t};\n\t\t\t\treader.onerror = () => {\n\t\t\t\t\treject(new Error(reader.error?.message ?? \"FileReader failed\"));\n\t\t\t\t};\n\t\t\t\treader.onabort = () => {\n\t\t\t\t\treject(new Error(\"FileReader aborted\"));\n\t\t\t\t};\n\t\t\t\treader.readAsDataURL(blob);\n\t\t\t});\n\t\t});\n\t}\n}\n","import { clamp } from \"../../math/math.ts\";\nimport { setPrefixed } from \"../../utils/agent.ts\";\nimport { createCanvas } from \"../canvas_factory.js\";\nimport RenderTarget from \"./rendertarget.ts\";\n\n/**\n * additional import for TypeScript\n * @import CanvasRenderer from \"./../canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../webgl/webgl_renderer.js\";\n */\n\n// default canvas settings\nconst defaultAttributes = {\n\toffscreenCanvas: false,\n\twillReadFrequently: false,\n\tantiAlias: false,\n\tcontext: \"2d\",\n\ttransparent: false,\n\tpremultipliedAlpha: true,\n\tstencil: true,\n\tblendMode: \"normal\",\n\tfailIfMajorPerformanceCaveat: true,\n\tpowerPreference: \"default\",\n};\n\n// a helper function to create the 2d/webgl context\nfunction createContext(canvas, attributes) {\n\tlet context;\n\n\tif (attributes.context === \"2d\") {\n\t\t// 2d/canvas mode\n\t\tcontext = canvas.getContext(attributes.context, {\n\t\t\twillReadFrequently: attributes.willReadFrequently,\n\t\t});\n\t} else if (attributes.context === \"webgl\") {\n\t\tconst attr = {\n\t\t\talpha: attributes.transparent,\n\t\t\tantialias: attributes.antiAlias,\n\t\t\tdepth: true,\n\t\t\tstencil: true,\n\t\t\tpreserveDrawingBuffer: false,\n\t\t\tpremultipliedAlpha: attributes.transparent\n\t\t\t\t? attributes.premultipliedAlpha\n\t\t\t\t: false,\n\t\t\tpowerPreference: attributes.powerPreference,\n\t\t\tfailIfMajorPerformanceCaveat: attributes.failIfMajorPerformanceCaveat,\n\t\t};\n\n\t\t// WebGL 2 only since 20.0 (#1509) — no WebGL 1 fallback; callers\n\t\t// (autoDetectRenderer / Application) fall back to the Canvas\n\t\t// renderer instead when this throws\n\t\tcontext = canvas.getContext(\"webgl2\", attr);\n\n\t\tif (!context) {\n\t\t\tthrow new Error(\"A WebGL 2 context could not be created.\");\n\t\t}\n\t} else if (attributes.context === \"webgpu\") {\n\t\t// the GPUCanvasContext handle is acquired synchronously; configuring\n\t\t// it against a device happens later, in `WebGPURenderer.init()`, once\n\t\t// the (inherently asynchronous) adapter/device negotiation completed\n\t\tcontext = canvas.getContext(\"webgpu\");\n\n\t\tif (!context) {\n\t\t\tthrow new Error(\n\t\t\t\t\"A WebGPU context could not be created (WebGPU is not supported in this environment).\",\n\t\t\t);\n\t\t}\n\t} else {\n\t\tthrow new Error(\n\t\t\t\"Invalid context type. Must be one of '2d', 'webgl' or 'webgpu'\",\n\t\t);\n\t}\n\n\t// set the context size\n\treturn context;\n}\n\n/**\n * A 2D canvas render target for offscreen texture generation.\n * Used by Text, Gradient, Light2d and Particle systems to render into\n * a canvas that is then uploaded as a WebGL texture.\n * @augments RenderTarget\n * @category Rendering\n */\nclass CanvasRenderTarget extends RenderTarget {\n\t/**\n\t * @param {number} width - the desired width of the canvas\n\t * @param {number} height - the desired height of the canvas\n\t * @param {object} attributes - The attributes to create both the canvas and context\n\t * @param {string} [attributes.context=\"2d\"] - the context type to be created (\"2d\", \"webgl\" — a \"webgl\" request creates a WebGL 2 context — or \"webgpu\")\n\t * @param {boolean} [attributes.transparent=false] - specify if the canvas contains an alpha channel\n\t * @param {boolean} [attributes.offscreenCanvas=false] - will create an offscreenCanvas if true instead of a standard canvas\n\t * @param {boolean} [attributes.willReadFrequently=false] - Indicates whether or not a lot of read-back operations are planned\n\t * @param {boolean} [attributes.antiAlias=false] - Whether to enable anti-aliasing, use false (default) for a pixelated effect.\n\t */\n\tconstructor(width, height, attributes = defaultAttributes) {\n\t\tsuper();\n\t\t// clean up the given attributes\n\t\tthis.attributes = Object.assign({}, defaultAttributes, attributes);\n\n\t\t// make sure context is defined\n\t\tif (typeof attributes.context === \"undefined\") {\n\t\t\tattributes.context = \"2d\";\n\t\t}\n\n\t\t// used the given canvas if any\n\t\tif (typeof attributes.canvas !== \"undefined\") {\n\t\t\tthis.canvas = attributes.canvas;\n\t\t} else {\n\t\t\tthis.canvas = createCanvas(\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tthis.attributes.offscreenCanvas,\n\t\t\t);\n\t\t}\n\n\t\t// create the context\n\t\tthis.context = createContext(this.canvas, this.attributes);\n\n\t\tif (this.attributes.context === \"webgl\") {\n\t\t\t/**\n\t\t\t * The WebGL version of the created context.\n\t\t\t * @type {number}\n\t\t\t * @deprecated since 20.0.0 — the WebGL renderer is WebGL 2 only, this is always `2`\n\t\t\t */\n\t\t\tthis.WebGLVersion = 2;\n\t\t}\n\n\t\t// enable or disable antiAlias if specified\n\t\tthis.setAntiAlias(this.attributes.antiAlias);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tonResetEvent(width, height) {\n\t\tthis.clear();\n\t\tthis.resize(width, height);\n\t}\n\n\t/**\n\t * Clears the content of the canvas texture\n\t */\n\tclear() {\n\t\tthis.context.setTransform(1, 0, 0, 1, 0, 0);\n\t\tthis.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n\t}\n\n\t/**\n\t * enable/disable image smoothing (scaling interpolation)\n\t * @param {boolean} [enable=false] - whether to enable or not image smoothing (scaling interpolation)\n\t */\n\tsetAntiAlias(enable = false) {\n\t\tconst canvas = this.canvas;\n\n\t\t// enable/disable antialias on the given Context2d object\n\t\tsetPrefixed(\"imageSmoothingEnabled\", enable, this.context);\n\n\t\t// set antialias CSS property on the main canvas\n\t\tif (typeof canvas.style !== \"undefined\") {\n\t\t\tif (enable !== true) {\n\t\t\t\t// https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"optimizeSpeed\"; // legal fallback\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"-moz-crisp-edges\"; // Firefox\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"-o-crisp-edges\"; // Opera\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"-webkit-optimize-contrast\"; // Safari\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"optimize-contrast\"; // CSS 3\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"crisp-edges\"; // CSS 4\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"pixelated\"; // CSS 4\n\t\t\t\tcanvas.style.msInterpolationMode = \"nearest-neighbor\"; // IE8+\n\t\t\t} else {\n\t\t\t\tcanvas.style[\"image-rendering\"] = \"auto\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resizes the canvas texture to the given width and height.\n\t * @param {number} width - the desired width\n\t * @param {number} height - the desired height\n\t */\n\tresize(width, height) {\n\t\tthis.canvas.width = Math.round(width);\n\t\tthis.canvas.height = Math.round(height);\n\t}\n\n\t/**\n\t * Returns an ImageData object representing the underlying pixel data for a specified portion of this canvas texture.\n\t * (Note: when using getImageData(), it is highly recommended to use the `willReadFrequently` attribute when creatimg the corresponding canvas texture)\n\t * @param {number} x - The x-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted\n\t * @param {number} y - The y-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted\n\t * @param {number} width - The width of the rectangle from which the ImageData will be extracted. Positive values are to the right, and negative to the left\n\t * @param {number} height - The height of the rectangle from which the ImageData will be extracted. Positive values are down, and negative are up\n\t * @returns {ImageData} The ImageData extracted from this CanvasRenderTarget.\n\t */\n\tgetImageData(x, y, width, height) {\n\t\t// clamp values\n\t\tx = clamp(Math.floor(x), 0, this.canvas.width - 1);\n\t\ty = clamp(Math.floor(y), 0, this.canvas.height - 1);\n\t\twidth = clamp(width, 1, this.canvas.width - x);\n\t\theight = clamp(height, 1, this.canvas.height - y);\n\t\t// return imageData\n\t\treturn this.context.getImageData(x, y, width, height);\n\t}\n\n\t/**\n\t * Returns a data URL directly from the canvas (avoids the getImageData/Blob round-trip).\n\t * Note: not supported by OffscreenCanvas — falls back to the base implementation.\n\t * @param {string} [type=\"image/png\"] - A string indicating the image format\n\t * @param {number} [quality] - A number between 0 and 1 for lossy formats (image/jpeg, image/webp)\n\t * @returns {Promise<string>} A Promise resolving to a data URL string\n\t */\n\ttoDataURL(type = \"image/png\", quality) {\n\t\tif (typeof this.canvas.toDataURL === \"function\") {\n\t\t\treturn Promise.resolve(this.canvas.toDataURL(type, quality));\n\t\t}\n\t\t// OffscreenCanvas doesn't have toDataURL — use base implementation\n\t\treturn super.toDataURL(type, quality);\n\t}\n\n\t/**\n\t * Creates a Blob directly from the canvas (avoids the getImageData round-trip).\n\t * @param {string} [type=\"image/png\"] - A string indicating the image format\n\t * @param {number} [quality] - A number between 0 and 1 for lossy formats (image/jpeg, image/webp)\n\t * @returns {Promise<Blob>} A Promise resolving to a Blob\n\t */\n\ttoBlob(type = \"image/png\", quality) {\n\t\tif (typeof this.canvas.convertToBlob === \"function\") {\n\t\t\t// OffscreenCanvas path\n\t\t\treturn this.canvas.convertToBlob({ type, quality });\n\t\t}\n\t\t// HTMLCanvasElement path\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.canvas.toBlob(\n\t\t\t\t(blob) => {\n\t\t\t\t\tif (blob) {\n\t\t\t\t\t\tresolve(blob);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treject(new Error(`toBlob failed for type \"${type}\"`));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttype,\n\t\t\t\tquality,\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Creates an ImageBitmap directly from the canvas (avoids the getImageData round-trip).\n\t * @returns {Promise<ImageBitmap>} A Promise resolving to an ImageBitmap\n\t */\n\ttoImageBitmap() {\n\t\treturn globalThis.createImageBitmap(this.canvas);\n\t}\n\n\t/**\n\t * invalidate the current CanvasRenderTarget, and force a reupload of the corresponding texture\n\t * (call this if you modify the canvas content between two draw calls)\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - the renderer to which this canvas texture is attached\n\t */\n\tinvalidate(renderer) {\n\t\tif (renderer.type.startsWith(\"WebGL\")) {\n\t\t\t// flush pending draws referencing the current texture data\n\t\t\trenderer.flush();\n\t\t\trenderer.setBatcher(\"quad\");\n\t\t\t// mark the content stale so the next upload refreshes the pixels\n\t\t\t// IN PLACE (same texture object — with immutable storage a\n\t\t\t// same-shape re-upload is a pure texSubImage2D, no reallocation)\n\t\t\tthis.glTextureUnit = renderer.cache.getUnit(\n\t\t\t\trenderer.cache.get(this.canvas),\n\t\t\t);\n\t\t\trenderer.currentBatcher.markTextureDirty(this.glTextureUnit);\n\t\t} else if (renderer.type === \"WebGPU\" && renderer.cache.has(this.canvas)) {\n\t\t\t// drain pending vertices, then re-upload the canvas. The store\n\t\t\t// gives the new pixels a fresh GPUTexture when draws recorded\n\t\t\t// earlier this frame reference the old ones (queue writes execute\n\t\t\t// before every recorded draw, so an in-place re-upload would\n\t\t\t// apply retroactively); otherwise it re-uploads in place.\n\t\t\trenderer.currentBatcher?.flush();\n\t\t\trenderer.textureStore.getBinding(renderer.cache.get(this.canvas), {\n\t\t\t\tforce: true,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Destroy this canvas render target and release associated GPU resources.\n\t * @param {CanvasRenderer|WebGLRenderer} [renderer] - the renderer to clean up WebGL resources from\n\t * @ignore\n\t */\n\tdestroy(renderer) {\n\t\t// Look up the cached texture by SOURCE identity (the `canvas`\n\t\t// itself), NOT by `this.glTextureUnit`. The previous code\n\t\t// path passed `getTexture2D(this.glTextureUnit)` into\n\t\t// `deleteTexture2D`, but the `QuadBatcher` recycles texture\n\t\t// units LRU under pressure (typically 8–16 hardware units),\n\t\t// so the unit stored at upload time may now reference a\n\t\t// different texture entirely — leading to either a crash\n\t\t// (`getTexture2D` returns `undefined` → `texture.getTexture`\n\t\t// throws) or, worse, silently deleting an unrelated texture\n\t\t// that now occupies the recycled unit. Resolving via\n\t\t// `renderer.cache.get(canvas)` is unit-recycling-safe: the\n\t\t// cache map is keyed on the source image, so the lookup\n\t\t// always returns the entry that actually belongs to *this*\n\t\t// render target.\n\t\tif (\n\t\t\trenderer &&\n\t\t\trenderer.type.startsWith(\"WebGL\") &&\n\t\t\tthis.canvas &&\n\t\t\trenderer.cache.has(this.canvas)\n\t\t) {\n\t\t\trenderer.setBatcher(\"quad\");\n\t\t\trenderer.currentBatcher.deleteTexture2D(renderer.cache.get(this.canvas));\n\t\t} else if (\n\t\t\trenderer &&\n\t\t\trenderer.type === \"WebGPU\" &&\n\t\t\tthis.canvas &&\n\t\t\trenderer.cache.has(this.canvas)\n\t\t) {\n\t\t\t// destroy the resident GPUTexture(s) for this canvas source\n\t\t\trenderer.textureStore.destroyTexture(renderer.cache.get(this.canvas));\n\t\t}\n\t\tif (renderer) {\n\t\t\trenderer.cache.delete(this.canvas);\n\t\t}\n\t\tthis.glTextureUnit = undefined;\n\t\tthis.context = undefined;\n\t\tthis.canvas = undefined;\n\t}\n\n\t/**\n\t * The width of this canvas texture in pixels\n\t * @public\n\t * @type {number}\n\t */\n\tget width() {\n\t\treturn this.canvas.width;\n\t}\n\n\tset width(val) {\n\t\tthis.canvas.width = Math.round(val);\n\t}\n\n\t/**\n\t * The height of this canvas texture in pixels\n\t * @public\n\t * @type {number}\n\t */\n\tget height() {\n\t\treturn this.canvas.height;\n\t}\n\n\tset height(val) {\n\t\tthis.canvas.height = Math.round(val);\n\t}\n}\n\nexport default CanvasRenderTarget;\n","import { colorPool } from \"../math/color.ts\";\n\n/**\n * @import {Color} from \"../math/color.ts\";\n */\nimport CanvasRenderTarget from \"./rendertarget/canvasrendertarget.js\";\n\n/**\n * The gradient bake resolution: gradients are rasterized into a FIXED\n * 256×256 shared target regardless of on-screen size and stretched by the\n * destination quad — a gradient is piecewise-linear between its stops and\n * the GPU interpolates linearly between texels, so a capped bake is\n * visually equivalent to a full-size one. The fixed size means the shared\n * target is allocated exactly once and never resized: every re-bake is a\n * same-size texture update (the cheap path on every backend), and memory\n * is capped at 256 KB instead of growing with the largest gradient drawn.\n * @ignore\n */\nconst GRADIENT_BAKE_SIZE = 256;\n\n/**\n * Shared render target for GPU gradient textures.\n * Reused across all Gradient instances to avoid GPU memory leaks.\n * @ignore\n */\nlet sharedRenderTarget = null;\nlet sharedLastId = -1;\nlet sharedLastX = NaN;\nlet sharedLastY = NaN;\nlet sharedLastW = NaN;\nlet sharedLastH = NaN;\nlet nextGradientId = 0;\n\n/**\n * A Gradient object representing a linear or radial gradient fill.\n * Created via {@link Renderer#createLinearGradient} or {@link Renderer#createRadialGradient}.\n * Can be passed to {@link Renderer#setColor} as a fill style.\n */\nexport class Gradient {\n\t/**\n\t * @param {\"linear\"|\"radial\"} type - the gradient type\n\t * @param {number[]} coords - gradient coordinates [x0, y0, x1, y1] for linear, [x0, y0, r0, x1, y1, r1] for radial\n\t */\n\tconstructor(type, coords) {\n\t\t/**\n\t\t * gradient type\n\t\t * @type {\"linear\"|\"radial\"}\n\t\t */\n\t\t/** @ignore */\n\t\tthis._id = nextGradientId++;\n\n\t\tthis.type = type;\n\n\t\t/**\n\t\t * gradient coordinates\n\t\t * @type {number[]}\n\t\t * @ignore\n\t\t */\n\t\tthis.coords = coords;\n\n\t\t/**\n\t\t * color stops\n\t\t * @type {Array<{offset: number, color: string}>}\n\t\t * @ignore\n\t\t */\n\t\tthis.colorStops = [];\n\n\t\t/**\n\t\t * cached canvas gradient (for Canvas renderer)\n\t\t * @type {CanvasGradient|undefined}\n\t\t * @ignore\n\t\t */\n\t\tthis._canvasGradient = undefined;\n\n\t\t/**\n\t\t * cached gradient render target (for WebGL renderer)\n\t\t * @type {CanvasRenderTarget|undefined}\n\t\t * @ignore\n\t\t */\n\t\tthis._renderTarget = undefined;\n\n\t\t/**\n\t\t * whether the gradient needs to be regenerated\n\t\t * @type {boolean}\n\t\t * @ignore\n\t\t */\n\t\tthis._dirty = true;\n\n\t\t/**\n\t\t * cached parsed Color objects for sampling (lazily built)\n\t\t * @type {{offset: number, color: Color}[]|null}\n\t\t * @ignore\n\t\t */\n\t\tthis._parsedStops = null;\n\t}\n\n\t/**\n\t * Add a color stop to the gradient.\n\t * @param {number} offset - value between 0.0 and 1.0\n\t * @param {Color|string} color - a CSS color string or Color object\n\t * @returns {Gradient} this gradient for chaining\n\t * @example\n\t * gradient.addColorStop(0, \"#FF0000\");\n\t * gradient.addColorStop(0.5, \"green\");\n\t * gradient.addColorStop(1, \"blue\");\n\t */\n\taddColorStop(offset, color) {\n\t\tif (offset < 0.0 || offset > 1.0) {\n\t\t\tthrow new Error(\"offset must be between 0.0 and 1.0\");\n\t\t}\n\t\tthis.colorStops.push({\n\t\t\toffset,\n\t\t\tcolor: typeof color === \"string\" ? color : color.toRGBA(),\n\t\t});\n\t\tthis._dirty = true;\n\t\tthis._canvasGradient = undefined;\n\t\t// invalidate parsed Color cache so getColorAt() rebuilds on next call\n\t\tif (this._parsedStops) {\n\t\t\tfor (const stop of this._parsedStops) {\n\t\t\t\tcolorPool.release(stop.color);\n\t\t\t}\n\t\t\tthis._parsedStops = null;\n\t\t}\n\t\t// keep _renderTarget alive — the _dirty flag will trigger re-rendering\n\t\t// in toCanvasGradient() / toCanvas(), avoiding a GL texture leak\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get or create a native CanvasGradient for use with a 2D context.\n\t * @param {CanvasRenderingContext2D} context - the 2D context to create the gradient on\n\t * @returns {CanvasGradient}\n\t * @ignore\n\t */\n\ttoCanvasGradient(context) {\n\t\tif (this._canvasGradient && !this._dirty) {\n\t\t\treturn this._canvasGradient;\n\t\t}\n\n\t\tconst c = this.coords;\n\t\tif (this.type === \"linear\") {\n\t\t\tthis._canvasGradient = context.createLinearGradient(\n\t\t\t\tc[0],\n\t\t\t\tc[1],\n\t\t\t\tc[2],\n\t\t\t\tc[3],\n\t\t\t);\n\t\t} else {\n\t\t\tthis._canvasGradient = context.createRadialGradient(\n\t\t\t\tc[0],\n\t\t\t\tc[1],\n\t\t\t\tc[2],\n\t\t\t\tc[3],\n\t\t\t\tc[4],\n\t\t\t\tc[5],\n\t\t\t);\n\t\t}\n\n\t\tfor (const stop of this.colorStops) {\n\t\t\tthis._canvasGradient.addColorStop(stop.offset, stop.color);\n\t\t}\n\n\t\tthis._dirty = false;\n\t\treturn this._canvasGradient;\n\t}\n\n\t/**\n\t * Render the gradient into the fixed-resolution shared bake target for\n\t * the given draw rect. Uses the original gradient coordinates so the\n\t * result matches Canvas 2D behavior. Rects larger than the bake target\n\t * are rasterized scaled-down; the destination quad stretches them back,\n\t * which is visually equivalent (linear stop interpolation × linear\n\t * texture filtering). The returned `width`/`height` describe the region\n\t * of the canvas the caller must use as the drawImage SOURCE rect.\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - the active renderer (used to invalidate the GPU texture)\n\t * @param {number} x - draw rect x\n\t * @param {number} y - draw rect y\n\t * @param {number} width - draw rect width\n\t * @param {number} height - draw rect height\n\t * @returns {{canvas: HTMLCanvasElement|OffscreenCanvas, width: number, height: number}} the shared gradient canvas + the used source-rect size\n\t * @ignore\n\t */\n\ttoCanvas(renderer, x, y, width, height) {\n\t\tconst w = Math.max(1, width);\n\t\tconst h = Math.max(1, height);\n\t\t// bake 1:1 up to the target size, scaled-down beyond it\n\t\tconst sx = w > GRADIENT_BAKE_SIZE ? GRADIENT_BAKE_SIZE / w : 1;\n\t\tconst sy = h > GRADIENT_BAKE_SIZE ? GRADIENT_BAKE_SIZE / h : 1;\n\t\tconst sw = w * sx;\n\t\tconst sh = h * sy;\n\n\t\t// skip if this gradient already rendered to the shared target for\n\t\t// this exact rect (the target is fixed-size, so the RECT — not the\n\t\t// canvas dimensions — is the identity of the last bake)\n\t\tif (\n\t\t\tsharedRenderTarget &&\n\t\t\tsharedLastId === this._id &&\n\t\t\t!this._dirty &&\n\t\t\tsharedLastX === x &&\n\t\t\tsharedLastY === y &&\n\t\t\tsharedLastW === w &&\n\t\t\tsharedLastH === h\n\t\t) {\n\t\t\tthis._renderTarget = sharedRenderTarget;\n\t\t\treturn { canvas: this._renderTarget.canvas, width: sw, height: sh };\n\t\t}\n\n\t\t// the shared target is allocated once and never resized — every\n\t\t// bake is a same-size update, the cheap path on every backend\n\t\tif (!sharedRenderTarget) {\n\t\t\tsharedRenderTarget = new CanvasRenderTarget(\n\t\t\t\tGRADIENT_BAKE_SIZE,\n\t\t\t\tGRADIENT_BAKE_SIZE,\n\t\t\t);\n\t\t}\n\t\tthis._renderTarget = sharedRenderTarget;\n\n\t\tconst ctx = this._renderTarget.context;\n\t\tctx.setTransform(1, 0, 0, 1, 0, 0);\n\t\tctx.clearRect(0, 0, GRADIENT_BAKE_SIZE, GRADIENT_BAKE_SIZE);\n\n\t\t// bake through the scale so gradient coordinates stay in draw-rect\n\t\t// (logical) space; the destination quad's stretch inverts it exactly\n\t\tctx.setTransform(sx, 0, 0, sy, 0, 0);\n\n\t\t// create gradient with coordinates offset to the draw rect origin\n\t\tconst c = this.coords;\n\t\tlet gradient;\n\n\t\tif (this.type === \"linear\") {\n\t\t\tgradient = ctx.createLinearGradient(\n\t\t\t\tc[0] - x,\n\t\t\t\tc[1] - y,\n\t\t\t\tc[2] - x,\n\t\t\t\tc[3] - y,\n\t\t\t);\n\t\t} else {\n\t\t\tgradient = ctx.createRadialGradient(\n\t\t\t\tc[0] - x,\n\t\t\t\tc[1] - y,\n\t\t\t\tc[2],\n\t\t\t\tc[3] - x,\n\t\t\t\tc[4] - y,\n\t\t\t\tc[5],\n\t\t\t);\n\t\t}\n\n\t\tfor (const stop of this.colorStops) {\n\t\t\tgradient.addColorStop(stop.offset, stop.color);\n\t\t}\n\n\t\tctx.fillStyle = gradient;\n\t\t// fill the WHOLE canvas (in logical units) so the padding beyond the\n\t\t// used region carries the extended gradient — linear filtering at the\n\t\t// source-rect edge then samples gradient-colored texels, not\n\t\t// transparent ones (same edge behavior as the old full-canvas bake)\n\t\tctx.fillRect(0, 0, GRADIENT_BAKE_SIZE / sx, GRADIENT_BAKE_SIZE / sy);\n\t\tctx.setTransform(1, 0, 0, 1, 0, 0);\n\n\t\tthis._dirty = false;\n\t\tsharedLastId = this._id;\n\t\tsharedLastX = x;\n\t\tsharedLastY = y;\n\t\tsharedLastW = w;\n\t\tsharedLastH = h;\n\t\tthis._renderTarget.invalidate(renderer);\n\t\treturn { canvas: this._renderTarget.canvas, width: sw, height: sh };\n\t}\n\n\t/**\n\t * Get the interpolated color at a given position along the gradient.\n\t * Useful for procedural effects like trails that need per-segment colors.\n\t * @param {number} position - position along the gradient (0.0–1.0)\n\t * @param {Color} out - output Color object to write into\n\t * @returns {Color} the output Color object\n\t * @example\n\t * const gradient = new Gradient(\"linear\", [0, 0, 1, 0]);\n\t * gradient.addColorStop(0, \"#ff0000\");\n\t * gradient.addColorStop(1, \"#0000ff\");\n\t * gradient.getColorAt(0.5, myColor); // myColor is now purple\n\t */\n\tgetColorAt(position, out) {\n\t\tif (!this._parsedStops) {\n\t\t\tthis._buildParsedStops();\n\t\t}\n\n\t\tconst stops = this._parsedStops;\n\t\tconst len = stops.length;\n\n\t\t// no stops defined\n\t\tif (len === 0) {\n\t\t\treturn out;\n\t\t}\n\n\t\t// single stop or before first\n\t\tif (len === 1 || position <= stops[0].offset) {\n\t\t\treturn out.copy(stops[0].color);\n\t\t}\n\n\t\t// at or past last stop\n\t\tif (position >= stops[len - 1].offset) {\n\t\t\treturn out.copy(stops[len - 1].color);\n\t\t}\n\n\t\t// find surrounding stops and interpolate in float space\n\t\tfor (let i = 0; i < len - 1; i++) {\n\t\t\tif (position >= stops[i].offset && position <= stops[i + 1].offset) {\n\t\t\t\tconst range = stops[i + 1].offset - stops[i].offset;\n\t\t\t\tconst frac = range > 0 ? (position - stops[i].offset) / range : 0;\n\t\t\t\tconst a = stops[i].color.toArray();\n\t\t\t\tconst b = stops[i + 1].color.toArray();\n\t\t\t\treturn out.setFloat(\n\t\t\t\t\ta[0] + (b[0] - a[0]) * frac,\n\t\t\t\t\ta[1] + (b[1] - a[1]) * frac,\n\t\t\t\t\ta[2] + (b[2] - a[2]) * frac,\n\t\t\t\t\ta[3] + (b[3] - a[3]) * frac,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Build the parsed Color cache from colorStops strings.\n\t * @ignore\n\t */\n\t_buildParsedStops() {\n\t\tthis._parsedStops = this.colorStops\n\t\t\t.map((stop) => {\n\t\t\t\treturn {\n\t\t\t\t\toffset: stop.offset,\n\t\t\t\t\tcolor: colorPool.get(stop.color),\n\t\t\t\t};\n\t\t\t})\n\t\t\t.sort((a, b) => {\n\t\t\t\treturn a.offset - b.offset;\n\t\t\t});\n\t}\n\n\t/**\n\t * Release pooled resources held by this gradient.\n\t */\n\tdestroy() {\n\t\tif (this._parsedStops) {\n\t\t\tfor (const stop of this._parsedStops) {\n\t\t\t\tcolorPool.release(stop.color);\n\t\t\t}\n\t\t\tthis._parsedStops = null;\n\t\t}\n\t}\n}\n","import { degToRad, pow } from \"../math/math.ts\";\n\n/**\n * @ignore\n */\nfunction correctRadii(\n\tsignedRx: number,\n\tsignedRy: number,\n\tx1p: number,\n\ty1p: number,\n) {\n\tconst prx = Math.abs(signedRx);\n\tconst pry = Math.abs(signedRy);\n\n\tconst A = pow(x1p) / pow(prx) + pow(y1p) / pow(pry);\n\n\tconst rx = A > 1 ? Math.sqrt(A) * prx : prx;\n\tconst ry = A > 1 ? Math.sqrt(A) * pry : pry;\n\n\treturn [rx, ry];\n}\n\n/**\n * @ignore\n */\nfunction mat2DotVec2(\n\t[m00, m01, m10, m11]: [number, number, number, number],\n\t[vx, vy]: [number, number],\n) {\n\treturn [m00 * vx + m01 * vy, m10 * vx + m11 * vy] as [number, number];\n}\n\n/**\n * @ignore\n */\nfunction vec2Add([ux, uy]: [number, number], [vx, vy]: [number, number]) {\n\treturn [ux + vx, uy + vy];\n}\n\n/**\n * @ignore\n */\nfunction vec2Scale([a0, a1]: [number, number], scalar: number) {\n\treturn [a0 * scalar, a1 * scalar];\n}\n\n/**\n * @ignore\n */\nfunction vec2Dot([ux, uy]: [number, number], [vx, vy]: [number, number]) {\n\treturn ux * vx + uy * vy;\n}\n\n/**\n * @ignore\n */\nfunction vec2Mag([ux, uy]: [number, number]) {\n\treturn Math.sqrt(ux ** 2 + uy ** 2);\n}\n\n/**\n * @ignore\n */\nfunction vec2Angle(u: [number, number], v: [number, number]) {\n\tconst [ux, uy] = u;\n\tconst [vx, vy] = v;\n\tconst sign = ux * vy - uy * vx >= 0 ? 1 : -1;\n\treturn sign * Math.acos(vec2Dot(u, v) / (vec2Mag(u) * vec2Mag(v)));\n}\n\n// From https://svgwg.org/svg2-draft/implnote.html#ArcConversionEndpointToCenter\n/**\n * Converts endpoint parameterization of an SVG arc to center parameterization.\n *\n * This function calculates the center (cx, cy), radii (rx, ry), start angle,\n * end angle, x-axis rotation, and sweep flag (anticlockwise) of an SVG arc path\n * given its endpoint parameterization.\n * @param x1 - The x-coordinate of the start point of the arc.\n * @param y1 - The y-coordinate of the start point of the arc.\n * @param x2 - The x-coordinate of the end point of the arc.\n * @param y2 - The y-coordinate of the end point of the arc.\n * @param largeArcFlag - The large arc flag. If true, the arc sweep will be greater than 180 degrees.\n * @param sweepFlag - The sweep flag. If true, the arc will be drawn in a \"positive-angle\" direction.\n * @param srx - The x-axis radius of the arc.\n * @param sry - The y-axis radius of the arc.\n * @param xAxisRotationDeg - The rotation angle in degrees of the x-axis of the ellipse relative to the x-axis of the coordinate system.\n * @returns An object containing the center coordinates, radii, start and end angles, x-axis rotation, and sweep direction of an SVG arc given its endpoint parameters.\n */\nexport function endpointToCenterParameterization(\n\tx1: number,\n\ty1: number,\n\tx2: number,\n\ty2: number,\n\tlargeArcFlag: boolean,\n\tsweepFlag: boolean,\n\tsrx: number,\n\tsry: number,\n\txAxisRotationDeg: number,\n) {\n\tconst xAxisRotation = degToRad(xAxisRotationDeg);\n\n\tconst cosphi = Math.cos(xAxisRotation);\n\tconst sinphi = Math.sin(xAxisRotation);\n\n\tconst [x1p, y1p] = mat2DotVec2(\n\t\t[cosphi, sinphi, -sinphi, cosphi],\n\t\t[(x1 - x2) / 2, (y1 - y2) / 2],\n\t);\n\n\tconst [rx, ry] = correctRadii(srx, sry, x1p, y1p);\n\n\tconst sign = largeArcFlag !== sweepFlag ? 1 : -1;\n\tconst n = pow(rx) * pow(ry) - pow(rx) * pow(y1p) - pow(ry) * pow(x1p);\n\tconst d = pow(rx) * pow(y1p) + pow(ry) * pow(x1p);\n\n\tconst [cxp, cyp] = vec2Scale(\n\t\t[(rx * y1p) / ry, (-ry * x1p) / rx],\n\t\tsign * Math.sqrt(Math.abs(n / d)),\n\t);\n\n\tconst [cx, cy] = vec2Add(\n\t\tmat2DotVec2([cosphi, -sinphi, sinphi, cosphi], [cxp, cyp]),\n\t\t[(x1 + x2) / 2, (y1 + y2) / 2],\n\t);\n\n\tconst a = [(x1p - cxp) / rx, (y1p - cyp) / ry] satisfies [number, number];\n\tconst b = [(-x1p - cxp) / rx, (-y1p - cyp) / ry] satisfies [number, number];\n\tconst startAngle = vec2Angle([1, 0], a);\n\tconst deltaAngle0 = vec2Angle(a, b) % (2 * Math.PI);\n\n\tconst deltaAngle =\n\t\t!sweepFlag && deltaAngle0 > 0\n\t\t\t? deltaAngle0 - 2 * Math.PI\n\t\t\t: sweepFlag && deltaAngle0 < 0\n\t\t\t\t? deltaAngle0 + 2 * Math.PI\n\t\t\t\t: deltaAngle0;\n\n\tconst endAngle = startAngle + deltaAngle;\n\n\treturn {\n\t\tcx,\n\t\tcy,\n\t\trx,\n\t\try,\n\t\tstartAngle,\n\t\tendAngle,\n\t\txAxisRotation,\n\t\tanticlockwise: deltaAngle < 0,\n\t};\n}\n","import { TAU } from \"./../math/math.ts\";\nimport { earcut } from \"./earcut.ts\";\nimport { type Point, pointPool } from \"./point.ts\";\nimport { endpointToCenterParameterization } from \"./toarccanvas.ts\";\n\n/**\n * A 2D path builder used by both the WebGL and Canvas renderers to build and\n * interpolate paths from SVG command strings or direct method calls. Its method\n * semantics follow the standard\n * [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) API.\n *\n * Each renderer exposes a reusable instance as `renderer.path2D`; you can also\n * create your own with `new Path2D()`. Build a path (via {@link parseSVGPath} or\n * the imperative {@link moveTo}/{@link lineTo}/curve methods), then draw it with\n * the renderer's `stroke()` (outline) and `fill()` (solid) methods. Curves and\n * arcs are flattened into line segments; {@link arcResolution} controls how fine\n * that approximation is.\n *\n * **Sub-paths & holes:** a new sub-path begins on every {@link moveTo} (and on\n * each `M` in an SVG string). When filled, the first sub-path is the outer\n * contour and each subsequent sub-path is treated as a hole — so shapes with\n * holes or disconnected regions (a donut, a ring, the letter \"O\", text outlines)\n * fill correctly. The Canvas renderer fills via the native non-zero winding rule\n * (so holes should be wound opposite to the outer contour); the WebGL renderer\n * triangulates with holes via earcut, which matches that result for the usual\n * case of oppositely-wound holes. Sub-path boundaries are tracked in\n * {@link subPaths}.\n *\n * Supported SVG commands: `M`, `L`, `H`, `V`, `Q`, `C`, `A`, `Z` (uppercase /\n * absolute only; `H` and `V` take a relative offset).\n * @example\n * // build a path with the imperative API, then fill and outline it\n * const path = renderer.path2D;\n * path.beginPath();\n * path.moveTo(50, 0);\n * path.lineTo(100, 100);\n * path.lineTo(0, 100);\n * path.closePath();\n * renderer.setColor(\"#4CAF50\");\n * renderer.fill(); // fill the current path\n * renderer.setColor(\"#1B5E20\");\n * renderer.stroke(); // outline the current path\n * @example\n * // the same triangle from an SVG path string\n * renderer.path2D.parseSVGPath(\"M 50 0 L 100 100 L 0 100 Z\");\n * renderer.fill();\n * @category Geometry\n */\nclass Path2D {\n\t/**\n\t * the points defining the current path\n\t */\n\tpoints: Point[] = [];\n\n\t/**\n\t * start index (into {@link points}) of each sub-path after the first; used\n\t * as the hole indices when triangulating, and as the `moveTo` boundaries\n\t * when the Canvas renderer replays the path.\n\t */\n\tsubPaths: number[] = [];\n\n\t/**\n\t * whether the pen has been explicitly placed (via {@link moveTo}) since\n\t * the last {@link beginPath} — distinguishes \"virgin path\" from \"pen\n\t * parked at (0, 0)\" so {@link arc}/{@link ellipse}/{@link arcTo} know\n\t * whether to start fresh or connect from the current point (the native\n\t * Path2D behavior).\n\t * @ignore\n\t */\n\tprivate penMoved = false;\n\n\t/**\n\t * space between interpolated points for quadratic and bezier curve approx. in pixels.\n\t * @default 2\n\t */\n\tarcResolution = 2;\n\n\t/* @ignore */\n\tvertices: Point[] = [];\n\n\t/* @ignore */\n\tstartPoint: Point = pointPool.get();\n\n\t/* @ignore */\n\tisDirty = false;\n\n\tconstructor(svgPath?: string) {\n\t\tif (typeof svgPath === \"string\") {\n\t\t\tthis.parseSVGPath(svgPath);\n\t\t}\n\t}\n\n\t/**\n\t * Parses an SVG path string and generates interpolated points for rendering.\n\t * Clears any existing path data before parsing (calls {@link beginPath} internally).\n\t *\n\t * Supported commands:\n\t * - **M** x y — move to (starts a new sub-path)\n\t * - **L** x y — line to\n\t * - **H** dx — horizontal line (relative offset from current x)\n\t * - **V** dy — vertical line (relative offset from current y)\n\t * - **Q** cx cy x y — quadratic Bézier curve\n\t * - **C** cx1 cy1 cx2 cy2 x y — cubic Bézier curve\n\t * - **A** rx ry xRot largeArc sweep x y — elliptical arc\n\t * - **Z** — close the current sub-path (line back to its starting point)\n\t *\n\t * Multiple `M` commands define separate sub-paths; when filled the first is\n\t * the outer contour and the rest are holes. After parsing, the generated\n\t * points are available in {@link points} and can be rendered using the\n\t * renderer's `stroke()` and `fill()` methods.\n\t * @example\n\t * // draw a heart shape\n\t * renderer.path2D.parseSVGPath(\n\t *   \"M 10 30 A 20 20 0 0 1 50 30 A 20 20 0 0 1 90 30 Q 90 60 50 90 Q 10 60 10 30 Z\"\n\t * );\n\t * renderer.setColor(\"#EF5350\");\n\t * renderer.fill();\n\t * @example\n\t * // a donut: an outer ring with an inner hole (two `M` sub-paths)\n\t * renderer.path2D.parseSVGPath(\n\t *   \"M 0 40 A 40 40 0 1 0 80 40 A 40 40 0 1 0 0 40 Z \" +\n\t *   \"M 20 40 A 20 20 0 1 0 60 40 A 20 20 0 1 0 20 40 Z\"\n\t * );\n\t * renderer.fill();\n\t * @param svgPath - An SVG path data string (e.g. \"M 0 0 L 100 0 L 50 100 Z\").\n\t * Only uppercase (absolute) commands are supported.\n\t */\n\tparseSVGPath(svgPath: string) {\n\t\t// Split path into commands and coordinates\n\t\tconst pathCommands = svgPath.match(/([a-df-z])[^a-df-z]*/gi);\n\t\tconst startPoint = this.startPoint;\n\n\t\tthis.beginPath();\n\n\t\tif (pathCommands === null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Process each command and corresponding coordinates\n\t\tfor (let i = 0; i < pathCommands.length; i++) {\n\t\t\tconst pathCommand = pathCommands[i];\n\t\t\tconst command = pathCommand[0].toUpperCase();\n\t\t\tconst coordinates = pathCommand\n\t\t\t\t.slice(1)\n\t\t\t\t.trim()\n\t\t\t\t.split(/[\\s,]+/)\n\t\t\t\t.map(parseFloat);\n\n\t\t\tswitch (command) {\n\t\t\t\tcase \"A\":\n\t\t\t\t\t{\n\t\t\t\t\t\t// SVG A command: rx ry xAxisRotation largeArcFlag sweepFlag x y\n\t\t\t\t\t\t// the current pen position is always tracked by startPoint (set\n\t\t\t\t\t\t// by the preceding M/line/curve). Use it rather than the last\n\t\t\t\t\t\t// pushed point so an arc that is the first command of a new\n\t\t\t\t\t\t// sub-path (e.g. a circular hole) starts from the right place.\n\t\t\t\t\t\tconst [rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y] =\n\t\t\t\t\t\t\tcoordinates;\n\t\t\t\t\t\tconst p = endpointToCenterParameterization(\n\t\t\t\t\t\t\tstartPoint.x,\n\t\t\t\t\t\t\tstartPoint.y,\n\t\t\t\t\t\t\tx,\n\t\t\t\t\t\t\ty,\n\t\t\t\t\t\t\t// SVG flags are parsed as 0/1; the parameterization expects booleans\n\t\t\t\t\t\t\tlargeArcFlag !== 0,\n\t\t\t\t\t\t\tsweepFlag !== 0,\n\t\t\t\t\t\t\trx,\n\t\t\t\t\t\t\try,\n\t\t\t\t\t\t\txAxisRotation,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// generate arc points inline using lineTo — the SVG\n\t\t\t\t\t\t// endpoint parameterization (large-arc/sweep flags)\n\t\t\t\t\t\t// needs custom math that ellipse() doesn't expose\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlet { startAngle, endAngle } = p;\n\t\t\t\t\t\t\tconst {\n\t\t\t\t\t\t\t\tcx,\n\t\t\t\t\t\t\t\tcy,\n\t\t\t\t\t\t\t\trx: arcRx,\n\t\t\t\t\t\t\t\try: arcRy,\n\t\t\t\t\t\t\t\txAxisRotation: rot,\n\t\t\t\t\t\t\t\tanticlockwise,\n\t\t\t\t\t\t\t} = p;\n\n\t\t\t\t\t\t\tif (startAngle === endAngle) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst fullCircle = anticlockwise\n\t\t\t\t\t\t\t\t? Math.abs(startAngle - endAngle) >= TAU\n\t\t\t\t\t\t\t\t: Math.abs(endAngle - startAngle) >= TAU;\n\n\t\t\t\t\t\t\tstartAngle = startAngle % TAU;\n\t\t\t\t\t\t\tendAngle = endAngle % TAU;\n\t\t\t\t\t\t\tif (startAngle < 0) {\n\t\t\t\t\t\t\t\tstartAngle += TAU;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (endAngle < 0) {\n\t\t\t\t\t\t\t\tendAngle += TAU;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (startAngle >= endAngle) {\n\t\t\t\t\t\t\t\tendAngle += TAU;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet diff = endAngle - startAngle;\n\t\t\t\t\t\t\tlet direction = 1;\n\t\t\t\t\t\t\tif (anticlockwise) {\n\t\t\t\t\t\t\t\tdirection = -1;\n\t\t\t\t\t\t\t\tdiff = TAU - diff;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (fullCircle) {\n\t\t\t\t\t\t\t\tdiff = TAU;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst length = (diff * arcRx + diff * arcRy) / 2;\n\t\t\t\t\t\t\tconst nr = length / this.arcResolution;\n\t\t\t\t\t\t\tconst angleStep = (diff / nr) * direction;\n\t\t\t\t\t\t\tconst cosRot = Math.cos(rot);\n\t\t\t\t\t\t\tconst sinRot = Math.sin(rot);\n\n\t\t\t\t\t\t\tlet angle = startAngle;\n\t\t\t\t\t\t\tfor (let j = 0; j < nr; j++) {\n\t\t\t\t\t\t\t\tconst ex = arcRx * Math.cos(angle);\n\t\t\t\t\t\t\t\tconst ey = arcRy * Math.sin(angle);\n\t\t\t\t\t\t\t\tthis.lineTo(\n\t\t\t\t\t\t\t\t\tcx + ex * cosRot - ey * sinRot,\n\t\t\t\t\t\t\t\t\tcy + ex * sinRot + ey * cosRot,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tangle += angleStep;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// end exactly at the SVG endpoint\n\t\t\t\t\t\t\tthis.lineTo(x, y);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"H\":\n\t\t\t\t\t// H take 1 coordinate, relative to the current pen position\n\t\t\t\t\tthis.lineTo(startPoint.x + coordinates[0], startPoint.y);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"V\":\n\t\t\t\t\t// V take 1 coordinate, relative to the current pen position\n\t\t\t\t\tthis.lineTo(startPoint.x, startPoint.y + coordinates[0]);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"M\":\n\t\t\t\t\t// M takes 2 coordinates (starts a new sub-path)\n\t\t\t\t\tthis.moveTo(coordinates[0], coordinates[1]);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"L\":\n\t\t\t\t\t// L takes 2 coordinates\n\t\t\t\t\tthis.lineTo(coordinates[0], coordinates[1]);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"Q\":\n\t\t\t\t\t// Q takes 4 coordinates\n\t\t\t\t\tthis.quadraticCurveTo(\n\t\t\t\t\t\tcoordinates[0],\n\t\t\t\t\t\tcoordinates[1],\n\t\t\t\t\t\tcoordinates[2],\n\t\t\t\t\t\tcoordinates[3],\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"C\":\n\t\t\t\t\t// C takes 6 coordinates\n\t\t\t\t\tthis.bezierCurveTo(\n\t\t\t\t\t\tcoordinates[0],\n\t\t\t\t\t\tcoordinates[1],\n\t\t\t\t\t\tcoordinates[2],\n\t\t\t\t\t\tcoordinates[3],\n\t\t\t\t\t\tcoordinates[4],\n\t\t\t\t\t\tcoordinates[5],\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"Z\":\n\t\t\t\t\tthis.closePath();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tconsole.warn(\"Unsupported command:\", command);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * begin a new (empty) path, discarding all previously recorded points and\n\t * sub-paths. Called automatically by {@link parseSVGPath}.\n\t * @example\n\t * path.beginPath();\n\t * path.rect(0, 0, 100, 60);\n\t * renderer.fill();\n\t */\n\tbeginPath() {\n\t\t// empty the cache and recycle all vectors\n\t\tthis.points.forEach((point) => {\n\t\t\tpointPool.release(point);\n\t\t});\n\t\tthis.isDirty = true;\n\t\tthis.points.length = 0;\n\t\tthis.subPaths.length = 0;\n\t\tthis.startPoint.set(0, 0);\n\t\tthis.penMoved = false;\n\t}\n\n\t/**\n\t * causes the point of the pen to move back to the start of the current sub-path.\n\t * It tries to draw a straight line from the current point to the start.\n\t * If the shape has already been closed or has only one point, this function does nothing.\n\t * @example\n\t * path.beginPath();\n\t * path.moveTo(0, 0);\n\t * path.lineTo(50, 0);\n\t * path.lineTo(25, 40);\n\t * path.closePath(); // draw the final edge back to (0, 0)\n\t */\n\tclosePath() {\n\t\tconst points = this.points;\n\t\tif (points.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// if the pen has moved (via moveTo / `M`) without drawing since, the\n\t\t// current sub-path is empty — closing it is a no-op (matches native\n\t\t// Path2D). Detected the same way lineTo detects a new sub-path: a\n\t\t// significant gap between the pen (startPoint) and the last drawn point.\n\t\tconst lastPoint = points[points.length - 1];\n\t\tconst dx = this.startPoint.x - lastPoint.x;\n\t\tconst dy = this.startPoint.y - lastPoint.y;\n\t\tif (dx * dx + dy * dy > 1e-6) {\n\t\t\treturn;\n\t\t}\n\t\t// the start of the current sub-path (the last recorded boundary, or\n\t\t// the very first point when there is only one sub-path)\n\t\tconst startIndex =\n\t\t\tthis.subPaths.length > 0 ? this.subPaths[this.subPaths.length - 1] : 0;\n\t\tconst firstPoint = points[startIndex];\n\t\tif (!firstPoint.equals(lastPoint)) {\n\t\t\tthis.lineTo(firstPoint.x, firstPoint.y);\n\t\t}\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * triangulate the shape defined by this path into a flat list of triangle\n\t * vertices (every three consecutive points form one triangle). Sub-paths\n\t * after the first are treated as holes. Used by the WebGL renderer to fill,\n\t * and cached until the path changes.\n\t * @returns the triangulated vertices, three per triangle\n\t * @example\n\t * path.parseSVGPath(\"M 0 0 L 100 0 L 50 100 Z\");\n\t * const verts = path.triangulatePath(); // 3 vertices = 1 triangle\n\t */\n\ttriangulatePath() {\n\t\tconst vertices = this.vertices;\n\n\t\tif (this.isDirty) {\n\t\t\tconst points = this.points;\n\t\t\tconst data: number[] = [];\n\t\t\tfor (let i = 0; i < points.length; i++) {\n\t\t\t\tdata.push(points[i].x, points[i].y);\n\t\t\t}\n\t\t\t// sub-paths after the first become holes (their start indices)\n\t\t\tconst indices = earcut(\n\t\t\t\tdata,\n\t\t\t\tthis.subPaths.length > 0 ? this.subPaths : null,\n\t\t\t);\n\t\t\tconst indicesLength = indices.length;\n\n\t\t\t// pre-allocate vertices if necessary\n\t\t\twhile (vertices.length < indicesLength) {\n\t\t\t\tvertices.push(pointPool.get());\n\t\t\t}\n\n\t\t\t// calculate all vertices\n\t\t\tfor (let i = 0; i < indicesLength; i++) {\n\t\t\t\tconst point = points[indices[i]];\n\t\t\t\tvertices[i].set(point.x, point.y);\n\t\t\t}\n\n\t\t\t// recycle overhead from a previous triangulation\n\t\t\twhile (vertices.length > indicesLength) {\n\t\t\t\tpointPool.release(vertices[vertices.length - 1]);\n\t\t\t\tvertices.length -= 1;\n\t\t\t}\n\t\t\tthis.isDirty = false;\n\t\t}\n\n\t\treturn vertices;\n\t}\n\n\t/**\n\t * moves the starting point of a new sub-path to the (x, y) coordinates.\n\t * @param x - the x-axis (horizontal) coordinate of the point.\n\t * @param y - the y-axis (vertical) coordinate of the point.\n\t * @example\n\t * path.beginPath();\n\t * path.moveTo(20, 20); // lift the pen to (20, 20): starts a new sub-path\n\t * path.lineTo(80, 20);\n\t */\n\tmoveTo(x: number, y: number) {\n\t\tthis.startPoint.set(x, y);\n\t\tthis.penMoved = true;\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Start the outline of an arc/ellipse at (x, y): per the native Path2D\n\t * spec, when the path already has content the arc is CONNECTED to the\n\t * current point with a straight line — only a virgin path (no points, no\n\t * explicit moveTo) starts fresh. An unconditional moveTo here would\n\t * silently start a new sub-path, which fill() treats as a HOLE cut out\n\t * of the shape.\n\t * @ignore\n\t */\n\tprivate connectTo(x: number, y: number) {\n\t\tif (this.points.length === 0 && !this.penMoved) {\n\t\t\tthis.moveTo(x, y);\n\t\t} else {\n\t\t\tthis.lineTo(x, y);\n\t\t}\n\t}\n\n\t/**\n\t * connects the last point in the current sub-path to the (x, y) coordinates with a straight line.\n\t * @param x - the x-axis coordinate of the line's end point.\n\t * @param y - the y-axis coordinate of the line's end point.\n\t * @example\n\t * path.beginPath();\n\t * path.moveTo(0, 0);\n\t * path.lineTo(50, 0); // horizontal segment\n\t * path.lineTo(50, 50); // vertical segment (an \"L\" shape)\n\t */\n\tlineTo(x: number, y: number) {\n\t\tconst points = this.points;\n\t\tconst startPoint = this.startPoint;\n\t\tconst lastPoint =\n\t\t\tpoints.length === 0 ? startPoint : points[points.length - 1];\n\n\t\tif (!startPoint.equals(lastPoint)) {\n\t\t\t// the pen moved without drawing: start this segment from the pen\n\t\t\t// position. A *significant* gap marks a genuinely new sub-path (e.g.\n\t\t\t// an SVG `M` mid-path) → record its start so fill treats it as a hole\n\t\t\t// and the Canvas replay can `moveTo` across it. Float-precision noise\n\t\t\t// from arc/roundRect joins (well below this threshold) is ignored, so\n\t\t\t// those stay single sub-paths.\n\t\t\tif (points.length > 0) {\n\t\t\t\tconst dx = startPoint.x - lastPoint.x;\n\t\t\t\tconst dy = startPoint.y - lastPoint.y;\n\t\t\t\tif (dx * dx + dy * dy > 1e-6) {\n\t\t\t\t\tthis.subPaths.push(points.length);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpoints.push(pointPool.get(startPoint.x, startPoint.y));\n\t\t} else {\n\t\t\tpoints.push(pointPool.get(lastPoint.x, lastPoint.y));\n\t\t}\n\t\tpoints.push(pointPool.get(x, y));\n\n\t\tstartPoint.x = x;\n\t\tstartPoint.y = y;\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * adds an arc to the current path which is centered at (x, y) position with the given radius,\n\t * starting at startAngle and ending at endAngle going in the given direction by counterclockwise (defaulting to clockwise).\n\t * @param x - the horizontal coordinate of the arc's center.\n\t * @param y - the vertical coordinate of the arc's center.\n\t * @param radius - the arc's radius. Must be positive.\n\t * @param startAngle - the angle at which the arc starts in radians, measured from the positive x-axis.\n\t * @param endAngle - the angle at which the arc ends in radians, measured from the positive x-axis.\n\t * @param [anticlockwise=false] - an optional boolean value. If true, draws the arc counter-clockwise between the start and end angles.\n\t * @example\n\t * // a full circle of radius 40 centred at (50, 50)\n\t * path.beginPath();\n\t * path.arc(50, 50, 40, 0, Math.PI * 2);\n\t * renderer.stroke();\n\t */\n\tarc(\n\t\tx: number,\n\t\ty: number,\n\t\tradius: number,\n\t\tstartAngle: number,\n\t\tendAngle: number,\n\t\tanticlockwise = false,\n\t) {\n\t\t// based on from https://github.com/karellodewijk/canvas-webgl/blob/master/canvas-webgl.js\n\t\t//bring angles all in [0, 2*PI] range\n\t\tif (startAngle === endAngle) {\n\t\t\treturn;\n\t\t}\n\t\tconst fullCircle = anticlockwise\n\t\t\t? Math.abs(startAngle - endAngle) >= TAU\n\t\t\t: Math.abs(endAngle - startAngle) >= TAU;\n\n\t\tstartAngle = startAngle % TAU;\n\t\tendAngle = endAngle % TAU;\n\n\t\tif (startAngle < 0) {\n\t\t\tstartAngle += TAU;\n\t\t}\n\t\tif (endAngle < 0) {\n\t\t\tendAngle += TAU;\n\t\t}\n\n\t\tif (startAngle >= endAngle) {\n\t\t\tendAngle += TAU;\n\t\t}\n\n\t\tlet diff = endAngle - startAngle;\n\t\tlet direction = 1;\n\t\tif (anticlockwise) {\n\t\t\tdirection = -1;\n\t\t\tdiff = TAU - diff;\n\t\t}\n\n\t\tif (fullCircle) {\n\t\t\tdiff = TAU;\n\t\t}\n\n\t\tconst length = diff * radius;\n\t\tconst nr_of_interpolation_points = length / this.arcResolution;\n\t\tconst dangle = diff / nr_of_interpolation_points;\n\t\tconst angleStep = dangle * direction;\n\n\t\tthis.connectTo(\n\t\t\tx + radius * Math.cos(startAngle),\n\t\t\ty + radius * Math.sin(startAngle),\n\t\t);\n\n\t\tlet angle = startAngle;\n\t\tfor (let j = 0; j < nr_of_interpolation_points; j++) {\n\t\t\tthis.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle));\n\t\t\tangle += angleStep;\n\t\t}\n\n\t\tthis.lineTo(\n\t\t\tx + radius * Math.cos(endAngle),\n\t\t\ty + radius * Math.sin(endAngle),\n\t\t);\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * adds a circular arc to the path with the given control points and radius, connected to the previous point by a straight line.\n\t * @param x1 - the x-axis coordinate of the first control point.\n\t * @param y1 - the y-axis coordinate of the first control point.\n\t * @param x2 - the x-axis coordinate of the second control point.\n\t * @param y2 - the y-axis coordinate of the second control point.\n\t * @param radius - the arc's radius. Must be positive.\n\t * @example\n\t * // round the corner where two segments meet at (50, 0)\n\t * path.beginPath();\n\t * path.moveTo(0, 0);\n\t * path.arcTo(50, 0, 50, 50, 20);\n\t * path.lineTo(50, 50);\n\t */\n\tarcTo(x1: number, y1: number, x2: number, y2: number, radius: number) {\n\t\tconst points = this.points;\n\t\tconst startPoint = this.startPoint;\n\t\tconst lastPoint =\n\t\t\tpoints.length === 0 ? startPoint : points[points.length - 1];\n\n\t\t// based on from https://github.com/karellodewijk/canvas-webgl/blob/master/canvas-webgl.js\n\t\tconst x0 = lastPoint.x;\n\t\tconst y0 = lastPoint.y;\n\t\t//a = -incoming vector, b = outgoing vector to x1, y1\n\t\tlet a0 = x0 - x1;\n\t\tlet a1 = y0 - y1;\n\t\tlet b0 = x2 - x1;\n\t\tlet b1 = y2 - y1;\n\n\t\t//normalize\n\t\tconst l_a = Math.sqrt(a0 * a0 + a1 * a1);\n\t\tconst l_b = Math.sqrt(b0 * b0 + b1 * b1);\n\t\ta0 /= l_a;\n\t\ta1 /= l_a;\n\t\tb0 /= l_b;\n\t\tb1 /= l_b;\n\t\tconst angle = Math.atan2(a1, a0) - Math.atan2(b1, b0);\n\n\t\t//work out tangent points using tan(θ) = opposite / adjacent; angle/2 because hypotenuse is the bisection of a,b\n\t\tconst tan_angle_div2 = Math.tan(angle / 2);\n\t\tconst adj_l = radius / tan_angle_div2;\n\n\t\tconst tangent1_pointx = x1 + a0 * adj_l;\n\t\tconst tangent1_pointy = y1 + a1 * adj_l;\n\t\tconst tangent2_pointx = x1 + b0 * adj_l;\n\t\tconst tangent2_pointy = y1 + b1 * adj_l;\n\n\t\tthis.connectTo(tangent1_pointx, tangent1_pointy);\n\n\t\tlet bisec0 = (a0 + b0) / 2.0;\n\t\tlet bisec1 = (a1 + b1) / 2.0;\n\t\tconst bisec_l = Math.sqrt(bisec0 * bisec0 + bisec1 * bisec1);\n\t\tbisec0 /= bisec_l;\n\t\tbisec1 /= bisec_l;\n\n\t\tconst hyp_l = Math.sqrt(radius * radius + adj_l * adj_l);\n\t\tconst centerx = x1 + hyp_l * bisec0;\n\t\tconst centery = y1 + hyp_l * bisec1;\n\n\t\tconst startAngle = Math.atan2(\n\t\t\ttangent1_pointy - centery,\n\t\t\ttangent1_pointx - centerx,\n\t\t);\n\t\tconst endAngle = Math.atan2(\n\t\t\ttangent2_pointy - centery,\n\t\t\ttangent2_pointx - centerx,\n\t\t);\n\n\t\tthis.arc(centerx, centery, radius, startAngle, endAngle);\n\t}\n\n\t/**\n\t * adds an elliptical arc to the path which is centered at (x, y) position with the radii radiusX and radiusY\n\t * starting at startAngle and ending at endAngle going in the given direction by counterclockwise.\n\t * @param x - the x-axis (horizontal) coordinate of the ellipse's center.\n\t * @param y - the  y-axis (vertical) coordinate of the ellipse's center.\n\t * @param radiusX - the ellipse's major-axis radius. Must be non-negative.\n\t * @param radiusY - the ellipse's minor-axis radius. Must be non-negative.\n\t * @param rotation - the rotation of the ellipse, expressed in radians.\n\t * @param startAngle - the angle at which the ellipse starts, measured clockwise from the positive x-axis and expressed in radians.\n\t * @param endAngle - the angle at which the ellipse ends, measured clockwise from the positive x-axis and expressed in radians.\n\t * @param [anticlockwise=false] - an optional boolean value which, if true, draws the ellipse counterclockwise (anticlockwise).\n\t * @example\n\t * // a 45°-rotated ellipse, filled\n\t * path.beginPath();\n\t * path.ellipse(50, 50, 60, 30, Math.PI / 4, 0, Math.PI * 2);\n\t * renderer.fill();\n\t */\n\tellipse(\n\t\tx: number,\n\t\ty: number,\n\t\tradiusX: number,\n\t\tradiusY: number,\n\t\trotation: number,\n\t\tstartAngle: number,\n\t\tendAngle: number,\n\t\tanticlockwise = false,\n\t) {\n\t\t// based on from https://github.com/karellodewijk/canvas-webgl/blob/master/canvas-webgl.js\n\t\tif (startAngle === endAngle) {\n\t\t\treturn;\n\t\t}\n\t\tconst fullCircle = anticlockwise\n\t\t\t? Math.abs(startAngle - endAngle) >= TAU\n\t\t\t: Math.abs(endAngle - startAngle) >= TAU;\n\n\t\t//bring angles all in [0, 2*PI] range\n\t\tstartAngle = startAngle % TAU;\n\t\tendAngle = endAngle % TAU;\n\t\tif (startAngle < 0) {\n\t\t\tstartAngle += TAU;\n\t\t}\n\t\tif (endAngle < 0) {\n\t\t\tendAngle += TAU;\n\t\t}\n\n\t\tif (startAngle >= endAngle) {\n\t\t\tendAngle += TAU;\n\t\t}\n\n\t\tlet diff = endAngle - startAngle;\n\n\t\tlet direction = 1;\n\t\tif (anticlockwise) {\n\t\t\tdirection = -1;\n\t\t\tdiff = TAU - diff;\n\t\t}\n\n\t\tif (fullCircle) {\n\t\t\tdiff = TAU;\n\t\t}\n\n\t\tconst length = (diff * radiusX + diff * radiusY) / 2;\n\t\tconst nr_of_interpolation_points = length / this.arcResolution;\n\t\tconst dangle = diff / nr_of_interpolation_points;\n\t\tconst angleStep = dangle * direction;\n\n\t\tlet angle = startAngle;\n\t\tconst cos_rotation = Math.cos(rotation);\n\t\tconst sin_rotation = Math.sin(rotation);\n\n\t\tconst sx = radiusX * Math.cos(startAngle);\n\t\tconst sy = radiusY * Math.sin(startAngle);\n\t\tthis.connectTo(\n\t\t\tx + sx * cos_rotation - sy * sin_rotation,\n\t\t\ty + sx * sin_rotation + sy * cos_rotation,\n\t\t);\n\n\t\tfor (let j = 0; j < nr_of_interpolation_points; j++) {\n\t\t\tconst _x1 = radiusX * Math.cos(angle);\n\t\t\tconst _y1 = radiusY * Math.sin(angle);\n\t\t\tconst _x2 = x + _x1 * cos_rotation - _y1 * sin_rotation;\n\t\t\tconst _y2 = y + _x1 * sin_rotation + _y1 * cos_rotation;\n\t\t\tthis.lineTo(_x2, _y2);\n\t\t\tangle += angleStep;\n\t\t}\n\t\t// close the ellipse\n\t\tthis.lineTo(\n\t\t\tx + sx * cos_rotation - sy * sin_rotation,\n\t\t\ty + sx * sin_rotation + sy * cos_rotation,\n\t\t);\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Adds a quadratic Bézier curve to the path.\n\t * @param cpX - The x-coordinate of the control point.\n\t * @param cpY - The y-coordinate of the control point.\n\t * @param x - The x-coordinate of the end point of the curve.\n\t * @param y - The y-coordinate of the end point of the curve.\n\t * @example\n\t * path.beginPath();\n\t * path.moveTo(0, 50);\n\t * path.quadraticCurveTo(50, 0, 100, 50); // one control point at (50, 0)\n\t */\n\tquadraticCurveTo(cpX: number, cpY: number, x: number, y: number) {\n\t\tconst points = this.points;\n\t\tconst startPoint = this.startPoint;\n\t\t// capture coordinates (not reference) since lineTo mutates startPoint\n\t\tconst lx = points.length === 0 ? startPoint.x : points[points.length - 1].x;\n\t\tconst ly = points.length === 0 ? startPoint.y : points[points.length - 1].y;\n\t\tconst endPoint = pointPool.get().set(x, y);\n\t\tconst controlPoint = pointPool.get().set(cpX, cpY);\n\t\t// estimate curve length via control polygon\n\t\tconst polyLen =\n\t\t\tMath.sqrt((cpX - lx) ** 2 + (cpY - ly) ** 2) +\n\t\t\tMath.sqrt((x - cpX) ** 2 + (y - cpY) ** 2);\n\t\tconst resolution = Math.max(4, Math.ceil(polyLen / this.arcResolution));\n\n\t\tconst t = 1 / resolution;\n\t\tfor (let i = 1; i <= resolution; i++) {\n\t\t\tconst ti = t * i;\n\t\t\tconst omt = 1 - ti;\n\t\t\tconst omt2 = omt * omt;\n\t\t\tconst ti2 = ti * ti;\n\t\t\tthis.lineTo(\n\t\t\t\tlx * omt2 + controlPoint.x * 2 * omt * ti + endPoint.x * ti2,\n\t\t\t\tly * omt2 + controlPoint.y * 2 * omt * ti + endPoint.y * ti2,\n\t\t\t);\n\t\t}\n\t\tpointPool.release(endPoint);\n\t\tpointPool.release(controlPoint);\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Adds a cubic Bézier curve to the path.\n\t * @param cp1X - The x-coordinate of the first control point.\n\t * @param cp1Y - The y-coordinate of the first control point.\n\t * @param cp2X - The x-coordinate of the second control point.\n\t * @param cp2Y - The y-coordinate of the second control point.\n\t * @param x - The x-coordinate of the end point of the curve.\n\t * @param y - The y-coordinate of the end point of the curve.\n\t * @example\n\t * path.beginPath();\n\t * path.moveTo(0, 50);\n\t * path.bezierCurveTo(25, 0, 75, 100, 100, 50); // two control points\n\t */\n\tbezierCurveTo(\n\t\tcp1X: number,\n\t\tcp1Y: number,\n\t\tcp2X: number,\n\t\tcp2Y: number,\n\t\tx: number,\n\t\ty: number,\n\t) {\n\t\tconst points = this.points;\n\t\tconst startPoint = this.startPoint;\n\t\t// capture coordinates (not reference) since lineTo mutates startPoint\n\t\tconst lx = points.length === 0 ? startPoint.x : points[points.length - 1].x;\n\t\tconst ly = points.length === 0 ? startPoint.y : points[points.length - 1].y;\n\t\tconst endPoint = pointPool.get().set(x, y);\n\t\tconst controlPoint1 = pointPool.get().set(cp1X, cp1Y);\n\t\tconst controlPoint2 = pointPool.get().set(cp2X, cp2Y);\n\t\t// estimate curve length via control polygon\n\t\tconst polyLen =\n\t\t\tMath.sqrt((cp1X - lx) ** 2 + (cp1Y - ly) ** 2) +\n\t\t\tMath.sqrt((cp2X - cp1X) ** 2 + (cp2Y - cp1Y) ** 2) +\n\t\t\tMath.sqrt((x - cp2X) ** 2 + (y - cp2Y) ** 2);\n\t\tconst resolution = Math.max(4, Math.ceil(polyLen / this.arcResolution));\n\n\t\tconst t = 1 / resolution;\n\t\tfor (let i = 1; i <= resolution; i++) {\n\t\t\tconst ti = t * i;\n\t\t\tconst omt = 1 - ti;\n\t\t\tconst omt2 = omt * omt;\n\t\t\tconst omt3 = omt2 * omt;\n\t\t\tconst ti2 = ti * ti;\n\t\t\tconst ti3 = ti2 * ti;\n\t\t\tthis.lineTo(\n\t\t\t\tlx * omt3 +\n\t\t\t\t\tcontrolPoint1.x * 3 * omt2 * ti +\n\t\t\t\t\tcontrolPoint2.x * 3 * omt * ti2 +\n\t\t\t\t\tendPoint.x * ti3,\n\t\t\t\tly * omt3 +\n\t\t\t\t\tcontrolPoint1.y * 3 * omt2 * ti +\n\t\t\t\t\tcontrolPoint2.y * 3 * omt * ti2 +\n\t\t\t\t\tendPoint.y * ti3,\n\t\t\t);\n\t\t}\n\n\t\tpointPool.release(endPoint);\n\t\tpointPool.release(controlPoint1);\n\t\tpointPool.release(controlPoint2);\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * creates a path for a rectangle at position (x, y) with a size that is determined by width and height.\n\t * @param x - the x-axis coordinate of the rectangle's starting point.\n\t * @param y - the y-axis coordinate of the rectangle's starting point.\n\t * @param width - the rectangle's width. Positive values are to the right, and negative to the left.\n\t * @param height - the rectangle's height. Positive values are down, and negative are up.\n\t * @example\n\t * path.beginPath();\n\t * path.rect(10, 10, 80, 50);\n\t * renderer.fill();\n\t */\n\trect(x: number, y: number, width: number, height: number) {\n\t\tthis.moveTo(x, y);\n\t\tthis.lineTo(x + width, y);\n\t\tthis.lineTo(x + width, y + height);\n\t\tthis.lineTo(x, y + height);\n\t\tthis.lineTo(x, y);\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * adds an rounded rectangle to the current path.\n\t * @param x - the x-axis coordinate of the rectangle's starting point.\n\t * @param y - the y-axis coordinate of the rectangle's starting point.\n\t * @param width - the rectangle's width. Positive values are to the right, and negative to the left.\n\t * @param height - the rectangle's height. Positive values are down, and negative are up.\n\t * @param radius - the arc's radius to draw the borders. Must be positive.\n\t * @example\n\t * path.beginPath();\n\t * path.roundRect(10, 10, 80, 50, 12); // 12px corner radius\n\t * renderer.fill();\n\t */\n\troundRect(\n\t\tx: number,\n\t\ty: number,\n\t\twidth: number,\n\t\theight: number,\n\t\tradius: number,\n\t) {\n\t\tthis.moveTo(x + radius, y);\n\t\tthis.lineTo(x + width - radius, y);\n\t\tthis.arcTo(x + width, y, x + width, y + radius, radius);\n\n\t\tthis.lineTo(x + width, y + height - radius);\n\t\tthis.arcTo(x + width, y + height, x + width - radius, y + height, radius);\n\n\t\tthis.lineTo(x + radius, y + height);\n\t\tthis.arcTo(x, y + height, x, y + height - radius, radius);\n\n\t\tthis.lineTo(x, y + radius);\n\t\tthis.arcTo(x, y, x + radius, y, radius);\n\n\t\tthis.isDirty = true;\n\t}\n}\n\nexport default Path2D;\n","import { Point } from \"../geometries/point.js\";\nimport { createPool } from \"../system/pool.js\";\nimport { Matrix3d } from \"./matrix3d.js\";\nimport { Vector2d } from \"./vector2d.js\";\nimport { Vector3d } from \"./vector3d.js\";\n\ntype ConstructorArg =\n\t| [Matrix2d]\n\t| [Matrix3d]\n\t| [number, number, number, number, number, number]\n\t| [number, number, number, number, number, number, number, number, number]\n\t| [undefined]\n\t| [];\n\nconst reset = (instance: Matrix2d, ...value: ConstructorArg) => {\n\tif (value[0] instanceof Matrix3d) {\n\t\tinstance.fromMat3d(value[0]);\n\t} else if (value[0] instanceof Matrix2d) {\n\t\tinstance.copy(value[0]);\n\t} else if (typeof value[0] === \"number\") {\n\t\tinstance.setTransform(...value);\n\t} else {\n\t\tinstance.identity();\n\t}\n};\n\n/**\n * a Matrix2d Object.<br>\n * the identity matrix and parameters position : <br>\n * <img src=\"images/identity-matrix_2x.png\"/>\n * @category Math\n */\nexport class Matrix2d {\n\t/**\n\t * The matrix values\n\t */\n\tval: Float32Array;\n\n\t/**\n\t * Constructs a new Matrix2d object.\n\t * @param value - The values to initialize the matrix with.\n\t */\n\tconstructor(...value: ConstructorArg) {\n\t\tthis.val = new Float32Array(9);\n\t\treset(this, ...value);\n\t}\n\n\t/**\n\t * Gets the tx component of the matrix.\n\t * @returns The tx component of the matrix.\n\t */\n\tget tx() {\n\t\treturn this.val[6];\n\t}\n\n\t/**\n\t * Gets the ty component of the matrix.\n\t * @returns The ty component of the matrix.\n\t */\n\tget ty() {\n\t\treturn this.val[7];\n\t}\n\n\t/**\n\t * reset the transformation matrix to the identity matrix (no transformation).<br>\n\t * the identity matrix and parameters position : <br>\n\t * <img src=\"images/identity-matrix_2x.png\"/>\n\t * @returns Reference to this object for method chaining\n\t */\n\tidentity() {\n\t\tthis.setTransform(1, 0, 0, 0, 1, 0, 0, 0, 1);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the matrix to the specified value.\n\t * @param values - The matrix components.\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetTransform(\n\t\t...values:\n\t\t\t| [number, number, number, number, number, number]\n\t\t\t| [number, number, number, number, number, number, number, number, number]\n\t) {\n\t\tif (values.length === 9) {\n\t\t\tthis.val[0] = values[0]; // a - m00\n\t\t\tthis.val[1] = values[1]; // b - m10\n\t\t\tthis.val[2] = values[2]; // c - m20\n\t\t\tthis.val[3] = values[3]; // d - m01\n\t\t\tthis.val[4] = values[4]; // e - m11\n\t\t\tthis.val[5] = values[5]; // f - m21\n\t\t\tthis.val[6] = values[6]; // g - m02\n\t\t\tthis.val[7] = values[7]; // h - m12\n\t\t\tthis.val[8] = values[8]; // i - m22\n\t\t} else if (values.length === 6) {\n\t\t\t// canvas-style (a, b, c, d, e, f): (a, b) is the first column\n\t\t\t// (m00/m10), (c, d) the second (m01/m11), (e, f) the translation\n\t\t\t// (m02/m12 — the tx/ty getters read val[6]/val[7])\n\t\t\tthis.val[0] = values[0]; // a - m00\n\t\t\tthis.val[1] = values[1]; // b - m10\n\t\t\tthis.val[2] = 0; // m20\n\t\t\tthis.val[3] = values[2]; // c - m01\n\t\t\tthis.val[4] = values[3]; // d - m11\n\t\t\tthis.val[5] = 0; // m21\n\t\t\tthis.val[6] = values[4]; // e - m02 (tx)\n\t\t\tthis.val[7] = values[5]; // f - m12 (ty)\n\t\t\tthis.val[8] = 1; // m22\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Multiplies the current transformation with the matrix described by the arguments of this method.\n\t * @param a - a component (scale x / cos)\n\t * @param b - b component (skew y / sin)\n\t * @param c - c component (skew x / -sin)\n\t * @param d - d component (scale y / cos)\n\t * @param e - e component (translate x)\n\t * @param f - f component (translate y)\n\t * @returns Reference to this object for method chaining\n\t */\n\ttransform(a: number, b: number, c: number, d: number, e: number, f: number) {\n\t\tconst v = this.val;\n\t\tconst a0 = v[0];\n\t\tconst a1 = v[1];\n\t\tconst a3 = v[3];\n\t\tconst a4 = v[4];\n\t\tconst b0 = a;\n\t\tconst b1 = b;\n\t\tconst b3 = c;\n\t\tconst b4 = d;\n\t\tconst b6 = e;\n\t\tconst b7 = f;\n\n\t\tv[0] = a0 * b0 + a3 * b1;\n\t\tv[1] = a1 * b0 + a4 * b1;\n\t\tv[3] = a0 * b3 + a3 * b4;\n\t\tv[4] = a1 * b3 + a4 * b4;\n\t\tv[6] += a0 * b6 + a3 * b7;\n\t\tv[7] += a1 * b6 + a4 * b7;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Copies over the values from another me.Matrix2d.\n\t * @param m - the matrix object to copy from\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(m: Matrix2d) {\n\t\tthis.val.set(m.val);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Extract the 2D affine components from the given Matrix3d\n\t * @param m - the 4x4 matrix to extract from\n\t * @returns Reference to this object for method chaining\n\t */\n\tfromMat3d(m: Matrix3d) {\n\t\tconst v = m.val;\n\t\tthis.val[0] = v[0]; // a (scaleX / cos)\n\t\tthis.val[1] = v[1]; // b (sin)\n\t\tthis.val[2] = 0;\n\t\tthis.val[3] = v[4]; // c (-sin / skewX)\n\t\tthis.val[4] = v[5]; // d (scaleY / cos)\n\t\tthis.val[5] = 0;\n\t\tthis.val[6] = v[12]; // tx (translation X)\n\t\tthis.val[7] = v[13]; // ty (translation Y)\n\t\tthis.val[8] = 1;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * multiply both matrix\n\t * @param m - the other matrix\n\t * @returns Reference to this object for method chaining\n\t */\n\tmultiply(m: Matrix2d) {\n\t\tconst b = m.val;\n\t\tconst a = this.val;\n\t\tconst a0 = a[0];\n\t\tconst a1 = a[1];\n\t\tconst a3 = a[3];\n\t\tconst a4 = a[4];\n\t\tconst b0 = b[0];\n\t\tconst b1 = b[1];\n\t\tconst b3 = b[3];\n\t\tconst b4 = b[4];\n\t\tconst b6 = b[6];\n\t\tconst b7 = b[7];\n\n\t\ta[0] = a0 * b0 + a3 * b1;\n\t\ta[1] = a1 * b0 + a4 * b1;\n\t\ta[3] = a0 * b3 + a3 * b4;\n\t\ta[4] = a1 * b3 + a4 * b4;\n\t\ta[6] += a0 * b6 + a3 * b7;\n\t\ta[7] += a1 * b6 + a4 * b7;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Transpose the value of this matrix.\n\t * @returns Reference to this object for method chaining\n\t */\n\ttranspose() {\n\t\tconst a = this.val;\n\t\tconst a1 = a[1];\n\t\tconst a2 = a[2];\n\t\tconst a5 = a[5];\n\n\t\ta[1] = a[3];\n\t\ta[2] = a[6];\n\t\ta[3] = a1;\n\t\ta[5] = a[7];\n\t\ta[6] = a2;\n\t\ta[7] = a5;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * invert this matrix, causing it to apply the opposite transformation.\n\t * @returns Reference to this object for method chaining\n\t */\n\tinvert() {\n\t\tconst val = this.val;\n\n\t\tconst a = val[0];\n\t\tconst b = val[1];\n\t\tconst c = val[2];\n\t\tconst d = val[3];\n\t\tconst e = val[4];\n\t\tconst f = val[5];\n\t\tconst g = val[6];\n\t\tconst h = val[7];\n\t\tconst i = val[8];\n\n\t\tconst ta = i * e - f * h;\n\t\tconst td = f * g - i * d;\n\t\tconst tg = h * d - e * g;\n\n\t\tconst n = a * ta + b * td + c * tg;\n\n\t\tval[0] = ta / n;\n\t\tval[1] = (c * h - i * b) / n;\n\t\tval[2] = (f * b - c * e) / n;\n\n\t\tval[3] = td / n;\n\t\tval[4] = (i * a - c * g) / n;\n\t\tval[5] = (c * d - f * a) / n;\n\n\t\tval[6] = tg / n;\n\t\tval[7] = (b * g - h * a) / n;\n\t\tval[8] = (e * a - b * d) / n;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * apply the current transform to the given 2d or 3d vector or point\n\t * @param v - the vector object to be transformed\n\t * @returns result vector object.\n\t */\n\tapply(v: Vector2d | Vector3d | Point) {\n\t\tconst a = this.val;\n\t\tconst x = v.x;\n\t\tconst y = v.y;\n\t\tconst z = \"z\" in v ? v.z : 1;\n\n\t\tv.x = x * a[0] + y * a[3] + z * a[6];\n\t\tv.y = x * a[1] + y * a[4] + z * a[7];\n\n\t\tif (\"z\" in v) {\n\t\t\tv.z = x * a[2] + y * a[5] + z * a[8];\n\t\t}\n\n\t\treturn v;\n\t}\n\n\t/**\n\t * apply the inverted current transform to the given 2d vector\n\t * @param v - the vector object to be transformed\n\t * @returns result vector object.\n\t */\n\tapplyInverse(v: Vector2d) {\n\t\tconst a = this.val;\n\t\tconst x = v.x;\n\t\tconst y = v.y;\n\n\t\tconst invD = 1 / (a[0] * a[4] + a[3] * -a[1]);\n\n\t\tv.x =\n\t\t\ta[4] * invD * x + -a[3] * invD * y + (a[7] * a[3] - a[6] * a[4]) * invD;\n\t\tv.y =\n\t\t\ta[0] * invD * y + -a[1] * invD * x + (-a[7] * a[0] + a[6] * a[1]) * invD;\n\n\t\treturn v;\n\t}\n\n\t/**\n\t * scale the matrix\n\t * @param x - a number representing the abscissa of the scaling vector.\n\t * @param [y] - a number representing the ordinate of the scaling vector.\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x) {\n\t\tconst a = this.val;\n\n\t\ta[0] *= x;\n\t\ta[1] *= x;\n\t\t//a[2] *= x; // z axis remains unchanged for 2d scale operation\n\n\t\ta[3] *= y;\n\t\ta[4] *= y;\n\t\t//a[5] *= y; // w axis remains unchanged for 2d scale operation\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * adds a 2D scaling transformation.\n\t * @param v - scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector2d) {\n\t\treturn this.scale(v.x, v.y);\n\t}\n\n\t/**\n\t * specifies a 2D scale operation using the [sx, 1] scaling vector\n\t * @param x - x scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleX(x: number) {\n\t\treturn this.scale(x, 1);\n\t}\n\n\t/**\n\t * specifies a 2D scale operation using the [1,sy] scaling vector\n\t * @param y - y scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleY(y: number) {\n\t\treturn this.scale(1, y);\n\t}\n\n\t/**\n\t * rotate the matrix (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - Rotation angle in radians.\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number) {\n\t\tif (angle !== 0) {\n\t\t\tconst a00 = this.val[0];\n\t\t\tconst a01 = this.val[1];\n\t\t\tconst a02 = this.val[2];\n\t\t\tconst a10 = this.val[3];\n\t\t\tconst a11 = this.val[4];\n\t\t\tconst a12 = this.val[5];\n\t\t\tconst s = Math.sin(angle);\n\t\t\tconst c = Math.cos(angle);\n\n\t\t\tthis.val[0] = c * a00 + s * a10;\n\t\t\tthis.val[1] = c * a01 + s * a11;\n\t\t\tthis.val[2] = c * a02 + s * a12;\n\n\t\t\tthis.val[3] = c * a10 - s * a00;\n\t\t\tthis.val[4] = c * a11 - s * a01;\n\t\t\tthis.val[5] = c * a12 - s * a02;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * translate the matrix position on the horizontal and vertical axis\n\t * @param x - the x coordindates or a vector to translate the matrix by\n\t * @param [y] - the y coordindates to translate the matrix by\n\t * @returns Reference to this object for method chaining\n\t */\n\ttranslate(x: number, y: number): Matrix2d;\n\ttranslate(vector: Vector2d): Matrix2d;\n\ttranslate(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\tthis.val[6] += this.val[0] * _x + this.val[3] * _y;\n\t\tthis.val[7] += this.val[1] * _x + this.val[4] * _y;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Check if matrix is an identity matrix.\n\t * @returns true if the matrix is an identity matrix\n\t */\n\tisIdentity() {\n\t\tconst a = this.val;\n\n\t\t// check translation first (most commonly non-zero), then diagonal, then rest\n\t\treturn (\n\t\t\ta[6] === 0 &&\n\t\t\ta[7] === 0 &&\n\t\t\ta[0] === 1 &&\n\t\t\ta[4] === 1 &&\n\t\t\ta[8] === 1 &&\n\t\t\ta[1] === 0 &&\n\t\t\ta[2] === 0 &&\n\t\t\ta[3] === 0 &&\n\t\t\ta[5] === 0\n\t\t);\n\t}\n\n\t/**\n\t * return true if the two matrices are identical\n\t * @param m - the other matrix\n\t * @returns true if both are equals\n\t */\n\tequals(m: Matrix2d) {\n\t\treturn (\n\t\t\tthis.val[0] === m.val[0] &&\n\t\t\tthis.val[1] === m.val[1] &&\n\t\t\tthis.val[2] === m.val[2] &&\n\t\t\tthis.val[3] === m.val[3] &&\n\t\t\tthis.val[4] === m.val[4] &&\n\t\t\tthis.val[5] === m.val[5] &&\n\t\t\tthis.val[6] === m.val[6] &&\n\t\t\tthis.val[7] === m.val[7] &&\n\t\t\tthis.val[8] === m.val[8]\n\t\t);\n\t}\n\n\t/**\n\t * Clone the Matrix\n\t * @returns a clones matrix\n\t */\n\tclone() {\n\t\treturn matrix2dPool.get(this);\n\t}\n\n\t/**\n\t * return an array representation of this Matrix\n\t * @returns the internal matrix values\n\t */\n\ttoArray() {\n\t\treturn this.val;\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns string representation of the matrix\n\t */\n\ttoString() {\n\t\tconst a = this.val;\n\n\t\treturn `Matrix2d(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]}, ${a[4]}, ${a[5]}, ${\n\t\t\ta[6]\n\t\t}, ${a[7]}, ${a[8]})`;\n\t}\n}\n\nexport const matrix2dPool = createPool<Matrix2d, ConstructorArg>(\n\t(...values) => {\n\t\tconst instance = new Matrix2d(...values);\n\n\t\treturn {\n\t\t\tinstance,\n\t\t\treset(...value) {\n\t\t\t\treset(instance, ...value);\n\t\t\t},\n\t\t};\n\t},\n);\n","import { createPool } from \"../system/pool.ts\";\nimport { XYPoint } from \"../utils/types.ts\";\nimport { clamp, damp, lerp } from \"./math.ts\";\nimport { Vector2d } from \"./vector2d.ts\";\n\n/**\n * a generic 3D Vector Object\n * @category Math\n */\nexport class Vector3d {\n\tx: number;\n\ty: number;\n\tz: number;\n\n\t/**\n\t * @param [x] - x value of the vector\n\t * @param [y] - y value of the vector\n\t * @param [z] - z value of the vector\n\t */\n\tconstructor(x = 0, y = 0, z = 0) {\n\t\tthis.onResetEvent(x, y, z);\n\t}\n\n\tonResetEvent(x = 0, y = 0, z = 0) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to the given values\n\t * @param x x component\n\t * @param y y component\n\t * @param [z] z component\n\t * @returns Reference to this object for method chaining\n\t */\n\tset(x: number, y: number, z: number | undefined = 0) {\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to 0\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetZero() {\n\t\treturn this.set(0, 0, 0);\n\t}\n\n\t/**\n\t * set the Vector x and y properties using the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetV(v: Vector2d | Vector3d) {\n\t\treturn this.set(v.x, v.y, \"z\" in v ? v.z : undefined);\n\t}\n\n\t/**\n\t * Add the passed vector to this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tadd(v: Vector2d | Vector3d) {\n\t\treturn this.set(this.x + v.x, this.y + v.y, this.z + (\"z\" in v ? v.z : 0));\n\t}\n\n\t/**\n\t * Subtract the passed vector from this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsub(v: Vector2d | Vector3d) {\n\t\treturn this.set(this.x - v.x, this.y - v.y, this.z - (\"z\" in v ? v.z : 0));\n\t}\n\n\t/**\n\t * Multiply this vector values by the given scalar\n\t * @param x x component\n\t * @param [y] y component\n\t * @param [z] z component\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x, z = 1) {\n\t\treturn this.set(this.x * x, this.y * y, this.z * z);\n\t}\n\n\t/**\n\t * Multiply this vector values by the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector3d) {\n\t\treturn this.scale(v.x, v.y, v.z);\n\t}\n\n\t/**\n\t * Convert this vector into isometric coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\ttoIso() {\n\t\treturn this.set(this.x - this.y, (this.x + this.y) * 0.5, this.z);\n\t}\n\n\t/**\n\t * Convert this vector into 2d coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\tto2d() {\n\t\treturn this.set(this.y + this.x / 2, this.y - this.x / 2, this.z);\n\t}\n\n\t/**\n\t * Divide this vector values by the passed value\n\t * @param n - the value to divide the vector by\n\t * @returns Reference to this object for method chaining\n\t */\n\tdiv(n: number) {\n\t\treturn this.set(this.x / n, this.y / n, this.z / n);\n\t}\n\n\t/**\n\t * Update this vector values to absolute values\n\t * @returns Reference to this object for method chaining\n\t */\n\tabs() {\n\t\treturn this.set(\n\t\t\tthis.x < 0 ? -this.x : this.x,\n\t\t\tthis.y < 0 ? -this.y : this.y,\n\t\t\tthis.z < 0 ? -this.z : this.z,\n\t\t);\n\t}\n\n\t/**\n\t * Clamp the vector value within the specified value range\n\t * @param low lower bound\n\t * @param high upper bound\n\t * @returns new Vector3d\n\t */\n\tclamp(low: number, high: number) {\n\t\treturn new Vector3d(\n\t\t\tclamp(this.x, low, high),\n\t\t\tclamp(this.y, low, high),\n\t\t\tclamp(this.z, low, high),\n\t\t);\n\t}\n\n\t/**\n\t * Clamp this vector value within the specified value range\n\t * @param low lower bound\n\t * @param high upper bound\n\t * @returns Reference to this object for method chaining\n\t */\n\tclampSelf(low: number, high: number) {\n\t\treturn this.set(\n\t\t\tclamp(this.x, low, high),\n\t\t\tclamp(this.y, low, high),\n\t\t\tclamp(this.z, low, high),\n\t\t);\n\t}\n\n\t/**\n\t * Update this vector with the minimum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tminV(v: Vector2d | Vector3d) {\n\t\tconst _vz = \"z\" in v ? v.z : 0;\n\t\treturn this.set(\n\t\t\tthis.x < v.x ? this.x : v.x,\n\t\t\tthis.y < v.y ? this.y : v.y,\n\t\t\tthis.z < _vz ? this.z : _vz,\n\t\t);\n\t}\n\n\t/**\n\t * Update this vector with the maximum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tmaxV(v: Vector2d | Vector3d) {\n\t\tconst _vz = \"z\" in v ? v.z : 0;\n\t\treturn this.set(\n\t\t\tthis.x > v.x ? this.x : v.x,\n\t\t\tthis.y > v.y ? this.y : v.y,\n\t\t\tthis.z > _vz ? this.z : _vz,\n\t\t);\n\t}\n\n\t/**\n\t * Floor the vector values\n\t * @returns new Vector3d\n\t */\n\tfloor() {\n\t\treturn new Vector3d(\n\t\t\tMath.floor(this.x),\n\t\t\tMath.floor(this.y),\n\t\t\tMath.floor(this.z),\n\t\t);\n\t}\n\n\t/**\n\t * Floor this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tfloorSelf() {\n\t\treturn this.set(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));\n\t}\n\n\t/**\n\t * Ceil the vector values\n\t * @returns new Vector3d\n\t */\n\tceil() {\n\t\treturn new Vector3d(\n\t\t\tMath.ceil(this.x),\n\t\t\tMath.ceil(this.y),\n\t\t\tMath.ceil(this.z),\n\t\t);\n\t}\n\n\t/**\n\t * Ceil this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tceilSelf() {\n\t\treturn this.set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z));\n\t}\n\n\t/**\n\t * Negate the vector values\n\t * @returns new Vector3d\n\t */\n\tnegate() {\n\t\treturn new Vector3d(-this.x, -this.y, -this.z);\n\t}\n\n\t/**\n\t * Negate this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tnegateSelf() {\n\t\treturn this.set(-this.x, -this.y, -this.z);\n\t}\n\n\t/**\n\t * Copy the components of the given vector into this one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(v: Vector2d | Vector3d) {\n\t\treturn this.set(v.x, v.y, \"z\" in v ? v.z : 0);\n\t}\n\n\t/**\n\t * return true if this vector is equal to the given values or vector\n\t * @param args other vector or vector components\n\t * @returns true if both vectors are equal\n\t */\n\tequals(\n\t\t...args:\n\t\t\t| [number]\n\t\t\t| [Vector2d]\n\t\t\t| [Vector3d]\n\t\t\t| [number, number]\n\t\t\t| [number, number, number]\n\t): boolean {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\t\tlet _z: number;\n\n\t\tif (args.length === 1) {\n\t\t\tconst arg = args[0];\n\t\t\tif (arg instanceof Vector2d || arg instanceof Vector3d) {\n\t\t\t\t[_x, _y, _z] = [arg.x, arg.y, \"z\" in arg ? arg.z : this.z];\n\t\t\t} else {\n\t\t\t\t_x = arg;\n\t\t\t\t_y = this.y;\n\t\t\t\t_z = this.z;\n\t\t\t}\n\t\t} else if (args.length === 2) {\n\t\t\t[_x, _y, _z] = [args[0], args[1], this.z];\n\t\t} else {\n\t\t\t[_x, _y, _z] = args;\n\t\t}\n\n\t\treturn this.x === _x && this.y === _y && this.z === _z;\n\t}\n\n\t/**\n\t * normalize this vector (scale the vector so that its magnitude is 1)\n\t * @returns Reference to this object for method chaining\n\t */\n\tnormalize() {\n\t\treturn this.div(this.length() || 1);\n\t}\n\n\t/**\n\t * change this vector to be perpendicular to what it was before.<br>\n\t * (Effectively rotates it 90 degrees in a clockwise direction around the z axis)\n\t * @returns Reference to this object for method chaining\n\t */\n\tperp() {\n\t\treturn this.set(this.y, -this.x, this.z);\n\t}\n\n\t/**\n\t * Rotate this vector (counter-clockwise) by the specified angle (in radians) around the z axis\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: XYPoint | Vector3d) {\n\t\tlet cx = 0;\n\t\tlet cy = 0;\n\n\t\tif (v) {\n\t\t\tcx = v.x;\n\t\t\tcy = v.y;\n\t\t}\n\n\t\tconst x = this.x - cx;\n\t\tconst y = this.y - cy;\n\n\t\tconst c = Math.cos(angle);\n\t\tconst s = Math.sin(angle);\n\n\t\treturn this.set(x * c - y * s + cx, x * s + y * c + cy, this.z);\n\t}\n\n\t/**\n\t * return the dot product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The dot product.\n\t */\n\tdot(v: Vector2d | Vector3d) {\n\t\treturn this.x * v.x + this.y * v.y + this.z * (\"z\" in v ? v.z : this.z);\n\t}\n\n\t/**\n\t * calculate the cross product of this vector and the passed one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcross(v: Vector3d) {\n\t\tconst ax = this.x;\n\t\tconst ay = this.y;\n\t\tconst az = this.z;\n\t\tconst bx = v.x;\n\t\tconst by = v.y;\n\t\tconst bz = v.z;\n\n\t\tthis.x = ay * bz - az * by;\n\t\tthis.y = az * bx - ax * bz;\n\t\tthis.z = ax * by - ay * bx;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the square length of this vector\n\t * @returns The length^2 of this vector.\n\t */\n\tlength2(): number {\n\t\treturn this.dot(this);\n\t}\n\n\t/**\n\t * return the length (magnitude) of this vector\n\t * @returns the length of this vector\n\t */\n\tlength() {\n\t\treturn Math.sqrt(this.length2());\n\t}\n\n\t/**\n\t * Linearly interpolate between this vector and the given one.\n\t * Frame-rate dependent at a fixed alpha — for smooth tracking\n\t * use {@link Vector3d.damp} instead.\n\t * @param v - other vector\n\t * @param alpha - distance along the line (alpha = 0 → this vector, alpha = 1 → `v`)\n\t * @returns Reference to this object for method chaining\n\t */\n\tlerp(v: Vector3d, alpha: number) {\n\t\tthis.x = lerp(this.x, v.x, alpha);\n\t\tthis.y = lerp(this.y, v.y, alpha);\n\t\tthis.z = lerp(this.z, v.z, alpha);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Frame-rate independent exponential damping toward `target`.\n\t * Wrapper around {@link damp} applied per component.\n\t * @param target - vector to approach (Vector2d treats `z` as 0)\n\t * @param lambda - decay rate in `1/seconds` (higher = snappier)\n\t * @param dt - delta time in **seconds**\n\t * @returns Reference to this object for method chaining\n\t * @example\n\t * // smooth-follow a Camera3d target at a frame-rate independent rate:\n\t * camera.pos.damp(target.pos, 5, dt / 1000);\n\t */\n\tdamp(target: Vector2d | Vector3d, lambda: number, dt: number) {\n\t\tthis.x = damp(this.x, target.x, lambda, dt);\n\t\tthis.y = damp(this.y, target.y, lambda, dt);\n\t\tthis.z = damp(this.z, \"z\" in target ? target.z : 0, lambda, dt);\n\t\treturn this;\n\t}\n\n\t/**\n\t * interpolate the position of this vector on the x and y axis towards the given one by the given maximum step.\n\t * @param target other vector\n\t * @param step - the maximum step per iteration (Negative values will push the vector away from the target)\n\t * @returns Reference to this object for method chaining\n\t */\n\tmoveTowards(target: Vector2d | Vector3d, step: number) {\n\t\tconst dx = target.x - this.x;\n\t\tconst dy = target.y - this.y;\n\n\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\n\t\t// within one step (and not fleeing): land exactly on the target (x/y\n\t\t// only, this is a 2D interpolation) — and always mutate/return `this`,\n\t\t// never the caller's target\n\t\tif (distance === 0 || (step >= 0 && distance <= step)) {\n\t\t\tthis.x = target.x;\n\t\t\tthis.y = target.y;\n\t\t\treturn this;\n\t\t}\n\n\t\tconst angle = Math.atan2(dy, dx);\n\t\tthis.x += Math.cos(angle) * step;\n\t\tthis.y += Math.sin(angle) * step;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the distance between this vector and the passed one\n\t * @param v other vector\n\t * @returns distance\n\t */\n\tdistance(v: Vector2d | Vector3d) {\n\t\tconst dx = this.x - v.x;\n\t\tconst dy = this.y - v.y;\n\t\tconst dz = this.z - (\"z\" in v ? v.z : 0);\n\t\treturn Math.sqrt(dx * dx + dy * dy + dz * dz);\n\t}\n\n\t/**\n\t * return the angle between this vector and the passed one\n\t * @param v other vector\n\t * @returns angle in radians\n\t */\n\tangle(v: Vector2d | Vector3d) {\n\t\treturn Math.acos(clamp(this.dot(v) / (this.length() * v.length()), -1, 1));\n\t}\n\n\t/**\n\t * project this vector on to another vector.\n\t * @param v - The vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tproject(v: Vector2d | Vector3d) {\n\t\tconst ratio = this.dot(v) / v.length2();\n\t\treturn this.scale(ratio, ratio, ratio);\n\t}\n\n\t/**\n\t * Project this vector onto a vector of unit length.<br>\n\t * This is slightly more efficient than `project` when dealing with unit vectors.\n\t * @param v - The unit vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tprojectN(v: Vector2d | Vector3d) {\n\t\tconst ratio = this.dot(v) / v.length2();\n\t\treturn this.scale(ratio, ratio, ratio);\n\t}\n\n\t/**\n\t * return a clone copy of this vector\n\t * @returns new Vector3d\n\t */\n\tclone() {\n\t\treturn vector3dPool.get(this.x, this.y, this.z);\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns stringified representation\n\t */\n\ttoString() {\n\t\treturn `x:${this.x},y:${this.y},z:${this.z}` as const;\n\t}\n}\n\nexport const vector3dPool = createPool<\n\tVector3d,\n\t[x?: number | undefined, y?: number | undefined, z?: number | undefined]\n>((x, y, z) => {\n\tconst vector = new Vector3d(x, y, z);\n\n\treturn {\n\t\tinstance: vector,\n\t\treset(x = 0, y = 0, z = 0) {\n\t\t\tvector.x = x;\n\t\t\tvector.y = y;\n\t\t\tvector.z = z;\n\t\t},\n\t};\n});\n","import { Point } from \"../geometries/point.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { EPSILON } from \"./math.ts\";\nimport { Matrix2d } from \"./matrix2d.ts\";\nimport { Vector2d } from \"./vector2d.ts\";\nimport { Vector3d } from \"./vector3d.ts\";\n\ntype SixteenNumbers = [\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n\tnumber,\n];\n\ntype ConstructorArg = [Matrix3d] | SixteenNumbers | [undefined] | [];\n\nconst reset = (instance: Matrix3d, ...value: ConstructorArg) => {\n\tif (value[0] instanceof Matrix3d) {\n\t\tinstance.copy(value[0]);\n\t} else if (typeof value[0] === \"number\") {\n\t\tinstance.setTransform(...value);\n\t} else {\n\t\tinstance.identity();\n\t}\n};\n\n/**\n * a 4x4 Matrix3d Object\n * @category Math\n */\nexport class Matrix3d {\n\t/**\n\t * The matrix values\n\t */\n\tval: Float32Array;\n\n\t/**\n\t * Constructs a new Matrix3d object.\n\t * @param value - The values to initialize the matrix with.\n\t */\n\tconstructor(...value: ConstructorArg) {\n\t\tthis.val = new Float32Array(16);\n\t\treset(this, ...value);\n\t}\n\n\t/**\n\t * Gets the tx component of the matrix.\n\t * @returns The tx component of the matrix.\n\t */\n\tget tx() {\n\t\treturn this.val[12];\n\t}\n\n\t/**\n\t * Gets the ty component of the matrix.\n\t * @returns The ty component of the matrix.\n\t */\n\tget ty() {\n\t\treturn this.val[13];\n\t}\n\n\t/**\n\t * Gets the tz component of the matrix.\n\t * @returns The tz component of the matrix.\n\t */\n\tget tz() {\n\t\treturn this.val[14];\n\t}\n\n\t/**\n\t * reset the transformation matrix to the identity matrix (no transformation).<br>\n\t * the identity matrix and parameters position : <br>\n\t * <img src=\"images/identity-matrix_2x.png\"/>\n\t * @returns Reference to this object for method chaining\n\t */\n\tidentity() {\n\t\treturn this.setTransform(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);\n\t}\n\n\t/**\n\t * Set the matrix to the specified value.\n\t * @param values - The matrix components.\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetTransform(...values: SixteenNumbers) {\n\t\tconst a = this.val;\n\n\t\ta[0] = values[0];\n\t\ta[1] = values[1];\n\t\ta[2] = values[2];\n\t\ta[3] = values[3];\n\t\ta[4] = values[4];\n\t\ta[5] = values[5];\n\t\ta[6] = values[6];\n\t\ta[7] = values[7];\n\t\ta[8] = values[8];\n\t\ta[9] = values[9];\n\t\ta[10] = values[10];\n\t\ta[11] = values[11];\n\t\ta[12] = values[12];\n\t\ta[13] = values[13];\n\t\ta[14] = values[14];\n\t\ta[15] = values[15];\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Copies over the values from another me.Matrix3d.\n\t * @param m - the matrix object to copy from\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(m: Matrix3d) {\n\t\tthis.val.set(m.val);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Copies over the upper-left 2x2 values from the given me.Matrix2d\n\t * @param m - the matrix object to copy from\n\t * @returns Reference to this object for method chaining\n\t */\n\tfromMat2d(m: Matrix2d) {\n\t\tconst b = m.val;\n\t\treturn this.setTransform(\n\t\t\tb[0],\n\t\t\tb[3],\n\t\t\tb[6],\n\t\t\t0,\n\t\t\tb[1],\n\t\t\tb[4],\n\t\t\tb[7],\n\t\t\t0,\n\t\t\tb[2],\n\t\t\tb[5],\n\t\t\tb[8],\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * multiply both matrix\n\t * @param m - Other matrix (Matrix3d or Matrix2d)\n\t * @returns Reference to this object for method chaining\n\t */\n\tmultiply(m: Matrix3d | Matrix2d) {\n\t\tconst a = this.val;\n\t\tconst b = m.val;\n\n\t\t// if the input is a Matrix2d (9 elements), promote to 4x4 layout inline\n\t\t// Matrix2d: [a,b,0, c,d,0, tx,ty,1] → 4x4: [a,b,0,0, c,d,0,0, 0,0,1,0, tx,ty,0,1]\n\t\tlet b00: number, b01: number, b02: number, b03: number;\n\t\tlet b10: number, b11: number, b12: number, b13: number;\n\t\tlet b20: number, b21: number, b22: number, b23: number;\n\t\tlet b30: number, b31: number, b32: number, b33: number;\n\n\t\tif (b.length === 9) {\n\t\t\tb00 = b[0];\n\t\t\tb01 = b[1];\n\t\t\tb02 = 0;\n\t\t\tb03 = 0;\n\t\t\tb10 = b[3];\n\t\t\tb11 = b[4];\n\t\t\tb12 = 0;\n\t\t\tb13 = 0;\n\t\t\tb20 = 0;\n\t\t\tb21 = 0;\n\t\t\tb22 = 1;\n\t\t\tb23 = 0;\n\t\t\tb30 = b[6];\n\t\t\tb31 = b[7];\n\t\t\tb32 = 0;\n\t\t\tb33 = 1;\n\t\t} else {\n\t\t\tb00 = b[0];\n\t\t\tb01 = b[1];\n\t\t\tb02 = b[2];\n\t\t\tb03 = b[3];\n\t\t\tb10 = b[4];\n\t\t\tb11 = b[5];\n\t\t\tb12 = b[6];\n\t\t\tb13 = b[7];\n\t\t\tb20 = b[8];\n\t\t\tb21 = b[9];\n\t\t\tb22 = b[10];\n\t\t\tb23 = b[11];\n\t\t\tb30 = b[12];\n\t\t\tb31 = b[13];\n\t\t\tb32 = b[14];\n\t\t\tb33 = b[15];\n\t\t}\n\n\t\tconst a00 = a[0];\n\t\tconst a01 = a[1];\n\t\tconst a02 = a[2];\n\t\tconst a03 = a[3];\n\t\tconst a10 = a[4];\n\t\tconst a11 = a[5];\n\t\tconst a12 = a[6];\n\t\tconst a13 = a[7];\n\t\tconst a20 = a[8];\n\t\tconst a21 = a[9];\n\t\tconst a22 = a[10];\n\t\tconst a23 = a[11];\n\t\tconst a30 = a[12];\n\t\tconst a31 = a[13];\n\t\tconst a32 = a[14];\n\t\tconst a33 = a[15];\n\n\t\ta[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;\n\t\ta[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;\n\t\ta[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;\n\t\ta[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;\n\n\t\ta[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;\n\t\ta[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;\n\t\ta[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;\n\t\ta[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;\n\n\t\ta[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;\n\t\ta[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;\n\t\ta[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;\n\t\ta[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;\n\n\t\tconst b0 = b30;\n\t\tconst b1 = b31;\n\t\tconst b2 = b32;\n\t\tconst b3 = b33;\n\n\t\ta[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n\t\ta[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n\t\ta[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n\t\ta[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Multiplies the current transformation with a 2D affine matrix (a, b, c, d, e, f).\n\t * The 2D matrix is promoted to 4x4 internally.\n\t * @param a - a component (scale x / cos)\n\t * @param b - b component (skew y / sin)\n\t * @param c - c component (skew x / -sin)\n\t * @param d - d component (scale y / cos)\n\t * @param e - e component (translate x)\n\t * @param f - f component (translate y)\n\t * @returns Reference to this object for method chaining\n\t */\n\ttransform(\n\t\ta: number,\n\t\tb: number,\n\t\tc: number,\n\t\td: number,\n\t\te: number,\n\t\tf: number,\n\t): this;\n\t/**\n\t * Multiplies the current transformation with a full 4x4 matrix specified as 16 individual values (column-major).\n\t * @returns Reference to this object for method chaining\n\t */\n\ttransform(\n\t\tb00: number,\n\t\tb01: number,\n\t\tb02: number,\n\t\tb03: number,\n\t\tb10: number,\n\t\tb11: number,\n\t\tb12: number,\n\t\tb13: number,\n\t\tb20: number,\n\t\tb21: number,\n\t\tb22: number,\n\t\tb23: number,\n\t\tb30: number,\n\t\tb31: number,\n\t\tb32: number,\n\t\tb33: number,\n\t): this;\n\ttransform(\n\t\tb00: number,\n\t\tb01: number,\n\t\tb02: number,\n\t\tb03: number,\n\t\tb10: number,\n\t\tb11: number,\n\t\tb12?: number,\n\t\tb13?: number,\n\t\tb20?: number,\n\t\tb21?: number,\n\t\tb22?: number,\n\t\tb23?: number,\n\t\tb30?: number,\n\t\tb31?: number,\n\t\tb32?: number,\n\t\tb33?: number,\n\t) {\n\t\t// resolve all 16 components (promote 2D affine if only 6 args)\n\t\tconst argc = arguments.length;\n\t\tif (argc !== 6 && argc !== 16) {\n\t\t\tthrow new Error(`transform() requires 6 or 16 arguments, got ${argc}`);\n\t\t}\n\t\tlet c0: number, c1: number, c2: number, c3: number;\n\t\tlet c4: number, c5: number, c6: number, c7: number;\n\t\tlet c8: number, c9: number, c10: number, c11: number;\n\t\tlet c12: number, c13: number, c14: number, c15: number;\n\t\tif (argc === 6) {\n\t\t\t// 2D affine (a, b, c, d, tx, ty) → 4x4\n\t\t\tc0 = b00;\n\t\t\tc1 = b01;\n\t\t\tc2 = 0;\n\t\t\tc3 = 0;\n\t\t\tc4 = b02;\n\t\t\tc5 = b03;\n\t\t\tc6 = 0;\n\t\t\tc7 = 0;\n\t\t\tc8 = 0;\n\t\t\tc9 = 0;\n\t\t\tc10 = 1;\n\t\t\tc11 = 0;\n\t\t\tc12 = b10;\n\t\t\tc13 = b11;\n\t\t\tc14 = 0;\n\t\t\tc15 = 1;\n\t\t} else {\n\t\t\tc0 = b00;\n\t\t\tc1 = b01;\n\t\t\tc2 = b02;\n\t\t\tc3 = b03;\n\t\t\tc4 = b10;\n\t\t\tc5 = b11;\n\t\t\tc6 = b12!;\n\t\t\tc7 = b13!;\n\t\t\tc8 = b20!;\n\t\t\tc9 = b21!;\n\t\t\tc10 = b22!;\n\t\t\tc11 = b23!;\n\t\t\tc12 = b30!;\n\t\t\tc13 = b31!;\n\t\t\tc14 = b32!;\n\t\t\tc15 = b33!;\n\t\t}\n\t\tconst a = this.val;\n\t\tconst a00 = a[0],\n\t\t\ta01 = a[1],\n\t\t\ta02 = a[2],\n\t\t\ta03 = a[3];\n\t\tconst a10 = a[4],\n\t\t\ta11 = a[5],\n\t\t\ta12 = a[6],\n\t\t\ta13 = a[7];\n\t\tconst a20 = a[8],\n\t\t\ta21 = a[9],\n\t\t\ta22 = a[10],\n\t\t\ta23 = a[11];\n\t\tconst a30 = a[12],\n\t\t\ta31 = a[13],\n\t\t\ta32 = a[14],\n\t\t\ta33 = a[15];\n\n\t\ta[0] = c0 * a00 + c1 * a10 + c2 * a20 + c3 * a30;\n\t\ta[1] = c0 * a01 + c1 * a11 + c2 * a21 + c3 * a31;\n\t\ta[2] = c0 * a02 + c1 * a12 + c2 * a22 + c3 * a32;\n\t\ta[3] = c0 * a03 + c1 * a13 + c2 * a23 + c3 * a33;\n\n\t\ta[4] = c4 * a00 + c5 * a10 + c6 * a20 + c7 * a30;\n\t\ta[5] = c4 * a01 + c5 * a11 + c6 * a21 + c7 * a31;\n\t\ta[6] = c4 * a02 + c5 * a12 + c6 * a22 + c7 * a32;\n\t\ta[7] = c4 * a03 + c5 * a13 + c6 * a23 + c7 * a33;\n\n\t\ta[8] = c8 * a00 + c9 * a10 + c10 * a20 + c11 * a30;\n\t\ta[9] = c8 * a01 + c9 * a11 + c10 * a21 + c11 * a31;\n\t\ta[10] = c8 * a02 + c9 * a12 + c10 * a22 + c11 * a32;\n\t\ta[11] = c8 * a03 + c9 * a13 + c10 * a23 + c11 * a33;\n\n\t\ta[12] = c12 * a00 + c13 * a10 + c14 * a20 + c15 * a30;\n\t\ta[13] = c12 * a01 + c13 * a11 + c14 * a21 + c15 * a31;\n\t\ta[14] = c12 * a02 + c13 * a12 + c14 * a22 + c15 * a32;\n\t\ta[15] = c12 * a03 + c13 * a13 + c14 * a23 + c15 * a33;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Transpose the value of this matrix.\n\t * @returns Reference to this object for method chaining\n\t */\n\ttranspose() {\n\t\tconst a = this.val;\n\t\tconst a01 = a[1];\n\t\tconst a02 = a[2];\n\t\tconst a03 = a[3];\n\t\tconst a12 = a[6];\n\t\tconst a13 = a[7];\n\t\tconst a23 = a[11];\n\n\t\ta[1] = a[4];\n\t\ta[2] = a[8];\n\t\ta[3] = a[12];\n\t\ta[4] = a01;\n\t\ta[6] = a[9];\n\t\ta[7] = a[13];\n\t\ta[8] = a02;\n\t\ta[9] = a12;\n\t\ta[11] = a[14];\n\t\ta[12] = a03;\n\t\ta[13] = a13;\n\t\ta[14] = a23;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * invert this matrix, causing it to apply the opposite transformation.\n\t * @returns Reference to this object for method chaining\n\t */\n\tinvert() {\n\t\tconst a = this.val;\n\n\t\tconst a00 = a[0];\n\t\tconst a01 = a[1];\n\t\tconst a02 = a[2];\n\t\tconst a03 = a[3];\n\t\tconst a10 = a[4];\n\t\tconst a11 = a[5];\n\t\tconst a12 = a[6];\n\t\tconst a13 = a[7];\n\t\tconst a20 = a[8];\n\t\tconst a21 = a[9];\n\t\tconst a22 = a[10];\n\t\tconst a23 = a[11];\n\t\tconst a30 = a[12];\n\t\tconst a31 = a[13];\n\t\tconst a32 = a[14];\n\t\tconst a33 = a[15];\n\n\t\tconst b00 = a00 * a11 - a01 * a10;\n\t\tconst b01 = a00 * a12 - a02 * a10;\n\t\tconst b02 = a00 * a13 - a03 * a10;\n\t\tconst b03 = a01 * a12 - a02 * a11;\n\n\t\tconst b04 = a01 * a13 - a03 * a11;\n\t\tconst b05 = a02 * a13 - a03 * a12;\n\t\tconst b06 = a20 * a31 - a21 * a30;\n\t\tconst b07 = a20 * a32 - a22 * a30;\n\n\t\tconst b08 = a20 * a33 - a23 * a30;\n\t\tconst b09 = a21 * a32 - a22 * a31;\n\t\tconst b10 = a21 * a33 - a23 * a31;\n\t\tconst b11 = a22 * a33 - a23 * a32;\n\n\t\t// Calculate the determinant\n\t\tlet det =\n\t\t\tb00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n\n\t\tif (!det) {\n\t\t\treturn this.identity();\n\t\t}\n\n\t\tdet = 1 / det;\n\n\t\ta[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;\n\t\ta[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;\n\t\ta[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;\n\t\ta[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;\n\t\ta[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;\n\t\ta[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;\n\t\ta[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;\n\t\ta[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;\n\t\ta[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;\n\t\ta[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;\n\t\ta[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;\n\t\ta[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;\n\t\ta[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;\n\t\ta[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;\n\t\ta[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;\n\t\ta[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * apply the current transform to the given 2d or 3d vector or point\n\t * @param v - the vector object to be transformed\n\t * @returns result vector object.\n\t */\n\tapply(v: Vector3d | Vector2d | Point) {\n\t\tconst a = this.val;\n\t\tconst x = v.x;\n\t\tconst y = v.y;\n\t\tconst z = \"z\" in v ? v.z : 1;\n\n\t\tconst w = a[3] * x + a[7] * y + a[11] * z + a[15] || 1.0;\n\n\t\tv.x = (a[0] * x + a[4] * y + a[8] * z + a[12]) / w;\n\t\tv.y = (a[1] * x + a[5] * y + a[9] * z + a[13]) / w;\n\n\t\tif (\"z\" in v) {\n\t\t\tv.z = (a[2] * x + a[6] * y + a[10] * z + a[14]) / w;\n\t\t}\n\n\t\treturn v;\n\t}\n\n\t/**\n\t * apply the inverted current transform to the given 2d or 3d vector\n\t * @param v - the vector object to be transformed\n\t * @returns result vector object.\n\t */\n\tapplyInverse(v: Vector3d | Vector2d) {\n\t\t// invert the current matrix\n\t\tconst im = matrix3dPool.get(this).invert();\n\t\t// apply the inverted matrix\n\t\tim.apply(v);\n\n\t\tmatrix3dPool.release(im);\n\n\t\treturn v;\n\t}\n\n\t/**\n\t * generate an orthogonal projection matrix, with the result replacing the current matrix\n\t * <img src=\"images/glortho.gif\"/><br>\n\t * @param left - farthest left on the x-axis\n\t * @param right - farthest right on the x-axis\n\t * @param bottom - farthest down on the y-axis\n\t * @param top - farthest up on the y-axis\n\t * @param near - distance to the near clipping plane along the -Z axis\n\t * @param far - distance to the far clipping plane along the -Z axis\n\t * @returns Reference to this object for method chaining\n\t */\n\tortho(\n\t\tleft: number,\n\t\tright: number,\n\t\tbottom: number,\n\t\ttop: number,\n\t\tnear: number,\n\t\tfar: number,\n\t) {\n\t\tconst a = this.val;\n\t\tconst leftRight = 1.0 / (left - right);\n\t\tconst bottomTop = 1.0 / (bottom - top);\n\t\tconst nearFar = 1.0 / (near - far);\n\n\t\ta[0] = -2.0 * leftRight;\n\t\ta[1] = 0.0;\n\t\ta[2] = 0.0;\n\t\ta[3] = 0.0;\n\t\ta[4] = 0.0;\n\t\ta[5] = -2.0 * bottomTop;\n\t\ta[6] = 0.0;\n\t\ta[7] = 0.0;\n\t\ta[8] = 0.0;\n\t\ta[9] = 0.0;\n\t\ta[10] = 2.0 * nearFar;\n\t\ta[11] = 0.0;\n\t\ta[12] = (left + right) * leftRight;\n\t\ta[13] = (top + bottom) * bottomTop;\n\t\ta[14] = (far + near) * nearFar;\n\t\ta[15] = 1.0;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * generate a perspective projection matrix, with the result replacing the current matrix\n\t * @param fov - vertical field of view in radians\n\t * @param aspect - aspect ratio (width / height)\n\t * @param near - distance to the near clipping plane along the -Z axis\n\t * @param far - distance to the far clipping plane along the -Z axis\n\t * @returns Reference to this object for method chaining\n\t */\n\tperspective(fov: number, aspect: number, near: number, far: number) {\n\t\tconst a = this.val;\n\t\tconst f = 1.0 / Math.tan(fov / 2.0);\n\t\tconst nf = 1.0 / (near - far);\n\n\t\ta[0] = f / aspect;\n\t\ta[1] = 0.0;\n\t\ta[2] = 0.0;\n\t\ta[3] = 0.0;\n\t\ta[4] = 0.0;\n\t\ta[5] = f;\n\t\ta[6] = 0.0;\n\t\ta[7] = 0.0;\n\t\ta[8] = 0.0;\n\t\ta[9] = 0.0;\n\t\ta[10] = (far + near) * nf;\n\t\ta[11] = -1.0;\n\t\ta[12] = 0.0;\n\t\ta[13] = 0.0;\n\t\ta[14] = 2.0 * far * near * nf;\n\t\ta[15] = 0.0;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * scale the matrix\n\t * @param x - a number representing the abscissa of the scaling vector.\n\t * @param [y] - a number representing the ordinate of the scaling vector.\n\t * @param [z] - a number representing the depth vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x, z = 1) {\n\t\tconst a = this.val;\n\n\t\ta[0] = a[0] * x;\n\t\ta[1] = a[1] * x;\n\t\ta[2] = a[2] * x;\n\t\ta[3] = a[3] * x;\n\n\t\ta[4] = a[4] * y;\n\t\ta[5] = a[5] * y;\n\t\ta[6] = a[6] * y;\n\t\ta[7] = a[7] * y;\n\n\t\ta[8] = a[8] * z;\n\t\ta[9] = a[9] * z;\n\t\ta[10] = a[10] * z;\n\t\ta[11] = a[11] * z;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * adds a 2D scaling transformation.\n\t * @param v - scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector3d) {\n\t\treturn this.scale(v.x, v.y, v.z);\n\t}\n\n\t/**\n\t * specifies a 2D scale operation using the [sx, 1] scaling vector\n\t * @param x - x scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleX(x: number) {\n\t\treturn this.scale(x, 1);\n\t}\n\n\t/**\n\t * specifies a 2D scale operation using the [1,sy] scaling vector\n\t * @param y - y scaling vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleY(y: number) {\n\t\treturn this.scale(1, y);\n\t}\n\n\t/**\n\t * rotate this matrix (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - Rotation angle in radians.\n\t * @param [v] - the axis to rotate around (defaults to Z axis)\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: Vector3d) {\n\t\tif (angle !== 0) {\n\t\t\tconst a = this.val;\n\t\t\tlet x = v ? v.x : 0;\n\t\t\tlet y = v ? v.y : 0;\n\t\t\tlet z = v ? v.z : 1;\n\n\t\t\tlet len = Math.sqrt(x * x + y * y + z * z);\n\n\t\t\tif (len < EPSILON) {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tlen = 1 / len;\n\t\t\tx *= len;\n\t\t\ty *= len;\n\t\t\tz *= len;\n\n\t\t\tconst s = Math.sin(angle);\n\t\t\tconst c = Math.cos(angle);\n\t\t\tconst t = 1 - c;\n\n\t\t\tconst a00 = a[0];\n\t\t\tconst a01 = a[1];\n\t\t\tconst a02 = a[2];\n\t\t\tconst a03 = a[3];\n\t\t\tconst a10 = a[4];\n\t\t\tconst a11 = a[5];\n\t\t\tconst a12 = a[6];\n\t\t\tconst a13 = a[7];\n\t\t\tconst a20 = a[8];\n\t\t\tconst a21 = a[9];\n\t\t\tconst a22 = a[10];\n\t\t\tconst a23 = a[11];\n\n\t\t\t// Construct the elements of the rotation matrix\n\t\t\tconst b00 = x * x * t + c;\n\t\t\tconst b01 = y * x * t + z * s;\n\t\t\tconst b02 = z * x * t - y * s;\n\t\t\tconst b10 = x * y * t - z * s;\n\t\t\tconst b11 = y * y * t + c;\n\t\t\tconst b12 = z * y * t + x * s;\n\t\t\tconst b20 = x * z * t + y * s;\n\t\t\tconst b21 = y * z * t - x * s;\n\t\t\tconst b22 = z * z * t + c;\n\n\t\t\t// Perform rotation-specific matrix multiplication\n\t\t\ta[0] = a00 * b00 + a10 * b01 + a20 * b02;\n\t\t\ta[1] = a01 * b00 + a11 * b01 + a21 * b02;\n\t\t\ta[2] = a02 * b00 + a12 * b01 + a22 * b02;\n\t\t\ta[3] = a03 * b00 + a13 * b01 + a23 * b02;\n\t\t\ta[4] = a00 * b10 + a10 * b11 + a20 * b12;\n\t\t\ta[5] = a01 * b10 + a11 * b11 + a21 * b12;\n\t\t\ta[6] = a02 * b10 + a12 * b11 + a22 * b12;\n\t\t\ta[7] = a03 * b10 + a13 * b11 + a23 * b12;\n\t\t\ta[8] = a00 * b20 + a10 * b21 + a20 * b22;\n\t\t\ta[9] = a01 * b20 + a11 * b21 + a21 * b22;\n\t\t\ta[10] = a02 * b20 + a12 * b21 + a22 * b22;\n\t\t\ta[11] = a03 * b20 + a13 * b21 + a23 * b22;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * translate the matrix position using the given vector\n\t * @param x - a number representing the abscissa of the vector, or a vector object\n\t * @param [y] - a number representing the ordinate of the vector.\n\t * @param [z] - a number representing the depth of the vector\n\t * @returns Reference to this object for method chaining\n\t */\n\ttranslate(x: number, y: number, z?: number): Matrix3d;\n\ttranslate(vector: Vector3d | Vector2d): Matrix3d;\n\ttranslate(xOrVector: Vector3d | Vector2d | number, y?: number, z?: number) {\n\t\tconst a = this.val;\n\t\tlet _x: number;\n\t\tlet _y: number;\n\t\tlet _z: number;\n\n\t\tif (xOrVector instanceof Vector3d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t\t_z = xOrVector.z;\n\t\t} else if (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t\t_z = 0;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t\t_z = z ?? 0;\n\t\t}\n\n\t\ta[12] = a[0] * _x + a[4] * _y + a[8] * _z + a[12];\n\t\ta[13] = a[1] * _x + a[5] * _y + a[9] * _z + a[13];\n\t\ta[14] = a[2] * _x + a[6] * _y + a[10] * _z + a[14];\n\t\ta[15] = a[3] * _x + a[7] * _y + a[11] * _z + a[15];\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Check if the matrix is an identity matrix.\n\t * @returns true if the matrix is an identity matrix\n\t */\n\tisIdentity() {\n\t\tconst a = this.val;\n\n\t\t// check translation first (most commonly non-zero), then diagonal, then rest\n\t\treturn (\n\t\t\ta[12] === 0 &&\n\t\t\ta[13] === 0 &&\n\t\t\ta[0] === 1 &&\n\t\t\ta[5] === 1 &&\n\t\t\ta[10] === 1 &&\n\t\t\ta[15] === 1 &&\n\t\t\ta[1] === 0 &&\n\t\t\ta[2] === 0 &&\n\t\t\ta[3] === 0 &&\n\t\t\ta[4] === 0 &&\n\t\t\ta[6] === 0 &&\n\t\t\ta[7] === 0 &&\n\t\t\ta[8] === 0 &&\n\t\t\ta[9] === 0 &&\n\t\t\ta[11] === 0 &&\n\t\t\ta[14] === 0\n\t\t);\n\t}\n\n\t/**\n\t * return true if the two matrices are identical\n\t * @param m - the other matrix\n\t * @returns true if both are equals\n\t */\n\tequals(m: Matrix3d) {\n\t\tconst b = m.val;\n\t\tconst a = this.val;\n\n\t\treturn (\n\t\t\ta[0] === b[0] &&\n\t\t\ta[1] === b[1] &&\n\t\t\ta[2] === b[2] &&\n\t\t\ta[3] === b[3] &&\n\t\t\ta[4] === b[4] &&\n\t\t\ta[5] === b[5] &&\n\t\t\ta[6] === b[6] &&\n\t\t\ta[7] === b[7] &&\n\t\t\ta[8] === b[8] &&\n\t\t\ta[9] === b[9] &&\n\t\t\ta[10] === b[10] &&\n\t\t\ta[11] === b[11] &&\n\t\t\ta[12] === b[12] &&\n\t\t\ta[13] === b[13] &&\n\t\t\ta[14] === b[14] &&\n\t\t\ta[15] === b[15]\n\t\t);\n\t}\n\n\t/**\n\t * return a Matrix2d projection of this Matrix3d\n\t * @param [out] - an optional Matrix2d to reuse (avoids allocation)\n\t * @returns a Matrix2d containing the upper-left 2D affine components\n\t */\n\ttoMatrix2d(out?: Matrix2d) {\n\t\tif (out) {\n\t\t\treturn out.fromMat3d(this);\n\t\t}\n\t\treturn new Matrix2d(this);\n\t}\n\n\t/**\n\t * Clone the Matrix\n\t * @returns a cloned matrix\n\t */\n\tclone() {\n\t\treturn matrix3dPool.get(this);\n\t}\n\n\t/**\n\t * return an array representation of this Matrix\n\t * @returns internal matrix values\n\t */\n\ttoArray() {\n\t\treturn this.val;\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns stringified representation\n\t */\n\ttoString() {\n\t\tconst a = this.val;\n\n\t\treturn `Matrix3d(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]}, ${a[4]}, ${a[5]}, ${\n\t\t\ta[6]\n\t\t}, ${a[7]}, ${a[8]}, ${a[9]}, ${a[10]}, ${a[11]}, ${a[12]}, ${a[13]}, ${\n\t\t\ta[14]\n\t\t}, ${a[15]})`;\n\t}\n}\n\nexport const matrix3dPool = createPool<Matrix3d, ConstructorArg>(\n\t(...values) => {\n\t\tconst instance = new Matrix3d(...values);\n\n\t\treturn {\n\t\t\tinstance,\n\t\t\treset(...value) {\n\t\t\t\treset(instance, ...value);\n\t\t\t},\n\t\t};\n\t},\n);\n","import { Color } from \"./../math/color.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\n\n/**\n * @import {Gradient} from \"./gradient.js\";\n */\n\n/**\n * Renderer-agnostic state container with a pre-allocated save/restore stack.\n *\n * Owns the mutable rendering state (color, tint, transform, scissor, blend mode)\n * and provides zero-allocation save()/restore() via index-based stacks.\n * Both CanvasRenderer and WebGLRenderer (and future WebGPU) delegate state\n * management here, keeping rendering-context-specific code in the renderers.\n */\nexport default class RenderState {\n\tconstructor() {\n\t\t/**\n\t\t * current fill & stroke style color\n\t\t * @type {Color}\n\t\t */\n\t\tthis.currentColor = new Color(0, 0, 0, 1.0);\n\n\t\t/**\n\t\t * current tint applied to sprites\n\t\t * @type {Color}\n\t\t */\n\t\tthis.currentTint = new Color(255, 255, 255, 1.0);\n\n\t\t/**\n\t\t * current transformation matrix\n\t\t * @type {Matrix3d}\n\t\t */\n\t\tthis.currentTransform = new Matrix3d();\n\n\t\t/**\n\t\t * current scissor/clipping rectangle [x, y, width, height]\n\t\t * @type {Int32Array}\n\t\t */\n\t\tthis.currentScissor = new Int32Array(4);\n\n\t\t/**\n\t\t * current gradient fill (null when using solid color)\n\t\t * @type {Gradient|null}\n\t\t */\n\t\tthis.currentGradient = null;\n\n\t\t/**\n\t\t * current line dash pattern (empty array = solid line)\n\t\t * @type {number[]}\n\t\t */\n\t\tthis.lineDash = [];\n\n\t\t/**\n\t\t * current blend mode\n\t\t * @type {string}\n\t\t */\n\t\tthis.currentBlendMode = \"none\";\n\n\t\t/**\n\t\t * current custom shader effect (undefined when using default shader)\n\t\t * @type {ShaderEffect|undefined}\n\t\t */\n\t\tthis.currentShader = undefined;\n\n\t\t/**\n\t\t * current per-renderable depth value, pushed into the vertex stream\n\t\t * by the WebGL batchers as the `z` component of each vertex. Has no\n\t\t * visible effect under the engine's default orthographic projection;\n\t\t * required for perspective (Camera3d) to scale sprites by distance.\n\t\t * Mirrors `renderable.depth`, set by `Renderable.preDraw` via\n\t\t * {@link Renderer#setDepth}.\n\t\t * @type {number}\n\t\t */\n\t\tthis.currentDepth = 0;\n\n\t\t// ---- pre-allocated save/restore stack ----\n\n\t\t/**\n\t\t * @ignore\n\t\t */\n\t\tthis._stackCapacity = 32;\n\n\t\t/**\n\t\t * current stack depth\n\t\t * @ignore\n\t\t */\n\t\tthis._stackDepth = 0;\n\n\t\t/** @ignore */\n\t\tthis._colorStack = Array.from({ length: this._stackCapacity }, () => {\n\t\t\treturn new Color();\n\t\t});\n\n\t\t/** @ignore */\n\t\tthis._tintStack = Array.from({ length: this._stackCapacity }, () => {\n\t\t\treturn new Color();\n\t\t});\n\n\t\t/** @ignore */\n\t\tthis._matrixStack = Array.from({ length: this._stackCapacity }, () => {\n\t\t\treturn new Matrix3d();\n\t\t});\n\n\t\t/** @ignore */\n\t\tthis._scissorStack = Array.from({ length: this._stackCapacity }, () => {\n\t\t\treturn new Int32Array(4);\n\t\t});\n\n\t\t/** @ignore */\n\t\tthis._lineDashStack = new Array(this._stackCapacity);\n\n\t\t/** @ignore */\n\t\tthis._scissorActive = new Uint8Array(this._stackCapacity);\n\n\t\t/** @ignore */\n\t\tthis._gradientStack = new Array(this._stackCapacity);\n\n\t\t/** @ignore */\n\t\tthis._blendStack = new Array(this._stackCapacity);\n\n\t\t/** @ignore */\n\t\tthis._shaderStack = new Array(this._stackCapacity);\n\n\t\t/** @ignore */\n\t\tthis._depthStack = new Float32Array(this._stackCapacity);\n\t}\n\n\t/**\n\t * Save the current state onto the stack (zero allocations).\n\t * @param {boolean} [scissorTestActive=false] - whether scissor/clip is currently enabled\n\t */\n\tsave(scissorTestActive = false) {\n\t\tconst depth = this._stackDepth;\n\n\t\tif (depth >= this._stackCapacity) {\n\t\t\tthis._growStacks();\n\t\t}\n\n\t\tthis._colorStack[depth].copy(this.currentColor);\n\t\tthis._tintStack[depth].copy(this.currentTint);\n\t\tthis._matrixStack[depth].copy(this.currentTransform);\n\t\tthis._gradientStack[depth] = this.currentGradient;\n\t\tthis._lineDashStack[depth] = this.lineDash;\n\t\tthis._blendStack[depth] = this.currentBlendMode;\n\t\tthis._shaderStack[depth] = this.currentShader;\n\t\tthis._depthStack[depth] = this.currentDepth;\n\n\t\tif (scissorTestActive) {\n\t\t\tthis._scissorStack[depth].set(this.currentScissor);\n\t\t\tthis._scissorActive[depth] = 1;\n\t\t} else {\n\t\t\tthis._scissorActive[depth] = 0;\n\t\t}\n\n\t\tthis._stackDepth = depth + 1;\n\t}\n\n\t/**\n\t * Inspect the scissor box that the next `restore()` would install,\n\t * without mutating any state. Lets renderers detect whether a\n\t * pending `restore()` will actually change the scissor (and\n\t * decide, e.g., whether to flush GPU work first).\n\t *\n\t * Returns:\n\t * - `Int32Array` (length 4) — `[x, y, width, height]` of the\n\t *   saved scissor when scissor was active at the matching `save()`\n\t *   call.\n\t * - `null` — when the saved state had scissor disabled, or the\n\t *   stack is empty. Treat this as \"next scissor will be inactive\".\n\t *\n\t * The returned array is a **live reference into the internal\n\t * stack** — zero allocation on a hot path. Callers MUST treat it\n\t * as read-only; mutating it corrupts subsequent `restore()` calls.\n\t * @ignore\n\t * @returns {Int32Array | null}\n\t */\n\tpeekScissor() {\n\t\tconst depth = this._stackDepth - 1;\n\t\tif (depth < 0 || !this._scissorActive[depth]) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this._scissorStack[depth];\n\t}\n\n\t/**\n\t * Restore state from the stack.\n\t * Color, tint, transform, and scissor are restored in place.\n\t * Blend mode is NOT applied to `currentBlendMode` — it is returned so the\n\t * renderer can call its own `setBlendMode()` (which has context-specific side effects).\n\t * @param {number} canvasWidth - current canvas width (used when scissor was inactive)\n\t * @param {number} canvasHeight - current canvas height (used when scissor was inactive)\n\t * @returns {{ blendMode: string, scissorActive: boolean } | null} restored blend mode and scissor flag, or null if stack was empty\n\t */\n\trestore(canvasWidth, canvasHeight) {\n\t\tif (this._stackDepth > 0) {\n\t\t\tconst depth = --this._stackDepth;\n\n\t\t\tthis.currentColor.copy(this._colorStack[depth]);\n\t\t\tthis.currentTint.copy(this._tintStack[depth]);\n\t\t\tthis.currentTransform.copy(this._matrixStack[depth]);\n\t\t\tthis.currentGradient = this._gradientStack[depth];\n\t\t\tthis.lineDash = this._lineDashStack[depth];\n\t\t\tthis.currentShader = this._shaderStack[depth];\n\t\t\tthis.currentDepth = this._depthStack[depth];\n\t\t\tconst scissorActive = !!this._scissorActive[depth];\n\t\t\tif (scissorActive) {\n\t\t\t\tthis.currentScissor.set(this._scissorStack[depth]);\n\t\t\t} else {\n\t\t\t\tthis.currentScissor[0] = 0;\n\t\t\t\tthis.currentScissor[1] = 0;\n\t\t\t\tthis.currentScissor[2] = canvasWidth;\n\t\t\t\tthis.currentScissor[3] = canvasHeight;\n\t\t\t}\n\n\t\t\treturn { blendMode: this._blendStack[depth], scissorActive };\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Reset all state to defaults and clear the stack.\n\t * @param {number} width - canvas width\n\t * @param {number} height - canvas height\n\t */\n\treset(width, height) {\n\t\tthis._stackDepth = 0;\n\t\tthis.currentTransform.identity();\n\t\tthis.currentScissor[0] = 0;\n\t\tthis.currentScissor[1] = 0;\n\t\tthis.currentScissor[2] = width;\n\t\tthis.currentScissor[3] = height;\n\t\tthis.currentShader = undefined;\n\t\tthis.currentDepth = 0;\n\t}\n\n\t/** @private — doubles stack capacity when exceeded */\n\t_growStacks() {\n\t\tconst oldCap = this._stackCapacity;\n\t\tconst newCap = oldCap * 2;\n\t\tfor (let i = oldCap; i < newCap; i++) {\n\t\t\tthis._colorStack.push(new Color());\n\t\t\tthis._tintStack.push(new Color());\n\t\t\tthis._matrixStack.push(new Matrix3d());\n\t\t\tthis._scissorStack.push(new Int32Array(4));\n\t\t\tthis._gradientStack.push(null);\n\t\t\tthis._lineDashStack.push([]);\n\t\t\tthis._blendStack.push(undefined);\n\t\t\tthis._shaderStack.push(undefined);\n\t\t}\n\t\tconst newScissorActive = new Uint8Array(newCap);\n\t\tnewScissorActive.set(this._scissorActive);\n\t\tthis._scissorActive = newScissorActive;\n\t\tconst newDepthStack = new Float32Array(newCap);\n\t\tnewDepthStack.set(this._depthStack);\n\t\tthis._depthStack = newDepthStack;\n\t\tthis._stackCapacity = newCap;\n\t}\n}\n","import Path2D from \"./../geometries/path2d.ts\";\nimport { Color } from \"./../math/color.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport { CANVAS_ONRESIZE, emit } from \"../system/event.ts\";\nimport { createCanvas } from \"./canvas_factory.js\";\nimport { Gradient } from \"./gradient.js\";\nimport RenderState from \"./renderstate.js\";\nimport CanvasRenderTarget from \"./rendertarget/canvasrendertarget.js\";\n\n/**\n * @import RenderTargetPool from \"./rendertarget/render_target_pool.js\";\n */\n\n/**\n * @import {Rect} from \"./../geometries/rectangle.ts\";\n * @import {RoundRect} from \"./../geometries/roundrect.ts\";\n * @import {Polygon} from \"../geometries/polygon.ts\";\n * @import {Line} from \"./../geometries/line.ts\";\n * @import {Ellipse} from \"./../geometries/ellipse.ts\";\n * @import {Bounds} from \"./../physics/bounds.ts\";\n * @import {default as Texture2d} from \"./texture/texture2d.ts\";\n */\n\n/**\n * a base renderer object\n * @category Rendering\n */\nexport default class Renderer {\n\t/**\n\t * @param {ApplicationSettings} [options] - optional parameters for the renderer\n\t */\n\tconstructor(options) {\n\t\t/**\n\t\t * The renderer renderTarget\n\t\t * @name renderTarget\n\t\t * @type {CanvasRenderTarget}\n\t\t */\n\t\tthis.renderTarget = new CanvasRenderTarget(\n\t\t\toptions.width,\n\t\t\toptions.height,\n\t\t\t// support case when a global canvas is available, e.g. webapp adapter for wechat\n\t\t\ttypeof globalThis.canvas !== \"undefined\"\n\t\t\t\t? Object.assign(options, { canvas: globalThis.canvas })\n\t\t\t\t: options,\n\t\t);\n\n\t\t/**\n\t\t * The given constructor options\n\t\t * @public\n\t\t * @type {object}\n\t\t */\n\t\tthis.settings = options;\n\n\t\t/**\n\t\t * the requested video size ratio\n\t\t * @public\n\t\t * @type {number}\n\t\t */\n\t\tthis.designRatio = this.settings.width / this.settings.height;\n\n\t\t/**\n\t\t * the scaling ratio to be applied to the main canvas\n\t\t * @type {Vector2d}\n\t\t * @default <1,1>\n\t\t */\n\t\tthis.scaleRatio = new Vector2d(this.settings.scale, this.settings.scale);\n\n\t\t/**\n\t\t * true if the current rendering context is valid\n\t\t * @default true\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isContextValid = true;\n\n\t\t/**\n\t\t * The GPU device identifier string (set by GPU renderers — WebGL today,\n\t\t * WebGPU once it lands; undefined for Canvas).\n\t\t * @type {string|undefined}\n\t\t */\n\t\tthis.GPURenderer = undefined;\n\n\t\t/**\n\t\t * an optional custom shader to use instead of the default one.\n\t\t * Set by a renderable's preDraw when a shader is assigned.\n\t\t * (GPU renderers only; ignored by Canvas renderer)\n\t\t * @type {GLShader|ShaderEffect|undefined}\n\t\t * @ignore\n\t\t */\n\t\tthis.customShader = undefined;\n\n\t\t/**\n\t\t * The render target pool for post-effect processing (ping-pong FBOs).\n\t\t * Initialized by GPU renderers (WebGL, WebGPU). Null on Canvas renderer.\n\t\t * @type {RenderTargetPool|null}\n\t\t * @ignore\n\t\t */\n\t\tthis._renderTargetPool = null;\n\n\t\t/**\n\t\t * The Path2D instance used by the renderer to draw primitives\n\t\t * @type {Path2D}\n\t\t */\n\t\tthis.path2D = new Path2D();\n\n\t\t/**\n\t\t * The renderer backend identity — the built-in renderers report\n\t\t * `\"CANVAS\"`, `\"WebGL2\"` and `\"WebGPU\"`. Use it for identity\n\t\t * checks (code coupled to one backend's machinery); prefer the\n\t\t * capability flags (`shaderLanguage`, `supportsDepthBuffer`,\n\t\t * `supportsRetainedMesh`, `supportsShaderTileLayers`) when the\n\t\t * requirement is a capability rather than a specific backend.\n\t\t * (override this property with a specific value when implementing\n\t\t * a custom renderer)\n\t\t * @type {string}\n\t\t */\n\t\tthis.type = \"Generic\";\n\n\t\t/**\n\t\t * The {@link Application} this renderer belongs to, set by\n\t\t * `Application.init()` — engine code holding a renderer reference\n\t\t * must use this rather than the global game instance.\n\t\t * @type {Application|undefined}\n\t\t */\n\t\tthis.parentApplication = undefined;\n\n\t\t/**\n\t\t * Whether this renderer backend can draw TMX tile layers through a\n\t\t * GPU shader path (see the `gpuTilemap` application setting).\n\t\t * `false` here on the base/Canvas renderer; GPU backends flip it.\n\t\t * A capability flag rather than a version/class check so future\n\t\t * backends advertise support without consumers growing type checks.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.supportsShaderTileLayers = false;\n\n\t\t/**\n\t\t * Whether this renderer backend can keep mesh geometry resident on the\n\t\t * GPU, letting a mesh supply a model matrix instead of vertices it has\n\t\t * already positioned itself. `false` here on the base/Canvas renderer.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.supportsRetainedMesh = false;\n\n\t\t/**\n\t\t * Whether this renderer backend can draw one geometry many times in a\n\t\t * single call from per-instance data — what {@link InstancedMesh}\n\t\t * needs. `false` here on the base/Canvas renderer, which falls back to\n\t\t * drawing each instance individually.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.supportsInstancing = false;\n\n\t\t/**\n\t\t * The source language this backend accepts for user-supplied shaders,\n\t\t * or `null` when it has no programmable pipeline at all (the Canvas\n\t\t * backend). `\"glsl\"` on the WebGL backend, `\"wgsl\"` on the WebGPU\n\t\t * backend.\n\t\t *\n\t\t * Consumers that need a *specific* language — `ShaderEffect` and the\n\t\t * loader's `{vertex, fragment}` shader assets both hand GLSL source\n\t\t * straight to the driver — must compare against the language rather\n\t\t * than test for a GPU backend, or they would accept a backend that\n\t\t * cannot read what they are about to give it.\n\t\t * @type {\"glsl\"|\"wgsl\"|null}\n\t\t * @default null\n\t\t */\n\t\tthis.shaderLanguage = null;\n\n\t\t/**\n\t\t * Whether this backend has a depth buffer, and with it the 3D\n\t\t * projection and mesh paths a depth-sorted scene needs (`Camera3d`\n\t\t * and any camera declaring `defaultSortOn = \"depth\"`). `false` here\n\t\t * on the base/Canvas renderer.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.supportsDepthBuffer = false;\n\n\t\t/**\n\t\t * The background color used to clear the main framebuffer.\n\t\t * Note: alpha value will be set based on the transparent property of the renderer settings.\n\t\t * @default black\n\t\t * @type {Color}\n\t\t */\n\t\tthis.backgroundColor = new Color(\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tthis.settings.transparent ? 0.0 : 1.0,\n\t\t);\n\n\t\t/**\n\t\t * The renderer state container (color, tint, transform, scissor, blend mode)\n\t\t * with a zero-allocation save/restore stack.\n\t\t * @type {RenderState}\n\t\t */\n\t\tthis.renderState = new RenderState();\n\n\t\t// expose renderState properties directly (same references)\n\t\tthis.currentColor = this.renderState.currentColor;\n\t\tthis.currentTint = this.renderState.currentTint;\n\t\tthis.currentScissor = this.renderState.currentScissor;\n\n\t\t/**\n\t\t * The thickness of lines for shape drawing operations like\n\t\t * {@link Renderer#strokeRect}, {@link Renderer#strokeEllipse}\n\t\t * and {@link Renderer#strokeLine}. Subclasses override the\n\t\t * storage — `CanvasRenderer` proxies it to the underlying 2D\n\t\t * context via a getter/setter, while `WebGLRenderer` treats it\n\t\t * as a regular field consumed by its shape-stroke routines.\n\t\t * @type {number}\n\t\t * @default 1\n\t\t */\n\t\tthis.lineWidth = 1;\n\n\t\t/**\n\t\t * @ignore\n\t\t */\n\t\tthis.maskLevel = 0;\n\n\t\t// the projectionMatrix (set through setProjection)\n\t\tthis.projectionMatrix = new Matrix3d();\n\n\t\t// default uvOffset\n\t\tthis.uvOffset = 0;\n\n\t\t/**\n\t\t * The normal-map texture associated with the next `drawImage` call,\n\t\t * if any. Set by `Sprite.draw` (and any other normal-map-aware\n\t\t * renderable) just before calling `drawImage`, then cleared back\n\t\t * to `null` after. The WebGL renderer reads this state and routes\n\t\t * lit quads through the shader's lighting path; the Canvas\n\t\t * renderer ignores it entirely.\n\t\t * @type {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|null}\n\t\t */\n\t\tthis.currentNormalMap = null;\n\n\t\t/**\n\t\t * Number of active `Light2d` instances uploaded to the lit batcher\n\t\t * for the current frame. Set by `setLightUniforms`. The WebGL\n\t\t * renderer's `drawImage` reads this to decide between the unlit\n\t\t * fast-path batcher (default) and the lit batcher (only when\n\t\t * lights AND a normal map are both in play).\n\t\t * @type {number}\n\t\t */\n\t\tthis.activeLightCount = 0;\n\t}\n\n\t/**\n\t * @type {string}\n\t */\n\tget currentBlendMode() {\n\t\treturn this.renderState.currentBlendMode;\n\t}\n\n\tset currentBlendMode(value) {\n\t\tthis.renderState.currentBlendMode = value;\n\t}\n\n\t/**\n\t * Current per-renderable depth value. The GPU batchers (WebGL and\n\t * WebGPU) push it into the vertex stream as the `z`\n\t * component of each vertex — a no-op under the default orthographic\n\t * projection, used by perspective (Camera3d) to\n\t * scale and parallax sprites by distance. Mirrors `renderable.depth`,\n\t * set automatically by `Renderable.preDraw` via {@link Renderer#setDepth}.\n\t * @type {number}\n\t * @default 0\n\t */\n\tget currentDepth() {\n\t\treturn this.renderState.currentDepth;\n\t}\n\n\tset currentDepth(value) {\n\t\tthis.renderState.currentDepth = value;\n\t}\n\n\t/**\n\t * return the height of the canvas which this renderer draws to\n\t * @returns {number} height of the system Canvas\n\t */\n\tget height() {\n\t\treturn this.getCanvas().height;\n\t}\n\n\tset height(value) {\n\t\tthis.resize(this.width, value);\n\t}\n\n\t/**\n\t * return the width of the canvas which this renderer draws to\n\t * @returns {number} width of the system Canvas\n\t */\n\tget width() {\n\t\treturn this.getCanvas().width;\n\t}\n\n\tset width(value) {\n\t\tthis.resize(value, this.height);\n\t}\n\n\t/**\n\t * Acquire whatever the backend cannot acquire synchronously — awaited by\n\t * {@link Application#init} right after the renderer is constructed, and\n\t * the reason `Application#init` is asynchronous at all. The Canvas and\n\t * WebGL backends have their context by the end of the constructor and\n\t * resolve immediately (this default); the WebGPU backend performs its\n\t * adapter/device negotiation here.\n\t * @returns {Promise<void>} resolves once the renderer is ready to draw\n\t */\n\tinit() {\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * prepare the framebuffer for drawing a new frame\n\t */\n\tclear() {}\n\n\t/**\n\t * render the main framebuffer on screen\n\t */\n\tflush() {}\n\n\t/**\n\t * Hold one ground shadow ({@link Mesh#castGroundShadow}) back until the end\n\t * of the mesh pass (#1515).\n\t *\n\t * A blob shadow deliberately writes no depth, so that two overlapping at\n\t * one ground height blend instead of fighting. The price of that choice is\n\t * that it leaves nothing in the depth buffer to defend itself with: any\n\t * opaque mesh drawn afterwards simply paints over it. The ground plane is\n\t * exactly that mesh whenever it sorts after the props standing on it — the\n\t * common case, because a large plane's single sort key says nothing useful\n\t * about where it sits relative to what stands on it.\n\t *\n\t * So shadows are collected here and drawn once the opaque meshes are down.\n\t * Depth *testing* stays on throughout, so a shadow is still correctly\n\t * hidden behind geometry genuinely in front of it: deferring changes only\n\t * who paints over whom among draws that write no depth.\n\t * @param {object} quad - the shared shadow quad\n\t * @param {Matrix3d} modelMatrix - where the blob sits (copied, not retained)\n\t * @param {number} tint - packed ARGB tint for the draw\n\t * @param {object} [instanced] - the `InstancedMesh` whose instance buffer\n\t * supplies one blob per instance, for the instanced tier\n\t * @ignore\n\t */\n\tqueueGroundShadow(quad, modelMatrix, tint, instanced) {\n\t\tconst pool = (this._shadowPool ??= []);\n\t\tconst at = this._shadowCount ?? 0;\n\t\t// The matrix is COPIED, not referenced: every shadow in a scene shares\n\t\t// one quad, so `quad._modelMatrix` is rewritten by the next caster long\n\t\t// before this entry is drawn. Slots are reused frame to frame, so a\n\t\t// steady scene allocates nothing after its first.\n\t\tlet entry = pool[at];\n\t\tif (entry === undefined) {\n\t\t\tentry = { quad: null, matrix: new Matrix3d(), tint: 0, instanced: null };\n\t\t\tpool[at] = entry;\n\t\t}\n\t\tentry.quad = quad;\n\t\tentry.matrix.copy(modelMatrix);\n\t\tentry.tint = tint;\n\t\tentry.instanced = instanced ?? null;\n\t\tthis._shadowCount = at + 1;\n\t}\n\n\t/**\n\t * Draw every ground shadow queued since the last drain, then empty the\n\t * queue (#1515). Called when the renderer leaves mesh mode, and once more\n\t * at end of frame for a scene made only of meshes, which never switches\n\t * away from mesh mode on its own.\n\t *\n\t * Inert on a backend that never queues — the Canvas renderer has no depth\n\t * buffer and so no ground shadows at all.\n\t */\n\tflushGroundShadows() {\n\t\tconst count = this._shadowCount ?? 0;\n\t\tif (count === 0 || this._shadowFlushing === true) {\n\t\t\treturn;\n\t\t}\n\t\tconst pool = this._shadowPool;\n\t\t// emptied BEFORE the replay, not after: the draws below re-enter\n\t\t// `setBatcher`, which calls back in here, and a queue still holding\n\t\t// entries would recurse. `_shadowFlushing` guards the same door from\n\t\t// the other side, and covers the re-entry into `drawMesh`.\n\t\tthis._shadowCount = 0;\n\t\tthis._shadowFlushing = true;\n\t\ttry {\n\t\t\t// The colour the shadow was queued WITH has to be put back, because\n\t\t\t// both backends rebuild the draw tint from `currentTint` +\n\t\t\t// `getGlobalAlpha()` — long since restored to the scene's values by\n\t\t\t// the time this replay runs.\n\t\t\tconst tint = this.currentTint;\n\t\t\tconst savedR = tint.r;\n\t\t\tconst savedG = tint.g;\n\t\t\tconst savedB = tint.b;\n\t\t\t// the colour's OWN alpha, which `setColor(r, g, b)` resets to 1\n\t\t\tconst savedTintAlpha = tint.alpha;\n\t\t\tconst savedAlpha = this.getGlobalAlpha();\n\t\t\ttry {\n\t\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\t\tconst entry = pool[i];\n\t\t\t\t\tconst packed = entry.tint;\n\t\t\t\t\ttint.setColor(\n\t\t\t\t\t\t(packed >>> 16) & 0xff,\n\t\t\t\t\t\t(packed >>> 8) & 0xff,\n\t\t\t\t\t\tpacked & 0xff,\n\t\t\t\t\t);\n\t\t\t\t\tthis.setGlobalAlpha(((packed >>> 24) & 0xff) / 255);\n\t\t\t\t\tif (entry.instanced !== null) {\n\t\t\t\t\t\tthis.drawInstancedShadow(entry.instanced, entry.matrix, entry.quad);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.drawMesh(entry.quad, entry.matrix);\n\t\t\t\t\t}\n\t\t\t\t\t// drop the references, so a destroyed mesh is not held alive by\n\t\t\t\t\t// a pooled slot until that slot is next reused\n\t\t\t\t\tentry.quad = null;\n\t\t\t\t\tentry.instanced = null;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\ttint.setColor(savedR, savedG, savedB, savedTintAlpha);\n\t\t\t\tthis.setGlobalAlpha(savedAlpha);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._shadowFlushing = false;\n\t\t}\n\t}\n\n\t/**\n\t * Draw a textured triangle mesh.\n\t * The mesh object must provide: `vertices` (Float32Array, x/y/z triplets),\n\t * `uvs` (Float32Array, u/v pairs), `indices` (Uint16Array, triangle indices),\n\t * `texture` (TextureAtlas), `vertexCount` (number), and optionally\n\t * `cullBackFaces` (boolean, default true).\n\t *\n\t * On the GPU backends (WebGL and WebGPU — hardware depth testing),\n\t * passing a `modelMatrix` selects the **retained** path: the mesh's\n\t * model-space geometry stays resident on the GPU and the matrix\n\t * places it, so redrawing never re-uploads vertices (see\n\t * {@link Renderer#supportsRetainedMesh}). Without a matrix the\n\t * vertices are taken as already CPU-projected (the 2D-camera path —\n\t * the only path the Canvas renderer supports, using painter's\n\t * algorithm). `Mesh.draw` selects the right form automatically.\n\t * @param {Mesh} mesh - a Mesh renderable or compatible object\n\t * @param {Matrix3d} [modelMatrix] - the mesh's placement, for the retained path (GPU backends)\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tdrawMesh(mesh, modelMatrix) {}\n\n\t/**\n\t * Reset context state\n\t */\n\treset() {\n\t\t// Drop any ground shadows still queued (#1515) BEFORE anything below\n\t\t// can trip a drain. A reset re-inits every batcher and then switches\n\t\t// batcher, which is itself a drain point — replaying entries from the\n\t\t// frame we are abandoning would paint them after `clear()`, and on the\n\t\t// context-lost branch would draw geometry belonging to the dead\n\t\t// context. The frame is being thrown away; its shadows go with it.\n\t\tthis._shadowCount = 0;\n\t\tthis.renderState.reset(this.width, this.height);\n\t\tthis.resetTransform();\n\t\tthis.setBlendMode(this.settings.blendMode);\n\t\tthis.setColor(\"#000000\");\n\t\tthis.clearTint();\n\t\tthis.cache.clear();\n\t\tthis.clearMask();\n\t}\n\n\t/**\n\t * Release any renderer-owned resources. Default is a no-op;\n\t * subclasses override to tear down batchers, FBO pools, GL\n\t * programs, etc. Called by {@link Application#destroy}.\n\t */\n\tdestroy() {}\n\n\t/**\n\t * return a reference to the current render target corresponding canvas which this renderer draws to\n\t * @returns {HTMLCanvasElement}\n\t */\n\tgetCanvas() {\n\t\treturn this.renderTarget.canvas;\n\t}\n\n\t/**\n\t * Create and return a new Canvas element, sized as requested.\n\t *\n\t * The instance form of {@link Renderer.createCanvas}. `app.renderer` is\n\t * the renderer entry point, and a static is not reachable through an\n\t * instance — so this is what user code actually calls.\n\t * @param {number} width - width in pixels\n\t * @param {number} height - height in pixels\n\t * @param {boolean} [returnOffscreenCanvas=false] - return an OffscreenCanvas where supported\n\t * @returns {HTMLCanvasElement|OffscreenCanvas} the new canvas\n\t */\n\tcreateCanvas(width, height, returnOffscreenCanvas = false) {\n\t\treturn createCanvas(width, height, returnOffscreenCanvas);\n\t}\n\n\t/**\n\t * Create and return a new Canvas element (or `OffscreenCanvas` when\n\t * supported and `returnOffscreenCanvas` is true). Centralized\n\t * renderer-side allocator so every scratch / fallback / render-\n\t * target canvas in the engine routes through the same\n\t * `OffscreenCanvas`-aware path, instead of duplicating\n\t * `document.createElement` calls that throw in worker contexts.\n\t * @param {number} width - canvas width in pixels\n\t * @param {number} height - canvas height in pixels\n\t * @param {boolean} [returnOffscreenCanvas=false] - return an\n\t *   `OffscreenCanvas` if the platform supports it\n\t * @returns {HTMLCanvasElement|OffscreenCanvas} a new canvas of the given size\n\t */\n\tstatic createCanvas(width, height, returnOffscreenCanvas = false) {\n\t\treturn createCanvas(width, height, returnOffscreenCanvas);\n\t}\n\n\t/**\n\t * Shared 1×1 fully-white canvas used as a no-op texture fallback.\n\t * Renderers and renderables that need a \"blank\" texture binding\n\t * (e.g. to satisfy a shader's sampler input when there's no real\n\t * image — Kd-only `Mesh` materials, solid-color quad fills, etc.)\n\t * should use this rather than allocating their own.\n\t *\n\t * Lazily created on first call; shared across every caller; uses\n\t * `OffscreenCanvas` where supported (worker-safe). Static so it's\n\t * accessible without a renderer instance (e.g. from a `Mesh`\n\t * constructor that runs before the active renderer is set).\n\t * @returns {HTMLCanvasElement|OffscreenCanvas} the shared 1×1 white canvas\n\t */\n\tstatic getWhitePixel() {\n\t\tif (Renderer._whitePixel === null) {\n\t\t\tconst c = Renderer.createCanvas(1, 1, true);\n\t\t\tconst ctx = c.getContext(\"2d\");\n\t\t\tif (ctx === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Renderer.getWhitePixel: 2D context unavailable on the allocated canvas\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tctx.fillStyle = \"#ffffff\";\n\t\t\tctx.fillRect(0, 0, 1, 1);\n\t\t\tRenderer._whitePixel = c;\n\t\t}\n\t\treturn Renderer._whitePixel;\n\t}\n\n\t/**\n\t * return a reference to the current render target corresponding Context\n\t * @returns {CanvasRenderingContext2D|WebGLRenderingContext|GPUCanvasContext}\n\t */\n\tgetContext() {\n\t\treturn this.renderTarget.context;\n\t}\n\n\t/**\n\t * return the list of supported compressed texture formats.\n\t * The base implementation returns null for all formats (no GPU\n\t * compressed texture support). GPU renderers (WebGL today, WebGPU\n\t * once it lands) override this with the backend-specific extension /\n\t * feature query.\n\t * @returns {Object} an object with one key per extension family, each value is the backend's compressed-texture handle or null\n\t */\n\tgetSupportedCompressedTextureFormats() {\n\t\treturn {\n\t\t\tastc: null,\n\t\t\tbptc: null,\n\t\t\ts3tc: null,\n\t\t\ts3tc_srgb: null,\n\t\t\tpvrtc: null,\n\t\t\tetc1: null,\n\t\t\tetc2: null,\n\t\t};\n\t}\n\n\t/**\n\t * return true if the given compressed texture format is supported\n\t * @param {number} format - a WebGL compressed texture format constant\n\t * @returns {boolean}\n\t */\n\thasSupportedCompressedFormats(format) {\n\t\tconst supportedFormats = this.getSupportedCompressedTextureFormats();\n\t\tfor (const supportedFormat in supportedFormats) {\n\t\t\tconst entry = supportedFormats[supportedFormat];\n\t\t\tif (entry === null || typeof entry === \"undefined\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const extension in entry) {\n\t\t\t\tif (format === entry[extension]) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Begin capturing rendering to an offscreen buffer for post-effect processing.\n\t * Call endPostEffect() after rendering to blit the result to the screen.\n\t * No-op on Canvas renderer.\n\t * @param {Renderable} renderable - the renderable with postEffects to apply\n\t * @returns {boolean} false (Canvas renderer does not support post-effect processing)\n\t * @ignore\n\t */\n\tbeginPostEffect(renderable) {\n\t\t// on Canvas, only set customShader for single-effect fast path\n\t\tconst effects = renderable.postEffects.filter((fx) => {\n\t\t\treturn fx.enabled !== false;\n\t\t});\n\t\tif (effects.length === 1) {\n\t\t\tthis.customShader = effects[0];\n\t\t} else {\n\t\t\tthis.customShader = undefined;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * End post-effect capture and blit the offscreen buffer to the screen\n\t * through the renderable's post-effects.\n\t * No-op on Canvas renderer.\n\t * @param {Renderable} renderable - the renderable with postEffects to apply\n\t * @ignore\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tendPostEffect(renderable) {}\n\n\t/**\n\t * Blit a texture to the screen through a shader effect.\n\t * Draws a screen-aligned quad using the given texture as source\n\t * and the given shader for post-processing (e.g. scanlines, desaturation).\n\t * No-op on Canvas renderer.\n\t * @param {WebGLTexture} source - the source texture to blit\n\t * @param {number} x - destination x position\n\t * @param {number} y - destination y position\n\t * @param {number} width - destination width\n\t * @param {number} height - destination height\n\t * @param {ShaderEffect} shader - the shader effect to apply\n\t * @param {boolean} [keepBlend=false] - if true, keep current blend mode (for sprite compositing)\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tblitEffect(source, x, y, width, height, shader, keepBlend) {}\n\n\t/**\n\t * Sets the viewport for the renderer.\n\t * Defines the affine transformation from normalized device coordinates to window coordinates.\n\t * No-op on Canvas renderer.\n\t * @param {number} [x=0] - x coordinate of the viewport origin\n\t * @param {number} [y=0] - y coordinate of the viewport origin\n\t * @param {number} [w] - width of the viewport (defaults to canvas width)\n\t * @param {number} [h] - height of the viewport (defaults to canvas height)\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tsetViewport(x = 0, y = 0, w, h) {}\n\n\t/**\n\t * Clear the current render target to transparent black (color + stencil).\n\t * Used to prepare FBOs for post-effect capture.\n\t * No-op on Canvas renderer.\n\t */\n\tclearRenderTarget() {}\n\n\t/**\n\t * Enable the scissor test with the given rectangle.\n\t * No-op on Canvas renderer.\n\t * @param {number} x - x coordinate of the scissor rectangle\n\t * @param {number} y - y coordinate of the scissor rectangle\n\t * @param {number} width - width of the scissor rectangle\n\t * @param {number} height - height of the scissor rectangle\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tenableScissor(x, y, width, height) {}\n\n\t/**\n\t * Disable the scissor test, allowing rendering to the full viewport.\n\t * No-op on Canvas renderer.\n\t */\n\tdisableScissor() {}\n\n\t/**\n\t * Enable or disable alpha blending.\n\t * No-op on Canvas renderer (Canvas always blends).\n\t * @param {boolean} enable - true to enable blending, false to disable\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tsetBlendEnabled(enable) {}\n\n\t/**\n\t * returns the current blend mode for this renderer\n\t * @returns {string} blend mode\n\t */\n\tgetBlendMode() {\n\t\treturn this.currentBlendMode;\n\t}\n\n\t/**\n\t * set the current blend mode.\n\t * Subclasses (CanvasRenderer, WebGLRenderer, WebGPURenderer once it\n\t * lands) implement the actual backend logic.\n\t * @param {string} [mode=\"normal\"] - blend mode\n\t * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (GPU renderers only)\n\t */\n\tsetBlendMode(mode = \"normal\") {\n\t\tthis.currentBlendMode = mode;\n\t}\n\n\t/**\n\t * Upload the active scene lights to the lit sprite pipeline.\n\t *\n\t * Called once per camera per frame by `Camera2d.draw()` (after the\n\t * FBO is bound, before the world tree walk fires `Sprite.draw` for\n\t * any normal-mapped sprite). GPU renderers (WebGL today, WebGPU once\n\t * it lands) override this to pack the lights into the lit shader's\n\t * uniform buffers; the Canvas renderer cannot do per-pixel normal-map\n\t * lighting and silently ignores the call. The first time a non-empty\n\t * light list is passed in Canvas mode, a one-shot console warning is\n\t * emitted.\n\t *\n\t * Stage stays renderer-agnostic by passing the raw scene data —\n\t * lights iterable and ambient color — and letting the renderer\n\t * decide how to encode them.\n\t * @param {Iterable<object>} [lights] - active `Light2d` instances; falsy/empty no-ops\n\t * @param {object} [ambient] - ambient lighting color (0..255 RGB)\n\t * @param {number} [translateX=0] - world-to-screen X translate (matches `Camera2d.draw()`)\n\t * @param {number} [translateY=0] - world-to-screen Y translate\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tsetLightUniforms(lights, ambient, translateX, translateY) {\n\t\tif (this._litPipelineWarned || !lights) {\n\t\t\treturn;\n\t\t}\n\t\t// detect \"non-empty\" via `Set.size`, array length, or by iterating\n\t\tconst hasAny =\n\t\t\t(typeof lights.size === \"number\" && lights.size > 0) ||\n\t\t\t(typeof lights.length === \"number\" && lights.length > 0) ||\n\t\t\t(typeof lights[Symbol.iterator] === \"function\" &&\n\t\t\t\t!lights[Symbol.iterator]().next().done);\n\t\tif (hasAny) {\n\t\t\tthis._litPipelineWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: Light2d normal-map lighting requires the WebGL renderer; \" +\n\t\t\t\t\t\"the Canvas fallback renders sprites without per-pixel lighting. \" +\n\t\t\t\t\t\"Switch to `video.WEBGL` or `video.AUTO` to enable the lit pipeline.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Render a `Light2d` instance.\n\t *\n\t * Each renderer implements its own strategy: the WebGL renderer\n\t * draws lights as quads through a shared procedural radial-falloff\n\t * fragment shader (no per-light texture, color and intensity\n\t * encoded in the per-vertex tint so consecutive draws batch); the\n\t * Canvas renderer caches a small `Gradient` config object per\n\t * light in a `WeakMap` (rebuilt only when the light's radii /\n\t * color / intensity change), rasterizes it with `Gradient.toCanvas()`\n\t * into a single shared `CanvasRenderTarget`, and composites the\n\t * result via `drawImage`. The base implementation is a no-op so\n\t * renderers without a lighting path can be polymorphically\n\t * substituted.\n\t *\n\t * Light2d itself is renderer-agnostic — it just calls\n\t * `renderer.drawLight(this)` and relies on the renderer to pick\n\t * the right machinery.\n\t * @param {object} light - the Light2d instance to render\n\t * @see Light2d\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tdrawLight(light) {\n\t\t// base no-op; concrete renderers override\n\t}\n\n\t/**\n\t * Draw a TMX tile layer. Default behavior:\n\t *   - if `layer.canvasRenderer` is set (preRender bake), blit the cached\n\t *     offscreen canvas in a single `drawImage` call;\n\t *   - otherwise delegate to the layer's TMX orientation renderer for\n\t *     the per-tile loop.\n\t *\n\t * `WebGLRenderer` overrides this to add the procedural shader fast\n\t * path on top (when `layer.renderMode === \"shader\"`) and fall through\n\t * to this base behavior for all other layers.\n\t * @param {object} layer - the TMXLayer to draw\n\t * @param {Rect} rect - the visible region in world coords\n\t */\n\tdrawTileLayer(layer, rect) {\n\t\tif (layer.canvasRenderer) {\n\t\t\t// clamp the source rect to the cached canvas bounds — the\n\t\t\t// visible region can start inside the layer (camera scrolled\n\t\t\t// in from the origin) and extend past the layer's right /\n\t\t\t// bottom edge, so the source width must be reduced by\n\t\t\t// `rect.pos.*` to avoid reading past the canvas\n\t\t\tconst width = Math.min(rect.width, layer.width - rect.pos.x);\n\t\t\tconst height = Math.min(rect.height, layer.height - rect.pos.y);\n\t\t\tif (width <= 0 || height <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.drawImage(\n\t\t\t\tlayer.canvasRenderer.getCanvas(),\n\t\t\t\trect.pos.x,\n\t\t\t\trect.pos.y,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\trect.pos.x,\n\t\t\t\trect.pos.y,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tlayer.getRenderer().drawTileLayer(this, layer, rect);\n\t}\n\n\t/**\n\t * Set the current fill & stroke style color.\n\t * By default, or upon reset, the value is set to #000000.\n\t * @param {Color|string|Gradient} color - css color value or a Gradient object\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tsetColor(color) {\n\t\t// implemented by subclasses\n\t}\n\n\t/**\n\t * get the current fill & stroke style color.\n\t * @returns {Color} current global color\n\t */\n\tgetColor() {\n\t\treturn this.currentColor;\n\t}\n\n\t/**\n\t * Create a linear gradient that can be used with {@link Renderer#setColor}.\n\t * @param {number} x0 - x-axis coordinate of the start point\n\t * @param {number} y0 - y-axis coordinate of the start point\n\t * @param {number} x1 - x-axis coordinate of the end point\n\t * @param {number} y1 - y-axis coordinate of the end point\n\t * @returns {Gradient} a Gradient object\n\t */\n\tcreateLinearGradient(x0, y0, x1, y1) {\n\t\treturn new Gradient(\"linear\", [x0, y0, x1, y1]);\n\t}\n\n\t/**\n\t * Create a radial gradient that can be used with {@link Renderer#setColor}.\n\t * @param {number} x0 - x-axis coordinate of the start circle\n\t * @param {number} y0 - y-axis coordinate of the start circle\n\t * @param {number} r0 - radius of the start circle\n\t * @param {number} x1 - x-axis coordinate of the end circle\n\t * @param {number} y1 - y-axis coordinate of the end circle\n\t * @param {number} r1 - radius of the end circle\n\t * @returns {Gradient} a Gradient object\n\t */\n\tcreateRadialGradient(x0, y0, r0, x1, y1, r1) {\n\t\treturn new Gradient(\"radial\", [x0, y0, r0, x1, y1, r1]);\n\t}\n\n\t/**\n\t * Set the line dash pattern for stroke operations.\n\t * @param {number[]} segments - an array of numbers specifying distances to alternately draw a line and a gap. An empty array clears the dash pattern (solid lines).\n\t * @example\n\t * // draw a dashed line\n\t * renderer.setLineDash([10, 5]);\n\t * renderer.strokeLine(0, 0, 100, 0);\n\t * // clear the dash pattern\n\t * renderer.setLineDash([]);\n\t */\n\tsetLineDash(segments) {\n\t\t// skip allocation if the pattern hasn't changed\n\t\tconst current = this.renderState.lineDash;\n\t\tif (\n\t\t\tsegments.length === current.length &&\n\t\t\tsegments.every((v, i) => {\n\t\t\t\treturn v === current[i];\n\t\t\t})\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tthis.renderState.lineDash = segments.filter((v) => {\n\t\t\treturn Number.isFinite(v) && v >= 0;\n\t\t});\n\t}\n\n\t/**\n\t * Get the current line dash pattern.\n\t * @returns {number[]} the current dash pattern\n\t */\n\tgetLineDash() {\n\t\treturn this.renderState.lineDash;\n\t}\n\n\t/**\n\t * return the current global alpha\n\t * @returns {number}\n\t */\n\tglobalAlpha() {\n\t\treturn this.currentColor.alpha;\n\t}\n\n\t/**\n\t * check if the given rect or bounds overlaps with the renderer screen coordinates\n\t * @param {Rect|Bounds} bounds\n\t * @returns {boolean} true if overlaps\n\t */\n\toverlaps(bounds) {\n\t\treturn (\n\t\t\tbounds.left <= this.width &&\n\t\t\tbounds.right >= 0 &&\n\t\t\tbounds.top <= this.height &&\n\t\t\tbounds.bottom >= 0\n\t\t);\n\t}\n\n\t/**\n\t * resizes the system canvas\n\t * @param {number} width - new width of the canvas\n\t * @param {number} height - new height of the canvas\n\t */\n\tresize(width, height) {\n\t\tconst canvas = this.getCanvas();\n\t\tif (width !== canvas.width || height !== canvas.height) {\n\t\t\tcanvas.width = width;\n\t\t\tcanvas.height = height;\n\t\t\tthis.currentScissor[0] = 0;\n\t\t\tthis.currentScissor[1] = 0;\n\t\t\tthis.currentScissor[2] = width;\n\t\t\tthis.currentScissor[3] = height;\n\t\t\t// publish the corresponding event\n\t\t\temit(CANVAS_ONRESIZE, width, height);\n\t\t}\n\t}\n\n\t/**\n\t * enable/disable image smoothing (scaling interpolation) for the current render target\n\t * @param {boolean} [enable=false]\n\t */\n\tsetAntiAlias(enable = false) {\n\t\tif (this.settings.antiAlias !== enable) {\n\t\t\tthis.settings.antiAlias = enable;\n\t\t\tthis.renderTarget.setAntiAlias(enable);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the default texture filter **mode** for this renderer, decoupled\n\t * from {@link Renderer#setAntiAlias} (which controls polygon-edge MSAA on GPU\n\t * backends). Honors the `textureFilter` setting (`\"nearest\"` / `\"linear\"`),\n\t * falling back to the `antiAlias` setting when it's `\"auto\"` (the default).\n\t *\n\t * Backend-neutral on purpose: it returns the mode as a string so each GPU\n\t * backend maps it to its own enum (WebGL `gl.LINEAR` / `gl.NEAREST`, a future\n\t * WebGPU renderer `GPUFilterMode`). The Canvas renderer has no per-texture\n\t * filtering, so this is informational there.\n\t * @returns {\"linear\"|\"nearest\"} the resolved default filter mode\n\t */\n\tgetDefaultTextureFilter() {\n\t\tconst mode = this.settings.textureFilter;\n\t\tif (mode === \"linear\" || mode === \"nearest\") {\n\t\t\treturn mode;\n\t\t}\n\t\t// \"auto\" (or unset) → follow the antiAlias setting\n\t\treturn this.settings.antiAlias ? \"linear\" : \"nearest\";\n\t}\n\n\t/**\n\t * Set the default texture magnification/minification filter at runtime,\n\t * decoupled from {@link Renderer#setAntiAlias}. The base implementation just\n\t * records the setting (the Canvas renderer has no per-texture filtering);\n\t * GPU backends override this to re-apply the filter to live textures.\n\t * @param {\"auto\"|\"nearest\"|\"linear\"} [mode=\"auto\"] - `\"auto\"` follows `antiAlias`\n\t */\n\tsetTextureFilter(mode = \"auto\") {\n\t\tthis.settings.textureFilter = mode;\n\t}\n\n\t/**\n\t * set/change the current projection matrix (GPU renderers only —\n\t * the Canvas renderer applies projection via the 2D context's transform stack).\n\t * @param {Matrix3d} matrix\n\t */\n\tsetProjection(matrix) {\n\t\tthis.projectionMatrix.copy(matrix);\n\t}\n\n\t/**\n\t * stroke the given shape\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} shape - a shape object to stroke\n\t * @param {boolean} [fill=false] - fill the shape with the current color if true\n\t */\n\tstroke(shape, fill) {\n\t\tswitch (shape.type) {\n\t\t\t// RoundRect\n\t\t\tcase \"RoundRect\":\n\t\t\t\tif (fill) {\n\t\t\t\t\tthis.fillRoundRect(\n\t\t\t\t\t\tshape.left,\n\t\t\t\t\t\tshape.top,\n\t\t\t\t\t\tshape.width,\n\t\t\t\t\t\tshape.height,\n\t\t\t\t\t\tshape.radius,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.strokeRoundRect(\n\t\t\t\t\t\tshape.left,\n\t\t\t\t\t\tshape.top,\n\t\t\t\t\t\tshape.width,\n\t\t\t\t\t\tshape.height,\n\t\t\t\t\t\tshape.radius,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t// Rect or Bounds\n\t\t\tcase \"Rectangle\":\n\t\t\tcase \"Bounds\":\n\t\t\t\tif (fill) {\n\t\t\t\t\tthis.fillRect(shape.left, shape.top, shape.width, shape.height);\n\t\t\t\t} else {\n\t\t\t\t\tthis.strokeRect(shape.left, shape.top, shape.width, shape.height);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t// Polygon or Line\n\t\t\tcase \"Polygon\":\n\t\t\tcase \"Line\":\n\t\t\t\tif (fill) {\n\t\t\t\t\tthis.fillPolygon(shape);\n\t\t\t\t} else {\n\t\t\t\t\tthis.strokePolygon(shape);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ellipse\":\n\t\t\t\tif (shape.angle !== 0) {\n\t\t\t\t\tthis.save();\n\t\t\t\t\tthis.translate(shape.pos.x, shape.pos.y);\n\t\t\t\t\tthis.rotate(shape.angle);\n\t\t\t\t\tif (fill) {\n\t\t\t\t\t\tthis.fillEllipse(0, 0, shape.radiusV.x, shape.radiusV.y);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.strokeEllipse(0, 0, shape.radiusV.x, shape.radiusV.y);\n\t\t\t\t\t}\n\t\t\t\t\tthis.restore();\n\t\t\t\t} else if (fill) {\n\t\t\t\t\tthis.fillEllipse(\n\t\t\t\t\t\tshape.pos.x,\n\t\t\t\t\t\tshape.pos.y,\n\t\t\t\t\t\tshape.radiusV.x,\n\t\t\t\t\t\tshape.radiusV.y,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.strokeEllipse(\n\t\t\t\t\t\tshape.pos.x,\n\t\t\t\t\t\tshape.pos.y,\n\t\t\t\t\t\tshape.radiusV.x,\n\t\t\t\t\t\tshape.radiusV.y,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t// Point\n\t\t\tcase \"Point\":\n\t\t\t\tthis.strokePoint(shape.x, shape.y);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"Invalid geometry for fill/stroke\");\n\t\t}\n\t}\n\n\t/**\n\t * fill the given shape\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} shape - a shape object to fill\n\t */\n\tfill(shape) {\n\t\tthis.stroke(shape, true);\n\t}\n\n\t/**\n\t * tint the given image or canvas using the given color\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas} src - the source image to be tinted\n\t * @param {Color|string} color - the color that will be used to tint the image\n\t * @param {string} [mode=\"multiply\"] - the composition mode used to tint the image\n\t * @returns {HTMLCanvasElement|OffscreenCanvas} a new canvas or offscreencanvas (if supported) element representing the tinted image\n\t */\n\ttint(src, color, mode = \"multiply\") {\n\t\tconst attributes = {\n\t\t\tcontext: \"2d\",\n\t\t\toffscreenCanvas: true,\n\t\t\ttransparent: true,\n\t\t\tantiAlias: this.settings.antiAlias,\n\t\t};\n\t\tconst canvasTexture = new CanvasRenderTarget(\n\t\t\tsrc.width,\n\t\t\tsrc.height,\n\t\t\tattributes,\n\t\t);\n\t\tconst context = canvasTexture.context;\n\n\t\tcontext.fillStyle = color instanceof Color ? color.toRGB() : color;\n\t\tcontext.fillRect(0, 0, src.width, src.height);\n\n\t\tcontext.globalCompositeOperation = mode;\n\t\tcontext.drawImage(src, 0, 0);\n\t\tcontext.globalCompositeOperation = \"destination-atop\";\n\t\tcontext.drawImage(src, 0, 0);\n\n\t\treturn canvasTexture.canvas;\n\t}\n\n\t/**\n\t * Clip the region anything drawn afterwards is confined to.\n\t * Implemented by subclasses.\n\t * @param {number} x - x position of the clipping rectangle\n\t * @param {number} y - y position of the clipping rectangle\n\t * @param {number} width - width of the clipping rectangle\n\t * @param {number} height - height of the clipping rectangle\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tclipRect(x, y, width, height) {}\n\n\t/**\n\t * Reset the current transform back to the identity matrix.\n\t * Implemented by subclasses.\n\t */\n\tresetTransform() {}\n\n\t/**\n\t * Push the current transform / alpha / clip state onto an internal\n\t * stack. Pair with {@link Renderer#restore}. Implemented by subclasses.\n\t */\n\tsave() {}\n\n\t/**\n\t * Pop the topmost saved state from the stack, restoring transform,\n\t * alpha and clip. Pair with {@link Renderer#save}. Implemented by\n\t * subclasses.\n\t */\n\trestore() {}\n\n\t/**\n\t * Translate the current transform by `(x, y)`. Implemented by subclasses.\n\t * @param {number} x - horizontal translation in pixels\n\t * @param {number} y - vertical translation in pixels\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\ttranslate(x, y) {}\n\n\t/**\n\t * Rotate the current transform by `angle` (radians). Implemented by subclasses.\n\t * @param {number} angle - rotation angle in radians\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\trotate(angle) {}\n\n\t/**\n\t * Scale the current transform by `(x, y)`. Implemented by subclasses.\n\t * @param {number} x - horizontal scale factor\n\t * @param {number} y - vertical scale factor\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tscale(x, y) {}\n\n\t/**\n\t * Set the renderer global alpha (0..1) for subsequent draw calls.\n\t * Implemented by subclasses.\n\t * @param {number} alpha - opacity value in the [0..1] range\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tsetGlobalAlpha(alpha) {}\n\n\t/**\n\t * Fill an axis-aligned rectangle with the current color.\n\t * Implemented by subclasses.\n\t * @param {number} x - left edge in viewport pixels\n\t * @param {number} y - top edge in viewport pixels\n\t * @param {number} width - rectangle width\n\t * @param {number} height - rectangle height\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tfillRect(x, y, width, height) {}\n\n\t/**\n\t * Stroke (outline) an axis-aligned rectangle with the current color\n\t * at the current {@link Renderer#lineWidth}. Implemented by subclasses.\n\t * @param {number} x - left edge in viewport pixels\n\t * @param {number} y - top edge in viewport pixels\n\t * @param {number} width - rectangle width\n\t * @param {number} height - rectangle height\n\t * @param {boolean} [fill=false] - also fill the rectangle\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tstrokeRect(x, y, width, height, fill) {}\n\n\t/**\n\t * Fill an axis-aligned ellipse with the current color.\n\t * Implemented by subclasses.\n\t * @param {number} x - ellipse center X in viewport pixels\n\t * @param {number} y - ellipse center Y in viewport pixels\n\t * @param {number} w - horizontal radius\n\t * @param {number} h - vertical radius\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tfillEllipse(x, y, w, h) {}\n\n\t/**\n\t * Stroke (outline) an axis-aligned ellipse with the current color\n\t * at the current {@link Renderer#lineWidth}. Implemented by subclasses.\n\t * @param {number} x - ellipse center X in viewport pixels\n\t * @param {number} y - ellipse center Y in viewport pixels\n\t * @param {number} w - horizontal radius\n\t * @param {number} h - vertical radius\n\t * @param {boolean} [fill=false] - also fill the ellipse\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tstrokeEllipse(x, y, w, h, fill) {}\n\n\t/**\n\t * Draw a straight line between two points with the current color\n\t * at the current {@link Renderer#lineWidth}. Implemented by subclasses.\n\t * @param {number} startX - start X in viewport pixels\n\t * @param {number} startY - start Y in viewport pixels\n\t * @param {number} endX - end X in viewport pixels\n\t * @param {number} endY - end Y in viewport pixels\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tstrokeLine(startX, startY, endX, endY) {}\n\n\t/**\n\t * Draw an image or sub-image into the framebuffer.\n\t * Implemented by subclasses (`CanvasRenderer` / `WebGLRenderer` /\n\t * future `WebGPURenderer`).\n\t *\n\t * The 9-argument signature shown here is the canonical form used\n\t * throughout the engine; the concrete renderers also accept the\n\t * shorter `drawImage(image, dx, dy)` and `drawImage(image, dx, dy,\n\t * dWidth, dHeight)` variants from the HTML Canvas2D spec.\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - the source image\n\t * @param {number} sx - source rectangle left\n\t * @param {number} sy - source rectangle top\n\t * @param {number} sw - source rectangle width\n\t * @param {number} sh - source rectangle height\n\t * @param {number} dx - destination left\n\t * @param {number} dy - destination top\n\t * @param {number} dw - destination width\n\t * @param {number} dh - destination height\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tdrawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {}\n\n\t/**\n\t * A mask limits rendering elements to the shape and position of the given mask object.\n\t * So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.\n\t * Mask are not preserved through renderer context save and restore.\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] - the shape defining the mask to be applied\n\t * @param {boolean} [invert=false] - either the given shape should define what is visible (default) or the opposite\n\t */\n\tsetMask() {}\n\n\t/**\n\t * disable (remove) the rendering mask set through setMask.\n\t * @see Renderer#setMask\n\t */\n\tclearMask() {}\n\n\t/**\n\t * set a coloring tint for sprite based renderables\n\t * @param {Color} tint - the tint color\n\t * @param {number} [alpha] - an alpha value to be applied to the tint\n\t */\n\tsetTint(tint, alpha = tint.alpha) {\n\t\t// global tint color\n\t\tthis.currentTint.copy(tint);\n\t\tthis.currentTint.alpha *= alpha;\n\t}\n\n\t/**\n\t * clear the rendering tint set through setTint.\n\t * @see Renderer#setTint\n\t */\n\tclearTint() {\n\t\t// reset to default\n\t\tthis.currentTint.setFloat(1.0, 1.0, 1.0, 1.0);\n\t}\n\n\t/**\n\t * Set the current per-renderable depth value. GPU batchers (WebGL\n\t * today, WebGPU once it lands) push it into the vertex stream as the\n\t * `z` component of each vertex — a no-op under the default\n\t * orthographic projection, used by perspective (Camera3d) to scale\n\t * and parallax sprites by distance.\n\t *\n\t * Typically called automatically by `Renderable.preDraw` from the\n\t * renderable's `depth` property. User code only needs to call this\n\t * directly when emitting draw calls outside of a `Renderable.draw()`\n\t * — e.g. from a custom `Container.draw()` override.\n\t *\n\t * Honored by the save/restore stack like `setTint` / `setColor`.\n\t * @param {number} depth - the depth value to set\n\t * @see Renderable#depth\n\t */\n\tsetDepth(depth) {\n\t\t// A sprite's depth rides into clip-space as `aVertex.z`. Under an\n\t\t// orthographic (2D) projection that z is a painter's sort key only — the\n\t\t// world is CPU-sorted on `pos.z` — and must stay out of clip-space, or a\n\t\t// large sort value (e.g. a `baseZ + pos.y` Y-sort, or `autoDepth` on a big\n\t\t// world) clips the sprite against near/far and it silently vanishes. A\n\t\t// perspective projection (Camera3d) genuinely uses z for parallax + depth,\n\t\t// so pass it through. Perspective ⇔ the projection's w-from-z term is\n\t\t// non-zero (`val[11] === -1`); orthographic leaves it 0. Gated here (once\n\t\t// per renderable) so the per-quad batcher hot path is untouched. Mesh\n\t\t// geometry z is unaffected — only this sprite/quad sort key is zeroed.\n\t\t// val[11] is the projection's z→w (perspective-divide) term: 0 ⇒ orthographic, non-zero ⇒ perspective.\n\t\tthis.currentDepth = this.projectionMatrix.val[11] !== 0 ? depth : 0;\n\t}\n\n\t/**\n\t * creates a Blob object representing the last rendered frame\n\t * @param {string} [type=\"image/png\"] - A string indicating the image format\n\t * @param {number} [quality] - A Number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range.\n\t * @returns {Promise} A Promise returning a Blob object representing the last rendered frame\n\t * @example\n\t * renderer.convertToBlob().then((blob) => console.log(blob));\n\t */\n\ttoBlob(type = \"image/png\", quality) {\n\t\treturn this.renderTarget.toBlob(type, quality);\n\t}\n\n\t/**\n\t * creates an ImageBitmap object of the last frame rendered\n\t * (not supported by standard Canvas)\n\t * @param {string} [type=\"image/png\"] - A string indicating the image format\n\t * @param {number} [quality] - A Number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range.\n\t * @returns {Promise} A Promise returning an ImageBitmap.\n\t * @example\n\t * renderer.transferToImageBitmap().then((image) => console.log(image));\n\t */\n\ttoImageBitmap(type = \"image/png\", quality) {\n\t\treturn this.renderTarget.toImageBitmap(type, quality);\n\t}\n\n\t/**\n\t * returns a data URL containing a representation of the last frame rendered\n\t * @param {string} [type=\"image/png\"] - A string indicating the image format\n\t * @param {number} [quality] - A Number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range.\n\t * @returns {Promise} A Promise returning a string containing the requested data URL.\n\t * @example\n\t * renderer.toDataURL().then((dataURL) => console.log(dataURL));\n\t */\n\ttoDataURL(type = \"image/png\", quality) {\n\t\treturn this.renderTarget.toDataURL(type, quality);\n\t}\n\n\t/**\n\t * Capture the current frame — everything drawn to the active framebuffer /\n\t * canvas so far — into a {@link Texture2d}, on the GPU where possible (no\n\t * `readPixels` round-trip). The fourth member of the {@link Renderer#toDataURL}\n\t * / {@link Renderer#toBlob} / {@link Renderer#toImageBitmap} family — \"the\n\t * current frame as X\" — and the only one whose result can stay GPU-resident,\n\t * ready to feed a shader as a screen texture (water refraction, heat haze,\n\t * frosted glass).\n\t *\n\t * Abstract on the base renderer: the concrete backends implement it —\n\t * {@link WebGLRenderer#toFrameTexture} (a `copyTexImage2D` into a live GPU\n\t * texture, bound as an extra sampler via {@link ShaderEffect#setTexture}) and\n\t * {@link CanvasRenderer#toFrameTexture} (an offscreen-canvas copy, for family\n\t * parity — custom shaders don't run under Canvas).\n\t * @param {object} [options]\n\t * @param {Texture2d|null} [options.target] - controls the destination: omit\n\t *   for the shared renderer slot (default); pass a capture previously\n\t *   returned by this method to refresh it in place; pass `null` to mint a\n\t *   fresh, caller-owned capture\n\t * @param {Bounds|{x: number, y: number, width: number, height: number}} [options.region] - capture\n\t *   only this sub-region of the frame — a {@link Bounds} (or any\n\t *   `{x, y, width, height}`); from a {@link Rect} pass `rect.getBounds()`.\n\t *   NOTE the origin differs by backend: WebGL uses framebuffer coords\n\t *   (**bottom-left** origin), Canvas uses **top-left** — relevant under\n\t *   `video.AUTO`.\n\t * @returns {Texture2d} a texture holding the captured frame\n\t * @example\n\t * effect.setTexture(\"uScene\", renderer.toFrameTexture());\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\ttoFrameTexture(options) {\n\t\tthrow new Error(\"toFrameTexture() is not implemented by this renderer\");\n\t}\n}\n\n// Backing field for `Renderer.getWhitePixel()` — declared outside the\n// class body so it's initialized to null at module load (static class\n// fields aren't universally supported in our transpile target).\nRenderer._whitePixel = null;\n","/**\n * A simple multimap that stores multiple values per key using arrays.\n * @ignore\n */\nclass ArrayMultimap {\n\tconstructor() {\n\t\tthis._map = new Map();\n\t}\n\tput(key, value) {\n\t\tconst arr = this._map.get(key);\n\t\tif (arr) {\n\t\t\tarr.push(value);\n\t\t} else {\n\t\t\tthis._map.set(key, [value]);\n\t\t}\n\t}\n\tget(key) {\n\t\treturn this._map.get(key) || [];\n\t}\n\thas(key) {\n\t\treturn this._map.has(key);\n\t}\n\tdelete(key) {\n\t\tthis._map.delete(key);\n\t}\n\tforEach(callback) {\n\t\tthis._map.forEach((values, key) => {\n\t\t\tfor (const value of values) {\n\t\t\t\tcallback(value, key);\n\t\t\t}\n\t\t});\n\t}\n\tclear() {\n\t\tthis._map.clear();\n\t}\n}\n\nexport { ArrayMultimap };\n","/**\n * Backend-neutral texture slot assignment.\n *\n * Both GPU backends solve the same problem — \"many textures, one draw call\" —\n * by giving each texture a small integer slot that the fragment shader selects\n * on per-vertex. Only the *resource* differs: a WebGL texture unit, or an entry\n * in a WebGPU material bind group. The assignment policy (who gets a slot, what\n * happens when they run out, what is dropped) is identical, and used to be\n * written twice — `TextureCache.allocateTextureUnit` on one side,\n * `WebGPUQuadBatcher.segmentSlotFor` on the other — which is how the two drifted.\n *\n * This class owns that policy and nothing else. It never touches a GL unit or a\n * bind group; the caller supplies the binding through the callbacks. That keeps\n * it pure, so the same test suite proves both backends behave alike.\n * @ignore\n */\nexport class TextureSlotTable {\n\t/**\n\t * @param {object} options - table configuration\n\t * @param {number} options.capacity - number of slots, `[0, capacity)`\n\t * @param {Function} [options.onOverflow] - called when a new key arrives with\n\t * no slot free. The caller must turn the pending work into GPU work (flush)\n\t * before the table reassigns slots out from under it.\n\t * @param {Function} [options.onEvict] - called with each slot index as it is\n\t * dropped, so the caller can forget its binding for that slot\n\t * @param {Function} [options.isReserved] - optional predicate; a slot it\n\t * approves is held out of assignment entirely (WebGL parks `ShaderEffect`\n\t * extra samplers on high units this way). Reservations survive a reset.\n\t * @ignore\n\t */\n\tconstructor({ capacity, onOverflow, onEvict, isReserved } = {}) {\n\t\t/** @type {Map<string, number>} key → slot */\n\t\tthis.slots = new Map();\n\t\t/** @type {Set<number>} occupied slot indices */\n\t\tthis.used = new Set();\n\t\tthis.capacity = capacity ?? 0;\n\t\tthis.onOverflow = onOverflow;\n\t\tthis.onEvict = onEvict;\n\t\tthis.isReserved = isReserved;\n\t}\n\n\t/**\n\t * How many slots are currently taken.\n\t *\n\t * Occupancy, NOT the key map: slots claimed through {@link claim} carry no\n\t * key at all (the WebGL cache allocates that way), so counting keys would\n\t * report an empty table while every unit was in use.\n\t * @returns {number} the number of occupied slots\n\t * @ignore\n\t */\n\tget size() {\n\t\treturn this.used.size;\n\t}\n\n\t/**\n\t * Resize the table. Shrinking evicts every assignment at or above the new\n\t * capacity; growing keeps everything. Called on context restore / device\n\t * change, where the resolved limit can differ from the previous one.\n\t * @param {number} capacity - the new slot count\n\t * @ignore\n\t */\n\tsetCapacity(capacity) {\n\t\tthis.capacity = capacity;\n\t\tfor (const [key, slot] of this.slots) {\n\t\t\tif (slot >= capacity) {\n\t\t\t\tthis.slots.delete(key);\n\t\t\t}\n\t\t}\n\t\t// occupancy is the authority, not the key map: slots taken through\n\t\t// `claim()` have no key at all (the WebGL cache allocates that way), so\n\t\t// iterating `slots` alone would leave them marked used above the new\n\t\t// capacity and every later allocation would spuriously overflow\n\t\tfor (const slot of [...this.used]) {\n\t\t\tif (slot >= capacity) {\n\t\t\t\tthis.used.delete(slot);\n\t\t\t\tthis.onEvict?.(slot);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * The slot this key already holds, without assigning one. Use when a miss\n\t * should not trigger an overflow (probing, debug, bookkeeping).\n\t * @param {string} key - the slot key\n\t * @returns {number|undefined} the assigned slot, or `undefined`\n\t * @ignore\n\t */\n\tpeek(key) {\n\t\treturn this.slots.get(key);\n\t}\n\n\t/**\n\t * The lowest assignable slot, or `-1` when every one is taken or reserved.\n\t * Bounded by `capacity` on purpose: a caller that reserves every slot must\n\t * get an answer rather than an infinite scan.\n\t * @returns {number} a free slot index, or -1\n\t * @ignore\n\t */\n\tfreeSlot() {\n\t\tfor (let slot = 0; slot < this.capacity; slot++) {\n\t\t\tif (!this.used.has(slot) && this.isReserved?.(slot) !== true) {\n\t\t\t\treturn slot;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Resolve a key to its slot, assigning one if needed.\n\t *\n\t * On exhaustion this calls `onOverflow` (the caller flushes) and then drops\n\t * every assignment, so the incoming key starts a fresh set — the behaviour\n\t * both backends already had. The eviction step is deliberately the only\n\t * policy here: replacing this wipe with something finer (see #1586) changes\n\t * both backends at once, which is the point of sharing it.\n\t * @param {string} key - identifies the texture *and* the sampling state it\n\t * needs, since two draws of one image under different filter/wrap settings\n\t * cannot share a slot\n\t * @returns {number} the slot to write into the vertex stream\n\t * @ignore\n\t */\n\tslotFor(key) {\n\t\tconst existing = this.slots.get(key);\n\t\tif (existing !== undefined) {\n\t\t\treturn existing;\n\t\t}\n\t\tconst slot = this.claim();\n\t\tthis.slots.set(key, slot);\n\t\treturn slot;\n\t}\n\n\t/**\n\t * Take a slot without associating a key with it.\n\t *\n\t * The WebGL cache resolves textures to units through its own source-keyed\n\t * index (a source can hold one unit per wrap mode it was sampled with), so\n\t * it needs the *policy* — free-slot search honoring reservations, and the\n\t * flush-then-evict behaviour on exhaustion — without the key map. WebGPU's\n\t * batcher keys directly and goes through {@link slotFor}, which is this plus\n\t * the mapping. Both therefore overflow on the same rule.\n\t * @returns {number} the claimed slot\n\t * @ignore\n\t */\n\tclaim() {\n\t\tlet slot = this.freeSlot();\n\t\tif (slot < 0) {\n\t\t\t// full: let the caller draw what is pending BEFORE anything is\n\t\t\t// reassigned, or those vertices would sample the wrong texture\n\t\t\tthis.onOverflow?.();\n\t\t\tthis.reset();\n\t\t\tslot = this.freeSlot();\n\t\t\tif (slot < 0) {\n\t\t\t\t// every slot is reserved (or capacity is 0) — there is no\n\t\t\t\t// correct answer, and silently returning 0 would alias onto a\n\t\t\t\t// reserved texture\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`TextureSlotTable: no assignable slot (capacity ${this.capacity}, all reserved)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis.used.add(slot);\n\t\treturn slot;\n\t}\n\n\t/**\n\t * Drop a single assignment, freeing its slot. Reservations are untouched —\n\t * they are owned by whoever reserved them, not by the table.\n\t * @param {string} key - the slot key to release\n\t * @returns {boolean} whether the key held a slot\n\t * @ignore\n\t */\n\trelease(key) {\n\t\tconst slot = this.slots.get(key);\n\t\tif (slot === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.slots.delete(key);\n\t\tthis.used.delete(slot);\n\t\tthis.onEvict?.(slot);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Drop every assignment. `onEvict` fires once per slot that was live, so a\n\t * caller tracking bindings per slot can forget exactly those.\n\t * @ignore\n\t */\n\treset() {\n\t\tif (this.onEvict !== undefined) {\n\t\t\tfor (const slot of this.used) {\n\t\t\t\tthis.onEvict(slot);\n\t\t\t}\n\t\t}\n\t\tthis.slots.clear();\n\t\tthis.used.clear();\n\t}\n}\n","import * as audio from \"./../audio/audio.ts\";\nimport { warning } from \"../lang/console.js\";\nimport state from \"./../state/state.ts\";\nimport {\n\temit,\n\tLOADER_COMPLETE,\n\tLOADER_ERROR,\n\tLOADER_PROGRESS,\n} from \"../system/event.ts\";\nimport { getBasename } from \"../utils/file.ts\";\nimport {\n\tbinList,\n\tfontList,\n\tgltfList,\n\timgList,\n\tjsonList,\n\tmtlList,\n\tobjList,\n\tshaderList,\n\ttmxList,\n\tvideoList,\n} from \"./cache.js\";\nimport { preloadAseprite } from \"./parsers/aseprite.js\";\nimport { preloadBinary } from \"./parsers/binary.js\";\nimport { preloadFontFace } from \"./parsers/fontface.js\";\nimport { preloadGLTF } from \"./parsers/gltf.js\";\nimport { preloadImage } from \"./parsers/image.js\";\nimport { preloadJSON } from \"./parsers/json.js\";\nimport { preloadMTL } from \"./parsers/mtl.js\";\nimport { preloadOBJ } from \"./parsers/obj.js\";\nimport { preloadJavascript } from \"./parsers/script.js\";\nimport { preloadShader } from \"./parsers/shader.js\";\nimport { preloadTMX } from \"./parsers/tmx.js\";\nimport { preloadVideo } from \"./parsers/video.js\";\n\n/**\n * a small class to manage loading of stuff and manage resources\n * @namespace loader\n */\n\n//  to enable/disable caching\nexport let nocache = \"\";\n\n/**\n * @type {Object.<string, string>}\n */\nexport const baseURL = {};\n\n/**\n * crossOrigin attribute to configure the CORS requests for Image and Video data element.\n * By default (that is, when the attribute is not specified), CORS is not used at all.\n * The \"anonymous\" keyword means that there will be no exchange of user credentials via cookies,\n * client-side SSL certificates or HTTP authentication as described in the Terminology section of the CORS specification.<br>\n * @type {string}\n * @name crossOrigin\n * @memberof loader\n * @default undefined\n * @see {@link setOptions}\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes}\n * @example\n *  // allow for cross-origin texture loading\n * me.loader.crossOrigin = \"anonymous\";\n *\n * // set all resources to be loaded\n * me.loader.preload(game.resources, () => this.loaded());\n */\nexport let crossOrigin;\n\n/**\n * indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies,\n * authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.\n * @public\n * @type {boolean}\n * @name withCredentials\n * @see {@link setOptions}\n * @default false\n * @memberof loader\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials}\n * @example\n *  // enable withCredentials\n * me.loader.withCredentials = true;\n *\n * // set all resources to be loaded\n * me.loader.preload(game.resources, () => this.loaded());\n */\nexport let withCredentials = false;\n\n/**\n * enable the nocache mechanism\n * @ignore\n */\nexport function setNocache(enable = false) {\n\tnocache = enable ? \"?\" + ~~(Math.random() * 10000000) : \"\";\n}\n\n/**\n * Sets the options for the loader.\n * @memberof loader\n * @param {Object} options - The options to set.\n * @param {string} [options.crossOrigin] - The crossOrigin attribute to configure the CORS requests for Image and Video data element.\n * @param {boolean} [options.nocache] - Enable or disable the nocache mechanism.\n * @param {boolean} [options.withCredentials] - Indicates whether or not cross-site Access-Control requests should be made using credentials.\n * @example\n * // Set the crossOrigin attribute to \"anonymous\"\n * me.loader.setOptions({ crossOrigin: \"anonymous\" });\n *\n * // Enable the nocache mechanism\n * me.loader.setOptions({ nocache: true });\n *\n * // Enable withCredentials\n * me.loader.setOptions({ withCredentials: true });\n * @category Assets\n */\nexport function setOptions(options) {\n\tif (options.crossOrigin !== undefined) {\n\t\tcrossOrigin = options.crossOrigin;\n\t}\n\tif (options.nocache !== undefined) {\n\t\tsetNocache(options.nocache);\n\t}\n\tif (options.withCredentials !== undefined) {\n\t\twithCredentials = options.withCredentials;\n\t}\n}\n\n/**\n * change the default baseURL for the given asset type.<br>\n * (this will prepend the asset URL and must finish with a '/')\n * @name setBaseURL\n * @memberof loader\n * @public\n * @param {string} type  - \"*\", \"audio\", \"video\", \"binary\", \"image\", \"json\", \"js\", \"tmx\", \"tsx\", \"fontface\", \"aseprite\", \"shader\", \"obj\", \"mtl\", \"gltf\", \"glb\"\n * @param {string} [url=\"./\"] - default base URL\n * @example\n * // change the base URL relative address for audio assets\n * me.loader.setBaseURL(\"audio\", \"data/audio/\");\n * // change the base URL absolute address for all object types\n * me.loader.setBaseURL(\"*\", \"http://myurl.com/\")\n * @category Assets\n */\nexport function setBaseURL(type, url = \"./\") {\n\tif (type !== \"*\") {\n\t\tbaseURL[type] = url;\n\t} else {\n\t\t// \"wildcards\"\n\t\tbaseURL[\"audio\"] = url;\n\t\tbaseURL[\"video\"] = url;\n\t\tbaseURL[\"binary\"] = url;\n\t\tbaseURL[\"image\"] = url;\n\t\tbaseURL[\"json\"] = url;\n\t\tbaseURL[\"js\"] = url;\n\t\tbaseURL[\"tmx\"] = url;\n\t\tbaseURL[\"tsx\"] = url;\n\t\tbaseURL[\"fontface\"] = url;\n\t\tbaseURL[\"aseprite\"] = url;\n\t\tbaseURL[\"shader\"] = url;\n\t\t// deliberately NOT part of the wildcard: 3D asset types resolve\n\t\t// their internal references (map_Kd textures, glTF buffers) relative\n\t\t// to their own file, so a global prefix would double-prefix them —\n\t\t// set them individually when needed\n\t}\n}\n\n/**\n * onload callback\n * @default undefined\n * @memberof loader\n * @type {function}\n * @deprecated since 18.2.0 - Use the {@link event.LOADER_COMPLETE} event or the `onloadcb` parameter of {@link loader.preload} instead.\n * @example\n * // use the LOADER_COMPLETE event instead\n * me.event.on(me.event.LOADER_COMPLETE, this.loaded.bind(this));\n */\nexport let onload;\n\n/**\n * onProgress callback<br>\n * each time a resource is loaded, the loader will fire the specified function,\n * giving the actual progress [0 ... 1], as argument, and an object describing the resource loaded\n * @default undefined\n * @memberof loader\n * @type {function}\n * @deprecated since 18.2.0 - Use the {@link event.LOADER_PROGRESS} event instead.\n * @example\n * // use the LOADER_PROGRESS event instead\n * me.event.on(me.event.LOADER_PROGRESS, (progress, resource) => this.updateProgress(progress, resource));\n */\nexport let onProgress; // eslint-disable-line no-unassigned-vars\n\n/**\n * onError callback<br>\n * each time a resource loading is failed, the loader will fire the specified function giving the actual asset as argument.\n * @default undefined\n * @memberof loader\n * @type {function}\n * @deprecated since 18.2.0 - Use the {@link event.LOADER_ERROR} event instead.\n * @example\n * // use the LOADER_ERROR event instead\n * me.event.on(me.event.LOADER_ERROR, (resource) => this.loaderError(resource));\n */\nexport let onError;\n\n/**\n * list of parser function for supported format type\n */\nconst parsers = new Map();\n\n/**\n * keep track if parsers were registered\n */\nlet parserInitialized = false;\n\n// flag to check loading status\nlet resourceCount = 0;\nlet loadCount = 0;\n\n/**\n * Assets uploaded with an error\n */\nconst failureLoadedAssets = {};\n\n/**\n * init all supported parsers\n * @ignore\n */\nfunction initParsers() {\n\tsetParser(\"binary\", preloadBinary);\n\tsetParser(\"image\", preloadImage);\n\tsetParser(\"json\", preloadJSON);\n\tsetParser(\"js\", preloadJavascript);\n\tsetParser(\"tmx\", preloadTMX);\n\tsetParser(\"tsx\", preloadTMX);\n\tsetParser(\"audio\", audio.load);\n\tsetParser(\"fontface\", preloadFontFace);\n\tsetParser(\"video\", preloadVideo);\n\tsetParser(\"obj\", preloadOBJ);\n\tsetParser(\"mtl\", preloadMTL);\n\tsetParser(\"gltf\", preloadGLTF);\n\tsetParser(\"glb\", preloadGLTF);\n\tsetParser(\"aseprite\", preloadAseprite);\n\tsetParser(\"shader\", preloadShader);\n\tparserInitialized = true;\n}\n\n/**\n * Complete loading: invoke the callback and emit the LOADER_COMPLETE event.\n * @param {Function} onloadcb - the completion callback\n * @ignore\n */\nfunction completeLoading(onloadcb) {\n\tconst callback = onloadcb || onload;\n\tif (typeof callback === \"function\") {\n\t\tcallback();\n\t}\n\t// always signal completion — with the Promise form of preload() a callback\n\t// is optional (you can `await` instead), so a missing callback is no longer\n\t// an error; the loading screen / consumers react to LOADER_COMPLETE.\n\temit(LOADER_COMPLETE);\n}\n\n/**\n * just increment the number of already loaded resources\n * @ignore\n */\nfunction onResourceLoaded(res) {\n\tdelete failureLoadedAssets[res.src];\n\t// increment the loading counter\n\tloadCount++;\n\n\t// currrent progress\n\tconst progress = loadCount / resourceCount;\n\n\t// call callback if defined\n\tif (typeof onProgress === \"function\") {\n\t\t// pass the load progress in percent, as parameter\n\t\tonProgress(progress, res);\n\t}\n\temit(LOADER_PROGRESS, progress, res);\n}\n\n/**\n * on error callback for image loading\n * @param {Asset} asset - asset that loaded with failure\n * @ignore\n */\nfunction onLoadingError(res) {\n\tfailureLoadedAssets[res.src] = res;\n\tif (this.onError) {\n\t\tthis.onError(res);\n\t}\n\temit(LOADER_ERROR, res);\n\tthrow new Error(\"Failed loading resource \" + res.src);\n}\n\n/**\n * an asset definition to be used with the loader\n * @typedef {object} Asset\n * @memberof loader\n * @property {string} name - name of the asset\n * @property {string} type  - the type of the asset (\"audio\"|\"binary\"|\"image\"|\"json\"|\"js\"|\"tmx\"|\"tmj\"|\"tsx\"|\"tsj\"|\"fontface\"|\"video\"|\"aseprite\"|\"shader\"|\"obj\"|\"mtl\"|\"gltf\"|\"glb\")\n * @property {string|string[]} [src]  - path and/or file name of the resource (for audio assets only the path is required).\n * For image assets, an array of sources can be provided as a fallback chain (e.g. compressed texture formats by priority, with a PNG fallback).\n * The loader will try each source in order and use the first one that loads successfully.\n * @property {string} [data]  - inline content if not provided through a src url: TMX data for \"tmx\" assets, GLSL source (the ShaderEffect fragment-body convention) for \"shader\" assets\n * @property {boolean} [stream=false] - Set to true to not to wait for large audio or video file to be downloaded before playing.\n * @property {boolean} [autoplay=false] - Set to true to automatically start playing audio or video when loaded or added to a scene (using autoplay might require user iteraction to enable it)\n * @property {boolean} [loop=false] - Set to true to automatically loop the audio or video when playing\n * @see {@link preload}\n * @see {@link load}\n * @example\n *   // PNG tileset\n *   {name: \"tileset-platformer\", type: \"image\",  src: \"data/map/tileset.png\"}\n *   // PNG packed texture\n *   {name: \"texture\", type:\"image\", src: \"data/gfx/texture.png\"}\n *   // PNG base64 encoded image\n *   {name: \"texture\", type:\"image\", src: \"data:image/png;base64,iVBORw0KAAAQAAAAEACA...\"}\n *   // compressed texture with fallback chain (tries each source in order until one succeeds)\n *   {name: \"terrain\", type:\"image\", src: [\"data/gfx/terrain.astc.ktx\", \"data/gfx/terrain.dds\", \"data/gfx/terrain.png\"]}\n *   // TSX file\n *   {name: \"meta_tiles\", type: \"tsx\", src: \"data/map/meta_tiles.tsx\"}\n *   // TMX level (XML & JSON)\n *   {name: \"map1\", type: \"tmx\", src: \"data/map/map1.json\"}\n *   {name: \"map2\", type: \"tmx\", src: \"data/map/map2.tmx\"}\n *   {name: \"map3\", type: \"tmx\", format: \"json\", data: {\"height\":15,\"layers\":[...],\"tilewidth\":32,\"version\":1,\"width\":20}}\n *   {name: \"map4\", type: \"tmx\", format: \"xml\", data: {xml representation of tmx}}\n *   // audio resources\n *   {name: \"bgmusic\", type: \"audio\",  src: \"data/audio/\"}\n *   {name: \"cling\",   type: \"audio\",  src: \"data/audio/\"}\n *   // base64 encoded audio resources\n *   {name: \"band\",   type: \"audio\",  src: \"data:audio/wav;base64,...\"}\n *   // binary file\n *   {name: \"ymTrack\", type: \"binary\", src: \"data/audio/main.ym\"}\n *   // JSON file (used for texturePacker)\n *   {name: \"texture\", type: \"json\", src: \"data/gfx/texture.json\"}\n *   // JavaScript file\n *   {name: \"plugin\", type: \"js\", src: \"data/js/plugin.js\"}\n *   // Font Face\n *   { name: \"'kenpixel'\", type: \"fontface\",  src: \"data/font/kenvector_future.woff2\" }\n *   // video resources\n *   {name: \"intro\", type: \"video\",  src: \"data/video/\"}\n *   // 3D assets: Wavefront OBJ model + MTL material library\n *   {name: \"ship\",  type: \"obj\", src: \"data/models/ship.obj\"}\n *   {name: \"ship\",  type: \"mtl\", src: \"data/models/ship.mtl\"}\n *   // glTF / GLB scene (auto-registers with the level director)\n *   {name: \"diorama\", type: \"glb\", src: \"data/scenes/diorama.glb\"}\n *   // shader assets: an effect body, or a complete dual-backend program\n *   {name: \"flash\", type: \"shader\", src: \"shaders/flash.frag\"}\n *   {name: \"toon\",  type: \"shader\", src: {vertex: \"shaders/toon.vert\", fragment: \"shaders/toon.frag\", wgsl: \"shaders/toon.wgsl\"}}\n */\n\n/**\n * specify a parser/preload function for the given asset type\n * @memberof loader\n * @param {string} type - asset type\n * @param {function} parserFn - parser function\n * @see {@link Asset.type}\n * @example\n * // specify a custom function for \"abc\" format\n * function customAbcParser(data, onload, onerror) {\n *    // preload and do something with the data\n *    let parsedData = doSomething(data);\n *    // when done, call the onload callback with the parsed data\n *    onload(parsedData);\n *    // in case of error, call the onerror callback\n *    onerror();\n *    // return the amount of asset parsed\n *    return 1\n * }\n * // set the parser for the custom format\n * loader.setParser(\"abc\", customAbcParser);\n * @category Assets\n */\nexport function setParser(type, parserFn) {\n\tif (typeof parserFn !== \"function\") {\n\t\tthrow new Error(\"invalid parser function for \" + type);\n\t}\n\n\tif (typeof parsers.get(type) !== \"undefined\") {\n\t\twarning(\"overriding parser for \" + type + \" format\");\n\t}\n\n\tparsers.set(type, parserFn);\n}\n\n/**\n * set all the specified game assets to be preloaded.\n * @memberof loader\n * @param {Asset[]} assets - list of assets to load\n * @param {Function} [onloadcb=loader.onload] - function to be called when all resources are loaded\n * @param {boolean} [switchToLoadState=true] - automatically switch to the loading screen\n * @returns {Promise<void>} resolves once every asset has loaded (rejects on a\n * load failure). The `onloadcb` callback is still invoked on success, so both\n * the callback and `await` forms work — use whichever you prefer.\n * @example\n * game.assets = [\n *   // PNG tileset\n *   {name: \"tileset-platformer\", type: \"image\",  src: \"data/map/tileset.png\"},\n *   // PNG packed texture\n *   {name: \"texture\", type:\"image\", src: \"data/gfx/texture.png\"}\n *   // PNG base64 encoded image\n *   {name: \"texture\", type:\"image\", src: \"data:image/png;base64,iVBORw0KAAAQAAAAEACA...\"},\n *   // compressed texture with fallback chain (tries each source in order until one succeeds)\n *   {name: \"terrain\", type:\"image\", src: [\"data/gfx/terrain.astc.ktx\", \"data/gfx/terrain.dds\", \"data/gfx/terrain.png\"]},\n *   // TSX file\n *   {name: \"meta_tiles\", type: \"tsx\", src: \"data/map/meta_tiles.tsx\"},\n *   // TMX level (XML & JSON)\n *   {name: \"map1\", type: \"tmx\", src: \"data/map/map1.json\"},\n *   {name: \"map2\", type: \"tmx\", src: \"data/map/map2.tmx\"},\n *   {name: \"map3\", type: \"tmx\", format: \"json\", data: {\"height\":15,\"layers\":[...],\"tilewidth\":32,\"version\":1,\"width\":20}},\n *   {name: \"map4\", type: \"tmx\", format: \"xml\", data: {xml representation of tmx}},\n *   // audio resources\n *   {name: \"bgmusic\", type: \"audio\",  src: \"data/audio/\"},\n *   {name: \"cling\",   type: \"audio\",  src: \"data/audio/\"},\n *   // base64 encoded audio resources\n *   {name: \"band\",   type: \"audio\",  src: \"data:audio/wav;base64,...\"},\n *   // binary file\n *   {name: \"ymTrack\", type: \"binary\", src: \"data/audio/main.ym\"},\n *   // JSON file (used for texturePacker)\n *   {name: \"texture\", type: \"json\", src: \"data/gfx/texture.json\"},\n *   // JavaScript file\n *   {name: \"plugin\", type: \"js\", src: \"data/js/plugin.js\"},\n *   // Font Face\n *   {name: \"'kenpixel'\", type: \"fontface\",  src: \"data/font/kenvector_future.woff2\"},\n *   // video resources\n *   {name: \"intro\", type: \"video\",  src: \"data/video/\"},\n *   // base64 encoded video asset\n *   me.loader.load({name: \"avatar\", type:\"video\", src: \"data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZ...\"};\n * ];\n * ...\n * // set all resources to be loaded (callback form)\n * me.loader.preload(game.assets, () => this.loaded());\n * // ...or await it (the callback still fires too)\n * await me.loader.preload(game.assets);\n * @category Assets\n */\nexport function preload(assets, onloadcb, switchToLoadState = true) {\n\t// set the onload callback if defined\n\tif (typeof onloadcb !== \"undefined\") {\n\t\tonload = onloadcb;\n\t}\n\n\t// parse the resources and collect a promise for each asset. `load()` now\n\t// returns a Promise in its no-callback form, so there's no need to wrap a\n\t// callback in `new Promise` here — just layer the progress / error reporting\n\t// on top of it. Each asset counts as one toward the progress total (parsers\n\t// load 0 or 1 resource; a cached asset resolves immediately and still\n\t// reports progress, keeping `loadCount`/`resourceCount` balanced).\n\tconst promises = assets.map((asset) => {\n\t\tresourceCount += 1;\n\t\treturn load(asset)\n\t\t\t.then(() => {\n\t\t\t\tonResourceLoaded(asset);\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\t// onLoadingError emits LOADER_ERROR and throws by design, so this\n\t\t\t\t// re-rejects → Promise.all (and the returned promise) reject and\n\t\t\t\t// `await loader.preload(...)` surfaces the failure. The trailing\n\t\t\t\t// throw keeps the rejection meaningful should it ever stop throwing.\n\t\t\t\tonLoadingError.call(this, asset);\n\t\t\t\tthrow err;\n\t\t\t});\n\t});\n\n\tif (switchToLoadState === true) {\n\t\t// switch to the loading screen\n\t\tstate.change(state.LOADING);\n\t}\n\n\t// Resolve once every asset has loaded; call the completion callback on\n\t// success (back-compat). Returned so callers can `await loader.preload(...)`\n\t// instead of (or as well as) passing a callback.\n\treturn Promise.all(promises).then(() => {\n\t\tcompleteLoading(onload);\n\t});\n}\n\n/**\n * retry loading assets after a loading failure\n * @memberof loader\n * @param {string} src - src of asset to reload\n * @example\n *  event.on(\n *      event.LOADER_ERROR,\n *      (res) => {\n *          // custom function\n *          showErrorNotification({\n *              text: `Error during loading content: ${res.name}`,\n *              done: loader.reload(res.src);\n *          })\n *      }\n *  );\n **/\nexport function reload(src) {\n\tconst assetToReload = failureLoadedAssets[src];\n\tunload(assetToReload);\n\tresourceCount -= 1;\n\n\tnew Promise((resolve, reject) => {\n\t\tconst count = load(\n\t\t\tassetToReload,\n\t\t\t() => {\n\t\t\t\tonResourceLoaded(assetToReload);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t(err) => {\n\t\t\t\tonLoadingError.call(this, assetToReload);\n\t\t\t\treject(err);\n\t\t\t},\n\t\t);\n\t\tresourceCount += count;\n\t\tif (count === 0) {\n\t\t\tresolve();\n\t\t}\n\t}).then(() => {\n\t\tcompleteLoading(onload);\n\t});\n}\n\n/**\n * Load a single asset (to be used if you need to load additional asset(s) during the game)\n * @memberof loader\n * @param {Asset} asset\n * @param {Function} [onload] - function to be called when the asset is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @returns {number|Promise<void>} with `onload`/`onerror` provided, the amount\n * of corresponding resource to be preloaded (the legacy callback form). With\n * **both omitted**, a Promise that resolves once the asset has loaded, so you\n * can `await loader.load(asset)` for a one-off dynamic load.\n * @example\n * // load an image asset (callback form)\n * me.loader.load({name: \"avatar\",  type:\"image\",  src: \"data/avatar.png\"}, () => this.onload(), () => this.onerror());\n * // ...or await a single dynamic asset (no callbacks)\n * await me.loader.load({name: \"avatar\", type: \"image\", src: \"data/avatar.png\"});\n * // load a compressed texture with fallback chain\n * me.loader.load({name: \"terrain\", type:\"image\", src: [\"data/gfx/terrain.astc.ktx\", \"data/gfx/terrain.dds\", \"data/gfx/terrain.png\"]}, () => this.onload());\n * // load a base64 image asset\n *  me.loader.load({name: \"avatar\", type:\"image\", src: \"data:image/png;base64,iVBORw0KAAAQAAAAEACA...\"};\n *  // load a base64 video asset\n *  me.loader.load({\n *     name: \"avatar\",\n *     type:\"video\",\n *     src: \"data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZ..\"\n *  };\n * // start loading music\n * me.loader.load({\n *     name   : \"bgmusic\",\n *     type   : \"audio\",\n *     src    : \"data/audio/\"\n * }, function () {\n *     me.audio.play(\"bgmusic\");\n * });\n * @category Assets\n */\nexport function load(asset, onload, onerror) {\n\t// make sure all parsers have been initialized\n\tif (parserInitialized === false) {\n\t\tinitParsers();\n\t}\n\n\t// Resolve the effective src WITHOUT mutating the caller's asset\n\t// descriptor: load() used to write the transformed url back into\n\t// asset.src, so retrying the same object — loader.reload() after a\n\t// failure, or simply load()ing the same manifest entry twice —\n\t// prepended baseURL onto an already-transformed src and fetched a\n\t// mangled URL. Caches, events and failure tracking all key on the\n\t// caller's original src; only the parser sees the resolved one.\n\tlet src = asset.src;\n\n\t// strip url() wrapper for fontface assets so baseURL can be prepended to the raw path\n\tif (asset.type === \"fontface\" && typeof src === \"string\") {\n\t\tconst urlMatch = src.match(/^url\\(\\s*['\"]?(.*?)['\"]?\\s*\\)$/);\n\t\tif (urlMatch) {\n\t\t\tsrc = urlMatch[1];\n\t\t}\n\t}\n\n\t// transform the url if necessary (skip for local() font sources and data URIs)\n\tif (\n\t\ttypeof baseURL[asset.type] !== \"undefined\" &&\n\t\ttypeof src === \"string\" &&\n\t\t!src.startsWith(\"local(\") &&\n\t\t!src.startsWith(\"data:\")\n\t) {\n\t\tsrc = baseURL[asset.type] + src;\n\t}\n\n\tconst resource =\n\t\tsrc === asset.src ? asset : Object.assign({}, asset, { src });\n\n\tconst parser = parsers.get(asset.type);\n\n\tif (typeof parser === \"undefined\") {\n\t\tthrow new Error(\"load : unknown or invalid resource type : \" + asset.type);\n\t}\n\n\tconst settings = {\n\t\tnocache: nocache,\n\t\tcrossOrigin: crossOrigin,\n\t\twithCredentials: withCredentials,\n\t};\n\n\t// Promise form: with no callbacks, resolve once the asset (and any\n\t// sub-resources) have loaded — for a one-off `await loader.load(asset)`.\n\t// The legacy callback form below still returns the resource count.\n\tif (onload === undefined && onerror === undefined) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// parser returns the amount of asset to be loaded (usually 1, more\n\t\t\t// if it splits into several); 0 means already cached → resolve now.\n\t\t\tconst count = parser.call(\n\t\t\t\tthis,\n\t\t\t\tresource,\n\t\t\t\t() => {\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t\tsettings,\n\t\t\t);\n\t\t\tif (count === 0) {\n\t\t\t\tresolve();\n\t\t\t}\n\t\t});\n\t}\n\n\t// parser returns the amount of asset to be loaded (usually 1 unless an asset is splitted into several ones)\n\treturn parser.call(this, resource, onload, onerror, settings);\n}\n\n/**\n * unload the specified asset to free memory\n * @memberof loader\n * @param {Asset} asset\n * @returns {boolean} true if unloaded\n * @example me.loader.unload({name: \"avatar\",  type:\"image\"});\n * @category Assets\n */\nexport function unload(asset) {\n\tswitch (asset.type) {\n\t\tcase \"binary\":\n\t\t\tif (!(asset.name in binList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete binList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"image\":\n\t\t\tif (!(asset.name in imgList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdelete imgList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"json\":\n\t\t\tif (!(asset.name in jsonList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete jsonList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"js\":\n\t\t\t// ??\n\t\t\treturn true;\n\n\t\tcase \"fontface\":\n\t\t\t// membership guard like every other type — FontFaceSet.delete is\n\t\t\t// WebIDL-typed and THROWS on undefined instead of returning false\n\t\t\tif (!(asset.name in fontList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttypeof globalThis.document !== \"undefined\" &&\n\t\t\t\ttypeof globalThis.document.fonts !== \"undefined\"\n\t\t\t) {\n\t\t\t\tglobalThis.document.fonts.delete(fontList[asset.name]);\n\t\t\t\tdelete fontList[asset.name];\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\n\t\tcase \"tmx\":\n\t\tcase \"tsx\":\n\t\t\tif (!(asset.name in tmxList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete tmxList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"audio\":\n\t\t\treturn audio.unload(asset.name);\n\n\t\tcase \"video\":\n\t\t\tif (!(asset.name in videoList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete videoList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"obj\":\n\t\t\tif (!(asset.name in objList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete objList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"gltf\":\n\t\tcase \"glb\":\n\t\t\tif (!(asset.name in gltfList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete gltfList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"mtl\":\n\t\t\tif (!(asset.name in mtlList)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tdelete mtlList[asset.name];\n\t\t\treturn true;\n\n\t\tcase \"aseprite\": {\n\t\t\t// aseprite asset populates both imgList and jsonList under the same key\n\t\t\tconst hadImage = asset.name in imgList;\n\t\t\tconst hadJson = asset.name in jsonList;\n\t\t\tif (!hadImage && !hadJson) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tdelete imgList[asset.name];\n\t\t\tdelete jsonList[asset.name];\n\t\t\treturn true;\n\t\t}\n\n\t\tcase \"shader\": {\n\t\t\tconst effect = shaderList[asset.name];\n\t\t\tif (typeof effect === \"undefined\") {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// the loader owns the shared ShaderEffect/GLShader — actually free\n\t\t\t// the GL program, don't just drop the cache entry. A `null` entry\n\t\t\t// is a {vertex, fragment} pair loaded under the Canvas renderer\n\t\t\t// (no GL program to free).\n\t\t\teffect?.destroy();\n\t\t\tdelete shaderList[asset.name];\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t\"unload : unknown or invalid resource type : \" + asset.type,\n\t\t\t);\n\t}\n}\n\n/**\n * unload all resources to free memory\n * @memberof loader\n * @example me.loader.unloadAll();\n * @category Assets\n */\nexport function unloadAll() {\n\tlet name;\n\n\t// unload all binary resources\n\tfor (name in binList) {\n\t\tif (binList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"binary\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all image resources\n\tfor (name in imgList) {\n\t\tif (imgList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"image\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all tmx resources\n\tfor (name in tmxList) {\n\t\tif (tmxList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"tmx\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all json resources\n\tfor (name in jsonList) {\n\t\tif (jsonList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"json\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all video resources\n\tfor (name in videoList) {\n\t\tif (videoList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"video\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all font resources\n\tfor (name in fontList) {\n\t\tif (fontList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"fontface\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all OBJ resources\n\tfor (name in objList) {\n\t\tif (objList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"obj\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all MTL resources\n\tfor (name in mtlList) {\n\t\tif (mtlList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"mtl\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all glTF/GLB scene resources\n\tfor (name in gltfList) {\n\t\tif (gltfList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"glb\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all shader resources (destroys their shared GL programs)\n\tfor (name in shaderList) {\n\t\tif (shaderList.hasOwnProperty(name)) {\n\t\t\tunload({\n\t\t\t\tname: name,\n\t\t\t\ttype: \"shader\",\n\t\t\t});\n\t\t}\n\t}\n\n\t// unload all audio resources\n\taudio.unloadAll();\n}\n\n/**\n * return the specified TMX/TSX object\n * @memberof loader\n * @param {string} elt - name of the tmx/tsx element (\"map1\");\n * @returns {object} requested element or null if not found\n * @category Assets\n */\nexport function getTMX(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in tmxList) {\n\t\treturn tmxList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified Binary object\n * @memberof loader\n * @param {string} elt - name of the binary object (\"ymTrack\");\n * @returns {object} requested element or null if not found\n * @category Assets\n */\nexport function getBinary(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in binList) {\n\t\treturn binList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified Image Object\n * @memberof loader\n * @param {string} image - name of the Image element (\"tileset-platformer\");\n * @returns {HTMLImageElement|CompressedImage|null} requested element or null if not found\n * @category Assets\n */\nexport function getImage(image) {\n\t// force as string and extract the base name\n\timage = getBasename(\"\" + image);\n\tif (image in imgList) {\n\t\t// return the corresponding Image object\n\t\treturn imgList[image];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified JSON Object\n * @memberof loader\n * @param {string} elt - name of the json file\n * @returns {JSON}\n * @category Assets\n */\nexport function getJSON(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in jsonList) {\n\t\treturn jsonList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified OBJ model data\n * @memberof loader\n * @param {string} elt - name of the OBJ file (as specified in the preload list)\n * @returns {object} parsed OBJ data with `vertices` (Float32Array), `uvs` (Float32Array), `indices` (Uint16Array), and `vertexCount` (number), or null if not found\n * @category Assets\n * @example\n * // 1. preload the OBJ model and its texture\n * me.loader.preload([\n *     { name: \"cube\", type: \"obj\", src: \"models/cube.obj\" },\n *     { name: \"cube\", type: \"image\", src: \"models/cube_texture.png\" },\n * ], () => {\n *     // 2. create a Mesh using the preloaded model name\n *     const mesh = new me.Mesh(400, 300, {\n *         model: \"cube\",        // references the preloaded OBJ\n *         texture: \"cube\",      // references the preloaded image\n *         width: 200,\n *         height: 200,\n *     });\n *     me.game.world.addChild(mesh);\n *\n *     // 3. or access the raw parsed data directly\n *     const data = me.loader.getOBJ(\"cube\");\n *     // data.vertices — Float32Array of x,y,z positions\n *     // data.uvs — Float32Array of u,v texture coordinates\n *     // data.indices — Uint16Array of triangle vertex indices\n *     // data.vertexCount — number of unique vertices\n *     // data.groups — usemtl material groups ({materialName, start, count} index ranges)\n * });\n */\nexport function getOBJ(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in objList) {\n\t\treturn objList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * a parsed glTF/GLB scene descriptor, as returned by {@link loader.getGLTF}\n * @typedef {object} GLTFData\n * @property {object[]} nodes - one entry per mesh primitive: accumulated `world` transform, `vertices`, `normals`, `uvs`, `indices`, `vertexCount`, decoded baseColor `image` (or `null`), `baseColorFactor`, per-vertex `colors`, sampler-derived `textureRepeat`/`textureFilter`, `alphaCutoff`, `emissive`, `unlit` (KHR_materials_unlit), `doubleSided`, and the source node `name`\n * @property {Array<{world: number[], type?: string, perspective?: {yfov?: number, aspectRatio?: number, znear?: number, zfar?: number}, orthographic?: object}>} cameras - glTF cameras, each with its `world` transform + the glTF camera parameters (`perspective` for perspective cameras, `orthographic` otherwise)\n * @property {object[]} lights - parsed `KHR_lights_punctual` lights (`type`, `color`, `intensity`, `range`, `innerConeAngle`/`outerConeAngle` for spots, world-space `direction`/`position`, `name`)\n * @property {{min: number[], max: number[]}} bounds - world-space scene bounds in glTF units\n * @property {object[]} graph - the full node graph (every node's TRS/matrix + children), for custom traversal\n * @property {object[]} animations - parsed node animations (consumed by `GLTFModel` playback)\n */\n\n/**\n * return the parsed glTF/GLB scene descriptor for the given asset name.\n *\n * The descriptor is `{ nodes, cameras, lights, bounds, graph, animations }`:\n * - `nodes` — one entry per mesh primitive, each carrying its accumulated\n *   `world` transform (16 floats, column-major), `vertices`, `normals`,\n *   `uvs`, `indices`, `vertexCount`, a decoded baseColor `image` (or `null`),\n *   and a `doubleSided` flag.\n * - `cameras` — glTF cameras, each with its `world` transform + perspective\n *   parameters.\n * - `lights` — parsed `KHR_lights_punctual` lights (`type`, `color`,\n *   `intensity`, `range`, spot cone angles, world-space\n *   `direction`/`position`, `name`); empty without the extension. The\n *   level director instantiates directional, point and spot lights\n *   automatically (see {@link level.load} options).\n * - `bounds` — world-space `{ min, max }` (glTF units), handy for framing.\n *\n * Most code never needs this: a preloaded glTF/GLB auto-registers with the\n * {@link level} director, so the whole scene loads into a container in one\n * call via `me.level.load(name)` — exactly like a Tiled map. Reach for\n * `getGLTF` only when you want to inspect the raw descriptor (e.g. to frame\n * a `Camera3d` from the embedded camera).\n * @memberof loader\n * @param {string} elt - name of the glTF/GLB file (as specified in the preload list)\n * @returns {GLTFData|null} the parsed scene descriptor, or `null` if not found\n * @category Assets\n * @example\n * me.loader.preload(\n *     [{ name: \"diorama\", type: \"glb\", src: \"scenes/diorama.glb\" }],\n *     () => {\n *         // load the whole scene into the world (view under a Camera3d)\n *         me.level.load(\"diorama\", { scale: 32 });\n *\n *         // ...or inspect the raw descriptor for custom framing\n *         const scene = me.loader.getGLTF(\"diorama\");\n *         const { min, max } = scene.bounds;\n *     },\n * );\n */\nexport function getGLTF(elt) {\n\telt = \"\" + elt;\n\tif (elt in gltfList) {\n\t\treturn gltfList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * Return the precompiled `ShaderEffect` for the given \"shader\" asset —\n * compiled once during preloading, ready to assign to a renderable or\n * camera `shader` property.\n *\n * **This returns a SHARED instance**: the *same* `ShaderEffect` object on\n * every call, owned by the loader (its `shared` flag is `true`). That means:\n * - it is safe to assign to any number of renderables — none of their\n *   cleanup paths will auto-destroy it, only {@link loader.unload} /\n *   {@link loader.unloadAll} free it (and its GL program);\n * - all of them share ONE set of uniform values — `setUniform` on it\n *   affects every renderable using the shader.\n *\n * When a renderable needs its **own** uniform values, make a private,\n * caller-owned copy with `ShaderEffect.clone()` — the clone's `shared` flag\n * is reset to `false`, so it is auto-destroyed with the renderable it is\n * assigned to, like any hand-constructed effect.\n *\n * A shader asset declared as a **complete program** — a\n * `{vertex, fragment}` GLSL pair and/or a full `wgsl` module (see the\n * example) — compiles into a raw {@link GLShader} instead, carrying one\n * realization per GPU backend (`isWebGL` / `isWebGPU`): the type the\n * hosted paths take directly (a `Mesh` custom shader,\n * `renderer.customShader`, a custom batcher). Same shared-instance\n * semantics, and `GLShader.clone()` likewise yields a caller-owned copy.\n *\n * Degradation is never fatal: a fragment-body asset without a body in the\n * active renderer's language (or on Canvas) is an inert `ShaderEffect`\n * stub, and a complete-program asset without a realization for the active\n * backend is an inert `GLShader` — assigning either just keeps the\n * built-in rendering. Note that shader assets require an initialized\n * Application (`await app.init()`) — an inherent precondition of the\n * preload flow, since the loading screen itself needs the renderer.\n * @memberof loader\n * @param {string} elt - name of the shader asset (as specified in the preload list)\n * @returns {ShaderEffect|GLShader|null} the shared, precompiled shader, or `null` if not found\n * @category Assets\n * @example\n * me.loader.preload([\n *     // from a file (or data: URI)\n *     { name: \"waterRipple\", type: \"shader\", src: \"shaders/waterRipple.frag\" },\n *     // or inline GLSL via the `data` field\n *     { name: \"flash\", type: \"shader\", data: `\n *         uniform float uIntensity;\n *         vec4 apply(vec4 color, vec2 uv) { return mix(color, vec4(1.0), uIntensity); }\n *     ` },\n *     // or a complete program — a {vertex, fragment} GLSL pair and/or a\n *     // full WGSL module → one GLShader carrying both realizations; the\n *     // active renderer hosts the one it speaks\n *     { name: \"toonMesh\", type: \"shader\", src: {\n *         vertex: \"shaders/toon.vert\",\n *         fragment: \"shaders/toon.frag\",\n *         wgsl: \"shaders/toon.wgsl\",\n *     } },\n * ], () => {\n *     // one shared program — same uniform state for every user\n *     mySprite.shader = me.loader.getShader(\"waterRipple\");\n *     // private copy with its own uniforms (caller-owned, shared = false)\n *     boss.shader = me.loader.getShader(\"flash\").clone();\n *     // a complete program hosts on a mesh, replacing the built-in shading\n *     myMesh.shader = me.loader.getShader(\"toonMesh\");\n * });\n */\nexport function getShader(elt) {\n\telt = \"\" + elt;\n\tif (elt in shaderList) {\n\t\treturn shaderList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified MTL material data\n * @memberof loader\n * @param {string} elt - name of the MTL file (as specified in the preload list)\n * @returns {object} map of material names to properties (`Kd`, `d`, `map_Kd`), or null if not found\n * @category Assets\n * @example\n * // 1. preload OBJ + MTL + texture\n * me.loader.preload([\n *     { name: \"fox\", type: \"obj\", src: \"models/fox.obj\" },\n *     { name: \"fox\", type: \"mtl\", src: \"models/fox.mtl\" },\n *     { name: \"colormap\", type: \"image\", src: \"models/colormap.png\" },\n * ], () => {\n *     // 2. create a Mesh with material — texture, tint, opacity auto-applied\n *     const mesh = new me.Mesh(400, 300, {\n *         model: \"fox\",\n *         material: \"fox\",\n *         texture: \"colormap\",\n *         width: 200,\n *         height: 200,\n *     });\n *\n *     // 3. or access the raw material data directly\n *     const materials = me.loader.getMTL(\"fox\");\n *     // materials[\"colormap\"].Kd — [r, g, b] diffuse color (0-1 range)\n *     // materials[\"colormap\"].d — opacity (0-1)\n *     // materials[\"colormap\"].Ke — [r, g, b] emissive color (glow, applied as Mesh.emissive)\n *     // materials[\"colormap\"].map_Kd — resolved texture URL\n * });\n */\nexport function getMTL(elt) {\n\telt = \"\" + elt;\n\tif (elt in mtlList) {\n\t\treturn mtlList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified Video Object\n * @memberof loader\n * @param {string} elt - name of the video file\n * @returns {HTMLVideoElement}\n * @category Assets\n */\nexport function getVideo(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in videoList) {\n\t\treturn videoList[elt];\n\t}\n\treturn null;\n}\n\n/**\n * return the specified FontFace Object\n * @memberof loader\n * @param {string} elt - name of the font file\n * @returns {FontFace}\n * @category Assets\n */\nexport function getFont(elt) {\n\t// force as string\n\telt = \"\" + elt;\n\tif (elt in fontList) {\n\t\treturn fontList[elt];\n\t}\n\treturn null;\n}\n","/**\n * `me.audio` — the audio module's public surface.\n *\n * Composition:\n * - {@link ./backend.ts} — shared internal state + the two WebAudio\n *   escape hatches (`getAudioContext`, `getMasterGain`) + the\n *   `stopOnAudioError` flag.\n * - {@link ./playback.ts} — file-based playback (`load`, `play`,\n *   `pause`, `resume`, `stop`, `fade`, `seek`, `rate`, `stereo`,\n *   `position`, `orientation`, `panner`).\n * - {@link ./procedural.ts} — procedural primitives (`tone`, `noise`).\n * - {@link ./types.ts} — public TypeScript shapes.\n *\n * This file owns the remaining lifecycle / track / mix / unload\n * helpers, plus the barrel re-exports that compose the namespace.\n */\n\nimport {\n\tgetGlobalVolume,\n\tgetSoundOrThrow,\n\thasCodec,\n\tisAudioAvailable,\n\tisGlobalMuted,\n\tsetGlobalMuted,\n\tsetGlobalVolume,\n\tstate,\n} from \"./backend.ts\";\nimport { play } from \"./playback.ts\";\n\n// Public re-exports from the split modules.\nexport {\n\tgetAudioContext,\n\tgetMasterGain,\n\tsetStopOnAudioError,\n\tstopOnAudioError,\n} from \"./backend.ts\";\nexport {\n\tfade,\n\tload,\n\torientation,\n\tpanner,\n\tpause,\n\tplay,\n\tposition,\n\trate,\n\tresume,\n\tseek,\n\tstereo,\n\tstop,\n} from \"./playback.ts\";\nexport { noise, tone } from \"./procedural.ts\";\n// Public type surface.\nexport type {\n\tLoadSettings,\n\tNoiseFilter,\n\tNoiseOptions,\n\tPannerAttributes,\n\tSoundAsset,\n\tToneOptions,\n} from \"./types.ts\";\n\n/**\n * Initialize and configure the audio module.\n *\n * For maximum browser coverage, list at least two formats — typically\n * webm first then mp3. Webm has near-universal modern coverage with\n * great compression / quality balance; mp3 covers older browsers.\n * Order matters: melonJS picks the first compatible format from the\n * list, so put the preferred one first.\n * @param format - Comma-separated audio formats to prioritize.\n *   One or more of: `\"mp3\"`, `\"mpeg\"`, `\"opus\"`, `\"ogg\"`, `\"oga\"`,\n *   `\"wav\"`, `\"aac\"`, `\"caf\"`, `\"m4a\"`, `\"m4b\"`, `\"mp4\"`, `\"weba\"`,\n *   `\"webm\"`, `\"dolby\"`, `\"flac\"`. Defaults to `\"mp3\"`.\n * @returns `true` when audio support was successfully initialised.\n * @example\n * // initialise with webm preferred, mp3 as fallback\n * if (!me.audio.init(\"webm,mp3\")) {\n *     alert(\"Sorry but your browser does not support html 5 audio !\");\n *     return;\n * }\n * @category Audio\n */\nexport function init(format: string = \"mp3\"): boolean {\n\tstate.audioExts = format.split(\",\");\n\treturn isAudioAvailable();\n}\n\n/**\n * Check whether the given audio codec is supported by the browser.\n * @param codec - The audio format to check.\n * @returns `true` when the format is supported.\n * @category Audio\n */\nexport function hasFormat(codec: string): boolean {\n\treturn hasCodec(codec);\n}\n\n/**\n * Check whether audio (HTML5 or WebAudio) is supported by the browser.\n * @returns `true` when at least one audio backend is available.\n * @category Audio\n */\nexport function hasAudio(): boolean {\n\treturn isAudioAvailable();\n}\n\n/**\n * Enable audio output. Only useful if audio is supported and was\n * previously disabled through {@link disable}.\n * @category Audio\n */\nexport function enable(): void {\n\tunmuteAll();\n}\n\n/**\n * Disable audio output.\n * @category Audio\n */\nexport function disable(): void {\n\tmuteAll();\n}\n\n/**\n * Play the specified audio track. Automatically loops the clip and\n * tracks it as the current track for {@link stopTrack} / {@link pauseTrack}\n * / {@link resumeTrack}.\n * @param sound_name - Audio track name (case-sensitive).\n * @param volume - Playback volume, `0.0..1.0`. Defaults to the current\n *   global volume.\n * @returns The sound instance ID.\n * @example\n * me.audio.playTrack(\"awesome_music\");\n * @category Audio\n */\nexport function playTrack(sound_name: string, volume?: number): number {\n\tstate.currentTrackId = sound_name;\n\treturn play(state.currentTrackId, true, null, volume);\n}\n\n/**\n * Stop the current audio track.\n * @see {@link playTrack}\n * @example\n * me.audio.playTrack(\"awesome_music\");\n * me.audio.stopTrack();\n * @category Audio\n */\nexport function stopTrack(): void {\n\tif (state.currentTrackId !== null) {\n\t\tstate.tracks[state.currentTrackId]?.stop();\n\t\tstate.currentTrackId = null;\n\t}\n}\n\n/**\n * Pause the current audio track.\n * @example\n * me.audio.pauseTrack();\n * @category Audio\n */\nexport function pauseTrack(): void {\n\tif (state.currentTrackId !== null) {\n\t\tstate.tracks[state.currentTrackId]?.pause();\n\t}\n}\n\n/**\n * Resume the previously paused audio track.\n * @example\n * me.audio.playTrack(\"awesome_music\");\n * me.audio.pauseTrack();\n * me.audio.resumeTrack();\n * @category Audio\n */\nexport function resumeTrack(): void {\n\tif (state.currentTrackId !== null) {\n\t\tstate.tracks[state.currentTrackId]?.play();\n\t}\n}\n\n/**\n * Return the name of the current audio track, or `null` if none is set.\n * @returns The current track name, or `null`.\n * @category Audio\n */\nexport function getCurrentTrack(): string | null {\n\treturn state.currentTrackId;\n}\n\n/**\n * Set the default global volume.\n * @param volume - Volume value, `0.0..1.0`.\n * @category Audio\n */\nexport function setVolume(volume: number): void {\n\tsetGlobalVolume(volume);\n}\n\n/**\n * Get the default global volume.\n * @returns The current volume value, `0.0..1.0`.\n * @category Audio\n */\nexport function getVolume(): number {\n\treturn getGlobalVolume();\n}\n\n/**\n * Mute or unmute the specified sound. Playback continues; only the\n * audible output is suppressed.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @param shouldMute - `true` to mute, `false` to unmute. Defaults to\n *   `true`.\n * @example\n * // mute the background music\n * me.audio.mute(\"awesome_music\");\n * @category Audio\n */\nexport function mute(\n\tsound_name: string,\n\tid?: number,\n\tshouldMute: boolean = true,\n): void {\n\tgetSoundOrThrow(sound_name).mute(shouldMute, id);\n}\n\n/**\n * Unmute the specified sound.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @category Audio\n */\nexport function unmute(sound_name: string, id?: number): void {\n\tmute(sound_name, id, false);\n}\n\n/**\n * Mute all audio.\n * @category Audio\n */\nexport function muteAll(): void {\n\tsetGlobalMuted(true);\n}\n\n/**\n * Unmute all audio.\n * @category Audio\n */\nexport function unmuteAll(): void {\n\tsetGlobalMuted(false);\n}\n\n/**\n * Return whether audio is currently muted globally.\n * @returns `true` when audio is muted globally.\n * @category Audio\n */\nexport function muted(): boolean {\n\treturn isGlobalMuted();\n}\n\n/**\n * Unload the specified audio track to free memory.\n * @param sound_name - Audio track name (case-sensitive).\n * @returns `true` when the track was found and unloaded.\n * @example\n * me.audio.unload(\"awesome_music\");\n * @category Audio\n */\nexport function unload(sound_name: string): boolean {\n\tconst sound = state.tracks[sound_name];\n\tif (!sound) {\n\t\treturn false;\n\t}\n\n\t// forget the current-track pointer if it referenced this sound, so\n\t// getCurrentTrack() / pauseTrack() / resumeTrack() don't act on a ghost\n\tif (state.currentTrackId === sound_name) {\n\t\tstate.currentTrackId = null;\n\t}\n\n\t// destroy the Howl object\n\tsound.unload();\n\tdelete state.tracks[sound_name];\n\treturn true;\n}\n\n/**\n * Unload every loaded audio track to free memory.\n * @example\n * me.audio.unloadAll();\n * @category Audio\n */\nexport function unloadAll(): void {\n\tfor (const sound_name in state.tracks) {\n\t\tif (Object.prototype.hasOwnProperty.call(state.tracks, sound_name)) {\n\t\t\tunload(sound_name);\n\t\t}\n\t}\n}\n","/**\n * Audio backend — the shared internal surface every other audio module\n * (procedural, playback) builds on. Keeps the Howler reference and the\n * cross-module mutable state in one place so the public surface modules\n * stay backend-agnostic.\n *\n * Not part of the public `me.audio.*` API — the two getters\n * `getAudioContext` / `getMasterGain` are re-exported from `audio.ts`\n * for end users; everything else (the `state` object, `soundLoadError`)\n * is internal.\n */\n\nimport { Howl, Howler } from \"howler\";\n\n/**\n * Whether to stop on an audio loading error.\n *\n * When `true`, melonJS throws an exception and aborts loading.\n * When `false`, melonJS disables sound and logs a warning to the console.\n *\n * Read-only through the `me.audio` namespace (module namespace properties\n * can't be assigned) — change it with {@link setStopOnAudioError}.\n * @default true\n * @see setStopOnAudioError\n */\nexport let stopOnAudioError: boolean = true;\n\n/**\n * Set the {@link stopOnAudioError} flag — whether an audio clip that still\n * fails after its retries throws (aborting loading) or just disables sound\n * with a console warning. This setter is the supported way to change the\n * flag: assigning `me.audio.stopOnAudioError = false` directly throws a\n * TypeError, because module namespace properties are read-only.\n * @param value - `true` to throw on a failed load, `false` to disable sound instead\n * @example\n * // don't abort the whole game when audio fails to load\n * me.audio.setStopOnAudioError(false);\n * @category Audio\n */\nexport function setStopOnAudioError(value: boolean): void {\n\tstopOnAudioError = value;\n}\n\n/**\n * Cross-module mutable state. A single object so multiple consumers\n * can read and mutate the same fields without the \"ESM `let` exports\n * don't share writes across modules\" footgun.\n *\n * Fields:\n * - `tracks` — loaded Howl instances keyed by logical sound name.\n *   `Howl | undefined` because missing keys return undefined at runtime\n *   even though the type signature wouldn't normally admit it.\n * - `currentTrackId` — the name of the currently-playing track managed\n *   by the `playTrack` / `stopTrack` helpers.\n * - `retryCounters` — per-sound retry counters for `soundLoadError`'s\n *   back-off, keyed by sound name (a single shared counter let parallel\n *   loads steal each other's retry budget).\n * - `audioExts` — the active list of audio formats set by `init`.\n * @ignore\n */\nexport const state = {\n\ttracks: {} as Record<string, Howl | undefined>,\n\tcurrentTrackId: null as string | null,\n\tretryCounters: {} as Record<string, number>,\n\taudioExts: [] as string[],\n};\n\n/**\n * Look up a loaded `Howl` instance by logical name, or throw a\n * uniform \"audio clip X does not exist\" error if it isn't loaded.\n * Used by every per-clip helper across `playback.ts` / `audio.ts` so\n * the error contract stays identical across the whole surface.\n * @ignore\n */\nexport function getSoundOrThrow(sound_name: string): Howl {\n\tconst sound = state.tracks[sound_name];\n\tif (!sound) {\n\t\tthrow new Error(`audio clip ${sound_name} does not exist`);\n\t}\n\treturn sound;\n}\n\n/**\n * Event listener callback on load error. Retries the load up to 3\n * times, then either throws or disables audio (depending on the\n * `stopOnAudioError` flag re-exported from `audio.ts`).\n * @ignore\n */\nexport const soundLoadError = function (\n\tsound_name: string,\n\tonerror_cb?: () => void,\n\tstopOnError: boolean = true,\n): void {\n\t// per-sound retry budget — a single shared counter let parallel loads\n\t// steal each other's retries: three failures of one flaky file pushed\n\t// ANOTHER sound's first failure straight over the give-up threshold\n\tconst retries = state.retryCounters[sound_name] ?? 0;\n\tif (retries >= 3) {\n\t\tdelete state.retryCounters[sound_name];\n\t\tconst errmsg = `melonJS: failed loading ${sound_name}`;\n\t\tif (!stopOnError) {\n\t\t\t// disable audio\n\t\t\tHowler.mute(true);\n\t\t\tonerror_cb?.();\n\t\t\tconsole.warn(`${errmsg}, disabling audio`);\n\t\t} else {\n\t\t\tonerror_cb?.();\n\t\t\tthrow new Error(errmsg);\n\t\t}\n\t} else {\n\t\tstate.retryCounters[sound_name] = retries + 1;\n\t\tstate.tracks[sound_name]?.load();\n\t}\n};\n\n/**\n * Returns the underlying WebAudio `AudioContext` used by the audio\n * module (the same one shared with file-based playback), or `null` if\n * audio is disabled or no compatible WebAudio implementation is\n * available.\n *\n * Use this when you need to build a custom WebAudio graph — procedural\n * SFX, custom filters / spatial nodes, audio analysis — without\n * spawning a second context. Browsers throttle or refuse multiple\n * `AudioContext` instances on the same page and each has its own\n * suspend-until-gesture state, so sharing matters.\n *\n * The context is lazily created on first access; the call also returns\n * the cached instance on every subsequent call.\n * @category Audio\n */\nexport function getAudioContext(): AudioContext | null {\n\tif (Howler.noAudio) return null;\n\t// Howler only creates its `AudioContext` lazily — on the first Howl\n\t// constructor, the first volume/mute call, etc. Procedural-only\n\t// users (calling `tone` without ever loading a sound file) never\n\t// hit any of those code paths, leaving `Howler.ctx` null. Nudging\n\t// `Howler.volume()` triggers Howler's internal `setupAudioContext`\n\t// without changing the master volume.\n\t//\n\t// Gate the nudge on `usingWebAudio` so we don't fire it on every\n\t// call in HTML5-only mode — `Howler.ctx` is permanently null there\n\t// and the nudge can't help (the runtime check would just re-decide\n\t// \"no WebAudio\" and return immediately).\n\tif (Howler.usingWebAudio && !Howler.ctx) {\n\t\tHowler.volume(Howler.volume());\n\t}\n\t// `ctx` is declared non-nullable in @types/howler but can still be\n\t// null when WebAudio is unavailable (HTML5-only mode).\n\treturn Howler.ctx ?? null;\n}\n\n/**\n * Return the audio module's master gain node — the single `GainNode`\n * every playback path runs through on its way to `ctx.destination`,\n * and the lever that {@link setVolume} / {@link muteAll} manipulate.\n *\n * Connect to this node (instead of `ctx.destination`) whenever you\n * build a custom WebAudio graph and want the result to respect the\n * engine's mute / volume state. Returns `null` when audio is disabled\n * or unavailable.\n * @category Audio\n */\nexport function getMasterGain(): GainNode | null {\n\t// Chains through `getAudioContext` so the same lazy-init nudge\n\t// covers both — when audio runs on HTML5 Audio instead of WebAudio,\n\t// `Howler.ctx` is null and we short-circuit here. The remaining\n\t// `?? null` defends against the narrow iOS-8-webview edge case where\n\t// ctx is created but `masterGain` isn't (Howler flips\n\t// `usingWebAudio` to false between the two steps).\n\tif (!getAudioContext()) return null;\n\treturn Howler.masterGain ?? null;\n}\n\n// ---------------------------------------------------------------------\n// Thin wrappers over Howler's global surface. Kept internal (not\n// re-exported from `audio.ts`) so users still go through the public\n// `setVolume` / `muteAll` / `hasFormat` / etc. helpers. Their job is\n// to isolate the Howler reference to this file — when the backend\n// gets swapped, only these wrappers change.\n// ---------------------------------------------------------------------\n\n/**\n * Get the audio module's global volume.\n * @ignore\n */\nexport function getGlobalVolume(): number {\n\treturn Howler.volume();\n}\n\n/**\n * Set the audio module's global volume.\n * @ignore\n */\nexport function setGlobalVolume(v: number): void {\n\tHowler.volume(v);\n}\n\n/**\n * Mute or unmute the audio module globally.\n * @ignore\n */\nexport function setGlobalMuted(muted: boolean): void {\n\tHowler.mute(muted);\n}\n\n/**\n * Whether the audio module is currently muted globally.\n * @ignore\n */\nexport function isGlobalMuted(): boolean {\n\t// Howler doesn't expose a public muted getter — peek at the private\n\t// flag that `Howler.mute(true/false)` sets internally. Narrow cast\n\t// (vs. `as any`) documents the single field we're reaching for.\n\treturn (Howler as unknown as { _muted: boolean })._muted;\n}\n\n/**\n * Stop every playing sound on every channel.\n * @ignore\n */\nexport function stopAllPlayback(): void {\n\tHowler.stop();\n}\n\n/**\n * Whether the given audio codec is supported by the backend / browser.\n * @ignore\n */\nexport function hasCodec(codec: string): boolean {\n\tif (!isAudioAvailable()) return false;\n\t// `Howler.codecs(...)` is declared `boolean` in @types/howler but at\n\t// runtime returns `undefined` for unrecognised codecs (lookup in a\n\t// dict). Widen the cast so a strict comparison yields a clean\n\t// boolean for the public surface (`audio.hasFormat`).\n\treturn (Howler.codecs(codec) as boolean | undefined) === true;\n}\n\n/**\n * Whether at least one audio backend (HTML5 or WebAudio) is available.\n * @ignore\n */\nexport function isAudioAvailable(): boolean {\n\treturn !Howler.noAudio;\n}\n","/**\n * File-based playback — load audio assets, then play / pause / fade /\n * seek / etc. Every function in this module operates on the shared\n * `state.tracks` map exposed from `backend.ts`, so the audio module's\n * other surfaces (track helpers, mix, unload) can see the same set of\n * loaded sounds.\n */\n\nimport { Howl } from \"howler\";\nimport { clamp } from \"../math/math.ts\";\nimport { isDataUrl } from \"../utils/string.ts\";\nimport {\n\tgetGlobalVolume,\n\tgetSoundOrThrow,\n\tsoundLoadError,\n\tstate,\n\tstopAllPlayback,\n\tstopOnAudioError,\n} from \"./backend.ts\";\nimport type { LoadSettings, PannerAttributes, SoundAsset } from \"./types.ts\";\n\n/**\n * Load an audio file.\n *\n * `sound.src` is treated as a base path / prefix; the URL is built as\n * `${sound.src}${sound.name}.${ext}` for each extension configured by\n * {@link init}, until one loads. Data URLs (`data:audio/...`) are\n * used as-is and skip the prefix-and-extension dance.\n * @param sound - The {@link SoundAsset} descriptor — logical `name`,\n *   `src` base path / prefix (or data URL), and optional playback\n *   flags (`autoplay`, `loop`, `stream`, `html5`).\n * @param onloadcb - Called when the resource has finished loading.\n * @param onerrorcb - Called when loading fails.\n * @param settings - Optional {@link LoadSettings} — `nocache` (query\n *   string appended for cache busting) and `withCredentials` (forwarded\n *   to the underlying XHR for cross-origin authenticated requests).\n * @returns The number of assets loaded (always `1` on success).\n * @category Audio\n */\nexport function load(\n\tsound: SoundAsset,\n\tonloadcb?: () => void,\n\tonerrorcb?: () => void,\n\tsettings: LoadSettings = {},\n): number {\n\t// already loaded? Return 0 (\"cached\", like every other asset parser) —\n\t// re-preloading a manifest (e.g. re-entering a stage that preloads) used\n\t// to silently replace the Howl and leak the old instance's decoded\n\t// buffers / HTML5 nodes. Unload first to genuinely reload a clip.\n\tif (typeof state.tracks[sound.name] !== \"undefined\") {\n\t\treturn 0;\n\t}\n\tconst urls: string[] = [];\n\tif (state.audioExts.length === 0) {\n\t\tthrow new Error(\n\t\t\t\"target audio extension(s) should be set through me.audio.init() before calling the preloader.\",\n\t\t);\n\t}\n\tif (isDataUrl(sound.src)) {\n\t\turls.push(sound.src);\n\t} else {\n\t\tfor (let i = 0; i < state.audioExts.length; i++) {\n\t\t\turls.push(\n\t\t\t\t`${sound.src + sound.name}.${state.audioExts[i]}${settings.nocache ?? \"\"}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tstate.tracks[sound.name] = new Howl({\n\t\tsrc: urls,\n\t\tvolume: getGlobalVolume(),\n\t\tautoplay: sound.autoplay === true,\n\t\tloop: sound.loop === true,\n\t\thtml5: sound.stream === true || sound.html5 === true,\n\t\t// @ts-expect-error xhrWithCredentials is a valid Howl option but not in the type definitions\n\t\txhrWithCredentials: settings.withCredentials,\n\t\tonloaderror() {\n\t\t\tsoundLoadError.call(this, sound.name, onerrorcb, stopOnAudioError);\n\t\t},\n\t\tonload() {\n\t\t\tdelete state.retryCounters[sound.name];\n\t\t\tif (typeof onloadcb === \"function\") {\n\t\t\t\tonloadcb();\n\t\t\t}\n\t\t},\n\t});\n\n\treturn 1;\n}\n\n/**\n * Play the specified sound.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param loop - Whether to loop the clip. Defaults to `false`.\n * @param onend - Called when the sound instance ends playing.\n * @param volume - Playback volume, `0.0..1.0`. Defaults to the current\n *   global volume.\n * @returns The sound instance ID.\n * @example\n * // play the \"cling\" audio clip\n * me.audio.play(\"cling\");\n * // play & loop the \"engine\" audio clip\n * me.audio.play(\"engine\", true);\n * // play the \"gameover_sfx\" audio clip and call myFunc when finished\n * me.audio.play(\"gameover_sfx\", false, myFunc);\n * // play the \"gameover_sfx\" audio clip at half volume\n * me.audio.play(\"gameover_sfx\", false, null, 0.5);\n * @category Audio\n */\nexport function play(\n\tsound_name: string,\n\tloop: boolean = false,\n\tonend?: (() => void) | null,\n\tvolume?: number,\n): number {\n\tconst sound = getSoundOrThrow(sound_name);\n\tconst id = sound.play();\n\tsound.loop(loop, id);\n\tsound.volume(\n\t\ttypeof volume === \"number\" ? clamp(volume, 0.0, 1.0) : getGlobalVolume(),\n\t\tid,\n\t);\n\tif (typeof onend === \"function\") {\n\t\tif (loop) {\n\t\t\tsound.on(\"end\", onend, id);\n\t\t} else {\n\t\t\tsound.once(\"end\", onend, id);\n\t\t}\n\t}\n\treturn id;\n}\n\n/**\n * Fade a currently playing sound between two volumes.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param from - Volume to fade from, `0.0..1.0`.\n * @param to - Volume to fade to, `0.0..1.0`.\n * @param duration - Fade time in milliseconds.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are faded.\n * @category Audio\n */\nexport function fade(\n\tsound_name: string,\n\tfrom: number,\n\tto: number,\n\tduration: number,\n\tid?: number,\n): void {\n\tgetSoundOrThrow(sound_name).fade(from, to, duration, id);\n}\n\n/** @inheritDoc */\nexport function seek(sound_name: string): number;\n/** @inheritDoc */\nexport function seek(sound_name: string, seek: number, id?: number): void;\n/**\n * Get or set the playback position of a sound.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param pos - Seek position in seconds. Omit to read.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The current seek position when called as a getter; nothing\n *   when called as a setter (the Howl object Howler returns from the\n *   setter form is an internal, not part of this API).\n * @example\n * // read the current position of the background music\n * let current_pos = me.audio.seek(\"dst-gameforest\");\n * // rewind the background music to the beginning\n * me.audio.seek(\"dst-gameforest\", 0);\n * @category Audio\n */\nexport function seek(\n\tsound_name: string,\n\tpos?: number,\n\tid?: number,\n): number | void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (pos === undefined) {\n\t\treturn sound.seek();\n\t}\n\t// forward the exact arity — Howler's core methods dispatch on argument\n\t// count, so an explicit trailing undefined would be parsed as an id\n\tif (id === undefined) {\n\t\tsound.seek(pos);\n\t} else {\n\t\tsound.seek(pos, id);\n\t}\n}\n\n/** @inheritDoc */\nexport function rate(sound_name: string): number;\n/** @inheritDoc */\nexport function rate(sound_name: string, rate: number, id?: number): void;\n/**\n * Get or set the playback rate of a sound.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param rate - Playback rate (`0.5..4.0`, where `1.0` is normal\n *   speed). Omit to read.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The current playback rate when called as a getter; nothing\n *   when called as a setter (the Howl object Howler returns from the\n *   setter form is an internal, not part of this API).\n * @example\n * // read the current playback rate\n * let rate = me.audio.rate(\"dst-gameforest\");\n * // speed it up 2×\n * me.audio.rate(\"dst-gameforest\", 2.0);\n * @category Audio\n */\nexport function rate(\n\tsound_name: string,\n\trate?: number,\n\tid?: number,\n): number | void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (rate === undefined) {\n\t\treturn sound.rate();\n\t}\n\t// forward the exact arity — Howler's core methods dispatch on argument\n\t// count, so an explicit trailing undefined would be parsed as an id\n\tif (id === undefined) {\n\t\tsound.rate(rate);\n\t} else {\n\t\tsound.rate(rate, id);\n\t}\n}\n\n/** @inheritDoc */\nexport function stereo(sound_name: string): number;\n/** @inheritDoc */\nexport function stereo(sound_name: string, pan: number, id?: number): void;\n/**\n * Get or set the stereo panning for a sound.\n *\n * Call with just `sound_name` to read back the group's current pan;\n * call with a `pan` value (and optionally `id`) to write it.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param pan - Pan value, `-1.0` (full left) to `1.0` (full right).\n *   Omit to read the current value.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The current pan value when called as a getter; nothing when\n *   called as a setter.\n * @example\n * me.audio.stereo(\"cling\", -1);   // set\n * me.audio.stereo(\"cling\");        // read\n * @category Audio\n */\nexport function stereo(\n\tsound_name: string,\n\tpan?: number,\n\tid?: number,\n): number | void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (pan === undefined) {\n\t\t// Howler keeps the group pan at null until it's first set — the\n\t\t// documented return type is a number, so map that to centered\n\t\treturn sound.stereo() ?? 0;\n\t}\n\tsound.stereo(pan, id);\n}\n\n/** @inheritDoc */\nexport function position(sound_name: string): [number, number, number];\n/** @inheritDoc */\nexport function position(\n\tsound_name: string,\n\tx: number,\n\ty?: number,\n\tz?: number,\n\tid?: number,\n): void;\n/**\n * Get or set the 3D spatial position of a sound.\n *\n * Call with just `sound_name` to read back the group's current\n * position; call with `x` (and optionally `y` / `z` / `id`) to write\n * it. Missing `y` / `z` default to `0` and `-0.5` respectively.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param x - X-coordinate of the audio source. Omit to read.\n * @param y - Y-coordinate. Defaults to `0` when setting.\n * @param z - Z-coordinate. Defaults to `-0.5` when setting.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The current `[x, y, z]` when called as a getter; nothing\n *   when called as a setter.\n * @category Audio\n */\nexport function position(\n\tsound_name: string,\n\tx?: number,\n\ty?: number,\n\tz?: number,\n\tid?: number,\n): [number, number, number] | void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (x === undefined) {\n\t\t// Howler keeps the group position at null until it's first set — the\n\t\t// documented return type is a tuple, so map that to the origin\n\t\treturn sound.pos() ?? [0, 0, 0];\n\t}\n\tsound.pos(x, y, z, id);\n}\n\n/** @inheritDoc */\nexport function orientation(sound_name: string): [number, number, number];\n/** @inheritDoc */\nexport function orientation(\n\tsound_name: string,\n\tx: number,\n\ty?: number,\n\tz?: number,\n\tid?: number,\n): void;\n/**\n * Get or set the direction the audio source is pointing in 3D space.\n * Combined with the {@link PannerAttributes} cone settings, a sound\n * pointing away from the listener will be quieter or silent.\n *\n * Call with just `sound_name` to read back the group's current\n * orientation; call with `x` (and optionally `y` / `z` / `id`) to write\n * it.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param x - X-component of the orientation vector. Omit to read.\n * @param y - Y-component. Defaults to the current value when setting.\n * @param z - Z-component. Defaults to the current value when setting.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The current `[x, y, z]` when called as a getter; nothing\n *   when called as a setter.\n * @category Audio\n */\nexport function orientation(\n\tsound_name: string,\n\tx?: number,\n\ty?: number,\n\tz?: number,\n\tid?: number,\n): [number, number, number] | void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (x === undefined) {\n\t\treturn sound.orientation();\n\t}\n\tsound.orientation(x, y, z, id);\n}\n\n/**\n * Get or set the panner-node attributes for a sound or sound group.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param attributes - The {@link PannerAttributes} to apply (cone angles,\n *   distance model, panning algorithm, …). See the interface for\n *   per-field defaults.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are affected.\n * @returns The resulting {@link PannerAttributes} after the update.\n * @example\n * me.audio.panner(\"cling\", {\n *     panningModel: \"HRTF\",\n *     refDistance: 0.8,\n *     rolloffFactor: 2.5,\n *     distanceModel: \"exponential\",\n * });\n * @category Audio\n */\nexport function panner(\n\tsound_name: string,\n\tattributes?: PannerAttributes,\n\tid?: number,\n): PannerAttributes {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (attributes !== undefined) {\n\t\t// \"set\" overload returns the Howl for chaining; we still want\n\t\t// to hand the caller the current attribute snapshot back. Our\n\t\t// `distanceModel` covers the full WebAudio union (including\n\t\t// `\"exponential\"`) while Howler's declared parameter type only\n\t\t// lists `\"linear\" | \"inverse\"` — its runtime accepts all three.\n\t\t// Cast at the boundary so the type check passes; the upstream\n\t\t// `@types/howler` declaration is incomplete here.\n\t\tconst attrs = attributes as Parameters<Howl[\"pannerAttr\"]>[0];\n\t\tif (id !== undefined) sound.pannerAttr(attrs, id);\n\t\telse sound.pannerAttr(attrs);\n\t}\n\treturn id !== undefined ? sound.pannerAttr(id) : sound.pannerAttr();\n}\n\n/**\n * Stop the specified sound on all channels.\n * @param sound_name - Audio clip name (case-sensitive). When omitted,\n *   every sound currently playing is stopped.\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are stopped.\n * @example\n * me.audio.stop(\"cling\");\n * @category Audio\n */\nexport function stop(sound_name?: string, id?: number): void {\n\tif (sound_name === undefined) {\n\t\tstopAllPlayback();\n\t\treturn;\n\t}\n\tconst sound = getSoundOrThrow(sound_name);\n\tsound.stop(id);\n\t// remove the defined onend callback (if any defined)\n\tsound.off(\"end\", undefined, id);\n}\n\n/**\n * Pause the specified sound on all channels. Does not reset the\n * current playback position.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are paused.\n * @example\n * me.audio.pause(\"cling\");\n * @category Audio\n */\nexport function pause(sound_name: string, id?: number): void {\n\tgetSoundOrThrow(sound_name).pause(id);\n}\n\n/**\n * Resume the specified sound on all channels.\n * @param sound_name - Audio clip name (case-sensitive).\n * @param id - Sound instance ID. When omitted, all sounds in the group\n *   are resumed.\n * @example\n * // play an audio clip\n * let id = me.audio.play(\"myClip\");\n * // ...\n * // pause it\n * me.audio.pause(\"myClip\", id);\n * // ...\n * // resume\n * me.audio.resume(\"myClip\", id);\n * @category Audio\n */\nexport function resume(sound_name: string, id?: number): void {\n\tconst sound = getSoundOrThrow(sound_name);\n\tif (typeof id !== \"undefined\") {\n\t\tsound.play(id);\n\t\treturn;\n\t}\n\t// \"all sounds in the group are resumed\" (see JSDoc above): Howler's bare\n\t// play() only auto-resumes when EXACTLY ONE instance is paused — with two\n\t// or more (e.g. pause() without id pauses the whole group) it spawns a\n\t// brand-new instance from 0 and leaves the paused ones stuck. Howler has\n\t// no public instance list, so read _sounds directly (same justified\n\t// private access as the Howler `_muted` read in audio.ts).\n\tconst sounds = (\n\t\tsound as unknown as {\n\t\t\t_sounds: { _paused: boolean; _ended: boolean; _id: number }[];\n\t\t}\n\t)._sounds;\n\tconst paused = sounds.filter((s) => {\n\t\treturn s._paused && !s._ended;\n\t});\n\tif (paused.length === 0) {\n\t\t// nothing to resume — keep the legacy start-playback behavior\n\t\tsound.play();\n\t\treturn;\n\t}\n\tfor (const s of paused) {\n\t\tsound.play(s._id);\n\t}\n}\n","/**\n * Procedural audio — fire-and-forget envelope-shaped primitives that\n * build on the shared `AudioContext` exposed by `backend.ts`. Designed\n * for \"just play a beep / explosion / whoosh\" use cases where loading\n * an audio file would be overkill.\n *\n * Two primitives:\n *   - `tone` for pitched sources (clicks, chimes, lasers, chord pings).\n *   - `noise` for non-pitched sources (explosions, hi-hats, swooshes,\n *     wind, footsteps).\n *\n * Both share the same envelope + output routing rails so future\n * primitives can be added with zero copy-paste.\n */\n\nimport { getAudioContext, getMasterGain } from \"./backend.ts\";\nimport type { NoiseOptions, ToneOptions } from \"./types.ts\";\n\n// ---------------------------------------------------------------------\n// Procedural-audio internals — shared between `tone` and `noise`.\n// ---------------------------------------------------------------------\n\n/**\n * Resume the AudioContext if a browser autoplay policy has it\n * suspended. Best-effort and idempotent; the promise is intentionally\n * unawaited because we want the same call site to work in both gesture\n * and non-gesture contexts.\n * @ignore\n */\nfunction _resumeIfSuspended(ctx: AudioContext): void {\n\tif (ctx.state === \"suspended\") {\n\t\tctx.resume().catch(() => {\n\t\t\t/* ignore */\n\t\t});\n\t}\n}\n\n/**\n * Build the linear-attack / exponential-decay gain envelope used by\n * every procedural primitive.\n *\n * The decay uses an exponential ramp when there's enough headroom for\n * a smooth taper (peak gain greater than the `0.0001` near-silence\n * floor). Otherwise we use a linear ramp to `0`, which covers both\n * `gain === 0` (exp ramps reject a `0` start value with\n * `InvalidStateError`) AND tiny positive gains where a target of\n * `0.0001` would ramp UP and produce an audible click.\n * @ignore\n */\nfunction _buildGainEnvelope(\n\tctx: AudioContext,\n\tt0: number,\n\tt1: number,\n\tattack: number,\n\tduration: number,\n\tgain: number,\n): GainNode {\n\t// Clamp attack to `[0.001, duration / 2]`. For very short durations\n\t// (< 2 ms) the range is degenerate — the upper bound wins so the\n\t// envelope still fits inside the playback window.\n\tconst atk = Math.min(duration / 2, Math.max(0.001, attack));\n\tconst env = ctx.createGain();\n\tenv.gain.setValueAtTime(0, t0);\n\tenv.gain.linearRampToValueAtTime(gain, t0 + atk);\n\tif (gain > 0.0001) {\n\t\tenv.gain.exponentialRampToValueAtTime(0.0001, t1);\n\t} else {\n\t\tenv.gain.linearRampToValueAtTime(0, t1);\n\t}\n\treturn env;\n}\n\n/**\n * Final hop of any procedural primitive — route `source` to the audio\n * module's master gain (so `muteAll` / `setVolume` apply), optionally\n * through a `StereoPanner`. Falls back to `ctx.destination` only if the\n * master gain isn't available (very restricted audio envs).\n *\n * Returns the `StereoPannerNode` it created (or `null` if `pan === 0`)\n * so the caller can disconnect it once playback ends.\n * @ignore\n */\nfunction _connectToOutput(\n\tctx: AudioContext,\n\tsource: AudioNode,\n\tpan: number,\n\tt0: number,\n): StereoPannerNode | null {\n\tconst out = getMasterGain() ?? ctx.destination;\n\tif (pan === 0) {\n\t\tsource.connect(out);\n\t\treturn null;\n\t}\n\tconst panner = ctx.createStereoPanner();\n\tpanner.pan.setValueAtTime(Math.max(-1, Math.min(1, pan)), t0);\n\tsource.connect(panner).connect(out);\n\treturn panner;\n}\n\n/**\n * Fire a single-shot envelope-shaped oscillator on the shared\n * {@link getAudioContext} context. Designed for the \"just play a beep\"\n * niche where loading an audio file is overkill — UI clicks, hit\n * confirms, retro arcade-style cues, placeholder feedback during\n * prototyping.\n *\n * Multi-partial `freq` makes chimes, bells, and simple chords a single\n * call; `pitchSlide` covers percussive pitch-drops and rising stings.\n * The context is shared with file-based playback, so the usual browser\n * autoplay gating applies: the first call after a user gesture lets\n * every subsequent call play.\n *\n * **Requires WebAudio.** When WebAudio is not supported (or audio is\n * explicitly disabled) this is a silent no-op: {@link getAudioContext}\n * returns `null` and nothing is scheduled. Use the return value of\n * {@link getAudioContext} to detect that case up front if your game\n * wants to show a \"no audio\" badge or fall back to a different\n * feedback channel.\n * @param opts - the {@link ToneOptions} (frequency, duration,\n *   envelope, pan, slide). See the interface for per-field defaults.\n * @example\n * // simple UI click\n * me.audio.tone({ freq: 1200, duration: 0.08, pitchSlide: 0.5 });\n * // chime, panned right, two partials a fifth apart\n * me.audio.tone({ freq: [880, 1320], duration: 0.4, gain: 0.18, pan: 0.5 });\n * // descending \"thud\" — square wave with a wide pitch drop\n * me.audio.tone({ freq: 200, duration: 0.15, wave: \"square\", pitchSlide: 0.25 });\n * @category Audio\n */\nexport function tone(opts: ToneOptions): void {\n\tconst ctx = getAudioContext();\n\tif (!ctx) return;\n\n\tconst {\n\t\tfreq,\n\t\tduration,\n\t\twave = \"sine\",\n\t\tgain = 0.1,\n\t\tattack = 0.005,\n\t\tpan = 0,\n\t\tpitchSlide = 1,\n\t} = opts;\n\n\tconst freqs = Array.isArray(freq) ? freq : [freq];\n\t// Empty partial list = nothing to play. Bail before building any\n\t// nodes so we don't leave an env/panner connected forever (no\n\t// oscillator would ever fire `onended` to clean them up).\n\tif (freqs.length === 0) return;\n\n\t_resumeIfSuspended(ctx);\n\n\tconst dur = Math.max(0.001, duration);\n\tconst t0 = ctx.currentTime;\n\tconst t1 = t0 + dur;\n\tconst env = _buildGainEnvelope(ctx, t0, t1, attack, dur, gain);\n\tconst panner = _connectToOutput(ctx, env, pan, t0);\n\n\t// Count oscillators down to zero so the LAST one to end is the one\n\t// that disconnects the shared envelope + panner — otherwise we'd\n\t// leave the graph half-wired.\n\tlet remaining = freqs.length;\n\tfor (const f of freqs) {\n\t\tconst osc = ctx.createOscillator();\n\t\tosc.type = wave;\n\t\tosc.frequency.setValueAtTime(f, t0);\n\t\t// Pitch slide also goes through `exponentialRampToValueAtTime`,\n\t\t// which needs the starting frequency to be > 0. Skip the ramp\n\t\t// when `f <= 0` (silent / DC) so it can't throw.\n\t\tif (pitchSlide !== 1 && f > 0) {\n\t\t\tosc.frequency.exponentialRampToValueAtTime(\n\t\t\t\tMath.max(0.01, f * pitchSlide),\n\t\t\t\tt1,\n\t\t\t);\n\t\t}\n\t\tosc.connect(env);\n\t\tosc.start(t0);\n\t\t// Small tail so the exponential ramp has room to settle before\n\t\t// the node is torn down.\n\t\tosc.stop(t1 + 0.02);\n\t\tosc.onended = () => {\n\t\t\tosc.disconnect();\n\t\t\tif (--remaining === 0) {\n\t\t\t\tenv.disconnect();\n\t\t\t\tpanner?.disconnect();\n\t\t\t}\n\t\t};\n\t}\n}\n\n/**\n * Fill a buffer in-place with single-channel noise samples of the\n * requested spectral colour. Pink uses Paul Kellet's refined coefficients;\n * brown is a leaky integrator over white. Output is roughly normalised\n * to `[-1, 1]` for both coloured variants.\n * @ignore\n */\nfunction fillNoiseBuffer(\n\tdata: Float32Array,\n\ttype: \"white\" | \"pink\" | \"brown\",\n): void {\n\tconst n = data.length;\n\tif (type === \"white\") {\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tdata[i] = Math.random() * 2 - 1;\n\t\t}\n\t\treturn;\n\t}\n\tif (type === \"pink\") {\n\t\tlet b0 = 0;\n\t\tlet b1 = 0;\n\t\tlet b2 = 0;\n\t\tlet b3 = 0;\n\t\tlet b4 = 0;\n\t\tlet b5 = 0;\n\t\tlet b6 = 0;\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst w = Math.random() * 2 - 1;\n\t\t\tb0 = 0.99886 * b0 + w * 0.0555179;\n\t\t\tb1 = 0.99332 * b1 + w * 0.0750759;\n\t\t\tb2 = 0.969 * b2 + w * 0.153852;\n\t\t\tb3 = 0.8665 * b3 + w * 0.3104856;\n\t\t\tb4 = 0.55 * b4 + w * 0.5329522;\n\t\t\tb5 = -0.7616 * b5 - w * 0.016898;\n\t\t\tdata[i] = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + w * 0.5362) * 0.11;\n\t\t\tb6 = w * 0.115926;\n\t\t}\n\t\treturn;\n\t}\n\t// brown: leaky integrator over white, rescaled into audible range.\n\tlet last = 0;\n\tfor (let i = 0; i < n; i++) {\n\t\tconst w = Math.random() * 2 - 1;\n\t\tlast = (last + 0.02 * w) / 1.02;\n\t\tdata[i] = last * 3.5;\n\t}\n}\n\n/**\n * Fire a single-shot envelope-shaped noise burst on the shared\n * {@link getAudioContext} context. Sits alongside {@link tone} as the\n * non-pitched half of the procedural-audio surface — `tone` is the right\n * tool for anything with a clear pitch (clicks, chimes, lasers), `noise`\n * is the right tool for anything percussive without one (explosions,\n * hi-hats, swooshes, footsteps, wind).\n *\n * The output runs through the master gain shared with file-based\n * playback, so {@link muteAll} / {@link setVolume} apply uniformly.\n * Browser autoplay gating applies — the first call after a user\n * gesture lets every subsequent call play.\n *\n * **Requires WebAudio.** When WebAudio is not supported (or audio is\n * explicitly disabled) this is a silent no-op: {@link getAudioContext}\n * returns `null` and nothing is scheduled.\n * @param opts - the {@link NoiseOptions} (duration, spectral colour,\n *   envelope, pan, optional filter + sweep). See the interface for\n *   per-field defaults.\n * @example\n * // Explosion: brown rumble closing into a thud\n * me.audio.noise({\n *     duration: 0.8,\n *     type: \"brown\",\n *     gain: 0.4,\n *     filter: { type: \"lowpass\", frequency: 800 },\n *     filterSweep: 0.3,\n * });\n * // Hi-hat: short, bright, top-end only\n * me.audio.noise({\n *     duration: 0.05,\n *     filter: { type: \"highpass\", frequency: 7000 },\n *     gain: 0.2,\n * });\n * // Swoosh: bandpass white with rising sweep — UI transition, melee whoosh\n * me.audio.noise({\n *     duration: 0.3,\n *     filter: { type: \"bandpass\", frequency: 400, Q: 1.5 },\n *     filterSweep: 4,\n *     gain: 0.3,\n * });\n * // Wind / breath: long, low-pink, no sweep\n * me.audio.noise({\n *     duration: 2,\n *     type: \"pink\",\n *     filter: { type: \"bandpass\", frequency: 600 },\n *     gain: 0.08,\n * });\n * // Footstep on dirt — short brown thump\n * me.audio.noise({\n *     duration: 0.08,\n *     type: \"brown\",\n *     filter: { type: \"lowpass\", frequency: 200 },\n *     gain: 0.25,\n * });\n * @category Audio\n */\nexport function noise(opts: NoiseOptions): void {\n\tconst ctx = getAudioContext();\n\tif (!ctx) return;\n\n\tconst {\n\t\tduration,\n\t\ttype = \"white\",\n\t\tgain = 0.1,\n\t\tattack = 0.005,\n\t\tpan = 0,\n\t\tfilter,\n\t\tfilterSweep = 1,\n\t} = opts;\n\n\t_resumeIfSuspended(ctx);\n\n\tconst dur = Math.max(0.001, duration);\n\tconst t0 = ctx.currentTime;\n\tconst t1 = t0 + dur;\n\tconst env = _buildGainEnvelope(ctx, t0, t1, attack, dur, gain);\n\n\t// Source: a one-shot buffer of `dur * sampleRate` random samples.\n\tconst sampleCount = Math.max(1, Math.ceil(dur * ctx.sampleRate));\n\tconst buffer = ctx.createBuffer(1, sampleCount, ctx.sampleRate);\n\tfillNoiseBuffer(buffer.getChannelData(0), type);\n\tconst src = ctx.createBufferSource();\n\tsrc.buffer = buffer;\n\tsrc.connect(env);\n\n\t// Optional band-shaping filter (with optional sweep on its frequency).\n\tlet tail: AudioNode = env;\n\tif (filter !== undefined) {\n\t\tconst biquad = ctx.createBiquadFilter();\n\t\tbiquad.type = filter.type;\n\t\tbiquad.frequency.setValueAtTime(filter.frequency, t0);\n\t\tif (filter.Q !== undefined) {\n\t\t\tbiquad.Q.setValueAtTime(filter.Q, t0);\n\t\t}\n\t\t// Filter sweep also goes through `exponentialRampToValueAtTime`\n\t\t// — only schedule the ramp when both start and target are > 0.\n\t\tif (filterSweep !== 1 && filter.frequency > 0) {\n\t\t\tbiquad.frequency.exponentialRampToValueAtTime(\n\t\t\t\tMath.max(0.01, filter.frequency * filterSweep),\n\t\t\t\tt1,\n\t\t\t);\n\t\t}\n\t\ttail.connect(biquad);\n\t\ttail = biquad;\n\t}\n\n\tconst panner = _connectToOutput(ctx, tail, pan, t0);\n\n\tsrc.start(t0);\n\tsrc.stop(t1 + 0.02);\n\tsrc.onended = () => {\n\t\tsrc.disconnect();\n\t\tenv.disconnect();\n\t\t// If a filter was inserted between env and the output, tail !== env.\n\t\tif (tail !== env) tail.disconnect();\n\t\tpanner?.disconnect();\n\t};\n}\n","/**\n * display a deprecation warning in the console\n * @param {string} deprecated - deprecated class,function or property name\n * @param {string} replacement - the replacement class, function, or property name\n * @param {string} version - the version since when the lass,function or property is deprecated\n */\nexport function warning(deprecated, replacement, version) {\n\tconst msg = \"melonJS: %s is deprecated since version %s, please use %s\";\n\tconst stack = new Error().stack;\n\n\tif (console.groupCollapsed) {\n\t\tconsole.groupCollapsed(\n\t\t\t\"%c\" + msg,\n\t\t\t\"font-weight:normal;color:yellow;\",\n\t\t\tdeprecated,\n\t\t\tversion,\n\t\t\treplacement,\n\t\t);\n\t} else {\n\t\tconsole.warn(msg, deprecated, version, replacement);\n\t}\n\n\tif (typeof stack !== \"undefined\") {\n\t\tconsole.warn(stack);\n\t}\n\n\tif (console.groupCollapsed) {\n\t\tconsole.groupEnd();\n\t}\n}\n","import { clamp } from \"../math/math.js\";\nimport state from \"../state/state.ts\";\nimport { defer } from \"./../utils/function.js\";\nimport {\n\tBOOT,\n\tGAME_BEFORE_UPDATE,\n\ton,\n\tonce,\n\tSTATE_CHANGE,\n\tSTATE_RESTART,\n\tSTATE_RESUME,\n} from \"./event.js\";\n\n/**\n * a Timer class to manage timing related function (FPS, Game Tick, Time...)\n * @see {@link timer} the default global timer instance\n */\nclass Timer {\n\ttick: number;\n\tfps: number;\n\tmaxfps: number;\n\tinterpolation: boolean;\n\tframecount: number;\n\tframedelta: number;\n\tlast: number;\n\tnow: number;\n\tdelta: number;\n\tstep: number;\n\tminstep: number;\n\ttimers: {\n\t\tfn: (...args: any[]) => any;\n\t\tdelay: number;\n\t\telapsed: number;\n\t\trepeat: boolean;\n\t\ttimerId: number;\n\t\tpauseable: boolean;\n\t\targs: any[];\n\t}[];\n\ttimerId: number;\n\n\tconstructor() {\n\t\t/**\n\t\t * Last game tick value. <br>\n\t\t * Use this value to scale velocities during frame drops due to slow hardware or when setting an FPS limit.\n\t\t * This feature is disabled by default (Enable interpolation to use it).\n\t\t * @see interpolation\n\t\t */\n\t\tthis.tick = 1.0;\n\n\t\t/**\n\t\t * Last measured fps rate.<br>\n\t\t * This feature is disabled by default, unless the debugPanel is enabled/visible.\n\t\t */\n\t\tthis.fps = 0;\n\n\t\t/**\n\t\t * Set the maximum target display frame per second\n\t\t * @see tick\n\t\t * @default 60\n\t\t */\n\t\tthis.maxfps = 60;\n\n\t\t/**\n\t\t * Enable/disable frame interpolation\n\t\t * @see tick\n\t\t * @default false\n\t\t */\n\t\tthis.interpolation = false;\n\n\t\t//hold element to display fps\n\t\tthis.framecount = 0;\n\t\tthis.framedelta = 0;\n\n\t\t/* fps count stuff */\n\t\tthis.last = 0;\n\t\tthis.now = 0;\n\t\tthis.delta = 0;\n\t\t// for timeout/interval update\n\t\tthis.step = 0;\n\t\tthis.minstep = 0;\n\n\t\t// list of defined timer function\n\t\tthis.timers = [];\n\t\tthis.timerId = 0;\n\n\t\t// Initialize timer on Boot event\n\t\tonce(BOOT, () => {\n\t\t\t// reset variables to initial state\n\t\t\tthis.reset();\n\t\t\tthis.now = this.last = 0;\n\t\t\t// register to the game before update event\n\t\t\ton(GAME_BEFORE_UPDATE, (time) => {\n\t\t\t\tthis.update(time);\n\t\t\t});\n\t\t});\n\n\t\t// reset timer\n\t\ton(STATE_RESUME, () => {\n\t\t\tthis.reset();\n\t\t});\n\t\ton(STATE_RESTART, () => {\n\t\t\tthis.reset();\n\t\t});\n\t\ton(STATE_CHANGE, () => {\n\t\t\tthis.reset();\n\t\t});\n\t}\n\n\t/**\n\t * reset time (e.g. usefull in case of pause)\n\t * @ignore\n\t */\n\treset() {\n\t\t// set to \"now\"\n\t\tthis.last = this.now = globalThis.performance.now();\n\t\tthis.delta = 0;\n\t\t// reset delta counting variables\n\t\tthis.framedelta = 0;\n\t\tthis.framecount = 0;\n\t\tthis.step = 1000 / this.maxfps;\n\t\tthis.minstep = (1000 / this.maxfps) * 1.25;\n\t}\n\n\t/**\n\t * Calls a function once after a specified delay. See me.timer.setInterval to repeativly call a function.\n\t * @param fn - the function you want to execute after delay milliseconds.\n\t * @param delay - the number of milliseconds (thousandths of a second) that the function call should be delayed by.\n\t * @param [pauseable] - respects the pause state of the engine.\n\t * @param args - optional parameters which are passed through to the function specified by fn once the timer expires.\n\t * @returns a positive integer value which identifies the timer created by the call to setTimeout(), which can be used later with me.timer.clearTimeout().\n\t * @example\n\t * // set a timer to call \"myFunction\" after 1000ms\n\t * me.timer.setTimeout(myFunction, 1000);\n\t * // set a timer to call \"myFunction\" after 1000ms (respecting the pause state) and passing param1 and param2\n\t * me.timer.setTimeout(myFunction, 1000, true, param1, param2);\n\t */\n\tsetTimeout(\n\t\tfn: (...args: any[]) => any,\n\t\tdelay: number,\n\t\tpauseable?: boolean,\n\t\t...args: any[]\n\t) {\n\t\tthis.timers.push({\n\t\t\tfn: fn,\n\t\t\tdelay: delay,\n\t\t\telapsed: 0,\n\t\t\trepeat: false,\n\t\t\ttimerId: ++this.timerId,\n\t\t\tpauseable: pauseable ?? true,\n\t\t\targs: args,\n\t\t});\n\t\treturn this.timerId;\n\t}\n\n\t/**\n\t * Calls a function continously at the specified interval.  See setTimeout to call function a single time.\n\t * @param fn - the function to execute\n\t * @param delay - the number of milliseconds (thousandths of a second) on how often to execute the function\n\t * @param [pauseable] - respects the pause state of the engine.\n\t * @param args - optional parameters which are passed through to the function specified by fn once the timer expires.\n\t * @returns a numeric, non-zero value which identifies the timer created by the call to setInterval(), which can be used later with me.timer.clearInterval().\n\t * @example\n\t * // set a timer to call \"myFunction\" every 1000ms\n\t * me.timer.setInterval(myFunction, 1000);\n\t * // set a timer to call \"myFunction\" every 1000ms (respecting the pause state) and passing param1 and param2\n\t * me.timer.setInterval(myFunction, 1000, true, param1, param2);\n\t */\n\tsetInterval(\n\t\tfn: (...args: any[]) => any,\n\t\tdelay: number,\n\t\tpauseable?: boolean,\n\t\t...args: any[]\n\t) {\n\t\tthis.timers.push({\n\t\t\tfn: fn,\n\t\t\tdelay: delay,\n\t\t\telapsed: 0,\n\t\t\trepeat: true,\n\t\t\ttimerId: ++this.timerId,\n\t\t\tpauseable: pauseable === true || true,\n\t\t\targs: args,\n\t\t});\n\t\treturn this.timerId;\n\t}\n\n\t/**\n\t * Cancels a timeout previously established by calling setTimeout().\n\t * @param timeoutID - ID of the timeout to be cancelled\n\t */\n\tclearTimeout(timeoutID: number) {\n\t\tif (timeoutID > 0) {\n\t\t\tdefer(() => {\n\t\t\t\tthis.clearTimer(timeoutID);\n\t\t\t}, this);\n\t\t}\n\t}\n\n\t/**\n\t * cancels the timed, repeating action which was previously established by a call to setInterval().\n\t * @param intervalID - ID of the interval to be cleared\n\t */\n\tclearInterval(intervalID: number) {\n\t\tif (intervalID > 0) {\n\t\t\tdefer(() => {\n\t\t\t\tthis.clearTimer(intervalID);\n\t\t\t}, this);\n\t\t}\n\t}\n\n\t/**\n\t * Return the current timestamp in milliseconds <br>\n\t * since the game has started or since linux epoch (based on browser support for High Resolution Timer)\n\t * @returns current time\n\t */\n\tgetTime() {\n\t\treturn this.now;\n\t}\n\n\t/**\n\t * Return elapsed time in milliseconds since the last update\n\t * @returns elapsed time\n\t */\n\tgetDelta() {\n\t\treturn this.delta;\n\t}\n\n\t/**\n\t * update\n\t * @ignore\n\t */\n\tupdate(time: number) {\n\t\tthis.last = this.now;\n\t\tthis.now = time;\n\t\tthis.delta = this.now - this.last;\n\n\t\t// fix for negative timestamp returned by wechat or chrome on startup\n\t\tif (this.delta < 0) {\n\t\t\tthis.delta = 0;\n\t\t}\n\n\t\t// get the game tick\n\t\tthis.tick =\n\t\t\tthis.delta > this.minstep && this.interpolation\n\t\t\t\t? this.delta / this.step\n\t\t\t\t: 1;\n\n\t\t// update the running FPS estimate (averaged over the last 10 frames)\n\t\tthis.framecount++;\n\t\tthis.framedelta += this.delta;\n\t\tif (this.framecount % 10 === 0) {\n\t\t\tthis.fps = clamp(\n\t\t\t\tMath.round((1000 * this.framecount) / this.framedelta),\n\t\t\t\t0,\n\t\t\t\tthis.maxfps,\n\t\t\t);\n\t\t\tthis.framedelta = 0;\n\t\t\tthis.framecount = 0;\n\t\t}\n\n\t\tthis.updateTimers();\n\t}\n\n\t/**\n\t * clear Timers\n\t * @ignore\n\t */\n\tclearTimer(timerId: number) {\n\t\tfor (let i = 0; i < this.timers.length; i++) {\n\t\t\tif (this.timers[i].timerId === timerId) {\n\t\t\t\tthis.timers.splice(i, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * update timers\n\t * @ignore\n\t */\n\tupdateTimers() {\n\t\tfor (const timer of this.timers) {\n\t\t\tif (!(timer.pauseable && state.isPaused())) {\n\t\t\t\ttimer.elapsed += this.delta;\n\t\t\t}\n\t\t\tif (timer.elapsed >= timer.delay) {\n\t\t\t\ttimer.fn(...timer.args);\n\n\t\t\t\tif (timer.repeat) {\n\t\t\t\t\ttimer.elapsed -= timer.delay;\n\t\t\t\t} else {\n\t\t\t\t\tthis.clearTimer(timer.timerId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst timer = new Timer();\n\n/**\n * the default global Timer instance\n * @see Timer\n * @example\n * // set a timer to call \"myFunction\" after 1000ms\n * timer.setTimeout(myFunction, 1000);\n * // set a timer to call \"myFunction\" after 1000ms (respecting the pause state) and passing param1 and param2\n * timer.setTimeout(myFunction, 1000, true, param1, param2);\n * // set a timer to call \"myFunction\" every 1000ms\n * timer.setInterval(myFunction, 1000);\n * // set a timer to call \"myFunction\" every 1000ms (respecting the pause state) and passing param1 and param2\n * timer.setInterval(myFunction, 1000, true, param1, param2);\n */\nexport default timer;\n","/*\n * Tween.js - Licensed under the MIT license\n * https://github.com/tweenjs/tween.js\n */\n\n/**\n * Easing functions for use with {@link Tween}.\n * Each family provides `In` (accelerate), `Out` (decelerate), and `InOut` (both) variants.\n *\n * Available families:\n * - `Linear.None` — constant speed\n * - `Quadratic` — power of 2\n * - `Cubic` — power of 3\n * - `Quartic` — power of 4\n * - `Quintic` — power of 5\n * - `Sinusoidal` — sine wave\n * - `Exponential` — base-2 exponential\n * - `Circular` — circular arc\n * - `Elastic` — spring overshoot\n * - `Back` — slight overshoot before settling\n * - `Bounce` — bouncing ball effect\n * @example\n * // use with Tween\n * new me.Tween(obj).to({ x: 100 }, {\n *     duration: 1000,\n *     easing: me.Tween.Easing.Bounce.Out,\n * }).start();\n * @see https://easings.net/ for visual reference\n * @category Tweens\n */\n\nexport type EasingFunction = (t: number) => number;\n\nexport const Easing = {\n\tLinear: {\n\t\tNone: (k: number) => {\n\t\t\treturn k;\n\t\t},\n\t},\n\n\tQuadratic: {\n\t\tIn: (k: number) => {\n\t\t\treturn k * k;\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn k * (2 - k);\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * k * k;\n\t\t\t}\n\t\t\treturn -0.5 * (--k * (k - 2) - 1);\n\t\t},\n\t},\n\n\tCubic: {\n\t\tIn: (k: number) => {\n\t\t\treturn k * k * k;\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn --k * k * k + 1;\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * k * k * k;\n\t\t\t}\n\t\t\treturn 0.5 * ((k -= 2) * k * k + 2);\n\t\t},\n\t},\n\n\tQuartic: {\n\t\tIn: (k: number) => {\n\t\t\treturn k * k * k * k;\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn 1 - --k * k * k * k;\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * k * k * k * k;\n\t\t\t}\n\t\t\treturn -0.5 * ((k -= 2) * k * k * k - 2);\n\t\t},\n\t},\n\n\tQuintic: {\n\t\tIn: (k: number) => {\n\t\t\treturn k * k * k * k * k;\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn --k * k * k * k * k + 1;\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * k * k * k * k * k;\n\t\t\t}\n\t\t\treturn 0.5 * ((k -= 2) * k * k * k * k + 2);\n\t\t},\n\t},\n\n\tSinusoidal: {\n\t\tIn: (k: number) => {\n\t\t\treturn 1 - Math.cos((k * Math.PI) / 2);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn Math.sin((k * Math.PI) / 2);\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\treturn 0.5 * (1 - Math.cos(Math.PI * k));\n\t\t},\n\t},\n\n\tExponential: {\n\t\tIn: (k: number) => {\n\t\t\treturn k === 0 ? 0 : Math.pow(1024, k - 1);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif (k === 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (k === 1) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * Math.pow(1024, k - 1);\n\t\t\t}\n\t\t\treturn 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\n\t\t},\n\t},\n\n\tCircular: {\n\t\tIn: (k: number) => {\n\t\t\treturn 1 - Math.sqrt(1 - k * k);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\treturn Math.sqrt(1 - --k * k);\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn -0.5 * (Math.sqrt(1 - k * k) - 1);\n\t\t\t}\n\t\t\treturn 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\n\t\t},\n\t},\n\n\tElastic: {\n\t\tIn: (k: number) => {\n\t\t\tif (k === 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (k === 1) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\tif (k === 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (k === 1) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1;\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif (k === 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (k === 1) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tk *= 2;\n\t\t\tif (k < 1) {\n\t\t\t\treturn (\n\t\t\t\t\t-0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1\n\t\t\t);\n\t\t},\n\t},\n\n\tBack: {\n\t\tIn: (k: number) => {\n\t\t\tconst s = 1.70158;\n\t\t\treturn k * k * ((s + 1) * k - s);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\tconst s = 1.70158;\n\t\t\treturn --k * k * ((s + 1) * k + s) + 1;\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tconst s = 1.70158 * 1.525;\n\t\t\tif ((k *= 2) < 1) {\n\t\t\t\treturn 0.5 * (k * k * ((s + 1) * k - s));\n\t\t\t}\n\t\t\treturn 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\n\t\t},\n\t},\n\n\tBounce: {\n\t\tIn: (k: number) => {\n\t\t\treturn 1 - Easing.Bounce.Out(1 - k);\n\t\t},\n\t\tOut: (k: number) => {\n\t\t\tif (k < 1 / 2.75) {\n\t\t\t\treturn 7.5625 * k * k;\n\t\t\t} else if (k < 2 / 2.75) {\n\t\t\t\treturn 7.5625 * (k -= 1.5 / 2.75) * k + 0.75;\n\t\t\t} else if (k < 2.5 / 2.75) {\n\t\t\t\treturn 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375;\n\t\t\t} else {\n\t\t\t\treturn 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375;\n\t\t\t}\n\t\t},\n\t\tInOut: (k: number) => {\n\t\t\tif (k < 0.5) {\n\t\t\t\treturn Easing.Bounce.In(k * 2) * 0.5;\n\t\t\t}\n\t\t\treturn Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;\n\t\t},\n\t},\n};\n","/*\n * Tween.js - Licensed under the MIT license\n * https://github.com/tweenjs/tween.js\n */\n\nconst linear = (p0: number, p1: number, t: number) => {\n\treturn (p1 - p0) * t + p0;\n};\n\nconst factorialCache = [1];\n\nconst factorial = (n: number) => {\n\tif (factorialCache[n]) {\n\t\treturn factorialCache[n];\n\t}\n\tlet s = 1;\n\tfor (let i = n; i > 1; i--) {\n\t\ts *= i;\n\t}\n\tfactorialCache[n] = s;\n\treturn s;\n};\n\nconst bernstein = (n: number, i: number) => {\n\treturn factorial(n) / factorial(i) / factorial(n - i);\n};\n\nconst catmullRom = (\n\tp0: number,\n\tp1: number,\n\tp2: number,\n\tp3: number,\n\tt: number,\n) => {\n\tconst v0 = (p2 - p0) * 0.5;\n\tconst v1 = (p3 - p1) * 0.5;\n\tconst t2 = t * t;\n\tconst t3 = t * t2;\n\treturn (\n\t\t(2 * p1 - 2 * p2 + v0 + v1) * t3 +\n\t\t(-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 +\n\t\tv0 * t +\n\t\tp1\n\t);\n};\n\nexport type InterpolationFunction = (v: number[], k: number) => number;\n\n/**\n * Interpolation functions for tweening through arrays of values.\n * Used when a tween target property is an array (e.g. a path of waypoints).\n *\n * Available functions:\n * - `Linear` — straight-line interpolation between consecutive values\n * - `Bezier` — smooth Bezier curve through all values\n * - `CatmullRom` — smooth Catmull-Rom spline through all values (best for paths)\n * @example\n * // tween through waypoints using CatmullRom spline\n * new me.Tween(obj).to({ x: [100, 200, 300, 400] }, {\n *     duration: 2000,\n *     interpolation: me.Tween.Interpolation.CatmullRom,\n * }).start();\n * @see {@link Tween}\n * @category Tweens\n */\nexport const Interpolation = {\n\t/**\n\t * Piecewise linear interpolation between consecutive array values.\n\t * @param v - array of values\n\t * @param k - interpolation factor (0 to 1)\n\t */\n\tLinear: (v: number[], k: number) => {\n\t\tconst m = v.length - 1;\n\t\tconst f = m * k;\n\t\tconst i = Math.floor(f);\n\n\t\tif (k < 0) {\n\t\t\treturn linear(v[0], v[1], f);\n\t\t}\n\t\tif (k > 1) {\n\t\t\treturn linear(v[m], v[m - 1], m - f);\n\t\t}\n\n\t\treturn linear(v[i], v[i + 1 > m ? m : i + 1], f - i);\n\t},\n\t/**\n\t * Smooth Bezier curve interpolation through all array values.\n\t * @param v - array of values\n\t * @param k - interpolation factor (0 to 1)\n\t */\n\tBezier: (v: number[], k: number) => {\n\t\tlet b = 0;\n\t\tconst n = v.length - 1;\n\n\t\tfor (let i = 0; i <= n; i++) {\n\t\t\tb += Math.pow(1 - k, n - i) * Math.pow(k, i) * v[i] * bernstein(n, i);\n\t\t}\n\n\t\treturn b;\n\t},\n\t/**\n\t * Smooth Catmull-Rom spline interpolation — best for path-following tweens.\n\t * @param v - array of values\n\t * @param k - interpolation factor (0 to 1)\n\t */\n\tCatmullRom: (v: number[], k: number) => {\n\t\tconst m = v.length - 1;\n\t\tlet f = m * k;\n\t\tlet i = Math.floor(f);\n\n\t\tif (v[0] === v[m]) {\n\t\t\tif (k < 0) {\n\t\t\t\ti = Math.floor((f = m * (1 + k)));\n\t\t\t}\n\n\t\t\treturn catmullRom(\n\t\t\t\tv[(i - 1 + m) % m],\n\t\t\t\tv[i],\n\t\t\t\tv[(i + 1) % m],\n\t\t\t\tv[(i + 2) % m],\n\t\t\t\tf - i,\n\t\t\t);\n\t\t} else {\n\t\t\tif (k < 0) {\n\t\t\t\treturn v[0] - (catmullRom(v[0], v[0], v[1], v[1], -f) - v[0]);\n\t\t\t}\n\t\t\tif (k > 1) {\n\t\t\t\treturn (\n\t\t\t\t\tv[m] - (catmullRom(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m])\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn catmullRom(\n\t\t\t\tv[i ? i - 1 : 0],\n\t\t\t\tv[i],\n\t\t\t\tv[m < i + 1 ? m : i + 1],\n\t\t\t\tv[m < i + 2 ? m : i + 2],\n\t\t\t\tf - i,\n\t\t\t);\n\t\t}\n\t},\n};\n","import {\n\tGAME_AFTER_UPDATE,\n\tGAME_RESET,\n\toff,\n\ton,\n\tSTATE_PAUSE,\n\tSTATE_RESUME,\n\tTICK,\n} from \"../system/event.js\";\nimport { createPool } from \"../system/pool.ts\";\nimport timer from \"../system/timer.js\";\nimport { Easing, EasingFunction } from \"./easing.js\";\nimport { Interpolation, InterpolationFunction } from \"./interpolation.js\";\n\n/*\n * Tween.js - Licensed under the MIT license\n * https://github.com/tweenjs/tween.js\n */\n\n/** @ignore */\ntype OnStartCallback<T> = (this: T) => void;\n/** @ignore */\ntype OnUpdateCallback<T> = (this: T, value: number) => void;\n/** @ignore */\ntype OnCompleteCallback<T> = (this: T) => void;\n\n/**\n * A tweening engine for smoothly interpolating object properties over time.\n * Based on <a href=\"https://github.com/sole/Tween.js\">tween.js</a> with\n * optimised Robert Penner's equations.\n *\n * Tweens use an event-based lifecycle — on `start()` the tween subscribes to\n * the game loop events (`TICK`, `GAME_AFTER_UPDATE`, `STATE_PAUSE`,\n * `STATE_RESUME`, `GAME_RESET`)\n * and automatically unsubscribes on completion or `stop()`.\n * They do not need to be added to a container.\n * @example\n * // basic usage\n * new me.Tween(myObject.pos)\n *     .to({ x: 200, y: 200 }, { duration: 3000, easing: me.Tween.Easing.Bounce.Out })\n *     .onComplete(() => console.log(\"done!\"))\n *     .start();\n * @example\n * // auto-start with options\n * new me.Tween(myObject.pos).to({ x: 200 }, {\n *     duration: 1000,\n *     easing: me.Tween.Easing.Quadratic.InOut,\n *     yoyo: true,\n *     repeat: Infinity,\n *     autoStart: true,\n * });\n * @category Tweens\n */\nexport default class Tween {\n\t_object: object;\n\t_valuesStart: Record<string, unknown>;\n\t_valuesEnd: Record<string, unknown>;\n\t_valuesStartRepeat: Record<string, unknown>;\n\t_duration: number;\n\t_repeat: number;\n\t_yoyo: boolean;\n\t_reversed: boolean;\n\t_delayTime: number;\n\t_repeatDelayTime: number | undefined;\n\t_startTime: number | null;\n\t_easingFunction: EasingFunction;\n\t_interpolationFunction: InterpolationFunction;\n\t_chainedTweens: Array<Tween>;\n\t_onStartCallback: OnStartCallback<object> | null;\n\t_onStartCallbackFired: boolean;\n\t_onUpdateCallback: OnUpdateCallback<object> | null;\n\t_onCompleteCallback: OnCompleteCallback<object> | null;\n\t_tweenTimeTracker: number;\n\t_lastUpdate: number;\n\t_isRunning: boolean;\n\t_isPaused: boolean;\n\t_lastTick: number;\n\n\t/**\n\t * whether the tween should persist across state changes (not auto-stopped on game reset)\n\t * @default false\n\t */\n\tisPersistent: boolean;\n\n\t/**\n\t * whether the tween should keep running when the game is paused\n\t * @default false\n\t */\n\tupdateWhenPaused: boolean;\n\n\t/**\n\t * @param object - the object whose properties will be tweened\n\t */\n\tconstructor(object: object) {\n\t\tthis.setProperties(object);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tonResetEvent(object: object) {\n\t\tthis.setProperties(object);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tsetProperties(object: object) {\n\t\t// ensure any running tween is stopped before resetting (e.g., pool reuse)\n\t\tthis._unsubscribe();\n\n\t\tthis._object = object;\n\t\tthis._valuesStart = {};\n\t\tthis._valuesEnd = {};\n\t\tthis._valuesStartRepeat = {};\n\t\tthis._duration = 1000;\n\t\tthis._repeat = 0;\n\t\tthis._yoyo = false;\n\t\tthis._reversed = false;\n\t\tthis._delayTime = 0;\n\t\tthis._repeatDelayTime = undefined;\n\t\tthis._startTime = null;\n\t\tthis._easingFunction = Easing.Linear.None;\n\t\tthis._interpolationFunction = Interpolation.Linear;\n\t\tthis._chainedTweens = [];\n\t\tthis._onStartCallback = null;\n\t\tthis._onStartCallbackFired = false;\n\t\tthis._onUpdateCallback = null;\n\t\tthis._onCompleteCallback = null;\n\t\t// track the last update timestamp from the game loop\n\t\tthis._lastUpdate = globalThis.performance.now();\n\t\tthis._tweenTimeTracker = this._lastUpdate;\n\t\tthis._isRunning = false;\n\t\tthis._isPaused = false;\n\t\tthis._lastTick = globalThis.performance.now();\n\t\tthis.isPersistent = false;\n\t\tthis.updateWhenPaused = false;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\t_resumeCallback(elapsed: number) {\n\t\tthis._isPaused = false;\n\t\tif (this._startTime !== null && !this.updateWhenPaused) {\n\t\t\tthis._startTime += elapsed;\n\t\t}\n\t}\n\n\t/** @ignore */\n\t_onAfterUpdate(lastUpdate: number) {\n\t\tthis._lastUpdate = lastUpdate;\n\t}\n\n\t/** @ignore */\n\t_onTick(timestamp: number) {\n\t\tif (!this._isPaused || this.updateWhenPaused) {\n\t\t\t// compute delta from the raw RAF timestamp\n\t\t\tconst dt = timestamp - this._lastTick;\n\t\t\tthis._lastTick = timestamp;\n\t\t\tif (dt > 0 && dt < 1000) {\n\t\t\t\tthis.update(dt);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** @ignore */\n\t_onPause() {\n\t\tthis._isPaused = true;\n\t}\n\n\t/** @ignore */\n\t_onReset() {\n\t\tif (!this.isPersistent) {\n\t\t\tthis.stop();\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to the game loop events\n\t * @ignore\n\t */\n\t_subscribe() {\n\t\tif (!this._isRunning) {\n\t\t\tthis._isRunning = true;\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(TICK, this._onTick, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(STATE_PAUSE, this._onPause, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(STATE_RESUME, this._resumeCallback, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(GAME_AFTER_UPDATE, this._onAfterUpdate, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(GAME_RESET, this._onReset, this);\n\t\t}\n\t}\n\n\t/**\n\t * Unsubscribe from the game loop events\n\t * @ignore\n\t */\n\t_unsubscribe() {\n\t\tif (this._isRunning) {\n\t\t\tthis._isRunning = false;\n\t\t\tthis._isPaused = false;\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(TICK, this._onTick, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(STATE_PAUSE, this._onPause, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(STATE_RESUME, this._resumeCallback, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(GAME_AFTER_UPDATE, this._onAfterUpdate, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(GAME_RESET, this._onReset, this);\n\t\t}\n\t}\n\n\t/**\n\t * Define the target property values and tween options.\n\t * @param properties - target property values to tween to (e.g. `{ x: 200, y: 100 }`)\n\t * @param [options] - tween configuration\n\t * @param [options.duration] - tween duration in milliseconds\n\t * @param [options.easing] - easing function (e.g. `Tween.Easing.Quadratic.InOut`)\n\t * @param [options.delay] - delay before starting, in milliseconds\n\t * @param [options.yoyo] - bounce back to original values when finished (use with `repeat`)\n\t * @param [options.repeat] - number of times to repeat (use `Infinity` for endless loops)\n\t * @param [options.repeatDelay] - delay in milliseconds before each repeat cycle\n\t * @param [options.interpolation] - interpolation function for array values\n\t * @param [options.autoStart] - start the tween immediately without calling `start()`\n\t * @returns this instance for object chaining\n\t */\n\tto(\n\t\tproperties: Record<string, unknown>,\n\t\toptions?: {\n\t\t\tduration?: number | undefined;\n\t\t\teasing?: EasingFunction | undefined;\n\t\t\tyoyo?: boolean | undefined;\n\t\t\trepeat?: number | undefined;\n\t\t\tdelay?: number | undefined;\n\t\t\trepeatDelay?: number | undefined;\n\t\t\tinterpolation?: InterpolationFunction | undefined;\n\t\t\tautoStart?: boolean | undefined;\n\t\t},\n\t) {\n\t\tthis._valuesEnd = properties;\n\n\t\tif (typeof options !== \"undefined\") {\n\t\t\tif (options.duration !== undefined) {\n\t\t\t\tthis._duration = options.duration;\n\t\t\t}\n\t\t\tif (options.yoyo !== undefined) {\n\t\t\t\tthis.yoyo(options.yoyo);\n\t\t\t}\n\t\t\tif (options.easing !== undefined) {\n\t\t\t\tthis.easing(options.easing);\n\t\t\t}\n\t\t\tif (options.repeat !== undefined) {\n\t\t\t\tthis.repeat(options.repeat);\n\t\t\t}\n\t\t\tif (options.delay !== undefined) {\n\t\t\t\tthis.delay(options.delay);\n\t\t\t}\n\t\t\tif (options.repeatDelay !== undefined) {\n\t\t\t\tthis.repeatDelay(options.repeatDelay);\n\t\t\t}\n\t\t\tif (options.interpolation !== undefined) {\n\t\t\t\tthis.interpolation(options.interpolation);\n\t\t\t}\n\t\t\tif (options.autoStart === true) {\n\t\t\t\tthis.start();\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Start the tween. Subscribes to game loop events and begins interpolation.\n\t * @param [time] - the start time (defaults to current game time)\n\t * @returns this instance for object chaining\n\t */\n\tstart(time = timer.getTime()) {\n\t\t// stop any running tween before restarting (prevents listener leaks\n\t\t// and array value corruption from repeated start() calls)\n\t\tthis._unsubscribe();\n\n\t\tthis._onStartCallbackFired = false;\n\n\t\t// sync internal time tracker to the provided start time\n\t\tthis._tweenTimeTracker = time;\n\t\tthis._lastUpdate = time;\n\n\t\t// subscribe to game loop events\n\t\tthis._lastTick = globalThis.performance.now();\n\t\tthis._subscribe();\n\n\t\tthis._startTime = time + this._delayTime;\n\n\t\tfor (const property in this._valuesEnd) {\n\t\t\tconst endValue = this._valuesEnd[property];\n\t\t\t// check if an Array was provided as property value\n\t\t\tif (Array.isArray(endValue)) {\n\t\t\t\tif (endValue.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// create a local copy of the Array with the start value at the front\n\t\t\t\tthis._valuesEnd[property] = [\n\t\t\t\t\t(this._object as Record<string, unknown>)[property],\n\t\t\t\t].concat(endValue);\n\t\t\t}\n\n\t\t\tthis._valuesStart[property] = (this._object as Record<string, unknown>)[\n\t\t\t\tproperty\n\t\t\t];\n\n\t\t\tif (!Array.isArray(this._valuesStart[property])) {\n\t\t\t\t(this._valuesStart[property] as number) *= 1.0; // Ensures we're using numbers, not strings\n\t\t\t}\n\n\t\t\tthis._valuesStartRepeat[property] = this._valuesStart[property] || 0;\n\t\t}\n\n\t\treturn this;\n\t}\n\t/**\n\t * Stop the tween. Unsubscribes from all game loop events.\n\t * @returns this instance for object chaining\n\t */\n\tstop() {\n\t\tthis._unsubscribe();\n\t\treturn this;\n\t}\n\n\t/**\n\t * delay the tween\n\t * @param amount - delay amount expressed in milliseconds\n\t * @returns this instance for object chaining\n\t */\n\tdelay(amount: number) {\n\t\tthis._delayTime = amount;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Repeat the tween\n\t * @param times - amount of times the tween should be repeated\n\t * @returns this instance for object chaining\n\t */\n\trepeat(times: number) {\n\t\tthis._repeat = times;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a delay before each repeat.\n\t * @param amount - delay in milliseconds before each repeat cycle\n\t * @returns this instance for object chaining\n\t */\n\trepeatDelay(amount: number) {\n\t\tthis._repeatDelayTime = amount;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Allows the tween to bounce back to their original value when finished.\n\t * To be used together with repeat to create endless loops.\n\t * @param yoyo flag\n\t * @returns this instance for object chaining\n\t */\n\tyoyo(yoyo: boolean) {\n\t\tthis._yoyo = yoyo;\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the easing function\n\t * @param easing - easing function\n\t * @returns this instance for object chaining\n\t */\n\teasing(easing: EasingFunction) {\n\t\tthis._easingFunction = easing;\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the interpolation function\n\t * @param interpolation - interpolation function\n\t * @returns this instance for object chaining\n\t */\n\tinterpolation(interpolation: InterpolationFunction) {\n\t\tthis._interpolationFunction = interpolation;\n\t\treturn this;\n\t}\n\n\t/**\n\t * chain the tween\n\t * @param tweens - Tween(s) to be chained\n\t * @returns this instance for object chaining\n\t */\n\tchain(...tweens: Tween[]) {\n\t\tthis._chainedTweens = tweens;\n\t\treturn this;\n\t}\n\n\t/**\n\t * onStart callback\n\t * @param onStartCallback - callback\n\t * @returns this instance for object chaining\n\t */\n\tonStart(onStartCallback: OnStartCallback<object>) {\n\t\tthis._onStartCallback = onStartCallback;\n\t\treturn this;\n\t}\n\n\t/**\n\t * onUpdate callback\n\t * @param onUpdateCallback - callback\n\t * @returns this instance for object chaining\n\t */\n\tonUpdate(onUpdateCallback: OnUpdateCallback<object>) {\n\t\tthis._onUpdateCallback = onUpdateCallback;\n\t\treturn this;\n\t}\n\n\t/**\n\t * onComplete callback\n\t * @param onCompleteCallback - callback\n\t * @returns this instance for object chaining\n\t */\n\tonComplete(onCompleteCallback: OnCompleteCallback<object>) {\n\t\tthis._onCompleteCallback = onCompleteCallback;\n\t\treturn this;\n\t}\n\n\t/** @ignore */\n\tupdate(dt: number) {\n\t\t// the original Tween implementation expect\n\t\t// a timestamp and not a time delta\n\t\tthis._tweenTimeTracker =\n\t\t\tthis._lastUpdate > this._tweenTimeTracker\n\t\t\t\t? this._lastUpdate\n\t\t\t\t: this._tweenTimeTracker + dt;\n\t\tconst time = this._tweenTimeTracker;\n\n\t\tif (this._startTime === null || time < this._startTime) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!this._onStartCallbackFired) {\n\t\t\tif (this._onStartCallback !== null) {\n\t\t\t\tthis._onStartCallback.call(this._object);\n\t\t\t}\n\n\t\t\tthis._onStartCallbackFired = true;\n\t\t}\n\n\t\tlet elapsed =\n\t\t\tthis._duration > 0 ? (time - this._startTime) / this._duration : 1;\n\t\telapsed = elapsed > 1 ? 1 : elapsed;\n\n\t\tconst value = this._easingFunction(elapsed);\n\n\t\tfor (const property in this._valuesEnd) {\n\t\t\tconst start = this._valuesStart[property] || 0;\n\t\t\tlet end = this._valuesEnd[property];\n\n\t\t\tif (Array.isArray(end)) {\n\t\t\t\t// @ts-expect-error todo\n\t\t\t\tthis._object[property] = this._interpolationFunction(end, value);\n\t\t\t} else {\n\t\t\t\t// Parses relative end values with start as base (e.g.: +10, -3)\n\t\t\t\tif (typeof end === \"string\") {\n\t\t\t\t\tconst parsed = parseFloat(end);\n\t\t\t\t\tif (isNaN(parsed)) {\n\t\t\t\t\t\tcontinue; // skip non-numeric string properties\n\t\t\t\t\t}\n\t\t\t\t\t// @ts-expect-error todo\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\t\t\tend = start + parsed;\n\t\t\t\t}\n\n\t\t\t\t// protect against non numeric properties.\n\t\t\t\tif (typeof end === \"number\") {\n\t\t\t\t\t// @ts-expect-error todo\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\t\t\tthis._object[property] = start + (end - start) * value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this._onUpdateCallback !== null) {\n\t\t\tthis._onUpdateCallback.call(this._object, value);\n\t\t}\n\n\t\tif (elapsed === 1) {\n\t\t\tif (this._repeat > 0) {\n\t\t\t\tif (isFinite(this._repeat)) {\n\t\t\t\t\tthis._repeat--;\n\t\t\t\t}\n\n\t\t\t\t// reassign starting values, restart by making startTime = now\n\t\t\t\tfor (const property in this._valuesStartRepeat) {\n\t\t\t\t\tif (typeof this._valuesEnd[property] === \"string\") {\n\t\t\t\t\t\tthis._valuesStartRepeat[property] =\n\t\t\t\t\t\t\t// @ts-expect-error todo\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n\t\t\t\t\t\t\tthis._valuesStartRepeat[property] +\n\t\t\t\t\t\t\t// @ts-ignore\n\t\t\t\t\t\t\tparseFloat(this._valuesEnd[property]);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this._yoyo) {\n\t\t\t\t\t\tconst tmp = this._valuesStartRepeat[property];\n\t\t\t\t\t\tthis._valuesStartRepeat[property] = this._valuesEnd[property];\n\t\t\t\t\t\tthis._valuesEnd[property] = tmp;\n\t\t\t\t\t}\n\t\t\t\t\tthis._valuesStart[property] = this._valuesStartRepeat[property];\n\t\t\t\t}\n\n\t\t\t\tif (this._yoyo) {\n\t\t\t\t\tthis._reversed = !this._reversed;\n\t\t\t\t}\n\n\t\t\t\tthis._startTime = time + (this._repeatDelayTime ?? this._delayTime);\n\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t// unsubscribe from events on completion\n\t\t\t\tthis._unsubscribe();\n\n\t\t\t\tif (this._onCompleteCallback !== null) {\n\t\t\t\t\tthis._onCompleteCallback.call(this._object);\n\t\t\t\t}\n\n\t\t\t\tfor (\n\t\t\t\t\tlet i = 0, numChainedTweens = this._chainedTweens.length;\n\t\t\t\t\ti < numChainedTweens;\n\t\t\t\t\ti++\n\t\t\t\t) {\n\t\t\t\t\tthis._chainedTweens[i].start(time);\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Available easing functions, accessed via `Tween.Easing`.\n\t * Each family provides `In`, `Out`, and `InOut` variants.\n\t * @see {@link Easing} for the full list\n\t * @example\n\t * me.Tween.Easing.Quadratic.InOut\n\t * me.Tween.Easing.Bounce.Out\n\t * me.Tween.Easing.Elastic.In\n\t */\n\tstatic get Easing() {\n\t\treturn Easing;\n\t}\n\n\t/**\n\t * Available interpolation functions for tweening array values.\n\t * @see {@link Interpolation}\n\t * @example\n\t * me.Tween.Interpolation.Linear\n\t * me.Tween.Interpolation.Bezier\n\t * me.Tween.Interpolation.CatmullRom\n\t */\n\tstatic get Interpolation() {\n\t\treturn Interpolation;\n\t}\n}\n\nexport const tweenPool = createPool((obj: object) => {\n\tconst tween = new Tween(obj);\n\n\treturn {\n\t\tinstance: tween,\n\t\treset(obj: object) {\n\t\t\ttween.setProperties(obj);\n\t\t},\n\t};\n});\n","import type Renderer from \"../../video/renderer.js\";\nimport type Camera2d from \"../camera2d.ts\";\n\n/**\n * Base class for camera effects (shake, fade, flash, transitions, etc.).\n * Subclasses implement update() for per-frame logic and/or draw() for rendering overlays.\n * Effects are automatically removed from the camera when isComplete becomes true.\n * @category Camera\n */\nexport default class CameraEffect {\n\t/**\n\t * the camera this effect is attached to\n\t */\n\tcamera: Camera2d;\n\n\t/**\n\t * whether this effect has finished and should be removed\n\t */\n\tisComplete: boolean;\n\n\t/**\n\t * whether this effect should persist across camera/game resets\n\t * (e.g. transition effects that span state changes)\n\t * @default false\n\t */\n\tisPersistent: boolean;\n\n\t/**\n\t * @param camera - the camera this effect is attached to\n\t */\n\tconstructor(camera: Camera2d) {\n\t\tthis.camera = camera;\n\t\tthis.isComplete = false;\n\t\tthis.isPersistent = false;\n\t}\n\n\t/**\n\t * Called each frame to update the effect state (e.g. modify camera offset, countdown duration).\n\t * @param _dt - time elapsed since last frame in milliseconds\n\t */\n\tupdate(_dt: number): void {}\n\n\t/**\n\t * Called after the scene renders to draw visual overlays (e.g. color fills for fading).\n\t * @param _renderer - the renderer to draw with\n\t * @param _width - the camera viewport width\n\t * @param _height - the camera viewport height\n\t */\n\tdraw(_renderer: Renderer, _width: number, _height: number): void {}\n\n\t/**\n\t * Called when the effect is removed from the camera. Override to clean up resources.\n\t */\n\tdestroy(): void {}\n}\n","import type { Color } from \"../../math/color.ts\";\nimport { colorPool } from \"../../math/color.ts\";\nimport type Tween from \"../../tweens/tween.ts\";\nimport { tweenPool } from \"../../tweens/tween.ts\";\nimport type Renderer from \"../../video/renderer.js\";\nimport type Camera2d from \"../camera2d.ts\";\nimport CameraEffect from \"./camera_effect.ts\";\n\n/**\n * A camera effect that fades the screen to or from a color overlay.\n * @category Camera\n * @example\n * // fade to black\n * camera.addCameraEffect(new FadeEffect(camera, {\n *     color: \"#000\",\n *     duration: 500,\n *     direction: \"in\",\n * }));\n * @example\n * // flash white then fade back\n * camera.addCameraEffect(new FadeEffect(camera, {\n *     color: \"#fff\",\n *     duration: 200,\n *     direction: \"out\",\n * }));\n */\nexport default class FadeEffect extends CameraEffect {\n\t/**\n\t * the overlay color\n\t */\n\tcolor: Color;\n\n\t/**\n\t * the tween controlling alpha transition\n\t */\n\ttween: Tween;\n\n\t/**\n\t * fade direction: \"in\" fades to the color, \"out\" fades from the color back to transparent\n\t */\n\tdirection: \"in\" | \"out\";\n\n\t/**\n\t * target alpha value for completion check\n\t * @ignore\n\t */\n\t_targetAlpha: number;\n\n\t/**\n\t * @param camera - the camera to apply the fade to\n\t * @param options - fade parameters\n\t * @param options.color - CSS color value or Color instance\n\t * @param [options.duration=1000] - fade duration in milliseconds\n\t * @param [options.direction=\"in\"] - \"in\" fades to color, \"out\" fades from color to transparent\n\t * @param [options.onComplete] - callback when the fade finishes\n\t */\n\tconstructor(\n\t\tcamera: Camera2d,\n\t\toptions: {\n\t\t\tcolor: Color | string;\n\t\t\tduration?: number | undefined;\n\t\t\tdirection?: \"in\" | \"out\" | undefined;\n\t\t\tonComplete?: (() => void) | undefined;\n\t\t},\n\t) {\n\t\tsuper(camera);\n\n\t\tconst duration = options.duration ?? 1000;\n\t\tthis.direction = options.direction ?? \"in\";\n\t\tthis.color = colorPool.get(options.color);\n\n\t\tif (this.direction === \"in\") {\n\t\t\t// fade IN to the color: alpha goes from 0 → target\n\t\t\tthis._targetAlpha = this.color.alpha;\n\t\t\tthis.color.alpha = 0.0;\n\t\t\tthis.tween = tweenPool\n\t\t\t\t.get(this.color)\n\t\t\t\t.to({ alpha: this._targetAlpha }, { duration });\n\t\t} else {\n\t\t\t// fade OUT from the color: alpha goes from current → 0\n\t\t\tthis._targetAlpha = 0.0;\n\t\t\tthis.tween = tweenPool.get(this.color).to({ alpha: 0.0 }, { duration });\n\t\t}\n\n\t\tif (options.onComplete) {\n\t\t\tthis.tween.onComplete(options.onComplete);\n\t\t}\n\t\tthis.tween.isPersistent = true;\n\t\tthis.tween.start();\n\t}\n\n\toverride draw(renderer: Renderer, width: number, height: number): void {\n\t\tif (this.color.alpha === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst r = renderer as any;\n\t\tr.save();\n\t\tr.resetTransform();\n\t\tr.setColor(this.color);\n\t\tr.fillRect(0, 0, width, height);\n\t\tr.restore();\n\t}\n\n\toverride update(): void {\n\t\t// check if the tween has reached its target\n\t\tif (this.direction === \"in\" && this.color.alpha >= this._targetAlpha) {\n\t\t\tthis.isComplete = true;\n\t\t} else if (this.direction === \"out\" && this.color.alpha <= 0.0) {\n\t\t\tthis.isComplete = true;\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\t// Guarded and cleared so a second destroy() is a no-op rather than a\n\t\t// throw. `removePostEffect()` destroys the effect it removes, so a\n\t\t// caller that also destroys it explicitly hits this path, and an\n\t\t// unguarded re-release throws \"Instance is already in pool\" from the\n\t\t// pool rather than from anything the caller can see. Same defect and\n\t\t// same fix as `Body.destroy()` in 20.0.0.\n\t\tif (this.tween !== undefined) {\n\t\t\tthis.tween.stop();\n\t\t\ttweenPool.release(this.tween);\n\t\t\tthis.tween = undefined as unknown as typeof this.tween;\n\t\t}\n\t\tif (this.color !== undefined) {\n\t\t\tcolorPool.release(this.color);\n\t\t\tthis.color = undefined as unknown as typeof this.color;\n\t\t}\n\t}\n}\n","import type { Matrix2d } from \"../math/matrix2d.ts\";\nimport { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\nimport { Bounds, boundsPool } from \"../physics/bounds.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { Polygon } from \"./polygon.ts\";\n\n/**\n * an ellipse Object\n * @category Geometry\n */\nexport class Ellipse {\n\t/**\n\t * the center coordinates of the ellipse\n\t */\n\tpos: Vector2d;\n\n\t/**\n\t * The bounding rectangle for this shape\n\t */\n\t_bounds: Bounds;\n\n\t/**\n\t * Maximum radius of the ellipse\n\t */\n\tradius: number;\n\n\t/**\n\t * Pre-scaled radius vector for ellipse\n\t */\n\tradiusV: Vector2d;\n\n\t/**\n\t * Radius squared, for pythagorean theorem\n\t */\n\tprivate radiusSq: Vector2d;\n\n\t/**\n\t * x/y scaling ratio for ellipse\n\t */\n\tratio: Vector2d;\n\n\t/**\n\t * the internal rotation angle of the ellipse in radians\n\t */\n\tprivate _angle: number;\n\n\t/**\n\t * cached cosine of the current angle\n\t */\n\tprivate _cos: number;\n\n\t/**\n\t * cached sine of the current angle\n\t */\n\tprivate _sin: number;\n\n\t/**\n\t * cached polygon approximation, invalidated when shape changes\n\t */\n\tprivate _polygon: Polygon | null = null;\n\n\t/**\n\t * the rotation angle of the ellipse in radians\n\t */\n\tget angle() {\n\t\treturn this._angle;\n\t}\n\tset angle(value) {\n\t\tthis._angle = value;\n\t\tthis._cos = Math.cos(value);\n\t\tthis._sin = Math.sin(value);\n\t\tthis._polygon = null;\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * the shape type (used internally)\n\t */\n\ttype = \"Ellipse\";\n\n\t/**\n\t * @param x - the center x coordinate of the ellipse\n\t * @param y - the center y coordinate of the ellipse\n\t * @param w - width (diameter) of the ellipse\n\t * @param h - height (diameter) of the ellipse\n\t */\n\tconstructor(x: number, y: number, w: number, h: number) {\n\t\tthis.pos = new Vector2d();\n\t\tthis._bounds = new Bounds();\n\t\tthis.radius = NaN;\n\t\tthis.radiusV = new Vector2d();\n\t\tthis.radiusSq = new Vector2d();\n\t\tthis.ratio = vector2dPool.get();\n\t\tthis._angle = 0;\n\t\tthis._cos = 1;\n\t\tthis._sin = 0;\n\t\tthis.setShape(x, y, w, h);\n\t}\n\n\t/**\n\t * set new value to the Ellipse shape\n\t * @param x - the center x coordinate of the ellipse\n\t * @param y - the center y coordinate of the ellipse\n\t * @param w - width (diameter) of the ellipse\n\t * @param h - height (diameter) of the ellipse\n\t * @returns this instance for object chaining\n\t */\n\tsetShape(x: number, y: number, w: number, h: number) {\n\t\tconst hW = w / 2;\n\t\tconst hH = h / 2;\n\t\tconst radius = Math.max(hW, hH);\n\n\t\tthis.pos.set(x, y);\n\t\tthis.radius = radius;\n\t\tthis.ratio.set(hW / radius, hH / radius);\n\t\tthis.radiusV.set(radius, radius).scaleV(this.ratio);\n\t\tthis.radiusSq.set(\n\t\t\tthis.radiusV.x * this.radiusV.x,\n\t\t\tthis.radiusV.y * this.radiusV.y,\n\t\t);\n\t\tthis._angle = 0;\n\t\tthis._cos = 1;\n\t\tthis._sin = 0;\n\t\tthis._polygon = null;\n\n\t\tthis.updateBounds();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * update the bounding box for this ellipse, taking rotation into account\n\t */\n\tprivate updateBounds() {\n\t\t// invalidate cached polygon\n\t\tthis._polygon = null;\n\n\t\tconst bounds = this.getBounds();\n\t\tconst rx = this.radiusV.x;\n\t\tconst ry = this.radiusV.y;\n\n\t\tif (this._angle !== 0) {\n\t\t\tconst cos = this._cos;\n\t\t\tconst sin = this._sin;\n\t\t\tconst halfW = Math.sqrt(rx * rx * cos * cos + ry * ry * sin * sin);\n\t\t\tconst halfH = Math.sqrt(rx * rx * sin * sin + ry * ry * cos * cos);\n\t\t\tbounds.setMinMax(\n\t\t\t\tthis.pos.x - halfW,\n\t\t\t\tthis.pos.y - halfH,\n\t\t\t\tthis.pos.x + halfW,\n\t\t\t\tthis.pos.y + halfH,\n\t\t\t);\n\t\t} else {\n\t\t\tbounds.setMinMax(\n\t\t\t\tthis.pos.x - rx,\n\t\t\t\tthis.pos.y - ry,\n\t\t\t\tthis.pos.x + rx,\n\t\t\t\tthis.pos.y + ry,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Rotate this Ellipse (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: Vector2d) {\n\t\tif (v) {\n\t\t\tthis.pos.rotate(angle, v);\n\t\t}\n\t\tthis.angle += angle;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Scale this Ellipse by the specified scalar.\n\t * @param x - the scale factor along the x-axis\n\t * @param [y=x] - the scale factor along the y-axis\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x) {\n\t\treturn this.setShape(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.radiusV.x * 2 * x,\n\t\t\tthis.radiusV.y * 2 * y,\n\t\t);\n\t}\n\n\t/**\n\t * Scale this Ellipse by the specified vector.\n\t * @param v - Scaling vector.\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector2d) {\n\t\treturn this.scale(v.x, v.y);\n\t}\n\n\t/**\n\t * apply the given transformation matrix to this ellipse\n\t * @param m - the transformation matrix\n\t * @returns Reference to this object for method chaining\n\t */\n\ttransform(m: Matrix2d) {\n\t\tconst a = m.val;\n\n\t\t// extract translation and apply to position\n\t\tthis.pos.x += a[6];\n\t\tthis.pos.y += a[7];\n\n\t\t// extract scale from the matrix columns\n\t\tconst sx = Math.sqrt(a[0] * a[0] + a[1] * a[1]);\n\t\tconst sy = Math.sqrt(a[3] * a[3] + a[4] * a[4]);\n\n\t\t// extract rotation from the matrix\n\t\tconst rotation = Math.atan2(a[1], a[0]);\n\n\t\t// apply scale to radii\n\t\tthis.radiusV.x *= sx;\n\t\tthis.radiusV.y *= sy;\n\t\tthis.radius = Math.max(this.radiusV.x, this.radiusV.y);\n\t\tthis.radiusSq.set(\n\t\t\tthis.radiusV.x * this.radiusV.x,\n\t\t\tthis.radiusV.y * this.radiusV.y,\n\t\t);\n\n\t\t// apply rotation (setter updates trig cache, polygon, and bounds)\n\t\tthis.angle += rotation;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * translate the circle/ellipse by the specified offset\n\t * @param x -  x coordinate or a vector point to translate by\n\t * @param [y] - y offset\n\t * @returns this ellipse\n\t * @example\n\t * ellipse.translate(10, 10);\n\t * // or\n\t * ellipse.translate(myVector2d);\n\t */\n\ttranslate(x: number, y: number): Ellipse;\n\ttranslate(vector: Vector2d): Ellipse;\n\ttranslate(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\tthis.pos.x += _x;\n\t\tthis.pos.y += _y;\n\t\tthis.updateBounds();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * check if this circle/ellipse contains the specified point\n\t * @param x -  x coordinate or a vector point to check\n\t * @param [y] -  y coordinate\n\t * @param args\n\t * @returns true if contains\n\t * @example\n\t * if (circle.contains(10, 10)) {\n\t *   // do something\n\t * }\n\t * // or\n\t * if (circle.contains(myVector2d)) {\n\t *  // do something\n\t * }\n\t */\n\tcontains(x: number, y: number): boolean;\n\tcontains(vector: Vector2d): boolean;\n\tcontains(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t[_x, _y] = [xOrVector.x, xOrVector.y];\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\t// Make position relative to object center point.\n\t\t_x -= this.pos.x;\n\t\t_y -= this.pos.y;\n\n\t\t// Un-rotate the point if the ellipse is rotated\n\t\t// cos(-θ) = cos(θ), sin(-θ) = -sin(θ)\n\t\tif (this._angle !== 0) {\n\t\t\tconst cos = this._cos;\n\t\t\tconst sin = -this._sin;\n\t\t\tconst rx = _x * cos - _y * sin;\n\t\t\tconst ry = _x * sin + _y * cos;\n\t\t\t_x = rx;\n\t\t\t_y = ry;\n\t\t}\n\n\t\t// Pythagorean theorem.\n\t\treturn (_x * _x) / this.radiusSq.x + (_y * _y) / this.radiusSq.y <= 1.0;\n\t}\n\n\t/**\n\t * returns the bounding box for this shape, the smallest Rectangle object completely containing this shape.\n\t * @returns this shape bounding box Rectangle object\n\t */\n\tgetBounds() {\n\t\tif (typeof this._bounds === \"undefined\") {\n\t\t\tthis._bounds = boundsPool.get();\n\t\t}\n\t\treturn this._bounds;\n\t}\n\n\t/**\n\t * Returns a polygon approximation of this ellipse.\n\t * The polygon is cached and only recomputed when the ellipse shape changes.\n\t * @param [segments=16] - the number of segments to use for the approximation\n\t * @returns a Polygon representing this ellipse\n\t */\n\ttoPolygon(segments = 16) {\n\t\tif (this._polygon !== null) {\n\t\t\treturn this._polygon;\n\t\t}\n\n\t\tconst points: Vector2d[] = [];\n\t\tconst cos = this._cos;\n\t\tconst sin = this._sin;\n\t\tconst rx = this.radiusV.x;\n\t\tconst ry = this.radiusV.y;\n\t\tconst step = (2 * Math.PI) / segments;\n\n\t\tfor (let i = 0; i < segments; i++) {\n\t\t\tconst angle = i * step;\n\t\t\tconst px = rx * Math.cos(angle);\n\t\t\tconst py = ry * Math.sin(angle);\n\t\t\tpoints.push(new Vector2d(px * cos - py * sin, px * sin + py * cos));\n\t\t}\n\n\t\tthis._polygon = new Polygon(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tpoints as [Vector2d, Vector2d, Vector2d, ...Vector2d[]],\n\t\t);\n\t\treturn this._polygon;\n\t}\n\n\t/**\n\t * clone this Ellipse\n\t * @returns new Ellipse\n\t */\n\tclone() {\n\t\tconst clone = ellipsePool.get(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.radiusV.x * 2,\n\t\t\tthis.radiusV.y * 2,\n\t\t);\n\t\tclone._angle = this._angle;\n\t\tclone._cos = this._cos;\n\t\tclone._sin = this._sin;\n\t\tclone.updateBounds();\n\t\treturn clone;\n\t}\n}\n\nexport const ellipsePool = createPool<\n\tEllipse,\n\t[x: number, y: number, width: number, height: number]\n>((x, y, width, height) => {\n\tconst instance = new Ellipse(x, y, width, height);\n\n\treturn {\n\t\tinstance,\n\t\treset(x, y, width, height) {\n\t\t\tinstance.setShape(x, y, width, height);\n\t\t},\n\t};\n});\n","import { Ellipse, ellipsePool } from \"../../geometries/ellipse.ts\";\nimport { Polygon, polygonPool } from \"../../geometries/polygon.ts\";\nimport type { Color } from \"../../math/color.ts\";\nimport { colorPool } from \"../../math/color.ts\";\nimport type Tween from \"../../tweens/tween.ts\";\nimport { tweenPool } from \"../../tweens/tween.ts\";\nimport type Renderer from \"../../video/renderer.js\";\nimport type Camera2d from \"../camera2d.ts\";\nimport CameraEffect from \"./camera_effect.ts\";\n\ntype MaskShape = Ellipse | Polygon;\n\n/**\n * A camera effect that performs a mask-based scene transition.\n * A shape (ellipse, polygon) is scaled from full-screen to zero (hide)\n * or from zero to full-screen (reveal), with the area outside the shape\n * filled with a solid color.\n * @category Camera\n * @example\n * // iris transition (circle shrinks to hide the scene)\n * camera.addCameraEffect(new MaskEffect(camera, {\n *     shape: new Ellipse(0, 0, 1, 1),\n *     color: \"#000\",\n *     duration: 500,\n *     direction: \"hide\",\n * }));\n * @example\n * // diamond reveal transition\n * camera.addCameraEffect(new MaskEffect(camera, {\n *     shape: new Polygon(0, 0, [\n *         { x: 0, y: -1 }, { x: 1, y: 0 },\n *         { x: 0, y: 1 }, { x: -1, y: 0 },\n *     ]),\n *     color: \"#000\",\n *     duration: 500,\n *     direction: \"reveal\",\n * }));\n */\nexport default class MaskEffect extends CameraEffect {\n\t/**\n\t * the transition fill color\n\t */\n\tcolor: Color;\n\n\t/**\n\t * the mask shape template (unit-sized, centered at origin)\n\t */\n\tshape: MaskShape;\n\n\t/**\n\t * current progress value (0 = fully covered, 1 = fully visible)\n\t */\n\tprogress: { value: number };\n\n\t/**\n\t * the tween controlling progress\n\t */\n\ttween: Tween;\n\n\t/**\n\t * transition direction\n\t */\n\tdirection: \"hide\" | \"reveal\";\n\n\t/**\n\t * optional callback when transition completes\n\t */\n\tonComplete: (() => void) | undefined;\n\n\t/**\n\t * pooled shape used for rendering (avoids per-frame allocation)\n\t * @ignore\n\t */\n\t_maskShape: MaskShape;\n\n\t/**\n\t * @param camera - the camera to apply the transition to\n\t * @param options - transition parameters\n\t * @param options.shape - an Ellipse or Polygon (unit-sized, centered at origin) defining the mask shape\n\t * @param options.color - CSS color value or Color instance for the transition fill\n\t * @param [options.duration=500] - transition duration in milliseconds\n\t * @param [options.direction=\"hide\"] - \"hide\" shrinks the visible area, \"reveal\" grows it\n\t * @param [options.onComplete] - callback when the transition finishes\n\t */\n\tconstructor(\n\t\tcamera: Camera2d,\n\t\toptions: {\n\t\t\tshape: MaskShape;\n\t\t\tcolor: Color | string;\n\t\t\tduration?: number | undefined;\n\t\t\tdirection?: \"hide\" | \"reveal\" | undefined;\n\t\t\tonComplete?: (() => void) | undefined;\n\t\t},\n\t) {\n\t\tsuper(camera);\n\n\t\tthis.color = colorPool.get(options.color);\n\t\tthis.direction = options.direction ?? \"hide\";\n\t\tthis.onComplete = options.onComplete;\n\n\t\tthis.shape = options.shape;\n\n\t\t// clone the shape for rendering\n\t\tthis._maskShape = this.shape.clone();\n\n\t\t// progress: 1 = fully visible, 0 = fully covered\n\t\tconst duration = options.duration ?? 500;\n\t\tif (this.direction === \"hide\") {\n\t\t\tthis.progress = { value: 1.0 };\n\t\t\tthis.tween = tweenPool\n\t\t\t\t.get(this.progress)\n\t\t\t\t.to({ value: 0.0 }, { duration });\n\t\t} else {\n\t\t\tthis.progress = { value: 0.0 };\n\t\t\tthis.tween = tweenPool\n\t\t\t\t.get(this.progress)\n\t\t\t\t.to({ value: 1.0 }, { duration });\n\t\t}\n\n\t\tthis.tween.isPersistent = true;\n\t\tthis.tween.start();\n\t}\n\n\toverride update(): void {\n\t\t// check if the transition has completed\n\t\tif (\n\t\t\t(this.direction === \"hide\" && this.progress.value <= 0.0) ||\n\t\t\t(this.direction === \"reveal\" && this.progress.value >= 1.0)\n\t\t) {\n\t\t\tthis.progress.value = this.direction === \"hide\" ? 0.0 : 1.0;\n\t\t\tthis.isComplete = true;\n\t\t\tif (typeof this.onComplete === \"function\") {\n\t\t\t\tthis.onComplete();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// update the mask shape based on current progress\n\t\tconst width = this.camera.width;\n\t\tconst height = this.camera.height;\n\t\tconst maxRadius = Math.sqrt(width * width + height * height) / 2;\n\t\tconst scale = this.progress.value * maxRadius;\n\t\tconst cx = width / 2;\n\t\tconst cy = height / 2;\n\n\t\tif (this._maskShape.type === \"Ellipse\") {\n\t\t\t(this._maskShape as Ellipse).setShape(cx, cy, scale * 2, scale * 2);\n\t\t} else {\n\t\t\t// set scaled vertices directly\n\t\t\tconst mask = this._maskShape as Polygon;\n\t\t\tconst srcPoints = (this.shape as Polygon).points;\n\t\t\tfor (let i = 0; i < srcPoints.length; i++) {\n\t\t\t\tmask.points[i].set(srcPoints[i].x * scale, srcPoints[i].y * scale);\n\t\t\t}\n\t\t\tmask.pos.set(cx, cy);\n\t\t}\n\t}\n\n\toverride draw(renderer: Renderer, width: number, height: number): void {\n\t\tif (this.isComplete && this.direction === \"reveal\") {\n\t\t\treturn;\n\t\t}\n\n\t\tconst r = renderer as any;\n\t\tr.save();\n\t\tr.resetTransform();\n\n\t\tif (this.progress.value <= 0.0) {\n\t\t\t// fully covered — just fill with color\n\t\t\tr.setColor(this.color);\n\t\t\tr.fillRect(0, 0, width, height);\n\t\t} else {\n\t\t\t// mask: only fill OUTSIDE the shape (invert = true)\n\t\t\tr.setMask(this._maskShape, true);\n\t\t\tr.setColor(this.color);\n\t\t\tr.fillRect(0, 0, width, height);\n\t\t\tr.clearMask();\n\t\t}\n\n\t\tr.restore();\n\t}\n\n\toverride destroy(): void {\n\t\t// see FadeEffect.destroy(): guarded so a second call is a no-op instead\n\t\t// of throwing \"Instance is already in pool\" out of the pool\n\t\tif (this.tween !== undefined) {\n\t\t\tthis.tween.stop();\n\t\t\ttweenPool.release(this.tween);\n\t\t\tthis.tween = undefined as unknown as typeof this.tween;\n\t\t}\n\t\tif (this.color !== undefined) {\n\t\t\tcolorPool.release(this.color);\n\t\t\tthis.color = undefined as unknown as typeof this.color;\n\t\t}\n\t\tif (this._maskShape !== undefined) {\n\t\t\tif (this._maskShape.type === \"Ellipse\") {\n\t\t\t\tellipsePool.release(this._maskShape as Ellipse);\n\t\t\t} else {\n\t\t\t\tpolygonPool.release(this._maskShape as Polygon);\n\t\t\t}\n\t\t\tthis._maskShape = undefined as unknown as typeof this._maskShape;\n\t\t}\n\t}\n}\n","import { Matrix3d } from \"./matrix3d.ts\";\n\n/**\n * A 4x4 color transformation matrix extending {@link Matrix3d}.\n * Provides chainable methods for common color adjustments.\n * @category Math\n * @example\n * // grayscale\n * const cm = new ColorMatrix().saturate(0.0);\n * @example\n * // combine brightness + contrast\n * const cm = new ColorMatrix().brightness(1.3).contrast(1.5);\n */\nexport class ColorMatrix extends Matrix3d {\n\t/**\n\t * Apply a brightness adjustment.\n\t * @param amount - brightness multiplier (1.0 = normal, >1 brighter, <1 darker)\n\t * @returns this instance for chaining\n\t */\n\tbrightness(amount: number): this {\n\t\treturn this.transform(\n\t\t\tamount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tamount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tamount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a contrast adjustment.\n\t * @param amount - contrast multiplier (1.0 = normal, >1 more contrast, <1 less)\n\t * @returns this instance for chaining\n\t */\n\tcontrast(amount: number): this {\n\t\tconst o = (1 - amount) / 2;\n\t\treturn this.transform(\n\t\t\tamount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tamount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tamount,\n\t\t\t0,\n\t\t\to,\n\t\t\to,\n\t\t\to,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a saturation adjustment.\n\t * @param amount - saturation level (0.0 = grayscale, 1.0 = normal, >1 over-saturated)\n\t * @returns this instance for chaining\n\t */\n\tsaturate(amount: number): this {\n\t\tconst ir = 0.299 * (1 - amount);\n\t\tconst ig = 0.587 * (1 - amount);\n\t\tconst ib = 0.114 * (1 - amount);\n\t\treturn this.transform(\n\t\t\tir + amount,\n\t\t\tir,\n\t\t\tir,\n\t\t\t0,\n\t\t\tig,\n\t\t\tig + amount,\n\t\t\tig,\n\t\t\t0,\n\t\t\tib,\n\t\t\tib,\n\t\t\tib + amount,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a hue rotation.\n\t * @param angle - rotation angle in radians\n\t * @returns this instance for chaining\n\t */\n\thueRotate(angle: number): this {\n\t\tconst cos = Math.cos(angle);\n\t\tconst sin = Math.sin(angle);\n\t\tconst lumR = 0.299;\n\t\tconst lumG = 0.587;\n\t\tconst lumB = 0.114;\n\t\treturn this.transform(\n\t\t\tlumR + cos * (1 - lumR) + sin * -lumR,\n\t\t\tlumR + cos * -lumR + sin * 0.143,\n\t\t\tlumR + cos * -lumR + sin * -(1 - lumR),\n\t\t\t0,\n\t\t\tlumG + cos * -lumG + sin * -lumG,\n\t\t\tlumG + cos * (1 - lumG) + sin * 0.14,\n\t\t\tlumG + cos * -lumG + sin * lumG,\n\t\t\t0,\n\t\t\tlumB + cos * -lumB + sin * (1 - lumB),\n\t\t\tlumB + cos * -lumB + sin * -0.283,\n\t\t\tlumB + cos * (1 - lumB) + sin * lumB,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a sepia tone.\n\t * @param amount - sepia intensity (0.0 = original, 1.0 = full sepia)\n\t * @returns this instance for chaining\n\t */\n\tsepia(amount: number = 1.0): this {\n\t\tif (amount >= 1.0) {\n\t\t\treturn this.transform(\n\t\t\t\t0.393,\n\t\t\t\t0.349,\n\t\t\t\t0.272,\n\t\t\t\t0,\n\t\t\t\t0.769,\n\t\t\t\t0.686,\n\t\t\t\t0.534,\n\t\t\t\t0,\n\t\t\t\t0.189,\n\t\t\t\t0.168,\n\t\t\t\t0.131,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t1,\n\t\t\t);\n\t\t}\n\t\tconst a = amount;\n\t\tconst b = 1 - a;\n\t\treturn this.transform(\n\t\t\tb + 0.393 * a,\n\t\t\t0.349 * a,\n\t\t\t0.272 * a,\n\t\t\t0,\n\t\t\t0.769 * a,\n\t\t\tb + 0.686 * a,\n\t\t\t0.534 * a,\n\t\t\t0,\n\t\t\t0.189 * a,\n\t\t\t0.168 * a,\n\t\t\tb + 0.131 * a,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\t}\n\n\t/**\n\t * Apply a color inversion.\n\t * @param amount - inversion amount (0.0 = original, 1.0 = fully inverted)\n\t * @returns this instance for chaining\n\t */\n\tinvertColors(amount: number = 1.0): this {\n\t\tconst c = 1 - 2 * amount;\n\t\treturn this.transform(\n\t\t\tc,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tc,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\tc,\n\t\t\t0,\n\t\t\tamount,\n\t\t\tamount,\n\t\t\tamount,\n\t\t\t1,\n\t\t);\n\t}\n}\n","import { Point } from \"./point.ts\";\n\n/**\n * Represents a point in a 2D coordinate system that can be observed for changes.\n */\n/**\n * Represents an observable point in 2D space.\n */\nexport class ObservablePoint {\n\tprivate _callback: () => void;\n\tprivate _point: Point;\n\tprivate _revoke: () => void;\n\n\tprivate callBackEnabled: boolean = true;\n\n\ttype: \"ObservablePoint\";\n\n\t/**\n\t * Creates a new ObservablePoint instance.\n\t * @param x - The x-coordinate of the point. Default is 0.\n\t * @param y - The y-coordinate of the point. Default is 0.\n\t * @param callback - The callback function to be called when the point changes. Default is undefined.\n\t */\n\tconstructor(x: number = 0, y: number = 0, callback?: () => void) {\n\t\tconst { proxy, revoke } = Proxy.revocable(new Point(x, y), {\n\t\t\tset: (target, property, value) => {\n\t\t\t\tif (property === \"x\" || property === \"y\") {\n\t\t\t\t\tReflect.set(target, property, value);\n\t\t\t\t\tif (this.callBackEnabled) {\n\t\t\t\t\t\tthis._callback?.();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t});\n\n\t\tthis.callBackEnabled = true;\n\t\tthis._revoke = revoke;\n\t\tthis._point = proxy;\n\n\t\tif (callback) {\n\t\t\tthis.setCallback(callback);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tset(x = 0, y = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._point.x = x;\n\t\tthis._point.y = y;\n\t\tthis._callback?.();\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point without triggering the callback.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetMuted(x = 0, y = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._point.x = x;\n\t\tthis._point.y = y;\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the x-coordinate of the point.\n\t */\n\tget x() {\n\t\treturn this._point.x;\n\t}\n\n\t/**\n\t * Sets the x-coordinate of the point.\n\t * @param value - The new x-coordinate value.\n\t */\n\tset x(value: number) {\n\t\tthis._point.x = value;\n\t}\n\n\t/**\n\t * Gets the y-coordinate of the point.\n\t */\n\tget y() {\n\t\treturn this._point.y;\n\t}\n\n\t/**\n\t * Sets the y-coordinate of the point.\n\t * @param value - The new y-coordinate value.\n\t */\n\tset y(value: number) {\n\t\tthis._point.y = value;\n\t}\n\n\t/**\n\t * Sets the callback function to be called when the point changes.\n\t * @param callback - The callback function.\n\t */\n\tsetCallback(callback: () => void) {\n\t\tthis._callback = callback;\n\t}\n\n\t/**\n\t * Checks if the point is equal to the given coordinates or another ObservablePoint.\n\t * @param x - The x-coordinate or the ObservablePoint to compare.\n\t * @param y - The y-coordinate. Required if the first parameter is a number.\n\t * @returns True if the point is equal to the given coordinates or another ObservablePoint, false otherwise.\n\t */\n\tequals(x: number, y: number): boolean;\n\tequals(point: ObservablePoint): boolean;\n\tequals(xOrPoint: number | ObservablePoint, y?: number) {\n\t\treturn this._point.equals(xOrPoint as any, y as any);\n\t}\n\n\t/**\n\t * Creates a clone of the ObservablePoint.\n\t * @returns A new ObservablePoint instance with the same coordinates and callback function.\n\t */\n\tclone() {\n\t\treturn new ObservablePoint(this._point.x, this._point.y, this._callback);\n\t}\n\n\t/**\n\t * Revokes the proxy object, preventing further access to the ObservablePoint instance.\n\t */\n\trevoke() {\n\t\tthis._revoke();\n\t}\n}\n","/**\n * @namespace input\n */\n\n/**\n * specify if melonJS should prevent all default browser action on registered events.\n * @default true\n */\n// eslint-disable-next-line prefer-const\nexport let preventDefault: boolean = true;\n\nexport * from \"./gamepad.ts\";\nexport * from \"./key.ts\";\nexport * from \"./keyboard.ts\";\nexport * from \"./pointerevent.ts\";\n","import { emit, KEYDOWN, KEYUP } from \"../system/event.ts\";\nimport { preventDefault as preventDefaultAction } from \"./input.ts\";\n\n// corresponding actions\nconst _keyStatus: Record<string, number> = {};\n\n// lock enable flag for keys\nconst _keyLock: Record<string, boolean> = {};\n\n// actual lock status of each key\nconst _keyLocked: Record<string, boolean> = {};\n\n// List of binded keys being held\nconst _keyRefs: Record<string, Record<number, boolean>> = {};\n\n// whether default event should be prevented for a given keypress\nconst _preventDefaultForKeys: Record<number, boolean> = {};\n\n// list of binded keys\nconst _keyBindings: Record<number, string> = {};\n\ntype KeyEventHandler = (\n\toptions:\n\t\t| { keyCode: number; mouseButton?: number | undefined }\n\t\t| KeyboardEvent,\n) => void;\n\nconst keyDownEvent: KeyEventHandler = (options) => {\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\tconst { keyCode } = options;\n\tconst action = _keyBindings[keyCode];\n\n\temit(KEYDOWN, action, keyCode, action ? !_keyLocked[action] : true);\n\n\tif (action) {\n\t\tif (!_keyLocked[action]) {\n\t\t\tconst trigger =\n\t\t\t\t(\"mouseButton\" in options ? options.mouseButton : undefined) ?? keyCode;\n\t\t\tif (!_keyRefs[action][trigger]) {\n\t\t\t\t_keyStatus[action]++;\n\t\t\t\t_keyRefs[action][trigger] = true;\n\t\t\t}\n\t\t}\n\t\tif (\"preventDefault\" in options && _preventDefaultForKeys[keyCode]) {\n\t\t\toptions.preventDefault();\n\t\t}\n\t}\n};\n\nconst keyUpEvent: KeyEventHandler = (options) => {\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\tconst { keyCode } = options;\n\tconst action = _keyBindings[keyCode];\n\n\temit(KEYUP, action, keyCode);\n\n\tif (action) {\n\t\tconst trigger =\n\t\t\t(\"mouseButton\" in options ? options.mouseButton : undefined) ?? keyCode;\n\t\tdelete _keyRefs[action][trigger];\n\n\t\tif (_keyStatus[action] > 0) {\n\t\t\t_keyStatus[action]--;\n\t\t}\n\n\t\t_keyLocked[action] = false;\n\n\t\tif (\"preventDefault\" in options && _preventDefaultForKeys[keyCode]) {\n\t\t\toptions.preventDefault();\n\t\t}\n\t}\n};\n\n/*\n * PUBLIC STUFF\n */\n\n/**\n * the default target element for keyboard events (usually the window element in which the game is running)\n */\nexport const keyBoardEventTarget = null;\n\nexport function initKeyboardEvent() {\n\tif (typeof globalThis.addEventListener === \"function\") {\n\t\tglobalThis.addEventListener(\n\t\t\t\"keydown\",\n\t\t\t(e) => {\n\t\t\t\tkeyDownEvent(e);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t\tglobalThis.addEventListener(\"keyup\", keyUpEvent, false);\n\t}\n}\n\n/**\n * return the key press status of the specified action\n * @param action - user defined corresponding action\n * @returns true if pressed\n * @example\n * if (me.input.isKeyPressed('left')) {\n *    //do something\n * }\n * else if (me.input.isKeyPressed('right')) {\n *    //do something else...\n * }\n * @category Input\n */\nexport function isKeyPressed(action: string) {\n\tif (_keyStatus[action] && !_keyLocked[action]) {\n\t\tif (_keyLock[action]) {\n\t\t\t_keyLocked[action] = true;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * return the key status of the specified action\n * @param action - user defined corresponding action\n * @returns down (true) or up(false)\n * @category Input\n */\nexport function keyStatus(action: string) {\n\treturn _keyStatus[action] > 0;\n}\n\n/**\n * trigger the specified key (simulated) event <br>\n * @param keyCode - (See {@link input.KEY})\n * @param [status=false] - true to trigger a key down event, or false for key up event\n * @param [mouseButton] - the mouse button to trigger\n * @example\n * // trigger a key press\n * me.input.triggerKeyEvent(me.input.KEY.LEFT, true);\n * @category Input\n */\nexport function triggerKeyEvent(\n\tkeyCode: number,\n\tstatus: boolean,\n\tmouseButton?: number,\n) {\n\tconst handler = status ? keyDownEvent : keyUpEvent;\n\thandler({ keyCode, mouseButton });\n}\n\n/**\n * associate a user defined action to a keycode\n * @param keyCode - (See {@link input.KEY})\n * @param action - user defined corresponding action\n * @param [lock=false] - cancel the keypress event once read\n * @param [preventDefault=input.preventDefault] - prevent default browser action\n * @example\n * // enable the keyboard\n * me.input.bindKey(me.input.KEY.LEFT,  \"left\");\n * me.input.bindKey(me.input.KEY.RIGHT, \"right\");\n * me.input.bindKey(me.input.KEY.X,     \"jump\", true);\n * me.input.bindKey(me.input.KEY.F1,    \"options\", true, true);\n */\nexport const bindKey = (\n\tkeyCode: number,\n\taction: string,\n\tlock?: boolean,\n\tpreventDefault = preventDefaultAction,\n) => {\n\t_keyBindings[keyCode] = action;\n\t_preventDefaultForKeys[keyCode] = preventDefault;\n\n\t_keyStatus[action] = 0;\n\t_keyLock[action] = lock ? lock : false;\n\t_keyLocked[action] = false;\n\t_keyRefs[action] = {};\n};\n\n/**\n * return the action associated with the given keycode\n * @param keyCode - (See {@link input.KEY})\n * @returns user defined associated action\n * @category Input\n */\nexport function getBindingKey(keyCode: number) {\n\treturn _keyBindings[keyCode];\n}\n\n/**\n * unlock a key manually\n * @param action - user defined corresponding action\n * @example\n * // Unlock jump when touching the ground\n * if (!this.falling && !this.jumping) {\n *     me.input.unlockKey(\"jump\");\n * }\n * @category Input\n */\nexport function unlockKey(action: string) {\n\t_keyLocked[action] = false;\n}\n\n/**\n * unbind the defined keycode\n * @param keyCode - (See {@link input.KEY})\n * @example\n * me.input.unbindKey(me.input.KEY.LEFT);\n * @category Input\n */\nexport function unbindKey(keyCode: number) {\n\t// clear the event status\n\tconst keybinding = _keyBindings[keyCode];\n\t_keyStatus[keybinding] = 0;\n\t_keyLock[keybinding] = false;\n\t_keyRefs[keybinding] = {};\n\t// remove the key binding\n\tdelete _keyBindings[keyCode];\n\tdelete _preventDefaultForKeys[keyCode];\n}\n","import {\n\temit,\n\tGAME_BEFORE_UPDATE,\n\tGAMEPAD_CONNECTED,\n\tGAMEPAD_DISCONNECTED,\n\tGAMEPAD_UPDATE,\n\thas,\n\ton,\n} from \"../system/event.ts\";\nimport { getBindingKey, triggerKeyEvent } from \"./keyboard.ts\";\n\ninterface GamepadMapping {\n\taxes: number[];\n\tbuttons: number[];\n\tanalog: number[];\n\tnormalize_fn: (value: number, axis: number, button: number) => number;\n}\n\ninterface ButtonBinding {\n\tkeyCode: number;\n\tvalue: number;\n\tpressed: boolean;\n\tthreshold?: number | undefined;\n}\n\ninterface GamepadBindings {\n\taxes: Record<string, Record<number, ButtonBinding>>;\n\tbuttons: Record<string, ButtonBinding>;\n}\n\n// Analog deadzone\nlet deadzone = 0.1;\n\n/**\n * Normalize axis values for wired Xbox 360\n * @ignore\n */\nfunction wiredXbox360NormalizeFn(\n\tthis: any,\n\tvalue: number,\n\t_axis: number,\n\tbutton: number,\n): number {\n\tif (\n\t\tbutton === this.GAMEPAD.BUTTONS.L2 ||\n\t\tbutton === this.GAMEPAD.BUTTONS.R2\n\t) {\n\t\treturn (value + 1) / 2;\n\t}\n\treturn value;\n}\n\n/**\n * Normalize axis values for OUYA\n * @ignore\n */\nfunction ouyaNormalizeFn(\n\tthis: any,\n\tvalue: number,\n\t_axis: number,\n\tbutton: number,\n): number {\n\tif (value > 0) {\n\t\tif (button === this.GAMEPAD.BUTTONS.L2) {\n\t\t\t// L2 is wonky; seems like the deadzone is around 20000\n\t\t\t// (That's over 15% of the total range!)\n\t\t\tvalue = Math.max(0, value - 20000) / 111070;\n\t\t} else {\n\t\t\t// Normalize [1..65536] => [0.0..0.5]\n\t\t\tvalue = (value - 1) / 131070;\n\t\t}\n\t} else {\n\t\t// Normalize [-65536..-1] => [0.5..1.0]\n\t\tvalue = (65536 + value) / 131070 + 0.5;\n\t}\n\n\treturn value;\n}\n\n// Match vendor and product codes for Firefox\nconst vendorProductRE = /^([0-9a-f]{1,4})-([0-9a-f]{1,4})-/i;\n\n// Match leading zeros\nconst leadingZeroRE = /^0+/;\n\n/**\n * Firefox reports different ids for gamepads depending on the platform:\n * - Windows: vendor and product codes contain leading zeroes\n * - Mac: vendor and product codes are sparse (no leading zeroes)\n *\n * This function normalizes the id to support both formats\n * @ignore\n */\nfunction addMapping(id: string, mapping: Partial<GamepadMapping>): void {\n\tconst expanded_id = id.replace(\n\t\tvendorProductRE,\n\t\t(_: string, a: string, b: string) => {\n\t\t\treturn `${\"000\".slice(a.length - 1) + a}-${\"000\".slice(b.length - 1)}${b}-`;\n\t\t},\n\t);\n\tconst sparse_id = id.replace(\n\t\tvendorProductRE,\n\t\t(_: string, a: string, b: string) => {\n\t\t\treturn `${a.replace(leadingZeroRE, \"\")}-${b.replace(leadingZeroRE, \"\")}-`;\n\t\t},\n\t);\n\n\t// Normalize optional parameters\n\tmapping.analog =\n\t\tmapping.analog ||\n\t\tmapping.buttons!.map(() => {\n\t\t\treturn -1;\n\t\t});\n\tmapping.normalize_fn =\n\t\tmapping.normalize_fn ||\n\t\tfunction (value: number) {\n\t\t\treturn value;\n\t\t};\n\n\tremap.set(expanded_id, mapping as GamepadMapping);\n\tremap.set(sparse_id, mapping as GamepadMapping);\n}\n\n// binding list\nconst bindings: Record<string, GamepadBindings | undefined> = {};\n\n// mapping list\nconst remap: Map<string, GamepadMapping> = new Map();\n\n// Default gamepad mappings\n(\n\t[\n\t\t// Firefox mappings\n\t\t[\n\t\t\t\"45e-28e-Xbox 360 Wired Controller\",\n\t\t\t{\n\t\t\t\taxes: [0, 1, 3, 4],\n\t\t\t\tbuttons: [11, 12, 13, 14, 8, 9, -1, -1, 5, 4, 6, 7, 0, 1, 2, 3, 10],\n\t\t\t\tanalog: [\n\t\t\t\t\t-1, -1, -1, -1, -1, -1, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t\t\t\t],\n\t\t\t\tnormalize_fn: wiredXbox360NormalizeFn,\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t\"54c-268-PLAYSTATION(R)3 Controller\",\n\t\t\t{\n\t\t\t\taxes: [0, 1, 2, 3],\n\t\t\t\tbuttons: [14, 13, 15, 12, 10, 11, 8, 9, 0, 3, 1, 2, 4, 6, 7, 5, 16],\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t\"54c-5c4-Wireless Controller\", // PS4 Controller\n\t\t\t{\n\t\t\t\taxes: [0, 1, 2, 3],\n\t\t\t\tbuttons: [1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 12, 13],\n\t\t\t},\n\t\t],\n\t\t[\n\t\t\t\"2836-1-OUYA Game Controller\",\n\t\t\t{\n\t\t\t\taxes: [0, 3, 7, 9],\n\t\t\t\tbuttons: [3, 6, 4, 5, 7, 8, 15, 16, -1, -1, 9, 10, 11, 12, 13, 14, -1],\n\t\t\t\tanalog: [\n\t\t\t\t\t-1, -1, -1, -1, -1, -1, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t\t\t\t],\n\t\t\t\tnormalize_fn: ouyaNormalizeFn,\n\t\t\t},\n\t\t],\n\n\t\t// Chrome mappings\n\t\t[\n\t\t\t\"OUYA Game Controller (Vendor: 2836 Product: 0001)\",\n\t\t\t{\n\t\t\t\taxes: [0, 1, 3, 4],\n\t\t\t\tbuttons: [0, 3, 1, 2, 4, 5, 12, 13, -1, -1, 6, 7, 8, 9, 10, 11, -1],\n\t\t\t\tanalog: [\n\t\t\t\t\t-1, -1, -1, -1, -1, -1, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n\t\t\t\t],\n\t\t\t\tnormalize_fn: ouyaNormalizeFn,\n\t\t\t},\n\t\t],\n\t] as [string, Partial<GamepadMapping>][]\n).forEach((value) => {\n\taddMapping(value[0], value[1]);\n});\n\n/**\n * Update gamepad status\n * @ignore\n */\nconst updateGamepads = function (): void {\n\tconst gamepads = navigator.getGamepads();\n\n\t// Trigger button bindings\n\tObject.keys(bindings).forEach((index) => {\n\t\tconst gamepad = gamepads[index as any];\n\t\tif (!gamepad) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet mapping: GamepadMapping | null = null;\n\t\tif (gamepad.mapping !== \"standard\") {\n\t\t\tmapping = remap.get(gamepad.id) || null;\n\t\t}\n\n\t\tconst binding = bindings[index];\n\t\tif (!binding) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Iterate all buttons that have active bindings\n\t\tObject.keys(binding.buttons).forEach((button) => {\n\t\t\tconst last = binding.buttons[button];\n\t\t\tlet mapped_button: any = button;\n\t\t\tlet mapped_axis = -1;\n\n\t\t\t// Remap buttons if necessary\n\t\t\tif (mapping) {\n\t\t\t\tmapped_button = mapping.buttons[button as any];\n\t\t\t\tmapped_axis = mapping.analog[button as any];\n\t\t\t\tif (mapped_button < 0 && mapped_axis < 0) {\n\t\t\t\t\t// Button is not mapped\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Get mapped button\n\t\t\tlet current: { value: number; pressed: boolean } = gamepad.buttons[\n\t\t\t\tmapped_button\n\t\t\t] || { value: 0, pressed: false };\n\n\t\t\t// Remap an axis to an analog button\n\t\t\tif (mapping) {\n\t\t\t\tif (mapped_axis >= 0) {\n\t\t\t\t\tconst value = mapping.normalize_fn(\n\t\t\t\t\t\tgamepad.axes[mapped_axis],\n\t\t\t\t\t\t-1,\n\t\t\t\t\t\t+button,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Create a new object, because GamepadButton is read-only\n\t\t\t\t\tcurrent = {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tpressed: current.pressed || Math.abs(value) >= deadzone,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\temit(GAMEPAD_UPDATE, index, \"buttons\", +button, current);\n\n\t\t\t// Edge detection\n\t\t\tif (!last.pressed && current.pressed) {\n\t\t\t\ttriggerKeyEvent(last.keyCode, true, Number(mapped_button) + 256);\n\t\t\t} else if (last.pressed && !current.pressed) {\n\t\t\t\ttriggerKeyEvent(last.keyCode, false, Number(mapped_button) + 256);\n\t\t\t}\n\n\t\t\t// Update last button state\n\t\t\tlast.value = current.value;\n\t\t\tlast.pressed = current.pressed;\n\t\t});\n\n\t\t// Iterate all axes that have active bindings\n\t\tObject.keys(binding.axes).forEach((axis) => {\n\t\t\tconst last = binding.axes[axis];\n\t\t\tlet mapped_axis: any = axis;\n\n\t\t\t// Remap buttons if necessary\n\t\t\tif (mapping) {\n\t\t\t\tmapped_axis = mapping.axes[axis as any];\n\t\t\t\tif (mapped_axis < 0) {\n\t\t\t\t\t// axe is not mapped\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// retrieve the current value and normalize if necessary\n\t\t\tlet value = gamepad.axes[mapped_axis];\n\t\t\tif (typeof value === \"undefined\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (mapping) {\n\t\t\t\tvalue = mapping.normalize_fn(value, +axis, -1);\n\t\t\t}\n\t\t\t// normalize value into a [-1, 1] range value (treat 0 as positive)\n\t\t\tlet range: number = Math.sign(value) || 1;\n\t\t\tif (last[range].keyCode === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst pressed =\n\t\t\t\tMath.abs(value) >= deadzone + Math.abs(last[range].threshold || 0);\n\n\t\t\temit(GAMEPAD_UPDATE, index, \"axes\", +axis, value as any);\n\n\t\t\t// Edge detection\n\t\t\tif (!last[range].pressed && pressed) {\n\t\t\t\t// Release the opposite direction, if necessary\n\t\t\t\tif (last[-range].pressed) {\n\t\t\t\t\ttriggerKeyEvent(\n\t\t\t\t\t\tlast[-range].keyCode,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tNumber(mapped_axis) + 256,\n\t\t\t\t\t);\n\t\t\t\t\tlast[-range].value = 0;\n\t\t\t\t\tlast[-range].pressed = false;\n\t\t\t\t}\n\n\t\t\t\ttriggerKeyEvent(last[range].keyCode, true, Number(mapped_axis) + 256);\n\t\t\t} else if ((last[range].pressed || last[-range].pressed) && !pressed) {\n\t\t\t\trange = last[range].pressed ? range : -range;\n\t\t\t\ttriggerKeyEvent(last[range].keyCode, false, Number(mapped_axis) + 256);\n\t\t\t}\n\n\t\t\t// Update last axis state\n\t\t\tlast[range].value = value;\n\t\t\tlast[range].pressed = pressed;\n\t\t});\n\t});\n};\n\n// gamepad connected callback\nif (\n\tglobalThis.navigator &&\n\ttypeof globalThis.navigator.getGamepads === \"function\"\n) {\n\tglobalThis.addEventListener(\n\t\t\"gamepadconnected\",\n\t\t(e: GamepadEvent) => {\n\t\t\temit(GAMEPAD_CONNECTED, e.gamepad);\n\t\t},\n\t\tfalse,\n\t);\n\n\t/*\n\t * gamepad disconnected callback\n\t */\n\tglobalThis.addEventListener(\n\t\t\"gamepaddisconnected\",\n\t\t(e: GamepadEvent) => {\n\t\t\temit(GAMEPAD_DISCONNECTED, e.gamepad);\n\t\t},\n\t\tfalse,\n\t);\n}\n\n/*\n * PUBLIC STUFF\n */\n\n/**\n * Namespace for standard gamepad mapping constants\n */\nexport const GAMEPAD = {\n\t/**\n\t * Standard gamepad mapping information for axes<br>\n\t * <ul>\n\t *   <li>Left control stick: <code>LX</code> (horizontal), <code>LY</code> (vertical)</li>\n\t *   <li>Right control stick: <code>RX</code> (horizontal), <code>RY</code> (vertical)</li>\n\t *   <li>Extras: <code>EXTRA_1</code>, <code>EXTRA_2</code>, <code>EXTRA_3</code>, <code>EXTRA_4</code></li>\n\t * </ul>\n\t * @see {@link https://w3c.github.io/gamepad/#remapping}\n\t */\n\tAXES: {\n\t\tLX: 0,\n\t\tLY: 1,\n\t\tRX: 2,\n\t\tRY: 3,\n\t\tEXTRA_1: 4,\n\t\tEXTRA_2: 5,\n\t\tEXTRA_3: 6,\n\t\tEXTRA_4: 7,\n\t},\n\n\t/**\n\t * Standard gamepad mapping information for buttons<br>\n\t * <ul>\n\t *   <li>Face buttons: <code>FACE_1</code>, <code>FACE_2</code>, <code>FACE_3</code>, <code>FACE_4</code></li>\n\t *   <li>D-Pad: <code>UP</code>, <code>DOWN</code>, <code>LEFT</code>, <code>RIGHT</code></li>\n\t *   <li>Shoulder buttons: <code>L1</code>, <code>L2</code>, <code>R1</code>, <code>R2</code></li>\n\t *   <li>Analog stick (clicks): <code>L3</code>, <code>R3</code></li>\n\t *   <li>Navigation: <code>SELECT</code> (<code>BACK</code>), <code>START</code> (<code>FORWARD</code>), <code>HOME</code></li>\n\t *   <li>Extras: <code>EXTRA_1</code>, <code>EXTRA_2</code>, <code>EXTRA_3</code>, <code>EXTRA_4</code></li>\n\t * </ul>\n\t * @see {@link https://w3c.github.io/gamepad/#remapping}\n\t */\n\tBUTTONS: {\n\t\tFACE_1: 0,\n\t\tFACE_2: 1,\n\t\tFACE_3: 2,\n\t\tFACE_4: 3,\n\t\tL1: 4,\n\t\tR1: 5,\n\t\tL2: 6,\n\t\tR2: 7,\n\t\tSELECT: 8,\n\t\tBACK: 8,\n\t\tSTART: 9,\n\t\tFORWARD: 9,\n\t\tL3: 10,\n\t\tR3: 11,\n\t\tUP: 12,\n\t\tDOWN: 13,\n\t\tLEFT: 14,\n\t\tRIGHT: 15,\n\t\tHOME: 16,\n\t\tEXTRA_1: 17,\n\t\tEXTRA_2: 18,\n\t\tEXTRA_3: 19,\n\t\tEXTRA_4: 20,\n\t},\n} as const;\n\n/**\n * Associate a gamepad event to a keycode\n * @param index - Gamepad index\n * @param button - Button/Axis definition\n * @param button.type - \"buttons\" or \"axes\"\n * @param button.code - button or axis code id\n * @param button.threshold - value indicating when the axis should trigger the keycode\n * @param keyCode - (See {@link input.KEY})\n * @example\n * // enable the keyboard\n * me.input.bindKey(me.input.KEY.X, \"shoot\");\n * ...\n * // map the lower face button on the first gamepad to the X key\n * me.input.bindGamepad(0, {type:\"buttons\", code: me.input.GAMEPAD.BUTTONS.FACE_1}, me.input.KEY.X);\n * // map the left axis value on the first gamepad to the LEFT key\n * me.input.bindGamepad(0, {type:\"axes\", code: me.input.GAMEPAD.AXES.LX, threshold: -0.5}, me.input.KEY.LEFT);\n * @category Input\n */\nexport function bindGamepad(\n\tindex: number,\n\tbutton: { type: \"buttons\" | \"axes\"; code: number; threshold?: number },\n\tkeyCode: number,\n): void {\n\t// Throw an exception if no action is defined for the specified keycode\n\tif (!getBindingKey(keyCode)) {\n\t\tthrow new Error(`no action defined for keycode ${keyCode}`);\n\t}\n\n\t// register to the the update event if not yet done and supported by the browser\n\t// if not supported, the function will fail silently (-> update loop won't be called)\n\tif (\n\t\t!has(GAME_BEFORE_UPDATE, updateGamepads) &&\n\t\ttypeof navigator.getGamepads === \"function\"\n\t) {\n\t\ton(GAME_BEFORE_UPDATE, updateGamepads);\n\t}\n\n\t// Allocate bindings if not defined\n\tif (!bindings[index]) {\n\t\tbindings[index] = {\n\t\t\taxes: {},\n\t\t\tbuttons: {},\n\t\t};\n\t}\n\n\tconst mapping: ButtonBinding = {\n\t\tkeyCode: keyCode,\n\t\tvalue: 0,\n\t\tpressed: false,\n\t\tthreshold: button.threshold, // can be undefined\n\t};\n\tconst binding = bindings[index][button.type] as any;\n\n\t// Map the gamepad button or axis to the keycode\n\tif (button.type === \"buttons\") {\n\t\t// buttons are defined by a `gamePadButton` object\n\t\tbinding[button.code] = mapping;\n\t} else if (button.type === \"axes\") {\n\t\t// normalize threshold into a value that can represent both side of the axis\n\t\tconst range = Math.sign(button.threshold!) || 1;\n\t\t// axes are defined using two objects; one for negative and one for positive\n\t\tif (!binding[button.code]) {\n\t\t\tbinding[button.code] = {};\n\t\t}\n\t\tconst axes = binding[button.code];\n\t\taxes[range] = mapping;\n\n\t\t// Ensure the opposite axis exists\n\t\tif (!axes[-range]) {\n\t\t\taxes[-range] = {\n\t\t\t\tkeyCode: 0,\n\t\t\t\tvalue: 0,\n\t\t\t\tpressed: false,\n\t\t\t\tthreshold: -range,\n\t\t\t};\n\t\t}\n\t}\n}\n\n/**\n * unbind the defined keycode\n * @param index - Gamepad index\n * @param button - (See {@link input.GAMEPAD})\n * @example\n * me.input.unbindGamepad(0, me.input.GAMEPAD.BUTTONS.FACE_1);\n * @category Input\n */\nexport function unbindGamepad(index: number, button: number): void {\n\tif (!bindings[index]) {\n\t\tthrow new Error(`no bindings for gamepad ${index}`);\n\t}\n\tbindings[index].buttons[button] = {} as ButtonBinding;\n}\n\n/**\n * Set deadzone for analog gamepad inputs<br>\n * The default deadzone is 0.1 (10%) Analog values less than this will be ignored\n * @param value - Deadzone value\n * @category Input\n */\nexport function setGamepadDeadzone(value: number): void {\n\tdeadzone = value;\n}\n\n/**\n * specify a custom mapping for a specific gamepad id<br>\n * see below for the default mapping : <br>\n * <center><img src=\"images/gamepad_diagram.png\"/></center><br>\n * @param id - Gamepad id string\n * @param mapping - A hash table\n * @example\n * // A weird controller that has its axis mappings reversed\n * me.input.setGamepadMapping(\"Generic USB Controller\", {\n *   \"axes\" : [ 3, 2, 1, 0 ],\n *   \"buttons\" : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]\n * });\n *\n * // Mapping extra axes to analog buttons\n * me.input.setGamepadMapping(\"Generic Analog Controller\", {\n *   \"axes\" : [ 0, 1, 2, 3 ],\n *   \"buttons\" : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ],\n *\n *   // Raw axis 4 is mapped to GAMEPAD.BUTTONS.FACE_1\n *   // Raw axis 5 is mapped to GAMEPAD.BUTTONS.FACE_2\n *   // etc...\n *   // Also maps left and right triggers\n *   \"analog\" : [ 4, 5, 6, 7, -1, -1, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],\n *\n *   // Normalize the value of button L2: [-1.0..1.0] => [0.0..1.0]\n *   \"normalize_fn\" : function (value, axis, button) {\n *     return ((button === me.input.GAMEPAD.BUTTONS.L2) ? ((value + 1) / 2) : value) || 0;\n *   }\n * });\n */\nexport const setGamepadMapping: (\n\tid: string,\n\tmapping: Partial<GamepadMapping>,\n) => void = addMapping;\n","/**\n * standard keyboard constants\n */\nexport const KEY = {\n\tBACKSPACE: 8,\n\tTAB: 9,\n\tENTER: 13,\n\tSHIFT: 16,\n\tCTRL: 17,\n\tALT: 18,\n\tPAUSE: 19,\n\tCAPS_LOCK: 20,\n\tESC: 27,\n\tSPACE: 32,\n\tPAGE_UP: 33,\n\tPAGE_DOWN: 34,\n\tEND: 35,\n\tHOME: 36,\n\tLEFT: 37,\n\tUP: 38,\n\tRIGHT: 39,\n\tDOWN: 40,\n\tPRINT_SCREEN: 42,\n\tINSERT: 45,\n\tDELETE: 46,\n\tNUM0: 48,\n\tNUM1: 49,\n\tNUM2: 50,\n\tNUM3: 51,\n\tNUM4: 52,\n\tNUM5: 53,\n\tNUM6: 54,\n\tNUM7: 55,\n\tNUM8: 56,\n\tNUM9: 57,\n\tA: 65,\n\tB: 66,\n\tC: 67,\n\tD: 68,\n\tE: 69,\n\tF: 70,\n\tG: 71,\n\tH: 72,\n\tI: 73,\n\tJ: 74,\n\tK: 75,\n\tL: 76,\n\tM: 77,\n\tN: 78,\n\tO: 79,\n\tP: 80,\n\tQ: 81,\n\tR: 82,\n\tS: 83,\n\tT: 84,\n\tU: 85,\n\tV: 86,\n\tW: 87,\n\tX: 88,\n\tY: 89,\n\tZ: 90,\n\tWINDOW_KEY: 91,\n\tNUMPAD0: 96,\n\tNUMPAD1: 97,\n\tNUMPAD2: 98,\n\tNUMPAD3: 99,\n\tNUMPAD4: 100,\n\tNUMPAD5: 101,\n\tNUMPAD6: 102,\n\tNUMPAD7: 103,\n\tNUMPAD8: 104,\n\tNUMPAD9: 105,\n\tMULTIPLY: 106,\n\tADD: 107,\n\tSUBSTRACT: 109,\n\tDECIMAL: 110,\n\tDIVIDE: 111,\n\tF1: 112,\n\tF2: 113,\n\tF3: 114,\n\tF4: 115,\n\tF5: 116,\n\tF6: 117,\n\tF7: 118,\n\tF8: 119,\n\tF9: 120,\n\tF10: 121,\n\tF11: 122,\n\tF12: 123,\n\tTILDE: 126,\n\tNUM_LOCK: 144,\n\tSCROLL_LOCK: 145,\n\tSEMICOLON: 186,\n\tPLUS: 187,\n\tCOMMA: 188,\n\tMINUS: 189,\n\tPERIOD: 190,\n\tFORWAND_SLASH: 191,\n\tGRAVE_ACCENT: 192,\n\tOPEN_BRACKET: 219,\n\tBACK_SLASH: 220,\n\tCLOSE_BRACKET: 221,\n\tSINGLE_QUOTE: 222,\n};\n","import { TupleToUnion } from \"type-fest\";\nimport { Point } from \"../geometries/point.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { damp, lerp } from \"./math.ts\";\nimport { Vector2d } from \"./vector2d\";\nimport { Vector3d } from \"./vector3d\";\n\nconst propertiesToObserve = [\"x\", \"y\", \"z\"] as const;\ntype ObservableProperty = keyof Pick<\n\tVector3d,\n\tTupleToUnion<typeof propertiesToObserve>\n>;\nconst setOfProperties = new Set<ObservableProperty>(propertiesToObserve);\n\nconst isObservableProperty = (\n\tproperty: string | symbol,\n): property is ObservableProperty => {\n\treturn setOfProperties.has(property as any);\n};\n\n/**\n * Represents a point in a 3D coordinate vector that can be observed for changes.\n * @category Math\n */\nexport class ObservableVector3d {\n\tprivate _callback: () => void;\n\tprivate _vector3d: Vector3d;\n\tprivate _revoke: () => void;\n\n\tprivate callBackEnabled: boolean = true;\n\n\t/**\n\t * Creates a new ObservableVector3d instance.\n\t * @param x - The x-coordinate of the vector. Default is 0.\n\t * @param y - The y-coordinate of the vector. Default is 0.\n\t * @param z - The z-coordinate of the vector. Default is 0.\n\t * @param callback - The callback function to be called when the point changes. Default is undefined.\n\t */\n\tconstructor(\n\t\tx: number = 0,\n\t\ty: number = 0,\n\t\tz: number = 0,\n\t\tcallback?: () => void,\n\t) {\n\t\tconst { proxy, revoke } = Proxy.revocable(new Vector3d(x, y, z), {\n\t\t\tset: (target, property, value) => {\n\t\t\t\tif (isObservableProperty(property)) {\n\t\t\t\t\tReflect.set(target, property, value);\n\t\t\t\t\tif (this.callBackEnabled) {\n\t\t\t\t\t\tthis._callback?.();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t});\n\n\t\tthis._revoke = revoke;\n\t\tthis._vector3d = proxy;\n\n\t\tif (callback) {\n\t\t\tthis.setCallback(callback);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @param z - The new z-coordinate value.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tset(x = 0, y = 0, z = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._vector3d.x = x;\n\t\tthis._vector3d.y = y;\n\t\tthis._vector3d.z = z;\n\t\tthis._callback?.();\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point without triggering the callback.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @param z -\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetMuted(x = 0, y = 0, z = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._vector3d.x = x;\n\t\tthis._vector3d.y = y;\n\t\tthis._vector3d.z = z;\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the x-coordinate of the point.\n\t */\n\tget x() {\n\t\treturn this._vector3d.x;\n\t}\n\n\t/**\n\t * Sets the x-coordinate of the point.\n\t * @param value - The new x-coordinate value.\n\t */\n\tset x(value: number) {\n\t\tthis._vector3d.x = value;\n\t}\n\n\t/**\n\t * Gets the y-coordinate of the point.\n\t */\n\tget y() {\n\t\treturn this._vector3d.y;\n\t}\n\n\t/**\n\t * Sets the y-coordinate of the point.\n\t * @param value - The new y-coordinate value.\n\t */\n\tset y(value: number) {\n\t\tthis._vector3d.y = value;\n\t}\n\n\tget z() {\n\t\treturn this._vector3d.z;\n\t}\n\n\tset z(value: number) {\n\t\tthis._vector3d.z = value;\n\t}\n\n\t/**\n\t * Sets the callback function to be called when the point changes.\n\t * @param callback - The callback function.\n\t */\n\tsetCallback(callback: () => void) {\n\t\tthis._callback = callback;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to 0\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetZero() {\n\t\treturn this.set(0, 0, 0);\n\t}\n\n\t/**\n\t * set the Vector x and y properties using the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetV(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this.set(v.x, v.y, \"z\" in v ? v.z : undefined);\n\t}\n\n\t/**\n\t * Add the passed vector to this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tadd(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this.set(this.x + v.x, this.y + v.y, this.z + (\"z\" in v ? v.z : 0));\n\t}\n\n\t/**\n\t * Subtract the passed vector from this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsub(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this.set(this.x - v.x, this.y - v.y, this.z - (\"z\" in v ? v.z : 0));\n\t}\n\n\t/**\n\t * Multiply this vector values by the given scalar\n\t * @param x x component\n\t * @param [y] y component\n\t * @param [z] z component\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x, z = 1) {\n\t\treturn this.set(this.x * x, this.y * y, this.z * z);\n\t}\n\n\t/**\n\t * Multiply this vector values by the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector3d | ObservableVector3d) {\n\t\treturn this.scale(v.x, v.y, v.z);\n\t}\n\n\t/**\n\t * Convert this vector into isometric coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\ttoIso() {\n\t\treturn this.set(this.x - this.y, (this.x + this.y) * 0.5, this.z);\n\t}\n\n\t/**\n\t * Convert this vector into 2d coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\tto2d() {\n\t\treturn this.set(this.y + this.x / 2, this.y - this.x / 2, this.z);\n\t}\n\n\t/**\n\t * Divide this vector values by the passed value\n\t * @param n - the value to divide the vector by\n\t * @returns Reference to this object for method chaining\n\t */\n\tdiv(n: number) {\n\t\treturn this.set(this.x / n, this.y / n, this.z / n);\n\t}\n\n\t/**\n\t * Update this vector values to absolute values\n\t * @returns Reference to this object for method chaining\n\t */\n\tabs() {\n\t\treturn this.set(\n\t\t\tthis.x < 0 ? -this.x : this.x,\n\t\t\tthis.y < 0 ? -this.y : this.y,\n\t\t\tthis.z < 0 ? -this.z : this.z,\n\t\t);\n\t}\n\n\t/**\n\t * Clamp the vector value within the specified value range\n\t * @param low lower bound\n\t * @param high upper bound\n\t * @returns new Vector3d\n\t */\n\tclamp(low: number, high: number) {\n\t\treturn this._vector3d.clamp(low, high);\n\t}\n\n\t/**\n\t * Clamp this vector value within the specified value range\n\t * @param low lower bound\n\t * @param high upper bound\n\t * @returns Reference to this object for method chaining\n\t */\n\tclampSelf(low: number, high: number) {\n\t\treturn this._vector3d.clampSelf(low, high);\n\t}\n\n\t/**\n\t * Update this vector with the minimum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tminV(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\tconst _vz = \"z\" in v ? v.z : 0;\n\t\treturn this.set(\n\t\t\tthis.x < v.x ? this.x : v.x,\n\t\t\tthis.y < v.y ? this.y : v.y,\n\t\t\tthis.z < _vz ? this.z : _vz,\n\t\t);\n\t}\n\n\t/**\n\t * Update this vector with the maximum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tmaxV(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\tconst _vz = \"z\" in v ? v.z : 0;\n\t\treturn this.set(\n\t\t\tthis.x > v.x ? this.x : v.x,\n\t\t\tthis.y > v.y ? this.y : v.y,\n\t\t\tthis.z > _vz ? this.z : _vz,\n\t\t);\n\t}\n\n\t/**\n\t * Floor the vector values\n\t * @returns new Vector3d\n\t */\n\tfloor() {\n\t\treturn this._vector3d.floor();\n\t}\n\n\t/**\n\t * Floor this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tfloorSelf() {\n\t\treturn this._vector3d.floorSelf();\n\t}\n\n\t/**\n\t * Ceil the vector values\n\t * @returns new Vector3d\n\t */\n\tceil() {\n\t\treturn this._vector3d.ceil();\n\t}\n\n\t/**\n\t * Ceil this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tceilSelf() {\n\t\treturn this._vector3d.ceilSelf();\n\t}\n\n\t/**\n\t * Negate the vector values\n\t * @returns new Vector3d\n\t */\n\tnegate() {\n\t\treturn this._vector3d.negate();\n\t}\n\n\t/**\n\t * Negate this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tnegateSelf() {\n\t\treturn this._vector3d.negateSelf();\n\t}\n\n\t/**\n\t * Copy the components of the given vector into this one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this.set(v.x, v.y, \"z\" in v ? v.z : 0);\n\t}\n\n\t/**\n\t * return true if this vector is equal to the given values or vector\n\t * @param args other vector or vector components\n\t * @returns true if both vectors are equal\n\t */\n\tequals(\n\t\t...args:\n\t\t\t| [number]\n\t\t\t| [Vector2d]\n\t\t\t| [Vector3d]\n\t\t\t| [ObservableVector3d]\n\t\t\t| [number, number]\n\t\t\t| [number, number, number]\n\t): boolean {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\t\tlet _z: number;\n\n\t\tif (args.length === 1) {\n\t\t\tconst arg = args[0];\n\t\t\tif (typeof arg === \"object\") {\n\t\t\t\t[_x, _y, _z] = [arg.x, arg.y, \"z\" in arg ? arg.z : this.z];\n\t\t\t} else {\n\t\t\t\t_x = arg;\n\t\t\t\t_y = this.y;\n\t\t\t\t_z = this.z;\n\t\t\t}\n\t\t} else if (args.length === 2) {\n\t\t\t[_x, _y, _z] = [args[0], args[1], this.z];\n\t\t} else {\n\t\t\t[_x, _y, _z] = args;\n\t\t}\n\n\t\treturn this.x === _x && this.y === _y && this.z === _z;\n\t}\n\n\t/**\n\t * normalize this vector (scale the vector so that its magnitude is 1)\n\t * @returns Reference to this object for method chaining\n\t */\n\tnormalize() {\n\t\treturn this._vector3d.normalize();\n\t}\n\n\t/**\n\t * change this vector to be perpendicular to what it was before.<br>\n\t * (Effectively rotates it 90 degrees in a clockwise direction around the z axis)\n\t * @returns Reference to this object for method chaining\n\t */\n\tperp() {\n\t\treturn this._vector3d.perp();\n\t}\n\n\t/**\n\t * Rotate this vector (counter-clockwise) by the specified angle (in radians) around the z axis\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: Vector2d | Point | Vector3d) {\n\t\tlet cx = 0;\n\t\tlet cy = 0;\n\n\t\tif (v) {\n\t\t\tcx = v.x;\n\t\t\tcy = v.y;\n\t\t}\n\n\t\tconst x = this.x - cx;\n\t\tconst y = this.y - cy;\n\n\t\tconst c = Math.cos(angle);\n\t\tconst s = Math.sin(angle);\n\n\t\treturn this.set(x * c - y * s + cx, x * s + y * c + cy, this.z);\n\t}\n\n\t/**\n\t * return the dot product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The dot product.\n\t */\n\tdot(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this._vector3d.dot(v as Vector3d);\n\t}\n\n\t/**\n\t * calculate the cross product of this vector and the passed one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcross(v: Vector3d | ObservableVector3d) {\n\t\tconst ax = this.x;\n\t\tconst ay = this.y;\n\t\tconst az = this.z;\n\t\tconst bx = v.x;\n\t\tconst by = v.y;\n\t\tconst bz = v.z;\n\n\t\tthis.set(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the square length of this vector\n\t * @returns The length^2 of this vector.\n\t */\n\tlength2(): number {\n\t\treturn this._vector3d.length2();\n\t}\n\n\t/**\n\t * return the length (magnitude) of this vector\n\t * @returns the length of this vector\n\t */\n\tlength() {\n\t\treturn this._vector3d.length();\n\t}\n\n\t/**\n\t * Linearly interpolate between this vector and the given one.\n\t * Frame-rate dependent at a fixed alpha — for smooth tracking\n\t * use {@link ObservableVector3d.damp} instead.\n\t * @param v - other vector\n\t * @param alpha - distance along the line (alpha = 0 → this vector, alpha = 1 → `v`)\n\t * @returns Reference to this object for method chaining\n\t */\n\tlerp(v: Vector3d | ObservableVector3d, alpha: number) {\n\t\tthis.set(\n\t\t\tlerp(this.x, v.x, alpha),\n\t\t\tlerp(this.y, v.y, alpha),\n\t\t\tlerp(this.z, v.z, alpha),\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Frame-rate independent exponential damping toward `target`.\n\t * Per-component wrapper around {@link damp} that triggers the\n\t * change-callback exactly once via the underlying {@link set}.\n\t * @param target - vector to approach (Vector2d treats `z` as 0)\n\t * @param lambda - decay rate in `1/seconds` (higher = snappier)\n\t * @param dt - delta time in **seconds**\n\t * @returns Reference to this object for method chaining\n\t */\n\tdamp(\n\t\ttarget: Vector2d | Vector3d | ObservableVector3d,\n\t\tlambda: number,\n\t\tdt: number,\n\t) {\n\t\tthis.set(\n\t\t\tdamp(this.x, target.x, lambda, dt),\n\t\t\tdamp(this.y, target.y, lambda, dt),\n\t\t\tdamp(this.z, \"z\" in target ? target.z : 0, lambda, dt),\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * interpolate the position of this vector on the x and y axis towards the given one by the given maximum step.\n\t * @param target other vector\n\t * @param step - the maximum step per iteration (Negative values will push the vector away from the target)\n\t * @returns Reference to this object for method chaining\n\t */\n\tmoveTowards(target: Vector2d | Vector3d | ObservableVector3d, step: number) {\n\t\tconst dx = target.x - this.x;\n\t\tconst dy = target.y - this.y;\n\n\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\n\t\t// within one step (and not fleeing): land exactly on the target (x/y\n\t\t// only, this is a 2D interpolation) — and always mutate/return `this`\n\t\t// (via set(), so observers fire), never the caller's target\n\t\tif (distance === 0 || (step >= 0 && distance <= step)) {\n\t\t\tthis.set(target.x, target.y, this.z);\n\t\t\treturn this;\n\t\t}\n\n\t\tconst angle = Math.atan2(dy, dx);\n\t\tthis.set(\n\t\t\tthis.x + Math.cos(angle) * step,\n\t\t\tthis.y + Math.sin(angle) * step,\n\t\t\tthis.z,\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the distance between this vector and the passed one\n\t * @param v other vector\n\t * @returns distance\n\t */\n\tdistance(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this._vector3d.distance(v as Vector3d);\n\t}\n\n\t/**\n\t * return the angle between this vector and the passed one\n\t * @param v other vector\n\t * @returns angle in radians\n\t */\n\tangle(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\treturn this._vector3d.angle(v as Vector3d);\n\t}\n\n\t/**\n\t * project this vector on to another vector.\n\t * @param v - The vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tproject(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\tconst ratio = this.dot(v) / v.length2();\n\t\treturn this.scale(ratio, ratio, ratio);\n\t}\n\n\t/**\n\t * Project this vector onto a vector of unit length.<br>\n\t * This is slightly more efficient than `project` when dealing with unit vectors.\n\t * @param v - The unit vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tprojectN(v: Vector2d | Vector3d | ObservableVector3d) {\n\t\tconst ratio = this.dot(v) / v.length2();\n\t\treturn this.scale(ratio, ratio, ratio);\n\t}\n\n\t/**\n\t * return a clone copy of this vector\n\t * @param [cb] callback function to override the clone values\n\t * @returns new Vector3d\n\t */\n\tclone(cb?: () => void) {\n\t\treturn observableVector3dPool.get(this.x, this.y, this.z, cb);\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns stringified representation\n\t */\n\ttoString() {\n\t\treturn this._vector3d.toString();\n\t}\n\n\t/**\n\t * Revokes the proxy object, preventing further access to the ObservablePoint instance.\n\t */\n\trevoke() {\n\t\tthis._revoke();\n\t}\n}\n\nexport const observableVector3dPool = createPool<\n\tObservableVector3d,\n\t[\n\t\tx?: number | undefined,\n\t\ty?: number | undefined,\n\t\tz?: number | undefined,\n\t\tcallback?: () => void,\n\t]\n>((x, y, z, cb) => {\n\tconst vector = new ObservableVector3d(x, y, z, cb);\n\n\treturn {\n\t\tinstance: vector,\n\t\treset(x = 0, y = 0, z = 0, cb?: () => void): void {\n\t\t\tvector.setMuted(x, y, z);\n\t\t\tif (cb) {\n\t\t\t\tvector.setCallback(cb);\n\t\t\t}\n\t\t},\n\t};\n});\n","import type { XYPoint } from \"../utils/types.ts\";\nimport type { Vector2d } from \"./vector2d.ts\";\n\n/**\n * Normalize a vertex array to fit within a unit cube centered at the origin.\n * Vertices are scaled uniformly so that the largest axis spans [-0.5, 0.5].\n * Modifies the array in place.\n * @param vertices - vertex positions as x,y,z triplets\n */\nexport function normalizeVertices(vertices: Float32Array) {\n\tconst len = vertices.length;\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet minZ = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\tlet maxZ = -Infinity;\n\n\t// single pass to find bounds — cache indexed reads\n\tfor (let i = 0; i < len; i += 3) {\n\t\tconst x = vertices[i];\n\t\tconst y = vertices[i + 1];\n\t\tconst z = vertices[i + 2];\n\t\tif (x < minX) {\n\t\t\tminX = x;\n\t\t}\n\t\tif (x > maxX) {\n\t\t\tmaxX = x;\n\t\t}\n\t\tif (y < minY) {\n\t\t\tminY = y;\n\t\t}\n\t\tif (y > maxY) {\n\t\t\tmaxY = y;\n\t\t}\n\t\tif (z < minZ) {\n\t\t\tminZ = z;\n\t\t}\n\t\tif (z > maxZ) {\n\t\t\tmaxZ = z;\n\t\t}\n\t}\n\n\tconst cx = (minX + maxX) * 0.5;\n\tconst cy = (minY + maxY) * 0.5;\n\tconst cz = (minZ + maxZ) * 0.5;\n\t// precompute reciprocal to replace division with multiplication in the loop\n\tconst invScale =\n\t\t1.0 / Math.max(maxX - minX || 1, maxY - minY || 1, maxZ - minZ || 1);\n\n\tfor (let i = 0; i < len; i += 3) {\n\t\tvertices[i] = (vertices[i] - cx) * invScale;\n\t\tvertices[i + 1] = (vertices[i + 1] - cy) * invScale;\n\t\tvertices[i + 2] = (vertices[i + 2] - cz) * invScale;\n\t}\n}\n\n/**\n * Project 3D vertices through a 4x4 matrix with perspective divide,\n * mapping the result to a 2D display area.\n * @param src - source vertex positions (x,y,z triplets)\n * @param dst - destination array (x,y,z triplets, written in place)\n * @param count - number of vertices to project\n * @param matrix - 4x4 matrix values (column-major, 16 elements)\n * @param width - display width to map projected x to\n * @param height - display height to map projected y to\n * @param offsetX - x offset added to each projected vertex\n * @param offsetY - y offset added to each projected vertex\n * @param zScale - scale factor for Z output (0 = don't compute Z)\n */\nexport function projectVertices(\n\tsrc: Float32Array,\n\tdst: Float32Array,\n\tcount: number,\n\tmatrix: Float32Array,\n\twidth: number,\n\theight: number,\n\toffsetX = 0,\n\toffsetY = 0,\n\tzScale = 0,\n) {\n\t// hoist all constants out of the loop\n\tconst centerX = width * 0.5 + offsetX;\n\tconst centerY = height * 0.5 + offsetY;\n\tconst negHeight = -height;\n\n\t// extract matrix elements once (avoids repeated indexed access)\n\tconst m0 = matrix[0];\n\tconst m1 = matrix[1];\n\tconst m2 = matrix[2];\n\tconst m3 = matrix[3];\n\tconst m4 = matrix[4];\n\tconst m5 = matrix[5];\n\tconst m6 = matrix[6];\n\tconst m7 = matrix[7];\n\tconst m8 = matrix[8];\n\tconst m9 = matrix[9];\n\tconst m10 = matrix[10];\n\tconst m11 = matrix[11];\n\tconst m12 = matrix[12];\n\tconst m13 = matrix[13];\n\tconst m14 = matrix[14];\n\tconst m15 = matrix[15];\n\n\tif (zScale !== 0) {\n\t\tconst negZScale = -zScale;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst vx = src[i3];\n\t\t\tconst vy = src[i3 + 1];\n\t\t\tconst vz = src[i3 + 2];\n\n\t\t\tconst tw = m3 * vx + m7 * vy + m11 * vz + m15;\n\t\t\tconst invW = tw !== 0 ? 1.0 / tw : 1.0;\n\n\t\t\tdst[i3] = (m0 * vx + m4 * vy + m8 * vz + m12) * invW * width + centerX;\n\t\t\tdst[i3 + 1] =\n\t\t\t\t(m1 * vx + m5 * vy + m9 * vz + m13) * invW * negHeight + centerY;\n\t\t\tdst[i3 + 2] = (m2 * vx + m6 * vy + m10 * vz + m14) * invW * negZScale;\n\t\t}\n\t} else {\n\t\t// fast path: skip Z computation entirely\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst vx = src[i3];\n\t\t\tconst vy = src[i3 + 1];\n\t\t\tconst vz = src[i3 + 2];\n\n\t\t\tconst tw = m3 * vx + m7 * vy + m11 * vz + m15;\n\t\t\tconst invW = tw !== 0 ? 1.0 / tw : 1.0;\n\n\t\t\tdst[i3] = (m0 * vx + m4 * vy + m8 * vz + m12) * invW * width + centerX;\n\t\t\tdst[i3 + 1] =\n\t\t\t\t(m1 * vx + m5 * vy + m9 * vz + m13) * invW * negHeight + centerY;\n\t\t\tdst[i3 + 2] = 0;\n\t\t}\n\t}\n}\n\n/**\n * Extend an axis-aligned bounding box by a set of 3D vertices transformed\n * through a 4x4 matrix (translation included). `min` / `max` are updated in\n * place, so the function can be called repeatedly to accumulate the bounds of\n * many transformed vertex sets (e.g. every node of a scene graph). Seed `min`\n * with `+Infinity` and `max` with `-Infinity` before the first call.\n * @param src - source vertex positions (x,y,z triplets)\n * @param count - number of vertices to read from `src`\n * @param matrix - 4x4 matrix values (column-major, 16 elements)\n * @param min - `[x,y,z]` lower corner, extended in place\n * @param max - `[x,y,z]` upper corner, extended in place\n */\nexport function transformedBounds(\n\tsrc: Float32Array,\n\tcount: number,\n\tmatrix: ArrayLike<number>,\n\tmin: number[],\n\tmax: number[],\n) {\n\tconst m0 = matrix[0];\n\tconst m1 = matrix[1];\n\tconst m2 = matrix[2];\n\tconst m4 = matrix[4];\n\tconst m5 = matrix[5];\n\tconst m6 = matrix[6];\n\tconst m8 = matrix[8];\n\tconst m9 = matrix[9];\n\tconst m10 = matrix[10];\n\tconst m12 = matrix[12];\n\tconst m13 = matrix[13];\n\tconst m14 = matrix[14];\n\n\tfor (let i = 0; i < count; i++) {\n\t\tconst i3 = i * 3;\n\t\tconst vx = src[i3];\n\t\tconst vy = src[i3 + 1];\n\t\tconst vz = src[i3 + 2];\n\n\t\tconst wx = m0 * vx + m4 * vy + m8 * vz + m12;\n\t\tconst wy = m1 * vx + m5 * vy + m9 * vz + m13;\n\t\tconst wz = m2 * vx + m6 * vy + m10 * vz + m14;\n\n\t\tif (wx < min[0]) {\n\t\t\tmin[0] = wx;\n\t\t}\n\t\tif (wy < min[1]) {\n\t\t\tmin[1] = wy;\n\t\t}\n\t\tif (wz < min[2]) {\n\t\t\tmin[2] = wz;\n\t\t}\n\t\tif (wx > max[0]) {\n\t\t\tmax[0] = wx;\n\t\t}\n\t\tif (wy > max[1]) {\n\t\t\tmax[1] = wy;\n\t\t}\n\t\tif (wz > max[2]) {\n\t\t\tmax[2] = wz;\n\t\t}\n\t}\n}\n\n/**\n * Compute the bounding-sphere radius of a set of 3D vertices around the local\n * origin — the distance to the farthest vertex. When a matrix is supplied,\n * each vertex is taken through its rotation/scale columns (the translation is\n * ignored, so the radius stays centered on the transform's own origin). Handy\n * for frustum / culling bounds on a transformed mesh.\n * @param src - source vertex positions (x,y,z triplets)\n * @param count - number of vertices to read from `src`\n * @param [matrix] - optional 4x4 matrix (column-major); only the upper 3x3 rotation/scale is applied\n * @returns the radius of the smallest origin-centered sphere enclosing the vertices\n */\nexport function boundingRadius(\n\tsrc: Float32Array,\n\tcount: number,\n\tmatrix?: ArrayLike<number>,\n): number {\n\tlet maxLenSq = 0;\n\tif (matrix === undefined) {\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst vx = src[i3];\n\t\t\tconst vy = src[i3 + 1];\n\t\t\tconst vz = src[i3 + 2];\n\t\t\tconst lenSq = vx * vx + vy * vy + vz * vz;\n\t\t\tif (lenSq > maxLenSq) {\n\t\t\t\tmaxLenSq = lenSq;\n\t\t\t}\n\t\t}\n\t\treturn Math.sqrt(maxLenSq);\n\t}\n\n\tconst m0 = matrix[0];\n\tconst m1 = matrix[1];\n\tconst m2 = matrix[2];\n\tconst m4 = matrix[4];\n\tconst m5 = matrix[5];\n\tconst m6 = matrix[6];\n\tconst m8 = matrix[8];\n\tconst m9 = matrix[9];\n\tconst m10 = matrix[10];\n\n\tfor (let i = 0; i < count; i++) {\n\t\tconst i3 = i * 3;\n\t\tconst vx = src[i3];\n\t\tconst vy = src[i3 + 1];\n\t\tconst vz = src[i3 + 2];\n\t\tconst ox = m0 * vx + m4 * vy + m8 * vz;\n\t\tconst oy = m1 * vx + m5 * vy + m9 * vz;\n\t\tconst oz = m2 * vx + m6 * vy + m10 * vz;\n\t\tconst lenSq = ox * ox + oy * oy + oz * oz;\n\t\tif (lenSq > maxLenSq) {\n\t\t\tmaxLenSq = lenSq;\n\t\t}\n\t}\n\treturn Math.sqrt(maxLenSq);\n}\n\nconst _defaultNormal: XYPoint = { x: 0, y: 0 };\n\n/**\n * Compute the averaged perpendicular normal at a vertex in a 2D polyline.\n * At interior vertices, normals from both adjacent edges are averaged\n * to produce a smooth miter direction.\n * @param points - the polyline vertices\n * @param index - the vertex index\n * @param out - output object to write into (avoids allocation)\n * @returns unit normal\n */\nexport function computeVertexNormal(\n\tpoints: XYPoint[],\n\tindex: number,\n\tout: XYPoint = _defaultNormal,\n): XYPoint {\n\tlet nx = 0;\n\tlet ny = 0;\n\tconst last = points.length - 1;\n\tconst p = points[index];\n\n\tif (index < last) {\n\t\tconst dx = points[index + 1].x - p.x;\n\t\tconst dy = points[index + 1].y - p.y;\n\t\tconst len = Math.sqrt(dx * dx + dy * dy);\n\t\tif (len > 0) {\n\t\t\tconst invLen = 1 / len;\n\t\t\tnx -= dy * invLen;\n\t\t\tny += dx * invLen;\n\t\t}\n\t}\n\tif (index > 0) {\n\t\tconst dx = p.x - points[index - 1].x;\n\t\tconst dy = p.y - points[index - 1].y;\n\t\tconst len = Math.sqrt(dx * dx + dy * dy);\n\t\tif (len > 0) {\n\t\t\tconst invLen = 1 / len;\n\t\t\tnx -= dy * invLen;\n\t\t\tny += dx * invLen;\n\t\t}\n\t}\n\n\tconst nlen = Math.sqrt(nx * nx + ny * ny);\n\tif (nlen > 0) {\n\t\tconst invNlen = 1 / nlen;\n\t\tnx *= invNlen;\n\t\tny *= invNlen;\n\t} else {\n\t\tnx = 0;\n\t\tny = 1;\n\t}\n\n\tout.x = nx;\n\tout.y = ny;\n\treturn out;\n}\n\n/**\n * 2D cross product of vectors (p1-p0) and (p2-p0).\n * Positive = CCW, negative = CW, zero = collinear.\n * @ignore\n */\nfunction cross2d(p0: XYPoint, p1: XYPoint, p2: XYPoint): number {\n\treturn (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x);\n}\n\n/**\n * Squared distance between two 2D points.\n * @ignore\n */\nfunction dist2(a: XYPoint, b: XYPoint): number {\n\tconst dx = a.x - b.x;\n\tconst dy = a.y - b.y;\n\treturn dx * dx + dy * dy;\n}\n\n/**\n * Compute the convex hull of a set of 2D points using the Graham scan algorithm.\n * The input array is sorted in place. Returns a subset of the input points\n * forming the convex hull in counter-clockwise order.\n * @param points - array of 2D points (modified in place by sorting)\n * @returns convex hull vertices in CCW order\n */\nexport function convexHull(points: Vector2d[]): Vector2d[] {\n\tconst len = points.length;\n\tif (len <= 3) {\n\t\treturn points;\n\t}\n\n\t// find the bottom-most (then left-most) point as pivot\n\tlet pivotIdx = 0;\n\tlet pivotY = points[0].y;\n\tlet pivotX = points[0].x;\n\n\tfor (let i = 1; i < len; i++) {\n\t\tconst py = points[i].y;\n\t\tconst px = points[i].x;\n\t\tif (py > pivotY || (py === pivotY && px < pivotX)) {\n\t\t\tpivotIdx = i;\n\t\t\tpivotY = py;\n\t\t\tpivotX = px;\n\t\t}\n\t}\n\n\t// swap pivot to index 0\n\t[points[0], points[pivotIdx]] = [points[pivotIdx], points[0]];\n\tconst p0 = points[0];\n\n\t// sort remaining points by polar angle relative to pivot\n\tpoints.sort((a, b) => {\n\t\tif (a === p0) {\n\t\t\treturn -1;\n\t\t}\n\t\tif (b === p0) {\n\t\t\treturn 1;\n\t\t}\n\t\tconst c = cross2d(p0, a, b);\n\t\treturn c !== 0 ? -c : dist2(p0, a) - dist2(p0, b);\n\t});\n\n\t// build hull using a stack\n\tconst hull: Vector2d[] = [points[0], points[1]];\n\tfor (let i = 2; i < len; i++) {\n\t\tconst pt = points[i];\n\t\twhile (\n\t\t\thull.length > 1 &&\n\t\t\tcross2d(hull[hull.length - 2], hull[hull.length - 1], pt) <= 0\n\t\t) {\n\t\t\thull.pop();\n\t\t}\n\t\thull.push(pt);\n\t}\n\n\treturn hull;\n}\n","import { Vector3d } from \"../../math/vector3d.ts\";\nimport { transformedBounds } from \"../../math/vertex.ts\";\nimport type { XYZPoint } from \"../../utils/types.ts\";\n\n// column-major 4×4 identity, used when `fromVertices` is called without a matrix\nconst IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];\n\n// Module scratch for `fromVertices`. `transformedBounds` (the shared vertex.ts\n// helper) writes into `[x, y, z]` number arrays, but AABB3d stores `{x, y, z}`\n// objects — these bridge the two without per-call allocation. Reused across\n// calls; never escapes the method.\nconst _fvMin = [Infinity, Infinity, Infinity];\nconst _fvMax = [-Infinity, -Infinity, -Infinity];\n\n/**\n * A 3D axis-aligned bounding box — the 3D sibling of {@link Bounds}.\n *\n * Used by {@link Octree} as the bounding primitive of every node\n * (root, subnodes, queries). Intentionally minimal: only the\n * operations the {@link Octree} hot path needs are implemented, with\n * a small handful of helpers ({@link AABB3d#contains},\n * {@link AABB3d#overlaps}, {@link AABB3d#overlapsSphere}) for\n * test-driven `querySphere` / `queryAABB` callers.\n *\n * Coordinate convention matches the rest of melonJS 3D code (see\n * {@link Camera3d}): **Y-down, +Z forward / away from the camera.**\n *\n * Lives next to {@link Octree} under `physics/broadphase/` rather\n * than `math/` to mirror {@link Bounds} (which lives in `physics/`,\n * not `math/`). It originated as a broadphase-internal primitive; it is\n * now also the public 3D-bounds type returned by {@link Mesh#getBounds3d}\n * (the 3D analog of {@link Renderable#getBounds} → {@link Bounds}), built\n * from mesh geometry via {@link AABB3d#fromVertices}.\n * @category Geometry\n */\nexport class AABB3d {\n\tmin: XYZPoint;\n\tmax: XYZPoint;\n\n\t/**\n\t * Construct an empty AABB (min = +∞, max = −∞). The empty AABB\n\t * satisfies `addPoint(p)` => `min = max = p` for the first point\n\t * added, and `overlaps(b)` => `false` against any other AABB —\n\t * same convention as {@link Bounds}.\n\t * @param [min] - optional initial minimum corner\n\t * @param [max] - optional initial maximum corner\n\t */\n\tconstructor(min?: XYZPoint, max?: XYZPoint) {\n\t\tthis.min = { x: Infinity, y: Infinity, z: Infinity };\n\t\tthis.max = { x: -Infinity, y: -Infinity, z: -Infinity };\n\t\tif (min && max) {\n\t\t\tthis.setMinMax(min.x, min.y, min.z, max.x, max.y, max.z);\n\t\t}\n\t}\n\n\t/**\n\t * Reset the AABB to its empty state.\n\t */\n\tclear() {\n\t\tthis.min.x = Infinity;\n\t\tthis.min.y = Infinity;\n\t\tthis.min.z = Infinity;\n\t\tthis.max.x = -Infinity;\n\t\tthis.max.y = -Infinity;\n\t\tthis.max.z = -Infinity;\n\t}\n\n\t/**\n\t * Set min and max corners directly. Mirror of `Bounds.setMinMax`.\n\t * @param minX - minimum corner x\n\t * @param minY - minimum corner y\n\t * @param minZ - minimum corner z\n\t * @param maxX - maximum corner x\n\t * @param maxY - maximum corner y\n\t * @param maxZ - maximum corner z\n\t */\n\tsetMinMax(\n\t\tminX: number,\n\t\tminY: number,\n\t\tminZ: number,\n\t\tmaxX: number,\n\t\tmaxY: number,\n\t\tmaxZ: number,\n\t) {\n\t\tthis.min.x = minX;\n\t\tthis.min.y = minY;\n\t\tthis.min.z = minZ;\n\t\tthis.max.x = maxX;\n\t\tthis.max.y = maxY;\n\t\tthis.max.z = maxZ;\n\t}\n\n\tget x(): number {\n\t\treturn this.min.x;\n\t}\n\tget y(): number {\n\t\treturn this.min.y;\n\t}\n\tget z(): number {\n\t\treturn this.min.z;\n\t}\n\n\tget width(): number {\n\t\treturn this.max.x - this.min.x;\n\t}\n\tget height(): number {\n\t\treturn this.max.y - this.min.y;\n\t}\n\tget depth(): number {\n\t\treturn this.max.z - this.min.z;\n\t}\n\n\tget left(): number {\n\t\treturn this.min.x;\n\t}\n\tget right(): number {\n\t\treturn this.max.x;\n\t}\n\tget top(): number {\n\t\treturn this.min.y;\n\t}\n\tget bottom(): number {\n\t\treturn this.max.y;\n\t}\n\tget front(): number {\n\t\treturn this.min.z;\n\t}\n\tget back(): number {\n\t\treturn this.max.z;\n\t}\n\n\tget centerX(): number {\n\t\treturn (this.min.x + this.max.x) * 0.5;\n\t}\n\tget centerY(): number {\n\t\treturn (this.min.y + this.max.y) * 0.5;\n\t}\n\tget centerZ(): number {\n\t\treturn (this.min.z + this.max.z) * 0.5;\n\t}\n\n\t/**\n\t * True if every coordinate is a finite number. Used by the\n\t * {@link Octree} to reject inserts whose bounding box is the\n\t * empty AABB (a sprite with no shape and never updated) — without\n\t * this guard a `±Infinity` corner would silently classify into\n\t * an arbitrary octant and never come back out via `remove`.\n\t */\n\tisFinite() {\n\t\treturn (\n\t\t\tisFinite(this.min.x) &&\n\t\t\tisFinite(this.min.y) &&\n\t\t\tisFinite(this.min.z) &&\n\t\t\tisFinite(this.max.x) &&\n\t\t\tisFinite(this.max.y) &&\n\t\t\tisFinite(this.max.z)\n\t\t);\n\t}\n\n\t/**\n\t * True if this AABB fully encloses the given point.\n\t * @param x - point x\n\t * @param y - point y\n\t * @param z - point z\n\t */\n\tcontains(x: number, y: number, z: number) {\n\t\treturn (\n\t\t\tx >= this.min.x &&\n\t\t\tx <= this.max.x &&\n\t\t\ty >= this.min.y &&\n\t\t\ty <= this.max.y &&\n\t\t\tz >= this.min.z &&\n\t\t\tz <= this.max.z\n\t\t);\n\t}\n\n\t/**\n\t * True if this AABB overlaps the given AABB on every axis. The\n\t * intersection includes the boundary (shared face/edge/corner\n\t * counts as overlap), same convention as {@link Bounds.overlaps}\n\t * so {@link Octree} candidate sets don't drop items that sit\n\t * exactly on an octant boundary.\n\t * @param aabb - the other AABB to test against\n\t */\n\toverlaps(aabb: AABB3d) {\n\t\treturn !(\n\t\t\tthis.right < aabb.left ||\n\t\t\tthis.left > aabb.right ||\n\t\t\tthis.bottom < aabb.top ||\n\t\t\tthis.top > aabb.bottom ||\n\t\t\tthis.back < aabb.front ||\n\t\t\tthis.front > aabb.back\n\t\t);\n\t}\n\n\t/**\n\t * True if this AABB overlaps a sphere. Computes the squared\n\t * distance from the sphere center to the nearest point on the\n\t * AABB and compares against `r²` — avoids the `sqrt` on the hot\n\t * path. Used by `Octree.querySphere`.\n\t * @param cx - sphere center x\n\t * @param cy - sphere center y\n\t * @param cz - sphere center z\n\t * @param r - sphere radius (must be ≥ 0; `r=0` becomes a point-in-AABB test)\n\t */\n\toverlapsSphere(cx: number, cy: number, cz: number, r: number) {\n\t\t// per-axis closest-point: clamp center into the AABB extents\n\t\tconst dx =\n\t\t\tcx < this.min.x ? this.min.x - cx : cx > this.max.x ? cx - this.max.x : 0;\n\t\tconst dy =\n\t\t\tcy < this.min.y ? this.min.y - cy : cy > this.max.y ? cy - this.max.y : 0;\n\t\tconst dz =\n\t\t\tcz < this.min.z ? this.min.z - cz : cz > this.max.z ? cz - this.max.z : 0;\n\t\treturn dx * dx + dy * dy + dz * dz <= r * r;\n\t}\n\n\t/**\n\t * Expand this AABB to include the given point.\n\t * @param p - the point to fold in\n\t */\n\taddPoint(p: XYZPoint | Vector3d) {\n\t\tif (p.x < this.min.x) this.min.x = p.x;\n\t\tif (p.y < this.min.y) this.min.y = p.y;\n\t\tif (p.z < this.min.z) this.min.z = p.z;\n\t\tif (p.x > this.max.x) this.max.x = p.x;\n\t\tif (p.y > this.max.y) this.max.y = p.y;\n\t\tif (p.z > this.max.z) this.max.z = p.z;\n\t}\n\n\t/**\n\t * Expand this AABB to include the given AABB. With `clear=true`,\n\t * copies the input AABB directly — same shape as {@link Bounds.addBounds}.\n\t * @param aabb - the AABB to fold in\n\t * @param [clear] - reset this AABB before folding\n\t */\n\taddAABB(aabb: AABB3d, clear = false) {\n\t\tif (clear) {\n\t\t\tthis.min.x = aabb.min.x;\n\t\t\tthis.min.y = aabb.min.y;\n\t\t\tthis.min.z = aabb.min.z;\n\t\t\tthis.max.x = aabb.max.x;\n\t\t\tthis.max.y = aabb.max.y;\n\t\t\tthis.max.z = aabb.max.z;\n\t\t\treturn;\n\t\t}\n\t\tif (aabb.min.x < this.min.x) this.min.x = aabb.min.x;\n\t\tif (aabb.min.y < this.min.y) this.min.y = aabb.min.y;\n\t\tif (aabb.min.z < this.min.z) this.min.z = aabb.min.z;\n\t\tif (aabb.max.x > this.max.x) this.max.x = aabb.max.x;\n\t\tif (aabb.max.y > this.max.y) this.max.y = aabb.max.y;\n\t\tif (aabb.max.z > this.max.z) this.max.z = aabb.max.z;\n\t}\n\n\t/**\n\t * Build this AABB from a flat vertex buffer (`x,y,z` triplets), optionally\n\t * transformed by a column-major 4×4 matrix — the bridge from raw mesh /\n\t * glTF geometry (e.g. a {@link Mesh}'s vertices or a parsed glTF node) to an\n\t * `AABB3d`. **Replaces** the current bounds (seeds empty, then folds in the\n\t * `count` vertices); for the point-array form use {@link AABB3d#addPoint}.\n\t *\n\t * The per-vertex math is **delegated to {@link transformedBounds}** (the\n\t * shared `math/vertex.ts` helper) rather than duplicated here, so the\n\t * flat-buffer ↔ AABB conversion stays in one place. Allocation-free.\n\t * @param src - source vertex positions (`x,y,z` triplets)\n\t * @param count - number of vertices to read from `src`\n\t * @param [matrix] - optional column-major 4×4 (16 elements); identity if omitted\n\t * @returns this AABB, for chaining\n\t * @example\n\t * // bounds of a glTF node's geometry under its world transform\n\t * const box = new AABB3d().fromVertices(node.vertices, node.vertexCount, node.world);\n\t */\n\tfromVertices(src: Float32Array, count: number, matrix?: ArrayLike<number>) {\n\t\t// seed empty so the result is built purely from these vertices\n\t\t_fvMin[0] = _fvMin[1] = _fvMin[2] = Infinity;\n\t\t_fvMax[0] = _fvMax[1] = _fvMax[2] = -Infinity;\n\t\ttransformedBounds(src, count, matrix ?? IDENTITY, _fvMin, _fvMax);\n\t\tthis.setMinMax(\n\t\t\t_fvMin[0],\n\t\t\t_fvMin[1],\n\t\t\t_fvMin[2],\n\t\t\t_fvMax[0],\n\t\t\t_fvMax[1],\n\t\t\t_fvMax[2],\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Returns a deep copy of this AABB.\n\t */\n\tclone() {\n\t\treturn new AABB3d(this.min, this.max);\n\t}\n}\n","import { Vector2d } from \"../math/vector2d.ts\";\nimport { Vector3d } from \"../math/vector3d.ts\";\nimport { Bounds } from \"../physics/bounds.ts\";\nimport { AABB3d } from \"../physics/broadphase/aabb3d.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { Polygon } from \"./polygon.ts\";\n\n/**\n * Smallest XY footprint edge handed to {@link Polygon#recalc}. See\n * {@link Box3d#_syncFootprint} for why a zero-length edge is unsafe.\n * @ignore\n */\nconst MIN_FOOTPRINT = 1e-6;\n\n/**\n * An axis-aligned 3D box, usable as a {@link Body} collision shape.\n *\n * This is the shape that lets a body collide along **Z** as well as X and Y.\n * Every other built-in shape ({@link Polygon}, {@link Rect}, {@link RoundRect},\n * {@link Ellipse}) is planar and is resolved by the 2D SAT narrowphase, which\n * can only ever produce a 2D pushback — see {@link ResponseObject#overlapV}.\n * A `Box3d` pair is instead resolved by an AABB-vs-AABB narrowphase that also\n * fills {@link ResponseObject#overlapZ}.\n *\n * Coordinate convention matches the rest of melonJS 3D code (see\n * {@link Camera3d}): **Y-down, +Z forward / away from the camera.**\n *\n * ## Position is the CENTER\n *\n * Unlike {@link Rect} (top-left) and like {@link Ellipse} (center), `pos` is\n * the box **center**. That matches how 3D objects are placed everywhere else\n * in the engine — {@link Mesh}, {@link Sprite3d} and the ground-shadow\n * footprint all work from a center plus half-extents — and it keeps the\n * narrowphase free of corner/center conversions on the hot path.\n *\n * ## Mixing with 2D shapes\n *\n * A `Box3d` can collide with a planar shape. The planar shape is treated as\n * **unbounded along Z** (an infinitely extruded prism of its own outline), so\n * the pair degrades to the ordinary 2D test on the XY footprint and the box's\n * z never causes it to miss. This keeps an existing 2D game working unchanged\n * when a single `Box3d` body is introduced: its world shapes go on colliding\n * exactly as before. Use `collisionType` / `collisionMask` to opt specific\n * shapes out of a 3D body.\n * @category Geometry\n * @example\n * // a 64x16x64 floor slab centered on the origin\n * const floor = new Box3d(0, 0, 0, 64, 16, 64);\n * myFloor.body.addShape(floor);\n */\nexport class Box3d {\n\t/**\n\t * the center of the box, as an offset from the owning body's position\n\t */\n\tpos: Vector3d;\n\n\t/**\n\t * half the box size on each axis. Always non-negative; a negative\n\t * extent passed to {@link Box3d#setShape} is stored as its magnitude,\n\t * since a box with a mirrored axis has no meaning to the narrowphase.\n\t */\n\thalfExtents: Vector3d;\n\n\t/**\n\t * the shape type (used internally)\n\t * @default \"Box3d\"\n\t */\n\ttype = \"Box3d\";\n\n\t/**\n\t * 2D XY footprint, kept in sync by {@link Box3d#setShape}. This is what\n\t * {@link Box3d#getBounds} hands back, so every legacy 2D consumer\n\t * ({@link Body#bounds}, the broadphase pre-gate, debug draw) sees a\n\t * plain rectangle and needs no 3D awareness.\n\t * @ignore\n\t */\n\t_bounds: Bounds;\n\n\t/**\n\t * XY footprint as a {@link Polygon}, kept in sync by\n\t * {@link Box3d#setShape}. The degraded `Box3d` × planar-shape tests\n\t * hand this to the existing polygon SAT, so no polygon is allocated\n\t * per collision test.\n\t * @ignore\n\t */\n\t_footprint: Polygon;\n\n\t/**\n\t * @param x - center of the box on the horizontal axis\n\t * @param y - center of the box on the vertical axis\n\t * @param z - center of the box on the depth axis\n\t * @param width - width of the box\n\t * @param height - height of the box\n\t * @param depth - depth of the box\n\t */\n\tconstructor(x = 0, y = 0, z = 0, width = 0, height = 0, depth = 0) {\n\t\tthis.pos = new Vector3d();\n\t\tthis.halfExtents = new Vector3d();\n\t\tthis._bounds = new Bounds();\n\t\t// seeded with a unit quad; setShape rewrites the points immediately\n\t\tthis._footprint = new Polygon(0, 0, [\n\t\t\tnew Vector2d(0, 0),\n\t\t\tnew Vector2d(1, 0),\n\t\t\tnew Vector2d(1, 1),\n\t\t\tnew Vector2d(0, 1),\n\t\t]);\n\t\tthis.setShape(x, y, z, width, height, depth);\n\t}\n\n\t/**\n\t * set new position and size for this box\n\t * @param x - center of the box on the horizontal axis\n\t * @param y - center of the box on the vertical axis\n\t * @param z - center of the box on the depth axis\n\t * @param width - width of the box\n\t * @param height - height of the box\n\t * @param depth - depth of the box\n\t * @returns this box, for chaining\n\t */\n\tsetShape(\n\t\tx: number,\n\t\ty: number,\n\t\tz: number,\n\t\twidth: number,\n\t\theight: number,\n\t\tdepth: number,\n\t) {\n\t\tthis.pos.set(x, y, z);\n\t\tthis.halfExtents.set(\n\t\t\tMath.abs(width) * 0.5,\n\t\t\tMath.abs(height) * 0.5,\n\t\t\tMath.abs(depth) * 0.5,\n\t\t);\n\t\tthis._syncFootprint();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Rebuild the cached XY footprint (both the {@link Bounds} and the\n\t * {@link Polygon}) from the current center and half-extents.\n\t *\n\t * The footprint polygon's `pos` is the **min corner**, not the center,\n\t * because the SAT narrowphase resolves a shape's absolute position as\n\t * `renderable.pos + ancestor.getAbsolutePosition() + shape.pos` and\n\t * then treats `points` as offsets from it.\n\t * @ignore\n\t */\n\t_syncFootprint() {\n\t\tconst hx = this.halfExtents.x;\n\t\tconst hy = this.halfExtents.y;\n\t\t// Footprint edges are floored at MIN_FOOTPRINT. `Polygon.recalc`\n\t\t// normalizes each edge by its own length with no zero guard, so two\n\t\t// coincident points yield `0 / 0 = NaN` normals and poison every\n\t\t// later SAT axis test. A zero-width or zero-height box (including a\n\t\t// default-constructed one) would do exactly that. Z is unaffected:\n\t\t// a zero-DEPTH box is perfectly well-defined and stays exact.\n\t\tconst w = Math.max(hx * 2, MIN_FOOTPRINT);\n\t\tconst h = Math.max(hy * 2, MIN_FOOTPRINT);\n\t\tconst minX = this.pos.x - hx;\n\t\tconst minY = this.pos.y - hy;\n\n\t\tconst points = this._footprint.points;\n\t\tpoints[0].set(0, 0);\n\t\tpoints[1].set(w, 0);\n\t\tpoints[2].set(w, h);\n\t\tpoints[3].set(0, h);\n\t\tthis._footprint.pos.set(minX, minY);\n\t\t// rebuild edges / normals for the SAT axes\n\t\tthis._footprint.recalc();\n\n\t\tthis._bounds.setMinMax(minX, minY, minX + w, minY + h);\n\t}\n\n\t/**\n\t * width of the box\n\t */\n\tget width(): number {\n\t\treturn this.halfExtents.x * 2;\n\t}\n\tset width(value: number) {\n\t\tthis.halfExtents.x = Math.abs(value) * 0.5;\n\t\tthis._syncFootprint();\n\t}\n\n\t/**\n\t * height of the box\n\t */\n\tget height(): number {\n\t\treturn this.halfExtents.y * 2;\n\t}\n\tset height(value: number) {\n\t\tthis.halfExtents.y = Math.abs(value) * 0.5;\n\t\tthis._syncFootprint();\n\t}\n\n\t/**\n\t * depth of the box\n\t */\n\tget depth(): number {\n\t\treturn this.halfExtents.z * 2;\n\t}\n\tset depth(value: number) {\n\t\tthis.halfExtents.z = Math.abs(value) * 0.5;\n\t\tthis._syncFootprint();\n\t}\n\n\t/**\n\t * translate this box by the given offset\n\t * @param x - x offset, or a vector carrying the whole offset\n\t * @param [y] - y offset\n\t * @param [z] - z offset\n\t * @returns this box, for chaining\n\t */\n\tshift(x: number | Vector3d, y = 0, z = 0) {\n\t\tif (typeof x === \"object\") {\n\t\t\tthis.pos.add(x);\n\t\t} else {\n\t\t\tthis.pos.set(this.pos.x + x, this.pos.y + y, this.pos.z + z);\n\t\t}\n\t\tthis._syncFootprint();\n\t\treturn this;\n\t}\n\n\t/**\n\t * the 2D XY footprint of this box.\n\t *\n\t * Deliberately 2D: this is the {@link Renderable#getBounds} contract that\n\t * {@link Body} and the broadphase pre-gate already speak. For the depth\n\t * extent use {@link Box3d#getBounds3d}.\n\t * @returns the XY footprint\n\t */\n\tgetBounds(): Bounds {\n\t\treturn this._bounds;\n\t}\n\n\t/**\n\t * the 3D bounds of this box, in the body's local space.\n\t * @param [out] - an existing AABB3d to write into, to avoid allocating\n\t * @returns the 3D bounds\n\t */\n\tgetBounds3d(out?: AABB3d): AABB3d {\n\t\tconst target = out ?? new AABB3d();\n\t\tconst hx = this.halfExtents.x;\n\t\tconst hy = this.halfExtents.y;\n\t\tconst hz = this.halfExtents.z;\n\t\ttarget.setMinMax(\n\t\t\tthis.pos.x - hx,\n\t\t\tthis.pos.y - hy,\n\t\t\tthis.pos.z - hz,\n\t\t\tthis.pos.x + hx,\n\t\t\tthis.pos.y + hy,\n\t\t\tthis.pos.z + hz,\n\t\t);\n\t\treturn target;\n\t}\n\n\t/**\n\t * true if this box contains the given point\n\t * @param x - point x, or a vector carrying the whole point\n\t * @param [y] - point y\n\t * @param [z] - point z\n\t */\n\tcontains(x: number | Vector3d, y = 0, z = 0): boolean {\n\t\tconst isVector = typeof x === \"object\";\n\t\tconst px = isVector ? x.x : x;\n\t\tconst py = isVector ? x.y : y;\n\t\tconst pz = isVector ? x.z : z;\n\t\treturn (\n\t\t\tMath.abs(px - this.pos.x) <= this.halfExtents.x &&\n\t\t\tMath.abs(py - this.pos.y) <= this.halfExtents.y &&\n\t\t\tMath.abs(pz - this.pos.z) <= this.halfExtents.z\n\t\t);\n\t}\n\n\t/**\n\t * clone this box\n\t * @returns a new Box3d\n\t */\n\tclone(): Box3d {\n\t\treturn new Box3d(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.pos.z,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t\tthis.depth,\n\t\t);\n\t}\n}\n\nexport const box3dPool = createPool<\n\tBox3d,\n\t[\n\t\tx?: number,\n\t\ty?: number,\n\t\tz?: number,\n\t\twidth?: number,\n\t\theight?: number,\n\t\tdepth?: number,\n\t]\n>((x, y, z, width, height, depth) => {\n\tconst instance = new Box3d(x, y, z, width, height, depth);\n\n\treturn {\n\t\tinstance,\n\t\treset(x = 0, y = 0, z = 0, width = 0, height = 0, depth = 0) {\n\t\t\tinstance.setShape(x, y, z, width, height, depth);\n\t\t},\n\t};\n});\n","import { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { LineVertices, Polygon } from \"./polygon.ts\";\n\n/**\n * a line segment Object\n * @category Geometry\n * @param x - origin point of the Line\n * @param y - origin point of the Line\n * @param points - array of vectors defining the Line\n */\nexport class Line extends Polygon {\n\t/**\n\t * Returns true if the Line contains the given point\n\t * @param x -  x coordinate or a vector point to check\n\t * @param [y] -  y coordinate\n\t * @returns true if contains\n\t * @example\n\t * if (line.contains(10, 10)) {\n\t *   // do something\n\t * }\n\t * // or\n\t * if (line.contains(myVector2d)) {\n\t *   // do something\n\t * }\n\t */\n\toverride contains(x: number, y: number): boolean;\n\toverride contains(vector: Vector2d): boolean;\n\toverride contains(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\t// translate the given coordinates,\n\t\t// rather than creating temp translated vectors\n\t\t_x -= this.pos.x; // Cx\n\t\t_y -= this.pos.y; // Cy\n\t\tconst [start, end] = this.points;\n\n\t\t//(Cy - Ay) * (Bx - Ax) = (By - Ay) * (Cx - Ax)\n\t\treturn (\n\t\t\t(_y - start.y) * (end.x - start.x) === (end.y - start.y) * (_x - start.x)\n\t\t);\n\t}\n\n\t/**\n\t * Computes the calculated collision edges and normals.\n\t * This **must** be called if the `points` array, `angle`, or `offset` is modified manually.\n\t * @returns this instance for object chaining\n\t */\n\toverride recalc() {\n\t\tconst edges = this.edges;\n\t\tconst normals = this.normals;\n\t\tconst indices = this.indices;\n\n\t\t// Copy the original points array and apply the offset/angle\n\t\tconst points = this.points;\n\n\t\tif (points.length !== 2) {\n\t\t\tthrow new Error(\"Requires exactly 2 points\");\n\t\t}\n\n\t\t// Calculate the edges/normals\n\t\tif (edges[0] === undefined) {\n\t\t\tedges[0] = vector2dPool.get();\n\t\t}\n\t\tedges[0].copy(points[1]).sub(points[0]);\n\t\tif (normals[0] === undefined) {\n\t\t\tnormals[0] = vector2dPool.get();\n\t\t}\n\t\tnormals[0].copy(edges[0]).perp().normalize();\n\n\t\t// do not do anything here, indices will be computed by\n\t\t// toIndices if array is empty upon function call\n\t\tindices.length = 0;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * clone this line segment\n\t * @returns new Line\n\t */\n\toverride clone() {\n\t\treturn new Line(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.points.map((point) => point.clone()) as LineVertices,\n\t\t);\n\t}\n}\n\nexport const linePool = createPool<\n\tLine,\n\t[x: number, y: number, points: LineVertices]\n>((x, y, points) => {\n\tconst line = new Line(x, y, points);\n\treturn {\n\t\tinstance: line,\n\t\treset(x, y, points) {\n\t\t\tline.pos.set(x, y);\n\t\t\tline.setVertices(points);\n\t\t},\n\t};\n});\n","import { Vector2d } from \"../math/vector2d.ts\";\nimport { createPool } from \"../system/pool.ts\";\nimport { Polygon } from \"./polygon.ts\";\n\n// https://developer.chrome.com/blog/canvas2d/#round-rect\n\nconst DEFAULT_RADIUS = 20;\n\n// number of segments per rounded corner arc\nconst CORNER_SEGMENTS = 8;\n\n// total vertex count for a rounded rectangle (4 corners × (segments + 1))\nconst ROUNDED_VERTEX_COUNT = 4 * (CORNER_SEGMENTS + 1);\n\n// precomputed cos/sin for each corner arc step (shared across all instances)\nconst CORNER_COS: number[] = new Array(CORNER_SEGMENTS + 1);\nconst CORNER_SIN: number[] = new Array(CORNER_SEGMENTS + 1);\nfor (let i = 0; i <= CORNER_SEGMENTS; i++) {\n\tconst angle = (i / CORNER_SEGMENTS) * (Math.PI / 2);\n\tCORNER_COS[i] = Math.cos(angle);\n\tCORNER_SIN[i] = Math.sin(angle);\n}\n\n/**\n * Update existing vertex positions to approximate a rounded rectangle,\n * or create new vertices if the array length doesn't match.\n * Vertices are in clockwise winding order.\n * @param points - existing points array (may be reused)\n * @param w - width\n * @param h - height\n * @param r - corner radius (already clamped)\n * @returns the points array (same reference if reused, or new)\n */\nfunction updateRoundRectVertices(\n\tpoints: Vector2d[],\n\tw: number,\n\th: number,\n\tr: number,\n): Vector2d[] {\n\tif (r <= 0) {\n\t\t// plain rectangle — 4 vertices\n\t\tif (points.length === 4) {\n\t\t\tpoints[0].set(0, 0);\n\t\t\tpoints[1].set(w, 0);\n\t\t\tpoints[2].set(w, h);\n\t\t\tpoints[3].set(0, h);\n\t\t\treturn points;\n\t\t}\n\t\treturn [\n\t\t\tnew Vector2d(0, 0),\n\t\t\tnew Vector2d(w, 0),\n\t\t\tnew Vector2d(w, h),\n\t\t\tnew Vector2d(0, h),\n\t\t];\n\t}\n\n\t// reuse existing array if vertex count matches\n\tconst canReuse = points.length === ROUNDED_VERTEX_COUNT;\n\tlet idx = 0;\n\n\tfunction setOrPush(x: number, y: number) {\n\t\tif (canReuse) {\n\t\t\tpoints[idx].set(x, y);\n\t\t} else {\n\t\t\tpoints[idx] = new Vector2d(x, y);\n\t\t}\n\t\tidx++;\n\t}\n\n\tif (!canReuse) {\n\t\tpoints.length = ROUNDED_VERTEX_COUNT;\n\t}\n\n\t// top-right corner: angle -π/2 to 0\n\t// cos(-π/2 + t) = sin(t), sin(-π/2 + t) = -cos(t) where t = i/N * π/2\n\tfor (let i = 0; i <= CORNER_SEGMENTS; i++) {\n\t\tsetOrPush(w - r + r * CORNER_SIN[i], r - r * CORNER_COS[i]);\n\t}\n\n\t// bottom-right corner: angle 0 to π/2\n\t// cos(t) = cos(t), sin(t) = sin(t)\n\tfor (let i = 0; i <= CORNER_SEGMENTS; i++) {\n\t\tsetOrPush(w - r + r * CORNER_COS[i], h - r + r * CORNER_SIN[i]);\n\t}\n\n\t// bottom-left corner: angle π/2 to π\n\t// cos(π/2 + t) = -sin(t), sin(π/2 + t) = cos(t)\n\tfor (let i = 0; i <= CORNER_SEGMENTS; i++) {\n\t\tsetOrPush(r - r * CORNER_SIN[i], h - r + r * CORNER_COS[i]);\n\t}\n\n\t// top-left corner: angle π to 3π/2\n\t// cos(π + t) = -cos(t), sin(π + t) = -sin(t)\n\tfor (let i = 0; i <= CORNER_SEGMENTS; i++) {\n\t\tsetOrPush(r - r * CORNER_COS[i], r - r * CORNER_SIN[i]);\n\t}\n\n\treturn points;\n}\n\n/**\n * a rectangle object with rounded corners\n * @category Geometry\n */\nexport class RoundRect extends Polygon {\n\t/**\n\t * Corner radius.\n\t */\n\t_radius: number;\n\n\t/**\n\t * stored width\n\t */\n\t_width: number;\n\n\t/**\n\t * stored height\n\t */\n\t_height: number;\n\n\t/**\n\t * the shape type (used internally)\n\t */\n\toverride type = \"RoundRect\";\n\n\t/**\n\t * @param x - position of the rounded rectangle\n\t * @param y - position of the rounded rectangle\n\t * @param width - the rectangle width\n\t * @param height - the rectangle height\n\t * @param [radius=20] - the radius of the rounded corner\n\t */\n\tconstructor(\n\t\tx: number,\n\t\ty: number,\n\t\twidth: number,\n\t\theight: number,\n\t\tradius = DEFAULT_RADIUS,\n\t) {\n\t\t// initialize with rectangle vertices; radius setter will rebuild if needed\n\t\tsuper(x, y, [\n\t\t\tnew Vector2d(0, 0),\n\t\t\tnew Vector2d(width, 0),\n\t\t\tnew Vector2d(width, height),\n\t\t\tnew Vector2d(0, height),\n\t\t]);\n\t\tthis.type = \"RoundRect\";\n\t\tthis._width = width;\n\t\tthis._height = height;\n\t\tthis.radius = radius;\n\t}\n\n\t/**\n\t * width of the RoundRect\n\t */\n\tget width() {\n\t\treturn this._width;\n\t}\n\tset width(value) {\n\t\tthis._width = value;\n\t\t// re-clamp radius and rebuild vertices\n\t\tthis.radius = this._radius;\n\t}\n\n\t/**\n\t * height of the RoundRect\n\t */\n\tget height() {\n\t\treturn this._height;\n\t}\n\tset height(value) {\n\t\tthis._height = value;\n\t\t// re-clamp radius and rebuild vertices\n\t\tthis.radius = this._radius;\n\t}\n\n\t/**\n\t * left coordinate of the RoundRect\n\t */\n\tget left() {\n\t\treturn this.pos.x;\n\t}\n\n\t/**\n\t * right coordinate of the RoundRect\n\t */\n\tget right() {\n\t\treturn this.pos.x + this._width;\n\t}\n\n\t/**\n\t * top coordinate of the RoundRect\n\t */\n\tget top() {\n\t\treturn this.pos.y;\n\t}\n\n\t/**\n\t * bottom coordinate of the RoundRect\n\t */\n\tget bottom() {\n\t\treturn this.pos.y + this._height;\n\t}\n\n\t/**\n\t * absolute center of this RoundRect on the horizontal axis\n\t */\n\tget centerX() {\n\t\treturn this.pos.x + this._width / 2;\n\t}\n\n\t/**\n\t * absolute center of this RoundRect on the vertical axis\n\t */\n\tget centerY() {\n\t\treturn this.pos.y + this._height / 2;\n\t}\n\n\t/**\n\t * the radius of the rounded corner\n\t * @default 20\n\t */\n\tget radius() {\n\t\treturn this._radius;\n\t}\n\tset radius(value) {\n\t\t// clamp to non-negative and to half the shorter side\n\t\tvalue = Math.max(0, value);\n\t\tif (this._width < 2 * value) {\n\t\t\tvalue = this._width / 2;\n\t\t}\n\t\tif (this._height < 2 * value) {\n\t\t\tvalue = this._height / 2;\n\t\t}\n\t\tthis._radius = value;\n\t\tthis._updateVertices();\n\t}\n\n\t/**\n\t * Rebuild polygon vertices to approximate the rounded corners.\n\t * Reuses existing Vector2d instances when the vertex count matches.\n\t * @ignore\n\t */\n\t_updateVertices() {\n\t\tconst updated = updateRoundRectVertices(\n\t\t\tthis.points,\n\t\t\tthis._width,\n\t\t\tthis._height,\n\t\t\tthis._radius,\n\t\t);\n\t\tif (updated !== this.points) {\n\t\t\t// vertex count changed — need full setVertices\n\t\t\tthis.setVertices(\n\t\t\t\tupdated as [Vector2d, Vector2d, Vector2d, ...Vector2d[]],\n\t\t\t);\n\t\t} else {\n\t\t\t// same array, just recalc edges/normals/bounds\n\t\t\tthis.recalc();\n\t\t\tthis.updateBounds();\n\t\t}\n\t}\n\n\t/**\n\t * Set new dimensions for the rounded rectangle.\n\t * @param width - The new width.\n\t * @param height - The new height.\n\t */\n\tsetSize(width: number, height: number) {\n\t\tthis._width = width;\n\t\tthis._height = height;\n\t\t// re-clamp radius and rebuild vertices\n\t\tthis.radius = this._radius;\n\t\treturn this;\n\t}\n\n\t/**\n\t * resize the rounded rectangle\n\t * @param w - new width\n\t * @param h - new height\n\t * @returns this rounded rectangle\n\t */\n\tresize(w: number, h: number) {\n\t\treturn this.setSize(w, h);\n\t}\n\n\t/**\n\t * copy the position, size and radius of the given rounded rectangle into this one\n\t * @param rrect - source rounded rectangle\n\t * @returns this rounded rectangle\n\t */\n\tcopy(rrect: RoundRect) {\n\t\tthis.pos.set(rrect.pos.x, rrect.pos.y);\n\t\tthis.setSize(rrect.width, rrect.height);\n\t\tthis.radius = rrect.radius;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Returns true if the rounded rectangle contains the given point\n\t * @param x - x coordinate or a vector point\n\t * @param [y] - y coordinate\n\t * @returns True if the rounded rectangle contains the given point\n\t */\n\toverride contains(x: number, y: number): boolean;\n\toverride contains(vector: Vector2d): boolean;\n\toverride contains(xOrVector: Vector2d | number, y?: number) {\n\t\tlet _x: number;\n\t\tlet _y: number;\n\n\t\tif (xOrVector instanceof Vector2d) {\n\t\t\t_x = xOrVector.x;\n\t\t\t_y = xOrVector.y;\n\t\t} else {\n\t\t\t_x = xOrVector;\n\t\t\t_y = y!;\n\t\t}\n\n\t\t// check whether point is outside the bounding box\n\t\tif (\n\t\t\t_x < this.left ||\n\t\t\t_x >= this.right ||\n\t\t\t_y < this.top ||\n\t\t\t_y >= this.bottom\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// check whether point is within the bounding box minus radius\n\t\tif (\n\t\t\t(_x >= this.left + this.radius && _x <= this.right - this.radius) ||\n\t\t\t(_y >= this.top + this.radius && _y <= this.bottom - this.radius)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// check whether point is in one of the rounded corner areas\n\t\tlet tx;\n\t\tlet ty;\n\t\tconst radiusX = Math.max(0, Math.min(this.radius, this._width / 2));\n\t\tconst radiusY = Math.max(0, Math.min(this.radius, this._height / 2));\n\n\t\tif (_x < this.left + radiusX && _y < this.top + radiusY) {\n\t\t\ttx = _x - this.left - radiusX;\n\t\t\tty = _y - this.top - radiusY;\n\t\t} else if (_x > this.right - radiusX && _y < this.top + radiusY) {\n\t\t\ttx = _x - this.right + radiusX;\n\t\t\tty = _y - this.top - radiusY;\n\t\t} else if (_x > this.right - radiusX && _y > this.bottom - radiusY) {\n\t\t\ttx = _x - this.right + radiusX;\n\t\t\tty = _y - this.bottom + radiusY;\n\t\t} else if (_x < this.left + radiusX && _y > this.bottom - radiusY) {\n\t\t\ttx = _x - this.left - radiusX;\n\t\t\tty = _y - this.bottom + radiusY;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Pythagorean theorem\n\t\treturn tx * tx + ty * ty <= radiusX * radiusY;\n\t}\n\n\t/**\n\t * Returns true if the rounded rectangle contains the given rectangle\n\t * @param rectangle - rectangle to test\n\t * @param rectangle.left - left coordinate\n\t * @param rectangle.right - right coordinate\n\t * @param rectangle.top - top coordinate\n\t * @param rectangle.bottom - bottom coordinate\n\t * @returns true if contained\n\t */\n\tcontainsRectangle(rectangle: {\n\t\tleft: number;\n\t\tright: number;\n\t\ttop: number;\n\t\tbottom: number;\n\t}) {\n\t\treturn (\n\t\t\trectangle.left >= this.left &&\n\t\t\trectangle.right <= this.right &&\n\t\t\trectangle.top >= this.top &&\n\t\t\trectangle.bottom <= this.bottom\n\t\t);\n\t}\n\n\t/**\n\t * check if this RoundRect is identical to the specified one\n\t * @param rrect - Other rounded rectangle.\n\t * @returns true if equals\n\t */\n\tequals(rrect: RoundRect) {\n\t\treturn (\n\t\t\tthis.left === rrect.left &&\n\t\t\tthis.right === rrect.right &&\n\t\t\tthis.top === rrect.top &&\n\t\t\tthis.bottom === rrect.bottom &&\n\t\t\tthis.radius === rrect.radius\n\t\t);\n\t}\n\n\t/**\n\t * clone this RoundRect\n\t * @returns new RoundRect\n\t */\n\toverride clone() {\n\t\treturn new RoundRect(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis._width,\n\t\t\tthis._height,\n\t\t\tthis._radius,\n\t\t);\n\t}\n}\n\nexport const roundedRectanglePool = createPool<\n\tRoundRect,\n\t[\n\t\tx: number,\n\t\ty: number,\n\t\twidth: number,\n\t\theight: number,\n\t\tradius?: number | undefined,\n\t]\n>((x, y, width, height, radius) => {\n\tconst roundedRectangle = new RoundRect(x, y, width, height, radius);\n\treturn {\n\t\tinstance: roundedRectangle,\n\t\treset(x, y, width, height, radius = DEFAULT_RADIUS) {\n\t\t\troundedRectangle.pos.set(x, y);\n\t\t\troundedRectangle.setSize(width, height);\n\t\t\troundedRectangle.radius = radius;\n\t\t},\n\t};\n});\n","const LOG2_PAGE_SIZE = 9;\nconst PAGE_SIZE = 1 << LOG2_PAGE_SIZE;\n\n/**\n * a glyph representing a single character in a font\n * @ignore\n */\nexport default class Glyph {\n\t/**\n\t * @ignore\n\t */\n\tid: number;\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tu: number;\n\tv: number;\n\tu2: number;\n\tv2: number;\n\txoffset: number;\n\tyoffset: number;\n\txadvance: number;\n\tfixedWidth: boolean;\n\tkerning?: { [key: number]: { [key: number]: number } };\n\n\t/**\n\t * @ignore\n\t */\n\tconstructor() {\n\t\tthis.id = 0;\n\t\tthis.x = 0;\n\t\tthis.y = 0;\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t\tthis.u = 0;\n\t\tthis.v = 0;\n\t\tthis.u2 = 0;\n\t\tthis.v2 = 0;\n\t\tthis.xoffset = 0;\n\t\tthis.yoffset = 0;\n\t\tthis.xadvance = 0;\n\t\tthis.fixedWidth = false;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tgetKerning(ch: number): number {\n\t\tif (this.kerning) {\n\t\t\tconst page = this.kerning[ch >>> LOG2_PAGE_SIZE];\n\t\t\tif (page) {\n\t\t\t\treturn page[ch & (PAGE_SIZE - 1)] || 0;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tsetKerning(ch: number, value: number): void {\n\t\tif (!this.kerning) {\n\t\t\tthis.kerning = {};\n\t\t}\n\t\tlet page = this.kerning[ch >>> LOG2_PAGE_SIZE];\n\t\tif (typeof page === \"undefined\") {\n\t\t\tthis.kerning[ch >>> LOG2_PAGE_SIZE] = {};\n\t\t\tpage = this.kerning[ch >>> LOG2_PAGE_SIZE];\n\t\t}\n\t\tpage[ch & (PAGE_SIZE - 1)] = value;\n\t}\n}\n","import { createPool } from \"../../system/pool.ts\";\nimport Glyph from \"./glyph.ts\";\n\n// bitmap constants\nconst capChars: string[] = [\n\t\"M\",\n\t\"N\",\n\t\"B\",\n\t\"D\",\n\t\"C\",\n\t\"E\",\n\t\"F\",\n\t\"K\",\n\t\"A\",\n\t\"G\",\n\t\"H\",\n\t\"I\",\n\t\"J\",\n\t\"L\",\n\t\"O\",\n\t\"P\",\n\t\"Q\",\n\t\"R\",\n\t\"S\",\n\t\"T\",\n\t\"U\",\n\t\"V\",\n\t\"W\",\n\t\"X\",\n\t\"Y\",\n\t\"Z\",\n];\n\n/**\n * Gets the value from a string of pairs.\n * @ignore\n */\nfunction getValueFromPair(string: string, pattern: RegExp): string {\n\tconst value = string.match(pattern);\n\tif (!value) {\n\t\tthrow new Error(`Could not find pattern ${pattern} in string: ${string}`);\n\t}\n\n\treturn value[0].split(\"=\")[1];\n}\n\n/**\n * Gets the first glyph in the map that is not a space character\n * @ignore\n * @param glyphs - the map of glyphs, each key is a char code\n * @returns the first glyph that is not a space character\n */\nfunction getFirstGlyph(glyphs: any): Glyph | null {\n\tconst keys = Object.keys(glyphs);\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tif (parseInt(keys[i]) > 32) {\n\t\t\treturn glyphs[keys[i]];\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Creates a glyph to use for the space character\n * @ignore\n * @param glyphs - the map of glyphs, each key is a char code\n */\nfunction createSpaceGlyph(glyphs: any) {\n\tconst spaceCharCode = \" \".charCodeAt(0);\n\tlet glyph = glyphs[spaceCharCode];\n\tif (!glyph) {\n\t\tglyph = new Glyph();\n\t\tglyph.id = spaceCharCode;\n\t\tconst firstGlyph = getFirstGlyph(glyphs);\n\t\tglyph.xadvance = firstGlyph !== null ? firstGlyph.xadvance : 0;\n\t\tglyphs[spaceCharCode] = glyph;\n\t}\n}\n\n/**\n * a bitmap font data object — parses the AngelCode BMFont format in both its\n * text (`.fnt`) and XML serialisations (the flavour is auto-detected).\n * @category Text\n */\nexport default class BitmapTextData {\n\tpadTop: number = 0;\n\tpadRight: number = 0;\n\tpadBottom: number = 0;\n\tpadLeft: number = 0;\n\tlineHeight: number = 0;\n\tcapHeight: number = 1;\n\tdescent: number = 0;\n\tglyphMinTop: number = 0;\n\tglyphMaxBottom: number = 0;\n\tglyphs: { [key: number]: Glyph } = {};\n\n\tconstructor(data: string) {\n\t\tthis.parse(data);\n\t}\n\n\tparse(fontData: string) {\n\t\tif (!fontData) {\n\t\t\tthrow new Error(\n\t\t\t\t\"File containing font data was empty, cannot load the bitmap font.\",\n\t\t\t);\n\t\t}\n\n\t\tthis.capHeight = 1;\n\t\tthis.descent = 0;\n\t\tthis.glyphMinTop = Infinity;\n\t\tthis.glyphMaxBottom = 0;\n\t\tthis.glyphs = {};\n\n\t\t// AngelCode BMFont ships in a text (.fnt) and an XML flavour — same data,\n\t\t// different serialisation. Detect which and dispatch.\n\t\tif (fontData.trimStart().startsWith(\"<\")) {\n\t\t\tthis.parseXML(fontData);\n\t\t} else {\n\t\t\tthis.parseText(fontData);\n\t\t}\n\n\t\tif (this.glyphMinTop === Infinity) {\n\t\t\tthis.glyphMinTop = 0;\n\t\t}\n\n\t\tthis.descent += this.padBottom;\n\n\t\tcreateSpaceGlyph(this.glyphs);\n\n\t\tlet capGlyph: Glyph | undefined;\n\t\tfor (let i = 0; i < capChars.length; i++) {\n\t\t\tconst capChar = capChars[i];\n\t\t\tcapGlyph = this.glyphFor(capChar.charCodeAt(0));\n\t\t\tif (capGlyph) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!capGlyph) {\n\t\t\tfor (const charCode in this.glyphs) {\n\t\t\t\tif (Object.prototype.hasOwnProperty.call(this.glyphs, charCode)) {\n\t\t\t\t\tconst glyph = this.glyphs[charCode];\n\t\t\t\t\tif (glyph.height === 0 || glyph.width === 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthis.capHeight = Math.max(this.capHeight, glyph.height);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.capHeight = capGlyph.height;\n\t\t}\n\t\tthis.capHeight -= this.padTop + this.padBottom;\n\t}\n\n\t/**\n\t * Record a parsed glyph and fold it into the font's vertical metrics.\n\t * @ignore\n\t */\n\trecordGlyph(glyph: Glyph, baseLine: number) {\n\t\tif (glyph.width > 0 && glyph.height > 0) {\n\t\t\tthis.descent = Math.min(baseLine + glyph.yoffset, this.descent);\n\t\t\tthis.glyphMinTop = Math.min(this.glyphMinTop, glyph.yoffset);\n\t\t\tthis.glyphMaxBottom = Math.max(\n\t\t\t\tthis.glyphMaxBottom,\n\t\t\t\tglyph.yoffset + glyph.height,\n\t\t\t);\n\t\t}\n\t\tthis.glyphs[glyph.id] = glyph;\n\t}\n\n\t/**\n\t * Look up a glyph by char code, honestly typed as possibly missing (the\n\t * glyph map's index signature otherwise hides that a lookup can be absent).\n\t * @ignore\n\t */\n\tglyphFor(id: number): Glyph | undefined {\n\t\treturn this.glyphs[id];\n\t}\n\n\t/**\n\t * Parse the AngelCode BMFont text (.fnt) format.\n\t * @ignore\n\t */\n\tparseText(fontData: string) {\n\t\tconst lines = fontData.split(/\\r\\n|\\n/);\n\t\tconst padding = fontData.match(/padding=\\d+,\\d+,\\d+,\\d+/g);\n\t\tif (!padding) {\n\t\t\tthrow new Error(\"Padding not found in first line\");\n\t\t}\n\t\tconst paddingValues = padding[0].split(\"=\")[1].split(\",\");\n\n\t\t// AngelCode order is up, RIGHT, down, LEFT (T/R/B/L, like CSS)\n\t\tthis.padTop = parseFloat(paddingValues[0]);\n\t\tthis.padRight = parseFloat(paddingValues[1]);\n\t\tthis.padBottom = parseFloat(paddingValues[2]);\n\t\tthis.padLeft = parseFloat(paddingValues[3]);\n\t\tthis.lineHeight = parseFloat(getValueFromPair(lines[1], /lineHeight=\\d+/g));\n\t\tconst baseLine = parseFloat(getValueFromPair(lines[1], /base=\\d+/g));\n\n\t\tfor (let i = 4; i < lines.length; i++) {\n\t\t\tconst line = lines[i];\n\t\t\tconst characterValues = line.split(/=|\\s+/);\n\t\t\tif (!line || /^kernings/.test(line)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (/^kerning\\s/.test(line)) {\n\t\t\t\tconst first = parseFloat(characterValues[2]);\n\t\t\t\tconst second = parseFloat(characterValues[4]);\n\t\t\t\tconst amount = parseFloat(characterValues[6]);\n\n\t\t\t\tconst glyph = this.glyphFor(first);\n\t\t\t\tif (glyph) {\n\t\t\t\t\tglyph.setKerning(second, amount);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst glyph = new Glyph();\n\t\t\t\tglyph.id = parseFloat(characterValues[2]);\n\t\t\t\tglyph.x = parseFloat(characterValues[4]);\n\t\t\t\tglyph.y = parseFloat(characterValues[6]);\n\t\t\t\tglyph.width = parseFloat(characterValues[8]);\n\t\t\t\tglyph.height = parseFloat(characterValues[10]);\n\t\t\t\tglyph.xoffset = parseFloat(characterValues[12]);\n\t\t\t\tglyph.yoffset = parseFloat(characterValues[14]);\n\t\t\t\tglyph.xadvance = parseFloat(characterValues[16]);\n\t\t\t\tthis.recordGlyph(glyph, baseLine);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Parse the AngelCode BMFont XML format (same data as the .fnt text form).\n\t * Padding is optional in XML exports (e.g. frostyfreeze) and defaults to 0.\n\t * @ignore\n\t */\n\tparseXML(fontData: string) {\n\t\t// Parsed with simple regex rather than DOMParser, so it also works where\n\t\t// no DOM is available (Node / SSR / headless). BMFont XML is a flat,\n\t\t// attributes-only format, which makes this safe and dependency-free.\n\t\tconst attrs = (tag: string) => {\n\t\t\tconst out = new Map<string, string>();\n\t\t\t// bounded name/whitespace repetition keeps this strictly linear (no\n\t\t\t// polynomial backtracking) on arbitrary font data; BMFont attribute\n\t\t\t// names and the surrounding spacing are always short\n\t\t\tfor (const m of tag.matchAll(/(\\w{1,64})\\s{0,8}=\\s{0,8}\"([^\"]*)\"/g)) {\n\t\t\t\tout.set(m[1], m[2]);\n\t\t\t}\n\t\t\treturn out;\n\t\t};\n\t\tconst num = (o: Map<string, string>, name: string) => {\n\t\t\treturn parseFloat(o.get(name) ?? \"0\");\n\t\t};\n\n\t\tconst commonTag = fontData.match(/<common\\b[^>]*>/);\n\t\tif (commonTag === null) {\n\t\t\tthrow new Error(\"Invalid BMFont XML: missing <common> element.\");\n\t\t}\n\t\tconst infoTag = fontData.match(/<info\\b[^>]*>/);\n\t\tconst info = infoTag ? attrs(infoTag[0]) : new Map<string, string>();\n\t\tconst common = attrs(commonTag[0]);\n\n\t\t// padding is optional in XML exports (e.g. frostyfreeze) → default to 0\n\t\tconst pad = (info.get(\"padding\") ?? \"0,0,0,0\").split(\",\");\n\t\t// AngelCode order is up, RIGHT, down, LEFT (T/R/B/L, like CSS)\n\t\tthis.padTop = parseFloat(pad[0]);\n\t\tthis.padRight = parseFloat(pad[1]);\n\t\tthis.padBottom = parseFloat(pad[2]);\n\t\tthis.padLeft = parseFloat(pad[3]);\n\t\tthis.lineHeight = num(common, \"lineHeight\");\n\t\tconst baseLine = num(common, \"base\");\n\n\t\tfor (const m of fontData.matchAll(/<char\\b[^>]*>/g)) {\n\t\t\tconst a = attrs(m[0]);\n\t\t\tconst glyph = new Glyph();\n\t\t\tglyph.id = num(a, \"id\");\n\t\t\tglyph.x = num(a, \"x\");\n\t\t\tglyph.y = num(a, \"y\");\n\t\t\tglyph.width = num(a, \"width\");\n\t\t\tglyph.height = num(a, \"height\");\n\t\t\tglyph.xoffset = num(a, \"xoffset\");\n\t\t\tglyph.yoffset = num(a, \"yoffset\");\n\t\t\tglyph.xadvance = num(a, \"xadvance\");\n\t\t\tthis.recordGlyph(glyph, baseLine);\n\t\t}\n\t\tfor (const m of fontData.matchAll(/<kerning\\b[^>]*>/g)) {\n\t\t\tconst a = attrs(m[0]);\n\t\t\tconst glyph = this.glyphFor(num(a, \"first\"));\n\t\t\tif (glyph) {\n\t\t\t\tglyph.setKerning(num(a, \"second\"), num(a, \"amount\"));\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const bitmapTextDataPool = createPool<\n\tBitmapTextData,\n\t[fontData: string]\n>((fontData: string) => {\n\tconst instance = new BitmapTextData(fontData);\n\n\treturn {\n\t\tinstance,\n\t\treset(fontData) {\n\t\t\tinstance.parse(fontData);\n\t\t},\n\t};\n});\n","import { ellipsePool } from \"./geometries/ellipse\";\nimport { linePool } from \"./geometries/line\";\nimport { pointPool } from \"./geometries/point\";\nimport { polygonPool } from \"./geometries/polygon\";\nimport { rectPool } from \"./geometries/rectangle\";\nimport { roundedRectanglePool } from \"./geometries/roundrect\";\nimport { colorPool } from \"./math/color\";\nimport { matrix2dPool } from \"./math/matrix2d\";\nimport { matrix3dPool } from \"./math/matrix3d\";\nimport { vector2dPool } from \"./math/vector2d\";\nimport { vector3dPool } from \"./math/vector3d\";\nimport type ParticleEmitter from \"./particles/emitter\";\nimport type Particle from \"./particles/particle\";\nimport { boundsPool } from \"./physics/bounds\";\nimport { bitmapTextDataPool } from \"./renderable/text/bitmaptextdata\";\nimport type { Pool } from \"./system/pool\";\nimport { getRegisteredPools, registerPool } from \"./system/pool\";\nimport { tweenPool } from \"./tweens/tween\";\n\n// Register all pools that don't have circular dependency issues\nregisterPool(\"vector2d\", vector2dPool);\nregisterPool(\"vector3d\", vector3dPool);\nregisterPool(\"point\", pointPool);\nregisterPool(\"matrix2d\", matrix2dPool);\nregisterPool(\"matrix3d\", matrix3dPool);\nregisterPool(\"bounds\", boundsPool);\nregisterPool(\"color\", colorPool);\nregisterPool(\"polygon\", polygonPool);\nregisterPool(\"line\", linePool);\nregisterPool(\"rectangle\", rectPool);\nregisterPool(\"roundedRectangle\", roundedRectanglePool);\nregisterPool(\"ellipse\", ellipsePool);\nregisterPool(\"tween\", tweenPool);\nregisterPool(\"bitmapTextData\", bitmapTextDataPool);\n// Note: particlePool registers itself in particles/particle.ts to avoid circular dependencies\n\n// Type map derived from actual pool instances for type-safe getPool()\n// particlePool uses import() type to avoid circular runtime dependency\ninterface PoolMap {\n\tvector2d: typeof vector2dPool;\n\tvector3d: typeof vector3dPool;\n\tpoint: typeof pointPool;\n\tmatrix2d: typeof matrix2dPool;\n\tmatrix3d: typeof matrix3dPool;\n\tbounds: typeof boundsPool;\n\tcolor: typeof colorPool;\n\tpolygon: typeof polygonPool;\n\tline: typeof linePool;\n\trectangle: typeof rectPool;\n\troundedRectangle: typeof roundedRectanglePool;\n\tellipse: typeof ellipsePool;\n\ttween: typeof tweenPool;\n\tparticle: Pool<Particle, [emitter: ParticleEmitter]>;\n\tbitmapTextData: typeof bitmapTextDataPool;\n}\n\ntype PoolKey = keyof PoolMap;\n\nexport const getPool = <K extends PoolKey>(key: K): PoolMap[K] => {\n\treturn getRegisteredPools()[key] as PoolMap[K];\n};\n\nexport const getTotalPoolSize = (): number => {\n\tconst pools = getRegisteredPools();\n\tlet totalSize = 0;\n\tfor (const key in pools) {\n\t\ttotalSize += pools[key].size();\n\t}\n\treturn totalSize;\n};\n\nexport { createPool, registerPool } from \"./system/pool\";\n","import { getTotalPoolSize } from \"../pool\";\n\n/**\n * callback invoked when a class is registered, used by the Tiled object factory\n * @ignore\n */\nlet onRegisterCallback = null;\n\n/**\n * Set a callback to be invoked whenever pool.register() is called.\n * @param {Function} callback - function(className, classObj, poolInstance) called on each registration\n * @ignore\n */\nexport function setPoolRegisterCallback(callback) {\n\tonRegisterCallback = callback;\n}\n\n/**\n * Object pooling - a technique that might speed up your game if used properly.<br>\n * If some of your classes will be instantiated and removed a lot at a time, it is a\n * good idea to add the class to this object pool. A separate pool for that class\n * will be created, which will reuse objects of the class. That way they won't be instantiated\n * each time you need a new one (slowing your game), but stored into that pool and taking one\n * already instantiated when you need it.<br><br>\n * This technique is also used by the engine to instantiate objects defined in the map,\n * which means, that on level loading the engine will try to instantiate every object\n * found in the map, based on the user defined name in each Object Properties<br>\n * <img src=\"../images/object_properties.png\"/><br>\n * @see {@link pool} the default global instance of ObjectPool\n */\nclass ObjectPool {\n\tconstructor() {\n\t\tthis.objectClass = {};\n\t\tthis.instance_counter = 0;\n\n\t\t/**\n\t\t * When enabled, classes registered via {@link pool.register} are also\n\t\t * automatically registered as Tiled object factories, so that objects\n\t\t * placed in a Tiled map with a matching class or name will be\n\t\t * instantiated using the registered constructor.\n\t\t * Set to `false` to disable this behavior (e.g. for classes that should\n\t\t * only be used programmatically and not from Tiled maps).\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t * @example\n\t\t * // disable auto-registration for a specific class\n\t\t * pool.autoRegisterTiled = false;\n\t\t * pool.register(\"InternalHelper\", HelperClass);\n\t\t * pool.autoRegisterTiled = true; // re-enable for subsequent registrations\n\t\t */\n\t\tthis.autoRegisterTiled = true;\n\t}\n\n\t/**\n\t * Register an object to the pool. <br>\n\t * Pooling must be set to true if more than one such objects will be created. <br>\n\t * (Note: for an object to be poolable, it must implement an `onResetEvent` method) <br><br>\n\t * Registered classes are also automatically available as Tiled object factories,\n\t * meaning objects placed in a Tiled map with a matching class or name will be\n\t * instantiated using the registered constructor. For more control, use\n\t * {@link registerTiledObjectClass} or {@link registerTiledObjectFactory} instead.\n\t * @param {string} className - as defined in the Name field of the Object Properties (in Tiled)\n\t * @param {object} classObj - corresponding Class to be instantiated\n\t * @param {boolean} [recycling=false] - enables object recycling for the specified class\n\t * @example\n\t * // implement CherryEntity\n\t * class Cherry extends Sprite {\n\t *    onResetEvent() {\n\t *        // reset object mutable properties\n\t *        this.lifeBar = 100;\n\t *    }\n\t * };\n\t * // add our users defined entities in the object pool and enable object recycling\n\t * // this also registers \"cherrysprite\" as a Tiled object factory, so any object\n\t * // with class or name \"cherrysprite\" in a Tiled map will create a Cherry instance\n\t * me.pool.register(\"cherrysprite\", Cherry, true);\n\t */\n\tregister(className, classObj, recycling = false) {\n\t\tif (typeof classObj !== \"undefined\") {\n\t\t\tconst entry = {\n\t\t\t\tclass: classObj,\n\t\t\t\tpool: recycling ? [] : undefined,\n\t\t\t};\n\t\t\tthis.objectClass[className] = entry;\n\t\t\t// also register with \"me.\" prefix for backward compatibility\n\t\t\tif (!className.startsWith(\"me.\")) {\n\t\t\t\tthis.objectClass[\"me.\" + className] = entry;\n\t\t\t}\n\t\t\t// also register as a Tiled object factory\n\t\t\tif (this.autoRegisterTiled && typeof onRegisterCallback === \"function\") {\n\t\t\t\tonRegisterCallback(className, classObj, this);\n\t\t\t\t// also register with \"me.\" prefix for backward compatibility\n\t\t\t\tif (!className.startsWith(\"me.\")) {\n\t\t\t\t\tonRegisterCallback(\"me.\" + className, classObj, this);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t\"Cannot register object '\" + className + \"', invalid class\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Pull a new instance of the requested object (if added into the object pool)\n\t * @param {string} name - as used in {@link pool.register}\n\t * @param {...*} [args] - arguments to be passed when instantiating/reinitializing the object\n\t * @returns {object} the instance of the requested object\n\t * @example\n\t * me.pool.register(\"bullet\", BulletEntity, true);\n\t * me.pool.register(\"enemy\", EnemyEntity, true);\n\t * // ...\n\t * // when we need to manually create a new bullet:\n\t * let bullet = me.pool.pull(\"bullet\", x, y, direction);\n\t * // ...\n\t * // params aren't a fixed number\n\t * // when we need new enemy we can add more params, that the object construct requires:\n\t * let enemy = me.pool.pull(\"enemy\", x, y, direction, speed, power, life);\n\t * // ...\n\t * // when we want to destroy existing object, the remove\n\t * // function will ensure the object can then be reallocated later\n\t * app.world.removeChild(enemy);\n\t * app.world.removeChild(bullet);\n\t */\n\tpull(name, ...args) {\n\t\tconst className = this.objectClass[name];\n\t\tif (className) {\n\t\t\tconst proto = className[\"class\"];\n\t\t\tconst poolArray = className.pool;\n\t\t\tlet obj;\n\n\t\t\tif (poolArray && (obj = poolArray.pop())) {\n\t\t\t\t// poolable object must implement a `onResetEvent` method\n\t\t\t\tobj.onResetEvent.apply(obj, args);\n\t\t\t\tthis.instance_counter--;\n\t\t\t} else {\n\t\t\t\t// create a new instance\n\t\t\t\tobj = new (proto.bind.apply(proto, [proto, ...args]))();\n\t\t\t\tif (poolArray) {\n\t\t\t\t\tobj.className = name;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn obj;\n\t\t}\n\t\tthrow new Error(\"Cannot instantiate object of type '\" + name + \"'\");\n\t}\n\n\t/**\n\t * purge the object pool from any inactive object <br>\n\t * Object pooling must be enabled for this function to work<br>\n\t * note: this will trigger the garbage collector\n\t */\n\tpurge() {\n\t\tfor (const className in this.objectClass) {\n\t\t\tif (this.objectClass[className]) {\n\t\t\t\tthis.objectClass[className].pool = [];\n\t\t\t}\n\t\t}\n\t\tthis.instance_counter = 0;\n\t}\n\n\t/**\n\t * Push back an object instance into the object pool <br>\n\t * Object pooling for the object class must be enabled,\n\t * and object must have been instantiated using {@link pull},\n\t * otherwise this function won't work\n\t * @throws will throw an error if the object cannot be recycled\n\t * @param {object} obj - instance to be recycled\n\t * @param {boolean} [throwOnError=true] - throw an exception if the object cannot be recycled\n\t * @returns {boolean} true if the object was successfully recycled in the object pool\n\t */\n\tpush(obj, throwOnError = true) {\n\t\tif (!this.poolable(obj)) {\n\t\t\tif (throwOnError === true) {\n\t\t\t\tthrow new Error(\"me.pool: object \" + obj + \" cannot be recycled\");\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// store back the object instance for later recycling\n\t\tthis.objectClass[obj.className].pool.push(obj);\n\t\tthis.instance_counter++;\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if an object with the provided name is registered\n\t * @param {string} name - of the registered object class\n\t * @returns {boolean} true if the classname is registered\n\t */\n\texists(name) {\n\t\treturn name in this.objectClass;\n\t}\n\n\t/**\n\t * Check if an object is poolable\n\t * (was properly registered with the recycling feature enable)\n\t * @see register\n\t * @param {object} obj - object to be checked\n\t * @returns {boolean} true if the object is poolable\n\t * @example\n\t * if (!me.pool.poolable(myCherryEntity)) {\n\t *     // object was not properly registered\n\t * }\n\t */\n\tpoolable(obj) {\n\t\tconst className = obj.className;\n\t\treturn (\n\t\t\ttypeof className !== \"undefined\" &&\n\t\t\ttypeof obj.onResetEvent === \"function\" &&\n\t\t\tclassName in this.objectClass &&\n\t\t\ttypeof this.objectClass[className].pool !== \"undefined\"\n\t\t);\n\t}\n\n\t/**\n\t * returns the amount of object instance currently in the pool\n\t * @returns {number} amount of object instance\n\t */\n\tgetInstanceCount() {\n\t\treturn this.instance_counter + getTotalPoolSize();\n\t}\n}\n\n/**\n * a default global ObjectPool instance\n * @namespace pool\n * @see ObjectPool\n * @example\n * // register our bullet object into the object pool\n * pool.register(\"bullet\", BulletEntity, true);\n * // ...\n * // when we need to manually create a new bullet:\n * let bullet = pool.pull(\"bullet\", x, y, direction, velocity);\n * // ...\n * // when we want to destroy existing object, the remove\n * // function will ensure the object can then be reallocated later\n * game.world.removeChild(bullet);\n */\nconst pool = new ObjectPool();\n\nexport default pool;\n","import { game } from \"../application/application.ts\";\n\n/**\n * @import {Line} from \"./../geometries/line.ts\";\n * @import Renderable from \"./../renderable/renderable.js\";\n **/\n\n/**\n * Collision detection (and projection-based collision response) of 2D shapes.<br>\n * Based on the Separating Axis Theorem and supports detecting collisions between simple Axis-Aligned Boxes, convex polygons and circles based shapes.\n * @namespace collision\n */\n\nexport const collision = {\n\t/**\n\t * The maximum number of children that a quadtree node can contain before it is split into sub-nodes.\n\t * @name maxChildren\n\t * @memberof collision\n\t * @public\n\t * @type {number}\n\t * @default 8\n\t * @see game.world.broadphase\n\t */\n\tmaxChildren: 8,\n\n\t/**\n\t * The maximum number of levels that the quadtree will create.\n\t * @name maxDepth\n\t * @memberof collision\n\t * @public\n\t * @type {number}\n\t * @default 4\n\t * @see game.world.broadphase\n\t */\n\tmaxDepth: 4,\n\n\t/**\n\t * Enum for collision type values.\n\t * @property {number} NO_OBJECT to disable collision check\n\t * @property {number} PLAYER_OBJECT playbable characters\n\t * @property {number} NPC_OBJECT non playable characters\n\t * @property {number} ENEMY_OBJECT enemies objects\n\t * @property {number} COLLECTABLE_OBJECT collectable objects\n\t * @property {number} ACTION_OBJECT e.g. doors\n\t * @property {number} PROJECTILE_OBJECT e.g. missiles\n\t * @property {number} WORLD_SHAPE e.g. walls; for map collision shapes\n\t * @property {number} USER user-defined collision types (see example)\n\t * @property {number} ALL_OBJECT all of the above (including user-defined types)\n\t * @readonly\n\t * @enum {number}\n\t * @name types\n\t * @memberof collision\n\t * @see Body.setCollisionMask\n\t * @see Body.collisionType\n\t * @example\n\t * // set the body collision type\n\t * myEntity.body.collisionType = me.collision.types.PLAYER_OBJECT;\n\t *\n\t * // filter collision detection with collision shapes, enemies and collectables\n\t * myEntity.body.setCollisionMask(\n\t *     me.collision.types.WORLD_SHAPE |\n\t *     me.collision.types.ENEMY_OBJECT |\n\t *     me.collision.types.COLLECTABLE_OBJECT\n\t * );\n\t *\n\t * // User-defined collision types are defined using BITWISE LEFT-SHIFT:\n\t * game.collisionTypes = {\n\t *     LOCKED_DOOR : me.collision.types.USER << 0,\n\t *     OPEN_DOOR   : me.collision.types.USER << 1,\n\t *     LOOT        : me.collision.types.USER << 2,\n\t * };\n\t *\n\t * // Set collision type for a door entity\n\t * myDoorEntity.body.collisionType = game.collisionTypes.LOCKED_DOOR;\n\t *\n\t * // Set collision mask for the player entity, so it collides with locked doors and loot\n\t * myPlayerEntity.body.setCollisionMask(\n\t *     me.collision.types.ENEMY_OBJECT |\n\t *     me.collision.types.WORLD_SHAPE |\n\t *     game.collisionTypes.LOCKED_DOOR |\n\t *     game.collisionTypes.LOOT\n\t * );\n\t */\n\ttypes: {\n\t\t/** to disable collision check */\n\t\tNO_OBJECT: 0,\n\t\tPLAYER_OBJECT: 1 << 0,\n\t\tNPC_OBJECT: 1 << 1,\n\t\tENEMY_OBJECT: 1 << 2,\n\t\tCOLLECTABLE_OBJECT: 1 << 3,\n\t\tACTION_OBJECT: 1 << 4, // door, etc...\n\t\tPROJECTILE_OBJECT: 1 << 5, // missiles, etc...\n\t\tWORLD_SHAPE: 1 << 6, // walls, etc...\n\t\tUSER: 1 << 7, // user-defined types start here...\n\t\tALL_OBJECT: 0xffffffff, // all objects\n\t},\n\n\t/**\n\t * Checks for object colliding with the given line\n\t * @name rayCast\n\t * @memberof collision\n\t * @public\n\t * @param {Line} line - line to be tested for collision\n\t * @param {Array.<Renderable>} [result] - a user defined array that will be populated with intersecting physic objects.\n\t * @returns {Array.<Renderable>} an array of intersecting physic objects\n\t * @example\n\t *    // define a line accross the viewport\n\t *    let ray = new Line(\n\t *        // absolute position of the line\n\t *        0, 0, [\n\t *        // starting point relative to the initial position\n\t *        new me.Vector2d(0, 0),\n\t *        // ending point\n\t *        new me.Vector2d(app.viewport.width, app.viewport.height)\n\t *    ]);\n\t *\n\t *    // check for collition\n\t *    result = me.collision.rayCast(ray);\n\t *\n\t *    if (result.length > 0) {\n\t *        // ...\n\t *    }\n\t */\n\trayCast(line, result) {\n\t\treturn game.world.detector.rayCast(line, result);\n\t},\n};\n","import { Box3d, box3dPool } from \"../../geometries/box3d.ts\";\nimport { Ellipse, ellipsePool } from \"../../geometries/ellipse.ts\";\nimport { Line, linePool } from \"../../geometries/line.ts\";\nimport { Point, pointPool } from \"../../geometries/point.ts\";\nimport { Polygon, polygonPool } from \"../../geometries/polygon.ts\";\nimport { Rect } from \"../../geometries/rectangle.ts\";\nimport { clamp } from \"../../math/math.ts\";\nimport { Vector2d, vector2dPool } from \"../../math/vector2d.ts\";\nimport pool from \"../../system/legacy_pool.js\";\nimport timer from \"../../system/timer.ts\";\nimport { remove } from \"../../utils/array.ts\";\nimport { Bounds, boundsPool } from \"../bounds.ts\";\nimport { collision } from \"../collision.js\";\n\n/**\n * @import Entity from \"../../renderable/entity/entity.js\";\n * @import Container from \"../../renderable/container.js\";\n * @import Renderable from \"../../renderable/renderable.js\";\n * @import Sprite from \"../../renderable/sprite.js\";\n * @import NineSliceSprite from \"../../renderable/nineslicesprite.js\";\n * @import {Vector2d} from \"../../math/vector2d.js\";\n * @import ResponseObject from \"../response.js\";\n **/\n\n/**\n * Monotonic source of stable per-shape contact ids (#1596).\n * Never reset: ids only have to be unique among LIVE shapes, and wrapping\n * would be reachable only after 2^53 shapes.\n */\nlet shapeContactIdCounter = 0;\n\n/**\n * a Generic Physic Body Object with some physic properties and behavior functionality, to add as a member of a Renderable.\n * @category Physics\n * @see Renderable.body\n */\nexport default class Body {\n\t/**\n\t * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} ancestor - the parent object this body is attached to\n\t * @param {Rect|Rect[]|Polygon|Polygon[]|Line|Line[]|Ellipse|Ellipse[]|Point|Point[]|Bounds|Bounds[]|object} [shapes] - a initial shape, list of shapes, or JSON object defining the body\n\t * @param {Function} [onBodyUpdate] - callback for when the body is updated (e.g. add/remove shapes)\n\t */\n\tconstructor(ancestor, shapes, onBodyUpdate) {\n\t\t/**\n\t\t * a reference to the parent object that contains this body,\n\t\t * or undefined if it has not been added to one.\n\t\t * @public\n\t\t * @type {Renderable|Container|Entity|Sprite|NineSliceSprite}\n\t\t * @default undefined\n\t\t */\n\t\tthis.ancestor = ancestor;\n\n\t\tif (typeof this.bounds === \"undefined\") {\n\t\t\t/**\n\t\t\t * The AABB bounds box representing this body\n\t\t\t * @public\n\t\t\t * @type {Bounds}\n\t\t\t */\n\t\t\tthis.bounds = boundsPool.get();\n\t\t}\n\n\t\tif (typeof this.shapes === \"undefined\") {\n\t\t\t/**\n\t\t\t * The collision shapes of the body. Always an array — every\n\t\t\t * mutation path goes through `push` / `includes`. The\n\t\t\t * non-array `Point` that used to be in this union was a\n\t\t\t * mis-doc and broke downstream `shapes.length` reads in\n\t\t\t * TypeScript.\n\t\t\t * @ignore\n\t\t\t * @type {(Polygon|Line|Ellipse|Point)[]}\n\t\t\t */\n\t\t\tthis.shapes = [];\n\t\t}\n\n\t\t/**\n\t\t * The body collision mask, that defines what should collide with what.<br>\n\t\t * (by default will collide with all entities)\n\t\t * @ignore\n\t\t * @type {number}\n\t\t * @default collision.types.ALL_OBJECT\n\t\t * @see collision.types\n\t\t */\n\t\tthis.collisionMask = collision.types.ALL_OBJECT;\n\n\t\t/**\n\t\t * define the collision type of the body for collision filtering\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default collision.types.ENEMY_OBJECT\n\t\t * @see collision.types\n\t\t * @example\n\t\t * // set the body collision type\n\t\t * body.collisionType = me.collision.types.PLAYER_OBJECT;\n\t\t */\n\t\tthis.collisionType = collision.types.ENEMY_OBJECT;\n\n\t\tif (typeof this.vel === \"undefined\") {\n\t\t\t/**\n\t\t\t * The current velocity of the body.\n\t\t\t * See to apply a force if you need to modify a body velocity\n\t\t\t * @see Body.force\n\t\t\t * @public\n\t\t\t * @type {Vector2d}\n\t\t\t * @default <0,0>\n\t\t\t */\n\t\t\tthis.vel = vector2dPool.get();\n\t\t}\n\t\tthis.vel.set(0, 0);\n\n\t\tif (typeof this.force === \"undefined\") {\n\t\t\t/**\n\t\t\t * body force to apply to this the body in the current step.\n\t\t\t * (any positive or negative force will be cancelled after every world/body update cycle)\n\t\t\t * @public\n\t\t\t * @type {Vector2d}\n\t\t\t * @default <0,0>\n\t\t\t * @see Body.setMaxVelocity\n\t\t\t * @example\n\t\t\t * // define a default maximum acceleration, initial force and friction\n\t\t\t * this.body.force.set(1, 0);\n\t\t\t * this.body.friction.set(0.4, 0);\n\t\t\t * this.body.setMaxVelocity(3, 15);\n\t\t\t *\n\t\t\t * // apply a positive or negative force when pressing left of right key\n\t\t\t * update(dt) {\n\t\t\t *     if (me.input.isKeyPressed(\"left\"))    {\n\t\t\t *          this.body.force.x = -this.body.maxVel.x;\n\t\t\t *      } else if (me.input.isKeyPressed(\"right\")) {\n\t\t\t *         this.body.force.x = this.body.maxVel.x;\n\t\t\t *     }\n\t\t\t * }\n\t\t\t */\n\t\t\tthis.force = vector2dPool.get();\n\t\t}\n\t\tthis.force.set(0, 0);\n\n\t\tif (typeof this.friction === \"undefined\") {\n\t\t\t/**\n\t\t\t * body friction\n\t\t\t * @public\n\t\t\t * @type {Vector2d}\n\t\t\t * @default <0,0>\n\t\t\t */\n\t\t\tthis.friction = vector2dPool.get();\n\t\t}\n\t\tthis.friction.set(0, 0);\n\n\t\t/**\n\t\t * the body bounciness level when colliding with other solid bodies :\n\t\t * a value of 0 will not bounce, a value of 1 will fully rebound.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.bounce = 0;\n\n\t\t/**\n\t\t * the body mass\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 1\n\t\t */\n\t\tthis.mass = 1;\n\n\t\t/**\n\t\t * Current rotation angle (radians). Visual-only under the built-in\n\t\t * SAT solver — the collision shapes themselves are NOT rotated by\n\t\t * angular integration; only `renderable.currentTransform` is.\n\t\t * Defaults to `0` and is integrated each step from\n\t\t * {@link Body#angularVelocity}. Set directly via\n\t\t * {@link Body#setAngle} or as a side effect of integration.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @example\n\t\t * // teleport the sprite to face down-right (45°):\n\t\t * sprite.body.angle = Math.PI / 4;\n\t\t */\n\t\tthis.angle = 0;\n\n\t\t/**\n\t\t * Angular velocity in radians per frame. Default `0` — bodies that\n\t\t * never touch the angular API pay no integration cost. See\n\t\t * {@link Body#setAngularVelocity} and {@link Body#applyTorque}.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @example\n\t\t * // continuously spin a pickup at ~3°/frame (~180°/sec at 60fps):\n\t\t * pickup.body.angularVelocity = 0.05;\n\t\t */\n\t\tthis.angularVelocity = 0;\n\n\t\t/**\n\t\t * Per-step exponential decay applied to {@link Body#angularVelocity}\n\t\t * — analog of `frictionAir` for rotation. `0` = no damping\n\t\t * (bodies spin forever); `1` = full damping per step (rotation\n\t\t * stops instantly). Typical values are small (`0.01` – `0.05`)\n\t\t * so spin decays naturally over a few seconds. Negative values\n\t\t * are ignored (treated as `0`); values `> 1` flip the sign each\n\t\t * step (overdamped — usually a foot-gun).\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @example\n\t\t * // dynamite barrel spins on impact then slows to a stop:\n\t\t * barrel.body.angularDrag = 0.02;\n\t\t * barrel.body.applyTorque(120);\n\t\t */\n\t\tthis.angularDrag = 0;\n\n\t\t/**\n\t\t * Moment-of-inertia analog used when converting force-at-offset\n\t\t * and torque into angular velocity: `Δω = τ / pseudoInertia`.\n\t\t * Defaults to a geometry-derived approximation\n\t\t * `(width² + height²) / 12` matching the moment of inertia of a\n\t\t * unit-mass rectangle. Override directly to make a body harder or\n\t\t * easier to spin. Auto-recomputed after every {@link Body#addShape}\n\t\t * call — manual overrides must come AFTER the body is fully built.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default `(width² + height²) / 12`\n\t\t * @example\n\t\t * // a heavy boss is hard to knock around:\n\t\t * boss.body.addShape(new me.Rect(0, 0, 64, 64));\n\t\t * boss.body.pseudoInertia *= 10;\n\t\t */\n\t\tthis.pseudoInertia = 1;\n\n\t\tif (typeof this.maxVel === \"undefined\") {\n\t\t\t/**\n\t\t\t * max velocity (to limit body velocity)\n\t\t\t * @public\n\t\t\t * @type {Vector2d}\n\t\t\t * @default <490,490>\n\t\t\t */\n\t\t\tthis.maxVel = vector2dPool.get();\n\t\t}\n\t\t// cap by default to half the default gravity force\n\t\tthis.maxVel.set(490, 490);\n\n\t\t/**\n\t\t * The current velocity of the body along the depth axis.\n\t\t *\n\t\t * Z arrives as **scalars beside** `vel` / `force` / `friction` /\n\t\t * `maxVel` rather than by widening them to {@link Vector3d}, because\n\t\t * `Vector3d` is not a subclass of {@link Vector2d}: retyping them —\n\t\t * or the {@link Body#getVelocity} vector they hand back — would break\n\t\t * every existing 2D consumer. They stay exactly as they were.\n\t\t *\n\t\t * All four default to the inert value, so a body that never touches\n\t\t * them integrates z by `+= 0` and behaves identically to 19.x. Only a\n\t\t * body carrying a {@link Box3d} shape can be pushed back along z by\n\t\t * the solver.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @see Body#forceZ\n\t\t * @see Box3d\n\t\t */\n\t\tthis.velZ = 0;\n\n\t\t/**\n\t\t * body force to apply along the depth axis in the current step.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @see Body#velZ\n\t\t */\n\t\tthis.forceZ = 0;\n\n\t\t/**\n\t\t * body friction along the depth axis.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 0\n\t\t * @see Body#setFriction\n\t\t */\n\t\tthis.frictionZ = 0;\n\n\t\t/**\n\t\t * max velocity along the depth axis (to limit body velocity).\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 490\n\t\t * @see Body#setMaxVelocity\n\t\t */\n\t\tthis.maxVelZ = 490;\n\n\t\t/**\n\t\t * `true` when at least one of this body's shapes is a {@link Box3d},\n\t\t * i.e. this body has a depth extent and can be resolved along z.\n\t\t * Maintained by {@link Body#addShape} / {@link Body#removeShape}.\n\t\t * @readonly\n\t\t * @public\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\t// derived rather than reset to `false`: `this.shapes` is only\n\t\t// allocated when undefined, so a pooled body handed back for reuse\n\t\t// still carries its previous shapes at this point.\n\t\tthis.hasDepth = this.shapes.some((shape) => {\n\t\t\treturn shape.type === \"Box3d\";\n\t\t});\n\n\t\t/**\n\t\t * Either this body is a static body or not.\n\t\t * A static body is completely fixed and can never change position or angle.\n\t\t * @readonly\n\t\t * @public\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.isStatic = false;\n\n\t\t/**\n\t\t * Whether this body is a sensor. A sensor detects collisions and\n\t\t * fires the `onCollision*` events on the renderable, but does not\n\t\t * physically respond to the contact (the SAT solver skips the\n\t\t * positional push-out). Useful for triggers, ground-snap assists,\n\t\t * etc. — same role as Matter's `isSensor`.\n\t\t * @public\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.isSensor = false;\n\n\t\t/**\n\t\t * The degree to which this body is affected by the world gravity\n\t\t * @public\n\t\t * @see {@link World.gravity}\n\t\t * @type {number}\n\t\t * @default 1.0\n\t\t */\n\t\tthis.gravityScale = 1.0;\n\n\t\t/**\n\t\t * If true this body won't be affected by the world gravity.\n\t\t * @public\n\t\t * @see {@link World.gravity}\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t * @deprecated since 19.5.0 — use `gravityScale = 0` (or\n\t\t * `bodyDef.gravityScale = 0` at construction, or\n\t\t * `body.setGravityScale(0)` at runtime) instead. `gravityScale`\n\t\t * is part of the portable {@link PhysicsBody} surface and works\n\t\t * on every adapter; `ignoreGravity` is a builtin-only field that\n\t\t * the matter adapter silently ignores. The two-field check\n\t\t * (`!ignoreGravity && gravityScale !== 0`) used by builtin is\n\t\t * redundant — set `gravityScale = 0` and both branches agree.\n\t\t */\n\t\tthis.ignoreGravity = false;\n\n\t\t/**\n\t\t * falling state of the body<br>\n\t\t * true if the object is falling<br>\n\t\t * false if the object is standing on something<br>\n\t\t * @readonly\n\t\t * @public\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.falling = false;\n\n\t\t/**\n\t\t * jumping state of the body<br>\n\t\t * equal true if the body is jumping<br>\n\t\t * @readonly\n\t\t * @public\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.jumping = false;\n\n\t\tif (typeof onBodyUpdate === \"function\") {\n\t\t\tthis.onBodyUpdate = onBodyUpdate;\n\t\t}\n\n\t\tthis.bounds.clear();\n\n\t\t// parses the given shapes array and add them\n\t\tif (typeof shapes !== \"undefined\") {\n\t\t\tif (Array.isArray(shapes)) {\n\t\t\t\tfor (let s = 0; s < shapes.length; s++) {\n\t\t\t\t\tthis.addShape(shapes[s]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.addShape(shapes);\n\t\t\t}\n\t\t}\n\n\t\t// automatically enable physic when a body is added to a renderable\n\t\tthis.ancestor.isKinematic = false;\n\t}\n\n\t/**\n\t * set the body as a static body\n\t * static body do not move automatically and do not check against collision with others\n\t * @param {boolean} [isStatic=true]\n\t */\n\tsetStatic(isStatic = true) {\n\t\tthis.isStatic = isStatic === true;\n\t}\n\n\t/**\n\t * set this body's linear velocity. Portable across physics adapters —\n\t * under the builtin adapter this mutates `body.vel`; under Matter it\n\t * delegates to `Matter.Body.setVelocity`.\n\t * @param {number} x - velocity along the X axis\n\t * @param {number} y - velocity along the Y axis\n\t */\n\tsetVelocity(x, y) {\n\t\tthis.vel.set(x, y);\n\t}\n\n\t/**\n\t * read this body's linear velocity into an optional output vector.\n\t * @param {Vector2d} [out] - vector to write into; a new Vector2d is\n\t * allocated when omitted\n\t * @returns {Vector2d}\n\t */\n\tgetVelocity(out) {\n\t\treturn (out ?? vector2dPool.get()).copy(this.vel);\n\t}\n\n\t/**\n\t * accumulate a force on this body for the current step. Repeated calls\n\t * within a single update add together; the engine clears the\n\t * accumulator at the end of each integration step. Force magnitude\n\t * conventions differ between adapters — consult the active adapter's\n\t * docs for tuning ranges (builtin: px/frame²; Matter: Newtonian\n\t * `force/mass·dt²`, typically ~100× smaller than builtin).\n\t * @param {number} x - force along the X axis\n\t * @param {number} y - force along the Y axis\n\t * @param {number} [pointX] - world X of the application point; when\n\t *   present (along with `pointY`) and different from the body centroid,\n\t *   the resulting lever arm generates a torque\n\t *   `τ = (r.x · F.y) − (r.y · F.x)` that bumps {@link Body#angularVelocity}\n\t *   by `τ / pseudoInertia`. Omit both `pointX` and `pointY` for the\n\t *   linear-only behaviour that's compatible with code written before\n\t *   the angular API was added.\n\t * @param {number} [pointY] - world Y of the application point\n\t * @example\n\t * // pure linear thrust (2-arg form, unchanged behaviour):\n\t * ship.body.applyForce(0, -0.05);\n\t *\n\t * // off-centre push on a crate: the contact point at the top of the\n\t * // crate is above its centroid, so the same horizontal force now\n\t * // both translates AND tips the crate forward.\n\t * const topX = crate.pos.x + crate.width / 2;\n\t * const topY = crate.pos.y;\n\t * crate.body.applyForce(1.5, 0, topX, topY);\n\t *\n\t * // wind pushing on the top of a flag-pole: pole tilts, base stays put\n\t * // (only meaningful here if the base is anchored / static).\n\t * pole.body.applyForce(0.3, 0, pole.pos.x + pole.width / 2, pole.pos.y);\n\t */\n\tapplyForce(x, y, pointX, pointY) {\n\t\tthis.force.x += x;\n\t\tthis.force.y += y;\n\t\tif (typeof pointX === \"number\" && typeof pointY === \"number\") {\n\t\t\t// Lever arm r = point - centroid. Centroid is the geometric\n\t\t\t// center of the body's bounds.\n\t\t\tconst bounds = this.bounds;\n\t\t\tconst rx = pointX - bounds.centerX;\n\t\t\tconst ry = pointY - bounds.centerY;\n\t\t\t// Z-component of r × F.\n\t\t\tconst torque = rx * y - ry * x;\n\t\t\t// Guard against division by zero / negative inertia\n\t\t\t// (pseudoInertia is a user-tunable; nonsense values must\n\t\t\t// not crash the integrator).\n\t\t\tif (this.pseudoInertia > 0) {\n\t\t\t\tthis.angularVelocity += torque / this.pseudoInertia;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Apply an instantaneous angular impulse: `Δω = τ / pseudoInertia`.\n\t * The angular analog of {@link Body#applyImpulse} — bypasses the\n\t * lever-arm computation in {@link Body#applyForce} for the\n\t * \"just spin this up directly\" case (a power-up's intrinsic spin,\n\t * an explicit thruster, a knockback spin effect on hit).\n\t * @param {number} torque - angular impulse magnitude. Positive values\n\t *   produce clockwise rotation on screen (matching the Y-down canvas\n\t *   convention); negative values rotate counter-clockwise.\n\t * @example\n\t * // give a pickup a one-shot spin-up when collected:\n\t * pickup.body.applyTorque(80);\n\t *\n\t * // explosion knockback that both pushes and spins:\n\t * crate.body.applyImpulse(impulseX, impulseY);\n\t * crate.body.applyTorque((Math.random() - 0.5) * 100);\n\t */\n\tapplyTorque(torque) {\n\t\tif (this.pseudoInertia > 0) {\n\t\t\tthis.angularVelocity += torque / this.pseudoInertia;\n\t\t}\n\t}\n\n\t/**\n\t * Set angular velocity directly. Bypasses inertia — the value is the\n\t * actual rad/frame that integration will apply next step. Use this\n\t * for \"set and hold\" rotation (a coin that always spins at the same\n\t * rate); use {@link Body#applyTorque} for impulse-style spin-up.\n\t * @param {number} omega - target angular velocity (rad / frame)\n\t * @example\n\t * // make a fan blade spin at a fixed rate:\n\t * fan.body.setAngularVelocity(0.1);  // ~6°/frame\n\t */\n\tsetAngularVelocity(omega) {\n\t\tthis.angularVelocity = omega;\n\t}\n\n\t/**\n\t * Read current angular velocity (rad / frame).\n\t * @returns {number}\n\t * @example\n\t * // freeze rotation if the body is spinning too fast:\n\t * if (Math.abs(body.getAngularVelocity()) > 2) {\n\t *     body.setAngularVelocity(0);\n\t * }\n\t */\n\tgetAngularVelocity() {\n\t\treturn this.angularVelocity;\n\t}\n\n\t/**\n\t * Set absolute rotation angle (radians). Updates the body's `angle`\n\t * field and re-syncs the renderable's `currentTransform` immediately\n\t * so the visual rotation reflects the new value without waiting for\n\t * the next integration step.\n\t * @param {number} rad - target angle in radians\n\t * @example\n\t * // turret aims at the player every frame:\n\t * const dx = player.centerX - turret.centerX;\n\t * const dy = player.centerY - turret.centerY;\n\t * turret.body.setAngle(Math.atan2(dy, dx));\n\t */\n\tsetAngle(rad) {\n\t\tthis.angle = rad;\n\t\t// Force a transform sync so visual rotation tracks the new angle\n\t\t// even when angular velocity is zero (the integrator block in\n\t\t// `update()` is gated on `angularVelocity !== 0 || angle !== 0`,\n\t\t// so a setAngle(nonzero) call alone still triggers the sync next\n\t\t// frame; doing it here makes a manual setAngle visible without\n\t\t// requiring an `update()` call in between).\n\t\tthis._syncAngleTransform();\n\t}\n\n\t/**\n\t * Read absolute rotation angle (radians).\n\t * @returns {number}\n\t */\n\tgetAngle() {\n\t\treturn this.angle;\n\t}\n\n\t/**\n\t * Sync `this.angle` to the renderable's `currentTransform`. Pivot is\n\t * the body's bounds center (matches the matter adapter's rotation\n\t * pivot — see `MatterAdapter.syncFromPhysics`). Internal helper used\n\t * by both the per-step integrator and {@link Body#setAngle}.\n\t * @ignore\n\t */\n\t_syncAngleTransform() {\n\t\tconst t = this.ancestor?.currentTransform;\n\t\tif (!t) {\n\t\t\treturn;\n\t\t}\n\t\tconst bounds = this.bounds;\n\t\t// Pivot is body-local: subtract the renderable's pos so the\n\t\t// translate is in the local frame of the renderable.\n\t\tconst cx = bounds.centerX - this.ancestor.pos.x;\n\t\tconst cy = bounds.centerY - this.ancestor.pos.y;\n\t\tt.identity();\n\t\tif (this.angle !== 0) {\n\t\t\tif (cx !== 0 || cy !== 0) {\n\t\t\t\tt.translate(cx, cy);\n\t\t\t\tt.rotate(this.angle);\n\t\t\t\tt.translate(-cx, -cy);\n\t\t\t} else {\n\t\t\t\tt.rotate(this.angle);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * apply an instantaneous impulse to this body — a single-step velocity\n\t * change scaled by inverse mass (`dv = J / m`). Useful for one-shot\n\t * events like a cue strike, projectile launch, or knockback, where\n\t * mass should influence the resulting velocity change. Repeated calls\n\t * within a single update accumulate. Static bodies (mass 0) ignore\n\t * the call. Identical signature on the Matter adapter, where the\n\t * adapter integrates the impulse the same way (matter has no native\n\t * `applyImpulse`).\n\t * @param {number} x - impulse along the X axis\n\t * @param {number} y - impulse along the Y axis\n\t */\n\tapplyImpulse(x, y) {\n\t\tconst invMass = this.mass > 0 ? 1 / this.mass : 0;\n\t\tthis.vel.x += x * invMass;\n\t\tthis.vel.y += y * invMass;\n\t}\n\n\t/**\n\t * set this body's mass. Useful for variable-mass entities (projectiles\n\t * loaded with ammo, weight pickups, characters carrying objects).\n\t * Mass affects `applyImpulse` (via `dv = J / m`) and the proportional\n\t * push-out response in dynamic-dynamic collisions. A mass of 0 makes\n\t * the body inert to forces and impulses (without going static).\n\t * @param {number} m - new mass, non-negative\n\t */\n\tsetMass(m) {\n\t\tthis.mass = m;\n\t}\n\n\t/**\n\t * set this body's restitution / bounce factor. `0` = no bounce (energy\n\t * absorbed on contact); `1` = perfect elastic rebound; values in\n\t * between dampen the rebound. Applied by `Body.respondToCollision` —\n\t * see `BuiltinAdapter` docs for the cancellation math.\n\t *\n\t * Matches the `bodyDef.restitution` field name used at registration\n\t * time; the body-side property has historically been called `bounce`,\n\t * which is the canonical legacy name and is preserved.\n\t * @param {number} r - restitution factor, typically in [0, 1]\n\t */\n\tsetBounce(r) {\n\t\tthis.bounce = r;\n\t}\n\n\t/**\n\t * set this body's per-body gravity multiplier. `1` = world gravity\n\t * (default), `0` = ignore world gravity (e.g. flying enemy, underwater\n\t * float), `2` = 2× gravity (heavy-feel objects). Multiplied with the\n\t * world's `gravity.y` each frame inside `applyGravity`.\n\t * @param {number} scale - gravity scale factor\n\t */\n\tsetGravityScale(scale) {\n\t\tthis.gravityScale = scale;\n\t}\n\n\t/**\n\t * toggle this body between solid and sensor. Sensor bodies still emit\n\t * collision events (`onCollisionStart`, `onCollisionActive`,\n\t * `onCollisionEnd`) but the solver does not physically resolve the\n\t * contact — same semantics as Matter's `isSensor`. Useful for one-way\n\t * platforms, trigger zones, and ground-snap assists.\n\t * @param {boolean} [isSensor=true]\n\t */\n\tsetSensor(isSensor = true) {\n\t\tthis.isSensor = isSensor === true;\n\t}\n\n\t/**\n\t * add a collision shape to this body <br>\n\t * (note: me.Rect objects will be converted to me.Polygon before being added)\n\t *\n\t * A shape may carry its own collision settings, which refine the body's\n\t * (see {@link Body#collisionType} / {@link Body#collisionMask}). All are\n\t * optional, and their defaults are the behaviour a shape has always had:\n\t *\n\t * | property | default | effect |\n\t * |---|---|---|\n\t * | `collisionType` | inherit body | this shape's own category |\n\t * | `collisionMask` | inherit body | what this shape collides with |\n\t * | `isActive` | `true` | `false` removes the shape from collision entirely — no test, no contact, no events — without removing it from the body |\n\t * | `isTrigger` | `false` | `true` still collides and still fires events, but skips the position correction (per-shape {@link Body#isSensor}) |\n\t *\n\t * A shape can only NARROW what its body allows, never widen it: the body's\n\t * type/mask are still checked first, so a permissive shape on a restrictive\n\t * body collides with nothing.\n\t *\n\t * A trigger removes only ITSELF from the solver. If a solid shape on the\n\t * same body also overlaps, that contact still pushes out — the narrowphase\n\t * prefers a solid pair over a trigger pair, so the outcome does not depend\n\t * on the order shapes were added. `response.isTriggerContact` reports\n\t * whether a given contact exists only through trigger shapes.\n\t *\n\t * `isActive: false` removes the shape from the narrowphase AND from\n\t * raycasts. It does not shrink the body's `bounds` — those stay\n\t * conservative, so toggling a shape never resizes the body in the\n\t * broadphase — nor does it clear `hasDepth` for a `Box3d`.\n\t *\n\t * Supported by the builtin and planck adapters. The matter adapter filters\n\t * per body only and ignores these. Note that under planck, the body-wide\n\t * setters (`setCollisionType`, `setCollisionMask`, `setSensor`) write every\n\t * fixture and so overwrite per-shape values.\n\t * @param {Rect|Polygon|Line|Ellipse|Point|Point[]|Bounds|object} shape - a shape or JSON object\n\t * @returns {number} the shape array length\n\t * @example\n\t * // add a rectangle shape\n\t * this.body.addShape(new me.Rect(0, 0, image.width, image.height));\n\t * @example\n\t * // a character whose feet collide with terrain and whose torso is a hurtbox\n\t * const feet = new me.Rect(0, 24, 32, 8);\n\t * feet.collisionMask = me.collision.types.WORLD_SHAPE;\n\t * const torso = new me.Rect(0, 0, 32, 24);\n\t * torso.collisionMask = me.collision.types.ENEMY_OBJECT;\n\t * torso.isTrigger = true;      // detect hits, never get pushed by them\n\t * this.body.addShape(feet);\n\t * this.body.addShape(torso);\n\t * // add a shape from a JSON object\n\t * this.body.addShape(me.loader.getJSON(\"shapesdef\").banana);\n\t */\n\t/**\n\t * Give a stored shape its per-shape collision settings, carrying them over\n\t * from the source object when `addShape` converted it (a `Rect` and a\n\t * `Bounds` both become a `Polygon`, so anything set on the original would\n\t * otherwise be dropped on the floor).\n\t *\n\t * Every field is written explicitly, including the defaults. That is not\n\t * tidiness: converted shapes come from `polygonPool`, whose reset restores\n\t * only position and vertices — a recycled instance would otherwise arrive\n\t * still carrying the previous owner's `isTrigger` or mask and quietly\n\t * change how an unrelated body collides.\n\t *\n\t * Idempotent, because `removeShape` re-adds every surviving shape through\n\t * `addShape` and must not disturb their settings.\n\t * @param {object} stored - the shape as held in `this.shapes`\n\t * @param {object} [source] - the object passed to `addShape`; omitted when\n\t * the shape was built here from a pool and has no user-supplied original\n\t * @private\n\t */\n\t_initShapeCollision(stored, source) {\n\t\t// Stable identity for shape-level contact tracking (#1596).\n\t\t//\n\t\t// Stamped ONCE and never re-stamped: `removeShape()` splices the array\n\t\t// and re-adds every remaining shape through `addShape`, so an id derived\n\t\t// from array position (or re-generated here) would change under a caller\n\t\t// who removed an unrelated sibling, and the contact lifecycle would see\n\t\t// spurious End/Start pairs. `destroy()` clears it when the instance goes\n\t\t// back to a pool, so a recycled shape gets a fresh one.\n\t\tif (stored._contactId === undefined) {\n\t\t\tstored._contactId = ++shapeContactIdCounter;\n\t\t}\n\t\tif (source === undefined) {\n\t\t\t// no source at all: the shape was built here, straight from a pool,\n\t\t\t// so every field is reset to its default rather than inherited\n\t\t\tstored.collisionType = undefined;\n\t\t\tstored.collisionMask = undefined;\n\t\t\tstored.isTrigger = false;\n\t\t\tstored.isActive = true;\n\t\t\treturn;\n\t\t}\n\t\tif (stored !== source) {\n\t\t\t// CONVERTED (Rect / Bounds -> pooled Polygon). Assign the source's\n\t\t\t// values verbatim rather than falling back to what is on `stored`:\n\t\t\t// a recycled instance may still carry the previous owner's mask or\n\t\t\t// `isTrigger`, and `?? stored.x` would preserve exactly that.\n\t\t\t// `undefined` here is the correct result — it means \"inherit the\n\t\t\t// body's value\" at test time.\n\t\t\tstored.collisionType = source.collisionType;\n\t\t\tstored.collisionMask = source.collisionMask;\n\t\t}\n\t\t// The flags are normalized to real booleans on every path, so a stale\n\t\t// pooled `isTrigger: true` cannot survive, and re-running (which\n\t\t// `removeShape` does) is a no-op.\n\t\tstored.isTrigger = source.isTrigger === true;\n\t\tstored.isActive = source.isActive !== false;\n\t}\n\n\taddShape(shape) {\n\t\t// The object that ends up in `this.shapes` is not always the one passed\n\t\t// in — a Rect and a Bounds are converted to a Polygon. Track it so the\n\t\t// per-shape collision settings can be carried across and normalized\n\t\t// once, below.\n\t\tlet stored = shape;\n\t\tif (shape instanceof Rect) {\n\t\t\tconst poly = shape.toPolygon();\n\t\t\tstored = poly;\n\t\t\tthis.shapes.push(poly);\n\t\t\t// update the body bounds\n\t\t\tthis.bounds.add(poly.points);\n\t\t\tthis.bounds.translate(poly.pos);\n\t\t} else if (shape instanceof Bounds) {\n\t\t\t// Bounds doesn't carry a `toPolygon` method — it's a primitive\n\t\t\t// AABB that polygon.ts already depends on, so we keep the\n\t\t\t// conversion in the consumer to avoid a cyclic import. Same\n\t\t\t// rectangle-from-bounds geometry the old `Bounds.toPolygon`\n\t\t\t// produced.\n\t\t\tconst poly = polygonPool.get(shape.x, shape.y, [\n\t\t\t\tnew Vector2d(0, 0),\n\t\t\t\tnew Vector2d(shape.width, 0),\n\t\t\t\tnew Vector2d(shape.width, shape.height),\n\t\t\t\tnew Vector2d(0, shape.height),\n\t\t\t]);\n\t\t\tstored = poly;\n\t\t\tthis.shapes.push(poly);\n\t\t\tthis.bounds.add(poly.points);\n\t\t\tthis.bounds.translate(poly.pos);\n\t\t} else if (shape instanceof Ellipse) {\n\t\t\tif (!this.shapes.includes(shape)) {\n\t\t\t\t// see removeShape\n\t\t\t\tthis.shapes.push(shape);\n\t\t\t}\n\t\t\t// update the body bounds\n\t\t\tthis.bounds.addBounds(shape.getBounds());\n\t\t\t// use bounds position as ellipse position is center\n\t\t\tthis.bounds.translate(shape.getBounds().x, shape.getBounds().y);\n\t\t} else if (shape instanceof Polygon) {\n\t\t\tif (!this.shapes.includes(shape)) {\n\t\t\t\t// see removeShape\n\t\t\t\tthis.shapes.push(shape);\n\t\t\t}\n\t\t\t// update the body bounds\n\t\t\tthis.bounds.add(shape.points);\n\t\t\tthis.bounds.translate(shape.pos);\n\t\t} else if (shape instanceof Point) {\n\t\t\tif (!this.shapes.includes(shape)) {\n\t\t\t\t// see removeShape\n\t\t\t\tthis.shapes.push(shape);\n\t\t\t}\n\t\t\tthis.bounds.addPoint(shape);\n\t\t} else if (shape instanceof Box3d) {\n\t\t\tif (!this.shapes.includes(shape)) {\n\t\t\t\t// see removeShape\n\t\t\t\tthis.shapes.push(shape);\n\t\t\t}\n\t\t\t// Only the XY footprint goes into `this.bounds`. That bounds is a\n\t\t\t// 2D `Bounds` and feeds the broadphase pre-gate, debug draw and\n\t\t\t// `containsPoint` — all of which stay 2D. The depth extent lives\n\t\t\t// on the shape and is read by the narrowphase directly.\n\t\t\tthis.bounds.addBounds(shape.getBounds());\n\t\t\tthis.hasDepth = true;\n\t\t} else {\n\t\t\t// JSON object — `fromJSON` re-enters `addShape` per shape it\n\t\t\t// builds, so normalization happens there rather than here\n\t\t\tstored = undefined;\n\t\t\tthis.fromJSON(shape);\n\t\t}\n\n\t\tif (stored !== undefined) {\n\t\t\tthis._initShapeCollision(stored, shape);\n\t\t}\n\n\t\t// Refresh the bounds-derived `pseudoInertia` default whenever the\n\t\t// body's geometry changes. The user can still override it manually\n\t\t// after `addShape` if they need a stiffer / floppier body.\n\t\tthis._recomputePseudoInertia();\n\n\t\tif (typeof this.onBodyUpdate === \"function\") {\n\t\t\tthis.onBodyUpdate(this);\n\t\t}\n\n\t\t// return the length of the shape list\n\t\treturn this.shapes.length;\n\t}\n\n\t/**\n\t * Recompute the default `pseudoInertia` from the current bounds.\n\t * Uses the moment-of-inertia formula for a unit-mass rectangle —\n\t * `(width² + height²) / 12` — which gives a value that scales\n\t * sensibly with body size: a small body resists rotation less, a\n\t * large body more. Clamped to a minimum of 1 to keep divisions\n\t * well-defined even on degenerate 0-size bodies.\n\t * @ignore\n\t */\n\t_recomputePseudoInertia() {\n\t\tconst w = this.bounds.width;\n\t\tconst h = this.bounds.height;\n\t\tthis.pseudoInertia = Math.max(1, (w * w + h * h) / 12);\n\t}\n\n\t/**\n\t * set the body vertices to the given one\n\t * @param {Vector2d[]} vertices - an array of me.Vector2d points defining a convex hull\n\t * @param {number} [index=0] - the shape object for which to set the vertices\n\t * @param {boolean} [clear=true] - either to reset the body definition before adding the new vertices\n\t */\n\tsetVertices(vertices, index = 0, clear = true) {\n\t\tconst polygon = this.getShape(index);\n\t\tif (polygon instanceof Polygon) {\n\t\t\tpolygon.setShape(0, 0, vertices);\n\t\t} else {\n\t\t\t// this will replace any other non polygon shape type if defined\n\t\t\tthis.shapes[index] = polygonPool.get(0, 0, vertices);\n\t\t\t// `fromJSON` / `addVertices` reach `this.shapes` through here rather\n\t\t\t// than `addShape`, so the pooled instance needs the same\n\t\t\t// normalization or it keeps its previous owner's settings (#1590)\n\t\t\tthis._initShapeCollision(this.shapes[index], undefined);\n\t\t}\n\n\t\t// update the body bounds to take in account the new vertices\n\t\tthis.bounds.add(this.shapes[index].points, clear);\n\n\t\tif (typeof this.onBodyUpdate === \"function\") {\n\t\t\tthis.onBodyUpdate(this);\n\t\t}\n\t}\n\n\t/**\n\t * add the given vertices to the body shape\n\t * @param {Vector2d[]} vertices - an array of me.Vector2d points defining a convex hull\n\t * @param {number} [index=0] - the shape object for which to set the vertices\n\t */\n\taddVertices(vertices, index = 0) {\n\t\tthis.setVertices(vertices, index, false);\n\t}\n\n\t/**\n\t * add collision mesh based on a JSON object\n\t * (this will also apply any physic properties defined in the given JSON file)\n\t * @param {object} json - a JSON object as exported from a Physics Editor tool\n\t * @param {string} [id] - an optional shape identifier within the given the json object\n\t * @see https://www.codeandweb.com/physicseditor\n\t * @returns {number} how many shapes were added to the body\n\t * @example\n\t * // define the body based on the banana shape\n\t * this.body.fromJSON(me.loader.getJSON(\"shapesdef\").banana);\n\t * // or ...\n\t * this.body.fromJSON(me.loader.getJSON(\"shapesdef\"), \"banana\");\n\t */\n\tfromJSON(json, id) {\n\t\tlet data = json;\n\n\t\tif (typeof id !== \"undefined\") {\n\t\t\tdata = json[id];\n\t\t}\n\n\t\t// Physic Editor Format (https://www.codeandweb.com/physicseditor)\n\t\tif (typeof data === \"undefined\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Identifier (\" + id + \") undefined for the given JSON object)\",\n\t\t\t);\n\t\t}\n\n\t\tif (data.length) {\n\t\t\t// go through all shapes and add them to the body\n\t\t\tfor (let i = 0; i < data.length; i++) {\n\t\t\t\tthis.addVertices(data[i].shape, i);\n\t\t\t}\n\t\t\t// apply density, friction and bounce properties from the first shape\n\t\t\t// Note : how to manage different mass or friction for all different shapes?\n\t\t\tthis.mass = data[0].density || 0;\n\t\t\tthis.friction.set(data[0].friction || 0, data[0].friction || 0);\n\t\t\tthis.bounce = data[0].bounce || 0;\n\t\t}\n\n\t\t// return the amount of shapes added to the body\n\t\treturn data.length;\n\t}\n\n\t/**\n\t * return the collision shape at the given index\n\t * @param {number} [index=0] - the shape object at the specified index\n\t * @returns {Polygon|Line|Ellipse} shape a shape object if defined\n\t */\n\tgetShape(index) {\n\t\treturn this.shapes[index || 0];\n\t}\n\n\t/**\n\t * returns the AABB bounding box for this body\n\t * @returns {Bounds} bounding box Rectangle object\n\t */\n\tgetBounds() {\n\t\treturn this.bounds;\n\t}\n\n\t/**\n\t * remove the specified shape from the body shape list\n\t * @param {Polygon|Line|Ellipse|Box3d} shape - a shape object\n\t * @returns {number} the shape array length\n\t */\n\tremoveShape(shape) {\n\t\t// clear the current bounds\n\t\tthis.bounds.clear();\n\t\t// `addShape` only ever raises `hasDepth`, so clear it here and let the\n\t\t// re-add below raise it again if a Box3d is still in the list —\n\t\t// otherwise removing the last Box3d would leave the body claiming a\n\t\t// depth extent it no longer has.\n\t\tthis.hasDepth = false;\n\t\t// remove the shape from shape list\n\t\tremove(this.shapes, shape);\n\t\t// add everything left back\n\t\tfor (let s = 0; s < this.shapes.length; s++) {\n\t\t\tthis.addShape(this.shapes[s]);\n\t\t}\n\t\t// return the length of the shape list\n\t\treturn this.shapes.length;\n\t}\n\n\t/**\n\t * remove the shape at the given index from the body shape list\n\t * @param {number} index - the shape object at the specified index\n\t * @returns {number} the shape array length\n\t */\n\tremoveShapeAt(index) {\n\t\treturn this.removeShape(this.getShape(index));\n\t}\n\n\t/**\n\t * By default all physic bodies are able to collide with all other bodies, <br>\n\t * but it's also possible to specify 'collision filters' to provide a finer <br>\n\t * control over which body can collide with each other.\n\t * @see collision.types\n\t * @param {number} [bitmask = collision.types.ALL_OBJECT] - the collision mask\n\t * @example\n\t * // filter collision detection with collision shapes, enemies and collectables\n\t * body.setCollisionMask(me.collision.types.WORLD_SHAPE | me.collision.types.ENEMY_OBJECT | me.collision.types.COLLECTABLE_OBJECT);\n\t * ...\n\t * // disable collision detection with all other objects\n\t * body.setCollisionMask(me.collision.types.NO_OBJECT);\n\t */\n\tsetCollisionMask(bitmask = collision.types.ALL_OBJECT) {\n\t\tthis.collisionMask = bitmask;\n\t}\n\n\t/**\n\t * define the collision type of the body for collision filtering\n\t * @see collision.types\n\t * @param {number} type - the collision type\n\t * @example\n\t * // set the body collision type\n\t * body.collisionType = me.collision.types.PLAYER_OBJECT;\n\t */\n\tsetCollisionType(type) {\n\t\tif (typeof type !== \"undefined\") {\n\t\t\tif (typeof type === \"string\") {\n\t\t\t\tif (typeof collision.types[type] !== \"undefined\") {\n\t\t\t\t\tthis.collisionType = collision.types[type];\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\"Invalid value for the collisionType property\");\n\t\t\t\t}\n\t\t\t} else if (typeof type === \"number\") {\n\t\t\t\tthis.collisionType = type;\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"Invalid value for the collisionType property\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * the built-in function to solve the collision response\n\t * @param {ResponseObject} response - the collision response object\n\t * @see {@link ResponseObject}\n\t */\n\trespondToCollision(response) {\n\t\t// the overlap vector\n\t\tconst overlap = response.overlapV;\n\t\tconst overlapN = response.overlapN;\n\n\t\t// determine mass ratio: when both bodies are dynamic, split\n\t\t// the response proportionally to mass; otherwise apply full overlap\n\t\tconst other = response.a === this.ancestor ? response.b : response.a;\n\t\tlet ratio = 1;\n\t\tif (other && other.body && !other.body.isStatic) {\n\t\t\tconst totalMass = this.mass + other.body.mass;\n\t\t\tratio = totalMass > 0 ? other.body.mass / totalMass : 0.5;\n\t\t}\n\n\t\t// Z half of the minimum translation vector. Both are 0 for every\n\t\t// planar shape pair (see ResponseObject#overlapZ), so everything below\n\t\t// stays bit-for-bit identical for a 2D body — no branch required.\n\t\t//\n\t\t// Defaulted with `??` because this method is DUCK-TYPED, not typed:\n\t\t// it is public and callers legitimately hand it a plain object\n\t\t// literal carrying only `overlapV` / `overlapN`. Reading the new\n\t\t// fields off one of those yields `undefined`, and a single\n\t\t// `vel * undefined` poisons `projVel` to NaN — which fails the\n\t\t// `projVel > 0` gate and silently stops cancelling velocity into the\n\t\t// surface, while the position write turns z into NaN.\n\t\tconst overlapZ = response.overlapZ ?? 0;\n\t\tconst overlapNZ = response.overlapNZ ?? 0;\n\n\t\t// Move out of the other object shape\n\t\tthis.ancestor.pos.set(\n\t\t\tthis.ancestor.pos.x - overlap.x * ratio,\n\t\t\tthis.ancestor.pos.y - overlap.y * ratio,\n\t\t\tthis.ancestor.pos.z - overlapZ * ratio,\n\t\t);\n\n\t\t// cancel the velocity component along the collision normal\n\t\tconst projVel =\n\t\t\tthis.vel.x * overlapN.x + this.vel.y * overlapN.y + this.velZ * overlapNZ;\n\t\tif (projVel > 0) {\n\t\t\tif (this.bounce > 0) {\n\t\t\t\t// reflect velocity along normal with bounce damping\n\t\t\t\tthis.vel.x -= (1 + this.bounce) * projVel * ratio * overlapN.x;\n\t\t\t\tthis.vel.y -= (1 + this.bounce) * projVel * ratio * overlapN.y;\n\t\t\t\tthis.velZ -= (1 + this.bounce) * projVel * ratio * overlapNZ;\n\t\t\t} else {\n\t\t\t\t// remove the velocity component along the collision normal\n\t\t\t\tthis.vel.x -= projVel * ratio * overlapN.x;\n\t\t\t\tthis.vel.y -= projVel * ratio * overlapN.y;\n\t\t\t\tthis.velZ -= projVel * ratio * overlapNZ;\n\t\t\t}\n\t\t}\n\n\t\t// Update falling/jumping flags only for gravity-affected bodies.\n\t\t// `ignoreGravity` is the legacy opt-out; `gravityScale === 0` is the\n\t\t// portable equivalent (see Body#gravityScale). Either disables the\n\t\t// state machine — a hovering platform or a free-floating projectile\n\t\t// shouldn't be marked \"falling\" on a head-on side collision.\n\t\tif (overlap.y !== 0 && !this.ignoreGravity && this.gravityScale !== 0) {\n\t\t\t// cancel the falling an jumping flags if necessary\n\t\t\tconst dir = this.falling === true ? 1 : this.jumping === true ? -1 : 0;\n\t\t\tthis.falling = overlap.y >= dir;\n\t\t\tthis.jumping = overlap.y <= -dir;\n\t\t}\n\t}\n\n\t/**\n\t * The forEach() method executes a provided function once per body shape element. <br>\n\t * the callback function is invoked with three arguments: <br>\n\t *    - The current element being processed in the array <br>\n\t *    - The index of element in the array. <br>\n\t *    - The array forEach() was called upon. <br>\n\t * @param {Function} callback - function to execute on each element\n\t * @param {object} [thisArg] - value to use as this(i.e reference Object) when executing callback.\n\t * @example\n\t * // iterate through all shapes of the physic body\n\t * mySprite.body.forEach((shape) => {\n\t *    shape.doSomething();\n\t * });\n\t * mySprite.body.forEach((shape, index) => { ... });\n\t * mySprite.body.forEach((shape, index, array) => { ... });\n\t * mySprite.body.forEach((shape, index, array) => { ... }, thisArg);\n\t */\n\tforEach(callback, thisArg) {\n\t\tlet i = 0;\n\t\tconst shapes = this.shapes;\n\n\t\tconst len = shapes.length;\n\n\t\tif (typeof callback !== \"function\") {\n\t\t\tthrow new Error(callback + \" is not a function\");\n\t\t}\n\n\t\twhile (i < len) {\n\t\t\tcallback.call(thisArg ?? this, shapes[i], i, shapes);\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/**\n\t * Returns true if the any of the shape composing the body contains the given point.\n\t * @param {number|Vector2d} x -  x coordinate or a vector point to check\n\t * @param {number} [y] -  y coordinate\n\t * @returns {boolean} true if contains\n\t * @example\n\t * if (mySprite.body.contains(10, 10)) {\n\t *   // do something\n\t * }\n\t * // or\n\t * if (mySprite.body.contains(myVector2d)) {\n\t *   // do something\n\t * }\n\t */\n\tcontains(...args) {\n\t\tlet _x;\n\t\tlet _y;\n\n\t\tif (args.length === 2) {\n\t\t\t// x, y\n\t\t\t[_x, _y] = args;\n\t\t} else {\n\t\t\t// vector\n\t\t\t[_x, _y] = [args[0].x, args[0].y];\n\t\t}\n\n\t\tif (this.getBounds().contains(_x, _y)) {\n\t\t\t// cannot use forEach here as cannot break out with a return\n\t\t\tfor (let i = this.shapes.length, shape; i--, (shape = this.shapes[i]); ) {\n\t\t\t\tif (shape.contains(_x, _y)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Rotate this body (counter-clockwise) by the specified angle (in radians).\n\t * Unless specified the body will be rotated around its center point\n\t * @param {number} angle - The angle to rotate (in radians)\n\t * @param {Vector2d} [v=Body.getBounds().center] - an optional point to rotate around\n\t * @returns {Body} Reference to this object for method chaining\n\t */\n\trotate(angle, v = this.getBounds().center) {\n\t\tif (angle !== 0) {\n\t\t\tthis.bounds.clear();\n\t\t\tthis.forEach((shape) => {\n\t\t\t\tshape.rotate(angle, v);\n\t\t\t\tthis.bounds.addBounds(shape.getBounds());\n\t\t\t\t/*\n                if (!(shape instanceof Ellipse)) {\n                    // ellipse position is center\n                    this.bounds.translate(shape.pos);\n                }\n                */\n\t\t\t});\n\t\t\t/*\n            if (typeof this.onBodyUpdate === \"function\") {\n                this.onBodyUpdate(this);\n            }\n            */\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * cap the body velocity (body.maxVel property) to the specified value<br>\n\t * @param {number} x - max velocity on x axis\n\t * @param {number} y - max velocity on y axis\n\t * @param {number} [z] - max velocity on the depth axis; left unchanged when omitted, so existing two-argument calls keep their behaviour\n\t */\n\tsetMaxVelocity(x, y, z) {\n\t\tthis.maxVel.x = x;\n\t\tthis.maxVel.y = y;\n\t\tif (typeof z !== \"undefined\") {\n\t\t\tthis.maxVelZ = z;\n\t\t}\n\t}\n\n\t/**\n\t * set the body default friction\n\t * @param {number} x - horizontal friction\n\t * @param {number} y - vertical friction\n\t * @param {number} [z] - depth friction; left unchanged when omitted, so existing two-argument calls keep their behaviour\n\t */\n\tsetFriction(x = 0, y = 0, z) {\n\t\tthis.friction.x = x;\n\t\tthis.friction.y = y;\n\t\tif (typeof z !== \"undefined\") {\n\t\t\tthis.frictionZ = z;\n\t\t}\n\t}\n\n\t/**\n\t * Updates the parent's position as well as computes the new body's velocity based\n\t * on the values of force/friction.  Velocity changes are proportional to the\n\t * me.timer.tick value (which can be used to scale velocities).  The approach to moving the\n\t * parent renderable is to compute new values of the Body.vel property then add them to\n\t * the parent.pos value thus changing the position by the amount of Body.vel each time the\n\t * update call is made. <br>\n\t * Updates to Body.vel are bounded by maxVel (which defaults to viewport size if not set) <br>\n\t * At this time a call to Body.Update does not call the onBodyUpdate callback that is listed in the constructor arguments.\n\t * @protected\n\t * @param {number} dt - time since the last update in milliseconds.\n\t * @returns {boolean} true if resulting velocity is different than 0\n\t */\n\tupdate() {\n\t\t// apply timer.tick to delta time for linear interpolation (when enabled)\n\t\t// #761 add delta time in body update\n\t\tconst deltaTime = /* dt * */ timer.tick;\n\n\t\t// apply force if defined\n\t\tif (this.force.x !== 0) {\n\t\t\tthis.vel.x += this.force.x * deltaTime;\n\t\t}\n\t\tif (this.force.y !== 0) {\n\t\t\tthis.vel.y += this.force.y * deltaTime;\n\t\t}\n\t\tif (this.forceZ !== 0) {\n\t\t\tthis.velZ += this.forceZ * deltaTime;\n\t\t}\n\n\t\t// apply friction if defined\n\t\tif (this.friction.x > 0) {\n\t\t\tconst fx = this.friction.x * deltaTime;\n\t\t\tconst nx = this.vel.x + fx;\n\t\t\tconst x = this.vel.x - fx;\n\n\t\t\tthis.vel.x = nx < 0 ? nx : x > 0 ? x : 0;\n\t\t}\n\t\tif (this.friction.y > 0) {\n\t\t\tconst fy = this.friction.y * deltaTime;\n\t\t\tconst ny = this.vel.y + fy;\n\t\t\tconst y = this.vel.y - fy;\n\n\t\t\tthis.vel.y = ny < 0 ? ny : y > 0 ? y : 0;\n\t\t}\n\t\tif (this.frictionZ > 0) {\n\t\t\tconst fz = this.frictionZ * deltaTime;\n\t\t\tconst nz = this.velZ + fz;\n\t\t\tconst z = this.velZ - fz;\n\n\t\t\tthis.velZ = nz < 0 ? nz : z > 0 ? z : 0;\n\t\t}\n\n\t\t// cap velocity\n\t\tif (this.vel.y !== 0) {\n\t\t\tthis.vel.y = clamp(this.vel.y, -this.maxVel.y, this.maxVel.y);\n\t\t}\n\t\tif (this.vel.x !== 0) {\n\t\t\tthis.vel.x = clamp(this.vel.x, -this.maxVel.x, this.maxVel.x);\n\t\t}\n\t\tif (this.velZ !== 0) {\n\t\t\tthis.velZ = clamp(this.velZ, -this.maxVelZ, this.maxVelZ);\n\t\t}\n\n\t\t// check if falling / jumping\n\t\tthis.falling = this.vel.y * Math.sign(this.force.y) > 0;\n\t\tthis.jumping = this.falling ? false : this.jumping;\n\n\t\t// update the body ancestor position.\n\t\t//\n\t\t// Folded into a single `set` rather than `add(this.vel)` followed by a\n\t\t// separate z write: `pos` may be an ObservableVector3d, and a second\n\t\t// write would fire a second change notification every frame for every\n\t\t// body in the world. With `velZ` at its default 0 this computes\n\t\t// exactly what `add(this.vel)` did — `add` already resolves the\n\t\t// missing z of a Vector2d to 0.\n\t\tthis.ancestor.pos.set(\n\t\t\tthis.ancestor.pos.x + this.vel.x,\n\t\t\tthis.ancestor.pos.y + this.vel.y,\n\t\t\tthis.ancestor.pos.z + this.velZ,\n\t\t);\n\n\t\t// Angular integration — gated so bodies that never touch the\n\t\t// rotation API pay zero cost. The instant either `angle` or\n\t\t// `angularVelocity` becomes non-zero (via setAngle, setAngularVelocity,\n\t\t// applyTorque, or applyForce-with-offset) the branch fires until\n\t\t// both decay back to zero.\n\t\tif (this.angularVelocity !== 0 || this.angle !== 0) {\n\t\t\tif (this.angularDrag > 0 && this.angularVelocity !== 0) {\n\t\t\t\t// Exponential decay matching `frictionAir`'s linear behavior:\n\t\t\t\t// `omega *= (1 - drag)` each step. Negative drag is gated\n\t\t\t\t// out (treated as \"off\") so a typo can't accidentally\n\t\t\t\t// amplify rotation to infinity. Values > 1 still flip the\n\t\t\t\t// sign each step — a documented foot-gun, since clamping\n\t\t\t\t// would silently truncate a legitimate large damping value\n\t\t\t\t// like 1.5 (which makes physical sense as \"overdamped\").\n\t\t\t\tthis.angularVelocity *= 1 - this.angularDrag;\n\t\t\t}\n\t\t\tthis.angle += this.angularVelocity * deltaTime;\n\t\t\tthis._syncAngleTransform();\n\t\t}\n\n\t\t// returns true if vel is different from 0\n\t\treturn this.vel.x !== 0 || this.vel.y !== 0;\n\t}\n\n\t/**\n\t * Destroy function<br>\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// push back instance into object pool.\n\t\t//\n\t\t// Guarded because `destroy` is reachable twice — a game calling it\n\t\t// explicitly and then the container teardown calling it again, for\n\t\t// instance. `this.shapes.length = 0` at the end already makes the shape\n\t\t// loop below a no-op on the second pass, but these fields are set to\n\t\t// `undefined` rather than emptied, so re-releasing them threw\n\t\t// \"Instance is already in pool\" and aborted the teardown partway.\n\t\tif (this.bounds !== undefined) {\n\t\t\tboundsPool.release(this.bounds);\n\t\t}\n\t\tif (this.vel !== undefined) {\n\t\t\tvector2dPool.release(this.vel);\n\t\t}\n\t\tif (this.force !== undefined) {\n\t\t\tvector2dPool.release(this.force);\n\t\t}\n\t\tif (this.friction !== undefined) {\n\t\t\tvector2dPool.release(this.friction);\n\t\t}\n\t\tif (this.maxVel !== undefined) {\n\t\t\tvector2dPool.release(this.maxVel);\n\t\t}\n\t\tthis.shapes.forEach((shape) => {\n\t\t\t// Scrub the per-shape collision settings before the instance goes\n\t\t\t// back to a pool (#1590). The pools' reset restores only geometry,\n\t\t\t// so a recycled shape would otherwise arrive at its next owner\n\t\t\t// still carrying this body's mask or `isTrigger` — and for a shape\n\t\t\t// handed straight to `addShape` there is no separate source object\n\t\t\t// to correct it from. Cleaning up on release is the only point\n\t\t\t// where the engine still knows the values are stale.\n\t\t\tshape.collisionType = undefined;\n\t\t\tshape.collisionMask = undefined;\n\t\t\tshape.isTrigger = false;\n\t\t\tshape.isActive = true;\n\t\t\t// cleared too, so the next owner gets a fresh contact identity rather\n\t\t\t// than inheriting one that a live contact may still be keyed on (#1596)\n\t\t\tshape._contactId = undefined;\n\t\t\tif (shape instanceof Point) {\n\t\t\t\tpointPool.release(shape);\n\t\t\t} else if (shape instanceof Line) {\n\t\t\t\tlinePool.release(shape);\n\t\t\t} else if (shape instanceof Polygon) {\n\t\t\t\tpolygonPool.release(shape);\n\t\t\t} else if (shape instanceof Ellipse) {\n\t\t\t\t// Same trap the Box3d branch below documents: `addShape`\n\t\t\t\t// accepts an Ellipse, but without a branch here it reaches the\n\t\t\t\t// legacy `pool.push`, which THROWS for any class never\n\t\t\t\t// `pool.register`ed. `boundsPool.release` has already run by\n\t\t\t\t// then, so the throw aborts `destroy` partway and leaves the\n\t\t\t\t// body holding a recycled Bounds — which dies later somewhere\n\t\t\t\t// unrelated. Destroying a body with an ellipse collider threw\n\t\t\t\t// on every version that had one.\n\t\t\t\tellipsePool.release(shape);\n\t\t\t} else if (shape instanceof Box3d) {\n\t\t\t\t// Box3d has its own pool. Without this branch it falls through\n\t\t\t\t// to the legacy `pool.push`, which THROWS for any class that\n\t\t\t\t// was never `pool.register`ed — and because `boundsPool.release`\n\t\t\t\t// above has already run, the throw aborts `destroy` partway and\n\t\t\t\t// leaves the body holding a recycled Bounds. The next\n\t\t\t\t// broadphase insert then reads `bounds.min.x` off it and dies\n\t\t\t\t// somewhere completely unrelated.\n\t\t\t\tbox3dPool.release(shape);\n\t\t\t} else {\n\t\t\t\tpool.push(shape);\n\t\t\t}\n\t\t});\n\n\t\t// set to undefined\n\t\tthis.onBodyUpdate = undefined;\n\t\tthis.ancestor = undefined;\n\t\tthis.bounds = undefined;\n\t\tthis.vel = undefined;\n\t\tthis.force = undefined;\n\t\tthis.friction = undefined;\n\t\tthis.maxVel = undefined;\n\t\tthis.shapes.length = 0;\n\n\t\t// reset some variable to default\n\t\tthis.setStatic(false);\n\t}\n}\n","import { ObservablePoint } from \"../geometries/observablePoint.ts\";\nimport { Rect } from \"./../geometries/rectangle.ts\";\nimport { releaseAllPointerEvents } from \"./../input/input.ts\";\nimport { colorPool } from \"./../math/color.ts\";\nimport { clamp } from \"./../math/math.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { ObservableVector3d } from \"../math/observableVector3d.ts\";\nimport { vector3dPool } from \"../math/vector3d.ts\";\nimport { boundsPool } from \"./../physics/bounds.ts\";\nimport Body from \"./../physics/builtin/body.js\";\nimport pool from \"../system/legacy_pool.js\";\n\n/**\n * additional import for TypeScript\n * @import {Matrix3d} from \"../math/matrix3d.ts\";\n * @import Entity from \"./entity/entity.js\";\n * @import Container from \"./container.js\";\n * @import {Line} from \"./../geometries/line.ts\";\n * @import {Ellipse} from \"./../geometries/ellipse.ts\";\n * @import {Polygon} from \"../geometries/polygon.ts\";\n * @import {RoundRect} from \"./../geometries/roundrect.ts\";\n * @import Application from \"./../application/application.ts\";\n * @import CanvasRenderer from \"./../video/canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../video/webgl/webgl_renderer.js\";\n * @import ResponseObject from \"./../physics/response.js\";\n **/\n\n/**\n * A base class for renderable objects.\n * @category Game Objects\n */\nexport default class Renderable extends Rect {\n\t/**\n\t * @param {number} x - position of the renderable object (accessible through inherited pos.x property)\n\t * @param {number} y - position of the renderable object (accessible through inherited pos.y property)\n\t * @param {number} width - object width\n\t * @param {number} height - object height\n\t */\n\tconstructor(x, y, width, height) {\n\t\t// parent constructor\n\t\tsuper(x, y, width, height);\n\n\t\t/**\n\t\t * Position of the Renderable relative to its parent container\n\t\t * @public\n\t\t * @type {ObservableVector3d}\n\t\t */\n\t\tthis.pos = new ObservableVector3d(x, y, 0, () => {\n\t\t\tthis.updateBounds();\n\t\t\tthis.isDirty = true;\n\t\t});\n\n\t\t/**\n\t\t * The anchor point is used for attachment behavior, and/or when applying transformations.<br>\n\t\t * The coordinate system places the origin at the top left corner of the frame (0, 0) and (1, 1) means the bottom-right corner<br>\n\t\t * <img src=\"images/anchor_point.png\"/><br>\n\t\t * a Renderable's anchor point defaults to (0.5,0.5), which corresponds to the center position.<br>\n\t\t * <br>\n\t\t * <i><b>Note:</b> Object created through Tiled will have their anchorPoint set to (0, 0) to match Tiled Level editor implementation.\n\t\t * To specify a value through Tiled, use a json expression like `json:{\"x\":0.5,\"y\":0.5}`, or (since 19.9) a plain string preset such as `bottom`. </i>\n\t\t * <br>\n\t\t * At construction time, `settings.anchorPoint` also accepts the named presets\n\t\t * `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`,\n\t\t * `\"bottom-left\"`, `\"bottom-right\"` on every renderable that consumes it\n\t\t * (Sprite, Entity, Collectable, ImageLayer, Text, BitmapText, Sprite3d and subclasses).\n\t\t * @type {ObservablePoint}\n\t\t * @default <0.5,0.5>\n\t\t */\n\t\tthis.anchorPoint = new ObservablePoint(0.5, 0.5, () => {\n\t\t\tthis.updateBounds();\n\t\t\tthis.isDirty = true;\n\t\t});\n\n\t\tif (typeof this.currentTransform === \"undefined\") {\n\t\t\t/**\n\t\t\t * the renderable transformation matrix (4x4).\n\t\t\t * For standard 2D use, only the 2D components are used (rotate around Z, scale X/Y, translate X/Y).\n\t\t\t * For 3D use (e.g. Mesh), the full 4x4 matrix supports rotation around any axis,\n\t\t\t * 3D translation, and perspective projection.\n\t\t\t * Use the `rotate()`, `scale()`, and `translate()` methods rather than modifying this directly.\n\t\t\t * @type {Matrix3d}\n\t\t\t */\n\t\t\tthis.currentTransform = new Matrix3d();\n\t\t}\n\t\tthis.currentTransform.identity();\n\n\t\t/**\n\t\t * the renderable physics body — the handle returned by the\n\t\t * active {@link PhysicsAdapter}'s `addBody` (or constructed\n\t\t * imperatively via `new Body(...)`). Typed as the portable\n\t\t * {@link PhysicsBody} interface; cast to the adapter-specific\n\t\t * concrete type (`MatterAdapter.Body`, `BuiltinAdapter.Body`, or\n\t\t * the legacy {@link Body} class) to reach native fields.\n\t\t * @type {PhysicsBody}\n\t\t * @example\n\t\t *  // define a new Player Class\n\t\t *  class PlayerEntity extends me.Sprite {\n\t\t *      // constructor\n\t\t *      constructor(x, y, settings) {\n\t\t *          // call the parent constructor\n\t\t *          super(x, y , settings);\n\t\t *\n\t\t *          // define a basic walking animation\n\t\t *          this.addAnimation(\"walk\",  [...]);\n\t\t *          // define a standing animation (using the first frame)\n\t\t *          this.addAnimation(\"stand\",  [...]);\n\t\t *          // set the standing animation as default\n\t\t *          this.setCurrentAnimation(\"stand\");\n\t\t *\n\t\t *          // add a physic body\n\t\t *          this.body = new me.Body(this);\n\t\t *          // add a default collision shape\n\t\t *          this.body.addShape(new me.Rect(0, 0, this.width, this.height));\n\t\t *          // configure max speed, friction, and initial force to be applied\n\t\t *          this.body.setMaxVelocity(3, 15);\n\t\t *          this.body.setFriction(0.4, 0);\n\t\t *          this.body.force.set(3, 0);\n\t\t *          this.isKinematic = false;\n\t\t *\n\t\t *          // set the display to follow our position on both axis\n\t\t *          app.viewport.follow(this.pos, app.viewport.AXIS.BOTH);\n\t\t *      }\n\t\t *\n\t\t *      ...\n\t\t *\n\t\t * }\n\t\t */\n\t\tthis.body = undefined;\n\n\t\t/**\n\t\t * Declarative body definition consumed by the active\n\t\t * {@link PhysicsAdapter} when this renderable is added to a\n\t\t * container. **Adapter API only** — leave `undefined` if you\n\t\t * build a body imperatively via `this.body = new Body(...)`.\n\t\t *\n\t\t * When set, the parent container forwards it to\n\t\t * `world.adapter.addBody(this, this.bodyDef)`, which constructs\n\t\t * the underlying physics body (matter, builtin SAT, …) and\n\t\t * assigns the engine-portable wrapper to `this.body`. This is\n\t\t * the engine-portable path: the same `bodyDef` produces an\n\t\t * equivalent body under any adapter.\n\t\t *\n\t\t * Typical fields: `type` (`\"static\"`/`\"dynamic\"`/`\"kinematic\"`),\n\t\t * `shapes`, `collisionType`, `collisionMask`, `restitution`,\n\t\t * `frictionAir`, `density`, `gravityScale`, `isSensor`,\n\t\t * `maxVelocity`, `fixedRotation`. See {@link BodyDefinition}.\n\t\t * @type {object|undefined}\n\t\t * @default undefined\n\t\t */\n\t\tthis.bodyDef = undefined;\n\n\t\t/**\n\t\t * (G)ame (U)nique (Id)entifier\" <br>\n\t\t * a GUID will be allocated for any renderable object added <br>\n\t\t * to an object container (including the `app.world` container)\n\t\t * @type {string}\n\t\t */\n\t\tthis.GUID = undefined;\n\n\t\t/**\n\t\t * an event handler that is called when the renderable leave or enter a camera viewport\n\t\t * @type {Function}\n\t\t * @default undefined\n\t\t * @example\n\t\t * this.onVisibilityChange = function(inViewport) {\n\t\t *     if (inViewport === true) {\n\t\t *         console.log(\"object has entered the in a camera viewport!\");\n\t\t *     }\n\t\t * };\n\t\t */\n\t\tthis.onVisibilityChange = undefined;\n\n\t\t/**\n\t\t * Whether the renderable object will always update, even when outside of the viewport<br>\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.alwaysUpdate = false;\n\n\t\t/**\n\t\t * Whether to update this object when the game is paused.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.updateWhenPaused = false;\n\n\t\t/**\n\t\t * make the renderable object persistent over level changes<br>\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.isPersistent = false;\n\n\t\t/**\n\t\t * If true, this renderable will be rendered using screen coordinates,\n\t\t * as opposed to world coordinates. Use this, for example, to define UI elements.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.floating = false;\n\n\t\t/**\n\t\t * If true, this floating renderable will be rendered by all cameras\n\t\t * (e.g. background image layers). If false (default), floating elements\n\t\t * are only rendered by the default camera (e.g. UI/HUD elements).\n\t\t * Only applies to floating renderables in multi-camera setups.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.visibleInAllCameras = false;\n\n\t\t/**\n\t\t * When enabled, an object container will automatically apply\n\t\t * any defined transformation before calling the child draw method.\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t * @example\n\t\t * // enable \"automatic\" transformation when the object is activated\n\t\t * onActivateEvent: function () {\n\t\t *     // reset the transformation matrix\n\t\t *     this.currentTransform.identity();\n\t\t *     // ensure the anchor point is the renderable center\n\t\t *     this.anchorPoint.set(0.5, 0.5);\n\t\t *     // enable auto transform\n\t\t *     this.autoTransform = true;\n\t\t *     ....\n\t\t * }\n\t\t */\n\t\tthis.autoTransform = true;\n\n\t\t/**\n\t\t * Whether {@link Renderable#preDraw} applies the\n\t\t * {@link Renderable#anchorPoint} offset to the renderer transform.\n\t\t *\n\t\t * When `true` (the default), the renderable is shifted by\n\t\t * `-anchorPoint × (width, height)` so its anchor — not its top-left\n\t\t * corner — aligns with its position. Correct for sprites and other 2D\n\t\t * renderables.\n\t\t *\n\t\t * Set to `false` for a renderable that emits its own final world\n\t\t * coordinates and takes its origin from geometry rather than a bounds\n\t\t * box — e.g. a {@link Mesh} on the `Camera3d` world-space path (a 3D\n\t\t * mesh is positioned by its transform and has no anchor). The WebGL mesh\n\t\t * batcher reuses this same renderer transform as its **view matrix**,\n\t\t * so applying the normalized anchor there would shift every mesh by\n\t\t * half its OWN bounds box; because scene meshes size that box per node,\n\t\t * props and the platforms they rest on would drift apart and overlap.\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t * @see Mesh#preDraw\n\t\t */\n\t\tthis.applyAnchorTransform = true;\n\n\t\t/**\n\t\t * Define the renderable opacity<br>\n\t\t * Set to zero if you do not wish an object to be drawn\n\t\t * @see Renderable#setOpacity\n\t\t * @see Renderable#getOpacity\n\t\t * @type {number}\n\t\t * @default 1.0\n\t\t */\n\t\tthis.alpha = 1.0;\n\n\t\t/**\n\t\t * a reference to the parent object that contains this renderable\n\t\t * @type {Container|Entity}\n\t\t * @default undefined\n\t\t */\n\t\tthis.ancestor = undefined;\n\n\t\t/**\n\t\t * A mask limits rendering elements to the shape and position of the given mask object.\n\t\t * So, if the renderable is larger than the mask, only the intersecting part of the renderable will be visible.\n\t\t * @type {Rect|RoundRect|Polygon|Line|Ellipse}\n\t\t * @default undefined\n\t\t * @example\n\t\t * // apply a mask in the shape of a Star\n\t\t * myNPCSprite.mask = new me.Polygon(myNPCSprite.width / 2, 0, [\n\t\t *    // draw a star\n\t\t *    {x: 0, y: 0},\n\t\t *    {x: 14, y: 30},\n\t\t *    {x: 47, y: 35},\n\t\t *    {x: 23, y: 57},\n\t\t *    {x: 44, y: 90},\n\t\t *    {x: 0, y: 62},\n\t\t *    {x: -44, y: 90},\n\t\t *    {x: -23, y: 57},\n\t\t *    {x: -47, y: 35},\n\t\t *    {x: -14, y: 30}\n\t\t * ]);\n\t\t */\n\t\tthis.mask = undefined;\n\n\t\t/**\n\t\t * the list of post-processing shader effects applied to this renderable\n\t\t * (GPU backends — WebGL and WebGPU). Effects are applied in order. Use\n\t\t * {@link addPostEffect}, {@link getPostEffect}, and\n\t\t * {@link removePostEffect} to manage effects, or assign directly.\n\t\t * On the Canvas renderer effects stay inert (the scene keeps rendering\n\t\t * un-effected).\n\t\t * @type {Array<GLShader|ShaderEffect>}\n\t\t * @default []\n\t\t * @example\n\t\t * // add effects via helper methods\n\t\t * mySprite.addPostEffect(new DesaturateEffect(renderer));\n\t\t * mySprite.addPostEffect(new VignetteEffect(renderer));\n\t\t * @example\n\t\t * // assign directly\n\t\t * mySprite.postEffects = [new SepiaEffect(renderer), new VignetteEffect(renderer)];\n\t\t */\n\t\tthis.postEffects = [];\n\n\t\t/**\n\t\t * whether this renderable manages its own FBO lifecycle (e.g. Camera2d)\n\t\t * @ignore\n\t\t */\n\t\tthis._postEffectManaged = false;\n\n\t\t/**\n\t\t * the blend mode to be applied to this renderable (see renderer setBlendMode for available blend mode)\n\t\t * @type {string}\n\t\t * @default \"normal\"\n\t\t * @see CanvasRenderer#setBlendMode\n\t\t * @see WebGLRenderer#setBlendMode\n\t\t */\n\t\tthis.blendMode = \"normal\";\n\n\t\t/**\n\t\t * The name of the renderable\n\t\t * @type {string}\n\t\t * @default \"\"\n\t\t */\n\t\tthis.name = \"\";\n\n\t\t/**\n\t\t * to identify the object as a renderable object\n\t\t * @ignore\n\t\t */\n\t\tthis.isRenderable = true;\n\n\t\t/**\n\t\t * If true then physic collision and input events will not impact this renderable\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.isKinematic = true;\n\n\t\t/**\n\t\t * when true the renderable will be redrawn during the next update cycle\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.isDirty = true;\n\n\t\t/**\n\t\t * cache the absolute position of the renderable\n\t\t * @ignore\n\t\t */\n\t\tthis._absPos = undefined;\n\n\t\t/**\n\t\t * keep track of when we flip\n\t\t * @ignore\n\t\t */\n\t\tthis._flip = {\n\t\t\tx: false,\n\t\t\ty: false,\n\t\t};\n\n\t\t/**\n\t\t * viewport flag\n\t\t * @ignore\n\t\t */\n\t\tthis._inViewport = false;\n\n\t\t/**\n\t\t * cache value for the parentApp\n\t\t * @ignore\n\t\t */\n\t\tthis._parentApp = undefined;\n\n\t\t/**\n\t\t * renderable cache tint value used by the getter/setter\n\t\t * @ignore\n\t\t */\n\t\tthis._tint = colorPool.get(255, 255, 255, 1.0);\n\n\t\t// ensure it's fully opaque by default\n\t\tthis.setOpacity(1.0);\n\t}\n\n\t/**\n\t * returns the parent application (or game) to which this renderable is attached to\n\t * @return {Application} the parent application or undefined if not attached to any container/app\n\t */\n\tget parentApp() {\n\t\tif (!this._parentApp && this.ancestor) {\n\t\t\t// the `app` property is only defined in the world \"root\" container\n\t\t\tthis._parentApp = this.ancestor.getRootAncestor().app;\n\t\t}\n\t\treturn this._parentApp;\n\t}\n\n\t/**\n\t * Whether the renderable object is floating (i.e. used screen coordinates), or contained in a floating parent container\n\t * @see Renderable#floating\n\t * @type {boolean}\n\t */\n\tget isFloating() {\n\t\treturn (\n\t\t\tthis.floating === true ||\n\t\t\t(this.ancestor && this.ancestor.isFloating === true)\n\t\t);\n\t}\n\n\t/**\n\t * define a tint for this renderable. a (255, 255, 255) r, g, b value will remove the tint effect.\n\t * @type {Color}\n\t * @default (255, 255, 255)\n\t * @example\n\t * // add a red tint to this renderable\n\t * this.tint.setColor(255, 128, 128);\n\t * // remove the tint\n\t * this.tint.setColor(255, 255, 255);\n\t */\n\tget tint() {\n\t\treturn this._tint;\n\t}\n\tset tint(value) {\n\t\tthis._tint.copy(value);\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * the depth of this renderable on the z axis\n\t * @type {number}\n\t */\n\tget depth() {\n\t\treturn this.pos.z;\n\t}\n\tset depth(value) {\n\t\tthis.pos.z = value;\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Whether the renderable object is visible and within the viewport\n\t * @type {boolean}\n\t * @default false\n\t */\n\tget inViewport() {\n\t\treturn this._inViewport;\n\t}\n\tset inViewport(value) {\n\t\tif (this._inViewport !== value) {\n\t\t\tthis._inViewport = value;\n\t\t\tif (typeof this.onVisibilityChange === \"function\") {\n\t\t\t\tthis.onVisibilityChange.call(this, value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @deprecated since 19.2.0 — use {@link addPostEffect} / {@link getPostEffect} / {@link removePostEffect} instead\n\t * @type {GLShader|ShaderEffect|undefined}\n\t */\n\tget shader() {\n\t\treturn this.postEffects[0];\n\t}\n\n\tset shader(value) {\n\t\t// destroy existing effects before replacing\n\t\tfor (const effect of this.postEffects) {\n\t\t\t// `effect.shared` opts a shader out of auto-destroy — it is reused\n\t\t\t// across renderables, so freeing it here would break the others.\n\t\t\tif (typeof effect.destroy === \"function\" && !effect.shared) {\n\t\t\t\teffect.destroy();\n\t\t\t}\n\t\t}\n\t\t// null clears like undefined: loader.getShader stores null for a\n\t\t// shader asset the active backend cannot compile (e.g. a GLSL-only\n\t\t// program pair under WebGPU) — assigning that must degrade to \"no\n\t\t// shader\", not enqueue a null the effect filter would trip over\n\t\tif (value == null) {\n\t\t\tthis.postEffects.length = 0;\n\t\t} else {\n\t\t\tthis.postEffects = [value];\n\t\t}\n\t}\n\n\t/**\n\t * Add a post-processing shader effect to this renderable.\n\t * @param {GLShader|ShaderEffect} effect - the effect to add\n\t * @returns {GLShader|ShaderEffect} the added effect\n\t * @example\n\t * mySprite.addPostEffect(new DesaturateEffect(renderer));\n\t */\n\taddPostEffect(effect) {\n\t\tthis.postEffects.push(effect);\n\t\treturn effect;\n\t}\n\n\t/**\n\t * Get post-processing shader effects.\n\t * When called with a class, returns the first effect matching the given class.\n\t * When called without arguments, returns the full effects array.\n\t * @param {Function} [effectClass] - the effect class to search for\n\t * @returns {GLShader|ShaderEffect|Array|undefined} the matching effect, the effects array, or undefined\n\t * @example\n\t * const desat = sprite.getPostEffect(DesaturateEffect);\n\t * const allEffects = sprite.getPostEffect();\n\t */\n\tgetPostEffect(effectClass) {\n\t\tif (typeof effectClass === \"undefined\") {\n\t\t\treturn this.postEffects;\n\t\t}\n\t\treturn this.postEffects.find((fx) => {\n\t\t\treturn fx instanceof effectClass;\n\t\t});\n\t}\n\n\t/**\n\t * Remove all post-processing shader effects.\n\t * @example\n\t * sprite.clearPostEffects();\n\t */\n\tclearPostEffects() {\n\t\tfor (const effect of this.postEffects) {\n\t\t\t// `effect.shared` opts a shader out of auto-destroy — it is reused\n\t\t\t// across renderables, so freeing it here would break the others.\n\t\t\tif (typeof effect.destroy === \"function\" && !effect.shared) {\n\t\t\t\teffect.destroy();\n\t\t\t}\n\t\t}\n\t\tthis.postEffects.length = 0;\n\t}\n\n\t/**\n\t * Remove a specific post-processing shader effect.\n\t * @param {GLShader|ShaderEffect} effect - the effect to remove\n\t * @example\n\t * sprite.removePostEffect(effect);\n\t */\n\tremovePostEffect(effect) {\n\t\tconst idx = this.postEffects.indexOf(effect);\n\t\tif (idx !== -1) {\n\t\t\tthis.postEffects.splice(idx, 1);\n\t\t\t// `effect.shared` opts a shader out of auto-destroy — it is reused\n\t\t\t// across renderables, so freeing it here would break the others.\n\t\t\tif (typeof effect.destroy === \"function\" && !effect.shared) {\n\t\t\t\teffect.destroy();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * returns true if this renderable is flipped on the horizontal axis\n\t * @public\n\t * @see Renderable#flipX\n\t * @type {boolean}\n\t */\n\tget isFlippedX() {\n\t\treturn this._flip.x === true;\n\t}\n\n\t/**\n\t * returns true if this renderable is flipped on the vertical axis\n\t * @public\n\t * @see Renderable#flipY\n\t * @type {boolean}\n\t */\n\tget isFlippedY() {\n\t\treturn this._flip.y === true;\n\t}\n\n\t/**\n\t * returns the bounding box for this renderable\n\t * @returns {Bounds} bounding box Rectangle object\n\t */\n\tgetBounds() {\n\t\tif (typeof this._bounds === \"undefined\") {\n\t\t\tsuper.getBounds();\n\t\t\tif (this.isFinite()) {\n\t\t\t\tthis.updateBounds();\n\t\t\t} else {\n\t\t\t\t// e.g. containers or game world can have infinite size\n\t\t\t\tthis._bounds.setMinMax(this.pos.x, this.pos.y, this.width, this.height);\n\t\t\t}\n\t\t}\n\t\treturn this._bounds;\n\t}\n\n\t/**\n\t * get the renderable alpha channel value<br>\n\t * @returns {number} current opacity value between 0 and 1\n\t */\n\tgetOpacity() {\n\t\treturn this.alpha;\n\t}\n\n\t/**\n\t * set the renderable alpha channel value<br>\n\t * @param {number} alpha - opacity value between 0.0 and 1.0\n\t */\n\tsetOpacity(alpha) {\n\t\tif (typeof alpha === \"number\") {\n\t\t\tthis.alpha = clamp(alpha, 0.0, 1.0);\n\t\t\t// Set to 1 if alpha is NaN\n\t\t\tif (isNaN(this.alpha)) {\n\t\t\t\tthis.alpha = 1.0;\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * flip the renderable on the horizontal axis (around the center of the renderable)\n\t * @see Matrix3d#scaleX\n\t * @param {boolean} [flip=true] - `true` to flip this renderable.\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\tflipX(flip = true) {\n\t\tthis._flip.x = !!flip;\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * flip the renderable on the vertical axis (around the center of the renderable)\n\t * @see Matrix3d#scaleY\n\t * @param {boolean} [flip=true] - `true` to flip this renderable.\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\tflipY(flip = true) {\n\t\tthis._flip.y = !!flip;\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * multiply the renderable currentTransform with the given matrix\n\t * @see Renderable#currentTransform\n\t * @param {Matrix2d|Matrix3d} m - the transformation matrix\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\ttransform(m) {\n\t\tthis.currentTransform.multiply(m);\n\t\tthis.updateBounds();\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the angle to the specified target\n\t * @param {Renderable|Vector2d|Vector3d} target\n\t * @returns {number} angle in radians\n\t */\n\tangleTo(target) {\n\t\tconst a = this.getBounds();\n\t\tlet ax;\n\t\tlet ay;\n\n\t\tif (target instanceof Renderable) {\n\t\t\tconst b = target.getBounds();\n\t\t\tax = b.centerX - a.centerX;\n\t\t\tay = b.centerY - a.centerY;\n\t\t} else {\n\t\t\t// vector object\n\t\t\tax = target.x - a.centerX;\n\t\t\tay = target.y - a.centerY;\n\t\t}\n\n\t\treturn Math.atan2(ay, ax);\n\t}\n\n\t/**\n\t * return the distance to the specified target\n\t * @param {Renderable|Vector2d|Vector3d} target\n\t * @returns {number} distance\n\t */\n\tdistanceTo(target) {\n\t\tconst a = this.getBounds();\n\t\tlet dx;\n\t\tlet dy;\n\n\t\tif (target instanceof Renderable) {\n\t\t\tconst b = target.getBounds();\n\t\t\tdx = a.centerX - b.centerX;\n\t\t\tdy = a.centerY - b.centerY;\n\t\t} else {\n\t\t\t// vector object\n\t\t\tdx = a.centerX - target.x;\n\t\t\tdy = a.centerY - target.y;\n\t\t}\n\n\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t}\n\n\t/**\n\t * Rotate this renderable towards the given target.\n\t * @param {Renderable|Vector2d|Vector3d} target - the renderable or position to look at\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\tlookAt(target) {\n\t\tlet position;\n\n\t\tif (target instanceof Renderable) {\n\t\t\tposition = target.pos;\n\t\t} else {\n\t\t\tposition = target;\n\t\t}\n\n\t\tconst angle = this.angleTo(position);\n\n\t\tthis.rotate(angle);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Rotate this renderable by the specified angle (in radians).\n\t * When called with just an angle, rotates around the Z axis (2D rotation).\n\t * When called with an angle and a Vector3d axis, rotates around that axis in 3D.\n\t * @param {number} angle - The angle to rotate (in radians)\n\t * @param {Vector3d} [v] - the axis to rotate around (defaults to Z axis for 2D)\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\trotate(angle, v) {\n\t\tif (angle !== 0) {\n\t\t\tthis.currentTransform.rotate(angle, v);\n\t\t\tthis.updateBounds();\n\t\t\tthis.isDirty = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * scale the renderable around his anchor point.  Scaling actually applies changes\n\t * to the currentTransform member which is used by the renderer to scale the object\n\t * when rendering.  It does not scale the object itself.  For example if the renderable\n\t * is an image, the image.width and image.height properties are unaltered but the currentTransform\n\t * member will be changed.\n\t * @param {number} x - a number representing the abscissa of the scaling vector.\n\t * @param {number} [y=x] - a number representing the ordinate of the scaling vector.\n\t * @param {number} [z=1] - a number representing the depth of the scaling vector.\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\tscale(x, y = x, z = 1) {\n\t\tthis.currentTransform.scale(x, y, z);\n\t\tthis.updateBounds();\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * scale the renderable around his anchor point\n\t * @param {Vector2d} v - scaling vector\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\tscaleV(v) {\n\t\tthis.scale(v.x, v.y);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Translate the renderable by the specified offset.\n\t * @param {number} x - x offset\n\t * @param {number} [y=0] - y offset\n\t * @param {number} [z=0] - z offset\n\t * @returns {Renderable} Reference to this object for method chaining\n\t */\n\ttranslate(x, y = 0, z = 0) {\n\t\tthis.currentTransform.translate(x, y, z);\n\t\tthis.updateBounds();\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * update function (automatically called by melonJS).\n\t * @param {number} dt - time since the last update in milliseconds.\n\t * @returns {boolean} true if the renderable is dirty\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tupdate(dt) {\n\t\treturn this.isDirty;\n\t}\n\n\t/**\n\t * update the bounding box for this shape.\n\t * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates\n\t * @returns {Bounds} this shape bounding box Rectangle object\n\t */\n\tupdateBounds(absolute = true) {\n\t\tif (this.isRenderable) {\n\t\t\tconst bounds = this.getBounds();\n\n\t\t\tbounds.clear();\n\t\t\tif (this.autoTransform === true && !this.currentTransform.isIdentity()) {\n\t\t\t\t// temporarily translate the matrix based on the anchor point\n\t\t\t\tthis.currentTransform.translate(\n\t\t\t\t\t-this.width * this.anchorPoint.x,\n\t\t\t\t\t-this.height * this.anchorPoint.y,\n\t\t\t\t);\n\t\t\t\tbounds.addFrame(0, 0, this.width, this.height, this.currentTransform);\n\t\t\t\tthis.currentTransform.translate(\n\t\t\t\t\tthis.width * this.anchorPoint.x,\n\t\t\t\t\tthis.height * this.anchorPoint.y,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tbounds.addFrame(0, 0, this.width, this.height);\n\t\t\t\t// translate the bounds based on the anchor point\n\t\t\t\tbounds.translate(\n\t\t\t\t\t-this.width * this.anchorPoint.x,\n\t\t\t\t\t-this.height * this.anchorPoint.y,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (absolute === true) {\n\t\t\t\tconst absPos = this.getAbsolutePosition();\n\t\t\t\tbounds.centerOn(\n\t\t\t\t\tabsPos.x + bounds.x + bounds.width / 2,\n\t\t\t\t\tabsPos.y + bounds.y + bounds.height / 2,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn bounds;\n\t\t} else {\n\t\t\t// manage the case where updateBounds is called\n\t\t\t// before the object being yet properly initialized\n\t\t\treturn super.updateBounds(absolute);\n\t\t}\n\t}\n\n\t/**\n\t * return the renderable absolute position in the game world. The\n\t * returned vector is a {@link Vector3d} so the z component is summed\n\t * across the ancestor chain too — important for {@link Camera3d}'s\n\t * frustum culling, which previously read `obj.depth` (local\n\t * `pos.z`) and mis-culled children nested under a container with\n\t * its own non-zero depth.\n\t * @returns {Vector3d}\n\t */\n\tgetAbsolutePosition() {\n\t\tif (typeof this._absPos === \"undefined\") {\n\t\t\tthis._absPos = vector3dPool.get();\n\t\t}\n\t\t// TODO: cache the absolute position and invalidate when pos or ancestor changes\n\t\tthis._absPos.set(this.pos.x, this.pos.y, this.pos.z);\n\t\tif (this.ancestor && this.floating !== true) {\n\t\t\tthis._absPos.add(this.ancestor.getAbsolutePosition());\n\t\t}\n\t\treturn this._absPos;\n\t}\n\n\t/**\n\t * Prepare the rendering context before drawing (automatically called by melonJS).\n\t * This will apply any defined transforms, anchor point, tint or blend mode and translate the context accordingly to this renderable position.\n\t * @see Renderable#draw\n\t * @see Renderable#postDraw\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer object\n\t */\n\tpreDraw(renderer) {\n\t\t// The anchor offset of an `Infinity`-sized renderable (a Container's\n\t\t// default size, ColorLayer) is `NaN` (`Infinity * 0`) or `±Infinity` —\n\t\t// one `translate()` by that writes NaN into the transform's z column\n\t\t// (the sub-pixel snap only repairs tx/ty), and every batched vertex\n\t\t// recorded under it inherits z = NaN and is clipped away by the GPU\n\t\t// backends. The 2D canvas context ignores non-finite transforms per\n\t\t// spec; match it by treating the offset as 0 — an anchor point has no\n\t\t// meaningful offset on an infinite size anyway.\n\t\tconst ax = Number.isFinite(this.width)\n\t\t\t? this.width * this.anchorPoint.x\n\t\t\t: 0;\n\t\tconst ay = Number.isFinite(this.height)\n\t\t\t? this.height * this.anchorPoint.y\n\t\t\t: 0;\n\n\t\t// save renderer context\n\t\trenderer.save();\n\n\t\t// apply the defined alpha value\n\t\trenderer.setGlobalAlpha(renderer.globalAlpha() * this.getOpacity());\n\n\t\t// apply flip\n\t\tif (this._flip.x || this._flip.y) {\n\t\t\tconst dx = this._flip.x ? this.centerX - ax : 0;\n\t\t\tconst dy = this._flip.y ? this.centerY - ay : 0;\n\n\t\t\trenderer.translate(dx, dy);\n\t\t\trenderer.scale(this._flip.x ? -1 : 1, this._flip.y ? -1 : 1);\n\t\t\trenderer.translate(-dx, -dy);\n\t\t}\n\n\t\t// apply stencil mask if defined\n\t\tif (this.mask) {\n\t\t\trenderer.translate(this.pos.x, this.pos.y);\n\t\t\trenderer.setMask(this.mask);\n\t\t\trenderer.translate(-this.pos.x, -this.pos.y);\n\t\t}\n\n\t\t// delegate post-effect setup to the renderer.\n\t\t// Camera manages its own FBO lifecycle in draw().\n\t\tif (!this._postEffectManaged) {\n\t\t\trenderer.beginPostEffect(this);\n\t\t}\n\n\t\tif (this.autoTransform === true && !this.currentTransform.isIdentity()) {\n\t\t\t// apply the renderable transformation matrix\n\t\t\trenderer.translate(this.pos.x, this.pos.y);\n\t\t\trenderer.transform(this.currentTransform);\n\t\t\trenderer.translate(-this.pos.x, -this.pos.y);\n\t\t}\n\n\t\t// offset by the anchor point — unless this renderable manages its own\n\t\t// world placement (see Renderable#applyAnchorTransform), in which case\n\t\t// the normalized anchor offset must not leak into the renderer/view\n\t\t// transform.\n\t\tif (this.applyAnchorTransform !== false) {\n\t\t\trenderer.translate(-ax, -ay);\n\t\t}\n\n\t\t// apply the current tint and opacity\n\t\trenderer.setTint(this.tint, this.getOpacity());\n\n\t\t// forward depth to the renderer — pushed into the WebGL vertex\n\t\t// stream as the `z` component by the batchers. No visible effect\n\t\t// under the default ortho projection; required for perspective\n\t\t// (Camera3d) to scale and parallax this renderable by distance.\n\t\trenderer.setDepth(this.depth);\n\n\t\t// apply blending if different from \"normal\"\n\t\tif (this.blendMode !== renderer.getBlendMode()) {\n\t\t\trenderer.setBlendMode(this.blendMode);\n\t\t}\n\t}\n\n\t/**\n\t * Draw this renderable (automatically called by melonJS).\n\t * All draw operations for renderable are made respectively\n\t * to the position or transforms set or applied by the preDraw method.\n\t * The main draw loop will first call preDraw() to prepare the context for drawing the renderable,\n\t * then draw() to draw the renderable, and finally postDraw() to clear the context.\n\t * If you override this method, be mindful about the drawing logic; for example if you draw a shape\n\t * from the draw method, you should make sure that your draw it at the 0, 0 coordinates.\n\t * @see Renderable#preDraw\n\t * @see Renderable#postDraw\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tdraw(renderer, viewport) {\n\t\t// empty one !\n\t}\n\n\t/**\n\t * restore the rendering context after drawing (automatically called by melonJS).\n\t * @see Renderable#preDraw\n\t * @see Renderable#draw\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer object\n\t */\n\tpostDraw(renderer) {\n\t\t// remove the previously applied tint\n\t\trenderer.clearTint();\n\n\t\t// clear the mask if set\n\t\tif (this.mask) {\n\t\t\trenderer.clearMask();\n\t\t}\n\n\t\t// finalize post-effects (renderer decides what to do)\n\t\tif (!this._postEffectManaged) {\n\t\t\trenderer.endPostEffect(this);\n\t\t}\n\n\t\t// restore the context (also restores customShader)\n\t\trenderer.restore();\n\n\t\t// reset the dirty flag\n\t\tthis.isDirty = false;\n\t}\n\n\t/**\n\t * Legacy collision callback — fires every frame this renderable body is\n\t * overlapping another body. Kept for backward compatibility with code\n\t * written against pre-19.5 melonJS; semantics are unchanged from the\n\t * 19.4 contract.\n\t *\n\t * **NOTE — `onCollision` is NOT equivalent to {@link Renderable.onCollisionActive}.**\n\t * The two handlers exist side by side and have intentionally different\n\t * contracts:\n\t *\n\t * | | `onCollision` (legacy) | `onCollisionActive` (modern) |\n\t * |---|---|---|\n\t * | Cadence for dynamic-dynamic pairs | 2× per frame per side | 1× per frame per side |\n\t * | `response.a` semantics | Fixed per pair (first body in detector call) | Always the receiver (`response.a === this`) |\n\t * | `response.b` semantics | Fixed per pair | Always the partner (`response.b === other`) |\n\t * | `response.normal` / `response.depth` | ✗ | ✓ — `normal.y < -0.7` = \"push me up\" |\n\t * | `return false` to skip push-out | ✓ (honored by SAT) | ✗ — use `bodyDef.isSensor` or `setSensor` instead |\n\t *\n\t * If you're writing new code, prefer `onCollisionActive`. Keep\n\t * `onCollision` only when its every-frame, return-false, fixed-`a`/`b`\n\t * semantics are what you want.\n\t *\n\t * @param {import(\"../physics/adapter.ts\").CollisionResponse} response - the collision response object\n\t * @param {Renderable} other - the other renderable touching this one (a reference to response.a or response.b)\n\t * @returns {boolean} true if the object should respond to the collision (its position and velocity will be corrected); the return value is only honored by the builtin SAT adapter.\n\t * @example\n\t * // legacy collision handler — note the receiver-side check on response.a\n\t * onCollision(response) {\n\t *     if (response.b.body.collisionType === me.collision.types.ENEMY_OBJECT) {\n\t *         this.pos.sub(response.overlapV);\n\t *         this.hurt();\n\t *         return false;   // skip the SAT push-out\n\t *     }\n\t *     return true;\n\t * }\n\t */\n\tonCollision(_response, _other) {\n\t\t// Default returns `undefined` so that bodies without a user-defined\n\t\t// `onCollision` get push-out by default (matches matter's \"solver\n\t\t// resolves contacts unless `isSensor`\" model). A user-defined\n\t\t// override returning `false` still opts out — that's the legacy\n\t\t// SAT idiom for one-way platforms / triggers, preserved for\n\t\t// backward compatibility. Subclasses that don't override this\n\t\t// method effectively inherit the modern default.\n\t}\n\n\t/*\n\t * Modern collision lifecycle hooks (`onCollisionStart`,\n\t * `onCollisionActive`, `onCollisionEnd`) are intentionally NOT\n\t * defined on the base class. Subclasses override the ones they\n\t * care about; the adapter's dispatch logic checks\n\t * `typeof renderable.onCollisionX === \"function\"` to know whether\n\t * to fire them. Defining base stubs would make every renderable\n\t * \"look like\" it implements every hook, which would in turn make\n\t * the `onCollisionActive supersedes onCollision` rule fire even\n\t * for legacy-only code.\n\t *\n\t * Documented surface (subclasses implement these as needed):\n\t *\n\t *   onCollisionStart(response, other)\n\t *     One-shot on first contact. Use for stomp bounces, pickups,\n\t *     trigger entry. Receiver-symmetric (`response.a === this`,\n\t *     `response.b === other`). Same contract on every adapter.\n\t *\n\t *   onCollisionActive(response, other)\n\t *     Every step while two bodies remain in contact. The modern\n\t *     equivalent of `onCollision`, with different semantics:\n\t *       - 1× per frame per side (vs `onCollision`'s 2× for dyn-dyn)\n\t *       - `response.a === this`, `response.b === other`\n\t *       - `response.normal` is the MTV of the receiver\n\t *       - `return false` is NOT honored (use `bodyDef.isSensor`)\n\t *     When defined, `onCollision` is suppressed on the same\n\t *     renderable. Stomp idiom: `response.normal.y < -0.7`.\n\t *\n\t *   onCollisionEnd(response, other)\n\t *     One-shot when two bodies separate.\n\t */\n\n\t/**\n\t * Destroy function<br>\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// allow recycling object properties\n\t\tthis.currentTransform = undefined;\n\n\t\tthis.anchorPoint.revoke();\n\t\tthis.anchorPoint = undefined;\n\n\t\tthis.pos = undefined;\n\n\t\tif (this._absPos) {\n\t\t\tvector3dPool.release(this._absPos);\n\t\t\tthis._absPos = undefined;\n\t\t}\n\n\t\tif (this._bounds) {\n\t\t\tboundsPool.release(this._bounds);\n\t\t\tthis._bounds = undefined;\n\t\t}\n\n\t\tthis.onVisibilityChange = undefined;\n\n\t\tif (this.mask) {\n\t\t\tpool.push(this.mask);\n\t\t\tthis.mask = undefined;\n\t\t}\n\n\t\tif (this._tint) {\n\t\t\tcolorPool.release(this._tint);\n\t\t\tthis._tint = undefined;\n\t\t}\n\n\t\t// cannot import and reference a Container from a Renderable, since Container extends Renderable, creating a circular dependency\n\t\t//if (this.ancestor instanceof Container || this.ancestor instanceof Entity) {\n\t\tthis.ancestor = undefined;\n\n\t\t// cannot import and reference a Application from a Renderable, messsing up class order in the bundle\n\t\t//if (this._parentApp instanceof Application) {\n\t\tthis._parentApp = undefined;\n\t\t//}\n\n\t\t// destroy the physic body if a builtin body object\n\t\tif (this.body instanceof Body) {\n\t\t\tthis.body.destroy.apply(this.body, arguments);\n\t\t\tthis.body = undefined;\n\t\t}\n\n\t\t// release all registered events\n\t\treleaseAllPointerEvents(this);\n\n\t\t// call the user defined destroy method\n\t\tthis.onDestroyEvent.apply(this, arguments);\n\n\t\t// destroy any shader effects if not done by the user through onDestroyEvent()\n\t\tfor (const effect of this.postEffects) {\n\t\t\t// `effect.shared` opts a shader out of auto-destroy — it is reused\n\t\t\t// across renderables, so freeing it here would break the others.\n\t\t\tif (typeof effect.destroy === \"function\" && !effect.shared) {\n\t\t\t\teffect.destroy();\n\t\t\t}\n\t\t}\n\t\tthis.postEffects.length = 0;\n\t}\n\n\t/**\n\t * OnDestroy Notification function<br>\n\t * Called by engine before deleting the object. `Stage.destroy(app)`\n\t * forwards the active Application, so subclasses that wire teardown\n\t * against the app object can override as `onDestroyEvent(app)`.\n\t * @param {...*} _args - forwarded by `destroy(...args)`; Stage passes the Application instance\n\t */\n\tonDestroyEvent(..._args) {\n\t\t// to be extended !\n\t}\n\n\t/**\n\t * Lifecycle hook fired by {@link Container} when this renderable is\n\t * added to a container that is part of the active scene graph.\n\t * Override to wire up input handlers, register external listeners,\n\t * or grab adapter references — `this.parentApp` is guaranteed to be\n\t * available here. Pair with {@link Renderable#onDeactivateEvent}.\n\t * @param {...*} _args - forwarded by `Container.addChild`\n\t */\n\tonActivateEvent(..._args) {\n\t\t// to be extended !\n\t}\n\n\t/**\n\t * Lifecycle hook fired by {@link Container} when this renderable is\n\t * removed from its container or its container is itself removed.\n\t * Override to release input handlers, unsubscribe from events, or\n\t * drop adapter references. Pair with {@link Renderable#onActivateEvent}.\n\t * @param {...*} _args - forwarded by `Container.removeChildNow`\n\t */\n\tonDeactivateEvent(..._args) {\n\t\t// to be extended !\n\t}\n}\n","import { emit, TEXTURE2D_DESTROYED } from \"../../system/event.ts\";\n\n/**\n * Compile-time-only brand key; no runtime value can hold it.\n * @ignore\n */\ndeclare const gpuResidentBrand: unique symbol;\n\n/**\n * An opaque, GPU-resident texture backing — the source a GPU-resident\n * {@link Texture2d} resolves to (for example the framebuffer copy\n * {@link WebGLRenderer#toFrameTexture} produces). Such a backing uploads/binds\n * itself through the texture cache instead of being read back to the CPU. Its\n * concrete shape is an engine internal that may change between releases:\n * obtain values from the engine and pass them back to the engine — do not\n * construct or inspect them.\n *\n * Nominally branded with a `unique symbol`, so only engine-provided values\n * satisfy the type — a plain object is NOT assignable, keeping\n * {@link Texture2dSource} (and {@link Texture2d#getTexture}) honestly typed.\n */\nexport interface GPUResidentTexture {\n\t/**\n\t * Phantom nominal brand — never present at runtime.\n\t * @ignore\n\t */\n\treadonly [gpuResidentBrand]: true;\n}\n\n/**\n * The backing source a {@link Texture2d} resolves to through\n * {@link Texture2d#getTexture}: a drawable (canvas/image) for CPU-backed\n * assets, or an opaque {@link GPUResidentTexture} for GPU-resident ones.\n */\nexport type Texture2dSource =\n\t| HTMLCanvasElement\n\t| HTMLImageElement\n\t| OffscreenCanvas\n\t| ImageBitmap\n\t| GPUResidentTexture;\n\n/**\n * Abstract base for a 2D texture asset — an object that owns a texture\n * source and can be used anywhere the engine expects an image:\n * {@link Sprite#image}, {@link Sprite#normalMap}, an {@link ImageLayer}, or\n * bound as a sampler uniform in a custom shader (see\n * {@link ShaderEffect#setTexture}).\n *\n * A `Texture2d` is recognized via `instanceof` and resolved to its backing\n * source through {@link Texture2d#getTexture} — so passing the asset object\n * directly (`{ image: myTexture }`) works the same as passing a raw\n * `HTMLCanvasElement`. Raw DOM image/canvas sources and the loader's decoded\n * `CompressedImage` data are accepted too, but are not part of this class\n * hierarchy.\n *\n * Most assets are **CPU-backed** and resolve to a drawable canvas/image — those\n * are the ones assignable to {@link Sprite#image} / {@link Sprite#normalMap} /\n * an {@link ImageLayer} (which upload the drawable through the texture cache).\n * Subclasses may also be **GPU-resident**, resolving to an opaque renderer\n * backing that never leaves the GPU (e.g. the capture returned by\n * `renderer.toFrameTexture()`). A GPU-resident texture is **not** a drawable\n * source: use it as a custom-shader sampler via {@link ShaderEffect#setTexture}\n * (which binds its live GL handle), NOT as `Sprite#image`. The contract admits\n * both (a future WebGPU backend follows the same shape with a `GPUTexture`).\n *\n * Concrete implementations:\n * - {@link TextureAtlas} — packed multi-region sprite sheet (CPU-backed)\n * @category Game Objects\n */\nexport default abstract class Texture2d {\n\t/**\n\t * Return the backing source for this texture — a drawable canvas/image for\n\t * CPU-backed assets (assignable to {@link Sprite#image} /\n\t * {@link Sprite#normalMap} / an {@link ImageLayer}), or an opaque\n\t * GPU-resident backing for GPU-resident ones (bind those as a custom-shader\n\t * sampler via {@link ShaderEffect#setTexture}; they are not drawable sources).\n\t * @returns the backing source\n\t */\n\tabstract getTexture(): Texture2dSource;\n\n\t/**\n\t * Release any GPU/CPU resources held by this texture. The texture must not\n\t * be used after calling destroy.\n\t *\n\t * Broadcasts {@link event.TEXTURE2D_DESTROYED} with the backing source so\n\t * GPU-side caches keyed by source image (e.g. the lit batcher's normal-map\n\t * textures) release what they hold for it. Subclasses that own additional\n\t * resources override this and call `super.destroy()` FIRST — the source\n\t * must still be resolvable when the event fires.\n\t */\n\tdestroy(): void {\n\t\temit(TEXTURE2D_DESTROYED, this.getTexture());\n\t}\n}\n","/**\n * Pick out every vertex attribute name from a shader source, regardless of\n * GLSL version. GLSL 1.00 marks attributes with the `attribute` storage\n * qualifier; GLSL ES 3.00 reuses `in` at file scope for the same purpose\n * (and `in` inside function parameter lists, which we exclude by requiring\n * the qualifier to start at the beginning of a line). Skipping the 3.00\n * form leaves the shader with no bound vertex data and the rasterizer\n * silently degenerates every triangle.\n * @ignore\n */\nexport function extractAttributes(gl, shader) {\n\tconst attributes = {};\n\t// Match either `attribute <type> <name>` (GLSL 1.00) or `in <type>\n\t// <name>` (GLSL 3.00), with an optional precision / aux qualifier in\n\t// front of the type (`highp vec3`, `mediump vec2`, etc.). Whitespace\n\t// inside the declaration is restricted to `[ \\t]+` (horizontal only)\n\t// rather than `\\s+` — letting `\\s` also match `\\n` made the regex\n\t// ambiguous around line boundaries and CodeQL flagged it as a\n\t// polynomial-time ReDoS risk on shader sources with many newlines.\n\t// Declarations span one line in practice, so this is a non-issue.\n\tconst attrRx = /(?:^|\\n)[ \\t]*(?:attribute|in)[ \\t]+(?:\\w+[ \\t]+)+(\\w+)/g;\n\tlet match;\n\tlet i = 0;\n\n\twhile ((match = attrRx.exec(shader.vertex))) {\n\t\tattributes[match[1]] = i++;\n\t}\n\n\treturn attributes;\n}\n","/**\n * set precision for the given shader source\n * won't do anything if the precision is already specified\n * @ignore\n */\nexport function setPrecision(src, precision) {\n\t// Skip injection when the shader already declares precision near the\n\t// top — either at byte 0 (GLSL 1.00) or right after the `#version`\n\t// directive (GLSL 3.00). The substring check we used to do only caught\n\t// the byte-0 case, so a 3.00 shader with its own `precision highp\n\t// float;` after `#version` was getting a second precision line injected\n\t// (still legal GLSL but at best wasteful and at worst overriding user\n\t// intent).\n\tif (/^\\s*(?:#version[^\\n]*\\n)?\\s*precision\\b/.test(src)) {\n\t\treturn src;\n\t}\n\t// WebGL2 GLSL 3.00 requires `#version 300 es` as the very first line,\n\t// before any other directive. Preserve it and insert precisions after.\n\t// For 3.00 shaders we inject precisions for float + int so individual\n\t// shader files don't have to hardcode them; the engine's chosen\n\t// precision applies uniformly. GLSL 1.00 shaders only need a float\n\t// precision declaration (samplers and ints have default precisions\n\t// there).\n\tif (src.substring(0, 8) === \"#version\") {\n\t\tconst inject =\n\t\t\t\"\\nprecision \" + precision + \" float;\\nprecision \" + precision + \" int;\";\n\t\tconst nl = src.indexOf(\"\\n\");\n\t\t// A single-line shader (just `#version 300 es` with no trailing\n\t\t// newline) has no `\\n` to anchor the insert on; append at end —\n\t\t// the `#version` directive must remain on its own first line.\n\t\tif (nl < 0) {\n\t\t\treturn src + inject;\n\t\t}\n\t\treturn src.substring(0, nl) + inject + src.substring(nl);\n\t}\n\treturn \"precision \" + precision + \" float;\\n\" + src;\n}\n\n/**\n * return the best shader precision for this device, up to the requested cap.\n * @ignore\n * @param {WebGLRenderingContext} gl - the current WebGL context\n * @param {boolean} [highPrecision=true] - if false, cap at \"mediump\" even when \"highp\" is available\n * @returns {string} \"lowp\", \"mediump\", or \"highp\"\n */\nexport function getMaxShaderPrecision(gl, highPrecision = true) {\n\tif (\n\t\thighPrecision &&\n\t\tgl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision >\n\t\t\t0 &&\n\t\tgl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0\n\t) {\n\t\treturn \"highp\";\n\t}\n\tif (\n\t\tgl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision >\n\t\t\t0 &&\n\t\tgl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision >\n\t\t\t0\n\t) {\n\t\treturn \"mediump\";\n\t}\n\treturn \"lowp\";\n}\n","/**\n * @ignore\n */\nfunction compileShader(gl, type, source) {\n\tconst shader = gl.createShader(type);\n\tgl.shaderSource(shader, source);\n\tgl.compileShader(shader);\n\n\tif (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n\t\tthrow new Error(gl.getShaderInfoLog(shader));\n\t}\n\n\treturn shader;\n}\n\n/**\n * Compile GLSL into a shader object\n * @ignore\n */\nexport function compileProgram(gl, vertex, fragment, attributes) {\n\tconst vertShader = compileShader(gl, gl.VERTEX_SHADER, vertex);\n\tconst fragShader = compileShader(gl, gl.FRAGMENT_SHADER, fragment);\n\n\tconst program = gl.createProgram();\n\n\tgl.attachShader(program, vertShader);\n\tgl.attachShader(program, fragShader);\n\n\t// force vertex attributes to use location 0 as starting location to prevent\n\t// browser to do complicated emulation when running on desktop OpenGL (e.g. on macOS)\n\tfor (const location in attributes) {\n\t\tgl.bindAttribLocation(program, attributes[location], location);\n\t}\n\n\tgl.linkProgram(program);\n\n\tif (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n\t\tconst error_msg =\n\t\t\t\"Error initializing Shader \" +\n\t\t\tthis +\n\t\t\t\"\\n\" +\n\t\t\t\"gl.VALIDATE_STATUS: \" +\n\t\t\tgl.getProgramParameter(program, gl.VALIDATE_STATUS) +\n\t\t\t\"\\n\" +\n\t\t\t\"gl.getError()\" +\n\t\t\tgl.getError() +\n\t\t\t\"\\n\" +\n\t\t\t\"gl.getProgramInfoLog()\" +\n\t\t\tgl.getProgramInfoLog(program);\n\t\t// house cleaning\n\t\tgl.deleteProgram(program);\n\t\t// throw the exception\n\t\tthrow new Error(error_msg);\n\t}\n\n\tgl.useProgram(program);\n\n\t// clean-up\n\tgl.deleteShader(vertShader);\n\tgl.deleteShader(fragShader);\n\n\treturn program;\n}\n","/**\n * clean the given source from space, comments, etc...\n * @ignore\n */\nexport function minify(src) {\n\t// remove comments\n\tsrc = src.replace(/\\/\\*[\\s\\S]*?\\*\\/|([^\\\\:]|^)\\/\\/.*$/gm, \"$1\");\n\t// Remove leading and trailing whitespace from lines\n\tsrc = src.replace(/(\\\\n\\s+)|(\\s+\\\\n)/g, \"\");\n\t// Remove line breaks\n\tsrc = src.replace(/(\\\\r|\\\\n)+/g, \"\");\n\t// Remove unnecessary whitespace (spaces/tabs only, preserving newlines for preprocessor directives)\n\tsrc = src.replace(/[ \\t]*([;,[\\](){}\\\\\\/\\-+*|^&!=<>?~%])[ \\t]*/g, \"$1\");\n\n\treturn src;\n}\n\n/**\n * Prepend preprocessor defines to a GLSL source, respecting `#version`.\n *\n * `#version` must be the very first statement of a GLSL ES 3.00 shader —\n * anything before it, including a `#define`, is a compile error. So defines\n * go immediately *after* that line when one is present, and at the top when\n * it is not (GLSL ES 1.00 sources declare no version).\n *\n * Used to compile shader variants from one source: the optional parts are\n * `#ifdef`-guarded, and the variant is what defines are injected here.\n * @param {string} source - the shader source\n * @param {string} defines - `#define` lines, each newline-terminated\n * @returns {string} the source with the defines injected\n * @ignore\n */\nexport function injectDefines(source, defines) {\n\tif (!defines) {\n\t\treturn source;\n\t}\n\t// GLSL ES 3.00 allows comments (and whitespace) BEFORE `#version`, so the\n\t// directive is not necessarily at byte 0 — a licence header above it is\n\t// enough to push the defines in front of it, which fails to compile and\n\t// leaves the draw with no program at all. Skip leading comments too, and\n\t// tolerate a `#version` line that ends the source without a newline.\n\tconst lead = /^(?:\\s|\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/)*/.exec(source)[0];\n\tconst rest = source.slice(lead.length);\n\tconst version = /^#version[^\\n]*(?:\\n|$)/.exec(rest);\n\tif (version === null) {\n\t\treturn defines + source;\n\t}\n\tconst directive = version[0].endsWith(\"\\n\") ? version[0] : `${version[0]}\\n`;\n\treturn lead + directive + defines + rest.slice(version[0].length);\n}\n","/**\n * Hash map of GLSL data types to WebGL Uniform methods\n * @ignore\n */\nconst fnHash = {\n\tbool: \"1i\",\n\tint: \"1i\",\n\tfloat: \"1f\",\n\tvec2: \"2fv\",\n\tvec3: \"3fv\",\n\tvec4: \"4fv\",\n\tbvec2: \"2iv\",\n\tbvec3: \"3iv\",\n\tbvec4: \"4iv\",\n\tivec2: \"2iv\",\n\tivec3: \"3iv\",\n\tivec4: \"4iv\",\n\tuvec2: \"2uiv\",\n\tuvec3: \"3uiv\",\n\tuvec4: \"4uiv\",\n\tmat2: \"Matrix2fv\",\n\tmat3: \"Matrix3fv\",\n\tmat4: \"Matrix4fv\",\n\tsampler2D: \"1i\",\n\t// WebGL2 integer-typed samplers — bound to a unit just like a\n\t// `sampler2D`; the GLSL `usampler2D` / `isampler2D` types let the\n\t// shader read raw integer values via `texelFetch` instead of the\n\t// normalized-float `texture()` path.\n\tusampler2D: \"1i\",\n\tisampler2D: \"1i\",\n};\n\n/**\n * Compare a freshly-incoming uniform value to the last value we sent for\n * the same uniform. Scalars compare by `===`; vec/mat values compare\n * element-wise (callers commonly reuse a scratch `Float32Array`, so\n * reference equality would miss every change).\n * @ignore\n */\nfunction valuesMatch(cached, val) {\n\tif (cached === undefined) {\n\t\treturn false;\n\t}\n\tif (\n\t\tval !== null &&\n\t\ttypeof val === \"object\" &&\n\t\ttypeof val.length === \"number\"\n\t) {\n\t\tif (cached.length !== val.length) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (let i = 0; i < val.length; i++) {\n\t\t\tif (cached[i] !== val[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\treturn cached === val;\n}\n\n/**\n * Capture the current value into the cache slot so future `setUniform`\n * calls can short-circuit. Reuses the existing slot (and its allocation)\n * when the length matches — only a length change or a first capture\n * allocates a fresh array.\n * @ignore\n */\nexport function captureValue(prev, val) {\n\tif (\n\t\tval === null ||\n\t\ttypeof val !== \"object\" ||\n\t\ttypeof val.length !== \"number\"\n\t) {\n\t\treturn val;\n\t}\n\tif (\n\t\tprev !== undefined &&\n\t\ttypeof prev === \"object\" &&\n\t\ttypeof prev.length === \"number\" &&\n\t\tprev.length === val.length\n\t) {\n\t\tfor (let i = 0; i < val.length; i++) {\n\t\t\tprev[i] = val[i];\n\t\t}\n\t\treturn prev;\n\t}\n\treturn typeof val.slice === \"function\" ? val.slice() : Array.from(val);\n}\n\n/**\n * Build the `uniforms` proxy object for a compiled shader program.\n *\n * Each detected uniform gets a defineProperty getter (returns its\n * `WebGLUniformLocation`) and a setter that pushes the value to GL. The\n * setter caches the last value it sent and skips the underlying\n * `gl.uniform*` call when the incoming value matches — uniform writes are\n * cheap individually, but a typical per-frame draw pass sets a dozen of\n * them per shader, and most are layer-lifetime constants (`uMapSize`,\n * `uCellSize`, `uOpacity`, projection matrix on idle frames, etc.).\n *\n * Cache scope is per-shader: each `GLShader` calls `extractUniforms` once\n * and gets its own closure-captured `cache` map, so caches don't leak\n * across programs.\n * @ignore\n */\nexport function extractUniforms(gl, shader) {\n\tconst uniforms = {};\n\tconst uniRx = /uniform\\s+(\\w+)\\s+(\\w+)/g;\n\tconst uniformsData = {};\n\tconst descriptor = {};\n\tconst locations = {};\n\t// last value sent to GL for each uniform, keyed by name. Filled lazily\n\t// on first set; reused (in place) on subsequent sets of the same\n\t// length to avoid steady-state allocation.\n\tconst cache = {};\n\tlet match;\n\n\t// Detect all uniform names and types\n\t[shader.vertex, shader.fragment].forEach((shader) => {\n\t\twhile ((match = uniRx.exec(shader))) {\n\t\t\tuniformsData[match[2]] = match[1];\n\t\t}\n\t});\n\n\t// Get uniform references\n\tObject.keys(uniformsData).forEach((name) => {\n\t\tconst type = uniformsData[name];\n\t\tlocations[name] = gl.getUniformLocation(shader.program, name);\n\n\t\tdescriptor[name] = {\n\t\t\tget: (function (name) {\n\t\t\t\t/*\n\t\t\t\t * A getter for the uniform location\n\t\t\t\t */\n\t\t\t\treturn function () {\n\t\t\t\t\treturn locations[name];\n\t\t\t\t};\n\t\t\t})(name),\n\t\t\tset: (function (name, type, fn) {\n\t\t\t\tif (/^mat/.test(type)) {\n\t\t\t\t\t/*\n\t\t\t\t\t * A generic setter for uniform matrices\n\t\t\t\t\t */\n\t\t\t\t\treturn function (val) {\n\t\t\t\t\t\tif (valuesMatch(cache[name], val)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcache[name] = captureValue(cache[name], val);\n\t\t\t\t\t\tgl[fn](locations[name], false, val);\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\t/*\n\t\t\t\t\t * A generic setter for uniform vectors\n\t\t\t\t\t */\n\t\t\t\t\treturn function (val) {\n\t\t\t\t\t\tlet fnv = fn;\n\t\t\t\t\t\tif (val.length && !/v$/.test(fn)) {\n\t\t\t\t\t\t\tfnv += \"v\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (valuesMatch(cache[name], val)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcache[name] = captureValue(cache[name], val);\n\t\t\t\t\t\tgl[fnv](locations[name], val);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t})(name, type, \"uniform\" + fnHash[type]),\n\t\t};\n\t});\n\tObject.defineProperties(uniforms, descriptor);\n\n\treturn uniforms;\n}\n","import {\n\tONCONTEXT_LOST,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n} from \"../../system/event.ts\";\nimport { extractAttributes } from \"./utils/attributes.js\";\nimport { getMaxShaderPrecision, setPrecision } from \"./utils/precision.js\";\nimport { compileProgram } from \"./utils/program.js\";\nimport { minify } from \"./utils/string.js\";\nimport { captureValue, extractUniforms } from \"./utils/uniforms.js\";\n\n/**\n * a complete custom shader program. Historically a WebGL-only GLSL\n * program (the class keeps its name for compatibility), since 20.0 it can\n * carry one realization per GPU backend — a `{vertex, fragment}` GLSL\n * pair for the WebGL renderer and/or a complete `wgsl` module for the\n * WebGPU renderer — mirroring how {@link ShaderEffect} carries one body\n * per shading language. The {@link GLShader#isWebGL} / {@link GLShader#isWebGPU}\n * flags report which realizations exist; a renderer hosts the one it\n * speaks and anything else degrades to the built-in shading (never fatal).\n *\n * ### The WGSL module contract (custom `Mesh` shaders)\n *\n * The `wgsl` source is a complete module hosted by the mesh batchers:\n * entry points must be named **`vertex_main`** (`@vertex`) and\n * **`fragment_main`** (`@fragment`), over the frozen mesh vertex layout —\n * `@location(0) aVertex : vec3f`, `@location(1) aRegion : vec2f`,\n * `@location(2) aColor : vec4f`, plus `@location(3) aNormal : vec3f` on\n * the 48-byte `lit` layout. Bind groups (declare only what is read):\n * group 0 `FrameUniforms {projection : mat4x4<f32>, lineWidth : f32}`,\n * group 1 the mesh texture + sampler, group 2 the `Light3dBlock` (lit\n * host only), group 3 `MeshUniforms {model, view : mat4x4<f32>,\n * tint, params, emissive : vec4f}` (`params.x` = alpha cutout). The\n * vertex stage owns the projection (`projection * view * model`) and must\n * remap the GL-convention clip z: `(clip.z + clip.w) * 0.5`. WGSL\n * validation is asynchronous and never throws — a failing module logs its\n * errors once and the mesh falls back to the built-in shading.\n * @category Rendering\n */\nexport default class GLShader {\n\t/**\n\t * @param {WebGLRenderingContext} [gl] - the current WebGL rendering\n\t * context (`renderer.gl` — undefined on non-WebGL renderers, which\n\t * simply skips the GLSL realization)\n\t * @param {string|object} vertex - a string containing the GLSL vertex\n\t * source, OR a sources object `{vertex, fragment, wgsl, precision,\n\t * label}` carrying one realization per backend (any omittable)\n\t * @param {string} [fragment] - a string containing the GLSL fragment source\n\t * @param {string} [precision=auto detected] - float precision ('lowp', 'mediump' or 'highp').\n\t * @see https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders\n\t * @example\n\t * // create a basic shader\n\t * let myShader = new me.GLShader(\n\t *    // WebGL rendering context\n\t *    gl,\n\t *    // vertex shader\n\t *    [\n\t *        \"void main() {\",\n\t *        \"    gl_Position = doMathToMakeClipspaceCoordinates;\",\n\t *        \"}\"\n\t *    ].join(\"\\n\"),\n\t *    // fragment shader\n\t *    [\n\t *        \"void main() {\",\n\t *        \"    gl_FragColor = doMathToMakeAColor;\",\n\t *        \"}\"\n\t *    ].join(\"\\n\")\n\t *  )\n\t * // use the shader\n\t * myShader.bind();\n\t * @example\n\t * // a dual-backend custom mesh shader: the same object hosts on\n\t * // whichever GPU renderer is active (gl is undefined on WebGPU)\n\t * myMesh.shader = new me.GLShader(app.renderer.gl, {\n\t *     vertex: toonVertexGLSL,\n\t *     fragment: toonFragmentGLSL,\n\t *     wgsl: toonModuleWGSL,\n\t * });\n\t */\n\tconstructor(gl, vertex, fragment, precision) {\n\t\t// object sources form: {vertex, fragment, wgsl, precision, label}\n\t\tlet wgsl;\n\t\tlet label;\n\t\tif (typeof vertex === \"object\" && vertex !== null) {\n\t\t\tconst sources = vertex;\n\t\t\tvertex = sources.vertex;\n\t\t\tfragment = sources.fragment;\n\t\t\tprecision = sources.precision ?? precision;\n\t\t\twgsl = sources.wgsl;\n\t\t\tlabel = sources.label;\n\t\t}\n\n\t\t/**\n\t\t * the active gl rendering context\n\t\t * @type {WebGLRenderingContext}\n\t\t */\n\t\tthis.gl = gl;\n\n\t\t/**\n\t\t * `true` when this shader carries a WebGL realization: a\n\t\t * `{vertex, fragment}` GLSL pair compiled against a live context.\n\t\t * @type {boolean}\n\t\t * @readonly\n\t\t */\n\t\tthis.isWebGL =\n\t\t\tgl != null && typeof vertex === \"string\" && typeof fragment === \"string\";\n\n\t\t/**\n\t\t * `true` when this shader carries a WebGPU realization: a complete\n\t\t * `wgsl` module (see the class docs for the module contract).\n\t\t * @type {boolean}\n\t\t * @readonly\n\t\t */\n\t\tthis.isWebGPU = typeof wgsl === \"string\";\n\n\t\t/**\n\t\t * the complete WGSL module source, when {@link GLShader#isWebGPU}\n\t\t * @type {string|undefined}\n\t\t */\n\t\tthis.wgsl = wgsl;\n\n\t\t/**\n\t\t * optional debug label, carried onto the GPU shader module (shows\n\t\t * up in browser GPU error messages and profilers)\n\t\t * @type {string|undefined}\n\t\t */\n\t\tthis.label = label;\n\n\t\t// flipped false when asynchronous WGSL validation reports errors —\n\t\t// the mesh batchers then fall back to their built-in shading\n\t\tthis.wgslValid = true;\n\n\t\t// per-host WGSL family registrations for the current device\n\t\t// generation: {epoch, keys: Map<host key, family key>} — compared\n\t\t// against the pipeline cache's epoch so a device loss re-registers\n\t\tthis._wgslRegistrations = { epoch: -1, keys: new Map() };\n\n\t\tif (!this.isWebGL && !this.isWebGPU) {\n\t\t\t// no realization at all (e.g. a GLSL-only asset loaded under a\n\t\t\t// non-WebGL renderer): warn and stay inert — assigning the\n\t\t\t// shader is harmless, hosts skip it and keep built-in shading\n\t\t\tconsole.warn(\n\t\t\t\t\"GLShader: no usable realization — provide {vertex, fragment} GLSL sources with a WebGL context, and/or a complete `wgsl` module for the WebGPU renderer\",\n\t\t\t);\n\t\t}\n\n\t\t/**\n\t\t * `true` once {@link destroy} has been called. After this flag is\n\t\t * `true`, every method on the shader is a silent no-op — callers\n\t\t * holding a stale reference (e.g. a still-registered update loop)\n\t\t * do not crash the frame.\n\t\t * @type {boolean}\n\t\t * @readonly\n\t\t */\n\t\tthis.destroyed = false;\n\n\t\t/**\n\t\t * When `true`, a renderable will NOT auto-destroy this shader when it is\n\t\t * removed from its `postEffects` (via the `shader` setter,\n\t\t * `removePostEffect`, `clearPostEffects`) or when the renderable itself\n\t\t * is destroyed. Set this on a shader shared across several renderables so\n\t\t * one of them going away doesn't free the GL program still used by the\n\t\t * others — you then own its lifecycle and call {@link destroy} yourself.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.shared = false;\n\n\t\t/**\n\t\t * `true` while the WebGL context is lost (and until it's\n\t\t * restored). The GL program/attributes/uniforms are released\n\t\t * during the suspended window but the shader source code is\n\t\t * preserved so {@link _onContextRestored} can rebuild the\n\t\t * program against the new context. User code generally doesn't\n\t\t * read this — methods short-circuit internally — but it's\n\t\t * available for diagnostic / debug-plugin tooling.\n\t\t * @type {boolean}\n\t\t * @readonly\n\t\t */\n\t\tthis.suspended = false;\n\n\t\t// raw source kept so we can recompile against a restored context\n\t\tthis._sourceVertex = vertex;\n\t\tthis._sourceFragment = fragment;\n\t\tthis._precision = precision;\n\n\t\t// uniform writes are cached + replayed across a context cycle\n\t\tthis._uniformCache = Object.create(null);\n\n\t\tif (this.isWebGL) {\n\t\t\t// defer compile if constructed mid-suspended-window; replay handles it\n\t\t\tif (gl.isContextLost()) {\n\t\t\t\tthis.suspended = true;\n\t\t\t\tthis.program = null;\n\t\t\t\tthis.uniforms = null;\n\t\t\t\tthis.attributes = null;\n\t\t\t} else {\n\t\t\t\tthis._compile();\n\t\t\t}\n\n\t\t\t// context lifecycle only concerns the GL program — a WGSL-only\n\t\t\t// shader has nothing to tear down or rebuild\n\t\t\ton(ONCONTEXT_LOST, this._onContextLost, this);\n\t\t\ton(ONCONTEXT_RESTORED, this._onContextRestored, this);\n\t\t} else {\n\t\t\tthis.program = null;\n\t\t\tthis.uniforms = null;\n\t\t\tthis.attributes = null;\n\t\t}\n\t}\n\n\t/**\n\t * (Re)compile the shader program against `this.gl` from the\n\t * preserved source. Called from the constructor and from\n\t * {@link _onContextRestored}. Replays any cached uniform values\n\t * against the freshly-extracted uniforms proxy.\n\t * @private\n\t */\n\t_compile() {\n\t\tthis.vertex = setPrecision(\n\t\t\tminify(this._sourceVertex),\n\t\t\tthis._precision || getMaxShaderPrecision(this.gl),\n\t\t);\n\t\tthis.fragment = setPrecision(\n\t\t\tminify(this._sourceFragment),\n\t\t\tthis._precision || getMaxShaderPrecision(this.gl),\n\t\t);\n\n\t\t/**\n\t\t * the location attributes of the shader\n\t\t * @type {GLint[]}\n\t\t */\n\t\tthis.attributes = extractAttributes(this.gl, this);\n\n\t\t/**\n\t\t * a reference to the shader program (once compiled)\n\t\t * @type {WebGLProgram}\n\t\t */\n\t\tthis.program = compileProgram(\n\t\t\tthis.gl,\n\t\t\tthis.vertex,\n\t\t\tthis.fragment,\n\t\t\tthis.attributes,\n\t\t);\n\n\t\t/**\n\t\t * the uniforms of the shader\n\t\t * @type {object}\n\t\t */\n\t\tthis.uniforms = extractUniforms(this.gl, this);\n\n\t\tthis.suspended = false;\n\n\t\t// replay cached uniforms against the new program\n\t\tfor (const name of Object.keys(this._uniformCache)) {\n\t\t\tif (typeof this.uniforms[name] !== \"undefined\") {\n\t\t\t\tthis.bind();\n\t\t\t\tthis.uniforms[name] = this._uniformCache[name];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handler for {@link ONCONTEXT_LOST}. Tears down the GL program\n\t * but preserves the shader source + cached uniform values so the\n\t * shader can be transparently rebuilt on context restore.\n\t * @private\n\t */\n\t_onContextLost() {\n\t\tif (this.destroyed || this.suspended) {\n\t\t\treturn;\n\t\t}\n\t\tthis.suspended = true;\n\t\tthis.uniforms = null;\n\t\tthis.attributes = null;\n\t\t// deleteProgram against a dead ANGLE context throws — swallow it\n\t\tif (this.program !== null) {\n\t\t\ttry {\n\t\t\t\tthis.gl.deleteProgram(this.program);\n\t\t\t} catch {\n\t\t\t\t/* context already gone */\n\t\t\t}\n\t\t\tthis.program = null;\n\t\t}\n\t}\n\n\t/**\n\t * Handler for {@link ONCONTEXT_RESTORED}. Re-compiles and\n\t * re-links the shader against the new GL context, then replays\n\t * any cached uniform values via {@link _compile}.\n\t * @private\n\t */\n\t_onContextRestored() {\n\t\tif (this.destroyed) {\n\t\t\t// destroyed shaders are not resurrected\n\t\t\treturn;\n\t\t}\n\t\tthis._compile();\n\t}\n\n\t/**\n\t * Installs this shader program as part of current rendering state\n\t */\n\tbind() {\n\t\t// program === null also covers shaders with no WebGL realization\n\t\tif (this.destroyed || this.suspended || this.program === null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.gl.useProgram(this.program);\n\t}\n\n\t/**\n\t * returns the location of an attribute variable in this shader program\n\t * @param {string} name - the name of the attribute variable whose location to get.\n\t * @returns {GLint} number indicating the location of the variable name if found. Returns -1 otherwise\n\t */\n\tgetAttribLocation(name) {\n\t\t// attributes === null also covers shaders with no WebGL realization\n\t\t// — the same silent-no-op contract as bind()/setUniform()\n\t\tif (this.destroyed || this.suspended || this.attributes === null) {\n\t\t\treturn -1;\n\t\t}\n\t\tconst attr = this.attributes[name];\n\t\tif (typeof attr !== \"undefined\") {\n\t\t\treturn attr;\n\t\t} else {\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/**\n\t * Set the uniform to the given value\n\t * @param {string} name - the uniform name\n\t * @param {object|Float32Array} value - the value to assign to that uniform\n\t * @example\n\t * myShader.setUniform(\"uProjectionMatrix\", this.projectionMatrix);\n\t */\n\tsetUniform(name, value) {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\t// cache via captureValue (slot reuse, mutation-detached); pass\n\t\t// caller's value to GL (uniform setters copy synchronously)\n\t\tlet cached;\n\t\tlet glValue;\n\t\tif (typeof value === \"object\" && value !== null) {\n\t\t\tif (typeof value.toArray === \"function\") {\n\t\t\t\tconst arr = value.toArray();\n\t\t\t\tcached = captureValue(this._uniformCache[name], arr);\n\t\t\t\tglValue = arr;\n\t\t\t} else if (Array.isArray(value) || ArrayBuffer.isView(value)) {\n\t\t\t\tcached = captureValue(this._uniformCache[name], value);\n\t\t\t\tglValue = value;\n\t\t\t} else {\n\t\t\t\tcached = value;\n\t\t\t\tglValue = value;\n\t\t\t}\n\t\t} else {\n\t\t\tcached = value;\n\t\t\tglValue = value;\n\t\t}\n\t\tthis._uniformCache[name] = cached;\n\n\t\tif (this.suspended || this.uniforms === null) {\n\t\t\t// deferred: the restore replay handles the live write. A shader\n\t\t\t// with no WebGL realization only caches — its WGSL side has no\n\t\t\t// custom uniforms to receive the value (see the module contract)\n\t\t\treturn;\n\t\t}\n\n\t\tconst uniforms = this.uniforms;\n\t\tif (typeof uniforms[name] !== \"undefined\") {\n\t\t\tthis.bind();\n\t\t\tuniforms[name] = glValue;\n\t\t} else {\n\t\t\tthrow new Error(\"undefined (\" + name + \") uniform for shader \" + this);\n\t\t}\n\t}\n\n\t/**\n\t * activate the given vertex attribute for this shader.\n\t *\n\t * Note: since 20.0 the engine no longer calls this per frame — each\n\t * {@link WebGLBatcher} captures its attribute layout once into an immutable\n\t * vertex-state object (VAO) at init (see `WebGLBatcher.createVertexState`).\n\t * Kept public for custom callers managing their own vertex setup.\n\t * Custom shaders hosted by a built-in batcher must declare that\n\t * batcher's attributes first, in layout order — attribute locations\n\t * are bound in declaration order and the vertex state is frozen.\n\t * @param {WebGLRenderingContext} gl - the current WebGL rendering context\n\t * @param {object[]} attributes - an array of vertex attributes\n\t * @param {number} stride - the size of a single vertex in bytes\n\t */\n\tsetVertexAttributes(gl, attributes, stride) {\n\t\tfor (let index = 0; index < attributes.length; ++index) {\n\t\t\tconst element = attributes[index];\n\t\t\tconst location = this.getAttribLocation(element.name);\n\n\t\t\tif (location !== -1) {\n\t\t\t\tgl.enableVertexAttribArray(location);\n\t\t\t\tgl.vertexAttribPointer(\n\t\t\t\t\tlocation,\n\t\t\t\t\telement.size,\n\t\t\t\t\telement.type,\n\t\t\t\t\telement.normalized,\n\t\t\t\t\tstride,\n\t\t\t\t\telement.offset,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create an independent copy of this shader, compiled as its own GL\n\t * program from the same vertex + fragment sources (and precision), with\n\t * every uniform value set so far replayed onto the copy. Use it when\n\t * several renderables need the same custom shader with *different*\n\t * uniform values — a single instance has a single set of uniforms.\n\t *\n\t * Ownership and lifecycle state do NOT carry over: the clone's\n\t * {@link shared} flag is **always reset to `false`**, even when cloning\n\t * a shared shader — the clone is caller-owned and will be auto-destroyed\n\t * by the renderable it is assigned to, unless you explicitly set\n\t * `shared = true` on it yourself. Note that `ShaderEffect` (which wraps\n\t * a GLShader by composition) has its own `clone()` with the same\n\t * semantics, which additionally copies its effect-level state (extra\n\t * `setTexture` bindings).\n\t * @returns {GLShader} a new, caller-owned shader (`shared === false`)\n\t */\n\tclone() {\n\t\tif (this.destroyed) {\n\t\t\tthrow new Error(\"GLShader.clone: shader has been destroyed\");\n\t\t}\n\t\tconst copy = new GLShader(this.gl, {\n\t\t\tvertex: this._sourceVertex,\n\t\t\tfragment: this._sourceFragment,\n\t\t\tprecision: this._precision,\n\t\t\twgsl: this.wgsl,\n\t\t\tlabel: this.label,\n\t\t});\n\t\t// replay this shader's cached uniform values onto the clone (the same\n\t\t// snapshot store the context-loss recovery replays from). When the\n\t\t// clone is constructed mid-context-loss its program compiles deferred\n\t\t// (`uniforms` is null) — the suspended setUniform caches values for\n\t\t// the restore replay. When live, validate against the clone's active\n\t\t// uniforms (same guard the restore replay uses) so a name cached\n\t\t// during an earlier suspended window can't make setUniform throw.\n\t\tfor (const name of Object.keys(this._uniformCache)) {\n\t\t\tif (\n\t\t\t\tcopy.suspended ||\n\t\t\t\tcopy.uniforms === null ||\n\t\t\t\ttypeof copy.uniforms[name] !== \"undefined\"\n\t\t\t) {\n\t\t\t\tcopy.setUniform(name, this._uniformCache[name]);\n\t\t\t}\n\t\t}\n\t\treturn copy;\n\t}\n\n\t/**\n\t * Register this shader's WGSL module as a pipeline family for one\n\t * hosting mesh batcher, against that host's positional group-layout\n\t * list and frozen vertex layout. Registered once per host per device\n\t * generation; the module text is namespaced by the host key so the\n\t * same shader yields distinct families (distinct pipeline layouts) on\n\t * the unlit and lit hosts. Asynchronous WGSL validation errors flip\n\t * {@link GLShader#wgslValid} and the host falls back to its built-in\n\t * shading (warn-and-degrade, never fatal).\n\t * @param {object} cache - the WebGPU renderer's pipeline cache\n\t * @param {string} host - the hosting family's vertex-layout key\n\t * @param {GPUBindGroupLayout[]} bindGroupLayouts - the host's group list\n\t * @param {string} vertexLayoutKey - the host's registered vertex layout\n\t * @returns {string|null} the family key to pass to the pipeline cache's\n\t *   `get`, or null when the module failed validation on this device\n\t *   generation (the host then uses its built-in family)\n\t * @ignore\n\t */\n\tregisterWGSL(cache, host, bindGroupLayouts, vertexLayoutKey) {\n\t\t// epoch first, invalid-gate second: a device loss replaces the cache\n\t\t// (new epoch), and the fresh device gets a fresh validation verdict\n\t\t// even for a shader that failed on the previous one\n\t\tif (this._wgslRegistrations.epoch !== cache.epoch) {\n\t\t\tthis._wgslRegistrations = { epoch: cache.epoch, keys: new Map() };\n\t\t\tthis.wgslValid = true;\n\t\t}\n\t\tif (this.wgslValid === false) {\n\t\t\treturn null;\n\t\t}\n\t\tlet key = this._wgslRegistrations.keys.get(host);\n\t\tif (typeof key === \"undefined\") {\n\t\t\t// namespace suffix LAST so compilation-error line numbers still\n\t\t\t// match the authored module text\n\t\t\tkey = cache.registerShader(`${this.wgsl}\\n// melonJS host: ${host}`, {\n\t\t\t\tbindGroupLayouts,\n\t\t\t\tvertexLayoutKey,\n\t\t\t\tlabel: this.label ?? \"melonJS custom mesh shader\",\n\t\t\t});\n\t\t\tthis._wgslRegistrations.keys.set(host, key);\n\n\t\t\tconst module = cache.modules?.[key];\n\t\t\tif (typeof module?.getCompilationInfo === \"function\") {\n\t\t\t\tmodule\n\t\t\t\t\t.getCompilationInfo()\n\t\t\t\t\t.then((info) => {\n\t\t\t\t\t\tconst errors = info.messages.filter((message) => {\n\t\t\t\t\t\t\treturn message.type === \"error\";\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\t\t\tthis.wgslValid = false;\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t`GLShader${this.label ? ` (${this.label})` : \"\"}: WGSL compilation failed — falling back to the built-in mesh shading\\n${errors\n\t\t\t\t\t\t\t\t\t.map((message) => {\n\t\t\t\t\t\t\t\t\t\treturn `  line ${message.lineNum}: ${message.message}`;\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t.join(\"\\n\")}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {});\n\t\t\t}\n\n\t\t\t// Pipeline-level containment: a module can compile CLEAN yet\n\t\t\t// declare a binding absent from this host's pipeline layout —\n\t\t\t// that error only surfaces at pipeline creation, and an invalid\n\t\t\t// pipeline poisons every frame that records it (the whole\n\t\t\t// submit fails). Build one representative pipeline inside an\n\t\t\t// error scope; any validation error flips the same fallback.\n\t\t\tconst device = cache.device;\n\t\t\tif (typeof device?.pushErrorScope === \"function\") {\n\t\t\t\tdevice.pushErrorScope(\"validation\");\n\t\t\t\ttry {\n\t\t\t\t\tcache.get(key, \"triangle-list\", \"none\", true, \"none\", {\n\t\t\t\t\t\tcullMode: \"none\",\n\t\t\t\t\t\tfrontFace: \"ccw\",\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t/* surfaced through the scope below */\n\t\t\t\t}\n\t\t\t\tdevice\n\t\t\t\t\t.popErrorScope()\n\t\t\t\t\t.then((error) => {\n\t\t\t\t\t\tif (error) {\n\t\t\t\t\t\t\tthis.wgslValid = false;\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t`GLShader${this.label ? ` (${this.label})` : \"\"}: WGSL module is incompatible with the mesh pipeline layout — falling back to the built-in mesh shading\\n  ${error.message}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {});\n\t\t\t}\n\t\t}\n\t\treturn key;\n\t}\n\n\t/**\n\t * destroy this shader objects resources (program, attributes, uniforms).\n\t * Idempotent — calling destroy twice (or after a context-lost suspend)\n\t * is safe. Unsubscribes from the renderer's context lost / restored\n\t * events so a destroyed shader is never automatically resurrected.\n\t */\n\tdestroy() {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.destroyed = true;\n\n\t\toff(ONCONTEXT_LOST, this._onContextLost, this);\n\t\toff(ONCONTEXT_RESTORED, this._onContextRestored, this);\n\n\t\tthis.uniforms = null;\n\t\tthis.attributes = null;\n\n\t\t// deleteProgram can throw on ANGLE/D3D11 — the 19.5.0 crash\n\t\tif (this.program !== null) {\n\t\t\ttry {\n\t\t\t\tthis.gl.deleteProgram(this.program);\n\t\t\t} catch {\n\t\t\t\t/* context already gone */\n\t\t\t}\n\t\t\tthis.program = null;\n\t\t}\n\n\t\tthis.vertex = null;\n\t\tthis.fragment = null;\n\t\tthis._sourceVertex = null;\n\t\tthis._sourceFragment = null;\n\t\tthis._uniformCache = null;\n\n\t\t// WGSL side: the GPU shader modules are owned by the pipeline cache\n\t\t// (shared with any other shader of the same text) — only drop the\n\t\t// registrations and the module source\n\t\tthis.wgsl = undefined;\n\t\tthis.isWebGPU = false;\n\t\tthis._wgslRegistrations = { epoch: -1, keys: new Map() };\n\t}\n}\n","// Current vertex point — z carries per-sprite depth (renderable.depth),\n// a no-op under the default ortho projection, used by perspective (Camera3d).\n// This is also the default vertex template used by ShaderEffect, so user-\n// defined fragment-only effects honor the renderable's depth automatically.\nattribute vec3 aVertex;\nattribute vec2 aRegion;\nattribute vec4 aColor;\n\n// Projection matrix\nuniform mat4 uProjectionMatrix;\n\nvarying vec2 vRegion;\nvarying vec4 vColor;\n\nvoid main(void) {\n    // Transform the vertex position by the projection matrix\n    gl_Position = uProjectionMatrix * vec4(aVertex, 1.0);\n    // Pass the remaining attributes to the fragment shader\n    vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n    vRegion = aRegion;\n}\n","import quadVertex from \"../webgl/shaders/quad.vert\";\n\n/*\n * The GLSL realization of a ShaderEffect body — the pure source-assembly\n * half of the WebGL path, extracted verbatim so the neutral ShaderEffect\n * class can dispatch per backend while the emitted GLSL stays\n * byte-identical (pinned by tests/effects_golden_glsl.spec.js).\n *\n * ---- Shader builtins -------------------------------------------------------\n *\n * Inside a ShaderEffect fragment body, three names get special treatment so\n * users never compute screen/frame UVs by hand:\n *\n * - `uniform sampler2D <name> : screen_texture;` — the engine strips the\n *   annotation and keeps the sampler filled with a capture of everything\n *   drawn so far (a back-buffer copy, via the renderer's shared\n *   toFrameTexture slot). An optional wrap mode is accepted:\n *   `: screen_texture(repeat)`.\n * - `screen_uv`  — varying with this fragment's position in that capture\n *   (0..1 across the screen).\n * - `noise_uv`   — varying with a frame-local coordinate across the drawn\n *   object (undoes atlas packing; scaled to object pixels so patterns keep\n *   their density when the destination is scaled).\n *\n * The builtins only activate when referenced, and never when the body\n * carries its OWN declaration of the identifier — a shader that used these\n * names before this feature keeps compiling unchanged.\n */\nconst SCREEN_TEXTURE_ANNOTATION =\n\t/\\buniform\\s+sampler2D\\s+([A-Za-z_]\\w*)\\s*:\\s*screen_texture(?:\\((repeat|repeat-x|repeat-y|no-repeat)\\))?\\s*;/g;\nconst SCREEN_UV_IDENTIFIER = /\\bscreen_uv\\b/;\nconst NOISE_UV_IDENTIFIER = /\\bnoise_uv\\b/;\n\n/**\n * whether the body declares `name` itself (as a varying/uniform/attribute) —\n * the engine then leaves that identifier fully user-managed\n * @ignore\n */\nfunction hasOwnDeclaration(source, name) {\n\treturn new RegExp(\n\t\t`\\\\b(?:varying|uniform|attribute)\\\\s+\\\\w+\\\\s+${name}\\\\s*;`,\n\t).test(source);\n}\n\n/**\n * Parse the builtin usages out of a fragment body: collect and strip the\n * `: screen_texture` annotations, and detect the `screen_uv` / `noise_uv`\n * varyings. Bodies without builtins pass through byte-identical.\n * @ignore\n */\nfunction parseShaderBuiltins(fragmentBody) {\n\tconst screenTextures = [];\n\tconst body = fragmentBody.replace(\n\t\tSCREEN_TEXTURE_ANNOTATION,\n\t\t(match, name, repeat = \"no-repeat\") => {\n\t\t\tscreenTextures.push({ name, repeat });\n\t\t\t// keep the plain `uniform sampler2D <name>;` declaration\n\t\t\treturn match.replace(/\\s*:\\s*screen_texture(?:\\([\\w-]+\\))?/, \"\");\n\t\t},\n\t);\n\tconst screenUV =\n\t\t(screenTextures.length > 0 || SCREEN_UV_IDENTIFIER.test(body)) &&\n\t\t!hasOwnDeclaration(body, \"screen_uv\");\n\tconst noiseUV =\n\t\tNOISE_UV_IDENTIFIER.test(body) && !hasOwnDeclaration(body, \"noise_uv\");\n\treturn { body, screenTextures, screenUV, noiseUV };\n}\n\n/**\n * The effect's vertex shader: the stock quad template, or — when a builtin\n * varying is in play — a variant that additionally computes `screen_uv`\n * (clip space → 0..1) and/or `noise_uv` (atlas UV → frame-local, fed by\n * `_setNoiseUVRect` through the `ME_*` uniforms). Built deterministically\n * from a template; user source is never rewritten.\n * @ignore\n */\nfunction buildEffectVertex(builtins) {\n\tif (!builtins.screenUV && !builtins.noiseUV) {\n\t\treturn quadVertex;\n\t}\n\treturn [\n\t\t\"attribute vec3 aVertex;\",\n\t\t\"attribute vec2 aRegion;\",\n\t\t\"attribute vec4 aColor;\",\n\t\t\"uniform mat4 uProjectionMatrix;\",\n\t\t\"varying vec2 vRegion;\",\n\t\t\"varying vec4 vColor;\",\n\t\t...(builtins.screenUV ? [\"varying vec2 screen_uv;\"] : []),\n\t\t...(builtins.noiseUV\n\t\t\t? [\n\t\t\t\t\t\"uniform vec2 ME_size_obj;\",\n\t\t\t\t\t\"uniform vec2 ME_size_img;\",\n\t\t\t\t\t\"uniform vec2 ME_offset;\",\n\t\t\t\t\t\"varying vec2 noise_uv;\",\n\t\t\t\t]\n\t\t\t: []),\n\t\t\"void main(void) {\",\n\t\t\"    vec4 ME_clip = uProjectionMatrix * vec4(aVertex, 1.0);\",\n\t\t\"    gl_Position = ME_clip;\",\n\t\t...(builtins.screenUV\n\t\t\t? [\"    screen_uv = ME_clip.xy / ME_clip.w * 0.5 + 0.5;\"]\n\t\t\t: []),\n\t\t...(builtins.noiseUV\n\t\t\t? [\n\t\t\t\t\t\"    noise_uv = aRegion * (ME_size_img / ME_size_obj) - ME_offset / ME_size_obj;\",\n\t\t\t\t]\n\t\t\t: []),\n\t\t\"    vColor = vec4(aColor.bgr * aColor.a, aColor.a);\",\n\t\t\"    vRegion = aRegion;\",\n\t\t\"}\",\n\t].join(\"\\n\");\n}\n\n/**\n * Assemble the complete GLSL program sources for a ShaderEffect fragment\n * body: parse the builtins, build the vertex variant, and wrap the user's\n * `apply()` with the texture-sampling boilerplate.\n * @param {string} fragmentBody - the user body (GLSL, `vec4 apply(vec4, vec2)` convention)\n * @returns {{vertex: string, fragment: string, screenTextures: Array<{name: string, repeat: string}>, noiseUV: boolean}} the assembled sources + builtin usage\n * @ignore\n */\nexport function buildGLSLProgram(fragmentBody) {\n\t// Shader builtins: parse & strip `: screen_texture` annotations,\n\t// detect the free `screen_uv` / `noise_uv` varyings (see the module\n\t// header). A body using none of them passes through untouched.\n\tconst builtins = parseShaderBuiltins(fragmentBody);\n\n\t// wrap the user's apply() with the texture-sampling boilerplate\n\tconst fragment = [\n\t\t\"uniform sampler2D uSampler;\",\n\t\t\"varying vec4 vColor;\",\n\t\t\"varying vec2 vRegion;\",\n\t\t...(builtins.screenUV ? [\"varying vec2 screen_uv;\"] : []),\n\t\t...(builtins.noiseUV ? [\"varying vec2 noise_uv;\"] : []),\n\t\tbuiltins.body,\n\t\t\"void main(void) {\",\n\t\t\"    vec4 texColor = texture2D(uSampler, vRegion) * vColor;\",\n\t\t\"    gl_FragColor = apply(texColor, vRegion);\",\n\t\t\"}\",\n\t].join(\"\\n\");\n\n\treturn {\n\t\tvertex: buildEffectVertex(builtins),\n\t\tfragment,\n\t\tscreenTextures: builtins.screenTextures,\n\t\tnoiseUV: builtins.noiseUV,\n\t};\n}\n","/**\n * WGSL uniform-address-space layout calculator.\n *\n * WebGPU has no uniform reflection: when `setUniform(\"uStrength\", v)` must\n * land at the right byte offset of an effect's uniform buffer, the offsets\n * have to be computed CPU-side from the struct the body declares. These are\n * the WGSL *uniform* address-space rules (WGSL spec §memory-layouts): every\n * member is aligned to its type's alignment, vec3 aligns like vec4, arrays\n * require a 16-byte-multiple element stride, and the struct itself rounds\n * up to 16 bytes.\n *\n * Kept deliberately to the types an effect uniform block meaningfully uses;\n * an unsupported type is a parse failure upstream (effect disables), never\n * a silently-wrong offset.\n * @ignore\n */\n\n/**\n * `{align, size}` per supported member type, keyed by the canonical\n * spelling. Aliases (`vec2<f32>` form) are normalized before lookup.\n * @ignore\n */\nconst TYPE_LAYOUT = {\n\tf32: { align: 4, size: 4 },\n\ti32: { align: 4, size: 4 },\n\tu32: { align: 4, size: 4 },\n\tvec2f: { align: 8, size: 8 },\n\t// vec3 aligns to 16 with 12 bytes of data — the classic layout trap\n\tvec3f: { align: 16, size: 12 },\n\tvec4f: { align: 16, size: 16 },\n\t// matCxR: C columns of vecR, column stride = roundUp(16, sizeof(vecR))\n\tmat3x3f: { align: 16, size: 48 },\n\tmat4x4f: { align: 16, size: 64 },\n};\n\nconst ARRAY_TYPE = /^array<\\s*vec4f\\s*,\\s*(\\d+)\\s*>$/;\n\n/**\n * normalize the `vecN<f32>` / `matCxR<f32>` spellings onto the short forms\n * used as TYPE_LAYOUT keys\n * @param {string} type - a WGSL type token\n * @returns {string} the canonical spelling\n * @ignore\n */\nexport function normalizeWGSLType(type) {\n\treturn type\n\t\t.replace(/\\s+/g, \"\")\n\t\t.replace(/^vec([234])<f32>$/, \"vec$1f\")\n\t\t.replace(/^mat([34])x([34])<f32>$/, \"mat$1x$2f\");\n}\n\n/**\n * round `value` up to the next multiple of `alignment` (a power of two)\n * @ignore\n */\nfunction roundUp(alignment, value) {\n\treturn (value + alignment - 1) & ~(alignment - 1);\n}\n\n/**\n * Compute the uniform-buffer layout for a parsed struct member list.\n * @param {Array<{name: string, type: string}>} members - struct members in declaration order\n * @returns {{map: Map<string, {offset: number, size: number, type: string}>, size: number}|null}\n *   the name → placement map and the total (16-byte-rounded) struct size,\n *   or `null` when a member type is unsupported\n * @ignore\n */\nexport function computeUniformLayout(members) {\n\tconst map = new Map();\n\tlet cursor = 0;\n\tfor (const member of members) {\n\t\tconst type = normalizeWGSLType(member.type);\n\t\tlet layout = TYPE_LAYOUT[type];\n\t\tif (typeof layout === \"undefined\") {\n\t\t\tconst array = ARRAY_TYPE.exec(type);\n\t\t\tif (array === null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tlayout = { align: 16, size: 16 * parseInt(array[1], 10) };\n\t\t}\n\t\tconst offset = roundUp(layout.align, cursor);\n\t\tmap.set(member.name, { offset, size: layout.size, type });\n\t\tcursor = offset + layout.size;\n\t}\n\treturn { map, size: roundUp(16, cursor) };\n}\n","import { computeUniformLayout } from \"./layout.js\";\n\n/**\n * Declaration-only parser for a WGSL ShaderEffect body.\n *\n * The body is real WGSL, compiled verbatim inside the engine scaffold —\n * nothing is rewritten. This parser only reads declarations to learn what\n * the engine must provide around it:\n *\n * - `fn apply(color : vec4f, uv : vec2f) -> vec4f` — required entry point\n * - at most one `@group(3) @binding(0) var<uniform> <name> : <Struct>;`\n *   whose struct members become the `setUniform` names (offsets computed\n *   by the layout calculator — WebGPU has no reflection)\n * - extra `setTexture` samplers as texture/sampler PAIRS at explicit\n *   consecutive bindings: `@group(3) @binding(k) var t : texture_2d<f32>;`\n *   immediately followed by `@binding(k+1) var s : sampler;` (k ≥ 1)\n * - builtin identifiers activated on reference: `screen_uv`, `noise_uv`,\n *   `screen_texture` (+ `screen_sampler` / `screen_sampler_repeat`)\n *\n * Every failure mode returns `{ok: false, error}` — the effect then warns\n * and disables (the generalized missing-language behavior), it never\n * throws and never guesses at offsets.\n * @ignore\n */\n\nconst APPLY_FN = /\\bfn\\s+apply\\s*\\(/;\nconst UNIFORM_DECL =\n\t/@group\\(\\s*3\\s*\\)\\s*@binding\\(\\s*0\\s*\\)\\s*var\\s*<\\s*uniform\\s*>\\s*([A-Za-z_]\\w*)\\s*:\\s*([A-Za-z_]\\w*)\\s*;/;\nconst RESOURCE_DECL =\n\t/@group\\(\\s*3\\s*\\)\\s*@binding\\(\\s*(\\d+)\\s*\\)\\s*var\\s+([A-Za-z_]\\w*)\\s*:\\s*(texture_2d<f32>|sampler)\\s*;/g;\nconst GROUP3_ANY = /@group\\(\\s*3\\s*\\)\\s*@binding\\(\\s*(\\d+)\\s*\\)/g;\n\n/**\n * strip `//` line and `/* *\\/` block comments so declarations inside\n * comments are never honored (scratch copy — the original compiles).\n * A plain single-pass scanner rather than a regex: comment-matching\n * patterns are where polynomial backtracking hides (ReDoS), and a\n * character scan is provably linear.\n * @ignore\n */\nfunction stripComments(source) {\n\tconst parts = [];\n\tconst length = source.length;\n\tlet start = 0;\n\tlet i = 0;\n\twhile (i < length) {\n\t\tif (source[i] === \"/\" && source[i + 1] === \"/\") {\n\t\t\tparts.push(source.slice(start, i), \" \");\n\t\t\ti += 2;\n\t\t\twhile (i < length && source[i] !== \"\\n\") {\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tstart = i;\n\t\t} else if (source[i] === \"/\" && source[i + 1] === \"*\") {\n\t\t\tparts.push(source.slice(start, i), \" \");\n\t\t\ti += 2;\n\t\t\t// WGSL block comments NEST — track depth\n\t\t\tlet depth = 1;\n\t\t\twhile (i < length && depth > 0) {\n\t\t\t\tif (source[i] === \"/\" && source[i + 1] === \"*\") {\n\t\t\t\t\tdepth++;\n\t\t\t\t\ti += 2;\n\t\t\t\t} else if (source[i] === \"*\" && source[i + 1] === \"/\") {\n\t\t\t\t\tdepth--;\n\t\t\t\t\ti += 2;\n\t\t\t\t} else {\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tstart = i;\n\t\t} else {\n\t\t\ti++;\n\t\t}\n\t}\n\tparts.push(source.slice(start));\n\treturn parts.join(\"\");\n}\n\n/**\n * split a struct body on top-level commas only — `array<vec4f, 4>`\n * carries a comma inside its angle brackets and must stay one member\n * @param {string} body - the text between the struct's braces\n * @returns {string[]} one entry per member declaration\n * @ignore\n */\nfunction splitMembers(body) {\n\tconst members = [];\n\tlet depth = 0;\n\tlet start = 0;\n\tfor (let i = 0; i < body.length; i++) {\n\t\tconst character = body[i];\n\t\tif (character === \"<\") {\n\t\t\tdepth++;\n\t\t} else if (character === \">\") {\n\t\t\tdepth--;\n\t\t} else if (character === \",\" && depth === 0) {\n\t\t\tmembers.push(body.slice(start, i));\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\tmembers.push(body.slice(start));\n\treturn members;\n}\n\n/**\n * whether the body declares the identifier itself (a `var`/`let`/`const`\n * or struct member of that name) — the builtin then stays user-managed,\n * same contract as the GLSL side\n * @ignore\n */\nfunction hasOwnDeclaration(source, name) {\n\treturn new RegExp(\n\t\t`(?:\\\\bvar\\\\s*(?:<[^>]*>)?\\\\s+|\\\\blet\\\\s+|\\\\bconst\\\\s+)${name}\\\\s*[:=]`,\n\t).test(source);\n}\n\n/**\n * Parse a WGSL effect body's declarations.\n * @param {string} body - the user WGSL body\n * @returns {object} `{ok: true, layout, structSize, uniformVar, textures, builtins, maxUserBinding}`\n *   or `{ok: false, error}`\n * @ignore\n */\nexport function parseWGSLBody(body) {\n\tconst source = stripComments(body);\n\n\tif (!APPLY_FN.test(source)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror:\n\t\t\t\t\"a WGSL effect body must define `fn apply(color : vec4f, uv : vec2f) -> vec4f`\",\n\t\t};\n\t}\n\n\t// ---- the (optional) uniform struct at @group(3) @binding(0) ----------\n\tlet layout = new Map();\n\tlet structSize = 0;\n\tlet uniformVar = null;\n\tconst uniform = UNIFORM_DECL.exec(source);\n\tif (uniform !== null) {\n\t\tconst structName = uniform[2];\n\t\tconst struct = new RegExp(`struct\\\\s+${structName}\\\\s*\\\\{([^}]*)\\\\}`).exec(\n\t\t\tsource,\n\t\t);\n\t\tif (struct === null) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `uniform struct \\`${structName}\\` is not declared in the body`,\n\t\t\t};\n\t\t}\n\t\tconst members = [];\n\t\tfor (const entry of splitMembers(struct[1])) {\n\t\t\tconst trimmed = entry.trim();\n\t\t\tif (trimmed === \"\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst member = /^([A-Za-z_]\\w*)\\s*:\\s*([A-Za-z0-9_<>,\\s]+)$/.exec(\n\t\t\t\ttrimmed,\n\t\t\t);\n\t\t\tif (member === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `unsupported struct member declaration \\`${trimmed}\\``,\n\t\t\t\t};\n\t\t\t}\n\t\t\tmembers.push({ name: member[1], type: member[2].trim() });\n\t\t}\n\t\tconst computed = computeUniformLayout(members);\n\t\tif (computed === null) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `unsupported uniform member type in struct \\`${structName}\\` (supported: f32, i32, u32, vec2f, vec3f, vec4f, mat3x3f, mat4x4f, array<vec4f, N>)`,\n\t\t\t};\n\t\t}\n\t\tlayout = computed.map;\n\t\tstructSize = computed.size;\n\t\tuniformVar = uniform[1];\n\t}\n\n\t// ---- extra texture/sampler pairs at explicit group-3 bindings ---------\n\tconst resources = [];\n\tRESOURCE_DECL.lastIndex = 0;\n\tfor (const match of source.matchAll(RESOURCE_DECL)) {\n\t\tresources.push({\n\t\t\tbinding: parseInt(match[1], 10),\n\t\t\tname: match[2],\n\t\t\tkind: match[3] === \"sampler\" ? \"sampler\" : \"texture\",\n\t\t});\n\t}\n\tresources.sort((a, b) => {\n\t\treturn a.binding - b.binding;\n\t});\n\tconst textures = [];\n\tconst seen = new Set();\n\tfor (let i = 0; i < resources.length; i++) {\n\t\tconst resource = resources[i];\n\t\tif (resource.binding === 0 || seen.has(resource.binding)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `invalid or duplicate @group(3) @binding(${resource.binding})`,\n\t\t\t};\n\t\t}\n\t\tseen.add(resource.binding);\n\t\tif (resource.kind === \"texture\") {\n\t\t\tconst sampler = resources[i + 1];\n\t\t\tif (\n\t\t\t\ttypeof sampler === \"undefined\" ||\n\t\t\t\tsampler.kind !== \"sampler\" ||\n\t\t\t\tsampler.binding !== resource.binding + 1\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: `texture \\`${resource.name}\\` needs its sampler at @binding(${resource.binding + 1})`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tseen.add(sampler.binding);\n\t\t\ttextures.push({\n\t\t\t\tname: resource.name,\n\t\t\t\tbinding: resource.binding,\n\t\t\t\tsamplerName: sampler.name,\n\t\t\t\tsamplerBinding: sampler.binding,\n\t\t\t});\n\t\t\ti++;\n\t\t} else {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `sampler \\`${resource.name}\\` at @binding(${resource.binding}) has no texture at @binding(${resource.binding - 1})`,\n\t\t\t};\n\t\t}\n\t}\n\n\t// any OTHER group-3 binding the regexes didn't classify is a shape we\n\t// can't build a bind group for\n\tlet maxUserBinding = 0;\n\tGROUP3_ANY.lastIndex = 0;\n\tfor (const match of source.matchAll(GROUP3_ANY)) {\n\t\tconst binding = parseInt(match[1], 10);\n\t\tif (binding !== 0 && !seen.has(binding)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `unsupported declaration at @group(3) @binding(${binding}) (only one uniform struct at binding 0 and texture/sampler pairs are supported)`,\n\t\t\t};\n\t\t}\n\t\tmaxUserBinding = Math.max(maxUserBinding, binding);\n\t}\n\n\t// ---- builtins, activated on reference -------------------------------\n\tconst screenTexture =\n\t\t/\\bscreen_texture\\b/.test(source) &&\n\t\t!hasOwnDeclaration(source, \"screen_texture\");\n\tconst builtins = {\n\t\tscreenTexture,\n\t\tscreenSamplerClamp: /\\bscreen_sampler\\b/.test(source),\n\t\tscreenSamplerRepeat: /\\bscreen_sampler_repeat\\b/.test(source),\n\t\tscreenUV:\n\t\t\t(screenTexture || /\\bscreen_uv\\b/.test(source)) &&\n\t\t\t!hasOwnDeclaration(source, \"screen_uv\"),\n\t\tnoiseUV:\n\t\t\t/\\bnoise_uv\\b/.test(source) && !hasOwnDeclaration(source, \"noise_uv\"),\n\t\t// the interpolated tint (the GLSL `vColor` varying) — bodies that\n\t\t// re-sample the source texture reference it to re-apply the tint\n\t\tvColor: /\\bvColor\\b/.test(source) && !hasOwnDeclaration(source, \"vColor\"),\n\t};\n\tif (\n\t\tbuiltins.screenTexture &&\n\t\t!builtins.screenSamplerClamp &&\n\t\t!builtins.screenSamplerRepeat\n\t) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror:\n\t\t\t\t\"`screen_texture` is sampled through `screen_sampler` (clamp) or `screen_sampler_repeat` — reference one of them\",\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tlayout,\n\t\tstructSize,\n\t\tuniformVar,\n\t\ttextures,\n\t\tbuiltins,\n\t\tmaxUserBinding,\n\t};\n}\n","/**\n * WGSL module assembly for a ShaderEffect body — the WGSL counterpart of\n * `glsl_realization.js`'s source templating. The user body is embedded\n * VERBATIM (WGSL module-scope declarations are order-independent); the\n * scaffold contributes the vertex/fragment entry points over the frozen\n * 28-byte quad layout, the frame-globals and material groups, and the\n * builtin declarations the parser detected.\n *\n * Frozen conventions carried over from the core quad shader:\n * - clip-z remap `(z + w) * 0.5` (GL-convention [-w, w] → WebGPU [0, w])\n * - packed-ARGB attribute read as (B,G,R,A) → `.bgr * .a` premultiply\n * - `screen_uv` is y-down (`0.5 - ndc.y * 0.5`): WebGPU texture row 0 is\n *   the top, so the capture is sampled without any flip\n * @ignore\n */\n\n/**\n * builtin group-3 bindings are assigned ABOVE the highest user binding so\n * they can never collide: ME uniform first, then the capture texture and\n * its sampler variants\n * @param {object} parsed - the parseWGSLBody result\n * @returns {{me: number, screenTexture: number, screenSamplerClamp: number, screenSamplerRepeat: number}}\n *   assigned binding indices (-1 when the builtin is unused)\n * @ignore\n */\nexport function assignBuiltinBindings(parsed) {\n\tlet next =\n\t\tMath.max(parsed.maxUserBinding, parsed.structSize > 0 ? 0 : -1) + 1;\n\tconst bindings = {\n\t\tme: -1,\n\t\tscreenTexture: -1,\n\t\tscreenSamplerClamp: -1,\n\t\tscreenSamplerRepeat: -1,\n\t};\n\tif (parsed.builtins.noiseUV) {\n\t\tbindings.me = next++;\n\t}\n\tif (parsed.builtins.screenTexture) {\n\t\tbindings.screenTexture = next++;\n\t\tif (parsed.builtins.screenSamplerClamp) {\n\t\t\tbindings.screenSamplerClamp = next++;\n\t\t}\n\t\tif (parsed.builtins.screenSamplerRepeat) {\n\t\t\tbindings.screenSamplerRepeat = next;\n\t\t}\n\t}\n\treturn bindings;\n}\n\n/**\n * Assemble the complete WGSL module for an effect body.\n * @param {string} body - the user WGSL body (embedded verbatim)\n * @param {object} parsed - the parseWGSLBody result\n * @returns {{code: string, bindings: object}} the module text and the\n *   builtin binding assignment (consumed when building the bind group)\n * @ignore\n */\nexport function buildWGSLModule(body, parsed) {\n\tconst builtins = parsed.builtins;\n\tconst bindings = assignBuiltinBindings(parsed);\n\n\tconst scaffold = [\n\t\t\"// ---- melonJS effect scaffold ----\",\n\t\t\"struct FrameUniforms {\",\n\t\t\"\\tprojection : mat4x4<f32>,\",\n\t\t\"\\tlineWidth : f32,\",\n\t\t\"};\",\n\t\t\"@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\",\n\t\t\"@group(1) @binding(0) var uTexture : texture_2d<f32>;\",\n\t\t\"@group(1) @binding(1) var uSampler : sampler;\",\n\t];\n\n\tif (builtins.screenUV) {\n\t\tscaffold.push(\"var<private> screen_uv : vec2f;\");\n\t}\n\tif (builtins.vColor) {\n\t\t// the interpolated tint, exposed under its GLSL varying name so\n\t\t// re-sampling bodies port literally\n\t\tscaffold.push(\"var<private> vColor : vec4f;\");\n\t}\n\tif (builtins.noiseUV) {\n\t\tscaffold.push(\n\t\t\t\"var<private> noise_uv : vec2f;\",\n\t\t\t\"struct MEBuiltins {\",\n\t\t\t\"\\tsize_obj : vec2f,\",\n\t\t\t\"\\tsize_img : vec2f,\",\n\t\t\t\"\\toffset : vec2f,\",\n\t\t\t\"};\",\n\t\t\t`@group(3) @binding(${bindings.me}) var<uniform> ME : MEBuiltins;`,\n\t\t);\n\t}\n\tif (builtins.screenTexture) {\n\t\tscaffold.push(\n\t\t\t`@group(3) @binding(${bindings.screenTexture}) var screen_texture : texture_2d<f32>;`,\n\t\t);\n\t\tif (builtins.screenSamplerClamp) {\n\t\t\tscaffold.push(\n\t\t\t\t`@group(3) @binding(${bindings.screenSamplerClamp}) var screen_sampler : sampler;`,\n\t\t\t);\n\t\t}\n\t\tif (builtins.screenSamplerRepeat) {\n\t\t\tscaffold.push(\n\t\t\t\t`@group(3) @binding(${bindings.screenSamplerRepeat}) var screen_sampler_repeat : sampler;`,\n\t\t\t);\n\t\t}\n\t}\n\n\tscaffold.push(\n\t\t\"struct VSOut {\",\n\t\t\"\\t@builtin(position) position : vec4f,\",\n\t\t\"\\t@location(0) vRegion : vec2f,\",\n\t\t\"\\t@location(1) vColor : vec4f,\",\n\t\t...(builtins.screenUV ? [\"\\t@location(2) vScreenUV : vec2f,\"] : []),\n\t\t...(builtins.noiseUV ? [\"\\t@location(3) vNoiseUV : vec2f,\"] : []),\n\t\t\"};\",\n\t\t\"\",\n\t\t\"@vertex\",\n\t\t\"fn vertex_main(\",\n\t\t\"\\t@location(0) aVertex : vec3f,\",\n\t\t\"\\t@location(1) aRegion : vec2f,\",\n\t\t\"\\t@location(2) aColor : vec4f,\",\n\t\t\"\\t@location(3) aTextureId : f32,\",\n\t\t\") -> VSOut {\",\n\t\t\"\\tvar out : VSOut;\",\n\t\t\"\\tlet clip = uFrame.projection * vec4f(aVertex, 1.0);\",\n\t\t\"\\t// GL-convention clip z in [-w, w] -> WebGPU [0, w]\",\n\t\t\"\\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\",\n\t\t\"\\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\",\n\t\t\"\\tout.vRegion = aRegion;\",\n\t\t...(builtins.screenUV\n\t\t\t? [\n\t\t\t\t\t\"\\tlet ndc = clip.xy / clip.w;\",\n\t\t\t\t\t\"\\t// y-down: capture row 0 = screen top under WebGPU\",\n\t\t\t\t\t\"\\tout.vScreenUV = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\",\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(builtins.noiseUV\n\t\t\t? [\n\t\t\t\t\t\"\\tout.vNoiseUV = aRegion * (ME.size_img / ME.size_obj) - ME.offset / ME.size_obj;\",\n\t\t\t\t]\n\t\t\t: []),\n\t\t\"\\treturn out;\",\n\t\t\"}\",\n\t\t\"\",\n\t\t\"// ---- user body (verbatim) ----\",\n\t\tbody,\n\t\t\"\",\n\t\t\"@fragment\",\n\t\t\"fn fragment_main(in : VSOut) -> @location(0) vec4f {\",\n\t\t...(builtins.screenUV ? [\"\\tscreen_uv = in.vScreenUV;\"] : []),\n\t\t...(builtins.noiseUV ? [\"\\tnoise_uv = in.vNoiseUV;\"] : []),\n\t\t...(builtins.vColor ? [\"\\tvColor = in.vColor;\"] : []),\n\t\t\"\\tlet texColor = textureSample(uTexture, uSampler, in.vRegion) * in.vColor;\",\n\t\t\"\\treturn apply(texColor, in.vRegion);\",\n\t\t\"}\",\n\t);\n\n\treturn { code: scaffold.join(\"\\n\"), bindings };\n}\n","import { parseWGSLBody } from \"./wgsl/parse.js\";\nimport { buildWGSLModule } from \"./wgsl/scaffold.js\";\n\n/**\n * The WGSL realization of a ShaderEffect — the WebGPU counterpart of the\n * GLShader the GLSL branch compiles.\n *\n * WebGPU has no uniform reflection, so this object carries everything the\n * backend needs to drive the effect: the parsed uniform layout (name →\n * byte placement), a CPU-side mirror of the uniform block that\n * `setUniform` writes into, the texture binding table, and the assembled\n * module text. GPU objects (shader module, bind group layout, bind group)\n * are built lazily by the renderer's effect path — uniform VALUES are\n * snapshotted from the mirror into a per-frame arena at every bind, so a\n * shared effect bound twice in a frame with different values stays\n * correct under WebGPU's queue-write-before-draws ordering.\n * @ignore\n */\nexport default class WGSLEffectRealization {\n\t/**\n\t * Parse + assemble; on any parse failure `valid` is false and `error`\n\t * carries the reason (the effect then warns and stays disabled).\n\t * @param {string} body - the user WGSL body\n\t */\n\tconstructor(body) {\n\t\tconst parsed = parseWGSLBody(body);\n\n\t\t/**\n\t\t * whether the body parsed into a realizable shape\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.valid = parsed.ok === true;\n\t\t/** @type {string|null} */\n\t\tthis.error = parsed.ok === true ? null : parsed.error;\n\n\t\tif (!this.valid) {\n\t\t\treturn;\n\t\t}\n\n\t\t/** name → {offset, size, type} placement in the uniform block */\n\t\tthis.layout = parsed.layout;\n\t\t/** total uniform block byte size (0 = no uniform struct) */\n\t\tthis.structSize = parsed.structSize;\n\t\t/** user texture/sampler pairs (binding table) */\n\t\tthis.textures = parsed.textures;\n\t\t/** builtin usage flags */\n\t\tthis.builtins = parsed.builtins;\n\n\t\tconst module = buildWGSLModule(body, parsed);\n\t\t/** the complete assembled WGSL module text */\n\t\tthis.code = module.code;\n\t\t/** builtin binding assignment (ME / capture texture / samplers) */\n\t\tthis.builtinBindings = module.bindings;\n\n\t\t// CPU mirror of the uniform block; snapshot-uploaded per bind\n\t\tthis.cpu = new ArrayBuffer(Math.max(this.structSize, 0));\n\t\tthis.f32 = new Float32Array(this.cpu);\n\t\tthis.i32 = new Int32Array(this.cpu);\n\t\tthis.u32 = new Uint32Array(this.cpu);\n\n\t\t// CPU mirror of the engine `MEBuiltins` struct (noise_uv): size_obj\n\t\t// at float 0, size_img at 2, offset at 4; 32 bytes with tail padding\n\t\tthis.meMirror = new Float32Array(8);\n\n\t\t/**\n\t\t * last user-set value per uniform name — the clone-replay source\n\t\t * (exact values, not mirror read-back)\n\t\t * @type {Map<string, number|number[]>}\n\t\t */\n\t\tthis.values = new Map();\n\n\t\t// names already warned about, so a per-frame setUniform on an\n\t\t// unknown name doesn't flood the console\n\t\tthis.warned = new Set();\n\n\t\t/**\n\t\t * device-scoped GPU state, built lazily by the renderer's effect\n\t\t * path and invalidated via the pipeline-cache epoch (device loss)\n\t\t * @type {object|null}\n\t\t */\n\t\tthis.gpu = null;\n\t}\n\n\t/**\n\t * Write a uniform value into the CPU mirror.\n\t * @param {string} name - a struct member name of the body's uniform block\n\t * @param {number|number[]|Float32Array|{val: Float32Array}} value - the value\n\t *   (objects exposing `.val` — Matrix2d/3d, ColorMatrix — are unwrapped)\n\t * @returns {boolean} true when the name exists and the value was written\n\t */\n\tsetUniform(name, value) {\n\t\tconst placement = this.layout.get(name);\n\t\tif (typeof placement === \"undefined\") {\n\t\t\tif (!this.warned.has(name)) {\n\t\t\t\tthis.warned.add(name);\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`ShaderEffect: unknown WGSL uniform \"${name}\" (not a member of the effect's uniform struct)`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\t// unwrap matrix-like objects the GLSL path also accepts\n\t\tif (\n\t\t\tvalue !== null &&\n\t\t\ttypeof value === \"object\" &&\n\t\t\t!Array.isArray(value) &&\n\t\t\tArrayBuffer.isView(value) === false &&\n\t\t\ttypeof value.val !== \"undefined\"\n\t\t) {\n\t\t\tvalue = value.val;\n\t\t}\n\t\t// GLSL accepts booleans for bool/int uniforms — normalize\n\t\tif (typeof value === \"boolean\") {\n\t\t\tvalue = value ? 1 : 0;\n\t\t}\n\t\tconst index = placement.offset >> 2;\n\t\tif (typeof value === \"number\") {\n\t\t\tif (placement.type === \"i32\") {\n\t\t\t\tthis.i32[index] = value | 0;\n\t\t\t} else if (placement.type === \"u32\") {\n\t\t\t\tthis.u32[index] = value >>> 0;\n\t\t\t} else {\n\t\t\t\tthis.f32[index] = value;\n\t\t\t}\n\t\t\tthis.values.set(name, value);\n\t\t} else if (placement.type === \"mat3x3f\" && value.length === 9) {\n\t\t\t// a 9-float column-major matrix (the uniformMatrix3fv shape):\n\t\t\t// WGSL mat3x3f columns are vec4-strided — place each column at\n\t\t\t// float offset c*4, or columns 2-3 read scrambled\n\t\t\tfor (let column = 0; column < 3; column++) {\n\t\t\t\tfor (let row = 0; row < 3; row++) {\n\t\t\t\t\tthis.f32[index + column * 4 + row] = value[column * 3 + row];\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.values.set(name, Array.from(value));\n\t\t} else {\n\t\t\tconst array = value;\n\t\t\tconst count = Math.min(array.length, placement.size >> 2);\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tthis.f32[index + i] = array[i];\n\t\t\t}\n\t\t\tthis.values.set(name, Array.from(array));\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * whether the uniform block declares the given member (the WGSL\n\t * counterpart of the GLSL active-uniform check `setTime` relies on)\n\t * @param {string} name - member name\n\t * @returns {boolean} true when present\n\t */\n\thasUniform(name) {\n\t\treturn this.layout.has(name);\n\t}\n\n\t/**\n\t * drop device-scoped state (device loss / destroy) — the CPU mirror\n\t * and values survive, so nothing needs replaying on restore\n\t */\n\treleaseGPU() {\n\t\tthis.gpu = null;\n\t}\n}\n","import {\n\tONCONTEXT_LOST,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n} from \"../../system/event.ts\";\nimport Texture2d from \"../texture/texture2d.ts\";\nimport GLShader from \"../webgl/glshader.js\";\nimport { buildGLSLProgram } from \"./glsl_realization.js\";\nimport WGSLEffectRealization from \"./wgsl_realization.js\";\n\n/**\n * A simplified shader class for applying custom fragment effects to renderables.\n * Only requires a fragment `apply()` function — the vertex shader, uniforms, and\n * texture sampling boilerplate are handled automatically.\n *\n * ## Dual-language bodies\n *\n * An effect body is written in the active renderer's shading language:\n * GLSL on the WebGL renderer, WGSL on the WebGPU renderer. Pass a plain\n * string for a GLSL-only effect (the historical form), or one body per\n * language for an effect that runs on both backends:\n *\n * ```js\n * new ShaderEffect(renderer, { glsl: glslBody, wgsl: wgslBody });\n * ```\n *\n * The renderer compiles the body matching its\n * {@link Renderer#shaderLanguage}. When no matching body exists — a\n * GLSL-only effect on the WebGPU renderer, any effect on the Canvas\n * renderer — the effect warns once and stays **disabled**\n * (`enabled === false`, every method a safe no-op): the scene renders\n * without the effect, it never breaks.\n *\n * ## The WGSL convention\n *\n * A WGSL body mirrors the GLSL one — declarations plus an apply function,\n * compiled verbatim inside engine boilerplate:\n *\n * - `fn apply(color : vec4f, uv : vec2f) -> vec4f` — required; receives\n *   the sampled, tinted pixel and its UV, returns the modified color.\n * - Uniforms are the members of ONE struct bound as\n *   `@group(3) @binding(0) var<uniform> fx : MyUniforms;` — member names\n *   are the {@link ShaderEffect#setUniform} names, so a dual-language\n *   effect uses the same uniform names in both bodies and one\n *   `setUniform` call serves both. Supported member types: `f32`, `i32`,\n *   `u32`, `vec2f`, `vec3f`, `vec4f`, `mat3x3f`, `mat4x4f`,\n *   `array<vec4f, N>`.\n * - Extra {@link ShaderEffect#setTexture} samplers are texture/sampler\n *   pairs at explicit consecutive group-3 bindings (from 1):\n *   `@group(3) @binding(1) var uNoise : texture_2d<f32>;`\n *   `@group(3) @binding(2) var uNoiseSampler : sampler;`\n * - The source texture is available as `uTexture` with `uSampler`\n *   (`textureSample(uTexture, uSampler, uv)` — the WGSL spelling of\n *   GLSL's `texture2D(uSampler, uv)`), and the interpolated tint as\n *   `vColor`, under the same names as the GLSL side.\n * - The shader builtins keep their names: `screen_uv`, `noise_uv`, and\n *   `screen_texture` — sampled through `screen_sampler` (clamped) or\n *   `screen_sampler_repeat` (wrapping), replacing the GLSL\n *   `: screen_texture(repeat)` annotation.\n * - Porting note: a texture sampled after a non-uniform `return` or\n *   inside a varying branch must use\n *   `textureSampleLevel(uTexture, uSampler, uv, 0.0)` (a WGSL\n *   uniform-control-flow rule; identical output for sprite textures).\n *\n * ## Directional UV arithmetic (`uUVYDir`)\n *\n * The vertical orientation of `apply()`'s UV space depends on the draw\n * path: sampling the sprite directly, `uv.y` grows downward, but the\n * WebGL multi-effect (pooled) path composites through capture FBOs whose\n * rows are bottom-up — there `uv.y` grows upward. A body that offsets its\n * sampling coordinate vertically (a drop shadow, a directional smear)\n * would render mirrored on that path. Declare a `float uUVYDir` uniform\n * (WGSL: `uUVYDir : f32` in the uniform struct) and multiply vertical UV\n * offsets by it: the renderer feeds `+1` where `uv.y` grows downward —\n * including every WebGPU path — and `-1` on the WebGL pooled path, so\n * \"down\" stays down everywhere. Initialize it to `1.0` with `setUniform`;\n * bodies that don't declare it are unaffected. The built-in\n * {@link DropShadowEffect} is the reference use.\n * @category Rendering\n * @example\n * // one effect, both backends: dual-language body\n * mySprite.shader = new ShaderEffect(renderer, {\n *     glsl: `\n *         uniform float uStrength;\n *         vec4 apply(vec4 color, vec2 uv) {\n *             return vec4(color.rgb * uStrength, color.a);\n *         }\n *     `,\n *     wgsl: `\n *         struct Fx { uStrength : f32, };\n *         @group(3) @binding(0) var<uniform> fx : Fx;\n *         fn apply(color : vec4f, uv : vec2f) -> vec4f {\n *             return vec4f(color.rgb * fx.uStrength, color.a);\n *         }\n *     `,\n * });\n * mySprite.shader.setUniform(\"uStrength\", 0.5); // sets either backend\n * @example\n * // create a grayscale effect\n * mySprite.shader = new ShaderEffect(renderer, `\n *     vec4 apply(vec4 color, vec2 uv) {\n *         float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n *         return vec4(vec3(gray), color.a);\n *     }\n * `);\n * @example\n * // create an effect with a custom uniform\n * const pulse = new ShaderEffect(renderer, `\n *     uniform float uTime;\n *     vec4 apply(vec4 color, vec2 uv) {\n *         float brightness = 0.8 + 0.2 * sin(uTime * 3.0);\n *         return vec4(color.rgb * brightness, color.a);\n *     }\n * `);\n * mySprite.shader = pulse;\n * // update the uniform each frame\n * pulse.setUniform(\"uTime\", time);\n * @example\n * // Shader builtins — no JS plumbing, no UV math:\n * // `: screen_texture` keeps the sampler filled with everything drawn so\n * // far (a back-buffer copy; one screen copy per draw of the effect),\n * // `screen_uv` is this fragment's 0..1 screen position, and `noise_uv`\n * // runs 0..1 across the sprite regardless of its atlas frame.\n * const water = new ShaderEffect(renderer, `\n *     uniform sampler2D uNoise;\n *     uniform sampler2D screenTex : screen_texture;\n *     uniform float uTime;\n *     vec4 apply(vec4 color, vec2 uv) {\n *         vec2 flow = texture2D(uNoise, noise_uv + uTime * 0.25).rg;\n *         vec4 refracted = texture2D(screenTex, screen_uv + flow * 0.005);\n *         return refracted * texture2D(uSampler, uv + flow * 0.005);\n *     }\n * `);\n * water.setTexture(\"uNoise\", noiseTexture.getTexture(), \"repeat\");\n */\nexport default class ShaderEffect {\n\t/**\n\t * whether this effect is active (false in Canvas mode, false after\n\t * {@link destroy}, and false while the WebGL context is suspended\n\t * between an `ONCONTEXT_LOST` and the matching `ONCONTEXT_RESTORED`\n\t * event).\n\t * @type {boolean}\n\t */\n\tenabled = false;\n\n\t/**\n\t * `true` once {@link destroy} has been called. Distinct from\n\t * `enabled` — which also toggles transiently across a context\n\t * lost / restored cycle — to give callers a stable signal for\n\t * \"this effect has been explicitly released.\"\n\t * @type {boolean}\n\t * @readonly\n\t */\n\tdestroyed = false;\n\n\t/**\n\t * When `true`, a renderable will NOT auto-destroy this effect when it is\n\t * removed from its `postEffects` (via the `shader` setter,\n\t * {@link Renderable#removePostEffect}, {@link Renderable#clearPostEffects})\n\t * or when the renderable itself is destroyed. Set this on an effect shared\n\t * across several renderables so one of them going away doesn't free the GL\n\t * program still used by the others — you then own its lifecycle and call\n\t * {@link destroy} yourself.\n\t * @type {boolean}\n\t * @default false\n\t */\n\tshared = false;\n\n\t/**\n\t * @param {WebGLRenderer|WebGPURenderer|CanvasRenderer} renderer - the current renderer instance\n\t * @param {string|{glsl?: string, wgsl?: string}} body - the effect body:\n\t *   a GLSL string (containing a `vec4 apply(vec4 color, vec2 uv)` function —\n\t *   unchanged from previous versions), or an object carrying one body per\n\t *   shading language (`glsl` and/or `wgsl`, the WGSL body defining\n\t *   `fn apply(color : vec4f, uv : vec2f) -> vec4f`). The renderer picks the\n\t *   body matching its {@link Renderer#shaderLanguage}; when no matching body\n\t *   exists the effect warns once and stays disabled (`enabled === false`),\n\t *   exactly like the Canvas renderer.\n\t * @param {string} [precision=auto detected] - float precision ('lowp', 'mediump' or 'highp'), GLSL only\n\t */\n\tconstructor(renderer, body, precision) {\n\t\t/**\n\t\t * the renderer this effect was created for — kept so destroy and\n\t\t * context-loss can release the texture units reserved on its cache\n\t\t * for extra samplers ({@link setTexture}), and so {@link clone} can\n\t\t * compile an independent copy\n\t\t * @ignore\n\t\t */\n\t\tthis._renderer = renderer;\n\n\t\t/**\n\t\t * the construction \"recipe\" (body + precision), kept VERBATIM so\n\t\t * {@link clone} can compile an independent copy. Stored before the\n\t\t * disabled-stub early return so cloning behaves consistently there too.\n\t\t * @ignore\n\t\t */\n\t\tthis._fragmentBody = body;\n\t\t/** @ignore */\n\t\tthis._precision = precision;\n\n\t\t// resolve the body matching this renderer's shading language: a bare\n\t\t// string keeps meaning GLSL (the historical signature), an object\n\t\t// carries one body per language\n\t\tconst bodies = typeof body === \"string\" ? { glsl: body } : (body ?? {});\n\t\tconst language = renderer.shaderLanguage;\n\t\tconst source = language !== null ? bodies[language] : undefined;\n\n\t\tif (typeof source !== \"string\") {\n\t\t\t// no body this backend can compile — same inert-stub contract as\n\t\t\t// the Canvas renderer: warn, stay disabled, every method no-ops\n\t\t\tconsole.warn(\n\t\t\t\tlanguage === null\n\t\t\t\t\t? \"ShaderEffect requires a GPU backend and is disabled on this renderer (no programmable pipeline)\"\n\t\t\t\t\t: `ShaderEffect has no ${language} body and is disabled on this renderer (provide a { ${language}: ... } source)`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * extra texture samplers bound via {@link setTexture}, keyed by the\n\t\t * uniform name → `{ image, repeat, tex }` (`tex` is the uploaded GL\n\t\t * texture, created lazily on first draw; unused on WebGPU, where the\n\t\t * bind group is built lazily instead)\n\t\t * @ignore\n\t\t */\n\t\tthis._extraTextures = new Map();\n\n\t\tif (language === \"glsl\") {\n\t\t\t// assemble the GLSL sources (builtin parsing + boilerplate) — the\n\t\t\t// pure-assembly half lives in glsl_realization.js, pinned\n\t\t\t// byte-identical by the generated-GLSL golden spec\n\t\t\tconst program = buildGLSLProgram(source);\n\n\t\t\t/**\n\t\t\t * samplers annotated `: screen_texture` — the renderer checks this to\n\t\t\t * know when to refresh the shared frame capture before the effect draws\n\t\t\t * @type {Array<{name: string, repeat: string}>}\n\t\t\t * @ignore\n\t\t\t */\n\t\t\tthis._screenTextureUniforms = program.screenTextures;\n\t\t\t/** @ignore */\n\t\t\tthis._hasNoiseUV = program.noiseUV;\n\n\t\t\t/** @ignore */\n\t\t\tthis._shader = new GLShader(\n\t\t\t\trenderer.gl,\n\t\t\t\tprogram.vertex,\n\t\t\t\tprogram.fragment,\n\t\t\t\tprecision || renderer.shaderPrecision,\n\t\t\t);\n\t\t\tthis.enabled = true;\n\n\t\t\t// wire every annotated screen sampler to the renderer's shared frame\n\t\t\t// capture — a live GPU-resident entry, re-bound fresh on each draw and\n\t\t\t// skipped while no capture has been taken yet\n\t\t\tfor (const screenTexture of program.screenTextures) {\n\t\t\t\tthis.setTexture(\n\t\t\t\t\tscreenTexture.name,\n\t\t\t\t\trenderer.getSharedFrameTexture(),\n\t\t\t\t\tscreenTexture.repeat,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// WGSL: parse the body's declarations (WebGPU has no uniform\n\t\t\t// reflection — offsets are computed CPU-side) and assemble the\n\t\t\t// module; GPU objects are built lazily by the renderer's effect\n\t\t\t// path. A parse failure disables the effect like a missing body.\n\t\t\tconst realization = new WGSLEffectRealization(source);\n\t\t\tif (!realization.valid) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`ShaderEffect: invalid WGSL body — ${realization.error} (effect disabled)`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t/** @ignore */\n\t\t\tthis.wgslRealization = realization;\n\t\t\t// same renderer-facing capture contract as the GLSL side: a\n\t\t\t// non-empty list means \"refresh the frame capture before I draw\"\n\t\t\tthis._screenTextureUniforms = realization.builtins.screenTexture\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: \"screen_texture\",\n\t\t\t\t\t\t\trepeat: realization.builtins.screenSamplerRepeat\n\t\t\t\t\t\t\t\t? \"repeat\"\n\t\t\t\t\t\t\t\t: \"no-repeat\",\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: [];\n\t\t\tthis._hasNoiseUV = realization.builtins.noiseUV;\n\t\t\tthis.enabled = true;\n\t\t}\n\n\t\t// flip enabled across context/device loss so beginPostEffect skips us\n\t\ton(ONCONTEXT_LOST, this._onContextLost, this);\n\t\ton(ONCONTEXT_RESTORED, this._onContextRestored, this);\n\t}\n\n\t/** @private */\n\t_onContextLost() {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\t// remember user-set state so restore doesn't override it\n\t\tthis._enabledBeforeSuspend = this.enabled;\n\t\tthis.enabled = false;\n\t\t// GL texture handles + unit reservations are invalid after a context\n\t\t// loss — drop the handles and release the reserved units so, on restore,\n\t\t// _prepareTextures re-reserves + re-uploads instead of re-binding a\n\t\t// stale (black/invalid) handle\n\t\tfor (const entry of this._extraTextures.values()) {\n\t\t\tif (entry.unit !== undefined) {\n\t\t\t\tthis._renderer.cache.releaseUnit(entry.unit);\n\t\t\t\tentry.unit = undefined;\n\t\t\t}\n\t\t\tentry.tex = null;\n\t\t}\n\t}\n\n\t/** @private */\n\t_onContextRestored() {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\t// the inner GLShader recompiles itself; restore the gate to\n\t\t// whatever the user had it set to before the suspend\n\t\tthis.enabled = this._enabledBeforeSuspend !== false;\n\t\tthis._enabledBeforeSuspend = undefined;\n\t}\n\n\t/**\n\t * Set the uniform to the given value\n\t * @param {string} name - the uniform name\n\t * @param {object|Float32Array} value - the value to assign to that uniform\n\t */\n\tsetUniform(name, value) {\n\t\t// forward whenever a live shader exists (WebGL mode): GLShader handles\n\t\t// the suspended (context-lost) state by deferring to its uniform\n\t\t// cache. Gating on `enabled` here silently dropped values set during\n\t\t// a loss window — defeating that replay — or while the user had the\n\t\t// effect disabled. Canvas stubs (no shader) and destroyed effects\n\t\t// (partial-state immunity, see destroy()) keep no-oping.\n\t\tif (this.destroyed === true) {\n\t\t\treturn;\n\t\t}\n\t\tif (typeof this._shader !== \"undefined\") {\n\t\t\tthis._shader.setUniform(name, value);\n\t\t} else if (typeof this.wgslRealization !== \"undefined\") {\n\t\t\t// WGSL: write the CPU mirror; the value is snapshot-uploaded at\n\t\t\t// the effect's next bind (survives device loss for free)\n\t\t\tthis.wgslRealization.setUniform(name, value);\n\t\t}\n\t}\n\n\t/**\n\t * Set the shader's `uTime` uniform (elapsed time, in seconds). A convenience\n\t * over `setUniform(\"uTime\", ...)`; call it once per frame from your update\n\t * loop to animate a shader that declares `uniform float uTime` (e.g. scrolling\n\t * a static noise texture's UVs, pulsing, waving). Drive it with whatever clock\n\t * you like — real time, a paused/scaled/scrubbed one.\n\t *\n\t * No-op if the shader does not declare a `uTime` uniform (nothing to update),\n\t * or in Canvas mode. The engine does NOT call this for you — animation is\n\t * opt-in, exactly like re-baking a {@link NoiseTexture2d} with `update(dt)`.\n\t * @param {number} seconds - elapsed time in seconds\n\t * @returns {ShaderEffect} this effect for chaining\n\t * @example\n\t * // a shader that scrolls a static seamless noise texture over time\n\t * const flow = new me.ShaderEffect(renderer, `\n\t *     uniform float uTime;\n\t *     vec4 apply(vec4 color, vec2 uv) {\n\t *         return texture2D(uSampler, uv + vec2(uTime * 0.05, 0.0));\n\t *     }`);\n\t * mySprite.shader = flow;\n\t * // then in your Stage's update(dt):\n\t * flow.setTime(me.timer.getTime() / 1000);\n\t */\n\tsetTime(seconds) {\n\t\t// detect `uTime` from the compiled program's ACTIVE uniforms at call\n\t\t// time — not a substring scan of the source (which false-positives on\n\t\t// `uTimeScale`, comments, or a `uTime` the compiler optimised out and\n\t\t// would make setUniform throw), and not cached (so it stays correct\n\t\t// across a context-loss recompile). `enabled` is false while suspended\n\t\t// or destroyed, so the uniforms map is never null here.\n\t\t// skipped while suspended (uniforms === null mid-context-loss; a\n\t\t// per-frame call self-heals on the next frame) and on Canvas stubs;\n\t\t// a USER-disabled but live effect still takes the value\n\t\tif (\n\t\t\ttypeof this._shader !== \"undefined\" &&\n\t\t\tthis.destroyed !== true &&\n\t\t\t!this._shader.suspended &&\n\t\t\ttypeof this._shader.uniforms.uTime !== \"undefined\"\n\t\t) {\n\t\t\tthis._shader.setUniform(\"uTime\", seconds);\n\t\t} else if (\n\t\t\ttypeof this.wgslRealization !== \"undefined\" &&\n\t\t\tthis.destroyed !== true &&\n\t\t\tthis.wgslRealization.hasUniform(\"uTime\")\n\t\t) {\n\t\t\t// WGSL twin of the active-uniform check: the parsed struct map is\n\t\t\t// authoritative (no compiler elimination under WebGPU)\n\t\t\tthis.wgslRealization.setUniform(\"uTime\", seconds);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Feed the UV y-direction of the space `apply()` is about to run in:\n\t * `+1` where `uv.y` grows downward (direct sprite sampling, and every\n\t * WebGPU path), `-1` on the WebGL pooled-blit path, whose capture FBOs\n\t * are bottom-up — there, y-directional UV arithmetic inside a body\n\t * runs vertically mirrored unless compensated. Directional bodies\n\t * (DropShadow) declare a `uUVYDir` uniform and multiply their y\n\t * offsets with it, so \"down\" means down on every path of every\n\t * backend. A silent no-op for bodies that don't declare the uniform.\n\t * @param {number} dir - +1 (uv.y grows downward) or -1 (upward)\n\t * @ignore\n\t */\n\t_setUVYDir(dir) {\n\t\tif (this._uvYDir === dir || this.destroyed === true) {\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\ttypeof this._shader !== \"undefined\" &&\n\t\t\t!this._shader.suspended &&\n\t\t\ttypeof this._shader.uniforms.uUVYDir !== \"undefined\"\n\t\t) {\n\t\t\tthis._uvYDir = dir;\n\t\t\tthis._shader.setUniform(\"uUVYDir\", dir);\n\t\t} else if (\n\t\t\ttypeof this.wgslRealization !== \"undefined\" &&\n\t\t\tthis.wgslRealization.hasUniform(\"uUVYDir\")\n\t\t) {\n\t\t\tthis._uvYDir = dir;\n\t\t\tthis.wgslRealization.setUniform(\"uUVYDir\", dir);\n\t\t}\n\t}\n\n\t/**\n\t * Feed the `noise_uv` builtin's frame rect for the object about to draw:\n\t * the source texture dimensions, the destination (object) dimensions, and\n\t * the frame's top-left UV origin. Called by the quad batchers per draw;\n\t * a no-op unless this effect's fragment actually uses `noise_uv` (the\n\t * `ME_*` uniforms only exist — and are only active — then).\n\t * @param {number} sourceWidth - source texture width in pixels\n\t * @param {number} sourceHeight - source texture height in pixels\n\t * @param {number} objectWidth - destination (object) width in pixels\n\t * @param {number} objectHeight - destination (object) height in pixels\n\t * @param {number} u0 - the frame's left UV coordinate (flip-normalized)\n\t * @param {number} v0 - the frame's top UV coordinate (flip-normalized)\n\t * @ignore\n\t */\n\t_setNoiseUVRect(\n\t\tsourceWidth,\n\t\tsourceHeight,\n\t\tobjectWidth,\n\t\tobjectHeight,\n\t\tu0,\n\t\tv0,\n\t) {\n\t\tif (this._hasNoiseUV !== true || this.destroyed === true) {\n\t\t\treturn;\n\t\t}\n\t\tif (typeof this.wgslRealization !== \"undefined\") {\n\t\t\t// WGSL: write the engine ME struct's CPU mirror (size_obj @0,\n\t\t\t// size_img @2, offset @4 — the scaffold's MEBuiltins layout);\n\t\t\t// snapshot-uploaded alongside the uniform block at bind time\n\t\t\tconst me = this.wgslRealization.meMirror;\n\t\t\tme[0] = Math.max(Math.abs(objectWidth), 1);\n\t\t\tme[1] = Math.max(Math.abs(objectHeight), 1);\n\t\t\tme[2] = Math.max(sourceWidth, 1);\n\t\t\tme[3] = Math.max(sourceHeight, 1);\n\t\t\tme[4] = u0 * me[2];\n\t\t\tme[5] = v0 * me[3];\n\t\t\treturn;\n\t\t}\n\t\tif (typeof this._shader === \"undefined\" || this._shader.suspended) {\n\t\t\treturn;\n\t\t}\n\t\t// guard each set on the ACTIVE uniforms map — the compiler eliminates\n\t\t// the ME_* uniforms when `noise_uv` ends up unused, and setUniform\n\t\t// throws on inactive uniforms\n\t\tconst uniforms = this._shader.uniforms;\n\t\tif (typeof uniforms.ME_size_obj !== \"undefined\") {\n\t\t\tthis._shader.setUniform(\"ME_size_obj\", [\n\t\t\t\tMath.max(Math.abs(objectWidth), 1),\n\t\t\t\tMath.max(Math.abs(objectHeight), 1),\n\t\t\t]);\n\t\t}\n\t\tif (typeof uniforms.ME_size_img !== \"undefined\") {\n\t\t\tthis._shader.setUniform(\"ME_size_img\", [\n\t\t\t\tMath.max(sourceWidth, 1),\n\t\t\t\tMath.max(sourceHeight, 1),\n\t\t\t]);\n\t\t}\n\t\tif (typeof uniforms.ME_offset !== \"undefined\") {\n\t\t\tthis._shader.setUniform(\"ME_offset\", [\n\t\t\t\tu0 * Math.max(sourceWidth, 1),\n\t\t\t\tv0 * Math.max(sourceHeight, 1),\n\t\t\t]);\n\t\t}\n\t}\n\n\t/**\n\t * Apply the wrap mode a `: screen_texture(<repeat>)` annotation asked for\n\t * onto the live capture handle. The unit is force-activated first: the\n\t * batcher's bind may have been skipped as redundant while the GL active\n\t * unit points elsewhere.\n\t * @ignore\n\t */\n\t_applyCaptureWrap(batcher, glTex, entry) {\n\t\tconst gl = batcher.gl;\n\t\tgl.activeTexture(gl.TEXTURE0 + entry.unit);\n\t\tgl.bindTexture(gl.TEXTURE_2D, glTex);\n\t\tbatcher.currentTextureUnit = entry.unit;\n\t\tconst wrapS = /^repeat(-x)?$/.test(entry.repeat)\n\t\t\t? gl.REPEAT\n\t\t\t: gl.CLAMP_TO_EDGE;\n\t\tconst wrapT = /^repeat(-y)?$/.test(entry.repeat)\n\t\t\t? gl.REPEAT\n\t\t\t: gl.CLAMP_TO_EDGE;\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);\n\t}\n\n\t/**\n\t * Bind an **extra** texture to a named `sampler2D` uniform in this shader, so\n\t * a custom effect can read a *second* texture — a noise map, mask, gradient,\n\t * flow/lookup table — besides the sprite/target it post-processes (`uSampler`).\n\t * The engine uploads, caches, and re-binds it to a reserved texture unit each\n\t * time the effect draws, and points the sampler uniform at it — no raw WebGL\n\t * texture-unit juggling.\n\t *\n\t * Declare the sampler in your fragment (`uniform sampler2D <name>;`) and pass\n\t * that name here. Any engine texture works — a {@link Texture2d} asset\n\t * (`NoiseTexture2d`, `TextureAtlas`, …) can be passed directly, or a raw\n\t * drawable source. No-op in Canvas mode.\n\t * @param {string} name - the `sampler2D` uniform name declared in the fragment\n\t * @param {Texture2d|HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} image - the texture: an engine texture asset, or a raw drawable source\n\t * @param {\"repeat\"|\"repeat-x\"|\"repeat-y\"|\"no-repeat\"} [repeat=\"no-repeat\"] - wrap mode; use `\"repeat\"` for a tiled/scrolled texture\n\t * @returns {ShaderEffect} this effect for chaining\n\t * @example\n\t * // \"water\": distort the sprite by a static noise texture scrolled over time\n\t * const noise = new me.NoiseTexture2d({ width: 256, height: 256, seamless: true });\n\t * const water = new me.ShaderEffect(renderer, `\n\t *     uniform sampler2D uNoise;\n\t *     uniform float uTime;\n\t *     vec4 apply(vec4 color, vec2 uv) {\n\t *         vec2 flow = texture2D(uNoise, uv + uTime * 0.03).rg - 0.5;\n\t *         return texture2D(uSampler, uv + flow * 0.02);\n\t *     }`);\n\t * water.setTexture(\"uNoise\", noise, \"repeat\");\n\t * waterSprite.shader = water;\n\t * // each frame, in your Stage's update(dt):\n\t * water.setTime(me.timer.getTime() / 1000);\n\t */\n\tsetTexture(name, image, repeat = \"no-repeat\") {\n\t\t// Explicitly reject HTMLVideoElement — it duck-types past the static\n\t\t// upload path, which uploads ONCE (`entry.tex === null` guard in\n\t\t// _prepareTextures) and would silently freeze the sampler on the\n\t\t// video's first frame. Same contract as Sprite.normalMap. Checked\n\t\t// before the Canvas/destroyed no-op guard so the error is raised\n\t\t// consistently under every renderer.\n\t\tif (typeof image?.videoWidth === \"number\") {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"ShaderEffect.setTexture does not support HTMLVideoElement (extra textures upload once and would freeze on the first frame)\",\n\t\t\t);\n\t\t}\n\t\t// Canvas stub (no realization at all) and destroyed effects: keep the\n\t\t// inert no-op\n\t\tif (this.destroyed === true) {\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof this.wgslRealization !== \"undefined\") {\n\t\t\t// WGSL: the sampler must be a texture var the body DECLARED (the\n\t\t\t// bind group is built from the parsed binding table — an\n\t\t\t// undeclared name has no binding to fill)\n\t\t\tconst declared = this.wgslRealization.textures.some((t) => {\n\t\t\t\treturn t.name === name;\n\t\t\t});\n\t\t\tif (!declared) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`ShaderEffect.setTexture: \"${name}\" is not a texture declared in the WGSL body (expected a \\`@group(3) @binding(n) var ${name} : texture_2d<f32>;\\` + sampler pair)`,\n\t\t\t\t);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t\tconst wgslLive =\n\t\t\t\timage instanceof Texture2d && image.isGPUResident === true;\n\t\t\tif (!wgslLive && image instanceof Texture2d) {\n\t\t\t\timage = image.getTexture();\n\t\t\t}\n\t\t\tthis._extraTextures.set(name, {\n\t\t\t\timage,\n\t\t\t\trepeat,\n\t\t\t\ttex: null,\n\t\t\t\tlive: wgslLive,\n\t\t\t\tunit: undefined,\n\t\t\t});\n\t\t\t// the cached bind group (if any) referenced the previous texture\n\t\t\tthis.wgslRealization.gpu?.invalidateBindGroup?.();\n\t\t\treturn this;\n\t\t}\n\t\tif (typeof this._shader === \"undefined\") {\n\t\t\treturn this;\n\t\t}\n\t\t// A GPU-resident LIVE source (a frame capture from\n\t\t// renderer.toFrameTexture): keep the wrapper and bind its live GL\n\t\t// handle every draw — never upload a static copy — so re-capturing\n\t\t// into the same slot each frame is picked up with no re-bind. The live\n\t\t// path reads `.glTexture`, so require that handle to be present;\n\t\t// otherwise fall through and unwrap like any static Texture2d asset,\n\t\t// rather than take the live branch and silently never bind.\n\t\tconst live =\n\t\t\timage instanceof Texture2d &&\n\t\t\timage.isGPUResident === true &&\n\t\t\t\"glTexture\" in image;\n\t\tif (!live && image instanceof Texture2d) {\n\t\t\timage = image.getTexture();\n\t\t}\n\t\t// store the entry regardless of `enabled`: the GL work happens lazily\n\t\t// in _prepareTextures on the next enabled draw, so a binding set while\n\t\t// the effect is disabled — or mid-context-loss, where entries set\n\t\t// during the window used to vanish permanently — survives intact.\n\t\tconst existing = this._extraTextures.get(name);\n\t\tif (existing && existing.tex !== null) {\n\t\t\t// drop the old STATIC GL texture we own so the new image re-uploads\n\t\t\t// into the SAME reserved unit (kept below) on the next enabled draw.\n\t\t\t// A live entry never owns a GL texture (tex stays null — the\n\t\t\t// renderer/caller owns the handle), so this correctly skips it.\n\t\t\tthis._shader.gl.deleteTexture(existing.tex);\n\t\t}\n\t\tthis._extraTextures.set(name, {\n\t\t\timage,\n\t\t\trepeat,\n\t\t\ttex: null,\n\t\t\tlive,\n\t\t\t// keep any unit already reserved for this sampler across the\n\t\t\t// replace: releasing it here (then re-reserving lazily) would open\n\t\t\t// a window — unbounded while the effect is disabled — in which the\n\t\t\t// cache allocator could hand that high unit to a regular texture,\n\t\t\t// reintroducing the collision reserveUnit() exists to prevent. The\n\t\t\t// reservation is released only where the unit is truly invalid:\n\t\t\t// context loss (_onContextLost) and destroy().\n\t\t\tunit: existing ? existing.unit : undefined,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/**\n\t * (Re)bind this effect's extra textures ({@link setTexture}) to reserved GL\n\t * texture units and point their sampler uniforms at them. Called by the\n\t * renderer right after this effect's shader is bound — for both the\n\t * post-effect blit and the single-effect `customShader` sprite path — so the\n\t * bindings survive the batcher's rotating color-texture pool.\n\t * @param {object} batcher - the active batcher (owns the GL texture units)\n\t * @ignore\n\t */\n\t_prepareTextures(batcher) {\n\t\tif (!this.enabled || this._extraTextures.size === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst cache = batcher.renderer.cache;\n\t\tconst filter = batcher.renderer._glTextureFilter();\n\t\t// hand out units from the TOP of the batcher's range, counting down, so\n\t\t// they never collide with `uSampler` (unit 0 on a blit) or the low,\n\t\t// rotating color-texture units. Each unit is reserved in the cache the\n\t\t// first time it's claimed, so `allocateTextureUnit` can't hand the same\n\t\t// unit to a sprite's own texture in the single-effect customShader path.\n\t\t// count down from the RENDERER's top unit rather than the batcher's:\n\t\t// which batcher happens to be active must not decide where an effect's\n\t\t// extra samplers live (#1585)\n\t\tlet nextUnit =\n\t\t\t(batcher.renderer?.maxTextures ?? batcher.maxBatchTextures) - 1;\n\t\tfor (const [name, entry] of this._extraTextures) {\n\t\t\tif (entry.unit === undefined) {\n\t\t\t\t// skip units other holders already reserved — another effect's\n\t\t\t\t// extra samplers, say. The lit batcher no longer reserves a\n\t\t\t\t// fixed normal-map range (#1585), so this is the only claimant\n\t\t\t\t// class left to step over.\n\t\t\t\twhile (nextUnit >= 1 && cache.reservedUnits.has(nextUnit)) {\n\t\t\t\t\tnextUnit--;\n\t\t\t\t}\n\t\t\t\tif (nextUnit < 1) {\n\t\t\t\t\t// more extra textures than the batcher can hold beside\n\t\t\t\t\t// uSampler — bind what fits, warn once, skip the rest\n\t\t\t\t\tif (!this._textureOverflowWarned) {\n\t\t\t\t\t\tthis._textureOverflowWarned = true;\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`ShaderEffect.setTexture: too many extra textures for ${batcher.maxBatchTextures} texture units — \"${name}\" and any later ones were not bound`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tentry.unit = nextUnit;\n\t\t\t\tcache.reserveUnit(nextUnit);\n\t\t\t}\n\t\t\tnextUnit = entry.unit - 1;\n\t\t\tlet bound = false;\n\t\t\tif (entry.live === true) {\n\t\t\t\t// live GPU-resident source (frame capture): bind the current\n\t\t\t\t// handle fresh each draw — the renderer refreshes it in place,\n\t\t\t\t// so this samples the latest frame. Never uploaded, never freed\n\t\t\t\t// by this effect. Skip binding if not captured yet (null).\n\t\t\t\tconst glTex = entry.image.glTexture;\n\t\t\t\tif (glTex !== null && typeof glTex !== \"undefined\") {\n\t\t\t\t\tbatcher.bindTexture2D(glTex, entry.unit, false);\n\t\t\t\t\tif (entry.repeat !== \"no-repeat\") {\n\t\t\t\t\t\t// `: screen_texture(repeat)` — captures are created\n\t\t\t\t\t\t// clamped; apply the requested wrap on the live handle\n\t\t\t\t\t\tthis._applyCaptureWrap(batcher, glTex, entry);\n\t\t\t\t\t}\n\t\t\t\t\tthis._shader.setUniform(name, entry.unit);\n\t\t\t\t\tbound = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (entry.tex === null) {\n\t\t\t\t\tbatcher.createTexture2D(\n\t\t\t\t\t\tentry.unit,\n\t\t\t\t\t\tentry.image,\n\t\t\t\t\t\tfilter,\n\t\t\t\t\t\tentry.repeat,\n\t\t\t\t\t\tentry.image.width,\n\t\t\t\t\t\tentry.image.height,\n\t\t\t\t\t\tfalse, // premultipliedAlpha — keep raw texel values\n\t\t\t\t\t\tfalse, // mipmap — not needed for effect inputs\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tfalse, // flush — the following draw flushes with everything bound\n\t\t\t\t\t);\n\t\t\t\t\tentry.tex = batcher.boundTextures[entry.unit];\n\t\t\t\t} else {\n\t\t\t\t\tbatcher.bindTexture2D(entry.tex, entry.unit, false);\n\t\t\t\t}\n\t\t\t\tthis._shader.setUniform(name, entry.unit);\n\t\t\t\tbound = true;\n\t\t\t}\n\t\t\t// only when we actually bound: this sampler took a reserved high unit\n\t\t\t// DIRECTLY (bypassing the shared texture cache), which overlaps the\n\t\t\t// lit quad batcher's normal-map units — invalidate it on the OTHER\n\t\t\t// batchers so a later lit draw re-binds its normal there instead of\n\t\t\t// sampling this texture. A skipped live bind (no capture yet) clobbers\n\t\t\t// nothing, so there's nothing to invalidate.\n\t\t\tif (bound) {\n\t\t\t\tbatcher.renderer.invalidateTextureUnit(entry.unit, batcher);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create an independent copy of this effect, compiled as its own GL\n\t * program. Use it when several renderables need the same effect with\n\t * *different* uniform values — a single instance has a single set of\n\t * uniforms, shared by everything it is assigned to.\n\t *\n\t * The clone copies the **recipe**: the fragment source, float precision,\n\t * every uniform value set so far, and any extra textures bound via\n\t * {@link setTexture} (the clone uploads and owns its own GL copies).\n\t * It does NOT copy **ownership or lifecycle** state — in particular the\n\t * clone's {@link shared} flag is **always reset to `false`**, even when\n\t * cloning a shared shader (such as one returned by `loader.getShader()`):\n\t * the clone is caller-owned and will be auto-destroyed by the renderable\n\t * it is assigned to, exactly like a hand-constructed effect. Set\n\t * `shared = true` on the clone yourself if you intend to reuse it across\n\t * several renderables.\n\t * @returns {ShaderEffect} a new, caller-owned effect (`shared === false`)\n\t * @example\n\t * // the loader's shader is ONE shared program — one uniform state for all\n\t * sprite.shader = loader.getShader(\"flash\");\n\t * // the boss needs its own intensity — clone a private, caller-owned copy\n\t * boss.shader = loader.getShader(\"flash\").clone();\n\t * boss.shader.setUniform(\"uIntensity\", 0.9);\n\t */\n\tclone() {\n\t\tif (this.destroyed) {\n\t\t\tthrow new Error(\"ShaderEffect.clone: effect has been destroyed\");\n\t\t}\n\t\tconst copy = new ShaderEffect(\n\t\t\tthis._renderer,\n\t\t\tthis._fragmentBody,\n\t\t\tthis._precision,\n\t\t);\n\t\t// WGSL: replay the exact user-set values recorded by the realization\n\t\t// (not a mirror read-back), then re-declare the extra textures\n\t\tif (this.wgslRealization && copy.wgslRealization) {\n\t\t\tfor (const [name, value] of this.wgslRealization.values) {\n\t\t\t\tcopy.setUniform(name, value);\n\t\t\t}\n\t\t\tfor (const [name, entry] of this._extraTextures) {\n\t\t\t\tcopy.setTexture(name, entry.image, entry.repeat);\n\t\t\t}\n\t\t\treturn copy;\n\t\t}\n\t\t// Canvas-mode effects are inert stubs — nothing further to copy\n\t\tif (this._shader && copy._shader) {\n\t\t\t// replay this effect's cached uniform values onto the clone (same\n\t\t\t// snapshot store the context-loss recovery replays from). When the\n\t\t\t// clone was constructed during a LOST context its program compiles\n\t\t\t// deferred and `uniforms` is null — skip the active-uniform check\n\t\t\t// and let the suspended setUniform cache the value for the restore\n\t\t\t// replay (which re-validates). Note the cache also holds sampler\n\t\t\t// unit indices written by _prepareTextures — harmless to replay,\n\t\t\t// the clone's own first draw re-reserves and overwrites them.\n\t\t\tfor (const name of Object.keys(this._shader._uniformCache)) {\n\t\t\t\tif (\n\t\t\t\t\tcopy._shader.suspended ||\n\t\t\t\t\ttypeof copy._shader.uniforms[name] !== \"undefined\"\n\t\t\t\t) {\n\t\t\t\t\tcopy._shader.setUniform(name, this._shader._uniformCache[name]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// re-declare extra textures — the clone uploads + owns its own\n\t\t\t// GL texture and unit reservation on first draw\n\t\t\tfor (const [name, entry] of this._extraTextures) {\n\t\t\t\tcopy.setTexture(name, entry.image, entry.repeat);\n\t\t\t}\n\t\t}\n\t\treturn copy;\n\t}\n\n\t// the GL-program pass-throughs below additionally guard on `_shader`:\n\t// a WGSL-realized effect is `enabled` without ever owning a GL program\n\n\t/** @ignore */\n\tbind() {\n\t\tif (this.enabled && this._shader) {\n\t\t\tthis._shader.bind();\n\t\t}\n\t}\n\n\t/** @ignore */\n\tgetAttribLocation(name) {\n\t\treturn this.enabled && this._shader\n\t\t\t? this._shader.getAttribLocation(name)\n\t\t\t: -1;\n\t}\n\n\t/** @ignore */\n\tsetVertexAttributes(gl, attributes, stride) {\n\t\tif (this.enabled && this._shader) {\n\t\t\tthis._shader.setVertexAttributes(gl, attributes, stride);\n\t\t}\n\t}\n\n\t/** @ignore */\n\tget program() {\n\t\treturn this.enabled && this._shader ? this._shader.program : null;\n\t}\n\n\t/** @ignore */\n\tget vertex() {\n\t\treturn this.enabled && this._shader ? this._shader.vertex : null;\n\t}\n\n\t/** @ignore */\n\tget fragment() {\n\t\treturn this.enabled && this._shader ? this._shader.fragment : null;\n\t}\n\n\t/** @ignore */\n\tget attributes() {\n\t\treturn this.enabled && this._shader ? this._shader.attributes : {};\n\t}\n\n\t/** @ignore */\n\tget uniforms() {\n\t\treturn this.enabled && this._shader ? this._shader.uniforms : {};\n\t}\n\n\t/**\n\t * destroy this shader effect. Idempotent — calling destroy twice\n\t * is safe. Unsubscribes from the renderer's context-lost / restored\n\t * events so a destroyed effect is not auto-reactivated.\n\t */\n\tdestroy() {\n\t\tif (this.destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.destroyed = true;\n\n\t\t// flip enabled BEFORE inner destroy so a thrown deleteProgram\n\t\t// can't leave us with enabled=true + uniforms=null (19.5.0 bug)\n\t\tthis.enabled = false;\n\n\t\toff(ONCONTEXT_LOST, this._onContextLost, this);\n\t\toff(ONCONTEXT_RESTORED, this._onContextRestored, this);\n\n\t\t// WGSL: retire the effect-owned resident textures (static setTexture\n\t\t// uploads), then drop the lazily-built GPU state; the pipeline cache\n\t\t// keeps the shared module (bounded retention, rebuilt per epoch)\n\t\tif (this.wgslRealization) {\n\t\t\tconst gpu = this.wgslRealization.gpu;\n\t\t\tif (gpu?.residentTextures) {\n\t\t\t\tfor (const resident of gpu.residentTextures.values()) {\n\t\t\t\t\tthis._renderer.retireTexture?.(resident.texture);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.wgslRealization.releaseGPU();\n\t\t\tthis._extraTextures.clear();\n\t\t}\n\t\t// _shader is undefined on Canvas-mode effects (early-returned)\n\t\tif (this._shader) {\n\t\t\t// release any extra textures bound via setTexture — both the GL\n\t\t\t// handle and the cache unit reservation\n\t\t\tfor (const entry of this._extraTextures.values()) {\n\t\t\t\tif (entry.unit !== undefined) {\n\t\t\t\t\tthis._renderer.cache.releaseUnit(entry.unit);\n\t\t\t\t}\n\t\t\t\tif (entry.tex !== null) {\n\t\t\t\t\tthis._shader.gl.deleteTexture(entry.tex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._extraTextures.clear();\n\t\t\tthis._shader.destroy();\n\t\t}\n\t}\n}\n","import { ColorMatrix } from \"../../math/color_matrix.ts\";\nimport ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct ColorMatrixUniforms {\n\tuColorMatrix : mat4x4f,\n};\n@group(3) @binding(0) var<uniform> fx : ColorMatrixUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\treturn fx.uColorMatrix * color;\n}\n`;\n\n/**\n * A shader effect that applies a 4x4 color transformation matrix.\n * Provides chainable color adjustment methods that automatically update the GPU uniform.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // desaturate a sprite\n * mySprite.shader = new ColorMatrixEffect(renderer).saturate(0.0);\n * @example\n * // combine brightness + contrast on a camera\n * camera.shader = new ColorMatrixEffect(renderer).brightness(1.3).contrast(1.5);\n * @example\n * // update dynamically\n * effect.reset().brightness(1.5).saturate(0.5);\n */\nexport default class ColorMatrixEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {ColorMatrix} [options.matrix] - an initial color matrix. Defaults to identity.\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform mat4 uColorMatrix;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\treturn uColorMatrix * color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\t/**\n\t\t * the internal color matrix\n\t\t * @ignore\n\t\t */\n\t\tthis._matrix = options.matrix || new ColorMatrix();\n\t\tthis._syncUniform();\n\t}\n\n\t/**\n\t * Push the current matrix values to the GPU.\n\t * @ignore\n\t */\n\t_syncUniform() {\n\t\tthis.setUniform(\"uColorMatrix\", this._matrix.val);\n\t}\n\n\t/**\n\t * Reset the color matrix to identity (no color change).\n\t * @returns {this} this instance for chaining\n\t */\n\treset() {\n\t\tthis._matrix.identity();\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a brightness adjustment.\n\t * @param {number} amount - brightness multiplier (1.0 = normal, >1 brighter, <1 darker)\n\t * @returns {this} this instance for chaining\n\t */\n\tbrightness(amount) {\n\t\tthis._matrix.brightness(amount);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a contrast adjustment.\n\t * @param {number} amount - contrast multiplier (1.0 = normal, >1 more contrast, <1 less)\n\t * @returns {this} this instance for chaining\n\t */\n\tcontrast(amount) {\n\t\tthis._matrix.contrast(amount);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a saturation adjustment.\n\t * @param {number} amount - saturation level (0.0 = grayscale, 1.0 = normal, >1 over-saturated)\n\t * @returns {this} this instance for chaining\n\t */\n\tsaturate(amount) {\n\t\tthis._matrix.saturate(amount);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a hue rotation.\n\t * @param {number} angle - rotation angle in radians\n\t * @returns {this} this instance for chaining\n\t */\n\thueRotate(angle) {\n\t\tthis._matrix.hueRotate(angle);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a sepia tone.\n\t * @param {number} [amount=1.0] - sepia intensity (0.0 = original, 1.0 = full sepia)\n\t * @returns {this} this instance for chaining\n\t */\n\tsepia(amount = 1.0) {\n\t\tthis._matrix.sepia(amount);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a color inversion.\n\t * @param {number} [amount=1.0] - inversion amount (0.0 = original, 1.0 = fully inverted)\n\t * @returns {this} this instance for chaining\n\t */\n\tinvertColors(amount = 1.0) {\n\t\tthis._matrix.invertColors(amount);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Multiply the current matrix by another color matrix.\n\t * @param {ColorMatrix} matrix - the matrix to multiply with\n\t * @returns {this} this instance for chaining\n\t */\n\tmultiply(matrix) {\n\t\tthis._matrix.multiply(matrix);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Multiplies the current matrix with a transform described by individual values.\n\t * Accepts either 6 values (2D affine: a, b, c, d, e, f) or 16 values (full 4x4 column-major).\n\t * @param {...number} args - 6 or 16 numeric values\n\t * @returns {this} this instance for chaining\n\t */\n\ttransform(...args) {\n\t\tthis._matrix.transform(...args);\n\t\tthis._syncUniform();\n\t\treturn this;\n\t}\n}\n","import type Camera2d from \"../camera2d.ts\";\nimport CameraEffect from \"./camera_effect.ts\";\n\n/**\n * A camera effect that shakes the viewport by applying random offsets.\n * @category Camera\n * @example\n * camera.addCameraEffect(new ShakeEffect(camera, {\n *     intensity: 10,\n *     duration: 500,\n *     axis: camera.AXIS.BOTH,\n * }));\n */\nexport default class ShakeEffect extends CameraEffect {\n\t/**\n\t * maximum pixel offset during shake\n\t */\n\tintensity: number;\n\n\t/**\n\t * remaining duration in milliseconds\n\t */\n\tduration: number;\n\n\t/**\n\t * which axes to shake (use camera.AXIS constants)\n\t */\n\taxis: number;\n\n\t/**\n\t * optional callback when shake completes\n\t */\n\tonComplete: (() => void) | undefined;\n\n\t/**\n\t * @param camera - the camera to shake\n\t * @param options - shake parameters\n\t * @param options.intensity - maximum offset in pixels\n\t * @param options.duration - duration in milliseconds\n\t * @param [options.axis=3] - which axes (NONE=0, HORIZONTAL=1, VERTICAL=2, BOTH=3)\n\t * @param [options.onComplete] - callback when the shake finishes\n\t */\n\tconstructor(\n\t\tcamera: Camera2d,\n\t\toptions: {\n\t\t\tintensity: number;\n\t\t\tduration: number;\n\t\t\taxis?: number | undefined;\n\t\t\tonComplete?: (() => void) | undefined;\n\t\t},\n\t) {\n\t\tsuper(camera);\n\t\tthis.intensity = options.intensity;\n\t\tthis.duration = options.duration;\n\t\tthis.axis = options.axis ?? camera.AXIS.BOTH;\n\t\tthis.onComplete = options.onComplete;\n\t}\n\n\toverride update(dt: number): void {\n\t\tthis.duration -= dt;\n\t\tif (this.duration <= 0) {\n\t\t\tthis.duration = 0;\n\t\t\tthis.camera.offset.setZero();\n\t\t\tthis.isComplete = true;\n\t\t\tif (typeof this.onComplete === \"function\") {\n\t\t\t\tthis.onComplete();\n\t\t\t}\n\t\t} else {\n\t\t\tconst axis = this.camera.AXIS;\n\t\t\tif (this.axis === axis.BOTH || this.axis === axis.HORIZONTAL) {\n\t\t\t\tthis.camera.offset.x = (Math.random() - 0.5) * this.intensity;\n\t\t\t}\n\t\t\tif (this.axis === axis.BOTH || this.axis === axis.VERTICAL) {\n\t\t\t\tthis.camera.offset.y = (Math.random() - 0.5) * this.intensity;\n\t\t\t}\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\tthis.camera.offset.setZero();\n\t}\n}\n","import { game } from \"../application/application.ts\";\nimport { Rect } from \"./../geometries/rectangle.ts\";\nimport type { Color } from \"../math/color.ts\";\nimport { ColorMatrix } from \"../math/color_matrix.ts\";\nimport { clamp, toBeCloseTo } from \"./../math/math.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\nimport { Vector3d } from \"../math/vector3d.ts\";\nimport { Bounds, boundsPool } from \"./../physics/bounds.ts\";\nimport type Container from \"./../renderable/container.js\";\nimport Renderable from \"./../renderable/renderable.js\";\nimport state from \"../state/state.ts\";\nimport {\n\tCANVAS_ONRESIZE,\n\temit,\n\tGAME_RESET,\n\toff,\n\ton,\n\tVIEWPORT_ONCHANGE,\n\tVIEWPORT_ONRESIZE,\n} from \"../system/event.ts\";\nimport timer from \"../system/timer.ts\";\nimport ColorMatrixEffect from \"./../video/effects/colorMatrix.js\";\nimport type Renderer from \"./../video/renderer.js\";\nimport type CameraEffect from \"./effects/camera_effect.ts\";\nimport FadeEffect from \"./effects/fade_effect.ts\";\nimport ShakeEffect from \"./effects/shake_effect.ts\";\n\n/**\n * @import Entity from \"./../renderable/entity/entity.js\";\n * @import Sprite from \"./../renderable/sprite.js\";\n * @import NineSliceSprite from \"./../renderable/nineslicesprite.js\";\n */\n\ninterface AxisEnum {\n\treadonly NONE: 0;\n\treadonly HORIZONTAL: 1;\n\treadonly VERTICAL: 2;\n\treadonly BOTH: 3;\n}\n\nconst targetV = new Vector2d();\n\n/**\n * a 2D orthographic camera\n * @category Camera\n * @example\n * // create a minimap camera in the top-right corner showing the full level\n * const minimap = new Camera2d(0, 0, 180, 100);\n * minimap.name = \"minimap\";\n * minimap.screenX = app.viewport.width - 190;\n * minimap.screenY = 10;\n * minimap.autoResize = false;\n * minimap.setBounds(0, 0, levelWidth, levelHeight);\n * minimap.zoom = Math.min(180 / levelWidth, 100 / levelHeight);\n *\n * // add the camera to the current stage\n * this.cameras.set(\"minimap\", minimap);\n */\nexport default class Camera2d extends Renderable {\n\t/**\n\t * Preferred `Container.sortOn` mode for this camera type. Read by\n\t * `Application` (and `Stage`) at bootstrap to initialize the world\n\t * container's sort. Camera2d uses `\"z\"` (painter's-algorithm 2D\n\t * layering, higher pos.z draws on top); `Camera3d` overrides to\n\t * `\"depth\"` (camera-distance sort required by perspective).\n\t * Subclasses can override to declare their own preferred mode.\n\t */\n\tstatic defaultSortOn: \"x\" | \"y\" | \"z\" | \"depth\" = \"z\";\n\n\t/**\n\t * Axis definition\n\t * NONE no axis\n\t * HORIZONTAL horizontal axis only\n\t * VERTICAL vertical axis only\n\t * BOTH both axis\n\t */\n\tAXIS: AxisEnum;\n\n\t/**\n\t * Camera bounds\n\t */\n\tbounds: Bounds;\n\n\t/**\n\t * enable or disable damping\n\t * @default true\n\t */\n\tsmoothFollow: boolean;\n\n\t/**\n\t * Camera damping for smooth follow `[0 .. 1]`. `1` snaps the camera\n\t * to the target every frame (no smoothing); lower values produce a\n\t * trailing follow — `0.1` is a soft springy follow, `0.5` is snappy.\n\t *\n\t * **Frame-rate independent (since 19.7 / #1478).** The value is the\n\t * fraction of the gap covered per frame at the engine's target\n\t * framerate ({@link timer.maxfps}, default 60). The underlying\n\t * implementation switched from `pos.lerp` to `pos.damp` with\n\t * `lambda = -ln(1 - damping) * timer.maxfps`, so the legacy\n\t * per-frame fraction is recovered exactly at the target rate AND\n\t * the wall-clock convergence stays constant when the actual frame\n\t * rate drifts (slow machine, throttled tab, high-refresh display).\n\t * Existing `damping` tuning carries over with no changes.\n\t * @default 1.0\n\t */\n\tdamping: number;\n\n\t/**\n\t * the closest point relative to the camera. Widened from `-1000` in\n\t * 19.7 to accommodate sprites at large `depth` values now that\n\t * `aVertex.z` participates in clip-space (PR A): the default\n\t * `Container.autoDepth = true` assigns `pos.z = childCount` so any\n\t * container with >1000 children would otherwise clip-cull, and the\n\t * common Y-sort pattern `sprite.depth = sprite.pos.y` exceeds 1000\n\t * on tall maps. Override per-camera if you need tighter z clipping.\n\t * @default -1e6\n\t */\n\tnear: number;\n\n\t/**\n\t * the furthest point relative to the camera. Widened from `1000` in\n\t * 19.7 — see {@link Camera2d#near}.\n\t * @default 1e6\n\t */\n\tfar: number;\n\n\t/**\n\t * the x position on the screen where this camera viewport is rendered.\n\t * @default 0\n\t */\n\tscreenX: number;\n\n\t/**\n\t * the y position on the screen where this camera viewport is rendered.\n\t * @default 0\n\t */\n\tscreenY: number;\n\n\t/**\n\t * @ignore\n\t */\n\t_zoom: number;\n\n\t/**\n\t * the world-space projection matrix for non-default cameras (offset/zoomed).\n\t * Maps world coordinates to the camera's screen viewport.\n\t */\n\tworldProjection: Matrix3d;\n\n\t/**\n\t * the screen-space projection matrix for non-default cameras.\n\t * Maps coordinates so that (0,0) aligns with the camera's screenX/screenY position.\n\t * Used for rendering floating elements (e.g. background layers) in the correct screen area.\n\t */\n\tscreenProjection: Matrix3d;\n\n\t/**\n\t * Whether this camera should automatically resize when the canvas resizes.\n\t * Set to false for non-default cameras with fixed dimensions\n\t * (e.g. minimap, split-screen viewports).\n\t * @default true\n\t */\n\tautoResize: boolean;\n\n\t/**\n\t * cached world view bounds\n\t * @ignore\n\t */\n\t_worldView: Bounds;\n\n\t/**\n\t * the default camera projection matrix\n\t * (2d cameras use an orthographic projection by default).\n\t */\n\tprojectionMatrix: Matrix3d;\n\n\t/**\n\t * the invert camera transform used to unproject points\n\t * @ignore\n\t */\n\tinvCurrentTransform: Matrix3d;\n\n\t/** offset for shake effect */\n\toffset: Vector2d;\n\n\t/** target to follow */\n\ttarget: Vector2d | Vector3d | null;\n\n\t/** default value follow */\n\tfollow_axis: number;\n\n\t/**\n\t * active camera effects (shake, fade, etc.)\n\t */\n\tcameraEffects: CameraEffect[];\n\n\t/**\n\t * A built-in color transformation matrix applied as the final post-processing pass.\n\t * Provides convenient color grading (brightness, contrast, saturation, etc.)\n\t * that is always applied after any effects added via {@link addPostEffect}.\n\t * When set to identity (default), no effect is applied and there is zero overhead.\n\t * @example\n\t * // warm HDR-like color grading\n\t * camera.colorMatrix.contrast(1.2).saturate(1.1);\n\t * // reset to no color grading\n\t * camera.colorMatrix.identity();\n\t */\n\tcolorMatrix: ColorMatrix;\n\n\t/** @ignore */\n\t_colorMatrixEffect: ColorMatrixEffect | null;\n\n\t/** the camera deadzone */\n\tdeadzone: Rect;\n\n\t/**\n\t * @param minX - start x offset\n\t * @param minY - start y offset\n\t * @param maxX - end x offset\n\t * @param maxY - end y offset\n\t */\n\tconstructor(minX: number, minY: number, maxX: number, maxY: number) {\n\t\tsuper(minX, minY, maxX - minX, maxY - minY);\n\n\t\tthis.AXIS = {\n\t\t\tNONE: 0,\n\t\t\tHORIZONTAL: 1,\n\t\t\tVERTICAL: 2,\n\t\t\tBOTH: 3,\n\t\t};\n\n\t\tthis.bounds = boundsPool.get();\n\n\t\tthis.smoothFollow = true;\n\n\t\tthis.damping = 1.0;\n\n\t\tthis.near = -1e6;\n\n\t\tthis.far = 1e6;\n\n\t\tthis.projectionMatrix = new Matrix3d();\n\n\t\tthis.invCurrentTransform = new Matrix3d();\n\n\t\t// offset for shake effect\n\t\tthis.offset = new Vector2d();\n\n\t\t// target to follow\n\t\tthis.target = null;\n\n\t\t// default value follow\n\t\tthis.follow_axis = this.AXIS.NONE;\n\n\t\t// active camera effects\n\t\tthis.cameraEffects = [];\n\n\t\t// default screen position (top-left of canvas)\n\t\tthis.screenX = 0;\n\t\tthis.screenY = 0;\n\t\tthis.zoom = 1;\n\t\tthis.worldProjection = new Matrix3d();\n\t\tthis.screenProjection = new Matrix3d();\n\t\tthis._worldView = new Bounds();\n\t\tthis.autoResize = true;\n\n\t\t// default camera name\n\t\tthis.name = \"default\";\n\n\t\t// set a default deadzone\n\t\tthis.setDeadzone(this.width / 6, this.height / 6);\n\n\t\t// for backward \"compatibility\" (in terms of behavior)\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t// enable event detection on the camera\n\t\tthis.isKinematic = false;\n\n\t\t// camera manages its own FBO lifecycle in draw()\n\t\tthis._postEffectManaged = true;\n\n\t\tthis.colorMatrix = new ColorMatrix();\n\t\tthis._colorMatrixEffect = null;\n\n\t\tthis.bounds.setMinMax(minX, minY, maxX, maxY);\n\n\t\t// update the projection matrix\n\t\tthis._updateProjectionMatrix();\n\n\t\t// subscribe to the game reset event\n\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\ton(GAME_RESET, this.reset, this);\n\t\t// subscribe to the canvas resize event\n\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\ton(CANVAS_ONRESIZE, this.resize, this);\n\t}\n\n\t// -- some private function ---\n\n\t/** @ignore */\n\t// update the projection matrix based on the projection frame (a rectangle)\n\t_updateProjectionMatrix(): void {\n\t\tthis.projectionMatrix.ortho(\n\t\t\t0,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t\t0,\n\t\t\tthis.near,\n\t\t\tthis.far,\n\t\t);\n\t\t// Mirror the screen ortho into `screenProjection` so default-\n\t\t// camera floating renderables can swap to it in `Container.draw`\n\t\t// without a no-op risk. Non-default cameras overwrite this in\n\t\t// `Camera2d.draw` (line ~946) with a screen-relative ortho;\n\t\t// Camera3d overrides this method and sets `screenProjection`\n\t\t// to a screen-space ortho separate from its perspective\n\t\t// `projectionMatrix`.\n\t\tthis.screenProjection.copy(this.projectionMatrix);\n\t}\n\n\t/** @ignore */\n\t_followH(target: Vector2d | Vector3d): number {\n\t\tlet targetX = this.pos.x;\n\t\tif (target.x - this.pos.x > this.deadzone.right) {\n\t\t\t// clamp against the bounds' right EDGE — `bounds.width` is\n\t\t\t// `right - left`, so using it as a max only worked for a\n\t\t\t// zero-origin world and cut `left` px off the far side\n\t\t\ttargetX = Math.min(\n\t\t\t\ttarget.x - this.deadzone.right,\n\t\t\t\tthis.bounds.right - this.width,\n\t\t\t);\n\t\t} else if (target.x - this.pos.x < this.deadzone.pos.x) {\n\t\t\ttargetX = Math.max(target.x - this.deadzone.pos.x, this.bounds.left);\n\t\t}\n\t\treturn targetX;\n\t}\n\n\t/** @ignore */\n\t_followV(target: Vector2d | Vector3d): number {\n\t\tlet targetY = this.pos.y;\n\t\tif (target.y - this.pos.y > this.deadzone.bottom) {\n\t\t\ttargetY = Math.min(\n\t\t\t\ttarget.y - this.deadzone.bottom,\n\t\t\t\tthis.bounds.bottom - this.height,\n\t\t\t);\n\t\t} else if (target.y - this.pos.y < this.deadzone.pos.y) {\n\t\t\ttargetY = Math.max(target.y - this.deadzone.pos.y, this.bounds.top);\n\t\t}\n\t\treturn targetY;\n\t}\n\n\t// -- public function ---\n\n\t/**\n\t * the zoom level of this camera.\n\t * Values less than 1 zoom out (show more of the world),\n\t * values greater than 1 zoom in (show less of the world).\n\t * @default 1\n\t * @example\n\t * // zoom out to show the full level in a 180x100 minimap\n\t * camera.zoom = Math.min(180 / levelWidth, 100 / levelHeight);\n\t */\n\tget zoom(): number {\n\t\treturn this._zoom;\n\t}\n\n\tset zoom(value: number) {\n\t\tthis._zoom = value > 0 ? value : 1;\n\t}\n\n\t/**\n\t * Whether this camera is using default settings (no screen offset, no zoom).\n\t * Non-default cameras use custom projections for viewport positioning and scaling.\n\t * @returns true if this camera has no screen offset and zoom is 1\n\t */\n\tget isDefault(): boolean {\n\t\treturn this.screenX === 0 && this.screenY === 0 && this.zoom === 1;\n\t}\n\n\t/**\n\t * The world-space bounds currently visible through this camera,\n\t * taking into account position and zoom level.\n\t * @returns the visible world area\n\t */\n\tget worldView(): Bounds {\n\t\tthis._worldView.setMinMax(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.pos.x + this.width / this.zoom,\n\t\t\tthis.pos.y + this.height / this.zoom,\n\t\t);\n\t\treturn this._worldView;\n\t}\n\n\t/**\n\t * Set the camera screen position and size in a single call.\n\t * @param x - x position on screen\n\t * @param y - y position on screen\n\t * @param [w] - width (defaults to current width)\n\t * @param [h] - height (defaults to current height)\n\t * @returns this camera for chaining\n\t */\n\tsetViewport(x: number, y: number, w?: number, h?: number): this {\n\t\tthis.screenX = x;\n\t\tthis.screenY = y;\n\t\tif (typeof w !== \"undefined\" && typeof h !== \"undefined\") {\n\t\t\tsuper.resize(w, h);\n\t\t\tthis._updateProjectionMatrix();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * reset the camera position to specified coordinates\n\t * @param [x=0] - initial position of the camera on the x axis\n\t * @param [y=0] - initial position of the camera on the y axis\n\t */\n\treset(x: number = 0, y: number = 0): void {\n\t\t// reset the initial camera position to 0,0\n\t\tthis.pos.x = x;\n\t\tthis.pos.y = y;\n\n\t\t// reset the target\n\t\tthis.unfollow();\n\n\t\t// damping default value\n\t\tthis.smoothFollow = true;\n\t\tthis.damping = 1.0;\n\n\t\t// reset the transformation matrix\n\t\tthis.currentTransform.identity();\n\t\tthis.invCurrentTransform.identity().invert();\n\n\t\t// reset the projection matrices\n\t\tthis.worldProjection.identity();\n\t\tthis.screenProjection.identity();\n\n\t\t// update the projection matrix\n\t\tthis._updateProjectionMatrix();\n\n\t\t// remove non-persistent camera effects (persistent ones survive state changes)\n\t\tfor (let i = this.cameraEffects.length - 1; i >= 0; i--) {\n\t\t\tif (!this.cameraEffects[i].isPersistent) {\n\t\t\t\tthis.cameraEffects[i].destroy();\n\t\t\t\tthis.cameraEffects.splice(i, 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * change the deadzone settings.\n\t * the \"deadzone\" defines an area within the current camera in which\n\t * the followed renderable can move without scrolling the camera.\n\t * @see {@link follow}\n\t * @param w - deadzone width\n\t * @param h - deadzone height\n\t */\n\tsetDeadzone(w: number, h: number): void {\n\t\tif (typeof this.deadzone === \"undefined\") {\n\t\t\tthis.deadzone = new Rect(0, 0, 0, 0);\n\t\t}\n\n\t\t// reusing the old code for now...\n\t\tthis.deadzone.pos.set(\n\t\t\t~~((this.width - w) / 2),\n\t\t\t~~((this.height - h) / 2 - h * 0.25),\n\t\t);\n\t\tthis.deadzone.resize(w, h);\n\n\t\tthis.smoothFollow = false;\n\n\t\t// force a camera update\n\t\tthis.updateTarget();\n\n\t\tthis.smoothFollow = true;\n\t}\n\n\t/**\n\t * resize the camera\n\t * @param w - new width of the camera\n\t * @param h - new height of the camera\n\t * @returns this camera\n\t */\n\toverride resize(w: number, h: number): this {\n\t\t// skip resize for non-auto-resize cameras (e.g. minimap, split-screen)\n\t\tif (!this.autoResize) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// parent consctructor, resize camera rect\n\t\tsuper.resize(w, h);\n\n\t\t// disable damping while resizing\n\t\tthis.smoothFollow = false;\n\n\t\t// reset everything\n\t\tthis.setBounds(0, 0, w, h);\n\t\tthis.setDeadzone(w / 6, h / 6);\n\t\tthis.update();\n\t\tthis.smoothFollow = true;\n\n\t\t// update the projection matrix\n\t\tthis._updateProjectionMatrix();\n\n\t\t// publish the viewport resize event\n\t\temit(VIEWPORT_ONRESIZE, this.width, this.height);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the camera boundaries (set to the world limit by default).\n\t * the camera is bound to the given coordinates and cannot move/be scrolled outside of it.\n\t * @param x - world left limit\n\t * @param y - world top limit\n\t * @param w - world width limit\n\t * @param h - world height limit\n\t */\n\tsetBounds(x: number, y: number, w: number, h: number): void {\n\t\tthis.smoothFollow = false;\n\t\tthis.bounds.setMinMax(x, y, w + x, h + y);\n\t\tthis.moveTo(this.pos.x, this.pos.y);\n\t\tthis.update();\n\t\tthis.smoothFollow = true;\n\t}\n\n\t/**\n\t * set the camera to follow the specified renderable. <br>\n\t * (this will put the camera center around the given target)\n\t * @param target - renderable or position vector to follow\n\t * @param [axis=app.viewport.AXIS.BOTH] - Which axis to follow (see {@link Camera2d.AXIS})\n\t * @param [damping=1] - default damping value\n\t * @example\n\t * // set the camera to follow this renderable on both axis, and enable damping\n\t * app.viewport.follow(this, app.viewport.AXIS.BOTH, 0.1);\n\t */\n\tfollow(\n\t\ttarget: Renderable | Vector2d | Vector3d,\n\t\taxis?: number,\n\t\tdamping?: number,\n\t): void {\n\t\tif (target instanceof Renderable) {\n\t\t\tthis.target = target.pos;\n\t\t} else if (target instanceof Vector2d || target instanceof Vector3d) {\n\t\t\tthis.target = target;\n\t\t} else {\n\t\t\tthrow new Error(\"invalid target for me.Camera2d.follow\");\n\t\t}\n\t\t// if axis is null, camera is moved on target center\n\t\tthis.follow_axis = typeof axis === \"undefined\" ? this.AXIS.BOTH : axis;\n\n\t\tthis.smoothFollow = false;\n\n\t\tif (typeof damping !== \"number\") {\n\t\t\tthis.damping = 1;\n\t\t} else {\n\t\t\tthis.damping = clamp(damping, 0.0, 1.0);\n\t\t}\n\n\t\t// force a camera update\n\t\tthis.updateTarget();\n\n\t\tthis.smoothFollow = true;\n\t}\n\n\t/**\n\t * unfollow the current target\n\t */\n\tunfollow(): void {\n\t\tthis.target = null;\n\t\tthis.follow_axis = this.AXIS.NONE;\n\t}\n\n\t/**\n\t * move the camera upper-left position by the specified offset.\n\t * @see {@link focusOn}\n\t * @param x - horizontal offset\n\t * @param y - vertical offset\n\t * @example\n\t * // Move the camera up by four pixels\n\t * app.viewport.move(0, -4);\n\t */\n\tmove(x: number, y: number): void {\n\t\tthis.moveTo(this.pos.x + x, this.pos.y + y);\n\t}\n\n\t/**\n\t * move the camera upper-left position to the specified coordinates\n\t * @see {@link focusOn}\n\t * @param x - horizontal position\n\t * @param y - vertical position\n\t */\n\tmoveTo(x: number, y: number): void {\n\t\tconst _x = this.pos.x;\n\t\tconst _y = this.pos.y;\n\n\t\t// clamp against the bounds' far edges — `bounds.width/height` are\n\t\t// extents (`right - left`), correct as a max only for a zero origin\n\t\tthis.pos.x = clamp(x, this.bounds.left, this.bounds.right - this.width);\n\t\tthis.pos.y = clamp(y, this.bounds.top, this.bounds.bottom - this.height);\n\n\t\t//publish the VIEWPORT_ONCHANGE event if necessary\n\t\tif (_x !== this.pos.x || _y !== this.pos.y) {\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/** @ignore */\n\n\tupdateTarget(dt?: number): void {\n\t\tif (this.target) {\n\t\t\ttargetV.setV(this.pos);\n\n\t\t\tswitch (this.follow_axis) {\n\t\t\t\tcase this.AXIS.NONE:\n\t\t\t\t\t//this.focusOn(this.target);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase this.AXIS.HORIZONTAL:\n\t\t\t\t\ttargetV.x = this._followH(this.target);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase this.AXIS.VERTICAL:\n\t\t\t\t\ttargetV.y = this._followV(this.target);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase this.AXIS.BOTH:\n\t\t\t\t\ttargetV.x = this._followH(this.target);\n\t\t\t\t\ttargetV.y = this._followV(this.target);\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!this.pos.equals(targetV)) {\n\t\t\t\t// update the camera position\n\t\t\t\tif (this.smoothFollow && this.damping < 1.0) {\n\t\t\t\t\t// account for floating precision and check if we are close \"enough\"\n\t\t\t\t\tif (\n\t\t\t\t\t\ttoBeCloseTo(targetV.x, this.pos.x, 2) &&\n\t\t\t\t\t\ttoBeCloseTo(targetV.y, this.pos.y, 2)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.pos.setV(targetV);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Frame-rate independent follow: re-anchor the parametric\n\t\t\t\t\t\t// `damping` to a damp lambda at the engine's target rate.\n\t\t\t\t\t\tconst lambda = -Math.log(1 - this.damping) * timer.maxfps;\n\t\t\t\t\t\tconst dts = (dt ?? timer.step) / 1000;\n\t\t\t\t\t\tthis.pos.damp(targetV, lambda, dts);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.pos.setV(targetV);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t/** @ignore */\n\toverride update(dt?: number): boolean {\n\t\t// update the camera position\n\t\tthis.updateTarget(dt);\n\n\t\t// update all active camera effects\n\t\tif (this.cameraEffects.length > 0) {\n\t\t\tfor (const fx of this.cameraEffects) {\n\t\t\t\tfx.update(dt ?? 0);\n\t\t\t}\n\t\t\t// remove completed effects\n\t\t\tfor (let i = this.cameraEffects.length - 1; i >= 0; i--) {\n\t\t\t\tif (this.cameraEffects[i].isComplete) {\n\t\t\t\t\tthis.cameraEffects[i].destroy();\n\t\t\t\t\tthis.cameraEffects.splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t}\n\n\t\tif (this.isDirty) {\n\t\t\t//publish the corresponding message\n\t\t\temit(VIEWPORT_ONCHANGE, this.pos);\n\t\t}\n\n\t\tif (!this.currentTransform.isIdentity()) {\n\t\t\tthis.invCurrentTransform.copy(this.currentTransform).invert();\n\t\t} else {\n\t\t\t// reset to default\n\t\t\tthis.invCurrentTransform.identity();\n\t\t}\n\n\t\treturn super.update(dt ?? 0);\n\t}\n\n\t/**\n\t * shake the camera\n\t * @param intensity - maximum offset that the screen can be moved\n\t * while shaking\n\t * @param duration - expressed in milliseconds\n\t * @param [axis=app.viewport.AXIS.BOTH] - specify on which axis to apply the shake effect (see {@link Camera2d.AXIS})\n\t * @param [onComplete] - callback once shaking effect is over\n\t * @param [force] - if true this will override the current effect\n\t * @example\n\t * // shake it baby !\n\t * app.viewport.shake(10, 500, app.viewport.AXIS.BOTH);\n\t */\n\tshake(\n\t\tintensity: number,\n\t\tduration: number,\n\t\taxis?: number,\n\t\tonComplete?: () => void,\n\t\tforce?: boolean,\n\t): void {\n\t\t// check if a shake is already running\n\t\tconst existing = this.getCameraEffect(ShakeEffect);\n\t\tif (existing && force !== true) {\n\t\t\treturn;\n\t\t}\n\t\tif (existing) {\n\t\t\tthis.removeCameraEffect(existing);\n\t\t}\n\t\tthis.addCameraEffect(\n\t\t\tnew ShakeEffect(this, {\n\t\t\t\tintensity,\n\t\t\t\tduration,\n\t\t\t\taxis: axis || this.AXIS.BOTH,\n\t\t\t\tonComplete,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * fadeOut(flash) effect<p>\n\t * screen is filled with the specified color and slowly goes back to normal\n\t * @param color - a CSS color value\n\t * @param [duration=1000] - expressed in milliseconds\n\t * @param [onComplete] - callback once effect is over\n\t * @example\n\t * // fade the camera to white upon dying, reload the level, and then fade out back\n\t * app.viewport.fadeIn(\"#fff\", 150, function() {\n\t *     me.audio.play(\"die\", false);\n\t *     me.level.reload();\n\t *     app.viewport.fadeOut(\"#fff\", 150);\n\t * });\n\t */\n\tfadeOut(\n\t\tcolor: Color | string,\n\t\tduration: number = 1000,\n\t\tonComplete?: () => void,\n\t): void {\n\t\tthis.addCameraEffect(\n\t\t\tnew FadeEffect(this, {\n\t\t\t\tcolor,\n\t\t\t\tduration,\n\t\t\t\tdirection: \"out\",\n\t\t\t\tonComplete,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * fadeIn effect <p>\n\t * fade to the specified color\n\t * @param color - a CSS color value\n\t * @param [duration=1000] - expressed in milliseconds\n\t * @param [onComplete] - callback once effect is over\n\t * @example\n\t * // flash the camera to white for 75ms\n\t * app.viewport.fadeIn(\"#FFFFFF\", 75);\n\t */\n\tfadeIn(\n\t\tcolor: Color | string,\n\t\tduration: number = 1000,\n\t\tonComplete?: () => void,\n\t): void {\n\t\tthis.addCameraEffect(\n\t\t\tnew FadeEffect(this, {\n\t\t\t\tcolor,\n\t\t\t\tduration,\n\t\t\t\tdirection: \"in\",\n\t\t\t\tonComplete,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Add a camera effect to this camera.\n\t * @param effect - the camera effect to add\n\t * @returns the added effect\n\t * @example\n\t * camera.addCameraEffect(new ShakeEffect(camera, { intensity: 10, duration: 500 }));\n\t */\n\taddCameraEffect<T extends CameraEffect>(effect: T): T {\n\t\tthis.cameraEffects.push(effect);\n\t\treturn effect;\n\t}\n\n\t/**\n\t * Get the first camera effect matching the given class.\n\t * @param effectClass - the effect class to search for\n\t * @returns the first matching effect, or undefined\n\t * @example\n\t * const shake = camera.getCameraEffect(ShakeEffect);\n\t */\n\tgetCameraEffect<T extends CameraEffect>(\n\t\teffectClass: new (...args: any[]) => T,\n\t): T | undefined {\n\t\treturn this.cameraEffects.find((fx) => fx instanceof effectClass) as\n\t\t\t| T\n\t\t\t| undefined;\n\t}\n\n\t/**\n\t * Remove a specific camera effect instance.\n\t * @param effect - the effect to remove\n\t * @example\n\t * const fade = camera.getCameraEffect(FadeEffect);\n\t * if (fade) camera.removeCameraEffect(fade);\n\t */\n\tremoveCameraEffect(effect: CameraEffect): void {\n\t\tconst idx = this.cameraEffects.indexOf(effect);\n\t\tif (idx !== -1) {\n\t\t\teffect.destroy();\n\t\t\tthis.cameraEffects.splice(idx, 1);\n\t\t}\n\t}\n\n\t/**\n\t * set the camera position around the specified object\n\t * @param target - the renderable to focus the camera on\n\t */\n\tfocusOn(target: Renderable): void {\n\t\tconst bounds = target.getBounds() as Bounds;\n\t\tthis.moveTo(\n\t\t\tbounds.left + bounds.width / 2 - this.width / 2,\n\t\t\tbounds.top + bounds.height / 2 - this.height / 2,\n\t\t);\n\t}\n\n\t/**\n\t * check if the specified renderable is in the camera\n\t * @param obj - to be checked against\n\t * @param [floating = obj.floating] - if visibility check should be done against screen coordinates\n\t * @returns true if within the viewport\n\t */\n\tisVisible(obj: Renderable, floating: boolean = obj.floating): boolean {\n\t\tif (floating || obj.floating) {\n\t\t\t// floating objects use screen coordinates, check against the\n\t\t\t// camera viewport area (0, 0, camera.width, camera.height)\n\t\t\tconst objBounds = obj.getBounds();\n\t\t\tif (\n\t\t\t\tobjBounds.left === Infinity &&\n\t\t\t\tobjBounds.top === Infinity &&\n\t\t\t\tobjBounds.right === -Infinity &&\n\t\t\t\tobjBounds.bottom === -Infinity\n\t\t\t) {\n\t\t\t\t// containers with no explicit size (cleared Infinity bounds) are always visible\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn (\n\t\t\t\tobjBounds.left <= this.width &&\n\t\t\t\tobjBounds.right >= 0 &&\n\t\t\t\tobjBounds.top <= this.height &&\n\t\t\t\tobjBounds.bottom >= 0\n\t\t\t);\n\t\t}\n\t\t// non-floating objects use world coordinates, check against\n\t\t// the camera's visible world area (accounts for position and zoom)\n\t\treturn obj.getBounds().overlaps(this.worldView);\n\t}\n\n\t/**\n\t * convert the given \"local\" (screen) coordinates into world coordinates\n\t * @param x - the x coordinate of the local point to be converted\n\t * @param y - the y coordinate of the local point to be converted\n\t * @param [v] - an optional vector object where to set the converted value\n\t * @returns the converted world coordinates as a Vector2d\n\t */\n\tlocalToWorld(x: number, y: number, v?: Vector2d): Vector2d {\n\t\tv = v || vector2dPool.get();\n\t\tv.set(x, y).add(this.pos).sub(game.world.pos);\n\t\tif (!this.currentTransform.isIdentity()) {\n\t\t\tthis.invCurrentTransform.apply(v);\n\t\t}\n\t\treturn v;\n\t}\n\n\t/**\n\t * convert the given world coordinates into \"local\" (screen) coordinates\n\t * @param x - the x world coordinate to be converted\n\t * @param y - the y world coordinate to be converted\n\t * @param [v] - an optional vector object where to set the converted value\n\t * @returns a vector with the converted local coordinates\n\t */\n\tworldToLocal(x: number, y: number, v?: Vector2d): Vector2d {\n\t\tv = v || vector2dPool.get();\n\t\tv.set(x, y);\n\t\tif (!this.currentTransform.isIdentity()) {\n\t\t\tthis.currentTransform.apply(v);\n\t\t}\n\t\treturn v.sub(this.pos).add(game.world.pos);\n\t}\n\n\t/**\n\t * Build and install the world + screen projections used when this\n\t * camera is non-default (split-screen, picture-in-picture, etc.).\n\t * Default behavior is a shifted ortho that places the camera's\n\t * world view at the camera's screen-rect within the full renderer\n\t * viewport. Subclasses (e.g. Camera3d) override this to install a\n\t * perspective `worldProjection` instead.\n\t * @ignore\n\t */\n\t_setupNonDefaultProjection(renderer: Renderer): void {\n\t\tconst left = -this.screenX / this.zoom;\n\t\tconst top = -this.screenY / this.zoom;\n\t\tconst rw = renderer.width / this.zoom;\n\t\tconst rh = renderer.height / this.zoom;\n\n\t\tthis.worldProjection.ortho(\n\t\t\tleft,\n\t\t\tleft + rw,\n\t\t\ttop + rh,\n\t\t\ttop,\n\t\t\tthis.near,\n\t\t\tthis.far,\n\t\t);\n\t\trenderer.setProjection(this.worldProjection);\n\n\t\tthis.screenProjection.ortho(\n\t\t\t-this.screenX,\n\t\t\t-this.screenX + renderer.width,\n\t\t\t-this.screenY + renderer.height,\n\t\t\t-this.screenY,\n\t\t\tthis.near,\n\t\t\tthis.far,\n\t\t);\n\t}\n\n\t/**\n\t * render the camera effects\n\t * @ignore\n\t */\n\tdrawFX(renderer: Renderer): void {\n\t\tfor (const fx of this.cameraEffects) {\n\t\t\tfx.draw(renderer, this.width, this.height);\n\t\t}\n\t}\n\n\t/**\n\t * draw all objects visible in this viewport\n\t * @ignore\n\t */\n\toverride draw(renderer: Renderer, container: Container): void {\n\t\t// cast to any to access canvas/webgl renderer-specific methods not on base Renderer\n\t\tconst r = renderer as any;\n\t\tconst isNonDefault = !this.isDefault;\n\t\t// for non-default cameras, compensate for the world container's\n\t\t// centering offset (applied inside container.draw via renderer.translate)\n\t\tconst containerOffsetX = isNonDefault ? container.pos.x : 0;\n\t\tconst containerOffsetY = isNonDefault ? container.pos.y : 0;\n\t\tconst translateX = this.pos.x + this.offset.x + containerOffsetX;\n\t\tconst translateY = this.pos.y + this.offset.y + containerOffsetY;\n\n\t\t// sync the built-in colorMatrix: append as final pass if non-identity\n\t\tif (!this.colorMatrix.isIdentity()) {\n\t\t\tif (!this._colorMatrixEffect) {\n\t\t\t\tthis._colorMatrixEffect = new ColorMatrixEffect(renderer as any);\n\t\t\t}\n\t\t\tthis._colorMatrixEffect.reset().multiply(this.colorMatrix);\n\t\t\tthis.postEffects.push(this._colorMatrixEffect);\n\t\t}\n\n\t\t// post-effect: bind FBO if shader effects are set (WebGL only)\n\t\tconst usePostEffect = r.beginPostEffect(this);\n\n\t\t// apply the world-to-camera-view transform on the container.\n\t\t// Camera2d translates by -camera.pos; Camera3d overrides this\n\t\t// hook to additionally apply the camera's rotation (pitch / yaw)\n\t\t// in the correct order (R⁻¹ ∘ T(-pos), via post-multiplication).\n\t\tthis._applyContainerViewTransform(container, translateX, translateY);\n\n\t\tthis.preDraw(r);\n\n\t\t// clip to camera viewport on screen (skip when rendering to FBO —\n\t\t// the FBO itself defines the render area)\n\t\tif (!usePostEffect) {\n\t\t\tr.clipRect(this.screenX, this.screenY, this.width, this.height);\n\t\t}\n\n\t\t// set camera projection for non-default cameras\n\t\tif (isNonDefault) {\n\t\t\tthis._setupNonDefaultProjection(renderer);\n\t\t} else {\n\t\t\trenderer.setProjection(this.projectionMatrix);\n\t\t}\n\n\t\t// Upload active Light2d instances for the lit sprite pipeline.\n\t\t// Done here — after `setProjection()` (which can flush the\n\t\t// current batch) and before `container.draw()` walks the world\n\t\t// tree — so the lights are guaranteed to apply to every lit quad\n\t\t// pushed by the upcoming draw walk. Stage stays renderer-agnostic\n\t\t// (just exposes the raw lights set + ambient color); the renderer\n\t\t// decides how to encode them. Canvas's `setLightUniforms` is a\n\t\t// no-op.\n\t\tconst stage = state.current();\n\t\trenderer.setLightUniforms(\n\t\t\tstage?._activeLights,\n\t\t\tstage?.ambientLightingColor,\n\t\t\ttranslateX,\n\t\t\ttranslateY,\n\t\t);\n\n\t\tcontainer.preDraw(r);\n\n\t\t// for non-default cameras, temporarily expand the camera rect to the\n\t\t// visible world area so TMXLayer draws all tiles within the zoomed view.\n\t\t// When zoom is 1, worldView matches camera size — no visible effect.\n\t\tif (isNonDefault) {\n\t\t\tconst view = this.worldView;\n\t\t\tconst savedWidth = this.width;\n\t\t\tconst savedHeight = this.height;\n\t\t\tthis.width = view.width;\n\t\t\tthis.height = view.height;\n\t\t\tcontainer.draw(r, this);\n\t\t\tthis.width = savedWidth;\n\t\t\tthis.height = savedHeight;\n\t\t} else {\n\t\t\tcontainer.draw(r, this);\n\t\t}\n\n\t\t// Ground shadows (#1515) are held back until every opaque mesh in the\n\t\t// world is down, and this is where that is true — still inside the\n\t\t// camera's FBO/post-effect bracket, so they land in the frame the\n\t\t// camera is about to resolve rather than after it has been composited.\n\t\trenderer.flushGroundShadows();\n\n\t\t// draw the viewport/camera effects\n\t\tthis.drawFX(renderer);\n\n\t\tcontainer.postDraw(r);\n\n\t\tthis.postDraw(r);\n\n\t\t// stage-level lighting overlay — lights themselves render through the\n\t\t// world tree as Renderables; the Stage paints the ambient fill with\n\t\t// per-light cutouts here. Done inside the FBO bracket so any post-\n\t\t// effect on this camera (vignette, ColorMatrix, …) wraps the lighting.\n\t\t// duck-type rather than `instanceof Stage` to avoid a circular runtime\n\t\t// import between camera2d.ts and stage.ts. Pass the same\n\t\t// translateX/translateY the world container was rendered with so\n\t\t// non-default cameras (minimap/splitscreen) line up correctly —\n\t\t// `camera.pos + camera.offset` alone misses `containerOffsetX/Y`.\n\t\tif (stage) {\n\t\t\tstage.drawLighting(renderer, this, translateX, translateY);\n\t\t}\n\n\t\t// post-effect: unbind FBO and blit to screen through shader effect\n\t\tr.endPostEffect(this);\n\n\t\t// Restore the full-canvas viewport for any non-default camera.\n\t\t// Camera2d's non-default path never changes it (the sub-rect\n\t\t// is handled via the ortho left/right/top/bottom shift +\n\t\t// `clipRect`), so this is a no-op. Camera3d's non-default\n\t\t// path DOES change it inside `_setupNonDefaultProjection` to\n\t\t// remap NDC into the sub-rect — without this restore, the\n\t\t// next camera in the stage's render order would inherit the\n\t\t// shrunken WebGL viewport. `endPostEffect` already does this\n\t\t// for cameras with post-effects (FBO bracket), but non-FBO\n\t\t// non-default Camera3d cameras would otherwise leak.\n\t\tif (isNonDefault) {\n\t\t\trenderer.setViewport(0, 0, renderer.width, renderer.height);\n\t\t}\n\n\t\t// remove the transient colorMatrix effect so it doesn't persist between frames\n\t\tif (this._colorMatrixEffect) {\n\t\t\tconst idx = this.postEffects.indexOf(this._colorMatrixEffect);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis.postEffects.splice(idx, 1);\n\t\t\t}\n\t\t}\n\n\t\t// revert the world-to-camera-view transform applied above\n\t\tthis._revertContainerViewTransform(container, translateX, translateY);\n\t}\n\n\t/**\n\t * Apply the world-to-camera-view transform to the container before\n\t * its draw walk. Camera2d translates by `-camera.pos` (plus shake\n\t * offset); Camera3d overrides this to additionally rotate by\n\t * `-camera.pitch` / `-camera.yaw` in the correct order.\n\t * @ignore\n\t */\n\t_applyContainerViewTransform(\n\t\tcontainer: Container,\n\t\ttranslateX: number,\n\t\ttranslateY: number,\n\t): void {\n\t\tcontainer.translate(-translateX, -translateY);\n\t}\n\n\t/**\n\t * Revert the transform applied by {@link Camera2d#_applyContainerViewTransform}.\n\t * Must undo each mutation in reverse order. Subclasses overriding\n\t * `_applyContainerViewTransform` should override this too.\n\t * @ignore\n\t */\n\t_revertContainerViewTransform(\n\t\tcontainer: Container,\n\t\ttranslateX: number,\n\t\ttranslateY: number,\n\t): void {\n\t\tcontainer.translate(translateX, translateY);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\toverride destroy(): void {\n\t\t// unsubscribe the constructor-registered global listeners — without\n\t\t// this a destroyed camera stays reachable forever and keeps reacting\n\t\t// to GAME_RESET (TypeError on freed state) and CANVAS_ONRESIZE\n\t\t// (duplicate VIEWPORT_ONRESIZE emits from zombie cameras)\n\t\t/* eslint-disable @typescript-eslint/unbound-method -- listener identity must match the `on(...)` registration */\n\t\toff(GAME_RESET, this.reset, this);\n\t\toff(CANVAS_ONRESIZE, this.resize, this);\n\t\t/* eslint-enable @typescript-eslint/unbound-method */\n\t\t// clean up the internal colorMatrix effect (may not be in postEffects if identity)\n\t\tif (this._colorMatrixEffect) {\n\t\t\tif (typeof this._colorMatrixEffect.destroy === \"function\") {\n\t\t\t\tthis._colorMatrixEffect.destroy();\n\t\t\t}\n\t\t\tthis._colorMatrixEffect = null;\n\t\t}\n\t\tsuper.destroy();\n\t}\n}\n","/**\n * Shared `settings.anchorPoint` resolution for every renderable that accepts\n * it (Sprite, Entity, Collectable, ImageLayer, Text, BitmapText, Sprite3d).\n * Same normalized convention as {@link Renderable#anchorPoint}: x 0=left →\n * 1=right, y 0=top → 1=bottom.\n */\n\n/**\n * Named anchor-point presets accepted anywhere `settings.anchorPoint` is —\n * self-documenting shorthands for the common `{x, y}` pairs.\n * @ignore\n */\nexport const ANCHOR_POINT_PRESETS = Object.freeze({\n\tcenter: Object.freeze({ x: 0.5, y: 0.5 }),\n\ttop: Object.freeze({ x: 0.5, y: 0 }),\n\tbottom: Object.freeze({ x: 0.5, y: 1 }),\n\tleft: Object.freeze({ x: 0, y: 0.5 }),\n\tright: Object.freeze({ x: 1, y: 0.5 }),\n\t\"top-left\": Object.freeze({ x: 0, y: 0 }),\n\t\"top-right\": Object.freeze({ x: 1, y: 0 }),\n\t\"bottom-left\": Object.freeze({ x: 0, y: 1 }),\n\t\"bottom-right\": Object.freeze({ x: 1, y: 1 }),\n});\n\n/**\n * An anchor value as accepted by `settings.anchorPoint`: a preset name or\n * any object with numeric `x`/`y` (plain object, Vector2d, ObservablePoint).\n * @ignore\n */\nexport type AnchorPointValue =\n\t| keyof typeof ANCHOR_POINT_PRESETS\n\t| { x: number; y: number };\n\nconst isAnchorPair = (value: unknown): value is { x: number; y: number } => {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\tconst pair = value as { x?: unknown; y?: unknown };\n\treturn (\n\t\ttypeof pair.x === \"number\" &&\n\t\tNumber.isFinite(pair.x) &&\n\t\ttypeof pair.y === \"number\" &&\n\t\tNumber.isFinite(pair.y)\n\t);\n};\n\n/**\n * Resolve a `settings.anchorPoint` value — a preset name or an `{x, y}`\n * object — into a fresh `{x, y}` pair (never a shared/frozen object).\n * Values are passed through unclamped: anchors outside 0..1 are legal.\n *\n * Invalid values (unknown preset, malformed object, non-finite numbers) are\n * handled per surface:\n * - with a `fallback` (the legacy 2D classes): logs a console warning and\n *   returns the fallback — full backward compatibility with the historical\n *   silent `set(undefined, undefined)` → `(0, 0)` outcome, nothing that\n *   constructed before can start throwing;\n * - without (new surfaces, e.g. Sprite3d): throws.\n * @ignore\n */\nexport function resolveAnchorPoint(\n\tvalue: unknown,\n\towner = \"Renderable\",\n\tfallback?: { x: number; y: number },\n): { x: number; y: number } {\n\tif (typeof value === \"string\") {\n\t\tconst preset =\n\t\t\tANCHOR_POINT_PRESETS[value as keyof typeof ANCHOR_POINT_PRESETS];\n\t\tif (typeof preset !== \"undefined\") {\n\t\t\treturn { x: preset.x, y: preset.y };\n\t\t}\n\t} else if (isAnchorPair(value)) {\n\t\treturn { x: value.x, y: value.y };\n\t}\n\tif (typeof fallback !== \"undefined\") {\n\t\tconsole.warn(\n\t\t\t`${owner}: invalid anchorPoint value (expected a preset name — ${Object.keys(\n\t\t\t\tANCHOR_POINT_PRESETS,\n\t\t\t).join(\", \")} — or an {x, y} object); falling back to (${fallback.x}, ${\n\t\t\t\tfallback.y\n\t\t\t})`,\n\t\t);\n\t\treturn { x: fallback.x, y: fallback.y };\n\t}\n\tthrow new Error(\n\t\t`${owner}: invalid anchorPoint value (expected a preset name — ${Object.keys(\n\t\t\tANCHOR_POINT_PRESETS,\n\t\t).join(\", \")} — or an {x, y} object)`,\n\t);\n}\n","/**\n * Normalized options for a played animation, shared by the 2D {@link Sprite}\n * frame-animation path and the 3D {@link Mesh} keyframe-animation path so both\n * accept the same `setCurrentAnimation(name, …)` second argument.\n * @category Animation\n */\nexport interface AnimationOptions {\n\t/** called when the animation completes a cycle (each loop, or once if `loop: false`). */\n\tonComplete?: (() => unknown) | undefined;\n\t/** name of an animation to switch to when this one finishes. */\n\tnext?: string | undefined;\n\t/** loop forever (default) or play once and hold the last frame. */\n\tloop: boolean;\n\t/** playback rate multiplier (1 = authored speed). */\n\tspeed: number;\n\t/**\n\t * the argument was a bare function — the legacy `Sprite` callback whose\n\t * `false` return holds the last frame. Carried so callers can preserve that\n\t * exact contract; not part of the public options shape.\n\t * @ignore\n\t */\n\tlegacyFn?: boolean;\n}\n\n/**\n * Normalize the polymorphic second argument of `setCurrentAnimation` into a\n * uniform {@link AnimationOptions}. Accepts:\n * - `undefined` → loop forever\n * - a `string` → the name of the next animation to chain to\n * - a `function` → legacy completion callback (return `false` to hold the last frame)\n * - an options object → `{ onComplete, next, loop, speed }`\n * @param arg - the second argument passed to `setCurrentAnimation`\n * @returns the normalized options\n * @category Animation\n */\nexport function parseAnimationOptions(\n\targ?:\n\t\t| string\n\t\t| (() => unknown)\n\t\t| {\n\t\t\t\tonComplete?: () => unknown;\n\t\t\t\tnext?: string;\n\t\t\t\tloop?: boolean;\n\t\t\t\tspeed?: number;\n\t\t  }\n\t\t// `null` is passed by the internal animation-chain call\n\t\t// (`setCurrentAnimation(next, null, true)`), so it must be accepted.\n\t\t| null,\n): AnimationOptions {\n\tif (typeof arg === \"string\") {\n\t\treturn { next: arg, loop: true, speed: 1 };\n\t}\n\tif (typeof arg === \"function\") {\n\t\treturn { onComplete: arg, loop: true, speed: 1, legacyFn: true };\n\t}\n\tif (arg !== null && typeof arg === \"object\") {\n\t\treturn {\n\t\t\tonComplete: arg.onComplete,\n\t\t\tnext: arg.next,\n\t\t\t// only an explicit `false` disables looping\n\t\t\tloop: arg.loop !== false,\n\t\t\tspeed: typeof arg.speed === \"number\" ? arg.speed : 1,\n\t\t};\n\t}\n\treturn { loop: true, speed: 1 };\n}\n","import { vector2dPool } from \"../math/vector2d.ts\";\nimport { parseAnimationOptions } from \"./animation.ts\";\n\n/**\n * additional import for TypeScript\n * @import { TextureAtlas } from \"../video/texture/atlas.js\";\n * @import { Vector2d } from \"../math/vector2d.js\";\n */\n\n/**\n * The shared **frame-animation engine** behind {@link Sprite} (2D) and\n * {@link Sprite3d} (3D billboards). It *owns* the animation state — definitions,\n * the current frame, timing, looping and chaining — everything independent of\n * how a frame is ultimately *drawn*, and drives its host renderable through a\n * small contract:\n *\n * - it reads the host's resolved texture (`host.source`, `host.textureAtlas`,\n *   `host.atlasIndices`) to turn a frame index/name into a region;\n * - it calls the `applyFrame(region)` callback passed to the constructor whenever\n *   the frame changes — the host applies it to its own geometry ({@link Sprite}\n *   swaps its source sub-texture, size and anchor; {@link Sprite3d} maps the\n *   region onto its quad's UVs + vertices) **and marks itself dirty there**;\n * - it fires `host.onended()` at each cycle end.\n *\n * The engine owns no dirty flag: a frame change flows through `applyFrame`, where\n * the host sets its own `isDirty`. Callers read dirtiness from the host (the\n * host's `update()` returns `super.update()` → `isDirty`).\n *\n * Hosts expose the public-facing state (`anim`, `current`, `animationspeed`,\n * `animationpause`, …) as thin accessors onto this engine, so 2D and 3D frame\n * animation share one implementation with no behavioral fork.\n * @category Animation\n */\nexport default class FrameAnimation {\n\t/**\n\t * @param {object} host - the renderable this engine drives. Read for the\n\t * texture it animates (`source` / `textureAtlas` / `atlasIndices`) and the\n\t * mutable cycle-end callback (`onended`). These are read lazily (the texture is\n\t * usually resolved after the engine is constructed), which is why they live on\n\t * the host rather than being passed in. The engine does not touch `isDirty` —\n\t * the host marks itself dirty inside `applyFrame`.\n\t * @param {(region: object) => void} applyFrame - called whenever the frame\n\t * changes, with the selected texture region; the host applies it to its own\n\t * geometry (a {@link Sprite} swaps its sub-texture / size / anchor, a\n\t * {@link Sprite3d} remaps its quad's UVs + vertices).\n\t */\n\tconstructor(host, applyFrame) {\n\t\t/**\n\t\t * the host renderable this engine draws through.\n\t\t * @type {object}\n\t\t * @ignore\n\t\t */\n\t\tthis.host = host;\n\n\t\t/**\n\t\t * geometry hook invoked on every frame change (see constructor).\n\t\t * @type {(region: object) => void}\n\t\t * @ignore\n\t\t */\n\t\tthis._applyFrame = applyFrame;\n\n\t\t/** defined animations, keyed by id @type {object} */\n\t\tthis.anim = {};\n\n\t\t/** animation to chain to / completion callback on cycle end @ignore */\n\t\tthis.resetAnim = undefined;\n\n\t\t/** current frame info @type {object} */\n\t\tthis.current = {\n\t\t\t// the current animation name\n\t\t\tname: undefined,\n\t\t\t// length of the current animation\n\t\t\tlength: 0,\n\t\t\t// current frame texture offset\n\t\t\toffset: vector2dPool.get(0, 0),\n\t\t\t// current frame size\n\t\t\twidth: 0,\n\t\t\theight: 0,\n\t\t\t// source rotation angle for pre-rotating the source image\n\t\t\tangle: 0,\n\t\t\t// current frame index\n\t\t\tidx: 0,\n\t\t\t// trim offset for trimmed sprites\n\t\t\ttrim: null,\n\t\t};\n\n\t\t/** elapsed time within the current frame, in ms @type {number} */\n\t\tthis.dt = 0;\n\n\t\t/** default frame cycling speed (ms between frames) @type {number} */\n\t\tthis.animationspeed = 100;\n\n\t\t/** pause flag — freezes the current frame @type {boolean} */\n\t\tthis.animationpause = false;\n\n\t\t/** per-play speed multiplier (1 = authored speed) @ignore */\n\t\tthis._animSpeed = 1;\n\n\t\t/** set once a `loop:false` animation has finished its single cycle @ignore */\n\t\tthis._animDone = false;\n\t}\n\n\t/**\n\t * add an animation definition (see {@link Sprite#addAnimation}).\n\t * @param {string} name - animation id\n\t * @param {number[]|string[]|object[]} index - frame indices / names / objects\n\t * @param {number} [animationspeed] - cycling speed in ms\n\t * @returns {number} number of frames added (0 if no texture atlas)\n\t */\n\taddAnimation(name, index, animationspeed) {\n\t\tconst host = this.host;\n\t\tthis.anim[name] = {\n\t\t\tname: name,\n\t\t\tframes: [],\n\t\t\tidx: 0,\n\t\t\tlength: 0,\n\t\t};\n\n\t\t// # of frames\n\t\tlet counter = 0;\n\n\t\tif (typeof host.textureAtlas !== \"object\") {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (index == null) {\n\t\t\tindex = [];\n\t\t\t// create a default animation with all frame\n\t\t\tObject.keys(host.textureAtlas).forEach((v, i) => {\n\t\t\t\tindex[i] = i;\n\t\t\t});\n\t\t}\n\n\t\t// set each frame configuration (offset, size, etc..)\n\t\tfor (let i = 0, len = index.length; i < len; i++) {\n\t\t\tconst frame = index[i];\n\t\t\tlet frameObject;\n\t\t\tif (typeof frame === \"number\" || typeof frame === \"string\") {\n\t\t\t\tframeObject = {\n\t\t\t\t\tname: frame,\n\t\t\t\t\tdelay: animationspeed || this.animationspeed,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tframeObject = frame;\n\t\t\t}\n\t\t\tconst frameObjectName = frameObject.name;\n\t\t\tif (typeof frameObjectName === \"number\") {\n\t\t\t\tif (typeof host.textureAtlas[frameObjectName] !== \"undefined\") {\n\t\t\t\t\t// see https://github.com/melonjs/melonJS/issues/1281\n\t\t\t\t\tthis.anim[name].frames[i] = Object.assign(\n\t\t\t\t\t\t{},\n\t\t\t\t\t\thost.textureAtlas[frameObjectName],\n\t\t\t\t\t\tframeObject,\n\t\t\t\t\t);\n\t\t\t\t\tcounter++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// string\n\t\t\t\tif (host.source.getFormat().includes(\"Spritesheet\")) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"string parameters for addAnimation are not allowed for standard spritesheet based Texture\",\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.anim[name].frames[i] = Object.assign(\n\t\t\t\t\t\t{},\n\t\t\t\t\t\thost.textureAtlas[host.atlasIndices[frameObjectName]],\n\t\t\t\t\t\tframeObject,\n\t\t\t\t\t);\n\t\t\t\t\tcounter++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.anim[name].length = counter;\n\n\t\treturn counter;\n\t}\n\n\t/**\n\t * select the active animation (see {@link Sprite#setCurrentAnimation}).\n\t * @param {string} name - animation id\n\t * @param {string|Function|object} [resetAnim] - loop / chain / completion behavior\n\t * @param {boolean} [preserve_dt=false] - keep the elapsed-frame timer\n\t * @returns {object} the host (for method chaining)\n\t */\n\tsetCurrentAnimation(name, resetAnim, preserve_dt = false) {\n\t\tif (typeof this.anim[name] !== \"undefined\") {\n\t\t\t// re-selecting the already-current animation is a no-op (so the\n\t\t\t// common call-every-frame idiom doesn't restart it) — EXCEPT when\n\t\t\t// a play-once (`loop: false`) run has completed: without the\n\t\t\t// `_animDone` escape, a finished animation could never be\n\t\t\t// replayed (`_animDone` stayed set and new options were dropped)\n\t\t\tif (!this.isCurrentAnimation(name) || this._animDone) {\n\t\t\t\tthis.current.name = name;\n\t\t\t\tthis.current.length = this.anim[this.current.name].length;\n\t\t\t\tconst opts = parseAnimationOptions(resetAnim);\n\t\t\t\tthis._animSpeed = opts.speed;\n\t\t\t\tthis._animDone = false;\n\t\t\t\tconst onComplete = opts.onComplete;\n\t\t\t\tif (opts.legacyFn) {\n\t\t\t\t\t// legacy bare-function callback: invoked at each loop end,\n\t\t\t\t\t// return `false` to hold the last frame (contract unchanged)\n\t\t\t\t\tthis.resetAnim = onComplete;\n\t\t\t\t} else if (typeof opts.next === \"string\") {\n\t\t\t\t\t// chain to another animation when this one ends (the legacy\n\t\t\t\t\t// string form and the options `next` field), firing\n\t\t\t\t\t// `onComplete` first when provided\n\t\t\t\t\tconst next = opts.next;\n\t\t\t\t\tthis.resetAnim = () => {\n\t\t\t\t\t\tif (typeof onComplete === \"function\") {\n\t\t\t\t\t\t\tonComplete();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.setCurrentAnimation(next, null, true);\n\t\t\t\t\t};\n\t\t\t\t} else if (opts.loop === false) {\n\t\t\t\t\t// play once: fire onComplete, hold the last frame, and stop\n\t\t\t\t\t// advancing (without touching `animationpause`)\n\t\t\t\t\tthis.resetAnim = () => {\n\t\t\t\t\t\tif (typeof onComplete === \"function\") {\n\t\t\t\t\t\t\tonComplete();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis._animDone = true;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t};\n\t\t\t\t} else if (typeof onComplete === \"function\") {\n\t\t\t\t\t// loop forever, firing onComplete at each cycle\n\t\t\t\t\tthis.resetAnim = () => {\n\t\t\t\t\t\tonComplete();\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tthis.resetAnim = undefined;\n\t\t\t\t}\n\t\t\t\t// `setAnimationFrame(0)` applies frame 0 → `_applyFrame`, which is\n\t\t\t\t// where the host marks itself dirty (the engine never touches it).\n\t\t\t\tthis.setAnimationFrame(0);\n\t\t\t\tif (!preserve_dt) {\n\t\t\t\t\tthis.dt = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(\"animation id '\" + name + \"' not defined\");\n\t\t}\n\t\treturn this.host;\n\t}\n\n\t/**\n\t * reverse the given (or current) animation in place (see {@link Sprite#reverseAnimation}).\n\t * The host marks itself dirty (this path doesn't re-apply a frame, so it's the\n\t * one place a host's `reverseAnimation` wrapper sets `isDirty`).\n\t * @param {string} [name] - animation id\n\t */\n\treverseAnimation(name) {\n\t\tif (typeof name !== \"undefined\" && typeof this.anim[name] !== \"undefined\") {\n\t\t\tthis.anim[name].frames.reverse();\n\t\t} else {\n\t\t\tthis.anim[this.current.name].frames.reverse();\n\t\t}\n\t}\n\n\t/**\n\t * @param {string} name - animation id\n\t * @returns {boolean} true if `name` is the current animation\n\t */\n\tisCurrentAnimation(name) {\n\t\treturn this.current.name === name;\n\t}\n\n\t/**\n\t * @returns {string[]} the names of every defined animation\n\t */\n\tgetAnimationNames() {\n\t\treturn Object.keys(this.anim);\n\t}\n\n\t/**\n\t * apply a texture region as the current frame: store its geometry into\n\t * `current` and hand it to the host to draw (see {@link Sprite#setRegion}).\n\t * @param {object} region - the texture region object\n\t * @returns {object} the host (for method chaining)\n\t */\n\tsetRegion(region) {\n\t\tconst current = this.current;\n\t\t// set the frame offset within the texture\n\t\tcurrent.offset.setV(region.offset);\n\t\t// set angle if defined\n\t\tcurrent.angle = typeof region.angle === \"number\" ? region.angle : 0;\n\t\t// update the current frame size (trimmed dimensions, used for drawing)\n\t\tcurrent.width = region.width;\n\t\tcurrent.height = region.height;\n\t\t// cache trim offset for drawing\n\t\tcurrent.trim = region.trim || null;\n\t\t// hand the region to the host to apply to its own geometry; the host\n\t\t// marks itself dirty inside `_applyFrame` (the engine owns no dirty flag)\n\t\tthis._applyFrame(region);\n\t\treturn this.host;\n\t}\n\n\t/**\n\t * force the current animation frame index (see {@link Sprite#setAnimationFrame}).\n\t * @param {number} [index=0] - animation frame index\n\t * @returns {object} the host (for method chaining)\n\t */\n\tsetAnimationFrame(index = 0) {\n\t\tthis.current.idx = index % this.current.length;\n\t\treturn this.setRegion(\n\t\t\tthis.getAnimationFrameObjectByIndex(this.current.idx),\n\t\t);\n\t}\n\n\t/**\n\t * @returns {number} the current animation frame index\n\t */\n\tgetCurrentAnimationFrame() {\n\t\treturn this.current.idx;\n\t}\n\n\t/**\n\t * the frame object for the given index within the current animation.\n\t * @param {number} id - the frame id\n\t * @returns {object} the frame data\n\t */\n\tgetAnimationFrameObjectByIndex(id) {\n\t\treturn this.anim[this.current.name].frames[id];\n\t}\n\n\t/**\n\t * clear the frame timer and the play-once \"done\" hold, without changing the\n\t * current frame (the timer half of a stop, used by the video path which has\n\t * no frame to rewind to).\n\t */\n\tresetTimer() {\n\t\tthis._animDone = false;\n\t\tthis.dt = 0;\n\t}\n\n\t/**\n\t * reset the current animation to its first frame (the frame-animation half of\n\t * {@link Sprite#stop} — the host still handles any video / pause concerns).\n\t */\n\trewind() {\n\t\tthis.resetTimer();\n\t\tif (this.current.name !== undefined && this.current.length > 0) {\n\t\t\tthis.setAnimationFrame(0);\n\t\t}\n\t}\n\n\t/**\n\t * advance the frame animation by `dt` milliseconds, stepping frames, looping\n\t * and chaining as configured (see {@link Sprite#update}). On a frame change the\n\t * host marks itself dirty via `_applyFrame` (the engine owns no dirty flag);\n\t * this returns whether a frame actually changed this tick — a finer-grained\n\t * signal than the host's `isDirty` (which means \"needs redraw for any reason\").\n\t * @param {number} dt - elapsed time since the last update, in milliseconds\n\t * @returns {boolean} true if a frame changed this tick\n\t */\n\tupdate(dt) {\n\t\tlet changed = false;\n\t\tif (!this.animationpause && !this._animDone && this.current.length > 1) {\n\t\t\tlet duration = this.getAnimationFrameObjectByIndex(\n\t\t\t\tthis.current.idx,\n\t\t\t).delay;\n\t\t\t// `_animSpeed` (per-play multiplier) scales how fast the frame\n\t\t\t// delay is consumed — 2 = twice as fast, 0.5 = half speed\n\t\t\tthis.dt += dt * this._animSpeed;\n\t\t\twhile (this.dt >= duration) {\n\t\t\t\tchanged = true;\n\t\t\t\tthis.dt -= duration;\n\n\t\t\t\tconst nextFrame =\n\t\t\t\t\tthis.current.length > 1 ? this.current.idx + 1 : this.current.idx;\n\t\t\t\tthis.setAnimationFrame(nextFrame);\n\n\t\t\t\t// Switch animation if we reach the end of the strip and a callback is defined\n\t\t\t\tif (this.current.idx === 0) {\n\t\t\t\t\tif (typeof this.host.onended === \"function\") {\n\t\t\t\t\t\tthis.host.onended();\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof this.resetAnim === \"function\") {\n\t\t\t\t\t\t// Otherwise is must be callable\n\t\t\t\t\t\tif (this.resetAnim() === false) {\n\t\t\t\t\t\t\t// Reset to last frame\n\t\t\t\t\t\t\tthis.setAnimationFrame(this.current.length - 1);\n\n\t\t\t\t\t\t\t// Bail early without skipping any more frames.\n\t\t\t\t\t\t\tthis.dt %= duration;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Get next frame duration\n\t\t\t\tduration = this.getAnimationFrameObjectByIndex(this.current.idx).delay;\n\n\t\t\t\t// a zero (or negative) frame delay would never drain `dt` and\n\t\t\t\t// spin this loop forever — treat it as \"fastest possible\":\n\t\t\t\t// advance at most one frame per update tick\n\t\t\t\tif (duration <= 0) {\n\t\t\t\t\tthis.dt = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn changed;\n\t}\n\n\t/**\n\t * release engine-held resources — returns the pooled `current.offset`\n\t * Vector2d to the pool. Call from the host's `destroy()`.\n\t */\n\tdestroy() {\n\t\tif (this.current.offset !== null) {\n\t\t\tvector2dPool.release(this.current.offset);\n\t\t\tthis.current.offset = null;\n\t\t}\n\t\tthis.anim = {};\n\t\tthis.resetAnim = undefined;\n\t}\n}\n","import { game } from \"../application/application.ts\";\nimport { getImage } from \"./../loader/loader.js\";\nimport { Color } from \"../math/color.ts\";\nimport { vector2dPool } from \"../math/vector2d.ts\";\nimport { on, STATE_PAUSE } from \"../system/event.ts\";\nimport { TextureAtlas } from \"./../video/texture/atlas.js\";\nimport Texture2d from \"./../video/texture/texture2d.ts\";\nimport { resolveAnchorPoint } from \"./anchorPoint.ts\";\nimport FrameAnimation from \"./frameAnimation.js\";\nimport Renderable from \"./renderable.js\";\n\n// flicker interval in ms (~15 flashes per second)\nconst FLICKER_INTERVAL_MS = 33;\n\n/**\n * additional import for TypeScript\n * @import {Vector2d} from \"../math/vector2d.js\";\n * @import Renderer from \"./../video/renderer.js\";\n */\n\n/**\n * An object to display a fixed or animated sprite on screen.\n * @category Game Objects\n */\nexport default class Sprite extends Renderable {\n\t/**\n\t * @param {number} x - the x coordinates of the sprite object\n\t * @param {number} y - the y coordinates of the sprite object\n\t * @param {object} settings - Configuration parameters for the Sprite object\n\t * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|Texture2d|TextureAtlas|CompressedImage|string} settings.image - reference to a spritesheet image, a {@link Texture2d} asset (e.g. a {@link TextureAtlas}), a video element, a compressed texture, or a loader key\n\t * @param {string} [settings.name=\"\"] - name of this object\n\t * @param {string} [settings.region] - region name of a specific region to use when using a texture atlas, see {@link TextureAtlas}\n\t * @param {number} [settings.framewidth] - Width of a single frame within the spritesheet\n\t * @param {number} [settings.frameheight] - Height of a single frame within the spritesheet\n\t * @param {string|Color} [settings.tint] - a tint to be applied to this sprite\n\t * @param {number} [settings.flipX] - flip the sprite on the horizontal axis\n\t * @param {number} [settings.flipY] - flip the sprite on the vertical axis\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0.5, y:0.5}] - Anchor point to draw the frame at (defaults to the center of the frame). Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`. For spritesheet atlases the anchor also becomes the cached atlas's per-frame pivot (see {@link TextureAtlas}).\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|Texture2d|string} [settings.normalMap] - optional normal-map texture used for per-pixel lighting (SpriteIlluminator-style). Same layout/UVs as `settings.image`. When omitted (default), the sprite renders unlit and pays no extra cost. Ignored by the Canvas renderer. Note: `HTMLVideoElement` is intentionally not supported — normal maps encode static surface directions in RGB, and the engine caches the GL texture per image reference (a video would freeze on frame 0).\n\t * @example\n\t * // create a single sprite from a standalone image, with anchor in the center\n\t * let sprite = new me.Sprite(0, 0, {\n\t *     image : \"PlayerTexture\",\n\t *     framewidth : 64,\n\t *     frameheight : 64,\n\t *     anchorPoint : new me.Vector2d(0.5, 0.5)\n\t * });\n\t *\n\t * // create a single sprite from a packed texture\n\t * mytexture = new me.TextureAtlas(\n\t *     me.loader.getJSON(\"texture\"),\n\t *     me.loader.getImage(\"texture\")\n\t * );\n\t * let sprite = new me.Sprite(0, 0, {\n\t *     image : mytexture,\n\t *     region : \"npc2.png\",\n\t * });\n\t *\n\t * // create a video sprite\n\t * let videoSprite = new me.Sprite(0, 0, {\n\t *     image : me.loader.getVideo(\"bigbunny\"),\n\t *     anchorPoint : new me.Vector2d(0.5, 0.5)\n\t * });\n\t * // scale the video sprite\n\t * videoSprite.scale(2);\n\t * // start playing the video (if video is preloaded with `autoplay` set to false)\n\t * videoSprite.play();\n\t */\n\tconstructor(x, y, settings) {\n\t\t// call the super constructor\n\t\tsuper(x, y, 0, 0);\n\n\t\t// Resolve the anchor once, up front (preset name or {x, y} object;\n\t\t// invalid values warn + keep the historical silent (0, 0) outcome —\n\t\t// see resolveAnchorPoint). The resolved pair also rides into the\n\t\t// texture-cache descriptor below: spritesheet atlases store it as the\n\t\t// cached per-frame pivot, so presets and {x, y} objects behave\n\t\t// identically on both paths.\n\t\tconst resolvedAnchor = settings.anchorPoint\n\t\t\t? resolveAnchorPoint(settings.anchorPoint, \"Sprite\", { x: 0, y: 0 })\n\t\t\t: null;\n\n\t\t// the shared frame-animation engine — owns this sprite's animation state\n\t\t// (exposed via the `anim` / `current` / `animationspeed` / `animationpause`\n\t\t// accessors below) and calls back into `_applyFrame` on each frame change.\n\t\t// Created up front, before the texture is resolved, so the setup code can\n\t\t// use the accessors.\n\t\tthis._frameAnim = new FrameAnimation(this, (region) => {\n\t\t\tthis._applyFrame(region);\n\t\t});\n\n\t\t/**\n\t\t * global offset for the position to draw from on the source image.\n\t\t * @type {Vector2d}\n\t\t * @default <0.0,0.0>\n\t\t */\n\t\tthis.offset = vector2dPool.get(0, 0);\n\n\t\t/**\n\t\t * true if this is a video sprite (e.g. a HTMLVideoElement was passed as as source)\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.isVideo = false;\n\n\t\t/**\n\t\t * a callback fired when the end of a video or current animation was reached\n\t\t * @type {Function}\n\t\t * @default undefined\n\t\t */\n\t\t// this.onended;\n\n\t\t/**\n\t\t * The source texture object this sprite object is using\n\t\t * @type {TextureAtlas}\n\t\t */\n\t\tthis.source = null;\n\n\t\t/**\n\t\t * backing field for the `normalMap` accessor — see the getter/setter\n\t\t * defined on the class for the public API and validation rules.\n\t\t * @ignore\n\t\t */\n\t\tthis._normalMap = null;\n\n\t\t/**\n\t\t * flicker settings\n\t\t * @ignore\n\t\t */\n\t\tthis._flicker = {\n\t\t\tisFlickering: false,\n\t\t\tduration: 0,\n\t\t\tcallback: null,\n\t\t\telapsed: 0,\n\t\t};\n\n\t\t// set the proper image/texture to use\n\t\tif (settings.image instanceof TextureAtlas) {\n\t\t\tthis.source = settings.image;\n\t\t\tthis.image = this.source.getTexture();\n\t\t\tthis.textureAtlas = settings.image;\n\t\t\t// check for defined region\n\t\t\tif (typeof settings.region !== \"undefined\") {\n\t\t\t\t// use a texture atlas\n\t\t\t\tconst region = this.source.getRegion(settings.region);\n\t\t\t\tif (region) {\n\t\t\t\t\t// set the sprite region within the texture\n\t\t\t\t\tthis.setRegion(region);\n\t\t\t\t} else {\n\t\t\t\t\t// throw an error\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Texture - region for \" + settings.region + \" not found\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// a non-atlas Texture2d (e.g. a procedural NoiseTexture2d) resolves\n\t\t\t// to its baked canvas; an HTMLImageElement/HTMLVideoElement/canvas\n\t\t\t// flows through as-is; a {string} is resolved as a loader key.\n\t\t\tif (settings.image instanceof Texture2d) {\n\t\t\t\tthis.image = settings.image.getTexture();\n\t\t\t} else if (typeof settings.image === \"object\") {\n\t\t\t\tthis.image = settings.image;\n\t\t\t} else {\n\t\t\t\tthis.image = getImage(settings.image);\n\t\t\t}\n\t\t\t// throw an error if image ends up being null/undefined\n\t\t\tif (!this.image) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"me.Sprite: '\" + settings.image + \"' image/texture not found!\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.isVideo =\n\t\t\t\tglobalThis.HTMLVideoElement &&\n\t\t\t\tthis.image instanceof globalThis.HTMLVideoElement;\n\n\t\t\tif (this.isVideo) {\n\t\t\t\tthis.width =\n\t\t\t\t\tthis.current.width =\n\t\t\t\t\tsettings.framewidth =\n\t\t\t\t\t\tsettings.framewidth || this.image.videoWidth;\n\t\t\t\tthis.height =\n\t\t\t\t\tthis.current.height =\n\t\t\t\t\tsettings.frameheight =\n\t\t\t\t\t\tsettings.frameheight || this.image.videoHeight;\n\t\t\t\t// video specific parameter\n\t\t\t\tthis.animationpause = this.image.autoplay !== true;\n\t\t\t\tif (this.animationpause) {\n\t\t\t\t\tthis.image.pause();\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * pause the video when losing focus\n\t\t\t\t * @ignore\n\t\t\t\t */\n\t\t\t\tthis.removeStatePauseListener = on(STATE_PAUSE, () => {\n\t\t\t\t\tthis.image.pause();\n\t\t\t\t});\n\n\t\t\t\t// Keep a video-frame callback pending while this sprite is\n\t\t\t\t// alive: Chromium parks detached (never-in-DOM) videos on a\n\t\t\t\t// ~250ms background timer when the page compositor idles\n\t\t\t\t// (e.g. fullscreen direct scanout) — a pending rVFC forces\n\t\t\t\t// full-rate frame delivery. The callback also stamps a\n\t\t\t\t// monotonic frame counter on the element (the same\n\t\t\t\t// duck-typed `version` convention as NoiseTexture2d) so\n\t\t\t\t// update()/drawImage only repaint/re-upload when a frame\n\t\t\t\t// actually arrived. Browsers without rVFC never get the\n\t\t\t\t// stamp and stay on the legacy repaint-while-playing path.\n\t\t\t\tthis._lastVideoFrameVersion = -1;\n\t\t\t\tif (typeof this.image.requestVideoFrameCallback === \"function\") {\n\t\t\t\t\tthis.image.version ??= 0;\n\t\t\t\t\tconst tick = () => {\n\t\t\t\t\t\t// self-healing bump (never a bare `++`): a sharing\n\t\t\t\t\t\t// sprite's destroy() deletes the stamp, and\n\t\t\t\t\t\t// `undefined++` would poison the counter with NaN\n\t\t\t\t\t\t// (NaN !== NaN → permanently dirty + re-uploading)\n\t\t\t\t\t\tthis.image.version = (this.image.version ?? -1) + 1;\n\t\t\t\t\t\tthis._videoFrameHandle = this.image.requestVideoFrameCallback(tick);\n\t\t\t\t\t};\n\t\t\t\t\tthis._videoFrameHandle = this.image.requestVideoFrameCallback(tick);\n\t\t\t\t}\n\n\t\t\t\t// call the onended when the video has ended\n\t\t\t\tthis.image.onended = () => {\n\t\t\t\t\tif (typeof this.onended === \"function\") {\n\t\t\t\t\t\t// prevent the video from restarting if video.loop is false\n\t\t\t\t\t\tif (!this.image.loop) {\n\t\t\t\t\t\t\tthis.animationpause = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.onended();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// update the default \"current\" frame size\n\t\t\t\tthis.width =\n\t\t\t\t\tthis.current.width =\n\t\t\t\t\tsettings.framewidth =\n\t\t\t\t\t\tsettings.framewidth || this.image.width;\n\t\t\t\tthis.height =\n\t\t\t\t\tthis.current.height =\n\t\t\t\t\tsettings.frameheight =\n\t\t\t\t\t\tsettings.frameheight || this.image.height;\n\t\t\t\tthis.source = game.renderer.cache.get(\n\t\t\t\t\tthis.image,\n\t\t\t\t\t// forward the RESOLVED anchor into the atlas descriptor —\n\t\t\t\t\t// never the caller's raw value (a preset string, or an\n\t\t\t\t\t// aliased Vector2d, must not land in the shared cache)\n\t\t\t\t\tresolvedAnchor\n\t\t\t\t\t\t? Object.assign({}, settings, { anchorPoint: resolvedAnchor })\n\t\t\t\t\t\t: settings,\n\t\t\t\t);\n\t\t\t\tthis.textureAtlas = this.source.getAtlas();\n\t\t\t}\n\t\t}\n\n\t\t// resolve the optional normal-map paired with this sprite (used by\n\t\t// the WebGL renderer's lit pipeline; silently ignored by Canvas).\n\t\t// When the source is a TextureAtlas, prefer its paired normal-map\n\t\t// over an explicit `settings.normalMap` (the atlas drove the layout).\n\t\tif (\n\t\t\tsettings.image instanceof TextureAtlas &&\n\t\t\ttypeof settings.image.getNormalTexture === \"function\"\n\t\t) {\n\t\t\tconst fromAtlas = settings.image.getNormalTexture();\n\t\t\tif (fromAtlas) {\n\t\t\t\tthis.normalMap = fromAtlas;\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tthis.normalMap === null &&\n\t\t\ttypeof settings.normalMap !== \"undefined\" &&\n\t\t\tsettings.normalMap !== null\n\t\t) {\n\t\t\t// strings are loader keys — resolve through getImage first.\n\t\t\t// Anything else flows straight to the setter, which validates\n\t\t\t// image-like shape and throws TypeError for invalid types\n\t\t\t// (boolean, number, function, etc.).\n\t\t\tif (typeof settings.normalMap === \"string\") {\n\t\t\t\tconst resolved = getImage(settings.normalMap);\n\t\t\t\tif (!resolved) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"me.Sprite: '\" +\n\t\t\t\t\t\t\tsettings.normalMap +\n\t\t\t\t\t\t\t\"' normal map image not found!\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.normalMap = resolved;\n\t\t\t} else {\n\t\t\t\tthis.normalMap = settings.normalMap;\n\t\t\t}\n\t\t}\n\n\t\t// store/reset the current atlas information if specified\n\t\tif (typeof settings.atlas !== \"undefined\") {\n\t\t\tthis.textureAtlas = settings.atlas;\n\t\t\tthis.atlasIndices = settings.atlasIndices;\n\t\t}\n\n\t\t// apply flip flags if specified\n\t\tif (typeof settings.flipX !== \"undefined\") {\n\t\t\tthis.flipX(!!settings.flipX);\n\t\t}\n\t\tif (typeof settings.flipY !== \"undefined\") {\n\t\t\tthis.flipY(!!settings.flipY);\n\t\t}\n\n\t\t// set the default rotation angle is defined in the settings\n\t\t// * WARNING: rotating sprites decreases performance with Canvas Renderer\n\t\tif (typeof settings.rotation !== \"undefined\") {\n\t\t\tthis.rotate(settings.rotation);\n\t\t}\n\n\t\t// update anchorPoint (resolved once at the top of the constructor)\n\t\tif (resolvedAnchor) {\n\t\t\tthis.anchorPoint.set(resolvedAnchor.x, resolvedAnchor.y);\n\t\t}\n\n\t\tif (typeof settings.tint !== \"undefined\") {\n\t\t\tif (settings.tint instanceof Color) {\n\t\t\t\tthis.tint.copy(settings.tint);\n\t\t\t} else {\n\t\t\t\t// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)\n\t\t\t\tthis.tint.parseCSS(settings.tint);\n\t\t\t}\n\t\t}\n\n\t\t// set the sprite name if specified\n\t\tif (typeof settings.name === \"string\") {\n\t\t\tthis.name = settings.name;\n\t\t}\n\n\t\t// displaying order\n\t\tif (typeof settings.z !== \"undefined\") {\n\t\t\tthis.pos.z = settings.z;\n\t\t}\n\n\t\t// add predefined animations if defined (e.g. aseprite)\n\t\tif (typeof settings.anims !== \"undefined\") {\n\t\t\tfor (const anim in settings.anims) {\n\t\t\t\tthis.addAnimation(\n\t\t\t\t\tsettings.anims[anim].name,\n\t\t\t\t\tsettings.anims[anim].index,\n\t\t\t\t\tsettings.anims[anim].speed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// addAnimation will return 0 if no texture atlas is defined\n\t\tif (!this.isVideo && this.addAnimation(\"default\", null) !== 0) {\n\t\t\t// set as default\n\t\t\tthis.setCurrentAnimation(\"default\");\n\t\t}\n\t}\n\n\t/**\n\t * defined animations, keyed by id (see {@link Sprite#addAnimation}).\n\t * @type {object}\n\t */\n\tget anim() {\n\t\treturn this._frameAnim.anim;\n\t}\n\n\t/**\n\t * current frame information (name / index / texture offset & size / trim).\n\t * @type {object}\n\t */\n\tget current() {\n\t\treturn this._frameAnim.current;\n\t}\n\n\t/**\n\t * elapsed time within the current animation frame, in milliseconds.\n\t * @type {number}\n\t */\n\tget dt() {\n\t\treturn this._frameAnim.dt;\n\t}\n\tset dt(value) {\n\t\tthis._frameAnim.dt = value;\n\t}\n\n\t/**\n\t * animation cycling speed (delay between frames in ms).\n\t * @type {number}\n\t * @default 100\n\t */\n\tget animationspeed() {\n\t\treturn this._frameAnim.animationspeed;\n\t}\n\tset animationspeed(value) {\n\t\tthis._frameAnim.animationspeed = value;\n\t}\n\n\t/**\n\t * pause the frame animation, freezing the current frame.\n\t * @type {boolean}\n\t * @default false\n\t */\n\tget animationpause() {\n\t\treturn this._frameAnim.animationpause;\n\t}\n\tset animationpause(value) {\n\t\tthis._frameAnim.animationpause = value;\n\t}\n\n\t/**\n\t * The optional normal-map image paired with this sprite's color\n\t * texture (SpriteIlluminator workflow). When set, the WebGL\n\t * renderer's lit pipeline samples this texture for per-pixel\n\t * lighting using `Stage._activeLights`. `null` when unlit.\n\t * Setting any non-image value (or anything without numeric\n\t * `width`/`height`) throws — assign `null` to clear.\n\t *\n\t * Silently ignored by the Canvas renderer.\n\t * @type {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|null}\n\t */\n\tget normalMap() {\n\t\treturn this._normalMap;\n\t}\n\tset normalMap(value) {\n\t\tif (value === null || value === undefined) {\n\t\t\tthis._normalMap = null;\n\t\t\treturn;\n\t\t}\n\t\t// a Texture2d asset (e.g. a procedural NoiseTexture2d) resolves to its\n\t\t// baked canvas, so it can be assigned directly like the `image` slot.\n\t\tif (value instanceof Texture2d) {\n\t\t\tvalue = value.getTexture();\n\t\t}\n\t\tif (\n\t\t\ttypeof value !== \"object\" ||\n\t\t\ttypeof value.width !== \"number\" ||\n\t\t\ttypeof value.height !== \"number\"\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"Sprite.normalMap must be null or an image-like object with numeric width/height \" +\n\t\t\t\t\t\"(HTMLImageElement, HTMLCanvasElement, OffscreenCanvas, ImageBitmap)\",\n\t\t\t);\n\t\t}\n\t\t// Explicitly reject HTMLVideoElement — it duck-types past the\n\t\t// width/height check, but the lit pipeline caches the GL texture\n\t\t// per image reference and never re-uploads. A video as a normal\n\t\t// map would silently freeze on frame 0; better a loud TypeError\n\t\t// at assignment time than a confusing visual bug at runtime.\n\t\tif (typeof value.videoWidth === \"number\") {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"Sprite.normalMap does not support HTMLVideoElement (the lit pipeline caches the texture per image reference and would freeze on frame 0)\",\n\t\t\t);\n\t\t}\n\t\tthis._normalMap = value;\n\t}\n\n\t/**\n\t * return the flickering state of the object\n\t * @returns {boolean}\n\t */\n\tisFlickering() {\n\t\treturn this._flicker.isFlickering;\n\t}\n\n\t/**\n\t * Play an animation, or resume the current animation / video. A shorthand:\n\t * call with an animation id to switch to (and start) it, or with no argument\n\t * to resume after {@link Sprite#pause}. Always clears the paused state. The\n\t * options mirror {@link Sprite#setCurrentAnimation} and the 3D\n\t * {@link GLTFModel#play}, so 2D and 3D animation share one API.\n\t * @param {string} [name] - animation id to play; omit to just resume\n\t * @param {string|Function|object} [options] - loop / chain / completion behavior (see {@link Sprite#setCurrentAnimation})\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @example\n\t * sprite.play(\"walk\");                  // switch to + play \"walk\"\n\t * sprite.play(\"die\", { loop: false });  // play once, hold the last frame\n\t * sprite.pause();\n\t * sprite.play();                        // resume\n\t */\n\tplay(name, options) {\n\t\tthis.animationpause = false;\n\t\t// `name` only applies to frame animations; a video sprite just resumes\n\t\tif (name !== undefined && !this.isVideo) {\n\t\t\tthis.setCurrentAnimation(name, options);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Pause the current animation or video, freezing the current frame. Resume\n\t * with {@link Sprite#play}.\n\t * @returns {Sprite} Reference to this object for method chaining\n\t */\n\tpause() {\n\t\tthis.animationpause = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Stop the current animation or video and reset it to the first frame\n\t * (paused). (Use {@link Sprite#pause} instead to freeze in place.)\n\t * @returns {Sprite} Reference to this object for method chaining\n\t */\n\tstop() {\n\t\tthis.animationpause = true;\n\t\tif (this.isVideo) {\n\t\t\t// clear the frame-anim timer/hold flags too (parity with the legacy\n\t\t\t// unconditional reset), then rewind the video\n\t\t\tthis._frameAnim.resetTimer();\n\t\t\tthis.image.pause();\n\t\t\tthis.image.currentTime = 0;\n\t\t} else {\n\t\t\t// rewind the frame animation to its first frame\n\t\t\tthis._frameAnim.rewind();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * make the object flicker\n\t * @param {number} duration - expressed in milliseconds\n\t * @param {Function} [callback] - Function to call when flickering ends\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @example\n\t * // make the object flicker for 1 second\n\t * // and then remove it\n\t * this.flicker(1000, function () {\n\t *     world.removeChild(this);\n\t * });\n\t */\n\tflicker(duration, callback = undefined) {\n\t\tthis._flicker.duration = duration;\n\t\tif (this._flicker.duration <= 0) {\n\t\t\tthis._flicker.isFlickering = false;\n\t\t\tthis._flicker.callback = undefined;\n\t\t\tthis._flicker.elapsed = 0;\n\t\t} else {\n\t\t\tthis._flicker.callback = callback;\n\t\t\tthis._flicker.elapsed = 0;\n\t\t\tthis._flicker.isFlickering = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * add an animation <br>\n\t * For fixed-sized cell sprite sheet, the index list must follow the\n\t * logic as per the following example :<br>\n\t * <img src=\"images/spritesheet_grid.png\"/>\n\t * @param {string} name - animation id\n\t * @param {number[]|string[]|object[]} index - list of sprite index or name defining the animation. Can also use objects to specify delay for each frame, see below\n\t * @param {number} [animationspeed] - cycling speed for animation in ms\n\t * @returns {number} frame amount of frame added to the animation (delay between each frame).\n\t * @see Sprite#animationspeed\n\t * @example\n\t * // walking animation\n\t * this.addAnimation(\"walk\", [ 0, 1, 2, 3, 4, 5 ]);\n\t * // standing animation\n\t * this.addAnimation(\"stand\", [ 11, 12 ]);\n\t * // eating animation\n\t * this.addAnimation(\"eat\", [ 6, 6 ]);\n\t * // rolling animation\n\t * this.addAnimation(\"roll\", [ 7, 8, 9, 10 ]);\n\t * // slower animation\n\t * this.addAnimation(\"roll\", [ 7, 8, 9, 10 ], 200);\n\t * // or get more specific with delay for each frame. Good solution instead of repeating:\n\t * this.addAnimation(\"turn\", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }])\n\t * // can do this with atlas values as well:\n\t * this.addAnimation(\"turn\", [{ name: \"turnone\", delay: 200 }, { name: \"turntwo\", delay: 100 }])\n\t * // define a dying animation that stop on the last frame\n\t * this.addAnimation(\"die\", [{ name: 3, delay: 200 }, { name: 4, delay: 100 }, { name: 5, delay: Infinity }])\n\t * // set the standing animation as default\n\t * this.setCurrentAnimation(\"stand\");\n\t */\n\taddAnimation(name, index, animationspeed) {\n\t\treturn this._frameAnim.addAnimation(name, index, animationspeed);\n\t}\n\n\t/**\n\t * set the current animation\n\t * this will always change the animation & set the frame to zero\n\t * @param {string} name - animation id\n\t * @param {string|Function} [resetAnim] - animation id to switch to when complete, or callback\n\t * @param {boolean} [preserve_dt=false] - if false will reset the elapsed time counter since last frame\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @example\n\t * // set \"walk\" animation\n\t * this.setCurrentAnimation(\"walk\");\n\t *\n\t * // set \"walk\" animation if it is not the current animation\n\t * if (!this.isCurrentAnimation(\"walk\")) {\n\t *     this.setCurrentAnimation(\"walk\");\n\t * }\n\t *\n\t * // set \"eat\" animation, and switch to \"walk\" when complete\n\t * this.setCurrentAnimation(\"eat\", \"walk\");\n\t *\n\t * // set \"die\" animation, and remove the object when finished\n\t * this.setCurrentAnimation(\"die\", () => {\n\t *    world.removeChild(this);\n\t *    return false; // do not reset to first frame\n\t * });\n\t *\n\t * // set \"attack\" animation, and pause for a short duration\n\t * this.setCurrentAnimation(\"die\", () => {\n\t *    this.animationpause = true;\n\t *\n\t *    // back to \"standing\" animation after 1 second\n\t *    setTimeout(function () {\n\t *        this.setCurrentAnimation(\"standing\");\n\t *    }, 1000);\n\t *\n\t *    return false; // do not reset to first frame\n\t * });\n\t */\n\tsetCurrentAnimation(name, resetAnim, preserve_dt = false) {\n\t\tthis._frameAnim.setCurrentAnimation(name, resetAnim, preserve_dt);\n\t\treturn this;\n\t}\n\n\t/**\n\t * reverse the given or current animation if none is specified\n\t * @param {string} [name] - animation id\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @see Sprite#animationspeed\n\t */\n\treverseAnimation(name) {\n\t\tthis._frameAnim.reverseAnimation(name);\n\t\t// reversing doesn't re-apply a frame, so mark dirty here (the host owns\n\t\t// its dirty flag)\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * return true if the specified animation is the current one.\n\t * @param {string} name - animation id\n\t * @returns {boolean}\n\t * @example\n\t * if (!this.isCurrentAnimation(\"walk\")) {\n\t *     // do something funny...\n\t * }\n\t */\n\tisCurrentAnimation(name) {\n\t\treturn this._frameAnim.isCurrentAnimation(name);\n\t}\n\n\t/**\n\t * the names of every animation defined on this sprite (via\n\t * {@link Sprite#addAnimation}).\n\t * @returns {string[]} the defined animation names\n\t * @example\n\t * sprite.addAnimation(\"walk\", [0, 1, 2, 3]);\n\t * sprite.addAnimation(\"idle\", [4, 5]);\n\t * sprite.getAnimationNames(); // [\"walk\", \"idle\"]\n\t */\n\tgetAnimationNames() {\n\t\treturn this._frameAnim.getAnimationNames();\n\t}\n\n\t/**\n\t * change the current texture atlas region for this sprite\n\t * @see Texture.getRegion\n\t * @param {object} region - typically returned through me.Texture.getRegion()\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @example\n\t * // change the sprite to \"shadedDark13.png\";\n\t * mySprite.setRegion(mytexture.getRegion(\"shadedDark13.png\"));\n\t */\n\tsetRegion(region) {\n\t\tthis._frameAnim.setRegion(region);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply the current frame's texture region to this sprite's geometry: swap\n\t * the source sub-texture, then resolve size / anchor (honoring trimming).\n\t * Invoked by the shared {@link FrameAnimation} engine via `setRegion`.\n\t * @param {object} region - the texture region object\n\t * @ignore\n\t */\n\t_applyFrame(region) {\n\t\t// set the source texture for the given region\n\t\tthis.image = this.source.getTexture(region);\n\n\t\tif (region.trimmed && region.sourceSize) {\n\t\t\t// use the original untrimmed size for stable bounds across trimmed frames\n\t\t\tthis.width = region.sourceSize.w;\n\t\t\tthis.height = region.sourceSize.h;\n\t\t\t// recover the original pivot relative to sourceSize for stable anchor\n\t\t\tif (region.anchorPoint) {\n\t\t\t\tconst pivotX =\n\t\t\t\t\t(region.trim.x + region.width * region.anchorPoint.x) / this.width;\n\t\t\t\tconst pivotY =\n\t\t\t\t\t(region.trim.y + region.height * region.anchorPoint.y) / this.height;\n\t\t\t\tthis.anchorPoint.setMuted(\n\t\t\t\t\tthis._flip.x ? 1 - pivotX : pivotX,\n\t\t\t\t\tthis._flip.y ? 1 - pivotY : pivotY,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.width = region.width;\n\t\t\tthis.height = region.height;\n\t\t\tif (region.anchorPoint) {\n\t\t\t\tthis.anchorPoint.setMuted(\n\t\t\t\t\tthis._flip.x && region.trimmed === true\n\t\t\t\t\t\t? 1 - region.anchorPoint.x\n\t\t\t\t\t\t: region.anchorPoint.x,\n\t\t\t\t\tthis._flip.y && region.trimmed === true\n\t\t\t\t\t\t? 1 - region.anchorPoint.y\n\t\t\t\t\t\t: region.anchorPoint.y,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// update the sprite bounding box\n\t\tthis.updateBounds();\n\t\t// the frame changed → this sprite needs a redraw (the host owns its\n\t\t// dirty flag; the FrameAnimation engine never sets it)\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * force the current animation frame index.\n\t * @param {number} [index=0] - animation frame index\n\t * @returns {Sprite} Reference to this object for method chaining\n\t * @example\n\t * // reset the current animation to the first frame\n\t * this.setAnimationFrame();\n\t */\n\tsetAnimationFrame(index = 0) {\n\t\tthis._frameAnim.setAnimationFrame(index);\n\t\treturn this;\n\t}\n\n\t/**\n\t * return the current animation frame index.\n\t * @returns {number} current animation frame index\n\t */\n\tgetCurrentAnimationFrame() {\n\t\treturn this._frameAnim.getCurrentAnimationFrame();\n\t}\n\n\t/**\n\t * Returns the frame object by the index.\n\t * @ignore\n\t * @param {number} id - the frame id\n\t * @returns {number} if using number indices. Returns {object} containing frame data if using texture atlas\n\t */\n\tgetAnimationFrameObjectByIndex(id) {\n\t\treturn this._frameAnim.getAnimationFrameObjectByIndex(id);\n\t}\n\n\t/**\n\t * update function. <br>\n\t * automatically called by the game manager {@link game}\n\t * @protected\n\t * @param {number} dt - time since the last update in milliseconds.\n\t * @returns {boolean} true if the Sprite is dirty\n\t */\n\tupdate(dt) {\n\t\t// play/pause video if necessary\n\t\tif (this.isVideo) {\n\t\t\tif (this.animationpause) {\n\t\t\t\tthis.image.pause();\n\t\t\t} else if (this.image.paused) {\n\t\t\t\tthis.image.play();\n\t\t\t}\n\t\t\t// set-only (never assign false — that would stomp a dirty flag\n\t\t\t// set earlier in the tick, e.g. by a tween on a paused sprite)\n\t\t\tif (this.image.version === undefined) {\n\t\t\t\t// no rVFC tracking (old browsers): repaint while playing\n\t\t\t\tif (!this.image.paused) {\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t}\n\t\t\t} else if (this.image.version !== this._lastVideoFrameVersion) {\n\t\t\t\t// a new frame was presented since we last looked; deliberately\n\t\t\t\t// not gated on `paused` so a seek while paused (stop() rewind)\n\t\t\t\t// still repaints with the presented frame\n\t\t\t\tthis._lastVideoFrameVersion = this.image.version;\n\t\t\t\tthis.isDirty = true;\n\t\t\t}\n\t\t} else {\n\t\t\t// advance the shared frame-animation engine; a frame change marks this\n\t\t\t// sprite dirty via `_applyFrame` (the engine owns no dirty flag)\n\t\t\tthis._frameAnim.update(dt);\n\t\t}\n\n\t\t//update the \"flickering\" state if necessary\n\t\tif (this._flicker.isFlickering) {\n\t\t\tthis._flicker.elapsed += dt;\n\t\t\tif (this._flicker.elapsed >= this._flicker.duration) {\n\t\t\t\tif (typeof this._flicker.callback === \"function\") {\n\t\t\t\t\tthis._flicker.callback();\n\t\t\t\t}\n\t\t\t\tthis.flicker(-1);\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t}\n\n\t\t// `isDirty` is the single source of truth — sub-updates above set it,\n\t\t// `Renderable.update` returns it\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * Prepare the rendering context before drawing this sprite (automatically called by melonJS).\n\t * Extends `Renderable.preDraw` to publish this sprite's `normalMap` (if any)\n\t * on the renderer so the WebGL lit pipeline can pair it with the next\n\t * `drawImage` call. Cleared back in `postDraw`.\n\t * @param {Renderer} renderer - a renderer instance\n\t */\n\tpreDraw(renderer) {\n\t\tsuper.preDraw(renderer);\n\t\t// Route through the renderer's normal-map state slot — kept off\n\t\t// the public `drawImage` signature so material features can be\n\t\t// added without disturbing the API. The WebGL batcher reads\n\t\t// `currentNormalMap` from the renderer; Canvas ignores it.\n\t\tif (this._normalMap !== null) {\n\t\t\trenderer.currentNormalMap = this._normalMap;\n\t\t}\n\t}\n\n\t/**\n\t * restore the rendering context after drawing this sprite (automatically called by melonJS).\n\t * @param {Renderer} renderer - a renderer instance\n\t */\n\tpostDraw(renderer) {\n\t\t// Clear the slot so a subsequent un-lit sprite isn't accidentally lit.\n\t\tif (this._normalMap !== null) {\n\t\t\trenderer.currentNormalMap = null;\n\t\t}\n\t\tsuper.postDraw(renderer);\n\t}\n\n\t/**\n\t * draw this sprite (automatically called by melonJS)\n\t * @param {Renderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\tdraw(renderer) {\n\t\t// do nothing if we are flickering (time-based, frame-rate independent)\n\t\tif (\n\t\t\tthis._flicker.isFlickering &&\n\t\t\tMath.floor(this._flicker.elapsed / FLICKER_INTERVAL_MS) % 2 !== 0\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// the frame to draw\n\t\tconst frame = this.current;\n\n\t\t// cache the current position and size\n\t\tlet xpos = this.pos.x;\n\t\tlet ypos = this.pos.y;\n\n\t\tlet w = frame.width;\n\t\tlet h = frame.height;\n\n\t\t// frame offset in the texture/atlas\n\t\tconst frame_offset = frame.offset;\n\t\tconst g_offset = this.offset;\n\n\t\t// remove image's TexturePacker/ShoeBox rotation\n\t\tif (frame.angle !== 0) {\n\t\t\trenderer.translate(-xpos, -ypos);\n\t\t\trenderer.rotate(frame.angle);\n\t\t\txpos -= h;\n\t\t\tw = frame.height;\n\t\t\th = frame.width;\n\t\t\t// apply trim in rotated space: (tx, ty) → (-ty, tx) for -π/2\n\t\t\tif (frame.trim) {\n\t\t\t\txpos -= frame.trim.y;\n\t\t\t\typos += frame.trim.x;\n\t\t\t}\n\t\t} else if (frame.trim) {\n\t\t\t// apply trim offset for non-rotated trimmed sprites\n\t\t\txpos += frame.trim.x;\n\t\t\typos += frame.trim.y;\n\t\t}\n\n\t\trenderer.drawImage(\n\t\t\tthis.image,\n\t\t\tg_offset.x + frame_offset.x, // sx\n\t\t\tg_offset.y + frame_offset.y, // sy\n\t\t\tw,\n\t\t\th, // sw,sh\n\t\t\txpos,\n\t\t\typos, // dx,dy\n\t\t\tw,\n\t\t\th, // dw,dh\n\t\t);\n\t}\n\n\t/**\n\t * Destroy function<br>\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// release the engine's pooled `current.offset`\n\t\tthis._frameAnim.destroy();\n\t\tvector2dPool.release(this.offset);\n\t\tthis.offset = undefined;\n\t\tif (this.isVideo) {\n\t\t\tthis.removeStatePauseListener();\n\t\t\tif (typeof this._videoFrameHandle !== \"undefined\") {\n\t\t\t\tthis.image.cancelVideoFrameCallback(this._videoFrameHandle);\n\t\t\t\t// drop the frame-counter stamp so a later non-Sprite consumer\n\t\t\t\t// (manual renderer.drawImage) gets the legacy per-frame upload\n\t\t\t\t// path back; a still-alive sprite sharing this element simply\n\t\t\t\t// re-stamps on its next presented frame\n\t\t\t\tdelete this.image.version;\n\t\t\t}\n\t\t\tthis.image.onended = undefined;\n\t\t\tthis.image.pause();\n\t\t\tthis.image.currentTime = 0;\n\t\t}\n\t\tthis.image = undefined;\n\t\t// drop the normal-map reference too — same precedent as `image`,\n\t\t// avoids holding a (possibly large) paired image alive after the\n\t\t// sprite is gone\n\t\tthis._normalMap = null;\n\t\tsuper.destroy();\n\t}\n}\n","import type Application from \"./../application/application.ts\";\nimport Camera2d from \"./../camera/camera2d.ts\";\nimport type Light2d from \"./../lighting/light2d.ts\";\nimport type { Light3d } from \"./../lighting/light3d.ts\";\nimport { Color } from \"./../math/color.ts\";\nimport type World from \"./../physics/world.js\";\nimport { emit, STAGE_RESET } from \"../system/event.ts\";\nimport type Renderer from \"./../video/renderer.js\";\n\ninterface StageSettings {\n\tcameras: Camera2d[];\n\t/**\n\t * Default camera class to instantiate when this stage has no\n\t * explicit `cameras` list. Overrides any app-level `cameraClass`\n\t * setting for this specific stage. Built-in stages (e.g.\n\t * {@link DefaultLoadingScreen}) pin this to {@link Camera2d} so\n\t * the loader stays 2D regardless of app-wide `cameraClass`.\n\t */\n\tcameraClass?: new (\n\t\tminX: number,\n\t\tminY: number,\n\t\tmaxX: number,\n\t\tmaxY: number,\n\t) => Camera2d;\n\tonResetEvent?: (app: Application, ...args: unknown[]) => void;\n\tonDestroyEvent?: (app: Application) => void;\n}\n\n// a default camera instance to use across all stages\nlet default_camera: Camera2d | undefined;\n\n// default stage settings\nconst default_settings: StageSettings = {\n\tcameras: [],\n};\n\n/**\n * a default \"Stage\" object.\n * every \"stage\" object (title screen, credits, ingame, etc...) to be managed\n * through the state manager must inherit from this base class.\n * @category Application\n * @see state\n */\nexport default class Stage {\n\t/**\n\t * The list of active cameras in this stage.\n\t * Cameras will be rendered based on this order defined in this list.\n\t * Only the \"default\" camera will be resized when the window or canvas is resized.\n\t */\n\tcameras: Map<string, Camera2d>;\n\n\t/**\n\t * The list of active lights in this stage.\n\t *\n\t * Since 19.3.0, `Light2d` is a first-class world Renderable — the\n\t * recommended pattern is to add lights directly to `app.world` (or any\n\t * container, including a sprite, so the light follows it via parent\n\t * transforms). The `lights` Map remains for backward compatibility:\n\t * any entry added via `this.lights.set(name, light)` in\n\t * `onResetEvent()` is automatically adopted into the world tree at\n\t * stage reset time so it renders normally.\n\t * @see Light2d\n\t * @see Stage.ambientLight\n\t * @example\n\t * // recommended:\n\t * const whiteLight = new Light2d(100, 100, 140, 140, \"#fff\", 0.7);\n\t * app.world.addChild(whiteLight);\n\t *\n\t * // legacy (still works, auto-adopted into world):\n\t * this.lights.set(\"whiteLight\", whiteLight);\n\t *\n\t * this.ambientLight.parseCSS(\"#1117\");\n\t */\n\tlights: Map<string, Light2d>;\n\n\t/**\n\t * Internal set of active lights, auto-populated by `Light2d`'s\n\t * `onActivateEvent` / `onDeactivateEvent` hooks. Used by Camera2d's\n\t * ambient-overlay pass to compute the cutouts.\n\t * @ignore\n\t */\n\t_activeLights: Set<Light2d>;\n\n\t/**\n\t * Internal set of active 3D lights, auto-populated by `Light3d`'s\n\t * `onActivateEvent` / `onDeactivateEvent` hooks. Read by the lit mesh\n\t * batcher each frame to shade `lit` meshes under a `Camera3d`.\n\t * @ignore\n\t */\n\t_activeLights3d: Set<Light3d>;\n\n\t/**\n\t * an ambient light that will be added to the stage rendering\n\t * @default \"#000000\"\n\t * @see Light2d\n\t */\n\tambientLight: Color;\n\n\t/**\n\t * Base light level applied to every normal-mapped sprite in the\n\t * lit rendering path. Unlike {@link Stage#ambientLight} (which is\n\t * the dark overlay punched by each light's cutout), this color is\n\t * added to every lit pixel so unlit areas don't render pure\n\t * black. Defaults to black (0, 0, 0) — sprites without a\n\t * `normalMap` ignore it entirely.\n\t * @default \"#000000\"\n\t */\n\tambientLightingColor: Color;\n\n\t/**\n\t * The given constructor options\n\t */\n\tsettings: StageSettings;\n\n\t/**\n\t * @param settings - The stage parameters\n\t * @param [settings.cameras=[]] - a list of cameras (experimental)\n\t * @param [settings.onResetEvent] - called by the state manager when reseting the object\n\t * @param [settings.onDestroyEvent] - called by the state manager before switching to another state\n\t */\n\tconstructor(settings?: Partial<StageSettings>) {\n\t\tthis.cameras = new Map();\n\t\tthis.lights = new Map();\n\t\tthis._activeLights = new Set();\n\t\tthis._activeLights3d = new Set();\n\t\tthis.ambientLight = new Color(0, 0, 0, 0);\n\t\tthis.ambientLightingColor = new Color(0, 0, 0, 1);\n\t\tthis.settings = Object.assign({}, default_settings, settings || {});\n\t}\n\n\t/**\n\t * Called by `Light2d.onActivateEvent` to register the light with the\n\t * stage's ambient-overlay cutout list. Users normally don't call this.\n\t * @ignore\n\t */\n\t_registerLight(light: Light2d): void {\n\t\tthis._activeLights.add(light);\n\t}\n\n\t/**\n\t * Called by `Light2d.onDeactivateEvent` to deregister the light.\n\t * @ignore\n\t */\n\t_unregisterLight(light: Light2d): void {\n\t\tthis._activeLights.delete(light);\n\t}\n\n\t/**\n\t * Called by `Light3d.onActivateEvent` to register a 3D light with the stage.\n\t * Read by the lit mesh batcher. Users normally don't call this.\n\t * @ignore\n\t */\n\t_registerLight3d(light: Light3d): void {\n\t\tthis._activeLights3d.add(light);\n\t}\n\n\t/**\n\t * Called by `Light3d.onDeactivateEvent` to deregister a 3D light.\n\t * @ignore\n\t */\n\t_unregisterLight3d(light: Light3d): void {\n\t\tthis._activeLights3d.delete(light);\n\t}\n\n\t/**\n\t * Object reset function\n\t * @ignore\n\t */\n\treset(app: Application, ...extraArgs: unknown[]): void {\n\t\t// Snapshot whether this is the first reset of this stage instance\n\t\t// BEFORE we touch the cameras map. Used below to gate the\n\t\t// `defaultSortOn` re-apply — once the cameras map is populated,\n\t\t// subsequent resets must NOT trample the user's `world.sortOn`\n\t\t// override.\n\t\tconst isFirstReset = !this.cameras.has(\"default\");\n\n\t\t// add all defined cameras\n\t\tthis.settings.cameras.forEach((camera) => {\n\t\t\tthis.cameras.set(camera.name, camera);\n\t\t});\n\n\t\t// default-camera resolution order (most-specific wins):\n\t\t//   1. explicit `cameras` array on the stage → handled above\n\t\t//   2. `cameraClass` on the stage settings → fresh instance,\n\t\t//      overrides app-level (used by DefaultLoadingScreen to\n\t\t//      pin Camera2d regardless of app.settings.cameraClass)\n\t\t//   3. `cameraClass` on the application settings → fresh\n\t\t//      instance per stage (Camera3d state shouldn't bleed\n\t\t//      across stages)\n\t\t//   4. neither set → fall back to the Camera2d module-level\n\t\t//      singleton (preserves pre-19.7 behavior bit-for-bit\n\t\t//      for every app that doesn't opt into cameraClass)\n\t\tconst StageCameraClass = this.settings.cameraClass;\n\t\tconst AppCameraClass = app?.settings.cameraClass;\n\t\tif (!this.cameras.has(\"default\") && app) {\n\t\t\tconst width = app.renderer.width;\n\t\t\tconst height = app.renderer.height;\n\n\t\t\tif (typeof StageCameraClass === \"function\") {\n\t\t\t\tthis.cameras.set(\"default\", new StageCameraClass(0, 0, width, height));\n\t\t\t} else if (typeof AppCameraClass === \"function\") {\n\t\t\t\tthis.cameras.set(\"default\", new AppCameraClass(0, 0, width, height));\n\t\t\t} else {\n\t\t\t\t// no cameraClass anywhere — use the shared Camera2d singleton\n\t\t\t\tif (typeof default_camera === \"undefined\") {\n\t\t\t\t\tdefault_camera = new Camera2d(0, 0, width, height);\n\t\t\t\t}\n\t\t\t\tthis.cameras.set(\"default\", default_camera);\n\t\t\t}\n\t\t}\n\n\t\t// Re-apply the chosen camera's `defaultSortOn` to the world on\n\t\t// the FIRST reset of this stage — NOT on re-resets (`isFirstReset`\n\t\t// gate above), and NOT when the camera came from the\n\t\t// shared-Camera2d-singleton fallback (preserves pre-19.7\n\t\t// behavior for apps that don't opt into `cameraClass`).\n\t\t//\n\t\t// Sources for the chosen class, in priority order:\n\t\t//   1. `Stage({ cameraClass: ... })` — the loader uses this to\n\t\t//      pin Camera2d on a Camera3d app\n\t\t//   2. `Application({ cameraClass: ... })`\n\t\t//   3. `Stage({ cameras: [new MyCam(...)] })` — the documented\n\t\t//      explicit-camera pattern. Pulls the class off the\n\t\t//      already-instantiated camera's `constructor`.\n\t\t//\n\t\t// Without (3), a stage using the explicit pattern silently\n\t\t// inherited the loader's Camera2d → \"z\" sortOn and a Camera3d\n\t\t// sub-stage would never snap back to \"depth\".\n\t\tif (isFirstReset && app?.world) {\n\t\t\t// Every camera class in the engine carries\n\t\t\t// `static defaultSortOn: \"x\" | \"y\" | \"z\" | \"depth\"` (see\n\t\t\t// `Camera2d.defaultSortOn = \"z\"`, `Camera3d.defaultSortOn =\n\t\t\t// \"depth\"`), but the constructor-shape type used by\n\t\t\t// `settings.cameraClass` doesn't expose statics. Read the\n\t\t\t// static via a structural narrowing instead of a\n\t\t\t// constructor-to-static cast.\n\t\t\ttype SortAwareCameraClass = {\n\t\t\t\tdefaultSortOn?: \"x\" | \"y\" | \"z\" | \"depth\";\n\t\t\t};\n\t\t\tlet chosenClass: SortAwareCameraClass | undefined;\n\t\t\tif (typeof StageCameraClass === \"function\") {\n\t\t\t\tchosenClass = StageCameraClass as SortAwareCameraClass;\n\t\t\t} else if (typeof AppCameraClass === \"function\") {\n\t\t\t\tchosenClass = AppCameraClass as SortAwareCameraClass;\n\t\t\t} else if (this.settings.cameras.length > 0) {\n\t\t\t\t// Read the camera actually registered under the \"default\"\n\t\t\t\t// key, NOT `settings.cameras[0]`. A split-screen / minimap\n\t\t\t\t// stage can list a non-default Camera2d before its main\n\t\t\t\t// Camera3d in the cameras array, and `[0].constructor`\n\t\t\t\t// would pick the wrong class — leaving `world.sortOn` at\n\t\t\t\t// \"z\" while the main perspective view needed \"depth\".\n\t\t\t\t//\n\t\t\t\t// Gated on `settings.cameras.length > 0` so the\n\t\t\t\t// shared-Camera2d-singleton fallback (no cameraClass +\n\t\t\t\t// no explicit cameras anywhere) leaves `world.sortOn`\n\t\t\t\t// untouched, preserving pre-19.7 behavior.\n\t\t\t\tconst defaultCam = this.cameras.get(\"default\");\n\t\t\t\tif (defaultCam) {\n\t\t\t\t\tchosenClass =\n\t\t\t\t\t\tdefaultCam.constructor as unknown as SortAwareCameraClass;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst defaultSortOn = chosenClass?.defaultSortOn;\n\t\t\tif (defaultSortOn && app.world.sortOn !== defaultSortOn) {\n\t\t\t\tapp.world.sortOn = defaultSortOn;\n\t\t\t}\n\t\t}\n\n\t\t// reset the game\n\t\temit(STAGE_RESET, this);\n\n\t\t// call the onReset Function with the app reference and any extra args\n\t\tthis.onResetEvent(app, ...extraArgs);\n\n\t\t// adopt any lights registered via the legacy `this.lights.set(...)` API\n\t\t// into the world tree so they render as standard Renderables. Their\n\t\t// onActivateEvent hook then auto-registers them with `_activeLights`.\n\t\tif (app && app.world) {\n\t\t\tthis.lights.forEach((light) => {\n\t\t\t\tif (!light.ancestor) {\n\t\t\t\t\tapp.world.addChild(light);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * update function\n\t * @ignore\n\t * @param dt - time since the last update in milliseconds.\n\t * @returns true if the stage needs to be redrawn\n\t */\n\tupdate(dt: number): boolean {\n\t\tlet isDirty = false;\n\n\t\t// update the camera/viewport\n\t\tthis.cameras.forEach((camera) => {\n\t\t\tif (camera.update(dt)) {\n\t\t\t\tisDirty = true;\n\t\t\t}\n\t\t});\n\n\t\t// lights are part of the world tree now and updated by Container.update;\n\t\t// no separate per-light update loop is needed here.\n\n\t\treturn isDirty;\n\t}\n\n\t/**\n\t * draw the current stage\n\t *\n\t * Lights are rendered as part of the world tree (they're now first-class\n\t * Renderables) and the ambient overlay pass runs inside each Camera's\n\t * post-effect FBO bracket via {@link Stage#drawLighting}.\n\t * @ignore\n\t * @param renderer - the renderer object to draw with\n\t * @param world - the world object to draw\n\t */\n\tdraw(renderer: Renderer, world: World): void {\n\t\tthis.cameras.forEach((camera) => {\n\t\t\tcamera.draw(renderer, world);\n\t\t});\n\t}\n\n\t/**\n\t * Draw the stage's ambient-light overlay with cutouts for each active\n\t * light. Called from each `Camera2d` inside its post-effect FBO bracket —\n\t * lights themselves render via the world tree (they're standard\n\t * Renderables); this pass only paints the dark fill that the lights cut\n\t * holes through.\n\t *\n\t * Subclasses can override this method to implement custom lighting (e.g.\n\t * per-pixel normal-mapped lighting via a custom shader). Called once per\n\t * camera per frame.\n\t * @param renderer - the active renderer\n\t * @param camera - the camera currently rendering this stage\n\t * @param translateX - the same world-to-screen X translate that\n\t *   `Camera2d.draw()` applies to the world container (i.e.\n\t *   `camera.pos.x + camera.offset.x` for the default camera, plus\n\t *   the container's own offset for non-default cameras)\n\t * @param translateY - the world-to-screen Y translate (see `translateX`)\n\t */\n\tdrawLighting(\n\t\trenderer: Renderer,\n\t\tcamera: Camera2d,\n\t\ttranslateX: number = camera.pos.x + camera.offset.x,\n\t\ttranslateY: number = camera.pos.y + camera.offset.y,\n\t): void {\n\t\tif (this.ambientLight.alpha === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// cast for renderer-specific methods (setMask, setColor, fillRect, translate)\n\t\tconst r = renderer as any;\n\t\tr.save();\n\t\t// `light.getVisibleArea()` returns world-space coords (via\n\t\t// `getBounds()` → `getAbsolutePosition()` walking ancestors), but by\n\t\t// the time `drawLighting` runs the world container's\n\t\t// `translate(-translateX, -translateY)` has already been popped —\n\t\t// the renderer is back in camera-local/FBO space. Re-apply the\n\t\t// same world-to-screen translate here so the cutouts align with\n\t\t// the gradient regardless of camera scroll, container parenting,\n\t\t// or non-default cameras (minimap / splitscreen).\n\t\tconst tx = translateX;\n\t\tconst ty = translateY;\n\t\tif (tx !== 0 || ty !== 0) {\n\t\t\tr.translate(-tx, -ty);\n\t\t}\n\t\t// punch out each active light's visible area so the ambient fill\n\t\t// doesn't darken what the light is illuminating\n\t\tthis._activeLights.forEach((light) => {\n\t\t\tr.setMask(light.getVisibleArea(), true);\n\t\t});\n\t\tr.setColor(this.ambientLight);\n\t\t// fillRect must cover the camera's view in the (now world-space)\n\t\t// renderer — offset by the camera origin so it matches the viewport.\n\t\tr.fillRect(tx, ty, camera.width, camera.height);\n\t\tr.clearMask();\n\t\tr.restore();\n\t}\n\n\t/**\n\t * destroy function\n\t * @ignore\n\t */\n\tdestroy(app: Application): void {\n\t\t// destroy the cameras this stage constructed (the fresh per-stage\n\t\t// `cameraClass` instances) — without this, every state switch leaks\n\t\t// a zombie camera still subscribed to GAME_RESET/CANVAS_ONRESIZE.\n\t\t// NOT stage-owned (so not destroyed here): user instances from\n\t\t// `settings.cameras` (re-added from settings on the next reset) and\n\t\t// the shared module-level Camera2d singleton (reused across stages)\n\t\tthis.cameras.forEach((camera) => {\n\t\t\tif (\n\t\t\t\tcamera !== default_camera &&\n\t\t\t\t!this.settings.cameras.includes(camera)\n\t\t\t) {\n\t\t\t\tcamera.destroy();\n\t\t\t}\n\t\t});\n\t\t// clear all cameras\n\t\tthis.cameras.clear();\n\t\t// clear all lights — Light2d.onDeactivateEvent will deregister each\n\t\t// from `_activeLights` as the world container removes them. Lights\n\t\t// only stored in the legacy `lights` map (never adopted into the\n\t\t// world) get destroyed directly.\n\t\tthis.lights.forEach((light) => {\n\t\t\tif (!light.ancestor) {\n\t\t\t\tlight.destroy();\n\t\t\t}\n\t\t});\n\t\tthis.lights.clear();\n\t\tthis._activeLights.clear();\n\t\tthis._activeLights3d.clear();\n\t\t// notify the object\n\t\tthis.onDestroyEvent(app);\n\t}\n\n\t/**\n\t * onResetEvent function<br>\n\t * called by the state manager when resetting the object\n\t * this is typically where you will load a level, add renderables, etc...\n\t * @param app - the current application instance\n\t * @param args - optional arguments passed when switching state\n\t * @see state#change\n\t */\n\tonResetEvent(app: Application, ...args: unknown[]): void {\n\t\t// execute onResetEvent function if given through the constructor\n\t\tif (typeof this.settings.onResetEvent === \"function\") {\n\t\t\tthis.settings.onResetEvent(app, ...args);\n\t\t}\n\t}\n\n\t/**\n\t * onDestroyEvent function<br>\n\t * called by the state manager before switching to another state\n\t * @param app - the current application instance\n\t */\n\tonDestroyEvent(app: Application): void {\n\t\t// execute onDestroyEvent function if given through the constructor\n\t\tif (typeof this.settings.onDestroyEvent === \"function\") {\n\t\t\tthis.settings.onDestroyEvent(app);\n\t\t}\n\t}\n}\n","import Camera2d from \"./../camera/camera2d.ts\";\nimport Renderable from \"./../renderable/renderable.js\";\nimport Sprite from \"./../renderable/sprite.js\";\nimport Stage from \"./../state/stage.ts\";\nimport {\n\tLOADER_COMPLETE,\n\tLOADER_PROGRESS,\n\toff,\n\ton,\n\tonce,\n\tVIEWPORT_ONRESIZE,\n} from \"../system/event.ts\";\nimport { load, unload } from \"./loader.js\";\nimport logo_url from \"./melonjs_logo.png\";\n\n// a basic progress bar object\nclass ProgressBar extends Renderable {\n\t/**\n\t * @ignore\n\t */\n\tconstructor(x, y, w, h) {\n\t\tsuper(x, y, w, h);\n\n\t\tthis.barHeight = h;\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\ton(LOADER_PROGRESS, this.onProgressUpdate, this);\n\t\ton(VIEWPORT_ONRESIZE, this.resize, this);\n\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t// store current progress\n\t\tthis.progress = 0;\n\t}\n\n\t/**\n\t * make sure the screen is refreshed every frame\n\t * @ignore\n\t */\n\tonProgressUpdate(progress) {\n\t\tthis.progress = ~~(progress * this.width);\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * draw function\n\t * @ignore\n\t */\n\tdraw(renderer, viewport) {\n\t\t// draw the progress bar\n\t\trenderer.setColor(\"black\");\n\t\trenderer.fillRect(\n\t\t\tthis.pos.x,\n\t\t\tviewport.centerY,\n\t\t\trenderer.width,\n\t\t\tthis.barHeight / 2,\n\t\t);\n\n\t\trenderer.setColor(\"#55aa00\");\n\t\trenderer.fillRect(\n\t\t\tthis.pos.x,\n\t\t\tviewport.centerY,\n\t\t\tthis.progress,\n\t\t\tthis.barHeight / 2,\n\t\t);\n\t}\n\n\t/**\n\t * Called by engine before deleting the object\n\t * @ignore\n\t */\n\tonDestroyEvent() {\n\t\toff(LOADER_PROGRESS, this.onProgressUpdate, this);\n\t\toff(VIEWPORT_ONRESIZE, this.resize, this);\n\t}\n}\n\n/**\n * a default loading screen\n * @ignore\n */\nclass DefaultLoadingScreen extends Stage {\n\t/**\n\t * @ignore\n\t */\n\tprogressBar = null;\n\n\t/**\n\t * @ignore\n\t */\n\tlogoSprite = null;\n\n\t/**\n\t * reference to the application instance\n\t * @ignore\n\t */\n\t#app = null;\n\n\t/**\n\t * whether the cleanup has already run\n\t * @ignore\n\t */\n\t#cleanedUp = false;\n\n\t/**\n\t * Pin the loading screen to a Camera2d regardless of the\n\t * application's `cameraClass` setting. The loader must render\n\t * correctly even when the host app opts in to Camera3d globally\n\t * — a perspective camera applied to a 2D progress bar would\n\t * stretch / clip the bar based on its depth.\n\t */\n\tconstructor() {\n\t\tsuper({ cameraClass: Camera2d });\n\t}\n\n\t/**\n\t * call when the loader is resetted\n\t * @ignore\n\t */\n\tonResetEvent(app) {\n\t\tconst barHeight = 8;\n\n\t\tthis.#app = app;\n\t\tthis.#cleanedUp = false;\n\n\t\t// set a background color\n\t\tapp.world.backgroundColor.parseCSS(\"#202020\");\n\n\t\tconst { width, height } = app.renderer;\n\n\t\t// progress bar\n\t\tthis.progressBar = new ProgressBar(0, height / 2, width, barHeight);\n\t\tapp.world.addChild(this.progressBar, 1);\n\n\t\t// clean up loading screen children when the preloader completes,\n\t\t// whether or not a state.change() follows\n\t\tonce(LOADER_COMPLETE, this.#cleanup, this);\n\n\t\t// load the melonJS logo\n\t\tload({ name: \"melonjs_logo\", type: \"image\", src: logo_url }, () => {\n\t\t\t// guard against the logo loading after preload completed\n\t\t\tif (this.#cleanedUp) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// melonJS logo\n\t\t\tthis.logoSprite = new Sprite(width / 2, height / 2, {\n\t\t\t\timage: \"melonjs_logo\",\n\t\t\t\tframewidth: 256,\n\t\t\t\tframeheight: 256,\n\t\t\t});\n\t\t\tapp.world.addChild(this.logoSprite, 2);\n\t\t});\n\t}\n\n\t/**\n\t * Remove loading screen children and unload the logo\n\t * @ignore\n\t */\n\t#cleanup() {\n\t\tthis.#cleanedUp = true;\n\n\t\t// Tolerate the case where the user's preload callback already\n\t\t// removed our children — `world.reset()` is a common pattern\n\t\t// in user setup code (the benchmark example does this), and\n\t\t// it nukes every world child including our progress bar +\n\t\t// logo. Calling `removeChild` on a non-child throws \"Child is\n\t\t// not mine.\", which propagates up through the LOADER_COMPLETE\n\t\t// emit chain and corrupts downstream state.\n\t\tif (this.progressBar) {\n\t\t\tif (this.#app.world.hasChild(this.progressBar)) {\n\t\t\t\tthis.#app.world.removeChild(this.progressBar);\n\t\t\t}\n\t\t\tthis.progressBar = null;\n\t\t}\n\t\tif (this.logoSprite) {\n\t\t\tif (this.#app.world.hasChild(this.logoSprite)) {\n\t\t\t\tthis.#app.world.removeChild(this.logoSprite);\n\t\t\t}\n\t\t\tthis.logoSprite = null;\n\t\t}\n\n\t\t// unload the logo image\n\t\tunload({ name: \"melonjs_logo\", type: \"image\" });\n\t}\n\n\t/**\n\t * Called by engine before deleting the object\n\t * @ignore\n\t */\n\tonDestroyEvent() {\n\t\t// remove the listener in case state.change() is called\n\t\t// before the preloader fires LOADER_COMPLETE\n\t\tif (!this.#cleanedUp) {\n\t\t\toff(LOADER_COMPLETE, this.#cleanup, this);\n\t\t}\n\t\tthis.#cleanup();\n\t}\n}\n\nexport default DefaultLoadingScreen;\n","import type Application from \"../application/application.ts\";\nimport { pauseTrack, resumeTrack } from \"./../audio/audio.ts\";\nimport FadeEffect from \"../camera/effects/fade_effect.ts\";\nimport MaskEffect from \"../camera/effects/mask_effect.ts\";\nimport DefaultLoadingScreen from \"./../loader/loadingscreen.js\";\nimport Stage from \"./../state/stage.ts\";\nimport {\n\tBLUR,\n\tBOOT,\n\temit,\n\tGAME_INIT,\n\ton,\n\tonce,\n\tSTATE_CHANGE,\n\tSTATE_PAUSE,\n\tSTATE_RESTART,\n\tSTATE_RESUME,\n\tSTATE_STOP,\n\tTICK,\n\tVIDEO_INIT,\n} from \"../system/event.ts\";\nimport { defer } from \"../utils/function.ts\";\n\ninterface StageEntry {\n\tstage: Stage;\n\ttransition: boolean;\n}\n\n// current state\nlet _state: number = -1;\n\n// requestAnimeFrame Id\nlet _animFrameId: number = -1;\n\n// whether the game state is \"paused\"\nlet _isPaused: boolean = false;\n\n// list of stages\nconst _stages: Record<number, StageEntry> = {};\n\n// transition type and parameters\nlet _transitionType: \"fade\" | \"mask\" = \"fade\";\n\nconst _transitionConfig: {\n\tcolor: string;\n\tduration: number;\n\tshape?:\n\t\t| import(\"../geometries/ellipse.ts\").Ellipse\n\t\t| import(\"../geometries/polygon.ts\").Polygon\n\t\t| undefined;\n} = {\n\tcolor: \"\",\n\tduration: 0,\n};\n\n// callback when state switch is done\n/** @ignore */\nlet _onSwitchComplete: (() => void) | null = null;\n\n// just to keep track of possible extra arguments\nlet _extraArgs: unknown[] | null = null;\n\n// store the elapsed time during pause/stop period\nlet _pauseTime: number = 0;\n\n// freeze() bookkeeping\nlet _freezeTimer: ReturnType<typeof setTimeout> | null = null;\nlet _freezeEndsAt: number = 0;\nlet _freezeMusic: boolean = false;\n// whether freeze() itself performed the pause (vs. the game was already paused)\nlet _freezeStartedPause: boolean = false;\nlet _freezeResolvers: Array<() => void> = [];\n\n/**\n * @ignore\n */\nfunction _startRunLoop(): void {\n\t// ensure nothing is running first and in valid state\n\tif (_animFrameId === -1 && _state !== -1) {\n\t\t// start the main loop\n\t\t_animFrameId = globalThis.requestAnimationFrame(_renderFrame);\n\t}\n}\n\n/**\n * Resume the game loop after a pause.\n * @ignore\n */\nfunction _resumeRunLoop(): void {\n\t// ensure game is actually paused and in valid state\n\tif (_isPaused && _state !== -1) {\n\t\t_isPaused = false;\n\t}\n}\n\n/**\n * Pause the loop for most stage objects.\n * @ignore\n */\nfunction _pauseRunLoop(): void {\n\t// Set the paused boolean to stop updates on (most) entities\n\t_isPaused = true;\n}\n\n/**\n * Drain and resolve any pending freeze() promises.\n * @ignore\n */\nfunction _resolveFreezeWaiters(): void {\n\tconst resolvers = _freezeResolvers;\n\t_freezeResolvers = [];\n\tfor (const resolve of resolvers) {\n\t\tresolve();\n\t}\n}\n\n/**\n * End an active freeze on timer expiry: optionally resume the run loop\n * (only if freeze itself performed the pause), then resolve waiters.\n * @ignore\n */\nfunction _endFreeze(): void {\n\t_freezeTimer = null;\n\t_freezeEndsAt = 0;\n\tconst wasOwnedPause = _freezeStartedPause;\n\t_freezeStartedPause = false;\n\tif (wasOwnedPause) {\n\t\tstate.resume(_freezeMusic);\n\t}\n\t_resolveFreezeWaiters();\n}\n\n/**\n * Cancel any active freeze without auto-resuming (the caller is taking over\n * pause/resume control). Clears the timer and resolves pending waiters.\n * @ignore\n */\nfunction _cancelFreeze(): void {\n\tif (_freezeTimer !== null) {\n\t\tclearTimeout(_freezeTimer);\n\t\t_freezeTimer = null;\n\t\t_freezeEndsAt = 0;\n\t\t_freezeStartedPause = false;\n\t\t_resolveFreezeWaiters();\n\t}\n}\n\n/**\n * this is only called when using requestAnimFrame stuff\n * @param time - current timestamp in milliseconds\n * @ignore\n */\nfunction _renderFrame(time: number): void {\n\temit(TICK, time);\n\t// schedule the next frame update\n\tif (_animFrameId !== -1) {\n\t\t_animFrameId = globalThis.requestAnimationFrame(_renderFrame);\n\t}\n}\n\n/**\n * stop the SO main loop\n * @ignore\n */\nfunction _stopRunLoop(): void {\n\t// cancel any previous animationRequestFrame\n\tglobalThis.cancelAnimationFrame(_animFrameId);\n\t_animFrameId = -1;\n}\n\n/**\n * start the SO main loop\n * @ignore\n */\nfunction _switchState(stateId: number): void {\n\t// clear previous interval if any\n\t_stopRunLoop();\n\n\t// call the stage destroy method\n\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n\tif (_stages[_state]) {\n\t\t_stages[_state].stage.destroy(_app);\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n\tif (_stages[stateId]) {\n\t\t// set the global variable\n\t\t_state = stateId;\n\n\t\t// call the reset function with the app reference and any extra args\n\t\tif (_extraArgs) {\n\t\t\t_stages[_state].stage.reset(_app, ..._extraArgs);\n\t\t} else {\n\t\t\t_stages[_state].stage.reset(_app);\n\t\t}\n\n\t\t// and start the main loop of the\n\t\t// new requested state\n\t\t_startRunLoop();\n\n\t\t// publish the pause event\n\t\temit(STATE_CHANGE);\n\n\t\t// execute callback if defined\n\t\tif (_onSwitchComplete) {\n\t\t\t_onSwitchComplete();\n\t\t\t_onSwitchComplete = null;\n\t\t}\n\t}\n}\n\n// the active application instance\nlet _app: Application;\n\n// initialize me.state on system boot\nonce(BOOT, () => {\n\t// set the built-in loading stage\n\tstate.set(state.LOADING, new DefaultLoadingScreen());\n\t// set and enable the default stage\n\tstate.set(state.DEFAULT, new Stage());\n\t// store the application reference when initialized\n\ton(GAME_INIT, (app: Application) => {\n\t\t_app = app;\n\t});\n\t// enable by default as soon as the display is initialized\n\tonce(VIDEO_INIT, () => {\n\t\tstate.change(state.DEFAULT, true);\n\t});\n\t// cancel any in-flight freeze when the window loses focus — the visual\n\t// hit-stop is short enough that by the time the user comes back the\n\t// \"moment\" is over. Application._onBlur still pauses the state via the\n\t// regular pauseOnBlur path, so the game stays paused while away.\n\ton(BLUR, () => {\n\t\t_cancelFreeze();\n\t});\n});\n\n/**\n * a State Manager (state machine)\n */\nconst state = {\n\t/**\n\t * default state ID for Loading Stage\n\t */\n\tLOADING: 0 as const,\n\n\t/**\n\t * default state ID for Menu Stage\n\t */\n\tMENU: 1 as const,\n\n\t/**\n\t * default state ID for \"Ready\" Stage\n\t */\n\tREADY: 2 as const,\n\n\t/**\n\t * default state ID for Play Stage\n\t */\n\tPLAY: 3 as const,\n\n\t/**\n\t * default state ID for Game Over Stage\n\t */\n\tGAMEOVER: 4 as const,\n\n\t/**\n\t * default state ID for Game End Stage\n\t */\n\tGAME_END: 5 as const,\n\n\t/**\n\t * default state ID for High Score Stage\n\t */\n\tSCORE: 6 as const,\n\n\t/**\n\t * default state ID for Credits Stage\n\t */\n\tCREDITS: 7 as const,\n\n\t/**\n\t * default state ID for Settings Stage\n\t */\n\tSETTINGS: 8 as const,\n\n\t/**\n\t * default state ID for the default Stage\n\t * (the default stage is the one running as soon as melonJS is started)\n\t */\n\tDEFAULT: 9 as const,\n\n\t/**\n\t * default state ID for user defined constants<br>\n\t * @example\n\t * const STATE_INFO = state.USER + 0;\n\t * const STATE_WARN = state.USER + 1;\n\t * const STATE_ERROR = state.USER + 2;\n\t * const STATE_CUTSCENE = state.USER + 3;\n\t */\n\tUSER: 100 as const,\n\n\t/**\n\t * Stop the current stage.\n\t * @param [shouldPauseTrack=false] - pause current track on screen stop.\n\t */\n\tstop(shouldPauseTrack: boolean = false): void {\n\t\t// only stop when we are not loading stuff\n\t\tif (_state !== this.LOADING && this.isRunning()) {\n\t\t\t// caller is taking over — drop any in-flight freeze\n\t\t\t_cancelFreeze();\n\t\t\t// stop the main loop\n\t\t\t_stopRunLoop();\n\n\t\t\t// current music stop\n\t\t\tif (shouldPauseTrack) {\n\t\t\t\tpauseTrack();\n\t\t\t}\n\n\t\t\t// store time when stopped\n\t\t\t_pauseTime = globalThis.performance.now();\n\n\t\t\t// publish the stop notification\n\t\t\temit(STATE_STOP);\n\t\t}\n\t},\n\n\t/**\n\t * pause the current stage\n\t * @param [music=false] - pause current music track on screen pause\n\t */\n\tpause(music: boolean = false): void {\n\t\t// only pause when we are not loading stuff\n\t\tif (_state !== this.LOADING && !this.isPaused()) {\n\t\t\t// stop the main loop\n\t\t\t_pauseRunLoop();\n\t\t\t// current music stop\n\t\t\tif (music) {\n\t\t\t\tpauseTrack();\n\t\t\t}\n\n\t\t\t// store time when paused\n\t\t\t_pauseTime = globalThis.performance.now();\n\n\t\t\t// publish the pause event\n\t\t\temit(STATE_PAUSE);\n\t\t}\n\t},\n\n\t/**\n\t * Restart the current stage from a full stop.\n\t * @param [music=false] - resume current music track on screen resume\n\t */\n\trestart(music: boolean = false): void {\n\t\tif (!this.isRunning()) {\n\t\t\t// restart the main loop\n\t\t\t_startRunLoop();\n\t\t\t// current music stop\n\t\t\tif (music) {\n\t\t\t\tresumeTrack();\n\t\t\t}\n\n\t\t\t// calculate the elpased time\n\t\t\t_pauseTime = globalThis.performance.now() - _pauseTime;\n\n\t\t\t// publish the restart notification\n\t\t\temit(STATE_RESTART, _pauseTime);\n\t\t}\n\t},\n\n\t/**\n\t * resume the current stage\n\t * @param [music=false] - resume current music track on screen resume\n\t */\n\tresume(music: boolean = false): void {\n\t\tif (this.isPaused()) {\n\t\t\t// caller is taking over — drop any in-flight freeze\n\t\t\t_cancelFreeze();\n\t\t\t// resume the main loop\n\t\t\t_resumeRunLoop();\n\t\t\t// current music stop\n\t\t\tif (music) {\n\t\t\t\tresumeTrack();\n\t\t\t}\n\n\t\t\t// calculate the elpased time\n\t\t\t_pauseTime = globalThis.performance.now() - _pauseTime;\n\n\t\t\t// publish the resume event\n\t\t\temit(STATE_RESUME, _pauseTime);\n\t\t}\n\t},\n\n\t/**\n\t * Freeze the current stage for a fixed duration, then automatically resume.\n\t * Useful for hit-stop / hit-pause effects on impact.\n\t *\n\t * Behaviour notes:\n\t * - If `freeze()` is called again while a freeze is already active, the\n\t *   freeze is *extended* to whichever end-time is later (calls do not\n\t *   stack). The `music` flag from the initial call is preserved.\n\t * - If the game was already paused when `freeze()` was called, the freeze\n\t *   timer will *not* unpause it on expiry — the game stays paused.\n\t * - Calling `state.resume()` or `state.stop()` while a freeze is active\n\t *   cancels the timer and resolves the returned promise immediately.\n\t * - The freeze is also cancelled when the window loses focus (BLUR) since\n\t *   the hit-stop's \"moment\" is short enough that the user has missed it\n\t *   by the time they return. The regular `pauseOnBlur` behaviour still\n\t *   keeps the game paused while away.\n\t * - Negative, `NaN`, and `Infinity` durations silently no-op.\n\t * @param duration - duration of the freeze in milliseconds\n\t * @param [music=false] - also pause the current music track during the freeze\n\t * @returns a Promise that resolves once the freeze ends (or is cancelled)\n\t * @example\n\t * // simple hit-stop on impact\n\t * state.freeze(80);\n\t *\n\t * // chain VFX after the freeze\n\t * await state.freeze(120);\n\t * spawnImpactParticles();\n\t */\n\tfreeze(duration: number, music: boolean = false): Promise<void> {\n\t\t// guard against NaN, Infinity, and negative durations — silently no-op\n\t\t// (mirrors how state.pause/resume quietly ignore invalid states)\n\t\tif (!Number.isFinite(duration) || duration < 0) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst now = globalThis.performance.now();\n\t\tconst newEndsAt = now + duration;\n\n\t\tif (_freezeTimer !== null) {\n\t\t\t// already frozen: only extend if the new end-time is later\n\t\t\tif (newEndsAt > _freezeEndsAt) {\n\t\t\t\tclearTimeout(_freezeTimer);\n\t\t\t\t_freezeEndsAt = newEndsAt;\n\t\t\t\t_freezeTimer = setTimeout(_endFreeze, newEndsAt - now);\n\t\t\t}\n\t\t} else {\n\t\t\t// start a new freeze\n\t\t\t_freezeMusic = music;\n\t\t\t_freezeEndsAt = newEndsAt;\n\t\t\t// only own the pause if the game wasn't already paused — that way\n\t\t\t// _endFreeze() won't unpause a manually-paused game on timer expiry\n\t\t\t_freezeStartedPause = !this.isPaused();\n\t\t\tif (_freezeStartedPause) {\n\t\t\t\tthis.pause(music);\n\t\t\t}\n\t\t\t_freezeTimer = setTimeout(_endFreeze, duration);\n\t\t}\n\n\t\treturn new Promise<void>((resolve) => {\n\t\t\t_freezeResolvers.push(resolve);\n\t\t});\n\t},\n\n\t/**\n\t * return the running state of the state manager\n\t * @returns true if a \"process is running\"\n\t */\n\tisRunning(): boolean {\n\t\treturn _animFrameId !== -1;\n\t},\n\n\t/**\n\t * Return the pause state of the state manager\n\t * @returns true if the game is paused\n\t */\n\tisPaused(): boolean {\n\t\treturn _isPaused;\n\t},\n\n\t/**\n\t * associate the specified state with a Stage\n\t * @param stateId - State ID (see constants)\n\t * @param stage - Instantiated Stage to associate with state ID\n\t * @param [start = false] - if true the state will be changed immediately after adding it.\n\t * @example\n\t * class MenuScreen extends Stage {\n\t *     onResetEvent(app) {\n\t *         // Load background image\n\t *         app.world.addChild(\n\t *             new ImageLayer(0, 0, {\n\t *                 image : \"bg\",\n\t *                 z: 0 // z-index\n\t *             })\n\t *         );\n\t *\n\t *         // Play music\n\t *         audio.playTrack(\"menu\");\n\t *     }\n\t *\n\t *     onDestroyEvent() {\n\t *         // Stop music\n\t *         audio.stopTrack();\n\t *     }\n\t * }\n\t *\n\t * state.set(state.MENU, new MenuScreen());\n\t */\n\tset(stateId: number, stage: Stage, start: boolean = false): void {\n\t\tif (!(stage instanceof Stage)) {\n\t\t\tthrow new Error(`${String(stage)} is not an instance of me.Stage`);\n\t\t}\n\t\t_stages[stateId] = {\n\t\t\tstage,\n\t\t\ttransition: true,\n\t\t};\n\n\t\tif (start) {\n\t\t\tthis.change(stateId);\n\t\t}\n\t},\n\n\t/**\n\t * returns the stage associated with the specified state\n\t * (or the current one if none is specified)\n\t * @param [stateId] - State ID (see constants)\n\t * @returns the Stage instance associated with the given state ID, or undefined\n\t */\n\tget(stateId: number = _state): Stage | undefined {\n\t\tif (typeof _stages[stateId] !== \"undefined\") {\n\t\t\treturn _stages[stateId].stage;\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t},\n\n\t/**\n\t * return a reference to the current stage<br>\n\t * useful to call a object specific method\n\t * @returns the current Stage instance, or undefined if no stage is active\n\t */\n\tcurrent(): Stage | undefined {\n\t\treturn this.get();\n\t},\n\n\t/**\n\t * specify a global transition effect\n\t * @param effect - \"fade\" for a color fade, \"mask\" for a shape-based mask transition\n\t * @param color - a CSS color value\n\t * @param [duration=1000] - expressed in milliseconds\n\t * @param [shape] - an Ellipse or Polygon defining the mask shape (required when effect is \"mask\")\n\t * @example\n\t * // classic fade to black\n\t * state.transition(\"fade\", \"#000\", 500);\n\t * @example\n\t * // iris (circle) mask transition\n\t * state.transition(\"mask\", \"#000\", 500, new Ellipse(0, 0, 1, 1));\n\t * @example\n\t * // diamond mask transition\n\t * state.transition(\"mask\", \"#000\", 400, new Polygon(0, 0, [\n\t *     { x: 0, y: -1 }, { x: 1, y: 0 },\n\t *     { x: 0, y: 1 }, { x: -1, y: 0 },\n\t * ]));\n\t */\n\ttransition(\n\t\teffect: \"fade\" | \"mask\",\n\t\tcolor: string,\n\t\tduration: number = 1000,\n\t\tshape?:\n\t\t\t| import(\"../geometries/ellipse.ts\").Ellipse\n\t\t\t| import(\"../geometries/polygon.ts\").Polygon,\n\t): void {\n\t\t_transitionType = effect;\n\t\t_transitionConfig.color = color;\n\t\t_transitionConfig.duration = duration;\n\t\t_transitionConfig.shape = shape;\n\t},\n\n\t/**\n\t * enable/disable the transition to a particular state (by default enabled for all)\n\t * @param stateId - State ID (see constants)\n\t * @param enable - true to enable transition, false to disable\n\t */\n\tsetTransition(stateId: number, enable: boolean): void {\n\t\t_stages[stateId].transition = enable;\n\t},\n\n\t/**\n\t * change the game/app state\n\t * @param stateId - State ID (see constants)\n\t * @param [forceChange=false] - if true the state will be changed immediately (without waiting for the next frame)\n\t * @param extraArgs - extra arguments to be passed to the reset functions\n\t * @example\n\t * // The onResetEvent method on the play screen will receive two args:\n\t * // \"level_1\" and the number 3\n\t * state.change(state.PLAY, false, \"level_1\", 3);\n\t */\n\tchange(\n\t\tstateId: number,\n\t\tforceChange: boolean = false,\n\t\t...extraArgs: unknown[]\n\t): void {\n\t\t// Protect against undefined Stage\n\t\tif (typeof _stages[stateId] === \"undefined\") {\n\t\t\tthrow new Error(`Undefined Stage for state '${stateId}'`);\n\t\t}\n\n\t\t// do nothing if already the current state\n\t\tif (!this.isCurrent(stateId)) {\n\t\t\t// store extra arguments if any\n\t\t\t_extraArgs = extraArgs.length > 0 ? extraArgs : null;\n\t\t\t// apply transition effect if configured\n\t\t\tif (_transitionConfig.duration && _stages[stateId].transition) {\n\t\t\t\tconst onComplete = () => {\n\t\t\t\t\tdefer(_switchState, state, stateId);\n\t\t\t\t};\n\n\t\t\t\tswitch (_transitionType) {\n\t\t\t\t\tcase \"fade\": {\n\t\t\t\t\t\tconst color = _transitionConfig.color;\n\t\t\t\t\t\tconst duration = _transitionConfig.duration;\n\t\t\t\t\t\t_onSwitchComplete = () => {\n\t\t\t\t\t\t\t// create reveal with the post-switch viewport\n\t\t\t\t\t\t\t_app.viewport.addCameraEffect(\n\t\t\t\t\t\t\t\tnew FadeEffect(_app.viewport, {\n\t\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\t\tdirection: \"out\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst fadeEffect = new FadeEffect(_app.viewport, {\n\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\tdirection: \"in\",\n\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tfadeEffect.isPersistent = true;\n\t\t\t\t\t\t_app.viewport.addCameraEffect(fadeEffect);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"mask\": {\n\t\t\t\t\t\tif (!_transitionConfig.shape) {\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\"melonJS: mask transition requires a shape, falling back to direct switch\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tonComplete();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst shape = _transitionConfig.shape;\n\t\t\t\t\t\tconst color = _transitionConfig.color;\n\t\t\t\t\t\tconst duration = _transitionConfig.duration;\n\t\t\t\t\t\t_onSwitchComplete = () => {\n\t\t\t\t\t\t\t// create reveal with the post-switch viewport\n\t\t\t\t\t\t\t_app.viewport.addCameraEffect(\n\t\t\t\t\t\t\t\tnew MaskEffect(_app.viewport, {\n\t\t\t\t\t\t\t\t\tshape,\n\t\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\t\tdirection: \"reveal\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst maskEffect = new MaskEffect(_app.viewport, {\n\t\t\t\t\t\t\tshape,\n\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\tdirection: \"hide\",\n\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tmaskEffect.isPersistent = true;\n\t\t\t\t\t\t_app.viewport.addCameraEffect(maskEffect);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Invalid transition type: ${_transitionType as string}`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// no transition — switch directly\n\t\t\telse {\n\t\t\t\t// wait for the last frame to be\n\t\t\t\t// \"finished\" before switching\n\t\t\t\tif (forceChange) {\n\t\t\t\t\t_switchState(stateId);\n\t\t\t\t} else {\n\t\t\t\t\tdefer(_switchState, this, stateId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * return true if the specified state is the current one\n\t * @param stateId - State ID (see constants)\n\t * @returns true if the specified state is the current one\n\t */\n\tisCurrent(stateId: number): boolean {\n\t\treturn _state === stateId;\n\t},\n};\nexport default state;\n","/**\n * where all preloaded content is cached\n */\n\n// contains all the images loaded\nexport const imgList = {};\n\n// contains all the TMX loaded\nexport const tmxList = {};\n\n// contains all the binary files loaded\nexport const binList = {};\n\n// contains all the JSON files\nexport const jsonList = {};\n\n// contains all the video files\nexport const videoList = {};\n\n// contains all the font files\nexport const fontList = {};\n\n// contains all the OBJ model files\nexport const objList = {};\n\n// contains all the MTL material files\nexport const mtlList = {};\n\n// contains all the parsed glTF/GLB scene descriptors\nexport const gltfList = {};\n\n// contains all the preloaded shader assets, keyed by name → the shared,\n// precompiled ShaderEffect (compiled at load time; video.init is an\n// inherent precondition of the preload flow)\nexport const shaderList = {};\n","/**\n * Whether `url` must be loaded over the `file:` scheme — either an absolute\n * `file://` URL, or a relative URL resolved against a `file:` document (the\n * case for a Cordova/Capacitor build packaged into an APK, where the app runs\n * from `file://`). `fetch()` cannot load such URLs in several WebView contexts\n * (it rejects with an opaque network error), whereas `XMLHttpRequest` can. The\n * document `protocol` is injectable so the relative-URL branch is unit-testable\n * (`location` is non-configurable in a real browser).\n * @param {string} url\n * @param {string} [protocol] - the hosting document's protocol\n * @returns {boolean}\n * @ignore\n */\nexport function isFileProtocol(url, protocol = globalThis.location?.protocol) {\n\treturn url.startsWith(\"file://\") || protocol === \"file:\";\n}\n\n/**\n * `file:`-scheme transport via `XMLHttpRequest` — the fallback for contexts\n * where `fetch()` refuses local files (Cordova/Capacitor APK on Android). It\n * resolves with a minimal `fetch`-`Response`-shaped object (`ok` / `status` /\n * `statusText` + `json()` / `text()` / `blob()` / `arrayBuffer()`) so\n * {@link fetchData} handles the response identically to the `fetch` path. A\n * successful local read reports `status === 0` (there is no HTTP status).\n * @param {string} url\n * @param {string} responseType - 'json' | 'text' | 'blob' | 'arrayBuffer'\n * @param {object} settings\n * @returns {Promise} resolves with a `Response`-like object\n * @ignore\n */\nfunction fetchDataXHR(url, responseType, settings) {\n\treturn new Promise((resolve, reject) => {\n\t\tconst xhr = new XMLHttpRequest();\n\t\txhr.open(\"GET\", url, true);\n\t\txhr.withCredentials = settings.withCredentials === true;\n\t\t// `json` is parsed from the response text (matches `fetch`'s throw-on-\n\t\t// invalid-JSON); blob/arrayBuffer use the native XHR response types.\n\t\txhr.responseType =\n\t\t\tresponseType === \"blob\"\n\t\t\t\t? \"blob\"\n\t\t\t\t: responseType === \"arrayBuffer\"\n\t\t\t\t\t? \"arraybuffer\"\n\t\t\t\t\t: \"text\";\n\n\t\txhr.onload = () => {\n\t\t\t// status 0 = a successful `file:` read (no HTTP status); an http(s)\n\t\t\t// server, if this path is ever hit there, reports the usual 2xx range\n\t\t\tresolve({\n\t\t\t\tok: xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300),\n\t\t\t\tstatus: xhr.status,\n\t\t\t\tstatusText: xhr.statusText,\n\t\t\t\tjson: () => {\n\t\t\t\t\treturn Promise.resolve().then(() => {\n\t\t\t\t\t\treturn JSON.parse(xhr.responseText);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\ttext: () => {\n\t\t\t\t\treturn Promise.resolve(xhr.responseText);\n\t\t\t\t},\n\t\t\t\tblob: () => {\n\t\t\t\t\treturn Promise.resolve(xhr.response);\n\t\t\t\t},\n\t\t\t\tarrayBuffer: () => {\n\t\t\t\t\treturn Promise.resolve(xhr.response);\n\t\t\t\t},\n\t\t\t});\n\t\t};\n\t\txhr.onerror = () => {\n\t\t\treject(new Error(`fetchData: XHR error loading ${url}`));\n\t\t};\n\t\txhr.send();\n\t});\n}\n\n/**\n * Fetches data from the specified URL.\n * @param {string} url - The URL to fetch the data from.\n * @param {string} responseType - The type of response expected ('json', 'text', 'blob', 'arrayBuffer').\n * @param {Object} [settings] - custom settings to apply to the request (@link https://developer.mozilla.org/en-US/docs/Web/API/fetch#options)\n * @returns {Promise} A promise that resolves with the fetched data or rejects with an error.\n * @ignore\n * @example\n * fetchData('https://api.example.com/data', 'json')\n *     .then(data => {\n *         // Handle the fetched JSON data\n *     })\n *     .catch(error => {\n *         // Handle the error\n *     });\n */\nexport function fetchData(url, responseType, settings = {}) {\n\t// `fetch()` can't load `file:` URLs in several WebView contexts — notably a\n\t// Cordova/Capacitor APK on Android, where the app is served from `file://`\n\t// and `fetch()` rejects with a network error. Route those through XHR, which\n\t// supports the `file:` scheme and returns a `Response`-shaped object so the\n\t// response handling below is shared verbatim between both transports.\n\tconst request = isFileProtocol(url)\n\t\t? fetchDataXHR(url, responseType, settings)\n\t\t: fetch(url, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\t// internally nocache is a string with a generated random number\n\t\t\t\tcache: settings.nocache === \"\" ? \"no-cache\" : \"reload\",\n\t\t\t\tcredentials: settings.withCredentials === true ? \"include\" : \"omit\",\n\t\t\t\t// see setting.crossorigin, \"anonymous\" is used for cross-origin requests\n\t\t\t\tmode: settings.crossOrigin === \"anonymous\" ? \"cors\" : \"no-cors\",\n\t\t\t});\n\n\treturn request.then((response) => {\n\t\t// status = 0 when file protocol is used, or cross-domain origin\n\t\tif (!response.ok && response.status !== 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Network response was not ok: ${response.status} ${response.statusText}`,\n\t\t\t);\n\t\t}\n\n\t\tswitch (responseType) {\n\t\t\tcase \"json\":\n\t\t\t\treturn response.json();\n\t\t\tcase \"text\":\n\t\t\t\treturn response.text();\n\t\t\tcase \"blob\":\n\t\t\t\treturn response.blob();\n\t\t\tcase \"arrayBuffer\":\n\t\t\t\treturn response.arrayBuffer();\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"Invalid response type: \" + responseType);\n\t\t}\n\t});\n}\n","import { imgList, jsonList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n// Aseprite file format spec: https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md\nconst ASE_MAGIC_FILE = 0xa5e0;\nconst ASE_MAGIC_FRAME = 0xf1fa;\n\n// chunk types we handle (others are skipped)\nconst CHUNK_OLD_PALETTE_04 = 0x0004;\nconst CHUNK_OLD_PALETTE_11 = 0x0011;\nconst CHUNK_LAYER = 0x2004;\nconst CHUNK_CEL = 0x2005;\nconst CHUNK_PALETTE = 0x2019;\nconst CHUNK_TAGS = 0x2018;\n// Aseprite's own tilemap feature — authored inside Aseprite as a tilemap layer.\n// The tileset chunk carries the per-tile bitmap, and tilemap cels (type 3)\n// carry tile indices instead of pixels. We deliberately reject these files\n// rather than produce a silent empty composite.\nconst CHUNK_TILESET = 0x2023;\n\n// cel types\nconst CEL_RAW_IMAGE = 0;\nconst CEL_LINKED = 1;\nconst CEL_COMPRESSED_IMAGE = 2;\nconst CEL_COMPRESSED_TILEMAP = 3;\n\nconst ERR_TILEMAP_MODE =\n\t\"aseprite: this file uses Aseprite's tilemap mode (tileset chunk or \" +\n\t\"tilemap-cel), which is not supported. Export the sprite to a regular PNG \" +\n\t\"(File → Export Sprite Sheet) or flatten the tilemap layer to pixel layers.\";\n\n// color depths\nconst DEPTH_RGBA = 32;\nconst DEPTH_GRAYSCALE = 16;\nconst DEPTH_INDEXED = 8;\n\n// layer flags\nconst LAYER_FLAG_VISIBLE = 1;\n\nconst TAG_DIRECTIONS = [\"forward\", \"reverse\", \"pingpong\", \"pingpong_reverse\"];\n\n/**\n * lightweight little-endian binary reader over an ArrayBuffer.\n */\nclass Reader {\n\tconstructor(buffer, offset = 0) {\n\t\tthis.view = new DataView(buffer);\n\t\tthis.bytes = new Uint8Array(buffer);\n\t\tthis.offset = offset;\n\t}\n\tu8() {\n\t\treturn this.view.getUint8(this.offset++);\n\t}\n\tu16() {\n\t\tconst v = this.view.getUint16(this.offset, true);\n\t\tthis.offset += 2;\n\t\treturn v;\n\t}\n\ti16() {\n\t\tconst v = this.view.getInt16(this.offset, true);\n\t\tthis.offset += 2;\n\t\treturn v;\n\t}\n\tu32() {\n\t\tconst v = this.view.getUint32(this.offset, true);\n\t\tthis.offset += 4;\n\t\treturn v;\n\t}\n\ti32() {\n\t\tconst v = this.view.getInt32(this.offset, true);\n\t\tthis.offset += 4;\n\t\treturn v;\n\t}\n\tstr() {\n\t\tconst len = this.u16();\n\t\tconst slice = this.bytes.subarray(this.offset, this.offset + len);\n\t\tthis.offset += len;\n\t\treturn new TextDecoder(\"utf-8\").decode(slice);\n\t}\n\tskip(n) {\n\t\tthis.offset += n;\n\t}\n\tslice(n) {\n\t\tconst out = this.bytes.subarray(this.offset, this.offset + n);\n\t\tthis.offset += n;\n\t\treturn out;\n\t}\n}\n\n/**\n * Decompress a zlib-compressed Uint8Array via the DecompressionStream API\n * (zlib format = \"deflate\" stream type per the WHATWG Compression Streams spec).\n * @ignore\n */\nasync function inflate(bytes) {\n\tconst stream = new Response(bytes).body.pipeThrough(\n\t\tnew DecompressionStream(\"deflate\"),\n\t);\n\treturn new Uint8Array(await new Response(stream).arrayBuffer());\n}\n\n/**\n * Decode a cel's pixel buffer (per the file's color depth) into a flat\n * RGBA Uint8ClampedArray sized for ImageData.\n * @ignore\n */\nfunction decodeCelPixels(raw, w, h, depth, palette, transparentIndex) {\n\tconst out = new Uint8ClampedArray(w * h * 4);\n\tswitch (depth) {\n\t\tcase DEPTH_RGBA:\n\t\t\t// already RGBA; copy through\n\t\t\tout.set(raw);\n\t\t\tbreak;\n\t\tcase DEPTH_GRAYSCALE:\n\t\t\tfor (let i = 0, j = 0; i < w * h; i++, j += 4) {\n\t\t\t\tconst v = raw[i * 2];\n\t\t\t\tconst a = raw[i * 2 + 1];\n\t\t\t\tout[j] = v;\n\t\t\t\tout[j + 1] = v;\n\t\t\t\tout[j + 2] = v;\n\t\t\t\tout[j + 3] = a;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DEPTH_INDEXED:\n\t\t\tfor (let i = 0, j = 0; i < w * h; i++, j += 4) {\n\t\t\t\tconst idx = raw[i];\n\t\t\t\tif (idx === transparentIndex) {\n\t\t\t\t\tout[j] = 0;\n\t\t\t\t\tout[j + 1] = 0;\n\t\t\t\t\tout[j + 2] = 0;\n\t\t\t\t\tout[j + 3] = 0;\n\t\t\t\t} else {\n\t\t\t\t\tconst p = palette[idx];\n\t\t\t\t\tif (p) {\n\t\t\t\t\t\tout[j] = p[0];\n\t\t\t\t\t\tout[j + 1] = p[1];\n\t\t\t\t\t\tout[j + 2] = p[2];\n\t\t\t\t\t\tout[j + 3] = p[3];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new Error(\"aseprite: unsupported color depth \" + depth);\n\t}\n\treturn out;\n}\n\n/**\n * Parse an Aseprite binary file into an intermediate structure:\n * { width, height, depth, frames: [{ duration, cels: [...] }], layers, tags, palette }.\n * Cel image data is already decompressed at this stage but still per-cel —\n * compositing happens in a second pass so linked cels can resolve.\n * @ignore\n */\nasync function parseAsepriteFile(buffer) {\n\tconst r = new Reader(buffer);\n\n\t// --- file header (128 bytes total — see Aseprite format spec) ---\n\tr.u32(); // file size (we trust the buffer length)\n\tconst magic = r.u16();\n\tif (magic !== ASE_MAGIC_FILE) {\n\t\tthrow new Error(\n\t\t\t\"aseprite: invalid file magic 0x\" +\n\t\t\t\tmagic.toString(16) +\n\t\t\t\t\" (expected 0xA5E0)\",\n\t\t);\n\t}\n\tconst frameCount = r.u16();\n\tconst width = r.u16();\n\tconst height = r.u16();\n\tconst depth = r.u16();\n\tr.u32(); // flags\n\tr.u16(); // deprecated speed\n\tr.u32();\n\tr.u32();\n\tconst transparentIndex = r.u8();\n\tr.skip(3);\n\tr.u16(); // number of colors (unused)\n\t// the remaining 92 bytes of the header (pixel ratio, grid x/y/w/h, 84 reserved)\n\t// aren't used here; seek to the absolute end of the 128-byte header so the\n\t// first frame starts at the correct offset\n\tr.offset = 128;\n\n\tconst palette = []; // index → [r,g,b,a]\n\tconst layers = []; // [{ flags, name, visible, ... }]\n\tconst tags = []; // [{ name, from, to, direction }]\n\tconst frames = []; // [{ duration, cels: [{ layer, x, y, opacity, w, h, pixels|linkFrame }] }]\n\t// stack of effective (ancestor-chain) visibility keyed by Aseprite's\n\t// \"child level\" field. Layer chunks arrive in tree order — children\n\t// follow their group — so we can resolve \"am I inside a hidden group?\"\n\t// by reading the most-recent stack slot at the level above.\n\tconst ancestorVisible = [];\n\n\tfor (let f = 0; f < frameCount; f++) {\n\t\tconst frameStart = r.offset;\n\t\tconst frameSize = r.u32();\n\t\tconst fmagic = r.u16();\n\t\tif (fmagic !== ASE_MAGIC_FRAME) {\n\t\t\tthrow new Error(\n\t\t\t\t\"aseprite: invalid frame magic at frame \" +\n\t\t\t\t\tf +\n\t\t\t\t\t\" (got 0x\" +\n\t\t\t\t\tfmagic.toString(16) +\n\t\t\t\t\t\")\",\n\t\t\t);\n\t\t}\n\t\tlet chunkCount = r.u16();\n\t\tconst duration = r.u16();\n\t\tr.skip(2);\n\t\tconst newChunks = r.u32();\n\t\t// per spec: the 32-bit chunk count is only authoritative when the\n\t\t// 16-bit field is saturated (0xFFFF). Older Aseprite versions wrote\n\t\t// zero into the new field, so a non-zero new field doesn't imply the\n\t\t// old one is invalid.\n\t\tif (chunkCount === 0xffff) {\n\t\t\tchunkCount = newChunks;\n\t\t}\n\n\t\tconst cels = [];\n\n\t\tfor (let c = 0; c < chunkCount; c++) {\n\t\t\tconst chunkStart = r.offset;\n\t\t\tconst chunkSize = r.u32();\n\t\t\tconst chunkType = r.u16();\n\t\t\tconst chunkEnd = chunkStart + chunkSize;\n\n\t\t\tswitch (chunkType) {\n\t\t\t\tcase CHUNK_LAYER: {\n\t\t\t\t\tconst flags = r.u16();\n\t\t\t\t\tr.u16(); // layer type (0=normal, 1=group, 2=tilemap)\n\t\t\t\t\tconst childLevel = r.u16();\n\t\t\t\t\tr.u16(); // default width (unused)\n\t\t\t\t\tr.u16(); // default height (unused)\n\t\t\t\t\tr.u16(); // blend mode (TODO: honor non-normal modes)\n\t\t\t\t\tr.u8(); // opacity\n\t\t\t\t\tr.skip(3);\n\t\t\t\t\tconst name = r.str();\n\t\t\t\t\tconst selfVisible = (flags & LAYER_FLAG_VISIBLE) !== 0;\n\t\t\t\t\tconst parentVisible =\n\t\t\t\t\t\tchildLevel === 0 ? true : ancestorVisible[childLevel - 1] !== false;\n\t\t\t\t\tconst visible = selfVisible && parentVisible;\n\t\t\t\t\tancestorVisible[childLevel] = visible;\n\t\t\t\t\t// truncate the stack past this level so a future sibling at the\n\t\t\t\t\t// same level doesn't inherit a stale deeper-nested visibility\n\t\t\t\t\tancestorVisible.length = childLevel + 1;\n\t\t\t\t\tlayers.push({ flags, name, visible });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CHUNK_CEL: {\n\t\t\t\t\tconst layer = r.u16();\n\t\t\t\t\tconst x = r.i16();\n\t\t\t\t\tconst y = r.i16();\n\t\t\t\t\tconst opacity = r.u8();\n\t\t\t\t\tconst celType = r.u16();\n\t\t\t\t\tr.skip(7);\n\t\t\t\t\tif (celType === CEL_RAW_IMAGE) {\n\t\t\t\t\t\tconst cw = r.u16();\n\t\t\t\t\t\tconst ch = r.u16();\n\t\t\t\t\t\tconst pixelBytes = cw * ch * (depth / 8);\n\t\t\t\t\t\tconst raw = r.slice(pixelBytes);\n\t\t\t\t\t\tcels.push({ layer, x, y, opacity, w: cw, h: ch, raw });\n\t\t\t\t\t} else if (celType === CEL_LINKED) {\n\t\t\t\t\t\tconst linkFrame = r.u16();\n\t\t\t\t\t\tcels.push({ layer, x, y, opacity, linkFrame });\n\t\t\t\t\t} else if (celType === CEL_COMPRESSED_IMAGE) {\n\t\t\t\t\t\tconst cw = r.u16();\n\t\t\t\t\t\tconst ch = r.u16();\n\t\t\t\t\t\tconst compressed = r.slice(chunkEnd - r.offset);\n\t\t\t\t\t\t// decompress; pixel decode happens after we know depth+palette\n\t\t\t\t\t\t// for the final composite pass\n\t\t\t\t\t\tconst inflated = await inflate(compressed);\n\t\t\t\t\t\tcels.push({\n\t\t\t\t\t\t\tlayer,\n\t\t\t\t\t\t\tx,\n\t\t\t\t\t\t\ty,\n\t\t\t\t\t\t\topacity,\n\t\t\t\t\t\t\tw: cw,\n\t\t\t\t\t\t\th: ch,\n\t\t\t\t\t\t\traw: inflated,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (celType === CEL_COMPRESSED_TILEMAP) {\n\t\t\t\t\t\tthrow new Error(ERR_TILEMAP_MODE);\n\t\t\t\t\t}\n\t\t\t\t\t// unknown cel types are skipped via chunkEnd\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CHUNK_PALETTE: {\n\t\t\t\t\tconst size = r.u32();\n\t\t\t\t\tconst first = r.u32();\n\t\t\t\t\tconst last = r.u32();\n\t\t\t\t\tr.skip(8);\n\t\t\t\t\tfor (let i = first; i <= last; i++) {\n\t\t\t\t\t\tconst flags = r.u16();\n\t\t\t\t\t\tconst cr = r.u8();\n\t\t\t\t\t\tconst cg = r.u8();\n\t\t\t\t\t\tconst cb = r.u8();\n\t\t\t\t\t\tconst ca = r.u8();\n\t\t\t\t\t\tif (flags & 1) {\n\t\t\t\t\t\t\tr.str();\n\t\t\t\t\t\t} // skip color name\n\t\t\t\t\t\tpalette[i] = [cr, cg, cb, ca];\n\t\t\t\t\t}\n\t\t\t\t\t// keep size for sanity; chunkEnd will realign\n\t\t\t\t\tvoid size;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CHUNK_OLD_PALETTE_04:\n\t\t\t\tcase CHUNK_OLD_PALETTE_11: {\n\t\t\t\t\t// only use the old palette chunk if no new palette has been read.\n\t\t\t\t\t// 0x0004 stores RGB as full bytes (0-255); 0x0011 uses 6-bit values\n\t\t\t\t\t// (0-63) — scale them up so both decode to the same color space.\n\t\t\t\t\tconst isLegacy6Bit = chunkType === CHUNK_OLD_PALETTE_11;\n\t\t\t\t\tif (palette.length === 0) {\n\t\t\t\t\t\tconst packets = r.u16();\n\t\t\t\t\t\tlet idx = 0;\n\t\t\t\t\t\tfor (let p = 0; p < packets; p++) {\n\t\t\t\t\t\t\tidx += r.u8(); // skip entries\n\t\t\t\t\t\t\tlet entries = r.u8();\n\t\t\t\t\t\t\tif (entries === 0) {\n\t\t\t\t\t\t\t\tentries = 256;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfor (let e = 0; e < entries; e++) {\n\t\t\t\t\t\t\t\tlet cr = r.u8();\n\t\t\t\t\t\t\t\tlet cg = r.u8();\n\t\t\t\t\t\t\t\tlet cb = r.u8();\n\t\t\t\t\t\t\t\tif (isLegacy6Bit) {\n\t\t\t\t\t\t\t\t\t// expand 6-bit (0-63) to 8-bit (0-255)\n\t\t\t\t\t\t\t\t\tcr = (cr << 2) | (cr >> 4);\n\t\t\t\t\t\t\t\t\tcg = (cg << 2) | (cg >> 4);\n\t\t\t\t\t\t\t\t\tcb = (cb << 2) | (cb >> 4);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tpalette[idx++] = [cr, cg, cb, 255];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CHUNK_TAGS: {\n\t\t\t\t\tconst count = r.u16();\n\t\t\t\t\tr.skip(8);\n\t\t\t\t\tfor (let t = 0; t < count; t++) {\n\t\t\t\t\t\tconst from = r.u16();\n\t\t\t\t\t\tconst to = r.u16();\n\t\t\t\t\t\tconst dir = r.u8();\n\t\t\t\t\t\t// per spec: WORD repeat count (0 = play forever), then\n\t\t\t\t\t\t// 6 bytes reserved. Older files predate the repeat field\n\t\t\t\t\t\t// and write zero here, which naturally maps to \"infinite\".\n\t\t\t\t\t\tconst repeat = r.u16();\n\t\t\t\t\t\tr.skip(6);\n\t\t\t\t\t\tr.skip(3); // RGB (deprecated)\n\t\t\t\t\t\tr.skip(1);\n\t\t\t\t\t\tconst name = r.str();\n\t\t\t\t\t\ttags.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tfrom,\n\t\t\t\t\t\t\tto,\n\t\t\t\t\t\t\tdirection: TAG_DIRECTIONS[dir] || \"forward\",\n\t\t\t\t\t\t\trepeat,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase CHUNK_TILESET:\n\t\t\t\t\tthrow new Error(ERR_TILEMAP_MODE);\n\t\t\t\tdefault:\n\t\t\t\t\t// unknown / unhandled chunk — skip via chunkEnd\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// jump to end of chunk regardless of how much we consumed\n\t\t\tr.offset = chunkEnd;\n\t\t}\n\n\t\tframes.push({ duration, cels });\n\t\t// jump to end of frame to handle trailing data / padding\n\t\tr.offset = frameStart + frameSize;\n\t}\n\n\treturn {\n\t\twidth,\n\t\theight,\n\t\tdepth,\n\t\ttransparentIndex,\n\t\tpalette,\n\t\tlayers,\n\t\ttags,\n\t\tframes,\n\t};\n}\n\n/**\n * Composite all frames into a single horizontal sprite-strip canvas, drawing\n * each visible cel onto its frame at (cel.x, cel.y). Returns the composited\n * canvas (HTMLCanvasElement or OffscreenCanvas).\n * @ignore\n */\nfunction composite(parsed) {\n\tconst { width, height, depth, frames, layers, palette, transparentIndex } =\n\t\tparsed;\n\tconst stripWidth = width * frames.length;\n\tconst stripHeight = height;\n\n\tconst canvas =\n\t\ttypeof OffscreenCanvas !== \"undefined\"\n\t\t\t? new OffscreenCanvas(stripWidth, stripHeight)\n\t\t\t: Object.assign(document.createElement(\"canvas\"), {\n\t\t\t\t\twidth: stripWidth,\n\t\t\t\t\theight: stripHeight,\n\t\t\t\t});\n\tconst ctx = canvas.getContext(\"2d\");\n\tctx.imageSmoothingEnabled = false;\n\n\tfor (let f = 0; f < frames.length; f++) {\n\t\tconst frame = frames[f];\n\t\tconst fx = f * width;\n\n\t\t// sort cels by layer index to draw bottom-up\n\t\tconst ordered = frame.cels.slice().sort((a, b) => {\n\t\t\treturn a.layer - b.layer;\n\t\t});\n\t\tfor (const cel of ordered) {\n\t\t\tconst layer = layers[cel.layer];\n\t\t\t// `visible` is the effective visibility (own flag AND every ancestor\n\t\t\t// group's flag); a child layer inside a hidden group still has its\n\t\t\t// own VISIBLE bit set but its `visible` resolves to false\n\t\t\tif (layer && layer.visible === false) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// resolve linked cels by copying the source frame's cel for this layer\n\t\t\tlet source = cel;\n\t\t\tif (cel.linkFrame !== undefined) {\n\t\t\t\tsource = frames[cel.linkFrame]?.cels.find((c) => {\n\t\t\t\t\treturn c.layer === cel.layer;\n\t\t\t\t});\n\t\t\t\tif (!source || !source.raw) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst rgba = decodeCelPixels(\n\t\t\t\tsource.raw,\n\t\t\t\tsource.w,\n\t\t\t\tsource.h,\n\t\t\t\tdepth,\n\t\t\t\tpalette,\n\t\t\t\ttransparentIndex,\n\t\t\t);\n\t\t\tconst img = new ImageData(rgba, source.w, source.h);\n\n\t\t\t// Always go through an intermediate canvas + drawImage. putImageData\n\t\t\t// replaces destination pixels wholesale (including any transparent\n\t\t\t// holes in the source), which would erase lower layers wherever an\n\t\t\t// upper layer is transparent — we need source-over compositing.\n\t\t\tconst tmp =\n\t\t\t\ttypeof OffscreenCanvas !== \"undefined\"\n\t\t\t\t\t? new OffscreenCanvas(source.w, source.h)\n\t\t\t\t\t: Object.assign(document.createElement(\"canvas\"), {\n\t\t\t\t\t\t\twidth: source.w,\n\t\t\t\t\t\t\theight: source.h,\n\t\t\t\t\t\t});\n\t\t\ttmp.getContext(\"2d\").putImageData(img, 0, 0);\n\t\t\tif (cel.opacity !== 255) {\n\t\t\t\tctx.globalAlpha = cel.opacity / 255;\n\t\t\t}\n\t\t\tctx.drawImage(tmp, fx + cel.x, cel.y);\n\t\t\tif (cel.opacity !== 255) {\n\t\t\t\tctx.globalAlpha = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn canvas;\n}\n\n/**\n * Build the JSON sidecar in the shape expected by parseAseprite()\n * in src/video/texture/parser/aseprite.js — meta.app must include\n * \"aseprite\" so identifyFormat() picks the right route.\n * @ignore\n */\nfunction buildAtlasJSON(parsed, imageName) {\n\tconst { width, height, frames, tags } = parsed;\n\tconst out = {};\n\tfor (let i = 0; i < frames.length; i++) {\n\t\tout[String(i)] = {\n\t\t\tframe: { x: i * width, y: 0, w: width, h: height },\n\t\t\trotated: false,\n\t\t\ttrimmed: false,\n\t\t\tspriteSourceSize: { x: 0, y: 0, w: width, h: height },\n\t\t\tsourceSize: { w: width, h: height },\n\t\t\tduration: frames[i].duration,\n\t\t};\n\t}\n\treturn {\n\t\tframes: out,\n\t\tmeta: {\n\t\t\tapp: \"http://www.aseprite.org/\",\n\t\t\tversion: \"binary\",\n\t\t\timage: imageName,\n\t\t\tformat: \"RGBA8888\",\n\t\t\tsize: { w: width * frames.length, h: height },\n\t\t\tscale: \"1\",\n\t\t\tframeTags: tags,\n\t\t},\n\t};\n}\n\n/**\n * Convert a canvas to an ImageBitmap when possible (matches the regular image\n * parser's cache shape so the renderer's texture cache treats it identically).\n * @ignore\n */\nasync function canvasToBitmap(canvas) {\n\tif (\n\t\ttypeof canvas.transferToImageBitmap === \"function\" &&\n\t\ttypeof createImageBitmap === \"function\"\n\t) {\n\t\treturn canvas.transferToImageBitmap();\n\t}\n\tif (typeof createImageBitmap === \"function\") {\n\t\treturn await createImageBitmap(canvas);\n\t}\n\treturn canvas;\n}\n\n/**\n * Parse a raw .aseprite ArrayBuffer into a composited bitmap and an atlas-style\n * JSON sidecar. Exported for advanced use cases (e.g. runtime-generated sprites)\n * — the preload path below is the usual entry point.\n */\nexport async function parseAseprite(buffer, imageName = \"default\") {\n\tconst parsed = await parseAsepriteFile(buffer);\n\tconst canvas = composite(parsed);\n\tconst bitmap = await canvasToBitmap(canvas);\n\tconst json = buildAtlasJSON(parsed, imageName);\n\treturn { bitmap, json };\n}\n\n/**\n * preload an .aseprite binary file: fetches the file, parses + composites it,\n * then stores the resulting bitmap in `imgList` and the JSON sidecar in\n * `jsonList` under the same asset name — so TextureAtlas, TMXTileset, etc.\n * can resolve it via the usual getImage() / getJSON() lookups.\n * @param {loader.Asset} data - asset descriptor\n * @param {Function} [onload]\n * @param {Function} [onerror]\n * @param {Object} [settings]\n * @returns {number}\n * @ignore\n */\nexport function preloadAseprite(data, onload, onerror, settings) {\n\tfetchData(data.src, \"arrayBuffer\", settings)\n\t\t.then(async (buffer) => {\n\t\t\tconst { bitmap, json } = await parseAseprite(buffer, data.name);\n\t\t\timgList[data.name] = bitmap;\n\t\t\tjsonList[data.name] = json;\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\t// surface the real error — the loader's generic onerror only knows\n\t\t\t// \"this asset failed\", which makes binary-format issues impossible\n\t\t\t// to debug from the outside\n\t\t\tconsole.error(`Failed to parse aseprite asset \"${data.name}\":`, error);\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { binList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n/**\n * parse/preload a Binary file\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the asset is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadBinary(data, onload, onerror, settings) {\n\tfetchData(data.src, \"arrayBuffer\", settings)\n\t\t.then((response) => {\n\t\t\t// this method is native and might be slightly more efficient\n\t\t\tconst decoder = new TextDecoder(); // the default for this is 'utf-8'\n\t\t\tbinList[data.name] = decoder.decode(response);\n\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t// callback\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { fontList } from \"../cache.js\";\n\n/**\n * parse/preload a font face\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the asset is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n * @example\n * preloadFontFace([\n *     { name: \"'kenpixel'\", type: \"fontface\", src: \"data/font/kenvector_future.woff2\" }\n * ]);\n */\nexport function preloadFontFace(data, onload, onerror) {\n\tconst fontFaceSet =\n\t\ttypeof globalThis.document !== \"undefined\"\n\t\t\t? globalThis.document.fonts\n\t\t\t: undefined;\n\n\t// FontFace constructor expects src in `url(...)` or `local()` format\n\t// only wrap plain paths in url(); leave url(), local(), and data URIs as-is\n\tif (!data.src.startsWith(\"url(\") && !data.src.startsWith(\"local(\")) {\n\t\tdata.src = \"url(\" + data.src + \")\";\n\t}\n\n\tif (typeof fontFaceSet !== \"undefined\") {\n\t\t// create a new font face\n\t\tconst font = new FontFace(data.name, data.src);\n\t\t// loading promise\n\t\tfont.load().then(\n\t\t\t() => {\n\t\t\t\t// add the font to the cache\n\t\t\t\tfontList[data.name] = font;\n\t\t\t\t// add the font to the document\n\t\t\t\tfontFaceSet.add(font);\n\t\t\t\t// onloaded callback\n\t\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t\tonload();\n\t\t\t\t}\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\t// rejected\n\t\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\t\tonerror(error);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t} else {\n\t\tif (typeof onerror === \"function\") {\n\t\t\tonerror();\n\t\t}\n\t}\n\n\treturn 1;\n}\n","import { Color } from \"../math/color.ts\";\nimport { Vector3d } from \"../math/vector3d.ts\";\nimport Renderable from \"../renderable/renderable.js\";\nimport state from \"../state/state.ts\";\n\n/**\n * Options accepted by the {@link Light3d} constructor.\n * @category Lighting\n */\nexport interface Light3dOptions {\n\t/**\n\t * light type: `\"directional\"` (a sun), `\"ambient\"` (a flat fill added to\n\t * every lit pixel), `\"point\"` (radiates from `position` with quadratic\n\t * falloff over `range`) or `\"spot\"` (a point light confined to a cone\n\t * along `direction`).\n\t */\n\ttype?: \"directional\" | \"ambient\" | \"point\" | \"spot\";\n\t/**\n\t * world-space direction the light travels along (directional lights,\n\t * and the cone axis of spot lights).\n\t */\n\tdirection?: [number, number, number];\n\t/** world-space position (point and spot lights). */\n\tposition?: [number, number, number];\n\t/**\n\t * light color — a {@link Color}, a CSS color string, or an `[r, g, b]`\n\t * array with components in `0..1` (the glTF convention). Defaults to white.\n\t */\n\tcolor?: Color | string | [number, number, number];\n\t/** scalar multiplier on the light's contribution. Defaults to `1`. */\n\tintensity?: number;\n\t/**\n\t * falloff distance in world units (point and spot lights): the light\n\t * fades quadratically from full strength at its position to zero at\n\t * `range` — the same stylized model as {@link Light2d}'s radius, not a\n\t * physical inverse-square. Defaults to `1000`.\n\t */\n\trange?: number;\n\t/**\n\t * spot cone: angle (radians) from the axis where the light is at full\n\t * strength. Defaults to `0`.\n\t */\n\tinnerConeAngle?: number;\n\t/**\n\t * spot cone: angle (radians) from the axis where the light reaches\n\t * zero, fading smoothly from `innerConeAngle`. Defaults to `π/4`.\n\t */\n\touterConeAngle?: number;\n}\n\n/**\n * A 3D light source for the mesh lighting path — the 3D counterpart of\n * {@link Light2d}. Like `Light2d`, a `Light3d` is a world {@link Renderable}:\n * add it to a container with `app.world.addChild(light)` and it auto-registers\n * with the active {@link Stage}, so any lit mesh in that scene is shaded by it.\n * Remove it from the world to turn it off. A light draws nothing itself.\n *\n * Four types are shaded:\n * - **`\"directional\"`** — a sun: a world-space `direction`, no falloff. Shaded\n *   via half-Lambert diffuse.\n * - **`\"ambient\"`** — a flat fill added to every lit pixel (the dark side of a\n *   mesh never goes fully black). `direction` / `position` are ignored.\n * - **`\"point\"`** — radiates from `position`, fading quadratically to zero at\n *   `range` (the same stylized falloff as {@link Light2d}'s radius).\n * - **`\"spot\"`** — a point light confined to a cone along `direction`, at\n *   full strength inside `innerConeAngle` and fading smoothly to zero at\n *   `outerConeAngle`.\n *\n * Fields are public and mutable, so a light can be animated at runtime\n * (e.g. a day/night cycle rotating `direction`, a flickering torch fading\n * `intensity`, a searchlight sweeping its cone).\n * @category Lighting\n * @example\n * import { Light3d } from \"melonjs\";\n *\n * // a sun + a soft ambient fill, added to the world like any renderable\n * const sun = new Light3d({ direction: [0.3, 1, 0.2], color: \"#fff\" });\n * app.world.addChild(sun);\n * app.world.addChild(new Light3d({ type: \"ambient\", intensity: 0.3 }));\n *\n * // a street lamp pool and a searchlight cone\n * app.world.addChild(new Light3d({\n *     type: \"point\", position: [120, -40, 60], color: \"#ffb45e\", range: 300,\n * }));\n * app.world.addChild(new Light3d({\n *     type: \"spot\", position: [0, -200, 0], direction: [0, 1, 0],\n *     range: 800, innerConeAngle: 0.2, outerConeAngle: 0.45,\n * }));\n *\n * // animate the sun in-game (direction is the way light travels)\n * sun.direction.set(Math.sin(t), 1, Math.cos(t)).normalize();\n */\nexport class Light3d extends Renderable {\n\t/** `\"directional\"`, `\"ambient\"`, `\"point\"` or `\"spot\"`. */\n\toverride type: \"directional\" | \"ambient\" | \"point\" | \"spot\";\n\t/**\n\t * world-space travel direction (directional lights, spot cone axis);\n\t * kept normalized.\n\t */\n\tdirection: Vector3d;\n\t/** world-space position (point and spot lights). */\n\tposition: Vector3d;\n\t/** the light color. */\n\tcolor: Color;\n\t/** scalar multiplier on the light's contribution. */\n\tintensity: number;\n\t/**\n\t * falloff distance in world units (point/spot) — full strength at the\n\t * position, zero at `range`.\n\t */\n\trange: number;\n\t/** spot cone inner angle (radians) — full strength inside it. */\n\tinnerConeAngle: number;\n\t/** spot cone outer angle (radians) — zero beyond it. */\n\touterConeAngle: number;\n\n\t/**\n\t * @param [options] - see {@link Light3dOptions}\n\t */\n\tconstructor(options: Light3dOptions = {}) {\n\t\t// a light has no visual footprint — a sizeless renderable at the origin\n\t\tsuper(0, 0, 0, 0);\n\n\t\tthis.type = options.type ?? \"directional\";\n\n\t\tthis.direction = new Vector3d(0, 1, 0);\n\t\tif (options.direction) {\n\t\t\tthis.direction.set(\n\t\t\t\toptions.direction[0],\n\t\t\t\toptions.direction[1],\n\t\t\t\toptions.direction[2],\n\t\t\t);\n\t\t}\n\t\tthis.direction.normalize();\n\n\t\tthis.position = new Vector3d(0, 0, 0);\n\t\tif (options.position) {\n\t\t\tthis.position.set(\n\t\t\t\toptions.position[0],\n\t\t\t\toptions.position[1],\n\t\t\t\toptions.position[2],\n\t\t\t);\n\t\t}\n\n\t\tif (options.color instanceof Color) {\n\t\t\tthis.color = options.color;\n\t\t} else if (Array.isArray(options.color)) {\n\t\t\t// glTF convention: [r, g, b] in 0..1\n\t\t\tthis.color = new Color(\n\t\t\t\toptions.color[0] * 255,\n\t\t\t\toptions.color[1] * 255,\n\t\t\t\toptions.color[2] * 255,\n\t\t\t\t1,\n\t\t\t);\n\t\t} else if (typeof options.color === \"string\") {\n\t\t\tthis.color = new Color().parseCSS(options.color);\n\t\t} else {\n\t\t\tthis.color = new Color(255, 255, 255, 1);\n\t\t}\n\n\t\tthis.intensity = options.intensity ?? 1;\n\t\tthis.range = options.range ?? 1000;\n\t\tthis.innerConeAngle = options.innerConeAngle ?? 0;\n\t\tthis.outerConeAngle = options.outerConeAngle ?? Math.PI / 4;\n\n\t\t// nothing to draw, and no transform to apply — keep it off the\n\t\t// renderer-state path entirely\n\t\tthis.autoTransform = false;\n\t}\n\n\t/**\n\t * Register with the active stage's 3D-light set on activation (when added to\n\t * a rooted container), mirroring {@link Light2d}.\n\t * @ignore\n\t */\n\toverride onActivateEvent() {\n\t\tstate.current()?._registerLight3d(this);\n\t}\n\n\t/**\n\t * Deregister from the active stage when removed from the world.\n\t * @ignore\n\t */\n\toverride onDeactivateEvent() {\n\t\tstate.current()?._unregisterLight3d(this);\n\t}\n\n\t/**\n\t * A light has no visual representation.\n\t * @ignore\n\t */\n\toverride draw() {}\n}\n","import Renderer from \"./../video/renderer.js\";\n\n/**\n * Ground (\"blob\") shadows for 3D objects — #1515.\n *\n * A soft dark ellipse painted on the ground beneath an object. This is not a\n * simulated shadow and does not try to be: for the paper-thin billboard\n * characters a 2.5D game is made of, a real shadow map both costs far more\n * than this engine wants to spend and *looks worse*, because a flat quad's\n * silhouette has to be special-cased to cast anything sensible at all. What\n * the player actually needs from a shadow here is contact — where the object\n * is standing, and how far off the ground it is mid-jump — and a blob says\n * exactly that.\n *\n * The blob is one textured quad laid flat on the ground plane, drawn through\n * the ordinary mesh path with blending on and depth writes off. Every shadow\n * in the scene shares one geometry and one texture, so a scene full of them\n * costs one draw each and no extra memory.\n * @ignore\n */\n\n/**\n * Side length of the shared falloff bake. Large enough that the ramp reads\n * smooth when a shadow fills a good part of the screen, small enough that the\n * whole thing is a rounding error against any real texture.\n * @ignore\n */\nconst FALLOFF_SIZE = 128;\n\n/**\n * The shared radial-falloff canvas, or `null` before first use.\n *\n * Kept as a **CPU canvas** for the same reason\n * {@link Renderer.getWhitePixel} is: the GPU copy lives in the renderer's\n * `TextureCache`, which is wiped and rebuilt on context loss, so keeping the\n * source on the CPU means the shadow survives a lost context for free. It is\n * also why this can be module-level while the quad below cannot.\n * @ignore\n */\nlet falloffCanvas = null;\n\n/**\n * Bake (once) a radial alpha ramp: opaque white at the centre, fading to\n * fully transparent at the rim.\n *\n * White rather than black because the colour comes from the draw tint — that\n * way the same bake serves a black shadow, a tinted one, or a coloured pool\n * of light, and nothing has to be re-baked to change it.\n * @returns {HTMLCanvasElement|OffscreenCanvas} the shared falloff canvas\n * @ignore\n */\nexport function getShadowFalloff() {\n\tif (falloffCanvas !== null) {\n\t\treturn falloffCanvas;\n\t}\n\tconst canvas = Renderer.createCanvas(FALLOFF_SIZE, FALLOFF_SIZE, true);\n\tconst context = canvas.getContext(\"2d\");\n\tif (context === null) {\n\t\tthrow new Error(\n\t\t\t\"groundshadow: 2D context unavailable on the allocated canvas\",\n\t\t);\n\t}\n\tconst image = context.createImageData(FALLOFF_SIZE, FALLOFF_SIZE);\n\tconst data = image.data;\n\tconst centre = (FALLOFF_SIZE - 1) / 2;\n\tlet at = 0;\n\tfor (let y = 0; y < FALLOFF_SIZE; y++) {\n\t\tfor (let x = 0; x < FALLOFF_SIZE; x++) {\n\t\t\tconst dx = (x - centre) / centre;\n\t\t\tconst dy = (y - centre) / centre;\n\t\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\t\t\t// smoothstep from the centre to the rim rather than a linear ramp:\n\t\t\t// a linear falloff leaves a visible hard disc in the middle and a\n\t\t\t// noticeable seam where it reaches zero\n\t\t\tconst t = distance >= 1 ? 0 : 1 - distance;\n\t\t\tconst alpha = t * t * (3 - 2 * t);\n\t\t\tdata[at] = 255;\n\t\t\tdata[at + 1] = 255;\n\t\t\tdata[at + 2] = 255;\n\t\t\tdata[at + 3] = Math.round(alpha * 255);\n\t\t\tat += 4;\n\t\t}\n\t}\n\tcontext.putImageData(image, 0, 0);\n\tfalloffCanvas = canvas;\n\treturn falloffCanvas;\n}\n\n/**\n * Drop the shared bake. Only the tests need this — the canvas is CPU-side and\n * costs 64 KB, so an application never has a reason to release it.\n * @ignore\n */\nexport function resetShadowFalloff() {\n\tfalloffCanvas = null;\n}\n\n/**\n * Whether a mesh's geometry has any vertical extent at all.\n *\n * The test a **blanket** opt-in needs: a ground shadow says \"this object is\n * standing above the floor\", and a flat plane lying in the floor is the floor.\n * Shadowing it with itself smears a blob the size of the whole ground across\n * it, which is what a scene-wide `castGroundShadow` would otherwise do to\n * every glTF scene that ships one. A per-object opt-in bypasses this — that\n * one is an explicit instruction.\n *\n * Deliberately model-space and unrotated: the caller asks about the geometry,\n * not about where it currently sits.\n * @param {Float32Array} vertices - model-space vertex positions (x, y, z)\n * @param {number} vertexCount - how many vertices to read\n * @returns {boolean} true when the geometry is not flat in Y\n * @ignore\n */\nexport function hasVerticalExtent(vertices, vertexCount) {\n\tif (vertices === undefined || vertexCount === 0) {\n\t\treturn false;\n\t}\n\tlet min = Number.POSITIVE_INFINITY;\n\tlet max = Number.NEGATIVE_INFINITY;\n\tfor (let i = 0; i < vertexCount; i++) {\n\t\tconst y = vertices[i * 3 + 1];\n\t\tif (y < min) {\n\t\t\tmin = y;\n\t\t}\n\t\tif (y > max) {\n\t\t\tmax = y;\n\t\t}\n\t}\n\t// relative to the horizontal size, so the answer does not depend on the\n\t// units a model happens to be authored in\n\tlet spread = 0;\n\tfor (let i = 0; i < vertexCount; i++) {\n\t\tconst x = Math.abs(vertices[i * 3]);\n\t\tconst z = Math.abs(vertices[i * 3 + 2]);\n\t\tconst wider = x > z ? x : z;\n\t\tif (wider > spread) {\n\t\t\tspread = wider;\n\t\t}\n\t}\n\treturn max - min > spread * 1e-3;\n}\n\n/**\n * The shadow quad: a 1×1 square lying in the ground plane, centred on the\n * origin, facing up.\n *\n * Render space is Y-DOWN, so the ground plane is XZ and \"up\" is `-Y` — the\n * same convention `Sprite3d.WORLD_UP` states.\n * @ignore\n */\nconst QUAD_VERTICES = new Float32Array([\n\t-0.5, 0, -0.5, 0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5,\n]);\nconst QUAD_UVS = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);\nconst QUAD_INDICES = new Uint16Array([0, 1, 2, 0, 2, 3]);\nconst QUAD_NORMALS = new Float32Array([0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0]);\n\n/**\n * Borrow the shadow quad for a renderer, building it on first use.\n *\n * Cached **on the renderer**, not at module level, for two reasons: a `Mesh`\n * resolves its texture through the active renderer's texture cache, so a\n * module-level one would bind itself to whichever application happened to\n * construct it first; and hanging it off the renderer means it dies with the\n * renderer instead of outliving it.\n *\n * Two are kept — lit and unlit — so the shadow can be drawn through whichever\n * mesh batcher its owner is already using. Sharing one unlit quad between lit\n * owners would force a `litMesh → mesh → litMesh` batcher transition per\n * shadowed object, each costing a bind/unbind pair and a flush: far more than\n * the single draw the shadow is supposed to be.\n *\n * Retained geometry is keyed per mesh object, so one quad uploads once and\n * every shadow in the scene redraws it at a different matrix for free.\n * @param {object} renderer - the active renderer\n * @param {boolean} lit - whether the owning mesh draws lit\n * @param {Function} MeshClass - the `Mesh` constructor (passed in to avoid a\n * circular import: `Mesh` owns the shadow, not the other way round)\n * @returns {object} the shared shadow quad for that tier\n * @ignore\n */\nexport function getShadowQuad(renderer, lit, MeshClass) {\n\tlet quads = renderer._shadowQuads;\n\tif (quads === undefined) {\n\t\tquads = renderer._shadowQuads = {};\n\t}\n\tconst key = lit === true ? \"lit\" : \"unlit\";\n\tlet quad = quads[key];\n\tif (quad === undefined) {\n\t\tquad = new MeshClass(0, 0, {\n\t\t\tvertices: QUAD_VERTICES,\n\t\t\tuvs: QUAD_UVS,\n\t\t\tindices: QUAD_INDICES,\n\t\t\tnormals: QUAD_NORMALS,\n\t\t\ttexture: getShadowFalloff(),\n\t\t\t// the geometry is already unit-sized; placement rides the matrix\n\t\t\twidth: 1,\n\t\t\tnormalize: false,\n\t\t\tlit: lit === true,\n\t\t\t// a ground quad is seen from above and, on a mirrored bridge, from\n\t\t\t// whichever side the winding lands on — never cull it\n\t\t\tcullBackFaces: false,\n\t\t});\n\t\t// Internal: this mesh draws with blending on and depth writes off.\n\t\t// Deliberately NOT a public `Mesh` option (#1516): a public blended\n\t\t// mesh axis means owning back-to-front sorting for arbitrary\n\t\t// translucent geometry, which a flat ground-hugging blob does not need.\n\t\tquad._blendedDraw = true;\n\t\tquads[key] = quad;\n\t}\n\treturn quad;\n}\n\n/**\n * The blob quad an `InstancedMesh` draws from — sized to the prototype's own\n * footprint (#1515).\n *\n * The shared quad is a unit square, and the instanced vertex stage scales it by\n * each record's horizontal scale alone. That silently assumes the prototype\n * geometry is exactly 1 unit across, which is true of almost nothing: glTF\n * meshes load with `normalize: false` and keep their authored size, so a tree\n * 3 units wide got a blob sized for a 1-unit tree. It also skipped the contact\n * spread and the minor-axis floor the per-object tier applies, so the SAME\n * asset drew a different shadow depending on whether it was instanced.\n *\n * Baking the extents into four vertices fixes it with no shader change and no\n * per-draw uniform: retained geometry is keyed per mesh object, so this uploads\n * once per `InstancedMesh` and is redrawn for free.\n * @param {object} mesh - the InstancedMesh casting the shadows\n * @param {Function} MeshClass - the `Mesh` constructor (avoids a circular import)\n * @param {number} halfX - model-space half-extent along X, spread applied\n * @param {number} halfZ - model-space half-extent along Z, spread applied\n * @param {number} version - the prototype's geometry version\n * @returns {object} the quad to draw this scatter's blobs from\n * @ignore\n */\nexport function getInstancedShadowQuad(mesh, MeshClass, halfX, halfZ, version) {\n\tconst key = `${mesh.lit === true}:${version}:${halfX}:${halfZ}`;\n\tlet quad = mesh._shadowQuad;\n\tif (quad !== undefined && quad._shadowKey === key) {\n\t\treturn quad;\n\t}\n\tquad?.destroy();\n\tquad = new MeshClass(0, 0, {\n\t\tvertices: new Float32Array([\n\t\t\t-halfX,\n\t\t\t0,\n\t\t\t-halfZ,\n\t\t\thalfX,\n\t\t\t0,\n\t\t\t-halfZ,\n\t\t\thalfX,\n\t\t\t0,\n\t\t\thalfZ,\n\t\t\t-halfX,\n\t\t\t0,\n\t\t\thalfZ,\n\t\t]),\n\t\tuvs: QUAD_UVS,\n\t\tindices: QUAD_INDICES,\n\t\tnormals: QUAD_NORMALS,\n\t\ttexture: getShadowFalloff(),\n\t\twidth: 1,\n\t\tnormalize: false,\n\t\tlit: mesh.lit === true,\n\t\tcullBackFaces: false,\n\t});\n\tquad._blendedDraw = true;\n\tquad._shadowKey = key;\n\tmesh._shadowQuad = quad;\n\treturn quad;\n}\n\n/**\n * Release the shadow quads a renderer built, if any. Called from renderer\n * teardown so the retained GPU geometry goes with it.\n * @param {object} renderer - the renderer being torn down\n * @ignore\n */\nexport function releaseShadowQuads(renderer) {\n\tconst quads = renderer._shadowQuads;\n\tif (quads !== undefined) {\n\t\tquads.lit?.destroy();\n\t\tquads.unlit?.destroy();\n\t\trenderer._shadowQuads = undefined;\n\t}\n}\n","import { Matrix3d } from \"../math/matrix3d.ts\";\n\n/**\n * A plane in 3D space, expressed as `Ax + By + Cz + D = 0`.\n * `normal` is `(A, B, C)`; `constant` is `D`. Used by {@link Frustum}\n * to represent the six bounding planes for culling.\n * @category Camera\n */\nexport interface Plane {\n\t/** plane normal (A, B, C in the plane equation) — not necessarily unit-length */\n\tnx: number;\n\tny: number;\n\tnz: number;\n\t/** plane constant (D in the plane equation) */\n\td: number;\n}\n\nexport interface FrustumOptions {\n\t/** vertical field of view in radians (default: π / 3 = 60°) */\n\tfov?: number;\n\t/** aspect ratio (width / height) — default 1.0 (square) */\n\taspect?: number;\n\t/** distance to the near clipping plane (default 0.1) */\n\tnear?: number;\n\t/** distance to the far clipping plane (default 1000) */\n\tfar?: number;\n}\n\n/**\n * A view frustum — the truncated pyramid that defines a perspective\n * camera's visible volume. Holds the four projection parameters\n * (`fov`, `aspect`, `near`, `far`) and the matching projection matrix.\n *\n * Used by {@link Camera3d} as its source of truth for perspective\n * projection. The matrix follows melonJS conventions: Y-down (sprite\n * at higher `y` appears lower on screen, matching Camera2d) and +Z\n * forward (sprite at higher `pos.z` is farther from the camera and\n * renders smaller). This differs from the OpenGL default of Y-up and\n * -Z forward, but matches the rest of the engine and lets Camera2d\n * code translate directly to Camera3d.\n *\n * Plane-based frustum culling is available via {@link Frustum#planes}\n * (rebuilt by {@link Frustum#setFromViewProjection} per frame) plus\n * {@link Frustum#containsPoint} / {@link Frustum#intersectsSphere} for\n * culling tests. `Camera3d.update` calls `setFromViewProjection` once\n * per frame so its `isVisible(obj)` queries against current planes —\n * no per-frame setup needed from user code.\n * @category Camera\n * @example\n * const frustum = new Frustum({ fov: Math.PI / 3, aspect: 16 / 9 });\n * frustum.near = 0.5;\n * frustum.update();\n * renderer.setProjection(frustum.projectionMatrix);\n */\nexport default class Frustum {\n\t/**\n\t * vertical field of view in radians.\n\t * Mutating this field requires calling {@link Frustum#update} to\n\t * rebuild the projection matrix — or use {@link Frustum#set} to\n\t * change multiple parameters and update in one call.\n\t */\n\tfov: number;\n\n\t/**\n\t * aspect ratio (width / height). Camera3d sets this automatically\n\t * from its viewport on resize.\n\t */\n\taspect: number;\n\n\t/**\n\t * distance to the near clipping plane (positive — measured along\n\t * +Z, the camera's forward direction).\n\t */\n\tnear: number;\n\n\t/**\n\t * distance to the far clipping plane.\n\t */\n\tfar: number;\n\n\t/**\n\t * the perspective projection matrix derived from `fov`, `aspect`,\n\t * `near` and `far`. Rebuilt by {@link Frustum#update}.\n\t */\n\tprojectionMatrix: Matrix3d;\n\n\t/**\n\t * The six bounding planes of this frustum in world space, in order:\n\t * left, right, bottom, top, near, far. Each plane is oriented so\n\t * its `(nx, ny, nz)` normal points **inward** — a point with\n\t * positive signed distance to a plane is on the visible side.\n\t *\n\t * Populated by {@link Frustum#setFromViewProjection}. Callers that\n\t * use {@link Frustum#intersectsSphere} or {@link Frustum#containsPoint}\n\t * must first call `setFromViewProjection` each frame the camera\n\t * moves; otherwise the planes describe a stale frustum.\n\t */\n\tplanes: Plane[];\n\n\t/**\n\t * @param [opts] - initial parameters; any omitted field uses the\n\t *   class default\n\t */\n\tconstructor(opts?: FrustumOptions) {\n\t\tthis.fov = opts?.fov ?? Math.PI / 3;\n\t\tthis.aspect = opts?.aspect ?? 1.0;\n\t\tthis.near = opts?.near ?? 0.1;\n\t\tthis.far = opts?.far ?? 1000;\n\t\tthis.projectionMatrix = new Matrix3d();\n\t\t// six planes — left, right, bottom, top, near, far. Initialised\n\t\t// to all-zero; populated by `setFromViewProjection` on first\n\t\t// camera update.\n\t\tthis.planes = [\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t\t{ nx: 0, ny: 0, nz: 0, d: 0 },\n\t\t];\n\t\tthis.update();\n\t}\n\n\t/**\n\t * Atomically set all four parameters and rebuild the projection\n\t * matrix in one call.\n\t * @param fov - vertical field of view in radians\n\t * @param aspect - aspect ratio (width / height)\n\t * @param near - distance to the near clipping plane\n\t * @param far - distance to the far clipping plane\n\t * @returns this Frustum for chaining\n\t */\n\tset(fov: number, aspect: number, near: number, far: number): this {\n\t\tthis.fov = fov;\n\t\tthis.aspect = aspect;\n\t\tthis.near = near;\n\t\tthis.far = far;\n\t\tthis.update();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Rebuild {@link Frustum#projectionMatrix} from the current\n\t * parameter values. Call this after mutating any of `fov`,\n\t * `aspect`, `near`, `far` individually.\n\t *\n\t * The matrix is the standard OpenGL perspective post-multiplied by\n\t * `scale(1, -1, -1)` so that:\n\t * - Y-down matches melonJS screen + Camera2d conventions\n\t * - +Z is forward (positive `pos.z` = farther from camera)\n\t */\n\tupdate(): void {\n\t\tthis.projectionMatrix.perspective(\n\t\t\tthis.fov,\n\t\t\tthis.aspect,\n\t\t\tthis.near,\n\t\t\tthis.far,\n\t\t);\n\t\t// flip Y (down) + Z (+Z forward) to match engine conventions\n\t\tthis.projectionMatrix.scale(1, -1, -1);\n\t}\n\n\t/**\n\t * Rebuild the six bounding {@link Frustum#planes} from the world →\n\t * clip matrix. Standard Gribb–Hartmann extraction: each plane is\n\t * one of the six combinations of the matrix's row 3 (the \"w\" row)\n\t * ± rows 0, 1, 2.\n\t *\n\t * Call this once per frame after the camera has moved (typically\n\t * from `Camera3d.update`); the planes are then valid in world\n\t * space for that frame and can be tested against world-space\n\t * bounds via {@link Frustum#intersectsSphere} /\n\t * {@link Frustum#containsPoint}.\n\t *\n\t * **Pass `projectionMatrix × viewMatrix`** — column-major /\n\t * gl-matrix convention: a world-space point becomes a clip-space\n\t * point via `clip = projection × view × world`, so the combined\n\t * matrix is `projection × view`. The parameter name says\n\t * `viewProjection` for that reason — it's the matrix uploaded to\n\t * `uProjectionMatrix` in the vertex shader (after Camera3d bakes\n\t * the per-frame world translate into the view via\n\t * `container.translate`).\n\t * @param viewProjection - the `projectionMatrix × viewMatrix` matrix\n\t */\n\tsetFromViewProjection(viewProjection: Matrix3d): void {\n\t\t// column-major matrix: `m.val[col * 4 + row]`. Row r is the\n\t\t// elements at indices `r, 4+r, 8+r, 12+r` (one from each column).\n\t\tconst m = viewProjection.val;\n\t\tconst r0x = m[0],\n\t\t\tr0y = m[4],\n\t\t\tr0z = m[8],\n\t\t\tr0w = m[12];\n\t\tconst r1x = m[1],\n\t\t\tr1y = m[5],\n\t\t\tr1z = m[9],\n\t\t\tr1w = m[13];\n\t\tconst r2x = m[2],\n\t\t\tr2y = m[6],\n\t\t\tr2z = m[10],\n\t\t\tr2w = m[14];\n\t\tconst r3x = m[3],\n\t\t\tr3y = m[7],\n\t\t\tr3z = m[11],\n\t\t\tr3w = m[15];\n\n\t\t// each plane is a sum/difference of two rows, then normalized\n\t\t// so the `(nx, ny, nz)` is unit length (so `intersectsSphere`\n\t\t// can compare distance against radius in world units).\n\t\tconst set = (\n\t\t\tidx: number,\n\t\t\tnx: number,\n\t\t\tny: number,\n\t\t\tnz: number,\n\t\t\td: number,\n\t\t) => {\n\t\t\tconst inv = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);\n\t\t\tconst p = this.planes[idx];\n\t\t\tp.nx = nx * inv;\n\t\t\tp.ny = ny * inv;\n\t\t\tp.nz = nz * inv;\n\t\t\tp.d = d * inv;\n\t\t};\n\n\t\t// left = row3 + row0  (points pass when their signed distance > 0)\n\t\tset(0, r3x + r0x, r3y + r0y, r3z + r0z, r3w + r0w);\n\t\t// right = row3 - row0\n\t\tset(1, r3x - r0x, r3y - r0y, r3z - r0z, r3w - r0w);\n\t\t// bottom = row3 + row1\n\t\tset(2, r3x + r1x, r3y + r1y, r3z + r1z, r3w + r1w);\n\t\t// top = row3 - row1\n\t\tset(3, r3x - r1x, r3y - r1y, r3z - r1z, r3w - r1w);\n\t\t// near = row3 + row2\n\t\tset(4, r3x + r2x, r3y + r2y, r3z + r2z, r3w + r2w);\n\t\t// far = row3 - row2\n\t\tset(5, r3x - r2x, r3y - r2y, r3z - r2z, r3w - r2w);\n\t}\n\n\t/**\n\t * Test whether a world-space sphere overlaps this frustum.\n\t * Conservative — a sphere that touches even one plane's positive\n\t * side is reported visible. Always run {@link Frustum#setFromViewProjection}\n\t * first so the planes describe the current camera view.\n\t * @param x - sphere center x in world coords\n\t * @param y - sphere center y in world coords\n\t * @param z - sphere center z in world coords\n\t * @param radius - sphere radius in world units\n\t * @returns true if the sphere is at least partially inside the frustum\n\t */\n\tintersectsSphere(x: number, y: number, z: number, radius: number): boolean {\n\t\t// for each plane, compute signed distance from sphere center.\n\t\t// if the center is farther than `radius` on the OUTSIDE side\n\t\t// (distance < -radius) of ANY plane, the whole sphere is outside.\n\t\tfor (let i = 0; i < 6; i++) {\n\t\t\tconst p = this.planes[i];\n\t\t\tconst distance = p.nx * x + p.ny * y + p.nz * z + p.d;\n\t\t\tif (distance < -radius) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Test whether a world-space point is inside this frustum.\n\t * Always run {@link Frustum#setFromViewProjection} first.\n\t * @param x - world x\n\t * @param y - world y\n\t * @param z - world z\n\t * @returns true if the point is inside (on the positive side of every plane)\n\t */\n\tcontainsPoint(x: number, y: number, z: number): boolean {\n\t\tfor (let i = 0; i < 6; i++) {\n\t\t\tconst p = this.planes[i];\n\t\t\tif (p.nx * x + p.ny * y + p.nz * z + p.d < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n}\n","import { Matrix3d } from \"../math/matrix3d.ts\";\nimport type { ObservableVector3d } from \"../math/observableVector3d.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport { Vector3d } from \"../math/vector3d.ts\";\nimport type Container from \"./../renderable/container.js\";\nimport type Renderable from \"./../renderable/renderable.js\";\nimport type Renderer from \"./../video/renderer.js\";\nimport Camera2d from \"./camera2d.ts\";\nimport Frustum, { type FrustumOptions } from \"./frustum.ts\";\n\n// reusable unit-axis vectors for rotation calls. Pure constants so\n// allocation only happens once per module load, not per frame.\nconst AXIS_X = new Vector3d(1, 0, 0);\nconst AXIS_Y = new Vector3d(0, 1, 0);\n\n// Scratch matrices reused by `_rebuildFrustumPlanes` to avoid per-frame\n// allocation. Single-instance is safe because draw / update is\n// single-threaded and these are only touched inside one method.\nconst _viewMatrix = new Matrix3d();\nconst _viewProjection = new Matrix3d();\n// scratch point for worldToScreen, reused to avoid per-call allocation\nconst _wsPoint = new Vector3d();\n// scratch reused by the orientation-basis accessors (getBasis / getRight / …),\n// to avoid per-call allocation on the billboard draw path.\nconst _basis = new Matrix3d();\nconst _bScratchA = new Vector3d();\nconst _bScratchB = new Vector3d();\n\n/**\n * A perspective camera that extends {@link Camera2d} with a view\n * {@link Frustum} (fov / aspect / near / far) and orientation\n * (pitch / yaw / roll). Slots into `Stage.cameras` as a drop-in\n * replacement for `Camera2d` — inherits the post-effect FBO bracket,\n * color-matrix, fade / shake / follow plumbing, and screen viewport.\n *\n * **GPU backend required.** Camera3d's perspective projection,\n * depth-buffer painter sort and retained mesh draw path need a renderer\n * with a depth buffer (`renderer.supportsDepthBuffer` — WebGL 2 or\n * WebGPU); the Canvas backend has none of these and would render a\n * stuck blank scene. Construct the Application with\n * `renderer: video.WEBGL` or `video.WEBGPU` to make `app.init()` reject\n * when that backend is unavailable. Pairing `cameraClass: Camera3d`\n * with `video.AUTO` will emit a `console.warn` (and silently misrender)\n * when AUTO falls back to Canvas — see\n * {@link ApplicationSettings.renderer} for the contract.\n *\n * Conventions:\n * - **Y-down + +Z forward.** Sprite at higher `pos.y` appears lower\n *   on screen (same as Camera2d). Sprite at higher `pos.z` is\n *   farther from the camera and renders smaller. Matches melonJS's\n *   2D conventions so existing Camera2d code translates directly.\n * - **Rotations are extrinsic XYZ.** `pitch` (X axis, look up/down),\n *   `yaw` (Y axis, look left/right), `roll` (Z axis, screen-plane\n *   bank — also exposed as `Camera2d.rotation` via Renderable\n *   inheritance for backward compatibility).\n * - **Follow offset (PR B scope).** When a target is set,\n *   `followOffset` is applied in **world space**:\n *   `camera.pos = target.pos + followOffset`. Target-rotation-aware\n *   follow (spring-arm style, where the offset\n *   rotates with the target's orientation) is deferred until a\n *   showcase needs it (e.g. AfterBurner's banking jet).\n *\n * Known limitations (PR B scope):\n * - `Light2d` is 2D-only — visible artifacts under perspective.\n *   Avoid combining with Camera3d for now.\n * - `localToWorld` / `worldToLocal` overrides fall back to the\n *   ortho-equivalent 2D projection at z=0. Full 3D unproject for\n *   arbitrary depth is future work.\n * @category Camera\n * @example\n * // opt in app-wide:\n * const app = new Application(1024, 768, {\n *   parent: \"screen\",\n *   cameraClass: Camera3d,\n * });\n *\n * // or per-stage with custom fov:\n * class GameStage extends Stage {\n *   constructor() {\n *     super({\n *       cameras: [new Camera3d(0, 0, 1024, 768, { fov: Math.PI / 3 })],\n *     });\n *   }\n * }\n */\nexport default class Camera3d extends Camera2d {\n\t/**\n\t * Override `Camera2d.defaultSortOn` to declare `\"depth\"` as this\n\t * camera's preferred sort mode. `Application` / `Stage` apply this\n\t * to `world.sortOn` at bootstrap, so games opting into Camera3d via\n\t * `cameraClass: Camera3d` get camera-distance painter's sort for\n\t * free — the only correct sort for alpha-blended sprites under\n\t * perspective.\n\t */\n\tstatic override defaultSortOn: \"x\" | \"y\" | \"z\" | \"depth\" = \"depth\";\n\n\t/**\n\t * the view frustum (perspective parameters + projection matrix).\n\t * Mutating `frustum.fov` / `aspect` / `near` / `far` directly\n\t * requires calling `frustum.update()` to rebuild the matrix;\n\t * the proxy setters on this camera (`camera.fov = ...`) handle\n\t * that automatically.\n\t */\n\tfrustum: Frustum;\n\n\t/**\n\t * X-axis rotation in radians (look up/down). Positive values\n\t * pitch the camera up.\n\t * @default 0\n\t */\n\tpitch: number;\n\n\t/**\n\t * Y-axis rotation in radians (look left/right). Positive values\n\t * yaw the camera to the right.\n\t * @default 0\n\t */\n\tyaw: number;\n\n\t/**\n\t * World-space offset from the followed target. When `target` is\n\t * set via {@link Camera2d#follow}, the camera position resolves to\n\t * `target.pos + followOffset`. Common usage: `(0, -2, -8)` for a\n\t * behind-and-above third-person view.\n\t *\n\t * Treated as world-space in this release — target-rotation-aware\n\t * follow (where the offset rotates with the target's orientation,\n\t * spring-arm style) is deferred until a\n\t * showcase needs it (e.g. AfterBurner's banking jet).\n\t * @default (0, 0, 0)\n\t */\n\tfollowOffset: Vector3d;\n\n\t/**\n\t * Reserved for future follow-look-ahead support — currently unused\n\t * by `updateTarget`. The intent is: when wired in, the camera will\n\t * look at `target.pos + lookAhead` instead of `target.pos`, so a\n\t * follow-cam stays slightly ahead of its target (e.g. for a\n\t * cinematic forward-looking shot in AfterBurner). Field is exposed\n\t * now so user code can set it without waiting for the wiring.\n\t * @default (0, 0, 1)\n\t */\n\tlookAhead: Vector3d;\n\n\t/**\n\t * @param minX - start x offset\n\t * @param minY - start y offset\n\t * @param maxX - end x offset\n\t * @param maxY - end y offset\n\t * @param [opts] - perspective parameters (see {@link FrustumOptions})\n\t */\n\tconstructor(\n\t\tminX: number,\n\t\tminY: number,\n\t\tmaxX: number,\n\t\tmaxY: number,\n\t\topts?: FrustumOptions,\n\t) {\n\t\tsuper(minX, minY, maxX, maxY);\n\n\t\t// build the frustum with the user's opts, defaulting aspect to\n\t\t// the camera viewport rect\n\t\tthis.frustum = new Frustum({\n\t\t\tfov: opts?.fov ?? Math.PI / 3,\n\t\t\taspect: opts?.aspect ?? this.width / this.height,\n\t\t\tnear: opts?.near ?? 0.1,\n\t\t\tfar: opts?.far ?? 1000,\n\t\t});\n\n\t\tthis.pitch = 0;\n\t\tthis.yaw = 0;\n\t\tthis.followOffset = new Vector3d(0, 0, 0);\n\t\tthis.lookAhead = new Vector3d(0, 0, 1);\n\n\t\t// override Camera2d's wide ortho range — perspective wants\n\t\t// tight near/far for meaningful z resolution\n\t\tthis.near = this.frustum.near;\n\t\tthis.far = this.frustum.far;\n\n\t\t// copy the frustum's already-built perspective matrix over\n\t\t// Camera2d's ortho (left by the super-constructor's call to\n\t\t// `_updateProjectionMatrix`). We do NOT call our overridden\n\t\t// `_updateProjectionMatrix` here, because that would re-derive\n\t\t// `aspect` from the viewport rect and overwrite any custom\n\t\t// aspect the user passed in `opts`. Auto-derivation is the\n\t\t// right behavior on `resize()` — but at construction time,\n\t\t// the user's explicit `opts.aspect` should win.\n\t\tthis.projectionMatrix.copy(this.frustum.projectionMatrix);\n\t}\n\n\t/**\n\t * vertical field of view in radians. Setting this rebuilds the\n\t * projection matrix. Proxies to `frustum.fov`.\n\t */\n\tget fov(): number {\n\t\treturn this.frustum.fov;\n\t}\n\tset fov(value: number) {\n\t\tthis.frustum.fov = value;\n\t\tthis.frustum.update();\n\t\tthis.projectionMatrix.copy(this.frustum.projectionMatrix);\n\t}\n\n\t/**\n\t * aspect ratio (width / height). Auto-updated on `resize()`.\n\t * Setting manually overrides the auto-derived value until the\n\t * next resize. Proxies to `frustum.aspect`.\n\t */\n\tget aspect(): number {\n\t\treturn this.frustum.aspect;\n\t}\n\tset aspect(value: number) {\n\t\tthis.frustum.aspect = value;\n\t\tthis.frustum.update();\n\t\tthis.projectionMatrix.copy(this.frustum.projectionMatrix);\n\t}\n\n\t/**\n\t * Update the perspective near/far clip distances and rebuild the\n\t * projection matrix in one shot. Anything closer than `near` or\n\t * farther than `far` is clipped by the GPU; projection math also\n\t * degrades sharply just before `far`, so size the far plane to the\n\t * deepest object in your scene with a little headroom. Defaults are\n\t * `near = 0.1`, `far = 1000` — typical AfterBurner-class scenes\n\t * with enemies spawning at z = 3000+ need to push `far` out.\n\t *\n\t * **This is the supported way to change near/far at runtime.** The\n\t * inherited `Camera2d.near` / `.far` are plain instance fields —\n\t * direct assignment (`camera.near = 5`) updates the cached value\n\t * but leaves the projection matrix stale until the next\n\t * `resize()`. TypeScript's property-vs-accessor rule prevents\n\t * shadowing the inherited fields with accessor pairs, so the\n\t * convenience method is the public contract instead.\n\t * @param near - near clip distance\n\t * @param far - far clip distance\n\t * @returns this camera (chainable)\n\t */\n\tsetClipPlanes(near: number, far: number): this {\n\t\tthis.near = near;\n\t\tthis.far = far;\n\t\tthis.frustum.near = near;\n\t\tthis.frustum.far = far;\n\t\tthis.frustum.update();\n\t\tthis.projectionMatrix.copy(this.frustum.projectionMatrix);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Write the camera's world-space orientation basis into the given vectors:\n\t * `right` (camera local +X), `up` (+Y), and `forward` (+Z — the direction the\n\t * camera looks). Derived from `yaw` / `pitch` (the inverse of the view\n\t * rotation), so they update as the camera turns. Handy for orienting\n\t * camera-facing geometry — e.g. {@link Sprite3d} billboards.\n\t * @param right - receives the right axis (unit)\n\t * @param up - receives the up axis (unit)\n\t * @param forward - receives the forward / look axis (unit)\n\t * @returns this camera, for chaining\n\t */\n\tgetBasis(right: Vector3d, up: Vector3d, forward: Vector3d): this {\n\t\t// camera world orientation R = inverse of the view rotation. The view is\n\t\t// R(-pitch, X) ∘ R(-yaw, Y) (see _applyContainerViewTransform), so\n\t\t// R = R(yaw, Y) ∘ R(pitch, X); the columns of R (column-major `val`) are\n\t\t// the camera's right / up / forward axes in world space.\n\t\t_basis.identity();\n\t\t_basis.rotate(this.yaw, AXIS_Y);\n\t\t_basis.rotate(this.pitch, AXIS_X);\n\t\tconst v = _basis.val;\n\t\tright.set(v[0], v[1], v[2]);\n\t\tup.set(v[4], v[5], v[6]);\n\t\tforward.set(v[8], v[9], v[10]);\n\t\treturn this;\n\t}\n\n\t/**\n\t * The camera's world-space right axis (unit). See {@link Camera3d#getBasis}.\n\t * @param out - vector to write into (returned)\n\t * @returns `out`\n\t */\n\tgetRight(out: Vector3d): Vector3d {\n\t\tthis.getBasis(out, _bScratchA, _bScratchB);\n\t\treturn out;\n\t}\n\n\t/**\n\t * The camera's world-space up axis (unit). See {@link Camera3d#getBasis}.\n\t * @param out - vector to write into (returned)\n\t * @returns `out`\n\t */\n\tgetUp(out: Vector3d): Vector3d {\n\t\tthis.getBasis(_bScratchA, out, _bScratchB);\n\t\treturn out;\n\t}\n\n\t/**\n\t * The camera's world-space forward / look axis (unit). See\n\t * {@link Camera3d#getBasis}.\n\t * @param out - vector to write into (returned)\n\t * @returns `out`\n\t */\n\tgetForward(out: Vector3d): Vector3d {\n\t\tthis.getBasis(_bScratchA, _bScratchB, out);\n\t\treturn out;\n\t}\n\n\t/**\n\t * Rebuild the projection matrix from the frustum. Called by the\n\t * base `Camera2d` constructor and by `resize()`. Camera3d's\n\t * version replaces the ortho matrix with the frustum's perspective.\n\t * @ignore\n\t */\n\toverride _updateProjectionMatrix(): void {\n\t\t// guard: this is called from the Camera2d super-constructor\n\t\t// before our `frustum` field is initialized. Fall through to\n\t\t// Camera2d's ortho path in that case; the Camera3d constructor\n\t\t// copies the just-built frustum matrix into `projectionMatrix`\n\t\t// directly (without re-entering this method — that would\n\t\t// overwrite any user-supplied `opts.aspect`). TypeScript can't\n\t\t// model \"this method runs during super-construction\" so the\n\t\t// type system sees `this.frustum` as always-defined.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n\t\tif (!this.frustum) {\n\t\t\tsuper._updateProjectionMatrix();\n\t\t\treturn;\n\t\t}\n\t\tthis.frustum.aspect = this.width / this.height;\n\t\tthis.frustum.near = this.near;\n\t\tthis.frustum.far = this.far;\n\t\tthis.frustum.update();\n\t\tthis.projectionMatrix.copy(this.frustum.projectionMatrix);\n\t\t// `screenProjection` stays a flat screen-space ortho so floating\n\t\t// renderables (HUDs, Text overlays) can swap to it during draw\n\t\t// instead of going through the perspective projection (which\n\t\t// would `w=0`-divide and NaN their projected positions).\n\t\t// Container.draw consults this for every floating child under\n\t\t// any camera, default or not.\n\t\t//\n\t\t// The ortho z range is wide and centered on 0 (NOT the\n\t\t// perspective `near`/`far`) because:\n\t\t// - The perspective frustum has `near = 0.1`, so a floating\n\t\t//   renderable left at the default `depth = 0` would sit in\n\t\t//   front of the near plane and get clipped.\n\t\t// - Floating UI doesn't need depth precision — z just has to\n\t\t//   be inside the ortho range. `[-1e6, +1e6]` matches Camera2d's\n\t\t//   own `near`/`far` defaults and gives the same \"z doesn't\n\t\t//   matter\" behavior 2D code already relies on.\n\t\tthis.screenProjection.ortho(0, this.width, this.height, 0, -1e6, 1e6);\n\t}\n\n\t/**\n\t * Override Camera2d's non-default projection setup so that a Camera3d\n\t * used in split-screen / picture-in-picture still renders the world\n\t * with perspective instead of falling back to a 2D ortho. The\n\t * camera's perspective `projectionMatrix` is mirrored into\n\t * `worldProjection`; the surrounding `clipRect` set by the base\n\t * `draw()` handles confining the draw region to the camera's\n\t * sub-screen rect, and the frustum's `aspect` is already in sync\n\t * with the camera's width/height. `screenProjection` is left alone\n\t * because it was already set up correctly in\n\t * {@link Camera3d#_updateProjectionMatrix}.\n\t * @ignore\n\t */\n\toverride _setupNonDefaultProjection(renderer: Renderer): void {\n\t\tthis.worldProjection.copy(this.projectionMatrix);\n\t\trenderer.setProjection(this.worldProjection);\n\t\t// Confine the perspective NDC `[-1, +1]` to the camera's\n\t\t// sub-rect via the GL viewport. Without this, the perspective\n\t\t// matrix maps to the FULL canvas and `clipRect` then crops\n\t\t// to the sub-rect — producing a cropped slice of the\n\t\t// full-screen view rather than a properly-remapped sub-camera\n\t\t// (PR #1464 review). WebGL's `gl.viewport` uses bottom-left\n\t\t// origin, so the canvas-top-left `screenY` is flipped via\n\t\t// `renderer.height - screenY - height`. Canvas renderer's\n\t\t// inherited no-op `setViewport` swallows this safely.\n\t\trenderer.setViewport(\n\t\t\tthis.screenX,\n\t\t\trenderer.height - this.screenY - this.height,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t);\n\t}\n\n\t/**\n\t * Resize the camera viewport and recompute aspect ratio.\n\t * @param w - new width\n\t * @param h - new height\n\t * @returns this camera\n\t */\n\toverride resize(w: number, h: number): this {\n\t\tsuper.resize(w, h);\n\t\t// super.resize calls _updateProjectionMatrix which already\n\t\t// re-derives aspect — nothing more to do\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply the camera's full 3D view transform to the world container.\n\t * Order: rotate first (pitch, yaw), then translate by `-camera.pos`.\n\t * Post-multiplication semantics give us\n\t * `currentTransform = R⁻¹ ∘ T(-pos)` — applied to a world point,\n\t * this subtracts the camera position then rotates by the camera's\n\t * inverse orientation, which is the standard view transform.\n\t * @ignore\n\t */\n\toverride _applyContainerViewTransform(\n\t\tcontainer: Container,\n\t\ttranslateX: number,\n\t\ttranslateY: number,\n\t): void {\n\t\t// Build the view matrix R⁻¹ ∘ T(-cam.pos) via the container's\n\t\t// `currentTransform` using post-multiplication semantics.\n\t\t//\n\t\t// `Renderable.translate` / `.rotate` post-multiply: each call\n\t\t// adds `currentTransform = currentTransform × M`. When this\n\t\t// matrix is later applied to a world vertex P, the result is\n\t\t// `currentTransform × P` — the rightmost matrix in the chain\n\t\t// acts on P first.\n\t\t//\n\t\t// We want the view transform to first subtract the camera\n\t\t// position (so vertices are camera-relative), then rotate by\n\t\t// the camera's inverse orientation. To achieve\n\t\t// `R(-pitch) ∘ R(-yaw) ∘ T(-pos)` as the final matrix, we\n\t\t// post-multiply in that same left-to-right order:\n\t\t//   1. rotate(-pitch, X)  → currentTransform = R(-pitch)\n\t\t//   2. rotate(-yaw,   Y)  → currentTransform = R(-pitch) ∘ R(-yaw)\n\t\t//   3. translate(-pos)    → currentTransform = R(-pitch) ∘ R(-yaw) ∘ T(-pos)\n\t\tif (this.pitch !== 0) {\n\t\t\tcontainer.rotate(-this.pitch, AXIS_X);\n\t\t}\n\t\tif (this.yaw !== 0) {\n\t\t\tcontainer.rotate(-this.yaw, AXIS_Y);\n\t\t}\n\t\tcontainer.translate(-translateX, -translateY, -this.depth);\n\n\t\t// Refresh the container's painter's-algorithm order from the\n\t\t// current camera position — but ONLY when the container is on\n\t\t// the camera-distance sort that actually depends on the camera\n\t\t// moving. For `\"x\"`/`\"y\"`/`\"z\"` sorts the comparator is a pure\n\t\t// function of `pos`, so the container's normal \"re-sort on\n\t\t// child mutation\" lifecycle is sufficient and a per-camera\n\t\t// `sortNow` would be wasted O(N log N) work each frame. Only\n\t\t// `\"depth\"` keys off `(child.pos − camera.pos)²`, which DOES\n\t\t// shift when the camera moves between two frames where no\n\t\t// child mutated.\n\t\tif (container.sortOn === \"depth\") {\n\t\t\tcontainer.sortNow(true);\n\t\t}\n\t}\n\n\t/**\n\t * Revert {@link Camera3d#_applyContainerViewTransform} in reverse\n\t * order to restore the container's `currentTransform` to its\n\t * pre-camera state.\n\t * @ignore\n\t */\n\toverride _revertContainerViewTransform(\n\t\tcontainer: Container,\n\t\ttranslateX: number,\n\t\ttranslateY: number,\n\t): void {\n\t\t// reverse of apply: undo translate first, then yaw, then pitch\n\t\tcontainer.translate(translateX, translateY, this.depth);\n\t\tif (this.yaw !== 0) {\n\t\t\tcontainer.rotate(this.yaw, AXIS_Y);\n\t\t}\n\t\tif (this.pitch !== 0) {\n\t\t\tcontainer.rotate(this.pitch, AXIS_X);\n\t\t}\n\t}\n\n\t/**\n\t * Point the camera at a world-space target by deriving pitch and\n\t * yaw from the direction (target − camera.pos). Roll is unaffected.\n\t *\n\t * Three call shapes:\n\t * - `lookAt(x, y, z)` — raw world coordinates\n\t * - `lookAt(vector3d)` — a 3D point\n\t * - `lookAt(renderable)` — uses `renderable.pos` (matches the\n\t *   `Renderable.lookAt(target)` signature so Camera3d is a structural\n\t *   drop-in replacement for Camera2d / Renderable in user code).\n\t *\n\t * Last-write-wins with manual `pitch` / `yaw` assignment: if you\n\t * call `lookAt(...)` then set `camera.pitch = 0.1` directly, the\n\t * next frame renders with the manual pitch.\n\t * @param xOrTarget - target world x, or a target with `pos` / `x`,`y`,`z`\n\t * @param y - target world y (only when first arg is a number)\n\t * @param z - target world z (only when first arg is a number)\n\t * @returns this camera\n\t */\n\toverride lookAt(\n\t\txOrTarget:\n\t\t\t| number\n\t\t\t| {\n\t\t\t\t\tx: number;\n\t\t\t\t\ty: number;\n\t\t\t\t\tz?: number;\n\t\t\t\t\tpos?: ObservableVector3d;\n\t\t\t  },\n\t\ty?: number,\n\t\tz?: number,\n\t): this {\n\t\tlet tx: number;\n\t\tlet ty: number;\n\t\tlet tz: number;\n\t\tif (typeof xOrTarget === \"number\") {\n\t\t\ttx = xOrTarget;\n\t\t\tty = y ?? 0;\n\t\t\ttz = z ?? 0;\n\t\t} else if (xOrTarget.pos) {\n\t\t\ttx = xOrTarget.pos.x;\n\t\t\tty = xOrTarget.pos.y;\n\t\t\ttz = xOrTarget.pos.z;\n\t\t} else {\n\t\t\ttx = xOrTarget.x;\n\t\t\tty = xOrTarget.y;\n\t\t\ttz = xOrTarget.z ?? 0;\n\t\t}\n\t\tconst dx = tx - this.pos.x;\n\t\tconst dy = ty - this.pos.y;\n\t\tconst dz = tz - this.depth;\n\n\t\t// yaw = atan2(dx, dz) — rotation around Y axis to face the\n\t\t// XZ-plane projection of the direction vector\n\t\tthis.yaw = Math.atan2(dx, dz);\n\n\t\t// Negate `dy` so the result matches the camera's sign\n\t\t// convention (positive pitch = camera looks up — see the\n\t\t// `pitch` field doc). Under Y-down, a target with positive\n\t\t// `dy` sits BELOW the camera; the camera should look DOWN,\n\t\t// which is a NEGATIVE pitch — exactly what `atan2(-dy, …)`\n\t\t// produces.\n\t\tconst horizontalDist = Math.sqrt(dx * dx + dz * dz);\n\t\tthis.pitch = Math.atan2(-dy, horizontalDist);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Convenience overload of `lookAt` accepting a {@link Vector3d}.\n\t * @param target - world-space point to look at\n\t * @returns this camera\n\t */\n\tsetLookAt(target: Vector3d): this {\n\t\treturn this.lookAt(target.x, target.y, target.z);\n\t}\n\n\t/**\n\t * Set the target-local follow offset. Called once when configuring\n\t * a follow-cam (e.g. behind-and-above third person:\n\t * `setFollowOffset(0, -2, -8)`).\n\t * @param x - target-local x offset\n\t * @param y - target-local y offset\n\t * @param z - target-local z offset\n\t * @returns this camera\n\t */\n\tsetFollowOffset(x: number, y: number, z: number): this {\n\t\tthis.followOffset.set(x, y, z);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Override Camera2d's 2D follow logic to additionally resolve\n\t * `followOffset` against the target's z. When `target` is set, the\n\t * camera's world position becomes `target.pos + followOffset`.\n\t *\n\t * **Semantic change vs Camera2d.follow:** this override **does not\n\t * honor `follow_axis`, `deadzone`, or `smoothFollow` / `damping`**.\n\t * Camera3d tracks its target exactly each frame because the typical\n\t * 3D use case (behind-the-plane follow-cam, third-person orbit) wants\n\t * 1:1 tracking with no scroll-deadzone. If you need damped or\n\t * axis-constrained follow under perspective, set `target = null` and\n\t * lerp `camera.pos` toward the target manually in your `update()`.\n\t *\n\t * **PR B scope:** `followOffset` is treated as **world-space**.\n\t * Target-rotation-aware follow (where the offset rotates with the\n\t * target's orientation, spring-arm style) lands when a\n\t * showcase (AfterBurner's banking jet) demands it.\n\t * @param dt - delta time in milliseconds (ignored — no damping)\n\t * @ignore\n\t */\n\toverride updateTarget(dt?: number): void {\n\t\tconst target = this.target;\n\t\tif (target) {\n\t\t\t// duck-type the z read via `'z' in target` so this works\n\t\t\t// for both `Vector3d` (when the user passed a raw vector to\n\t\t\t// `follow()`) and `ObservableVector3d` (when\n\t\t\t// `follow(renderable)` assigned `renderable.pos`, which is\n\t\t\t// observable not plain). The previous `instanceof Vector3d`\n\t\t\t// check missed the observable variant — Renderable targets\n\t\t\t// silently lost their depth.\n\t\t\tconst targetZ =\n\t\t\t\t\"z\" in target && typeof target.z === \"number\" ? target.z : 0;\n\t\t\t// Direct .x/.y assignment + `this.depth` (which proxies to\n\t\t\t// pos.z) avoids the `as unknown as Pos3d` cast — Renderable\n\t\t\t// already exposes `depth` as the proper z accessor.\n\t\t\tthis.pos.x = target.x + this.followOffset.x;\n\t\t\tthis.pos.y = target.y + this.followOffset.y;\n\t\t\tthis.depth = targetZ + this.followOffset.z;\n\t\t\tthis.isDirty = true;\n\t\t\treturn;\n\t\t}\n\t\t// no target — fall through to Camera2d's behavior (no-op when\n\t\t// target is null)\n\t\tsuper.updateTarget(dt);\n\t}\n\n\t/**\n\t * Visibility check used by `Container.update` (in turn driving\n\t * `Container.draw`) to skip rendering off-screen children.\n\t *\n\t * Camera2d's implementation tests a 2D bounds-rectangle overlap\n\t * against `this.worldView` — that test is invalid under perspective:\n\t * the visible region is a frustum that widens with distance and\n\t * rotates with the camera's pitch / yaw, not a fixed axis-aligned\n\t * rect at the camera's x / y. Camera3d substitutes plane-based\n\t * frustum culling — each non-floating renderable's bounding sphere\n\t * is tested against the six frustum planes that were extracted in\n\t * the most recent `update()` call. Floating elements (HUD / UI)\n\t * still use Camera2d's 2D rect test because their bounds are\n\t * screen-space and the perspective transform doesn't apply to them.\n\t * @param obj - the renderable to test\n\t * @param [floating] - test against screen coordinates instead of frustum\n\t * @returns true if the renderable's bounds overlap the frustum\n\t */\n\toverride isVisible(\n\t\tobj: Renderable,\n\t\tfloating: boolean = obj.floating,\n\t): boolean {\n\t\tif (floating || obj.floating) {\n\t\t\treturn super.isVisible(obj, floating);\n\t\t}\n\t\t// Use the renderable's WORLD position for the frustum-sphere\n\t\t// test, not `bounds.centerX/Y`. The bounds rect is computed\n\t\t// with `addFrame()` and can stay in local coords when the\n\t\t// renderable is nested inside a Container (e.g. Particle inside\n\t\t// ParticleEmitter) — testing local coords against a world-\n\t\t// space frustum mis-culls nested children even when their actual\n\t\t// world position is inside the view.\n\t\t// `getAbsolutePosition` walks the ancestor chain summing\n\t\t// parent x/y AND z; previously we read `obj.depth` (local\n\t\t// `pos.z`) here, which silently mis-culled children of any\n\t\t// container whose own depth was non-zero.\n\t\tconst bounds = obj.getBounds();\n\t\t// A grouping container with no intrinsic size has infinite / cleared\n\t\t// bounds (left=+∞, right=-∞). Its width/height are non-finite, so the\n\t\t// radius below would be NaN and `intersectsSphere` would silently report\n\t\t// it (and its whole subtree) invisible — skipping both its draw AND its\n\t\t// update. Such a container can't be frustum-culled meaningfully, so treat\n\t\t// it as always visible and let its children be culled individually\n\t\t// (matching Camera2d, which special-cases the same sentinel). This is\n\t\t// what keeps e.g. a GLTFModel rig (meshes nested under a sizeless\n\t\t// container) rendering under a 3D camera.\n\t\tif (!bounds.isFinite()) {\n\t\t\treturn true;\n\t\t}\n\t\t// Half-diagonal — the conservative bounding-sphere radius for\n\t\t// a rectangular bounds rect. `max(w, h) * 0.5` is the\n\t\t// inradius and can mark a renderable invisible while one of\n\t\t// its corners is still on-screen near a frustum edge; the\n\t\t// circumradius √(w² + h²) / 2 always encloses every corner.\n\t\tconst radius =\n\t\t\tMath.sqrt(bounds.width * bounds.width + bounds.height * bounds.height) *\n\t\t\t0.5;\n\t\tconst absPos = obj.getAbsolutePosition();\n\t\treturn this.frustum.intersectsSphere(absPos.x, absPos.y, absPos.z, radius);\n\t}\n\n\t/**\n\t * Bulk frustum cull via the world's {@link Octree}. Returns every\n\t * renderable whose octant the current frustum overlaps —\n\t * conservative (some renderables may still narrow-cull out\n\t * via {@link Camera3d#isVisible}'s per-sphere test) but\n\t * O(visible + walk) instead of O(scene).\n\t *\n\t * Only applicable under `cameraClass: Camera3d` (or any setup\n\t * where `world.sortOn === \"depth\"` and the broadphase is an\n\t * Octree). Returns an empty array under a 2D broadphase — call\n\t * sites can guard on the array length or branch on\n\t * `world.sortOn`.\n\t *\n\t * For a 1000-renderable scene with ~50 visible, expect a 5-20×\n\t * speedup over walking every renderable and per-item\n\t * {@link Camera3d#isVisible}.\n\t * @param world - the world to cull (its broadphase must be an Octree); typed structurally to sidestep the Camera3d → World import cycle\n\t * @param world.broadphase - the world's spatial broadphase\n\t * @param world.sortOn - guard: returns empty unless this equals `\"depth\"`\n\t * @param [out] - caller-supplied result array (re-entrancy-safe)\n\t * @returns visible-renderable candidates\n\t * @example\n\t * const visible = camera.queryVisible(app.world);\n\t * for (const r of visible) {\n\t *   // narrow-phase per-renderable visibility (sphere / OBB) if needed\n\t *   if (camera.isVisible(r)) r.draw(renderer);\n\t * }\n\t */\n\tqueryVisible(\n\t\tworld: { broadphase: unknown; sortOn: string },\n\t\tout?: Renderable[],\n\t): Renderable[] {\n\t\tconst result = out ?? [];\n\t\tresult.length = 0;\n\t\tif (world.sortOn !== \"depth\") {\n\t\t\treturn result;\n\t\t}\n\t\t// Duck-typed: the Octree exposes `queryFrustum`; the QuadTree\n\t\t// doesn't. We don't import the class here to avoid the cycle.\n\t\tconst broadphase = world.broadphase as {\n\t\t\tqueryFrustum?: (\n\t\t\t\tplanes: Frustum[\"planes\"],\n\t\t\t\tout?: Renderable[],\n\t\t\t) => Renderable[];\n\t\t};\n\t\tbroadphase.queryFrustum?.(this.frustum.planes, result);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Per-frame update — extends Camera2d's behavior (target follow,\n\t * camera effects) with rebuilding the frustum's six bounding\n\t * planes so {@link Camera3d#isVisible} returns accurate results\n\t * for the current camera state.\n\t * @param dt - delta time in milliseconds\n\t * @returns true if the camera's state changed\n\t * @ignore\n\t */\n\toverride update(dt?: number): boolean {\n\t\tconst dirty = super.update(dt);\n\t\tthis._rebuildFrustumPlanes();\n\t\treturn dirty;\n\t}\n\n\t/**\n\t * Project a world-space point to 2D screen (canvas pixel) coordinates\n\t * through this camera's view + perspective projection (perspective divide\n\t * included). The origin is top-left with **y down**, matching where\n\t * geometry at `world` rasterizes and the engine's 2D draw space — so the\n\t * result can be fed straight to the 2D draw API (HUD pinned to a 3D object,\n\t * picking, debug overlays such as the 3D bounding-box wireframe).\n\t *\n\t * **Returns `null` when the point is at or behind the camera** (clip\n\t * `w ≤ 0`) — projecting it would yield a mirrored/degenerate pixel, so\n\t * callers (e.g. a debug wireframe) can skip it cleanly instead of drawing\n\t * garbage. Otherwise returns the screen-space pixel coordinates.\n\t * @param world - the world-space point to project\n\t * @param [out] - optional Vector2d to receive the result (allocated if omitted)\n\t * @returns the screen-space pixel coordinates, or `null` if behind the camera\n\t */\n\tworldToScreen(\n\t\tworld: Vector3d,\n\t\tout: Vector2d = new Vector2d(),\n\t): Vector2d | null {\n\t\t// projection × view — built exactly like `_rebuildFrustumPlanes`:\n\t\t// rotate (pitch then yaw), translate by -pos, then pre-multiply by the\n\t\t// frustum projection.\n\t\t_viewMatrix.identity();\n\t\tif (this.pitch !== 0) {\n\t\t\t_viewMatrix.rotate(-this.pitch, AXIS_X);\n\t\t}\n\t\tif (this.yaw !== 0) {\n\t\t\t_viewMatrix.rotate(-this.yaw, AXIS_Y);\n\t\t}\n\t\t_viewMatrix.translate(-this.pos.x, -this.pos.y, -this.depth);\n\t\t_viewProjection.copy(this.frustum.projectionMatrix);\n\t\t_viewProjection.multiply(_viewMatrix);\n\n\t\t// clip-space w (column-major): reject points at/behind the camera before\n\t\t// the perspective divide would mirror them.\n\t\tconst m = _viewProjection.val;\n\t\tconst w = m[3] * world.x + m[7] * world.y + m[11] * world.z + m[15];\n\t\tif (w <= 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// `Matrix3d.apply` divides by the clip-space w → normalized device\n\t\t// coordinates in [-1, 1].\n\t\t_wsPoint.set(world.x, world.y, world.z);\n\t\t_viewProjection.apply(_wsPoint);\n\n\t\t// NDC → screen pixels. NDC +y points up, screen +y points down, so the\n\t\t// y axis is flipped.\n\t\tout.set(\n\t\t\t(_wsPoint.x * 0.5 + 0.5) * this.width,\n\t\t\t(1 - (_wsPoint.y * 0.5 + 0.5)) * this.height,\n\t\t);\n\t\treturn out;\n\t}\n\n\t/**\n\t * Recompute the frustum's six bounding planes from the current\n\t * `projectionMatrix × viewMatrix` (the world → clip matrix).\n\t * Called from {@link Camera3d#update} each frame; `isVisible`\n\t * then tests against the cached planes.\n\t * @ignore\n\t */\n\t_rebuildFrustumPlanes(): void {\n\t\t// build the view matrix R⁻¹ ∘ T(-pos) the same way\n\t\t// `_applyContainerViewTransform` builds it on the container —\n\t\t// rotate first (pitch then yaw), then translate.\n\t\t_viewMatrix.identity();\n\t\tif (this.pitch !== 0) {\n\t\t\t_viewMatrix.rotate(-this.pitch, AXIS_X);\n\t\t}\n\t\tif (this.yaw !== 0) {\n\t\t\t_viewMatrix.rotate(-this.yaw, AXIS_Y);\n\t\t}\n\t\t_viewMatrix.translate(-this.pos.x, -this.pos.y, -this.depth);\n\n\t\t// projectionMatrix × viewMatrix — the matrix that maps world\n\t\t// coords to clip space (column-major / gl-matrix convention),\n\t\t// which `Frustum.setFromViewProjection` decomposes into the\n\t\t// six bounding planes via Gribb-Hartmann extraction.\n\t\t_viewProjection.copy(this.frustum.projectionMatrix);\n\t\t_viewProjection.multiply(_viewMatrix);\n\n\t\tthis.frustum.setFromViewProjection(_viewProjection);\n\t}\n}\n","import type { Matrix3d } from \"../../math/matrix3d.ts\";\n\n/**\n * The per-instance record shared by both GPU backends (#1508).\n *\n * An instance carries a **row-major 3×4 affine transform** — three `vec4`\n * rows — rather than a full 4×4: the bottom row of an affine matrix is\n * always `(0, 0, 0, 1)`, so storing it would waste 16 bytes and a fourth\n * attribute slot per instance. Two further slots are opt-in, declared once\n * per instanced mesh:\n *\n * The transform occupies 12 floats (48 bytes); a colour slot adds 4 more\n * (16 bytes) when `instanceColors` is declared, and a custom-data slot\n * another 4 when `instanceData` is.\n *\n * Colour is four floats rather than four packed bytes for the same reason\n * `aColor` is on the mesh vertex layout: a packed 4-byte slot can form a\n * NaN bit pattern that Apple's Metal-backed driver canonicalizes on upload,\n * zeroing the bytes the shader reads.\n *\n * The custom slot is deliberately **opaque** — the stock lit shader reads\n * its `rgb` as emissive, while a custom mesh shader is free to read it as a\n * wind phase, an atlas offset or a random seed.\n * @ignore\n */\n\n/**\n * floats occupied by the 3×4 transform\n * @ignore\n */\nexport const TRANSFORM_FLOATS = 12;\n\n/**\n * floats occupied by an optional `vec4` slot\n * @ignore\n */\nexport const SLOT_FLOATS = 4;\n\n/**\n * Describes one instanced mesh's record: how wide it is and where each\n * optional slot begins. Offsets are in floats; multiply by 4 for bytes.\n * @ignore\n */\nexport interface InstanceRecordLayout {\n\t/** floats per instance */\n\tfloats: number;\n\t/** bytes per instance (the vertex-buffer `arrayStride`) */\n\tstride: number;\n\t/** float offset of the colour slot, or -1 when absent */\n\tcolorOffset: number;\n\t/** float offset of the custom-data slot, or -1 when absent */\n\tdataOffset: number;\n\t/** whether the colour slot is present */\n\thasColor: boolean;\n\t/** whether the custom-data slot is present */\n\thasData: boolean;\n}\n\n/**\n * Build the record layout for the given opt-in slots.\n * @param hasColor - whether a per-instance colour slot is present\n * @param hasData - whether a per-instance custom `vec4` slot is present\n * @returns the resolved layout\n * @ignore\n */\nexport function instanceRecordLayout(\n\thasColor: boolean,\n\thasData: boolean,\n): InstanceRecordLayout {\n\tlet floats = TRANSFORM_FLOATS;\n\tconst colorOffset = hasColor ? floats : -1;\n\tif (hasColor) {\n\t\tfloats += SLOT_FLOATS;\n\t}\n\tconst dataOffset = hasData ? floats : -1;\n\tif (hasData) {\n\t\tfloats += SLOT_FLOATS;\n\t}\n\treturn {\n\t\tfloats,\n\t\tstride: floats * Float32Array.BYTES_PER_ELEMENT,\n\t\tcolorOffset,\n\t\tdataOffset,\n\t\thasColor,\n\t\thasData,\n\t};\n}\n\n/**\n * Write a matrix into an instance record as three row-major `vec4` rows.\n *\n * `Matrix3d` stores its values **column-major** (the GL convention —\n * translation lives at `val[12..14]`), so this transposes the upper 3×4 on\n * the way in. The shader transposes back when it rebuilds a `mat4`.\n * @param target - the CPU-side instance buffer\n * @param offset - float offset of this instance's record\n * @param matrix - the transform to write\n * @ignore\n */\nexport function writeInstanceTransform(\n\ttarget: Float32Array,\n\toffset: number,\n\tmatrix: Matrix3d,\n): void {\n\tconst m = matrix.val;\n\t// row 0: basis x-components + translation x\n\ttarget[offset] = m[0];\n\ttarget[offset + 1] = m[4];\n\ttarget[offset + 2] = m[8];\n\ttarget[offset + 3] = m[12];\n\t// row 1\n\ttarget[offset + 4] = m[1];\n\ttarget[offset + 5] = m[5];\n\ttarget[offset + 6] = m[9];\n\ttarget[offset + 7] = m[13];\n\t// row 2\n\ttarget[offset + 8] = m[2];\n\ttarget[offset + 9] = m[6];\n\ttarget[offset + 10] = m[10];\n\ttarget[offset + 11] = m[14];\n}\n\n/**\n * Write a translation / rotation / scale triple into an instance record.\n *\n * This is the shape authored instancing arrives in: glTF's\n * `EXT_mesh_gpu_instancing` stores per-instance `TRANSLATION`, `ROTATION`\n * (a quaternion) and `SCALE` accessors rather than matrices. Composing them\n * straight into the record avoids building a `Matrix3d` per instance —\n * a forest of five thousand trees would otherwise allocate five thousand\n * throwaway matrices at load.\n *\n * The rotation is applied first, then scale, then translation (glTF's\n * `T * R * S` convention), and the result is transposed into the record's\n * row-major rows.\n * @param target - the CPU-side instance buffer\n * @param offset - float offset of this instance's record\n * @param tx - translation x\n * @param ty - translation y\n * @param tz - translation z\n * @param qx - rotation quaternion x\n * @param qy - rotation quaternion y\n * @param qz - rotation quaternion z\n * @param qw - rotation quaternion w\n * @param sx - scale x\n * @param sy - scale y\n * @param sz - scale z\n * @ignore\n */\nexport function writeInstanceTRS(\n\ttarget: Float32Array,\n\toffset: number,\n\ttx: number,\n\tty: number,\n\ttz: number,\n\tqx: number,\n\tqy: number,\n\tqz: number,\n\tqw: number,\n\tsx: number,\n\tsy: number,\n\tsz: number,\n): void {\n\t// quaternion → 3×3 basis (columns), then each column scaled\n\tconst x2 = qx + qx;\n\tconst y2 = qy + qy;\n\tconst z2 = qz + qz;\n\tconst xx = qx * x2;\n\tconst xy = qx * y2;\n\tconst xz = qx * z2;\n\tconst yy = qy * y2;\n\tconst yz = qy * z2;\n\tconst zz = qz * z2;\n\tconst wx = qw * x2;\n\tconst wy = qw * y2;\n\tconst wz = qw * z2;\n\n\t// row 0 = (basis[0][0], basis[1][0], basis[2][0], tx)\n\ttarget[offset] = (1 - (yy + zz)) * sx;\n\ttarget[offset + 1] = (xy - wz) * sy;\n\ttarget[offset + 2] = (xz + wy) * sz;\n\ttarget[offset + 3] = tx;\n\t// row 1\n\ttarget[offset + 4] = (xy + wz) * sx;\n\ttarget[offset + 5] = (1 - (xx + zz)) * sy;\n\ttarget[offset + 6] = (yz - wx) * sz;\n\ttarget[offset + 7] = ty;\n\t// row 2\n\ttarget[offset + 8] = (xz - wy) * sx;\n\ttarget[offset + 9] = (yz + wx) * sy;\n\ttarget[offset + 10] = (1 - (xx + yy)) * sz;\n\ttarget[offset + 11] = tz;\n}\n\n/**\n * Write an identity transform into an instance record — what a freshly\n * grown slot holds until the caller places it, so an unplaced instance\n * renders at the group origin rather than collapsing onto a zero matrix.\n * @param target - the CPU-side instance buffer\n * @param offset - float offset of this instance's record\n * @ignore\n */\nexport function writeIdentityTransform(\n\ttarget: Float32Array,\n\toffset: number,\n): void {\n\ttarget[offset] = 1;\n\ttarget[offset + 1] = 0;\n\ttarget[offset + 2] = 0;\n\ttarget[offset + 3] = 0;\n\ttarget[offset + 4] = 0;\n\ttarget[offset + 5] = 1;\n\ttarget[offset + 6] = 0;\n\ttarget[offset + 7] = 0;\n\ttarget[offset + 8] = 0;\n\ttarget[offset + 9] = 0;\n\ttarget[offset + 10] = 1;\n\ttarget[offset + 11] = 0;\n}\n\n/**\n * Read an instance's transform back into a `Matrix3d`, undoing the\n * row-major packing.\n * @param source - the CPU-side instance buffer\n * @param offset - float offset of this instance's record\n * @param out - the matrix to write into\n * @returns `out`\n * @ignore\n */\nexport function readInstanceTransform(\n\tsource: Float32Array,\n\toffset: number,\n\tout: Matrix3d,\n): Matrix3d {\n\tconst m = out.val;\n\tm[0] = source[offset];\n\tm[4] = source[offset + 1];\n\tm[8] = source[offset + 2];\n\tm[12] = source[offset + 3];\n\tm[1] = source[offset + 4];\n\tm[5] = source[offset + 5];\n\tm[9] = source[offset + 6];\n\tm[13] = source[offset + 7];\n\tm[2] = source[offset + 8];\n\tm[6] = source[offset + 9];\n\tm[10] = source[offset + 10];\n\tm[14] = source[offset + 11];\n\t// the row the record does not store\n\tm[3] = 0;\n\tm[7] = 0;\n\tm[11] = 0;\n\tm[15] = 1;\n\treturn out;\n}\n\n/**\n * The attribute names an instanced shader declares, in record order. Shared\n * by both backends so the GL attribute records and the WGSL vertex layout\n * describe the same thing.\n * @ignore\n */\nexport const INSTANCE_ATTRIBUTE_NAMES = {\n\trows: [\"aInstanceRow0\", \"aInstanceRow1\", \"aInstanceRow2\"],\n\tcolor: \"aInstanceColor\",\n\tdata: \"aInstanceData\",\n} as const;\n\n/**\n * Shader locations for the instance slots, counted from the first location\n * after a tier's geometry attributes.\n *\n * These are **fixed per slot rather than sequential**, so a variant that\n * omits the colour slot does not shift the data slot down. On WebGPU that is\n * what lets one WGSL module text serve every variant — WGSL has no\n * preprocessor to renumber `@location` with — and on WebGL it keeps the two\n * backends describing the same thing.\n * @ignore\n */\nexport const INSTANCE_SLOT_OFFSETS = {\n\trows: [0, 1, 2],\n\tcolor: 3,\n\tdata: 4,\n} as const;\n\n/**\n * Backend-neutral attribute descriptors for an instance record, in the\n * `{name, format, offset}` vocabulary both backends consume.\n * @param layout - the record layout\n * @param baseLocation - first shader location for this record (the location\n * after the tier's geometry attributes). Omit on WebGL, whose attribute\n * locations come from the shader's own declaration order.\n * @returns attribute descriptors covering the whole record\n * @ignore\n */\nexport function instanceAttributes(\n\tlayout: InstanceRecordLayout,\n\tbaseLocation?: number,\n) {\n\tconst bytes = Float32Array.BYTES_PER_ELEMENT;\n\tconst at = (slot: number) => {\n\t\treturn baseLocation === undefined\n\t\t\t? undefined\n\t\t\t: { shaderLocation: baseLocation + slot };\n\t};\n\tconst attributes: {\n\t\tname: string;\n\t\tformat: string;\n\t\toffset: number;\n\t\tshaderLocation?: number;\n\t}[] = [\n\t\t{\n\t\t\tname: INSTANCE_ATTRIBUTE_NAMES.rows[0],\n\t\t\tformat: \"float32x4\",\n\t\t\toffset: 0,\n\t\t\t...at(INSTANCE_SLOT_OFFSETS.rows[0]),\n\t\t},\n\t\t{\n\t\t\tname: INSTANCE_ATTRIBUTE_NAMES.rows[1],\n\t\t\tformat: \"float32x4\",\n\t\t\toffset: 4 * bytes,\n\t\t\t...at(INSTANCE_SLOT_OFFSETS.rows[1]),\n\t\t},\n\t\t{\n\t\t\tname: INSTANCE_ATTRIBUTE_NAMES.rows[2],\n\t\t\tformat: \"float32x4\",\n\t\t\toffset: 8 * bytes,\n\t\t\t...at(INSTANCE_SLOT_OFFSETS.rows[2]),\n\t\t},\n\t];\n\tif (layout.hasColor) {\n\t\tattributes.push({\n\t\t\tname: INSTANCE_ATTRIBUTE_NAMES.color,\n\t\t\tformat: \"float32x4\",\n\t\t\toffset: layout.colorOffset * bytes,\n\t\t\t...at(INSTANCE_SLOT_OFFSETS.color),\n\t\t});\n\t}\n\tif (layout.hasData) {\n\t\tattributes.push({\n\t\t\tname: INSTANCE_ATTRIBUTE_NAMES.data,\n\t\t\tformat: \"float32x4\",\n\t\t\toffset: layout.dataOffset * bytes,\n\t\t\t...at(INSTANCE_SLOT_OFFSETS.data),\n\t\t});\n\t}\n\treturn attributes;\n}\n","/**\n * Mapping authored metallic/roughness onto the lit mesh path's Blinn-Phong\n * terms (#1575).\n *\n * Two formats describe the same material concept — glTF's\n * `pbrMetallicRoughness` and MTL's `Pr` / `Pm` extension, both of which\n * Blender writes by default — and both land here so the two cannot drift\n * apart. What they land ON is a stylized half-Lambert shading model, not a\n * microfacet BRDF, so this is deliberately an APPROXIMATION: the point is to\n * recover the \"is this surface glossy, and what colour does it glint\"\n * information the asset already carries, rather than to be energy-correct.\n *\n * An explicitly authored specular (`Ks` + `Ns`) always wins over this — it\n * says exactly what the artist wanted, where metallic/roughness only implies\n * it.\n * @ignore\n */\n\n/**\n * The exponent a mirror-smooth surface is clamped to. The Blinn-Phong\n * exponent runs away as roughness approaches 0, and a highlight narrower\n * than a pixel only aliases.\n * @ignore\n */\nexport const MAX_SHININESS = 256;\n\n/**\n * The specular reflectance of a non-metal, the standard value used by every\n * metallic/roughness workflow. Metals reflect their own base colour instead.\n * @ignore\n */\nexport const DIELECTRIC_F0 = 0.04;\n\n/**\n * The result of the mapping: a specular colour and exponent ready to hand to\n * a {@link Mesh}, or `undefined` / `0` when the material implies no\n * highlight at all.\n * @ignore\n */\nexport interface SpecularTerms {\n\t/** specular colour `[r, g, b]`, or `undefined` for none */\n\tspecular: number[] | undefined;\n\t/** specular exponent; `0` disables the term outright */\n\tshininess: number;\n}\n\n/**\n * Read a factor that should default to 1 when absent or malformed.\n *\n * Defaulting rather than propagating matters: `NaN` would fail the `> 0`\n * test below and fall into the mirror-smooth branch, turning a broken file\n * into a chrome one.\n * @param value - the authored factor\n * @returns the factor, or 1\n * @ignore\n */\nfunction factor(value: unknown): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : 1;\n}\n\n/**\n * Map a metallic/roughness pair onto a Blinn-Phong specular colour and\n * exponent.\n *\n * - **roughness → exponent** through the usual Blinn-Phong/GGX bridge,\n *   `2 / a^4 - 2` for `a = roughness^2`. A roughness of 1 — which is the\n *   glTF default, and what a matte material declares — lands on exactly 0,\n *   so a material that says nothing gets no highlight and shades exactly as\n *   it did before this existed.\n * - **metallic → tint**, interpolating from the dielectric reflectance to\n *   the material's own base colour. Chrome glints in its own hue while\n *   plaster gets a faint neutral sheen, which is the visible half of the\n *   metal/non-metal distinction.\n * @param roughness - authored roughness (0..1); absent or malformed reads as 1\n * @param metallic - authored metalness (0..1); absent or malformed reads as 1\n * @param baseColor - the material's base colour, `[r, g, b, a?]`\n * @returns the specular terms, inert when the material implies no highlight\n * @ignore\n */\nexport function specularFromMetallicRoughness(\n\troughness: unknown,\n\tmetallic: unknown,\n\tbaseColor: number[] = [1, 1, 1, 1],\n): SpecularTerms {\n\tconst r = factor(roughness);\n\tconst m = factor(metallic);\n\tconst a = r * r;\n\tconst shininess =\n\t\ta > 0 ? Math.min(MAX_SHININESS, 2 / (a * a) - 2) : MAX_SHININESS;\n\tif (!(shininess > 0)) {\n\t\treturn { specular: undefined, shininess: 0 };\n\t}\n\tconst channel = (index: number) => {\n\t\treturn DIELECTRIC_F0 + ((baseColor[index] || 0) - DIELECTRIC_F0) * m;\n\t};\n\treturn { specular: [channel(0), channel(1), channel(2)], shininess };\n}\n","import { game } from \"../application/application.ts\";\nimport Camera3d from \"../camera/camera3d.ts\";\nimport { Polygon } from \"../geometries/polygon.ts\";\nimport { getImage, getMTL, getOBJ } from \"./../loader/loader.js\";\nimport { specularFromMetallicRoughness } from \"./../loader/parsers/pbr.ts\";\nimport { Color } from \"../math/color.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport {\n\tconvexHull,\n\tnormalizeVertices,\n\tprojectVertices,\n} from \"../math/vertex.ts\";\nimport { AABB3d } from \"../physics/broadphase/aabb3d.ts\";\nimport Renderer from \"./../video/renderer.js\";\nimport { TextureAtlas } from \"./../video/texture/atlas.js\";\nimport Texture2d from \"./../video/texture/texture2d.ts\";\nimport { getShadowQuad, hasVerticalExtent } from \"./groundshadow.js\";\nimport Renderable from \"./renderable.js\";\n\n/**\n * additional import for TypeScript\n * @import CanvasRenderer from \"./../video/canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../video/webgl/webgl_renderer.js\";\n */\n\n// How far above the ground an object rises before its shadow is gone, as a\n// multiple of the object's own horizontal extent. Tuned by eye: a character\n// roughly two of its own widths up has clearly left the floor.\nconst SHADOW_FADE_SPAN = 6;\n\n// How far the blob is lifted off the ground plane, as a fraction of its own\n// radius. A shadow drawn EXACTLY coplanar with the floor it sits on is\n// order-dependent: both survive the LEQUAL depth test at equal depth, so\n// whichever draws last wins, and the sort between two objects at one depth is\n// arbitrary. Lifting it a hair toward the sky makes it win by depth instead of\n// by luck. Proportional to the radius so it holds at any scale — a fixed\n// epsilon would z-fight on a large shadow and float visibly on a small one.\nconst SHADOW_LIFT = 0.01;\n\n// How thin a blob is allowed to get on its minor axis, as a fraction of its\n// major. A paper-thin caster — a `Sprite3d` billboard, a flat plate — projects\n// honestly to a hairline, which reads as a rendering fault rather than a\n// shadow; this keeps it an ellipse that is still clearly elongated along the\n// object.\nconst SHADOW_MIN_AXIS_RATIO = 0.5;\n\n// How far the blob spreads past the caster's own footprint. A shadow sized to\n// exactly the footprint is invisible under anything with vertical sides — a\n// crate sitting flat on the floor covers its own shadow completely — and real\n// contact shadows spread a little anyway, because no light source is a point.\nconst SHADOW_SPREAD = 1.2;\n\n// reusable matrix for combining projection × model in draw()\nconst _combinedMatrix = new Matrix3d();\n\n// Resolve any acceptable texture input (TextureAtlas, image / canvas\n// object, or asset name) to a cached `TextureAtlas`. Throws if nothing\n// resolves — Mesh requires a texture binding for its GL pipeline.\n// `framewidth`/`frameheight` define the spritesheet cell size (defaulting\n// to the whole image); a subclass like Sprite3d passes them so the atlas\n// carries an animation frame grid.\nfunction resolveTextureAtlas(src, framewidth, frameheight) {\n\tif (src instanceof TextureAtlas) {\n\t\treturn src;\n\t}\n\t// a non-atlas Texture2d (e.g. a procedural NoiseTexture2d) is bound via its\n\t// baked source; an image/canvas object flows through; a {string} is a key.\n\tlet image;\n\tif (src instanceof Texture2d) {\n\t\timage = src.getTexture();\n\t} else if (typeof src === \"object\") {\n\t\timage = src;\n\t} else {\n\t\timage = getImage(src);\n\t}\n\tif (!image) {\n\t\tthrow new Error(\"Mesh: '\" + src + \"' image/texture not found!\");\n\t}\n\treturn game.renderer.cache.get(image, {\n\t\tframewidth: framewidth || image.width,\n\t\tframeheight: frameheight || image.height,\n\t});\n}\n\n/**\n * Normalize an emissive color input (`[r, g, b]` array / Float32Array, or\n * nullish) into a `Float32Array(3)`, or `undefined` when there's no emission\n * (nullish or all-zero) so the Mesh stays on the lean no-emissive path.\n * @param {number[]|Float32Array|undefined|null} src\n * @returns {Float32Array|undefined}\n * @ignore\n */\nfunction toEmissive(src) {\n\tif (src === undefined || src === null) {\n\t\treturn undefined;\n\t}\n\tconst r = src[0] || 0;\n\tconst g = src[1] || 0;\n\tconst b = src[2] || 0;\n\tif (r === 0 && g === 0 && b === 0) {\n\t\treturn undefined;\n\t}\n\treturn new Float32Array([r, g, b]);\n}\n\n/**\n * Resolve an OBJ material group into a draw descriptor. Builds the\n * group's tint from the MTL's `Kd` (defaults to white if missing) and\n * its opacity from `d`. Returns a self-contained record carrying the\n * index slice + color state; the group's own diffuse texture is filled\n * in afterwards by {@link buildTextureGroups}, once the mesh-level\n * fallback texture is known.\n * @param {{materialName: string|null, start: number, count: number}} group\n * @param {object} materials - MTL material table keyed by material name\n * @returns {{materialName: string|null, start: number, count: number, tint: Color, opacity: number}} draw descriptor for this group\n * @ignore\n */\nfunction resolveGroupMaterial(group, materials) {\n\tconst mat = group.materialName ? materials[group.materialName] : null;\n\tconst tint = new Color(255, 255, 255, 1);\n\tlet opacity = 1;\n\tif (mat) {\n\t\tif (mat.Kd) {\n\t\t\ttint.setColor(\n\t\t\t\tMath.round(mat.Kd[0] * 255),\n\t\t\t\tMath.round(mat.Kd[1] * 255),\n\t\t\t\tMath.round(mat.Kd[2] * 255),\n\t\t\t);\n\t\t}\n\t\tif (typeof mat.d === \"number\" && mat.d < 1) {\n\t\t\topacity = mat.d;\n\t\t}\n\t}\n\treturn {\n\t\tmaterialName: group.materialName,\n\t\tstart: group.start,\n\t\tcount: group.count,\n\t\ttint,\n\t\topacity,\n\t\ttexture: undefined,\n\t};\n}\n\n/**\n * Resolve each material group's own diffuse texture (`map_Kd`) and reduce\n * the result to the shortest list of index ranges that have to be drawn\n * with distinct textures (#1573).\n *\n * A group whose material declares no `map_Kd` keeps the mesh-level texture,\n * so a model mixing textured and `Kd`-only materials still draws in one\n * binding wherever it can. Adjacent ranges resolving to the same\n * `TextureAtlas` are merged — the atlas cache is keyed by image, so two\n * materials pointing at one file coalesce by identity rather than by\n * comparing names — and a model that ends up with a single texture returns\n * `undefined`, which keeps the common single-material case on exactly the\n * draw-call count it had before.\n *\n * A `map_Kd` naming an image that was never preloaded warns and falls back\n * to the mesh texture rather than throwing: only the first material's\n * `map_Kd` used to be resolved at all, so a partially-preloaded model that\n * rendered before must keep rendering.\n * @param {Array<object>} groups - the material groups, mutated in place to carry their resolved `texture`\n * @param {object} materials - MTL material table keyed by material name\n * @param {TextureAtlas} shared - the mesh-level texture, used by groups with no `map_Kd` of their own\n * @param {number} [framewidth] - spritesheet cell width, as passed to the Mesh\n * @param {number} [frameheight] - spritesheet cell height, as passed to the Mesh\n * @returns {Array<{texture: TextureAtlas, start: number, count: number}>|undefined} the per-texture draw ranges, or `undefined` when one binding covers the whole mesh\n * @ignore\n */\nfunction buildTextureGroups(\n\tgroups,\n\tmaterials,\n\tshared,\n\tframewidth,\n\tframeheight,\n) {\n\tlet distinct = false;\n\tfor (const group of groups) {\n\t\tconst mat = group.materialName ? materials[group.materialName] : null;\n\t\tlet texture = shared;\n\t\tif (mat?.map_Kd) {\n\t\t\ttry {\n\t\t\t\ttexture = resolveTextureAtlas(mat.map_Kd, framewidth, frameheight);\n\t\t\t} catch {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`melonJS: Mesh material \"${group.materialName}\" references the texture \"${mat.map_Kd}\", which is not loaded — that material falls back to the mesh texture`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tgroup.texture = texture;\n\t\t// only geometry that actually draws can make a split necessary\n\t\tif (texture !== shared && group.count > 0) {\n\t\t\tdistinct = true;\n\t\t}\n\t}\n\tif (distinct === false) {\n\t\treturn undefined;\n\t}\n\n\tconst slices = [];\n\tfor (const group of groups) {\n\t\t// an empty group (two `usemtl` directives in a row) would emit a\n\t\t// zero-index draw and, worse, break the adjacency test below\n\t\tif (group.count === 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst previous = slices[slices.length - 1];\n\t\tif (\n\t\t\tprevious !== undefined &&\n\t\t\tprevious.texture === group.texture &&\n\t\t\tprevious.start + previous.count === group.start\n\t\t) {\n\t\t\tprevious.count += group.count;\n\t\t} else {\n\t\t\tslices.push({\n\t\t\t\ttexture: group.texture,\n\t\t\t\tstart: group.start,\n\t\t\t\tcount: group.count,\n\t\t\t});\n\t\t}\n\t}\n\t// A single surviving range still needs the plan when its texture is not\n\t// the mesh-level one — the whole model draws with that material, and\n\t// returning `undefined` here would hand it back to `mesh.texture`.\n\t// Reachable when an empty group precedes the only drawing one.\n\tif (slices.length > 1) {\n\t\treturn slices;\n\t}\n\treturn slices.length === 1 && slices[0].texture !== shared\n\t\t? slices\n\t\t: undefined;\n}\n\n/**\n * A renderable object for displaying textured triangle meshes.\n * Supports loading from Wavefront OBJ models (via `loader.preload` with type \"obj\")\n * or from raw geometry data (vertices, uvs, indices).\n * Includes a built-in perspective projection and supports 3D transforms\n * through the standard Renderable API (`rotate`, `scale`, `translate`).\n * Works on both GPU backends — WebGL 2 and WebGPU, with hardware depth\n * testing — and on the Canvas renderer via the CPU-projected 2D-camera\n * path (painter's algorithm, no depth buffer).\n *\n * **Retained rendering** — under a {@link Camera3d} on a GPU backend\n * (`renderer.supportsRetainedMesh`), the mesh's model-space geometry is\n * uploaded to GPU buffers **once** and every frame just draws it by\n * reference: placement, camera, tint, alpha and emissive all ride\n * per-draw uniforms, so moving, rotating, scaling or re-tinting a mesh\n * never re-uploads geometry (near-zero per-frame allocation). Editing\n * the geometry itself in place is still possible — signal it with\n * {@link Mesh#needsUpdate} and the GPU copy refreshes on the next draw.\n * Under a 2D camera (or on Canvas) the mesh instead re-projects its\n * vertices on the CPU each frame — same API, the camera and backend\n * decide the path.\n *\n * **Pivot — transforms are applied about the mesh's local origin `(0, 0, 0)`,\n * NOT a normalized anchor point.** Unlike a {@link Sprite} (whose\n * {@link Renderable#anchorPoint} recenters a `width`×`height` box), a mesh has\n * real vertex coordinates, so rotation and scale pivot around the model origin\n * the geometry is authored against — the standard convention for 3D meshes.\n * Place the origin where you want the pivot at authoring time (or nest the\n * mesh under a transformed parent to pivot elsewhere). Consequently, on the\n * `Camera3d` world-space path the mesh opts out of the anchor offset entirely\n * (see {@link Renderable#applyAnchorTransform}); the legacy 2D path still\n * honors `anchorPoint` for backward compatibility. Subclasses that bake the\n * anchor into their vertex data instead (e.g. {@link Sprite3d}, via the\n * internal `_anchorBaked` flag) opt out on **both** paths — for those, the\n * vertex bake is the single anchoring mechanism.\n * @category Game Objects\n */\nexport default class Mesh extends Renderable {\n\t/**\n\t * @param {number} x - the x screen position of the mesh object\n\t * @param {number} y - the y screen position of the mesh object\n\t * @param {object} settings - Configuration parameters for the Mesh object\n\t * @param {string} [settings.model] - name of a preloaded OBJ model (via loader.preload with type \"obj\"). Vertex normals come with it — authored `vn` when the file has them, generated from face geometry when it does not — so an OBJ model can be `lit`.\n\t * @param {Float32Array|number[]} [settings.vertices] - vertex positions as x,y,z triplets (alternative to settings.model)\n\t * @param {Float32Array|number[]} [settings.uvs] - texture coordinates as u,v pairs (alternative to settings.model)\n\t * @param {Uint16Array|number[]} [settings.indices] - triangle vertex indices (alternative to settings.model)\n\t * @param {HTMLImageElement|TextureAtlas|string} [settings.texture] - the texture to apply (image name, HTMLImageElement, or TextureAtlas). If omitted and settings.material is provided, the texture is resolved from the MTL material's map_Kd. Passing this pins ONE binding over the whole model, which on a multi-material model suppresses the per-material texture split — see {@link Mesh#textureGroups}.\n\t * @param {string} [settings.material] - name of a preloaded MTL material (via loader.preload with type \"mtl\"). When provided, the diffuse texture (map_Kd), tint color (Kd), and opacity (d) are automatically applied. On a multi-material model each material's own `map_Kd` is bound for its own slice of the geometry (#1573) and each `Kd` is baked per-vertex, so one `Mesh` renders the whole model.\n\t * @param {number} settings.width - display width in pixels. With normalization on (the default) the model is scaled to fit this size; with `normalize: false` this is the uniform pixels-per-unit scale applied to the raw geometry.\n\t * @param {number} [settings.height] - display height in pixels (normalized models only; ignored when `normalize: false`)\n\t * @param {boolean} [settings.cullBackFaces=true] - enable backface culling\n\t * @param {boolean} [settings.normalize=true] - fit the source geometry into a `[-0.5, 0.5]` unit cube before scaling, so `width`/`height` behave like a Sprite. Set `false` to keep the geometry's real-world coordinates — required when several meshes share one coordinate space (e.g. nodes of an imported glTF scene) so their relative scale and layout are preserved.\n\t * @param {number} [settings.scale] - world-space scale (pixels per source unit) for the Camera3d path; defaults to `width`. Set this when `width`/`height` describe the renderable's world bounds (frustum culling) rather than the geometry scale — see {@link Mesh#meshScale}.\n\t * @param {boolean} [settings.rightHanded=false] - treat the source as right-handed (Y-up, e.g. glTF) under the `Camera3d` world path. The default Y-up→Y-down bridge negates Y only (a reflection, which mirrors the scene left/right); `true` negates Y **and** Z (a rotation) so chirality is preserved and the result matches the authoring tool. See {@link Mesh#rightHanded}.\n\t * @param {string} [settings.textureRepeat] - texture wrap mode (`\"repeat\"` / `\"repeat-x\"` / `\"repeat-y\"` / `\"no-repeat\"`) this mesh samples its texture with (per-mesh — it does not modify the shared texture, so other meshes/sprites using the same image are unaffected). Use `\"repeat\"` when the geometry's UVs fall outside the `[0, 1]` range and rely on the texture tiling (e.g. glTF assets, whose default sampler wrap is REPEAT) — otherwise the texture clamps to its edge texels and looks flat. Ignored for the white-pixel fallback. Note: REPEAT on a non-power-of-two texture requires WebGL 2.\n\t * @param {string} [settings.textureFilter] - texture magnification filter (`\"nearest\"` for crisp pixel-art upscaling, `\"linear\"` for smooth) applied to the resolved texture. Omit to keep the renderer's global `antiAlias` default. On the mesh path, linear filtering also samples a generated mip chain with trilinear minification and 4× anisotropy (distant geometry stops shimmering) — `\"nearest\"` opts out, keeping crisp pixel-art models on hard level-0 sampling. GPU backends only (ignored by the Canvas renderer).\n\t * @param {number} [settings.alphaCutoff=0] - alpha cutout threshold. Fragments whose final alpha is below this value are discarded (hard-edged cutout — foliage, fences, decals — with no blending or sorting). `0` disables the cutout. Set automatically by the glTF loader from a material's `alphaMode: \"MASK\"`. GPU mesh path only (WebGL and WebGPU; the Canvas renderer ignores it).\n\t * @param {number[]|Float32Array} [settings.emissive] - emissive (self-illumination) color `[r, g, b]` (0..1, may exceed 1 for HDR glow) added on top of the lit/unlit color so the surface glows regardless of scene lights (neon, lava, screens). Omit / all-zero for no emission. Set automatically by the glTF loader (`emissiveFactor`) and OBJ loader (MTL `Ke`). GPU mesh path only (WebGL and WebGPU; the Canvas renderer ignores it).\n\t * @example\n\t * // create from OBJ + MTL (texture auto-resolved from material)\n\t * let mesh = new me.Mesh(0, 0, {\n\t *     model: \"fox\",\n\t *     material: \"fox\",\n\t *     width: 200,\n\t *     height: 200,\n\t * });\n\t *\n\t * // create from OBJ with explicit texture (no MTL needed)\n\t * let mesh = new me.Mesh(0, 0, {\n\t *     model: \"cube\",\n\t *     texture: \"cube_texture\",\n\t *     width: 200,\n\t *     height: 200,\n\t * });\n\t *\n\t * // create from raw geometry that already carries its own world scale\n\t * // (e.g. a glTF scene node) — keep real coordinates, no mirror\n\t * let node = new me.Mesh(0, 0, {\n\t *     vertices: positions, // Float32Array of x,y,z triplets\n\t *     uvs: texcoords,      // Float32Array of u,v pairs\n\t *     indices: tris,       // Uint16Array of triangle indices\n\t *     texture: baseColorImage,\n\t *     width: 32,           // pixels per unit\n\t *     normalize: false,\n\t *     rightHanded: true,\n\t * });\n\t *\n\t * // material settings (WebGL) — usually set for you by the glTF/OBJ loader,\n\t * // but available directly on a hand-built mesh too\n\t * let sign = new me.Mesh(0, 0, {\n\t *     vertices, uvs, indices, texture: \"neon-sign\",\n\t *     width: 64, normalize: false,\n\t *     textureFilter: \"nearest\",   // crisp pixel-art upscaling\n\t *     alphaCutoff: 0.5,           // discard texels below 0.5 alpha (cutout)\n\t *     emissive: [0.9, 0.2, 0.6],  // glow, independent of scene lights\n\t * });\n\t *\n\t * // 3D rotation using the standard rotate() API\n\t * mesh.rotate(Math.PI / 4, new me.Vector3d(0, 1, 0)); // rotate around Y axis\n\t *\n\t * // 2D rotation (Z axis, same as Sprite)\n\t * mesh.rotate(Math.PI / 4);\n\t */\n\tconstructor(x, y, settings) {\n\t\tsuper(x, y, settings.width, settings.height);\n\n\t\t// load geometry from OBJ model or raw data\n\t\tlet objGroups = null;\n\t\t// the normal source that wins: an explicit `settings.normals`, else\n\t\t// whatever the OBJ supplied\n\t\tlet sourceNormals = settings.normals;\n\t\tif (typeof settings.model === \"string\") {\n\t\t\tconst objData = getOBJ(settings.model);\n\t\t\tif (!objData) {\n\t\t\t\tthrow new Error(\"Mesh: '\" + settings.model + \"' OBJ model not found!\");\n\t\t\t}\n\t\t\t/**\n\t\t\t * the original (untransformed) vertex positions as x,y,z triplets\n\t\t\t * @type {Float32Array}\n\t\t\t */\n\t\t\tthis.originalVertices = objData.vertices;\n\n\t\t\t/**\n\t\t\t * texture coordinates as u,v pairs\n\t\t\t * @type {Float32Array}\n\t\t\t */\n\t\t\tthis.uvs = objData.uvs;\n\n\t\t\t// Authored (or generated) vertex normals from the OBJ, so `lit`\n\t\t\t// models shade against the surface the artist modelled rather\n\t\t\t// than a fallback — the same data a glTF import supplies through\n\t\t\t// its NORMAL accessor. An explicit `settings.normals` still wins.\n\t\t\t//\n\t\t\t// Held locally rather than written back onto `settings`: the\n\t\t\t// caller's object is not ours to mutate. A frozen settings\n\t\t\t// literal would throw, and one reused for two different `model`\n\t\t\t// names would hand the second mesh the first model's normals.\n\t\t\tif (sourceNormals === undefined && objData.normals !== undefined) {\n\t\t\t\tsourceNormals = objData.normals;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * triangle indices\n\t\t\t * @type {Uint16Array}\n\t\t\t */\n\t\t\tthis.indices = objData.indices;\n\n\t\t\t/**\n\t\t\t * number of vertices\n\t\t\t * @type {number}\n\t\t\t */\n\t\t\tthis.vertexCount = objData.vertexCount;\n\n\t\t\t// pick up the material-group list emitted by the OBJ parser\n\t\t\t// (always non-null for non-empty models thanks to the\n\t\t\t// parser's \"anonymous group\" fallback)\n\t\t\tobjGroups = objData.groups;\n\t\t} else {\n\t\t\tthis.originalVertices =\n\t\t\t\tsettings.vertices instanceof Float32Array\n\t\t\t\t\t? settings.vertices\n\t\t\t\t\t: new Float32Array(settings.vertices);\n\t\t\tthis.uvs =\n\t\t\t\tsettings.uvs instanceof Float32Array\n\t\t\t\t\t? settings.uvs\n\t\t\t\t\t: new Float32Array(settings.uvs);\n\t\t\t// Preserve a typed index buffer as-is — coercing a Uint32Array to\n\t\t\t// Uint16Array would truncate index values > 65535, silently\n\t\t\t// corrupting meshes with more than 65535 vertices (the glTF parser\n\t\t\t// emits Uint32 indices for exactly that case). Only a plain JS\n\t\t\t// array is materialized, as Uint16 (the small-mesh default). The\n\t\t\t// batcher chunks large meshes into ≤maxVertices flushes, so its own\n\t\t\t// index buffer never needs more than 16 bits regardless.\n\t\t\tthis.indices =\n\t\t\t\tsettings.indices instanceof Uint16Array ||\n\t\t\t\tsettings.indices instanceof Uint32Array\n\t\t\t\t\t? settings.indices\n\t\t\t\t\t: new Uint16Array(settings.indices);\n\t\t\tthis.vertexCount = this.originalVertices.length / 3;\n\t\t}\n\n\t\t// Capture the original winding once at construction so the\n\t\t// Camera3d world-space path can swap to a reversed copy without\n\t\t// losing the ability to restore the original (see\n\t\t// `_setupWorldSpace` for the rationale). Same reference — we\n\t\t// never mutate it in place.\n\t\tthis._indicesOriginal = this.indices;\n\n\t\t// working array for projected vertices (see the note below on when it\n\t\t// is, and is not, refreshed)\n\t\t/**\n\t\t * the projected vertex positions.\n\t\t *\n\t\t * **Not refreshed on the retained `Camera3d` path.** There, geometry is\n\t\t * uploaded once in model space and placed by the GPU, so nothing\n\t\t * projects vertices per frame and this array holds whatever it last\n\t\t * did. It is still maintained by the Canvas renderer and by the 2D\n\t\t * camera path. Engine consumers that need current world positions —\n\t\t * {@link Mesh#getBounds3d}, {@link Mesh#toPolygon} — derive them on\n\t\t * demand instead of reading this; user code should do the same.\n\t\t *\n\t\t * To edit geometry, write to {@link Mesh#originalVertices} and set\n\t\t * {@link Mesh#needsUpdate}.\n\t\t * @type {Float32Array}\n\t\t */\n\t\tthis.vertices = new Float32Array(this.vertexCount * 3);\n\n\t\t/**\n\t\t * the source per-vertex normals (x,y,z triplets), or `undefined` if the\n\t\t * mesh was built without them. Supplied by the glTF loader; used for\n\t\t * lit shading under a `Camera3d` (see {@link Light3d}).\n\t\t * @type {Float32Array|undefined}\n\t\t */\n\t\tthis.originalNormals =\n\t\t\tsourceNormals !== undefined\n\t\t\t\t? sourceNormals instanceof Float32Array\n\t\t\t\t\t? sourceNormals\n\t\t\t\t\t: new Float32Array(sourceNormals)\n\t\t\t\t: undefined;\n\n\t\t/**\n\t\t * world-space normals for the current draw, recomputed from\n\t\t * {@link Mesh#originalNormals} along the Camera3d path. Empty (zero) when\n\t\t * the mesh has no source normals — the shader then ignores lighting.\n\t\t *\n\t\t * Carries the same caveat as {@link Mesh#vertices}: the WebGL\n\t\t * `Camera3d` path rotates normals on the GPU and leaves this array\n\t\t * untouched.\n\t\t * @type {Float32Array}\n\t\t */\n\t\tthis.normals = new Float32Array(this.vertexCount * 3);\n\n\t\t/**\n\t\t * Whether this mesh is lit by the active stage's {@link Light3d} lights.\n\t\t * When `true` it renders through the lit mesh batcher (diffuse shading\n\t\t * from the scene's lights, using {@link Mesh#originalNormals}); when\n\t\t * `false` (the default) it uses the lean unlit path and pays no lighting\n\t\t * cost. The glTF loader sets this on scene meshes when the scene has\n\t\t * lights. Only meaningful under a `Camera3d` on a GPU backend.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.lit = settings.lit === true;\n\n\t\t/**\n\t\t * Alpha cutout threshold. A fragment whose final alpha is below this\n\t\t * value is discarded — a hard-edged cutout (foliage, fences, chain-link,\n\t\t * decals) that needs no blending or back-to-front sorting. `0` (the\n\t\t * default) disables the cutout and the mesh renders fully opaque. Set by\n\t\t * the glTF loader from a material's `alphaMode: \"MASK\"` / `alphaCutoff`.\n\t\t * GPU mesh path only (the Canvas renderer ignores it).\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.alphaCutoff =\n\t\t\ttypeof settings.alphaCutoff === \"number\" ? settings.alphaCutoff : 0;\n\n\t\t/**\n\t\t * Emissive (self-illumination) color as an `[r, g, b]` `Float32Array`\n\t\t * (0..1, may exceed 1 for HDR glow), added on top of the lit/unlit color\n\t\t * so the surface glows independently of the scene lights (neon, lava,\n\t\t * screens, glowing eyes). `undefined` (the default) means no emission and\n\t\t * keeps the mesh on the lean path. Set by the glTF loader from a material's\n\t\t * `emissiveFactor` (× `KHR_materials_emissive_strength`) and by the OBJ\n\t\t * loader from an MTL's `Ke`. GPU mesh path only (WebGL and WebGPU; the Canvas renderer ignores it).\n\t\t * @type {Float32Array|undefined}\n\t\t */\n\t\tthis.emissive = toEmissive(settings.emissive);\n\n\t\t/**\n\t\t * Specular (highlight) color as an `[r, g, b]` `Float32Array`, or\n\t\t * `undefined` for a purely diffuse surface — which is the default, and\n\t\t * what every mesh rendered as before this existed.\n\t\t *\n\t\t * Drives a Blinn-Phong highlight on the **lit** mesh path, so it needs\n\t\t * `lit: true`, a `Light3d`, and normals. Set by the OBJ loader from an\n\t\t * MTL's `Ks`; paired with {@link Mesh#shininess}, which decides how\n\t\t * tight the highlight is. A `Ks` with no `Ns` produces nothing — the\n\t\t * exponent is what turns the term on.\n\t\t * @type {Float32Array|undefined}\n\t\t * @see Mesh#shininess\n\t\t */\n\t\tthis.specular = toEmissive(settings.specular);\n\n\t\t/**\n\t\t * Specular exponent — how tight the highlight is. The MTL `Ns` range\n\t\t * is 0..1000; higher is a smaller, harder highlight (polished metal),\n\t\t * lower is a broad sheen (satin). `0` (the default) disables the\n\t\t * specular term outright however bright {@link Mesh#specular} is, which\n\t\t * is what keeps a material declaring neither on the diffuse-only path.\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.shininess =\n\t\t\ttypeof settings.shininess === \"number\" ? settings.shininess : 0;\n\n\t\t/**\n\t\t * Per-texel opacity map (MTL `map_d`), or `undefined`. Its red channel\n\t\t * multiplies the fragment's alpha before {@link Mesh#alphaCutoff} is\n\t\t * applied, so a single material can cut out per pixel — the shape of\n\t\t * a leaf, the holes in a chain-link fence — where `alphaCutoff` alone\n\t\t * can only threshold uniformly across the whole material.\n\t\t *\n\t\t * Only meaningful alongside a non-zero `alphaCutoff`: without one there\n\t\t * is nothing to discard against. GPU mesh path only.\n\t\t * @type {TextureAtlas|undefined}\n\t\t */\n\t\tthis.alphaMap = undefined;\n\n\t\t/**\n\t\t * Cast a soft dark ellipse — a \"blob\" shadow — on the ground beneath\n\t\t * this mesh (#1515).\n\t\t *\n\t\t * Not a simulated shadow, deliberately: what a 2.5D scene needs from\n\t\t * one is *contact* — where the object is standing, and how far off the\n\t\t * ground it is mid-jump. It costs one extra draw per shadowed object,\n\t\t * shares one geometry and one texture with every other shadow in the\n\t\t * scene, and is inert while `false`.\n\t\t *\n\t\t * Requires a GPU backend and a {@link Camera3d}: the shadow rides the\n\t\t * retained world-space path, so the Canvas renderer and the 2D-camera\n\t\t * path draw none.\n\t\t *\n\t\t * Left **unset** (`undefined`, the default) this follows the\n\t\t * application's `castGroundShadow` setting — with one safeguard: a\n\t\t * scene-wide opt-in skips meshes with no vertical extent, because a\n\t\t * flat plane lying on the floor is the floor, and shadowing it with\n\t\t * itself smears the whole ground. Setting the property here is an\n\t\t * explicit instruction and always obeyed, safeguard included.\n\t\t * @type {boolean|undefined}\n\t\t * @default undefined\n\t\t * @see Mesh#shadowGroundY\n\t\t */\n\t\tthis.castGroundShadow =\n\t\t\ttypeof settings.castGroundShadow === \"boolean\"\n\t\t\t\t? settings.castGroundShadow\n\t\t\t\t: undefined;\n\n\t\t/**\n\t\t * World Y of the floor the shadow lands on, or `undefined` (the\n\t\t * default) to mean \"this object is standing on the ground\" — the\n\t\t * shadow sits at the object's own base, at full strength, and does not\n\t\t * shrink or fade.\n\t\t *\n\t\t * Set it, and the shadow shrinks and fades as the object rises above\n\t\t * it: the readable part of a jump. The game already knows this value\n\t\t * from collision, which is why it is not derived — deriving it from the\n\t\t * object's live bounds would make the \"ground\" jump with the jumper,\n\t\t * so the height could never be anything but zero.\n\t\t *\n\t\t * Render space is Y-DOWN, so the floor is a **greater** Y than the\n\t\t * object above it.\n\t\t * @type {number|undefined}\n\t\t * @default undefined\n\t\t */\n\t\tthis.shadowGroundY = settings.shadowGroundY;\n\n\t\t/**\n\t\t * Opacity of the shadow directly beneath the object, before any\n\t\t * height fade.\n\t\t * @type {number}\n\t\t * @default 0.45\n\t\t */\n\t\tthis.shadowOpacity =\n\t\t\ttypeof settings.shadowOpacity === \"number\"\n\t\t\t\t? settings.shadowOpacity\n\t\t\t\t: 0.45;\n\n\t\t/**\n\t\t * Cached horizontal half-extent used to size the shadow, resolved on\n\t\t * first shadowed draw. Cached because the alternative is a\n\t\t * `getBounds3d()` per frame per shadow, which re-bounds every source\n\t\t * vertex through the model matrix.\n\t\t * @ignore\n\t\t */\n\t\tthis._shadowHalfX = -1;\n\t\tthis._shadowHalfZ = -1;\n\t\t// the geometry version the two above (and `_shadowHasHeight`) were\n\t\t// measured at; -1 means \"never\". Recomputed when the mesh signals a\n\t\t// geometry change, so a deforming mesh — or a `Sprite3d` whose frame\n\t\t// bake rewrites `originalVertices` every animation step — does not keep\n\t\t// a blob frozen at whatever its first shadowed draw happened to see.\n\t\tthis._shadowGeomVersion = -1;\n\t\tthis._shadowHasHeight = false;\n\n\t\t/**\n\t\t * whether to cull back-facing triangles\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.cullBackFaces =\n\t\t\tsettings.cullBackFaces !== undefined ? settings.cullBackFaces : true;\n\n\t\t/**\n\t\t * Treat the source geometry as right-handed (Y-up, e.g. glTF) under\n\t\t * the Camera3d world path. The default (`false`) Y-up→Y-down bridge\n\t\t * negates Y only — a reflection, which mirrors the scene left/right.\n\t\t * When `true`, the bridge negates Y **and** Z (a 180° rotation about\n\t\t * X, determinant +1) so chirality is preserved and the result matches\n\t\t * the authoring tool (no mirror); triangle winding is left untouched\n\t\t * since a rotation doesn't invert it.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.rightHanded = settings.rightHanded === true;\n\n\t\t/**\n\t\t * Uniform world-space scale (pixels per source unit) applied along the\n\t\t * Camera3d world path. Defaults to `width`. Scene loaders (e.g. glTF)\n\t\t * set this independently of `width` / `height` so those can describe\n\t\t * the renderable's world-space bounds (used for frustum culling) while\n\t\t * the geometry is still scaled by this factor — `width` alone can't\n\t\t * serve both roles for a non-normalized scene mesh.\n\t\t * @type {number}\n\t\t * @default settings.width\n\t\t */\n\t\tthis.meshScale =\n\t\t\ttypeof settings.scale === \"number\" ? settings.scale : this.width;\n\n\t\t// resolve material (MTL) — applies texture, tint, and opacity.\n\t\t// Two paths:\n\t\t// - Single-material: pick the first MTL entry, apply to the whole\n\t\t//   mesh (legacy behavior, unchanged).\n\t\t// - Multi-material: build a per-group descriptor with each\n\t\t//   group's texture, tint, and opacity resolved from its own MTL\n\t\t//   entry. `draw()` later iterates the groups and swaps state\n\t\t//   per draw.\n\t\tlet textureSource = settings.texture;\n\t\t// per-texel opacity map, resolved after the diffuse texture below\n\t\tlet alphaMapSource = settings.alphaMap;\n\t\tconst materials =\n\t\t\ttypeof settings.material === \"string\" ? getMTL(settings.material) : null;\n\t\tconst isMultiMaterial =\n\t\t\tmaterials !== null &&\n\t\t\tobjGroups !== null &&\n\t\t\tobjGroups.length > 1 &&\n\t\t\tobjGroups.some((g) => {\n\t\t\t\treturn g.materialName !== null;\n\t\t\t});\n\n\t\tif (isMultiMaterial) {\n\t\t\t/**\n\t\t\t * Per-material submesh groups, populated when the OBJ\n\t\t\t * contains multiple `usemtl` directives AND a matching MTL\n\t\t\t * is bound via the `material` setting. Each entry slices\n\t\t\t * the shared `indices` buffer; field shape (`start`,\n\t\t\t * `count`, `materialName`) matches the glTF \"groups\"\n\t\t\t * convention.\n\t\t\t *\n\t\t\t * Under the per-vertex color baking path (tier 2), the\n\t\t\t * `tint` / `opacity` fields here are informational — the\n\t\t\t * actual rendered color is baked into `vertexColors` at\n\t\t\t * construction time. Mutating `groups[i].tint` after\n\t\t\t * construction has no visible effect; use `mesh.tint` for\n\t\t\t * runtime color multiplication, or rebuild the Mesh with\n\t\t\t * new material settings.\n\t\t\t * @type {Array<{materialName: string|null, start: number,\n\t\t\t *   count: number, tint: Color, opacity: number,\n\t\t\t *   texture: TextureAtlas|undefined}>}\n\t\t\t */\n\t\t\tthis.groups = objGroups.map((g) => {\n\t\t\t\treturn resolveGroupMaterial(g, materials);\n\t\t\t});\n\t\t\t// `this.tint` stays at its default (white) — every\n\t\t\t// material's Kd is already baked into `vertexColors` below,\n\t\t\t// so applying the first group's tint globally would double-\n\t\t\t// multiply it onto every vertex at render time. The\n\t\t\t// renderer-level `setTint` path on top of the baked colors\n\t\t\t// is still available for runtime flash / fade / team color.\n\t\t\t// The first `map_Kd` in the model becomes the mesh-level\n\t\t\t// texture — what groups whose material declares none draw\n\t\t\t// with, and the single binding a consumer that ignores\n\t\t\t// `textureGroups` (the Canvas renderer) sees. Groups with\n\t\t\t// their own `map_Kd` get it bound per draw range further\n\t\t\t// down (#1573). With no `map_Kd` anywhere this falls\n\t\t\t// through to the white-pixel fallback.\n\t\t\tif (!textureSource) {\n\t\t\t\tfor (const g of objGroups) {\n\t\t\t\t\t// a zero-index group (two `usemtl` in a row) draws nothing,\n\t\t\t\t\t// so letting its material name the mesh-level texture would\n\t\t\t\t\t// pick a map no geometry uses — and leave `mesh.texture`\n\t\t\t\t\t// outside the split plan entirely\n\t\t\t\t\tif (g.count === 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst mat = g.materialName ? materials[g.materialName] : null;\n\t\t\t\t\tif (mat && mat.map_Kd) {\n\t\t\t\t\t\ttextureSource = mat.map_Kd;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Per-vertex color buffer (one packed Uint32 per vertex)\n\t\t\t * populated for multi-material meshes. The mesh batcher\n\t\t\t * reads from this when present, pushing the per-vertex\n\t\t\t * color as the `aColor` attribute — so multi-material\n\t\t\t * rendering needs no extra draw calls per material vs\n\t\t\t * single-material rendering (the batcher still chunks\n\t\t\t * very large meshes across multiple draws to fit its\n\t\t\t * vertex/index buffer limits, same as the single-material\n\t\t\t * path). Multiplied at render time by the global\n\t\t\t * `mesh.tint`, so runtime tint mutation still works as\n\t\t\t * expected (flash, fade, team color, etc.).\n\t\t\t *\n\t\t\t * Vertices were split per-material at parse time (each\n\t\t\t * material has its own dedup scope in the OBJ parser), so\n\t\t\t * every vertex belongs to exactly one material group and\n\t\t\t * carries that group's color unambiguously.\n\t\t\t * @type {Uint32Array}\n\t\t\t */\n\t\t\tthis.vertexColors = new Uint32Array(this.vertexCount);\n\t\t\tfor (const g of this.groups) {\n\t\t\t\tconst c = g.tint.toUint32(g.opacity);\n\t\t\t\tconst end = g.start + g.count;\n\t\t\t\tfor (let i = g.start; i < end; i++) {\n\t\t\t\t\tthis.vertexColors[this.indices[i]] = c;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (materials) {\n\t\t\t// Single-material path. Prefer the MTL entry whose name\n\t\t\t// matches the OBJ's `usemtl` directive — an OBJ with one\n\t\t\t// `usemtl jet_body` referencing a `.mtl` that defines\n\t\t\t// `jet_body` alongside other materials should pick\n\t\t\t// `jet_body`, not whichever entry happens to be first in\n\t\t\t// the MTL parser's output. Fall back to the first entry\n\t\t\t// when there's no `usemtl` at all (the parser emits a\n\t\t\t// `materialName: null` anonymous group in that case).\n\t\t\tconst namedMaterial =\n\t\t\t\tobjGroups !== null && objGroups[0] && objGroups[0].materialName\n\t\t\t\t\t? materials[objGroups[0].materialName]\n\t\t\t\t\t: null;\n\t\t\tconst mat = namedMaterial ?? materials[Object.keys(materials)[0]];\n\t\t\tif (mat) {\n\t\t\t\tif (!textureSource && mat.map_Kd) {\n\t\t\t\t\ttextureSource = mat.map_Kd;\n\t\t\t\t}\n\t\t\t\tif (mat.Kd) {\n\t\t\t\t\tthis.tint.setColor(\n\t\t\t\t\t\tMath.round(mat.Kd[0] * 255),\n\t\t\t\t\t\tMath.round(mat.Kd[1] * 255),\n\t\t\t\t\t\tMath.round(mat.Kd[2] * 255),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (mat.d < 1.0) {\n\t\t\t\t\tthis.setOpacity(mat.d);\n\t\t\t\t}\n\t\t\t\t// MTL emissive (Ke) — self-illumination, kept separate from the\n\t\t\t\t// diffuse tint so it glows regardless of scene lighting\n\t\t\t\tconst ke = toEmissive(mat.Ke);\n\t\t\t\tif (ke !== undefined) {\n\t\t\t\t\tthis.emissive = ke;\n\t\t\t\t}\n\t\t\t\t// MTL specular (Ks + Ns). Both are needed: `Ns` of 0 is \"no\n\t\t\t\t// highlight\" however bright `Ks` is, and exporters write a\n\t\t\t\t// black `Ks` for matte materials, so either one alone leaves\n\t\t\t\t// the mesh on the diffuse-only path.\n\t\t\t\tconst ks = toEmissive(mat.Ks);\n\t\t\t\tif (ks !== undefined && mat.Ns > 0) {\n\t\t\t\t\tthis.specular = ks;\n\t\t\t\t\tthis.shininess = mat.Ns;\n\t\t\t\t} else if (mat.Pr !== null && mat.Pr !== undefined) {\n\t\t\t\t\t// The PBR extension (`Pr` / `Pm`), approximated onto the\n\t\t\t\t\t// same terms — the shared mapping the glTF loader uses, so\n\t\t\t\t\t// one material described in two formats shades the same.\n\t\t\t\t\t//\n\t\t\t\t\t// Only when `Ks`/`Ns` said nothing: an explicit specular\n\t\t\t\t\t// states exactly what the artist wanted, where\n\t\t\t\t\t// roughness/metalness merely implies it. Blender writes\n\t\t\t\t\t// both blocks, so this precedence decides most real files.\n\t\t\t\t\tconst derived = specularFromMetallicRoughness(\n\t\t\t\t\t\tmat.Pr,\n\t\t\t\t\t\tmat.Pm ?? 0,\n\t\t\t\t\t\tmat.Kd,\n\t\t\t\t\t);\n\t\t\t\t\tif (derived.specular !== undefined) {\n\t\t\t\t\t\tthis.specular = new Float32Array(derived.specular);\n\t\t\t\t\t\tthis.shininess = derived.shininess;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// MTL alpha map (map_d) — per-texel opacity\n\t\t\t\tif (mat.map_d) {\n\t\t\t\t\talphaMapSource = mat.map_d;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// resolve texture. Fall back to the shared 1×1 white pixel when\n\t\t// no texture source was resolved — covers Kd-only multi-material\n\t\t// models (Kenney style) AND single-material meshes constructed\n\t\t// without a `texture:` or a `map_Kd`-bearing `material:` (the\n\t\t// GPU pipeline still needs something to sample; tint / per-\n\t\t// vertex color does the actual coloring).\n\t\tconst hasRealTexture = !!textureSource;\n\t\tif (!textureSource) {\n\t\t\ttextureSource = Renderer.getWhitePixel();\n\t\t}\n\t\tthis.texture = resolveTextureAtlas(\n\t\t\ttextureSource,\n\t\t\tsettings.framewidth,\n\t\t\tsettings.frameheight,\n\t\t);\n\n\t\t// Per-texel opacity map (MTL `map_d`). A map that failed to preload\n\t\t// warns and leaves the mesh on the uniform cutout rather than throwing\n\t\t// — the model still renders, just without its cut-outs, which is far\n\t\t// easier to diagnose than a mesh that never appeared.\n\t\tif (alphaMapSource) {\n\t\t\ttry {\n\t\t\t\tthis.alphaMap = resolveTextureAtlas(\n\t\t\t\t\talphaMapSource,\n\t\t\t\t\tsettings.framewidth,\n\t\t\t\t\tsettings.frameheight,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`melonJS: Mesh alpha map \"${alphaMapSource}\" is not loaded — the mesh keeps its uniform alpha cutout`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Index ranges that each need their own diffuse texture bound, for a\n\t\t * multi-material model whose materials carry different `map_Kd` maps\n\t\t * (#1573) — `undefined` whenever one binding covers the whole mesh,\n\t\t * which is every single-material model and every `Kd`-only one.\n\t\t *\n\t\t * The GPU backends draw one indexed range per entry instead of one\n\t\t * range for the whole mesh; adjacent materials sharing a texture are\n\t\t * already merged here, so the list is the minimum number of draws the\n\t\t * model needs. An explicit `settings.texture` suppresses the split\n\t\t * entirely — asking for one texture is asking for one texture.\n\t\t *\n\t\t * The Canvas renderer ignores this: a multi-material mesh takes its\n\t\t * per-triangle solid-fill path there and never samples a texture at\n\t\t * all.\n\t\t * @type {Array<{texture: TextureAtlas, start: number, count: number}>|undefined}\n\t\t */\n\t\tthis.textureGroups =\n\t\t\tisMultiMaterial === true && !settings.texture\n\t\t\t\t? buildTextureGroups(\n\t\t\t\t\t\tthis.groups,\n\t\t\t\t\t\tmaterials,\n\t\t\t\t\t\tthis.texture,\n\t\t\t\t\t\tsettings.framewidth,\n\t\t\t\t\t\tsettings.frameheight,\n\t\t\t\t\t)\n\t\t\t\t: undefined;\n\n\t\t/**\n\t\t * Per-mesh texture wrap mode (`\"repeat\"` / `\"repeat-x\"` / `\"repeat-y\"`\n\t\t * / `\"no-repeat\"`), or `undefined` to sample with the texture's own\n\t\t * wrap. Some assets author UVs outside the `[0, 1]` range and rely on\n\t\t * the sampler repeating the texture (this is the glTF default sampler\n\t\t * behavior); the mesh would otherwise clamp to the edge texels and\n\t\t * look flat / untextured.\n\t\t *\n\t\t * Kept on the mesh and threaded to the batcher at draw time — sampler\n\t\t * state per use, like a GL sampler object — so it never mutates the\n\t\t * per-image `TextureAtlas` shared with every other consumer of the\n\t\t * same image (#1503). Two meshes (or a mesh and a sprite) can point\n\t\t * at one image with different wrap modes; the texture cache keys GL\n\t\t * units by `(source, repeat)` so each wrap gets its own GL texture.\n\t\t * Applied only to a real texture — never the shared white-pixel\n\t\t * fallback, which is global and must stay `\"no-repeat\"`.\n\t\t * @type {string|undefined}\n\t\t */\n\t\tthis.textureRepeat =\n\t\t\thasRealTexture && typeof settings.textureRepeat === \"string\"\n\t\t\t\t? settings.textureRepeat\n\t\t\t\t: undefined;\n\n\t\t// Optional texture magnification filter (`\"nearest\"` for crisp pixel-art\n\t\t// upscaling, `\"linear\"` for smooth). When omitted the texture keeps the\n\t\t// renderer's global `antiAlias` default. GPU backends only — the Canvas\n\t\t// renderer ignores it. WebGL stores its GL enum; other backends (the\n\t\t// WebGPU texture store) consume the string form directly.\n\t\t//\n\t\t// NOTE: unlike `textureRepeat` above, this still mutates the shared\n\t\t// per-image atlas — the texture-unit cache doesn't discriminate by\n\t\t// filter, so a true per-mesh filter needs the unit-key change planned\n\t\t// with the #1410 TextureCache refactor. Two consumers of one image\n\t\t// wanting different filters is last-writer-wins until then.\n\t\tif (hasRealTexture && typeof settings.textureFilter === \"string\") {\n\t\t\tconst gl = game.renderer?.gl;\n\t\t\tconst filter = gl\n\t\t\t\t? settings.textureFilter === \"nearest\"\n\t\t\t\t\t? gl.NEAREST\n\t\t\t\t\t: gl.LINEAR\n\t\t\t\t: settings.textureFilter;\n\t\t\tthis.texture.filter = filter;\n\t\t\t// every texture the model draws with, not just the mesh-level one:\n\t\t\t// a per-material split would otherwise leave the other materials on\n\t\t\t// the renderer default, rendering one material crisp and the rest\n\t\t\t// smooth (#1573)\n\t\t\tif (this.textureGroups !== undefined) {\n\t\t\t\tfor (const group of this.textureGroups) {\n\t\t\t\t\tgroup.texture.filter = filter;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Projection matrix applied automatically before the model transform in draw().\n\t\t * Defaults to a perspective projection (45° FOV, camera at z=-2.5) suitable for\n\t\t * viewing unit-cube-sized geometry. Set to identity for orthographic (flat) projection.\n\t\t * Most users don't need to modify this — the default works for standard OBJ models.\n\t\t * @type {Matrix3d}\n\t\t */\n\t\tthis.projectionMatrix = new Matrix3d();\n\t\tthis.projectionMatrix.perspective(Math.PI / 4, 1, 0.1, 10);\n\t\tthis.projectionMatrix.translate(0, 0, -2.5);\n\n\t\t// normalize original vertices to fit within [-0.5, 0.5] range\n\t\t// so that width/height scaling works like Sprite. Scene meshes\n\t\t// (e.g. a glTF node that already carries its own world transform\n\t\t// and shares a coordinate space with sibling meshes) opt out via\n\t\t// `normalize: false` to preserve real-world scale across the scene.\n\t\tif (settings.normalize !== false) {\n\t\t\tnormalizeVertices(this.originalVertices);\n\t\t}\n\n\t\tthis.anchorPoint.set(0.5, 0.5);\n\n\t\t// Subclasses that bake the anchor offset directly into their vertex\n\t\t// data (e.g. Sprite3d) set this true, so preDraw suppresses the base\n\t\t// anchor translate on BOTH camera paths — the vertex bake is then the\n\t\t// single anchoring mechanism (the Camera2d fallback loses its exact\n\t\t// pixel-shift anchor for such subclasses; that path is documented as\n\t\t// degraded for them anyway). Plain meshes keep the legacy 2D anchor.\n\t\t/** @ignore */\n\t\tthis._anchorBaked = false;\n\n\t\t// skip applying the 3D transform to the 2D renderer context in preDraw\n\t\t// the full 3D transform is applied to vertices in draw() instead\n\t\tthis.autoTransform = false;\n\n\t\t// pre-allocate points array and polygon for toPolygon() (avoids per-call allocation)\n\t\t/** @ignore */\n\t\tthis._hullPoints = Array.from({ length: this.vertexCount }, () => {\n\t\t\treturn new Vector2d();\n\t\t});\n\t\t/** @ignore */\n\t\tthis._hullPolygon = null;\n\n\t\t// Bumped whenever the geometry itself changes, so a renderer holding\n\t\t// GPU-resident copies knows when to refresh them. Placement changes\n\t\t// (transform, position, tint) deliberately do NOT bump it.\n\t\t/** @ignore */\n\t\tthis._geometryVersion = 0;\n\t}\n\n\t/**\n\t * Project all vertices through projectionMatrix × currentTransform\n\t * and store the results in `this.vertices`.\n\t * @param {number} [offsetX=0] - x offset added to each projected vertex\n\t * @param {number} [offsetY=0] - y offset added to each projected vertex\n\t * @param {number} [zScale=0] - scale factor for Z output (0 = skip Z, 1000 = depth buffer range)\n\t * @ignore\n\t */\n\t_projectVertices(offsetX = 0, offsetY = 0, zScale = 0) {\n\t\t_combinedMatrix.copy(this.projectionMatrix);\n\t\t_combinedMatrix.multiply(this.currentTransform);\n\t\tprojectVertices(\n\t\t\tthis.originalVertices,\n\t\t\tthis.vertices,\n\t\t\tthis.vertexCount,\n\t\t\t_combinedMatrix.val,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t\toffsetX,\n\t\t\toffsetY,\n\t\t\tzScale,\n\t\t);\n\t}\n\n\t/**\n\t * Signal that this mesh's geometry itself has changed — its vertices, UVs,\n\t * indices, normals or per-vertex colours were edited in place.\n\t *\n\t * Placement is *not* geometry: moving, rotating, scaling or re-tinting a\n\t * mesh needs no signal, because those are applied when drawing rather than\n\t * stored in the geometry. Only reach for this after writing into\n\t * {@link Mesh#originalVertices} and friends directly.\n\t * @type {boolean}\n\t * @example\n\t * // deform the mesh, then tell it the shape moved\n\t * mesh.originalVertices[1] += 10;\n\t * mesh.needsUpdate = true;\n\t */\n\t// Deliberately write-only: this is a signal, not a state. Whether the GPU\n\t// copy is actually behind is known to the renderer that uploaded it, not\n\t// to the mesh, so any getter here would have to guess.\n\t// eslint-disable-next-line accessor-pairs\n\tset needsUpdate(value) {\n\t\tif (value !== false) {\n\t\t\tthis._geometryVersion++;\n\t\t}\n\t}\n\n\t/**\n\t * A custom shader hosted on this mesh's draw, replacing the built-in\n\t * mesh shading: a {@link GLShader} carrying a `{vertex, fragment}`\n\t * GLSL program (hosted by the WebGL renderer) and/or a complete\n\t * `wgsl` module (hosted by the WebGPU renderer — see the GLShader\n\t * class docs for the module contract). One object serves both\n\t * backends; a shader without a realization for the active backend\n\t * (or `null`) degrades to the built-in shading. The tint, texture,\n\t * placement and (for `lit` meshes) light data keep flowing through\n\t * their usual uniforms — the custom shader decides what to do with\n\t * them.\n\t * @type {GLShader|undefined}\n\t * @example\n\t * me.loader.preload([{ name: \"toon\", type: \"shader\", src: {\n\t *     vertex: \"shaders/toon.vert\",     // GLSL pair for WebGL\n\t *     fragment: \"shaders/toon.frag\",\n\t *     wgsl: \"shaders/toon.wgsl\",       // complete module for WebGPU\n\t * }}], () => {\n\t *     myMesh.shader = me.loader.getShader(\"toon\");\n\t * });\n\t */\n\tget shader() {\n\t\treturn super.shader;\n\t}\n\n\tset shader(value) {\n\t\tsuper.shader = value;\n\t}\n\n\t/**\n\t * Compose this mesh's placement into a single matrix: where its model-space\n\t * geometry sits in the world.\n\t *\n\t * This is the matrix form of {@link Mesh#_projectVerticesWorld} — the same\n\t * transform, scale and axis bridge, expressed once instead of applied to\n\t * every vertex:\n\t *\n\t *   `translate(pos, depth) · scale(s, -s, ±s) · currentTransform`\n\t *\n\t * The Y negation bridges Y-up geometry to the engine's Y-down world; Z is\n\t * negated too for right-handed (glTF) sources, which makes the bridge a\n\t * rotation rather than a reflection and so preserves triangle winding.\n\t * @returns {Matrix3d} the mesh's model matrix (reused instance)\n\t * @ignore\n\t */\n\t_composeModelMatrix() {\n\t\tif (this._modelMatrix === undefined) {\n\t\t\t/** @ignore */\n\t\t\tthis._modelMatrix = new Matrix3d();\n\t\t}\n\t\tconst out = this._modelMatrix.val;\n\t\tconst c = this.currentTransform.val;\n\t\tconst s = this.meshScale;\n\t\tconst sy = -s;\n\t\tconst sz = this.rightHanded ? -s : s;\n\n\t\tfor (let col = 0; col < 4; col++) {\n\t\t\tconst o = col * 4;\n\t\t\tout[o] = c[o] * s;\n\t\t\tout[o + 1] = c[o + 1] * sy;\n\t\t\tout[o + 2] = c[o + 2] * sz;\n\t\t\tout[o + 3] = c[o + 3];\n\t\t}\n\t\t// the node origin rides in pos/depth, applied after the scale + bridge\n\t\tout[12] += this.pos.x;\n\t\tout[13] += this.pos.y;\n\t\tout[14] += this.depth;\n\n\t\treturn this._modelMatrix;\n\t}\n\n\t/**\n\t * Project vertices into **world space** for the Camera3d render path.\n\t * Bypasses {@link Mesh#projectionMatrix} (the self-contained\n\t * perspective that's meaningful only under Camera2d) and emits\n\t * vertices that the active 3D camera then projects via its own\n\t * view + projection matrices, the same way it does for Sprites.\n\t *\n\t * Per-vertex math: `currentTransform × originalVertex`, then a uniform\n\t * scale by `this.width`, the Y-up→Y-down bridge (negate Y, and also Z when\n\t * {@link Mesh#rightHanded} is set so the bridge is a rotation rather than a\n\t * mirror), and a translate to `(offsetX, offsetY, offsetZ)`.\n\t *\n\t * @param {number} offsetX - world X to place the mesh center at\n\t * @param {number} offsetY - world Y to place the mesh center at\n\t * @param {number} offsetZ - world Z to place the mesh center at\n\t * @ignore\n\t */\n\t_projectVerticesWorld(offsetX, offsetY, offsetZ) {\n\t\tconst out = this.vertices;\n\t\tconst src = this.originalVertices;\n\t\tconst scale = this.meshScale;\n\t\tconst m = this.currentTransform.val;\n\t\t// Include the translation column (m[12..14]) — a proper\n\t\t// matrix-vector multiplication treats the vertex as\n\t\t// `(vx, vy, vz, 1)` so the translation column gets added\n\t\t// once. Previously the loop only used the rotation/scale\n\t\t// columns, which silently dropped `mesh.translate(x, y, z)`\n\t\t// under Camera3d while the Camera2d path (via\n\t\t// `projectVertices` in vertex.ts) honored it.\n\t\tconst tx = m[12];\n\t\tconst ty = m[13];\n\t\tconst tz = m[14];\n\t\t// right-handed source (glTF): negate Z as well as Y so the Y-up→\n\t\t// Y-down bridge is a rotation (det +1, no mirror) rather than a\n\t\t// reflection. Left-handed default keeps Z as-is (legacy reflection).\n\t\tconst zSign = this.rightHanded ? -1 : 1;\n\n\t\tfor (let i = 0; i < this.vertexCount; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst vx = src[i3];\n\t\t\tconst vy = src[i3 + 1];\n\t\t\tconst vz = src[i3 + 2];\n\n\t\t\tconst rx = m[0] * vx + m[4] * vy + m[8] * vz + tx;\n\t\t\tconst ry = m[1] * vx + m[5] * vy + m[9] * vz + ty;\n\t\t\tconst rz = m[2] * vx + m[6] * vy + m[10] * vz + tz;\n\n\t\t\tout[i3] = rx * scale + offsetX;\n\t\t\tout[i3 + 1] = -ry * scale + offsetY;\n\t\t\tout[i3 + 2] = zSign * rz * scale + offsetZ;\n\t\t}\n\t}\n\n\t/**\n\t * Project {@link Mesh#originalNormals} into world space for lit shading,\n\t * storing the result in {@link Mesh#normals}. Applies the rotation/scale of\n\t * {@link Renderable#currentTransform} (no translation), the same Y-down /\n\t * `rightHanded` Y/Z bridge as {@link Mesh#_projectVerticesWorld}, then\n\t * renormalizes. No-op when the mesh has no source normals.\n\t *\n\t * Note: uses the transform's rotation/scale columns directly rather than\n\t * the inverse-transpose, so normals are exact under rotation + uniform\n\t * scale (the common case); a strongly non-uniform scale would skew them\n\t * slightly — acceptable for the diffuse lighting path.\n\t * @ignore\n\t */\n\t_projectNormalsWorld() {\n\t\tconst src = this.originalNormals;\n\t\tif (src === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst out = this.normals;\n\t\tconst m = this.currentTransform.val;\n\t\tconst zSign = this.rightHanded ? -1 : 1;\n\t\tfor (let i = 0; i < this.vertexCount; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst nx = src[i3];\n\t\t\tconst ny = src[i3 + 1];\n\t\t\tconst nz = src[i3 + 2];\n\t\t\tconst rx = m[0] * nx + m[4] * ny + m[8] * nz;\n\t\t\tconst ry = -(m[1] * nx + m[5] * ny + m[9] * nz); // Y-down flip\n\t\t\tconst rz = zSign * (m[2] * nx + m[6] * ny + m[10] * nz);\n\t\t\tconst len = Math.hypot(rx, ry, rz);\n\t\t\tif (len > 1e-8) {\n\t\t\t\tout[i3] = rx / len;\n\t\t\t\tout[i3 + 1] = ry / len;\n\t\t\t\tout[i3 + 2] = rz / len;\n\t\t\t} else {\n\t\t\t\t// degenerate normal → fall back to +Y (a unit vector) rather\n\t\t\t\t// than zero: the shader does `normalize(vNormal)`, and\n\t\t\t\t// normalize((0,0,0)) is NaN (black/garbage fragment).\n\t\t\t\tout[i3] = 0;\n\t\t\t\tout[i3 + 1] = 1;\n\t\t\t\tout[i3 + 2] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Build the winding-reversed indices buffer used by the Camera3d\n\t * world-space path. Called once, lazily, the first time the mesh\n\t * draws under Camera3d. Why: the world-space path Y-flips vertices\n\t * on output (a reflection, det = -1), which inverts triangle\n\t * winding in screen space. Without the swap, `cullBackFaces: true`\n\t * would end up culling the front faces and the model would look\n\t * hollow.\n\t *\n\t * Both buffers are kept alive — `_indicesOriginal` is the original\n\t * OBJ-shared (or user-supplied) winding, `_indicesReversed` is the\n\t * Camera3d-flipped copy. `draw()` swaps `this.indices` between them\n\t * each frame based on the active camera, so a Mesh that was first\n\t * drawn under Camera3d and later re-parented to a Camera2d stage\n\t * (e.g. a level transition into a 2D minigame) gets its original\n\t * winding back and stays correctly oriented under\n\t * `cullBackFaces: true`.\n\t * @param {boolean} [needsReversedIndices=true] - build the winding-reversed index copy. `false` on the retained path, which corrects winding with `frontFace` and would otherwise duplicate the index array for nothing.\n\t * @ignore\n\t */\n\t_setupWorldSpace(needsReversedIndices = true) {\n\t\t// Only the reflection bridge (left-handed, Y-only negate) inverts\n\t\t// winding and needs the reversed copy. `rightHanded` meshes (all glTF\n\t\t// scenes) use a rotation bridge that preserves winding, so they keep\n\t\t// `_indicesOriginal` and never read the reversed buffer — skip the\n\t\t// allocation entirely for them.\n\t\t//\n\t\t// The retained path also never reads it: it uploads the authored order\n\t\t// and flips `frontFace` instead, so building the copy there would\n\t\t// duplicate the whole index array for nothing (megabytes on a dense\n\t\t// mesh).\n\t\tif (needsReversedIndices === true && this.rightHanded !== true) {\n\t\t\tconst src = this._indicesOriginal;\n\t\t\t// match the source index type (Uint16Array OR Uint32Array): a copy\n\t\t\t// into a hard-coded Uint16Array would truncate indices for meshes\n\t\t\t// with > 65535 vertices.\n\t\t\tconst Ctor = src.constructor;\n\t\t\tconst dst = new Ctor(src.length);\n\t\t\tfor (let i = 0; i < src.length; i += 3) {\n\t\t\t\tdst[i] = src[i];\n\t\t\t\tdst[i + 1] = src[i + 2];\n\t\t\t\tdst[i + 2] = src[i + 1];\n\t\t\t}\n\t\t\tthis._indicesReversed = dst;\n\t\t}\n\t\tthis._worldSpace = true;\n\t}\n\n\t/**\n\t * Resolve the projection mode once when the mesh enters the world.\n\t * The active stage's camera class determines which path `draw()` takes\n\t * for the rest of the mesh's lifetime — no per-frame branch is needed,\n\t * and we don't depend on the `me.game` singleton during the draw loop.\n\t *\n\t * Detection walks the ancestor chain to the root container, which is\n\t * the active stage's world (Stage.world). The world's `_currentCamera`\n\t * is set by the stage at draw time, but we can instead read off the\n\t * mesh's already-set ancestors against the active viewport — at this\n\t * point in the engine lifecycle, `game.viewport` is the right camera\n\t * to use as the source of truth, captured here ONCE rather than per\n\t * frame.\n\t * @ignore\n\t */\n\tonActivateEvent(...args) {\n\t\tsuper.onActivateEvent(...args);\n\t\tthis._useWorldSpace = game.viewport instanceof Camera3d;\n\t}\n\n\t/**\n\t * Release any GPU geometry when this mesh leaves the world.\n\t *\n\t * `destroy()` is not enough on its own: a child removed with\n\t * `keepalive: true`, or one recycled through {@link pool}, never gets\n\t * destroyed, so its persistent vertex/index buffers would outlive every\n\t * reference to it and only be reclaimed on a renderer reset. Releasing\n\t * here also means a recycled instance starts from a clean slate — it\n\t * rebuilds on its next draw rather than inheriting the previous\n\t * occupant's geometry.\n\t * @ignore\n\t */\n\tonDeactivateEvent(...args) {\n\t\tconst renderer = this.parentApp?.renderer ?? game.renderer;\n\t\trenderer?.deleteMeshGeometry?.(this);\n\t\tsuper.onDeactivateEvent(...args);\n\t}\n\n\t/**\n\t * The mesh's world-space 3D axis-aligned bounding box. This is the 3D analog\n\t * of {@link Renderable#getBounds} (which returns a flat 2D box from\n\t * `width`/`height` and so cannot describe a mesh's real extent).\n\t *\n\t * Computed on demand by bounding the model-space geometry through the\n\t * mesh's current placement, so it reflects the live transform and is valid\n\t * before the mesh has ever been drawn. Meaningful for the `Camera3d` path;\n\t * for the 2D path use {@link Renderable#getBounds}.\n\t *\n\t * The same {@link AABB3d} instance is returned each call (recomputed in\n\t * place), so copy it (`.clone()`) if you need to keep it.\n\t * @returns {AABB3d} the world-space bounding box (reused instance)\n\t */\n\tgetBounds3d() {\n\t\tif (this._bounds3d === undefined) {\n\t\t\t/** @ignore */\n\t\t\tthis._bounds3d = new AABB3d();\n\t\t}\n\t\t// Bound the model-space geometry through this mesh's placement, rather\n\t\t// than reading whatever the last draw happened to leave in `vertices`.\n\t\t// The result is therefore correct before the first draw, and stays\n\t\t// correct when the mesh is drawn from retained GPU geometry.\n\t\treturn this._bounds3d.fromVertices(\n\t\t\tthis.originalVertices,\n\t\t\tthis.vertexCount,\n\t\t\tthis._composeModelMatrix().val,\n\t\t);\n\t}\n\n\t/**\n\t * Prepare the renderer for drawing the mesh. On the `Camera3d` world-space\n\t * path the mesh emits final world coordinates, so it opts out of the base\n\t * anchor-point offset ({@link Renderable#applyAnchorTransform} = `false`) —\n\t * otherwise the offset would leak into the shared mesh view matrix. The 2D\n\t * path keeps the anchor — except for subclasses with a vertex-baked anchor\n\t * (`_anchorBaked`, e.g. {@link Sprite3d}), which suppress it on both paths.\n\t * See the class description for the pivot rationale.\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t */\n\tpreDraw(renderer) {\n\t\t// world-space meshes pivot about their own origin, not a bounds-box\n\t\t// anchor (see Mesh class doc + Renderable#applyAnchorTransform); a\n\t\t// subclass with a vertex-baked anchor (`_anchorBaked`) opts out on\n\t\t// both paths — recomputed here every frame, so a one-shot constructor\n\t\t// assignment could never stick\n\t\tthis.applyAnchorTransform =\n\t\t\tthis._useWorldSpace !== true && this._anchorBaked !== true;\n\t\tsuper.preDraw(renderer);\n\t}\n\n\t/**\n\t * Whether this mesh casts a ground shadow on this draw (#1515).\n\t *\n\t * Precedence, most specific first: the mesh's own `castGroundShadow`, then\n\t * the application's `castGroundShadow` setting. A per-mesh value is an\n\t * explicit instruction and is obeyed as given; the application default is a\n\t * blanket one, so it skips meshes with no vertical extent — which is what a\n\t * ground plane is, and a flat plane lying on the floor has nothing to cast.\n\t * Read off the renderer rather than the global application instance, the\n\t * same way the `textureFilter` default is.\n\t * @param {WebGLRenderer|WebGPURenderer} renderer - the active renderer\n\t * @returns {boolean} true if a shadow should be drawn\n\t * @ignore\n\t */\n\t_castsGroundShadow(renderer) {\n\t\tif (typeof this.castGroundShadow === \"boolean\") {\n\t\t\treturn this.castGroundShadow;\n\t\t}\n\t\tif (renderer.settings?.castGroundShadow !== true) {\n\t\t\treturn false;\n\t\t}\n\t\t// Measured with the footprint and cached against the geometry version.\n\t\t// This runs per mesh per FRAME on the application-default path — which\n\t\t// is now the default — and `hasVerticalExtent` walks every vertex\n\t\t// twice, so an uncached call makes a 100k-vertex ground plane pay 200k\n\t\t// iterations a frame purely to answer \"no\".\n\t\tthis._measureShadowFootprint();\n\t\treturn this._shadowHasHeight;\n\t}\n\n\t/**\n\t * Measure the model-space footprint the blob is built from, and whether the\n\t * geometry has any height at all — once per geometry version.\n\t * @ignore\n\t */\n\t_measureShadowFootprint() {\n\t\tconst version = this._geometryVersion ?? 0;\n\t\tif (this._shadowGeomVersion === version) {\n\t\t\treturn;\n\t\t}\n\t\tconst src = this.originalVertices;\n\t\tlet hx = 0;\n\t\tlet hz = 0;\n\t\tfor (let i = 0; i < this.vertexCount; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst x = Math.abs(src[i3]);\n\t\t\tconst z = Math.abs(src[i3 + 2]);\n\t\t\tif (x > hx) {\n\t\t\t\thx = x;\n\t\t\t}\n\t\t\tif (z > hz) {\n\t\t\t\thz = z;\n\t\t\t}\n\t\t}\n\t\t// about the model ORIGIN, not the bounding box centre: the shadow is\n\t\t// centred on where the object is placed, so an off-centre model needs\n\t\t// the blob wide enough to still cover it\n\t\tthis._shadowHalfX = hx;\n\t\tthis._shadowHalfZ = hz;\n\t\tthis._shadowHasHeight = hasVerticalExtent(src, this.vertexCount);\n\t\tthis._shadowGeomVersion = version;\n\t}\n\n\t/**\n\t * Draw this mesh's ground shadow: the shared blob quad, flattened onto the\n\t * ground plane beneath the object (#1515).\n\t *\n\t * The quad is shared by every shadow in the scene and its geometry is\n\t * uploaded once, so this is one draw and no allocation.\n\t * @param {WebGLRenderer} renderer - the active renderer\n\t * @ignore\n\t */\n\t_drawGroundShadow(renderer) {\n\t\tconst quad = getShadowQuad(renderer, this.lit === true, Mesh);\n\t\tconst model = this._modelMatrix.val;\n\t\tconst originX = model[12];\n\t\tconst originY = model[13];\n\t\tconst originZ = model[14];\n\n\t\tthis._measureShadowFootprint();\n\n\t\t// The footprint axes: the model-space half-extents carried through the\n\t\t// object's own rotation and scale, then flattened onto the ground.\n\t\t//\n\t\t// Taken from `currentTransform` + `meshScale` rather than from the model\n\t\t// matrix, and that distinction matters: a billboarded `Sprite3d` builds\n\t\t// its model matrix from a CAMERA-FACING basis, so reading that would\n\t\t// make its blob spin as the camera orbits. `currentTransform` carries\n\t\t// only what the object itself was turned by.\n\t\t//\n\t\t// Doing it as two axes rather than one radius is what gives a shadow\n\t\t// that matches the object: a thin upright panel gets a thin ellipse\n\t\t// lying along the panel — turning as the panel turns — instead of a disc\n\t\t// that reads as perpendicular to it.\n\t\tconst c = this.currentTransform.val;\n\t\tconst s = this.meshScale;\n\t\tconst zSign = this.rightHanded ? -1 : 1;\n\t\tconst hx = this._shadowHalfX * s;\n\t\tconst hz = this._shadowHalfZ * s;\n\t\t// Columns 0 and 2 of the horizontal basis, Y dropped. `_composeModelMatrix`\n\t\t// applies the axis bridge as a ROW scale — `diag(s, -s, ±s) · transform` —\n\t\t// so it is the world **Z component of both columns** that carries `zSign`,\n\t\t// not one whole column. Folding it into `hz` instead mirrors the basis\n\t\t// about world X, and a right-handed (glTF) caster's blob then turns the\n\t\t// WRONG WAY: an elongated prop rotated θ gets an ellipse at −θ.\n\t\tlet axX = c[0] * hx;\n\t\tlet axZ = c[2] * hx * zSign;\n\t\tlet azX = c[8] * hz;\n\t\tlet azZ = c[10] * hz * zSign;\n\t\tlet axLen = Math.hypot(axX, axZ);\n\t\tlet azLen = Math.hypot(azX, azZ);\n\n\t\t// A paper-thin caster — a `Sprite3d` billboard, a flat plate — has no\n\t\t// depth at all on one axis, and an honest projection of it is a\n\t\t// hairline, which reads as a rendering fault rather than a shadow. Give\n\t\t// the minor axis a floor relative to the major, so the blob stays a\n\t\t// blob: still clearly elongated along the object, never degenerate.\n\t\tconst minor = axLen > azLen ? azLen : axLen;\n\t\tconst major = axLen > azLen ? axLen : azLen;\n\t\tconst least = major * SHADOW_MIN_AXIS_RATIO;\n\t\tif (minor < least && major > 1e-6) {\n\t\t\tif (axLen < least) {\n\t\t\t\tif (axLen > 1e-6) {\n\t\t\t\t\tconst k = least / axLen;\n\t\t\t\t\taxX *= k;\n\t\t\t\t\taxZ *= k;\n\t\t\t\t} else {\n\t\t\t\t\t// nothing to scale up — grow along the perpendicular of the\n\t\t\t\t\t// axis that DOES have length, in the ground plane\n\t\t\t\t\taxX = (-azZ / azLen) * least;\n\t\t\t\t\taxZ = (azX / azLen) * least;\n\t\t\t\t}\n\t\t\t\taxLen = least;\n\t\t\t}\n\t\t\tif (azLen < least) {\n\t\t\t\tif (azLen > 1e-6) {\n\t\t\t\t\tconst k = least / azLen;\n\t\t\t\t\tazX *= k;\n\t\t\t\t\tazZ *= k;\n\t\t\t\t} else {\n\t\t\t\t\tazX = (-axZ / axLen) * least;\n\t\t\t\t\tazZ = (axX / axLen) * least;\n\t\t\t\t}\n\t\t\t\tazLen = least;\n\t\t\t}\n\t\t}\n\n\t\t// one scalar for the height fade and the lift — the blob's own size\n\t\tconst extent = (axLen + azLen) * 0.5;\n\n\t\t// Render space is Y-DOWN, so the floor sits at a GREATER Y than the\n\t\t// object standing on it. `bottom` is `max.y` for the same reason.\n\t\tlet groundY = this.shadowGroundY;\n\t\tlet strength = 1;\n\t\tif (groundY === undefined) {\n\t\t\tgroundY = this.getBounds3d().bottom;\n\t\t} else {\n\t\t\t// how far the object floats above that floor, as a fraction of its\n\t\t\t// own size — a blob that shrinks and fades is what reads as height\n\t\t\tconst height = groundY - originY;\n\t\t\tconst fade = 1 - height / (extent * SHADOW_FADE_SPAN);\n\t\t\tstrength = fade > 0 ? (fade < 1 ? fade : 1) : 0;\n\t\t\tif (strength === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// the quad is a unit square, so a half-extent of `k · axis` needs the\n\t\t// basis column to be twice that\n\t\tconst k = (0.5 + strength * 0.5) * 2 * SHADOW_SPREAD;\n\t\t// written into the quad's OWN model matrix rather than a module scratch:\n\t\t// it is the quad's placement, the renderer copies it when queueing the\n\t\t// deferred draw, and it keeps the placement inspectable from the outside\n\t\tif (quad._modelMatrix === undefined) {\n\t\t\tquad._modelMatrix = new Matrix3d();\n\t\t}\n\t\tconst out = quad._modelMatrix.val;\n\t\tout[0] = axX * k;\n\t\tout[1] = 0;\n\t\tout[2] = axZ * k;\n\t\tout[3] = 0;\n\t\tout[4] = 0;\n\t\tout[5] = 1;\n\t\tout[6] = 0;\n\t\tout[7] = 0;\n\t\tout[8] = azX * k;\n\t\tout[9] = 0;\n\t\tout[10] = azZ * k;\n\t\tout[11] = 0;\n\t\tout[12] = originX;\n\t\t// render space is Y-DOWN, so lifting off the floor is a SMALLER y\n\t\tout[13] = groundY - extent * SHADOW_LIFT;\n\t\tout[14] = originZ;\n\t\tout[15] = 1;\n\n\t\t// Stash and restore by hand rather than save()/restore(): restore()\n\t\t// runs setBlendMode, which would re-enable GL_BLEND for the rest of an\n\t\t// otherwise opaque mesh pass — MeshBatcher.bind() turned it off behind\n\t\t// that cache's back. Opacity must go through the global alpha too:\n\t\t// drawMesh builds its tint as currentTint.toUint32(getGlobalAlpha()),\n\t\t// and toUint32 ignores the colour's own alpha.\n\t\tconst tint = renderer.currentTint;\n\t\tconst savedR = tint.r;\n\t\tconst savedG = tint.g;\n\t\tconst savedB = tint.b;\n\t\t// the colour's OWN alpha, which `setColor(r, g, b)` resets to 1\n\t\tconst savedTintAlpha = tint.alpha;\n\t\tconst savedAlpha = renderer.getGlobalAlpha();\n\t\ttint.setColor(0, 0, 0);\n\t\trenderer.setGlobalAlpha(this.shadowOpacity * strength * savedAlpha);\n\t\ttry {\n\t\t\trenderer.drawMesh(quad, quad._modelMatrix);\n\t\t} finally {\n\t\t\ttint.setColor(savedR, savedG, savedB, savedTintAlpha);\n\t\t\trenderer.setGlobalAlpha(savedAlpha);\n\t\t}\n\t}\n\n\t/**\n\t * Draw the mesh (automatically called by melonJS). Picks between two\n\t * projection paths based on the camera that was active when this mesh\n\t * was added to the world (captured in {@link Mesh#onActivateEvent}):\n\t *\n\t * - **Camera3d** → world-space output via {@link Mesh#_projectVerticesWorld}.\n\t *   Vertices land in world coordinates at `(pos.x, pos.y, depth)`; the\n\t *   camera's perspective handles the final projection per-frame on the\n\t *   GPU. Triangle winding is reversed once at first draw so back-face\n\t *   culling stays correct under the Y-flip.\n\t * - **Camera2d** (or no camera) → legacy self-projection via\n\t *   {@link Mesh#projectionMatrix} × {@link Renderable#currentTransform},\n\t *   emitting vertices in pixel-ish space directly. Backwards-compatible\n\t *   with single-mesh-in-a-2D-scene usage.\n\t *\n\t * Multi-material meshes need no extra `drawMesh` calls per material vs\n\t * single-material — each material's diffuse color is baked into\n\t * `vertexColors` at construction time and pushed through the renderer's\n\t * per-vertex `aColor` (GPU backends) or per-triangle solid-fill (Canvas)\n\t * path. The GPU batchers may still chunk very large meshes across multiple\n\t * `drawElements` calls to fit its vertex/index buffer limits, and split the\n\t * draw once per entry in {@link Mesh#textureGroups} when the materials\n\t * carry different diffuse textures (#1573).\n\t *\n\t * The active path is picked from the `viewport` passed in by\n\t * `Container.draw`, NOT from the activation-time `_useWorldSpace`\n\t * flag. The flag is kept as a fallback only for callers that\n\t * invoke `draw(renderer)` without a viewport (tests, batched\n\t * builds). Without the per-draw read, a stage with multiple\n\t * cameras (Camera3d main + Camera2d minimap, say) would run the\n\t * wrong projection on whichever camera didn't match activation.\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the camera rendering this frame\n\t */\n\tdraw(renderer, viewport) {\n\t\t// Prefer the live viewport (per-draw) over the flag captured\n\t\t// at activation. Falling back to the flag when no viewport is\n\t\t// passed preserves test callers that call `draw(renderer)`\n\t\t// directly.\n\t\tconst useWorldSpace =\n\t\t\tviewport !== undefined\n\t\t\t\t? viewport instanceof Camera3d\n\t\t\t\t: this._useWorldSpace === true;\n\t\tif (useWorldSpace) {\n\t\t\tif (this._worldSpace !== true) {\n\t\t\t\tthis._setupWorldSpace(renderer.supportsRetainedMesh !== true);\n\t\t\t}\n\t\t\tif (renderer.supportsRetainedMesh === true) {\n\t\t\t\t// Retained path: the geometry already lives on the GPU in model\n\t\t\t\t// space, so nothing is transformed here. Where the mesh sits is\n\t\t\t\t// described by a matrix and applied by the shader, which is what\n\t\t\t\t// makes moving/rotating/scaling it free.\n\t\t\t\t//\n\t\t\t\t// The reflection bridge still inverts winding, but the fix is\n\t\t\t\t// `frontFace` at draw time rather than a reversed index copy, so\n\t\t\t\t// the authored order is what reaches the GPU — keep `indices`\n\t\t\t\t// agreeing with that, including when this instance was\n\t\t\t\t// previously drawn through the reversing path below.\n\t\t\t\tthis.indices = this._indicesOriginal;\n\t\t\t\trenderer.drawMesh(this, this._composeModelMatrix());\n\t\t\t\tif (\n\t\t\t\t\tthis._castsGroundShadow(renderer) === true &&\n\t\t\t\t\tthis.instanceLayout === undefined\n\t\t\t\t) {\n\t\t\t\t\t// after the object, so the shadow blends over whatever the\n\t\t\t\t\t// object did not cover. An InstancedMesh takes its own path\n\t\t\t\t\t// — one shadow draw for the whole set, not one per instance.\n\t\t\t\t\tthis._drawGroundShadow(renderer);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Camera3d path. The reflection bridge (Y-only negate) inverts\n\t\t\t// winding, so it needs the reversed indices to keep\n\t\t\t// `cullBackFaces: true` correct. The rotation bridge\n\t\t\t// (`rightHanded`: Y+Z negate) preserves winding, so the\n\t\t\t// original indices are already correct.\n\t\t\tthis.indices = this.rightHanded\n\t\t\t\t? this._indicesOriginal\n\t\t\t\t: this._indicesReversed;\n\t\t\t// Emit FINAL world coordinates: the node origin is baked into\n\t\t\t// pos/depth and the geometry carries its own extent. The anchor\n\t\t\t// offset is suppressed for this path via `applyAnchorTransform`\n\t\t\t// (set in `preDraw`), so it does not leak into the view matrix.\n\t\t\tthis._projectVerticesWorld(this.pos.x, this.pos.y, this.depth);\n\t\t\t// world-space normals — only when this mesh is lit (the unlit batcher\n\t\t\t// never reads `normals`, so projecting them otherwise is wasted\n\t\t\t// per-frame work; glTF meshes carry normals even when the scene has\n\t\t\t// no lights). No-op without source normals.\n\t\t\tif (this.lit === true) {\n\t\t\t\tthis._projectNormalsWorld();\n\t\t\t}\n\t\t} else {\n\t\t\t// Camera2d / no-camera path: restore the original winding.\n\t\t\t// Important when the same Mesh instance was previously drawn\n\t\t\t// under Camera3d (`_setupWorldSpace` ran) and is now being\n\t\t\t// re-rendered under a 2D camera — without this restore the\n\t\t\t// reversed indices stay in place and front-facing triangles\n\t\t\t// get culled, leaving the model looking inside-out.\n\t\t\tthis.indices = this._indicesOriginal;\n\t\t\tthis._projectVertices(this.pos.x, this.pos.y, 1000);\n\t\t}\n\t\trenderer.drawMesh(this);\n\t}\n\n\t/**\n\t * Compute a 2D convex hull polygon from the current projected vertices.\n\t * Useful for creating collision shapes.\n\t * @returns {Polygon} a convex hull polygon in local coordinates\n\t */\n\ttoPolygon() {\n\t\t// Draws no longer refresh `vertices` on the retained path, so project on\n\t\t// demand — this is a query, not a per-frame cost. Keyed off\n\t\t// `_worldSpace` (set once `_setupWorldSpace` has run) rather than the\n\t\t// activation-time `_useWorldSpace`: with several cameras on a stage the\n\t\t// two disagree, and reading a never-written `vertices` returns a hull\n\t\t// collapsed onto the origin.\n\t\tif (this._worldSpace === true) {\n\t\t\tthis._projectVerticesWorld(this.pos.x, this.pos.y, this.depth);\n\t\t}\n\t\t// update cached points from projected vertices\n\t\tfor (let i = 0; i < this.vertexCount; i++) {\n\t\t\tthis._hullPoints[i].set(this.vertices[i * 3], this.vertices[i * 3 + 1]);\n\t\t}\n\n\t\t// Graham scan convex hull (sorts in place, returns a subset)\n\t\tconst hull = convexHull(this._hullPoints);\n\n\t\tif (this._hullPolygon === null) {\n\t\t\tthis._hullPolygon = new Polygon(0, 0, hull);\n\t\t} else {\n\t\t\tthis._hullPolygon.setVertices(hull);\n\t\t}\n\n\t\treturn this._hullPolygon;\n\t}\n\n\t/**\n\t * Render the mesh at its current state (transforms, projection, tint) to an offscreen canvas.\n\t * The returned canvas can be used with `renderer.drawImage()`, as a `Sprite` image source,\n\t * or converted to an ImageBitmap via `createImageBitmap()`.\n\t * @returns {HTMLCanvasElement} an offscreen canvas containing the rendered mesh\n\t * @example\n\t * // snapshot the mesh and create a Sprite from it\n\t * const canvas = mesh.toCanvas();\n\t * const sprite = new me.Sprite(100, 100, { image: canvas });\n\t *\n\t * // or draw directly\n\t * renderer.drawImage(mesh.toCanvas(), 100, 100);\n\t */\n\ttoCanvas() {\n\t\tconst w = this.width;\n\t\tconst h = this.height;\n\n\t\t// project vertices into local space (no pos offset, no Z)\n\t\tthis._projectVertices(0, 0, false);\n\n\t\t// render to offscreen canvas using affine texture mapping\n\t\tconst canvas = document.createElement(\"canvas\");\n\t\tcanvas.width = w;\n\t\tcanvas.height = h;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\n\t\tconst image = this.texture.getTexture();\n\t\tconst imgW = image.width;\n\t\tconst imgH = image.height;\n\t\tconst uvs = this.uvs;\n\t\tconst indices = this.indices;\n\t\tconst vertices = this.vertices;\n\t\tconst cullBack = this.cullBackFaces === true;\n\n\t\t// build visible triangles sorted back-to-front\n\t\tconst tris = [];\n\t\tfor (let j = 0; j < indices.length; j += 3) {\n\t\t\tconst i0 = indices[j];\n\t\t\tconst i1 = indices[j + 1];\n\t\t\tconst i2 = indices[j + 2];\n\t\t\tconst x0 = vertices[i0 * 3];\n\t\t\tconst y0 = vertices[i0 * 3 + 1];\n\t\t\tconst x1 = vertices[i1 * 3];\n\t\t\tconst y1 = vertices[i1 * 3 + 1];\n\t\t\tconst x2 = vertices[i2 * 3];\n\t\t\tconst y2 = vertices[i2 * 3 + 1];\n\n\t\t\tif (cullBack) {\n\t\t\t\tconst cross = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);\n\t\t\t\tif (cross > 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttris.push(i0, i1, i2);\n\t\t}\n\n\t\t// draw each triangle\n\t\tfor (let t = 0; t < tris.length; t += 3) {\n\t\t\tconst i0 = tris[t];\n\t\t\tconst i1 = tris[t + 1];\n\t\t\tconst i2 = tris[t + 2];\n\t\t\tconst x0 = vertices[i0 * 3];\n\t\t\tconst y0 = vertices[i0 * 3 + 1];\n\t\t\tconst x1 = vertices[i1 * 3];\n\t\t\tconst y1 = vertices[i1 * 3 + 1];\n\t\t\tconst x2 = vertices[i2 * 3];\n\t\t\tconst y2 = vertices[i2 * 3 + 1];\n\n\t\t\tconst u0 = uvs[i0 * 2] * imgW;\n\t\t\tconst v0 = uvs[i0 * 2 + 1] * imgH;\n\t\t\tconst u1 = uvs[i1 * 2] * imgW;\n\t\t\tconst v1 = uvs[i1 * 2 + 1] * imgH;\n\t\t\tconst u2 = uvs[i2 * 2] * imgW;\n\t\t\tconst v2 = uvs[i2 * 2 + 1] * imgH;\n\n\t\t\tconst du1 = u1 - u0;\n\t\t\tconst dv1 = v1 - v0;\n\t\t\tconst du2 = u2 - u0;\n\t\t\tconst dv2 = v2 - v0;\n\t\t\tconst rawDet = du1 * dv2 - du2 * dv1;\n\n\t\t\tctx.save();\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x0, y0);\n\t\t\tctx.lineTo(x1, y1);\n\t\t\tctx.lineTo(x2, y2);\n\n\t\t\tif (rawDet === 0) {\n\t\t\t\t// degenerate UV — fill with sampled solid color\n\t\t\t\tctx.closePath();\n\t\t\t\tconst tmpCanvas = document.createElement(\"canvas\");\n\t\t\t\ttmpCanvas.width = 1;\n\t\t\t\ttmpCanvas.height = 1;\n\t\t\t\tconst tmpCtx = tmpCanvas.getContext(\"2d\");\n\t\t\t\tconst sx = Math.min(Math.max(Math.round(u0), 0), imgW - 1);\n\t\t\t\tconst sy = Math.min(Math.max(Math.round(v0), 0), imgH - 1);\n\t\t\t\ttmpCtx.drawImage(image, sx, sy, 1, 1, 0, 0, 1, 1);\n\t\t\t\tconst pixel = tmpCtx.getImageData(0, 0, 1, 1).data;\n\t\t\t\tctx.fillStyle = `rgb(${pixel[0]},${pixel[1]},${pixel[2]})`;\n\t\t\t\tctx.fill();\n\t\t\t} else {\n\t\t\t\tconst dx1 = x1 - x0;\n\t\t\t\tconst dy1 = y1 - y0;\n\t\t\t\tconst dx2 = x2 - x0;\n\t\t\t\tconst dy2 = y2 - y0;\n\t\t\t\tconst det = 1 / rawDet;\n\t\t\t\tconst a = (dv2 * dx1 - dv1 * dx2) * det;\n\t\t\t\tconst b = (dv2 * dy1 - dv1 * dy2) * det;\n\t\t\t\tconst c = (du1 * dx2 - du2 * dx1) * det;\n\t\t\t\tconst d = (du1 * dy2 - du2 * dy1) * det;\n\n\t\t\t\tctx.clip();\n\t\t\t\tctx.transform(a, b, c, d, x0 - a * u0 - c * v0, y0 - b * u0 - d * v0);\n\t\t\t\tctx.drawImage(image, 0, 0);\n\t\t\t}\n\t\t\tctx.restore();\n\t\t}\n\n\t\treturn canvas;\n\t}\n\n\t/**\n\t * Render the mesh at its current state to an ImageBitmap.\n\t * Useful for creating textures or sprites from the rendered mesh.\n\t * @returns {Promise<ImageBitmap>} a promise that resolves to an ImageBitmap of the rendered mesh\n\t * @example\n\t * const bitmap = await mesh.toImageBitmap();\n\t * const sprite = new me.Sprite(100, 100, { image: bitmap });\n\t */\n\ttoImageBitmap() {\n\t\treturn createImageBitmap(this.toCanvas());\n\t}\n\n\t/**\n\t * Release this mesh's GPU-resident geometry, if a renderer is holding any,\n\t * then destroy the renderable.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// the fallback is load-bearing: a mesh that was never parented has no\n\t\t// `parentApp`, and skipping the release would leak its GPU geometry\n\t\tconst renderer = this.parentApp?.renderer ?? game.renderer;\n\t\trenderer?.deleteMeshGeometry?.(this);\n\t\tsuper.destroy();\n\t}\n}\n","import Camera3d from \"../camera/camera3d.ts\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { AABB3d } from \"../physics/broadphase/aabb3d.ts\";\nimport {\n\tinstanceRecordLayout,\n\treadInstanceTransform,\n\twriteIdentityTransform,\n\twriteInstanceTransform,\n} from \"../video/gpu/instancerecord.ts\";\nimport { getInstancedShadowQuad } from \"./groundshadow.js\";\nimport Mesh from \"./mesh.js\";\n\n// scratch reused by getBounds3d(); never handed out\nconst _instanceMatrix = new Matrix3d();\n\n// scratch for the flattened group matrix a shadow pass draws with —\n// synchronous, single-use per draw, never held\nconst _SHADOW_MATRIX = new Matrix3d();\nconst _instanceBounds = new AABB3d();\n// scratch for dirtyRange() — read synchronously and never retained\nconst _dirtySpan = [0, 0];\n\n/**\n * A {@link Mesh} drawn many times from one copy of its geometry.\n *\n * Ordinary meshes pay for every repeat: a forest of five thousand trees is\n * five thousand geometries on the GPU, each re-uploaded when it changes. An\n * `InstancedMesh` uploads the geometry **once** and stamps out copies from a\n * small per-instance record, so cost scales with the number of instances\n * rather than with `instances × vertices`, and the whole thing draws in a\n * single call.\n *\n * Every `Mesh` setting works unchanged — `model` + `material` from an OBJ,\n * raw `vertices`/`uvs`/`indices`, `lit`, `cullBackFaces`, `rightHanded`,\n * `tint`, `textureRepeat`, the lot. What an instanced mesh adds is the\n * instance buffer:\n *\n * - a **transform** per instance, always present;\n * - a **colour** per instance, when `instanceColors` is declared, multiplied\n *   into the mesh tint;\n * - an opaque **`vec4`** per instance, when `instanceData` is declared. The\n *   built-in lit shading reads its `rgb` as emissive; a custom\n *   {@link Mesh#shader} may read it as a wind phase, an atlas offset, a\n *   random seed — whatever the shader wants.\n *\n * The mesh's own position, rotation and scale keep their ordinary meaning and\n * act as the **group** transform: moving an `InstancedMesh` moves every\n * instance with one uniform write and re-uploads nothing.\n *\n * Requires a GPU backend (`renderer.supportsInstancing`). Under the Canvas\n * renderer the instances are drawn one at a time through the ordinary CPU\n * mesh path — correct, but without any of the benefit.\n * @augments Mesh\n * @category Rendering\n * @example\n * const forest = new me.InstancedMesh(0, 0, {\n *     model: \"tree\",\n *     material: \"tree\",\n *     width: 64,\n *     lit: true,\n *     instanceCount: 5000,     // pre-allocate\n *     instanceColors: true,\n *     instanceData: true,\n * });\n *\n * const placement = new me.Matrix3d();   // one scratch — zero allocation\n * for (let i = 0; i < forest.instanceCount; i++) {\n *     placement.identity().translate(x, y, z).scale(s);\n *     forest.setInstance(i, placement);\n *     forest.setInstanceColor(i, autumnTint);\n *     forest.setInstanceData(i, windPhase, seed, 0, 0);\n * }\n *\n * // draw only the nearest 1200 — no re-upload, just a smaller draw\n * forest.visibleInstanceCount = 1200;\n * app.world.addChild(forest, 10);\n */\nexport default class InstancedMesh extends Mesh {\n\t/**\n\t * @param {number} x - the x coordinate of the group origin\n\t * @param {number} y - the y coordinate of the group origin\n\t * @param {object} settings - every {@link Mesh} setting, plus those below\n\t * @param {number} [settings.instanceCount=0] - number of instances to pre-allocate. Instances start at the group origin (identity transform) until placed; `addInstance` grows past this.\n\t * @param {boolean} [settings.instanceColors=false] - give each instance its own colour (16 bytes per instance), multiplied into the mesh tint\n\t * @param {boolean} [settings.instanceData=false] - give each instance an opaque `vec4` (16 bytes per instance). Read as emissive by the built-in lit shading, or as anything at all by a custom mesh shader.\n\t */\n\tconstructor(x, y, settings) {\n\t\tsuper(x, y, settings);\n\n\t\t/**\n\t\t * How this mesh's per-instance records are laid out — record width and\n\t\t * the offset of each opt-in slot. Consumed by the batchers to describe\n\t\t * the instance vertex buffer.\n\t\t * @type {object}\n\t\t * @ignore\n\t\t */\n\t\tthis.instanceLayout = instanceRecordLayout(\n\t\t\tsettings.instanceColors === true,\n\t\t\tsettings.instanceData === true,\n\t\t);\n\n\t\t/**\n\t\t * The packed instance records, `instanceLayout.floats` per instance.\n\t\t * Editable in place for bulk updates — announce those with\n\t\t * {@link InstancedMesh#needsInstanceUpdate} or\n\t\t * {@link InstancedMesh#markInstancesDirty}, since writing here bypasses\n\t\t * the setters that track the dirty range.\n\t\t * @type {Float32Array}\n\t\t */\n\t\tthis.instanceBuffer = new Float32Array(0);\n\n\t\t/**\n\t\t * bumped whenever the buffer is reallocated, so a renderer holding a\n\t\t * GPU copy knows its capacity assumption is stale\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis._instanceVersion = 0;\n\n\t\t// dirty span in floats, as [first, lastExclusive); empty when first\n\t\t// exceeds last\n\t\t// Monotonic revision of the record CONTENTS. The dirty span alone is\n\t\t// not enough once more than one GPU buffer holds a copy (the unlit and\n\t\t// lit batchers keep separate ones, and `lit` is a public field): the\n\t\t// first consumer to drain the span would leave the second stuck on\n\t\t// stale records forever. Each buffer records the revision it reached,\n\t\t// so a consumer that missed an edit re-uploads in full.\n\t\t/** @ignore */\n\t\tthis._instanceRevision = 0;\n\t\t// the revision the CURRENT span started from — a consumer at exactly\n\t\t// this revision can take the cheap partial upload\n\t\t/** @ignore */\n\t\tthis._spanFromRevision = 0;\n\n\t\t/** @ignore */\n\t\tthis._dirtyFirst = Infinity;\n\t\t/** @ignore */\n\t\tthis._dirtyLast = 0;\n\n\t\t/** @ignore */\n\t\tthis._instanceCount = 0;\n\t\t/** @ignore */\n\t\tthis._visibleInstanceCount = -1;\n\t\t// revision the cull box was last sized for\n\t\t/** @ignore */\n\t\tthis._cullRevision = -1;\n\n\t\tif (settings.instanceCount > 0) {\n\t\t\tthis.instanceCount = settings.instanceCount;\n\t\t}\n\t}\n\n\t/**\n\t * How many instances this mesh holds.\n\t *\n\t * Growing pre-allocates the new records as identity transforms (an\n\t * unplaced instance sits at the group origin rather than collapsing onto\n\t * a zero matrix); shrinking keeps the allocation, so a count that\n\t * oscillates does not thrash it.\n\t * @type {number}\n\t */\n\tget instanceCount() {\n\t\treturn this._instanceCount;\n\t}\n\n\tset instanceCount(count) {\n\t\t// `| 0` alone wraps anything past 2^31 to a negative and NaN to 0, so\n\t\t// an accidental `1e10` would silently empty the mesh rather than fail\n\t\tconst wanted = Number.isFinite(count) ? Math.max(0, Math.trunc(count)) : 0;\n\t\tconst floats = this.instanceLayout.floats;\n\t\tif (wanted * floats > this.instanceBuffer.length) {\n\t\t\t// grow geometrically so building a large set one addInstance at a\n\t\t\t// time stays linear overall rather than quadratic\n\t\t\tconst capacity = Math.max(wanted, this._instanceCount * 2, 8);\n\t\t\tconst grown = new Float32Array(capacity * floats);\n\t\t\tgrown.set(this.instanceBuffer);\n\t\t\tthis.instanceBuffer = grown;\n\t\t\tthis._instanceVersion++;\n\t\t}\n\t\t// initialize whatever is newly in range\n\t\tfor (let i = this._instanceCount; i < wanted; i++) {\n\t\t\twriteIdentityTransform(this.instanceBuffer, i * floats);\n\t\t\tif (this.instanceLayout.hasColor) {\n\t\t\t\tconst at = i * floats + this.instanceLayout.colorOffset;\n\t\t\t\tthis.instanceBuffer[at] = 1;\n\t\t\t\tthis.instanceBuffer[at + 1] = 1;\n\t\t\t\tthis.instanceBuffer[at + 2] = 1;\n\t\t\t\tthis.instanceBuffer[at + 3] = 1;\n\t\t\t}\n\t\t\tif (this.instanceLayout.hasData) {\n\t\t\t\t// a recycled slot still holds the dead instance's vec4, and the\n\t\t\t\t// built-in shading reads its rgb as emissive — so a brand-new\n\t\t\t\t// instance would inherit the old one's glow\n\t\t\t\tconst at = i * floats + this.instanceLayout.dataOffset;\n\t\t\t\tthis.instanceBuffer[at] = 0;\n\t\t\t\tthis.instanceBuffer[at + 1] = 0;\n\t\t\t\tthis.instanceBuffer[at + 2] = 0;\n\t\t\t\tthis.instanceBuffer[at + 3] = 0;\n\t\t\t}\n\t\t}\n\t\tif (wanted > this._instanceCount) {\n\t\t\tthis.markInstancesDirty(\n\t\t\t\tthis._instanceCount,\n\t\t\t\twanted - this._instanceCount,\n\t\t\t);\n\t\t}\n\t\tthis._instanceCount = wanted;\n\t}\n\n\t/**\n\t * How many instances are actually drawn, counted from the first.\n\t *\n\t * The cheap culling and level-of-detail knob: sort the instances by\n\t * distance once, then draw fewer of them by moving this single number —\n\t * no re-upload, no rebuild. Defaults to every instance (`-1`).\n\t * @type {number}\n\t * @example\n\t * forest.visibleInstanceCount = playerIsIndoors ? 0 : 1200;\n\t */\n\tget visibleInstanceCount() {\n\t\treturn this._visibleInstanceCount < 0\n\t\t\t? this._instanceCount\n\t\t\t: Math.min(this._visibleInstanceCount, this._instanceCount);\n\t}\n\n\tset visibleInstanceCount(count) {\n\t\t// NaN would read as 0 and silently stop the mesh drawing\n\t\tif (!Number.isFinite(count)) {\n\t\t\tthis._visibleInstanceCount = -1;\n\t\t\treturn;\n\t\t}\n\t\tthis._visibleInstanceCount = count < 0 ? -1 : Math.trunc(count);\n\t}\n\n\t/**\n\t * Append an instance, growing the buffer as needed.\n\t * @param {Matrix3d} [transform] - where this instance sits, relative to the group. Defaults to the group origin.\n\t * @param {object} [options] - optional per-instance slots\n\t * @param {Color} [options.color] - instance colour (requires `instanceColors`)\n\t * @param {number[]} [options.data] - four numbers for the custom slot (requires `instanceData`)\n\t * @returns {number} the new instance's index\n\t */\n\taddInstance(transform, options) {\n\t\tconst index = this._instanceCount;\n\t\tthis.instanceCount = index + 1;\n\t\tif (transform !== undefined) {\n\t\t\tthis.setInstance(index, transform);\n\t\t}\n\t\tif (options !== undefined) {\n\t\t\tif (options.color !== undefined) {\n\t\t\t\tthis.setInstanceColor(index, options.color);\n\t\t\t}\n\t\t\tif (options.data !== undefined) {\n\t\t\t\t// a short array would leave components undefined, and\n\t\t\t\t// `Float32Array[i] = undefined` is NaN — which propagates\n\t\t\t\t// through the shader as black or discarded fragments\n\t\t\t\tconst [x = 0, y = 0, z = 0, w = 0] = options.data;\n\t\t\t\tthis.setInstanceData(index, x, y, z, w);\n\t\t\t}\n\t\t}\n\t\treturn index;\n\t}\n\n\t/**\n\t * Remove an instance by moving the last one into its place.\n\t *\n\t * Swapping is what keeps this O(1) instead of shifting every record after\n\t * the hole — but it means **indices are not stable across a removal**: the\n\t * instance that was last now answers to `index`. Callers holding indices\n\t * must re-read them, or avoid removal in favour of\n\t * {@link InstancedMesh#visibleInstanceCount}.\n\t * @param {number} index - the instance to remove\n\t */\n\tremoveInstance(index) {\n\t\tconst last = this._instanceCount - 1;\n\t\tif (index < 0 || index > last) {\n\t\t\treturn;\n\t\t}\n\t\tconst floats = this.instanceLayout.floats;\n\t\tif (index !== last) {\n\t\t\tthis.instanceBuffer.copyWithin(\n\t\t\t\tindex * floats,\n\t\t\t\tlast * floats,\n\t\t\t\t(last + 1) * floats,\n\t\t\t);\n\t\t\tthis.markInstancesDirty(index, 1);\n\t\t}\n\t\tthis._instanceCount = last;\n\t}\n\n\t/**\n\t * Place one instance.\n\t * @param {number} index - the instance to place\n\t * @param {Matrix3d} transform - where it sits, relative to the group\n\t */\n\tsetInstance(index, transform) {\n\t\tif (index < 0 || index >= this._instanceCount) {\n\t\t\treturn;\n\t\t}\n\t\twriteInstanceTransform(\n\t\t\tthis.instanceBuffer,\n\t\t\tindex * this.instanceLayout.floats,\n\t\t\ttransform,\n\t\t);\n\t\tthis.markInstancesDirty(index, 1);\n\t}\n\n\t/**\n\t * Read one instance's transform back.\n\t * @param {number} index - the instance to read\n\t * @param {Matrix3d} [out] - matrix to write into; a new one is allocated when omitted\n\t * @returns {Matrix3d|undefined} the transform, or `undefined` for an out-of-range index\n\t */\n\tgetInstance(index, out = new Matrix3d()) {\n\t\tif (index < 0 || index >= this._instanceCount) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn readInstanceTransform(\n\t\t\tthis.instanceBuffer,\n\t\t\tindex * this.instanceLayout.floats,\n\t\t\tout,\n\t\t);\n\t}\n\n\t/**\n\t * Set one instance's colour, multiplied into the mesh tint.\n\t * @param {number} index - the instance to colour\n\t * @param {Color} color - the instance colour\n\t */\n\tsetInstanceColor(index, color) {\n\t\tconst layout = this.instanceLayout;\n\t\tif (!layout.hasColor || index < 0 || index >= this._instanceCount) {\n\t\t\treturn;\n\t\t}\n\t\tconst at = index * layout.floats + layout.colorOffset;\n\t\tthis.instanceBuffer[at] = color.r / 255;\n\t\tthis.instanceBuffer[at + 1] = color.g / 255;\n\t\tthis.instanceBuffer[at + 2] = color.b / 255;\n\t\tthis.instanceBuffer[at + 3] = color.alpha;\n\t\tthis.markInstancesDirty(index, 1);\n\t}\n\n\t/**\n\t * Set one instance's custom `vec4`.\n\t * @param {number} index - the instance to write\n\t * @param {number} x - first component (emissive red, under the built-in lit shading)\n\t * @param {number} y - second component\n\t * @param {number} z - third component\n\t * @param {number} [w=0] - fourth component\n\t */\n\tsetInstanceData(index, x, y, z, w = 0) {\n\t\tconst layout = this.instanceLayout;\n\t\tif (!layout.hasData || index < 0 || index >= this._instanceCount) {\n\t\t\treturn;\n\t\t}\n\t\tconst at = index * layout.floats + layout.dataOffset;\n\t\tthis.instanceBuffer[at] = x;\n\t\tthis.instanceBuffer[at + 1] = y;\n\t\tthis.instanceBuffer[at + 2] = z;\n\t\tthis.instanceBuffer[at + 3] = w;\n\t\tthis.markInstancesDirty(index, 1);\n\t}\n\n\t/**\n\t * Announce that a span of instances was edited directly through\n\t * {@link InstancedMesh#instanceBuffer}, so the next draw re-uploads it.\n\t * The per-instance setters do this themselves.\n\t * @param {number} first - first instance changed\n\t * @param {number} count - how many\n\t */\n\tmarkInstancesDirty(first, count) {\n\t\tconst floats = this.instanceLayout.floats;\n\t\t// clamp to what is actually allocated: an out-of-range span reaches\n\t\t// the upload as an out-of-bounds typed-array view and throws from\n\t\t// inside the draw loop\n\t\tconst limit = this.instanceBuffer.length;\n\t\tconst from = Math.min(Math.max(0, first | 0) * floats, limit);\n\t\tconst to = Math.min(Math.max(0, (first | 0) + (count | 0)) * floats, limit);\n\t\tif (to <= from) {\n\t\t\treturn;\n\t\t}\n\t\tif (this._dirtyLast <= this._dirtyFirst) {\n\t\t\t// the span was empty: remember what revision consumers must be at\n\t\t\t// to take the cheap partial path\n\t\t\tthis._spanFromRevision = this._instanceRevision;\n\t\t}\n\t\tif (from < this._dirtyFirst) {\n\t\t\tthis._dirtyFirst = from;\n\t\t}\n\t\tif (to > this._dirtyLast) {\n\t\t\tthis._dirtyLast = to;\n\t\t}\n\t\tthis._instanceRevision++;\n\t}\n\n\t/**\n\t * Announce that the whole instance buffer was edited in place. The\n\t * instancing counterpart of {@link Mesh#needsUpdate} — and, like it, a\n\t * signal rather than a state, so it is write-only.\n\t * @type {boolean}\n\t * @example\n\t * forest.instanceBuffer.set(myRecords);\n\t * forest.needsInstanceUpdate = true;\n\t */\n\t// eslint-disable-next-line accessor-pairs\n\tset needsInstanceUpdate(value) {\n\t\tif (value !== false) {\n\t\t\tthis.markInstancesDirty(0, this._instanceCount);\n\t\t}\n\t}\n\n\t/**\n\t * The dirty span, in floats, as `[first, lastExclusive)`. Empty when\n\t * nothing changed since the last upload.\n\t * @returns {number[]} the span\n\t * @ignore\n\t */\n\tdirtyRange() {\n\t\t// reuses one array: this is read once per instanced mesh per frame,\n\t\t// and the class is sold on allocating nothing in the steady state\n\t\t_dirtySpan[0] = this._dirtyLast > this._dirtyFirst ? this._dirtyFirst : 0;\n\t\t_dirtySpan[1] =\n\t\t\tthis._dirtyLast > this._dirtyFirst\n\t\t\t\t? this._dirtyLast - this._dirtyFirst\n\t\t\t\t: 0;\n\t\treturn _dirtySpan;\n\t}\n\n\t/**\n\t * What one GPU buffer must upload to catch up, given the revision it last\n\t * reached. Replaces a shared \"clear the dirty flag\" step, which the first\n\t * consumer would drain out from under the second.\n\t * @param {number} uploadedRevision - the revision that buffer holds\n\t * @returns {object} `{first, count, full, revision}` — float span, or `full`\n\t * @ignore\n\t */\n\tinstanceUpload(uploadedRevision) {\n\t\tconst revision = this._instanceRevision;\n\t\tif (uploadedRevision === revision) {\n\t\t\treturn { first: 0, count: 0, full: false, revision };\n\t\t}\n\t\tif (uploadedRevision === this._spanFromRevision) {\n\t\t\tconst [first, count] = this.dirtyRange();\n\t\t\treturn { first, count, full: false, revision };\n\t\t}\n\t\t// this buffer missed edits the span no longer describes\n\t\treturn { first: 0, count: 0, full: true, revision };\n\t}\n\n\t/**\n\t * Called by the batcher once the dirty span has been uploaded.\n\t * @ignore\n\t */\n\tclearInstanceDirty() {\n\t\tthis._dirtyFirst = Infinity;\n\t\tthis._dirtyLast = 0;\n\t\tthis._spanFromRevision = this._instanceRevision;\n\t}\n\n\t/**\n\t * The world-space bounding box of every instance, so the whole group\n\t * frustum-culls as one object.\n\t *\n\t * Bounds the prototype geometry through each instance transform and then\n\t * through the group placement — correct before the first draw, like the\n\t * base implementation, and independent of what any draw left behind.\n\t * @returns {AABB3d} the union of all instance bounds (reused instance)\n\t */\n\t/**\n\t * Resize the renderable's own 2D bounds box so it encloses every\n\t * instance.\n\t *\n\t * This is what makes the group frustum-cull correctly. `Camera3d.isVisible`\n\t * tests a sphere derived from `getBounds()` — the flat width/height box —\n\t * **not** {@link InstancedMesh#getBounds3d}. Left at the prototype's size,\n\t * a forest scattered over thousands of units presents a box a few tens of\n\t * pixels across at the group origin: pan the camera just past that origin\n\t * and every instance disappears at once, while still on screen.\n\t * @ignore\n\t */\n\t_refreshCullVolume() {\n\t\tif (this._instanceCount === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst bounds = super.getBounds();\n\t\tif (this._cullRevision !== this._instanceRevision) {\n\t\t\tthis._cullRevision = this._instanceRevision;\n\t\t\tconst box = this.getBounds3d();\n\t\t\tconst origin = this._composeModelMatrix().val;\n\t\t\tlet radius = 0;\n\t\t\tfor (const x of [box.min.x, box.max.x]) {\n\t\t\t\tfor (const y of [box.min.y, box.max.y]) {\n\t\t\t\t\tfor (const z of [box.min.z, box.max.z]) {\n\t\t\t\t\t\tradius = Math.max(\n\t\t\t\t\t\t\tradius,\n\t\t\t\t\t\t\tMath.hypot(x - origin[12], y - origin[13], z - origin[14]),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._cullRadius = Math.max(radius, 1);\n\t\t}\n\t\t// Widen the box rather than resizing the renderable: `width`/`height`\n\t\t// are the mesh's own geometry scale and feed the model matrix, so\n\t\t// growing them would grow the mesh. Camera3d only reads the box's\n\t\t// width/height (its centre comes from getAbsolutePosition), so a\n\t\t// symmetric box of the right size is all that is needed.\n\t\tconst r = this._cullRadius;\n\t\tbounds.setMinMax(-r, -r, r, r);\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * The 2D bounds box, widened to enclose every instance.\n\t *\n\t * This is the box `Camera3d.isVisible` actually culls against — left at\n\t * the prototype's size, a scatter spanning thousands of units would\n\t * vanish wholesale the moment the group origin left the frustum.\n\t * @returns {Bounds} the bounds\n\t */\n\tgetBounds() {\n\t\treturn this._refreshCullVolume() ?? super.getBounds();\n\t}\n\n\tgetBounds3d() {\n\t\tif (this._bounds3d === undefined) {\n\t\t\t/** @ignore */\n\t\t\tthis._bounds3d = new AABB3d();\n\t\t}\n\t\tconst count = this._instanceCount;\n\t\tif (count === 0) {\n\t\t\treturn super.getBounds3d();\n\t\t}\n\t\tconst group = this._composeModelMatrix();\n\t\tconst floats = this.instanceLayout.floats;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\treadInstanceTransform(this.instanceBuffer, i * floats, _instanceMatrix);\n\t\t\t// group × instance — the order the shader applies them in\n\t\t\tconst placed = _instanceBounds.fromVertices(\n\t\t\t\tthis.originalVertices,\n\t\t\t\tthis.vertexCount,\n\t\t\t\tmultiplyInto(group, _instanceMatrix).val,\n\t\t\t);\n\t\t\t// the first fold clears, so the union starts from this instance\n\t\t\t// rather than from whatever the previous frame left behind\n\t\t\tthis._bounds3d.addAABB(placed, i === 0);\n\t\t}\n\t\treturn this._bounds3d;\n\t}\n\n\t/**\n\t * Draw the instanced mesh. Under a GPU backend this is one instanced\n\t * draw call; the Canvas renderer has no instancing, so each instance is\n\t * drawn through the ordinary mesh path instead.\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the camera rendering this frame\n\t */\n\tdraw(renderer, viewport) {\n\t\tif (this._instanceCount === 0 || this.visibleInstanceCount === 0) {\n\t\t\treturn;\n\t\t}\n\t\tthis._refreshCullVolume();\n\t\t// The instanced GPU path is a world-space (Camera3d) path, exactly as\n\t\t// the retained mesh path is: it hands the renderer a model matrix and\n\t\t// lets the shader place the geometry. Under a 2D camera the base class\n\t\t// pre-projects vertices on the CPU instead, so taking the instanced\n\t\t// path there would apply a different projection AND leak the anchor\n\t\t// transform into the shared view matrix. Fall back per instance.\n\t\tconst useWorldSpace =\n\t\t\tviewport !== undefined\n\t\t\t\t? viewport instanceof Camera3d\n\t\t\t\t: this._useWorldSpace === true;\n\t\tif (renderer.supportsInstancing !== true || !useWorldSpace) {\n\t\t\tthis._drawInstancesIndividually(renderer, viewport);\n\t\t\treturn;\n\t\t}\n\t\tif (this._worldSpace !== true) {\n\t\t\tthis._setupWorldSpace(false);\n\t\t}\n\t\tthis.indices = this._indicesOriginal;\n\t\trenderer.drawMesh(this, this._composeModelMatrix());\n\t\tif (this._castsGroundShadow(renderer) === true) {\n\t\t\tthis._drawInstancedGroundShadow(renderer);\n\t\t}\n\t}\n\n\t/**\n\t * Fallback for renderers without instancing (Canvas): walk the instances\n\t * and draw the prototype once per instance, composing the group and\n\t * instance transforms by hand. Correct, and as slow as the uninstanced\n\t * scene it replaces — the point is that a scene authored for instancing\n\t * still renders everywhere.\n\t * @param {CanvasRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the camera rendering this frame\n\t * @ignore\n\t */\n\t_drawInstancesIndividually(renderer, viewport) {\n\t\tconst count = this.visibleInstanceCount;\n\t\tconst floats = this.instanceLayout.floats;\n\t\tconst savedX = this.pos.x;\n\t\tconst savedY = this.pos.y;\n\t\tconst savedZ = this.pos.z;\n\t\ttry {\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\treadInstanceTransform(this.instanceBuffer, i * floats, _instanceMatrix);\n\t\t\t\t// Where the instance lands is the TRANSLATION OF `group ×\n\t\t\t\t// instance`, not the raw record: the record is in model space,\n\t\t\t\t// and the group matrix carries the mesh scale and the Y/Z axis\n\t\t\t\t// bridge. Adding the raw value collapses the scatter toward the\n\t\t\t\t// origin and mirrors it in Y.\n\t\t\t\tconst placed = multiplyInto(\n\t\t\t\t\tthis._composeModelMatrix(),\n\t\t\t\t\t_instanceMatrix,\n\t\t\t\t).val;\n\t\t\t\tthis.pos.set(placed[12], placed[13], placed[14]);\n\t\t\t\tsuper.draw(renderer, viewport);\n\t\t\t}\n\t\t} finally {\n\t\t\t// a throw mid-loop would otherwise strand the renderable at\n\t\t\t// whichever instance was being drawn\n\t\t\tthis.pos.set(savedX, savedY, savedZ);\n\t\t}\n\t}\n\n\t/**\n\t * Release the instance records along with the mesh.\n\t * @ignore\n\t */\n\t/**\n\t * Draw a ground shadow for every visible instance — one call for the whole\n\t * set, over the instance buffer the mesh itself just drew from (#1515).\n\t *\n\t * The group matrix is passed with its Y basis column zeroed and its\n\t * translation Y set to the ground, so whatever Y the shadow vertex stage\n\t * produces is flattened onto the floor. That is what lets the ground\n\t * height reach the shader without a new uniform.\n\t *\n\t * One ground height serves the whole set — flat terrain. Per-instance\n\t * ground height would have to ride the `instanceData` slot, which the\n\t * built-in shading already reads as emissive.\n\t * @param {WebGLRenderer} renderer - the active renderer\n\t * @ignore\n\t */\n\t_drawInstancedGroundShadow(renderer) {\n\t\tif (typeof renderer.drawInstancedShadow !== \"function\") {\n\t\t\treturn;\n\t\t}\n\t\t// Sized from the PROTOTYPE's own footprint, with the same contact\n\t\t// spread and minor-axis floor the per-object tier applies — otherwise\n\t\t// the identical asset draws a different shadow instanced than it does\n\t\t// standalone. The instanced vertex stage multiplies these by each\n\t\t// record's horizontal scale.\n\t\tthis._measureShadowFootprint();\n\t\tconst spread = 1.2;\n\t\tlet hx = this._shadowHalfX * spread;\n\t\tlet hz = this._shadowHalfZ * spread;\n\t\tconst major = hx > hz ? hx : hz;\n\t\tconst least = major * 0.5;\n\t\tif (major > 1e-6) {\n\t\t\thx = hx < least ? least : hx;\n\t\t\thz = hz < least ? least : hz;\n\t\t}\n\t\tconst quad = getInstancedShadowQuad(\n\t\t\tthis,\n\t\t\tMesh,\n\t\t\thx,\n\t\t\thz,\n\t\t\tthis._geometryVersion ?? 0,\n\t\t);\n\t\tconst group = this._modelMatrix.val;\n\t\tconst out = _SHADOW_MATRIX.val;\n\t\tfor (let i = 0; i < 16; i++) {\n\t\t\tout[i] = group[i];\n\t\t}\n\t\t// Zero the Y ROW — `out[1]`, `out[5]`, `out[9]`, the Y components of all\n\t\t// three basis columns — not the Y column. Clearing the column alone\n\t\t// kills only the instance's own height; the X and Z columns keep their\n\t\t// Y components, so a group rotated about X or Z would tilt the blobs\n\t\t// onto a slanted plane and sink half of them below the floor. Zeroing\n\t\t// the row makes every output Y the translation, whatever the rotation.\n\t\tout[1] = 0;\n\t\tout[5] = 0;\n\t\tout[9] = 0;\n\t\t// ...and put that plane at the ground. Render space is Y-DOWN, so the\n\t\t// floor is a GREATER Y than the objects standing on it.\n\t\t// lifted a hair off the floor, for the reason spelled out at\n\t\t// `SHADOW_LIFT` in mesh.js: a coplanar blob is order-dependent. Y is\n\t\t// DOWN, so off the floor is a smaller y.\n\t\tconst ground =\n\t\t\tthis.shadowGroundY !== undefined\n\t\t\t\t? this.shadowGroundY\n\t\t\t\t: this.getBounds3d().bottom;\n\t\tout[13] = ground - this.meshScale * 0.01;\n\n\t\tconst tint = renderer.currentTint;\n\t\tconst savedR = tint.r;\n\t\tconst savedG = tint.g;\n\t\tconst savedB = tint.b;\n\t\t// the colour's OWN alpha, which `setColor(r, g, b)` resets to 1\n\t\tconst savedTintAlpha = tint.alpha;\n\t\tconst savedAlpha = renderer.getGlobalAlpha();\n\t\ttint.setColor(0, 0, 0);\n\t\trenderer.setGlobalAlpha(this.shadowOpacity * savedAlpha);\n\t\ttry {\n\t\t\trenderer.drawInstancedShadow(this, _SHADOW_MATRIX, quad);\n\t\t} finally {\n\t\t\ttint.setColor(savedR, savedG, savedB, savedTintAlpha);\n\t\t\trenderer.setGlobalAlpha(savedAlpha);\n\t\t}\n\t}\n\n\tdestroy() {\n\t\tthis.instanceBuffer = new Float32Array(0);\n\t\tthis._instanceCount = 0;\n\t\tsuper.destroy();\n\t}\n}\n\nconst _composed = new Matrix3d();\n\n/**\n * `group × instance` into a scratch matrix, without disturbing either.\n * @param {Matrix3d} group - the group transform\n * @param {Matrix3d} instance - the instance transform (written into)\n * @returns {Matrix3d} `instance`, now holding the product\n * @ignore\n */\nfunction multiplyInto(group, instance) {\n\t_composed.copy(group);\n\t_composed.multiply(instance);\n\tinstance.copy(_composed);\n\treturn instance;\n}\n","import { colorPool } from \"../math/color.ts\";\nimport Body from \"../physics/builtin/body.js\";\nimport state from \"../state/state.ts\";\nimport { CANVAS_ONRESIZE, off, on } from \"../system/event.ts\";\nimport pool from \"../system/legacy_pool.js\";\nimport { defer } from \"../utils/function\";\nimport { createGUID } from \"../utils/utils\";\nimport Renderable from \"./renderable.js\";\n\n/**\n * Private function to re-use for object removal in a defer\n * @ignore\n */\nfunction deferredRemove(child, keepalive) {\n\tthis.removeChildNow(child, keepalive);\n}\n\n/**\n * Module-level cache of the camera position used by `_sortDepth`.\n * Captured once per sort (in `sort` / `sortNow`) so the comparator —\n * which runs O(N log N) times — doesn't pay a `state.current()` lookup\n * per comparison. Safe to be module-scoped: JS is single-threaded and\n * sort is synchronous within each container.\n * @ignore\n */\nlet _depthCamX = 0;\nlet _depthCamY = 0;\nlet _depthCamZ = 0;\n// World-space offset of the container currently being sorted. Captured\n// once per sort alongside `_depthCam*` so `_sortDepth` can translate\n// each child's LOCAL `pos.x/y/z` into the world frame the camera\n// distance is computed in. Without this, a container at world z=500\n// (e.g. a particle emitter) would compare its children at local z=0\n// against `_depthCam*` (world coords), producing a meaningless\n// distance and an incorrect painter-sort order when the camera sits\n// closer to the container's world position than to the global origin.\nlet _depthOffsetX = 0;\nlet _depthOffsetY = 0;\nlet _depthOffsetZ = 0;\n\n/**\n * Refresh the cached camera position from the active stage. Falls back\n * to (0, 0, 0) when no camera exists yet (e.g. early init, before any\n * stage has been set). Called just-in-time before `sortOn === \"depth\"`\n * runs its sort. The camera is a `Renderable` (Camera2d/Camera3d), so\n * `pos.z` is guaranteed by `ObservableVector3d` — no per-field guard.\n *\n * `Stage.cameras` is a `Map`, not an array — we read the `\"default\"`\n * key (every Stage seeds this in `reset`). For split-screen setups the\n * world sorts once per frame against the primary camera; the secondary\n * camera sees slightly-imperfect order which is the standard\n * compromise for split-screen rendering.\n * @ignore\n */\nfunction captureDepthCamera() {\n\tconst stage = state.current();\n\tconst cam = stage?.cameras?.get(\"default\");\n\tif (cam) {\n\t\t_depthCamX = cam.pos.x;\n\t\t_depthCamY = cam.pos.y;\n\t\t_depthCamZ = cam.pos.z;\n\t} else {\n\t\t_depthCamX = 0;\n\t\t_depthCamY = 0;\n\t\t_depthCamZ = 0;\n\t}\n}\n\n/**\n * Capture the world-space position of the given container into the\n * module-level `_depthOffset*` triple. Called once per sort by `sort` /\n * `sortNow` before walking the children, so `_sortDepth` can translate\n * each child's LOCAL `pos.x/y/z` into world space without paying for\n * an `ancestor.getAbsolutePosition()` walk per comparator call.\n * @ignore\n */\nfunction captureDepthOffset(container) {\n\tconst abs = container.getAbsolutePosition();\n\t_depthOffsetX = abs.x;\n\t_depthOffsetY = abs.y;\n\t_depthOffsetZ = abs.z;\n}\n\n/**\n * Register a child's physics body with the root world container when the\n * child is added to the tree. Two paths coexist:\n *  - `child.bodyDef`: declarative {@link BodyDefinition}, routed through\n *    `world.adapter.addBody(child, child.bodyDef)`. Engine-portable.\n *    Wins whenever set — even on re-registration of a previously-built\n *    body — so pool-recycled renderables get their def fields re-applied\n *    each time (collisionType, maxVelocity, etc. restored to the def's\n *    values regardless of any in-game mutation).\n *  - `child.body instanceof Body`: pre-constructed legacy {@link Body}\n *    with no `bodyDef`, routed through `world.addBody(child.body)`.\n *    BuiltinAdapter-only.\n * @ignore\n */\nfunction registerChildBody(child, worldContainer) {\n\tif (child.bodyDef) {\n\t\t// adapter.addBody handles both cases: builds a fresh Body when\n\t\t// none exists, or re-applies def fields onto the existing one\n\t\t// (legacy-bridge path) — see BuiltinAdapter.addBody.\n\t\tworldContainer.adapter.addBody(child, child.bodyDef);\n\t} else if (child.body instanceof Body) {\n\t\tworldContainer.addBody(child.body);\n\t}\n}\n\nlet globalFloatingCounter = 0;\n\n/**\n * additional import for TypeScript\n * @import {Color} from \"./../math/color.ts\";\n * @import Entity from \"./entity/entity.js\";\n * @import Sprite from \"./sprite.js\";\n * @import Collectable from \"./collectable.js\";\n * @import Trigger from \"./trigger.js\";\n * @import { Draggable } from \"./draggable.js\";\n * @import { DropTarget } from \"./dragndrop.js\";\n * @import NineSliceSprite from \"./nineslicesprite.js\";\n * @import ImageLayer from \"./imagelayer.js\";\n * @import ColorLayer from \"./colorlayer.js\";\n * @import Light2d from \"./light2d.js\";\n * @import UIBaseElement from \"./ui/uibaseelement.ts\";\n * @import UISpriteElement from \"./ui/uispriteelement.ts\";\n * @import UITextButton from \"./ui/uitextbutton.ts\";\n * @import Text from \"./text/text.js\";\n * @import BitmapText from \"./text/bitmaptext.js\";\n * @import {Bounds} from \"./../physics/bounds.ts\";\n * @import CanvasRenderer from \"./../video/canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../video/webgl/webgl_renderer.js\";\n */\n\n/**\n * Container represents a collection of child objects.\n * When no explicit dimensions are given, width and height default to Infinity,\n * meaning the container has no intrinsic size, no clipping, and acts as a pure\n * grouping/transform node.\n * In this case, anchorPoint is treated as (0, 0) since there is no meaningful\n * center for an infinite area. Bounds are then derived entirely from children\n * when {@link Container#enableChildBoundsUpdate} is enabled.\n * @category Container\n */\nexport default class Container extends Renderable {\n\t/**\n\t * @param {number} [x=0] - position of the container (accessible via the inherited pos.x property)\n\t * @param {number} [y=0] - position of the container (accessible via the inherited pos.y property)\n\t * @param {number} [width=Infinity] - width of the container. Defaults to Infinity (no intrinsic size, no clipping).\n\t * @param {number} [height=Infinity] - height of the container. Defaults to Infinity (no intrinsic size, no clipping).\n\t * @param {boolean} [root=false] - internal flag, true for the world root container\n\t * @ignore root\n\t */\n\tconstructor(x = 0, y = 0, width = Infinity, height = Infinity, root = false) {\n\t\t// call the super constructor\n\t\tsuper(x, y, width, height);\n\n\t\t/**\n\t\t * keep track of pending sort\n\t\t * @ignore\n\t\t */\n\t\tthis.pendingSort = null;\n\n\t\t/**\n\t\t * whether the container is the root of the scene\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.root = root;\n\n\t\t/**\n\t\t * The array of children of this container.\n\t\t * @ignore\n\t\t */\n\t\tthis.children = undefined;\n\n\t\t/**\n\t\t * The property of the child object that should be used to sort on this container\n\t\t * value : \"x\", \"y\", \"z\"\n\t\t * @type {string}\n\t\t * @default \"z\"\n\t\t */\n\t\tthis._sortOn = \"z\";\n\t\tthis._comparator = this._sortZ;\n\n\t\t/**\n\t\t * Specify if the children list should be automatically sorted when adding a new child\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.autoSort = true;\n\n\t\t/**\n\t\t * Specify if the children z index should automatically be managed by the parent container\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.autoDepth = true;\n\n\t\t/**\n\t\t * Specify if the container draw operation should clip its children to its own bounds\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.clipping = false;\n\n\t\t/**\n\t\t * a callback to be extended, triggered after a child has been added or removed\n\t\t * @param {number} index - added or removed child index\n\t\t */\n\t\tthis.onChildChange = function () {\n\t\t\t// to be extended\n\t\t};\n\n\t\t/**\n\t\t * Specify if the container bounds should automatically take in account\n\t\t * all child bounds when updated (this is expensive and disabled by default,\n\t\t * only enable if necessary)\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.enableChildBoundsUpdate = false;\n\n\t\t/**\n\t\t * define a background color for this container\n\t\t * @type {Color}\n\t\t * @default (0, 0, 0, 0.0)\n\t\t * @example\n\t\t * // add a red background color to this container\n\t\t * this.backgroundColor.setColor(255, 0, 0);\n\t\t */\n\t\tthis.backgroundColor = colorPool.get(0, 0, 0, 0.0);\n\n\t\t// container self apply any defined transformation\n\t\tthis.autoTransform = true;\n\n\t\t// enable collision and event detection\n\t\tthis.isKinematic = false;\n\n\t\t// container anchorPoint is always (0, 0) — children position from\n\t\t// the container's origin (top-left). This also avoids\n\t\t// Infinity * 0.5 = Infinity issues when the container has no\n\t\t// explicit size.\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t// subscribe on the canvas resize event\n\t\tif (this.root === true) {\n\t\t\t// Held as a field, not an inline arrow, so `destroy()` can pass it\n\t\t\t// to `off()` — an anonymous handler is unremovable, and its closure\n\t\t\t// would keep this container (and its whole child tree) reachable\n\t\t\t// from the event bus for the life of the page.\n\t\t\t// Workaround for not updating container child-bounds automatically (it's expensive!)\n\t\t\tthis.onCanvasResize = () => {\n\t\t\t\t// temporarly enable the enableChildBoundsUpdate flag\n\t\t\t\t// this.enableChildBoundsUpdate === true;\n\t\t\t\t// update bounds\n\t\t\t\tthis.updateBounds();\n\t\t\t\t// this.enableChildBoundsUpdate === false;\n\t\t\t};\n\t\t\ton(CANVAS_ONRESIZE, this.onCanvasResize);\n\t\t}\n\t}\n\n\t/**\n\t * The property of the child object that should be used to sort on\n\t * this container.\n\t *\n\t * - `\"x\"` / `\"y\"` — 2D scroll-order, descending on both axes\n\t *   (higher pos.z first; tied z, higher pos.x or pos.y first).\n\t *   Suited to side-scrollers and top-down games.\n\t * - `\"z\"` — descending pos.z (higher z draws on top). Default for\n\t *   `Camera2d` — matches the painter's-algorithm 2D layering model\n\t *   where pos.z is \"layer index\".\n\t * - `\"depth\"` — **Camera3d-only.** Ascending squared distance from\n\t *   the active camera (closest first in the array → far drawn first\n\t *   → close drawn on top under `Container.draw`'s reverse-iteration\n\t *   walk). This is the only correct sort for alpha-blended sprites\n\t *   under perspective projection, where neither pos.z nor pos.x/y\n\t *   layering produces correct occlusion (the closer/farther\n\t *   relationship flips as the camera orbits). Set automatically\n\t *   when an `Application` or `Stage` is constructed with\n\t *   `cameraClass: Camera3d`. Has no useful effect under `Camera2d`\n\t *   (ortho projection doesn't need camera-distance ordering) and\n\t *   pays an O(N log N) sort per frame, so don't enable it manually\n\t *   in 2D-only games.\n\t * @type {\"x\"|\"y\"|\"z\"|\"depth\"}\n\t * @default \"z\"\n\t */\n\tget sortOn() {\n\t\treturn this._sortOn;\n\t}\n\tset sortOn(value) {\n\t\tconst v = value.toLowerCase();\n\t\tif (v !== \"x\" && v !== \"y\" && v !== \"z\" && v !== \"depth\") {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid sortOn value: \"${value}\" (expected \"x\", \"y\", \"z\", or \"depth\")`,\n\t\t\t);\n\t\t}\n\t\tthis._sortOn = v;\n\t\tif (v === \"depth\") {\n\t\t\tthis._comparator = this._sortDepth;\n\t\t} else {\n\t\t\tthis._comparator = this[\"_sort\" + v.toUpperCase()];\n\t\t}\n\t}\n\n\t/**\n\t * reset the container, removing all children, and resetting transforms.\n\t */\n\treset() {\n\t\t// cancel any sort operation\n\t\tif (this.pendingSort) {\n\t\t\tclearTimeout(this.pendingSort);\n\t\t\tthis.pendingSort = null;\n\t\t}\n\n\t\t// delete all children\n\t\tconst children = this.getChildren();\n\t\tfor (let i = children.length, child; i >= 0; child = children[--i]) {\n\t\t\t// don't remove it if a persistent object\n\t\t\tif (child && child.isPersistent !== true) {\n\t\t\t\tthis.removeChildNow(child);\n\t\t\t}\n\t\t}\n\n\t\tthis.currentTransform.identity();\n\n\t\tthis.backgroundColor.setColor(0, 0, 0, 0.0);\n\t}\n\n\t/**\n\t * Add a child to the container <br>\n\t * if auto-sort is disabled, the object will be appended at the bottom of the list.\n\t * Adding a child to the container will automatically remove it from its other container.\n\t * Meaning a child can only have one parent. This is important if you add a renderable\n\t * to a container then add it to the World container it will move it out of the\n\t * original container. Then when the World container reset() method is called the renderable\n\t * will not be in any container. <br>\n\t * if the given child implements an onActivateEvent method, that method will be called\n\t * once the child is added to this container.\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be added\n\t * @param {number} [z] - forces the z index of the child to the specified value\n\t * @returns {Renderable} the added child\n\t */\n\taddChild(child, z) {\n\t\tif (!(child instanceof Renderable)) {\n\t\t\tthrow new Error(`${String(child)} is not an instance of Renderable`);\n\t\t}\n\t\tif (child.ancestor instanceof Container) {\n\t\t\t// Reparenting: detach from the previous ancestor WITHOUT\n\t\t\t// destroying the child. The default `removeChildNow` path\n\t\t\t// runs `destroy()` for any child not registered with the\n\t\t\t// legacy `pool.push` registry (Particle pool-recycled\n\t\t\t// instances are a real-world example) — which nulls\n\t\t\t// `child.pos` and crashes the very next `updateBounds`\n\t\t\t// call below. We're moving the child, not disposing of it.\n\t\t\tchild.ancestor.removeChildNow(child, true);\n\t\t} else {\n\t\t\t// only allocate a GUID if the object has no previous ancestor\n\t\t\t// (e.g. move one child from one container to another)\n\t\t\tchild.GUID = createGUID(child.id);\n\t\t}\n\n\t\t// add the new child\n\t\tchild.ancestor = this;\n\t\tthis.getChildren().push(child);\n\n\t\t// update child bounds to reflect the new ancestor\n\t\tif (this.isFloating === true) {\n\t\t\t// only parent container can be floating\n\t\t\tchild.floating = false;\n\t\t}\n\t\tchild.updateBounds();\n\n\t\t// set the child z value if required\n\t\tif (typeof z === \"number\") {\n\t\t\tchild.pos.z = z;\n\t\t} else if (this.autoDepth === true) {\n\t\t\tchild.pos.z = this.getChildren().length;\n\t\t}\n\n\t\tif (this.autoSort === true) {\n\t\t\tthis.sort();\n\t\t}\n\n\t\t// Register physics bodies BEFORE firing onActivateEvent so that\n\t\t// `child.body` is available inside the hook for both legacy\n\t\t// (pre-built `new Body(...)`) AND declarative (`bodyDef`) users.\n\t\t// Legacy users are unaffected — their body was already set in\n\t\t// the constructor; the order swap only matters for bodyDef.\n\t\tif (this.isAttachedToRoot()) {\n\t\t\tconst worldContainer = this.getRootAncestor();\n\t\t\tregisterChildBody(child, worldContainer);\n\t\t\t// if the child is a container\n\t\t\tif (child instanceof Container) {\n\t\t\t\t// add all container child bodies\n\t\t\t\tchild.forEach((cchild) => {\n\t\t\t\t\tregisterChildBody(cchild, worldContainer);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\ttypeof child.onActivateEvent === \"function\" &&\n\t\t\tthis.isAttachedToRoot()\n\t\t) {\n\t\t\tchild.onActivateEvent();\n\t\t}\n\n\t\t// force container bounds update if required\n\t\tif (this.enableChildBoundsUpdate === true) {\n\t\t\tthis.updateBounds();\n\t\t}\n\n\t\t// mark the container for repaint\n\t\tthis.isDirty = true;\n\n\t\t// triggered callback if defined\n\t\tthis.onChildChange.call(this, this.getChildren().length - 1);\n\n\t\treturn child;\n\t}\n\n\t/**\n\t * Add a child to the container at the specified index<br>\n\t * (the list won't be sorted after insertion)\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be added\n\t * @param {number} index - The index at which to insert the child\n\t * @returns {Renderable} the added child\n\t */\n\taddChildAt(child, index) {\n\t\tif (!(child instanceof Renderable)) {\n\t\t\tthrow new Error(`${String(child)} is not an instance of Renderable`);\n\t\t}\n\t\tif (index >= 0 && index <= this.getChildren().length) {\n\t\t\tif (child.ancestor instanceof Container) {\n\t\t\t\t// Reparenting — see Container.addChild for why\n\t\t\t\t// `keepalive=true` is required here.\n\t\t\t\tchild.ancestor.removeChildNow(child, true);\n\t\t\t} else {\n\t\t\t\t// only allocate a GUID if the object has no previous ancestor\n\t\t\t\t// (e.g. move one child from one container to another)\n\t\t\t\tchild.GUID = createGUID();\n\t\t\t}\n\n\t\t\t// add the new child\n\t\t\tchild.ancestor = this;\n\t\t\tthis.getChildren().splice(index, 0, child);\n\n\t\t\t// update child bounds to reflect the new ancestor\n\t\t\tif (this.isFloating === true) {\n\t\t\t\t// only parent container can be floating\n\t\t\t\tchild.floating = false;\n\t\t\t}\n\t\t\tchild.updateBounds();\n\n\t\t\t// Register physics bodies BEFORE firing onActivateEvent so\n\t\t\t// `child.body` is available in the hook for both legacy\n\t\t\t// (pre-built `new Body(...)`) AND declarative (`bodyDef`)\n\t\t\t// users. See addChild for the rationale.\n\t\t\tif (this.isAttachedToRoot()) {\n\t\t\t\tconst worldContainer = this.getRootAncestor();\n\t\t\t\tregisterChildBody(child, worldContainer);\n\t\t\t\t// if the child is a container\n\t\t\t\tif (child instanceof Container) {\n\t\t\t\t\t// add all container child bodies\n\t\t\t\t\tchild.forEach((cchild) => {\n\t\t\t\t\t\tregisterChildBody(cchild, worldContainer);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\ttypeof child.onActivateEvent === \"function\" &&\n\t\t\t\tthis.isAttachedToRoot()\n\t\t\t) {\n\t\t\t\tchild.onActivateEvent();\n\t\t\t}\n\n\t\t\t// force container bounds update if required\n\t\t\tif (this.enableChildBoundsUpdate === true) {\n\t\t\t\tthis.updateBounds();\n\t\t\t}\n\n\t\t\t// mark the container for repaint\n\t\t\tthis.isDirty = true;\n\n\t\t\t// triggered callback if defined\n\t\t\tthis.onChildChange.call(this, index);\n\n\t\t\treturn child;\n\t\t} else {\n\t\t\tthrow new Error(\"Index (\" + index + \") Out Of Bounds for addChildAt()\");\n\t\t}\n\t}\n\n\t/**\n\t * The forEach() method executes a provided function once per child element. <br>\n\t * the callback function is invoked with three arguments: <br>\n\t *    - The current element being processed in the array <br>\n\t *    - The index of element in the array. <br>\n\t *    - The array forEach() was called upon. <br>\n\t * @param {Function} callback - function to execute on each element\n\t * @param {object} [thisArg] - value to use as this(i.e reference Object) when executing callback.\n\t * @example\n\t * // iterate through all children of this container\n\t * container.forEach((child) => {\n\t *    // do something with the child\n\t *    child.doSomething();\n\t * });\n\t * container.forEach((child, index) => { ... });\n\t * container.forEach((child, index, array) => { ... });\n\t * container.forEach((child, index, array) => { ... }, thisArg);\n\t */\n\tforEach(callback, thisArg) {\n\t\tlet i = 0;\n\t\tconst children = this.getChildren();\n\n\t\tconst len = children.length;\n\n\t\tif (typeof callback !== \"function\") {\n\t\t\tthrow new Error(callback + \" is not a function\");\n\t\t}\n\n\t\twhile (i < len) {\n\t\t\tcallback.call(thisArg ?? this, children[i], i, children);\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/**\n\t * Swaps the position (z-index) of 2 children\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be added\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child2 - Child to be added\n\t */\n\tswapChildren(child, child2) {\n\t\tconst index = this.getChildIndex(child);\n\t\tconst index2 = this.getChildIndex(child2);\n\n\t\tif (index !== -1 && index2 !== -1) {\n\t\t\t// swap z index\n\t\t\tconst _z = child.pos.z;\n\t\t\tchild.pos.z = child2.pos.z;\n\t\t\tchild2.pos.z = _z;\n\t\t\t// swap the positions..\n\t\t\tthis.getChildren()[index] = child2;\n\t\t\tthis.getChildren()[index2] = child;\n\t\t\t// mark the container as dirty\n\t\t\tthis.isDirty = true;\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\tchild +\n\t\t\t\t\t\" Both the supplied children must be a child of the caller \" +\n\t\t\t\t\tthis,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the Child at the specified index\n\t * @param {number} index - The index of the child\n\t * @returns {Renderable} the child at the specified index\n\t */\n\tgetChildAt(index) {\n\t\tif (index >= 0 && index < this.getChildren().length) {\n\t\t\treturn this.getChildren()[index];\n\t\t} else {\n\t\t\tthrow new Error(\"Index (\" + index + \") Out Of Bounds for getChildAt()\");\n\t\t}\n\t}\n\n\t/**\n\t * Returns the index of the given Child\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - The child object\n\t * @returns {number} index\n\t */\n\tgetChildIndex(child) {\n\t\treturn this.getChildren().indexOf(child);\n\t}\n\n\t/**\n\t * Returns the next child within the container or undefined if none\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - The child object\n\t * @returns {Renderable} child\n\t */\n\tgetNextChild(child) {\n\t\tconst index = this.getChildren().indexOf(child);\n\t\t// `indexOf` returns -1 for a non-member — without this check the +1\n\t\t// would alias \"not found\" to index 0 and return the first child\n\t\tif (index !== -1 && index + 1 < this.getChildren().length) {\n\t\t\treturn this.getChildAt(index + 1);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Returns true if contains the specified Child\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - The child object\n\t * @returns {boolean}\n\t */\n\thasChild(child) {\n\t\treturn this === child.ancestor;\n\t}\n\n\t/**\n\t * return the child corresponding to the given property and value.<br>\n\t * note : avoid calling this function every frame since\n\t * it parses the whole object tree each time\n\t * @param {string} prop - Property name\n\t * @param {string|RegExp|number|boolean} value - Value of the property\n\t * @returns {Renderable[]} Array of children\n\t * @example\n\t * // get the first child object called \"mainPlayer\" in a specific container :\n\t * let ent = myContainer.getChildByProp(\"name\", \"mainPlayer\");\n\t *\n\t * // or query the whole world :\n\t * let ent = container.getChildByProp(\"name\", \"mainPlayer\");\n\t *\n\t * // partial property matches are also allowed by using a RegExp.\n\t * // the following matches \"redCOIN\", \"bluecoin\", \"bagOfCoins\", etc :\n\t * let allCoins = container.getChildByProp(\"name\", /coin/i);\n\t *\n\t * // searching for numbers or other data types :\n\t * let zIndex10 = container.getChildByProp(\"z\", 10);\n\t * let inViewport = container.getChildByProp(\"inViewport\", true);\n\t */\n\tgetChildByProp(prop, value, objList = []) {\n\t\tthis.forEach((child) => {\n\t\t\tconst v = child[prop];\n\t\t\tif (value instanceof RegExp && typeof v === \"string\") {\n\t\t\t\tif (value.test(v)) {\n\t\t\t\t\tobjList.push(child);\n\t\t\t\t}\n\t\t\t} else if (v === value) {\n\t\t\t\tobjList.push(child);\n\t\t\t}\n\t\t\tif (child instanceof Container) {\n\t\t\t\tchild.getChildByProp(prop, value, objList);\n\t\t\t}\n\t\t});\n\n\t\treturn objList;\n\t}\n\n\t/**\n\t * returns the list of children with the specified class type\n\t * @param {object} classType - Class type\n\t * @returns {Renderable[]} Array of children\n\t */\n\tgetChildByType(classType, objList = []) {\n\t\tthis.forEach((child) => {\n\t\t\tif (child instanceof classType) {\n\t\t\t\tobjList.push(child);\n\t\t\t}\n\t\t\tif (child instanceof Container) {\n\t\t\t\tchild.getChildByType(classType, objList);\n\t\t\t}\n\t\t});\n\n\t\treturn objList;\n\t}\n\n\t/**\n\t * returns the list of children with the specified name<br>\n\t * as defined in Tiled (Name field of the Object Properties)<br>\n\t * note : avoid calling this function every frame since\n\t * it parses the whole object list each time\n\t * @param {string|RegExp|number|boolean} name - child name\n\t * @returns {Renderable[]} Array of children\n\t */\n\tgetChildByName(name) {\n\t\treturn this.getChildByProp(\"name\", name);\n\t}\n\n\t/**\n\t * return the child corresponding to the specified GUID<br>\n\t * note : avoid calling this function every frame since\n\t * it parses the whole object list each time\n\t * @param {string|RegExp|number|boolean} guid - child GUID\n\t * @returns {Renderable} corresponding child or null\n\t */\n\tgetChildByGUID(guid) {\n\t\tconst obj = this.getChildByProp(\"GUID\", guid);\n\t\treturn obj.length > 0 ? obj[0] : null;\n\t}\n\n\t/**\n\t * return all child in this container\n\t * @returns {Renderable[]} an array of renderable object\n\t */\n\tgetChildren() {\n\t\tif (typeof this.children === \"undefined\") {\n\t\t\tthis.children = [];\n\t\t}\n\t\treturn this.children;\n\t}\n\n\t/**\n\t * update the bounding box for this container.\n\t * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates\n\t * @returns {Bounds} this container bounding box Rectangle object\n\t */\n\tupdateBounds(absolute = true) {\n\t\tconst bounds = this.getBounds();\n\n\t\tif (this.isFinite()) {\n\t\t\t// call parent method only when container has finite dimensions\n\t\t\tsuper.updateBounds(absolute);\n\t\t} else if (this.enableChildBoundsUpdate === true) {\n\t\t\t// clear bounds so child aggregation starts fresh\n\t\t\tbounds.clear();\n\t\t}\n\n\t\tif (this.enableChildBoundsUpdate === true) {\n\t\t\tthis.forEach((child) => {\n\t\t\t\tconst childBounds = child.updateBounds(absolute);\n\t\t\t\tif (childBounds.isFinite()) {\n\t\t\t\t\tbounds.addBounds(childBounds);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * Checks if this container is root or if it's attached to the root container.\n\t * @returns {boolean} true if this container is root or if it's attached to the root container\n\t */\n\tisAttachedToRoot() {\n\t\tif (this.root === true) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tlet ancestor = this.ancestor;\n\t\t\twhile (ancestor) {\n\t\t\t\tif (ancestor.root === true) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tancestor = ancestor.ancestor;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the instance of the root container (i.e. the current application World container).\n\t * @returns {Container} root container\n\t */\n\tgetRootAncestor() {\n\t\tif (this.root === true) {\n\t\t\treturn this;\n\t\t} else {\n\t\t\tlet ancestor = this.ancestor;\n\t\t\twhile (ancestor) {\n\t\t\t\tif (ancestor.root === true) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tancestor = ancestor.ancestor;\n\t\t\t}\n\t\t\treturn ancestor;\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t * @param {...*} _args - reserved; widens the signature so subclass\n\t *   overrides like `onActivateEvent(app)` remain structurally\n\t *   assignable to the base Container/Renderable type.\n\t */\n\tonActivateEvent(..._args) {\n\t\tthis.forEach((child) => {\n\t\t\tif (typeof child.onActivateEvent === \"function\") {\n\t\t\t\tchild.onActivateEvent();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Invokes the removeChildNow in a defer, to ensure the child is removed safely after the update & draw stack has completed. <br>\n\t * if the given child implements an onDeactivateEvent() method, that method will be called once the child is removed from this container.\n\t * @param {Renderable|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be removed\n\t * @param {boolean} [keepalive=false] - true to prevent calling child.destroy()\n\t */\n\tremoveChild(child, keepalive) {\n\t\tif (this.hasChild(child)) {\n\t\t\tdefer(deferredRemove, this, child, keepalive);\n\t\t} else {\n\t\t\tthrow new Error(\"Child is not mine.\");\n\t\t}\n\t}\n\n\t/**\n\t * Removes (and optionally destroys) a child from the container.<br>\n\t * (removal is immediate and unconditional)<br>\n\t * Never use keepalive=true with objects from {@link pool}. Doing so will create a memory leak.\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be removed\n\t * @param {boolean} [keepalive=False] - True to prevent calling child.destroy()\n\t */\n\tremoveChildNow(child, keepalive) {\n\t\tif (this.hasChild(child) && this.getChildIndex(child) >= 0) {\n\t\t\tif (typeof child.onDeactivateEvent === \"function\") {\n\t\t\t\tchild.onDeactivateEvent();\n\t\t\t}\n\n\t\t\tconst root = this.getRootAncestor();\n\n\t\t\t// Evict the child (and any descendants if it's a container)\n\t\t\t// from the broadphase quadtree. The broadphase is rebuilt\n\t\t\t// each `world.update()`, but pointer events and narrow-phase\n\t\t\t// queries can fire between the deferred `removeChildNow` and\n\t\t\t// the next rebuild — a stale reference there would return a\n\t\t\t// destroyed renderable and crash any caller that read its\n\t\t\t// `pos` (e.g. `_sortReverseZ`).\n\t\t\tif (root?.broadphase) {\n\t\t\t\tif (typeof child.addChild === \"function\") {\n\t\t\t\t\troot.broadphase.removeContainer(child);\n\t\t\t\t}\n\t\t\t\troot.broadphase.remove(child);\n\t\t\t}\n\n\t\t\t// Remove the body first to avoid a condition where a body can\n\t\t\t// be detached from its parent before it's removed from the\n\t\t\t// game world. `child.body` may be a melonJS `Body` (legacy /\n\t\t\t// builtin adapter) or an adapter-specific handle (e.g.\n\t\t\t// `Matter.Body` under the matter adapter); either way the\n\t\t\t// adapter knows how to clean it up by renderable identity.\n\t\t\tif (child.body) {\n\t\t\t\t/** @type {{ adapter?: { removeBody?: (r: object) => void } }} */\n\t\t\t\tconst world = root;\n\t\t\t\tif (world?.adapter?.removeBody) {\n\t\t\t\t\tworld.adapter.removeBody(child);\n\t\t\t\t} else if (child.body instanceof Body) {\n\t\t\t\t\t// Container detached from the world tree — fall back to\n\t\t\t\t\t// the legacy `world.removeBody(body)` path if available.\n\t\t\t\t\t// This only handles a legacy `Body`; an adapter-specific\n\t\t\t\t\t// handle reaching this branch silently leaks (warn so it\n\t\t\t\t\t// surfaces in development).\n\t\t\t\t\troot?.removeBody?.(child.body);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"melonJS: removeChildNow could not clean up an adapter-specific body — the container is not attached to a world with an adapter. The body remains in the physics engine.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!keepalive) {\n\t\t\t\t// attempt at recycling the object\n\t\t\t\tif (pool.push(child, false) === false) {\n\t\t\t\t\t//  else just destroy it\n\t\t\t\t\tif (typeof child.destroy === \"function\") {\n\t\t\t\t\t\tchild.destroy();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Don't cache the child index; another element might have been removed\n\t\t\t// by the child's `onDeactivateEvent` or `destroy` methods\n\t\t\tconst childIndex = this.getChildIndex(child);\n\t\t\tif (childIndex >= 0) {\n\t\t\t\tthis.getChildren().splice(childIndex, 1);\n\t\t\t\tchild.ancestor = undefined;\n\t\t\t}\n\n\t\t\t// force bounds update if required\n\t\t\tif (this.enableChildBoundsUpdate === true) {\n\t\t\t\tthis.updateBounds();\n\t\t\t}\n\n\t\t\t// mark the container for repaint\n\t\t\tthis.isDirty = true;\n\n\t\t\t// triggered callback if defined\n\t\t\tthis.onChildChange.call(this, childIndex);\n\t\t}\n\t}\n\n\t/**\n\t * Automatically set the specified property of all childs to the given value\n\t * @param {string} prop - property name\n\t * @param {object} value - property value\n\t * @param {boolean} [recursive=false] - recursively apply the value to child containers if true\n\t */\n\tsetChildsProperty(prop, value, recursive) {\n\t\tthis.forEach((child) => {\n\t\t\tif (recursive === true && child instanceof Container) {\n\t\t\t\tchild.setChildsProperty(prop, value, recursive);\n\t\t\t}\n\t\t\tchild[prop] = value;\n\t\t});\n\t}\n\n\t/**\n\t * Move the child in the group one step forward (z depth).\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child -  Child to be moved\n\t */\n\tmoveUp(child) {\n\t\tconst childIndex = this.getChildIndex(child);\n\t\tif (childIndex - 1 >= 0) {\n\t\t\t// note : we use an inverted loop\n\t\t\tthis.swapChildren(child, this.getChildAt(childIndex - 1));\n\t\t\t// mark the container as dirty\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * Move the child in the group one step backward (z depth).\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be moved\n\t */\n\tmoveDown(child) {\n\t\tconst childIndex = this.getChildIndex(child);\n\t\tif (childIndex >= 0 && childIndex + 1 < this.getChildren().length) {\n\t\t\t// note : we use an inverted loop\n\t\t\tthis.swapChildren(child, this.getChildAt(childIndex + 1));\n\t\t\t// mark the container as dirty\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * Move the specified child to the top(z depth).\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be moved\n\t */\n\tmoveToTop(child) {\n\t\tconst childIndex = this.getChildIndex(child);\n\t\tif (childIndex > 0) {\n\t\t\tconst children = this.getChildren();\n\t\t\t// note : we use an inverted loop\n\t\t\tchildren.splice(0, 0, children.splice(childIndex, 1)[0]);\n\t\t\t// increment our child z value based on the previous child depth\n\t\t\tchild.pos.z = children[1].pos.z + 1;\n\t\t\t// mark the container as dirty\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * Move the specified child the bottom (z depth).\n\t * @param {Renderable|Entity|Sprite|Collectable|Trigger|Draggable|DropTarget|NineSliceSprite|ImageLayer|ColorLayer|Light2d|UIBaseElement|UISpriteElement|UITextButton|Text|BitmapText} child - Child to be moved\n\t */\n\tmoveToBottom(child) {\n\t\tconst childIndex = this.getChildIndex(child);\n\t\tconst children = this.getChildren();\n\t\tif (childIndex >= 0 && childIndex < children.length - 1) {\n\t\t\t// note : we use an inverted loop\n\t\t\tchildren.splice(\n\t\t\t\tchildren.length - 1,\n\t\t\t\t0,\n\t\t\t\tchildren.splice(childIndex, 1)[0],\n\t\t\t);\n\t\t\t// increment our child z value based on the next child depth\n\t\t\tchild.pos.z = children[children.length - 2].pos.z - 1;\n\t\t\t// mark the container as dirty\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * Manually trigger the sort of all the childs in the container\n\t * @param {boolean} [recursive=false] - recursively sort all containers if true\n\t */\n\tsort(recursive) {\n\t\t// do nothing if there is already a pending sort\n\t\tif (!this.pendingSort) {\n\t\t\tif (recursive === true) {\n\t\t\t\tthis.forEach((child) => {\n\t\t\t\t\tif (child instanceof Container) {\n\t\t\t\t\t\t// note : this will generate one deferred sorting function\n\t\t\t\t\t\t// for each existing containe\n\t\t\t\t\t\tchild.sort(recursive);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.pendingSort = defer(function () {\n\t\t\t\t// refresh the cached camera + container offset so\n\t\t\t\t// `_sortDepth` sees the current view in world space —\n\t\t\t\t// no-op for x/y/z modes.\n\t\t\t\tif (this._sortOn === \"depth\") {\n\t\t\t\t\tcaptureDepthCamera();\n\t\t\t\t\tcaptureDepthOffset(this);\n\t\t\t\t}\n\t\t\t\t// sort everything in this container\n\t\t\t\tthis.getChildren().sort(this._comparator);\n\t\t\t\t// clear the defer id\n\t\t\t\tthis.pendingSort = null;\n\t\t\t\t// make sure we redraw everything\n\t\t\t\tthis.isDirty = true;\n\t\t\t}, this);\n\t\t}\n\t}\n\n\t/**\n\t * Synchronous variant of {@link Container#sort}. Sorts immediately\n\t * — no defer — using the current `_comparator`. Intended for\n\t * per-frame callers that need the order to be valid for the very\n\t * next render (e.g. `Camera3d` refreshing the `\"depth\"` sort after\n\t * the camera has moved, where waiting for the deferred sort tick\n\t * would render a stale frame).\n\t * @param {boolean} [recursive=false] - recursively sort sub-containers\n\t */\n\tsortNow(recursive) {\n\t\tif (this._sortOn === \"depth\") {\n\t\t\tcaptureDepthCamera();\n\t\t\tcaptureDepthOffset(this);\n\t\t}\n\t\tconst children = this.getChildren();\n\t\tif (children.length > 1) {\n\t\t\tchildren.sort(this._comparator);\n\t\t\tthis.isDirty = true;\n\t\t}\n\t\tif (recursive === true) {\n\t\t\tfor (let i = 0; i < children.length; i++) {\n\t\t\t\tconst c = children[i];\n\t\t\t\tif (c instanceof Container) {\n\t\t\t\t\tc.sortNow(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t * @param {...*} _args - reserved; widens the signature so subclass\n\t *   overrides like `onDeactivateEvent(app)` remain structurally\n\t *   assignable to the base Container/Renderable type.\n\t */\n\tonDeactivateEvent(..._args) {\n\t\tthis.forEach((child) => {\n\t\t\tif (typeof child.onDeactivateEvent === \"function\") {\n\t\t\t\tchild.onDeactivateEvent();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Z Sorting function\n\t * @ignore\n\t */\n\t_sortZ(a, b) {\n\t\treturn b.pos.z - a.pos.z;\n\t}\n\n\t/**\n\t * Reverse Z Sorting function\n\t * @ignore\n\t */\n\t_sortReverseZ(a, b) {\n\t\treturn a.pos.z - b.pos.z;\n\t}\n\n\t/**\n\t * Camera-distance sorting function used by `sortOn = \"depth\"`.\n\t * Orders children by ascending squared distance from the cached\n\t * camera position — closest first in the array, so\n\t * {@link Container#draw}'s reverse-iteration walk paints far→near\n\t * (correct painter's algorithm for alpha-blended sprites under\n\t * perspective). The cached position is refreshed by\n\t * {@link captureDepthCamera} just before each sort runs; this\n\t * comparator stays tight (zero allocation, six subtracts + three\n\t * mul-adds per pair) for the hot O(N log N) path.\n\t *\n\t * Container children are `Renderable` instances with an\n\t * `ObservableVector3d` pos — `pos.z` is always defined, no guard\n\t * needed here.\n\t * @ignore\n\t */\n\t_sortDepth(a, b) {\n\t\t// Translate each child's LOCAL `pos` into world space via the\n\t\t// parent container's offset (captured once per sort by\n\t\t// `captureDepthOffset`). For the world container itself the\n\t\t// offset is (0, 0, 0), so this collapses to the original\n\t\t// local-only math. For nested containers (an emitter at\n\t\t// world z=500, particles at local z=0) it's the difference\n\t\t// between \"particles all sort identically because their\n\t\t// local d²=0\" and \"particles sort by their actual world\n\t\t// distance from the camera\".\n\t\tconst ax = a.pos.x + _depthOffsetX - _depthCamX;\n\t\tconst ay = a.pos.y + _depthOffsetY - _depthCamY;\n\t\tconst az = a.pos.z + _depthOffsetZ - _depthCamZ;\n\t\tconst bx = b.pos.x + _depthOffsetX - _depthCamX;\n\t\tconst by = b.pos.y + _depthOffsetY - _depthCamY;\n\t\tconst bz = b.pos.z + _depthOffsetZ - _depthCamZ;\n\t\treturn ax * ax + ay * ay + az * az - (bx * bx + by * by + bz * bz);\n\t}\n\n\t/**\n\t * X Sorting function\n\t * @ignore\n\t */\n\t_sortX(a, b) {\n\t\treturn b.pos.z - a.pos.z || b.pos.x - a.pos.x;\n\t}\n\n\t/**\n\t * Y Sorting function\n\t * @ignore\n\t */\n\t_sortY(a, b) {\n\t\treturn b.pos.z - a.pos.z || b.pos.y - a.pos.y;\n\t}\n\n\t/**\n\t * Destroy function<br>\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// drop the root container's resize subscription before anything else —\n\t\t// the handler closes over `this`, so leaving it registered keeps the\n\t\t// container and its entire child tree reachable from the event bus,\n\t\t// and a destroyed container would still try to update its bounds on\n\t\t// every canvas resize. Only root containers ever subscribe.\n\t\tif (this.onCanvasResize) {\n\t\t\toff(CANVAS_ONRESIZE, this.onCanvasResize);\n\t\t\tthis.onCanvasResize = undefined;\n\t\t}\n\n\t\t// empty the container\n\t\tthis.reset();\n\t\t// call the parent destroy method, spreading the actual arguments —\n\t\t// passing the `arguments` object itself would hand subclasses'\n\t\t// `onDestroyEvent(app)` an Arguments wrapper instead of the value\n\t\tsuper.destroy(...arguments);\n\n\t\tcolorPool.release(this.backgroundColor);\n\t}\n\n\t/**\n\t * container update function. <br>\n\t * automatically called by the application update loop {@link Application}\n\t * @protected\n\t * @param {number} dt - time since the last update in milliseconds.\n\t * @returns {boolean} true if the Container is dirty\n\t */\n\tupdate(dt) {\n\t\tlet isFloating = false;\n\t\tconst isPaused = state.isPaused();\n\t\tconst children = this.getChildren();\n\t\tconst childrenLength = children.length;\n\t\tconst cameras = state.current().cameras;\n\n\t\tfor (let i = childrenLength, obj; i--, (obj = children[i]); ) {\n\t\t\tif (isPaused && !obj.updateWhenPaused) {\n\t\t\t\t// skip this object\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tisFloating = globalFloatingCounter > 0 || obj.floating;\n\t\t\tif (isFloating) {\n\t\t\t\tglobalFloatingCounter++;\n\t\t\t}\n\n\t\t\t// check if object is in any active cameras — accumulate into a\n\t\t\t// local and assign ONCE: assigning `false` then `true` through\n\t\t\t// the setter would fire `onVisibilityChange` (a change-detecting\n\t\t\t// setter) twice per frame for every continuously-visible object\n\t\t\tlet inViewport = false;\n\t\t\t// iterate through all cameras\n\t\t\tcameras.forEach((camera) => {\n\t\t\t\tif (camera.isVisible(obj, isFloating)) {\n\t\t\t\t\tinViewport = true;\n\t\t\t\t}\n\t\t\t});\n\t\t\tobj.inViewport = inViewport;\n\n\t\t\t// update our object\n\t\t\tthis.isDirty |= (obj.inViewport || obj.alwaysUpdate) && obj.update(dt);\n\n\t\t\tif (globalFloatingCounter > 0) {\n\t\t\t\tglobalFloatingCounter--;\n\t\t\t}\n\t\t}\n\n\t\t// call the parent method\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * draw this renderable (automatically called by melonJS)\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\tdraw(renderer, viewport) {\n\t\tconst bounds = this.getBounds();\n\n\t\t// adjust position before clipping so the renderer's currentTransform\n\t\t// sits at this container's local origin. `clipRect` then operates\n\t\t// in container-local coords and any scale/rotation accumulated in\n\t\t// the matrix is honored when the renderer converts to screen space.\n\t\t// (Issue #1349 — passing world-space `bounds.left/top` to the\n\t\t// previous local-space-expecting `clipRect` double-counted the\n\t\t// translation when the container was nested inside a translated\n\t\t// parent.)\n\t\trenderer.translate(this.pos.x, this.pos.y);\n\n\t\t// clip the container children to the container bounds.\n\t\t// Container's anchorPoint is forced to (0, 0), so the local rect\n\t\t// is (0, 0, width, height). `bounds.isFinite()` covers the\n\t\t// aggregate (self + children) — a container may have a finite\n\t\t// child bounds while its own `width/height` are `Infinity`\n\t\t// (e.g., the world container or any `enableChildBoundsUpdate`\n\t\t// container with a finite child but no intrinsic size). Passing\n\t\t// `Infinity` to `clipRect` would NaN-poison the WebGL scissor\n\t\t// math, so guard on the size we actually pass too.\n\t\tif (\n\t\t\tthis.root === false &&\n\t\t\tthis.clipping === true &&\n\t\t\tbounds.isFinite() === true &&\n\t\t\tNumber.isFinite(this.width) === true &&\n\t\t\tNumber.isFinite(this.height) === true\n\t\t) {\n\t\t\trenderer.clipRect(0, 0, this.width, this.height);\n\t\t}\n\n\t\t// color background if defined\n\t\tif (this.backgroundColor.alpha > 1 / 255) {\n\t\t\trenderer.clearColor(this.backgroundColor);\n\t\t}\n\n\t\t// viewport is documented optional — legacy subclass overrides call\n\t\t// `super.draw(renderer)` without one; treat that as the default camera\n\t\tconst isNonDefaultCamera = viewport ? !viewport.isDefault : false;\n\n\t\tconst children = this.getChildren();\n\t\tfor (let i = children.length, obj; i--, (obj = children[i]); ) {\n\t\t\tconst isFloating = obj.floating === true;\n\n\t\t\tif (obj.inViewport || isFloating) {\n\t\t\t\t// skip UI-only floating elements on non-default cameras\n\t\t\t\tif (isFloating && isNonDefaultCamera && !obj.visibleInAllCameras) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (isFloating) {\n\t\t\t\t\trenderer.save();\n\t\t\t\t\trenderer.resetTransform();\n\t\t\t\t\t// Floating renderables draw in screen space — swap to\n\t\t\t\t\t// the camera's flat screen ortho regardless of whether\n\t\t\t\t\t// we're on the default camera. Under Camera2d this is\n\t\t\t\t\t// a no-op (its `screenProjection` mirrors\n\t\t\t\t\t// `projectionMatrix`); under Camera3d this is the only\n\t\t\t\t\t// way to render floating Text / HUD / overlays without\n\t\t\t\t\t// the perspective projection NaN-ing them via\n\t\t\t\t\t// `w = 0` perspective divide on world-z=0 points.\n\t\t\t\t\tif (viewport) {\n\t\t\t\t\t\trenderer.setProjection(viewport.screenProjection);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tobj.preDraw(renderer);\n\t\t\t\tobj.draw(renderer, viewport);\n\t\t\t\tobj.postDraw(renderer);\n\n\t\t\t\tif (isFloating) {\n\t\t\t\t\t// Restore the projection the camera had installed for\n\t\t\t\t\t// this draw pass — non-default cameras use a separate\n\t\t\t\t\t// `worldProjection`; the default camera just uses\n\t\t\t\t\t// `projectionMatrix` directly.\n\t\t\t\t\tif (viewport) {\n\t\t\t\t\t\trenderer.setProjection(\n\t\t\t\t\t\t\tisNonDefaultCamera\n\t\t\t\t\t\t\t\t? viewport.worldProjection\n\t\t\t\t\t\t\t\t: viewport.projectionMatrix,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\trenderer.restore();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * Keyframe sampling for glTF node-TRS animation. Pure, engine-free helpers so\n * the interpolation math can be unit-tested in isolation from the renderer.\n *\n * A parsed channel (see the glTF loader) has the shape:\n * `{ node, path, times: Float32Array, values: Float32Array, stride, interpolation }`\n * where `stride` is the component count of one value (3 for translation/scale,\n * 4 for a rotation quaternion) and `interpolation` is `\"LINEAR\"` | `\"STEP\"` |\n * `\"CUBICSPLINE\"`.\n * @ignore\n */\n\n// reused result for findKeyframe — the value is consumed immediately by the\n// caller (sampleChannel destructures it on return), so a single shared object\n// keeps the per-frame sample path allocation-free.\nconst _kf = { i0: 0, i1: 0, alpha: 0 };\n\n/**\n * Locate the keyframe interval for time `t` in the (ascending) `times` array.\n * Clamps to the endpoints — glTF animations do not extrapolate beyond their\n * first/last keyframe. Returns the bracketing indices and the 0..1 blend factor.\n *\n * The returned object is **reused** across calls (read/copy its fields\n * immediately; don't retain the reference).\n * @param {ArrayLike<number>} times - keyframe times, ascending\n * @param {number} t - sample time (same units as `times`, i.e. seconds)\n * @returns {{ i0: number, i1: number, alpha: number }} reused result object\n * @ignore\n */\nexport function findKeyframe(times, t) {\n\tconst n = times.length;\n\tif (n === 0 || t <= times[0]) {\n\t\t_kf.i0 = 0;\n\t\t_kf.i1 = 0;\n\t\t_kf.alpha = 0;\n\t\treturn _kf;\n\t}\n\tif (t >= times[n - 1]) {\n\t\t_kf.i0 = n - 1;\n\t\t_kf.i1 = n - 1;\n\t\t_kf.alpha = 0;\n\t\treturn _kf;\n\t}\n\t// binary search for the last index whose time is <= t\n\tlet lo = 0;\n\tlet hi = n - 1;\n\twhile (lo < hi) {\n\t\tconst mid = (lo + hi + 1) >> 1;\n\t\tif (times[mid] <= t) {\n\t\t\tlo = mid;\n\t\t} else {\n\t\t\thi = mid - 1;\n\t\t}\n\t}\n\tconst span = times[lo + 1] - times[lo];\n\t_kf.i0 = lo;\n\t_kf.i1 = lo + 1;\n\t// guard against a zero-length span (duplicate keyframe times)\n\t_kf.alpha = span > 0 ? (t - times[lo]) / span : 0;\n\treturn _kf;\n}\n\n/**\n * Spherical-linear interpolation between two quaternions stored in `values` at\n * component offsets `o0` and `o1`. Picks the shortest arc (sign-flips the second\n * quaternion when the dot is negative) and falls back to normalized-lerp for\n * nearly-parallel inputs (where `sin(theta)` underflows). Result is normalized.\n * @param {ArrayLike<number>} values - flat quaternion buffer (xyzw per key)\n * @param {number} o0 - offset of the first quaternion\n * @param {number} o1 - offset of the second quaternion\n * @param {number} t - blend factor 0..1\n * @param {number[]} out - 4-element [x,y,z,w] result\n * @ignore\n */\nexport function slerpQuat(values, o0, o1, t, out) {\n\tconst ax = values[o0];\n\tconst ay = values[o0 + 1];\n\tconst az = values[o0 + 2];\n\tconst aw = values[o0 + 3];\n\tlet bx = values[o1];\n\tlet by = values[o1 + 1];\n\tlet bz = values[o1 + 2];\n\tlet bw = values[o1 + 3];\n\tlet cosom = ax * bx + ay * by + az * bz + aw * bw;\n\t// shortest path: negate the second quaternion if the dot is negative\n\tif (cosom < 0) {\n\t\tcosom = -cosom;\n\t\tbx = -bx;\n\t\tby = -by;\n\t\tbz = -bz;\n\t\tbw = -bw;\n\t}\n\tlet s0;\n\tlet s1;\n\tif (cosom > 0.9995) {\n\t\t// quaternions almost parallel — normalized lerp avoids a divide-by-~0\n\t\ts0 = 1 - t;\n\t\ts1 = t;\n\t} else {\n\t\tconst omega = Math.acos(cosom);\n\t\tconst sinom = Math.sin(omega);\n\t\ts0 = Math.sin((1 - t) * omega) / sinom;\n\t\ts1 = Math.sin(t * omega) / sinom;\n\t}\n\tlet ox = s0 * ax + s1 * bx;\n\tlet oy = s0 * ay + s1 * by;\n\tlet oz = s0 * az + s1 * bz;\n\tlet ow = s0 * aw + s1 * bw;\n\tconst len = Math.hypot(ox, oy, oz, ow) || 1;\n\tox /= len;\n\toy /= len;\n\toz /= len;\n\tow /= len;\n\tout[0] = ox;\n\tout[1] = oy;\n\tout[2] = oz;\n\tout[3] = ow;\n\treturn out;\n}\n\n/**\n * Sample one animation channel at time `t`, writing `channel.stride` components\n * into `out`. Rotation channels (stride 4) use {@link slerpQuat}; translation /\n * scale (stride 3) use component-wise linear interpolation. `STEP` holds the\n * lower keyframe; `CUBICSPLINE` uses the keyframe value (its tangents are\n * ignored — a deliberate Tier-1 approximation, not full spline evaluation).\n * @param {{times: ArrayLike<number>, values: ArrayLike<number>, stride: number, interpolation: string}} channel\n * @param {number} t - sample time in seconds\n * @param {number[]} out - destination, at least `channel.stride` long\n * @returns {number[]} `out`\n * @ignore\n */\nexport function sampleChannel(channel, t, out) {\n\tconst { times, values, stride, interpolation } = channel;\n\tlet { i0, i1, alpha } = findKeyframe(times, t);\n\tif (interpolation === \"STEP\") {\n\t\talpha = 0;\n\t}\n\t// CUBICSPLINE stores 3 values per keyframe (inTangent, value, outTangent);\n\t// the actual value is the middle third. We sample that value and linearly\n\t// blend (tangents ignored). LINEAR / STEP store a single value per keyframe.\n\tconst cubic = interpolation === \"CUBICSPLINE\";\n\tconst block = cubic ? stride * 3 : stride;\n\tconst valueOffset = cubic ? stride : 0;\n\tconst o0 = i0 * block + valueOffset;\n\tconst o1 = i1 * block + valueOffset;\n\tif (stride === 4) {\n\t\treturn slerpQuat(values, o0, o1, alpha, out);\n\t}\n\tfor (let c = 0; c < stride; c++) {\n\t\tout[c] = values[o0 + c] + (values[o1 + c] - values[o0 + c]) * alpha;\n\t}\n\treturn out;\n}\n","/**\n * Color-space bridge for glTF material factors.\n *\n * glTF 2.0 defines `pbrMetallicRoughness.baseColorFactor` in **linear** space\n * (spec §3.9.2), while a melonJS `tint` is an 8-bit **sRGB** value — the same\n * space as a CSS color or a texel out of a PNG. Handing the linear number\n * straight to `tint.setColor(f * 255)` therefore displays every untextured\n * glTF material far too light and desaturated: a linear `0.29` shows up as\n * sRGB `0.58`, so an authored mid-green renders as pale mint.\n *\n * The encode below is the standard sRGB transfer function.\n * @module level/gltf/srgb\n */\n\n/**\n * Encode one linear channel (0..1) to an 8-bit sRGB value (0..255).\n * @param {number} c - linear channel value\n * @returns {number} the sRGB-encoded channel, rounded to 0..255\n */\nexport function linearToSrgb8(c) {\n\t// guard the domain: exporters can emit slightly out-of-range factors, and\n\t// `Math.pow` on a negative base returns NaN, which would poison the tint\n\tconst v = c <= 0 ? 0 : c >= 1 ? 1 : c;\n\tconst s = v <= 0.0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - 0.055;\n\treturn Math.round(s * 255);\n}\n","import {\n\tcomposeTRS,\n\tcomposeTRSInto,\n\tmultiplyMatrixInto,\n} from \"../../loader/parsers/gltf.js\";\nimport { parseAnimationOptions } from \"../../renderable/animation.ts\";\nimport Container from \"../../renderable/container.js\";\nimport { hasVerticalExtent } from \"../../renderable/groundshadow.js\";\nimport InstancedMesh from \"../../renderable/instanced_mesh.js\";\nimport Mesh from \"../../renderable/mesh.js\";\nimport { fillInstances } from \"./GLTFScene.js\";\nimport { sampleChannel } from \"./gltf_sampler.js\";\nimport { linearToSrgb8 } from \"./srgb.js\";\n\n/**\n * additional import for TypeScript\n * @import { AnimationOptions } from \"../../renderable/animation.ts\";\n */\n\n// column-major identity, the root's parent transform\nconst IDENTITY16 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];\n\n// per-frame scratch reused while composing one node's local matrix (DFS visits\n// a node fully before recursing, and the local matrix is consumed by the\n// world-matrix multiply before the next node is touched, so a single shared\n// set is safe and keeps update() allocation-free per node)\nconst _t = [0, 0, 0];\nconst _r = [0, 0, 0, 1];\nconst _s = [1, 1, 1];\nconst _val = [0, 0, 0, 0];\nconst _localScratch = new Array(16);\n\n/**\n * @classdesc\n * A rig-driven 3D model loaded from an animated glTF/GLB asset. Unlike a static\n * {@link GLTFScene} (which flattens each node into an independent {@link Mesh}),\n * a `GLTFModel` keeps the node **hierarchy** intact so a parent transform\n * carries its children — e.g. rotating a character's `torso` moves the attached\n * `arm` and `head`. Each frame, the active animation clip is sampled, world\n * matrices are propagated down the tree, and every part mesh's placement is\n * re-derived.\n *\n * The animation API mirrors {@link Sprite} for familiarity — `setCurrentAnimation`,\n * `isCurrentAnimation`, `getAnimationNames`, `play`/`pause`, `animationpause` —\n * but uses the cleaner options form everywhere: `setCurrentAnimation(name, {\n * loop, speed, onComplete, next })`. Here `animationspeed` is a **playback\n * multiplier** (1 = authored speed), not a per-frame delay.\n *\n * Instances are created automatically by {@link GLTFScene} when the asset\n * defines animation channels; you usually obtain one via `level.load(...)`\n * rather than constructing it directly.\n * @augments Container\n */\nexport default class GLTFModel extends Container {\n\t/**\n\t * @param {object} data - the parsed glTF descriptor (`{ graph, animations, bounds, ... }`)\n\t * @param {object} [options]\n\t * @param {number} [options.scale=1] - pixels per glTF unit (uniform scene scale)\n\t * @param {boolean} [options.rightHanded=true] - glTF Y-up → engine Y-down via a rotation (no mirror)\n\t * @param {boolean} [options.lit=false] - render the part meshes through the lit batcher\n\t * @param {boolean} [options.castGroundShadow] - give the parts a ground shadow; omit to inherit the application setting\n\t * @param {number} [options.shadowGroundY] - world Y of the floor those shadows land on\n\t */\n\tconstructor(data, options = {}) {\n\t\tsuper(0, 0);\n\n\t\t/**\n\t\t * pixels per glTF unit (uniform scene scale)\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis.scale = options.scale ?? 1;\n\t\t// right-handed (glTF) → negate Z as well as Y so the Y-up→Y-down bridge\n\t\t// is a rotation, matching Mesh#rightHanded / GLTFScene\n\t\tthis._zSign = options.rightHanded !== false ? -1 : 1;\n\n\t\t// scene meshes carry their own world transform; the GPU depth test\n\t\t// resolves occlusion, so don't let the container reassign child depth\n\t\tthis.autoDepth = false;\n\n\t\t// This container is a logical group sitting at the world origin — its\n\t\t// child meshes carry absolute world placement themselves. It has no\n\t\t// meaningful anchor box, and its width/height are Infinity (the Container\n\t\t// default), so the base `preDraw` anchor offset `width * anchorPoint`\n\t\t// would be `Infinity * 0 = NaN` and NaN-poison the renderer transform,\n\t\t// silently dropping every child mesh. Opt out of the anchor offset\n\t\t// entirely (same mechanism Mesh uses on the Camera3d world path).\n\t\tthis.applyAnchorTransform = false;\n\n\t\t/** the node hierarchy keyed by glTF node index @ignore */\n\t\tthis._nodes = data.graph.nodes;\n\t\t/** root node indices @ignore */\n\t\tthis._roots = data.graph.roots;\n\t\t/** glTF node index → its part Mesh instances (one per primitive) @ignore */\n\t\tthis._meshByNode = {};\n\t\t/** glTF node index → cached rest (bind-pose) local matrix @ignore */\n\t\tthis._restMatrix = {};\n\t\t/**\n\t\t * glTF node index → its world matrix, a persistent 16-element buffer\n\t\t * recomputed in place every pose (a child reads its parent's buffer\n\t\t * during the DFS, so each node needs its own). Preallocated here so the\n\t\t * per-frame pose path allocates nothing.\n\t\t * @ignore\n\t\t */\n\t\tthis._world = {};\n\n\t\t// a generous per-part cull radius taken from the whole scene's bounds so\n\t\t// the model culls as a unit (a limb never pops out while the body is on\n\t\t// screen). Camera3d derives the cull sphere as √(w²+h²)/2, so a square\n\t\t// box of side `radius·√2` yields a sphere of exactly `radius`.\n\t\tconst b = data.bounds;\n\t\tconst dx = b.max[0] - b.min[0];\n\t\tconst dy = b.max[1] - b.min[1];\n\t\tconst dz = b.max[2] - b.min[2];\n\t\tconst radius = (Math.hypot(dx, dy, dz) / 2) * this.scale;\n\t\tconst boxSize = Math.max(radius, 1) * Math.SQRT2;\n\n\t\tconst lit = options.lit === true;\n\t\tconst rightHanded = options.rightHanded !== false;\n\t\t// tri-state: `undefined` falls through to the application setting\n\t\tconst castGroundShadow =\n\t\t\ttypeof options.castGroundShadow === \"boolean\"\n\t\t\t\t? options.castGroundShadow\n\t\t\t\t: undefined;\n\n\t\t// build the rest matrices + instantiate a Mesh per mesh-node primitive\n\t\tfor (const idx in this._nodes) {\n\t\t\tconst node = this._nodes[idx];\n\t\t\tthis._restMatrix[idx] = node.matrix\n\t\t\t\t? node.matrix\n\t\t\t\t: composeTRS(node.translation, node.rotation, node.scale);\n\t\t\t// persistent per-node world-matrix buffer (recomputed in place each pose)\n\t\t\tthis._world[idx] = new Array(16);\n\n\t\t\tfor (const prim of node.primitives) {\n\t\t\t\t// an instanced node inside an ANIMATED asset still instances:\n\t\t\t\t// the node's own TRS is animated, the records place the copies\n\t\t\t\t// within it\n\t\t\t\tconst MeshClass = prim.instances ? InstancedMesh : Mesh;\n\t\t\t\tconst mesh = new MeshClass(0, 0, {\n\t\t\t\t\tvertices: prim.vertices,\n\t\t\t\t\tuvs: prim.uvs,\n\t\t\t\t\tindices: prim.indices,\n\t\t\t\t\tnormals: prim.normals,\n\t\t\t\t\ttexture: prim.image,\n\t\t\t\t\twidth: boxSize,\n\t\t\t\t\theight: boxSize,\n\t\t\t\t\tscale: this.scale,\n\t\t\t\t\tnormalize: false,\n\t\t\t\t\trightHanded,\n\t\t\t\t\t// KHR_materials_unlit materials skip the lit path even in a lit scene\n\t\t\t\t\tlit: lit && prim.unlit !== true,\n\t\t\t\t\t// honor the glTF sampler wrap (default REPEAT) — many exporters\n\t\t\t\t\t// author UVs outside [0,1] that tile; clamping flattens them\n\t\t\t\t\ttextureRepeat: prim.textureRepeat,\n\t\t\t\t\t// honor the glTF sampler magnification filter (nearest = pixel-art)\n\t\t\t\t\ttextureFilter: prim.textureFilter,\n\t\t\t\t\t// alpha cutout threshold (glTF alphaMode MASK)\n\t\t\t\t\talphaCutoff: prim.alphaCutoff,\n\t\t\t\t\t// emissive color (glTF emissiveFactor) — self-illumination\n\t\t\t\t\temissive: prim.emissive,\n\t\t\t\t\t// specular highlight approximated from the material's\n\t\t\t\t\t// metallic/roughness factors (#1575)\n\t\t\t\t\tspecular: prim.specular,\n\t\t\t\t\tshininess: prim.shininess,\n\t\t\t\t\t// thin/flat double-sided parts must not be back-face culled\n\t\t\t\t\tcullBackFaces: prim.doubleSided !== true,\n\t\t\t\t\t// ground shadows (#1515), same scene-wide rule as the static\n\t\t\t\t\t// path: a blanket opt-in skips parts with no vertical extent\n\t\t\t\t\tcastGroundShadow:\n\t\t\t\t\t\tcastGroundShadow === true\n\t\t\t\t\t\t\t? hasVerticalExtent(prim.vertices, prim.vertexCount)\n\t\t\t\t\t\t\t: castGroundShadow,\n\t\t\t\t\tshadowGroundY: options.shadowGroundY,\n\t\t\t\t});\n\t\t\t\tif (prim.instances) {\n\t\t\t\t\tfillInstances(mesh, prim.instances);\n\t\t\t\t}\n\t\t\t\t// LINEAR per the glTF spec; a tint is 8-bit sRGB — see\n\t\t\t\t// `linearToSrgb8`\n\t\t\t\tconst f = prim.baseColorFactor;\n\t\t\t\tif (f) {\n\t\t\t\t\tmesh.tint.setColor(\n\t\t\t\t\t\tlinearToSrgb8(f[0]),\n\t\t\t\t\t\tlinearToSrgb8(f[1]),\n\t\t\t\t\t\tlinearToSrgb8(f[2]),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (prim.colors) {\n\t\t\t\t\tmesh.vertexColors = prim.colors;\n\t\t\t\t}\n\t\t\t\tmesh.name = node.name;\n\t\t\t\t(this._meshByNode[idx] ??= []).push(mesh);\n\t\t\t\tthis.addChild(mesh);\n\t\t\t}\n\t\t}\n\n\t\t// index the animation clips, pre-grouping each clip's channels by the\n\t\t// node they target (so sampling a node is a single map lookup)\n\t\t/** name → clip `{ name, duration, channelsByNode, animatedNodes }` @ignore */\n\t\tthis.anim = {};\n\t\tfor (const clip of data.animations ?? []) {\n\t\t\tconst channelsByNode = new Map();\n\t\t\tfor (const ch of clip.channels) {\n\t\t\t\tif (!channelsByNode.has(ch.node)) {\n\t\t\t\t\tchannelsByNode.set(ch.node, []);\n\t\t\t\t}\n\t\t\t\tchannelsByNode.get(ch.node).push(ch);\n\t\t\t}\n\t\t\tthis.anim[clip.name] = {\n\t\t\t\tname: clip.name,\n\t\t\t\tduration: clip.duration,\n\t\t\t\tchannelsByNode,\n\t\t\t\tanimatedNodes: new Set(channelsByNode.keys()),\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * playback multiplier for the current animation (1 = authored speed).\n\t\t * @type {number}\n\t\t * @default 1\n\t\t */\n\t\tthis.animationspeed = 1;\n\n\t\t/**\n\t\t * pause/resume the current animation without losing its pose or time.\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.animationpause = false;\n\n\t\t/**\n\t\t * a callback fired each time the current animation completes a cycle.\n\t\t * @type {Function}\n\t\t * @default undefined\n\t\t */\n\t\t// this.onended;\n\n\t\t// current animation state\n\t\t/** @ignore */\n\t\tthis.current = { name: undefined, time: 0, length: 0 };\n\t\t/** loop-completion callback (built from the options) @ignore */\n\t\tthis.resetAnim = undefined;\n\t\t/** set when a `loop:false` clip has finished its single cycle @ignore */\n\t\tthis._animDone = false;\n\n\t\t// pose to the bind/rest pose so the model is correctly assembled even\n\t\t// before any clip plays\n\t\tthis._pose();\n\t}\n\n\t/**\n\t * the names of every animation clip defined by the source asset.\n\t * @returns {string[]}\n\t * @example\n\t * model.getAnimationNames(); // [\"idle\", \"walk\", \"sprint\", ...]\n\t */\n\tgetAnimationNames() {\n\t\treturn Object.keys(this.anim);\n\t}\n\n\t/**\n\t * return true if `name` is the currently playing animation.\n\t * @param {string} name - animation clip id\n\t * @returns {boolean}\n\t */\n\tisCurrentAnimation(name) {\n\t\treturn this.current.name === name;\n\t}\n\n\t/**\n\t * play the given animation clip. The second argument mirrors {@link Sprite}\n\t * and accepts the same forms: omit to loop forever, a `string` to chain to\n\t * another clip when this one ends, a `function` legacy completion callback\n\t * (return `false` to hold the final pose), or an options object.\n\t * @param {string} name - animation clip id (see {@link GLTFModel#getAnimationNames})\n\t * @param {string|Function|AnimationOptions} [options] - loop / chain / completion behavior\n\t * @param {boolean} [preserveTime=false] - keep the current playback time instead of restarting at 0\n\t * @returns {GLTFModel} this, for chaining\n\t * @example\n\t * model.setCurrentAnimation(\"walk\");                       // loop forever\n\t * model.setCurrentAnimation(\"die\", { loop: false });       // play once, hold last pose\n\t * model.setCurrentAnimation(\"jump\", { next: \"idle\" });     // jump, then idle\n\t * model.setCurrentAnimation(\"walk\", { speed: 2 });         // twice as fast\n\t * model.setCurrentAnimation(\"emote-yes\", () => spawnFx()); // legacy callback\n\t */\n\tsetCurrentAnimation(name, options, preserveTime = false) {\n\t\tif (this.anim[name] === undefined) {\n\t\t\tthrow new Error(\"animation id '\" + name + \"' not defined\");\n\t\t}\n\t\tif (this.isCurrentAnimation(name)) {\n\t\t\treturn this;\n\t\t}\n\t\tthis.current.name = name;\n\t\tthis.current.length = this.anim[name].duration;\n\t\tconst opts = parseAnimationOptions(options);\n\t\tthis.animationspeed = opts.speed;\n\t\tthis._animDone = false;\n\t\tconst onComplete = opts.onComplete;\n\t\tif (opts.legacyFn) {\n\t\t\t// legacy bare-function callback (return false → hold the last pose)\n\t\t\tthis.resetAnim = onComplete;\n\t\t} else if (typeof opts.next === \"string\") {\n\t\t\tconst next = opts.next;\n\t\t\tthis.resetAnim = () => {\n\t\t\t\tif (typeof onComplete === \"function\") {\n\t\t\t\t\tonComplete();\n\t\t\t\t}\n\t\t\t\tthis.setCurrentAnimation(next);\n\t\t\t};\n\t\t} else if (opts.loop === false) {\n\t\t\t// play once: fire onComplete, hold the final pose\n\t\t\tthis.resetAnim = () => {\n\t\t\t\tif (typeof onComplete === \"function\") {\n\t\t\t\t\tonComplete();\n\t\t\t\t}\n\t\t\t\tthis._animDone = true;\n\t\t\t\treturn false;\n\t\t\t};\n\t\t} else if (typeof onComplete === \"function\") {\n\t\t\tthis.resetAnim = () => {\n\t\t\t\tonComplete();\n\t\t\t};\n\t\t} else {\n\t\t\tthis.resetAnim = undefined;\n\t\t}\n\t\tif (!preserveTime) {\n\t\t\tthis.current.time = 0;\n\t\t}\n\t\tthis._pose();\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Play an animation clip, or resume the current one. A shorthand for\n\t * {@link GLTFModel#setCurrentAnimation}: call with a clip name to switch to\n\t * (and start) it, or with no argument to resume after {@link GLTFModel#pause}.\n\t * Always clears the paused state.\n\t * @param {string} [name] - clip id to play; omit to just resume\n\t * @param {string|Function|AnimationOptions} [options] - loop / chain / completion behavior (see {@link GLTFModel#setCurrentAnimation})\n\t * @returns {GLTFModel} this, for chaining\n\t * @example\n\t * model.play(\"walk\");                  // switch to + play \"walk\"\n\t * model.play(\"die\", { loop: false });  // play once, hold the last pose\n\t * model.pause();\n\t * model.play();                        // resume\n\t */\n\tplay(name, options) {\n\t\tthis.animationpause = false;\n\t\tif (name !== undefined) {\n\t\t\tthis.setCurrentAnimation(name, options);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Pause the current animation, freezing it at its current pose. Resume with\n\t * {@link GLTFModel#play}.\n\t * @returns {GLTFModel} this, for chaining\n\t */\n\tpause() {\n\t\tthis.animationpause = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Stop playback and reset the rig to its bind/rest pose (no clip active).\n\t * After this {@link GLTFModel#isCurrentAnimation} is false for every clip;\n\t * call {@link GLTFModel#play} to start again. (Use {@link GLTFModel#pause}\n\t * instead to freeze in place.)\n\t * @returns {GLTFModel} this, for chaining\n\t */\n\tstop() {\n\t\tthis.current.name = undefined;\n\t\tthis.current.time = 0;\n\t\tthis.current.length = 0;\n\t\tthis.resetAnim = undefined;\n\t\tthis._animDone = false;\n\t\tthis.animationpause = false;\n\t\t// re-pose with no active clip → every node falls back to its rest matrix\n\t\tthis._pose();\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Advance the active clip and re-pose the rig.\n\t * @param {number} dt - elapsed time since the last update, in milliseconds\n\t * @returns {boolean} true if the model (or any child) needs redrawing\n\t * @protected\n\t */\n\tupdate(dt) {\n\t\tif (\n\t\t\tthis.current.name !== undefined &&\n\t\t\t!this.animationpause &&\n\t\t\t!this._animDone &&\n\t\t\tthis.current.length > 0\n\t\t) {\n\t\t\tconst duration = this.current.length;\n\t\t\t// glTF keyframe times are in seconds; dt is in milliseconds\n\t\t\tthis.current.time += (dt / 1000) * this.animationspeed;\n\t\t\tif (this.current.time >= duration) {\n\t\t\t\tif (typeof this.onended === \"function\") {\n\t\t\t\t\tthis.onended();\n\t\t\t\t}\n\t\t\t\tif (typeof this.resetAnim === \"function\") {\n\t\t\t\t\tif (this.resetAnim() === false) {\n\t\t\t\t\t\t// hold the final pose\n\t\t\t\t\t\tthis.current.time = duration;\n\t\t\t\t\t} else if (this.current.time >= duration) {\n\t\t\t\t\t\t// default loop / loop-with-callback: wrap the overflow\n\t\t\t\t\t\t// (guarded — a chain may already have reset the time)\n\t\t\t\t\t\tthis.current.time %= duration;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.current.time %= duration;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._pose();\n\t\t\tthis.isDirty = true;\n\t\t}\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * Sample the active clip (if any) and propagate world transforms down the\n\t * node tree, writing each part mesh's placement. Nodes the current clip does\n\t * not animate use their cached rest matrix.\n\t * @ignore\n\t */\n\t_pose() {\n\t\tconst clip = this.current.name ? this.anim[this.current.name] : null;\n\t\tconst t = this.current.time;\n\t\tfor (const root of this._roots) {\n\t\t\tthis._visit(root, IDENTITY16, clip, t);\n\t\t}\n\t}\n\n\t/**\n\t * DFS one node: compose its local matrix, multiply by the parent world,\n\t * apply to its meshes, recurse into children.\n\t * @ignore\n\t */\n\t_visit(idx, parentWorld, clip, t) {\n\t\tconst node = this._nodes[idx];\n\t\tif (node === undefined) {\n\t\t\treturn;\n\t\t}\n\t\t// `local` may be the shared `_localScratch` (animated node) — it's\n\t\t// consumed by the multiply below before any child overwrites it.\n\t\tconst local = this._localMatrix(idx, clip, t);\n\t\t// write into this node's persistent world buffer (distinct from\n\t\t// parentWorld and local, so the in-place multiply is safe); children\n\t\t// read it as their parentWorld during recursion.\n\t\tconst world = multiplyMatrixInto(this._world[idx], parentWorld, local);\n\t\tconst meshes = this._meshByNode[idx];\n\t\tif (meshes !== undefined) {\n\t\t\tfor (const mesh of meshes) {\n\t\t\t\tthis._applyWorldToMesh(mesh, world);\n\t\t\t}\n\t\t}\n\t\tfor (const child of node.children) {\n\t\t\tthis._visit(child, world, clip, t);\n\t\t}\n\t}\n\n\t/**\n\t * The node's local matrix: sampled TRS when the active clip animates it\n\t * (starting from the rest pose, overriding only the animated components),\n\t * otherwise the cached rest matrix.\n\t * @returns {number[]} 16-element column-major matrix\n\t * @ignore\n\t */\n\t_localMatrix(idx, clip, t) {\n\t\tconst node = this._nodes[idx];\n\t\tif (clip === null || !clip.animatedNodes.has(node.index)) {\n\t\t\t// cached rest matrix — never mutated, safe to return directly\n\t\t\treturn this._restMatrix[idx];\n\t\t}\n\t\t// start from the rest TRS, then override the channels this clip drives\n\t\t_t[0] = node.translation[0];\n\t\t_t[1] = node.translation[1];\n\t\t_t[2] = node.translation[2];\n\t\t_r[0] = node.rotation[0];\n\t\t_r[1] = node.rotation[1];\n\t\t_r[2] = node.rotation[2];\n\t\t_r[3] = node.rotation[3];\n\t\t_s[0] = node.scale[0];\n\t\t_s[1] = node.scale[1];\n\t\t_s[2] = node.scale[2];\n\t\tfor (const ch of clip.channelsByNode.get(node.index)) {\n\t\t\tsampleChannel(ch, t, _val);\n\t\t\tif (ch.path === \"translation\") {\n\t\t\t\t_t[0] = _val[0];\n\t\t\t\t_t[1] = _val[1];\n\t\t\t\t_t[2] = _val[2];\n\t\t\t} else if (ch.path === \"rotation\") {\n\t\t\t\t_r[0] = _val[0];\n\t\t\t\t_r[1] = _val[1];\n\t\t\t\t_r[2] = _val[2];\n\t\t\t\t_r[3] = _val[3];\n\t\t\t} else {\n\t\t\t\t_s[0] = _val[0];\n\t\t\t\t_s[1] = _val[1];\n\t\t\t\t_s[2] = _val[2];\n\t\t\t}\n\t\t}\n\t\t// in-place into the shared scratch (consumed immediately by the caller's\n\t\t// world multiply, before the next node is visited)\n\t\treturn composeTRSInto(_localScratch, _t, _r, _s);\n\t}\n\n\t/**\n\t * Split a node's world matrix into the renderable placement a {@link Mesh}'s\n\t * Camera3d path expects: the translation (scaled, Y/Z-bridged) becomes\n\t * `pos`/`depth`, the rotation+scale becomes `currentTransform` (translation\n\t * zeroed). Mirrors the static {@link GLTFScene} center-split, recomputed per\n\t * frame.\n\t * @ignore\n\t */\n\t_applyWorldToMesh(mesh, world) {\n\t\tmesh.pos.set(world[12] * this.scale, -world[13] * this.scale);\n\t\tmesh.depth = this._zSign * world[14] * this.scale;\n\t\tconst v = mesh.currentTransform.val;\n\t\tv.set(world);\n\t\tv[12] = 0;\n\t\tv[13] = 0;\n\t\tv[14] = 0;\n\t\tmesh.isDirty = true;\n\t}\n}\n","import { Light3d } from \"../../lighting/light3d.ts\";\nimport { getGLTF } from \"../../loader/loader.js\";\nimport { boundingRadius } from \"../../math/vertex.ts\";\nimport { hasVerticalExtent } from \"../../renderable/groundshadow.js\";\nimport InstancedMesh from \"../../renderable/instanced_mesh.js\";\nimport Mesh from \"../../renderable/mesh.js\";\nimport { writeInstanceTRS } from \"../../video/gpu/instancerecord.ts\";\nimport GLTFModel from \"./GLTFModel.js\";\nimport { linearToSrgb8 } from \"./srgb.js\";\n\n/**\n * @classdesc\n * A loadable 3D scene parsed from a glTF / GLB asset. Instances are created\n * and registered with the {@link level} director (usually automatically by\n * the preloader), so a glTF scene loads with the same one-call ergonomics as\n * a Tiled map: `level.load(\"myScene\")`.\n *\n * Each glTF mesh node is instantiated as a {@link Mesh} carrying its own\n * world transform, so the scene's relative scale and layout are preserved.\n * View the result under a `Camera3d` for a coherent perspective.\n */\nexport default class GLTFScene {\n\t/**\n\t * @param {string} levelId - the glTF/GLB asset name (as preloaded)\n\t */\n\tconstructor(levelId) {\n\t\t/**\n\t\t * the level/asset name\n\t\t * @type {string}\n\t\t */\n\t\tthis.name = levelId;\n\t\t/**\n\t\t * level format discriminator used by the level director's dispatch\n\t\t * @type {string}\n\t\t */\n\t\tthis.format = \"gltf\";\n\t\t/**\n\t\t * the parsed scene descriptor (`{ nodes, cameras, lights, bounds }`)\n\t\t * @type {object}\n\t\t */\n\t\tthis.data = getGLTF(levelId);\n\t}\n\n\t/**\n\t * the world-space bounds of the scene (`{ min, max }` in glTF units), or\n\t * undefined if the scene failed to load. Handy for framing a `Camera3d`.\n\t * @returns {object|undefined}\n\t */\n\tget bounds() {\n\t\treturn this.data?.bounds;\n\t}\n\n\t/**\n\t * the cameras parsed from the glTF scene (each with a world matrix +\n\t * perspective parameters), or an empty array.\n\t * @returns {Array}\n\t */\n\tget cameras() {\n\t\treturn this.data?.cameras ?? [];\n\t}\n\n\t/**\n\t * Instantiate every glTF mesh node as a `Mesh` in the given container.\n\t * Called by the level director on `level.load(...)`.\n\t * @param {Container} container - the target container (e.g. `game.world`)\n\t * @param {object} [options]\n\t * @param {number} [options.scale=1] - pixels per glTF unit (uniform scene scale)\n\t * @param {boolean} [options.rightHanded=true] - convert glTF Y-up right-handed\n\t * geometry to the engine's Y-down via a rotation (no mirror). See the wiki.\n\t * @param {boolean} [options.lights=true] - add the scene's authored\n\t * `KHR_lights_punctual` lights — directional suns, point and spot lamps\n\t * (plus a soft ambient fill) — to the world as {@link Light3d}\n\t * renderables, so the meshes are lit as set up in the authoring tool.\n\t * Set false to keep the meshes unlit / manage lighting yourself with\n\t * `world.addChild(new Light3d(...))`. Each instantiated light carries\n\t * its authored name, so `world.getChildByName(\"Sun\")` finds it for\n\t * runtime tuning (day/night cycles, flicker).\n\t * @param {number} [options.lightIntensityScale] - multiply each light's\n\t * AUTHORED intensity by this factor instead of normalizing it to 1.\n\t * glTF stores physical units — lux for suns (a Blender daylight sun is\n\t * ~1000+), candela for lamps — which blow out the engine's stylized\n\t * half-Lambert shading when used raw, so the default keeps every light\n\t * at unit intensity and lets the app tune. With this option the\n\t * authored ratios survive: e.g. `0.001` maps a 1000-lux sun to 1 while\n\t * a half-strength 500-lux fill lands at 0.5.\n\t */\n\taddTo(container, options = {}) {\n\t\tif (!this.data) {\n\t\t\treturn;\n\t\t}\n\t\tconst scale = options.scale ?? 1;\n\t\tconst rightHanded = options.rightHanded !== false;\n\t\t// tri-state on purpose: `undefined` means \"the caller said nothing\",\n\t\t// which falls through to the application setting at draw time\n\t\tconst castGroundShadow =\n\t\t\ttypeof options.castGroundShadow === \"boolean\"\n\t\t\t\t? options.castGroundShadow\n\t\t\t\t: undefined;\n\t\tconst zSign = rightHanded ? -1 : 1;\n\n\t\t// the scene is lit when it carries ANY shading-capable authored light\n\t\t// (directional sun, point or spot lamp) and the caller didn't opt out\n\t\t// — meshes then render through the lit batcher. A lamp-only scene must\n\t\t// count too, or its instantiated lights would shine on unlit meshes.\n\t\tconst lit =\n\t\t\toptions.lights !== false &&\n\t\t\t(this.data.lights ?? []).some((l) => {\n\t\t\t\treturn (\n\t\t\t\t\tl.type === \"directional\" || l.type === \"point\" || l.type === \"spot\"\n\t\t\t\t);\n\t\t\t});\n\n\t\t// scene meshes carry their own world transform — keep the container\n\t\t// from reassigning per-child depth (the GPU depth test resolves\n\t\t// occlusion between meshes under Camera3d)\n\t\tcontainer.autoDepth = false;\n\n\t\t// Animated asset → keep the node hierarchy intact inside one rig-driven\n\t\t// GLTFModel (a parent transform carries its children). Static asset →\n\t\t// the flat-mesh path below. The model is named after the asset so it can\n\t\t// be retrieved from the world (`world.getChildByName(name)[0]`) to drive\n\t\t// playback. Lights are still instantiated (shared block at the end).\n\t\tif ((this.data.animations ?? []).length > 0) {\n\t\t\tconst model = new GLTFModel(this.data, {\n\t\t\t\tscale,\n\t\t\t\trightHanded,\n\t\t\t\tlit,\n\t\t\t\tcastGroundShadow,\n\t\t\t\tshadowGroundY: options.shadowGroundY,\n\t\t\t});\n\t\t\tmodel.name = this.name;\n\t\t\tcontainer.addChild(model);\n\t\t\tthis._addLights(container, zSign, scale, options);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const node of this.data.nodes) {\n\t\t\tconst m = node.world;\n\n\t\t\t// The node's world placement splits into a CENTER (the matrix\n\t\t\t// translation, in render space) carried by the renderable's\n\t\t\t// pos/depth, and the rotation/scale carried by currentTransform.\n\t\t\t// Why: Camera3d frustum culling and depth sorting key off the\n\t\t\t// renderable's pos — if every mesh stayed at pos (0,0,0) with all\n\t\t\t// placement hidden inside currentTransform, they would all cull as\n\t\t\t// a single point at the world origin and the whole scene would pop\n\t\t\t// in/out together. The rendered geometry is identical either way\n\t\t\t// (`_projectVerticesWorld` adds the pos offset back).\n\t\t\tconst cx = m[12] * scale;\n\t\t\tconst cy = -m[13] * scale;\n\t\t\tconst cz = zSign * m[14] * scale;\n\n\t\t\t// Conservative world-space bounding radius: the farthest vertex\n\t\t\t// from the node origin, taken through the node's rotation/scale\n\t\t\t// columns and the scene scale. Feeds the renderable's bounds so\n\t\t\t// Camera3d's bounding-sphere test only culls a mesh once its\n\t\t\t// geometry is genuinely outside the view (not just its center).\n\t\t\tconst radius = boundingRadius(node.vertices, node.vertexCount, m) * scale;\n\t\t\t// Camera3d derives the cull sphere radius as √(w² + h²) / 2, so a\n\t\t\t// square bounds box of side `radius · √2` yields a sphere of\n\t\t\t// exactly `radius`. Guard against a zero-size (point) box.\n\t\t\tconst boxSize = Math.max(radius, 1) * Math.SQRT2;\n\n\t\t\t// EXT_mesh_gpu_instancing: the node is one geometry stamped out\n\t\t\t// many times (a scattered forest, a crowd). One InstancedMesh\n\t\t\t// draws the lot in a single call instead of N separate meshes,\n\t\t\t// each with its own GPU copy of identical geometry.\n\t\t\tconst instances = node.instances;\n\t\t\t// `count: 0` is an authored EMPTY scatter (everything culled at\n\t\t\t// author time) — it must draw zero copies, not one stray prototype\n\t\t\t// at the node origin, so it still becomes an InstancedMesh\n\t\t\tconst MeshClass = instances ? InstancedMesh : Mesh;\n\t\t\tconst mesh = new MeshClass(0, 0, {\n\t\t\t\tvertices: node.vertices,\n\t\t\t\tuvs: node.uvs,\n\t\t\t\tindices: node.indices,\n\t\t\t\tnormals: node.normals,\n\t\t\t\ttexture: node.image,\n\t\t\t\twidth: boxSize,\n\t\t\t\theight: boxSize,\n\t\t\t\tscale,\n\t\t\t\tnormalize: false,\n\t\t\t\trightHanded,\n\t\t\t\t// honor the glTF sampler wrap (default REPEAT) so tiling UVs\n\t\t\t\t// (UVs outside [0,1]) sample correctly instead of clamping flat\n\t\t\t\ttextureRepeat: node.textureRepeat,\n\t\t\t\t// honor the glTF sampler magnification filter (nearest for pixel-art)\n\t\t\t\ttextureFilter: node.textureFilter,\n\t\t\t\t// alpha cutout threshold (glTF alphaMode MASK) — discard fully\n\t\t\t\t// transparent texels so cutout props (foliage, fences) read crisp\n\t\t\t\talphaCutoff: node.alphaCutoff,\n\t\t\t\t// emissive color (glTF emissiveFactor) — self-illumination so neon /\n\t\t\t\t// lava / screens glow regardless of scene lighting\n\t\t\t\temissive: node.emissive,\n\t\t\t\t// specular highlight approximated from the material's\n\t\t\t\t// metallic/roughness factors (#1575)\n\t\t\t\tspecular: node.specular,\n\t\t\t\tshininess: node.shininess,\n\t\t\t\t// light this mesh (via the lit batcher) when the scene has lights —\n\t\t\t\t// unless the material is KHR_materials_unlit (baked lighting, must\n\t\t\t\t// not be shaded again)\n\t\t\t\tlit: lit && node.unlit !== true,\n\t\t\t\t// honor the glTF material's double-sided flag: thin/flat props\n\t\t\t\t// (coins, fences, foliage) are double-sided and must NOT be\n\t\t\t\t// back-face culled, or half their faces vanish\n\t\t\t\tcullBackFaces: node.doubleSided !== true,\n\t\t\t\t// Ground shadows (#1515). A scene-wide opt-in skips nodes with\n\t\t\t\t// no vertical extent: a glTF scene ships its ground as a flat\n\t\t\t\t// plane, and shadowing that with itself smears a blob across\n\t\t\t\t// the whole floor. Left unset by the caller this stays\n\t\t\t\t// `undefined`, which is what lets the application-level\n\t\t\t\t// setting through — passing `false` explicitly opts the scene\n\t\t\t\t// out of that default.\n\t\t\t\tcastGroundShadow:\n\t\t\t\t\tcastGroundShadow === true\n\t\t\t\t\t\t? hasVerticalExtent(node.vertices, node.vertexCount)\n\t\t\t\t\t\t: castGroundShadow,\n\t\t\t\tshadowGroundY: options.shadowGroundY,\n\t\t\t});\n\t\t\tif (instances) {\n\t\t\t\tfillInstances(mesh, instances);\n\t\t\t}\n\t\t\t// material baseColorFactor → mesh tint, so an untextured solid-color\n\t\t\t// material renders its color instead of the white-pixel fallback.\n\t\t\t// (RGB only; alpha/transparency is a separate feature — the mesh\n\t\t\t// path renders opaque.) Composes with COLOR_0 and the texture: the\n\t\t\t// batcher does factor × vertexColor × texel, matching glTF.\n\t\t\t//\n\t\t\t// The factor is LINEAR per the glTF spec and a tint is 8-bit sRGB,\n\t\t\t// so it has to be encoded rather than scaled by 255 — see\n\t\t\t// `linearToSrgb8`.\n\t\t\tconst f = node.baseColorFactor;\n\t\t\tif (f) {\n\t\t\t\tmesh.tint.setColor(\n\t\t\t\t\tlinearToSrgb8(f[0]),\n\t\t\t\t\tlinearToSrgb8(f[1]),\n\t\t\t\t\tlinearToSrgb8(f[2]),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// per-vertex colors (COLOR_0) — multiplied by the tint per vertex\n\t\t\tif (node.colors) {\n\t\t\t\tmesh.vertexColors = node.colors;\n\t\t\t}\n\n\t\t\t// rotation/scale only — the translation is carried by pos/depth\n\t\t\tconst local = m.slice();\n\t\t\tlocal[12] = 0;\n\t\t\tlocal[13] = 0;\n\t\t\tlocal[14] = 0;\n\t\t\tmesh.currentTransform.val.set(local);\n\t\t\tmesh.pos.set(cx, cy);\n\t\t\tmesh.depth = cz;\n\t\t\tmesh.name = node.name;\n\t\t\tcontainer.addChild(mesh);\n\t\t}\n\n\t\tthis._addLights(container, zSign, scale, options);\n\t}\n\n\t/**\n\t * Add the scene's authored lights — directional, point and spot — (plus a soft ambient fill) to\n\t * the world as {@link Light3d} renderables, so the meshes are lit by the\n\t * same sun set up in the authoring tool (Blender etc.). Shared by the static\n\t * and animated paths. The lights are ordinary world children — the level\n\t * director's `container.reset()` removes them on the next load, exactly like\n\t * {@link Light2d}, so there's nothing to track or tear down here.\n\t * @param {Container} container - the target container the lights are added to\n\t * @param {number} zSign - the Y-up→Y-down Z bridge sign (rightHanded → -1)\n\t * @param {number} scale - the scene's world scale (positions/ranges follow it)\n\t * @param {object} options - the `addTo` options (`lights` toggle)\n\t * @ignore\n\t */\n\t_addLights(container, zSign, scale, options) {\n\t\tif (options.lights === false) {\n\t\t\treturn;\n\t\t}\n\t\t// glTF intensities are physical units (lux for directional, candela for\n\t\t// point/spot — often in the thousands), which blow out the stylized\n\t\t// half-Lambert shading when used raw. Default: normalize every light to\n\t\t// unit intensity and let the app tune. With `lightIntensityScale` the\n\t\t// authored values are kept, multiplied by the given factor, so relative\n\t\t// light strengths from the authoring tool survive.\n\t\tconst intensityScale =\n\t\t\ttypeof options.lightIntensityScale === \"number\"\n\t\t\t\t? options.lightIntensityScale\n\t\t\t\t: null;\n\t\tlet added = 0;\n\t\tfor (const light of this.data.lights ?? []) {\n\t\t\tconst d = light.direction;\n\t\t\t// glTF-space → render space: the same Y-down / rightHanded Y/Z\n\t\t\t// bridge the geometry uses, for directions AND positions\n\t\t\tconst direction = [d[0], -d[1], zSign * d[2]];\n\t\t\tconst intensity =\n\t\t\t\tintensityScale !== null ? light.intensity * intensityScale : 1;\n\t\t\tlet light3d;\n\t\t\tif (light.type === \"directional\") {\n\t\t\t\tlight3d = new Light3d({\n\t\t\t\t\ttype: \"directional\",\n\t\t\t\t\tdirection,\n\t\t\t\t\tcolor: [light.color[0], light.color[1], light.color[2]],\n\t\t\t\t\tintensity,\n\t\t\t\t});\n\t\t\t} else if (light.type === \"point\" || light.type === \"spot\") {\n\t\t\t\tconst p = light.position;\n\t\t\t\tlight3d = new Light3d({\n\t\t\t\t\ttype: light.type,\n\t\t\t\t\tposition: [p[0] * scale, -p[1] * scale, zSign * p[2] * scale],\n\t\t\t\t\tdirection,\n\t\t\t\t\tcolor: [light.color[0], light.color[1], light.color[2]],\n\t\t\t\t\tintensity,\n\t\t\t\t\t// glTF range is in source units — scale like the geometry.\n\t\t\t\t\t// Absent means unbounded in the spec; the engine's quadratic\n\t\t\t\t\t// falloff needs a scale, so fall back to the Light3d default\n\t\t\t\t\trange:\n\t\t\t\t\t\ttypeof light.range === \"number\" ? light.range * scale : undefined,\n\t\t\t\t\tinnerConeAngle: light.innerConeAngle,\n\t\t\t\t\touterConeAngle: light.outerConeAngle,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// carry the authored name so the app can look the light up for\n\t\t\t// runtime tuning (e.g. `world.getChildByName(\"Sun\")`)\n\t\t\tif (light.name) {\n\t\t\t\tlight3d.name = light.name;\n\t\t\t}\n\t\t\tcontainer.addChild(light3d);\n\t\t\tadded++;\n\t\t}\n\t\t// a soft ambient fill so the shadow side of lit meshes isn't pure black.\n\t\t// `KHR_lights_punctual` has no ambient light type, so this is an engine\n\t\t// default; only meaningful when the scene actually has directional lights\n\t\t// (otherwise the meshes render fullbright / unlit).\n\t\tif (added > 0) {\n\t\t\tcontainer.addChild(\n\t\t\t\tnew Light3d({ type: \"ambient\", color: \"#ffffff\", intensity: 0.3 }),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Director cleanup hook (parity with `TMXTileMap.destroy`). Nothing to do —\n\t * the meshes and lights are ordinary world children, removed by the\n\t * director's `container.reset()` on the next load.\n\t * @ignore\n\t */\n\tdestroy() {}\n}\n\n/**\n * Fill an {@link InstancedMesh}'s records from an\n * `EXT_mesh_gpu_instancing` node's per-instance TRS arrays.\n *\n * The records are written in the node's own (glTF, Y-up, right-handed)\n * space, **verbatim** — no scene scale, no axis bridge. That is not a\n * simplification but the consequence of where the instance transform sits\n * in the chain the shader evaluates:\n *\n *     clip = projection × view × model(group) × instance × vertex\n *\n * The group's model matrix already carries the scene scale and the Y/Z\n * bridge, and it is applied AFTER the instance transform — so an instance\n * expressed in the same space as the vertices comes out placed correctly.\n * Pre-scaling or pre-bridging the records here applies both twice (which\n * put the first version of this forest 26× too far out, and well outside\n * the far plane).\n * @param {InstancedMesh} mesh - the mesh to fill\n * @param {object} instances - `{count, translation, rotation, scale}`\n * @ignore\n */\nexport function fillInstances(mesh, instances) {\n\tconst { count, translation, rotation, scale } = instances;\n\t// A negative per-instance scale mirrors the geometry, which inverts its\n\t// winding — but face culling is decided once per mesh, so those instances\n\t// would render inside-out. Blender mirrors on linked duplicates produce\n\t// exactly this, so say so rather than let it look like a modelling error.\n\tif (scale !== undefined && mesh.cullBackFaces === true) {\n\t\tfor (let i = 0; i < scale.length; i++) {\n\t\t\tif (scale[i] < 0) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"glTF: EXT_mesh_gpu_instancing has mirrored (negative-scale) instances — face culling is per-mesh, so those instances render inside-out; set `doubleSided` on the material to avoid it\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tmesh.instanceCount = count;\n\tconst floats = mesh.instanceLayout.floats;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst t3 = i * 3;\n\t\tconst r4 = i * 4;\n\t\twriteInstanceTRS(\n\t\t\tmesh.instanceBuffer,\n\t\t\ti * floats,\n\t\t\ttranslation ? translation[t3] : 0,\n\t\t\ttranslation ? translation[t3 + 1] : 0,\n\t\t\ttranslation ? translation[t3 + 2] : 0,\n\t\t\trotation ? rotation[r4] : 0,\n\t\t\trotation ? rotation[r4 + 1] : 0,\n\t\t\trotation ? rotation[r4 + 2] : 0,\n\t\t\trotation ? rotation[r4 + 3] : 1,\n\t\t\tscale ? scale[t3] : 1,\n\t\t\tscale ? scale[t3 + 1] : 1,\n\t\t\tscale ? scale[t3 + 2] : 1,\n\t\t);\n\t}\n\tmesh.markInstancesDirty(0, count);\n}\n","// latest supported Tiled map format version\nexport const TILED_SUPPORTED_VERSION = \"1.12\";\n\n// bitmask constants to check for flipped & rotated tiles\nexport const TMX_FLIP_H = 0x80000000;\nexport const TMX_FLIP_V = 0x40000000;\nexport const TMX_FLIP_AD = 0x20000000;\nexport const TMX_CLEAR_BIT_MASK = ~(TMX_FLIP_H | TMX_FLIP_V | TMX_FLIP_AD);\n\n// constant to identify the collision object layer\nexport const COLLISION_GROUP = \"collision\";\n","import { degToRad } from \"../../math/math.ts\";\nimport { Matrix2d } from \"../../math/matrix2d.ts\";\nimport { Bounds } from \"../../physics/bounds.ts\";\nimport Sprite from \"../../renderable/sprite.js\";\nimport {\n\tTMX_CLEAR_BIT_MASK,\n\tTMX_FLIP_AD,\n\tTMX_FLIP_H,\n\tTMX_FLIP_V,\n} from \"./constants.js\";\n\n// flip-mask bit layout shared with TMXLayer.layerData's G channel\nconst FLIP_H_BIT = 1 << 0;\nconst FLIP_V_BIT = 1 << 1;\nconst FLIP_AD_BIT = 1 << 2;\n\n/**\n * Apply a flip-mask transform to a Matrix2d in-place. Resets the matrix to\n * identity first, then applies the H / V / AD operations to flip / rotate a\n * tile of `width × height` pixels around its center.\n *\n * This is the typed-array equivalent of `Tile.setTileTransform()` — both share\n * the same math, but this variant is driven by a packed 3-bit mask (H=1, V=2,\n * AD=4) so callers that have raw layerData bytes can build a flip transform\n * without constructing a Tile.\n *\n * @param {Matrix2d} transform - the matrix to fill (mutated in place)\n * @param {number} flipMask - 3-bit packed flip mask\n * @param {number} width - tile width in pixels\n * @param {number} height - tile height in pixels\n * @returns {Matrix2d} the same matrix, for chaining\n * @ignore\n */\nexport function buildFlipTransform(transform, flipMask, width, height) {\n\tconst halfW = width / 2;\n\tconst halfH = height / 2;\n\tconst flippedH = (flipMask & FLIP_H_BIT) !== 0;\n\tconst flippedV = (flipMask & FLIP_V_BIT) !== 0;\n\tconst flippedAD = (flipMask & FLIP_AD_BIT) !== 0;\n\n\ttransform.identity();\n\ttransform.translate(halfW, halfH);\n\tif (flippedAD) {\n\t\ttransform.rotate(degToRad(-90));\n\t\ttransform.scale(-1, 1);\n\t}\n\tif (flippedH) {\n\t\ttransform.scale(flippedAD ? 1 : -1, flippedAD ? -1 : 1);\n\t}\n\tif (flippedV) {\n\t\ttransform.scale(flippedAD ? -1 : 1, flippedAD ? 1 : -1);\n\t}\n\ttransform.translate(-halfW, -halfH);\n\treturn transform;\n}\n\n/**\n * a basic tile object\n * @category Tilemap\n */\nexport default class Tile extends Bounds {\n\t/**\n\t * @param {number} x - x index of the Tile in the map\n\t * @param {number} y - y index of the Tile in the map\n\t * @param {number} gid - tile gid\n\t * @param {TMXTileset} tileset - the corresponding tileset object\n\t */\n\tconstructor(x, y, gid, tileset) {\n\t\tsuper();\n\n\t\t// determine the tile size from per-tile image or tileset grid\n\t\tlet width;\n\t\tlet height;\n\t\tif (tileset.isCollection) {\n\t\t\tconst tileImage = tileset.getTileImage(gid & TMX_CLEAR_BIT_MASK);\n\t\t\twidth = tileImage.width;\n\t\t\theight = tileImage.height;\n\t\t} else {\n\t\t\twidth = tileset.tilewidth;\n\t\t\theight = tileset.tileheight;\n\t\t}\n\n\t\tthis.setMinMax(0, 0, width, height);\n\n\t\t/**\n\t\t * the corresponding tileset\n\t\t * @type {TMXTileset}\n\t\t */\n\t\tthis.tileset = tileset;\n\n\t\t/**\n\t\t * the tile transformation matrix (if flipped)\n\t\t * @type {Matrix2d|null}\n\t\t * @ignore\n\t\t */\n\t\tthis.currentTransform = null;\n\n\t\t/**\n\t\t * tile column position in the map\n\t\t * @type {number}\n\t\t */\n\t\tthis.col = x;\n\n\t\t/**\n\t\t * tile row position in the map\n\t\t * @type {number}\n\t\t */\n\t\tthis.row = y;\n\n\t\t/**\n\t\t * the global tile ID (with flip bits cleared)\n\t\t * @type {number}\n\t\t */\n\t\tthis.tileId = gid;\n\n\t\t/**\n\t\t * True if the tile is flipped horizontally\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.flippedX = (this.tileId & TMX_FLIP_H) !== 0;\n\n\t\t/**\n\t\t * True if the tile is flipped vertically\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.flippedY = (this.tileId & TMX_FLIP_V) !== 0;\n\n\t\t/**\n\t\t * True if the tile is flipped anti-diagonally\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.flippedAD = (this.tileId & TMX_FLIP_AD) !== 0;\n\n\t\t/**\n\t\t * Global flag that indicates if the tile is flipped\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.flipped = this.flippedX || this.flippedY || this.flippedAD;\n\n\t\t// create and apply transformation matrix if required\n\t\tif (this.flipped) {\n\t\t\tthis.currentTransform = new Matrix2d();\n\t\t\tthis.setTileTransform(this.currentTransform.identity());\n\t\t}\n\n\t\t// clear out the flip flags and keep the actual tileId\n\t\tthis.tileId &= TMX_CLEAR_BIT_MASK;\n\t}\n\n\t/**\n\t * set the transformation matrix for this tile\n\t * @param {Matrix2d} transform - the transformation matrix to apply\n\t * @ignore\n\t */\n\tsetTileTransform(transform) {\n\t\tconst halfW = this.width / 2;\n\t\tconst halfH = this.height / 2;\n\n\t\ttransform.translate(halfW, halfH);\n\t\tif (this.flippedAD) {\n\t\t\ttransform.rotate(degToRad(-90));\n\t\t\ttransform.scale(-1, 1);\n\t\t}\n\t\tif (this.flippedX) {\n\t\t\ttransform.scale(this.flippedAD ? 1 : -1, this.flippedAD ? -1 : 1);\n\t\t}\n\t\tif (this.flippedY) {\n\t\t\ttransform.scale(this.flippedAD ? -1 : 1, this.flippedAD ? 1 : -1);\n\t\t}\n\t\ttransform.translate(-halfW, -halfH);\n\t}\n\n\t/**\n\t * return a renderable object for this Tile object\n\t * @param {object} [settings] - see {@link Sprite}\n\t * @returns {Renderable} a me.Sprite object\n\t */\n\tgetRenderable(settings) {\n\t\tlet renderable;\n\t\tconst tileset = this.tileset;\n\n\t\tconst localId = this.tileId - tileset.firstgid;\n\n\t\tif (tileset.animations.has(localId)) {\n\t\t\t// animated tile — create an animated sprite\n\t\t\tconst frames = [];\n\t\t\tconst frameId = [];\n\t\t\tfor (const frame of tileset.animations.get(localId).frames) {\n\t\t\t\tframeId.push(frame.tileid);\n\t\t\t\tframes.push({\n\t\t\t\t\tname: \"\" + frame.tileid,\n\t\t\t\t\tdelay: frame.duration,\n\t\t\t\t});\n\t\t\t}\n\t\t\trenderable = tileset.texture.createAnimationFromName(frameId, settings);\n\t\t\trenderable.addAnimation(localId, frames);\n\t\t\trenderable.setCurrentAnimation(localId);\n\t\t} else if (tileset.isCollection) {\n\t\t\t// collection tile — create a sprite from the tile image\n\t\t\tconst image = tileset.getTileImage(this.tileId);\n\t\t\trenderable = new Sprite(0, 0, { image });\n\t\t\trenderable.anchorPoint.set(0, 0);\n\t\t\trenderable.scale(\n\t\t\t\tsettings.width / this.width,\n\t\t\t\tsettings.height / this.height,\n\t\t\t);\n\t\t\tif (settings.rotation !== undefined) {\n\t\t\t\trenderable.anchorPoint.set(0.5, 0.5);\n\t\t\t\trenderable.rotate(settings.rotation);\n\t\t\t\trenderable.translate(settings.width / 2, settings.height / 2);\n\t\t\t\t// clear rotation to prevent double-application by the caller\n\t\t\t\tsettings.rotation = undefined;\n\t\t\t}\n\t\t} else {\n\t\t\t// regular tile — create a sprite from the tileset atlas\n\t\t\trenderable = tileset.texture.createSpriteFromName(\n\t\t\t\tthis.tileId - tileset.firstgid,\n\t\t\t\tsettings,\n\t\t\t);\n\t\t\trenderable.anchorPoint.set(0, 0);\n\t\t}\n\n\t\t// apply any H/V/AD flip transforms\n\t\tthis.setTileTransform(renderable.currentTransform);\n\n\t\treturn renderable;\n\t}\n}\n","import { imgList } from \"../../loader/cache.js\";\nimport { colorPool } from \"../../math/color.ts\";\nimport { decode, decodeBase64Image } from \"../../utils/decode.ts\";\nimport { xmlToObject } from \"../../utils/xml.ts\";\n\n// counter for generating unique cache keys for embedded images\nlet embeddedImageId = 0;\n\n/**\n * Decode an embedded base64 image, cache it in imgList, and return\n * a generated filename (with extension) suitable for getImage().\n * Works for both XML-parsed data (base64 string) and JSON data.\n * @ignore\n * @param {string} base64 - raw base64-encoded image data\n * @param {string} [format=\"png\"] - image format\n * @param {number} [width] - image width hint\n * @param {number} [height] - image height hint\n * @returns {string} generated filename (e.g. \"__embedded_0.png\")\n */\nexport function cacheEmbeddedImage(base64, format = \"png\", width, height) {\n\tconst name = `__embedded_${embeddedImageId++}`;\n\timgList[name] = decodeBase64Image(base64, format, width, height);\n\treturn `${name}.${format}`;\n}\n\n/**\n * If the given data object has an embedded base64 image (JSON `imagedata`\n * property), decode it, cache it, and replace with a generated filename.\n * @ignore\n * @param {object} data - tileset, tile, or layer data\n */\nexport function resolveEmbeddedImage(data) {\n\tif (data.imagedata) {\n\t\tconst format = data.imageformat || \"png\";\n\t\tdata.image = cacheEmbeddedImage(\n\t\t\tdata.imagedata,\n\t\t\tformat,\n\t\t\tdata.imagewidth ? +data.imagewidth : undefined,\n\t\t\tdata.imageheight ? +data.imageheight : undefined,\n\t\t);\n\t\tdelete data.imagedata;\n\t\tdelete data.imageformat;\n\t}\n}\n\n/**\n * Convert a Tiled blend mode name to a canonical melonJS blend mode.\n * Tiled uses \"add\" for additive blending; melonJS renderers accept\n * \"add\", \"additive\", and \"lighter\" as equivalent. This helper normalizes\n * the Tiled \"add\" value to \"lighter\" for internal consistency.\n * Returns \"normal\" when the mode is undefined or explicitly \"normal\";\n * all other values are passed through to the renderer (which will fall\n * back to \"normal\" for unsupported modes).\n * @param {string} [mode] - the Tiled blend mode\n * @returns {string} the melonJS blend mode name\n */\nexport function tiledBlendMode(mode) {\n\tif (typeof mode === \"undefined\" || mode === \"normal\") {\n\t\treturn \"normal\";\n\t}\n\treturn mode === \"add\" ? \"lighter\" : mode;\n}\n\n/**\n * Apply an opacity multiplier to a renderable and its child renderable (if any).\n * @ignore\n * @param {Renderable} obj - the renderable to apply to\n * @param {number} opacity - the opacity multiplier\n */\nexport function applyObjectOpacity(obj, opacity) {\n\tobj.setOpacity(obj.getOpacity() * opacity);\n\tif (\n\t\ttypeof obj.renderable !== \"undefined\" &&\n\t\tobj.renderable.isRenderable === true\n\t) {\n\t\tobj.renderable.setOpacity(obj.renderable.getOpacity() * opacity);\n\t}\n}\n\n/**\n * Propagate a blend mode to a renderable and its child renderable (if any).\n * Only applies when the object still has the default \"normal\" blend mode.\n * @ignore\n * @param {Renderable} obj - the renderable to apply to\n * @param {string} blendMode - the blend mode to propagate\n */\nexport function propagateBlendMode(obj, blendMode) {\n\tif (\n\t\tblendMode !== \"normal\" &&\n\t\tobj.isRenderable === true &&\n\t\tobj.blendMode === \"normal\"\n\t) {\n\t\tobj.blendMode = blendMode;\n\t\tif (\n\t\t\ttypeof obj.renderable !== \"undefined\" &&\n\t\t\tobj.renderable.isRenderable === true &&\n\t\t\tobj.renderable.blendMode === \"normal\"\n\t\t) {\n\t\t\tobj.renderable.blendMode = blendMode;\n\t\t}\n\t}\n}\n\n/**\n * Parse a Tiled tint color hex string into a melonJS Color object.\n * @ignore\n * @param {string} tintcolor - hex color string from Tiled (e.g. \"#ff0000\")\n * @returns {Color|undefined} parsed Color, or undefined if no tint\n */\nexport function parseTintColor(tintcolor) {\n\tif (typeof tintcolor !== \"undefined\") {\n\t\treturn colorPool.get().parseHex(tintcolor, true);\n\t}\n\treturn undefined;\n}\n\n// pre-compiled regex for #ARGB → #RGBA color conversion\n// matches #ARGB (5 chars) or #AARRGGBB (9 chars)\nconst SHORT_ARGB = /^#([\\da-fA-F])([\\da-fA-F]{3})$/;\nconst LONG_ARGB = /^#([\\da-fA-F]{2})([\\da-fA-F]{6})$/;\n\n/**\n * Coerce a raw TMX property string into its typed JS value.\n * Handles int, float, bool, json:, eval:, #ARGB colors,\n * and auto-detection for untyped properties.\n * @ignore\n * @param {string} name - property name (used for ratio/anchorPoint normalization)\n * @param {string} type - declared Tiled type (\"int\",\"float\",\"bool\",\"string\", etc.)\n * @param {*} raw - raw value (string from XML, or already-typed from JSON)\n * @returns {*} coerced value\n */\nfunction coerceTMXValue(name, type, raw) {\n\tif (typeof raw !== \"string\" && type !== \"list\") {\n\t\t// already typed (e.g. JSON maps with native bool/number values)\n\t\treturn raw;\n\t}\n\n\tswitch (type) {\n\t\tcase \"int\":\n\t\tcase \"float\":\n\t\t\treturn Number(raw);\n\n\t\tcase \"bool\":\n\t\t\treturn raw === \"true\";\n\n\t\tcase \"list\":\n\t\t\t// JSON list property: value is an array of { type, value } objects\n\t\t\tif (Array.isArray(raw)) {\n\t\t\t\treturn raw.map((item) => {\n\t\t\t\t\t// use \"\" as name to avoid name-dependent coercions (e.g. ratio/anchorPoint)\n\t\t\t\t\treturn coerceTMXValue(\"\", item.type || \"string\", item.value);\n\t\t\t\t});\n\t\t\t}\n\t\t\t// XML list: items already collected as array by normalizeTMX\n\t\t\treturn raw;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t// --- untyped / \"string\" type: auto-detect ---\n\n\t// empty string → true (legacy TMX flag behavior)\n\tif (raw === \"\") {\n\t\treturn true;\n\t}\n\n\t// boolean strings\n\tif (raw === \"true\" || raw === \"false\") {\n\t\treturn raw === \"true\";\n\t}\n\n\t// numeric strings — inline check avoids function-call overhead\n\t// matches: optional sign, digits with optional decimal, or leading decimal\n\tif (\n\t\traw === raw.trim() &&\n\t\traw !== \"\" &&\n\t\t!Number.isNaN(+raw) &&\n\t\t/^[+-]?(\\d+(\\.\\d+)?|\\.\\d+)$/.test(raw)\n\t) {\n\t\tconst num = Number(raw);\n\t\t// ratio and anchorPoint are expanded to {x, y} vectors\n\t\tif (name === \"ratio\" || name === \"anchorPoint\") {\n\t\t\treturn { x: num, y: num };\n\t\t}\n\t\treturn num;\n\t}\n\n\t// json: prefix (case-insensitive)\n\tif ((raw.length > 5 && raw[0] === \"j\") || raw[0] === \"J\") {\n\t\tconst lower = raw.slice(0, 5).toLowerCase();\n\t\tif (lower === \"json:\") {\n\t\t\tconst body = raw.slice(5);\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(body);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Unable to parse JSON: ${body}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// eval: prefix (case-insensitive)\n\tif (raw.length > 5 && (raw[0] === \"e\" || raw[0] === \"E\")) {\n\t\tconst lower = raw.slice(0, 5).toLowerCase();\n\t\tif (lower === \"eval:\") {\n\t\t\tconst expr = raw.slice(5);\n\t\t\ttry {\n\t\t\t\t// eslint-disable-next-line\n\t\t\t\treturn Function(`'use strict';return (${expr})`)();\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Unable to evaluate: ${expr}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// #ARGB → #RGBA color conversion (Tiled stores alpha first)\n\tlet colorMatch;\n\tif (\n\t\traw[0] === \"#\" &&\n\t\t((colorMatch = SHORT_ARGB.exec(raw)) || (colorMatch = LONG_ARGB.exec(raw)))\n\t) {\n\t\treturn `#${colorMatch[2]}${colorMatch[1]}`;\n\t}\n\n\t// plain string — return as-is\n\treturn raw;\n}\n\n/**\n * Flatten image child object into parent (used by tile and tileset).\n * Moves source → image, width → imagewidth, height → imageheight.\n * For embedded images (no source, has data), decodes and caches the image.\n * @ignore\n */\nfunction flattenImage(obj) {\n\tif (obj.image) {\n\t\tobj.imagewidth = obj.image.width;\n\t\tobj.imageheight = obj.image.height;\n\t\tif (obj.image.source) {\n\t\t\tobj.image = obj.image.source;\n\t\t} else if (obj.image.data) {\n\t\t\t// embedded XML image: <data encoding=\"base64\">...</data>\n\t\t\tconst format = obj.image.format || \"png\";\n\t\t\tconst base64 = obj.image.data.text || obj.image.data;\n\t\t\tobj.image = cacheEmbeddedImage(\n\t\t\t\tbase64,\n\t\t\t\tformat,\n\t\t\t\tobj.image.width ? +obj.image.width : undefined,\n\t\t\t\tobj.image.height ? +obj.image.height : undefined,\n\t\t\t);\n\t\t} else {\n\t\t\tobj.image = obj.image.source;\n\t\t}\n\t}\n}\n\n/**\n * Normalizer callback for xmlToObject — converts TMX XML into Tiled JSON format.\n * @ignore\n */\nfunction normalizeTMX(obj, item, parse) {\n\tconst nodeName = item.nodeName;\n\n\tswitch (nodeName) {\n\t\tcase \"data\": {\n\t\t\tconst data = parse(item);\n\t\t\tconst encoding = data.encoding || \"xml\";\n\t\t\tconst compression = data.compression;\n\n\t\t\t// infinite maps: decode each chunk\n\t\t\tif (data.chunks !== undefined) {\n\t\t\t\tconst srcChunks = data.chunks;\n\t\t\t\tconst chunks = obj.chunks || (obj.chunks = []);\n\t\t\t\tfor (let i = 0, len = srcChunks.length; i < len; i++) {\n\t\t\t\t\tconst c = srcChunks[i];\n\t\t\t\t\tchunks.push({\n\t\t\t\t\t\tx: +c.x,\n\t\t\t\t\t\ty: +c.y,\n\t\t\t\t\t\twidth: +c.width,\n\t\t\t\t\t\theight: +c.height,\n\t\t\t\t\t\tdata: decode(c.text, encoding, compression),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tobj.encoding = \"none\";\n\t\t\t}\n\n\t\t\t// finite maps (guard: data.text can exist alongside chunks)\n\t\t\tif (data.text !== undefined && obj.chunks === undefined) {\n\t\t\t\tobj.data = decode(data.text, encoding, compression);\n\t\t\t\tobj.encoding = \"none\";\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"chunk\": {\n\t\t\tconst chunks = obj.chunks || (obj.chunks = []);\n\t\t\tchunks.push(parse(item));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"imagelayer\":\n\t\tcase \"layer\":\n\t\tcase \"objectgroup\":\n\t\tcase \"group\": {\n\t\t\tconst layer = parse(item);\n\t\t\tlayer.type = nodeName === \"layer\" ? \"tilelayer\" : nodeName;\n\t\t\tif (layer.image) {\n\t\t\t\t// flattenImage equivalent for image layers\n\t\t\t\tif (layer.image.source) {\n\t\t\t\t\tlayer.image = layer.image.source;\n\t\t\t\t} else if (layer.image.data) {\n\t\t\t\t\tconst format = layer.image.format || \"png\";\n\t\t\t\t\tconst base64 = layer.image.data.text || layer.image.data;\n\t\t\t\t\tlayer.image = cacheEmbeddedImage(\n\t\t\t\t\t\tbase64,\n\t\t\t\t\t\tformat,\n\t\t\t\t\t\tlayer.image.width ? +layer.image.width : undefined,\n\t\t\t\t\t\tlayer.image.height ? +layer.image.height : undefined,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst layers = obj.layers || (obj.layers = []);\n\t\t\tlayers.push(layer);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"animation\":\n\t\t\tobj.animation = parse(item).frames;\n\t\t\tbreak;\n\n\t\tcase \"frame\":\n\t\tcase \"object\": {\n\t\t\tconst key = nodeName + \"s\";\n\t\t\tconst arr = obj[key] || (obj[key] = []);\n\t\t\tarr.push(parse(item));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"tile\": {\n\t\t\tconst tile = parse(item);\n\t\t\tflattenImage(tile);\n\t\t\tconst tiles = obj.tiles || (obj.tiles = {});\n\t\t\ttiles[tile.id] = tile;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"tileset\": {\n\t\t\tconst tileset = parse(item);\n\t\t\tflattenImage(tileset);\n\t\t\tconst tilesets = obj.tilesets || (obj.tilesets = []);\n\t\t\ttilesets.push(tileset);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"polygon\":\n\t\tcase \"polyline\": {\n\t\t\tconst raw = parse(item).points;\n\t\t\tconst pairs = raw.split(\" \");\n\t\t\tconst points = new Array(pairs.length);\n\t\t\tfor (let i = 0, len = pairs.length; i < len; i++) {\n\t\t\t\tconst sep = pairs[i].indexOf(\",\");\n\t\t\t\tpoints[i] = {\n\t\t\t\t\tx: +pairs[i].slice(0, sep),\n\t\t\t\t\ty: +pairs[i].slice(sep + 1),\n\t\t\t\t};\n\t\t\t}\n\t\t\tobj[nodeName] = points;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"properties\":\n\t\t\tobj.properties = parse(item);\n\t\t\tbreak;\n\n\t\tcase \"item\": {\n\t\t\t// list item: coerce and append to the parent's items array\n\t\t\tconst itemData = parse(item);\n\t\t\tconst items = obj.items || (obj.items = []);\n\t\t\titems.push(\n\t\t\t\tcoerceTMXValue(\n\t\t\t\t\t\"\",\n\t\t\t\t\titemData.type || \"string\",\n\t\t\t\t\titemData.value !== undefined ? itemData.value : itemData.text,\n\t\t\t\t),\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"property\": {\n\t\t\tconst prop = parse(item);\n\t\t\tif (prop.type === \"class\") {\n\t\t\t\t// class properties have nested <properties> with member values\n\t\t\t\tobj[prop.name] = prop.properties || {};\n\t\t\t} else if (prop.type === \"list\") {\n\t\t\t\t// list properties: items collected via \"item\" case above\n\t\t\t\tobj[prop.name] = prop.items || [];\n\t\t\t} else {\n\t\t\t\tobj[prop.name] = coerceTMXValue(\n\t\t\t\t\tprop.name,\n\t\t\t\t\t// in XML, type is undefined for \"string\" values\n\t\t\t\t\tprop.type || \"string\",\n\t\t\t\t\tprop.value !== undefined ? prop.value : prop.text,\n\t\t\t\t);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"image\":\n\t\t\t// parse <image> without the TMX normalizer so that a <data> child\n\t\t\t// (embedded base64 image) is not mistakenly decoded as tile layer data\n\t\t\tobj.image = xmlToObject(item);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tobj[nodeName] = parse(item);\n\t\t\tbreak;\n\t}\n}\n\n/**\n * a collection of utility functions for parsing TMX maps\n * @namespace TMXUtils\n */\n\n// re-export generic decode utilities so existing consumers of TMXUtils can still access them\nexport { decode, setInflateFunction } from \"../../utils/decode.ts\";\n\n/**\n * Parse a XML TMX object and returns the corresponding javascript object\n * @memberof TMXUtils\n * @param {Document} xml - XML TMX object\n * @returns {object} Javascript object\n */\nexport function parse(xml) {\n\treturn xmlToObject(xml, normalizeTMX);\n}\n\n/**\n * Apply TMX Properties to the given object\n * @memberof TMXUtils\n * @param {object} obj - object to apply the properties to\n * @param {object} data - TMX data object\n */\nexport function applyTMXProperties(obj, data) {\n\tconst properties = data.properties;\n\tif (properties === undefined) {\n\t\treturn;\n\t}\n\n\t// new Tiled JSON format: array of { name, type, value }\n\tif (Array.isArray(properties)) {\n\t\tfor (let i = 0, len = properties.length; i < len; i++) {\n\t\t\tconst prop = properties[i];\n\t\t\tobj[prop.name] = coerceTMXValue(\n\t\t\t\tprop.name,\n\t\t\t\tprop.type || \"string\",\n\t\t\t\tprop.value,\n\t\t\t);\n\t\t}\n\t\treturn;\n\t}\n\n\t// old JSON format: flat { key: value } with optional separate propertytypes\n\tconst types = data.propertytypes;\n\tconst keys = Object.keys(properties);\n\tfor (let i = 0, len = keys.length; i < len; i++) {\n\t\tconst key = keys[i];\n\t\tconst prop = properties[key];\n\n\t\t// old \"new\" format: { 0: { name, type, value }, 1: ... }\n\t\t// require numeric key + name + type to avoid matching class property values\n\t\tif (\n\t\t\tprop !== null &&\n\t\t\ttypeof prop === \"object\" &&\n\t\t\t!isNaN(key) &&\n\t\t\tprop.name !== undefined &&\n\t\t\tprop.type !== undefined\n\t\t) {\n\t\t\tobj[prop.name] = coerceTMXValue(\n\t\t\t\tprop.name,\n\t\t\t\tprop.type || \"string\",\n\t\t\t\tprop.value !== undefined ? prop.value : prop,\n\t\t\t);\n\t\t} else {\n\t\t\t// flat key-value pair\n\t\t\tconst type = types !== undefined ? types[key] : \"string\";\n\t\t\tobj[key] = coerceTMXValue(key, type, prop);\n\t\t}\n\t}\n}\n","/**\n * the function used to decompress zlib/gzip data\n * @ignore\n */\nlet inflateFunction:\n\t| ((data: string, format: string) => Uint32Array)\n\t| undefined;\n\n/**\n * set the function used to inflate gzip/zlib data\n * @param fn - inflate function\n */\nexport function setInflateFunction(\n\tfn: ((data: string, format: string) => Uint32Array) | undefined,\n) {\n\tinflateFunction = fn;\n}\n\n/**\n * decompress and decode zlib/gzip data\n * @param data - Base64 encoded and compressed data\n * @param format - compressed data format (\"gzip\",\"zlib\", \"zstd\")\n * @returns Decoded and decompressed data\n */\nexport function decompress(data: string, format: string): Uint32Array {\n\tif (typeof inflateFunction === \"function\") {\n\t\treturn inflateFunction(data, format);\n\t} else {\n\t\tthrow new Error(\n\t\t\t\"No inflate function set — GZIP/ZLIB decompression not supported!\",\n\t\t);\n\t}\n}\n\n/**\n * Decode a CSV encoded array into a binary array\n * @param input - CSV formatted data (only numbers, everything else will be converted to NaN)\n * @returns Decoded data\n */\nexport function decodeCSV(input: string): number[] {\n\tconst trimmed = input.trim();\n\n\t// count commas to pre-allocate (avoids array resizing)\n\tlet count = 1;\n\tfor (let i = 0, len = trimmed.length; i < len; i++) {\n\t\tif (trimmed.charCodeAt(i) === 44 /* comma */) {\n\t\t\tcount++;\n\t\t}\n\t}\n\n\tconst result = new Array<number>(count);\n\tlet idx = 0;\n\tlet start = 0;\n\n\tfor (let i = 0, len = trimmed.length; i <= len; i++) {\n\t\tconst ch = i < len ? trimmed.charCodeAt(i) : 44; // treat end-of-string as comma\n\t\t// skip newlines (10 = \\n, 13 = \\r)\n\t\tif (ch === 10 || ch === 13) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === 44 /* comma */) {\n\t\t\tresult[idx++] = +trimmed.slice(start, i);\n\t\t\tstart = i + 1;\n\t\t}\n\t}\n\n\t// trim trailing empty entries from trailing commas\n\tresult.length = idx;\n\treturn result;\n}\n\n/**\n * Decode a base64 encoded string into a byte array\n * @param input - Base64 encoded data\n * @param bytes - number of bytes per array entry\n * @returns Decoded data\n */\nexport function decodeBase64AsArray(\n\tinput: string,\n\tbytes: number = 1,\n): Uint32Array {\n\tconst dec = globalThis.atob(input.replace(/[^A-Za-z0-9+/=]/g, \"\"));\n\tconst len = (dec.length / bytes) | 0;\n\tconst ar = new Uint32Array(len);\n\n\tif (bytes === 4) {\n\t\t// fast path for the common case (tile data is always 4 bytes per entry)\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tconst base = i << 2; // i * 4\n\t\t\tar[i] =\n\t\t\t\tdec.charCodeAt(base) |\n\t\t\t\t(dec.charCodeAt(base + 1) << 8) |\n\t\t\t\t(dec.charCodeAt(base + 2) << 16) |\n\t\t\t\t(dec.charCodeAt(base + 3) << 24);\n\t\t}\n\t} else {\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tlet val = 0;\n\t\t\tconst base = i * bytes;\n\t\t\tfor (let j = bytes - 1; j >= 0; --j) {\n\t\t\t\tval += dec.charCodeAt(base + j) << (j << 3);\n\t\t\t}\n\t\t\tar[i] = val;\n\t\t}\n\t}\n\treturn ar;\n}\n\n/**\n * Decode a base64 encoded image string into an HTMLImageElement\n * @param base64 - Base64 encoded image data (raw, without data URI prefix)\n * @param format - image format (e.g. \"png\", \"jpg\")\n * @param width - optional image width hint\n * @param height - optional image height hint\n * @returns an HTMLImageElement with the decoded image\n */\n// map common file extensions to MIME types where they differ\nconst mimeTypes: Record<string, string> = {\n\tjpg: \"image/jpeg\",\n\tsvg: \"image/svg+xml\",\n};\n\nexport function decodeBase64Image(\n\tbase64: string,\n\tformat: string = \"png\",\n\twidth?: number,\n\theight?: number,\n): HTMLImageElement {\n\tconst img = new Image();\n\tif (width != null) {\n\t\timg.width = width;\n\t}\n\tif (height != null) {\n\t\timg.height = height;\n\t}\n\tconst mime = mimeTypes[format] || `image/${format}`;\n\timg.src = `data:${mime};base64,${base64}`;\n\treturn img;\n}\n\n/**\n * Decode an encoded array into a binary array\n * @param data - data to be decoded\n * @param encoding - data encoding (\"csv\", \"base64\", \"none\")\n * @param compression - data compression (\"none\", \"gzip\", \"zlib\", \"zstd\")\n * @returns Decoded data\n */\nexport function decode(\n\tdata: string | number[],\n\tencoding?: string,\n\tcompression?: string,\n): number[] | Uint32Array {\n\tconst comp = compression || \"none\";\n\tconst enc = encoding || \"none\";\n\n\tswitch (enc) {\n\t\tcase \"csv\":\n\t\t\treturn decodeCSV(data as string);\n\n\t\tcase \"base64\":\n\t\t\tif (comp !== \"none\") {\n\t\t\t\treturn decompress(data as string, comp);\n\t\t\t} else {\n\t\t\t\treturn decodeBase64AsArray(data as string, 4);\n\t\t\t}\n\n\t\tcase \"none\":\n\t\t\treturn data as number[];\n\n\t\tcase \"xml\":\n\t\t\tthrow new Error(\"XML encoding is deprecated, use base64 instead\");\n\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown layer encoding: ${enc}`);\n\t}\n}\n","// DOM Node type constants\nconst ELEMENT_NODE = 1;\nconst TEXT_NODE = 3;\n\n/**\n * Normalizer callback type for {@link xmlToObject}.\n * Called for each child element node during XML-to-object conversion.\n * @param obj - the parent object being built\n * @param child - the child XML element to process\n * @param parse - recursive parser (calls xmlToObject with the same normalizer)\n */\nexport type XMLNormalizer = (\n\tobj: Record<string, unknown>,\n\tchild: Element,\n\tparse: (node: Element) => Record<string, unknown>,\n) => void;\n\n/**\n * Convert an XML element tree into a plain JS object.\n *\n * - Element attributes become string properties\n * - Text nodes are concatenated (trimmed) into a `text` property\n * - Child elements are processed by the optional `normalizer` callback;\n *   without one, each child is recursively converted using its `nodeName` as key\n * @param element - XML element to convert\n * @param normalizer - optional callback to handle child elements\n * @returns plain object representation of the XML element\n */\nexport function xmlToObject(\n\telement: Element,\n\tnormalizer?: XMLNormalizer,\n): Record<string, unknown> {\n\tconst obj: Record<string, unknown> = {};\n\n\t// only Element nodes (nodeType 1) have attributes; Document nodes (nodeType 9) do not\n\tif (element.nodeType === ELEMENT_NODE) {\n\t\tconst attributes = element.attributes;\n\t\tfor (let i = 0, len = attributes.length; i < len; i++) {\n\t\t\tconst attr = attributes[i];\n\t\t\tobj[attr.name] = attr.value;\n\t\t}\n\t}\n\n\t// only allocate the closure when a normalizer needs it\n\tconst parse = normalizer\n\t\t? (node: Element) => xmlToObject(node, normalizer)\n\t\t: undefined;\n\n\tlet text = \"\";\n\n\tconst children = element.childNodes;\n\tfor (let i = 0, len = children.length; i < len; i++) {\n\t\tconst node = children[i];\n\t\tif (node.nodeType === ELEMENT_NODE) {\n\t\t\tif (parse) {\n\t\t\t\tnormalizer!(obj, node as Element, parse);\n\t\t\t} else {\n\t\t\t\tobj[(node as Element).nodeName] = xmlToObject(node as Element);\n\t\t\t}\n\t\t} else if (node.nodeType === TEXT_NODE) {\n\t\t\ttext += node.nodeValue!.trim();\n\t\t}\n\t}\n\n\tif (text) {\n\t\tobj.text = text;\n\t}\n\n\treturn obj;\n}\n","import { vector2dPool } from \"../../math/vector2d.ts\";\nimport Renderable from \"../../renderable/renderable.js\";\nimport CanvasRenderer from \"../../video/canvas/canvas_renderer\";\nimport { createCanvas } from \"../../video/canvas_factory.js\";\nimport {\n\tTMX_CLEAR_BIT_MASK,\n\tTMX_FLIP_AD,\n\tTMX_FLIP_H,\n\tTMX_FLIP_V,\n} from \"./constants.js\";\nimport Tile from \"./TMXTile.js\";\nimport * as TMXUtils from \"./TMXUtils.js\";\n\n// flip-mask bit layout for layerData's G channel\nconst FLIP_H_BIT = 1 << 0;\nconst FLIP_V_BIT = 1 << 1;\nconst FLIP_AD_BIT = 1 << 2;\n\n// session-wide latch for the \"gpuTilemap requested but no WebGL2\" warning.\n// Emitted at most once across all TMXLayer instances so a multi-layer map\n// doesn't spam the console; deferred to the first layer that actually hits\n// the fallback so apps without any tilemap stay quiet (the prior heads-up\n// at Application init time fired regardless of whether a TMX layer was\n// ever loaded).\nlet _warnedNoGpuTileSupportOnce = false;\n\n/**\n * extract a 3-bit flip mask from a raw 32-bit GID (Tiled's flip bits live in\n * the upper 3 bits)\n * @ignore\n */\nfunction flipMaskFromGid(gid) {\n\treturn (\n\t\t(gid & TMX_FLIP_H ? FLIP_H_BIT : 0) |\n\t\t(gid & TMX_FLIP_V ? FLIP_V_BIT : 0) |\n\t\t(gid & TMX_FLIP_AD ? FLIP_AD_BIT : 0)\n\t);\n}\n\n/**\n * extract a 3-bit flip mask from a Tile object's boolean flip flags\n * @ignore\n */\nfunction flipMaskFromTile(tile) {\n\treturn (\n\t\t(tile.flippedX ? FLIP_H_BIT : 0) |\n\t\t(tile.flippedY ? FLIP_V_BIT : 0) |\n\t\t(tile.flippedAD ? FLIP_AD_BIT : 0)\n\t);\n}\n\n/**\n * reconstruct a legacy 32-bit GID (with Tiled's high flip bits set) from the\n * cleaned GID and a 3-bit flip mask, for passing to the Tile constructor\n * @ignore\n */\nfunction gidWithFlips(gid, flipMask) {\n\treturn (\n\t\tgid |\n\t\t(flipMask & FLIP_H_BIT ? TMX_FLIP_H : 0) |\n\t\t(flipMask & FLIP_V_BIT ? TMX_FLIP_V : 0) |\n\t\t(flipMask & FLIP_AD_BIT ? TMX_FLIP_AD : 0)\n\t);\n}\n\n/**\n * Decode a tiled layer's data blob directly into the typed-array layerData\n * @ignore\n */\nfunction setLayerData(layer, bounds, data) {\n\tlet idx = 0;\n\tlet width;\n\tlet height;\n\n\t// layer provide rows and cols, chunk width and height\n\tif (typeof bounds.rows === \"undefined\") {\n\t\twidth = bounds.width;\n\t\theight = bounds.height;\n\t} else {\n\t\twidth = bounds.cols;\n\t\theight = bounds.rows;\n\t}\n\n\tconst cols = layer.cols;\n\tconst layerData = layer.layerData;\n\tconst offsetX = bounds.x;\n\tconst offsetY = bounds.y;\n\t// One-shot warning when a layer's flip-stripped GID won't fit in the\n\t// `Uint16Array` cell — silent truncation would render the wrong tile.\n\t// 65535 is far above any realistic tileset size, but flag it loudly\n\t// so users with degenerate maps notice immediately.\n\tlet overflowedGid = 0;\n\tfor (let y = 0; y < height; y++) {\n\t\tfor (let x = 0; x < width; x++) {\n\t\t\tconst rawGid = data[idx++];\n\t\t\tif (rawGid !== 0) {\n\t\t\t\tconst flatIdx = ((y + offsetY) * cols + (x + offsetX)) * 2;\n\t\t\t\tconst cleanGid = rawGid & TMX_CLEAR_BIT_MASK;\n\t\t\t\tif (cleanGid > 0xffff && overflowedGid === 0) {\n\t\t\t\t\toverflowedGid = cleanGid;\n\t\t\t\t}\n\t\t\t\tlayerData[flatIdx] = cleanGid;\n\t\t\t\tlayerData[flatIdx + 1] = flipMaskFromGid(rawGid);\n\t\t\t}\n\t\t}\n\t}\n\tif (overflowedGid !== 0) {\n\t\tconsole.warn(\n\t\t\t\"melonJS: TMX layer contains GID \" +\n\t\t\t\toverflowedGid +\n\t\t\t\t\" which exceeds the 16-bit cell capacity (max 65535). Tiles will be truncated and render incorrectly.\",\n\t\t);\n\t}\n}\n\n/**\n * a TMX Tile Layer Object\n * Tiled QT 0.7.x format\n * @category Tilemap\n */\nexport default class TMXLayer extends Renderable {\n\t/**\n\t * @param {object} map - layer data in JSON format ({@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#layer})\n\t * @param {object} data - layer data in JSON format ({@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#layer})\n\t * @param {number} tilewidth - width of each tile in pixels\n\t * @param {number} tileheight - height of each tile in pixels\n\t * @param {string} orientation - \"isometric\" or \"orthogonal\"\n\t * @param {TMXTilesetGroup} tilesets - tileset as defined in Tiled\n\t * @param {number} z - z-index position\n\t */\n\tconstructor(map, data, tilewidth, tileheight, orientation, tilesets, z) {\n\t\t// super constructor\n\t\tsuper(0, 0, 0, 0);\n\n\t\t// tile width & height\n\t\tthis.tilewidth = data.tilewidth || tilewidth;\n\t\tthis.tileheight = data.tileheight || tileheight;\n\n\t\t// layer orientation\n\t\tthis.orientation = orientation;\n\n\t\t/**\n\t\t * Horizontal layer offset in tiles\n\t\t * @default 0\n\t\t * @type {number}\n\t\t */\n\t\tthis.x = 0;\n\n\t\t/**\n\t\t * Vertical layer offset in tiles\n\t\t * @default 0\n\t\t * @type {number}\n\t\t */\n\t\tthis.y = 0;\n\n\t\t/**\n\t\t * The Layer corresponding Tilesets\n\t\t * @type {TMXTilesetGroup}\n\t\t */\n\t\tthis.tilesets = tilesets;\n\n\t\t// the default tileset (used as cache in cellAt)\n\t\tthis.tileset = this.tilesets ? this.tilesets.getTilesetByIndex(0) : null;\n\n\t\t// Biggest tile size to draw\n\t\tthis.maxTileSize = {\n\t\t\twidth: 0,\n\t\t\theight: 0,\n\t\t};\n\t\tfor (let i = 0; i < this.tilesets.length; i++) {\n\t\t\tconst tileset = this.tilesets.getTilesetByIndex(i);\n\t\t\tthis.maxTileSize.width = Math.max(\n\t\t\t\tthis.maxTileSize.width,\n\t\t\t\ttileset.tilewidth,\n\t\t\t);\n\t\t\tthis.maxTileSize.height = Math.max(\n\t\t\t\tthis.maxTileSize.height,\n\t\t\t\ttileset.tileheight,\n\t\t\t);\n\t\t}\n\n\t\t/**\n\t\t * All animated tilesets in this layer\n\t\t * @type {TMXTileset[]}\n\t\t */\n\t\tthis.animatedTilesets = [];\n\n\t\t/**\n\t\t * Layer contains tileset animations\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isAnimated = false;\n\n\t\t/**\n\t\t * the order in which tiles on orthogonal tile layers are rendered.\n\t\t * (valid values are \"left-down\", \"left-up\", \"right-down\", \"right-up\")\n\t\t * @type {string}\n\t\t * @default \"right-down\"\n\t\t */\n\t\tthis.renderorder = data.renderorder || \"right-down\";\n\n\t\t/**\n\t\t * the layer class\n\t\t * @type {string}\n\t\t */\n\t\tthis.class = data.class;\n\n\t\t// for displaying order\n\t\tthis.pos.z = z;\n\n\t\t// tiled default coordinates are top-left\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t// additional TMX flags\n\t\tthis.name = data.name;\n\t\tthis.cols = +data.width;\n\t\tthis.rows = +data.height;\n\n\t\t// layer opacity\n\t\tconst visible = typeof data.visible !== \"undefined\" ? +data.visible : 1;\n\t\tthis.setOpacity(visible ? +data.opacity : 0);\n\n\t\t// layer blend mode (Tiled 1.12+)\n\t\tthis.blendMode = TMXUtils.tiledBlendMode(data.mode);\n\n\t\t// layer tint\n\t\tif (typeof data.tintcolor === \"string\") {\n\t\t\t// Tiled provides #RRGGBB or #AARRGGBB\n\t\t\tthis.tint.parseHex(data.tintcolor, true);\n\t\t}\n\n\t\t// layer \"real\" size\n\t\tif (this.orientation === \"isometric\") {\n\t\t\tthis.width = (this.cols + this.rows) * (this.tilewidth / 2);\n\t\t\tthis.height = (this.cols + this.rows) * (this.tileheight / 2);\n\t\t} else {\n\t\t\tthis.width = this.cols * this.tilewidth;\n\t\t\tthis.height = this.rows * this.tileheight;\n\t\t}\n\n\t\t// check if we have any user-defined properties\n\t\tTMXUtils.applyTMXProperties(this, data);\n\n\t\t// set a renderer\n\t\tthis.setRenderer(map.getRenderer());\n\n\t\t/**\n\t\t * The raw tile data for this layer. Each cell occupies two consecutive\n\t\t * `Uint16` slots: the GID (with flip bits stripped) and a 3-bit flip\n\t\t * mask. Cell `(x, y)` is at `layerData[(y * cols + x) * 2]` (row-major).\n\t\t *\n\t\t * The 16-bit GID slot caps per-tileset GIDs at 65 535. This matches the\n\t\t * planned WebGL2 shader path (`RG16UI` index texture) — switching to\n\t\t * `Uint32Array` would force a truncating copy at GPU upload time.\n\t\t * @type {Uint16Array}\n\t\t */\n\t\tthis.layerData = new Uint16Array(this.cols * this.rows * 2);\n\n\t\t/**\n\t\t * Lazy view cache of Tile objects, indexed by `y * cols + x` (row-major).\n\t\t * Allocated lazily on the first `cellAt` / `getTile` call — the renderer\n\t\t * hot path reads `layerData` directly and never touches this cache, so\n\t\t * for games that never call `getTile`/`cellAt` from user code, this\n\t\t * stays `null` for the layer's lifetime. Invalidated entry-by-entry by\n\t\t * `setTile` and `clearTile`. The raw bytes in `layerData` are the source\n\t\t * of truth; this exists only to preserve stable Tile identity across\n\t\t * repeated user-facing reads.\n\t\t * @type {Array<Tile|null>|null}\n\t\t * @ignore\n\t\t */\n\t\tthis.cachedTile = null;\n\n\t\t/**\n\t\t * Monotonically-increasing counter bumped by `setTile` and `clearTile`.\n\t\t * Renderers can compare against a stashed value to detect mutations and\n\t\t * decide whether to re-upload the layer data to the GPU.\n\t\t * @type {number}\n\t\t */\n\t\tthis.dataVersion = 0;\n\n\t\t/**\n\t\t * How this layer is rendered. Resolved by `onActivateEvent` to one of:\n\t\t *   - `\"shader\"`   — WebGL2 procedural shader path (single quad per tileset, fragment GID lookup)\n\t\t *   - `\"prerender\"`— offscreen-canvas bake at activation, blitted as one drawImage per frame\n\t\t *   - `\"perTile\"`  — per-frame loop, one drawImage per visible tile\n\t\t *\n\t\t * User code may set this to one of the above values (or the special\n\t\t * `\"auto\"`) before the layer is activated to override the engine's\n\t\t * default choice; Tiled custom properties named `renderMode` are\n\t\t * applied automatically via `applyTMXProperties`. If a forced mode\n\t\t * is ineligible (e.g. `\"shader\"` on Canvas), a one-shot warning is\n\t\t * emitted at activation and the layer falls back to the legacy path.\n\t\t * @type {string}\n\t\t * @default \"auto\"\n\t\t */\n\t\tthis.renderMode = \"auto\";\n\n\t\tif (map.infinite === 0) {\n\t\t\t// initialize and set the layer data\n\t\t\tsetLayerData(\n\t\t\t\tthis,\n\t\t\t\tthis,\n\t\t\t\tTMXUtils.decode(data.data, data.encoding, data.compression),\n\t\t\t);\n\t\t} else if (map.infinite === 1) {\n\t\t\t// infinite map, initialize per chunk\n\t\t\tconst chunks = data.chunks;\n\t\t\tfor (let i = 0, len = chunks.length; i < len; i++) {\n\t\t\t\tconst chunk = chunks[i];\n\t\t\t\tsetLayerData(\n\t\t\t\t\tthis,\n\t\t\t\t\tchunk,\n\t\t\t\t\tTMXUtils.decode(chunk.data, data.encoding, data.compression),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// called when the layer is added to the game world or a container\n\tonActivateEvent() {\n\t\tif (this.tilesets) {\n\t\t\tconst tileset = this.tilesets.tilesets;\n\t\t\tfor (let i = 0; i < tileset.length; i++) {\n\t\t\t\tif (tileset[i].isAnimated) {\n\t\t\t\t\tthis.animatedTilesets.push(tileset[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.isAnimated = this.animatedTilesets.length > 0;\n\n\t\t// resolve renderMode: shader > prerender > perTile, taking into\n\t\t// account user-forced values, Application/world settings, and the\n\t\t// per-layer preRender hint\n\t\tthis._resolveRenderMode();\n\n\t\t// if pre-rendering method is in use, create an offline canvas/renderer\n\t\tif (this.renderMode === \"prerender\" && !this.canvasRenderer) {\n\t\t\tthis.canvasRenderer = new CanvasRenderer({\n\t\t\t\tcanvas: createCanvas(this.width, this.height),\n\t\t\t\twidth: this.width,\n\t\t\t\theight: this.height,\n\t\t\t\ttransparent: true,\n\t\t\t});\n\t\t\t// pre render the layer on the canvas\n\t\t\tthis.getRenderer().drawTileLayer(this.canvasRenderer, this, this);\n\t\t}\n\t\t// keep `preRender` boolean in sync with the resolved mode (legacy\n\t\t// callers still read it; `Renderer.drawTileLayer` itself reads\n\t\t// `layer.canvasRenderer`)\n\t\tthis.preRender = this.renderMode === \"prerender\";\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Resolve `this.renderMode` to one of \"shader\" / \"prerender\" / \"perTile\"\n\t * based on eligibility checks and user/world hints. Emits a single\n\t * `console.warn` at activation when a forced mode is ineligible, or\n\t * when an auto-eligible mode falls back due to a layer feature the GPU\n\t * path doesn't support (orientation, collection-of-image tileset, etc.).\n\t * @ignore\n\t */\n\t_resolveRenderMode() {\n\t\tconst root = this.ancestor?.getRootAncestor?.();\n\t\tconst renderer = this.parentApp?.renderer;\n\t\tconst gpuAllowed = root?.gpuTilemap !== false;\n\t\tconst preRenderHint =\n\t\t\ttypeof this.preRender === \"boolean\" ? this.preRender : root?.preRender;\n\n\t\tconst elig = this._checkShaderEligibility(renderer, gpuAllowed);\n\n\t\tconst requested = this.renderMode;\n\t\t// explicit \"shader\" — honor if eligible, warn otherwise\n\t\tif (requested === \"shader\") {\n\t\t\tif (elig.ok) {\n\t\t\t\treturn; // already \"shader\"\n\t\t\t}\n\t\t\tconsole.warn(\n\t\t\t\t`melonJS: layer \"${this.name}\" forced renderMode \"shader\" not available (${elig.reason}) — falling back to perTile`,\n\t\t\t);\n\t\t\tthis.renderMode = \"perTile\";\n\t\t\treturn;\n\t\t}\n\t\t// explicit \"prerender\" — honor unless animated (cache would go stale)\n\t\tif (requested === \"prerender\") {\n\t\t\tif (this.isAnimated) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`melonJS: layer \"${this.name}\" forced renderMode \"prerender\" disabled (layer has animated tiles) — falling back to perTile`,\n\t\t\t\t);\n\t\t\t\tthis.renderMode = \"perTile\";\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// explicit \"perTile\" — pass through\n\t\tif (requested === \"perTile\") {\n\t\t\treturn;\n\t\t}\n\t\t// auto-resolve: shader > prerender > perTile\n\t\tif (elig.ok) {\n\t\t\tthis.renderMode = \"shader\";\n\t\t\treturn;\n\t\t}\n\t\t// only emit an info warning when the user enabled gpuTilemap and\n\t\t// the fallback is meaningful. Layer-specific limitations (isometric\n\t\t// orientation, collection-of-image tileset, etc.) get a per-layer\n\t\t// warning because they're actionable per layer. The renderer-wide\n\t\t// \"no WebGL2 context\" case gets a single session-scoped warning\n\t\t// — emitted from the first TMX layer that hits it, so apps with\n\t\t// no tilemap loaded stay quiet.\n\t\tif (gpuAllowed) {\n\t\t\tif (elig.reason === \"no-gpu-renderer\") {\n\t\t\t\tif (!_warnedNoGpuTileSupportOnce) {\n\t\t\t\t\t_warnedNoGpuTileSupportOnce = true;\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"melonJS: gpuTilemap is enabled but the active renderer has no GPU tile-layer support — falling back to the legacy tile renderer for every tile layer\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`melonJS: layer \"${this.name}\" using legacy tile renderer (${elig.reason})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (preRenderHint && !this.isAnimated) {\n\t\t\tthis.renderMode = \"prerender\";\n\t\t\treturn;\n\t\t}\n\t\tthis.renderMode = \"perTile\";\n\t}\n\n\t/**\n\t * Check whether this layer is eligible for the WebGL2 shader path.\n\t * @param {object} renderer\n\t * @param {boolean} gpuAllowed - whether `gpuTilemap` is enabled at the world level\n\t * @returns {{ok: boolean, reason?: string}}\n\t * @ignore\n\t */\n\t_checkShaderEligibility(renderer, gpuAllowed) {\n\t\tif (!gpuAllowed) {\n\t\t\treturn { ok: false, reason: \"gpuTilemap disabled\" };\n\t\t}\n\t\tif (!renderer || renderer.supportsShaderTileLayers !== true) {\n\t\t\treturn { ok: false, reason: \"no-gpu-renderer\" };\n\t\t}\n\t\tif (this.orientation !== \"orthogonal\") {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: `no gpu renderer supported yet for \"${this.orientation}\" orientation`,\n\t\t\t};\n\t\t}\n\t\tif (!this.tilesets || this.tilesets.tilesets.length === 0) {\n\t\t\treturn { ok: false, reason: \"no tilesets\" };\n\t\t}\n\t\t// the shader iterates a fixed-size loop over candidate cells to\n\t\t// support oversized tiles (tile dim > cell dim) drawn bottom-aligned.\n\t\t// Loop bound is MAX_OVERFLOW + 1; anything beyond would silently clip,\n\t\t// so refuse the shader path for layers with extreme oversampling.\n\t\tconst MAX_OVERFLOW_CELLS = 4;\n\t\tfor (const ts of this.tilesets.tilesets) {\n\t\t\tif (ts.isCollection) {\n\t\t\t\treturn { ok: false, reason: \"collection-of-image tileset\" };\n\t\t\t}\n\t\t\tif (ts.tilerendersize !== \"tile\") {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: `tilerendersize \"${ts.tilerendersize}\" not supported`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ts.tileoffset.x !== 0 || ts.tileoffset.y !== 0) {\n\t\t\t\treturn { ok: false, reason: \"non-zero tileoffset\" };\n\t\t\t}\n\t\t\tconst overflowX = Math.ceil(ts.tilewidth / this.tilewidth) - 1;\n\t\t\tconst overflowY = Math.ceil(ts.tileheight / this.tileheight) - 1;\n\t\t\tif (overflowX > MAX_OVERFLOW_CELLS || overflowY > MAX_OVERFLOW_CELLS) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: `tile overflow exceeds shader limit (${MAX_OVERFLOW_CELLS} cells)`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn { ok: true };\n\t}\n\n\t// called when the layer is removed from the game world or a container\n\tonDeactivateEvent() {\n\t\t// renderer-side caches keyed by this layer (e.g. the WebGL2 shader\n\t\t// path's per-layer GID index texture) are cleared from the renderer's\n\t\t// own `reset()` path — tile layers only come and go on game reset.\n\t\tthis.animatedTilesets = undefined;\n\t\t// keep canvasRenderer for reuse — dropping the reference would leak\n\t\t// event listeners registered by CanvasRenderer's constructor\n\t}\n\n\t/**\n\t * Set the TMX renderer for this layer object\n\t * @param {TMXRenderer} renderer\n\t * @example\n\t * // use the parent map default renderer\n\t * let layer = new me.TMXLayer(...);\n\t * layer.setRenderer(map.getRenderer());\n\t */\n\tsetRenderer(renderer) {\n\t\tthis.renderer = renderer;\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Return the layer current renderer object\n\t * @returns {TMXRenderer} renderer\n\t */\n\tgetRenderer() {\n\t\treturn this.renderer;\n\t}\n\n\t/**\n\t * Return the TileId of the Tile at the specified position\n\t * @param {number} x - X coordinate (in world/pixels coordinates)\n\t * @param {number} y - Y coordinate (in world/pixels coordinates)\n\t * @returns {number} TileId or null if there is no Tile at the given position\n\t */\n\tgetTileId(x, y) {\n\t\tconst tile = this.getTile(x, y);\n\t\treturn tile ? tile.tileId : null;\n\t}\n\n\t/**\n\t * Return the Tile object at the specified position\n\t * @param {number} x - X coordinate (in world/pixels coordinates)\n\t * @param {number} y - Y coordinate (in world/pixels coordinates)\n\t * @returns {Tile} corresponding tile or null if there is no defined tile at the coordinate or if outside of the layer bounds\n\t * @example\n\t * // get the TMX Map Layer called \"Front layer\"\n\t * let layer = app.world.getChildByName(\"Front Layer\")[0];\n\t * // get the tile object corresponding to the latest pointer position\n\t * let tile = layer.getTile(me.input.pointer.x, me.input.pointer.y);\n\t */\n\tgetTile(x, y) {\n\t\tlet tile = null;\n\n\t\tif (this.contains(x, y)) {\n\t\t\tconst coord = this.getRenderer().pixelToTileCoords(\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tvector2dPool.get(),\n\t\t\t);\n\t\t\ttile = this.cellAt(coord.x, coord.y);\n\t\t\tvector2dPool.release(coord);\n\t\t}\n\t\treturn tile;\n\t}\n\n\t/**\n\t * assign the given Tile object to the specified position\n\t * @param {Tile} tile - the tile object to be assigned\n\t * @param {number} x - x coordinate (in tile/column coordinates)\n\t * @param {number} y - y coordinate (in tile/row coordinates)\n\t * @returns {Tile} the tile object\n\t */\n\tsetTile(tile, x, y) {\n\t\tif (x < 0 || x >= this.cols || y < 0 || y >= this.rows) {\n\t\t\treturn tile;\n\t\t}\n\t\tconst slot = y * this.cols + x;\n\t\tconst idx = slot * 2;\n\t\tconst cleanGid = tile.tileId & TMX_CLEAR_BIT_MASK;\n\t\t// `layerData` is a Uint16Array; writes silently truncate above\n\t\t// 0xFFFF. Warn once per layer so a runtime `setTile` with a\n\t\t// GID >= 65536 doesn't corrupt the cell undetected.\n\t\tif (cleanGid > 0xffff && !this._truncationWarned) {\n\t\t\tthis._truncationWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: setTile received GID \" +\n\t\t\t\t\tcleanGid +\n\t\t\t\t\t\" which exceeds the 16-bit cell capacity (max 65535). Tile will be truncated and render incorrectly.\",\n\t\t\t);\n\t\t}\n\t\tthis.layerData[idx] = cleanGid;\n\t\tthis.layerData[idx + 1] = flipMaskFromTile(tile);\n\t\tif (this.cachedTile !== null) {\n\t\t\tthis.cachedTile[slot] = tile;\n\t\t}\n\t\tthis.dataVersion++;\n\t\tthis.isDirty = true;\n\t\treturn tile;\n\t}\n\n\t/**\n\t * return a new the Tile object corresponding to the given tile id\n\t * @param {number} tileId - tileId\n\t * @param {number} x - X coordinate (in world/pixels coordinates)\n\t * @param {number} y - Y coordinate (in world/pixels coordinates)\n\t * @returns {Tile} the tile object\n\t */\n\tgetTileById(tileId, x, y) {\n\t\tif (!this.tileset.contains(tileId)) {\n\t\t\t// look for the corresponding tileset\n\t\t\tthis.tileset = this.tilesets.getTilesetByGid(tileId);\n\t\t}\n\t\treturn new Tile(x, y, tileId, this.tileset);\n\t}\n\n\t/**\n\t * Return the Tile object at the specified tile coordinates\n\t * @param {number} x - x position of the tile (in Tile unit)\n\t * @param {number} y - x position of the tile (in Tile unit)\n\t * @param {number} [boundsCheck=true] - check first if within the layer bounds\n\t * @returns {Tile} corresponding tile or null if there is no defined tile at the position or if outside of the layer bounds\n\t * @example\n\t * // return the first tile at offset 0, 0\n\t * let tile = layer.cellAt(0, 0);\n\t */\n\tcellAt(x, y, boundsCheck) {\n\t\tconst _x = ~~x;\n\t\tconst _y = ~~y;\n\n\t\t// boundsCheck only used internally by the tiled renderer, when the layer bound check was already done\n\t\tif (\n\t\t\tboundsCheck !== false &&\n\t\t\t(_x < 0 || _x >= this.cols || _y < 0 || _y >= this.rows)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst slot = _y * this.cols + _x;\n\t\tconst idx = slot * 2;\n\t\tconst gid = this.layerData[idx];\n\t\t// `cellAt(x, y, false)` skips the explicit bounds check on the\n\t\t// coords for speed, but out-of-range reads from a typed array\n\t\t// return `undefined` — treat both that and an explicit empty\n\t\t// cell (gid 0) as \"no tile\" so we never push a bogus GID into\n\t\t// the tileset lookup path\n\t\tif (!gid) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// lazy-allocate the view cache on first user-facing query — the\n\t\t// renderer hot loop bypasses this method and reads layerData directly,\n\t\t// so games that never call cellAt/getTile keep cachedTile null forever\n\t\tif (this.cachedTile === null) {\n\t\t\tthis.cachedTile = new Array(this.cols * this.rows).fill(null);\n\t\t} else {\n\t\t\tconst cached = this.cachedTile[slot];\n\t\t\tif (cached !== null) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\n\t\tconst flipMask = this.layerData[idx + 1];\n\t\tconst tile = this.getTileById(gidWithFlips(gid, flipMask), _x, _y);\n\t\tthis.cachedTile[slot] = tile;\n\t\treturn tile;\n\t}\n\n\t/**\n\t * clear the tile at the specified position\n\t * @param {number} x - X coordinate (in map coordinates: row/column)\n\t * @param {number} y - Y coordinate (in map coordinates: row/column)\n\t * @example\n\t * app.world.getChildByType(me.TMXLayer).forEach(function(layer) {\n\t *     // clear all tiles at the given x,y coordinates\n\t *     layer.clearTile(x, y);\n\t * });\n\t */\n\tclearTile(x, y) {\n\t\tif (x < 0 || x >= this.cols || y < 0 || y >= this.rows) {\n\t\t\treturn;\n\t\t}\n\t\t// clearing tile\n\t\tconst slot = y * this.cols + x;\n\t\tconst idx = slot * 2;\n\t\tthis.layerData[idx] = 0;\n\t\tthis.layerData[idx + 1] = 0;\n\t\tif (this.cachedTile !== null) {\n\t\t\tthis.cachedTile[slot] = null;\n\t\t}\n\t\t// erase the corresponding area in the canvas\n\t\tif (this.preRender) {\n\t\t\tthis.canvasRenderer.clearRect(\n\t\t\t\tx * this.tilewidth,\n\t\t\t\ty * this.tileheight,\n\t\t\t\tthis.tilewidth,\n\t\t\t\tthis.tileheight,\n\t\t\t);\n\t\t}\n\t\tthis.dataVersion++;\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * update animations in a tileset layer\n\t * @ignore\n\t */\n\tupdate(dt) {\n\t\tlet result = this.isDirty;\n\t\tif (this.isAnimated) {\n\t\t\tfor (let i = 0; i < this.animatedTilesets.length; i++) {\n\t\t\t\tresult = this.animatedTilesets[i].update(dt) || result;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * draw a tileset layer\n\t * @ignore\n\t */\n\tdraw(renderer, rect) {\n\t\t// dispatch to the active renderer — picks shader / preRender / perTile\n\t\t// based on `this.renderMode` and the renderer's capabilities\n\t\trenderer.drawTileLayer(this, rect);\n\t}\n}\n","import { Bounds, boundsPool } from \"./../../../physics/bounds.ts\";\nimport TMXLayer from \"./../TMXLayer.js\";\n\n/**\n * The map renderer base class\n * @category Tilemap\n */\nexport default class TMXRenderer {\n\t/**\n\t * @param {number} cols - width of the tilemap in tiles\n\t * @param {number} rows - height of the tilemap in tiles\n\t * @param {number} tilewidth - width of each tile in pixels\n\t * @param {number} tileheight - height of each tile in pixels\n\t */\n\tconstructor(cols, rows, tilewidth, tileheight) {\n\t\tthis.cols = cols;\n\t\tthis.rows = rows;\n\t\tthis.tilewidth = tilewidth;\n\t\tthis.tileheight = tileheight;\n\t\tthis.bounds = new Bounds();\n\t}\n\n\t/**\n\t * return true if the renderer can render the specified map or layer\n\t * @param {TMXTileMap|TMXLayer} component - TMX Map or Layer\n\t * @returns {boolean}\n\t */\n\tcanRender(component) {\n\t\treturn (\n\t\t\t/*\n            // layers can have different size within\n            // the same maps, so commenting these two lines\n            (this.cols === component.cols) &&\n            (this.rows === component.rows) &&\n            */\n\t\t\tthis.tilewidth === component.tilewidth &&\n\t\t\tthis.tileheight === component.tileheight\n\t\t);\n\t}\n\n\t/**\n\t * return the bounding rect for this map renderer\n\t * @param {TMXLayer} [layer] - calculate the bounding rect for a specific layer (will return a new bounds object)\n\t * @returns {Bounds}\n\t */\n\tgetBounds(layer) {\n\t\tconst bounds = layer instanceof TMXLayer ? boundsPool.get() : this.bounds;\n\t\tbounds.setMinMax(\n\t\t\t0,\n\t\t\t0,\n\t\t\tthis.cols * this.tilewidth,\n\t\t\tthis.rows * this.tileheight,\n\t\t);\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @param {number} x - X coordinate\n\t * @param {number} y - Y coordinate\n\t * @param {Vector2d} [v] - an optional vector object where to put the return values\n\t * @returns {Vector2d}\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\treturn v;\n\t}\n\n\t/**\n\t * return the pixel position corresponding of the specified tile\n\t * @param {number} col - tile horizontal position\n\t * @param {number} row - tile vertical position\n\t * @param {Vector2d} [v] - an optional vector object where to put the return values\n\t * @returns {Vector2d}\n\t */\n\ttileToPixelCoords(col, row, v) {\n\t\treturn v;\n\t}\n\n\t/**\n\t * fix the position of Objects to match\n\t * the way Tiled places them\n\t * @param {object} obj - a TMX Object with gid, x, y, height properties\n\t */\n\tadjustPosition(obj) {\n\t\t// only adjust position if obj.gid is defined\n\t\tif (typeof obj.gid === \"number\") {\n\t\t\t// Tiled objects origin point is \"bottom-left\" in Tiled,\n\t\t\t// \"top-left\" in melonJS\n\t\t\tobj.y -= obj.height;\n\t\t}\n\t}\n\n\t/**\n\t * draw the given tile at the specified layer\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer object\n\t * @param {number} x - X coordinate where to draw the tile\n\t * @param {number} y - Y coordinate where to draw the tile\n\t * @param {Tile} tile - the tile object to draw\n\t */\n\tdrawTile() {}\n\n\t/**\n\t * draw the given TMX Layer for the given area\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer object\n\t * @param {TMXLayer} layer - a TMX Layer object\n\t * @param {Rect} rect - the area of the layer to draw\n\t */\n\tdrawTileLayer() {}\n}\n","import { Vector2d, vector2dPool } from \"../../../math/vector2d.ts\";\nimport { boundsPool } from \"../../../physics/bounds.ts\";\nimport TMXLayer from \"./../TMXLayer.js\";\nimport TMXRenderer from \"./TMXRenderer.js\";\n\n// scope global variables & constants\nconst offsetsStaggerX = [\n\t{ x: 0, y: 0 },\n\t{ x: +1, y: -1 },\n\t{ x: +1, y: 0 },\n\t{ x: +2, y: 0 },\n];\nconst offsetsStaggerY = [\n\t{ x: 0, y: 0 },\n\t{ x: -1, y: +1 },\n\t{ x: 0, y: +1 },\n\t{ x: 0, y: +2 },\n];\n\n/**\n * an Hexagonal Map Renderer\n * @category Tilemap\n */\nexport default class TMXHexagonalRenderer extends TMXRenderer {\n\t/**\n\t * @param {TMXTileMap} map - the TMX map\n\t */\n\tconstructor(map) {\n\t\tsuper(map.cols, map.rows, map.tilewidth & ~1, map.tileheight & ~1);\n\n\t\tthis.hexsidelength = map.hexsidelength || 0;\n\t\tthis.staggerX = map.staggeraxis === \"x\";\n\t\tthis.staggerEven = map.staggerindex === \"even\";\n\n\t\tthis.sidelengthx = 0;\n\t\tthis.sidelengthy = 0;\n\n\t\tif (map.orientation === \"hexagonal\") {\n\t\t\tif (this.staggerX) {\n\t\t\t\tthis.sidelengthx = this.hexsidelength;\n\t\t\t} else {\n\t\t\t\tthis.sidelengthy = this.hexsidelength;\n\t\t\t}\n\t\t}\n\n\t\tthis.sideoffsetx = (this.tilewidth - this.sidelengthx) / 2;\n\t\tthis.sideoffsety = (this.tileheight - this.sidelengthy) / 2;\n\n\t\tthis.columnwidth = this.sideoffsetx + this.sidelengthx;\n\t\tthis.rowheight = this.sideoffsety + this.sidelengthy;\n\n\t\tthis.centers = [\n\t\t\tnew Vector2d(),\n\t\t\tnew Vector2d(),\n\t\t\tnew Vector2d(),\n\t\t\tnew Vector2d(),\n\t\t];\n\t}\n\n\t/**\n\t * return true if the renderer can render the specified layer\n\t * @ignore\n\t */\n\tcanRender(layer) {\n\t\treturn layer.orientation === \"hexagonal\" && super.canRender(layer);\n\t}\n\n\t/**\n\t * return the bounding rect for this map renderer\n\t * @ignore\n\t */\n\tgetBounds(layer) {\n\t\tconst bounds = layer instanceof TMXLayer ? boundsPool.get() : this.bounds;\n\n\t\t// The map size is the same regardless of which indexes are shifted.\n\t\tif (this.staggerX) {\n\t\t\tbounds.setMinMax(\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tthis.cols * this.columnwidth + this.sideoffsetx,\n\t\t\t\tthis.rows * (this.tileheight + this.sidelengthy),\n\t\t\t);\n\t\t\tif (bounds.width > 1) {\n\t\t\t\tbounds.height += this.rowheight;\n\t\t\t}\n\t\t} else {\n\t\t\tbounds.setMinMax(\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tthis.cols * (this.tilewidth + this.sidelengthx),\n\t\t\t\tthis.rows * this.rowheight + this.sideoffsety,\n\t\t\t);\n\t\t\tif (bounds.height > 1) {\n\t\t\t\tbounds.width += this.columnwidth;\n\t\t\t}\n\t\t}\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tdoStaggerX(x) {\n\t\treturn this.staggerX && (x & 1) ^ this.staggerEven;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tdoStaggerY(y) {\n\t\treturn !this.staggerX && (y & 1) ^ this.staggerEven;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\ttopLeft(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (!this.staggerX) {\n\t\t\tif ((y & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x, y - 1);\n\t\t\t} else {\n\t\t\t\tret.set(x - 1, y - 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif ((x & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x - 1, y);\n\t\t\t} else {\n\t\t\t\tret.set(x - 1, y - 1);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\ttopRight(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (!this.staggerX) {\n\t\t\tif ((y & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x + 1, y - 1);\n\t\t\t} else {\n\t\t\t\tret.set(x, y - 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif ((x & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x + 1, y);\n\t\t\t} else {\n\t\t\t\tret.set(x + 1, y - 1);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tbottomLeft(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (!this.staggerX) {\n\t\t\tif ((y & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x, y + 1);\n\t\t\t} else {\n\t\t\t\tret.set(x - 1, y + 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif ((x & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x - 1, y + 1);\n\t\t\t} else {\n\t\t\t\tret.set(x - 1, y);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tbottomRight(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (!this.staggerX) {\n\t\t\tif ((y & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x + 1, y + 1);\n\t\t\t} else {\n\t\t\t\tret.set(x, y + 1);\n\t\t\t}\n\t\t} else {\n\t\t\tif ((x & 1) ^ this.staggerEven) {\n\t\t\t\tret.set(x + 1, y + 1);\n\t\t\t} else {\n\t\t\t\tret.set(x + 1, y);\n\t\t\t}\n\t\t}\n\t\treturn ret;\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @ignore\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (this.staggerX) {\n\t\t\t//flat top\n\t\t\tx -= this.staggerEven ? this.tilewidth : this.sideoffsetx;\n\t\t} else {\n\t\t\t//pointy top\n\t\t\ty -= this.staggerEven ? this.tileheight : this.sideoffsety;\n\t\t}\n\n\t\t// Start with the coordinates of a grid-aligned tile\n\t\tconst referencePoint = vector2dPool.get(\n\t\t\tMath.floor(x / (this.columnwidth * 2)),\n\t\t\tMath.floor(y / (this.rowheight * 2)),\n\t\t);\n\n\t\t// Relative x and y position on the base square of the grid-aligned tile\n\t\tconst rel = vector2dPool.get(\n\t\t\tx - referencePoint.x * (this.columnwidth * 2),\n\t\t\ty - referencePoint.y * (this.rowheight * 2),\n\t\t);\n\n\t\t// Adjust the reference point to the correct tile coordinates\n\t\tif (this.staggerX) {\n\t\t\treferencePoint.x = referencePoint.x * 2;\n\t\t\tif (this.staggerEven) {\n\t\t\t\t++referencePoint.x;\n\t\t\t}\n\t\t} else {\n\t\t\treferencePoint.y = referencePoint.y * 2;\n\t\t\tif (this.staggerEven) {\n\t\t\t\t++referencePoint.y;\n\t\t\t}\n\t\t}\n\n\t\t// Determine the nearest hexagon tile by the distance to the center\n\t\tlet left;\n\t\tlet top;\n\t\tlet centerX;\n\t\tlet centerY;\n\t\tif (this.staggerX) {\n\t\t\tleft = this.sidelengthx / 2;\n\t\t\tcenterX = left + this.columnwidth;\n\t\t\tcenterY = this.tileheight / 2;\n\n\t\t\tthis.centers[0].set(left, centerY);\n\t\t\tthis.centers[1].set(centerX, centerY - this.rowheight);\n\t\t\tthis.centers[2].set(centerX, centerY + this.rowheight);\n\t\t\tthis.centers[3].set(centerX + this.columnwidth, centerY);\n\t\t} else {\n\t\t\ttop = this.sidelengthy / 2;\n\t\t\tcenterX = this.tilewidth / 2;\n\t\t\tcenterY = top + this.rowheight;\n\n\t\t\tthis.centers[0].set(centerX, top);\n\t\t\tthis.centers[1].set(centerX - this.columnwidth, centerY);\n\t\t\tthis.centers[2].set(centerX + this.columnwidth, centerY);\n\t\t\tthis.centers[3].set(centerX, centerY + this.rowheight);\n\t\t}\n\n\t\tlet nearest = 0;\n\t\tlet minDist = Number.MAX_VALUE;\n\t\tfor (let i = 0; i < 4; ++i) {\n\t\t\t// calculate squared distance without mutating this.centers[i]\n\t\t\tconst dx = this.centers[i].x - rel.x;\n\t\t\tconst dy = this.centers[i].y - rel.y;\n\t\t\tconst dc = dx * dx + dy * dy;\n\t\t\tif (dc < minDist) {\n\t\t\t\tminDist = dc;\n\t\t\t\tnearest = i;\n\t\t\t}\n\t\t}\n\n\t\tconst offsets = this.staggerX ? offsetsStaggerX : offsetsStaggerY;\n\n\t\tret.set(\n\t\t\treferencePoint.x + offsets[nearest].x,\n\t\t\treferencePoint.y + offsets[nearest].y,\n\t\t);\n\n\t\tvector2dPool.release(referencePoint);\n\t\tvector2dPool.release(rel);\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * return the pixel position corresponding of the specified tile\n\t * @ignore\n\t */\n\ttileToPixelCoords(x, y, v) {\n\t\tconst tileX = Math.floor(x);\n\t\tconst tileY = Math.floor(y);\n\t\tconst ret = v || vector2dPool.get();\n\n\t\tif (this.staggerX) {\n\t\t\tret.y = tileY * (this.tileheight + this.sidelengthy);\n\t\t\tif (this.doStaggerX(tileX)) {\n\t\t\t\tret.y += this.rowheight;\n\t\t\t}\n\t\t\tret.x = tileX * this.columnwidth;\n\t\t} else {\n\t\t\tret.x = tileX * (this.tilewidth + this.sidelengthx);\n\t\t\tif (this.doStaggerY(tileY)) {\n\t\t\t\tret.x += this.columnwidth;\n\t\t\t}\n\t\t\tret.y = tileY * this.rowheight;\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * draw the tile map (legacy entry point — accepts a fully-constructed Tile)\n\t * @ignore\n\t */\n\tdrawTile(renderer, x, y, tmxTile) {\n\t\tconst tileset = tmxTile.tileset;\n\t\tconst point = this.tileToPixelCoords(x, y, vector2dPool.get());\n\n\t\t// draw the tile\n\t\ttileset.drawTile(\n\t\t\trenderer,\n\t\t\ttileset.tileoffset.x + point.x,\n\t\t\ttileset.tileoffset.y + point.y + (this.tileheight - tileset.tileheight),\n\t\t\ttmxTile,\n\t\t);\n\n\t\tvector2dPool.release(point);\n\t}\n\n\t/**\n\t * draw a tile from raw (gid, flipMask, tileset) data — used by the hot\n\t * rendering loop to bypass Tile construction\n\t * @ignore\n\t */\n\tdrawTileRaw(renderer, x, y, gid, flipMask, tileset) {\n\t\tconst point = this.tileToPixelCoords(x, y, vector2dPool.get());\n\n\t\ttileset.drawTileRaw(\n\t\t\trenderer,\n\t\t\ttileset.tileoffset.x + point.x,\n\t\t\ttileset.tileoffset.y + point.y + (this.tileheight - tileset.tileheight),\n\t\t\tgid,\n\t\t\tflipMask,\n\t\t);\n\n\t\tvector2dPool.release(point);\n\t}\n\n\t/**\n\t * draw the tile map\n\t * @ignore\n\t */\n\tdrawTileLayer(renderer, layer, rect) {\n\t\t// get top-left and bottom-right tile position\n\t\tconst startTile = this.pixelToTileCoords(\n\t\t\trect.pos.x,\n\t\t\trect.pos.y,\n\t\t\tvector2dPool.get(),\n\t\t);\n\n\t\t// Compensate for the layer position\n\t\tstartTile.sub(layer.pos);\n\n\t\t// get top-left and bottom-right tile position\n\t\tlet startPos = this.tileToPixelCoords(\n\t\t\tstartTile.x + layer.pos.x,\n\t\t\tstartTile.y + layer.pos.y,\n\t\t\tvector2dPool.get(),\n\t\t);\n\n\t\tconst rowTile = vector2dPool.get().setV(startTile);\n\t\tconst rowPos = vector2dPool.get().setV(startPos);\n\n\t\t/* Determine in which half of the tile the top-left corner of the area we\n\t\t * need to draw is. If we're in the upper half, we need to start one row\n\t\t * up due to those tiles being visible as well. How we go up one row\n\t\t * depends on whether we're in the left or right half of the tile.\n\t\t */\n\t\tconst inUpperHalf = rect.pos.y - startPos.y < this.sideoffsety;\n\t\tconst inLeftHalf = rect.pos.x - startPos.x < this.sideoffsetx;\n\n\t\tif (inUpperHalf) {\n\t\t\tstartTile.y--;\n\t\t}\n\t\tif (inLeftHalf) {\n\t\t\tstartTile.x--;\n\t\t}\n\n\t\tconst endX = layer.cols;\n\t\tconst endY = layer.rows;\n\n\t\t// shared hot-loop state for both stagger branches — read (gid, flipMask)\n\t\t// straight from the typed array and resolve the tileset with a\n\t\t// short-circuit cache.\n\t\t// NOTE: the hex hot loop bypasses `this.drawTileRaw` and calls\n\t\t// `tilesetCache.drawTileRaw` directly because it maintains pre-computed\n\t\t// pixel coords (`rowPos.x`, `rowPos.y`) incrementally across the\n\t\t// staggered iteration; going through drawTileRaw would re-derive them\n\t\t// from layer-cell coords via tileToPixelCoords and lose the gain.\n\t\tconst layerCols = layer.cols;\n\t\tconst data = layer.layerData;\n\t\tconst tilesets = layer.tilesets;\n\t\tlet tilesetCache = layer.tileset;\n\t\tif (tilesetCache === null) {\n\t\t\tvector2dPool.release(startTile);\n\t\t\tvector2dPool.release(startPos);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.staggerX) {\n\t\t\t//ensure we are in the valid tile range\n\t\t\tstartTile.x = Math.max(0, startTile.x);\n\t\t\tstartTile.y = Math.max(0, startTile.y);\n\n\t\t\tstartPos = this.tileToPixelCoords(\n\t\t\t\tstartTile.x + layer.pos.x,\n\t\t\t\tstartTile.y + layer.pos.y,\n\t\t\t\tstartPos,\n\t\t\t);\n\n\t\t\tlet staggeredRow = this.doStaggerX(startTile.x + layer.pos.x);\n\n\t\t\t// main drawing loop\n\t\t\tfor (; startPos.y < rect.bottom && startTile.y < endY; ) {\n\t\t\t\trowTile.setV(startTile);\n\t\t\t\trowPos.setV(startPos);\n\n\t\t\t\tfor (; rowPos.x < rect.right && rowTile.x < endX; rowTile.x += 2) {\n\t\t\t\t\tif (rowTile.x >= 0 && rowTile.y >= 0 && rowTile.y < layer.rows) {\n\t\t\t\t\t\tconst idx = (rowTile.y * layerCols + rowTile.x) * 2;\n\t\t\t\t\t\tconst gid = data[idx];\n\t\t\t\t\t\tif (gid) {\n\t\t\t\t\t\t\tconst flipMask = data[idx + 1];\n\t\t\t\t\t\t\tif (!tilesetCache.contains(gid)) {\n\t\t\t\t\t\t\t\ttilesetCache = tilesets.getTilesetByGid(gid);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttilesetCache.drawTileRaw(\n\t\t\t\t\t\t\t\trenderer,\n\t\t\t\t\t\t\t\trowPos.x,\n\t\t\t\t\t\t\t\trowPos.y,\n\t\t\t\t\t\t\t\tgid,\n\t\t\t\t\t\t\t\tflipMask,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trowPos.x += this.tilewidth + this.sidelengthx;\n\t\t\t\t}\n\n\t\t\t\tif (staggeredRow) {\n\t\t\t\t\tstartTile.x -= 1;\n\t\t\t\t\tstartTile.y += 1;\n\t\t\t\t\tstartPos.x -= this.columnwidth;\n\t\t\t\t\tstaggeredRow = false;\n\t\t\t\t} else {\n\t\t\t\t\tstartTile.x += 1;\n\t\t\t\t\tstartPos.x += this.columnwidth;\n\t\t\t\t\tstaggeredRow = true;\n\t\t\t\t}\n\n\t\t\t\tstartPos.y += this.rowheight;\n\t\t\t}\n\t\t\tvector2dPool.release(rowTile);\n\t\t\tvector2dPool.release(rowPos);\n\t\t} else {\n\t\t\t//ensure we are in the valid tile range\n\t\t\tstartTile.x = Math.max(0, startTile.x);\n\t\t\tstartTile.y = Math.max(0, startTile.y);\n\n\t\t\tstartPos = this.tileToPixelCoords(\n\t\t\t\tstartTile.x + layer.pos.x,\n\t\t\t\tstartTile.y + layer.pos.y,\n\t\t\t\tstartPos,\n\t\t\t);\n\n\t\t\t// Odd row shifting is applied in the rendering loop, so un-apply it here\n\t\t\tif (this.doStaggerY(startTile.y)) {\n\t\t\t\tstartPos.x -= this.columnwidth;\n\t\t\t}\n\n\t\t\t// main drawing loop\n\t\t\tfor (; startPos.y < rect.bottom && startTile.y < endY; startTile.y++) {\n\t\t\t\trowTile.setV(startTile);\n\t\t\t\trowPos.setV(startPos);\n\n\t\t\t\tif (this.doStaggerY(startTile.y)) {\n\t\t\t\t\trowPos.x += this.columnwidth;\n\t\t\t\t}\n\n\t\t\t\tfor (; rowPos.x < rect.right && rowTile.x < endX; rowTile.x++) {\n\t\t\t\t\tif (rowTile.x >= 0 && rowTile.y >= 0 && rowTile.y < layer.rows) {\n\t\t\t\t\t\tconst idx = (rowTile.y * layerCols + rowTile.x) * 2;\n\t\t\t\t\t\tconst gid = data[idx];\n\t\t\t\t\t\tif (gid) {\n\t\t\t\t\t\t\tconst flipMask = data[idx + 1];\n\t\t\t\t\t\t\tif (!tilesetCache.contains(gid)) {\n\t\t\t\t\t\t\t\ttilesetCache = tilesets.getTilesetByGid(gid);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttilesetCache.drawTileRaw(\n\t\t\t\t\t\t\t\trenderer,\n\t\t\t\t\t\t\t\trowPos.x,\n\t\t\t\t\t\t\t\trowPos.y,\n\t\t\t\t\t\t\t\tgid,\n\t\t\t\t\t\t\t\tflipMask,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\trowPos.x += this.tilewidth + this.sidelengthx;\n\t\t\t\t}\n\t\t\t\tstartPos.y += this.rowheight;\n\t\t\t}\n\t\t\tvector2dPool.release(rowTile);\n\t\t\tvector2dPool.release(rowPos);\n\t\t}\n\n\t\tvector2dPool.release(startTile);\n\t\tvector2dPool.release(startPos);\n\t}\n}\n","import { vector2dPool } from \"../../../math/vector2d.ts\";\nimport { boundsPool } from \"../../../physics/bounds.ts\";\nimport TMXLayer from \"./../TMXLayer.js\";\nimport TMXRenderer from \"./TMXRenderer.js\";\n\n/**\n * an Isometric Map Renderer\n * @category Tilemap\n */\nexport default class TMXIsometricRenderer extends TMXRenderer {\n\t/**\n\t * @param {TMXTileMap} map - the TMX map\n\t */\n\tconstructor(map) {\n\t\tsuper(map.cols, map.rows, map.tilewidth, map.tileheight);\n\n\t\tthis.hTilewidth = this.tilewidth / 2;\n\t\tthis.hTileheight = this.tileheight / 2;\n\t\tthis.originX = this.rows * this.hTilewidth;\n\t}\n\n\t/**\n\t * return true if the renderer can render the specified layer\n\t * @ignore\n\t */\n\tcanRender(layer) {\n\t\treturn layer.orientation === \"isometric\" && super.canRender(layer);\n\t}\n\n\t/**\n\t * return the bounding rect for this map renderer\n\t * @ignore\n\t */\n\tgetBounds(layer) {\n\t\tconst bounds = layer instanceof TMXLayer ? boundsPool.get() : this.bounds;\n\t\tbounds.setMinMax(\n\t\t\t0,\n\t\t\t0,\n\t\t\t(this.cols + this.rows) * (this.tilewidth / 2),\n\t\t\t(this.cols + this.rows) * (this.tileheight / 2),\n\t\t);\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @ignore\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\treturn ret.set(\n\t\t\ty / this.tileheight + (x - this.originX) / this.tilewidth,\n\t\t\ty / this.tileheight - (x - this.originX) / this.tilewidth,\n\t\t);\n\t}\n\n\t/**\n\t * return the pixel position corresponding of the specified tile\n\t * @ignore\n\t */\n\ttileToPixelCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\treturn ret.set(\n\t\t\t(x - y) * this.hTilewidth + this.originX,\n\t\t\t(x + y) * this.hTileheight,\n\t\t);\n\t}\n\n\t/**\n\t * fix the position of Objects to match\n\t * the way Tiled places them\n\t * @ignore\n\t */\n\tadjustPosition(obj) {\n\t\tconst tileX = obj.x / this.hTilewidth;\n\t\tconst tileY = obj.y / this.tileheight;\n\t\tconst isoPos = vector2dPool.get();\n\n\t\tthis.tileToPixelCoords(tileX, tileY, isoPos);\n\n\t\tobj.x = isoPos.x;\n\t\tobj.y = isoPos.y;\n\n\t\tvector2dPool.release(isoPos);\n\t}\n\n\t/**\n\t * draw the tile map (legacy entry point — accepts a fully-constructed Tile)\n\t * @ignore\n\t */\n\tdrawTile(renderer, x, y, tmxTile) {\n\t\tconst tileset = tmxTile.tileset;\n\t\t// draw the tile\n\t\ttileset.drawTile(\n\t\t\trenderer,\n\t\t\t((this.cols - 1) * tileset.tilewidth + (x - y) * tileset.tilewidth) >> 1,\n\t\t\t(-tileset.tilewidth + (x + y) * tileset.tileheight) >> 2,\n\t\t\ttmxTile,\n\t\t);\n\t}\n\n\t/**\n\t * draw a tile from raw (gid, flipMask, tileset) data — used by the hot\n\t * rendering loop to bypass Tile construction\n\t * @ignore\n\t */\n\tdrawTileRaw(renderer, x, y, gid, flipMask, tileset) {\n\t\ttileset.drawTileRaw(\n\t\t\trenderer,\n\t\t\t((this.cols - 1) * tileset.tilewidth + (x - y) * tileset.tilewidth) >> 1,\n\t\t\t(-tileset.tilewidth + (x + y) * tileset.tileheight) >> 2,\n\t\t\tgid,\n\t\t\tflipMask,\n\t\t);\n\t}\n\n\t/**\n\t * draw the tile map\n\t * @ignore\n\t */\n\tdrawTileLayer(renderer, layer, rect) {\n\t\t// cache a couple of useful references\n\t\tlet tileset = layer.tileset;\n\n\t\t// get top-left and bottom-right tile position\n\t\tconst rowItr = this.pixelToTileCoords(\n\t\t\trect.pos.x - tileset.tilewidth,\n\t\t\trect.pos.y - tileset.tileheight,\n\t\t\tvector2dPool.get(),\n\t\t).floorSelf();\n\t\tconst tileEnd = this.pixelToTileCoords(\n\t\t\trect.pos.x + rect.width + tileset.tilewidth,\n\t\t\trect.pos.y + rect.height + tileset.tileheight,\n\t\t\tvector2dPool.get(),\n\t\t).ceilSelf();\n\n\t\tconst rectEnd = this.tileToPixelCoords(\n\t\t\ttileEnd.x,\n\t\t\ttileEnd.y,\n\t\t\tvector2dPool.get(),\n\t\t);\n\n\t\t// Determine the tile and pixel coordinates to start at\n\t\tconst startPos = this.tileToPixelCoords(\n\t\t\trowItr.x,\n\t\t\trowItr.y,\n\t\t\tvector2dPool.get(),\n\t\t);\n\t\tstartPos.x -= this.hTilewidth;\n\t\tstartPos.y += this.tileheight;\n\n\t\t/* Determine in which half of the tile the top-left corner of the area we\n\t\t * need to draw is. If we're in the upper half, we need to start one row\n\t\t * up due to those tiles being visible as well. How we go up one row\n\t\t * depends on whether we're in the left or right half of the tile.\n\t\t */\n\t\tconst inUpperHalf = startPos.y - rect.pos.y > this.hTileheight;\n\t\tconst inLeftHalf = rect.pos.x - startPos.x < this.hTilewidth;\n\n\t\tif (inUpperHalf) {\n\t\t\tif (inLeftHalf) {\n\t\t\t\trowItr.x--;\n\t\t\t\tstartPos.x -= this.hTilewidth;\n\t\t\t} else {\n\t\t\t\trowItr.y--;\n\t\t\t\tstartPos.x += this.hTilewidth;\n\t\t\t}\n\t\t\tstartPos.y -= this.hTileheight;\n\t\t}\n\n\t\t// Determine whether the current row is shifted half a tile to the right\n\t\tlet shifted = inUpperHalf ^ inLeftHalf;\n\n\t\t// initialize the columnItr vector\n\t\tconst columnItr = vector2dPool.get().setV(rowItr);\n\n\t\t// main drawing loop — read (gid, flipMask) straight from the typed\n\t\t// array and resolve the tileset with a short-circuit cache.\n\t\t// NOTE: the iso hot loop bypasses `this.drawTileRaw` because it has\n\t\t// already computed pixel coords (`x`, `y/2`) from the staggered scan;\n\t\t// going through drawTileRaw would re-derive them from layer-cell coords\n\t\t// and lose the precomputation.\n\t\tconst layerCols = layer.cols;\n\t\tconst layerRows = layer.rows;\n\t\tconst data = layer.layerData;\n\t\tconst tilesets = layer.tilesets;\n\t\tlet tilesetCache = tileset;\n\t\tif (tilesetCache === null) {\n\t\t\tvector2dPool.release(columnItr);\n\t\t\tvector2dPool.release(rowItr);\n\t\t\tvector2dPool.release(tileEnd);\n\t\t\tvector2dPool.release(rectEnd);\n\t\t\tvector2dPool.release(startPos);\n\t\t\treturn;\n\t\t}\n\t\tfor (\n\t\t\tlet y = startPos.y * 2;\n\t\t\ty - this.tileheight * 2 < rectEnd.y * 2;\n\t\t\ty += this.tileheight\n\t\t) {\n\t\t\tcolumnItr.setV(rowItr);\n\t\t\tfor (let x = startPos.x; x < rectEnd.x; x += this.tilewidth) {\n\t\t\t\tconst cx = columnItr.x;\n\t\t\t\tconst cy = columnItr.y;\n\t\t\t\t// bounds check (this loop didn't disable it in the old path)\n\t\t\t\tif (cx >= 0 && cx < layerCols && cy >= 0 && cy < layerRows) {\n\t\t\t\t\tconst idx = (cy * layerCols + cx) * 2;\n\t\t\t\t\tconst gid = data[idx];\n\t\t\t\t\tif (gid !== 0) {\n\t\t\t\t\t\tconst flipMask = data[idx + 1];\n\t\t\t\t\t\tif (!tilesetCache.contains(gid)) {\n\t\t\t\t\t\t\ttilesetCache = tilesets.getTilesetByGid(gid);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst offset = tilesetCache.tileoffset;\n\t\t\t\t\t\ttilesetCache.drawTileRaw(\n\t\t\t\t\t\t\trenderer,\n\t\t\t\t\t\t\toffset.x + x,\n\t\t\t\t\t\t\toffset.y + y / 2 - tilesetCache.tileheight,\n\t\t\t\t\t\t\tgid,\n\t\t\t\t\t\t\tflipMask,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Advance to the next column\n\t\t\t\tcolumnItr.x++;\n\t\t\t\tcolumnItr.y--;\n\t\t\t}\n\n\t\t\t// Advance to the next row\n\t\t\tif (!shifted) {\n\t\t\t\trowItr.x++;\n\t\t\t\tstartPos.x += this.hTilewidth;\n\t\t\t\tshifted = true;\n\t\t\t} else {\n\t\t\t\trowItr.y++;\n\t\t\t\tstartPos.x -= this.hTilewidth;\n\t\t\t\tshifted = false;\n\t\t\t}\n\t\t}\n\n\t\tvector2dPool.release(columnItr);\n\t\tvector2dPool.release(rowItr);\n\t\tvector2dPool.release(tileEnd);\n\t\tvector2dPool.release(rectEnd);\n\t\tvector2dPool.release(startPos);\n\t}\n}\n","import { vector2dPool } from \"../../../math/vector2d.ts\";\nimport TMXRenderer from \"./TMXRenderer.js\";\n\n/**\n * an Orthogonal Map Renderer\n * @category Tilemap\n */\nexport default class TMXOrthogonalRenderer extends TMXRenderer {\n\t/**\n\t * @param {TMXTileMap} map - the TMX map\n\t */\n\tconstructor(map) {\n\t\tsuper(map.cols, map.rows, map.tilewidth, map.tileheight);\n\t}\n\n\t/**\n\t * return true if the renderer can render the specified layer\n\t * @ignore\n\t */\n\tcanRender(layer) {\n\t\treturn layer.orientation === \"orthogonal\" && super.canRender(layer);\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @ignore\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\treturn ret.set(x / this.tilewidth, y / this.tileheight);\n\t}\n\n\t/**\n\t * return the pixel position corresponding of the specified tile\n\t * @ignore\n\t */\n\ttileToPixelCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\treturn ret.set(x * this.tilewidth, y * this.tileheight);\n\t}\n\n\t/**\n\t * draw the tile map (legacy entry point — accepts a fully-constructed Tile)\n\t * @ignore\n\t */\n\tdrawTile(renderer, x, y, tmxTile) {\n\t\tconst tileset = tmxTile.tileset;\n\t\t// draw the tile\n\t\ttileset.drawTile(\n\t\t\trenderer,\n\t\t\ttileset.tileoffset.x + x * this.tilewidth,\n\t\t\ttileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight,\n\t\t\ttmxTile,\n\t\t);\n\t}\n\n\t/**\n\t * draw a tile from raw (gid, flipMask, tileset) data — used by the hot\n\t * rendering loop to bypass Tile construction\n\t * @ignore\n\t */\n\tdrawTileRaw(renderer, x, y, gid, flipMask, tileset) {\n\t\ttileset.drawTileRaw(\n\t\t\trenderer,\n\t\t\ttileset.tileoffset.x + x * this.tilewidth,\n\t\t\ttileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight,\n\t\t\tgid,\n\t\t\tflipMask,\n\t\t);\n\t}\n\n\t/**\n\t * draw the tile map\n\t * @ignore\n\t */\n\tdrawTileLayer(renderer, layer, rect) {\n\t\tlet incX = 1;\n\t\tlet incY = 1;\n\n\t\t// get top-left and bottom-right tile position\n\t\tconst start = this.pixelToTileCoords(\n\t\t\tMath.max(rect.pos.x - (layer.maxTileSize.width - layer.tilewidth), 0),\n\t\t\tMath.max(rect.pos.y - (layer.maxTileSize.height - layer.tileheight), 0),\n\t\t\tvector2dPool.get(),\n\t\t).floorSelf();\n\n\t\tconst end = this.pixelToTileCoords(\n\t\t\trect.pos.x + rect.width + this.tilewidth,\n\t\t\trect.pos.y + rect.height + this.tileheight,\n\t\t\tvector2dPool.get(),\n\t\t).ceilSelf();\n\n\t\t//ensure we are in the valid tile range\n\t\tend.x = Math.min(end.x, this.cols);\n\t\tend.y = Math.min(end.y, this.rows);\n\n\t\tswitch (layer.renderorder) {\n\t\t\tcase \"right-up\":\n\t\t\t\t// swapping start.y and end.y\n\t\t\t\tend.y = start.y + (start.y = end.y) - end.y;\n\t\t\t\tincY = -1;\n\t\t\t\tbreak;\n\t\t\tcase \"left-down\":\n\t\t\t\t// swapping start.x and end.x\n\t\t\t\tend.x = start.x + (start.x = end.x) - end.x;\n\t\t\t\tincX = -1;\n\t\t\t\tbreak;\n\t\t\tcase \"left-up\":\n\t\t\t\t// swapping start.x and end.x\n\t\t\t\tend.x = start.x + (start.x = end.x) - end.x;\n\t\t\t\t// swapping start.y and end.y\n\t\t\t\tend.y = start.y + (start.y = end.y) - end.y;\n\t\t\t\tincX = -1;\n\t\t\t\tincY = -1;\n\t\t\t\tbreak;\n\t\t\tdefault: // right-down\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// main drawing loop — read (gid, flipMask) straight from the typed\n\t\t// array and resolve the tileset with a short-circuit cache (the common\n\t\t// single-tileset case never enters the lookup branch)\n\t\tconst cols = layer.cols;\n\t\tconst data = layer.layerData;\n\t\tconst tilesets = layer.tilesets;\n\t\tlet tilesetCache = layer.tileset;\n\t\tif (tilesetCache === null) {\n\t\t\t// no tilesets attached — nothing to draw\n\t\t\tvector2dPool.release(start);\n\t\t\tvector2dPool.release(end);\n\t\t\treturn;\n\t\t}\n\t\tfor (let y = start.y; y !== end.y; y += incY) {\n\t\t\tfor (let x = start.x; x !== end.x; x += incX) {\n\t\t\t\tconst idx = (y * cols + x) * 2;\n\t\t\t\tconst gid = data[idx];\n\t\t\t\t// `!gid` covers both empty cells (0) and out-of-range reads\n\t\t\t\t// (Uint16Array returns undefined for idx beyond length — happens\n\t\t\t\t// when a non-default renderorder swap pushes start past `cols`)\n\t\t\t\tif (!gid) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst flipMask = data[idx + 1];\n\t\t\t\tif (!tilesetCache.contains(gid)) {\n\t\t\t\t\ttilesetCache = tilesets.getTilesetByGid(gid);\n\t\t\t\t}\n\t\t\t\tthis.drawTileRaw(renderer, x, y, gid, flipMask, tilesetCache);\n\t\t\t}\n\t\t}\n\n\t\tvector2dPool.release(start);\n\t\tvector2dPool.release(end);\n\t}\n}\n","import { vector2dPool } from \"../../../math/vector2d.ts\";\nimport { boundsPool } from \"../../../physics/bounds.ts\";\nimport TMXLayer from \"./../TMXLayer.js\";\nimport TMXOrthogonalRenderer from \"./TMXOrthogonalRenderer.js\";\n\n/**\n * an Oblique Map Renderer (Tiled 1.12+)\n * Extends the orthogonal renderer with a 2D shear transform\n * controlled by the map's skewx/skewy attributes.\n * @category Tilemap\n */\nexport default class TMXObliqueRenderer extends TMXOrthogonalRenderer {\n\t/**\n\t * @param {TMXTileMap} map - the TMX map\n\t */\n\tconstructor(map) {\n\t\tsuper(map);\n\n\t\t/**\n\t\t * horizontal pixel offset per tile row\n\t\t * @type {number}\n\t\t */\n\t\tthis.skewX = map.skewx;\n\n\t\t/**\n\t\t * vertical pixel offset per tile column\n\t\t * @type {number}\n\t\t */\n\t\tthis.skewY = map.skewy;\n\n\t\t/**\n\t\t * horizontal shear factor (skewX / tileheight)\n\t\t * @type {number}\n\t\t */\n\t\tthis.shearX = this.tileheight !== 0 ? this.skewX / this.tileheight : 0;\n\n\t\t/**\n\t\t * vertical shear factor (skewY / tilewidth)\n\t\t * @type {number}\n\t\t */\n\t\tthis.shearY = this.tilewidth !== 0 ? this.skewY / this.tilewidth : 0;\n\n\t\t/**\n\t\t * determinant of the shear matrix (for inverse transform)\n\t\t * @type {number}\n\t\t */\n\t\tthis._det = 1 - this.shearX * this.shearY;\n\t}\n\n\t/**\n\t * return true if the renderer can render the specified layer\n\t * @ignore\n\t */\n\tcanRender(layer) {\n\t\treturn (\n\t\t\tlayer.orientation === \"oblique\" &&\n\t\t\tthis.tilewidth === layer.tilewidth &&\n\t\t\tthis.tileheight === layer.tileheight\n\t\t);\n\t}\n\n\t/**\n\t * return the bounding rect for this map renderer\n\t * @param {TMXLayer} [layer] - calculate the bounding rect for a specific layer\n\t * @returns {Bounds}\n\t */\n\tgetBounds(layer) {\n\t\tconst bounds = layer instanceof TMXLayer ? boundsPool.get() : this.bounds;\n\t\tconst cols = layer instanceof TMXLayer ? layer.cols : this.cols;\n\t\tconst rows = layer instanceof TMXLayer ? layer.rows : this.rows;\n\t\tconst w = cols * this.tilewidth;\n\t\tconst h = rows * this.tileheight;\n\n\t\t// the sheared rectangle forms a parallelogram; compute its AABB\n\t\t// corners: (0,0), (w, skewY*cols), (skewX*rows, h), (w+skewX*rows, h+skewY*cols)\n\t\tconst sx = this.skewX * rows;\n\t\tconst sy = this.skewY * cols;\n\n\t\tconst minX = Math.min(0, sx, sy * this.shearX, w + sx);\n\t\tconst minY = Math.min(0, sy, sx * this.shearY, h + sy);\n\t\tconst maxX = Math.max(0, w, w + sx, sx);\n\t\tconst maxY = Math.max(0, h, h + sy, sy);\n\n\t\tbounds.setMinMax(minX, minY, maxX, maxY);\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @ignore\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\t// inverse shear: [1, -shearX; -shearY, 1] / det\n\t\tconst px = (x - this.shearX * y) / this._det;\n\t\tconst py = (y - this.shearY * x) / this._det;\n\t\treturn ret.set(px / this.tilewidth, py / this.tileheight);\n\t}\n\n\t/**\n\t * return the pixel position corresponding of the specified tile\n\t * @ignore\n\t */\n\ttileToPixelCoords(x, y, v) {\n\t\tconst ret = v || vector2dPool.get();\n\t\tconst px = x * this.tilewidth;\n\t\tconst py = y * this.tileheight;\n\t\t// apply shear\n\t\treturn ret.set(px + this.shearX * py, this.shearY * px + py);\n\t}\n\n\t/**\n\t * draw the tile map (legacy entry point — accepts a fully-constructed Tile)\n\t * @ignore\n\t */\n\tdrawTile(renderer, x, y, tmxTile) {\n\t\tconst tileset = tmxTile.tileset;\n\t\t// compute screen position with skew offset\n\t\tconst dx = tileset.tileoffset.x + x * this.tilewidth + this.skewX * y;\n\t\tconst dy =\n\t\t\ttileset.tileoffset.y +\n\t\t\t(y + 1) * this.tileheight -\n\t\t\ttileset.tileheight +\n\t\t\tthis.skewY * x;\n\t\ttileset.drawTile(renderer, dx, dy, tmxTile);\n\t}\n\n\t/**\n\t * draw a tile from raw (gid, flipMask, tileset) data — used by the hot\n\t * rendering loop to bypass Tile construction\n\t * @ignore\n\t */\n\tdrawTileRaw(renderer, x, y, gid, flipMask, tileset) {\n\t\tconst dx = tileset.tileoffset.x + x * this.tilewidth + this.skewX * y;\n\t\tconst dy =\n\t\t\ttileset.tileoffset.y +\n\t\t\t(y + 1) * this.tileheight -\n\t\t\ttileset.tileheight +\n\t\t\tthis.skewY * x;\n\t\ttileset.drawTileRaw(renderer, dx, dy, gid, flipMask);\n\t}\n\n\t/**\n\t * draw the given TMX Layer for the given area\n\t * @ignore\n\t */\n\tdrawTileLayer(renderer, layer, rect) {\n\t\tlet incX = 1;\n\t\tlet incY = 1;\n\n\t\t// inverse-shear the visible rect to find the tile range in map space\n\t\tconst rxMin = rect.pos.x;\n\t\tconst ryMin = rect.pos.y;\n\t\tconst rxMax = rect.pos.x + rect.width;\n\t\tconst ryMax = rect.pos.y + rect.height;\n\n\t\t// un-shear all four corners and find the tile bounds\n\t\tconst invCorners = [\n\t\t\tthis.pixelToTileCoords(rxMin, ryMin, vector2dPool.get()),\n\t\t\tthis.pixelToTileCoords(rxMax, ryMin, vector2dPool.get()),\n\t\t\tthis.pixelToTileCoords(rxMin, ryMax, vector2dPool.get()),\n\t\t\tthis.pixelToTileCoords(rxMax, ryMax, vector2dPool.get()),\n\t\t];\n\n\t\tlet startX = Infinity;\n\t\tlet startY = Infinity;\n\t\tlet endX = -Infinity;\n\t\tlet endY = -Infinity;\n\t\tfor (const c of invCorners) {\n\t\t\tif (c.x < startX) {\n\t\t\t\tstartX = c.x;\n\t\t\t}\n\t\t\tif (c.y < startY) {\n\t\t\t\tstartY = c.y;\n\t\t\t}\n\t\t\tif (c.x > endX) {\n\t\t\t\tendX = c.x;\n\t\t\t}\n\t\t\tif (c.y > endY) {\n\t\t\t\tendY = c.y;\n\t\t\t}\n\t\t}\n\t\tfor (const c of invCorners) {\n\t\t\tvector2dPool.release(c);\n\t\t}\n\n\t\t// add margin for oversize tiles\n\t\tstartX = Math.max(\n\t\t\tMath.floor(\n\t\t\t\tstartX - (layer.maxTileSize.width - layer.tilewidth) / this.tilewidth,\n\t\t\t),\n\t\t\t0,\n\t\t);\n\t\tstartY = Math.max(\n\t\t\tMath.floor(\n\t\t\t\tstartY -\n\t\t\t\t\t(layer.maxTileSize.height - layer.tileheight) / this.tileheight,\n\t\t\t),\n\t\t\t0,\n\t\t);\n\t\tendX = Math.min(Math.ceil(endX) + 1, this.cols);\n\t\tendY = Math.min(Math.ceil(endY) + 1, this.rows);\n\n\t\tswitch (layer.renderorder) {\n\t\t\tcase \"right-up\":\n\t\t\t\tendY = startY + (startY = endY) - endY;\n\t\t\t\tincY = -1;\n\t\t\t\tbreak;\n\t\t\tcase \"left-down\":\n\t\t\t\tendX = startX + (startX = endX) - endX;\n\t\t\t\tincX = -1;\n\t\t\t\tbreak;\n\t\t\tcase \"left-up\":\n\t\t\t\tendX = startX + (startX = endX) - endX;\n\t\t\t\tendY = startY + (startY = endY) - endY;\n\t\t\t\tincX = -1;\n\t\t\t\tincY = -1;\n\t\t\t\tbreak;\n\t\t\tdefault: // right-down\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// main drawing loop — direct typed-array reads, short-circuit tileset cache\n\t\tconst cols = layer.cols;\n\t\tconst data = layer.layerData;\n\t\tconst tilesets = layer.tilesets;\n\t\tlet tilesetCache = layer.tileset;\n\t\tif (tilesetCache === null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (let y = startY; y !== endY; y += incY) {\n\t\t\tfor (let x = startX; x !== endX; x += incX) {\n\t\t\t\tconst idx = (y * cols + x) * 2;\n\t\t\t\tconst gid = data[idx];\n\t\t\t\tif (!gid) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst flipMask = data[idx + 1];\n\t\t\t\tif (!tilesetCache.contains(gid)) {\n\t\t\t\t\ttilesetCache = tilesets.getTilesetByGid(gid);\n\t\t\t\t}\n\t\t\t\tthis.drawTileRaw(renderer, x, y, gid, flipMask, tilesetCache);\n\t\t\t}\n\t\t}\n\t}\n}\n","import { degToRad } from \"./../../../math/math.ts\";\nimport { vector2dPool } from \"../../../math/vector2d.ts\";\nimport TMXHexagonalRenderer from \"./TMXHexagonalRenderer.js\";\n\n/**\n * a Staggered Map Renderer\n * @category Tilemap\n */\nexport default class TMXStaggeredRenderer extends TMXHexagonalRenderer {\n\t/**\n\t * return true if the renderer can render the specified layer\n\t * @ignore\n\t */\n\tcanRender(layer) {\n\t\treturn layer.orientation === \"staggered\" && super.canRender(layer);\n\t}\n\n\t/**\n\t * return the tile position corresponding to the specified pixel\n\t * @ignore\n\t */\n\tpixelToTileCoords(x, y, v) {\n\t\tlet ret = v || vector2dPool.get();\n\n\t\tlet alignedX = x;\n\t\tlet alignedY = y;\n\n\t\tif (this.staggerX) {\n\t\t\talignedX -= this.staggerEven ? this.sideoffsetx : 0;\n\t\t} else {\n\t\t\talignedY -= this.staggerEven ? this.sideoffsety : 0;\n\t\t}\n\n\t\t// Start with the coordinates of a grid-aligned tile\n\t\tlet referencePoint = vector2dPool.get(\n\t\t\tMath.floor(alignedX / this.tilewidth),\n\t\t\tMath.floor(alignedY / this.tileheight),\n\t\t);\n\n\t\t// Adjust the reference point to the correct tile coordinates\n\t\tif (this.staggerX) {\n\t\t\treferencePoint.x = referencePoint.x * 2;\n\t\t\tif (this.staggerEven) {\n\t\t\t\t++referencePoint.x;\n\t\t\t}\n\t\t} else {\n\t\t\treferencePoint.y = referencePoint.y * 2;\n\t\t\tif (this.staggerEven) {\n\t\t\t\t++referencePoint.y;\n\t\t\t}\n\t\t}\n\n\t\t// Relative x and y position on the base square of the grid-aligned tile\n\t\tconst rel = vector2dPool.get(\n\t\t\talignedX - referencePoint.x * this.tilewidth,\n\t\t\talignedY - referencePoint.y * this.tileheight,\n\t\t);\n\n\t\t// Check whether the cursor is in any of the corners (neighboring tiles)\n\t\tconst y_pos = rel.x * (this.tileheight / this.tilewidth);\n\n\t\tif (this.sideoffsety - y_pos > rel.y) {\n\t\t\treferencePoint = this.topLeft(\n\t\t\t\treferencePoint.x,\n\t\t\t\treferencePoint.y,\n\t\t\t\treferencePoint,\n\t\t\t);\n\t\t}\n\t\tif (-this.sideoffsety + y_pos > rel.y) {\n\t\t\treferencePoint = this.topRight(\n\t\t\t\treferencePoint.x,\n\t\t\t\treferencePoint.y,\n\t\t\t\treferencePoint,\n\t\t\t);\n\t\t}\n\t\tif (this.sideoffsety + y_pos < rel.y) {\n\t\t\treferencePoint = this.bottomLeft(\n\t\t\t\treferencePoint.x,\n\t\t\t\treferencePoint.y,\n\t\t\t\treferencePoint,\n\t\t\t);\n\t\t}\n\t\tif (this.sideoffsety * 3 - y_pos < rel.y) {\n\t\t\treferencePoint = this.bottomRight(\n\t\t\t\treferencePoint.x,\n\t\t\t\treferencePoint.y,\n\t\t\t\treferencePoint,\n\t\t\t);\n\t\t}\n\n\t\tret = this.tileToPixelCoords(referencePoint.x, referencePoint.y, ret);\n\n\t\tret.set(x - ret.x, y - ret.y);\n\n\t\t// Start with the coordinates of a grid-aligned tile\n\t\tret.set(\n\t\t\tret.x - this.tilewidth / 2,\n\t\t\tret.y * (this.tilewidth / this.tileheight),\n\t\t);\n\n\t\tret\n\t\t\t.div(this.tilewidth / Math.sqrt(2))\n\t\t\t.rotate(degToRad(-45))\n\t\t\t.add(referencePoint);\n\n\t\tvector2dPool.release(referencePoint);\n\t\tvector2dPool.release(rel);\n\n\t\treturn ret;\n\t}\n}\n","import TMXHexagonalRenderer from \"./TMXHexagonalRenderer.js\";\nimport TMXIsometricRenderer from \"./TMXIsometricRenderer.js\";\nimport TMXObliqueRenderer from \"./TMXObliqueRenderer.js\";\nimport TMXOrthogonalRenderer from \"./TMXOrthogonalRenderer.js\";\nimport TMXStaggeredRenderer from \"./TMXStaggeredRenderer.js\";\n\n/**\n * return a compatible renderer object for the given map\n * @param {TMXTileMap} map\n * @ignore\n */\nexport function getNewTMXRenderer(map) {\n\tswitch (map.orientation) {\n\t\tcase \"orthogonal\":\n\t\t\treturn new TMXOrthogonalRenderer(map);\n\n\t\tcase \"isometric\":\n\t\t\treturn new TMXIsometricRenderer(map);\n\n\t\tcase \"hexagonal\":\n\t\t\treturn new TMXHexagonalRenderer(map);\n\n\t\tcase \"staggered\":\n\t\t\treturn new TMXStaggeredRenderer(map);\n\n\t\tcase \"oblique\":\n\t\t\treturn new TMXObliqueRenderer(map);\n\n\t\t// if none found, throw an exception\n\t\tdefault:\n\t\t\tthrow new Error(`${map.orientation} type TMX Tile Map not supported!`);\n\t}\n}\n","import { ellipsePool } from \"../../geometries/ellipse.ts\";\nimport { linePool } from \"../../geometries/line.ts\";\nimport { pointPool } from \"../../geometries/point.ts\";\nimport { polygonPool } from \"../../geometries/polygon.ts\";\nimport { roundedRectanglePool } from \"../../geometries/roundrect.ts\";\nimport { degToRad } from \"../../math/math.ts\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport Tile from \"./TMXTile.js\";\nimport { applyTMXProperties } from \"./TMXUtils.js\";\n\n/**\n * Detect the shape type from the TMX object settings.\n * Shape markers are mutually exclusive; the first match wins.\n * @param {object} settings - TMX object settings\n * @returns {string} one of \"ellipse\", \"capsule\", \"point\", \"polygon\", \"polyline\", \"rectangle\"\n * @ignore\n */\nfunction detectShape(settings) {\n\tif (typeof settings.ellipse !== \"undefined\") {\n\t\treturn \"ellipse\";\n\t}\n\tif (typeof settings.capsule !== \"undefined\") {\n\t\treturn \"capsule\";\n\t}\n\tif (typeof settings.point !== \"undefined\") {\n\t\treturn \"point\";\n\t}\n\tif (typeof settings.polygon !== \"undefined\") {\n\t\treturn \"polygon\";\n\t}\n\tif (typeof settings.polyline !== \"undefined\") {\n\t\treturn \"polyline\";\n\t}\n\treturn \"rectangle\";\n}\n\n/**\n * a TMX Object defintion, as defined in Tiled\n * (Object definition is translated into the virtual `app.world` using `me.Renderable`)\n * @ignore\n */\nexport default class TMXObject {\n\tconstructor(map, settings, z) {\n\t\t/**\n\t\t * point list in JSON format\n\t\t * @type {object[]}\n\t\t */\n\t\tthis.points = undefined;\n\n\t\t/**\n\t\t * object name\n\t\t * @type {string}\n\t\t */\n\t\tthis.name = settings.name;\n\n\t\t/**\n\t\t * object x position\n\t\t * @type {number}\n\t\t */\n\t\tthis.x = +settings.x;\n\n\t\t/**\n\t\t * object y position\n\t\t * @type {number}\n\t\t */\n\t\tthis.y = +settings.y;\n\n\t\t/**\n\t\t * object z order\n\t\t * @type {number}\n\t\t */\n\t\tthis.z = +z;\n\n\t\t/**\n\t\t * object width\n\t\t * @type {number}\n\t\t */\n\t\tthis.width = +settings.width || 0;\n\n\t\t/**\n\t\t * object height\n\t\t * @type {number}\n\t\t */\n\t\tthis.height = +settings.height || 0;\n\n\t\t/**\n\t\t * object gid value\n\t\t * when defined the object is a tiled object\n\t\t * @type {number}\n\t\t */\n\t\tthis.gid = +settings.gid || null;\n\n\t\t/**\n\t\t * tint color\n\t\t * @type {string}\n\t\t */\n\t\tthis.tintcolor = settings.tintcolor;\n\n\t\t/**\n\t\t * object type\n\t\t * @type {string}\n\t\t * @deprecated since Tiled 1.9\n\t\t * @see {@link https://docs.mapeditor.org/en/stable/reference/tmx-changelog/#tiled-1-9}\n\t\t */\n\t\tthis.type = settings.type;\n\n\t\t/**\n\t\t * the object class\n\t\t * @type {string}\n\t\t */\n\t\tthis.class = settings.class ?? settings.type;\n\n\t\t/**\n\t\t * object text\n\t\t * @type {object}\n\t\t * @see {@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#text}\n\t\t */\n\t\tthis.text = undefined;\n\n\t\t/**\n\t\t * The rotation of the object in radians clockwise (defaults to 0)\n\t\t * @type {number}\n\t\t */\n\t\tthis.rotation = degToRad(+settings.rotation || 0);\n\n\t\t/**\n\t\t * the object opacity (0-1), defaults to 1 (since Tiled 1.12)\n\t\t * @type {number}\n\t\t */\n\t\tthis.opacity = +(settings.opacity ?? 1);\n\n\t\t/**\n\t\t * whether the object is visible, defaults to true\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.visible = +(settings.visible ?? 1) !== 0;\n\n\t\t/**\n\t\t * object unique identifier per level (Tiled 0.11.x+)\n\t\t * @type {number}\n\t\t */\n\t\tthis.id = +settings.id || undefined;\n\n\t\t/**\n\t\t * object orientation (orthogonal or isometric)\n\t\t * @type {string}\n\t\t */\n\t\tthis.orientation = map.orientation;\n\n\t\t/**\n\t\t * the collision shapes defined for this object\n\t\t * @type {object[]}\n\t\t */\n\t\tthis.shapes = undefined;\n\n\t\t/**\n\t\t * the detected shape type\n\t\t * @type {string}\n\t\t */\n\t\tthis.shapeType = \"rectangle\";\n\n\t\t// detect shape type and store points for polygon/polyline\n\t\tif (typeof this.gid === \"number\") {\n\t\t\tthis.setTile(map.tilesets);\n\t\t} else {\n\t\t\tthis.shapeType = detectShape(settings);\n\t\t\tif (this.shapeType === \"polygon\" || this.shapeType === \"polyline\") {\n\t\t\t\tthis.points = settings[this.shapeType];\n\t\t\t}\n\t\t}\n\n\t\t// backward-compatible boolean flags\n\t\t/** @type {boolean} */\n\t\tthis.isEllipse = this.shapeType === \"ellipse\";\n\t\t/** @type {boolean} */\n\t\tthis.isCapsule = this.shapeType === \"capsule\";\n\t\t/** @type {boolean} */\n\t\tthis.isPoint = this.shapeType === \"point\";\n\t\t/** @type {boolean} */\n\t\tthis.isPolygon = this.shapeType === \"polygon\";\n\t\t/** @type {boolean} */\n\t\tthis.isPolyLine = this.shapeType === \"polyline\";\n\n\t\t// check for text information\n\t\tif (typeof settings.text !== \"undefined\") {\n\t\t\tthis.text = settings.text;\n\t\t\tthis.text.font = settings.text.fontfamily || \"sans-serif\";\n\t\t\tthis.text.size = settings.text.pixelsize || 16;\n\t\t\tthis.text.fillStyle = settings.text.color || \"#000000\";\n\t\t\tthis.text.textAlign = settings.text.halign || \"left\";\n\t\t\tthis.text.textBaseline = settings.text.valign || \"top\";\n\t\t\tthis.text.width = this.width;\n\t\t\tthis.text.height = this.height;\n\t\t\tapplyTMXProperties(this.text, settings);\n\t\t} else {\n\t\t\tapplyTMXProperties(this, settings);\n\t\t\tif (!this.shapes) {\n\t\t\t\tthis.shapes = this.parseTMXShapes();\n\t\t\t}\n\t\t}\n\n\t\t// Adjust the Position to match Tiled\n\t\tif (!map.isEditor) {\n\t\t\tmap.getRenderer().adjustPosition(this);\n\t\t}\n\t}\n\n\t/**\n\t * set the object image (for Tiled Object)\n\t * @ignore\n\t */\n\tsetTile(tilesets) {\n\t\tconst tileset = tilesets.getTilesetByGid(this.gid);\n\n\t\tif (tileset.isCollection === false) {\n\t\t\tthis.width = this.framewidth = tileset.tilewidth;\n\t\t\tthis.height = this.frameheight = tileset.tileheight;\n\t\t}\n\n\t\tthis.tile = new Tile(this.x, this.y, this.gid, tileset);\n\t}\n\n\t/**\n\t * parses the TMX shape definition and returns a corresponding array of shape objects\n\t * @private\n\t * @returns {Polygon[]|Line[]|Ellipse[]|RoundRect[]} an array of shape objects\n\t */\n\tparseTMXShapes() {\n\t\tconst shapes = [];\n\n\t\tswitch (this.shapeType) {\n\t\t\tcase \"ellipse\":\n\t\t\t\tshapes.push(\n\t\t\t\t\tellipsePool\n\t\t\t\t\t\t.get(this.width / 2, this.height / 2, this.width, this.height)\n\t\t\t\t\t\t.rotate(this.rotation),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"capsule\": {\n\t\t\t\tconst radius = Math.min(this.width, this.height) / 2;\n\t\t\t\tshapes.push(\n\t\t\t\t\troundedRectanglePool\n\t\t\t\t\t\t.get(0, 0, this.width, this.height, radius)\n\t\t\t\t\t\t.rotate(this.rotation),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"point\":\n\t\t\t\tshapes.push(pointPool.get(this.x, this.y));\n\t\t\t\tbreak;\n\n\t\t\tcase \"polygon\": {\n\t\t\t\tconst polygon = polygonPool.get(0, 0, this.points);\n\t\t\t\tconst isConvex = polygon.isConvex();\n\n\t\t\t\tif (isConvex === true) {\n\t\t\t\t\tshapes.push(polygon.rotate(this.rotation));\n\t\t\t\t} else if (isConvex === false) {\n\t\t\t\t\t// decompose concave polygon into convex triangles\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"melonJS: concave collision polygon detected, decomposing into convex triangles\",\n\t\t\t\t\t);\n\t\t\t\t\tconst indices = polygon.getIndices();\n\t\t\t\t\tconst pts = polygon.points;\n\t\t\t\t\tfor (let t = 0; t < indices.length; t += 3) {\n\t\t\t\t\t\tshapes.push(\n\t\t\t\t\t\t\tpolygonPool\n\t\t\t\t\t\t\t\t.get(0, 0, [\n\t\t\t\t\t\t\t\t\tvector2dPool.get(pts[indices[t]].x, pts[indices[t]].y),\n\t\t\t\t\t\t\t\t\tvector2dPool.get(\n\t\t\t\t\t\t\t\t\t\tpts[indices[t + 1]].x,\n\t\t\t\t\t\t\t\t\t\tpts[indices[t + 1]].y,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tvector2dPool.get(\n\t\t\t\t\t\t\t\t\t\tpts[indices[t + 2]].x,\n\t\t\t\t\t\t\t\t\t\tpts[indices[t + 2]].y,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\t\t.rotate(this.rotation),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tpolygonPool.release(polygon);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn(\"melonJS: invalid polygon definition, skipping\");\n\t\t\t\t\tpolygonPool.release(polygon);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"polyline\": {\n\t\t\t\tconst p = this.points;\n\t\t\t\tfor (let i = 0, len = p.length - 1; i < len; i++) {\n\t\t\t\t\tlet p1 = vector2dPool.get(p[i].x, p[i].y);\n\t\t\t\t\tlet p2 = vector2dPool.get(p[i + 1].x, p[i + 1].y);\n\t\t\t\t\tif (this.rotation !== 0) {\n\t\t\t\t\t\tp1 = p1.rotate(this.rotation);\n\t\t\t\t\t\tp2 = p2.rotate(this.rotation);\n\t\t\t\t\t}\n\t\t\t\t\tshapes.push(linePool.get(0, 0, [p1, p2]));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\t// rectangle — returns a polygon\n\t\t\t\tshapes.push(\n\t\t\t\t\tpolygonPool\n\t\t\t\t\t\t.get(0, 0, [\n\t\t\t\t\t\t\tvector2dPool.get(),\n\t\t\t\t\t\t\tvector2dPool.get(this.width, 0),\n\t\t\t\t\t\t\tvector2dPool.get(this.width, this.height),\n\t\t\t\t\t\t\tvector2dPool.get(0, this.height),\n\t\t\t\t\t\t])\n\t\t\t\t\t\t.rotate(this.rotation),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Apply isometric projection\n\t\tif (this.orientation === \"isometric\") {\n\t\t\tfor (let i = 0; i < shapes.length; i++) {\n\t\t\t\tif (typeof shapes[i].toIso === \"function\") {\n\t\t\t\t\tshapes[i].toIso();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn shapes;\n\t}\n\n\t/**\n\t * getObjectPropertyByName\n\t * @ignore\n\t */\n\tgetObjectPropertyByName(name) {\n\t\treturn this[name];\n\t}\n}\n","import { clamp } from \"../../math/math.ts\";\nimport TMXLayer from \"./TMXLayer.js\";\nimport TMXObject from \"./TMXObject.js\";\nimport { applyTMXProperties, tiledBlendMode } from \"./TMXUtils.js\";\n\n/**\n * object group definition as defined in Tiled.\n * (group definition is translated into the virtual `app.world` using `me.Container`)\n * @ignore\n */\nexport default class TMXGroup {\n\tconstructor(map, data, z) {\n\t\t/**\n\t\t * group name\n\t\t * @type {string}\n\t\t */\n\t\tthis.name = data.name;\n\n\t\t/**\n\t\t * group width\n\t\t * @type {number}\n\t\t */\n\t\tthis.width = data.width || 0;\n\n\t\t/**\n\t\t * group height\n\t\t * @type {number}\n\t\t */\n\t\tthis.height = data.height || 0;\n\n\t\t/**\n\t\t * tint color\n\t\t * @type {string}\n\t\t */\n\t\tthis.tintcolor = data.tintcolor;\n\n\t\t/**\n\t\t * the group class\n\t\t * @type {string}\n\t\t */\n\t\tthis.class = data.class;\n\n\t\t/**\n\t\t * group z order\n\t\t * @type {number}\n\t\t */\n\t\tthis.z = z;\n\n\t\t/**\n\t\t * group objects list definition\n\t\t * @see {@link TMXObject}\n\t\t * @type {object[]}\n\t\t */\n\t\tthis.objects = [];\n\n\t\tconst visible = data.visible ?? true;\n\t\tthis.opacity = visible === true ? clamp(+data.opacity || 1.0, 0.0, 1.0) : 0;\n\n\t\t/**\n\t\t * group blend mode (Tiled 1.12+)\n\t\t * @type {string}\n\t\t */\n\t\tthis.blendMode = tiledBlendMode(data.mode);\n\n\t\t// check if we have any user-defined properties\n\t\tapplyTMXProperties(this, data);\n\n\t\t// parse all child objects/layers\n\t\tif (data.objects) {\n\t\t\tconst objects = data.objects;\n\t\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\t\tobjects[i].tintcolor = this.tintcolor;\n\t\t\t\tthis.objects.push(new TMXObject(map, objects[i], z));\n\t\t\t}\n\t\t}\n\n\t\tif (data.layers) {\n\t\t\tconst layers = data.layers;\n\t\t\tfor (let i = 0, len = layers.length; i < len; i++) {\n\t\t\t\tconst layer = new TMXLayer(\n\t\t\t\t\tmap,\n\t\t\t\t\tlayers[i],\n\t\t\t\t\tmap.tilewidth,\n\t\t\t\t\tmap.tileheight,\n\t\t\t\t\tmap.orientation,\n\t\t\t\t\tmap.tilesets,\n\t\t\t\t\tz,\n\t\t\t\t);\n\t\t\t\tz++;\n\t\t\t\t// set a renderer\n\t\t\t\tlayer.setRenderer(map.getRenderer());\n\t\t\t\t// resize container accordingly\n\t\t\t\tthis.width = Math.max(this.width, layer.width);\n\t\t\t\tthis.height = Math.max(this.height, layer.height);\n\t\t\t\tthis.objects.push(layer);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * reset function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// clear all allocated objects\n\t\tthis.objects = null;\n\t}\n\n\t/**\n\t * return the object count\n\t * @ignore\n\t */\n\tgetObjectCount() {\n\t\treturn this.objects.length;\n\t}\n\n\t/**\n\t * returns the object at the specified index\n\t * @ignore\n\t */\n\tgetObjectByIndex(idx) {\n\t\treturn this.objects[idx];\n\t}\n}\n","import { boundsPool } from \"../../../physics/bounds.ts\";\nimport Renderable from \"../../../renderable/renderable.js\";\nimport { getDefaultShape } from \"../TMXObjectFactory.js\";\n\n/**\n * create an unnamed shape object (Renderable with a static adapter-managed body)\n * @param {object} settings - TMX object settings\n * @returns {Renderable} the created shape object\n * @ignore\n */\nexport function createShapeObject(settings) {\n\tconst obj = new Renderable(\n\t\tsettings.x,\n\t\tsettings.y,\n\t\tsettings.width,\n\t\tsettings.height,\n\t);\n\tconst shape = getDefaultShape(settings);\n\tif (!shape || (Array.isArray(shape) && shape.length === 0)) {\n\t\t// Bail out loudly instead of poisoning `bodyDef.shapes` with\n\t\t// `[undefined]` and letting downstream `addShapes` / adapter\n\t\t// `addBody` blow up with an unhelpful \"cannot read shape\" later.\n\t\tthrow new Error(\n\t\t\t`melonJS: TMX object (id=${settings.id}, name=${settings.name ?? \"\"}, type=${settings.type ?? \"\"}) has no usable collision shape. Check the object's geometry in Tiled.`,\n\t\t);\n\t}\n\tobj.anchorPoint.set(0, 0);\n\tobj.name = settings.name;\n\tobj.type = settings.type;\n\tobj.class = settings.class || settings.type;\n\tobj.id = settings.id;\n\t// declarative body — adapter-portable, auto-registered on addChild\n\tobj.bodyDef = {\n\t\ttype: \"static\",\n\t\tshapes: Array.isArray(shape) ? shape : [shape],\n\t};\n\t// Size from the union of all shapes' bounds — the body isn't built\n\t// until adapter auto-registration, so we can't read body.getBounds().\n\tconst bounds = boundsPool.get();\n\tbounds.addShapes(obj.bodyDef.shapes, true);\n\tobj.resize(bounds.width, bounds.height);\n\tboundsPool.release(bounds);\n\tobj.pos.z = settings.z;\n\treturn obj;\n}\n","/**\n * apply the current text style to the given context\n * @ignore\n */\nexport default function setContextStyle(context, style) {\n\tcontext.font = style.font;\n\tcontext.fillStyle = style.fillStyle.toRGBA();\n\tcontext.strokeStyle = style.strokeStyle.toRGBA();\n\tcontext.lineWidth = style.lineWidth;\n\tcontext.textAlign = style.textAlign;\n\tcontext.textBaseline = style.textBaseline;\n}\n","import { game } from \"../../application/application.ts\";\nimport { Color, colorPool } from \"../../math/color.ts\";\n\nimport CanvasRenderTarget from \"../../video/rendertarget/canvasrendertarget.js\";\nimport { resolveAnchorPoint } from \"../anchorPoint.ts\";\nimport Renderable from \"../renderable.js\";\nimport TextMetrics from \"./textmetrics.js\";\nimport setContextStyle from \"./textstyle.js\";\n\n/*\n * ASCII Table\n * http://www.asciitable.com/\n * [ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz]\n *\n * -> first char \" \" 32d (0x20);\n */\n\nconst runits = [\"ex\", \"em\", \"pt\", \"px\"];\nconst toPX = [12, 24, 0.75, 1];\n\n// CSS generic font families must NOT be quoted: quoting a generic keyword turns\n// it into a (nonexistent) specific family name, so the browser silently falls\n// back to its default serif. These are kept unquoted by setFont().\nconst genericFontFamilies = new Set([\n\t\"serif\",\n\t\"sans-serif\",\n\t\"monospace\",\n\t\"cursive\",\n\t\"fantasy\",\n\t\"system-ui\",\n\t\"ui-serif\",\n\t\"ui-sans-serif\",\n\t\"ui-monospace\",\n\t\"ui-rounded\",\n\t\"math\",\n\t\"emoji\",\n\t\"fangsong\",\n]);\n\n/**\n * a Text object: draws a string using a system or web font.\n *\n * The text is rasterised to a cached texture, so it draws as fast as a sprite\n * while staying fully styleable — `fillStyle`, `strokeStyle` + `lineWidth`, and\n * per-object opacity — with multi-line text (embedded `\\n`), optional\n * `wordWrapWidth` word-wrapping, and chainable {@link Text#bold} /\n * {@link Text#italic}. `font` accepts any CSS family, including the generic\n * keywords (`sans-serif`, `monospace`, …); a web font loaded through the\n * `fontface` loader is referenced by its family name.\n *\n * For a crisp, tintable, retro look — or large amounts of mostly-static text —\n * consider {@link BitmapText} instead.\n * @category Text\n */\nexport default class Text extends Renderable {\n\t/**\n\t * @param {number} x - position of the text object\n\t * @param {number} y - position of the text object\n\t * @param {object} settings - the text configuration\n\t * @param {string} settings.font - a CSS font family: a specific name (`\"Arial\"`), a generic keyword (`\"sans-serif\"`, `\"monospace\"`, …), or a web font loaded via the `fontface` loader (referenced by its family name)\n\t * @param {number|string} settings.size - the font size: a number in pixels, or a CSS size string with a unit (`\"24px\"` / `\"1.5em\"` / `\"18pt\"`)\n\t * @param {Color|string} [settings.fillStyle=\"#000000\"] - a CSS color value used to fill the glyphs\n\t * @param {Color|string} [settings.strokeStyle=\"#000000\"] - a CSS color value used for the glyph outline (drawn when `lineWidth` > 0)\n\t * @param {number} [settings.lineWidth=0] - outline width in pixels (0 = no stroke)\n\t * @param {string} [settings.textAlign=\"left\"] - horizontal text alignment (\"left\", \"center\", \"right\")\n\t * @param {string} [settings.textBaseline=\"top\"] - the text baseline (\"top\", \"hanging\", \"middle\", \"alphabetic\", \"ideographic\", \"bottom\")\n\t * @param {number} [settings.lineHeight=1.0] - line spacing height\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0.0, y:0.0}] - anchor point to draw the text at. Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`.\n\t * @param {number} [settings.wordWrapWidth] - the maximum length in CSS pixels of a line before it wraps\n\t * @param {(string|string[])} [settings.text=\"\"] - a string, or an array of strings\n\t * @example\n\t * // a styled, word-wrapped, multi-line label using a generic system font\n\t * const label = new Text(8, 8, {\n\t *     font: \"sans-serif\",          // any CSS family (generic keywords work too)\n\t *     size: 24,                    // number (px) or a \"1.5em\" / \"18pt\" string\n\t *     fillStyle: \"#ffffff\",\n\t *     strokeStyle: \"#202020\",\n\t *     lineWidth: 2,                // outline the glyphs\n\t *     textAlign: \"left\",\n\t *     textBaseline: \"top\",\n\t *     wordWrapWidth: 200,          // wrap lines longer than 200px\n\t *     text: \"Hello melonJS!\\nStyled, wrapped, multi-line text.\",\n\t * });\n\t * label.bold();                    // bold() / italic() are chainable\n\t * label.setOpacity(0.8);           // per-object transparency\n\t * app.world.addChild(label);\n\t * @example\n\t * // a web font (loaded via the fontface loader) is used by its family name\n\t * loader.preload(\n\t *     [{ name: \"kenpixel\", type: \"fontface\", src: \"data/font/kenvector.woff2\" }],\n\t *     () => {\n\t *         app.world.addChild(\n\t *             new Text(0, 0, { font: \"kenpixel\", size: 32, text: \"Web font\" }),\n\t *         );\n\t *     },\n\t * );\n\t */\n\tconstructor(x, y, settings) {\n\t\t// call the parent constructor\n\t\tsuper(x, y, settings.width || 0, settings.height || 0);\n\n\t\t/**\n\t\t * defines the color used to draw the font.\n\t\t * @type {Color}\n\t\t * @default black\n\t\t */\n\t\tthis.fillStyle = colorPool.get(0, 0, 0);\n\n\t\t/**\n\t\t * defines the color used to draw the font stroke.<br>\n\t\t * @type {Color}\n\t\t * @default black\n\t\t */\n\t\tthis.strokeStyle = colorPool.get(0, 0, 0);\n\n\t\t/**\n\t\t * sets the current line width, in pixels, when drawing stroke\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.lineWidth = 0;\n\n\t\t/**\n\t\t * Set the default text alignment (or justification),<br>\n\t\t * possible values are \"left\", \"right\", and \"center\".<br>\n\t\t * @type {string}\n\t\t * @default \"left\"\n\t\t */\n\t\tthis.textAlign = \"left\";\n\n\t\t/**\n\t\t * Set the text baseline (e.g. the Y-coordinate for the draw operation), <br>\n\t\t * possible values are \"top\", \"hanging\", \"middle\", \"alphabetic\", \"ideographic\", \"bottom\"<br>\n\t\t * @type {string}\n\t\t * @default \"top\"\n\t\t */\n\t\tthis.textBaseline = \"top\";\n\n\t\t/**\n\t\t * Set the line spacing height (when displaying multi-line strings). <br>\n\t\t * Current font height will be multiplied with this value to set the line height.\n\t\t * @type {number}\n\t\t * @default 1.0\n\t\t */\n\t\tthis.lineHeight = 1.0;\n\n\t\t/**\n\t\t * the maximum length in CSS pixel for a single segment of text.\n\t\t * (use -1 to disable word wrapping)\n\t\t * @type {number}\n\t\t * @default -1\n\t\t */\n\t\tthis.wordWrapWidth = -1;\n\n\t\t/**\n\t\t * the font size (in px)\n\t\t * @type {number}\n\t\t * @default 10\n\t\t */\n\t\tthis.fontSize = 10;\n\n\t\t/**\n\t\t * the text to be displayed\n\t\t * @private\n\t\t */\n\t\tthis._text = [];\n\n\t\t// initialize the object based on the given settings\n\t\tthis.onResetEvent(x, y, settings);\n\t}\n\n\t/** @ignore */\n\tonResetEvent(x, y, settings) {\n\t\tif (typeof this.fillStyle === \"undefined\") {\n\t\t\tthis.fillStyle = colorPool.get(0, 0, 0);\n\t\t}\n\n\t\tif (typeof this.strokeStyle === \"undefined\") {\n\t\t\tthis.strokeStyle = colorPool.get(0, 0, 0);\n\t\t}\n\n\t\tif (typeof settings.fillStyle !== \"undefined\") {\n\t\t\tif (settings.fillStyle instanceof Color) {\n\t\t\t\tthis.fillStyle.copy(settings.fillStyle);\n\t\t\t} else {\n\t\t\t\t// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)\n\t\t\t\tthis.fillStyle.parseCSS(settings.fillStyle);\n\t\t\t}\n\t\t}\n\n\t\tif (typeof settings.strokeStyle !== \"undefined\") {\n\t\t\tif (settings.strokeStyle instanceof Color) {\n\t\t\t\tthis.strokeStyle.copy(settings.strokeStyle);\n\t\t\t} else {\n\t\t\t\t// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)\n\t\t\t\tthis.strokeStyle.parseCSS(settings.strokeStyle);\n\t\t\t}\n\t\t}\n\n\t\tthis.lineWidth = settings.lineWidth || 0;\n\t\tthis.textAlign = settings.textAlign || \"left\";\n\t\tthis.textBaseline = settings.textBaseline || \"top\";\n\t\tthis.lineHeight = settings.lineHeight || 1.0;\n\t\tthis.wordWrapWidth = settings.wordWrapWidth || -1;\n\t\tthis.fontSize = 10;\n\n\t\t// anchor point\n\t\tif (typeof settings.anchorPoint !== \"undefined\") {\n\t\t\tconst anchor = resolveAnchorPoint(settings.anchorPoint, \"Text\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t});\n\t\t\tthis.anchorPoint.set(anchor.x, anchor.y);\n\t\t} else {\n\t\t\tthis.anchorPoint.set(0, 0);\n\t\t}\n\n\t\t// if floating was specified through settings\n\t\tif (typeof settings.floating !== \"undefined\") {\n\t\t\tthis.floating = !!settings.floating;\n\t\t}\n\n\t\t// font name and type\n\t\tthis.setFont(settings.font, settings.size);\n\n\t\t// additional font styles\n\t\tif (settings.bold === true) {\n\t\t\tthis.bold();\n\t\t}\n\t\tif (settings.italic === true) {\n\t\t\tthis.italic();\n\t\t}\n\n\t\t// the canvas Texture used to render this text\n\t\t// offscreenCanvas is currently disabled for text rendering due to issue in WebGL mode\n\t\t// see https://github.com/melonjs/melonJS/issues/1180\n\t\tthis.canvasTexture = new CanvasRenderTarget(2, 2, {\n\t\t\toffscreenCanvas: false,\n\t\t});\n\n\t\t/**\n\t\t * @ignore\n\t\t */\n\t\tthis._visibleCharacters = -1;\n\n\t\t// instance to text metrics functions\n\t\tthis.metrics = new TextMetrics(this);\n\n\t\t// set the text\n\t\tthis.setText(settings.text);\n\t}\n\n\t/**\n\t * make the font bold\n\t * @returns {Text} this object for chaining\n\t */\n\tbold() {\n\t\tif (\n\t\t\t!this.font.startsWith(\"bold \") &&\n\t\t\t!this.font.startsWith(\"italic bold \")\n\t\t) {\n\t\t\tthis.font = \"bold \" + this.font;\n\t\t\tthis.isDirty = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * make the font italic\n\t * @returns {Text} this object for chaining\n\t */\n\titalic() {\n\t\tif (\n\t\t\t!this.font.startsWith(\"italic \") &&\n\t\t\t!this.font.startsWith(\"bold italic \")\n\t\t) {\n\t\t\tthis.font = \"italic \" + this.font;\n\t\t\tthis.isDirty = true;\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * set the font family and size\n\t * @param {string} font - a CSS font name\n\t * @param {number|string} [size=10] - size in px, or size + suffix (px, em, pt)\n\t * @returns {Text} this object for chaining\n\t * @example\n\t * font.setFont(\"Arial\", 20);\n\t * font.setFont(\"Arial\", \"1.5em\");\n\t */\n\tsetFont(font, size = 10) {\n\t\t// font name and type\n\t\tconst font_names = font.split(\",\").map((value) => {\n\t\t\tvalue = value.trim();\n\t\t\t// leave already-quoted names and CSS generic families untouched\n\t\t\tif (\n\t\t\t\t/(^\".*\"$)|(^'.*'$)/.test(value) ||\n\t\t\t\tgenericFontFamilies.has(value.toLowerCase())\n\t\t\t) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\treturn `\"${value}\"`;\n\t\t});\n\n\t\t// font size\n\t\tif (typeof size === \"number\") {\n\t\t\tthis.fontSize = size;\n\t\t\tsize += \"px\";\n\t\t} /* string */ else {\n\t\t\t// extract the units and convert if necessary\n\t\t\tconst CSSval = size.match(/([-+]?[\\d.]*)(.*)/);\n\t\t\tthis.fontSize = parseFloat(CSSval[1]);\n\t\t\tif (CSSval[2]) {\n\t\t\t\tthis.fontSize *= toPX[runits.indexOf(CSSval[2])];\n\t\t\t} else {\n\t\t\t\t// no unit define, assume px\n\t\t\t\tsize += \"px\";\n\t\t\t}\n\t\t}\n\t\tthis.height = this.fontSize;\n\t\tthis.font = size + \" \" + font_names.join(\",\");\n\n\t\tthis.isDirty = true;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * change the text to be displayed\n\t * @param {number|string|string[]} value - a string, or an array of strings\n\t * @returns {Text} this object for chaining\n\t */\n\tsetText(value = \"\") {\n\t\tconst bounds = this.getBounds();\n\n\t\t// set the next text\n\t\tif (this._text.toString() !== value.toString()) {\n\t\t\tif (!Array.isArray(value)) {\n\t\t\t\tthis._text = (\"\" + value).split(\"\\n\");\n\t\t\t} else {\n\t\t\t\t// copy — storing the caller's array by reference would let\n\t\t\t\t// destroy() (`_text.length = 0`) wipe it, and external\n\t\t\t\t// mutation would change the rendered text without isDirty\n\t\t\t\tthis._text = value.slice();\n\t\t\t}\n\t\t}\n\n\t\t// word wrap if necessary\n\t\tif (this._text.length > 0 && this.wordWrapWidth > 0) {\n\t\t\tthis._text = this.metrics.wordWrap(\n\t\t\t\tthis._text,\n\t\t\t\tthis.wordWrapWidth,\n\t\t\t\tthis.canvasTexture.context,\n\t\t\t);\n\t\t}\n\n\t\t// calculcate the text size and update the bounds accordingly\n\t\tbounds.addBounds(\n\t\t\tthis.metrics.measureText(this._text, this.canvasTexture.context),\n\t\t\ttrue,\n\t\t);\n\n\t\t// Quantize the offscreen canvas size to 32-pixel buckets: small\n\t\t// metric changes (a score ticking, typewriter text) land on the\n\t\t// SAME canvas dimensions, so the re-bake stays a same-size texture\n\t\t// update — the cheap path on every backend (a size change means\n\t\t// respecifying GL storage / retiring the WebGPU texture). Coarser\n\t\t// than exact sizing on purpose (hysteresis), far tighter than the\n\t\t// old power-of-two rounding (waste is bounded at 31px per axis\n\t\t// instead of up to 2× each).\n\t\tconst width = Math.ceil(this.metrics.width / 32) * 32;\n\t\tconst height = Math.ceil(this.metrics.height / 32) * 32;\n\n\t\t// invalidate the texture\n\t\tconst renderer = this.parentApp?.renderer ?? game.renderer;\n\t\tthis.canvasTexture.invalidate(renderer);\n\n\t\t// resize the cache canvas if necessary\n\t\tif (\n\t\t\tthis.canvasTexture.width < width ||\n\t\t\tthis.canvasTexture.height < height\n\t\t) {\n\t\t\tthis.canvasTexture.resize(width, height);\n\t\t}\n\n\t\tthis.canvasTexture.clear();\n\t\tthis._drawFont(\n\t\t\tthis.canvasTexture.context,\n\t\t\tthis._text,\n\t\t\tthis.pos.x - this.metrics.x,\n\t\t\tthis.pos.y - this.metrics.y,\n\t\t);\n\n\t\tthis.isDirty = true;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * the number of characters to display (use -1 to show all).\n\t * Useful for typewriter effects combined with Tween.\n\t * @public\n\t * @type {number}\n\t * @default -1\n\t * @see Text#visibleRatio\n\t * @example\n\t * // typewriter effect\n\t * text.visibleCharacters = 0;\n\t * new Tween(text).to({ visibleRatio: 1.0 }, { duration: 2000 }).start();\n\t */\n\tget visibleCharacters() {\n\t\treturn this._visibleCharacters;\n\t}\n\n\tset visibleCharacters(value) {\n\t\tif (this._visibleCharacters !== value) {\n\t\t\tthis._visibleCharacters = value;\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * the ratio of visible characters (0.0 to 1.0).\n\t * Setting this automatically updates {@link visibleCharacters}.\n\t * @public\n\t * @type {number}\n\t */\n\tget visibleRatio() {\n\t\tif (this._visibleCharacters === -1) {\n\t\t\treturn 1.0;\n\t\t}\n\t\tconst total = this._text.reduce((sum, line) => {\n\t\t\treturn sum + line.length;\n\t\t}, 0);\n\t\treturn total > 0 ? this._visibleCharacters / total : 1.0;\n\t}\n\n\tset visibleRatio(value) {\n\t\tconst clamped = Math.max(0, Math.min(1, isFinite(value) ? value : 1));\n\t\tif (clamped >= 1.0) {\n\t\t\tthis.visibleCharacters = -1;\n\t\t} else {\n\t\t\tconst total = this._text.reduce((sum, line) => {\n\t\t\t\treturn sum + line.length;\n\t\t\t}, 0);\n\t\t\tthis.visibleCharacters = Math.floor(clamped * total);\n\t\t}\n\t}\n\n\t/**\n\t * update the bounding box for this Text, accounting for textAlign and textBaseline.\n\t * @param {boolean} [absolute=true] - update in absolute coordinates\n\t * @returns {Bounds} this renderable's bounding box\n\t */\n\tupdateBounds(absolute = true) {\n\t\tif (typeof this.metrics !== \"undefined\" && this._text.length > 0) {\n\t\t\tconst bounds = this.getBounds();\n\t\t\tbounds.clear();\n\n\t\t\tconst w = this.metrics.width;\n\t\t\tconst h = this.metrics.height;\n\n\t\t\t// compute x offset based on textAlign\n\t\t\tlet ax = 0;\n\t\t\tswitch (this.textAlign) {\n\t\t\t\tcase \"right\":\n\t\t\t\t\tax = w;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"center\":\n\t\t\t\t\tax = w / 2;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// compute y offset based on textBaseline\n\t\t\tlet ay = 0;\n\t\t\tswitch (this.textBaseline) {\n\t\t\t\tcase \"middle\":\n\t\t\t\t\tay = h / 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ideographic\":\n\t\t\t\tcase \"alphabetic\":\n\t\t\t\tcase \"bottom\":\n\t\t\t\t\tay = h;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbounds.addFrame(-ax, -ay, w - ax, h - ay);\n\n\t\t\tif (absolute === true) {\n\t\t\t\tconst absPos = this.getAbsolutePosition();\n\t\t\t\tbounds.centerOn(\n\t\t\t\t\tabsPos.x + bounds.x + bounds.width / 2,\n\t\t\t\t\tabsPos.y + bounds.y + bounds.height / 2,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn bounds;\n\t\t}\n\t\treturn super.updateBounds(absolute);\n\t}\n\n\t/**\n\t * measure the given text size in pixels\n\t * @param {string} [text] - the text to be measured\n\t * @returns {TextMetrics} a TextMetrics object defining the dimensions of the given piece of text\n\t */\n\tmeasureText(text = this._text) {\n\t\treturn this.metrics.measureText(text, this.canvasTexture.context);\n\t}\n\n\t/**\n\t * draw a text at the specified coord\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - Reference to the destination renderer instance\n\t */\n\tdraw(renderer) {\n\t\t// re-render the canvas texture when dirty (e.g. visibleCharacters changed)\n\t\tif (this.isDirty) {\n\t\t\tthis.canvasTexture.invalidate(renderer);\n\t\t\tthis.canvasTexture.clear();\n\t\t\tthis._drawFont(\n\t\t\t\tthis.canvasTexture.context,\n\t\t\t\tthis._text,\n\t\t\t\tthis.pos.x - this.metrics.x,\n\t\t\t\tthis.pos.y - this.metrics.y,\n\t\t\t);\n\t\t}\n\n\t\t// adjust x,y position based on the bounding box\n\t\tlet x = this.metrics.x;\n\t\tlet y = this.metrics.y;\n\n\t\t// clamp to pixel grid if required\n\t\tif (renderer.settings.subPixel === false) {\n\t\t\tx = ~~x;\n\t\t\ty = ~~y;\n\t\t}\n\n\t\t// draw the text\n\t\trenderer.drawImage(this.canvasTexture.canvas, x, y);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\t_drawFont(context, text, x, y) {\n\t\tsetContextStyle(context, this);\n\n\t\tlet remaining = this.visibleCharacters;\n\n\t\tfor (let i = 0; i < text.length; i++) {\n\t\t\tlet string = text[i].trimEnd();\n\n\t\t\t// limit visible characters if needed\n\t\t\tif (remaining !== -1) {\n\t\t\t\tif (remaining <= 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstring = string.substring(0, remaining);\n\t\t\t\tremaining -= string.length;\n\t\t\t}\n\n\t\t\t// draw the string\n\t\t\tif (this.fillStyle.alpha > 0) {\n\t\t\t\tcontext.fillText(string, x, y);\n\t\t\t}\n\t\t\t// stroke the text\n\t\t\tif (this.lineWidth > 0 && this.strokeStyle.alpha > 0) {\n\t\t\t\tcontext.strokeText(string, x, y);\n\t\t\t}\n\t\t\t// add leading space\n\t\t\ty += this.metrics.lineHeight();\n\t\t}\n\t\treturn this.metrics;\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tconst renderer = this.parentApp?.renderer ?? game.renderer;\n\t\tthis.canvasTexture.destroy(renderer);\n\t\tthis.canvasTexture = undefined;\n\t\tcolorPool.release(this.fillStyle);\n\t\tcolorPool.release(this.strokeStyle);\n\t\tthis.fillStyle = this.strokeStyle = undefined;\n\t\tthis.metrics = undefined;\n\t\tthis._text.length = 0;\n\t\tsuper.destroy();\n\t}\n}\n","import { Bounds } from \"../../physics/bounds.ts\";\nimport Text from \"./text.js\";\nimport setContextStyle from \"./textstyle.js\";\n\n/**\n * a Text Metrics object that contains helper for text manipulation\n */\nclass TextMetrics extends Bounds {\n\t/**\n\t * @param {Text|BitmapText} ancestor - the parent object that contains this TextMetrics object\n\t */\n\tconstructor(ancestor) {\n\t\t// parent constructor\n\t\tsuper();\n\n\t\t/**\n\t\t * a reference to the parent object that contains this TextMetrics object\n\t\t * @type {Renderable}\n\t\t * @default undefined\n\t\t */\n\t\tthis.ancestor = ancestor;\n\n\t\tthis.setMinMax(0, 0, 0, 0);\n\t}\n\n\t/**\n\t * Returns the height of a segment of inline text in CSS pixels.\n\t * @returns {number} the height of a segment of inline text in CSS pixels.\n\t */\n\tlineHeight() {\n\t\tif (this.ancestor instanceof Text) {\n\t\t\treturn this.ancestor.fontSize * this.ancestor.lineHeight;\n\t\t} else {\n\t\t\t// it's a BitmapText\n\t\t\treturn (\n\t\t\t\tthis.ancestor.fontData.capHeight *\n\t\t\t\tthis.ancestor.lineHeight *\n\t\t\t\tthis.ancestor.fontScale.y\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the width of the given segment of inline text in CSS pixels.\n\t * @param {string} text - the text to be measured\n\t * @param {CanvasRenderingContext2D} [context] - reference to an active 2d context for canvas rendering\n\t * @returns {number} the width of the given segment of inline text in CSS pixels.\n\t */\n\tlineWidth(text, context) {\n\t\tif (this.ancestor instanceof Text) {\n\t\t\treturn context.measureText(text).width;\n\t\t} else {\n\t\t\t// it's a BitmapText\n\t\t\tconst characters = text.split(\"\");\n\t\t\tconst charactersLength = characters.length;\n\t\t\tlet width = 0;\n\t\t\tlet lastGlyph = null;\n\t\t\tfor (let i = 0; i < charactersLength; i++) {\n\t\t\t\tconst ch = characters[i].charCodeAt(0);\n\t\t\t\tconst glyph = this.ancestor.fontData.glyphs[ch];\n\t\t\t\tif (typeof glyph !== \"undefined\") {\n\t\t\t\t\tconst kerning =\n\t\t\t\t\t\tlastGlyph && lastGlyph.kerning ? lastGlyph.getKerning(ch) : 0;\n\t\t\t\t\t// for the last glyph, use the visual extent if it exceeds xadvance\n\t\t\t\t\tconst advance =\n\t\t\t\t\t\ti < charactersLength - 1\n\t\t\t\t\t\t\t? glyph.xadvance\n\t\t\t\t\t\t\t: Math.max(glyph.xadvance, glyph.xoffset + glyph.width);\n\t\t\t\t\twidth += (advance + kerning) * this.ancestor.fontScale.x;\n\t\t\t\t\tlastGlyph = glyph;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn width;\n\t\t}\n\t}\n\n\t/**\n\t * measure the given text size in CSS pixels\n\t * @param {string} text - the text to be measured\n\t * @param {CanvasRenderingContext2D} [context] - reference to an active 2d context for canvas rendering\n\t * @returns {TextMetrics} this\n\t */\n\tmeasureText(text, context) {\n\t\tlet strings;\n\n\t\tif (!Array.isArray(text)) {\n\t\t\tstrings = (\"\" + text).split(\"\\n\");\n\t\t} else {\n\t\t\tstrings = text;\n\t\t}\n\n\t\tif (typeof context !== \"undefined\") {\n\t\t\t// save the previous context\n\t\t\tcontext.save();\n\n\t\t\t// apply the style font\n\t\t\tsetContextStyle(context, this.ancestor);\n\t\t}\n\n\t\t// compute the bounding box size\n\t\tthis.width = this.height = 0;\n\n\t\tconst isBitmapText = !(this.ancestor instanceof Text);\n\n\t\tfor (let i = 0; i < strings.length; i++) {\n\t\t\tthis.width = Math.max(\n\t\t\t\tthis.lineWidth(strings[i].trimEnd(), context),\n\t\t\t\tthis.width,\n\t\t\t);\n\t\t\tthis.height += this.lineHeight();\n\t\t}\n\n\t\t// glyph vertical extents (BitmapText only) — use font-wide\n\t\t// precomputed values from BitmapTextData instead of per-line iteration\n\t\tthis.glyphYOffset = 0;\n\t\tthis.glyphMaxBottom = 0;\n\n\t\tif (isBitmapText && strings.length > 0) {\n\t\t\tconst fontData = this.ancestor.fontData;\n\t\t\tconst scaleY = this.ancestor.fontScale.y;\n\t\t\tconst visualLineH =\n\t\t\t\t(fontData.glyphMaxBottom - fontData.glyphMinTop) * scaleY;\n\t\t\t// replace the last capHeight-based line with the actual visual height\n\t\t\tthis.height += visualLineH - this.lineHeight();\n\t\t\tthis.glyphYOffset = fontData.glyphMinTop * scaleY;\n\t\t\tthis.glyphMaxBottom = fontData.glyphMaxBottom * scaleY;\n\t\t}\n\n\t\tthis.width = Math.ceil(this.width);\n\t\tthis.height = Math.ceil(this.height);\n\n\t\t// compute the bounding box position\n\t\tthis.x = Math.floor(\n\t\t\tthis.ancestor.textAlign === \"right\"\n\t\t\t\t? this.ancestor.pos.x - this.width\n\t\t\t\t: this.ancestor.textAlign === \"center\"\n\t\t\t\t\t? this.ancestor.pos.x - this.width / 2\n\t\t\t\t\t: this.ancestor.pos.x,\n\t\t);\n\t\tthis.y = Math.floor(\n\t\t\tthis.ancestor.textBaseline.search(/^(top|hanging)$/) === 0\n\t\t\t\t? this.ancestor.pos.y\n\t\t\t\t: this.ancestor.textBaseline === \"middle\"\n\t\t\t\t\t? this.ancestor.pos.y - this.lineHeight() / 2\n\t\t\t\t\t: this.ancestor.pos.y - this.lineHeight(),\n\t\t);\n\n\t\tif (typeof context !== \"undefined\") {\n\t\t\t// restore the context\n\t\t\tcontext.restore();\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * wrap the given text based on the given width\n\t * @param {string|string[]} text - the text to be wrapped\n\t * @param {number} width - maximum width of one segment of text in css pixel\n\t * @param {CanvasRenderingContext2D} [context] - reference to an active 2d context for canvas rendering\n\t * @returns {string[]} an array of string representing wrapped text\n\t */\n\twordWrap(text, width, context) {\n\t\tlet currentLine = \"\";\n\t\tconst output = [];\n\n\t\tif (Array.isArray(text)) {\n\t\t\t// join into a single string\n\t\t\ttext = text.join(\" \");\n\t\t}\n\t\t// word splitting to be improved as it replaces \\n by space if present\n\t\tconst words = text.replace(/[\\r\\n]+/g, \" \").split(\" \");\n\n\t\tif (typeof context !== \"undefined\") {\n\t\t\t// save the previous context\n\t\t\tcontext.save();\n\n\t\t\t// apply the style font\n\t\t\tsetContextStyle(context, this.ancestor);\n\t\t}\n\n\t\tfor (let i = 0; i < words.length; i++) {\n\t\t\tconst word = words[i];\n\t\t\tconst lineWidth = this.lineWidth(currentLine + word + \" \", context);\n\t\t\tif (lineWidth < width) {\n\t\t\t\t// add the word to the current line\n\t\t\t\tcurrentLine += word + \" \";\n\t\t\t} else {\n\t\t\t\toutput.push(currentLine + \"\\n\");\n\t\t\t\tcurrentLine = word + \" \";\n\t\t\t}\n\t\t}\n\t\t// last line\n\t\toutput.push(currentLine);\n\n\t\tif (typeof context !== \"undefined\") {\n\t\t\t// restore the context\n\t\t\tcontext.restore();\n\t\t}\n\n\t\treturn output;\n\t}\n}\nexport default TextMetrics;\n","import { getBinary, getImage } from \"../../loader/loader.js\";\nimport { Color } from \"../../math/color.ts\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport { resolveAnchorPoint } from \"../anchorPoint.ts\";\nimport Renderable from \"../renderable.js\";\nimport { bitmapTextDataPool } from \"./bitmaptextdata.ts\";\nimport TextMetrics from \"./textmetrics.js\";\n\n/**\n * a bitmap font object.\n *\n * The font descriptor uses the AngelCode BMFont format and may be supplied in\n * either flavour — the **text** (`.fnt`) or the **XML** form — the\n * serialisation is auto-detected, so packs that ship an `.xml` descriptor load\n * as-is, with no conversion step. Load the descriptor as a `binary` asset and\n * its page image as an `image` asset, then reference both by the same name.\n * @category Text\n */\nexport default class BitmapText extends Renderable {\n\t/**\n\t * @param {number} x - position of the text object\n\t * @param {number} y - position of the text object\n\t * @param {object} settings - the text configuration\n\t * @param {string|Image} settings.font - a font name to identify the corresponding source image\n\t * @param {string} [settings.fontData=settings.font] - the bitmap font data corresponding name, or the bitmap font data itself (AngelCode BMFont, `.fnt` text or `.xml`)\n\t * @param {number} [settings.size] - size a scaling ratio\n\t * @param {Color|string} [settings.fillStyle] - a CSS color value used to tint the bitmapText (@see BitmapText.tint)\n\t * @param {number} [settings.lineWidth=1] - line width, in pixels, when drawing stroke\n\t * @param {string} [settings.textAlign=\"left\"] - horizontal text alignment\n\t * @param {string} [settings.textBaseline=\"top\"] - the text baseline\n\t * @param {number} [settings.lineHeight=1.0] - line spacing height\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0.0, y:0.0}] - anchor point to draw the text at. Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`.\n\t * @param {number} [settings.wordWrapWidth] - the maximum length in CSS pixel for a single segment of text\n\t * @param {(string|string[])} [settings.text] - a string, or an array of strings\n\t * @example\n\t * // Load the BMFont descriptor as a \"binary\" asset and its page as an \"image\".\n\t * // Both the text (.fnt) and XML (.xml) BMFont flavours are accepted and\n\t * // auto-detected, so an .xml descriptor can be used directly:\n\t * loader.preload([\n\t *     // text (.fnt) BMFont\n\t *     { name: \"arial\", type: \"binary\", src: \"data/font/arial.fnt\" },\n\t *     { name: \"arial\", type: \"image\",  src: \"data/font/arial.png\" },\n\t *     // XML BMFont (exported by many bitmap-font tools) — loaded as-is\n\t *     { name: \"pixel\", type: \"binary\", src: \"data/font/pixel.xml\" },\n\t *     { name: \"pixel\", type: \"image\",  src: \"data/font/pixel.png\" },\n\t * ])\n\t * // Then create an instance of your bitmap font:\n\t * let myFont = new BitmapText(x, y, { font: \"arial\", text: \"Hello\" });\n\t * // add it to the world container\n\t * app.world.addChild(myFont);\n\t */\n\tconstructor(x, y, settings) {\n\t\t// call the parent constructor\n\t\tsuper(x, y, settings.width || 0, settings.height || 0);\n\n\t\t/**\n\t\t * Set the default text alignment (or justification),<br>\n\t\t * possible values are \"left\", \"right\", and \"center\".\n\t\t * @public\n\t\t * @type {string}\n\t\t * @default \"left\"\n\t\t */\n\t\tthis.textAlign = settings.textAlign || \"left\";\n\n\t\t/**\n\t\t * Set the text baseline (e.g. the Y-coordinate for the draw operation), <br>\n\t\t * possible values are \"top\", \"hanging\", \"middle\", \"alphabetic\", \"ideographic\", \"bottom\"<br>\n\t\t * @public\n\t\t * @type {string}\n\t\t * @default \"top\"\n\t\t */\n\t\tthis.textBaseline = settings.textBaseline || \"top\";\n\n\t\t/**\n\t\t * Set the line spacing height (when displaying multi-line strings). <br>\n\t\t * Current font height will be multiplied with this value to set the line height.\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default 1.0\n\t\t */\n\t\tthis.lineHeight = settings.lineHeight || 1.0;\n\n\t\t/**\n\t\t * the maximum length in CSS pixel for a single segment of text.\n\t\t * (use -1 to disable word wrapping)\n\t\t * @public\n\t\t * @type {number}\n\t\t * @default -1\n\t\t */\n\t\tthis.wordWrapWidth = settings.wordWrapWidth || -1;\n\n\t\t/**\n\t\t * the text to be displayed\n\t\t * @private\n\t\t */\n\t\tthis._text = [];\n\n\t\t/**\n\t\t * scaled font size\n\t\t * @private\n\t\t */\n\t\tthis.fontScale = vector2dPool.get(1.0, 1.0);\n\n\t\t/**\n\t\t * font image\n\t\t * @private\n\t\t */\n\t\tthis.fontImage =\n\t\t\ttypeof settings.font === \"object\"\n\t\t\t\t? settings.font\n\t\t\t\t: getImage(settings.font);\n\n\t\tif (typeof settings.fontData !== \"string\") {\n\t\t\t/**\n\t\t\t * font data\n\t\t\t * @private\n\t\t\t */\n\t\t\t// use settings.font to retrieve the data from the loader\n\t\t\tthis.fontData = bitmapTextDataPool.get(getBinary(settings.font));\n\t\t} else {\n\t\t\tthis.fontData = bitmapTextDataPool.get(\n\t\t\t\t// if starting/includes \"info face\" the whole data string was passed as parameter\n\t\t\t\tsettings.fontData.includes(\"info face\")\n\t\t\t\t\t? settings.fontData\n\t\t\t\t\t: getBinary(settings.fontData),\n\t\t\t);\n\t\t}\n\n\t\t// if floating was specified through settings\n\t\tif (typeof settings.floating !== \"undefined\") {\n\t\t\tthis.floating = !!settings.floating;\n\t\t}\n\n\t\t// apply given fillstyle\n\t\tif (typeof settings.fillStyle !== \"undefined\") {\n\t\t\tthis.fillStyle = settings.fillStyle;\n\t\t}\n\n\t\t// update anchorPoint if provided\n\t\tif (typeof settings.anchorPoint !== \"undefined\") {\n\t\t\tconst anchor = resolveAnchorPoint(settings.anchorPoint, \"BitmapText\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t});\n\t\t\tthis.anchorPoint.set(anchor.x, anchor.y);\n\t\t} else {\n\t\t\tthis.anchorPoint.set(0, 0);\n\t\t}\n\n\t\t/**\n\t\t * @ignore\n\t\t */\n\t\tthis._visibleCharacters = -1;\n\n\t\t// instance to text metrics functions\n\t\tthis.metrics = new TextMetrics(this);\n\n\t\t// resize if necessary\n\t\tif (typeof settings.size === \"number\" && settings.size !== 1.0) {\n\t\t\tthis.resize(settings.size);\n\t\t}\n\n\t\t// set the text\n\t\tthis.setText(settings.text);\n\t}\n\n\t/**\n\t * change the font settings\n\t * @param {string} textAlign - (\"left\", \"center\", \"right\")\n\t * @param {number} [scale]\n\t * @returns {BitmapText} this object for chaining\n\t */\n\tset(textAlign, scale) {\n\t\tthis.textAlign = textAlign;\n\t\t// updated scaled Size\n\t\tif (scale) {\n\t\t\tthis.resize(scale);\n\t\t}\n\t\tthis.isDirty = true;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * change the text to be displayed\n\t * @param {number|string|string[]} value - a string, or an array of strings\n\t * @returns {BitmapText} this object for chaining\n\t */\n\tsetText(value = \"\") {\n\t\tif (this._text.toString() !== value.toString()) {\n\t\t\tif (!Array.isArray(value)) {\n\t\t\t\tthis._text = (\"\" + value).split(\"\\n\");\n\t\t\t} else {\n\t\t\t\t// copy — storing the caller's array by reference would let\n\t\t\t\t// destroy() (`_text.length = 0`) wipe it, and external\n\t\t\t\t// mutation would change the rendered text without isDirty\n\t\t\t\tthis._text = value.slice();\n\t\t\t}\n\t\t\tthis.isDirty = true;\n\t\t}\n\n\t\tif (this._text.length > 0 && this.wordWrapWidth > 0) {\n\t\t\tthis._text = this.metrics.wordWrap(this._text, this.wordWrapWidth);\n\t\t}\n\n\t\t// measure text dimensions (cached for updateBounds)\n\t\tthis.metrics.measureText(this._text);\n\t\tthis.updateBounds();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * the number of characters to display (use -1 to show all).\n\t * Useful for typewriter effects combined with Tween.\n\t * @public\n\t * @type {number}\n\t * @default -1\n\t * @see BitmapText#visibleRatio\n\t * @example\n\t * // show only the first 5 characters\n\t * bitmapText.visibleCharacters = 5;\n\t * // typewriter effect\n\t * bitmapText.visibleCharacters = 0;\n\t * new Tween(bitmapText).to({ visibleRatio: 1.0 }, { duration: 2000 }).start();\n\t */\n\tget visibleCharacters() {\n\t\treturn this._visibleCharacters;\n\t}\n\n\tset visibleCharacters(value) {\n\t\tif (this._visibleCharacters !== value) {\n\t\t\tthis._visibleCharacters = value;\n\t\t\tthis.isDirty = true;\n\t\t}\n\t}\n\n\t/**\n\t * the ratio of visible characters (0.0 to 1.0).\n\t * Setting this automatically updates {@link visibleCharacters}.\n\t * @public\n\t * @type {number}\n\t */\n\tget visibleRatio() {\n\t\tif (this._visibleCharacters === -1) {\n\t\t\treturn 1.0;\n\t\t}\n\t\tconst total = this._text.reduce((sum, line) => {\n\t\t\treturn sum + line.length;\n\t\t}, 0);\n\t\treturn total > 0 ? this._visibleCharacters / total : 1.0;\n\t}\n\n\tset visibleRatio(value) {\n\t\tconst clamped = Math.max(0, Math.min(1, isFinite(value) ? value : 1));\n\t\tif (clamped >= 1.0) {\n\t\t\tthis.visibleCharacters = -1;\n\t\t} else {\n\t\t\tconst total = this._text.reduce((sum, line) => {\n\t\t\t\treturn sum + line.length;\n\t\t\t}, 0);\n\t\t\tthis.visibleCharacters = Math.floor(clamped * total);\n\t\t}\n\t}\n\n\t/**\n\t * update the bounding box for this Bitmap Text.\n\t * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates\n\t * @returns {Bounds} this Bitmap Text bounding box Rectangle object\n\t */\n\tupdateBounds(absolute = true) {\n\t\tconst bounds = this.getBounds();\n\n\t\tbounds.clear();\n\n\t\tif (typeof this.metrics !== \"undefined\") {\n\t\t\tconst w = this.metrics.width;\n\t\t\tconst h = this.metrics.height;\n\n\t\t\t// compute x offset based on textAlign\n\t\t\tlet ax = 0;\n\t\t\tswitch (this.textAlign) {\n\t\t\t\tcase \"right\":\n\t\t\t\t\tax = w;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"center\":\n\t\t\t\t\tax = w / 2;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// baseline shift based on total text height (matching draw code)\n\t\t\tconst gy = this.metrics.glyphYOffset || 0;\n\t\t\tlet ay = 0;\n\t\t\tswitch (this.textBaseline) {\n\t\t\t\tcase \"middle\":\n\t\t\t\t\tay = gy + h * 0.5;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ideographic\":\n\t\t\t\tcase \"alphabetic\":\n\t\t\t\tcase \"bottom\":\n\t\t\t\t\tay = gy + h;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbounds.addFrame(-ax, -ay + gy, w - ax, -ay + gy + h);\n\t\t}\n\n\t\tif (absolute === true) {\n\t\t\tconst absPos = this.getAbsolutePosition();\n\t\t\tbounds.centerOn(\n\t\t\t\tabsPos.x + bounds.x + bounds.width / 2,\n\t\t\t\tabsPos.y + bounds.y + bounds.height / 2,\n\t\t\t);\n\t\t}\n\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * defines the color used to tint the bitmap text\n\t * @public\n\t * @type {Color}\n\t * @see Renderable#tint\n\t */\n\tget fillStyle() {\n\t\treturn this.tint;\n\t}\n\tset fillStyle(value) {\n\t\tif (value instanceof Color) {\n\t\t\tthis.tint.copy(value);\n\t\t} else {\n\t\t\t// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)\n\t\t\tthis.tint.parseCSS(value);\n\t\t}\n\t}\n\n\t/**\n\t * change the font display size\n\t * @param {number} scale - ratio\n\t * @returns {BitmapText} this object for chaining\n\t */\n\tresize(scale) {\n\t\tthis.fontScale.set(scale, scale);\n\n\t\t// remeasure with new scale (cached for updateBounds)\n\t\tthis.metrics.measureText(this._text);\n\t\tthis.updateBounds();\n\n\t\tthis.isDirty = true;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * measure the given text size in pixels\n\t * @param {string} [text]\n\t * @returns {TextMetrics} a TextMetrics object with two properties: `width` and `height`, defining the output dimensions\n\t */\n\tmeasureText(text = this._text) {\n\t\treturn this.metrics.measureText(text);\n\t}\n\n\t/**\n\t * draw the bitmap font\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - Reference to the destination renderer instance\n\t */\n\tdraw(renderer) {\n\t\tlet x = this.pos.x;\n\t\tlet y = this.pos.y;\n\n\t\tconst lX = x;\n\t\tconst stringHeight = this.metrics.lineHeight();\n\t\tconst gy = this.metrics.glyphYOffset || 0;\n\t\tconst h = this.metrics.height;\n\n\t\t// apply baseline shift once for the entire text block\n\t\tswitch (this.textBaseline) {\n\t\t\tcase \"middle\":\n\t\t\t\ty -= gy + h * 0.5;\n\t\t\t\tbreak;\n\n\t\t\tcase \"ideographic\":\n\t\t\tcase \"alphabetic\":\n\t\t\tcase \"bottom\":\n\t\t\t\ty -= gy + h;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// running character counter for visibleCharacters\n\t\tlet charCount = 0;\n\t\tconst maxChars = this.visibleCharacters;\n\n\t\tfor (let i = 0; i < this._text.length; i++) {\n\t\t\tx = lX;\n\t\t\tconst string = this._text[i].trimEnd();\n\t\t\t// adjust x pos based on alignment value\n\t\t\tconst stringWidth = this.metrics.lineWidth(string);\n\t\t\tswitch (this.textAlign) {\n\t\t\t\tcase \"right\":\n\t\t\t\t\tx -= stringWidth;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"center\":\n\t\t\t\t\tx -= stringWidth * 0.5;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// draw the string\n\t\t\tlet lastGlyph = null;\n\t\t\tfor (let c = 0, len = string.length; c < len; c++) {\n\t\t\t\t// stop drawing when visibleCharacters limit is reached\n\t\t\t\tif (maxChars !== -1 && charCount >= maxChars) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// calculate the char index\n\t\t\t\tconst ch = string.charCodeAt(c);\n\t\t\t\tconst glyph = this.fontData.glyphs[ch];\n\n\t\t\t\tif (typeof glyph !== \"undefined\") {\n\t\t\t\t\tconst glyphWidth = glyph.width;\n\t\t\t\t\tconst glyphHeight = glyph.height;\n\t\t\t\t\tconst kerning =\n\t\t\t\t\t\tlastGlyph && lastGlyph.kerning ? lastGlyph.getKerning(ch) : 0;\n\t\t\t\t\tconst scaleX = this.fontScale.x;\n\t\t\t\t\tconst scaleY = this.fontScale.y;\n\n\t\t\t\t\t// draw it\n\t\t\t\t\tif (glyphWidth !== 0 && glyphHeight !== 0) {\n\t\t\t\t\t\t// some browser throw an exception when drawing a 0 width or height image\n\t\t\t\t\t\trenderer.drawImage(\n\t\t\t\t\t\t\tthis.fontImage,\n\t\t\t\t\t\t\tglyph.x,\n\t\t\t\t\t\t\tglyph.y,\n\t\t\t\t\t\t\tglyphWidth,\n\t\t\t\t\t\t\tglyphHeight,\n\t\t\t\t\t\t\tx + glyph.xoffset * scaleX,\n\t\t\t\t\t\t\ty + glyph.yoffset * scaleY,\n\t\t\t\t\t\t\tglyphWidth * scaleX,\n\t\t\t\t\t\t\tglyphHeight * scaleY,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// increment position\n\t\t\t\t\tx += (glyph.xadvance + kerning) * scaleX;\n\t\t\t\t\tlastGlyph = glyph;\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"BitmapText: no defined Glyph in for \" + String.fromCharCode(ch),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tcharCount++;\n\t\t\t}\n\t\t\t// increment line\n\t\t\ty += stringHeight;\n\t\t}\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tvector2dPool.release(this.fontScale);\n\t\tthis.fontScale = undefined;\n\t\tbitmapTextDataPool.release(this.fontData);\n\t\tthis.fontData = undefined;\n\t\tthis._text.length = 0;\n\t\tthis.metrics = undefined;\n\t\tsuper.destroy();\n\t}\n}\n","import BitmapText from \"../../../renderable/text/bitmaptext.js\";\nimport Text from \"../../../renderable/text/text.js\";\n\n/**\n * create a Text or BitmapText object from TMX settings\n * @param {object} settings - TMX object settings\n * @returns {Renderable} the created text object\n * @ignore\n */\nexport function createTextObject(settings) {\n\tif (typeof settings.text.anchorPoint === \"undefined\") {\n\t\tsettings.text.anchorPoint = settings.anchorPoint;\n\t}\n\tconst obj =\n\t\tsettings.text.bitmap === true\n\t\t\t? new BitmapText(settings.x, settings.y, settings.text)\n\t\t\t: new Text(settings.x, settings.y, settings.text);\n\tobj.pos.z = settings.z;\n\treturn obj;\n}\n","import { getDefaultShape } from \"../TMXObjectFactory.js\";\n\n/**\n * create a renderable from an embedded tile object\n * @param {object} settings - TMX object settings\n * @returns {Renderable} the created tile object\n * @ignore\n */\nexport function createTileObject(settings) {\n\tconst shape = getDefaultShape(settings);\n\tconst obj = settings.tile.getRenderable(settings);\n\t// declarative body — auto-registered with the active adapter when\n\t// the renderable is added to the world tree. `getDefaultShape` may\n\t// return either a single shape or an array (parseTMXShapes returns\n\t// an array for polygon/polyline/ellipse types), so normalize.\n\tobj.bodyDef = {\n\t\ttype: \"static\",\n\t\tshapes: Array.isArray(shape) ? shape : [shape],\n\t};\n\tobj.pos.setMuted(settings.x, settings.y, settings.z);\n\treturn obj;\n}\n","import { polygonPool } from \"../../geometries/polygon.ts\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport { setPoolRegisterCallback } from \"../../system/legacy_pool.js\";\nimport { createShapeObject } from \"./factories/shape.js\";\nimport { createTextObject } from \"./factories/text.js\";\nimport { createTileObject } from \"./factories/tile.js\";\nimport TMXLayer from \"./TMXLayer.js\";\n\n/**\n * registry of Tiled object factory functions\n * @ignore\n */\nconst factories = new Map();\n\n/**\n * tracks class constructors registered via registerTiledObjectClass\n * (used to detect duplicate registrations with different constructors)\n * @ignore\n */\nconst registeredClasses = new Map();\n\n/**\n * whether built-in factories have been registered\n * @ignore\n */\nlet factoriesInitialized = false;\n\n/**\n * Return a default shape (polygon) for the given dimensions,\n * or the existing shapes if already defined in settings.\n * @param {object} settings - TMX object settings\n * @returns {Polygon|object[]} shape(s) for the object body\n * @ignore\n */\nexport function getDefaultShape(settings) {\n\tif (typeof settings.shapes !== \"undefined\") {\n\t\treturn settings.shapes;\n\t}\n\treturn polygonPool.get(0, 0, [\n\t\tvector2dPool.get(0, 0),\n\t\tvector2dPool.get(settings.width, 0),\n\t\tvector2dPool.get(settings.width, settings.height),\n\t]);\n}\n\n/**\n * Detect the object type from its TMX settings.\n * The detection order is:\n * 1. TMXLayer instances → \"layer\" (passthrough)\n * 2. Object class matching a registered factory → class name\n * 3. Object name matching a registered factory → name\n * 4. Object with text data → \"text\"\n * 5. Object with tile/gid data → \"tile\"\n * 6. Everything else → \"shape\"\n * @param {object} settings - TMX object settings\n * @returns {string} the factory type key\n * @ignore\n */\nexport function detectObjectType(settings) {\n\tif (settings instanceof TMXLayer) {\n\t\treturn \"layer\";\n\t}\n\t// check if a factory is registered for this object's class\n\tif (typeof settings.class === \"string\" && factories.has(settings.class)) {\n\t\treturn settings.class;\n\t}\n\t// check if a factory is registered for this object's name\n\tif (typeof settings.name === \"string\" && factories.has(settings.name)) {\n\t\treturn settings.name;\n\t}\n\tif (typeof settings.text === \"object\") {\n\t\treturn \"text\";\n\t}\n\tif (typeof settings.tile === \"object\") {\n\t\treturn \"tile\";\n\t}\n\treturn \"shape\";\n}\n\n/**\n * Register a factory function for a given Tiled object type.\n * When a Tiled map is loaded, objects are matched to factories using the following\n * priority: object class → object name → structural type (\"text\", \"tile\", \"shape\").\n * <br><br>\n * Built-in structural types are: `\"text\"`, `\"tile\"`, `\"shape\"`.\n * Custom types are matched against the object's Tiled `class` or `name` property.\n * <br><br>\n * For simple cases where you just want to map a Tiled class to a constructor,\n * use {@link registerTiledObjectClass} instead.\n * @category Tilemap\n * @param {string} type - the object type key (built-in type or Tiled class/name)\n * @param {Function} factory - factory function with signature `(settings, map) => Renderable`\n * @example\n * // register a custom factory for \"Spine\" objects in Tiled\n * // (set the object class to \"Spine\" in Tiled, and add atlasFile/jsonFile custom properties)\n * registerTiledObjectFactory(\"Spine\", (settings, map) => {\n *     const obj = new Spine(settings.x, settings.y, settings);\n *     obj.pos.z = settings.z;\n *     return obj;\n * });\n *\n * @example\n * // override the built-in \"shape\" factory to add custom behavior\n * registerTiledObjectFactory(\"shape\", (settings, map) => {\n *     const obj = new Renderable(settings.x, settings.y, settings.width, settings.height);\n *     obj.pos.z = settings.z;\n *     // add custom initialization...\n *     return obj;\n * });\n */\nexport function registerTiledObjectFactory(type, factory) {\n\tif (typeof factory !== \"function\") {\n\t\tthrow new Error(\"invalid factory function for \" + type);\n\t}\n\n\tif (typeof factories.get(type) !== \"undefined\") {\n\t\tconsole.warn(\n\t\t\t\"melonJS: overriding Tiled object factory for \" + type + \" type\",\n\t\t);\n\t}\n\n\tfactories.set(type, factory);\n}\n\n/**\n * Register a class constructor as a Tiled object factory.\n * When an object with a matching `class` or `name` is found in a Tiled map,\n * an instance of the given constructor will be created with `new Constructor(x, y, settings)`.\n * <br><br>\n * This is a convenience wrapper around {@link registerTiledObjectFactory}.\n * If the same class is registered twice with the same constructor, the call is silently ignored.\n * If a different constructor is registered for the same name, an error is thrown.\n * <br><br>\n * Note: classes registered via {@link pool.register} are also automatically registered\n * as Tiled object factories (unless {@link pool.autoRegisterTiled} is set to `false`).\n * @category Tilemap\n * @param {string} name - the Tiled class or object name to match\n * @param {Function} Constructor - class constructor with signature `(x, y, settings)`\n * @example\n * // register a custom enemy class for use in Tiled maps\n * // In Tiled: set the object class to \"Enemy\" and add custom properties\n * registerTiledObjectClass(\"Enemy\", Enemy);\n *\n * @example\n * // equivalent to pool.register (which auto-registers for Tiled too)\n * pool.register(\"CoinEntity\", CoinEntity, true);\n * // CoinEntity is now available both in the pool AND as a Tiled object factory\n */\nexport function registerTiledObjectClass(name, Constructor) {\n\tconst existing = registeredClasses.get(name);\n\tif (typeof existing !== \"undefined\") {\n\t\tif (existing === Constructor) {\n\t\t\t// same class already registered — no-op\n\t\t\treturn;\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"a different class is already registered for Tiled type: \" + name,\n\t\t);\n\t}\n\tregisteredClasses.set(name, Constructor);\n\tregisterTiledObjectFactory(name, (settings) => {\n\t\tconst obj = new Constructor(settings.x, settings.y, settings);\n\t\tobj.pos.z = settings.z;\n\t\treturn obj;\n\t});\n}\n\n/**\n * pending class registrations queued before initFactories runs\n * @ignore\n */\nconst pendingClasses = [];\n\n/**\n * Queue a class for registration as a Tiled object factory.\n * Registrations are applied when initFactories() runs (on first\n * createTMXObject call), avoiding circular import issues at module load time.\n * @param {string} name - the Tiled class or name to match\n * @param {Function} Constructor - class constructor with signature (x, y, settings)\n * @ignore\n */\nexport function registerBuiltinTiledClass(name, Constructor) {\n\tif (factoriesInitialized) {\n\t\t// already initialized, register immediately\n\t\tregisterTiledObjectClass(name, Constructor);\n\t} else {\n\t\tpendingClasses.push([name, Constructor]);\n\t}\n}\n\n/**\n * Register built-in factories and apply pending class registrations.\n * Called lazily on first createTMXObject call, after all modules are fully loaded.\n * @ignore\n */\nfunction initFactories() {\n\t// only register built-in structural factories if not already overridden\n\tif (!factories.has(\"text\")) {\n\t\tregisterTiledObjectFactory(\"text\", createTextObject);\n\t}\n\tif (!factories.has(\"tile\")) {\n\t\tregisterTiledObjectFactory(\"tile\", createTileObject);\n\t}\n\tif (!factories.has(\"shape\")) {\n\t\tregisterTiledObjectFactory(\"shape\", createShapeObject);\n\t}\n\n\t// apply pending class-based factories\n\tfor (const entry of pendingClasses) {\n\t\tconst [name, Constructor, factoryFn] = entry;\n\t\tif (typeof factoryFn === \"function\") {\n\t\t\t// pool-registered: use provided factory (preserves pool.pull recycling)\n\t\t\tregisteredClasses.set(name, Constructor);\n\t\t\tregisterTiledObjectFactory(name, factoryFn);\n\t\t} else {\n\t\t\t// direct registration: use new Constructor()\n\t\t\tregisterTiledObjectClass(name, Constructor);\n\t\t}\n\t}\n\tpendingClasses.length = 0;\n\n\tfactoriesInitialized = true;\n}\n\n/**\n * Instantiate a TMX object based on its settings, using the registered factory\n * for its detected type.\n * @param {object} settings - TMX object settings\n * @param {TMXTileMap} map - the parent tile map\n * @returns {Renderable} the instantiated object\n * @ignore\n */\nexport function createTMXObject(settings, map) {\n\tif (!factoriesInitialized) {\n\t\tinitFactories();\n\t}\n\n\tconst type = detectObjectType(settings);\n\n\t// TMXLayer instances are already instantiated — pass through\n\tif (type === \"layer\") {\n\t\treturn settings;\n\t}\n\n\tconst factory = factories.get(type);\n\tif (typeof factory === \"undefined\") {\n\t\tthrow new Error(\"no Tiled object factory registered for type: \" + type);\n\t}\n\n\treturn factory(settings, map);\n}\n\n// wire pool.register() to automatically register Tiled object factories\n// uses pool.pull instead of new Constructor to preserve object recycling\nsetPoolRegisterCallback((className, classObj, poolInstance) => {\n\tconst factoryFn = (settings) => {\n\t\tconst obj = poolInstance.pull(className, settings.x, settings.y, settings);\n\t\tobj.pos.z = settings.z;\n\t\treturn obj;\n\t};\n\n\tif (factoriesInitialized) {\n\t\tregisteredClasses.set(className, classObj);\n\t\tregisterTiledObjectFactory(className, factoryFn);\n\t} else {\n\t\t// queue as a raw factory (not via registerTiledObjectClass which uses new Constructor)\n\t\tpendingClasses.push([className, classObj, factoryFn]);\n\t}\n});\n","import { game } from \"../../application/application.ts\";\nimport { getImage, getJSON, getTMX } from \"../../loader/loader.js\";\nimport { Matrix2d } from \"../../math/matrix2d.ts\";\nimport { Vector2d } from \"../../math/vector2d.ts\";\nimport timer from \"../../system/timer.ts\";\nimport { getBasename, getExtension } from \"../../utils/file.ts\";\nimport { buildFlipTransform } from \"./TMXTile.js\";\nimport { resolveEmbeddedImage } from \"./TMXUtils.js\";\n\n// shared scratch matrix for flip-transform construction in drawTileRaw — avoids\n// per-call allocation in the hot path. Single-threaded JS means it's safe to\n// share across all tilesets without contention.\nconst SCRATCH_MATRIX = new Matrix2d();\n\n/**\n * a TMX Tile Set Object\n * @category Tilemap\n */\nexport default class TMXTileset {\n\t/**\n\t *  @param {object} tileset - tileset data in JSON format ({@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#tileset})\n\t *  @param {number} [mapTilewidth] - the map's tile grid width in pixels\n\t *  @param {number} [mapTileheight] - the map's tile grid height in pixels\n\t */\n\tconstructor(tileset, mapTilewidth, mapTileheight) {\n\t\t/**\n\t\t * per-tile properties indexed by gid\n\t\t * @type {Map<number, object>}\n\t\t * @ignore\n\t\t */\n\t\tthis.tileProperties = new Map();\n\n\t\t/**\n\t\t * per-tile images for \"Collection of Image\" tilesets, indexed by gid\n\t\t * @type {Map<number, HTMLImageElement|HTMLCanvasElement>}\n\t\t * @ignore\n\t\t */\n\t\tthis.imageCollection = new Map();\n\n\t\t/**\n\t\t * the first global tile ID of this tileset\n\t\t * @type {number}\n\t\t */\n\t\tthis.firstgid = +tileset.firstgid;\n\n\t\t/**\n\t\t * the last global tile ID of this tileset\n\t\t * @type {number}\n\t\t */\n\t\tthis.lastgid = this.firstgid;\n\n\t\t// check if an external tileset is defined\n\t\tif (tileset.source !== undefined) {\n\t\t\tconst src = tileset.source;\n\t\t\tconst ext = getExtension(src);\n\t\t\tif (ext === \"tsx\" || ext === \"xml\" || ext === \"json\" || ext === \"tsj\") {\n\t\t\t\t// load the external tileset (TSX/XML/JSON/TSJ)\n\t\t\t\ttileset = getTMX(getBasename(src));\n\t\t\t\tif (!tileset) {\n\t\t\t\t\tthrow new Error(`${src} external tileset not found`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * the tileset name\n\t\t * @type {string}\n\t\t */\n\t\tthis.name = tileset.name;\n\n\t\t/**\n\t\t * the width of each tile in pixels\n\t\t * @type {number}\n\t\t */\n\t\tthis.tilewidth = +tileset.tilewidth;\n\n\t\t/**\n\t\t * the height of each tile in pixels\n\t\t * @type {number}\n\t\t */\n\t\tthis.tileheight = +tileset.tileheight;\n\n\t\t/**\n\t\t * the spacing between tiles in pixels\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.spacing = +(tileset.spacing ?? 0);\n\n\t\t/**\n\t\t * the margin around tiles in pixels\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.margin = +(tileset.margin ?? 0);\n\n\t\t/**\n\t\t * tile drawing offset\n\t\t * @type {Vector2d}\n\t\t */\n\t\tthis.tileoffset = new Vector2d();\n\n\t\t/**\n\t\t * Tileset contains animated tiles\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isAnimated = false;\n\n\t\t/**\n\t\t * true if the tileset is a \"Collection of Image\" Tileset\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isCollection = false;\n\n\t\t/**\n\t\t * the tileset class (since Tiled 1.9, renamed back to type on tile/object in 1.10)\n\t\t * @type {string}\n\t\t */\n\t\tthis.class = tileset.class ?? tileset.type;\n\n\t\t/**\n\t\t * how tiles render relative to the grid (since Tiled 1.9)\n\t\t * @type {string}\n\t\t * @default \"tile\"\n\t\t */\n\t\tthis.tilerendersize = tileset.tilerendersize ?? \"tile\";\n\n\t\t/**\n\t\t * fill mode when tiles are not rendered at native size (since Tiled 1.9)\n\t\t * @type {string}\n\t\t * @default \"stretch\"\n\t\t */\n\t\tthis.fillmode = tileset.fillmode ?? \"stretch\";\n\n\t\t/**\n\t\t * the map's tile grid width (used for tilerendersize=\"grid\")\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis.mapTilewidth = mapTilewidth ?? this.tilewidth;\n\n\t\t/**\n\t\t * the map's tile grid height (used for tilerendersize=\"grid\")\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis.mapTileheight = mapTileheight ?? this.tileheight;\n\n\t\t/**\n\t\t * precomputed render scale for tilerendersize=\"grid\" (spritesheet only)\n\t\t * @private\n\t\t */\n\t\tthis._renderScaleX = 1;\n\t\tthis._renderScaleY = 1;\n\t\tthis._renderDw = this.tilewidth;\n\t\tthis._renderDh = this.tileheight;\n\t\tthis._renderDyOffset = 0;\n\t\tthis._renderDxCenter = 0;\n\t\tthis._renderDyCenter = 0;\n\n\t\tif (this.tilerendersize === \"grid\") {\n\t\t\tthis._renderScaleX = this.mapTilewidth / this.tilewidth;\n\t\t\tthis._renderScaleY = this.mapTileheight / this.tileheight;\n\n\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\tconst minScale = Math.min(this._renderScaleX, this._renderScaleY);\n\t\t\t\tthis._renderScaleX = minScale;\n\t\t\t\tthis._renderScaleY = minScale;\n\t\t\t}\n\n\t\t\tthis._renderDw = this.tilewidth * this._renderScaleX;\n\t\t\tthis._renderDh = this.tileheight * this._renderScaleY;\n\t\t\tthis._renderDyOffset = this.tileheight - this._renderDh;\n\n\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\tthis._renderDxCenter = (this.mapTilewidth - this._renderDw) / 2;\n\t\t\t\tthis._renderDyCenter = -(this.mapTileheight - this._renderDh) / 2;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * per-tile sub-rectangles (Tiled 1.9+), indexed by local tile id\n\t\t * @type {Map<number, {x: number, y: number, width: number, height: number}>}\n\t\t * @ignore\n\t\t */\n\t\tthis.tileSubRects = new Map();\n\n\t\t/**\n\t\t * Tileset animations\n\t\t * @private\n\t\t * @type {Map}\n\t\t */\n\t\tthis.animations = new Map();\n\n\t\t/**\n\t\t * Remember the last update timestamp to prevent too many animation updates\n\t\t * @private\n\t\t */\n\t\tthis._lastUpdate = 0;\n\n\t\t// resolve embedded JSON image data on the tileset itself\n\t\tresolveEmbeddedImage(tileset);\n\n\t\t// parse individual tile entries (animations, properties, images)\n\t\tthis._parseTiles(tileset.tiles);\n\n\t\t// Tiled 1.10+ provides an explicit flag; fall back to detection for older maps\n\t\tthis.isCollection = tileset.isCollection ?? this.imageCollection.size > 0;\n\n\t\t// set tile offset\n\t\tconst offset = tileset.tileoffset;\n\t\tif (offset) {\n\t\t\tthis.tileoffset.x = +offset.x;\n\t\t\tthis.tileoffset.y = +offset.y;\n\t\t}\n\n\t\t// set tile properties from old JSON format (tileproperties)\n\t\tconst tileInfo = tileset.tileproperties;\n\t\tif (tileInfo) {\n\t\t\tfor (const [id, props] of Object.entries(tileInfo)) {\n\t\t\t\tthis.setTileProperty(+id + this.firstgid, props);\n\t\t\t}\n\t\t}\n\n\t\t// initialize atlas for spritesheet tilesets\n\t\tif (!this.isCollection) {\n\t\t\tthis._initAtlas(tileset);\n\t\t\t// if the tileset image was loaded from an .aseprite file, the\n\t\t\t// loader produces a JSON sidecar with frame tags. Synthesize per-tile\n\t\t\t// animations from those tags so animated tilesets authored in\n\t\t\t// Aseprite (Tiled 1.11+ workflow) work without manual <animation>\n\t\t\t// markup in the TSX.\n\t\t\tthis._applyAsepriteFrameTags(tileset);\n\t\t}\n\t}\n\n\t/**\n\t * If `tileset.image` was loaded via the .aseprite binary parser, walk\n\t * its `meta.frameTags` and synthesize per-tile animations. Each tag\n\t * `{ from, to }` becomes an animation on tile `from` that cycles through\n\t * tiles `[from..to]` using each frame's `duration`.\n\t *\n\t * Throws on tag configurations that don't map to Tiled's animation model:\n\t * non-forward direction (Tiled animations are forward-only) and non-zero\n\t * repeat counts (Tiled animations loop forever).\n\t * @ignore\n\t */\n\t_applyAsepriteFrameTags(tileset) {\n\t\t// getJSON is a direct key lookup (no basename normalization, unlike\n\t\t// getImage), so use the basename ourselves to match the loader's\n\t\t// asset registration convention\n\t\tconst atlas = getJSON(getBasename(\"\" + tileset.image));\n\t\tif (!atlas || !atlas.meta || typeof atlas.meta.app !== \"string\") {\n\t\t\treturn;\n\t\t}\n\t\tif (!atlas.meta.app.includes(\"aseprite\")) {\n\t\t\treturn;\n\t\t}\n\t\tconst tags = atlas.meta.frameTags;\n\t\tif (!Array.isArray(tags) || tags.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst frames = atlas.meta.frames || atlas.frames || {};\n\n\t\tfor (const tag of tags) {\n\t\t\tif (tag.direction !== \"forward\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`melonJS: aseprite tag \"${tag.name}\" on tileset '${this.name}' uses ` +\n\t\t\t\t\t\t`direction \"${tag.direction}\" — Tiled tile animations only support ` +\n\t\t\t\t\t\t`forward playback. Re-export the tag as \"forward\" in Aseprite.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (typeof tag.repeat === \"number\" && tag.repeat !== 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`melonJS: aseprite tag \"${tag.name}\" on tileset '${this.name}' has a ` +\n\t\t\t\t\t\t`finite repeat count (${tag.repeat}) — Tiled tile animations only ` +\n\t\t\t\t\t\t`support infinite looping. Set the tag's repeat to \"∞\" in Aseprite.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// build the animation: each frame index in [from..to] becomes a\n\t\t\t// tile id; each tile's display duration comes from its frame entry.\n\t\t\tconst anim = [];\n\t\t\tfor (let f = tag.from; f <= tag.to; f++) {\n\t\t\t\tconst frame = frames[String(f)] || frames[f];\n\t\t\t\tanim.push({\n\t\t\t\t\ttileid: f,\n\t\t\t\t\tduration:\n\t\t\t\t\t\tframe && typeof frame.duration === \"number\" ? frame.duration : 100,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (anim.length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Tiled's convention: the animation lives on the tile whose id\n\t\t\t// matches the first frame; the engine cycles through the rest.\n\t\t\t// If `_parseTiles` already attached an explicit Tiled <animation>\n\t\t\t// for this tile, defer to it — hand-authored TSX data wins over\n\t\t\t// the auto-generated Aseprite tag.\n\t\t\tif (this.animations.has(tag.from)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.isAnimated = true;\n\t\t\tthis.animations.set(tag.from, {\n\t\t\t\tdt: 0,\n\t\t\t\tidx: 0,\n\t\t\t\tframes: anim,\n\t\t\t\tcur: anim[0],\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Parse individual tile entries for animations, properties, and images.\n\t * @param {object[]|object} [tiles] - tile entries (array in JSON, object in XML)\n\t * @ignore\n\t */\n\t_parseTiles(tiles) {\n\t\tif (!tiles) {\n\t\t\treturn;\n\t\t}\n\n\t\t// normalize: tiles can be an array (JSON new) or an object keyed by id (XML / JSON old)\n\t\tlet tileEntries;\n\t\tif (Array.isArray(tiles)) {\n\t\t\ttileEntries = tiles;\n\t\t} else {\n\t\t\t// convert object-keyed format to array, injecting the key as id\n\t\t\ttileEntries = Object.entries(tiles).map(([key, value]) => {\n\t\t\t\treturn { id: key, ...value };\n\t\t\t});\n\t\t}\n\n\t\tfor (const tile of tileEntries) {\n\t\t\tconst tileId = +tile.id;\n\n\t\t\t// parse animation frames\n\t\t\tif (tile.animation) {\n\t\t\t\tthis.isAnimated = true;\n\t\t\t\tconst anim = tile.animation;\n\t\t\t\t// key by the tile's own local id, not the first frame's tileid\n\t\t\t\tthis.animations.set(tileId, {\n\t\t\t\t\tdt: 0,\n\t\t\t\t\tidx: 0,\n\t\t\t\t\tframes: anim,\n\t\t\t\t\tcur: anim[0],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// set tile properties, if any\n\t\t\tif (tile.properties) {\n\t\t\t\tif (Array.isArray(tile.properties)) {\n\t\t\t\t\t// JSON (new format) — array of { name, value }\n\t\t\t\t\tconst tileProperty = {};\n\t\t\t\t\tfor (const prop of tile.properties) {\n\t\t\t\t\t\ttileProperty[prop.name] = prop.value;\n\t\t\t\t\t}\n\t\t\t\t\tthis.setTileProperty(tileId + this.firstgid, tileProperty);\n\t\t\t\t} else {\n\t\t\t\t\t// XML format — flat object\n\t\t\t\t\tthis.setTileProperty(tileId + this.firstgid, tile.properties);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// resolve embedded JSON image data on individual tiles\n\t\t\tresolveEmbeddedImage(tile);\n\n\t\t\t// store per-tile image (collection of images tileset)\n\t\t\tif (tile.image) {\n\t\t\t\tconst image = getImage(tile.image);\n\t\t\t\tif (!image) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`melonJS: '${tile.image}' file for tile '${tileId + this.firstgid}' not found!`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// check for sub-rectangle (Tiled 1.9+)\n\t\t\t\tconst hasSubRect =\n\t\t\t\t\ttypeof tile.x !== \"undefined\" ||\n\t\t\t\t\ttypeof tile.y !== \"undefined\" ||\n\t\t\t\t\ttypeof tile.width !== \"undefined\" ||\n\t\t\t\t\ttypeof tile.height !== \"undefined\";\n\n\t\t\t\tif (hasSubRect) {\n\t\t\t\t\tconst sx = +(tile.x ?? 0);\n\t\t\t\t\tconst sy = +(tile.y ?? 0);\n\t\t\t\t\tconst sw =\n\t\t\t\t\t\ttypeof tile.width !== \"undefined\" ? +tile.width : image.width;\n\t\t\t\t\tconst sh =\n\t\t\t\t\t\ttypeof tile.height !== \"undefined\" ? +tile.height : image.height;\n\n\t\t\t\t\t// store the sub-rect metadata\n\t\t\t\t\tthis.tileSubRects.set(tileId, {\n\t\t\t\t\t\tx: sx,\n\t\t\t\t\t\ty: sy,\n\t\t\t\t\t\twidth: sw,\n\t\t\t\t\t\theight: sh,\n\t\t\t\t\t});\n\n\t\t\t\t\t// crop and cache a sub-image if the rect differs from the full image\n\t\t\t\t\tif (\n\t\t\t\t\t\tsx !== 0 ||\n\t\t\t\t\t\tsy !== 0 ||\n\t\t\t\t\t\tsw !== image.width ||\n\t\t\t\t\t\tsh !== image.height\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst canvas = document.createElement(\"canvas\");\n\t\t\t\t\t\tcanvas.width = sw;\n\t\t\t\t\t\tcanvas.height = sh;\n\t\t\t\t\t\tcanvas\n\t\t\t\t\t\t\t.getContext(\"2d\")\n\t\t\t\t\t\t\t.drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh);\n\t\t\t\t\t\tthis.imageCollection.set(tileId + this.firstgid, canvas);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.imageCollection.set(tileId + this.firstgid, image);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.imageCollection.set(tileId + this.firstgid, image);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Initialize the texture atlas for a spritesheet tileset.\n\t * @param {object} tileset - tileset data\n\t * @ignore\n\t */\n\t_initAtlas(tileset) {\n\t\t// get the global tileset texture\n\t\tthis.image = getImage(tileset.image);\n\n\t\tif (!this.image) {\n\t\t\tthrow new Error(\n\t\t\t\t`melonJS: '${tileset.image}' file for tileset '${this.name}' not found!`,\n\t\t\t);\n\t\t}\n\n\t\t// create a texture atlas for the given tileset\n\t\tthis.texture = game.renderer.cache.get(this.image, {\n\t\t\tframewidth: this.tilewidth,\n\t\t\tframeheight: this.tileheight,\n\t\t\tmargin: this.margin,\n\t\t\tspacing: this.spacing,\n\t\t});\n\t\tthis.atlas = this.texture.getAtlas();\n\n\t\t// calculate the number of tiles per horizontal line\n\t\tconst hTileCount =\n\t\t\t+tileset.columns ||\n\t\t\tMath.round(this.image.width / (this.tilewidth + this.spacing));\n\t\tlet vTileCount = Math.round(\n\t\t\tthis.image.height / (this.tileheight + this.spacing),\n\t\t);\n\t\tif (tileset.tilecount % hTileCount > 0) {\n\t\t\t++vTileCount;\n\t\t}\n\t\t// compute the last gid value in the tileset\n\t\tthis.lastgid = this.firstgid + (hTileCount * vTileCount - 1 || 0);\n\t\tif (\n\t\t\ttileset.tilecount &&\n\t\t\tthis.lastgid - this.firstgid + 1 !== +tileset.tilecount\n\t\t) {\n\t\t\tconsole.warn(\n\t\t\t\t`Computed tilecount (${this.lastgid - this.firstgid + 1}) does not match expected tilecount (${tileset.tilecount})`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * return the tile image from a \"Collection of Image\" tileset\n\t * @param {number} gid\n\t * @returns {HTMLImageElement|HTMLCanvasElement|undefined} corresponding image or undefined\n\t */\n\tgetTileImage(gid) {\n\t\treturn this.imageCollection.get(gid);\n\t}\n\n\t/**\n\t * set the tile properties\n\t * @param {number} gid - global tile ID\n\t * @param {object} prop - property object\n\t * @ignore\n\t */\n\tsetTileProperty(gid, prop) {\n\t\tthis.tileProperties.set(gid, prop);\n\t}\n\n\t/**\n\t * return true if the gid belongs to the tileset\n\t * @param {number} gid\n\t * @returns {boolean}\n\t */\n\tcontains(gid) {\n\t\treturn gid >= this.firstgid && gid <= this.lastgid;\n\t}\n\n\t/**\n\t * Get the view (local) tile ID from a GID, with animations applied\n\t * @param {number} gid - Global tile ID\n\t * @returns {number} View tile ID\n\t */\n\tgetViewTileId(gid) {\n\t\tconst localId = gid - this.firstgid;\n\n\t\t// return the current corresponding tile id if animated\n\t\tconst anim = this.animations.get(localId);\n\t\tif (anim !== undefined) {\n\t\t\treturn anim.cur.tileid;\n\t\t}\n\n\t\treturn localId;\n\t}\n\n\t/**\n\t * return the properties of the specified tile\n\t * @param {number} tileId - global tile ID\n\t * @returns {object|undefined} tile properties or undefined\n\t */\n\tgetTileProperties(tileId) {\n\t\treturn this.tileProperties.get(tileId);\n\t}\n\n\t/**\n\t * update tile animations\n\t * @param {number} dt - time delta in milliseconds\n\t * @returns {boolean} true if any animation frame changed\n\t * @ignore\n\t */\n\tupdate(dt) {\n\t\tconst now = timer.getTime();\n\t\tlet result = false;\n\n\t\tif (this._lastUpdate !== now) {\n\t\t\tthis._lastUpdate = now;\n\n\t\t\tfor (const anim of this.animations.values()) {\n\t\t\t\tanim.dt += dt;\n\t\t\t\tlet duration = anim.cur.duration;\n\t\t\t\twhile (anim.dt >= duration) {\n\t\t\t\t\tanim.dt -= duration;\n\t\t\t\t\tanim.idx = (anim.idx + 1) % anim.frames.length;\n\t\t\t\t\tanim.cur = anim.frames[anim.idx];\n\t\t\t\t\tduration = anim.cur.duration;\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * draw a tile at the specified position\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {number} dx - destination x position\n\t * @param {number} dy - destination y position\n\t * @param {Tile} tmxTile - the tile object to draw\n\t * @ignore\n\t */\n\tdrawTile(renderer, dx, dy, tmxTile) {\n\t\tlet dw, dh;\n\n\t\tif (this.isCollection) {\n\t\t\t// collection tiles can have varying sizes; compute scale per-tile\n\t\t\tif (this.tilerendersize === \"grid\") {\n\t\t\t\tlet scaleX = this.mapTilewidth / tmxTile.width;\n\t\t\t\tlet scaleY = this.mapTileheight / tmxTile.height;\n\n\t\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\t\tconst scale = Math.min(scaleX, scaleY);\n\t\t\t\t\tscaleX = scale;\n\t\t\t\t\tscaleY = scale;\n\t\t\t\t}\n\n\t\t\t\tdw = tmxTile.width * scaleX;\n\t\t\t\tdh = tmxTile.height * scaleY;\n\n\t\t\t\t// bottom-align against tileset baseline (renderer uses tileset.tileheight)\n\t\t\t\tdy += this.tileheight - dh;\n\n\t\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\t\tdx += (this.mapTilewidth - dw) / 2;\n\t\t\t\t\tdy -= (this.mapTileheight - dh) / 2;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdw = tmxTile.width;\n\t\t\t\tdh = tmxTile.height;\n\t\t\t}\n\t\t} else {\n\t\t\t// spritesheet: use precomputed values\n\t\t\tdw = this._renderDw;\n\t\t\tdh = this._renderDh;\n\t\t\tdy += this._renderDyOffset;\n\t\t\tdx += this._renderDxCenter;\n\t\t\tdy += this._renderDyCenter;\n\t\t}\n\n\t\t// check if any transformation is required\n\t\tif (tmxTile.flipped) {\n\t\t\trenderer.save();\n\t\t\t// apply the tile current transform\n\t\t\trenderer.translate(dx, dy);\n\t\t\trenderer.transform(tmxTile.currentTransform);\n\t\t\t// reset both values as managed through transform()\n\t\t\tdx = dy = 0;\n\t\t}\n\n\t\t// check if the tile has an associated image\n\t\tif (this.isCollection) {\n\t\t\t// draw the tile from the image collection\n\t\t\trenderer.drawImage(\n\t\t\t\tthis.imageCollection.get(tmxTile.tileId),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\ttmxTile.width,\n\t\t\t\ttmxTile.height,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw,\n\t\t\t\tdh,\n\t\t\t);\n\t\t} else {\n\t\t\t// use the tileset texture atlas\n\t\t\tconst offset = this.atlas[this.getViewTileId(tmxTile.tileId)].offset;\n\t\t\trenderer.drawImage(\n\t\t\t\tthis.image,\n\t\t\t\toffset.x,\n\t\t\t\toffset.y,\n\t\t\t\tthis.tilewidth,\n\t\t\t\tthis.tileheight,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw + renderer.uvOffset,\n\t\t\t\tdh + renderer.uvOffset,\n\t\t\t);\n\t\t}\n\n\t\tif (tmxTile.flipped) {\n\t\t\t// restore the context to the previous state\n\t\t\trenderer.restore();\n\t\t}\n\t}\n\n\t/**\n\t * draw a tile at the specified position from raw (gid, flipMask) data\n\t *\n\t * Like {@link drawTile} but bypasses the {@link Tile} object entirely:\n\t * the renderer hot loop can pass the GID and flip mask straight from\n\t * `layer.layerData` without ever allocating a Tile instance.\n\t *\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {number} dx - destination x position\n\t * @param {number} dy - destination y position\n\t * @param {number} gid - the tile's global id (with flip bits already stripped)\n\t * @param {number} flipMask - 3-bit packed flip mask (H=1, V=2, AD=4)\n\t * @ignore\n\t */\n\tdrawTileRaw(renderer, dx, dy, gid, flipMask) {\n\t\tlet dw, dh;\n\t\tlet tileImage;\n\n\t\tif (this.isCollection) {\n\t\t\t// collection tiles can have varying sizes; compute scale per-tile\n\t\t\ttileImage = this.imageCollection.get(gid);\n\t\t\tconst tileWidth = tileImage.width;\n\t\t\tconst tileHeight = tileImage.height;\n\n\t\t\tif (this.tilerendersize === \"grid\") {\n\t\t\t\tlet scaleX = this.mapTilewidth / tileWidth;\n\t\t\t\tlet scaleY = this.mapTileheight / tileHeight;\n\n\t\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\t\tconst scale = Math.min(scaleX, scaleY);\n\t\t\t\t\tscaleX = scale;\n\t\t\t\t\tscaleY = scale;\n\t\t\t\t}\n\n\t\t\t\tdw = tileWidth * scaleX;\n\t\t\t\tdh = tileHeight * scaleY;\n\n\t\t\t\t// bottom-align against tileset baseline (renderer uses tileset.tileheight)\n\t\t\t\tdy += this.tileheight - dh;\n\n\t\t\t\tif (this.fillmode === \"preserve-aspect-fit\") {\n\t\t\t\t\tdx += (this.mapTilewidth - dw) / 2;\n\t\t\t\t\tdy -= (this.mapTileheight - dh) / 2;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdw = tileWidth;\n\t\t\t\tdh = tileHeight;\n\t\t\t}\n\t\t} else {\n\t\t\t// spritesheet: use precomputed values\n\t\t\tdw = this._renderDw;\n\t\t\tdh = this._renderDh;\n\t\t\tdy += this._renderDyOffset;\n\t\t\tdx += this._renderDxCenter;\n\t\t\tdy += this._renderDyCenter;\n\t\t}\n\n\t\t// check if any transformation is required\n\t\tif (flipMask !== 0) {\n\t\t\trenderer.save();\n\t\t\trenderer.translate(dx, dy);\n\t\t\t// rebuild the flip transform into the shared scratch matrix\n\t\t\t// (size driven by the tileset's tile dims for spritesheets, the\n\t\t\t// tile image's dims for collections)\n\t\t\trenderer.transform(\n\t\t\t\tbuildFlipTransform(\n\t\t\t\t\tSCRATCH_MATRIX,\n\t\t\t\t\tflipMask,\n\t\t\t\t\tthis.isCollection ? tileImage.width : this.tilewidth,\n\t\t\t\t\tthis.isCollection ? tileImage.height : this.tileheight,\n\t\t\t\t),\n\t\t\t);\n\t\t\tdx = dy = 0;\n\t\t}\n\n\t\t// draw the tile image\n\t\tif (this.isCollection) {\n\t\t\trenderer.drawImage(\n\t\t\t\ttileImage,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\ttileImage.width,\n\t\t\t\ttileImage.height,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw,\n\t\t\t\tdh,\n\t\t\t);\n\t\t} else {\n\t\t\tconst offset = this.atlas[this.getViewTileId(gid)].offset;\n\t\t\trenderer.drawImage(\n\t\t\t\tthis.image,\n\t\t\t\toffset.x,\n\t\t\t\toffset.y,\n\t\t\t\tthis.tilewidth,\n\t\t\t\tthis.tileheight,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw + renderer.uvOffset,\n\t\t\t\tdh + renderer.uvOffset,\n\t\t\t);\n\t\t}\n\n\t\tif (flipMask !== 0) {\n\t\t\trenderer.restore();\n\t\t}\n\t}\n}\n","import { TMX_CLEAR_BIT_MASK } from \"./constants\";\n\n/**\n * an object containing all tileset\n */\nexport default class TMXTilesetGroup {\n\tconstructor() {\n\t\tthis.tilesets = [];\n\t\tthis.length = 0;\n\t\t// cache last matched tileset — consecutive tiles usually share the same tileset\n\t\tthis._lastTileset = null;\n\t}\n\n\t/**\n\t * add a tileset to the tileset group\n\t * @param {TMXTileset} tileset\n\t */\n\tadd(tileset) {\n\t\tthis.tilesets.push(tileset);\n\t\tthis.length++;\n\t}\n\n\t/**\n\t * return the tileset at the specified index\n\t * @param {number} i\n\t * @returns {TMXTileset} corresponding tileset\n\t */\n\tgetTilesetByIndex(i) {\n\t\treturn this.tilesets[i];\n\t}\n\n\t/**\n\t * return the tileset corresponding to the specified id <br>\n\t * will throw an exception if no matching tileset is found\n\t * @param {number} gid\n\t * @returns {TMXTileset} corresponding tileset\n\t */\n\tgetTilesetByGid(gid) {\n\t\t// clear the gid of all flip/rotation flags\n\t\tgid &= TMX_CLEAR_BIT_MASK;\n\n\t\t// fast path: check cached tileset first (high hit rate for sequential tile data)\n\t\tconst last = this._lastTileset;\n\t\tif (last !== null && last.contains(gid)) {\n\t\t\treturn last;\n\t\t}\n\n\t\tlet invalidRange = -1;\n\t\tconst tilesets = this.tilesets;\n\n\t\t// cycle through all tilesets\n\t\tfor (let i = 0, len = tilesets.length; i < len; i++) {\n\t\t\tconst ts = tilesets[i];\n\t\t\t// return the corresponding tileset if matching\n\t\t\tif (ts.contains(gid)) {\n\t\t\t\tthis._lastTileset = ts;\n\t\t\t\treturn ts;\n\t\t\t}\n\t\t\t// typically indicates a layer with no asset loaded (collision?)\n\t\t\tif (ts.firstgid === ts.lastgid && gid >= ts.firstgid) {\n\t\t\t\tinvalidRange = i;\n\t\t\t}\n\t\t}\n\t\t// return the tileset with the invalid range\n\t\tif (invalidRange !== -1) {\n\t\t\tconst fallback = tilesets[invalidRange];\n\t\t\tthis._lastTileset = fallback;\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow new Error(`no matching tileset found for gid ${gid}`);\n\t}\n}\n","import { warning } from \"../../lang/console.js\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport { collision } from \"../../physics/collision.js\";\nimport Container from \"../../renderable/container.js\";\nimport { off, on, VIEWPORT_ONRESIZE } from \"../../system/event.ts\";\nimport pool from \"../../system/legacy_pool.js\";\nimport { checkVersion } from \"../../utils/utils.ts\";\nimport { COLLISION_GROUP, TILED_SUPPORTED_VERSION } from \"./constants.js\";\nimport { getNewTMXRenderer } from \"./renderer/autodetect.js\";\nimport TMXGroup from \"./TMXGroup.js\";\nimport TMXLayer from \"./TMXLayer.js\";\nimport { createTMXObject } from \"./TMXObjectFactory.js\";\nimport TMXTileset from \"./TMXTileset.js\";\nimport TMXTilesetGroup from \"./TMXTilesetGroup.js\";\nimport {\n\tapplyObjectOpacity,\n\tapplyTMXProperties,\n\tparseTintColor,\n\tpropagateBlendMode,\n\tresolveEmbeddedImage,\n\ttiledBlendMode,\n} from \"./TMXUtils.js\";\n\n/**\n * read the layer Data\n * @ignore\n */\nfunction readLayer(map, data, z) {\n\treturn new TMXLayer(\n\t\tmap,\n\t\tdata,\n\t\tmap.tilewidth,\n\t\tmap.tileheight,\n\t\tmap.orientation,\n\t\tmap.tilesets,\n\t\tz,\n\t);\n}\n\n/**\n * read the Image Layer Data\n * @ignore\n */\nfunction readImageLayer(map, data, z) {\n\t// resolve embedded JSON image data if present\n\tresolveEmbeddedImage(data);\n\n\t// Normalize properties\n\tapplyTMXProperties(data.properties, data);\n\n\t// derive repeat mode: legacy custom \"repeat\" property takes precedence\n\t// (for backward compatibility), then Tiled 1.8+ native repeatx/repeaty\n\tconst repX = data.repeatx === true || data.repeatx === \"1\";\n\tconst repY = data.repeaty === true || data.repeaty === \"1\";\n\tlet repeat = data.properties?.repeat;\n\tif (typeof repeat === \"undefined\" && (repX || repY)) {\n\t\tif (repX && repY) {\n\t\t\trepeat = \"repeat\";\n\t\t} else if (repX) {\n\t\t\trepeat = \"repeat-x\";\n\t\t} else {\n\t\t\trepeat = \"repeat-y\";\n\t\t}\n\t}\n\n\t// Tiled 1.8+ parallax origin (map-level, baked into layer offset)\n\tconst pox = +(map.data.parallaxoriginx ?? 0);\n\tconst poy = +(map.data.parallaxoriginy ?? 0);\n\tconst ratioX = +(data.parallaxx ?? 1.0);\n\tconst ratioY = +(data.parallaxy ?? 1.0);\n\tconst ox = +(data.offsetx ?? data.x ?? 0) + pox * ratioX;\n\tconst oy = +(data.offsety ?? data.y ?? 0) + poy * ratioY;\n\n\t// create the layer\n\tconst imageLayer = pool.pull(\n\t\t\"ImageLayer\",\n\t\tox,\n\t\toy,\n\t\tObject.assign(\n\t\t\t{\n\t\t\t\tname: data.name,\n\t\t\t\timage: data.image,\n\t\t\t\tratio: vector2dPool.get(ratioX, ratioY),\n\t\t\t\ttint: parseTintColor(data.tintcolor),\n\t\t\t\tz: z,\n\t\t\t\trepeat: repeat,\n\t\t\t},\n\t\t\tdata.properties,\n\t\t),\n\t);\n\n\t// set some additional flags\n\tconst visible = data.visible ?? true;\n\timageLayer.setOpacity(visible ? +data.opacity : 0);\n\n\t// layer blend mode (Tiled 1.12+)\n\timageLayer.blendMode = tiledBlendMode(data.mode);\n\n\treturn imageLayer;\n}\n\n/**\n * read the tileset Data\n * @ignore\n */\nfunction readTileset(data, mapTilewidth, mapTileheight) {\n\treturn new TMXTileset(data, mapTilewidth, mapTileheight);\n}\n\n/**\n * read the object group Data\n * @ignore\n */\nfunction readObjectGroup(map, data, z) {\n\treturn new TMXGroup(map, data, z);\n}\n\n/**\n * Recursively recompute absolute bounds for a container and all its\n * descendants. Used when the container's pos changes (centering on a wider\n * viewport): children cache their absolute bounds at addChild time, and\n * those caches are stale until each child happens to call updateBounds on\n * its own (which only happens organically when its own pos changes).\n * @ignore\n */\nfunction refreshAbsoluteBounds(container) {\n\tcontainer.forEach((child) => {\n\t\tchild.updateBounds(true);\n\t\tif (child instanceof Container) {\n\t\t\trefreshAbsoluteBounds(child);\n\t\t}\n\t});\n}\n\n/**\n * a TMX Tile Map Object\n * Tiled QT +0.7.x format\n * @category Tilemap\n */\nexport default class TMXTileMap {\n\t/**\n\t * @param {string} levelId - name of TMX map\n\t * @param {object} data - TMX map in JSON format\n\t * @example\n\t * // create a new level object based on the TMX JSON object\n\t * let level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));\n\t * // add the level to the game world container\n\t * level.addTo(app.world, true);\n\t */\n\tconstructor(levelId, data) {\n\t\t/**\n\t\t * the level data (JSON)\n\t\t * @ignore\n\t\t */\n\t\tthis.data = data;\n\n\t\t/**\n\t\t * name of the tilemap\n\t\t * @type {string}\n\t\t */\n\t\tthis.name = levelId;\n\n\t\t/**\n\t\t * level format discriminator used by the level director's dispatch\n\t\t * @type {string}\n\t\t */\n\t\tthis.format = \"tmx\";\n\n\t\t/**\n\t\t * width of the tilemap in tiles\n\t\t * @type {number}\n\t\t */\n\t\tthis.cols = +data.width;\n\n\t\t/**\n\t\t * height of the tilemap in tiles\n\t\t * @type {number}\n\t\t */\n\t\tthis.rows = +data.height;\n\n\t\t/**\n\t\t * Tile width\n\t\t * @type {number}\n\t\t */\n\t\tthis.tilewidth = +data.tilewidth;\n\n\t\t/**\n\t\t * Tile height\n\t\t * @type {number}\n\t\t */\n\t\tthis.tileheight = +data.tileheight;\n\n\t\t/**\n\t\t * is the map an infinite map\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.infinite = +data.infinite || 0;\n\n\t\t/**\n\t\t * the map orientation type. melonJS supports “orthogonal”, “isometric”, “staggered”, “hexagonal” and “oblique”.\n\t\t * @type {string}\n\t\t * @default “orthogonal”\n\t\t */\n\t\tthis.orientation = data.orientation;\n\n\t\t/**\n\t\t * horizontal skew in pixels per tile row (oblique maps, since Tiled 1.12)\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.skewx = +(data.skewx ?? 0);\n\n\t\t/**\n\t\t * vertical skew in pixels per tile column (oblique maps, since Tiled 1.12)\n\t\t * @type {number}\n\t\t * @default 0\n\t\t */\n\t\tthis.skewy = +(data.skewy ?? 0);\n\n\t\t/**\n\t\t * the order in which tiles on orthogonal tile layers are rendered.\n\t\t * (valid values are \"left-down\", \"left-up\", \"right-down\", \"right-up\")\n\t\t * @type {string}\n\t\t * @default \"right-down\"\n\t\t */\n\t\tthis.renderorder = data.renderorder || \"right-down\";\n\n\t\t/**\n\t\t * the TMX format version\n\t\t * @type {string}\n\t\t */\n\t\tthis.version = \"\" + data.version;\n\n\t\t/**\n\t\t * The Tiled version used to save the file (since Tiled 1.0.1).\n\t\t * @type {string}\n\t\t */\n\t\tthis.tiledversion = \"\" + data.tiledversion;\n\n\t\t/**\n\t\t * The map class.\n\t\t * @type {string}\n\t\t */\n\t\tthis.class = data.class;\n\n\t\t// tilesets for this map\n\t\tthis.tilesets = null;\n\n\t\t// layers\n\t\tif (typeof this.layers === \"undefined\") {\n\t\t\tthis.layers = [];\n\t\t}\n\t\t// group objects\n\t\tif (typeof this.objectGroups === \"undefined\") {\n\t\t\tthis.objectGroups = [];\n\t\t}\n\n\t\t// Check if map is from melon editor\n\t\tthis.isEditor = data.editor === \"melon-editor\";\n\n\t\t// object id\n\t\tthis.nextobjectid = +data.nextobjectid || undefined;\n\n\t\t// hex/iso properties\n\t\tthis.hexsidelength = +data.hexsidelength;\n\t\tthis.staggeraxis = data.staggeraxis;\n\t\tthis.staggerindex = data.staggerindex;\n\n\t\t// calculate the map bounding rect\n\t\tthis.bounds = this.getRenderer().getBounds().clone();\n\n\t\t// map \"real\" size\n\t\tthis.width = this.bounds.width;\n\t\tthis.height = this.bounds.height;\n\n\t\t// background color\n\t\tthis.backgroundcolor = data.backgroundcolor;\n\n\t\t// if version is undefined or empty it usually means the map was not created with Tiled\n\t\tif (this.version !== \"undefined\" && this.version !== \"\") {\n\t\t\t// deprecation warning if map tiled version is older than 1.5\n\t\t\tif (checkVersion(this.version, \"1.5\") < 0) {\n\t\t\t\twarning(\n\t\t\t\t\t`(${this.name}) Tiled Map format version 1.4 and below`,\n\t\t\t\t\t\"format 1.5 or higher\",\n\t\t\t\t\t\"10.4.4\",\n\t\t\t\t);\n\t\t\t}\n\t\t\t// warning if map version is newer than what we currently support\n\t\t\tif (checkVersion(this.version, TILED_SUPPORTED_VERSION) > 0) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`(${this.name}) Tiled Map format version ${this.version} is newer than the currently supported version ${TILED_SUPPORTED_VERSION}. Some features may not work as expected.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// set additional map properties (if any)\n\t\tapplyTMXProperties(this, data);\n\n\t\t// internal flag\n\t\tthis.initialized = false;\n\t}\n\n\t/**\n\t * Return the map default renderer\n\t * @returns {TMXRenderer} a TMX renderer\n\t */\n\tgetRenderer() {\n\t\tif (\n\t\t\ttypeof this.renderer === \"undefined\" ||\n\t\t\t!this.renderer.canRender(this)\n\t\t) {\n\t\t\tthis.renderer = getNewTMXRenderer(this);\n\t\t}\n\t\treturn this.renderer;\n\t}\n\n\t/**\n\t * return the map bounding rect\n\t * @returns {Bounds}\n\t */\n\tgetBounds() {\n\t\t// calculated in the constructor\n\t\treturn this.bounds;\n\t}\n\n\t/**\n\t * parse the map\n\t * @ignore\n\t */\n\treadMapObjects(data) {\n\t\tif (this.initialized === true) {\n\t\t\treturn;\n\t\t}\n\n\t\t// to automatically increment z index\n\t\tlet zOrder = 0;\n\n\t\t// Tileset information\n\t\tif (!this.tilesets) {\n\t\t\t// make sure we have a TilesetGroup Object\n\t\t\tthis.tilesets = new TMXTilesetGroup();\n\t\t}\n\n\t\t// parse all tileset objects\n\t\tif (typeof data.tilesets !== \"undefined\") {\n\t\t\tconst tilesets = data.tilesets;\n\t\t\tfor (let i = 0, len = tilesets.length; i < len; i++) {\n\t\t\t\tthis.tilesets.add(\n\t\t\t\t\treadTileset(tilesets[i], this.tilewidth, this.tileheight),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// check if a background image is defined\n\t\tif (this.background_image) {\n\t\t\t// add a new image layer\n\t\t\tthis.layers.push(\n\t\t\t\tpool.pull(\"ImageLayer\", 0, 0, {\n\t\t\t\t\tname: \"background_image\",\n\t\t\t\t\timage: this.background_image,\n\t\t\t\t\tz: zOrder++,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tconst layers = data.layers;\n\t\tfor (let i = 0, len = layers.length; i < len; i++) {\n\t\t\tconst layer = layers[i];\n\t\t\tswitch (layer.type) {\n\t\t\t\tcase \"imagelayer\":\n\t\t\t\t\tthis.layers.push(readImageLayer(this, layer, zOrder++));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"tilelayer\":\n\t\t\t\t\tthis.layers.push(readLayer(this, layer, zOrder++));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"objectgroup\":\n\t\t\t\tcase \"group\":\n\t\t\t\t\tthis.objectGroups.push(readObjectGroup(this, layer, zOrder++));\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.initialized = true;\n\t}\n\n\t/**\n\t * add all the map layers and objects to the given container.\n\t * note : this will not automatically update the camera viewport\n\t * @param {Container} container - target container\n\t * @param {boolean} [flatten=true] - if true, flatten all objects into the given container, else a `me.Container` object will be created for each corresponding groups\n\t * @param {boolean} [setViewportBounds=false] - if true, set the viewport bounds to the map size, this should be set to true especially if adding a level to the game world container.\n\t * @example\n\t * // create a new level object based on the TMX JSON object\n\t * let level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));\n\t * // add the level to the game world container\n\t * level.addTo(app.world, true, true);\n\t */\n\taddTo(container, flatten, setViewportBounds) {\n\t\tconst _sort = container.autoSort;\n\t\tconst _depth = container.autoDepth;\n\n\t\tconst levelBounds = this.getBounds();\n\n\t\t// disable auto-sort and auto-depth\n\t\tcontainer.autoSort = false;\n\t\tcontainer.autoDepth = false;\n\n\t\tif (this.backgroundcolor) {\n\t\t\tcontainer.backgroundColor.parseCSS(this.backgroundcolor);\n\t\t}\n\n\t\t// add all layers instances\n\t\tthis.getLayers().forEach((layer) => {\n\t\t\tcontainer.addChild(layer);\n\t\t});\n\n\t\t// add all Object instances\n\t\tthis.getObjects(flatten).forEach((object) => {\n\t\t\tcontainer.addChild(object);\n\t\t});\n\n\t\t// resize the container accordingly\n\t\tcontainer.resize(this.bounds.width, this.bounds.height);\n\n\t\t// sort everything (recursively)\n\t\tcontainer.sort(true);\n\n\t\t/**\n\t\t * callback funtion for the viewport resize event\n\t\t * @ignore\n\t\t */\n\t\tif (setViewportBounds === true) {\n\t\t\tconst app = container.getRootAncestor().app;\n\n\t\t\tfunction _setBounds(width, height) {\n\t\t\t\t// adjust the viewport bounds if level is smaller\n\t\t\t\tapp.viewport.setBounds(\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tMath.max(levelBounds.width, width),\n\t\t\t\t\tMath.max(levelBounds.height, height),\n\t\t\t\t);\n\t\t\t\t// center the map if smaller than the current viewport\n\t\t\t\tconst newX = Math.max(0, ~~((width - levelBounds.width) / 2));\n\t\t\t\tconst newY = Math.max(0, ~~((height - levelBounds.height) / 2));\n\t\t\t\tif (container.pos.x !== newX || container.pos.y !== newY) {\n\t\t\t\t\tcontainer.pos.set(newX, newY, container.pos.z);\n\t\t\t\t\t// A child caches its absolute bounds (centered around\n\t\t\t\t\t// ancestor.absPos at the time of the last `updateBounds`).\n\t\t\t\t\t// When the parent's pos changes, descendants that don't\n\t\t\t\t\t// move on their own (TMX layers, static collision\n\t\t\t\t\t// objects, triggers) keep stale absolute bounds, so\n\t\t\t\t\t// anything that reads them (debug overlay, viewport\n\t\t\t\t\t// culling) sees the pre-centering position. Walk the\n\t\t\t\t\t// tree once after a real move and refresh.\n\t\t\t\t\trefreshAbsoluteBounds(container);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\toff(VIEWPORT_ONRESIZE, _setBounds);\n\t\t\t// force viewport bounds update\n\t\t\t_setBounds(app.viewport.width, app.viewport.height);\n\t\t\t// Replace the resize handler\n\t\t\ton(VIEWPORT_ONRESIZE, _setBounds);\n\t\t}\n\n\t\t//  set back auto-sort and auto-depth\n\t\tcontainer.autoSort = _sort;\n\t\tcontainer.autoDepth = _depth;\n\t}\n\n\t/**\n\t * Return an Array of instantiated objects, based on the map object definition.\n\t * Objects are created using the Tiled object factory registry, which dispatches\n\t * to the appropriate factory based on the object's class, name, or structure.\n\t * Custom factories can be registered via {@link registerTiledObjectFactory} or\n\t * {@link registerTiledObjectClass}.\n\t * @param {boolean} [flatten=true] - if true, flatten all objects into the returned array.\n\t * when false, a `me.Container` object will be created for each corresponding groups\n\t * @returns {Renderable[]} Array of Objects\n\t */\n\tgetObjects(flatten) {\n\t\tconst objects = [];\n\t\tlet targetContainer;\n\n\t\t// parse the map for objects\n\t\tthis.readMapObjects(this.data);\n\n\t\tfor (let g = 0; g < this.objectGroups.length; g++) {\n\t\t\tconst group = this.objectGroups[g];\n\n\t\t\t// check if this is the collision shape group\n\t\t\tconst isCollisionGroup = group.name\n\t\t\t\t.toLowerCase()\n\t\t\t\t.includes(COLLISION_GROUP);\n\n\t\t\tif (flatten === false) {\n\t\t\t\t// create a new container\n\t\t\t\ttargetContainer = new Container(0, 0, this.width, this.height);\n\n\t\t\t\t// tiled uses 0,0 by default\n\t\t\t\ttargetContainer.anchorPoint.set(0, 0);\n\n\t\t\t\t// set additional properties\n\t\t\t\ttargetContainer.name = group.name;\n\t\t\t\ttargetContainer.pos.z = group.z;\n\t\t\t\ttargetContainer.setOpacity(group.opacity);\n\t\t\t\ttargetContainer.blendMode = group.blendMode;\n\n\t\t\t\t// disable auto-sort and auto-depth\n\t\t\t\ttargetContainer.autoSort = false;\n\t\t\t\ttargetContainer.autoDepth = false;\n\t\t\t}\n\n\t\t\t// iterate through the group and add all objects into their\n\t\t\t// corresponding target Container\n\t\t\tfor (let o = 0; o < group.objects.length; o++) {\n\t\t\t\tconst settings = group.objects[o];\n\n\t\t\t\t// Tiled uses 0,0 by default\n\t\t\t\tif (typeof settings.anchorPoint === \"undefined\") {\n\t\t\t\t\tsettings.anchorPoint = { x: 0, y: 0 };\n\t\t\t\t}\n\t\t\t\t// convert to melonJS renderable argument name\n\t\t\t\tif (typeof settings.tintcolor !== \"undefined\") {\n\t\t\t\t\tsettings.tint = parseTintColor(settings.tintcolor);\n\t\t\t\t}\n\n\t\t\t\t// instantiate the object based on its type\n\t\t\t\tconst obj = createTMXObject(settings, this);\n\n\t\t\t\t// configure collision on unnamed objects in collision groups.\n\t\t\t\t// Factory output uses `bodyDef` (engine-portable, declarative).\n\t\t\t\t// We rewrite the def fields so when the world auto-registers\n\t\t\t\t// the body, it picks up WORLD_SHAPE / static. Legacy objects\n\t\t\t\t// that still carry a pre-constructed `body` are patched\n\t\t\t\t// in-place for backward compatibility.\n\t\t\t\tif (isCollisionGroup && !settings.name) {\n\t\t\t\t\tif (obj.bodyDef) {\n\t\t\t\t\t\tobj.bodyDef.collisionType = collision.types.WORLD_SHAPE;\n\t\t\t\t\t\tobj.bodyDef.type = \"static\";\n\t\t\t\t\t} else if (obj.body) {\n\t\t\t\t\t\tobj.body.collisionType = collision.types.WORLD_SHAPE;\n\t\t\t\t\t\tobj.body.isStatic = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// apply per-object opacity and visibility (skip TMXLayer)\n\t\t\t\tif (obj.isRenderable === true && !(settings instanceof TMXLayer)) {\n\t\t\t\t\tif (!settings.visible) {\n\t\t\t\t\t\tobj.setOpacity(0);\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof obj.renderable !== \"undefined\" &&\n\t\t\t\t\t\t\tobj.renderable.isRenderable === true\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tobj.renderable.setOpacity(0);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (settings.opacity < 1) {\n\t\t\t\t\t\tapplyObjectOpacity(obj, settings.opacity);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// propagate group blend mode to child objects\n\t\t\t\tpropagateBlendMode(obj, group.blendMode);\n\n\t\t\t\tif (flatten !== false) {\n\t\t\t\t\t// apply group opacity when flattened\n\t\t\t\t\tif (obj.isRenderable === true) {\n\t\t\t\t\t\tapplyObjectOpacity(obj, group.opacity);\n\t\t\t\t\t}\n\t\t\t\t\tobjects.push(obj);\n\t\t\t\t} else {\n\t\t\t\t\ttargetContainer.addChild(obj);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if we created a new container with children\n\t\t\tif (flatten === false && targetContainer.getChildren().length > 0) {\n\t\t\t\t// re-enable auto-sort and auto-depth\n\t\t\t\ttargetContainer.autoSort = true;\n\t\t\t\ttargetContainer.autoDepth = true;\n\n\t\t\t\t// add our container to the world\n\t\t\t\tobjects.push(targetContainer);\n\t\t\t}\n\t\t}\n\t\treturn objects;\n\t}\n\n\t/**\n\t * return all the existing layers\n\t * @returns {TMXLayer[]} Array of Layers\n\t */\n\tgetLayers() {\n\t\t// parse the map for objects\n\t\tthis.readMapObjects(this.data);\n\t\treturn this.layers;\n\t}\n\n\t/**\n\t * destroy function, clean all allocated objects\n\t */\n\tdestroy() {\n\t\tthis.tilesets = undefined;\n\t\tthis.layers.length = 0;\n\t\tthis.objectGroups.length = 0;\n\t\tthis.initialized = false;\n\t}\n}\n","import { game } from \"../application/application.ts\";\nimport { getTMX } from \"./../loader/loader.js\";\nimport state from \"./../state/state.ts\";\nimport { emit, LEVEL_LOADED } from \"../system/event.ts\";\nimport { resetGUID } from \"./../utils/utils.ts\";\nimport GLTFScene from \"./gltf/GLTFScene.js\";\nimport TMXTileMap from \"./tiled/TMXTileMap.js\";\n\n// our levels\nconst levels = {};\n// level index table\nconst levelIdx = [];\n// current level index\nlet currentLevelIdx = 0;\n\n/**\n * @ignore\n */\nfunction safeLoadLevel(levelId, options, restart) {\n\t// clean the destination container\n\toptions.container.reset();\n\n\t// reset the renderer\n\tgame.reset();\n\n\t// clean the current (previous) level\n\tif (levels[level.getCurrentLevelId()]) {\n\t\tlevels[level.getCurrentLevelId()].destroy();\n\t}\n\n\t// update current level index\n\tcurrentLevelIdx = levelIdx.indexOf(levelId);\n\n\t// add the specified level to the game world. TMX maps keep their\n\t// dedicated loader (GUID reset + viewport bounds + object flattening);\n\t// other formats (glTF/GLB scenes, …) use the generic duck-typed\n\t// `addTo(container, options)` interface.\n\tconst targetLevel = levels[levelId];\n\tif (targetLevel.format === \"tmx\") {\n\t\tloadTMXLevel(\n\t\t\tlevelId,\n\t\t\toptions.container,\n\t\t\toptions.flatten,\n\t\t\toptions.setViewportBounds,\n\t\t);\n\t} else {\n\t\toptions.container.anchorPoint.set(0, 0);\n\t\ttargetLevel.addTo(options.container, options);\n\t}\n\n\t// publish the corresponding message\n\temit(LEVEL_LOADED, levelId);\n\n\t// fire the callback\n\toptions.onLoaded(levelId);\n\n\tif (restart) {\n\t\t// resume the game loop if it was previously running\n\t\tstate.restart();\n\t}\n}\n\n/**\n * Load a TMX level\n * @name loadTMXLevel\n * @memberof level\n * @private\n * @param {string} levelId - level id\n * @param {Container} container - target container\n * @param {boolean} [flatten=true] - if true, flatten all objects into the given container\n * @param {boolean} [setViewportBounds=false] - if true, set the viewport bounds to the map size, this should be set to true especially if adding a level to the game world container.\n * @ignore\n */\nfunction loadTMXLevel(levelId, container, flatten, setViewportBounds) {\n\tconst level = levels[levelId];\n\t// reset the GUID generator\n\t// and pass the level id as parameter\n\tresetGUID(levelId, level.nextobjectid);\n\n\t// Tiled use 0,0 anchor coordinates\n\tcontainer.anchorPoint.set(0, 0);\n\n\t// add all level elements to the target container\n\tlevel.addTo(container, flatten, setViewportBounds);\n}\n\n/**\n * a level manager. once resources loaded, the level manager contains all references of defined levels.\n * @namespace level\n */\n\nexport const level = {\n\t/**\n\t * add a level into the game manager (usually called by the preloader)\n\t * @name add\n\t * @memberof level\n\t * @public\n\t * @param {string} format - level format (\"tmx\" for Tiled maps, \"gltf\" / \"glb\" for 3D scenes)\n\t * @param {string} levelId - the level id (or name)\n\t * @param {Function} [callback] - a function to be called once the level is loaded\n\t * @returns {boolean} true if the level was loaded\n\t */\n\tadd(format, levelId, callback) {\n\t\tlet levelObject;\n\t\tswitch (format) {\n\t\t\tcase \"tmx\":\n\t\t\t\tlevelObject = () => {\n\t\t\t\t\treturn new TMXTileMap(levelId, getTMX(levelId));\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"gltf\":\n\t\t\tcase \"glb\":\n\t\t\t\tlevelObject = () => {\n\t\t\t\t\treturn new GLTFScene(levelId);\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"no level loader defined for format \" + format);\n\t\t}\n\n\t\t// register the level once (idempotent)\n\t\tif (levels[levelId] == null) {\n\t\t\tlevels[levelId] = levelObject();\n\t\t\tlevelIdx.push(levelId);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\n\t\t// call the callback if defined\n\t\tif (callback) {\n\t\t\tcallback();\n\t\t}\n\t\t// true if level loaded\n\t\treturn true;\n\t},\n\n\t/**\n\t * load a level into the game manager<br>\n\t * (will also create all level defined entities, etc..)\n\t * @name load\n\t * @memberof level\n\t * @public\n\t * @param {string} levelId - level id\n\t * @param {object} [options] - additional optional parameters\n\t * @param {Container} [options.container=game.world] - container in which to load the specified level\n\t * @param {Function} [options.onLoaded=game.onLevelLoaded] - callback for when the level is fully loaded\n\t * @param {boolean} [options.flatten=game.mergeGroup] - (TMX only) if true, flatten all objects into the given container\n\t * @param {boolean} [options.setViewportBounds=true] - (TMX only) if true, set the viewport bounds to the map size\n\t * @param {number} [options.scale=1] - (glTF/GLB only) pixels per glTF unit applied to the whole scene\n\t * @param {boolean} [options.rightHanded=true] - (glTF/GLB only) convert the right-handed (Y-up) source to the engine's Y-down via a rotation rather than a mirror\n\t * @param {boolean} [options.lights=true] - (glTF/GLB only) add the scene's authored `KHR_lights_punctual` lights (plus a soft ambient fill) as {@link Light3d} world children; each carries its authored name for `getChildByName` lookups\n\t * @param {number} [options.lightIntensityScale] - (glTF/GLB only) multiply each light's authored physical intensity (lux/candela) by this factor instead of normalizing it to 1 — see {@link GLTFScene#addTo}\n\t * @param {boolean} [options.castGroundShadow] - (glTF/GLB only) give this scene's meshes a ground shadow ({@link Mesh#castGroundShadow}). Overrides the application's `castGroundShadow` setting for this scene, in both directions; omit it to inherit. As a scene-wide opt-in it skips nodes with no vertical extent — a scene's ground plane is exactly that, and shadowing it with itself smears a blob across the whole floor\n\t * @param {number} [options.shadowGroundY] - (glTF/GLB only) world Y of the floor those shadows land on ({@link Mesh#shadowGroundY}); omit it and each blob sits at its own object's base at full strength, which is right for a scene whose props already rest on the ground\n\t * @returns {boolean} true if the level was successfully loaded\n\t * @example\n\t * // the game assets to be be preloaded\n\t * // TMX maps\n\t * let resources = [\n\t *     {name: \"a4_level1\",   type: \"tmx\",   src: \"data/level/a4_level1.tmx\"},\n\t *     {name: \"a4_level2\",   type: \"tmx\",   src: \"data/level/a4_level2.tmx\"},\n\t *     {name: \"a4_level3\",   type: \"tmx\",   src: \"data/level/a4_level3.tmx\"},\n\t *     // ...\n\t * ];\n\t *\n\t * // ...\n\t *\n\t * // load a level into the game world\n\t * me.level.load(\"a4_level1\");\n\t * ...\n\t * ...\n\t * // load a level into a specific container\n\t * let levelContainer = new me.Container();\n\t * me.level.load(\"a4_level2\", {container:levelContainer});\n\t * // add a simple transformation\n\t * levelContainer.translate(levelContainer.width / 2, levelContainer.height / 2 );\n\t * levelContainer.rotate(0.05);\n\t * levelContainer.translate(-levelContainer.width / 2, -levelContainer.height / 2 );\n\t * // add it to the game world\n\t * app.world.addChild(levelContainer);\n\t *\n\t * // load a glTF/GLB scene (preloaded with type \"glb\") under a Camera3d:\n\t * // 50 pixels per glTF unit, authored lux/candela intensities kept at\n\t * // a 1/1000 scale instead of being normalized to 1\n\t * me.level.load(\"diorama\", { scale: 50, lightIntensityScale: 0.001 });\n\t * // …and give every prop in it a ground shadow landing on the floor at y = 0\n\t * // (the scene's own ground plane is skipped — it has no height to cast)\n\t * me.level.load(\"diorama\", { scale: 50, castGroundShadow: true, shadowGroundY: 0 });\n\t * // the authored lights are world children — grab the sun for a day/night cycle\n\t * const sun = app.world.getChildByName(\"Sun\")[0];\n\t */\n\tload(levelId, options) {\n\t\toptions = Object.assign(\n\t\t\t{\n\t\t\t\tcontainer: game.world,\n\t\t\t\tonLoaded: game.onLevelLoaded,\n\t\t\t\tflatten: game.mergeGroup,\n\t\t\t\tsetViewportBounds: true,\n\t\t\t},\n\t\t\toptions || {},\n\t\t);\n\n\t\t// throw an exception if not existing\n\t\tif (typeof levels[levelId] === \"undefined\") {\n\t\t\tthrow new Error(\"level \" + levelId + \" not found\");\n\t\t}\n\n\t\t// check the status of the state mngr\n\t\tconst wasRunning = state.isRunning();\n\n\t\tif (wasRunning) {\n\t\t\t// stop the game loop to avoid\n\t\t\t// some silly side effects\n\t\t\tstate.stop();\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tsafeLoadLevel(levelId, options, true);\n\t\t\t});\n\t\t} else {\n\t\t\tsafeLoadLevel(levelId, options);\n\t\t}\n\t\treturn true;\n\t},\n\n\t/**\n\t * return the current level id<br>\n\t * @name getCurrentLevelId\n\t * @memberof level\n\t * @public\n\t * @returns {string}\n\t */\n\tgetCurrentLevelId() {\n\t\treturn levelIdx[currentLevelIdx];\n\t},\n\n\t/**\n\t * return the current level definition.\n\t * for a reference to the live instantiated level,\n\t * rather use the container in which it was loaded (e.g. app.world)\n\t * @name getCurrentLevel\n\t * @memberof level\n\t * @public\n\t * @returns {TMXTileMap|GLTFScene} the current level object (a TMXTileMap for Tiled maps, a GLTFScene for glTF/GLB scenes)\n\t */\n\tgetCurrentLevel() {\n\t\treturn levels[this.getCurrentLevelId()];\n\t},\n\n\t/**\n\t * reload the current level\n\t * @name reload\n\t * @memberof level\n\t * @public\n\t * @param {object} [options] - additional optional parameters\n\t * @param {Container} [options.container=game.world] - container in which to load the specified level\n\t * @param {Function} [options.onLoaded=game.onLevelLoaded] - callback for when the level is fully loaded\n\t * @param {boolean} [options.flatten=game.mergeGroup] - if true, flatten all objects into the given container\n\t * @returns {object} the current level\n\t */\n\treload(options) {\n\t\t// reset the level to initial state\n\t\t//levels[currentLevel].reset();\n\t\treturn this.load(this.getCurrentLevelId(), options);\n\t},\n\n\t/**\n\t * load the next level\n\t * @name next\n\t * @memberof level\n\t * @public\n\t * @param {object} [options] - additional optional parameters\n\t * @param {Container} [options.container=game.world] - container in which to load the specified level\n\t * @param {Function} [options.onLoaded=game.onLevelLoaded] - callback for when the level is fully loaded\n\t * @param {boolean} [options.flatten=game.mergeGroup] - if true, flatten all objects into the given container\n\t * @returns {boolean} true if the next level was successfully loaded\n\t */\n\tnext(options) {\n\t\t//go to the next level\n\t\tif (currentLevelIdx + 1 < levelIdx.length) {\n\t\t\treturn this.load(levelIdx[currentLevelIdx + 1], options);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t},\n\n\t/**\n\t * load the previous level<br>\n\t * @name previous\n\t * @memberof level\n\t * @public\n\t * @param {object} [options] - additional optional parameters\n\t * @param {Container} [options.container=game.world] - container in which to load the specified level\n\t * @param {Function} [options.onLoaded=game.onLevelLoaded] - callback for when the level is fully loaded\n\t * @param {boolean} [options.flatten=game.mergeGroup] - if true, flatten all objects into the given container\n\t * @returns {boolean} true if the previous level was successfully loaded\n\t */\n\tprevious(options) {\n\t\t// go to previous level\n\t\tif (currentLevelIdx - 1 >= 0) {\n\t\t\treturn this.load(levelIdx[currentLevelIdx - 1], options);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t},\n\n\t/**\n\t * return the amount of level preloaded\n\t * @name levelCount\n\t * @memberof level\n\t * @public\n\t * @returns {number} the amount of level preloaded\n\t */\n\tlevelCount() {\n\t\treturn levelIdx.length;\n\t},\n};\n","import { level } from \"../../level/level.js\";\nimport { transformedBounds } from \"../../math/vertex.ts\";\nimport { gltfList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\nimport { specularFromMetallicRoughness } from \"./pbr.ts\";\n\n/**\n * glTF 2.0 (.gltf / .glb) scene loader — Tier 1.\n *\n * Parses a glTF asset into a flat list of mesh nodes with their world\n * transforms, geometry (positions / uvs / indices), and decoded baseColor\n * texture, ready to instantiate as melonJS {@link Mesh} renderables.\n *\n * Tier 1 scope: static node graph + mesh primitives (POSITION / TEXCOORD_0\n * / indices) + pbrMetallicRoughness.baseColorTexture (and baseColorFactor) +\n * node TRS animation (translation / rotation / scale channels, the rigid\n * hierarchical animation used by e.g. Kenney's blocky characters).\n * Out of scope: skinning (vertex skinning / JOINTS_0 / WEIGHTS_0), morph\n * targets, full PBR maps, KHR extensions, Draco compression.\n * @ignore\n */\n\n// glTF componentType -> TypedArray + DataView reader\nconst COMPONENT = {\n\t5120: { array: Int8Array, size: 1, get: \"getInt8\" },\n\t5121: { array: Uint8Array, size: 1, get: \"getUint8\" },\n\t5122: { array: Int16Array, size: 2, get: \"getInt16\" },\n\t5123: { array: Uint16Array, size: 2, get: \"getUint16\" },\n\t5125: { array: Uint32Array, size: 4, get: \"getUint32\" },\n\t5126: { array: Float32Array, size: 4, get: \"getFloat32\" },\n};\n// glTF accessor type -> component count\nconst TYPE_COUNT = {\n\tSCALAR: 1,\n\tVEC2: 2,\n\tVEC3: 3,\n\tVEC4: 4,\n\tMAT2: 4,\n\tMAT3: 9,\n\tMAT4: 16,\n};\n\n/**\n * Split a GLB binary container into its JSON description and binary buffer.\n * @param {ArrayBuffer} arrayBuffer\n * @returns {{ json: object, bin: Uint8Array | null }}\n * @ignore\n */\nexport function parseGLB(arrayBuffer) {\n\tconst dv = new DataView(arrayBuffer);\n\tconst magic = dv.getUint32(0, true);\n\t// 0x46546C67 === \"glTF\"\n\tif (magic !== 0x46546c67) {\n\t\t// not a binary container — assume a JSON .gltf\n\t\tconst json = JSON.parse(new TextDecoder().decode(arrayBuffer));\n\t\treturn { json, bin: null };\n\t}\n\tconst length = dv.getUint32(8, true);\n\tlet offset = 12;\n\tlet json = null;\n\tlet bin = null;\n\twhile (offset < length) {\n\t\tconst chunkLength = dv.getUint32(offset, true);\n\t\tconst chunkType = dv.getUint32(offset + 4, true);\n\t\tconst start = offset + 8;\n\t\tif (chunkType === 0x4e4f534a) {\n\t\t\t// \"JSON\"\n\t\t\tjson = JSON.parse(\n\t\t\t\tnew TextDecoder().decode(\n\t\t\t\t\tnew Uint8Array(arrayBuffer, start, chunkLength),\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (chunkType === 0x004e4942) {\n\t\t\t// \"BIN\\0\"\n\t\t\tbin = new Uint8Array(arrayBuffer, start, chunkLength);\n\t\t}\n\t\toffset = start + chunkLength;\n\t}\n\treturn { json, bin };\n}\n\n/**\n * Resolve a glTF relative resource URI (external `.bin` / image) against the\n * asset's own URL, the same way a browser resolves a relative `<img src>`.\n * Returns an absolute URL string, or `null` when the asset URL is unknown\n * (e.g. a GLB parsed straight from an ArrayBuffer in a test) — the caller then\n * fails with a clear \"external resource\" message instead of fetching garbage.\n * @ignore\n */\nfunction resolveURI(uri, baseURI) {\n\tif (baseURI === undefined || baseURI === null) {\n\t\treturn null;\n\t}\n\t// `new URL` handles ./, ../, %20-encoding and absolute base normalization.\n\t// Resolve the (possibly relative) asset URL against the document first so a\n\t// page-relative `data.src` like \"assets/x.gltf\" becomes absolute.\n\tconst absoluteBase = new URL(\n\t\tbaseURI,\n\t\ttypeof document !== \"undefined\" ? document.baseURI : undefined,\n\t);\n\treturn new URL(uri, absoluteBase).href;\n}\n\n/**\n * Decode a single base64 `data:` URI payload into a Uint8Array.\n * @ignore\n */\nfunction decodeDataURI(uri) {\n\tconst base64 = uri.slice(uri.indexOf(\",\") + 1);\n\tconst binary = atob(base64);\n\tconst out = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tout[i] = binary.charCodeAt(i);\n\t}\n\treturn out;\n}\n\n/**\n * Resolve every glTF buffer to a Uint8Array. Handles the GLB binary chunk\n * (no uri), embedded `data:` URIs, and external `.bin` files fetched relative\n * to the asset URL (`baseURI`). Async because external buffers are fetched.\n * @ignore\n */\nfunction resolveBuffers(json, bin, baseURI, settings) {\n\treturn Promise.all(\n\t\t(json.buffers || []).map((buffer) => {\n\t\t\tif (buffer.uri === undefined) {\n\t\t\t\treturn bin;\n\t\t\t}\n\t\t\tif (buffer.uri.startsWith(\"data:\")) {\n\t\t\t\treturn decodeDataURI(buffer.uri);\n\t\t\t}\n\t\t\t// external .bin — fetch it relative to the asset URL\n\t\t\tconst url = resolveURI(buffer.uri, baseURI);\n\t\t\tif (url === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`glTF: external buffer \"${buffer.uri}\" cannot be resolved without the asset URL`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn fetchData(url, \"arrayBuffer\", settings).then((ab) => {\n\t\t\t\treturn new Uint8Array(ab);\n\t\t\t});\n\t\t}),\n\t);\n}\n\n/**\n * Read an accessor into a flat TypedArray (stride-aware, non-interleaved\n * fast-path covered as a subset).\n * @ignore\n */\nexport function readAccessor(json, buffers, accessorIndex) {\n\tconst accessor = json.accessors[accessorIndex];\n\tconst comp = COMPONENT[accessor.componentType];\n\tconst numComp = TYPE_COUNT[accessor.type];\n\tif (comp === undefined || numComp === undefined) {\n\t\tthrow new Error(\n\t\t\t`glTF: unsupported accessor (componentType ${accessor.componentType}, type \"${accessor.type}\")`,\n\t\t);\n\t}\n\t// An accessor with no bufferView is sparse-only or zero-initialized (out of\n\t// Tier 1 scope). Fail with a clear message rather than a cryptic\n\t// \"cannot read byteStride of undefined\" further down.\n\tif (accessor.bufferView === undefined) {\n\t\tthrow new Error(\n\t\t\t`glTF: accessor ${accessorIndex} has no bufferView (sparse / zero-initialized accessors are not supported)`,\n\t\t);\n\t}\n\tconst view = json.bufferViews[accessor.bufferView];\n\tconst elementSize = comp.size * numComp;\n\tconst stride = view.byteStride || elementSize;\n\tconst bytes = buffers[view.buffer];\n\tconst dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\tconst base = (view.byteOffset || 0) + (accessor.byteOffset || 0);\n\tconst TypedArrayCtor = comp.array;\n\tconst out = new TypedArrayCtor(accessor.count * numComp);\n\tfor (let i = 0; i < accessor.count; i++) {\n\t\tconst elementOffset = base + i * stride;\n\t\tfor (let c = 0; c < numComp; c++) {\n\t\t\tout[i * numComp + c] = dv[comp.get](elementOffset + c * comp.size, true);\n\t\t}\n\t}\n\treturn out;\n}\n\n/**\n * Read an `EXT_mesh_gpu_instancing` node's per-instance TRS accessors.\n *\n * The extension stores up to three parallel accessors — `TRANSLATION`\n * (VEC3), `ROTATION` (VEC4 quaternion) and `SCALE` (VEC3) — each with one\n * element per instance. Any of them may be absent, in which case that\n * component takes its glTF default (no translation, identity rotation, unit\n * scale).\n *\n * `ROTATION` may be stored as normalized `BYTE` or `SHORT` as well as float\n * (the spec allows it, and exporters use it to shrink large scatters).\n * `readAccessor` returns raw component values, so those encodings are scaled\n * back to [-1, 1] here — read raw, a byte-encoded quaternion would arrive as\n * values up to 127 and every instance would be flung out of the scene.\n * @param {object} json - the glTF document\n * @param {ArrayBuffer[]} buffers - the resolved binary buffers\n * @param {object} attributes - the extension's `attributes` map\n * @returns {object|undefined} `{count, translation, rotation, scale}`, or undefined when empty\n * @ignore\n */\n/**\n * Scale a normalized-integer quaternion accessor back to [-1, 1].\n * @param {ArrayLike<number>} raw - the raw component values\n * @param {number} componentType - the accessor's glTF componentType\n * @returns {ArrayLike<number>} the de-normalized quaternion components\n * @ignore\n */\nfunction normalizeQuaternions(raw, componentType) {\n\t// float components are already in range\n\tif (componentType === 5126) {\n\t\treturn raw;\n\t}\n\t// signed normalized: c / MAX, clamped at -1 (the spec's rule for the\n\t// extra negative value two's complement allows)\n\tconst max = componentType === 5120 ? 127 : componentType === 5122 ? 32767 : 0;\n\tif (max === 0) {\n\t\t// An unsigned encoding cannot represent a quaternion's negative\n\t\t// components. Passing it through raw is not \"looking wrong\" — a\n\t\t// component of 255 makes the basis blow up by ~1e5 and destroys the\n\t\t// scene — so fail loudly, as readAccessor does for a bad componentType.\n\t\tthrow new Error(\n\t\t\t`glTF: EXT_mesh_gpu_instancing ROTATION must be float or normalized signed byte/short (componentType ${componentType})`,\n\t\t);\n\t}\n\tconst out = new Float32Array(raw.length);\n\tfor (let i = 0; i < raw.length; i++) {\n\t\tout[i] = Math.max(raw[i] / max, -1);\n\t}\n\treturn out;\n}\n\n/**\n * @param {object} json - the glTF document\n * @param {ArrayBuffer[]} buffers - the resolved binary buffers\n * @param {object} attributes - the extension's `attributes` map\n * @returns {object|undefined} `{count, translation, rotation, scale}`, or undefined when empty\n * @ignore\n */\nfunction readInstanceAttributes(json, buffers, attributes) {\n\tif (!attributes) {\n\t\treturn undefined;\n\t}\n\t// The extension pins the accessor types; a mismatch would divide by the\n\t// wrong component count and yield a fractional instance count, which\n\t// truncates one way when allocating and another when filling.\n\tconst expect = (name, type) => {\n\t\tconst index = attributes[name];\n\t\tif (index === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst accessor = json.accessors[index];\n\t\tif (accessor.type !== type) {\n\t\t\tthrow new Error(\n\t\t\t\t`glTF: EXT_mesh_gpu_instancing ${name} must be ${type} (accessor ${index} is ${accessor.type})`,\n\t\t\t);\n\t\t}\n\t\tif (accessor.sparse !== undefined) {\n\t\t\t// readAccessor ignores sparse overrides, so the instances would\n\t\t\t// silently land at their base-buffer positions\n\t\t\tthrow new Error(\n\t\t\t\t`glTF: EXT_mesh_gpu_instancing ${name} uses a sparse accessor, which is not supported`,\n\t\t\t);\n\t\t}\n\t};\n\texpect(\"TRANSLATION\", \"VEC3\");\n\texpect(\"ROTATION\", \"VEC4\");\n\texpect(\"SCALE\", \"VEC3\");\n\n\tconst translation =\n\t\tattributes.TRANSLATION !== undefined\n\t\t\t? readAccessor(json, buffers, attributes.TRANSLATION)\n\t\t\t: undefined;\n\tconst rotation =\n\t\tattributes.ROTATION !== undefined\n\t\t\t? normalizeQuaternions(\n\t\t\t\t\treadAccessor(json, buffers, attributes.ROTATION),\n\t\t\t\t\tjson.accessors[attributes.ROTATION].componentType,\n\t\t\t\t)\n\t\t\t: undefined;\n\tconst scale =\n\t\tattributes.SCALE !== undefined\n\t\t\t? readAccessor(json, buffers, attributes.SCALE)\n\t\t\t: undefined;\n\n\t// The spec requires every supplied accessor to have the same count. Take\n\t// the MINIMUM rather than the first present one: a truncated export would\n\t// otherwise read past the end of the short accessor, and `undefined`\n\t// arithmetic writes NaN records — instances that silently vanish on the\n\t// GPU while the rest render.\n\tconst counts = [];\n\tif (translation !== undefined) {\n\t\tcounts.push(translation.length / 3);\n\t}\n\tif (rotation !== undefined) {\n\t\tcounts.push(rotation.length / 4);\n\t}\n\tif (scale !== undefined) {\n\t\tcounts.push(scale.length / 3);\n\t}\n\tconst count = counts.length > 0 ? Math.floor(Math.min(...counts)) : 0;\n\tif (counts.length > 1 && Math.min(...counts) !== Math.max(...counts)) {\n\t\tconsole.warn(\n\t\t\t`glTF: EXT_mesh_gpu_instancing attributes disagree on count (${counts.join(\", \")}) — using ${count}`,\n\t\t);\n\t}\n\tif (counts.length === 0) {\n\t\t// the extension is declared with no attributes at all — nothing to\n\t\t// instance, so the node stays an ordinary single mesh\n\t\treturn undefined;\n\t}\n\treturn { count, translation, rotation, scale };\n}\n\n/**\n * Read a `COLOR_0` accessor into a packed ARGB Uint32 per vertex (the format\n * {@link Mesh}'s `vertexColors` consumes). Handles `VEC3` (alpha defaults to 1)\n * and `VEC4`, and the three glTF color encodings: float `0..1`, and normalized\n * `UNSIGNED_BYTE` / `UNSIGNED_SHORT`.\n * @ignore\n */\nfunction readVertexColors(json, buffers, accessorIndex) {\n\tconst accessor = json.accessors[accessorIndex];\n\tconst raw = readAccessor(json, buffers, accessorIndex);\n\tconst numComp = TYPE_COUNT[accessor.type]; // 3 (rgb) or 4 (rgba)\n\t// normalize integer encodings to 0..1; float is already 0..1\n\tconst div =\n\t\taccessor.componentType === 5121\n\t\t\t? 255\n\t\t\t: accessor.componentType === 5123\n\t\t\t\t? 65535\n\t\t\t\t: 1;\n\tconst out = new Uint32Array(accessor.count);\n\tfor (let i = 0; i < accessor.count; i++) {\n\t\tconst o = i * numComp;\n\t\tconst r = Math.round((raw[o] / div) * 255);\n\t\tconst g = Math.round((raw[o + 1] / div) * 255);\n\t\tconst b = Math.round((raw[o + 2] / div) * 255);\n\t\tconst a = numComp === 4 ? Math.round((raw[o + 3] / div) * 255) : 255;\n\t\t// ARGB packed (A<<24 | R<<16 | G<<8 | B), matching Color.toUint32\n\t\tout[i] = ((a << 24) | (r << 16) | (g << 8) | b) >>> 0;\n\t}\n\treturn out;\n}\n\n// ---- 4x4 column-major matrix helpers (glTF convention) ----\n\nconst IDENTITY = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];\n\n/**\n * Compose a column-major 4x4 local matrix from translation / rotation\n * (quaternion) / scale arrays — the glTF TRS convention — writing into `out`.\n * In-place so the per-frame animation pose path allocates nothing.\n * @param {number[]} out - 16-element destination (returned)\n * @param {number[]} translation - [tx, ty, tz]\n * @param {number[]} rotation - quaternion [qx, qy, qz, qw]\n * @param {number[]} scale - [sx, sy, sz]\n * @returns {number[]} `out`\n * @ignore\n */\nexport function composeTRSInto(out, translation, rotation, scale) {\n\tconst [tx, ty, tz] = translation;\n\tconst [qx, qy, qz, qw] = rotation;\n\tconst [sx, sy, sz] = scale;\n\tconst x2 = qx + qx;\n\tconst y2 = qy + qy;\n\tconst z2 = qz + qz;\n\tconst xx = qx * x2;\n\tconst xy = qx * y2;\n\tconst xz = qx * z2;\n\tconst yy = qy * y2;\n\tconst yz = qy * z2;\n\tconst zz = qz * z2;\n\tconst wx = qw * x2;\n\tconst wy = qw * y2;\n\tconst wz = qw * z2;\n\tout[0] = (1 - (yy + zz)) * sx;\n\tout[1] = (xy + wz) * sx;\n\tout[2] = (xz - wy) * sx;\n\tout[3] = 0;\n\tout[4] = (xy - wz) * sy;\n\tout[5] = (1 - (xx + zz)) * sy;\n\tout[6] = (yz + wx) * sy;\n\tout[7] = 0;\n\tout[8] = (xz + wy) * sz;\n\tout[9] = (yz - wx) * sz;\n\tout[10] = (1 - (xx + yy)) * sz;\n\tout[11] = 0;\n\tout[12] = tx;\n\tout[13] = ty;\n\tout[14] = tz;\n\tout[15] = 1;\n\treturn out;\n}\n\n/**\n * Allocating form of {@link composeTRSInto} — returns a fresh 16-element array.\n * @param {number[]} translation - [tx, ty, tz]\n * @param {number[]} rotation - quaternion [qx, qy, qz, qw]\n * @param {number[]} scale - [sx, sy, sz]\n * @returns {number[]} 16-element column-major matrix\n * @ignore\n */\nexport function composeTRS(translation, rotation, scale) {\n\treturn composeTRSInto(new Array(16), translation, rotation, scale);\n}\n\n/** Compose a node's local matrix from its `matrix` or TRS fields. @ignore */\nexport function nodeLocalMatrix(node) {\n\tif (node.matrix) {\n\t\treturn node.matrix.slice();\n\t}\n\treturn composeTRS(\n\t\tnode.translation || [0, 0, 0],\n\t\tnode.rotation || [0, 0, 0, 1],\n\t\tnode.scale || [1, 1, 1],\n\t);\n}\n\n/**\n * Synthesize per-vertex normals for a primitive that lacks a `NORMAL`\n * accessor: accumulate each triangle's face normal onto its three vertices,\n * then normalize. Shared vertices end up area-weighted (smooth); isolated\n * faces stay flat. A degenerate (zero-length) result falls back to +Y.\n * @param {Float32Array} positions - x,y,z triplets\n * @param {Uint16Array|Uint32Array} indices - triangle indices\n * @param {number} vertexCount\n * @returns {Float32Array} x,y,z normals, one per vertex\n * @ignore\n */\nfunction computeFlatNormals(positions, indices, vertexCount) {\n\tconst normals = new Float32Array(vertexCount * 3);\n\tfor (let i = 0; i < indices.length; i += 3) {\n\t\tconst a = indices[i] * 3;\n\t\tconst b = indices[i + 1] * 3;\n\t\tconst c = indices[i + 2] * 3;\n\t\tconst e1x = positions[b] - positions[a];\n\t\tconst e1y = positions[b + 1] - positions[a + 1];\n\t\tconst e1z = positions[b + 2] - positions[a + 2];\n\t\tconst e2x = positions[c] - positions[a];\n\t\tconst e2y = positions[c + 1] - positions[a + 1];\n\t\tconst e2z = positions[c + 2] - positions[a + 2];\n\t\t// face normal = e1 × e2 (unnormalized → area-weighted accumulation)\n\t\tconst nx = e1y * e2z - e1z * e2y;\n\t\tconst ny = e1z * e2x - e1x * e2z;\n\t\tconst nz = e1x * e2y - e1y * e2x;\n\t\t// accumulate the (area-weighted) face normal onto each of the 3\n\t\t// vertices — unrolled to avoid a per-triangle array allocation\n\t\tnormals[a] += nx;\n\t\tnormals[a + 1] += ny;\n\t\tnormals[a + 2] += nz;\n\t\tnormals[b] += nx;\n\t\tnormals[b + 1] += ny;\n\t\tnormals[b + 2] += nz;\n\t\tnormals[c] += nx;\n\t\tnormals[c + 1] += ny;\n\t\tnormals[c + 2] += nz;\n\t}\n\tfor (let i = 0; i < normals.length; i += 3) {\n\t\tconst x = normals[i];\n\t\tconst y = normals[i + 1];\n\t\tconst z = normals[i + 2];\n\t\tconst len = Math.hypot(x, y, z);\n\t\tif (len > 1e-8) {\n\t\t\tnormals[i] = x / len;\n\t\t\tnormals[i + 1] = y / len;\n\t\t\tnormals[i + 2] = z / len;\n\t\t} else {\n\t\t\tnormals[i] = 0;\n\t\t\tnormals[i + 1] = 1;\n\t\t\tnormals[i + 2] = 0;\n\t\t}\n\t}\n\treturn normals;\n}\n\n/** Normalize a 3-component vector (returns +Y on a zero-length input). @ignore */\nfunction normalize3(v) {\n\tconst len = Math.hypot(v[0], v[1], v[2]);\n\treturn len > 1e-8 ? [v[0] / len, v[1] / len, v[2] / len] : [0, 1, 0];\n}\n\n/**\n * Column-major 4x4 multiply `a * b`, writing into `out`. `out` must NOT alias\n * `a` or `b` (results are written as they're computed). In-place so the\n * per-frame pose path allocates nothing.\n * @ignore\n */\nexport function multiplyMatrixInto(out, a, b) {\n\tfor (let col = 0; col < 4; col++) {\n\t\tfor (let row = 0; row < 4; row++) {\n\t\t\tout[col * 4 + row] =\n\t\t\t\ta[0 * 4 + row] * b[col * 4 + 0] +\n\t\t\t\ta[1 * 4 + row] * b[col * 4 + 1] +\n\t\t\t\ta[2 * 4 + row] * b[col * 4 + 2] +\n\t\t\t\ta[3 * 4 + row] * b[col * 4 + 3];\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Allocating form of {@link multiplyMatrixInto}: `a * b` → fresh array. @ignore */\nexport function multiplyMatrix(a, b) {\n\treturn multiplyMatrixInto(new Array(16), a, b);\n}\n\n/**\n * Decode a glTF image. Handles the three sources: an embedded `bufferView`,\n * an inline `data:` URI, and an external image file referenced by relative\n * `uri` (resolved against the asset URL `baseURI`).\n *\n * Resolves an `ImageBitmap` wherever the platform provides one — matching\n * what the ordinary image loader (`parsers/image.js`) produces, so every\n * texture in the engine reaches the GPU as the same, fully-decoded kind of\n * source. That consistency is load-bearing rather than cosmetic: an\n * `HTMLImageElement` decoded from an INDEXED (PNG `colorType` 3) file is\n * uploaded by WebKit's `copyExternalImageToTexture` as raw palette indices,\n * so the texture arrives greyscale. An `ImageBitmap` is RGBA by definition\n * and has no such ambiguity. Nothing in the WebGPU API can correct this at\n * upload time — `flipY` / `premultipliedAlpha` / `colorSpace` are the only\n * knobs, and palette expansion belongs to the decoder.\n * @returns {Promise<ImageBitmap|HTMLImageElement>}\n * @ignore\n */\nfunction decodeImage(json, buffers, imageIndex, baseURI, settings) {\n\tconst image = json.images[imageIndex];\n\tlet blob;\n\tif (image.bufferView !== undefined) {\n\t\tconst view = json.bufferViews[image.bufferView];\n\t\tconst bytes = buffers[view.buffer];\n\t\tconst slice = bytes.subarray(\n\t\t\tview.byteOffset || 0,\n\t\t\t(view.byteOffset || 0) + view.byteLength,\n\t\t);\n\t\tblob = new Blob([slice], { type: image.mimeType || \"image/png\" });\n\t} else if (image.uri && image.uri.startsWith(\"data:\")) {\n\t\tif (typeof globalThis.createImageBitmap === \"function\") {\n\t\t\treturn fetch(image.uri)\n\t\t\t\t.then((r) => {\n\t\t\t\t\treturn r.blob();\n\t\t\t\t})\n\t\t\t\t.then(decodeBlob)\n\t\t\t\t.catch(() => {\n\t\t\t\t\treturn loadImageFromUrl(image.uri);\n\t\t\t\t});\n\t\t}\n\t\treturn loadImageFromUrl(image.uri);\n\t} else if (image.uri) {\n\t\t// external image file — resolve relative to the asset URL and let the\n\t\t// browser fetch it directly (no object-URL to revoke). Forward the\n\t\t// loader's crossOrigin so a cross-origin texture isn't tainted (which\n\t\t// would throw on the WebGL `texImage2D` upload); same-origin is\n\t\t// unaffected.\n\t\tconst url = resolveURI(image.uri, baseURI);\n\t\tif (url === null) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew Error(\n\t\t\t\t\t`glTF: external image \"${image.uri}\" cannot be resolved without the asset URL`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\ttypeof globalThis.createImageBitmap === \"function\" &&\n\t\t\ttypeof settings?.crossOrigin !== \"string\"\n\t\t) {\n\t\t\treturn fetch(url)\n\t\t\t\t.then((r) => {\n\t\t\t\t\tif (!r.ok) {\n\t\t\t\t\t\tthrow new Error(`glTF: failed to fetch image (${r.status})`);\n\t\t\t\t\t}\n\t\t\t\t\treturn r.blob();\n\t\t\t\t})\n\t\t\t\t.then(decodeBlob)\n\t\t\t\t.catch(() => {\n\t\t\t\t\treturn loadImageFromUrl(url, false, settings?.crossOrigin);\n\t\t\t\t});\n\t\t}\n\t\treturn loadImageFromUrl(url, false, settings?.crossOrigin);\n\t} else {\n\t\treturn Promise.reject(new Error(\"glTF: unsupported image source\"));\n\t}\n\treturn decodeBlob(blob);\n}\n\n/**\n * Decode a Blob to an ImageBitmap, falling back to the element path on a\n * platform without `createImageBitmap`.\n * @ignore\n */\nfunction decodeBlob(blob) {\n\tconst viaElement = () => {\n\t\t// `revoke: true` — the blob URL is a transient handle, only needed\n\t\t// until the image has decoded\n\t\treturn loadImageFromUrl(URL.createObjectURL(blob), true);\n\t};\n\tif (typeof globalThis.createImageBitmap !== \"function\") {\n\t\treturn viaElement();\n\t}\n\t// Fall back rather than fail: `createImageBitmap` is STRICTER than the\n\t// element path (it rejects sources the element decodes leniently), and a\n\t// stricter loader would be a regression. This can only ever upgrade a\n\t// decode, never break one.\n\treturn globalThis.createImageBitmap(blob).catch(viaElement);\n}\n\n/** @ignore */\nfunction loadImageFromUrl(url, revoke = false, crossOrigin) {\n\treturn new Promise((resolve, reject) => {\n\t\tconst img = new Image();\n\t\t// must be set before `src` to take effect; only for real (non-blob,\n\t\t// non-data) URLs that may be cross-origin\n\t\tif (typeof crossOrigin === \"string\") {\n\t\t\timg.crossOrigin = crossOrigin;\n\t\t}\n\t\timg.onload = () => {\n\t\t\tif (revoke) {\n\t\t\t\tURL.revokeObjectURL(url);\n\t\t\t}\n\t\t\tresolve(img);\n\t\t};\n\t\timg.onerror = () => {\n\t\t\tif (revoke) {\n\t\t\t\tURL.revokeObjectURL(url);\n\t\t\t}\n\t\t\treject(new Error(\"glTF: failed to decode image\"));\n\t\t};\n\t\timg.src = url;\n\t});\n}\n\n/**\n * Parse a glTF/GLB ArrayBuffer into a flat, instantiable scene descriptor.\n * @param {ArrayBuffer} arrayBuffer - the .glb / .gltf bytes\n * @param {string} [baseURI] - the asset's own URL, used to resolve external\n * `.bin` buffers and image files referenced by relative `uri`. Omit for a fully\n * self-contained GLB (embedded buffers + data-URI / bufferView images).\n * @param {object} [settings] - loader settings forwarded to `fetchData` for\n * external resources (crossOrigin / withCredentials / nocache).\n * @returns {Promise<object>} `{ nodes, cameras, lights, bounds, graph, animations }`\n * @ignore\n */\nexport async function parseGLTF(arrayBuffer, baseURI, settings) {\n\tconst { json, bin } = parseGLB(arrayBuffer);\n\tconst buffers = await resolveBuffers(json, bin, baseURI, settings);\n\n\t// decode every image once, keyed by image index\n\tconst images = await Promise.all(\n\t\t(json.images || []).map((_, i) => {\n\t\t\treturn decodeImage(json, buffers, i, baseURI, settings);\n\t\t}),\n\t);\n\n\t// resolve material index -> decoded baseColor image\n\tconst materialImage = (materialIndex) => {\n\t\tif (materialIndex === undefined) {\n\t\t\treturn null;\n\t\t}\n\t\tconst mat = json.materials?.[materialIndex];\n\t\tconst tex = mat?.pbrMetallicRoughness?.baseColorTexture;\n\t\tif (!tex) {\n\t\t\treturn null;\n\t\t}\n\t\tconst imageIndex = json.textures?.[tex.index]?.source;\n\t\treturn imageIndex !== undefined ? images[imageIndex] || null : null;\n\t};\n\n\t// resolve material index -> baseColorFactor [r,g,b,a] in 0..1 (defaults to\n\t// opaque white). A material with no baseColorTexture but a non-white factor\n\t// is a solid-colored mesh — without this it would render as the white-pixel\n\t// fallback.\n\tconst materialBaseColor = (materialIndex) => {\n\t\tif (materialIndex === undefined) {\n\t\t\treturn [1, 1, 1, 1];\n\t\t}\n\t\treturn (\n\t\t\tjson.materials?.[materialIndex]?.pbrMetallicRoughness\n\t\t\t\t?.baseColorFactor ?? [1, 1, 1, 1]\n\t\t);\n\t};\n\n\t// resolve material index -> melonJS texture wrap mode, honoring the glTF\n\t// sampler's `wrapS` / `wrapT`. The glTF default sampler wrap is REPEAT\n\t// (10497) on both axes — many exporters author UVs outside `[0, 1]` that\n\t// rely on it, so a missing sampler / texture must default to \"repeat\", not\n\t// clamp. CLAMP_TO_EDGE is 33071; MIRRORED_REPEAT (33648) has no melonJS\n\t// equivalent and maps to plain repeat.\n\tconst CLAMP = 33071;\n\tconst materialTextureRepeat = (materialIndex) => {\n\t\tlet wrapS = 10497;\n\t\tlet wrapT = 10497;\n\t\tconst tex =\n\t\t\tmaterialIndex !== undefined\n\t\t\t\t? json.materials?.[materialIndex]?.pbrMetallicRoughness\n\t\t\t\t\t\t?.baseColorTexture\n\t\t\t\t: undefined;\n\t\tif (tex) {\n\t\t\tconst samplerIndex = json.textures?.[tex.index]?.sampler;\n\t\t\tconst sampler =\n\t\t\t\tsamplerIndex !== undefined ? json.samplers?.[samplerIndex] : undefined;\n\t\t\twrapS = sampler?.wrapS ?? 10497;\n\t\t\twrapT = sampler?.wrapT ?? 10497;\n\t\t}\n\t\tconst repeatS = wrapS !== CLAMP;\n\t\tconst repeatT = wrapT !== CLAMP;\n\t\tif (repeatS && repeatT) {\n\t\t\treturn \"repeat\";\n\t\t}\n\t\tif (repeatS) {\n\t\t\treturn \"repeat-x\";\n\t\t}\n\t\tif (repeatT) {\n\t\t\treturn \"repeat-y\";\n\t\t}\n\t\treturn \"no-repeat\";\n\t};\n\n\t// resolve material index -> whether it opts out of lighting via\n\t// `KHR_materials_unlit` (a common stylized workflow: lighting is baked into\n\t// the texture, so the engine must NOT shade it again or it double-lights).\n\tconst materialUnlit = (materialIndex) => {\n\t\treturn (\n\t\t\tmaterialIndex !== undefined &&\n\t\t\tjson.materials?.[materialIndex]?.extensions?.KHR_materials_unlit !==\n\t\t\t\tundefined\n\t\t);\n\t};\n\n\t// resolve material index -> texture magnification filter from the glTF\n\t// sampler: `\"nearest\"` (9728, crisp pixel-art upscaling) or `\"linear\"`\n\t// (9729, smooth). `undefined` when the sampler doesn't specify one, so the\n\t// engine keeps its global antiAlias default — only an explicit NEAREST/LINEAR\n\t// overrides it.\n\tconst NEAREST = 9728;\n\tconst LINEAR = 9729;\n\tconst materialTextureFilter = (materialIndex) => {\n\t\tconst tex =\n\t\t\tmaterialIndex !== undefined\n\t\t\t\t? json.materials?.[materialIndex]?.pbrMetallicRoughness\n\t\t\t\t\t\t?.baseColorTexture\n\t\t\t\t: undefined;\n\t\tif (!tex) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst samplerIndex = json.textures?.[tex.index]?.sampler;\n\t\tconst magFilter =\n\t\t\tsamplerIndex !== undefined\n\t\t\t\t? json.samplers?.[samplerIndex]?.magFilter\n\t\t\t\t: undefined;\n\t\tif (magFilter === NEAREST) {\n\t\t\treturn \"nearest\";\n\t\t}\n\t\tif (magFilter === LINEAR) {\n\t\t\treturn \"linear\";\n\t\t}\n\t\treturn undefined;\n\t};\n\n\t// Resolve material index -> `{ specular, shininess }` from the material's\n\t// metallic/roughness factors (#1575). The mapping itself lives in\n\t// `pbr.ts` because MTL's `Pr`/`Pm` extension describes the same concept\n\t// and must not approximate it differently.\n\tconst materialSpecular = (materialIndex) => {\n\t\tconst pbr =\n\t\t\tmaterialIndex !== undefined\n\t\t\t\t? json.materials?.[materialIndex]?.pbrMetallicRoughness\n\t\t\t\t: undefined;\n\t\t// both factors default to 1 per the glTF spec — fully rough, which\n\t\t// yields no highlight, so an asset declaring neither is untouched\n\t\treturn specularFromMetallicRoughness(\n\t\t\tpbr?.roughnessFactor,\n\t\t\tpbr?.metallicFactor,\n\t\t\tpbr?.baseColorFactor ?? [1, 1, 1, 1],\n\t\t);\n\t};\n\n\t// resolve material index -> alpha cutout threshold. glTF `alphaMode:\n\t// \"MASK\"` is a hard cutout: a fragment is fully opaque where its alpha is\n\t// >= `alphaCutoff` and fully discarded below it (foliage, fences,\n\t// chain-link, decals — crisp edges, no blending or back-to-front sorting).\n\t// The spec default `alphaCutoff` is 0.5. Any other mode (OPAQUE / BLEND) →\n\t// 0, i.e. no discard (the mesh path renders opaque; BLEND is out of scope).\n\tconst materialAlphaCutoff = (materialIndex) => {\n\t\tconst mat =\n\t\t\tmaterialIndex !== undefined ? json.materials?.[materialIndex] : undefined;\n\t\tif (mat?.alphaMode === \"MASK\") {\n\t\t\treturn mat.alphaCutoff ?? 0.5;\n\t\t}\n\t\treturn 0;\n\t};\n\n\t// resolve material index -> emissive color [r,g,b] (0..1, possibly HDR), or\n\t// undefined when the material has no (non-zero) emission. glTF `emissiveFactor`\n\t// self-illuminates a surface (neon, lava, screens, glowing eyes) independently\n\t// of scene lights; `KHR_materials_emissive_strength` scales it past 1 for a\n\t// brighter glow (default strength 1). A zero factor → undefined so the Mesh\n\t// stays on the lean no-emissive path.\n\tconst materialEmissive = (materialIndex) => {\n\t\tconst mat =\n\t\t\tmaterialIndex !== undefined ? json.materials?.[materialIndex] : undefined;\n\t\tconst f = mat?.emissiveFactor;\n\t\tif (!f) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst strength =\n\t\t\tmat.extensions?.KHR_materials_emissive_strength?.emissiveStrength ?? 1;\n\t\t// `|| 0` guards a malformed (short) emissiveFactor: a missing component\n\t\t// would otherwise be `undefined * strength = NaN`, and NaN reaches the\n\t\t// `uEmissive` uniform (NaN !== 0, so the all-zero collapse below misses\n\t\t// it) → NaN fragments. Valid glTF always has 3 components.\n\t\tconst r = (f[0] || 0) * strength;\n\t\tconst g = (f[1] || 0) * strength;\n\t\tconst b = (f[2] || 0) * strength;\n\t\tif (r === 0 && g === 0 && b === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn [r, g, b];\n\t};\n\n\t// walk the active scene's node graph, accumulating world matrices.\n\t// A malformed asset is not allowed to crash the loader: a missing scene\n\t// or scene-node list degrades to an empty (but valid) descriptor rather\n\t// than throwing on a null dereference.\n\tconst meshNodes = [];\n\tconst cameras = [];\n\tconst lights = [];\n\t// the top-level KHR_lights_punctual light definitions (nodes reference these\n\t// by index); empty when the extension isn't used.\n\tconst lightDefs = json.extensions?.KHR_lights_punctual?.lights ?? [];\n\tconst sceneIndex = json.scene ?? 0;\n\tconst roots = json.scenes?.[sceneIndex]?.nodes ?? [];\n\n\t// Read one mesh primitive's geometry (positions / uvs / indices / normals /\n\t// colors) + resolved material color. Shared by the flat static `meshNodes`\n\t// list and the hierarchical `graph` (animated path) so geometry is read\n\t// exactly once per primitive and both views share the same typed arrays.\n\tconst readPrimitiveGeometry = (prim) => {\n\t\tconst vertices = readAccessor(json, buffers, prim.attributes.POSITION);\n\t\tconst uvs =\n\t\t\tprim.attributes.TEXCOORD_0 !== undefined\n\t\t\t\t? readAccessor(json, buffers, prim.attributes.TEXCOORD_0)\n\t\t\t\t: new Float32Array((vertices.length / 3) * 2);\n\t\tconst vertexCount = vertices.length / 3;\n\t\tlet indices;\n\t\tif (prim.indices !== undefined) {\n\t\t\tconst raw = readAccessor(json, buffers, prim.indices);\n\t\t\tindices = raw instanceof Uint32Array ? raw : Uint16Array.from(raw);\n\t\t} else {\n\t\t\t// non-indexed primitive (drawArrays-style): synthesize a\n\t\t\t// sequential index buffer so the geometry is still drawable.\n\t\t\tconst Indexed = vertexCount > 65535 ? Uint32Array : Uint16Array;\n\t\t\tindices = new Indexed(vertexCount);\n\t\t\tfor (let i = 0; i < vertexCount; i++) {\n\t\t\t\tindices[i] = i;\n\t\t\t}\n\t\t}\n\t\t// per-vertex normals for lit shading — read NORMAL when present,\n\t\t// otherwise synthesize them from the geometry so a mesh without\n\t\t// authored normals can still be lit.\n\t\tconst normals =\n\t\t\tprim.attributes.NORMAL !== undefined\n\t\t\t\t? readAccessor(json, buffers, prim.attributes.NORMAL)\n\t\t\t\t: computeFlatNormals(vertices, indices, vertexCount);\n\t\t// optional per-vertex colors (COLOR_0) → packed ARGB Uint32, for\n\t\t// untextured vertex-colored meshes (MagicaVoxel, vertex paint).\n\t\tconst colors =\n\t\t\tprim.attributes.COLOR_0 !== undefined\n\t\t\t\t? readVertexColors(json, buffers, prim.attributes.COLOR_0)\n\t\t\t\t: undefined;\n\t\treturn {\n\t\t\tvertices,\n\t\t\tnormals,\n\t\t\tuvs,\n\t\t\tindices,\n\t\t\tvertexCount,\n\t\t\timage: materialImage(prim.material),\n\t\t\t// texture wrap mode from the glTF sampler (default REPEAT) — see\n\t\t\t// materialTextureRepeat; carried so the Mesh samples tiling UVs\n\t\t\t// correctly instead of clamping to flat edge texels\n\t\t\ttextureRepeat: materialTextureRepeat(prim.material),\n\t\t\t// texture magnification filter from the glTF sampler (\"nearest\" for\n\t\t\t// crisp pixel-art, \"linear\" for smooth) — undefined keeps the engine\n\t\t\t// default\n\t\t\ttextureFilter: materialTextureFilter(prim.material),\n\t\t\t// baseColorFactor [r,g,b,a] — applied as the mesh tint so a\n\t\t\t// solid-colored (untextured) material renders its color\n\t\t\tbaseColorFactor: materialBaseColor(prim.material),\n\t\t\t// per-vertex colors (COLOR_0), packed ARGB, or undefined\n\t\t\tcolors,\n\t\t\t// honor the glTF material's double-sided flag — many props\n\t\t\t// (coins, fences, foliage) are thin/flat double-sided\n\t\t\t// geometry that a single-sided back-face cull would gut\n\t\t\tdoubleSided:\n\t\t\t\tprim.material !== undefined &&\n\t\t\t\tjson.materials?.[prim.material]?.doubleSided === true,\n\t\t\t// KHR_materials_unlit — the material bakes its own lighting and must\n\t\t\t// not be shaded again (skips the lit path even in a lit scene)\n\t\t\tunlit: materialUnlit(prim.material),\n\t\t\t// alpha cutout threshold (glTF alphaMode MASK); 0 = no discard\n\t\t\talphaCutoff: materialAlphaCutoff(prim.material),\n\t\t\t// emissive color [r,g,b] (glTF emissiveFactor × emissive_strength), or\n\t\t\t// undefined when the material doesn't self-illuminate\n\t\t\temissive: materialEmissive(prim.material),\n\t\t\t// specular highlight approximated from metallic/roughness (#1575);\n\t\t\t// a fully-rough material yields shininess 0 and no highlight\n\t\t\t...materialSpecular(prim.material),\n\t\t};\n\t};\n\n\t// Guard against cyclic node graphs. Per the glTF spec the node hierarchy\n\t// is a strict tree (each node has at most one parent), so a node visited\n\t// twice means the file is malformed — skip it rather than recursing\n\t// forever into a stack overflow.\n\tconst visited = new Set();\n\n\t// the full node hierarchy (animated path): glTF node index → graph node\n\t// carrying rest TRS, children, and any mesh primitives. Built alongside the\n\t// flat `meshNodes` from the same single geometry read.\n\tconst graphNodes = {};\n\n\tconst visit = (nodeIndex, parentWorld) => {\n\t\tif (visited.has(nodeIndex)) {\n\t\t\treturn;\n\t\t}\n\t\tvisited.add(nodeIndex);\n\t\tconst node = json.nodes[nodeIndex];\n\t\tconst world = multiplyMatrix(parentWorld, nodeLocalMatrix(node));\n\t\tconst nodeName = node.name || `node_${nodeIndex}`;\n\t\tconst primitives = [];\n\t\tif (node.mesh !== undefined) {\n\t\t\t// EXT_mesh_gpu_instancing: the node carries per-instance TRS\n\t\t\t// accessors, i.e. one geometry stamped out many times. This is what\n\t\t\t// an exporter writes for linked duplicates (a scattered forest, a\n\t\t\t// crowd), and it maps straight onto an InstancedMesh — so read it\n\t\t\t// here rather than making every consumer discover it.\n\t\t\tconst instancing = node.extensions?.EXT_mesh_gpu_instancing;\n\t\t\tconst instances = instancing\n\t\t\t\t? readInstanceAttributes(json, buffers, instancing.attributes)\n\t\t\t\t: undefined;\n\t\t\tfor (const prim of json.meshes[node.mesh].primitives) {\n\t\t\t\tconst geo = readPrimitiveGeometry(prim);\n\t\t\t\tprimitives.push(geo);\n\t\t\t\t// flat static entry — same shape (+ world + name) as before so the\n\t\t\t\t// static path and bounds computation are unchanged\n\t\t\t\tmeshNodes.push({ name: nodeName, world, ...geo, instances });\n\t\t\t\t// the animated path builds from `graphNodes[].primitives`, so\n\t\t\t\t// the records have to ride along there as well — otherwise a\n\t\t\t\t// single animation clip anywhere in the asset silently drops\n\t\t\t\t// instancing for the whole scene\n\t\t\t\tprimitives[primitives.length - 1].instances = instances;\n\t\t\t}\n\t\t}\n\t\t// graph node: rest TRS (glTF defaults when a field is absent), an explicit\n\t\t// `matrix` if the node used one, children, and its mesh primitives. The\n\t\t// animated path samples into a mutable copy of this TRS each frame.\n\t\tgraphNodes[nodeIndex] = {\n\t\t\tindex: nodeIndex,\n\t\t\tname: nodeName,\n\t\t\ttranslation: node.translation ? node.translation.slice() : [0, 0, 0],\n\t\t\trotation: node.rotation ? node.rotation.slice() : [0, 0, 0, 1],\n\t\t\tscale: node.scale ? node.scale.slice() : [1, 1, 1],\n\t\t\tmatrix: node.matrix ? node.matrix.slice() : null,\n\t\t\tchildren: (node.children || []).slice(),\n\t\t\tprimitives,\n\t\t};\n\t\tif (node.camera !== undefined) {\n\t\t\tcameras.push({ ...json.cameras[node.camera], world });\n\t\t}\n\t\t// KHR_lights_punctual: a node references a light defined in the\n\t\t// top-level extension. Resolve its world-space direction (directional /\n\t\t// spot lights point down the node's local -Z) and position (point /\n\t\t// spot) from the node world matrix, so consumers get ready-to-use data.\n\t\tconst lightIndex = node.extensions?.KHR_lights_punctual?.light;\n\t\tif (lightIndex !== undefined && lightDefs[lightIndex] !== undefined) {\n\t\t\tconst def = lightDefs[lightIndex];\n\t\t\tlights.push({\n\t\t\t\ttype: def.type, // \"directional\" | \"point\" | \"spot\"\n\t\t\t\tcolor: def.color ?? [1, 1, 1],\n\t\t\t\tintensity: def.intensity ?? 1,\n\t\t\t\trange: def.range,\n\t\t\t\t// spot cone angles (radians) — the spec defaults\n\t\t\t\tinnerConeAngle: def.spot?.innerConeAngle ?? 0,\n\t\t\t\touterConeAngle: def.spot?.outerConeAngle ?? Math.PI / 4,\n\t\t\t\t// world -Z axis of the node (third basis column negated), normalized\n\t\t\t\tdirection: normalize3([-world[8], -world[9], -world[10]]),\n\t\t\t\t// world translation\n\t\t\t\tposition: [world[12], world[13], world[14]],\n\t\t\t\tname: def.name,\n\t\t\t});\n\t\t}\n\t\tfor (const child of node.children || []) {\n\t\t\tvisit(child, world);\n\t\t}\n\t};\n\tfor (const root of roots) {\n\t\tvisit(root, IDENTITY);\n\t}\n\n\t// scene bounds (world space) for camera framing — accumulate each node's\n\t// transformed vertices into one AABB\n\tconst min = [Infinity, Infinity, Infinity];\n\tconst max = [-Infinity, -Infinity, -Infinity];\n\tfor (const n of meshNodes) {\n\t\ttransformedBounds(n.vertices, n.vertexCount, n.world, min, max);\n\t}\n\t// a scene with no drawable geometry leaves the bounds at their sentinel\n\t// ±Infinity — collapse to a degenerate box at the origin so consumers\n\t// (camera framing, etc.) get finite numbers instead of NaN.\n\tif (meshNodes.length === 0) {\n\t\tmin[0] = min[1] = min[2] = 0;\n\t\tmax[0] = max[1] = max[2] = 0;\n\t}\n\n\t// node-TRS animation clips. Each channel binds a sampler (keyframe times +\n\t// values) to a node's translation / rotation / scale. Other paths (weights /\n\t// morph targets) are skipped — out of Tier-1 scope. Channels targeting a node\n\t// outside the active scene are dropped. `duration` is the latest keyframe\n\t// time across the clip's samplers (seconds).\n\tconst animations = (json.animations || []).map((anim, ai) => {\n\t\tlet duration = 0;\n\t\tconst channels = [];\n\t\tfor (const ch of anim.channels) {\n\t\t\tconst path = ch.target?.path;\n\t\t\tconst nodeIndex = ch.target?.node;\n\t\t\tif (\n\t\t\t\tnodeIndex === undefined ||\n\t\t\t\tgraphNodes[nodeIndex] === undefined ||\n\t\t\t\t(path !== \"translation\" && path !== \"rotation\" && path !== \"scale\")\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sampler = anim.samplers[ch.sampler];\n\t\t\tconst times = readAccessor(json, buffers, sampler.input);\n\t\t\tconst values = readAccessor(json, buffers, sampler.output);\n\t\t\tif (times.length > 0) {\n\t\t\t\tduration = Math.max(duration, times[times.length - 1]);\n\t\t\t}\n\t\t\tchannels.push({\n\t\t\t\tnode: nodeIndex,\n\t\t\t\tpath,\n\t\t\t\ttimes,\n\t\t\t\tvalues,\n\t\t\t\t// component count per keyframe value (rotation = quaternion VEC4)\n\t\t\t\tstride: path === \"rotation\" ? 4 : 3,\n\t\t\t\tinterpolation: sampler.interpolation || \"LINEAR\",\n\t\t\t});\n\t\t}\n\t\treturn { name: anim.name || `anim_${ai}`, duration, channels };\n\t});\n\n\treturn {\n\t\tnodes: meshNodes,\n\t\tcameras,\n\t\tlights,\n\t\tbounds: { min, max },\n\t\t// hierarchical node graph + animation clips for the animated path; the\n\t\t// static path ignores both. `graph.nodes` is keyed by glTF node index.\n\t\tgraph: { roots, nodes: graphNodes },\n\t\tanimations,\n\t};\n}\n\n/**\n * parse/preload a glTF/GLB file\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadGLTF(data, onload, onerror, settings) {\n\tif (typeof gltfList[data.name] !== \"undefined\") {\n\t\treturn 0;\n\t}\n\tfetchData(data.src, \"arrayBuffer\", settings)\n\t\t.then((buffer) => {\n\t\t\t// pass the asset URL so external `.bin` / image `uri`s resolve\n\t\t\t// relative to it (a GLB with an external texture, like Kenney's\n\t\t\t// blocky characters, loads as-shipped without repackaging)\n\t\t\treturn parseGLTF(buffer, data.src, settings);\n\t\t})\n\t\t.then((scene) => {\n\t\t\tgltfList[data.name] = scene;\n\t\t\t// register with the level director so the scene can be loaded\n\t\t\t// into the world via `me.level.load(name)`, like a Tiled map\n\t\t\tlevel.add(data.type, data.name);\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\treturn 1;\n}\n","// DDS magic number\nconst DDS_MAGIC = 0x20534444;\n\n// DDS header size in bytes (excluding magic)\nconst DDS_HEADER_SIZE = 124;\n\n// DDS_PIXELFORMAT fourCC values\nconst FOURCC_DXT1 = 0x31545844; // \"DXT1\"\nconst FOURCC_DXT3 = 0x33545844; // \"DXT3\"\nconst FOURCC_DXT5 = 0x35545844; // \"DXT5\"\nconst FOURCC_DX10 = 0x30315844; // \"DX10\"\n\n// DXGI format for BC7\nconst DXGI_FORMAT_BC7_UNORM = 98;\nconst DXGI_FORMAT_BC7_UNORM_SRGB = 99;\n\n// WebGL compressed texture format constants\nconst COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83f0;\nconst COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83f2;\nconst COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83f3;\nconst COMPRESSED_RGBA_BPTC_UNORM_EXT = 0x8e8c;\n\nconst FOURCC_TO_WEBGL = {\n\t[FOURCC_DXT1]: COMPRESSED_RGB_S3TC_DXT1_EXT,\n\t[FOURCC_DXT3]: COMPRESSED_RGBA_S3TC_DXT3_EXT,\n\t[FOURCC_DXT5]: COMPRESSED_RGBA_S3TC_DXT5_EXT,\n};\n\nconst DXGI_TO_WEBGL = {\n\t[DXGI_FORMAT_BC7_UNORM]: COMPRESSED_RGBA_BPTC_UNORM_EXT,\n\t[DXGI_FORMAT_BC7_UNORM_SRGB]: COMPRESSED_RGBA_BPTC_UNORM_EXT,\n};\n\n// Block size in bytes per 4×4 block\nfunction blockSize(format) {\n\treturn format === COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16;\n}\n\n/**\n * Parse a DDS compressed texture file.\n * @param {ArrayBuffer} data - the DDS file data\n * @returns {CompressedImage} a compressed texture object with mipmaps, width, height, format\n * @ignore\n */\nexport function parseDDS(data) {\n\t// validate magic number\n\tconst magicView = new DataView(data, 0, 4);\n\tif (magicView.getUint32(0, true) !== DDS_MAGIC) {\n\t\tthrow new Error(\"Invalid DDS file: bad magic number\");\n\t}\n\n\t// read header (124 bytes starting at offset 4)\n\tconst header = new DataView(data, 4, DDS_HEADER_SIZE);\n\tconst height = header.getUint32(8, true);\n\tconst width = header.getUint32(12, true);\n\tconst mipmapCount = Math.max(1, header.getUint32(24, true));\n\tconst fourCC = header.getUint32(80, true);\n\n\tlet format;\n\tlet dataOffset = 4 + DDS_HEADER_SIZE; // 128\n\n\tif (fourCC === FOURCC_DX10) {\n\t\t// DX10 extended header (20 bytes at offset 128)\n\t\tconst dx10Header = new DataView(data, 128, 20);\n\t\tconst dxgiFormat = dx10Header.getUint32(0, true);\n\n\t\tformat = DXGI_TO_WEBGL[dxgiFormat];\n\t\tif (typeof format === \"undefined\") {\n\t\t\tthrow new Error(\"Unrecognized DDS DXGI format: \" + dxgiFormat);\n\t\t}\n\t\tdataOffset = 148; // 128 + 20\n\t} else {\n\t\tformat = FOURCC_TO_WEBGL[fourCC];\n\t\tif (typeof format === \"undefined\") {\n\t\t\tthrow new Error(\"Unrecognized DDS fourCC: 0x\" + fourCC.toString(16));\n\t\t}\n\t}\n\n\tconst bs = blockSize(format);\n\tconst mipmaps = new Array(mipmapCount);\n\tlet levelWidth = width;\n\tlet levelHeight = height;\n\tlet offset = dataOffset;\n\n\tfor (let i = 0; i < mipmapCount; i++) {\n\t\tconst levelSize =\n\t\t\tMath.max(1, (levelWidth + 3) >> 2) *\n\t\t\tMath.max(1, (levelHeight + 3) >> 2) *\n\t\t\tbs;\n\n\t\tmipmaps[i] = {\n\t\t\tdata: new Uint8Array(data, offset, levelSize),\n\t\t\twidth: levelWidth,\n\t\t\theight: levelHeight,\n\t\t};\n\n\t\toffset += levelSize;\n\t\tlevelWidth = Math.max(1, levelWidth >> 1);\n\t\tlevelHeight = Math.max(1, levelHeight >> 1);\n\t}\n\n\treturn {\n\t\tmipmaps: mipmaps,\n\t\twidth: width,\n\t\theight: height,\n\t\tformat: format,\n\t\tcompressed: true,\n\t\tgenerateMipmap: false,\n\t};\n}\n","// KTX v1 file identifier (12 bytes): «KTX 11»\\r\\n\\x1A\\n\nconst KTX_IDENTIFIER = [\n\t0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a,\n];\n\nconst KTX_ENDIANNESS = 0x04030201;\nconst KTX_ENDIANNESS_SWAP = 0x01020304;\n\n// Header field offsets (in Uint32Array indices, after the 12-byte identifier)\n// Byte offsets: identifier(12) + endianness(4) + 12 × uint32(48) = 64 bytes total\nconst KTX_HEADER_SIZE = 64;\n\n/**\n * Parse a KTX v1 compressed texture file.\n * @param {ArrayBuffer} data - the KTX file data\n * @returns {CompressedImage} a compressed texture object with mipmaps, width, height, format\n * @ignore\n */\nexport function parseKTX(data) {\n\tconst idView = new Uint8Array(data, 0, 12);\n\n\t// validate file identifier\n\tfor (let i = 0; i < 12; i++) {\n\t\tif (idView[i] !== KTX_IDENTIFIER[i]) {\n\t\t\tthrow new Error(\"Invalid KTX file: bad identifier\");\n\t\t}\n\t}\n\n\t// read header uint32 values (starting at byte 12)\n\tconst headerView = new DataView(data, 12, KTX_HEADER_SIZE - 12);\n\tconst endianness = headerView.getUint32(0, true);\n\n\tlet littleEndian;\n\tif (endianness === KTX_ENDIANNESS) {\n\t\tlittleEndian = true;\n\t} else if (endianness === KTX_ENDIANNESS_SWAP) {\n\t\tlittleEndian = false;\n\t} else {\n\t\tthrow new Error(\"Invalid KTX file: bad endianness indicator\");\n\t}\n\n\tconst glType = headerView.getUint32(4, littleEndian);\n\tconst glFormat = headerView.getUint32(12, littleEndian);\n\tconst glInternalFormat = headerView.getUint32(16, littleEndian);\n\tconst width = headerView.getUint32(24, littleEndian);\n\tconst height = headerView.getUint32(28, littleEndian);\n\tconst numberOfMipmapLevels = Math.max(\n\t\t1,\n\t\theaderView.getUint32(44, littleEndian),\n\t);\n\tconst bytesOfKeyValueData = headerView.getUint32(48, littleEndian);\n\n\t// verify this is a compressed texture (glType and glFormat must be 0)\n\tif (glType !== 0 || glFormat !== 0) {\n\t\tthrow new Error(\n\t\t\t\"KTX file contains uncompressed texture data, only compressed formats are supported\",\n\t\t);\n\t}\n\n\t// pixel data starts after header + key/value data\n\tlet dataOffset = KTX_HEADER_SIZE + bytesOfKeyValueData;\n\n\tconst mipmaps = new Array(numberOfMipmapLevels);\n\tlet levelWidth = width;\n\tlet levelHeight = height;\n\n\tfor (let i = 0; i < numberOfMipmapLevels; i++) {\n\t\t// read imageSize (uint32, endianness-aware)\n\t\tconst imageSizeView = new DataView(data, dataOffset, 4);\n\t\tconst imageSize = imageSizeView.getUint32(0, littleEndian);\n\t\tdataOffset += 4;\n\n\t\tmipmaps[i] = {\n\t\t\tdata: new Uint8Array(data, dataOffset, imageSize),\n\t\t\twidth: levelWidth,\n\t\t\theight: levelHeight,\n\t\t};\n\n\t\t// advance past image data + padding to 4-byte alignment\n\t\tdataOffset += imageSize;\n\t\tdataOffset = (dataOffset + 3) & ~3;\n\n\t\tlevelWidth = Math.max(1, levelWidth >> 1);\n\t\tlevelHeight = Math.max(1, levelHeight >> 1);\n\t}\n\n\treturn {\n\t\tmipmaps: mipmaps,\n\t\twidth: width,\n\t\theight: height,\n\t\tformat: glInternalFormat,\n\t\tcompressed: true,\n\t\tgenerateMipmap: false,\n\t};\n}\n","// KTX v2 file identifier (12 bytes): «KTX 20»\\r\\n\\x1A\\n\nconst KTX2_IDENTIFIER = [\n\t0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a,\n];\n\n// VkFormat → WebGL compressed texture format mapping\nconst VKFORMAT_TO_WEBGL = {\n\t// BC1 (S3TC DXT1)\n\t131: 0x83f0, // VK_FORMAT_BC1_RGB_UNORM_BLOCK\n\t132: 0x83f0, // VK_FORMAT_BC1_RGB_SRGB_BLOCK\n\t133: 0x83f0, // VK_FORMAT_BC1_RGBA_UNORM_BLOCK\n\t134: 0x83f0, // VK_FORMAT_BC1_RGBA_SRGB_BLOCK\n\n\t// BC2 (S3TC DXT3)\n\t135: 0x83f2, // VK_FORMAT_BC2_UNORM_BLOCK\n\t136: 0x83f2, // VK_FORMAT_BC2_SRGB_BLOCK\n\n\t// BC3 (S3TC DXT5)\n\t137: 0x83f3, // VK_FORMAT_BC3_UNORM_BLOCK\n\t138: 0x83f3, // VK_FORMAT_BC3_SRGB_BLOCK\n\n\t// BC7 (BPTC)\n\t145: 0x8e8c, // VK_FORMAT_BC7_UNORM_BLOCK\n\t146: 0x8e8c, // VK_FORMAT_BC7_SRGB_BLOCK\n\n\t// ETC2\n\t147: 0x9274, // VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK → COMPRESSED_RGB8_ETC2\n\t148: 0x9274, // VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK\n\t149: 0x9276, // VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK → COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2\n\t150: 0x9276, // VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK\n\t151: 0x9278, // VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK → COMPRESSED_RGBA8_ETC2_EAC\n\t152: 0x9278, // VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK\n\n\t// EAC\n\t153: 0x9270, // VK_FORMAT_EAC_R11_UNORM_BLOCK → COMPRESSED_R11_EAC\n\t154: 0x9271, // VK_FORMAT_EAC_R11_SNORM_BLOCK → COMPRESSED_SIGNED_R11_EAC\n\t155: 0x9272, // VK_FORMAT_EAC_R11G11_UNORM_BLOCK → COMPRESSED_RG11_EAC\n\t156: 0x9273, // VK_FORMAT_EAC_R11G11_SNORM_BLOCK → COMPRESSED_SIGNED_RG11_EAC\n\n\t// ASTC\n\t157: 0x93b0, // VK_FORMAT_ASTC_4x4_UNORM_BLOCK\n\t158: 0x93b0, // VK_FORMAT_ASTC_4x4_SRGB_BLOCK\n\t159: 0x93b1, // VK_FORMAT_ASTC_5x4_UNORM_BLOCK\n\t160: 0x93b1, // VK_FORMAT_ASTC_5x4_SRGB_BLOCK\n\t161: 0x93b2, // VK_FORMAT_ASTC_5x5_UNORM_BLOCK\n\t162: 0x93b2, // VK_FORMAT_ASTC_5x5_SRGB_BLOCK\n\t163: 0x93b3, // VK_FORMAT_ASTC_6x5_UNORM_BLOCK\n\t164: 0x93b3, // VK_FORMAT_ASTC_6x5_SRGB_BLOCK\n\t165: 0x93b4, // VK_FORMAT_ASTC_6x6_UNORM_BLOCK\n\t166: 0x93b4, // VK_FORMAT_ASTC_6x6_SRGB_BLOCK\n\t167: 0x93b5, // VK_FORMAT_ASTC_8x5_UNORM_BLOCK\n\t168: 0x93b5, // VK_FORMAT_ASTC_8x5_SRGB_BLOCK\n\t169: 0x93b6, // VK_FORMAT_ASTC_8x6_UNORM_BLOCK\n\t170: 0x93b6, // VK_FORMAT_ASTC_8x6_SRGB_BLOCK\n\t171: 0x93b7, // VK_FORMAT_ASTC_8x8_UNORM_BLOCK\n\t172: 0x93b7, // VK_FORMAT_ASTC_8x8_SRGB_BLOCK\n\t173: 0x93b8, // VK_FORMAT_ASTC_10x5_UNORM_BLOCK\n\t174: 0x93b8, // VK_FORMAT_ASTC_10x5_SRGB_BLOCK\n\t175: 0x93b9, // VK_FORMAT_ASTC_10x6_UNORM_BLOCK\n\t176: 0x93b9, // VK_FORMAT_ASTC_10x6_SRGB_BLOCK\n\t177: 0x93ba, // VK_FORMAT_ASTC_10x8_UNORM_BLOCK\n\t178: 0x93ba, // VK_FORMAT_ASTC_10x8_SRGB_BLOCK\n\t179: 0x93bb, // VK_FORMAT_ASTC_10x10_UNORM_BLOCK\n\t180: 0x93bb, // VK_FORMAT_ASTC_10x10_SRGB_BLOCK\n\t181: 0x93bc, // VK_FORMAT_ASTC_12x10_UNORM_BLOCK\n\t182: 0x93bc, // VK_FORMAT_ASTC_12x10_SRGB_BLOCK\n\t183: 0x93bd, // VK_FORMAT_ASTC_12x12_UNORM_BLOCK\n\t184: 0x93bd, // VK_FORMAT_ASTC_12x12_SRGB_BLOCK\n};\n\n/**\n * Parse a KTX v2 compressed texture file.\n * @param {ArrayBuffer} data - the KTX2 file data\n * @returns {CompressedImage} a compressed texture object with mipmaps, width, height, format\n * @ignore\n */\nexport function parseKTX2(data) {\n\tconst idView = new Uint8Array(data, 0, 12);\n\n\t// validate file identifier\n\tfor (let i = 0; i < 12; i++) {\n\t\tif (idView[i] !== KTX2_IDENTIFIER[i]) {\n\t\t\tthrow new Error(\"Invalid KTX2 file: bad identifier\");\n\t\t}\n\t}\n\n\t// KTX2 is always little-endian\n\tconst header = new DataView(data, 12, 56);\n\tconst vkFormat = header.getUint32(0, true);\n\tconst width = header.getUint32(8, true);\n\tconst height = header.getUint32(12, true);\n\tconst levelCount = Math.max(1, header.getUint32(28, true));\n\tconst supercompressionScheme = header.getUint32(32, true);\n\n\t// reject Basis Universal (vkFormat = 0) and supercompressed data\n\tif (vkFormat === 0) {\n\t\tthrow new Error(\"KTX2 Basis Universal transcoding is not supported\");\n\t}\n\n\tif (supercompressionScheme !== 0) {\n\t\tthrow new Error(\n\t\t\t\"KTX2 supercompression scheme \" +\n\t\t\t\tsupercompressionScheme +\n\t\t\t\t\" is not supported\",\n\t\t);\n\t}\n\n\tconst format = VKFORMAT_TO_WEBGL[vkFormat];\n\tif (typeof format === \"undefined\") {\n\t\tthrow new Error(\"Unrecognized KTX2 vkFormat: \" + vkFormat);\n\t}\n\n\t// Level index starts at offset 80 from file start (12 byte identifier + 68 byte header)\n\t// Each level entry: byteOffset(uint64) + byteLength(uint64) + uncompressedByteLength(uint64) = 24 bytes\n\tconst levelIndexOffset = 80;\n\tconst levelIndex = new DataView(data, levelIndexOffset, levelCount * 24);\n\n\tconst mipmaps = new Array(levelCount);\n\tlet levelWidth = width;\n\tlet levelHeight = height;\n\n\tfor (let i = 0; i < levelCount; i++) {\n\t\t// KTX2 stores levels smallest-first in the level index, but we want largest-first\n\t\t// Level 0 in the index = largest mip level\n\t\tconst entryOffset = i * 24;\n\t\t// read low 32 bits of uint64 byteOffset and byteLength\n\t\tconst byteOffset = levelIndex.getUint32(entryOffset, true);\n\t\tconst byteLength = levelIndex.getUint32(entryOffset + 8, true);\n\n\t\tmipmaps[i] = {\n\t\t\tdata: new Uint8Array(data, byteOffset, byteLength),\n\t\t\twidth: levelWidth,\n\t\t\theight: levelHeight,\n\t\t};\n\n\t\tlevelWidth = Math.max(1, levelWidth >> 1);\n\t\tlevelHeight = Math.max(1, levelHeight >> 1);\n\t}\n\n\treturn {\n\t\tmipmaps: mipmaps,\n\t\twidth: width,\n\t\theight: height,\n\t\tformat: format,\n\t\tcompressed: true,\n\t\tgenerateMipmap: false,\n\t};\n}\n","/**\n * PKM file format parser (ETC1/ETC2 container).\n *\n * Header layout (16 bytes, big-endian):\n *   [0-3]   magic      \"PKM \" (0x504B4D20)\n *   [4-5]   version    \"10\" (ETC1) or \"20\" (ETC2)\n *   [6-7]   format     texture format type\n *   [8-9]   encodedWidth   padded width (multiple of 4)\n *   [10-11] encodedHeight  padded height (multiple of 4)\n *   [12-13] width      original width\n *   [14-15] height     original height\n *\n * Data immediately follows the 16-byte header.\n */\n\nconst PKM_HEADER_SIZE = 16;\nconst PKM_MAGIC = 0x504b4d20; // \"PKM \"\n\n// ETC1 (via WEBGL_compressed_texture_etc1)\nconst COMPRESSED_RGB_ETC1_WEBGL = 0x8d64;\n\n// ETC2 (via WEBGL_compressed_texture_etc)\nconst COMPRESSED_RGB8_ETC2 = 0x9274;\nconst COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276;\nconst COMPRESSED_RGBA8_ETC2_EAC = 0x9278;\nconst COMPRESSED_R11_EAC = 0x9270;\nconst COMPRESSED_SIGNED_R11_EAC = 0x9271;\nconst COMPRESSED_RG11_EAC = 0x9272;\nconst COMPRESSED_SIGNED_RG11_EAC = 0x9273;\n\n// PKM format type → WebGL constant\nconst PKM_FORMAT_TO_WEBGL = {\n\t0: COMPRESSED_RGB_ETC1_WEBGL, // ETC1_RGB_NO_MIPMAPS\n\t1: COMPRESSED_RGB8_ETC2, // ETC2_RGB_NO_MIPMAPS\n\t3: COMPRESSED_RGBA8_ETC2_EAC, // ETC2_RGBA_NO_MIPMAPS\n\t4: COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, // ETC2_RGB_A1_NO_MIPMAPS\n\t5: COMPRESSED_R11_EAC, // ETC2_R_NO_MIPMAPS\n\t6: COMPRESSED_RG11_EAC, // ETC2_RG_NO_MIPMAPS\n\t7: COMPRESSED_SIGNED_R11_EAC, // ETC2_SIGNED_R_NO_MIPMAPS\n\t8: COMPRESSED_SIGNED_RG11_EAC, // ETC2_SIGNED_RG_NO_MIPMAPS\n};\n\n/**\n * Parse a PKM compressed texture file.\n * @param {ArrayBuffer} data - the raw PKM file data\n * @returns {CompressedImage} a compressed texture object\n * @ignore\n */\nexport function parsePKM(data) {\n\tconst header = new DataView(data, 0, PKM_HEADER_SIZE);\n\n\t// validate magic\n\tconst magic = header.getUint32(0, false); // big-endian\n\tif (magic !== PKM_MAGIC) {\n\t\tthrow new Error(\"Invalid PKM file: bad magic 0x\" + magic.toString(16));\n\t}\n\n\t// read format type (big-endian uint16 at offset 6)\n\tconst formatType = header.getUint16(6, false);\n\n\tconst format = PKM_FORMAT_TO_WEBGL[formatType];\n\tif (typeof format === \"undefined\") {\n\t\tthrow new Error(\"Unrecognized PKM format type: \" + formatType);\n\t}\n\n\t// original dimensions (big-endian uint16)\n\tconst width = header.getUint16(12, false);\n\tconst height = header.getUint16(14, false);\n\n\t// encoded (padded) dimensions for calculating data size\n\tconst encodedWidth = header.getUint16(8, false);\n\tconst encodedHeight = header.getUint16(10, false);\n\n\t// block size: 8 bytes for ETC1/ETC2 RGB, 16 bytes for RGBA/R11/RG11\n\tconst blockSize =\n\t\tformatType === 0 || formatType === 1 || formatType === 4 ? 8 : 16;\n\n\tconst dataSize =\n\t\t((encodedWidth + 3) >> 2) * ((encodedHeight + 3) >> 2) * blockSize;\n\n\treturn {\n\t\tmipmaps: [\n\t\t\t{\n\t\t\t\tdata: new Uint8Array(data, PKM_HEADER_SIZE, dataSize),\n\t\t\t\twidth: width,\n\t\t\t\theight: height,\n\t\t\t},\n\t\t],\n\t\twidth: width,\n\t\theight: height,\n\t\tformat: format,\n\t\tcompressed: true,\n\t\tgenerateMipmap: false,\n\t};\n}\n","const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8c00;\nconst COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8c01;\nconst COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8c02;\nconst COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8c03;\n\nconst COMPRESSED_RGB_ETC1_WEBGL = 0x8d64;\n\nconst PVR_HEADER_LENGTH = 13; // The header length in 32 bit ints.\nconst PVR_MAGIC = 0x03525650;\n\n// Offsets into the header array.\nconst PVR_HEADER_MAGIC = 0;\nconst PVR_HEADER_FORMAT = 2;\nconst PVR_HEADER_HEIGHT = 6;\nconst PVR_HEADER_WIDTH = 7;\nconst PVR_HEADER_MIPMAPCOUNT = 11;\nconst PVR_HEADER_METADATA = 12;\n\nconst PVR_FORMAT_2BPP_RGB = 0;\nconst PVR_FORMAT_2BPP_RGBA = 1;\nconst PVR_FORMAT_4BPP_RGB = 2;\nconst PVR_FORMAT_4BPP_RGBA = 3;\nconst PVR_FORMAT_ETC1 = 6;\nconst PVR_FORMAT_DXT1 = 7;\nconst PVR_FORMAT_DXT3 = 9;\nconst PVR_FORMAT_DXT5 = 5;\n\nconst COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83f0;\nconst COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83f2;\nconst COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83f3;\n\nconst PVR_TO_WEBGL_FORMAT = {\n\t[PVR_FORMAT_2BPP_RGB]: COMPRESSED_RGB_PVRTC_2BPPV1_IMG,\n\t[PVR_FORMAT_2BPP_RGBA]: COMPRESSED_RGBA_PVRTC_2BPPV1_IMG,\n\t[PVR_FORMAT_4BPP_RGB]: COMPRESSED_RGB_PVRTC_4BPPV1_IMG,\n\t[PVR_FORMAT_4BPP_RGBA]: COMPRESSED_RGBA_PVRTC_4BPPV1_IMG,\n\t[PVR_FORMAT_ETC1]: COMPRESSED_RGB_ETC1_WEBGL,\n\t[PVR_FORMAT_DXT1]: COMPRESSED_RGB_S3TC_DXT1_EXT,\n\t[PVR_FORMAT_DXT3]: COMPRESSED_RGBA_S3TC_DXT3_EXT,\n\t[PVR_FORMAT_DXT5]: COMPRESSED_RGBA_S3TC_DXT5_EXT,\n};\n\nfunction levelBufferSize(format, width, height) {\n\tswitch (format) {\n\t\tcase COMPRESSED_RGB_S3TC_DXT1_EXT:\n\t\tcase COMPRESSED_RGB_ETC1_WEBGL:\n\t\t\treturn ((width + 3) >> 2) * ((height + 3) >> 2) * 8;\n\n\t\tcase COMPRESSED_RGBA_S3TC_DXT3_EXT:\n\t\tcase COMPRESSED_RGBA_S3TC_DXT5_EXT:\n\t\t\treturn ((width + 3) >> 2) * ((height + 3) >> 2) * 16;\n\n\t\tcase COMPRESSED_RGB_PVRTC_4BPPV1_IMG:\n\t\tcase COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:\n\t\t\treturn Math.floor((Math.max(width, 8) * Math.max(height, 8) * 4 + 7) / 8);\n\n\t\tcase COMPRESSED_RGB_PVRTC_2BPPV1_IMG:\n\t\tcase COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:\n\t\t\treturn Math.floor(\n\t\t\t\t(Math.max(width, 16) * Math.max(height, 8) * 2 + 7) / 8,\n\t\t\t);\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\n/**\n * parse a PVR v3 compressed texture file\n * @param {ArrayBuffer} data - the PVR file data\n * @returns {CompressedImage} a compressed texture object with mipmaps, width, height, format\n * @ignore\n */\nexport function parsePVR(data) {\n\tconst header = new Uint32Array(data, 0, PVR_HEADER_LENGTH);\n\n\t// validate magic number\n\tif (header[PVR_HEADER_MAGIC] !== PVR_MAGIC) {\n\t\tthrow new Error(\"Invalid PVR file: bad magic number\");\n\t}\n\n\tconst pvrFormat = PVR_TO_WEBGL_FORMAT[header[PVR_HEADER_FORMAT]];\n\tif (typeof pvrFormat === \"undefined\") {\n\t\tthrow new Error(\n\t\t\t\"Unrecognized PVR pixel format: \" + header[PVR_HEADER_FORMAT],\n\t\t);\n\t}\n\n\tconst width = header[PVR_HEADER_WIDTH];\n\tconst height = header[PVR_HEADER_HEIGHT];\n\tconst dataOffset = header[PVR_HEADER_METADATA] + 52;\n\tconst mipmapCount = header[PVR_HEADER_MIPMAPCOUNT];\n\n\tconst image = new Uint8Array(data, dataOffset);\n\tconst mipmaps = new Array(mipmapCount);\n\n\tlet offset = 0;\n\tlet levelWidth = width;\n\tlet levelHeight = height;\n\n\tfor (let i = 0; i < mipmapCount; i++) {\n\t\tconst levelSize = levelBufferSize(pvrFormat, levelWidth, levelHeight);\n\n\t\tmipmaps[i] = {\n\t\t\tdata: new Uint8Array(image.buffer, image.byteOffset + offset, levelSize),\n\t\t\twidth: levelWidth,\n\t\t\theight: levelHeight,\n\t\t};\n\n\t\tlevelWidth = Math.max(1, levelWidth >> 1);\n\t\tlevelHeight = Math.max(1, levelHeight >> 1);\n\n\t\toffset += levelSize;\n\t}\n\n\treturn {\n\t\tmipmaps: mipmaps,\n\t\twidth: width,\n\t\theight: height,\n\t\tformat: pvrFormat,\n\t\tcompressed: true,\n\t\tgenerateMipmap: false,\n\t};\n}\n","import { once, VIDEO_INIT } from \"../../../system/event.ts\";\nimport { parseDDS } from \"./parseDDS.js\";\nimport { parseKTX } from \"./parseKTX.js\";\nimport { parseKTX2 } from \"./parseKTX2.js\";\nimport { parsePKM } from \"./parsePKM.js\";\nimport { parsePVR } from \"./parsePVR.js\";\n\n/**\n * @typedef {object} CompressedTextureLevel\n * @property {Uint8Array} data - the compressed pixel data for this mip level\n * @property {number} width - width of this mip level in pixels\n * @property {number} height - height of this mip level in pixels\n */\n\n/**\n * A parsed compressed texture object, returned by the compressed texture parsers (DDS, KTX, KTX2, PVR, PKM).\n * @typedef {object} CompressedImage\n * @property {CompressedTextureLevel[]} mipmaps - array of mip levels, largest first\n * @property {number} width - width of the base (largest) mip level in pixels\n * @property {number} height - height of the base (largest) mip level in pixels\n * @property {number} format - WebGL compressed texture format constant (e.g. 0x83F0 for COMPRESSED_RGB_S3TC_DXT1_EXT)\n * @property {boolean} compressed - always true\n * @property {boolean} generateMipmap - always false (mipmaps are pre-computed)\n */\n\nlet _renderer;\n\n// gracefully capture a reference to the active renderer without adding more cyclic redundancy\nonce(VIDEO_INIT, (renderer) => {\n\t_renderer = renderer;\n});\n\n// Map file extensions to the WebGL extensions they may require.\n// If none of the listed extensions are available, parsing is skipped entirely.\n// KTX and KTX2 can contain any format, so they are not listed here and\n// always go through parsing + post-parse format check.\nconst EXT_REQUIREMENTS = {\n\tdds: [\"s3tc\", \"bptc\"],\n\tpvr: [\"pvrtc\", \"s3tc\", \"etc1\"],\n\tpkm: [\"etc1\", \"etc2\"],\n};\n\n/**\n * Check whether the renderer supports at least one of the required extensions\n * for the given file extension. Returns true if parsing should proceed.\n * @param {string} imgExt - file extension\n * @returns {boolean}\n * @ignore\n */\nfunction hasRequiredExtension(imgExt) {\n\tconst requirements = EXT_REQUIREMENTS[imgExt];\n\tif (typeof requirements === \"undefined\") {\n\t\t// no pre-filter for this extension (ktx, ktx2)\n\t\treturn true;\n\t}\n\tconst formats = _renderer.getSupportedCompressedTextureFormats();\n\tfor (let i = 0; i < requirements.length; i++) {\n\t\tif (\n\t\t\tformats[requirements[i]] !== null &&\n\t\t\ttypeof formats[requirements[i]] !== \"undefined\"\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function parseCompressedImage(arrayBuffer, imgExt) {\n\t// compressed textures need a GPU backend (WebGL or WebGPU)\n\tif (!_renderer.type.includes(\"WebGL\") && !_renderer.type.includes(\"WebGPU\")) {\n\t\tthrow new Error(\n\t\t\t\"unsupported texture format: \" + imgExt + \" (a GPU renderer required)\",\n\t\t);\n\t}\n\n\t// early rejection: check if the renderer supports any extension\n\t// that this container format could contain\n\tif (!hasRequiredExtension(imgExt)) {\n\t\tthrow new Error(\n\t\t\t\"unsupported texture format: \" + imgExt + \" (no matching GPU extension)\",\n\t\t);\n\t}\n\n\tlet texture;\n\n\tswitch (imgExt) {\n\t\tcase \"dds\":\n\t\t\ttexture = parseDDS(arrayBuffer);\n\t\t\tbreak;\n\t\tcase \"pvr\":\n\t\t\ttexture = parsePVR(arrayBuffer);\n\t\t\tbreak;\n\t\tcase \"pkm\":\n\t\t\ttexture = parsePKM(arrayBuffer);\n\t\t\tbreak;\n\t\tcase \"ktx\":\n\t\t\ttexture = parseKTX(arrayBuffer);\n\t\t\tbreak;\n\t\tcase \"ktx2\":\n\t\t\ttexture = parseKTX2(arrayBuffer);\n\t\t\tbreak;\n\t}\n\n\tif (typeof texture !== \"undefined\") {\n\t\t// post-parse check: verify the specific format constant is supported\n\t\tif (_renderer.hasSupportedCompressedFormats(texture.format)) {\n\t\t\treturn texture;\n\t\t}\n\t}\n\n\tthrow new Error(\n\t\t\"unsupported texture format: \" +\n\t\t\timgExt +\n\t\t\t(texture ? \" (\" + texture.format + \")\" : \"\"),\n\t);\n}\n","import { getExtension } from \"../../utils/file.ts\";\nimport { imgList } from \"../cache.js\";\nimport { parseCompressedImage } from \"./compressed_textures/compressed_image.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n/**\n * try to load a single image source\n * @param {string} imgPath - the image path\n * @param {string} imgName - the image name (cache key)\n * @param {Object} settings - fetch settings\n * @returns {Promise} resolves on success, rejects on failure (so the next source can be tried)\n * @ignore\n */\nfunction tryLoadSource(imgPath, imgName, settings) {\n\tconst imgExt = getExtension(imgPath);\n\n\tswitch (imgExt) {\n\t\t// Compressed textures\n\t\tcase \"dds\":\n\t\tcase \"pvr\":\n\t\tcase \"pkm\":\n\t\tcase \"ktx\":\n\t\tcase \"ktx2\":\n\t\t\treturn fetchData(imgPath, \"arrayBuffer\", settings).then((arrayBuffer) => {\n\t\t\t\t// parseCompressedImage will throw if the format is not supported\n\t\t\t\timgList[imgName] = parseCompressedImage(arrayBuffer, imgExt);\n\t\t\t});\n\n\t\t// SVG file\n\t\tcase \"svg\":\n\t\t\treturn fetchData(imgPath, \"text\", settings).then((svgText) => {\n\t\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t\tconst svgImage = new Image();\n\t\t\t\t\tsvgImage.onload = function () {\n\t\t\t\t\t\timgList[imgName] = svgImage;\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t};\n\t\t\t\t\tsvgImage.onerror = function (error) {\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t};\n\t\t\t\t\tsvgImage.src =\n\t\t\t\t\t\t\"data:image/svg+xml;charset=utf8,\" + encodeURIComponent(svgText);\n\t\t\t\t});\n\t\t\t});\n\n\t\t// default is regular images (jpg, png and friends)\n\t\tdefault:\n\t\t\treturn fetchData(imgPath, \"blob\", settings).then((blob) => {\n\t\t\t\treturn globalThis.createImageBitmap(blob).then((bitmap) => {\n\t\t\t\t\timgList[imgName] = bitmap;\n\t\t\t\t});\n\t\t\t});\n\t}\n}\n\n/**\n * parse/preload an image\n * @param {loader.Asset} img\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n * @example\n * preloadImages([\n *     { name : 'image1', src : 'images/image1.png'},\n *     { name : 'image2', src : 'images/image2.png'},\n *     { name : 'image3', src : 'images/image3.png'},\n *     // compressed texture with fallback chain\n *     { name : 'image4', src : [\n *         'images/image4.astc.ktx',  // try ASTC first\n *         'images/image4.dds',       // then S3TC via DDS\n *         'images/image4.png'        // fallback to PNG\n *     ]}\n * ]);\n */\nexport function preloadImage(img, onload, onerror, settings) {\n\tif (typeof imgList[img.name] !== \"undefined\") {\n\t\t// already loaded\n\t\treturn 0;\n\t}\n\n\tconst sources = Array.isArray(img.src) ? img.src : [img.src];\n\n\t// try each source in order; stop at the first one that succeeds\n\tlet chain = Promise.reject();\n\tfor (const imgPath of sources) {\n\t\tchain = chain.catch(() => {\n\t\t\treturn tryLoadSource(imgPath, img.name, settings);\n\t\t});\n\t}\n\n\tchain\n\t\t.then(() => {\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { jsonList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n/**\n * parse/preload a JSON files\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadJSON(data, onload, onerror, settings) {\n\tif (typeof jsonList[data.name] !== \"undefined\") {\n\t\t// already loaded\n\t\treturn 0;\n\t}\n\n\tfetchData(data.src, \"json\", settings)\n\t\t.then((response) => {\n\t\t\tjsonList[data.name] = response;\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t// callback\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { getBasename } from \"../../utils/file.ts\";\nimport { mtlList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\nimport { preloadImage } from \"./image.js\";\n\n// supported MTL properties\nconst SUPPORTED_PROPS = new Set([\n\t\"newmtl\",\n\t\"Kd\",\n\t\"Ke\",\n\t\"d\",\n\t\"Tr\",\n\t\"map_Kd\",\n\t\"Ks\",\n\t\"Ns\",\n\t\"map_d\",\n\t\"Pr\",\n\t\"Pm\",\n\t// ignored but harmless\n\t\"Ka\",\n\t\"Ni\",\n\t\"illum\",\n]);\n\n// unsupported texture maps (would need multi-texture or shader changes)\nconst UNSUPPORTED_MAPS = new Set([\n\t\"map_Ka\",\n\t\"map_Ke\",\n\t\"map_Ks\",\n\t\"map_Ns\",\n\t\"map_bump\",\n\t\"bump\",\n\t\"map_refl\",\n\t\"refl\",\n\t\"disp\",\n]);\n\n/**\n * Parse a Wavefront MTL file into material data.\n * Supports: `newmtl`, `Kd` (diffuse color), `Ke` (emissive color), `Ks`/`Ns`\n * (specular color and exponent), `Pr`/`Pm` (the PBR roughness/metalness\n * extension, approximated onto the specular terms when `Ks`/`Ns` are absent),\n * `map_Kd` (diffuse texture), `map_d` (alpha map), `d`/`Tr`\n * (opacity/transparency).\n *\n * Limitations:\n * - Ambient (`Ka`), optical density (`Ni`) and illumination model (`illum`) are parsed but ignored\n * - Normal maps (`map_bump`, `bump`), specular maps (`map_Ks`), and other texture maps are not supported\n *\n * @param {string} text - raw MTL file contents\n * @param {string} basePath - base URL path for resolving texture references\n * @returns {object} map of material names to their properties\n * @ignore\n */\nexport function parseMTL(text, basePath) {\n\tconst materials = {};\n\tlet current = null;\n\n\tconst lines = text.split(\"\\n\");\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i].trim();\n\t\tif (line.length === 0 || line[0] === \"#\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parts = line.split(/\\s+/);\n\t\tconst keyword = parts[0];\n\n\t\t// warn on unsupported texture maps\n\t\tif (UNSUPPORTED_MAPS.has(keyword)) {\n\t\t\tconsole.warn(\n\t\t\t\t\"MTL: '\" + keyword + \"' is not supported and will be ignored\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// warn on completely unknown properties\n\t\tif (!SUPPORTED_PROPS.has(keyword) && !UNSUPPORTED_MAPS.has(keyword)) {\n\t\t\tconsole.warn(\"MTL: unknown property '\" + keyword + \"' will be ignored\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch (keyword) {\n\t\t\tcase \"newmtl\":\n\t\t\t\tif (!parts[1]) {\n\t\t\t\t\tconsole.warn(\"MTL: newmtl missing material name, skipping\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent = {\n\t\t\t\t\tname: parts[1],\n\t\t\t\t\tKd: [1, 1, 1],\n\t\t\t\t\tKe: [0, 0, 0],\n\t\t\t\t\t// no specular by default: an MTL that declares none must\n\t\t\t\t\t// shade exactly as it did before specular existed\n\t\t\t\t\tKs: [0, 0, 0],\n\t\t\t\t\tNs: 0,\n\t\t\t\t\t// the PBR extension. `null` rather than a default value:\n\t\t\t\t\t// absent must be distinguishable from an authored 0, which\n\t\t\t\t\t// means \"mirror-smooth\" for Pr and \"non-metal\" for Pm\n\t\t\t\t\tPr: null,\n\t\t\t\t\tPm: null,\n\t\t\t\t\td: 1.0,\n\t\t\t\t\tmap_Kd: null,\n\t\t\t\t\tmap_d: null,\n\t\t\t\t};\n\t\t\t\tmaterials[parts[1]] = current;\n\t\t\t\tbreak;\n\n\t\t\tcase \"Kd\":\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Kd = [\n\t\t\t\t\t\tparseFloat(parts[1]),\n\t\t\t\t\t\tparseFloat(parts[2]),\n\t\t\t\t\t\tparseFloat(parts[3]),\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ke\":\n\t\t\t\t// emissive color (self-illumination — Blender's OBJ exporter\n\t\t\t\t// writes it for materials with an emission shader)\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Ke = [\n\t\t\t\t\t\tparseFloat(parts[1]),\n\t\t\t\t\t\tparseFloat(parts[2]),\n\t\t\t\t\t\tparseFloat(parts[3]),\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ks\":\n\t\t\t\t// specular color — the highlight's tint and strength\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Ks = [\n\t\t\t\t\t\tparseFloat(parts[1]),\n\t\t\t\t\t\tparseFloat(parts[2]),\n\t\t\t\t\t\tparseFloat(parts[3]),\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Ns\":\n\t\t\t\t// specular exponent (0..1000 in the format): how tight the\n\t\t\t\t// highlight is. 0 means none, which is why Ks alone is not\n\t\t\t\t// enough to turn specular on\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Ns = parseFloat(parts[1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Pr\":\n\t\t\t\t// PBR extension: roughness. Blender's OBJ exporter writes it\n\t\t\t\t// (and `Pm`) for every material, so these are already in the\n\t\t\t\t// assets people bring to the engine\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Pr = parseFloat(parts[1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Pm\":\n\t\t\t\t// PBR extension: metalness\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.Pm = parseFloat(parts[1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"d\":\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.d = parseFloat(parts[1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"Tr\":\n\t\t\t\t// Tr is inverse of d (transparency = 1 - opacity)\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.d = 1.0 - parseFloat(parts[1]);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"map_Kd\":\n\t\t\t\tif (current) {\n\t\t\t\t\t// resolve texture path relative to MTL file location\n\t\t\t\t\tcurrent.map_Kd = basePath + parts.slice(1).join(\" \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"map_d\":\n\t\t\t\t// per-texel opacity, driving the mesh alpha cutout per pixel\n\t\t\t\t// rather than per material\n\t\t\t\tif (current) {\n\t\t\t\t\tcurrent.map_d = basePath + parts.slice(1).join(\" \");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn materials;\n}\n\n/**\n * Parse/preload a Wavefront MTL material file.\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadMTL(data, onload, onerror, settings) {\n\tif (typeof mtlList[data.name] !== \"undefined\") {\n\t\treturn 0;\n\t}\n\n\t// derive base path from the MTL file URL\n\tconst basePath = data.src.substring(0, data.src.lastIndexOf(\"/\") + 1);\n\n\tfetchData(data.src, \"text\", settings)\n\t\t.then((response) => {\n\t\t\tconst materials = parseMTL(response, basePath);\n\t\t\tmtlList[data.name] = materials;\n\t\t\t// Auto-load the diffuse textures referenced by `map_Kd`, resolved\n\t\t\t// relative to the MTL file and registered under that resolved path —\n\t\t\t// so a Mesh built with `material:` (and no explicit `texture:`) finds\n\t\t\t// them via `getImage(map_Kd)` without the caller having to preload\n\t\t\t// each texture separately (parity with the glTF loader, which fetches\n\t\t\t// a scene's external textures automatically). A texture that fails to\n\t\t\t// load is warned and skipped (the mesh falls back to the white pixel),\n\t\t\t// so one missing map_Kd doesn't abort the whole load. `map_d`\n\t\t\t// (per-texel opacity) rides the same fetch for the same reason.\n\t\t\tconst texturePaths = [\n\t\t\t\t...new Set(\n\t\t\t\t\tObject.values(materials)\n\t\t\t\t\t\t.flatMap((material) => {\n\t\t\t\t\t\t\treturn [material.map_Kd, material.map_d];\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.filter(Boolean),\n\t\t\t\t),\n\t\t\t];\n\t\t\treturn Promise.all(\n\t\t\t\ttexturePaths.map((path) => {\n\t\t\t\t\treturn new Promise((resolve) => {\n\t\t\t\t\t\t// register under the basename — `getImage` (used by Mesh to\n\t\t\t\t\t\t// resolve `map_Kd`) normalizes its lookup key via getBasename,\n\t\t\t\t\t\t// so the image must be stored under that same key to be found.\n\t\t\t\t\t\tconst loading = preloadImage(\n\t\t\t\t\t\t\t{ name: getBasename(path), src: path },\n\t\t\t\t\t\t\tresolve,\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`melonJS: MTL texture \"${path}\" could not be loaded`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tsettings,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// preloadImage returns 0 when the image is already cached —\n\t\t\t\t\t\t// it then never calls our onload, so resolve now to avoid\n\t\t\t\t\t\t// hanging the Promise.all.\n\t\t\t\t\t\tif (loading === 0) {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}),\n\t\t\t);\n\t\t})\n\t\t.then(() => {\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { objList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n// OBJ line type identifiers\nconst VERTEX_PREFIX = \"v\";\nconst TEXCOORD_PREFIX = \"vt\";\nconst FACE_PREFIX = \"f\";\nconst COMMENT_CHAR = \"#\";\nconst SLASH_CHAR = \"/\";\n\n// vertex map key multiplier: pack v and vt into a single numeric key\n// supports up to Number.MAX_SAFE_INTEGER / VT_KEY_MULTIPLIER unique positions\nconst VT_KEY_MULTIPLIER = 1048576;\n\n// stride constants for position (x,y,z) and texcoord (u,v) arrays\nconst POS_STRIDE = 3;\nconst UV_STRIDE = 2;\n\n// sentinel for missing UV index\nconst NO_UV = -1;\n// OBJ vertex-normal line prefix\nconst NORMAL_PREFIX = \"vn\";\n\n// OBJ indices are 1-based\nconst OBJ_INDEX_OFFSET = 1;\n\n// largest unified vertex count a Uint16 index buffer can address\nconst UINT16_VERTEX_LIMIT = 65536;\n\n/**\n * The unsigned angle between two vectors, in radians. `0` when either is\n * degenerate, so a zero-length edge contributes no weight instead of NaN.\n * @param {number} x1\n * @param {number} y1\n * @param {number} z1\n * @param {number} x2\n * @param {number} y2\n * @param {number} z2\n * @returns {number} the angle in radians\n * @ignore\n */\nfunction angleBetween(x1, y1, z1, x2, y2, z2) {\n\tconst l1 = Math.hypot(x1, y1, z1);\n\tconst l2 = Math.hypot(x2, y2, z2);\n\tif (l1 === 0 || l2 === 0) {\n\t\treturn 0;\n\t}\n\t// clamped: rounding can push the quotient a hair outside [-1, 1], where\n\t// acos is NaN\n\tconst cosine = (x1 * x2 + y1 * y2 + z1 * z2) / (l1 * l2);\n\treturn Math.acos(cosine < -1 ? -1 : cosine > 1 ? 1 : cosine);\n}\n\n/**\n * Add `weight × (nx, ny, nz)` into an accumulator at a float offset.\n * @param {Float64Array} target - the accumulator\n * @param {number} at - float offset of the entry\n * @param {number} nx\n * @param {number} ny\n * @param {number} nz\n * @param {number} weight\n * @ignore\n */\nfunction addWeighted(target, at, nx, ny, nz, weight) {\n\ttarget[at] += nx * weight;\n\ttarget[at + 1] += ny * weight;\n\ttarget[at + 2] += nz * weight;\n}\n\n/**\n * Parse a Wavefront OBJ file into geometry data.\n * Supports: `v` (vertex positions), `vt` (texture coordinates),\n * `f` (faces in `v`, `v/vt`, `v/vt/vn`, or `v//vn` format),\n * `mtllib` (material library reference),\n * `usemtl` (material group boundaries — emitted as `groups[]`).\n *\n * Features:\n * - Quad and n-gon triangulation (fan from first vertex)\n * - Automatic CW → CCW winding correction via signed volume test\n * - V texture coordinate flipped for OpenGL convention (OBJ has origin at bottom-left)\n * - Single-pass parsing with direct vertex unification (no intermediate arrays)\n * - Material grouping: each `usemtl` switch emits a new `groups[]` entry\n *   pointing to a slice of the unified `indices` buffer, so callers\n *   (e.g. `Mesh`) can render each group with its own material without\n *   touching the geometry. A model with no `usemtl` directives produces\n *   a single group with `materialName: null`.\n *\n * `vn` (vertex normals) are consumed: a face referencing them\n * (`v//vn` or `v/vt/vn`) produces unified vertices carrying the authored\n * normal, so an OBJ model lights under `Light3d` exactly as the same model\n * imported from glTF does. Every vertex WITHOUT a usable authored normal\n * gets one GENERATED from face geometry — angle-weighted, accumulated per\n * source position and normalized, i.e. smooth across UV and material seams\n * — after the winding correction below, so the result follows the final\n * triangle orientation rather than the authored one. That rule is\n * per-vertex, so a file mixing normalled and un-normalled faces, or one\n * carrying an out-of-range `vn`, still comes out fully normalled.\n *\n * Parsed but ignored: `g` (groups), `s` (smooth shading — generated normals\n * are smooth across the whole mesh, so a model relying on hard edges from\n * smoothing groups will read softer than authored),\n * `o` (object name).\n *\n * @param {string} text - raw OBJ file contents\n * @returns {object} parsed geometry with `vertices` (Float32Array),\n *   `uvs` (Float32Array), `normals` (Float32Array), `indices`\n *   (Uint16Array, widening to Uint32Array past 65 536 vertices),\n *   `vertexCount` (number),\n *   `mtllib` (string|null), and `groups`\n *   (Array<{materialName: string|null, start: number, count: number}>).\n *   `groups` follows the glTF convention — each entry is a\n *   contiguous slice of the shared `indices` buffer that draws as one\n *   submesh against a single material. Single-material models still\n *   produce a `groups` array of length 1, so consumers don't need a\n *   special case.\n * @ignore\n */\nexport function parseOBJ(text) {\n\tconst positions = [];\n\tconst texcoords = [];\n\tconst sourceNormals = [];\n\n\t// unified output arrays (built in a single pass)\n\tconst vertices = [];\n\tconst uvs = [];\n\tconst indices = [];\n\tlet vertexCount = 0;\n\n\t// per-unified-vertex provenance, consumed by the normal resolution pass\n\t// once the whole file has been read: the `vn` this vertex asked for (or\n\t// NO_NORMAL), and the source position index it was built from\n\tconst vertexNormalIndex = [];\n\tconst vertexPosition = [];\n\n\t// Per-material vertex dedup: each material name owns its own\n\t// `vertexMap`, so the same (v, vt) reused across different\n\t// materials produces SEPARATE unified vertices (needed for\n\t// per-vertex color baking in `Mesh`), but the same material\n\t// reappearing in a later `usemtl` block re-uses its existing\n\t// vertex slots. Pre-usemtl faces use the `null` map (the\n\t// \"anonymous\" group).\n\tconst materialMaps = new Map();\n\tmaterialMaps.set(null, new Map());\n\tlet vertexMap = materialMaps.get(null);\n\n\t// helper: look up or create a unified vertex for a v/vt pair in the\n\t// current material's dedup scope\n\tfunction addVertex(v, vt, vn) {\n\t\t// The dedup key gains the normal index only when the file supplies\n\t\t// normals: two faces sharing a position/UV but referencing DIFFERENT\n\t\t// normals (a hard edge) must become separate vertices, or the edge\n\t\t// smooths itself away. A string key is used in that case rather than\n\t\t// packing a third component into the numeric one — `v * M²` overflows\n\t\t// Number.MAX_SAFE_INTEGER well before the position count does.\n\t\tconst key =\n\t\t\tvn >= 0\n\t\t\t\t? `${v}|${vt}|${vn}`\n\t\t\t\t: v * VT_KEY_MULTIPLIER + (vt + OBJ_INDEX_OFFSET);\n\t\tlet index = vertexMap.get(key);\n\t\tif (index === undefined) {\n\t\t\tindex = vertexCount++;\n\t\t\tvertexMap.set(key, index);\n\t\t\tconst v3 = v * POS_STRIDE;\n\t\t\tvertices.push(positions[v3], positions[v3 + 1], positions[v3 + 2]);\n\t\t\tif (vt >= 0) {\n\t\t\t\tconst vt2 = vt * UV_STRIDE;\n\t\t\t\tuvs.push(texcoords[vt2], texcoords[vt2 + 1]);\n\t\t\t} else {\n\t\t\t\tuvs.push(0, 0);\n\t\t\t}\n\t\t\t// Normal VALUES are resolved after the whole file is parsed (see\n\t\t\t// the resolution pass below), not here: `vn` may legally be\n\t\t\t// declared after the face referencing it, and an out-of-range\n\t\t\t// index must degrade to \"no authored normal\" rather than reading\n\t\t\t// past `sourceNormals` and writing NaN. Only the provenance is\n\t\t\t// recorded now — which normal this vertex asked for, and which\n\t\t\t// source POSITION it came from.\n\t\t\tvertexNormalIndex.push(vn);\n\t\t\tvertexPosition.push(v);\n\t\t}\n\t\treturn index;\n\t}\n\n\t/**\n\t * parse a face vertex component (e.g. \"1/2/3\" or \"1//3\" or \"1\")\n\t * and return the UV index, or NO_UV if not present\n\t * @param {string} part - face vertex string\n\t * @returns {number} UV index (0-based) or NO_UV\n\t * @ignore\n\t */\n\tfunction parseNormalIndex(part, slashIdx) {\n\t\tif (slashIdx === -1) {\n\t\t\treturn NO_UV;\n\t\t}\n\t\tconst second = part.indexOf(SLASH_CHAR, slashIdx + 1);\n\t\tif (second === -1) {\n\t\t\treturn NO_UV;\n\t\t}\n\t\treturn parseInt(part.substring(second + 1), 10) - OBJ_INDEX_OFFSET;\n\t}\n\n\t/**\n\t * parse a face vertex component and return the UV index, or NO_UV\n\t * @param {string} part - face vertex string\n\t * @param {number} slashIdx - index of the first slash\n\t * @returns {number} UV index (0-based) or NO_UV\n\t * @ignore\n\t */\n\tfunction parseUVIndex(part, slashIdx) {\n\t\tif (slashIdx !== -1 && part[slashIdx + 1] !== SLASH_CHAR) {\n\t\t\treturn parseInt(part.substring(slashIdx + 1), 10) - OBJ_INDEX_OFFSET;\n\t\t}\n\t\treturn NO_UV;\n\t}\n\n\t// mtllib reference (if present)\n\tlet mtllib = null;\n\n\t// Material grouping. Each `usemtl` switch closes the running group\n\t// (recording its index count) and opens a new one. Models without\n\t// any `usemtl` produce a single group spanning all indices with\n\t// `materialName: null` — consumers can treat that uniformly with\n\t// the multi-material path. Field name `materialName` matches the\n\t// glTF convention for \"name of the material this\n\t// submesh wants to be drawn with\"; renderers / mesh objects look\n\t// it up in their own material table.\n\tconst groups = [];\n\tconst startGroup = (materialName) => {\n\t\t// close the previous group if it has any indices\n\t\tconst prev = groups[groups.length - 1];\n\t\tif (prev) {\n\t\t\tprev.count = indices.length - prev.start;\n\t\t} else if (indices.length > 0) {\n\t\t\t// pre-usemtl indices belong to an anonymous group\n\t\t\tgroups.push({\n\t\t\t\tmaterialName: null,\n\t\t\t\tstart: 0,\n\t\t\t\tcount: indices.length,\n\t\t\t});\n\t\t}\n\t\tgroups.push({ materialName, start: indices.length, count: 0 });\n\t\t// Swap to this material's vertex dedup scope. Vertices shared\n\t\t// across materials get separate slots (required for per-vertex\n\t\t// color baking in `Mesh`), but vertices reused within the same\n\t\t// material — even across non-contiguous `usemtl` blocks — hit\n\t\t// the cache and don't get duplicated. Lazily allocated per\n\t\t// material name on first switch.\n\t\tlet cached = materialMaps.get(materialName);\n\t\tif (cached === undefined) {\n\t\t\tcached = new Map();\n\t\t\tmaterialMaps.set(materialName, cached);\n\t\t}\n\t\tvertexMap = cached;\n\t};\n\n\t// parse lines and build geometry in a single pass\n\tconst lines = text.split(\"\\n\");\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i].trim();\n\t\tif (line.length === 0 || line[0] === COMMENT_CHAR) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst first = line[0];\n\t\t// tokenize once for keyword + argument lookup. The v/vt/f\n\t\t// paths below also split on `\\s+`, so this is just hoisting\n\t\t// the same parse — handles tabs and multiple-space separators\n\t\t// consistently across every line type.\n\t\tif (first === \"m\" || first === \"u\") {\n\t\t\tconst parts = line.split(/\\s+/);\n\t\t\tif (parts[0] === \"mtllib\") {\n\t\t\t\tmtllib = parts.slice(1).join(\" \");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (parts[0] === \"usemtl\") {\n\t\t\t\tstartGroup(parts.slice(1).join(\" \"));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (first === VERTEX_PREFIX) {\n\t\t\tconst parts = line.split(/\\s+/);\n\t\t\tif (parts[0] === VERTEX_PREFIX) {\n\t\t\t\tpositions.push(\n\t\t\t\t\tparseFloat(parts[1]),\n\t\t\t\t\tparseFloat(parts[2]),\n\t\t\t\t\tparseFloat(parts[3]),\n\t\t\t\t);\n\t\t\t} else if (parts[0] === TEXCOORD_PREFIX) {\n\t\t\t\ttexcoords.push(parseFloat(parts[1]), 1.0 - parseFloat(parts[2]));\n\t\t\t} else if (parts[0] === NORMAL_PREFIX) {\n\t\t\t\t// stored raw: the Y/Z axis bridge is applied at draw through\n\t\t\t\t// the model matrix (`mat3(uModelMatrix) * aNormal`), exactly\n\t\t\t\t// as it is for glTF normals — flipping here would double it\n\t\t\t\tsourceNormals.push(\n\t\t\t\t\tparseFloat(parts[1]),\n\t\t\t\t\tparseFloat(parts[2]),\n\t\t\t\t\tparseFloat(parts[3]),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (first === FACE_PREFIX) {\n\t\t\tconst parts = line.split(/\\s+/);\n\n\t\t\t// first vertex of the fan\n\t\t\tlet slashIdx = parts[1].indexOf(SLASH_CHAR);\n\t\t\tconst v0 = parseInt(parts[1], 10) - OBJ_INDEX_OFFSET;\n\t\t\tconst vt0 = parseUVIndex(parts[1], slashIdx);\n\t\t\tconst vn0 = parseNormalIndex(parts[1], slashIdx);\n\t\t\tconst idx0 = addVertex(v0, vt0, vn0);\n\n\t\t\tlet prevIdx = -1;\n\t\t\tfor (let j = 2; j < parts.length; j++) {\n\t\t\t\tslashIdx = parts[j].indexOf(SLASH_CHAR);\n\t\t\t\tconst v = parseInt(parts[j], 10) - OBJ_INDEX_OFFSET;\n\t\t\t\tconst vt = parseUVIndex(parts[j], slashIdx);\n\t\t\t\tconst vn = parseNormalIndex(parts[j], slashIdx);\n\t\t\t\tconst idx = addVertex(v, vt, vn);\n\n\t\t\t\tif (prevIdx !== -1) {\n\t\t\t\t\tindices.push(idx0, prevIdx, idx);\n\t\t\t\t}\n\t\t\t\tprevIdx = idx;\n\t\t\t}\n\t\t}\n\t}\n\n\t// winding order check using signed volume\n\t// positive = CCW (outward normals), negative = CW (inward normals)\n\tlet signedVolume = 0;\n\tfor (let i = 0; i < indices.length; i += 3) {\n\t\tconst i0 = indices[i] * 3;\n\t\tconst i1 = indices[i + 1] * 3;\n\t\tconst i2 = indices[i + 2] * 3;\n\t\t// signed volume contribution of this triangle\n\t\tsignedVolume +=\n\t\t\tvertices[i0] *\n\t\t\t\t(vertices[i1 + 1] * vertices[i2 + 2] -\n\t\t\t\t\tvertices[i1 + 2] * vertices[i2 + 1]) +\n\t\t\tvertices[i0 + 1] *\n\t\t\t\t(vertices[i1 + 2] * vertices[i2] - vertices[i1] * vertices[i2 + 2]) +\n\t\t\tvertices[i0 + 2] *\n\t\t\t\t(vertices[i1] * vertices[i2 + 1] - vertices[i1 + 1] * vertices[i2]);\n\t}\n\n\tif (signedVolume < 0) {\n\t\t// CW winding detected — flip all triangles to CCW\n\t\tfor (let i = 0; i < indices.length; i += 3) {\n\t\t\tconst tmp = indices[i + 1];\n\t\t\tindices[i + 1] = indices[i + 2];\n\t\t\tindices[i + 2] = tmp;\n\t\t}\n\t}\n\n\t// ── normal resolution ────────────────────────────────────────────────\n\t//\n\t// Authored `vn` wins wherever one is available; every vertex left over\n\t// is GENERATED from face geometry. Running per-vertex rather than\n\t// per-file is what makes a partially-normalled model coherent: a file\n\t// mixing `f v//vn` and `f v` faces, one that declares `vn` its faces\n\t// never reference, or one carrying an out-of-range index all end up\n\t// fully normalled instead of half zero-filled.\n\t//\n\t// Deliberately AFTER the winding correction above: generated normals\n\t// follow triangle orientation, so generating first would point them\n\t// inward on a CW-wound model — the exact case that correction exists\n\t// to fix.\n\tconst normals = new Array(vertexCount * POS_STRIDE).fill(0);\n\tconst authored = new Array(vertexCount).fill(false);\n\tlet needsGenerated = false;\n\tfor (let i = 0; i < vertexCount; i++) {\n\t\tconst vn = vertexNormalIndex[i];\n\t\tconst vn3 = vn * POS_STRIDE;\n\t\t// an index past the end is a malformed file, or a `vn` line that\n\t\t// never arrived — treated as \"no authored normal\" so the generation\n\t\t// pass below covers it\n\t\tif (vn >= 0 && vn3 + POS_STRIDE <= sourceNormals.length) {\n\t\t\tconst at = i * POS_STRIDE;\n\t\t\tnormals[at] = sourceNormals[vn3];\n\t\t\tnormals[at + 1] = sourceNormals[vn3 + 1];\n\t\t\tnormals[at + 2] = sourceNormals[vn3 + 2];\n\t\t\tauthored[i] = true;\n\t\t} else {\n\t\t\tneedsGenerated = true;\n\t\t}\n\t}\n\n\tif (needsGenerated === true && vertexCount > 0) {\n\t\t// Accumulated per SOURCE POSITION rather than per unified vertex. A\n\t\t// position split by a UV seam or a `usemtl` boundary is still one\n\t\t// point on the surface, and a generated normal is smooth by\n\t\t// definition — accumulating per unified vertex would crease the\n\t\t// model along every seam, most visibly at material boundaries.\n\t\t//\n\t\t// Weighted by the INTERIOR ANGLE at each corner. Area weighting is\n\t\t// tempting because the unnormalized cross product already carries\n\t\t// twice the area, but it is wrong here: an n-gon arrives\n\t\t// fan-triangulated, which hands the fan's pivot corners two\n\t\t// triangles' worth of one face and the others only one. A cube built\n\t\t// from quads then accumulates (1, 0.5, 0.5) at a corner instead of\n\t\t// (1, 1, 1) — 19.5° off, and asymmetric on a symmetric model. Angle\n\t\t// weighting is independent of how the polygon happened to be\n\t\t// triangulated.\n\t\tconst accumulated = new Float64Array(positions.length);\n\t\tfor (let i = 0; i < indices.length; i += 3) {\n\t\t\tconst a = vertexPosition[indices[i]] * POS_STRIDE;\n\t\t\tconst b = vertexPosition[indices[i + 1]] * POS_STRIDE;\n\t\t\tconst c = vertexPosition[indices[i + 2]] * POS_STRIDE;\n\t\t\tconst abx = positions[b] - positions[a];\n\t\t\tconst aby = positions[b + 1] - positions[a + 1];\n\t\t\tconst abz = positions[b + 2] - positions[a + 2];\n\t\t\tconst acx = positions[c] - positions[a];\n\t\t\tconst acy = positions[c + 1] - positions[a + 1];\n\t\t\tconst acz = positions[c + 2] - positions[a + 2];\n\t\t\tlet nx = aby * acz - abz * acy;\n\t\t\tlet ny = abz * acx - abx * acz;\n\t\t\tlet nz = abx * acy - aby * acx;\n\t\t\tconst faceLength = Math.hypot(nx, ny, nz);\n\t\t\tif (faceLength === 0) {\n\t\t\t\t// degenerate (zero-area or collinear) — contributes no\n\t\t\t\t// direction, and normalizing it would be a division by zero\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnx /= faceLength;\n\t\t\tny /= faceLength;\n\t\t\tnz /= faceLength;\n\t\t\tconst bcx = positions[c] - positions[b];\n\t\t\tconst bcy = positions[c + 1] - positions[b + 1];\n\t\t\tconst bcz = positions[c + 2] - positions[b + 2];\n\t\t\taddWeighted(\n\t\t\t\taccumulated,\n\t\t\t\ta,\n\t\t\t\tnx,\n\t\t\t\tny,\n\t\t\t\tnz,\n\t\t\t\tangleBetween(abx, aby, abz, acx, acy, acz),\n\t\t\t);\n\t\t\taddWeighted(\n\t\t\t\taccumulated,\n\t\t\t\tb,\n\t\t\t\tnx,\n\t\t\t\tny,\n\t\t\t\tnz,\n\t\t\t\tangleBetween(-abx, -aby, -abz, bcx, bcy, bcz),\n\t\t\t);\n\t\t\taddWeighted(\n\t\t\t\taccumulated,\n\t\t\t\tc,\n\t\t\t\tnx,\n\t\t\t\tny,\n\t\t\t\tnz,\n\t\t\t\tangleBetween(-acx, -acy, -acz, -bcx, -bcy, -bcz),\n\t\t\t);\n\t\t}\n\t\tfor (let i = 0; i < vertexCount; i++) {\n\t\t\tif (authored[i] === true) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst from = vertexPosition[i] * POS_STRIDE;\n\t\t\tconst at = i * POS_STRIDE;\n\t\t\tconst length = Math.hypot(\n\t\t\t\taccumulated[from],\n\t\t\t\taccumulated[from + 1],\n\t\t\t\taccumulated[from + 2],\n\t\t\t);\n\t\t\tif (length > 0) {\n\t\t\t\tnormals[at] = accumulated[from] / length;\n\t\t\t\tnormals[at + 1] = accumulated[from + 1] / length;\n\t\t\t\tnormals[at + 2] = accumulated[from + 2] / length;\n\t\t\t} else {\n\t\t\t\t// a position referenced by no triangle, or by ones whose\n\t\t\t\t// contributions cancel exactly: leave a valid unit normal\n\t\t\t\t// rather than a zero vector, which would be NaN once\n\t\t\t\t// normalized\n\t\t\t\tnormals[at + 1] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\t// finalize the last open group (or, if no `usemtl` was ever seen,\n\t// emit a single material-less group covering all indices so the\n\t// `groups[]` contract is always non-empty for non-empty OBJs)\n\tif (groups.length === 0) {\n\t\tif (indices.length > 0) {\n\t\t\tgroups.push({ materialName: null, start: 0, count: indices.length });\n\t\t}\n\t} else {\n\t\tconst last = groups[groups.length - 1];\n\t\tlast.count = indices.length - last.start;\n\t}\n\n\treturn {\n\t\tvertices: new Float32Array(vertices),\n\t\tuvs: new Float32Array(uvs),\n\t\tnormals: new Float32Array(normals),\n\t\t// Uint16 holds indices up to 65 535, so it covers a vertex count of\n\t\t// 65 536 exactly. Past that the array must widen or every index\n\t\t// wraps mod 65 536 in silence — reachable on ordinary models now\n\t\t// that a position referencing several normals is split per normal,\n\t\t// which can treble the unified vertex count of a flat-shaded mesh.\n\t\tindices:\n\t\t\tvertexCount > UINT16_VERTEX_LIMIT\n\t\t\t\t? new Uint32Array(indices)\n\t\t\t\t: new Uint16Array(indices),\n\t\tvertexCount,\n\t\tmtllib,\n\t\tgroups,\n\t};\n}\n\n/**\n * parse/preload a Wavefront OBJ file\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadOBJ(data, onload, onerror, settings) {\n\tif (typeof objList[data.name] !== \"undefined\") {\n\t\t// already loaded\n\t\treturn 0;\n\t}\n\n\tfetchData(data.src, \"text\", settings)\n\t\t.then((response) => {\n\t\t\tobjList[data.name] = parseOBJ(response);\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","/**\n * parse/preload a Javascript files\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadJavascript(data, onload, onerror, settings) {\n\tconst script = globalThis.document.createElement(\"script\");\n\n\tscript.src = data.src;\n\tscript.type = \"text/javascript\";\n\tif (typeof settings.crossOrigin === \"string\") {\n\t\tscript.crossOrigin = settings.crossOrigin;\n\t}\n\tscript.defer = true;\n\n\tif (typeof onload === \"function\") {\n\t\tscript.onload = () => {\n\t\t\t// callback\n\t\t\tonload();\n\t\t};\n\t}\n\n\tif (typeof onerror === \"function\") {\n\t\tscript.onerror = () => {\n\t\t\t// callback\n\t\t\tonerror(data.name);\n\t\t};\n\t}\n\n\tglobalThis.document.getElementsByTagName(\"body\")[0].appendChild(script);\n\n\treturn 1;\n}\n","import { on, VIDEO_INIT } from \"../../system/event.ts\";\nimport ShaderEffect from \"../../video/effects/shadereffect.js\";\nimport GLShader from \"../../video/webgl/glshader.js\";\nimport { shaderList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n// a `wgsl` source is either an effect BODY (the apply() convention — the\n// realization generates the vertex stage around it) or a complete MODULE\n// (a custom mesh shader). A module must declare its own `@vertex` entry\n// point named `vertex_main`; a body never legally can — the realization\n// already emits one, so a body containing it would fail compilation\n// anyway. Comments are stripped before sniffing: a body whose comment\n// merely MENTIONS \"@vertex\" must not be misrouted into a raw program.\nconst WGSL_MODULE = /@vertex\\b[\\s\\S]*?\\bfn\\s+vertex_main\\b/;\nconst WGSL_COMMENTS = /\\/\\/[^\\n]*|\\/\\*[\\s\\S]*?\\*\\//g;\n\nfunction isWGSLModule(source) {\n\treturn WGSL_MODULE.test(source.replace(WGSL_COMMENTS, \"\"));\n}\n\nlet _renderer;\n\n// gracefully capture a reference to the active renderer without adding more\n// cyclic redundancy (same pattern as the compressed-textures parser). `on`\n// rather than `once` so a re-init (renderer switch, tests) stays current.\non(VIDEO_INIT, (renderer) => {\n\t_renderer = renderer;\n});\n\n/**\n * compile a shader asset's source into its shared, loader-owned instance:\n * a fragment body (the `apply()` convention) compiles into a\n * {@link ShaderEffect}; a complete program — a `{vertex, fragment}` GLSL\n * pair and/or a `wgsl` full module, either omittable — compiles into a\n * {@link GLShader} carrying whichever realizations were declared, for the\n * hosted paths that take one (a `Mesh` custom shader,\n * `renderer.customShader`, a custom batcher). The active renderer hosts\n * the realization it speaks; a shader without one for this backend is\n * inert — assigning it degrades to built-in shading, never fails.\n * @param {string|object} source - the fragment body (or `{glsl, wgsl}`\n * bodies), or a complete program: `{vertex, fragment, wgsl?, precision?}`\n * @returns {ShaderEffect|GLShader} the compiled asset, flagged `shared`\n * @throws if called before any `app.init()` resolved (no renderer to compile against)\n * @ignore\n */\nexport function compileShaderAsset(source) {\n\tif (typeof _renderer === \"undefined\") {\n\t\tthrow new Error(\n\t\t\t\"shader assets require an initialized Application — `await app.init()` first (no renderer available to compile against)\",\n\t\t);\n\t}\n\tif (typeof source === \"object\" && source !== null) {\n\t\tconst hasPair =\n\t\t\ttypeof source.vertex === \"string\" && typeof source.fragment === \"string\";\n\t\t// a wgsl source that declares an @vertex vertex_main is a complete\n\t\t// module (a custom mesh shader), not an effect body — see above\n\t\tconst wgslModule =\n\t\t\ttypeof source.wgsl === \"string\" && isWGSLModule(source.wgsl)\n\t\t\t\t? source.wgsl\n\t\t\t\t: null;\n\n\t\t// complete-program shapes → one GLShader carrying every declared\n\t\t// realization; `renderer.gl` is undefined on non-WebGL backends,\n\t\t// which simply skips the GLSL compile (isWebGL stays false)\n\t\tif (hasPair || wgslModule !== null) {\n\t\t\tconst shader = new GLShader(_renderer.gl, {\n\t\t\t\tvertex: source.vertex,\n\t\t\t\tfragment: source.fragment,\n\t\t\t\tprecision: source.precision,\n\t\t\t\twgsl: wgslModule ?? undefined,\n\t\t\t\tlabel: \"melonJS shader asset\",\n\t\t\t});\n\t\t\tshader.shared = true;\n\t\t\treturn shader;\n\t\t}\n\n\t\t// dual-language fragment bodies ({glsl, wgsl} — either may be\n\t\t// omitted): the ShaderEffect constructor picks the body matching\n\t\t// the renderer's shading language, and degrades to the inert stub\n\t\t// when none matches — the preload itself always succeeds\n\t\tif (typeof source.glsl === \"string\" || typeof source.wgsl === \"string\") {\n\t\t\tconst effect = new ShaderEffect(_renderer, source);\n\t\t\teffect.shared = true;\n\t\t\treturn effect;\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"a program pair needs both `vertex` and `fragment` GLSL sources\",\n\t\t);\n\t}\n\tconst effect = new ShaderEffect(_renderer, source);\n\t// loader-owned: a renderable's cleanup must never auto-destroy it —\n\t// only loader.unload() / unloadAll() does (see the `shared` flag)\n\teffect.shared = true;\n\treturn effect;\n}\n\n/**\n * parse/preload a shader asset, from a `src` URL (or data: URI) or inline\n * source via the `data` field. Three source shapes are accepted:\n *\n * - a GLSL **fragment body** following the ShaderEffect convention (uniform\n *   declarations + `vec4 apply(vec4, vec2)`) → compiles into a shared\n *   {@link ShaderEffect};\n * - a **dual-language body** — `src: {glsl: url, wgsl: url}` or\n *   `data: {glsl: source, wgsl: source}`, either language omittable — →\n *   a shared {@link ShaderEffect} carrying one body per shading language;\n *   the renderer compiles the matching one, and when none matches the\n *   preload still succeeds with an inert (`enabled === false`) effect;\n * - a complete **program** — `src: {vertex: url, fragment: url}` (GLSL\n *   pair), `src: {wgsl: url}` where the WGSL declares its own `@vertex`\n *   entry point (a complete module), or both together for a dual-backend\n *   asset — → compiles into a shared raw {@link GLShader} carrying the\n *   declared realizations (`isWebGL` / `isWebGPU`), for the hosted paths\n *   that take one (a `Mesh` custom shader, `renderer.customShader`, a\n *   custom batcher); the active renderer hosts the realization it speaks\n *   and a missing one degrades to built-in shading.\n *\n * Always compiled AT LOAD TIME, so the GLSL compile cost lands in the\n * loading screen and compile errors carry the asset name. An initialized\n * Application (`await app.init()`) is an inherent precondition of the\n * preload flow (the loading screen itself needs the renderer, and a failed\n * init rejects and halts) — loading a shader without it fails with a clear\n * error.\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadShader(data, onload, onerror, settings) {\n\tif (typeof shaderList[data.name] !== \"undefined\") {\n\t\t// already loaded\n\t\treturn 0;\n\t}\n\n\t// inline GLSL source via the `data` field (same convention as inline\n\t// TMX): a fragment-body string, or a {vertex, fragment} program pair\n\tif (\n\t\ttypeof data.data === \"string\" ||\n\t\t(typeof data.data === \"object\" && data.data !== null)\n\t) {\n\t\ttry {\n\t\t\tshaderList[data.name] = compileShaderAsset(data.data);\n\t\t} catch (error) {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(new Error(`shader asset \"${data.name}\": ${error.message}`));\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tif (typeof onload === \"function\") {\n\t\t\tonload();\n\t\t}\n\t\treturn 1;\n\t}\n\n\t// `src` as an object of URLs — {glsl, wgsl} effect bodies, a\n\t// {vertex, fragment} GLSL program pair, or a complete dual-backend\n\t// program {vertex, fragment, wgsl}: fetch whatever is declared and\n\t// let compileShaderAsset dispatch on the assembled shape\n\tif (typeof data.src === \"object\" && data.src !== null) {\n\t\tconst fields = [\"glsl\", \"wgsl\", \"vertex\", \"fragment\"].filter((field) => {\n\t\t\treturn typeof data.src[field] === \"string\";\n\t\t});\n\t\tconst pairComplete =\n\t\t\t(typeof data.src.vertex === \"string\") ===\n\t\t\t(typeof data.src.fragment === \"string\");\n\t\tif (fields.length === 0 || !pairComplete) {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\tfields.length === 0\n\t\t\t\t\t\t\t? `shader asset \"${data.name}\": \\`src\\` needs {glsl, wgsl} body URLs and/or a {vertex, fragment} program pair`\n\t\t\t\t\t\t\t: `shader asset \"${data.name}\": a program pair needs both \\`src.vertex\\` and \\`src.fragment\\` URLs`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tPromise.all(\n\t\t\tfields.map((field) => {\n\t\t\t\treturn fetchData(data.src[field], \"text\", settings);\n\t\t\t}),\n\t\t)\n\t\t\t.then((sources) => {\n\t\t\t\t// concurrent-load guard — see the single-source path below\n\t\t\t\tif (typeof shaderList[data.name] === \"undefined\") {\n\t\t\t\t\tconst parts = {};\n\t\t\t\t\tfields.forEach((field, index) => {\n\t\t\t\t\t\tparts[field] = sources[index];\n\t\t\t\t\t});\n\t\t\t\t\tif (typeof data.precision === \"string\") {\n\t\t\t\t\t\tparts.precision = data.precision;\n\t\t\t\t\t}\n\t\t\t\t\tshaderList[data.name] = compileShaderAsset(parts);\n\t\t\t\t}\n\t\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t\tonload();\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\t\tonerror(new Error(`shader asset \"${data.name}\": ${error.message}`));\n\t\t\t\t}\n\t\t\t});\n\t\treturn 1;\n\t}\n\n\tif (typeof data.src !== \"string\") {\n\t\tif (typeof onerror === \"function\") {\n\t\t\tonerror(\n\t\t\t\tnew Error(\n\t\t\t\t\t`shader asset \"${data.name}\": needs a \\`src\\` URL (or {vertex, fragment} URL pair), or inline GLSL via \\`data\\``,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn 1;\n\t}\n\n\tfetchData(data.src, \"text\", settings)\n\t\t.then((source) => {\n\t\t\t// a concurrent load for the same name may have stored while this\n\t\t\t// fetch was in flight — compiling again would orphan a live GL\n\t\t\t// program (pinned by its context-loss event subscriptions, so\n\t\t\t// never GC-eligible and unreachable by unload)\n\t\t\tif (typeof shaderList[data.name] === \"undefined\") {\n\t\t\t\tshaderList[data.name] = compileShaderAsset(source);\n\t\t\t}\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(new Error(`shader asset \"${data.name}\": ${error.message}`));\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { level } from \"../../level/level.js\";\nimport * as TMXUtils from \"../../level/tiled/TMXUtils.js\";\nimport { getExtension } from \"../../utils/file.ts\";\nimport { tmxList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n/**\n * parse/preload a TMX file\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the asset is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadTMX(tmxData, onload, onerror, settings) {\n\tif (typeof tmxList[tmxData.name] !== \"undefined\") {\n\t\t// already loaded\n\t\treturn 0;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tfunction addToTMXList(data) {\n\t\t// set the TMX content\n\t\ttmxList[tmxData.name] = data;\n\n\t\t// add the tmx to the level manager\n\t\tif (tmxData.type === \"tmx\") {\n\t\t\tlevel.add(tmxData.type, tmxData.name);\n\t\t}\n\t}\n\n\t//if the data is in the tmxData object, don't get it via a XMLHTTPRequest\n\tif (tmxData.data) {\n\t\taddToTMXList(tmxData.data);\n\t\tif (typeof onload === \"function\") {\n\t\t\tonload();\n\t\t}\n\t\treturn 1;\n\t}\n\n\tfetchData(tmxData.src, \"text\", settings)\n\t\t.then((response) => {\n\t\t\tif (typeof response !== \"string\") {\n\t\t\t\tthrow new Error(\"Invalid response type\");\n\t\t\t}\n\n\t\t\tlet result;\n\n\t\t\tswitch (getExtension(tmxData.src)) {\n\t\t\t\tcase \"xml\":\n\t\t\t\tcase \"tmx\":\n\t\t\t\tcase \"tsx\": {\n\t\t\t\t\tconst parser = new DOMParser();\n\n\t\t\t\t\tif (typeof parser.parseFromString === \"undefined\") {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\"XML file format loading supported, use the JSON file format instead\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst xmlDoc = parser.parseFromString(response, \"text/xml\");\n\t\t\t\t\tconst data = TMXUtils.parse(xmlDoc);\n\n\t\t\t\t\tresult = data.map || data.tilesets[0] || data;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"json\":\n\t\t\t\tcase \"tmj\":\n\t\t\t\tcase \"tsj\":\n\t\t\t\t\tresult = JSON.parse(response);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`TMX file format not supported: ${getExtension(tmxData.src)}`,\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\taddToTMXList(result);\n\n\t\t\tif (typeof onload === \"function\") {\n\t\t\t\tonload();\n\t\t\t}\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\tonerror(error);\n\t\t\t}\n\t\t});\n\n\treturn 1;\n}\n","import { hasVideoFormat } from \"../../system/device.js\";\nimport * as fileUtil from \"../../utils/file.ts\";\nimport { isDataUrl } from \"../../utils/string.ts\";\nimport { videoList } from \"../cache.js\";\nimport { fetchData } from \"./fetchdata.js\";\n\n/**\n * parse/preload a Video file\n * @param {loader.Asset} data - asset data\n * @param {Function} [onload] - function to be called when the resource is loaded\n * @param {Function} [onerror] - function to be called in case of error\n * @param {Object} [settings] - Additional settings to be passed when loading the asset\n * @returns {number} the amount of corresponding resource parsed/preloaded\n * @ignore\n */\nexport function preloadVideo(data, onload, onerror, settings) {\n\tif (typeof videoList[data.name] !== \"undefined\") {\n\t\t// Video already preloaded\n\t\treturn 0;\n\t}\n\n\tconst videoElement = (videoList[data.name] =\n\t\tglobalThis.document.createElement(\"video\"));\n\n\tif (isDataUrl(data.src)) {\n\t\tconst mimeMatch = data.src.match(/[^:]\\w+\\/[\\w-+\\d.]+(?=;|,)/);\n\t\tconst mimeType = mimeMatch ? mimeMatch[0] : null;\n\t\tif (!mimeType || videoElement.canPlayType(mimeType) === \"\") {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid dataURL or Video file format not supported: ${mimeType}`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (!hasVideoFormat(fileUtil.getExtension(data.src))) {\n\t\t\tthrow new Error(\n\t\t\t\t`Video file format not supported: ${fileUtil.getExtension(data.src)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (isDataUrl(data.src)) {\n\t\tfetchData(data.src, \"blob\", settings)\n\t\t\t.then((blob) => {\n\t\t\t\tvideoElement.src = globalThis.URL.createObjectURL(blob);\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tif (typeof onerror === \"function\") {\n\t\t\t\t\tonerror(error);\n\t\t\t\t}\n\t\t\t});\n\t} else {\n\t\t// just a url path\n\t\tvideoElement.src = data.src;\n\t}\n\n\tvideoElement.setAttribute(\n\t\t\"preload\",\n\t\tdata.stream === true ? \"metadata\" : \"auto\",\n\t);\n\tvideoElement.setAttribute(\"playsinline\", \"true\");\n\tvideoElement.setAttribute(\"disablePictureInPicture\", \"true\");\n\tvideoElement.setAttribute(\"controls\", \"false\");\n\t// only stamp crossorigin when actually configured: per the HTML spec a\n\t// MISSING attribute means no CORS, but an INVALID value (e.g. the string\n\t// \"undefined\") maps to Anonymous — which force-enables CORS and breaks\n\t// cross-origin sources served without CORS headers. Note \"\" is a valid\n\t// value (anonymous), so guard on type, not truthiness.\n\tif (typeof settings.crossOrigin === \"string\") {\n\t\tvideoElement.setAttribute(\"crossorigin\", settings.crossOrigin);\n\t}\n\n\tif (data.autoplay === true) {\n\t\tvideoElement.setAttribute(\"autoplay\", \"true\");\n\t}\n\tif (data.loop === true) {\n\t\tvideoElement.setAttribute(\"loop\", \"true\");\n\t}\n\n\tif (typeof onload === \"function\") {\n\t\t// some mobile browsers (e.g. safari) won't emit the canplay event\n\t\t// unless the video actually plays — only wait for it when autoplay\n\t\t// was explicitly requested; everywhere else (including the common\n\t\t// case of autoplay simply omitted) loadedmetadata is the reliable\n\t\t// \"preloaded\" signal, otherwise preload hangs forever on\n\t\t// autoplay-restricted browsers. Streaming videos always use\n\t\t// loadedmetadata — their preload=\"metadata\" never buffers to canplay.\n\t\tif (data.stream === true || data.autoplay !== true) {\n\t\t\tvideoElement.onloadedmetadata = () => {\n\t\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t\tonload();\n\t\t\t\t}\n\t\t\t};\n\t\t} else {\n\t\t\tvideoElement.oncanplay = () => {\n\t\t\t\tif (typeof onload === \"function\") {\n\t\t\t\t\tonload();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\tif (typeof onerror === \"function\") {\n\t\tvideoElement.onerror = (error) => {\n\t\t\tonerror(error);\n\t\t};\n\t}\n\n\tvideoElement.load();\n\n\treturn 1;\n}\n","import { ETA } from \"../../../math/math.ts\";\nimport { Vector2d } from \"../../../math/vector2d.ts\";\n\n/**\n * parse the given data and return a corresponding atlas\n * @param {Object} data - atlas data information. See {@link loader.getJSON}\n * @param {TextureAtlas} textureAtlas - the texture atlas class calling the parser\n * @returns {Object} the corresponding Atlas\n * @ignore\n */\nexport function parseAseprite(data, textureAtlas) {\n\tconst atlas = {};\n\n\t// per-frame durations in frame order, consumed by the frameTags loop\n\t// below to build per-frame animation delays\n\tconst frameDurations = [];\n\n\tconst frames = data.frames;\n\tfor (const name in frames) {\n\t\t// in aseprite's JSON export (hash and array forms alike), `trimmed`,\n\t\t// `spriteSourceSize`, `sourceSize`, `rotated`, `pivot` and `duration`\n\t\t// are SIBLINGS of the `frame` rect on each entry — same layout as\n\t\t// TexturePacker's (see texturepacker.js)\n\t\tconst entry = frames[name];\n\t\tconst frame = entry.frame;\n\t\tconst trimmed = !!entry.trimmed;\n\n\t\tlet trim;\n\n\t\tif (trimmed) {\n\t\t\ttrim = {\n\t\t\t\tx: entry.spriteSourceSize.x,\n\t\t\t\ty: entry.spriteSourceSize.y,\n\t\t\t\tw: entry.spriteSourceSize.w,\n\t\t\t\th: entry.spriteSourceSize.h,\n\t\t\t};\n\t\t}\n\n\t\tlet originX;\n\t\tlet originY;\n\t\t// Pixel-based offset origin from the top-left of the source frame\n\t\tconst hasTextureAnchorPoint = entry.sourceSize && entry.pivot;\n\t\tif (hasTextureAnchorPoint) {\n\t\t\toriginX = entry.sourceSize.w * entry.pivot.x - (trimmed ? trim.x : 0);\n\t\t\toriginY = entry.sourceSize.h * entry.pivot.y - (trimmed ? trim.y : 0);\n\t\t}\n\n\t\tatlas[name] = {\n\t\t\tname: name, // frame name\n\t\t\ttexture: data.meta.image || \"default\", // the source texture\n\t\t\toffset: new Vector2d(frame.x, frame.y),\n\t\t\tanchorPoint: hasTextureAnchorPoint\n\t\t\t\t? new Vector2d(originX / frame.w, originY / frame.h)\n\t\t\t\t: null,\n\t\t\ttrimmed: trimmed,\n\t\t\ttrim: trim,\n\t\t\twidth: frame.w,\n\t\t\theight: frame.h,\n\t\t\tsourceSize: entry.sourceSize || { w: frame.w, h: frame.h },\n\t\t\tangle: entry.rotated === true ? -ETA : 0,\n\t\t};\n\t\tframeDurations.push(entry.duration);\n\t\ttextureAtlas.addUVs(atlas, name, data.meta.size.w, data.meta.size.h);\n\t}\n\n\tconst anims = {};\n\tfor (const name in data.meta.frameTags) {\n\t\tconst anim = data.meta.frameTags[name];\n\t\t// aseprite provides a [from..to] frame range plus a per-frame\n\t\t// `duration` (ms) on each frame — build frame objects carrying each\n\t\t// frame's own delay so the animation plays at its authored timing\n\t\t// (100 ms is aseprite's default duration, used as a safety net)\n\t\tconst indexArray = Array.from(\n\t\t\t{ length: anim.to - anim.from + 1 },\n\t\t\t(_, i) => {\n\t\t\t\tconst idx = anim.from + i;\n\t\t\t\treturn {\n\t\t\t\t\tname: idx,\n\t\t\t\t\tdelay:\n\t\t\t\t\t\ttypeof frameDurations[idx] === \"number\" ? frameDurations[idx] : 100,\n\t\t\t\t};\n\t\t\t},\n\t\t);\n\t\tanims[name] = {\n\t\t\tname: anim.name,\n\t\t\tindex: indexArray,\n\t\t\t// only \"forward\" is supported for now\n\t\t\tdirection: anim.direction,\n\t\t};\n\t}\n\tatlas.anims = anims;\n\n\treturn atlas;\n}\n","import { Vector2d, vector2dPool } from \"../../../math/vector2d.ts\";\n\n/**\n * parse the given data and return a corresponding atlas\n * @param {Object} data - atlas data information. See {@link loader.getJSON}\n * @param {TextureAtlas} textureAtlas - the texture atlas class calling the parser\n * @returns {Object} the corresponding Atlas\n * @ignore\n */\nexport function parseSpriteSheet(data, textureAtlas) {\n\tconst atlas = {};\n\tconst image = data.image;\n\tconst spacing = data.spacing || 0;\n\tconst margin = data.margin || 0;\n\n\tconst width = image.width;\n\tconst height = image.height;\n\n\t// calculate the sprite count (line, col)\n\tconst spritecount = vector2dPool.get(\n\t\t~~((width - margin + spacing) / (data.framewidth + spacing)),\n\t\t~~((height - margin + spacing) / (data.frameheight + spacing)),\n\t);\n\n\t// verifying the texture size. The frame GRID is implicitly truncated by\n\t// the floored spritecount above; the UV divisor below must stay the\n\t// PHYSICAL image size — the GPU texture is uploaded at full size, so\n\t// dividing by a truncated size would scale/shift every frame's UVs.\n\tif (\n\t\twidth % (data.framewidth + spacing) !== 0 ||\n\t\theight % (data.frameheight + spacing) !== 0\n\t) {\n\t\tconst computed_width = spritecount.x * (data.framewidth + spacing);\n\t\tconst computed_height = spritecount.y * (data.frameheight + spacing);\n\t\tif (\n\t\t\tcomputed_width - width !== spacing &&\n\t\t\tcomputed_height - height !== spacing\n\t\t) {\n\t\t\t// warning message\n\t\t\tconsole.warn(\n\t\t\t\t\"Spritesheet Texture for image: \" +\n\t\t\t\t\timage.src +\n\t\t\t\t\t\" is not divisible by \" +\n\t\t\t\t\t(data.framewidth + spacing) +\n\t\t\t\t\t\"x\" +\n\t\t\t\t\t(data.frameheight + spacing) +\n\t\t\t\t\t\", truncating the frame grid to \" +\n\t\t\t\t\tcomputed_width +\n\t\t\t\t\t\"x\" +\n\t\t\t\t\tcomputed_height,\n\t\t\t);\n\t\t}\n\t}\n\n\t// build the local atlas\n\tfor (\n\t\tlet frame = 0, count = spritecount.x * spritecount.y;\n\t\tframe < count;\n\t\tframe++\n\t) {\n\t\tconst name = \"\" + frame;\n\t\tatlas[name] = {\n\t\t\tname: name,\n\t\t\ttexture: \"default\", // the source texture\n\t\t\toffset: new Vector2d(\n\t\t\t\tmargin + (spacing + data.framewidth) * (frame % spritecount.x),\n\t\t\t\tmargin + (spacing + data.frameheight) * ~~(frame / spritecount.x),\n\t\t\t),\n\t\t\tanchorPoint: data.anchorPoint || null,\n\t\t\ttrimmed: false,\n\t\t\ttrim: undefined,\n\t\t\twidth: data.framewidth,\n\t\t\theight: data.frameheight,\n\t\t\tangle: 0,\n\t\t};\n\t\ttextureAtlas.addUVs(atlas, name, width, height);\n\t}\n\n\tvector2dPool.release(spritecount);\n\n\treturn atlas;\n}\n","import { ETA } from \"./../../../math/math.ts\";\nimport { Vector2d } from \"../../../math/vector2d.ts\";\n\n/**\n * parse the given data and return a corresponding atlas\n * @param {Object} data - atlas data information. See {@link loader.getJSON}\n * @param {TextureAtlas} textureAtlas - the texture atlas class calling the parser\n * @returns {Object} the corresponding Atlas\n * @ignore\n */\nexport function parseTexturePacker(data, textureAtlas) {\n\tconst atlas = {};\n\n\tdata.frames.forEach((frame) => {\n\t\t// fix wrongly formatted JSON (e.g. last dummy object in ShoeBox)\n\t\tif (frame.hasOwnProperty(\"filename\")) {\n\t\t\t// Source coordinates\n\t\t\tconst s = frame.frame;\n\t\t\tconst trimmed = !!frame.trimmed;\n\n\t\t\tlet trim;\n\n\t\t\tif (trimmed) {\n\t\t\t\ttrim = {\n\t\t\t\t\tx: frame.spriteSourceSize.x,\n\t\t\t\t\ty: frame.spriteSourceSize.y,\n\t\t\t\t\tw: frame.spriteSourceSize.w,\n\t\t\t\t\th: frame.spriteSourceSize.h,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tlet originX;\n\t\t\tlet originY;\n\t\t\t// Pixel-based offset origin from the top-left of the source frame\n\t\t\tconst hasTextureAnchorPoint = frame.sourceSize && frame.pivot;\n\t\t\tif (hasTextureAnchorPoint) {\n\t\t\t\toriginX = frame.sourceSize.w * frame.pivot.x - (trimmed ? trim.x : 0);\n\t\t\t\toriginY = frame.sourceSize.h * frame.pivot.y - (trimmed ? trim.y : 0);\n\t\t\t}\n\n\t\t\tatlas[frame.filename] = {\n\t\t\t\tname: frame.filename, // frame name\n\t\t\t\ttexture: data.meta.image || \"default\", // the source texture\n\t\t\t\toffset: new Vector2d(s.x, s.y),\n\t\t\t\tanchorPoint: hasTextureAnchorPoint\n\t\t\t\t\t? new Vector2d(originX / s.w, originY / s.h)\n\t\t\t\t\t: null,\n\t\t\t\ttrimmed: trimmed,\n\t\t\t\ttrim: trim,\n\t\t\t\twidth: s.w,\n\t\t\t\theight: s.h,\n\t\t\t\tsourceSize: frame.sourceSize || { w: s.w, h: s.h },\n\t\t\t\tangle: frame.rotated === true ? -ETA : 0,\n\t\t\t};\n\t\t\ttextureAtlas.addUVs(\n\t\t\t\tatlas,\n\t\t\t\tframe.filename,\n\t\t\t\tdata.meta.size.w,\n\t\t\t\tdata.meta.size.h,\n\t\t\t);\n\t\t}\n\t});\n\treturn atlas;\n}\n","import { getImage } from \"./../../loader/loader.js\";\nimport { Vector2d } from \"../../math/vector2d.ts\";\nimport Sprite from \"./../../renderable/sprite.js\";\nimport { on, VIDEO_INIT } from \"../../system/event.ts\";\nimport pool from \"../../system/legacy_pool.js\";\nimport { parseAseprite } from \"./parser/aseprite.js\";\nimport { parseSpriteSheet } from \"./parser/spritesheet.js\";\nimport { parseTexturePacker } from \"./parser/texturepacker.js\";\nimport Texture2d from \"./texture2d.ts\";\n\n// The active renderer, captured at video init — atlas registration goes\n// through the renderer's own cache rather than the global game instance\n// (`on`, not `once`: a re-init — new Application after destroy — must\n// re-capture the new renderer).\nlet _renderer;\non(VIDEO_INIT, (renderer) => {\n\t_renderer = renderer;\n});\n\n/**\n * additional import for TypeScript\n * @import NineSliceSprite from \"./../../renderable/nineslicesprite.js\";\n */\n\n/**\n * create a simple 1 frame texture atlas based on the given parameters\n * @ignore\n */\nexport function createAtlas(\n\twidth,\n\theight,\n\tname = \"default\",\n\trepeat = \"no-repeat\",\n) {\n\treturn {\n\t\tmeta: {\n\t\t\tapp: \"melonJS\",\n\t\t\tsize: { w: width, h: height },\n\t\t\trepeat: repeat,\n\t\t\timage: \"default\",\n\t\t},\n\t\tframes: [\n\t\t\t{\n\t\t\t\tfilename: name,\n\t\t\t\tframe: { x: 0, y: 0, w: width, h: height },\n\t\t\t},\n\t\t],\n\t};\n}\n\n/**\n * return a string that identifies the texture atlas type\n * @ignore\n */\nexport function identifyFormat(app) {\n\tif (app.includes(\"texturepacker\") || app.includes(\"free-tex-packer\")) {\n\t\treturn \"texturepacker\";\n\t} else if (app.includes(\"shoebox\")) {\n\t\treturn \"shoebox\";\n\t} else if (app.includes(\"aseprite\")) {\n\t\treturn \"aseprite\";\n\t} else if (app.includes(\"melonJS\")) {\n\t\treturn \"melonJS\";\n\t} else {\n\t\tthrow new Error(\"Unknown texture atlas format: \" + app);\n\t}\n}\n\n/**\n * A Texture atlas class, currently supports : <br>\n * - [TexturePacker]{@link http://www.codeandweb.com/texturepacker/} : through JSON export (standard and multipack texture atlas) <br>\n * - [Free Texture Packer]{@link http://free-tex-packer.com/app/} : through JSON export (standard and multipack texture atlas) <br>\n * - [aseprite]{@link https://www.aseprite.org/} : through JSON export (standard and multipack texture atlas) <br>\n * - [ShoeBox]{@link http://renderhjs.net/shoebox/} : through JSON export using the\n * melonJS setting [file]{@link https://github.com/melonjs/melonJS/raw/master/media/shoebox_JSON_export.sbx} <br>\n * - Standard (fixed cell size) spritesheet : through a {framewidth:xx, frameheight:xx, anchorPoint:me.Vector2d} object\n * );\n * @category Game Objects\n */\nexport class TextureAtlas extends Texture2d {\n\t/**\n\t * @param {object|object[]} atlases - atlas information. See {@link loader.getJSON}\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|CompressedImage|string|OffscreenCanvas[]|HTMLImageElement[]|HTMLCanvasElement[]|string[]} [src=atlas.meta.image] - Image source\n\t * @param {boolean|object} [options] - either a boolean (legacy `cache` flag — `false` disables `TextureCache` registration; default behavior is to cache) or an options object `{ cache?: boolean, normalMap?: HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|string }`. When `normalMap` is provided, the atlas exposes a paired normal-map texture sharing the same UVs as the color texture (used by the WebGL renderer's lit pipeline). `HTMLVideoElement` is intentionally not supported as a normal map (would freeze on frame 0 due to per-image GL texture caching).\n\t * @example\n\t * // create a texture atlas from a JSON Object\n\t * game.texture = new me.TextureAtlas(\n\t *     me.loader.getJSON(\"texture\")\n\t * );\n\t *\n\t * // create a texture atlas from a multipack JSON Object\n\t * game.texture = new me.TextureAtlas([\n\t *     me.loader.getJSON(\"texture-0\"),\n\t *     me.loader.getJSON(\"texture-1\"),\n\t *     me.loader.getJSON(\"texture-2\")\n\t * ]);\n\t *\n\t * // create a texture atlas for a spritesheet with an anchorPoint in the center of each frame\n\t * game.texture = new me.TextureAtlas(\n\t *     {\n\t *         framewidth : 32,\n\t *         frameheight : 32,\n\t *         anchorPoint : new me.Vector2d(0.5, 0.5)\n\t *     },\n\t *     me.loader.getImage(\"spritesheet\")\n\t * );\n\t *\n\t * // SpriteIlluminator workflow: pair the color atlas with its normal map\n\t * game.texture = new me.TextureAtlas(\n\t *     me.loader.getJSON(\"scene\"),\n\t *     me.loader.getImage(\"scene\"),\n\t *     { normalMap: me.loader.getImage(\"scene_n\") }\n\t * );\n\t */\n\tconstructor(atlases, src, options) {\n\t\tsuper();\n\t\t// backward compat: 3rd arg used to be a `cache` boolean.\n\t\tconst opts =\n\t\t\ttypeof options === \"boolean\" ? { cache: options } : options || {};\n\t\tconst cache = opts.cache;\n\t\tconst normalMap = opts.normalMap;\n\t\t/**\n\t\t * to identify the atlas format (e.g. texture packer)\n\t\t * @ignore\n\t\t */\n\t\tthis.format = null;\n\n\t\t/**\n\t\t * the texture source(s) itself\n\t\t * @type {Map}\n\t\t * @ignore\n\t\t */\n\t\tthis.sources = new Map();\n\n\t\t/**\n\t\t * paired normal-map source(s), keyed identically to `sources`.\n\t\t * Populated from the constructor's `options.normalMap`. Used by\n\t\t * the WebGL renderer's lit pipeline for per-pixel lighting.\n\t\t * @type {Map}\n\t\t * @ignore\n\t\t */\n\t\tthis.normalSources = new Map();\n\n\t\t/**\n\t\t * the atlas dictionnaries\n\t\t * @type {Map}\n\t\t * @ignore\n\t\t */\n\t\tthis.atlases = new Map();\n\n\t\t/**\n\t\t * the default \"active\" atlas (used for multiAtlas)\n\t\t * @type {Map}\n\t\t * @ignore\n\t\t */\n\t\tthis.activeAtlas = undefined;\n\n\t\t/**\n\t\t * UV lookup cache to avoid per-frame string allocation in drawImage\n\t\t * @ignore\n\t\t */\n\t\tthis._uvCache = { sx: -1, sy: -1, sw: -1, sh: -1, uvs: null };\n\n\t\t// parse given atlas(es) paremeters\n\t\tif (typeof atlases !== \"undefined\") {\n\t\t\t// normalize to array to keep the following code generic\n\t\t\tatlases = Array.isArray(atlases) ? atlases : [atlases];\n\t\t\tfor (const i in atlases) {\n\t\t\t\tconst atlas = atlases[i];\n\n\t\t\t\tif (typeof atlas.meta !== \"undefined\") {\n\t\t\t\t\tthis.format = identifyFormat(atlas.meta.app);\n\t\t\t\t\tthis.repeat = atlas.meta.repeat || \"no-repeat\";\n\t\t\t\t\tswitch (this.format) {\n\t\t\t\t\t\tcase \"texturepacker\":\n\t\t\t\t\t\tcase \"aseprite\":\n\t\t\t\t\t\t\t// set the texture\n\t\t\t\t\t\t\tif (typeof src === \"undefined\") {\n\t\t\t\t\t\t\t\t// get the texture name from the atlas meta data\n\t\t\t\t\t\t\t\tconst image = getImage(atlas.meta.image);\n\t\t\t\t\t\t\t\tif (!image) {\n\t\t\t\t\t\t\t\t\tthrow new Error(\"Atlas texture '\" + image + \"' not found\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthis.sources.set(atlas.meta.image, image);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.sources.set(\n\t\t\t\t\t\t\t\t\tatlas.meta.image || \"default\",\n\t\t\t\t\t\t\t\t\ttypeof src === \"string\" ? getImage(src) : src,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// initialize the atlas\n\t\t\t\t\t\t\tif (this.format === \"texturepacker\") {\n\t\t\t\t\t\t\t\tthis.atlases.set(\n\t\t\t\t\t\t\t\t\tatlas.meta.image || \"default\",\n\t\t\t\t\t\t\t\t\tparseTexturePacker(atlas, this),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.atlases.set(\n\t\t\t\t\t\t\t\t\tatlas.meta.image || \"default\",\n\t\t\t\t\t\t\t\t\tparseAseprite(atlas, this),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"shoebox\":\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!atlas.meta.exporter ||\n\t\t\t\t\t\t\t\t!atlas.meta.exporter.includes(\"melonJS\")\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t\"ShoeBox requires the JSON exporter : \" +\n\t\t\t\t\t\t\t\t\t\t\"https://github.com/melonjs/melonJS/tree/master/media/shoebox_JSON_export.sbx\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthis.sources.set(\n\t\t\t\t\t\t\t\t\"default\",\n\t\t\t\t\t\t\t\ttypeof src === \"string\" ? getImage(src) : src,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t// initialize the atlas\n\t\t\t\t\t\t\tthis.atlases.set(\n\t\t\t\t\t\t\t\tatlas.meta.image || \"default\",\n\t\t\t\t\t\t\t\tparseTexturePacker(atlas, this),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"melonJS\":\n\t\t\t\t\t\t\tthis.sources.set(\n\t\t\t\t\t\t\t\t\"default\",\n\t\t\t\t\t\t\t\ttypeof src === \"string\" ? getImage(src) : src,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t// initialize the atlas\n\t\t\t\t\t\t\tthis.atlases.set(\n\t\t\t\t\t\t\t\tatlas.meta.image || \"default\",\n\t\t\t\t\t\t\t\tparseTexturePacker(atlas, this),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\"Unknown texture atlas format: \" + atlas.meta.app,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// a regular spritesheet\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof atlas.framewidth !== \"undefined\" &&\n\t\t\t\t\t\ttypeof atlas.frameheight !== \"undefined\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.format = \"Spritesheet (fixed cell size)\";\n\t\t\t\t\t\tthis.repeat = \"no-repeat\";\n\n\t\t\t\t\t\tif (typeof src !== \"undefined\") {\n\t\t\t\t\t\t\t// overwrite if specified\n\t\t\t\t\t\t\tatlas.image = typeof src === \"string\" ? getImage(src) : src;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// initialize the atlas\n\t\t\t\t\t\tthis.atlases.set(\"default\", parseSpriteSheet(atlas, this));\n\t\t\t\t\t\tthis.sources.set(\"default\", atlas.image);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} // end forEach\n\t\t\tthis.activeAtlas = this.atlases.keys().next().value;\n\t\t}\n\n\t\t// if format not recognized (`atlases` is a Map — `.size`, not `.length`)\n\t\tif (this.atlases.size === 0) {\n\t\t\tthrow new Error(\"texture atlas format not supported\");\n\t\t}\n\n\t\t// Add self to TextureCache if cache !== false\n\t\tif (cache !== false) {\n\t\t\tthis.sources.forEach((source) => {\n\t\t\t\t_renderer.cache.set(source, this);\n\t\t\t});\n\t\t}\n\n\t\t// Populate the parallel normal-map sources map. For Phase 1 we\n\t\t// support a single paired normal-map (the typical SpriteIlluminator\n\t\t// workflow); each color source gets the same normal-map image.\n\t\t// Multipack atlases with per-source normal maps can be handled later\n\t\t// by accepting an array/Map matching `sources`.\n\t\tif (typeof normalMap !== \"undefined\" && normalMap !== null) {\n\t\t\tlet resolved;\n\t\t\tif (typeof normalMap === \"string\") {\n\t\t\t\tresolved = getImage(normalMap);\n\t\t\t\tif (!resolved) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"TextureAtlas: normal map image '\" + normalMap + \"' not found\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\ttypeof normalMap === \"object\" &&\n\t\t\t\ttypeof normalMap.width === \"number\" &&\n\t\t\t\ttypeof normalMap.height === \"number\"\n\t\t\t) {\n\t\t\t\t// image-like (HTMLImageElement, HTMLCanvasElement,\n\t\t\t\t// OffscreenCanvas, ImageBitmap). Explicitly reject\n\t\t\t\t// HTMLVideoElement — it duck-types past the size check\n\t\t\t\t// but the lit pipeline caches the GL texture per image\n\t\t\t\t// reference and never re-uploads, so a video would freeze\n\t\t\t\t// on frame 0 (see `Sprite.normalMap`).\n\t\t\t\tif (typeof normalMap.videoWidth === \"number\") {\n\t\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\t\"TextureAtlas: options.normalMap does not support HTMLVideoElement (the lit pipeline caches the texture per image reference and would freeze on frame 0)\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tresolved = normalMap;\n\t\t\t} else {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\"TextureAtlas: options.normalMap must be an image-like, a loader key string, or null/undefined; got \" +\n\t\t\t\t\t\ttypeof normalMap,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.sources.forEach((_source, key) => {\n\t\t\t\tthis.normalSources.set(key, resolved);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * return the default or specified atlas dictionnary\n\t * @param {string} [name] - atlas name in case of multipack textures\n\t * @returns {object}\n\t */\n\tgetAtlas(name) {\n\t\tif (typeof name === \"string\") {\n\t\t\treturn this.atlases.get(name);\n\t\t} else {\n\t\t\treturn this.atlases.get(this.activeAtlas);\n\t\t}\n\t}\n\n\t/**\n\t * return the format of the atlas dictionnary\n\t * @returns {string} will return \"texturepacker\", or \"ShoeBox\", or \"melonJS\", or \"Spritesheet (fixed cell size)\"\n\t */\n\tgetFormat() {\n\t\treturn this.format;\n\t}\n\n\t/**\n\t * return the source texture for the given region (or default one if none specified)\n\t * @param {object} [region] - region name in case of multipack textures\n\t * @returns {HTMLImageElement|HTMLCanvasElement}\n\t */\n\tgetTexture(region) {\n\t\tif (typeof region === \"object\" && typeof region.texture === \"string\") {\n\t\t\treturn this.sources.get(region.texture);\n\t\t} else {\n\t\t\treturn this.sources.get(this.activeAtlas);\n\t\t}\n\t}\n\n\t/**\n\t * Return the paired normal-map texture for the given region, or `null`\n\t * if no normal map was provided to this atlas. The normal map shares\n\t * the same UV layout as the color texture returned by {@link TextureAtlas#getTexture}.\n\t * @param {object} [region] - region name in case of multipack textures\n\t * @returns {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|null}\n\t */\n\tgetNormalTexture(region) {\n\t\tif (this.normalSources.size === 0) {\n\t\t\treturn null;\n\t\t}\n\t\tif (typeof region === \"object\" && typeof region.texture === \"string\") {\n\t\t\treturn this.normalSources.get(region.texture) ?? null;\n\t\t}\n\t\treturn this.normalSources.get(this.activeAtlas) ?? null;\n\t}\n\n\t/**\n\t * add a region to the atlas\n\t * @param {string} name - region mame\n\t * @param {number} x - x origin of the region\n\t * @param {number} y - y origin of the region\n\t * @param {number} w - width of the region\n\t * @param {number} h - height of the region\n\t * @returns {object} the created region\n\t */\n\taddRegion(name, x, y, w, h) {\n\t\tconst source = this.getTexture();\n\t\tconst atlas = this.getAtlas();\n\t\t// an unsized HTMLVideoElement reports width/height = 0 — its real\n\t\t// pixel dimensions are videoWidth/videoHeight (same fallback as\n\t\t// `uploadTexture` and `TextureCache.get`)\n\t\tconst dw = source.width || source.videoWidth;\n\t\tconst dh = source.height || source.videoHeight;\n\n\t\tatlas[name] = {\n\t\t\tname: name,\n\t\t\toffset: new Vector2d(x, y),\n\t\t\twidth: w,\n\t\t\theight: h,\n\t\t\tangle: 0,\n\t\t};\n\n\t\tthis.addUVs(atlas, name, dw, dh);\n\n\t\treturn atlas[name];\n\t}\n\n\t/**\n\t * return a normalized region (or frame) information for the specified sprite name\n\t * @param {string} name - name of the sprite\n\t * @param {string} [atlas] - name of a specific atlas where to search for the region\n\t * @returns {object}\n\t */\n\tgetRegion(name, atlas) {\n\t\tlet region;\n\t\tif (typeof atlas === \"string\") {\n\t\t\tregion = this.getAtlas(atlas)[name];\n\t\t} else {\n\t\t\t// look for the given region in each existing atlas\n\t\t\tfor (const atlas of this.atlases.values()) {\n\t\t\t\tif (typeof atlas[name] !== \"undefined\") {\n\t\t\t\t\t// there should be only one\n\t\t\t\t\tregion = atlas[name];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn region;\n\t}\n\n\t/**\n\t * return the uvs mapping for the given region\n\t * @param {string|number} name - region (or frame) name, or sx when using numeric parameters\n\t * @param {number} [sy] - source y coordinate (when using numeric parameters)\n\t * @param {number} [sw] - source width (when using numeric parameters)\n\t * @param {number} [sh] - source height (when using numeric parameters)\n\t * @returns {Float32Array} region Uvs\n\t */\n\tgetUVs(name, sy, sw, sh) {\n\t\tif (typeof name === \"number\") {\n\t\t\tconst sx = name;\n\t\t\tconst cache = this._uvCache;\n\t\t\t// fast path: return cached UVs if coordinates match last lookup\n\t\t\tif (\n\t\t\t\tsx === cache.sx &&\n\t\t\t\tsy === cache.sy &&\n\t\t\t\tsw === cache.sw &&\n\t\t\t\tsh === cache.sh\n\t\t\t) {\n\t\t\t\treturn cache.uvs;\n\t\t\t}\n\t\t\t// cache miss: fall back to string-keyed region lookup\n\t\t\tconst key = sx + \",\" + sy + \",\" + sw + \",\" + sh;\n\t\t\tlet region = this.getRegion(key);\n\t\t\tif (typeof region === \"undefined\") {\n\t\t\t\tregion = this.addRegion(key, sx, sy, sw, sh);\n\t\t\t}\n\t\t\tcache.sx = sx;\n\t\t\tcache.sy = sy;\n\t\t\tcache.sw = sw;\n\t\t\tcache.sh = sh;\n\t\t\tcache.uvs = region.uvs;\n\t\t\treturn region.uvs;\n\t\t}\n\n\t\t// string-based lookup (named regions)\n\t\tlet region = this.getRegion(name);\n\n\t\tif (typeof region === \"undefined\") {\n\t\t\t// see https://github.com/melonjs/melonJS/issues/1281\n\t\t\tconst keys = name.split(\",\");\n\t\t\tregion = this.addRegion(name, +keys[0], +keys[1], +keys[2], +keys[3]);\n\t\t}\n\t\treturn region.uvs;\n\t}\n\n\t/**\n\t * add uvs mapping for the given region\n\t * @param {object} atlas - the atlas dictionnary where the region is define\n\t * @param {object} name - region (or frame) name\n\t * @param {number} w - the width of the region\n\t * @param {number} h - the height of the region\n\t * @returns {Float32Array} the created region UVs\n\t */\n\taddUVs(atlas, name, w, h) {\n\t\t// Source coordinates\n\t\tconst s = atlas[name].offset;\n\t\tconst sw = atlas[name].width;\n\t\tconst sh = atlas[name].height;\n\n\t\tatlas[name].uvs = new Float32Array([\n\t\t\ts.x / w, // u0 (left)\n\t\t\ts.y / h, // v0 (top)\n\t\t\t(s.x + sw) / w, // u1 (right)\n\t\t\t(s.y + sh) / h, // v1 (bottom)\n\t\t]);\n\t\t// Cache source coordinates\n\t\t// see https://github.com/melonjs/melonJS/issues/1281\n\t\t// keyed by the REGION rect (x, y, width, height) — the same key shape\n\t\t// `getUVs(sx, sy, sw, sh)` builds for coordinate lookups. (Keying by\n\t\t// the texture dimensions instead made the alias unmatchable for any\n\t\t// sub-region AND poisoned full-image lookups whenever a frame sat at\n\t\t// (0,0): \"0,0,texW,texH\" pointed at that frame, not the whole image.)\n\t\tconst key = `${s.x},${s.y},${sw},${sh}`;\n\t\tif (key !== name) {\n\t\t\tatlas[key] = atlas[name];\n\t\t}\n\n\t\treturn atlas[name].uvs;\n\t}\n\n\t/**\n\t * Create a sprite object using the first region found using the specified name\n\t * @param {string} name - name of the sprite\n\t * @param {object} [settings] - Additional settings passed to the {@link Sprite} contructor\n\t * @param {boolean} [nineSlice=false] - if true returns a 9-slice sprite\n\t * @returns {Sprite|NineSliceSprite}\n\t * @example\n\t * // create a new texture object under the `game` namespace\n\t * game.texture = new me.TextureAtlas(\n\t *    me.loader.getJSON(\"texture\"),\n\t *    me.loader.getImage(\"texture\")\n\t * );\n\t * ...\n\t * ...\n\t * // create a new \"coin\" sprite\n\t * let sprite = game.texture.createSpriteFromName(\"coin.png\");\n\t * // set the renderable position to bottom center\n\t * sprite.anchorPoint.set(0.5, 1.0);\n\t * ...\n\t * ...\n\t * // create a 9-slice sprite\n\t * let dialogPanel = game.texture.createSpriteFromName(\n\t *    \"rpg_dialo.png\",\n\t *    // width & height are mandatory for 9-slice sprites\n\t *    { width: this.width, height: this.height },\n\t *    true\n\t * );\n\t */\n\tcreateSpriteFromName(name, settings, nineSlice = false) {\n\t\t// instantiate a new sprite object\n\t\treturn pool.pull(\n\t\t\tnineSlice === true ? \"me.NineSliceSprite\" : \"me.Sprite\",\n\t\t\t0,\n\t\t\t0,\n\t\t\tObject.assign(\n\t\t\t\t{\n\t\t\t\t\timage: this,\n\t\t\t\t\tregion: name,\n\t\t\t\t},\n\t\t\t\tsettings || {},\n\t\t\t),\n\t\t);\n\t}\n\n\t/**\n\t * Create an animation object using the first region found using all specified names\n\t * @param {string[]|number[]} [names] - list of names for each sprite (if not specified all defined names/entries in the atlas will be added)\n\t * (when manually creating a Texture out of a spritesheet, only numeric values are authorized)\n\t * @param {object} [settings] - Additional settings passed to the {@link Sprite} contructor\n\t * @returns {Sprite}\n\t * @example\n\t * // create a new texture object under the `game` namespace\n\t * game.texture = new me.TextureAtlas(\n\t *     me.loader.getJSON(\"texture\"),\n\t *     me.loader.getImage(\"texture\")\n\t * );\n\t *\n\t * // create a new Animated Sprite\n\t * let sprite = game.texture.createAnimationFromName([\n\t *     \"walk0001.png\", \"walk0002.png\", \"walk0003.png\",\n\t *     \"walk0004.png\", \"walk0005.png\", \"walk0006.png\",\n\t *     \"walk0007.png\", \"walk0008.png\", \"walk0009.png\",\n\t *     \"walk0010.png\", \"walk0011.png\"\n\t * ]);\n\t *\n\t * // define an additional basic walking animation\n\t * sprite.addAnimation (\"simple_walk\", [0,2,1]);\n\t * // you can also use frame name to define your animation\n\t * sprite.addAnimation (\"speed_walk\", [\"walk0007.png\", \"walk0008.png\", \"walk0009.png\", \"walk0010.png\"]);\n\t * // set the default animation\n\t * sprite.setCurrentAnimation(\"simple_walk\");\n\t * // set the renderable position to bottom center\n\t * sprite.anchorPoint.set(0.5, 1.0);\n\t */\n\t/**\n\t * Return the Sprite settings object (framewidth, frameheight, atlas, atlasIndices, etc.)\n\t * for the given set of atlas frame names. This is useful when extending {@link Sprite}\n\t * and you need to pass atlas animation data to the parent constructor.\n\t * @param {string[]|number[]} [names] - list of names for each sprite\n\t * (when manually creating a Texture out of a spritesheet, only numeric values are authorized).\n\t * If not specified, all defined names/entries in the atlas will be added.\n\t * @returns {object} A settings object suitable for passing to the {@link Sprite} constructor\n\t * @example\n\t * // extend Sprite directly with texture atlas animation frames\n\t * class MyPlayer extends me.Sprite {\n\t *     constructor(x, y, settings) {\n\t *         super(x, y, {\n\t *             ...game.texture.getAnimationSettings([\n\t *                 \"walk0001.png\", \"walk0002.png\", \"walk0003.png\"\n\t *             ]),\n\t *             anchorPoint: { x: 0.5, y: 1.0 }\n\t *         });\n\t *         // add a physic body\n\t *         this.body = new me.Body(this, new me.Rect(0, 0, this.width, this.height));\n\t *     }\n\t * }\n\t */\n\tgetAnimationSettings(names) {\n\t\tconst tpAtlas = [];\n\t\tconst indices = {};\n\t\tlet width = 0;\n\t\tlet height = 0;\n\t\tconst textureAtlas = this.getAtlas();\n\n\t\tif (typeof names === \"undefined\") {\n\t\t\tnames = textureAtlas;\n\t\t}\n\n\t\t// first pass: collect regions and compute max dimensions\n\t\tconst regions = [];\n\t\tfor (const i in names) {\n\t\t\tconst isArray = Array.isArray(names);\n\t\t\tconst name = isArray ? names[i] : i;\n\t\t\t// in dictionary mode, only iterate real regions: a region's `name`\n\t\t\t// matches its dictionary key. This skips both the coordinate-alias\n\t\t\t// keys added by `addUVs` (#1281 — they'd duplicate every frame) and\n\t\t\t// non-region entries like the aseprite parser's `anims` dictionary\n\t\t\t// (whose value has no `name`, and which `getRegion` would otherwise\n\t\t\t// happily return as a NaN-sized pseudo-region).\n\t\t\tif (!isArray && (names[i] == null || names[i].name !== i)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst region = this.getRegion(name);\n\t\t\tif (region == null) {\n\t\t\t\tif (isArray) {\n\t\t\t\t\tthrow new Error(\"Texture - region for \" + name + \" not found\");\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tregions.push({ name, region });\n\t\t\tconst frameW = region.sourceSize ? region.sourceSize.w : region.width;\n\t\t\tconst frameH = region.sourceSize ? region.sourceSize.h : region.height;\n\t\t\twidth = Math.max(frameW, width);\n\t\t\theight = Math.max(frameH, height);\n\t\t}\n\n\t\t// second pass: clone regions, strip anchorPoint,\n\t\t// and add trim offset to bottom-align smaller frames\n\t\tfor (const { name, region } of regions) {\n\t\t\tconst regionCopy = Object.assign({}, region);\n\t\t\tdelete regionCopy.anchorPoint;\n\n\t\t\tconst frameH = region.sourceSize ? region.sourceSize.h : region.height;\n\t\t\tif (frameH < height) {\n\t\t\t\t// bottom-align by offsetting the y draw position\n\t\t\t\tregionCopy.trim = Object.assign({}, region.trim || { x: 0, y: 0 });\n\t\t\t\tregionCopy.trim.y += height - frameH;\n\t\t\t}\n\n\t\t\ttpAtlas.push(regionCopy);\n\t\t\tindices[name] = tpAtlas.length - 1;\n\t\t}\n\n\t\treturn {\n\t\t\timage: this,\n\t\t\tframewidth: width,\n\t\t\tframeheight: height,\n\t\t\tmargin: 0,\n\t\t\tspacing: 0,\n\t\t\tatlas: tpAtlas,\n\t\t\tanims: textureAtlas.anims,\n\t\t\tatlasIndices: indices,\n\t\t};\n\t}\n\n\tcreateAnimationFromName(names, settings) {\n\t\tconst tpAtlas = [];\n\t\tconst indices = {};\n\t\tlet width = 0;\n\t\tlet height = 0;\n\t\tconst textureAtlas = this.getAtlas();\n\n\t\tif (typeof names === \"undefined\") {\n\t\t\tnames = textureAtlas;\n\t\t}\n\n\t\tfor (const i in names) {\n\t\t\tconst isArray = Array.isArray(names);\n\t\t\tconst name = isArray ? names[i] : i;\n\t\t\t// dictionary mode: skip coordinate-alias keys and non-region\n\t\t\t// entries (see getAnimationSettings for the full rationale)\n\t\t\tif (!isArray && (names[i] == null || names[i].name !== i)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst region = this.getRegion(name);\n\t\t\tif (region == null) {\n\t\t\t\tthrow new Error(\"Texture - region for \" + name + \" not found\");\n\t\t\t}\n\t\t\ttpAtlas.push(region);\n\t\t\tindices[name] = tpAtlas.length - 1;\n\t\t\tconst frameW = region.sourceSize ? region.sourceSize.w : region.width;\n\t\t\tconst frameH = region.sourceSize ? region.sourceSize.h : region.height;\n\t\t\twidth = Math.max(frameW, width);\n\t\t\theight = Math.max(frameH, height);\n\t\t}\n\n\t\t// instantiate a new animation sheet object\n\t\treturn new Sprite(\n\t\t\t0,\n\t\t\t0,\n\t\t\tObject.assign(\n\t\t\t\t{\n\t\t\t\t\timage: this,\n\t\t\t\t\tframewidth: width,\n\t\t\t\t\tframeheight: height,\n\t\t\t\t\tmargin: 0,\n\t\t\t\t\tspacing: 0,\n\t\t\t\t\tatlas: tpAtlas,\n\t\t\t\t\tanims: textureAtlas.anims,\n\t\t\t\t\tatlasIndices: indices,\n\t\t\t\t},\n\t\t\t\tsettings || {},\n\t\t\t),\n\t\t);\n\t}\n}\n","import { emit, GPU_TEXTURE_CACHE_RESET } from \"../../system/event.ts\";\nimport { ArrayMultimap } from \"../../utils/array-multimap.js\";\nimport { getBasename } from \"../../utils/file.ts\";\nimport { TextureSlotTable } from \"../gpu/textureslots.js\";\nimport { createAtlas, TextureAtlas } from \"./atlas.js\";\n\n// Canonical repeat values accepted by `CanvasRenderingContext2D.createPattern`\n// (per WHATWG Canvas2D spec). Anything outside this set is silently clamped\n// to `\"no-repeat\"` at the GL wrap-mode mapping; the cache normalizes here so\n// a typo'd repeat string (e.g. `\"repat-x\"`) doesn't allocate its own unit\n// indefinitely and leak texture-unit slots.\nconst VALID_REPEATS = new Set([\"repeat\", \"repeat-x\", \"repeat-y\", \"no-repeat\"]);\nfunction normalizeRepeat(repeat) {\n\treturn VALID_REPEATS.has(repeat) ? repeat : \"no-repeat\";\n}\n\n/**\n * a basic texture cache object\n * @ignore\n */\nclass TextureCache {\n\t/**\n\t * @ignore\n\t */\n\tconstructor(renderer, max_size = Infinity) {\n\t\t// reference to the renderer that owns this cache\n\t\tthis.renderer = renderer;\n\t\t// cache uses an array to allow for duplicated key\n\t\tthis.cache = new ArrayMultimap();\n\t\tthis.tinted = new Map();\n\t\t// `units` keys each (source, repeat-mode) pair to a distinct GL\n\t\t// texture unit. Keying by source alone (pre-19.7.0) collided\n\t\t// when the same image was used as both a sprite/atlas AND a\n\t\t// pattern, or as multiple patterns with different repeat modes —\n\t\t// see https://github.com/melonjs/melonJS/issues/1448. The nested\n\t\t// `Map<source, Map<repeat, unit>>` discriminates by wrap mode\n\t\t// while keeping the outer key the source object so it lines up\n\t\t// with the rest of the cache's source-keyed API (`set` / `has` /\n\t\t// `get` / `delete` all take an image). Explicit eviction happens\n\t\t// via `delete(image)` (free all repeats) and `clear()` (wipe the\n\t\t// whole map); `Map` keys are strong references, so the entries\n\t\t// don't drop when the source goes out of scope user-side.\n\t\tthis.units = new Map();\n\t\t// Slot assignment is the shared, backend-neutral policy (#1585): which\n\t\t// slot is free, what reservations are skipped, and what happens on\n\t\t// exhaustion. The WebGPU quad batcher runs the same table, so the two\n\t\t// backends cannot drift on when a texture set overflows. What stays\n\t\t// here is residency — the source-keyed index above, tinted variants,\n\t\t// the atlas cache — and the reservations below.\n\t\tthis.slotTable = new TextureSlotTable({\n\t\t\tcapacity: max_size,\n\t\t\tisReserved: (unit) => {\n\t\t\t\treturn this.reservedUnits.has(unit);\n\t\t\t},\n\t\t\tonOverflow: () => {\n\t\t\t\t// draw what is pending with THEIR units before any reassignment\n\t\t\t\t// see https://github.com/melonjs/melonJS/issues/1280\n\t\t\t\tif (this.renderer.currentBatcher) {\n\t\t\t\t\tthis.renderer.currentBatcher.flush();\n\t\t\t\t}\n\t\t\t\t// the source-keyed index is about to be invalidated wholesale\n\t\t\t\tthis.units.clear();\n\t\t\t\temit(GPU_TEXTURE_CACHE_RESET);\n\t\t\t},\n\t\t});\n\t\t// units held out of `allocateTextureUnit` for shader extra-samplers\n\t\t// (`ShaderEffect.setTexture`). Reference-counted (unit → count) so a\n\t\t// unit shared by several effects stays reserved until the last one\n\t\t// releases it. Not touched by `clear()` — reservations are owned by the\n\t\t// effects, and released on their destroy / context-loss.\n\t\tthis.reservedUnits = new Map();\n\t\tthis.clear();\n\t}\n\n\t/**\n\t * Which texture units are currently taken. This is the slot table's own\n\t * occupancy set, exposed under the name it has always had — seeding or\n\t * clearing it drives the allocator directly.\n\t * @returns {Set<number>} the occupied units\n\t * @ignore\n\t */\n\tget usedUnits() {\n\t\treturn this.slotTable.used;\n\t}\n\n\t/**\n\t * How many texture units this cache may hand out. Backed by the slot\n\t * table's capacity, so assigning it (tests narrow it to force exhaustion,\n\t * and a context restore can resolve a different device limit) re-sizes the\n\t * allocator rather than leaving the two disagreeing.\n\t * @returns {number} the unit count\n\t * @ignore\n\t */\n\tget max_size() {\n\t\treturn this.slotTable.capacity;\n\t}\n\n\tset max_size(size) {\n\t\tthis.slotTable.setCapacity(size);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tclear() {\n\t\tthis.cache.clear();\n\t\tthis.tinted.clear();\n\t\tthis.units.clear();\n\t\tthis.slotTable.reset();\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tallocateTextureUnit() {\n\t\t// the policy — free-slot search skipping reservations, and flush +\n\t\t// evict-everything on exhaustion — lives in the shared table\n\t\treturn this.slotTable.claim();\n\t}\n\n\t/**\n\t * Reserve a texture unit so {@link allocateTextureUnit} never hands it to a\n\t * color texture — used by `ShaderEffect.setTexture` to hold high units for\n\t * its extra samplers. Reference-counted, so a unit shared by several effects\n\t * stays reserved until the last one releases it.\n\t * @param {number} unit - the texture unit to reserve\n\t * @ignore\n\t */\n\treserveUnit(unit) {\n\t\tthis.reservedUnits.set(unit, (this.reservedUnits.get(unit) || 0) + 1);\n\t}\n\n\t/**\n\t * Release a unit previously held by {@link reserveUnit}. The unit becomes\n\t * allocatable again once its last holder releases it.\n\t * @param {number} unit - the texture unit to release\n\t * @ignore\n\t */\n\treleaseUnit(unit) {\n\t\tconst count = this.reservedUnits.get(unit);\n\t\tif (count > 1) {\n\t\t\tthis.reservedUnits.set(unit, count - 1);\n\t\t} else {\n\t\t\tthis.reservedUnits.delete(unit);\n\t\t}\n\t}\n\n\t/**\n\t * Reset all texture unit assignments without clearing the texture cache.\n\t * Used by multi-texture batching when the shader's sampler range is exceeded.\n\t * @ignore\n\t */\n\tresetUnitAssignments() {\n\t\tthis.units.clear();\n\t\tthis.slotTable.reset();\n\t\temit(GPU_TEXTURE_CACHE_RESET);\n\t}\n\n\t/**\n\t * @ignore\n\t *\n\t * Frees the single `(source, repeat)` unit matching the texture's\n\t * current `repeat` field — deliberately granular, so freeing one live\n\t * consumer (e.g. one of two patterns with different repeats over the\n\t * same image) never evicts the other's unit. When a SOURCE goes away\n\t * entirely, use {@link freeAllUnits} instead — a source can hold units\n\t * under several repeat modes (per-mesh `textureRepeat` overrides,\n\t * #1503) that this granular free would leave pinned.\n\t */\n\tfreeTextureUnit(texture) {\n\t\tconst source = texture.sources.get(texture.activeAtlas);\n\t\tconst repeat = normalizeRepeat(texture.repeat);\n\t\tconst perRepeat = this.units.get(source);\n\t\tconst unit = perRepeat?.get(repeat);\n\t\t// was a texture unit allocated ?\n\t\tif (typeof unit !== \"undefined\") {\n\t\t\tthis.usedUnits.delete(unit);\n\t\t\tperRepeat.delete(repeat);\n\t\t\tif (perRepeat.size === 0) {\n\t\t\t\tthis.units.delete(source);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t *\n\t * Free every texture unit allocated for the texture's source, across\n\t * ALL repeat modes. A single source can hold several `(source, repeat)`\n\t * units — patterns with different repeats over one image, or meshes\n\t * sampling one image with per-mesh `textureRepeat` overrides (#1503) —\n\t * and the source going away invalidates all of them at once.\n\t */\n\tfreeAllUnits(texture) {\n\t\tconst source = texture.sources.get(texture.activeAtlas);\n\t\tconst perRepeat = this.units.get(source);\n\t\tif (typeof perRepeat !== \"undefined\") {\n\t\t\tfor (const unit of perRepeat.values()) {\n\t\t\t\tthis.usedUnits.delete(unit);\n\t\t\t}\n\t\t\tthis.units.delete(source);\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t * @param {string} [repeat] - overrides the texture's own `repeat` for the\n\t * unit lookup — sampler state per use (a mesh's `textureRepeat`), so one\n\t * source can be sampled with several wrap modes without mutating the\n\t * shared atlas (#1503). Omit to use `texture.repeat`.\n\t *\n\t * Hot-path note: `getUnit` / `peekUnit` are called per-texture per-draw,\n\t * so the `(source, repeat)` lookup is inlined here rather than going\n\t * through a helper that allocates a `{source, repeat}` object per call.\n\t */\n\tgetUnit(texture, repeat) {\n\t\tconst source = texture.sources.get(texture.activeAtlas);\n\t\tconst wrap = normalizeRepeat(\n\t\t\ttypeof repeat === \"string\" ? repeat : texture.repeat,\n\t\t);\n\t\tlet perRepeat = this.units.get(source);\n\t\tif (perRepeat === undefined) {\n\t\t\tperRepeat = new Map();\n\t\t\tthis.units.set(source, perRepeat);\n\t\t}\n\t\tif (!perRepeat.has(wrap)) {\n\t\t\tperRepeat.set(wrap, this.allocateTextureUnit());\n\t\t}\n\t\treturn perRepeat.get(wrap);\n\t}\n\n\t/**\n\t * @ignore\n\t * return every texture unit allocated for the given texture's source,\n\t * across ALL repeat modes — the unload-time counterpart of the per-use\n\t * `getUnit(texture, repeat)` override (#1503): a single source can hold\n\t * one unit per wrap mode it was sampled with, and per-repeat `peekUnit`\n\t * lookups would only ever find the one matching the texture's current\n\t * `repeat` field. Not a hot path (allocates the result array).\n\t */\n\tpeekAllUnits(texture) {\n\t\tconst source = texture.sources.get(texture.activeAtlas);\n\t\tconst perRepeat = this.units.get(source);\n\t\treturn typeof perRepeat !== \"undefined\" ? [...perRepeat.values()] : [];\n\t}\n\n\t/**\n\t * @ignore\n\t * return the texture unit for the given texture, or -1 if not allocated\n\t * @param {string} [repeat] - same per-use wrap override as {@link getUnit}\n\t */\n\tpeekUnit(texture, repeat) {\n\t\tconst source = texture.sources.get(texture.activeAtlas);\n\t\tconst wrap = normalizeRepeat(\n\t\t\ttypeof repeat === \"string\" ? repeat : texture.repeat,\n\t\t);\n\t\tconst perRepeat = this.units.get(source);\n\t\treturn perRepeat?.has(wrap) ? perRepeat.get(wrap) : -1;\n\t}\n\n\t/**\n\t * @ignore\n\t * cache the textureAltas for the given image\n\t */\n\tset(image, textureAtlas) {\n\t\treturn this.cache.put(image, textureAtlas);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\thas(image) {\n\t\treturn this.cache.has(image);\n\t}\n\n\t/**\n\t * @ignore\n\t * return the textureAltas for the given image\n\t */\n\tget(image, atlas) {\n\t\t// Always the FIRST atlas registered for this image. A framewidth/\n\t\t// frameheight-based refinement loop lived here for years but was\n\t\t// provably dead (#1489): it compared top-level `.width`/`.height`\n\t\t// on the parsed atlas dict — properties the parsers never set\n\t\t// (frames are keyed by name) — so it never selected anything else.\n\t\t// Whether multi-atlas selection should exist at all is a design\n\t\t// question deferred to the #1410 cache refactor; the single-result\n\t\t// contract is codified in texture.spec.js.\n\t\tlet entry = this.cache.get(image)[0];\n\n\t\tif (typeof entry === \"undefined\") {\n\t\t\tif (!atlas) {\n\t\t\t\tatlas = createAtlas(\n\t\t\t\t\timage.width || image.videoWidth,\n\t\t\t\t\timage.height || image.videoHeight,\n\t\t\t\t\timage.src ? getBasename(image.src) : undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tentry = new TextureAtlas(atlas, image, false);\n\t\t\tthis.set(image, entry);\n\t\t}\n\n\t\t// \"activate\" the corresponding sources (in case of multi texture atlas)\n\t\tif (typeof entry.sources !== \"undefined\" && entry.sources.size > 1) {\n\t\t\t// manage cases where a specific atlas is specified\n\t\t\tfor (const [key, value] of entry.sources.entries()) {\n\t\t\t\t// Check if the imageData matches the provided image\n\t\t\t\tif (value === image) {\n\t\t\t\t\tentry.activeAtlas = key;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tdelete(image) {\n\t\tif (this.cache.has(image)) {\n\t\t\t// Free every atlas registered under this image, not just the\n\t\t\t// first one. Post-#1448 (units keyed by (source, repeat))\n\t\t\t// multiple atlases can coexist for one image — freeing only\n\t\t\t// `cache.get(image)[0]` would leak the remaining repeats'\n\t\t\t// texture units after `this.cache.delete(image)` wipes the\n\t\t\t// entire multimap bucket. Deleting an image means the SOURCE\n\t\t\t// is going away, so sweep ALL repeat modes per atlas\n\t\t\t// (`freeAllUnits`) — one atlas can hold several per-repeat\n\t\t\t// units via per-mesh `textureRepeat` overrides (#1503), which\n\t\t\t// the granular `freeTextureUnit` (keyed on the atlas's current\n\t\t\t// `repeat` field) would leave pinned in `units`/`usedUnits`.\n\t\t\tfor (const texture of this.cache.get(image)) {\n\t\t\t\tthis.freeAllUnits(texture);\n\t\t\t}\n\t\t\tthis.cache.delete(image);\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\ttint(src, color) {\n\t\t// make sure the src is in the cache\n\t\tlet image_cache = this.tinted.get(src);\n\n\t\tif (image_cache === undefined) {\n\t\t\timage_cache = new Map();\n\t\t\tthis.tinted.set(src, image_cache);\n\t\t}\n\n\t\tif (!image_cache.has(color)) {\n\t\t\timage_cache.set(color, this.renderer.tint(src, color, \"multiply\"));\n\t\t}\n\n\t\treturn image_cache.get(color);\n\t}\n}\nexport default TextureCache;\n","import Texture2d from \"./texture2d.ts\";\n\n/**\n * A GPU-resident texture holding a captured frame — the value returned by\n * {@link WebGLRenderer#toFrameTexture}. Its backing is a live `WebGLTexture`\n * that the renderer refreshes **in place** on each capture, so a shader that\n * has bound it once (via {@link ShaderEffect#setTexture}) keeps sampling the\n * latest frame with no re-bind.\n *\n * Not part of the public class surface: obtain instances from\n * `renderer.toFrameTexture()` and treat them as opaque {@link Texture2d}\n * values. The `isGPUResident` flag is the discriminant `setTexture` uses to\n * take its live-bind path (bind the existing handle) instead of uploading a\n * static copy.\n * @augments Texture2d\n * @ignore\n */\nexport class FrameTexture extends Texture2d {\n\t/**\n\t * @param {WebGLRenderer} renderer - the owning renderer (for the GL context)\n\t * @param {number} width - capture width in pixels\n\t * @param {number} height - capture height in pixels\n\t */\n\tconstructor(renderer, width, height) {\n\t\tsuper();\n\t\tthis._renderer = renderer;\n\t\t/** @type {number} */\n\t\tthis.width = width;\n\t\t/** @type {number} */\n\t\tthis.height = height;\n\t\t/**\n\t\t * the live backing texture, (re)filled by `renderer.toFrameTexture` and\n\t\t * bound (never re-uploaded) by consumers\n\t\t * @type {WebGLTexture|null}\n\t\t */\n\t\tthis.glTexture = null;\n\t\t/**\n\t\t * marks this as a live GPU-resident source — see {@link ShaderEffect#setTexture}\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isGPUResident = true;\n\t}\n\n\t/**\n\t * The opaque GPU-resident backing — itself.\n\t * @returns {FrameTexture}\n\t */\n\tgetTexture() {\n\t\treturn this;\n\t}\n\n\t/**\n\t * Delete the backing GL texture. Invoked on the renderer-owned shared slot\n\t * at context loss / renderer teardown, or by a caller that owns a `target`\n\t * capture and is done with it. Idempotent.\n\t */\n\tdestroy() {\n\t\tif (this.glTexture !== null) {\n\t\t\tthis._renderer.gl.deleteTexture(this.glTexture);\n\t\t\tthis.glTexture = null;\n\t\t}\n\t}\n}\n\n/**\n * The Canvas-renderer counterpart of {@link FrameTexture}: a captured frame\n * backed by an offscreen `<canvas>` copy of the drawing buffer, keeping the\n * `toFrameTexture` family renderer-complete. A drawable source (not\n * GPU-resident), so it flows through the normal image path; custom shaders\n * don't run under the Canvas renderer, so this exists for API parity and\n * CPU-side reuse (e.g. drawing the captured frame back onto the scene).\n * @augments Texture2d\n * @ignore\n */\nexport class CanvasFrameTexture extends Texture2d {\n\t/**\n\t * @param {HTMLCanvasElement|OffscreenCanvas} canvas - the backing copy\n\t */\n\tconstructor(canvas) {\n\t\tsuper();\n\t\t/** @type {HTMLCanvasElement|OffscreenCanvas} */\n\t\tthis.canvas = canvas;\n\t\t/** @type {number} */\n\t\tthis.width = canvas.width;\n\t\t/** @type {number} */\n\t\tthis.height = canvas.height;\n\t}\n\n\t/**\n\t * The backing canvas copy.\n\t * @returns {HTMLCanvasElement|OffscreenCanvas}\n\t */\n\tgetTexture() {\n\t\treturn this.canvas;\n\t}\n}\n","import { Color } from \"./../../math/color.ts\";\nimport {\n\temit,\n\tGAME_RESET,\n\tONCONTEXT_LOST,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n} from \"../../system/event.ts\";\nimport { Gradient } from \"./../gradient.js\";\nimport Renderer from \"./../renderer.js\";\nimport TextureCache from \"./../texture/cache.js\";\nimport { CanvasFrameTexture } from \"./../texture/frametexture.js\";\n\n/**\n * additional import for TypeScript\n * @import {Rect} from \"./../../geometries/rectangle.ts\";\n * @import {RoundRect} from \"./../../geometries/roundrect.ts\";\n * @import {Polygon} from \"../../geometries/polygon.ts\";\n * @import {Line} from \"./../../geometries/line.ts\";\n * @import {Ellipse} from \"./../../geometries/ellipse.ts\";\n * @import {Matrix2d} from \"../../math/matrix2d.ts\";\n * @import {Bounds} from \"../../physics/bounds.ts\";\n * @import {default as Texture2d} from \"../texture/texture2d.ts\";\n */\n\n/**\n * a canvas renderer object\n * @category Rendering\n */\nexport default class CanvasRenderer extends Renderer {\n\t/**\n\t * @param {ApplicationSettings} [options] - optional parameters for the renderer\n\t */\n\tconstructor(options) {\n\t\t// parent constructor. `context` is stamped explicitly (same as the\n\t\t// WebGL/WebGPU backends stamp theirs) so a retried `app.init()` —\n\t\t// e.g. falling back to Canvas after a WebGPU rejection — does not\n\t\t// inherit the failed attempt's context type from the shared settings\n\t\tsuper(Object.assign(options, { context: \"2d\" }));\n\n\t\tthis.setBlendMode(this.settings.blendMode);\n\n\t\t// apply the default color to the 2d context\n\t\tthis.setColor(this.currentColor);\n\n\t\t// create a texture cache\n\t\tthis.cache = new TextureCache(this);\n\n\t\tif (this.settings.textureSeamFix !== false && !this.settings.antiAlias) {\n\t\t\t// enable the tile texture seam fix with the canvas renderer\n\t\t\tthis.uvOffset = 1;\n\t\t}\n\n\t\t// set the renderer type\n\t\tthis.type = \"CANVAS\";\n\n\t\t// context lost & restore event for canvas\n\t\tthis.getCanvas().addEventListener(\n\t\t\t\"contextlost\",\n\t\t\t(e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.isContextValid = false;\n\t\t\t\temit(ONCONTEXT_LOST, this);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t\t// ctx.restoreContext()\n\t\tthis.getCanvas().addEventListener(\n\t\t\t\"contextrestored\",\n\t\t\t() => {\n\t\t\t\tthis.isContextValid = true;\n\t\t\t\temit(ONCONTEXT_RESTORED, this);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\n\t\t// Held as a bound field, not an inline arrow, so `destroy()` can\n\t\t// unregister it — an anonymous handler cannot be passed to `off()`,\n\t\t// and the closure would otherwise pin this renderer forever.\n\t\tthis.onGameReset = () => {\n\t\t\tthis.reset();\n\t\t};\n\n\t\t// reset the renderer on game reset\n\t\ton(GAME_RESET, this.onGameReset);\n\t}\n\n\t/**\n\t * Release the resources held by this renderer. The Canvas backend owns\n\t * no GPU objects, but it does hold an event subscription whose closure\n\t * keeps the renderer (and its canvas) reachable — so a torn-down\n\t * application would otherwise leak one listener per teardown and keep\n\t * reacting to `GAME_RESET` after it was destroyed.\n\t */\n\tdestroy() {\n\t\toff(GAME_RESET, this.onGameReset);\n\t}\n\n\t/**\n\t * Reset context state\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.clearColor(this.currentColor, this.settings.transparent !== true);\n\t\t// drop the per-light gradient cache; entries will lazily re-bake on\n\t\t// the next `drawLight()` call.\n\t\tthis._lightCache = undefined;\n\t}\n\n\t/**\n\t * Capture the current frame into a {@link Texture2d} — the Canvas-renderer\n\t * counterpart of {@link WebGLRenderer#toFrameTexture}, keeping the\n\t * `toDataURL` / `toBlob` / `toImageBitmap` family renderer-complete. Backed\n\t * by an offscreen `<canvas>` copy of the drawing buffer (Canvas rendering is\n\t * immediate, so no flush is needed). Custom shaders don't run under the\n\t * Canvas renderer, so the result is a plain drawable for CPU-side reuse\n\t * rather than a live shader input.\n\t * @param {object} [options]\n\t * @param {Texture2d|null} [options.target] - controls the destination: omit\n\t *   for the shared renderer slot (default); pass a capture previously\n\t *   returned by this method to refresh it in place; pass `null` to mint a\n\t *   fresh, caller-owned capture (mirrors {@link WebGLRenderer#toFrameTexture})\n\t * @param {Bounds|{x: number, y: number, width: number, height: number}} [options.region] - capture\n\t *   only this sub-region (canvas pixels, top-left origin). A {@link Bounds}\n\t *   (or any `{x, y, width, height}`); from a {@link Rect} pass `rect.getBounds()`.\n\t * @returns {Texture2d} a texture holding the captured frame\n\t */\n\ttoFrameTexture(options = {}) {\n\t\t// a non-null `target` must be a capture this method previously returned\n\t\t// (a CanvasFrameTexture), or the frame.canvas dereference below throws a\n\t\t// confusing error — mirror the WebGLRenderer guard\n\t\tif (\n\t\t\ttypeof options.target !== \"undefined\" &&\n\t\t\toptions.target !== null &&\n\t\t\t!(options.target instanceof CanvasFrameTexture)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"CanvasRenderer.toFrameTexture: `target` must be a capture returned by this method\",\n\t\t\t);\n\t\t}\n\n\t\tconst src = this.getCanvas();\n\t\tlet x = 0;\n\t\tlet y = 0;\n\t\tlet w = src.width;\n\t\tlet h = src.height;\n\t\tconst region = options.region;\n\t\tif (typeof region !== \"undefined\") {\n\t\t\tconst rx = region.x;\n\t\t\tconst ry = region.y;\n\t\t\t// clamp the origin into the canvas first, then size to what remains\n\t\t\t// (matches WebGLRenderer#toFrameTexture); missing width/height\n\t\t\t// defaults to \"the rest of the canvas from x/y\"\n\t\t\tx = Math.min(Math.max(0, Math.floor(rx || 0)), src.width - 1);\n\t\t\ty = Math.min(Math.max(0, Math.floor(ry || 0)), src.height - 1);\n\t\t\tconst rw = Number.isFinite(region.width)\n\t\t\t\t? Math.ceil(region.width)\n\t\t\t\t: src.width - x;\n\t\t\tconst rh = Number.isFinite(region.height)\n\t\t\t\t? Math.ceil(region.height)\n\t\t\t\t: src.height - y;\n\t\t\tw = Math.max(1, Math.min(src.width - x, rw));\n\t\t\th = Math.max(1, Math.min(src.height - y, rh));\n\t\t}\n\n\t\t// no target → shared slot; target: null → mint owned; target: <capture>\n\t\t// → refresh in place (mirrors WebGLRenderer#toFrameTexture)\n\t\tconst shared = typeof options.target === \"undefined\";\n\t\tlet frame = shared\n\t\t\t? this._frameTexture\n\t\t\t: options.target === null\n\t\t\t\t? undefined\n\t\t\t\t: options.target;\n\t\tif (typeof frame === \"undefined\") {\n\t\t\t// mint a fresh capture (empty shared slot, or target: null). Route\n\t\t\t// through the engine's canvas factory so it honors the same\n\t\t\t// OffscreenCanvas capability gating as everything else.\n\t\t\tframe = new CanvasFrameTexture(Renderer.createCanvas(w, h));\n\t\t\tif (shared) {\n\t\t\t\tthis._frameTexture = frame;\n\t\t\t}\n\t\t} else if (frame.width !== w || frame.height !== h) {\n\t\t\t// resize the SAME capture in place (keep the object identity, like\n\t\t\t// the WebGL variant) rather than returning a different instance\n\t\t\tframe.canvas.width = w;\n\t\t\tframe.canvas.height = h;\n\t\t\tframe.width = w;\n\t\t\tframe.height = h;\n\t\t}\n\n\t\tconst ctx = frame.canvas.getContext(\"2d\");\n\t\tif (ctx === null) {\n\t\t\tthrow new Error(\n\t\t\t\t\"CanvasRenderer.toFrameTexture: 2D context unavailable on the capture canvas\",\n\t\t\t);\n\t\t}\n\t\tctx.clearRect(0, 0, w, h);\n\t\tctx.drawImage(src, x, y, w, h, 0, 0, w, h);\n\t\treturn frame;\n\t}\n\n\t/**\n\t * Reset the canvas transform to identity\n\t */\n\tresetTransform() {\n\t\tthis.getContext().setTransform(1, 0, 0, 1, 0, 0);\n\t}\n\n\t/**\n\t * set/change the current projection matrix.\n\t * In Canvas mode, this applies the ortho projection as a canvas 2D transform\n\t * (translate + scale) to map world coordinates to screen coordinates.\n\t * @param {Matrix3d} matrix - the new projection matrix\n\t */\n\tsetProjection(matrix) {\n\t\tsuper.setProjection(matrix);\n\t\t// convert the ortho projection matrix to a canvas 2D transform\n\t\t// ortho uses OpenGL convention (Y-up), canvas is Y-down, so negate Y scale\n\t\tconst val = matrix.val;\n\t\tconst w = this.getCanvas().width;\n\t\tconst h = this.getCanvas().height;\n\t\tconst sx = val[0] * w * 0.5;\n\t\tconst sy = -val[5] * h * 0.5;\n\t\tconst tx = (val[12] + 1) * w * 0.5;\n\t\tconst ty = (1 - val[13]) * h * 0.5;\n\t\tthis.getContext().setTransform(sx, 0, 0, sy, tx, ty);\n\t}\n\n\t/**\n\t * set the current blend mode for this renderer. <br>\n\t * All renderers support: <br>\n\t * - \"normal\" : draws new content on top of the existing content <br>\n\t * <img src=\"../images/normal-blendmode.png\" width=\"180\"/> <br>\n\t * - \"add\", \"additive\", or \"lighter\" : color values are added together <br>\n\t * <img src=\"../images/add-blendmode.png\" width=\"180\"/> <br>\n\t * - \"multiply\" : pixels are multiplied, resulting in a darker picture <br>\n\t * <img src=\"../images/multiply-blendmode.png\" width=\"180\"/> <br>\n\t * - \"screen\" : pixels are inverted, multiplied, and inverted again (opposite of multiply) <br>\n\t * <img src=\"../images/screen-blendmode.png\" width=\"180\"/> <br>\n\t * Canvas (browser-dependent) and WebGL2: <br>\n\t * - \"darken\" : retains the darkest pixels of both layers <br>\n\t * <img src=\"../images/darken-blendmode.png\" width=\"180\"/> <br>\n\t * - \"lighten\" : retains the lightest pixels of both layers <br>\n\t * <img src=\"../images/lighten-blendmode.png\" width=\"180\"/> <br>\n\t * Canvas only, browser-dependent (falls back to \"normal\" if unsupported or in WebGL): <br>\n\t * - \"overlay\" <br>\n\t * <img src=\"../images/overlay-blendmode.png\" width=\"180\"/> <br>\n\t * - \"color-dodge\" <br>\n\t * <img src=\"../images/color-dodge-blendmode.png\" width=\"180\"/> <br>\n\t * - \"color-burn\" <br>\n\t * <img src=\"../images/color-burn-blendmode.png\" width=\"180\"/> <br>\n\t * - \"hard-light\" <br>\n\t * <img src=\"../images/hard-light-blendmode.png\" width=\"180\"/> <br>\n\t * - \"soft-light\" <br>\n\t * <img src=\"../images/soft-light-blendmode.png\" width=\"180\"/> <br>\n\t * - \"difference\" <br>\n\t * <img src=\"../images/difference-blendmode.png\" width=\"180\"/> <br>\n\t * - \"exclusion\" <br>\n\t * <img src=\"../images/exclusion-blendmode.png\" width=\"180\"/> <br>\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n\t * @param {string} [mode=\"normal\"] - blend mode\n\t * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported)\n\t */\n\tsetBlendMode(mode = \"normal\") {\n\t\tconst context = this.getContext();\n\t\tthis.currentBlendMode = mode;\n\t\tswitch (mode) {\n\t\t\tcase \"lighter\":\n\t\t\tcase \"additive\":\n\t\t\tcase \"add\":\n\t\t\t\tcontext.globalCompositeOperation = \"lighter\";\n\t\t\t\tbreak;\n\n\t\t\tcase \"multiply\":\n\t\t\tcase \"screen\":\n\t\t\tcase \"overlay\":\n\t\t\tcase \"darken\":\n\t\t\tcase \"lighten\":\n\t\t\tcase \"color-dodge\":\n\t\t\tcase \"color-burn\":\n\t\t\tcase \"hard-light\":\n\t\t\tcase \"soft-light\":\n\t\t\tcase \"difference\":\n\t\t\tcase \"exclusion\":\n\t\t\t\tcontext.globalCompositeOperation = mode;\n\t\t\t\t// verify the browser accepted the mode\n\t\t\t\tif (context.globalCompositeOperation !== mode) {\n\t\t\t\t\tcontext.globalCompositeOperation = \"source-over\";\n\t\t\t\t\tthis.currentBlendMode = \"normal\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault: // normal\n\t\t\t\tcontext.globalCompositeOperation = \"source-over\";\n\t\t\t\tthis.currentBlendMode = \"normal\";\n\t\t\t\tbreak;\n\t\t}\n\t\treturn this.currentBlendMode;\n\t}\n\n\t/**\n\t * prepare the framebuffer for drawing a new frame\n\t */\n\tclear() {\n\t\tif (this.backgroundColor.alpha > 0) {\n\t\t\tthis.clearColor(this.backgroundColor);\n\t\t}\n\t}\n\n\t/**\n\t * Clears the main framebuffer with the given color\n\t * @param {Color|string} [color=\"#000000\"] - CSS color.\n\t * @param {boolean} [opaque=false] - Allow transparency [default] or clear the surface completely [true]\n\t */\n\tclearColor(color = \"#000000\", opaque = false) {\n\t\tconst canvas = this.getCanvas();\n\t\tconst context = this.getContext();\n\n\t\tthis.save();\n\t\tthis.resetTransform();\n\t\tcontext.globalAlpha = 1;\n\t\tcontext.globalCompositeOperation = opaque === true ? \"copy\" : \"source-over\";\n\t\tcontext.fillStyle = color instanceof Color ? color.toRGBA() : color;\n\t\tthis.fillRect(0, 0, canvas.width, canvas.height);\n\t\tthis.restore();\n\t}\n\n\t/**\n\t * Erase the pixels in the given rectangular area by setting them to transparent black (rgba(0,0,0,0)).\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t */\n\tclearRect(x, y, width, height) {\n\t\tthis.getContext().clearRect(x, y, width, height);\n\t}\n\n\t/**\n\t * Create a pattern with the specified repetition\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - Source image to be used as the pattern's image\n\t * @param {string} [repeat=\"no-repeat\"] - Define how the pattern should be repeated. One of `\"repeat\"` / `\"repeat-x\"` / `\"repeat-y\"` / `\"no-repeat\"`.\n\t * @returns {CanvasPattern}\n\t * @see ImageLayer#repeat\n\t * @example\n\t * let tileable   = renderer.createPattern(image, \"repeat\");\n\t * let horizontal = renderer.createPattern(image, \"repeat-x\");\n\t * let vertical   = renderer.createPattern(image, \"repeat-y\");\n\t * let basic      = renderer.createPattern(image, \"no-repeat\");\n\t */\n\tcreatePattern(image, repeat = \"no-repeat\") {\n\t\t// DOM `CanvasRenderingContext2D.createPattern` throws a\n\t\t// TypeError when called without a `repeat` argument; defaulting\n\t\t// at the engine boundary matches the WebGL renderer's behaviour\n\t\t// (where TextureAtlas would have defaulted to \"no-repeat\"\n\t\t// internally anyway) so both renderers behave identically.\n\t\treturn this.getContext().createPattern(image, repeat);\n\t}\n\n\t/**\n\t * Draw an image onto the main using the canvas api\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - An element to draw into the context.\n\t * @param {number} sx - The X coordinate of the top left corner of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} sy - The Y coordinate of the top left corner of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} sw - The width of the sub-rectangle of the source image to draw into the destination context. If not specified, the entire rectangle from the coordinates specified by sx and sy to the bottom-right corner of the image is used.\n\t * @param {number} sh - The height of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} dx - The X coordinate in the destination canvas at which to place the top-left corner of the source image.\n\t * @param {number} dy - The Y coordinate in the destination canvas at which to place the top-left corner of the source image.\n\t * @param {number} dw - The width to draw the image in the destination canvas. This allows scaling of the drawn image. If not specified, the image is not scaled in width when drawn.\n\t * @param {number} dh - The height to draw the image in the destination canvas. This allows scaling of the drawn image. If not specified, the image is not scaled in height when drawn.\n\t * @example\n\t * // Position the image on the canvas:\n\t * renderer.drawImage(image, dx, dy);\n\t * // Position the image on the canvas, and specify width and height of the image:\n\t * renderer.drawImage(image, dx, dy, dWidth, dHeight);\n\t * // Clip the image and position the clipped part on the canvas:\n\t * renderer.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);\n\t */\n\tdrawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\n\t\tif (typeof sw === \"undefined\") {\n\t\t\tsw = dw = image.width;\n\t\t\tsh = dh = image.height;\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t} else if (typeof dx === \"undefined\") {\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tdw = sw;\n\t\t\tdh = sh;\n\t\t\tsw = image.width;\n\t\t\tsh = image.height;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t}\n\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// clamp to pixel grid\n\t\t\tdx = ~~dx;\n\t\t\tdy = ~~dy;\n\t\t}\n\n\t\t// apply a tint if required\n\t\tlet source = image;\n\t\tconst tint = this.currentTint.toArray();\n\t\tif (tint[0] !== 1.0 || tint[1] !== 1.0 || tint[2] !== 1.0) {\n\t\t\t// get a tinted version of this image from the texture cache\n\t\t\tsource = this.cache.tint(image, this.currentTint.toRGB());\n\t\t}\n\t\tcontext.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);\n\t}\n\n\t/**\n\t * @inheritdoc\n\t *\n\t * Renders the light by drawing a cached radial `Gradient` via\n\t * `Gradient.toCanvas()`. The Gradient instance is cached per-Light2d\n\t * in a `WeakMap` and rebuilt only when the light's radii / color /\n\t * intensity change. `toCanvas` itself shares a single\n\t * `CanvasRenderTarget` across all gradients in the engine, so memory\n\t * stays at O(1) regardless of how many lights are active.\n\t *\n\t * The cached Gradient is always circular (outer radius =\n\t * `max(radiusX, radiusY)`) — `drawImage`'s non-uniform stretch\n\t * produces the elliptical falloff for non-square lights, matching\n\t * the procedural shader's behavior on WebGL.\n\t * @param {object} light - the Light2d instance to render\n\t */\n\tdrawLight(light) {\n\t\tif (this._lightCache === undefined) {\n\t\t\tthis._lightCache = new WeakMap();\n\t\t}\n\t\tlet entry = this._lightCache.get(light);\n\t\tconst c = light.color;\n\t\tif (\n\t\t\tentry === undefined ||\n\t\t\tentry.radiusX !== light.radiusX ||\n\t\t\tentry.radiusY !== light.radiusY ||\n\t\t\tentry.r !== c.r ||\n\t\t\tentry.g !== c.g ||\n\t\t\tentry.b !== c.b ||\n\t\t\tentry.intensity !== light.intensity\n\t\t) {\n\t\t\tconst r = Math.max(light.radiusX, light.radiusY);\n\t\t\tconst gradient = this.createRadialGradient(r, r, 0, r, r, r);\n\t\t\tgradient.addColorStop(0, c.toRGBA(light.intensity));\n\t\t\tgradient.addColorStop(1, c.toRGBA(0.0));\n\t\t\tentry = {\n\t\t\t\tgradient,\n\t\t\t\tradius: r,\n\t\t\t\tradiusX: light.radiusX,\n\t\t\t\tradiusY: light.radiusY,\n\t\t\t\tr: c.r,\n\t\t\t\tg: c.g,\n\t\t\t\tb: c.b,\n\t\t\t\tintensity: light.intensity,\n\t\t\t};\n\t\t\tthis._lightCache.set(light, entry);\n\t\t}\n\t\tconst r2 = entry.radius * 2;\n\t\t// `Gradient.toCanvas` renders into the fixed-resolution shared\n\t\t// `CanvasRenderTarget` (one per engine, reused across all\n\t\t// gradients). `drawImage` with the returned source rect crops the\n\t\t// padding and stretches the circular gradient into the elliptical\n\t\t// bounding box `(light.width × light.height)`.\n\t\tconst baked = entry.gradient.toCanvas(this, 0, 0, r2, r2);\n\t\tthis.drawImage(\n\t\t\tbaked.canvas,\n\t\t\t0,\n\t\t\t0,\n\t\t\tbaked.width,\n\t\t\tbaked.height,\n\t\t\tlight.pos.x,\n\t\t\tlight.pos.y,\n\t\t\tlight.width,\n\t\t\tlight.height,\n\t\t);\n\t}\n\n\t/**\n\t * Draw a pattern within the given rectangle.\n\t * @param {CanvasPattern} pattern - Pattern object\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t * @see CanvasRenderer#createPattern\n\t */\n\tdrawPattern(pattern, x, y, width, height) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\t\tconst fillStyle = context.fillStyle;\n\t\tcontext.fillStyle = pattern;\n\t\t// translate so the pattern origin aligns with the draw position\n\t\tcontext.save();\n\t\tcontext.translate(x, y);\n\t\tcontext.fillRect(0, 0, width, height);\n\t\tcontext.restore();\n\t\tcontext.fillStyle = fillStyle;\n\t}\n\n\t/**\n\t * Draw a textured triangle mesh.\n\t * Uses per-triangle affine texture mapping with back-to-front depth\n\t * sorting (painter's algorithm) and optional backface culling.\n\t * Note: the painter's algorithm works well for convex shapes but\n\t * may produce visual artifacts with concave or self-overlapping\n\t * geometry (e.g. a torus), as Canvas 2D has no hardware depth\n\t * buffer. Use the WebGL renderer for correct depth ordering on\n\t * complex meshes.\n\t *\n\t * Multi-material meshes (`mesh.vertexColors` present) render here\n\t * via per-triangle solid fill — each triangle's three vertices\n\t * share one baked material color, so we read `vertexColors[v0]`\n\t * and use it as the triangle's fillStyle, multiplied by\n\t * `currentTint`. The single shared texture (typically the 1×1\n\t * white-pixel fallback for Kd-only models) is bypassed in that\n\t * path. Canvas can't do per-material textures; if you need that,\n\t * use the WebGL renderer.\n\t * @param {Mesh} mesh - a Mesh renderable or compatible object\n\t */\n\tdrawMesh(mesh) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst vertices = mesh.vertices;\n\t\tconst uvs = mesh.uvs;\n\t\tconst indices = mesh.indices;\n\t\tconst vertexColors = mesh.vertexColors;\n\n\t\t// apply tint if set. When `vertexColors` is present the solid-\n\t\t// fill path below reads color from the baked buffer per\n\t\t// triangle and never samples the image, so skip the tint cache\n\t\t// allocation entirely in that case.\n\t\tlet image = mesh.texture.getTexture();\n\t\tconst tint = this.currentTint.toArray();\n\t\tif (\n\t\t\t!vertexColors &&\n\t\t\t(tint[0] !== 1.0 || tint[1] !== 1.0 || tint[2] !== 1.0)\n\t\t) {\n\t\t\timage = this.cache.tint(image, this.currentTint.toRGB());\n\t\t}\n\t\tconst imgW = image.width;\n\t\tconst imgH = image.height;\n\t\t// Solid-fill fast path: kicks in for either (a) per-vertex\n\t\t// color meshes (multi-material) where each triangle's color\n\t\t// comes from the baked `vertexColors`, or (b) Kd-only single-\n\t\t// material meshes where the texture is the 1×1 white-pixel\n\t\t// fallback. Both bypass Canvas's per-triangle affine drawImage\n\t\t// (which produces sub-pixel artifacts mapping a 1×1 source\n\t\t// onto large triangles) and fall back to `fill()`.\n\t\tconst solidFillKd = imgW === 1 && imgH === 1;\n\t\tconst solidFill = solidFillKd || vertexColors !== undefined;\n\t\t// pre-extract tint as 0..1 floats for per-vertex color modulation\n\t\tconst tintR = tint[0];\n\t\tconst tintG = tint[1];\n\t\tconst tintB = tint[2];\n\t\tlet solidFillStyle = null;\n\t\tif (solidFillKd && !vertexColors) {\n\t\t\t// Single-material 1×1 path — one fill color for the whole\n\t\t\t// mesh, sampled from the pre-tinted image. Routes through\n\t\t\t// `Renderer.createCanvas` for `OffscreenCanvas` / worker\n\t\t\t// safety.\n\t\t\tif (!this._meshColorCanvas) {\n\t\t\t\tthis._meshColorCanvas = Renderer.createCanvas(1, 1, true);\n\t\t\t\tthis._meshColorCtx = this._meshColorCanvas.getContext(\"2d\");\n\t\t\t}\n\t\t\tthis._meshColorCtx.clearRect(0, 0, 1, 1);\n\t\t\tthis._meshColorCtx.drawImage(image, 0, 0);\n\t\t\tconst pixel = this._meshColorCtx.getImageData(0, 0, 1, 1).data;\n\t\t\tsolidFillStyle = `rgb(${pixel[0]},${pixel[1]},${pixel[2]})`;\n\t\t}\n\t\tconst cullBack = mesh.cullBackFaces === true;\n\t\tconst triCount = indices.length / 3;\n\n\t\t// pre-allocate flat sort array (reuse across frames via closure)\n\t\t// each entry stores: [sortKey, originalIndex]\n\t\tif (!this._meshSortBuf || this._meshSortBuf.length < triCount * 2) {\n\t\t\tthis._meshSortBuf = new Float64Array(triCount * 2);\n\t\t}\n\t\tconst sortBuf = this._meshSortBuf;\n\t\tlet visCount = 0;\n\n\t\t// build sort keys for visible triangles (no object allocation)\n\t\tfor (let j = 0; j < indices.length; j += 3) {\n\t\t\tconst i0 = indices[j];\n\t\t\tconst i1 = indices[j + 1];\n\t\t\tconst i2 = indices[j + 2];\n\n\t\t\t// backface culling\n\t\t\tif (cullBack) {\n\t\t\t\tconst x0 = vertices[i0 * 3];\n\t\t\t\tconst y0 = vertices[i0 * 3 + 1];\n\t\t\t\tconst cross =\n\t\t\t\t\t(vertices[i1 * 3] - x0) * (vertices[i2 * 3 + 1] - y0) -\n\t\t\t\t\t(vertices[i2 * 3] - x0) * (vertices[i1 * 3 + 1] - y0);\n\t\t\t\tif (cross > 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store maxZ as sort key and triangle start index\n\t\t\tconst z0 = vertices[i0 * 3 + 2];\n\t\t\tconst z1 = vertices[i1 * 3 + 2];\n\t\t\tconst z2 = vertices[i2 * 3 + 2];\n\t\t\tconst idx = visCount * 2;\n\t\t\tsortBuf[idx] = z0 > z1 ? (z0 > z2 ? z0 : z2) : z1 > z2 ? z1 : z2;\n\t\t\tsortBuf[idx + 1] = j; // original index into indices array\n\t\t\tvisCount++;\n\t\t}\n\n\t\t// sort back-to-front (largest Z = farthest = drawn first)\n\t\tfor (let i = 1; i < visCount; i++) {\n\t\t\tconst keyZ = sortBuf[i * 2];\n\t\t\tconst keyJ = sortBuf[i * 2 + 1];\n\t\t\tlet k = i - 1;\n\t\t\twhile (k >= 0 && sortBuf[k * 2] < keyZ) {\n\t\t\t\tsortBuf[(k + 1) * 2] = sortBuf[k * 2];\n\t\t\t\tsortBuf[(k + 1) * 2 + 1] = sortBuf[k * 2 + 1];\n\t\t\t\tk--;\n\t\t\t}\n\t\t\tsortBuf[(k + 1) * 2] = keyZ;\n\t\t\tsortBuf[(k + 1) * 2 + 1] = keyJ;\n\t\t}\n\n\t\t// draw sorted triangles using raw context\n\t\tconst context = this.getContext();\n\n\t\tfor (let t = 0; t < visCount; t++) {\n\t\t\tconst j = sortBuf[t * 2 + 1];\n\t\t\tconst i0 = indices[j];\n\t\t\tconst i1 = indices[j + 1];\n\t\t\tconst i2 = indices[j + 2];\n\n\t\t\tconst x0 = vertices[i0 * 3];\n\t\t\tconst y0 = vertices[i0 * 3 + 1];\n\t\t\tconst x1 = vertices[i1 * 3];\n\t\t\tconst y1 = vertices[i1 * 3 + 1];\n\t\t\tconst x2 = vertices[i2 * 3];\n\t\t\tconst y2 = vertices[i2 * 3 + 1];\n\n\t\t\t// UV coordinates scaled to pixel space\n\t\t\tconst u0 = uvs[i0 * 2] * imgW;\n\t\t\tconst v0 = uvs[i0 * 2 + 1] * imgH;\n\t\t\tconst u1 = uvs[i1 * 2] * imgW;\n\t\t\tconst v1 = uvs[i1 * 2 + 1] * imgH;\n\t\t\tconst u2 = uvs[i2 * 2] * imgW;\n\t\t\tconst v2 = uvs[i2 * 2 + 1] * imgH;\n\n\t\t\tconst du1 = u1 - u0;\n\t\t\tconst dv1 = v1 - v0;\n\t\t\tconst du2 = u2 - u0;\n\t\t\tconst dv2 = v2 - v0;\n\t\t\tconst rawDet = du1 * dv2 - du2 * dv1;\n\n\t\t\t// expand triangle by 0.5px to cover seams\n\t\t\tconst cx = (x0 + x1 + x2) * 0.333333;\n\t\t\tconst cy = (y0 + y1 + y2) * 0.333333;\n\n\t\t\tcontext.save();\n\t\t\tcontext.beginPath();\n\t\t\tcontext.moveTo(\n\t\t\t\tx0 + (x0 > cx ? 0.5 : x0 < cx ? -0.5 : 0),\n\t\t\t\ty0 + (y0 > cy ? 0.5 : y0 < cy ? -0.5 : 0),\n\t\t\t);\n\t\t\tcontext.lineTo(\n\t\t\t\tx1 + (x1 > cx ? 0.5 : x1 < cx ? -0.5 : 0),\n\t\t\t\ty1 + (y1 > cy ? 0.5 : y1 < cy ? -0.5 : 0),\n\t\t\t);\n\t\t\tcontext.lineTo(\n\t\t\t\tx2 + (x2 > cx ? 0.5 : x2 < cx ? -0.5 : 0),\n\t\t\t\ty2 + (y2 > cy ? 0.5 : y2 < cy ? -0.5 : 0),\n\t\t\t);\n\n\t\t\tif (solidFill) {\n\t\t\t\t// Solid-fill path. Either the texture is 1×1 (Kd-only\n\t\t\t\t// single-material — `solidFillStyle` is pre-computed) or\n\t\t\t\t// the mesh has per-vertex baked colors (multi-material\n\t\t\t\t// — read color from `vertexColors[v0]` since all 3\n\t\t\t\t// vertices of a triangle share a material color).\n\t\t\t\tcontext.closePath();\n\t\t\t\tif (vertexColors) {\n\t\t\t\t\t// ARGB-packed: A R G B in 4 bytes MSB→LSB. Carries\n\t\t\t\t\t// the per-material opacity (MTL `d`) baked at\n\t\t\t\t\t// construction time — emit as `rgba(...)` so it\n\t\t\t\t\t// reaches the canvas alpha channel rather than\n\t\t\t\t\t// silently dropping to fully opaque.\n\t\t\t\t\tconst c = vertexColors[indices[j]];\n\t\t\t\t\tconst cr = Math.round(((c >>> 16) & 0xff) * tintR);\n\t\t\t\t\tconst cg = Math.round(((c >>> 8) & 0xff) * tintG);\n\t\t\t\t\tconst cb = Math.round((c & 0xff) * tintB);\n\t\t\t\t\tconst ca = ((c >>> 24) & 0xff) / 255;\n\t\t\t\t\tcontext.fillStyle = `rgba(${cr},${cg},${cb},${ca})`;\n\t\t\t\t} else {\n\t\t\t\t\tcontext.fillStyle = solidFillStyle;\n\t\t\t\t}\n\t\t\t\tcontext.fill();\n\t\t\t} else if (rawDet === 0) {\n\t\t\t\t// degenerate UV triangle — sample a solid color from\n\t\t\t\t// the texture (common with color-palette models where\n\t\t\t\t// all 3 UVs map to the same point). Routes through\n\t\t\t\t// `Renderer.createCanvas` for worker / `OffscreenCanvas`\n\t\t\t\t// safety, matching the `solidFillKd` path above.\n\t\t\t\tcontext.closePath();\n\t\t\t\tif (!this._meshColorCanvas) {\n\t\t\t\t\tthis._meshColorCanvas = Renderer.createCanvas(1, 1, true);\n\t\t\t\t\tthis._meshColorCtx = this._meshColorCanvas.getContext(\"2d\");\n\t\t\t\t}\n\t\t\t\tconst sx = Math.min(Math.max(Math.round(u0), 0), imgW - 1);\n\t\t\t\tconst sy = Math.min(Math.max(Math.round(v0), 0), imgH - 1);\n\t\t\t\tthis._meshColorCtx.drawImage(image, sx, sy, 1, 1, 0, 0, 1, 1);\n\t\t\t\tconst pixel = this._meshColorCtx.getImageData(0, 0, 1, 1).data;\n\t\t\t\tcontext.fillStyle = `rgb(${pixel[0]},${pixel[1]},${pixel[2]})`;\n\t\t\t\tcontext.fill();\n\t\t\t} else {\n\t\t\t\tconst dx1 = x1 - x0;\n\t\t\t\tconst dy1 = y1 - y0;\n\t\t\t\tconst dx2 = x2 - x0;\n\t\t\t\tconst dy2 = y2 - y0;\n\t\t\t\tconst det = 1 / rawDet;\n\t\t\t\tconst a = (dv2 * dx1 - dv1 * dx2) * det;\n\t\t\t\tconst b = (dv2 * dy1 - dv1 * dy2) * det;\n\t\t\t\tconst c = (du1 * dx2 - du2 * dx1) * det;\n\t\t\t\tconst d = (du1 * dy2 - du2 * dy1) * det;\n\n\t\t\t\tcontext.clip();\n\t\t\t\tcontext.transform(\n\t\t\t\t\ta,\n\t\t\t\t\tb,\n\t\t\t\t\tc,\n\t\t\t\t\td,\n\t\t\t\t\tx0 - a * u0 - c * v0,\n\t\t\t\t\ty0 - b * u0 - d * v0,\n\t\t\t\t);\n\t\t\t\tcontext.drawImage(image, 0, 0);\n\t\t\t}\n\t\t\tcontext.restore();\n\t\t}\n\t}\n\n\t/**\n\t * starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path\n\t * @example\n\t * // First path\n\t * renderer.beginPath();\n\t * renderer.setColor(\"blue\");\n\t * renderer.moveTo(20, 20);\n\t * renderer.lineTo(200, 20);\n\t * renderer.stroke();\n\t * // Second path\n\t * renderer.beginPath();\n\t * renderer.setColor(\"green\");\n\t * renderer.moveTo(20, 20);\n\t * renderer.lineTo(120, 120);\n\t * renderer.stroke();\n\t */\n\tbeginPath() {\n\t\tthis.getContext().beginPath();\n\t}\n\n\t/**\n\t * begins a new sub-path at the point specified by the given (x, y) coordinates.\n\t * @param {number} x - The x axis of the point.\n\t * @param {number} y - The y axis of the point.\n\t */\n\tmoveTo(x, y) {\n\t\tthis.getContext().moveTo(x, y);\n\t}\n\n\t/**\n\t * adds a straight line to the current sub-path by connecting the sub-path's last point to the specified (x, y) coordinates.\n\t */\n\tlineTo(x, y) {\n\t\tthis.getContext().lineTo(x, y);\n\t}\n\n\t/**\n\t * Adds a quadratic Bezier curve to the current sub-path.\n\t * @param {number} cpx - The x-axis coordinate of the control point.\n\t * @param {number} cpy - The y-axis coordinate of the control point.\n\t * @param {number} x - The x-axis coordinate of the end point.\n\t * @param {number} y - The y-axis coordinate of the end point.\n\t */\n\tquadraticCurveTo(cpx, cpy, x, y) {\n\t\tthis.getContext().quadraticCurveTo(cpx, cpy, x, y);\n\t}\n\n\t/**\n\t * Adds a cubic Bezier curve to the current sub-path.\n\t * @param {number} cp1x - The x-axis coordinate of the first control point.\n\t * @param {number} cp1y - The y-axis coordinate of the first control point.\n\t * @param {number} cp2x - The x-axis coordinate of the second control point.\n\t * @param {number} cp2y - The y-axis coordinate of the second control point.\n\t * @param {number} x - The x-axis coordinate of the end point.\n\t * @param {number} y - The y-axis coordinate of the end point.\n\t */\n\tbezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {\n\t\tthis.getContext().bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n\t}\n\n\t/**\n\t * Adds a circular arc to the current sub-path, using the given control points and radius.\n\t * @param {number} x1 - The x-axis coordinate of the first control point.\n\t * @param {number} y1 - The y-axis coordinate of the first control point.\n\t * @param {number} x2 - The x-axis coordinate of the second control point.\n\t * @param {number} y2 - The y-axis coordinate of the second control point.\n\t * @param {number} radius - The arc's radius. Must be non-negative.\n\t */\n\tarcTo(x1, y1, x2, y2, radius) {\n\t\tthis.getContext().arcTo(x1, y1, x2, y2, radius);\n\t}\n\n\t/**\n\t * creates a rectangular path whose starting point is at (x, y) and whose size is specified by width and height.\n\t * @param {number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t */\n\trect(x, y, width, height) {\n\t\tthis.getContext().rect(x, y, width, height);\n\t}\n\n\t/**\n\t * adds a rounded rectangle to the current path.\n\t * @param {number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t * @param {number} radius - The corner radius.\n\t */\n\troundRect(x, y, width, height, radii) {\n\t\tthis.getContext().roundRect(x, y, width, height, radii);\n\t}\n\n\t/**\n\t * stroke the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to stroke\n\t * @param {boolean} [fill=false] - fill the shape with the current color if true\n\t */\n\tstroke(shape, fill) {\n\t\tif (typeof shape === \"undefined\") {\n\t\t\tif (this.path2D.points.length > 0) {\n\t\t\t\t// replay path2D interpolated points onto the native context\n\t\t\t\tconst context = this.getContext();\n\t\t\t\tconst points = this.path2D.points;\n\t\t\t\tconst subPaths = this.path2D.subPaths;\n\t\t\t\tcontext.beginPath();\n\t\t\t\tcontext.moveTo(points[0].x, points[0].y);\n\t\t\t\tlet s = 0;\n\t\t\t\tfor (let i = 1; i < points.length; i++) {\n\t\t\t\t\t// break at each sub-path boundary (moveTo) so sub-paths aren't\n\t\t\t\t\t// joined; native fill() then carves holes via its winding rule\n\t\t\t\t\tif (s < subPaths.length && i === subPaths[s]) {\n\t\t\t\t\t\tcontext.moveTo(points[i].x, points[i].y);\n\t\t\t\t\t\ts++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontext.lineTo(points[i].x, points[i].y);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (fill === true) {\n\t\t\t\t\tcontext.fill();\n\t\t\t\t} else {\n\t\t\t\t\tcontext.stroke();\n\t\t\t\t}\n\t\t\t\tthis.path2D.beginPath();\n\t\t\t} else if (fill === true) {\n\t\t\t\tthis.getContext().fill();\n\t\t\t} else {\n\t\t\t\tthis.getContext().stroke();\n\t\t\t}\n\t\t} else {\n\t\t\tsuper.stroke(shape, fill);\n\t\t}\n\t}\n\n\t/**\n\t * fill the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to fill\n\t */\n\tfill(shape) {\n\t\tthis.stroke(shape, true);\n\t}\n\n\t/**\n\t * add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing\n\t */\n\tclosePath() {\n\t\tthis.getContext().closePath();\n\t}\n\n\t/**\n\t * Stroke an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeArc(x, y, radius, start, end, antiClockwise, fill = false) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\n\t\tcontext.translate(x, y);\n\t\tcontext.beginPath();\n\t\tcontext.arc(0, 0, radius, start, end, antiClockwise || false);\n\t\tcontext[fill === true ? \"fill\" : \"stroke\"]();\n\t\tcontext.translate(-x, -y);\n\t}\n\n\t/**\n\t * Fill an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t */\n\tfillArc(x, y, radius, start, end, antiClockwise) {\n\t\tthis.strokeArc(x, y, radius, start, end, antiClockwise || false, true);\n\t}\n\n\t/**\n\t * Stroke an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeEllipse(x, y, w, h, fill = false) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\t\tcontext.beginPath();\n\t\tcontext.ellipse(x, y, w, h, 0, 0, Math.PI * 2);\n\t\tcontext[fill === true ? \"fill\" : \"stroke\"]();\n\t\tcontext.closePath();\n\t}\n\n\t/**\n\t * Fill an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t */\n\tfillEllipse(x, y, w, h) {\n\t\tthis.strokeEllipse(x, y, w, h, true);\n\t}\n\n\t/**\n\t * Stroke a line of the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tstrokeLine(startX, startY, endX, endY) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\n\t\tconst context = this.getContext();\n\n\t\tcontext.beginPath();\n\t\tcontext.moveTo(startX, startY);\n\t\tcontext.lineTo(endX, endY);\n\t\tcontext.stroke();\n\t}\n\n\t/**\n\t * Fill a line of the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tfillLine(startX, startY, endX, endY) {\n\t\tthis.strokeLine(startX, startY, endX, endY);\n\t}\n\n\t/**\n\t * Set the line dash pattern.\n\t * @param {number[]} segments - dash pattern\n\t */\n\tsetLineDash(segments) {\n\t\tsuper.setLineDash(segments);\n\t\tthis.getContext().setLineDash(this.renderState.lineDash);\n\t}\n\n\t/**\n\t * Get the current line dash pattern.\n\t * @returns {number[]} dash pattern\n\t */\n\tgetLineDash() {\n\t\treturn this.getContext().getLineDash();\n\t}\n\n\t/**\n\t * Stroke the given me.Polygon on the screen\n\t * @param {Polygon} poly - the shape to draw\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokePolygon(poly, fill = false) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\t\tconst points = poly.points;\n\t\tconst pointsLength = points.length;\n\t\tconst firstPoint = points[0];\n\n\t\tthis.translate(poly.pos.x, poly.pos.y);\n\n\t\tcontext.beginPath();\n\t\tcontext.moveTo(firstPoint.x, firstPoint.y);\n\t\tfor (let i = 1; i < pointsLength; i++) {\n\t\t\tconst point = points[i];\n\t\t\tcontext.lineTo(point.x, point.y);\n\t\t}\n\t\tcontext.lineTo(firstPoint.x, firstPoint.y);\n\t\tcontext[fill === true ? \"fill\" : \"stroke\"]();\n\t\tcontext.closePath();\n\n\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t}\n\n\t/**\n\t * Fill the given me.Polygon on the screen\n\t * @param {Polygon} poly - the shape to draw\n\t */\n\tfillPolygon(poly) {\n\t\tthis.strokePolygon(poly, true);\n\t}\n\n\t/**\n\t * Stroke a rectangle at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRect(x, y, width, height, fill = false) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\n\t\tcontext[fill === true ? \"fillRect\" : \"strokeRect\"](x, y, width, height);\n\t}\n\n\t/**\n\t * Draw a filled rectangle at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t */\n\tfillRect(x, y, width, height) {\n\t\tthis.strokeRect(x, y, width, height, true);\n\t}\n\n\t/**\n\t * Stroke a rounded rectangle at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t * @param {number} radius\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRoundRect(x, y, width, height, radius, fill = false) {\n\t\tif (this.getGlobalAlpha() < 1 / 255) {\n\t\t\t// Fast path: don't draw fully transparent\n\t\t\treturn;\n\t\t}\n\t\tconst context = this.getContext();\n\n\t\tcontext.beginPath();\n\t\tcontext.roundRect(x, y, width, height, radius);\n\t\tcontext[fill === true ? \"fill\" : \"stroke\"]();\n\t}\n\n\t/**\n\t * Draw a rounded filled rectangle at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t * @param {number} radius\n\t */\n\tfillRoundRect(x, y, width, height, radius) {\n\t\tthis.strokeRoundRect(x, y, width, height, radius, true);\n\t}\n\n\t/**\n\t * Stroke a Point at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t */\n\tstrokePoint(x, y) {\n\t\tthis.strokeLine(x, y, x + 1, y + 1);\n\t}\n\n\t/**\n\t * Draw a point at the specified coordinates\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t */\n\tfillPoint(x, y) {\n\t\tthis.strokePoint(x, y);\n\t}\n\n\t/**\n\t * restores the most recently saved renderer state by popping the top entry in the drawing state stack\n\t * @example\n\t * // Save the current state\n\t * renderer.save();\n\t *\n\t * // apply a transform and draw a rect\n\t * renderer.tranform(matrix);\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Restore to the state saved by the most recent call to save()\n\t * renderer.restore();\n\t */\n\trestore() {\n\t\tconst canvas = this.getCanvas();\n\t\tconst context = this.getContext();\n\t\t// restore the native context state (transform, globalAlpha, fillStyle, clip, etc.)\n\t\tcontext.restore();\n\t\t// restore JS-side state that the native context doesn't track (tint, blend mode)\n\t\tconst result = this.renderState.restore(canvas.width, canvas.height);\n\t\tif (result !== null) {\n\t\t\tthis.setBlendMode(result.blendMode);\n\t\t}\n\t\t// sync customShader from renderState (mirrors WebGLRenderer.restore)\n\t\tthis.customShader = this.renderState.currentShader;\n\t\t// re-sync from the native context (which is authoritative for Canvas)\n\t\t// fillStyle may be a CanvasGradient/CanvasPattern — only sync if it's a color string\n\t\tif (typeof context.fillStyle === \"string\") {\n\t\t\tthis.currentColor.copy(context.fillStyle);\n\t\t}\n\t\tthis.currentColor.alpha = context.globalAlpha;\n\t\t// reset scissor cache so the next clipRect() won't skip\n\t\tthis.currentScissor[0] = 0;\n\t\tthis.currentScissor[1] = 0;\n\t\tthis.currentScissor[2] = canvas.width;\n\t\tthis.currentScissor[3] = canvas.height;\n\t}\n\n\t/**\n\t * saves the entire state of the renderer by pushing the current state onto a stack.\n\t * @example\n\t * // Save the current state\n\t * renderer.save();\n\t *\n\t * // apply a transform and draw a rect\n\t * renderer.tranform(matrix);\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Restore to the state saved by the most recent call to save()\n\t * renderer.restore();\n\t */\n\tsave() {\n\t\tthis.getContext().save();\n\t\tthis.renderState.currentShader = this.customShader;\n\t\tthis.renderState.save();\n\t}\n\n\t/**\n\t * adds a rotation to the transformation matrix.\n\t * @param {number} angle - the rotation angle, clockwise in radians\n\t * @example\n\t * // Rotated rectangle\n\t * renderer.rotate((45 * Math.PI) / 180);\n\t * renderer.setColor(\"red\");\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Reset transformation matrix to the identity matrix\n\t * renderer.setTransform(1, 0, 0, 1, 0, 0);\n\t */\n\trotate(angle) {\n\t\tthis.getContext().rotate(angle);\n\t}\n\n\t/**\n\t * adds a scaling transformation to the renderer units horizontally and/or vertically\n\t * @param {number} x - Scaling factor in the horizontal direction. A negative value flips pixels across the vertical axis. A value of 1 results in no horizontal scaling.\n\t * @param {number} y - Scaling factor in the vertical direction. A negative value flips pixels across the horizontal axis. A value of 1 results in no vertical scaling\n\t */\n\tscale(x, y) {\n\t\tthis.getContext().scale(x, y);\n\t}\n\n\t/**\n\t * Set the current fill & stroke style color.\n\t * By default, or upon reset, the value is set to #000000.\n\t * @param {Color|string|Gradient} color - css color value or a Gradient object\n\t */\n\tsetColor(color) {\n\t\tconst context = this.getContext();\n\n\t\tif (color instanceof Gradient) {\n\t\t\tthis.renderState.currentGradient = color;\n\t\t\tcontext.strokeStyle = context.fillStyle = color.toCanvasGradient(context);\n\t\t} else {\n\t\t\tthis.renderState.currentGradient = null;\n\t\t\tthis.currentColor.copy(color);\n\t\t\tcontext.strokeStyle = context.fillStyle = this.currentColor.toRGBA();\n\t\t}\n\t}\n\n\t/**\n\t * Set the global alpha\n\t * @param {number} alpha - 0.0 to 1.0 values accepted.\n\t */\n\tsetGlobalAlpha(alpha) {\n\t\tthis.currentColor.alpha = alpha;\n\t\tthis.getContext().globalAlpha = alpha;\n\t}\n\n\t/**\n\t * Return the global alpha\n\t * @returns {number} global alpha value\n\t */\n\tgetGlobalAlpha() {\n\t\treturn this.getContext().globalAlpha;\n\t}\n\n\t/**\n\t * sets or returns the thickness of lines for shape drawing\n\t * @type {number}\n\t * @default 1\n\t */\n\tget lineWidth() {\n\t\treturn this.getContext().lineWidth;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tset lineWidth(value) {\n\t\tthis.getContext().lineWidth = value;\n\t}\n\n\t/**\n\t * sets or returns the shape used to join two line segments where they meet.\n\t * There are three possible values for this property: \"round\", \"bevel\", and \"miter\"\n\t * @type {string}\n\t * @default \"miter\"\n\t */\n\tget lineJoin() {\n\t\treturn this.getContext().lineJoin;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tset lineJoin(value) {\n\t\tconst context = this.getContext();\n\t\tcontext.lineJoin = value;\n\t}\n\n\t/**\n\t * Reset (overrides) the renderer transformation matrix to the\n\t * identity one, and then apply the given transformation matrix.\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix to transform by, or the a component to multiply the current matrix by\n\t * @param {number} b - the b component to multiply the current matrix by\n\t * @param {number} c - the c component to multiply the current matrix by\n\t * @param {number} d - the d component to multiply the current matrix by\n\t * @param {number} e - the e component to multiply the current matrix by\n\t * @param {number} f - the f component to multiply the current matrix by\n\t */\n\tsetTransform(a, b, c, d, e, f) {\n\t\tthis.resetTransform();\n\t\tthis.transform(a, b, c, d, e, f);\n\t}\n\n\t/**\n\t * Multiply given matrix into the renderer tranformation matrix\n\t * @see {@link CanvasRenderer.setTransform} which will reset the current transform matrix prior to performing the new transformation\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix to transform by, or the a component to multiply the current matrix by\n\t * @param {number} b - the b component to multiply the current matrix by\n\t * @param {number} c - the c component to multiply the current matrix by\n\t * @param {number} d - the d component to multiply the current matrix by\n\t * @param {number} e - the e component to multiply the current matrix by\n\t * @param {number} f - the f component to multiply the current matrix by\n\t */\n\ttransform(a, b, c, d, e, f) {\n\t\tif (typeof a === \"object\") {\n\t\t\tconst m = a.val;\n\t\t\tif (m.length === 16) {\n\t\t\t\t// Matrix3d (column-major 4x4)\n\t\t\t\tb = m[1];\n\t\t\t\tc = m[4];\n\t\t\t\td = m[5];\n\t\t\t\te = m[12];\n\t\t\t\tf = m[13];\n\t\t\t\ta = m[0];\n\t\t\t} else {\n\t\t\t\t// Matrix2d (3x3)\n\t\t\t\tb = m[1];\n\t\t\t\tc = m[3];\n\t\t\t\td = m[4];\n\t\t\t\te = m[6];\n\t\t\t\tf = m[7];\n\t\t\t\ta = m[0];\n\t\t\t}\n\t\t}\n\t\t// else individual components\n\n\t\tif (this.settings.subPixel === false) {\n\t\t\te |= 0;\n\t\t\tf |= 0;\n\t\t}\n\n\t\tthis.getContext().transform(a, b, c, d, e, f);\n\t}\n\n\t/**\n\t * adds a translation transformation to the current matrix.\n\t * @param {number} x - Distance to move in the horizontal direction. Positive values are to the right, and negative to the left.\n\t * @param {number} y - Distance to move in the vertical direction. Positive values are down, and negative are up.\n\t */\n\ttranslate(x, y) {\n\t\tif (this.settings.subPixel === false) {\n\t\t\tthis.getContext().translate(~~x, ~~y);\n\t\t} else {\n\t\t\tthis.getContext().translate(x, y);\n\t\t}\n\t}\n\n\t/**\n\t * clip the given region from the original canvas. Once a region is clipped,\n\t * all future drawing will be limited to the clipped region.\n\t * You can however save the current region using the save(),\n\t * and restore it (with the restore() method) any time in the future.\n\t * (<u>this is an experimental feature !</u>)\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} width\n\t * @param {number} height\n\t */\n\tclipRect(x, y, width, height) {\n\t\tconst context = this.getContext();\n\t\tconst t = context.getTransform();\n\n\t\t// The two skip-optimizations below (full-viewport no-op and\n\t\t// same-as-last-clip) must reason in DEVICE space — the rect arrives\n\t\t// in LOCAL coordinates (e.g. Container clipping translates first,\n\t\t// then clips at (0, 0)), so comparing raw values against the canvas\n\t\t// box or the cache is wrong under any transform. The WebGL renderer\n\t\t// transforms the rect the same way (see the #1349 fix).\n\t\tif (t.b !== 0 || t.c !== 0) {\n\t\t\t// rotated/skewed: the axis-aligned skip logic can't reason about\n\t\t\t// the clipped region — always clip, and poison the cache so a\n\t\t\t// following axis-aligned call can't false-match. The cache is an\n\t\t\t// Int32Array (NaN would coerce to 0!) and widths are normalized\n\t\t\t// to >= 0 below, so -1 in the width slot can never match.\n\t\t\tcontext.beginPath();\n\t\t\tcontext.rect(x, y, width, height);\n\t\t\tcontext.clip();\n\t\t\tthis.currentScissor[2] = -1;\n\t\t\treturn;\n\t\t}\n\n\t\t// axis-aligned device-space box (scale can be negative — normalize),\n\t\t// floored/ceiled to integers so the Int32Array cache is exact under\n\t\t// fractional transforms (same discipline as the WebGL clipRect)\n\t\tlet dx = x * t.a + t.e;\n\t\tlet dy = y * t.d + t.f;\n\t\tlet dw = width * t.a;\n\t\tlet dh = height * t.d;\n\t\tif (dw < 0) {\n\t\t\tdx += dw;\n\t\t\tdw = -dw;\n\t\t}\n\t\tif (dh < 0) {\n\t\t\tdy += dh;\n\t\t\tdh = -dh;\n\t\t}\n\t\tconst ix = Math.floor(dx);\n\t\tconst iy = Math.floor(dy);\n\t\tconst iw = Math.ceil(dx + dw) - ix;\n\t\tconst ih = Math.ceil(dy + dh) - iy;\n\n\t\tconst canvas = this.getCanvas();\n\t\t// if the requested box is different from the full canvas;\n\t\tif (ix !== 0 || iy !== 0 || iw !== canvas.width || ih !== canvas.height) {\n\t\t\tconst currentScissor = this.currentScissor;\n\t\t\t// if different from the current scissor box\n\t\t\tif (\n\t\t\t\tcurrentScissor[0] !== ix ||\n\t\t\t\tcurrentScissor[1] !== iy ||\n\t\t\t\tcurrentScissor[2] !== iw ||\n\t\t\t\tcurrentScissor[3] !== ih\n\t\t\t) {\n\t\t\t\tcontext.beginPath();\n\t\t\t\tcontext.rect(x, y, width, height);\n\t\t\t\tcontext.clip();\n\t\t\t\t// save the new scissor box in device space\n\t\t\t\tcurrentScissor[0] = ix;\n\t\t\t\tcurrentScissor[1] = iy;\n\t\t\t\tcurrentScissor[2] = iw;\n\t\t\t\tcurrentScissor[3] = ih;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * A mask limits rendering elements to the shape and position of the given mask object.\n\t * If the drawing or rendering area is larger than the mask, only the intersecting part of the renderable will be visible.\n\t * (Note Mask are not preserved through renderer context save and restore and need so be manually cleared)\n\t * @see CanvasRenderer#clearMask\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] - the shape defining the mask to be applied\n\t * @param {boolean} [invert=false] - either the given shape should define what is visible (default) or the opposite\n\t */\n\tsetMask(mask, invert = false) {\n\t\tconst context = this.getContext();\n\n\t\tif (this.maskLevel === 0) {\n\t\t\t// only save context on the first mask\n\t\t\tcontext.save();\n\t\t\tif (typeof mask !== \"undefined\") {\n\t\t\t\tcontext.beginPath();\n\t\t\t}\n\t\t\t// else use the current path\n\t\t\tthis._maskInvertOuterAdded = false;\n\t\t}\n\n\t\tif (typeof mask !== \"undefined\") {\n\t\t\tswitch (mask.type) {\n\t\t\t\t// RoundRect\n\t\t\t\tcase \"RoundRect\":\n\t\t\t\t\tcontext.roundRect(\n\t\t\t\t\t\tmask.left,\n\t\t\t\t\t\tmask.top,\n\t\t\t\t\t\tmask.width,\n\t\t\t\t\t\tmask.height,\n\t\t\t\t\t\tmask.radius,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// Rect or Bounds\n\t\t\t\tcase \"Rectangle\":\n\t\t\t\tcase \"Bounds\":\n\t\t\t\t\tcontext.rect(mask.left, mask.top, mask.width, mask.height);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// Polygon or Line\n\t\t\t\tcase \"Polygon\":\n\t\t\t\t\t{\n\t\t\t\t\t\t// polygon\n\t\t\t\t\t\tconst _x = mask.pos.x;\n\t\t\t\t\t\tconst _y = mask.pos.y;\n\t\t\t\t\t\tcontext.moveTo(_x + mask.points[0].x, _y + mask.points[0].y);\n\t\t\t\t\t\tfor (let i = 1; i < mask.points.length; i++) {\n\t\t\t\t\t\t\tconst point = mask.points[i];\n\t\t\t\t\t\t\tcontext.lineTo(_x + point.x, _y + point.y);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"Ellipse\":\n\t\t\t\t\t{\n\t\t\t\t\t\tconst _x = mask.pos.x;\n\t\t\t\t\t\tconst _y = mask.pos.y;\n\t\t\t\t\t\tconst hw = mask.radiusV.x;\n\t\t\t\t\t\tconst hh = mask.radiusV.y;\n\t\t\t\t\t\tconst lx = _x - hw;\n\t\t\t\t\t\tconst rx = _x + hw;\n\t\t\t\t\t\tconst ty = _y - hh;\n\t\t\t\t\t\tconst by = _y + hh;\n\n\t\t\t\t\t\tconst xmagic = hw * 0.551784;\n\t\t\t\t\t\tconst ymagic = hh * 0.551784;\n\t\t\t\t\t\tconst xmin = _x - xmagic;\n\t\t\t\t\t\tconst xmax = _x + xmagic;\n\t\t\t\t\t\tconst ymin = _y - ymagic;\n\t\t\t\t\t\tconst ymax = _y + ymagic;\n\n\t\t\t\t\t\tcontext.moveTo(_x, ty);\n\t\t\t\t\t\tcontext.bezierCurveTo(xmax, ty, rx, ymin, rx, _y);\n\t\t\t\t\t\tcontext.bezierCurveTo(rx, ymax, xmax, by, _x, by);\n\t\t\t\t\t\tcontext.bezierCurveTo(xmin, by, lx, ymax, lx, _y);\n\t\t\t\t\t\tcontext.bezierCurveTo(lx, ymin, xmin, ty, _x, ty);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(\"Invalid geometry for setMask\");\n\t\t\t}\n\t\t}\n\n\t\tthis.maskLevel++;\n\n\t\tif (invert === true) {\n\t\t\t// draw a full-canvas rect as the outer path, then close the\n\t\t\t// inner shape(s) — clipping with \"evenodd\" makes the shape a hole.\n\t\t\t// The outer rect is added only once per mask sequence so chained\n\t\t\t// invert masks accumulate their cutouts correctly (each new\n\t\t\t// shape adds another hole to the same evenodd path) instead of\n\t\t\t// duplicate full-canvas rects cancelling under evenodd parity.\n\t\t\tif (this._maskInvertOuterAdded !== true) {\n\t\t\t\tcontext.rect(0, 0, this.getCanvas().width, this.getCanvas().height);\n\t\t\t\tthis._maskInvertOuterAdded = true;\n\t\t\t}\n\t\t\tcontext.clip(\"evenodd\");\n\t\t} else {\n\t\t\tcontext.clip();\n\t\t}\n\t}\n\n\t/**\n\t * disable (remove) the rendering mask set through setMask.\n\t * @see CanvasRenderer#setMask\n\t */\n\tclearMask() {\n\t\tif (this.maskLevel > 0) {\n\t\t\tthis.maskLevel = 0;\n\t\t\tthis.getContext().restore();\n\t\t}\n\t}\n}\n","/**\n * A fixed-size vertex array buffer for batching vertex data.\n * Renderer-agnostic — stores vertex data in typed arrays (Float32/Uint32).\n * Batchers must check isFull() and flush before the buffer overflows.\n * @ignore\n */\n\nexport default class VertexArrayBuffer {\n\tconstructor(vertexSize, maxVertex) {\n\t\t// the size of one vertex in floats\n\t\tthis.vertexSize = vertexSize;\n\t\t// the maximum number of vertices the vertex array buffer can hold\n\t\tthis.maxVertex = maxVertex;\n\t\t// the current number of vertices added to the vertex array buffer\n\t\tthis.vertexCount = 0;\n\n\t\t// the actual vertex data buffer\n\t\tthis.buffer = new ArrayBuffer(\n\t\t\tthis.maxVertex * this.vertexSize * Float32Array.BYTES_PER_ELEMENT,\n\t\t);\n\t\t// Float32 and Uint32 view of the vertex data array buffer\n\t\tthis.bufferF32 = new Float32Array(this.buffer);\n\t\tthis.bufferU32 = new Uint32Array(this.buffer);\n\t\t// Byte view used only for `gl.bufferData` UPLOADS via\n\t\t// `toUint8()` — bypasses NaN canonicalization that some drivers\n\t\t// (notably Apple's Metal-backed ANGLE) perform when the same\n\t\t// 4-byte slot is uploaded through a `Float32Array` view and\n\t\t// happens to form a NaN bit pattern (`A=0xFF` AND `R≥0x80`,\n\t\t// which covers most non-faded MTL diffuse colors). NOT used\n\t\t// for writing packed colors into the buffer — `bufferU32[i] =\n\t\t// tint` is byte-identical (V8 doesn't canonicalize at the\n\t\t// typed-array write site) and cheaper (one store vs four).\n\t\tthis.bufferU8 = new Uint8Array(this.buffer);\n\t}\n\n\t/**\n\t * clear the vertex array buffer\n\t * @ignore\n\t */\n\tclear() {\n\t\tthis.vertexCount = 0;\n\t}\n\n\t/**\n\t * return true if full\n\t * @ignore\n\t */\n\tisFull(vertex) {\n\t\treturn this.vertexCount + vertex >= this.maxVertex;\n\t}\n\n\t/**\n\t * push a new vertex to the buffer\n\t * @param {number} x - x position\n\t * @param {number} y - y position\n\t * @param {number} z - z position (per-renderable depth; 0 under ortho)\n\t * @param {number} u - texture U coordinate (or aNormal.x for primitives)\n\t * @param {number} v - texture V coordinate (or aNormal.y for primitives)\n\t * @param {number} tint - tint color in UINT32 (argb) format\n\t * @param {number} [textureId] - texture unit index for multi-texture batching\n\t * @param {number} [normalTextureId] - paired normal-map texture unit index, or `-1` for unlit quads\n\t * @ignore\n\t */\n\tpush(x, y, z, u, v, tint, textureId, normalTextureId) {\n\t\tconst offset = this.vertexCount * this.vertexSize;\n\n\t\tthis.bufferF32[offset] = x;\n\t\tthis.bufferF32[offset + 1] = y;\n\t\tthis.bufferF32[offset + 2] = z;\n\t\tthis.bufferF32[offset + 3] = u;\n\t\tthis.bufferF32[offset + 4] = v;\n\t\tthis.bufferU32[offset + 5] = tint;\n\t\tif (this.vertexSize > 6) {\n\t\t\tthis.bufferF32[offset + 6] = textureId || 0;\n\t\t\tif (this.vertexSize > 7) {\n\t\t\t\t// `aNormalTextureId`: -1 (sentinel for unlit) is the safe\n\t\t\t\t// default when the caller doesn't supply one. Writing 0\n\t\t\t\t// here would let the fragment shader's lit path activate\n\t\t\t\t// on unlit quads with whatever normal-map happened to be\n\t\t\t\t// bound at unit 0 — visible as garbage hemispheric\n\t\t\t\t// shading on every sprite.\n\t\t\t\tthis.bufferF32[offset + 7] =\n\t\t\t\t\ttypeof normalTextureId === \"number\" ? normalTextureId : -1;\n\t\t\t}\n\t\t}\n\n\t\tthis.vertexCount++;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * push a new vertex with all-float data to the buffer\n\t * @param {ArrayLike<number>} data - float values for one vertex\n\t * @param {number} srcOffset - start index in the source data\n\t * @param {number} count - number of floats to copy (should equal vertexSize)\n\t * @ignore\n\t */\n\tpushFloats(data, srcOffset, count) {\n\t\tconst offset = this.vertexCount * this.vertexSize;\n\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tthis.bufferF32[offset + i] = data[srcOffset + i];\n\t\t}\n\n\t\tthis.vertexCount++;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * push a new vertex to the buffer (mesh format: x, y, z, u, v, tint).\n\t * Color is unpacked from the ARGB Uint32 input into 4 normalized\n\t * `[0, 1]` floats — the mesh batcher's `aColor` attribute is\n\t * `FLOAT × 4` rather than `UNSIGNED_BYTE × 4` to avoid NaN-pattern\n\t * bit values getting canonicalized on Metal-backed WebGL drivers.\n\t * See `mesh_batcher.init` for the rationale.\n\t * @ignore\n\t */\n\tpushMesh(x, y, z, u, v, tint) {\n\t\tconst offset = this.vertexCount * this.vertexSize;\n\n\t\tthis.bufferF32[offset] = x;\n\t\tthis.bufferF32[offset + 1] = y;\n\t\tthis.bufferF32[offset + 2] = z;\n\t\tthis.bufferF32[offset + 3] = u;\n\t\tthis.bufferF32[offset + 4] = v;\n\t\tthis.bufferF32[offset + 5] = ((tint >> 16) & 0xff) / 255; // R\n\t\tthis.bufferF32[offset + 6] = ((tint >> 8) & 0xff) / 255; // G\n\t\tthis.bufferF32[offset + 7] = (tint & 0xff) / 255; // B\n\t\tthis.bufferF32[offset + 8] = ((tint >>> 24) & 0xff) / 255; // A\n\n\t\tthis.vertexCount++;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * push a new lit-mesh vertex (mesh format + a world-space normal):\n\t * x, y, z, u, v, tint, nx, ny, nz. Used by the lit mesh batcher, whose\n\t * vertex layout is 12 floats (the 9 of {@link pushMesh} plus `aNormal`).\n\t * @ignore\n\t */\n\tpushMeshLit(x, y, z, u, v, tint, nx, ny, nz) {\n\t\tconst offset = this.vertexCount * this.vertexSize;\n\n\t\tthis.bufferF32[offset] = x;\n\t\tthis.bufferF32[offset + 1] = y;\n\t\tthis.bufferF32[offset + 2] = z;\n\t\tthis.bufferF32[offset + 3] = u;\n\t\tthis.bufferF32[offset + 4] = v;\n\t\tthis.bufferF32[offset + 5] = ((tint >> 16) & 0xff) / 255; // R\n\t\tthis.bufferF32[offset + 6] = ((tint >> 8) & 0xff) / 255; // G\n\t\tthis.bufferF32[offset + 7] = (tint & 0xff) / 255; // B\n\t\tthis.bufferF32[offset + 8] = ((tint >>> 24) & 0xff) / 255; // A\n\t\t// world-space normal (aNormal)\n\t\tthis.bufferF32[offset + 9] = nx;\n\t\tthis.bufferF32[offset + 10] = ny;\n\t\tthis.bufferF32[offset + 11] = nz;\n\n\t\tthis.vertexCount++;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * return a reference to the data in Float32 format\n\t * @ignore\n\t */\n\ttoFloat32(begin, end) {\n\t\tif (typeof end !== \"undefined\") {\n\t\t\treturn this.bufferF32.subarray(begin, end);\n\t\t} else {\n\t\t\treturn this.bufferF32;\n\t\t}\n\t}\n\n\t/**\n\t * return a reference to the data in Uint32 format\n\t * @ignore\n\t */\n\ttoUint32(begin, end) {\n\t\tif (typeof end !== \"undefined\") {\n\t\t\treturn this.bufferU32.subarray(begin, end);\n\t\t} else {\n\t\t\treturn this.bufferU32;\n\t\t}\n\t}\n\n\t/**\n\t * return a byte-view reference to the data — use this for `gl.bufferData`\n\t * uploads to avoid the NaN-canonicalization that some drivers perform\n\t * on Float32 uploads of the same backing buffer (see the constructor's\n\t * `bufferU8` comment).\n\t * @ignore\n\t */\n\ttoUint8(byteBegin, byteEnd) {\n\t\tif (typeof byteEnd !== \"undefined\") {\n\t\t\treturn this.bufferU8.subarray(byteBegin, byteEnd);\n\t\t} else {\n\t\t\treturn this.bufferU8;\n\t\t}\n\t}\n}\n","/**\n * additional import for TypeScript\n * @import Renderer from \"../renderer.js\";\n */\n\n/**\n * Default maximum number of vertices per batch.\n * At 4096 vertices (1024 quads), the vertex buffer is ~80 KB (5 floats × 4 bytes × 4096),\n * which balances draw call reduction with safe buffer upload sizes on mobile tile-based GPUs.\n * Within the Uint16 index limit (65,535) — a deliberate capacity choice\n * (smaller index uploads), not an API constraint.\n * @ignore\n */\nexport const DEFAULT_MAX_VERTICES = 4096;\n\n/**\n * Backend-neutral base class for renderer batchers.\n *\n * A batcher accumulates vertices into a CPU staging buffer and turns them\n * into GPU work in batches — one draw call for many shapes/sprites. This\n * class defines the lifecycle contract shared by every backend:\n *\n * - `init(renderer, settings)` — build (or rebuild after a context/device\n *   loss) the batcher's GPU-facing state; called by the constructor\n * - `bind()` / `unbind()` — the renderer switches the active batcher\n * - `flush()` — turn the pending vertices into GPU work\n * - `reset()` — drop transient state (game reset / context restore)\n * - `destroy()` — release every GPU resource the batcher owns\n *\n * Custom batchers extend the class matching the renderer they target —\n * {@link WebGLBatcher} for the WebGL renderer, `WebGPUBatcher` for the\n * WebGPU renderer — and are registered through `renderer.addBatcher()`,\n * which accepts any Batcher subclass.\n * @category Rendering\n */\nexport class Batcher {\n\t// no base constructor on purpose: the backend base classes call\n\t// `this.init(renderer, settings)` from their own constructor, AFTER\n\t// `super()` — a base-constructor call would run before the derived\n\t// class's private fields are installed and throw on first access\n\n\t/**\n\t * Initialize (or re-initialize after a context/device loss) this batcher.\n\t * Derived classes override this with their full `(renderer, settings)`\n\t * signature and own the whole body; the base implementation only stores\n\t * the renderer reference.\n\t * @param {Renderer} renderer - the owning renderer\n\t */\n\tinit(renderer) {\n\t\t/**\n\t\t * the renderer this batcher is bound to\n\t\t * @type {Renderer}\n\t\t */\n\t\tthis.renderer = renderer;\n\t}\n\n\t/**\n\t * called by the renderer when this batcher becomes the current one\n\t */\n\tbind() {}\n\n\t/**\n\t * called by the renderer when this batcher is being replaced by another\n\t */\n\tunbind() {}\n\n\t/**\n\t * turn the pending vertices into GPU work\n\t */\n\tflush() {}\n\n\t/**\n\t * reset the batcher's transient state (game reset / context restore)\n\t */\n\treset() {}\n\n\t/**\n\t * release every GPU resource this batcher owns\n\t */\n\tdestroy() {}\n}\n\nexport default Batcher;\n","/**\n * Backend-neutral vertex attribute formats.\n *\n * A vertex format names a component type and a component count in one token:\n * `\"float32x3\"` is three 32-bit floats, `\"unorm8x4\"` is four unsigned bytes\n * normalized to `[0, 1]`. That is everything a backend needs to interpret a\n * slice of an interleaved vertex buffer, which is why it replaces the\n * `size` + `type` + `normalized` triple the WebGL API works in.\n *\n * Naming follows the GPU vocabulary rather than the GL one, because the\n * translation only runs one way: `\"unorm8x4\"` gives you `4` +\n * `UNSIGNED_BYTE` + `normalized`, whereas `UNSIGNED_BYTE` alone cannot tell\n * you the component count or whether it is normalized. Declaring the format\n * also means a layout no longer needs a live rendering context to be written\n * down — the GL enums are only resolved when a backend consumes it.\n *\n * This module holds no GL references at all, so a non-WebGL backend can\n * import it without pulling in the WebGL layer.\n * @see {@link Batcher#addAttribute}\n */\n\n/**\n * The scalar component type underlying a {@link VertexFormat}.\n * @ignore\n */\nexport type VertexScalar =\n\t| \"float32\"\n\t| \"uint8\"\n\t| \"sint8\"\n\t| \"unorm8\"\n\t| \"snorm8\"\n\t| \"uint16\"\n\t| \"sint16\"\n\t| \"unorm16\"\n\t| \"snorm16\"\n\t| \"uint32\"\n\t| \"sint32\";\n\n/**\n * A vertex attribute format: component type and count.\n *\n * The set is deliberately narrower than the full GPU vocabulary — see the\n * module notes for what is excluded and why. Layouts that cannot be spelled\n * here (three-component 8- and 16-bit types, for instance) remain expressible\n * with the {@link Batcher#addAttribute} GL-enum form, which is supported\n * indefinitely.\n */\nexport type VertexFormat =\n\t// 32-bit float\n\t| \"float32\"\n\t| \"float32x2\"\n\t| \"float32x3\"\n\t| \"float32x4\"\n\t// 8-bit integer\n\t| \"uint8\"\n\t| \"uint8x2\"\n\t| \"uint8x4\"\n\t| \"sint8\"\n\t| \"sint8x2\"\n\t| \"sint8x4\"\n\t// 8-bit normalized\n\t| \"unorm8\"\n\t| \"unorm8x2\"\n\t| \"unorm8x4\"\n\t| \"snorm8\"\n\t| \"snorm8x2\"\n\t| \"snorm8x4\"\n\t// 16-bit integer\n\t| \"uint16\"\n\t| \"uint16x2\"\n\t| \"uint16x4\"\n\t| \"sint16\"\n\t| \"sint16x2\"\n\t| \"sint16x4\"\n\t// 16-bit normalized\n\t| \"unorm16\"\n\t| \"unorm16x2\"\n\t| \"unorm16x4\"\n\t| \"snorm16\"\n\t| \"snorm16x2\"\n\t| \"snorm16x4\"\n\t// 32-bit integer\n\t| \"uint32\"\n\t| \"uint32x2\"\n\t| \"uint32x3\"\n\t| \"uint32x4\"\n\t| \"sint32\"\n\t| \"sint32x2\"\n\t| \"sint32x3\"\n\t| \"sint32x4\";\n\n/**\n * What a {@link VertexFormat} resolves to.\n * @ignore\n */\nexport interface VertexFormatInfo {\n\t/** number of components (1, 2, 3 or 4) */\n\tcomponents: 1 | 2 | 3 | 4;\n\t/** total size of the attribute in bytes */\n\tbytes: number;\n\t/** whether integer values are scaled into `[0, 1]` / `[-1, 1]` when read by a shader */\n\tnormalized: boolean;\n\t/** the underlying scalar type */\n\tscalar: VertexScalar;\n}\n\n/**\n * Bytes occupied by one component of each scalar type.\n * @ignore\n */\nconst SCALAR_BYTES: Record<VertexScalar, number> = {\n\tfloat32: 4,\n\tuint8: 1,\n\tsint8: 1,\n\tunorm8: 1,\n\tsnorm8: 1,\n\tuint16: 2,\n\tsint16: 2,\n\tunorm16: 2,\n\tsnorm16: 2,\n\tuint32: 4,\n\tsint32: 4,\n};\n\n/**\n * Whether a scalar type is read as a normalized fraction.\n * @ignore\n */\nconst SCALAR_NORMALIZED: Record<VertexScalar, boolean> = {\n\tfloat32: false,\n\tuint8: false,\n\tsint8: false,\n\tunorm8: true,\n\tsnorm8: true,\n\tuint16: false,\n\tsint16: false,\n\tunorm16: true,\n\tsnorm16: true,\n\tuint32: false,\n\tsint32: false,\n};\n\n/**\n * Build one table entry, keeping `bytes` derived rather than transcribed so a\n * typo cannot silently change a stride.\n * @param scalar - the component type\n * @param components - how many components the attribute has\n * @returns the resolved format description\n * @ignore\n */\nfunction entry(\n\tscalar: VertexScalar,\n\tcomponents: 1 | 2 | 3 | 4,\n): VertexFormatInfo {\n\treturn {\n\t\tcomponents,\n\t\tbytes: components * SCALAR_BYTES[scalar],\n\t\tnormalized: SCALAR_NORMALIZED[scalar],\n\t\tscalar,\n\t};\n}\n\n/**\n * Every supported format, resolved.\n *\n * `bytes` is computed as `components × scalar size`, which is the same\n * arithmetic the GL-enum path has always used — so a layout expressed either\n * way produces an identical stride.\n * @ignore\n */\nexport const VERTEX_FORMATS: Record<VertexFormat, VertexFormatInfo> = {\n\tfloat32: entry(\"float32\", 1),\n\tfloat32x2: entry(\"float32\", 2),\n\tfloat32x3: entry(\"float32\", 3),\n\tfloat32x4: entry(\"float32\", 4),\n\n\tuint8: entry(\"uint8\", 1),\n\tuint8x2: entry(\"uint8\", 2),\n\tuint8x4: entry(\"uint8\", 4),\n\tsint8: entry(\"sint8\", 1),\n\tsint8x2: entry(\"sint8\", 2),\n\tsint8x4: entry(\"sint8\", 4),\n\n\tunorm8: entry(\"unorm8\", 1),\n\tunorm8x2: entry(\"unorm8\", 2),\n\tunorm8x4: entry(\"unorm8\", 4),\n\tsnorm8: entry(\"snorm8\", 1),\n\tsnorm8x2: entry(\"snorm8\", 2),\n\tsnorm8x4: entry(\"snorm8\", 4),\n\n\tuint16: entry(\"uint16\", 1),\n\tuint16x2: entry(\"uint16\", 2),\n\tuint16x4: entry(\"uint16\", 4),\n\tsint16: entry(\"sint16\", 1),\n\tsint16x2: entry(\"sint16\", 2),\n\tsint16x4: entry(\"sint16\", 4),\n\n\tunorm16: entry(\"unorm16\", 1),\n\tunorm16x2: entry(\"unorm16\", 2),\n\tunorm16x4: entry(\"unorm16\", 4),\n\tsnorm16: entry(\"snorm16\", 1),\n\tsnorm16x2: entry(\"snorm16\", 2),\n\tsnorm16x4: entry(\"snorm16\", 4),\n\n\tuint32: entry(\"uint32\", 1),\n\tuint32x2: entry(\"uint32\", 2),\n\tuint32x3: entry(\"uint32\", 3),\n\tuint32x4: entry(\"uint32\", 4),\n\tsint32: entry(\"sint32\", 1),\n\tsint32x2: entry(\"sint32\", 2),\n\tsint32x3: entry(\"sint32\", 3),\n\tsint32x4: entry(\"sint32\", 4),\n};\n\n/**\n * Whether a value is a supported vertex format name.\n * @param value - the value to test\n * @returns `true` when `value` names a format in {@link VERTEX_FORMATS}\n */\nexport function isVertexFormat(value: unknown): value is VertexFormat {\n\treturn typeof value === \"string\" && Object.hasOwn(VERTEX_FORMATS, value);\n}\n\n/**\n * Resolve a vertex format to its component count, size and normalization.\n * @param format - the format to resolve\n * @returns the resolved description\n * @throws {Error} when `format` is not a supported format name\n */\nexport function resolveVertexFormat(format: VertexFormat): VertexFormatInfo {\n\tif (!isVertexFormat(format)) {\n\t\tthrow new Error(\n\t\t\t`Unknown vertex format \"${String(format)}\". Supported formats: ${Object.keys(\n\t\t\t\tVERTEX_FORMATS,\n\t\t\t).join(\", \")}`,\n\t\t);\n\t}\n\treturn VERTEX_FORMATS[format];\n}\n","/**\n * A renderer-agnostic index buffer.\n * Manages a typed array of vertex indices for indexed drawing.\n * {@link WebGLIndexBuffer} extends this with GL-specific bind/upload operations.\n * @ignore\n */\n\nexport default class IndexBuffer {\n\t/**\n\t * @param {number} maxIndices - maximum number of indices this buffer can hold\n\t * @param {boolean} [useUint32=false] - use Uint32 indices instead of Uint16\n\t */\n\tconstructor(maxIndices, useUint32 = false) {\n\t\tthis.data = useUint32\n\t\t\t? new Uint32Array(maxIndices)\n\t\t\t: new Uint16Array(maxIndices);\n\n\t\t/**\n\t\t * the current number of indices in the buffer\n\t\t * @type {number}\n\t\t */\n\t\tthis.length = 0;\n\t}\n\n\t/**\n\t * Fill the buffer with a repeating quad index pattern [0,1,2, 2,1,3, 4,5,6, ...]\n\t * @param {number} maxQuads - number of quads to generate indices for\n\t */\n\tfillQuadPattern(maxQuads) {\n\t\tfor (let i = 0, vertex = 0; i < maxQuads * 6; i += 6, vertex += 4) {\n\t\t\tthis.data[i] = vertex;\n\t\t\tthis.data[i + 1] = vertex + 1;\n\t\t\tthis.data[i + 2] = vertex + 2;\n\t\t\tthis.data[i + 3] = vertex + 2;\n\t\t\tthis.data[i + 4] = vertex + 1;\n\t\t\tthis.data[i + 5] = vertex + 3;\n\t\t}\n\t\tthis.length = maxQuads * 6;\n\t}\n\n\t/**\n\t * Reset the index count\n\t */\n\tclear() {\n\t\tthis.length = 0;\n\t}\n\n\t/**\n\t * Add indices to the buffer, rebased by the given vertex offset\n\t * @param {number[]} indices - source indices to add\n\t * @param {number} vertexOffset - value to add to each index\n\t */\n\tadd(indices, vertexOffset) {\n\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\tthis.data[this.length + i] = indices[i] + vertexOffset;\n\t\t}\n\t\tthis.length += indices.length;\n\t}\n\n\t/**\n\t * Add pre-computed absolute indices to the buffer (no rebasing)\n\t * @param {number[]} indices - absolute index values to add\n\t */\n\taddRaw(indices) {\n\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\tthis.data[this.length + i] = indices[i];\n\t\t}\n\t\tthis.length += indices.length;\n\t}\n}\n","import IndexBuffer from \"../../buffer/index.js\";\n\n/**\n * A WebGL Index Buffer — extends {@link IndexBuffer} with GL buffer binding and upload.\n * @ignore\n */\n\nexport default class WebGLIndexBuffer extends IndexBuffer {\n\t/**\n\t * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - the WebGL context\n\t * @param {number} maxIndices - maximum number of indices this buffer can hold\n\t * @param {boolean} [useUint32=false] - use Uint32 indices instead of Uint16 (a capacity knob — Uint16 keeps index uploads half the size)\n\t */\n\tconstructor(gl, maxIndices, useUint32 = false) {\n\t\tsuper(maxIndices, useUint32);\n\t\tthis.gl = gl;\n\t\tthis.type = useUint32 ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;\n\t\tthis.buffer = gl.createBuffer();\n\t}\n\n\t/**\n\t * Fill the buffer with a repeating quad index pattern and upload as static.\n\t * @param {number} maxQuads - number of quads to generate indices for\n\t */\n\tfillQuadPattern(maxQuads) {\n\t\tsuper.fillQuadPattern(maxQuads);\n\t\tthis.bind();\n\t\tthis.gl.bufferData(\n\t\t\tthis.gl.ELEMENT_ARRAY_BUFFER,\n\t\t\tthis.data,\n\t\t\tthis.gl.STATIC_DRAW,\n\t\t);\n\t}\n\n\t/**\n\t * Upload the current index data to the GPU (for dynamic buffers)\n\t */\n\tupload() {\n\t\tthis.bind();\n\t\tthis.gl.bufferData(\n\t\t\tthis.gl.ELEMENT_ARRAY_BUFFER,\n\t\t\tthis.data.subarray(0, this.length),\n\t\t\tthis.gl.STREAM_DRAW,\n\t\t);\n\t}\n\n\t/**\n\t * Bind this index buffer\n\t */\n\tbind() {\n\t\tthis.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.buffer);\n\t}\n\n\t/**\n\t * Replace the underlying GL buffer with a fresh one, deleting the old.\n\t * Needed on context restore — the old buffer object belongs to the lost\n\t * context and every upload through it would silently fail. Also keeps\n\t * plain resets leak-free (`deleteBuffer` on a lost-context object is a\n\t * harmless no-op).\n\t */\n\trecreate() {\n\t\tthis.gl.deleteBuffer(this.buffer);\n\t\tthis.buffer = this.gl.createBuffer();\n\t\tthis.length = 0;\n\t}\n\n\t/**\n\t * Delete the underlying GL buffer. The index buffer must not be used\n\t * after calling destroy.\n\t */\n\tdestroy() {\n\t\tthis.gl.deleteBuffer(this.buffer);\n\t\tthis.buffer = null;\n\t}\n}\n","/**\n * Normalize a descriptor into the list-of-buffer-layouts form.\n *\n * The single-buffer shape (`attributes` + `stride` + `buffer`) is the\n * original one and remains the common case, so it is accepted verbatim and\n * widened here into a one-entry list rather than pushed onto every caller.\n * A descriptor that declares `buffers` is used as-is.\n * @param {object} descriptor - the descriptor to read\n * @returns {object[]} buffer layout groups\n * @ignore\n */\nfunction bufferGroups(descriptor) {\n\tif (Array.isArray(descriptor.buffers)) {\n\t\treturn descriptor.buffers;\n\t}\n\treturn [\n\t\t{\n\t\t\tbuffer: descriptor.buffer,\n\t\t\tstride: descriptor.stride,\n\t\t\tstepMode: \"vertex\",\n\t\t\tattributes: descriptor.attributes,\n\t\t},\n\t];\n}\n\n/**\n * A WebGL Vertex State — a Vertex Array Object owning a frozen vertex\n * buffer layout: one `vertexAttribPointer` / `enableVertexAttribArray` per\n * attribute against a given vertex buffer, plus the ELEMENT_ARRAY_BUFFER\n * binding when the geometry is indexed.\n *\n * This is the engine's `GPUVertexState` analogue, and the layout it is built\n * from is a `GPUVertexBufferLayout[]`: a list of buffer groups, each with its\n * own buffer, `arrayStride` and `stepMode`. Attributes in a `\"vertex\"` group\n * advance once per vertex (the ordinary case); attributes in an\n * `\"instance\"` group advance once per *instance*, which is what\n * `drawElementsInstanced` reads to stamp out N copies of one geometry.\n * The layout is immutable once built, exactly like a vertex layout baked\n * into a render pipeline.\n *\n * Attribute divisors are vertex-array state, so a fresh VAO starts with every\n * divisor at 0 and `build()` creates one each time — an instanced layout can\n * therefore never leak its divisors into an unrelated vertex state, and the\n * single-buffer path issues no `vertexAttribDivisor` calls at all.\n *\n * Every method that mutates GL binding state saves and restores the live\n * bindings itself, so building or rebuilding one vertex state can never\n * disturb whichever one is mid-frame. Callers never touch\n * `bindVertexArray` directly.\n * @ignore\n */\nexport default class WebGLVertexState {\n\t/**\n\t * Accepts either shape — the single-buffer one (`attributes` + `stride` +\n\t * `buffer`) or a list of buffer groups (`buffers`). They are equivalent;\n\t * the former is the latter with one `\"vertex\"` group.\n\t * @param {WebGL2RenderingContext} gl - the WebGL context\n\t * @param {object} descriptor - the vertex layout to realize\n\t * @param {object[]} [descriptor.attributes] - attribute definitions (`name`, `size`, `type`, `normalized`, `offset`)\n\t * @param {number} [descriptor.stride] - size of a single vertex in bytes (`arrayStride`)\n\t * @param {WebGLBuffer} [descriptor.buffer] - the vertex buffer the attribute pointers read from\n\t * @param {object[]} [descriptor.buffers] - buffer groups, each `{buffer, stride, attributes, stepMode}` where `stepMode` is `\"vertex\"` (default) or `\"instance\"`\n\t * @param {Function} descriptor.resolveLocation - maps an attribute name to its shader location (`-1` when absent)\n\t * @param {WebGLIndexBuffer} [descriptor.indexBuffer] - index buffer to capture, for indexed geometry\n\t */\n\tconstructor(gl, descriptor) {\n\t\tthis.gl = gl;\n\t\tthis.descriptor = descriptor;\n\t\t/**\n\t\t * the underlying GL vertex array object\n\t\t * @type {WebGLVertexArrayObject}\n\t\t */\n\t\tthis.handle = null;\n\t\tthis.build();\n\t}\n\n\t/**\n\t * Snapshot the live vertex-array / array-buffer bindings. Pure GL\n\t * bookkeeping — a WebGPU backend builds immutable descriptors and has\n\t * no global binding points to disturb.\n\t * @ignore\n\t */\n\t#captureBindings() {\n\t\tconst gl = this.gl;\n\t\treturn {\n\t\t\tvertexArray: gl.getParameter(gl.VERTEX_ARRAY_BINDING),\n\t\t\tarrayBuffer: gl.getParameter(gl.ARRAY_BUFFER_BINDING),\n\t\t};\n\t}\n\n\t/** @ignore */\n\t#restoreBindings(saved) {\n\t\tconst gl = this.gl;\n\t\tgl.bindVertexArray(saved.vertexArray);\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, saved.arrayBuffer);\n\t}\n\n\t/**\n\t * (Re)build the vertex array object from this state's descriptor,\n\t * replacing any previous one. Called on construction and from every\n\t * buffer-recreation path — a vertex state referencing a deleted buffer\n\t * keeps it alive per the GL spec and draws stale data.\n\t * @param {object} [changes] - descriptor fields to replace first (e.g. recreated `buffer` / `buffers` / `indexBuffer`)\n\t */\n\tbuild(changes) {\n\t\tconst gl = this.gl;\n\t\tif (changes !== undefined) {\n\t\t\t// the two descriptor shapes are alternatives, not a union: leaving\n\t\t\t// a stale `buffers` in place while `buffer` is replaced would keep\n\t\t\t// drawing from the old buffer, silently\n\t\t\tif (changes.buffers !== undefined) {\n\t\t\t\tthis.descriptor.buffer = undefined;\n\t\t\t\tthis.descriptor.attributes = undefined;\n\t\t\t\tthis.descriptor.stride = undefined;\n\t\t\t} else if (changes.buffer !== undefined) {\n\t\t\t\tthis.descriptor.buffers = undefined;\n\t\t\t}\n\t\t\tObject.assign(this.descriptor, changes);\n\t\t}\n\t\tconst { indexBuffer, resolveLocation } = this.descriptor;\n\n\t\tconst saved = this.#captureBindings();\n\t\tconst replaced = this.handle;\n\t\tthis.release();\n\n\t\tthis.handle = gl.createVertexArray();\n\t\tgl.bindVertexArray(this.handle);\n\n\t\tfor (const group of bufferGroups(this.descriptor)) {\n\t\t\t// ARRAY_BUFFER is NOT vertex-array state, but each pointer record\n\t\t\t// captures whichever buffer is bound at the time it is issued —\n\t\t\t// which is exactly how one vertex array reads several buffers\n\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, group.buffer);\n\t\t\tconst perInstance = group.stepMode === \"instance\";\n\t\t\tfor (const attr of group.attributes) {\n\t\t\t\tconst location = resolveLocation(attr.name);\n\t\t\t\tif (location === -1) {\n\t\t\t\t\t// on a lost context every location is -1 (the shader never\n\t\t\t\t\t// compiled); that is expected and rebuilt on restore\n\t\t\t\t\tif (!gl.isContextLost()) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`melonJS: vertex attribute \"${attr.name}\" not found in the shader — skipped in the vertex state`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tgl.enableVertexAttribArray(location);\n\t\t\t\tgl.vertexAttribPointer(\n\t\t\t\t\tlocation,\n\t\t\t\t\tattr.size,\n\t\t\t\t\tattr.type,\n\t\t\t\t\tattr.normalized,\n\t\t\t\t\tgroup.stride,\n\t\t\t\t\tattr.offset,\n\t\t\t\t);\n\t\t\t\tif (perInstance) {\n\t\t\t\t\t// advance once per instance rather than per vertex. Only\n\t\t\t\t\t// issued for instance groups: a fresh vertex array already\n\t\t\t\t\t// has every divisor at 0, so the ordinary path stays free\n\t\t\t\t\t// of these calls entirely.\n\t\t\t\t\tgl.vertexAttribDivisor(location, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (indexBuffer) {\n\t\t\tindexBuffer.bind();\n\t\t}\n\n\t\tif (saved.vertexArray === replaced && replaced !== null) {\n\t\t\t// this state was the bound one when it was asked to rebuild, and\n\t\t\t// `release()` has since deleted that name. Re-binding a deleted\n\t\t\t// vertex array raises INVALID_OPERATION and leaves nothing bound,\n\t\t\t// so hand back the replacement instead — the caller asked for a\n\t\t\t// rebuild, not for its vertex array to go away.\n\t\t\tsaved.vertexArray = this.handle;\n\t\t}\n\t\tthis.#restoreBindings(saved);\n\t}\n\n\t/**\n\t * Capture an ELEMENT_ARRAY_BUFFER binding into this vertex state.\n\t * Used when an index buffer is (re)created after the state was built —\n\t * the quad batchers' static pattern buffer — since that binding is\n\t * vertex-array state and would otherwise land in whichever state\n\t * happens to be bound.\n\t * @param {WebGLIndexBuffer} indexBuffer - the index buffer to capture\n\t * @param {Function} [fill] - optional work to run with this state bound (buffer creation/upload)\n\t */\n\tcaptureIndexBuffer(indexBuffer, fill) {\n\t\tconst gl = this.gl;\n\t\tconst saved = this.#captureBindings();\n\t\tgl.bindVertexArray(this.handle);\n\t\tif (typeof fill === \"function\") {\n\t\t\tfill();\n\t\t}\n\t\tthis.descriptor.indexBuffer = indexBuffer ?? this.descriptor.indexBuffer;\n\t\tthis.descriptor.indexBuffer?.bind();\n\t\tthis.#restoreBindings(saved);\n\t}\n\n\t/**\n\t * Make this vertex state current. One call restores the entire frozen\n\t * layout — the WebGL analogue of `setPipeline` + `setVertexBuffer` +\n\t * `setIndexBuffer`.\n\t */\n\tbind() {\n\t\tthis.gl.bindVertexArray(this.handle);\n\t}\n\n\t/**\n\t * Delete the underlying vertex array object, if any. Idempotent, and\n\t * safe on a lost context: deleting an object belonging to a lost\n\t * context would queue `INVALID_OPERATION` (deletion counts as *using*\n\t * it — only the `is*` queries are exempt), so probe first.\n\t */\n\trelease() {\n\t\tif (this.handle !== null) {\n\t\t\tif (this.gl.isVertexArray(this.handle)) {\n\t\t\t\tthis.gl.deleteVertexArray(this.handle);\n\t\t\t}\n\t\t\tthis.handle = null;\n\t\t}\n\t}\n\n\t/**\n\t * Release the vertex array object. The vertex state must not be used\n\t * after calling destroy.\n\t */\n\tdestroy() {\n\t\tthis.release();\n\t\tthis.descriptor = null;\n\t}\n}\n","/**\n * Backend-neutral primitive topologies.\n *\n * Names how a stream of vertices is assembled into primitives, in the GPU\n * vocabulary rather than the GL one. Like {@link VertexFormat}, this lets a\n * draw be described without a live rendering context, and lets a backend read\n * the description directly instead of translating enums.\n *\n * This module holds no GL references, so a non-WebGL backend can import it\n * without pulling in the WebGL layer.\n */\n\n/**\n * How vertices are assembled into primitives.\n *\n * Five of these are portable across backends. `\"line-loop\"` and\n * `\"triangle-fan\"` are **engine extensions** with no modern GPU-API\n * equivalent — they exist because the primitive batcher has always accepted\n * them, and a future backend will have to emulate them (a line loop is a line\n * strip plus a closing segment, which is already how the WebGL path draws it).\n * Prefer {@link PORTABLE_TOPOLOGIES} for anything that must survive a backend\n * change.\n */\nexport type Topology =\n\t| \"point-list\"\n\t| \"line-list\"\n\t| \"line-strip\"\n\t| \"line-loop\"\n\t| \"triangle-list\"\n\t| \"triangle-strip\"\n\t| \"triangle-fan\";\n\n/**\n * The topologies with a direct equivalent in modern GPU APIs.\n *\n * `\"line-loop\"` and `\"triangle-fan\"` are deliberately absent — see\n * {@link Topology}.\n */\nexport const PORTABLE_TOPOLOGIES = [\n\t\"point-list\",\n\t\"line-list\",\n\t\"line-strip\",\n\t\"triangle-list\",\n\t\"triangle-strip\",\n] as const satisfies readonly Topology[];\n\n/**\n * Every accepted topology name.\n * @ignore\n */\nconst TOPOLOGIES: readonly Topology[] = [\n\t\"point-list\",\n\t\"line-list\",\n\t\"line-strip\",\n\t\"line-loop\",\n\t\"triangle-list\",\n\t\"triangle-strip\",\n\t\"triangle-fan\",\n];\n\n/**\n * Whether a value is a supported topology name.\n * @param value - the value to test\n * @returns `true` when `value` names a topology\n */\nexport function isTopology(value: unknown): value is Topology {\n\treturn typeof value === \"string\" && TOPOLOGIES.includes(value as Topology);\n}\n\n/**\n * Whether a topology has a direct equivalent in modern GPU APIs.\n * @param topology - the topology to test\n * @returns `false` for the two engine extensions, which a non-WebGL backend must emulate\n */\nexport function isPortableTopology(topology: Topology): boolean {\n\treturn (PORTABLE_TOPOLOGIES as readonly Topology[]).includes(topology);\n}\n","import { isTopology } from \"../../gpu/topology.ts\";\n\n/**\n * Translate between the backend-neutral topology vocabulary and WebGL's\n * primitive-mode enums.\n * @ignore\n */\n\n/**\n * GL enum name for each topology. Indirection through the name keeps this\n * table context-free; the value is read off `gl` at call time.\n * @ignore\n */\nconst TOPOLOGY_TO_GL_NAME = {\n\t\"point-list\": \"POINTS\",\n\t\"line-list\": \"LINES\",\n\t\"line-strip\": \"LINE_STRIP\",\n\t\"line-loop\": \"LINE_LOOP\",\n\t\"triangle-list\": \"TRIANGLES\",\n\t\"triangle-strip\": \"TRIANGLE_STRIP\",\n\t\"triangle-fan\": \"TRIANGLE_FAN\",\n};\n\n/**\n * Reverse lookup, built once per context.\n * @type {WeakMap<WebGL2RenderingContext, Map<GLenum, string>>}\n * @ignore\n */\nconst reverseCache = new WeakMap();\n\n/**\n * Normalize a draw mode to its GL enum.\n *\n * Accepts either vocabulary and always returns a number, so callers can keep\n * comparing modes numerically. This matters more than it looks: a topology\n * string that reaches a GL draw call is coerced to `NaN` and then to `0`,\n * which *is* `POINTS` — so a missed translation does not throw, it silently\n * renders the batch as points.\n * @param {WebGL2RenderingContext} gl - the rendering context\n * @param {string|GLenum} value - a topology name or a GL primitive enum\n * @returns {GLenum} the GL primitive mode\n * @throws {Error} when `value` is neither a known topology nor a number\n * @ignore\n */\nexport function resolveTopology(gl, value) {\n\tif (typeof value === \"number\") {\n\t\t// Reject a number we cannot name. Passing anything through would let\n\t\t// NaN or a bogus enum reach the draw call, where it coerces to 0 —\n\t\t// POINTS — which is exactly the silent failure this module exists to\n\t\t// prevent. The seven topologies cover every GL primitive mode, so a\n\t\t// number with no name is not a valid draw mode.\n\t\tif (topologyFromGL(gl, value) === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown draw mode ${String(value)}. Expected a GL primitive mode ` +\n\t\t\t\t\t`or one of: ${Object.keys(TOPOLOGY_TO_GL_NAME).join(\", \")}`,\n\t\t\t);\n\t\t}\n\t\treturn value;\n\t}\n\tif (isTopology(value)) {\n\t\treturn gl[TOPOLOGY_TO_GL_NAME[value]];\n\t}\n\tthrow new Error(\n\t\t`Unknown draw topology \"${String(value)}\". Expected a GL primitive mode ` +\n\t\t\t`or one of: ${Object.keys(TOPOLOGY_TO_GL_NAME).join(\", \")}`,\n\t);\n}\n\n/**\n * Name a GL primitive mode in the neutral vocabulary.\n * @param {WebGL2RenderingContext} gl - the rendering context\n * @param {GLenum} mode - a GL primitive mode\n * @returns {string|undefined} the topology name, or `undefined` for an unknown mode\n * @ignore\n */\nexport function topologyFromGL(gl, mode) {\n\tlet reverse = reverseCache.get(gl);\n\tif (reverse === undefined) {\n\t\treverse = new Map();\n\t\tfor (const [topology, name] of Object.entries(TOPOLOGY_TO_GL_NAME)) {\n\t\t\treverse.set(gl[name], topology);\n\t\t}\n\t\treverseCache.set(gl, reverse);\n\t}\n\treturn reverse.get(mode);\n}\n","import { isVertexFormat, resolveVertexFormat } from \"../../gpu/vertexformat.ts\";\n\n/**\n * Translate between the backend-neutral vertex vocabulary and WebGL's\n * `size` + `type` + `normalized` triple.\n *\n * Everything here takes a live `gl` so the enum values come from the context\n * rather than from hard-coded constants — which keeps the neutral tier free\n * of GL references and matches how the rest of the WebGL layer is written.\n * @ignore\n */\n\n/**\n * The scalar type of each format, as a GL enum name. Indirection through the\n * name (rather than the value) is what lets this table exist without a\n * context: the value is looked up on `gl` at call time.\n * @ignore\n */\nconst SCALAR_TO_GL_NAME = {\n\tfloat32: \"FLOAT\",\n\tuint8: \"UNSIGNED_BYTE\",\n\tsint8: \"BYTE\",\n\tunorm8: \"UNSIGNED_BYTE\",\n\tsnorm8: \"BYTE\",\n\tuint16: \"UNSIGNED_SHORT\",\n\tsint16: \"SHORT\",\n\tunorm16: \"UNSIGNED_SHORT\",\n\tsnorm16: \"SHORT\",\n\tuint32: \"UNSIGNED_INT\",\n\tsint32: \"INT\",\n};\n\n/**\n * The GL enum a vertex format's components are read as.\n * @param {WebGL2RenderingContext} gl - the rendering context\n * @param {string} format - a supported vertex format name\n * @returns {GLenum} the matching component type\n * @throws {Error} when `format` is not a supported format name\n * @ignore\n */\nexport function glTypeForFormat(gl, format) {\n\tconst { scalar } = resolveVertexFormat(format);\n\treturn gl[SCALAR_TO_GL_NAME[scalar]];\n}\n\n/**\n * Name a WebGL attribute description in the neutral vocabulary.\n *\n * Returns `undefined` when the combination has no name — three-component 8-\n * and 16-bit types, for instance, which the GPU vocabulary simply does not\n * define. That absence is deliberate and meaningful: a made-up name would\n * travel into a backend that cannot honour it and fail there, far from the\n * layout that produced it. An attribute with no portable format is still\n * perfectly valid on the WebGL path.\n * @param {WebGL2RenderingContext} gl - the rendering context\n * @param {number} size - component count (1-4)\n * @param {GLenum} type - component type\n * @param {boolean} normalized - whether integers are scaled into `[0, 1]` / `[-1, 1]`\n * @returns {string|undefined} the format name, or `undefined` when none exists\n * @ignore\n */\nexport function formatFromGL(gl, size, type, normalized) {\n\t// `FLOAT` has no normalized spelling: WebGL accepts `normalized: true`\n\t// alongside it and ignores the flag, so the name is keyed off the type and\n\t// size alone. Reporting `undefined` for that legal combination would strip\n\t// a portable format from every float attribute that happens to set it.\n\tconst isFloat = type === gl.FLOAT;\n\tconst wants = isFloat ? false : normalized === true;\n\n\tlet scalar;\n\tswitch (type) {\n\t\tcase gl.FLOAT:\n\t\t\tscalar = \"float32\";\n\t\t\tbreak;\n\t\tcase gl.UNSIGNED_BYTE:\n\t\t\tscalar = wants ? \"unorm8\" : \"uint8\";\n\t\t\tbreak;\n\t\tcase gl.BYTE:\n\t\t\tscalar = wants ? \"snorm8\" : \"sint8\";\n\t\t\tbreak;\n\t\tcase gl.UNSIGNED_SHORT:\n\t\t\tscalar = wants ? \"unorm16\" : \"uint16\";\n\t\t\tbreak;\n\t\tcase gl.SHORT:\n\t\t\tscalar = wants ? \"snorm16\" : \"sint16\";\n\t\t\tbreak;\n\t\tcase gl.UNSIGNED_INT:\n\t\t\tscalar = \"uint32\";\n\t\t\tbreak;\n\t\tcase gl.INT:\n\t\t\tscalar = \"sint32\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n\n\tconst candidate = size === 1 ? scalar : `${scalar}x${size}`;\n\treturn isVertexFormat(candidate) ? candidate : undefined;\n}\n","import VertexArrayBuffer from \"../../buffer/vertex.js\";\nimport { Batcher, DEFAULT_MAX_VERTICES } from \"../../gpu/batcher.js\";\nimport { resolveVertexFormat } from \"../../gpu/vertexformat.ts\";\nimport WebGLIndexBuffer from \"../buffer/index.js\";\nimport WebGLVertexState from \"../buffer/vertexstate.js\";\nimport GLShader from \"../glshader.js\";\nimport { resolveTopology, topologyFromGL } from \"../utils/topology.js\";\nimport { formatFromGL, glTypeForFormat } from \"../utils/vertexformat.js\";\n\n/**\n * additional import for TypeScript\n * @import WebGLRenderer from \"./../webgl_renderer.js\";\n * @import {Matrix3d} from \"../../../math/matrix3d.ts\";\n * @import {VertexFormat} from \"../../gpu/vertexformat.ts\";\n * @import {Topology} from \"../../gpu/topology.ts\";\n */\n\n/**\n * The base WebGL Batcher — manages shader programs, vertex attribute\n * definitions, and vertex buffer batching for efficient GPU draw calls,\n * realizing the backend-neutral {@link Batcher} lifecycle on GL state.\n * Custom WebGL batchers extend this class.\n * @augments Batcher\n * @category Rendering\n */\nexport class WebGLBatcher extends Batcher {\n\t/**\n\t * Canonical topology; `#mode` is derived from it so the two cannot\n\t * disagree.\n\t * @type {Topology}\n\t * @ignore\n\t */\n\t#topology = \"triangle-list\";\n\n\t/**\n\t * The GL enum for `#topology`, precomputed so reads stay cheap.\n\t * @type {number}\n\t * @ignore\n\t */\n\t#mode = 4;\n\n\t/**\n\t * @param {WebGLRenderer} renderer - the current WebGL renderer session\n\t * @param {object} settings - additional settings to initialize this batcher (see {@link WebGLBatcher#init})\n\t */\n\tconstructor(renderer, settings) {\n\t\tsuper();\n\t\tthis.init(renderer, settings);\n\t}\n\n\t/**\n\t * Initialize the batcher (called by the constructor)\n\t * @param {WebGLRenderer} renderer - the current WebGL renderer session\n\t * @param {object} settings - additional settings to initialize this batcher\n\t * @param {object[]} settings.attributes - an array of attributes definition\n\t * @param {string} settings.attributes.name - name of the attribute in the vertex shader\n\t * @param {number} settings.attributes.size - number of components per vertex attribute. Must be 1, 2, 3, or 4.\n\t * @param {GLenum} settings.attributes.type - data type of each component in the array\n\t * @param {boolean} settings.attributes.normalized - whether integer data values should be normalized into a certain range when being cast to a float\n\t * @param {number} settings.attributes.offset - offset in bytes of the first component in the vertex attribute array\n\t * @param {object} settings.shader - shader definition\n\t * @param {string} settings.shader.vertex - a string containing the GLSL source code to set\n\t * @param {string} settings.shader.fragment - a string containing the GLSL source code to set\n\t * @param {number} [settings.maxVertices=4096] - the maximum number of vertices this batcher can hold\n\t * @param {boolean} [settings.indexed=false] - whether this batcher uses an index buffer for indexed drawing (drawElements)\n\t * @param {string} [settings.projectionUniform=\"uProjectionMatrix\"] - the name of the projection matrix uniform in the shader\n\t */\n\tinit(renderer, settings) {\n\t\t// the associated renderer\n\t\tthis.renderer = renderer;\n\n\t\t// WebGL context\n\t\tthis.gl = renderer.gl;\n\n\t\t// Global transformation matrix\n\t\tthis.viewMatrix = renderer.currentTransform;\n\n\t\t/**\n\t\t * the default shader created by this batcher\n\t\t * @type {GLShader}\n\t\t */\n\t\tthis.defaultShader = undefined;\n\n\t\t/**\n\t\t * the shader currently used by this batcher\n\t\t * @type {GLShader}\n\t\t */\n\t\tthis.currentShader = undefined;\n\n\t\t// goes through the setter below, which accepts either vocabulary\n\t\tthis.mode = this.gl.TRIANGLES;\n\n\t\t// re-init (context restore runs init() on the existing instance):\n\t\t// drop the previous life's vertex state, which also unfreezes the\n\t\t// layout so the attribute definitions below can be rebuilt\n\t\tif (this.vertexState) {\n\t\t\tthis.vertexState.destroy();\n\t\t\tthis.vertexState = null;\n\t\t}\n\n\t\t/**\n\t\t * an array of vertex attribute properties\n\t\t * @see WebGLBatcher.addAttribute\n\t\t * @type {Array.<Object>}\n\t\t */\n\t\tthis.attributes = [];\n\n\t\t/**\n\t\t * the stride of a single vertex in bytes\n\t\t * (will automatically be calculated as attributes definitions are added)\n\t\t * @see WebGLBatcher.addAttribute\n\t\t * @type {number}\n\t\t */\n\t\tthis.stride = 0;\n\n\t\t/**\n\t\t * the size of a single vertex in floats\n\t\t * (will automatically be calculated as attributes definitions are added)\n\t\t * @see WebGLBatcher.addAttribute\n\t\t * @type {number}\n\t\t */\n\t\tthis.vertexSize = 0;\n\n\t\t/**\n\t\t * the vertex data buffer used by this batcher\n\t\t * @type {VertexArrayBuffer}\n\t\t */\n\t\tthis.vertexData = null;\n\n\t\t// maximum number of vertices\n\t\tconst maxVertices =\n\t\t\t(settings && settings.maxVertices) || DEFAULT_MAX_VERTICES;\n\n\t\t// parse given attributes\n\t\tif (typeof settings !== \"undefined\" && Array.isArray(settings.attributes)) {\n\t\t\tsettings.attributes.forEach((attr) => {\n\t\t\t\t// pass the descriptor through untouched — `addAttribute` is the\n\t\t\t\t// single place either vocabulary is resolved\n\t\t\t\tthis.addAttribute(attr);\n\t\t\t});\n\t\t\t// `vertexSize` is a float count, so a stride that is not a whole\n\t\t\t// number of floats makes it fractional — and `VertexArrayBuffer`\n\t\t\t// computes write offsets as `vertexCount * vertexSize`, so every\n\t\t\t// write would land off-grid and be silently discarded. Only\n\t\t\t// reachable now that sub-4-byte formats can be spelled.\n\t\t\tif (this.stride % Float32Array.BYTES_PER_ELEMENT !== 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`WebGLBatcher: vertex stride must be a multiple of ${Float32Array.BYTES_PER_ELEMENT} bytes, got ${this.stride}. ` +\n\t\t\t\t\t\t\"Pad the layout (a 3-component byte attribute usually wants 4 components).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.vertexData = new VertexArrayBuffer(this.vertexSize, maxVertices);\n\t\t} else {\n\t\t\tthrow new Error(\"attributes definition missing\");\n\t\t}\n\n\t\t// parse and instantiate the default shader\n\t\tif (\n\t\t\ttypeof settings !== \"undefined\" &&\n\t\t\ttypeof settings.shader !== \"undefined\"\n\t\t) {\n\t\t\tthis.defaultShader = new GLShader(\n\t\t\t\tthis.gl,\n\t\t\t\tsettings.shader.vertex,\n\t\t\t\tsettings.shader.fragment,\n\t\t\t\tthis.renderer.shaderPrecision,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(\"shader definition missing\");\n\t\t}\n\n\t\t/**\n\t\t * the name of the projection matrix uniform in the shader\n\t\t * @type {string}\n\t\t */\n\t\tthis.projectionUniform = settings.projectionUniform || \"uProjectionMatrix\";\n\n\t\t/**\n\t\t * whether this batcher uses indexed drawing\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.useIndexBuffer = settings.indexed === true;\n\n\t\t/**\n\t\t * the GL vertex buffer object (own buffer for indexed batchers, null for shared)\n\t\t * @type {WebGLBuffer|null}\n\t\t * @ignore\n\t\t */\n\t\tthis.glVertexBuffer = null;\n\n\t\t/**\n\t\t * the dynamic index buffer (only for indexed batchers)\n\t\t * @type {WebGLIndexBuffer|null}\n\t\t * @ignore\n\t\t */\n\t\tthis.indexBuffer = null;\n\n\t\tif (this.useIndexBuffer) {\n\t\t\tconst gl = this.gl;\n\t\t\tthis.glVertexBuffer = gl.createBuffer();\n\t\t\t// max indices: worst case is 3 indices per vertex (all triangles, no sharing)\n\t\t\tthis.indexBuffer = new WebGLIndexBuffer(gl, maxVertices * 3, false);\n\t\t}\n\n\t\t// build the frozen vertex-state object (VAO) once all buffers exist;\n\t\t// quad-family batchers create their static index buffer AFTER this\n\t\t// (in their own init) and capture it via the wrap in\n\t\t// `createIndexBuffer`\n\t\tthis.createVertexState();\n\t}\n\n\t/**\n\t * The GL buffer this batcher uploads its vertex data into: its own\n\t * buffer for indexed (mesh-family) batchers, otherwise the renderer's\n\t * shared one.\n\t * @type {WebGLBuffer}\n\t * @ignore\n\t */\n\tget uploadBuffer() {\n\t\treturn this.glVertexBuffer ?? this.renderer.vertexBuffer;\n\t}\n\n\t/**\n\t * (Re)build this batcher's {@link WebGLVertexState} — the frozen vertex\n\t * buffer layout (`attributes` + `stride`) realized as a GL vertex array\n\t * object bound to this batcher's upload buffer and, for indexed\n\t * batchers, its index buffer.\n\t *\n\t * Every shader hosted by this batcher must declare a prefix of that\n\t * layout, in layout order (see {@link validateShaderLocations}) — the\n\t * locations are frozen here at the default shader's mapping.\n\t * @ignore\n\t */\n\tcreateVertexState() {\n\t\tconst descriptor = {\n\t\t\tattributes: this.attributes,\n\t\t\tstride: this.stride,\n\t\t\tbuffer: this.uploadBuffer,\n\t\t\tindexBuffer: this.useIndexBuffer ? this.indexBuffer : undefined,\n\t\t\tresolveLocation: (name) => {\n\t\t\t\treturn this.defaultShader.getAttribLocation(name);\n\t\t\t},\n\t\t};\n\t\tif (this.vertexState) {\n\t\t\t// keep the object identity across rebuilds; only the GL handle\n\t\t\t// and the buffers it references change\n\t\t\tthis.vertexState.build(descriptor);\n\t\t} else {\n\t\t\tthis.vertexState = new WebGLVertexState(this.gl, descriptor);\n\t\t}\n\t}\n\n\t/**\n\t * Release every GL object this batcher owns (vertex state, own vertex\n\t * and index buffers, default shader). Called by\n\t * {@link WebGLRenderer#destroy}; subclasses that override must chain to\n\t * `super.destroy()`.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tconst gl = this.gl;\n\t\tif (this.vertexState) {\n\t\t\tthis.vertexState.destroy();\n\t\t\tthis.vertexState = null;\n\t\t}\n\t\tif (this.glVertexBuffer) {\n\t\t\tgl.deleteBuffer(this.glVertexBuffer);\n\t\t\tthis.glVertexBuffer = null;\n\t\t}\n\t\tif (this.indexBuffer) {\n\t\t\tthis.indexBuffer.destroy();\n\t\t\tthis.indexBuffer = null;\n\t\t}\n\t\tif (this.defaultShader) {\n\t\t\tthis.defaultShader.destroy();\n\t\t\tthis.defaultShader = undefined;\n\t\t}\n\t\tthis.currentShader = undefined;\n\t}\n\n\t/**\n\t * Reset batcher internal state\n\t * @ignore\n\t */\n\treset() {\n\t\t// WebGL context\n\t\tthis.gl = this.renderer.gl;\n\n\t\t// clear the vertex data buffer\n\t\tthis.vertexData.clear();\n\n\t\tif (this.useIndexBuffer) {\n\t\t\t// re-create the GL buffers rather than just clearing counters: on\n\t\t\t// context restore the old buffer objects belong to the LOST\n\t\t\t// context, and every upload through them would silently fail\n\t\t\t// (mesh draws vanish). `deleteBuffer` on a lost-context object is\n\t\t\t// a harmless no-op, so plain resets stay leak-free too. The\n\t\t\t// quad-family batchers (non-indexed, static pattern) re-create\n\t\t\t// their own index buffer in their reset() override.\n\t\t\tconst gl = this.gl;\n\t\t\tgl.deleteBuffer(this.glVertexBuffer);\n\t\t\tthis.glVertexBuffer = gl.createBuffer();\n\t\t\tthis.indexBuffer.recreate();\n\t\t\t// the vertex state still references the DELETED buffers — a VAO\n\t\t\t// keeps deleted attachments alive per the GL spec and draws from\n\t\t\t// them (stale/dead data). Rebuild against the new buffers.\n\t\t\tthis.createVertexState();\n\t\t}\n\t}\n\n\t/**\n\t * called by the WebGL renderer when a batcher becomes the current one\n\t */\n\tbind() {\n\t\tconst gl = this.gl;\n\t\t// one bind restores the whole frozen vertex state (attribute\n\t\t// pointers + element buffer) — the WebGL analogue of setPipeline +\n\t\t// setVertexBuffer + setIndexBuffer\n\t\tthis.vertexState.bind();\n\t\t// ARRAY_BUFFER binding is NOT VAO state; uploads in flush() need the\n\t\t// batcher's upload target bound (this also preserves the invariant\n\t\t// custom batchers with hand-rolled flush() relied on)\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.uploadBuffer);\n\n\t\tif (this.renderer.currentProgram !== this.defaultShader.program) {\n\t\t\tthis.useShader(this.defaultShader);\n\t\t}\n\t}\n\n\t/**\n\t * called by the WebGL renderer when this batcher is being replaced by\n\t * another. Attribute state no longer needs disabling — it lives in this\n\t * batcher's vertex-state object and the incoming batcher's `bind()`\n\t * replaces the binding wholesale. Kept as a lifecycle hook: subclasses\n\t * override it to restore mode-specific GL state (see MeshBatcher's\n\t * blend/depth restore).\n\t */\n\tunbind() {}\n\n\t/**\n\t * Validate (once per shader) that a hosted shader's attribute locations\n\t * match this batcher's frozen vertex state. The engine's analogue of\n\t * WebGPU's pipeline-creation validation: locations are bound in vertex\n\t * source declaration order, so every shader hosted by this batcher must\n\t * declare a prefix of the batcher's attribute layout, in layout order.\n\t * A mismatched shader silently reads wrong vertex data — warn loudly.\n\t * @ignore\n\t */\n\tvalidateShaderLocations(shader) {\n\t\tif (this.validatedShaders === undefined) {\n\t\t\tthis.validatedShaders = new WeakSet();\n\t\t}\n\t\tif (shader === this.defaultShader || this.validatedShaders.has(shader)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.validatedShaders.add(shader);\n\t\tconst mismatches = [];\n\t\tfor (const attr of this.attributes) {\n\t\t\tconst expected = this.defaultShader.getAttribLocation(attr.name);\n\t\t\tconst actual = shader.getAttribLocation(attr.name);\n\t\t\t// -1 = the shader doesn't declare this attribute — allowed (the\n\t\t\t// enabled array is simply unconsumed); a DIFFERENT location is\n\t\t\t// the contract violation\n\t\t\tif (actual !== -1 && actual !== expected) {\n\t\t\t\tmismatches.push(`\"${attr.name}\" at ${actual} (expected ${expected})`);\n\t\t\t}\n\t\t}\n\t\tif (mismatches.length > 0) {\n\t\t\t// one consolidated warning per (batcher, shader) pair\n\t\t\tconsole.warn(\n\t\t\t\t`melonJS: shader attribute location mismatch: ${mismatches.join(\", \")} — ` +\n\t\t\t\t\t\"custom shaders must declare the batcher's attributes first, in layout order (vertex data will be read incorrectly)\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Ensure the draw about to be recorded runs on THIS batcher's program.\n\t * Normally `bind()` guaranteed that, but an `ONCONTEXT_RESTORED`\n\t * emission recompiles every live GLShader — each replaying its uniform\n\t * snapshot on its OWN program — leaving whichever recompiled last\n\t * really bound while the renderer's cache still named another. The\n\t * renderer invalidates its program cache on that event, so this\n\t * pointer compare re-issues `useProgram` exactly when the drift\n\t * happened and costs two loads otherwise. Called by every flush path\n\t * (the base and the quad override).\n\t * @ignore\n\t */\n\tsyncProgram() {\n\t\tconst shader = this.currentShader;\n\t\tif (\n\t\t\ttypeof shader !== \"undefined\" &&\n\t\t\tthis.renderer.currentProgram !== shader.program\n\t\t) {\n\t\t\t// the RAW rebind only — never useShader() from inside a flush\n\t\t\t// (it drains pending vertices itself: recursion). Attribute\n\t\t\t// state lives in the batcher's vertex state and uniforms are\n\t\t\t// per-program, so useProgram is the whole fix.\n\t\t\tshader.bind();\n\t\t\tthis.renderer.currentProgram = shader.program;\n\t\t}\n\t}\n\n\t/**\n\t * Select the shader to use for compositing\n\t * @see GLShader\n\t * @param {GLShader} shader - a reference to a GLShader instance\n\t */\n\tuseShader(shader) {\n\t\tif (\n\t\t\tthis.currentShader !== shader ||\n\t\t\tthis.renderer.currentProgram !== shader.program\n\t\t) {\n\t\t\tthis.flush();\n\t\t\tshader.bind();\n\t\t\tshader.setUniform(this.projectionUniform, this.renderer.projectionMatrix);\n\n\t\t\t// attribute pointers live in the frozen vertex state — hosted\n\t\t\t// shaders must conform to it (checked once per shader)\n\t\t\tthis.validateShaderLocations(shader);\n\n\t\t\tthis.currentShader = shader;\n\t\t\tthis.renderer.currentProgram = shader.program;\n\n\t\t\t// force sampler uniform to be re-set on next addQuad\n\t\t\tif (typeof this.currentSamplerUnit !== \"undefined\") {\n\t\t\t\tthis.currentSamplerUnit = -1;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * How this batcher assembles vertices into primitives, in the\n\t * backend-neutral vocabulary.\n\t *\n\t * `\"line-loop\"` and `\"triangle-fan\"` are engine extensions with no\n\t * equivalent in modern GPU APIs — prefer the others for anything that must\n\t * survive a backend change.\n\t * @type {Topology}\n\t * @default \"triangle-list\"\n\t */\n\tget topology() {\n\t\treturn this.#topology;\n\t}\n\n\t/**\n\t * @param {Topology|number} value - a topology name or a GL primitive mode\n\t */\n\tset topology(value) {\n\t\tthis.mode = value;\n\t}\n\n\t/**\n\t * Primitive type to render, as a GL enum (`gl.TRIANGLES`, `gl.LINES`, …).\n\t *\n\t * Assignable from either vocabulary — `batcher.mode = \"line-list\"` and\n\t * `batcher.mode = gl.LINES` are equivalent — but always reads back as the\n\t * GL enum, so existing comparisons keep working. Read\n\t * {@link WebGLBatcher#topology} for the portable name.\n\t * @type {number}\n\t * @default gl.TRIANGLES\n\t */\n\tget mode() {\n\t\treturn this.#mode;\n\t}\n\n\t/**\n\t * @param {Topology|number} value - a topology name or a GL primitive mode\n\t */\n\tset mode(value) {\n\t\t// Resolve on assignment rather than at draw time. A topology string\n\t\t// that reached a GL draw call would be coerced to 0, which *is*\n\t\t// POINTS — so the batch would silently render as points with no error\n\t\t// anywhere. Normalizing here is what makes that unreachable.\n\t\tthis.#mode = resolveTopology(this.gl, value);\n\t\tthis.#topology = topologyFromGL(this.gl, this.#mode);\n\t}\n\n\t/**\n\t * Add a vertex attribute to this batcher's layout.\n\t *\n\t * Accepts either vocabulary. The backend-neutral form names the component\n\t * type and count in one token, which is what a non-WebGL backend can\n\t * consume directly and what lets a layout be written without a live\n\t * rendering context:\n\t *\n\t * ```js\n\t * batcher.addAttribute({ name: \"aColor\", format: \"unorm8x4\", offset: 20 });\n\t * batcher.addAttribute(\"aColor\", \"unorm8x4\", 20);\n\t * ```\n\t *\n\t * The GL form is supported indefinitely and behaves exactly as before:\n\t *\n\t * ```js\n\t * batcher.addAttribute(\"aColor\", 4, gl.UNSIGNED_BYTE, true, 20);\n\t * ```\n\t *\n\t * Records keep both spellings, so existing readers of `size` / `type` /\n\t * `normalized` are unaffected. A GL combination with no portable name —\n\t * three-component 8- and 16-bit types, which the neutral vocabulary does\n\t * not define — is stored with `format: undefined` rather than an invented\n\t * name that a backend could not honour.\n\t *\n\t * The layout is frozen once `init()` has built the vertex state, and each\n\t * record is frozen on insertion: a batcher rebuilds its vertex state from\n\t * these records after a context loss, so a later mutation would take\n\t * effect at restore time rather than where it was written.\n\t * @param {string|object} name - attribute name, or a descriptor object\n\t * @param {number|VertexFormat} [size] - component count (GL form), or the format (neutral form)\n\t * @param {GLenum|number} [type] - component type (GL form), or the byte offset (neutral form)\n\t * @param {boolean} [normalized] - whether integers are scaled into `[0, 1]` / `[-1, 1]` (GL form only)\n\t * @param {number} [offset] - byte offset of the attribute within a vertex (GL form)\n\t * @throws {Error} when the layout is frozen, the format or GL type is unknown, or a descriptor contradicts itself\n\t */\n\taddAttribute(name, size, type, normalized, offset) {\n\t\t// Frozen check first, before any argument resolution: a frozen batcher\n\t\t// must report that regardless of what else is wrong with the call.\n\t\tif (this.vertexState) {\n\t\t\t// the layout is baked into the vertex-state object at init —\n\t\t\t// immutable afterwards, exactly like a vertex layout in a\n\t\t\t// compiled render pipeline\n\t\t\tthrow new Error(\n\t\t\t\t\"WebGLBatcher.addAttribute: the vertex buffer layout is frozen once the vertex state is built (add attributes before/without calling init)\",\n\t\t\t);\n\t\t}\n\n\t\tconst descriptor = this.#toAttributeDescriptor(\n\t\t\tname,\n\t\t\tsize,\n\t\t\ttype,\n\t\t\tnormalized,\n\t\t\toffset,\n\t\t\targuments.length,\n\t\t);\n\t\tconst resolved = this.#resolveAttribute(descriptor);\n\n\t\tthis.attributes.push(Object.freeze(resolved));\n\t\tthis.stride += resolved.bytes;\n\t\tthis.vertexSize = this.stride / Float32Array.BYTES_PER_ELEMENT;\n\t}\n\n\t/**\n\t * Normalize the three accepted call shapes into one descriptor.\n\t *\n\t * Disambiguated on the second argument: a number is a component count (GL\n\t * form), a string is a format (neutral form). The two can never collide.\n\t * @param {string|object} name - first argument as received\n\t * @param {number|string} size - second argument as received\n\t * @param {number} type - third argument as received\n\t * @param {boolean} normalized - fourth argument as received\n\t * @param {number} offset - fifth argument as received\n\t * @param {number} argCount - how many arguments the caller actually passed\n\t * @returns {object} a `{name, format?, size?, type?, normalized?, offset?}` descriptor\n\t * @ignore\n\t */\n\t#toAttributeDescriptor(name, size, type, normalized, offset, argCount) {\n\t\tif (typeof name === \"object\" && name !== null) {\n\t\t\treturn name;\n\t\t}\n\t\tif (typeof size === \"string\") {\n\t\t\t// neutral positional form: (name, format, offset). Guard the arity\n\t\t\t// rather than let a copied-over `normalized` argument land in\n\t\t\t// `offset` and silently place the attribute at byte 0.\n\t\t\tif (argCount > 3) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`WebGLBatcher.addAttribute(\"${name}\", \"${size}\", …): the format form takes ` +\n\t\t\t\t\t\t`(name, format, offset) — got ${argCount} arguments. ` +\n\t\t\t\t\t\t\"Drop the size/normalized arguments; the format implies them.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (type !== undefined && typeof type !== \"number\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`WebGLBatcher.addAttribute(\"${name}\", \"${size}\", …): offset must be a number, got ${typeof type}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn { name, format: size, offset: type };\n\t\t}\n\t\treturn { name, size, type, normalized, offset };\n\t}\n\n\t/**\n\t * Resolve a descriptor written in either vocabulary into the record shape\n\t * every consumer reads, plus the byte size to advance the stride by.\n\t * @param {object} descriptor - the normalized descriptor\n\t * @returns {object} `{name, format, size, type, normalized, offset, bytes}`\n\t * @ignore\n\t */\n\t#resolveAttribute(descriptor) {\n\t\tconst { name } = descriptor;\n\t\t// tightly packed by default in the neutral form: an omitted offset is\n\t\t// almost always meant to follow the previous attribute, and silently\n\t\t// defaulting to 0 would overlap them\n\t\tconst offset = descriptor.offset ?? this.stride;\n\n\t\tif (typeof descriptor.format === \"string\") {\n\t\t\tconst info = resolveVertexFormat(descriptor.format);\n\t\t\t// A redundant size/normalized is allowed only if it agrees. Silently\n\t\t\t// preferring one would produce a wrong stride, which does not throw\n\t\t\t// — it makes every vertex read from the wrong offset.\n\t\t\tif (\n\t\t\t\tdescriptor.size !== undefined &&\n\t\t\t\tdescriptor.size !== info.components\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`WebGLBatcher.addAttribute(\"${name}\"): format \"${descriptor.format}\" has ` +\n\t\t\t\t\t\t`${info.components} components but size ${descriptor.size} was given`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tdescriptor.normalized !== undefined &&\n\t\t\t\tdescriptor.normalized !== info.normalized\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`WebGLBatcher.addAttribute(\"${name}\"): format \"${descriptor.format}\" is ` +\n\t\t\t\t\t\t`${info.normalized ? \"\" : \"not \"}normalized but normalized=${descriptor.normalized} was given`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tname,\n\t\t\t\tformat: descriptor.format,\n\t\t\t\tsize: info.components,\n\t\t\t\ttype: glTypeForFormat(this.gl, descriptor.format),\n\t\t\t\tnormalized: info.normalized,\n\t\t\t\toffset,\n\t\t\t\tbytes: info.bytes,\n\t\t\t};\n\t\t}\n\n\t\t// GL form. `type` may also carry a format string as a migration alias;\n\t\t// resolve it here so no string ever survives into the record — one that\n\t\t// reached `vertexAttribPointer` would coerce to 0 and raise\n\t\t// INVALID_ENUM far from its cause.\n\t\tif (typeof descriptor.type === \"string\") {\n\t\t\treturn this.#resolveAttribute({\n\t\t\t\tname,\n\t\t\t\tformat: descriptor.type,\n\t\t\t\tsize: descriptor.size,\n\t\t\t\tnormalized: descriptor.normalized,\n\t\t\t\toffset: descriptor.offset,\n\t\t\t});\n\t\t}\n\n\t\tconst { size, type, normalized } = descriptor;\n\t\tconst bytes = this.#byteSizeOf(size, type);\n\t\treturn {\n\t\t\tname,\n\t\t\t// undefined when the combination has no portable name\n\t\t\tformat: formatFromGL(this.gl, size, type, normalized),\n\t\t\tsize,\n\t\t\ttype,\n\t\t\tnormalized,\n\t\t\toffset,\n\t\t\tbytes,\n\t\t};\n\t}\n\n\t/**\n\t * Byte size of a GL-form attribute.\n\t * @param {number} size - component count\n\t * @param {GLenum} type - component type\n\t * @returns {number} bytes occupied\n\t * @ignore\n\t */\n\t#byteSizeOf(size, type) {\n\t\tswitch (type) {\n\t\t\tcase this.gl.BYTE:\n\t\t\t\treturn size * Int8Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.UNSIGNED_BYTE:\n\t\t\t\treturn size * Uint8Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.SHORT:\n\t\t\t\treturn size * Int16Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.UNSIGNED_SHORT:\n\t\t\t\treturn size * Uint16Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.INT:\n\t\t\t\treturn size * Int32Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.UNSIGNED_INT:\n\t\t\t\treturn size * Uint32Array.BYTES_PER_ELEMENT;\n\t\t\tcase this.gl.FLOAT:\n\t\t\t\treturn size * Float32Array.BYTES_PER_ELEMENT;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"Invalid GL Attribute type\");\n\t\t}\n\t}\n\n\t/**\n\t * set/change the current projection matrix\n\t * @param {Matrix3d} matrix - the new projection matrix\n\t */\n\tsetProjection(matrix) {\n\t\tthis.currentShader.setUniform(this.projectionUniform, matrix);\n\t}\n\n\t/**\n\t * Add index values to the index buffer (only for indexed batchers).\n\t * Indices are rebased relative to the current vertex count.\n\t * @param {number[]} indices - array of index values to add\n\t */\n\taddIndices(indices) {\n\t\tif (!this.useIndexBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tthis.indexBuffer.add(indices, this.vertexData.vertexCount);\n\t}\n\n\t/**\n\t * Flush batched vertex data to the GPU\n\t * @param {number} [mode=gl.TRIANGLES] - the GL drawing mode\n\t */\n\tflush(mode = this.mode) {\n\t\t// Accepts either vocabulary. Only strings need resolving: `this.mode`\n\t\t// is already normalized by its setter, and this runs on every flush, so\n\t\t// a table lookup per draw would be pure overhead on the hottest path.\n\t\tif (typeof mode === \"string\") {\n\t\t\tmode = resolveTopology(this.gl, mode);\n\t\t}\n\t\tconst vertex = this.vertexData;\n\t\tconst vertexCount = vertex.vertexCount;\n\n\t\tif (vertexCount > 0) {\n\t\t\tconst gl = this.gl;\n\t\t\tconst vertexSize = vertex.vertexSize;\n\n\t\t\tthis.syncProgram();\n\n\t\t\t// Upload byte length covers exactly the vertices we've pushed.\n\t\t\t// Use the Uint8 view (NOT Float32) to keep packed-color bytes\n\t\t\t// intact — see `VertexArrayBuffer.bufferU8` for why this matters\n\t\t\t// on Metal-backed drivers.\n\t\t\tconst byteLength =\n\t\t\t\tvertexCount * vertexSize * Float32Array.BYTES_PER_ELEMENT;\n\n\t\t\tif (this.useIndexBuffer && this.indexBuffer.length > 0) {\n\t\t\t\t// indexed drawing path — attribute pointers live in the\n\t\t\t\t// vertex state; only the upload target needs (re)binding\n\t\t\t\t// (belt-and-braces: bind() already did this, but a custom\n\t\t\t\t// flush caller may not have gone through bind())\n\t\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.glVertexBuffer);\n\n\t\t\t\t// upload vertex data (WebGL 2 srcOffset/length overload — no\n\t\t\t\t// subview copy needed)\n\t\t\t\tgl.bufferData(\n\t\t\t\t\tgl.ARRAY_BUFFER,\n\t\t\t\t\tvertex.toUint8(),\n\t\t\t\t\tgl.STREAM_DRAW,\n\t\t\t\t\t0,\n\t\t\t\t\tbyteLength,\n\t\t\t\t);\n\n\t\t\t\t// upload and draw with index buffer\n\t\t\t\tthis.indexBuffer.upload();\n\t\t\t\tgl.drawElements(\n\t\t\t\t\tmode,\n\t\t\t\t\tthis.indexBuffer.length,\n\t\t\t\t\tthis.indexBuffer.type,\n\t\t\t\t\t0,\n\t\t\t\t);\n\n\t\t\t\t// clear index buffer\n\t\t\t\tthis.indexBuffer.clear();\n\t\t\t} else {\n\t\t\t\t// non-indexed drawing path (original behavior)\n\t\t\t\tgl.bufferData(\n\t\t\t\t\tgl.ARRAY_BUFFER,\n\t\t\t\t\tvertex.toUint8(),\n\t\t\t\t\tgl.STREAM_DRAW,\n\t\t\t\t\t0,\n\t\t\t\t\tbyteLength,\n\t\t\t\t);\n\n\t\t\t\tgl.drawArrays(mode, 0, vertexCount);\n\t\t\t}\n\n\t\t\t// clear the vertex buffer\n\t\t\tvertex.clear();\n\t\t}\n\t}\n}\n\nexport default WebGLBatcher;\n","/**\n * Primitive-batcher geometry assembly shared by both backends — pure CPU\n * code, no GPU references (the `meshchunk.ts` / `meshvertex.ts`\n * precedent). The vertex layout is the primitive tier's 6-float\n * `x, y, z, nx, ny, packed-color` stream on both backends.\n * @ignore\n */\n\n/** the minimal vertex-buffer surface this module writes — `@ignore` */\ninterface PrimitiveVertexData {\n\tpush(\n\t\tx: number,\n\t\ty: number,\n\t\tz: number,\n\t\tnx: number,\n\t\tny: number,\n\t\tcolor: number,\n\t): void;\n\tisFull(count: number): boolean;\n}\n\n/** a 4×4 column-major transform (Matrix3d-shaped) — `@ignore` */\ninterface MatrixLike {\n\tval: Float32Array;\n\tisIdentity(): boolean;\n}\n\ninterface PointLike {\n\tx: number;\n\ty: number;\n}\n\n/**\n * Push `verts[start..end)` into the vertex buffer, transformed by the\n * given view matrix. The caller guarantees the range fits.\n *\n * The transform includes the z column (m[8] / m[9] / m[10] / m[14]) so\n * Camera3d's view matrix (X/Y-axis rotation) actually rotates the\n * primitive in 3D. For 2D matrices those slots are identity, so output\n * (x, y, z) is bit-identical to the legacy 2D-only multiply.\n * @ignore\n */\nexport function pushPrimitiveRange(\n\tvertexData: PrimitiveVertexData,\n\tviewMatrix: MatrixLike,\n\tverts: PointLike[],\n\tstart: number,\n\tend: number,\n\tcolorUint32: number,\n\tz: number,\n): void {\n\tif (!viewMatrix.isIdentity()) {\n\t\tconst m = viewMatrix.val;\n\t\tfor (let i = start; i < end; i++) {\n\t\t\tconst vert = verts[i];\n\t\t\tconst x = vert.x;\n\t\t\tconst y = vert.y;\n\t\t\tvertexData.push(\n\t\t\t\tx * m[0] + y * m[4] + z * m[8] + m[12],\n\t\t\t\tx * m[1] + y * m[5] + z * m[9] + m[13],\n\t\t\t\tx * m[2] + y * m[6] + z * m[10] + m[14],\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tcolorUint32,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tfor (let i = start; i < end; i++) {\n\t\t\tconst vert = verts[i];\n\t\t\tvertexData.push(vert.x, vert.y, z, 0, 0, colorUint32);\n\t\t}\n\t}\n}\n\n/**\n * Expand line pairs into triangles with perpendicular normals. The vertex\n * shader offsets each vertex by `aNormal * uLineWidth * 0.5`, producing\n * thick lines without manual geometry expansion in the renderer. The\n * caller has already switched its draw mode to triangles.\n *\n * Each line pair expands to 2 triangles (6 vertices) — capacity is\n * checked per pair through the `flush` callback, so a dashed/long\n * thick-line path larger than the whole buffer flushes mid-shape instead\n * of silently dropping the out-of-range writes (pairs are independent\n * quads, so a mid-shape flush is invisible).\n *\n * The view matrix is applied (z column included) without mutating the\n * inputs. Note: the perpendicular normal is computed in pre-projection\n * world space, which appears non-perpendicular under perspective — a\n * known limitation shared by both backends.\n * @param vertexData - destination vertex stream\n * @param viewMatrix - the current view matrix\n * @param verts - line vertices in pairs [from, to, from, to, ...]\n * @param vertexCount - number of vertices to consume\n * @param colorUint32 - packed line color\n * @param z - the current renderer depth\n * @param flush - drains the vertex buffer when a pair no longer fits\n * @ignore\n */\nexport function expandLinesToTriangles(\n\tvertexData: PrimitiveVertexData,\n\tviewMatrix: MatrixLike,\n\tverts: PointLike[],\n\tvertexCount: number,\n\tcolorUint32: number,\n\tz: number,\n\tflush: () => void,\n): void {\n\tconst hasTransform = !viewMatrix.isIdentity();\n\tconst m = hasTransform ? viewMatrix.val : null;\n\n\tfor (let i = 0; i < vertexCount; i += 2) {\n\t\tconst from = verts[i];\n\t\tconst to = verts[i + 1];\n\n\t\tif (vertexData.isFull(6)) {\n\t\t\tflush();\n\t\t}\n\n\t\tlet fromX: number;\n\t\tlet fromY: number;\n\t\tlet fromZ: number;\n\t\tlet toX: number;\n\t\tlet toY: number;\n\t\tlet toZ: number;\n\t\tif (m !== null) {\n\t\t\tfromX = from.x * m[0] + from.y * m[4] + z * m[8] + m[12];\n\t\t\tfromY = from.x * m[1] + from.y * m[5] + z * m[9] + m[13];\n\t\t\tfromZ = from.x * m[2] + from.y * m[6] + z * m[10] + m[14];\n\t\t\ttoX = to.x * m[0] + to.y * m[4] + z * m[8] + m[12];\n\t\t\ttoY = to.x * m[1] + to.y * m[5] + z * m[9] + m[13];\n\t\t\ttoZ = to.x * m[2] + to.y * m[6] + z * m[10] + m[14];\n\t\t} else {\n\t\t\tfromX = from.x;\n\t\t\tfromY = from.y;\n\t\t\tfromZ = z;\n\t\t\ttoX = to.x;\n\t\t\ttoY = to.y;\n\t\t\ttoZ = z;\n\t\t}\n\n\t\t// compute perpendicular unit normal\n\t\tconst dx = toX - fromX;\n\t\tconst dy = toY - fromY;\n\t\tconst len = Math.sqrt(dx * dx + dy * dy);\n\n\t\tif (len === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nx = -dy / len;\n\t\tconst ny = dx / len;\n\n\t\t// two triangles forming a quad around the line segment\n\t\tvertexData.push(fromX, fromY, fromZ, nx, ny, colorUint32);\n\t\tvertexData.push(fromX, fromY, fromZ, -nx, -ny, colorUint32);\n\t\tvertexData.push(toX, toY, toZ, -nx, -ny, colorUint32);\n\n\t\tvertexData.push(fromX, fromY, fromZ, nx, ny, colorUint32);\n\t\tvertexData.push(toX, toY, toZ, -nx, -ny, colorUint32);\n\t\tvertexData.push(toX, toY, toZ, nx, ny, colorUint32);\n\t}\n}\n","varying vec4 vColor;\n\nvoid main(void) {\n    gl_FragColor = vColor;\n}\n","// Current vertex point — z carries per-sprite depth (renderable.depth),\n// a no-op under the default ortho projection, used by perspective (Camera3d).\nattribute vec3 aVertex;\n// Per-vertex normal for line expansion (2D — line expansion is in the\n// screen plane and doesn't participate in depth).\nattribute vec2 aNormal;\nattribute vec4 aColor;\n\n// Projection matrix\nuniform mat4 uProjectionMatrix;\n// Line width for stroke expansion\nuniform float uLineWidth;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n    // Expand vertex position along the normal by half the line width (2D)\n    vec2 position = aVertex.xy + aNormal * uLineWidth * 0.5;\n    // Transform the vertex position by the projection matrix\n    gl_Position = uProjectionMatrix * vec4(position, aVertex.z, 1.0);\n    // Pass the remaining attributes to the fragment shader\n    vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n}\n","import {\n\texpandLinesToTriangles,\n\tpushPrimitiveRange,\n} from \"../../gpu/primitives.ts\";\nimport primitiveFragment from \"./../shaders/primitive.frag\";\nimport primitiveVertex from \"./../shaders/primitive.vert\";\nimport { resolveTopology } from \"../utils/topology.js\";\nimport { WebGLBatcher } from \"./batcher.js\";\n\n/**\n * additional import for TypeScript\n * @import {Point} from \"./../../../geometries/point.ts\";\n */\n\n/**\n * A WebGL Compositor object. This class handles all of the WebGL state<br>\n * Pushes texture regions or shape geometry into WebGL buffers, automatically flushes to GPU\n * @category Rendering\n */\nexport default class PrimitiveBatcher extends WebGLBatcher {\n\t/**\n\t * Initialize the compositor\n\t * @ignore\n\t */\n\tinit(renderer) {\n\t\tsuper.init(renderer, {\n\t\t\tattributes: [\n\t\t\t\t{\n\t\t\t\t\t// vec3: (x, y, z). z carries `renderable.depth` for\n\t\t\t\t\t// perspective projection (Camera3d). Stride = 24 bytes.\n\t\t\t\t\tname: \"aVertex\",\n\t\t\t\t\tformat: \"float32x3\",\n\t\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aNormal\",\n\t\t\t\t\tformat: \"float32x2\",\n\t\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aColor\",\n\t\t\t\t\tformat: \"unorm8x4\",\n\t\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t],\n\t\t\tshader: {\n\t\t\t\tvertex: primitiveVertex,\n\t\t\t\tfragment: primitiveFragment,\n\t\t\t},\n\t\t});\n\n\t\t/**\n\t\t * the current line width applied to the shader uniform\n\t\t * @ignore\n\t\t */\n\t\tthis.currentLineWidth = 1;\n\t}\n\n\t/**\n\t * called by the WebGL renderer when a compositor become the current one\n\t */\n\tbind() {\n\t\tconst shaderChanged =\n\t\t\tthis.renderer.currentProgram !== this.defaultShader.program;\n\t\tsuper.bind();\n\t\tif (shaderChanged) {\n\t\t\t// set the default line width uniform after a shader change\n\t\t\tthis.currentLineWidth = this.renderer.lineWidth;\n\t\t\tthis.currentShader.setUniform(\"uLineWidth\", this.currentLineWidth);\n\t\t}\n\t}\n\n\t/**\n\t * Reset compositor internal state\n\t * @ignore\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.currentLineWidth = 1;\n\t}\n\n\t/**\n\t * Draw an array of vertices\n\t * @param {GLenum} mode - primitive type to render (gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN, gl.TRIANGLES)\n\t * @param {Point[]} verts - an array of vertices\n\t * @param {number} [vertexCount=verts.length] - amount of points defined in the points array\n\t */\n\tdrawVertices(mode, verts, vertexCount = verts.length) {\n\t\t// Normalize before anything compares it. Every branch below tests the\n\t\t// mode numerically (`=== gl.LINES`, `!== this.mode`, the topology\n\t\t// switch), and a string would fail all of them while coercing to 0 —\n\t\t// i.e. POINTS — at the draw call.\n\t\tif (typeof mode === \"string\") {\n\t\t\tmode = resolveTopology(this.gl, mode);\n\t\t}\n\t\tconst lineWidth = this.renderer.lineWidth;\n\n\t\t// update uLineWidth uniform if changed\n\t\tif (lineWidth !== this.currentLineWidth) {\n\t\t\tthis.flush();\n\t\t\tthis.currentLineWidth = lineWidth;\n\t\t\tthis.currentShader.setUniform(\"uLineWidth\", lineWidth);\n\t\t}\n\n\t\t// expand thick lines to triangles with normals for shader-based expansion\n\t\tif (mode === this.gl.LINES && lineWidth > 1) {\n\t\t\tthis.#expandLinesToTriangles(verts, vertexCount);\n\t\t\treturn;\n\t\t}\n\n\t\tconst vertexData = this.vertexData;\n\t\tconst alpha = this.renderer.getGlobalAlpha();\n\t\tconst colorUint32 = this.renderer.currentColor.toUint32(alpha);\n\t\t// z = current renderer depth (Renderable.preDraw); a no-op under ortho,\n\t\t// consumed by perspective (Camera3d). Same value for every vertex —\n\t\t// primitives don't have per-vertex depth.\n\t\tconst z = this.renderer.currentDepth;\n\n\t\t// flush if drawing vertices with a different drawing mode\n\t\tif (mode !== this.mode) {\n\t\t\tthis.flush(this.mode);\n\t\t\tthis.mode = mode;\n\t\t}\n\n\t\tif (vertexCount < vertexData.maxVertex) {\n\t\t\t// fast path: the shape fits in one batch\n\t\t\tif (vertexData.isFull(vertexCount)) {\n\t\t\t\t// is the vertex buffer full if we add more vertices\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t\tthis.#pushRange(verts, 0, vertexCount, colorUint32, z);\n\t\t} else {\n\t\t\t// a single shape larger than the whole vertex buffer (a filled\n\t\t\t// ellipse already exceeds it at radius ≳ 435 px) — split it into\n\t\t\t// buffer-sized chunks; without this, the typed-array writes past\n\t\t\t// the buffer end are silently dropped while the draw call still\n\t\t\t// uses the full count, raising GL errors and rendering nothing\n\t\t\tthis.#drawVerticesChunked(mode, verts, vertexCount, colorUint32, z);\n\t\t}\n\n\t\t// force flush for primitive using LINE_STRIP or LINE_LOOP\n\t\tif (this.mode === this.gl.LINE_STRIP || this.mode === this.gl.LINE_LOOP) {\n\t\t\tthis.flush(this.mode);\n\t\t}\n\t}\n\n\t/**\n\t * Push `verts[start..end)` into the vertex buffer, transformed by the\n\t * current view matrix. The caller guarantees the range fits.\n\t * @ignore\n\t */\n\t#pushRange(verts, start, end, colorUint32, z) {\n\t\t// the shared neutral range push (z-column-aware transform) — one\n\t\t// copy for both backends, see `gpu/primitives.ts`\n\t\tpushPrimitiveRange(\n\t\t\tthis.vertexData,\n\t\t\tthis.viewMatrix,\n\t\t\tverts,\n\t\t\tstart,\n\t\t\tend,\n\t\t\tcolorUint32,\n\t\t\tz,\n\t\t);\n\t}\n\n\t/**\n\t * Draw an over-capacity vertex list as a sequence of buffer-sized chunks,\n\t * split on primitive boundaries so every triangle/line stays whole:\n\t *\n\t * - TRIANGLES / LINES chunk on multiples of 3 / 2, POINTS anywhere.\n\t * - LINE_STRIP re-pushes the boundary vertex so the connecting segment\n\t *   is preserved; LINE_LOOP additionally appends the first vertex at the\n\t *   very end (drawn as open strips, the final duplicate closes the loop).\n\t * - TRIANGLE_STRIP overlaps 2 boundary vertices; TRIANGLE_FAN re-anchors\n\t *   every chunk on `verts[0]` and overlaps 1. (Strip chunk parity can\n\t *   flip triangle winding at a boundary — irrelevant here, the primitive\n\t *   pipeline never enables face culling.)\n\t * @ignore\n\t */\n\t#drawVerticesChunked(mode, verts, vertexCount, colorUint32, z) {\n\t\tconst gl = this.gl;\n\t\t// stay one below maxVertex to match isFull()'s `>=` convention\n\t\tconst capacity = this.vertexData.maxVertex - 1;\n\t\tlet step = capacity;\n\t\tlet overlap = 0;\n\t\tlet anchor = false;\n\t\tlet drawMode = mode;\n\t\tswitch (mode) {\n\t\t\tcase gl.TRIANGLES:\n\t\t\t\tstep = capacity - (capacity % 3);\n\t\t\t\tbreak;\n\t\t\tcase gl.LINES:\n\t\t\t\tstep = capacity - (capacity % 2);\n\t\t\t\tbreak;\n\t\t\tcase gl.LINE_STRIP:\n\t\t\t\toverlap = 1;\n\t\t\t\tbreak;\n\t\t\tcase gl.LINE_LOOP:\n\t\t\t\t// chunks are open strips; the loop is closed explicitly below\n\t\t\t\toverlap = 1;\n\t\t\t\tdrawMode = gl.LINE_STRIP;\n\t\t\t\tbreak;\n\t\t\tcase gl.TRIANGLE_STRIP:\n\t\t\t\toverlap = 2;\n\t\t\t\tbreak;\n\t\t\tcase gl.TRIANGLE_FAN:\n\t\t\t\toverlap = 1;\n\t\t\t\tanchor = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// POINTS: any split works\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.mode = drawMode;\n\n\t\tlet start = 0;\n\t\twhile (start < vertexCount) {\n\t\t\t// each chunk starts on an empty buffer\n\t\t\tthis.flush(drawMode);\n\t\t\tconst anchored = anchor && start > 0;\n\t\t\tif (anchored) {\n\t\t\t\tthis.#pushRange(verts, 0, 1, colorUint32, z);\n\t\t\t}\n\t\t\tconst count = Math.min(vertexCount - start, step - (anchored ? 1 : 0));\n\t\t\tthis.#pushRange(verts, start, start + count, colorUint32, z);\n\t\t\tstart += count;\n\t\t\tif (start < vertexCount) {\n\t\t\t\tstart -= overlap;\n\t\t\t}\n\t\t}\n\n\t\tif (mode === gl.LINE_LOOP) {\n\t\t\t// close the loop: duplicate the first vertex after the last one\n\t\t\tif (this.vertexData.isFull(1)) {\n\t\t\t\tthis.flush(drawMode);\n\t\t\t\tthis.#pushRange(verts, vertexCount - 1, vertexCount, colorUint32, z);\n\t\t\t}\n\t\t\tthis.#pushRange(verts, 0, 1, colorUint32, z);\n\t\t}\n\t}\n\n\t/**\n\t * Expand line pairs into triangles with perpendicular normals.\n\t * The vertex shader offsets each vertex by aNormal * uLineWidth * 0.5,\n\t * producing thick lines without manual geometry expansion in the renderer.\n\t * @param {Point[]} verts - line vertices in pairs [from, to, from, to, ...]\n\t * @param {number} vertexCount - number of vertices\n\t * @ignore\n\t */\n\t#expandLinesToTriangles(verts, vertexCount) {\n\t\t// switch to TRIANGLES mode, then delegate the expansion to the\n\t\t// shared neutral helper — see `gpu/primitives.ts`\n\t\tif (this.mode !== this.gl.TRIANGLES) {\n\t\t\tthis.flush(this.mode);\n\t\t\tthis.mode = this.gl.TRIANGLES;\n\t\t}\n\t\texpandLinesToTriangles(\n\t\t\tthis.vertexData,\n\t\t\tthis.viewMatrix,\n\t\t\tverts,\n\t\t\tvertexCount,\n\t\t\tthis.renderer.currentColor.toUint32(this.renderer.getGlobalAlpha()),\n\t\t\t// z = current renderer depth (Renderable.preDraw); a no-op under\n\t\t\t// ortho, consumed by perspective (Camera3d)\n\t\t\tthis.renderer.currentDepth,\n\t\t\t() => {\n\t\t\t\tthis.flush();\n\t\t\t},\n\t\t);\n\t}\n}\n","import type { Matrix3d } from \"../../math/matrix3d.ts\";\nimport { Vector3d } from \"../../math/vector3d.ts\";\n\n/**\n * The quad corner transform shared by the quad batchers of both backends\n * (and their lit variants) — pure CPU code, no GPU references.\n *\n * A reusable pool of four `Vector3d` corners: the per-sprite depth rides\n * `z` so `Matrix3d.apply` fully rotates the vertex under a `Camera3d`\n * view matrix, while 2D-only matrices leave (x, y) bit-identical to the\n * legacy `Vector2d` path with z passing through unchanged. Callers must\n * always pass `z` explicitly — the vectors are shared, so leftover depth\n * from a previous quad would otherwise leak into a z = 0 blit.\n *\n * Module-level sharing is safe: a quad is assembled synchronously and the\n * corners are consumed before the next call, whichever backend (or\n * coexisting renderer) drives it.\n * @ignore\n */\nconst corners = [\n\tnew Vector3d(),\n\tnew Vector3d(),\n\tnew Vector3d(),\n\tnew Vector3d(),\n];\n\n/**\n * Set the four corners of an axis-aligned quad and transform them by `m`\n * (skipped when `m` is null/identity — the common untransformed case).\n * Returns the shared corner pool, valid until the next call:\n * `[topLeft, topRight, bottomLeft, bottomRight]`.\n * @ignore\n */\nexport function transformQuadCorners(\n\tm: Matrix3d | null | undefined,\n\tx: number,\n\ty: number,\n\tw: number,\n\th: number,\n\tz: number,\n): Vector3d[] {\n\tcorners[0].set(x, y, z);\n\tcorners[1].set(x + w, y, z);\n\tcorners[2].set(x, y + h, z);\n\tcorners[3].set(x + w, y + h, z);\n\tif (m && !m.isIdentity()) {\n\t\tm.apply(corners[0]);\n\t\tm.apply(corners[1]);\n\t\tm.apply(corners[2]);\n\t\tm.apply(corners[3]);\n\t}\n\treturn corners;\n}\n","/**\n * Generates a multi-texture fragment shader source string.\n * Declares individual sampler uniforms (uSampler0..uSamplerN) and uses\n * an if/else chain with 0.5-offset thresholds to select the correct texture unit.\n * @param {number} maxTextures - the number of texture units to support\n * @returns {string} GLSL fragment shader source\n * @ignore\n */\nexport function buildMultiTextureFragment(maxTextures) {\n\tconst count = Math.max(maxTextures, 1);\n\tconst lines = [];\n\n\t// declare sampler uniforms\n\tfor (let i = 0; i < count; i++) {\n\t\tlines.push(\"uniform sampler2D uSampler\" + i + \";\");\n\t}\n\n\tlines.push(\"varying vec4 vColor;\");\n\tlines.push(\"varying vec2 vRegion;\");\n\tlines.push(\"varying float vTextureId;\");\n\tlines.push(\"\");\n\tlines.push(\"void main(void) {\");\n\tlines.push(\"    vec4 color;\");\n\n\t// generate if/else chain using < N.5 thresholds\n\tfor (let i = 0; i < count; i++) {\n\t\tif (i === 0) {\n\t\t\tlines.push(\"    if (vTextureId < 0.5) {\");\n\t\t} else {\n\t\t\tlines.push(\"    } else if (vTextureId < \" + (i + 0.5) + \") {\");\n\t\t}\n\t\tlines.push(\"        color = texture2D(uSampler\" + i + \", vRegion);\");\n\t}\n\n\t// fallback to first sampler if vTextureId is out of range\n\tlines.push(\"    } else {\");\n\tlines.push(\"        color = texture2D(uSampler0, vRegion);\");\n\tlines.push(\"    }\");\n\tlines.push(\"    gl_FragColor = color * vColor;\");\n\tlines.push(\"}\");\n\n\treturn lines.join(\"\\n\");\n}\n","// Current vertex point — z carries per-sprite depth (renderable.depth),\n// has no visible effect under the default ortho projection but is required\n// for perspective projection (Camera3d) to scale sprites by distance.\nattribute vec3 aVertex;\nattribute vec2 aRegion;\nattribute vec4 aColor;\nattribute float aTextureId;\n\n// Projection matrix\nuniform mat4 uProjectionMatrix;\n\nvarying vec2 vRegion;\nvarying vec4 vColor;\nvarying float vTextureId;\n\nvoid main(void) {\n    // Transform the vertex position by the projection matrix\n    gl_Position = uProjectionMatrix * vec4(aVertex, 1.0);\n    // Pass the remaining attributes to the fragment shader\n    vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n    vRegion = aRegion;\n    vTextureId = aTextureId;\n}\n","import { GPU_TEXTURE_CACHE_RESET, off, on } from \"../../../system/event.ts\";\nimport { WebGLBatcher } from \"./batcher.js\";\n\n/**\n * additional import for TypeScript\n * @import {TextureAtlas} from \"./../../texture/atlas.js\";\n */\n\n/**\n * Base class for batchers that manage WebGL textures and material properties.\n * Provides texture creation, binding, uploading, and deletion.\n * @category Rendering\n */\n/**\n * Immutable-storage bookkeeping: `texStorage2D` allocations can never be\n * respecified, so each GL texture's allocated shape is recorded here and a\n * mismatching re-upload replaces the texture object outright. WeakMap so\n * deleted textures drop their records with them.\n * @type {WeakMap<WebGLTexture, {width: number, height: number, levels: number, format: number}>}\n * @ignore\n */\nconst immutableStorage = new WeakMap();\n\n/**\n * full mip chain length for a base size (immutable storage allocates the\n * whole pyramid up front)\n * @ignore\n */\nfunction mipLevels(w, h) {\n\treturn Math.floor(Math.log2(Math.max(w, h))) + 1;\n}\n\nexport class MaterialBatcher extends WebGLBatcher {\n\t/**\n\t * Initialize the textured batcher\n\t * @ignore\n\t */\n\tinit(renderer, settings) {\n\t\tsuper.init(renderer, settings);\n\n\t\t// invalidate the active-unit tracking (see the currentTextureUnit\n\t\t// accessor — the state lives on the renderer, shared by all batchers)\n\t\tthis.currentTextureUnit = -1;\n\n\t\t/**\n\t\t * bound textures by unit\n\t\t * @ignore\n\t\t */\n\t\tthis.boundTextures = [];\n\n\t\t/**\n\t\t * Units whose texture CONTENT changed since the last upload (a\n\t\t * canvas re-bake announced via `CanvasRenderTarget.invalidate`).\n\t\t * Consumed by {@link MaterialBatcher#uploadTexture}, which then\n\t\t * re-uploads INTO the existing texture — with immutable storage a\n\t\t * same-shape re-upload is a pure `texSubImage2D`, so a ticking\n\t\t * Text / gradient re-bake never re-allocates.\n\t\t * @type {Set<number>}\n\t\t * @ignore\n\t\t */\n\t\tthis.dirtyUnits = new Set();\n\n\t\t/**\n\t\t * track the current sampler unit to avoid redundant gl.uniform1i calls\n\t\t * @ignore\n\t\t */\n\t\tthis.currentSamplerUnit = -1;\n\n\t\t// Drop our `texture → unit` tracking whenever the renderer-wide\n\t\t// texture cache reassigns units. Without this, our\n\t\t// `bindTexture2D` short-circuit (`texture ===\n\t\t// boundTextures[unit]`) would skip the actual `gl.bindTexture`\n\t\t// call after a reset — the unit on the GPU is now occupied by\n\t\t// whichever texture triggered the reset (or whichever batcher\n\t\t// drew next), and we'd sample that one instead. Symptom was\n\t\t// meshes coming out black and sprites/bullets coming out\n\t\t// white. Inherited by every MaterialBatcher subclass\n\t\t// (QuadBatcher, LitQuadBatcher, MeshBatcher) so each one gets\n\t\t// the same handler automatically — PrimitiveBatcher extends\n\t\t// `Batcher` directly and has no texture state, so it doesn't\n\t\t// need this.\n\t\tif (!this._onCacheReset) {\n\t\t\t// delegate to an overridable method so subclasses that track extra\n\t\t\t// per-unit bindings (lit normal maps) can drop those on a reset too\n\t\t\tthis._onCacheReset = () => {\n\t\t\t\tthis._onTextureCacheReset();\n\t\t\t};\n\t\t\ton(GPU_TEXTURE_CACHE_RESET, this._onCacheReset);\n\t\t}\n\t}\n\n\t/**\n\t * Drop every cached texture binding after a {@link GPU_TEXTURE_CACHE_RESET}\n\t * (the shared texture cache reassigned units — our per-unit view is stale).\n\t * Subclasses that pair extra samplers to units (lit normal maps) override to\n\t * forget those too; without that they'd assume the extra texture is still\n\t * resident and skip re-binding it after the reset.\n\t * @ignore\n\t */\n\t_onTextureCacheReset() {\n\t\tthis.boundTextures.length = 0;\n\t\tthis.dirtyUnits.clear();\n\t\tthis.currentTextureUnit = -1;\n\t\tthis.currentSamplerUnit = -1;\n\t}\n\n\t/**\n\t * The GL texture unit currently active (`gl.activeTexture`). Tracked on\n\t * the RENDERER, not per batcher: the active unit is global GL state\n\t * shared by every batcher instance, and a per-batcher copy desyncs as\n\t * soon as another batcher (or a blit, or an FBO pass) moves the active\n\t * unit — the stale copy then lets `bindTexture2D` skip `gl.activeTexture`\n\t * and bind (or upload) a texture onto whatever unit is REALLY active.\n\t * Symptom was video force-re-uploads overwriting a mesh texture with\n\t * video frames after a mesh pass.\n\t * @type {number}\n\t * @ignore\n\t */\n\tget currentTextureUnit() {\n\t\treturn this.renderer._activeTextureUnit;\n\t}\n\n\tset currentTextureUnit(unit) {\n\t\tthis.renderer._activeTextureUnit = unit;\n\t}\n\n\t/**\n\t * Free resources used by the batcher. Currently unsubscribes the\n\t * texture-cache-reset listener so a discarded batcher doesn't keep\n\t * accumulating handlers (relevant on context loss / renderer\n\t * teardown).\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tif (this._onCacheReset) {\n\t\t\toff(GPU_TEXTURE_CACHE_RESET, this._onCacheReset);\n\t\t\tthis._onCacheReset = null;\n\t\t}\n\t\tsuper.destroy();\n\t}\n\n\t/**\n\t * Reset batcher internal state\n\t * @ignore\n\t */\n\treset() {\n\t\tsuper.reset();\n\n\t\t// The store owns every colour-texture handle since #1585, so releasing\n\t\t// them means asking it — walking `boundTextures` would miss any texture\n\t\t// not currently assigned a unit, and would double-free the ones that are.\n\t\tthis.renderer.textureStore?.releaseAll(true);\n\t\tthis.boundTextures.length = 0;\n\t\tthis.dirtyUnits.clear();\n\t\tthis.currentTextureUnit = -1;\n\t\tthis.currentSamplerUnit = -1;\n\t}\n\n\t/**\n\t * Create a WebGL texture from an image\n\t * @param {number} unit - Destination texture unit\n\t * @param {Image|HTMLCanvasElement|ImageData|Uint8Array[]|Float32Array[]} [pixels=null] - Source image\n\t * @param {number} filter - gl.LINEAR or gl.NEAREST\n\t * @param {string} [repeat=\"no-repeat\"] - Image repeat behavior\n\t * @param {number} [w=pixels.width] - Source image width\n\t * @param {number} [h=pixels.height] - Source image height\n\t * @param {boolean} [premultipliedAlpha=true] - Multiplies the alpha channel into the other color channels\n\t * @param {boolean} [mipmap=true] - Whether mipmap levels should be generated\n\t * @returns {WebGLTexture} a WebGL texture\n\t */\n\tcreateTexture2D(\n\t\tunit,\n\t\tpixels = null,\n\t\tfilter,\n\t\trepeat = \"no-repeat\",\n\t\tw = pixels.width,\n\t\th = pixels.height,\n\t\tpremultipliedAlpha = true,\n\t\tmipmap = true,\n\t\ttexture,\n\t\tflush = true,\n\t) {\n\t\tconst gl = this.gl;\n\t\t// WebGL 2: REPEAT wrap and mipmaps work on NPOT textures — no\n\t\t// power-of-two gating (the WebGL 1 clamp-downgrade path is gone)\n\t\tconst rs =\n\t\t\trepeat.search(/^repeat(-x)?$/) === 0 ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\t\tconst rt =\n\t\t\trepeat.search(/^repeat(-y)?$/) === 0 ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\n\t\tlet currentTexture = texture;\n\t\tif (!currentTexture) {\n\t\t\tcurrentTexture = gl.createTexture();\n\t\t}\n\n\t\tthis.bindTexture2D(currentTexture, unit, flush);\n\n\t\t// `bindTexture2D` skips the GL calls entirely when its bookkeeping says\n\t\t// the texture is already bound and active — but the uploads below\n\t\t// write through whatever unit/binding is REALLY active in GL. Force\n\t\t// both (uploads are a cold path) so a re-upload can never land on a\n\t\t// foreign texture even if the tracked state went stale.\n\t\tgl.activeTexture(gl.TEXTURE0 + unit);\n\t\tgl.bindTexture(gl.TEXTURE_2D, currentTexture);\n\t\tthis.currentTextureUnit = unit;\n\n\t\t// Immutable storage (`texStorage2D` + sized formats): allocate the\n\t\t// complete texture — every mip level, explicit RGBA8 layout — once,\n\t\t// and update contents with `texSubImage2D` thereafter. The driver\n\t\t// then skips per-draw completeness validation (the texture is\n\t\t// known-complete forever) and same-size re-uploads (video frames,\n\t\t// Text/gradient re-bakes) are pure data copies — the same model as\n\t\t// the WebGPU texture store. `TextureResource` sources own their\n\t\t// upload call and keep classic mutable storage.\n\t\tconst resourceOwned =\n\t\t\tpixels !== null && typeof pixels.upload === \"function\";\n\t\tconst compressed = pixels !== null && pixels.compressed === true;\n\t\tif (!resourceOwned) {\n\t\t\tconst storageW = Math.max(1, w | 0);\n\t\t\tconst storageH = Math.max(1, h | 0);\n\t\t\tconst levels = compressed\n\t\t\t\t? pixels.mipmaps.length\n\t\t\t\t: mipmap === true\n\t\t\t\t\t? mipLevels(storageW, storageH)\n\t\t\t\t\t: 1;\n\t\t\tconst format = compressed ? pixels.format : gl.RGBA8;\n\t\t\tlet storage = immutableStorage.get(currentTexture);\n\t\t\tif (\n\t\t\t\tstorage &&\n\t\t\t\t(storage.width !== storageW ||\n\t\t\t\t\tstorage.height !== storageH ||\n\t\t\t\t\tstorage.levels !== levels ||\n\t\t\t\t\tstorage.format !== format)\n\t\t\t) {\n\t\t\t\t// immutable storage can never be respecified — a shape change\n\t\t\t\t// replaces the texture object outright (the engine's stores\n\t\t\t\t// already treat size changes as recreation on every backend)\n\t\t\t\tgl.deleteTexture(currentTexture);\n\t\t\t\tcurrentTexture = gl.createTexture();\n\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, currentTexture);\n\t\t\t\tthis.boundTextures[unit] = currentTexture;\n\t\t\t\tstorage = undefined;\n\t\t\t}\n\t\t\tif (!storage) {\n\t\t\t\tgl.texStorage2D(gl.TEXTURE_2D, levels, format, storageW, storageH);\n\t\t\t\timmutableStorage.set(currentTexture, {\n\t\t\t\t\twidth: storageW,\n\t\t\t\t\theight: storageH,\n\t\t\t\t\tlevels,\n\t\t\t\t\tformat,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, rs);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, rt);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);\n\n\t\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultipliedAlpha);\n\n\t\tif (resourceOwned) {\n\t\t\t// `TextureResource` path: the resource owns its upload (raw\n\t\t\t// buffer, future synthesized sources, etc.). Keeps every\n\t\t\t// backend-specific upload call in one place per source type.\n\t\t\t// The resource's `upload(context, target)` contract is\n\t\t\t// renderer-agnostic — we pass `gl` here because this is the\n\t\t\t// WebGL batcher; a future WebGPU batcher would pass its own\n\t\t\t// `renderer.getContext()` result and the resource subclass\n\t\t\t// implementing that backend would handle it.\n\t\t\tpixels.upload(gl, gl.TEXTURE_2D);\n\t\t} else if (compressed) {\n\t\t\t// immutable storage was allocated with exactly the authored\n\t\t\t// levels, so the chain is inherently complete — the old\n\t\t\t// TEXTURE_MAX_LEVEL cap is subsumed by the `levels` parameter\n\t\t\tconst mipmaps = pixels.mipmaps;\n\t\t\tfor (let i = 0; i < mipmaps.length; i++) {\n\t\t\t\tgl.compressedTexSubImage2D(\n\t\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t\ti,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tmipmaps[i].width,\n\t\t\t\t\tmipmaps[i].height,\n\t\t\t\t\tpixels.format,\n\t\t\t\t\tmipmaps[i].data,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (pixels === null) {\n\t\t\t// allocation without data — WebGL guarantees zero-initialized\n\t\t\t// storage, so texStorage2D alone is the blank texture\n\t\t} else if (typeof pixels.byteLength !== \"undefined\") {\n\t\t\tgl.texSubImage2D(\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t\tgl.RGBA,\n\t\t\t\tgl.UNSIGNED_BYTE,\n\t\t\t\tpixels,\n\t\t\t\t0,\n\t\t\t);\n\t\t} else {\n\t\t\t// any TexImageSource (image, canvas, OffscreenCanvas, video,\n\t\t\t// ImageBitmap): WebGL2's DOM overload of texSubImage2D uploads\n\t\t\t// the source at its natural size into the allocated storage.\n\t\t\t// (OffscreenCanvas deliberately NOT routed through\n\t\t\t// transferToImageBitmap — that call is destructive and blanked\n\t\t\t// re-uploads; see the plinko-planck particle regression.)\n\t\t\tgl.texSubImage2D(\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tgl.RGBA,\n\t\t\t\tgl.UNSIGNED_BYTE,\n\t\t\t\tpixels,\n\t\t\t);\n\t\t}\n\n\t\t// WebGL 2 mipmaps NPOT textures fine — no POT gate. generateMipmap\n\t\t// fills the pre-allocated immutable chain in place.\n\t\tif (\n\t\t\tmipmap === true &&\n\t\t\t(pixels === null || (!compressed && !resourceOwned))\n\t\t) {\n\t\t\tgl.generateMipmap(gl.TEXTURE_2D);\n\t\t}\n\n\t\treturn currentTexture;\n\t}\n\n\t/**\n\t * delete the given WebGL texture\n\t * @param {WebGLTexture|TextureAtlas} texture - a WebGL texture or TextureAtlas to delete\n\t */\n\tdeleteTexture2D(texture) {\n\t\tif (typeof texture.getTexture === \"function\") {\n\t\t\t// Iterate every atlas registered under this image — post-#1448,\n\t\t\t// the multimap can hold multiple atlases per image — and, per\n\t\t\t// atlas, EVERY (source, repeat) unit: a single atlas can own\n\t\t\t// several per-repeat units via the per-use wrap override\n\t\t\t// (meshes' `textureRepeat`, #1503), which a per-repeat\n\t\t\t// `peekUnit` lookup (keyed on the atlas's current `repeat`\n\t\t\t// field) would miss. `cache.delete(image)` below frees all of\n\t\t\t// those units; any GL texture left behind in\n\t\t\t// `boundTextures[unit]` would make a later allocation of the\n\t\t\t// same unit look \"already uploaded\" and bind a stale texture.\n\t\t\tconst image = texture.getTexture();\n\t\t\tconst cache = this.renderer.cache;\n\t\t\t// The store owns the handle since #1585, so it has to do the\n\t\t\t// freeing: deleting it here left the store holding a record whose\n\t\t\t// texture no longer existed, and the next resolve for this source\n\t\t\t// would hand that dead handle straight back to a draw.\n\t\t\tconst record = this.renderer.textureStore?.peek(image);\n\t\t\tif (record !== undefined) {\n\t\t\t\tthis.unbindTexture2D(record.handle);\n\t\t\t}\n\t\t\tthis.renderer.textureStore?.destroyTexture(image);\n\t\t\tif (cache.has(image)) {\n\t\t\t\tfor (const atlas of cache.cache.get(image)) {\n\t\t\t\t\tfor (const unit of cache.peekAllUnits(atlas)) {\n\t\t\t\t\t\t// drop the per-unit belief too, or a later allocation of\n\t\t\t\t\t\t// the same unit looks \"already bound\" and samples nothing\n\t\t\t\t\t\tthis.invalidateUnit(unit);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcache.delete(image);\n\t\t} else {\n\t\t\tthis.gl.deleteTexture(texture);\n\t\t\tthis.unbindTexture2D(texture);\n\t\t}\n\t}\n\n\t/**\n\t * returns the WebGL texture associated to the given texture unit\n\t * @param {number} unit - Texture unit to which a texture is bound\n\t * @returns {WebGLTexture} texture a WebGL texture\n\t */\n\tgetTexture2D(unit) {\n\t\treturn this.boundTextures[unit];\n\t}\n\n\t/**\n\t * assign the given WebGL texture to the current batch\n\t * @param {WebGLTexture} texture - a WebGL texture\n\t * @param {number} unit - Texture unit to which the given texture is bound\n\t */\n\tbindTexture2D(texture, unit, flush = true) {\n\t\tconst gl = this.gl;\n\n\t\tif (texture !== this.boundTextures[unit]) {\n\t\t\tif (flush) {\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t\tif (this.currentTextureUnit !== unit) {\n\t\t\t\tthis.currentTextureUnit = unit;\n\t\t\t\tgl.activeTexture(gl.TEXTURE0 + unit);\n\t\t\t}\n\t\t\tgl.bindTexture(gl.TEXTURE_2D, texture);\n\t\t\tthis.boundTextures[unit] = texture;\n\t\t} else if (this.currentTextureUnit !== unit) {\n\t\t\tif (flush) {\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t\tthis.currentTextureUnit = unit;\n\t\t\tgl.activeTexture(gl.TEXTURE0 + unit);\n\t\t}\n\t}\n\n\t/**\n\t * unbind the given WebGL texture, forcing it to be reuploaded\n\t * @param {WebGLTexture} [texture] - a WebGL texture\n\t * @param {number} [unit] - Texture unit to unbind from\n\t * @returns {number} unit the unit number that was associated with the given texture\n\t */\n\t/**\n\t * Announce that the SOURCE content behind `unit` changed (same object,\n\t * new pixels — a canvas re-bake): the next {@link MaterialBatcher#uploadTexture}\n\t * for it re-uploads into the existing texture in place instead of\n\t * discarding it. The allocation-preserving counterpart of\n\t * {@link MaterialBatcher#unbindTexture2D}.\n\t * @param {number} unit - the texture unit whose content is stale\n\t * @ignore\n\t */\n\tmarkTextureDirty(unit) {\n\t\tthis.dirtyUnits.add(unit);\n\t}\n\n\tunbindTexture2D(texture, unit) {\n\t\tif (typeof unit === \"undefined\") {\n\t\t\tunit = this.boundTextures.indexOf(texture);\n\t\t}\n\t\tif (unit !== -1) {\n\t\t\tdelete this.boundTextures[unit];\n\t\t\tif (unit === this.currentTextureUnit) {\n\t\t\t\tthis.currentTextureUnit = -1;\n\t\t\t}\n\t\t}\n\t\treturn unit;\n\t}\n\n\t/**\n\t * Forget whatever texture this batcher believes is bound to `unit`, so the\n\t * next bind to it re-issues the GL bind. Used when a GL texture unit is\n\t * clobbered OUTSIDE this batcher's own accounting — e.g.\n\t * {@link WebGLRenderer#toFrameTexture}, which binds its capture to a scratch\n\t * unit directly (not via the shared texture cache), so a different batcher's\n\t * unit cache would otherwise skip a needed re-bind. Subclasses that pair\n\t * extra samplers to the same unit (lit normal maps) override to drop those\n\t * too.\n\t * @param {number} unit - the GL texture unit to invalidate\n\t * @ignore\n\t */\n\tinvalidateUnit(unit) {\n\t\tdelete this.boundTextures[unit];\n\t\tif (this.currentTextureUnit === unit) {\n\t\t\tthis.currentTextureUnit = -1;\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t * @param {TextureAtlas|TextureResource} texture\n\t * @param {number} [w] - ignored when the source has its own `width` (the\n\t *   common case); kept for the legacy signature where callers passed a\n\t *   destination size. Forwarded only as a last-resort default.\n\t * @param {number} [h] - same as `w`.\n\t * @param {boolean} [force=false]\n\t * @param {boolean} [flush=true]\n\t * @param {string} [repeat] - per-use wrap-mode override (a mesh's\n\t *   `textureRepeat`, #1503) — sampled with this wrap without mutating\n\t *   the shared atlas's `repeat`. The texture-unit cache keys by\n\t *   `(source, repeat)`, so each wrap gets its own unit + GL texture.\n\t *   Omit to use `texture.repeat`.\n\t */\n\tuploadTexture(texture, w, h, force = false, flush = true, repeat) {\n\t\tconst wrap = typeof repeat === \"string\" ? repeat : texture.repeat;\n\t\tconst unit = this.renderer.cache.getUnit(texture, wrap);\n\t\t// honor a resource-specified filter (e.g. tilemap index textures need\n\t\t// NEAREST regardless of the global setting, or a Mesh's own\n\t\t// `textureFilter`), otherwise fall back to the renderer-wide default\n\t\t// (the `textureFilter` setting, decoupled from MSAA — see\n\t\t// WebGLRenderer#getDefaultTextureFilter). Resolved before the branch\n\t\t// because the sampler binding below needs it whether or not this call\n\t\t// uploads.\n\t\tlet filter =\n\t\t\ttypeof texture.filter !== \"undefined\"\n\t\t\t\t? texture.filter\n\t\t\t\t: this.renderer._glTextureFilter();\n\t\t// the STRING form (\"nearest\"/\"linear\") is what non-GL renderers store\n\t\t// (the WebGPU texture store consumes it directly) — an atlas that met\n\t\t// one of those first must still upload correctly here, so map it to the\n\t\t// GL enum instead of feeding texParameteri a string\n\t\tif (filter === \"nearest\") {\n\t\t\tfilter = this.gl.NEAREST;\n\t\t} else if (filter === \"linear\") {\n\t\t\tfilter = this.gl.LINEAR;\n\t\t}\n\n\t\t// TWO independent decisions, not one (#1585). Whether this unit already\n\t\t// holds the texture decides a BIND; whether the source's content is\n\t\t// current decides an UPLOAD. Conflating them — which is what reading\n\t\t// `boundTextures[unit]` alone did — meant a texture that merely moved\n\t\t// units was rebuilt from scratch, because the handle was reachable only\n\t\t// through that array and a cache reset cleared it.\n\t\tconst source = texture.getTexture();\n\t\t// `HTMLVideoElement` exposes its real pixel dimensions through\n\t\t// `videoWidth`/`videoHeight`; `width`/`height` default to 0 until the\n\t\t// element is explicitly sized. Prefer the regular width/height when\n\t\t// non-zero, otherwise fall back to the video-specific properties, and\n\t\t// finally to the caller-supplied w/h for sources that have neither.\n\t\tconst texW = source.width || source.videoWidth || w;\n\t\tconst texH = source.height || source.videoHeight || h;\n\t\t// a video with no decoded frame yet (readyState < HAVE_CURRENT_DATA)\n\t\t// has nothing to upload — texImage2D on it is browser-dependent (an\n\t\t// exception on some engines, an empty upload plus a GL error on\n\t\t// others). Allocate a blank texture instead and skip the copy; the\n\t\t// video path force-re-uploads every frame, so content lands the moment\n\t\t// a frame exists — same contract as the WebGPU store.\n\t\tconst frameless =\n\t\t\ttypeof source.videoWidth !== \"undefined\" && source.readyState < 2;\n\n\t\t// `markTextureDirty` announces that the SOURCE behind a unit changed\n\t\t// (same object, new pixels — a canvas re-bake), so it forces a\n\t\t// re-upload. It is a CONTENT signal, not a binding one: a merely stale\n\t\t// binding is handled by the unconditional bind below.\n\t\tconst dirty = this.dirtyUnits.delete(unit);\n\t\tconst version = source.version ?? 0;\n\n\t\t// Fast path: already resident and current. Taken for all but a handful\n\t\t// of the hundreds of quads in a frame, so it must allocate NOTHING —\n\t\t// the options object and the upload closure below are per-call garbage\n\t\t// that the steady state has no use for.\n\t\tlet record = this.renderer.textureStore.peek(source);\n\t\tif (\n\t\t\trecord === undefined ||\n\t\t\tforce === true ||\n\t\t\tdirty === true ||\n\t\t\trecord.version !== version\n\t\t) {\n\t\t\trecord = this.renderer.textureStore.getResidentRecord(source, {\n\t\t\t\tversion,\n\t\t\t\tforce: force === true || dirty === true,\n\t\t\t\tupload: (handle) => {\n\t\t\t\t\treturn this.createTexture2D(\n\t\t\t\t\t\tunit,\n\t\t\t\t\t\tframeless ? null : source,\n\t\t\t\t\t\tfilter,\n\t\t\t\t\t\twrap,\n\t\t\t\t\t\ttexW,\n\t\t\t\t\t\ttexH,\n\t\t\t\t\t\ttexture.premultipliedAlpha,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\thandle,\n\t\t\t\t\t\tflush,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// bind unconditionally — cheap, and the only thing that guarantees this\n\t\t// unit really holds this texture. `bindTexture2D` no-ops when its\n\t\t// bookkeeping already agrees.\n\t\tthis.bindTexture2D(record.handle, unit, flush);\n\n\t\t// The variant (wrap + filter) rides a sampler object rather than the\n\t\t// texture's own parameters, so one upload can serve a source drawn at\n\t\t// several repeat modes. `createTexture2D` still sets the texture\n\t\t// parameters too, which keeps any path that binds no sampler working\n\t\t// exactly as before.\n\t\tthis.renderer.samplerCache.bind(\n\t\t\tunit,\n\t\t\tthis.renderer.samplerCache.get(filter, wrap, false),\n\t\t);\n\n\t\treturn flush ? this.currentTextureUnit : unit;\n\t}\n}\n","import { transformQuadCorners } from \"../../gpu/quadcorners.ts\";\nimport IndexBuffer from \"../buffer/index.js\";\nimport { buildMultiTextureFragment } from \"./../shaders/multitexture.js\";\nimport quadMultiVertex from \"./../shaders/quad-multi.vert\";\nimport { resolveTopology } from \"../utils/topology.js\";\nimport { MaterialBatcher } from \"./material_batcher.js\";\n\n/**\n * additional import for TypeScript\n * @import {TextureAtlas} from \"./../../texture/atlas.js\";\n */\n\n/**\n * A WebGL Compositor object. This class handles all of the WebGL state<br>\n * Pushes texture regions or shape geometry into WebGL buffers, automatically flushes to GPU\n * @category Rendering\n */\nexport default class QuadBatcher extends MaterialBatcher {\n\t/**\n\t * Initialize the compositor\n\t * @ignore\n\t */\n\tinit(renderer) {\n\t\t/**\n\t\t * the maximum number of texture units used for multi-texture batching\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\t// the full pool the renderer resolved (#1585). The 16 that used to cap\n\t\t// this was the WebGL 2 spec FLOOR for MAX_TEXTURE_IMAGE_UNITS — safe\n\t\t// everywhere, and half of what current desktop and mobile hardware\n\t\t// actually reports. `renderer.maxTextures` is already clamped there,\n\t\t// including by the `maxTextures` application setting.\n\t\tthis.maxBatchTextures = renderer.maxTextures;\n\n\t\tsuper.init(renderer, {\n\t\t\tattributes: [\n\t\t\t\t{\n\t\t\t\t\t// vec3: (x, y, z). z carries `renderable.depth` for\n\t\t\t\t\t// perspective projection (Camera3d). Stride = 28 bytes.\n\t\t\t\t\tname: \"aVertex\",\n\t\t\t\t\tformat: \"float32x3\",\n\t\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aRegion\",\n\t\t\t\t\tformat: \"float32x2\",\n\t\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aColor\",\n\t\t\t\t\tformat: \"unorm8x4\",\n\t\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aTextureId\",\n\t\t\t\t\tformat: \"float32\",\n\t\t\t\t\toffset: 6 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t],\n\t\t\tshader: {\n\t\t\t\tvertex: quadMultiVertex,\n\t\t\t\tfragment: buildMultiTextureFragment(this.maxBatchTextures),\n\t\t\t},\n\t\t});\n\n\t\tthis.bindColorSamplers();\n\n\t\t/**\n\t\t * whether multi-texture batching is currently active\n\t\t * (disabled when a custom ShaderEffect is applied)\n\t\t * @type {boolean}\n\t\t * @ignore\n\t\t */\n\t\tthis.useMultiTexture = true;\n\n\t\tthis.createIndexBuffer();\n\t}\n\n\t/**\n\t * (Re-)create the index buffer for quad batching (4 verts + 6 indices per quad).\n\t * Called from `init` and `reset` (after context loss).\n\t * @ignore\n\t */\n\tcreateIndexBuffer() {\n\t\t// The ELEMENT_ARRAY_BUFFER binding is vertex-state state, so the\n\t\t// buffer must be created and captured with THIS batcher's vertex\n\t\t// state current — `captureIndexBuffer` owns that protocol (and\n\t\t// restores whatever was bound, so rebuilding a non-current batcher\n\t\t// never disturbs the in-flight one).\n\t\tthis.vertexState.captureIndexBuffer(undefined, () => {\n\t\t\t// free the previous GL buffer first (reset / context-restore\n\t\t\t// path) — recreating without deleting orphaned one ~12 KB\n\t\t\t// ELEMENT_ARRAY buffer per reset until GC collected the wrapper\n\t\t\tif (this.indexBuffer) {\n\t\t\t\tthis.indexBuffer.destroy();\n\t\t\t}\n\t\t\tconst maxQuads = this.vertexData.maxVertex / 4;\n\t\t\t// Uint32 indices — OES_element_index_uint is core in WebGL 2\n\t\t\tthis.indexBuffer = new IndexBuffer(this.gl, maxQuads * 6, true);\n\t\t\tthis.indexBuffer.fillQuadPattern(maxQuads);\n\t\t\tthis.vertexState.descriptor.indexBuffer = this.indexBuffer;\n\t\t});\n\t}\n\n\t/**\n\t * Bind the color sampler uniforms (`uSampler0..uSamplerN-1`) to their\n\t * respective texture units. Called from `init` and `reset`.\n\t * @ignore\n\t */\n\tbindColorSamplers() {\n\t\tfor (let i = 0; i < this.maxBatchTextures; i++) {\n\t\t\tthis.defaultShader.setUniform(\"uSampler\" + i, i);\n\t\t}\n\t}\n\n\t/**\n\t * Select the shader to use for compositing.\n\t * Multi-texture batching is automatically enabled when the default\n\t * shader is active, and disabled for custom ShaderEffect shaders.\n\t * @see GLShader\n\t * @see ShaderEffect\n\t * @param {GLShader|ShaderEffect} shader - a reference to a GLShader or ShaderEffect instance\n\t */\n\tuseShader(shader) {\n\t\tsuper.useShader(shader);\n\t\tthis.useMultiTexture = shader === this.defaultShader;\n\t}\n\n\t/**\n\t * Reset compositor internal state\n\t * @ignore\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.createIndexBuffer();\n\t\tthis.bindColorSamplers();\n\t\tthis.useMultiTexture = true;\n\t}\n\n\t/**\n\t * Flush batched texture data to the GPU using indexed drawing.\n\t * @param {number} [mode=gl.TRIANGLES] - the GL drawing mode\n\t */\n\tflush(mode = this.mode) {\n\t\t// this override has its own drawElements and does not chain to\n\t\t// super.flush, so it must normalize independently\n\t\tif (typeof mode === \"string\") {\n\t\t\tmode = resolveTopology(this.gl, mode);\n\t\t}\n\t\tconst vertex = this.vertexData;\n\t\tconst vertexCount = vertex.vertexCount;\n\n\t\tif (vertexCount > 0) {\n\t\t\tconst gl = this.gl;\n\t\t\tconst vertexSize = vertex.vertexSize;\n\n\t\t\tthis.syncProgram();\n\n\t\t\t// (index buffer binding is captured in the vertex state — no\n\t\t\t// per-flush rebind)\n\n\t\t\t// Byte-view upload — keeps packed-color bytes intact across\n\t\t\t// drivers that canonicalize NaN-pattern Float32 values on\n\t\t\t// upload (see `VertexArrayBuffer.bufferU8`).\n\t\t\tconst byteLength =\n\t\t\t\tvertexCount * vertexSize * Float32Array.BYTES_PER_ELEMENT;\n\t\t\tgl.bufferData(\n\t\t\t\tgl.ARRAY_BUFFER,\n\t\t\t\tvertex.toUint8(),\n\t\t\t\tgl.STREAM_DRAW,\n\t\t\t\t0,\n\t\t\t\tbyteLength,\n\t\t\t);\n\n\t\t\t// 4 vertices per quad -> vertexCount/4 quads -> *6 indices per quad\n\t\t\tconst indexCount = (vertexCount / 4) * 6;\n\t\t\tgl.drawElements(mode, indexCount, this.indexBuffer.type, 0);\n\n\t\t\tvertex.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Draw a screen-aligned quad with the given raw WebGL texture through the given shader.\n\t * Binds the texture to unit 0, pushes 4 vertices (Y-flipped UVs), flushes,\n\t * then unbinds the texture.\n\t * @param {WebGLTexture} source - the raw GL texture to blit\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} width - destination width\n\t * @param {number} height - destination height\n\t * @param {GLShader|ShaderEffect} shader - the shader effect to apply\n\t */\n\tblitTexture(source, x, y, width, height, shader) {\n\t\tconst gl = this.gl;\n\n\t\tthis.useShader(shader);\n\n\t\t// bind the source texture to unit 0 — keep the batcher's internal\n\t\t// texture-unit bookkeeping in sync with the actual GL state, or\n\t\t// later `bindTexture2D` calls may early-out on a stale\n\t\t// `currentTextureUnit` and bind to the wrong unit.\n\t\tgl.activeTexture(gl.TEXTURE0);\n\t\tgl.bindTexture(gl.TEXTURE_2D, source);\n\t\tthis.currentTextureUnit = 0;\n\t\tthis.boundTextures[0] = source;\n\t\tshader.setUniform(\"uSampler\", 0);\n\n\t\t// `noise_uv` builtin: a blit is a full-frame quad — identity rect\n\t\tshader._setNoiseUVRect?.(width, height, width, height, 0, 0);\n\n\t\t// the blit samples a bottom-up capture FBO (the Y-flipped UVs\n\t\t// below): uv.y grows UPWARD inside apply(), so directional bodies\n\t\t// (DropShadow) flip their y arithmetic to keep \"down\" down\n\t\tshader._setUVYDir?.(-1);\n\n\t\t// (re)bind any extra textures a ShaderEffect declared via setTexture,\n\t\t// to their reserved high units — after the source claims unit 0\n\t\tshader._prepareTextures?.(this);\n\n\t\t// push a screen-aligned quad with Y-flipped UVs, transformed by\n\t\t// the current renderer transform. `WebGLRenderer.blitEffect` (the\n\t\t// only caller today) resets `currentTransform` to identity before\n\t\t// calling so FBO blits land in screen space; the matrix path is\n\t\t// kept for any future world-space caller that wants its preDraw\n\t\t// translate/scale honored.\n\t\t// blits are always at z = 0 (screen-space) — the shared corner\n\t\t// pool is Vector3d, so the explicit z stops leftover depth from a\n\t\t// prior addQuad leaking into the transform\n\t\tconst [vec0, vec1, vec2, vec3] = transformQuadCorners(\n\t\t\tthis.viewMatrix,\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight,\n\t\t\t0,\n\t\t);\n\n\t\t// blits stay at z = 0 (screen-space, ortho); pre-PR behavior\n\t\t// preserved unchanged\n\t\tconst tint = 0xffffffff;\n\t\tthis.vertexData.push(vec0.x, vec0.y, 0, 0, 1, tint, 0);\n\t\tthis.vertexData.push(vec1.x, vec1.y, 0, 1, 1, tint, 0);\n\t\tthis.vertexData.push(vec2.x, vec2.y, 0, 0, 0, tint, 0);\n\t\tthis.vertexData.push(vec3.x, vec3.y, 0, 1, 0, tint, 0);\n\n\t\tthis.flush();\n\n\t\t// unbind the texture to prevent feedback loop on next frame, and drop\n\t\t// EVERY batcher's unit-0 slot — unit 0's GL binding is now null, and a\n\t\t// non-current batcher trusting its stale record would skip the re-bind\n\t\t// and sample an unbound texture (opaque black)\n\t\tgl.activeTexture(gl.TEXTURE0);\n\t\tgl.bindTexture(gl.TEXTURE_2D, null);\n\t\tthis.currentTextureUnit = -1;\n\t\tthis.renderer.invalidateTextureUnit(0);\n\n\t\t// restore the default shader (also re-enables multi-texture batching)\n\t\tthis.useShader(this.defaultShader);\n\t}\n\n\t/**\n\t * Add a textured quad\n\t * @param {TextureAtlas} texture - Source texture atlas\n\t * @param {number} x - Destination x-coordinate\n\t * @param {number} y - Destination y-coordinate\n\t * @param {number} w - Destination width\n\t * @param {number} h - Destination height\n\t * @param {number} u0 - Texture UV (u0) value.\n\t * @param {number} v0 - Texture UV (v0) value.\n\t * @param {number} u1 - Texture UV (u1) value.\n\t * @param {number} v1 - Texture UV (v1) value.\n\t * @param {number} tint - tint color to be applied to the texture in UINT32 (argb) format\n\t * @param {boolean} [reupload=false] - Force the texture to be reuploaded even if already bound\n\t */\n\taddQuad(texture, x, y, w, h, u0, v0, u1, v1, tint, reupload = false) {\n\t\tconst vertexData = this.vertexData;\n\n\t\tif (vertexData.isFull(4)) {\n\t\t\tthis.flush();\n\t\t}\n\n\t\tlet unit;\n\n\t\tif (this.useMultiTexture) {\n\t\t\t// multi-texture path: embed the texture unit in the vertex data\n\t\t\t// and avoid flushing on texture changes\n\t\t\tunit = this.uploadTexture(texture, w, h, reupload, false);\n\t\t\t// shader only supports maxBatchTextures samplers — flush and\n\t\t\t// reset if the cache assigned a unit beyond the shader's range\n\t\t\t// Desync guard, not the normal path — see LitQuadBatcher.addQuad.\n\t\t\tif (unit >= this.maxBatchTextures) {\n\t\t\t\tthis.flush();\n\t\t\t\tthis.renderer.cache.resetUnitAssignments();\n\t\t\t\tunit = this.uploadTexture(texture, w, h, reupload, false);\n\t\t\t}\n\t\t} else {\n\t\t\t// single-texture fallback (custom ShaderEffect active):\n\t\t\t// use regular upload which flushes on texture change, and set uSampler\n\t\t\tunit = this.uploadTexture(texture, w, h, reupload);\n\t\t\tif (unit !== this.currentSamplerUnit) {\n\t\t\t\tthis.currentShader.setUniform(\"uSampler\", unit);\n\t\t\t\tthis.currentSamplerUnit = unit;\n\t\t\t}\n\t\t\t// feed the effect's `noise_uv` builtin (frame-local UVs) with this\n\t\t\t// quad's frame rect — min() normalizes flipped (swapped) UVs\n\t\t\tif (typeof this.currentShader._setNoiseUVRect === \"function\") {\n\t\t\t\tconst source = texture.getTexture();\n\t\t\t\tthis.currentShader._setNoiseUVRect(\n\t\t\t\t\tsource.width || source.videoWidth || 1,\n\t\t\t\t\tsource.height || source.videoHeight || 1,\n\t\t\t\t\tw,\n\t\t\t\t\th,\n\t\t\t\t\tMath.min(u0, u1),\n\t\t\t\t\tMath.min(v0, v1),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// direct atlas sampling: uv.y grows downward (directional bodies\n\t\t\t// compensate on the pooled path, where captures are bottom-up)\n\t\t\tthis.currentShader._setUVYDir?.(1);\n\t\t}\n\n\t\t// Transform vertices. Stamp per-sprite depth onto z BEFORE\n\t\t// `m.apply` so Camera3d's view matrix (3D R⁻¹ ∘ T(-pos)) fully\n\t\t// rotates the vertex. For 2D-only matrices the z column is\n\t\t// identity, so output (x, y) is bit-identical to the legacy\n\t\t// Vector2d path and z passes through unchanged.\n\t\tconst [vec0, vec1, vec2, vec3] = transformQuadCorners(\n\t\t\tthis.viewMatrix,\n\t\t\tx,\n\t\t\ty,\n\t\t\tw,\n\t\t\th,\n\t\t\tthis.renderer.currentDepth,\n\t\t);\n\n\t\t// 4 vertices per quad; the index buffer provides the 6 indices.\n\t\t// textureId is the unit index for multi-texture, or 0 for\n\t\t// single-texture fallback.\n\t\tconst textureId = this.useMultiTexture ? unit : 0;\n\t\tvertexData.push(vec0.x, vec0.y, vec0.z, u0, v0, tint, textureId);\n\t\tvertexData.push(vec1.x, vec1.y, vec1.z, u1, v0, tint, textureId);\n\t\tvertexData.push(vec2.x, vec2.y, vec2.z, u0, v1, tint, textureId);\n\t\tvertexData.push(vec3.x, vec3.y, vec3.z, u1, v1, tint, textureId);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n/**\n * additional import for TypeScript\n * @import { Color } from \"../../math/color.ts\";\n * @import { default as WebGLRenderer } from \"../webgl_renderer.js\";\n */\n\n/**\n * A procedural radial-gradient shader effect: solid color at the center\n * fading linearly to fully transparent at the edge of the host quad.\n * The falloff is naturally elliptical for non-square quads.\n *\n * **UV-space caveat.** The `apply(color, uv)` function receives\n * `vRegion` — the atlas UVs of the host quad. The falloff math\n * (`length(uv * 2 - 1)`) assumes those UVs span `[0, 1] × [0, 1]`,\n * which is true when the quad samples a full-rect texture (a\n * dedicated 1×1 white pixel, a non-atlased Sprite, an FBO blit, or\n * the engine-provided light atlas used by `WebGLRenderer.drawLight`).\n * If you attach this effect to a Sprite that uses a *sub-region* of a\n * larger atlas, `uv` will be in `[u0..u1] × [v0..v1]` and the radial\n * center will be misplaced. For atlas-based renderables, set the\n * effect's `uColor`/`uIntensity` and pair it with a Sprite whose\n * texture covers a full atlas, or use a Sprite created from a\n * standalone image.\n *\n * The falloff curve is **linear** (`f = clamp(1 - d, 0, 1)`) to match\n * the Canvas 2D `createRadialGradient` two-stop output exactly. Output\n * is premultiplied so the result composes correctly under additive\n * (`\"lighter\"`) blending across overlapping quads.\n *\n * Color & intensity come from **two stacked sources**, multiplied\n * together: the `uColor`/`uIntensity` uniforms (set per-effect via\n * `setColor` / `setIntensity` — the natural API for a single-instance\n * shader attached to a renderable) AND the per-vertex tint coming\n * through `aColor` (used by `WebGLRenderer.drawLight` to encode each\n * light's color + intensity in the vertex stream so multiple lights\n * sharing this shader can batch into a single draw call). For typical\n * standalone usage the per-vertex tint is `(1,1,1,1)` and the uniforms\n * drive the look; for the Light2d batching path the uniforms stay at\n * defaults and the tint carries everything.\n * @category Effects\n * @example\n * // Soft white spot, 50% peak alpha at center\n * const spot = new RadialGradientEffect(renderer, { intensity: 0.5 });\n * @example\n * // Tinted hotspot — orange center, full brightness, sized via the\n * // host quad's bounds\n * const hot = new RadialGradientEffect(renderer, {\n *     color: new Color(255, 128, 64),\n *     intensity: 1.0,\n * });\n * hot.setIntensity(2.0); // pulse brighter at runtime\n * @example\n * // Pickup highlight — attach to any Renderable so it renders inside\n * // the renderable's bounding rect (anchorPoint applies). Combine with\n * // `blendMode = \"lighter\"` for the additive glow look.\n * pickup.shader = new RadialGradientEffect(renderer, {\n *     color: new Color(120, 255, 200), // mint green\n *     intensity: 0.8,\n * });\n * pickup.blendMode = \"lighter\";\n * @example\n * // Damage / impact indicator — short-lived elliptical flash on hit.\n * // The quad's width/height drive the falloff aspect for free.\n * const flash = new RadialGradientEffect(renderer, {\n *     color: new Color(255, 32, 32),\n *     intensity: 1.5,\n * });\n * // animate intensity to fade out\n * tween.to({ intensity: 0 }, 200).onUpdate((s) => flash.setIntensity(s.intensity));\n * @example\n * // Debug overlay — draw a soft circle wherever the player is to mark\n * // a trigger zone, without baking a texture per zone.\n * const zoneMarker = new RadialGradientEffect(renderer, {\n *     color: new Color(80, 160, 255),\n *     intensity: 0.4,\n * });\n */\nexport default class RadialGradientEffect extends ShaderEffect {\n\t/**\n\t * @param {WebGLRenderer} renderer - the current renderer instance\n\t * @param {object} [options] - initial uniform values\n\t * @param {Color} [options.color] - center color (0..255 RGB); defaults to white\n\t * @param {number} [options.intensity=1] - peak alpha at the center (0..1+)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uColor;\n\t\t\tuniform float uIntensity;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\t// recenter to [-1, 1] across the quad. The quad's own aspect\n\t\t\t\t// ratio handles elliptical falloffs naturally — length(c) == 1\n\t\t\t\t// lies on the inscribed ellipse in world space.\n\t\t\t\tvec2 c = uv * 2.0 - 1.0;\n\t\t\t\tfloat d = length(c);\n\t\t\t\t// linear ramp matches Canvas createRadialGradient's two-stop output\n\t\t\t\tfloat f = clamp(1.0 - d, 0.0, 1.0);\n\t\t\t\t// 'color' is the per-vertex tint, already premultiplied by\n\t\t\t\t// alpha in the vertex shader (vColor = vec4(aColor.bgr *\n\t\t\t\t// aColor.a, aColor.a)). For standalone use the tint is\n\t\t\t\t// (1,1,1,1) and the uniforms drive the look; for the Light2d\n\t\t\t\t// batching path the uniforms stay at default and the tint\n\t\t\t\t// carries the per-light color + intensity.\n\t\t\t\tvec3 rgb = color.rgb * uColor * uIntensity * f;\n\t\t\t\tfloat a = color.a * uIntensity * f;\n\t\t\t\treturn vec4(rgb, a);\n\t\t\t}\n\t\t\t`,\n\t\t\t// the WGSL twin — same logic and uniform names; `color` arrives\n\t\t\t// tinted (textureSample * vColor), identical to the GLSL contract\n\t\t\twgsl: `\n\t\t\tstruct RadialGradientUniforms {\n\t\t\t\tuColor : vec3f,\n\t\t\t\tuIntensity : f32,\n\t\t\t};\n\t\t\t@group(3) @binding(0) var<uniform> fx : RadialGradientUniforms;\n\n\t\t\tfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\t\t\t\tlet c = uv * 2.0 - vec2f(1.0);\n\t\t\t\tlet d = length(c);\n\t\t\t\tlet f = clamp(1.0 - d, 0.0, 1.0);\n\t\t\t\tlet rgb = color.rgb * fx.uColor * fx.uIntensity * f;\n\t\t\t\tlet a = color.a * fx.uIntensity * f;\n\t\t\t\treturn vec4f(rgb, a);\n\t\t\t}\n\t\t\t`,\n\t\t});\n\n\t\t// reused across `setColor` calls so we don't allocate a fresh\n\t\t// 3-element array every frame on every light.\n\t\tthis._colorBuf = new Float32Array(3);\n\n\t\tconst color = options.color;\n\t\tif (color) {\n\t\t\tthis.setColor(color);\n\t\t} else {\n\t\t\tthis._colorBuf[0] = 1;\n\t\t\tthis._colorBuf[1] = 1;\n\t\t\tthis._colorBuf[2] = 1;\n\t\t\tthis.setUniform(\"uColor\", this._colorBuf);\n\t\t}\n\t\tthis.setIntensity(options.intensity ?? 1);\n\t}\n\n\t/**\n\t * Set the center color. RGB only — alpha is ignored (the radial\n\t * falloff supplies the per-pixel alpha).\n\t * @param {Color} color - 0..255 RGB color\n\t */\n\tsetColor(color) {\n\t\tthis._colorBuf[0] = color.r / 255;\n\t\tthis._colorBuf[1] = color.g / 255;\n\t\tthis._colorBuf[2] = color.b / 255;\n\t\tthis.setUniform(\"uColor\", this._colorBuf);\n\t}\n\n\t/**\n\t * Set the peak intensity. Acts as a brightness multiplier on the\n\t * falloff curve; values above 1 over-saturate the center of the gradient.\n\t * @param {number} intensity - 0..1+ multiplier\n\t */\n\tsetIntensity(intensity) {\n\t\tthis.setUniform(\"uIntensity\", intensity);\n\t}\n}\n","/**\n * @import RenderTarget from \"./rendertarget.ts\";\n */\n\n/**\n * Manages a pool of {@link RenderTarget} instances for post-effect processing.\n * Renderer-agnostic — the actual RenderTarget creation is delegated to a\n * factory function provided by the renderer (WebGL, WebGPU, etc.).\n *\n * Camera effects use pool indices 0 and 1 (capture + ping-pong); sprite\n * effects use indices 2 and 3, and each NESTED sprite pass (a multi-effect\n * renderable drawn inside another multi-effect renderable) gets its own pair\n * above that (4/5, 6/7, …), tracked by a stack of active bases so begin/end\n * pairs compose like save/restore.\n * Render targets are lazily created and resized to match the required dimensions.\n * @ignore\n */\nexport default class RenderTargetPool {\n\t/**\n\t * @param {function(number, number, boolean=): RenderTarget} factory - creates a RenderTarget with the given width and height; the third argument is `true` for CAPTURE slots (scene rasterization — may be multisampled) and `false` for ping-pong intermediates\n\t */\n\tconstructor(factory) {\n\t\t/** @type {function(number, number): RenderTarget} */\n\t\tthis._factory = factory;\n\t\t/** @type {RenderTarget[]} */\n\t\tthis._pool = [];\n\t\t/**\n\t\t * active pass bases, innermost last — a STACK, so nested begin/end\n\t\t * pairs unwind correctly (two scalars silently corrupted the pool on\n\t\t * nested passes: the inner end() popped the outer pass's slot)\n\t\t * @type {number[]}\n\t\t */\n\t\tthis._baseStack = [];\n\t}\n\n\t/**\n\t * The base index of the innermost active pass, or -1 when none.\n\t * @returns {number}\n\t */\n\tget activeBase() {\n\t\treturn this._baseStack.length > 0\n\t\t\t? this._baseStack[this._baseStack.length - 1]\n\t\t\t: -1;\n\t}\n\n\t/**\n\t * Get or create a render target at the given pool index, resized to the given dimensions.\n\t * @param {number} index - pool index\n\t * @param {number} width - desired width in pixels\n\t * @param {number} height - desired height in pixels\n\t * @returns {RenderTarget} the render target\n\t */\n\tget(index, width, height) {\n\t\tif (!this._pool[index]) {\n\t\t\t// even indices are CAPTURE targets (the scene rasterizes into\n\t\t\t// them — factories may give those MSAA); odd indices are\n\t\t\t// ping-pong intermediates (screen-aligned effect quads only,\n\t\t\t// nothing to antialias — always single-sampled)\n\t\t\tthis._pool[index] = this._factory(width, height, index % 2 === 0);\n\t\t} else {\n\t\t\tthis._pool[index].resize(width, height);\n\t\t}\n\t\treturn this._pool[index];\n\t}\n\n\t/**\n\t * Prepare render targets for a post-effect pass.\n\t * Allocates/resizes the capture target and optionally the ping-pong target.\n\t * @param {boolean} isCamera - true for camera effects (indices 0+1), false for sprite (indices 2+3)\n\t * @param {number} effectCount - number of enabled effects\n\t * @param {number} width - target width in pixels\n\t * @param {number} height - target height in pixels\n\t * @returns {RenderTarget} the capture target (ready to bind)\n\t */\n\tbegin(isCamera, effectCount, width, height) {\n\t\tlet newBase;\n\t\tif (isCamera) {\n\t\t\tnewBase = 0;\n\t\t} else {\n\t\t\t// each nested sprite pass gets its own capture/ping-pong pair\n\t\t\t// (2/3, then 4/5, 6/7, … — lazily allocated)\n\t\t\tlet depth = 0;\n\t\t\tfor (const base of this._baseStack) {\n\t\t\t\tif (base >= 2) {\n\t\t\t\t\tdepth++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewBase = 2 + depth * 2;\n\t\t}\n\t\tthis._baseStack.push(newBase);\n\t\tconst rt = this.get(newBase, width, height);\n\t\tif (effectCount > 1) {\n\t\t\tthis.get(newBase + 1, width, height);\n\t\t}\n\t\treturn rt;\n\t}\n\n\t/**\n\t * Get the capture render target for the current active pass.\n\t * @returns {RenderTarget|undefined} the capture target, or undefined if no active pass\n\t */\n\tgetCaptureTarget() {\n\t\tconst base = this.activeBase;\n\t\tif (base < 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this._pool[base];\n\t}\n\n\t/**\n\t * Get the ping-pong render target for the current active pass.\n\t * @returns {RenderTarget|undefined} the ping-pong target, or undefined if no active pass\n\t */\n\tgetPingPongTarget() {\n\t\tconst base = this.activeBase;\n\t\tif (base < 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn this._pool[base + 1];\n\t}\n\n\t/**\n\t * End the current pass and restore the previous active base.\n\t * Returns the parent render target to rebind (or null for screen).\n\t * @returns {RenderTarget|null} the parent target, or null if returning to screen\n\t */\n\tend() {\n\t\tthis._baseStack.pop();\n\t\tconst base = this.activeBase;\n\t\tif (base >= 0 && this._pool[base]) {\n\t\t\treturn this._pool[base];\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Resize all existing render targets in the pool to the given dimensions.\n\t * @param {number} width - new width in pixels\n\t * @param {number} height - new height in pixels\n\t */\n\tresizeAll(width, height) {\n\t\tfor (const rt of this._pool) {\n\t\t\tif (rt) {\n\t\t\t\trt.resize(width, height);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Destroy all render targets and clear the pool.\n\t */\n\tdestroy() {\n\t\tfor (const rt of this._pool) {\n\t\t\tif (rt) {\n\t\t\t\trt.destroy();\n\t\t\t}\n\t\t}\n\t\tthis._pool.length = 0;\n\t\tthis._baseStack.length = 0;\n\t}\n}\n","import RenderTarget from \"./rendertarget.ts\";\n\n/**\n * Try to attach a depth+stencil (preferred) or depth-only renderbuffer to\n * the currently-bound framebuffer, validating completeness at each step.\n *\n * Returns `{ hasStencil, isComplete }`:\n * - `hasStencil = true` only if packed depth+stencil attachment succeeded\n * - `isComplete = false` means even depth-only failed → callers should not\n *   render into the FBO (post-effects, blits, etc. will fail)\n * @ignore\n */\nfunction attachDepthStencil(\n\tgl,\n\tframebuffer,\n\trenderbuffer,\n\twidth,\n\theight,\n\tsamples = 0,\n) {\n\t// core WebGL 2 constants — always present on the context (the WebGL 1\n\t// driver-gap numeric fallbacks were removed with WebGL 1 support).\n\t// Multisampled storage needs the SIZED format; single-sampled keeps the\n\t// legacy unsized token (identical layout, broadest driver compatibility)\n\tconst depthStencil = samples > 0 ? gl.DEPTH24_STENCIL8 : gl.DEPTH_STENCIL;\n\tconst depthStencilAttachment = gl.DEPTH_STENCIL_ATTACHMENT;\n\tconst storage = (format) => {\n\t\tif (samples > 0) {\n\t\t\tgl.renderbufferStorageMultisample(\n\t\t\t\tgl.RENDERBUFFER,\n\t\t\t\tsamples,\n\t\t\t\tformat,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t} else {\n\t\t\tgl.renderbufferStorage(gl.RENDERBUFFER, format, width, height);\n\t\t}\n\t};\n\n\t// preferred path: packed depth + stencil so masking works\n\tgl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);\n\tstorage(depthStencil);\n\tgl.framebufferRenderbuffer(\n\t\tgl.FRAMEBUFFER,\n\t\tdepthStencilAttachment,\n\t\tgl.RENDERBUFFER,\n\t\trenderbuffer,\n\t);\n\tif (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {\n\t\treturn { hasStencil: true, isComplete: true };\n\t}\n\n\t// fallback: depth-only — masking unavailable, but the FBO is still usable\n\t// for plain post-effect / blit pipelines\n\tgl.framebufferRenderbuffer(\n\t\tgl.FRAMEBUFFER,\n\t\tdepthStencilAttachment,\n\t\tgl.RENDERBUFFER,\n\t\tnull,\n\t);\n\tstorage(gl.DEPTH_COMPONENT16);\n\tgl.framebufferRenderbuffer(\n\t\tgl.FRAMEBUFFER,\n\t\tgl.DEPTH_ATTACHMENT,\n\t\tgl.RENDERBUFFER,\n\t\trenderbuffer,\n\t);\n\tconst status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n\treturn {\n\t\thasStencil: false,\n\t\tisComplete: status === gl.FRAMEBUFFER_COMPLETE,\n\t};\n}\n\n/**\n * A WebGL Framebuffer Object (FBO) render target for offscreen rendering.\n * Used by the post-processing pipeline to render a camera's output to a texture,\n * which can then be drawn to the screen through a post-process shader.\n * @augments RenderTarget\n * @ignore\n */\nexport default class WebGLRenderTarget extends RenderTarget {\n\t/**\n\t * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - the WebGL context\n\t * @param {number} width - initial width in pixels\n\t * @param {number} height - initial height in pixels\n\t */\n\t/**\n\t * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - the WebGL context\n\t * @param {number} width - initial width in pixels\n\t * @param {number} height - initial height in pixels\n\t * @param {object} [options] - target options\n\t * @param {number} [options.samples=0] - MSAA sample count for the scene\n\t * rasterization. When > 0 the target keeps a multisampled render FBO\n\t * (color + depth-stencil renderbuffers) that draws rasterize into, and\n\t * resolves it into the sampleable texture via `blitFramebuffer` on\n\t * {@link WebGLRenderTarget#resolve} (called automatically by `unbind`).\n\t */\n\tconstructor(gl, width, height, options = {}) {\n\t\tsuper();\n\t\tthis.gl = gl;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\n\t\t/**\n\t\t * MSAA sample count of the render half (0 = single-sampled)\n\t\t * @type {number}\n\t\t */\n\t\tthis.samples = options.samples ?? 0;\n\n\t\t// the render half needs a resolve before the texture is sampled\n\t\tthis._needsResolve = false;\n\n\t\t// create the (resolve) framebuffer — the one whose color TEXTURE\n\t\t// the effect chain samples\n\t\tthis.framebuffer = gl.createFramebuffer();\n\n\t\t// create color texture — use TEXTURE0 explicitly to avoid corrupting\n\t\t// other texture units that the multi-texture batcher may have active.\n\t\t// Save/restore the active unit so the batcher's cache stays in sync.\n\t\tconst prevUnit = gl.getParameter(gl.ACTIVE_TEXTURE);\n\t\tthis._allocateColorTexture(width, height);\n\n\t\t// create depth+stencil renderbuffer (needed for masks). It attaches\n\t\t// to whichever framebuffer draws actually rasterize into — the\n\t\t// multisampled render FBO when MSAA is on, the plain one otherwise.\n\t\tthis.depthStencilBuffer = gl.createRenderbuffer();\n\n\t\tif (this.samples > 0) {\n\t\t\t// the multisampled render half: color renderbuffer + the\n\t\t\t// depth-stencil above, resolved into `framebuffer`'s texture\n\t\t\tthis.renderFramebuffer = gl.createFramebuffer();\n\t\t\tthis.colorRenderbuffer = gl.createRenderbuffer();\n\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, this.renderFramebuffer);\n\t\t\tgl.bindRenderbuffer(gl.RENDERBUFFER, this.colorRenderbuffer);\n\t\t\tgl.renderbufferStorageMultisample(\n\t\t\t\tgl.RENDERBUFFER,\n\t\t\t\tthis.samples,\n\t\t\t\tgl.RGBA8,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\tgl.framebufferRenderbuffer(\n\t\t\t\tgl.FRAMEBUFFER,\n\t\t\t\tgl.COLOR_ATTACHMENT0,\n\t\t\t\tgl.RENDERBUFFER,\n\t\t\t\tthis.colorRenderbuffer,\n\t\t\t);\n\t\t\tthis._applyDepthStencil(width, height);\n\t\t} else {\n\t\t\tthis.renderFramebuffer = null;\n\t\t\tthis.colorRenderbuffer = null;\n\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\t\t\tthis._applyDepthStencil(width, height);\n\t\t}\n\n\t\t// unbind and restore the previously active texture unit\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\t\tgl.bindRenderbuffer(gl.RENDERBUFFER, null);\n\t\tgl.bindTexture(gl.TEXTURE_2D, null);\n\t\tgl.activeTexture(prevUnit);\n\t}\n\n\t/**\n\t * (Re)create the sampleable color texture with immutable storage\n\t * (`texStorage2D` — the driver skips completeness validation and the\n\t * layout is explicit) and attach it to the resolve framebuffer.\n\t * Immutable storage can never be respecified, so a resize replaces the\n\t * texture object. Caller manages ACTIVE_TEXTURE save/restore.\n\t * @ignore\n\t */\n\t_allocateColorTexture(width, height) {\n\t\tconst gl = this.gl;\n\t\tif (this.texture) {\n\t\t\tgl.deleteTexture(this.texture);\n\t\t}\n\t\tthis.texture = gl.createTexture();\n\t\tgl.activeTexture(gl.TEXTURE0);\n\t\tgl.bindTexture(gl.TEXTURE_2D, this.texture);\n\t\tgl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\t\tgl.framebufferTexture2D(\n\t\t\tgl.FRAMEBUFFER,\n\t\t\tgl.COLOR_ATTACHMENT0,\n\t\t\tgl.TEXTURE_2D,\n\t\t\tthis.texture,\n\t\t\t0,\n\t\t);\n\t}\n\n\t/**\n\t * (Re)attach the depth/stencil renderbuffer at the given size and\n\t * update `_hasStencil` based on the resulting framebuffer status.\n\t * Falls back to depth-only when packed depth+stencil fails; warns\n\t * once if even depth-only is incomplete.\n\t * @ignore\n\t */\n\t_applyDepthStencil(width, height) {\n\t\tconst result = attachDepthStencil(\n\t\t\tthis.gl,\n\t\t\tthis.framebuffer,\n\t\t\tthis.depthStencilBuffer,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tthis.samples,\n\t\t);\n\t\tthis._hasStencil = result.hasStencil;\n\t\tif (!result.isComplete) {\n\t\t\tconsole.warn(\n\t\t\t\t\"WebGLRenderTarget: framebuffer incomplete after depth-only fallback — rendering into this target may fail\",\n\t\t\t);\n\t\t} else if (!result.hasStencil) {\n\t\t\tconsole.warn(\n\t\t\t\t\"WebGLRenderTarget: depth+stencil attachment failed; using depth-only — stencil masking disabled for this target\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Bind this FBO as the active render target.\n\t * All subsequent draw calls will render into this FBO's texture.\n\t */\n\tbind() {\n\t\t// draws rasterize into the multisampled half when MSAA is on\n\t\tif (this.renderFramebuffer) {\n\t\t\tthis._needsResolve = true;\n\t\t\tthis.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.renderFramebuffer);\n\t\t} else {\n\t\t\tthis.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the multisampled render half into the sampleable texture\n\t * (no-op when single-sampled or already resolved). Leaves no\n\t * framebuffer bound — callers rebind what they need.\n\t */\n\tresolve() {\n\t\tif (!this.renderFramebuffer || this._needsResolve !== true) {\n\t\t\treturn;\n\t\t}\n\t\tconst gl = this.gl;\n\t\tgl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.renderFramebuffer);\n\t\tgl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);\n\t\tgl.blitFramebuffer(\n\t\t\t0,\n\t\t\t0,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t\t0,\n\t\t\t0,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t\tgl.COLOR_BUFFER_BIT,\n\t\t\tgl.NEAREST,\n\t\t);\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\t\tthis._needsResolve = false;\n\t}\n\n\t/**\n\t * Unbind this FBO and restore rendering to the screen — resolving the\n\t * multisampled half first, so `texture` always holds the final pixels\n\t * by the time anything samples it.\n\t */\n\tunbind() {\n\t\tif (this.renderFramebuffer) {\n\t\t\tthis.resolve();\n\t\t}\n\t\tthis.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\t}\n\n\t/**\n\t * Resize the FBO texture and renderbuffer.\n\t * @param {number} width - new width in pixels\n\t * @param {number} height - new height in pixels\n\t */\n\tresize(width, height) {\n\t\tif (this.width === width && this.height === height) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst gl = this.gl;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\n\t\t// immutable color storage can't be respecified — recreate the\n\t\t// texture at the new size (and re-attach it). Save/restore the\n\t\t// active unit so the batcher's cache stays in sync.\n\t\tconst prevUnit = gl.getParameter(gl.ACTIVE_TEXTURE);\n\t\tthis._allocateColorTexture(width, height);\n\n\t\tif (this.renderFramebuffer) {\n\t\t\t// renderbuffers respecify in place — re-allocate the\n\t\t\t// multisampled color half at the new size\n\t\t\tgl.bindRenderbuffer(gl.RENDERBUFFER, this.colorRenderbuffer);\n\t\t\tgl.renderbufferStorageMultisample(\n\t\t\t\tgl.RENDERBUFFER,\n\t\t\t\tthis.samples,\n\t\t\t\tgl.RGBA8,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\tthis._needsResolve = false;\n\t\t}\n\n\t\t// reattach depth/stencil at the new size (and re-validate completeness\n\t\t// so `_hasStencil` reflects the post-resize attachment status) — on\n\t\t// the framebuffer draws actually rasterize into\n\t\tgl.bindFramebuffer(\n\t\t\tgl.FRAMEBUFFER,\n\t\t\tthis.renderFramebuffer ?? this.framebuffer,\n\t\t);\n\t\tthis._applyDepthStencil(width, height);\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n\t\tgl.bindTexture(gl.TEXTURE_2D, null);\n\t\tgl.bindRenderbuffer(gl.RENDERBUFFER, null);\n\t\tgl.activeTexture(prevUnit);\n\t}\n\n\t/**\n\t * Clear the FBO contents to transparent black.\n\t */\n\tclear() {\n\t\tconst gl = this.gl;\n\t\tthis.bind();\n\t\tgl.clearColor(0, 0, 0, 0);\n\t\tgl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n\t\tthis.unbind();\n\t}\n\n\t/**\n\t * Returns an ImageData object representing the pixel contents of this render target.\n\t * @param {number} [x=0] - x coordinate of the top-left corner\n\t * @param {number} [y=0] - y coordinate of the top-left corner\n\t * @param {number} [width=this.width] - width of the area to read\n\t * @param {number} [height=this.height] - height of the area to read\n\t * @returns {ImageData} the pixel data\n\t */\n\tgetImageData(x = 0, y = 0, width = this.width, height = this.height) {\n\t\tconst gl = this.gl;\n\t\t// clamp to render target bounds\n\t\tx = Math.max(0, Math.min(Math.floor(x), this.width - 1));\n\t\ty = Math.max(0, Math.min(Math.floor(y), this.height - 1));\n\t\twidth = Math.max(1, Math.min(width, this.width - x));\n\t\theight = Math.max(1, Math.min(height, this.height - y));\n\t\tconst pixels = new Uint8ClampedArray(width * height * 4);\n\t\t// reads must come from the single-sampled resolve half — readPixels\n\t\t// on a multisampled framebuffer is a GL error\n\t\tthis.resolve();\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);\n\t\tgl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\t\t// gl.readPixels returns bottom-up rows, flip to top-down\n\t\tconst rowSize = width * 4;\n\t\tconst temp = new Uint8ClampedArray(rowSize);\n\t\tfor (let i = 0; i < Math.floor(height / 2); i++) {\n\t\t\tconst topOffset = i * rowSize;\n\t\t\tconst bottomOffset = (height - 1 - i) * rowSize;\n\t\t\ttemp.set(pixels.subarray(topOffset, topOffset + rowSize));\n\t\t\tpixels.copyWithin(topOffset, bottomOffset, bottomOffset + rowSize);\n\t\t\tpixels.set(temp, bottomOffset);\n\t\t}\n\t\treturn new ImageData(pixels, width, height);\n\t}\n\n\t/**\n\t * Release all GPU resources.\n\t */\n\tdestroy() {\n\t\tconst gl = this.gl;\n\t\tif (this.framebuffer) {\n\t\t\tgl.deleteFramebuffer(this.framebuffer);\n\t\t\tthis.framebuffer = null;\n\t\t}\n\t\tif (this.texture) {\n\t\t\tgl.deleteTexture(this.texture);\n\t\t\tthis.texture = null;\n\t\t}\n\t\tif (this.depthStencilBuffer) {\n\t\t\tgl.deleteRenderbuffer(this.depthStencilBuffer);\n\t\t\tthis.depthStencilBuffer = null;\n\t\t}\n\t\tif (this.renderFramebuffer) {\n\t\t\tgl.deleteFramebuffer(this.renderFramebuffer);\n\t\t\tthis.renderFramebuffer = null;\n\t\t}\n\t\tif (this.colorRenderbuffer) {\n\t\t\tgl.deleteRenderbuffer(this.colorRenderbuffer);\n\t\t\tthis.colorRenderbuffer = null;\n\t\t}\n\t}\n}\n","/**\n * Apply a dash pattern across an entire path (multiple connected segments).\n * Carries the dash state across segment boundaries for continuous dashing.\n * @param {Array<{x: number, y: number}>} pts - pairs of start/end points\n * @param {number[]} pattern - dash pattern [on, off, on, off, ...]\n * @returns {Array<{x: number, y: number}>} dashed segment pairs (internal scratch array — consume immediately, do not store)\n */\n// pre-allocated scratch array to avoid per-frame allocations\nconst _dashPathResult = [];\n\nexport function dashPath(pts, pattern) {\n\tif (\n\t\t!pattern.some((v) => {\n\t\t\treturn v > 0;\n\t\t})\n\t) {\n\t\treturn pts;\n\t}\n\n\tconst result = _dashPathResult;\n\tlet resultLen = 0;\n\tlet patIdx = 0;\n\tlet drawing = true;\n\tlet remaining = Math.max(0, pattern[0]);\n\n\tfor (let i = 0; i < pts.length - 1; i += 2) {\n\t\tconst x0 = pts[i].x;\n\t\tconst y0 = pts[i].y;\n\t\tconst x1 = pts[i + 1].x;\n\t\tconst y1 = pts[i + 1].y;\n\t\tconst dx = x1 - x0;\n\t\tconst dy = y1 - y0;\n\t\tconst segLen = Math.sqrt(dx * dx + dy * dy);\n\t\tif (segLen === 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst nx = dx / segLen;\n\t\tconst ny = dy / segLen;\n\t\tlet dist = 0;\n\n\t\twhile (dist < segLen) {\n\t\t\tconst step = Math.min(remaining, segLen - dist);\n\t\t\tif (drawing && step > 0) {\n\t\t\t\t// grow array if needed, reuse existing objects\n\t\t\t\twhile (result.length <= resultLen + 1) {\n\t\t\t\t\tresult.push({ x: 0, y: 0 });\n\t\t\t\t}\n\t\t\t\tresult[resultLen].x = x0 + nx * dist;\n\t\t\t\tresult[resultLen].y = y0 + ny * dist;\n\t\t\t\tresult[resultLen + 1].x = x0 + nx * (dist + step);\n\t\t\t\tresult[resultLen + 1].y = y0 + ny * (dist + step);\n\t\t\t\tresultLen += 2;\n\t\t\t}\n\t\t\tdist += step;\n\t\t\tremaining -= step;\n\t\t\tif (remaining <= 0) {\n\t\t\t\tdrawing = !drawing;\n\t\t\t\tpatIdx = (patIdx + 1) % pattern.length;\n\t\t\t\tremaining = Math.max(0, pattern[patIdx]);\n\t\t\t\t// skip zero-length entries (bounded to avoid infinite loop)\n\t\t\t\tlet skipped = 0;\n\t\t\t\twhile (remaining <= 0 && skipped < pattern.length) {\n\t\t\t\t\tdrawing = !drawing;\n\t\t\t\t\tpatIdx = (patIdx + 1) % pattern.length;\n\t\t\t\t\tremaining = Math.max(0, pattern[patIdx]);\n\t\t\t\t\tskipped++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tresult.length = resultLen;\n\treturn result;\n}\n\n/**\n * Split a single line segment into dashed sub-segments.\n * @param {number} x0 - start x\n * @param {number} y0 - start y\n * @param {number} x1 - end x\n * @param {number} y1 - end y\n * @param {number[]} pattern - dash pattern [on, off, on, off, ...]\n * @returns {Array<{x: number, y: number}>} pairs of start/end points for visible segments\n */\nexport function dashSegments(x0, y0, x1, y1, pattern) {\n\tconst dx = x1 - x0;\n\tconst dy = y1 - y0;\n\tconst lineLen = Math.sqrt(dx * dx + dy * dy);\n\tif (lineLen === 0 || pattern.length === 0) {\n\t\treturn [\n\t\t\t{ x: x0, y: y0 },\n\t\t\t{ x: x1, y: y1 },\n\t\t];\n\t}\n\n\tconst nx = dx / lineLen;\n\tconst ny = dy / lineLen;\n\t// bail out if pattern has no positive values (would loop forever)\n\tif (\n\t\t!pattern.some((v) => {\n\t\t\treturn v > 0;\n\t\t})\n\t) {\n\t\treturn [\n\t\t\t{ x: x0, y: y0 },\n\t\t\t{ x: x1, y: y1 },\n\t\t];\n\t}\n\n\tconst segments = [];\n\tlet dist = 0;\n\tlet patIdx = 0;\n\tlet drawing = true;\n\n\twhile (dist < lineLen) {\n\t\tconst dashLen = pattern[patIdx % pattern.length];\n\t\tif (dashLen <= 0) {\n\t\t\tpatIdx++;\n\t\t\tdrawing = !drawing;\n\t\t\tcontinue;\n\t\t}\n\t\tconst segEnd = Math.min(dist + dashLen, lineLen);\n\n\t\tif (drawing) {\n\t\t\tsegments.push(\n\t\t\t\t{ x: x0 + nx * dist, y: y0 + ny * dist },\n\t\t\t\t{ x: x0 + nx * segEnd, y: y0 + ny * segEnd },\n\t\t\t);\n\t\t}\n\n\t\tdist = segEnd;\n\t\tdrawing = !drawing;\n\t\tpatIdx++;\n\t}\n\n\treturn segments;\n}\n","/**\n * Generate triangle fan vertices for arcs, ellipses, and rounded rect corners.\n * @param {number} cx - center x\n * @param {number} cy - center y\n * @param {number} rx - horizontal radius\n * @param {number} ry - vertical radius\n * @param {number} startAngle - start angle in radians\n * @param {number} endAngle - end angle in radians\n * @param {number} segments - number of segments\n * @returns {Array<{x: number, y: number}>} triangle vertices\n */\nexport function generateTriangleFan(\n\tcx,\n\tcy,\n\trx,\n\try,\n\tstartAngle,\n\tendAngle,\n\tsegments,\n) {\n\tif (segments <= 0) {\n\t\treturn [];\n\t}\n\tconst angleStep = (endAngle - startAngle) / segments;\n\tconst verts = [];\n\tfor (let i = 0; i < segments; i++) {\n\t\tconst a1 = startAngle + i * angleStep;\n\t\tconst a2 = a1 + angleStep;\n\t\tverts.push(\n\t\t\t{ x: cx, y: cy },\n\t\t\t{ x: cx + Math.cos(a1) * rx, y: cy + Math.sin(a1) * ry },\n\t\t\t{ x: cx + Math.cos(a2) * rx, y: cy + Math.sin(a2) * ry },\n\t\t);\n\t}\n\treturn verts;\n}\n\n/**\n * Generate triangle vertices for round line join circles at the given centers.\n * @param {Array<{x: number, y: number}>} centers - join point positions\n * @param {number} radius - join circle radius (typically lineWidth / 2)\n * @returns {Array<{x: number, y: number}>} triangle vertices\n */\nexport function generateJoinCircles(centers, radius) {\n\tconst segments = 8;\n\tconst angleStep = (Math.PI * 2) / segments;\n\tconst verts = [];\n\n\tfor (let c = 0; c < centers.length; c++) {\n\t\tconst cx = centers[c].x;\n\t\tconst cy = centers[c].y;\n\t\tfor (let i = 0; i < segments; i++) {\n\t\t\tconst a1 = i * angleStep;\n\t\t\tconst a2 = a1 + angleStep;\n\t\t\tverts.push(\n\t\t\t\t{ x: cx, y: cy },\n\t\t\t\t{ x: cx + Math.cos(a1) * radius, y: cy + Math.sin(a1) * radius },\n\t\t\t\t{ x: cx + Math.cos(a2) * radius, y: cy + Math.sin(a2) * radius },\n\t\t\t);\n\t\t}\n\t}\n\n\treturn verts;\n}\n","/**\n * Retained mesh vertex-data assembly shared by the mesh batchers of both\n * backends — pure CPU code, no GPU references (the `meshchunk.ts` /\n * `lighting/` precedent). Kept in ONE place because the two layouts exist\n * in an unlit and a lit variant on each backend, and the subtle parts —\n * the ARGB unpack order and the lit path's zero-length-normal guard —\n * must never drift between the four call sites.\n *\n * The data deliberately carries no placement, camera or tint information —\n * those are uniforms — so it stays valid for the lifetime of the geometry.\n * @ignore\n */\n\n/** the minimal mesh surface this module reads — `@ignore` */\ninterface RetainedMeshSource {\n\toriginalVertices: Float32Array;\n\toriginalNormals?: Float32Array | null;\n\tuvs: Float32Array;\n\tvertexColors?: Uint32Array | null;\n\tvertexCount: number;\n}\n\n// Growable scratch for assembling a mesh's interleaved vertex data before\n// it is uploaded to its retained buffers. Module-level sharing is safe:\n// building is synchronous and never re-enters, whichever backend (or\n// coexisting renderer) drives it, so a rebuild allocates nothing\n// steady-state.\nlet buildScratch = new Float32Array(0);\n\n/**\n * a reusable Float32Array of at least `floatCount` floats\n * @ignore\n */\nexport function retainedScratch(floatCount: number): Float32Array {\n\tif (buildScratch.length < floatCount) {\n\t\tbuildScratch = new Float32Array(floatCount);\n\t}\n\treturn buildScratch;\n}\n\n/**\n * Write one mesh's model-space geometry into `out` in the unlit mesh\n * layout — `x, y, z, u, v, r, g, b, a` — striding by `vertexSize`.\n * @param mesh - the mesh to read geometry from\n * @param out - destination scratch, at least `vertexCount × vertexSize` long\n * @param vertexSize - floats per vertex in the hosting batcher's layout\n * @returns number of floats written\n * @ignore\n */\nexport function buildMeshVertexData(\n\tmesh: RetainedMeshSource,\n\tout: Float32Array,\n\tvertexSize: number,\n): number {\n\tconst vertices = mesh.originalVertices;\n\tconst uvs = mesh.uvs;\n\tconst colors = mesh.vertexColors;\n\tconst count = mesh.vertexCount;\n\tlet o = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst i3 = i * 3;\n\t\tconst i2 = i * 2;\n\t\tconst c = colors ? colors[i] : 0xffffffff;\n\t\tout[o] = vertices[i3];\n\t\tout[o + 1] = vertices[i3 + 1];\n\t\tout[o + 2] = vertices[i3 + 2];\n\t\tout[o + 3] = uvs[i2];\n\t\tout[o + 4] = uvs[i2 + 1];\n\t\tout[o + 5] = ((c >> 16) & 0xff) / 255;\n\t\tout[o + 6] = ((c >> 8) & 0xff) / 255;\n\t\tout[o + 7] = (c & 0xff) / 255;\n\t\tout[o + 8] = ((c >>> 24) & 0xff) / 255;\n\t\to += vertexSize;\n\t}\n\treturn o;\n}\n\n/**\n * The lit variant: the unlit layout plus the model-space normal —\n * `…, nx, ny, nz` — so lighting can be evaluated after the shader rotates\n * it into world space.\n *\n * A zero-length normal must not reach the shader: it does\n * `normalize(vNormal)`, and normalizing a zero vector is NaN — every\n * fragment touching that vertex goes black or garbage. Degenerate\n * triangles in OBJ/glTF assets produce these, so substitute a unit\n * vector, matching what the CPU projection path does.\n * @param mesh - the mesh to read geometry from\n * @param out - destination scratch, at least `vertexCount × vertexSize` long\n * @param vertexSize - floats per vertex in the hosting batcher's layout\n * @returns number of floats written\n * @ignore\n */\nexport function buildLitMeshVertexData(\n\tmesh: RetainedMeshSource,\n\tout: Float32Array,\n\tvertexSize: number,\n): number {\n\tconst vertices = mesh.originalVertices;\n\tconst normals = mesh.originalNormals;\n\tconst uvs = mesh.uvs;\n\tconst colors = mesh.vertexColors;\n\tconst count = mesh.vertexCount;\n\tlet o = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst i3 = i * 3;\n\t\tconst i2 = i * 2;\n\t\tconst c = colors ? colors[i] : 0xffffffff;\n\t\tout[o] = vertices[i3];\n\t\tout[o + 1] = vertices[i3 + 1];\n\t\tout[o + 2] = vertices[i3 + 2];\n\t\tout[o + 3] = uvs[i2];\n\t\tout[o + 4] = uvs[i2 + 1];\n\t\tout[o + 5] = ((c >> 16) & 0xff) / 255;\n\t\tout[o + 6] = ((c >> 8) & 0xff) / 255;\n\t\tout[o + 7] = (c & 0xff) / 255;\n\t\tout[o + 8] = ((c >>> 24) & 0xff) / 255;\n\t\tconst nx = normals ? normals[i3] : 0;\n\t\tconst ny = normals ? normals[i3 + 1] : 0;\n\t\tconst nz = normals ? normals[i3 + 2] : 0;\n\t\tif (nx * nx + ny * ny + nz * nz > 1e-16) {\n\t\t\tout[o + 9] = nx;\n\t\t\tout[o + 10] = ny;\n\t\t\tout[o + 11] = nz;\n\t\t} else {\n\t\t\tout[o + 9] = 0;\n\t\t\tout[o + 10] = 1;\n\t\t\tout[o + 11] = 0;\n\t\t}\n\t\to += vertexSize;\n\t}\n\treturn o;\n}\n","/**\n * A uniform buffer bound to a named block in one or more shader programs.\n *\n * Uniform arrays are charged against `MAX_FRAGMENT_UNIFORM_VECTORS`, a\n * driver-reported budget that is small on weak devices. A uniform buffer is\n * charged against `MAX_UNIFORM_BLOCK_SIZE` instead — guaranteed at least\n * 16 KB, typically 64 KB — so bulk per-frame data stops competing with\n * everything else the shader declares.\n *\n * Data is staged in a `Float32Array` and uploaded with `bufferSubData`, so\n * only the live prefix crosses the bus. Uploads are skipped entirely when the\n * staged bytes have not changed, which preserves the property the uniform\n * cache gives today: a scene whose lights are static costs nothing per frame.\n *\n * ## Context loss\n *\n * A block binding is program state, not buffer state, and every program is\n * recompiled on restore — so the binding has to be re-established afterwards\n * or the shader reads an unbound block and renders black. Callers re-bind\n * lazily, whenever the program they last bound is no longer the current one;\n * that covers a context restore without anyone subscribing to an event. The\n * buffer itself needs no recovery hook — the renderer re-runs each batcher's\n * `init()` on restore, which releases this block and builds a new one.\n * @ignore\n */\nexport default class UniformBlock {\n\t/**\n\t * @param {WebGL2RenderingContext} gl - the rendering context\n\t * @param {number} floats - capacity in floats\n\t * @param {number} bindingPoint - the indexed binding point to occupy\n\t */\n\tconstructor(gl, floats, bindingPoint) {\n\t\tthis.gl = gl;\n\n\t\t/**\n\t\t * staging buffer; callers write into this then call `upload`\n\t\t * @type {Float32Array}\n\t\t */\n\t\tthis.data = new Float32Array(floats);\n\n\t\t/**\n\t\t * mirror of what was last uploaded, so an unchanged frame can skip the\n\t\t * `bufferSubData` entirely\n\t\t * @type {Float32Array}\n\t\t */\n\t\tthis._uploaded = new Float32Array(floats);\n\n\t\t/** nothing has been uploaded yet, so the mirror is not yet valid */\n\t\tthis._everUploaded = false;\n\n\t\t/** live length of the last upload; -1 until there has been one */\n\t\tthis._uploadedLength = -1;\n\n\t\t/** @type {number} */\n\t\tthis.bindingPoint = bindingPoint;\n\n\t\t/** @type {WebGLBuffer|null} */\n\t\tthis.buffer = gl.createBuffer();\n\n\t\tgl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);\n\t\tgl.bufferData(gl.UNIFORM_BUFFER, this.data.byteLength, gl.DYNAMIC_DRAW);\n\t\tgl.bindBufferBase(gl.UNIFORM_BUFFER, bindingPoint, this.buffer);\n\t\tgl.bindBuffer(gl.UNIFORM_BUFFER, null);\n\t}\n\n\t/**\n\t * Point a program's named block at this buffer's binding point.\n\t *\n\t * Safe to call repeatedly — after a context restore the program is a new\n\t * object and must be re-bound, and there is no way to ask whether it\n\t * already was.\n\t * @param {WebGLProgram} program - the linked program\n\t * @param {string} blockName - the block's name in GLSL\n\t * @returns {boolean} `false` when the program does not declare the block\n\t */\n\tbindTo(program, blockName) {\n\t\tconst gl = this.gl;\n\t\tconst index = gl.getUniformBlockIndex(program, blockName);\n\t\tif (index === gl.INVALID_INDEX) {\n\t\t\t// the shader compiled without the block — either it does not use\n\t\t\t// lighting, or the declaration was optimised out because nothing\n\t\t\t// read it. Not an error; the caller simply has nothing to feed.\n\t\t\treturn false;\n\t\t}\n\t\tgl.uniformBlockBinding(program, index, this.bindingPoint);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Upload the first `floats` entries of {@link UniformBlock#data}.\n\t *\n\t * Compares against the previous upload first and does nothing when the\n\t * bytes are identical. Uniform writes are already skipped when unchanged\n\t * (`utils/uniforms.js`), so uploading unconditionally here would make a\n\t * static scene more expensive than it was before the buffer existed.\n\t * @param {number} floats - how many floats of the staging buffer are live\n\t * @returns {boolean} whether anything was actually sent to the GPU\n\t */\n\tupload(floats) {\n\t\tif (this.buffer === null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this._everUploaded === true && this.#matches(floats)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst gl = this.gl;\n\t\tgl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);\n\t\tgl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, floats);\n\t\tgl.bindBuffer(gl.UNIFORM_BUFFER, null);\n\n\t\tthis._uploaded.set(this.data.subarray(0, floats), 0);\n\t\tthis._uploadedLength = floats;\n\t\tthis._everUploaded = true;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Whether the live prefix is byte-identical to what was last uploaded.\n\t * @param {number} floats - length of the live prefix\n\t * @returns {boolean} true when an upload would be a no-op\n\t * @ignore\n\t */\n\t#matches(floats) {\n\t\tif (this._uploadedLength !== floats) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (let i = 0; i < floats; i++) {\n\t\t\tif (this._uploaded[i] !== this.data[i]) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Release the GL buffer. Safe to call twice, and on a lost context.\n\t */\n\tdestroy() {\n\t\tconst gl = this.gl;\n\t\tif (this.buffer !== null) {\n\t\t\t// `isBuffer` is false for a handle from a context that has since\n\t\t\t// been lost, and deleting such a handle raises INVALID_OPERATION\n\t\t\tif (gl.isBuffer(this.buffer)) {\n\t\t\t\tgl.deleteBuffer(this.buffer);\n\t\t\t}\n\t\t\tthis.buffer = null;\n\t\t}\n\t\tthis._everUploaded = false;\n\t\tthis._uploadedLength = -1;\n\t}\n}\n","/**\n * Constants shared between the lit fragment shaders\n * (`multitexture-lit.js`, `mesh-lit.frag`), the lit batchers\n * (`LitQuadBatcher`, `LitMeshBatcher`), the uniform packers (`pack.ts`,\n * `pack3d.ts`) and the std140 writer (`std140.ts`). Kept in a tiny\n * standalone module so the consumers don't depend on each other\n * transitively for a couple of integers.\n */\n\n/**\n * Maximum number of lights the lit fragment shaders support concurrently\n * per draw call. Lights past this index are ignored.\n *\n * The data travels in a uniform buffer, so this is an allocation rather\n * than a compatibility question: 32 lights cost 1056 bytes against\n * `MAX_UNIFORM_BLOCK_SIZE`, which is at least 16 KB everywhere. Uniform\n * _arrays_, the previous transport, are charged against the much smaller\n * `MAX_FRAGMENT_UNIFORM_VECTORS` — shared with every other uniform the\n * shader declares — which is what held this at 8.\n *\n * It is not a statement about what a device can afford to _shade_: the\n * fragment loop still runs once per pixel per live light. Unused capacity\n * is free — the loop bound is the live count — but filling it is not.\n */\nexport const MAX_LIGHTS = 32;\n","import type { Light3d } from \"../../../lighting/light3d.ts\";\nimport { MAX_LIGHTS } from \"./constants.ts\";\n\n/**\n * Uniform-ready packing of the active 3D lights for the mesh-lit shader.\n * @ignore\n */\nexport interface PackedMeshLighting {\n\t/** number of active shaded lights, clamped to `MAX_LIGHTS`. */\n\tcount: number;\n\t/**\n\t * `MAX_LIGHTS × 4` — `[x, y, z, range]` per light; `range` is `-1` for\n\t * directional lights (the shader's type sentinel).\n\t */\n\tposRange: Float32Array;\n\t/**\n\t * `MAX_LIGHTS × 4` — `[dx, dy, dz, cos(outerConeAngle)]` per light;\n\t * `cosOuter` is `-1` when no cone applies (directional carries the\n\t * pre-negated surface→light vector, point carries no direction).\n\t */\n\tdirCone: Float32Array;\n\t/**\n\t * `MAX_LIGHTS × 4` — `[r, g, b, cos(innerConeAngle)]` per light,\n\t * color premultiplied by intensity.\n\t */\n\tcolorInner: Float32Array;\n\t/** the summed ambient color (RGB, 0..1+). */\n\tambient: Float32Array;\n}\n\n// reused output buffers — the packed result is consumed immediately each frame\n// by the lit mesh batcher, so a single shared set is safe and allocation-free.\nconst _posRange = new Float32Array(MAX_LIGHTS * 4);\nconst _dirCone = new Float32Array(MAX_LIGHTS * 4);\nconst _colorInner = new Float32Array(MAX_LIGHTS * 4);\nconst _ambient = new Float32Array(3);\nconst _result: PackedMeshLighting = {\n\tcount: 0,\n\tposRange: _posRange,\n\tdirCone: _dirCone,\n\tcolorInner: _colorInner,\n\tambient: _ambient,\n};\n\n/**\n * Pack an iterable of {@link Light3d} (e.g. the active `Stage`'s 3D-light set)\n * into the std140-ready arrays the mesh-lit shader reads:\n * - **directional** lights contribute a surface→light direction (negated\n *   travel direction, normalized) + a color premultiplied by intensity.\n *   Re-normalized here so a direction mutated at runtime without\n *   re-normalizing still shades correctly.\n * - **point** lights contribute their world position, `range` (quadratic\n *   falloff over the range — the Light2d model) and premultiplied color.\n * - **spot** lights add their travel direction and the cone cosines\n *   (`innerConeAngle` clamped just inside `outerConeAngle`, so the\n *   smoothstep denominator never collapses).\n * - **ambient** lights are summed into a single flat ambient color.\n *\n * The same buffers are returned each call (overwritten in place).\n * @param lights - iterable of lights, or `null`/`undefined` (treated as empty)\n * @returns the packed lighting (reused instance)\n * @ignore\n */\nexport function packMeshLights(\n\tlights: Iterable<Light3d> | null | undefined,\n): PackedMeshLighting {\n\tlet count = 0;\n\tlet ar = 0;\n\tlet ag = 0;\n\tlet ab = 0;\n\tif (lights) {\n\t\tfor (const light of lights) {\n\t\t\tif (light.type === \"ambient\") {\n\t\t\t\tconst k = light.intensity;\n\t\t\t\tar += (light.color.r / 255) * k;\n\t\t\t\tag += (light.color.g / 255) * k;\n\t\t\t\tab += (light.color.b / 255) * k;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (count >= MAX_LIGHTS) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst o = count * 4;\n\t\t\tconst k = light.intensity;\n\t\t\t_colorInner[o] = (light.color.r / 255) * k;\n\t\t\t_colorInner[o + 1] = (light.color.g / 255) * k;\n\t\t\t_colorInner[o + 2] = (light.color.b / 255) * k;\n\t\t\t_colorInner[o + 3] = 0;\n\n\t\t\tif (light.type === \"directional\") {\n\t\t\t\tconst dx = light.direction.x;\n\t\t\t\tconst dy = light.direction.y;\n\t\t\t\tconst dz = light.direction.z;\n\t\t\t\tconst len = Math.hypot(dx, dy, dz) || 1;\n\t\t\t\t// directional sentinel: range < 0; position unused\n\t\t\t\t_posRange[o] = 0;\n\t\t\t\t_posRange[o + 1] = 0;\n\t\t\t\t_posRange[o + 2] = 0;\n\t\t\t\t_posRange[o + 3] = -1;\n\t\t\t\t// store the surface→light vector (negated travel direction)\n\t\t\t\t_dirCone[o] = -dx / len;\n\t\t\t\t_dirCone[o + 1] = -dy / len;\n\t\t\t\t_dirCone[o + 2] = -dz / len;\n\t\t\t\t_dirCone[o + 3] = -1;\n\t\t\t\tcount++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (light.type === \"point\" || light.type === \"spot\") {\n\t\t\t\t_posRange[o] = light.position.x;\n\t\t\t\t_posRange[o + 1] = light.position.y;\n\t\t\t\t_posRange[o + 2] = light.position.z;\n\t\t\t\t// quadratic falloff needs a scale — a degenerate range would\n\t\t\t\t// make the light a point-sized pop\n\t\t\t\t_posRange[o + 3] = Math.max(light.range ?? 0, 1);\n\t\t\t\tif (light.type === \"spot\") {\n\t\t\t\t\tconst dx = light.direction.x;\n\t\t\t\t\tconst dy = light.direction.y;\n\t\t\t\t\tconst dz = light.direction.z;\n\t\t\t\t\tconst len = Math.hypot(dx, dy, dz) || 1;\n\t\t\t\t\t// the light's TRAVEL direction (cone axis), normalized\n\t\t\t\t\t_dirCone[o] = dx / len;\n\t\t\t\t\t_dirCone[o + 1] = dy / len;\n\t\t\t\t\t_dirCone[o + 2] = dz / len;\n\t\t\t\t\t// floor the outer angle so the inner clamp below can never\n\t\t\t\t\t// invert the smoothstep edges (edge0 >= edge1 is undefined\n\t\t\t\t\t// in GLSL, NaN-prone in WGSL) — an authored cone under\n\t\t\t\t\t// ~0.11° is a degenerate laser anyway\n\t\t\t\t\tconst outer = Math.max(light.outerConeAngle ?? Math.PI / 4, 2e-3);\n\t\t\t\t\t// inner strictly inside outer, or the cone edge divides by 0\n\t\t\t\t\tconst inner = Math.max(\n\t\t\t\t\t\tMath.min(light.innerConeAngle ?? 0, outer - 1e-3),\n\t\t\t\t\t\t0,\n\t\t\t\t\t);\n\t\t\t\t\t_dirCone[o + 3] = Math.cos(outer);\n\t\t\t\t\t_colorInner[o + 3] = Math.cos(inner);\n\t\t\t\t} else {\n\t\t\t\t\t// point: no cone — the shader's \"no cone\" sentinel\n\t\t\t\t\t_dirCone[o] = 0;\n\t\t\t\t\t_dirCone[o + 1] = 0;\n\t\t\t\t\t_dirCone[o + 2] = 0;\n\t\t\t\t\t_dirCone[o + 3] = -1;\n\t\t\t\t}\n\t\t\t\tcount++;\n\t\t\t}\n\t\t\t// unknown types are skipped\n\t\t}\n\t}\n\t_ambient[0] = ar;\n\t_ambient[1] = ag;\n\t_ambient[2] = ab;\n\t_result.count = count;\n\treturn _result;\n}\n","import { MAX_LIGHTS } from \"./constants.ts\";\n\n/**\n * std140 layout for the light uniform blocks.\n *\n * `std140` is a published set of alignment rules that both the shader and\n * this module can compute offsets from independently, so the block can be\n * filled by writing into a plain `Float32Array` rather than querying the\n * driver for where each member landed.\n *\n * The rule that shapes everything here: **every array element is padded up\n * to 16 bytes**, whatever its type. A `vec3` occupies 16 bytes rather than\n * 12, and a `float[]` costs 16 bytes per entry rather than 4. So the layout\n * folds the loose scalars into the `w` components that would otherwise be\n * padding — `radius` and `intensity` ride along with the position, and\n * `lightHeight` with the colour. That is free: the bytes were reserved\n * either way.\n *\n * A literal translation of the old uniform arrays would have cost 1536\n * bytes of light data per 32 lights, a third of it padding; this costs\n * 1024, with none. The whole block is 1056 bytes — the extra 32 are the\n * count + ambient header.\n *\n * ## Getting this wrong is silent\n *\n * A mislaid offset does not throw. The buffer uploads, the shader reads,\n * and every light after the mistake is simply shifted — which renders\n * subtly wrong rather than obviously broken. The offsets below are\n * therefore stated as explicit constants and pinned byte-for-byte by\n * `tests/lighting_std140.spec.js` against a layout written out by hand.\n */\n\n/**\n * floats per `vec4`, the block's fundamental unit\n * @ignore\n */\nconst VEC4 = 4;\n\n/**\n * Header, occupying two `vec4`s before the light array begins.\n *\n * ```glsl\n * int  count;     // offset 0\n * vec3 ambient;   // offset 16 — a vec3 aligns to 16, so it cannot share\n * ```\n *\n * `count` is written as a float and read back with `int(...)` in the\n * shader. Declaring it `int` in the block would be tidier, but mixing int\n * and float members means the staging buffer can no longer be a single\n * `Float32Array` view, and the cast is free on every GPU that matters.\n * @ignore\n */\nexport const HEADER_FLOATS = VEC4 * 2;\n\n/**\n * floats occupied by one light: two `vec4`s\n * @ignore\n */\nexport const LIGHT_FLOATS = VEC4 * 2;\n\n/**\n * total floats in a light block, header included\n * @ignore\n */\nexport const BLOCK_FLOATS = HEADER_FLOATS + MAX_LIGHTS * LIGHT_FLOATS;\n\n/**\n * total bytes in a light block\n * @ignore\n */\nexport const BLOCK_BYTES = BLOCK_FLOATS * Float32Array.BYTES_PER_ELEMENT;\n\n/**\n * Write the block header.\n * @param out - staging buffer\n * @param count - number of live lights\n * @param ambient - ambient colour, 0..1 per channel\n * @ignore\n */\nfunction writeHeader(\n\tout: Float32Array,\n\tcount: number,\n\tambient: ArrayLike<number> | undefined,\n): void {\n\tout[0] = count;\n\t// [1..3] are padding: `count` is a scalar but the following `vec3`\n\t// aligns to 16, so it cannot share this slot\n\tout[1] = 0;\n\tout[2] = 0;\n\tout[3] = 0;\n\tout[4] = ambient?.[0] ?? 0;\n\tout[5] = ambient?.[1] ?? 0;\n\tout[6] = ambient?.[2] ?? 0;\n\tout[7] = 0;\n}\n\n/**\n * Lay out 2D (`Light2d`) data.\n *\n * ```glsl\n * struct Light2dData {\n *   vec4 posRadiusIntensity;  // x, y, radius, intensity\n *   vec4 colorHeight;         // r, g, b, lightHeight\n * };\n * ```\n * @param out - staging buffer, at least `BLOCK_FLOATS` long\n * @param packed - what {@link packLights} produced\n * @param packed.count - number of live lights, clamped to `MAX_LIGHTS`\n * @param packed.positions - `[x, y, radius, intensity]` per light\n * @param packed.colors - `[r, g, b]` per light, 0..1 per channel\n * @param packed.heights - per-light height; absent means 0\n * @param packed.ambient - `[r, g, b]` ambient floor; absent means black\n * @returns how many floats of `out` are live, for a partial upload\n * @ignore\n */\nexport function writeLight2dBlock(\n\tout: Float32Array,\n\tpacked: {\n\t\tcount: number;\n\t\tpositions: Float32Array;\n\t\tcolors: Float32Array;\n\t\theights?: Float32Array;\n\t\tambient?: ArrayLike<number>;\n\t},\n): number {\n\t// `count` reaches here straight from a caller (`setLightUniforms` is\n\t// public), so clamp rather than trust it — against the block's capacity\n\t// AND against what the source arrays actually hold. Overrunning the\n\t// block would corrupt a neighbouring binding point; overrunning the\n\t// source reads `undefined`, and `Float32Array[i] = undefined` stores\n\t// NaN, which shades black and — because `NaN !== NaN` — also defeats\n\t// the upload dirty-check from then on, turning a free frame into a\n\t// `bufferSubData` every frame. A negative or NaN count must produce an\n\t// empty block, not a negative live length: hence `Math.max(0, …)`.\n\tconst count = Math.min(\n\t\tMath.max(packed.count | 0, 0),\n\t\tMAX_LIGHTS,\n\t\tpacked.positions.length >> 2,\n\t\tMath.floor(packed.colors.length / 3),\n\t);\n\twriteHeader(out, count, packed.ambient);\n\n\tconst heights = packed.heights;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst o = HEADER_FLOATS + i * LIGHT_FLOATS;\n\t\tout[o] = packed.positions[i * 4];\n\t\tout[o + 1] = packed.positions[i * 4 + 1];\n\t\tout[o + 2] = packed.positions[i * 4 + 2];\n\t\tout[o + 3] = packed.positions[i * 4 + 3];\n\t\tout[o + 4] = packed.colors[i * 3];\n\t\tout[o + 5] = packed.colors[i * 3 + 1];\n\t\tout[o + 6] = packed.colors[i * 3 + 2];\n\t\t// no `heights` means \"flat\" — the shader normalizes\n\t\t// `vec3(toLight, height)`, and a 0 there is degenerate only when the\n\t\t// light sits exactly on the fragment, which the old path had too\n\t\tout[o + 7] = heights?.[i] ?? 0;\n\t}\n\treturn HEADER_FLOATS + count * LIGHT_FLOATS;\n}\n\n/**\n * floats occupied by one 3D light: three `vec4`s. The 3D block grew from\n * two when point/spot shading landed (#1536) — a spot light needs position\n * + range + direction + both cone cosines + color, which cannot fold into\n * two vec4s' padding. The 2D block keeps its two-vec4 stride.\n * @ignore\n */\nexport const LIGHT3D_FLOATS = VEC4 * 3;\n\n/**\n * total floats in a 3D light block, header included\n * @ignore\n */\nexport const BLOCK3D_FLOATS = HEADER_FLOATS + MAX_LIGHTS * LIGHT3D_FLOATS;\n\n/**\n * total bytes in a 3D light block\n * @ignore\n */\nexport const BLOCK3D_BYTES = BLOCK3D_FLOATS * Float32Array.BYTES_PER_ELEMENT;\n\n/**\n * Lay out 3D (`Light3d`) data.\n *\n * ```glsl\n * struct Light3dData {\n *   vec4 posRange;    // x, y, z = world position · w = range;\n *                     //   w < 0 marks a DIRECTIONAL light (xyz unused)\n *   vec4 dirCone;     // x, y, z = direction · w = cos(outerConeAngle);\n *                     //   w = -1 marks \"no cone\" (directional dir is the\n *                     //   pre-negated surface→light vector; a point light\n *                     //   carries no direction)\n *   vec4 colorInner;  // r, g, b = color × intensity · w = cos(innerConeAngle)\n * };\n * ```\n *\n * The type is inferred from the sentinels rather than a tag slot:\n * `posRange.w < 0` → directional; otherwise positional, with a cone\n * applied when `dirCone.w > -1`.\n * @param out - staging buffer, at least `BLOCK3D_FLOATS` long\n * @param packed - what {@link packMeshLights} produced\n * @param packed.count - number of live lights, clamped to `MAX_LIGHTS`\n * @param packed.posRange - `[x, y, z, range]` per light (range -1 for directional)\n * @param packed.dirCone - `[dx, dy, dz, cosOuter]` per light (cosOuter -1 for none)\n * @param packed.colorInner - `[r, g, b, cosInner]` per light\n * @param packed.ambient - `[r, g, b]` ambient floor; absent means black\n * @returns how many floats of `out` are live, for a partial upload\n * @ignore\n */\nexport function writeLight3dBlock(\n\tout: Float32Array,\n\tpacked: {\n\t\tcount: number;\n\t\tposRange: Float32Array;\n\t\tdirCone: Float32Array;\n\t\tcolorInner: Float32Array;\n\t\tambient?: ArrayLike<number>;\n\t},\n): number {\n\t// clamped the same way as the 2D path, and for the same reasons\n\tconst count = Math.min(\n\t\tMath.max(packed.count | 0, 0),\n\t\tMAX_LIGHTS,\n\t\tMath.floor(packed.posRange.length / 4),\n\t\tMath.floor(packed.dirCone.length / 4),\n\t\tMath.floor(packed.colorInner.length / 4),\n\t);\n\twriteHeader(out, count, packed.ambient);\n\n\tfor (let i = 0; i < count; i++) {\n\t\tconst o = HEADER_FLOATS + i * LIGHT3D_FLOATS;\n\t\tconst i4 = i * 4;\n\t\tout[o] = packed.posRange[i4];\n\t\tout[o + 1] = packed.posRange[i4 + 1];\n\t\tout[o + 2] = packed.posRange[i4 + 2];\n\t\tout[o + 3] = packed.posRange[i4 + 3];\n\t\tout[o + 4] = packed.dirCone[i4];\n\t\tout[o + 5] = packed.dirCone[i4 + 1];\n\t\tout[o + 6] = packed.dirCone[i4 + 2];\n\t\tout[o + 7] = packed.dirCone[i4 + 3];\n\t\tout[o + 8] = packed.colorInner[i4];\n\t\tout[o + 9] = packed.colorInner[i4 + 1];\n\t\tout[o + 10] = packed.colorInner[i4 + 2];\n\t\tout[o + 11] = packed.colorInner[i4 + 3];\n\t}\n\treturn HEADER_FLOATS + count * LIGHT3D_FLOATS;\n}\n","#version 300 es\n// Lit mesh fragment shader: half-Lambert diffuse from directional lights\n// plus an ambient floor.\n//\n// GLSL ES 3.00 because the light data arrives in a uniform block, which does\n// not exist in ES 1.00. That is what lifted the light cap: uniform arrays are\n// charged against MAX_FRAGMENT_UNIFORM_VECTORS, a small driver-reported\n// budget, while a block is charged against MAX_UNIFORM_BLOCK_SIZE.\n//\n// `__MAX_LIGHTS__` is replaced with the MAX_LIGHTS constant\n// (src/video/webgl/lighting/constants.ts) by LitMeshBatcher._shaderSources at\n// load time, so the array size can't drift from the uniform packer. (Used\n// directly rather than via a #define so it survives the GLSL preprocessor\n// regardless of how it handles macros.)\n//\n// The block's layout is std140 and is written by\n// src/video/webgl/lighting/std140.ts. The two must agree byte for byte — a\n// mismatch does not fail to link, it silently shifts every light.\n//\n// No `precision` line here on purpose: `setPrecision` injects float and int\n// precision after `#version`, using whatever the device reports and the\n// application asked for, so shader files don't hardcode it.\n\nuniform sampler2D uSampler;\nuniform float uAlphaCutoff;               // alpha cutout threshold (0 = disabled)\nuniform vec3 uEmissive;                   // self-illumination color (0 = none)\nuniform vec3 uSpecular;                   // specular color (0 = purely diffuse)\nuniform float uShininess;                 // specular exponent (0 = no highlight)\nuniform vec3 uEyePosition;                // camera position, world space\nuniform sampler2D uAlphaMap;              // per-texel opacity (MTL map_d)\nuniform float uHasAlphaMap;               // 0 = uAlphaMap is filler, ignore it\n\n// One light, type inferred from sentinels (see std140.ts):\n// posRange.w < 0 -> directional (dirCone.xyz = surface->light, normalized);\n// otherwise positional at posRange.xyz with quadratic falloff over\n// posRange.w, plus a spot cone when dirCone.w > -1 (dirCone.xyz = the cone\n// axis / travel direction, w = cos(outer), colorInner.w = cos(inner)).\n// colorInner.rgb is premultiplied by intensity.\nstruct Light3dData {\n    vec4 posRange;\n    vec4 dirCone;\n    vec4 colorInner;\n};\n\nlayout(std140) uniform Light3dBlock {\n    // `count` is a float: mixing int and float members would stop the staging\n    // buffer being a single Float32Array view, and the cast below is free. The\n    // vec3 that follows aligns to 16 bytes, so the three floats after `count`\n    // are padding either way.\n    float uLightCount;\n    vec3 uAmbient;\n    Light3dData uLights[__MAX_LIGHTS__];\n};\n\nin vec4 vColor;\nin vec2 vRegion;\nin vec3 vNormal;\nin vec3 vWorldPos;\n#ifdef INSTANCE_DATA\n// per-instance custom slot — read as emissive by this built-in shading\nin vec4 vInstanceData;\n#endif\n\nout vec4 fragColor;\n\nvoid main(void) {\n    vec4 base = texture(uSampler, vRegion) * vColor;\n\n    // per-texel opacity (MTL map_d) multiplies in BEFORE the cutout, so one\n    // material can cut out to the shape of a leaf rather than at a single\n    // threshold across the whole surface. Red channel: the format stores a\n    // greyscale map, and every channel carries the same value.\n    // Sampled unconditionally and WEIGHTED rather than branched: when no map\n    // is bound the second sampler is filler (the diffuse texture), so the\n    // value is thrown away — and both backends run the identical expression\n    // instead of one branching and the other not.\n    base.a *= mix(1.0, texture(uAlphaMap, vRegion).r, uHasAlphaMap);\n\n    // hard alpha cutout (glTF alphaMode MASK) — discard before any shading\n    // so cut-away texels cost nothing and never write depth.\n    if (base.a < uAlphaCutoff) {\n        discard;\n    }\n\n    // A mesh marked `lit` with no usable normals — the 2D-camera path,\n    // which leaves world normals unwritten, or geometry that supplied none —\n    // would normalize a zero vector to NaN and render BLACK. Degrade to\n    // unlit instead: wrong, but recognisably the model rather than a hole.\n    float nLength = length(vNormal);\n    if (nLength < 1e-6) {\n        // the emissive term is built exactly as the lit path below builds\n        // it, per-instance slot included — degrading to unlit must not also\n        // drop an instance's glow\n        vec3 unlitEmissive = uEmissive;\n#ifdef INSTANCE_DATA\n        unlitEmissive += vInstanceData.rgb;\n#endif\n        fragColor = vec4(base.rgb + unlitEmissive, base.a);\n        return;\n    }\n    vec3 N = vNormal / nLength;\n    vec3 lit = uAmbient;\n    // Blinn-Phong specular, accumulated alongside the diffuse term. Gated on\n    // the exponent rather than the colour: `Ns` of 0 is the format's \"no\n    // highlight\", and exporters happily write a non-black `Ks` next to it.\n    // The half-vector form (rather than reflect()) is the cheaper of the two\n    // and is what the fixed-function pipeline this format predates used.\n    vec3 specular = vec3(0.0);\n    bool hasSpecular = uShininess > 0.0;\n    vec3 V = hasSpecular ? normalize(uEyePosition - vWorldPos) : vec3(0.0);\n    // ES 3.00 allows a non-constant loop bound, so this runs exactly as many\n    // iterations as there are live lights — unused capacity costs nothing,\n    // which is what makes a larger cap safe.\n    // clamped to the array size, not just taken on trust: if the block ever\n    // read as something other than what the writer put there, an unbounded\n    // trip count would also index `uLights` out of range\n    int count = min(int(uLightCount), __MAX_LIGHTS__);\n    for (int i = 0; i < count; i++) {\n        vec4 pr = uLights[i].posRange;\n        vec4 dc = uLights[i].dirCone;\n        vec3 L;\n        float atten = 1.0;\n        if (pr.w < 0.0) {\n            // directional: dirCone.xyz is already the surface->light vector\n            L = dc.xyz;\n        } else {\n            // positional: quadratic falloff over range (the Light2d model —\n            // stylized, not physical inverse-square, which is unusable in\n            // pixel-unit worlds with unit intensities)\n            vec3 toLight = pr.xyz - vWorldPos;\n            float dist = max(length(toLight), 1e-4);\n            L = toLight / dist;\n            float linearAtt = max(0.0, 1.0 - dist / pr.w);\n            atten = linearAtt * linearAtt;\n            if (dc.w > -1.0) {\n                // spot cone: -L is the light->surface direction; fade from\n                // the inner cone cosine to the outer\n                float cd = dot(-L, dc.xyz);\n                atten *= smoothstep(dc.w, uLights[i].colorInner.w, cd);\n            }\n        }\n        // Half-Lambert (\"wrap\") diffuse: dot * 0.5 + 0.5. Softens the\n        // terminator and lifts the shadowed side, for a gentler, more\n        // diffuse look than hard Lambert (which reads as harsh noon).\n        float ndl = dot(N, L) * 0.5 + 0.5;\n        lit += uLights[i].colorInner.rgb * (ndl * ndl * atten);\n        if (hasSpecular) {\n            // masked by the UNWRAPPED Lambert term: half-Lambert lifts the\n            // shadowed side, and a highlight on a surface facing away from\n            // the light reads as a rendering error\n            float facing = max(dot(N, L), 0.0);\n            vec3 H = normalize(L + V);\n            float spec = pow(max(dot(N, H), 0.0), uShininess);\n            specular += uLights[i].colorInner.rgb * (spec * facing * atten);\n        }\n    }\n\n    // emissive self-illuminates: added AFTER lighting so it glows at full\n    // strength regardless of the scene lights (neon, lava, glowing eyes).\n    vec3 emissive = uEmissive;\n#ifdef INSTANCE_DATA\n    emissive += vInstanceData.rgb;\n#endif\n    fragColor = vec4(base.rgb * lit + specular * uSpecular + emissive, base.a);\n}\n","#version 300 es\n// Lit mesh vertex shader (Camera3d + Light3d) — retained-mode.\n//\n// GLSL ES 3.00 because the fragment stage reads light data from a uniform\n// block, which does not exist in ES 1.00. Both stages of a program must share\n// a dialect, so the vertex shader follows.\n//\n// Same placement as mesh.vert (clip = projection × view × model × vertex,\n// vertices in MODEL space) plus a normal carried to the fragment shader in\n// world space for diffuse shading.\nin vec3 aVertex;\nin vec2 aRegion;\nin vec4 aColor;\n// Model-space normal, rotated into world space below.\nin vec3 aNormal;\n\nuniform mat4 uProjectionMatrix;\nuniform mat4 uViewMatrix;\nuniform mat4 uModelMatrix;\nuniform vec4 uTint;\n\nout vec2 vRegion;\nout vec4 vColor;\nout vec3 vNormal;\n// world-space fragment position — positional lights (point / spot) fall\n// off with distance, so the fragment stage needs where the surface IS\nout vec3 vWorldPos;\n\nvoid main(void) {\n    vec4 worldPos = uModelMatrix * vec4(aVertex, 1.0);\n    gl_Position = uProjectionMatrix * uViewMatrix * worldPos;\n    vWorldPos = worldPos.xyz;\n    vec4 tinted = aColor * uTint;\n    vColor = vec4(tinted.rgb * tinted.a, tinted.a);\n    vRegion = aRegion;\n    // Rotate the normal into world space with the model matrix's upper 3×3.\n    // Lighting is evaluated in world space, so the view transform is\n    // deliberately excluded. Uniform scale cancels when the fragment shader\n    // renormalizes; non-uniform scale is approximated — unchanged behaviour,\n    // an exact result would need the inverse-transpose.\n    vNormal = mat3(uModelMatrix) * aNormal;\n}\n","#version 300 es\n// Instanced lit mesh vertex shader (#1508).\n//\n// The lit mesh vertex stage drawn many times from one copy of the geometry:\n// `aVertex`/`aRegion`/`aColor`/`aNormal` advance per vertex, the `aInstance*`\n// attributes once per INSTANCE (their buffer is declared with a divisor of 1):\n//\n//   clip = projection × view × model(group) × instance × vertex\n//\n// GLSL ES 3.00 for the same reason mesh-lit.vert is — the fragment stage reads\n// light data from a uniform block, and both stages of a program must share a\n// dialect. (The UNLIT instanced shader stays ES 1.00: `attribute mat4`/`vec4`\n// are legal there, so instancing itself forces no dialect change.)\n//\n// The instance transform arrives as three ROW-major vec4 rows rather than a\n// full mat4 — the bottom row of an affine matrix is always (0,0,0,1), so\n// storing it would waste 16 bytes and an attribute slot per instance. GLSL's\n// mat4 constructor takes COLUMNS, hence the transpose below.\nin vec3 aVertex;\nin vec2 aRegion;\nin vec4 aColor;\nin vec3 aNormal;\n\nin vec4 aInstanceRow0;\nin vec4 aInstanceRow1;\nin vec4 aInstanceRow2;\n#ifdef INSTANCE_COLORS\nin vec4 aInstanceColor;\n#endif\n#ifdef INSTANCE_DATA\nin vec4 aInstanceData;\n#endif\n\nuniform mat4 uProjectionMatrix;\nuniform mat4 uViewMatrix;\nuniform mat4 uModelMatrix;\nuniform vec4 uTint;\n\nout vec2 vRegion;\nout vec4 vColor;\nout vec3 vNormal;\nout vec3 vWorldPos;\n#ifdef INSTANCE_DATA\nout vec4 vInstanceData;\n#endif\n\nmat4 instanceMatrix() {\n    return mat4(\n        vec4(aInstanceRow0.x, aInstanceRow1.x, aInstanceRow2.x, 0.0),\n        vec4(aInstanceRow0.y, aInstanceRow1.y, aInstanceRow2.y, 0.0),\n        vec4(aInstanceRow0.z, aInstanceRow1.z, aInstanceRow2.z, 0.0),\n        vec4(aInstanceRow0.w, aInstanceRow1.w, aInstanceRow2.w, 1.0));\n}\n\nvoid main(void) {\n    mat4 instance = instanceMatrix();\n    vec4 worldPos = uModelMatrix * instance * vec4(aVertex, 1.0);\n    gl_Position = uProjectionMatrix * uViewMatrix * worldPos;\n    vWorldPos = worldPos.xyz;\n\n    vec4 tinted = aColor * uTint;\n#ifdef INSTANCE_COLORS\n    tinted *= aInstanceColor;\n#endif\n    vColor = vec4(tinted.rgb * tinted.a, tinted.a);\n    vRegion = aRegion;\n\n    // Rotate the normal into world space through BOTH transforms, in the same\n    // order the position takes them. Uniform scale cancels when the fragment\n    // shader renormalizes; non-uniform scale is approximated exactly as the\n    // non-instanced path approximates it (an exact result would need the\n    // inverse-transpose) — so an instanced mesh shades like its uninstanced\n    // twin rather than subtly differently.\n    vNormal = mat3(uModelMatrix) * mat3(instance) * aNormal;\n#ifdef INSTANCE_DATA\n    vInstanceData = aInstanceData;\n#endif\n}\n","/**\n * The versioned-remap vertex dedup shared by the mesh batchers of both\n * backends — pure CPU code, no GPU references (the `lighting/` modules set\n * the precedent for cross-backend sharing).\n *\n * The accumulated mesh path re-indexes each chunk's triangles against a\n * chunk-local vertex range. Dedup uses a \"versioned\" typed-array remap\n * rather than a `Map`: a `Map` here churned the GC badly, because V8's\n * `Map.clear()` drops the backing table, so re-filling it each chunk\n * reallocated as it grew — and the cost scaled with vertex count (a dense\n * mesh = MBs/sec of garbage). Instead, `remapSlot[orig]` holds the local\n * index assigned to original-vertex `orig` THIS chunk, valid only when\n * `remapStamp[orig] === stamp`. Bumping `stamp` per chunk invalidates every\n * entry in O(1) — no clearing, no allocation. The arrays grow lazily (to a\n * power of two ≥ the largest mesh's vertex count) and are reused.\n *\n * Module-level sharing is safe because a chunk is built synchronously and\n * never re-enters — only one `addMesh` runs at a time, whichever backend\n * (or coexisting renderer) drives it, and no remap state persists across\n * chunks.\n * @ignore\n */\n\nlet remapSlot = new Int32Array(0);\nlet remapStamp = new Int32Array(0);\nlet stamp = 0;\nconst chunkIndices: number[] = [];\n\n/**\n * Ensure the versioned-remap scratch arrays can index every vertex of a\n * mesh with `vertexCount` vertices. Grows to the next power of two and\n * reuses thereafter (one-time cost when a larger mesh first appears).\n * @ignore\n */\nexport function ensureRemapCapacity(vertexCount: number): void {\n\tif (remapSlot.length >= vertexCount) {\n\t\treturn;\n\t}\n\t// next power of two ≥ vertexCount (Math.clz32 → leading-zero count)\n\tconst cap = vertexCount <= 1 ? 1 : 1 << (32 - Math.clz32(vertexCount - 1));\n\tremapSlot = new Int32Array(cap);\n\tremapStamp = new Int32Array(cap); // zero-filled; stamp is always ≥ 1 in use\n}\n\n/**\n * Start a new chunk: invalidate the whole remap in O(1) and hand back the\n * shared (emptied) chunk-index list. The stamp resets before int32 overflow\n * (~weeks of continuous rendering away), keeping stored stamps valid.\n * @returns the shared chunk-index array, emptied\n * @ignore\n */\nexport function beginChunk(): number[] {\n\tif (stamp >= 0x7fffffff) {\n\t\tremapStamp.fill(0);\n\t\tstamp = 0;\n\t}\n\tstamp++;\n\tchunkIndices.length = 0;\n\treturn chunkIndices;\n}\n\n/**\n * The local index assigned to `origIdx` this chunk, or -1 when the vertex\n * has not been emitted yet.\n * @ignore\n */\nexport function remapIndex(origIdx: number): number {\n\treturn remapStamp[origIdx] === stamp ? remapSlot[origIdx] : -1;\n}\n\n/**\n * Record the local index assigned to `origIdx` for the rest of this chunk.\n * @ignore\n */\nexport function assignIndex(origIdx: number, localIdx: number): void {\n\tremapStamp[origIdx] = stamp;\n\tremapSlot[origIdx] = localIdx;\n}\n","/**\n * The GPU half of an {@link InstancedMesh}'s instance records — one\n * `ARRAY_BUFFER` holding the packed per-instance transforms (and the opt-in\n * colour / custom slots), uploaded with `bufferSubData` over only the range\n * the CPU side actually touched.\n *\n * This is the instancing counterpart of {@link RetainedGeometry}: the mesh's\n * geometry is uploaded once and never rewritten, and this buffer carries\n * everything that differs between the copies. Moving one tree in a forest of\n * five thousand therefore re-uploads 48 bytes, not the geometry and not the\n * other 4 999 records.\n *\n * The buffer is sized to the mesh's capacity rather than its live instance\n * count, so growing by one instance does not reallocate on the GPU until the\n * CPU-side array itself grows.\n * @ignore\n */\nexport default class WebGLInstanceBuffer {\n\t/**\n\t * @param {WebGL2RenderingContext} gl - the WebGL context\n\t */\n\tconstructor(gl) {\n\t\tthis.gl = gl;\n\n\t\t/**\n\t\t * the GL buffer holding the packed instance records\n\t\t * @type {WebGLBuffer}\n\t\t */\n\t\tthis.buffer = gl.createBuffer();\n\n\t\t/**\n\t\t * byte capacity currently allocated on the GPU; a CPU-side array\n\t\t * larger than this forces a full reallocation rather than a sub-update\n\t\t * @type {number}\n\t\t */\n\t\tthis.capacity = 0;\n\t}\n\n\t/**\n\t * Push the dirty span of `data` to the GPU.\n\t *\n\t * Three cases, cheapest last: the CPU array outgrew the allocation (full\n\t * `bufferData`), nothing is dirty (no call at all), or a span changed\n\t * (one `bufferSubData` covering exactly it).\n\t * @param {Float32Array} data - the CPU-side instance records\n\t * @param {number} firstFloat - first dirty float offset (inclusive)\n\t * @param {number} floatCount - number of dirty floats, 0 when clean\n\t * @param {number} usedBytes - bytes of `data` actually in use\n\t */\n\tupload(data, firstFloat, floatCount, usedBytes) {\n\t\tconst gl = this.gl;\n\t\tif (floatCount === 0 && usedBytes <= this.capacity) {\n\t\t\t// nothing to do — and no reason to perturb the ARRAY_BUFFER binding\n\t\t\treturn;\n\t\t}\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\n\n\t\tif (usedBytes > this.capacity) {\n\t\t\t// (re)allocate to the CPU array's full length, so subsequent\n\t\t\t// growth up to that capacity is a sub-update rather than a realloc\n\t\t\tgl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);\n\t\t\tthis.capacity = data.byteLength;\n\t\t\treturn;\n\t\t}\n\n\t\tif (floatCount === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// upload through a Uint8Array view for the same reason the retained\n\t\t// geometry does: some drivers canonicalize NaN bit patterns on a\n\t\t// float upload, which would corrupt packed values\n\t\tconst byteOffset = firstFloat * Float32Array.BYTES_PER_ELEMENT;\n\t\tconst byteLength = floatCount * Float32Array.BYTES_PER_ELEMENT;\n\t\tgl.bufferSubData(\n\t\t\tgl.ARRAY_BUFFER,\n\t\t\tbyteOffset,\n\t\t\tnew Uint8Array(data.buffer, data.byteOffset + byteOffset, byteLength),\n\t\t);\n\t}\n\n\t/**\n\t * Release the GL buffer. Safe to call twice, and on a lost context — the\n\t * `isBuffer` probe is itself error-free, whereas deleting a handle whose\n\t * context died raises INVALID_OPERATION.\n\t */\n\tdestroy() {\n\t\tconst gl = this.gl;\n\t\tif (this.buffer !== null) {\n\t\t\tif (gl.isBuffer(this.buffer)) {\n\t\t\t\tgl.deleteBuffer(this.buffer);\n\t\t\t}\n\t\t\tthis.buffer = null;\n\t\t}\n\t\tthis.capacity = 0;\n\t}\n}\n","import WebGLVertexState from \"./vertexstate.js\";\n\n/**\n * Persistent GPU geometry for one mesh — a vertex buffer, an index buffer\n * and the {@link WebGLVertexState} that binds them together, all uploaded\n * once and re-drawn from VRAM on subsequent frames.\n *\n * This is what makes the mesh path retained rather than immediate: the\n * geometry it holds is in model space and carries no camera, placement or\n * tint information, so moving, rotating, scaling or re-tinting a mesh\n * changes only uniforms and never touches these buffers. They are rewritten\n * only when the geometry itself changes, which the owning mesh signals by\n * bumping its geometry version.\n *\n * Uploads go through a `Uint8Array` view of the float data rather than the\n * `Float32Array` itself: some drivers canonicalize NaN bit patterns on a\n * float upload, which would corrupt packed values. See `VertexArrayBuffer`.\n * @ignore\n */\nexport default class RetainedGeometry {\n\t/**\n\t * @param {WebGL2RenderingContext} gl - the WebGL context\n\t * @param {object[]} attributes - the owning batcher's attribute definitions\n\t * @param {number} stride - the owning batcher's vertex stride in bytes\n\t * @param {Function} resolveLocation - maps an attribute name to its shader location\n\t */\n\tconstructor(gl, attributes, stride, resolveLocation) {\n\t\tthis.gl = gl;\n\t\tthis.attributes = attributes;\n\t\tthis.stride = stride;\n\t\tthis.resolveLocation = resolveLocation;\n\n\t\t/**\n\t\t * the GL buffer holding the interleaved model-space vertex data\n\t\t * @type {WebGLBuffer}\n\t\t */\n\t\tthis.vertexBuffer = gl.createBuffer();\n\n\t\t/**\n\t\t * the GL buffer holding the index data, as authored\n\t\t * @type {WebGLBuffer}\n\t\t */\n\t\tthis.glIndexBuffer = gl.createBuffer();\n\n\t\t/**\n\t\t * `gl.UNSIGNED_SHORT` or `gl.UNSIGNED_INT`, matching the index array\n\t\t * the mesh supplied — WebGL 2 draws 32-bit indices natively, so a\n\t\t * mesh past 65 535 vertices is still a single draw call.\n\t\t * @type {number}\n\t\t */\n\t\tthis.indexType = gl.UNSIGNED_SHORT;\n\n\t\t/**\n\t\t * number of indices to draw\n\t\t * @type {number}\n\t\t */\n\t\tthis.indexCount = 0;\n\n\t\t/**\n\t\t * the mesh geometry version this upload reflects; a mismatch against\n\t\t * the mesh means the buffers are stale and must be rewritten\n\t\t * @type {number}\n\t\t */\n\t\tthis.uploadedVersion = -1;\n\n\t\t// `WebGLIndexBuffer`-shaped shim: the vertex state captures whatever\n\t\t// its descriptor's `indexBuffer.bind()` binds, and this geometry owns\n\t\t// a raw GL buffer rather than a full index-buffer wrapper (there is no\n\t\t// CPU-side accumulation to do — the data is uploaded once).\n\t\tconst indexBinding = {\n\t\t\tbind: () => {\n\t\t\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glIndexBuffer);\n\t\t\t},\n\t\t};\n\n\t\t/**\n\t\t * the frozen vertex state binding this geometry's buffers\n\t\t * @type {WebGLVertexState}\n\t\t */\n\t\tthis.vertexState = new WebGLVertexState(gl, {\n\t\t\tattributes,\n\t\t\tstride,\n\t\t\tbuffer: this.vertexBuffer,\n\t\t\tindexBuffer: indexBinding,\n\t\t\tresolveLocation,\n\t\t});\n\t}\n\n\t/**\n\t * Upload interleaved vertex data and indices, replacing whatever this\n\t * geometry held before.\n\t * @param {Float32Array} vertexData - interleaved vertex data in the batcher's layout\n\t * @param {number} floatCount - how many floats of `vertexData` are in use\n\t * @param {Uint16Array|Uint32Array|number[]} indices - the index data, as authored\n\t * @param {number} version - the mesh geometry version this data reflects\n\t */\n\tupload(vertexData, floatCount, indices, version) {\n\t\tconst gl = this.gl;\n\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);\n\t\tgl.bufferData(\n\t\t\tgl.ARRAY_BUFFER,\n\t\t\tnew Uint8Array(\n\t\t\t\tvertexData.buffer,\n\t\t\t\tvertexData.byteOffset,\n\t\t\t\tfloatCount * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t),\n\t\t\tgl.STATIC_DRAW,\n\t\t);\n\n\t\t// keep 32-bit indices 32-bit: narrowing them would silently corrupt\n\t\t// any mesh with more than 65 535 vertices\n\t\tconst wide =\n\t\t\tindices instanceof Uint32Array ||\n\t\t\t(!(indices instanceof Uint16Array) && vertexDataExceedsUint16(indices));\n\t\tconst indexArray = wide\n\t\t\t? indices instanceof Uint32Array\n\t\t\t\t? indices\n\t\t\t\t: new Uint32Array(indices)\n\t\t\t: indices instanceof Uint16Array\n\t\t\t\t? indices\n\t\t\t\t: new Uint16Array(indices);\n\t\tthis.indexType = wide ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;\n\t\tthis.indexCount = indexArray.length;\n\n\t\t// bind through the vertex state so the ELEMENT_ARRAY_BUFFER binding\n\t\t// is captured by it rather than leaking into whatever is current\n\t\tthis.vertexState.captureIndexBuffer(undefined, () => {\n\t\t\tgl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);\n\t\t});\n\n\t\tthis.uploadedVersion = version;\n\t}\n\n\t/**\n\t * Make this geometry current for drawing.\n\t */\n\tbind() {\n\t\tthis.vertexState.bind();\n\t}\n\n\t/**\n\t * Release every GL object this geometry owns. Safe to call twice, and on\n\t * a lost context.\n\t */\n\tdestroy() {\n\t\tconst gl = this.gl;\n\t\tif (this.vertexState !== null) {\n\t\t\tthis.vertexState.destroy();\n\t\t\tthis.vertexState = null;\n\t\t}\n\t\t// `isBuffer` is false for a handle belonging to a context that has since\n\t\t// been lost, and asking is itself error-free. Deleting such a handle\n\t\t// after the context came back raises INVALID_OPERATION — the objects\n\t\t// died with the old context, so there is nothing left to release.\n\t\tif (this.vertexBuffer !== null) {\n\t\t\tif (gl.isBuffer(this.vertexBuffer)) {\n\t\t\t\tgl.deleteBuffer(this.vertexBuffer);\n\t\t\t}\n\t\t\tthis.vertexBuffer = null;\n\t\t}\n\t\tif (this.glIndexBuffer !== null) {\n\t\t\tif (gl.isBuffer(this.glIndexBuffer)) {\n\t\t\t\tgl.deleteBuffer(this.glIndexBuffer);\n\t\t\t}\n\t\t\tthis.glIndexBuffer = null;\n\t\t}\n\t\tthis.indexCount = 0;\n\t\tthis.uploadedVersion = -1;\n\t}\n}\n\n/**\n * Whether a plain index array needs 32-bit indices.\n * @ignore\n */\nfunction vertexDataExceedsUint16(indices) {\n\tfor (let i = 0; i < indices.length; i++) {\n\t\tif (indices[i] > 0xffff) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n","uniform sampler2D uSampler;\nuniform float uAlphaCutoff;   // alpha cutout threshold (0 = disabled)\nuniform vec3 uEmissive;       // self-illumination color added on top (0 = none)\nuniform sampler2D uAlphaMap;  // per-texel opacity (MTL map_d)\nuniform float uHasAlphaMap;   // 0 = uAlphaMap is filler, ignore it\nvarying vec4 vColor;\nvarying vec2 vRegion;\n#ifdef INSTANCE_DATA\n// per-instance custom slot. The built-in shading reads its rgb as emissive,\n// so a forest can glow per tree without a uniform per instance; a CUSTOM mesh\n// shader is free to read the same slot as anything else entirely.\nvarying vec4 vInstanceData;\n#endif\n\nvoid main(void) {\n    vec4 color = texture2D(uSampler, vRegion) * vColor;\n    // per-texel opacity (MTL map_d) multiplies in BEFORE the cutout, so one\n    // material can cut out to the shape of a leaf rather than at a single\n    // threshold across the whole surface. Red channel: the format stores a\n    // greyscale map, and every channel carries the same value.\n    // Sampled unconditionally and WEIGHTED rather than branched: when no map\n    // is bound the second sampler is filler (the diffuse texture), so the\n    // value is thrown away — and both backends run the identical expression\n    // instead of one branching and the other not.\n    color.a *= mix(1.0, texture2D(uAlphaMap, vRegion).r, uHasAlphaMap);\n    // hard alpha cutout (glTF alphaMode MASK): drop fully-transparent texels\n    // so foliage / fences / decals read crisp without blending or sorting.\n    if (color.a < uAlphaCutoff) {\n        discard;\n    }\n    // emissive adds a self-lit color on top (neon, lava, screens); the unlit\n    // path has no lighting, so it's simply added to the base color.\n    vec3 emissive = uEmissive;\n#ifdef INSTANCE_DATA\n    emissive += vInstanceData.rgb;\n#endif\n    gl_FragColor = vec4(color.rgb + emissive, color.a);\n}\n","// Mesh vertex shader (retained-mode).\n//\n// Vertices arrive in MODEL space and are placed by uniforms rather than by\n// CPU-side transformation, so moving/rotating/scaling a mesh costs a uniform\n// update instead of re-uploading its geometry:\n//\n//   clip = projection × view × model × vertex\n//\n// `uModelMatrix` carries the mesh's own transform (including the Y/Z axis\n// bridge and mesh scale); `uViewMatrix` carries the camera view plus any\n// ancestor container transform. Both are always written before a draw — the\n// accumulation path (2D camera, vertices pre-projected on the CPU) pushes\n// identity explicitly. Note GLSL uniforms default to ZERO, not identity, so a\n// path that reached this shader without setting them would collapse every\n// vertex onto the origin rather than drawing untransformed.\nattribute vec3 aVertex;\nattribute vec2 aRegion;\nattribute vec4 aColor;\n\nuniform mat4 uProjectionMatrix;\nuniform mat4 uViewMatrix;\nuniform mat4 uModelMatrix;\n// Per-draw tint × global alpha. Kept out of the vertex data so that changing\n// a mesh's tint never invalidates its retained geometry.\nuniform vec4 uTint;\n\nvarying vec2 vRegion;\nvarying vec4 vColor;\n\nvoid main(void) {\n    gl_Position =\n        uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertex, 1.0);\n    // tint first, then premultiply — matches the fragment shader's expectation\n    vec4 tinted = aColor * uTint;\n    vColor = vec4(tinted.rgb * tinted.a, tinted.a);\n    vRegion = aRegion;\n}\n","// Instanced mesh vertex shader (#1508).\n//\n// The unlit mesh vertex stage, with the geometry drawn many times from one\n// copy: `aVertex`/`aRegion`/`aColor` advance per vertex as usual, while the\n// `aInstance*` attributes advance once per INSTANCE (their vertex buffer is\n// declared with a divisor of 1). So one geometry plus a small per-instance\n// record produces N copies in a single draw call:\n//\n//   clip = projection × view × model(group) × instance × vertex\n//\n// `uModelMatrix` places the whole group — moving an InstancedMesh is still\n// one uniform write — and the per-instance transform places each copy within\n// it.\n//\n// The instance transform arrives as three ROW-major vec4 rows rather than a\n// full mat4: the bottom row of an affine matrix is always (0,0,0,1), so\n// storing it would waste 16 bytes and a fourth attribute slot per instance.\n// GLSL's mat4 constructor takes COLUMNS, hence the transpose below.\nattribute vec3 aVertex;\nattribute vec2 aRegion;\nattribute vec4 aColor;\n\nattribute vec4 aInstanceRow0;\nattribute vec4 aInstanceRow1;\nattribute vec4 aInstanceRow2;\n#ifdef INSTANCE_COLORS\nattribute vec4 aInstanceColor;\n#endif\n#ifdef INSTANCE_DATA\nattribute vec4 aInstanceData;\n#endif\n\nuniform mat4 uProjectionMatrix;\nuniform mat4 uViewMatrix;\nuniform mat4 uModelMatrix;\nuniform vec4 uTint;\n\nvarying vec2 vRegion;\nvarying vec4 vColor;\n#ifdef INSTANCE_DATA\nvarying vec4 vInstanceData;\n#endif\n\nmat4 instanceMatrix() {\n    return mat4(\n        vec4(aInstanceRow0.x, aInstanceRow1.x, aInstanceRow2.x, 0.0),\n        vec4(aInstanceRow0.y, aInstanceRow1.y, aInstanceRow2.y, 0.0),\n        vec4(aInstanceRow0.z, aInstanceRow1.z, aInstanceRow2.z, 0.0),\n        vec4(aInstanceRow0.w, aInstanceRow1.w, aInstanceRow2.w, 1.0));\n}\n\nvoid main(void) {\n    mat4 instance = instanceMatrix();\n    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * instance\n        * vec4(aVertex, 1.0);\n\n    vec4 tinted = aColor * uTint;\n#ifdef INSTANCE_COLORS\n    tinted *= aInstanceColor;\n#endif\n    // tint first, then premultiply — matches the fragment shader's expectation\n    vColor = vec4(tinted.rgb * tinted.a, tinted.a);\n    vRegion = aRegion;\n#ifdef INSTANCE_DATA\n    vInstanceData = aInstanceData;\n#endif\n}\n","// Instanced ground-shadow vertex shader (#1515).\n//\n// One flat blob per instance, drawn from the SAME instance buffer the mesh\n// itself uses — so a forest of a hundred thousand trees costs one extra draw\n// call, not a hundred thousand.\n//\n// This is a STANDALONE shader, not a variant of the instanced mesh family,\n// and that is the whole trick. It reads only the three transform rows, never\n// `aInstanceColor` or `aInstanceData`, which means:\n//\n//   * one shader serves every record layout — no hasColor/hasData matrix;\n//   * a forest with per-instance colour and per-instance emissive cannot end\n//     up with coloured, glowing shadows, because neither slot is read;\n//   * the vertex layout can declare just the rows, leaving the buffer's\n//     stride and offsets untouched.\n//\n// The instance's rotation and vertical scale are DISCARDED: a blob lies flat\n// on the ground whichever way its tree is turned, and only the horizontal\n// footprint decides how wide it is. What survives is the translation and the\n// horizontal scale, both read straight out of the rows with no matrix built:\n//\n//   translation      = (aInstanceRow0.w, aInstanceRow1.w, aInstanceRow2.w)\n//   horizontal scale = length(vec3(aInstanceRow0.x, aInstanceRow1.x, aInstanceRow2.x))\n//\n// Flattening onto the ground plane is `uModelMatrix`'s job: the caller passes\n// the group matrix with its Y basis column zeroed and its translation Y set\n// to the ground height, so whatever Y this stage produces is discarded and\n// every blob lands on the floor.\nattribute vec3 aVertex;\nattribute vec2 aRegion;\nattribute vec4 aColor;\n\nattribute vec4 aInstanceRow0;\nattribute vec4 aInstanceRow1;\nattribute vec4 aInstanceRow2;\n\nuniform mat4 uProjectionMatrix;\nuniform mat4 uViewMatrix;\nuniform mat4 uModelMatrix;\nuniform vec4 uTint;\n\nvarying vec2 vRegion;\nvarying vec4 vColor;\n\nvoid main(void) {\n    vec3 instancePos = vec3(aInstanceRow0.w, aInstanceRow1.w, aInstanceRow2.w);\n    // column 0 of the instance basis carries the X axis times its scale; a\n    // scatter is rotated about the vertical axis, so its length is the\n    // horizontal footprint whatever the rotation\n    float footprint = length(\n        vec3(aInstanceRow0.x, aInstanceRow1.x, aInstanceRow2.x));\n\n    // the quad is a unit square in the ground plane (XZ); only its horizontal\n    // extent is scaled, and its own Y is irrelevant — uModelMatrix flattens it\n    vec3 local = instancePos + vec3(aVertex.x, 0.0, aVertex.z) * footprint;\n\n    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix\n        * vec4(local, 1.0);\n\n    // tint first, then premultiply — matches the fragment shader's expectation\n    vec4 tinted = aColor * uTint;\n    vColor = vec4(tinted.rgb * tinted.a, tinted.a);\n    vRegion = aRegion;\n}\n","import { Matrix3d } from \"../../../math/matrix3d.ts\";\nimport { off, on, RENDER_TARGET_CHANGED } from \"../../../system/event.ts\";\nimport { instanceAttributes } from \"../../gpu/instancerecord.ts\";\nimport {\n\tassignIndex,\n\tbeginChunk,\n\tensureRemapCapacity,\n\tremapIndex,\n} from \"../../gpu/meshchunk.ts\";\nimport { buildMeshVertexData, retainedScratch } from \"../../gpu/meshvertex.ts\";\nimport WebGLInstanceBuffer from \"../buffer/instance_buffer.js\";\nimport RetainedGeometry from \"../buffer/retained_geometry.js\";\nimport WebGLVertexState from \"../buffer/vertexstate.js\";\nimport GLShader from \"../glshader.js\";\nimport meshFragment from \"./../shaders/mesh.frag\";\nimport meshVertex from \"./../shaders/mesh.vert\";\nimport meshInstancedVertex from \"./../shaders/mesh-instanced.vert\";\nimport meshShadowInstancedVertex from \"./../shaders/mesh-shadow-instanced.vert\";\nimport { injectDefines } from \"../utils/string.js\";\nimport { MaterialBatcher } from \"./material_batcher.js\";\n\n// Shared identity model matrix for draws whose vertices are already placed\n// (the 2D-camera path pre-projects them on the CPU). Never mutated.\nconst _IDENTITY_MATRIX = new Matrix3d();\n\n// Scratch for unpacking a packed ARGB tint into the shader's vec4. Reused —\n// setPlacementUniforms runs synchronously and never re-enters.\nconst _TINT_RGBA = new Float32Array(4);\n\n// The per-chunk vertex dedup (versioned typed-array remap) lives in the\n// backend-neutral `gpu/meshchunk.ts`, shared with the WebGPU mesh batcher —\n// see the rationale there. Safe because addMesh runs synchronously and never\n// re-enters (flush() only draws); only one addMesh runs at a time.\n\n// The lazy-depth-clear state for the mesh-mode pass lives on the RENDERER\n// (`renderer._meshDepthDirty`), not per batcher instance: the unlit\n// `MeshBatcher` and the `LitMeshBatcher` — which extends it and overrides\n// `updatePassState()` — must coordinate on a SINGLE depth clear per target\n// (per-instance\n// flags would re-clear the shared depth buffer when switching between the two\n// mid-frame and break inter-mesh occlusion), while two coexisting renderers\n// must NOT share it (a module-level flag let one Application's `clear()`\n// re-arm — or steal — the other's depth clear mid-frame). The first draw of\n// either batcher clears + marks clean; `RENDER_TARGET_CHANGED` from the\n// owning renderer re-arms it.\n\n// shared zero emissive, passed to the shader when a mesh has no emission so the\n// `uEmissive` add is a no-op. Never mutated.\nconst _ZERO_EMISSIVE = new Float32Array(3);\n\n// scratch for the camera's world position, recomputed per draw that needs it\nconst _EYE_POSITION = new Float32Array(3);\n\n/**\n * A WebGL Batcher for rendering textured triangle meshes.\n * Uses indexed drawing to efficiently render arbitrary triangle geometry.\n *\n * Owns mesh-mode GL state ownership (since 19.7 / #1468):\n *\n * - {@link MeshBatcher#bind} enters mesh mode — enables `DEPTH_TEST` +\n *   `LEQUAL` + `depthMask`, disables `BLEND`.\n * - {@link MeshBatcher#updatePassState} runs the one-shot\n *   `clearDepth(1.0) + clear(DEPTH_BUFFER_BIT)` when the active target's\n *   depth attachment is still dirty, immediately before a draw rather than\n *   on entry — `bind()` fires on a batcher *transition*, not once a frame,\n *   so a scene made only of meshes would otherwise never clear again after\n *   its first frame. Subsequent draws against the same target rely on the\n *   accumulated depth buffer.\n * - {@link MeshBatcher#unbind} exits mesh mode — restores non-mesh\n *   defaults (`BLEND` on, `DEPTH_TEST` off, `depthMask` false) that the\n *   2D rendering paths assume.\n * - Subscribes to {@link event.RENDER_TARGET_CHANGED} (emitted by the\n *   renderer at frame-start `clear()`, non-camera FBO bind, post-effect\n *   FBO unbind) to re-arm the lazy depth clear whenever the active\n *   framebuffer's attachments change identity.\n *\n * The WebGLRenderer doesn't know any of this — `setBatcher(\"mesh\")` calls\n * `bind()` and the batcher sets up its own pass, refreshing whatever is\n * per-draw as it goes. Same lifecycle ports\n * cleanly to a future WebGPU renderer: `bind()` becomes \"begin a\n * depth-enabled `RenderPassEncoder`\", `unbind()` ends it.\n * @category Rendering\n */\nexport default class MeshBatcher extends MaterialBatcher {\n\t/**\n\t * Initialize the mesh batcher\n\t * @ignore\n\t */\n\tinit(renderer) {\n\t\tsuper.init(renderer, {\n\t\t\tattributes: this._attributeLayout(renderer),\n\t\t\tshader: this._shaderSources(),\n\t\t\tindexed: true,\n\t\t});\n\n\t\t// last `uAlphaCutoff` value pushed to the current shader, so consecutive\n\t\t// meshes sharing a cutoff don't re-issue the uniform. -1 is an impossible\n\t\t// cutoff (valid range 0..1), forcing the first mesh of a pass to set it.\n\t\tthis.currentAlphaCutoff = -1;\n\n\t\t// last `uEmissive` value pushed (per channel), same redundant-set guard.\n\t\t// -1 is an impossible emissive (valid range 0..∞), forcing the first set.\n\t\tthis.currentEmissiveR = -1;\n\t\tthis.currentEmissiveG = -1;\n\t\tthis.currentEmissiveB = -1;\n\n\t\t// last `uShininess` / `uSpecular` pushed — -1 is impossible (valid\n\t\t// range 0..inf), forcing the first set of a pass\n\t\tthis.currentShininess = -1;\n\t\tthis.currentSpecularR = -1;\n\t\tthis.currentSpecularG = -1;\n\t\tthis.currentSpecularB = -1;\n\n\t\t// last `uAlphaMap` unit and `uHasAlphaMap` flag pushed; -1 is not a\n\t\t// valid unit or flag, so the first mesh of a pass always sets both\n\t\tthis.currentAlphaMapUnit = -1;\n\t\tthis.currentHasAlphaMap = -1;\n\n\t\t// last `uEyePosition` pushed. NaN never equals itself, so the first\n\t\t// camera position of a pass always sets it whatever it is\n\t\tthis.currentEyeX = Number.NaN;\n\t\tthis.currentEyeY = Number.NaN;\n\t\tthis.currentEyeZ = Number.NaN;\n\n\t\t// Retained geometry per mesh (model-space buffers uploaded once). A\n\t\t// re-init means a new GL context or a fresh batcher life, so anything\n\t\t// held is stale — release it rather than leak it.\n\t\tif (this.retained !== undefined) {\n\t\t\tthis.releaseAllRetained();\n\t\t}\n\t\tthis.retained = new Map();\n\n\t\t// Per-mesh instance buffers, and the vertex states pairing a mesh's\n\t\t// retained geometry with its instance records. Same lifetime rule as\n\t\t// `retained`: a re-init means a new context, so drop what is held.\n\t\tif (this.instanced !== undefined) {\n\t\t\tthis.releaseAllInstanced();\n\t\t}\n\t\tthis.instanced = new Map();\n\n\t\t// Instanced shader variants, keyed on the opt-in slots a mesh\n\t\t// declares. Compiled on first use rather than up front: most scenes\n\t\t// use one combination, and a scene with no instanced mesh at all\n\t\t// compiles none. Dropped on re-init with everything else GL-owned.\n\t\tthis.instancedShaders?.forEach((shader) => {\n\t\t\tshader.destroy();\n\t\t});\n\t\tthis.instancedShaders = new Map();\n\n\t\t// the standalone ground-shadow program (#1515) is GL-owned too. An\n\t\t// orphan keeps its program AND its context-lost/restored subscriptions\n\t\t// alive, and would try to recompile against a dead context on the next\n\t\t// restore — the same hazard the instanced variants above are dropped\n\t\t// for.\n\t\tthis.shadowShader?.destroy();\n\t\tthis.shadowShader = undefined;\n\n\t\t// last `uTint` value pushed, same redundant-set guard — but the\n\t\t// sentinel is `undefined`, NOT a number: a packed ARGB tint spans the\n\t\t// whole 32-bit range, and white at full alpha (0xffffffff) reads back\n\t\t// as -1 when signed, so any numeric sentinel can collide with a real\n\t\t// tint and silently suppress the very first set.\n\t\tthis.currentTintValue = undefined;\n\n\t\t// GL textures already upgraded to trilinear minification (mesh\n\t\t// textures sample their mip chain — see applyMeshMaterial). WeakSet:\n\t\t// entries die with their GL texture objects.\n\t\tthis.trilinearTextures = new WeakSet();\n\n\t\t// 4× anisotropic filtering rides the same upgrade (oblique surfaces\n\t\t// keep detail plain trilinear blurs away). Resolved per init — a\n\t\t// context restore re-runs this against the fresh context.\n\t\tconst gl = this.gl;\n\t\tthis.anisotropicExt = gl.getExtension(\"EXT_texture_filter_anisotropic\");\n\t\tthis.maxAnisotropy = this.anisotropicExt\n\t\t\t? Math.min(\n\t\t\t\t\t4,\n\t\t\t\t\tgl.getParameter(this.anisotropicExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT),\n\t\t\t\t)\n\t\t\t: 0;\n\n\t\t// arm the (renderer-owned) lazy depth clear for the first mesh pass\n\t\trenderer._meshDepthDirty = true;\n\n\t\t// Subscribe to the renderer's target-changed broadcast so we re-arm the\n\t\t// lazy depth clear (`renderer._meshDepthDirty`) whenever the active\n\t\t// framebuffer's attachments change identity (FBO bind/unbind for\n\t\t// post-effects, frame-start `clear()`). Same pattern as\n\t\t// `MaterialBatcher`'s `GPU_TEXTURE_CACHE_RESET` subscription — only\n\t\t// batchers that care subscribe.\n\t\tif (!this._onTargetChanged) {\n\t\t\tthis._onTargetChanged = (emitter) => {\n\t\t\t\t// the event is global — ignore broadcasts from OTHER renderer\n\t\t\t\t// instances (several Applications can coexist on one page);\n\t\t\t\t// a missing emitter (legacy/manual emit) is treated as ours\n\t\t\t\tif (emitter === undefined || emitter === this.renderer) {\n\t\t\t\t\tthis.renderer._meshDepthDirty = true;\n\t\t\t\t}\n\t\t\t};\n\t\t\ton(RENDER_TARGET_CHANGED, this._onTargetChanged);\n\t\t}\n\t}\n\n\t/**\n\t * The vertex attribute layout. The base (unlit) mesh batcher is\n\t * `aVertex` (3) + `aRegion` (2) + `aColor` (4) = 9 floats. Subclasses\n\t * (e.g. {@link LitMeshBatcher}) append their own attributes.\n\t * @ignore\n\t */\n\t_attributeLayout(_renderer) {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"aVertex\",\n\t\t\t\tformat: \"float32x3\",\n\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"aRegion\",\n\t\t\t\tformat: \"float32x2\",\n\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// aColor: 4 normalized floats (R, G, B, A in [0, 1]) rather\n\t\t\t\t// than packed 4×UNSIGNED_BYTE. The byte-packed path is\n\t\t\t\t// byte-identical in memory but exposes the 4-byte slot to\n\t\t\t\t// NaN-pattern bit values when the alpha byte is 0xFF and the\n\t\t\t\t// red byte has its high bit set (R≥0x80) — a NaN-pattern that\n\t\t\t\t// Apple's Metal-backed WebGL driver canonicalizes on some\n\t\t\t\t// upload paths, zeroing the bytes the shader reads. The float\n\t\t\t\t// path uses values in [0, 1] which never form NaN bit patterns.\n\t\t\t\tname: \"aColor\",\n\t\t\t\tformat: \"float32x4\",\n\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * The shader sources for this batcher (unlit by default). Subclasses\n\t * override to supply a lit shader.\n\t * @ignore\n\t */\n\t_shaderSources() {\n\t\treturn { vertex: meshVertex, fragment: meshFragment };\n\t}\n\n\t/**\n\t * Invalidate the per-mesh `uAlphaCutoff` / `uEmissive` caches when the bound\n\t * shader (GL program) changes — the new program's uniforms are at their own\n\t * defaults, so the next mesh must re-issue them rather than trust the cache.\n\t * Mirrors the base batcher's `currentSamplerUnit` reset (same condition), and\n\t * matters when a custom mesh shader declaring those uniforms is interleaved\n\t * with the built-in one on this batcher (e.g. `drawMesh` swapping a custom\n\t * shader in and back out).\n\t * @ignore\n\t */\n\tuseShader(shader) {\n\t\tif (\n\t\t\tthis.currentShader !== shader ||\n\t\t\tthis.renderer.currentProgram !== shader.program\n\t\t) {\n\t\t\tthis.currentAlphaCutoff = -1;\n\t\t\tthis.currentEmissiveR = -1;\n\t\t\tthis.currentEmissiveG = -1;\n\t\t\tthis.currentEmissiveB = -1;\n\t\t\t// the placement uniforms live on the program too — a swapped\n\t\t\t// shader starts at its own defaults, so re-issue them\n\t\t\tthis.currentTintValue = undefined;\n\t\t}\n\t\tsuper.useShader(shader);\n\t}\n\n\t/**\n\t * Unsubscribe the `RENDER_TARGET_CHANGED` listener so a discarded\n\t * batcher doesn't keep getting notified (relevant on context loss /\n\t * renderer teardown). Delegates to `MaterialBatcher.destroy()` for\n\t * the texture-cache-reset listener.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// variants hold GL programs AND stay subscribed to the context-loss\n\t\t// events until destroyed — an orphan would try to recompile against a\n\t\t// dead context on the next restore\n\t\tthis.instancedShaders?.forEach((shader) => {\n\t\t\tshader.destroy();\n\t\t});\n\t\tthis.shadowShader?.destroy();\n\t\tthis.shadowShader = undefined;\n\t\tthis.instancedShaders?.clear();\n\t\tif (this._onTargetChanged) {\n\t\t\toff(RENDER_TARGET_CHANGED, this._onTargetChanged);\n\t\t\tthis._onTargetChanged = null;\n\t\t}\n\t\tthis.releaseAllRetained();\n\t\tsuper.destroy();\n\t}\n\n\t/**\n\t * Drop every retained geometry alongside the batcher's own buffers: a\n\t * reset means the GL buffers are being recreated, so anything still\n\t * referencing the old ones would draw from freed memory. Meshes rebuild\n\t * lazily on their next draw.\n\t * @ignore\n\t */\n\treset() {\n\t\tthis.releaseAllRetained();\n\t\tsuper.reset();\n\t}\n\n\t/**\n\t * Enter mesh-mode GL state: depth test on (LEQUAL — `LEQUAL` not\n\t * `LESS` so coplanar triangles obey the OBJ's draw order; Kenney\n\t * low-poly assets layer feature primitives like eye sockets / pupils\n\t * coincident with the underlying face, and `LESS` would lose them),\n\t * depth write on, blend off.\n\t *\n\t * The renderer doesn't need to know any of this; it just calls\n\t * `bind()` during a `setBatcher` transition and the mesh batcher\n\t * sets up its own pass. The same pattern ports cleanly to WebGPU:\n\t * `bind()` becomes \"begin a depth-enabled render pass\" there.\n\t *\n\t * State that has to be current for each *draw* rather than for each\n\t * batcher transition lives in {@link MeshBatcher#updatePassState}, not\n\t * here — see the note there.\n\t */\n\tbind() {\n\t\tsuper.bind();\n\t\tconst gl = this.gl;\n\t\tgl.enable(gl.DEPTH_TEST);\n\t\tgl.depthFunc(gl.LEQUAL);\n\t\tgl.depthMask(true);\n\t\tgl.disable(gl.BLEND);\n\t}\n\n\t/**\n\t * Refresh pass-scoped state, immediately before a draw.\n\t *\n\t * This exists because `bind()` is *not* a per-frame hook. `setBatcher`\n\t * returns early when the requested batcher is already current, so a scene\n\t * that draws nothing but meshes — no sprites, no UI, no unlit geometry to\n\t * force a switch — binds once and never again. Anything set up in `bind()`\n\t * then freezes at its first-frame value.\n\t *\n\t * The depth clear is one such thing. `renderer._meshDepthDirty` is re-armed\n\t * every time the render target changes (frame-start `clear()`, FBO\n\t * bind/unbind), but a flag nobody reads is a flag that does nothing: left in\n\t * `bind()` it would stay armed forever and the depth attachment would carry\n\t * the first frame's values for the life of the scene, occluding geometry as\n\t * the camera moved. Consuming it here runs the clear once per target, which\n\t * is what the flag always meant. {@link LitMeshBatcher} extends this with\n\t * its light data, which had the same problem.\n\t *\n\t * Cheap by construction: one boolean test per draw in the common case.\n\t * `depthMask` is guaranteed true here — `bind()` sets it and only `unbind()`\n\t * clears it, so this batcher being current implies a writable depth buffer,\n\t * without which `gl.clear(DEPTH_BUFFER_BIT)` would silently do nothing.\n\t * @ignore\n\t */\n\tupdatePassState() {\n\t\tif (this.renderer._meshDepthDirty) {\n\t\t\tconst gl = this.gl;\n\t\t\tgl.clearDepth(1.0);\n\t\t\tgl.clear(gl.DEPTH_BUFFER_BIT);\n\t\t\tthis.renderer._meshDepthDirty = false;\n\t\t}\n\t}\n\n\t/**\n\t * Exit mesh-mode GL state: restore the non-mesh defaults (blend on,\n\t * depth test off, depth write off) that 2D rendering paths assume.\n\t */\n\tunbind() {\n\t\tsuper.unbind();\n\t\tconst gl = this.gl;\n\t\tgl.enable(gl.BLEND);\n\t\tgl.disable(gl.DEPTH_TEST);\n\t\tgl.depthMask(false);\n\t}\n\n\t/**\n\t * Write one vertex into the buffer. The base (unlit) layout is\n\t * `x, y, z, u, v, color`. Subclasses override to append per-vertex data\n\t * matching their {@link MeshBatcher#_attributeLayout} (e.g.\n\t * {@link LitMeshBatcher} pushes the world-space normal too).\n\t * @param {object} vertexData - the batcher's vertex buffer\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} z\n\t * @param {number} u\n\t * @param {number} v\n\t * @param {number} color - packed ARGB Uint32\n\t * @param {object} _mesh - the source mesh (unused here; for subclasses)\n\t * @param {number} _i3 - the source vertex's `index * 3` (for subclasses)\n\t * @ignore\n\t */\n\t_pushVertex(vertexData, x, y, z, u, v, color, _mesh, _i3) {\n\t\tvertexData.pushMesh(x, y, z, u, v, color);\n\t}\n\n\t/**\n\t * Add a textured mesh to the batch. When the mesh has a\n\t * `vertexColors` array (multi-material OBJ + bound MTL), each\n\t * vertex's `aColor` attribute comes from that buffer instead of\n\t * the shared `tint` argument — so multi-material rendering needs\n\t * no extra draw calls per material vs single-material (large\n\t * meshes still get chunked across multiple flushes to fit the\n\t * vertex/index buffer limits — same behavior as single-material).\n\t * The runtime `tint` is applied by the shader through the `uTint`\n\t * uniform rather than being multiplied into each vertex color, so\n\t * flash / fade / team-color effects never touch vertex data.\n\t * @param {object} mesh - a Mesh object with vertices, uvs, indices, and texture properties\n\t * @param {number} tint - tint color in UINT32 (argb) format\n\t */\n\t/**\n\t * Issue the per-draw placement uniforms: where the geometry sits\n\t * (`uModelMatrix`), where the camera is (`uViewMatrix`), and the colour\n\t * it is tinted with (`uTint`).\n\t *\n\t * Keeping these as uniforms rather than baking them into vertex data is\n\t * what lets geometry be uploaded once and reused: moving, rotating,\n\t * scaling or re-tinting a mesh changes only these values.\n\t *\n\t * Each set is guarded on the uniform actually being declared, because\n\t * `GLShader.setUniform` throws on an unknown name and a custom mesh\n\t * shader need not declare all of them.\n\t * @param {Matrix3d} modelMatrix - the mesh's own placement, or identity when its vertices are already positioned\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @ignore\n\t */\n\t/**\n\t * Write one mesh's model-space geometry into `out` in this batcher's\n\t * vertex layout, returning how many floats were written.\n\t *\n\t * The data deliberately carries no placement, camera or tint information\n\t * — those are uniforms — so it stays valid for the lifetime of the\n\t * geometry. Subclasses override to append their own per-vertex data.\n\t * @param {object} mesh - the mesh to read geometry from\n\t * @param {Float32Array} out - destination scratch, at least `vertexCount × vertexSize` long\n\t * @returns {number} number of floats written\n\t * @ignore\n\t */\n\tbuildRetainedVertexData(mesh, out) {\n\t\t// the shared neutral builder — one copy for both backends\n\t\treturn buildMeshVertexData(mesh, out, this.vertexSize);\n\t}\n\n\t/**\n\t * Get this mesh's retained geometry, building or refreshing it when the\n\t * mesh's geometry version has moved on.\n\t * @param {object} mesh - the mesh whose geometry is wanted\n\t * @returns {RetainedGeometry} up-to-date geometry for the mesh\n\t * @ignore\n\t */\n\tretainedGeometryFor(mesh) {\n\t\tlet geometry = this.retained.get(mesh);\n\t\tif (geometry === undefined) {\n\t\t\tgeometry = new RetainedGeometry(\n\t\t\t\tthis.gl,\n\t\t\t\tthis.attributes,\n\t\t\t\tthis.stride,\n\t\t\t\t(name) => {\n\t\t\t\t\treturn this.defaultShader.getAttribLocation(name);\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis.retained.set(mesh, geometry);\n\t\t}\n\t\tconst version = mesh._geometryVersion ?? 0;\n\t\tif (geometry.uploadedVersion !== version) {\n\t\t\tconst scratch = retainedScratch(mesh.vertexCount * this.vertexSize);\n\t\t\tconst floats = this.buildRetainedVertexData(mesh, scratch);\n\t\t\tgeometry.upload(scratch, floats, mesh._indicesOriginal, version);\n\t\t}\n\t\treturn geometry;\n\t}\n\n\t/**\n\t * Draw a mesh from its retained geometry: bind the persistent buffers and\n\t * issue one indexed draw, with placement supplied entirely by uniforms.\n\t *\n\t * Unlike {@link addMesh} this accumulates nothing and never chunks — the\n\t * whole mesh is one draw call regardless of size, except for a\n\t * multi-material model whose materials carry different diffuse textures:\n\t * that draws one indexed range per entry in {@link Mesh#textureGroups}\n\t * (#1573), over the same buffers, with only the sampler moving between\n\t * them.\n\t * @param {object} mesh - the mesh to draw\n\t * @param {Matrix3d} modelMatrix - where the mesh sits in the world\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @ignore\n\t */\n\tdrawRetainedMesh(mesh, modelMatrix, tint) {\n\t\tconst gl = this.gl;\n\n\t\t// A ground shadow is deferred to the end of the mesh pass rather than\n\t\t// drawn here (#1515). It writes no depth, so it has nothing to defend\n\t\t// itself with: every opaque mesh still to come would paint straight\n\t\t// over it — the ground plane above all, which routinely sorts after the\n\t\t// props standing on it. The renderer replays the queue once the opaque\n\t\t// meshes are down, and calls back in with `_shadowFlushing` set.\n\t\tif (\n\t\t\tmesh._blendedDraw === true &&\n\t\t\tthis.renderer._shadowFlushing !== true &&\n\t\t\tthis.renderer.queueGroundShadow !== undefined\n\t\t) {\n\t\t\tthis.renderer.queueGroundShadow(mesh, modelMatrix, tint);\n\t\t\treturn;\n\t\t}\n\n\t\t// anything the caller had queued must land first, or this draw would\n\t\t// reorder ahead of it\n\t\tthis.flush();\n\n\t\t// Strictly BEFORE the blended-draw toggle below: `updatePassState`\n\t\t// runs the one-shot depth clear, and `gl.clear(DEPTH_BUFFER_BIT)`\n\t\t// silently does nothing with `depthMask` off. A blended mesh drawn\n\t\t// first in a frame would otherwise swallow the clear and leave the\n\t\t// whole frame rendering against stale depth.\n\t\tthis.updatePassState();\n\n\t\tconst blended = mesh._blendedDraw === true;\n\t\tif (blended === true) {\n\t\t\tthis.beginBlendedDraw();\n\t\t}\n\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t}\n\t\tthis.setPlacementUniforms(modelMatrix, tint);\n\n\t\tconst geometry = this.retainedGeometryFor(mesh);\n\t\tgeometry.bind();\n\t\tif (slices === undefined) {\n\t\t\tgl.drawElements(this.mode, geometry.indexCount, geometry.indexType, 0);\n\t\t} else {\n\t\t\tconst indexBytes = geometry.indexType === gl.UNSIGNED_INT ? 4 : 2;\n\t\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\t\t// Bound one range at a time, immediately before its own draw.\n\t\t\t\t// Resolving every range's texture up front would be faster\n\t\t\t\t// but is wrong: exhausting the unit budget makes the texture\n\t\t\t\t// cache recycle units from unit 0, which silently invalidates\n\t\t\t\t// the units already handed out. Binding per range means a\n\t\t\t\t// later range recycling an earlier one's unit is harmless —\n\t\t\t\t// that range has already drawn.\n\t\t\t\t//\n\t\t\t\t// Safe between `geometry.bind()` and these draws because\n\t\t\t\t// nothing is accumulated on this path: the flush a texture\n\t\t\t\t// upload may trigger returns immediately at zero vertices,\n\t\t\t\t// touching neither the vertex array nor ARRAY_BUFFER.\n\t\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\t\tgl.drawElements(\n\t\t\t\t\tthis.mode,\n\t\t\t\t\tslices[i].count,\n\t\t\t\t\tgeometry.indexType,\n\t\t\t\t\tslices[i].start * indexBytes,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (blended === true) {\n\t\t\tthis.endBlendedDraw();\n\t\t}\n\n\t\t// hand the batcher's own vertex state back, so a subsequent\n\t\t// accumulated draw uploads and draws through its buffers, not these\n\t\tthis.vertexState.bind();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.uploadBuffer);\n\t}\n\n\t/**\n\t * Enter the blended mesh draw state: alpha blending on, depth test still\n\t * on, depth WRITES off (#1515).\n\t *\n\t * The blend function is set explicitly rather than through\n\t * `renderer.setBlendMode`. That method short-circuits when the mode is\n\t * unchanged, and `MeshBatcher.bind()` has already disabled `GL_BLEND`\n\t * behind that cache's back — so asking for the mode the cache already\n\t * claims would leave blending off and the draw opaque. Merely calling\n\t * `setBlendEnabled(true)` is no better: it inherits whatever function the\n\t * last 2D draw left, and under `\"additive\"` a dark shadow would *brighten*\n\t * the ground.\n\t * @ignore\n\t */\n\tbeginBlendedDraw() {\n\t\tconst gl = this.gl;\n\t\tgl.enable(gl.BLEND);\n\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\tgl.blendFunc(\n\t\t\tthis.renderer.premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA,\n\t\t\tgl.ONE_MINUS_SRC_ALPHA,\n\t\t);\n\t\t// depth TEST stays on — the shadow must still be occluded by geometry\n\t\t// in front of it — but writes are off, so overlapping shadows at one\n\t\t// ground height blend instead of fighting under LEQUAL\n\t\tgl.depthMask(false);\n\t}\n\n\t/**\n\t * Leave the blended draw state, restoring mesh mode exactly as\n\t * {@link MeshBatcher#bind} left it.\n\t *\n\t * The blend FUNCTION is put back too, not just the enable bit. This draw\n\t * overwrote it without touching `renderer.currentBlendMode`, so a later 2D\n\t * draw asking for the mode the cache already claims would short-circuit\n\t * and silently inherit this one's function. Re-issuing through\n\t * `setBlendMode` with the cache invalidated restores both, and leaves the\n\t * cache reading exactly what it read before.\n\t * @ignore\n\t */\n\tendBlendedDraw() {\n\t\tconst gl = this.gl;\n\t\tgl.depthMask(true);\n\t\tconst renderer = this.renderer;\n\t\tconst mode = renderer.currentBlendMode;\n\t\tif (typeof mode === \"string\") {\n\t\t\trenderer.currentBlendMode = null;\n\t\t\trenderer.setBlendMode(mode, renderer.currentPremultipliedAlpha);\n\t\t}\n\t\t// back to mesh mode, which is opaque\n\t\tgl.disable(gl.BLEND);\n\t}\n\n\t/**\n\t * Get (building or refreshing as needed) the GPU state one instanced mesh\n\t * draws from: its retained prototype geometry, its instance buffer, and\n\t * the vertex state binding the two together.\n\t *\n\t * The vertex state is rebuilt only when a buffer object it references is\n\t * replaced — a growing instance set reallocates, a merely-edited one does\n\t * not — because a vertex array holding a deleted buffer keeps it alive per\n\t * the GL spec and silently draws stale data.\n\t * @param {InstancedMesh} mesh - the mesh to draw\n\t * @returns {object} `{geometry, instances, vertexState}`\n\t * @ignore\n\t */\n\tinstancedStateFor(mesh) {\n\t\tconst gl = this.gl;\n\t\tconst geometry = this.retainedGeometryFor(mesh);\n\t\tlet state = this.instanced.get(mesh);\n\t\tif (state === undefined) {\n\t\t\tstate = {\n\t\t\t\tinstances: new WebGLInstanceBuffer(gl),\n\t\t\t\tvertexState: null,\n\t\t\t\tbuiltVersion: -1,\n\t\t\t\tbuiltGeometry: null,\n\t\t\t\tbuiltShader: null,\n\t\t\t\tuploadedRevision: -1,\n\t\t\t};\n\t\t\tthis.instanced.set(mesh, state);\n\t\t}\n\n\t\t// push whatever the CPU side changed before the layout is described,\n\t\t// so a first upload has allocated the buffer by the time the vertex\n\t\t// state points attribute records at it\n\t\t// what THIS buffer must upload to catch up — a shared \"clear the dirty\n\t\t// flag\" step would let the unlit batcher drain the span before the lit\n\t\t// one had seen it\n\t\tconst plan = mesh.instanceUpload(state.uploadedRevision);\n\t\tconst usedBytes = mesh.instanceCount * mesh.instanceLayout.stride;\n\t\tif (plan.full) {\n\t\t\tstate.instances.upload(mesh.instanceBuffer, 0, 0, Infinity);\n\t\t} else {\n\t\t\tstate.instances.upload(\n\t\t\t\tmesh.instanceBuffer,\n\t\t\t\tplan.first,\n\t\t\t\tplan.count,\n\t\t\t\tusedBytes,\n\t\t\t);\n\t\t}\n\t\tstate.uploadedRevision = plan.revision;\n\t\tmesh.clearInstanceDirty();\n\n\t\tconst stale =\n\t\t\tstate.vertexState === null ||\n\t\t\tstate.builtVersion !== mesh._instanceVersion ||\n\t\t\tstate.builtGeometry !== geometry.vertexBuffer ||\n\t\t\t// the layout is frozen against the CURRENT program's attribute\n\t\t\t// locations, so a different program needs a different vertex state\n\t\t\t// — otherwise the rows stay wired to the old locations and the\n\t\t\t// mesh silently reads zeros (a singular matrix collapses it)\n\t\t\tstate.builtShader !== this.currentShader;\n\t\tif (stale) {\n\t\t\t// geometry group (per vertex) + instance group (per instance) —\n\t\t\t// one vertex array describing both buffers\n\t\t\tconst descriptor = {\n\t\t\t\tbuffers: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbuffer: geometry.vertexBuffer,\n\t\t\t\t\t\tstride: this.stride,\n\t\t\t\t\t\tattributes: this.attributes,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbuffer: state.instances.buffer,\n\t\t\t\t\t\tstride: mesh.instanceLayout.stride,\n\t\t\t\t\t\tstepMode: \"instance\",\n\t\t\t\t\t\tattributes: this._instanceAttributeRecords(mesh.instanceLayout),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tindexBuffer: {\n\t\t\t\t\tbind: () => {\n\t\t\t\t\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.glIndexBuffer);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tresolveLocation: (name) => {\n\t\t\t\t\treturn this.currentShader.getAttribLocation(name);\n\t\t\t\t},\n\t\t\t};\n\t\t\tif (state.vertexState === null) {\n\t\t\t\tstate.vertexState = new WebGLVertexState(gl, descriptor);\n\t\t\t} else {\n\t\t\t\tstate.vertexState.build(descriptor);\n\t\t\t}\n\t\t\tstate.builtVersion = mesh._instanceVersion;\n\t\t\tstate.builtGeometry = geometry.vertexBuffer;\n\t\t\tstate.builtShader = this.currentShader;\n\t\t}\n\t\treturn { geometry, state };\n\t}\n\n\t/**\n\t * The instanced shader for a given record layout, compiled on first use.\n\t *\n\t * Variants are source permutations rather than runtime branches: the\n\t * optional slots are `#ifdef`-guarded, so a mesh that declares neither\n\t * costs no unused attributes and no dead code. `minify` deliberately\n\t * preserves newlines so the directives survive it.\n\t * @param {object} layout - the instance record layout\n\t * @returns {GLShader} the shader for that combination\n\t * @ignore\n\t */\n\tinstancedShaderFor(layout) {\n\t\tconst key = (layout.hasColor ? 1 : 0) | (layout.hasData ? 2 : 0);\n\t\tlet shader = this.instancedShaders.get(key);\n\t\tif (shader === undefined) {\n\t\t\tconst defines =\n\t\t\t\t(layout.hasColor ? \"#define INSTANCE_COLORS\\n\" : \"\") +\n\t\t\t\t(layout.hasData ? \"#define INSTANCE_DATA\\n\" : \"\");\n\t\t\tconst sources = this._instancedShaderSources();\n\t\t\t// only INSTANCE_DATA reaches the fragment stage (as the\n\t\t\t// per-instance emissive term); injecting the colour flag there too\n\t\t\t// would compile four distinct fragment texts where two suffice\n\t\t\tconst fragmentDefines = layout.hasData ? \"#define INSTANCE_DATA\\n\" : \"\";\n\t\t\tshader = new GLShader(this.gl, {\n\t\t\t\tvertex: injectDefines(sources.vertex, defines),\n\t\t\t\tfragment: injectDefines(sources.fragment, fragmentDefines),\n\t\t\t\tlabel: `melonJS instanced mesh ${key}`,\n\t\t\t});\n\t\t\tthis.instancedShaders.set(key, shader);\n\t\t}\n\t\treturn shader;\n\t}\n\n\t/**\n\t * The instanced shader sources for this batcher (unlit by default).\n\t * Subclasses override to supply the lit pair.\n\t * @ignore\n\t */\n\t_instancedShaderSources() {\n\t\treturn { vertex: meshInstancedVertex, fragment: meshFragment };\n\t}\n\n\t/**\n\t * Resolve an instance record layout into GL attribute records, in the\n\t * `{name, size, type, normalized, offset}` shape the vertex state wants.\n\t * Every slot is a `float32x4`.\n\t * @param {object} layout - the instance record layout\n\t * @returns {object[]} attribute records\n\t * @ignore\n\t */\n\t_instanceAttributeRecords(layout) {\n\t\tconst gl = this.gl;\n\t\treturn instanceAttributes(layout).map((attr) => {\n\t\t\treturn {\n\t\t\t\tname: attr.name,\n\t\t\t\tsize: 4,\n\t\t\t\ttype: gl.FLOAT,\n\t\t\t\tnormalized: false,\n\t\t\t\toffset: attr.offset,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Draw every visible instance of a mesh in one call.\n\t *\n\t * The geometry is bound once and the GPU walks the instance buffer,\n\t * advancing the per-instance attributes one record per copy. Placement of\n\t * the *group* still rides the ordinary uniforms, so moving the whole set\n\t * costs one matrix and re-uploads nothing.\n\t * @param {InstancedMesh} mesh - the mesh to draw\n\t * @param {Matrix3d} modelMatrix - where the group sits in the world\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @ignore\n\t */\n\tdrawInstancedMesh(mesh, modelMatrix, tint) {\n\t\tconst gl = this.gl;\n\t\tconst count = mesh.visibleInstanceCount;\n\t\tif (count === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// anything queued must land first, or this draw reorders ahead of it\n\t\tthis.flush();\n\n\t\t// the instanced variant must be current BEFORE the vertex state is\n\t\t// built: its attribute locations are what the layout is frozen against\n\t\t// A custom mesh shader is NOT hosted on the instanced path: its\n\t\t// attribute declarations decide the vertex-state layout, so a shader\n\t\t// that omits (or reorders) the instance slots wires them to the wrong\n\t\t// locations — or leaves them disabled, which makes the instance matrix\n\t\t// singular and collapses the mesh to a point. The WebGPU backend has\n\t\t// the same limitation, so both warn and fall back identically.\n\t\tif (\n\t\t\tthis.renderer.customShader != null &&\n\t\t\tthis._instancedShaderWarned !== true\n\t\t) {\n\t\t\tthis._instancedShaderWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: a custom shader cannot be hosted on an InstancedMesh — the mesh draws with the built-in instanced shading\",\n\t\t\t);\n\t\t}\n\t\tthis.useShader(this.instancedShaderFor(mesh.instanceLayout));\n\n\t\tthis.updatePassState();\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t}\n\t\tthis.setPlacementUniforms(modelMatrix, tint);\n\n\t\tconst { geometry, state } = this.instancedStateFor(mesh);\n\t\tstate.vertexState.bind();\n\t\tif (slices === undefined) {\n\t\t\tgl.drawElementsInstanced(\n\t\t\t\tthis.mode,\n\t\t\t\tgeometry.indexCount,\n\t\t\t\tgeometry.indexType,\n\t\t\t\t0,\n\t\t\t\tcount,\n\t\t\t);\n\t\t} else {\n\t\t\t// a multi-material prototype: every instance draws the same split\n\t\t\t// (#1573), so each range is one instanced draw over the whole set\n\t\t\t// — bound per range for the reason spelled out in drawRetainedMesh\n\t\t\tconst indexBytes = geometry.indexType === gl.UNSIGNED_INT ? 4 : 2;\n\t\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\t\tgl.drawElementsInstanced(\n\t\t\t\t\tthis.mode,\n\t\t\t\t\tslices[i].count,\n\t\t\t\t\tgeometry.indexType,\n\t\t\t\t\tslices[i].start * indexBytes,\n\t\t\t\t\tcount,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Hand the default shader and this batcher's own vertex state back.\n\t\t// Both matter: `bind()` only restores the default program when the\n\t\t// batcher is re-entered, and `setBatcher` returns early when it is\n\t\t// already current — so a following non-instanced mesh would otherwise\n\t\t// draw through the instanced program, reading per-instance attributes\n\t\t// that no longer have a buffer behind them.\n\t\tthis.useShader(this.defaultShader);\n\t\tthis.vertexState.bind();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.uploadBuffer);\n\t}\n\n\t/**\n\t * The instanced ground-shadow program (#1515), compiled on first use.\n\t *\n\t * ONE shader, not a variant per record layout: it reads only the three\n\t * transform rows, so `hasColor` / `hasData` make no difference to it —\n\t * which is also what stops a forest with per-instance colour and emissive\n\t * from getting coloured, glowing shadows.\n\t * @returns {GLShader} the shadow program\n\t * @ignore\n\t */\n\tinstancedShadowShader() {\n\t\tif (this.shadowShader === undefined) {\n\t\t\tthis.shadowShader = new GLShader(this.gl, {\n\t\t\t\tvertex: meshShadowInstancedVertex,\n\t\t\t\t// the UNLIT fragment stage, on both tiers, not\n\t\t\t\t// `_instancedShaderSources().fragment`. A blob needs nothing\n\t\t\t\t// from lighting — it samples the falloff and multiplies by the\n\t\t\t\t// tint — and borrowing the lit tier's pairs a GLSL ES 3.00\n\t\t\t\t// fragment shader with this ES 1.00 vertex shader, which does\n\t\t\t\t// not link (\"Fragment shader version does not match other\n\t\t\t\t// shader versions\") and takes the whole lit instanced tier\n\t\t\t\t// down with it. It would also read `vNormal` / `vWorldPos`,\n\t\t\t\t// which a flat blob never writes.\n\t\t\t\tfragment: meshFragment,\n\t\t\t\tlabel: \"melonJS instanced mesh shadow\",\n\t\t\t});\n\t\t}\n\t\treturn this.shadowShader;\n\t}\n\n\t/**\n\t * The vertex state pairing the SHARED shadow quad's geometry with this\n\t * mesh's own instance records.\n\t *\n\t * Kept in its own slot with its own staleness fields rather than reusing\n\t * the main pass's: that one keys on `builtGeometry` and `builtShader`, and\n\t * the shadow pass differs in both, so sharing a slot would make the two\n\t * passes rebuild each other's vertex array on every single draw.\n\t *\n\t * The instance buffer itself IS shared — the main pass already uploaded\n\t * it this frame, so this adds no upload.\n\t * @param {InstancedMesh} mesh - the mesh casting the shadows\n\t * @param {object} quadGeometry - the shared shadow quad's retained geometry\n\t * @returns {object} the per-mesh instanced state, with `shadowVertexState` built\n\t * @ignore\n\t */\n\tinstancedShadowStateFor(mesh, quadGeometry) {\n\t\t// Deliberately NOT `instancedStateFor`. That one keys its staleness on\n\t\t// the CURRENTLY BOUND shader, and by here we are bound to the standalone\n\t\t// shadow program — so it would judge the MAIN vertex state stale, tear\n\t\t// it down and rebuild it against a program that has none of the mesh's\n\t\t// attributes (warning about each), and the next frame's main draw would\n\t\t// rebuild it straight back: two VAO teardowns per instanced mesh per\n\t\t// frame, forever.\n\t\t//\n\t\t// The main pass has already run — the shadow queue drains after every\n\t\t// opaque mesh — so the state and its uploaded instance buffer exist.\n\t\t// The fallback covers a caller that somehow arrives first.\n\t\tconst state =\n\t\t\tthis.instanced.get(mesh) ?? this.instancedStateFor(mesh).state;\n\t\tconst stale =\n\t\t\tstate.shadowVertexState === undefined ||\n\t\t\tstate.shadowBuiltVersion !== mesh._instanceVersion ||\n\t\t\tstate.shadowBuiltGeometry !== quadGeometry.vertexBuffer ||\n\t\t\tstate.shadowBuiltShader !== this.currentShader;\n\t\tif (stale === true) {\n\t\t\tconst gl = this.gl;\n\t\t\tconst descriptor = {\n\t\t\t\tbuffers: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbuffer: quadGeometry.vertexBuffer,\n\t\t\t\t\t\tstride: this.stride,\n\t\t\t\t\t\t// TRIMMED to position / region / colour at the original\n\t\t\t\t\t\t// stride, so the offsets still land. The lit tier carries\n\t\t\t\t\t\t// `aNormal`, which the standalone shadow program does not\n\t\t\t\t\t\t// declare — leaving it in warns on every build. A flat\n\t\t\t\t\t\t// blob has no use for a normal.\n\t\t\t\t\t\tattributes: this.attributes.slice(0, 3),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t// only the three rows: the shadow reads no colour and no\n\t\t\t\t\t\t// custom data, so declaring them would make the vertex\n\t\t\t\t\t\t// state warn about attributes its program does not have\n\t\t\t\t\t\tbuffer: state.instances.buffer,\n\t\t\t\t\t\tstride: mesh.instanceLayout.stride,\n\t\t\t\t\t\tstepMode: \"instance\",\n\t\t\t\t\t\tattributes: this._instanceAttributeRecords(\n\t\t\t\t\t\t\tmesh.instanceLayout,\n\t\t\t\t\t\t).slice(0, 3),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tindexBuffer: {\n\t\t\t\t\tbind: () => {\n\t\t\t\t\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadGeometry.glIndexBuffer);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tresolveLocation: (name) => {\n\t\t\t\t\treturn this.currentShader.getAttribLocation(name);\n\t\t\t\t},\n\t\t\t};\n\t\t\tif (state.shadowVertexState === undefined) {\n\t\t\t\tstate.shadowVertexState = new WebGLVertexState(gl, descriptor);\n\t\t\t} else {\n\t\t\t\tstate.shadowVertexState.build(descriptor);\n\t\t\t}\n\t\t\tstate.shadowBuiltVersion = mesh._instanceVersion;\n\t\t\tstate.shadowBuiltGeometry = quadGeometry.vertexBuffer;\n\t\t\tstate.shadowBuiltShader = this.currentShader;\n\t\t}\n\t\treturn state;\n\t}\n\n\t/**\n\t * Draw one flat blob per instance, in a single call, from the same\n\t * instance buffer the mesh itself drew from (#1515).\n\t * @param {InstancedMesh} mesh - the mesh casting the shadows\n\t * @param {Matrix3d} shadowMatrix - the group matrix, flattened onto the ground\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @param {object} quad - the shared shadow quad mesh\n\t * @ignore\n\t */\n\tdrawInstancedShadow(mesh, shadowMatrix, tint, quad) {\n\t\tconst gl = this.gl;\n\t\tconst count = mesh.visibleInstanceCount;\n\t\tif (count === 0) {\n\t\t\treturn;\n\t\t}\n\t\tthis.flush();\n\t\tthis.useShader(this.instancedShadowShader());\n\t\tthis.updatePassState();\n\t\tthis.applyMeshMaterial(quad);\n\t\tthis.setPlacementUniforms(shadowMatrix, tint);\n\n\t\tconst quadGeometry = this.retainedGeometryFor(quad);\n\t\tconst state = this.instancedShadowStateFor(mesh, quadGeometry);\n\t\tthis.beginBlendedDraw();\n\t\tstate.shadowVertexState.bind();\n\t\tgl.drawElementsInstanced(\n\t\t\tthis.mode,\n\t\t\tquadGeometry.indexCount,\n\t\t\tquadGeometry.indexType,\n\t\t\t0,\n\t\t\tcount,\n\t\t);\n\t\tthis.endBlendedDraw();\n\n\t\tthis.useShader(this.defaultShader);\n\t\tthis.vertexState.bind();\n\t\tgl.bindBuffer(gl.ARRAY_BUFFER, this.uploadBuffer);\n\t}\n\n\t/**\n\t * Release the instance buffer and vertex state held for one mesh, if any.\n\t * @param {object} mesh - the mesh whose instance state should be freed\n\t * @ignore\n\t */\n\treleaseInstanced(mesh) {\n\t\tconst state = this.instanced?.get(mesh);\n\t\tif (state !== undefined) {\n\t\t\tstate.shadowVertexState?.destroy();\n\t\t\tstate.vertexState?.destroy();\n\t\t\tstate.instances.destroy();\n\t\t\tthis.instanced.delete(mesh);\n\t\t}\n\t}\n\n\t/**\n\t * Release every instance buffer this batcher holds.\n\t * @ignore\n\t */\n\treleaseAllInstanced() {\n\t\tthis.instanced?.forEach((state) => {\n\t\t\tstate.shadowVertexState?.destroy();\n\t\t\tstate.vertexState?.destroy();\n\t\t\tstate.instances.destroy();\n\t\t});\n\t\tthis.instanced?.clear();\n\t}\n\n\t/**\n\t * Release the retained geometry held for one mesh, if any.\n\t * @param {object} mesh - the mesh whose geometry should be freed\n\t * @ignore\n\t */\n\treleaseRetained(mesh) {\n\t\tthis.releaseInstanced(mesh);\n\t\tconst geometry = this.retained.get(mesh);\n\t\tif (geometry !== undefined) {\n\t\t\tgeometry.destroy();\n\t\t\tthis.retained.delete(mesh);\n\t\t}\n\t}\n\n\t/**\n\t * Release every retained geometry this batcher holds.\n\t * @ignore\n\t */\n\treleaseAllRetained() {\n\t\tthis.releaseAllInstanced();\n\t\tthis.retained.forEach((geometry) => {\n\t\t\tgeometry.destroy();\n\t\t});\n\t\tthis.retained.clear();\n\t}\n\n\t/**\n\t * In addition to the attribute-layout check, warn once when a shader\n\t * hosted on this batcher declares none of the placement uniforms.\n\t *\n\t * Mesh geometry is now uploaded in model space, so a shader that only\n\t * declares `uProjectionMatrix` — which is what every mesh shader needed\n\t * before, and what {@link ShaderEffect} still generates — will draw the\n\t * mesh at its untransformed origin with no camera applied. That renders\n\t * as \"the model vanished\" rather than as an error, so say so explicitly.\n\t * @param {GLShader} shader - the shader about to be hosted\n\t * @ignore\n\t */\n\tvalidateShaderLocations(shader) {\n\t\tconst firstTime =\n\t\t\tthis.validatedShaders === undefined || !this.validatedShaders.has(shader);\n\t\tsuper.validateShaderLocations(shader);\n\t\tif (firstTime === true && shader !== this.defaultShader) {\n\t\t\tconst uniforms = shader.uniforms;\n\t\t\tif (\n\t\t\t\tuniforms.uModelMatrix === undefined &&\n\t\t\t\tuniforms.uViewMatrix === undefined\n\t\t\t) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"melonJS: this mesh shader declares neither `uModelMatrix` nor `uViewMatrix` — \" +\n\t\t\t\t\t\t\"mesh geometry is supplied in model space, so the mesh will be drawn \" +\n\t\t\t\t\t\t\"unplaced and without the camera. Multiply the vertex position by \" +\n\t\t\t\t\t\t\"`uProjectionMatrix * uViewMatrix * uModelMatrix`, and tint with `uTint`.\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tsetPlacementUniforms(modelMatrix, tint) {\n\t\tconst shader = this.currentShader;\n\t\tconst uniforms = shader.uniforms;\n\n\t\tif (uniforms.uViewMatrix !== undefined) {\n\t\t\tshader.setUniform(\"uViewMatrix\", this.viewMatrix);\n\t\t}\n\t\tif (uniforms.uEyePosition !== undefined) {\n\t\t\t// The camera's world position, which the specular half-vector\n\t\t\t// needs and nothing else in this shader does. Derived from the\n\t\t\t// view matrix rather than plumbed from the camera so it stays\n\t\t\t// correct for any caller that sets a view directly: a view is\n\t\t\t// rigid, so its inverse translation is -Rᵀ·t — twelve\n\t\t\t// multiply-adds against a full 4×4 inversion.\n\t\t\tconst v = this.viewMatrix.val;\n\t\t\tconst ex = -(v[0] * v[12] + v[1] * v[13] + v[2] * v[14]);\n\t\t\tconst ey = -(v[4] * v[12] + v[5] * v[13] + v[6] * v[14]);\n\t\t\tconst ez = -(v[8] * v[12] + v[9] * v[13] + v[10] * v[14]);\n\t\t\t// the camera moves once a frame at most, while this runs once per\n\t\t\t// lit mesh — compare before paying for the GL call\n\t\t\tif (\n\t\t\t\tex !== this.currentEyeX ||\n\t\t\t\tey !== this.currentEyeY ||\n\t\t\t\tez !== this.currentEyeZ\n\t\t\t) {\n\t\t\t\t_EYE_POSITION[0] = ex;\n\t\t\t\t_EYE_POSITION[1] = ey;\n\t\t\t\t_EYE_POSITION[2] = ez;\n\t\t\t\tshader.setUniform(\"uEyePosition\", _EYE_POSITION);\n\t\t\t\tthis.currentEyeX = ex;\n\t\t\t\tthis.currentEyeY = ey;\n\t\t\t\tthis.currentEyeZ = ez;\n\t\t\t}\n\t\t}\n\t\tif (uniforms.uModelMatrix !== undefined) {\n\t\t\tshader.setUniform(\"uModelMatrix\", modelMatrix);\n\t\t}\n\t\tif (tint !== this.currentTintValue && uniforms.uTint !== undefined) {\n\t\t\t_TINT_RGBA[0] = ((tint >>> 16) & 0xff) / 255;\n\t\t\t_TINT_RGBA[1] = ((tint >>> 8) & 0xff) / 255;\n\t\t\t_TINT_RGBA[2] = (tint & 0xff) / 255;\n\t\t\t_TINT_RGBA[3] = ((tint >>> 24) & 0xff) / 255;\n\t\t\tshader.setUniform(\"uTint\", _TINT_RGBA);\n\t\t\tthis.currentTintValue = tint;\n\t\t}\n\t}\n\n\t/**\n\t * Bind a mesh's material state: its texture (honouring the mesh's own\n\t * `textureRepeat` wrap override) and the guarded `uAlphaCutoff` /\n\t * `uEmissive` uniforms.\n\t *\n\t * Shared by the accumulated and retained draw paths so material changes\n\t * take effect immediately either way, without touching geometry.\n\t * @param {object} mesh - the mesh whose material should be applied\n\t * @param {TextureAtlas} [texture] - bind this texture instead of the mesh's\n\t * own — how a multi-material model's per-material `map_Kd` reaches the\n\t * sampler, one draw range at a time (#1573). Everything else here is a\n\t * property of the mesh, not of the material group.\n\t * @returns {number} the texture unit the material landed on\n\t * @ignore\n\t */\n\tapplyMeshMaterial(mesh, texture = mesh.texture) {\n\t\t// upload and activate the texture. The mesh's own `textureRepeat`\n\t\t// (when set) is threaded through as a per-use wrap override — sampler\n\t\t// state per mesh, never a mutation of the shared per-image atlas\n\t\t// (#1503). The unit cache keys by `(source, repeat)`, so meshes with\n\t\t// different wraps over one image coexist on distinct GL textures.\n\t\tconst unit = this.uploadTexture(\n\t\t\ttexture,\n\t\t\tundefined,\n\t\t\tundefined,\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t\tmesh.textureRepeat,\n\t\t);\n\t\tthis.bindSamplerUnit(unit);\n\n\t\t// Mesh textures sample their mip chain: `createTexture2D` already\n\t\t// runs `generateMipmap` for every plain image upload, but the min\n\t\t// filter stays LINEAR so the chain went unused — upgrade to\n\t\t// trilinear once per GL texture. `textureFilter: \"nearest\"` opts\n\t\t// out (crisp pixel-art models keep hard minification), and a sprite\n\t\t// sharing the exact (source, wrap) unit sees the same\n\t\t// last-writer-wins caveat as the `textureFilter` setting.\n\t\tconst gl = this.gl;\n\t\tconst glFilter =\n\t\t\ttypeof texture.filter !== \"undefined\"\n\t\t\t\t? texture.filter\n\t\t\t\t: this.renderer._glTextureFilter();\n\t\t// the filter can be a GL enum (this backend's Mesh) or the string\n\t\t// form (an atlas first configured under a non-GL renderer)\n\t\tif (glFilter === gl.LINEAR || glFilter === \"linear\") {\n\t\t\tconst glTexture = this.boundTextures[unit];\n\t\t\tconst source =\n\t\t\t\ttypeof texture.getTexture === \"function\" ? texture.getTexture() : null;\n\t\t\t// TextureResource-backed sources own their upload and carry no\n\t\t\t// generated chain — a mipmap min filter over their single level\n\t\t\t// is mipmap-incomplete under ES3 (samples opaque black), so they\n\t\t\t// stay on plain LINEAR. Videos re-upload every frame, which\n\t\t\t// resets MIN_FILTER back to LINEAR — re-apply the upgrade per\n\t\t\t// draw for them instead of trusting the once-per-texture set.\n\t\t\tconst resourceOwned =\n\t\t\t\tsource !== null && typeof source.upload === \"function\";\n\t\t\tconst isVideo =\n\t\t\t\tsource !== null && typeof source.videoWidth !== \"undefined\";\n\t\t\tif (\n\t\t\t\ttypeof glTexture !== \"undefined\" &&\n\t\t\t\t!resourceOwned &&\n\t\t\t\t(isVideo || !this.trilinearTextures.has(glTexture))\n\t\t\t) {\n\t\t\t\t// uploads/bind tracking can skip real GL calls — force the\n\t\t\t\t// binding so the parameter lands on the right texture\n\t\t\t\tgl.activeTexture(gl.TEXTURE0 + unit);\n\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, glTexture);\n\t\t\t\tgl.texParameteri(\n\t\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t\tgl.TEXTURE_MIN_FILTER,\n\t\t\t\t\tgl.LINEAR_MIPMAP_LINEAR,\n\t\t\t\t);\n\t\t\t\tif (this.maxAnisotropy > 1) {\n\t\t\t\t\tgl.texParameterf(\n\t\t\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t\t\tthis.anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT,\n\t\t\t\t\t\tthis.maxAnisotropy,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.trilinearTextures.add(glTexture);\n\t\t\t}\n\t\t}\n\n\t\t// Per-texel opacity map (MTL `map_d`), on its own texture unit. Bound\n\t\t// before the cutout uniform below because it feeds it: the map scales\n\t\t// alpha, the cutout thresholds the result.\n\t\tif (this.currentShader.uniforms.uHasAlphaMap !== undefined) {\n\t\t\tconst alphaMap = mesh.alphaMap;\n\t\t\t// with no map the sampler points at the diffuse unit as filler and\n\t\t\t// the weight is 0, so nothing samples an unbound texture unit\n\t\t\tconst alphaUnit =\n\t\t\t\talphaMap !== undefined\n\t\t\t\t\t? this.uploadTexture(\n\t\t\t\t\t\t\talphaMap,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\tmesh.textureRepeat,\n\t\t\t\t\t\t)\n\t\t\t\t\t: unit;\n\t\t\t// change-guarded like every other per-mesh uniform here: an\n\t\t\t// unguarded pair costs two GL uniform calls on EVERY mesh draw,\n\t\t\t// and the overwhelmingly common case is \"no alpha map, again\"\n\t\t\tconst hasAlphaMap = alphaMap !== undefined ? 1 : 0;\n\t\t\tif (\n\t\t\t\talphaUnit !== this.currentAlphaMapUnit ||\n\t\t\t\thasAlphaMap !== this.currentHasAlphaMap\n\t\t\t) {\n\t\t\t\tthis.currentShader.setUniform(\"uAlphaMap\", alphaUnit);\n\t\t\t\tthis.currentShader.setUniform(\"uHasAlphaMap\", hasAlphaMap);\n\t\t\t\tthis.currentAlphaMapUnit = alphaUnit;\n\t\t\t\tthis.currentHasAlphaMap = hasAlphaMap;\n\t\t\t}\n\t\t\tif (alphaMap !== undefined) {\n\t\t\t\t// uploading the alpha map moved the active sampler — put the\n\t\t\t\t// diffuse binding back before the draw reads it. Skipped when\n\t\t\t\t// there is no map: nothing moved, and re-setting `uSampler`\n\t\t\t\t// would double the sampler traffic of every ordinary mesh.\n\t\t\t\tthis.bindSamplerUnit(unit);\n\t\t\t}\n\t\t}\n\n\t\t// Specular (MTL Ks + Ns). Guarded by the exponent, not the colour:\n\t\t// `Ns` of 0 is the format's \"no highlight\" however bright `Ks` is.\n\t\tif (this.currentShader.uniforms.uShininess !== undefined) {\n\t\t\tconst shininess = mesh.shininess || 0;\n\t\t\tif (shininess !== this.currentShininess) {\n\t\t\t\tthis.currentShader.setUniform(\"uShininess\", shininess);\n\t\t\t\tthis.currentShininess = shininess;\n\t\t\t}\n\t\t\tif (shininess > 0) {\n\t\t\t\tconst ks = mesh.specular ?? _ZERO_EMISSIVE;\n\t\t\t\tif (\n\t\t\t\t\tks[0] !== this.currentSpecularR ||\n\t\t\t\t\tks[1] !== this.currentSpecularG ||\n\t\t\t\t\tks[2] !== this.currentSpecularB\n\t\t\t\t) {\n\t\t\t\t\tthis.currentShader.setUniform(\"uSpecular\", ks);\n\t\t\t\t\tthis.currentSpecularR = ks[0];\n\t\t\t\t\tthis.currentSpecularG = ks[1];\n\t\t\t\t\tthis.currentSpecularB = ks[2];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// alpha cutout (glTF alphaMode MASK): discard fragments whose final alpha\n\t\t// is below the mesh's threshold (0 = disabled). The built-in mesh shaders\n\t\t// declare `uAlphaCutoff`; a custom shader without it is left untouched.\n\t\t// Each mesh is flushed on its own (see WebGLRenderer.drawMesh), so setting\n\t\t// the uniform before the vertices are pushed is enough — no extra flush.\n\t\tconst cutoff = mesh.alphaCutoff || 0;\n\t\tif (\n\t\t\tcutoff !== this.currentAlphaCutoff &&\n\t\t\tthis.currentShader.uniforms.uAlphaCutoff !== undefined\n\t\t) {\n\t\t\tthis.currentShader.setUniform(\"uAlphaCutoff\", cutoff);\n\t\t\tthis.currentAlphaCutoff = cutoff;\n\t\t}\n\n\t\t// emissive (glTF emissiveFactor / MTL Ke): a self-illumination color\n\t\t// added to the final fragment, unaffected by lighting. Same per-mesh,\n\t\t// flush-free, guarded-by-uniform-presence pattern as the cutoff above.\n\t\t// `undefined` (no emission) → the shared zero vector, a no-op add.\n\t\tconst em = mesh.emissive;\n\t\tconst er = em ? em[0] : 0;\n\t\tconst eg = em ? em[1] : 0;\n\t\tconst eb = em ? em[2] : 0;\n\t\tif (\n\t\t\t(er !== this.currentEmissiveR ||\n\t\t\t\teg !== this.currentEmissiveG ||\n\t\t\t\teb !== this.currentEmissiveB) &&\n\t\t\tthis.currentShader.uniforms.uEmissive !== undefined\n\t\t) {\n\t\t\tthis.currentShader.setUniform(\"uEmissive\", em ?? _ZERO_EMISSIVE);\n\t\t\tthis.currentEmissiveR = er;\n\t\t\tthis.currentEmissiveG = eg;\n\t\t\tthis.currentEmissiveB = eb;\n\t\t}\n\t\treturn unit;\n\t}\n\n\t/**\n\t * Point `uSampler` at a texture unit, if it moved and if the current\n\t * shader has a sampler at all — a custom mesh shader that never samples\n\t * (vertex colours only) declares none, and `setUniform` throws on an\n\t * unknown name.\n\t * @param {number} unit - the texture unit to sample from\n\t * @ignore\n\t */\n\tbindSamplerUnit(unit) {\n\t\tif (\n\t\t\tunit !== this.currentSamplerUnit &&\n\t\t\tthis.currentShader.uniforms?.uSampler !== undefined\n\t\t) {\n\t\t\tthis.currentShader.setUniform(\"uSampler\", unit);\n\t\t\tthis.currentSamplerUnit = unit;\n\t\t}\n\t}\n\n\taddMesh(mesh, tint) {\n\t\tthis.updatePassState();\n\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t\t// Placement uniforms. The view transform and the tint used to be\n\t\t\t// baked into every vertex on the CPU; they are uniforms now, so the\n\t\t\t// vertex data depends only on the geometry itself. This path (2D\n\t\t\t// camera / pre-projected vertices) supplies an identity model\n\t\t\t// matrix — the vertices already sit where they belong.\n\t\t\tthis.setPlacementUniforms(_IDENTITY_MATRIX, tint);\n\t\t\tthis.accumulateRange(mesh, 0, mesh.indices.length);\n\t\t\treturn;\n\t\t}\n\t\t// Multi-material with per-material textures (#1573): one accumulation\n\t\t// pass per range. `applyMeshMaterial` flushes on a texture change, so\n\t\t// the previous range's vertices land under the texture they were\n\t\t// accumulated for — and the uniforms are re-armed after that flush,\n\t\t// never before it.\n\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\tthis.setPlacementUniforms(_IDENTITY_MATRIX, tint);\n\t\t\tthis.accumulateRange(mesh, slices[i].start, slices[i].count);\n\t\t}\n\t}\n\n\t/**\n\t * Accumulate one index range of a mesh into the batch, chunking triangles\n\t * across flushes as the vertex / index buffers fill.\n\t * @param {object} mesh - a Mesh with vertices, uvs, indices, texture\n\t * @param {number} from - first index to accumulate\n\t * @param {number} length - how many indices to accumulate\n\t * @ignore\n\t */\n\taccumulateRange(mesh, from, length) {\n\t\tconst vertices = mesh.vertices;\n\t\tconst uvs = mesh.uvs;\n\t\tconst indices = mesh.indices;\n\t\tconst vertexColors = mesh.vertexColors;\n\t\tconst until = from + length;\n\n\t\tconst maxVerts = this.vertexData.maxVertex;\n\t\tconst maxIndices = this.indexBuffer.data.length;\n\n\t\t// size the versioned-remap scratch for this mesh's vertex range (one-time\n\t\t// growth; reused across frames thereafter)\n\t\tensureRemapCapacity(mesh.vertexCount);\n\n\t\t// process triangles in chunks that fit the buffer\n\t\tlet triIdx = from;\n\t\twhile (triIdx < until) {\n\t\t\t// figure out how many triangles fit in the current batch\n\t\t\tconst vertexData = this.vertexData;\n\t\t\tconst availVerts = maxVerts - vertexData.vertexCount;\n\t\t\tconst availIndices = maxIndices - this.indexBuffer.length;\n\t\t\t// each triangle needs at most 3 new vertices and 3 indices\n\t\t\tconst maxTris = Math.min(\n\t\t\t\tMath.floor(availVerts / 3),\n\t\t\t\tMath.floor(availIndices / 3),\n\t\t\t);\n\n\t\t\tif (maxTris === 0) {\n\t\t\t\tthis.flush();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst endIdx = Math.min(triIdx + maxTris * 3, until);\n\n\t\t\t// build a local vertex remap for this chunk (shared reused\n\t\t\t// scratch — see gpu/meshchunk.ts). Capture the base offset\n\t\t\t// before pushing any vertices.\n\t\t\tconst baseOffset = vertexData.vertexCount;\n\t\t\tconst chunkIndices = beginChunk();\n\t\t\tlet localCount = 0;\n\n\t\t\tfor (let j = triIdx; j < endIdx; j++) {\n\t\t\t\tconst origIdx = indices[j];\n\t\t\t\tlet localIdx = remapIndex(origIdx);\n\t\t\t\tif (localIdx === -1) {\n\t\t\t\t\tlocalIdx = localCount++;\n\t\t\t\t\tassignIndex(origIdx, localIdx);\n\n\t\t\t\t\tconst i3 = origIdx * 3;\n\t\t\t\t\tconst i2 = origIdx * 2;\n\t\t\t\t\tconst x = vertices[i3];\n\t\t\t\t\tconst y = vertices[i3 + 1];\n\t\t\t\t\tconst z = vertices[i3 + 2];\n\n\t\t\t\t\t// per-vertex color when the mesh provides one\n\t\t\t\t\t// (multi-material baked colors). The runtime tint is a\n\t\t\t\t\t// uniform now, so it is NOT folded in here — the shader\n\t\t\t\t\t// multiplies it, which keeps vertex data tint-independent.\n\t\t\t\t\tconst vertColor = vertexColors ? vertexColors[origIdx] : 0xffffffff;\n\t\t\t\t\t// delegate the actual write so subclasses can add per-vertex\n\t\t\t\t\t// data (e.g. LitMeshBatcher appends the world-space normal).\n\t\t\t\t\tthis._pushVertex(\n\t\t\t\t\t\tvertexData,\n\t\t\t\t\t\tx,\n\t\t\t\t\t\ty,\n\t\t\t\t\t\tz,\n\t\t\t\t\t\tuvs[i2],\n\t\t\t\t\t\tuvs[i2 + 1],\n\t\t\t\t\t\tvertColor,\n\t\t\t\t\t\tmesh,\n\t\t\t\t\t\ti3,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// absolute index = baseOffset + localIdx\n\t\t\t\tchunkIndices.push(baseOffset + localIdx);\n\t\t\t}\n\n\t\t\t// add raw indices (already absolute, bypass rebasing) — addRaw\n\t\t\t// copies the values, so reusing the shared chunk list is safe\n\t\t\tthis.indexBuffer.addRaw(chunkIndices);\n\t\t\ttriIdx = endIdx;\n\t\t}\n\t}\n}\n","import state from \"../../../state/state.ts\";\nimport { buildLitMeshVertexData } from \"../../gpu/meshvertex.ts\";\nimport UniformBlock from \"../buffer/uniformblock.js\";\nimport { MAX_LIGHTS } from \"../lighting/constants.ts\";\nimport { packMeshLights } from \"../lighting/pack3d.ts\";\nimport { BLOCK3D_FLOATS, writeLight3dBlock } from \"../lighting/std140.ts\";\nimport litFragment from \"./../shaders/mesh-lit.frag\";\nimport litVertex from \"./../shaders/mesh-lit.vert\";\nimport litInstancedVertex from \"./../shaders/mesh-lit-instanced.vert\";\nimport MeshBatcher from \"./mesh_batcher.js\";\n\n// resolve the lit fragment shader's light-array size from the single source of\n// truth (MAX_LIGHTS) so the GLSL array can't diverge from the uniform packer.\n// replaceAll: the token appears at every use site (the GLSL preprocessor may\n// have already expanded any macro, leaving multiple occurrences).\nconst litFragmentResolved = litFragment.replaceAll(\n\t\"__MAX_LIGHTS__\",\n\tString(MAX_LIGHTS),\n);\n\n// ambient used when a lit mesh is drawn with no active directional lights —\n// render it fullbright (white ambient) rather than dark, so a `lit` mesh in a\n// scene without lights still looks like the unlit path.\nconst _WHITE_AMBIENT = new Float32Array([1, 1, 1]);\n\n/**\n * A {@link MeshBatcher} variant that shades meshes with the active stage's\n * {@link Light3d} lights (half-Lambert diffuse from directional lights +\n * ambient). It extends the unlit batcher, adding a world-space `aNormal`\n * vertex attribute (12-float layout vs 9) and a lit shader.\n *\n * Meshes opt in via `mesh.lit` — so standalone, unlit meshes keep the lean\n * 9-float `MeshBatcher` and pay nothing for lighting. Both batchers share the\n * mesh-mode depth-clear state (a single clear per target) since this one\n * inherits {@link MeshBatcher#bind}.\n * @category Rendering\n */\nexport default class LitMeshBatcher extends MeshBatcher {\n\t/**\n\t * Allocate the `Light3dBlock` uniform buffer alongside the base setup.\n\t * Re-run on context restore (`WebGLRenderer.reset` calls `init` rather\n\t * than `reset` while the context is invalid), so the previous block —\n\t * whose GL name belonged to the dead context — is dropped first.\n\t * @ignore\n\t */\n\tinit(renderer) {\n\t\tsuper.init(renderer);\n\n\t\tthis.lightBlock?.destroy();\n\n\t\t/**\n\t\t * The `Light3dBlock` uniform buffer, refreshed before each draw in\n\t\t * {@link LitMeshBatcher#updatePassState}.\n\t\t * @type {UniformBlock}\n\t\t * @ignore\n\t\t */\n\t\t// Claim a binding point once and hold it: `init()` re-runs on context\n\t\t// restore, and claiming again each time would walk through the\n\t\t// device's budget over a long session.\n\t\tthis._bindingPoint ??= renderer.reserveUniformBindingPoint();\n\t\tthis.lightBlock = new UniformBlock(\n\t\t\trenderer.gl,\n\t\t\tBLOCK3D_FLOATS,\n\t\t\tthis._bindingPoint,\n\t\t);\n\t\tthis._lightBlockProgram = null;\n\t\t// see the note in LitQuadBatcher.init: an active-but-unbound uniform\n\t\t// block makes the program undrawable, so bind it at birth rather than\n\t\t// on first activation\n\t\tthis._bindLightBlock();\n\t}\n\n\t/**\n\t * Point the active program's `Light3dBlock` at our uniform buffer.\n\t *\n\t * A block binding is program state, so it has to be re-established\n\t * whenever the program changes: a context restore recompiles it, `init`\n\t * re-runs on reset, and `useShader` can swap one in without a batcher\n\t * transition. Comparing against the program we last bound covers all\n\t * three — a pointer compare in the steady state.\n\t * @ignore\n\t */\n\t_bindLightBlock() {\n\t\tconst shader = this.currentShader || this.defaultShader;\n\t\tconst program = shader?.program;\n\t\tif (!program || program === this._lightBlockProgram) {\n\t\t\treturn;\n\t\t}\n\t\tthis._lightBlockProgram = program;\n\t\tthis.lightBlock.bindTo(program, \"Light3dBlock\");\n\t}\n\n\t/** release the light block along with the rest. @ignore */\n\tdestroy() {\n\t\tthis.lightBlock?.destroy();\n\t\tthis.lightBlock = undefined;\n\t\tthis._lightBlockProgram = null;\n\t\tsuper.destroy();\n\t}\n\n\t/** add the world-space normal attribute on top of the base layout. @ignore */\n\t_attributeLayout(renderer) {\n\t\t// the parameter is kept for the subclass contract even though a\n\t\t// format-declared layout no longer needs a rendering context\n\t\tconst attributes = super._attributeLayout(renderer);\n\t\tattributes.push({\n\t\t\t// aNormal: world-space vertex normal, pushed WITHOUT the view\n\t\t\t// transform so lighting is evaluated in world space.\n\t\t\tname: \"aNormal\",\n\t\t\tformat: \"float32x3\",\n\t\t\toffset: 9 * Float32Array.BYTES_PER_ELEMENT,\n\t\t});\n\t\treturn attributes;\n\t}\n\n\t/** use the lit (half-Lambert) shader. @ignore */\n\t_shaderSources() {\n\t\treturn { vertex: litVertex, fragment: litFragmentResolved };\n\t}\n\n\t/**\n\t * The instanced lit pair. The fragment stage is the SAME shader the\n\t * non-instanced path uses — the per-instance emissive term inside it is\n\t * `#ifdef`-guarded, so the two programs differ only by the defines the\n\t * variant is compiled with, and the lighting loop exists in one place.\n\t * @ignore\n\t */\n\t_instancedShaderSources() {\n\t\treturn { vertex: litInstancedVertex, fragment: litFragmentResolved };\n\t}\n\n\t/** push the 12-float lit vertex, appending the mesh's world-space normal. @ignore */\n\t_pushVertex(vertexData, x, y, z, u, v, color, mesh, i3) {\n\t\tconst n = mesh.normals;\n\t\tvertexData.pushMeshLit(\n\t\t\tx,\n\t\t\ty,\n\t\t\tz,\n\t\t\tu,\n\t\t\tv,\n\t\t\tcolor,\n\t\t\tn ? n[i3] : 0,\n\t\t\tn ? n[i3 + 1] : 0,\n\t\t\tn ? n[i3 + 2] : 0,\n\t\t);\n\t}\n\n\t/**\n\t * Same as the base layout plus the model-space normal, so lighting can be\n\t * evaluated after the shader rotates it into world space.\n\t * @param {object} mesh - the mesh to read geometry from\n\t * @param {Float32Array} out - destination scratch\n\t * @returns {number} number of floats written\n\t * @ignore\n\t */\n\tbuildRetainedVertexData(mesh, out) {\n\t\t// the shared neutral builder (zero-normal guard included) — one\n\t\t// copy for both backends, see `gpu/meshvertex.ts`\n\t\treturn buildLitMeshVertexData(mesh, out, this.vertexSize);\n\t}\n\n\t/**\n\t * Refresh the light block on top of the inherited depth handling.\n\t *\n\t * Deliberately here and not in `bind()`: `setBatcher` returns early when\n\t * this batcher is already current, so a scene made only of lit meshes binds\n\t * once and never again. Uploading in `bind()` meant such a scene shaded\n\t * every frame with the lights it had on the first one — move a `Light3d`\n\t * and nothing happened. Most scenes escape it by accident, because a sprite\n\t * or an unlit mesh forces a switch (`night-city`'s emissive windows are\n\t * `lit: false`, which is what keeps it correct); a purely lit scene does\n\t * not.\n\t *\n\t * Costs nothing when the lights are unchanged: `UniformBlock.upload`\n\t * compares against the previous upload and skips the `bufferSubData`\n\t * outright, so a static light rig is one pack plus one array compare per\n\t * draw and zero GL calls.\n\t *\n\t * With NO lights at all (no directional and no ambient), a white ambient\n\t * keeps a `lit` mesh fullbright — matching the unlit path — while an\n\t * ambient-only scene still uses its real ambient.\n\t * @ignore\n\t */\n\tupdatePassState() {\n\t\tsuper.updatePassState();\n\n\t\tthis._bindLightBlock();\n\n\t\tconst stage = state.current();\n\t\tconst lit = packMeshLights(stage ? stage._activeLights3d : null);\n\t\t// use the packed ambient whenever any light contributed (directional\n\t\t// OR ambient); fall back to fullbright white only when the scene has no\n\t\t// 3D lights at all — otherwise an ambient-only setup would be ignored.\n\t\tconst a = lit.ambient;\n\t\tconst hasLight = lit.count > 0 || a[0] > 0 || a[1] > 0 || a[2] > 0;\n\t\tthis.lightBlock.upload(\n\t\t\twriteLight3dBlock(this.lightBlock.data, {\n\t\t\t\tcount: lit.count,\n\t\t\t\tposRange: lit.posRange,\n\t\t\t\tdirCone: lit.dirCone,\n\t\t\t\tcolorInner: lit.colorInner,\n\t\t\t\tambient: hasLight ? a : _WHITE_AMBIENT,\n\t\t\t}),\n\t\t);\n\t}\n}\n","import { MAX_LIGHTS } from \"../lighting/constants.ts\";\n\n/**\n * Build the GLSL `if/else` chain that picks among N samplers based on\n * an interpolated float texture-id. Dynamic indexing of a `sampler2D`\n * array is forbidden in GLSL ES 3.00 exactly as it was in 1.00, so this\n * stays an if-ladder using the usual `< i + 0.5` threshold pattern.\n * @ignore\n */\nfunction buildSamplerSelect(varName, samplerPrefix, count, target) {\n\tconst lines = [];\n\tfor (let i = 0; i < count; i++) {\n\t\tif (i === 0) {\n\t\t\tlines.push(\"    if (\" + varName + \" < 0.5) {\");\n\t\t} else {\n\t\t\tlines.push(\"    } else if (\" + varName + \" < \" + (i + 0.5) + \") {\");\n\t\t}\n\t\tlines.push(\n\t\t\t\"        \" + target + \" = texture(\" + samplerPrefix + i + \", vRegion);\",\n\t\t);\n\t}\n\tlines.push(\"    } else {\");\n\tlines.push(\n\t\t\"        \" + target + \" = texture(\" + samplerPrefix + \"0, vRegion);\",\n\t);\n\tlines.push(\"    }\");\n\treturn lines;\n}\n\n/**\n * Generate the fragment shader for `LitQuadBatcher` — the SpriteIlluminator\n * path that supports per-pixel normal-map lighting from up to `MAX_LIGHTS`\n * point lights.\n *\n * Sprites without a `normalMap` push `aNormalTextureId = -1` and take the\n * unlit fast path inside this same shader (sample color, multiply by\n * vertex color, return) so lit and unlit sprites can share the lit batch\n * when they coexist with active lights. The cost vs the truly-unlit\n * `QuadBatcher` shader is one interpolated read + one comparison per\n * fragment.\n *\n * The light's \"height above the sprite plane\" is encoded as a small\n * fraction of the light's radius (default `0.075`) so that lights\n * exactly at a fragment's screen position don't produce degenerate\n * flat shading.\n *\n * GLSL ES 3.00, because the light data arrives in a uniform block and\n * blocks do not exist in ES 1.00 (see #1552). The paired vertex shader\n * `quad-multi-lit.vert` is on the same version — both stages of a program\n * must agree.\n * @param {number} maxTextures - the number of color/normal texture units to support\n * @returns {string} GLSL fragment shader source\n * @ignore\n */\nexport function buildLitMultiTextureFragment(maxTextures) {\n\tconst count = Math.max(maxTextures, 1);\n\t// `precision` is inserted after the version directive by\n\t// utils/precision.js, so it is deliberately not written here\n\tconst lines = [\"#version 300 es\"];\n\n\t// ONE sampler set, addressed by two independent per-quad ids (#1585).\n\t// A separate `uNormalSampler0..n-1` set doubles the declaration count, and\n\t// needing 2n samplers is what forced this batcher to halve its unit budget\n\t// and permanently reserve the upper half for normal maps — so a lit scene\n\t// could never use more than half the units the device reported, however\n\t// many that was. A normal map is now just another texture in the shared\n\t// pool, and `vNormalTextureId` says which slot it landed in.\n\tfor (let i = 0; i < count; i++) {\n\t\tlines.push(\"uniform sampler2D uSampler\" + i + \";\");\n\t}\n\n\t// Light data arrives in a std140 uniform block rather than uniform\n\t// arrays. Arrays are charged against MAX_FRAGMENT_UNIFORM_VECTORS, a\n\t// small driver-reported budget that also has to cover the sampler set\n\t// above — which is what held the light cap at 8. The layout is written\n\t// by src/video/webgl/lighting/std140.ts and the two must agree byte for\n\t// byte: a mismatch links cleanly and shifts every light.\n\tlines.push(\"struct Light2dData {\");\n\t// (x, y, radius, intensity) and (r, g, b, height): the scalars ride in\n\t// the w components a vec3 would have padded out anyway\n\tlines.push(\"    vec4 posRadiusIntensity;\");\n\tlines.push(\"    vec4 colorHeight;\");\n\tlines.push(\"};\");\n\tlines.push(\"layout(std140) uniform Light2dBlock {\");\n\t// float rather than int so the staging buffer stays one Float32Array\n\tlines.push(\"    float uLightCount;\");\n\tlines.push(\"    vec3 uAmbient;\");\n\tlines.push(\"    Light2dData uLights[\" + MAX_LIGHTS + \"];\");\n\tlines.push(\"};\");\n\n\tlines.push(\"in vec4 vColor;\");\n\tlines.push(\"in vec2 vRegion;\");\n\tlines.push(\"in float vTextureId;\");\n\tlines.push(\"in float vNormalTextureId;\");\n\tlines.push(\"in vec2 vWorldPos;\");\n\tlines.push(\"out vec4 fragColor;\");\n\tlines.push(\"\");\n\tlines.push(\"void main(void) {\");\n\tlines.push(\"    vec4 color;\");\n\n\tlines.push(...buildSamplerSelect(\"vTextureId\", \"uSampler\", count, \"color\"));\n\n\t// unlit fast path: a quad without `normalMap` is in this batch only\n\t// because some other quad in the same batch needed lighting; bail\n\t// out before sampling the normal map and running the lit math.\n\tlines.push(\"    if (vNormalTextureId < -0.5) {\");\n\tlines.push(\"        fragColor = color * vColor;\");\n\tlines.push(\"        return;\");\n\tlines.push(\"    }\");\n\n\tlines.push(\"    vec4 normalSample;\");\n\tlines.push(\n\t\t...buildSamplerSelect(\n\t\t\t\"vNormalTextureId\",\n\t\t\t\"uSampler\",\n\t\t\tcount,\n\t\t\t\"normalSample\",\n\t\t),\n\t);\n\t// Decode 0..1 → -1..1. Normal maps emitted by SpriteIlluminator (and\n\t// most 3D authoring tools) use Y-up convention, but screen-space\n\t// here is Y-down — flip the Y component so `dot(normal, lightDir)`\n\t// is computed in a single coherent coord system.\n\tlines.push(\n\t\t\"    vec3 normal = normalize(normalSample.rgb * 2.0 - vec3(1.0));\",\n\t);\n\tlines.push(\"    normal.y = -normal.y;\");\n\n\tlines.push(\"    vec3 lighting = uAmbient;\");\n\t// ES 3.00 permits a non-constant bound, so this runs exactly as many\n\t// iterations as there are live lights — unused capacity costs nothing\n\tlines.push(\"    int count = min(int(uLightCount), \" + MAX_LIGHTS + \");\");\n\tlines.push(\"    for (int i = 0; i < count; i++) {\");\n\tlines.push(\"        vec4 lp = uLights[i].posRadiusIntensity;\");\n\tlines.push(\"        vec2 toLight = lp.xy - vWorldPos;\");\n\tlines.push(\"        float dist = length(toLight);\");\n\t// quadratic attenuation over [0, radius]: gives a wider plateau near\n\t// the cursor and a softer feathered edge than the linear formula\n\tlines.push(\"        float linear = max(0.0, 1.0 - dist / max(lp.z, 1.0));\");\n\tlines.push(\"        float att = linear * linear;\");\n\tlines.push(\n\t\t\"        vec3 lightDir = normalize(vec3(toLight, uLights[i].colorHeight.w));\",\n\t);\n\tlines.push(\"        float NdotL = max(0.0, dot(normal, lightDir));\");\n\tlines.push(\n\t\t\"        lighting += uLights[i].colorHeight.rgb * (lp.w * att * NdotL);\",\n\t);\n\tlines.push(\"    }\");\n\n\tlines.push(\"    fragColor = vec4(color.rgb * lighting, color.a) * vColor;\");\n\tlines.push(\"}\");\n\n\treturn lines.join(\"\\n\");\n}\n","#version 300 es\n// Lit-aware vertex shader used by `LitQuadBatcher` (the SpriteIlluminator\n// path). Carries a paired `aNormalTextureId` per vertex so the fragment\n// shader knows which `uNormalSampler<n>` to read, and `vWorldPos` so the\n// lit math can compute per-fragment `lightPos - pos` deltas.\n// `aVertex.z` carries per-sprite depth (renderable.depth) — a no-op under\n// the default ortho projection, used by perspective (Camera3d).\nin vec3 aVertex;\nin vec2 aRegion;\nin vec4 aColor;\nin float aTextureId;\nin float aNormalTextureId;\n\nuniform mat4 uProjectionMatrix;\n\nout vec2 vRegion;\nout vec4 vColor;\nout float vTextureId;\nout float vNormalTextureId;\n// Pre-projection vertex position (in the renderer's pre-projection\n// space — typically camera-local for default cameras with the world\n// container's translate applied). Used by the lit fragment path to\n// compute `lightPos - fragmentPos` for each Light2d. 2D — light math is\n// in the screen plane, depth doesn't participate.\nout vec2 vWorldPos;\n\nvoid main(void) {\n    gl_Position = uProjectionMatrix * vec4(aVertex, 1.0);\n    vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n    vRegion = aRegion;\n    vTextureId = aTextureId;\n    vNormalTextureId = aNormalTextureId;\n    vWorldPos = aVertex.xy;\n}\n","/**\n * Backend-neutral texture residency.\n *\n * There are two separate questions about a texture, and conflating them is\n * what makes a GPU renderer re-upload work it already has:\n *\n * - **which slot is it in, for this draw?** — transient, per-batch, bounded by\n *   the shader's sampler count. That is {@link TextureSlotTable}.\n * - **does it exist on the GPU, and is its content current?** — persistent,\n *   per-source, bounded by disposal. That is this class.\n *\n * The WebGL backend used to answer both with one field (`boundTextures[unit]`),\n * so dropping a slot assignment destroyed the texture handle and the next draw\n * rebuilt it from scratch — a full re-upload and mip regeneration per quad,\n * once past the batching limit.\n *\n * This class owns the source → record index, the reuse-or-upload decision, and\n * the lifetime bookkeeping. It touches no GL or WebGPU API: each backend\n * supplies `onCreate` / `onUpload` / `onDestroy`, exactly as the slot table\n * takes its binding through callbacks. That keeps the decision logic identical\n * on both backends and testable without a device.\n * @ignore\n */\nexport class TextureStore {\n\t/**\n\t * @param {object} [options] - store configuration\n\t * @param {Function} [options.onCreate] - `(source, options) => handle`, allocate\n\t * the backing GPU texture for a source seen for the first time\n\t * @param {Function} [options.onUpload] - `(handle, source, record, options) => handle`,\n\t * push the source's current content. May return a *replacement* handle when\n\t * the shape changed and the old one cannot be respecified (immutable\n\t * storage); returning nothing keeps the existing handle.\n\t * @param {Function} [options.onDestroy] - `(handle, source)`, release the\n\t * GPU texture. Called exactly once per handle.\n\t * @ignore\n\t */\n\tconstructor({ onCreate, onUpload, onDestroy } = {}) {\n\t\t/** @type {Map<object, {handle: *, version: number, generation: number}>} */\n\t\tthis.records = new Map();\n\t\t// The three hooks are prototype METHODS, so a backend subclass overrides\n\t\t// them; passing them to the constructor installs per-instance overrides\n\t\t// instead, which is how a test drives the policy with no GPU at all.\n\t\tif (onCreate !== undefined) {\n\t\t\tthis.onCreate = onCreate;\n\t\t}\n\t\tif (onUpload !== undefined) {\n\t\t\tthis.onUpload = onUpload;\n\t\t}\n\t\tif (onDestroy !== undefined) {\n\t\t\tthis.onDestroy = onDestroy;\n\t\t}\n\t\t// Bumped whenever the underlying context or device dies. A record from\n\t\t// an older generation is never handed back: a handle minted under a\n\t\t// dead context is not merely stale, binding it is undefined behaviour,\n\t\t// and the failure is silent. Comparing generations turns that into an\n\t\t// ordinary miss followed by a re-upload.\n\t\tthis.generation = 0;\n\t}\n\n\t/**\n\t * Allocate the backing GPU texture for a source seen for the first time.\n\t * @param {object} source - the image/canvas/resource\n\t * @param {object} options - the resolve options\n\t * @returns {*} the new handle\n\t * @ignore\n\t */\n\tonCreate(source, options) {\n\t\tvoid source;\n\t\tvoid options;\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Push the source's current content. May return a REPLACEMENT handle when\n\t * the shape changed and the old one cannot be respecified.\n\t * @param {*} handle - the existing handle\n\t * @param {object} source - the image/canvas/resource\n\t * @param {object} record - the resident record\n\t * @param {object} options - the resolve options\n\t * @returns {*} a replacement handle, or nothing to keep the existing one\n\t * @ignore\n\t */\n\tonUpload(handle, source, record, options) {\n\t\tvoid handle;\n\t\tvoid source;\n\t\tvoid record;\n\t\tvoid options;\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Release a GPU texture. Called exactly once per handle.\n\t * @param {*} handle - the handle to release\n\t * @param {object} source - the source it belonged to\n\t * @ignore\n\t */\n\tonDestroy(handle, source) {\n\t\tvoid handle;\n\t\tvoid source;\n\t}\n\n\t/**\n\t * how many sources are currently resident\n\t * @returns {number} the record count\n\t * @ignore\n\t */\n\tget size() {\n\t\treturn this.records.size;\n\t}\n\n\t/**\n\t * The live record for a source, uploading only when it is genuinely needed:\n\t * the source has never been seen, its content changed, its handle predates\n\t * the current context, or the caller forced it.\n\t *\n\t * A texture that merely moved to a different slot is NOT a reason to upload\n\t * — that is the whole point of separating residency from slot assignment.\n\t * @param {object} source - the image/canvas/resource backing the texture\n\t * @param {object} [options] - resolution options\n\t * @param {number} [options.version=0] - the source's content revision; a\n\t * change from the recorded value forces a re-upload\n\t * @param {boolean} [options.force=false] - re-upload regardless\n\t * @returns {{handle: *, version: number, generation: number, uploaded: boolean}}\n\t * the record, with `uploaded` reporting whether this call did GPU work\n\t * @ignore\n\t */\n\tgetResidentRecord(source, options = {}) {\n\t\tconst { version = 0, force = false } = options;\n\t\tlet record = this.records.get(source);\n\n\t\tif (record === undefined) {\n\t\t\trecord = {\n\t\t\t\thandle: this.onCreate(source, options),\n\t\t\t\t// deliberately unmatchable, so a freshly created record always\n\t\t\t\t// takes the upload path once — a record that exists but was\n\t\t\t\t// never filled is the failure this rules out\n\t\t\t\tversion: -1,\n\t\t\t\tgeneration: this.generation,\n\t\t\t};\n\t\t\tthis.records.set(source, record);\n\t\t}\n\n\t\tif (force || record.version !== version) {\n\t\t\tconst replacement = this.onUpload?.(\n\t\t\t\trecord.handle,\n\t\t\t\tsource,\n\t\t\t\trecord,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tif (replacement !== undefined && replacement !== null) {\n\t\t\t\t// the backend could not respecify in place and swapped handles\n\t\t\t\trecord.handle = replacement;\n\t\t\t}\n\t\t\trecord.version = version;\n\t\t\trecord.uploaded = true;\n\t\t} else {\n\t\t\trecord.uploaded = false;\n\t\t}\n\t\treturn record;\n\t}\n\n\t/**\n\t * The record for a source without resolving one, for callers that must not\n\t * trigger GPU work (bookkeeping, debug, invalidation checks).\n\t * @param {object} source - the source to look up\n\t * @returns {object|undefined} the record, or `undefined`\n\t * @ignore\n\t */\n\tpeek(source) {\n\t\treturn this.records.get(source);\n\t}\n\n\t/**\n\t * Release the GPU texture for one source. Driven by the source going away\n\t * — never by a slot being reassigned, which is exactly the coupling this\n\t * class exists to break.\n\t * @param {object} source - the disposed source\n\t * @returns {boolean} whether a record was held\n\t * @ignore\n\t */\n\tdestroyTexture(source) {\n\t\tconst record = this.records.get(source);\n\t\tif (record === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\tthis.records.delete(source);\n\t\tif (record.generation === this.generation) {\n\t\t\tthis.onDestroy(record.handle, source);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Drop every record and bump the generation. Called on context or device\n\t * loss.\n\t *\n\t * This CLEARS the store in place — callers must never respond to a restore\n\t * by constructing a replacement store. Doing so orphans every handle the\n\t * old one was tracking, so the *second* loss leaks everything it had\n\t * accumulated since the first. Both WebGL batchers re-run `init()` on\n\t * restore, which is precisely how that mistake gets made here.\n\t * @param {boolean} [destroy=false] - whether to release the handles first.\n\t * Leave `false` for a lost context (the GPU objects are already gone);\n\t * pass `true` for an orderly teardown.\n\t * @ignore\n\t */\n\treleaseAll(destroy = false) {\n\t\tif (destroy === true) {\n\t\t\tfor (const [source, record] of this.records) {\n\t\t\t\tif (record.generation === this.generation) {\n\t\t\t\t\tthis.onDestroy(record.handle, source);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.records.clear();\n\t\tthis.generation++;\n\t}\n}\n","import { TextureStore } from \"./../../gpu/texturestore.js\";\n\n/**\n * The WebGL realization of {@link TextureStore}.\n *\n * The shared base owns the policy — source → record, the reuse-or-upload\n * decision, generation and lifetime bookkeeping. This subclass owns the GL\n * calls, and nothing else. Mirrors the `Batcher` / `WebGLBatcher` /\n * `WebGPUBatcher` arrangement: one neutral base, one realization per backend.\n *\n * Two instances exist per renderer: the colour store, which every batcher\n * shares through `WebGLRenderer.textureStore`, and the lit batcher's\n * normal-map store — normal maps live outside the colour `TextureCache`, so\n * their handles are that batcher's to own, but the policy is identical.\n * @augments TextureStore\n * @ignore\n */\nexport class WebGLTextureStore extends TextureStore {\n\t/**\n\t * @param {WebGL2RenderingContext} gl - the owning context\n\t * @ignore\n\t */\n\tconstructor(gl) {\n\t\tsuper();\n\t\tthis.gl = gl;\n\t}\n\n\t/**\n\t * @returns {WebGLTexture} a fresh texture object\n\t * @ignore\n\t */\n\tonCreate() {\n\t\treturn this.gl.createTexture();\n\t}\n\n\t/**\n\t * Push the source's pixels.\n\t *\n\t * Delegated to a per-call closure rather than implemented here, because a\n\t * GL upload needs the *batcher's* `createTexture2D`: it binds the target\n\t * unit, tracks immutable-storage shape, and swaps the texture object when\n\t * the shape changes. None of that is residency, and moving it into the\n\t * store would drag the batcher's unit bookkeeping along with it.\n\t * @param {WebGLTexture} handle - the existing texture object\n\t * @param {object} source - the image/canvas being uploaded\n\t * @param {object} record - the resident record\n\t * @param {object} options - carries `upload(handle)`\n\t * @returns {WebGLTexture} the handle actually used, which may be a\n\t * replacement when immutable storage had to be respecified\n\t * @ignore\n\t */\n\tonUpload(handle, source, record, options) {\n\t\treturn options.upload(handle);\n\t}\n\n\t/**\n\t * @param {WebGLTexture} handle - the texture object to release\n\t * @ignore\n\t */\n\tonDestroy(handle) {\n\t\tthis.gl.deleteTexture(handle);\n\t}\n}\n","import { off, on, TEXTURE2D_DESTROYED } from \"../../../system/event.ts\";\nimport { transformQuadCorners } from \"../../gpu/quadcorners.ts\";\nimport UniformBlock from \"../buffer/uniformblock.js\";\nimport { MAX_LIGHTS } from \"../lighting/constants.ts\";\nimport {\n\tBLOCK_FLOATS,\n\tHEADER_FLOATS,\n\twriteLight2dBlock,\n} from \"../lighting/std140.ts\";\nimport { buildLitMultiTextureFragment } from \"./../shaders/multitexture-lit.js\";\nimport quadMultiLitVertex from \"./../shaders/quad-multi-lit.vert\";\nimport { WebGLTextureStore } from \"../texture/store.js\";\nimport QuadBatcher from \"./quad_batcher.js\";\n\n/**\n * additional import for TypeScript\n * @import {TextureAtlas} from \"./../../texture/atlas.js\";\n */\n\n/**\n * Lit-aware variant of `QuadBatcher` for the SpriteIlluminator workflow.\n *\n * Adds a 5th vertex attribute (`aNormalTextureId`) so each quad knows\n * which slot its normal map occupies, and owns the per-frame\n * `Light2dBlock` uniform buffer that the lit fragment shader iterates.\n *\n * Colors and normal maps share ONE slot pool the same size as `QuadBatcher`'s\n * (#1585) — a normal map is allocated a unit like any other texture, and\n * `aNormalTextureId` records which one. The `WebGLRenderer` only\n * dispatches sprites here when the scene actually needs lighting (active\n * `Light2d` AND the sprite has a `normalMap`); unlit sprites stay on\n * `QuadBatcher` and pay nothing.\n * @category Rendering\n */\nexport default class LitQuadBatcher extends QuadBatcher {\n\t/**\n\t * @ignore\n\t */\n\tinit(renderer) {\n\t\t// One shared pool, same size as the unlit batcher's (#1585). This used\n\t\t// to be HALVED, because the fragment shader declared a second\n\t\t// `uNormalSampler0..n-1` set and 2n samplers had to fit the device — and\n\t\t// the upper half was then reserved permanently, so a lit scene could\n\t\t// never allocate more than half the units the device reported. The\n\t\t// shader now addresses ONE sampler set with two per-quad ids, so a\n\t\t// normal map is just another texture competing for the same slots. The\n\t\t// split became dynamic: sprites sharing a normal map cost one slot\n\t\t// between them, rather than every scene paying half its budget upfront.\n\t\tconst pool = renderer.maxTextures;\n\t\tthis.maxBatchTextures = pool;\n\n\t\t// Skip QuadBatcher.init (its attribute layout / shader differ) and\n\t\t// invoke MaterialBatcher.init directly with the lit configuration.\n\t\tObject.getPrototypeOf(QuadBatcher.prototype).init.call(this, renderer, {\n\t\t\tattributes: [\n\t\t\t\t{\n\t\t\t\t\t// vec3: (x, y, z). z carries `renderable.depth` for\n\t\t\t\t\t// perspective projection (Camera3d). Stride = 32 bytes.\n\t\t\t\t\tname: \"aVertex\",\n\t\t\t\t\tformat: \"float32x3\",\n\t\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aRegion\",\n\t\t\t\t\tformat: \"float32x2\",\n\t\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aColor\",\n\t\t\t\t\tformat: \"unorm8x4\",\n\t\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aTextureId\",\n\t\t\t\t\tformat: \"float32\",\n\t\t\t\t\toffset: 6 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"aNormalTextureId\",\n\t\t\t\t\tformat: \"float32\",\n\t\t\t\t\toffset: 7 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t},\n\t\t\t],\n\t\t\tshader: {\n\t\t\t\tvertex: quadMultiLitVertex,\n\t\t\t\tfragment: buildLitMultiTextureFragment(pool),\n\t\t\t},\n\t\t});\n\n\t\t// Reuse the parent's setup helpers — they're agnostic to the\n\t\t// shader/attribute layout, just iterate `this.maxBatchTextures`.\n\t\tthis.bindColorSamplers();\n\t\tthis.createIndexBuffer();\n\n\t\tthis.useMultiTexture = true;\n\n\t\t/**\n\t\t * normal-map texture per color slot — keyed by the same unit index as\n\t\t * `boundTextures`. Used by `addQuad` to detect when a normal-map slot\n\t\t * needs (re-)uploading, mirroring the color-texture cache.\n\t\t * @type {Array<HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|null>}\n\t\t * @ignore\n\t\t */\n\t\tthis.boundNormalMaps = new Array(pool).fill(null);\n\n\t\t/**\n\t\t * Per-slot content `version` of the normal map currently bound there. An\n\t\t * animated source keeps a stable canvas reference across re-bakes but\n\t\t * bumps its `version`, so `addQuad` compares this (not just the reference)\n\t\t * to know when to re-bind/re-upload. `-1` = nothing bound.\n\t\t * @type {number[]}\n\t\t * @ignore\n\t\t */\n\t\tthis.boundNormalVersions = new Array(pool).fill(-1);\n\n\t\t/**\n\t\t * Residency for normal-map sources — the same shared policy the colour\n\t\t * path uses, just a separate instance: normal maps live outside the\n\t\t * colour `TextureCache`, so their handles are this batcher's to own.\n\t\t *\n\t\t * This used to be a hand-rolled `Map<source, {tex, version}>`. It was\n\t\t * the correct DESIGN before the colour path had one (which is why the\n\t\t * normal path never suffered the re-upload storm), but keeping a third\n\t\t * copy of the logic is how the two drifted in the first place.\n\t\t *\n\t\t * Re-created rather than cleared here on purpose: `init()` re-runs on\n\t\t * context restore, where the old handles died with the context — and\n\t\t * `destroy()` releases the previous instance first, so nothing leaks.\n\t\t * @type {TextureStore}\n\t\t * @ignore\n\t\t */\n\t\tthis.normalStore?.releaseAll();\n\t\tthis.normalStore = new WebGLTextureStore(this.gl);\n\n\t\t/**\n\t\t * Which slot in the shared pool each normal-map source currently holds.\n\t\t * Normal maps are not in the color `TextureCache` (they are raw sources\n\t\t * with their own GL textures), so their unit assignment is tracked here\n\t\t * while the unit itself comes from the same allocator the colors use.\n\t\t * @type {Map<HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap, number>}\n\t\t * @ignore\n\t\t */\n\t\tthis.normalUnits = new Map();\n\t\tthis._cacheEpoch = 0;\n\n\t\tthis._lightCount = 0;\n\t\tthis._maxLights = MAX_LIGHTS;\n\n\t\t// `init` is re-run on context restore (WebGLRenderer.reset), against a\n\t\t// new context and a freshly compiled program — so drop the old block\n\t\t// rather than leaking it, and let `bind` re-establish the binding.\n\t\tthis.lightBlock?.destroy();\n\n\t\t/**\n\t\t * The `Light2dBlock` uniform buffer. Filled by\n\t\t * {@link LitQuadBatcher#setLightUniforms}, which no longer needs the\n\t\t * program bound — a buffer upload is independent of `useProgram`.\n\t\t * @type {UniformBlock}\n\t\t * @ignore\n\t\t */\n\t\t// Claim a binding point once and hold it: `init()` re-runs on context\n\t\t// restore, and claiming again each time would walk through the\n\t\t// device's budget over a long session.\n\t\tthis._bindingPoint ??= renderer.reserveUniformBindingPoint();\n\t\tthis.lightBlock = new UniformBlock(\n\t\t\trenderer.gl,\n\t\t\tBLOCK_FLOATS,\n\t\t\tthis._bindingPoint,\n\t\t);\n\t\tthis._lightBlockProgram = null;\n\t\t// Bind immediately rather than waiting for the first activation. An\n\t\t// active-but-unbound uniform block is INVALID_OPERATION at draw time,\n\t\t// not a read of zeroes — so a renderer that never draws lit content\n\t\t// would otherwise carry a program that cannot legally draw at all.\n\t\tthis._bindLightBlock();\n\n\t\t// release the cached GL texture when a Texture2d normal-map source\n\t\t// (e.g. a per-level NoiseTexture2d) is destroyed — `normalMapTextures`\n\t\t// keys sources strongly and is otherwise only emptied on full reset\n\t\tif (!this._onTexture2dDestroyed) {\n\t\t\tthis._onTexture2dDestroyed = (source) => {\n\t\t\t\tthis.evictNormalMap(source);\n\t\t\t};\n\t\t\ton(TEXTURE2D_DESTROYED, this._onTexture2dDestroyed);\n\t\t}\n\t}\n\n\t/**\n\t * Unsubscribe the `TEXTURE2D_DESTROYED` listener; delegates to\n\t * `MaterialBatcher.destroy()` for the texture-cache-reset one.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tif (this._onTexture2dDestroyed) {\n\t\t\toff(TEXTURE2D_DESTROYED, this._onTexture2dDestroyed);\n\t\t\tthis._onTexture2dDestroyed = null;\n\t\t}\n\t\tthis.lightBlock?.destroy();\n\t\tthis.lightBlock = undefined;\n\t\tthis._lightBlockProgram = null;\n\t\tsuper.destroy();\n\t}\n\n\t/**\n\t * No unit reservation any more (#1585). This used to claim\n\t * `[maxBatchTextures, 2*maxBatchTextures)` permanently on first bind,\n\t * because the lit shader's normal samplers were bound to those fixed\n\t * units — which took half the pool away from every allocator for the rest\n\t * of the session, and collided with the top units `ShaderEffect` and\n\t * `toFrameTexture` claim. Normal maps now go through the shared allocator\n\t * like everything else, so there is nothing to hold back.\n\t * @ignore\n\t */\n\tbind() {\n\t\tsuper.bind();\n\t\tthis._bindLightBlock();\n\t}\n\n\t/**\n\t * Point the active program's `Light2dBlock` at our uniform buffer.\n\t *\n\t * A block binding is *program* state, and the program can change under us\n\t * in three ways: a context restore recompiles it, `setBatcher` can host a\n\t * {@link ShaderEffect} on this batcher, and `init` re-runs on reset. Rather\n\t * than hook each of those, re-bind whenever the program is not the one we\n\t * last bound — a pointer compare per `bind`, and self-healing.\n\t *\n\t * A hosted `ShaderEffect` is a plain unlit ES 1.00 program with no light\n\t * block, so `bindTo` reports failure and we simply record it, leaving the\n\t * next real lit program to bind normally.\n\t * @ignore\n\t */\n\t_bindLightBlock() {\n\t\tconst shader = this.currentShader || this.defaultShader;\n\t\tconst program = shader?.program;\n\t\tif (!program || program === this._lightBlockProgram) {\n\t\t\treturn;\n\t\t}\n\t\tthis._lightBlockProgram = program;\n\t\tthis.lightBlock.bindTo(program, \"Light2dBlock\");\n\t}\n\n\t/**\n\t * Release the cached GL texture uploaded for the given normal-map source\n\t * and clear any slot bookkeeping referencing it. Safe to call for images\n\t * that were never bound.\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} image - normal-map source\n\t */\n\tevictNormalMap(image) {\n\t\tif (this.normalStore.peek(image) !== undefined) {\n\t\t\tthis.normalStore.destroyTexture(image);\n\t\t\tthis.releaseNormalUnit(image);\n\t\t\tfor (let i = 0; i < this.boundNormalMaps.length; i++) {\n\t\t\t\tif (this.boundNormalMaps[i] === image) {\n\t\t\t\t\tthis.boundNormalMaps[i] = null;\n\t\t\t\t\tthis.boundNormalVersions[i] = -1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\treset() {\n\t\t// QuadBatcher.reset rebuilds the index buffer, rebinds color\n\t\t// samplers, and resets `useMultiTexture`. MaterialBatcher.reset\n\t\t// (called transitively) iterates `this.renderer.maxTextures` —\n\t\t// covering both color and paired-normal slots — and deletes\n\t\t// every bound GL texture, so our normal-map textures are\n\t\t// already disposed by the time we get here. We just need to\n\t\t// drop the JS references and re-bind the per-frame uniforms.\n\t\tsuper.reset();\n\t\t// `MaterialBatcher.reset` releases the renderer's colour store; normal\n\t\t// maps live outside it, in this batcher's own map, so their GL textures\n\t\t// are ours to delete. Before #1585 they were freed incidentally, by\n\t\t// `reset` walking `boundTextures` — which stopped being the sole\n\t\t// reference to a handle, so freeing has to be explicit now.\n\t\tthis.normalStore.releaseAll(true);\n\t\tthis.boundNormalMaps.fill(null);\n\t\tthis.boundNormalVersions.fill(-1);\n\t\tthis.normalUnits.clear();\n\t\tthis._lightCount = 0;\n\t\t// zero the header (count + ambient) and push it, so a reset mid-scene\n\t\t// leaves the shader reading \"no lights\" rather than the previous\n\t\t// frame's set — the light array past the count is never read.\n\t\tthis.lightBlock.data.fill(0, 0, HEADER_FLOATS);\n\t\tthis.lightBlock.upload(HEADER_FLOATS);\n\t}\n\n\t/**\n\t * Also drop the normal-map binding when its unit is invalidated. A normal\n\t * map occupies a unit in the shared pool, so anything that binds a GL unit\n\t * directly — {@link WebGLRenderer#toFrameTexture}'s scratch unit,\n\t * {@link ShaderEffect#_prepareTextures}'s extra samplers — can clobber one.\n\t * Forget it here, or the next lit draw assumes the normal is still resident,\n\t * skips re-binding, and samples the clobbering texture as a normal map.\n\t * @param {number} unit - the GL texture unit to invalidate\n\t * @ignore\n\t */\n\tinvalidateUnit(unit) {\n\t\tsuper.invalidateUnit(unit);\n\t\tconst stale = this.boundNormalMaps?.[unit];\n\t\tif (stale != null) {\n\t\t\t// unit-driven, not source-driven: whatever clobbered this GL unit\n\t\t\t// bound directly, so drop our belief about it. The allocator claim\n\t\t\t// is deliberately NOT released — the clobberer (a toFrameTexture\n\t\t\t// scratch bind, a ShaderEffect sampler) is squatting there outside\n\t\t\t// the allocator's accounting, and handing the unit to a colour\n\t\t\t// texture now would put two textures on it.\n\t\t\tthis.normalUnits?.delete(stale);\n\t\t\tthis.boundNormalMaps[unit] = null;\n\t\t\tthis.boundNormalVersions[unit] = -1;\n\t\t}\n\t}\n\n\t/**\n\t * On a full texture-cache reset, also forget the paired normal-map\n\t * bindings — the base handler only clears the color `boundTextures`, so a\n\t * lit draw after a reset would otherwise assume a normal map is still bound\n\t * (and skip re-binding it) while its GL unit may have been reused for a\n\t * color texture. The `?.` guards a reset firing before `init` allocates the\n\t * arrays (none does today; hardens against future init reordering).\n\t * @ignore\n\t */\n\t_onTextureCacheReset() {\n\t\tsuper._onTextureCacheReset();\n\t\tthis.boundNormalMaps?.fill(null);\n\t\tthis.boundNormalVersions?.fill(-1);\n\t\tthis.normalUnits?.clear();\n\t\t// `addQuad` resolves a color unit and a normal unit in sequence; the\n\t\t// second allocation can exhaust the pool and wipe the first. Bumping a\n\t\t// counter here is how it notices and re-resolves, rather than stamping\n\t\t// a stale unit into the vertex stream.\n\t\tthis._cacheEpoch = (this._cacheEpoch ?? 0) + 1;\n\t}\n\n\t/**\n\t * Upload the per-frame Light2d data used by the lit fragment path.\n\t * Called once per camera per frame (before the world tree walk).\n\t * Lights past `MAX_LIGHTS` are silently ignored.\n\t *\n\t * Coordinates must be supplied in the same space as the renderer's\n\t * pre-projection vertex coords (i.e. camera-local / FBO-local),\n\t * matching `Stage.drawLighting`'s convention.\n\t *\n\t * The data goes into the `Light2dBlock` uniform buffer, which is\n\t * independent of which program is current — so unlike the uniform arrays\n\t * this replaced, calling it does not disturb the active shader. The\n\t * upload is skipped outright when the packed bytes are unchanged, so a\n\t * scene whose lights are static still costs nothing per frame.\n\t * @param {object} uniforms\n\t * @param {Float32Array} uniforms.positions - flat array of `[x, y, radius, intensity]` per light, length = 4 * count\n\t * @param {Float32Array} uniforms.colors - flat array of `[r, g, b]` per light, length = 3 * count\n\t * @param {Float32Array} [uniforms.heights] - flat array of per-light height, length >= count\n\t * @param {number} uniforms.count - number of lights to render (clamped to MAX_LIGHTS)\n\t * @param {number[]} [uniforms.ambient] - `[r, g, b]` ambient floor (0..1 each)\n\t */\n\tsetLightUniforms(uniforms) {\n\t\tthis._lightCount = Math.min(uniforms.count | 0, this._maxLights);\n\t\t// write straight into the block's own staging buffer — that is what\n\t\t// `UniformBlock.data` is for, and `upload` sends exactly it\n\t\tthis.lightBlock.upload(writeLight2dBlock(this.lightBlock.data, uniforms));\n\t}\n\n\t/**\n\t * Bind a normal-map image to the given GL texture unit. Uploads on\n\t * first use (via `uploadNormalMap`) and rebinds the cached\n\t * `WebGLTexture` on subsequent calls. Mirrors the\n\t * `bindTexture2D` / `createTexture2D` split used by `MaterialBatcher`,\n\t * but for normal-map textures which live outside the color\n\t * `TextureCache` (cached per-image in `normalMapTextures`).\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} image - normal-map source\n\t * @param {number} unit - GL texture unit the normal map is resolved to\n\t */\n\tbindNormalMap(image, unit) {\n\t\t// `image.version` (the dynamic-texture revision; absent => 0/static)\n\t\t// lets an animated source force a re-upload by bumping it — only when\n\t\t// it actually changed, not every frame. The store compares it and\n\t\t// re-uploads into the SAME handle, so an animated source does not churn\n\t\t// a new texture object per frame.\n\t\tconst record = this.normalStore.getResidentRecord(image, {\n\t\t\tversion: image.version ?? 0,\n\t\t\tupload: (handle) => {\n\t\t\t\treturn this.createTexture2D(\n\t\t\t\t\tunit,\n\t\t\t\t\timage,\n\t\t\t\t\tthis.renderer._glTextureFilter(),\n\t\t\t\t\t\"no-repeat\",\n\t\t\t\t\timage.width,\n\t\t\t\t\timage.height,\n\t\t\t\t\t// normal maps store linear-encoded surface normals;\n\t\t\t\t\t// multiplying through alpha would corrupt the encoding\n\t\t\t\t\tfalse,\n\t\t\t\t\tundefined,\n\t\t\t\t\thandle,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\t\t// `bindTexture2D` updates `boundTextures[unit]` and `currentTextureUnit`\n\t\t// so subsequent colour binds don't land on the wrong unit thinking it is\n\t\t// still free. `flush=false` so the in-progress lit batch is undisturbed.\n\t\tthis.bindTexture2D(record.handle, unit, false);\n\t\t// the variant is fixed for normal maps, but the sampler still has to be\n\t\t// bound or the unit keeps whatever the previous texture left there\n\t\tthis.renderer.samplerCache.bind(\n\t\t\tunit,\n\t\t\tthis.renderer.samplerCache.get(this.renderer._glTextureFilter()),\n\t\t);\n\t}\n\n\t/**\n\t * Drop a normal map's slot claim and hand the unit back to the allocator.\n\t *\n\t * A normal map claims its unit through `allocateTextureUnit()`, which has no\n\t * key to release against — so without this the unit stayed marked used for\n\t * the rest of the session and the pool drained monotonically as normal maps\n\t * came and went, until something forced a full reset.\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} image - normal-map source\n\t * @ignore\n\t */\n\treleaseNormalUnit(image) {\n\t\tconst unit = this.normalUnits?.get(image);\n\t\tif (unit === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tthis.normalUnits.delete(image);\n\t\tthis.boundNormalMaps[unit] = null;\n\t\tthis.boundNormalVersions[unit] = -1;\n\t\t// the slot was claimed keylessly, so occupancy is the only record of it\n\t\tthis.renderer.cache.usedUnits.delete(unit);\n\t}\n\n\t/**\n\t * Resolve a normal-map source to its slot in the shared pool, uploading and\n\t * binding it if it is not already resident.\n\t *\n\t * A version-only bump (an animated source re-baking into the same canvas\n\t * reference) re-uploads into the same GL handle and needs no flush —\n\t * `version` only changes between frames, and the batch is flushed at every\n\t * frame/camera boundary, so no in-flight vertex references stale content.\n\t * A DIFFERENT source landing on a live slot does need the pending vertices\n\t * drawn first.\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} normalMap - the source\n\t * @returns {number} the slot to write into `aNormalTextureId`\n\t * @ignore\n\t */\n\tresolveNormalUnit(normalMap) {\n\t\tconst version = normalMap.version ?? 0;\n\t\tconst held = this.normalUnits.get(normalMap);\n\n\t\tif (held !== undefined && this.boundNormalMaps[held] === normalMap) {\n\t\t\tif (this.boundNormalVersions[held] !== version) {\n\t\t\t\tthis.bindNormalMap(normalMap, held);\n\t\t\t\tthis.boundNormalVersions[held] = version;\n\t\t\t}\n\t\t\treturn held;\n\t\t}\n\n\t\t// may flush and wipe every assignment when the pool is exhausted —\n\t\t// `addQuad` re-checks `_cacheEpoch` for exactly that reason\n\t\t// A freshly claimed slot is always vacant: `freeSlot()` only returns\n\t\t// units absent from occupancy, and a normal map's claim is held until it\n\t\t// is explicitly released or the pool is wiped (which clears these arrays\n\t\t// too). So there is never a live normal map to displace here.\n\t\tconst unit = this.renderer.cache.allocateTextureUnit();\n\t\tthis.bindNormalMap(normalMap, unit);\n\t\t// Colors and normal maps share one pool since #1585, so a unit this\n\t\t// batcher binds a normal map onto may be one ANOTHER batcher still\n\t\t// believes holds its color texture — it would then skip re-binding and\n\t\t// sample the normal map. Under the old fixed reservation that was\n\t\t// impossible; now it has to be announced. `except` is `this`, whose own\n\t\t// bookkeeping `bindNormalMap` just updated correctly.\n\t\tthis.renderer.invalidateTextureUnit(unit, this);\n\t\t// `boundTextures[unit]` deliberately keeps pointing at the normal map's\n\t\t// GL texture: `uploadNormalMap` reads it back to cache the handle, and\n\t\t// `MaterialBatcher.reset()` walks that array to DELETE every texture it\n\t\t// owns. Clearing it here leaked one GL texture per normal map per reset.\n\t\tthis.normalUnits.set(normalMap, unit);\n\t\tthis.boundNormalMaps[unit] = normalMap;\n\t\tthis.boundNormalVersions[unit] = version;\n\t\treturn unit;\n\t}\n\n\t/**\n\t * Add a textured quad with optional paired normal map.\n\t * @param {TextureAtlas} texture - Source texture atlas\n\t * @param {number} x - Destination x-coordinate\n\t * @param {number} y - Destination y-coordinate\n\t * @param {number} w - Destination width\n\t * @param {number} h - Destination height\n\t * @param {number} u0 - Texture UV (u0) value\n\t * @param {number} v0 - Texture UV (v0) value\n\t * @param {number} u1 - Texture UV (u1) value\n\t * @param {number} v1 - Texture UV (v1) value\n\t * @param {number} tint - tint color (UINT32 argb)\n\t * @param {boolean} [reupload=false] - Force the texture to be reuploaded\n\t * @param {HTMLImageElement|HTMLCanvasElement|null} [normalMap=null] - paired normal-map (SpriteIlluminator workflow)\n\t */\n\taddQuad(\n\t\ttexture,\n\t\tx,\n\t\ty,\n\t\tw,\n\t\th,\n\t\tu0,\n\t\tv0,\n\t\tu1,\n\t\tv1,\n\t\ttint,\n\t\treupload = false,\n\t\tnormalMap = null,\n\t) {\n\t\tconst vertexData = this.vertexData;\n\n\t\tif (vertexData.isFull(4)) {\n\t\t\tthis.flush();\n\t\t}\n\n\t\tlet unit;\n\n\t\tif (this.useMultiTexture) {\n\t\t\tunit = this.uploadTexture(texture, w, h, reupload, false);\n\t\t\t// Desync guard, not the normal path: the cache and this batcher are\n\t\t\t// both sized from `renderer.maxTextures`, so the allocator cannot\n\t\t\t// return an out-of-range unit unless something reassigns one of them\n\t\t\t// at runtime. Cheap enough to keep as a net.\n\t\t\tif (unit >= this.maxBatchTextures) {\n\t\t\t\tthis.flush();\n\t\t\t\tthis.renderer.cache.resetUnitAssignments();\n\t\t\t\tthis.boundNormalMaps.fill(null);\n\t\t\t\tthis.boundNormalVersions.fill(-1);\n\t\t\t\tunit = this.uploadTexture(texture, w, h, reupload, false);\n\t\t\t}\n\t\t} else {\n\t\t\tunit = this.uploadTexture(texture, w, h, reupload);\n\t\t\tif (unit !== this.currentSamplerUnit) {\n\t\t\t\tthis.currentShader.setUniform(\"uSampler\", unit);\n\t\t\t\tthis.currentSamplerUnit = unit;\n\t\t\t}\n\t\t\t// feed the effect's `noise_uv` builtin — see QuadBatcher.addQuad\n\t\t\tif (typeof this.currentShader._setNoiseUVRect === \"function\") {\n\t\t\t\tconst source = texture.getTexture();\n\t\t\t\tthis.currentShader._setNoiseUVRect(\n\t\t\t\t\tsource.width || source.videoWidth || 1,\n\t\t\t\t\tsource.height || source.videoHeight || 1,\n\t\t\t\t\tw,\n\t\t\t\t\th,\n\t\t\t\t\tMath.min(u0, u1),\n\t\t\t\t\tMath.min(v0, v1),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// direct atlas sampling: uv.y grows downward — see\n\t\t\t// QuadBatcher.addQuad\n\t\t\tthis.currentShader._setUVYDir?.(1);\n\t\t}\n\n\t\tlet normalTextureId = -1;\n\t\tif (normalMap !== null && this.useMultiTexture) {\n\t\t\tconst epoch = this._cacheEpoch;\n\t\t\tnormalTextureId = this.resolveNormalUnit(normalMap);\n\t\t\tif (this._cacheEpoch !== epoch) {\n\t\t\t\t// Claiming the normal's slot exhausted the pool and wiped every\n\t\t\t\t// assignment, so the color unit resolved above is stale. Only the\n\t\t\t\t// color: the normal claimed AFTER the wipe, so its slot is live.\n\t\t\t\t// Re-resolving cannot wipe again either — the normal is the sole\n\t\t\t\t// occupant and the pool never resolves below two slots.\n\t\t\t\tunit = this.uploadTexture(texture, w, h, reupload, false);\n\t\t\t}\n\t\t\tif (normalTextureId === unit) {\n\t\t\t\t// Reachable when reservations leave only ONE assignable slot: the\n\t\t\t\t// normal claims it, the color's re-resolve wipes and claims the\n\t\t\t\t// same one. Sampling the sprite's own albedo as its normal map\n\t\t\t\t// wrecks the lighting silently, so take the unlit path instead —\n\t\t\t\t// flat shading is wrong, but visibly and recoverably so.\n\t\t\t\tnormalTextureId = -1;\n\t\t\t}\n\t\t}\n\n\t\t// Stamp per-sprite depth onto z BEFORE the transform — see\n\t\t// `QuadBatcher.addQuad` for the full rationale (shared corner pool)\n\t\tconst [vec0, vec1, vec2, vec3] = transformQuadCorners(\n\t\t\tthis.viewMatrix,\n\t\t\tx,\n\t\t\ty,\n\t\t\tw,\n\t\t\th,\n\t\t\tthis.renderer.currentDepth,\n\t\t);\n\n\t\tconst textureId = this.useMultiTexture ? unit : 0;\n\t\tvertexData.push(\n\t\t\tvec0.x,\n\t\t\tvec0.y,\n\t\t\tvec0.z,\n\t\t\tu0,\n\t\t\tv0,\n\t\t\ttint,\n\t\t\ttextureId,\n\t\t\tnormalTextureId,\n\t\t);\n\t\tvertexData.push(\n\t\t\tvec1.x,\n\t\t\tvec1.y,\n\t\t\tvec1.z,\n\t\t\tu1,\n\t\t\tv0,\n\t\t\ttint,\n\t\t\ttextureId,\n\t\t\tnormalTextureId,\n\t\t);\n\t\tvertexData.push(\n\t\t\tvec2.x,\n\t\t\tvec2.y,\n\t\t\tvec2.z,\n\t\t\tu0,\n\t\t\tv1,\n\t\t\ttint,\n\t\t\ttextureId,\n\t\t\tnormalTextureId,\n\t\t);\n\t\tvertexData.push(\n\t\t\tvec3.x,\n\t\t\tvec3.y,\n\t\t\tvec3.z,\n\t\t\tu1,\n\t\t\tv1,\n\t\t\ttint,\n\t\t\ttextureId,\n\t\t\tnormalTextureId,\n\t\t);\n\t}\n\n\t/**\n\t * Override `blitTexture` so the FBO blit pushes `-1` as the unlit\n\t * sentinel (this batcher's vertex layout includes `aNormalTextureId`).\n\t * @param {WebGLTexture} source - the raw GL texture to blit\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} width - destination width\n\t * @param {number} height - destination height\n\t * @param {GLShader|ShaderEffect} shader - the shader effect to apply\n\t */\n\tblitTexture(source, x, y, width, height, shader) {\n\t\tconst gl = this.gl;\n\n\t\tthis.useShader(shader);\n\n\t\t// keep the batcher's texture-unit bookkeeping aligned with the GL\n\t\t// state we just mutated — see `QuadBatcher.blitTexture`.\n\t\tgl.activeTexture(gl.TEXTURE0);\n\t\tgl.bindTexture(gl.TEXTURE_2D, source);\n\t\tthis.currentTextureUnit = 0;\n\t\tthis.boundTextures[0] = source;\n\t\tshader.setUniform(\"uSampler\", 0);\n\n\t\t// `noise_uv` builtin: a blit is a full-frame quad — identity rect\n\t\tshader._setNoiseUVRect?.(width, height, width, height, 0, 0);\n\n\t\t// bottom-up capture FBO: uv.y grows upward inside apply() — see\n\t\t// QuadBatcher.blitTexture\n\t\tshader._setUVYDir?.(-1);\n\n\t\t// transform corners through the renderer transform — see\n\t\t// `QuadBatcher.blitTexture` for the rationale. Only caller today\n\t\t// is `WebGLRenderer.blitEffect`, which resets `currentTransform`\n\t\t// to identity, so the matrix branch is dormant in practice.\n\t\t// Explicit z = 0: the shared corner pool is Vector3d.\n\t\tconst [vec0, vec1, vec2, vec3] = transformQuadCorners(\n\t\t\tthis.viewMatrix,\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight,\n\t\t\t0,\n\t\t);\n\n\t\t// blits are always rendered at z = 0 (screen-space, ortho)\n\t\tconst tint = 0xffffffff;\n\t\tthis.vertexData.push(vec0.x, vec0.y, 0, 0, 1, tint, 0, -1);\n\t\tthis.vertexData.push(vec1.x, vec1.y, 0, 1, 1, tint, 0, -1);\n\t\tthis.vertexData.push(vec2.x, vec2.y, 0, 0, 0, tint, 0, -1);\n\t\tthis.vertexData.push(vec3.x, vec3.y, 0, 1, 0, tint, 0, -1);\n\n\t\tthis.flush();\n\n\t\t// see QuadBatcher.blitTexture — unit 0 is nulled renderer-wide\n\t\tgl.activeTexture(gl.TEXTURE0);\n\t\tgl.bindTexture(gl.TEXTURE_2D, null);\n\t\tthis.currentTextureUnit = -1;\n\t\tthis.renderer.invalidateTextureUnit(0);\n\n\t\tthis.useShader(this.defaultShader);\n\t}\n}\n","import { MAX_LIGHTS } from \"./constants.ts\";\n\n/**\n * Per-frame scratch buffer for `packLights()`. Pre-allocates one entry\n * per light slot so the per-camera-per-frame upload doesn't allocate.\n */\nexport interface LightUniformScratch {\n\tpositions: Float32Array;\n\tcolors: Float32Array;\n\theights: Float32Array;\n\tambient: number[];\n}\n\n/**\n * Result of `packLights()` — typed-array views into the scratch plus the\n * actual count. `WebGLRenderer.setLightUniforms` forwards this to\n * `LitQuadBatcher.setLightUniforms` for upload.\n */\nexport interface LightUniforms extends LightUniformScratch {\n\tcount: number;\n}\n\n/**\n * Allocate a fresh scratch buffer sized for `MAX_LIGHTS` slots. One per\n * renderer is enough; reuse across frames.\n */\nexport function createLightUniformScratch(): LightUniformScratch {\n\treturn {\n\t\tpositions: new Float32Array(MAX_LIGHTS * 4),\n\t\tcolors: new Float32Array(MAX_LIGHTS * 3),\n\t\theights: new Float32Array(MAX_LIGHTS),\n\t\tambient: [0, 0, 0],\n\t};\n}\n\n/**\n * Light2d-shaped duck-type — anything `packLights()` reads from. Avoids\n * importing `Light2d` directly (keeps this module renderer-agnostic).\n */\ninterface LightLike {\n\tgetBounds(): {\n\t\tcenterX: number;\n\t\tcenterY: number;\n\t\twidth: number;\n\t\theight: number;\n\t};\n\tintensity: number;\n\tcolor: { r: number; g: number; b: number };\n\tlightHeight: number;\n}\n\n/** Color-shaped duck-type for the ambient floor. */\ninterface ColorLike {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\n/**\n * Pack the active scene lights into the scratch buffer in a shape the\n * lit fragment shader uploads directly.\n *\n * Light positions are translated from world-space (where\n * `light.getBounds().centerX/Y` lives) into the renderer's\n * pre-projection coords by subtracting `(translateX, translateY)` —\n * the same translate `Camera2d.draw()` applies to the world container.\n * This matches what `Stage.drawLighting` does for the cutout pass, so\n * the lit fragment's `lightPos - vWorldPos` math lines up with the\n * camera's view.\n *\n * Lights past `MAX_LIGHTS` are silently dropped. Unused slots are\n * zero-filled so stale data from a previous frame can't leak into the\n * shader.\n *\n * Pure function: deterministic given the same inputs, no I/O, scratch\n * is the only mutable target.\n * @param lights - active lights iterable (e.g. `Stage._activeLights`); falsy treated as empty\n * @param ambient - ambient floor color (0..255 RGB); falsy treated as black\n * @param translateX - world-to-screen X translate (matches `Camera2d.draw()`)\n * @param translateY - world-to-screen Y translate\n * @param scratch - pre-allocated scratch (see `createLightUniformScratch`)\n * @returns the scratch with `count` filled in\n */\nexport function packLights(\n\tlights: Iterable<LightLike> | null | undefined,\n\tambient: ColorLike | null | undefined,\n\ttranslateX: number,\n\ttranslateY: number,\n\tscratch: LightUniformScratch,\n): LightUniforms {\n\tscratch.positions.fill(0);\n\tscratch.colors.fill(0);\n\tscratch.heights.fill(0);\n\n\tlet i = 0;\n\tif (lights) {\n\t\tfor (const light of lights) {\n\t\t\tif (i >= MAX_LIGHTS) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst b = light.getBounds();\n\t\t\t// derive the radius from the transform-aware bbox so a scaled\n\t\t\t// light's brightness range tracks its visible range\n\t\t\tconst radius = Math.max(b.width, b.height) / 2;\n\t\t\tscratch.positions[i * 4 + 0] = b.centerX - translateX;\n\t\t\tscratch.positions[i * 4 + 1] = b.centerY - translateY;\n\t\t\tscratch.positions[i * 4 + 2] = radius;\n\t\t\tscratch.positions[i * 4 + 3] = light.intensity;\n\t\t\tscratch.colors[i * 3 + 0] = light.color.r / 255;\n\t\t\tscratch.colors[i * 3 + 1] = light.color.g / 255;\n\t\t\tscratch.colors[i * 3 + 2] = light.color.b / 255;\n\t\t\tscratch.heights[i] = light.lightHeight;\n\t\t\ti++;\n\t\t}\n\t}\n\n\tif (ambient) {\n\t\tscratch.ambient[0] = ambient.r / 255;\n\t\tscratch.ambient[1] = ambient.g / 255;\n\t\tscratch.ambient[2] = ambient.b / 255;\n\t} else {\n\t\tscratch.ambient[0] = 0;\n\t\tscratch.ambient[1] = 0;\n\t\tscratch.ambient[2] = 0;\n\t}\n\n\treturn {\n\t\tpositions: scratch.positions,\n\t\tcolors: scratch.colors,\n\t\theights: scratch.heights,\n\t\tambient: scratch.ambient,\n\t\tcount: i,\n\t};\n}\n","/**\n * A texture data source that knows how to upload itself to a GPU\n * texture. Subclasses provide the actual upload logic for their kind\n * of source (raw buffer, image, compressed data, etc.).\n *\n * Resources flow through the same `TextureCache` / batcher machinery\n * as image-backed `TextureAtlas` instances: they expose the minimal\n * shape (`sources`, `activeAtlas`, `getTexture()`, plus `width` /\n * `height` / `premultipliedAlpha` / `repeat` / `filter`) the cache\n * uses for unit allocation and the batcher uses for `boundTextures`\n * bookkeeping. The cache therefore owns every backend bind call,\n * which keeps the JS-side binding state in lockstep with the actual\n * backend state across all texture kinds — image atlases included.\n *\n * Subclasses MUST implement `upload(context, target)`. The framework\n * calls it once per texture on first use (and again on forced re-upload\n * via `batcher.uploadTexture(resource, w, h, true)`). `context` is the\n * renderer's backend context (the value returned by\n * `renderer.getContext()`) — a `WebGLRenderingContext` /\n * `WebGL2RenderingContext` today, the WebGPU equivalent once that\n * renderer lands. The signature stays the same; each concrete\n * subclass uses whatever the context happens to be.\n *\n * @category Rendering\n */\nexport class TextureResource {\n\t/**\n\t * @param {object} options\n\t * @param {number} options.width   - pixel width of the texture\n\t * @param {number} options.height  - pixel height of the texture\n\t * @param {boolean} [options.premultipliedAlpha=false]\n\t * @param {string}  [options.repeat=\"no-repeat\"] - \"no-repeat\" | \"repeat\" | \"repeat-x\" | \"repeat-y\"\n\t * @param {number}  [options.filter] - `gl.NEAREST` or `gl.LINEAR`; when\n\t *   omitted the batcher falls back to the renderer's `antiAlias` setting\n\t */\n\tconstructor({\n\t\twidth,\n\t\theight,\n\t\tpremultipliedAlpha = false,\n\t\trepeat = \"no-repeat\",\n\t\tfilter,\n\t} = {}) {\n\t\t/** @type {number} */\n\t\tthis.width = width;\n\t\t/** @type {number} */\n\t\tthis.height = height;\n\t\t/** @type {boolean} */\n\t\tthis.premultipliedAlpha = premultipliedAlpha;\n\t\t/** @type {string} */\n\t\tthis.repeat = repeat;\n\t\t/** @type {number|undefined} */\n\t\tthis.filter = filter;\n\n\t\t// minimal `TextureAtlas`-shaped surface for the cache + batcher\n\t\tthis.sources = new Map([[\"default\", this]]);\n\t\tthis.activeAtlas = \"default\";\n\t}\n\n\t/**\n\t * Returns the upload \"source\" the batcher hands to `createTexture2D`.\n\t * For a resource this is the resource itself — `createTexture2D`\n\t * dispatches to `resource.upload(context, target)`.\n\t * @ignore\n\t */\n\tgetTexture() {\n\t\treturn this;\n\t}\n\n\t/**\n\t * Issue the backend-specific upload call (`gl.texImage2D` on WebGL,\n\t * the WebGPU equivalent once that renderer lands) that places this\n\t * resource's data into the currently-bound texture slot. Subclasses\n\t * MUST override.\n\t * @abstract\n\t * @param {WebGLRenderingContext|WebGL2RenderingContext} context - the\n\t *   renderer's backend context, as returned by `renderer.getContext()`\n\t * @param {number} target - the target to upload into (`TEXTURE_2D` on\n\t *   WebGL today, the WebGPU equivalent once that lands)\n\t */\n\t// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars\n\tupload(context, target) {\n\t\tthrow new Error(\"TextureResource subclasses must implement upload()\");\n\t}\n}\n\n/**\n * A texture sourced from a raw byte buffer. Used for synthesized\n * textures (TMX layer GID index, font atlases, color LUTs, signed-\n * distance fields, palette tables, etc.) — any case where the texture\n * data isn't an image file.\n *\n * The buffer is uploaded as-is; the resource's `premultipliedAlpha`\n * flag is applied at upload time so a raw-data texture (typical:\n * `premultipliedAlpha = false`) doesn't get its RGB wiped by the\n * driver when the alpha channel is zero.\n *\n * @category Rendering\n */\nexport class BufferTextureResource extends TextureResource {\n\t/**\n\t * @param {ArrayBufferView} data - the pixel data; size must be\n\t *   `width * height * 4` bytes for the default RGBA / UNSIGNED_BYTE\n\t *   format\n\t * @param {object} options\n\t * @param {number}  options.width\n\t * @param {number}  options.height\n\t * @param {boolean} [options.premultipliedAlpha=false]\n\t * @param {string}  [options.repeat=\"no-repeat\"]\n\t * @param {number}  [options.filter]\n\t * @param {\"rgba8\"|\"rgba8ui\"} [options.format=\"rgba8\"] - storage format.\n\t *   `\"rgba8\"` (default): normalized RGBA, sampled via `sampler2D` /\n\t *   `texture()`. `\"rgba8ui\"`: unsigned-integer RGBA, sampled via\n\t *   `usampler2D` / `texelFetch()` — requires WebGL2. Use the integer\n\t *   form for raw-data lookups (GID tables, palette indices, etc.) to\n\t *   skip the float-decode round trip and gain exact integer reads.\n\t */\n\tconstructor(data, options) {\n\t\tsuper(options);\n\t\t/** @type {ArrayBufferView} */\n\t\tthis.data = data;\n\t\t/** @type {string} */\n\t\tthis.format = options.format || \"rgba8\";\n\t}\n\n\t/** @ignore */\n\tupload(context, target) {\n\t\tif (this.format === \"rgba8ui\") {\n\t\t\t// `RGBA8UI` / `RGBA_INTEGER` are WebGL 2-only enums. On a\n\t\t\t// WebGL 1 context they're `undefined`, which would otherwise\n\t\t\t// silently invoke `texImage2D` with bogus values and corrupt\n\t\t\t// the texture — surface a clear error so callers know to\n\t\t\t// either drop down to `rgba8` or guard their construction.\n\t\t\tif (typeof context.RGBA8UI === \"undefined\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'BufferTextureResource: format \"rgba8ui\" requires a WebGL 2 context',\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontext.texImage2D(\n\t\t\t\ttarget,\n\t\t\t\t0,\n\t\t\t\tcontext.RGBA8UI,\n\t\t\t\tthis.width,\n\t\t\t\tthis.height,\n\t\t\t\t0,\n\t\t\t\tcontext.RGBA_INTEGER,\n\t\t\t\tcontext.UNSIGNED_BYTE,\n\t\t\t\tthis.data,\n\t\t\t);\n\t\t} else {\n\t\t\tcontext.texImage2D(\n\t\t\t\ttarget,\n\t\t\t\t0,\n\t\t\t\tcontext.RGBA,\n\t\t\t\tthis.width,\n\t\t\t\tthis.height,\n\t\t\t\t0,\n\t\t\t\tcontext.RGBA,\n\t\t\t\tcontext.UNSIGNED_BYTE,\n\t\t\t\tthis.data,\n\t\t\t);\n\t\t}\n\t}\n}\n","#version 300 es\n\n// Fragment shader for the orthogonal TMX layer GPU renderer (WebGL2 /\n// GLSL ES 3.00).\n//\n// Per fragment the shader:\n//   1. recovers the world-pixel position from the host UV,\n//   2. walks candidate cells (geometric cell + cells whose oversized,\n//      bottom-aligned tiles could reach this fragment),\n//   3. fetches GIDs from the per-layer index texture, and\n//   4. samples the tileset atlas at the correct sub-region.\n//\n// Fast path: when the tileset has no oversized tiles (`uOverflow == (0, 0)`)\n// — the common case for grid-aligned maps — only the geometric cell can\n// hold this fragment's tile, so we skip the candidate loop entirely and\n// run a single `tryRenderCell` call. The loop branch is on a uniform\n// value, coherent across the wave, and trims 25 worst-case loop\n// iterations + their guard checks to a single inlined function call.\n//\n// Slow path: tiles drawing larger than the cell are bottom-aligned\n// vertically and left-aligned horizontally. Render order is \"right-down\":\n// later cells end up on top, so the candidate loop scans dy high→low,\n// dx low→high and picks the FIRST match.\n//\n// Index texture encoding (`RGBA8`, one cell per texel):\n//   R = GID low byte\n//   G = GID high byte    (combined: R | (G << 8) = 16-bit GID)\n//   B = flip mask        (bit 0 = H, bit 1 = V, bit 2 = AD)\n//   A = unused\n//\n// Animation lookup (`RGBA8`, 1 row, `tileCount` texels wide): per local\n// tile id, the CURRENT frame's local id, same R/G byte-pair encoding.\n//\n// Why `sampler2D` + float decode rather than `usampler2D`: the engine's\n// multi-texture default shader declares `uSampler0..uSamplerN-1` as\n// `sampler2D` — all of them are active for WebGL's draw-time validation.\n// A `usampler2D`-backed `RGBA8UI` texture cached at any of those units\n// (units 0..15 on a typical 16-unit fragment stage) would mismatch when\n// the default shader next draws sprites, killing every quad with\n// `GL_INVALID_OPERATION`. Staying on regular RGBA8 keeps the cache\n// path coherent — the cost is one `floor(c * 255 + 0.5)` per fetch.\n//\n// `texelFetch` is still used (vs `texture()`) for byte-exact reads —\n// it bypasses interpolation, so the integer byte values come out\n// unmolested even on a normalized-float sampler.\n\n// The engine's `setPrecision` step injects precision declarations for\n// float and int after the `#version` line, using whatever precision the\n// renderer was configured with (`highPrecisionShader` setting on the\n// Application). Individual shader files don't hardcode precision so the\n// engine-wide preference applies.\n\nin vec2 vRegion;\nin vec4 vColor;\n\nuniform sampler2D uSampler;      // tileset atlas (RGBA)\nuniform sampler2D uTileIndex;    // per-layer GID index (RGBA8)\nuniform sampler2D uAnimLookup;   // per-tileset animation table (RGBA8)\n\nuniform vec2 uMapSize;\nuniform vec2 uCellSize;\nuniform vec2 uTileSize;\nuniform vec2 uOverflow;\nuniform vec2 uTilesetCols;\nuniform vec2 uInvTilesetSize;\nuniform vec4 uTilesetMargin;     // (marginX, marginY, spacingX, spacingY)\nuniform vec2 uGidRange;          // (firstgid, lastgid)\nuniform vec2 uVisibleStart;\nuniform vec2 uVisibleSize;\nuniform int  uAnimSize;          // number of entries in uAnimLookup, 0 if disabled\nuniform float uOpacity;\nuniform vec4 uTint;\n\nout vec4 fragColor;\n\nconst int MAX_OVERFLOW = 4;\n\n// Try to render the tile at cell (cx, cy) for the current fragment.\n// Returns true and writes the sampled color to `outColor` when the cell\n// contains a visible, in-range tile whose pixel covers this fragment.\n// Identical logic for both fast and slow paths — GLSL inlines this\n// trivially so there's no function-call overhead at runtime.\nbool tryRenderCell(int cx, int cy, vec2 worldPx, out vec4 outColor) {\n\tint mapW = int(uMapSize.x);\n\tint mapH = int(uMapSize.y);\n\tif (cx < 0 || cx >= mapW || cy < 0 || cy >= mapH) {\n\t\treturn false;\n\t}\n\n\t// `texelFetch` skips filtering — the 8-bit channel values come back\n\t// as normalized floats, decoded to byte ints below\n\tvec4 cellF = texelFetch(uTileIndex, ivec2(cx, cy), 0);\n\tuvec4 cell = uvec4(cellF * 255.0 + 0.5);\n\tint firstGid = int(uGidRange.x);\n\tint gid = int(cell.r) | (int(cell.g) << 8);\n\tif (gid < firstGid || gid > int(uGidRange.y)) {\n\t\treturn false;\n\t}\n\n\tvec2 tileWorldOrigin = vec2(\n\t\tfloat(cx) * uCellSize.x,\n\t\t(float(cy) + 1.0) * uCellSize.y - uTileSize.y\n\t);\n\tvec2 inTile = (worldPx - tileWorldOrigin) / uTileSize;\n\tif (inTile.x < 0.0 || inTile.x >= 1.0 || inTile.y < 0.0 || inTile.y >= 1.0) {\n\t\treturn false;\n\t}\n\n\t// flip mask + axis-swap trick: see TMX shader-path flip spec.\n\t// AD performs a transpose; with AD set, H and V swap their effective\n\t// axes (matches the legacy `buildFlipTransform`).\n\tint flipMask = int(cell.b);\n\tfloat flipH = float(flipMask & 1);\n\tfloat flipV = float((flipMask >> 1) & 1);\n\tfloat flipAD = float((flipMask >> 2) & 1);\n\tinTile = mix(inTile, inTile.yx, flipAD);\n\tfloat effH = mix(flipH, flipV, flipAD);\n\tfloat effV = mix(flipV, flipH, flipAD);\n\tinTile.x = mix(inTile.x, 1.0 - inTile.x, effH);\n\tinTile.y = mix(inTile.y, 1.0 - inTile.y, effV);\n\n\tint localId = gid - firstGid;\n\n\t// animation: if the tileset has animated tiles, swap the local id for\n\t// its current frame's id via the lookup texture (CPU updates the\n\t// lookup in lockstep with `tileset.update(dt)`).\n\tif (uAnimSize > 0) {\n\t\tvec4 animF = texelFetch(uAnimLookup, ivec2(localId, 0), 0);\n\t\tuvec4 animTexel = uvec4(animF * 255.0 + 0.5);\n\t\tlocalId = int(animTexel.r) | (int(animTexel.g) << 8);\n\t}\n\n\tfloat row = floor(float(localId) / uTilesetCols.x);\n\tfloat col = float(localId) - row * uTilesetCols.x;\n\tvec2 tileOriginPx = uTilesetMargin.xy\n\t\t+ vec2(col, row) * (uTileSize + uTilesetMargin.zw);\n\tvec2 texelPx = tileOriginPx + inTile * uTileSize;\n\tvec2 texelUV = texelPx * uInvTilesetSize;\n\n\tvec4 sampled = texture(uSampler, texelUV);\n\tif (sampled.a <= 0.0) {\n\t\treturn false;\n\t}\n\toutColor = sampled;\n\treturn true;\n}\n\nvoid main(void) {\n\tvec2 tileCoord = uVisibleStart + vRegion * uVisibleSize;\n\tvec2 geomCell = floor(tileCoord);\n\tvec2 worldPx = tileCoord * uCellSize;\n\n\tint gx = int(geomCell.x);\n\tint gy = int(geomCell.y);\n\tint overflowX = int(uOverflow.x + 0.5);\n\tint overflowY = int(uOverflow.y + 0.5);\n\n\tvec4 result;\n\n\t// Fast path: tiles fit the cell exactly (the common case) — only the\n\t// geometric cell can contain this fragment's tile.\n\tif (overflowX == 0 && overflowY == 0) {\n\t\tif (!tryRenderCell(gx, gy, worldPx, result)) {\n\t\t\tdiscard;\n\t\t}\n\t\tresult.a *= uOpacity;\n\t\tfragColor = result * uTint;\n\t\treturn;\n\t}\n\n\t// Slow path: oversized tiles. Walk candidates dy high→low, dx low→high\n\t// and pick the FIRST match — render order is \"right-down\" so later\n\t// cells go on top.\n\tbool found = false;\n\tfor (int idy = 0; idy <= MAX_OVERFLOW; idy++) {\n\t\tint dy = MAX_OVERFLOW - idy;\n\t\tif (dy > overflowY) continue;\n\t\tfor (int dx = 0; dx <= MAX_OVERFLOW; dx++) {\n\t\t\tif (dx > overflowX) break;\n\t\t\tif (tryRenderCell(gx - dx, gy + dy, worldPx, result)) {\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (found) break;\n\t}\n\tif (!found) discard;\n\tresult.a *= uOpacity;\n\tfragColor = result * uTint;\n}\n","#version 300 es\n\n// Vertex shader for the orthogonal TMX layer GPU renderer.\n//\n// Matches the quad batcher's vertex layout (`aVertex`, `aRegion`, `aColor`,\n// `uProjectionMatrix`) so the standard `setBatcher(\"quad\", this.shader)` +\n// `addQuad()` flow drives it like any other quad. Same attribute names,\n// same uniforms — just expressed in GLSL ES 3.00 (`in`/`out` in place of\n// `attribute`/`varying`) so the program can pair with the 3.00 fragment\n// shader that uses `usampler2D` / `texelFetch` for integer-typed lookups.\n\n// `aVertex.z` carries per-sprite depth (renderable.depth) — a no-op under\n// the default ortho projection, used by perspective (Camera3d).\nin vec3 aVertex;\nin vec2 aRegion;\nin vec4 aColor;\n\nuniform mat4 uProjectionMatrix;\n\nout vec2 vRegion;\nout vec4 vColor;\n\nvoid main(void) {\n\tgl_Position = uProjectionMatrix * vec4(aVertex, 1.0);\n\t// premultiplied-alpha + bgra → rgba swap, same convention the batcher\n\t// uses for its default sprite shader\n\tvColor = vec4(aColor.bgr * aColor.a, aColor.a);\n\tvRegion = aRegion;\n}\n","import { BufferTextureResource } from \"../../../texture/resource.js\";\nimport GLShader from \"../../glshader.js\";\nimport orthogonalTMXLayerFragment from \"../../shaders/orthogonal-tmxlayer.frag\";\nimport orthogonalTMXLayerVertex from \"../../shaders/orthogonal-tmxlayer.vert\";\n\n/**\n * additional imports for TypeScript\n * @import { default as TMXLayer } from \"../../../../level/tiled/TMXLayer.js\";\n * @import { default as WebGLRenderer } from \"../../webgl_renderer.js\";\n */\n\n// Default `uTint` when the layer has no tint set. Module-scoped so the\n// fallback path doesn't allocate per frame.\nconst DEFAULT_TINT = new Float32Array([1, 1, 1, 1]);\n\n/**\n * GPU-accelerated renderer for orthogonal TMX tile layers (WebGL2). Draws\n * the visible region of a layer as one screen-aligned quad per tileset\n * referenced by the layer — the fragment shader samples a per-layer GID\n * index texture and the tileset atlas, eliminating the per-tile draw\n * loop. The visible rect, GID range, tile size, opacity, and tint are\n * pushed as uniforms; the index texture is uploaded once at activation\n * and re-uploaded only when `layer.dataVersion` changes (mutations from\n * `setTile`/`clearTile`).\n *\n * The per-layer index texture is a `BufferTextureResource` flowing\n * through the standard `TextureCache` / batcher path — same lane as\n * every other texture in the engine. Dynamic unit allocation, correct\n * `boundTextures` bookkeeping, and per-resource premultiplied-alpha /\n * filter all come for free; nothing here pokes `gl.bindTexture` or\n * `gl.activeTexture` directly.\n *\n * Cache lifecycle: one `BufferTextureResource` per `TMXLayer`. Tile\n * layers don't come and go individually — they only churn on game reset\n * — so the cache is freed in bulk via `reset()`, called from\n * `WebGLRenderer.reset()` (which the `GAME_RESET` event already\n * triggers).\n *\n * @ignore\n */\nexport default class OrthogonalTMXLayerGPURenderer {\n\t/**\n\t * @param {WebGLRenderer} renderer - the WebGL renderer instance\n\t */\n\tconstructor(renderer) {\n\t\tthis.renderer = renderer;\n\t\tthis.gl = renderer.gl;\n\t\t// Standalone `GLShader` (not `ShaderEffect`) so we own both the\n\t\t// vertex and the fragment source — they're paired in GLSL ES 3.00,\n\t\t// which lets the fragment shader use `texelFetch` for byte-exact\n\t\t// reads from the index / animation lookup textures (samplers stay\n\t\t// `sampler2D` and decode the 8-bit channels as normalized floats;\n\t\t// `usampler2D` would conflict with the engine's multi-texture\n\t\t// batching cache). `setBatcher(\"quad\", this.shader)` integrates\n\t\t// the program with the quad batcher just like a `ShaderEffect` —\n\t\t// only the per-fragment math gets cleaner.\n\t\tthis.shader = new GLShader(\n\t\t\trenderer.gl,\n\t\t\torthogonalTMXLayerVertex,\n\t\t\torthogonalTMXLayerFragment,\n\t\t\trenderer.shaderPrecision,\n\t\t);\n\t\t// per-layer `BufferTextureResource`. `Map` (not `WeakMap`) so we\n\t\t// can iterate on `reset()` to walk through the global texture\n\t\t// cache and free GL handles. The world's container holds the\n\t\t// strong ref to each layer anyway, so this doesn't change layer\n\t\t// lifetime.\n\t\tthis.resources = new Map();\n\t\t// per-tileset animation-lookup resources, keyed by `TMXTileset`.\n\t\t// Only allocated for tilesets that actually have animated tiles\n\t\t// (`tileset.isAnimated === true`).\n\t\tthis.animLookups = new Map();\n\t\t// pre-allocated scratch for uniform uploads — avoids per-frame\n\t\t// allocation in the hot path\n\t\tthis._v2 = new Float32Array(2);\n\t\tthis._v4 = new Float32Array(4);\n\t}\n\n\t/**\n\t * Free every cached per-layer index texture and empty the local\n\t * resource map. Called from `WebGLRenderer.reset()` (which\n\t * `GAME_RESET` triggers) so each level transition starts clean.\n\t * @ignore\n\t */\n\treset() {\n\t\t// Uploads happen via the quad batcher (see `_drawLayer`), so pin\n\t\t// the cleanup to that same batcher rather than whatever happens\n\t\t// to be `currentBatcher` at reset time. `currentBatcher` may\n\t\t// legitimately be a `PrimitiveBatcher` (e.g. debug-plugin drew\n\t\t// its quadtree overlay on the previous frame) which has no\n\t\t// `deleteTexture2D` and would crash the reset. Issue #1471.\n\t\tconst batcher = this.renderer.batchers.get(\"quad\");\n\t\tconst cache = this.renderer.cache;\n\t\tconst drop = (resource) => {\n\t\t\t// route through the batcher so its `boundTextures` bookkeeping\n\t\t\t// stays in sync. When no batcher is available (context tear-\n\t\t\t// down) or the registered \"quad\" batcher is a user-supplied\n\t\t\t// custom class that doesn't extend `MaterialBatcher`, we\n\t\t\t// don't have a clean GL deletion path — still need to free\n\t\t\t// the unit assignment, since `cache.delete()` only touches\n\t\t\t// the image→atlas map and would leave the unit slot held\n\t\t\t// forever otherwise.\n\t\t\tif (typeof batcher?.deleteTexture2D === \"function\") {\n\t\t\t\tbatcher.deleteTexture2D(resource);\n\t\t\t} else {\n\t\t\t\tcache.freeTextureUnit(resource);\n\t\t\t\tcache.delete(resource);\n\t\t\t}\n\t\t};\n\t\tfor (const resource of this.resources.values()) {\n\t\t\tdrop(resource);\n\t\t}\n\t\tfor (const entry of this.animLookups.values()) {\n\t\t\tdrop(entry.resource);\n\t\t}\n\t\tthis.resources.clear();\n\t\tthis.animLookups.clear();\n\t}\n\n\t/**\n\t * Write a `vec2` uniform without allocating a fresh Float32Array per\n\t * call. Both components flow into the shared `_v2` scratch buffer,\n\t * which `setUniform` reads synchronously and forwards to\n\t * `gl.uniform2fv` — so reusing the buffer across calls is safe.\n\t * @param {string} name\n\t * @param {number} x\n\t * @param {number} y\n\t * @private\n\t */\n\t_setV2(name, x, y) {\n\t\tthis._v2[0] = x;\n\t\tthis._v2[1] = y;\n\t\tthis.shader.setUniform(name, this._v2);\n\t}\n\n\t/**\n\t * `vec4` counterpart to {@link _setV2}.\n\t * @param {string} name\n\t * @param {number} x\n\t * @param {number} y\n\t * @param {number} z\n\t * @param {number} w\n\t * @private\n\t */\n\t_setV4(name, x, y, z, w) {\n\t\tthis._v4[0] = x;\n\t\tthis._v4[1] = y;\n\t\tthis._v4[2] = z;\n\t\tthis._v4[3] = w;\n\t\tthis.shader.setUniform(name, this._v4);\n\t}\n\n\t/**\n\t * Get-or-create the per-tileset animation-lookup entry. Returns\n\t * `undefined` for tilesets that have no animated tiles (the shader's\n\t * `uAnimEnabled` uniform is then set to 0 and the lookup texture is\n\t * not bound).\n\t *\n\t * The entry holds a `tileCount × 1` RGBA8 `BufferTextureResource`\n\t * where texel `localId` encodes the CURRENT frame's local id as\n\t * `R = lo byte, G = hi byte` (same encoding as the GID index\n\t * texture). Each call walks `tileset.animations` and rewrites\n\t * dirty texels — `tileset.update(dt)` (driven by the layer) advances\n\t * `anim.cur.tileid` independently of this renderer.\n\t *\n\t * @param {object} tileset\n\t * @param {number} tileCount - tiles in the tileset's atlas grid\n\t *   (`atlasCols * atlasRows`)\n\t * @returns {{resource: BufferTextureResource, data: Uint8Array,\n\t *   tileCount: number, dirty: boolean}|undefined}\n\t */\n\t_getOrUpdateAnimLookup(tileset, tileCount) {\n\t\tif (!tileset.isAnimated || tileset.animations.size === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tlet entry = this.animLookups.get(tileset);\n\t\tif (entry === undefined) {\n\t\t\t// allocate the lookup texture and initialize it to identity\n\t\t\t// (localId → localId); animated entries get overwritten below\n\t\t\tconst data = new Uint8Array(tileCount * 4);\n\t\t\tfor (let id = 0; id < tileCount; id++) {\n\t\t\t\tdata[id * 4 + 0] = id & 0xff;\n\t\t\t\tdata[id * 4 + 1] = (id >> 8) & 0xff;\n\t\t\t}\n\t\t\tconst resource = new BufferTextureResource(data, {\n\t\t\t\twidth: tileCount,\n\t\t\t\theight: 1,\n\t\t\t\tpremultipliedAlpha: false,\n\t\t\t\trepeat: \"no-repeat\",\n\t\t\t\t// NEAREST so the shader's `texelFetch` reads byte-exact\n\t\t\t\t// channel values back as normalized floats\n\t\t\t\tfilter: this.gl.NEAREST,\n\t\t\t\tformat: \"rgba8\",\n\t\t\t});\n\t\t\t// new entry → first upload happens unconditionally via the\n\t\t\t// batcher's `boundTextures[unit] === undefined` path\n\t\t\tentry = { resource, data, tileCount, dirty: false };\n\t\t\tthis.animLookups.set(tileset, entry);\n\t\t}\n\t\t// walk animations and rewrite the current-frame ids. The dirty\n\t\t// flag flips true only when at least one texel actually changed\n\t\t// — the batcher then force-reuploads on the next bind, otherwise\n\t\t// reuses the existing GL texture untouched.\n\t\tconst data = entry.data;\n\t\tfor (const [localId, anim] of tileset.animations) {\n\t\t\tconst off = localId * 4;\n\t\t\tconst cur = anim.cur.tileid;\n\t\t\tconst lo = cur & 0xff;\n\t\t\tconst hi = (cur >> 8) & 0xff;\n\t\t\tif (data[off] !== lo || data[off + 1] !== hi) {\n\t\t\t\tdata[off] = lo;\n\t\t\t\tdata[off + 1] = hi;\n\t\t\t\tentry.dirty = true;\n\t\t\t}\n\t\t}\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Get-or-create the per-layer index `BufferTextureResource`.\n\t * @param {TMXLayer} layer\n\t * @returns {BufferTextureResource}\n\t */\n\t_getResource(layer) {\n\t\tlet resource = this.resources.get(layer);\n\t\tif (resource === undefined) {\n\t\t\t// reinterpret the layer's `Uint16Array` payload as RGBA bytes\n\t\t\t// (zero-copy on every little-endian platform = every browser)\n\t\t\tresource = new BufferTextureResource(\n\t\t\t\tnew Uint8Array(layer.layerData.buffer),\n\t\t\t\t{\n\t\t\t\t\twidth: layer.cols,\n\t\t\t\t\theight: layer.rows,\n\t\t\t\t\t// raw GID bytes — must NOT have alpha pre-multiplied\n\t\t\t\t\t// into RGB, otherwise A=0 cells wipe their R/G/B\n\t\t\t\t\tpremultipliedAlpha: false,\n\t\t\t\t\trepeat: \"no-repeat\",\n\t\t\t\t\t// NEAREST so `texelFetch` returns the original byte\n\t\t\t\t\t// values (as normalized floats) for the GID/flip\n\t\t\t\t\t// decode below\n\t\t\t\t\tfilter: this.gl.NEAREST,\n\t\t\t\t\tformat: \"rgba8\",\n\t\t\t\t},\n\t\t\t);\n\t\t\t// track the last-uploaded version so we know when to force a\n\t\t\t// re-upload after `setTile` / `clearTile` mutations\n\t\t\tresource._uploadedVersion = -1;\n\t\t\tthis.resources.set(layer, resource);\n\t\t}\n\t\treturn resource;\n\t}\n\n\t/**\n\t * Draw an orthogonal TMX layer through the shader path.\n\t * @param {TMXLayer} layer\n\t * @param {object} rect - the visible viewport rect (world coords)\n\t */\n\tdraw(layer, rect) {\n\t\tconst renderer = this.renderer;\n\t\tconst tileWidth = layer.tilewidth;\n\t\tconst tileHeight = layer.tileheight;\n\t\tconst cols = layer.cols;\n\t\tconst rows = layer.rows;\n\n\t\t// compute the visible region in tile-coord space\n\t\tconst startTileX = Math.max(0, Math.floor(rect.pos.x / tileWidth));\n\t\tconst startTileY = Math.max(0, Math.floor(rect.pos.y / tileHeight));\n\t\tconst endTileX = Math.min(\n\t\t\tcols,\n\t\t\tMath.ceil((rect.pos.x + rect.width) / tileWidth),\n\t\t);\n\t\tconst endTileY = Math.min(\n\t\t\trows,\n\t\t\tMath.ceil((rect.pos.y + rect.height) / tileHeight),\n\t\t);\n\n\t\t// visible rect entirely outside the layer — nothing to draw\n\t\tif (endTileX <= startTileX || endTileY <= startTileY) {\n\t\t\treturn;\n\t\t}\n\n\t\t// quad world coords\n\t\tconst worldX = startTileX * tileWidth;\n\t\tconst worldY = startTileY * tileHeight;\n\t\tconst worldW = (endTileX - startTileX) * tileWidth;\n\t\tconst worldH = (endTileY - startTileY) * tileHeight;\n\n\t\t// visible region size in tile units (matches the quad's [0..1] UV\n\t\t// span — shader recovers the per-fragment tile coord as\n\t\t// `uVisibleStart + uv * uVisibleSize`)\n\t\tconst visStartX = startTileX;\n\t\tconst visStartY = startTileY;\n\t\tconst visSizeX = endTileX - startTileX;\n\t\tconst visSizeY = endTileY - startTileY;\n\n\t\t// switch the quad batcher to our custom shader. This flushes any\n\t\t// queued sprite vertices first so they render under their original\n\t\t// shader, then re-binds for our pass.\n\t\tconst batcher = renderer.setBatcher(\"quad\", this.shader);\n\n\t\t// upload (or rebind) the index texture through the standard cache\n\t\t// path: dynamic unit allocation, correct `boundTextures` tracking,\n\t\t// per-resource premultiply / filter applied automatically\n\t\tconst resource = this._getResource(layer);\n\t\tconst indexUnit = batcher.uploadTexture(\n\t\t\tresource,\n\t\t\tcols,\n\t\t\trows,\n\t\t\tresource._uploadedVersion !== layer.dataVersion,\n\t\t);\n\t\tresource._uploadedVersion = layer.dataVersion;\n\n\t\tthis.shader.setUniform(\"uTileIndex\", indexUnit);\n\t\tthis._setV2(\"uMapSize\", cols, rows);\n\t\tthis._setV2(\"uVisibleStart\", visStartX, visStartY);\n\t\tthis._setV2(\"uVisibleSize\", visSizeX, visSizeY);\n\t\tthis._setV2(\"uCellSize\", tileWidth, tileHeight);\n\t\tthis.shader.setUniform(\"uOpacity\", layer.getOpacity());\n\t\tthis.shader.setUniform(\n\t\t\t\"uTint\",\n\t\t\tlayer.tint ? layer.tint.toArray() : DEFAULT_TINT,\n\t\t);\n\n\t\t// one pass per tileset — the shader's uGidRange uniform + discard\n\t\t// hides cells outside the active tileset's GID range\n\t\tconst tilesets = layer.tilesets.tilesets;\n\t\tfor (let i = 0; i < tilesets.length; i++) {\n\t\t\tconst tileset = tilesets[i];\n\t\t\t// skip collection-of-image tilesets (caller's eligibility check\n\t\t\t// should have downgraded these layers to the legacy path, but\n\t\t\t// guard defensively — we can't compute uTilesetCols for them)\n\t\t\tif (tileset.isCollection || tileset.image === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst tsW = tileset.tilewidth;\n\t\t\tconst tsH = tileset.tileheight;\n\t\t\tconst margin = tileset.margin;\n\t\t\tconst spacing = tileset.spacing;\n\t\t\tconst atlasW = tileset.image.width;\n\t\t\tconst atlasH = tileset.image.height;\n\n\t\t\t// atlas geometry (margin + spacing) — number of tiles per row/col\n\t\t\t// in the atlas image\n\t\t\tconst atlasCols = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.floor((atlasW - margin * 2 + spacing) / (tsW + spacing)),\n\t\t\t);\n\t\t\tconst atlasRows = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.floor((atlasH - margin * 2 + spacing) / (tsH + spacing)),\n\t\t\t);\n\n\t\t\t// per-tileset animation lookup. Only allocated/bound for\n\t\t\t// tilesets that actually have animated tiles — static tilesets\n\t\t\t// pay neither a texture-unit nor an upload.\n\t\t\tconst tileCount = atlasCols * atlasRows;\n\t\t\tconst animEntry = this._getOrUpdateAnimLookup(tileset, tileCount);\n\t\t\tif (animEntry !== undefined) {\n\t\t\t\tconst animUnit = batcher.uploadTexture(\n\t\t\t\t\tanimEntry.resource,\n\t\t\t\t\tanimEntry.tileCount,\n\t\t\t\t\t1,\n\t\t\t\t\tanimEntry.dirty,\n\t\t\t\t);\n\t\t\t\tanimEntry.dirty = false;\n\t\t\t\tthis.shader.setUniform(\"uAnimLookup\", animUnit);\n\t\t\t\tthis.shader.setUniform(\"uAnimSize\", animEntry.tileCount);\n\t\t\t} else {\n\t\t\t\t// shader skips the animation lookup when uAnimSize == 0,\n\t\t\t\t// but every declared sampler must still point at a valid\n\t\t\t\t// unit — reuse the index unit so WebGL's draw-time\n\t\t\t\t// validation is happy (the sampler is never read because\n\t\t\t\t// of the uAnimSize guard)\n\t\t\t\tthis.shader.setUniform(\"uAnimLookup\", indexUnit);\n\t\t\t\tthis.shader.setUniform(\"uAnimSize\", 0);\n\t\t\t}\n\n\t\t\tthis._setV2(\"uTileSize\", tsW, tsH);\n\t\t\tthis._setV2(\"uTilesetCols\", atlasCols, atlasRows);\n\t\t\tthis._setV2(\"uInvTilesetSize\", 1 / atlasW, 1 / atlasH);\n\t\t\tthis._setV4(\"uTilesetMargin\", margin, margin, spacing, spacing);\n\t\t\tthis._setV2(\n\t\t\t\t\"uOverflow\",\n\t\t\t\tMath.max(0, Math.ceil(tsW / tileWidth) - 1),\n\t\t\t\tMath.max(0, Math.ceil(tsH / tileHeight) - 1),\n\t\t\t);\n\t\t\tthis._setV2(\"uGidRange\", tileset.firstgid, tileset.lastgid);\n\n\t\t\t// emit one screen-quad. The batcher will bind the tileset's\n\t\t\t// TextureAtlas to uSampler (single-texture fallback path is\n\t\t\t// active because we have a custom ShaderEffect).\n\t\t\tbatcher.addQuad(\n\t\t\t\ttileset.texture,\n\t\t\t\tworldX,\n\t\t\t\tworldY,\n\t\t\t\tworldW,\n\t\t\t\tworldH,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t1,\n\t\t\t\t1,\n\t\t\t\t0xffffffff,\n\t\t\t);\n\n\t\t\t// flush per-pass so each tileset's draw call uses its own\n\t\t\t// uniforms (uGidRange / uTileSize / uTilesetCols differ)\n\t\t\tbatcher.flush();\n\t\t}\n\t}\n}\n","/**\n * Default ceiling applied when `maxTextures` is `\"auto\"`.\n *\n * There is no spec maximum to defer to — WebGL 2 / GLES 3.0 only set a\n * *minimum* of 16 for `MAX_TEXTURE_IMAGE_UNITS`, and an implementation may\n * report any value at or above it. What actually limits us is that the\n * multi-texture fragment shader unrolls one sampler and one branch per unit\n * (see `buildMultiTextureFragment`), so a very wide pool costs compile time\n * and register pressure, and historically some drivers mis-compile the widest\n * ladders. 32 covers current desktop and mobile hardware while staying a\n * conservative default; raise or lower it with the `maxTextures` setting.\n * @ignore\n */\nexport const AUTO_MAX_TEXTURES = 32;\n\n/**\n * Smallest pool the engine will resolve to. A lit quad occupies a colour slot\n * and a normal-map slot simultaneously, so anything narrower makes the two ids\n * collide and the sprite samples its own albedo as a normal map.\n * @ignore\n */\nexport const MIN_MAX_TEXTURES = 2;\n\n/**\n * Resolve the texture-unit pool this renderer will use.\n *\n * Clamping happens ONCE, at renderer construction, before the batchers and the\n * `TextureCache` are built — every consumer derives from the result, so the\n * batchers' sampler counts and the cache's capacity cannot disagree. A setting\n * above what the device reports is clamped down rather than honoured: declaring\n * more `sampler2D` uniforms than the device has units fails to link, and a link\n * failure at startup is not a useful way to learn about a typo.\n * @param {number} deviceMax - the device's `MAX_TEXTURE_IMAGE_UNITS`\n * @param {\"auto\"|number} [setting=\"auto\"] - the `maxTextures` application setting\n * @returns {number} the pool size, at least 1 and never above `deviceMax`\n * @ignore\n */\nexport function resolveMaxTextures(deviceMax, setting = \"auto\") {\n\t// a device that reports nothing usable still has to render something\n\tconst device = Number.isFinite(deviceMax) && deviceMax > 0 ? deviceMax : 1;\n\t// MIN_POOL, not 1: a lit quad needs a colour slot and a normal slot at the\n\t// same time, and a one-slot pool makes the two collide — the sprite would\n\t// sample its own albedo as its normal map. Never above what the device has.\n\tconst floor = Math.min(MIN_MAX_TEXTURES, device);\n\tif (typeof setting === \"number\" && Number.isFinite(setting)) {\n\t\treturn Math.max(floor, Math.min(Math.floor(setting), device));\n\t}\n\t// \"auto\" (or anything unrecognized — an unset/typo'd value must not brick\n\t// the renderer, matching how `textureFilter` tolerates an unknown mode)\n\treturn Math.max(floor, Math.min(device, AUTO_MAX_TEXTURES));\n}\n","/**\n * WebGL 2 sampler objects, deduplicated by their state.\n *\n * GL bakes wrap and filter into the texture *object*, which is why the same\n * image drawn at two repeat modes historically needed two texture units AND\n * two uploads (#1448). Sampler objects — core in GLES 3.0, and unconditionally\n * available since 20.0 dropped WebGL 1 — move that state out of the texture and\n * onto the unit, exactly as the WebGPU backend already separates `GPUTexture`\n * from `GPUSampler`.\n *\n * That separation is what lets texture residency be keyed by **source alone**:\n * one upload serves every variant, and the variant rides the sampler. Slot\n * assignment stays per `(source, variant)`, because a bound sampler applies to\n * a unit and a unit carries one at a time.\n *\n * Deliberately mirrors `WebGPUTextureStore.getSampler(filter, repeat, mipmaps)`\n * — same key, same dedup, so the two backends stay legible side by side.\n * @ignore\n */\nexport class GLSamplerCache {\n\t/**\n\t * @param {WebGL2RenderingContext} gl - the owning context\n\t * @ignore\n\t */\n\tconstructor(gl) {\n\t\tthis.gl = gl;\n\t\t/** @type {Map<string, WebGLSampler>} */\n\t\tthis.samplers = new Map();\n\t\t// `get` runs once per QUAD — hundreds of times a frame, almost always\n\t\t// with the arguments it was just called with. Building a string key and\n\t\t// running two regexes each time is pure waste in the hottest path, so\n\t\t// the last answer is memoized behind a three-value guard.\n\t\tthis.lastFilter = -1;\n\t\tthis.lastRepeat = null;\n\t\tthis.lastMipmap = null;\n\t\tthis.lastSampler = null;\n\t\t// which sampler each unit currently holds, so a redundant bind is\n\t\t// skipped rather than issued\n\t\t/** @type {Array<WebGLSampler|null>} */\n\t\tthis.bound = [];\n\t}\n\n\t/**\n\t * The sampler for a filter/wrap/mip combination, created once and reused.\n\t *\n\t * There are only a handful of live combinations in any scene — two filters\n\t * times four repeat modes — so this Map stays tiny however many textures a\n\t * game loads.\n\t * @param {number} filter - `gl.NEAREST` or `gl.LINEAR`\n\t * @param {string} [repeat=\"no-repeat\"] - canvas-style repeat mode\n\t * @param {boolean} [mipmap=false] - sample the mip chain (trilinear)\n\t * @returns {WebGLSampler} the shared sampler\n\t * @ignore\n\t */\n\tget(filter, repeat = \"no-repeat\", mipmap = false) {\n\t\tif (\n\t\t\tfilter === this.lastFilter &&\n\t\t\trepeat === this.lastRepeat &&\n\t\t\tmipmap === this.lastMipmap\n\t\t) {\n\t\t\treturn this.lastSampler;\n\t\t}\n\t\tconst gl = this.gl;\n\t\t// same per-axis mapping as `createTexture2D`\n\t\tconst wrapS = /^repeat(-x)?$/.test(repeat) ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\t\tconst wrapT = /^repeat(-y)?$/.test(repeat) ? gl.REPEAT : gl.CLAMP_TO_EDGE;\n\t\t// trilinear only over a linear-filtered chain — \"nearest\" opts out so\n\t\t// crisp pixel-art keeps hard minification, matching the mesh path's rule\n\t\tconst mip = mipmap === true && filter === gl.LINEAR;\n\t\tconst key = `${filter}|${wrapS}|${wrapT}|${mip ? \"mip\" : \"flat\"}`;\n\n\t\tlet sampler = this.samplers.get(key);\n\t\tif (sampler === undefined) {\n\t\t\tsampler = gl.createSampler();\n\t\t\tgl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, wrapS);\n\t\t\tgl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, wrapT);\n\t\t\tgl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, filter);\n\t\t\tgl.samplerParameteri(\n\t\t\t\tsampler,\n\t\t\t\tgl.TEXTURE_MIN_FILTER,\n\t\t\t\tmip ? gl.LINEAR_MIPMAP_LINEAR : filter,\n\t\t\t);\n\t\t\tthis.samplers.set(key, sampler);\n\t\t}\n\t\tthis.lastFilter = filter;\n\t\tthis.lastRepeat = repeat;\n\t\tthis.lastMipmap = mipmap;\n\t\tthis.lastSampler = sampler;\n\t\treturn sampler;\n\t}\n\n\t/**\n\t * Bind a sampler to a texture unit, or unbind with `null`.\n\t *\n\t * A bound sampler overrides the texture object's own parameters entirely,\n\t * which is what makes one texture usable at several variants at once.\n\t * @param {number} unit - the texture unit\n\t * @param {WebGLSampler|null} sampler - the sampler, or `null` to fall back\n\t * to the texture's own state\n\t * @ignore\n\t */\n\tbind(unit, sampler) {\n\t\tif (this.bound[unit] === sampler) {\n\t\t\treturn;\n\t\t}\n\t\tthis.bound[unit] = sampler;\n\t\tthis.gl.bindSampler(unit, sampler);\n\t}\n\n\t/**\n\t * Delete every sampler. Called on context loss — the GL objects died with\n\t * the context, so this only drops our side of the bookkeeping and lets the\n\t * next `get` mint fresh ones.\n\t * @param {boolean} [destroy=false] - whether to `deleteSampler` first\n\t * (orderly teardown; skip it for a lost context)\n\t * @ignore\n\t */\n\treleaseAll(destroy = false) {\n\t\tif (destroy === true) {\n\t\t\tfor (const sampler of this.samplers.values()) {\n\t\t\t\tthis.gl.deleteSampler(sampler);\n\t\t\t}\n\t\t}\n\t\tthis.samplers.clear();\n\t\t// every memo now points at a deleted or dead-context object\n\t\tthis.lastFilter = -1;\n\t\tthis.lastRepeat = null;\n\t\tthis.lastMipmap = null;\n\t\tthis.lastSampler = null;\n\t\tthis.bound.length = 0;\n\t}\n}\n","import { Color, colorPool } from \"./../../math/color.ts\";\nimport { Matrix3d } from \"../../math/matrix3d.ts\";\nimport { Bounds } from \"../../physics/bounds.ts\";\nimport { releaseShadowQuads } from \"../../renderable/groundshadow.js\";\nimport {\n\tCANVAS_ONRESIZE,\n\temit,\n\tGAME_RESET,\n\tONCONTEXT_LOST,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n\tRENDER_TARGET_CHANGED,\n} from \"../../system/event.ts\";\nimport RadialGradientEffect from \"../effects/radialGradient.js\";\nimport { Gradient } from \"../gradient.js\";\nimport Renderer from \"./../renderer.js\";\nimport RenderTargetPool from \"../rendertarget/render_target_pool.js\";\nimport WebGLRenderTarget from \"../rendertarget/webglrendertarget.js\";\nimport { createAtlas, TextureAtlas } from \"./../texture/atlas.js\";\nimport TextureCache from \"./../texture/cache.js\";\nimport { FrameTexture } from \"./../texture/frametexture.js\";\nimport { dashPath, dashSegments } from \"../utils/dash.js\";\nimport {\n\tgenerateJoinCircles,\n\tgenerateTriangleFan,\n} from \"../utils/tessellation.js\";\nimport { WebGLBatcher } from \"./batchers/batcher.js\";\nimport LitMeshBatcher from \"./batchers/lit_mesh_batcher\";\nimport LitQuadBatcher from \"./batchers/lit_quad_batcher\";\nimport MeshBatcher from \"./batchers/mesh_batcher\";\nimport PrimitiveBatcher from \"./batchers/primitive_batcher\";\nimport QuadBatcher from \"./batchers/quad_batcher\";\nimport { createLightUniformScratch, packLights } from \"./lighting/pack.ts\";\nimport OrthogonalTMXLayerGPURenderer from \"./renderers/tmxlayer/orthogonal.js\";\nimport { WebGLTextureStore } from \"./texture/store.js\";\nimport { resolveMaxTextures } from \"./utils/maxtextures.js\";\nimport { getMaxShaderPrecision } from \"./utils/precision.js\";\nimport { GLSamplerCache } from \"./utils/samplercache.js\";\n\n/**\n * additional import for TypeScript\n * @import {Rect} from \"./../../geometries/rectangle.ts\";\n * @import {RoundRect} from \"./../../geometries/roundrect.ts\";\n * @import {Polygon} from \"../../geometries/polygon.ts\";\n * @import {Line} from \"./../../geometries/line.ts\";\n * @import {Ellipse} from \"./../../geometries/ellipse.ts\";\n * @import {Matrix2d} from \"../../math/matrix2d.ts\";\n * @import {Matrix3d} from \"../../math/matrix3d.ts\";\n * @import {default as Texture2d} from \"../texture/texture2d.ts\";\n */\n\n// reusable constants for 2D→3D matrix operations\nconst _tempMatrix = new Matrix3d();\n\n// pre-allocated matrices for blitEffect (avoids per-frame allocation)\nconst _savedTransform = new Matrix3d();\nconst _savedProjection = new Matrix3d();\n\n/**\n * a WebGL renderer object\n * @category Rendering\n */\nexport default class WebGLRenderer extends Renderer {\n\t/**\n\t * @param {ApplicationSettings} [options] - optional parameters for the renderer\n\t */\n\tconstructor(options) {\n\t\t// parent contructor\n\t\tsuper(Object.assign(options, { context: \"webgl\" }));\n\n\t\t/**\n\t\t * The vendor string of the underlying graphics driver.\n\t\t * @type {string}\n\t\t * @default undefined\n\t\t * @readonly\n\t\t */\n\t\tthis.GPUVendor = undefined;\n\n\t\t/**\n\t\t * The renderer string of the underlying graphics driver.\n\t\t * @type {string}\n\t\t * @default undefined\n\t\t * @readonly\n\t\t */\n\t\tthis.GPURenderer = undefined;\n\n\t\t/**\n\t\t * The WebGL context\n\t\t * @name gl\n\t\t * @type {WebGLRenderingContext}\n\t\t */\n\t\tthis.gl = this.renderTarget.context;\n\n\t\t/**\n\t\t * cached FBO for post-effect processing (lazily created on first use)\n\t\t * @type {WebGLRenderTarget|null}\n\t\t * @ignore\n\t\t */\n\t\t// initialize the render target pool with a WebGL factory\n\t\t// capture targets rasterize the scene — under antiAlias they get a\n\t\t// multisampled render half resolved via blitFramebuffer, so MSAA\n\t\t// composes with post-effects instead of silently turning off the\n\t\t// moment a camera has one. Ping-pong intermediates stay 1×.\n\t\tconst msaaSamples =\n\t\t\tthis.settings.antiAlias === true\n\t\t\t\t? Math.min(4, this.gl.getParameter(this.gl.MAX_SAMPLES))\n\t\t\t\t: 0;\n\t\tthis._renderTargetPool = new RenderTargetPool((w, h, isCapture) => {\n\t\t\treturn new WebGLRenderTarget(this.gl, w, h, {\n\t\t\t\tsamples: isCapture === true ? msaaSamples : 0,\n\t\t\t});\n\t\t});\n\n\t\t/**\n\t\t * Saved projection matrix for begin/endPostEffect.\n\t\t * @type {Matrix3d}\n\t\t * @ignore\n\t\t */\n\t\t// projections saved by beginPostEffect, one per (possibly nested)\n\t\t// active pass — preallocated slots + a depth counter (zero-alloc\n\t\t// steady state), matching RenderTargetPool's base stack\n\t\tthis._effectProjectionStack = [];\n\t\tthis._effectPassDepth = 0;\n\n\t\t/**\n\t\t * sets or returns the thickness of lines for shape drawing\n\t\t * @type {number}\n\t\t * @default 1\n\t\t */\n\t\tthis.lineWidth = 1;\n\n\t\t/**\n\t\t * sets or returns the shape used to join two line segments where they meet.\n\t\t * Out of the three possible values for this property: \"round\", \"bevel\", and \"miter\", only \"round\" is supported for now in WebGL\n\t\t * @type {string}\n\t\t * @default \"round\"\n\t\t */\n\t\tthis.lineJoin = \"round\";\n\n\t\t/**\n\t\t * the vertex buffer used by this WebGL Renderer\n\t\t * @type {WebGLBuffer}\n\t\t */\n\t\tthis.vertexBuffer = this.gl.createBuffer();\n\n\t\t/**\n\t\t * Sampler objects, deduplicated by state. GL bakes wrap/filter into the\n\t\t * texture object; a bound sampler overrides that, so one texture can\n\t\t * serve several variants at once — which is what lets residency be\n\t\t * keyed by source alone. Renderer-owned so every batcher shares it.\n\t\t * @type {GLSamplerCache}\n\t\t * @ignore\n\t\t */\n\t\tthis.samplerCache = new GLSamplerCache(this.gl);\n\n\t\t/**\n\t\t * Texture residency, keyed by SOURCE. The GL handle used to be\n\t\t * reachable only through a batcher's per-unit array, so dropping a unit\n\t\t * assignment destroyed the texture and the next draw rebuilt it from\n\t\t * scratch — a full re-upload and mip regeneration per quad once past\n\t\t * the batching limit. Owning it here, per renderer, means a texture\n\t\t * that merely moves units costs a bind.\n\t\t *\n\t\t * Renderer-owned rather than batcher-owned on purpose: `init()` re-runs\n\t\t * on context restore, so a batcher-owned store would be REPLACED there,\n\t\t * orphaning every handle it tracked and leaking them on the next loss.\n\t\t * @type {TextureStore}\n\t\t * @ignore\n\t\t */\n\t\tthis.textureStore = new WebGLTextureStore(this.gl);\n\n\t\t/**\n\t\t * Maximum number of texture unit supported under the current context\n\t\t * @type {number}\n\t\t * @readonly\n\t\t */\n\t\tthis.maxTextures = resolveMaxTextures(\n\t\t\tthis.gl.getParameter(this.gl.MAX_TEXTURE_IMAGE_UNITS),\n\t\t\tthis.settings.maxTextures,\n\t\t);\n\t\t/**\n\t\t * Next free indexed `UNIFORM_BUFFER` binding point, handed out by\n\t\t * {@link WebGLRenderer#reserveUniformBindingPoint}. Binding points are\n\t\t * a context-wide namespace shared by every uniform block, so they are\n\t\t * allocated here rather than picked as literals by whoever needs one —\n\t\t * the same reason texture units go through `TextureCache.reserveUnit`.\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis._nextUniformBindingPoint = 0;\n\n\t\t/**\n\t\t * the default shader precision based on application settings\n\t\t * @type {string}\n\t\t * @ignore\n\t\t */\n\t\tthis.shaderPrecision = getMaxShaderPrecision(\n\t\t\tthis.gl,\n\t\t\tthis.settings.highPrecisionShader !== false,\n\t\t);\n\n\t\t/**\n\t\t * reusable scratch array for fillRect (2 triangles = 6 vertices)\n\t\t * @ignore\n\t\t */\n\t\tthis._rectTriangles = Array.from({ length: 6 }, () => {\n\t\t\treturn { x: 0, y: 0 };\n\t\t});\n\n\t\t// scratch AABB reused by `clipRect` for screen-space corner\n\t\t// transforms, kept on the instance so the call doesn't allocate.\n\t\tthis._clipAABB = new Bounds();\n\n\t\t// scratch array for fillPolygon to avoid mutating polygon points\n\t\tthis._polyVerts = [];\n\n\t\t// current gradient state (null when using solid color)\n\t\tthis._currentGradient = null;\n\n\t\t// the stencil value of currently-VISIBLE pixels under the innermost\n\t\t// active mask, as installed by setMask (0 for an inverted mask,\n\t\t// maskLevel otherwise) — the single source #gradientMask gates and\n\t\t// restores against\n\t\tthis._maskVisibleRef = 0;\n\n\t\t/**\n\t\t * The current transformation matrix used for transformations on the overall scene\n\t\t * (alias to renderState.currentTransform for backward compatibility)\n\t\t * @type {Matrix3d}\n\t\t */\n\t\tthis.currentTransform = this.renderState.currentTransform;\n\n\t\t/**\n\t\t * The current batcher used by the renderer\n\t\t * @type {WebGLBatcher}\n\t\t */\n\t\tthis.currentBatcher = undefined;\n\n\t\t/**\n\t\t * a reference to the current shader program used by the renderer\n\t\t * @type {WebGLProgram}\n\t\t */\n\t\tthis.currentProgram = undefined;\n\n\t\t/**\n\t\t * the GL texture unit currently active (`gl.activeTexture`) — global\n\t\t * GL state, so tracked once per renderer and shared by every batcher\n\t\t * through the `MaterialBatcher.currentTextureUnit` accessor. `-1`\n\t\t * forces the next bind to re-issue `gl.activeTexture`.\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis._activeTextureUnit = -1;\n\n\t\t/**\n\t\t * The list of active batchers\n\t\t * @type {Map<WebGLBatcher>}\n\t\t */\n\t\tthis.batchers = new Map();\n\n\t\t// (no raw vertex-buffer bind here — each batcher's vertex state\n\t\t// captures its buffers at init, and bind() binds the upload target)\n\n\t\t// Create the default batchers. The lit-aware quad batcher is only\n\t\t// created when no custom batcher override is supplied — its lit\n\t\t// fragment path needs the paired (color, normal) sampler layout.\n\t\tconst CustomBatcher = this.settings.batcher || this.settings.compositor;\n\t\tthis.addBatcher(new (CustomBatcher || QuadBatcher)(this), \"quad\", true);\n\t\tif (!CustomBatcher) {\n\t\t\tthis.addBatcher(new LitQuadBatcher(this), \"litQuad\");\n\t\t}\n\t\tthis.addBatcher(new (CustomBatcher || PrimitiveBatcher)(this), \"primitive\");\n\t\tthis.addBatcher(new MeshBatcher(this), \"mesh\");\n\t\tthis.addBatcher(new LitMeshBatcher(this), \"litMesh\");\n\n\t\t// default WebGL state(s)\n\t\t// depth testing disabled for 2D (painter's algorithm handles z-ordering).\n\t\t// `MeshBatcher.bind()` enables it temporarily for 3D mesh rendering.\n\t\tthis.gl.disable(this.gl.DEPTH_TEST);\n\t\tthis.gl.depthMask(false);\n\n\t\tthis.gl.disable(this.gl.SCISSOR_TEST);\n\t\tthis._scissorActive = false;\n\t\tthis.gl.enable(this.gl.BLEND);\n\n\t\t// set default mode\n\t\tthis.setBlendMode(this.settings.blendMode);\n\n\t\t// get GPU vendor and renderer\n\t\tconst debugInfo = this.gl.getExtension(\"WEBGL_debug_renderer_info\");\n\t\tif (debugInfo !== null) {\n\t\t\tthis.GPUVendor = this.gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);\n\t\t\tthis.GPURenderer = this.gl.getParameter(\n\t\t\t\tdebugInfo.UNMASKED_RENDERER_WEBGL,\n\t\t\t);\n\t\t}\n\n\t\t// customShader is declared on the base Renderer class\n\n\t\t// Create a texture cache\n\t\tthis.cache = new TextureCache(this, this.maxTextures);\n\n\t\t// set the renderer type\n\t\tthis.type = \"WebGL2\";\n\n\t\t// this backend renders TMX tile layers on the GPU (see TMXLayer)\n\t\tthis.supportsShaderTileLayers = true;\n\n\t\t// this backend keeps mesh geometry resident on the GPU, so meshes can\n\t\t// hand it their model matrix instead of pre-transformed vertices\n\t\tthis.supportsRetainedMesh = true;\n\n\t\t// drawElementsInstanced is WebGL 2 core\n\t\tthis.supportsInstancing = true;\n\n\t\t// GLSL, not \"shaders exist\" — `ShaderEffect` and the loader's\n\t\t// `{vertex, fragment}` assets hand their source straight to the\n\t\t// driver, so the language is the thing they have to agree on\n\t\t/** @type {\"glsl\"|\"wgsl\"|null} */\n\t\tthis.shaderLanguage = \"glsl\";\n\n\t\t// depth buffer + 3D projection + drawMesh, i.e. everything a\n\t\t// depth-sorted Camera3d scene needs\n\t\tthis.supportsDepthBuffer = true;\n\n\t\t// to simulate context lost and restore in WebGL:\n\t\t// let ctx = me.video.renderer.context.getExtension('WEBGL_lose_context');\n\t\t// ctx.loseContext()\n\t\tthis.getCanvas().addEventListener(\n\t\t\t\"webglcontextlost\",\n\t\t\t(e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tthis.isContextValid = false;\n\t\t\t\t// driver auto-frees lost resources; just drop the ref\n\t\t\t\tthis.vertexBuffer = null;\n\t\t\t\temit(ONCONTEXT_LOST, this);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t\tthis.getCanvas().addEventListener(\n\t\t\t\"webglcontextrestored\",\n\t\t\t() => {\n\t\t\t\t// restore renderer-owned GL state before downstream subscribers run\n\t\t\t\t// (no raw bind needed — reset() below re-inits every batcher,\n\t\t\t\t// rebuilding their vertex states against this new buffer)\n\t\t\t\tthis.vertexBuffer = this.gl.createBuffer();\n\n\t\t\t\t// driver wipes all GL state on restore; re-apply ours\n\t\t\t\tthis.gl.disable(this.gl.DEPTH_TEST);\n\t\t\t\tthis.gl.depthMask(false);\n\t\t\t\tthis.gl.disable(this.gl.SCISSOR_TEST);\n\t\t\t\tthis._scissorActive = false;\n\t\t\t\tthis.gl.enable(this.gl.BLEND);\n\t\t\t\t// invalidate the blend cache: blendFunc/blendEquation are back\n\t\t\t\t// at driver defaults, so a matching cached mode would otherwise\n\t\t\t\t// short-circuit setBlendMode and never re-apply them\n\t\t\t\tthis.currentBlendMode = undefined;\n\t\t\t\tthis.setBlendMode(this.settings.blendMode);\n\n\t\t\t\t// reset() re-inits batchers, FBO pool, light shader, TMX renderer\n\t\t\t\tthis.reset();\n\n\t\t\t\t// stale per-source unit assignments — force re-upload on next draw\n\t\t\t\tthis.cache.units.clear();\n\t\t\t\tthis.cache.usedUnits.clear();\n\t\t\t\t// the samplers and textures died with the context; drop our side\n\t\t\t\t// so the next resolve mints fresh ones against the restored one.\n\t\t\t\t// CLEARED, never replaced: rebuilding these would orphan every\n\t\t\t\t// handle they tracked and leak it all on the next loss.\n\t\t\t\tthis.samplerCache.releaseAll();\n\t\t\t\tthis.textureStore.releaseAll();\n\n\t\t\t\t// the restored context is back at TEXTURE0 — invalidate the\n\t\t\t\t// shared active-unit tracking so the next bind re-issues it\n\t\t\t\tthis._activeTextureUnit = -1;\n\n\t\t\t\tthis.isContextValid = true;\n\t\t\t\temit(ONCONTEXT_RESTORED, this);\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\n\t\t// Held as bound fields rather than inline arrows so `destroy()` can\n\t\t// actually unregister them. An anonymous handler cannot be passed to\n\t\t// `off()`, so every torn-down renderer used to leave its listeners on\n\t\t// the bus forever — and each closure pins the renderer, its batchers\n\t\t// and its GL objects against garbage collection, so releasing the GL\n\t\t// context alone was not enough to let the canvas go.\n\t\tthis.onGameReset = () => {\n\t\t\tthis.reset();\n\t\t};\n\t\tthis.onContextRestoredInvalidate = (renderer) => {\n\t\t\tif (renderer === this) {\n\t\t\t\tthis.currentProgram = undefined;\n\t\t\t}\n\t\t};\n\t\tthis.onCanvasResize = (width, height) => {\n\t\t\tthis.flush();\n\t\t\tthis.setViewport(0, 0, width, height);\n\t\t\t// FBOs are lazily resized in beginPostEffect via get() → resize()\n\t\t};\n\n\t\t// reset the renderer on game reset\n\t\ton(GAME_RESET, this.onGameReset);\n\n\t\t// Every live GLShader recompiles on this event, and each recompile\n\t\t// binds its own program to replay its uniform snapshot — so the\n\t\t// REAL current program afterwards is whichever shader recompiled\n\t\t// last, while this cache still names whatever drew before the\n\t\t// event. Invalidate it (order-immune: whatever is really bound,\n\t\t// the next flush's syncProgram re-issues useProgram) instead of\n\t\t// trusting a stale match and drawing with a foreign program.\n\t\t// Latent for years; surfaced when the Light3dBlock outgrew the\n\t\t// Light2dBlock — a foreign mesh-lit program parks its block at\n\t\t// default binding point 0, whose 2D buffer is now too small, and a\n\t\t// plain sprite flush dies with INVALID_OPERATION.\n\t\ton(ONCONTEXT_RESTORED, this.onContextRestoredInvalidate);\n\n\t\t// register to the CANVAS resize channel\n\t\ton(CANVAS_ONRESIZE, this.onCanvasResize);\n\t}\n\n\t/**\n\t * The WebGL version used by this renderer.\n\t * @type {number}\n\t * @default 2\n\t * @deprecated since 20.0.0 — the WebGL renderer is WebGL 2 only, this is always `2`\n\t */\n\tget WebGLVersion() {\n\t\treturn 2;\n\t}\n\n\t/**\n\t * return the list of supported compressed texture formats\n\t * @return {Object}\n\t */\n\tgetSupportedCompressedTextureFormats() {\n\t\t// per-instance (the WebGPU backend's convention): a module-level memo\n\t\t// would be shared across coexisting renderer instances and survive a\n\t\t// destroyed context — divergence-by-drift, even if format support\n\t\t// rarely differs in practice\n\t\tif (typeof this._compressedTextureFormats === \"undefined\") {\n\t\t\tconst gl = this.gl;\n\t\t\tif (typeof gl === \"undefined\" || gl === null) {\n\t\t\t\t// WebGL context not available\n\t\t\t\treturn super.getSupportedCompressedTextureFormats();\n\t\t\t}\n\t\t\tthis._compressedTextureFormats = {\n\t\t\t\tastc:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_astc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_astc\"),\n\t\t\t\tbptc:\n\t\t\t\t\tgl.getExtension(\"EXT_texture_compression_bptc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_EXT_texture_compression_bptc\"),\n\t\t\t\ts3tc:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_s3tc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_s3tc\"),\n\t\t\t\ts3tc_srgb:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_s3tc_srgb\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_s3tc_srgb\"),\n\t\t\t\tpvrtc:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_pvrtc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_pvrtc\"),\n\t\t\t\tetc1:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_etc1\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_etc1\"),\n\t\t\t\tetc2:\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_etc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBKIT_WEBGL_compressed_texture_etc\") ||\n\t\t\t\t\tgl.getExtension(\"WEBGL_compressed_texture_es3_0\"),\n\t\t\t};\n\t\t\t// ETC2 is a superset of ETC1 — if we have ETC2 but not ETC1,\n\t\t\t// synthesize ETC1 support so that PKM/KTX ETC1 textures work\n\t\t\tif (\n\t\t\t\t!this._compressedTextureFormats.etc1 &&\n\t\t\t\tthis._compressedTextureFormats.etc2\n\t\t\t) {\n\t\t\t\tthis._compressedTextureFormats.etc1 = {\n\t\t\t\t\tCOMPRESSED_RGB_ETC1_WEBGL: 0x8d64,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn this._compressedTextureFormats;\n\t}\n\n\t/**\n\t * return true if the given compressed texture format is supported\n\t * @param {Number} format\n\t * @returns\n\t */\n\thasSupportedCompressedFormats(format) {\n\t\tconst supportedFormats = this.getSupportedCompressedTextureFormats();\n\t\tfor (const supportedFormat in supportedFormats) {\n\t\t\tfor (const extension in supportedFormats[supportedFormat]) {\n\t\t\t\tif (format === supportedFormats[supportedFormat][extension]) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Reset context state\n\t */\n\t/**\n\t * Tear down this renderer and free GPU/event resources. Walks every\n\t * registered batcher's `destroy()` so cross-renderer subscriptions\n\t * (`GPU_TEXTURE_CACHE_RESET` on `MaterialBatcher`, etc.) don't\n\t * leak across `Application.destroy()` cycles. Safe to call multiple\n\t * times — subsequent calls are no-ops.\n\t */\n\t/**\n\t * Release any GPU geometry retained for the given mesh. Called when a mesh\n\t * is destroyed; harmless if it never had any.\n\t * @param {object} mesh - the mesh whose geometry should be freed\n\t */\n\tdeleteMeshGeometry(mesh) {\n\t\tthis.batchers?.forEach((batcher) => {\n\t\t\tbatcher.releaseRetained?.(mesh);\n\t\t});\n\t}\n\n\tdestroy() {\n\t\t// Unregister first: every handler closes over `this`, so leaving them\n\t\t// on the bus keeps the renderer (and transitively its batchers and GL\n\t\t// objects) reachable forever — a destroyed renderer would also still\n\t\t// react to GAME_RESET and CANVAS_ONRESIZE. Matches what the WebGPU\n\t\t// backend already does.\n\t\toff(GAME_RESET, this.onGameReset);\n\t\toff(ONCONTEXT_RESTORED, this.onContextRestoredInvalidate);\n\t\toff(CANVAS_ONRESIZE, this.onCanvasResize);\n\n\t\t// the shared ground-shadow quads (#1515) hold retained GPU geometry\n\t\t// keyed off this renderer's batchers — released before those go\n\t\treleaseShadowQuads(this);\n\t\tif (this.batchers) {\n\t\t\tthis.batchers.forEach((batcher) => {\n\t\t\t\tbatcher.destroy?.();\n\t\t\t});\n\t\t\tthis.batchers.clear();\n\t\t}\n\t\t// the shared vertex buffer is renderer-owned (batchers only\n\t\t// reference it), so it is released here rather than per batcher\n\t\tif (this.vertexBuffer) {\n\t\t\tthis.gl.deleteBuffer(this.vertexBuffer);\n\t\t\tthis.vertexBuffer = null;\n\t\t}\n\n\t\t// Release the GL context itself. Dropping every GL object above and\n\t\t// removing the canvas from the DOM does NOT do this: a canvas keeps\n\t\t// its context until the canvas is garbage-collected, which is\n\t\t// non-deterministic and routinely delayed. Browsers cap how many live\n\t\t// contexts they keep (Chromium ~16) and force-lose the oldest past\n\t\t// that, so a long-lived page that creates and tears down several\n\t\t// applications — an SPA moving between scenes, or a test session\n\t\t// sharing one page across spec files — accumulates dead-but-unfreed\n\t\t// contexts until an unrelated later `getContext` stalls or fails.\n\t\t//\n\t\t// `WEBGL_lose_context` is the only way to hand a context back\n\t\t// deterministically. `destroy()` is terminal (`Application.destroy`\n\t\t// refuses a subsequent `init`), so losing it here forecloses nothing.\n\t\t// The extension is absent on some drivers, hence the optional call.\n\t\tif (this.isContextValid !== false) {\n\t\t\tthis.gl.getExtension(\"WEBGL_lose_context\")?.loseContext();\n\t\t}\n\t}\n\n\treset() {\n\t\tsuper.reset();\n\n\t\t// drop any pass state orphaned by a mid-pass exception — a stale\n\t\t// depth would misalign every later pass and inflate the pool's\n\t\t// nesting forever (the pool's own stack is cleared via its\n\t\t// destroy() below; the preallocated slots are kept for reuse)\n\t\tthis._effectPassDepth = 0;\n\n\t\t// clear gl context\n\t\tthis.clear();\n\n\t\t// drop every per-layer GPU tilemap texture — tile layers churn on\n\t\t// game reset and we'd otherwise leak a WebGLTexture per layer\n\t\t// across each level transition\n\t\tthis._orthogonalTMXGPURenderer?.reset();\n\n\t\t// initial viewport size\n\t\tthis.setViewport();\n\n\t\tthis.currentBatcher = undefined;\n\t\tthis.currentProgram = undefined;\n\t\tthis.customShader = undefined;\n\n\t\tthis.batchers.forEach((batcher) => {\n\t\t\tif (this.isContextValid === false) {\n\t\t\t\t// on context lost/restore\n\t\t\t\tbatcher.init(this);\n\t\t\t} else {\n\t\t\t\tbatcher.reset();\n\t\t\t}\n\t\t});\n\n\t\tthis.setBatcher(\"quad\");\n\n\t\tthis.gl.disable(this.gl.SCISSOR_TEST);\n\t\tthis._scissorActive = false;\n\n\t\t// release post-process FBOs (will be recreated on demand)\n\t\tthis._renderTargetPool.destroy();\n\n\t\t// drop the lazily-cached drawLight resources — after a context\n\t\t// loss/restore the cached shader program and white-pixel atlas\n\t\t// reference the OLD GL context and would error if reused.\n\t\t// Lazy re-init happens on the next drawLight call.\n\t\tif (this._lightShader !== undefined) {\n\t\t\tthis._lightShader.destroy?.();\n\t\t\tthis._lightShader = undefined;\n\t\t}\n\t\tif (this._lightAtlas !== undefined) {\n\t\t\t// `TextureCache` is keyed by the source image, not by the\n\t\t\t// `TextureAtlas` instance. Dropping the wrong key would leak\n\t\t\t// the entry (so a context lost / restore would later resolve\n\t\t\t// the canvas to a `TextureAtlas` whose internal GL texture\n\t\t\t// reference is invalid). Iterate the atlas's sources to drop\n\t\t\t// every cached entry it registered in `cache.set(source, this)`.\n\t\t\tthis._lightAtlas.sources.forEach((source) => {\n\t\t\t\tthis.cache.delete?.(source);\n\t\t\t});\n\t\t\tthis._lightAtlas = undefined;\n\t\t}\n\n\t\t// the shared frame-capture texture (toFrameTexture) holds a GL texture\n\t\t// tied to this context — drop it so the next capture reallocates. Safe\n\t\t// on a still-valid context too (a resize/reset just re-creates it).\n\t\tif (typeof this._frameTexture !== \"undefined\") {\n\t\t\tthis._frameTexture.destroy();\n\t\t\tthis._frameTexture = undefined;\n\t\t}\n\n\t\t// Context-loss-only cleanup for the TMX GPU renderer: the cached\n\t\t// `GLShader` and per-layer GL textures reference the OLD context\n\t\t// and are invalid. On a regular `GAME_RESET` (context still\n\t\t// valid) we already dropped per-layer textures via `.reset()`\n\t\t// above and keep the renderer instance so its compiled shader\n\t\t// program survives across level transitions instead of leaking a\n\t\t// `WebGLProgram` per reset and re-paying the compile cost.\n\t\tif (this.isContextValid === false) {\n\t\t\tthis._orthogonalTMXGPURenderer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Draw a TMX tile layer through whichever path the layer's `renderMode`\n\t * resolves to. WebGL2-eligible layers (`renderMode === \"shader\"`) take\n\t * the procedural shader path — one quad per tileset, GID lookup in a\n\t * per-layer data texture. All other layers fall through to the base\n\t * `Renderer.drawTileLayer` (preRender blit or per-tile loop).\n\t * @param {object} layer - the TMXLayer to draw\n\t * @param {object} rect - the visible region in world coords\n\t */\n\tdrawTileLayer(layer, rect) {\n\t\tif (layer.renderMode === \"shader\") {\n\t\t\tconst gpu = this._getTMXGPURendererFor(layer.orientation);\n\t\t\tif (gpu !== undefined) {\n\t\t\t\tgpu.draw(layer, rect);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tsuper.drawTileLayer(layer, rect);\n\t}\n\n\t/**\n\t * Lazy-init the orientation-specific GPU tilemap renderer.\n\t * @param {string} orientation\n\t * @returns {object|undefined}\n\t * @ignore\n\t */\n\t_getTMXGPURendererFor(orientation) {\n\t\tif (orientation === \"orthogonal\") {\n\t\t\tif (this._orthogonalTMXGPURenderer === undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tthis._orthogonalTMXGPURenderer = new OrthogonalTMXLayerGPURenderer(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// shader compile / link failure on this driver — disable\n\t\t\t\t\t// the GPU path permanently for the rest of the session\n\t\t\t\t\t// (the next `drawTileLayer` call falls through to the\n\t\t\t\t\t// legacy renderer). Stored as `null` so subsequent\n\t\t\t\t\t// attempts short-circuit without re-trying the failing\n\t\t\t\t\t// compile every frame.\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"melonJS: GPU tilemap shader failed to compile, falling back to legacy renderer\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t);\n\t\t\t\t\tthis._orthogonalTMXGPURenderer = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this._orthogonalTMXGPURenderer || undefined;\n\t\t}\n\t\t// isometric / staggered / hexagonal: phase 2\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * add a new batcher to this renderer\n\t * @param {WebGLBatcher} batcher - a batcher instance (must extend WebGLBatcher)\n\t * @param {string} name - a name uniquely identifying this batcher\n\t * @param {boolean} [activate=false] - true if the given batcher should be set as the active one\n\t */\n\taddBatcher(batcher, name = \"default\", activate = false) {\n\t\t// gate on the BACKEND base, not the neutral one: a neutral or\n\t\t// WebGPU batcher has none of the GL machinery (defaultShader,\n\t\t// setProjection, …) and would fail mid-activation instead\n\t\tif (!(batcher instanceof WebGLBatcher)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"addBatcher: batcher must be a WebGLBatcher subclass (custom WebGL batchers extend WebGLBatcher)\",\n\t\t\t);\n\t\t}\n\t\tif (typeof this.batchers.get(name) !== \"undefined\") {\n\t\t\tthrow new Error(\"Invalid Batcher name\");\n\t\t}\n\n\t\tthis.batchers.set(name, batcher);\n\n\t\tif (activate === true) {\n\t\t\tthis.setBatcher(name);\n\t\t}\n\t}\n\n\t/**\n\t * set the active batcher for this renderer\n\t * @param {string} name - a batcher name\n\t * @param {GLShader} [shader] - an optional shader program to be used, instead of the default one, when activating the batcher\n\t * @returns {WebGLBatcher} an instance to the current active batcher\n\t */\n\tsetBatcher(name = \"default\", shader) {\n\t\tconst batcher = this.batchers.get(name);\n\n\t\tif (typeof batcher === \"undefined\") {\n\t\t\tthrow new Error(\"Invalid Batcher\");\n\t\t}\n\n\t\t// resolve the target shader — the explicitly-passed one if any,\n\t\t// otherwise the batcher's default. We always reconcile the\n\t\t// currentShader to this target so a custom shader left bound by a\n\t\t// prior call (e.g. `drawLight` parking the radial-gradient\n\t\t// program) gets evicted before the next sprite batch flushes.\n\t\t// `shader != null` excludes both `null` and `undefined`\n\t\t// (`typeof null === \"object\"` would otherwise let null through).\n\t\tconst targetShader = shader != null ? shader : batcher.defaultShader;\n\n\t\tif (\n\t\t\tthis.currentBatcher === batcher &&\n\t\t\tbatcher.currentShader === targetShader\n\t\t) {\n\t\t\t// fast path: same batcher, same shader — nothing to do.\n\t\t\treturn this.currentBatcher;\n\t\t}\n\n\t\tif (this.currentBatcher !== batcher) {\n\t\t\t// Leaving mesh mode drains the deferred ground-shadow queue first,\n\t\t\t// so the blobs land on top of every opaque mesh in the pass (#1515).\n\t\t\t// Switching *within* mesh mode (lit ↔ unlit) must NOT drain it —\n\t\t\t// the meshes still to come are exactly what the shadows have to\n\t\t\t// beat.\n\t\t\tif (name !== \"mesh\" && name !== \"litMesh\" && this._canDrainShadows()) {\n\t\t\t\tthis.flushGroundShadows();\n\t\t\t}\n\t\t\tif (this.currentBatcher !== undefined) {\n\t\t\t\t// flush the current batcher, then let it tear down any\n\t\t\t\t// state it set up at `bind()` time (Mesh batcher restores\n\t\t\t\t// non-mesh blend/depth defaults here, for example)\n\t\t\t\tthis.currentBatcher.flush();\n\t\t\t\tthis.currentBatcher.unbind();\n\t\t\t}\n\t\t\tthis.currentBatcher = batcher;\n\t\t\t// `bind()` is where each batcher sets up its own GL state\n\t\t\t// (vertex attributes, plus mode-specific state like depth /\n\t\t\t// blend for `MeshBatcher`). Renderer doesn't need to know.\n\t\t\tthis.currentBatcher.bind();\n\t\t\t// sync the projection matrix to the new batcher\n\t\t\tthis.currentBatcher.setProjection(this.projectionMatrix);\n\t\t}\n\n\t\t// useShader() is internally a no-op when the shader is already\n\t\t// bound; it flushes and rebinds otherwise. Pending vertices that\n\t\t// were queued under the prior shader get drained against that\n\t\t// shader before the switch, which is exactly what we want.\n\t\tthis.currentBatcher.useShader(targetShader);\n\n\t\treturn this.currentBatcher;\n\t}\n\n\t/**\n\t * Whether the device state right now is the scene's, so a deferred\n\t * ground-shadow drain (#1515) would actually land where it is meant to.\n\t *\n\t * A batcher transition is normally the end of the mesh pass — but not\n\t * every one is. `setMask` fills its shape through the primitive batcher\n\t * with **colour writes off and `stencilOp(…, INCR)` armed**, so draining\n\t * there both discards every blob and stamps their footprints into the mask\n\t * being built. A transition inside a post-effect bracket is bound to the\n\t * child's FBO, so the blobs would be captured into that effect chain and\n\t * lost from the world. In either case the queue simply waits: the camera\n\t * drains it at the end of the world walk, which is the point that is always\n\t * correct.\n\t * @returns {boolean} true when a drain is safe here\n\t * @ignore\n\t */\n\t_canDrainShadows() {\n\t\treturn this.maskLevel === 0 && this._effectPassDepth === 0;\n\t}\n\n\t/**\n\t * Reset the gl transform to identity\n\t */\n\tresetTransform() {\n\t\tthis.currentTransform.identity();\n\t}\n\n\t/**\n\t * Create a pattern with the specified repetition\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - Source image to be used as the pattern's image\n\t * @param {string} [repeat=\"no-repeat\"] - Define how the pattern should be repeated. One of `\"repeat\"` / `\"repeat-x\"` / `\"repeat-y\"` / `\"no-repeat\"`.\n\t * @returns {TextureAtlas} the patterned texture created\n\t * @see ImageLayer#repeat\n\t * @example\n\t * let tileable   = renderer.createPattern(image, \"repeat\");\n\t * let horizontal = renderer.createPattern(image, \"repeat-x\");\n\t * let vertical   = renderer.createPattern(image, \"repeat-y\");\n\t * let basic      = renderer.createPattern(image, \"no-repeat\");\n\t */\n\tcreatePattern(image, repeat = \"no-repeat\") {\n\t\tthis.setBatcher(\"quad\");\n\n\t\t// No band-aid texture cleanup here anymore — the cache's\n\t\t// `(source, repeat)` unit keying (see `TextureCache.getUnit` /\n\t\t// `peekUnit` / `freeTextureUnit`, which inline the `(source,\n\t\t// repeat)` lookup) already separates this call from any\n\t\t// previous createPattern over the same image with a different\n\t\t// repeat mode. A repeat match against an existing call reuses\n\t\t// the same unit, and `uploadTexture`'s `boundTextures[unit]`\n\t\t// check short-circuits a redundant GL upload. Fixes #1448 (the\n\t\t// prior delete-and-replace trampled the still-live pattern\n\t\t// returned by the earlier call).\n\t\tconst texture = new TextureAtlas(\n\t\t\tcreateAtlas(image.width, image.height, \"pattern\", repeat),\n\t\t\timage,\n\t\t);\n\n\t\tthis.currentBatcher.uploadTexture(texture);\n\n\t\treturn texture;\n\t}\n\n\t/**\n\t * Flush the batcher to the frame buffer\n\t */\n\tflush() {\n\t\tthis.currentBatcher.flush();\n\t}\n\n\t/**\n\t * Upload per-frame Light2d uniforms used by the lit sprite pipeline.\n\t *\n\t * Packs the active lights into pre-allocated scratch buffers, then\n\t * forwards to `LitQuadBatcher`. Light positions are translated from\n\t * world-space (where `light.getBounds().centerX/Y` lives) into the\n\t * renderer's pre-projection coords by subtracting `(translateX, translateY)`,\n\t * matching what `Stage.drawLighting` does for the cutout pass — so\n\t * the lit fragment's `lightPos - vWorldPos` math lines up with the\n\t * camera's view.\n\t *\n\t * Lights past `MAX_LIGHTS` are silently dropped. Also caches the\n\t * active light count on the renderer so `drawImage` can dispatch\n\t * normal-mapped sprites to the lit batcher only when there's\n\t * something to light them with.\n\t * @param {Iterable<object>} [lights] - active `Light2d` instances; falsy/empty no-ops the lit pipeline\n\t * @param {object} [ambient] - ambient lighting color (0..255 RGB); defaults to black\n\t * @param {number} [translateX=0] - world-to-screen X translate (matches `Camera2d.draw()`)\n\t * @param {number} [translateY=0] - world-to-screen Y translate\n\t */\n\tsetLightUniforms(lights, ambient, translateX = 0, translateY = 0) {\n\t\t// scratch is allocated lazily on first call so non-lit scenes\n\t\t// don't pay for it\n\t\tif (this._lightUniformsScratch === undefined) {\n\t\t\tthis._lightUniformsScratch = createLightUniformScratch();\n\t\t}\n\t\tconst u = packLights(\n\t\t\tlights,\n\t\t\tambient,\n\t\t\ttranslateX,\n\t\t\ttranslateY,\n\t\t\tthis._lightUniformsScratch,\n\t\t);\n\n\t\tthis.activeLightCount = u.count;\n\t\tconst lit = this.batchers.get(\"litQuad\");\n\t\tif (lit && typeof lit.setLightUniforms === \"function\") {\n\t\t\t// A uniform-buffer upload does not touch `useProgram`, so this no\n\t\t\t// longer disturbs the active shader. It used to: the light data\n\t\t\t// went through `GLShader.setUniform`, which binds litQuad's program\n\t\t\t// to write, and litQuad is rarely the *active* batcher — leaving it\n\t\t\t// bound fed 4-attribute vertex data to a 5-attribute shader.\n\t\t\tlit.setLightUniforms(u);\n\t\t}\n\t}\n\n\t/**\n\t * Claim the next indexed `UNIFORM_BUFFER` binding point.\n\t *\n\t * Binding points are context-wide: two uniform blocks sharing one silently\n\t * overwrite each other, and the loser reads the winner's bytes as its own.\n\t * Allocating them from here rather than hard-coding an integer per feature\n\t * makes that collision impossible, and mirrors how texture units are\n\t * claimed through {@link TextureCache#reserveUnit}.\n\t *\n\t * Callers hold their point for the life of the batcher — a context restore\n\t * re-runs `init()`, which must reuse the point it already has rather than\n\t * claim another, or a long-lived session would leak through the budget.\n\t * @returns {number} the claimed binding point\n\t * @throws {Error} when the device's binding points are exhausted\n\t * @ignore\n\t */\n\treserveUniformBindingPoint() {\n\t\tconst max = this.gl.getParameter(this.gl.MAX_UNIFORM_BUFFER_BINDINGS);\n\t\tif (this._nextUniformBindingPoint >= max) {\n\t\t\tthrow new Error(\n\t\t\t\t`WebGLRenderer: no free uniform buffer binding point (device limit ${max})`,\n\t\t\t);\n\t\t}\n\t\treturn this._nextUniformBindingPoint++;\n\t}\n\n\t/**\n\t * @inheritdoc\n\t *\n\t * Renders the light as a quad through a shared\n\t * {@link RadialGradientEffect} fragment shader (procedural — no\n\t * per-light texture). The shader and a shared 1×1 white-pixel atlas\n\t * are lazy-allocated on first call and reused for every Light2d on\n\t * this renderer. Each light's color and intensity are encoded into\n\t * the per-vertex tint so consecutive `drawLight` calls accumulate\n\t * into the quad batcher's buffer and flush together — N lights\n\t * become 1 program switch + 1 flush instead of 2N + N.\n\t * @param {object} light - the Light2d instance to render\n\t */\n\tdrawLight(light) {\n\t\tif (this._lightShader === undefined) {\n\t\t\tthis._lightShader = new RadialGradientEffect(this);\n\t\t}\n\t\t// `setBatcher(\"quad\", _lightShader)` switches the quad batcher's\n\t\t// shader to the radial gradient if it isn't already bound (and\n\t\t// flushes any sprite vertices queued under the previous shader).\n\t\t// On subsequent back-to-back `drawLight` calls this is a no-op,\n\t\t// so the lights pile into the same vertex buffer.\n\t\tconst batcher = this.setBatcher(\"quad\", this._lightShader);\n\t\tbatcher.addQuad(\n\t\t\tthis._getLightAtlas(),\n\t\t\tlight.pos.x,\n\t\t\tlight.pos.y,\n\t\t\tlight.width,\n\t\t\tlight.height,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t\t1,\n\t\t\t// pack the light's color (RGB) and intensity (A) into the\n\t\t\t// vertex tint — the shader's `apply()` reads `color.rgb` and\n\t\t\t// `color.a` as the per-light values.\n\t\t\tlight.color.toUint32(light.intensity),\n\t\t);\n\t\t// Note: we deliberately do NOT switch back to the default shader\n\t\t// here. The next `setBatcher` call (sprites, primitives, etc.)\n\t\t// will reconcile to the right shader on its own (see\n\t\t// `setBatcher`), and that's what unlocks the cross-light batch.\n\t}\n\n\t/**\n\t * Lazy-init a shared 1×1 white `TextureAtlas` used as the source\n\t * texture for `drawLight`'s procedural shader. The shader ignores\n\t * the sampled color, but `addQuad`'s vertex format includes a\n\t * texture-unit attribute so we still need a real texture; sharing\n\t * one across every light keeps them on the same multi-texture slot\n\t * (no flush on light switch).\n\t * @returns {TextureAtlas}\n\t * @ignore\n\t */\n\t_getLightAtlas() {\n\t\tif (this._lightAtlas === undefined) {\n\t\t\t// build a 1×1 white canvas — TextureAtlas wants an image-like\n\t\t\t// source that can feed `gl.texImage2D` directly.\n\t\t\tconst canvas = globalThis.document\n\t\t\t\t? globalThis.document.createElement(\"canvas\")\n\t\t\t\t: new OffscreenCanvas(1, 1);\n\t\t\tcanvas.width = 1;\n\t\t\tcanvas.height = 1;\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\t\t\tctx.fillStyle = \"#fff\";\n\t\t\tctx.fillRect(0, 0, 1, 1);\n\t\t\tthis._lightAtlas = new TextureAtlas(\n\t\t\t\tcreateAtlas(1, 1, \"lightWhite\", \"no-repeat\"),\n\t\t\t\tcanvas,\n\t\t\t);\n\t\t}\n\t\treturn this._lightAtlas;\n\t}\n\n\t/**\n\t * Capture the current frame — everything drawn to the active framebuffer so\n\t * far — into a {@link Texture2d}, entirely on the GPU (a `copyTexImage2D` on\n\t * the first capture, then `copyTexSubImage2D` to refresh in place; no\n\t * `readPixels` round-trip or pipeline stall). The fourth member of the\n\t * {@link Renderer#toDataURL} / {@link Renderer#toBlob} /\n\t * {@link Renderer#toImageBitmap} family — \"the current frame as X\" — and the\n\t * only one whose result never leaves the GPU.\n\t *\n\t * The capture reflects the framebuffer **at the call site** (a `flush()`\n\t * runs first), so call it right before drawing the surface that needs the\n\t * backdrop — the classic back-buffer-copy placement model. It reads whichever\n\t * framebuffer is currently bound: the backbuffer normally, or a camera's\n\t * post-effect FBO during that pass — so it works under both `Camera2d` and\n\t * `Camera3d`. Under a perspective camera, \"drawn so far\" is draw order, not\n\t * depth order: capture after the opaque scene, just before the refracting\n\t * surface (the usual opaque-scene-texture guidance).\n\t *\n\t * Feed the result straight into a custom post-effect:\n\t * ```js\n\t * effect.setTexture(\"uScene\", renderer.toFrameTexture());\n\t * ```\n\t * {@link ShaderEffect#setTexture} binds the **live** handle, so re-capturing\n\t * each frame into the shared slot refreshes what the shader samples without\n\t * any re-bind.\n\t *\n\t * Color only, captured into an RGB texture (opaque — samples with alpha = 1);\n\t * the framebuffer's depth is not captured. By default the result is a shared,\n\t * renderer-owned texture valid until the next parameterless call — pass\n\t * `options.target` for an independent, caller-owned capture (e.g. two\n\t * captures alive in the same frame).\n\t * @param {object} [options]\n\t * @param {Texture2d|null} [options.target] - controls the destination: omit\n\t *   for the shared renderer slot (default); pass a capture previously\n\t *   returned by this method to refresh it in place; pass `null` to mint a\n\t *   fresh, caller-owned capture (for two independent captures in one frame —\n\t *   `destroy()` it yourself when done)\n\t * @param {Bounds|{x: number, y: number, width: number, height: number}} [options.region] - capture\n\t *   only this sub-region (framebuffer pixels, bottom-left origin); a smaller\n\t *   region is a proportionally cheaper copy. Defaults to the whole framebuffer.\n\t *   A {@link Bounds} (or any `{x, y, width, height}`); from a {@link Rect}\n\t *   pass `rect.getBounds()`.\n\t * @returns {Texture2d} a GPU-resident texture holding the captured frame\n\t * @example\n\t * // a water surface that refracts the scene rendered behind it\n\t * draw(renderer) {\n\t *     const scene = renderer.toFrameTexture();          // grab the backdrop\n\t *     this.shader.setTexture(\"uScene\", scene);          // live-bound\n\t *     super.draw(renderer);                             // draw the rippling water\n\t * }\n\t */\n\ttoFrameTexture(options = {}) {\n\t\tconst gl = this.gl;\n\t\tconst canvas = this.getCanvas();\n\n\t\t// resolve the capture rect in framebuffer pixels (default: everything).\n\t\t// a Bounds — or any object exposing numeric x/y/width/height (a Rect via\n\t\t// `rect.getBounds()`).\n\t\tlet x = 0;\n\t\tlet y = 0;\n\t\tlet w = canvas.width;\n\t\tlet h = canvas.height;\n\t\tconst region = options.region;\n\t\tif (typeof region !== \"undefined\") {\n\t\t\tconst rx = region.x;\n\t\t\tconst ry = region.y;\n\t\t\t// clamp the ORIGIN into the framebuffer first, then size to what\n\t\t\t// remains — an out-of-range x/y must not push x+w past the edge, or\n\t\t\t// copyTex(Sub)Image2D throws INVALID_VALUE. Missing width/height\n\t\t\t// defaults to \"the rest of the framebuffer from x/y\".\n\t\t\tx = Math.min(Math.max(0, Math.floor(rx || 0)), canvas.width - 1);\n\t\t\ty = Math.min(Math.max(0, Math.floor(ry || 0)), canvas.height - 1);\n\t\t\tconst rw = Number.isFinite(region.width)\n\t\t\t\t? Math.ceil(region.width)\n\t\t\t\t: canvas.width - x;\n\t\t\tconst rh = Number.isFinite(region.height)\n\t\t\t\t? Math.ceil(region.height)\n\t\t\t\t: canvas.height - y;\n\t\t\tw = Math.max(1, Math.min(canvas.width - x, rw));\n\t\t\th = Math.max(1, Math.min(canvas.height - y, rh));\n\t\t}\n\n\t\t// flush pending geometry so the capture holds everything drawn so far\n\t\tthis.flush();\n\n\t\t// a non-null `target` must be a capture THIS renderer previously returned\n\t\t// (a FrameTexture) — any other Texture2d has no `glTexture`, and a capture\n\t\t// from a different renderer would delete textures on the wrong GL context\n\t\tif (typeof options.target !== \"undefined\" && options.target !== null) {\n\t\t\tif (!(options.target instanceof FrameTexture)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"WebGLRenderer.toFrameTexture: `target` must be a capture returned by this method\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.target._renderer !== this) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"WebGLRenderer.toFrameTexture: `target` belongs to a different renderer\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// destination:\n\t\t//  - no `target`            → the shared, renderer-owned slot (default;\n\t\t//                             overwritten by the next parameterless call)\n\t\t//  - `target: null`         → mint a fresh, caller-owned capture (for two\n\t\t//                             independent captures alive in one frame)\n\t\t//  - `target: <capture>`    → refresh that caller-owned capture in place\n\t\tconst shared = typeof options.target === \"undefined\";\n\t\tlet frame = shared\n\t\t\t? this.getSharedFrameTexture()\n\t\t\t: options.target === null\n\t\t\t\t? undefined\n\t\t\t\t: options.target;\n\n\t\t// (re)allocate when missing, resized, or the GL handle went stale (a\n\t\t// context-loss/restore cycle deletes it — gl.isTexture catches that)\n\t\tif (\n\t\t\ttypeof frame === \"undefined\" ||\n\t\t\tframe.width !== w ||\n\t\t\tframe.height !== h ||\n\t\t\tframe.glTexture === null ||\n\t\t\tgl.isTexture(frame.glTexture) === false\n\t\t) {\n\t\t\tif (typeof frame !== \"undefined\") {\n\t\t\t\tframe.destroy();\n\t\t\t\tframe.width = w;\n\t\t\t\tframe.height = h;\n\t\t\t} else {\n\t\t\t\tframe = new FrameTexture(this, w, h);\n\t\t\t}\n\t\t\tif (shared) {\n\t\t\t\tthis._frameTexture = frame;\n\t\t\t}\n\t\t}\n\n\t\t// Copy the bound framebuffer into the capture texture — the standard\n\t\t// framebuffer→texture path.\n\t\t// An RGB internalformat is a valid subset of BOTH an alpha-less default\n\t\t// framebuffer (melonJS creates the context with `alpha: transparent`,\n\t\t// usually false) AND an RGBA camera FBO, so the copy never trips\n\t\t// INVALID_OPERATION on a format mismatch, and the capture samples as an\n\t\t// opaque backdrop (alpha = 1). Unlike a blitFramebuffer destination —\n\t\t// which some drivers won't sample in the same frame — an RGB texture\n\t\t// copied this way samples reliably everywhere.\n\t\tconst batcher = this.setBatcher(\"quad\");\n\t\t// the renderer's top unit, not the batcher's — a scratch bind is GL\n\t\t// state, so it must not move when a batcher resolves a smaller cap\n\t\tconst unit = this.maxTextures - 1;\n\n\t\t// a multisampled post-effect capture cannot be read directly\n\t\t// (copyTex*Image2D from an MSAA framebuffer is INVALID_OPERATION) —\n\t\t// resolve it and point the READ binding at the single-sampled half\n\t\t// for the copy, then rebind the render half for continued drawing\n\t\tconst activeTarget =\n\t\t\tthis._effectPassDepth > 0\n\t\t\t\t? this._renderTargetPool.getCaptureTarget()\n\t\t\t\t: null;\n\t\tconst msaaActive = activeTarget?.renderFramebuffer ? activeTarget : null;\n\t\tif (msaaActive) {\n\t\t\tmsaaActive.resolve();\n\t\t\tgl.bindFramebuffer(gl.READ_FRAMEBUFFER, msaaActive.framebuffer);\n\t\t}\n\n\t\tif (frame.glTexture === null) {\n\t\t\t// first capture into this slot: copyTexImage2D allocates the RGB\n\t\t\t// storage AND copies in one call\n\t\t\tframe.glTexture = gl.createTexture();\n\t\t\tbatcher.bindTexture2D(frame.glTexture, unit, false);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\t\tgl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGB, x, y, w, h, 0);\n\t\t} else {\n\t\t\t// steady state: refresh the existing storage in place — no per-frame\n\t\t\t// reallocation. Same size is guaranteed (a size change nulls\n\t\t\t// glTexture above via the shared-slot realloc), so copyTexSubImage2D\n\t\t\t// is valid and copies RGB from either an RGB or RGBA source.\n\t\t\tbatcher.bindTexture2D(frame.glTexture, unit, false);\n\t\t\tgl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, x, y, w, h);\n\t\t}\n\n\t\tif (msaaActive) {\n\t\t\t// back to rasterizing into the multisampled half\n\t\t\tmsaaActive.bind();\n\t\t}\n\n\t\t// the scratch bind clobbered GL `unit` OUTSIDE the shared texture-cache\n\t\t// accounting (bound directly, not via allocateTextureUnit), so other\n\t\t// batchers' unit caches don't know it changed — invalidate it on all of\n\t\t// them (see invalidateTextureUnit).\n\t\tthis.invalidateTextureUnit(unit);\n\n\t\treturn frame;\n\t}\n\n\t/**\n\t * The renderer-owned shared frame-capture slot, minted lazily so it has a\n\t * stable identity from the moment an effect is constructed — before the\n\t * first actual capture fills it (`glTexture` stays `null` until then, and\n\t * the live-sampler bind path skips unfilled captures). `ShaderEffect`\n\t * wires `: screen_texture` annotated samplers to this object.\n\t * @returns {FrameTexture} the shared capture slot\n\t * @ignore\n\t */\n\tgetSharedFrameTexture() {\n\t\tif (typeof this._frameTexture === \"undefined\") {\n\t\t\tconst canvas = this.getCanvas();\n\t\t\tthis._frameTexture = new FrameTexture(this, canvas.width, canvas.height);\n\t\t}\n\t\treturn this._frameTexture;\n\t}\n\n\t/**\n\t * Forget the cached binding for GL texture `unit` on every batcher (except\n\t * `except`). Call this whenever a texture is bound to a unit **directly** —\n\t * bypassing the shared texture cache and its `GPU_TEXTURE_CACHE_RESET`\n\t * coordination — as {@link WebGLRenderer#toFrameTexture}'s capture copy and\n\t * {@link ShaderEffect#_prepareTextures}'s reserved extra samplers both do.\n\t * Those binds land on the TOP of the unit range, which overlaps the lit quad\n\t * batcher's normal-map units; without invalidation a later lit draw would\n\t * assume its normal map is still resident there and skip re-binding it,\n\t * sampling the directly-bound texture as a normal map (wrong lighting).\n\t * @param {number} unit - the GL texture unit that was clobbered\n\t * @param {WebGLBatcher} [except] - a batcher to skip (the one that just bound the\n\t *   texture — its own cache is already accurate)\n\t * @ignore\n\t */\n\tinvalidateTextureUnit(unit, except) {\n\t\tfor (const b of this.batchers.values()) {\n\t\t\tif (b !== except) {\n\t\t\t\tb.invalidateUnit?.(unit);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Begin capturing rendering to an offscreen FBO for post-effect processing.\n\t * @param {Renderable} renderable - the renderable requesting post-effect processing\n\t * @returns {boolean} true if FBO capture started, false if skipped\n\t * @ignore\n\t */\n\tbeginPostEffect(renderable) {\n\t\t// filter to only enabled effects\n\t\tconst effects = renderable.postEffects.filter((fx) => {\n\t\t\treturn fx.enabled !== false;\n\t\t});\n\t\tif (effects.length === 0) {\n\t\t\tthis.customShader = undefined;\n\t\t\treturn false;\n\t\t}\n\t\t// single effect on non-managed renderable: fast path via customShader (no FBO)\n\t\tif (effects.length === 1 && !renderable._postEffectManaged) {\n\t\t\tthis.customShader = effects[0];\n\t\t\treturn false;\n\t\t}\n\n\t\t// multi-pass FBO path: clear customShader so children render with default shader\n\t\tthis.customShader = undefined;\n\n\t\tconst isCamera = renderable._postEffectManaged;\n\t\tconst canvas = this.getCanvas();\n\t\tconst w = canvas.width;\n\t\tconst h = canvas.height;\n\n\t\t// flush pending draws BEFORE creating/resizing FBOs,\n\t\t// since FBO construction temporarily changes GL framebuffer bindings\n\t\tthis.flush();\n\t\tthis.save();\n\t\t// save the current projection (not part of the render state stack) —\n\t\t// one preallocated slot per nesting depth, so nested passes each\n\t\t// restore their own without per-frame allocation\n\t\tlet savedProjection = this._effectProjectionStack[this._effectPassDepth];\n\t\tif (typeof savedProjection === \"undefined\") {\n\t\t\tsavedProjection = this._effectProjectionStack[this._effectPassDepth] =\n\t\t\t\tnew Matrix3d();\n\t\t}\n\t\tsavedProjection.copy(this.projectionMatrix);\n\t\tthis._effectPassDepth++;\n\n\t\tconst rt = this._renderTargetPool.begin(isCamera, effects.length, w, h);\n\t\t// FBO creation/resize binds (then nulls) TEXTURE0 — invalidate EVERY\n\t\t// batcher's cache for that unit, not just the current one: a batcher\n\t\t// that isn't current right now would otherwise keep trusting its stale\n\t\t// `boundTextures[0]` and skip the re-bind, sampling an unbound texture\n\t\t// (opaque black) for as long as the post effect is active\n\t\tthis.invalidateTextureUnit(0);\n\t\trt.bind();\n\t\tthis.setViewport(0, 0, w, h);\n\t\tthis.disableScissor();\n\t\tthis.setGlobalAlpha(1.0);\n\t\tthis.setBlendMode(\"normal\");\n\n\t\tif (isCamera) {\n\t\t\tthis.clear();\n\t\t} else {\n\t\t\tthis.clearRenderTarget();\n\t\t}\n\t\treturn true;\n\t}\n\n\t/** @ignore */\n\tendPostEffect(renderable) {\n\t\t// filter to only enabled effects\n\t\tconst effects = renderable.postEffects.filter((fx) => {\n\t\t\treturn fx.enabled !== false;\n\t\t});\n\t\tif (effects.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// single effect on non-managed renderable used customShader — no FBO to unbind\n\t\tif (effects.length === 1 && !renderable._postEffectManaged) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst isCamera = renderable._postEffectManaged;\n\t\tconst rt1 = this._renderTargetPool.getCaptureTarget();\n\t\tconst rt2 = this._renderTargetPool.getPingPongTarget();\n\t\tconst keepBlend = !isCamera;\n\t\tconst canvas = this.getCanvas();\n\t\tconst w = canvas.width;\n\t\tconst h = canvas.height;\n\n\t\tthis.flush();\n\n\t\t// `screen_texture` shader builtin (back-buffer copy semantics):\n\t\t// refresh the shared frame capture the effects' live samplers are\n\t\t// wired to. For a CAMERA effect the \"screen\" is the scene itself —\n\t\t// capture the still-bound camera FBO before unbinding it. For a\n\t\t// renderable's effect chain the \"screen\" is everything drawn behind\n\t\t// it — captured from the parent target after it is rebound below.\n\t\tconst needsScreenTexture = effects.some((fx) => {\n\t\t\treturn fx._screenTextureUniforms?.length > 0;\n\t\t});\n\t\tif (needsScreenTexture && isCamera) {\n\t\t\tthis.toFrameTexture();\n\t\t}\n\n\t\trt1.unbind();\n\n\t\t// get parent render target for rebinding after blits\n\t\tconst parentRT = this._renderTargetPool.end();\n\n\t\t// clip to camera viewport for non-default cameras\n\t\tif (isCamera && renderable.isDefault === false) {\n\t\t\tthis.clipRect(\n\t\t\t\trenderable.screenX,\n\t\t\t\trenderable.screenY,\n\t\t\t\trenderable.width,\n\t\t\t\trenderable.height,\n\t\t\t);\n\t\t}\n\n\t\t// rebind parent render target (or screen) and restore viewport.\n\t\t// The attachments we're returning to are different physical\n\t\t// buffers than the FBO we just unbound — let batchers with\n\t\t// target-scoped state invalidate it (e.g. `MeshBatcher`'s lazy\n\t\t// depth clear flag).\n\t\tif (parentRT) {\n\t\t\tparentRT.bind();\n\t\t}\n\t\tthis.setViewport(0, 0, w, h);\n\t\temit(RENDER_TARGET_CHANGED, this);\n\n\t\t// see needsScreenTexture above — the scene behind this renderable\n\t\tif (needsScreenTexture && !isCamera) {\n\t\t\tthis.toFrameTexture();\n\t\t}\n\n\t\tif (effects.length === 1) {\n\t\t\tthis.blitEffect(rt1.texture, 0, 0, w, h, effects[0], keepBlend);\n\t\t} else {\n\t\t\t// multi-pass: ping-pong between two render targets\n\t\t\tlet src = rt1;\n\t\t\tlet dst = rt2;\n\n\t\t\tfor (let i = 0; i < effects.length - 1; i++) {\n\t\t\t\tdst.bind();\n\t\t\t\tthis.setViewport(0, 0, w, h);\n\t\t\t\tthis.clearRenderTarget();\n\t\t\t\tthis.blitEffect(src.texture, 0, 0, w, h, effects[i]);\n\t\t\t\tdst.unbind();\n\t\t\t\tconst tmp = src;\n\t\t\t\tsrc = dst;\n\t\t\t\tdst = tmp;\n\t\t\t}\n\n\t\t\t// rebind parent for final blit\n\t\t\tif (parentRT) {\n\t\t\t\tparentRT.bind();\n\t\t\t}\n\t\t\tthis.setViewport(0, 0, w, h);\n\n\t\t\tthis.blitEffect(\n\t\t\t\tsrc.texture,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t\teffects[effects.length - 1],\n\t\t\t\tkeepBlend,\n\t\t\t);\n\t\t}\n\n\t\tif (isCamera && renderable.isDefault === false) {\n\t\t\tthis.disableScissor();\n\t\t}\n\n\t\t// restore renderer state and projection saved in beginPostEffect\n\t\tthis.restore();\n\t\tthis._effectPassDepth--;\n\t\tthis.projectionMatrix.copy(\n\t\t\tthis._effectProjectionStack[this._effectPassDepth],\n\t\t);\n\t\tthis.currentBatcher.setProjection(this.projectionMatrix);\n\t}\n\n\t/**\n\t * Blit a texture to the screen through a shader effect.\n\t * Draws a screen-aligned quad using the given texture as source\n\t * and the given shader for post-processing (e.g. scanlines, desaturation).\n\t * @param {WebGLTexture} source - the source texture to blit\n\t * @param {number} x - destination x position\n\t * @param {number} y - destination y position\n\t * @param {number} width - destination width\n\t * @param {number} height - destination height\n\t * @param {ShaderEffect} shader - the shader effect to apply\n\t * @param {boolean} [keepBlend=false] - if true, keep current blend mode (for sprite compositing)\n\t */\n\tblitEffect(source, x, y, width, height, shader, keepBlend = false) {\n\t\t// flush any pending draws\n\t\tthis.flush();\n\n\t\tconst batcher = this.setBatcher(\"quad\");\n\n\t\t// set up a screen-space ortho projection for the blit quad\n\t\t// (not the camera's world projection which includes scrolling)\n\t\t_savedTransform.copy(this.currentTransform);\n\t\t_savedProjection.copy(this.projectionMatrix);\n\t\tthis.currentTransform.identity();\n\t\tthis.projectionMatrix.ortho(0, width, height, 0, -1, 1);\n\t\tbatcher.setProjection(this.projectionMatrix);\n\n\t\t// disable blending for camera blits (render target is fully composited).\n\t\t// keep blending for per-sprite blits (transparent areas must not overwrite scene).\n\t\tif (!keepBlend) {\n\t\t\tthis.setBlendEnabled(false);\n\t\t}\n\t\tbatcher.blitTexture(source, x, y, width, height, shader);\n\t\tif (!keepBlend) {\n\t\t\tthis.setBlendEnabled(true);\n\t\t}\n\n\t\t// restore state\n\t\tthis.currentTransform.copy(_savedTransform);\n\t\tthis.projectionMatrix.copy(_savedProjection);\n\t\tbatcher.setProjection(this.projectionMatrix);\n\t}\n\n\t/**\n\t * set/change the current projection matrix (WebGL only)\n\t * @param {Matrix3d} matrix - the new projection matrix\n\t */\n\tsetProjection(matrix) {\n\t\t// flush pending draws before switching projection\n\t\tthis.flush();\n\t\tsuper.setProjection(matrix);\n\t\tthis.currentBatcher.setProjection(matrix);\n\t}\n\n\t/**\n\t * Sets the WebGL viewport, which specifies the affine transformation of x and y from normalized device coordinates to window coordinates\n\t * @param {number} [x = 0] - x the horizontal coordinate for the lower left corner of the viewport origin\n\t * @param {number} [y = 0] - y the vertical coordinate for the lower left corner of the viewport origin\n\t * @param {number} [w = width of the canvas] - the width of viewport\n\t * @param {number} [h = height of the canvas] - the height of viewport\n\t */\n\tsetViewport(\n\t\tx = 0,\n\t\ty = 0,\n\t\tw = this.getCanvas().width,\n\t\th = this.getCanvas().height,\n\t) {\n\t\tthis.gl.viewport(x, y, w, h);\n\t}\n\n\t/**\n\t * Clear the current render target to transparent black.\n\t *\n\t * Only clears the color buffer — `setMask()` clears `STENCIL_BUFFER_BIT`\n\t * itself when starting a mask, and including it here would emit a WebGL\n\t * `Clear called for non-existing buffers` warning when the active FBO\n\t * has no stencil attachment (rare, but happens on drivers that fail\n\t * the depth+stencil renderbuffer attachment for the FBO).\n\t */\n\tclearRenderTarget() {\n\t\tconst gl = this.gl;\n\t\tgl.clearColor(0, 0, 0, 0);\n\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\t\temit(RENDER_TARGET_CHANGED, this);\n\t}\n\n\t/**\n\t * Enable the scissor test with the given rectangle.\n\t * @param {number} x - x coordinate of the scissor rectangle\n\t * @param {number} y - y coordinate of the scissor rectangle\n\t * @param {number} width - width of the scissor rectangle\n\t * @param {number} height - height of the scissor rectangle\n\t */\n\tenableScissor(x, y, width, height) {\n\t\tconst gl = this.gl;\n\t\tconst canvas = this.getCanvas();\n\t\t// Derive the screen-space AABB via `Bounds.addFrame`, which\n\t\t// short-circuits the 4-corner walk when `currentTransform` is\n\t\t// identity. `currentScissor` stores screen-space coords directly\n\t\t// so save/restore can re-apply without re-running transform math\n\t\t// and so a scaled or rotated parent transform is honored — not\n\t\t// just translation. Issue #1349.\n\t\tconst aabb = this._clipAABB;\n\t\taabb.clear();\n\t\taabb.addFrame(x, y, x + width, y + height, this.currentTransform);\n\t\tconst sx = Math.floor(aabb.min.x);\n\t\tconst sy = Math.floor(aabb.min.y);\n\t\tconst sw = Math.ceil(aabb.max.x - sx);\n\t\tconst sh = Math.ceil(aabb.max.y - sy);\n\t\tthis.flush();\n\t\tgl.enable(gl.SCISSOR_TEST);\n\t\tthis._scissorActive = true;\n\t\tgl.scissor(sx, canvas.height - sh - sy, sw, sh);\n\t\tthis.currentScissor[0] = sx;\n\t\tthis.currentScissor[1] = sy;\n\t\tthis.currentScissor[2] = sw;\n\t\tthis.currentScissor[3] = sh;\n\t}\n\n\t/**\n\t * Disable the scissor test, allowing rendering to the full viewport.\n\t */\n\tdisableScissor() {\n\t\tif (this._scissorActive === true) {\n\t\t\t// drain quads batched under the scissor BEFORE turning the test\n\t\t\t// off — GL scissor applies at draw time, not at batch time\n\t\t\t// (mirrors enableScissor / clipRect / restore, which all flush\n\t\t\t// for exactly this reason)\n\t\t\tthis.flush();\n\t\t}\n\t\tthis.gl.disable(this.gl.SCISSOR_TEST);\n\t\tthis._scissorActive = false;\n\t}\n\n\t/**\n\t * Enable or disable alpha blending.\n\t * @param {boolean} enable - true to enable blending, false to disable\n\t */\n\tsetBlendEnabled(enable) {\n\t\tif (enable) {\n\t\t\tthis.gl.enable(this.gl.BLEND);\n\t\t} else {\n\t\t\tthis.gl.disable(this.gl.BLEND);\n\t\t}\n\t}\n\n\t/**\n\t * Clear the frame buffer\n\t */\n\tclear() {\n\t\tconst gl = this.gl;\n\t\tconst clearColor = this.backgroundColor.toArray();\n\t\tgl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);\n\t\tthis.lineWidth = 1;\n\t\t// Color + stencil are cleared here. Depth (if any batcher cares\n\t\t// — currently only `MeshBatcher`) is cleared lazily on the first\n\t\t// draw of its mode, via `_onTargetChanged` → `updatePassState()`.\n\t\tgl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);\n\t\temit(RENDER_TARGET_CHANGED, this);\n\t}\n\n\t/**\n\t * Clears the gl context with the given color.\n\t * @param {Color|string} [color=\"#000000\"] - CSS color.\n\t * @param {boolean} [opaque=false] - Allow transparency [default] or clear the surface completely [true]\n\t */\n\tclearColor(color = \"#000000\", opaque = false) {\n\t\tlet glArray;\n\t\tconst gl = this.gl;\n\n\t\tif (color instanceof Color) {\n\t\t\tglArray = color.toArray();\n\t\t} else {\n\t\t\tconst _color = colorPool.get();\n\t\t\t// reuse temporary the renderer default color object\n\t\t\tglArray = _color.parseCSS(color).toArray();\n\t\t\tcolorPool.release(_color);\n\t\t}\n\n\t\t// clear gl context with the specified color\n\t\tgl.clearColor(\n\t\t\tglArray[0],\n\t\t\tglArray[1],\n\t\t\tglArray[2],\n\t\t\topaque === true ? 1.0 : glArray[3],\n\t\t);\n\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\t}\n\n\t/**\n\t * Erase the pixels in the given rectangular area by setting them to transparent black (rgba(0,0,0,0)).\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t */\n\tclearRect(x, y, width, height) {\n\t\tthis.save();\n\t\tthis.clipRect(x, y, width, height);\n\t\t// actual transparent black, per the JSDoc above and the Canvas\n\t\t// renderer's native clearRect — the bare clearColor() default\n\t\t// (\"#000000\") parses with alpha 1 and would paint OPAQUE black\n\t\tthis.clearColor(\"rgba(0,0,0,0)\");\n\t\tthis.restore();\n\t}\n\n\t/**\n\t * Draw an image to the gl context\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame|CompressedImage} image - An element to draw into the context.\n\t * @param {number} sx - The X coordinate of the top left corner of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} sy - The Y coordinate of the top left corner of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} sw - The width of the sub-rectangle of the source image to draw into the destination context. If not specified, the entire rectangle from the coordinates specified by sx and sy to the bottom-right corner of the image is used.\n\t * @param {number} sh - The height of the sub-rectangle of the source image to draw into the destination context.\n\t * @param {number} dx - The X coordinate in the destination canvas at which to place the top-left corner of the source image.\n\t * @param {number} dy - The Y coordinate in the destination canvas at which to place the top-left corner of the source image.\n\t * @param {number} dw - The width to draw the image in the destination canvas. This allows scaling of the drawn image. If not specified, the image is not scaled in width when drawn.\n\t * @param {number} dh - The height to draw the image in the destination canvas. This allows scaling of the drawn image. If not specified, the image is not scaled in height when drawn.\n\t * @example\n\t * // Position the image on the canvas:\n\t * renderer.drawImage(image, dx, dy);\n\t * // Position the image on the canvas, and specify width and height of the image:\n\t * renderer.drawImage(image, dx, dy, dWidth, dHeight);\n\t * // Clip the image and position the clipped part on the canvas:\n\t * renderer.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);\n\t */\n\tdrawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {\n\t\tif (typeof sw === \"undefined\") {\n\t\t\tsw = dw = image.width;\n\t\t\tsh = dh = image.height;\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t} else if (typeof dx === \"undefined\") {\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tdw = sw;\n\t\t\tdh = sh;\n\t\t\tsw = image.width;\n\t\t\tsh = image.height;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t}\n\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// clamp to pixel grid\n\t\t\tdx |= 0;\n\t\t\tdy |= 0;\n\t\t}\n\n\t\t// dispatch to the lit batcher only when the scene actually has\n\t\t// active lights AND this sprite has a normal map. Otherwise the\n\t\t// unlit `quad` batcher is faster (full texture-unit capacity, no\n\t\t// paired normal upload, no per-fragment lighting math).\n\t\t// Back-buffer copy semantics for the `screen_texture` shader\n\t\t// builtin: refresh the shared frame capture with everything drawn so\n\t\t// far BEFORE this sprite's own quad, so the effect samples the scene\n\t\t// behind it. Must run before setBatcher below — toFrameTexture\n\t\t// flushes and switches to the quad batcher internally.\n\t\tif (this.customShader?._screenTextureUniforms?.length > 0) {\n\t\t\tthis.toFrameTexture();\n\t\t}\n\n\t\tconst useLit =\n\t\t\tthis.batchers.has(\"litQuad\") &&\n\t\t\tthis.activeLightCount > 0 &&\n\t\t\tthis.currentNormalMap !== null;\n\t\tthis.setBatcher(useLit ? \"litQuad\" : \"quad\");\n\n\t\tconst shader = this.customShader;\n\t\tif (shader != null) {\n\t\t\tthis.currentBatcher.useShader(shader);\n\t\t\t// (re)bind any extra textures a ShaderEffect declared via setTexture\n\t\t\tshader._prepareTextures?.(this.currentBatcher);\n\t\t}\n\n\t\tconst texture = this.cache.get(image);\n\t\t// Video sources need their GL texture refreshed as the video plays.\n\t\t// When a Sprite tracks the element via requestVideoFrameCallback it\n\t\t// stamps a monotonic `version` counter on it (same duck-typed\n\t\t// convention as NoiseTexture2d) — re-upload only when a new frame was\n\t\t// actually presented, recorded per cached atlas so a shared element\n\t\t// drawn by several sprites uploads once per frame. Without the stamp\n\t\t// (no rVFC support / manual drawImage callers), or on the lit path\n\t\t// (each batcher owns a separate GL texture — one shared record would\n\t\t// go stale across them), keep the legacy per-frame force re-upload.\n\t\tlet reupload = false;\n\t\tif (typeof image.videoWidth !== \"undefined\") {\n\t\t\tif (image.version === undefined || useLit) {\n\t\t\t\treupload = true;\n\t\t\t} else if (texture._videoFrameVersion !== image.version) {\n\t\t\t\ttexture._videoFrameVersion = image.version;\n\t\t\t\treupload = true;\n\t\t\t}\n\t\t}\n\t\tconst uvs = texture.getUVs(sx, sy, sw, sh);\n\t\tif (useLit) {\n\t\t\tthis.currentBatcher.addQuad(\n\t\t\t\ttexture,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw,\n\t\t\t\tdh,\n\t\t\t\tuvs[0],\n\t\t\t\tuvs[1],\n\t\t\t\tuvs[2],\n\t\t\t\tuvs[3],\n\t\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t\t\treupload,\n\t\t\t\tthis.currentNormalMap,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.currentBatcher.addQuad(\n\t\t\t\ttexture,\n\t\t\t\tdx,\n\t\t\t\tdy,\n\t\t\t\tdw,\n\t\t\t\tdh,\n\t\t\t\tuvs[0],\n\t\t\t\tuvs[1],\n\t\t\t\tuvs[2],\n\t\t\t\tuvs[3],\n\t\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t\t\treupload,\n\t\t\t);\n\t\t}\n\n\t\tif (typeof shader === \"object\") {\n\t\t\tthis.currentBatcher.useShader(this.currentBatcher.defaultShader);\n\t\t}\n\t}\n\n\t/**\n\t * Draw a pattern within the given rectangle.\n\t * @param {TextureAtlas} pattern - Pattern object\n\t * @param {number} x - x position where to draw the pattern\n\t * @param {number} y - y position where to draw the pattern\n\t * @param {number} width - width of the pattern\n\t * @param {number} height - height of the pattern\n\t * @see WebGLRenderer#createPattern\n\t */\n\tdrawPattern(pattern, x, y, width, height) {\n\t\tconst uvs = pattern.getUVs(\"0,0,\" + width + \",\" + height);\n\t\tthis.setBatcher(\"quad\");\n\t\tthis.currentBatcher.addQuad(\n\t\t\tpattern,\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tuvs[0],\n\t\t\tuvs[1],\n\t\t\tuvs[2],\n\t\t\tuvs[3],\n\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t);\n\t}\n\n\t/**\n\t * Draw a textured triangle mesh.\n\t * Enables hardware depth testing and backface culling for the\n\t * duration of the draw, then restores the previous GL state. Large\n\t * meshes are automatically chunked across multiple draw calls to\n\t * fit the vertex/index buffer limits.\n\t *\n\t * Multi-material meshes (OBJ files with multiple `usemtl` groups +\n\t * a bound MTL) don't add any per-material draw calls here — the\n\t * per-material colors are baked into `mesh.vertexColors` at\n\t * construction time and pushed through the batcher's per-vertex\n\t * `aColor` attribute. `mesh.tint` still multiplies on top at draw\n\t * time, so flash / fade / team-color via `setTint` work the same\n\t * as single-material. (The chunking-for-buffer-limits behavior\n\t * above still applies to multi-material meshes as well.)\n\t * @param {Mesh} mesh - a Mesh renderable or compatible object\n\t * @param {Matrix3d} [modelMatrix] - where the mesh sits in the world. When\n\t * given, the mesh is drawn from persistent model-space geometry and placed\n\t * entirely by uniforms; when omitted, its vertices are taken as already\n\t * positioned and accumulated through the batcher (the 2D-camera path).\n\t */\n\t/**\n\t * Draw one ground shadow per instance of an `InstancedMesh` (#1515), in a\n\t * single call over the same instance buffer the mesh drew from.\n\t * @param {InstancedMesh} mesh - the mesh casting the shadows\n\t * @param {Matrix3d} shadowMatrix - the group matrix flattened onto the ground\n\t * @param {object} quad - the shared shadow quad\n\t * @ignore\n\t */\n\tdrawInstancedShadow(mesh, shadowMatrix, quad) {\n\t\tconst tint = this.currentTint.toUint32(this.getGlobalAlpha());\n\t\t// deferred to the end of the mesh pass for the same reason a per-object\n\t\t// shadow is — see `queueGroundShadow`\n\t\tif (this._shadowFlushing !== true) {\n\t\t\tthis.queueGroundShadow(quad, shadowMatrix, tint, mesh);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(quad.lit === true ? \"litMesh\" : \"mesh\");\n\t\tthis.currentBatcher.drawInstancedShadow(mesh, shadowMatrix, tint, quad);\n\t}\n\n\tdrawMesh(mesh, modelMatrix) {\n\t\tconst gl = this.gl;\n\t\tconst retained = modelMatrix !== undefined;\n\n\t\t// Route to the lit or unlit mesh batcher. `mesh.lit` meshes use the\n\t\t// `LitMeshBatcher` (world-space normals + lighting); everything else\n\t\t// uses the lean unlit `MeshBatcher`. Both share the mesh-mode depth\n\t\t// state, so mixing them keeps inter-mesh occlusion correct.\n\t\t//\n\t\t// `setBatcher` delegates mesh-mode state setup (DEPTH_TEST enable,\n\t\t// LEQUAL, depthMask, BLEND off) to the batcher's `bind()`; the one-shot\n\t\t// per-target depth clear and the lit light upload happen per draw in\n\t\t// `updatePassState()`, since `bind()` only fires on a batcher\n\t\t// transition. Switching away restores non-mesh defaults via\n\t\t// `unbind()`. Consecutive same-kind `drawMesh` calls pay zero state cost\n\t\t// between them; the GPU's LEQUAL depth test resolves inter-mesh\n\t\t// occlusion per pixel against the accumulated depth buffer.\n\t\tthis.setBatcher(mesh.lit === true ? \"litMesh\" : \"mesh\");\n\n\t\t// apply custom shader if set on the renderable (via preDraw) —\n\t\t// hostable only when it carries a live GL program (a WGSL-only\n\t\t// GLShader has none: keep the built-in shading, and say so once).\n\t\t// The warn keys on the DECLARED realization (isWebGL), not the live\n\t\t// program: a dual shader drawn during a lost-context window merely\n\t\t// lacks its program transiently and must neither warn nor consume\n\t\t// the one-shot for a later genuinely-unhostable shader.\n\t\tconst hostedShader =\n\t\t\tthis.customShader != null && this.customShader.program != null;\n\t\tif (hostedShader) {\n\t\t\tthis.currentBatcher.useShader(this.customShader);\n\t\t} else if (\n\t\t\tthis.customShader != null &&\n\t\t\tthis.customShader.isWebGL !== true &&\n\t\t\tthis._meshShaderWarned !== true\n\t\t) {\n\t\t\tthis._meshShaderWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: this custom shader cannot be hosted on a Mesh by the WebGL renderer (no compiled GLSL program) — the mesh draws with the built-in shading\",\n\t\t\t);\n\t\t}\n\n\t\t// toggle backface culling per-mesh — varies across meshes in\n\t\t// the same run, so this stays here rather than in the batcher's\n\t\t// bind/unbind lifecycle.\n\t\tif (mesh.cullBackFaces) {\n\t\t\tgl.enable(gl.CULL_FACE);\n\t\t\tgl.cullFace(gl.BACK);\n\t\t\t// Retained geometry keeps its authored winding, so the axis bridge's\n\t\t\t// reflection (which mirrors handedness) is undone by flipping which\n\t\t\t// orientation counts as front-facing — equivalent to reversing every\n\t\t\t// triangle, without a second copy of the index data. Right-handed\n\t\t\t// meshes are bridged by a rotation, which preserves winding.\n\t\t\tgl.frontFace(retained && mesh.rightHanded !== true ? gl.CW : gl.CCW);\n\t\t}\n\n\t\t// finally: a throw mid-draw (e.g. a texture upload failing) must not\n\t\t// leak the cull toggle or leave the custom program bound — the NEXT\n\t\t// unshaded mesh would silently draw with it\n\t\ttry {\n\t\t\tconst tint = this.currentTint.toUint32(this.getGlobalAlpha());\n\t\t\tif (\n\t\t\t\tmesh.instanceLayout !== undefined &&\n\t\t\t\tretained &&\n\t\t\t\tthis.supportsInstancing === true\n\t\t\t) {\n\t\t\t\t// one geometry, N copies, one call — the per-instance records\n\t\t\t\t// carry the placement each copy differs by, while `modelMatrix`\n\t\t\t\t// places the group as a whole\n\t\t\t\tthis.currentBatcher.drawInstancedMesh(mesh, modelMatrix, tint);\n\t\t\t} else if (retained) {\n\t\t\t\tthis.currentBatcher.drawRetainedMesh(mesh, modelMatrix, tint);\n\t\t\t} else {\n\t\t\t\tthis.currentBatcher.addMesh(mesh, tint);\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t} finally {\n\t\t\tif (mesh.cullBackFaces) {\n\t\t\t\tgl.disable(gl.CULL_FACE);\n\t\t\t\t// restore the default orientation alongside the cull toggle:\n\t\t\t\t// leaving it at CW would leak to any later consumer of the\n\t\t\t\t// context that reads `gl_FrontFacing` or enables culling itself\n\t\t\t\tgl.frontFace(gl.CCW);\n\t\t\t}\n\n\t\t\t// revert to default shader if custom was applied\n\t\t\tif (hostedShader) {\n\t\t\t\tthis.currentBatcher.useShader(this.currentBatcher.defaultShader);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path\n\t * @example\n\t * // First path\n\t * renderer.beginPath();\n\t * renderer.setColor(\"blue\");\n\t * renderer.moveTo(20, 20);\n\t * renderer.lineTo(200, 20);\n\t * renderer.stroke();\n\t * // Second path\n\t * renderer.beginPath();\n\t * renderer.setColor(\"green\");\n\t * renderer.moveTo(20, 20);\n\t * renderer.lineTo(120, 120);\n\t * renderer.stroke();\n\t */\n\tbeginPath() {\n\t\tthis.path2D.beginPath();\n\t}\n\n\t/**\n\t * begins a new sub-path at the point specified by the given (x, y) coordinates.\n\t * @param {number} x - The x axis of the point.\n\t * @param {number} y - The y axis of the point.\n\t */\n\tmoveTo(x, y) {\n\t\tthis.path2D.moveTo(x, y);\n\t}\n\n\t/**\n\t * adds a straight line to the current sub-path by connecting the sub-path's last point to the specified (x, y) coordinates.\n\t */\n\tlineTo(x, y) {\n\t\tthis.path2D.lineTo(x, y);\n\t}\n\n\t/**\n\t * Adds a quadratic Bezier curve to the current sub-path.\n\t * The curve is tessellated into line segments for WebGL rendering.\n\t * @param {number} cpx - The x-axis coordinate of the control point.\n\t * @param {number} cpy - The y-axis coordinate of the control point.\n\t * @param {number} x - The x-axis coordinate of the end point.\n\t * @param {number} y - The y-axis coordinate of the end point.\n\t */\n\tquadraticCurveTo(cpx, cpy, x, y) {\n\t\tthis.path2D.quadraticCurveTo(cpx, cpy, x, y);\n\t}\n\n\t/**\n\t * Adds a cubic Bezier curve to the current sub-path.\n\t * The curve is tessellated into line segments for WebGL rendering.\n\t * @param {number} cp1x - The x-axis coordinate of the first control point.\n\t * @param {number} cp1y - The y-axis coordinate of the first control point.\n\t * @param {number} cp2x - The x-axis coordinate of the second control point.\n\t * @param {number} cp2y - The y-axis coordinate of the second control point.\n\t * @param {number} x - The x-axis coordinate of the end point.\n\t * @param {number} y - The y-axis coordinate of the end point.\n\t */\n\tbezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {\n\t\tthis.path2D.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n\t}\n\n\t/**\n\t * Adds a circular arc to the current sub-path, using the given control points and radius.\n\t * The arc is tessellated into line segments for WebGL rendering.\n\t * @param {number} x1 - The x-axis coordinate of the first control point.\n\t * @param {number} y1 - The y-axis coordinate of the first control point.\n\t * @param {number} x2 - The x-axis coordinate of the second control point.\n\t * @param {number} y2 - The y-axis coordinate of the second control point.\n\t * @param {number} radius - The arc's radius. Must be non-negative.\n\t */\n\tarcTo(x1, y1, x2, y2, radius) {\n\t\tthis.path2D.arcTo(x1, y1, x2, y2, radius);\n\t}\n\n\t/**\n\t * creates a rectangular path whose starting point is at (x, y) and whose size is specified by width and height.\n\t * @param {number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t */\n\trect(x, y, width, height) {\n\t\tthis.path2D.rect(x, y, width, height);\n\t}\n\n\t/**\n\t * adds a rounded rectangle to the current path.\n\t * @param {number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t * @param {number} radius - The corner radius.\n\t */\n\troundRect(x, y, width, height, radii) {\n\t\tthis.path2D.roundRect(x, y, width, height, radii);\n\t}\n\n\t/**\n\t * stroke the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to stroke\n\t * @param {boolean} [fill=false] - fill the shape with the current color if true\n\t */\n\tstroke(shape, fill) {\n\t\tif (typeof shape === \"undefined\") {\n\t\t\tthis.setBatcher(\"primitive\");\n\t\t\tif (fill === true) {\n\t\t\t\t// draw all triangles\n\t\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\t\tthis.gl.TRIANGLES,\n\t\t\t\t\tthis.path2D.triangulatePath(),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst dash = this.renderState.lineDash;\n\t\t\t\tif (dash.length > 0) {\n\t\t\t\t\tconst pts = this.path2D.points;\n\t\t\t\t\tconst dashed = dashPath(pts, dash);\n\t\t\t\t\tif (dashed.length > 0) {\n\t\t\t\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, dashed);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// dispatches to strokeRect/strokePolygon/etc. which each call setBatcher\n\t\t\tsuper.stroke(shape, fill);\n\t\t}\n\t}\n\n\t/**\n\t * fill the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to fill\n\t */\n\tfill(shape) {\n\t\tthis.stroke(shape, true);\n\t}\n\n\t/**\n\t * add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing\n\t */\n\tclosePath() {\n\t\tthis.path2D.closePath();\n\t}\n\n\t/**\n\t * Returns the WebGLContext instance for the renderer\n\t * return a reference to the system 2d Context\n\t * @returns {WebGLRenderingContext} the current WebGL context\n\t */\n\tgetContext() {\n\t\treturn this.gl;\n\t}\n\n\t/**\n\t * set the current blend mode for this renderer. <br>\n\t * All renderers support: <br>\n\t * - \"normal\" : draws new content on top of the existing content <br>\n\t * <img src=\"../images/normal-blendmode.png\" width=\"180\"/> <br>\n\t * - \"add\", \"additive\", or \"lighter\" : color values are added together <br>\n\t * <img src=\"../images/add-blendmode.png\" width=\"180\"/> <br>\n\t * - \"multiply\" : pixels are multiplied, resulting in a darker picture <br>\n\t * <img src=\"../images/multiply-blendmode.png\" width=\"180\"/> <br>\n\t * - \"screen\" : pixels are inverted, multiplied, and inverted again (opposite of multiply) <br>\n\t * <img src=\"../images/screen-blendmode.png\" width=\"180\"/> <br>\n\t * WebGL2 additionally supports: <br>\n\t * - \"darken\" : retains the darkest pixels of both layers <br>\n\t * <img src=\"../images/darken-blendmode.png\" width=\"180\"/> <br>\n\t * - \"lighten\" : retains the lightest pixels of both layers <br>\n\t * <img src=\"../images/lighten-blendmode.png\" width=\"180\"/> <br>\n\t * - \"none\" : blending disabled — the source replaces the destination\n\t * outright, alpha included (matches the WebGPU renderer's \"none\") <br>\n\t * Other CSS blend modes (\"overlay\", \"color-dodge\", \"color-burn\", \"hard-light\", \"soft-light\",\n\t * \"difference\", \"exclusion\") may be supported by the Canvas renderer (browser-dependent)\n\t * and will always fall back to \"normal\" in WebGL. <br>\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n\t * @param {string} [mode=\"normal\"] - blend mode\n\t * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (affects source blend factor)\n\t * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported)\n\t */\n\tsetBlendMode(mode = \"normal\", premultipliedAlpha = true) {\n\t\tif (\n\t\t\tthis.currentBlendMode !== mode ||\n\t\t\tthis.currentPremultipliedAlpha !== premultipliedAlpha\n\t\t) {\n\t\t\tconst gl = this.gl;\n\t\t\tthis.flush();\n\t\t\tgl.enable(gl.BLEND);\n\t\t\tthis.currentBlendMode = mode;\n\t\t\tthis.currentPremultipliedAlpha = premultipliedAlpha;\n\n\t\t\t// source factor depends on whether textures use premultiplied alpha\n\t\t\tconst srcAlpha = premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA;\n\n\t\t\tswitch (mode) {\n\t\t\t\tcase \"screen\":\n\t\t\t\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\t\t\t\tgl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// exclusion(a, b) = a + b - 2ab. Unlike the other CSS advanced\n\t\t\t\t// modes this needs no per-pixel branch — it falls straight out\n\t\t\t\t// of `src * sfactor + dst * dfactor`:\n\t\t\t\t//   s*(1-d) + d*(1-s) = s + d - 2sd\n\t\t\t\t// Same caveat as `screen` above: the identity is exact for an\n\t\t\t\t// opaque source and approximate for a translucent one, because\n\t\t\t\t// the factors act on the premultiplied colour.\n\t\t\t\tcase \"exclusion\":\n\t\t\t\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\t\t\t\tgl.blendFunc(gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"lighter\":\n\t\t\t\tcase \"additive\":\n\t\t\t\tcase \"add\":\n\t\t\t\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\t\t\t\tgl.blendFunc(srcAlpha, gl.ONE);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"multiply\":\n\t\t\t\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\t\t\t\tgl.blendFunc(gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"darken\":\n\t\t\t\t\tgl.blendEquation(gl.MIN);\n\t\t\t\t\tgl.blendFunc(gl.ONE, gl.ONE);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"lighten\":\n\t\t\t\t\tgl.blendEquation(gl.MAX);\n\t\t\t\t\tgl.blendFunc(gl.ONE, gl.ONE);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"none\":\n\t\t\t\t\t// replace: source overwrites destination, alpha included —\n\t\t\t\t\t// the WebGPU backend's \"none\" pipeline blend state parity\n\t\t\t\t\tgl.disable(gl.BLEND);\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tgl.blendEquation(gl.FUNC_ADD);\n\t\t\t\t\tgl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA);\n\t\t\t\t\tthis.currentBlendMode = \"normal\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn this.currentBlendMode;\n\t}\n\n\t/**\n\t * restores the most recently saved renderer state by popping the top entry in the drawing state stack\n\t * @example\n\t * // Save the current state\n\t * renderer.save();\n\t *\n\t * // apply a transform and draw a rect\n\t * renderer.tranform(matrix);\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Restore to the state saved by the most recent call to save()\n\t * renderer.restore();\n\t */\n\trestore() {\n\t\tconst canvas = this.getCanvas();\n\t\t// Peek at the to-be-restored scissor (without mutating state)\n\t\t// so we can detect whether the restore actually changes the\n\t\t// scissor box. If it does, drain pending vertices first under\n\t\t// the *current* GL scissor — otherwise vertices queued inside\n\t\t// a tighter clip would flush later under a more permissive\n\t\t// scissor and visually escape their clip.\n\t\t// `peekScissor` returns the saved box directly (live ref) or\n\t\t// null when scissor will be inactive after the restore. Treat\n\t\t// the returned array as read-only — it points into the stack.\n\t\tconst peek = this.renderState.peekScissor();\n\t\tconst cur = this.currentScissor;\n\t\tconst curActive = this._scissorActive === true;\n\t\tconst willBeActive = peek !== null;\n\t\tconst scissorChanging =\n\t\t\tcurActive !== willBeActive ||\n\t\t\t(willBeActive &&\n\t\t\t\t(cur[0] !== peek[0] ||\n\t\t\t\t\tcur[1] !== peek[1] ||\n\t\t\t\t\tcur[2] !== peek[2] ||\n\t\t\t\t\tcur[3] !== peek[3]));\n\t\tif (scissorChanging) {\n\t\t\tthis.flush();\n\t\t}\n\t\tconst result = this.renderState.restore(canvas.width, canvas.height);\n\t\tif (result !== null) {\n\t\t\tthis.setBlendMode(result.blendMode);\n\t\t\tif (scissorChanging) {\n\t\t\t\tconst gl = this.gl;\n\t\t\t\tif (result.scissorActive) {\n\t\t\t\t\tconst next = this.currentScissor;\n\t\t\t\t\tgl.enable(gl.SCISSOR_TEST);\n\t\t\t\t\tthis._scissorActive = true;\n\t\t\t\t\t// `currentScissor` stores screen-space coords directly\n\t\t\t\t\t// (see `clipRect`); apply with the GL bottom-left y-flip.\n\t\t\t\t\tgl.scissor(\n\t\t\t\t\t\tnext[0],\n\t\t\t\t\t\tcanvas.height - next[3] - next[1],\n\t\t\t\t\t\tnext[2],\n\t\t\t\t\t\tnext[3],\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tgl.disable(gl.SCISSOR_TEST);\n\t\t\t\t\tthis._scissorActive = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// sync gradient and shader from renderState\n\t\tthis._currentGradient = this.renderState.currentGradient;\n\t\tthis.customShader = this.renderState.currentShader;\n\t}\n\n\t/**\n\t * saves the entire state of the renderer by pushing the current state onto a stack.\n\t * @example\n\t * // Save the current state\n\t * renderer.save();\n\t *\n\t * // apply a transform and draw a rect\n\t * renderer.tranform(matrix);\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Restore to the state saved by the most recent call to save()\n\t * renderer.restore();\n\t */\n\tsave() {\n\t\tthis.renderState.currentShader = this.customShader;\n\t\tthis.renderState.save(this._scissorActive === true);\n\t}\n\n\t/**\n\t * adds a rotation to the transformation matrix.\n\t * @param {number} angle - the rotation angle, clockwise in radians\n\t * @example\n\t * // Rotated rectangle\n\t * renderer.rotate((45 * Math.PI) / 180);\n\t * renderer.setColor(\"red\");\n\t * renderer.fillRect(10, 10, 100, 100);\n\t *\n\t * // Reset transformation matrix to the identity matrix\n\t * renderer.setTransform(1, 0, 0, 1, 0, 0);\n\t */\n\trotate(angle) {\n\t\tthis.currentTransform.rotate(angle);\n\t}\n\n\t/**\n\t * adds a scaling transformation to the renderer units horizontally and/or vertically\n\t * @param {number} x - Scaling factor in the horizontal direction. A negative value flips pixels across the vertical axis. A value of 1 results in no horizontal scaling.\n\t * @param {number} y - Scaling factor in the vertical direction. A negative value flips pixels across the horizontal axis. A value of 1 results in no vertical scaling\n\t */\n\tscale(x, y) {\n\t\tthis.currentTransform.scale(x, y, 1);\n\t}\n\n\t/**\n\t * Map the backend-neutral default filter mode\n\t * ({@link Renderer#getDefaultTextureFilter}) to the GL enum\n\t * (`gl.LINEAR` / `gl.NEAREST`) used for textures that don't carry their own\n\t * `texture.filter`. The neutral resolver lives on the base renderer so a\n\t * future WebGPU backend reuses it and maps to `GPUFilterMode` instead.\n\t * @returns {number} `gl.LINEAR` or `gl.NEAREST`\n\t * @ignore\n\t */\n\t_glTextureFilter() {\n\t\treturn this.getDefaultTextureFilter() === \"linear\"\n\t\t\t? this.gl.LINEAR\n\t\t\t: this.gl.NEAREST;\n\t}\n\n\t/**\n\t * Re-apply a min/mag filter to every currently-bound texture across all\n\t * batchers (see https://github.com/melonjs/melonJS/issues/1279). Shared by\n\t * {@link WebGLRenderer#setAntiAlias} and {@link WebGLRenderer#setTextureFilter}.\n\t * @param {number} filter - `gl.LINEAR` or `gl.NEAREST`\n\t * @ignore\n\t */\n\t_reapplyTextureFilter(filter) {\n\t\tconst gl = this.gl;\n\t\tthis.batchers.forEach((batcher) => {\n\t\t\tif (batcher.boundTextures) {\n\t\t\t\tfor (let i = 0; i < batcher.boundTextures.length; i++) {\n\t\t\t\t\tif (typeof batcher.boundTextures[i] !== \"undefined\") {\n\t\t\t\t\t\tgl.activeTexture(gl.TEXTURE0 + i);\n\t\t\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, batcher.boundTextures[i]);\n\t\t\t\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);\n\t\t\t\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// reset so next bindTexture2D re-selects the correct unit\n\t\t\t\tbatcher.currentTextureUnit = -1;\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * enable/disable image smoothing (scaling interpolation)\n\t * @param {boolean} [enable=false]\n\t */\n\tsetAntiAlias(enable = false) {\n\t\tif (this.settings.antiAlias !== enable) {\n\t\t\tsuper.setAntiAlias(enable);\n\t\t\t// re-filter bound textures to the resolved default — honors an explicit\n\t\t\t// `textureFilter` (so toggling antiAlias won't override a \"nearest\" /\n\t\t\t// \"linear\" choice), and tracks antiAlias when `textureFilter` is \"auto\".\n\t\t\tthis._reapplyTextureFilter(this._glTextureFilter());\n\t\t}\n\t}\n\n\t/**\n\t * Set the default texture magnification/minification filter at runtime,\n\t * decoupled from {@link WebGLRenderer#setAntiAlias} (which controls MSAA).\n\t * Records the setting (via the base) then re-filters every currently-bound\n\t * texture. WebGL only.\n\t * @param {\"auto\"|\"nearest\"|\"linear\"} [mode=\"auto\"] - `\"auto\"` follows `antiAlias`\n\t * @see Renderer#getDefaultTextureFilter\n\t */\n\tsetTextureFilter(mode = \"auto\") {\n\t\tsuper.setTextureFilter(mode);\n\t\tthis._reapplyTextureFilter(this._glTextureFilter());\n\t}\n\n\t/**\n\t * Set the global alpha\n\t * @param {number} alpha - 0.0 to 1.0 values accepted.\n\t */\n\tsetGlobalAlpha(alpha) {\n\t\tthis.currentColor.alpha = alpha;\n\t}\n\n\t/**\n\t * Return the global alpha\n\t * @returns {number} global alpha value\n\t */\n\tgetGlobalAlpha() {\n\t\treturn this.currentColor.alpha;\n\t}\n\n\t/**\n\t * Set the current fill & stroke style color.\n\t * By default, or upon reset, the value is set to #000000.\n\t * @param {Color|string|Gradient} color - css color string or a Gradient object.\n\t */\n\tsetColor(color) {\n\t\tif (color instanceof Gradient) {\n\t\t\tthis.renderState.currentGradient = color;\n\t\t\tthis._currentGradient = color;\n\t\t} else {\n\t\t\tthis.renderState.currentGradient = null;\n\t\t\tthis._currentGradient = null;\n\t\t\tconst alpha = this.currentColor.alpha;\n\t\t\tthis.currentColor.copy(color);\n\t\t\tthis.currentColor.alpha *= alpha;\n\t\t}\n\t}\n\n\t/**\n\t * Stroke an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius - arc radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeArc(x, y, radius, start, end, antiClockwise = false, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillArc(x, y, radius, start, end, antiClockwise);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.arc(x, y, radius, start, end, antiClockwise);\n\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t}\n\n\t/**\n\t * Fill an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius - arc radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t */\n\tfillArc(x, y, radius, start, end, antiClockwise = false) {\n\t\tif (this._currentGradient) {\n\t\t\tthis.#gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillArc(x, y, radius, start, end, antiClockwise);\n\t\t\t\t},\n\t\t\t\tx - radius,\n\t\t\t\ty - radius,\n\t\t\t\tradius * 2,\n\t\t\t\tradius * 2,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tlet diff = Math.abs(end - start);\n\t\tif (antiClockwise) {\n\t\t\tdiff = Math.PI * 2 - diff;\n\t\t}\n\t\tconst segments = Math.max(\n\t\t\t4,\n\t\t\tMath.round((diff * radius) / this.path2D.arcResolution),\n\t\t);\n\t\tconst startAngle = antiClockwise ? end : start;\n\t\tthis.currentBatcher.drawVertices(\n\t\t\tthis.gl.TRIANGLES,\n\t\t\tgenerateTriangleFan(\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tradius,\n\t\t\t\tradius,\n\t\t\t\tstartAngle,\n\t\t\t\tstartAngle + diff,\n\t\t\t\tsegments,\n\t\t\t),\n\t\t);\n\t}\n\n\t/**\n\t * Stroke an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeEllipse(x, y, w, h, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillEllipse(x, y, w, h);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.ellipse(x, y, w, h, 0, 0, 360);\n\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t}\n\n\t/**\n\t * Fill an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t */\n\tfillEllipse(x, y, w, h) {\n\t\tif (this._currentGradient) {\n\t\t\tthis.#gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillEllipse(x, y, w, h);\n\t\t\t\t},\n\t\t\t\tx - w,\n\t\t\t\ty - h,\n\t\t\t\tw * 2,\n\t\t\t\th * 2,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst segments = Math.max(\n\t\t\t8,\n\t\t\tMath.round((Math.PI * (w + h)) / this.path2D.arcResolution),\n\t\t);\n\t\tthis.currentBatcher.drawVertices(\n\t\t\tthis.gl.TRIANGLES,\n\t\t\tgenerateTriangleFan(x, y, w, h, 0, Math.PI * 2, segments),\n\t\t);\n\t}\n\n\t/**\n\t * Stroke a line of the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tstrokeLine(startX, startY, endX, endY) {\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst dash = this.renderState.lineDash;\n\t\tif (dash.length > 0) {\n\t\t\tconst segments = dashSegments(startX, startY, endX, endY, dash);\n\t\t\tif (segments.length > 0) {\n\t\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, segments);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.path2D.beginPath();\n\t\t\tthis.path2D.moveTo(startX, startY);\n\t\t\tthis.path2D.lineTo(endX, endY);\n\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t\t}\n\t}\n\n\t/**\n\t * Fill a line of the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tfillLine(startX, startY, endX, endY) {\n\t\tthis.strokeLine(startX, startY, endX, endY);\n\t}\n\n\t/**\n\t * Stroke a Polygon on the screen with a specified color\n\t * @param {Polygon} poly - the shape to draw\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokePolygon(poly, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillPolygon(poly);\n\t\t\treturn;\n\t\t}\n\t\tconst points = poly.points;\n\t\tconst len = points.length;\n\n\t\tthis.translate(poly.pos.x, poly.pos.y);\n\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tfor (let i = 0; i < len - 1; i++) {\n\t\t\tconst curPoint = points[i];\n\t\t\tconst nextPoint = points[i + 1];\n\t\t\tthis.path2D.moveTo(curPoint.x, curPoint.y);\n\t\t\tthis.path2D.lineTo(nextPoint.x, nextPoint.y);\n\t\t}\n\t\tthis.path2D.closePath();\n\t\tconst dash = this.renderState.lineDash;\n\t\tif (dash.length > 0) {\n\t\t\tconst pts = this.path2D.points;\n\t\t\tconst dashed = dashPath(pts, dash);\n\t\t\tif (dashed.length > 0) {\n\t\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, dashed);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t\t}\n\t\t// add round joins at vertices for thick lines\n\t\tif (this.lineWidth > 1) {\n\t\t\tconst radius = this.lineWidth / 2;\n\t\t\tconst joinPoints = [];\n\t\t\tfor (let i = 1; i < len; i++) {\n\t\t\t\tjoinPoints.push(points[i]);\n\t\t\t}\n\t\t\tconst lastPoint = points[len - 1];\n\t\t\tconst firstPoint = points[0];\n\t\t\tif (!lastPoint.equals(firstPoint)) {\n\t\t\t\tjoinPoints.push(firstPoint);\n\t\t\t}\n\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\tthis.gl.TRIANGLES,\n\t\t\t\tgenerateJoinCircles(joinPoints, radius),\n\t\t\t);\n\t\t}\n\n\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t}\n\n\t/**\n\t * Fill a me.Polygon on the screen\n\t * @param {Polygon} poly - the shape to draw\n\t */\n\tfillPolygon(poly) {\n\t\tif (this._currentGradient) {\n\t\t\tconst bounds = poly.getBounds();\n\t\t\t// translate to polygon's local space so gradient coords match\n\t\t\tthis.translate(poly.pos.x, poly.pos.y);\n\t\t\tthis.#gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\t// draw polygon vertices directly (already translated)\n\t\t\t\t\tthis.setBatcher(\"primitive\");\n\t\t\t\t\tconst indices = poly.getIndices();\n\t\t\t\t\tconst points = poly.points;\n\t\t\t\t\tconst verts = this._polyVerts;\n\t\t\t\t\tconst len = indices.length;\n\t\t\t\t\twhile (verts.length < len) {\n\t\t\t\t\t\tverts.push({ x: 0, y: 0 });\n\t\t\t\t\t}\n\t\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\t\tconst src = points[indices[i]];\n\t\t\t\t\t\tverts[i].x = src.x;\n\t\t\t\t\t\tverts[i].y = src.y;\n\t\t\t\t\t}\n\t\t\t\t\tthis.currentBatcher.drawVertices(this.gl.TRIANGLES, verts, len);\n\t\t\t\t},\n\t\t\t\t// use local bounds (subtract pos since getBounds includes it)\n\t\t\t\tbounds.x - poly.pos.x,\n\t\t\t\tbounds.y - poly.pos.y,\n\t\t\t\tbounds.width,\n\t\t\t\tbounds.height,\n\t\t\t);\n\t\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.translate(poly.pos.x, poly.pos.y);\n\t\tconst indices = poly.getIndices();\n\t\tconst points = poly.points;\n\t\tconst verts = this._polyVerts;\n\t\tconst len = indices.length;\n\n\t\t// grow the scratch array if needed\n\t\twhile (verts.length < len) {\n\t\t\tverts.push({ x: 0, y: 0 });\n\t\t}\n\n\t\t// copy point coordinates so drawVertices won't mutate the polygon\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tconst src = points[indices[i]];\n\t\t\tverts[i].x = src.x;\n\t\t\tverts[i].y = src.y;\n\t\t}\n\n\t\tthis.currentBatcher.drawVertices(this.gl.TRIANGLES, verts, len);\n\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t}\n\n\t/**\n\t * Draw a stroke rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRect(x, y, width, height, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillRect(x, y, width, height);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.rect(x, y, width, height);\n\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t\t// add round joins at corners for thick lines\n\t\tif (this.lineWidth > 1) {\n\t\t\tconst radius = this.lineWidth / 2;\n\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\tthis.gl.TRIANGLES,\n\t\t\t\tgenerateJoinCircles(\n\t\t\t\t\t[\n\t\t\t\t\t\t{ x, y },\n\t\t\t\t\t\t{ x: x + width, y },\n\t\t\t\t\t\t{ x: x + width, y: y + height },\n\t\t\t\t\t\t{ x, y: y + height },\n\t\t\t\t\t],\n\t\t\t\t\tradius,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Draw a filled rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t */\n\tfillRect(x, y, width, height) {\n\t\tif (this._currentGradient) {\n\t\t\t// toCanvas() calls invalidate() which flushes pending draws.\n\t\t\t// The bake is fixed-resolution: the returned width/height are the\n\t\t\t// SOURCE rect inside the shared canvas (scaled-down for large\n\t\t\t// rects), stretched back by the destination quad.\n\t\t\tconst baked = this._currentGradient.toCanvas(this, x, y, width, height);\n\t\t\tthis.drawImage(\n\t\t\t\tbaked.canvas,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tbaked.width,\n\t\t\t\tbaked.height,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\t// 2 triangles directly — avoids path2D + earcut overhead\n\t\tconst right = x + width;\n\t\tconst bottom = y + height;\n\t\tconst pts = this._rectTriangles;\n\t\tpts[0].x = x;\n\t\tpts[0].y = y;\n\t\tpts[1].x = right;\n\t\tpts[1].y = y;\n\t\tpts[2].x = x;\n\t\tpts[2].y = bottom;\n\t\tpts[3].x = right;\n\t\tpts[3].y = y;\n\t\tpts[4].x = right;\n\t\tpts[4].y = bottom;\n\t\tpts[5].x = x;\n\t\tpts[5].y = bottom;\n\t\tthis.currentBatcher.drawVertices(this.gl.TRIANGLES, pts);\n\t}\n\n\t/**\n\t * Stroke a rounded rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rounded rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rounded rectangle starting point.\n\t * @param {number} width - The rounded rectangle's width.\n\t * @param {number} height - The rounded rectangle's height.\n\t * @param {number} radius - The rounded corner's radius.\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRoundRect(x, y, width, height, radius, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillRoundRect(x, y, width, height, radius);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.roundRect(x, y, width, height, radius);\n\t\tthis.currentBatcher.drawVertices(this.gl.LINES, this.path2D.points);\n\t}\n\n\t/**\n\t * Draw a rounded filled rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rounded rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rounded rectangle starting point.\n\t * @param {number} width - The rounded rectangle's width.\n\t * @param {number} height - The rounded rectangle's height.\n\t * @param {number} radius - The rounded corner's radius.\n\t */\n\tfillRoundRect(x, y, width, height, radius) {\n\t\tif (this._currentGradient) {\n\t\t\tthis.#gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillRoundRect(x, y, width, height, radius);\n\t\t\t\t},\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst r = Math.min(radius, width / 2, height / 2);\n\t\tconst verts = [];\n\n\t\t// inner cross: 3 rects (6 triangles = 18 vertices)\n\t\t// horizontal bar (full width, inner height)\n\t\tverts.push(\n\t\t\t{ x, y: y + r },\n\t\t\t{ x: x + width, y: y + r },\n\t\t\t{ x, y: y + height - r },\n\t\t\t{ x: x + width, y: y + r },\n\t\t\t{ x: x + width, y: y + height - r },\n\t\t\t{ x, y: y + height - r },\n\t\t);\n\t\t// top bar (inner width, radius height)\n\t\tverts.push(\n\t\t\t{ x: x + r, y },\n\t\t\t{ x: x + width - r, y },\n\t\t\t{ x: x + r, y: y + r },\n\t\t\t{ x: x + width - r, y },\n\t\t\t{ x: x + width - r, y: y + r },\n\t\t\t{ x: x + r, y: y + r },\n\t\t);\n\t\t// bottom bar (inner width, radius height)\n\t\tverts.push(\n\t\t\t{ x: x + r, y: y + height - r },\n\t\t\t{ x: x + width - r, y: y + height - r },\n\t\t\t{ x: x + r, y: y + height },\n\t\t\t{ x: x + width - r, y: y + height - r },\n\t\t\t{ x: x + width - r, y: y + height },\n\t\t\t{ x: x + r, y: y + height },\n\t\t);\n\n\t\t// 4 corner arcs as triangle fans\n\t\tconst cornerSegments = Math.max(\n\t\t\t4,\n\t\t\tMath.round((Math.PI * r) / 2 / this.path2D.arcResolution),\n\t\t);\n\t\tconst PI = Math.PI;\n\t\tverts.push(\n\t\t\t...generateTriangleFan(x + r, y + r, r, r, PI, PI * 1.5, cornerSegments),\n\t\t); // top-left\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + width - r,\n\t\t\t\ty + r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\tPI * 1.5,\n\t\t\t\tPI * 2,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t); // top-right\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + width - r,\n\t\t\t\ty + height - r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\t0,\n\t\t\t\tPI * 0.5,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t); // bottom-right\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + r,\n\t\t\t\ty + height - r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\tPI * 0.5,\n\t\t\t\tPI,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t); // bottom-left\n\n\t\tthis.currentBatcher.drawVertices(this.gl.TRIANGLES, verts);\n\t}\n\n\t/**\n\t * Stroke a Point at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the point.\n\t * @param {number} y - y axis of the coordinate for the point.\n\t */\n\tstrokePoint(x, y) {\n\t\tthis.strokeLine(x, y, x + 1, y + 1);\n\t}\n\n\t/**\n\t * Draw a point at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the point.\n\t * @param {number} y - y axis of the coordinate for the point.\n\t */\n\tfillPoint(x, y) {\n\t\tthis.strokePoint(x, y);\n\t}\n\n\t/**\n\t * Draw a gradient-filled shape by masking with the shape and filling the bounding rect.\n\t * Temporarily disables the gradient to prevent recursion in the fill methods.\n\t * @param {Function} drawShape - draws the shape into the stencil buffer\n\t * @param {number} x - bounding rect x\n\t * @param {number} y - bounding rect y\n\t * @param {number} w - bounding rect width\n\t * @param {number} h - bounding rect height\n\t * @ignore\n\t */\n\t#gradientMask(drawShape, x, y, w, h) {\n\t\tconst gl = this.gl;\n\t\tconst grad = this._currentGradient;\n\t\tconst hasMask = this.maskLevel > 0;\n\t\tthis._currentGradient = null;\n\n\t\tthis.flush();\n\n\t\tgl.enable(gl.STENCIL_TEST);\n\t\tgl.colorMask(false, false, false, false);\n\n\t\t// The stencil value of currently-VISIBLE pixels, as installed by\n\t\t// setMask (its single source of truth). The shape's visible pixels\n\t\t// are tagged with a high-bit marker (mask levels only use the low 7\n\t\t// bits, so the marker can never collide with one), the gradient is\n\t\t// clipped to the marker, then the marker is cleared and setMask's\n\t\t// exact render test is re-installed.\n\t\tconst visibleRef = this._maskVisibleRef;\n\t\tlet markRef = 1;\n\n\t\tif (hasMask) {\n\t\t\tmarkRef = 0x80 | visibleRef;\n\t\t\t// tag: only pixels that are visible under the active mask\n\t\t\t// (low bits == visibleRef) get the marker written\n\t\t\tgl.stencilFunc(gl.EQUAL, markRef, 0x7f);\n\t\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n\t\t} else {\n\t\t\tgl.clear(gl.STENCIL_BUFFER_BIT);\n\t\t\tgl.stencilFunc(gl.ALWAYS, 1, 0xff);\n\t\t\tgl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);\n\t\t}\n\n\t\tdrawShape();\n\t\tthis.flush();\n\n\t\t// use stencil to clip the gradient to the marked pixels\n\t\tgl.colorMask(true, true, true, true);\n\t\tgl.stencilFunc(gl.EQUAL, markRef, 0xff);\n\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n\n\t\tthis._currentGradient = grad;\n\t\tthis.fillRect(x, y, w, h);\n\t\tthis.flush();\n\n\t\tif (hasMask) {\n\t\t\t// clear the marker: write the visible value back on the shape's\n\t\t\t// pixels (REPLACE writes the func ref — a no-op on unmarked\n\t\t\t// visible pixels, and it strips the high bit from marked ones)\n\t\t\tthis._currentGradient = null;\n\t\t\tgl.colorMask(false, false, false, false);\n\t\t\tgl.stencilFunc(gl.EQUAL, visibleRef, 0x7f);\n\t\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n\t\t\tdrawShape();\n\t\t\tthis.flush();\n\t\t\tgl.colorMask(true, true, true, true);\n\t\t\t// re-install the exact render test setMask had established\n\t\t\tgl.stencilFunc(gl.EQUAL, visibleRef, 0xff);\n\t\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n\t\t\tthis._currentGradient = grad;\n\t\t} else {\n\t\t\tgl.disable(gl.STENCIL_TEST);\n\t\t}\n\t}\n\n\t/**\n\t * Reset (overrides) the renderer transformation matrix to the\n\t * identity one, and then apply the given transformation matrix.\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix to transform by, or the a component to multiply the current matrix by\n\t * @param {number} b - the b component to multiply the current matrix by\n\t * @param {number} c - the c component to multiply the current matrix by\n\t * @param {number} d - the d component to multiply the current matrix by\n\t * @param {number} e - the e component to multiply the current matrix by\n\t * @param {number} f - the f component to multiply the current matrix by\n\t */\n\tsetTransform(a, b, c, d, e, f) {\n\t\tthis.resetTransform();\n\t\tthis.transform(a, b, c, d, e, f);\n\t}\n\n\t/**\n\t * Multiply given matrix into the renderer tranformation matrix\n\t * @see {@link WebGLRenderer.setTransform} which will reset the current transform matrix prior to performing the new transformation\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix to transform by, or the a component to multiply the current matrix by\n\t * @param {number} b - the b component to multiply the current matrix by\n\t * @param {number} c - the c component to multiply the current matrix by\n\t * @param {number} d - the d component to multiply the current matrix by\n\t * @param {number} e - the e component to multiply the current matrix by\n\t * @param {number} f - the f component to multiply the current matrix by\n\t */\n\ttransform(a, b, c, d, e, f) {\n\t\tif (typeof a === \"object\") {\n\t\t\t// accepts both Matrix2d and Matrix3d (no temporary copy needed)\n\t\t\tthis.currentTransform.multiply(a);\n\t\t} else {\n\t\t\t// individual 2D affine components\n\t\t\tthis.currentTransform.multiply(\n\t\t\t\t_tempMatrix.setTransform(\n\t\t\t\t\ta,\n\t\t\t\t\tb,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tc,\n\t\t\t\t\td,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\t1,\n\t\t\t\t\t0,\n\t\t\t\t\te,\n\t\t\t\t\tf,\n\t\t\t\t\t0,\n\t\t\t\t\t1,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// snap position values to pixel grid\n\t\t\tconst a = this.currentTransform.val;\n\t\t\ta[12] |= 0;\n\t\t\ta[13] |= 0;\n\t\t}\n\t}\n\n\t/**\n\t * adds a translation transformation to the current matrix.\n\t * @param {number} x - Distance to move in the horizontal direction. Positive values are to the right, and negative to the left.\n\t * @param {number} y - Distance to move in the vertical direction. Positive values are down, and negative are up.\n\t */\n\ttranslate(x, y) {\n\t\tthis.currentTransform.translate(x, y);\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// snap position values to pixel grid\n\t\t\tconst a = this.currentTransform.val;\n\t\t\ta[12] |= 0;\n\t\t\ta[13] |= 0;\n\t\t}\n\t}\n\n\t/**\n\t * clip the given region from the original canvas. Once a region is clipped,\n\t * all future drawing will be limited to the clipped region.\n\t * You can however save the current region using the save(),\n\t * and restore it (with the restore() method) any time in the future.\n\t * (<u>this is an experimental feature !</u>)\n\t * @param {number} x - x axis of the coordinate for the upper-left corner of the rectangle to start clipping from.\n\t * @param {number} y - y axis of the coordinate for the upper-left corner of the rectangle to start clipping from.\n\t * @param {number} width - the width of the rectangle to start clipping from.\n\t * @param {number} height - the height of the rectangle to start clipping from.\n\t */\n\tclipRect(x, y, width, height) {\n\t\tconst canvas = this.getCanvas();\n\t\tconst gl = this.gl;\n\n\t\t// `ColorLayer`-style hosts have `Infinity` dimensions + a 0.5\n\t\t// anchor, leaving `currentTransform.tx/ty = -Infinity`. Running\n\t\t// transform math on that produces NaN corners and a broken\n\t\t// `gl.scissor`. Treat a non-finite transform as \"no clip\".\n\t\tconst m = this.currentTransform;\n\t\tif (!Number.isFinite(m.tx) || !Number.isFinite(m.ty)) {\n\t\t\tif (this._scissorActive) {\n\t\t\t\t// drain pending vertices under the active scissor first;\n\t\t\t\t// otherwise they would flush later under no scissor.\n\t\t\t\tthis.flush();\n\t\t\t\tgl.disable(gl.SCISSOR_TEST);\n\t\t\t\tthis._scissorActive = false;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// derive the screen-space AABB by feeding the rect's 4 corners\n\t\t// through `currentTransform` via `Bounds.addFrame` (which short-\n\t\t// circuits the corner walk when the transform is identity).\n\t\t// `gl.scissor` is not transform-aware, so any rotation collapses\n\t\t// to the rotated-rect AABB on screen — Canvas's `context.clip()`\n\t\t// would produce a true polygonal clip, but downstream rendering\n\t\t// only observes the AABB anyway. Issue #1349.\n\t\tconst aabb = this._clipAABB;\n\t\taabb.clear();\n\t\taabb.addFrame(x, y, x + width, y + height, m);\n\t\tconst sx = Math.floor(aabb.min.x);\n\t\tconst sy = Math.floor(aabb.min.y);\n\t\tconst sw = Math.ceil(aabb.max.x - sx);\n\t\tconst sh = Math.ceil(aabb.max.y - sy);\n\n\t\t// If the resulting screen AABB covers the whole canvas, treat as\n\t\t// no-clip and disable the scissor. Caller intent: \"clip to the\n\t\t// full viewport\" → no scissor needed. Works regardless of the\n\t\t// transform that produced the AABB.\n\t\tif (\n\t\t\tsx <= 0 &&\n\t\t\tsy <= 0 &&\n\t\t\tsx + sw >= canvas.width &&\n\t\t\tsy + sh >= canvas.height\n\t\t) {\n\t\t\tif (this._scissorActive) {\n\t\t\t\tthis.flush();\n\t\t\t\tgl.disable(gl.SCISSOR_TEST);\n\t\t\t\tthis._scissorActive = false;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// `currentScissor` now stores screen-space coords directly so\n\t\t// `restore()` can re-apply without re-running the transform math.\n\t\tconst cs = this.currentScissor;\n\t\tif (\n\t\t\tthis._scissorActive &&\n\t\t\tcs[0] === sx &&\n\t\t\tcs[1] === sy &&\n\t\t\tcs[2] === sw &&\n\t\t\tcs[3] === sh\n\t\t) {\n\t\t\treturn; // already at the right scissor\n\t\t}\n\t\t// flush the batcher BEFORE enabling/changing scissor — pending\n\t\t// vertices were queued under the previous clip state and need to\n\t\t// drain there.\n\t\tthis.flush();\n\t\tgl.enable(gl.SCISSOR_TEST);\n\t\tthis._scissorActive = true;\n\t\t// GL scissor uses bottom-left origin\n\t\tgl.scissor(sx, canvas.height - sh - sy, sw, sh);\n\t\tcs[0] = sx;\n\t\tcs[1] = sy;\n\t\tcs[2] = sw;\n\t\tcs[3] = sh;\n\t}\n\n\t/**\n\t * A mask limits rendering elements to the shape and position of the given mask object.\n\t * If the drawing or rendering area is larger than the mask, only the intersecting part of the renderable will be visible.\n\t * (Note Mask are not preserved through renderer context save and restore and need so be manually cleared)\n\t * @see CanvasRenderer#clearMask\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] - a shape defining the mask to be applied\n\t * @param {boolean} [invert=false] - either the given shape should define what is visible (default) or the opposite\n\t */\n\tsetMask(mask, invert = false) {\n\t\tconst gl = this.gl;\n\n\t\t// flush the batcher\n\t\tthis.flush();\n\n\t\tif (this.maskLevel === 0) {\n\t\t\t// Enable and setup GL state to write to stencil buffer\n\t\t\tgl.enable(gl.STENCIL_TEST);\n\t\t\tgl.clear(gl.STENCIL_BUFFER_BIT);\n\t\t}\n\n\t\tthis.maskLevel++;\n\t\tif (this.maskLevel > 0x7f) {\n\t\t\t// mask levels live in the stencil's low 7 bits — the high bit is\n\t\t\t// reserved as #gradientMask's temporary marker, and an 8-bit\n\t\t\t// stencil couldn't represent deeper nesting anyway\n\t\t\tthis.maskLevel = 0x7f;\n\t\t\tif (this._maskDepthWarned !== true) {\n\t\t\t\tthis._maskDepthWarned = true;\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"melonJS: setMask nesting deeper than 127 — mask level clamped\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Write phase: increment stencil for the drawn shape's pixels so each\n\t\t// setMask call adds +1 to stencil inside the shape. This lets chained\n\t\t// inverted masks (e.g. multi-light cutouts) accumulate correctly:\n\t\t// stencil > 0 means \"covered by some mask shape\"; stencil == 0 means\n\t\t// \"outside every mask shape\".\n\t\tgl.colorMask(false, false, false, false);\n\t\tgl.stencilMask(0xff);\n\t\tgl.stencilFunc(gl.ALWAYS, 0, 0xff);\n\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);\n\n\t\t// fill the given mask shape\n\t\tthis.fill(mask);\n\n\t\t// flush the batcher\n\t\tthis.flush();\n\n\t\tgl.colorMask(true, true, true, true);\n\n\t\t// Render phase:\n\t\t// - invert=true: render where no mask shape covers (stencil == 0).\n\t\t//   With chained invert calls this naturally produces canvas \\ ⋃ shapes.\n\t\t// - invert=false: render where the just-added mask covers\n\t\t//   (stencil == maskLevel, i.e. the intersection of all masks so far).\n\t\tif (invert === true) {\n\t\t\tgl.stencilFunc(gl.EQUAL, 0, 0xff);\n\t\t} else {\n\t\t\tgl.stencilFunc(gl.EQUAL, this.maskLevel, 0xff);\n\t\t}\n\t\tgl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n\t\tthis._maskVisibleRef = invert === true ? 0 : this.maskLevel;\n\t}\n\n\t/**\n\t * disable (remove) the rendering mask set through setMask.\n\t * @see WebGLRenderer#setMask\n\t */\n\tclearMask() {\n\t\tif (this.maskLevel > 0) {\n\t\t\t// flush the batcher\n\t\t\tthis.flush();\n\t\t\tthis.maskLevel = 0;\n\t\t\tthis._maskVisibleRef = 0;\n\t\t\tthis.gl.disable(this.gl.STENCIL_TEST);\n\t\t}\n\t}\n}\n","import { game } from \"../application/application.ts\";\nimport { hasFullscreenSupport, isFullscreen } from \"../system/fullscreen.ts\";\nimport CanvasRenderer from \"../video/canvas/canvas_renderer.js\";\nimport CanvasRenderTarget from \"../video/rendertarget/canvasrendertarget.js\";\nimport { WebGLBatcher } from \"../video/webgl/batchers/batcher.js\";\nimport PrimitiveBatcher from \"../video/webgl/batchers/primitive_batcher.js\";\nimport QuadBatcher from \"../video/webgl/batchers/quad_batcher.js\";\nimport WebGLRenderer from \"../video/webgl/webgl_renderer.js\";\nimport { warning } from \"./console.js\";\n\n/*\n * placeholder for all deprecated classes and corresponding alias for backward compatibility\n */\n\n/**\n * @deprecated since 17.1.0\n * @see CanvasRenderTarget\n */\nexport class CanvasTexture extends CanvasRenderTarget {\n\t/**\n\t * @param {number} width - the desired width of the canvas\n\t * @param {number} height - the desired height of the canvas\n\t * @param {object} attributes - The attributes to create both the canvas and context\n\t * @param {boolean} [attributes.context=\"2d\"] - the context type to be created (\"2d\", \"webgl\" — creates a WebGL 2 context)\n\t * @param {boolean} [attributes.offscreenCanvas=false] - will create an offscreenCanvas if true instead of a standard canvas\n\t * @param {boolean} [attributes.willReadFrequently=false] - Indicates whether or not a lot of read-back operations are planned\n\t * @param {boolean} [attributes.antiAlias=false] - Whether to enable anti-aliasing, use false (default) for a pixelated effect.\n\t */\n\tconstructor(width, height, attributes) {\n\t\twarning(\"CanvasTexture\", \"CanvasRenderTarget\", \"17.1.0\");\n\t\tsuper(width, height, attributes);\n\t}\n}\n\n/**\n * return the height of the system Canvas\n * @public\n * @name setLineWidth\n * @memberof CanvasRenderer#\n * @deprecated since 17.3.0\n * @see lineWidth\n */\nCanvasRenderer.prototype.setLineWidth = function (width) {\n\twarning(\"setLineWidth\", \"lineWidth\", \"17.3.0\");\n\tthis.lineWidth = width;\n};\n\n/**\n * return the height of the system Canvas\n * @public\n * @name setLineWidth\n * @memberof WebGLRenderer#\n * @deprecated since 17.3.0\n * @see lineWidth\n */\nWebGLRenderer.prototype.setLineWidth = function (width) {\n\twarning(\"setLineWidth\", \"lineWidth\", \"17.3.0\");\n\tthis.lineWidth = width;\n};\n\n/**\n * @deprecated since 18.1.0\n * @see WebGLBatcher\n */\nexport class Compositor extends WebGLBatcher {\n\t/** @param {any[]} args */\n\tconstructor(...args) {\n\t\twarning(\"Compositor\", \"WebGLBatcher\", \"18.1.0\");\n\t\tsuper(...args);\n\t}\n}\n\n/**\n * @deprecated since 18.1.0\n * @see PrimitiveBatcher\n */\nexport class PrimitiveCompositor extends PrimitiveBatcher {\n\t/** @param {any[]} args */\n\tconstructor(...args) {\n\t\twarning(\"PrimitiveCompositor\", \"PrimitiveBatcher\", \"18.1.0\");\n\t\tsuper(...args);\n\t}\n}\n\n/**\n * @deprecated since 18.1.0\n * @see QuadBatcher\n */\nexport class QuadCompositor extends QuadBatcher {\n\t/** @param {any[]} args */\n\tconstructor(...args) {\n\t\twarning(\"QuadCompositor\", \"QuadBatcher\", \"18.1.0\");\n\t\tsuper(...args);\n\t}\n}\n\n/**\n * @deprecated since 18.1.0 — use currentBatcher instead\n */\nObject.defineProperty(WebGLRenderer.prototype, \"currentCompositor\", {\n\tget() {\n\t\twarning(\"currentCompositor\", \"currentBatcher\", \"18.1.0\");\n\t\treturn this.currentBatcher;\n\t},\n\tset(value) {\n\t\twarning(\"currentCompositor\", \"currentBatcher\", \"18.1.0\");\n\t\tthis.currentBatcher = value;\n\t},\n});\n\n/**\n * @deprecated since 18.1.0 — use batchers instead\n */\nObject.defineProperty(WebGLRenderer.prototype, \"compositors\", {\n\tget() {\n\t\twarning(\"compositors\", \"batchers\", \"18.1.0\");\n\t\treturn this.batchers;\n\t},\n\tset(value) {\n\t\twarning(\"compositors\", \"batchers\", \"18.1.0\");\n\t\tthis.batchers = value;\n\t},\n});\n\n/**\n * @deprecated since 18.1.0 — use addBatcher instead\n */\nWebGLRenderer.prototype.addCompositor = function (\n\tcompositor,\n\tname = \"default\",\n\tactivate = false,\n) {\n\twarning(\"addCompositor\", \"addBatcher\", \"18.1.0\");\n\treturn this.addBatcher(compositor, name, activate);\n};\n\n/**\n * @deprecated since 18.1.0 — use setBatcher instead\n */\nWebGLRenderer.prototype.setCompositor = function (name = \"default\", shader) {\n\twarning(\"setCompositor\", \"setBatcher\", \"18.1.0\");\n\treturn this.setBatcher(name, shader);\n};\n\n/**\n * @namespace Math\n * @deprecated since 18.0.0\n * Use lowercase `math` export instead.\n */\nexport * as Math from \"./../math/math.ts\";\n\n/**\n * Triggers a fullscreen request. Requires fullscreen support from the browser/device.\n *\n * Re-exported under `me.device.*` for backwards compatibility; the canonical\n * post-19.7 entry point is `Application#requestFullscreen`, which uses the\n * Application's own `parentElement` instead of the deprecated global-game canvas lookup.\n * @param {Element} [element] - the element to be set in full-screen mode\n * @deprecated since 19.7.0 — use {@link Application#requestFullscreen} instead.\n */\nexport function requestFullscreen(element) {\n\twarning(\n\t\t\"device.requestFullscreen\",\n\t\t\"Application#requestFullscreen\",\n\t\t\"19.7.0\",\n\t);\n\tif (!hasFullscreenSupport || isFullscreen()) {\n\t\treturn;\n\t}\n\tconst target = element ?? game.getParentElement();\n\tconst request =\n\t\ttarget.requestFullscreen ||\n\t\ttarget.webkitRequestFullscreen ||\n\t\ttarget.mozRequestFullScreen ||\n\t\ttarget.msRequestFullscreen;\n\tconst result = request?.call(target);\n\tif (result instanceof Promise) {\n\t\tresult.catch(console.error);\n\t}\n}\n\n/**\n * Exit fullscreen mode. Requires fullscreen support from the browser/device.\n *\n * Re-exported under `me.device.*` for backwards compatibility; the canonical\n * post-19.7 entry point is `Application#exitFullscreen`.\n * @deprecated since 19.7.0 — use {@link Application#exitFullscreen} instead.\n */\nexport function exitFullscreen() {\n\twarning(\"device.exitFullscreen\", \"Application#exitFullscreen\", \"19.7.0\");\n\tif (!hasFullscreenSupport || !isFullscreen()) {\n\t\treturn;\n\t}\n\tconst doc = globalThis.document;\n\tconst exit =\n\t\tdoc.exitFullscreen ||\n\t\tdoc.webkitExitFullscreen ||\n\t\tdoc.mozCancelFullScreen ||\n\t\tdoc.msExitFullscreen;\n\tconst result = exit?.call(doc);\n\tif (result instanceof Promise) {\n\t\tresult.catch(console.error);\n\t}\n}\n","import { Vector2d } from \"../math/vector2d.ts\";\nimport { Bounds } from \"./../physics/bounds.ts\";\nimport { globalToLocal } from \"./input.ts\";\nimport { _app, locked } from \"./pointerevent.ts\";\n\n/**\n * a temporary vector object\n * @ignore\n */\nconst tmpVec = new Vector2d();\n\n/**\n * a pointer object, representing a single finger on a touch enabled device.\n */\nclass Pointer extends Bounds {\n\t/**\n\t * constant for left button\n\t */\n\tLEFT: number;\n\n\t/**\n\t * constant for middle button\n\t */\n\tMIDDLE: number;\n\n\t/**\n\t * constant for right button\n\t */\n\tRIGHT: number;\n\n\t/**\n\t * the originating Event Object\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent}\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent}\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent}\n\t */\n\tevent: PointerEvent | TouchEvent | MouseEvent | undefined;\n\n\t/**\n\t * a string containing the event's type.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/type}\n\t */\n\toverride type: string = \"\";\n\n\t/**\n\t * the button property indicates which button was pressed on the mouse to trigger the event.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button}\n\t */\n\tbutton: number;\n\n\t/**\n\t * indicates whether or not the pointer device that created the event is the primary pointer.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary}\n\t */\n\tisPrimary: boolean;\n\n\t/**\n\t * the horizontal coordinate at which the event occurred, relative to the left edge of the entire document.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX}\n\t */\n\tpageX: number;\n\n\t/**\n\t * the vertical coordinate at which the event occurred, relative to the left edge of the entire document.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY}\n\t */\n\tpageY: number;\n\n\t/**\n\t * the horizontal coordinate within the application's client area at which the event occurred\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX}\n\t */\n\tclientX: number;\n\n\t/**\n\t * the vertical coordinate within the application's client area at which the event occurred\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY}\n\t */\n\tclientY: number;\n\n\t/**\n\t * the difference in the X coordinate of the pointer since the previous move event\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX}\n\t */\n\tmovementX: number;\n\n\t/**\n\t * the difference in the Y coordinate of the pointer since the previous move event\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementY}\n\t */\n\tmovementY: number;\n\n\t/**\n\t * an unsigned long representing the unit of the delta values scroll amount\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode}\n\t */\n\tdeltaMode: number;\n\n\t/**\n\t * a double representing the horizontal scroll amount in the Wheel Event deltaMode unit.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaX}\n\t */\n\tdeltaX: number;\n\n\t/**\n\t * a double representing the vertical scroll amount in the Wheel Event deltaMode unit.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY}\n\t */\n\tdeltaY: number;\n\n\t/**\n\t * a double representing the scroll amount in the z-axis, in the Wheel Event deltaMode unit.\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaZ}\n\t */\n\tdeltaZ: number;\n\n\t/**\n\t * Event normalized X coordinate within the game canvas itself<br>\n\t * <img src=\"images/event_coord.png\"/>\n\t */\n\tgameX: number;\n\n\t/**\n\t * Event normalized Y coordinate within the game canvas itself<br>\n\t * <img src=\"images/event_coord.png\"/>\n\t */\n\tgameY: number;\n\n\t/**\n\t * Event X coordinate relative to the viewport\n\t */\n\tgameScreenX: number;\n\n\t/**\n\t * Event Y coordinate relative to the viewport\n\t */\n\tgameScreenY: number;\n\n\t/**\n\t * Event X coordinate relative to the map\n\t */\n\tgameWorldX: number;\n\n\t/**\n\t * Event Y coordinate relative to the map\n\t */\n\tgameWorldY: number;\n\n\t/**\n\t * Event X coordinate relative to the holding container\n\t */\n\tgameLocalX: number;\n\n\t/**\n\t * Event Y coordinate relative to the holding container\n\t */\n\tgameLocalY: number;\n\n\t/**\n\t * The unique identifier of the contact for a touch, mouse or pen\n\t * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId}\n\t */\n\tpointerId: number | undefined;\n\n\t/**\n\t * true if not originally a pointer event\n\t */\n\tisNormalized: boolean;\n\n\t/**\n\t * true if the pointer is currently locked\n\t */\n\tlocked: boolean;\n\n\t// bind list for mouse buttons\n\tbind: (number | null)[];\n\n\t/**\n\t * @ignore\n\t */\n\tconstructor(x: number = 0, y: number = 0, w: number = 1, h: number = 1) {\n\t\t// parent constructor\n\t\tsuper();\n\n\t\t// initial coordinates/size\n\t\tthis.setMinMax(x, y, x + w, y + h);\n\n\t\tthis.LEFT = 0;\n\t\tthis.MIDDLE = 1;\n\t\tthis.RIGHT = 2;\n\n\t\tthis.event = undefined;\n\t\tthis.type = \"\";\n\t\tthis.button = 0;\n\t\tthis.isPrimary = false;\n\t\tthis.pageX = 0;\n\t\tthis.pageY = 0;\n\t\tthis.clientX = 0;\n\t\tthis.clientY = 0;\n\t\tthis.movementX = 0;\n\t\tthis.movementY = 0;\n\t\tthis.deltaMode = 0;\n\t\tthis.deltaX = 0;\n\t\tthis.deltaY = 0;\n\t\tthis.deltaZ = 0;\n\t\tthis.gameX = 0;\n\t\tthis.gameY = 0;\n\t\tthis.gameScreenX = 0;\n\t\tthis.gameScreenY = 0;\n\t\tthis.gameWorldX = 0;\n\t\tthis.gameWorldY = 0;\n\t\tthis.gameLocalX = 0;\n\t\tthis.gameLocalY = 0;\n\t\tthis.pointerId = undefined;\n\t\tthis.isNormalized = false;\n\t\tthis.locked = false;\n\n\t\t// bind list for mouse buttons\n\t\tthis.bind = [0, 0, 0];\n\t}\n\n\t/**\n\t * initialize the Pointer object using the given Event Object\n\t * @param event - the original Event object\n\t * @param pageX - the horizontal coordinate at which the event occurred, relative to the left edge of the entire document\n\t * @param pageY - the vertical coordinate at which the event occurred, relative to the left edge of the entire document\n\t * @param clientX - the horizontal coordinate within the application's client area at which the event occurred\n\t * @param clientY - the vertical coordinate within the application's client area at which the event occurred\n\t * @param pointerId - the Pointer, Touch or Mouse event Id (1)\n\t */\n\tsetEvent(\n\t\tevent: Event,\n\t\tpageX: number = 0,\n\t\tpageY: number = 0,\n\t\tclientX: number = 0,\n\t\tclientY: number = 0,\n\t\tpointerId: number = 1,\n\t): void {\n\t\t// the original event object\n\t\tthis.event = event as PointerEvent | TouchEvent | MouseEvent;\n\n\t\tthis.pageX = pageX;\n\t\tthis.pageY = pageY;\n\t\tthis.clientX = clientX;\n\t\tthis.clientY = clientY;\n\n\t\t// translate to local coordinates\n\t\tglobalToLocal(this.pageX, this.pageY, tmpVec);\n\t\tthis.gameScreenX = this.x = tmpVec.x;\n\t\tthis.gameScreenY = this.y = tmpVec.y;\n\n\t\t// true if not originally a pointer event\n\t\tthis.isNormalized =\n\t\t\ttypeof globalThis.PointerEvent !== \"undefined\" &&\n\t\t\t!(event instanceof globalThis.PointerEvent);\n\n\t\tthis.locked = locked;\n\t\tthis.movementX = (event as any).movementX || 0;\n\t\tthis.movementY = (event as any).movementY || 0;\n\n\t\tif (event.type === \"wheel\") {\n\t\t\tthis.deltaMode = (event as any).deltaMode || 0;\n\t\t\tthis.deltaX = (event as any).deltaX || 0;\n\t\t\tthis.deltaY = (event as any).deltaY || 0;\n\t\t\tthis.deltaZ = (event as any).deltaZ || 0;\n\t\t} else {\n\t\t\tthis.deltaMode = 0;\n\t\t\tthis.deltaX = 0;\n\t\t\tthis.deltaY = 0;\n\t\t\tthis.deltaZ = 0;\n\t\t}\n\n\t\tthis.pointerId = pointerId;\n\n\t\tthis.isPrimary =\n\t\t\ttypeof (event as any).isPrimary !== \"undefined\"\n\t\t\t\t? (event as any).isPrimary\n\t\t\t\t: true;\n\n\t\t// in case of touch events, button is not defined\n\t\tthis.button = (event as any).button || 0;\n\n\t\tthis.type = event.type;\n\n\t\t// get the current screen to game world offset\n\t\tif (typeof _app?.viewport !== \"undefined\") {\n\t\t\t_app.viewport.localToWorld(this.gameScreenX, this.gameScreenY, tmpVec);\n\t\t}\n\n\t\t/* Initialize the two coordinate space properties. */\n\t\tthis.gameWorldX = tmpVec.x;\n\t\tthis.gameWorldY = tmpVec.y;\n\n\t\t// get the pointer size\n\t\tif (!this.isNormalized) {\n\t\t\t// native PointerEvent\n\t\t\tthis.width = (event as any).width || 1;\n\t\t\tthis.height = (event as any).height || 1;\n\t\t} else if (typeof (event as any).radiusX === \"number\") {\n\t\t\t// TouchEvent\n\t\t\tthis.width = (event as any).radiusX * 2 || 1;\n\t\t\tthis.height = (event as any).radiusY * 2 || 1;\n\t\t} else {\n\t\t\tthis.width = this.height = 1;\n\t\t}\n\t}\n}\n\nexport default Pointer;\n","import type Application from \"../application/application.ts\";\nimport { Rect } from \"./../geometries/rectangle.ts\";\nimport type { Vector2d } from \"../math/vector2d.ts\";\nimport { vector2dPool } from \"../math/vector2d.ts\";\nimport * as device from \"./../system/device.js\";\nimport {\n\temit,\n\tGAME_INIT,\n\ton,\n\tPOINTERLOCKCHANGE,\n\tPOINTERMOVE,\n} from \"../system/event.ts\";\nimport timer from \"./../system/timer.ts\";\nimport { remove } from \"./../utils/array.ts\";\nimport { throttle } from \"./../utils/function.ts\";\nimport { preventDefault } from \"./input.ts\";\nimport { getBindingKey, triggerKeyEvent } from \"./keyboard.ts\";\nimport Pointer from \"./pointer.ts\";\n\ninterface PointerHandler {\n\tregion: any;\n\tcallbacks: Record<string, Array<(pointer: Pointer) => boolean | void>>;\n\tpointerId: number | null;\n}\n\n/**\n * A pool of `Pointer` objects to cache pointer/touch event coordinates.\n * @ignore\n */\nconst T_POINTERS: Pointer[] = [];\n\n// list of registered Event handlers\nconst eventHandlers: Map<any, PointerHandler> = new Map();\n\n// a cache rect represeting the current pointer area\nlet currentPointer: Rect;\n\n/**\n * reference to the active application instance\n * @ignore\n */\nexport let _app: Application;\non(GAME_INIT, (app: Application) => {\n\t_app = app;\n});\n\n// some useful flags\nlet pointerInitialized = false;\n\n// Track last event timestamp to prevent firing events out of order\nlet lastTimeStamp = 0;\n\n// \"active\" list of supported events\nlet activeEventList: string[] = [];\n\n// internal constants\nconst WHEEL = [\"wheel\"];\nconst POINTER_MOVE = [\"pointermove\", \"mousemove\", \"touchmove\"];\nconst POINTER_DOWN = [\"pointerdown\", \"mousedown\", \"touchstart\"];\nconst POINTER_UP = [\"pointerup\", \"mouseup\", \"touchend\"];\nconst POINTER_CANCEL = [\"pointercancel\", \"mousecancel\", \"touchcancel\"];\nconst POINTER_ENTER = [\"pointerenter\", \"mouseenter\", \"touchenter\"];\nconst POINTER_OVER = [\"pointerover\", \"mouseover\", \"touchover\"];\nconst POINTER_LEAVE = [\"pointerleave\", \"mouseleave\", \"touchleave\"];\n\n// list of standard pointer event type\nconst pointerEventList: string[] = [\n\tWHEEL[0],\n\tPOINTER_MOVE[0],\n\tPOINTER_DOWN[0],\n\tPOINTER_UP[0],\n\tPOINTER_CANCEL[0],\n\tPOINTER_ENTER[0],\n\tPOINTER_OVER[0],\n\tPOINTER_LEAVE[0],\n];\n\n// legacy mouse event type\nconst mouseEventList: string[] = [\n\tWHEEL[0],\n\tPOINTER_MOVE[1],\n\tPOINTER_DOWN[1],\n\tPOINTER_UP[1],\n\tPOINTER_CANCEL[1],\n\tPOINTER_ENTER[1],\n\tPOINTER_OVER[1],\n\tPOINTER_LEAVE[1],\n];\n\n// iOS style touch event type\nconst touchEventList: string[] = [\n\tPOINTER_MOVE[2],\n\tPOINTER_DOWN[2],\n\tPOINTER_UP[2],\n\tPOINTER_CANCEL[2],\n\tPOINTER_ENTER[2],\n\tPOINTER_OVER[2],\n\tPOINTER_LEAVE[2],\n];\n\nconst pointerEventMap: Record<string, string[]> = {\n\twheel: WHEEL,\n\tpointermove: POINTER_MOVE,\n\tpointerdown: POINTER_DOWN,\n\tpointerup: POINTER_UP,\n\tpointercancel: POINTER_CANCEL,\n\tpointerenter: POINTER_ENTER,\n\tpointerover: POINTER_OVER,\n\tpointerleave: POINTER_LEAVE,\n};\n\n/**\n * Array of normalized events (mouse, touch, pointer)\n * @ignore\n */\nconst normalizedEvents: Pointer[] = [];\n\n/**\n * addEventListerner for the specified event list and callback\n * @ignore\n */\nfunction registerEventListener(\n\teventList: string[],\n\tcallback: EventListenerOrEventListenerObject,\n): void {\n\tfor (let x = 0; x < eventList.length; x++) {\n\t\tif (POINTER_MOVE.indexOf(eventList[x]) === -1) {\n\t\t\tpointerEventTarget!.addEventListener(eventList[x], callback, {\n\t\t\t\tpassive: !preventDefault,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * enable pointer event (Pointer/Mouse/Touch)\n * @ignore\n */\nfunction enablePointerEvent(): void {\n\tif (!pointerInitialized) {\n\t\tif (!_app) {\n\t\t\tthrow new Error(\"Pointer events require an initialized Application\");\n\t\t}\n\t\t// the current pointer area\n\t\tcurrentPointer = new Rect(0, 0, 1, 1);\n\n\t\t// instantiate a pool of pointer catched\n\t\tfor (let v = 0; v < device.maxTouchPoints; v++) {\n\t\t\tT_POINTERS.push(new Pointer());\n\t\t}\n\n\t\tif (pointerEventTarget === null || pointerEventTarget === undefined) {\n\t\t\t// default pointer event target\n\t\t\tpointerEventTarget = _app.renderer.getCanvas();\n\t\t}\n\n\t\tif (device.pointerEvent) {\n\t\t\t// standard Pointer Events\n\t\t\tactiveEventList = pointerEventList;\n\t\t} else {\n\t\t\t// Regular Mouse events\n\t\t\tactiveEventList = mouseEventList;\n\t\t}\n\t\tif (device.touch && !device.pointerEvent) {\n\t\t\t// touch event on mobile devices\n\t\t\tactiveEventList = activeEventList.concat(touchEventList);\n\t\t}\n\t\tregisterEventListener(activeEventList, onPointerEvent);\n\n\t\t// set the PointerMove/touchMove/MouseMove event\n\t\tif (typeof throttlingInterval === \"undefined\") {\n\t\t\t// set the default value\n\t\t\tthrottlingInterval = ~~(1000 / timer.maxfps);\n\t\t}\n\n\t\tif (device.autoFocus) {\n\t\t\tdevice.focus();\n\t\t\tpointerEventTarget.addEventListener(\n\t\t\t\tactiveEventList[2], // MOUSE/POINTER DOWN\n\t\t\t\t() => {\n\t\t\t\t\tdevice.focus();\n\t\t\t\t},\n\t\t\t\t{ passive: !preventDefault },\n\t\t\t);\n\t\t}\n\n\t\t// if time interval <= 16, disable the feature\n\t\tconst events = findAllActiveEvents(activeEventList, POINTER_MOVE);\n\t\tif (throttlingInterval < 17) {\n\t\t\tfor (let i = 0; i < events.length; i++) {\n\t\t\t\tif (activeEventList.indexOf(events[i]) !== -1) {\n\t\t\t\t\tpointerEventTarget.addEventListener(\n\t\t\t\t\t\tevents[i],\n\t\t\t\t\t\tonMoveEvent,\n\t\t\t\t\t\t{ passive: true }, // do not preventDefault on Move events\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (let i = 0; i < events.length; i++) {\n\t\t\t\tif (activeEventList.indexOf(events[i]) !== -1) {\n\t\t\t\t\tpointerEventTarget.addEventListener(\n\t\t\t\t\t\tevents[i],\n\t\t\t\t\t\tthrottle(onMoveEvent, throttlingInterval),\n\t\t\t\t\t\t{ passive: true }, // do not preventDefault on Move events\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// disable all gesture by default\n\t\tsetTouchAction(pointerEventTarget as HTMLCanvasElement);\n\n\t\t// set a on change listener on pointerlock if supported\n\t\tif (device.hasPointerLockSupport) {\n\t\t\tglobalThis.document.addEventListener(\n\t\t\t\tPOINTERLOCKCHANGE,\n\t\t\t\t() => {\n\t\t\t\t\t// change the locked status accordingly\n\t\t\t\t\tlocked =\n\t\t\t\t\t\tglobalThis.document.pointerLockElement === _app.getParentElement();\n\t\t\t\t\t// emit the corresponding internal event\n\t\t\t\t\temit(POINTERLOCKCHANGE, locked);\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\n\t\t// all done !\n\t\tpointerInitialized = true;\n\t}\n}\n\n/**\n * @ignore\n */\nfunction findActiveEvent(\n\tactiveEventList: string[],\n\teventTypes: string[],\n): string | undefined {\n\tfor (let i = 0; i < eventTypes.length; i++) {\n\t\tconst event = activeEventList.indexOf(eventTypes[i]);\n\t\tif (event !== -1) {\n\t\t\treturn eventTypes[i];\n\t\t}\n\t}\n}\n\n/**\n * @ignore\n */\nfunction findAllActiveEvents(\n\tactiveEventList: string[],\n\teventTypes: string[],\n): string[] {\n\tconst events: string[] = [];\n\tfor (let i = 0; i < eventTypes.length; i++) {\n\t\tconst event = activeEventList.indexOf(eventTypes[i]);\n\t\tif (event !== -1) {\n\t\t\tevents.push(eventTypes[i]);\n\t\t}\n\t}\n\n\treturn events;\n}\n\n/**\n * @ignore\n */\nfunction triggerEvent(\n\thandlers: PointerHandler,\n\ttype: string | undefined,\n\tpointer: Pointer,\n\tpointerId: number | null,\n): boolean {\n\tlet callback;\n\tif (type && handlers.callbacks[type]) {\n\t\thandlers.pointerId = pointerId;\n\t\tfor (\n\t\t\tlet i = handlers.callbacks[type].length - 1;\n\t\t\ti >= 0 && (callback = handlers.callbacks[type][i]);\n\t\t\ti--\n\t\t) {\n\t\t\tif (callback(pointer) === false) {\n\t\t\t\t// stop propagating the event if return false\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * propagate events to registered objects\n * @ignore\n */\nfunction dispatchEvent(normalizedEvents: Pointer[]): boolean {\n\tlet handled = false;\n\n\twhile (normalizedEvents.length > 0) {\n\t\t// keep a reference to the last item\n\t\tconst pointer = normalizedEvents.pop()!;\n\t\t// and put it back into our cache\n\t\tT_POINTERS.push(pointer);\n\n\t\t// Do not fire older touch events (not required for PointerEvent type)\n\t\tif (\n\t\t\tpointer.isNormalized &&\n\t\t\ttypeof pointer.event!.timeStamp !== \"undefined\"\n\t\t) {\n\t\t\tif (pointer.event!.timeStamp < lastTimeStamp) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlastTimeStamp = pointer.event!.timeStamp;\n\t\t}\n\n\t\tcurrentPointer.pos.set(pointer.gameWorldX, pointer.gameWorldY);\n\t\tcurrentPointer.setSize(pointer.width, pointer.height);\n\n\t\t// trigger a global event for pointer move\n\t\tif (POINTER_MOVE.includes(pointer.type)) {\n\t\t\tpointer.gameX = pointer.gameLocalX = pointer.gameScreenX;\n\t\t\tpointer.gameY = pointer.gameLocalY = pointer.gameScreenY;\n\t\t\temit(POINTERMOVE, pointer);\n\t\t}\n\n\t\t// fetch valid candiates from the game world container\n\t\tlet candidates = _app.world.broadphase.retrieve(\n\t\t\tcurrentPointer,\n\t\t\t(a: any, b: any) => _app.world._sortReverseZ(a, b),\n\t\t\tundefined,\n\t\t);\n\n\t\t// add the main game viewport to the list of candidates\n\t\tcandidates = candidates.concat([_app.viewport]);\n\n\t\tfor (\n\t\t\tlet c = candidates.length, candidate;\n\t\t\tc--, (candidate = candidates[c]);\n\t\t) {\n\t\t\tif (eventHandlers.has(candidate) && candidate.isKinematic !== true) {\n\t\t\t\tconst handlers = eventHandlers.get(candidate)!;\n\t\t\t\tconst region = handlers.region;\n\t\t\t\tconst ancestor = region.ancestor;\n\t\t\t\tconst bounds = region.getBounds();\n\n\t\t\t\tif (region.isFloating === true) {\n\t\t\t\t\tpointer.gameX = pointer.gameLocalX = pointer.gameScreenX;\n\t\t\t\t\tpointer.gameY = pointer.gameLocalY = pointer.gameScreenY;\n\t\t\t\t} else {\n\t\t\t\t\tpointer.gameX = pointer.gameLocalX = pointer.gameWorldX;\n\t\t\t\t\tpointer.gameY = pointer.gameLocalY = pointer.gameWorldY;\n\t\t\t\t}\n\n\t\t\t\t// adjust gameLocalX to specify coordinates\n\t\t\t\t// within the region ancestor container\n\t\t\t\tif (typeof ancestor !== \"undefined\") {\n\t\t\t\t\tconst parentBounds = ancestor.getBounds();\n\t\t\t\t\tpointer.gameLocalX = pointer.gameX - parentBounds.x;\n\t\t\t\t\tpointer.gameLocalY = pointer.gameY - parentBounds.y;\n\t\t\t\t}\n\n\t\t\t\tconst eventInBounds = bounds.contains(pointer.gameX, pointer.gameY);\n\n\t\t\t\tswitch (pointer.type) {\n\t\t\t\t\tcase POINTER_MOVE[0]:\n\t\t\t\t\tcase POINTER_MOVE[1]:\n\t\t\t\t\tcase POINTER_MOVE[2]:\n\t\t\t\t\tcase POINTER_MOVE[3]:\n\t\t\t\t\t\t// moved out of bounds: trigger the POINTER_LEAVE callbacks\n\t\t\t\t\t\tif (handlers.pointerId === pointer.pointerId && !eventInBounds) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\ttriggerEvent(\n\t\t\t\t\t\t\t\t\thandlers,\n\t\t\t\t\t\t\t\t\tfindActiveEvent(activeEventList, POINTER_LEAVE),\n\t\t\t\t\t\t\t\t\tpointer,\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// no pointer & moved inside of bounds: trigger the POINTER_ENTER callbacks\n\t\t\t\t\t\telse if (handlers.pointerId === null && eventInBounds) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\ttriggerEvent(\n\t\t\t\t\t\t\t\t\thandlers,\n\t\t\t\t\t\t\t\t\tfindActiveEvent(activeEventList, POINTER_ENTER),\n\t\t\t\t\t\t\t\t\tpointer,\n\t\t\t\t\t\t\t\t\tpointer.pointerId!,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// trigger the POINTER_MOVE callbacks\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\teventInBounds &&\n\t\t\t\t\t\t\ttriggerEvent(handlers, pointer.type, pointer, pointer.pointerId!)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase POINTER_UP[0]:\n\t\t\t\t\tcase POINTER_UP[1]:\n\t\t\t\t\tcase POINTER_UP[2]:\n\t\t\t\t\tcase POINTER_UP[3]:\n\t\t\t\t\t\t// pointer defined & inside of bounds: trigger the POINTER_UP callback\n\t\t\t\t\t\tif (handlers.pointerId === pointer.pointerId && eventInBounds) {\n\t\t\t\t\t\t\t// trigger the corresponding callback\n\t\t\t\t\t\t\tif (triggerEvent(handlers, pointer.type, pointer, null)) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase POINTER_CANCEL[0]:\n\t\t\t\t\tcase POINTER_CANCEL[1]:\n\t\t\t\t\tcase POINTER_CANCEL[2]:\n\t\t\t\t\tcase POINTER_CANCEL[3]:\n\t\t\t\t\t\t// pointer defined: trigger the POINTER_CANCEL callback\n\t\t\t\t\t\tif (handlers.pointerId === pointer.pointerId) {\n\t\t\t\t\t\t\t// trigger the corresponding callback\n\t\t\t\t\t\t\tif (triggerEvent(handlers, pointer.type, pointer, null)) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// event inside of bounds: trigger the POINTER_DOWN or WHEEL callback\n\t\t\t\t\t\tif (eventInBounds) {\n\t\t\t\t\t\t\t// trigger the corresponding callback\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\ttriggerEvent(\n\t\t\t\t\t\t\t\t\thandlers,\n\t\t\t\t\t\t\t\t\tpointer.type,\n\t\t\t\t\t\t\t\t\tpointer,\n\t\t\t\t\t\t\t\t\tpointer.pointerId!,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (handled) {\n\t\t\t\t// stop iterating through this list of candidates\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn handled;\n}\n\n/**\n * translate event coordinates\n * @ignore\n */\nfunction normalizeEvent(originalEvent: any): Pointer[] {\n\tlet _pointer: Pointer;\n\n\t// PointerEvent or standard Mouse event\n\tif (device.touchEvent && originalEvent.changedTouches) {\n\t\t// iOS/Android Touch event\n\t\tfor (let i = 0, l = originalEvent.changedTouches.length; i < l; i++) {\n\t\t\tconst touchEvent = originalEvent.changedTouches[i];\n\t\t\t_pointer = T_POINTERS.pop()!;\n\t\t\t_pointer.setEvent(\n\t\t\t\toriginalEvent,\n\t\t\t\ttouchEvent.pageX,\n\t\t\t\ttouchEvent.pageY,\n\t\t\t\ttouchEvent.clientX,\n\t\t\t\ttouchEvent.clientY,\n\t\t\t\ttouchEvent.identifier,\n\t\t\t);\n\t\t\tnormalizedEvents.push(_pointer);\n\t\t}\n\t} else {\n\t\t// Mouse or PointerEvent\n\t\t_pointer = T_POINTERS.pop()!;\n\t\t_pointer.setEvent(\n\t\t\toriginalEvent,\n\t\t\toriginalEvent.pageX,\n\t\t\toriginalEvent.pageY,\n\t\t\toriginalEvent.clientX,\n\t\t\toriginalEvent.clientY,\n\t\t\toriginalEvent.pointerId,\n\t\t);\n\t\tnormalizedEvents.push(_pointer);\n\t}\n\n\t// if event.isPrimary is defined and false, return\n\tif (originalEvent.isPrimary === false) {\n\t\treturn normalizedEvents;\n\t}\n\n\t// else use the first entry to simulate mouse event\n\tnormalizedEvents[0].isPrimary = true;\n\tObject.assign(pointer, normalizedEvents[0]);\n\n\treturn normalizedEvents;\n}\n\n/**\n * mouse/touch/pointer event management (move)\n * @ignore\n */\nfunction onMoveEvent(e: Event): void {\n\t// dispatch mouse event to registered object\n\tdispatchEvent(normalizeEvent(e));\n\t// do not prevent default on moveEvent :\n}\n\n/**\n * mouse/touch/pointer event management (start/down, end/up)\n * @ignore\n */\nfunction onPointerEvent(e: Event): void {\n\t// normalize eventTypes\n\tnormalizeEvent(e);\n\n\t// remember/use the first \"primary\" normalized event for pointer.bind\n\tconst button = normalizedEvents[0].button;\n\n\t// dispatch event to registered objects\n\tif (dispatchEvent(normalizedEvents) || e.type === \"wheel\") {\n\t\t// always preventDefault for wheel event (?legacy code/behavior?)\n\t\tif (preventDefault) {\n\t\t\te.preventDefault();\n\t\t}\n\t}\n\n\tconst keycode = pointer.bind[button];\n\n\t// check if mapped to a key\n\tif (keycode) {\n\t\ttriggerKeyEvent(keycode, POINTER_DOWN.includes(e.type), button + 1);\n\t}\n}\n\n/*\n * PUBLIC STUFF\n */\n\n/**\n * the default target element for pointer events (usually the canvas element in which the game is rendered)\n */\nexport let pointerEventTarget: EventTarget | null = null;\n\n/**\n * Pointer information (current position and size)\n */\nexport const pointer: Pointer = new Pointer(0, 0, 1, 1);\n\n/**\n * indicates if the pointer is currently locked\n */\nexport let locked: boolean = false;\n\n/**\n * time interval for event throttling in milliseconds<br>\n * default value : \"1000/me.timer.maxfps\" ms<br>\n * set to 0 ms to disable the feature\n */\nexport let throttlingInterval: number | undefined;\n\n/**\n * return true if there are pending pointer events in the queue\n * @returns true if there are pending events\n */\nexport function hasActiveEvents(): boolean {\n\treturn normalizedEvents.length > 0;\n}\n\n/**\n * return true if there are register pointer events\n * @see {@link registerPointerEvent}\n * @returns true if there are pending events\n */\nexport function hasRegisteredEvents(): boolean {\n\treturn eventHandlers.size > 0;\n}\n\n/**\n * Translate the specified x and y values from the global (absolute)\n * coordinate to local (viewport) relative coordinate.\n * @param x - the global x coordinate to be translated.\n * @param y - the global y coordinate to be translated.\n * @param v - an optional vector object where to set the translated coordinates\n * @returns A vector object with the corresponding translated coordinates\n * @example\n * onMouseEvent : function (pointer) {\n *    // convert the given into local (viewport) relative coordinates\n *    let pos = me.input.globalToLocal(pointer.clientX, pointer.clientY);\n *    // do something with pos !\n * };\n */\nexport function globalToLocal(x: number, y: number, v?: Vector2d): Vector2d {\n\tv = v || vector2dPool.get();\n\tconst rect = device.getElementBounds(_app.renderer.getCanvas());\n\tconst pixelRatio = globalThis.devicePixelRatio || 1;\n\tx -= rect.left + (globalThis.pageXOffset || 0);\n\ty -= rect.top + (globalThis.pageYOffset || 0);\n\tconst scale = _app.renderer.scaleRatio;\n\tif (scale.x !== 1.0 || scale.y !== 1.0) {\n\t\tx /= scale.x;\n\t\ty /= scale.y;\n\t}\n\treturn v.set(x * pixelRatio, y * pixelRatio);\n}\n\n/**\n * enable/disable all gestures on the given element.<br>\n * by default melonJS will disable browser handling of all panning and zooming gestures.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action}\n * @param element - the HTML element to configure\n * @param value - the touch-action CSS value\n */\nexport function setTouchAction(\n\telement: HTMLCanvasElement,\n\tvalue?: string,\n): void {\n\telement.style[\"touch-action\" as any] = value || \"none\";\n}\n\n/**\n * Associate a pointer event to a keycode<br>\n * Left button -- 0\n * Middle button -- 1\n * Right button -- 2\n * @param args - button and/or keyCode\n * @example\n * // enable the keyboard\n * me.input.bindKey(me.input.KEY.X, \"shoot\");\n * // map the left button click on the X key (default if the button is not specified)\n * me.input.bindPointer(me.input.KEY.X);\n * // map the right button click on the X key\n * me.input.bindPointer(me.input.pointer.RIGHT, me.input.KEY.X);\n */\nexport function bindPointer(...args: number[]): void {\n\tconst button = args.length < 2 ? pointer.LEFT : args[0];\n\tconst keyCode = args.length < 2 ? args[0] : args[1];\n\n\t// make sure the mouse is initialized\n\tenablePointerEvent();\n\n\t// throw an exception if no action is defined for the specified keycode\n\tif (!getBindingKey(keyCode)) {\n\t\tthrow new Error(`no action defined for keycode ${keyCode}`);\n\t}\n\t// map the mouse button to the keycode\n\tpointer.bind[button] = keyCode;\n}\n\n/**\n * unbind the defined keycode\n * @param button - (accordingly to W3C values : 0,1,2 for left, middle and right buttons)\n * @example\n * me.input.unbindPointer(me.input.pointer.LEFT);\n */\nexport function unbindPointer(button?: number): void {\n\t// clear the event status\n\tpointer.bind[typeof button === \"undefined\" ? pointer.LEFT : button] = null;\n}\n\n/**\n * allows registration of event listeners on the object target. <br>\n * melonJS will pass a me.Pointer object to the defined callback.\n * @see Pointer\n * @see {@link http://www.w3.org/TR/pointerevents/#list-of-pointer-events | W3C Pointer Event list}\n * @param eventType - The event type for which the object is registering <br>\n * melonJS currently supports: <br>\n * <ul>\n *   <li><code>\"pointermove\"</code></li>\n *   <li><code>\"pointerdown\"</code></li>\n *   <li><code>\"pointerup\"</code></li>\n *   <li><code>\"pointerenter\"</code></li>\n *   <li><code>\"pointerover\"</code></li>\n *   <li><code>\"pointerleave\"</code></li>\n *   <li><code>\"pointercancel\"</code></li>\n *   <li><code>\"wheel\"</code></li>\n * </ul>\n * @param region - a shape representing the region to register on\n * @param callback - methods to be called when the event occurs.\n * Returning `false` from the defined callback will prevent the event to be propagated to other objects\n * @example\n *  // onActivate function\n *  onActivateEvent: function () {\n *     // register on the 'pointerdown' event\n *     me.input.registerPointerEvent('pointerdown', this, (e) => this.pointerDown(e));\n *  },\n *\n *  // pointerDown event callback\n *  pointerDown: function (pointer) {\n *    // do something\n *    ....\n *    // don\"t propagate the event to other objects\n *    return false;\n *  },\n */\nexport function registerPointerEvent(\n\teventType: string,\n\tregion: any,\n\tcallback: (pointer: Pointer) => boolean | void,\n): void {\n\t// make sure the mouse/touch events are initialized\n\tenablePointerEvent();\n\n\tif (pointerEventList.indexOf(eventType) === -1) {\n\t\tthrow new Error(`invalid event type : ${eventType}`);\n\t}\n\n\tif (typeof region === \"undefined\") {\n\t\tthrow new Error(\n\t\t\t`registerPointerEvent: region for ${String(region)} event is undefined `,\n\t\t);\n\t}\n\n\tconst eventTypes = findAllActiveEvents(\n\t\tactiveEventList,\n\t\tpointerEventMap[eventType],\n\t);\n\n\t// register the event\n\tif (!eventHandlers.has(region)) {\n\t\teventHandlers.set(region, {\n\t\t\tregion: region,\n\t\t\tcallbacks: {},\n\t\t\tpointerId: null,\n\t\t});\n\t}\n\n\t// allocate array if not defined\n\tconst handlers = eventHandlers.get(region)!;\n\tfor (let i = 0; i < eventTypes.length; i++) {\n\t\tconst eventType = eventTypes[i];\n\t\tif (handlers.callbacks[eventType]) {\n\t\t\thandlers.callbacks[eventType].push(callback);\n\t\t} else {\n\t\t\thandlers.callbacks[eventType] = [callback];\n\t\t}\n\t}\n}\n\n/**\n * allows the removal of event listeners from the object target.\n * @see {@link http://www.w3.org/TR/pointerevents/#list-of-pointer-events|W3C Pointer Event list}\n * @param eventType - The event type for which the object was registered. See {@link input.registerPointerEvent}\n * @param region - the registered region to release for this event\n * @param callback - if specified unregister the event only for the specific callback\n * @example\n * // release the registered region on the 'pointerdown' event\n * me.input.releasePointerEvent('pointerdown', this);\n */\nexport function releasePointerEvent(\n\teventType: string,\n\tregion: any,\n\tcallback?: (pointer: Pointer) => boolean | void,\n): void {\n\tif (pointerEventList.indexOf(eventType) === -1) {\n\t\tthrow new Error(`invalid event type : ${eventType}`);\n\t}\n\n\t// convert to supported event type if pointerEvent not natively supported\n\tconst eventTypes = findAllActiveEvents(\n\t\tactiveEventList,\n\t\tpointerEventMap[eventType],\n\t);\n\n\tconst handlers = eventHandlers.get(region);\n\tif (typeof handlers !== \"undefined\") {\n\t\tfor (let i = 0; i < eventTypes.length; i++) {\n\t\t\tconst eventType = eventTypes[i];\n\t\t\tif (handlers.callbacks[eventType]) {\n\t\t\t\tif (typeof callback !== \"undefined\") {\n\t\t\t\t\tremove(handlers.callbacks[eventType], callback);\n\t\t\t\t} else {\n\t\t\t\t\twhile (handlers.callbacks[eventType].length > 0) {\n\t\t\t\t\t\thandlers.callbacks[eventType].pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// free the array if empty\n\t\t\t\tif (handlers.callbacks[eventType].length === 0) {\n\t\t\t\t\tdelete handlers.callbacks[eventType];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (Object.keys(handlers.callbacks).length === 0) {\n\t\t\teventHandlers.delete(region);\n\t\t}\n\t}\n}\n\n/**\n * allows the removal of all registered event listeners from the object target.\n * @param region - the registered region to release event from\n * @example\n * // release all registered event on the\n * me.input.releaseAllPointerEvents(this);\n */\nexport function releaseAllPointerEvents(region: any): void {\n\tif (eventHandlers.has(region)) {\n\t\tfor (let i = 0; i < pointerEventList.length; i++) {\n\t\t\treleasePointerEvent(pointerEventList[i], region);\n\t\t}\n\t}\n}\n\n/**\n * request for the pointer to be locked on the parent DOM element.\n * (Must be called in a click event or an event that requires user interaction)\n * @returns return true if the request was successfully submitted\n * @example\n * // register on the pointer lock change event\n * event.on(event.POINTERLOCKCHANGE, (locked)=> {\n *     console.log(\"pointer lock: \" + locked);\n * });\n * // request for pointer lock\n * me.input.requestPointerLock();\n */\nexport function requestPointerLock(): boolean {\n\tif (device.hasPointerLockSupport) {\n\t\tconst element = _app.getParentElement();\n\t\tvoid element.requestPointerLock();\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Initiates an exit from pointer lock state\n * @returns return true if the request was successfully submitted\n */\nexport function exitPointerLock(): boolean {\n\tif (device.hasPointerLockSupport) {\n\t\tglobalThis.document.exitPointerLock();\n\t\treturn true;\n\t}\n\treturn false;\n}\n","import { Vector3d } from \"../math/vector3d.ts\";\nimport { AABB3d } from \"../physics/broadphase/aabb3d.ts\";\nimport { createPool } from \"../system/pool.ts\";\n\n/**\n * A 3D sphere — `{ centre, radius }`. Sibling of {@link Ellipse} in\n * the geometry surface; used by {@link Octree} / `adapter.querySphere`\n * as the canonical 3D region-query shape, and by user code for any\n * sphere-vs-sphere / sphere-vs-AABB collision check under\n * {@link Camera3d}.\n *\n * Not currently part of the {@link BodyShape} union — `Body` is 2D-\n * only in melonJS today; Sphere is a math/geometry primitive that\n * would slot in once a 3D physics adapter ships.\n * @category Geometry\n * @example\n * import { Sphere } from \"melonjs\";\n *\n * const a = new Sphere(0, 0, 0, 10);\n * const b = new Sphere(15, 0, 0, 10);\n * a.overlaps(b); // true — surfaces touch at x = 5\n */\nexport class Sphere {\n\t/** Centre coordinates of the sphere (Y-down, +Z forward — Camera3d convention). */\n\tpos: Vector3d;\n\n\t/** Sphere radius. Negative values are treated like their absolute value in {@link Sphere.overlaps} / {@link Sphere.contains}. */\n\tradius: number;\n\n\t/**\n\t * Cached AABB for this sphere. Allocated lazily on the first\n\t * {@link Sphere.getBounds} call so a sphere used only for inline\n\t * `overlaps` checks doesn't pay for the AABB.\n\t * @ignore\n\t */\n\t_bounds?: AABB3d;\n\n\t/**\n\t * @param x - centre x\n\t * @param y - centre y\n\t * @param z - centre z\n\t * @param radius - sphere radius\n\t */\n\tconstructor(x: number, y: number, z: number, radius: number) {\n\t\tthis.pos = new Vector3d(x, y, z);\n\t\tthis.radius = radius;\n\t}\n\n\t/**\n\t * Re-position and resize the sphere in one shot. Mirrors\n\t * {@link Ellipse.setShape}.\n\t * @param x - centre x\n\t * @param y - centre y\n\t * @param z - centre z\n\t * @param radius - sphere radius\n\t * @returns this sphere for chaining\n\t */\n\tsetShape(x: number, y: number, z: number, radius: number) {\n\t\tthis.pos.set(x, y, z);\n\t\tthis.radius = radius;\n\t\t// Invalidate the cached AABB — next `getBounds` call rebuilds.\n\t\tif (typeof this._bounds !== \"undefined\") {\n\t\t\tthis._updateBounds();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Test whether a point lies inside the sphere (boundary inclusive).\n\t * Squared-distance test — no `sqrt` on the hot path. Matches the\n\t * boundary convention of {@link AABB3d.contains}.\n\t * @param x - point x\n\t * @param y - point y\n\t * @param z - point z\n\t */\n\tcontains(x: number, y: number, z: number): boolean;\n\t/**\n\t * Vector form of {@link Sphere.contains}.\n\t * @param v - point\n\t */\n\tcontains(v: Vector3d): boolean;\n\tcontains(xOrV: Vector3d | number, y?: number, z?: number): boolean {\n\t\tlet px: number;\n\t\tlet py: number;\n\t\tlet pz: number;\n\t\tif (xOrV instanceof Vector3d) {\n\t\t\tpx = xOrV.x;\n\t\t\tpy = xOrV.y;\n\t\t\tpz = xOrV.z;\n\t\t} else {\n\t\t\tpx = xOrV;\n\t\t\tpy = y!;\n\t\t\tpz = z!;\n\t\t}\n\t\tconst dx = px - this.pos.x;\n\t\tconst dy = py - this.pos.y;\n\t\tconst dz = pz - this.pos.z;\n\t\t// Use radius², handle negative-radius callers via `r * r`\n\t\t// (which is positive regardless of sign).\n\t\treturn dx * dx + dy * dy + dz * dz <= this.radius * this.radius;\n\t}\n\n\t/**\n\t * Test whether this sphere overlaps another. Boundary inclusive\n\t * (touching surfaces count as overlap), matching\n\t * {@link AABB3d.overlaps}.\n\t * @param other - the other sphere\n\t */\n\toverlaps(other: Sphere): boolean {\n\t\tconst dx = this.pos.x - other.pos.x;\n\t\tconst dy = this.pos.y - other.pos.y;\n\t\tconst dz = this.pos.z - other.pos.z;\n\t\t// Use |r| on each side so negative radii behave like their\n\t\t// absolute value (matches the documented contract in\n\t\t// {@link Sphere.radius} and the sign-cancelling `r * r` in\n\t\t// {@link Sphere.contains}). A naked `this.radius + other.radius`\n\t\t// would collapse to ~0 when one side passes a negative.\n\t\tconst r = Math.abs(this.radius) + Math.abs(other.radius);\n\t\treturn dx * dx + dy * dy + dz * dz <= r * r;\n\t}\n\n\t/**\n\t * Test whether this sphere overlaps an axis-aligned box. Delegates\n\t * to {@link AABB3d.overlapsSphere} so the two shapes agree on\n\t * boundary semantics.\n\t * @param aabb - the box\n\t */\n\toverlapsAABB(aabb: AABB3d): boolean {\n\t\treturn aabb.overlapsSphere(this.pos.x, this.pos.y, this.pos.z, this.radius);\n\t}\n\n\t/**\n\t * Smallest {@link AABB3d} containing this sphere. Cached — repeat\n\t * calls without a `setShape` between return the same instance and\n\t * the same values.\n\t */\n\tgetBounds(): AABB3d {\n\t\tif (typeof this._bounds === \"undefined\") {\n\t\t\tthis._bounds = new AABB3d();\n\t\t\tthis._updateBounds();\n\t\t}\n\t\treturn this._bounds;\n\t}\n\n\t/** @ignore */\n\t_updateBounds() {\n\t\tconst r = Math.abs(this.radius);\n\t\tthis._bounds!.setMinMax(\n\t\t\tthis.pos.x - r,\n\t\t\tthis.pos.y - r,\n\t\t\tthis.pos.z - r,\n\t\t\tthis.pos.x + r,\n\t\t\tthis.pos.y + r,\n\t\t\tthis.pos.z + r,\n\t\t);\n\t}\n\n\t/**\n\t * Reset the sphere to a zero-radius point at the origin.\n\t */\n\tclear() {\n\t\tthis.pos.set(0, 0, 0);\n\t\tthis.radius = 0;\n\t\tif (typeof this._bounds !== \"undefined\") {\n\t\t\tthis._updateBounds();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Deep copy of this sphere. The clone shares NOTHING with the\n\t * original — independent `pos` Vector3d, independent AABB cache.\n\t * Pulls from {@link spherePool} so back-to-back clones are\n\t * allocation-free.\n\t */\n\tclone() {\n\t\treturn spherePool.get(this.pos.x, this.pos.y, this.pos.z, this.radius);\n\t}\n}\n\n/**\n * A pool of {@link Sphere} instances. Mirrors {@link ellipsePool} —\n * `spherePool.get(x, y, z, r)` recycles an existing instance via\n * `setShape` before allocating a new one; release via the\n * matching `.release()` (the `createPool` contract).\n */\nexport const spherePool = createPool<\n\tSphere,\n\t[x: number, y: number, z: number, radius: number]\n>((x, y, z, radius) => {\n\tconst instance = new Sphere(x, y, z, radius);\n\treturn {\n\t\tinstance,\n\t\treset(x, y, z, radius) {\n\t\t\tinstance.setShape(x, y, z, radius);\n\t\t},\n\t};\n});\n","import { Sphere } from \"../../geometries/sphere.ts\";\nimport { Vector2d } from \"../../math/vector2d.ts\";\nimport type World from \"../world.js\";\nimport { AABB3d } from \"./aabb3d.ts\";\nimport type { Broadphase } from \"./broadphase.ts\";\n\n/**\n * Scratch reused by `getIndex` to read viewport-localToWorld for\n * floating items without allocating. Mirrors QuadTree's `QT_VECTOR`\n * — only one query runs at a time, single-instance is safe.\n * @ignore\n */\nconst OT_VECTOR = new Vector2d();\n\n/**\n * 8-octant spatial subdivision for `Camera3d` workloads. Sibling to\n * {@link QuadTree}: same public surface (`insert`, `remove`,\n * `retrieve`, `insertContainer`, `removeContainer`, `clear`,\n * `getIndex`, `isPrunable`, `hasChildren`), plus 3D-specific\n * region queries (`queryAABB`, `querySphere`). Selected\n * automatically by {@link World} when `world.sortOn === \"depth\"`\n * (which `Camera3d.defaultSortOn` sets on stage reset).\n *\n * Implementation notes:\n * - Bounding primitive is {@link AABB3d}; subnode bounds are POJOs\n *   with the same shape so node split is allocation-free apart from\n *   the eight literal objects (recycled via the global node pool).\n * - Per-frame clear/insert reuses both an octree-node pool\n *   (`OT_ARRAY`) and a result scratch (`_retrieveScratch`), so the\n *   steady state allocates nothing.\n * - Item z is read via `getAbsolutePosition()` — same convention as\n *   `Camera3d.isVisible` and the container depth sort, so the\n *   broadphase agrees with the renderer on which octant an item\n *   lives in even under nested transforms.\n */\n\n/**\n * Minimal axis-aligned box shape consumed by `Octree`. Both\n * {@link AABB3d} and the POJOs emitted by `split` satisfy it\n * structurally.\n */\nexport interface OctRect {\n\tleft: number;\n\ttop: number;\n\tfront: number;\n\twidth: number;\n\theight: number;\n\tdepth: number;\n}\n\n/**\n * Structural shape for an item inserted into the `Octree`. Same\n * shape as {@link QuadTree}'s item, with the addition that\n * `getAbsolutePosition()` is consulted for z (so nested transforms\n * still classify into the right octant).\n */\nexport interface OctreeItem {\n\tgetBounds(): { left: number; top: number; width: number; height: number };\n\tgetAbsolutePosition?(): { x: number; y: number; z: number };\n\t/**\n\t * 2D items expose Vector2d here (no z); 3D items expose Vector3d.\n\t * `getAbsolutePosition()` is the preferred path; this fallback\n\t * applies only to test doubles that skip `getAbsolutePosition`.\n\t */\n\tpos?: { x: number; y: number; z?: number };\n\tisFloating?: boolean;\n\tisKinematic?: boolean;\n\tname?: string;\n}\n\nexport type OctreeSortFn = (a: OctreeItem, b: OctreeItem) => number;\n\n/**\n * Structural shape consumed by {@link Octree.queryFrustum} —\n * matches the shape exposed by `Frustum#planes` (see\n * `camera/frustum.ts`). Each plane satisfies `nx·x + ny·y + nz·z + d = 0`;\n * positive side is the interior of the frustum.\n */\nexport interface FrustumPlane {\n\tnx: number;\n\tny: number;\n\tnz: number;\n\td: number;\n}\n\n/**\n * Node pool. Same shape as the QuadTree's QT_ARRAY — split allocates\n * from this, clear/collapse returns to it. Keeps steady-state\n * allocations at zero across the per-frame world rebuild.\n * @ignore\n */\nconst OT_ARRAY: Octree[] = [];\n\nfunction OT_ARRAY_POP(\n\tworld: World,\n\tbounds: OctRect,\n\tmax_objects = 4,\n\tmax_levels = 4,\n\tlevel = 0,\n): Octree {\n\tif (OT_ARRAY.length > 0) {\n\t\tconst _ot = OT_ARRAY.pop()!;\n\t\t_ot.world = world;\n\t\t_ot.bounds = bounds;\n\t\t_ot.max_objects = max_objects;\n\t\t_ot.max_levels = max_levels;\n\t\t_ot.level = level;\n\t\treturn _ot;\n\t}\n\treturn new Octree(world, bounds, max_objects, max_levels, level);\n}\n\nfunction OT_ARRAY_PUSH(ot: Octree) {\n\tOT_ARRAY.push(ot);\n}\n\n/**\n * Read the z component used to classify an item into octants. Uses\n * `getAbsolutePosition()` so items inside a transformed container\n * land in the same octant the renderer draws them in. Falls back to\n * `pos.z` if the item doesn't expose `getAbsolutePosition` (test\n * doubles do this), and finally `0` for items with no pos at all.\n * @ignore\n */\nfunction itemZ(item: OctreeItem): number {\n\tif (typeof item.getAbsolutePosition === \"function\") {\n\t\treturn item.getAbsolutePosition().z;\n\t}\n\treturn item.pos?.z ?? 0;\n}\n\n/**\n * an Octree implementation for 3D broadphase / spatial queries\n * under `Camera3d`.\n * @category Physics\n * @see World.broadphase\n */\nexport default class Octree implements Broadphase<OctreeItem> {\n\tworld: World;\n\tbounds: OctRect | AABB3d;\n\tmax_objects: number;\n\tmax_levels: number;\n\tlevel: number;\n\tobjects: OctreeItem[];\n\tnodes: Octree[];\n\n\t/**\n\t * see {@link QuadTree._subtreeCount} — same invariant.\n\t * @ignore\n\t */\n\t_subtreeCount: number;\n\n\t/**\n\t * see {@link QuadTree._retrieveScratch} — same contract.\n\t * @ignore\n\t */\n\t_retrieveScratch: OctreeItem[] | null;\n\n\t/**\n\t * @param world - the physic world this Octree belongs to\n\t * @param bounds - bounds of the node\n\t * @param [max_objects=4] - max objects a node can hold before splitting into 8 subnodes\n\t * @param [max_levels=4] - total max levels inside root Octree\n\t * @param [level] - depth level, required for subnodes\n\t */\n\tconstructor(\n\t\tworld: World,\n\t\tbounds: OctRect | AABB3d,\n\t\tmax_objects = 4,\n\t\tmax_levels = 4,\n\t\tlevel = 0,\n\t) {\n\t\tthis.world = world;\n\t\tthis.bounds = bounds;\n\t\tthis.max_objects = max_objects;\n\t\tthis.max_levels = max_levels;\n\t\tthis.level = level;\n\t\tthis.objects = [];\n\t\tthis.nodes = [];\n\t\tthis._subtreeCount = 0;\n\t\tthis._retrieveScratch = level === 0 ? [] : null;\n\t}\n\n\t/**\n\t * Split this node into 8 octants. The naming mirrors the\n\t * QuadTree's `top` / `bottom` / `left` / `right` ordering, with\n\t * `near` (smaller z) before `far` (larger z) — matches the\n\t * Y-down + +Z forward convention from {@link Camera3d}.\n\t *\n\t * Octant index layout (used by `getIndex`):\n\t * ```\n\t *   near (lower z)        far (higher z)\n\t *   0: TR-near  1: TL-near   4: TR-far  5: TL-far\n\t *   3: BR-near  2: BL-near   7: BR-far  6: BL-far\n\t * ```\n\t */\n\tsplit() {\n\t\tconst nextLevel = this.level + 1;\n\t\tconst subWidth = this.bounds.width / 2;\n\t\tconst subHeight = this.bounds.height / 2;\n\t\tconst subDepth = this.bounds.depth / 2;\n\t\tconst left = this.bounds.left;\n\t\tconst top = this.bounds.top;\n\t\tconst front = this.bounds.front;\n\n\t\tfor (let octant = 0; octant < 8; octant++) {\n\t\t\tconst isFar = octant >= 4;\n\t\t\tconst localIdx = octant & 3;\n\t\t\tconst isRight = localIdx === 0 || localIdx === 3;\n\t\t\tconst isBottom = localIdx === 2 || localIdx === 3;\n\t\t\tthis.nodes[octant] = OT_ARRAY_POP(\n\t\t\t\tthis.world,\n\t\t\t\t{\n\t\t\t\t\tleft: isRight ? left + subWidth : left,\n\t\t\t\t\ttop: isBottom ? top + subHeight : top,\n\t\t\t\t\tfront: isFar ? front + subDepth : front,\n\t\t\t\t\twidth: subWidth,\n\t\t\t\t\theight: subHeight,\n\t\t\t\t\tdepth: subDepth,\n\t\t\t\t},\n\t\t\t\tthis.max_objects,\n\t\t\t\tthis.max_levels,\n\t\t\t\tnextLevel,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Classify an item into an octant.\n\t *\n\t * Returns -1 (keep at this level) when the item straddles ANY\n\t * midpoint OR sits outside this node's bounds entirely. The\n\t * out-of-bounds guard matters for `querySphere`: a sphere walks\n\t * subnodes by AABB overlap, so an item assigned to a subnode\n\t * whose bounds don't actually contain it would be invisible to\n\t * any query whose sphere doesn't overlap that subnode's AABB.\n\t * Items outside the root live at `root.objects` and are visited\n\t * by every query unconditionally — they degrade to a linear scan\n\t * but stay findable.\n\t * @param item - the object to classify\n\t * @returns octant index (0-7) or -1\n\t */\n\tgetIndex(item: OctreeItem): number {\n\t\tconst quadrant = this._quadrantXY(item);\n\t\tif (quadrant === -1) {\n\t\t\treturn -1;\n\t\t}\n\t\t// items are point-z in the broadphase — bounds is 2D, so the\n\t\t// item occupies a single z plane in 3D space. This matches\n\t\t// how the renderer sorts (depth comes from pos.z, not from a\n\t\t// 3D AABB on the renderable). A point cannot STRADDLE the depth\n\t\t// midpoint the way an extended item straddles a vertical one, so\n\t\t// the tie goes to the far child rather than to this level.\n\t\tconst rz = itemZ(item);\n\t\tconst nodeFront = this.bounds.front;\n\t\tconst nodeBack = nodeFront + this.bounds.depth;\n\t\tif (rz < nodeFront || rz > nodeBack) {\n\t\t\treturn -1;\n\t\t}\n\t\tconst depthMidpoint = nodeFront + this.bounds.depth / 2;\n\t\tif (rz < depthMidpoint) {\n\t\t\treturn quadrant;\n\t\t}\n\t\tif (rz >= depthMidpoint) {\n\t\t\treturn quadrant + 4;\n\t\t}\n\t\t// unreachable for any real `rz` — catches NaN, which stays at\n\t\t// this level rather than vanishing into an octant no query\n\t\t// will look in.\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Classify an item into one of this node's four x/y quadrants,\n\t * **ignoring z entirely**. Returns the near-half octant index\n\t * (0-3, so `+ 4` gives the far-half sibling), or -1 when the item\n\t * straddles a midpoint or lies outside this node in x/y.\n\t *\n\t * Split out of {@link Octree.getIndex} for {@link Octree.retrieve},\n\t * whose consumers all decide overlap in the XY plane and therefore\n\t * need x/y pruning WITHOUT a depth opinion. Routing that through\n\t * `getIndex` instead would reject the item from its own z-sibling\n\t * on the depth out-of-bounds guard, collapsing that entire subtree\n\t * into an unpruned walk — measured at ~10× the nodes visited on a\n\t * depth-spread scene.\n\t *\n\t * Boundary convention: a midpoint belongs to the RIGHT / BOTTOM\n\t * child, and an item whose far edge merely touches a midpoint still\n\t * counts as wholly inside the near side — `<=` / `>=` rather than\n\t * strict, because an item sitting exactly ON a midpoint lies wholly\n\t * within one child and must descend. Only a genuine STRADDLE\n\t * belongs at this level.\n\t *\n\t * This matters more than it looks: the root box is origin-centred,\n\t * so its midpoints are (0, 0, 0) — the default `pos` of every\n\t * renderable. Under strict comparisons an entire gameplay plane\n\t * classified as -1 and sat unpartitioned at the root.\n\t *\n\t * Safety: two items separated by a midpoint cannot overlap. If A\n\t * lies left (`A.right <= mid`) and B lies right (`B.left >= mid`)\n\t * then `A.right <= B.left`, and touching edges are not an overlap\n\t * under the engine's strict bounds test.\n\t * @param item - the object to classify\n\t * @returns near-half octant index (0-3) or -1\n\t * @ignore\n\t */\n\t_quadrantXY(item: OctreeItem): number {\n\t\tconst bounds = item.getBounds();\n\t\tlet rx: number;\n\t\tlet ry: number;\n\t\t// Floating items live in viewport-local space; the broadphase\n\t\t// classifies in world space, so we localToWorld their bounds\n\t\t// before reading the corner. Mirrors the QuadTree branch —\n\t\t// without this, a HUD overlay at viewport `(10, 10)` ends up\n\t\t// classified by viewport-local coords and disagrees with the\n\t\t// renderer about which octant it's in.\n\t\tif (item.isFloating === true) {\n\t\t\tconst pos = this.world.app.viewport.localToWorld(\n\t\t\t\tbounds.left,\n\t\t\t\tbounds.top,\n\t\t\t\tOT_VECTOR,\n\t\t\t);\n\t\t\trx = pos.x;\n\t\t\try = pos.y;\n\t\t} else {\n\t\t\trx = bounds.left;\n\t\t\try = bounds.top;\n\t\t}\n\t\tconst rw = bounds.width;\n\t\tconst rh = bounds.height;\n\t\tconst nodeLeft = this.bounds.left;\n\t\tconst nodeTop = this.bounds.top;\n\t\tconst nodeRight = nodeLeft + this.bounds.width;\n\t\tconst nodeBottom = nodeTop + this.bounds.height;\n\n\t\t// Out-of-bounds guard — keep at parent's `objects` so the\n\t\t// spatial pruning in `querySphere` / `queryAABB` stays\n\t\t// correct for the items that ARE in-bounds.\n\t\tif (\n\t\t\trx < nodeLeft ||\n\t\t\trx + rw > nodeRight ||\n\t\t\try < nodeTop ||\n\t\t\try + rh > nodeBottom\n\t\t) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tconst verticalMidpoint = nodeLeft + this.bounds.width / 2;\n\t\tconst horizontalMidpoint = nodeTop + this.bounds.height / 2;\n\n\t\tconst topQuadrant =\n\t\t\try < horizontalMidpoint && ry + rh <= horizontalMidpoint;\n\t\tconst bottomQuadrant = ry >= horizontalMidpoint;\n\t\tconst leftQuadrant = rx < verticalMidpoint && rx + rw <= verticalMidpoint;\n\t\tconst rightQuadrant = rx >= verticalMidpoint;\n\n\t\tif (leftQuadrant) {\n\t\t\tif (topQuadrant) return 1;\n\t\t\tif (bottomQuadrant) return 2;\n\t\t} else if (rightQuadrant) {\n\t\t\tif (topQuadrant) return 0;\n\t\t\tif (bottomQuadrant) return 3;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t/**\n\t * Insert the given object container into the node — recursively\n\t * walks `Container.getChildren()` and inserts each non-kinematic\n\t * leaf. Mirrors `QuadTree.insertContainer`.\n\t * @param container - group of objects to be added\n\t */\n\tinsertContainer(container: ContainerLike) {\n\t\tconst children = container.getChildren();\n\t\tconst childrenLength = children.length;\n\t\tfor (\n\t\t\tlet i = childrenLength, child: ContainerOrChild;\n\t\t\ti--, (child = children[i]);\n\t\t) {\n\t\t\tif (child.isKinematic !== true) {\n\t\t\t\tif (typeof child.addChild === \"function\" && hasGetChildren(child)) {\n\t\t\t\t\tif (child.name !== \"rootContainer\") {\n\t\t\t\t\t\tthis.insert(child);\n\t\t\t\t\t}\n\t\t\t\t\t// `child` narrowed by `hasGetChildren` — no assertion.\n\t\t\t\t\tthis.insertContainer(child);\n\t\t\t\t} else if (typeof child.getBounds === \"function\") {\n\t\t\t\t\tthis.insert(child);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Insert the given object into the node. If this node exceeds\n\t * `max_objects`, it splits and redistributes existing items into\n\t * subnodes. Mirrors `QuadTree.insert` — same subtree-count\n\t * accounting (bump on insert, no bump on redistribution).\n\t * @param item - object to be added\n\t */\n\tinsert(item: OctreeItem) {\n\t\tthis._subtreeCount++;\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tconst index = this.getIndex(item);\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.nodes[index].insert(item);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tthis.objects.push(item);\n\n\t\tif (\n\t\t\tthis.objects.length > this.max_objects &&\n\t\t\tthis.level < this.max_levels\n\t\t) {\n\t\t\tif (this.nodes.length === 0) {\n\t\t\t\tthis.split();\n\t\t\t}\n\n\t\t\tlet writeIdx = 0;\n\t\t\tfor (let i = 0, len = this.objects.length; i < len; i++) {\n\t\t\t\tconst subIndex = this.getIndex(this.objects[i]);\n\t\t\t\tif (subIndex !== -1) {\n\t\t\t\t\tthis.nodes[subIndex].insert(this.objects[i]);\n\t\t\t\t} else {\n\t\t\t\t\tthis.objects[writeIdx++] = this.objects[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.objects.length = writeIdx;\n\t\t}\n\t}\n\n\t/**\n\t * Recursively remove the given container and its descendants from\n\t * the octree. Mirror of `QuadTree.removeContainer`.\n\t * @param container - group of objects to be removed\n\t */\n\tremoveContainer(container: ContainerLikeOptional) {\n\t\tconst children = container.getChildren?.();\n\t\tif (!children) {\n\t\t\treturn;\n\t\t}\n\t\tconst childrenLength = children.length;\n\t\tfor (\n\t\t\tlet i = childrenLength, child: ContainerOrChild;\n\t\t\ti--, (child = children[i]);\n\t\t) {\n\t\t\tif (child.isKinematic !== true) {\n\t\t\t\tif (typeof child.addChild === \"function\") {\n\t\t\t\t\tif (child.name !== \"rootContainer\") {\n\t\t\t\t\t\tthis.remove(child);\n\t\t\t\t\t}\n\t\t\t\t\tthis.removeContainer(child);\n\t\t\t\t} else if (typeof child.getBounds === \"function\") {\n\t\t\t\t\tthis.remove(child);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Return every object that could collide with the given item.\n\t * Re-entrancy contract identical to {@link QuadTree.retrieve}: the\n\t * default (scratch) path is allocation-free but cannot be safely\n\t * re-entered while iterating; pass an explicit `result` for safe\n\t * re-entry.\n\t * @param item - object to be checked against\n\t * @param [fn] - sorter applied to the returned array (root only)\n\t * @param [result] - caller-supplied result array (re-entrancy-safe)\n\t * @returns the collected candidates\n\t */\n\tretrieve(\n\t\titem: OctreeItem,\n\t\tfn?: OctreeSortFn,\n\t\tresult?: OctreeItem[],\n\t): OctreeItem[] {\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: OctreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\t// `retrieve` feeds 2D consumers ONLY — the SAT detector\n\t\t\t// (`Detector.collisions`), pointer picking, the 2D raycast and\n\t\t\t// `adapter.queryAABB`. Every one of them decides overlap in the\n\t\t\t// XY plane, so pruning on z here silently drops real collisions:\n\t\t\t// two bodies at different depths that overlap in XY do collide\n\t\t\t// under 2D SAT, but would never be offered to each other as\n\t\t\t// candidates.\n\t\t\t//\n\t\t\t// So classify on x/y ONLY and walk both depth halves of that\n\t\t\t// quadrant. Going through `getIndex` and walking `index ^ 4`\n\t\t\t// instead would work, but the sibling would then reject the item\n\t\t\t// on its depth out-of-bounds guard and fall back to an unpruned\n\t\t\t// 8-way walk of that whole subtree.\n\t\t\t//\n\t\t\t// x/y pruning is preserved at every level and in both halves: an\n\t\t\t// item lying wholly inside a DIFFERENT x/y quadrant cannot\n\t\t\t// overlap, and midpoint-straddlers live in `objects` at this\n\t\t\t// level and were already folded in above.\n\t\t\t//\n\t\t\t// Queries that genuinely want depth pruning have their own entry\n\t\t\t// points (`queryAABB`, `querySphere`, `queryRay`, `queryFrustum`)\n\t\t\t// and are unaffected.\n\t\t\tconst quadrant = this._quadrantXY(item);\n\t\t\tif (quadrant !== -1) {\n\t\t\t\tthis.nodes[quadrant].retrieve(item, undefined, out);\n\t\t\t\tthis.nodes[quadrant + 4].retrieve(item, undefined, out);\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\t\tthis.nodes[i].retrieve(item, undefined, out);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (isRoot && typeof fn === \"function\") {\n\t\t\tout.sort(fn);\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Return every object whose subtree overlaps the given AABB.\n\t * Walks only octants whose bounds overlap the query — same\n\t * pruning shape as `retrieve(item)`, just driven by an explicit\n\t * region instead of an item's own bounds.\n\t *\n\t * Re-entrancy: same contract as {@link Octree.retrieve}. Pass an\n\t * explicit `result` for safe re-entry.\n\t * @param aabb - the AABB region to query\n\t * @param [result] - caller-supplied result array (re-entrancy-safe)\n\t * @returns the collected candidates\n\t */\n\tqueryAABB(aabb: AABB3d, result?: OctreeItem[]): OctreeItem[] {\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: OctreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\t// short-circuit: if this node's bounds don't overlap the\n\t\t// query AABB, the whole subtree is irrelevant. The root is\n\t\t// always relevant by construction.\n\t\tif (this.level > 0 && !this._overlapsAABB(aabb)) {\n\t\t\treturn out;\n\t\t}\n\n\t\t// fold every object at this level whose bounds overlap\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tthis.nodes[i].queryAABB(aabb, out);\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Return every object whose containing octant overlaps the given\n\t * sphere. Replaces the hand-rolled O(n²) sphere-vs-sphere loops\n\t * common in arcade 3D code with a broadphase that scales.\n\t *\n\t * Two call shapes — both forms route to the same internal walk:\n\t * - `querySphere(cx, cy, cz, r, result?)` — loose floats\n\t * - `querySphere(sphere, result?)` — packaged {@link Sphere}\n\t *\n\t * Re-entrancy: same contract as {@link Octree.retrieve}.\n\t */\n\tquerySphere(sphere: Sphere, result?: OctreeItem[]): OctreeItem[];\n\t/**\n\t * @param cx - sphere center x\n\t * @param cy - sphere center y\n\t * @param cz - sphere center z\n\t * @param r - sphere radius (must be ≥ 0; r=0 becomes a point query)\n\t * @param [result] - caller-supplied result array (re-entrancy-safe)\n\t * @returns the collected candidates\n\t */\n\tquerySphere(\n\t\tcx: number,\n\t\tcy: number,\n\t\tcz: number,\n\t\tr: number,\n\t\tresult?: OctreeItem[],\n\t): OctreeItem[];\n\tquerySphere(\n\t\tsphereOrCx: Sphere | number,\n\t\tcyOrResult?: number | OctreeItem[],\n\t\tcz?: number,\n\t\tr?: number,\n\t\tresult?: OctreeItem[],\n\t): OctreeItem[] {\n\t\tif (sphereOrCx instanceof Sphere) {\n\t\t\treturn this._querySphereInternal(\n\t\t\t\tsphereOrCx.pos.x,\n\t\t\t\tsphereOrCx.pos.y,\n\t\t\t\tsphereOrCx.pos.z,\n\t\t\t\tsphereOrCx.radius,\n\t\t\t\tcyOrResult as OctreeItem[] | undefined,\n\t\t\t);\n\t\t}\n\t\treturn this._querySphereInternal(\n\t\t\tsphereOrCx,\n\t\t\tcyOrResult as number,\n\t\t\tcz!,\n\t\t\tr!,\n\t\t\tresult,\n\t\t);\n\t}\n\n\t/**\n\t * Loose-floats implementation. Recursive subnode walks call this\n\t * directly to bypass the Sphere/floats dispatch on every node.\n\t * @ignore\n\t */\n\t_querySphereInternal(\n\t\tcx: number,\n\t\tcy: number,\n\t\tcz: number,\n\t\tr: number,\n\t\tresult?: OctreeItem[],\n\t): OctreeItem[] {\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: OctreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\tif (this.level > 0 && !this._overlapsSphere(cx, cy, cz, r)) {\n\t\t\treturn out;\n\t\t}\n\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tthis.nodes[i]._querySphereInternal(cx, cy, cz, r, out);\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Return every object whose containing octant lies on the\n\t * positive side of all six frustum planes — i.e. every potential\n\t * candidate the frustum can see. Uses Gribb–Hartmann positive-\n\t * vertex pruning at each node: a subtree is skipped iff its\n\t * octant's most-positive corner along a plane normal is on that\n\t * plane's negative side (i.e. wholly outside the frustum).\n\t *\n\t * Mirrors {@link Frustum#intersectsSphere} on the renderer side —\n\t * use this as the broadphase pass before per-item narrow-phase\n\t * checks (sphere / OBB / etc.).\n\t *\n\t * Re-entrancy: same contract as {@link Octree.retrieve}. Pass an\n\t * explicit `result` for safe re-entry.\n\t * @param planes - 6 frustum planes (left, right, bottom, top, near, far) — `Frustum#planes` shape\n\t * @param [result] - caller-supplied result array (re-entrancy-safe)\n\t * @returns the collected candidates\n\t */\n\tqueryFrustum(planes: FrustumPlane[], result?: OctreeItem[]): OctreeItem[] {\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: OctreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\t// Subtree-bounds vs frustum prune at level>0. Root is always\n\t\t// walked so out-of-bounds items at root.objects stay findable.\n\t\tif (this.level > 0 && this._outsideFrustum(planes)) {\n\t\t\treturn out;\n\t\t}\n\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tthis.nodes[i].queryFrustum(planes, out);\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Return every object whose containing octant the ray segment\n\t * `from + t·dir`, `t ∈ [0, tMax]`, crosses. Slab AABB test prunes\n\t * subtrees the ray misses entirely — much tighter than enclosing\n\t * the segment in a single AABB and walking `queryAABB`. The\n\t * candidates aren't sorted by hit-distance (front-to-back walk\n\t * would require a per-octant DDA step); callers that need\n\t * nearest-hit sort the narrow-phase results themselves.\n\t *\n\t * Re-entrancy: same contract as {@link Octree.retrieve}.\n\t * @param fromX - ray origin x\n\t * @param fromY - ray origin y\n\t * @param fromZ - ray origin z\n\t * @param dirX - ray direction x (`to - from`; not unit-length)\n\t * @param dirY - ray direction y\n\t * @param dirZ - ray direction z\n\t * @param tMax - maximum t along the ray (usually `1` if `dir = to - from`)\n\t * @param [result] - caller-supplied result array (re-entrancy-safe)\n\t * @returns the collected candidates\n\t */\n\tqueryRay(\n\t\tfromX: number,\n\t\tfromY: number,\n\t\tfromZ: number,\n\t\tdirX: number,\n\t\tdirY: number,\n\t\tdirZ: number,\n\t\ttMax: number,\n\t\tresult?: OctreeItem[],\n\t): OctreeItem[] {\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: OctreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\tif (\n\t\t\tthis.level > 0 &&\n\t\t\t!this._overlapsRay(fromX, fromY, fromZ, dirX, dirY, dirZ, tMax)\n\t\t) {\n\t\t\treturn out;\n\t\t}\n\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tthis.nodes[i].queryRay(\n\t\t\t\t\tfromX,\n\t\t\t\t\tfromY,\n\t\t\t\t\tfromZ,\n\t\t\t\t\tdirX,\n\t\t\t\t\tdirY,\n\t\t\t\t\tdirZ,\n\t\t\t\t\ttMax,\n\t\t\t\t\tout,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Remove the given item from the octree. Mirror of\n\t * `QuadTree.remove` — same stale-bounds fallback walk, same\n\t * collapse-on-removal pool recycling.\n\t * @param item - object to be removed\n\t * @returns true if the item was found and removed.\n\t */\n\tremove(item: OctreeItem): boolean {\n\t\tlet found = false;\n\n\t\tif (typeof item.getBounds === \"undefined\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.nodes.length > 0) {\n\t\t\tconst index = this.getIndex(item);\n\t\t\tif (index !== -1) {\n\t\t\t\tfound = this.nodes[index].remove(item);\n\t\t\t}\n\t\t}\n\n\t\tif (!found) {\n\t\t\tconst idx = this.objects.indexOf(item);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis.objects.splice(idx, 1);\n\t\t\t\tfound = true;\n\t\t\t}\n\t\t}\n\n\t\t// Stale-bounds fallback — item's position may have moved\n\t\t// since insert.\n\t\tif (!found && this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tif (this.nodes[i].remove(item)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (found) {\n\t\t\tthis._subtreeCount--;\n\t\t\tif (\n\t\t\t\tthis.nodes.length > 0 &&\n\t\t\t\tthis.objects.length === 0 &&\n\t\t\t\tthis._subtreeCount === 0\n\t\t\t) {\n\t\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\t\tthis.nodes[i].clear();\n\t\t\t\t\tOT_ARRAY_PUSH(this.nodes[i]);\n\t\t\t\t}\n\t\t\t\tthis.nodes.length = 0;\n\t\t\t}\n\t\t}\n\n\t\treturn found;\n\t}\n\n\t/**\n\t * @returns true if this subtree holds no items at any level.\n\t */\n\tisPrunable(): boolean {\n\t\treturn this._subtreeCount === 0;\n\t}\n\n\t/**\n\t * @returns true if this node has any descendants (items in\n\t * subnodes, not at this level).\n\t */\n\thasChildren(): boolean {\n\t\treturn this._subtreeCount > this.objects.length;\n\t}\n\n\t/**\n\t * Empty this octree and recycle every subnode back to the pool.\n\t * If `bounds` is supplied, resizes the root bounds to match —\n\t * called by `World` on `LEVEL_LOADED` so the broadphase tracks\n\t * level boundary changes.\n\t * @param [bounds] - the new root bounds (resize on clear)\n\t */\n\tclear(bounds?: AABB3d) {\n\t\tthis.objects.length = 0;\n\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\tthis.nodes[i].clear();\n\t\t\tOT_ARRAY_PUSH(this.nodes[i]);\n\t\t}\n\t\tthis.nodes.length = 0;\n\t\tthis._subtreeCount = 0;\n\t\tif (typeof bounds !== \"undefined\") {\n\t\t\t(this.bounds as AABB3d).setMinMax(\n\t\t\t\tbounds.min.x,\n\t\t\t\tbounds.min.y,\n\t\t\t\tbounds.min.z,\n\t\t\t\tbounds.max.x,\n\t\t\t\tbounds.max.y,\n\t\t\t\tbounds.max.z,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Conservative outside-frustum test using the positive-vertex\n\t * method (Gribb–Hartmann). Returns true iff the octant is\n\t * provably outside ANY one plane, i.e. wholly outside the\n\t * frustum.\n\t * @ignore\n\t */\n\t_outsideFrustum(planes: FrustumPlane[]): boolean {\n\t\tconst b = this.bounds;\n\t\tconst minX = b.left;\n\t\tconst maxX = b.left + b.width;\n\t\tconst minY = b.top;\n\t\tconst maxY = b.top + b.height;\n\t\tconst minZ = b.front;\n\t\tconst maxZ = b.front + b.depth;\n\t\tfor (let i = 0; i < planes.length; i++) {\n\t\t\tconst p = planes[i];\n\t\t\t// positive vertex: the corner of the AABB furthest along\n\t\t\t// the plane normal. Signed distance from that corner to\n\t\t\t// the plane = nx*px + ny*py + nz*pz + d. If negative, the\n\t\t\t// entire AABB is on the plane's negative side.\n\t\t\tconst px = p.nx > 0 ? maxX : minX;\n\t\t\tconst py = p.ny > 0 ? maxY : minY;\n\t\t\tconst pz = p.nz > 0 ? maxZ : minZ;\n\t\t\tif (p.nx * px + p.ny * py + p.nz * pz + p.d < 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Slab method for ray–AABB intersection. Returns true iff the\n\t * segment `from + t·dir`, `t ∈ [0, tMax]`, intersects this\n\t * node's octant. dir components may be zero; the axis with zero\n\t * direction degenerates to a point-in-slab test.\n\t * @ignore\n\t */\n\t_overlapsRay(\n\t\tfromX: number,\n\t\tfromY: number,\n\t\tfromZ: number,\n\t\tdirX: number,\n\t\tdirY: number,\n\t\tdirZ: number,\n\t\ttMax: number,\n\t): boolean {\n\t\tconst b = this.bounds;\n\t\tconst minX = b.left;\n\t\tconst maxX = b.left + b.width;\n\t\tconst minY = b.top;\n\t\tconst maxY = b.top + b.height;\n\t\tconst minZ = b.front;\n\t\tconst maxZ = b.front + b.depth;\n\n\t\tlet tEnter = 0;\n\t\tlet tExit = tMax;\n\n\t\t// x axis\n\t\tif (dirX === 0) {\n\t\t\tif (fromX < minX || fromX > maxX) return false;\n\t\t} else {\n\t\t\tconst invD = 1 / dirX;\n\t\t\tconst t1 = (minX - fromX) * invD;\n\t\t\tconst t2 = (maxX - fromX) * invD;\n\t\t\tconst tMin = t1 < t2 ? t1 : t2;\n\t\t\tconst tMaxLocal = t1 < t2 ? t2 : t1;\n\t\t\tif (tMin > tEnter) tEnter = tMin;\n\t\t\tif (tMaxLocal < tExit) tExit = tMaxLocal;\n\t\t\tif (tEnter > tExit) return false;\n\t\t}\n\n\t\t// y axis\n\t\tif (dirY === 0) {\n\t\t\tif (fromY < minY || fromY > maxY) return false;\n\t\t} else {\n\t\t\tconst invD = 1 / dirY;\n\t\t\tconst t1 = (minY - fromY) * invD;\n\t\t\tconst t2 = (maxY - fromY) * invD;\n\t\t\tconst tMin = t1 < t2 ? t1 : t2;\n\t\t\tconst tMaxLocal = t1 < t2 ? t2 : t1;\n\t\t\tif (tMin > tEnter) tEnter = tMin;\n\t\t\tif (tMaxLocal < tExit) tExit = tMaxLocal;\n\t\t\tif (tEnter > tExit) return false;\n\t\t}\n\n\t\t// z axis\n\t\tif (dirZ === 0) {\n\t\t\tif (fromZ < minZ || fromZ > maxZ) return false;\n\t\t} else {\n\t\t\tconst invD = 1 / dirZ;\n\t\t\tconst t1 = (minZ - fromZ) * invD;\n\t\t\tconst t2 = (maxZ - fromZ) * invD;\n\t\t\tconst tMin = t1 < t2 ? t1 : t2;\n\t\t\tconst tMaxLocal = t1 < t2 ? t2 : t1;\n\t\t\tif (tMin > tEnter) tEnter = tMin;\n\t\t\tif (tMaxLocal < tExit) tExit = tMaxLocal;\n\t\t\tif (tEnter > tExit) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/** @ignore */\n\t_overlapsAABB(aabb: AABB3d): boolean {\n\t\tconst b = this.bounds;\n\t\tconst right = b.left + b.width;\n\t\tconst bottom = b.top + b.height;\n\t\tconst back = b.front + b.depth;\n\t\treturn !(\n\t\t\tright < aabb.min.x ||\n\t\t\tb.left > aabb.max.x ||\n\t\t\tbottom < aabb.min.y ||\n\t\t\tb.top > aabb.max.y ||\n\t\t\tback < aabb.min.z ||\n\t\t\tb.front > aabb.max.z\n\t\t);\n\t}\n\n\t/** @ignore */\n\t_overlapsSphere(cx: number, cy: number, cz: number, r: number): boolean {\n\t\tconst b = this.bounds;\n\t\tconst minX = b.left;\n\t\tconst maxX = b.left + b.width;\n\t\tconst minY = b.top;\n\t\tconst maxY = b.top + b.height;\n\t\tconst minZ = b.front;\n\t\tconst maxZ = b.front + b.depth;\n\t\tconst dx = cx < minX ? minX - cx : cx > maxX ? cx - maxX : 0;\n\t\tconst dy = cy < minY ? minY - cy : cy > maxY ? cy - maxY : 0;\n\t\tconst dz = cz < minZ ? minZ - cz : cz > maxZ ? cz - maxZ : 0;\n\t\treturn dx * dx + dy * dy + dz * dz <= r * r;\n\t}\n}\n\n/**\n * Structural shape consumed by `insertContainer` / `removeContainer`\n * — captures only the fields the walk inspects. Same approach as\n * QuadTree. `getChildren` is optional because leaf renderables don't\n * have it; the recursive walk narrows via {@link hasGetChildren}.\n * @ignore\n */\ninterface ContainerOrChild extends OctreeItem {\n\taddChild?: (...args: unknown[]) => unknown;\n\tgetChildren?: () => ContainerOrChild[];\n}\n\n/** @ignore */\ntype ContainerLike = { getChildren(): ContainerOrChild[] };\n/** @ignore */\ntype ContainerLikeOptional = { getChildren?(): ContainerOrChild[] };\n\n/**\n * Type predicate mirror of QuadTree's. Narrows a `ContainerOrChild`\n * to one whose `getChildren` is definitely a function, letting the\n * recursive `insertContainer(child)` call pass without an assertion.\n * @param c - the candidate to test\n * @returns true if `c` has a callable `getChildren`\n */\nfunction hasGetChildren(\n\tc: ContainerOrChild,\n): c is ContainerOrChild & ContainerLike {\n\treturn typeof c.getChildren === \"function\";\n}\n","import { Vector2d } from \"../../math/vector2d.ts\";\nimport type { Bounds } from \"../bounds.ts\";\nimport type World from \"../world.js\";\nimport type { Broadphase } from \"./broadphase.ts\";\n\n// Moved from `physics/builtin/quadtree.js` in 19.7.0 alongside the\n// addition of `Octree`. Lives next to `Octree` and `AABB3d` under\n// `physics/broadphase/` because none of the three are built-in-physics\n// specific — pointer-event picking and any third-party adapter that\n// reads `world.broadphase` use the same surface.\n\n/*\n * A QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm.\n * Based on the QuadTree Library by Timo Hausmann and released under the MIT license\n * https://github.com/timohausmann/quadtree-js/\n */\n\n/**\n * Minimal axis-aligned rectangle shape consumed by `QuadTree` — the\n * full {@link Bounds} class satisfies it structurally, so do the\n * `{ left, top, width, height }` POJOs `split` allocates for\n * subnodes. Kept as an exported interface so any subclass / adapter\n * that constructs its own root bounds doesn't have to import\n * `Bounds`.\n */\nexport interface QuadRect {\n\tleft: number;\n\ttop: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Structural shape for an item inserted into the `QuadTree`. Every\n * Renderable satisfies it; `getBounds()` is the only required\n * member. `isFloating` triggers the viewport-localToWorld coord\n * remap; `isKinematic` / `name` / `addChild` are consulted only by\n * `insertContainer` / `removeContainer` when walking a subtree.\n */\nexport interface QuadTreeItem {\n\tgetBounds(): QuadRect;\n\tisFloating?: boolean;\n\tisKinematic?: boolean;\n\tname?: string;\n}\n\n/**\n * Optional comparator passed to {@link QuadTree.retrieve} — invoked\n * once on the assembled candidate array, only when `retrieve` was\n * called in root mode (no caller-supplied `result`).\n */\nexport type QuadTreeSortFn = (a: QuadTreeItem, b: QuadTreeItem) => number;\n\n/**\n * a pool of `QuadTree` objects\n * @ignore\n */\nconst QT_ARRAY: QuadTree[] = [];\n\n/**\n * will pop a quadtree object from the array\n * or create a new one if the array is empty\n * @ignore\n */\nfunction QT_ARRAY_POP(\n\tworld: World,\n\tbounds: QuadRect,\n\tmax_objects = 4,\n\tmax_levels = 4,\n\tlevel = 0,\n): QuadTree {\n\tif (QT_ARRAY.length > 0) {\n\t\tconst _qt = QT_ARRAY.pop()!;\n\t\t_qt.world = world;\n\t\t_qt.bounds = bounds;\n\t\t_qt.max_objects = max_objects;\n\t\t_qt.max_levels = max_levels;\n\t\t_qt.level = level;\n\t\treturn _qt;\n\t} else {\n\t\treturn new QuadTree(world, bounds, max_objects, max_levels, level);\n\t}\n}\n\n/**\n * Push back a quadtree back into the array\n * @ignore\n */\nfunction QT_ARRAY_PUSH(qt: QuadTree) {\n\tQT_ARRAY.push(qt);\n}\n\n/**\n * a temporary vector object to be reused\n * @ignore\n */\nconst QT_VECTOR = new Vector2d();\n\n/**\n * a QuadTree implementation in JavaScript, a 2d spatial subdivision algorithm.\n * @category Physics\n * @see World.broadphase\n */\nexport default class QuadTree implements Broadphase<QuadTreeItem> {\n\tworld: World;\n\tbounds: QuadRect | Bounds;\n\tmax_objects: number;\n\tmax_levels: number;\n\tlevel: number;\n\tobjects: QuadTreeItem[];\n\tnodes: QuadTree[];\n\n\t/**\n\t * Total number of objects in this subtree (this node's own\n\t * `objects` plus every descendant's). Maintained by `insert`\n\t * and `remove` so `isPrunable` / `hasChildren` are O(1) reads\n\t * instead of O(tree-size) walks. Reset to 0 in `clear`.\n\t * @ignore\n\t */\n\t_subtreeCount: number;\n\n\t/**\n\t * Root-only scratch array reused across `retrieve` calls to\n\t * avoid allocating a fresh array per pointer event / per\n\t * narrow-phase query. Only the root allocates one; recursive\n\t * subnode calls receive the array via the `result` arg.\n\t * @ignore\n\t */\n\t_retrieveScratch: QuadTreeItem[] | null;\n\n\t/**\n\t * @param world - the physic world this QuadTree belongs to\n\t * @param bounds - bounds of the node\n\t * @param [max_objects=4] - max objects a node can hold before splitting into 4 subnodes\n\t * @param [max_levels=4] - total max levels inside root QuadTree\n\t * @param [level] - depth level, required for subnodes\n\t */\n\tconstructor(\n\t\tworld: World,\n\t\tbounds: QuadRect | Bounds,\n\t\tmax_objects = 4,\n\t\tmax_levels = 4,\n\t\tlevel = 0,\n\t) {\n\t\tthis.world = world;\n\t\tthis.bounds = bounds;\n\n\t\tthis.max_objects = max_objects;\n\t\tthis.max_levels = max_levels;\n\n\t\tthis.level = level;\n\n\t\tthis.objects = [];\n\t\tthis.nodes = [];\n\n\t\tthis._subtreeCount = 0;\n\t\tthis._retrieveScratch = level === 0 ? [] : null;\n\t}\n\n\t/*\n\t * Split the node into 4 subnodes\n\t */\n\tsplit() {\n\t\tconst nextLevel = this.level + 1;\n\t\tconst subWidth = this.bounds.width / 2;\n\t\tconst subHeight = this.bounds.height / 2;\n\t\tconst left = this.bounds.left;\n\t\tconst top = this.bounds.top;\n\n\t\t//top right node\n\t\tthis.nodes[0] = QT_ARRAY_POP(\n\t\t\tthis.world,\n\t\t\t{\n\t\t\t\tleft: left + subWidth,\n\t\t\t\ttop: top,\n\t\t\t\twidth: subWidth,\n\t\t\t\theight: subHeight,\n\t\t\t},\n\t\t\tthis.max_objects,\n\t\t\tthis.max_levels,\n\t\t\tnextLevel,\n\t\t);\n\n\t\t//top left node\n\t\tthis.nodes[1] = QT_ARRAY_POP(\n\t\t\tthis.world,\n\t\t\t{\n\t\t\t\tleft: left,\n\t\t\t\ttop: top,\n\t\t\t\twidth: subWidth,\n\t\t\t\theight: subHeight,\n\t\t\t},\n\t\t\tthis.max_objects,\n\t\t\tthis.max_levels,\n\t\t\tnextLevel,\n\t\t);\n\n\t\t//bottom left node\n\t\tthis.nodes[2] = QT_ARRAY_POP(\n\t\t\tthis.world,\n\t\t\t{\n\t\t\t\tleft: left,\n\t\t\t\ttop: top + subHeight,\n\t\t\t\twidth: subWidth,\n\t\t\t\theight: subHeight,\n\t\t\t},\n\t\t\tthis.max_objects,\n\t\t\tthis.max_levels,\n\t\t\tnextLevel,\n\t\t);\n\n\t\t//bottom right node\n\t\tthis.nodes[3] = QT_ARRAY_POP(\n\t\t\tthis.world,\n\t\t\t{\n\t\t\t\tleft: left + subWidth,\n\t\t\t\ttop: top + subHeight,\n\t\t\t\twidth: subWidth,\n\t\t\t\theight: subHeight,\n\t\t\t},\n\t\t\tthis.max_objects,\n\t\t\tthis.max_levels,\n\t\t\tnextLevel,\n\t\t);\n\t}\n\n\t/*\n\t * Determine which node the object belongs to\n\t * @param item - the object to be classified\n\t * @returns integer index of the subnode (0-3), or -1 if the item\n\t * cannot completely fit within a subnode and is part of the\n\t * parent node\n\t */\n\tgetIndex(item: QuadTreeItem): number {\n\t\tconst bounds = item.getBounds();\n\n\t\tlet rx: number;\n\t\tlet ry: number;\n\n\t\t// use game world coordinates for floating items\n\t\tif (item.isFloating === true) {\n\t\t\tconst pos = this.world.app.viewport.localToWorld(\n\t\t\t\tbounds.left,\n\t\t\t\tbounds.top,\n\t\t\t\tQT_VECTOR,\n\t\t\t);\n\t\t\trx = pos.x;\n\t\t\try = pos.y;\n\t\t} else {\n\t\t\trx = bounds.left;\n\t\t\try = bounds.top;\n\t\t}\n\n\t\tlet index = -1;\n\t\tconst rw = bounds.width;\n\t\tconst rh = bounds.height;\n\t\tconst verticalMidpoint = this.bounds.left + this.bounds.width / 2;\n\t\tconst horizontalMidpoint = this.bounds.top + this.bounds.height / 2;\n\t\t//rect can completely fit within the top quadrants\n\t\tconst topQuadrant = ry < horizontalMidpoint && ry + rh < horizontalMidpoint;\n\t\t//rect can completely fit within the bottom quadrants\n\t\tconst bottomQuadrant = ry > horizontalMidpoint;\n\n\t\t//rect can completely fit within the left quadrants\n\t\tif (rx < verticalMidpoint && rx + rw < verticalMidpoint) {\n\t\t\tif (topQuadrant) {\n\t\t\t\tindex = 1;\n\t\t\t} else if (bottomQuadrant) {\n\t\t\t\tindex = 2;\n\t\t\t}\n\t\t} else if (rx > verticalMidpoint) {\n\t\t\t//rect can completely fit within the right quadrants\n\t\t\tif (topQuadrant) {\n\t\t\t\tindex = 0;\n\t\t\t} else if (bottomQuadrant) {\n\t\t\t\tindex = 3;\n\t\t\t}\n\t\t}\n\n\t\treturn index;\n\t}\n\n\t/**\n\t * Insert the given object container into the node.\n\t *\n\t * Typed against the structural duck shape (`getChildren() →\n\t * ContainerOrChild[]`) rather than the concrete `Container` class\n\t * so the recursive call below — where `child` is the structural\n\t * `ContainerOrChild` shape from `getChildren()`, not a `Container`\n\t * instance — type-checks without an `as unknown as Container` cast.\n\t * `Container` itself satisfies the shape (its `getChildren()`\n\t * returns `Renderable[]`, which matches the looser structural\n\t * `ContainerOrChild[]`).\n\t * @param container - group of objects to be added\n\t */\n\tinsertContainer(container: ContainerLike) {\n\t\t// use getChildren() to lazily initialise an empty array — Container\n\t\t// stores `children` as `undefined` until first access, which would\n\t\t// otherwise crash here for a freshly-constructed world.\n\t\tconst children = container.getChildren();\n\t\tconst childrenLength = children.length;\n\t\tfor (\n\t\t\tlet i = childrenLength, child: ContainerOrChild;\n\t\t\ti--, (child = children[i]);\n\t\t) {\n\t\t\tif (child.isKinematic !== true) {\n\t\t\t\tif (typeof child.addChild === \"function\" && hasGetChildren(child)) {\n\t\t\t\t\t// `rootContainer` is the world itself — it owns the\n\t\t\t\t\t// quadtree, it isn't an item inside it.\n\t\t\t\t\tif (child.name !== \"rootContainer\") {\n\t\t\t\t\t\tthis.insert(child);\n\t\t\t\t\t}\n\t\t\t\t\t// `child` narrowed by `hasGetChildren` — no assertion.\n\t\t\t\t\tthis.insertContainer(child);\n\t\t\t\t} else {\n\t\t\t\t\t// only insert object with a bounding box\n\t\t\t\t\t// Probably redundant with `isKinematic`\n\t\t\t\t\tif (typeof child.getBounds === \"function\") {\n\t\t\t\t\t\tthis.insert(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Insert the given object into the node. If the node\n\t * exceeds the capacity, it will split and add all\n\t * objects to their corresponding subnodes.\n\t * @param item - object to be added\n\t */\n\tinsert(item: QuadTreeItem) {\n\t\t// Subtree count: this insert adds one object SOMEWHERE in\n\t\t// this subtree (either to `this.objects` or, by recursion,\n\t\t// to a descendant's subtree). Bumping at every level entered\n\t\t// gives every ancestor a correct running total.\n\t\tthis._subtreeCount++;\n\n\t\t//if we have subnodes ...\n\t\tif (this.nodes.length > 0) {\n\t\t\tconst index = this.getIndex(item);\n\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.nodes[index].insert(item);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tthis.objects.push(item);\n\n\t\tif (\n\t\t\tthis.objects.length > this.max_objects &&\n\t\t\tthis.level < this.max_levels\n\t\t) {\n\t\t\t//split if we don't already have subnodes\n\t\t\tif (this.nodes.length === 0) {\n\t\t\t\tthis.split();\n\t\t\t}\n\n\t\t\t//add all objects to their corresponding subnodes\n\t\t\tlet writeIdx = 0;\n\t\t\tfor (let i = 0, len = this.objects.length; i < len; i++) {\n\t\t\t\tconst subIndex = this.getIndex(this.objects[i]);\n\t\t\t\tif (subIndex !== -1) {\n\t\t\t\t\t// Redistribution: the item is being MOVED from\n\t\t\t\t\t// `this.objects` into a subnode. The total in this\n\t\t\t\t\t// subtree is unchanged, so we DON'T touch\n\t\t\t\t\t// `this._subtreeCount`. The subnode's `insert`\n\t\t\t\t\t// will increment its own count for the moved item.\n\t\t\t\t\tthis.nodes[subIndex].insert(this.objects[i]);\n\t\t\t\t} else {\n\t\t\t\t\tthis.objects[writeIdx++] = this.objects[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.objects.length = writeIdx;\n\t\t}\n\t}\n\n\t/**\n\t * Recursively remove the given container and its descendants from\n\t * the quadtree. Mirrors `insertContainer` so the broadphase can be\n\t * kept in sync when a subtree is detached via\n\t * `Container.removeChildNow` between two `world.update()` rebuilds\n\t * (pointer events fire async in that window and would otherwise\n\t * hit destroyed renderables).\n\t * @param container - group of objects to be removed\n\t */\n\tremoveContainer(container: ContainerLikeOptional) {\n\t\tconst children = container.getChildren?.();\n\t\tif (!children) {\n\t\t\treturn;\n\t\t}\n\t\tconst childrenLength = children.length;\n\t\tfor (\n\t\t\tlet i = childrenLength, child: ContainerOrChild;\n\t\t\ti--, (child = children[i]);\n\t\t) {\n\t\t\tif (child.isKinematic !== true) {\n\t\t\t\tif (typeof child.addChild === \"function\") {\n\t\t\t\t\t// `rootContainer` is the world itself — it owns the\n\t\t\t\t\t// quadtree, it isn't an item inside it. Mirrors the\n\t\t\t\t\t// `insertContainer` guard above.\n\t\t\t\t\tif (child.name !== \"rootContainer\") {\n\t\t\t\t\t\tthis.remove(child);\n\t\t\t\t\t}\n\t\t\t\t\tthis.removeContainer(child);\n\t\t\t\t} else if (typeof child.getBounds === \"function\") {\n\t\t\t\t\tthis.remove(child);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Return all objects that could collide with the given object.\n\t *\n\t * **Re-entrancy contract:** when called with no explicit `result`\n\t * argument, this method reuses a single root-level scratch array to\n\t * avoid per-frame allocations. The returned reference is therefore\n\t * **not safe to retain** past the next `retrieve()` call, AND it is\n\t * **not safe to issue another scratch-mode `retrieve()` while iterating\n\t * the previous result** — the second call clears the scratch and\n\t * refills it, corrupting the outer iteration. In-engine callers\n\t * (`pointerevent.ts`, `detector.js`) iterate synchronously and never\n\t * recurse into `retrieve()`, so they're fine. User-facing portable\n\t * APIs (`adapter.queryAABB`, `adapter.raycast`) pass their own array\n\t * via the `result` parameter, which bypasses the scratch entirely\n\t * and is safe to call from inside collision handlers.\n\t * @param item - object to be checked against\n\t * @param [fn] - a sorting function for the returned array\n\t * @param [result] - optional caller-supplied result array. Pass an\n\t *   explicit (typically empty) array to sidestep the shared scratch —\n\t *   required for re-entrancy safety.\n\t * @returns array with all detected objects\n\t */\n\tretrieve(\n\t\titem: QuadTreeItem,\n\t\tfn?: QuadTreeSortFn,\n\t\tresult?: QuadTreeItem[],\n\t): QuadTreeItem[] {\n\t\t// Reuse the root's scratch array across calls. Pointer events\n\t\t// fire on every mouse move and each one used to allocate a\n\t\t// fresh `[]`; resetting the existing array's length to 0 is\n\t\t// allocation-free. See the JSDoc above for the re-entrancy\n\t\t// contract that this optimization implies.\n\t\tconst isRoot = typeof result === \"undefined\";\n\t\tlet out: QuadTreeItem[];\n\t\tif (isRoot) {\n\t\t\tout = this._retrieveScratch!;\n\t\t\tout.length = 0;\n\t\t} else {\n\t\t\tout = result;\n\t\t}\n\n\t\t// add objects at this level\n\t\tconst objects = this.objects;\n\t\tfor (let i = 0, len = objects.length; i < len; i++) {\n\t\t\tout.push(objects[i]);\n\t\t}\n\n\t\t//if we have subnodes ...\n\t\tif (this.nodes.length > 0) {\n\t\t\tconst index = this.getIndex(item);\n\n\t\t\t//if rect fits into a subnode ..\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.nodes[index].retrieve(item, undefined, out);\n\t\t\t} else {\n\t\t\t\t//if rect does not fit into a subnode, check it against all subnodes\n\t\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\t\tthis.nodes[i].retrieve(item, undefined, out);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (isRoot && typeof fn === \"function\") {\n\t\t\tout.sort(fn);\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t/**\n\t * Remove the given item from the quadtree.\n\t * (this function won't recalculate the impacted node)\n\t * @param item - object to be removed\n\t * @returns true if the item was found and removed.\n\t */\n\tremove(item: QuadTreeItem): boolean {\n\t\tlet found = false;\n\n\t\tif (typeof item.getBounds === \"undefined\") {\n\t\t\t// ignore object that cannot be added in the first place\n\t\t\treturn false;\n\t\t}\n\n\t\t//if we have subnodes ...\n\t\tif (this.nodes.length > 0) {\n\t\t\t// determine which subnode the item's CURRENT bounds maps\n\t\t\t// to. If the item moved between insert and remove, this\n\t\t\t// might point to a different subnode than where the item\n\t\t\t// actually lives — the fallback walk below handles that.\n\t\t\tconst index = this.getIndex(item);\n\n\t\t\tif (index !== -1) {\n\t\t\t\tfound = this.nodes[index].remove(item);\n\t\t\t}\n\t\t}\n\n\t\tif (!found) {\n\t\t\t// try and remove the item from the list of items in this node\n\t\t\tconst idx = this.objects.indexOf(item);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis.objects.splice(idx, 1);\n\t\t\t\tfound = true;\n\t\t\t}\n\t\t}\n\n\t\t// Stale-bounds fallback: the item didn't fit in the expected\n\t\t// subnode and wasn't at this level either. The item's bounds\n\t\t// may have changed since insert (the broadphase wasn't\n\t\t// rebuilt in between). Walk every subnode as a last resort.\n\t\t// Costs O(subtree) in the worst case, but only fires on the\n\t\t// stale-position path; happy path stays O(log n).\n\t\tif (!found && this.nodes.length > 0) {\n\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\tif (this.nodes[i].remove(item)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (found) {\n\t\t\tthis._subtreeCount--;\n\n\t\t\t// Collapse: if this subtree is now fully empty (no own\n\t\t\t// objects, all subnodes are empty), recycle the subnodes\n\t\t\t// back to the global `QT_ARRAY` pool and drop them. Keeps\n\t\t\t// the tree compact across the deferred-destroy churn that\n\t\t\t// `Container.removeChildNow` produces, and lets the pool\n\t\t\t// reuse the QuadTree instances on the next split.\n\t\t\tif (\n\t\t\t\tthis.nodes.length > 0 &&\n\t\t\t\tthis.objects.length === 0 &&\n\t\t\t\tthis._subtreeCount === 0\n\t\t\t) {\n\t\t\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\t\t\tthis.nodes[i].clear();\n\t\t\t\t\tQT_ARRAY_PUSH(this.nodes[i]);\n\t\t\t\t}\n\t\t\t\tthis.nodes.length = 0;\n\t\t\t}\n\t\t}\n\n\t\treturn found;\n\t}\n\n\t/**\n\t * return true if the node is prunable\n\t * @returns true if the node is prunable\n\t */\n\tisPrunable(): boolean {\n\t\t// O(1) thanks to the cached subtree count maintained by\n\t\t// insert/remove. Previously a recursive `hasChildren()` walk.\n\t\treturn this._subtreeCount === 0;\n\t}\n\n\t/**\n\t * return true if the node has any children\n\t * @returns true if the node has any children\n\t */\n\thasChildren(): boolean {\n\t\t// Descendant content exists iff total subtree count exceeds\n\t\t// our own object count (anything counted at this level beyond\n\t\t// `objects` must live in a subnode).\n\t\treturn this._subtreeCount > this.objects.length;\n\t}\n\n\t/**\n\t * clear the quadtree\n\t * @param [bounds] - the bounds to be cleared\n\t */\n\tclear(bounds?: Bounds) {\n\t\tthis.objects.length = 0;\n\n\t\tfor (let i = 0; i < this.nodes.length; i++) {\n\t\t\tthis.nodes[i].clear();\n\t\t\t// recycle the quadTree object\n\t\t\tQT_ARRAY_PUSH(this.nodes[i]);\n\t\t}\n\t\t// empty the array\n\t\tthis.nodes.length = 0;\n\n\t\tthis._subtreeCount = 0;\n\n\t\t// resize the root bounds if required\n\t\tif (typeof bounds !== \"undefined\") {\n\t\t\t(this.bounds as Bounds).setMinMax(\n\t\t\t\tbounds.min.x,\n\t\t\t\tbounds.min.y,\n\t\t\t\tbounds.max.x,\n\t\t\t\tbounds.max.y,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Structural shape consumed by `insertContainer` / `removeContainer`\n * when walking a Container's `getChildren()`. Captures only the\n * fields the walk inspects — keeps the conversion strict-mode clean\n * without leaking `any`.\n *\n * `getChildren` is optional because leaf renderables don't have it;\n * the recursive `insertContainer` narrows via {@link hasGetChildren}.\n * @ignore\n */\ninterface ContainerOrChild extends QuadTreeItem {\n\taddChild?: (...args: unknown[]) => unknown;\n\tgetChildren?: () => ContainerOrChild[];\n}\n\n/**\n * Structural shape used as the `insertContainer` /\n * `removeContainer` parameter type. Named (rather than inline) so the\n * JSDoc-aware lint doesn't require nested `@param container.getChildren`\n * documentation at every call site. `Container` itself satisfies this\n * shape; the optional variant covers the case where the World's lazy\n * `children` accessor returns `undefined`.\n * @ignore\n */\ntype ContainerLike = { getChildren(): ContainerOrChild[] };\n/** @ignore */\ntype ContainerLikeOptional = { getChildren?(): ContainerOrChild[] };\n\n/**\n * Type predicate: narrows a `ContainerOrChild` to one whose\n * `getChildren` is definitely a function. Lets the recursive\n * `insertContainer(child)` call pass without an\n * `as Required<Pick<..., \"getChildren\">>` assertion.\n * @param c - the candidate to test\n * @returns true if `c` has a callable `getChildren`\n */\nfunction hasGetChildren(\n\tc: ContainerOrChild,\n): c is ContainerOrChild & ContainerLike {\n\treturn typeof c.getChildren === \"function\";\n}\n","import { collision } from \"../collision.js\";\nimport type World from \"../world.js\";\nimport { AABB3d } from \"./aabb3d.ts\";\nimport type { Broadphase } from \"./broadphase.ts\";\nimport Octree from \"./octree.ts\";\nimport QuadTree from \"./quadtree.ts\";\n\n/**\n * Build the spatial broadphase that matches `world.sortOn`. Called\n * by `World` on construction and on every 2D↔3D sortOn flip; not\n * part of the user-facing surface.\n *\n * - `sortOn === \"depth\"` → {@link Octree} rooted at an origin-\n *   centred ±10000 box. Camera3d games conventionally centre the\n *   play area on the world origin (items at e.g.\n *   `x ∈ [-PLAY_BOUND_X, PLAY_BOUND_X]`); `world.getBounds()` returns\n *   the 2D viewport rect `(0..w, 0..h)` which doesn't contain those\n *   items. Using viewport bounds as the octree root would\n *   misclassify negative-coord items into octants whose AABBs don't\n *   contain them, breaking `querySphere`'s spatial pruning. ±10000\n *   covers arcade-3D scales (AfterBurner ≈ ±350 xy, ±1000 z); items\n *   outside still work — `getIndex` keeps them at `root.objects` so\n *   queries walk them via the root-level pass, just without the\n *   spatial-partition win.\n * - any other sortOn → {@link QuadTree} rooted at\n *   `world.getBounds().clone()` (the viewport rect).\n *\n * Lives in `physics/broadphase/` (not on `World`) so the World class\n * doesn't need to know which concrete tree class implements the\n * spatial index, or what bounds shape each class wants — those are\n * broadphase concerns.\n * @param world - the world this broadphase will index\n */\nexport function createBroadphase(world: World): Broadphase {\n\tif (world.sortOn === \"depth\") {\n\t\tconst aabb = new AABB3d();\n\t\taabb.setMinMax(-10000, -10000, -10000, 10000, 10000, 10000);\n\t\treturn new Octree(world, aabb, collision.maxChildren, collision.maxDepth);\n\t}\n\treturn new QuadTree(\n\t\tworld,\n\t\tworld.getBounds().clone(),\n\t\tcollision.maxChildren,\n\t\tcollision.maxDepth,\n\t);\n}\n","import { Vector2d } from \"../math/vector2d.ts\";\n\n/**\n * An object representing the result of an intersection.\n * @typedef {object} ResponseObject\n * @property {Renderable} a The first object participating in the intersection\n * @property {Renderable} b The second object participating in the intersection\n * @property {number} overlap Magnitude of the overlap on the shortest colliding axis\n * @property {Vector2d} overlapV The overlap vector (i.e. `overlapN.scale(overlap, overlap)`). If this vector is subtracted from the position of a, a and b will no longer be colliding\n * @property {Vector2d} overlapN The shortest colliding axis (unit-vector)\n * @property {number} overlapNZ The Z component of the shortest colliding axis, as a unit scalar (`-1`, `0` or `1`). Always `0` for a collision between planar shapes\n * @property {number} overlapZ The Z component of the overlap vector (i.e. `overlapNZ * overlap`). Always `0` for a collision between planar shapes\n * @property {boolean} aInB Whether the first object is entirely inside the second\n * @property {boolean} bInA Whether the second object is entirely inside the first\n * @property {number} indexShapeA The index of the colliding shape for the object a body\n * @property {number} indexShapeB The index of the colliding shape for the object b body\n */\nclass ResponseObject {\n\tconstructor() {\n\t\tthis.a = null;\n\t\tthis.b = null;\n\t\tthis.overlapN = new Vector2d();\n\t\tthis.overlapV = new Vector2d();\n\t\t/**\n\t\t * Z half of the minimum translation axis.\n\t\t *\n\t\t * Z arrives as **scalars beside** `overlapN` / `overlapV` rather than\n\t\t * by widening them to {@link Vector3d}, because `Vector3d` is not a\n\t\t * subclass of {@link Vector2d} — retyping them would break every\n\t\t * existing consumer of a 2D collision response.\n\t\t *\n\t\t * The minimum translation axis is a single axis, so at most one of\n\t\t * `overlapN.x`, `overlapN.y` and `overlapNZ` is ever non-zero. The 2D\n\t\t * invariant `overlapV = overlapN * overlap` therefore extends\n\t\t * unchanged as `overlapZ = overlapNZ * overlap`, and a collision\n\t\t * resolved along Z leaves `overlapN` / `overlapV` at zero — a legacy\n\t\t * 2D handler reading them applies no push, which is correct, because\n\t\t * there is no 2D push to apply.\n\t\t *\n\t\t * Only ever non-zero when both shapes are a {@link Box3d}; every\n\t\t * planar shape pair leaves these at `0`.\n\t\t */\n\t\tthis.overlapNZ = 0;\n\t\tthis.overlapZ = 0;\n\t\tthis.aInB = true;\n\t\tthis.bInA = true;\n\t\tthis.indexShapeA = -1;\n\t\tthis.indexShapeB = -1;\n\t\tthis.isTriggerContact = false;\n\t\tthis.overlap = Number.MAX_VALUE;\n\t}\n\n\t/**\n\t * Set some values of the response back to their defaults. <br>\n\t * Call this between tests if you are going to reuse a single <br>\n\t * Response object for multiple intersection tests <br>\n\t * (recommended as it will avoid allocating extra memory) <br>\n\t * @name clear\n\t * @public\n\t * @returns {object} this object for chaining\n\t */\n\tclear() {\n\t\tthis.aInB = true;\n\t\tthis.bInA = true;\n\t\tthis.overlap = Number.MAX_VALUE;\n\t\tthis.indexShapeA = -1;\n\t\tthis.indexShapeB = -1;\n\t\tthis.isTriggerContact = false;\n\t\t// Reset alongside `overlap`, so a Box3d pair resolved along Z cannot\n\t\t// leak its Z push into the next test — which, for a planar pair, would\n\t\t// be a Z push that no shape in the test has any depth to justify.\n\t\tthis.overlapNZ = 0;\n\t\tthis.overlapZ = 0;\n\t\treturn this;\n\t}\n}\n\nexport default ResponseObject;\n","import type { Ellipse } from \"../../geometries/ellipse.ts\";\nimport { linePool } from \"../../geometries/line.ts\";\nimport type { Polygon } from \"../../geometries/polygon.ts\";\nimport { Vector2d } from \"../../math/vector2d.ts\";\nimport type Renderable from \"../../renderable/renderable.js\";\nimport type { RaycastHit } from \"../adapter.ts\";\nimport type World from \"../world.js\";\nimport type Body from \"./body.js\";\n\n/**\n * Shared internal raycast walker for the built-in physics path. Used by\n * BOTH `BuiltinAdapter.raycast` (portable single-nearest-hit API) and\n * `Detector.rayCast` (legacy multi-hit renderable list). Single SAT walk,\n * two thin format adapters on top.\n *\n * Computes **actual entry-point geometry** per shape:\n *   point     = the parametric intersection on the shape's surface\n *   normal    = the polygon edge normal (or ellipse surface normal) at\n *               the entry, pointing back toward the ray origin\n *   fraction  = parametric `t` on `[0, 1]` along the ray (0 = `from`,\n *               1 = `to`)\n *\n * Hits are sorted nearest-first. Sub-shape edges of compound bodies are\n * collapsed to one hit per renderable (the earliest entry across all\n * shapes wins, matching what game code wants for \"what did the ray\n * touch first\").\n */\n\ninterface ShapeHit {\n\tt: number;\n\tnormalX: number;\n\tnormalY: number;\n}\n\nfunction _cross(ax: number, ay: number, bx: number, by: number): number {\n\t// 2D cross product of (a → b) and (a → c): positive if c is to the\n\t// left of a → b, negative if right, zero if colinear. Reused by the\n\t// segment-vs-segment intersection.\n\treturn ax * by - ay * bx;\n}\n\nfunction _computeShapeAbsPos(\n\trenderable: Renderable,\n\tshape: Polygon | Ellipse,\n\toutPos: Vector2d,\n): void {\n\t// Compute the absolute world position of a shape attached to a body\n\t// attached to a renderable. Mirrors what SAT does:\n\t//   absPos = renderable.pos + ancestor.absPos + shape.pos\n\tconst ancestor = renderable.ancestor as\n\t\t| { getAbsolutePosition?(): { x: number; y: number } }\n\t\t| undefined;\n\tconst ancestorAbs =\n\t\tancestor && typeof ancestor.getAbsolutePosition === \"function\"\n\t\t\t? ancestor.getAbsolutePosition()\n\t\t\t: null;\n\tconst ax = ancestorAbs ? ancestorAbs.x : 0;\n\tconst ay = ancestorAbs ? ancestorAbs.y : 0;\n\toutPos.x = renderable.pos.x + ax + shape.pos.x;\n\toutPos.y = renderable.pos.y + ay + shape.pos.y;\n}\n\nconst _shapeAbs = new Vector2d(0, 0);\n\nfunction _raycastPolygon(\n\tfromX: number,\n\tfromY: number,\n\tdx: number,\n\tdy: number,\n\tshape: Polygon,\n\tshapeAbsX: number,\n\tshapeAbsY: number,\n): ShapeHit | null {\n\t// Find the smallest ray parameter `t` (in `[0, 1]`) where the ray\n\t// `from` → `to` enters the polygon, along with the outward normal of\n\t// the hit edge. Returns `null` if the ray misses every edge.\n\t//\n\t// Iterates polygon edges as line segments and solves the line-\n\t// segment vs line-segment intersection:\n\t//   ray:   from + t * (to - from), t ∈ [0, 1]\n\t//   edge:  A + s * (B - A),         s ∈ [0, 1]\n\tconst points = shape.points;\n\tconst normals = shape.normals;\n\tconst len = points.length;\n\tlet bestT = Infinity;\n\tlet bestNormalIdx = -1;\n\n\tfor (let i = 0; i < len; i++) {\n\t\tconst p0 = points[i];\n\t\tconst p1 = points[(i + 1) % len];\n\t\tconst ax = shapeAbsX + p0.x;\n\t\tconst ay = shapeAbsY + p0.y;\n\t\tconst ex = p1.x - p0.x;\n\t\tconst ey = p1.y - p0.y;\n\n\t\tconst denom = _cross(dx, dy, ex, ey);\n\t\t// Parallel (or near-parallel) — skip; SAT handled the grazing case.\n\t\tif (Math.abs(denom) < 1e-9) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst fx = ax - fromX;\n\t\tconst fy = ay - fromY;\n\t\tconst t = _cross(fx, fy, ex, ey) / denom;\n\t\tconst s = _cross(fx, fy, dx, dy) / denom;\n\t\tif (t >= 0 && t <= 1 && s >= 0 && s <= 1 && t < bestT) {\n\t\t\tbestT = t;\n\t\t\tbestNormalIdx = i;\n\t\t}\n\t}\n\n\tif (bestNormalIdx === -1) {\n\t\treturn null;\n\t}\n\tconst normal = normals[bestNormalIdx];\n\treturn { t: bestT, normalX: normal.x, normalY: normal.y };\n}\n\nfunction _raycastEllipse(\n\tfromX: number,\n\tfromY: number,\n\tdx: number,\n\tdy: number,\n\tshape: Ellipse,\n\tshapeAbsX: number,\n\tshapeAbsY: number,\n): ShapeHit | null {\n\t// Find the smallest ray parameter `t` where the ray enters the\n\t// ellipse, plus the surface normal at that point. Solves the\n\t// standard quadratic for\n\t// `((px - cx) / rx)² + ((py - cy) / ry)² = 1`:\n\t//   A·t² + B·t + C = 0\n\t// where:\n\t//   A = (dx/rx)² + (dy/ry)²\n\t//   B = 2 · ((dx · (fx-cx))/rx² + (dy · (fy-cy))/ry²)\n\t//   C = ((fx-cx)/rx)² + ((fy-cy)/ry)² - 1\n\tconst cx = shapeAbsX;\n\tconst cy = shapeAbsY;\n\tconst rx = shape.radiusV.x;\n\tconst ry = shape.radiusV.y;\n\tif (rx <= 0 || ry <= 0) {\n\t\treturn null;\n\t}\n\tconst ox = (fromX - cx) / rx;\n\tconst oy = (fromY - cy) / ry;\n\tconst ddx = dx / rx;\n\tconst ddy = dy / ry;\n\tconst A = ddx * ddx + ddy * ddy;\n\tconst B = 2 * (ddx * ox + ddy * oy);\n\tconst C = ox * ox + oy * oy - 1;\n\tconst disc = B * B - 4 * A * C;\n\tif (disc < 0 || A < 1e-12) {\n\t\treturn null;\n\t}\n\tconst sqrtDisc = Math.sqrt(disc);\n\tconst t0 = (-B - sqrtDisc) / (2 * A);\n\tconst t1 = (-B + sqrtDisc) / (2 * A);\n\t// Smaller positive root in [0, 1] is the entry; if ray origin is\n\t// inside the ellipse (`t0 < 0`), the entry is behind us so we use\n\t// `t1` (the exit point on this side).\n\tlet t: number;\n\tif (t0 >= 0 && t0 <= 1) {\n\t\tt = t0;\n\t} else if (t1 >= 0 && t1 <= 1) {\n\t\tt = t1;\n\t} else {\n\t\treturn null;\n\t}\n\tconst hitX = fromX + dx * t;\n\tconst hitY = fromY + dy * t;\n\t// Ellipse surface normal at (x, y) is gradient of the implicit\n\t// form: ∇F = (2(x-cx)/rx², 2(y-cy)/ry²). Normalise.\n\tlet nx = (hitX - cx) / (rx * rx);\n\tlet ny = (hitY - cy) / (ry * ry);\n\tconst nLen = Math.hypot(nx, ny);\n\tif (nLen > 0) {\n\t\tnx /= nLen;\n\t\tny /= nLen;\n\t}\n\treturn { t, normalX: nx, normalY: ny };\n}\n\nexport function raycastQuery(\n\tworld: World,\n\tfromX: number,\n\tfromY: number,\n\ttoX: number,\n\ttoY: number,\n): RaycastHit[] {\n\t// Walk the world's broadphase and return every body the ray\n\t// `(fromX, fromY) → (toX, toY)` enters, sorted nearest-first.\n\tconst dx = toX - fromX;\n\tconst dy = toY - fromY;\n\tconst segLen = Math.hypot(dx, dy);\n\tif (segLen < 1e-9) {\n\t\t// Degenerate ray — no direction, nothing to hit.\n\t\treturn [];\n\t}\n\n\t// Build a Line geometry for broadphase retrieval. We only use it for\n\t// the bounds-based AABB query; the per-shape entry math is done\n\t// directly below in `_raycastPolygon` / `_raycastEllipse`.\n\tconst line = linePool.get(fromX, fromY, [\n\t\tnew Vector2d(0, 0),\n\t\tnew Vector2d(dx, dy),\n\t]);\n\n\t// Pass our own result array to `retrieve` so we never touch the\n\t// broadphase's shared scratch — user code may call `adapter.raycast`\n\t// from inside an `onCollisionStart` handler that itself fires mid\n\t// iteration of the SAT detector's scratch walk; sharing would clobber\n\t// the outer iteration.\n\tconst candidates: Renderable[] = [];\n\tworld.broadphase.retrieve(line, undefined, candidates);\n\tconst hits: RaycastHit[] = [];\n\n\tfor (let i = candidates.length - 1; i >= 0; i--) {\n\t\tconst objB = candidates[i];\n\t\tif (!objB.body) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!line.getBounds().overlaps(objB.getBounds())) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// This raycast walks the BuiltinAdapter's broadphase, so every\n\t\t// `objB.body` here is a concrete builtin `Body` — narrow from\n\t\t// the portable `PhysicsBody` interface (which doesn't expose\n\t\t// `shapes` / `getShape` since those are adapter-specific) to\n\t\t// the concrete type.\n\t\tconst bodyB = objB.body as Body;\n\t\tconst shapeCount = bodyB.shapes.length;\n\t\tif (shapeCount === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Walk every shape in the compound body and keep the earliest\n\t\t// entry — that's \"what the ray hit first\" for the renderable as\n\t\t// a whole.\n\t\tlet bestT = Infinity;\n\t\tlet bestNormalX = 0;\n\t\tlet bestNormalY = 0;\n\t\tfor (let j = 0; j < shapeCount; j++) {\n\t\t\tconst shapeB = bodyB.getShape(j);\n\t\t\t// `isActive === false` takes a shape out of collision entirely, and\n\t\t\t// a ray IS a collision query — a body whose only shape is inactive\n\t\t\t// must not be hit (#1590)\n\t\t\tif ((shapeB as { isActive?: boolean }).isActive === false) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t_computeShapeAbsPos(objB, shapeB, _shapeAbs);\n\t\t\tconst hit =\n\t\t\t\tshapeB.type === \"Ellipse\"\n\t\t\t\t\t? _raycastEllipse(\n\t\t\t\t\t\t\tfromX,\n\t\t\t\t\t\t\tfromY,\n\t\t\t\t\t\t\tdx,\n\t\t\t\t\t\t\tdy,\n\t\t\t\t\t\t\tshapeB as Ellipse,\n\t\t\t\t\t\t\t_shapeAbs.x,\n\t\t\t\t\t\t\t_shapeAbs.y,\n\t\t\t\t\t\t)\n\t\t\t\t\t: _raycastPolygon(\n\t\t\t\t\t\t\tfromX,\n\t\t\t\t\t\t\tfromY,\n\t\t\t\t\t\t\tdx,\n\t\t\t\t\t\t\tdy,\n\t\t\t\t\t\t\tshapeB as Polygon,\n\t\t\t\t\t\t\t_shapeAbs.x,\n\t\t\t\t\t\t\t_shapeAbs.y,\n\t\t\t\t\t\t);\n\t\t\tif (hit !== null && hit.t < bestT) {\n\t\t\t\tbestT = hit.t;\n\t\t\t\tbestNormalX = hit.normalX;\n\t\t\t\tbestNormalY = hit.normalY;\n\t\t\t}\n\t\t}\n\n\t\tif (bestT === Infinity) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Flip normal to point back toward the ray origin (Box2D / planck\n\t\t// convention — caller treats `normal` as \"the direction the ray\n\t\t// bounces off in\"). Edge normals from the polygon side already\n\t\t// point outward, so this is usually a no-op; ellipse normals\n\t\t// point outward from the centre, also a no-op for an entry hit.\n\t\t// We still check to be robust.\n\t\tconst flipSign = bestNormalX * dx + bestNormalY * dy > 0 ? -1 : 1;\n\n\t\thits.push({\n\t\t\trenderable: objB,\n\t\t\tpoint: new Vector2d(fromX + dx * bestT, fromY + dy * bestT),\n\t\t\tnormal: new Vector2d(bestNormalX * flipSign, bestNormalY * flipSign),\n\t\t\tfraction: bestT,\n\t\t});\n\t}\n\n\tlinePool.release(line);\n\n\thits.sort((a, b) => {\n\t\treturn a.fraction - b.fraction;\n\t});\n\treturn hits;\n}\n","import { Vector2d } from \"../../math/vector2d.ts\";\n\n/*\n * Separating Axis Theorem implementation, based on the SAT.js library by Jim Riecken <jimr@jimr.ca>\n * Available under the MIT License - https://github.com/jriecken/sat-js\n */\n\n/**\n * @import Entity from \"../../renderable/entity/entity.js\";\n * @import Container from \"../../renderable/container.js\";\n * @import Renderable from \"../../renderable/renderable.js\";\n * @import Sprite from \"../../renderable/sprite.js\";\n * @import NineSliceSprite from \"../../renderable/nineslicesprite.js\";\n * @import {Polygon} from \"../../geometries/polygon.ts\";\n * @import {Ellipse} from \"../../geometries/ellipse.ts\";\n */\n\n/**\n * Constants for Vornoi regions\n * @ignore\n */\nconst LEFT_VORNOI_REGION = -1;\n\n/**\n * Constants for Vornoi regions\n * @ignore\n */\nconst MIDDLE_VORNOI_REGION = 0;\n\n/**\n * Constants for Vornoi regions\n * @ignore\n */\nconst RIGHT_VORNOI_REGION = 1;\n\n/**\n * A pool of `Vector` objects that are used in calculations to avoid allocating memory.\n * @type {Array.<Vector2d>}\n * @ignore\n */\nconst T_VECTORS = [];\nfor (let v = 0; v < 10; v++) {\n\tT_VECTORS.push(new Vector2d());\n}\n/**\n * Stack index for the T_VECTORS pool. Points to the next available slot.\n * @ignore\n */\nlet T_VECTORS_IDX = 10;\n\n/**\n * A pool of arrays of numbers used in calculations to avoid allocating memory.\n * @type {Array.<Array.<number>>}\n * @ignore\n */\nconst T_ARRAYS = [];\nfor (let a = 0; a < 5; a++) {\n\tT_ARRAYS.push([]);\n}\n/**\n * Stack index for the T_ARRAYS pool. Points to the next available slot.\n * @ignore\n */\nlet T_ARRAYS_IDX = 5;\n\n/**\n * Flattens the specified array of points onto a unit vector axis,\n * resulting in a one dimensional range of the minimum and\n * maximum value on that axis.\n * @ignore\n * @param {Array.<Vector2d>} points - The points to flatten.\n * @param {Vector2d} normal - The unit vector axis to flatten on.\n * @param {Array.<number>} result - An array.  After calling this function,\n *   result[0] will be the minimum value,\n *   result[1] will be the maximum value.\n */\nfunction flattenPointsOn(points, normal, result) {\n\tlet min = Number.MAX_VALUE;\n\tlet max = -Number.MAX_VALUE;\n\tconst len = points.length;\n\tfor (let i = 0; i < len; i++) {\n\t\t// The magnitude of the projection of the point onto the normal\n\t\tconst dot = points[i].dot(normal);\n\t\tif (dot < min) {\n\t\t\tmin = dot;\n\t\t}\n\t\tif (dot > max) {\n\t\t\tmax = dot;\n\t\t}\n\t}\n\tresult[0] = min;\n\tresult[1] = max;\n}\n\n/**\n * Check whether two convex polygons are separated by the specified\n * axis (must be a unit vector).\n * @ignore\n * @param {Vector2d} aPos - The position of the first polygon.\n * @param {Vector2d} bPos - The position of the second polygon.\n * @param {Array.<Vector2d>} aPoints - The points in the first polygon.\n * @param {Array.<Vector2d>} bPoints - The points in the second polygon.\n * @param {Vector2d} axis - The axis (unit sized) to test against.  The points of both polygons\n *   will be projected onto this axis.\n * @param {Response=} response - A Response object (optional) which will be populated\n *   if the axis is not a separating axis.\n * @returns {boolean} true if it is a separating axis, false otherwise.  If false,\n *   and a response is passed in, information about how much overlap and\n *   the direction of the overlap will be populated.\n */\nfunction isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response) {\n\tconst rangeA = T_ARRAYS[--T_ARRAYS_IDX];\n\tconst rangeB = T_ARRAYS[--T_ARRAYS_IDX];\n\t// The magnitude of the offset between the two polygons\n\tconst offsetV = T_VECTORS[--T_VECTORS_IDX].copy(bPos).sub(aPos);\n\tconst projectedOffset = offsetV.dot(axis);\n\n\t// Project the polygons onto the axis.\n\tflattenPointsOn(aPoints, axis, rangeA);\n\tflattenPointsOn(bPoints, axis, rangeB);\n\t// Move B's range to its position relative to A.\n\trangeB[0] += projectedOffset;\n\trangeB[1] += projectedOffset;\n\t// Check if there is a gap. If there is, this is a separating axis and we can stop\n\tif (rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) {\n\t\tT_VECTORS_IDX++;\n\t\tT_ARRAYS_IDX += 2;\n\t\treturn true;\n\t}\n\n\t// This is not a separating axis. If we're calculating a response, calculate the overlap.\n\tif (response) {\n\t\tlet overlap;\n\t\t// A starts further left than B\n\t\tif (rangeA[0] < rangeB[0]) {\n\t\t\tresponse.aInB = false;\n\t\t\t// A ends before B does. We have to pull A out of B\n\t\t\tif (rangeA[1] < rangeB[1]) {\n\t\t\t\toverlap = rangeA[1] - rangeB[0];\n\t\t\t\tresponse.bInA = false;\n\t\t\t\t// B is fully inside A.  Pick the shortest way out.\n\t\t\t} else {\n\t\t\t\tconst option1 = rangeA[1] - rangeB[0];\n\t\t\t\tconst option2 = rangeB[1] - rangeA[0];\n\t\t\t\toverlap = option1 < option2 ? option1 : -option2;\n\t\t\t}\n\t\t\t// B starts further left than A\n\t\t} else {\n\t\t\tresponse.bInA = false;\n\t\t\t// B ends before A ends. We have to push A out of B\n\t\t\tif (rangeA[1] > rangeB[1]) {\n\t\t\t\toverlap = rangeA[0] - rangeB[1];\n\t\t\t\tresponse.aInB = false;\n\t\t\t\t// A is fully inside B.  Pick the shortest way out.\n\t\t\t} else {\n\t\t\t\tconst option11 = rangeA[1] - rangeB[0];\n\t\t\t\tconst option22 = rangeB[1] - rangeA[0];\n\t\t\t\toverlap = option11 < option22 ? option11 : -option22;\n\t\t\t}\n\t\t}\n\n\t\t// If this is the smallest amount of overlap we've seen so far, set it as the minimum overlap.\n\t\tconst absOverlap = Math.abs(overlap);\n\t\tif (absOverlap < response.overlap) {\n\t\t\tresponse.overlap = absOverlap;\n\t\t\tresponse.overlapN.copy(axis);\n\t\t\tif (overlap < 0) {\n\t\t\t\tresponse.overlapN.negateSelf();\n\t\t\t}\n\t\t}\n\t}\n\tT_VECTORS_IDX++;\n\tT_ARRAYS_IDX += 2;\n\treturn false;\n}\n\n/**\n * Calculates which Vornoi region a point is on a line segment. <br>\n * It is assumed that both the line and the point are relative to `(0,0)`<br>\n * <pre>\n *             |       (0)      |\n *      (-1)  [S]--------------[E]  (1)\n *             |       (0)      |\n * </pre>\n *\n * @ignore\n * @param {Vector2d} line - The line segment.\n * @param {Vector2d} point - The point.\n * @returns  {number} LEFT_VORNOI_REGION (-1) if it is the left region,\n *          MIDDLE_VORNOI_REGION (0) if it is the middle region,\n *          RIGHT_VORNOI_REGION (1) if it is the right region.\n */\nfunction vornoiRegion(line, point) {\n\tconst len2 = line.length2();\n\tconst dp = point.dot(line);\n\tif (dp < 0) {\n\t\t// If the point is beyond the start of the line, it is in the\n\t\t// left vornoi region.\n\t\treturn LEFT_VORNOI_REGION;\n\t} else if (dp > len2) {\n\t\t// If the point is beyond the end of the line, it is in the\n\t\t// right vornoi region.\n\t\treturn RIGHT_VORNOI_REGION;\n\t} else {\n\t\t// Otherwise, it's in the middle one.\n\t\treturn MIDDLE_VORNOI_REGION;\n\t}\n}\n\n/**\n * Checks whether polygons collide.\n * @ignore\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} a - a reference to the object A.\n * @param {Polygon} polyA - a reference to the object A Polygon to be tested\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} b - a reference to the object B.\n * @param {Polygon} polyB - a reference to the object B Polygon to be tested\n * @param {Response=} response - Response object (optional) that will be populated if they intersect.\n * @returns {boolean} true if they intersect, false if they don't.\n */\nexport function testPolygonPolygon(a, polyA, b, polyB, response) {\n\t// specific point for\n\tconst aPoints = polyA.points;\n\tconst aNormals = polyA.normals;\n\tconst aLen = aNormals.length;\n\tconst bPoints = polyB.points;\n\tconst bNormals = polyB.normals;\n\tconst bLen = bNormals.length;\n\t// aboslute shape position\n\tconst posA = T_VECTORS[--T_VECTORS_IDX]\n\t\t.copy(a.pos)\n\t\t.add(a.ancestor.getAbsolutePosition())\n\t\t.add(polyA.pos);\n\tconst posB = T_VECTORS[--T_VECTORS_IDX]\n\t\t.copy(b.pos)\n\t\t.add(b.ancestor.getAbsolutePosition())\n\t\t.add(polyB.pos);\n\n\t// If any of the edge normals of A is a separating axis, no intersection.\n\tfor (let i = 0; i < aLen; i++) {\n\t\tif (isSeparatingAxis(posA, posB, aPoints, bPoints, aNormals[i], response)) {\n\t\t\tT_VECTORS_IDX += 2;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// If any of the edge normals of B is a separating axis, no intersection.\n\tfor (let i = 0; i < bLen; i++) {\n\t\tif (isSeparatingAxis(posA, posB, aPoints, bPoints, bNormals[i], response)) {\n\t\t\tT_VECTORS_IDX += 2;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Since none of the edge normals of A or B are a separating axis, there is an intersection\n\t// and we've already calculated the smallest overlap (in isSeparatingAxis).  Calculate the\n\t// final overlap vector.\n\tif (response) {\n\t\tresponse.a = a;\n\t\tresponse.b = b;\n\t\tresponse.overlapV.copy(response.overlapN).scale(response.overlap);\n\t}\n\tT_VECTORS_IDX += 2;\n\treturn true;\n}\n\n/**\n * Check if two Ellipse collide.\n * @ignore\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} a - a reference to the object A.\n * @param {Ellipse} ellipseA - a reference to the object A Ellipse to be tested\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} b - a reference to the object B.\n * @param {Ellipse} ellipseB - a reference to the object B Ellipse to be tested\n * @param {Response=} response - Response object (optional) that will be populated if\n *   the circles intersect.\n * @returns {boolean} true if the circles intersect, false if they don't.\n */\nexport function testEllipseEllipse(a, ellipseA, b, ellipseB, response) {\n\tconst isCircleA = ellipseA.radiusV.x === ellipseA.radiusV.y;\n\tconst isCircleB = ellipseB.radiusV.x === ellipseB.radiusV.y;\n\n\t// If either shape is a true ellipse (not a circle), use polygon approximation\n\tif (!isCircleA || !isCircleB) {\n\t\tif (!isCircleA && !isCircleB) {\n\t\t\t// both are ellipses: convert both to polygons\n\t\t\treturn testPolygonPolygon(\n\t\t\t\ta,\n\t\t\t\tellipseA.toPolygon(),\n\t\t\t\tb,\n\t\t\t\tellipseB.toPolygon(),\n\t\t\t\tresponse,\n\t\t\t);\n\t\t} else if (!isCircleA) {\n\t\t\t// A is ellipse, B is circle\n\t\t\treturn testPolygonEllipse(a, ellipseA.toPolygon(), b, ellipseB, response);\n\t\t} else {\n\t\t\t// A is circle, B is ellipse\n\t\t\treturn testEllipsePolygon(a, ellipseA, b, ellipseB.toPolygon(), response);\n\t\t}\n\t}\n\n\t// Fast path: both are circles\n\t// Check if the distance between the centers of the two\n\t// circles is greater than their combined radius. We need the absolute\n\t// position of B relative to A — both sides resolve their world position\n\t// via ancestor.getAbsolutePosition(), so A's offset must be subtracted\n\t// too, not added. (The bug was invisible when ancestor.absPos == 0, e.g.\n\t// the level was the same size as the viewport; it manifests as missed\n\t// circle collisions whenever the container is shifted, such as TMX\n\t// auto-centering on a wider viewport.)\n\tconst differenceV = T_VECTORS[--T_VECTORS_IDX]\n\t\t.copy(b.pos)\n\t\t.add(b.ancestor.getAbsolutePosition())\n\t\t.add(ellipseB.pos)\n\t\t.sub(a.pos)\n\t\t.sub(a.ancestor.getAbsolutePosition())\n\t\t.sub(ellipseA.pos);\n\tconst radiusA = ellipseA.radius;\n\tconst radiusB = ellipseB.radius;\n\tconst totalRadius = radiusA + radiusB;\n\tconst totalRadiusSq = totalRadius * totalRadius;\n\tconst distanceSq = differenceV.length2();\n\t// If the distance is bigger than the combined radius, they don't intersect.\n\tif (distanceSq > totalRadiusSq) {\n\t\tT_VECTORS_IDX++;\n\t\treturn false;\n\t}\n\t// They intersect.  If we're calculating a response, calculate the overlap.\n\tif (response) {\n\t\tconst dist = Math.sqrt(distanceSq);\n\t\tresponse.a = a;\n\t\tresponse.b = b;\n\t\tresponse.overlap = totalRadius - dist;\n\t\tresponse.overlapN.copy(differenceV.normalize());\n\t\tresponse.overlapV.copy(differenceV).scale(response.overlap);\n\t\tresponse.aInB = radiusA <= radiusB && dist <= radiusB - radiusA;\n\t\tresponse.bInA = radiusB <= radiusA && dist <= radiusA - radiusB;\n\t}\n\tT_VECTORS_IDX++;\n\treturn true;\n}\n\n/**\n * Check if a polygon and an ellipse collide.\n * @ignore\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} a - a reference to the object A.\n * @param {Polygon} polyA - a reference to the object A Polygon to be tested\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} b - a reference to the object B.\n * @param {Ellipse} ellipseB - a reference to the object B Ellipse to be tested\n * @param {Response=} response - Response object (optional) that will be populated if they intersect.\n * @returns {boolean} true if they intersect, false if they don't.\n */\nexport function testPolygonEllipse(a, polyA, b, ellipseB, response) {\n\t// If the ellipse is not a circle, convert to polygon and use polygon-polygon test\n\tif (ellipseB.radiusV.x !== ellipseB.radiusV.y) {\n\t\treturn testPolygonPolygon(a, polyA, b, ellipseB.toPolygon(), response);\n\t}\n\n\t// Fast path: ellipse is a circle\n\t// Get the position of the circle relative to the polygon. Both A and B\n\t// resolve their world position via ancestor.getAbsolutePosition(), so\n\t// A's ancestor offset must be subtracted (matches the polygon/polygon\n\t// path which builds absolute positions). The previous `+a.ancestor.abs`\n\t// was masked whenever the level container was at the origin, but missed\n\t// every circle-vs-polygon collision once the container was shifted (TMX\n\t// auto-centering on a wider viewport, manually translated containers).\n\tconst circlePos = T_VECTORS[--T_VECTORS_IDX]\n\t\t.copy(b.pos)\n\t\t.add(b.ancestor.getAbsolutePosition())\n\t\t.add(ellipseB.pos)\n\t\t.sub(a.pos)\n\t\t.sub(a.ancestor.getAbsolutePosition())\n\t\t.sub(polyA.pos);\n\tconst radius = ellipseB.radius;\n\tconst radius2 = radius * radius;\n\tconst points = polyA.points;\n\tconst edges = polyA.edges;\n\tconst len = edges.length;\n\tconst edge = T_VECTORS[--T_VECTORS_IDX];\n\tconst normal = T_VECTORS[--T_VECTORS_IDX];\n\tconst point = T_VECTORS[--T_VECTORS_IDX];\n\tlet dist;\n\n\t// For each edge in the polygon:\n\tfor (let i = 0; i < len; i++) {\n\t\tconst next = i === len - 1 ? 0 : i + 1;\n\t\tconst prev = i === 0 ? len - 1 : i - 1;\n\t\tlet overlap = 0;\n\t\tlet overlapN = null;\n\n\t\t// Get the edge.\n\t\tedge.copy(edges[i]);\n\t\t// Calculate the center of the circle relative to the starting point of the edge.\n\t\tpoint.copy(circlePos).sub(points[i]);\n\n\t\t// If the distance between the center of the circle and the point\n\t\t// is bigger than the radius, the polygon is definitely not fully in\n\t\t// the circle.\n\t\tif (response && point.length2() > radius2) {\n\t\t\tresponse.aInB = false;\n\t\t}\n\n\t\t// Calculate which Vornoi region the center of the circle is in.\n\t\tlet region = vornoiRegion(edge, point);\n\t\tlet inRegion = true;\n\t\t// If it's the left region:\n\t\tif (region === LEFT_VORNOI_REGION) {\n\t\t\tlet hasPoint2 = false;\n\t\t\tif (len > 1) {\n\t\t\t\t// We need to make sure we're in the RIGHT_VORNOI_REGION of the previous edge.\n\t\t\t\tedge.copy(edges[prev]);\n\t\t\t\t// Calculate the center of the circle relative the starting point of the previous edge\n\t\t\t\tconst point2 = T_VECTORS[--T_VECTORS_IDX]\n\t\t\t\t\t.copy(circlePos)\n\t\t\t\t\t.sub(points[prev]);\n\t\t\t\tregion = vornoiRegion(edge, point2);\n\t\t\t\thasPoint2 = true;\n\t\t\t\tif (region !== RIGHT_VORNOI_REGION) {\n\t\t\t\t\tinRegion = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inRegion) {\n\t\t\t\t// It's in the region we want.  Check if the circle intersects the point.\n\t\t\t\tdist = point.length();\n\t\t\t\tif (dist > radius) {\n\t\t\t\t\t// No intersection\n\t\t\t\t\tif (hasPoint2) {\n\t\t\t\t\t\tT_VECTORS_IDX++;\n\t\t\t\t\t}\n\t\t\t\t\tT_VECTORS_IDX += 4;\n\t\t\t\t\treturn false;\n\t\t\t\t} else if (response) {\n\t\t\t\t\t// It intersects, calculate the overlap.\n\t\t\t\t\tresponse.bInA = false;\n\t\t\t\t\toverlapN = point.normalize();\n\t\t\t\t\toverlap = radius - dist;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hasPoint2) {\n\t\t\t\tT_VECTORS_IDX++;\n\t\t\t}\n\t\t\t// If it's the right region:\n\t\t} else if (region === RIGHT_VORNOI_REGION) {\n\t\t\tif (len > 1) {\n\t\t\t\t// We need to make sure we're in the left region on the next edge\n\t\t\t\tedge.copy(edges[next]);\n\t\t\t\t// Calculate the center of the circle relative to the starting point of the next edge.\n\t\t\t\tpoint.copy(circlePos).sub(points[next]);\n\t\t\t\tregion = vornoiRegion(edge, point);\n\t\t\t\tif (region !== LEFT_VORNOI_REGION) {\n\t\t\t\t\tinRegion = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inRegion) {\n\t\t\t\t// It's in the region we want.  Check if the circle intersects the point.\n\t\t\t\tdist = point.length();\n\t\t\t\tif (dist > radius) {\n\t\t\t\t\t// No intersection\n\t\t\t\t\tT_VECTORS_IDX += 4;\n\t\t\t\t\treturn false;\n\t\t\t\t} else if (response) {\n\t\t\t\t\t// It intersects, calculate the overlap.\n\t\t\t\t\tresponse.bInA = false;\n\t\t\t\t\toverlapN = point.normalize();\n\t\t\t\t\toverlap = radius - dist;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Otherwise, it's the middle region:\n\t\t} else {\n\t\t\t// Need to check if the circle is intersecting the edge,\n\t\t\t// Get the normal.\n\t\t\tnormal.copy(polyA.normals[i]);\n\t\t\t// Find the perpendicular distance between the center of the\n\t\t\t// circle and the edge.\n\t\t\tdist = point.dot(normal);\n\t\t\tconst distAbs = Math.abs(dist);\n\t\t\t// If the circle is on the outside of the edge, there is no intersection.\n\t\t\tif ((len === 1 || dist > 0) && distAbs > radius) {\n\t\t\t\t// No intersection\n\t\t\t\tT_VECTORS_IDX += 4;\n\t\t\t\treturn false;\n\t\t\t} else if (response) {\n\t\t\t\t// It intersects, calculate the overlap.\n\t\t\t\toverlapN = normal;\n\t\t\t\toverlap = radius - dist;\n\t\t\t\t// If the center of the circle is on the outside of the edge, or part of the\n\t\t\t\t// circle is on the outside, the circle is not fully inside the polygon.\n\t\t\t\tif (dist >= 0 || overlap < 2 * radius) {\n\t\t\t\t\tresponse.bInA = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If this is the smallest overlap we've seen, keep it.\n\t\t// (overlapN may be null if the circle was in the wrong Vornoi region).\n\t\tif (\n\t\t\toverlapN &&\n\t\t\tresponse &&\n\t\t\tMath.abs(overlap) < Math.abs(response.overlap)\n\t\t) {\n\t\t\tresponse.overlap = overlap;\n\t\t\tresponse.overlapN.copy(overlapN);\n\t\t}\n\t}\n\n\t// Calculate the final overlap vector - based on the smallest overlap.\n\tif (response) {\n\t\tresponse.a = a;\n\t\tresponse.b = b;\n\t\tresponse.overlapV.copy(response.overlapN).scale(response.overlap);\n\t}\n\tT_VECTORS_IDX += 4;\n\treturn true;\n}\n\n/**\n * Check if an ellipse and a polygon collide. <br>\n * **NOTE:** This is slightly less efficient than testPolygonEllipse as it just\n * runs testPolygonEllipse and reverses the response at the end.\n * @ignore\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} a - a reference to the object A.\n * @param {Ellipse} ellipseA - a reference to the object A Ellipse to be tested\n * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} b - a reference to the object B.\n * @param {Polygon} polyB - a reference to the object B Polygon to be tested\n * @param {Response=} response - Response object (optional) that will be populated if\n *   they intersect.\n * @returns {boolean} true if they intersect, false if they don't.\n */\nexport function testEllipsePolygon(a, ellipseA, b, polyB, response) {\n\t// Test the polygon against the circle.\n\tconst result = testPolygonEllipse(b, polyB, a, ellipseA, response);\n\tif (result && response) {\n\t\t// Swap A and B in the response.\n\t\tconst resa = response.a;\n\t\tconst aInB = response.aInB;\n\t\tresponse.overlapN.negateSelf();\n\t\tresponse.overlapV.negateSelf();\n\t\tresponse.a = response.b;\n\t\tresponse.b = resa;\n\t\tresponse.aInB = response.bInA;\n\t\tresponse.bInA = aInB;\n\t}\n\treturn result;\n}\n","import {\n\ttestEllipsePolygon,\n\ttestPolygonEllipse,\n\ttestPolygonPolygon,\n} from \"./sat.js\";\n\n/**\n * @import {Box3d} from \"../../geometries/box3d.ts\";\n * @import {Polygon} from \"../../geometries/polygon.ts\";\n * @import {Ellipse} from \"../../geometries/ellipse.ts\";\n * @import Renderable from \"../../renderable/renderable.js\";\n */\n\n/**\n * Absolute (world) center of a {@link Box3d} shape on one axis.\n *\n * Mirrors the convention the 2D SAT tests use — a shape's world position is\n * `renderable.pos + renderable.ancestor.getAbsolutePosition() + shape.pos` —\n * except that all three terms are read in 3D. `getAbsolutePosition()` already\n * sums z across the whole ancestor chain.\n * @ignore\n */\nfunction absCenter(renderable, box, out) {\n\tconst anc = renderable.ancestor.getAbsolutePosition();\n\tout[0] = renderable.pos.x + anc.x + box.pos.x;\n\tout[1] = renderable.pos.y + anc.y + box.pos.y;\n\tout[2] = renderable.pos.z + anc.z + box.pos.z;\n\treturn out;\n}\n\n// module scratch for the two box centers; never escapes testBox3dBox3d\nconst _centerA = [0, 0, 0];\nconst _centerB = [0, 0, 0];\n\n/**\n * Check whether two axis-aligned 3D boxes collide.\n *\n * This is the only narrowphase in the engine that can produce a **Z**\n * pushback. The separating-axis set of two AABBs is just the three world axes,\n * so there is no projection loop: penetration on each axis is\n * `(halfA + halfB) - |centerDelta|`, a non-positive value on any axis means\n * separated, and the minimum translation vector is the axis with the smallest\n * positive penetration.\n *\n * The MTV is a single axis, so exactly one of `overlapN.x`, `overlapN.y` and\n * `overlapNZ` comes back non-zero — see {@link ResponseObject#overlapNZ}. When\n * that axis is Z the 2D fields stay at zero, which is what makes a legacy 2D\n * `onCollision` handler safely inert on a depth-only contact rather than\n * wrong.\n * @ignore\n * @param {Renderable} a - a reference to the object A.\n * @param {Box3d} boxA - a reference to the object A Box3d to be tested\n * @param {Renderable} b - a reference to the object B.\n * @param {Box3d} boxB - a reference to the object B Box3d to be tested\n * @param {object} [response] - Response object that will be populated if they intersect.\n * @returns {boolean} true if they intersect, false if they don't.\n */\nexport function testBox3dBox3d(a, boxA, b, boxB, response) {\n\tconst ca = absCenter(a, boxA, _centerA);\n\tconst cb = absCenter(b, boxB, _centerB);\n\n\tconst ha = boxA.halfExtents;\n\tconst hb = boxB.halfExtents;\n\n\tconst dx = cb[0] - ca[0];\n\tconst dy = cb[1] - ca[1];\n\tconst dz = cb[2] - ca[2];\n\n\tconst px = ha.x + hb.x - Math.abs(dx);\n\tif (px <= 0) {\n\t\treturn false;\n\t}\n\tconst py = ha.y + hb.y - Math.abs(dy);\n\tif (py <= 0) {\n\t\treturn false;\n\t}\n\tconst pz = ha.z + hb.z - Math.abs(dz);\n\tif (pz <= 0) {\n\t\treturn false;\n\t}\n\n\tif (response) {\n\t\tresponse.a = a;\n\t\tresponse.b = b;\n\n\t\t// Smallest positive penetration wins. Ties resolve X → Y → Z, which\n\t\t// keeps a body resting on a floor being pushed straight up rather\n\t\t// than sideways out of a corner when two axes penetrate equally.\n\t\tif (px <= py && px <= pz) {\n\t\t\t// `dx === 0` (perfectly concentric on this axis) has no\n\t\t\t// meaningful side, so bias to +1 rather than emitting a zero\n\t\t\t// normal, which would make the push-out a no-op and leave the\n\t\t\t// pair overlapping forever.\n\t\t\tconst n = dx < 0 ? -1 : 1;\n\t\t\tresponse.overlap = px;\n\t\t\tresponse.overlapN.set(n, 0);\n\t\t\tresponse.overlapV.set(n * px, 0);\n\t\t\tresponse.overlapNZ = 0;\n\t\t\tresponse.overlapZ = 0;\n\t\t} else if (py <= pz) {\n\t\t\tconst n = dy < 0 ? -1 : 1;\n\t\t\tresponse.overlap = py;\n\t\t\tresponse.overlapN.set(0, n);\n\t\t\tresponse.overlapV.set(0, n * py);\n\t\t\tresponse.overlapNZ = 0;\n\t\t\tresponse.overlapZ = 0;\n\t\t} else {\n\t\t\tconst n = dz < 0 ? -1 : 1;\n\t\t\tresponse.overlap = pz;\n\t\t\tresponse.overlapN.set(0, 0);\n\t\t\tresponse.overlapV.set(0, 0);\n\t\t\tresponse.overlapNZ = n;\n\t\t\tresponse.overlapZ = n * pz;\n\t\t}\n\n\t\tresponse.aInB =\n\t\t\tha.x <= hb.x &&\n\t\t\tha.y <= hb.y &&\n\t\t\tha.z <= hb.z &&\n\t\t\tMath.abs(dx) <= hb.x - ha.x &&\n\t\t\tMath.abs(dy) <= hb.y - ha.y &&\n\t\t\tMath.abs(dz) <= hb.z - ha.z;\n\t\tresponse.bInA =\n\t\t\thb.x <= ha.x &&\n\t\t\thb.y <= ha.y &&\n\t\t\thb.z <= ha.z &&\n\t\t\tMath.abs(dx) <= ha.x - hb.x &&\n\t\t\tMath.abs(dy) <= ha.y - hb.y &&\n\t\t\tMath.abs(dz) <= ha.z - hb.z;\n\t}\n\n\treturn true;\n}\n\n/**\n * Check whether a {@link Box3d} collides with a planar shape.\n *\n * The planar shape is treated as **unbounded along Z** — an infinitely\n * extruded prism of its own outline — so the pair reduces to the ordinary 2D\n * test between the box's XY footprint and that outline, and the box's z can\n * never make it miss. See {@link Box3d} for why that is the compatible\n * reading: it is what keeps an existing 2D game's world shapes colliding\n * unchanged the moment one `Box3d` body is introduced.\n *\n * `overlapZ` is left at `0` by construction, since neither participant has a\n * finite depth to resolve against.\n * @ignore\n */\nexport function testBox3dPolygon(a, boxA, b, polyB, response) {\n\treturn testPolygonPolygon(a, boxA._footprint, b, polyB, response);\n}\n\n/**\n * Planar-shape-first form of {@link testBox3dPolygon}.\n * @ignore\n */\nexport function testPolygonBox3d(a, polyA, b, boxB, response) {\n\treturn testPolygonPolygon(a, polyA, b, boxB._footprint, response);\n}\n\n/**\n * {@link Box3d} against an {@link Ellipse}, with the ellipse unbounded along\n * Z. See {@link testBox3dPolygon}.\n * @ignore\n */\nexport function testBox3dEllipse(a, boxA, b, ellipseB, response) {\n\treturn testPolygonEllipse(a, boxA._footprint, b, ellipseB, response);\n}\n\n/**\n * {@link Ellipse} against a {@link Box3d}, with the ellipse unbounded along\n * Z. See {@link testBox3dPolygon}.\n * @ignore\n */\nexport function testEllipseBox3d(a, ellipseA, b, boxB, response) {\n\treturn testEllipsePolygon(a, ellipseA, b, boxB._footprint, response);\n}\n","import { Bounds } from \"../bounds.ts\";\nimport ResponseObject from \"../response.js\";\nimport { raycastQuery } from \"./raycast.ts\";\nimport {\n\ttestEllipseEllipse,\n\ttestEllipsePolygon,\n\ttestPolygonEllipse,\n\ttestPolygonPolygon,\n} from \"./sat.js\";\nimport {\n\ttestBox3dBox3d,\n\ttestBox3dEllipse,\n\ttestBox3dPolygon,\n\ttestEllipseBox3d,\n\ttestPolygonBox3d,\n} from \"./sat3d.js\";\n\n// pre-built lookup table for SAT collision tests to avoid string concatenation\n// Rect and RoundRect extend Polygon, so they reuse the Polygon SAT tests\nconst SAT_LOOKUP = {\n\tPolygonPolygon: testPolygonPolygon,\n\tPolygonEllipse: testPolygonEllipse,\n\tEllipsePolygon: testEllipsePolygon,\n\tEllipseEllipse: testEllipseEllipse,\n\tRoundRectRoundRect: testPolygonPolygon,\n\tRoundRectPolygon: testPolygonPolygon,\n\tPolygonRoundRect: testPolygonPolygon,\n\tRoundRectEllipse: testPolygonEllipse,\n\tEllipseRoundRect: testEllipsePolygon,\n\tRectangleRectangle: testPolygonPolygon,\n\tRectanglePolygon: testPolygonPolygon,\n\tPolygonRectangle: testPolygonPolygon,\n\tRectangleEllipse: testPolygonEllipse,\n\tEllipseRectangle: testEllipsePolygon,\n\tRectangleRoundRect: testPolygonPolygon,\n\tRoundRectRectangle: testPolygonPolygon,\n\t// Box3d is the only shape with a depth extent, so Box3d-vs-Box3d is the\n\t// only pair resolved by the 3D narrowphase. Every mixed pair degrades to\n\t// the 2D test against the box's XY footprint, treating the planar shape\n\t// as unbounded along Z — see `sat3d.js` / `Box3d` for why. EVERY mixed\n\t// combination has to be listed: this table is looked up by string concat\n\t// and a missing entry is a hard crash, not a missed collision.\n\tBox3dBox3d: testBox3dBox3d,\n\tBox3dPolygon: testBox3dPolygon,\n\tPolygonBox3d: testPolygonBox3d,\n\tBox3dRectangle: testBox3dPolygon,\n\tRectangleBox3d: testPolygonBox3d,\n\tBox3dRoundRect: testBox3dPolygon,\n\tRoundRectBox3d: testPolygonBox3d,\n\tBox3dEllipse: testBox3dEllipse,\n\tEllipseBox3d: testEllipseBox3d,\n};\n\n/**\n * Shape-type pairs already reported as unsupported, so a mismatched pair\n * warns once instead of once per frame per pair.\n * @ignore\n */\nconst reportedMissingPairs = new Set();\n\n/**\n * @import Entity from \"../../renderable/entity/entity.js\";\n * @import Container from \"../../renderable/container.js\";\n * @import Renderable from \"../../renderable/renderable.js\";\n * @import Sprite from \"../../renderable/sprite.js\";\n * @import NineSliceSprite from \"../../renderable/nineslicesprite.js\";\n * @import {Line} from \"../../geometries/line.ts\";\n */\n\n// some cache bounds object used for collision detection\nconst boundsA = new Bounds();\nconst boundsB = new Bounds();\n\n/**\n * the Detector class contains methods for detecting collisions between bodies using a broadphase algorithm.\n */\nclass Detector {\n\t/**\n\t * @param {Container} world - the physic world this detector is bind to\n\t */\n\tconstructor(world) {\n\t\t// @ignore\n\t\tthis.world = world;\n\n\t\t/**\n\t\t * the default response object used for collisions\n\t\t * (will be automatically populated by the collides functions)\n\t\t * @type {ResponseObject}\n\t\t */\n\t\tthis.response = new ResponseObject();\n\n\t\t/**\n\t\t * Pairs (key → [renderableA, renderableB]) that were colliding in\n\t\t * the previous step. Diffed against `_frameSeen` at end of step\n\t\t * to fire `onCollisionEnd` for pairs that just separated.\n\t\t * @ignore\n\t\t */\n\t\tthis._activePairs = new Map();\n\t\t/**\n\t\t * Pairs seen during the current step. Built up as the per-object\n\t\t * `collisions()` calls run; consumed by `endFrame()`.\n\t\t * @ignore\n\t\t */\n\t\tthis._frameSeen = new Map();\n\t\t/**\n\t\t * Two-slot pool of \"symmetric view\" objects passed to the new\n\t\t * collision lifecycle handlers (`onCollisionStart` /\n\t\t * `onCollisionActive` / `onCollisionEnd`). Each view carries the\n\t\t * receiver-symmetric form: `a` = receiver, `b` = partner,\n\t\t * `normal` = MTV of receiver, `depth` = penetration scalar,\n\t\t * plus the SAT-legacy `overlapN` / `overlapV` / `overlap` fields\n\t\t * flipped to match the same convention.\n\t\t *\n\t\t * Two slots are needed because both views may be live at the\n\t\t * same time (one handler can dispatch into the other through\n\t\t * world mutation). Slot 0 is used for the original-a-side\n\t\t * dispatch, slot 1 for the original-b-side.\n\t\t * @ignore\n\t\t */\n\t\tthis._symViews = [\n\t\t\t{\n\t\t\t\ta: null,\n\t\t\t\tb: null,\n\t\t\t\toverlap: 0,\n\t\t\t\toverlapN: { x: 0, y: 0 },\n\t\t\t\toverlapV: { x: 0, y: 0 },\n\t\t\t\tnormal: { x: 0, y: 0 },\n\t\t\t\tdepth: 0,\n\t\t\t\t// Z half of the same three vectors, as scalars — see\n\t\t\t\t// `ResponseObject.overlapNZ`. Always 0 unless both shapes\n\t\t\t\t// are a Box3d and the contact resolved along Z.\n\t\t\t\toverlapNZ: 0,\n\t\t\t\toverlapZ: 0,\n\t\t\t\tnormalZ: 0,\n\t\t\t\t// which shape of `a` / `b` produced this contact (#1590), and\n\t\t\t\t// whether the contact exists only through trigger shapes.\n\t\t\t\t// Swapped alongside `a`/`b` so a receiver always reads its OWN\n\t\t\t\t// shape as `indexShapeA`.\n\t\t\t\tindexShapeA: -1,\n\t\t\t\tindexShapeB: -1,\n\t\t\t\tisTriggerContact: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\ta: null,\n\t\t\t\tb: null,\n\t\t\t\toverlap: 0,\n\t\t\t\toverlapN: { x: 0, y: 0 },\n\t\t\t\toverlapV: { x: 0, y: 0 },\n\t\t\t\tnormal: { x: 0, y: 0 },\n\t\t\t\tdepth: 0,\n\t\t\t\toverlapNZ: 0,\n\t\t\t\toverlapZ: 0,\n\t\t\t\tnormalZ: 0,\n\t\t\t\tindexShapeA: -1,\n\t\t\t\tindexShapeB: -1,\n\t\t\t\tisTriggerContact: false,\n\t\t\t},\n\t\t];\n\n\t\t/**\n\t\t * Shape-pair contacts that were overlapping in the previous step (#1596).\n\t\t * Keyed by `_shapePairKey`, diffed against `_frameShapeSeen` in\n\t\t * `endFrame()` to fire `onShapeCollisionEnd`. Populated only when someone\n\t\t * subscribes, so a game that does not use the feature keeps these empty.\n\t\t * @ignore\n\t\t */\n\t\tthis._activeShapePairs = new Map();\n\t\t/** @ignore */\n\t\tthis._frameShapeSeen = new Map();\n\n\t\t/**\n\t\t * Two-slot pool of receiver-symmetric shape-contact views, mirroring\n\t\t * `_symViews`: `a` is the receiver, `b` the partner, and `shapeA` /\n\t\t * `indexShapeA` are always the RECEIVER's shape. Two slots because both\n\t\t * sides may be live at once when a handler mutates the world.\n\t\t * @ignore\n\t\t */\n\t\tthis._shapeViews = [\n\t\t\t{\n\t\t\t\ta: null,\n\t\t\t\tb: null,\n\t\t\t\tshapeA: null,\n\t\t\t\tshapeB: null,\n\t\t\t\toverlap: 0,\n\t\t\t\toverlapN: { x: 0, y: 0 },\n\t\t\t\toverlapV: { x: 0, y: 0 },\n\t\t\t\tnormal: { x: 0, y: 0 },\n\t\t\t\tdepth: 0,\n\t\t\t\toverlapNZ: 0,\n\t\t\t\toverlapZ: 0,\n\t\t\t\tnormalZ: 0,\n\t\t\t\tindexShapeA: -1,\n\t\t\t\tindexShapeB: -1,\n\t\t\t\tisTrigger: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\ta: null,\n\t\t\t\tb: null,\n\t\t\t\tshapeA: null,\n\t\t\t\tshapeB: null,\n\t\t\t\toverlap: 0,\n\t\t\t\toverlapN: { x: 0, y: 0 },\n\t\t\t\toverlapV: { x: 0, y: 0 },\n\t\t\t\tnormal: { x: 0, y: 0 },\n\t\t\t\tdepth: 0,\n\t\t\t\toverlapNZ: 0,\n\t\t\t\toverlapZ: 0,\n\t\t\t\tnormalZ: 0,\n\t\t\t\tindexShapeA: -1,\n\t\t\t\tindexShapeB: -1,\n\t\t\t\tisTrigger: false,\n\t\t\t},\n\t\t];\n\n\t\t/**\n\t\t * The pair currently being enumerated, so the contact callback can be a\n\t\t * single pre-bound function rather than a closure allocated per body pair\n\t\t * per frame.\n\t\t * @ignore\n\t\t */\n\t\tthis._contactObjA = null;\n\t\tthis._contactObjB = null;\n\t\t/** @ignore */\n\t\tthis._onShapeContact = (shapeA, indexA, shapeB, indexB, isTrigger, res) => {\n\t\t\tthis._dispatchShapeContact(\n\t\t\t\tshapeA,\n\t\t\t\tindexA,\n\t\t\t\tshapeB,\n\t\t\t\tindexB,\n\t\t\t\tisTrigger,\n\t\t\t\tres,\n\t\t\t);\n\t\t};\n\t}\n\n\t/**\n\t * Populate one of the pooled symmetric views from a SAT response.\n\t * `flip=false` builds the view for `response.a`'s side (legacy\n\t * fields kept as-is, `normal = -overlapN` = MTV of original a).\n\t * `flip=true` builds it for `response.b`'s side: `a` / `b` swap,\n\t * `overlapN` / `overlapV` negated, `normal = +overlapN` (the MTV\n\t * of original b).\n\t * @ignore\n\t */\n\t_fillSymView(slot, satResponse, flip) {\n\t\tconst view = this._symViews[slot];\n\t\tconst oN = satResponse.overlapN;\n\t\tconst oV = satResponse.overlapV;\n\t\tconst oNZ = satResponse.overlapNZ;\n\t\tconst oZ = satResponse.overlapZ;\n\t\tif (flip) {\n\t\t\tview.a = satResponse.b;\n\t\t\tview.b = satResponse.a;\n\t\t\tview.overlapN.x = -oN.x;\n\t\t\tview.overlapN.y = -oN.y;\n\t\t\tview.overlapV.x = -oV.x;\n\t\t\tview.overlapV.y = -oV.y;\n\t\t\tview.overlapNZ = -oNZ;\n\t\t\tview.overlapZ = -oZ;\n\t\t\t// MTV of original b = +overlapN (b moves along \"from a to b\" to escape)\n\t\t\tview.normal.x = oN.x;\n\t\t\tview.normal.y = oN.y;\n\t\t\tview.normalZ = oNZ;\n\t\t\t// the receiver is original `b`, so ITS shape is `indexShapeA`\n\t\t\tview.indexShapeA = satResponse.indexShapeB;\n\t\t\tview.indexShapeB = satResponse.indexShapeA;\n\t\t} else {\n\t\t\tview.a = satResponse.a;\n\t\t\tview.b = satResponse.b;\n\t\t\tview.overlapN.x = oN.x;\n\t\t\tview.overlapN.y = oN.y;\n\t\t\tview.overlapV.x = oV.x;\n\t\t\tview.overlapV.y = oV.y;\n\t\t\tview.overlapNZ = oNZ;\n\t\t\tview.overlapZ = oZ;\n\t\t\t// MTV of original a = -overlapN (a moves opposite of \"from a to b\" to escape)\n\t\t\tview.normal.x = -oN.x;\n\t\t\tview.normal.y = -oN.y;\n\t\t\tview.normalZ = -oNZ;\n\t\t\tview.indexShapeA = satResponse.indexShapeA;\n\t\t\tview.indexShapeB = satResponse.indexShapeB;\n\t\t}\n\t\tview.overlap = satResponse.overlap;\n\t\tview.depth = satResponse.overlap;\n\t\tview.isTriggerContact = satResponse.isTriggerContact === true;\n\t\treturn view;\n\t}\n\n\t/**\n\t * Called by the adapter at the start of a physics step. Resets the\n\t * \"seen this frame\" set so the end-of-step diff can fire\n\t * `onCollisionEnd` for pairs that no longer overlap.\n\t * @ignore\n\t */\n\tbeginFrame() {\n\t\tthis._frameSeen.clear();\n\t\tthis._frameShapeSeen.clear();\n\t}\n\n\t/**\n\t * Called by the adapter at the end of a physics step. Diffs the\n\t * \"seen this frame\" set against the previous-frame active pairs:\n\t *   - pairs in active but not seen → fire onCollisionEnd\n\t *   - swap active ← seen for the next step's diff\n\t * @ignore\n\t */\n\tendFrame() {\n\t\tfor (const [key, pair] of this._activePairs) {\n\t\t\tif (this._frameSeen.has(key)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst [a, b] = pair;\n\t\t\t// Dispatch onCollisionEnd to whichever partner is still in the\n\t\t\t// scene tree. If both have been detached (level teardown), skip\n\t\t\t// entirely. Previously this short-circuited when *either* was\n\t\t\t// null/undefined, which left the survivor with an unbalanced\n\t\t\t// onCollisionStart / no End and they'd never learn the partner\n\t\t\t// left. `ancestor` is set to `undefined` on removeChild, so use\n\t\t\t// loose `!= null` to cover both null and undefined.\n\t\t\tconst aAttached = a.ancestor != null;\n\t\t\tconst bAttached = b.ancestor != null;\n\t\t\tif (!aAttached && !bAttached) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (aAttached && typeof a.onCollisionEnd === \"function\") {\n\t\t\t\ta.onCollisionEnd(undefined, b);\n\t\t\t}\n\t\t\tif (bAttached && typeof b.onCollisionEnd === \"function\") {\n\t\t\t\tb.onCollisionEnd(undefined, a);\n\t\t\t}\n\t\t}\n\t\t// rotate buffers: seen becomes the new active set\n\t\t// shape-pair contacts that separated this step (#1596). Mirrors the\n\t\t// per-renderable diff above, including the detached-object rule: a\n\t\t// contact whose objects have both left the world is dropped silently\n\t\t// rather than dispatched into torn-down handlers.\n\t\tfor (const [key, entry] of this._activeShapePairs) {\n\t\t\tif (this._frameShapeSeen.has(key)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst [a, b, shapeA, shapeB] = entry;\n\t\t\tconst aAttached = a.ancestor != null;\n\t\t\tconst bAttached = b.ancestor != null;\n\t\t\tif (!aAttached && !bAttached) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// No geometry: the shapes have separated, so there is nothing\n\t\t\t// truthful to measure. The view carries identity only, which is\n\t\t\t// why `onCollisionEnd` passes `undefined` for its response too.\n\t\t\tif (aAttached && typeof a.onShapeCollisionEnd === \"function\") {\n\t\t\t\ta.onShapeCollisionEnd(this._fillEndedView(0, a, b, shapeA, shapeB), b);\n\t\t\t}\n\t\t\tif (bAttached && typeof b.onShapeCollisionEnd === \"function\") {\n\t\t\t\tb.onShapeCollisionEnd(this._fillEndedView(1, b, a, shapeB, shapeA), a);\n\t\t\t}\n\t\t}\n\n\t\tconst prev = this._activePairs;\n\t\tthis._activePairs = this._frameSeen;\n\t\tthis._frameSeen = prev;\n\t\tconst prevShapes = this._activeShapePairs;\n\t\tthis._activeShapePairs = this._frameShapeSeen;\n\t\tthis._frameShapeSeen = prevShapes;\n\t\tthis._frameShapeSeen.clear();\n\t}\n\n\t/**\n\t * Identity-only shape-contact view for `onShapeCollisionEnd`: the shapes\n\t * have separated, so every geometric field is zeroed rather than left\n\t * holding the last overlap, which would read as a live contact.\n\t * @ignore\n\t */\n\t_fillEndedView(slot, receiver, partner, ownShape, otherShape) {\n\t\tconst view = this._shapeViews[slot];\n\t\tview.a = receiver;\n\t\tview.b = partner;\n\t\tview.shapeA = ownShape;\n\t\tview.shapeB = otherShape;\n\t\tview.indexShapeA = receiver.body?.shapes.indexOf(ownShape) ?? -1;\n\t\tview.indexShapeB = partner.body?.shapes.indexOf(otherShape) ?? -1;\n\t\tview.isTrigger =\n\t\t\townShape?.isTrigger === true || otherShape?.isTrigger === true;\n\t\tview.overlap = 0;\n\t\tview.depth = 0;\n\t\tview.overlapN.x = 0;\n\t\tview.overlapN.y = 0;\n\t\tview.overlapV.x = 0;\n\t\tview.overlapV.y = 0;\n\t\tview.normal.x = 0;\n\t\tview.normal.y = 0;\n\t\tview.overlapNZ = 0;\n\t\tview.overlapZ = 0;\n\t\tview.normalZ = 0;\n\t\treturn view;\n\t}\n\n\t/**\n\t * Build a stable order-independent key for a pair of renderables,\n\t * using their GUID. Returns undefined if either lacks a GUID (defensive\n\t * — detached or pool-recycled objects mid-step).\n\t * @ignore\n\t */\n\t_pairKey(a, b) {\n\t\tconst ga = a.GUID;\n\t\tconst gb = b.GUID;\n\t\tif (ga === undefined || gb === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn ga < gb ? `${ga}|${gb}` : `${gb}|${ga}`;\n\t}\n\n\t/**\n\t * Re-run the narrowphase for a remembered shape pair and repopulate\n\t * `response` from it.\n\t *\n\t * The scan clears `response` on every test, so whichever pair won has to be\n\t * measured again before handlers and the solver read it. Indices are looked\n\t * up here rather than carried from the scan because an enumeration handler\n\t * may have mutated either body in between: `removeShape()` re-indexes, and\n\t * `destroy()` empties the array outright. A shape that is no longer on its\n\t * body has no contact left to resolve, so this reports failure and the\n\t * caller falls through instead of throwing.\n\t * @ignore\n\t * @returns {boolean} true when the pair was re-measured\n\t */\n\t_retest(bodyA, bodyB, shapeA, shapeB, response) {\n\t\tconst indexA = bodyA.shapes.indexOf(shapeA);\n\t\tconst indexB = bodyB.shapes.indexOf(shapeB);\n\t\tif (indexA < 0 || indexB < 0) {\n\t\t\treturn false;\n\t\t}\n\t\tconst test = SAT_LOOKUP[shapeA.type + shapeB.type];\n\t\tif (test === undefined) {\n\t\t\treturn false;\n\t\t}\n\t\ttest.call(\n\t\t\tthis,\n\t\t\tbodyA.ancestor,\n\t\t\tshapeA,\n\t\t\tbodyB.ancestor,\n\t\t\tshapeB,\n\t\t\tresponse.clear(),\n\t\t);\n\t\tresponse.indexShapeA = indexA;\n\t\tresponse.indexShapeB = indexB;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Stable, order-independent key for one SHAPE pair (#1596).\n\t *\n\t * The renderable GUIDs order the pair, exactly as `_pairKey` does, and the\n\t * two shape ids MUST swap alongside them: keying `guidA|guidB|idA|idB`\n\t * without that swap gives the same physical contact two different keys\n\t * depending on which object the outer loop visited first, and the contact\n\t * would then End and Start every step.\n\t *\n\t * Shape identity is `_contactId`, stamped once in `Body#addShape` and never\n\t * derived from array position, because `removeShape()` re-indexes every\n\t * surviving shape.\n\t * @ignore\n\t */\n\t_shapePairKey(a, b, shapeA, shapeB) {\n\t\tconst ga = a.GUID;\n\t\tconst gb = b.GUID;\n\t\tconst sa = shapeA?._contactId;\n\t\tconst sb = shapeB?._contactId;\n\t\tif (\n\t\t\tga === undefined ||\n\t\t\tgb === undefined ||\n\t\t\tsa === undefined ||\n\t\t\tsb === undefined\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\t// Order the two (renderable, shape) TUPLES, not the GUIDs alone. Sorting\n\t\t// on GUID and swapping the shape ids behind it is only canonical while\n\t\t// the GUIDs differ; if two ever tie, the same physical contact gets two\n\t\t// different keys depending on which object the outer loop visited\n\t\t// first, and the contact would End and Start on every step.\n\t\tconst left = `${ga}#${sa}`;\n\t\tconst right = `${gb}#${sb}`;\n\t\treturn left < right ? `${left}|${right}` : `${right}|${left}`;\n\t}\n\n\t/**\n\t * True when an object subscribes to any shape-contact event.\n\t *\n\t * This is the opt-in gate: `collides()` only runs the full M x N scan when\n\t * this returns true for one of the two objects, so an application that\n\t * never declares these handlers performs exactly the narrowphase work it\n\t * did before the feature existed.\n\t * @ignore\n\t */\n\t_wantsShapeContacts(obj) {\n\t\treturn (\n\t\t\ttypeof obj.onShapeCollisionStart === \"function\" ||\n\t\t\ttypeof obj.onShapeCollisionActive === \"function\" ||\n\t\t\ttypeof obj.onShapeCollisionEnd === \"function\"\n\t\t);\n\t}\n\n\t/**\n\t * Populate a pooled shape-contact view, same flip convention as\n\t * `_fillSymView`: `flip=false` builds the view for `response.a`'s side,\n\t * `flip=true` for `response.b`'s, so `shapeA` is always the receiver's.\n\t * @ignore\n\t */\n\t_fillShapeView(slot, satResponse, flip, shapeA, shapeB, isTrigger) {\n\t\tconst view = this._shapeViews[slot];\n\t\tconst oN = satResponse.overlapN;\n\t\tconst oV = satResponse.overlapV;\n\t\tconst oNZ = satResponse.overlapNZ;\n\t\tconst oZ = satResponse.overlapZ;\n\t\tview.overlap = satResponse.overlap;\n\t\tview.depth = satResponse.overlap;\n\t\tview.isTrigger = isTrigger === true;\n\t\tif (flip) {\n\t\t\tview.a = satResponse.b;\n\t\t\tview.b = satResponse.a;\n\t\t\tview.shapeA = shapeB;\n\t\t\tview.shapeB = shapeA;\n\t\t\tview.indexShapeA = satResponse.indexShapeB;\n\t\t\tview.indexShapeB = satResponse.indexShapeA;\n\t\t\tview.overlapN.x = -oN.x;\n\t\t\tview.overlapN.y = -oN.y;\n\t\t\tview.overlapV.x = -oV.x;\n\t\t\tview.overlapV.y = -oV.y;\n\t\t\tview.overlapNZ = -oNZ;\n\t\t\tview.overlapZ = -oZ;\n\t\t\tview.normal.x = oN.x;\n\t\t\tview.normal.y = oN.y;\n\t\t\tview.normalZ = oNZ;\n\t\t} else {\n\t\t\tview.a = satResponse.a;\n\t\t\tview.b = satResponse.b;\n\t\t\tview.shapeA = shapeA;\n\t\t\tview.shapeB = shapeB;\n\t\t\tview.indexShapeA = satResponse.indexShapeA;\n\t\t\tview.indexShapeB = satResponse.indexShapeB;\n\t\t\tview.overlapN.x = oN.x;\n\t\t\tview.overlapN.y = oN.y;\n\t\t\tview.overlapV.x = oV.x;\n\t\t\tview.overlapV.y = oV.y;\n\t\t\tview.overlapNZ = oNZ;\n\t\t\tview.overlapZ = oZ;\n\t\t\tview.normal.x = -oN.x;\n\t\t\tview.normal.y = -oN.y;\n\t\t\tview.normalZ = -oNZ;\n\t\t}\n\t\treturn view;\n\t}\n\n\t/**\n\t * Fire Start / Active for one overlapping shape pair. Called back from\n\t * `collides()` once per pair, in scan order.\n\t * @ignore\n\t */\n\t_dispatchShapeContact(shapeA, shapeB, isTrigger, response) {\n\t\tconst objA = this._contactObjA;\n\t\tconst objB = this._contactObjB;\n\t\tconst key = this._shapePairKey(objA, objB, shapeA, shapeB);\n\t\tif (key === undefined || this._frameShapeSeen.has(key)) {\n\t\t\t// a dynamic-dynamic pair is visited twice per step (once per outer\n\t\t\t// loop object); the second visit must not re-fire\n\t\t\treturn;\n\t\t}\n\t\tthis._frameShapeSeen.set(key, [objA, objB, shapeA, shapeB]);\n\t\tconst isEntry = !this._activeShapePairs.has(key);\n\t\tconst viewA = this._fillShapeView(\n\t\t\t0,\n\t\t\tresponse,\n\t\t\tfalse,\n\t\t\tshapeA,\n\t\t\tshapeB,\n\t\t\tisTrigger,\n\t\t);\n\t\tconst viewB = this._fillShapeView(\n\t\t\t1,\n\t\t\tresponse,\n\t\t\ttrue,\n\t\t\tshapeA,\n\t\t\tshapeB,\n\t\t\tisTrigger,\n\t\t);\n\t\tif (isEntry) {\n\t\t\tif (typeof objA.onShapeCollisionStart === \"function\") {\n\t\t\t\tobjA.onShapeCollisionStart(viewA, objB);\n\t\t\t}\n\t\t\tif (typeof objB.onShapeCollisionStart === \"function\") {\n\t\t\t\tobjB.onShapeCollisionStart(viewB, objA);\n\t\t\t}\n\t\t}\n\t\tif (typeof objA.onShapeCollisionActive === \"function\") {\n\t\t\tobjA.onShapeCollisionActive(viewA, objB);\n\t\t}\n\t\tif (typeof objB.onShapeCollisionActive === \"function\") {\n\t\t\tobjB.onShapeCollisionActive(viewB, objA);\n\t\t}\n\t}\n\n\t/**\n\t * Drop the references the enumeration callback reads.\n\t *\n\t * They are plain fields rather than a per-pair closure so the hot path\n\t * allocates nothing, which means they outlive the pair unless cleared, and\n\t * would hold two renderables (and everything they reach) against garbage\n\t * collection until the next subscribed pair happened to overwrite them.\n\t * @ignore\n\t */\n\t_clearContactPair() {\n\t\tthis._contactObjA = null;\n\t\tthis._contactObjB = null;\n\t}\n\n\t/**\n\t * determine if two objects should collide (based on both respective objects body collision mask and type).<br>\n\t * you can redefine this function if you need any specific rules over what should collide with what.\n\t * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} a - a reference to the object A.\n\t * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} b - a reference to the object B.\n\t * @returns {boolean} true if they should collide, false otherwise\n\t */\n\tshouldCollide(a, b) {\n\t\tconst bodyA = a.body;\n\t\tconst bodyB = b.body;\n\t\treturn (\n\t\t\ttypeof bodyA === \"object\" &&\n\t\t\ttypeof bodyB === \"object\" &&\n\t\t\ta !== b &&\n\t\t\ta.isKinematic !== true &&\n\t\t\tb.isKinematic !== true &&\n\t\t\tbodyA.shapes.length > 0 &&\n\t\t\tbodyB.shapes.length > 0 &&\n\t\t\t!(bodyA.isStatic === true && bodyB.isStatic === true) &&\n\t\t\t(bodyA.collisionMask & bodyB.collisionType) !== 0 &&\n\t\t\t(bodyA.collisionType & bodyB.collisionMask) !== 0\n\t\t);\n\t}\n\n\t/**\n\t * detect collision between two bodies.\n\t * @param {Body} bodyA - a reference to body A.\n\t * @param {Body} bodyB - a reference to body B.\n\t * @returns {boolean} true if colliding\n\t */\n\tcollides(bodyA, bodyB, response = this.response, onContact = undefined) {\n\t\t// Shape-contact ENUMERATION is opt-in (#1596). With no `onContact` this\n\t\t// stays undefined, the loop below early-returns on the first solid pair,\n\t\t// and the number of SAT tests is exactly what it was before the feature\n\t\t// existed. Only a caller that actually subscribes pays for the full\n\t\t// M x N scan.\n\t\tconst enumerate = onContact !== undefined;\n\t\t// first solid pair seen. Without enumeration the loop returns there and\n\t\t// this is unused; with it, the scan continues and this remembers which\n\t\t// pair physical resolution gets, so the CHOSEN contact is identical\n\t\t// either way.\n\t\t//\n\t\t// Shape REFERENCES, not indices: on the enumerate path user handlers run\n\t\t// inside this scan, and a handler is free to call `removeShape()` (a\n\t\t// \"drop my hurtbox when it is hit\" reaction is entirely reasonable),\n\t\t// which splices and re-indexes the array. An index captured mid-scan\n\t\t// then names a different shape, or none at all, and the re-run below\n\t\t// would throw on `undefined.type` and take the whole world update with\n\t\t// it. Indices are recovered from the live arrays at re-run time.\n\t\tlet solidShapeA = null;\n\t\tlet solidShapeB = null;\n\t\t// first trigger-only contact seen, used only when NO solid pair overlaps\n\t\tlet triggerShapeA = null;\n\t\tlet triggerShapeB = null;\n\t\t// for each shape in body A\n\t\tfor (\n\t\t\tlet indexA = bodyA.shapes.length, shapeA;\n\t\t\tindexA--, (shapeA = bodyA.shapes[indexA]);\n\t\t) {\n\t\t\t// for each shape in body B\n\t\t\tfor (\n\t\t\t\tlet indexB = bodyB.shapes.length, shapeB;\n\t\t\t\tindexB--, (shapeB = bodyB.shapes[indexB]);\n\t\t\t) {\n\t\t\t\t// Per-shape gate (#1590), before any geometry work. A body's\n\t\t\t\t// `collisionType`/`collisionMask` still decide whether the pair\n\t\t\t\t// reaches this loop at all; these refine it per shape, so a\n\t\t\t\t// shape can narrow what its body allows but never widen it.\n\t\t\t\t//\n\t\t\t\t// `isActive === false` removes a shape from the simulation\n\t\t\t\t// entirely — no test, no contact, no events — without the cost\n\t\t\t\t// of removing and re-adding it.\n\t\t\t\tif (shapeA.isActive === false || shapeB.isActive === false) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// `??` and not `||`: 0 is a legitimate collision type, so an\n\t\t\t\t// unset field must fall through to the body while a deliberate\n\t\t\t\t// zero must not.\n\t\t\t\tconst typeA = shapeA.collisionType ?? bodyA.collisionType;\n\t\t\t\tconst maskA = shapeA.collisionMask ?? bodyA.collisionMask;\n\t\t\t\tconst typeB = shapeB.collisionType ?? bodyB.collisionType;\n\t\t\t\tconst maskB = shapeB.collisionMask ?? bodyB.collisionMask;\n\t\t\t\tif ((maskA & typeB) === 0 || (typeA & maskB) === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Resolve the narrowphase for this shape pair. An unlisted\n\t\t\t\t// combination used to index straight into `.call(...)` and\n\t\t\t\t// throw a TypeError mid-step, taking the whole world update\n\t\t\t\t// with it — reachable today with any user-defined shape type,\n\t\t\t\t// and newly reachable via `Box3d`. Warn once per pair and\n\t\t\t\t// treat it as \"no collision\" instead: a missed contact is\n\t\t\t\t// recoverable, a thrown physics step is not.\n\t\t\t\tconst test = SAT_LOOKUP[shapeA.type + shapeB.type];\n\t\t\t\tif (test === undefined) {\n\t\t\t\t\tconst pair = `${shapeA.type} / ${shapeB.type}`;\n\t\t\t\t\tif (!reportedMissingPairs.has(pair)) {\n\t\t\t\t\t\treportedMissingPairs.add(pair);\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`melonJS: no collision test for shape pair ${pair}; treating as no collision`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// full SAT collision check\n\t\t\t\tif (\n\t\t\t\t\ttest.call(\n\t\t\t\t\t\tthis,\n\t\t\t\t\t\tbodyA.ancestor, // a reference to the object A\n\t\t\t\t\t\tshapeA,\n\t\t\t\t\t\tbodyB.ancestor, // a reference to the object B\n\t\t\t\t\t\tshapeB,\n\t\t\t\t\t\t// clear response object before reusing\n\t\t\t\t\t\tresponse.clear(),\n\t\t\t\t\t) === true\n\t\t\t\t) {\n\t\t\t\t\t// set the shape index\n\t\t\t\t\t// A TRIGGER pair must not end the search. `collides` reports\n\t\t\t\t\t// one contact per body pair, and push-out is decided from\n\t\t\t\t\t// it — so returning here would let a trigger shape suppress\n\t\t\t\t\t// a solid sibling's push-out, and which one won would come\n\t\t\t\t\t// down to `shapes` array order. Remember the first trigger\n\t\t\t\t\t// contact and keep scanning for a solid pair, which wins if\n\t\t\t\t\t// one exists (#1590).\n\t\t\t\t\tconst pairIsTrigger =\n\t\t\t\t\t\tshapeA.isTrigger === true || shapeB.isTrigger === true;\n\t\t\t\t\tif (enumerate === true) {\n\t\t\t\t\t\t// Every overlapping pair is reported, solid and trigger\n\t\t\t\t\t\t// alike, in the order the scan finds them. `response`\n\t\t\t\t\t\t// currently holds THIS pair's SAT result, which is what\n\t\t\t\t\t\t// the contact is describing; it is reused on the next\n\t\t\t\t\t\t// iteration, so handlers must not retain it.\n\t\t\t\t\t\tresponse.indexShapeA = indexA;\n\t\t\t\t\t\tresponse.indexShapeB = indexB;\n\t\t\t\t\t\tresponse.isTriggerContact = pairIsTrigger;\n\t\t\t\t\t\tonContact(shapeA, shapeB, pairIsTrigger, response);\n\t\t\t\t\t}\n\t\t\t\t\tif (pairIsTrigger) {\n\t\t\t\t\t\tif (triggerShapeA === null) {\n\t\t\t\t\t\t\ttriggerShapeA = shapeA;\n\t\t\t\t\t\t\ttriggerShapeB = shapeB;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (enumerate === true) {\n\t\t\t\t\t\t// keep scanning so the remaining pairs are enumerated;\n\t\t\t\t\t\t// the first solid still wins resolution, as below\n\t\t\t\t\t\tif (solidShapeA === null) {\n\t\t\t\t\t\t\tsolidShapeA = shapeA;\n\t\t\t\t\t\t\tsolidShapeB = shapeB;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tresponse.indexShapeA = indexA;\n\t\t\t\t\tresponse.indexShapeB = indexB;\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\tsolidShapeA !== null &&\n\t\t\tthis._retest(bodyA, bodyB, solidShapeA, solidShapeB, response)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (triggerShapeA !== null) {\n\t\t\t// No solid pair overlaps, so the contact is real but non-solid.\n\t\t\t// Re-run the remembered pair to repopulate the response: the loop\n\t\t\t// above cleared it on every subsequent test, and the handlers still\n\t\t\t// need a truthful overlap to read.\n\t\t\tif (this._retest(bodyA, bodyB, triggerShapeA, triggerShapeB, response)) {\n\t\t\t\t// consumed at the push-out sites — the contact reports normally\n\t\t\t\t// and simply contributes no position correction\n\t\t\t\tresponse.isTriggerContact = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * find all the collisions for the specified object using a broadphase algorithm\n\t * @ignore\n\t * @param {Renderable|Container|Entity|Sprite|NineSliceSprite} objA - object to be tested for collision\n\t * @returns {boolean} in case of collision, false otherwise\n\t */\n\tcollisions(objA) {\n\t\tlet collisionCounter = 0;\n\t\t// retreive a list of potential colliding objects from the game world\n\t\tconst candidates = this.world.broadphase.retrieve(objA);\n\n\t\tboundsA.addBounds(objA.getBounds(), true);\n\t\tboundsA.addBounds(objA.body.getBounds());\n\n\t\tfor (let i = 0, len = candidates.length; i < len; i++) {\n\t\t\tconst objB = candidates[i];\n\t\t\t// check if both objects \"should\" collide\n\t\t\tif (this.shouldCollide(objA, objB)) {\n\t\t\t\tboundsB.addBounds(objB.getBounds(), true);\n\t\t\t\tboundsB.addBounds(objB.body.getBounds());\n\n\t\t\t\t// fast AABB check if both bounding boxes are overlaping\n\t\t\t\tif (boundsA.overlaps(boundsB)) {\n\t\t\t\t\t// Opt-in shape-contact enumeration (#1596). Resolved once\n\t\t\t\t\t// per candidate pair, and when neither side subscribes the\n\t\t\t\t\t// callback stays undefined so `collides()` takes exactly the\n\t\t\t\t\t// path it took before this feature existed.\n\t\t\t\t\tconst wantsContacts =\n\t\t\t\t\t\tthis._wantsShapeContacts(objA) || this._wantsShapeContacts(objB);\n\t\t\t\t\tlet onContact;\n\t\t\t\t\tif (wantsContacts === true) {\n\t\t\t\t\t\tthis._contactObjA = objA;\n\t\t\t\t\t\tthis._contactObjB = objB;\n\t\t\t\t\t\tonContact = this._onShapeContact;\n\t\t\t\t\t}\n\t\t\t\t\tconst didCollide = this.collides(\n\t\t\t\t\t\tobjA.body,\n\t\t\t\t\t\tobjB.body,\n\t\t\t\t\t\tthis.response,\n\t\t\t\t\t\tonContact,\n\t\t\t\t\t);\n\t\t\t\t\tif (wantsContacts === true) {\n\t\t\t\t\t\tthis._clearContactPair();\n\t\t\t\t\t}\n\t\t\t\t\tif (didCollide) {\n\t\t\t\t\t\t// we touched something !\n\t\t\t\t\t\tcollisionCounter++;\n\n\t\t\t\t\t\t// Frame-diff bookkeeping for the modern lifecycle\n\t\t\t\t\t\t// handlers (`onCollisionStart` / `onCollisionActive` /\n\t\t\t\t\t\t// `onCollisionEnd`). Each pair is dispatched at most\n\t\t\t\t\t\t// once per frame to these handlers (regardless of\n\t\t\t\t\t\t// the SAT detector visiting it twice across the two\n\t\t\t\t\t\t// outer iterations — once with objA as outer, once\n\t\t\t\t\t\t// with objB as outer). `_frameSeen` is the per-frame\n\t\t\t\t\t\t// dedup; `_activePairs` carries pair state across\n\t\t\t\t\t\t// frames so we can fire onCollisionStart on entry and\n\t\t\t\t\t\t// onCollisionEnd on separation.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// IMPORTANT: the new handlers receive a\n\t\t\t\t\t\t// receiver-symmetric view (response.a === this,\n\t\t\t\t\t\t// response.b === other, response.normal is the MTV\n\t\t\t\t\t\t// of `this`). The legacy `onCollision` dispatch\n\t\t\t\t\t\t// below uses the unmodified SAT response (fixed\n\t\t\t\t\t\t// a/b, fixed sign) for 19.4 backward compatibility.\n\t\t\t\t\t\tconst pairKey = this._pairKey(objA, objB);\n\t\t\t\t\t\tconst firstVisitThisFrame =\n\t\t\t\t\t\t\tpairKey !== undefined && !this._frameSeen.has(pairKey);\n\t\t\t\t\t\tif (firstVisitThisFrame) {\n\t\t\t\t\t\t\tthis._frameSeen.set(pairKey, [objA, objB]);\n\t\t\t\t\t\t\tconst isEntry = !this._activePairs.has(pairKey);\n\t\t\t\t\t\t\tconst viewA = this._fillSymView(0, this.response, false);\n\t\t\t\t\t\t\tconst viewB = this._fillSymView(1, this.response, true);\n\t\t\t\t\t\t\tif (isEntry) {\n\t\t\t\t\t\t\t\tif (typeof objA.onCollisionStart === \"function\") {\n\t\t\t\t\t\t\t\t\tobjA.onCollisionStart(viewA, objB);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (typeof objB.onCollisionStart === \"function\") {\n\t\t\t\t\t\t\t\t\tobjB.onCollisionStart(viewB, objA);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (typeof objA.onCollisionActive === \"function\") {\n\t\t\t\t\t\t\t\tobjA.onCollisionActive(viewA, objB);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (typeof objB.onCollisionActive === \"function\") {\n\t\t\t\t\t\t\t\tobjB.onCollisionActive(viewB, objA);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Legacy `onCollision` dispatch + SAT push-out.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Two contracts coexist on this branch, gated by\n\t\t\t\t\t\t// whether the renderable defines an `onCollision`\n\t\t\t\t\t\t// handler at all:\n\t\t\t\t\t\t//\n\t\t\t\t\t\t//  - **Legacy gate (19.4 contract)** — if\n\t\t\t\t\t\t//    `onCollision` IS defined, push-out is gated on\n\t\t\t\t\t\t//    its return value. `return false` opts out;\n\t\t\t\t\t\t//    anything else (true / undefined / no return)\n\t\t\t\t\t\t//    applies push-out. `onCollision` itself fires\n\t\t\t\t\t\t//    twice per frame for dynamic-dynamic pairs (one\n\t\t\t\t\t\t//    per outer-loop visit), receives the unmodified\n\t\t\t\t\t\t//    SAT response. Bit-for-bit compatible with\n\t\t\t\t\t\t//    pre-19.5 behavior.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t//  - **Modern default (matter-aligned)** — if\n\t\t\t\t\t\t//    `onCollision` is NOT defined, push-out happens\n\t\t\t\t\t\t//    by default for dynamic non-sensor bodies. This\n\t\t\t\t\t\t//    matches matter-js's \"the solver always\n\t\t\t\t\t\t//    resolves contacts unless you flag the body as\n\t\t\t\t\t\t//    a sensor\" model and removes the migration\n\t\t\t\t\t\t//    footgun for users who drop `onCollision` in\n\t\t\t\t\t\t//    favor of `onCollisionActive`.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Sensor (`body.isSensor === true`) and static\n\t\t\t\t\t\t// (`body.isStatic === true`) bodies skip push-out\n\t\t\t\t\t\t// in both contracts, matching matter.\n\t\t\t\t\t\t// A trigger shape collides and reports normally; only the\n\t\t\t\t\t\t// position correction is skipped. `isTriggerContact` is\n\t\t\t\t\t\t// set by `collides` and means \"this contact exists ONLY\n\t\t\t\t\t\t// through trigger shapes\" — it is false whenever any\n\t\t\t\t\t\t// solid pair overlaps, so a trigger can never suppress\n\t\t\t\t\t\t// a solid sibling's push-out.\n\t\t\t\t\t\tconst eitherSensor =\n\t\t\t\t\t\t\tobjA.body.isSensor === true ||\n\t\t\t\t\t\t\tobjB.body.isSensor === true ||\n\t\t\t\t\t\t\tthis.response.isTriggerContact === true;\n\t\t\t\t\t\t// \"supersedes\" rule: if a renderable defines the\n\t\t\t\t\t\t// modern `onCollisionActive`, suppress its legacy\n\t\t\t\t\t\t// `onCollision` dispatch entirely. They are the\n\t\t\t\t\t\t// same every-frame contact handler in two API\n\t\t\t\t\t\t// styles; firing both would invoke two handlers\n\t\t\t\t\t\t// for one overlap with different response shapes.\n\t\t\t\t\t\t// Per-side so A and B can migrate independently.\n\t\t\t\t\t\tconst aHasModern = typeof objA.onCollisionActive === \"function\";\n\t\t\t\t\t\tconst bHasModern = typeof objB.onCollisionActive === \"function\";\n\t\t\t\t\t\tconst aOptsOut =\n\t\t\t\t\t\t\t!aHasModern &&\n\t\t\t\t\t\t\ttypeof objA.onCollision === \"function\" &&\n\t\t\t\t\t\t\tobjA.onCollision(this.response, objB) === false;\n\t\t\t\t\t\tif (!aOptsOut && objA.body.isStatic === false && !eitherSensor) {\n\t\t\t\t\t\t\tobjA.body.respondToCollision.call(objA.body, this.response);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst bOptsOut =\n\t\t\t\t\t\t\t!bHasModern &&\n\t\t\t\t\t\t\ttypeof objB.onCollision === \"function\" &&\n\t\t\t\t\t\t\tobjB.onCollision(this.response, objA) === false;\n\t\t\t\t\t\tif (!bOptsOut && objB.body.isStatic === false && !eitherSensor) {\n\t\t\t\t\t\t\tobjB.body.respondToCollision.call(objB.body, this.response);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// for multi-shape bodies (e.g. polylines), resolve remaining\n\t\t\t\t\t\t// overlaps at segment junctions.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// `!eitherSensor` matters as much here as it does above: this\n\t\t\t\t\t\t// loop writes positions DIRECTLY (`ancestor.pos.set(...)`)\n\t\t\t\t\t\t// rather than going through `respondToCollision`, and it used\n\t\t\t\t\t\t// to gate only on `isStatic`. A sensor with a single shape was\n\t\t\t\t\t\t// therefore held in place correctly, and the same sensor with\n\t\t\t\t\t\t// a second shape was pushed out anyway — the flag silently\n\t\t\t\t\t\t// stopped working the moment a body became compound.\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!eitherSensor &&\n\t\t\t\t\t\t\t(objA.body.shapes.length > 1 || objB.body.shapes.length > 1)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlet extraPasses = 3;\n\t\t\t\t\t\t\twhile (extraPasses-- > 0 && this.collides(objA.body, objB.body)) {\n\t\t\t\t\t\t\t\t// Defence in depth. The `!eitherSensor` gate on this\n\t\t\t\t\t\t\t\t// loop already covers the common case, since it is\n\t\t\t\t\t\t\t\t// computed from the first reported pair. But\n\t\t\t\t\t\t\t\t// `collides` runs again each iteration and may report\n\t\t\t\t\t\t\t\t// a DIFFERENT pair — one involving a trigger — after\n\t\t\t\t\t\t\t\t// an earlier pass moved things. Cheap to re-check,\n\t\t\t\t\t\t\t\t// and the alternative is a trigger being repositioned\n\t\t\t\t\t\t\t\t// by a later pass having been correctly skipped by\n\t\t\t\t\t\t\t\t// the first.\n\t\t\t\t\t\t\t\tconst passShapeA = objA.body.shapes[this.response.indexShapeA];\n\t\t\t\t\t\t\t\tconst passShapeB = objB.body.shapes[this.response.indexShapeB];\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tpassShapeA?.isTrigger === true ||\n\t\t\t\t\t\t\t\t\tpassShapeB?.isTrigger === true\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst overlap = this.response.overlapV;\n\t\t\t\t\t\t\t\tconst overlapN = this.response.overlapN;\n\t\t\t\t\t\t\t\t// Z half of the same two vectors. Both are 0 for\n\t\t\t\t\t\t\t\t// every planar shape pair, so the arithmetic below\n\t\t\t\t\t\t\t\t// is bit-for-bit inert for a 2D body — no branch\n\t\t\t\t\t\t\t\t// needed to keep the legacy path unchanged.\n\t\t\t\t\t\t\t\tconst overlapZ = this.response.overlapZ;\n\t\t\t\t\t\t\t\tconst overlapNZ = this.response.overlapNZ;\n\n\t\t\t\t\t\t\t\t// mass ratio for proportional response\n\t\t\t\t\t\t\t\tconst bothDynamic = !objA.body.isStatic && !objB.body.isStatic;\n\t\t\t\t\t\t\t\tconst totalMass = bothDynamic\n\t\t\t\t\t\t\t\t\t? objA.body.mass + objB.body.mass\n\t\t\t\t\t\t\t\t\t: 0;\n\t\t\t\t\t\t\t\tconst ratioA = bothDynamic\n\t\t\t\t\t\t\t\t\t? totalMass > 0\n\t\t\t\t\t\t\t\t\t\t? objB.body.mass / totalMass\n\t\t\t\t\t\t\t\t\t\t: 0.5\n\t\t\t\t\t\t\t\t\t: 1;\n\t\t\t\t\t\t\t\tconst ratioB = bothDynamic\n\t\t\t\t\t\t\t\t\t? totalMass > 0\n\t\t\t\t\t\t\t\t\t\t? objA.body.mass / totalMass\n\t\t\t\t\t\t\t\t\t\t: 0.5\n\t\t\t\t\t\t\t\t\t: 1;\n\n\t\t\t\t\t\t\t\t// correct position\n\t\t\t\t\t\t\t\tif (objA.body.isStatic === false) {\n\t\t\t\t\t\t\t\t\tobjA.body.ancestor.pos.set(\n\t\t\t\t\t\t\t\t\t\tobjA.body.ancestor.pos.x - overlap.x * ratioA,\n\t\t\t\t\t\t\t\t\t\tobjA.body.ancestor.pos.y - overlap.y * ratioA,\n\t\t\t\t\t\t\t\t\t\tobjA.body.ancestor.pos.z - overlapZ * ratioA,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t// cancel velocity into this surface (no bounce)\n\t\t\t\t\t\t\t\t\tconst projVel =\n\t\t\t\t\t\t\t\t\t\tobjA.body.vel.x * overlapN.x +\n\t\t\t\t\t\t\t\t\t\tobjA.body.vel.y * overlapN.y +\n\t\t\t\t\t\t\t\t\t\tobjA.body.velZ * overlapNZ;\n\t\t\t\t\t\t\t\t\tif (projVel > 0) {\n\t\t\t\t\t\t\t\t\t\tobjA.body.vel.x -= projVel * ratioA * overlapN.x;\n\t\t\t\t\t\t\t\t\t\tobjA.body.vel.y -= projVel * ratioA * overlapN.y;\n\t\t\t\t\t\t\t\t\t\tobjA.body.velZ -= projVel * ratioA * overlapNZ;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (objB.body.isStatic === false) {\n\t\t\t\t\t\t\t\t\tobjB.body.ancestor.pos.set(\n\t\t\t\t\t\t\t\t\t\tobjB.body.ancestor.pos.x + overlap.x * ratioB,\n\t\t\t\t\t\t\t\t\t\tobjB.body.ancestor.pos.y + overlap.y * ratioB,\n\t\t\t\t\t\t\t\t\t\tobjB.body.ancestor.pos.z + overlapZ * ratioB,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst projVel =\n\t\t\t\t\t\t\t\t\t\tobjB.body.vel.x * overlapN.x +\n\t\t\t\t\t\t\t\t\t\tobjB.body.vel.y * overlapN.y +\n\t\t\t\t\t\t\t\t\t\tobjB.body.velZ * overlapNZ;\n\t\t\t\t\t\t\t\t\tif (projVel > 0) {\n\t\t\t\t\t\t\t\t\t\tobjB.body.vel.x -= projVel * ratioB * overlapN.x;\n\t\t\t\t\t\t\t\t\t\tobjB.body.vel.y -= projVel * ratioB * overlapN.y;\n\t\t\t\t\t\t\t\t\t\tobjB.body.velZ -= projVel * ratioB * overlapNZ;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// update bounds after position changed\n\t\t\t\t\t\t\t\tboundsA.addBounds(objA.getBounds(), true);\n\t\t\t\t\t\t\t\tboundsA.addBounds(objA.body.getBounds());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// we could return the amount of objects we collided with ?\n\t\treturn collisionCounter > 0;\n\t}\n\n\t/**\n\t * Checks for object colliding with the given line\n\t * @ignore\n\t * @param {Line} line - line to be tested for collision\n\t * @param {Array.<Renderable>} [result] - a user defined array that will be populated with intersecting physic objects.\n\t * @returns {Array.<Renderable>} an array of intersecting physic objects\n\t * @example\n\t *    // define a line accross the viewport\n\t *    let ray = new Line(\n\t *        // absolute position of the line\n\t *        0, 0, [\n\t *        // starting point relative to the initial position\n\t *        new Vector2d(0, 0),\n\t *        // ending point\n\t *        new Vector2d(app.viewport.width, app.viewport.height)\n\t *    ]);\n\t *\n\t *    // check for collition\n\t *    result = me.collision.rayCast(ray);\n\t *\n\t *    if (result.length > 0) {\n\t *        // ...\n\t *    }\n\t */\n\trayCast(line, result = []) {\n\t\t// Thin wrapper over the shared `raycastQuery` (in `./raycast.ts`),\n\t\t// which is also used by `BuiltinAdapter.raycast` to expose the\n\t\t// portable adapter API. We drop the per-hit `{ point, normal,\n\t\t// fraction }` info and keep just the renderable array, preserving\n\t\t// the legacy `collision.rayCast` return shape. Note that hits are\n\t\t// now sorted nearest-first (was unspecified-order previously);\n\t\t// callers that depended on the unsorted order need to re-sort.\n\t\tconst fromX = line.pos.x + line.points[0].x;\n\t\tconst fromY = line.pos.y + line.points[0].y;\n\t\tconst toX = line.pos.x + line.points[1].x;\n\t\tconst toY = line.pos.y + line.points[1].y;\n\t\tconst hits = raycastQuery(this.world, fromX, fromY, toX, toY);\n\t\tfor (let i = 0; i < hits.length; i++) {\n\t\t\tresult[i] = hits[i].renderable;\n\t\t}\n\t\tresult.length = hits.length;\n\t\treturn result;\n\t}\n}\nexport default Detector;\n","import type { Rect } from \"../../geometries/rectangle.ts\";\nimport { Sphere } from \"../../geometries/sphere.ts\";\nimport { Vector2d } from \"../../math/vector2d.ts\";\nimport { Vector3d } from \"../../math/vector3d.ts\";\nimport type Renderable from \"../../renderable/renderable.js\";\nimport state from \"../../state/state.ts\";\nimport type {\n\tAdapterCapabilities,\n\tAdapterOptions,\n\tBodyDefinition,\n\tBodyShape,\n\tPhysicsAdapter,\n\tRaycastHit,\n\tRaycastHit3d,\n} from \"../adapter.ts\";\nimport type { Bounds } from \"../bounds.ts\";\nimport type Octree from \"../broadphase/octree.ts\";\nimport type World from \"../world.js\";\nimport Body from \"./body.js\";\nimport Detector from \"./detector.js\";\nimport { raycastQuery } from \"./raycast.ts\";\n\n/** world-space AABB scratch for `raycast3d`; never escapes the call */\nconst _rayBox = {\n\tminX: 0,\n\tminY: 0,\n\tminZ: 0,\n\tmaxX: 0,\n\tmaxY: 0,\n\tmaxZ: 0,\n};\n\n/** reusable hit record for `rayAABB3d`; never escapes `raycast3d` */\nconst _rayHit = { t: 0, nx: 0, ny: 0, nz: 0 };\n\n/**\n * Fill `out` with the world-space AABB of a renderable's {@link Box3d}\n * shapes, unioned. Returns `false` when the renderable has no body, or a\n * body with no `Box3d` in it — those keep the bounding-sphere path, so no\n * existing `raycast3d` result changes.\n *\n * A shape's world position follows the same convention the SAT narrowphase\n * uses: the renderable's absolute position plus the shape's local offset.\n * @param renderable - the candidate to measure\n * @param cx - the renderable's absolute x\n * @param cy - the renderable's absolute y\n * @param cz - the renderable's absolute z\n * @param out - scratch AABB to fill; only written when this returns `true`\n */\nfunction worldBox3d(\n\trenderable: Renderable,\n\tcx: number,\n\tcy: number,\n\tcz: number,\n\tout: typeof _rayBox,\n): boolean {\n\tconst body = (renderable as { body?: Body }).body;\n\tif (body === undefined || !body.hasDepth) {\n\t\treturn false;\n\t}\n\tlet found = false;\n\tconst shapes = body.shapes as unknown as {\n\t\ttype: string;\n\t\tisActive?: boolean;\n\t\tpos: { x: number; y: number; z: number };\n\t\thalfExtents: { x: number; y: number; z: number };\n\t}[];\n\tfor (let i = 0, len = shapes.length; i < len; i++) {\n\t\tconst shape = shapes[i];\n\t\tif (shape.type !== \"Box3d\") continue;\n\t\t// an inactive shape is out of collision, and raycast3d is a collision\n\t\t// query — skip it rather than let it contribute a depth extent (#1590)\n\t\tif (shape.isActive === false) continue;\n\t\tconst minX = cx + shape.pos.x - shape.halfExtents.x;\n\t\tconst minY = cy + shape.pos.y - shape.halfExtents.y;\n\t\tconst minZ = cz + shape.pos.z - shape.halfExtents.z;\n\t\tconst maxX = cx + shape.pos.x + shape.halfExtents.x;\n\t\tconst maxY = cy + shape.pos.y + shape.halfExtents.y;\n\t\tconst maxZ = cz + shape.pos.z + shape.halfExtents.z;\n\t\tif (!found) {\n\t\t\tfound = true;\n\t\t\tout.minX = minX;\n\t\t\tout.minY = minY;\n\t\t\tout.minZ = minZ;\n\t\t\tout.maxX = maxX;\n\t\t\tout.maxY = maxY;\n\t\t\tout.maxZ = maxZ;\n\t\t} else {\n\t\t\tif (minX < out.minX) out.minX = minX;\n\t\t\tif (minY < out.minY) out.minY = minY;\n\t\t\tif (minZ < out.minZ) out.minZ = minZ;\n\t\t\tif (maxX > out.maxX) out.maxX = maxX;\n\t\t\tif (maxY > out.maxY) out.maxY = maxY;\n\t\t\tif (maxZ > out.maxZ) out.maxZ = maxZ;\n\t\t}\n\t}\n\treturn found;\n}\n\n/**\n * Ray-vs-AABB via the slab method, for `t ∈ [0, 1]` along the segment\n * `from → from + d`. Returns the entry fraction and the face normal of the\n * slab that was entered last (which is the face actually hit), or `null` on\n * a miss.\n *\n * A ray starting inside the box reports `t = 0`, matching the\n * bounding-sphere path's \"origin inside → t = 0\" convention. Its normal is\n * taken from the axis whose entry plane is nearest behind the origin, which\n * keeps the value well-defined rather than zero.\n * @param from - segment start\n * @param dx - segment delta on x (`to.x - from.x`)\n * @param dy - segment delta on y\n * @param dz - segment delta on z\n * @param box - the world-space AABB to test against\n */\nfunction rayAABB3d(\n\tfrom: Vector3d,\n\tdx: number,\n\tdy: number,\n\tdz: number,\n\tbox: typeof _rayBox,\n): typeof _rayHit | null {\n\tlet tMin = Number.NEGATIVE_INFINITY;\n\tlet tMax = Number.POSITIVE_INFINITY;\n\t// axis that produced tMin, and the sign of the face entered\n\tlet axis = 0;\n\tlet sign = -1;\n\n\t// x slab\n\tif (dx === 0) {\n\t\tif (from.x < box.minX || from.x > box.maxX) return null;\n\t} else {\n\t\tconst inv = 1 / dx;\n\t\tlet t1 = (box.minX - from.x) * inv;\n\t\tlet t2 = (box.maxX - from.x) * inv;\n\t\tlet s = -1;\n\t\tif (t1 > t2) {\n\t\t\tconst tmp = t1;\n\t\t\tt1 = t2;\n\t\t\tt2 = tmp;\n\t\t\ts = 1;\n\t\t}\n\t\tif (t1 > tMin) {\n\t\t\ttMin = t1;\n\t\t\taxis = 0;\n\t\t\tsign = s;\n\t\t}\n\t\tif (t2 < tMax) tMax = t2;\n\t\tif (tMin > tMax) return null;\n\t}\n\n\t// y slab\n\tif (dy === 0) {\n\t\tif (from.y < box.minY || from.y > box.maxY) return null;\n\t} else {\n\t\tconst inv = 1 / dy;\n\t\tlet t1 = (box.minY - from.y) * inv;\n\t\tlet t2 = (box.maxY - from.y) * inv;\n\t\tlet s = -1;\n\t\tif (t1 > t2) {\n\t\t\tconst tmp = t1;\n\t\t\tt1 = t2;\n\t\t\tt2 = tmp;\n\t\t\ts = 1;\n\t\t}\n\t\tif (t1 > tMin) {\n\t\t\ttMin = t1;\n\t\t\taxis = 1;\n\t\t\tsign = s;\n\t\t}\n\t\tif (t2 < tMax) tMax = t2;\n\t\tif (tMin > tMax) return null;\n\t}\n\n\t// z slab\n\tif (dz === 0) {\n\t\tif (from.z < box.minZ || from.z > box.maxZ) return null;\n\t} else {\n\t\tconst inv = 1 / dz;\n\t\tlet t1 = (box.minZ - from.z) * inv;\n\t\tlet t2 = (box.maxZ - from.z) * inv;\n\t\tlet s = -1;\n\t\tif (t1 > t2) {\n\t\t\tconst tmp = t1;\n\t\t\tt1 = t2;\n\t\t\tt2 = tmp;\n\t\t\ts = 1;\n\t\t}\n\t\tif (t1 > tMin) {\n\t\t\ttMin = t1;\n\t\t\taxis = 2;\n\t\t\tsign = s;\n\t\t}\n\t\tif (t2 < tMax) tMax = t2;\n\t\tif (tMin > tMax) return null;\n\t}\n\n\t// entirely behind the origin, or entirely past the segment end\n\tif (tMax < 0 || tMin > 1) return null;\n\n\t_rayHit.t = tMin < 0 ? 0 : tMin;\n\t_rayHit.nx = axis === 0 ? sign : 0;\n\t_rayHit.ny = axis === 1 ? sign : 0;\n\t_rayHit.nz = axis === 2 ? sign : 0;\n\treturn _rayHit;\n}\n\n/**\n * Default {@link PhysicsAdapter} that wraps melonJS's native SAT-based\n * physics. Owns the active body set, the {@link Detector}, gravity, and\n * the simulation step. Returns the legacy {@link Body} class as its body\n * handle so existing property-based game code (`body.vel.x = 5`,\n * `body.isStatic = true`) keeps working unchanged.\n *\n * Instantiated by default during `Application` construction; user code\n * only touches this directly when explicitly wiring it via\n * `new Application(w, h, { physic: { adapter: new BuiltinAdapter() } })`.\n * @category Physics\n */\nexport default class BuiltinAdapter implements PhysicsAdapter {\n\t/**\n\t * Short adapter identifier exposed as `world.physic`. Lets user code\n\t * branch on the active physics implementation without importing the\n\t * concrete adapter class.\n\t */\n\treadonly physicLabel = \"builtin\";\n\n\t/**\n\t * Advertised capabilities; user code may branch on these.\n\t */\n\treadonly capabilities: AdapterCapabilities = {\n\t\tconstraints: false,\n\t\tcontinuousCollisionDetection: false,\n\t\tsleepingBodies: false,\n\t\traycasts: true,\n\t\traycasts3d: true,\n\t\tvelocityLimit: true,\n\t\tisGrounded: true,\n\t};\n\n\t/**\n\t * World gravity. Mutate to change at runtime.\n\t * @default <0, 0.98>\n\t */\n\tgravity: Vector2d;\n\n\t/**\n\t * Active physics bodies in this simulation.\n\t */\n\treadonly bodies: Set<Body> = new Set();\n\n\t/**\n\t * Collision detector instance, created in {@link init}.\n\t */\n\tdetector!: Detector;\n\n\t/**\n\t * Back-reference to the owning world, set in {@link init}.\n\t */\n\tprivate world!: World;\n\n\tconstructor(options: AdapterOptions = {}) {\n\t\tthis.gravity = options.gravity ?? new Vector2d(0, 0.98);\n\t}\n\n\tinit(world: World): void {\n\t\tthis.world = world;\n\t\tthis.detector = new Detector(world);\n\t}\n\n\tdestroy(): void {\n\t\tthis.bodies.clear();\n\t}\n\n\tstep(dt: number): void {\n\t\tconst isPaused = state.isPaused();\n\t\t// open the per-frame collision diff so the detector can synthesize\n\t\t// start/end events on top of SAT's frame-by-frame overlap reports\n\t\tthis.detector.beginFrame();\n\t\t// iterate through all bodies\n\t\tfor (const body of this.bodies) {\n\t\t\tconst ancestor = body.ancestor;\n\t\t\tif (!body.isStatic && ancestor) {\n\t\t\t\t// if the game is not paused, and ancestor can be updated\n\t\t\t\tif (\n\t\t\t\t\t!(isPaused && !ancestor.updateWhenPaused) &&\n\t\t\t\t\t(ancestor.inViewport || ancestor.alwaysUpdate)\n\t\t\t\t) {\n\t\t\t\t\tthis.applyGravity(body);\n\t\t\t\t\t// `Body.update` is documented `@protected` in body.js\n\t\t\t\t\t// JSDoc, but the adapter is its sole legitimate caller\n\t\t\t\t\t// (the adapter step IS what advances the body). The\n\t\t\t\t\t// JSDoc also documents a `dt` param the runtime ignores\n\t\t\t\t\t// (it uses `timer.tick` instead); we pass it through\n\t\t\t\t\t// anyway for future compatibility / legibility.\n\t\t\t\t\t// @ts-expect-error -- @protected method called from the adapter that owns the body lifecycle\n\t\t\t\t\tif (body.update(dt)) {\n\t\t\t\t\t\tancestor.isDirty = true;\n\t\t\t\t\t}\n\t\t\t\t\tthis.detector.collisions(ancestor);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Always clear the force accumulator each step — even for static\n\t\t\t// bodies, out-of-viewport bodies, and paused bodies. Otherwise a\n\t\t\t// stray applyForce call would leak indefinitely and fire as a\n\t\t\t// surprise impulse when the body becomes simulatable again.\n\t\t\t//\n\t\t\t// `forceZ` is part of that accumulator and MUST be cleared with it:\n\t\t\t// left out, a single frame of input keeps accelerating the body\n\t\t\t// along z forever, because callers set the force per-frame while a\n\t\t\t// key is held and rely on this reset to stop.\n\t\t\tbody.force.set(0, 0);\n\t\t\tbody.forceZ = 0;\n\t\t}\n\t\t// fire onCollisionEnd for pairs that separated this step\n\t\tthis.detector.endFrame();\n\t}\n\n\tsyncFromPhysics(): void {\n\t\t// No-op: BuiltinAdapter mutates `renderable.pos` directly during\n\t\t// `step` via `body.update(dt)`, so there is nothing to copy back\n\t\t// after the step.\n\t}\n\n\taddBody(renderable: Renderable, def: BodyDefinition): Body {\n\t\t// Returns the legacy `Body` class as the body handle; the field\n\t\t// is also written to `renderable.body` so property-based code\n\t\t// keeps working.\n\t\t//\n\t\t// Two entry points converge here:\n\t\t// 1. Explicit (preferred): `world.adapter.addBody(this, { type: \"dynamic\", shapes: [...] })`\n\t\t// 2. Legacy migration: `this.body = new Body(this, shapes)` already\n\t\t//    created a body but it wasn't registered with the adapter yet;\n\t\t//    we register the existing instance and map any supplied def\n\t\t//    fields onto it.\n\t\t//\n\t\t// Pick ONE registration path per body. Calling `addBody` twice on\n\t\t// the same renderable after it's already adapter-managed throws —\n\t\t// the second call is a programming error.\n\t\tlet body = renderable.body as Body | undefined;\n\t\tconst isAlreadyAdapterManaged =\n\t\t\tbody instanceof Body && this.bodies.has(body);\n\t\tif (isAlreadyAdapterManaged) {\n\t\t\tthrow new Error(\n\t\t\t\t\"BuiltinAdapter.addBody: renderable is already adapter-managed. \" +\n\t\t\t\t\t\"Use adapter.updateShape() / property mutation / adapter.removeBody() \" +\n\t\t\t\t\t\"first if you need to change the body.\",\n\t\t\t);\n\t\t}\n\t\tif (!(body instanceof Body)) {\n\t\t\tbody = new Body(renderable, def.shapes);\n\t\t\trenderable.body = body;\n\t\t} else if (def.shapes.length > 0) {\n\t\t\t// legacy-bridge path: replace existing shapes if the def\n\t\t\t// provides them (otherwise keep what the user already built).\n\t\t\t// `body.shapes` is typed in body.js as a union that includes a\n\t\t\t// scalar `Point` variant — never used at runtime, but TS sees\n\t\t\t// it so `.length` doesn't narrow. Cast to the array form.\n\t\t\t(body.shapes as BodyShape[]).length = 0;\n\t\t\tbody.getBounds().clear();\n\t\t\tfor (const s of def.shapes) {\n\t\t\t\tbody.addShape(s);\n\t\t\t}\n\t\t}\n\t\t// map portable BodyDefinition fields onto Body properties\n\t\tbody.setStatic(def.type === \"static\");\n\t\tif (typeof def.collisionType === \"number\") {\n\t\t\tbody.collisionType = def.collisionType;\n\t\t}\n\t\tif (typeof def.collisionMask === \"number\") {\n\t\t\tbody.collisionMask = def.collisionMask;\n\t\t}\n\t\tif (def.frictionAir !== undefined) {\n\t\t\tif (typeof def.frictionAir === \"number\") {\n\t\t\t\tbody.setFriction(def.frictionAir, def.frictionAir);\n\t\t\t} else {\n\t\t\t\tbody.setFriction(def.frictionAir.x, def.frictionAir.y);\n\t\t\t}\n\t\t}\n\t\tif (typeof def.restitution === \"number\") {\n\t\t\tbody.bounce = def.restitution;\n\t\t}\n\t\tif (typeof def.density === \"number\") {\n\t\t\tbody.mass = def.density;\n\t\t}\n\t\tif (typeof def.gravityScale === \"number\") {\n\t\t\tbody.gravityScale = def.gravityScale;\n\t\t}\n\t\tif (def.maxVelocity !== undefined) {\n\t\t\tbody.setMaxVelocity(def.maxVelocity.x, def.maxVelocity.y);\n\t\t}\n\t\tif (def.isSensor === true) {\n\t\t\tbody.isSensor = true;\n\t\t}\n\t\tthis.bodies.add(body);\n\t\treturn body;\n\t}\n\n\tremoveBody(renderable: Renderable): void {\n\t\tconst body = renderable.body;\n\t\tif (body instanceof Body) {\n\t\t\tthis.bodies.delete(body);\n\t\t}\n\t}\n\n\tupdateShape(renderable: Renderable, shapes: BodyShape[]): void {\n\t\tconst body = renderable.body;\n\t\tif (!(body instanceof Body)) {\n\t\t\treturn;\n\t\t}\n\t\t// clear existing shapes, then add new ones. `body.shapes` is\n\t\t// typed too widely (see `addBody`); cast to the array form.\n\t\t(body.shapes as BodyShape[]).length = 0;\n\t\tbody.getBounds().clear();\n\t\tfor (const s of shapes) {\n\t\t\tbody.addShape(s);\n\t\t}\n\t}\n\n\tgetVelocity(renderable: Renderable, out?: Vector2d): Vector2d {\n\t\tconst body = renderable.body as Body | undefined;\n\t\tconst target = out ?? new Vector2d();\n\t\t// after removeBody the body reference may still dangle on the\n\t\t// renderable — return zero so the behaviour matches adapters that\n\t\t// fully forget the body (e.g. MatterAdapter clears its bodyMap).\n\t\tif (!body || !this.bodies.has(body)) {\n\t\t\treturn target.set(0, 0);\n\t\t}\n\t\treturn target.set(body.vel.x, body.vel.y);\n\t}\n\n\tsetVelocity(renderable: Renderable, v: Vector2d): void {\n\t\t(renderable.body as Body).vel.setV(v);\n\t}\n\n\tapplyForce(renderable: Renderable, force: Vector2d, point?: Vector2d): void {\n\t\t// When `point` is provided and differs from the body's centroid,\n\t\t// `Body.applyForce`'s 4-arg form generates a torque from the lever\n\t\t// arm (`τ = r × F`). The 2-arg call (no point) preserves the\n\t\t// pre-angular behavior exactly: just accumulate into the linear\n\t\t// force vector.\n\t\tconst body = renderable.body as Body;\n\t\tif (point) {\n\t\t\tbody.applyForce(force.x, force.y, point.x, point.y);\n\t\t} else {\n\t\t\tbody.applyForce(force.x, force.y);\n\t\t}\n\t}\n\n\tapplyImpulse(renderable: Renderable, impulse: Vector2d): void {\n\t\t// impulse = mass * dv  →  dv = impulse / mass\n\t\tconst body = renderable.body as Body;\n\t\tconst invMass = body.mass > 0 ? 1 / body.mass : 0;\n\t\tbody.vel.x += impulse.x * invMass;\n\t\tbody.vel.y += impulse.y * invMass;\n\t}\n\n\tsetPosition(renderable: Renderable, p: Vector2d): void {\n\t\t// `pos` is an ObservableVector3d on Renderable; preserve the\n\t\t// existing Z (`pos.z` is the depth field, set independently).\n\t\trenderable.pos.x = p.x;\n\t\trenderable.pos.y = p.y;\n\t}\n\n\tsetAngle(renderable: Renderable, angle: number): void {\n\t\t// Under BuiltinAdapter the rotation is *visual only* — SAT\n\t\t// collisions remain axis-aligned against the original shapes —\n\t\t// but the renderable's `currentTransform` is updated so the\n\t\t// sprite rotates correctly.\n\t\t(renderable.body as Body).setAngle(angle);\n\t}\n\n\tgetAngle(renderable: Renderable): number {\n\t\treturn (renderable.body as Body).angle;\n\t}\n\n\tsetAngularVelocity(renderable: Renderable, omega: number): void {\n\t\t(renderable.body as Body).setAngularVelocity(omega);\n\t}\n\n\tgetAngularVelocity(renderable: Renderable): number {\n\t\treturn (renderable.body as Body).angularVelocity;\n\t}\n\n\tapplyTorque(renderable: Renderable, torque: number): void {\n\t\t(renderable.body as Body).applyTorque(torque);\n\t}\n\n\tsetStatic(renderable: Renderable, isStatic: boolean): void {\n\t\t(renderable.body as Body).setStatic(isStatic);\n\t}\n\n\tsetGravityScale(renderable: Renderable, scale: number): void {\n\t\t(renderable.body as Body).gravityScale = scale;\n\t}\n\n\tsetFrictionAir(\n\t\trenderable: Renderable,\n\t\tfriction: number | { x: number; y: number },\n\t): void {\n\t\tconst body = renderable.body as Body;\n\t\tif (typeof friction === \"number\") {\n\t\t\tbody.setFriction(friction, friction);\n\t\t} else {\n\t\t\tbody.setFriction(friction.x, friction.y);\n\t\t}\n\t}\n\n\tsetMaxVelocity(\n\t\trenderable: Renderable,\n\t\tlimit: { x: number; y: number },\n\t): void {\n\t\t(renderable.body as Body).setMaxVelocity(limit.x, limit.y);\n\t}\n\n\tgetMaxVelocity(renderable: Renderable): { x: number; y: number } {\n\t\tconst v = (renderable.body as Body).maxVel;\n\t\treturn { x: v.x, y: v.y };\n\t}\n\n\tsetCollisionType(renderable: Renderable, type: number): void {\n\t\t(renderable.body as Body).collisionType = type;\n\t}\n\n\tsetCollisionMask(renderable: Renderable, mask: number): void {\n\t\t(renderable.body as Body).setCollisionMask(mask);\n\t}\n\n\tsetSensor(renderable: Renderable, isSensor: boolean): void {\n\t\t// A sensor body still fires collision events (`onCollisionStart` /\n\t\t// `onCollisionActive` / `onCollisionEnd`) — the SAT detector skips\n\t\t// only the positional push-out (`respondToCollision`). Same\n\t\t// semantics as Matter's `isSensor`, so adapter-agnostic trigger /\n\t\t// one-way-platform code behaves identically on both adapters.\n\t\t(renderable.body as Body).isSensor = isSensor;\n\t}\n\n\tgetBodyAABB(renderable: Renderable, out: Bounds): Bounds | undefined {\n\t\t// Adapter-side debug surface: the body's AABB in renderable-local\n\t\t// coordinates. Builtin Body already stores its bounds in local\n\t\t// space, so we just copy into the caller's `out`. Gated on\n\t\t// `this.bodies.has(body)` (same predicate as `getVelocity`) so a\n\t\t// dangling Body reference left on `renderable.body` after\n\t\t// `removeBody` matches the adapter contract — returns\n\t\t// `undefined` for an unregistered body.\n\t\tconst body = renderable.body as Body | undefined;\n\t\tif (!body || !this.bodies.has(body)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst b = body.bounds;\n\t\tout.setMinMax(b.min.x, b.min.y, b.max.x, b.max.y);\n\t\treturn out;\n\t}\n\n\tgetBodyShapes(renderable: Renderable): readonly BodyShape[] {\n\t\t// Adapter-side debug surface: live `shapes` list in renderable-\n\t\t// local coordinates. Read-only — callers must not mutate.\n\t\t// Gated on `this.bodies.has(body)` to match the adapter contract:\n\t\t// an unregistered body (or no body) returns an empty array.\n\t\t// `body.shapes` is typed in body.js as a union that includes a\n\t\t// scalar `Point` variant (legacy, never produced at runtime).\n\t\tconst body = renderable.body as Body | undefined;\n\t\tif (!body || !this.bodies.has(body)) {\n\t\t\treturn [];\n\t\t}\n\t\treturn body.shapes as BodyShape[];\n\t}\n\n\tisGrounded(renderable: Renderable): boolean {\n\t\t// Derived from the SAT body's `falling` flag, updated by the\n\t\t// detector each frame: a body is grounded when it's not currently\n\t\t// moving downward off a surface.\n\t\tconst body = renderable.body as Body;\n\t\treturn !body.falling && !body.jumping;\n\t}\n\n\traycast(from: Vector2d, to: Vector2d): RaycastHit | null {\n\t\t// Goes through the same SAT-based broadphase walk as the legacy\n\t\t// `Detector.rayCast` (both share `raycastQuery` in `./raycast.ts`),\n\t\t// and returns the portable `RaycastHit` shape (`renderable`,\n\t\t// `point`, `normal`, `fraction`) for parity with the matter /\n\t\t// planck adapters. `point` is the precise parametric entry point\n\t\t// on the shape surface (line-segment vs polygon edges, quadratic\n\t\t// ray vs ellipse); `normal` is the outward-facing surface normal\n\t\t// at that entry; `fraction` is `0..1` along the ray.\n\t\tconst hits = raycastQuery(this.world, from.x, from.y, to.x, to.y);\n\t\treturn hits[0] ?? null;\n\t}\n\n\traycast3d(from: Vector3d, to: Vector3d): RaycastHit3d | null {\n\t\t// Available only when the broadphase is an Octree (i.e. the\n\t\t// active camera's sortOn is \"depth\"). Under a 2D sortOn the\n\t\t// world's broadphase is a QuadTree and there's no z partition\n\t\t// to walk, so we return null. User code can guard via\n\t\t// `world.adapter.capabilities.raycasts3d` AND\n\t\t// `world.sortOn === \"depth\"` to skip a no-op call.\n\t\tif (this.world.sortOn !== \"depth\") {\n\t\t\treturn null;\n\t\t}\n\t\tconst octree = this.world.broadphase as Octree;\n\n\t\tconst dx = to.x - from.x;\n\t\tconst dy = to.y - from.y;\n\t\tconst dz = to.z - from.z;\n\n\t\t// Broadphase walk: only visit octants the ray segment\n\t\t// (`from` → `to`, `t ∈ [0, 1]`) actually crosses, via slab\n\t\t// AABB tests at each node. Strictly tighter candidate set\n\t\t// than enclosing the segment in a single AABB — saves the\n\t\t// per-candidate narrow-phase cost on long rays whose\n\t\t// segment-AABB would cover most of the scene.\n\t\tconst candidates: Renderable[] = [];\n\t\toctree.queryRay(from.x, from.y, from.z, dx, dy, dz, 1, candidates);\n\n\t\tlet bestFraction = Number.POSITIVE_INFINITY;\n\t\tlet bestRenderable: Renderable | null = null;\n\t\tlet bestEntryT = 0;\n\n\t\tlet bestNx = 0;\n\t\tlet bestNy = 0;\n\t\tlet bestNz = 0;\n\n\t\tfor (let i = 0, len = candidates.length; i < len; i++) {\n\t\t\tconst r = candidates[i];\n\t\t\tif (typeof r.getAbsolutePosition !== \"function\") continue;\n\t\t\tconst center = r.getAbsolutePosition();\n\t\t\tconst cx = center.x;\n\t\t\tconst cy = center.y;\n\t\t\t// `getAbsolutePosition()` returns Vector3d; the engine's\n\t\t\t// stale .js typings expose Vector2d as the union member,\n\t\t\t// so `.z` may not be visible to TS even though it's\n\t\t\t// always present at runtime.\n\t\t\tconst cz = (center as { z?: number }).z ?? 0;\n\n\t\t\t// Exact ray-vs-AABB when this renderable carries a Box3d body,\n\t\t\t// which is the case that matters: probing floor height under a\n\t\t\t// character. Everything else keeps the bounding-sphere path\n\t\t\t// below, so no existing raycast3d result changes.\n\t\t\tif (worldBox3d(r, cx, cy, cz, _rayBox)) {\n\t\t\t\tconst hit = rayAABB3d(from, dx, dy, dz, _rayBox);\n\t\t\t\tif (hit === null) continue;\n\t\t\t\tif (hit.t < bestFraction) {\n\t\t\t\t\tbestFraction = hit.t;\n\t\t\t\t\tbestRenderable = r;\n\t\t\t\t\tbestEntryT = hit.t;\n\t\t\t\t\tbestNx = hit.nx;\n\t\t\t\t\tbestNy = hit.ny;\n\t\t\t\t\tbestNz = hit.nz;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// bounding-sphere radius = bounds half-diagonal (matches\n\t\t\t// `Camera3d.isVisible`'s circumradius convention).\n\t\t\tconst bounds = r.getBounds();\n\t\t\tconst w = bounds.width;\n\t\t\tconst h = bounds.height;\n\t\t\tconst radius = Math.sqrt(w * w + h * h) * 0.5;\n\t\t\t// Ray–sphere intersection in parametric form: solve\n\t\t\t//   |from + t·(to − from) − center|² = r²\n\t\t\t// for t ∈ [0, 1]. Take the smaller root (entry).\n\t\t\tconst ox = from.x - cx;\n\t\t\tconst oy = from.y - cy;\n\t\t\tconst oz = from.z - cz;\n\t\t\tconst a = dx * dx + dy * dy + dz * dz;\n\t\t\tif (a === 0) continue;\n\t\t\tconst b = 2 * (ox * dx + oy * dy + oz * dz);\n\t\t\tconst c = ox * ox + oy * oy + oz * oz - radius * radius;\n\t\t\tconst disc = b * b - 4 * a * c;\n\t\t\tif (disc < 0) continue;\n\t\t\tconst sqrtDisc = Math.sqrt(disc);\n\t\t\tconst t0 = (-b - sqrtDisc) / (2 * a);\n\t\t\tconst t1 = (-b + sqrtDisc) / (2 * a);\n\t\t\t// Hit table (t0 < t1 always when the ray crosses the\n\t\t\t// sphere):\n\t\t\t//   t0 ∈ [0, 1]            — segment enters in-range; t=t0\n\t\t\t//   t0 < 0 ≤ t1            — `from` is inside the sphere; t=0\n\t\t\t//   t0 < 0 and t1 < 0      — sphere behind `from`; miss\n\t\t\t//   t0 > 1                 — sphere past `to`; miss\n\t\t\t// The `t1 ≥ 0` guard on the inside-sphere branch is\n\t\t\t// independent of whether `t1` is ≤ 1, because a sphere\n\t\t\t// that fully encloses the segment (t0 < 0, t1 > 1)\n\t\t\t// still counts as a t=0 hit at the ray origin.\n\t\t\tlet t: number;\n\t\t\tif (t0 >= 0 && t0 <= 1) {\n\t\t\t\tt = t0;\n\t\t\t} else if (t0 < 0 && t1 >= 0) {\n\t\t\t\tt = 0;\n\t\t\t} else {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (t < bestFraction) {\n\t\t\t\tbestFraction = t;\n\t\t\t\tbestRenderable = r;\n\t\t\t\tbestEntryT = t;\n\t\t\t\t// sphere normal, resolved at the entry point\n\t\t\t\tconst px = from.x + dx * t;\n\t\t\t\tconst py = from.y + dy * t;\n\t\t\t\tconst pz = from.z + dz * t;\n\t\t\t\tconst ux = px - cx;\n\t\t\t\tconst uy = py - cy;\n\t\t\t\tconst uz = pz - cz;\n\t\t\t\tconst uLen = Math.sqrt(ux * ux + uy * uy + uz * uz) || 1;\n\t\t\t\tbestNx = ux / uLen;\n\t\t\t\tbestNy = uy / uLen;\n\t\t\t\tbestNz = uz / uLen;\n\t\t\t}\n\t\t}\n\n\t\tif (bestRenderable === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\trenderable: bestRenderable,\n\t\t\tpoint: new Vector3d(\n\t\t\t\tfrom.x + dx * bestEntryT,\n\t\t\t\tfrom.y + dy * bestEntryT,\n\t\t\t\tfrom.z + dz * bestEntryT,\n\t\t\t),\n\t\t\tnormal: new Vector3d(bestNx, bestNy, bestNz),\n\t\t\tfraction: bestFraction,\n\t\t};\n\t}\n\n\tquerySphere(center: Vector3d, radius: number): Renderable[];\n\tquerySphere(sphere: Sphere): Renderable[];\n\tquerySphere(\n\t\tcenterOrSphere: Vector3d | Sphere,\n\t\tradius?: number,\n\t): Renderable[] {\n\t\t// Active only under a 3D broadphase. Return [] for the 2D case\n\t\t// so user code can call this unconditionally without a\n\t\t// `sortOn === \"depth\"` guard sprinkled at every call site.\n\t\tconst result: Renderable[] = [];\n\t\tif (this.world.sortOn !== \"depth\") {\n\t\t\treturn result;\n\t\t}\n\t\t// Unpack the two call shapes. We resolve to the loose (cx,\n\t\t// cy, cz, r) form once, then run the single shared narrow\n\t\t// phase below — no duplication between the Vector3d and\n\t\t// Sphere paths.\n\t\tconst isSphere = centerOrSphere instanceof Sphere;\n\t\tconst cx = isSphere ? centerOrSphere.pos.x : centerOrSphere.x;\n\t\tconst cy = isSphere ? centerOrSphere.pos.y : centerOrSphere.y;\n\t\tconst cz = isSphere ? centerOrSphere.pos.z : centerOrSphere.z;\n\t\tconst r = isSphere ? centerOrSphere.radius : radius!;\n\t\tconst octree = this.world.broadphase as Octree;\n\t\t// Broadphase walk: octree returns candidates whose octant the\n\t\t// sphere overlaps. Then narrow-phase by actual centre distance\n\t\t// so callers don't have to repeat the test (matches how\n\t\t// `queryAABB` does the bounds-overlap filter at this level).\n\t\toctree.querySphere(cx, cy, cz, r, result);\n\t\tconst r2 = r * r;\n\t\tlet writeIdx = 0;\n\t\tfor (let i = 0, len = result.length; i < len; i++) {\n\t\t\tconst item = result[i];\n\t\t\tif (typeof item.getAbsolutePosition !== \"function\") continue;\n\t\t\tconst p = item.getAbsolutePosition();\n\t\t\tconst dx = p.x - cx;\n\t\t\tconst dy = p.y - cy;\n\t\t\t// `getAbsolutePosition()` returns Vector3d; legacy .js\n\t\t\t// typings expose `Vector2d | Vector3d` — `.z` is always\n\t\t\t// present at runtime under Camera3d.\n\t\t\tconst dz = ((p as { z?: number }).z ?? 0) - cz;\n\t\t\tif (dx * dx + dy * dy + dz * dz <= r2) {\n\t\t\t\tresult[writeIdx++] = item;\n\t\t\t}\n\t\t}\n\t\tresult.length = writeIdx;\n\t\treturn result;\n\t}\n\n\tqueryAABB(rect: Rect): Renderable[] {\n\t\t// Walks the SAT broadphase quadtree to get a candidate set then\n\t\t// filters by actual AABB overlap, so output is the precise\n\t\t// intersection — not just same-partition neighbours. Useful for\n\t\t// AoE damage queries, mouse picking, trigger-zone sweeps.\n\t\tconst queryBounds = rect.getBounds();\n\t\t// Pass our own array to `retrieve` so we never touch the\n\t\t// broadphase's shared scratch — user code may call this from\n\t\t// inside an `onCollisionStart` handler firing mid-iteration of\n\t\t// the SAT detector's scratch walk; sharing would clobber the\n\t\t// outer iteration. Then filter in place so the call costs a\n\t\t// single allocation, not two.\n\t\tconst result: Renderable[] = [];\n\t\tthis.world.broadphase.retrieve(rect, undefined, result);\n\t\tlet writeIdx = 0;\n\t\tfor (let i = 0, len = result.length; i < len; i++) {\n\t\t\tconst r = result[i];\n\t\t\tconst b = r.getBounds();\n\t\t\tif (b.overlaps(queryBounds)) {\n\t\t\t\tresult[writeIdx++] = r;\n\t\t\t}\n\t\t}\n\t\tresult.length = writeIdx;\n\t\treturn result;\n\t}\n\n\tapplyGravity(body: Body): void {\n\t\t// Backward-compat shim for the legacy `World.bodyApplyGravity`.\n\t\t// `ignoreGravity` is deprecated in favor of `gravityScale = 0`\n\t\t// (see Body#ignoreGravity JSDoc). Keep both checks until the\n\t\t// legacy field is removed — `gravityScale = 0` is the portable\n\t\t// path that also works under matter; `ignoreGravity = true` is\n\t\t// the legacy path that only this adapter reads.\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- legacy field is read intentionally for backward compatibility\n\t\tif (body.gravityScale !== 0 && !body.ignoreGravity) {\n\t\t\tbody.force.x += body.mass * this.gravity.x * body.gravityScale;\n\t\t\tbody.force.y += body.mass * this.gravity.y * body.gravityScale;\n\t\t}\n\t}\n}\n","import { hasRegisteredEvents } from \"../input/pointerevent.ts\";\nimport Container from \"../renderable/container.js\";\nimport {\n\temit,\n\tGAME_RESET,\n\tLEVEL_LOADED,\n\toff,\n\ton,\n\tWORLD_STEP,\n} from \"../system/event.ts\";\nimport { createBroadphase } from \"./broadphase/factory.ts\";\nimport BuiltinAdapter from \"./builtin/builtin-adapter.ts\";\n\n/**\n * @import Application from \"./../application/application.ts\";\n * @import Body from \"./builtin/body.js\";\n * @import Detector from \"./builtin/detector.js\";\n * @import { Vector2d } from \"../math/vector2d.ts\";\n * @import { PhysicsAdapter } from \"./adapter.ts\";\n */\n\n/**\n * Frozen empty Set returned by `world.bodies` when the active adapter\n * doesn't expose a native `bodies` Set (third-party adapters that own\n * their own body storage). Frozen so `world.bodies.add(x)` throws\n * `TypeError` instead of silently mutating a throwaway.\n * @ignore\n */\nconst EMPTY_BODIES = Object.freeze(new Set());\n\n/**\n * an object representing the physic world, and responsible for managing and updating all children and physics\n * @category Container\n */\nexport default class World extends Container {\n\t/**\n\t * @param {number} [x=0] - position of the container (accessible via the inherited pos.x property)\n\t * @param {number} [y=0] - position of the container (accessible via the inherited pos.y property)\n\t * @param {number} [width=Infinity] - width of the world container\n\t * @param {number} [height=Infinity] - height of the world container\n\t * @param {PhysicsAdapter} [adapter] - physics adapter to use; defaults to a new {@link BuiltinAdapter} instance\n\t */\n\tconstructor(\n\t\tx = 0,\n\t\ty = 0,\n\t\twidth = Infinity,\n\t\theight = Infinity,\n\t\tadapter = undefined,\n\t) {\n\t\t// call the super constructor\n\t\tsuper(x, y, width, height, true);\n\n\t\t// world is the root container\n\t\tthis.name = \"rootContainer\";\n\n\t\t// to mimic the previous behavior\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t/**\n\t\t * the application (game) this physic world belong to\n\t\t * @type {Application}\n\t\t */\n\t\tthis.app = undefined;\n\n\t\t/**\n\t\t * Identifier of the active physics adapter, taken from the\n\t\t * adapter's `physicLabel` field at `Application` construction —\n\t\t * `\"builtin\"` (default — `BuiltinAdapter`), `\"matter\"`\n\t\t * (`@melonjs/matter-adapter`), or a third-party label.\n\t\t * The reserved value `\"none\"` is set when physics is disabled via\n\t\t * `physic: \"none\"` in `ApplicationSettings`; `World.step` skips\n\t\t * the simulation entirely under that label, and the rest of the\n\t\t * world container behaves like a pure scene graph.\n\t\t *\n\t\t * User code can branch on the value without importing the\n\t\t * adapter class:\n\t\t *\n\t\t * ```ts\n\t\t * if (app.world.physic === \"matter\") {\n\t\t *     // matter-only setup (constraints, native queries, …)\n\t\t * }\n\t\t * ```\n\t\t * @see ApplicationSettings.physic\n\t\t * @see PhysicsAdapter.physicLabel\n\t\t * @type {string}\n\t\t * @default \"builtin\"\n\t\t * @example\n\t\t * // disable physics entirely\n\t\t * app.world.physic = \"none\";\n\t\t */\n\t\tthis.physic = \"builtin\";\n\n\t\t/**\n\t\t * the rate at which the game world is updated,\n\t\t * may be greater than or lower than the display fps\n\t\t * @default 60\n\t\t * @see timer.maxfps\n\t\t */\n\t\tthis.fps = 60;\n\n\t\t/**\n\t\t * Enabled pre-rendering for all tile layers. <br>\n\t\t * If false layers are rendered dynamically, if true layers are first fully rendered into an offscreen canvas.<br>\n\t\t * the \"best\" rendering method depends of your game (amount of layer, layer size, amount of tiles per layer, etc.)<br>\n\t\t * Note : rendering method is also configurable per layer by adding a boolean \"preRender\" property to your layer in Tiled ({@link https://doc.mapeditor.org/en/stable/manual/custom-properties/#adding-properties}).\n\t\t * @type {boolean}\n\t\t * @default false\n\t\t */\n\t\tthis.preRender = false;\n\n\t\t/**\n\t\t * Enable the WebGL2 procedural shader path for orthogonal tile\n\t\t * layers. When `true` (default), eligible layers render via a\n\t\t * single quad per tileset + a fragment shader doing per-pixel GID\n\t\t * lookup — bypassing the per-tile drawImage loop entirely.\n\t\t * Supported features on the shader path: animated tiles, all\n\t\t * three flip bits (H/V/AD), per-layer opacity/tint/blend mode,\n\t\t * and oversized bottom-aligned tiles up to 4 cells of overflow.\n\t\t * Layers that don't qualify (Canvas/WebGL1, non-orthogonal,\n\t\t * collection-of-image tilesets, non-zero `tileoffset`, or tile\n\t\t * overflow beyond the shader's 4-cell limit) fall back to the\n\t\t * legacy path automatically. Set to `false` to disable globally.\n\t\t * @type {boolean}\n\t\t * @default true\n\t\t */\n\t\tthis.gpuTilemap = true;\n\n\t\t/**\n\t\t * the physics adapter driving this world. Defaults to a\n\t\t * {@link BuiltinAdapter} wrapping the engine's native SAT-based\n\t\t * physics. Override at `Application` construction time via\n\t\t * `settings.physic.adapter`. Cannot be swapped at runtime.\n\t\t * @type {PhysicsAdapter}\n\t\t */\n\t\tthis.adapter = adapter ?? new BuiltinAdapter();\n\t\tthis.adapter.init?.(this);\n\n\t\t/**\n\t\t * Spatial broadphase used by built-in physics and pointer\n\t\t * event picking. The concrete class depends on\n\t\t * {@link Container#sortOn}: under `\"depth\"` (the value\n\t\t * `Camera3d.defaultSortOn` sets on stage reset) it's an\n\t\t * `Octree`; under any 2D sortOn it's a `QuadTree`. The choice\n\t\t * is reactive — the `sortOn` setter swaps the broadphase if a\n\t\t * 2D↔3D boundary crossing occurs, so stage transitions between\n\t\t * Camera2d and Camera3d stages are handled transparently.\n\t\t *\n\t\t * Implementation detail — game code shouldn't reach in here.\n\t\t * Use `world.adapter.queryAABB(rect)` /\n\t\t * `world.adapter.querySphere(sphere)` /\n\t\t * `world.adapter.raycast(...)` instead.\n\t\t * @type {import(\"./broadphase/broadphase.ts\").Broadphase}\n\t\t */\n\t\tthis.broadphase = createBroadphase(this);\n\n\t\t// reset the world container on the game reset signal\n\t\ton(GAME_RESET, this.reset, this);\n\n\t\t// reset the broadphase on level load. 2D path resizes the\n\t\t// QuadTree root to match the new world bounds; 3D path just\n\t\t// clears contents because the Octree root is a fixed\n\t\t// origin-centred box that doesn't depend on the level's 2D\n\t\t// extent.\n\t\t// Held as a field, not an inline arrow, so `destroy()` can pass it to\n\t\t// `off()` — an anonymous handler is unremovable, and its closure keeps\n\t\t// this world (and its whole child tree and broadphase) reachable from\n\t\t// the event bus for the life of the page.\n\t\tthis.onLevelLoaded = () => {\n\t\t\tif (this._sortOn === \"depth\") {\n\t\t\t\tthis.broadphase.clear();\n\t\t\t} else {\n\t\t\t\tthis.broadphase.clear(this.getBounds().clone());\n\t\t\t}\n\t\t};\n\t\ton(LEVEL_LOADED, this.onLevelLoaded);\n\t}\n\n\t/**\n\t * Release this world's global event subscriptions before handing off to\n\t * the container teardown. Without this a destroyed world stays on the\n\t * bus: it keeps resetting itself on `GAME_RESET` and clearing a\n\t * broadphase nobody reads on `LEVEL_LOADED`, and it cannot be garbage\n\t * collected because both handlers close over it.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\toff(GAME_RESET, this.reset, this);\n\t\toff(LEVEL_LOADED, this.onLevelLoaded);\n\t\tthis.onLevelLoaded = undefined;\n\t\tsuper.destroy(...arguments);\n\t}\n\n\t/**\n\t * Override the inherited `Container.sortOn` setter so the\n\t * broadphase swaps to match when crossing a 2D↔3D boundary. See\n\t * the `broadphase` JSDoc for why the swap is reactive rather than\n\t * Stage-driven. The 2D/3D classification key is `\"depth\"` — every\n\t * other sortOn value lives in the 2D regime.\n\t * @returns {\"x\"|\"y\"|\"z\"|\"depth\"}\n\t */\n\tget sortOn() {\n\t\treturn this._sortOn;\n\t}\n\t/**\n\t * @param {\"x\"|\"y\"|\"z\"|\"depth\"} value\n\t */\n\tset sortOn(value) {\n\t\tconst was3D = this._sortOn === \"depth\";\n\t\t// delegate to Container's setter for validation + comparator\n\t\tsuper.sortOn = value;\n\t\tconst now3D = this._sortOn === \"depth\";\n\t\tif (was3D !== now3D) {\n\t\t\tthis.broadphase = createBroadphase(this);\n\t\t}\n\t}\n\n\t/**\n\t * Active physics bodies in this simulation. Backed by the active\n\t * adapter; mutating this set directly is no longer the recommended\n\t * pattern — use `world.adapter.addBody(...)` / `removeBody(...)`.\n\t *\n\t * Adapters that don't expose a native `bodies` Set (e.g. third-party\n\t * integrations that own their own body storage) cause this getter\n\t * to return a frozen empty Set, so any `world.bodies.add(...)`\n\t * attempt throws `TypeError` instead of silently mutating a\n\t * throwaway.\n\t * @returns {Set<Body>}\n\t */\n\tget bodies() {\n\t\treturn (\n\t\t\t/** @type {{ bodies?: Set<Body> }} */ (this.adapter).bodies ??\n\t\t\tEMPTY_BODIES\n\t\t);\n\t}\n\n\t/**\n\t * world gravity. Mutate to change at runtime.\n\t * @returns {Vector2d}\n\t */\n\tget gravity() {\n\t\treturn this.adapter.gravity;\n\t}\n\tset gravity(v) {\n\t\tthis.adapter.gravity = v;\n\t}\n\n\t/**\n\t * the collision detector instance used by this world instance.\n\t * Available only when the active adapter is {@link BuiltinAdapter}.\n\t * @returns {Detector | undefined}\n\t */\n\tget detector() {\n\t\treturn /** @type {{ detector?: Detector }} */ (this.adapter).detector;\n\t}\n\n\t/**\n\t * reset the game world\n\t */\n\treset() {\n\t\t// clear the quadtree\n\t\tthis.broadphase.clear();\n\n\t\t// reset the anchorPoint\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\t// call the parent method\n\t\tsuper.reset();\n\n\t\t// save persistent child bodies (only meaningful for the builtin adapter)\n\t\tconst bodies = /** @type {{ bodies?: Set<Body> }} */ (this.adapter).bodies;\n\t\tif (bodies !== undefined) {\n\t\t\tconst persistentBodies = [];\n\t\t\tbodies.forEach((value) => {\n\t\t\t\tif (value.ancestor && value.ancestor.isPersistent) {\n\t\t\t\t\tpersistentBodies.push(value);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// empty the list of active physic bodies\n\t\t\t// Note: this should be empty already when calling the parent method\n\t\t\tbodies.clear();\n\n\t\t\t// insert persistent child bodies into the new state\n\t\t\tif (persistentBodies.length > 0) {\n\t\t\t\tpersistentBodies.forEach((body) => {\n\t\t\t\t\tthis.addBody(body);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Add a physic body to the game world. Legacy API for code that\n\t * constructed `new Body(...)` directly and now wants to register it\n\t * with the active physics adapter.\n\t * @see Container.addChild\n\t * @param {Body} body\n\t * @returns {World} this game world\n\t */\n\taddBody(body) {\n\t\tif (this.physic === \"builtin\") {\n\t\t\tconst bodies = /** @type {{ bodies?: Set<Body> }} */ (this.adapter)\n\t\t\t\t.bodies;\n\t\t\tbodies?.add(body);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a physic body from the game world\n\t * @see Container.removeChild\n\t * @param {Body} body\n\t * @returns {World} this game world\n\t */\n\tremoveBody(body) {\n\t\tif (this.physic === \"builtin\") {\n\t\t\tconst bodies = /** @type {{ bodies?: Set<Body> }} */ (this.adapter)\n\t\t\t\t.bodies;\n\t\t\tbodies?.delete(body);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply gravity to the given body. Backward-compat shim; the actual\n\t * simulation runs through the active adapter.\n\t * @private\n\t * @param {Body} body\n\t */\n\tbodyApplyGravity(body) {\n\t\t/** @type {{ applyGravity?: (b: Body) => void }} */ (\n\t\t\tthis.adapter\n\t\t).applyGravity?.(body);\n\t}\n\n\t/**\n\t * update the game world\n\t * @param {number} dt - the time passed since the last frame update\n\t * @returns {boolean} true if the world is dirty\n\t */\n\tupdate(dt) {\n\t\t// only update the quadtree if necessary\n\t\tif (this.physic === \"builtin\" || hasRegisteredEvents() === true) {\n\t\t\t// clear the quadtree\n\t\t\tthis.broadphase.clear();\n\t\t\t// insert the world container (children) into the quadtree\n\t\t\tthis.broadphase.insertContainer(this);\n\t\t}\n\n\t\t// advance the active adapter's simulation\n\t\tthis.step(dt);\n\n\t\t// call the super constructor\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * update the physics simulation by one step (called by the game world update method)\n\t * @param {number} dt - the time passed since the last frame update\n\t */\n\tstep(dt) {\n\t\t// `physic` is the active adapter's identifier (\"builtin\", \"matter\",\n\t\t// etc.) or the sentinel \"none\" when physics is disabled. The step\n\t\t// runs under any adapter; only \"none\" skips it.\n\t\tif (this.physic !== \"none\") {\n\t\t\tthis.adapter.step(dt);\n\t\t\tthis.adapter.syncFromPhysics();\n\t\t}\n\t\temit(WORLD_STEP, dt);\n\t}\n}\n","import { Ellipse, ellipsePool } from \"../geometries/ellipse.ts\";\nimport { Color, colorPool } from \"../math/color.ts\";\nimport Renderable from \"../renderable/renderable.js\";\nimport state from \"../state/state.ts\";\nimport type CanvasRenderer from \"../video/canvas/canvas_renderer.js\";\nimport type WebGLRenderer from \"../video/webgl/webgl_renderer.js\";\n\n/**\n * A 2D point light.\n *\n * Light2d carries the *properties* of a light (color, radii, intensity,\n * height, flags, position) and asks the active renderer to render it\n * via `renderer.drawLight(this)`. The renderer picks the right machinery:\n *\n * - **WebGL**: a single quad through a shared procedural radial-falloff\n *   fragment shader (`RadialGradientEffect`). One shader is reused across\n *   every Light2d on the renderer; no per-light texture is allocated.\n * - **Canvas**: a small `Gradient` config object (cached per-light in a\n *   `WeakMap` and rebuilt only when radii / color / intensity change),\n *   rasterized via `Gradient.toCanvas()` on every draw into a single\n *   shared `CanvasRenderTarget`, then composited with `drawImage`. The\n *   per-light cache holds only the gradient stops, not the bitmap — the\n *   render target is one-per-engine.\n *\n * Light2d itself is renderer-agnostic — no shader knowledge, no canvas\n * allocation, no renderer reference held.\n * @category Lighting\n * @see stage.lights\n */\nexport default class Light2d extends Renderable {\n\t/**\n\t * the color of the light\n\t * @default \"#FFF\"\n\t */\n\tcolor: Color;\n\n\t/** The horizontal radius of the light */\n\tradiusX: number;\n\n\t/** The vertical radius of the light */\n\tradiusY: number;\n\n\t/**\n\t * The intensity of the light\n\t * @default 0.7\n\t */\n\tintensity: number;\n\n\t/**\n\t * the world-space geometry of the light's visible area, rewritten each\n\t * frame by {@link Light2d#getVisibleArea} from transform-aware bounds.\n\t * @ignore\n\t */\n\tvisibleArea: Ellipse;\n\n\t/**\n\t * When `true`, this light acts as a pure illumination source — the\n\t * gradient texture isn't drawn. The light still feeds the `Stage`\n\t * ambient-cutout pass and the WebGL lit-sprite pipeline's per-frame\n\t * uniforms, so normal-mapped sprites still get shaded by it. Use this for\n\t * SpriteIlluminator-style demos where the light should be invisible (only\n\t * its effect on normal-mapped surfaces is what you want to see).\n\t *\n\t * Default `false`, preserving the legacy \"soft glowing spot\" behavior.\n\t * @default false\n\t */\n\tilluminationOnly: boolean;\n\n\t/**\n\t * Light height above the sprite plane (Z axis), in the same units as\n\t * `radiusX`/`radiusY`. Used by the WebGL lit-sprite pipeline as the Z\n\t * component of the light direction in the `dot(normal, lightDir)`\n\t * calculation: a low height makes the lighting graze across the surface\n\t * (long visible shadows on normal-map detail), a high height makes it\n\t * head-on (more uniform brightness on the lit hemisphere).\n\t *\n\t * Default is `max(radiusX, radiusY) * 0.075` — a balanced look at the\n\t * asset's native scale that prevents lights at the sprite's center from\n\t * producing degenerate flat shading.\n\t *\n\t * Named `lightHeight` (not just `height`) to avoid colliding with the\n\t * bbox-height getter Light2d inherits from `Rect`.\n\t */\n\tlightHeight: number;\n\n\t/**\n\t * Create a 2D point light.\n\t *\n\t * A `Light2d` is a first-class world Renderable: add it to a container\n\t * with `app.world.addChild(light)` (or any sub-container, including a\n\t * `Sprite`, so the light follows the parent via its transform). On\n\t * activation, the light auto-registers with the active `Stage`'s\n\t * lighting set so the ambient overlay (`Stage.ambientLight`) cuts a\n\t * hole at the light's visible area, and a radial gradient from the\n\t * given `color` (full intensity at center → fully transparent at the\n\t * radius) is composited additively on top — producing a soft spot\n\t * light. Rendering happens inside each `Camera2d`'s post-effect FBO\n\t * bracket so any camera shader (vignette, color-matrix, scanlines,\n\t * etc.) wraps the lighting output.\n\t *\n\t * Set `radiusY` to a different value than `radiusX` for a stretched\n\t * (elliptical) light. The `intensity` parameter scales the gradient's\n\t * inner alpha; the `Stage.ambientLight` color and alpha control how\n\t * dark the unlit areas are. Use `light.blendMode` to override the\n\t * default additive blend if needed.\n\t * @param x - The horizontal position of the light's center (matches `Ellipse(x, y, w, h)` conventions).\n\t * @param y - The vertical position of the light's center.\n\t * @param radiusX - The horizontal radius of the light.\n\t * @param [radiusY=radiusX] - The vertical radius of the light.\n\t * @param [color=\"#FFF\"] - The color of the light at full intensity.\n\t * @param [intensity=0.7] - The peak alpha of the radial gradient at the light's center (0–1).\n\t */\n\tconstructor(\n\t\tx: number,\n\t\ty: number,\n\t\tradiusX: number,\n\t\tradiusY: number = radiusX,\n\t\tcolor: Color | string = \"#FFF\",\n\t\tintensity: number = 0.7,\n\t) {\n\t\t// pos is the light's CENTER (matches `Ellipse(x, y, w, h)` and\n\t\t// `Sprite` conventions); the centered anchor below makes Renderable's\n\t\t// transform stack scale/rotate around that center too.\n\t\tsuper(x, y, radiusX * 2, radiusY * 2);\n\n\t\tthis.color = colorPool.get();\n\t\tif (color instanceof Color) {\n\t\t\tthis.color.copy(color);\n\t\t} else {\n\t\t\tthis.color.parseCSS(color);\n\t\t}\n\n\t\tthis.radiusX = radiusX;\n\t\tthis.radiusY = radiusY;\n\t\tthis.intensity = intensity;\n\n\t\t/**\n\t\t * the default blend mode to be applied when rendering this light\n\t\t * @see CanvasRenderer#setBlendMode\n\t\t * @see WebGLRenderer#setBlendMode\n\t\t */\n\t\tthis.blendMode = \"lighter\";\n\n\t\t// initial shape — `getVisibleArea()` rewrites this each frame from\n\t\t// transform-aware bounds.\n\t\tthis.visibleArea = ellipsePool.get(\n\t\t\tthis.pos.x,\n\t\t\tthis.pos.y,\n\t\t\tthis.width,\n\t\t\tthis.height,\n\t\t);\n\n\t\t// centered anchor — transforms (scale, rotate) pivot around `pos`.\n\t\tthis.anchorPoint.set(0.5, 0.5);\n\n\t\tthis.illuminationOnly = false;\n\t\tthis.lightHeight = Math.max(radiusX, radiusY) * 0.075;\n\t}\n\n\t/**\n\t * the horizontal coordinate of this light's center.\n\t * Overrides Rect's getter, which assumes `pos` is the bbox top-left and\n\t * returns `pos.x + width/2`. Light2d uses `anchorPoint = (0.5, 0.5)`, so\n\t * `pos` already IS the center.\n\t */\n\toverride get centerX(): number {\n\t\treturn this.pos.x;\n\t}\n\toverride set centerX(value: number) {\n\t\tthis.pos.x = value;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * the vertical coordinate of this light's center.\n\t * @see Light2d#centerX\n\t */\n\toverride get centerY(): number {\n\t\treturn this.pos.y;\n\t}\n\toverride set centerY(value: number) {\n\t\tthis.pos.y = value;\n\t\tthis.recalc();\n\t\tthis.updateBounds();\n\t}\n\n\t/**\n\t * Set new radii for this light.\n\t *\n\t * Updates `radiusX`/`radiusY` and the underlying bbox (via\n\t * `Renderable.resize(width, height)`) so `getBounds()` and\n\t * `getVisibleArea()` — which feed the ambient-cutout pass — track the\n\t * new size. The Canvas renderer's gradient cache auto-invalidates on\n\t * next draw via its property comparison; the WebGL procedural shader\n\t * adapts to the new dimensions automatically.\n\t *\n\t * Named `setRadii` (not `resize`) so it does not shadow\n\t * `Renderable.resize(width, height)` — code that operates on a\n\t * generic `Renderable` and calls `.resize(w, h)` keeps working when\n\t * the instance happens to be a `Light2d`.\n\t * @param radiusX - new horizontal radius\n\t * @param [radiusY=radiusX] - new vertical radius\n\t */\n\tsetRadii(radiusX: number, radiusY: number = radiusX) {\n\t\tthis.radiusX = radiusX;\n\t\tthis.radiusY = radiusY;\n\t\tthis.resize(radiusX * 2, radiusY * 2);\n\t}\n\n\t/**\n\t * returns a geometry representing the visible area of this light, in\n\t * world-space coordinates (so it aligns with the rendered gradient\n\t * regardless of camera scroll or container parenting).\n\t * @returns the light visible mask\n\t */\n\tgetVisibleArea(): Ellipse {\n\t\tconst b = this.getBounds();\n\t\t// `b.width/b.height` are the transform-aware (and anchor-aware) bbox\n\t\t// dimensions, so the cutout tracks scale changes.\n\t\treturn this.visibleArea.setShape(b.centerX, b.centerY, b.width, b.height);\n\t}\n\n\t/**\n\t * update function\n\t * @returns true if dirty\n\t */\n\toverride update(): boolean {\n\t\treturn true;\n\t}\n\n\t/**\n\t * preDraw this Light2d (automatically called by melonJS)\n\t * @param renderer - a renderer instance\n\t */\n\toverride preDraw(renderer: CanvasRenderer | WebGLRenderer) {\n\t\tsuper.preDraw(renderer);\n\t\trenderer.setBlendMode(this.blendMode);\n\t}\n\n\t/**\n\t * draw this Light2d (automatically called by melonJS).\n\t *\n\t * Delegates to `renderer.drawLight(this)` — each renderer picks its\n\t * own implementation (procedural shader on WebGL; cached `Gradient`\n\t * rasterized into a shared `CanvasRenderTarget` on Canvas). Light2d\n\t * itself doesn't know which path is used.\n\t * @param renderer - a renderer instance\n\t */\n\toverride draw(renderer: CanvasRenderer | WebGLRenderer) {\n\t\tif (this.illuminationOnly) {\n\t\t\treturn;\n\t\t}\n\t\trenderer.drawLight(this);\n\t}\n\n\t/**\n\t * Auto-register this light with the active Stage's lighting set when\n\t * added to a container. The Stage uses that set to build the ambient\n\t * overlay cutouts; rendering the light itself is handled normally as\n\t * part of the world tree walk.\n\t * @ignore\n\t */\n\toverride onActivateEvent() {\n\t\tstate.current()?._registerLight(this);\n\t}\n\n\t/**\n\t * Auto-deregister this light from the active Stage's lighting set when\n\t * removed from a container.\n\t * @ignore\n\t */\n\toverride onDeactivateEvent() {\n\t\tstate.current()?._unregisterLight(this);\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\toverride destroy() {\n\t\tcolorPool.release(this.color);\n\t\tellipsePool.release(this.visibleArea);\n\t\t// The Canvas renderer's per-light gradient cache entry (if any) becomes\n\t\t// GC-eligible via its WeakMap once this Light2d is no longer referenced.\n\t\tsuper.destroy();\n\t}\n}\n","import { randomFloat } from \"../math/math.ts\";\nimport { Vector2d, vector2dPool } from \"../math/vector2d.ts\";\n\nimport type Container from \"../renderable/container.js\";\nimport Renderable from \"../renderable/renderable.js\";\nimport { createPool, registerPool } from \"../system/pool.ts\";\nimport CanvasRenderer from \"../video/canvas/canvas_renderer.js\";\nimport WebGLRenderer from \"../video/webgl/webgl_renderer.js\";\nimport ParticleEmitter from \"./emitter.ts\";\n\n/**\n * Single Particle Object.\n * @category Particles\n */\nexport default class Particle extends Renderable {\n\tvel: Vector2d;\n\timage: HTMLCanvasElement | HTMLImageElement;\n\tlife: number;\n\tstartLife: number;\n\tstartScale: number;\n\tendScale: number;\n\tgravity: number;\n\twind: number;\n\tfollowTrajectory: boolean;\n\tonlyInViewport: boolean;\n\taccurateBounds: boolean;\n\t_deltaInv: number;\n\t_halfW: number;\n\t_halfH: number;\n\t_angle: number;\n\talive: boolean;\n\n\t/**\n\t * @param emitter - the particle emitter\n\t */\n\tconstructor(emitter: ParticleEmitter) {\n\t\t// reset() ensures `settings.image` is set to either the user image or a\n\t\t// fallback canvas before any particle is spawned.\n\t\tconst image = emitter.settings.image as\n\t\t\t| HTMLCanvasElement\n\t\t\t| HTMLImageElement;\n\t\tsuper(\n\t\t\temitter.getRandomPointX(),\n\t\t\temitter.getRandomPointY(),\n\t\t\timage.width,\n\t\t\timage.height,\n\t\t);\n\t\t// particle velocity\n\t\tthis.vel = vector2dPool.get();\n\t\tthis.onResetEvent(emitter, true);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tonResetEvent(emitter: ParticleEmitter, newInstance: boolean = false) {\n\t\t// reset() guarantees `settings.image` is populated before particles spawn.\n\t\tconst image = emitter.settings.image as\n\t\t\t| HTMLCanvasElement\n\t\t\t| HTMLImageElement;\n\t\tif (!newInstance) {\n\t\t\tthis.pos.set(emitter.getRandomPointX(), emitter.getRandomPointY());\n\t\t\tthis.resize(image.width, image.height);\n\t\t\tthis.currentTransform.identity();\n\t\t}\n\n\t\tthis.image = image;\n\n\t\t// cache half-sizes — used every frame in the transform construction;\n\t\t// width/height stay fixed for the particle's lifetime.\n\t\tthis._halfW = this.width / 2;\n\t\tthis._halfH = this.height / 2;\n\n\t\t// Particle will always update\n\t\tthis.alwaysUpdate = true;\n\n\t\t// Anchor is baked into currentTransform (see update()), so reset the\n\t\t// renderable anchor to (0,0) — otherwise updateBounds() would apply\n\t\t// the default 0.5/0.5 offset on top of the already-anchored matrix.\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\tif (typeof emitter.settings.tint === \"string\") {\n\t\t\tthis.tint.parseCSS(emitter.settings.tint);\n\t\t}\n\n\t\tthis.blendMode = emitter.settings.textureAdditive ? \"additive\" : \"normal\";\n\n\t\tif (emitter.settings.blendMode !== \"normal\") {\n\t\t\tthis.blendMode = emitter.settings.blendMode;\n\t\t}\n\n\t\t// Sample start angle and speed around the emitter's base + variation.\n\t\t// `Math.random() * 2 - 1` gives a symmetric [-1, 1] multiplier; when the\n\t\t// variation is 0 the term collapses to 0 with no special-casing needed.\n\t\tconst angle =\n\t\t\temitter.settings.angle +\n\t\t\t(Math.random() * 2 - 1) * emitter.settings.angleVariation;\n\t\tconst speed =\n\t\t\temitter.settings.speed +\n\t\t\t(Math.random() * 2 - 1) * emitter.settings.speedVariation;\n\n\t\tthis.vel.set(speed * Math.cos(angle), -speed * Math.sin(angle));\n\n\t\t// randomFloat already returns a value in [min, max] — no extra clamp needed.\n\t\tthis.life = randomFloat(emitter.settings.minLife, emitter.settings.maxLife);\n\t\tthis.startLife = this.life;\n\t\tthis.startScale = randomFloat(\n\t\t\temitter.settings.minStartScale,\n\t\t\temitter.settings.maxStartScale,\n\t\t);\n\t\tthis.endScale = randomFloat(\n\t\t\temitter.settings.minEndScale,\n\t\t\temitter.settings.maxEndScale,\n\t\t);\n\n\t\t// Set the particle Gravity and Wind (horizontal gravity) as defined in emitter\n\t\tthis.gravity = emitter.settings.gravity;\n\t\tthis.wind = emitter.settings.wind;\n\n\t\t// Set if the particle update the rotation in accordance the trajectory\n\t\tthis.followTrajectory = emitter.settings.followTrajectory;\n\n\t\t// Set if the particle update only in Viewport\n\t\tthis.onlyInViewport = emitter.settings.onlyInViewport;\n\n\t\t// whether to refresh bounds every frame (debug-grade hitbox accuracy)\n\t\tthis.accurateBounds = emitter.settings.accurateBounds;\n\n\t\t// read the cached delta inverse from the emitter (constant after boot)\n\t\tthis._deltaInv = emitter._deltaInv;\n\n\t\t// Set the start particle rotation as defined in emitter\n\t\t// if the particle not follow trajectory\n\t\tif (!emitter.settings.followTrajectory) {\n\t\t\tthis._angle = randomFloat(\n\t\t\t\temitter.settings.minRotation,\n\t\t\t\temitter.settings.maxRotation,\n\t\t\t);\n\t\t}\n\n\t\tthis.alive = true;\n\t}\n\n\t/**\n\t * Update the Particle <br>\n\t * This is automatically called by the game manager {@link game}\n\t * @ignore\n\t * @param dt - time since the last update in milliseconds\n\t */\n\toverride update(dt: number) {\n\t\t// move things forward independent of the current frame rate\n\t\tconst skew = dt * this._deltaInv;\n\n\t\t// Decrease particle life\n\t\tthis.life = this.life > dt ? this.life - dt : 0;\n\n\t\tif (this.alive && this.life <= 0) {\n\t\t\tconst parent = this.ancestor as Container;\n\t\t\t// IMMEDIATE removal (not the deferred `removeChild`) because the\n\t\t\t// instance is released to the pool on the next line: a deferred\n\t\t\t// removal leaves a stale `removeChildNow` pending against this\n\t\t\t// instance, so a same-frame respawn recycling it would be\n\t\t\t// silently killed by that timer, and a same-frame emitter\n\t\t\t// teardown would destroy an instance already sitting in the\n\t\t\t// pool (poisoning every later `particlePool.get()`). The\n\t\t\t// immediate splice is safe here — `Container.update` walks its\n\t\t\t// children in reverse. keepalive=true since we recycle directly.\n\t\t\tparent.removeChildNow(this, true);\n\t\t\tparticlePool.release(this);\n\t\t\tthis.alive = false;\n\t\t\treturn false;\n\t\t}\n\n\t\t// Calculate the particle Age Ratio\n\t\tconst ageRatio = this.life / this.startLife;\n\n\t\t// Resize the particle as particle Age Ratio\n\t\tlet scale = this.startScale;\n\t\tif (this.startScale > this.endScale) {\n\t\t\tscale *= ageRatio;\n\t\t\tscale = scale < this.endScale ? this.endScale : scale;\n\t\t} else if (this.startScale < this.endScale) {\n\t\t\tscale /= ageRatio;\n\t\t\tscale = scale > this.endScale ? this.endScale : scale;\n\t\t}\n\n\t\t// Set the particle opacity as Age Ratio\n\t\tthis.alpha = ageRatio;\n\n\t\t// Adjust the particle velocity\n\t\tthis.vel.x += this.wind * skew;\n\t\tthis.vel.y += this.gravity * skew;\n\n\t\t// If necessary update the rotation of particle in accordance the particle trajectory\n\t\tconst angle = this.followTrajectory\n\t\t\t? Math.atan2(this.vel.y, this.vel.x)\n\t\t\t: this._angle;\n\n\t\tthis.pos.x += this.vel.x * skew;\n\t\tthis.pos.y += this.vel.y * skew;\n\n\t\t// Update particle transform — closed-form of the 4-step builder\n\t\t//   ScaleAndTranslate · T(half) · R(θ) · T(−half)\n\t\t// folded into a single setTransform() to skip 3 matrix multiplies per\n\t\t// particle per frame. See `closed-form equivalence` tests in\n\t\t// tests/emitter.spec.js for derivation + regression coverage.\n\t\tconst halfW = this._halfW;\n\t\tconst halfH = this._halfH;\n\t\tconst cos = Math.cos(angle);\n\t\tconst sin = Math.sin(angle);\n\t\tconst sCos = scale * cos;\n\t\tconst sSin = scale * sin;\n\t\tthis.currentTransform.setTransform(\n\t\t\tsCos,\n\t\t\tsSin,\n\t\t\t0,\n\t\t\t0,\n\t\t\t-sSin,\n\t\t\tsCos,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t\t0,\n\t\t\tthis.pos.x - scale * (halfW * cos - halfH * sin),\n\t\t\tthis.pos.y - scale * (halfW * sin + halfH * cos),\n\t\t\t0,\n\t\t\t1,\n\t\t);\n\n\t\t// Refresh bounds only when the user opted in to per-frame accuracy.\n\t\t// Without this, the hitbox lags one frame behind the visual — fine for\n\t\t// viewport culling, visible only if you draw debug bounds.\n\t\tif (this.accurateBounds) {\n\t\t\tthis.updateBounds();\n\t\t}\n\n\t\t// mark as dirty if the particle is not dead yet\n\t\tthis.isDirty = this.inViewport || !this.onlyInViewport;\n\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\toverride draw(renderer: CanvasRenderer | WebGLRenderer) {\n\t\tconst w = this.width;\n\t\tconst h = this.height;\n\t\t// the transform already places (0,0) at the visual top-left corner.\n\t\trenderer.drawImage(this.image, 0, 0, w, h, 0, 0, w, h);\n\t}\n}\n\nexport const particlePool = createPool<Particle, [emitter: ParticleEmitter]>(\n\t(emitter) => {\n\t\tconst instance = new Particle(emitter);\n\n\t\treturn {\n\t\t\tinstance,\n\t\t\treset(emitter) {\n\t\t\t\tinstance.onResetEvent(emitter, false);\n\t\t\t},\n\t\t};\n\t},\n);\n\nregisterPool(\"particle\", particlePool);\n","import { polygonPool } from \"../geometries/polygon.ts\";\nimport { vector2dPool } from \"../math/vector2d.ts\";\nimport { collision } from \"./../physics/collision.js\";\nimport { resolveAnchorPoint } from \"./anchorPoint.ts\";\nimport Sprite from \"./sprite.js\";\n\n/**\n * a basic collectable helper class for immovable object (e.g. a coin)\n * @category Game Objects\n */\nexport default class Collectable extends Sprite {\n\t/**\n\t * @param {number} x - the x coordinates of the collectable\n\t * @param {number} y - the y coordinates of the collectable\n\t * @param {object} settings - See {@link Sprite}\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0,y:0}] - anchor point (note the backward-compatible `(0, 0)` default, unlike Sprite's centered one). Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`.\n\t */\n\tconstructor(x, y, settings) {\n\t\t// call the super constructor\n\t\tsuper(x, y, settings);\n\n\t\tthis.name = settings.name;\n\t\tthis.type = settings.type;\n\t\tthis.id = settings.id;\n\n\t\t// add and configure the physic body\n\t\tlet shape = settings.shapes;\n\t\tif (typeof shape === \"undefined\") {\n\t\t\tshape = polygonPool.get(0, 0, [\n\t\t\t\tvector2dPool.get(0, 0),\n\t\t\t\tvector2dPool.get(this.width, 0),\n\t\t\t\tvector2dPool.get(this.width, this.height),\n\t\t\t]);\n\t\t}\n\t\t// declarative body — adapter-portable, auto-registered on addChild.\n\t\t// `isSensor: true` matches the documented intent (\"immovable\n\t\t// object, picked up on overlap\"): collision events still fire so\n\t\t// the pickup callback runs, but the solver does not physically\n\t\t// push the player away. Under the builtin adapter this is a\n\t\t// no-op for Collectable subclasses that don't define\n\t\t// `onCollision` (the SAT detector already skipped push-out);\n\t\t// under Matter it is required — matter's solver always resolves\n\t\t// contacts unless a body is flagged as a sensor.\n\t\tthis.bodyDef = {\n\t\t\ttype: \"static\",\n\t\t\tshapes: Array.isArray(shape) ? shape : [shape],\n\t\t\tcollisionType: collision.types.COLLECTABLE_OBJECT,\n\t\t\t// by default only collides with PLAYER_OBJECT\n\t\t\tcollisionMask: collision.types.PLAYER_OBJECT,\n\t\t\tisSensor: true,\n\t\t};\n\n\t\t// Update anchorPoint\n\t\tif (settings.anchorPoint) {\n\t\t\tconst anchor = resolveAnchorPoint(settings.anchorPoint, \"Collectable\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t});\n\t\t\tthis.anchorPoint.set(anchor.x, anchor.y);\n\t\t} else {\n\t\t\t// for backward compatibility\n\t\t\tthis.anchorPoint.set(0, 0);\n\t\t}\n\t}\n}\n","import { colorPool } from \"../math/color.ts\";\nimport Renderable from \"./renderable.js\";\n\n/**\n * additional import for TypeScript\n * @import {Color} from \"./../math/color.ts\";\n * @import CanvasRenderer from \"./../video/canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../video/webgl/webgl_renderer.js\";\n * @import Camera2d from \"./../camera/camera2d.ts\";\n */\n\n/**\n * a generic Color Layer Object.  Fills the entire Canvas with the color not just the container the object belongs to.\n * @category Game Objects\n */\nexport default class ColorLayer extends Renderable {\n\t/**\n\t * @param {string} name - Layer name\n\t * @param {Color|string} color - CSS color\n\t * @param {number} [z = 0] - z-index position\n\t */\n\tconstructor(name, color, z) {\n\t\t// parent constructor\n\t\tsuper(0, 0, Infinity, Infinity);\n\n\t\t/**\n\t\t * the layer color component\n\t\t * @type {Color}\n\t\t */\n\t\tthis.color = colorPool.get().parseCSS(color);\n\n\t\tthis.onResetEvent(name, color, z);\n\t}\n\n\tonResetEvent(name, color, z = 0) {\n\t\t// apply given parameters\n\t\tthis.name = name;\n\t\tthis.pos.z = z;\n\t\tthis.floating = true;\n\t\t// string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)\n\t\tthis.color.parseCSS(color);\n\t}\n\n\t/**\n\t * draw this color layer (automatically called by melonJS)\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\tdraw(renderer, viewport) {\n\t\trenderer.save();\n\t\trenderer.clipRect(0, 0, viewport.width, viewport.height);\n\t\trenderer.clearColor(this.color);\n\t\trenderer.restore();\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tcolorPool.release(this.color);\n\t\tthis.color = undefined;\n\t\tsuper.destroy();\n\t}\n}\n","import { polygonPool } from \"../../geometries/polygon.ts\";\nimport { warning } from \"../../lang/console.js\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport Body from \"../../physics/builtin/body.js\";\nimport { resolveAnchorPoint } from \"../anchorPoint.ts\";\nimport Renderable from \"../renderable.js\";\nimport Sprite from \"../sprite.js\";\n\n/**\n * @import {Line} from \"./../../geometries/line.ts\";\n * @import {Rect} from \"./../../geometries/rectangle.ts\";\n * @import {Ellipse} from \"./../../geometries/ellipse.ts\";\n * @import {Polygon} from \"../../geometries/polygon.ts\";\n * @import {Bounds} from \"./../../physics/bounds.ts\";\n * @import CanvasRenderer from \"./../../video/canvas/canvas_renderer.js\";\n * @import WebGLRenderer from \"./../../video/webgl/webgl_renderer.js\";\n **/\n\n/**\n * a Generic Object Entity\n * @deprecated since 18.1.0 — use {@link Sprite} or {@link Renderable} combined with {@link Body} instead.\n *\n * ### Why Entity is deprecated\n *\n * `Entity` introduced an unnecessary extra layer: it wrapped a child {@link Renderable} (typically a {@link Sprite})\n * inside a parent object that also held a {@link Body}. This design led to a number of long-standing issues:\n *\n * - **Anchor point confusion** — Entity used its own anchor point to position the child renderable relative to the\n *   body bounds, which behaved differently from the standard {@link Renderable} anchor point. This caused\n *   persistent alignment bugs with bounds and rendering (see issues #848, #834, #754, #580, #922).\n * - **Indirect API** — animations, flipping, tinting, and other visual operations had to go through\n *   `this.renderable` instead of being called directly, making the API more verbose and error-prone.\n * - **Custom rendering pipeline** — Entity overrode `preDraw`/`draw` with a custom coordinate system that\n *   differed from the rest of the engine, making it harder to reason about positioning and transforms.\n *\n * ### The Sprite + Body approach\n *\n * The recommended replacement is to extend {@link Sprite} (for animated/image-based objects) or\n * {@link Renderable} (for custom-drawn objects) directly, and attach a {@link Body} in the constructor.\n * This approach:\n *\n * - Uses the **standard rendering pipeline** — no custom `preDraw` or coordinate system surprises.\n * - Provides a **direct API** — call `this.flipX()`, `this.setCurrentAnimation()`, `this.tint` directly.\n * - Aligns with **industry conventions** — attaching a physics body to a renderable is the standard pattern\n *   used by other game engines.\n * - Gives **full control** over body shape and position within the sprite frame.\n *\n * ### Migration Guide\n *\n * #### Example 1 — Animated Sprite with physics (using a texture atlas)\n * ```js\n * class PlayerSprite extends me.Sprite {\n *     constructor(x, y, settings) {\n *         // create the Sprite using atlas animation frames\n *         super(x, y, {\n *             ...game.texture.getAnimationSettings([\n *                 \"walk0001.png\", \"walk0002.png\", \"walk0003.png\"\n *             ]),\n *             anchorPoint: { x: 0.5, y: 1.0 }\n *         });\n *\n *         // add a physic body (use Tiled shapes if available, or define your own)\n *         this.body = new me.Body(this,\n *             settings.shapes || new me.Rect(0, 0, settings.width, settings.height)\n *         );\n *         this.body.collisionType = me.collision.types.PLAYER_OBJECT;\n *         this.body.setMaxVelocity(3, 15);\n *         this.body.setFriction(0.4, 0);\n *\n *         // define animations (called directly on the sprite, not on this.renderable)\n *         this.addAnimation(\"walk\", [\"walk0001.png\", \"walk0002.png\", \"walk0003.png\"]);\n *         this.setCurrentAnimation(\"walk\");\n *     }\n *\n *     update(dt) {\n *         // input handling, animations, etc. — all directly on `this`\n *         if (me.input.isKeyPressed(\"right\")) {\n *             this.body.force.x = this.body.maxVel.x;\n *             this.flipX(false);\n *         }\n *         return super.update(dt);\n *     }\n *\n *     onCollision(response, other) {\n *         return true; // solid\n *     }\n * }\n * ```\n *\n * #### Example 2 — Sprite with physics (using a standalone spritesheet image)\n * ```js\n * class EnemySprite extends me.Sprite {\n *     constructor(x, y, settings) {\n *         super(x, y, Object.assign({\n *             image: \"enemy_spritesheet\",\n *             framewidth: 32,\n *             frameheight: 32,\n *         }, settings));\n *\n *         // add a physic body\n *         this.body = new me.Body(this, new me.Rect(0, 0, this.width, this.height));\n *         this.body.collisionType = me.collision.types.ENEMY_OBJECT;\n *         this.body.setMaxVelocity(1, 1);\n *         this.body.gravityScale = 0;\n *\n *         this.addAnimation(\"walk\", [0, 1, 2, 3]);\n *         this.addAnimation(\"dead\", [4]);\n *         this.setCurrentAnimation(\"walk\");\n *     }\n *\n *     onCollision(response, other) {\n *         return false;\n *     }\n * }\n * ```\n *\n * #### Example 3 — Custom-drawn Renderable with physics\n * ```js\n * class CustomBall extends me.Renderable {\n *     constructor(x, y, radius) {\n *         super(x, y, radius * 2, radius * 2);\n *         this.radius = radius;\n *\n *         // add a physic body with a circular shape\n *         this.body = new me.Body(this, new me.Ellipse(radius, radius, radius * 2, radius * 2));\n *         this.body.collisionType = me.collision.types.ENEMY_OBJECT;\n *         this.body.setMaxVelocity(4, 4);\n *         this.body.gravityScale = 0;\n *         this.body.force.set(\n *             me.Math.randomFloat(-4, 4),\n *             me.Math.randomFloat(-4, 4)\n *         );\n *     }\n *\n *     update(dt) {\n *         return super.update(dt);\n *     }\n *\n *     draw(renderer) {\n *         renderer.setColor(\"#FF0000\");\n *         renderer.fillEllipse(\n *             this.width / 2, this.height / 2,\n *             this.radius, this.radius\n *         );\n *     }\n *\n *     onCollision(response, other) {\n *         // bounce off on collision\n *         this.body.force.x = -this.body.force.x;\n *         this.body.force.y = -this.body.force.y;\n *         return false;\n *     }\n * }\n * ```\n *\n * @see Sprite\n * @see Renderable\n * @see Body\n * @category Game Objects\n */\nexport default class Entity extends Renderable {\n\tstatic _deprecationWarned = false;\n\n\t/**\n\t * @param {number} x - the x coordinates of the entity object\n\t * @param {number} y - the y coordinates of the entity object\n\t * @param {object} settings - Entity properties, to be defined through Tiled or when calling the entity constructor\n\t * <img src=\"../../images/object_properties.png\"/>\n\t * @param {number} settings.width - the physical width the entity takes up in game\n\t * @param {number} settings.height - the physical height the entity takes up in game\n\t * @param {string} [settings.name] - object entity name\n\t * @param {string} [settings.id] - object unique IDs\n\t * @param {Image|string} [settings.image] - resource name of a spritesheet to use for the entity renderable component\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint=0.0] - Entity anchor point. Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`.\n\t * @param {number} [settings.framewidth=settings.width] - width of a single frame in the given spritesheet\n\t * @param {number} [settings.frameheight=settings.width] - height of a single frame in the given spritesheet\n\t * @param {string} [settings.type] - object type\n\t * @param {number} [settings.collisionMask] - Mask collision detection for this object\n\t * @param {Rect[]|Polygon[]|Line[]|Ellipse[]} [settings.shapes] - the initial list of collision shapes (usually populated through Tiled)\n\t * @deprecated since 18.1.0 — see the class-level documentation for migration examples\n\t */\n\tconstructor(x, y, settings) {\n\t\t// deprecation warning (once per session)\n\t\tif (!Entity._deprecationWarned) {\n\t\t\twarning(\"me.Entity\", \"me.Sprite combined with me.Body\", \"18.1.0\");\n\t\t\tEntity._deprecationWarned = true;\n\t\t}\n\n\t\t// call the super constructor\n\t\tsuper(x, y, settings.width, settings.height);\n\n\t\t/**\n\t\t * The array of renderable children of this entity.\n\t\t * @ignore\n\t\t */\n\t\tthis.children = [];\n\n\t\tif (settings.image) {\n\t\t\t// set the frame size to the given entity size, if not defined in settings\n\t\t\tsettings.framewidth = settings.framewidth || settings.width;\n\t\t\tsettings.frameheight = settings.frameheight || settings.height;\n\t\t\tthis.renderable = new Sprite(0, 0, settings);\n\t\t}\n\n\t\t// Update anchorPoint\n\t\tif (settings.anchorPoint) {\n\t\t\tconst anchor = resolveAnchorPoint(settings.anchorPoint, \"Entity\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t});\n\t\t\tthis.anchorPoint.setMuted(anchor.x, anchor.y);\n\t\t} else {\n\t\t\t// for backward compatibility\n\t\t\tthis.anchorPoint.setMuted(0, 0);\n\t\t}\n\n\t\t// set the sprite name if specified\n\t\tif (typeof settings.name === \"string\") {\n\t\t\tthis.name = settings.name;\n\t\t}\n\n\t\t/**\n\t\t * object type (as defined in Tiled)\n\t\t * @type {string}\n\t\t */\n\t\tthis.type = settings.type || \"\";\n\n\t\t/**\n\t\t * object unique ID (as defined in Tiled)\n\t\t * @type {number}\n\t\t */\n\t\tthis.id = settings.id || \"\";\n\n\t\t/**\n\t\t * dead/living state of the entity<br>\n\t\t * default value : true\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.alive = true;\n\n\t\t// initialize the default body\n\t\tif (typeof settings.shapes === \"undefined\") {\n\t\t\tsettings.shapes = polygonPool.get(0, 0, [\n\t\t\t\tvector2dPool.get(0, 0),\n\t\t\t\tvector2dPool.get(this.width, 0),\n\t\t\t\tvector2dPool.get(this.width, this.height),\n\t\t\t\tvector2dPool.get(0, this.height),\n\t\t\t]);\n\t\t}\n\n\t\t/**\n\t\t * the entity body object\n\t\t * @type {Body}\n\t\t */\n\t\tthis.body = new Body(this, settings.shapes, () => {\n\t\t\treturn this.onBodyUpdate();\n\t\t});\n\n\t\t// resize the entity if required\n\t\tif (this.width === 0 && this.height === 0) {\n\t\t\tthis.resize(this.body.getBounds().width, this.body.getBounds().height);\n\t\t}\n\n\t\t// set the  collision mask and type (if defined)\n\t\tthis.body.setCollisionMask(settings.collisionMask);\n\t\tthis.body.setCollisionType(settings.collisionType);\n\n\t\t// disable for entities\n\t\tthis.autoTransform = false;\n\t}\n\n\t/**\n\t * The entity renderable component (can be any objects deriving from me.Renderable, like me.Sprite for example)\n\t * @type {Renderable}\n\t */\n\n\tget renderable() {\n\t\treturn this.children[0];\n\t}\n\n\tset renderable(value) {\n\t\tif (value instanceof Renderable) {\n\t\t\tthis.children[0] = value;\n\t\t\tthis.children[0].ancestor = this;\n\t\t\t// auto-align: inherit renderable's anchor if entity is still at default\n\t\t\tif (this.anchorPoint.x === 0 && this.anchorPoint.y === 0) {\n\t\t\t\tthis.anchorPoint.setMuted(value.anchorPoint.x, value.anchorPoint.y);\n\t\t\t}\n\t\t\tthis.updateBounds();\n\t\t} else {\n\t\t\tthrow new Error(value + \"should extend me.Renderable\");\n\t\t}\n\t}\n\n\t/** @ignore */\n\tupdate(dt) {\n\t\tif (this.renderable) {\n\t\t\tthis.isDirty |= this.renderable.update(dt);\n\t\t}\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * update the bounding box for this entity.\n\t * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates\n\t * @returns {Bounds} this entity bounding box Rectangle object\n\t */\n\tupdateBounds(absolute = true) {\n\t\tconst bounds = this.getBounds();\n\n\t\tbounds.clear();\n\t\tbounds.addFrame(0, 0, this.width, this.height);\n\n\t\t// add each renderable bounds\n\t\tif (this.children && this.children.length > 0) {\n\t\t\tbounds.addBounds(this.children[0].getBounds());\n\t\t}\n\n\t\tif (this.body) {\n\t\t\tbounds.addBounds(this.body.getBounds());\n\t\t}\n\n\t\tif (absolute === true) {\n\t\t\tconst absPos = this.getAbsolutePosition();\n\t\t\tbounds.centerOn(\n\t\t\t\tabsPos.x + bounds.x + bounds.width / 2,\n\t\t\t\tabsPos.y + bounds.y + bounds.height / 2,\n\t\t\t);\n\t\t}\n\n\t\treturn bounds;\n\t}\n\n\t/**\n\t * update the bounds when the body is modified\n\t */\n\tonBodyUpdate() {\n\t\tthis.updateBounds();\n\t}\n\n\tpreDraw(renderer) {\n\t\trenderer.save();\n\n\t\t// translate to the entity position\n\t\trenderer.translate(\n\t\t\tthis.pos.x + this.body.getBounds().x,\n\t\t\tthis.pos.y + this.body.getBounds().y,\n\t\t);\n\n\t\tif (this.renderable instanceof Renderable) {\n\t\t\t// draw the child renderable's anchorPoint at the entity's\n\t\t\t// anchor point.  the entity's anchor point is a scale from\n\t\t\t// body position to body width/height\n\t\t\trenderer.translate(\n\t\t\t\tthis.anchorPoint.x * this.body.getBounds().width,\n\t\t\t\tthis.anchorPoint.y * this.body.getBounds().height,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * draw this entity (automatically called by melonJS)\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\tdraw(renderer, viewport) {\n\t\tconst renderable = this.renderable;\n\t\tif (renderable instanceof Renderable) {\n\t\t\t// predraw (apply transforms)\n\t\t\trenderable.preDraw(renderer);\n\n\t\t\t// draw the object\n\t\t\trenderable.draw(renderer, viewport);\n\n\t\t\t// postdraw (clean-up);\n\t\t\trenderable.postDraw(renderer);\n\t\t}\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\t// free some property objects\n\t\tif (this.renderable) {\n\t\t\tthis.renderable.destroy.apply(this.renderable, arguments);\n\t\t\tthis.children.splice(0, 1);\n\t\t}\n\n\t\t// call the parent destroy method, spreading the actual arguments —\n\t\t// passing the `arguments` object itself would hand subclasses'\n\t\t// `onDestroyEvent(app)` an Arguments wrapper instead of the value\n\t\tsuper.destroy(...arguments);\n\t}\n\n\t/**\n\t * onDeactivateEvent Notification function\n\t */\n\tonDeactivateEvent() {\n\t\tif (this.renderable && this.renderable.onDeactivateEvent) {\n\t\t\tthis.renderable.onDeactivateEvent();\n\t\t}\n\t}\n}\n","import { vector2dPool } from \"../math/vector2d.ts\";\nimport {\n\tLEVEL_LOADED,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n\tVIEWPORT_ONCHANGE,\n\tVIEWPORT_ONRESIZE,\n} from \"../system/event.ts\";\nimport * as stringUtil from \"./../utils/string.ts\";\nimport { resolveAnchorPoint } from \"./anchorPoint.ts\";\nimport Sprite from \"./sprite.js\";\n\n/**\n * additional import for TypeScript\n * @import {Vector2d} from \"../math/vector2d.js\";\n */\n\n/**\n * a generic Image Layer Object\n * @category Game Objects\n */\nexport default class ImageLayer extends Sprite {\n\t/**\n\t * @param {number} x - x coordinate\n\t * @param {number} y - y coordinate\n\t * @param {object} settings - ImageLayer properties\n\t * @param {HTMLImageElement|HTMLCanvasElement|CompressedImage|string} settings.image - Image reference. See {@link loader.getImage}\n\t * @param {string} [settings.name=\"me.ImageLayer\"] - layer name\n\t * @param {number} [settings.z=0] - z-index position\n\t * @param {number|Vector2d} [settings.ratio=1.0] - Scrolling ratio to be applied. See {@link ImageLayer#ratio}\n\t * @param {\"repeat\"|\"repeat-x\"|\"repeat-y\"|\"no-repeat\"} [settings.repeat=\"repeat\"] - define if and how an Image Layer should be repeated. See {@link ImageLayer#repeat}\n\t * @param {number|string|Vector2d|{x:number,y:number}} [settings.anchorPoint=<0.0,0.0>] - Define how the image is anchored to the viewport bound. By default, its upper-left corner is anchored to the viewport bounds upper left corner. Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"` (anchored relative to the viewport, e.g. `\"bottom\"` = bottom-center of the viewport); a bare number anchors both axes.\n\t * @example\n\t * // create a repetitive background pattern on the X axis using the citycloud image asset\n\t * app.world.addChild(new me.ImageLayer(0, 0, {\n\t *     image:\"citycloud\",\n\t *     repeat :\"repeat-x\"\n\t * }), 1);\n\t */\n\tconstructor(x, y, settings) {\n\t\t// bare-number shorthand (anchors both axes) — normalize BEFORE super():\n\t\t// the Sprite base constructor consumes the same settings.anchorPoint,\n\t\t// and the shared resolver doesn't accept bare numbers (pre-super\n\t\t// settings mutation has precedent, e.g. Entity / TMXTileMap)\n\t\tif (typeof settings.anchorPoint === \"number\") {\n\t\t\tsettings.anchorPoint = {\n\t\t\t\tx: settings.anchorPoint,\n\t\t\t\ty: settings.anchorPoint,\n\t\t\t};\n\t\t}\n\t\t// call the constructor\n\t\tsuper(x, y, settings);\n\n\t\t// render in screen coordinates\n\t\tthis.floating = true;\n\n\t\t// background layers should render in all cameras (e.g. minimap, split-screen)\n\t\tthis.visibleInAllCameras = true;\n\n\t\t// image drawing offset\n\t\tthis.offset.set(x, y);\n\n\t\t/**\n\t\t * Define the image scrolling ratio<br>\n\t\t * Scrolling speed is defined by multiplying the viewport delta position by the specified ratio.\n\t\t * Setting this vector to &lt;0.0,0.0&gt; will disable automatic scrolling.<br>\n\t\t * To specify a value through Tiled, use one of the following format : <br>\n\t\t * - a number, to change the value for both axis <br>\n\t\t * - a json expression like `json:{\"x\":0.5,\"y\":0.5}` if you wish to specify a different value for both x and y\n\t\t * @type {Vector2d}\n\t\t * @default <1.0,1.0>\n\t\t */\n\t\tthis.ratio = vector2dPool.get(1.0, 1.0);\n\n\t\tif (typeof settings.ratio !== \"undefined\") {\n\t\t\t// little hack for backward compatibility\n\t\t\tif (stringUtil.isNumeric(settings.ratio)) {\n\t\t\t\tthis.ratio.set(settings.ratio, +settings.ratio);\n\t\t\t} /* vector */ else {\n\t\t\t\tthis.ratio.setV(settings.ratio);\n\t\t\t}\n\t\t}\n\n\t\tif (typeof settings.anchorPoint === \"undefined\") {\n\t\t\tthis.anchorPoint.set(0, 0);\n\t\t} else {\n\t\t\t// preset name or {x, y} (bare numbers were normalized pre-super);\n\t\t\t// invalid values warn + keep the historical silent (0, 0) outcome\n\t\t\tconst anchor = resolveAnchorPoint(settings.anchorPoint, \"ImageLayer\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t});\n\t\t\tthis.anchorPoint.set(anchor.x, anchor.y);\n\t\t}\n\n\t\tthis.repeat = settings.repeat || \"repeat\";\n\t}\n\n\t/**\n\t * Define if and how an Image Layer should be repeated.<br>\n\t * By default, an Image Layer is repeated both vertically and horizontally.<br>\n\t * Acceptable values : <br>\n\t * - 'repeat' - The background image will be repeated both vertically and horizontally <br>\n\t * - 'repeat-x' - The background image will be repeated only horizontally.<br>\n\t * - 'repeat-y' - The background image will be repeated only vertically.<br>\n\t * - 'no-repeat' - The background-image will not be repeated.<br>\n\t * @type {string}\n\t * @default 'repeat'\n\t */\n\tget repeat() {\n\t\treturn this._repeat;\n\t}\n\n\tset repeat(value) {\n\t\tthis._repeat = value;\n\t\tswitch (this._repeat) {\n\t\t\tcase \"no-repeat\":\n\t\t\t\tthis.repeatX = false;\n\t\t\t\tthis.repeatY = false;\n\t\t\t\tbreak;\n\t\t\tcase \"repeat-x\":\n\t\t\t\tthis.repeatX = true;\n\t\t\t\tthis.repeatY = false;\n\t\t\t\tbreak;\n\t\t\tcase \"repeat-y\":\n\t\t\t\tthis.repeatX = false;\n\t\t\t\tthis.repeatY = true;\n\t\t\t\tbreak;\n\t\t\tdefault: // \"repeat\"\n\t\t\t\tthis.repeatX = true;\n\t\t\t\tthis.repeatY = true;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t// called when the layer is added to the game world or a container\n\tonActivateEvent() {\n\t\tif (!this.parentApp) {\n\t\t\tthrow new Error(\n\t\t\t\t\"ImageLayer requires a parent Application (must be added to an app's world container)\",\n\t\t\t);\n\t\t}\n\t\tconst viewport = this.parentApp.viewport;\n\t\t// set the initial size to match the viewport\n\t\tthis.resize(viewport.width, viewport.height);\n\t\tthis.createPattern();\n\t\t// register to the viewport change notification and context restore\n\t\ton(VIEWPORT_ONCHANGE, this.updateLayer, this);\n\t\ton(VIEWPORT_ONRESIZE, this.resize, this);\n\t\ton(ONCONTEXT_RESTORED, this.createPattern, this);\n\t\t// force a first refresh when the level is loaded\n\t\ton(LEVEL_LOADED, this.updateLayer, this);\n\t\t// in case the level is not added to the root container,\n\t\t// the onActivateEvent call happens after the LEVEL_LOADED event\n\t\t// so we need to force a first update\n\t\tif (this.ancestor.root !== true) {\n\t\t\tthis.updateLayer();\n\t\t}\n\t}\n\n\t/**\n\t * resize the Image Layer to match the given size\n\t * @param {number} w - new width\n\t * @param {number} h - new height\n\t */\n\tresize(w, h) {\n\t\treturn super.resize(\n\t\t\tthis.repeatX ? Infinity : w,\n\t\t\tthis.repeatY ? Infinity : h,\n\t\t);\n\t}\n\n\t/**\n\t * createPattern function\n\t * @ignore\n\t */\n\tcreatePattern() {\n\t\tthis._pattern = this.parentApp.renderer.createPattern(\n\t\t\tthis.image,\n\t\t\tthis._repeat,\n\t\t);\n\t}\n\n\t/**\n\t * updateLayer function\n\t * @ignore\n\t */\n\tupdateLayer() {\n\t\tconst rx = this.ratio.x;\n\t\tconst ry = this.ratio.y;\n\n\t\tconst viewport = this.parentApp.viewport;\n\n\t\tif (rx === 0 && ry === 0) {\n\t\t\t// static image\n\t\t\treturn;\n\t\t}\n\n\t\tconst width = this.width;\n\t\tconst height = this.height;\n\t\tconst bw = viewport.bounds.width;\n\t\tconst bh = viewport.bounds.height;\n\t\tconst ax = this.anchorPoint.x;\n\t\tconst ay = this.anchorPoint.y;\n\t\t/*\n\t\t * Automatic positioning\n\t\t *\n\t\t * See https://github.com/melonjs/melonJS/issues/741#issuecomment-138431532\n\t\t * for a thorough description of how this works.\n\t\t */\n\t\tconst x =\n\t\t\tax * (rx - 1) * (bw - viewport.width) +\n\t\t\tthis.offset.x -\n\t\t\trx * viewport.pos.x;\n\t\tconst y =\n\t\t\tay * (ry - 1) * (bh - viewport.height) +\n\t\t\tthis.offset.y -\n\t\t\try * viewport.pos.y;\n\n\t\t// Repeat horizontally; start drawing from left boundary\n\t\tif (this.repeatX) {\n\t\t\tthis.pos.x = x % width;\n\t\t} else {\n\t\t\tthis.pos.x = x;\n\t\t}\n\n\t\t// Repeat vertically; start drawing from top boundary\n\t\tif (this.repeatY) {\n\t\t\tthis.pos.y = y % height;\n\t\t} else {\n\t\t\tthis.pos.y = y;\n\t\t}\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Override the default preDraw to skip the base class's anchor offset\n\t * translation and `autoTransform` application — `ImageLayer.draw()`\n\t * computes its own world-space position from the viewport, the parallax\n\t * `ratio`, anchor, and zoom, so applying anchor/transform here would\n\t * double-translate the layer.\n\t *\n\t * Coordinate-sensitive setup (flip, stencil mask) is intentionally\n\t * deferred to `draw()` where it runs *after* the per-camera zoom\n\t * translate/scale — that way it stays correctly aligned at any\n\t * `viewport.zoom` and across multiple cameras. Coordinate-independent\n\t * setup (alpha, post-effects, tint, blend) stays here. The inherited\n\t * `Renderable.postDraw` cleans up symmetrically (`clearTint` /\n\t * `clearMask` / `endPostEffect` / `restore`).\n\t * @ignore\n\t */\n\tpreDraw(renderer) {\n\t\t// save the context\n\t\trenderer.save();\n\n\t\t// apply the defined alpha value\n\t\trenderer.setGlobalAlpha(renderer.globalAlpha() * this.getOpacity());\n\n\t\t// delegate post-effect setup to the renderer (custom shader / postEffects)\n\t\tif (!this._postEffectManaged) {\n\t\t\trenderer.beginPostEffect(this);\n\t\t}\n\n\t\t// apply the defined tint\n\t\trenderer.setTint(this.tint);\n\n\t\t// apply blending if different from \"normal\"\n\t\tif (this.blendMode !== renderer.getBlendMode()) {\n\t\t\trenderer.setBlendMode(this.blendMode);\n\t\t}\n\t}\n\n\t/**\n\t * draw this ImageLayer (automatically called by melonJS)\n\t * @protected\n\t * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance\n\t * @param {Camera2d} [viewport] - the viewport to (re)draw\n\t */\n\tdraw(renderer, viewport) {\n\t\tconst width = this.width;\n\t\tconst height = this.height;\n\t\tconst bw = viewport.bounds.width;\n\t\tconst bh = viewport.bounds.height;\n\t\tconst ax = this.anchorPoint.x;\n\t\tconst ay = this.anchorPoint.y;\n\t\tconst rx = this.ratio.x;\n\t\tconst ry = this.ratio.y;\n\t\tconst vZoom = viewport.zoom;\n\n\t\tlet x, y;\n\n\t\tif (rx === 0 && ry === 0) {\n\t\t\t// static image\n\t\t\tx = this.pos.x + ax * (bw - width);\n\t\t\ty = this.pos.y + ay * (bh - height);\n\t\t} else {\n\t\t\t// parallax — compute position from the current viewport passed to draw()\n\t\t\tx =\n\t\t\t\tax * (rx - 1) * (bw - viewport.width) +\n\t\t\t\tthis.offset.x -\n\t\t\t\trx * viewport.pos.x;\n\t\t\ty =\n\t\t\t\tay * (ry - 1) * (bh - viewport.height) +\n\t\t\t\tthis.offset.y -\n\t\t\t\try * viewport.pos.y;\n\n\t\t\tif (this.repeatX) {\n\t\t\t\tx = x % width;\n\t\t\t}\n\t\t\tif (this.repeatY) {\n\t\t\t\ty = y % height;\n\t\t\t}\n\t\t}\n\n\t\t// for zoomed cameras, scale positions and pattern to match the world view\n\t\trenderer.translate(x * vZoom, y * vZoom);\n\t\trenderer.scale(vZoom, vZoom);\n\n\t\t// apply flip — pivot at the centre of the drawn pattern in the\n\t\t// post-zoom local frame, so the mirror lands in the visible viewport\n\t\t// regardless of `viewport.zoom`. Done here (not in preDraw) because\n\t\t// preDraw runs before this translate/scale and would misalign.\n\t\tif (this._flip.x || this._flip.y) {\n\t\t\tconst px = viewport.width;\n\t\t\tconst py = viewport.height;\n\t\t\trenderer.translate(px, py);\n\t\t\trenderer.scale(this._flip.x ? -1 : 1, this._flip.y ? -1 : 1);\n\t\t\trenderer.translate(-px, -py);\n\t\t}\n\n\t\t// stencil mask — also applied in the post-zoom local frame so the\n\t\t// mask shape's coordinates are interpreted relative to the layer's\n\t\t// drawn area, matching `drawPattern` below.\n\t\tif (this.mask) {\n\t\t\trenderer.setMask(this.mask);\n\t\t}\n\n\t\t// Clamp the draw extent to the source dimensions on any axis we\n\t\t// are NOT tiling. Asking the renderer to fill past the source on\n\t\t// a non-tiling axis is what makes Canvas (transparent fill per\n\t\t// HTML spec) and WebGL (GL_CLAMP_TO_EDGE stretches the edge\n\t\t// pixel) diverge — Canvas leaves the overflow transparent while\n\t\t// WebGL stretches the bottom row (repeat-x) or right column\n\t\t// (repeat-y) to fill it. By drawing exactly the source extent on\n\t\t// the non-tiling axis, neither renderer enters its overflow path\n\t\t// and the two outputs converge (issue #1290).\n\t\tconst drawW = this.repeatX ? viewport.width * 2 : width;\n\t\tconst drawH = this.repeatY ? viewport.height * 2 : height;\n\t\trenderer.drawPattern(this._pattern, 0, 0, drawW, drawH);\n\t}\n\n\t// called when the layer is removed from the game world or a container\n\tonDeactivateEvent() {\n\t\t// cancel all event subscriptions\n\t\toff(VIEWPORT_ONCHANGE, this.updateLayer, this);\n\t\toff(VIEWPORT_ONRESIZE, this.resize, this);\n\t\toff(LEVEL_LOADED, this.updateLayer, this);\n\t\toff(ONCONTEXT_RESTORED, this.createPattern, this);\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tvector2dPool.release(this.ratio);\n\t\tthis.ratio = undefined;\n\t\tsuper.destroy();\n\t}\n}\n","import Sprite from \"./sprite.js\";\n\n/**\n * additional import for TypeScript\n * @import {Color} from \"./../math/color.ts\";\n * @import { TextureAtlas } from \"./../video/texture/atlas.js\";\n */\n\n// Reusable scratch for the 9-slice grid, refilled on every draw. A\n// NineSliceSprite is drawn synchronously on the main thread (drawImage never\n// re-enters another NineSliceSprite.draw), so a shared module-level buffer is\n// safe and keeps draw() free of per-frame allocation. These hold the per-axis\n// source/dest *offsets*; the slice sizes are derived inline in the draw loop.\nconst sColX = [0, 0, 0];\nconst dColX = [0, 0, 0];\nconst sRowY = [0, 0, 0];\nconst dRowY = [0, 0, 0];\n\n/**\n * A NineSliceSprite is similar to a Sprite, but it uses 9-slice scaling to stretch its inner area to fit the size of the Renderable,\n * by proportionally scaling a sprite by splitting it in a grid of nine parts (with only parts 1, 3, 7, 9 not being scaled). <br>\n * <img src=\"images/9-slice-scaling.png\"/><br>\n * @category Game Objects\n * @see https://en.wikipedia.org/wiki/9-slice_scaling\n */\nexport default class NineSliceSprite extends Sprite {\n\t/**\n\t * @param {number} x - the x coordinates of the sprite object\n\t * @param {number} y - the y coordinates of the sprite object\n\t * @param {object} settings - Configuration parameters for the Sprite object\n\t * @param {number} settings.width - the width of the Renderable over which the sprite needs to be stretched\n\t * @param {number} settings.height - the height of the Renderable over which the sprite needs to be stretched\n\t * @param {number} [settings.insetx] - the width of a corner over which the sprite is unscaled (default is a quarter of the sprite width)\n\t * @param {number} [settings.insety] - the height of a corner over which the sprite is unscaled (default is a quarter of the sprite height)\n\t * @param {HTMLImageElement|HTMLCanvasElement|TextureAtlas|CompressedImage|string} settings.image - reference to spritesheet image, a texture atlas, a compressed texture, or to a texture atlas\n\t * @param {string} [settings.name=\"\"] - name of this object\n\t * @param {string} [settings.region] - region name of a specific region to use when using a texture atlas, see {@link TextureAtlas}\n\t * @param {number} [settings.framewidth] - Width of a single frame within the spritesheet\n\t * @param {number} [settings.frameheight] - Height of a single frame within the spritesheet\n\t * @param {string|Color} [settings.tint] - a tint to be applied to this sprite\n\t * @param {number} [settings.flipX] - flip the sprite on the horizontal axis\n\t * @param {number} [settings.flipY] - flip the sprite on the vertical axis\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0.5, y:0.5}] - Anchor point to draw the frame at (defaults to the center of the frame). Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`.\n\t * @example\n\t * this.panelSprite = new me.NineSliceSprite(0, 0, {\n\t *     image : game.texture,\n\t *     region : \"grey_panel\",\n\t *     width : this.width,\n\t *     height : this.height\n\t * });\n\t */\n\tconstructor(x, y, settings) {\n\t\t// call the super constructor\n\t\tsuper(x, y, settings);\n\n\t\t// ensure mandatory properties are defined\n\t\tif (\n\t\t\ttypeof settings.width !== \"number\" ||\n\t\t\ttypeof settings.height !== \"number\"\n\t\t) {\n\t\t\tthrow new Error(\"height and width properties are mandatory\");\n\t\t}\n\n\t\t// adjust the nss sprite size accordingly to the target \"expanded\" size\n\t\tthis.width = Math.floor(settings.width);\n\t\tthis.height = Math.floor(settings.height);\n\n\t\t// nine slice sprite specific internal variables\n\t\tthis.nss_width = this.width;\n\t\tthis.nss_height = this.height;\n\n\t\tthis.insetx = settings.insetx;\n\t\tthis.insety = settings.insety;\n\t}\n\n\t/**\n\t * width of the NineSliceSprite\n\t * @type {number}\n\t */\n\tget width() {\n\t\treturn super.width;\n\t}\n\tset width(value) {\n\t\tsuper.width = this.nss_width = value;\n\t}\n\n\t/**\n\t * height of the NineSliceSprite\n\t * @type {number}\n\t */\n\tget height() {\n\t\treturn super.height;\n\t}\n\tset height(value) {\n\t\tsuper.height = this.nss_height = value;\n\t}\n\n\t/**\n\t * Apply the current animation/atlas frame. The base {@link Sprite} swaps the\n\t * sub-texture, resolves the anchor (honoring trimming) and resizes itself to\n\t * the frame — but for a NineSliceSprite the size is the user-specified\n\t * \"expanded\" target that the 9 slices stretch to fill, and must NOT track the\n\t * frame. We delegate to the base for the texture/anchor work, then restore the\n\t * expanded size it clobbered. (Fixes #1115, where animating a NineSliceSprite\n\t * let the per-frame source size shrink the panel to a single frame.)\n\t * @param {object} region - the texture region object\n\t * @ignore\n\t */\n\t_applyFrame(region) {\n\t\t// capture the expanded size BEFORE super runs: its `this.width =` writes\n\t\t// route through our setter and overwrite nss_width/nss_height\n\t\tconst w = this.nss_width;\n\t\tconst h = this.nss_height;\n\t\tsuper._applyFrame(region);\n\t\t// during construction nss_* is still undefined (the constructor sets the\n\t\t// expanded size right after super()), so only restore once it exists\n\t\tif (w !== undefined) {\n\t\t\tthis.width = w;\n\t\t\tthis.height = h;\n\t\t}\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tdraw(renderer) {\n\t\t// the frame to draw\n\t\tconst frame = this.current;\n\n\t\t// cache the current position and size\n\t\tlet dx = this.pos.x;\n\t\tconst dy = this.pos.y;\n\n\t\tlet w = frame.width;\n\t\tlet h = frame.height;\n\n\t\t// frame offset in the texture/atlas\n\t\tconst frame_offset = frame.offset;\n\t\tconst g_offset = this.offset;\n\n\t\t// remove image's TexturePacker/ShoeBox rotation\n\t\tif (frame.angle !== 0) {\n\t\t\trenderer.translate(-dx, -dy);\n\t\t\trenderer.rotate(frame.angle);\n\t\t\tdx -= h;\n\t\t\tw = frame.height;\n\t\t\th = frame.width;\n\t\t}\n\n\t\tconst sx = g_offset.x + frame_offset.x;\n\t\tconst sy = g_offset.y + frame_offset.y;\n\n\t\t// should this be configurable ?\n\t\tconst corner_width = this.insetx || w / 4;\n\t\tconst corner_height = this.insety || h / 4;\n\n\t\t// `* 2`, not `<< 1`: corners default to w/4 which is fractional for\n\t\t// widths not divisible by 4, and the bit-shift truncates to int32 —\n\t\t// desyncing the center-slice size from the un-truncated corner\n\t\t// offsets below (up to ~2px of seam/bleed on the stretched panel)\n\t\tconst image_center_width = w - corner_width * 2;\n\t\tconst image_center_height = h - corner_height * 2;\n\n\t\tconst target_center_width = this.nss_width - corner_width * 2;\n\t\tconst target_center_height = this.nss_height - corner_height * 2;\n\n\t\t// The 9-slice grid is separable per axis: each column/row is an unscaled\n\t\t// corner, a stretched center, then an unscaled corner. Fill the shared\n\t\t// scratch with the per-axis source/dest offsets (no per-draw allocation).\n\t\tsColX[0] = sx;\n\t\tsColX[1] = sx + corner_width;\n\t\tsColX[2] = sx + w - corner_width;\n\t\tdColX[0] = dx;\n\t\tdColX[1] = dx + corner_width;\n\t\tdColX[2] = dx + this.nss_width - corner_width;\n\t\tsRowY[0] = sy;\n\t\tsRowY[1] = sy + corner_height;\n\t\tsRowY[2] = sy + h - corner_height;\n\t\tdRowY[0] = dy;\n\t\tdRowY[1] = dy + corner_height;\n\t\tdRowY[2] = dy + this.nss_height - corner_height;\n\n\t\t// blit the 9 slices: corners keep their size, the center column/row stretch.\n\t\tfor (let r = 0; r < 3; r++) {\n\t\t\tconst sh = r === 1 ? image_center_height : corner_height;\n\t\t\tconst dh = r === 1 ? target_center_height : corner_height;\n\t\t\tfor (let c = 0; c < 3; c++) {\n\t\t\t\tconst sw = c === 1 ? image_center_width : corner_width;\n\t\t\t\tconst dw = c === 1 ? target_center_width : corner_width;\n\t\t\t\trenderer.drawImage(\n\t\t\t\t\tthis.image,\n\t\t\t\t\tsColX[c], // sx\n\t\t\t\t\tsRowY[r], // sy\n\t\t\t\t\tsw, // sw\n\t\t\t\t\tsh, // sh\n\t\t\t\t\tdColX[c], // dx\n\t\t\t\t\tdRowY[r], // dy\n\t\t\t\t\tdw, // dw\n\t\t\t\t\tdh, // dh\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","import FadeEffect from \"../camera/effects/fade_effect.ts\";\nimport MaskEffect from \"../camera/effects/mask_effect.ts\";\nimport { polygonPool } from \"../geometries/polygon.ts\";\nimport { level } from \"./../level/level.js\";\nimport { vector2dPool } from \"../math/vector2d.ts\";\nimport { boundsPool } from \"./../physics/bounds.ts\";\nimport { collision } from \"./../physics/collision.js\";\nimport Renderable from \"./renderable.js\";\n\n/**\n * additional import for TypeScript\n * @import ResponseObject from \"./../physics/response.js\";\n */\n\n/**\n * Trigger an event when colliding with another object.\n * Supports both fade and mask-based transitions when loading a new level.\n * @category Game Objects\n */\nexport default class Trigger extends Renderable {\n\t/**\n\t * @param {number} x - the x coordinates of the trigger area\n\t * @param {number} y - the y coordinates of the trigger area\n\t * @param {Object} settings - trigger settings\n\t * @param {number} [settings.width] - width of the trigger area\n\t * @param {number} [settings.height] - height of the trigger area\n\t * @param {Rect[]|Polygon[]|Line[]|Ellipse[]} [settings.shapes] - collision shape(s) that will trigger the event\n\t * @param {number} [settings.duration] - Transition duration (in ms)\n\t * @param {string|Color} [settings.color] - Transition color (also accepts legacy `fade` property)\n\t * @param {string} [settings.transition=\"fade\"] - Transition type: \"fade\" for a color fade, \"mask\" for a shape-based mask transition\n\t * @param {Ellipse|Polygon} [settings.shape] - Mask shape for \"mask\" transition type (e.g. an Ellipse for iris, a Polygon for diamond/star)\n\t * @param {string} [settings.event=\"level\"] - the type of event to trigger (only \"level\" supported for now)\n\t * @param {string} [settings.to] - level to load if level trigger\n\t * @param {string|Container} [settings.container] - Target container. See {@link level.load}\n\t * @param {Function} [settings.onLoaded] - Level loaded callback. See {@link level.load}\n\t * @param {boolean} [settings.flatten] - Flatten all objects into the target container. See {@link level.load}\n\t * @param {boolean} [settings.setViewportBounds] - Resize the viewport to match the level. See {@link level.load}\n\t * @example\n\t * // fade transition (default)\n\t * world.addChild(new Trigger(x, y, {\n\t *     shapes: [new Rect(0, 0, 100, 100)],\n\t *     color: \"#000\",\n\t *     duration: 250,\n\t *     to: \"mymap2\",\n\t * }));\n\t * @example\n\t * // mask transition with iris (ellipse) shape\n\t * world.addChild(new Trigger(x, y, {\n\t *     shapes: [new Rect(0, 0, 100, 100)],\n\t *     transition: \"mask\",\n\t *     shape: new Ellipse(0, 0, 1, 1),\n\t *     color: \"#000\",\n\t *     duration: 500,\n\t *     to: \"mymap2\",\n\t * }));\n\t * @example\n\t * // mask transition with diamond polygon\n\t * world.addChild(new Trigger(x, y, {\n\t *     shapes: [new Rect(0, 0, 100, 100)],\n\t *     transition: \"mask\",\n\t *     shape: new Polygon(0, 0, [\n\t *         { x: 0, y: -1 }, { x: 1, y: 0 },\n\t *         { x: 0, y: 1 }, { x: -1, y: 0 },\n\t *     ]),\n\t *     color: \"#000\",\n\t *     duration: 400,\n\t *     to: \"mymap2\",\n\t * }));\n\t */\n\tconstructor(x, y, settings) {\n\t\tsuper(x, y, settings.width || 0, settings.height || 0);\n\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\tthis.color = settings.color || settings.fade;\n\t\tthis.duration = settings.duration;\n\t\tthis.transition = settings.transition || \"fade\";\n\t\tthis.transitionShape = settings.shape;\n\t\tthis.fading = false;\n\n\t\t// Tiled settings\n\t\tthis.name = \"Trigger\";\n\t\tthis.type = settings.type;\n\t\tthis.id = settings.id;\n\t\tthis.gotolevel = settings.to;\n\n\t\t// collect the defined trigger settings\n\t\tthis.triggerSettings = {\n\t\t\tevent: \"level\",\n\t\t};\n\n\t\tfor (const property of [\n\t\t\t\"type\",\n\t\t\t\"container\",\n\t\t\t\"onLoaded\",\n\t\t\t\"flatten\",\n\t\t\t\"setViewportBounds\",\n\t\t\t\"to\",\n\t\t]) {\n\t\t\tif (typeof settings[property] !== \"undefined\") {\n\t\t\t\tthis.triggerSettings[property] = settings[property];\n\t\t\t}\n\t\t}\n\n\t\t// add and configure the physic body\n\t\tlet shape = settings.shapes;\n\t\tif (typeof shape === \"undefined\") {\n\t\t\tshape = polygonPool.get(0, 0, [\n\t\t\t\tvector2dPool.get(0, 0),\n\t\t\t\tvector2dPool.get(this.width, 0),\n\t\t\t\tvector2dPool.get(this.width, this.height),\n\t\t\t]);\n\t\t}\n\t\t// declarative body — adapter-portable, auto-registered on addChild.\n\t\t// `isSensor: true` makes the trigger explicitly a detection-only\n\t\t// volume. Under the builtin adapter this is a no-op for triggers\n\t\t// (the SAT detector already skipped push-out because Trigger\n\t\t// doesn't define `onCollision`); under Matter it is required —\n\t\t// matter's solver always resolves contacts unless the body is\n\t\t// flagged as a sensor.\n\t\tthis.bodyDef = {\n\t\t\ttype: \"static\",\n\t\t\tshapes: Array.isArray(shape) ? shape : [shape],\n\t\t\tcollisionType: collision.types.ACTION_OBJECT,\n\t\t\tcollisionMask: collision.types.PLAYER_OBJECT,\n\t\t\tisSensor: true,\n\t\t};\n\t\t// Size from the union of all shapes' bounds (the body itself\n\t\t// isn't constructed until auto-registration runs on addChild).\n\t\tconst bounds = boundsPool.get();\n\t\tbounds.addShapes(this.bodyDef.shapes, true);\n\t\tthis.resize(bounds.width, bounds.height);\n\t\tboundsPool.release(bounds);\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\tgetTriggerSettings() {\n\t\tconst world = this.ancestor.getRootAncestor();\n\t\tif (typeof this.triggerSettings.container === \"string\") {\n\t\t\tthis.triggerSettings.container = world.getChildByName(\n\t\t\t\tthis.triggerSettings.container,\n\t\t\t)[0];\n\t\t}\n\t\treturn this.triggerSettings;\n\t}\n\n\t/**\n\t * Trigger this event. Override in subclasses to customize behavior.\n\t * @protected\n\t */\n\ttriggerEvent() {\n\t\tconst triggerSettings = this.getTriggerSettings();\n\t\tconst world = this.ancestor.getRootAncestor();\n\t\tconst app = world.app;\n\t\tconst viewport = app.viewport;\n\n\t\tif (triggerSettings.event === \"level\") {\n\t\t\tthis.gotolevel = triggerSettings.to;\n\t\t\tif (this.color && this.duration) {\n\t\t\t\tif (!this.fading) {\n\t\t\t\t\tthis.fading = true;\n\t\t\t\t\tconst gotolevel = this.gotolevel;\n\t\t\t\t\tconst settings = this.getTriggerSettings();\n\t\t\t\t\tconst color = this.color;\n\t\t\t\t\tconst duration = this.duration;\n\t\t\t\t\tconst useMask = this.transition === \"mask\" && this.transitionShape;\n\t\t\t\t\tconst shape = this.transitionShape;\n\n\t\t\t\t\t// wrap the user's onLoaded to add the reveal effect\n\t\t\t\t\tconst userOnLoaded = settings.onLoaded;\n\t\t\t\t\tsettings.onLoaded = function (levelId) {\n\t\t\t\t\t\t// re-read viewport after game.reset reassigns it\n\t\t\t\t\t\tconst vp = app.viewport;\n\t\t\t\t\t\t// reveal effect (same type as hide)\n\t\t\t\t\t\tif (useMask) {\n\t\t\t\t\t\t\tvp.addCameraEffect(\n\t\t\t\t\t\t\t\tnew MaskEffect(vp, {\n\t\t\t\t\t\t\t\t\tshape,\n\t\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\t\tdirection: \"reveal\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvp.addCameraEffect(\n\t\t\t\t\t\t\t\tnew FadeEffect(vp, {\n\t\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\t\tdirection: \"out\",\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// call the user's onLoaded if any\n\t\t\t\t\t\tif (typeof userOnLoaded === \"function\") {\n\t\t\t\t\t\t\tuserOnLoaded.call(this, levelId);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\tconst onComplete = () => {\n\t\t\t\t\t\tlevel.load(gotolevel, settings);\n\t\t\t\t\t};\n\n\t\t\t\t\t// hide effect, then load level + reveal\n\t\t\t\t\tif (useMask) {\n\t\t\t\t\t\tviewport.addCameraEffect(\n\t\t\t\t\t\t\tnew MaskEffect(viewport, {\n\t\t\t\t\t\t\t\tshape,\n\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\tdirection: \"hide\",\n\t\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tviewport.addCameraEffect(\n\t\t\t\t\t\t\tnew FadeEffect(viewport, {\n\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t\tduration,\n\t\t\t\t\t\t\t\tdirection: \"in\",\n\t\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlevel.load(this.gotolevel, triggerSettings);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(\"Trigger invalid type\");\n\t\t}\n\t}\n\n\t/**\n\t * Fire the trigger when an entity first enters the trigger zone.\n\t * Using `onCollisionStart` rather than `onCollision` so the event\n\t * runs exactly once per entry — under the legacy alias this would\n\t * fire every frame the entity was inside the trigger.\n\t */\n\tonCollisionStart() {\n\t\tif (this.name === \"Trigger\") {\n\t\t\tthis.triggerEvent();\n\t\t}\n\t}\n}\n","/**\n * current melonJS version\n */\nexport const version = __VERSION__;\n","import { initKeyboardEvent } from \"../input/keyboard.ts\";\nimport { registerBuiltinTiledClass } from \"../level/tiled/TMXObjectFactory.js\";\nimport Light2d from \"../lighting/light2d.ts\";\nimport { setNocache } from \"../loader/loader.js\";\nimport Particle from \"../particles/particle.ts\";\nimport Collectable from \"../renderable/collectable.js\";\nimport ColorLayer from \"../renderable/colorlayer.js\";\nimport Entity from \"../renderable/entity/entity.js\";\nimport ImageLayer from \"../renderable/imagelayer.js\";\nimport NineSliceSprite from \"../renderable/nineslicesprite.js\";\nimport Renderable from \"../renderable/renderable.js\";\nimport Sprite from \"../renderable/sprite.js\";\nimport BitmapText from \"../renderable/text/bitmaptext.js\";\nimport Text from \"../renderable/text/text.js\";\nimport Trigger from \"../renderable/trigger.js\";\nimport Tween from \"../tweens/tween.ts\";\nimport { getUriFragment } from \"../utils/utils.ts\";\nimport { version } from \"../version.ts\";\nimport { initVisibilityEvents } from \"./device.js\";\nimport { BOOT, DOM_READY, emit } from \"./event.ts\";\nimport pool from \"./legacy_pool.js\";\n\n/**\n * a flag indicating that melonJS is fully initialized\n */\nexport let initialized = false;\n\n/**\n * Initialize the melonJS library.\n * This is called automatically by the {@link Application} constructor.\n * Multiple calls are safe — boot() is idempotent.\n * When using {@link Application} directly, calling boot() manually is not needed.\n * @see {@link Application}\n */\nexport function boot() {\n\t// don't do anything if already initialized (should not happen anyway)\n\tif (initialized) {\n\t\treturn;\n\t}\n\n\t// output melonJS version in the console\n\tconsole.log(`melonJS 2 (v${version}) | http://melonjs.org`);\n\n\t// register all built-ins objects into the object legacy pool\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\tpool.register(\"Entity\", Entity);\n\tpool.register(\"Collectable\", Collectable);\n\tpool.register(\"Trigger\", Trigger);\n\tpool.register(\"Light2d\", Light2d);\n\tpool.register(\"Particle\", Particle, true);\n\tpool.register(\"Sprite\", Sprite);\n\tpool.register(\"NineSliceSprite\", NineSliceSprite);\n\tpool.register(\"Renderable\", Renderable);\n\tpool.register(\"Text\", Text, true);\n\tpool.register(\"BitmapText\", BitmapText);\n\tpool.register(\"ImageLayer\", ImageLayer);\n\tpool.register(\"Tween\", Tween, true);\n\tpool.register(\"ColorLayer\", ColorLayer, true);\n\n\t// ensure built-in classes are registered as Tiled object factories\n\t// (redundant with pool.register auto-registration, but ensures\n\t// built-ins remain available if pool behavior changes in the future)\n\tregisterBuiltinTiledClass(\"Renderable\", Renderable);\n\tregisterBuiltinTiledClass(\"Text\", Text);\n\tregisterBuiltinTiledClass(\"BitmapText\", BitmapText);\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\tregisterBuiltinTiledClass(\"Entity\", Entity);\n\tregisterBuiltinTiledClass(\"Collectable\", Collectable);\n\tregisterBuiltinTiledClass(\"Trigger\", Trigger);\n\tregisterBuiltinTiledClass(\"Light2d\", Light2d);\n\tregisterBuiltinTiledClass(\"Sprite\", Sprite);\n\tregisterBuiltinTiledClass(\"NineSliceSprite\", NineSliceSprite);\n\tregisterBuiltinTiledClass(\"ImageLayer\", ImageLayer);\n\tregisterBuiltinTiledClass(\"ColorLayer\", ColorLayer);\n\n\t// publish Boot notification\n\temit(BOOT);\n\n\t// enable/disable the cache\n\tsetNocache(!!getUriFragment().nocache);\n\n\t// automatically enable keyboard events\n\tinitKeyboardEvent();\n\n\t// register blur/focus and visibility change handlers\n\tinitVisibilityEvents();\n\n\t// mark melonJS as initialized\n\tinitialized = true;\n\n\t// notify that the engine is ready\n\temit(DOM_READY);\n}\n","import { isWebGLSupported } from \"../../system/device\";\nimport CanvasRenderer from \"../canvas/canvas_renderer\";\nimport WebGLRenderer from \"../webgl/webgl_renderer\";\n\n// Ordered SYNCHRONOUS backend candidates, walked front-to-back: the first\n// one whose support probe passes and whose construction succeeds wins.\n//\n// WebGPU is not in this list even though `AUTO` now prefers it — its\n// support can only be proven by negotiating an adapter/device, which is\n// asynchronous. `Application.init()`'s AUTO case awaits that attempt first\n// and only falls through to these candidates when it rejects, keeping this\n// module the synchronous tail of the ladder.\nconst BACKEND_CANDIDATES = [\n\t{\n\t\tname: \"webgl2\",\n\t\tisSupported: (options) => {\n\t\t\treturn isWebGLSupported(options);\n\t\t},\n\t\tcreate: (options) => {\n\t\t\treturn new WebGLRenderer(options);\n\t\t},\n\t},\n\t{\n\t\t// Canvas is the terminal fallback and always supported\n\t\tname: \"canvas\",\n\t\tisSupported: () => {\n\t\t\treturn true;\n\t\t},\n\t\tcreate: (options) => {\n\t\t\treturn new CanvasRenderer(options);\n\t\t},\n\t},\n];\n\n/**\n * Auto-detect the best renderer to use\n * @ignore\n */\nexport function autoDetectRenderer(options) {\n\tlet lastError;\n\tfor (const backend of BACKEND_CANDIDATES) {\n\t\ttry {\n\t\t\tif (backend.isSupported(options)) {\n\t\t\t\treturn backend.create(options);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlastError = e;\n\t\t\tconsole.log(\"Error creating \" + backend.name + \" renderer: \" + e.message);\n\t\t}\n\t}\n\t// every candidate failed — surface the terminal one's cause rather than\n\t// swallowing it behind a generic message\n\tthrow new Error(\"No usable renderer backend found\", { cause: lastError });\n}\n","import RenderTarget from \"./rendertarget.ts\";\n\n/**\n * WebGPU offscreen render target — the WebGPU counterpart of the WebGL\n * FBO-backed {@link WebGLRenderTarget}, used by the post-effect chain\n * through the shared {@link RenderTargetPool}.\n *\n * Owns one color `GPUTexture` (renderable AND sampleable — the whole point\n * of a post-effect target) in the renderer's preferred canvas format, so\n * pipelines keyed on that format serve canvas and offscreen passes alike.\n * The depth-stencil attachment is NOT per-target: every pass shares the\n * renderer's canvas-sized depth-stencil texture (targets in this flow are\n * canvas-sized, only one pass is open at a time — and sharing the stencil\n * means a mask active around a post-effect renderable keeps clipping its\n * offscreen content, which is what masks promise).\n *\n * Under the recording model \"binding\" a target is a pass break: draws\n * recorded after {@link WebGPURenderer#setRenderTarget} land in a new pass\n * whose color attachment is this texture. Mid-frame resize/destroy retire\n * the texture (a texture referenced by recorded draws must not be\n * destroyed before submit).\n * @augments RenderTarget\n * @category Rendering\n */\nexport default class WebGPURenderTarget extends RenderTarget {\n\t/**\n\t * @param {import(\"../webgpu/webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @param {number} width - width in pixels\n\t * @param {number} height - height in pixels\n\t */\n\tconstructor(renderer, width, height, options = {}) {\n\t\tsuper();\n\t\tthis.renderer = renderer;\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t\t/**\n\t\t * MSAA sample count for scene rasterization into this target\n\t\t * (1 = single-sampled). When > 1 the target owns a multisampled\n\t\t * color texture that passes rasterize into and resolve into\n\t\t * `texture` at every pass end — so anything sampling the target\n\t\t * always sees resolved pixels.\n\t\t * @type {number}\n\t\t */\n\t\tthis.sampleCount = options.sampleCount ?? 1;\n\t\t/** @type {GPUTexture|null} */\n\t\tthis.texture = null;\n\t\t/** @type {GPUTextureView|null} */\n\t\tthis.colorView = null;\n\t\t/** the multisampled render half, when sampleCount > 1 @type {GPUTexture|null} */\n\t\tthis.msaaTexture = null;\n\t\t/** @type {GPUTextureView|null} */\n\t\tthis.msaaView = null;\n\t\t/**\n\t\t * bumped whenever the backing texture is reallocated — cached bind\n\t\t * groups referencing the old view key on this\n\t\t * @type {number}\n\t\t */\n\t\tthis.generation = 0;\n\t\t// lazy (view + linear clamp sampler) pairing against the material\n\t\t// layout — what a blit binds at group 1 to sample this target\n\t\tthis.materialBindGroup = null;\n\t\tthis.materialBindGroupGeneration = -1;\n\t\t// consumed by the renderer as a colorLoadOp \"clear\" on next retarget\n\t\tthis.pendingClear = false;\n\n\t\tthis.resize(width, height);\n\t}\n\n\t/**\n\t * (Re)create the backing texture at the given size. No-op when the size\n\t * is unchanged; otherwise the old texture retires (mid-frame safe) and\n\t * `generation` advances so stale bind groups rebuild.\n\t * @param {number} width - new width in pixels\n\t * @param {number} height - new height in pixels\n\t */\n\tresize(width, height) {\n\t\twidth = Math.max(1, width | 0);\n\t\theight = Math.max(1, height | 0);\n\t\tif (this.width === width && this.height === height) {\n\t\t\treturn;\n\t\t}\n\t\tif (this.texture !== null) {\n\t\t\tthis.renderer.retireTexture(this.texture);\n\t\t}\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.texture = this.renderer.device.createTexture({\n\t\t\tlabel: \"melonJS render target\",\n\t\t\tsize: [width, height],\n\t\t\tformat: this.renderer.preferredFormat,\n\t\t\tusage:\n\t\t\t\tGPUTextureUsage.RENDER_ATTACHMENT |\n\t\t\t\tGPUTextureUsage.TEXTURE_BINDING |\n\t\t\t\tGPUTextureUsage.COPY_SRC,\n\t\t});\n\t\tthis.colorView = this.texture.createView();\n\t\tif (this.sampleCount > 1) {\n\t\t\tif (this.msaaTexture !== null) {\n\t\t\t\tthis.renderer.retireTexture(this.msaaTexture);\n\t\t\t}\n\t\t\t// per-target (not the renderer's shared canvas MSAA texture):\n\t\t\t// nested effect brackets interleave passes on DIFFERENT targets,\n\t\t\t// and a mid-frame pass restart loads the stored samples back —\n\t\t\t// sharing one multisampled texture would load another target's\n\t\t\tthis.msaaTexture = this.renderer.device.createTexture({\n\t\t\t\tlabel: \"melonJS render target msaa\",\n\t\t\t\tsize: [width, height],\n\t\t\t\tsampleCount: this.sampleCount,\n\t\t\t\tformat: this.renderer.preferredFormat,\n\t\t\t\tusage: GPUTextureUsage.RENDER_ATTACHMENT,\n\t\t\t});\n\t\t\tthis.msaaView = this.msaaTexture.createView();\n\t\t}\n\t\tthis.generation++;\n\t}\n\n\t/**\n\t * Request a clear on next use: consumed as the pass's `colorLoadOp:\n\t * \"clear\"` when the renderer next targets this — cheaper than a clearing\n\t * draw, and the WebGPU analogue of `clearRenderTarget`.\n\t */\n\tclear() {\n\t\tthis.pendingClear = true;\n\t}\n\n\t/**\n\t * make this target the active draw destination (a pass break under the\n\t * recording model)\n\t * @override\n\t */\n\tbind() {\n\t\tthis.renderer.setRenderTarget(this);\n\t}\n\n\t/**\n\t * restore the canvas as the draw destination\n\t * @override\n\t */\n\tunbind() {\n\t\tthis.renderer.setRenderTarget(null);\n\t}\n\n\t/**\n\t * The group-1 (material) bind group sampling this target with a linear\n\t * clamp sampler — what the effect blit binds as its source. Rebuilt\n\t * lazily when the backing texture was reallocated.\n\t * @returns {GPUBindGroup} the bind group\n\t * @ignore\n\t */\n\tgetMaterialBindGroup() {\n\t\tif (\n\t\t\tthis.materialBindGroup === null ||\n\t\t\tthis.materialBindGroupGeneration !== this.generation\n\t\t) {\n\t\t\tconst renderer = this.renderer;\n\t\t\tthis.materialBindGroup = renderer.device.createBindGroup({\n\t\t\t\tlabel: \"melonJS render-target material\",\n\t\t\t\tlayout: renderer.pipelineCache.materialLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{ binding: 0, resource: this.colorView },\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: renderer.textureStore.getSampler(\"linear\", \"no-repeat\"),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tthis.materialBindGroupGeneration = this.generation;\n\t\t}\n\t\treturn this.materialBindGroup;\n\t}\n\n\t/**\n\t * Synchronous readback is impossible under WebGPU — use\n\t * {@link WebGPURenderTarget#readPixels}.\n\t * @throws {Error} always\n\t * @override\n\t */\n\tgetImageData() {\n\t\tthrow new Error(\n\t\t\t\"WebGPURenderTarget.getImageData: WebGPU readback is asynchronous — use `await target.readPixels()` instead\",\n\t\t);\n\t}\n\n\t/**\n\t * Asynchronously read back this target's pixels.\n\t * @param {number} [x=0] - x of the top-left corner\n\t * @param {number} [y=0] - y of the top-left corner\n\t * @param {number} [width=this.width] - width of the area to read\n\t * @param {number} [height=this.height] - height of the area to read\n\t * @returns {Promise<ImageData>} the pixel data (RGBA order)\n\t */\n\tasync readPixels(x = 0, y = 0, width = this.width, height = this.height) {\n\t\tconst renderer = this.renderer;\n\t\tconst device = renderer.device;\n\t\t// clamp the read window to the target (an out-of-bounds copy is a\n\t\t// validation error, where a caller passing only x/y expects a crop)\n\t\tx = Math.max(0, x | 0);\n\t\ty = Math.max(0, y | 0);\n\t\twidth = Math.max(1, Math.min(width | 0, this.width - x));\n\t\theight = Math.max(1, Math.min(height | 0, this.height - y));\n\t\t// bytesPerRow must be a multiple of 256\n\t\tconst bytesPerRow = (width * 4 + 255) & ~255;\n\t\tconst buffer = device.createBuffer({\n\t\t\tlabel: \"melonJS readback\",\n\t\t\tsize: bytesPerRow * height,\n\t\t\tusage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,\n\t\t});\n\t\ttry {\n\t\t\t// pending draws into this target must land before the copy\n\t\t\trenderer.flush();\n\t\t\tconst encoder = device.createCommandEncoder({\n\t\t\t\tlabel: \"melonJS readback\",\n\t\t\t});\n\t\t\tencoder.copyTextureToBuffer(\n\t\t\t\t{ texture: this.texture, origin: { x, y } },\n\t\t\t\t{ buffer, bytesPerRow },\n\t\t\t\t[width, height],\n\t\t\t);\n\t\t\tdevice.queue.submit([encoder.finish()]);\n\t\t\tawait buffer.mapAsync(GPUMapMode.READ);\n\t\t\tconst mapped = new Uint8Array(buffer.getMappedRange());\n\t\t\tconst out = new Uint8ClampedArray(width * height * 4);\n\t\t\tconst bgra = renderer.preferredFormat.startsWith(\"bgra\");\n\t\t\tfor (let row = 0; row < height; row++) {\n\t\t\t\tconst src = row * bytesPerRow;\n\t\t\t\tconst dst = row * width * 4;\n\t\t\t\tfor (let px = 0; px < width; px++) {\n\t\t\t\t\tconst s = src + px * 4;\n\t\t\t\t\tconst d = dst + px * 4;\n\t\t\t\t\tif (bgra) {\n\t\t\t\t\t\tout[d] = mapped[s + 2];\n\t\t\t\t\t\tout[d + 1] = mapped[s + 1];\n\t\t\t\t\t\tout[d + 2] = mapped[s];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tout[d] = mapped[s];\n\t\t\t\t\t\tout[d + 1] = mapped[s + 1];\n\t\t\t\t\t\tout[d + 2] = mapped[s + 2];\n\t\t\t\t\t}\n\t\t\t\t\tout[d + 3] = mapped[s + 3];\n\t\t\t\t}\n\t\t\t}\n\t\t\tbuffer.unmap();\n\t\t\treturn new ImageData(out, width, height);\n\t\t} finally {\n\t\t\tbuffer.destroy();\n\t\t}\n\t}\n\n\t/**\n\t * Release the backing texture (retired when a frame is recording).\n\t * @override\n\t */\n\tdestroy() {\n\t\tif (this.texture !== null) {\n\t\t\tthis.renderer.retireTexture(this.texture);\n\t\t\tthis.texture = null;\n\t\t\tthis.colorView = null;\n\t\t}\n\t\tif (this.msaaTexture !== null) {\n\t\t\tthis.renderer.retireTexture(this.msaaTexture);\n\t\t\tthis.msaaTexture = null;\n\t\t\tthis.msaaView = null;\n\t\t}\n\t\tthis.materialBindGroup = null;\n\t\tthis.width = 0;\n\t\tthis.height = 0;\n\t}\n}\n","import {\n\tINSTANCE_ATTRIBUTE_NAMES,\n\tINSTANCE_SLOT_OFFSETS,\n} from \"../../gpu/instancerecord.ts\";\n\n/**\n * Derive an instanced mesh WGSL module from the ordinary one (#1508).\n *\n * WGSL has no preprocessor, and a module carries **both** entry points in\n * one file — so an instanced variant cannot be a second vertex file the way\n * it is on the WebGL side. Rather than copy the module (which for the lit\n * tier would duplicate the whole lighting loop, and the `Light3dBlock`\n * layout that must agree byte-for-byte with the uniform packer), this\n * rewrites the shared source:\n *\n * - everything before `@vertex` is kept — structs, bindings, `VSOut` —\n *   with one extra inter-stage field when the custom slot is in play;\n * - the vertex stage is replaced with one that reads the per-instance\n *   attributes and applies them;\n * - the fragment stage is kept **verbatim**, apart from one substitution\n *   that folds the per-instance slot into the emissive term.\n *\n * So the lighting loop, the light block and the alpha-cutout rule exist in\n * exactly one place, and an instanced mesh shades identically to its\n * uninstanced twin by construction rather than by review.\n *\n * Shader locations for the instance slots are **pinned**\n * (`INSTANCE_SLOT_OFFSETS`), so a variant that omits the colour slot does\n * not renumber the data slot.\n * @param {string} source - the ordinary module (`mesh.wgsl` / `mesh-lit.wgsl`)\n * @param {object} options - variant options\n * @param {number} options.baseLocation - first vertex location after the tier's geometry attributes\n * @param {number} options.varyingLocation - first free inter-stage location in `VSOut`\n * @param {boolean} options.hasColor - whether the per-instance colour slot is present\n * @param {boolean} options.hasData - whether the per-instance custom slot is present\n * @param {string} options.geometryInputs - the tier's geometry attribute declarations\n * @param {string} options.body - the tier's placement body, which reads the `instance` matrix the generated preamble declares and must leave a `clip` value for the shared tail\n * @returns {string} the instanced module text\n * @ignore\n */\nexport function buildInstancedMeshWGSL(source, options) {\n\tconst {\n\t\tbaseLocation,\n\t\tvaryingLocation,\n\t\thasColor,\n\t\thasData,\n\t\tgeometryInputs,\n\t\tbody,\n\t} = options;\n\n\t// match the entry points at the START of a line: `@vertex` mentioned in a\n\t// doc comment would otherwise slice the module in the wrong place\n\tconst vertexAt = source.search(/^@vertex\\b/m);\n\tconst fragmentAt = source.search(/^@fragment\\b/m);\n\tif (vertexAt < 0 || fragmentAt < 0 || fragmentAt < vertexAt) {\n\t\tthrow new Error(\n\t\t\t\"buildInstancedMeshWGSL: the source module must declare both stages, vertex first\",\n\t\t);\n\t}\n\n\tlet head = source.slice(0, vertexAt);\n\tlet fragment = source.slice(fragmentAt);\n\n\t// the custom slot travels to the fragment stage as one more varying, and\n\t// is folded into the term the fragment already adds after shading\n\tif (hasData) {\n\t\thead = head.replace(\n\t\t\t/(struct VSOut \\{[\\s\\S]*?)\\n\\};/,\n\t\t\t`$1\\n\\t// per-instance custom slot; this built-in shading reads its\\n\\t// rgb as emissive (a custom module may read it as anything)\\n\\t@location(${varyingLocation}) vInstanceData : vec4f,\\n};`,\n\t\t);\n\t\tconst emissive = \"uMesh.emissive.rgb\";\n\t\tif (!fragment.includes(emissive)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"buildInstancedMeshWGSL: the fragment stage no longer reads `uMesh.emissive.rgb` — the per-instance emissive substitution needs updating\",\n\t\t\t);\n\t\t}\n\t\tfragment = fragment.replaceAll(\n\t\t\temissive,\n\t\t\t\"(uMesh.emissive.rgb + in.vInstanceData.rgb)\",\n\t\t);\n\t}\n\n\tconst rows = INSTANCE_ATTRIBUTE_NAMES.rows;\n\tconst inputs = [geometryInputs];\n\trows.forEach((name, i) => {\n\t\tinputs.push(\n\t\t\t`\\t@location(${baseLocation + INSTANCE_SLOT_OFFSETS.rows[i]}) ${name} : vec4f,`,\n\t\t);\n\t});\n\tif (hasColor) {\n\t\tinputs.push(\n\t\t\t`\\t@location(${baseLocation + INSTANCE_SLOT_OFFSETS.color}) ${INSTANCE_ATTRIBUTE_NAMES.color} : vec4f,`,\n\t\t);\n\t}\n\tif (hasData) {\n\t\tinputs.push(\n\t\t\t`\\t@location(${baseLocation + INSTANCE_SLOT_OFFSETS.data}) ${INSTANCE_ATTRIBUTE_NAMES.data} : vec4f,`,\n\t\t);\n\t}\n\n\tconst vertex = [\n\t\t\"@vertex\",\n\t\t\"fn vertex_main(\",\n\t\tinputs.join(\"\\n\"),\n\t\t\") -> VSOut {\",\n\t\t\"\\tvar out : VSOut;\",\n\t\t\"\\t// The instance transform arrives as three ROW-major vec4 rows, not a\",\n\t\t\"\\t// full mat4: the bottom row of an affine matrix is always (0,0,0,1),\",\n\t\t\"\\t// so storing it would waste 16 bytes and an attribute slot per\",\n\t\t\"\\t// instance. mat4x4f takes COLUMNS, hence the transpose.\",\n\t\t\"\\tlet instance = mat4x4f(\",\n\t\t`\\t\\tvec4f(${rows[0]}.x, ${rows[1]}.x, ${rows[2]}.x, 0.0),`,\n\t\t`\\t\\tvec4f(${rows[0]}.y, ${rows[1]}.y, ${rows[2]}.y, 0.0),`,\n\t\t`\\t\\tvec4f(${rows[0]}.z, ${rows[1]}.z, ${rows[2]}.z, 0.0),`,\n\t\t`\\t\\tvec4f(${rows[0]}.w, ${rows[1]}.w, ${rows[2]}.w, 1.0));`,\n\t\tbody,\n\t\t\"\\t// GL-convention clip z in [-w, w] remapped to WebGPU's [0, w]\",\n\t\t\"\\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\",\n\t\thasColor\n\t\t\t? `\\tlet tinted = aColor * uMesh.tint * ${INSTANCE_ATTRIBUTE_NAMES.color};`\n\t\t\t: \"\\tlet tinted = aColor * uMesh.tint;\",\n\t\t\"\\t// tint first, then premultiply — matches the fragment's expectation\",\n\t\t\"\\tout.vColor = vec4f(tinted.rgb * tinted.a, tinted.a);\",\n\t\t\"\\tout.vRegion = aRegion;\",\n\t\thasData ? `\\tout.vInstanceData = ${INSTANCE_ATTRIBUTE_NAMES.data};` : null,\n\t\t\"\\treturn out;\",\n\t\t\"}\",\n\t\t\"\",\n\t\t\"\",\n\t]\n\t\t.filter((line) => {\n\t\t\treturn line !== null;\n\t\t})\n\t\t.join(\"\\n\");\n\n\tconst module = head + vertex + fragment;\n\n\t// The generated vertex stage hard-codes which VSOut members it writes, so\n\t// a member added to the shared source would silently arrive as zeros in\n\t// the fragment stage (WGSL zero-initializes `var out : VSOut`). Fail\n\t// instead — every declared member must be assigned.\n\tconst struct = /struct VSOut \\{([\\s\\S]*?)\\n\\};/.exec(head);\n\tif (struct !== null) {\n\t\tconst generated = module.slice(module.search(/^@vertex\\b/m));\n\t\tfor (const [, member] of struct[1].matchAll(\n\t\t\t/@location\\(\\d+\\)\\s+(\\w+)\\s*:/g,\n\t\t)) {\n\t\t\tif (!generated.includes(`out.${member}`)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`buildInstancedMeshWGSL: VSOut member \"${member}\" is never written by the generated vertex stage — the derived module has drifted from its source`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// and no two members may share an inter-stage location\n\t\tconst locations = [...struct[1].matchAll(/@location\\((\\d+)\\)/g)].map(\n\t\t\t(m) => {\n\t\t\t\treturn m[1];\n\t\t\t},\n\t\t);\n\t\tif (new Set(locations).size !== locations.length) {\n\t\t\tthrow new Error(\n\t\t\t\t\"buildInstancedMeshWGSL: duplicate @location in the derived VSOut\",\n\t\t\t);\n\t\t}\n\t}\n\n\treturn module;\n}\n\n/** the unlit tier's geometry inputs and placement body @ignore */\nexport const UNLIT_INSTANCED = {\n\tbaseLocation: 3,\n\tvaryingLocation: 2,\n\tgeometryInputs: [\n\t\t\"\\t@location(0) aVertex : vec3f,\",\n\t\t\"\\t@location(1) aRegion : vec2f,\",\n\t\t\"\\t@location(2) aColor : vec4f,\",\n\t].join(\"\\n\"),\n\tbody: [\n\t\t\"\\tlet clip = uFrame.projection * uMesh.view * uMesh.model * instance\",\n\t\t\"\\t\\t* vec4f(aVertex, 1.0);\",\n\t].join(\"\\n\"),\n};\n\n/** the lit tier's geometry inputs, placement body and normal handling @ignore */\nexport const LIT_INSTANCED = {\n\tbaseLocation: 4,\n\tvaryingLocation: 4,\n\tgeometryInputs: [\n\t\t\"\\t@location(0) aVertex : vec3f,\",\n\t\t\"\\t@location(1) aRegion : vec2f,\",\n\t\t\"\\t@location(2) aColor : vec4f,\",\n\t\t\"\\t@location(3) aNormal : vec3f,\",\n\t].join(\"\\n\"),\n\tbody: [\n\t\t\"\\tlet worldPos = uMesh.model * instance * vec4f(aVertex, 1.0);\",\n\t\t\"\\tlet clip = uFrame.projection * uMesh.view * worldPos;\",\n\t\t\"\\tout.vWorldPos = worldPos.xyz;\",\n\t\t\"\\t// Rotate the normal through BOTH transforms, in the order the\",\n\t\t\"\\t// position takes them. Non-uniform scale is approximated exactly as\",\n\t\t\"\\t// the uninstanced path approximates it, so an instanced mesh shades\",\n\t\t\"\\t// like its uninstanced twin rather than subtly differently.\",\n\t\t\"\\tlet m = uMesh.model;\",\n\t\t\"\\tlet mi = mat3x3f(instance[0].xyz, instance[1].xyz, instance[2].xyz);\",\n\t\t\"\\tout.vNormal = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * mi * aNormal;\",\n\t].join(\"\\n\"),\n};\n","// melonJS WebGPU lit mesh shader — the WGSL port of mesh-lit.vert /\n// mesh-lit.frag: half-Lambert diffuse from directional Light3d lights plus\n// an ambient floor, over the mesh tier's uniform-driven placement.\n//\n// Vertex layout (48-byte stride, shared with the WebGL backend): the\n// 36-byte unlit layout plus\n//   @location(3) aNormal float32x3 offset 36\n// aNormal is MODEL-space on the retained path (rotated below by the model\n// matrix's upper 3×3) and world-space on the accumulated path — which\n// supplies an identity model matrix, so one shader serves both.\n//\n// The std140 Light3dBlock binds at group 2 with a dynamic offset — one\n// snapshot per light change per frame, per the queue-write ordering law.\n// Its layout is written by src/video/webgl/lighting/std140.ts and the two\n// must agree byte for byte — a mismatch does not fail validation, it\n// silently shifts every light.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block\n\tlineWidth : f32,\n};\n\nstruct MeshUniforms {\n\tmodel : mat4x4<f32>,\n\tview : mat4x4<f32>,\n\ttint : vec4f,\n\t// x = alpha cutout threshold (0 = disabled), y = 1 when an opacity map\n\t// is bound (0 = the second sampler is filler); z, w reserved\n\tparams : vec4f,\n\t// self-illumination added AFTER lighting (r, g, b; w reserved)\n\temissive : vec4f,\n\t// specular color (rgb) and exponent (w); w = 0 means no highlight\n\tspecular : vec4f,\n\t// the camera's world position (xyz; w reserved), for the specular\n\t// half-vector — nothing else in this shader needs it\n\teye : vec4f,\n};\n\n// One light, type inferred from sentinels (see std140.ts):\n// posRange.w < 0 → directional (dirCone.xyz = surface→light, normalized);\n// otherwise positional at posRange.xyz with quadratic falloff over\n// posRange.w, plus a spot cone when dirCone.w > -1 (dirCone.xyz = the\n// cone axis / travel direction, w = cos(outer), colorInner.w =\n// cos(inner)). colorInner.rgb is premultiplied by intensity.\nstruct Light3dData {\n\tposRange : vec4f,\n\tdirCone : vec4f,\n\tcolorInner : vec4f,\n};\n\n// header = count + pad (vec4), ambient rgb + pad (vec4), then 32 lights\n// of 3 vec4 each — 1568 bytes total (BLOCK3D_BYTES)\nstruct Light3dBlock {\n\tcountPad : vec4f,\n\tambient : vec4f,\n\tlights : array<Light3dData, 32>,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n// per-texel opacity (MTL map_d). When no map is bound this pair is filler —\n// the diffuse texture again — and `params.y` is 0, so its sample is weighted\n// away rather than branched around.\n@group(1) @binding(2) var uAlphaMap : texture_2d<f32>;\n@group(1) @binding(3) var uAlphaSampler : sampler;\n@group(2) @binding(0) var<uniform> uLights : Light3dBlock;\n@group(3) @binding(0) var<uniform> uMesh : MeshUniforms;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n\t@location(2) vNormal : vec3f,\n\t// world-space fragment position — positional lights (point / spot)\n\t// fall off with distance, so the fragment stage needs where the\n\t// surface IS\n\t@location(3) vWorldPos : vec3f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aNormal : vec3f,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet worldPos = uMesh.model * vec4f(aVertex, 1.0);\n\tlet clip = uFrame.projection * uMesh.view * worldPos;\n\t// GL-convention clip z in [-w, w] remapped to WebGPU's [0, w]\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tlet tinted = aColor * uMesh.tint;\n\tout.vColor = vec4f(tinted.rgb * tinted.a, tinted.a);\n\tout.vRegion = aRegion;\n\tout.vWorldPos = worldPos.xyz;\n\t// Rotate the normal into world space with the model matrix's upper\n\t// 3×3. Lighting is evaluated in world space, so the view transform is\n\t// deliberately excluded. Uniform scale cancels when the fragment\n\t// renormalizes; non-uniform scale is approximated — an exact result\n\t// would need the inverse-transpose.\n\tlet m = uMesh.model;\n\tout.vNormal = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz) * aNormal;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\t// sampled unconditionally, before the discard (uniform control flow)\n\tvar base = textureSample(uTexture, uSampler, in.vRegion) * in.vColor;\n\t// Per-texel opacity (MTL map_d), applied BEFORE the cutout so one\n\t// material can cut out to the shape of a leaf rather than at a single\n\t// threshold across the whole surface. Sampled unconditionally and\n\t// WEIGHTED: with no map bound the second pair is filler, and this keeps\n\t// the sample in uniform control flow (and identical to the GLSL twin).\n\tbase.a = base.a * mix(1.0, textureSample(uAlphaMap, uAlphaSampler, in.vRegion).r, uMesh.params.y);\n\t// hard alpha cutout (glTF alphaMode MASK) — discard before any shading\n\t// so cut-away texels cost nothing and never write depth\n\tif (base.a < uMesh.params.x) {\n\t\tdiscard;\n\t}\n\n\t// see mesh-lit.frag: a `lit` mesh with no usable normals must degrade to\n\t// unlit rather than normalize a zero vector to NaN and render black\n\tlet nLength = length(in.vNormal);\n\tif (nLength < 1e-6) {\n\t\treturn vec4f(base.rgb + uMesh.emissive.rgb, base.a);\n\t}\n\tlet n = in.vNormal / nLength;\n\tvar lit = uLights.ambient.rgb;\n\t// Blinn-Phong specular, accumulated alongside the diffuse term. Gated on\n\t// the exponent rather than the colour: `Ns` of 0 is the MTL format's \"no\n\t// highlight\", and exporters happily write a non-black `Ks` next to it.\n\tvar specular = vec3f(0.0);\n\tlet shininess = uMesh.specular.w;\n\tlet hasSpecular = shininess > 0.0;\n\tvar viewDir = vec3f(0.0);\n\tif (hasSpecular) {\n\t\tviewDir = normalize(uMesh.eye.xyz - in.vWorldPos);\n\t}\n\t// clamped to the array size, not just taken on trust: if the block\n\t// ever read as something other than what the writer put there, an\n\t// unbounded trip count would also index out of range\n\tlet count = min(i32(uLights.countPad.x), 32);\n\tfor (var i = 0; i < count; i = i + 1) {\n\t\tlet pr = uLights.lights[i].posRange;\n\t\tlet dc = uLights.lights[i].dirCone;\n\t\tvar lightDir : vec3f;\n\t\tvar atten = 1.0;\n\t\tif (pr.w < 0.0) {\n\t\t\t// directional: dirCone.xyz is already the surface→light vector\n\t\t\tlightDir = dc.xyz;\n\t\t} else {\n\t\t\t// positional: quadratic falloff over range (the Light2d model —\n\t\t\t// stylized, not physical inverse-square, which is unusable in\n\t\t\t// pixel-unit worlds with unit intensities)\n\t\t\tlet toLight = pr.xyz - in.vWorldPos;\n\t\t\tlet dist = max(length(toLight), 1e-4);\n\t\t\tlightDir = toLight / dist;\n\t\t\tlet linearAtt = max(0.0, 1.0 - dist / pr.w);\n\t\t\tatten = linearAtt * linearAtt;\n\t\t\tif (dc.w > -1.0) {\n\t\t\t\t// spot cone: -lightDir is the light→surface direction; fade\n\t\t\t\t// from the inner cone cosine to the outer\n\t\t\t\tlet cd = dot(-lightDir, dc.xyz);\n\t\t\t\tatten = atten * smoothstep(dc.w, uLights.lights[i].colorInner.w, cd);\n\t\t\t}\n\t\t}\n\t\t// Half-Lambert (\"wrap\") diffuse: dot * 0.5 + 0.5, squared. Softens\n\t\t// the terminator and lifts the shadowed side — gentler than hard\n\t\t// Lambert, which reads as harsh noon.\n\t\tlet ndl = dot(n, lightDir) * 0.5 + 0.5;\n\t\tlit = lit + uLights.lights[i].colorInner.rgb * (ndl * ndl * atten);\n\t\tif (hasSpecular) {\n\t\t\t// masked by the UNWRAPPED Lambert term: half-Lambert lifts the\n\t\t\t// shadowed side, and a highlight on a surface facing away from\n\t\t\t// the light reads as a rendering error\n\t\t\tlet facing = max(dot(n, lightDir), 0.0);\n\t\t\tlet h = normalize(lightDir + viewDir);\n\t\t\tlet spec = pow(max(dot(n, h), 0.0), shininess);\n\t\t\tspecular = specular\n\t\t\t\t+ uLights.lights[i].colorInner.rgb * (spec * facing * atten);\n\t\t}\n\t}\n\n\t// emissive self-illuminates: added AFTER lighting so it glows at full\n\t// strength regardless of the scene lights (neon, lava, glowing eyes)\n\treturn vec4f(\n\t\tbase.rgb * lit + specular * uMesh.specular.rgb + uMesh.emissive.rgb,\n\t\tbase.a\n\t);\n}\n","/**\n * The WebGPU twin of the GL `WebGLInstanceBuffer`: one `InstancedMesh`'s\n * per-instance records resident on the GPU, bound as a second vertex buffer\n * whose layout steps once per instance.\n *\n * The same two queue-ordering laws that shape `WebGPURetainedGeometry`\n * apply, and matter more here because instance records change far more often\n * than geometry does:\n *\n * 1. `queue.writeBuffer` executes before **every** draw recorded this frame.\n *    So editing an instance on a mesh that has already drawn this frame — a\n *    second camera, a mid-frame animation step — must land in a FRESH\n *    buffer, or the earlier draw would retroactively show the new placement.\n * 2. Destroying a buffer referenced by recorded draws fails the whole\n *    submit, so a replaced buffer retires through `renderer.retireBuffer`\n *    and dies after the frame's submit.\n *\n * Within a frame that has not yet drawn, an edit is a partial\n * `writeBuffer` covering only the dirty span — moving one tree in a forest\n * of five thousand writes 48 bytes.\n * @ignore\n */\nexport default class WebGPUInstanceBuffer {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t */\n\tconstructor(renderer) {\n\t\tthis.renderer = renderer;\n\t\t/** @type {GPUBuffer|null} */\n\t\tthis.buffer = null;\n\t\t/** allocated capacity in bytes @type {number} */\n\t\tthis.capacity = 0;\n\t\t/** the frame a draw was last recorded against this buffer @type {number} */\n\t\tthis.lastDrawnFrameId = -1;\n\t}\n\n\t/**\n\t * Push the dirty span of `data` to the GPU, reallocating when the record\n\t * set outgrew the allocation or when this buffer has already been drawn\n\t * from this frame.\n\t * @param {Float32Array} data - the CPU-side instance records\n\t * @param {number} firstFloat - first dirty float offset (inclusive)\n\t * @param {number} floatCount - number of dirty floats, 0 when clean\n\t * @param {number} usedBytes - bytes of `data` actually in use\n\t */\n\tupload(data, firstFloat, floatCount, usedBytes) {\n\t\tconst renderer = this.renderer;\n\t\tconst device = renderer.device;\n\t\t// writeBuffer offsets and sizes must both be 4-byte multiples; a\n\t\t// record is a whole number of floats, so spans are aligned already\n\t\tconst bytes = Float32Array.BYTES_PER_ELEMENT;\n\t\tconst drawnThisFrame = this.lastDrawnFrameId === renderer.frameId;\n\t\tconst outgrew = usedBytes > this.capacity;\n\n\t\tif (this.buffer === null || outgrew || (drawnThisFrame && floatCount > 0)) {\n\t\t\tif (this.buffer !== null) {\n\t\t\t\t// a draw recorded earlier this frame still references it\n\t\t\t\trenderer.retireBuffer(this.buffer);\n\t\t\t}\n\t\t\t// allocate to the CPU array's full length so growth up to that\n\t\t\t// capacity stays a partial write. A zero-length buffer is invalid,\n\t\t\t// so never request less than one record's worth.\n\t\t\tthis.capacity = Math.max(data.byteLength, usedBytes);\n\t\t\tthis.buffer = device.createBuffer({\n\t\t\t\tlabel: \"melonJS instance records\",\n\t\t\t\tsize: Math.max(this.capacity, 4),\n\t\t\t\tusage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n\t\t\t});\n\t\t\tthis.lastDrawnFrameId = -1;\n\t\t\t// a fresh buffer starts empty, so the WHOLE record set goes in —\n\t\t\t// writing only the dirty span would leave the rest zeroed, which\n\t\t\t// reads as every other instance collapsed onto the origin\n\t\t\tthis.write(0, 0, data.byteLength, data);\n\t\t\treturn;\n\t\t}\n\n\t\tif (floatCount === 0) {\n\t\t\treturn;\n\t\t}\n\t\tthis.write(\n\t\t\tfirstFloat * bytes,\n\t\t\tfirstFloat * bytes,\n\t\t\tfloatCount * bytes,\n\t\t\tdata,\n\t\t);\n\t}\n\n\t/**\n\t * One `queue.writeBuffer`, in BYTES throughout.\n\t *\n\t * `writeBuffer`'s `dataOffset` and `size` are counted in *elements* when\n\t * `data` is a TypedArray and in *bytes* otherwise — an ambiguity that\n\t * silently changes meaning if the staging array's type ever changes. The\n\t * engine's other GPU uploads all pass a `Uint8Array` view with byte\n\t * counts, so this does too: one convention, no element/byte confusion.\n\t * @param {number} bufferOffset - destination byte offset\n\t * @param {number} byteOffset - source byte offset into `data`\n\t * @param {number} byteLength - bytes to copy\n\t * @param {Float32Array} data - the CPU-side instance records\n\t * @ignore\n\t */\n\twrite(bufferOffset, byteOffset, byteLength, data) {\n\t\tthis.renderer.device.queue.writeBuffer(\n\t\t\tthis.buffer,\n\t\t\tbufferOffset,\n\t\t\tnew Uint8Array(data.buffer, data.byteOffset, data.byteLength),\n\t\t\tbyteOffset,\n\t\t\tbyteLength,\n\t\t);\n\t}\n\n\t/**\n\t * Release the GPU buffer (retired when a frame is recording).\n\t */\n\tdestroy() {\n\t\tif (this.buffer !== null) {\n\t\t\tthis.renderer.retireBuffer(this.buffer);\n\t\t\tthis.buffer = null;\n\t\t}\n\t\tthis.capacity = 0;\n\t\tthis.lastDrawnFrameId = -1;\n\t}\n}\n","// Scratch for padding an odd-count uint16 index upload to the 4-byte\n// multiple `queue.writeBuffer` requires. Grows to the largest mesh seen and\n// is reused (uploads are version-change-rare and never re-enter).\nlet indexPadScratch = new Uint16Array(0);\n\n/**\n * The WebGPU twin of the GL `RetainedGeometry`: one mesh's model-space\n * geometry resident on the GPU — a vertex buffer and an index buffer,\n * uploaded on geometry-version change and bound per draw (`setVertexBuffer`\n * / `setIndexBuffer`; there is no VAO to capture, binding is explicit and\n * cheap under this backend).\n *\n * The two queue-ordering laws shape the upload rules:\n * 1. `queue.writeBuffer` executes before EVERY draw recorded this frame —\n *    so a version bump on a mesh that has already drawn this frame\n *    (Sprite3d animation UVs, a second camera) must land in FRESH buffers,\n *    or the earlier draw would retroactively show the new geometry.\n * 2. destroying a buffer referenced by recorded draws fails the whole\n *    submit — replaced buffers retire through `renderer.retireBuffer` and\n *    die after the frame's submit.\n * @ignore\n */\nexport default class WebGPURetainedGeometry {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t */\n\tconstructor(renderer) {\n\t\tthis.renderer = renderer;\n\t\t/** @type {GPUBuffer|null} */\n\t\tthis.vertexBuffer = null;\n\t\t/** @type {GPUBuffer|null} */\n\t\tthis.indexBuffer = null;\n\t\t/** @type {\"uint16\"|\"uint32\"} */\n\t\tthis.indexFormat = \"uint16\";\n\t\tthis.indexCount = 0;\n\t\t// geometry version the buffers hold; -1 forces the first upload\n\t\tthis.uploadedVersion = -1;\n\t\t// the last frame a draw was recorded against these buffers — the\n\t\t// in-place-vs-fresh upload decision (queue law 1)\n\t\tthis.lastDrawnFrameId = -1;\n\t\t// allocated capacities, for the in-place reuse check\n\t\tthis.vertexCapacity = 0;\n\t\tthis.indexCapacity = 0;\n\t}\n\n\t/**\n\t * Upload (or re-upload) the interleaved vertex data and indices.\n\t * @param {Float32Array} vertexData - interleaved data scratch\n\t * @param {number} floatCount - number of floats to upload\n\t * @param {Uint16Array|Uint32Array} indices - the mesh's authored indices\n\t * @param {number} version - the mesh's geometry version being uploaded\n\t */\n\tupload(vertexData, floatCount, indices, version) {\n\t\tconst renderer = this.renderer;\n\t\tconst device = renderer.device;\n\t\tconst vertexBytes = floatCount * Float32Array.BYTES_PER_ELEMENT;\n\t\tconst wide = indices instanceof Uint32Array;\n\t\t// writeBuffer sizes must be 4-byte multiples — pad an odd uint16 run\n\t\tconst indexBytes = (indices.byteLength + 3) & ~3;\n\n\t\t// in-place re-use is safe only when nothing recorded this frame\n\t\t// references the buffers and the new data fits the allocations\n\t\tconst fresh =\n\t\t\tthis.vertexBuffer === null ||\n\t\t\tthis.lastDrawnFrameId === renderer.frameId ||\n\t\t\tvertexBytes > this.vertexCapacity ||\n\t\t\tindexBytes > this.indexCapacity;\n\t\tif (fresh) {\n\t\t\tthis.releaseBuffers();\n\t\t\tthis.vertexBuffer = device.createBuffer({\n\t\t\t\tlabel: \"melonJS retained mesh vertices\",\n\t\t\t\tsize: vertexBytes,\n\t\t\t\tusage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,\n\t\t\t});\n\t\t\tthis.indexBuffer = device.createBuffer({\n\t\t\t\tlabel: \"melonJS retained mesh indices\",\n\t\t\t\tsize: indexBytes,\n\t\t\t\tusage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\n\t\t\t});\n\t\t\tthis.vertexCapacity = vertexBytes;\n\t\t\tthis.indexCapacity = indexBytes;\n\t\t\tthis.lastDrawnFrameId = -1;\n\t\t}\n\n\t\tdevice.queue.writeBuffer(\n\t\t\tthis.vertexBuffer,\n\t\t\t0,\n\t\t\tvertexData.buffer,\n\t\t\tvertexData.byteOffset,\n\t\t\tvertexBytes,\n\t\t);\n\n\t\tlet indexSource = indices;\n\t\tif (indexBytes !== indices.byteLength) {\n\t\t\t// odd uint16 count: stage through the padded scratch\n\t\t\tif (indexPadScratch.length < indices.length + 1) {\n\t\t\t\tindexPadScratch = new Uint16Array(indices.length + 1);\n\t\t\t}\n\t\t\tindexPadScratch.set(indices);\n\t\t\tindexPadScratch[indices.length] = 0;\n\t\t\tindexSource = indexPadScratch;\n\t\t}\n\t\tdevice.queue.writeBuffer(\n\t\t\tthis.indexBuffer,\n\t\t\t0,\n\t\t\tindexSource.buffer,\n\t\t\tindexSource.byteOffset,\n\t\t\tindexBytes,\n\t\t);\n\n\t\tthis.indexFormat = wide ? \"uint32\" : \"uint16\";\n\t\tthis.indexCount = indices.length;\n\t\tthis.uploadedVersion = version;\n\t}\n\n\t/**\n\t * retire both buffers (frame-safe) without resetting the version — the\n\t * shared tail of re-allocation and destruction\n\t * @ignore\n\t */\n\treleaseBuffers() {\n\t\tif (this.vertexBuffer !== null) {\n\t\t\tthis.renderer.retireBuffer(this.vertexBuffer);\n\t\t\tthis.vertexBuffer = null;\n\t\t}\n\t\tif (this.indexBuffer !== null) {\n\t\t\tthis.renderer.retireBuffer(this.indexBuffer);\n\t\t\tthis.indexBuffer = null;\n\t\t}\n\t\tthis.vertexCapacity = 0;\n\t\tthis.indexCapacity = 0;\n\t}\n\n\t/**\n\t * Release the GPU buffers. Idempotent — a destroyed geometry object\n\t * simply re-uploads if ever used again.\n\t */\n\tdestroy() {\n\t\tthis.releaseBuffers();\n\t\tthis.uploadedVersion = -1;\n\t\tthis.indexCount = 0;\n\t\tthis.lastDrawnFrameId = -1;\n\t}\n}\n","// melonJS WebGPU mesh shader — the WGSL port of mesh.vert / mesh.frag.\n//\n// Vertex layout (36-byte stride, shared with the WebGL backend):\n//   @location(0) aVertex float32x3 offset 0\n//   @location(1) aRegion float32x2 offset 12\n//   @location(2) aColor  float32x4 offset 20\n//\n// aColor is four straight floats (R, G, B, A in [0, 1]) — NOT the quad\n// tier's packed unorm8x4 — so no BGRA swizzle applies. The float layout is\n// shared with the GL backend, where it exists to dodge NaN-pattern\n// canonicalization on Metal-backed drivers.\n//\n// Placement is uniform-driven so retained geometry uploads once and reuses:\n//   clip = projection × view × model × vertex\n// One MeshUniforms snapshot binds per draw (group 3, dynamic offset — the\n// queue-write ordering law: a shared region would be retroactively\n// clobbered for draws already recorded). The accumulated path (2D camera,\n// CPU-projected vertices) supplies an identity model matrix.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block\n\tlineWidth : f32,\n};\n\nstruct MeshUniforms {\n\t// the mesh's own placement (axis bridge + mesh scale included)\n\tmodel : mat4x4<f32>,\n\t// the camera view plus any ancestor container transform\n\tview : mat4x4<f32>,\n\t// per-draw tint × global alpha (r, g, b, a) — kept out of the vertex\n\t// data so re-tinting never invalidates retained geometry\n\ttint : vec4f,\n\t// x = alpha cutout threshold (0 = disabled), y = 1 when an opacity map\n\t// is bound (0 = the second sampler is filler); z, w reserved\n\tparams : vec4f,\n\t// self-illumination added on top (r, g, b; w reserved)\n\temissive : vec4f,\n\t// specular color (rgb) and exponent (w); w = 0 means no highlight.\n\t// Unused by the unlit tier, present so both tiers share one block size.\n\tspecular : vec4f,\n\t// the camera's world position (xyz; w reserved)\n\teye : vec4f,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n// per-texel opacity (MTL map_d). When no map is bound this pair is filler —\n// the diffuse texture again — and `params.y` is 0, so its sample is weighted\n// away rather than branched around.\n@group(1) @binding(2) var uAlphaMap : texture_2d<f32>;\n@group(1) @binding(3) var uAlphaSampler : sampler;\n@group(3) @binding(0) var<uniform> uMesh : MeshUniforms;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet clip =\n\t\tuFrame.projection * uMesh.view * uMesh.model * vec4f(aVertex, 1.0);\n\t// GL-convention clip z in [-w, w] remapped to WebGPU's [0, w]\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\t// tint first, then premultiply — matches the fragment's expectation\n\tlet tinted = aColor * uMesh.tint;\n\tout.vColor = vec4f(tinted.rgb * tinted.a, tinted.a);\n\tout.vRegion = aRegion;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\t// sampled unconditionally, before the discard (uniform control flow)\n\tvar color = textureSample(uTexture, uSampler, in.vRegion) * in.vColor;\n\t// Per-texel opacity (MTL map_d), applied BEFORE the cutout so one\n\t// material can cut out to the shape of a leaf rather than at a single\n\t// threshold across the whole surface. Sampled unconditionally and\n\t// WEIGHTED: with no map bound the second pair is filler, and this keeps\n\t// the sample in uniform control flow (and identical to the GLSL twin).\n\tcolor.a = color.a * mix(1.0, textureSample(uAlphaMap, uAlphaSampler, in.vRegion).r, uMesh.params.y);\n\t// hard alpha cutout (glTF alphaMode MASK): drop cut texels so foliage /\n\t// fences / decals read crisp without blending or sorting\n\tif (color.a < uMesh.params.x) {\n\t\tdiscard;\n\t}\n\t// emissive adds a self-lit color on top (neon, lava, screens); the\n\t// unlit path has no lighting, so it is simply added to the base color\n\treturn vec4f(color.rgb + uMesh.emissive.rgb, color.a);\n}\n","// Instanced ground-shadow module (#1515) — the WGSL twin of\n// `webgl/shaders/mesh-shadow-instanced.vert`.\n//\n// One flat blob per instance, drawn from the SAME instance buffer the mesh\n// itself uses, so a forest of a hundred thousand trees costs one extra draw\n// rather than a hundred thousand.\n//\n// Hand-written and STANDALONE, rather than derived from the mesh module by\n// `buildInstancedMeshWGSL`. Deriving it would inherit three things it must\n// not have: the `hasColor`/`hasData` variant matrix (this reads neither slot,\n// so one module serves every record layout), the per-instance tint multiply\n// and emissive substitution (which would give a forest coloured, glowing\n// shadows), and the VSOut member-written guard, which on the lit tier\n// requires a body that writes `vWorldPos` and `vNormal` — neither of which a\n// flat unlit blob has any use for.\n//\n// The instance's rotation and vertical scale are DISCARDED: a blob lies flat\n// however its tree is turned, and only the horizontal footprint sets its\n// width. Translation and horizontal scale come straight out of the record's\n// three rows with no matrix built. Flattening onto the ground is\n// `uMesh.model`'s job — the caller passes the group matrix with its Y basis\n// column zeroed and its translation Y set to the ground height.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block. Its size\n\t// has to match the block the layout declares EXACTLY — a struct that reads\n\t// past `minBindingSize` is a pipeline-creation error, not a silent\n\t// over-read, and takes the whole frame's command buffer down with it.\n\tlineWidth : f32,\n};\n\nstruct MeshUniforms {\n\tmodel : mat4x4<f32>,\n\tview : mat4x4<f32>,\n\ttint : vec4f,\n\tparams : vec4f,\n\temissive : vec4f,\n\tspecular : vec4f,\n\teye : vec4f,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n@group(1) @binding(2) var uAlphaMap : texture_2d<f32>;\n@group(1) @binding(3) var uAlphaSampler : sampler;\n@group(3) @binding(0) var<uniform> uMesh : MeshUniforms;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aInstanceRow0 : vec4f,\n\t@location(4) aInstanceRow1 : vec4f,\n\t@location(5) aInstanceRow2 : vec4f,\n) -> VSOut {\n\tvar out : VSOut;\n\n\tlet instancePos = vec3f(aInstanceRow0.w, aInstanceRow1.w, aInstanceRow2.w);\n\t// column 0 of the instance basis is the X axis times its scale; a scatter\n\t// is rotated about the vertical axis, so its length is the horizontal\n\t// footprint whatever the rotation\n\tlet footprint = length(\n\t\tvec3f(aInstanceRow0.x, aInstanceRow1.x, aInstanceRow2.x));\n\n\t// the quad is a unit square in the ground plane (XZ); its own Y is\n\t// irrelevant, because uMesh.model flattens it onto the floor\n\tlet local = instancePos + vec3f(aVertex.x, 0.0, aVertex.z) * footprint;\n\n\tlet clip = uFrame.projection * uMesh.view * uMesh.model * vec4f(local, 1.0);\n\t// WebGPU clip space is [0, w] in Z where GL is [-w, w]\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\n\tlet tinted = aColor * uMesh.tint;\n\tout.vColor = vec4f(tinted.rgb * tinted.a, tinted.a);\n\tout.vRegion = aRegion;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\tlet texel = textureSample(uTexture, uSampler, in.vRegion);\n\treturn texel * in.vColor;\n}\n","import VertexArrayBuffer from \"../../buffer/vertex.js\";\nimport { Batcher, DEFAULT_MAX_VERTICES } from \"../../gpu/batcher.js\";\nimport { resolveVertexFormat } from \"../../gpu/vertexformat.ts\";\n\n/**\n * The base WebGPU Batcher — realizes the backend-neutral {@link Batcher}\n * lifecycle (`init` / `bind` / `unbind` / `flush` / `reset` / `destroy`,\n * driven by `renderer.addBatcher` / `setBatcher`) on a\n * `GPURenderPassEncoder` instead of GL state. Custom WebGPU batchers\n * extend this class.\n *\n * A \"flush\" here is one `queue.writeBuffer` of the pending vertex bytes\n * into a fresh region of the renderer's per-frame buffer arena, plus one\n * draw recorded into the frame's open pass — nothing is submitted until\n * the frame ends. Attribute layouts are declared in the backend-neutral\n * vocabulary of `src/video/gpu/vertexformat.ts` and consumed declaratively\n * into the pipeline's `GPUVertexBufferLayout` (#1492) — no enum bridge.\n * @augments Batcher\n * @category Rendering\n */\nexport default class WebGPUBatcher extends Batcher {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @param {object} [settings] - batcher settings (see {@link WebGPUBatcher#init})\n\t */\n\tconstructor(renderer, settings) {\n\t\tsuper();\n\t\tthis.init(renderer, settings);\n\t}\n\n\t/**\n\t * Initialize (or re-initialize after device loss) this batcher.\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @param {object} [settings] - batcher settings\n\t * @param {object[]} [settings.attributes] - neutral attribute descriptors\n\t * @param {string} [settings.shaderKey] - pipeline-cache shader family\n\t * @param {string} [settings.topology] - default portable topology\n\t * @param {number} [settings.maxVertices] - staging capacity in vertices\n\t */\n\tinit(renderer, settings) {\n\t\tthis.renderer = renderer;\n\t\tthis.device = renderer.device;\n\n\t\tif (typeof settings !== \"undefined\") {\n\t\t\tthis.shaderKey = settings.shaderKey;\n\t\t\tthis.topology = settings.topology ?? \"triangle-list\";\n\n\t\t\t// resolve the neutral attribute descriptors: offsets are given,\n\t\t\t// the stride is the end of the last attribute (the layouts are\n\t\t\t// tightly packed, same convention as the WebGL batchers)\n\t\t\tthis.attributes = settings.attributes.map((a) => {\n\t\t\t\tconst format = resolveVertexFormat(a.format);\n\t\t\t\treturn {\n\t\t\t\t\tname: a.name,\n\t\t\t\t\tformat: a.format,\n\t\t\t\t\toffset: a.offset,\n\t\t\t\t\tbytes: format.bytes,\n\t\t\t\t};\n\t\t\t});\n\t\t\tconst last = this.attributes[this.attributes.length - 1];\n\t\t\tthis.stride = last.offset + last.bytes;\n\t\t\tthis.vertexSize = this.stride / Float32Array.BYTES_PER_ELEMENT;\n\n\t\t\tthis.vertexData = new VertexArrayBuffer(\n\t\t\t\tthis.vertexSize,\n\t\t\t\tsettings.maxVertices ?? DEFAULT_MAX_VERTICES,\n\t\t\t);\n\n\t\t\t// hand the layout to the pipeline cache — every pipeline of this\n\t\t\t// shader family shares it\n\t\t\trenderer.pipelineCache.registerVertexLayout(\n\t\t\t\tthis.shaderKey,\n\t\t\t\tthis.stride,\n\t\t\t\tthis.attributes,\n\t\t\t);\n\t\t} else {\n\t\t\t// re-init after device loss: keep the frozen layout/staging,\n\t\t\t// re-register with the freshly-built pipeline cache. A FIRST\n\t\t\t// init without settings has no layout to re-register — fail\n\t\t\t// with the same message as the WebGL base instead of a\n\t\t\t// confusing TypeError inside the pipeline cache\n\t\t\tif (typeof this.shaderKey === \"undefined\") {\n\t\t\t\tthrow new Error(\"attributes definition missing\");\n\t\t\t}\n\t\t\tthis.vertexData?.clear();\n\t\t\trenderer.pipelineCache.registerVertexLayout(\n\t\t\t\tthis.shaderKey,\n\t\t\t\tthis.stride,\n\t\t\t\tthis.attributes,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * make this batcher the active one — nothing per-batcher persists on\n\t * the pass in the 2D tier; this is the seam where the mesh batcher\n\t * will later select its depth-enabled pipeline family\n\t */\n\tbind() {}\n\n\t/**\n\t * called when another batcher takes over\n\t */\n\tunbind() {}\n\n\t/**\n\t * per-draw refresh hook (pass-scoped state that `bind()` cannot own\n\t * because `setBatcher` early-returns when already current)\n\t */\n\tupdatePassState() {}\n\n\t/**\n\t * record the pending vertices as one draw in the frame's open pass\n\t * @param {string} [topology] - override the batcher's default topology\n\t */\n\tflush(topology = this.topology) {\n\t\tconst vertexData = this.vertexData;\n\t\tconst vertexCount = vertexData.vertexCount;\n\t\tif (vertexCount === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst renderer = this.renderer;\n\t\tconst pass = renderer.ensurePass();\n\t\tconst byteLength = vertexCount * this.stride;\n\n\t\tconst region = renderer.vertexArena.alloc(byteLength);\n\t\tthis.device.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tvertexData.toUint8(),\n\t\t\t0,\n\t\t\tbyteLength,\n\t\t);\n\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.shaderKey,\n\t\t\ttopology,\n\t\t\trenderer.currentBlendMode,\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tpass.setVertexBuffer(0, region.buffer, region.offset, byteLength);\n\n\t\tthis.recordDraw(pass, vertexCount);\n\n\t\tvertexData.clear();\n\t}\n\n\t/**\n\t * record the actual draw call — non-indexed by default; the quad\n\t * batcher overrides with its indexed 6-per-quad pattern\n\t * @param {GPURenderPassEncoder} pass - the open pass\n\t * @param {number} vertexCount - pending vertex count\n\t */\n\trecordDraw(pass, vertexCount) {\n\t\tpass.draw(vertexCount);\n\t}\n\n\t/**\n\t * drop any pending vertices (game reset)\n\t */\n\treset() {\n\t\tthis.vertexData.clear();\n\t}\n\n\t/**\n\t * release this batcher's GPU resources\n\t */\n\tdestroy() {\n\t\tthis.vertexData?.clear();\n\t}\n}\n","import { Matrix3d } from \"../../../math/matrix3d.ts\";\nimport { instanceAttributes } from \"../../gpu/instancerecord.ts\";\nimport {\n\tassignIndex,\n\tbeginChunk,\n\tensureRemapCapacity,\n\tremapIndex,\n} from \"../../gpu/meshchunk.ts\";\nimport { buildMeshVertexData, retainedScratch } from \"../../gpu/meshvertex.ts\";\nimport WebGPUInstanceBuffer from \"../buffer/instance_buffer.js\";\nimport WebGPURetainedGeometry from \"../buffer/retained_geometry.js\";\nimport meshWGSL from \"../shaders/mesh.wgsl\";\nimport {\n\tbuildInstancedMeshWGSL,\n\tUNLIT_INSTANCED,\n} from \"../shaders/mesh-instanced.js\";\nimport meshShadowInstancedWGSL from \"../shaders/mesh-shadow-instanced.wgsl\";\nimport WebGPUBatcher from \"./webgpu_batcher.js\";\n\n/**\n * Byte size of the per-draw mesh uniform block:\n * mat4x4 model (64) + mat4x4 view (64) + vec4 tint (16) + vec4 params\n * (alphaCutoff, hasAlphaMap, reserved ×2) (16) + vec4 emissive (16) +\n * vec4 specular (rgb + shininess) (16) + vec4 eye (camera world position;\n * w reserved) (16) → 208.\n * @ignore\n */\nexport const MESH_UNIFORM_SIZE = 208;\n\n// Shared identity model matrix for draws whose vertices are already placed\n// (the 2D-camera path pre-projects them on the CPU). Never mutated.\nconst IDENTITY_MATRIX = new Matrix3d();\n\n// Scratch for assembling one MeshUniforms snapshot (44 floats = 176 bytes).\n// Reused — setPlacementUniforms runs synchronously and never re-enters.\nconst UNIFORM_SCRATCH = new Float32Array(MESH_UNIFORM_SIZE / 4);\n\n/**\n * The WebGPU mesh batcher — textured triangle meshes with the same\n * geometry contract as the WebGL `MeshBatcher` (36-byte unlit layout,\n * model-space retained geometry, uniform-driven placement) realized on\n * this backend's recording model:\n *\n * - mesh mode is PIPELINE state, not device state: every mesh flush looks\n *   its pipeline up with the mesh axes (depth write + LEQUAL, per-mesh\n *   cull/frontFace from `renderer.drawMesh`) and blend forced \"none\" —\n *   there is no `bind()`/`unbind()` state to own, and the depth clear is\n *   the pass's `depthLoadOp` (see `resolveDepthOps`), not a draw-time op\n * - placement/tint/cutoff/emissive ride ONE per-draw uniform snapshot\n *   (group 3, dynamic offset into the effect uniform arena) — the\n *   queue-write law: a shared region would be retroactively clobbered for\n *   draws already recorded this frame\n * - the accumulated path chunks through the shared versioned-remap dedup\n *   (`gpu/meshchunk.ts`) into the frame's vertex arena plus a per-frame\n *   INDEX arena (`renderer.indexArena`) — one `drawIndexed` per chunk\n * @augments WebGPUBatcher\n * @category Rendering\n */\nexport default class WebGPUMeshBatcher extends WebGPUBatcher {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @override\n\t */\n\tinit(renderer, settings) {\n\t\tsuper.init(renderer, settings ?? this.defaultSettings());\n\t\tconst cache = renderer.pipelineCache;\n\n\t\t// group-3 layout for the per-draw uniform snapshot, cached by shape\n\t\t// signature (shared with the lit subclass and across device losses\n\t\t// via the pipeline cache's own lifecycle)\n\t\tthis.meshLayout = cache.getEffectLayout(this.uniformSignature(), [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,\n\t\t\t\tbuffer: {\n\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\tminBindingSize: MESH_UNIFORM_SIZE,\n\t\t\t\t},\n\t\t\t},\n\t\t]);\n\n\t\t// register the family: it rides this batcher's own vertex layout\n\t\t// (the key the base init just registered the layout under), with\n\t\t// the subclass-controlled module text and group-layout list. A\n\t\t// re-init against a SURVIVING cache (reset with a valid device)\n\t\t// short-circuits on the module text — registerShader would dedupe\n\t\t// anyway, but evaluating bindGroupLayoutList first would orphan a\n\t\t// fresh lit lightsLayout per re-init\n\t\tconst vertexLayoutKey = this.shaderKey;\n\t\tconst source = this.shaderSource();\n\t\tconst registered = cache.registeredModules.get(source);\n\t\tif (typeof registered !== \"undefined\") {\n\t\t\tthis.shaderKey = registered;\n\t\t} else {\n\t\t\tthis.shaderKey = cache.registerShader(source, {\n\t\t\t\tbindGroupLayouts: this.bindGroupLayoutList(cache),\n\t\t\t\tvertexLayoutKey,\n\t\t\t\tlabel: `melonJS ${vertexLayoutKey} shader`,\n\t\t\t});\n\t\t}\n\t\t// the host identity for custom modules: a hosted custom shader is\n\t\t// registered against this batcher's vertex layout and group list\n\t\tthis.vertexLayoutKey = vertexLayoutKey;\n\t\t// the custom shader hosted for the NEXT draw (a WGSL-carrying\n\t\t// GLShader routed by `renderer.drawMesh`, cleared after each mesh),\n\t\t// or null\n\t\tthis.customShader = null;\n\n\t\t// CPU index staging for the accumulated path (uint32 — matches the\n\t\t// backend's index convention and keeps writeBuffer 4-byte aligned)\n\t\tthis.indexData = new Uint32Array(this.vertexData.maxVertex * 6);\n\t\tthis.indexCount = 0;\n\n\t\t// the material bind group the pending vertices were queued under\n\t\tthis.currentMaterial = null;\n\t\t// per-draw uniform snapshot binding: {bindGroup, dynamicOffset}\n\t\tthis.uniformBinding = null;\n\t\t// uniform bind group per arena page (pages persist across frames)\n\t\tthis.uniformBindGroups = new Map();\n\t\t// the mesh pass axes for the NEXT flush — mutated in place by\n\t\t// `renderer.drawMesh` per mesh (read synchronously at pipeline lookup)\n\t\tthis.meshState = { cullMode: \"back\", frontFace: \"ccw\" };\n\n\t\t// Retained geometry per mesh (model-space buffers uploaded once). A\n\t\t// re-init means a new device or a fresh batcher life, so anything\n\t\t// held is stale — release it rather than leak it.\n\t\tif (this.retained !== undefined) {\n\t\t\tthis.releaseAllRetained();\n\t\t}\n\t\tthis.retained = new Map();\n\n\t\t// Per-mesh instance record buffers, and the pipeline-cache family key\n\t\t// per declared slot combination. Same lifetime rule as `retained`: a\n\t\t// re-init means a new device, so drop what is held.\n\t\tif (this.instanced !== undefined) {\n\t\t\tthis.releaseAllInstanced();\n\t\t}\n\t\tthis.instanced = new Map();\n\t\tthis.instancedKeys = new Map();\n\t\t// ground-shadow families, keyed by record shape like `instancedKeys`\n\t\t// — the module is shared, the instance buffer's stride is not\n\t\tthis.shadowFamilyKeys = new Map();\n\t}\n\n\t/**\n\t * The instanced variant options for this tier (unlit by default) — where\n\t * its instance attributes start and how its vertex stage places them.\n\t * @ignore\n\t */\n\tinstancedVariant() {\n\t\treturn UNLIT_INSTANCED;\n\t}\n\n\t/**\n\t * The pipeline-cache family for a given record layout, registered on\n\t * first use.\n\t *\n\t * WGSL has no preprocessor, so the variant is a module DERIVED from this\n\t * tier's ordinary source (see `buildInstancedMeshWGSL`) rather than the\n\t * same text compiled with different defines. Its vertex layout is two\n\t * groups: this batcher's geometry layout, plus the instance records\n\t * stepping once per instance.\n\t * @param {object} layout - the instance record layout\n\t * @returns {string} the family key\n\t * @ignore\n\t */\n\tinstancedFamilyFor(layout) {\n\t\tconst key = (layout.hasColor ? 1 : 0) | (layout.hasData ? 2 : 0);\n\t\tlet familyKey = this.instancedKeys.get(key);\n\t\tif (familyKey !== undefined) {\n\t\t\treturn familyKey;\n\t\t}\n\t\tconst cache = this.renderer.pipelineCache;\n\t\tconst variant = this.instancedVariant();\n\t\tconst source = buildInstancedMeshWGSL(this.shaderSource(), {\n\t\t\t...variant,\n\t\t\thasColor: layout.hasColor,\n\t\t\thasData: layout.hasData,\n\t\t});\n\t\tconst layoutKey = `${this.vertexLayoutKey}Instanced${key}`;\n\t\tcache.registerVertexLayout(layoutKey, [\n\t\t\t{\n\t\t\t\tstride: this.stride,\n\t\t\t\tattributes: this.attributes,\n\t\t\t},\n\t\t\t{\n\t\t\t\tstride: layout.stride,\n\t\t\t\tstepMode: \"instance\",\n\t\t\t\tattributes: instanceAttributes(layout, variant.baseLocation),\n\t\t\t},\n\t\t]);\n\t\tfamilyKey = cache.registerShader(source, {\n\t\t\tbindGroupLayouts: this.bindGroupLayoutList(cache),\n\t\t\tvertexLayoutKey: layoutKey,\n\t\t\tlabel: `melonJS ${layoutKey} shader`,\n\t\t});\n\t\tthis.instancedKeys.set(key, familyKey);\n\t\treturn familyKey;\n\t}\n\n\t/**\n\t * Get (creating on first use) the GPU instance buffer for one mesh, with\n\t * its dirty span already uploaded.\n\t * @param {InstancedMesh} mesh - the mesh being drawn\n\t * @returns {WebGPUInstanceBuffer} the up-to-date buffer\n\t * @ignore\n\t */\n\tinstanceBufferFor(mesh) {\n\t\tlet buffer = this.instanced.get(mesh);\n\t\tif (buffer === undefined) {\n\t\t\tbuffer = new WebGPUInstanceBuffer(this.renderer);\n\t\t\tthis.instanced.set(mesh, buffer);\n\t\t}\n\t\tconst plan = mesh.instanceUpload(buffer.uploadedRevision ?? -1);\n\t\tif (plan.full) {\n\t\t\t// this buffer missed edits the span no longer describes\n\t\t\tbuffer.capacity = 0;\n\t\t}\n\t\tbuffer.upload(\n\t\t\tmesh.instanceBuffer,\n\t\t\tplan.first,\n\t\t\tplan.count,\n\t\t\tmesh.instanceCount * mesh.instanceLayout.stride,\n\t\t);\n\t\tbuffer.uploadedRevision = plan.revision;\n\t\tmesh.clearInstanceDirty();\n\t\treturn buffer;\n\t}\n\n\t/**\n\t * Draw every visible instance of a mesh in one recorded call.\n\t * @param {InstancedMesh} mesh - the mesh to draw\n\t * @param {Matrix3d} modelMatrix - where the group sits in the world\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @ignore\n\t */\n\tdrawInstancedMesh(mesh, modelMatrix, tint) {\n\t\tconst count = mesh.visibleInstanceCount;\n\t\tif (count === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// anything queued must land first, or this draw would reorder ahead\n\t\tthis.flush();\n\n\t\tthis.updatePassState();\n\t\t// on the split path every range resolves its own binding below, so\n\t\t// resolving the mesh-level one here would reserve a texture unit (and\n\t\t// possibly run a first-use upload plus mip generation) for a binding\n\t\t// that is immediately overwritten\n\t\tif (mesh.textureGroups === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t}\n\t\tthis.setPlacementUniforms(modelMatrix, tint, mesh);\n\n\t\tconst renderer = this.renderer;\n\t\t// A custom mesh shader is not hosted on the instanced path: the\n\t\t// instanced families own their vertex layout (geometry group +\n\t\t// per-instance group at pinned locations), which a custom module does\n\t\t// not declare. Same limitation as the WebGL backend, warned the same\n\t\t// way, so the two agree rather than one silently ignoring it.\n\t\tif (this.customShader != null && this.instancedShaderWarned !== true) {\n\t\t\tthis.instancedShaderWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: a custom shader cannot be hosted on an InstancedMesh — the mesh draws with the built-in instanced shading\",\n\t\t\t);\n\t\t}\n\t\tconst pass = renderer.ensurePass();\n\t\tconst geometry = this.retainedGeometryFor(mesh);\n\t\tconst instances = this.instanceBufferFor(mesh);\n\n\t\tthis.meshState.depthWrite = undefined;\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.instancedFamilyFor(mesh.instanceLayout),\n\t\t\t\"triangle-list\",\n\t\t\t\"none\",\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t\tthis.meshState,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tthis.bindLights(pass);\n\t\tpass.setBindGroup(3, this.uniformBinding.bindGroup, [\n\t\t\tthis.uniformBinding.dynamicOffset,\n\t\t]);\n\t\tpass.setVertexBuffer(0, geometry.vertexBuffer);\n\t\tpass.setVertexBuffer(1, instances.buffer);\n\t\tpass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\t\tpass.drawIndexed(geometry.indexCount, count);\n\t\t} else {\n\t\t\t// a multi-material prototype: every instance draws the same split\n\t\t\t// (#1573), so each range is one instanced draw over the whole set\n\t\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\t\t\tpass.drawIndexed(slices[i].count, count, slices[i].start);\n\t\t\t}\n\t\t}\n\n\t\t// stamp both halves: an edit later this frame must go to fresh buffers\n\t\tgeometry.lastDrawnFrameId = renderer.frameId;\n\t\tinstances.lastDrawnFrameId = renderer.frameId;\n\t}\n\n\t/**\n\t * The instanced ground-shadow family (#1515), registered on first use.\n\t *\n\t * ONE family, not one per record layout: the module reads only the three\n\t * transform rows, so `hasColor` / `hasData` make no difference — and the\n\t * vertex layout declares only those rows, leaving the instance buffer's\n\t * stride and offsets exactly as the mesh pass uses them.\n\t * @param {object} layout - the mesh's instance record layout\n\t * @returns {string} the pipeline family key\n\t * @ignore\n\t */\n\tinstancedShadowFamily(layout) {\n\t\t// Keyed by the record shape, exactly as `instancedFamilyFor` is, and\n\t\t// for a sharper reason: the shadow reads only the three transform\n\t\t// rows, so ONE module serves every layout — but the instance buffer's\n\t\t// `arrayStride` is baked into the vertex layout, and it is 48 / 64 / 80\n\t\t// bytes depending on which optional slots the records carry. Caching a\n\t\t// single family would hand the second scatter the first one's stride\n\t\t// and its blobs would land at garbage positions.\n\t\tconst key = (layout.hasColor ? 1 : 0) | (layout.hasData ? 2 : 0);\n\t\tlet familyKey = this.shadowFamilyKeys.get(key);\n\t\tif (familyKey !== undefined) {\n\t\t\treturn familyKey;\n\t\t}\n\t\tconst cache = this.renderer.pipelineCache;\n\t\tconst layoutKey = `${this.vertexLayoutKey}InstancedShadow${key}`;\n\t\tcache.registerVertexLayout(layoutKey, [\n\t\t\t{\n\t\t\t\tstride: this.stride,\n\t\t\t\t// TRIMMED to position / region / colour, at the original stride\n\t\t\t\t// so the offsets still land. The lit tier carries `aNormal` at\n\t\t\t\t// location 3, which is where the instance rows start — declaring\n\t\t\t\t// the full layout collides with them and the pipeline is\n\t\t\t\t// rejected outright (\"shader location used more than once\"). A\n\t\t\t\t// flat blob has no use for a normal, so dropping it is also what\n\t\t\t\t// lets ONE module serve both tiers.\n\t\t\t\tattributes: this.attributes.slice(0, 3),\n\t\t\t},\n\t\t\t{\n\t\t\t\tstride: layout.stride,\n\t\t\t\tstepMode: \"instance\",\n\t\t\t\t// the three rows only — the shadow reads no colour, no data\n\t\t\t\tattributes: instanceAttributes(layout, 3).slice(0, 3),\n\t\t\t},\n\t\t]);\n\t\tfamilyKey = cache.registerShader(meshShadowInstancedWGSL, {\n\t\t\tbindGroupLayouts: this.bindGroupLayoutList(cache),\n\t\t\tvertexLayoutKey: layoutKey,\n\t\t\tlabel: `melonJS instanced mesh shadow ${key}`,\n\t\t});\n\t\tthis.shadowFamilyKeys.set(key, familyKey);\n\t\treturn familyKey;\n\t}\n\n\t/**\n\t * Record one flat blob per instance, in a single call, over the same\n\t * instance buffer the mesh itself drew from (#1515).\n\t * @param {InstancedMesh} mesh - the mesh casting the shadows\n\t * @param {Matrix3d} shadowMatrix - the group matrix flattened onto the ground\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @param {object} quad - the shared shadow quad\n\t * @ignore\n\t */\n\tdrawInstancedShadow(mesh, shadowMatrix, tint, quad) {\n\t\tconst count = mesh.visibleInstanceCount;\n\t\tif (count === 0) {\n\t\t\treturn;\n\t\t}\n\t\tthis.flush();\n\t\tthis.updatePassState();\n\t\tthis.applyMeshMaterial(quad);\n\t\tthis.setPlacementUniforms(shadowMatrix, tint, quad);\n\n\t\t// Cull state is a PIPELINE axis here, and `meshState.cullMode` /\n\t\t// `frontFace` are written only by `WebGPURenderer.drawMesh` — which this\n\t\t// path bypasses. Left alone the blob inherits whatever the last ordinary\n\t\t// mesh set, and since the shadow matrix's horizontal block has negative\n\t\t// determinant under the glTF bridge, an inherited `\"back\"` can cull the\n\t\t// blobs outright. A ground quad is seen from above and, on a mirrored\n\t\t// bridge, from whichever side the winding lands on — never cull it.\n\t\tthis.meshState.cullMode = \"none\";\n\t\tthis.meshState.frontFace = \"ccw\";\n\n\t\tconst renderer = this.renderer;\n\t\tconst pass = renderer.ensurePass();\n\t\tconst quadGeometry = this.retainedGeometryFor(quad);\n\t\tconst instances = this.instanceBufferFor(mesh);\n\n\t\t// blended, and no depth write: overlapping blobs blend rather than\n\t\t// fight under LEQUAL\n\t\tthis.meshState.depthWrite = false;\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.instancedShadowFamily(mesh.instanceLayout),\n\t\t\t\"triangle-list\",\n\t\t\t\"normal\",\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t\tthis.meshState,\n\t\t);\n\t\tthis.meshState.depthWrite = undefined;\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\tthis.bindLights(pass);\n\t\tpass.setBindGroup(3, this.uniformBinding.bindGroup, [\n\t\t\tthis.uniformBinding.dynamicOffset,\n\t\t]);\n\t\tpass.setVertexBuffer(0, quadGeometry.vertexBuffer);\n\t\tpass.setVertexBuffer(1, instances.buffer);\n\t\tpass.setIndexBuffer(quadGeometry.indexBuffer, quadGeometry.indexFormat);\n\t\tpass.drawIndexed(quadGeometry.indexCount, count);\n\n\t\tquadGeometry.lastDrawnFrameId = renderer.frameId;\n\t\tinstances.lastDrawnFrameId = renderer.frameId;\n\t}\n\n\t/**\n\t * Release the instance buffer held for one mesh, if any.\n\t * @param {object} mesh - the mesh whose instance records should be freed\n\t * @ignore\n\t */\n\treleaseInstanced(mesh) {\n\t\tconst buffer = this.instanced?.get(mesh);\n\t\tif (buffer !== undefined) {\n\t\t\tbuffer.destroy();\n\t\t\tthis.instanced.delete(mesh);\n\t\t}\n\t}\n\n\t/**\n\t * Release every instance buffer this batcher holds.\n\t * @ignore\n\t */\n\treleaseAllInstanced() {\n\t\tthis.instanced?.forEach((buffer) => {\n\t\t\tbuffer.destroy();\n\t\t});\n\t\tthis.instanced?.clear();\n\t}\n\n\t/**\n\t * the first-init settings — the lit subclass overrides the key and\n\t * appends its normal attribute\n\t * @ignore\n\t */\n\tdefaultSettings() {\n\t\treturn {\n\t\t\tshaderKey: \"mesh\",\n\t\t\ttopology: \"triangle-list\",\n\t\t\tattributes: this.attributeLayout(),\n\t\t};\n\t}\n\n\t/**\n\t * the WGSL module text of this family\n\t * @ignore\n\t */\n\tshaderSource() {\n\t\treturn meshWGSL;\n\t}\n\n\t/**\n\t * The vertex attribute layout — identical to the WebGL mesh batcher's:\n\t * `aVertex` (3) + `aRegion` (2) + `aColor` (4 floats, deliberately not\n\t * unorm8x4 — the layout is shared with GL, where float colors dodge\n\t * NaN-pattern canonicalization on Metal-backed drivers) = 9 floats.\n\t * @ignore\n\t */\n\tattributeLayout() {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"aVertex\",\n\t\t\t\tformat: \"float32x3\",\n\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"aRegion\",\n\t\t\t\tformat: \"float32x2\",\n\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"aColor\",\n\t\t\t\tformat: \"float32x4\",\n\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * the shape signature keying the group-3 layout in the pipeline cache\n\t * @ignore\n\t */\n\tuniformSignature() {\n\t\treturn `mesh:u${MESH_UNIFORM_SIZE}`;\n\t}\n\n\t/**\n\t * The shader family for the next recorded draw: the built-in family,\n\t * or the hosted custom module when `renderer.drawMesh` routed a\n\t * WGSL-carrying {@link GLShader} here — registered lazily against\n\t * THIS host's vertex layout and group list, so one instance serves\n\t * the unlit and lit hosts as distinct families. An invalid module\n\t * (failed async validation) falls back to the built-in family: the\n\t * mesh keeps drawing, default-shaded — the ShaderEffect\n\t * warn-and-degrade contract.\n\t * @returns {string} the pipeline-cache family key\n\t * @ignore\n\t */\n\tactiveShaderKey() {\n\t\tconst custom = this.customShader;\n\t\tif (custom === null) {\n\t\t\treturn this.shaderKey;\n\t\t}\n\t\t// registerWGSL owns the epoch/validity bookkeeping (a fresh device\n\t\t// re-validates an invalidated module) and returns null while invalid\n\t\tconst cache = this.renderer.pipelineCache;\n\t\tconst key = custom.registerWGSL(\n\t\t\tcache,\n\t\t\tthis.vertexLayoutKey,\n\t\t\tthis.bindGroupLayoutList(cache),\n\t\t\tthis.vertexLayoutKey,\n\t\t);\n\t\treturn key ?? this.shaderKey;\n\t}\n\n\t/**\n\t * the positional bind-group-layout list for this family — the lit\n\t * subclass swaps the group-2 filler for its light-block layout\n\t * @ignore\n\t */\n\tbindGroupLayoutList(cache) {\n\t\treturn [\n\t\t\tcache.frameLayout,\n\t\t\tcache.meshMaterialLayout,\n\t\t\tcache.emptyLayout,\n\t\t\tthis.meshLayout,\n\t\t];\n\t}\n\n\t/**\n\t * Resolve the group-1 material for a mesh: its texture through the\n\t * texture store, honouring the mesh's own `textureRepeat` wrap override\n\t * (#1503 — sampler state per use, never a mutation of the shared\n\t * per-image atlas). A material change with vertices pending flushes\n\t * them under the previous binding.\n\t * @param {object} mesh - the mesh whose material should be applied\n\t * @param {TextureAtlas} [texture] - bind this texture instead of the mesh's\n\t * own — how a multi-material model's per-material `map_Kd` reaches the\n\t * sampler, one draw range at a time (#1573). The wrap override stays a\n\t * property of the mesh, not of the material group.\n\t * @ignore\n\t */\n\tapplyMeshMaterial(mesh, texture = mesh.texture) {\n\t\tconst renderer = this.renderer;\n\t\tconst filter =\n\t\t\ttypeof texture.filter === \"string\"\n\t\t\t\t? texture.filter\n\t\t\t\t: renderer.getDefaultTextureFilter();\n\t\t// Four bindings always. A mesh with no `map_d` binds its own diffuse\n\t\t// texture into the second pair as filler: the shader weights that\n\t\t// sample by `hasAlphaMap`, so the value is discarded, and reusing a\n\t\t// texture already resident costs no extra unit and no extra upload.\n\t\tconst material = renderer.textureStore.getMeshBinding(\n\t\t\ttexture,\n\t\t\tmesh.alphaMap ?? texture,\n\t\t\t{\n\t\t\t\trepeat: mesh.textureRepeat,\n\t\t\t\t// mesh textures sample a generated mip chain — trilinear\n\t\t\t\t// minification keeps distant geometry from shimmering, while\n\t\t\t\t// \"nearest\" opts out (crisp pixel-art models) and 2D consumers\n\t\t\t\t// of the same image stay lod-clamped to level 0\n\t\t\t\tmipmaps: filter === \"linear\",\n\t\t\t},\n\t\t);\n\t\t// a source that never became resident returns null — keep the previous\n\t\t// binding rather than recording a draw against nothing\n\t\tif (material !== null && material !== this.currentMaterial) {\n\t\t\tthis.flush();\n\t\t\tthis.currentMaterial = material;\n\t\t}\n\t}\n\n\t/**\n\t * Snapshot the per-draw uniforms — placement (`model`, `view`), tint,\n\t * and the mesh's material scalars (alpha cutoff, emissive) — into a\n\t * fresh dynamic-offset region of the effect uniform arena. The draws\n\t * recorded after this bind against exactly these bytes.\n\t * @param {Matrix3d} modelMatrix - the mesh's own placement, or identity\n\t *   when its vertices are already positioned\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @param {object} mesh - the mesh (alphaCutoff / emissive source)\n\t * @ignore\n\t */\n\tsetPlacementUniforms(modelMatrix, tint, mesh) {\n\t\tconst renderer = this.renderer;\n\t\tconst device = this.device;\n\t\tconst scratch = UNIFORM_SCRATCH;\n\t\tscratch.set(modelMatrix.val, 0);\n\t\tscratch.set(renderer.currentTransform.val, 16);\n\t\tscratch[32] = ((tint >>> 16) & 0xff) / 255;\n\t\tscratch[33] = ((tint >>> 8) & 0xff) / 255;\n\t\tscratch[34] = (tint & 0xff) / 255;\n\t\tscratch[35] = ((tint >>> 24) & 0xff) / 255;\n\t\tscratch[36] = mesh.alphaCutoff || 0;\n\t\tscratch[37] = 0;\n\t\tscratch[38] = 0;\n\t\tscratch[39] = 0;\n\t\tscratch[37] = mesh.alphaMap !== undefined ? 1 : 0;\n\t\tconst em = mesh.emissive;\n\t\tscratch[40] = em ? em[0] : 0;\n\t\tscratch[41] = em ? em[1] : 0;\n\t\tscratch[42] = em ? em[2] : 0;\n\t\tscratch[43] = 0;\n\t\t// specular: rgb = Ks, w = Ns. Gated on the exponent, not the colour —\n\t\t// `Ns` of 0 is the format's \"no highlight\" however bright `Ks` is.\n\t\tconst shininess = mesh.shininess || 0;\n\t\tconst ks = shininess > 0 ? mesh.specular : undefined;\n\t\tscratch[44] = ks ? ks[0] : 0;\n\t\tscratch[45] = ks ? ks[1] : 0;\n\t\tscratch[46] = ks ? ks[2] : 0;\n\t\tscratch[47] = shininess;\n\t\t// the camera's world position, which the specular half-vector needs.\n\t\t// A view matrix is rigid, so its inverse translation is -Rᵀ·t.\n\t\tconst v = renderer.currentTransform.val;\n\t\tscratch[48] = -(v[0] * v[12] + v[1] * v[13] + v[2] * v[14]);\n\t\tscratch[49] = -(v[4] * v[12] + v[5] * v[13] + v[6] * v[14]);\n\t\tscratch[50] = -(v[8] * v[12] + v[9] * v[13] + v[10] * v[14]);\n\t\tscratch[51] = 0;\n\n\t\tconst region = renderer.effectUniformArena.alloc(\n\t\t\tMESH_UNIFORM_SIZE,\n\t\t\tdevice.limits.minUniformBufferOffsetAlignment,\n\t\t);\n\t\tdevice.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tscratch.buffer,\n\t\t\t0,\n\t\t\tMESH_UNIFORM_SIZE,\n\t\t);\n\t\tlet bindGroup = this.uniformBindGroups.get(region.buffer);\n\t\tif (typeof bindGroup === \"undefined\") {\n\t\t\tbindGroup = device.createBindGroup({\n\t\t\t\tlabel: \"melonJS mesh uniforms\",\n\t\t\t\tlayout: this.meshLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: { buffer: region.buffer, size: MESH_UNIFORM_SIZE },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tthis.uniformBindGroups.set(region.buffer, bindGroup);\n\t\t}\n\t\tthis.uniformBinding = { bindGroup, dynamicOffset: region.offset };\n\t}\n\n\t/**\n\t * Write one vertex into the staging buffer. The base (unlit) layout is\n\t * `x, y, z, u, v, color` — subclasses override to append per-vertex\n\t * data matching their attribute layout.\n\t * @ignore\n\t */\n\tpushVertex(vertexData, x, y, z, u, v, color, _mesh, _i3) {\n\t\tvertexData.pushMesh(x, y, z, u, v, color);\n\t}\n\n\t/**\n\t * Add a textured mesh to the batch (the accumulated path: vertices\n\t * already CPU-projected, identity model matrix). Chunks triangles\n\t * across flushes through the shared versioned-remap dedup, exactly\n\t * like the WebGL batcher. Multi-material vertex colors ride `aColor`;\n\t * the runtime tint stays a uniform.\n\t * @param {object} mesh - a Mesh with vertices, uvs, indices, texture\n\t * @param {number} tint - tint color in UINT32 (argb) format\n\t */\n\taddMesh(mesh, tint) {\n\t\tthis.updatePassState();\n\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t\tthis.setPlacementUniforms(IDENTITY_MATRIX, tint, mesh);\n\t\t\tthis.accumulateRange(mesh, 0, mesh.indices.length);\n\t\t\treturn;\n\t\t}\n\t\t// Multi-material with per-material textures (#1573): one accumulation\n\t\t// pass per range. `applyMeshMaterial` flushes on a material change, so\n\t\t// the previous range's vertices are recorded under the binding they\n\t\t// were accumulated for — and the uniforms are re-armed after that\n\t\t// flush, never before it.\n\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\tthis.setPlacementUniforms(IDENTITY_MATRIX, tint, mesh);\n\t\t\tthis.accumulateRange(mesh, slices[i].start, slices[i].count);\n\t\t}\n\t}\n\n\t/**\n\t * Accumulate one index range of a mesh into the batch, chunking triangles\n\t * across flushes as the vertex / index staging arrays fill.\n\t * @param {object} mesh - a Mesh with vertices, uvs, indices, texture\n\t * @param {number} from - first index to accumulate\n\t * @param {number} length - how many indices to accumulate\n\t * @ignore\n\t */\n\taccumulateRange(mesh, from, length) {\n\t\tconst vertices = mesh.vertices;\n\t\tconst uvs = mesh.uvs;\n\t\tconst indices = mesh.indices;\n\t\tconst vertexColors = mesh.vertexColors;\n\t\tconst until = from + length;\n\n\t\tconst maxVerts = this.vertexData.maxVertex;\n\t\tconst maxIndices = this.indexData.length;\n\n\t\tensureRemapCapacity(mesh.vertexCount);\n\n\t\tlet triIdx = from;\n\t\twhile (triIdx < until) {\n\t\t\tconst vertexData = this.vertexData;\n\t\t\tconst availVerts = maxVerts - vertexData.vertexCount;\n\t\t\tconst availIndices = maxIndices - this.indexCount;\n\t\t\t// each triangle needs at most 3 new vertices and 3 indices\n\t\t\tconst maxTris = Math.min(\n\t\t\t\tMath.floor(availVerts / 3),\n\t\t\t\tMath.floor(availIndices / 3),\n\t\t\t);\n\n\t\t\tif (maxTris === 0) {\n\t\t\t\tthis.flush();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst endIdx = Math.min(triIdx + maxTris * 3, until);\n\n\t\t\tconst baseOffset = vertexData.vertexCount;\n\t\t\tconst chunkIndices = beginChunk();\n\t\t\tlet localCount = 0;\n\n\t\t\tfor (let j = triIdx; j < endIdx; j++) {\n\t\t\t\tconst origIdx = indices[j];\n\t\t\t\tlet localIdx = remapIndex(origIdx);\n\t\t\t\tif (localIdx === -1) {\n\t\t\t\t\tlocalIdx = localCount++;\n\t\t\t\t\tassignIndex(origIdx, localIdx);\n\n\t\t\t\t\tconst i3 = origIdx * 3;\n\t\t\t\t\tconst i2 = origIdx * 2;\n\t\t\t\t\tconst vertColor = vertexColors ? vertexColors[origIdx] : 0xffffffff;\n\t\t\t\t\tthis.pushVertex(\n\t\t\t\t\t\tvertexData,\n\t\t\t\t\t\tvertices[i3],\n\t\t\t\t\t\tvertices[i3 + 1],\n\t\t\t\t\t\tvertices[i3 + 2],\n\t\t\t\t\t\tuvs[i2],\n\t\t\t\t\t\tuvs[i2 + 1],\n\t\t\t\t\t\tvertColor,\n\t\t\t\t\t\tmesh,\n\t\t\t\t\t\ti3,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tchunkIndices.push(baseOffset + localIdx);\n\t\t\t}\n\n\t\t\t// append the chunk's absolute indices to the staging array\n\t\t\tlet o = this.indexCount;\n\t\t\tfor (let i = 0; i < chunkIndices.length; i++) {\n\t\t\t\tthis.indexData[o++] = chunkIndices[i];\n\t\t\t}\n\t\t\tthis.indexCount = o;\n\t\t\ttriIdx = endIdx;\n\t\t}\n\t}\n\n\t/**\n\t * Record the pending mesh vertices as one indexed draw: vertex bytes\n\t * into the vertex arena, index bytes into the index arena, pipeline\n\t * from the mesh family with the depth/cull axes and blend forced\n\t * \"none\" (the GL mesh mode disables BLEND).\n\t * @override\n\t */\n\tflush() {\n\t\tconst vertexData = this.vertexData;\n\t\tconst vertexCount = vertexData.vertexCount;\n\t\tconst indexCount = this.indexCount;\n\t\tif (vertexCount === 0 || indexCount === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst renderer = this.renderer;\n\t\tconst device = this.device;\n\t\tconst pass = renderer.ensurePass();\n\n\t\tconst byteLength = vertexCount * this.stride;\n\t\tconst vertexRegion = renderer.vertexArena.alloc(byteLength);\n\t\tdevice.queue.writeBuffer(\n\t\t\tvertexRegion.buffer,\n\t\t\tvertexRegion.offset,\n\t\t\tvertexData.toUint8(),\n\t\t\t0,\n\t\t\tbyteLength,\n\t\t);\n\n\t\tconst indexBytes = indexCount * 4;\n\t\tconst indexRegion = renderer.indexArena.alloc(indexBytes);\n\t\tdevice.queue.writeBuffer(\n\t\t\tindexRegion.buffer,\n\t\t\tindexRegion.offset,\n\t\t\tthis.indexData.buffer,\n\t\t\t0,\n\t\t\tindexBytes,\n\t\t);\n\n\t\t// the accumulated path is opaque; a blended draw only reaches the\n\t\t// retained path, so this must never inherit a stale flag\n\t\tthis.meshState.depthWrite = undefined;\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.activeShaderKey(),\n\t\t\t\"triangle-list\",\n\t\t\t// mesh mode never blends — occlusion comes from the depth test\n\t\t\t\"none\",\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t\tthis.meshState,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\tthis.bindLights(pass);\n\t\tpass.setBindGroup(3, this.uniformBinding.bindGroup, [\n\t\t\tthis.uniformBinding.dynamicOffset,\n\t\t]);\n\t\tpass.setVertexBuffer(\n\t\t\t0,\n\t\t\tvertexRegion.buffer,\n\t\t\tvertexRegion.offset,\n\t\t\tbyteLength,\n\t\t);\n\t\tpass.setIndexBuffer(\n\t\t\tindexRegion.buffer,\n\t\t\t\"uint32\",\n\t\t\tindexRegion.offset,\n\t\t\tindexBytes,\n\t\t);\n\t\tpass.drawIndexed(indexCount);\n\n\t\tvertexData.clear();\n\t\tthis.indexCount = 0;\n\t}\n\n\t/**\n\t * bind group 2 — the unlit family interposes the shared empty group;\n\t * the lit subclass overrides with its light block\n\t * @ignore\n\t */\n\tbindLights(pass) {\n\t\tpass.setBindGroup(2, this.renderer.pipelineCache.emptyBindGroup);\n\t}\n\n\t/**\n\t * Write one mesh's model-space geometry into `out` in this batcher's\n\t * vertex layout, returning how many floats were written — the shared\n\t * neutral builder (`gpu/meshvertex.ts`), one copy for both backends.\n\t * @param {object} mesh - the mesh to read geometry from\n\t * @param {Float32Array} out - destination scratch\n\t * @returns {number} number of floats written\n\t * @ignore\n\t */\n\tbuildRetainedVertexData(mesh, out) {\n\t\treturn buildMeshVertexData(mesh, out, this.vertexSize);\n\t}\n\n\t/**\n\t * Get this mesh's retained geometry, building or refreshing it when\n\t * the mesh's geometry version has moved on.\n\t * @param {object} mesh - the mesh whose geometry is wanted\n\t * @returns {WebGPURetainedGeometry} up-to-date geometry for the mesh\n\t * @ignore\n\t */\n\tretainedGeometryFor(mesh) {\n\t\tlet geometry = this.retained.get(mesh);\n\t\tif (geometry === undefined) {\n\t\t\tgeometry = new WebGPURetainedGeometry(this.renderer);\n\t\t\tthis.retained.set(mesh, geometry);\n\t\t}\n\t\tconst version = mesh._geometryVersion ?? 0;\n\t\tif (geometry.uploadedVersion !== version) {\n\t\t\tconst scratch = retainedScratch(mesh.vertexCount * this.vertexSize);\n\t\t\tconst floats = this.buildRetainedVertexData(mesh, scratch);\n\t\t\tgeometry.upload(scratch, floats, mesh._indicesOriginal, version);\n\t\t}\n\t\treturn geometry;\n\t}\n\n\t/**\n\t * Draw a mesh from its retained geometry: bind the persistent buffers\n\t * and record one indexed draw, with placement supplied entirely by the\n\t * per-draw uniform snapshot. Unlike {@link WebGPUMeshBatcher#addMesh}\n\t * this accumulates nothing and never chunks — the whole mesh is one\n\t * draw regardless of size, except for a multi-material model whose\n\t * materials carry different diffuse textures: that records one indexed\n\t * range per entry in {@link Mesh#textureGroups} (#1573), over the same\n\t * buffers, with only the group-1 material binding moving between them.\n\t * @param {object} mesh - the mesh to draw\n\t * @param {Matrix3d} modelMatrix - where the mesh sits in the world\n\t * @param {number} tint - tint colour in UINT32 (argb) format\n\t * @ignore\n\t */\n\tdrawRetainedMesh(mesh, modelMatrix, tint) {\n\t\t// anything queued must land first, or this draw would reorder\n\t\t// ahead of it\n\t\tthis.flush();\n\n\t\tthis.updatePassState();\n\t\t// on the split path every range resolves its own binding below, so\n\t\t// resolving the mesh-level one here would reserve a texture unit (and\n\t\t// possibly run a first-use upload plus mip generation) for a binding\n\t\t// that is immediately overwritten\n\t\tif (mesh.textureGroups === undefined) {\n\t\t\tthis.applyMeshMaterial(mesh);\n\t\t}\n\t\tthis.setPlacementUniforms(modelMatrix, tint, mesh);\n\n\t\tconst renderer = this.renderer;\n\t\tconst pass = renderer.ensurePass();\n\t\tconst geometry = this.retainedGeometryFor(mesh);\n\n\t\t// A blended mesh (the ground shadow, #1515) keeps the depth TEST but\n\t\t// stops writing depth, so overlapping shadows blend instead of\n\t\t// fighting. Set per draw, never left behind: an ordinary mesh must\n\t\t// resolve to exactly the pipeline it always did.\n\t\tconst blended = mesh._blendedDraw === true;\n\t\t// `undefined` rather than `true` for the ordinary case: the axis reads\n\t\t// `!== false`, so leaving it unset keeps `meshState` byte-for-byte what\n\t\t// it was before this existed, and the pipeline key gains nothing\n\t\tthis.meshState.depthWrite = blended ? false : undefined;\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.activeShaderKey(),\n\t\t\t\"triangle-list\",\n\t\t\tblended ? \"normal\" : \"none\",\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t\tthis.meshState,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tthis.bindLights(pass);\n\t\tpass.setBindGroup(3, this.uniformBinding.bindGroup, [\n\t\t\tthis.uniformBinding.dynamicOffset,\n\t\t]);\n\t\tpass.setVertexBuffer(0, geometry.vertexBuffer);\n\t\tpass.setIndexBuffer(geometry.indexBuffer, geometry.indexFormat);\n\t\tconst slices = mesh.textureGroups;\n\t\tif (slices === undefined) {\n\t\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\t\tpass.drawIndexed(geometry.indexCount);\n\t\t} else {\n\t\t\tfor (let i = 0; i < slices.length; i++) {\n\t\t\t\t// nothing is queued at this point, so the flush this may run is\n\t\t\t\t// a no-op — it is here for the material tracking, not the flush\n\t\t\t\tthis.applyMeshMaterial(mesh, slices[i].texture);\n\t\t\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\t\t\tpass.drawIndexed(slices[i].count, 1, slices[i].start);\n\t\t\t}\n\t\t}\n\n\t\t// stamp: a version bump later this frame must go to fresh buffers\n\t\tgeometry.lastDrawnFrameId = renderer.frameId;\n\t}\n\n\t/**\n\t * Release the retained geometry held for one mesh, if any.\n\t * @param {object} mesh - the mesh whose geometry should be freed\n\t * @ignore\n\t */\n\treleaseRetained(mesh) {\n\t\tthis.releaseInstanced(mesh);\n\t\tconst geometry = this.retained.get(mesh);\n\t\tif (geometry !== undefined) {\n\t\t\tgeometry.destroy();\n\t\t\tthis.retained.delete(mesh);\n\t\t}\n\t}\n\n\t/**\n\t * Release every retained geometry this batcher holds.\n\t * @ignore\n\t */\n\treleaseAllRetained() {\n\t\tthis.releaseAllInstanced();\n\t\tthis.retained.forEach((geometry) => {\n\t\t\tgeometry.destroy();\n\t\t});\n\t\tthis.retained.clear();\n\t}\n\n\t/**\n\t * @override\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.indexCount = 0;\n\t\tthis.currentMaterial = null;\n\t\tthis.customShader = null;\n\t\tthis.uniformBinding = null;\n\t\tthis.uniformBindGroups.clear();\n\t\tthis.releaseAllRetained();\n\t}\n\n\t/**\n\t * @override\n\t */\n\tdestroy() {\n\t\tthis.reset();\n\t\tsuper.destroy();\n\t}\n}\n","import state from \"../../../state/state.ts\";\nimport { buildLitMeshVertexData } from \"../../gpu/meshvertex.ts\";\n// the lighting math modules are pure CPU code shared with the GL backend\n// (published std140 layout, no GL calls)\nimport { packMeshLights } from \"../../webgl/lighting/pack3d.ts\";\nimport {\n\tBLOCK3D_BYTES,\n\tBLOCK3D_FLOATS,\n\twriteLight3dBlock,\n} from \"../../webgl/lighting/std140.ts\";\nimport { LIT_INSTANCED } from \"../shaders/mesh-instanced.js\";\nimport litMeshWGSL from \"../shaders/mesh-lit.wgsl\";\nimport WebGPUMeshBatcher from \"./mesh_batcher.js\";\n\n// ambient used when a lit mesh is drawn with no active 3D lights at all —\n// render it fullbright (white ambient) rather than dark, so a `lit` mesh in\n// a scene without lights still looks like the unlit path.\nconst WHITE_AMBIENT = new Float32Array([1, 1, 1]);\n\n/**\n * The WebGPU lit mesh batcher — meshes shaded by the active stage's\n * {@link Light3d} lights (half-Lambert diffuse from directional lights +\n * ambient), the port of the GL `LitMeshBatcher`:\n *\n * - adds the `aNormal` vertex attribute (48-byte layout vs 36) and the lit\n *   shader family; meshes opt in via `mesh.lit`, so unlit meshes keep the\n *   lean base batcher and pay nothing for lighting\n * - the std140 `Light3dBlock` binds at group 2 with a dynamic offset. The\n *   GL batcher re-packs and dirty-compares per draw\n *   (`UniformBlock.upload`); here a change lands in a FRESH arena region\n *   (queue-write law — an in-place write would retroactively re-light\n *   draws already recorded this frame), and an unchanged rig re-uses the\n *   frame's existing snapshot at zero upload cost\n * @augments WebGPUMeshBatcher\n * @category Rendering\n */\nexport default class WebGPULitMeshBatcher extends WebGPUMeshBatcher {\n\t/**\n\t * @override\n\t */\n\tinit(renderer, settings) {\n\t\t// a new device invalidates the device-scoped lights layout — drop\n\t\t// it so it is recreated below (base init sets this.device)\n\t\tif (renderer.device !== this.device) {\n\t\t\tthis.lightsLayout = undefined;\n\t\t}\n\t\tsuper.init(renderer, settings);\n\t\t// the base init may have short-circuited on an already-registered\n\t\t// module (never calling bindGroupLayoutList) — the snapshot path\n\t\t// still needs a live layout on THIS instance\n\t\tthis.ensureLightsLayout();\n\n\t\t// CPU staging for the std140 block + the bytes of the frame's last\n\t\t// snapshot (the dirty compare)\n\t\tthis.blockData = new Float32Array(BLOCK3D_FLOATS);\n\t\tthis.uploadedBlock = new Float32Array(BLOCK3D_FLOATS);\n\t\t// light-block bind group per arena page (pages persist across frames)\n\t\tthis.lightBindGroups = new Map();\n\t\t// the current snapshot binding: {bindGroup, dynamicOffset, frameId}\n\t\tthis.lightBinding = null;\n\t}\n\n\t/**\n\t * @override\n\t * @ignore\n\t */\n\tdefaultSettings() {\n\t\tconst settings = super.defaultSettings();\n\t\tsettings.shaderKey = \"meshLit\";\n\t\treturn settings;\n\t}\n\n\t/**\n\t * @override\n\t * @ignore\n\t */\n\t/**\n\t * the lit tier's instanced variant: its geometry attributes run to\n\t * location 3 (the normal), so the instance slots start at 4\n\t * @ignore\n\t */\n\tinstancedVariant() {\n\t\treturn LIT_INSTANCED;\n\t}\n\n\tshaderSource() {\n\t\treturn litMeshWGSL;\n\t}\n\n\t/** add the normal attribute on top of the base layout. @ignore */\n\tattributeLayout() {\n\t\tconst attributes = super.attributeLayout();\n\t\tattributes.push({\n\t\t\t// aNormal: model-space on the retained path (the shader rotates\n\t\t\t// it), world-space on the accumulated path (identity model)\n\t\t\tname: \"aNormal\",\n\t\t\tformat: \"float32x3\",\n\t\t\toffset: 9 * Float32Array.BYTES_PER_ELEMENT,\n\t\t});\n\t\treturn attributes;\n\t}\n\n\t/**\n\t * build the light-block layout once per device (init drops it when the\n\t * device changes)\n\t * @ignore\n\t */\n\tensureLightsLayout() {\n\t\tif (typeof this.lightsLayout === \"undefined\") {\n\t\t\tthis.lightsLayout = this.device.createBindGroupLayout({\n\t\t\t\tlabel: \"melonJS light3d block\",\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\t\tbuffer: {\n\t\t\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\t\t\tminBindingSize: BLOCK3D_BYTES,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t}\n\t\treturn this.lightsLayout;\n\t}\n\n\t/**\n\t * the lit family swaps the group-2 filler for the light-block layout\n\t * @override\n\t * @ignore\n\t */\n\tbindGroupLayoutList(cache) {\n\t\treturn [\n\t\t\tcache.frameLayout,\n\t\t\tcache.meshMaterialLayout,\n\t\t\tthis.ensureLightsLayout(),\n\t\t\tthis.meshLayout,\n\t\t];\n\t}\n\n\t/** push the 12-float lit vertex, appending the world-space normal. @ignore */\n\tpushVertex(vertexData, x, y, z, u, v, color, mesh, i3) {\n\t\tconst n = mesh.normals;\n\t\tvertexData.pushMeshLit(\n\t\t\tx,\n\t\t\ty,\n\t\t\tz,\n\t\t\tu,\n\t\t\tv,\n\t\t\tcolor,\n\t\t\tn ? n[i3] : 0,\n\t\t\tn ? n[i3 + 1] : 0,\n\t\t\tn ? n[i3 + 2] : 0,\n\t\t);\n\t}\n\n\t/**\n\t * Same as the base layout plus the model-space normal (zero-normal\n\t * guarded), via the shared neutral builder — see `gpu/meshvertex.ts`.\n\t * @param {object} mesh - the mesh to read geometry from\n\t * @param {Float32Array} out - destination scratch\n\t * @returns {number} number of floats written\n\t * @override\n\t * @ignore\n\t */\n\tbuildRetainedVertexData(mesh, out) {\n\t\treturn buildLitMeshVertexData(mesh, out, this.vertexSize);\n\t}\n\n\t/**\n\t * Refresh the light snapshot before a draw. Per draw and not per bind,\n\t * for the same reason as the GL batcher: `setBatcher` early-returns\n\t * when this batcher is already current, so a purely-lit scene would\n\t * otherwise shade every frame with its first frame's lights.\n\t *\n\t * Cost when the rig is static: one pack plus one array compare, zero\n\t * queue writes — the frame's existing snapshot region is re-bound.\n\t * With NO lights at all a white ambient keeps a `lit` mesh fullbright\n\t * (matching the unlit path); an ambient-only scene uses its real\n\t * ambient.\n\t * @override\n\t * @ignore\n\t */\n\tupdatePassState() {\n\t\tconst renderer = this.renderer;\n\t\tconst stage = state.current();\n\t\tconst lit = packMeshLights(stage ? stage._activeLights3d : null);\n\t\tconst a = lit.ambient;\n\t\tconst hasLight = lit.count > 0 || a[0] > 0 || a[1] > 0 || a[2] > 0;\n\t\twriteLight3dBlock(this.blockData, {\n\t\t\tcount: lit.count,\n\t\t\tposRange: lit.posRange,\n\t\t\tdirCone: lit.dirCone,\n\t\t\tcolorInner: lit.colorInner,\n\t\t\tambient: hasLight ? a : WHITE_AMBIENT,\n\t\t});\n\n\t\t// an unchanged block within the frame re-uses its snapshot\n\t\tif (\n\t\t\tthis.lightBinding !== null &&\n\t\t\tthis.lightBinding.frameId === renderer.frameId\n\t\t) {\n\t\t\tconst prev = this.uploadedBlock;\n\t\t\tconst next = this.blockData;\n\t\t\tlet dirty = false;\n\t\t\tfor (let i = 0; i < BLOCK3D_FLOATS; i++) {\n\t\t\t\tif (prev[i] !== next[i]) {\n\t\t\t\t\tdirty = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dirty === false) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// a change (or a new frame — the arenas were reset) snapshots into\n\t\t// a fresh region: draws already recorded keep their own bytes\n\t\tconst device = this.device;\n\t\tconst region = renderer.effectUniformArena.alloc(\n\t\t\tBLOCK3D_BYTES,\n\t\t\tdevice.limits.minUniformBufferOffsetAlignment,\n\t\t);\n\t\tdevice.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tthis.blockData.buffer,\n\t\t\t0,\n\t\t\tBLOCK3D_BYTES,\n\t\t);\n\t\tlet bindGroup = this.lightBindGroups.get(region.buffer);\n\t\tif (typeof bindGroup === \"undefined\") {\n\t\t\tbindGroup = device.createBindGroup({\n\t\t\t\tlabel: \"melonJS light3d block\",\n\t\t\t\tlayout: this.lightsLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: { buffer: region.buffer, size: BLOCK3D_BYTES },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tthis.lightBindGroups.set(region.buffer, bindGroup);\n\t\t}\n\t\tthis.lightBinding = {\n\t\t\tbindGroup,\n\t\t\tdynamicOffset: region.offset,\n\t\t\tframeId: renderer.frameId,\n\t\t};\n\t\tthis.uploadedBlock.set(this.blockData);\n\t}\n\n\t/**\n\t * lit draws bind the light block at group 2\n\t * @override\n\t * @ignore\n\t */\n\tbindLights(pass) {\n\t\t// a stale or missing snapshot (a flush outside the drawMesh bracket,\n\t\t// a new frame) refreshes first — the arena region it pointed into\n\t\t// was reset\n\t\tif (\n\t\t\tthis.lightBinding === null ||\n\t\t\tthis.lightBinding.frameId !== this.renderer.frameId\n\t\t) {\n\t\t\tthis.updatePassState();\n\t\t}\n\t\tpass.setBindGroup(2, this.lightBinding.bindGroup, [\n\t\t\tthis.lightBinding.dynamicOffset,\n\t\t]);\n\t}\n\n\t/**\n\t * @override\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.lightBindGroups.clear();\n\t\tthis.lightBinding = null;\n\t}\n}\n","// melonJS WebGPU lit quad shader — the WGSL port of quad-multi-lit.vert /\n// multitexture-lit.js under this backend's single-texture-per-draw-segment\n// model: ONE color texture and ONE normal map bound per flush (group 1),\n// so the GL sampler ladder and the per-vertex normal-texture id are gone.\n// The std140 Light2dBlock binds at group 2 with a dynamic offset — one\n// snapshot per setLightUniforms call, per the queue-write ordering law.\n//\n// Vertex layout: the frozen 28-byte quad layout, unchanged.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block\n\tlineWidth : f32,\n};\n\nstruct Light2dData {\n\t// x, y, radius, intensity\n\tposRadiusIntensity : vec4f,\n\t// r, g, b, lightHeight\n\tcolorHeight : vec4f,\n};\n\n// the std140 layout published by src/video/webgl/lighting/std140.ts:\n// header = count + pad (vec4), ambient rgb + pad (vec4), then 32 lights\n// of 2 vec4 each — 1056 bytes total\nstruct Light2dBlock {\n\tcountPad : vec4f,\n\tambient : vec4f,\n\tlights : array<Light2dData, 32>,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n@group(1) @binding(2) var uNormal : texture_2d<f32>;\n@group(1) @binding(3) var uNormalSampler : sampler;\n@group(2) @binding(0) var<uniform> uLights : Light2dBlock;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n\t// pre-projection coordinates — the space packLights translates the\n\t// light positions into (world after the camera translate)\n\t@location(2) vWorldPos : vec2f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aTextureId : f32,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet clip = uFrame.projection * vec4f(aVertex, 1.0);\n\t// GL-convention clip z in [-w, w] remapped to WebGPU's [0, w]\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\n\tout.vRegion = aRegion;\n\tout.vWorldPos = aVertex.xy;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\tlet color = textureSample(uTexture, uSampler, in.vRegion);\n\t// the normal map is UV-paired with the color atlas — same vRegion\n\tlet normalSample = textureSample(uNormal, uNormalSampler, in.vRegion);\n\n\t// Decode 0..1 -> -1..1. Normal maps use the Y-up authoring convention\n\t// but screen space here is Y-down — flip Y so dot(normal, lightDir)\n\t// runs in one coherent coordinate system (GL-backend parity).\n\tvar normal = normalize(normalSample.rgb * 2.0 - vec3f(1.0));\n\tnormal.y = -normal.y;\n\n\tvar lighting = uLights.ambient.rgb;\n\tlet count = min(i32(uLights.countPad.x), 32);\n\tfor (var i = 0; i < count; i = i + 1) {\n\t\tlet lp = uLights.lights[i].posRadiusIntensity;\n\t\tlet ch = uLights.lights[i].colorHeight;\n\t\tlet toLight = lp.xy - in.vWorldPos;\n\t\tlet dist = length(toLight);\n\t\t// quadratic attenuation over [0, radius]: a wider plateau near the\n\t\t// light and a softer feathered edge than the linear formula\n\t\tlet linearAtt = max(0.0, 1.0 - dist / max(lp.z, 1.0));\n\t\tlet att = linearAtt * linearAtt;\n\t\tlet lightDir = normalize(vec3f(toLight, ch.w));\n\t\tlet ndotl = max(0.0, dot(normal, lightDir));\n\t\tlighting = lighting + ch.rgb * (lp.w * att * ndotl);\n\t}\n\n\treturn vec4f(color.rgb * lighting, color.a) * in.vColor;\n}\n","/**\n * The WebGPU realization of a ShaderEffect at draw time: building the\n * device-scoped state (module registration, group-3 layout, resident\n * textures) lazily and per pipeline-cache epoch, and producing — per bind —\n * the group-3 bind group plus dynamic offsets over a fresh uniform\n * snapshot.\n *\n * Snapshot-per-bind is a correctness requirement, not a convenience:\n * `queue.writeBuffer` data executes before EVERY draw recorded in the\n * frame, so an effect bound twice in one frame with different uniform\n * values (a shared effect on two renderables, draw-time mutation) must\n * give each bind its own bytes — exactly the vertex arena's rationale,\n * applied to uniforms.\n * @ignore\n */\n\n/**\n * round up to a power-of-two alignment\n * @ignore\n */\nfunction alignUp(value, alignment) {\n\treturn (value + alignment - 1) & ~(alignment - 1);\n}\n\n/**\n * byte size of the engine MEBuiltins struct (3×vec2f, 16-byte rounded)\n * @ignore\n */\nconst ME_SIZE = 32;\n\n/**\n * (Re)build the device-scoped GPU state of a WGSL effect realization —\n * called lazily on first bind and again after a device loss (detected via\n * the pipeline-cache epoch).\n * @param {import(\"./webgpu_renderer.js\").default} renderer - the renderer\n * @param {import(\"../effects/wgsl_realization.js\").default} realization - the effect's WGSL realization\n * @ignore\n */\nfunction buildEffectGPU(renderer, effect, realization) {\n\tconst cache = renderer.pipelineCache;\n\tconst bindings = realization.builtinBindings;\n\tconst builtins = realization.builtins;\n\tconst hasUniform = realization.structSize > 0;\n\tconst hasME = builtins.noiseUV;\n\n\t// ---- the group-3 layout, shared per shape signature ------------------\n\tconst entries = [];\n\tconst signatureParts = [];\n\tif (hasUniform) {\n\t\tentries.push({\n\t\t\tbinding: 0,\n\t\t\tvisibility: GPUShaderStage.FRAGMENT | GPUShaderStage.VERTEX,\n\t\t\tbuffer: {\n\t\t\t\ttype: \"uniform\",\n\t\t\t\thasDynamicOffset: true,\n\t\t\t\tminBindingSize: realization.structSize,\n\t\t\t},\n\t\t});\n\t\tsignatureParts.push(`u${realization.structSize}`);\n\t}\n\tfor (const texture of realization.textures) {\n\t\tentries.push({\n\t\t\tbinding: texture.binding,\n\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\ttexture: {},\n\t\t});\n\t\tentries.push({\n\t\t\tbinding: texture.samplerBinding,\n\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\tsampler: {},\n\t\t});\n\t\tsignatureParts.push(`t${texture.binding}`);\n\t}\n\tif (hasME) {\n\t\tentries.push({\n\t\t\tbinding: bindings.me,\n\t\t\tvisibility: GPUShaderStage.VERTEX,\n\t\t\tbuffer: {\n\t\t\t\ttype: \"uniform\",\n\t\t\t\thasDynamicOffset: true,\n\t\t\t\tminBindingSize: ME_SIZE,\n\t\t\t},\n\t\t});\n\t\tsignatureParts.push(`me${bindings.me}`);\n\t}\n\tif (builtins.screenTexture) {\n\t\tentries.push({\n\t\t\tbinding: bindings.screenTexture,\n\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\ttexture: {},\n\t\t});\n\t\tsignatureParts.push(`st${bindings.screenTexture}`);\n\t\tif (builtins.screenSamplerClamp) {\n\t\t\tentries.push({\n\t\t\t\tbinding: bindings.screenSamplerClamp,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: {},\n\t\t\t});\n\t\t\t// every entry variant must contribute a signature token, or two\n\t\t\t// different shapes share one cached layout and bind groups break\n\t\t\tsignatureParts.push(\"sc\");\n\t\t}\n\t\tif (builtins.screenSamplerRepeat) {\n\t\t\tentries.push({\n\t\t\t\tbinding: bindings.screenSamplerRepeat,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: {},\n\t\t\t});\n\t\t\tsignatureParts.push(\"sr\");\n\t\t}\n\t}\n\n\tconst hasEffectGroup = entries.length > 0;\n\tlet effectLayout = null;\n\tlet layouts;\n\tif (hasEffectGroup) {\n\t\teffectLayout = cache.getEffectLayout(signatureParts.join(\"|\"), entries);\n\t\t// positional through group 3: the reserved lights slot (group 2)\n\t\t// interposes the shared empty layout\n\t\tlayouts = [\n\t\t\tcache.frameLayout,\n\t\t\tcache.materialLayout,\n\t\t\tcache.emptyLayout,\n\t\t\teffectLayout,\n\t\t];\n\t} else {\n\t\tlayouts = [cache.frameLayout, cache.materialLayout];\n\t}\n\n\t// identical module text (clones, same-body effects) registers once and\n\t// shares every pipeline; the shape signature being derived from that\n\t// same text keeps the pipeline layout consistent per key\n\tconst key = cache.registerShader(realization.code, {\n\t\tbindGroupLayouts: layouts,\n\t\tvertexLayoutKey: \"quad\",\n\t\tlabel: \"melonJS effect shader\",\n\t});\n\n\t// WGSL validation is asynchronous and never throws: a body that parsed\n\t// but fails compilation would otherwise invalidate every pass drawing\n\t// it, dropping whole frames forever. Surface it and disable the effect\n\t// — the same warn-and-disable contract as a missing-language body.\n\t// (Fire-and-forget: draws recorded before the result resolve harmlessly\n\t// against the invalid pipeline; once disabled, the effect is filtered.)\n\tconst module = cache.modules?.[key];\n\tif (typeof module?.getCompilationInfo === \"function\") {\n\t\tmodule\n\t\t\t.getCompilationInfo()\n\t\t\t.then((info) => {\n\t\t\t\tconst errors = info.messages.filter((message) => {\n\t\t\t\t\treturn message.type === \"error\";\n\t\t\t\t});\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`ShaderEffect: WGSL compilation failed — effect disabled\\n${errors\n\t\t\t\t\t\t\t.map((message) => {\n\t\t\t\t\t\t\t\treturn `  line ${message.lineNum}: ${message.message}`;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.join(\"\\n\")}`,\n\t\t\t\t\t);\n\t\t\t\t\teffect.enabled = false;\n\t\t\t\t\trealization.valid = false;\n\t\t\t\t\trealization.releaseGPU();\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {});\n\t}\n\n\t// the per-bind snapshot: uniform struct + ME block share ONE arena\n\t// region (dynamic offsets must each be minUniformBufferOffsetAlignment\n\t// aligned, so the ME slice starts at the next aligned boundary)\n\tconst uniformAlignment =\n\t\trenderer.device.limits?.minUniformBufferOffsetAlignment ?? 256;\n\tconst meLocalOffset = hasUniform\n\t\t? alignUp(realization.structSize, uniformAlignment)\n\t\t: 0;\n\tconst snapshotSize = hasME\n\t\t? meLocalOffset + ME_SIZE\n\t\t: hasUniform\n\t\t\t? realization.structSize\n\t\t\t: 0;\n\n\trealization.gpu = {\n\t\tepoch: cache.epoch,\n\t\tkey,\n\t\thasEffectGroup,\n\t\teffectLayout,\n\t\thasUniform,\n\t\thasME,\n\t\tuniformAlignment,\n\t\tmeLocalOffset,\n\t\tsnapshotSize,\n\t\t// per-entry resident textures for static setTexture sources\n\t\tresidentTextures: new Map(),\n\t\tbindGroup: null,\n\t\tbindKey: \"\",\n\t\tinvalidateBindGroup() {\n\t\t\tthis.bindGroup = null;\n\t\t\tthis.bindKey = \"\";\n\t\t},\n\t};\n}\n\n/**\n * upload a static setTexture source into a resident effect-owned texture\n * @ignore\n */\nfunction residentTexture(renderer, gpu, name, entry) {\n\tlet resident = gpu.residentTextures.get(name);\n\tif (typeof resident === \"undefined\" || resident.image !== entry.image) {\n\t\tif (typeof resident !== \"undefined\") {\n\t\t\trenderer.retireTexture(resident.texture);\n\t\t}\n\t\tconst width = entry.image.width || 1;\n\t\tconst height = entry.image.height || 1;\n\t\tconst texture = renderer.device.createTexture({\n\t\t\tlabel: \"melonJS effect texture\",\n\t\t\tsize: [width, height],\n\t\t\tformat: \"rgba8unorm\",\n\t\t\tusage:\n\t\t\t\tGPUTextureUsage.TEXTURE_BINDING |\n\t\t\t\tGPUTextureUsage.COPY_DST |\n\t\t\t\tGPUTextureUsage.RENDER_ATTACHMENT,\n\t\t});\n\t\trenderer.device.queue.copyExternalImageToTexture(\n\t\t\t{ source: entry.image },\n\t\t\t// effect inputs keep raw texel values (GL parity: uploaded with\n\t\t\t// premultiply OFF)\n\t\t\t{ texture, premultipliedAlpha: false },\n\t\t\t[width, height],\n\t\t);\n\t\tresident = { image: entry.image, texture, view: texture.createView() };\n\t\tgpu.residentTextures.set(name, resident);\n\t}\n\treturn resident;\n}\n\n/**\n * Prepare an effect for a draw: ensure device state, snapshot the uniform\n * mirror(s) into the effect uniform arena, and return the pipeline family\n * key plus the group-3 binding for the pass.\n * @param {import(\"./webgpu_renderer.js\").default} renderer - the renderer\n * @param {import(\"../effects/shadereffect.js\").default} effect - the effect to bind\n * @returns {{key: string, hasEffectGroup: boolean, bindGroup: GPUBindGroup|null, dynamicOffsets: number[]}|null}\n *   the binding, or null when the effect has no WGSL realization\n * @ignore\n */\nexport function prepareEffectBinding(renderer, effect) {\n\tconst realization = effect.wgslRealization;\n\tif (typeof realization === \"undefined\" || !realization.valid) {\n\t\treturn null;\n\t}\n\tif (\n\t\trealization.gpu === null ||\n\t\trealization.gpu.epoch !== renderer.pipelineCache.epoch\n\t) {\n\t\tbuildEffectGPU(renderer, effect, realization);\n\t}\n\tconst gpu = realization.gpu;\n\tif (!gpu.hasEffectGroup) {\n\t\treturn {\n\t\t\tkey: gpu.key,\n\t\t\thasEffectGroup: false,\n\t\t\tbindGroup: null,\n\t\t\tdynamicOffsets: [],\n\t\t};\n\t}\n\n\t// ---- snapshot the CPU mirrors into a fresh arena region --------------\n\tconst dynamicOffsets = [];\n\tlet region = null;\n\tif (gpu.snapshotSize > 0) {\n\t\tregion = renderer.effectUniformArena.alloc(\n\t\t\tgpu.snapshotSize,\n\t\t\tgpu.uniformAlignment,\n\t\t);\n\t\tif (gpu.hasUniform) {\n\t\t\trenderer.device.queue.writeBuffer(\n\t\t\t\tregion.buffer,\n\t\t\t\tregion.offset,\n\t\t\t\trealization.cpu,\n\t\t\t\t0,\n\t\t\t\trealization.structSize,\n\t\t\t);\n\t\t\tdynamicOffsets.push(region.offset);\n\t\t}\n\t\tif (gpu.hasME) {\n\t\t\trenderer.device.queue.writeBuffer(\n\t\t\t\tregion.buffer,\n\t\t\t\tregion.offset + gpu.meLocalOffset,\n\t\t\t\trealization.meMirror.buffer,\n\t\t\t\t0,\n\t\t\t\tME_SIZE,\n\t\t\t);\n\t\t\tdynamicOffsets.push(region.offset + gpu.meLocalOffset);\n\t\t}\n\t}\n\n\t// ---- (re)build the bind group when any referenced resource moved ----\n\tconst capture = renderer.captureTexture;\n\tconst bindKey = [\n\t\tregion === null ? \"-\" : region.buffer.label,\n\t\t// only screen_texture consumers key on the capture — a capture\n\t\t// reallocation must not rebuild unrelated effects' bind groups\n\t\trealization.builtins.screenTexture ? (capture?.generation ?? -1) : -1,\n\t\tgpu.residentTextures.size,\n\t].join(\"|\");\n\tlet stale = gpu.bindGroup === null || gpu.bindKey !== bindKey;\n\t// a live extra texture (frame capture passed via setTexture) re-keys on\n\t// its own generation\n\tif (!stale) {\n\t\tfor (const entry of effect._extraTextures.values()) {\n\t\t\tif (entry.live && entry.image.generation !== entry.boundGeneration) {\n\t\t\t\tstale = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (stale) {\n\t\tconst entries = [];\n\t\tif (gpu.hasUniform) {\n\t\t\tentries.push({\n\t\t\t\tbinding: 0,\n\t\t\t\tresource: { buffer: region.buffer, size: realization.structSize },\n\t\t\t});\n\t\t}\n\t\tfor (const texture of realization.textures) {\n\t\t\tconst entry = effect._extraTextures.get(texture.name);\n\t\t\tlet view;\n\t\t\tif (typeof entry === \"undefined\") {\n\t\t\t\t// declared but never set: bind the renderer's 1×1 stub so the\n\t\t\t\t// bind group stays valid (samples transparent black)\n\t\t\t\tview = renderer.getStubTextureView();\n\t\t\t} else if (entry.live === true) {\n\t\t\t\tview = entry.image.view ?? renderer.getStubTextureView();\n\t\t\t\tentry.boundGeneration = entry.image.generation;\n\t\t\t} else {\n\t\t\t\tview = residentTexture(renderer, gpu, texture.name, entry).view;\n\t\t\t}\n\t\t\tentries.push({ binding: texture.binding, resource: view });\n\t\t\tentries.push({\n\t\t\t\tbinding: texture.samplerBinding,\n\t\t\t\tresource: renderer.textureStore.getSampler(\n\t\t\t\t\t\"linear\",\n\t\t\t\t\tentry?.repeat ?? \"no-repeat\",\n\t\t\t\t),\n\t\t\t});\n\t\t}\n\t\tif (gpu.hasME) {\n\t\t\tentries.push({\n\t\t\t\tbinding: realization.builtinBindings.me,\n\t\t\t\tresource: { buffer: region.buffer, size: ME_SIZE },\n\t\t\t});\n\t\t}\n\t\tif (realization.builtins.screenTexture) {\n\t\t\tentries.push({\n\t\t\t\tbinding: realization.builtinBindings.screenTexture,\n\t\t\t\tresource: capture?.view ?? renderer.getStubTextureView(),\n\t\t\t});\n\t\t\tif (realization.builtins.screenSamplerClamp) {\n\t\t\t\tentries.push({\n\t\t\t\t\tbinding: realization.builtinBindings.screenSamplerClamp,\n\t\t\t\t\tresource: renderer.textureStore.getSampler(\"linear\", \"no-repeat\"),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (realization.builtins.screenSamplerRepeat) {\n\t\t\t\tentries.push({\n\t\t\t\t\tbinding: realization.builtinBindings.screenSamplerRepeat,\n\t\t\t\t\tresource: renderer.textureStore.getSampler(\"linear\", \"repeat\"),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tgpu.bindGroup = renderer.device.createBindGroup({\n\t\t\tlabel: \"melonJS effect binding\",\n\t\t\tlayout: gpu.effectLayout,\n\t\t\tentries,\n\t\t});\n\t\tgpu.bindKey = bindKey;\n\t}\n\n\treturn {\n\t\tkey: gpu.key,\n\t\thasEffectGroup: true,\n\t\tbindGroup: gpu.bindGroup,\n\t\tdynamicOffsets,\n\t};\n}\n","// melonJS WebGPU blit shader — one source texture over the frozen quad\n// vertex layout. The compositing primitive: render-target blits and the\n// no-effect post-FX composite ride this family, which keeps the\n// single-texture group-1 shape the effect scaffold shares (the QUAD family\n// batches across eight textures and has its own group-1 layout).\n//\n// Vertex layout (28-byte stride, frozen, shared with the WebGL backend):\n//   @location(0) aVertex    float32x3  offset 0\n//   @location(1) aRegion    float32x2  offset 12\n//   @location(2) aColor     unorm8x4   offset 20\n//   @location(3) aTextureId float32    offset 24 (unused here)\n//\n// aColor arrives from a packed ARGB uint32 (Color.toUint32). Little-endian\n// memory bytes are [B,G,R,A] and unorm8x4 maps byte i -> component i, so the\n// attribute reads (B,G,R,A) — the same .bgr swizzle as the GLSL sources\n// reconstructs RGB, premultiplied by alpha.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block\n\tlineWidth : f32,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aTextureId : f32,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet clip = uFrame.projection * vec4f(aVertex, 1.0);\n\t// Matrix3d.ortho/perspective emit GL-convention clip z in [-w, w];\n\t// WebGPU clips z outside [0, w]. Without this remap any vertex with a\n\t// non-zero depth (Sprite3d, renderable.depth) is silently clipped away.\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\n\tout.vRegion = aRegion;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\treturn textureSample(uTexture, uSampler, in.vRegion) * in.vColor;\n}\n","// melonJS WebGPU clear shader — a bufferless full-viewport triangle whose\n// fragments are clipped by the CURRENT setScissorRect state and written with\n// blending disabled (replace). The WebGPU equivalent of gl.clearColor +\n// gl.clear inside an active scissor, which the API has no native op for:\n// mid-frame clearColor (ColorLayer, Container backgrounds) draws this.\n\nstruct ClearUniforms {\n\tcolor : vec4f,\n};\n\n@group(0) @binding(0) var<uniform> uClear : ClearUniforms;\n\n@vertex\nfn vertex_main(@builtin(vertex_index) i : u32) -> @builtin(position) vec4f {\n\tvar pos = array<vec2f, 3>(\n\t\tvec2f(-1.0, -1.0),\n\t\tvec2f(3.0, -1.0),\n\t\tvec2f(-1.0, 3.0),\n\t);\n\treturn vec4f(pos[i], 0.0, 1.0);\n}\n\n@fragment\nfn fragment_main() -> @location(0) vec4f {\n\treturn uClear.color;\n}\n","// melonJS WebGPU primitive shader — the WGSL port of primitive.vert/.frag.\n//\n// Vertex layout (24-byte stride, frozen, shared with the WebGL backend):\n//   @location(0) aVertex float32x3  offset 0\n//   @location(1) aNormal float32x2  offset 12   (thick-line expansion dir)\n//   @location(2) aColor  unorm8x4   offset 20   (packed ARGB — see quad.wgsl)\n//\n// uLineWidth lives in the shared frame-globals block: the batcher pushes a\n// new dynamic-offset slot whenever renderer.lineWidth changes, replacing the\n// per-program uLineWidth uniform of the GL backend.\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\tlineWidth : f32,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vColor : vec4f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aNormal : vec2f,\n\t@location(2) aColor : vec4f,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet position = aVertex.xy + aNormal * uFrame.lineWidth * 0.5;\n\tlet clip = uFrame.projection * vec4f(position, aVertex.z, 1.0);\n\t// GL-convention clip z in [-w, w] -> WebGPU [0, w] (see quad.wgsl)\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\treturn in.vColor;\n}\n","// melonJS WebGPU quad shader — the WGSL port of quad-multi.vert /\n// multitexture.frag: MULTI-TEXTURE batching, eight texture+sampler slots\n// per draw segment. `aTextureId` selects the slot per quad, so a segment\n// flushes only when a NINTH distinct texture appears (or on the usual\n// blend/effect/capacity boundaries) — the GL sampler-ladder equivalent.\n//\n// Vertex layout (28-byte stride, frozen, shared with the WebGL backend):\n//   @location(0) aVertex    float32x3  offset 0\n//   @location(1) aRegion    float32x2  offset 12\n//   @location(2) aColor     unorm8x4   offset 20\n//   @location(3) aTextureId float32    offset 24 (slot index, 0..7)\n//\n// aColor arrives from a packed ARGB uint32 (Color.toUint32). Little-endian\n// memory bytes are [B,G,R,A] and unorm8x4 maps byte i -> component i, so the\n// attribute reads (B,G,R,A) — the same .bgr swizzle as the GLSL sources\n// reconstructs RGB, premultiplied by alpha.\n//\n// Sampling uses textureSampleLevel(…, 0.0): the slot index is not uniform\n// across the draw, which bars implicit-derivative textureSample under WGSL's\n// uniformity analysis — and 2D quads sample level 0 by contract anyway\n// (their samplers are lod-clamped; mip chains are a mesh-path feature).\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\t// unused by this shader; part of the shared frame-globals block\n\tlineWidth : f32,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture0 : texture_2d<f32>;\n@group(1) @binding(1) var uTexture1 : texture_2d<f32>;\n@group(1) @binding(2) var uTexture2 : texture_2d<f32>;\n@group(1) @binding(3) var uTexture3 : texture_2d<f32>;\n@group(1) @binding(4) var uTexture4 : texture_2d<f32>;\n@group(1) @binding(5) var uTexture5 : texture_2d<f32>;\n@group(1) @binding(6) var uTexture6 : texture_2d<f32>;\n@group(1) @binding(7) var uTexture7 : texture_2d<f32>;\n@group(1) @binding(8) var uSampler0 : sampler;\n@group(1) @binding(9) var uSampler1 : sampler;\n@group(1) @binding(10) var uSampler2 : sampler;\n@group(1) @binding(11) var uSampler3 : sampler;\n@group(1) @binding(12) var uSampler4 : sampler;\n@group(1) @binding(13) var uSampler5 : sampler;\n@group(1) @binding(14) var uSampler6 : sampler;\n@group(1) @binding(15) var uSampler7 : sampler;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n\t// constant across a quad's four vertices, so plain interpolation\n\t// reproduces it exactly; rounded back to the slot index per fragment\n\t@location(2) vTextureId : f32,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aTextureId : f32,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet clip = uFrame.projection * vec4f(aVertex, 1.0);\n\t// Matrix3d.ortho/perspective emit GL-convention clip z in [-w, w];\n\t// WebGPU clips z outside [0, w]. Without this remap any vertex with a\n\t// non-zero depth (Sprite3d, renderable.depth) is silently clipped away.\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\n\tout.vRegion = aRegion;\n\tout.vTextureId = aTextureId;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\tlet uv = in.vRegion;\n\tvar color : vec4f;\n\tswitch i32(in.vTextureId + 0.5) {\n\t\tcase 1: {\n\t\t\tcolor = textureSampleLevel(uTexture1, uSampler1, uv, 0.0);\n\t\t}\n\t\tcase 2: {\n\t\t\tcolor = textureSampleLevel(uTexture2, uSampler2, uv, 0.0);\n\t\t}\n\t\tcase 3: {\n\t\t\tcolor = textureSampleLevel(uTexture3, uSampler3, uv, 0.0);\n\t\t}\n\t\tcase 4: {\n\t\t\tcolor = textureSampleLevel(uTexture4, uSampler4, uv, 0.0);\n\t\t}\n\t\tcase 5: {\n\t\t\tcolor = textureSampleLevel(uTexture5, uSampler5, uv, 0.0);\n\t\t}\n\t\tcase 6: {\n\t\t\tcolor = textureSampleLevel(uTexture6, uSampler6, uv, 0.0);\n\t\t}\n\t\tcase 7: {\n\t\t\tcolor = textureSampleLevel(uTexture7, uSampler7, uv, 0.0);\n\t\t}\n\t\tdefault: {\n\t\t\tcolor = textureSampleLevel(uTexture0, uSampler0, uv, 0.0);\n\t\t}\n\t}\n\treturn color * in.vColor;\n}\n","/**\n * Frozen bind-group conventions for the WebGPU backend.\n *\n * WebGPU bind-group indices are a per-pipeline-layout namespace with a hard\n * cap of four groups, so — unlike WebGL's context-wide uniform-buffer\n * binding points, which the renderer hands out from a monotonic counter\n * (`reserveUniformBindingPoint`) — the collision-proofing here is a frozen\n * convention: every pipeline in the backend agrees on what each group index\n * means, and additions claim the reserved slots below instead of inventing\n * their own.\n *\n * - group 0 — frame globals: one dynamic-offset uniform binding shared by\n *   every draw (projection matrix + line width), served by the uniform\n *   ring. The clear pipeline substitutes its own group-0 layout (a bare\n *   color) over the same ring.\n * - group 1 — material: texture view + sampler (quad pipeline).\n * - group 2 — reserved: lights (the std140 light block ports here).\n * - group 3 — reserved: per-effect extras (ShaderEffect-WGSL, post FX).\n * @ignore\n */\nexport const GROUP_FRAME = 0;\nexport const GROUP_MATERIAL = 1;\nexport const GROUP_LIGHTS = 2;\nexport const GROUP_EFFECT = 3;\n\n/**\n * Byte size of the frame-globals uniform block:\n * mat4x4<f32> projection (64) + f32 lineWidth (4) + pad to 16 → 80.\n * @ignore\n */\nexport const FRAME_UNIFORM_SIZE = 80;\n\n/**\n * Byte size of the clear-color uniform block: vec4<f32> → 16.\n * @ignore\n */\nexport const CLEAR_UNIFORM_SIZE = 16;\n","import blitWGSL from \"../shaders/blit.wgsl\";\nimport clearWGSL from \"../shaders/clear.wgsl\";\nimport primitiveWGSL from \"../shaders/primitive.wgsl\";\nimport quadWGSL from \"../shaders/quad.wgsl\";\nimport { CLEAR_UNIFORM_SIZE, FRAME_UNIFORM_SIZE } from \"./bindgroups.js\";\n\n/**\n * Texture slots per quad draw segment — the quad family batches quads\n * across up to this many distinct textures before a flush is forced (the\n * GL sampler-ladder equivalent). 8 texture + 8 sampler bindings stays\n * comfortably inside WebGPU's base limits (16 sampled textures / 16\n * samplers per stage) with headroom for the effect families' samplers.\n * @ignore\n */\nexport const MAX_QUAD_TEXTURES = 8;\n\n/**\n * The depth-stencil attachment format every pass and every pipeline of this\n * backend declares. Attached from day one — masks use the stencil half now,\n * meshes will use the depth half — because a pipeline's depthStencil state\n * must match the pass attachment, and adding it later would invalidate\n * every cached pipeline.\n * @ignore\n */\nexport const DEPTH_STENCIL_FORMAT = \"depth24plus-stencil8\";\n\n/**\n * Blend-mode → GPUBlendState mapping, the pipeline-state port of the GL\n * blendEquation/blendFunc table in `WebGLRenderer.setBlendMode`. Factors\n * are ignored by the min/max operations per spec, matching the GL\n * `MIN`/`MAX` + `(ONE, ONE)` setup for darken/lighten.\n * @param {string} mode - normalized blend mode\n * @param {boolean} premultipliedAlpha - whether sources are premultiplied\n * @returns {GPUBlendState|undefined} the blend state, or undefined for \"none\" (replace)\n * @ignore\n */\nfunction blendStateFor(mode, premultipliedAlpha) {\n\tconst src = premultipliedAlpha ? \"one\" : \"src-alpha\";\n\tlet component;\n\tswitch (mode) {\n\t\tcase \"none\":\n\t\t\treturn undefined;\n\t\tcase \"additive\":\n\t\t\tcomponent = { operation: \"add\", srcFactor: src, dstFactor: \"one\" };\n\t\t\tbreak;\n\t\tcase \"multiply\":\n\t\t\tcomponent = {\n\t\t\t\toperation: \"add\",\n\t\t\t\tsrcFactor: \"dst\",\n\t\t\t\tdstFactor: \"one-minus-src-alpha\",\n\t\t\t};\n\t\t\tbreak;\n\t\tcase \"exclusion\":\n\t\t\t// exclusion(a, b) = a + b - 2ab, expressed as\n\t\t\t// s*(1-d) + d*(1-s) — no shader needed. See the WebGL twin.\n\t\t\tcomponent = {\n\t\t\t\toperation: \"add\",\n\t\t\t\tsrcFactor: \"one-minus-dst\",\n\t\t\t\tdstFactor: \"one-minus-src\",\n\t\t\t};\n\t\t\tbreak;\n\n\t\tcase \"screen\":\n\t\t\tcomponent = {\n\t\t\t\toperation: \"add\",\n\t\t\t\tsrcFactor: \"one\",\n\t\t\t\tdstFactor: \"one-minus-src\",\n\t\t\t};\n\t\t\tbreak;\n\t\tcase \"darken\":\n\t\t\tcomponent = { operation: \"min\", srcFactor: \"one\", dstFactor: \"one\" };\n\t\t\tbreak;\n\t\tcase \"lighten\":\n\t\t\tcomponent = { operation: \"max\", srcFactor: \"one\", dstFactor: \"one\" };\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t// \"normal\" — (premultiplied) source-over\n\t\t\tcomponent = {\n\t\t\t\toperation: \"add\",\n\t\t\t\tsrcFactor: src,\n\t\t\t\tdstFactor: \"one-minus-src-alpha\",\n\t\t\t};\n\t\t\tbreak;\n\t}\n\treturn { color: component, alpha: component };\n}\n\n/**\n * Normalize a user-facing blend-mode string to a cache-key token — the\n * same collapsing the WebGL backend applies (\"add\"/\"lighter\" → additive,\n * unknown → normal).\n * @param {string} mode - blend mode as set through `setBlendMode`\n * @returns {string} normalized token\n * @ignore\n */\nexport function normalizeBlendMode(mode) {\n\tswitch (mode) {\n\t\tcase \"none\":\n\t\tcase \"multiply\":\n\t\tcase \"screen\":\n\t\tcase \"darken\":\n\t\tcase \"lighten\":\n\t\tcase \"exclusion\":\n\t\tcase \"normal\":\n\t\t\treturn mode;\n\t\tcase \"additive\":\n\t\tcase \"add\":\n\t\tcase \"lighter\":\n\t\t\treturn \"additive\";\n\t\tdefault:\n\t\t\treturn \"normal\";\n\t}\n}\n\n/**\n * Stencil-mode → depthStencil/colorWriteMask pipeline states, realizing the\n * GL stencil mask machinery (`setMask`/`clearMask`) as pipeline variants:\n * - \"none\"  — stencil ignored (default 2D drawing)\n * - \"write\" — the mask write phase: color writes off, every fragment\n *   increments the stencil (GL's `colorMask(false) + stencilOp INCR`)\n * - \"test\"  — the masked render phase: fragments pass only where stencil\n *   equals the dynamic `setStencilReference` (GL's `stencilFunc(EQUAL, ref)`)\n * @ignore\n */\nconst STENCIL_STATES = {\n\tnone: {\n\t\tstencil: {\n\t\t\tcompare: \"always\",\n\t\t\tfailOp: \"keep\",\n\t\t\tdepthFailOp: \"keep\",\n\t\t\tpassOp: \"keep\",\n\t\t},\n\t\treadMask: 0xff,\n\t\twriteMask: 0,\n\t\tcolorWriteMask: 0xf,\n\t},\n\twrite: {\n\t\tstencil: {\n\t\t\tcompare: \"always\",\n\t\t\tfailOp: \"keep\",\n\t\t\tdepthFailOp: \"keep\",\n\t\t\tpassOp: \"increment-clamp\",\n\t\t},\n\t\treadMask: 0xff,\n\t\twriteMask: 0xff,\n\t\tcolorWriteMask: 0,\n\t},\n\ttest: {\n\t\tstencil: {\n\t\t\tcompare: \"equal\",\n\t\t\tfailOp: \"keep\",\n\t\t\tdepthFailOp: \"keep\",\n\t\t\tpassOp: \"keep\",\n\t\t},\n\t\treadMask: 0xff,\n\t\twriteMask: 0,\n\t\tcolorWriteMask: 0xf,\n\t},\n\t// gradient-mask phases (the GL #gradientMask parity):\n\t// - \"tag\" stamps the shape's pixels with the dynamic stencil reference\n\t//   on a cleared stencil (GL's ALWAYS/REPLACE, color writes off) —\n\t//   replace, not increment, so overdrawing shape geometry is harmless\n\t// - \"mark\" writes the reference only where the stencil's low 7 bits\n\t//   already equal the reference's low bits — tags/untags the high-bit\n\t//   marker inside an active mask without disturbing mask levels\n\ttag: {\n\t\tstencil: {\n\t\t\tcompare: \"always\",\n\t\t\tfailOp: \"keep\",\n\t\t\tdepthFailOp: \"keep\",\n\t\t\tpassOp: \"replace\",\n\t\t},\n\t\treadMask: 0xff,\n\t\twriteMask: 0xff,\n\t\tcolorWriteMask: 0,\n\t},\n\tmark: {\n\t\tstencil: {\n\t\t\tcompare: \"equal\",\n\t\t\tfailOp: \"keep\",\n\t\t\tdepthFailOp: \"keep\",\n\t\t\tpassOp: \"replace\",\n\t\t},\n\t\treadMask: 0x7f,\n\t\twriteMask: 0xff,\n\t\tcolorWriteMask: 0,\n\t},\n};\n\n/**\n * Lazily-built cache of `GPURenderPipeline`s for the WebGPU backend, keyed\n * by everything that is pipeline state under WebGPU but dynamic state under\n * GL: shader family, topology, blend mode, premultiplied-alpha flag,\n * stencil mode (and, held constant today, the canvas format and sample\n * count — both stay in the key so render targets and MSAA slot in later\n * without a reshape).\n *\n * Also owns the shader modules, bind-group layouts and pipeline layouts —\n * the objects every pipeline shares and the uniform ring / texture store\n * build their bind groups against.\n * @ignore\n */\nexport default class WebGPUPipelineCache {\n\t/**\n\t * @param {GPUDevice} device - the device to build pipelines on\n\t * @param {string} format - the canvas color format (preferredFormat)\n\t */\n\tconstructor(device, format) {\n\t\tthis.device = device;\n\t\tthis.format = format;\n\t\tthis.sampleCount = 1;\n\t\t/** @type {Map<string, GPURenderPipeline>} */\n\t\tthis.pipelines = new Map();\n\t\t/** @type {Map<string, {stride: number, attributes: object[]}>} */\n\t\tthis.vertexLayouts = new Map();\n\n\t\t// ---- bind-group layouts (shared by pipelines and resource caches) --\n\t\tthis.frameLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS frame globals layout\",\n\t\t\tentries: [\n\t\t\t\t{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tvisibility: GPUShaderStage.VERTEX,\n\t\t\t\t\tbuffer: {\n\t\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\t\tminBindingSize: FRAME_UNIFORM_SIZE,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tthis.clearFrameLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS clear color layout\",\n\t\t\tentries: [\n\t\t\t\t{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\tbuffer: {\n\t\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\t\tminBindingSize: CLEAR_UNIFORM_SIZE,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tthis.materialLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS material layout\",\n\t\t\tentries: [\n\t\t\t\t{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t],\n\t\t});\n\t\t// The MESH family's group 1: the diffuse pair plus a second pair for\n\t\t// the per-texel opacity map (MTL `map_d`, #1575). Always four\n\t\t// bindings, even for a mesh with no alpha map — the batcher points\n\t\t// the second pair at the shared white pixel then, which costs one\n\t\t// extra bind-group entry and keeps every mesh pipeline on one\n\t\t// layout instead of splitting the family in two.\n\t\t//\n\t\t// Widening the layout does not break the documented custom-WGSL mesh\n\t\t// contract: a module may declare a SUBSET of its layout's bindings,\n\t\t// so a custom mesh shader declaring only texture+sampler still\n\t\t// validates against this.\n\t\tthis.meshMaterialLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS mesh material layout\",\n\t\t\tentries: [\n\t\t\t\t{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t\t{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t{ binding: 3, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t],\n\t\t});\n\t\t// the quad family's group 1: eight texture slots (bindings 0-7) and\n\t\t// their samplers (8-15) — one draw segment spans up to eight\n\t\t// distinct textures, selected per quad by aTextureId\n\t\tconst multiEntries = [];\n\t\tfor (let slot = 0; slot < MAX_QUAD_TEXTURES; slot++) {\n\t\t\tmultiEntries.push({\n\t\t\t\tbinding: slot,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\ttexture: {},\n\t\t\t});\n\t\t\tmultiEntries.push({\n\t\t\t\tbinding: MAX_QUAD_TEXTURES + slot,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: {},\n\t\t\t});\n\t\t}\n\t\tthis.multiMaterialLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS quad materials layout\",\n\t\t\tentries: multiEntries,\n\t\t});\n\n\t\t// ---- shader modules ----------------------------------------------\n\t\tthis.modules = {\n\t\t\tquad: device.createShaderModule({\n\t\t\t\tlabel: \"melonJS quad shader\",\n\t\t\t\tcode: quadWGSL,\n\t\t\t}),\n\t\t\t// the single-texture compositing family (render-target blits) —\n\t\t\t// same group-1 shape as the effect scaffold\n\t\t\tblit: device.createShaderModule({\n\t\t\t\tlabel: \"melonJS blit shader\",\n\t\t\t\tcode: blitWGSL,\n\t\t\t}),\n\t\t\tprimitive: device.createShaderModule({\n\t\t\t\tlabel: \"melonJS primitive shader\",\n\t\t\t\tcode: primitiveWGSL,\n\t\t\t}),\n\t\t\tclear: device.createShaderModule({\n\t\t\t\tlabel: \"melonJS clear shader\",\n\t\t\t\tcode: clearWGSL,\n\t\t\t}),\n\t\t};\n\n\t\t// ---- pipeline layouts --------------------------------------------\n\t\tthis.pipelineLayouts = {\n\t\t\tquad: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [this.frameLayout, this.multiMaterialLayout],\n\t\t\t}),\n\t\t\tblit: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [this.frameLayout, this.materialLayout],\n\t\t\t}),\n\t\t\tprimitive: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [this.frameLayout],\n\t\t\t}),\n\t\t\tclear: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [this.clearFrameLayout],\n\t\t\t}),\n\t\t};\n\n\t\t// ---- registered (non-built-in) shader families -------------------\n\t\t// effect modules are deduplicated by their exact module text: clones\n\t\t// and same-shape effects share one module and its pipelines\n\t\t/** @type {Map<string, string>} moduleText → family key */\n\t\tthis.registeredModules = new Map();\n\t\t// compiled GPUShaderModule per source text, shared across the families\n\t\t// that source serves (one module, several vertex layouts)\n\t\tthis.modulesBySource = new Map();\n\t\t/** @type {Map<string, {stride: number, attributes: object[]}>} family key → vertex-layout alias */\n\t\tthis.vertexLayoutAliases = new Map();\n\t\t// the blit family rides the frozen quad vertex layout\n\t\tthis.vertexLayoutAliases.set(\"blit\", \"quad\");\n\t\t/** @type {Map<string, GPUBindGroupLayout>} shape signature → group-3 layout */\n\t\tthis.effectLayouts = new Map();\n\n\t\t// group 2 (lights) is reserved but unused by the 2D tier: pipeline\n\t\t// layouts are positional, so families binding group 3 interpose a\n\t\t// shared empty layout — and passes set the matching empty bind group,\n\t\t// which some implementations validate even for empty layouts\n\t\tthis.emptyLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS empty group layout\",\n\t\t\tentries: [],\n\t\t});\n\t\tthis.emptyBindGroup = device.createBindGroup({\n\t\t\tlabel: \"melonJS empty group\",\n\t\t\tlayout: this.emptyLayout,\n\t\t\tentries: [],\n\t\t});\n\n\t\t/**\n\t\t * Device generation this cache belongs to — bumped once per cache\n\t\t * construction. Consumers holding device-scoped objects built\n\t\t * against this cache (effect modules, bind groups) compare their\n\t\t * recorded epoch and lazily rebuild after a device loss.\n\t\t * @type {number}\n\t\t */\n\t\tthis.epoch = ++WebGPUPipelineCache.epochCounter;\n\t}\n\n\t/**\n\t * monotonic construction counter backing {@link WebGPUPipelineCache#epoch}\n\t * @ignore\n\t */\n\tstatic epochCounter = 0;\n\n\t/**\n\t * Register a shader family beyond the built-in three, routing it through\n\t * the same keyed {@link WebGPUPipelineCache#get} lookup. Identical module\n\t * text registers once — the returned key is stable per text, so clones\n\t * and same-body effects share the module and every pipeline built on it.\n\t * @param {string} code - the complete WGSL module text\n\t * @param {object} [options] - family options\n\t * @param {GPUBindGroupLayout[]} [options.bindGroupLayouts] - full positional\n\t *   bind-group-layout list (defaults to `[frameLayout, materialLayout]`)\n\t * @param {string} [options.vertexLayoutKey=\"quad\"] - reuse a registered\n\t *   vertex layout under this family (the effect blit rides the quad layout)\n\t * @param {string} [options.label] - debug label for the shader module\n\t * @returns {string} the family key to pass to {@link WebGPUPipelineCache#get}\n\t */\n\tregisterShader(code, options = {}) {\n\t\tconst alias = options.vertexLayoutKey ?? \"quad\";\n\t\t// Keyed by (source, vertex layout), not by source alone. One module can\n\t\t// legitimately serve several vertex layouts — the instanced ground\n\t\t// shadow (#1515) reads only the transform rows, so one module covers\n\t\t// every record shape while the instance buffer's `arrayStride` still\n\t\t// differs per shape. Keying on the source alone silently handed every\n\t\t// later caller the FIRST registration's layout, which reads the records\n\t\t// at the wrong stride. Callers registering one layout per module (all\n\t\t// of them, until now) are unaffected: same source + same alias still\n\t\t// dedupes to the same family.\n\t\tconst cacheKey = `${alias}\\u0000${code}`;\n\t\tlet key = this.registeredModules.get(cacheKey);\n\t\tif (typeof key === \"undefined\") {\n\t\t\tkey = `effect:${this.registeredModules.size}`;\n\t\t\tthis.registeredModules.set(cacheKey, key);\n\t\t\t// the compiled module IS shared across layouts — only the pipeline\n\t\t\t// layout and vertex-layout alias below are per-family\n\t\t\tlet module = this.modulesBySource.get(code);\n\t\t\tif (module === undefined) {\n\t\t\t\tmodule = this.device.createShaderModule({\n\t\t\t\t\tlabel: options.label ?? `melonJS ${key} shader`,\n\t\t\t\t\tcode,\n\t\t\t\t});\n\t\t\t\tthis.modulesBySource.set(code, module);\n\t\t\t}\n\t\t\tthis.modules[key] = module;\n\t\t\tthis.pipelineLayouts[key] = this.device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: options.bindGroupLayouts ?? [\n\t\t\t\t\tthis.frameLayout,\n\t\t\t\t\tthis.materialLayout,\n\t\t\t\t],\n\t\t\t});\n\t\t\tthis.vertexLayoutAliases.set(key, alias);\n\t\t}\n\t\treturn key;\n\t}\n\n\t/**\n\t * Fetch (building on first use) the group-3 bind-group layout for an\n\t * effect shape. Effects with the same shape — same uniform block size,\n\t * same texture/sampler bindings, same builtins — share one layout, so\n\t * their pipeline layouts (and pipelines) are shareable too.\n\t * @param {string} signature - the shape signature (caller-composed)\n\t * @param {GPUBindGroupLayoutEntry[]} entries - the layout entries\n\t * @returns {GPUBindGroupLayout} the cached layout\n\t */\n\tgetEffectLayout(signature, entries) {\n\t\tlet layout = this.effectLayouts.get(signature);\n\t\tif (typeof layout === \"undefined\") {\n\t\t\tlayout = this.device.createBindGroupLayout({\n\t\t\t\tlabel: `melonJS effect layout ${signature}`,\n\t\t\t\tentries,\n\t\t\t});\n\t\t\tthis.effectLayouts.set(signature, layout);\n\t\t}\n\t\treturn layout;\n\t}\n\n\t/**\n\t * Register a shader family's vertex buffer layout from a batcher's\n\t * frozen attribute records — the declarative consumption of the\n\t * backend-neutral vertex formats (#1492): the records already carry\n\t * `format` and `offset` in WebGPU vocabulary, shader locations are\n\t * declaration order.\n\t * Accepts either the single-buffer form `(key, stride, attributes)` or a\n\t * list of buffer groups `(key, [{stride, stepMode, attributes}, …])` —\n\t * the latter is what an instanced family registers, with the geometry\n\t * group at index 0 and a `stepMode: \"instance\"` group after it. Shader\n\t * locations are assigned in declaration order and continue **across**\n\t * groups, since WGSL locations are a single namespace.\n\t * @param {string} shaderKey - \"quad\" | \"primitive\"\n\t * @param {number|object[]} stride - vertex byte stride, or the buffer-group list\n\t * @param {{format: string, offset: number}[]} [attributes] - frozen records (single-buffer form)\n\t */\n\tregisterVertexLayout(shaderKey, stride, attributes) {\n\t\tconst groups = Array.isArray(stride)\n\t\t\t? stride\n\t\t\t: [{ stride, attributes, stepMode: \"vertex\" }];\n\t\tlet location = 0;\n\t\tthis.vertexLayouts.set(\n\t\t\tshaderKey,\n\t\t\tgroups.map((group) => {\n\t\t\t\tconst layout = {\n\t\t\t\t\tarrayStride: group.stride,\n\t\t\t\t\tattributes: group.attributes.map((a) => {\n\t\t\t\t\t\t// an attribute may pin its own location. Instanced\n\t\t\t\t\t\t// families do: their optional slots come and go per\n\t\t\t\t\t\t// variant, and sequential numbering would shift the\n\t\t\t\t\t\t// remaining ones, so every WGSL variant would need\n\t\t\t\t\t\t// renumbering. Pinned locations keep the module text\n\t\t\t\t\t\t// identical whichever slots are present.\n\t\t\t\t\t\tconst shaderLocation = a.shaderLocation ?? location;\n\t\t\t\t\t\tlocation = shaderLocation + 1;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tformat: a.format,\n\t\t\t\t\t\t\toffset: a.offset,\n\t\t\t\t\t\t\tshaderLocation,\n\t\t\t\t\t\t};\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t\t// omitted entirely for per-vertex groups: \"vertex\" is the\n\t\t\t\t// WebGPU default, and emitting it would change the descriptor\n\t\t\t\t// every existing pipeline is built from\n\t\t\t\tif (group.stepMode === \"instance\") {\n\t\t\t\t\tlayout.stepMode = \"instance\";\n\t\t\t\t}\n\t\t\t\treturn layout;\n\t\t\t}),\n\t\t);\n\t}\n\n\t/**\n\t * Fetch (building on first use) the pipeline for the given state tuple.\n\t * @param {string} shaderKey - \"quad\" | \"primitive\" | \"clear\"\n\t * @param {string} topology - portable topology name (\"triangle-list\", …)\n\t * @param {string} blendMode - blend mode (normalized internally)\n\t * @param {boolean} premultipliedAlpha - source premultiplication flag\n\t * @param {string} [stencilMode=\"none\"] - \"none\" | \"write\" | \"test\" | \"tag\" | \"mark\"\n\t * @param {{cullMode: string, frontFace: string}} [meshState] - mesh pass\n\t *   state: its presence switches the depth half of the attachment on —\n\t *   depth writes enabled, \"less-equal\" testing (the GL mesh mode's\n\t *   LEQUAL, keeping coplanar geometry stable) — and sets the per-mesh\n\t *   face-culling axes. Omitted by the whole 2D tier, whose keys and\n\t *   descriptors stay byte-identical to a build without a mesh path.\n\t * @returns {GPURenderPipeline} the pipeline\n\t */\n\tget(\n\t\tshaderKey,\n\t\ttopology,\n\t\tblendMode,\n\t\tpremultipliedAlpha,\n\t\tstencilMode = \"none\",\n\t\tmeshState,\n\t) {\n\t\tconst blend = normalizeBlendMode(blendMode);\n\t\tconst pma = premultipliedAlpha !== false;\n\t\tlet key = `${shaderKey}|${topology}|${blend}|${pma ? 1 : 0}|${stencilMode}|${this.format}|${this.sampleCount}`;\n\t\tif (meshState) {\n\t\t\tkey += `|mesh:${meshState.cullMode}:${meshState.frontFace}`;\n\t\t\t// Appended ONLY when it differs from the default. Adding it\n\t\t\t// unconditionally would remint every existing mesh pipeline for a\n\t\t\t// value none of them changed.\n\t\t\tif (meshState.depthWrite === false) {\n\t\t\t\tkey += \"|dw0\";\n\t\t\t}\n\t\t}\n\t\tlet pipeline = this.pipelines.get(key);\n\t\tif (typeof pipeline === \"undefined\") {\n\t\t\tconst stencil = STENCIL_STATES[stencilMode] ?? STENCIL_STATES.none;\n\t\t\t// registered families may alias a built-in vertex layout (effect\n\t\t\t// blits ride the frozen quad layout)\n\t\t\tconst vertexLayout = this.vertexLayouts.get(\n\t\t\t\tthis.vertexLayoutAliases.get(shaderKey) ?? shaderKey,\n\t\t\t);\n\t\t\tconst primitive = {\n\t\t\t\ttopology,\n\t\t\t\tstripIndexFormat: topology.endsWith(\"-strip\") ? \"uint32\" : undefined,\n\t\t\t};\n\t\t\tif (meshState) {\n\t\t\t\tprimitive.cullMode = meshState.cullMode;\n\t\t\t\tprimitive.frontFace = meshState.frontFace;\n\t\t\t}\n\t\t\tpipeline = this.device.createRenderPipeline({\n\t\t\t\tlabel: `melonJS ${key}`,\n\t\t\t\tlayout: this.pipelineLayouts[shaderKey],\n\t\t\t\tvertex: {\n\t\t\t\t\tmodule: this.modules[shaderKey],\n\t\t\t\t\tentryPoint: \"vertex_main\",\n\t\t\t\t\tbuffers: vertexLayout ?? [],\n\t\t\t\t},\n\t\t\t\tfragment: {\n\t\t\t\t\tmodule: this.modules[shaderKey],\n\t\t\t\t\tentryPoint: \"fragment_main\",\n\t\t\t\t\ttargets: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tformat: this.format,\n\t\t\t\t\t\t\tblend:\n\t\t\t\t\t\t\t\tshaderKey === \"clear\" ? undefined : blendStateFor(blend, pma),\n\t\t\t\t\t\t\twriteMask: stencil.colorWriteMask,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tprimitive,\n\t\t\t\tdepthStencil: {\n\t\t\t\t\tformat: DEPTH_STENCIL_FORMAT,\n\t\t\t\t\t// `!== false`, not `!!`: mesh draws that never set the field (and\n\t\t\t\t\t// every bare `{cullMode, frontFace}` literal) must keep writing\n\t\t\t\t\t// depth exactly as they did before the axis existed\n\t\t\t\t\tdepthWriteEnabled: meshState ? meshState.depthWrite !== false : false,\n\t\t\t\t\tdepthCompare: meshState ? \"less-equal\" : \"always\",\n\t\t\t\t\tstencilFront: stencil.stencil,\n\t\t\t\t\tstencilBack: stencil.stencil,\n\t\t\t\t\tstencilReadMask: stencil.readMask,\n\t\t\t\t\tstencilWriteMask: stencil.writeMask,\n\t\t\t\t},\n\t\t\t\tmultisample: { count: this.sampleCount },\n\t\t\t});\n\t\t\tthis.pipelines.set(key, pipeline);\n\t\t}\n\t\treturn pipeline;\n\t}\n\n\t/**\n\t * drop every cached pipeline (device loss — the whole cache object is\n\t * rebuilt by the renderer's restore path, this exists for symmetry)\n\t */\n\tclear() {\n\t\tthis.pipelines.clear();\n\t}\n}\n","import IndexBuffer from \"../../buffer/index.js\";\nimport { transformQuadCorners } from \"../../gpu/quadcorners.ts\";\nimport { TextureSlotTable } from \"../../gpu/textureslots.js\";\nimport { prepareEffectBinding } from \"../effect_binding.js\";\nimport { MAX_QUAD_TEXTURES } from \"../pipeline/cache.js\";\nimport WebGPUBatcher from \"./webgpu_batcher.js\";\n\n/**\n * The WebGPU quad batcher — textured-quad accumulation with the same\n * frozen 28-byte vertex layout as the WebGL `QuadBatcher` and the same\n * static 6-indices-per-quad pattern.\n *\n * Texture batching is single-texture-per-draw-segment in this first\n * version: a texture (bind group) change flushes the pending quads — the\n * `aTextureId` attribute is written as 0 and kept in the layout so the\n * later multi-texture upgrade (`binding_array` / texture arrays) changes\n * only the group-1 layout and the fragment shader, never the vertex\n * stream. A WebGPU flush is one `writeBuffer` of the pending bytes plus\n * one `drawIndexed`, so the per-change cost is far below the GL\n * full-buffer re-upload this design avoids there.\n * @augments WebGPUBatcher\n * @category Rendering\n */\nexport default class WebGPUQuadBatcher extends WebGPUBatcher {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @override\n\t */\n\tinit(renderer, settings) {\n\t\tsuper.init(\n\t\t\trenderer,\n\t\t\tsettings ?? {\n\t\t\t\tshaderKey: \"quad\",\n\t\t\t\ttopology: \"triangle-list\",\n\t\t\t\tattributes: [\n\t\t\t\t\t{\n\t\t\t\t\t\t// vec3: (x, y, z). z carries `renderable.depth` for\n\t\t\t\t\t\t// perspective projection (Camera3d). Stride = 28 bytes.\n\t\t\t\t\t\tname: \"aVertex\",\n\t\t\t\t\t\tformat: \"float32x3\",\n\t\t\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"aRegion\",\n\t\t\t\t\t\tformat: \"float32x2\",\n\t\t\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"aColor\",\n\t\t\t\t\t\tformat: \"unorm8x4\",\n\t\t\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"aTextureId\",\n\t\t\t\t\t\tformat: \"float32\",\n\t\t\t\t\t\toffset: 6 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t);\n\n\t\t// the material bind group the pending vertices were queued under —\n\t\t// FAST-PATH state only: effect pipelines bind a single-source\n\t\t// material, while the normal path batches across texture slots\n\t\tthis.currentMaterial = null;\n\n\t\t// the ShaderEffect the pending vertices were queued under — the\n\t\t// single-effect fast path (renderer.customShader): the sprite's own\n\t\t// quad draws through the effect's pipeline, composited live against\n\t\t// the backdrop (no offscreen target)\n\t\tthis.currentEffect = null;\n\n\t\t// multi-texture segment state: up to MAX_QUAD_TEXTURES distinct\n\t\t// (texture view, sampler) pairs share one draw segment, selected\n\t\t// per quad by aTextureId — a flush is forced only by the NINTH\n\t\t// distinct texture (or the usual capacity/effect boundaries)\n\t\t// slot assignment is the shared backend-neutral policy (#1585) — the\n\t\t// same table the WebGL texture cache allocates units from, so the two\n\t\t// backends cannot drift on when a texture set overflows\n\t\tthis.slotTable = new TextureSlotTable({\n\t\t\tcapacity: MAX_QUAD_TEXTURES,\n\t\t\t// draw the pending quads with THEIR slots before any reassignment\n\t\t\tonOverflow: () => {\n\t\t\t\tthis.flush();\n\t\t\t},\n\t\t\tonEvict: (slot) => {\n\t\t\t\tthis.segmentEntries[slot] = undefined;\n\t\t\t},\n\t\t});\n\t\t/** @type {{view: GPUTextureView, sampler: GPUSampler}[]} indexed by slot */\n\t\tthis.segmentEntries = [];\n\t\t// the composed group-1 bind group for the pending segment (lazy)\n\t\tthis.segmentGroup = null;\n\t\t// composed bind groups cached by their slot-resource identity —\n\t\t// steady-state segments re-use instead of re-creating (the\n\t\t// litMaterials precedent; cleared on reset and filter changes)\n\t\t/** @type {Map<string, GPUBindGroup>} */\n\t\tthis.composedGroups = new Map();\n\t\t// monotonic ids for views/samplers, for composition cache keys\n\t\tthis.resourceIds = new WeakMap();\n\t\tthis.nextResourceId = 1;\n\n\t\t// static index buffer: 6 indices per 4 vertices, filled once by the\n\t\t// renderer-agnostic CPU pattern and uploaded at creation\n\t\tconst maxQuads = this.vertexData.maxVertex / 4;\n\t\tconst cpuIndices = new IndexBuffer(maxQuads * 6, true);\n\t\tcpuIndices.fillQuadPattern(maxQuads);\n\t\tthis.indexBuffer?.destroy();\n\t\tthis.indexBuffer = this.device.createBuffer({\n\t\t\tlabel: \"melonJS quad indices\",\n\t\t\tsize: cpuIndices.data.byteLength,\n\t\t\tusage: GPUBufferUsage.INDEX,\n\t\t\tmappedAtCreation: true,\n\t\t});\n\t\tnew Uint32Array(this.indexBuffer.getMappedRange()).set(cpuIndices.data);\n\t\tthis.indexBuffer.unmap();\n\t}\n\n\t/**\n\t * Add a textured quad. Same contract as the WebGL `QuadBatcher.addQuad`\n\t * — the CPU corner transform (view matrix + per-renderable depth) is\n\t * identical, only the texture-binding mechanism differs.\n\t * @param {object} texture - the texture atlas to draw with\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} w - destination width\n\t * @param {number} h - destination height\n\t * @param {number} u0 - texture UV (u0)\n\t * @param {number} v0 - texture UV (v0)\n\t * @param {number} u1 - texture UV (u1)\n\t * @param {number} v1 - texture UV (v1)\n\t * @param {number} tint - tint color in UINT32 (argb) format\n\t * @param {boolean} [reupload=false] - force the source pixels to re-upload (video frames)\n\t */\n\taddQuad(texture, x, y, w, h, u0, v0, u1, v1, tint, reupload = false) {\n\t\tconst vertexData = this.vertexData;\n\t\tconst renderer = this.renderer;\n\n\t\tif (vertexData.isFull(4)) {\n\t\t\tthis.flush();\n\t\t}\n\n\t\t// single-effect fast path: adopt the active customShader, draining\n\t\t// vertices queued under the previous state first (they must flush\n\t\t// under THEIR pipeline, not the incoming one)\n\t\tconst effect = renderer.customShader ?? null;\n\t\tif (effect !== this.currentEffect) {\n\t\t\tthis.flush();\n\t\t\tthis.currentEffect = effect;\n\t\t}\n\n\t\tif (effect !== null) {\n\t\t\t// `screen_texture`: refresh the shared capture with everything\n\t\t\t// drawn so far BEFORE this sprite's own quad, so the effect\n\t\t\t// samples the scene behind it (a pass break + encoder copy)\n\t\t\tif (effect._screenTextureUniforms?.length > 0) {\n\t\t\t\trenderer.captureFrame();\n\t\t\t}\n\n\t\t\t// the fast path stays single-texture: effect pipelines bind a\n\t\t\t// single-source material at group 1, and each quad flushes on\n\t\t\t// its own anyway\n\t\t\tconst bindGroup = renderer.textureStore.getBinding(texture, {\n\t\t\t\tforce: reupload,\n\t\t\t});\n\t\t\tif (bindGroup !== this.currentMaterial) {\n\t\t\t\tthis.flush();\n\t\t\t\tthis.currentMaterial = bindGroup;\n\t\t\t}\n\n\t\t\t// feed the effect's `noise_uv` builtin with this quad's frame\n\t\t\t// rect — min() normalizes flipped (swapped) UVs\n\t\t\tconst source = texture.getTexture();\n\t\t\teffect._setNoiseUVRect?.(\n\t\t\t\tsource.width || source.videoWidth || 1,\n\t\t\t\tsource.height || source.videoHeight || 1,\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t\tMath.min(u0, u1),\n\t\t\t\tMath.min(v0, v1),\n\t\t\t);\n\n\t\t\tthis.pushQuadVertices(x, y, w, h, u0, v0, u1, v1, tint, 0);\n\n\t\t\t// per-quad draw under the fast path: each sprite needs its own\n\t\t\t// capture state, noise rect and uniform snapshot (draw-time\n\t\t\t// setUniform mutation included)\n\t\t\tthis.flush();\n\t\t\treturn;\n\t\t}\n\n\t\t// multi-texture batching: resolve the quad's texture to a segment\n\t\t// slot, flushing only when an over-capacity NINTH texture appears\n\t\tthis.pushQuadVertices(\n\t\t\tx,\n\t\t\ty,\n\t\t\tw,\n\t\t\th,\n\t\t\tu0,\n\t\t\tv0,\n\t\t\tu1,\n\t\t\tv1,\n\t\t\ttint,\n\t\t\tthis.segmentSlotFor(texture, reupload),\n\t\t);\n\t}\n\n\t/**\n\t * Resolve a texture to its slot in the pending segment, claiming a new\n\t * slot (and flushing a full segment) when this (view, sampler) pair is\n\t * new. The upload rules — same-frame content changes, recycled units,\n\t * forced video re-uploads — all live in the store's resident-record\n\t * path, exactly as before.\n\t * @param {object} texture - the texture atlas to resolve\n\t * @param {boolean} reupload - force the source pixels to re-upload\n\t * @returns {number} the slot index written to aTextureId\n\t * @ignore\n\t */\n\tsegmentSlotFor(texture, reupload) {\n\t\tconst renderer = this.renderer;\n\t\tconst store = renderer.textureStore;\n\t\tconst record = store.getResidentRecord(texture, { force: reupload });\n\t\tconst wrap = texture.repeat ?? \"no-repeat\";\n\t\tconst filter =\n\t\t\ttypeof texture.filter === \"string\"\n\t\t\t\t? texture.filter\n\t\t\t\t: renderer.getDefaultTextureFilter();\n\t\tconst slotKey = `${this.resourceId(record.view)}|${filter}|${wrap}`;\n\t\t// a hit returns the live slot; a miss claims one, flushing the pending\n\t\t// segment first when the table is full (the `onOverflow` above)\n\t\tif (this.slotTable.peek(slotKey) === undefined) {\n\t\t\tconst slot = this.slotTable.slotFor(slotKey);\n\t\t\tthis.segmentEntries[slot] = {\n\t\t\t\tview: record.view,\n\t\t\t\tsampler: store.getSampler(filter, wrap),\n\t\t\t};\n\t\t\tthis.segmentGroup = null;\n\t\t\treturn slot;\n\t\t}\n\t\treturn this.slotTable.peek(slotKey);\n\t}\n\n\t/**\n\t * a stable id for a GPU resource object (bind-group composition keys)\n\t * @ignore\n\t */\n\tresourceId(resource) {\n\t\tlet id = this.resourceIds.get(resource);\n\t\tif (typeof id === \"undefined\") {\n\t\t\tid = this.nextResourceId++;\n\t\t\tthis.resourceIds.set(resource, id);\n\t\t}\n\t\treturn id;\n\t}\n\n\t/**\n\t * The composed group-1 bind group for the pending segment: the claimed\n\t * slots, with empty slots padded by slot 0 (every declared binding\n\t * needs a resource; the padding is never selected). Cached by the slot\n\t * resources' identity, so steady-state segments re-use one group.\n\t * @returns {GPUBindGroup} the segment's material bind group\n\t * @ignore\n\t */\n\tcomposeSegmentGroup() {\n\t\tif (this.segmentGroup !== null) {\n\t\t\treturn this.segmentGroup;\n\t\t}\n\t\tconst entries = this.segmentEntries;\n\t\tconst first = entries[0];\n\t\tconst groupEntries = [];\n\t\tlet key = \"\";\n\t\tfor (let slot = 0; slot < MAX_QUAD_TEXTURES; slot++) {\n\t\t\tconst entry = entries[slot] ?? first;\n\t\t\tkey += `${this.resourceId(entry.view)}.${this.resourceId(entry.sampler)}|`;\n\t\t\tgroupEntries.push({ binding: slot, resource: entry.view });\n\t\t\tgroupEntries.push({\n\t\t\t\tbinding: MAX_QUAD_TEXTURES + slot,\n\t\t\t\tresource: entry.sampler,\n\t\t\t});\n\t\t}\n\t\tlet group = this.composedGroups.get(key);\n\t\tif (typeof group === \"undefined\") {\n\t\t\tgroup = this.device.createBindGroup({\n\t\t\t\tlabel: \"melonJS quad materials\",\n\t\t\t\tlayout: this.renderer.pipelineCache.multiMaterialLayout,\n\t\t\t\tentries: groupEntries,\n\t\t\t});\n\t\t\tthis.composedGroups.set(key, group);\n\t\t}\n\t\tthis.segmentGroup = group;\n\t\treturn group;\n\t}\n\n\t/**\n\t * start the next segment fresh (the pending one was just recorded)\n\t * @ignore\n\t */\n\tresetSegment() {\n\t\t// the table clears each live slot's entry through `onEvict`; truncating\n\t\t// afterwards drops any stale tail a shrunk capacity left behind\n\t\tthis.slotTable.reset();\n\t\tthis.segmentEntries.length = 0;\n\t\tthis.segmentGroup = null;\n\t}\n\n\t/**\n\t * Drop every composed segment bind group — each embeds samplers\n\t * resolved from the default texture filter, so a filter change must\n\t * rebuild them (the multi-texture counterpart of the texture store's\n\t * invalidateBindGroups).\n\t */\n\tclearMaterialCache() {\n\t\tthis.composedGroups.clear();\n\t\tthis.resetSegment();\n\t}\n\n\t/**\n\t * Transform and queue the four corners of a quad — shared by the base\n\t * and lit addQuad paths. Stamps per-sprite depth onto z BEFORE\n\t * `m.apply` so Camera3d's view matrix (3D R⁻¹ ∘ T(-pos)) fully rotates\n\t * the vertex; for 2D-only matrices the z column is identity, so the\n\t * output (x, y) is bit-identical and z passes through. textureId is the\n\t * quad's segment slot (constant across its four corners); the lit and\n\t * fast paths pass 0 (single-texture bind groups).\n\t * @ignore\n\t */\n\tpushQuadVertices(x, y, w, h, u0, v0, u1, v1, tint, textureId = 0) {\n\t\tconst vertexData = this.vertexData;\n\t\tconst [vec0, vec1, vec2, vec3] = transformQuadCorners(\n\t\t\tthis.renderer.currentTransform,\n\t\t\tx,\n\t\t\ty,\n\t\t\tw,\n\t\t\th,\n\t\t\tthis.renderer.currentDepth,\n\t\t);\n\n\t\t// 4 vertices per quad; the index buffer provides the 6 indices\n\t\tvertexData.push(vec0.x, vec0.y, vec0.z, u0, v0, tint, textureId);\n\t\tvertexData.push(vec1.x, vec1.y, vec1.z, u1, v0, tint, textureId);\n\t\tvertexData.push(vec2.x, vec2.y, vec2.z, u0, v1, tint, textureId);\n\t\tvertexData.push(vec3.x, vec3.y, vec3.z, u1, v1, tint, textureId);\n\t}\n\n\t/**\n\t * Composite a render target through an effect's pipeline as one\n\t * screen-space quad — the WebGPU counterpart of the GL blitTexture.\n\t * The caller ({@link WebGPURenderer#blitEffect}) has already set the\n\t * screen-space projection; this records the draw with the effect's\n\t * shader family and group-3 binding (uniforms snapshot-uploaded per\n\t * bind). An effect without a WGSL realization composites as a plain\n\t * un-effected blit, so the scene content is never lost.\n\t * @param {import(\"../../rendertarget/webgpurendertarget.js\").default} source - the render target to sample\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} w - destination width\n\t * @param {number} h - destination height\n\t * @param {ShaderEffect} [effect] - the effect to composite with\n\t * @param {boolean} [keepBlend=false] - keep the current blend mode (else replace)\n\t * @ignore\n\t */\n\tblitTexture(source, x, y, w, h, effect, keepBlend = false) {\n\t\t// drain pending quads under their own material first\n\t\tthis.flush();\n\t\tconst renderer = this.renderer;\n\n\t\t// identity noise_uv rect for the fullscreen quad (GL parity), BEFORE\n\t\t// the uniform snapshot below\n\t\teffect?._setNoiseUVRect?.(w, h, w, h, 0, 0);\n\t\tconst binding = effect ? prepareEffectBinding(renderer, effect) : null;\n\n\t\tconst pass = renderer.ensurePass();\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\t// no effect → the single-texture blit family (the quad family's\n\t\t\t// group 1 is the eight-slot segment layout)\n\t\t\tbinding?.key ?? \"blit\",\n\t\t\t\"triangle-list\",\n\t\t\tkeepBlend ? renderer.currentBlendMode : \"none\",\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tpass.setBindGroup(1, source.getMaterialBindGroup());\n\t\tif (binding?.hasEffectGroup) {\n\t\t\t// positional through group 3 — the reserved lights slot gets the\n\t\t\t// shared empty group\n\t\t\tpass.setBindGroup(2, renderer.pipelineCache.emptyBindGroup);\n\t\t\tpass.setBindGroup(3, binding.bindGroup, binding.dynamicOffsets);\n\t\t}\n\n\t\t// one quad, UNFLIPPED UVs (WebGPU texture row 0 is the top — the GL\n\t\t// blit flips V because GL FBOs are bottom-up), white tint\n\t\tconst vertexData = this.vertexData;\n\t\tvertexData.push(x, y, 0, 0, 0, 0xffffffff, 0);\n\t\tvertexData.push(x + w, y, 0, 1, 0, 0xffffffff, 0);\n\t\tvertexData.push(x, y + h, 0, 0, 1, 0xffffffff, 0);\n\t\tvertexData.push(x + w, y + h, 0, 1, 1, 0xffffffff, 0);\n\t\tconst byteLength = 4 * this.stride;\n\t\tconst region = renderer.vertexArena.alloc(byteLength);\n\t\tthis.device.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tvertexData.toUint8(),\n\t\t\t0,\n\t\t\tbyteLength,\n\t\t);\n\t\tpass.setVertexBuffer(0, region.buffer, region.offset, byteLength);\n\t\tpass.setIndexBuffer(this.indexBuffer, \"uint32\");\n\t\tpass.drawIndexed(6);\n\t\tvertexData.clear();\n\t}\n\n\t/**\n\t * indexed draw: 6 indices per 4 queued vertices, region-relative. The\n\t * fast path binds its single-source material; the normal path binds\n\t * the composed segment (up to eight textures).\n\t * @param {GPURenderPassEncoder} pass - the open pass\n\t * @param {number} vertexCount - pending vertex count\n\t * @override\n\t */\n\trecordDraw(pass, vertexCount) {\n\t\tpass.setBindGroup(\n\t\t\t1,\n\t\t\tthis.currentEffect !== null\n\t\t\t\t? this.currentMaterial\n\t\t\t\t: this.composeSegmentGroup(),\n\t\t);\n\t\tpass.setIndexBuffer(this.indexBuffer, \"uint32\");\n\t\tpass.drawIndexed((vertexCount / 4) * 6);\n\t}\n\n\t/**\n\t * @override\n\t */\n\tflush(topology) {\n\t\tconst effect = this.currentEffect;\n\t\tif (effect !== null && this.vertexData.vertexCount > 0) {\n\t\t\tif (this.currentMaterial === null) {\n\t\t\t\t// defensive: fast-path vertices with no adopted material\n\t\t\t\tthis.vertexData.clear();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// fast-path draw: same recording as the base flush, through the\n\t\t\t// effect's pipeline family with its group-3 binding. An effect\n\t\t\t// without a WGSL realization draws plain (graceful, GL-parity\n\t\t\t// with a disabled effect).\n\t\t\tconst binding = prepareEffectBinding(this.renderer, effect);\n\t\t\tif (binding !== null) {\n\t\t\t\tthis.flushWithEffect(binding);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tthis.vertexData.vertexCount > 0 &&\n\t\t\tthis.hasPendingMaterial() === false\n\t\t) {\n\t\t\t// defensive: pending vertices but no material was ever claimed\n\t\t\t// (out-of-contract pushes) — recording would bind nothing valid\n\t\t\tthis.vertexData.clear();\n\t\t\treturn;\n\t\t}\n\t\tsuper.flush(topology);\n\t\tthis.resetSegment();\n\t}\n\n\t/**\n\t * whether the pending vertices have a material to draw with — the lit\n\t * subclass overrides (its material model is the combined color+normal\n\t * group, not the segment slots)\n\t * @ignore\n\t */\n\thasPendingMaterial() {\n\t\treturn this.slotTable.size > 0;\n\t}\n\n\t/**\n\t * record the pending vertices through an effect's pipeline — blending\n\t * KEPT (the sprite composites live against the backdrop, the defining\n\t * semantic of the fast path vs the pooled blit)\n\t * @param {object} binding - the prepared effect binding\n\t * @ignore\n\t */\n\tflushWithEffect(binding) {\n\t\tconst renderer = this.renderer;\n\t\tconst vertexData = this.vertexData;\n\t\tconst vertexCount = vertexData.vertexCount;\n\t\tconst pass = renderer.ensurePass();\n\t\tconst byteLength = vertexCount * this.stride;\n\n\t\tconst region = renderer.vertexArena.alloc(byteLength);\n\t\tthis.device.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tvertexData.toUint8(),\n\t\t\t0,\n\t\t\tbyteLength,\n\t\t);\n\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tbinding.key,\n\t\t\t\"triangle-list\",\n\t\t\trenderer.currentBlendMode,\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tif (binding.hasEffectGroup) {\n\t\t\tpass.setBindGroup(2, renderer.pipelineCache.emptyBindGroup);\n\t\t\tpass.setBindGroup(3, binding.bindGroup, binding.dynamicOffsets);\n\t\t}\n\t\tpass.setVertexBuffer(0, region.buffer, region.offset, byteLength);\n\t\tthis.recordDraw(pass, vertexCount);\n\t\tvertexData.clear();\n\t}\n\n\t/**\n\t * @override\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tthis.currentEffect = null;\n\t\tthis.currentMaterial = null;\n\t\tthis.resetSegment();\n\t\tthis.composedGroups.clear();\n\t}\n\n\t/**\n\t * @override\n\t */\n\tdestroy() {\n\t\tthis.indexBuffer?.destroy();\n\t\tthis.indexBuffer = null;\n\t\tthis.currentMaterial = null;\n\t\tthis.resetSegment();\n\t\tthis.composedGroups.clear();\n\t\tsuper.destroy();\n\t}\n}\n","// the lighting math modules are pure CPU code shared with the GL backend\n// (published std140 layout, no GL calls)\nimport {\n\tBLOCK_BYTES,\n\tBLOCK_FLOATS,\n\twriteLight2dBlock,\n} from \"../../webgl/lighting/std140.ts\";\nimport litQuadWGSL from \"../shaders/quad-lit.wgsl\";\nimport WebGPUQuadBatcher from \"./quad_batcher.js\";\n\n/**\n * The WebGPU lit quad batcher — normal-mapped sprites shaded by the\n * Light2d std140 block, the port of the GL `LitQuadBatcher` under this\n * backend's single-texture-per-draw-segment model:\n *\n * - group 1 is a combined material (color texture + normal map, four\n *   entries); a change of either flushes the segment, so there is no\n *   per-vertex normal-texture id and no sampler ladder\n * - group 2 binds the std140 `Light2dBlock` with a dynamic offset — each\n *   `setLightUniforms` call snapshots the packed block into the effect\n *   uniform arena (queue writes execute before every recorded draw, so a\n *   shared buffer region per camera would be retroactively clobbered)\n * - normal maps live outside the TextureCache (same as GL): resident\n *   GPUTextures keyed by source object, re-uploaded when the source's\n *   duck-typed `version` stamp advances (NoiseTexture2d and friends)\n * @augments WebGPUQuadBatcher\n * @category Rendering\n */\nexport default class WebGPULitQuadBatcher extends WebGPUQuadBatcher {\n\t/**\n\t * @override\n\t */\n\tinit(renderer, settings) {\n\t\tsuper.init(renderer, settings);\n\t\tconst cache = renderer.pipelineCache;\n\t\tconst device = renderer.device;\n\n\t\tthis.litMaterialLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS lit material\",\n\t\t\tentries: [\n\t\t\t\t{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t\t{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t{ binding: 3, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t],\n\t\t});\n\t\tthis.lightsLayout = device.createBindGroupLayout({\n\t\t\tlabel: \"melonJS light block\",\n\t\t\tentries: [\n\t\t\t\t{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\tbuffer: {\n\t\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\t\tminBindingSize: BLOCK_BYTES,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\t// the lit family rides the frozen quad vertex layout\n\t\tthis.shaderKey = cache.registerShader(litQuadWGSL, {\n\t\t\tbindGroupLayouts: [\n\t\t\t\tcache.frameLayout,\n\t\t\t\tthis.litMaterialLayout,\n\t\t\t\tthis.lightsLayout,\n\t\t\t],\n\t\t\tvertexLayoutKey: \"quad\",\n\t\t\tlabel: \"melonJS lit quad shader\",\n\t\t});\n\n\t\t// CPU staging for the std140 block (zero-filled tail keeps the full\n\t\t// BLOCK_BYTES write valid for minBindingSize)\n\t\tthis.blockData = new Float32Array(BLOCK_FLOATS);\n\t\t// light-block bind group per arena page (pages persist across frames)\n\t\tthis.lightBindGroups = new Map();\n\t\t// the current frame's snapshot binding: {bindGroup, dynamicOffset}\n\t\tthis.lightBinding = null;\n\n\t\t// combined color+normal bind groups: colorView → (normalView → group)\n\t\tthis.litMaterials = new Map();\n\t\t// resident normal-map textures: source → {texture, view, version, width, height}\n\t\tthis.normalTextures = new Map();\n\t}\n\n\t/**\n\t * Snapshot the packed light uniforms for the draws that follow — called\n\t * by {@link WebGPURenderer#setLightUniforms} once per camera per frame.\n\t * @param {object} packed - what packLights produced\n\t */\n\tsetLightUniforms(packed) {\n\t\tconst renderer = this.renderer;\n\t\tconst device = this.device;\n\t\twriteLight2dBlock(this.blockData, packed);\n\t\tconst region = renderer.effectUniformArena.alloc(\n\t\t\tBLOCK_BYTES,\n\t\t\tdevice.limits.minUniformBufferOffsetAlignment,\n\t\t);\n\t\tdevice.queue.writeBuffer(\n\t\t\tregion.buffer,\n\t\t\tregion.offset,\n\t\t\tthis.blockData.buffer,\n\t\t\t0,\n\t\t\tBLOCK_BYTES,\n\t\t);\n\t\tlet bindGroup = this.lightBindGroups.get(region.buffer);\n\t\tif (typeof bindGroup === \"undefined\") {\n\t\t\tbindGroup = device.createBindGroup({\n\t\t\t\tlabel: \"melonJS light block\",\n\t\t\t\tlayout: this.lightsLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: { buffer: region.buffer, size: BLOCK_BYTES },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tthis.lightBindGroups.set(region.buffer, bindGroup);\n\t\t}\n\t\tthis.lightBinding = {\n\t\t\tbindGroup,\n\t\t\tdynamicOffset: region.offset,\n\t\t\t// the arena region only holds these bytes until the next frame's\n\t\t\t// reset — the binding must never outlive the frame it was\n\t\t\t// snapshotted in\n\t\t\tframeId: renderer.frameId,\n\t\t};\n\t}\n\n\t/**\n\t * Whether a light block was snapshotted during the given frame — the\n\t * renderer's lit gate requires this: a previous-frame binding points\n\t * into a reset arena region, so it would sample reused bytes.\n\t * @param {number} frameId - the renderer's current frame stamp\n\t * @returns {boolean} true when lit draws can bind a valid light block\n\t */\n\thasCurrentLightBinding(frameId) {\n\t\treturn this.lightBinding !== null && this.lightBinding.frameId === frameId;\n\t}\n\n\t/**\n\t * Resolve (and lazily upload) the resident texture for a normal-map\n\t * source, re-uploading when its duck-typed `version` stamp advances.\n\t * @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} source - the normal-map image\n\t * @returns {object} {texture, view, version, width, height}\n\t * @ignore\n\t */\n\tresidentNormalMap(source) {\n\t\tconst version = source.version ?? 0;\n\t\tconst frameId = this.renderer.frameId;\n\t\tlet entry = this.normalTextures.get(source);\n\t\tconst width = source.width || source.videoWidth || 1;\n\t\tconst height = source.height || source.videoHeight || 1;\n\t\tif (\n\t\t\ttypeof entry === \"undefined\" ||\n\t\t\tentry.width !== width ||\n\t\t\tentry.height !== height ||\n\t\t\t// An in-place re-upload is retroactive: queue writes execute\n\t\t\t// before EVERY draw recorded this frame, so a version bump on a\n\t\t\t// map already sampled this frame must land in a fresh texture or\n\t\t\t// earlier draws would show the new pixels (same rule as the\n\t\t\t// texture store's color records)\n\t\t\t(entry.version !== version && entry.frameId === frameId)\n\t\t) {\n\t\t\tif (typeof entry !== \"undefined\") {\n\t\t\t\tthis.renderer.retireTexture(entry.texture);\n\t\t\t}\n\t\t\tconst texture = this.device.createTexture({\n\t\t\t\tlabel: \"melonJS normal map\",\n\t\t\t\tsize: [width, height],\n\t\t\t\tformat: \"rgba8unorm\",\n\t\t\t\tusage:\n\t\t\t\t\tGPUTextureUsage.TEXTURE_BINDING |\n\t\t\t\t\tGPUTextureUsage.COPY_DST |\n\t\t\t\t\tGPUTextureUsage.RENDER_ATTACHMENT,\n\t\t\t});\n\t\t\tentry = {\n\t\t\t\ttexture,\n\t\t\t\tview: texture.createView(),\n\t\t\t\tversion: undefined,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tframeId: -1,\n\t\t\t};\n\t\t\tthis.normalTextures.set(source, entry);\n\t\t}\n\t\tif (entry.version !== version) {\n\t\t\t// normals are geometry, not color — never premultiply\n\t\t\tthis.device.queue.copyExternalImageToTexture(\n\t\t\t\t{ source },\n\t\t\t\t{ texture: entry.texture, premultipliedAlpha: false },\n\t\t\t\t[width, height],\n\t\t\t);\n\t\t\tentry.version = version;\n\t\t}\n\t\t// stamp: draws recorded this frame sample this texture\n\t\tentry.frameId = frameId;\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Add a lit textured quad — the base contract plus the normal map the\n\t * segment shades with.\n\t * @param {object} texture - the texture atlas to draw with\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} w - destination width\n\t * @param {number} h - destination height\n\t * @param {number} u0 - texture UV (u0)\n\t * @param {number} v0 - texture UV (v0)\n\t * @param {number} u1 - texture UV (u1)\n\t * @param {number} v1 - texture UV (v1)\n\t * @param {number} tint - tint color in UINT32 (argb) format\n\t * @param {boolean} [reupload=false] - force the source pixels to re-upload\n\t * @param {object} [normalMap] - the normal-map image paired with the texture\n\t * @override\n\t */\n\taddQuad(\n\t\ttexture,\n\t\tx,\n\t\ty,\n\t\tw,\n\t\th,\n\t\tu0,\n\t\tv0,\n\t\tu1,\n\t\tv1,\n\t\ttint,\n\t\treupload = false,\n\t\tnormalMap,\n\t) {\n\t\tconst renderer = this.renderer;\n\t\t// resolve the combined color+normal material for this segment\n\t\tconst record = renderer.textureStore.getResidentRecord(texture, {\n\t\t\tforce: reupload,\n\t\t});\n\t\tconst normal = this.residentNormalMap(normalMap);\n\n\t\tlet byNormal = this.litMaterials.get(record.view);\n\t\tif (typeof byNormal === \"undefined\") {\n\t\t\tbyNormal = new Map();\n\t\t\tthis.litMaterials.set(record.view, byNormal);\n\t\t}\n\t\tlet combined = byNormal.get(normal.view);\n\t\tif (typeof combined === \"undefined\") {\n\t\t\tconst filter =\n\t\t\t\ttypeof texture.filter === \"string\"\n\t\t\t\t\t? texture.filter\n\t\t\t\t\t: renderer.getDefaultTextureFilter();\n\t\t\tconst wrap = texture.repeat ?? \"no-repeat\";\n\t\t\tconst store = renderer.textureStore;\n\t\t\tcombined = this.device.createBindGroup({\n\t\t\t\tlabel: \"melonJS lit material\",\n\t\t\t\tlayout: this.litMaterialLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{ binding: 0, resource: record.view },\n\t\t\t\t\t{ binding: 1, resource: store.getSampler(filter, wrap) },\n\t\t\t\t\t{ binding: 2, resource: normal.view },\n\t\t\t\t\t{ binding: 3, resource: store.getSampler(filter, \"no-repeat\") },\n\t\t\t\t],\n\t\t\t});\n\t\t\tbyNormal.set(normal.view, combined);\n\t\t}\n\n\t\tif (this.vertexData.isFull(4) || combined !== this.currentMaterial) {\n\t\t\tthis.flush();\n\t\t\tthis.currentMaterial = combined;\n\t\t}\n\n\t\t// transform + push the four corners exactly like the base batcher\n\t\tthis.pushQuadVertices(x, y, w, h, u0, v0, u1, v1, tint);\n\t}\n\n\t/**\n\t * Drop every combined color+normal bind group — each pairing embeds a\n\t * sampler resolved from the default texture filter, so a filter change\n\t * must rebuild them (the lit-tier counterpart of the texture store's\n\t * invalidateBindGroups; the resident textures stay).\n\t * @override\n\t */\n\tclearMaterialCache() {\n\t\tsuper.clearMaterialCache();\n\t\tthis.litMaterials.clear();\n\t}\n\n\t/**\n\t * the lit material model is the combined color+normal bind group, not\n\t * the base segment slots\n\t * @override\n\t * @ignore\n\t */\n\thasPendingMaterial() {\n\t\treturn this.currentMaterial !== null;\n\t}\n\n\t/**\n\t * lit draws bind the combined material at 1 and the light block at 2\n\t * @override\n\t */\n\trecordDraw(pass, vertexCount) {\n\t\tpass.setBindGroup(1, this.currentMaterial);\n\t\tif (this.lightBinding !== null) {\n\t\t\tpass.setBindGroup(2, this.lightBinding.bindGroup, [\n\t\t\t\tthis.lightBinding.dynamicOffset,\n\t\t\t]);\n\t\t}\n\t\tpass.setIndexBuffer(this.indexBuffer, \"uint32\");\n\t\tpass.drawIndexed((vertexCount / 4) * 6);\n\t}\n\n\t/**\n\t * @override\n\t */\n\treset() {\n\t\tsuper.reset();\n\t\tfor (const entry of this.normalTextures.values()) {\n\t\t\tthis.renderer.retireTexture(entry.texture);\n\t\t}\n\t\tthis.normalTextures.clear();\n\t\tthis.litMaterials.clear();\n\t\tthis.lightBindGroups.clear();\n\t\tthis.lightBinding = null;\n\t}\n\n\t/**\n\t * @override\n\t */\n\tdestroy() {\n\t\tthis.reset();\n\t\tsuper.destroy();\n\t}\n}\n","import {\n\texpandLinesToTriangles,\n\tpushPrimitiveRange,\n} from \"../../gpu/primitives.ts\";\nimport WebGPUBatcher from \"./webgpu_batcher.js\";\n\n/**\n * additional import for TypeScript\n * @import {Point} from \"./../../../geometries/point.ts\";\n */\n\n/**\n * The WebGPU primitive batcher — shape geometry accumulation with the same\n * frozen 24-byte vertex layout and CPU tessellation as the WebGL\n * `PrimitiveBatcher`, addressed purely in the portable topology vocabulary.\n *\n * `uLineWidth` lives in the shared frame-globals uniform block: a width\n * change flushes pending vertices and pushes a fresh dynamic-offset slot,\n * replacing the per-program uniform of the GL backend.\n *\n * The two non-portable engine topologies are emulated in this front end,\n * as `PORTABLE_TOPOLOGIES` documents: `\"line-loop\"` draws as a strip with\n * an explicit closing vertex, `\"triangle-fan\"` re-expands on the CPU into\n * a triangle list. No engine path emits either — they only serve direct\n * user calls.\n * @augments WebGPUBatcher\n * @category Rendering\n */\nexport default class WebGPUPrimitiveBatcher extends WebGPUBatcher {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @override\n\t */\n\tinit(renderer, settings) {\n\t\tsuper.init(\n\t\t\trenderer,\n\t\t\tsettings ?? {\n\t\t\t\tshaderKey: \"primitive\",\n\t\t\t\ttopology: \"triangle-list\",\n\t\t\t\tattributes: [\n\t\t\t\t\t{\n\t\t\t\t\t\t// vec3: (x, y, z). z carries `renderable.depth` for\n\t\t\t\t\t\t// perspective projection (Camera3d). Stride = 24 bytes.\n\t\t\t\t\t\tname: \"aVertex\",\n\t\t\t\t\t\tformat: \"float32x3\",\n\t\t\t\t\t\toffset: 0 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"aNormal\",\n\t\t\t\t\t\tformat: \"float32x2\",\n\t\t\t\t\t\toffset: 3 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"aColor\",\n\t\t\t\t\t\tformat: \"unorm8x4\",\n\t\t\t\t\t\toffset: 5 * Float32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Draw an array of vertices with the given topology.\n\t * @param {string} topology - portable topology name (\"point-list\", \"line-list\", \"line-strip\", \"triangle-list\", \"triangle-strip\"; \"line-loop\" and \"triangle-fan\" are emulated)\n\t * @param {Point[]} verts - an array of vertices\n\t * @param {number} [vertexCount=verts.length] - amount of points defined in the points array\n\t */\n\tdrawVertices(topology, verts, vertexCount = verts.length) {\n\t\tconst renderer = this.renderer;\n\t\tconst lineWidth = renderer.lineWidth;\n\n\t\t// the line width rides in the frame-globals block — a change means\n\t\t// pending vertices drain under the old slot, then a new slot is\n\t\t// pushed for everything after. Compared against the value the\n\t\t// CURRENT slot was written with (not a batcher-local cache):\n\t\t// clear() rewrites the slot with lineWidth = 1 every frame, so a\n\t\t// local cache goes stale across frames\n\t\tif (lineWidth !== renderer.currentFrameLineWidth) {\n\t\t\tthis.flush(this.topology);\n\t\t\trenderer.pushFrameGlobals();\n\t\t}\n\n\t\t// expand thick lines to triangles with normals for shader-based\n\t\t// expansion\n\t\tif (topology === \"line-list\" && lineWidth > 1) {\n\t\t\tthis.#expandLinesToTriangles(verts, vertexCount);\n\t\t\treturn;\n\t\t}\n\n\t\t// emulate the non-portable topologies before anything batches\n\t\tif (topology === \"triangle-fan\") {\n\t\t\tthis.#drawTriangleFan(verts, vertexCount);\n\t\t\treturn;\n\t\t}\n\t\tconst closeLoop = topology === \"line-loop\";\n\t\tif (closeLoop) {\n\t\t\ttopology = \"line-strip\";\n\t\t}\n\n\t\tconst vertexData = this.vertexData;\n\t\tconst alpha = renderer.getGlobalAlpha();\n\t\tconst colorUint32 = renderer.currentColor.toUint32(alpha);\n\t\t// z = current renderer depth (Renderable.preDraw); a no-op under\n\t\t// ortho, consumed by perspective (Camera3d)\n\t\tconst z = renderer.currentDepth;\n\n\t\t// flush if drawing vertices with a different topology\n\t\tif (topology !== this.topology) {\n\t\t\tthis.flush(this.topology);\n\t\t\tthis.topology = topology;\n\t\t}\n\n\t\tif (vertexCount < vertexData.maxVertex) {\n\t\t\t// fast path: the shape fits in one batch\n\t\t\tif (vertexData.isFull(vertexCount + (closeLoop ? 1 : 0))) {\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t\tthis.#pushRange(verts, 0, vertexCount, colorUint32, z);\n\t\t\tif (closeLoop) {\n\t\t\t\t// close the loop: duplicate the first vertex after the last\n\t\t\t\tthis.#pushRange(verts, 0, 1, colorUint32, z);\n\t\t\t}\n\t\t} else {\n\t\t\t// a single shape larger than the whole vertex buffer — split it\n\t\t\t// into buffer-sized chunks on primitive boundaries\n\t\t\tthis.#drawVerticesChunked(\n\t\t\t\tcloseLoop ? \"line-loop\" : topology,\n\t\t\t\tverts,\n\t\t\t\tvertexCount,\n\t\t\t\tcolorUint32,\n\t\t\t\tz,\n\t\t\t);\n\t\t}\n\n\t\t// strips must not concatenate across shapes\n\t\tif (this.topology === \"line-strip\" || this.topology === \"triangle-strip\") {\n\t\t\tthis.flush(this.topology);\n\t\t}\n\t}\n\n\t/**\n\t * CPU re-expansion of a triangle fan into a triangle list\n\t * @ignore\n\t */\n\t#drawTriangleFan(verts, vertexCount) {\n\t\tconst renderer = this.renderer;\n\t\tconst vertexData = this.vertexData;\n\t\tconst colorUint32 = renderer.currentColor.toUint32(\n\t\t\trenderer.getGlobalAlpha(),\n\t\t);\n\t\tconst z = renderer.currentDepth;\n\n\t\tif (this.topology !== \"triangle-list\") {\n\t\t\tthis.flush(this.topology);\n\t\t\tthis.topology = \"triangle-list\";\n\t\t}\n\t\tfor (let i = 1; i < vertexCount - 1; i++) {\n\t\t\tif (vertexData.isFull(3)) {\n\t\t\t\tthis.flush();\n\t\t\t}\n\t\t\tthis.#pushRange(verts, 0, 1, colorUint32, z);\n\t\t\tthis.#pushRange(verts, i, i + 2, colorUint32, z);\n\t\t}\n\t}\n\n\t/**\n\t * Push `verts[start..end)` into the vertex buffer, transformed by the\n\t * current view matrix. The caller guarantees the range fits.\n\t * @ignore\n\t */\n\t#pushRange(verts, start, end, colorUint32, z) {\n\t\t// the shared neutral range push (z-column-aware transform) — one\n\t\t// copy for both backends, see `gpu/primitives.ts`\n\t\tpushPrimitiveRange(\n\t\t\tthis.vertexData,\n\t\t\tthis.renderer.currentTransform,\n\t\t\tverts,\n\t\t\tstart,\n\t\t\tend,\n\t\t\tcolorUint32,\n\t\t\tz,\n\t\t);\n\t}\n\n\t/**\n\t * Draw an over-capacity vertex list as a sequence of buffer-sized\n\t * chunks, split on primitive boundaries so every triangle/line stays\n\t * whole (same boundary rules as the WebGL batcher).\n\t * @ignore\n\t */\n\t#drawVerticesChunked(topology, verts, vertexCount, colorUint32, z) {\n\t\t// stay one below maxVertex to match isFull()'s `>=` convention\n\t\tconst capacity = this.vertexData.maxVertex - 1;\n\t\tlet step = capacity;\n\t\tlet overlap = 0;\n\t\tlet drawTopology = topology;\n\t\tswitch (topology) {\n\t\t\tcase \"triangle-list\":\n\t\t\t\tstep = capacity - (capacity % 3);\n\t\t\t\tbreak;\n\t\t\tcase \"line-list\":\n\t\t\t\tstep = capacity - (capacity % 2);\n\t\t\t\tbreak;\n\t\t\tcase \"line-strip\":\n\t\t\t\toverlap = 1;\n\t\t\t\tbreak;\n\t\t\tcase \"line-loop\":\n\t\t\t\t// chunks are open strips; the loop is closed explicitly below\n\t\t\t\toverlap = 1;\n\t\t\t\tdrawTopology = \"line-strip\";\n\t\t\t\tbreak;\n\t\t\tcase \"triangle-strip\":\n\t\t\t\toverlap = 2;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// point-list: any split works. \"triangle-fan\" never reaches\n\t\t\t\t// here — #drawTriangleFan re-expands with per-triangle\n\t\t\t\t// capacity checks, so fans self-chunk\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.topology = drawTopology;\n\n\t\tlet start = 0;\n\t\twhile (start < vertexCount) {\n\t\t\t// each chunk starts on an empty buffer\n\t\t\tthis.flush(drawTopology);\n\t\t\tconst count = Math.min(vertexCount - start, step);\n\t\t\tthis.#pushRange(verts, start, start + count, colorUint32, z);\n\t\t\tstart += count;\n\t\t\tif (start < vertexCount) {\n\t\t\t\tstart -= overlap;\n\t\t\t}\n\t\t}\n\n\t\tif (topology === \"line-loop\") {\n\t\t\t// close the loop: duplicate the first vertex after the last one\n\t\t\tif (this.vertexData.isFull(1)) {\n\t\t\t\tthis.flush(drawTopology);\n\t\t\t\tthis.#pushRange(verts, vertexCount - 1, vertexCount, colorUint32, z);\n\t\t\t}\n\t\t\tthis.#pushRange(verts, 0, 1, colorUint32, z);\n\t\t}\n\t}\n\n\t/**\n\t * Expand line pairs into triangles with perpendicular normals.\n\t * The vertex shader offsets each vertex by aNormal * uLineWidth * 0.5,\n\t * producing thick lines without geometry expansion in the renderer.\n\t * @param {Point[]} verts - line vertices in pairs [from, to, from, to, ...]\n\t * @param {number} vertexCount - number of vertices\n\t * @ignore\n\t */\n\t#expandLinesToTriangles(verts, vertexCount) {\n\t\t// switch to triangle-list topology, then delegate the expansion to\n\t\t// the shared neutral helper — see `gpu/primitives.ts`\n\t\tif (this.topology !== \"triangle-list\") {\n\t\t\tthis.flush(this.topology);\n\t\t\tthis.topology = \"triangle-list\";\n\t\t}\n\t\tconst renderer = this.renderer;\n\t\texpandLinesToTriangles(\n\t\t\tthis.vertexData,\n\t\t\trenderer.currentTransform,\n\t\t\tverts,\n\t\t\tvertexCount,\n\t\t\trenderer.currentColor.toUint32(renderer.getGlobalAlpha()),\n\t\t\trenderer.currentDepth,\n\t\t\t() => {\n\t\t\t\tthis.flush();\n\t\t\t},\n\t\t);\n\t}\n}\n","/**\n * Per-frame GPU buffer arena — a bump allocator over a persistent list of\n * fixed-size `GPUBuffer` pages.\n *\n * Why an arena: melonJS flushes many times per frame (texture change, blend\n * change, scissor change, …). Under WebGL each flush re-uploads the shared\n * vertex buffer; under WebGPU all draw commands of a frame are recorded\n * first and execute at `queue.submit()` time, so every flush must write its\n * vertices into a region **no later flush overwrites** — `queue.writeBuffer`\n * data lands before the submitted commands run, and two flushes sharing one\n * region would have the second write clobber the first before either draw\n * executes.\n *\n * Why a page *list* rather than one growable buffer: draw commands already\n * recorded against earlier pages must stay valid; replacing a buffer\n * mid-frame would invalidate them. Pages persist across frames; `reset()`\n * just rewinds the bump pointer.\n * @ignore\n */\nexport default class WebGPUBufferArena {\n\t/**\n\t * @param {GPUDevice} device - the device to allocate pages on\n\t * @param {object} [options] - arena options\n\t * @param {number} [options.pageSize=1048576] - byte size of each page\n\t * @param {number} [options.usage] - GPUBufferUsage flags for the pages\n\t * @param {string} [options.label] - debug label prefix for the pages\n\t */\n\tconstructor(device, options = {}) {\n\t\tthis.device = device;\n\t\tthis.pageSize = options.pageSize ?? 1 << 20;\n\t\tthis.usage =\n\t\t\toptions.usage ?? GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST;\n\t\tthis.label = options.label ?? \"melonJS arena\";\n\t\t/** @type {GPUBuffer[]} */\n\t\tthis.pages = [];\n\t\tthis.pageIndex = 0;\n\t\tthis.offset = 0;\n\t}\n\n\t/**\n\t * rewind the arena — called once per frame, before any allocation\n\t */\n\treset() {\n\t\tthis.pageIndex = 0;\n\t\tthis.offset = 0;\n\t}\n\n\t/**\n\t * Reserve `byteLength` bytes and return where they live. The region is\n\t * valid until the next `reset()`.\n\t * @param {number} byteLength - bytes to reserve (must fit in one page)\n\t * @param {number} [alignment=4] - required offset alignment (a power of\n\t *   two; effect uniform snapshots pass the device's\n\t *   `minUniformBufferOffsetAlignment` so the region can serve as a\n\t *   dynamic bind-group offset)\n\t * @returns {{buffer: GPUBuffer, offset: number}} the reserved region\n\t */\n\talloc(byteLength, alignment = 4) {\n\t\t// writeBuffer offsets must be 4-byte aligned (callers may need more)\n\t\tconst aligned = (byteLength + 3) & ~3;\n\t\tthis.offset = (this.offset + alignment - 1) & ~(alignment - 1);\n\t\tif (aligned > this.pageSize) {\n\t\t\tthrow new Error(\n\t\t\t\t`WebGPUBufferArena: allocation of ${byteLength} bytes exceeds the page size (${this.pageSize})`,\n\t\t\t);\n\t\t}\n\t\tif (this.offset + aligned > this.pageSize) {\n\t\t\tthis.pageIndex++;\n\t\t\tthis.offset = 0;\n\t\t}\n\t\tif (this.pageIndex >= this.pages.length) {\n\t\t\tthis.pages.push(\n\t\t\t\tthis.device.createBuffer({\n\t\t\t\t\tlabel: `${this.label} page ${this.pages.length}`,\n\t\t\t\t\tsize: this.pageSize,\n\t\t\t\t\tusage: this.usage,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tconst region = {\n\t\t\tbuffer: this.pages[this.pageIndex],\n\t\t\toffset: this.offset,\n\t\t};\n\t\tthis.offset += aligned;\n\t\treturn region;\n\t}\n\n\t/**\n\t * release every page (device loss / renderer destroy)\n\t */\n\tdestroy() {\n\t\tfor (const page of this.pages) {\n\t\t\tpage.destroy();\n\t\t}\n\t\tthis.pages.length = 0;\n\t\tthis.pageIndex = 0;\n\t\tthis.offset = 0;\n\t}\n}\n","import {\n\tCLEAR_UNIFORM_SIZE,\n\tFRAME_UNIFORM_SIZE,\n} from \"../pipeline/bindgroups.js\";\n\n/**\n * Per-frame uniform ring — slot-granular bump allocation over persistent\n * uniform-buffer pages, with one cached dynamic-offset bind group per page\n * and per layout.\n *\n * This is the WebGPU realization of the \"frame globals in one uniform\n * buffer, bound once\" shape (#1555): projection and line width live in\n * group 0, and a *new slot* is pushed whenever either changes mid-frame\n * (floating containers swap the projection around their children), so every\n * recorded draw keeps the dynamic offset that was current when its vertices\n * were queued. The mid-frame `clearColor` pipeline shares the same pages\n * through its own layout/bind-group pair.\n * @ignore\n */\nexport default class WebGPUUniformRing {\n\t/**\n\t * @param {GPUDevice} device - the device to allocate on\n\t * @param {GPUBindGroupLayout} frameLayout - group-0 layout of the draw pipelines\n\t * @param {GPUBindGroupLayout} clearLayout - group-0 layout of the clear pipeline\n\t */\n\tconstructor(device, frameLayout, clearLayout) {\n\t\tthis.device = device;\n\t\tthis.frameLayout = frameLayout;\n\t\tthis.clearLayout = clearLayout;\n\t\tthis.slotSize = Math.max(\n\t\t\t256,\n\t\t\tdevice.limits.minUniformBufferOffsetAlignment,\n\t\t);\n\t\tthis.slotsPerPage = 64;\n\t\tthis.pageSize = this.slotSize * this.slotsPerPage;\n\t\t/** @type {{buffer: GPUBuffer, frameGroup: GPUBindGroup, clearGroup: GPUBindGroup}[]} */\n\t\tthis.pages = [];\n\t\tthis.slot = 0;\n\t\t// scratch for uniform writes (80 bytes covers both layouts)\n\t\tthis.scratch = new Float32Array(FRAME_UNIFORM_SIZE / 4);\n\t}\n\n\t/**\n\t * rewind the ring — called once per frame\n\t */\n\treset() {\n\t\tthis.slot = 0;\n\t}\n\n\t/** @ignore */\n\t#allocSlot() {\n\t\tconst pageIndex = (this.slot / this.slotsPerPage) | 0;\n\t\tif (pageIndex >= this.pages.length) {\n\t\t\tconst buffer = this.device.createBuffer({\n\t\t\t\tlabel: `melonJS uniform ring page ${this.pages.length}`,\n\t\t\t\tsize: this.pageSize,\n\t\t\t\tusage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n\t\t\t});\n\t\t\tthis.pages.push({\n\t\t\t\tbuffer,\n\t\t\t\tframeGroup: this.device.createBindGroup({\n\t\t\t\t\tlabel: \"melonJS frame globals\",\n\t\t\t\t\tlayout: this.frameLayout,\n\t\t\t\t\tentries: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\t\tresource: { buffer, size: FRAME_UNIFORM_SIZE },\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t\tclearGroup: this.device.createBindGroup({\n\t\t\t\t\tlabel: \"melonJS clear color\",\n\t\t\t\t\tlayout: this.clearLayout,\n\t\t\t\t\tentries: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\t\tresource: { buffer, size: CLEAR_UNIFORM_SIZE },\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t\tconst page = this.pages[pageIndex];\n\t\tconst dynamicOffset = (this.slot % this.slotsPerPage) * this.slotSize;\n\t\tthis.slot++;\n\t\treturn { page, dynamicOffset };\n\t}\n\n\t/**\n\t * Write a frame-globals slot (projection matrix + line width).\n\t * @param {import(\"../../math/matrix3d.ts\").Matrix3d} projection - the projection matrix\n\t * @param {number} lineWidth - current stroke line width\n\t * @returns {{bindGroup: GPUBindGroup, dynamicOffset: number}} the binding for subsequent draws\n\t */\n\tpushFrameGlobals(projection, lineWidth) {\n\t\tconst { page, dynamicOffset } = this.#allocSlot();\n\t\tconst scratch = this.scratch;\n\t\tscratch.set(projection.val, 0);\n\t\tscratch[16] = lineWidth;\n\t\tthis.device.queue.writeBuffer(\n\t\t\tpage.buffer,\n\t\t\tdynamicOffset,\n\t\t\tscratch.buffer,\n\t\t\t0,\n\t\t\tFRAME_UNIFORM_SIZE,\n\t\t);\n\t\treturn { bindGroup: page.frameGroup, dynamicOffset };\n\t}\n\n\t/**\n\t * Write a clear-color slot.\n\t * @param {number} r - red (0..1)\n\t * @param {number} g - green (0..1)\n\t * @param {number} b - blue (0..1)\n\t * @param {number} a - alpha (0..1)\n\t * @returns {{bindGroup: GPUBindGroup, dynamicOffset: number}} the binding for the clear draw\n\t */\n\tpushClearColor(r, g, b, a) {\n\t\tconst { page, dynamicOffset } = this.#allocSlot();\n\t\tconst scratch = this.scratch;\n\t\tscratch[0] = r;\n\t\tscratch[1] = g;\n\t\tscratch[2] = b;\n\t\tscratch[3] = a;\n\t\tthis.device.queue.writeBuffer(\n\t\t\tpage.buffer,\n\t\t\tdynamicOffset,\n\t\t\tscratch.buffer,\n\t\t\t0,\n\t\t\tCLEAR_UNIFORM_SIZE,\n\t\t);\n\t\treturn { bindGroup: page.clearGroup, dynamicOffset };\n\t}\n\n\t/**\n\t * release every page (device loss / renderer destroy)\n\t */\n\tdestroy() {\n\t\tfor (const page of this.pages) {\n\t\t\tpage.buffer.destroy();\n\t\t}\n\t\tthis.pages.length = 0;\n\t\tthis.slot = 0;\n\t}\n}\n","// melonJS WebGPU orthogonal TMX layer shader — the WGSL port of\n// orthogonal-tmxlayer.vert/frag (WebGL2).\n//\n// Per fragment: recover the world-pixel position from the quad UV, walk\n// candidate cells (the geometric cell, plus cells whose oversized\n// bottom-aligned tiles could reach this fragment), fetch GIDs from the\n// per-layer index texture, and sample the tileset atlas at the correct\n// sub-region. One draw per tileset — the GID-range guard discards cells\n// belonging to other tilesets.\n//\n// Index texture encoding (rgba8unorm, one cell per texel):\n//   R = GID low byte, G = GID high byte, B = flip mask (H|V<<1|AD<<2)\n// Animation lookup (rgba8unorm, 1 row): per local id, the CURRENT frame's\n// local id, same R/G byte-pair encoding.\n//\n// textureLoad replaces texelFetch (byte-exact, no sampler); the atlas\n// sample uses textureSampleLevel — the sample sites sit in non-uniform\n// control flow, where implicit-derivative sampling is not allowed (WGSL\n// uniformity rule; atlases are single-level so the output is identical).\n\nstruct FrameUniforms {\n\tprojection : mat4x4<f32>,\n\tlineWidth : f32,\n};\n\nstruct TMXUniforms {\n\tuMapSize : vec2f,\n\tuCellSize : vec2f,\n\tuTileSize : vec2f,\n\tuOverflow : vec2f,\n\tuTilesetCols : vec2f,\n\tuInvTilesetSize : vec2f,\n\tuTilesetMargin : vec4f, // (marginX, marginY, spacingX, spacingY)\n\tuGidRange : vec2f,      // (firstgid, lastgid)\n\tuVisibleStart : vec2f,\n\tuVisibleSize : vec2f,\n\tuOpacity : f32,\n\tuAnimSize : f32,        // entries in the anim lookup, 0 when disabled\n\tuTint : vec4f,\n};\n\n@group(0) @binding(0) var<uniform> uFrame : FrameUniforms;\n@group(1) @binding(0) var uTexture : texture_2d<f32>;   // tileset atlas\n@group(1) @binding(1) var uSampler : sampler;\n@group(3) @binding(0) var<uniform> tmx : TMXUniforms;\n@group(3) @binding(1) var uTileIndex : texture_2d<f32>; // per-layer GID index\n@group(3) @binding(2) var uAnimLookup : texture_2d<f32>;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) vRegion : vec2f,\n\t@location(1) vColor : vec4f,\n};\n\n@vertex\nfn vertex_main(\n\t@location(0) aVertex : vec3f,\n\t@location(1) aRegion : vec2f,\n\t@location(2) aColor : vec4f,\n\t@location(3) aTextureId : f32,\n) -> VSOut {\n\tvar out : VSOut;\n\tlet clip = uFrame.projection * vec4f(aVertex, 1.0);\n\t// GL-convention clip z in [-w, w] -> WebGPU [0, w]\n\tout.position = vec4f(clip.xy, (clip.z + clip.w) * 0.5, clip.w);\n\tout.vColor = vec4f(aColor.bgr * aColor.a, aColor.a);\n\tout.vRegion = aRegion;\n\treturn out;\n}\n\nconst MAX_OVERFLOW : i32 = 4;\n\nstruct CellSample {\n\thit : bool,\n\tcolor : vec4f,\n}\n\n// Try to render the tile at cell (cx, cy) for the current fragment —\n// identical logic to the GLSL tryRenderCell, returning a hit/color pair\n// (WGSL has no out parameters).\nfn tryRenderCell(cx : i32, cy : i32, worldPx : vec2f) -> CellSample {\n\tvar miss : CellSample;\n\tmiss.hit = false;\n\tmiss.color = vec4f(0.0);\n\n\tlet mapW = i32(tmx.uMapSize.x);\n\tlet mapH = i32(tmx.uMapSize.y);\n\tif (cx < 0 || cx >= mapW || cy < 0 || cy >= mapH) {\n\t\treturn miss;\n\t}\n\n\t// byte-exact read: rgba8unorm texel back to byte ints\n\tlet cellF = textureLoad(uTileIndex, vec2i(cx, cy), 0);\n\tlet cell = vec4u(cellF * 255.0 + 0.5);\n\tlet firstGid = i32(tmx.uGidRange.x);\n\tlet gid = i32(cell.r) | (i32(cell.g) << 8u);\n\tif (gid < firstGid || gid > i32(tmx.uGidRange.y)) {\n\t\treturn miss;\n\t}\n\n\t// oversized tiles are bottom-aligned vertically, left-aligned horizontally\n\tlet tileWorldOrigin = vec2f(\n\t\tf32(cx) * tmx.uCellSize.x,\n\t\t(f32(cy) + 1.0) * tmx.uCellSize.y - tmx.uTileSize.y,\n\t);\n\tvar inTile = (worldPx - tileWorldOrigin) / tmx.uTileSize;\n\tif (inTile.x < 0.0 || inTile.x >= 1.0 || inTile.y < 0.0 || inTile.y >= 1.0) {\n\t\treturn miss;\n\t}\n\n\t// flip mask + axis-swap trick: AD transposes; with AD set, H and V\n\t// swap their effective axes (matches the legacy buildFlipTransform)\n\tlet flipMask = i32(cell.b);\n\tlet flipH = f32(flipMask & 1);\n\tlet flipV = f32((flipMask >> 1u) & 1);\n\tlet flipAD = f32((flipMask >> 2u) & 1);\n\tinTile = mix(inTile, inTile.yx, vec2f(flipAD));\n\tlet effH = mix(flipH, flipV, flipAD);\n\tlet effV = mix(flipV, flipH, flipAD);\n\tinTile.x = mix(inTile.x, 1.0 - inTile.x, effH);\n\tinTile.y = mix(inTile.y, 1.0 - inTile.y, effV);\n\n\tvar localId = gid - firstGid;\n\n\t// animated tiles: swap the local id for the current frame's id (the\n\t// CPU rewrites the lookup in lockstep with tileset.update)\n\tif (tmx.uAnimSize > 0.5) {\n\t\tlet animF = textureLoad(uAnimLookup, vec2i(localId, 0), 0);\n\t\tlet animTexel = vec4u(animF * 255.0 + 0.5);\n\t\tlocalId = i32(animTexel.r) | (i32(animTexel.g) << 8u);\n\t}\n\n\tlet row = floor(f32(localId) / tmx.uTilesetCols.x);\n\tlet col = f32(localId) - row * tmx.uTilesetCols.x;\n\tlet tileOriginPx = tmx.uTilesetMargin.xy\n\t\t+ vec2f(col, row) * (tmx.uTileSize + tmx.uTilesetMargin.zw);\n\tlet texelPx = tileOriginPx + inTile * tmx.uTileSize;\n\tlet texelUV = texelPx * tmx.uInvTilesetSize;\n\n\tlet sampled = textureSampleLevel(uTexture, uSampler, texelUV, 0.0);\n\tif (sampled.a <= 0.0) {\n\t\treturn miss;\n\t}\n\tvar out : CellSample;\n\tout.hit = true;\n\tout.color = sampled;\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\tlet tileCoord = tmx.uVisibleStart + in.vRegion * tmx.uVisibleSize;\n\tlet geomCell = floor(tileCoord);\n\tlet worldPx = tileCoord * tmx.uCellSize;\n\n\tlet gx = i32(geomCell.x);\n\tlet gy = i32(geomCell.y);\n\tlet overflowX = i32(tmx.uOverflow.x + 0.5);\n\tlet overflowY = i32(tmx.uOverflow.y + 0.5);\n\n\tvar result : CellSample;\n\n\t// fast path: tiles fit the cell exactly — only the geometric cell can\n\t// contain this fragment's tile\n\tif (overflowX == 0 && overflowY == 0) {\n\t\tresult = tryRenderCell(gx, gy, worldPx);\n\t\tif (!result.hit) {\n\t\t\tdiscard;\n\t\t}\n\t\treturn vec4f(result.color.rgb, result.color.a * tmx.uOpacity) * tmx.uTint;\n\t}\n\n\t// slow path: oversized tiles. Scan candidates dy high→low, dx low→high\n\t// and take the FIRST match — \"right-down\" render order puts later\n\t// cells on top\n\tvar found = false;\n\tfor (var idy = 0; idy <= MAX_OVERFLOW; idy++) {\n\t\tlet dy = MAX_OVERFLOW - idy;\n\t\tif (dy > overflowY) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (var dx = 0; dx <= MAX_OVERFLOW; dx++) {\n\t\t\tif (dx > overflowX) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet candidate = tryRenderCell(gx - dx, gy + dy, worldPx);\n\t\t\tif (candidate.hit) {\n\t\t\t\tresult = candidate;\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (found) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!found) {\n\t\tdiscard;\n\t}\n\treturn vec4f(result.color.rgb, result.color.a * tmx.uOpacity) * tmx.uTint;\n}\n","import { transformQuadCorners } from \"../../../gpu/quadcorners.ts\";\nimport tmxLayerWGSL from \"../../shaders/tmxlayer.wgsl\";\n\n/**\n * additional imports for TypeScript\n * @import { default as TMXLayer } from \"../../../../level/tiled/TMXLayer.js\";\n * @import { default as WebGPURenderer } from \"../../webgpu_renderer.js\";\n */\n\n/**\n * byte size of the TMXUniforms block (see tmxlayer.wgsl): ten vec2f (80)\n * interleaved with one vec4f at a 16-aligned offset, two f32 and the\n * trailing vec4f tint → 112 bytes\n * @ignore\n */\nconst TMX_UNIFORM_SIZE = 112;\n\n/**\n * GPU-accelerated renderer for orthogonal TMX tile layers on the WebGPU\n * backend — the WGSL realization of the WebGL shader tile path: the\n * visible region draws as ONE quad per tileset, with the fragment shader\n * sampling a per-layer GID index texture and the tileset atlas.\n *\n * Structure mirrors the WebGL twin; the mechanisms are the backend's:\n * - the GID index / animation lookup are renderer-owned `rgba8unorm`\n *   textures written with `queue.writeTexture` (byte-exact `textureLoad`\n *   reads, no sampler needed) — re-uploaded only when `layer.dataVersion`\n *   moves or an animation frame advances\n * - the per-draw uniforms snapshot into the effect uniform arena with a\n *   dynamic offset (each tileset pass gets its own region, per the\n *   queue-write-before-draws ordering)\n * - the tileset atlas rides the standard texture-store material path\n * - pipelines flow through the registered `tmxlayer.wgsl` family, keyed\n *   on the frame's blend/stencil state like every other draw\n * @ignore\n */\nexport default class OrthogonalTMXLayerGPURenderer {\n\t/**\n\t * @param {WebGPURenderer} renderer - the WebGPU renderer instance\n\t */\n\tconstructor(renderer) {\n\t\tthis.renderer = renderer;\n\t\tconst cache = renderer.pipelineCache;\n\n\t\t// group-3 shape: the uniform block + the two lookup textures\n\t\tthis.tmxLayout = cache.getEffectLayout(\"tmx\", [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tbuffer: {\n\t\t\t\t\ttype: \"uniform\",\n\t\t\t\t\thasDynamicOffset: true,\n\t\t\t\t\tminBindingSize: TMX_UNIFORM_SIZE,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t{ binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t]);\n\t\tthis.key = cache.registerShader(tmxLayerWGSL, {\n\t\t\tbindGroupLayouts: [\n\t\t\t\tcache.frameLayout,\n\t\t\t\tcache.materialLayout,\n\t\t\t\tcache.emptyLayout,\n\t\t\t\tthis.tmxLayout,\n\t\t\t],\n\t\t\tvertexLayoutKey: \"quad\",\n\t\t\tlabel: \"melonJS tmx layer shader\",\n\t\t});\n\t\t// the pipeline cache this state was built against — a device loss\n\t\t// rebuilds the cache, and the renderer drops this instance with it\n\t\tthis.epoch = cache.epoch;\n\n\t\t/**\n\t\t * per-layer GID index texture: layer → {texture, view, version}\n\t\t * @type {Map<TMXLayer, object>}\n\t\t */\n\t\tthis.resources = new Map();\n\t\t/**\n\t\t * per-tileset animation lookup: tileset → {texture, view, data, tileCount}\n\t\t * @type {Map<object, object>}\n\t\t */\n\t\tthis.animLookups = new Map();\n\t\t/**\n\t\t * per-(layer, tileset) cached group-3 bind group, keyed on the\n\t\t * uniform arena page it binds\n\t\t * @type {Map<TMXLayer, Map<object, {bindGroup: GPUBindGroup, pageLabel: string, animView: object}>>}\n\t\t */\n\t\tthis.bindGroups = new Map();\n\n\t\t// CPU staging for the uniform block and the quad vertices\n\t\tthis.uniformScratch = new Float32Array(TMX_UNIFORM_SIZE / 4);\n\t\tthis.vertexScratch = new ArrayBuffer(4 * 28);\n\t\tthis.vertexF32 = new Float32Array(this.vertexScratch);\n\t\tthis.vertexU32 = new Uint32Array(this.vertexScratch);\n\t}\n\n\t/**\n\t * Retire every lookup texture and empty the local maps — called from\n\t * `WebGPURenderer.reset()` (GAME_RESET) so each level starts clean.\n\t * @ignore\n\t */\n\treset() {\n\t\tfor (const resource of this.resources.values()) {\n\t\t\tthis.renderer.retireTexture(resource.texture);\n\t\t}\n\t\tfor (const entry of this.animLookups.values()) {\n\t\t\tthis.renderer.retireTexture(entry.texture);\n\t\t}\n\t\tthis.resources.clear();\n\t\tthis.animLookups.clear();\n\t\tthis.bindGroups.clear();\n\t}\n\n\t/**\n\t * Get-or-create the per-layer GID index texture, re-uploading when the\n\t * layer's data version moved (`setTile`/`clearTile` mutations).\n\t * @param {TMXLayer} layer - the layer\n\t * @returns {{texture: GPUTexture, view: GPUTextureView, version: number}}\n\t * @ignore\n\t */\n\tgetResource(layer) {\n\t\tconst device = this.renderer.device;\n\t\tlet resource = this.resources.get(layer);\n\t\tif (resource === undefined) {\n\t\t\tconst texture = device.createTexture({\n\t\t\t\tlabel: \"melonJS tmx index\",\n\t\t\t\tsize: [layer.cols, layer.rows],\n\t\t\t\tformat: \"rgba8unorm\",\n\t\t\t\tusage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\n\t\t\t});\n\t\t\tresource = { texture, view: texture.createView(), version: -1 };\n\t\t\tthis.resources.set(layer, resource);\n\t\t}\n\t\tif (resource.version !== layer.dataVersion) {\n\t\t\t// reinterpret the layer's Uint16Array payload as RGBA bytes\n\t\t\t// (zero-copy view; little-endian = every browser)\n\t\t\tdevice.queue.writeTexture(\n\t\t\t\t{ texture: resource.texture },\n\t\t\t\tnew Uint8Array(layer.layerData.buffer),\n\t\t\t\t{ bytesPerRow: layer.cols * 4, rowsPerImage: layer.rows },\n\t\t\t\t[layer.cols, layer.rows],\n\t\t\t);\n\t\t\tresource.version = layer.dataVersion;\n\t\t}\n\t\treturn resource;\n\t}\n\n\t/**\n\t * Get-or-update the per-tileset animation lookup (undefined for\n\t * tilesets without animated tiles). Texel `localId` encodes the\n\t * CURRENT frame's local id as (R = lo byte, G = hi byte).\n\t * @param {object} tileset - the tileset\n\t * @param {number} tileCount - atlas grid tile count\n\t * @returns {object|undefined} the lookup entry\n\t * @ignore\n\t */\n\tgetOrUpdateAnimLookup(tileset, tileCount) {\n\t\tif (!tileset.isAnimated || tileset.animations.size === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst device = this.renderer.device;\n\t\tlet entry = this.animLookups.get(tileset);\n\t\tif (entry === undefined) {\n\t\t\t// identity mapping (localId → localId); animated entries get\n\t\t\t// overwritten below\n\t\t\tconst data = new Uint8Array(tileCount * 4);\n\t\t\tfor (let id = 0; id < tileCount; id++) {\n\t\t\t\tdata[id * 4 + 0] = id & 0xff;\n\t\t\t\tdata[id * 4 + 1] = (id >> 8) & 0xff;\n\t\t\t}\n\t\t\tconst texture = device.createTexture({\n\t\t\t\tlabel: \"melonJS tmx anim lookup\",\n\t\t\t\tsize: [tileCount, 1],\n\t\t\t\tformat: \"rgba8unorm\",\n\t\t\t\tusage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\n\t\t\t});\n\t\t\tentry = {\n\t\t\t\ttexture,\n\t\t\t\tview: texture.createView(),\n\t\t\t\tdata,\n\t\t\t\ttileCount,\n\t\t\t\tuploaded: false,\n\t\t\t};\n\t\t\tthis.animLookups.set(tileset, entry);\n\t\t}\n\t\t// rewrite current-frame ids; upload only when a texel changed\n\t\t// (tileset.update advanced a frame). At most one upload per frame —\n\t\t// queue writes execute before every recorded draw, and animation\n\t\t// state only moves in the update phase, so the frame stays coherent.\n\t\tconst data = entry.data;\n\t\tlet dirty = !entry.uploaded;\n\t\tfor (const [localId, anim] of tileset.animations) {\n\t\t\tconst off = localId * 4;\n\t\t\tconst cur = anim.cur.tileid;\n\t\t\tconst lo = cur & 0xff;\n\t\t\tconst hi = (cur >> 8) & 0xff;\n\t\t\tif (data[off] !== lo || data[off + 1] !== hi) {\n\t\t\t\tdata[off] = lo;\n\t\t\t\tdata[off + 1] = hi;\n\t\t\t\tdirty = true;\n\t\t\t}\n\t\t}\n\t\tif (dirty) {\n\t\t\tdevice.queue.writeTexture(\n\t\t\t\t{ texture: entry.texture },\n\t\t\t\tdata,\n\t\t\t\t{ bytesPerRow: entry.tileCount * 4, rowsPerImage: 1 },\n\t\t\t\t[entry.tileCount, 1],\n\t\t\t);\n\t\t\tentry.uploaded = true;\n\t\t}\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Draw an orthogonal TMX layer through the shader path.\n\t * @param {TMXLayer} layer - the layer to draw\n\t * @param {object} rect - the visible viewport rect (world coords)\n\t */\n\tdraw(layer, rect) {\n\t\tconst renderer = this.renderer;\n\t\tconst tileWidth = layer.tilewidth;\n\t\tconst tileHeight = layer.tileheight;\n\t\tconst cols = layer.cols;\n\t\tconst rows = layer.rows;\n\n\t\t// visible region in tile-coord space\n\t\tconst startTileX = Math.max(0, Math.floor(rect.pos.x / tileWidth));\n\t\tconst startTileY = Math.max(0, Math.floor(rect.pos.y / tileHeight));\n\t\tconst endTileX = Math.min(\n\t\t\tcols,\n\t\t\tMath.ceil((rect.pos.x + rect.width) / tileWidth),\n\t\t);\n\t\tconst endTileY = Math.min(\n\t\t\trows,\n\t\t\tMath.ceil((rect.pos.y + rect.height) / tileHeight),\n\t\t);\n\t\tif (endTileX <= startTileX || endTileY <= startTileY) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst worldX = startTileX * tileWidth;\n\t\tconst worldY = startTileY * tileHeight;\n\t\tconst worldW = (endTileX - startTileX) * tileWidth;\n\t\tconst worldH = (endTileY - startTileY) * tileHeight;\n\n\t\t// drain pending sprite vertices under their own pipeline, then use\n\t\t// the quad batcher's frozen machinery (staging layout, index buffer)\n\t\tconst batcher = renderer.setBatcher(\"quad\");\n\t\tbatcher.flush();\n\t\tconst pass = renderer.ensurePass();\n\t\tconst device = renderer.device;\n\n\t\tconst resource = this.getResource(layer);\n\n\t\t// the transformed quad, shared by every tileset pass (28-byte\n\t\t// stride: x,y,z, u,v, packed tint, textureId)\n\t\tconst corners = transformQuadCorners(\n\t\t\trenderer.currentTransform,\n\t\t\tworldX,\n\t\t\tworldY,\n\t\t\tworldW,\n\t\t\tworldH,\n\t\t\trenderer.currentDepth,\n\t\t);\n\t\tconst f32 = this.vertexF32;\n\t\tconst u32 = this.vertexU32;\n\t\tconst uvs = [0, 0, 1, 0, 0, 1, 1, 1];\n\t\tfor (let i = 0; i < 4; i++) {\n\t\t\tconst base = i * 7;\n\t\t\tf32[base] = corners[i].x;\n\t\t\tf32[base + 1] = corners[i].y;\n\t\t\tf32[base + 2] = corners[i].z;\n\t\t\tf32[base + 3] = uvs[i * 2];\n\t\t\tf32[base + 4] = uvs[i * 2 + 1];\n\t\t\tu32[base + 5] = 0xffffffff;\n\t\t\tf32[base + 6] = 0;\n\t\t}\n\t\tconst vertexRegion = renderer.vertexArena.alloc(4 * 28);\n\t\tdevice.queue.writeBuffer(\n\t\t\tvertexRegion.buffer,\n\t\t\tvertexRegion.offset,\n\t\t\tthis.vertexScratch,\n\t\t\t0,\n\t\t\t4 * 28,\n\t\t);\n\n\t\t// layer-level uniform values (identical for every tileset pass)\n\t\tconst scratch = this.uniformScratch;\n\t\tscratch[0] = cols;\n\t\tscratch[1] = rows;\n\t\tscratch[2] = tileWidth;\n\t\tscratch[3] = tileHeight;\n\t\tscratch[18] = startTileX;\n\t\tscratch[19] = startTileY;\n\t\tscratch[20] = endTileX - startTileX;\n\t\tscratch[21] = endTileY - startTileY;\n\t\tscratch[22] = layer.getOpacity();\n\t\tconst tint = layer.tint ? layer.tint.toArray() : null;\n\t\tscratch[24] = tint ? tint[0] : 1;\n\t\tscratch[25] = tint ? tint[1] : 1;\n\t\tscratch[26] = tint ? tint[2] : 1;\n\t\tscratch[27] = tint ? tint[3] : 1;\n\n\t\tconst pipeline = renderer.pipelineCache.get(\n\t\t\tthis.key,\n\t\t\t\"triangle-list\",\n\t\t\trenderer.currentBlendMode,\n\t\t\trenderer.premultipliedAlpha,\n\t\t\trenderer.stencilMode,\n\t\t);\n\t\tif (pipeline !== renderer.currentPipeline) {\n\t\t\tpass.setPipeline(pipeline);\n\t\t\trenderer.currentPipeline = pipeline;\n\t\t}\n\t\tconst frame = renderer.currentFrameBinding;\n\t\tpass.setBindGroup(0, frame.bindGroup, [frame.dynamicOffset]);\n\t\tpass.setBindGroup(2, renderer.pipelineCache.emptyBindGroup);\n\t\tpass.setVertexBuffer(0, vertexRegion.buffer, vertexRegion.offset, 4 * 28);\n\t\tpass.setIndexBuffer(batcher.indexBuffer, \"uint32\");\n\n\t\t// one pass per tileset — the GID-range guard hides other tilesets\n\t\tconst tilesets = layer.tilesets.tilesets;\n\t\tlet layerGroups = this.bindGroups.get(layer);\n\t\tif (layerGroups === undefined) {\n\t\t\tlayerGroups = new Map();\n\t\t\tthis.bindGroups.set(layer, layerGroups);\n\t\t}\n\t\tfor (let i = 0; i < tilesets.length; i++) {\n\t\t\tconst tileset = tilesets[i];\n\t\t\t// eligibility should have excluded these — guard defensively\n\t\t\tif (tileset.isCollection || tileset.image === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst tsW = tileset.tilewidth;\n\t\t\tconst tsH = tileset.tileheight;\n\t\t\tconst margin = tileset.margin;\n\t\t\tconst spacing = tileset.spacing;\n\t\t\tconst atlasW = tileset.image.width;\n\t\t\tconst atlasH = tileset.image.height;\n\t\t\tconst atlasCols = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.floor((atlasW - margin * 2 + spacing) / (tsW + spacing)),\n\t\t\t);\n\t\t\tconst atlasRows = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.floor((atlasH - margin * 2 + spacing) / (tsH + spacing)),\n\t\t\t);\n\n\t\t\tconst animEntry = this.getOrUpdateAnimLookup(\n\t\t\t\ttileset,\n\t\t\t\tatlasCols * atlasRows,\n\t\t\t);\n\t\t\t// every declared binding needs a view — reuse the index texture\n\t\t\t// as the filler (never read: uAnimSize stays 0)\n\t\t\tconst animView = animEntry ? animEntry.view : resource.view;\n\n\t\t\t// per-tileset uniform values + snapshot into the arena\n\t\t\tscratch[4] = tsW;\n\t\t\tscratch[5] = tsH;\n\t\t\tscratch[6] = Math.max(0, Math.ceil(tsW / tileWidth) - 1);\n\t\t\tscratch[7] = Math.max(0, Math.ceil(tsH / tileHeight) - 1);\n\t\t\tscratch[8] = atlasCols;\n\t\t\tscratch[9] = atlasRows;\n\t\t\tscratch[10] = 1 / atlasW;\n\t\t\tscratch[11] = 1 / atlasH;\n\t\t\tscratch[12] = margin;\n\t\t\tscratch[13] = margin;\n\t\t\tscratch[14] = spacing;\n\t\t\tscratch[15] = spacing;\n\t\t\tscratch[16] = tileset.firstgid;\n\t\t\tscratch[17] = tileset.lastgid;\n\t\t\tscratch[23] = animEntry ? animEntry.tileCount : 0;\n\t\t\tconst region = renderer.effectUniformArena.alloc(\n\t\t\t\tTMX_UNIFORM_SIZE,\n\t\t\t\tdevice.limits?.minUniformBufferOffsetAlignment ?? 256,\n\t\t\t);\n\t\t\tdevice.queue.writeBuffer(\n\t\t\t\tregion.buffer,\n\t\t\t\tregion.offset,\n\t\t\t\tscratch.buffer,\n\t\t\t\t0,\n\t\t\t\tTMX_UNIFORM_SIZE,\n\t\t\t);\n\n\t\t\t// group-3 bind group, cached per (layer, tileset) while the\n\t\t\t// arena page and lookup views stay the same\n\t\t\tlet cached = layerGroups.get(tileset);\n\t\t\tif (\n\t\t\t\tcached === undefined ||\n\t\t\t\tcached.pageLabel !== region.buffer.label ||\n\t\t\t\tcached.animView !== animView\n\t\t\t) {\n\t\t\t\tcached = {\n\t\t\t\t\tpageLabel: region.buffer.label,\n\t\t\t\t\tanimView,\n\t\t\t\t\tbindGroup: device.createBindGroup({\n\t\t\t\t\t\tlabel: \"melonJS tmx binding\",\n\t\t\t\t\t\tlayout: this.tmxLayout,\n\t\t\t\t\t\tentries: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\t\t\tresource: {\n\t\t\t\t\t\t\t\t\tbuffer: region.buffer,\n\t\t\t\t\t\t\t\t\tsize: TMX_UNIFORM_SIZE,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ binding: 1, resource: resource.view },\n\t\t\t\t\t\t\t{ binding: 2, resource: animView },\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t\tlayerGroups.set(tileset, cached);\n\t\t\t}\n\n\t\t\tpass.setBindGroup(1, renderer.textureStore.getBinding(tileset.texture));\n\t\t\tpass.setBindGroup(3, cached.bindGroup, [region.offset]);\n\t\t\tpass.drawIndexed(6);\n\t\t}\n\t}\n}\n","/**\n * WebGL compressed-format constant → WebGPU texture format + block\n * metrics. The loader parsers (dds/pvr/pkm/ktx/ktx2) are backend-neutral\n * and emit WebGL enum constants; this table is the WebGPU side of that\n * contract. Families gate on the device features requested at init:\n * \"texture-compression-bc\", \"texture-compression-etc2\",\n * \"texture-compression-astc\" (PVRTC has no WebGPU equivalent).\n * @ignore\n */\nexport const COMPRESSED_FORMATS = new Map([\n\t// S3TC / BC (texture-compression-bc)\n\t[0x83f0, { format: \"bc1-rgba-unorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x83f2, { format: \"bc2-rgba-unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x83f3, { format: \"bc3-rgba-unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x8c4c, { format: \"bc1-rgba-unorm-srgb\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x8c4d, { format: \"bc1-rgba-unorm-srgb\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x8c4e, { format: \"bc2-rgba-unorm-srgb\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x8c4f, { format: \"bc3-rgba-unorm-srgb\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x8e8c, { format: \"bc7-rgba-unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t// ETC1 payload is valid ETC2 rgb8 (superset)\n\t[0x8d64, { format: \"etc2-rgb8unorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t// ETC2 / EAC (texture-compression-etc2)\n\t[0x9274, { format: \"etc2-rgb8unorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9275, { format: \"etc2-rgb8unorm-srgb\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9276, { format: \"etc2-rgb8a1unorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9277, { format: \"etc2-rgb8a1unorm-srgb\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9278, { format: \"etc2-rgba8unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x9279, { format: \"etc2-rgba8unorm-srgb\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x9270, { format: \"eac-r11unorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9271, { format: \"eac-r11snorm\", blockW: 4, blockH: 4, bytes: 8 }],\n\t[0x9272, { format: \"eac-rg11unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x9273, { format: \"eac-rg11snorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t// ASTC (texture-compression-astc) — all 16 bytes per block\n\t[0x93b0, { format: \"astc-4x4-unorm\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x93b1, { format: \"astc-5x4-unorm\", blockW: 5, blockH: 4, bytes: 16 }],\n\t[0x93b2, { format: \"astc-5x5-unorm\", blockW: 5, blockH: 5, bytes: 16 }],\n\t[0x93b3, { format: \"astc-6x5-unorm\", blockW: 6, blockH: 5, bytes: 16 }],\n\t[0x93b4, { format: \"astc-6x6-unorm\", blockW: 6, blockH: 6, bytes: 16 }],\n\t[0x93b5, { format: \"astc-8x5-unorm\", blockW: 8, blockH: 5, bytes: 16 }],\n\t[0x93b6, { format: \"astc-8x6-unorm\", blockW: 8, blockH: 6, bytes: 16 }],\n\t[0x93b7, { format: \"astc-8x8-unorm\", blockW: 8, blockH: 8, bytes: 16 }],\n\t// ASTC sRGB (KTX1 files carry the raw GL internal formats)\n\t[0x93d0, { format: \"astc-4x4-unorm-srgb\", blockW: 4, blockH: 4, bytes: 16 }],\n\t[0x93d1, { format: \"astc-5x4-unorm-srgb\", blockW: 5, blockH: 4, bytes: 16 }],\n\t[0x93d2, { format: \"astc-5x5-unorm-srgb\", blockW: 5, blockH: 5, bytes: 16 }],\n\t[0x93d3, { format: \"astc-6x5-unorm-srgb\", blockW: 6, blockH: 5, bytes: 16 }],\n\t[0x93d4, { format: \"astc-6x6-unorm-srgb\", blockW: 6, blockH: 6, bytes: 16 }],\n\t[0x93d5, { format: \"astc-8x5-unorm-srgb\", blockW: 8, blockH: 5, bytes: 16 }],\n\t[0x93d6, { format: \"astc-8x6-unorm-srgb\", blockW: 8, blockH: 6, bytes: 16 }],\n\t[0x93d7, { format: \"astc-8x8-unorm-srgb\", blockW: 8, blockH: 8, bytes: 16 }],\n]);\n\n/**\n * the optional device features the renderer requests when the adapter\n * offers them\n * @ignore\n */\nexport const COMPRESSION_FEATURES = [\n\t\"texture-compression-bc\",\n\t\"texture-compression-etc2\",\n\t\"texture-compression-astc\",\n];\n\n/**\n * Upload every mip level of a parsed CompressedImage into a GPUTexture\n * via queue.writeTexture (block-aligned rows, largest mip first — the\n * parser contract).\n * @param {GPUDevice} device - the device\n * @param {GPUTexture} texture - destination created with the mapped format\n * @param {object} image - the parsed CompressedImage ({mipmaps, format})\n * @param {object} metrics - the COMPRESSED_FORMATS entry for image.format\n * @ignore\n */\nexport function uploadCompressedTexture(device, texture, image, metrics) {\n\tconst mipmaps = image.mipmaps;\n\tfor (let level = 0; level < mipmaps.length; level++) {\n\t\tconst mip = mipmaps[level];\n\t\tconst blocksPerRow = Math.ceil(mip.width / metrics.blockW);\n\t\tconst rows = Math.ceil(mip.height / metrics.blockH);\n\t\tdevice.queue.writeTexture(\n\t\t\t{ texture, mipLevel: level },\n\t\t\tmip.data,\n\t\t\t{\n\t\t\t\toffset: 0,\n\t\t\t\tbytesPerRow: blocksPerRow * metrics.bytes,\n\t\t\t\trowsPerImage: rows,\n\t\t\t},\n\t\t\t[mip.width, mip.height],\n\t\t);\n\t}\n}\n","import Texture2d from \"../../texture/texture2d.ts\";\n\n/**\n * The WebGPU counterpart of the WebGL `FrameTexture`: a GPU-resident\n * capture of the frame rendered so far, refreshed IN PLACE by\n * {@link WebGPURenderer#captureFrame} via `copyTextureToTexture` — an\n * encoder-ordered command, so the copy sees exactly the draws recorded\n * before the capture point and none after (no queue-write retroactivity).\n *\n * Effects sample it through the `screen_texture` builtin: the effect's\n * group-3 bind group binds `view`, keyed by `generation` so a size-change\n * reallocation rebuilds stale bind groups. `isGPUResident` keeps the\n * `setTexture` discriminant contract of the WebGL twin.\n * @augments Texture2d\n * @ignore\n */\nexport class WebGPUFrameTexture extends Texture2d {\n\t/**\n\t * monotonic generation source shared by every capture instance\n\t * @ignore\n\t */\n\tstatic generationCounter = 0;\n\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t * @param {number} width - capture width in pixels\n\t * @param {number} height - capture height in pixels\n\t */\n\tconstructor(renderer, width, height) {\n\t\tsuper();\n\t\tthis.renderer = renderer;\n\t\t/**\n\t\t * marks this as a live GPU-resident source — see {@link ShaderEffect#setTexture}\n\t\t * @type {boolean}\n\t\t */\n\t\tthis.isGPUResident = true;\n\t\t/** @type {GPUTexture} */\n\t\tthis.gpuTexture = null;\n\t\tthis.realloc(width, height);\n\t}\n\n\t/**\n\t * (Re)allocate the backing texture at the given size, keeping this\n\t * object's identity — the caller-owned-refresh contract of\n\t * `toFrameTexture({target})`. The old texture is retired (draws\n\t * already recorded against it stay valid) and `generation` advances,\n\t * so bind groups referencing the old view re-key instead of pointing\n\t * at a destroyed texture and failing every subsequent submit.\n\t * @param {number} width - capture width in pixels\n\t * @param {number} height - capture height in pixels\n\t */\n\trealloc(width, height) {\n\t\tif (this.gpuTexture !== null) {\n\t\t\tthis.renderer.retireTexture(this.gpuTexture);\n\t\t}\n\t\t/** @type {number} */\n\t\tthis.width = width;\n\t\t/** @type {number} */\n\t\tthis.height = height;\n\t\t/**\n\t\t * unique per allocation (module-wide counter) — the bind-group key\n\t\t * @type {number}\n\t\t */\n\t\tthis.generation = ++WebGPUFrameTexture.generationCounter;\n\t\tthis.gpuTexture = this.renderer.device.createTexture({\n\t\t\tlabel: \"melonJS frame capture\",\n\t\t\tsize: [width, height],\n\t\t\tformat: this.renderer.preferredFormat,\n\t\t\tusage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,\n\t\t});\n\t\t/** @type {GPUTextureView} */\n\t\tthis.view = this.gpuTexture.createView();\n\t}\n\n\t/**\n\t * The opaque GPU-resident backing — itself.\n\t * @returns {WebGPUFrameTexture}\n\t */\n\tgetTexture() {\n\t\treturn this;\n\t}\n\n\t/**\n\t * Release the backing texture (retired if a frame is recording, so\n\t * draws already recorded against it stay valid). Idempotent.\n\t */\n\tdestroy() {\n\t\tif (this.gpuTexture !== null) {\n\t\t\tthis.renderer.retireTexture(this.gpuTexture);\n\t\t\tthis.gpuTexture = null;\n\t\t\tthis.view = null;\n\t\t}\n\t}\n}\n\nexport default WebGPUFrameTexture;\n","// melonJS WebGPU mip-chain blit — renders mip level N-1 into level N with\n// linear filtering, one fullscreen triangle per level. Used by the texture\n// store's mipmap generation (mesh-path textures); runs in its own encoder\n// outside the frame's pass.\n\n@group(0) @binding(0) var srcTexture : texture_2d<f32>;\n@group(0) @binding(1) var srcSampler : sampler;\n\nstruct VSOut {\n\t@builtin(position) position : vec4f,\n\t@location(0) uv : vec2f,\n};\n\n@vertex\nfn vertex_main(@builtin(vertex_index) index : u32) -> VSOut {\n\t// bufferless fullscreen triangle; uv (0,0) lands on the attachment's\n\t// top-left texel so the downsample is orientation-preserving\n\tvar out : VSOut;\n\tlet x = f32((index << 1u) & 2u);\n\tlet y = f32(index & 2u);\n\tout.uv = vec2f(x, y);\n\tout.position = vec4f(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0);\n\treturn out;\n}\n\n@fragment\nfn fragment_main(in : VSOut) -> @location(0) vec4f {\n\t// the bind group's view exposes exactly one level — level 0 of the view\n\treturn textureSampleLevel(srcTexture, srcSampler, in.uv, 0.0);\n}\n","import { GPU_TEXTURE_CACHE_RESET, off, on } from \"../../../system/event.ts\";\nimport { TextureStore } from \"../../gpu/texturestore.js\";\nimport mipblitWGSL from \"../shaders/mipblit.wgsl\";\nimport { COMPRESSED_FORMATS, uploadCompressedTexture } from \"./compressed.js\";\n\n/**\n * Renderer-owned GPU texture store for the WebGPU backend — the counterpart\n * of `MaterialBatcher`'s createTexture2D/bindTexture2D/deleteTexture2D tier,\n * kept on the renderer (rather than a batcher) so the quad path and the\n * future mesh / tile-layer paths share one resident-texture map.\n *\n * The engine-side bookkeeping is untouched: `TextureCache` still allocates\n * the logical \"unit\" for each `(source, repeat)` pair; here a unit keys a\n * `{GPUTexture, view, bind groups}` record instead of a GL texture binding.\n * Bind groups pair the texture view with a cached `GPUSampler` — one bind\n * group per (texture, sampler) combination, invalidated wholesale when the\n * filter setting changes (textures stay resident; only the pairing is\n * rebuilt — the WebGPU analogue of `_reapplyTextureFilter`, without the\n * re-parameterization).\n * @ignore\n */\nexport default class WebGPUTextureStore extends TextureStore {\n\t/**\n\t * @param {import(\"../webgpu_renderer.js\").default} renderer - the owning renderer\n\t */\n\tconstructor(renderer) {\n\t\tsuper();\n\t\tthis.renderer = renderer;\n\t\tthis.device = renderer.device;\n\t\t/** @type {Map<string, GPUSampler>} */\n\t\tthis.samplers = new Map();\n\n\t\t// drop every unit → texture association when the cache resets\n\t\t// (unit numbers get reassigned; resident GPU textures would map to\n\t\t// the wrong sources). Mirrors MaterialBatcher._onTextureCacheReset.\n\t\tthis.onCacheReset = () => {\n\t\t\t// `true`: the device is alive here, so the textures are real and\n\t\t\t// must actually be retired. The base defaults to NOT destroying,\n\t\t\t// which is the right choice for a LOST context — there the GPU\n\t\t\t// objects died with it and asking to free them is meaningless.\n\t\t\tthis.releaseAll(true);\n\t\t};\n\t\ton(GPU_TEXTURE_CACHE_RESET, this.onCacheReset);\n\t}\n\n\t/**\n\t * resolve (creating on first use) the sampler for a filter/wrap combo\n\t * @param {string} filter - \"nearest\" | \"linear\"\n\t * @param {string} repeat - engine wrap mode string\n\t * @returns {GPUSampler} the sampler\n\t * @ignore\n\t */\n\tgetSampler(filter, repeat, mipmaps = false) {\n\t\t// same per-axis mapping as MaterialBatcher.createTexture2D\n\t\tconst addressModeU = /^repeat(-x)?$/.test(repeat)\n\t\t\t? \"repeat\"\n\t\t\t: \"clamp-to-edge\";\n\t\tconst addressModeV = /^repeat(-y)?$/.test(repeat)\n\t\t\t? \"repeat\"\n\t\t\t: \"clamp-to-edge\";\n\t\tconst key = `${filter}|${addressModeU}|${addressModeV}|${mipmaps ? \"mip\" : \"flat\"}`;\n\t\tlet sampler = this.samplers.get(key);\n\t\tif (typeof sampler === \"undefined\") {\n\t\t\tsampler = this.device.createSampler({\n\t\t\t\tlabel: `melonJS sampler ${key}`,\n\t\t\t\tmagFilter: filter,\n\t\t\t\tminFilter: filter,\n\t\t\t\taddressModeU,\n\t\t\t\taddressModeV,\n\t\t\t\t// mip: trilinear minification over the generated chain (the\n\t\t\t\t// mesh path), with 4× anisotropy — oblique surfaces (ground\n\t\t\t\t// planes, walls at grazing angles) keep detail that plain\n\t\t\t\t// trilinear blurs away. Valid because the mip variant is\n\t\t\t\t// only built fully linear. flat: clamp every consumer to\n\t\t\t\t// level 0, so a texture UPGRADED to a mip chain by a mesh\n\t\t\t\t// keeps rendering byte-identically for the sprites sharing it\n\t\t\t\t...(mipmaps\n\t\t\t\t\t? { mipmapFilter: \"linear\", maxAnisotropy: 4 }\n\t\t\t\t\t: { lodMinClamp: 0, lodMaxClamp: 0 }),\n\t\t\t});\n\t\t\tthis.samplers.set(key, sampler);\n\t\t}\n\t\treturn sampler;\n\t}\n\n\t/**\n\t * The per-draw entry point: resolve the atlas to a bind group pairing\n\t * its resident GPU texture with the currently-applicable sampler,\n\t * uploading the source on first use (or re-uploading when forced — the\n\t * video-frame path).\n\t * @param {object} texture - a TextureAtlas\n\t * @param {object} [options] - resolution options\n\t * @param {boolean} [options.force=false] - re-upload the source pixels\n\t * @param {string} [options.repeat] - per-use wrap override (else texture.repeat)\n\t * @returns {GPUBindGroup} the material bind group for this draw\n\t */\n\tgetBinding(texture, options = {}) {\n\t\tconst wrap =\n\t\t\ttypeof options.repeat === \"string\"\n\t\t\t\t? options.repeat\n\t\t\t\t: (texture.repeat ?? \"no-repeat\");\n\t\tconst source = texture.getTexture();\n\t\t// Keyed by SOURCE, not by texture unit (#1585). Unit-keying worked here\n\t\t// only because this backend builds its TextureCache with no capacity,\n\t\t// so units are never recycled — the same coupling that made the WebGL\n\t\t// path destroy a texture whenever its unit was reassigned. Keying by\n\t\t// source removes the dependence entirely, and with it the need to\n\t\t// listen for a unit-assignment reset.\n\t\tlet record = this.records.get(source);\n\n\t\t// A unit number is not a stable identity: the TextureCache recycles\n\t\t// units when sources are unloaded (stage switches free the loading\n\t\t// screen's assets, for instance), and there is no per-unit release\n\t\t// event — so a resident record must be validated against the SOURCE\n\t\t// it was uploaded from, or a recycled unit serves stale pixels.\n\t\tif (\n\t\t\ttypeof record === \"undefined\" ||\n\t\t\toptions.force === true ||\n\t\t\trecord.source !== source ||\n\t\t\t// a mip-wanting consumer over a resident level-0-only record —\n\t\t\t// same source, but the texture must be rebuilt with a chain\n\t\t\t(options.mipmaps === true &&\n\t\t\t\trecord.compressed !== true &&\n\t\t\t\t(record.mipLevelCount ?? 1) === 1)\n\t\t) {\n\t\t\t// compressed sources (parsed dds/ktx/pvr/pkm) carry pre-encoded\n\t\t\t// block data: a dedicated createTexture + per-mip writeTexture\n\t\t\t// path, and static content — no same-frame re-upload concerns\n\t\t\tif (source.compressed === true) {\n\t\t\t\tif (typeof record === \"undefined\" || record.source !== source) {\n\t\t\t\t\tconst metrics = COMPRESSED_FORMATS.get(source.format);\n\t\t\t\t\tif (typeof metrics === \"undefined\") {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`WebGPUTextureStore: unsupported compressed texture format 0x${source.format.toString(16)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof record !== \"undefined\") {\n\t\t\t\t\t\tthis.retire(record.texture);\n\t\t\t\t\t}\n\t\t\t\t\tconst gpuTexture = this.device.createTexture({\n\t\t\t\t\t\tlabel: \"melonJS compressed texture\",\n\t\t\t\t\t\tsize: [source.width, source.height],\n\t\t\t\t\t\tformat: metrics.format,\n\t\t\t\t\t\tmipLevelCount: source.mipmaps.length,\n\t\t\t\t\t\t// compressed formats cannot be render attachments\n\t\t\t\t\t\tusage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\n\t\t\t\t\t});\n\t\t\t\t\tuploadCompressedTexture(this.device, gpuTexture, source, metrics);\n\t\t\t\t\trecord = {\n\t\t\t\t\t\t// `handle` and `generation` are the base class's fields —\n\t\t\t\t\t\t// it walks them for lifetime, and a record missing the\n\t\t\t\t\t\t// generation silently never gets released. `texture` is\n\t\t\t\t\t\t// kept because this backend's own paths read it.\n\t\t\t\t\t\thandle: gpuTexture,\n\t\t\t\t\t\tgeneration: this.generation,\n\t\t\t\t\t\ttexture: gpuTexture,\n\t\t\t\t\t\t// 2D consumers stay lod-clamped to level 0 (sprites\n\t\t\t\t\t\t// sharing the asset render byte-identically) …\n\t\t\t\t\t\tview: gpuTexture.createView({\n\t\t\t\t\t\t\tbaseMipLevel: 0,\n\t\t\t\t\t\t\tmipLevelCount: 1,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t// … while a mip-wanting consumer (the mesh path) can\n\t\t\t\t\t\t// sample the AUTHORED chain — compressed assets ship\n\t\t\t\t\t\t// their mips pre-encoded, no generation needed (the GL\n\t\t\t\t\t\t// twin: TEXTURE_MAX_LEVEL caps the chain to what the\n\t\t\t\t\t\t// asset carries)\n\t\t\t\t\t\tfullView: gpuTexture.createView(),\n\t\t\t\t\t\tmipLevelCount: source.mipmaps.length,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\twidth: source.width,\n\t\t\t\t\t\theight: source.height,\n\t\t\t\t\t\tframeId: -1,\n\t\t\t\t\t\tcompressed: true,\n\t\t\t\t\t\tbindGroupBySampler: new Map(),\n\t\t\t\t\t};\n\t\t\t\t\tthis.records.set(source, record);\n\t\t\t\t}\n\t\t\t\trecord.frameId = this.renderer.frameId;\n\t\t\t\tthis.lastRecord = record;\n\t\t\t\treturn this.bindGroupFor(\n\t\t\t\t\trecord,\n\t\t\t\t\ttexture,\n\t\t\t\t\twrap,\n\t\t\t\t\toptions.mipmaps === true,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// prefer real pixel dimensions; HTMLVideoElement exposes them\n\t\t\t// through videoWidth/videoHeight (width/height default to 0)\n\t\t\tconst width = source.width || source.videoWidth || 1;\n\t\t\tconst height = source.height || source.videoHeight || 1;\n\n\t\t\t// An in-place re-upload is retroactive: queue writes execute\n\t\t\t// before EVERY draw recorded this frame, so a record already\n\t\t\t// sampled this frame must get a fresh texture or earlier draws\n\t\t\t// would show the new pixels (shared canvases — gradients, Text —\n\t\t\t// bake different content into the same source mid-frame).\n\t\t\tif (\n\t\t\t\ttypeof record === \"undefined\" ||\n\t\t\t\trecord.width !== width ||\n\t\t\t\trecord.height !== height ||\n\t\t\t\trecord.frameId === this.renderer.frameId ||\n\t\t\t\t// a mip-wanting consumer (the mesh path) upgrades a resident\n\t\t\t\t// level-0-only texture to a full chain — never the reverse:\n\t\t\t\t// flat consumers of a mipped record sample level 0 via their\n\t\t\t\t// lod-clamped sampler\n\t\t\t\t(options.mipmaps === true && (record.mipLevelCount ?? 1) === 1) ||\n\t\t\t\t// a recycled unit whose resident texture came from the\n\t\t\t\t// compressed path cannot adopt an image source: its format\n\t\t\t\t// is non-renderable and copyExternalImageToTexture would\n\t\t\t\t// fail validation while the stale pixels kept serving\n\t\t\t\trecord.compressed === true\n\t\t\t) {\n\t\t\t\t// (re)create at the source size; the replaced texture retires\n\t\t\t\t// at frame end — destroying it now would invalidate draws\n\t\t\t\t// already recorded against it (submit rejects the whole frame)\n\t\t\t\tif (typeof record !== \"undefined\") {\n\t\t\t\t\tthis.retire(record.texture);\n\t\t\t\t}\n\t\t\t\t// full chain down to 1×1 when the mesh path asks for mips\n\t\t\t\tconst mipLevelCount =\n\t\t\t\t\toptions.mipmaps === true\n\t\t\t\t\t\t? Math.floor(Math.log2(Math.max(width, height))) + 1\n\t\t\t\t\t\t: 1;\n\t\t\t\tconst gpuTexture = this.device.createTexture({\n\t\t\t\t\tlabel: \"melonJS texture\",\n\t\t\t\t\tsize: [width, height],\n\t\t\t\t\tformat: \"rgba8unorm\",\n\t\t\t\t\tmipLevelCount,\n\t\t\t\t\t// RENDER_ATTACHMENT is required by copyExternalImageToTexture\n\t\t\t\t\t// (and by the mip-chain blit passes)\n\t\t\t\t\tusage:\n\t\t\t\t\t\tGPUTextureUsage.TEXTURE_BINDING |\n\t\t\t\t\t\tGPUTextureUsage.COPY_DST |\n\t\t\t\t\t\tGPUTextureUsage.RENDER_ATTACHMENT,\n\t\t\t\t});\n\t\t\t\trecord = {\n\t\t\t\t\thandle: gpuTexture,\n\t\t\t\t\tgeneration: this.generation,\n\t\t\t\t\ttexture: gpuTexture,\n\t\t\t\t\tview: gpuTexture.createView(),\n\t\t\t\t\tsource,\n\t\t\t\t\twidth,\n\t\t\t\t\theight,\n\t\t\t\t\tframeId: -1,\n\t\t\t\t\tmipLevelCount,\n\t\t\t\t\tbindGroupBySampler: new Map(),\n\t\t\t\t};\n\t\t\t\tthis.records.set(source, record);\n\t\t\t} else {\n\t\t\t\t// same-size unit reuse, not yet drawn this frame (recycled\n\t\t\t\t// unit, or a video frame): keep the resident texture + bind\n\t\t\t\t// groups, adopt the source\n\t\t\t\trecord.source = source;\n\t\t\t}\n\n\t\t\t// An HTMLVideoElement without a current frame has no backing\n\t\t\t// resource and copyExternalImageToTexture THROWS (autoplay\n\t\t\t// blocked, first frame not yet decoded) — where GL's texImage2D\n\t\t\t// silently uploads nothing. Skip the copy; the video path\n\t\t\t// re-uploads with force every frame, so the content lands the\n\t\t\t// moment a frame exists.\n\t\t\tconst frameless =\n\t\t\t\ttypeof HTMLVideoElement !== \"undefined\" &&\n\t\t\t\tsource instanceof HTMLVideoElement &&\n\t\t\t\tsource.readyState < 2;\n\t\t\tif (frameless === false) {\n\t\t\t\t// same premultiplication convention as the GL path's\n\t\t\t\t// UNPACK_PREMULTIPLY_ALPHA_WEBGL default; no flipY (both APIs\n\t\t\t\t// store row 0 = image top)\n\t\t\t\tthis.device.queue.copyExternalImageToTexture(\n\t\t\t\t\t{ source },\n\t\t\t\t\t{\n\t\t\t\t\t\ttexture: record.texture,\n\t\t\t\t\t\tpremultipliedAlpha: texture.premultipliedAlpha !== false,\n\t\t\t\t\t},\n\t\t\t\t\t[record.width, record.height],\n\t\t\t\t);\n\t\t\t\tif ((record.mipLevelCount ?? 1) > 1) {\n\t\t\t\t\tthis.generateMipmaps(record);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// stamp: this record's texture is (about to be) referenced by draws\n\t\t// recorded in the current frame\n\t\trecord.frameId = this.renderer.frameId;\n\n\t\t// stash for getResidentRecord — composers (the lit batcher) build\n\t\t// combined bind groups from the raw view\n\t\tthis.lastRecord = record;\n\n\t\treturn this.bindGroupFor(record, texture, wrap, options.mipmaps === true);\n\t}\n\n\t/**\n\t * Build the full mip chain of a freshly-uploaded record: one blit pass\n\t * per level, each rendering the previous level with linear filtering.\n\t * Runs in its own encoder submitted immediately — queue ordering places\n\t * it after the level-0 copy (a queue operation) and before the frame's\n\t * own command buffer, so the frame's draws sample a complete chain.\n\t * @param {object} record - the resident record (mipLevelCount > 1)\n\t * @ignore\n\t */\n\tgenerateMipmaps(record) {\n\t\tconst device = this.device;\n\t\tif (typeof this.mipPipeline === \"undefined\") {\n\t\t\tconst module = device.createShaderModule({\n\t\t\t\tlabel: \"melonJS mip blit\",\n\t\t\t\tcode: mipblitWGSL,\n\t\t\t});\n\t\t\tthis.mipLayout = device.createBindGroupLayout({\n\t\t\t\tlabel: \"melonJS mip blit layout\",\n\t\t\t\tentries: [\n\t\t\t\t\t{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: {} },\n\t\t\t\t\t{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: {} },\n\t\t\t\t],\n\t\t\t});\n\t\t\t// deliberately NOT a pipeline-cache family: mip passes have no\n\t\t\t// depth-stencil attachment and target the texture format, both\n\t\t\t// of which every cached pipeline hard-declares otherwise\n\t\t\tthis.mipPipeline = device.createRenderPipeline({\n\t\t\t\tlabel: \"melonJS mip blit\",\n\t\t\t\tlayout: device.createPipelineLayout({\n\t\t\t\t\tbindGroupLayouts: [this.mipLayout],\n\t\t\t\t}),\n\t\t\t\tvertex: { module, entryPoint: \"vertex_main\" },\n\t\t\t\tfragment: {\n\t\t\t\t\tmodule,\n\t\t\t\t\tentryPoint: \"fragment_main\",\n\t\t\t\t\ttargets: [{ format: \"rgba8unorm\" }],\n\t\t\t\t},\n\t\t\t\tprimitive: { topology: \"triangle-list\" },\n\t\t\t});\n\t\t\tthis.mipSampler = device.createSampler({\n\t\t\t\tlabel: \"melonJS mip blit sampler\",\n\t\t\t\tmagFilter: \"linear\",\n\t\t\t\tminFilter: \"linear\",\n\t\t\t});\n\t\t}\n\t\tconst encoder = device.createCommandEncoder({ label: \"melonJS mipgen\" });\n\t\tfor (let level = 1; level < record.mipLevelCount; level++) {\n\t\t\tconst bindGroup = device.createBindGroup({\n\t\t\t\tlabel: \"melonJS mip blit binding\",\n\t\t\t\tlayout: this.mipLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: record.texture.createView({\n\t\t\t\t\t\t\tbaseMipLevel: level - 1,\n\t\t\t\t\t\t\tmipLevelCount: 1,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\t{ binding: 1, resource: this.mipSampler },\n\t\t\t\t],\n\t\t\t});\n\t\t\tconst pass = encoder.beginRenderPass({\n\t\t\t\tcolorAttachments: [\n\t\t\t\t\t{\n\t\t\t\t\t\tview: record.texture.createView({\n\t\t\t\t\t\t\tbaseMipLevel: level,\n\t\t\t\t\t\t\tmipLevelCount: 1,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tloadOp: \"clear\",\n\t\t\t\t\t\tstoreOp: \"store\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tpass.setPipeline(this.mipPipeline);\n\t\t\tpass.setBindGroup(0, bindGroup);\n\t\t\tpass.draw(3);\n\t\t\tpass.end();\n\t\t}\n\t\tdevice.queue.submit([encoder.finish()]);\n\t}\n\n\t/**\n\t * Resolve the view + sampler a resident record should be bound with,\n\t * plus the cache key identifying that pair. Shared by the one-texture\n\t * material path and the mesh path, so the two cannot drift on which\n\t * view a mip-wanting consumer gets.\n\t * @ignore\n\t */\n\tviewAndSampler(record, texture, wrap, wantMips) {\n\t\tconst filter =\n\t\t\ttypeof texture.filter === \"string\"\n\t\t\t\t? texture.filter\n\t\t\t\t: this.renderer.getDefaultTextureFilter();\n\t\t// trilinear only for the consumer that asked for mips, over a record\n\t\t// that has them, with linear filtering (\"nearest\" opts out — crisp\n\t\t// pixel-art models); everyone else stays lod-clamped to level 0\n\t\tconst mip =\n\t\t\twantMips === true &&\n\t\t\t(record.mipLevelCount ?? 1) > 1 &&\n\t\t\tfilter === \"linear\";\n\t\treturn {\n\t\t\t// compressed records keep a level-0 `view` for 2D consumers and a\n\t\t\t// `fullView` over the authored chain for mip sampling;\n\t\t\t// generated-chain records have one full view serving both (the\n\t\t\t// sampler's lod clamp does the 2D restriction there)\n\t\t\tview: mip ? (record.fullView ?? record.view) : record.view,\n\t\t\tsampler: this.getSampler(filter, wrap, mip),\n\t\t\tkey: `${filter}|${wrap}|${mip ? \"mip\" : \"flat\"}`,\n\t\t};\n\t}\n\n\t/**\n\t * The group-1 material for a MESH: the diffuse texture/sampler pair plus\n\t * the per-texel opacity pair (MTL `map_d`, #1575).\n\t *\n\t * Always four bindings. A mesh with no alpha map passes the shared white\n\t * pixel, whose red channel is 1 — so the shader's multiply is a no-op and\n\t * the same pipeline layout serves both cases rather than splitting the\n\t * mesh family in two.\n\t * @param {TextureAtlas} texture - the diffuse texture\n\t * @param {TextureAtlas} alphaTexture - the opacity map, or the white pixel\n\t * @param {object} [options] - wrap / mipmap options, as `getBinding` takes\n\t * @returns {GPUBindGroup} the group-1 bind group\n\t * @ignore\n\t */\n\tgetMeshBinding(texture, alphaTexture, options = {}) {\n\t\t// residency first: this is the call that uploads either source, so it\n\t\t// must happen before the records are read back\n\t\tthis.getBinding(texture, options);\n\t\tthis.getBinding(alphaTexture, options);\n\n\t\tconst wrapFor = (t) => {\n\t\t\treturn typeof options.repeat === \"string\"\n\t\t\t\t? options.repeat\n\t\t\t\t: (t.repeat ?? \"no-repeat\");\n\t\t};\n\t\tconst wrap = wrapFor(texture);\n\t\tconst alphaWrap = wrapFor(alphaTexture);\n\t\tconst record = this.records.get(texture.getTexture());\n\t\tconst alphaRecord = this.records.get(alphaTexture.getTexture());\n\t\tif (record === undefined || alphaRecord === undefined) {\n\t\t\t// a source that failed to become resident — the caller keeps its\n\t\t\t// previous binding rather than recording a draw against nothing\n\t\t\treturn null;\n\t\t}\n\n\t\tconst diffuse = this.viewAndSampler(\n\t\t\trecord,\n\t\t\ttexture,\n\t\t\twrap,\n\t\t\toptions.mipmaps === true,\n\t\t);\n\t\tconst alpha = this.viewAndSampler(\n\t\t\talphaRecord,\n\t\t\talphaTexture,\n\t\t\talphaWrap,\n\t\t\tfalse,\n\t\t);\n\t\t// Cached on the diffuse record, keyed FIRST by the alpha record object\n\t\t// and then by the two sampler keys. One diffuse texture is often drawn\n\t\t// by several meshes with different opacity maps, and keying on the\n\t\t// diffuse sampler alone would hand the second mesh the first mesh's\n\t\t// cut-outs. Keying on the record OBJECT (not its unit) also retires\n\t\t// the entry naturally: a re-upload builds a new record, which simply\n\t\t// misses rather than serving a bind group over a destroyed view.\n\t\tif (record.meshBindGroups === undefined) {\n\t\t\trecord.meshBindGroups = new Map();\n\t\t}\n\t\tlet byAlpha = record.meshBindGroups.get(alphaRecord);\n\t\tif (byAlpha === undefined) {\n\t\t\tbyAlpha = new Map();\n\t\t\trecord.meshBindGroups.set(alphaRecord, byAlpha);\n\t\t}\n\t\tconst key = `${diffuse.key}|${alpha.key}`;\n\t\tlet bindGroup = byAlpha.get(key);\n\t\tif (bindGroup === undefined) {\n\t\t\tbindGroup = this.device.createBindGroup({\n\t\t\t\tlabel: \"melonJS mesh material\",\n\t\t\t\tlayout: this.renderer.pipelineCache.meshMaterialLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{ binding: 0, resource: diffuse.view },\n\t\t\t\t\t{ binding: 1, resource: diffuse.sampler },\n\t\t\t\t\t{ binding: 2, resource: alpha.view },\n\t\t\t\t\t{ binding: 3, resource: alpha.sampler },\n\t\t\t\t],\n\t\t\t});\n\t\t\tbyAlpha.set(key, bindGroup);\n\t\t}\n\t\treturn bindGroup;\n\t}\n\n\t/**\n\t * the per-sampler material bind group for a resident record (shared by\n\t * the image and compressed upload paths)\n\t * @ignore\n\t */\n\tbindGroupFor(record, texture, wrap, wantMips = false) {\n\t\tconst resolved = this.viewAndSampler(record, texture, wrap, wantMips);\n\t\tlet bindGroup = record.bindGroupBySampler.get(resolved.key);\n\t\tif (typeof bindGroup === \"undefined\") {\n\t\t\tbindGroup = this.device.createBindGroup({\n\t\t\t\tlabel: \"melonJS material\",\n\t\t\t\tlayout: this.renderer.pipelineCache.materialLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{ binding: 0, resource: resolved.view },\n\t\t\t\t\t{ binding: 1, resource: resolved.sampler },\n\t\t\t\t],\n\t\t\t});\n\t\t\trecord.bindGroupBySampler.set(resolved.key, bindGroup);\n\t\t}\n\t\treturn bindGroup;\n\t}\n\n\t/**\n\t * Ensure the texture is resident (same upload/validation path as\n\t * getBinding) and return its record — callers composing their own bind\n\t * groups (the lit batcher pairs the color view with a normal map) need\n\t * the raw view rather than the standard material group.\n\t * @param {object} texture - the texture atlas to resolve\n\t * @param {object} [options] - same options as getBinding\n\t * @returns {object} the resident record ({texture, view, width, height, …})\n\t */\n\tgetResidentRecord(texture, options) {\n\t\tthis.getBinding(texture, options);\n\t\treturn this.lastRecord;\n\t}\n\n\t/**\n\t * Dispose of a GPUTexture safely: while a frame is being recorded the\n\t * texture may be referenced by already-recorded draws (destroying it\n\t * would make `queue.submit()` reject the whole command buffer), so it\n\t * parks on the renderer's retired list and is destroyed after submit.\n\t * @param {GPUTexture} gpuTexture - the texture to dispose of\n\t * @ignore\n\t */\n\tretire(gpuTexture) {\n\t\tthis.renderer.retireTexture(gpuTexture);\n\t}\n\n\t/**\n\t * drop every (texture, sampler) bind-group pairing while keeping the\n\t * textures resident — the filter setting changed and the next draw\n\t * re-pairs each texture with the newly-resolved sampler\n\t */\n\tinvalidateBindGroups() {\n\t\tfor (const record of this.records.values()) {\n\t\t\trecord.bindGroupBySampler.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Destroy the resident GPU texture(s) associated with an image — every\n\t * `(source, repeat)` unit the cache knows about, mirroring\n\t * `MaterialBatcher.deleteTexture2D`'s multi-repeat sweep.\n\t * @param {object} texture - a TextureAtlas\n\t */\n\tdestroyTexture(texture) {\n\t\t// one record per source now, whatever wrap modes it was sampled at —\n\t\t// the per-unit sweep this used to do exists only in the unit-keyed world\n\t\tconst source =\n\t\t\ttypeof texture?.getTexture === \"function\"\n\t\t\t\t? texture.getTexture()\n\t\t\t\t: texture;\n\t\tconst record = this.records.get(source);\n\t\tif (record !== undefined) {\n\t\t\tthis.retire(record.texture);\n\t\t\tthis.records.delete(source);\n\t\t}\n\t}\n\n\t/**\n\t * drop every unit association and dispose of the resident textures\n\t */\n\t/**\n\t * Release the GPU texture behind a record. The base calls this; the device\n\t * defers the actual destroy until the frame that referenced it has retired.\n\t * @param {GPUTexture} handle - the texture to release\n\t * @ignore\n\t */\n\tonDestroy(handle) {\n\t\tthis.retire(handle);\n\t}\n\n\t/**\n\t * full teardown (device loss / renderer destroy)\n\t */\n\tdestroy() {\n\t\toff(GPU_TEXTURE_CACHE_RESET, this.onCacheReset);\n\t\tthis.releaseAll();\n\t\tthis.samplers.clear();\n\t}\n}\n","import { Color, colorPool } from \"../../math/color.ts\";\nimport { Matrix3d } from \"../../math/matrix3d.ts\";\nimport { Bounds } from \"../../physics/bounds.ts\";\nimport { releaseShadowQuads } from \"../../renderable/groundshadow.js\";\nimport {\n\tCANVAS_ONRESIZE,\n\temit,\n\tGAME_RESET,\n\tONCONTEXT_LOST,\n\tONCONTEXT_RESTORED,\n\toff,\n\ton,\n\tRENDER_TARGET_CHANGED,\n} from \"../../system/event.ts\";\nimport RadialGradientEffect from \"../effects/radialGradient.js\";\nimport { Gradient } from \"../gradient.js\";\nimport Renderer from \"../renderer.js\";\nimport RenderTargetPool from \"../rendertarget/render_target_pool.js\";\nimport WebGPURenderTarget from \"../rendertarget/webgpurendertarget.js\";\nimport { createAtlas, TextureAtlas } from \"../texture/atlas.js\";\nimport TextureCache from \"../texture/cache.js\";\nimport { dashPath, dashSegments } from \"../utils/dash.js\";\nimport {\n\tgenerateJoinCircles,\n\tgenerateTriangleFan,\n} from \"../utils/tessellation.js\";\n// pure CPU lighting math shared with the GL backend (published std140\n// layout, no GL calls)\nimport {\n\tcreateLightUniformScratch,\n\tpackLights,\n} from \"../webgl/lighting/pack.ts\";\nimport WebGPULitMeshBatcher from \"./batchers/lit_mesh_batcher.js\";\nimport WebGPULitQuadBatcher from \"./batchers/lit_quad_batcher.js\";\nimport WebGPUMeshBatcher from \"./batchers/mesh_batcher.js\";\nimport WebGPUPrimitiveBatcher from \"./batchers/primitive_batcher.js\";\nimport WebGPUQuadBatcher from \"./batchers/quad_batcher.js\";\nimport WebGPUBatcher from \"./batchers/webgpu_batcher.js\";\nimport WebGPUBufferArena from \"./buffer/arena.js\";\nimport WebGPUUniformRing from \"./buffer/uniformring.js\";\nimport WebGPUPipelineCache, {\n\tDEPTH_STENCIL_FORMAT,\n\tnormalizeBlendMode,\n} from \"./pipeline/cache.js\";\nimport OrthogonalTMXLayerGPURenderer from \"./renderers/tmxlayer/orthogonal.js\";\nimport { COMPRESSION_FEATURES } from \"./texture/compressed.js\";\nimport { WebGPUFrameTexture } from \"./texture/frametexture.js\";\nimport WebGPUTextureStore from \"./texture/store.js\";\n\n// scratch matrix for the affine-components form of transform()\nconst tempMatrix = new Matrix3d();\n// scratch: the projection saved across a blitEffect quad\nconst blitSavedProjection = new Matrix3d();\n\n/**\n * Resolve the depth half of a pass's load/store ops — split out pure so the\n * single-clear-per-target-per-frame policy is unit-testable without a\n * device. Until the first mesh ever draws, the ops are the original\n * clear/discard pair: the depth half costs pure-2D applications nothing and\n * their passes stay byte-identical. Once the mesh path is active, depth\n * persists across pass restarts (load/store — the GL parity where the depth\n * buffer survives mid-frame stencil clears and captures) and clears only\n * where a clear is armed: frame start, render-target change, or a fresh\n * attachment (whose \"load\" would read zeros and fail every LEQUAL test).\n * @param {boolean} meshDepthActive - whether drawMesh has ever run\n * @param {boolean} pendingDepthClear - whether a depth clear is armed\n * @returns {{depthLoadOp: string, depthStoreOp: string}} the pass ops\n * @ignore\n * @internal\n */\nexport function resolveDepthOps(meshDepthActive, pendingDepthClear) {\n\tif (meshDepthActive !== true) {\n\t\treturn { depthLoadOp: \"clear\", depthStoreOp: \"discard\" };\n\t}\n\treturn {\n\t\tdepthLoadOp: pendingDepthClear === true ? \"clear\" : \"load\",\n\t\tdepthStoreOp: \"store\",\n\t};\n}\n\n/**\n * The **experimental** WebGPU renderer.\n *\n * The asynchronous bootstrap — `getContext(\"webgpu\")` at construction, then\n * adapter/device negotiation and canvas configuration in\n * {@link WebGPURenderer#init} (the reason {@link Application#init} is\n * asynchronous at all) — plus the core 2D frame pipeline: one command\n * encoder and one render pass per frame, opened by {@link\n * WebGPURenderer#clear} and submitted by {@link WebGPURenderer#flush}, with\n * a `depth24plus-stencil8` attachment carried from day one (the stencil\n * half serves masks; the depth half is reserved for the mesh path).\n *\n * It is **opt-in only**: `renderer: video.WEBGPU` (or the `#webgpu` URI\n * fragment). `video.AUTO` never selects it, and will not until it reaches\n * feature parity with the WebGL backend.\n * @augments Renderer\n * @category Rendering\n */\nexport default class WebGPURenderer extends Renderer {\n\t/**\n\t * @param {ApplicationSettings} options - The renderer parameters\n\t */\n\tconstructor(options) {\n\t\t// parent constructor — acquires the GPUCanvasContext handle\n\t\t// (synchronous; throws when WebGPU is unavailable, which\n\t\t// `Application.init()` surfaces as a rejection)\n\t\tsuper(Object.assign(options, { context: \"webgpu\" }));\n\n\t\t/**\n\t\t * The WebGPU adapter, set once {@link WebGPURenderer#init} resolves.\n\t\t * @type {GPUAdapter|undefined}\n\t\t * @readonly\n\t\t */\n\t\tthis.adapter = undefined;\n\n\t\t/**\n\t\t * The WebGPU device, set once {@link WebGPURenderer#init} resolves.\n\t\t * @name device\n\t\t * @type {GPUDevice|undefined}\n\t\t * @readonly\n\t\t */\n\t\tthis.device = undefined;\n\n\t\t/**\n\t\t * The WebGPU canvas context\n\t\t * @name context\n\t\t * @type {GPUCanvasContext}\n\t\t */\n\t\tthis.context = this.renderTarget.context;\n\n\t\t/**\n\t\t * The preferred canvas texture format reported by the platform,\n\t\t * set once {@link WebGPURenderer#init} resolves.\n\t\t * @type {string|undefined}\n\t\t * @readonly\n\t\t */\n\t\tthis.preferredFormat = undefined;\n\n\t\t// there is no renderer to draw with until init() resolves\n\t\tthis.isContextValid = false;\n\n\t\t// set the renderer type\n\t\tthis.type = \"WebGPU\";\n\n\t\t// user-supplied shaders for this backend are WGSL. GLSL consumers\n\t\t// (ShaderEffect bodies, {vertex, fragment} shader assets) key off\n\t\t// this and correctly refuse to hand this backend GLSL source\n\t\t/** @type {\"glsl\"|\"wgsl\"|null} */\n\t\tthis.shaderLanguage = \"wgsl\";\n\n\t\t// capability flags describe what the backend can DO today\n\n\t\t// orthogonal TMX layers draw through the WGSL shader tile path\n\t\tthis.supportsShaderTileLayers = true;\n\t\t// the mesh tier: depth-tested drawing (the depth half of the shared\n\t\t// attachment) and retained model-space mesh geometry — Camera3d\n\t\t// scenes take the uniforms-only drawMesh(mesh, modelMatrix) path\n\t\tthis.supportsDepthBuffer = true;\n\t\tthis.supportsRetainedMesh = true;\n\t\tthis.supportsInstancing = true;\n\t\t// lazy orientation-specific GPU tilemap renderer (device-scoped:\n\t\t// dropped on device loss, rebuilt on first use)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.orthogonalTMXRenderer = undefined;\n\n\t\t// create a texture cache\n\t\tthis.cache = new TextureCache(this);\n\n\t\t// the model/view matrix lives in the base-owned RenderState — same\n\t\t// aliasing as the WebGL backend\n\t\tthis.currentTransform = this.renderState.currentTransform;\n\n\t\t// active Gradient (setColor(Gradient)) — honored on fillRect via\n\t\t// the Canvas-baked gradient texture, and on arbitrary shapes by\n\t\t// clipping that baked rect through the stencil (gradientMask)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.currentGradient = null;\n\n\t\t// scratch vertices for fillRect (2 triangles) and fillPolygon\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.rectTriangles = Array.from({ length: 6 }, () => {\n\t\t\treturn { x: 0, y: 0 };\n\t\t});\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.polyVerts = [];\n\t\t// scratch bounds for the clipRect screen-space AABB derivation\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.clipAABB = new Bounds();\n\t\t// the stencil reference the masked render phase compares against\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.maskVisibleRef = 0;\n\n\t\t/**\n\t\t * the batchers registered with this renderer, by name\n\t\t * @type {Map<string, object>}\n\t\t * @ignore\n\t\t * @internal\n\t\t */\n\t\tthis.batchers = new Map();\n\n\t\t/**\n\t\t * the currently active batcher\n\t\t * @ignore\n\t\t * @internal\n\t\t */\n\t\tthis.currentBatcher = null;\n\n\t\t// GPU-facing infrastructure, created by init() once a device exists\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.pipelineCache = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.vertexArena = null;\n\t\t// per-frame index regions for the accumulated mesh path\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.indexArena = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.uniformRing = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.textureStore = null;\n\n\t\t// per-frame recording state\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.commandEncoder = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.renderPass = null;\n\t\t// the active offscreen render target (null = the canvas). Retargeting\n\t\t// is a pass break: the next pass opens on the target's color view.\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.currentRenderTarget = null;\n\t\t// consumed as the next pass's colorLoadOp \"clear\" (fresh target)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.pendingColorClear = false;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.pendingClearValue = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.pendingStencilClear = false;\n\t\t// consumed as the next pass's depthLoadOp \"clear\" once the mesh path\n\t\t// is active — armed at frame start, on render-target changes and on\n\t\t// depth-attachment recreation (single clear per target per frame,\n\t\t// the GL mesh-mode policy)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.pendingDepthClear = false;\n\t\t// sticky: flips true on the first drawMesh ever and stays — before\n\t\t// that, every pass keeps the original clear/discard depth ops and\n\t\t// pure-2D applications see zero change\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.meshDepthActive = false;\n\t\t// the split-screen camera viewport (top-left origin, canvas passes\n\t\t// only) — null = full target. Persists across frames like gl.viewport\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.viewportRect = null;\n\t\t// one-shot warn: a customShader (ShaderEffect fast path) cannot host\n\t\t// a mesh draw on this backend\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.meshEffectWarned = false;\n\t\t// the canvas GPUTexture handle of the current frame — kept beside its\n\t\t// view because captureFrame copies from the TEXTURE, not the view\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.frameTexture = null;\n\t\t// the shared frame-capture slot (screen_texture builtin), lazy\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.captureTexture = undefined;\n\t\t// 1×1 transparent stand-in bound where a declared texture has no\n\t\t// source yet (never-captured screen_texture, unset setTexture slot)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.stubTexture = null;\n\t\t// per-depth projection save slots for nested post-effect passes\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.effectProjectionStack = [];\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.effectPassDepth = 0;\n\t\t// per-bind effect uniform snapshots (created by init)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.effectUniformArena = null;\n\t\t// monotonically increasing frame id — the texture store uses it to\n\t\t// detect same-frame content changes that need a fresh texture\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.frameId = 0;\n\t\t// GPUTextures replaced mid-frame: destroying them immediately would\n\t\t// invalidate draws already recorded against them (submit rejects the\n\t\t// whole command buffer) — they retire at frame end, after submit\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.retiredTextures = [];\n\t\t// the lineWidth value written into the current frame-globals slot —\n\t\t// the primitive batcher compares against THIS (not its own cache)\n\t\t// because clear() rewrites the slot every frame\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.currentFrameLineWidth = 1;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.frameTextureView = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.depthTexture = null;\n\t\t// MSAA state (antiAlias: true): canvas passes render into a shared\n\t\t// multisampled color texture resolving into the canvas view, with a\n\t\t// matching multisampled depth-stencil twin. Offscreen render targets\n\t\t// stay single-sampled — GL parity, where only the default\n\t\t// framebuffer is ever antialiased and pool FBOs are not.\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.canvasSampleCount = 1;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.msaaColorTexture = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.msaaColorView = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.msaaDepthTexture = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.currentPipeline = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.currentFrameBinding = null;\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.scissorActive = false;\n\t\t// premultiplied-alpha flag mirrored from setBlendMode (pipeline key)\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.premultipliedAlpha = true;\n\t\t// stencil mode consulted by the pipeline lookup: \"none\"|\"write\"|\"test\"\n\t\t/** @ignore\n\t\t * @internal */\n\t\tthis.stencilMode = \"none\";\n\n\t\t// reset the renderer on game reset (stored so destroy() can\n\t\t// unsubscribe — a rejected-then-retried `Application.init()`\n\t\t// constructs a fresh renderer per attempt, and the event bus must\n\t\t// not pin every abandoned one for the page's lifetime)\n\t\tthis.onGameReset = () => {\n\t\t\tthis.reset();\n\t\t};\n\t\ton(GAME_RESET, this.onGameReset);\n\n\t\t// keep the depth-stencil attachment sized with the canvas\n\t\tthis.onCanvasResize = () => {\n\t\t\tif (typeof this.device !== \"undefined\") {\n\t\t\t\tthis.flush();\n\t\t\t\tthis.createDepthTexture();\n\t\t\t}\n\t\t\t// a stored split-screen viewport was un-flipped against the OLD\n\t\t\t// canvas height — drop it, like the GL resize path's\n\t\t\t// setViewport(0, 0, width, height); cameras re-set theirs next\n\t\t\t// frame\n\t\t\tthis.viewportRect = null;\n\t\t};\n\t\ton(CANVAS_ONRESIZE, this.onCanvasResize);\n\t}\n\n\t/**\n\t * Negotiate the GPU adapter and device, configure the canvas context,\n\t * and build the GPU-facing infrastructure (pipeline cache, buffer\n\t * arenas, depth-stencil attachment). Awaited by {@link Application#init}\n\t * right after construction — a WebGPU device cannot be acquired\n\t * synchronously, which is what this hook exists for.\n\t * @override\n\t * @returns {Promise<void>} resolves once the device is configured\n\t * @throws {Error} (as a rejection) when WebGPU is unavailable or no\n\t * suitable adapter is found\n\t */\n\tasync init() {\n\t\t// idempotent while the device is live: Application.init()'s AUTO\n\t\t// case awaits a full init during backend negotiation, then its\n\t\t// common path awaits init again on the winner — a second\n\t\t// negotiation would leak the first device. (restoreDevice clears\n\t\t// `device` before re-running, so the restore path passes through.)\n\t\tif (typeof this.device !== \"undefined\" && this.isContextValid === true) {\n\t\t\treturn;\n\t\t}\n\t\tconst gpu = globalThis.navigator?.gpu;\n\t\tif (typeof gpu === \"undefined\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"WebGPU is not available in this environment (no `navigator.gpu`)\",\n\t\t\t);\n\t\t}\n\n\t\t// WebGPU's GPUPowerPreference enum has no \"default\" member (the\n\t\t// WebGL vocabulary melonJS settings use) — omit the hint entirely\n\t\t// unless one of the two valid values was explicitly asked for\n\t\tconst { powerPreference } = this.settings;\n\t\tconst adapter = await gpu.requestAdapter(\n\t\t\tpowerPreference === \"high-performance\" || powerPreference === \"low-power\"\n\t\t\t\t? { powerPreference }\n\t\t\t\t: undefined,\n\t\t);\n\t\tif (adapter === null) {\n\t\t\tthrow new Error(\n\t\t\t\t\"WebGPU: no suitable GPUAdapter found (adapter request returned null)\",\n\t\t\t);\n\t\t}\n\t\t// the WebGPU analogue of GL's failIfMajorPerformanceCaveat: a\n\t\t// fallback adapter is software rendering — reject it when the app\n\t\t// asked for hardware-or-nothing (under AUTO this falls through to\n\t\t// the WebGL candidate, which honors the same setting)\n\t\tif (\n\t\t\tthis.settings.failIfMajorPerformanceCaveat === true &&\n\t\t\tadapter.isFallbackAdapter === true\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"WebGPU: only a fallback (software) adapter is available and failIfMajorPerformanceCaveat is set\",\n\t\t\t);\n\t\t}\n\t\tthis.adapter = adapter;\n\n\t\t// request the compressed-texture families the adapter offers, so\n\t\t// the loader's dds/ktx/pvr/pkm assets can upload natively\n\t\tthis.device = await adapter.requestDevice({\n\t\t\trequiredFeatures: COMPRESSION_FEATURES.filter((feature) => {\n\t\t\t\treturn adapter.features.has(feature);\n\t\t\t}),\n\t\t});\n\n\t\t// identify the driver in the console header, same as the WebGL\n\t\t// backend does through its debug-renderer-info strings. The\n\t\t// GPUAdapterInfo fields are heavily redacted for anti-fingerprinting:\n\t\t// `description` / `device` are usually empty, and vendor/architecture\n\t\t// can repeat one token (Safari on Apple Silicon reports \"apple\" for\n\t\t// both) — prefer the richest non-empty field, dedupe the rest\n\t\tconst info = adapter.info;\n\t\tif (typeof info !== \"undefined\") {\n\t\t\tthis.GPURenderer =\n\t\t\t\tinfo.description ||\n\t\t\t\tinfo.device ||\n\t\t\t\t[...new Set([info.vendor, info.architecture].filter(Boolean))].join(\n\t\t\t\t\t\" \",\n\t\t\t\t) ||\n\t\t\t\tundefined;\n\t\t\t// the GL backend's GPUVendor twin (debug-renderer-info UNMASKED_VENDOR)\n\t\t\tthis.GPUVendor = info.vendor || undefined;\n\t\t}\n\n\t\t// a lost device is this backend's context loss. `device.lost`\n\t\t// settles exactly once, including on an explicit `destroy()` —\n\t\t// only announce losses the application did not ask for, then\n\t\t// attempt a transparent recovery on a fresh device\n\t\tvoid this.device.lost.then((lossInfo) => {\n\t\t\tif (lossInfo.reason !== \"destroyed\") {\n\t\t\t\tthis.isContextValid = false;\n\t\t\t\temit(ONCONTEXT_LOST, this);\n\t\t\t\tvoid this.restoreDevice().catch((err) => {\n\t\t\t\t\tconsole.warn(\"WebGPU device restore failed:\", err);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tthis.preferredFormat = gpu.getPreferredCanvasFormat();\n\t\tthis.context.configure({\n\t\t\tdevice: this.device,\n\t\t\tformat: this.preferredFormat,\n\t\t\talphaMode: this.settings.transparent ? \"premultiplied\" : \"opaque\",\n\t\t\t// COPY_SRC on top of the default: captureFrame() copies the\n\t\t\t// canvas texture into the shared capture (screen_texture builtin)\n\t\t\tusage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,\n\t\t});\n\n\t\t// GPU-facing infrastructure, in dependency order\n\t\tthis.pipelineCache = new WebGPUPipelineCache(\n\t\t\tthis.device,\n\t\t\tthis.preferredFormat,\n\t\t);\n\t\tthis.uniformRing = new WebGPUUniformRing(\n\t\t\tthis.device,\n\t\t\tthis.pipelineCache.frameLayout,\n\t\t\tthis.pipelineCache.clearFrameLayout,\n\t\t);\n\t\tthis.vertexArena = new WebGPUBufferArena(this.device, {\n\t\t\tlabel: \"melonJS vertex arena\",\n\t\t});\n\t\tthis.effectUniformArena = new WebGPUBufferArena(this.device, {\n\t\t\tlabel: \"melonJS effect uniform arena\",\n\t\t\tusage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n\t\t\tpageSize: 64 << 10,\n\t\t});\n\t\tthis.indexArena = new WebGPUBufferArena(this.device, {\n\t\t\tlabel: \"melonJS index arena\",\n\t\t\tusage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,\n\t\t});\n\t\tthis.textureStore = new WebGPUTextureStore(this);\n\t\t// antiAlias maps to 4× MSAA on canvas passes (the GL context's\n\t\t// antialias flag equivalent); 4× is universally supported for the\n\t\t// canvas and depth formats\n\t\tthis.canvasSampleCount = this.settings.antiAlias === true ? 4 : 1;\n\t\tthis.pipelineCache.sampleCount = this.canvasSampleCount;\n\t\tthis.createDepthTexture();\n\n\t\t// register the built-in batchers (device-dependent, so here rather\n\t\t// than the constructor — a device-loss restore re-runs this path).\n\t\t// A custom batcher override rides the quad/primitive slots exactly\n\t\t// like the WebGL renderer's constructor — `addBatcher` rejects a\n\t\t// non-WebGPUBatcher class loudly, so a GL-only custom batcher fails\n\t\t// with a clear message instead of mid-draw\n\t\tif (this.batchers.size === 0) {\n\t\t\tconst CustomBatcher = this.settings.batcher || this.settings.compositor;\n\t\t\tthis.addBatcher(\n\t\t\t\tnew (CustomBatcher || WebGPUQuadBatcher)(this),\n\t\t\t\t\"quad\",\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tthis.addBatcher(\n\t\t\t\tnew (CustomBatcher || WebGPUPrimitiveBatcher)(this),\n\t\t\t\t\"primitive\",\n\t\t\t);\n\t\t\tif (!CustomBatcher) {\n\t\t\t\tthis.addBatcher(new WebGPULitQuadBatcher(this), \"litQuad\");\n\t\t\t}\n\t\t\tthis.addBatcher(new WebGPUMeshBatcher(this), \"mesh\");\n\t\t\tthis.addBatcher(new WebGPULitMeshBatcher(this), \"litMesh\");\n\t\t}\n\n\t\t// apply the configured blend mode up front (the GL constructor's\n\t\t// parity): before the first GAME_RESET, out-of-bracket draws (unit\n\t\t// tests, pre-stage drawing) would otherwise run on RenderState's\n\t\t// \"none\" default with blending disabled\n\t\tthis.setBlendMode(this.settings.blendMode);\n\n\t\tthis.isContextValid = true;\n\t}\n\n\t/**\n\t * (re)create the shared depth-stencil attachment. Defaults to the canvas\n\t * size; `beginPass` passes the active target's size because every\n\t * attachment of a pass must have identical dimensions (in the 2D flow\n\t * all targets are canvas-sized, so this recreates nothing per frame).\n\t * @param {number} [width] - required width (defaults to the canvas)\n\t * @param {number} [height] - required height (defaults to the canvas)\n\t * @ignore\n\t * @internal\n\t */\n\tcreateDepthTexture(width, height, sampleCount = 1) {\n\t\tconst canvas = this.getCanvas();\n\t\twidth = Math.max(1, width ?? canvas.width);\n\t\theight = Math.max(1, height ?? canvas.height);\n\t\t// single-sampled and multisampled passes each keep their own\n\t\t// attachment (a pass's attachments must share one sample count)\n\t\tconst slot = sampleCount > 1 ? \"msaaDepthTexture\" : \"depthTexture\";\n\t\tconst current = this[slot];\n\t\tif (current && current.width === width && current.height === height) {\n\t\t\treturn;\n\t\t}\n\t\tif (current) {\n\t\t\t// recorded passes may reference the old attachment — retire it\n\t\t\tthis.retireTexture(current);\n\t\t}\n\t\tthis[slot] = this.device.createTexture({\n\t\t\tlabel: `melonJS depth-stencil${sampleCount > 1 ? \" msaa\" : \"\"}`,\n\t\t\tsize: [width, height],\n\t\t\tformat: DEPTH_STENCIL_FORMAT,\n\t\t\tsampleCount,\n\t\t\tusage: GPUTextureUsage.RENDER_ATTACHMENT,\n\t\t});\n\t\t// a fresh attachment read with a \"load\" op is all zeros — every\n\t\t// LEQUAL test would fail — so recreation always arms a depth clear\n\t\tthis.pendingDepthClear = true;\n\t}\n\n\t/**\n\t * (re)create the shared multisampled color texture canvas passes render\n\t * into (resolved into the canvas view at every pass end).\n\t * @param {number} width - required width in pixels\n\t * @param {number} height - required height in pixels\n\t * @ignore\n\t * @internal\n\t */\n\tcreateMsaaColorTexture(width, height) {\n\t\tconst current = this.msaaColorTexture;\n\t\tif (current && current.width === width && current.height === height) {\n\t\t\treturn;\n\t\t}\n\t\tif (current) {\n\t\t\tthis.retireTexture(current);\n\t\t}\n\t\tthis.msaaColorTexture = this.device.createTexture({\n\t\t\tlabel: \"melonJS msaa color\",\n\t\t\tsize: [width, height],\n\t\t\tformat: this.preferredFormat,\n\t\t\tsampleCount: this.canvasSampleCount,\n\t\t\tusage: GPUTextureUsage.RENDER_ATTACHMENT,\n\t\t});\n\t\tthis.msaaColorView = this.msaaColorTexture.createView();\n\t}\n\n\t/**\n\t * Begin the frame: reset the per-frame allocators, acquire the canvas\n\t * texture, and open the frame's command encoder and render pass with a\n\t * clear load — the WebGPU realization of `gl.clearColor + gl.clear`.\n\t * Called by `Application.draw()` at the top of every frame.\n\t * @override\n\t */\n\tclear() {\n\t\tif (typeof this.device === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\tconst canvas = this.getCanvas();\n\t\tif (canvas.width === 0 || canvas.height === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// a pass still open here means the previous frame aborted between\n\t\t// clear() and flush() (an exception mid-draw) — drop it, un-submitted\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.abandonFrame();\n\t\t}\n\n\t\tthis.vertexArena.reset();\n\t\tthis.indexArena.reset();\n\t\tthis.uniformRing.reset();\n\t\tthis.effectUniformArena.reset();\n\t\tthis.frameId++;\n\t\t// a frame always begins on the canvas, outside any effect bracket\n\t\t// (an exception that escaped between begin/endPostEffect last frame\n\t\t// must not leave the projection stack wound up)\n\t\tthis.currentRenderTarget = null;\n\t\tthis.pendingColorClear = false;\n\t\tthis.effectPassDepth = 0;\n\t\tthis._renderTargetPool?.reset?.();\n\t\t// clear() also resets the stroke line width, like the GL backend\n\t\tthis.lineWidth = 1;\n\t\tthis.stencilMode = \"none\";\n\t\tthis.maskLevel = 0;\n\n\t\tconst [r, g, b, a] = this.backgroundColor.toArray();\n\t\t// frame start clears every attachment half, depth included\n\t\tthis.pendingDepthClear = true;\n\t\tthis.beginPass({\n\t\t\tcolorLoadOp: \"clear\",\n\t\t\tclearValue: { r, g, b, a },\n\t\t\tstencilLoadOp: \"clear\",\n\t\t});\n\n\t\t// the frame's first frame-globals slot\n\t\tthis.pushFrameGlobals();\n\n\t\t// a new frame is a new render pass on the active target — same\n\t\t// per-frame signal the WebGL backend emits from its clear()\n\t\t// the event payload is the RENDERER on every other emitter (both\n\t\t// backends), and subscribers filter on `emitter === this.renderer`\n\t\temit(RENDER_TARGET_CHANGED, this);\n\t}\n\n\t/**\n\t * open a render pass on the current frame's encoder (creating the\n\t * encoder and acquiring the canvas texture on first use this frame)\n\t * @param {object} [opts] - load operations for the pass\n\t * @ignore\n\t * @internal\n\t */\n\tbeginPass(opts = {}) {\n\t\tif (this.commandEncoder === null) {\n\t\t\tthis.commandEncoder = this.device.createCommandEncoder({\n\t\t\t\tlabel: \"melonJS frame\",\n\t\t\t});\n\t\t}\n\t\t// the pass's color attachment: the active render target's view, or\n\t\t// the canvas texture (acquired once per frame)\n\t\tlet colorView;\n\t\tconst target = this.currentRenderTarget;\n\t\tif (target !== null) {\n\t\t\tcolorView = target.colorView;\n\t\t} else {\n\t\t\tif (this.frameTextureView === null) {\n\t\t\t\tthis.frameTexture = this.context.getCurrentTexture();\n\t\t\t\tthis.frameTextureView = this.frameTexture.createView();\n\t\t\t}\n\t\t\tcolorView = this.frameTextureView;\n\t\t}\n\t\t// a retarget with a pending clear opens with a clearing load — the\n\t\t// WebGPU analogue of clearRenderTarget, no clearing draw needed\n\t\tlet colorLoadOp = opts.colorLoadOp;\n\t\tlet clearValue = opts.clearValue;\n\t\tlet stencilLoadOp = opts.stencilLoadOp;\n\t\tif (typeof colorLoadOp === \"undefined\" && this.pendingColorClear) {\n\t\t\tcolorLoadOp = \"clear\";\n\t\t\tclearValue = this.pendingClearValue ?? { r: 0, g: 0, b: 0, a: 0 };\n\t\t\tif (typeof stencilLoadOp === \"undefined\" && this.pendingStencilClear) {\n\t\t\t\tstencilLoadOp = \"clear\";\n\t\t\t}\n\t\t}\n\t\tthis.pendingColorClear = false;\n\t\tthis.pendingClearValue = null;\n\t\tthis.pendingStencilClear = false;\n\t\t// every attachment of a pass must have identical dimensions AND one\n\t\t// sample count — canvas passes are multisampled when antiAlias is\n\t\t// on (resolving into the canvas view), and post-effect CAPTURE\n\t\t// targets carry their own sample count (resolving into their\n\t\t// sampleable texture) so MSAA survives the offscreen detour;\n\t\t// ping-pong intermediates stay single-sampled\n\t\tconst [width, height] = this.getTargetSize();\n\t\tconst sampleCount =\n\t\t\ttarget === null ? this.canvasSampleCount : (target.sampleCount ?? 1);\n\t\tthis.createDepthTexture(width, height, sampleCount);\n\t\t// every pipeline recorded into this pass must declare its count —\n\t\t// the cache keys on it, so both variants coexist compiled\n\t\tthis.pipelineCache.sampleCount = sampleCount;\n\t\tlet colorAttachment;\n\t\tif (sampleCount > 1) {\n\t\t\tlet msaaView;\n\t\t\tif (target === null) {\n\t\t\t\tthis.createMsaaColorTexture(width, height);\n\t\t\t\tmsaaView = this.msaaColorView;\n\t\t\t} else {\n\t\t\t\t// the target owns its multisampled half (a shared one would\n\t\t\t\t// hand another target's stored samples to a load-back when\n\t\t\t\t// nested effect brackets interleave passes)\n\t\t\t\tmsaaView = target.msaaView;\n\t\t\t}\n\t\t\tcolorAttachment = {\n\t\t\t\tview: msaaView,\n\t\t\t\t// resolved into the sampleable view at every pass end;\n\t\t\t\t// samples are stored so mid-frame restarts can load them back\n\t\t\t\tresolveTarget: colorView,\n\t\t\t\tloadOp: colorLoadOp ?? \"load\",\n\t\t\t\tclearValue,\n\t\t\t\tstoreOp: \"store\",\n\t\t\t};\n\t\t} else {\n\t\t\tcolorAttachment = {\n\t\t\t\tview: colorView,\n\t\t\t\tloadOp: colorLoadOp ?? \"load\",\n\t\t\t\tclearValue,\n\t\t\t\tstoreOp: \"store\",\n\t\t\t};\n\t\t}\n\t\t// resolve AFTER createDepthTexture — a recreation arms the clear\n\t\tconst { depthLoadOp, depthStoreOp } = resolveDepthOps(\n\t\t\tthis.meshDepthActive,\n\t\t\tthis.pendingDepthClear,\n\t\t);\n\t\tthis.pendingDepthClear = false;\n\t\tthis.renderPass = this.commandEncoder.beginRenderPass({\n\t\t\tlabel: \"melonJS pass\",\n\t\t\tcolorAttachments: [colorAttachment],\n\t\t\tdepthStencilAttachment: {\n\t\t\t\tview: (sampleCount > 1\n\t\t\t\t\t? this.msaaDepthTexture\n\t\t\t\t\t: this.depthTexture\n\t\t\t\t).createView(),\n\t\t\t\tdepthLoadOp,\n\t\t\t\tdepthClearValue: 1.0,\n\t\t\t\tdepthStoreOp,\n\t\t\t\tstencilLoadOp: stencilLoadOp ?? \"load\",\n\t\t\t\tstencilClearValue: 0,\n\t\t\t\tstencilStoreOp: \"store\",\n\t\t\t},\n\t\t});\n\t\tthis.applyViewport();\n\t\tthis.applyScissor();\n\t\t// a new pass resets the stencil reference to 0 — re-apply the mask\n\t\t// reference so content masked ACROSS a pass restart (post-effect\n\t\t// retargets, captures) keeps testing against the right level\n\t\tthis.renderPass.setStencilReference(this.maskVisibleRef);\n\t\t// pipeline/bind state does not carry across passes\n\t\tthis.currentPipeline = null;\n\t}\n\n\t/**\n\t * pixel dimensions of the active draw destination — the current render\n\t * target, or the canvas\n\t * @returns {[number, number]} [width, height]\n\t * @ignore\n\t * @internal\n\t */\n\tgetTargetSize() {\n\t\tconst target = this.currentRenderTarget;\n\t\tif (target !== null) {\n\t\t\treturn [target.width, target.height];\n\t\t}\n\t\tconst canvas = this.getCanvas();\n\t\treturn [canvas.width, canvas.height];\n\t}\n\n\t/**\n\t * Switch the active draw destination — a pass break under the recording\n\t * model: pending vertices drain, the open pass ends, and the next pass\n\t * (opened lazily by `ensurePass`) targets the given render target's\n\t * color view, or the canvas when `target` is null.\n\t * @param {import(\"../rendertarget/webgpurendertarget.js\").default|null} target - the render target, or null for the canvas\n\t * @param {object} [options] - retarget options\n\t * @param {boolean} [options.clear=false] - open the next pass with a clearing color load\n\t * @ignore\n\t * @internal\n\t */\n\tsetRenderTarget(target, options = {}) {\n\t\tthis.currentBatcher?.flush();\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.renderPass.end();\n\t\t\tthis.renderPass = null;\n\t\t}\n\t\tthis.currentRenderTarget = target ?? null;\n\t\tthis.pendingColorClear =\n\t\t\toptions.clear === true || (target?.pendingClear ?? false);\n\t\tthis.pendingClearValue = options.clearValue ?? null;\n\t\tthis.pendingStencilClear = options.clearStencil === true;\n\t\t// a target change re-arms the depth clear in BOTH directions — the\n\t\t// GL RENDER_TARGET_CHANGED re-arm: each destination gets one depth\n\t\t// clear per frame (the shared attachment holds the other target's\n\t\t// depth, which is stale data from this destination's point of view)\n\t\tthis.pendingDepthClear = true;\n\t\tif (target) {\n\t\t\ttarget.pendingClear = false;\n\t\t}\n\t}\n\n\t/**\n\t * Dispose of a GPUTexture safely: while a frame is recording, the\n\t * texture may be referenced by already-recorded draws — destroying it\n\t * would make the whole `queue.submit()` fail validation — so it parks\n\t * on the retired list and is destroyed after submit (or abandon).\n\t * @param {GPUTexture} texture - the texture to dispose of\n\t * @ignore\n\t * @internal\n\t */\n\tretireTexture(texture) {\n\t\tif (this.commandEncoder !== null) {\n\t\t\tthis.retiredTextures.push(texture);\n\t\t} else {\n\t\t\ttexture.destroy();\n\t\t}\n\t}\n\n\t/**\n\t * Dispose of a GPUBuffer safely — the buffer twin of\n\t * {@link WebGPURenderer#retireTexture}, and the same reasoning: a buffer\n\t * referenced by already-recorded draws (retained mesh geometry) must\n\t * outlive the frame's submit. GPUBuffer and GPUTexture share the\n\t * `destroy()` contract, so retired buffers ride the same list.\n\t * @param {GPUBuffer} buffer - the buffer to dispose of\n\t * @ignore\n\t * @internal\n\t */\n\tretireBuffer(buffer) {\n\t\tthis.retireTexture(buffer);\n\t}\n\n\t/**\n\t * The 1×1 transparent-black stand-in view — bound where a declared\n\t * effect texture has no source yet, so bind groups stay valid.\n\t * @returns {GPUTextureView} the stub view\n\t * @ignore\n\t * @internal\n\t */\n\tgetStubTextureView() {\n\t\tif (this.stubTexture === null) {\n\t\t\tthis.stubTexture = this.device.createTexture({\n\t\t\t\tlabel: \"melonJS stub texture\",\n\t\t\t\tsize: [1, 1],\n\t\t\t\tformat: \"rgba8unorm\",\n\t\t\t\tusage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,\n\t\t\t});\n\t\t\tthis.device.queue.writeTexture(\n\t\t\t\t{ texture: this.stubTexture },\n\t\t\t\tnew Uint8Array(4),\n\t\t\t\t{},\n\t\t\t\t[1, 1],\n\t\t\t);\n\t\t\tthis.stubTextureView = this.stubTexture.createView();\n\t\t}\n\t\treturn this.stubTextureView;\n\t}\n\n\t/**\n\t * Restrict rendering to a sub-rectangle of the canvas — the split-screen\n\t * camera surface (`Camera2d`/`Camera3d._setupNonDefaultProjection`).\n\t * Callers pass GL-convention rects with a BOTTOM-left origin (the flip is\n\t * baked into the camera code, which the GL backend depends on); WebGPU\n\t * viewports are top-left, so it is un-flipped here. The rect applies to\n\t * canvas passes only — offscreen post-effect targets always render full\n\t * size, like the GL pool path re-viewporting per target.\n\t * @param {number} x - viewport x (pixels)\n\t * @param {number} y - viewport y, bottom-left origin (pixels)\n\t * @param {number} width - viewport width (pixels)\n\t * @param {number} height - viewport height (pixels)\n\t * @override\n\t */\n\tsetViewport(x, y, width, height) {\n\t\t// dynamic pass state applies to draws recorded after the call —\n\t\t// vertices queued under the previous viewport must land first\n\t\tthis.currentBatcher?.flush();\n\t\tconst canvas = this.getCanvas();\n\t\tthis.viewportRect = {\n\t\t\tx,\n\t\t\ty: canvas.height - y - height,\n\t\t\twidth,\n\t\t\theight,\n\t\t};\n\t\tthis.applyViewport();\n\t}\n\n\t/**\n\t * apply the effective viewport to the open pass (re-run on every pass\n\t * restart — pass state does not carry across passes)\n\t * @ignore\n\t * @internal\n\t */\n\tapplyViewport() {\n\t\tif (this.renderPass === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst [targetWidth, targetHeight] = this.getTargetSize();\n\t\tlet x = 0;\n\t\tlet y = 0;\n\t\tlet width = targetWidth;\n\t\tlet height = targetHeight;\n\t\tconst rect = this.viewportRect;\n\t\tif (rect != null && this.currentRenderTarget === null) {\n\t\t\t// clamp — a viewport extending past the attachment is a WebGPU\n\t\t\t// validation error, not a GL-style silent clip (stale rects\n\t\t\t// survive canvas resizes)\n\t\t\tx = Math.min(Math.max(rect.x, 0), targetWidth);\n\t\t\ty = Math.min(Math.max(rect.y, 0), targetHeight);\n\t\t\twidth = Math.min(rect.width, targetWidth - x);\n\t\t\theight = Math.min(rect.height, targetHeight - y);\n\t\t\tif (width <= 0 || height <= 0) {\n\t\t\t\tx = 0;\n\t\t\t\ty = 0;\n\t\t\t\twidth = targetWidth;\n\t\t\t\theight = targetHeight;\n\t\t\t}\n\t\t}\n\t\tthis.renderPass.setViewport(x, y, width, height, 0, 1);\n\t}\n\n\t/**\n\t * disable the scissor test — pass-model realization of the GL call\n\t * (pending vertices drain, the open pass widens back to the target)\n\t * @override\n\t */\n\tdisableScissor() {\n\t\tif (this.scissorActive === true) {\n\t\t\tthis.currentBatcher?.flush();\n\t\t\tthis.scissorActive = false;\n\t\t\tthis.applyScissor();\n\t\t}\n\t}\n\n\t/**\n\t * Capture everything drawn so far into the shared frame-capture slot —\n\t * the backing of the `screen_texture` effect builtin. A pass break: the\n\t * open pass ends, an encoder-ordered `copyTextureToTexture` snapshots\n\t * the active destination (render target or canvas), and drawing resumes\n\t * lazily with a preserving load. The capture is copy-only (never a\n\t * render attachment), so the very next pass can sample it hazard-free.\n\t * @returns {import(\"./texture/frametexture.js\").WebGPUFrameTexture|null} the shared capture, or null when no device\n\t * @ignore\n\t * @internal\n\t */\n\tcaptureFrame() {\n\t\treturn this.toFrameTexture();\n\t}\n\n\t/**\n\t * Capture the current frame — everything drawn to the active target so\n\t * far — into a {@link Texture2d}, entirely on the GPU (an encoder-ordered\n\t * `copyTextureToTexture`; no readback round-trip). Same contract as the\n\t * WebGL backend's `toFrameTexture`: a shared, renderer-owned slot by\n\t * default, `target: null` for a fresh caller-owned capture, or a prior\n\t * capture as `target` to refresh it in place; `options.region` captures\n\t * a sub-region (framebuffer pixels, bottom-left origin — converted to\n\t * this backend's top-left copy origin internally).\n\t *\n\t * Two documented divergences from the GL capture:\n\t * - alpha is preserved (the GL path captures into an opaque RGB texture)\n\t * - row 0 of the capture is the TOP of the frame (matching `screen_uv`),\n\t *   where the GL capture is bottom-up — GLSL bodies sampling a capture\n\t *   flip with `1.0 - uv.y`; their WGSL twins must not.\n\t * @param {object} [options]\n\t * @param {Texture2d|null} [options.target] - omit for the shared renderer\n\t *   slot; a prior capture to refresh it in place; `null` to mint a fresh,\n\t *   caller-owned capture (`destroy()` it yourself when done)\n\t * @param {Bounds|{x: number, y: number, width: number, height: number}} [options.region] - capture\n\t *   only this sub-region; defaults to the whole frame\n\t * @returns {Texture2d|null} a GPU-resident texture holding the captured\n\t *   frame, or null when no device is available\n\t */\n\ttoFrameTexture(options = {}) {\n\t\tif (typeof this.device === \"undefined\") {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst [fullWidth, fullHeight] = this.getTargetSize();\n\n\t\t// resolve the capture rect (same clamp rules as the GL backend:\n\t\t// origin clamped into the frame first, then sized to what remains)\n\t\tlet x = 0;\n\t\tlet y = 0;\n\t\tlet w = fullWidth;\n\t\tlet h = fullHeight;\n\t\tconst region = options.region;\n\t\tif (typeof region !== \"undefined\") {\n\t\t\tx = Math.min(Math.max(0, Math.floor(region.x || 0)), fullWidth - 1);\n\t\t\ty = Math.min(Math.max(0, Math.floor(region.y || 0)), fullHeight - 1);\n\t\t\tconst rw = Number.isFinite(region.width)\n\t\t\t\t? Math.ceil(region.width)\n\t\t\t\t: fullWidth - x;\n\t\t\tconst rh = Number.isFinite(region.height)\n\t\t\t\t? Math.ceil(region.height)\n\t\t\t\t: fullHeight - y;\n\t\t\tw = Math.max(1, Math.min(fullWidth - x, rw));\n\t\t\th = Math.max(1, Math.min(fullHeight - y, rh));\n\t\t}\n\n\t\t// a non-null target must be a capture THIS renderer returned — any\n\t\t// other Texture2d has no GPU backing here, and a foreign capture\n\t\t// would retire textures on the wrong device\n\t\tif (typeof options.target !== \"undefined\" && options.target !== null) {\n\t\t\tif (!(options.target instanceof WebGPUFrameTexture)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"WebGPURenderer.toFrameTexture: `target` must be a capture returned by this method\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (options.target.renderer !== this) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"WebGPURenderer.toFrameTexture: `target` belongs to a different renderer\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// drain pending geometry + end the pass: the copy is encoder-ordered,\n\t\t// so it sees exactly the draws recorded before this point\n\t\tthis.currentBatcher?.flush();\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.renderPass.end();\n\t\t\tthis.renderPass = null;\n\t\t}\n\n\t\t// resolve the source texture: the active target, or the canvas\n\t\t// (acquired now if nothing drew yet this frame)\n\t\tlet source;\n\t\tif (this.currentRenderTarget !== null) {\n\t\t\tsource = this.currentRenderTarget.texture;\n\t\t} else {\n\t\t\tif (this.frameTexture === null) {\n\t\t\t\tthis.frameTexture = this.context.getCurrentTexture();\n\t\t\t\tthis.frameTextureView = this.frameTexture.createView();\n\t\t\t}\n\t\t\tsource = this.frameTexture;\n\t\t}\n\n\t\t// destination: the shared slot, a fresh caller-owned capture, or the\n\t\t// given capture refreshed in place\n\t\tconst shared = typeof options.target === \"undefined\";\n\t\tlet frame = shared\n\t\t\t? this.captureTexture\n\t\t\t: options.target === null\n\t\t\t\t? undefined\n\t\t\t\t: options.target;\n\n\t\tif (typeof frame === \"undefined\") {\n\t\t\tframe = new WebGPUFrameTexture(this, w, h);\n\t\t\tif (shared) {\n\t\t\t\tthis.captureTexture = frame;\n\t\t\t}\n\t\t} else if (\n\t\t\tframe.width !== w ||\n\t\t\tframe.height !== h ||\n\t\t\tframe.gpuTexture === null\n\t\t) {\n\t\t\t// size change or released backing: reallocate keeping the object\n\t\t\t// identity — generation advances so stale bind groups re-key\n\t\t\tframe.realloc(w, h);\n\t\t}\n\n\t\tif (this.commandEncoder === null) {\n\t\t\tthis.commandEncoder = this.device.createCommandEncoder({\n\t\t\t\tlabel: \"melonJS frame\",\n\t\t\t});\n\t\t}\n\t\t// convert the public bottom-left region origin to the copy's\n\t\t// top-left one\n\t\tthis.commandEncoder.copyTextureToTexture(\n\t\t\t{ texture: source, origin: [x, fullHeight - y - h] },\n\t\t\t{ texture: frame.gpuTexture },\n\t\t\t[w, h],\n\t\t);\n\t\treturn frame;\n\t}\n\n\t/**\n\t * Begin a post-effect pass for the given renderable — the WebGPU\n\t * realization of the WebGL FBO path: the renderable's whole content\n\t * renders into a pooled offscreen target, composited by\n\t * {@link WebGPURenderer#endPostEffect}. The single-effect fast path\n\t * (customShader, no offscreen target) mirrors the WebGL split.\n\t * @param {Renderable} renderable - the renderable carrying postEffects\n\t * @returns {boolean} true when an offscreen pass began\n\t * @override\n\t * @ignore\n\t * @internal\n\t */\n\tbeginPostEffect(renderable) {\n\t\tconst effects = renderable.postEffects.filter((fx) => {\n\t\t\treturn fx.enabled !== false;\n\t\t});\n\t\tif (effects.length === 0) {\n\t\t\tthis.customShader = undefined;\n\t\t\treturn false;\n\t\t}\n\t\t// single effect on a non-managed renderable: fast path (no target)\n\t\tif (effects.length === 1 && !renderable._postEffectManaged) {\n\t\t\tthis.customShader = effects[0];\n\t\t\treturn false;\n\t\t}\n\n\t\t// pooled path: children render with the default pipeline\n\t\tthis.customShader = undefined;\n\n\t\tconst isCamera = renderable._postEffectManaged;\n\t\tconst canvas = this.getCanvas();\n\n\t\tthis.save();\n\t\t// save the current projection (not part of the render state stack) —\n\t\t// one preallocated slot per nesting depth\n\t\tlet savedProjection = this.effectProjectionStack[this.effectPassDepth];\n\t\tif (typeof savedProjection === \"undefined\") {\n\t\t\tsavedProjection = this.effectProjectionStack[this.effectPassDepth] =\n\t\t\t\tnew Matrix3d();\n\t\t}\n\t\tsavedProjection.copy(this.projectionMatrix);\n\t\tthis.effectPassDepth++;\n\n\t\t// capture targets rasterize the scene — under antiAlias they carry\n\t\t// the pass sample count so MSAA composes with post-effects (the GL\n\t\t// twin resolves via blitFramebuffer). Ping-pong intermediates draw\n\t\t// screen-aligned effect quads only and stay single-sampled.\n\t\tthis._renderTargetPool ??= new RenderTargetPool((w, h, isCapture) => {\n\t\t\treturn new WebGPURenderTarget(this, w, h, {\n\t\t\t\tsampleCount: isCapture === true ? this.canvasSampleCount : 1,\n\t\t\t});\n\t\t});\n\t\tconst rt = this._renderTargetPool.begin(\n\t\t\tisCamera,\n\t\t\teffects.length,\n\t\t\tcanvas.width,\n\t\t\tcanvas.height,\n\t\t);\n\t\t// retarget with the appropriate clear: a camera's offscreen pass\n\t\t// starts like a frame (background color + fresh stencil), a\n\t\t// sprite's starts transparent so unpainted texels stay see-through\n\t\tif (isCamera) {\n\t\t\tconst [r, g, b, a] = this.backgroundColor.toArray();\n\t\t\tthis.setRenderTarget(rt, {\n\t\t\t\tclear: true,\n\t\t\t\tclearValue: { r, g, b, a },\n\t\t\t\tclearStencil: true,\n\t\t\t});\n\t\t} else {\n\t\t\tthis.setRenderTarget(rt, { clear: true });\n\t\t}\n\t\tthis.disableScissor();\n\t\tthis.setGlobalAlpha(1.0);\n\t\tthis.setBlendMode(\"normal\");\n\t\treturn true;\n\t}\n\n\t/**\n\t * End a post-effect pass: retarget to the parent (or the canvas),\n\t * refresh the shared frame capture for `screen_texture` consumers, and\n\t * composite the offscreen content through the effect chain — one blit\n\t * per effect, ping-ponging between pool targets for chains.\n\t * @param {Renderable} renderable - the renderable passed to beginPostEffect\n\t * @override\n\t * @ignore\n\t * @internal\n\t */\n\tendPostEffect(renderable) {\n\t\tconst effects = renderable.postEffects.filter((fx) => {\n\t\t\treturn fx.enabled !== false;\n\t\t});\n\t\tif (effects.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// the fast path set customShader — nothing offscreen to composite\n\t\tif (effects.length === 1 && !renderable._postEffectManaged) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst isCamera = renderable._postEffectManaged;\n\t\tconst pool = this._renderTargetPool;\n\t\tconst rt1 = pool.getCaptureTarget();\n\t\tconst rt2 = pool.getPingPongTarget();\n\t\tconst keepBlend = !isCamera;\n\t\tconst canvas = this.getCanvas();\n\t\tconst w = canvas.width;\n\t\tconst h = canvas.height;\n\n\t\t// `screen_texture` builtin: for a CAMERA the \"screen\" is the scene\n\t\t// itself — capture the still-active offscreen target; for a sprite\n\t\t// chain it's everything behind it — captured after the retarget\n\t\tconst needsScreenTexture = effects.some((fx) => {\n\t\t\treturn fx._screenTextureUniforms?.length > 0;\n\t\t});\n\t\tif (needsScreenTexture && isCamera) {\n\t\t\tthis.captureFrame();\n\t\t}\n\n\t\tconst parentRT = pool.end();\n\n\t\t// clip the composite to the camera viewport for non-default cameras\n\t\t// (the offscreen content sits at the camera's screen position)\n\t\tif (isCamera && renderable.isDefault === false) {\n\t\t\tthis.clipRect(\n\t\t\t\trenderable.screenX,\n\t\t\t\trenderable.screenY,\n\t\t\t\trenderable.width,\n\t\t\t\trenderable.height,\n\t\t\t);\n\t\t}\n\n\t\tthis.setRenderTarget(parentRT);\n\t\temit(RENDER_TARGET_CHANGED, this);\n\n\t\tif (needsScreenTexture && !isCamera) {\n\t\t\tthis.captureFrame();\n\t\t}\n\n\t\tif (effects.length === 1) {\n\t\t\tthis.blitEffect(rt1, 0, 0, w, h, effects[0], keepBlend);\n\t\t} else {\n\t\t\t// multi-pass: ping-pong between the two pool targets\n\t\t\tlet src = rt1;\n\t\t\tlet dst = rt2;\n\t\t\tfor (let i = 0; i < effects.length - 1; i++) {\n\t\t\t\tthis.setRenderTarget(dst, { clear: true });\n\t\t\t\tthis.blitEffect(src, 0, 0, w, h, effects[i], false);\n\t\t\t\tconst tmp = src;\n\t\t\t\tsrc = dst;\n\t\t\t\tdst = tmp;\n\t\t\t}\n\t\t\tthis.setRenderTarget(parentRT);\n\t\t\tthis.blitEffect(src, 0, 0, w, h, effects[effects.length - 1], keepBlend);\n\t\t}\n\n\t\tif (isCamera && renderable.isDefault === false) {\n\t\t\tthis.disableScissor();\n\t\t}\n\n\t\t// restore renderer state and the projection saved in beginPostEffect\n\t\tthis.restore();\n\t\tthis.effectPassDepth--;\n\t\tthis.projectionMatrix.copy(\n\t\t\tthis.effectProjectionStack[this.effectPassDepth],\n\t\t);\n\t\tthis.pushFrameGlobals();\n\t}\n\n\t/**\n\t * Draw a pooled render target through an effect's pipeline as a\n\t * screen-space quad — the compositing primitive of the post-effect\n\t * chain. Blending is disabled for camera blits (the target is fully\n\t * composited) and kept for per-sprite blits (transparent texels must\n\t * not overwrite the scene).\n\t * @param {import(\"../rendertarget/webgpurendertarget.js\").default} source - the target to sample\n\t * @param {number} x - destination x\n\t * @param {number} y - destination y\n\t * @param {number} width - destination width\n\t * @param {number} height - destination height\n\t * @param {ShaderEffect} effect - the effect to composite with\n\t * @param {boolean} [keepBlend=false] - keep the current blend mode\n\t * @override\n\t */\n\tblitEffect(source, x, y, width, height, effect, keepBlend = false) {\n\t\tconst batcher = this.setBatcher(\"quad\");\n\t\t// screen-space ortho for the blit quad (not the camera's world\n\t\t// projection); restored — with a fresh frame-globals slot each way —\n\t\t// right after\n\t\tblitSavedProjection.copy(this.projectionMatrix);\n\t\tthis.projectionMatrix.ortho(0, width, height, 0, -1, 1);\n\t\tthis.pushFrameGlobals();\n\t\tbatcher.blitTexture(source, x, y, width, height, effect, keepBlend);\n\t\tthis.projectionMatrix.copy(blitSavedProjection);\n\t\tthis.pushFrameGlobals();\n\t}\n\n\t/**\n\t * a draw is about to be recorded outside the clear()/flush() bracket\n\t * (unit tests, user code) — open a pass that preserves the canvas.\n\t * Also the pass-restart primitive: masks break the pass to clear the\n\t * stencil, post effects will break it to retarget.\n\t * @ignore\n\t * @internal\n\t */\n\tensurePass() {\n\t\tif (this.renderPass === null) {\n\t\t\tthis.beginPass();\n\t\t\t// out-of-bracket draws can predate the first clear() of the\n\t\t\t// renderer's life — make sure a frame-globals slot exists\n\t\t\tif (this.currentFrameBinding === null && this.uniformRing !== null) {\n\t\t\t\tthis.pushFrameGlobals();\n\t\t\t}\n\t\t}\n\t\treturn this.renderPass;\n\t}\n\n\t/**\n\t * end the open pass and submit the frame's command buffer\n\t * @override\n\t */\n\tflush() {\n\t\tthis.currentBatcher?.flush();\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.renderPass.end();\n\t\t\tthis.renderPass = null;\n\t\t}\n\t\tif (this.commandEncoder !== null) {\n\t\t\tthis.device.queue.submit([this.commandEncoder.finish()]);\n\t\t\tthis.commandEncoder = null;\n\t\t\tthis.frameTextureView = null;\n\t\t\tthis.frameTexture = null;\n\t\t}\n\t\tthis.destroyRetiredTextures();\n\t}\n\n\t/**\n\t * drop the open pass and encoder without submitting (mid-frame\n\t * exception recovery, reset, destroy)\n\t * @ignore\n\t * @internal\n\t */\n\tabandonFrame() {\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.renderPass.end();\n\t\t\tthis.renderPass = null;\n\t\t}\n\t\t// an encoder cannot be discarded explicitly — finish it and drop\n\t\t// the command buffer unsubmitted\n\t\tif (this.commandEncoder !== null) {\n\t\t\tthis.commandEncoder.finish();\n\t\t\tthis.commandEncoder = null;\n\t\t}\n\t\tthis.frameTextureView = null;\n\t\tthis.frameTexture = null;\n\t\t// an abandoned frame may have died inside a post-effect bracket —\n\t\t// never leave an offscreen target active for the next frame\n\t\tthis.currentRenderTarget = null;\n\t\tthis.pendingColorClear = false;\n\t\tthis.pendingDepthClear = false;\n\t\tthis.currentPipeline = null;\n\t\t// the recorded draws are dropped with the command buffer, so any\n\t\t// texture retired during the frame can go now\n\t\tthis.destroyRetiredTextures();\n\t}\n\n\t/**\n\t * destroy GPUTextures replaced mid-frame, now that the command buffer\n\t * referencing them has been submitted (or abandoned)\n\t * @ignore\n\t * @internal\n\t */\n\tdestroyRetiredTextures() {\n\t\tif (this.retiredTextures.length > 0) {\n\t\t\tfor (const texture of this.retiredTextures) {\n\t\t\t\ttexture.destroy();\n\t\t\t}\n\t\t\tthis.retiredTextures.length = 0;\n\t\t}\n\t}\n\n\t/**\n\t * re-apply the current scissor state to the open pass\n\t * @ignore\n\t * @internal\n\t */\n\tapplyScissor() {\n\t\tif (this.renderPass === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst [width, height] = this.getTargetSize();\n\t\tif (this.scissorActive === true) {\n\t\t\t// clamp defensively: an out-of-attachment scissor is a WebGPU\n\t\t\t// validation error that invalidates the whole pass (GL clamps)\n\t\t\tconst s = this.currentScissor;\n\t\t\tconst x = Math.min(Math.max(s[0], 0), width);\n\t\t\tconst y = Math.min(Math.max(s[1], 0), height);\n\t\t\tconst w = Math.min(Math.max(s[2], 0), width - x);\n\t\t\tconst h = Math.min(Math.max(s[3], 0), height - y);\n\t\t\tthis.renderPass.setScissorRect(x, y, w, h);\n\t\t} else {\n\t\t\tthis.renderPass.setScissorRect(0, 0, width, height);\n\t\t}\n\t}\n\n\t/**\n\t * Clear the current clip region with the given color — a full-viewport\n\t * triangle clipped by the active scissor, drawn with blending replaced\n\t * (WebGPU has no scissored clear operation). Used mid-frame by\n\t * ColorLayer and Container backgrounds.\n\t *\n\t * Deliberate divergence from the GL backend: because this is a draw,\n\t * it honors an active stencil mask (GL's `gl.clear` ignores stencil and\n\t * clears the whole scissor region) — under a mask, the clear fills the\n\t * mask window only, which is the behavior masks actually promise.\n\t * @param {Color|string} [color=\"#000000\"] - css color\n\t * @param {boolean} [opaque=false] - allow transparency or not\n\t * @override\n\t */\n\tclearColor(color = \"#000000\", opaque = false) {\n\t\tif (typeof this.device === \"undefined\") {\n\t\t\treturn;\n\t\t}\n\t\t// pending vertices belong before the clear\n\t\tthis.currentBatcher?.flush();\n\t\tconst pass = this.ensurePass();\n\n\t\tlet rgba;\n\t\tif (color instanceof Color) {\n\t\t\trgba = color.toArray();\n\t\t} else {\n\t\t\tconst _color = colorPool.get();\n\t\t\trgba = _color.parseCSS(color).toArray();\n\t\t\tcolorPool.release(_color);\n\t\t}\n\t\tconst slot = this.uniformRing.pushClearColor(\n\t\t\trgba[0],\n\t\t\trgba[1],\n\t\t\trgba[2],\n\t\t\topaque === true ? 1.0 : rgba[3],\n\t\t);\n\t\tconst pipeline = this.pipelineCache.get(\n\t\t\t\"clear\",\n\t\t\t\"triangle-list\",\n\t\t\t\"none\",\n\t\t\ttrue,\n\t\t\t// the clear respects the mask like any other draw\n\t\t\tthis.stencilMode === \"test\" ? \"test\" : \"none\",\n\t\t);\n\t\tpass.setPipeline(pipeline);\n\t\tpass.setBindGroup(0, slot.bindGroup, [slot.dynamicOffset]);\n\t\tpass.draw(3);\n\t\t// the clear draw invalidated the tracked pipeline/bindings\n\t\tthis.currentPipeline = null;\n\t}\n\n\t/**\n\t * Erase the pixels in the given rectangular area by setting them to\n\t * transparent black (rgba(0,0,0,0)).\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point.\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point.\n\t * @param {number} width - The rectangle's width.\n\t * @param {number} height - The rectangle's height.\n\t * @override\n\t */\n\tclearRect(x, y, width, height) {\n\t\tthis.save();\n\t\tthis.clipRect(x, y, width, height);\n\t\t// actual transparent black — the bare clearColor() default\n\t\t// (\"#000000\") parses with alpha 1 and would paint OPAQUE black\n\t\tthis.clearColor(\"rgba(0,0,0,0)\");\n\t\tthis.restore();\n\t}\n\n\t/**\n\t * Draw a TMX tile layer: WGSL-eligible layers (`renderMode ===\n\t * \"shader\"`) draw through the GPU tile path — one quad per tileset,\n\t * GID lookup in a per-layer index texture; everything else falls\n\t * through to the base per-tile loop.\n\t * @param {object} layer - the TMXLayer to draw\n\t * @param {object} rect - the visible region in world coords\n\t * @override\n\t */\n\tdrawTileLayer(layer, rect) {\n\t\tif (layer.renderMode === \"shader\" && layer.orientation === \"orthogonal\") {\n\t\t\t// device-scoped, lazily (re)built: a device loss rebuilds the\n\t\t\t// pipeline cache, detected via the epoch stamp\n\t\t\tif (\n\t\t\t\tthis.orthogonalTMXRenderer === undefined ||\n\t\t\t\tthis.orthogonalTMXRenderer.epoch !== this.pipelineCache.epoch\n\t\t\t) {\n\t\t\t\tthis.orthogonalTMXRenderer = new OrthogonalTMXLayerGPURenderer(this);\n\t\t\t}\n\t\t\tthis.orthogonalTMXRenderer.draw(layer, rect);\n\t\t\treturn;\n\t\t}\n\t\tsuper.drawTileLayer(layer, rect);\n\t}\n\n\t/**\n\t * Add a batcher to this renderer.\n\t * @param {WebGPUBatcher} batcher - a batcher instance\n\t * @param {string} [name=\"default\"] - the batcher name\n\t * @param {boolean} [activate=false] - true to set this batcher as the active one\n\t */\n\taddBatcher(batcher, name = \"default\", activate = false) {\n\t\t// gate on the BACKEND base, not the neutral one: a neutral or\n\t\t// WebGL batcher has none of the pass/pipeline machinery and\n\t\t// would fail mid-activation instead\n\t\tif (!(batcher instanceof WebGPUBatcher)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"addBatcher: batcher must be a WebGPUBatcher subclass (custom WebGPU batchers extend WebGPUBatcher)\",\n\t\t\t);\n\t\t}\n\t\tif (typeof this.batchers.get(name) !== \"undefined\") {\n\t\t\tthrow new Error(\"Invalid Batcher name\");\n\t\t}\n\t\tthis.batchers.set(name, batcher);\n\t\tif (activate === true) {\n\t\t\tthis.setBatcher(name);\n\t\t}\n\t}\n\n\t/**\n\t * Set the active batcher for this renderer — flush + unbind the\n\t * outgoing one, bind the incoming. Unlike the GL backend there is no\n\t * projection re-sync: the projection lives in the shared frame-globals\n\t * bind group, not in per-program uniforms.\n\t * @param {string} [name=\"default\"] - a batcher name\n\t * @returns {WebGPUBatcher} the now-active batcher\n\t */\n\tsetBatcher(name = \"default\") {\n\t\tconst batcher = this.batchers.get(name);\n\t\tif (typeof batcher === \"undefined\") {\n\t\t\tthrow new Error(\"Invalid Batcher\");\n\t\t}\n\t\tif (this.currentBatcher !== batcher) {\n\t\t\t// Leaving mesh mode drains the deferred ground-shadow queue first,\n\t\t\t// so the blobs land on top of every opaque mesh in the pass (#1515).\n\t\t\t// Switching *within* mesh mode (lit ↔ unlit) must NOT drain it — the\n\t\t\t// meshes still to come are exactly what the shadows have to beat.\n\t\t\tif (\n\t\t\t\tname !== \"mesh\" &&\n\t\t\t\tname !== \"litMesh\" &&\n\t\t\t\t// see WebGLRenderer#_canDrainShadows: a transition raised while\n\t\t\t\t// a mask is being stencilled in (colour writes off) or inside a\n\t\t\t\t// post-effect bracket is not the end of the mesh pass, and\n\t\t\t\t// draining there loses the blobs. The camera drain still gets\n\t\t\t\t// them.\n\t\t\t\tthis.maskLevel === 0 &&\n\t\t\t\tthis.stencilMode !== \"write\"\n\t\t\t) {\n\t\t\t\tthis.flushGroundShadows();\n\t\t\t}\n\t\t\tif (this.currentBatcher !== null) {\n\t\t\t\tthis.currentBatcher.flush();\n\t\t\t\tthis.currentBatcher.unbind();\n\t\t\t}\n\t\t\tthis.currentBatcher = batcher;\n\t\t\tthis.currentBatcher.bind();\n\t\t}\n\t\treturn this.currentBatcher;\n\t}\n\n\t/**\n\t * Draw a textured triangle mesh — the mesh-path contract of the base\n\t * renderer, on this backend's pipeline-state model. Unlike GL there is\n\t * no device state to toggle: depth testing (write + LEQUAL) and the\n\t * per-mesh face culling are axes of the pipeline the mesh batcher\n\t * looks up per flush, and the once-per-target depth clear is the\n\t * pass's `depthLoadOp` (armed by `clear()`/`setRenderTarget`).\n\t * @param {object} mesh - a Mesh object with vertices, uvs, indices, and texture properties\n\t * @param {Matrix3d} [modelMatrix] - the mesh's model matrix. When given,\n\t *   the mesh is drawn from persistent model-space geometry and placed\n\t *   entirely by uniforms; when omitted, its vertices are taken as\n\t *   already positioned and accumulated through the batcher (the\n\t *   2D-camera path).\n\t * @override\n\t */\n\t/**\n\t * Draw one ground shadow per instance of an `InstancedMesh` (#1515), in a\n\t * single recorded call over the mesh's own instance buffer.\n\t * @param {InstancedMesh} mesh - the mesh casting the shadows\n\t * @param {Matrix3d} shadowMatrix - the group matrix flattened onto the ground\n\t * @param {object} quad - the shared shadow quad\n\t * @ignore\n\t */\n\tdrawInstancedShadow(mesh, shadowMatrix, quad) {\n\t\tconst tint = this.currentTint.toUint32(this.getGlobalAlpha());\n\t\t// deferred to the end of the mesh pass for the same reason a per-object\n\t\t// shadow is — see `Renderer#queueGroundShadow`\n\t\tif (this._shadowFlushing !== true) {\n\t\t\tthis.queueGroundShadow(quad, shadowMatrix, tint, mesh);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(quad.lit === true ? \"litMesh\" : \"mesh\");\n\t\tthis.currentBatcher.drawInstancedShadow(mesh, shadowMatrix, tint, quad);\n\t}\n\n\tdrawMesh(mesh, modelMatrix) {\n\t\tconst retained = modelMatrix !== undefined;\n\n\t\t// A ground shadow waits for the end of the mesh pass rather than\n\t\t// drawing here: it writes no depth, so every opaque mesh still to come\n\t\t// would paint over it (#1515). `flushGroundShadows` calls back in with\n\t\t// `_shadowFlushing` set, and that pass takes the branch below.\n\t\tif (\n\t\t\tmesh._blendedDraw === true &&\n\t\t\tretained &&\n\t\t\tthis._shadowFlushing !== true\n\t\t) {\n\t\t\tthis.queueGroundShadow(\n\t\t\t\tmesh,\n\t\t\t\tmodelMatrix,\n\t\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.meshDepthActive !== true) {\n\t\t\t// the depth half of the attachment is live from here on — pass\n\t\t\t// restarts preserve it and clears follow the armed policy (see\n\t\t\t// resolveDepthOps). The pass open RIGHT NOW was begun with the\n\t\t\t// pre-mesh discard ops, so arm a clear as well: a mid-frame\n\t\t\t// restart on this activation frame (mask, capture) must CLEAR\n\t\t\t// rather than \"load\" the discarded — undefined — contents.\n\t\t\t// Costs one extra depth clear on this frame only.\n\t\t\tthis.meshDepthActive = true;\n\t\t\tthis.pendingDepthClear = true;\n\t\t}\n\n\t\t// a hosted custom shader (mesh.shader): a GLShader carrying a WGSL\n\t\t// module (isWebGPU) is a complete mesh-contract program — route it\n\t\t// to the batcher, which realizes it as its own shader family.\n\t\t// Anything else in the slot cannot host a mesh here — a ShaderEffect\n\t\t// (WGSL effect realizations are bound to the frozen quad vertex\n\t\t// layout) or a GLSL-only shader — so draw the mesh with the built-in\n\t\t// shading rather than dropping it, and say so once.\n\t\tconst customShader =\n\t\t\tthis.customShader != null && this.customShader.isWebGPU === true\n\t\t\t\t? this.customShader\n\t\t\t\t: null;\n\t\tif (\n\t\t\tthis.customShader != null &&\n\t\t\tcustomShader === null &&\n\t\t\tthis.meshEffectWarned !== true\n\t\t) {\n\t\t\tthis.meshEffectWarned = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"melonJS: this custom shader cannot be hosted on a Mesh by the WebGPU renderer (it carries no `wgsl` module) — the mesh draws with the built-in shading\",\n\t\t\t);\n\t\t}\n\n\t\tconst batcher = this.setBatcher(\n\t\t\tmesh.lit === true && this.batchers.has(\"litMesh\") ? \"litMesh\" : \"mesh\",\n\t\t);\n\t\tbatcher.customShader = customShader;\n\n\t\t// per-mesh culling is a pipeline axis here. Retained geometry keeps\n\t\t// its authored winding, so the axis bridge's reflection (which\n\t\t// mirrors handedness) is undone by flipping which orientation counts\n\t\t// as front-facing; right-handed meshes are bridged by a rotation,\n\t\t// which preserves winding. Winding is irrelevant with culling off —\n\t\t// \"ccw\" there keeps the pipeline permutation count down.\n\t\tconst culling = mesh.cullBackFaces === true;\n\t\tconst state = batcher.meshState;\n\t\tstate.cullMode = culling ? \"back\" : \"none\";\n\t\tstate.frontFace =\n\t\t\tculling && retained && mesh.rightHanded !== true ? \"cw\" : \"ccw\";\n\n\t\t// finally: the hosted module is per-mesh state — a throw mid-draw\n\t\t// (e.g. an unsupported-format texture upload) must not leak it into\n\t\t// a later mesh (or the frame-end drain) that didn't ask for it\n\t\ttry {\n\t\t\tconst tint = this.currentTint.toUint32(this.getGlobalAlpha());\n\t\t\tif (\n\t\t\t\tmesh.instanceLayout !== undefined &&\n\t\t\t\tretained &&\n\t\t\t\tthis.supportsInstancing === true\n\t\t\t) {\n\t\t\t\t// one geometry, N copies, one recorded draw — the per-instance\n\t\t\t\t// records carry what each copy differs by, while `modelMatrix`\n\t\t\t\t// places the group as a whole\n\t\t\t\tbatcher.drawInstancedMesh(mesh, modelMatrix, tint);\n\t\t\t} else if (retained) {\n\t\t\t\tbatcher.drawRetainedMesh(mesh, modelMatrix, tint);\n\t\t\t} else {\n\t\t\t\tbatcher.addMesh(mesh, tint);\n\t\t\t\t// drain the batcher only — renderer.flush() submits the frame\n\t\t\t\tbatcher.flush();\n\t\t\t}\n\t\t} finally {\n\t\t\tbatcher.customShader = null;\n\t\t}\n\t}\n\n\t/**\n\t * Release any retained geometry held for the given mesh (called from\n\t * `Mesh.onDeactivateEvent` / `Mesh.destroy`).\n\t * @param {object} mesh - the mesh whose GPU geometry should be freed\n\t */\n\tdeleteMeshGeometry(mesh) {\n\t\tthis.batchers.forEach((batcher) => {\n\t\t\tbatcher.releaseRetained?.(mesh);\n\t\t});\n\t}\n\n\t/**\n\t * Reset the transform to identity\n\t */\n\tresetTransform() {\n\t\tthis.currentTransform.identity();\n\t}\n\n\t/**\n\t * adds a translation transformation to the current matrix\n\t * @param {number} x - distance to move in the horizontal direction\n\t * @param {number} y - distance to move in the vertical direction\n\t */\n\ttranslate(x, y) {\n\t\tthis.currentTransform.translate(x, y);\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// snap position values to pixel grid\n\t\t\tconst a = this.currentTransform.val;\n\t\t\ta[12] |= 0;\n\t\t\ta[13] |= 0;\n\t\t}\n\t}\n\n\t/**\n\t * adds a rotation to the transformation matrix\n\t * @param {number} angle - the rotation angle, clockwise in radians\n\t */\n\trotate(angle) {\n\t\tthis.currentTransform.rotate(angle);\n\t}\n\n\t/**\n\t * adds a scaling transformation to the renderer units\n\t * @param {number} x - horizontal scaling factor\n\t * @param {number} y - vertical scaling factor\n\t */\n\tscale(x, y) {\n\t\tthis.currentTransform.scale(x, y, 1);\n\t}\n\n\t/**\n\t * Multiply the given matrix (or 2D affine components) into the current\n\t * transformation matrix\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix, or the a component\n\t * @param {number} [b] - the b component\n\t * @param {number} [c] - the c component\n\t * @param {number} [d] - the d component\n\t * @param {number} [e] - the e component\n\t * @param {number} [f] - the f component\n\t */\n\ttransform(a, b, c, d, e, f) {\n\t\tif (typeof a === \"object\") {\n\t\t\t// accepts both Matrix2d and Matrix3d (no temporary copy needed)\n\t\t\tthis.currentTransform.multiply(a);\n\t\t} else {\n\t\t\t// individual 2D affine components\n\t\t\tthis.currentTransform.multiply(\n\t\t\t\ttempMatrix.setTransform(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, e, f, 0, 1),\n\t\t\t);\n\t\t}\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// snap position values to pixel grid\n\t\t\tconst m = this.currentTransform.val;\n\t\t\tm[12] |= 0;\n\t\t\tm[13] |= 0;\n\t\t}\n\t}\n\n\t/**\n\t * Reset then multiply the transformation matrix\n\t * @param {Matrix2d|Matrix3d|number} a - a matrix, or the a component\n\t * @param {number} [b] - the b component\n\t * @param {number} [c] - the c component\n\t * @param {number} [d] - the d component\n\t * @param {number} [e] - the e component\n\t * @param {number} [f] - the f component\n\t */\n\tsetTransform(a, b, c, d, e, f) {\n\t\tthis.resetTransform();\n\t\tthis.transform(a, b, c, d, e, f);\n\t}\n\n\t/**\n\t * saves the entire state of the renderer onto the state stack\n\t */\n\tsave() {\n\t\tthis.renderState.currentShader = this.customShader;\n\t\tthis.renderState.save(this.scissorActive === true);\n\t}\n\n\t/**\n\t * restores the most recently saved renderer state\n\t */\n\trestore() {\n\t\tconst canvas = this.getCanvas();\n\t\t// Peek at the to-be-restored scissor so vertices queued inside a\n\t\t// tighter clip drain under the CURRENT scissor before it widens —\n\t\t// otherwise they would flush later and visually escape their clip.\n\t\tconst peek = this.renderState.peekScissor();\n\t\tconst cur = this.currentScissor;\n\t\tconst curActive = this.scissorActive === true;\n\t\tconst willBeActive = peek !== null;\n\t\tconst scissorChanging =\n\t\t\tcurActive !== willBeActive ||\n\t\t\t(willBeActive &&\n\t\t\t\t(cur[0] !== peek[0] ||\n\t\t\t\t\tcur[1] !== peek[1] ||\n\t\t\t\t\tcur[2] !== peek[2] ||\n\t\t\t\t\tcur[3] !== peek[3]));\n\t\tif (scissorChanging) {\n\t\t\tthis.currentBatcher?.flush();\n\t\t}\n\t\tconst result = this.renderState.restore(canvas.width, canvas.height);\n\t\tif (result !== null) {\n\t\t\tthis.setBlendMode(result.blendMode);\n\t\t\tif (scissorChanging) {\n\t\t\t\tthis.scissorActive = result.scissorActive === true;\n\t\t\t\tthis.applyScissor();\n\t\t\t}\n\t\t}\n\t\t// sync gradient and shader from renderState\n\t\tthis.currentGradient = this.renderState.currentGradient;\n\t\tthis.customShader = this.renderState.currentShader;\n\t}\n\n\t/**\n\t * Multiply the global alpha\n\t * @param {number} alpha - global alpha value (0..1)\n\t */\n\tsetGlobalAlpha(alpha) {\n\t\tthis.currentColor.alpha = alpha;\n\t}\n\n\t/**\n\t * Return the global alpha\n\t * @returns {number} global alpha value\n\t */\n\tgetGlobalAlpha() {\n\t\treturn this.currentColor.alpha;\n\t}\n\n\t/**\n\t * Set the current fill & stroke style color\n\t * @param {Color|string|Gradient} color - css color string or a Gradient object\n\t */\n\tsetColor(color) {\n\t\tif (color instanceof Gradient) {\n\t\t\tthis.renderState.currentGradient = color;\n\t\t\tthis.currentGradient = color;\n\t\t} else {\n\t\t\tthis.renderState.currentGradient = null;\n\t\t\tthis.currentGradient = null;\n\t\t\tconst alpha = this.currentColor.alpha;\n\t\t\tthis.currentColor.copy(color);\n\t\t\tthis.currentColor.alpha *= alpha;\n\t\t}\n\t}\n\n\t/**\n\t * Set the blend mode for subsequent draws. Under WebGPU blending is\n\t * pipeline state: the change is recorded (after draining vertices\n\t * queued under the previous mode) and the next batcher flush picks the\n\t * matching pipeline variant.\n\t * @param {string} [mode=\"normal\"] - blend mode (\"normal\", \"multiply\", \"additive\"/\"lighter\", \"screen\", \"darken\", \"lighten\", \"none\")\n\t * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (affects the source blend factor)\n\t * @returns {string} the blend mode actually applied\n\t */\n\tsetBlendMode(mode = \"normal\", premultipliedAlpha = true) {\n\t\tconst normalized = normalizeBlendMode(mode);\n\t\tif (\n\t\t\tthis.currentBlendMode !== normalized ||\n\t\t\tthis.premultipliedAlpha !== premultipliedAlpha\n\t\t) {\n\t\t\tthis.currentBatcher?.flush();\n\t\t\tthis.currentBlendMode = normalized;\n\t\t\tthis.premultipliedAlpha = premultipliedAlpha;\n\t\t}\n\t\t// Report what the CALLER asked for when it was honoured, not the\n\t\t// canonical name it collapsed to. `add` / `lighter` / `additive` are\n\t\t// three spellings of one pipeline state, and returning `\"additive\"` for\n\t\t// all three made the first two look UNSUPPORTED to anything comparing\n\t\t// the result against its request — which is exactly how the engine's\n\t\t// own capability probe works, so the Blend Modes example painted `add`\n\t\t// red under WebGPU while WebGL showed it green. Same rendering, wrong\n\t\t// report. `\"normal\"` still means \"asked for something unsupported,\n\t\t// fell back\", which is what the contract is actually for.\n\t\treturn normalized === \"normal\" && mode !== \"normal\" ? \"normal\" : mode;\n\t}\n\n\t/**\n\t * Set the projection matrix — pushes a fresh frame-globals slot so\n\t * draws already recorded keep the projection they were queued under\n\t * (floating containers swap the projection mid-frame).\n\t * @param {Matrix3d} matrix - the new projection matrix\n\t */\n\tsetProjection(matrix) {\n\t\tthis.currentBatcher?.flush();\n\t\tsuper.setProjection(matrix);\n\t\t// a slot is pushed even when no pass is open (slots are plain\n\t\t// buffer writes; the binding is consumed by the next flush) so the\n\t\t// projection is never stale after an explicit mid-frame flush()\n\t\tif (this.uniformRing !== null) {\n\t\t\tthis.pushFrameGlobals();\n\t\t}\n\t}\n\n\t/**\n\t * push a fresh frame-globals slot (projection + lineWidth); records the\n\t * lineWidth written so batchers can detect when the slot goes stale\n\t * @ignore\n\t * @internal\n\t */\n\tpushFrameGlobals() {\n\t\tthis.currentFrameBinding = this.uniformRing.pushFrameGlobals(\n\t\t\tthis.projectionMatrix,\n\t\t\tthis.lineWidth,\n\t\t);\n\t\tthis.currentFrameLineWidth = this.lineWidth;\n\t}\n\n\t/**\n\t * Draw an image onto the frame (Canvas-compatible 3/5/9-argument forms).\n\t * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - the source image\n\t * @param {number} sx - source x (or destination x in the 3/5-arg forms)\n\t * @param {number} sy - source y (or destination y in the 3/5-arg forms)\n\t * @param {number} [sw] - source width\n\t * @param {number} [sh] - source height\n\t * @param {number} [dx] - destination x\n\t * @param {number} [dy] - destination y\n\t * @param {number} [dw] - destination width\n\t * @param {number} [dh] - destination height\n\t */\n\tdrawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {\n\t\tif (typeof sw === \"undefined\") {\n\t\t\tsw = dw = image.width;\n\t\t\tsh = dh = image.height;\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t} else if (typeof dx === \"undefined\") {\n\t\t\tdx = sx;\n\t\t\tdy = sy;\n\t\t\tdw = sw;\n\t\t\tdh = sh;\n\t\t\tsw = image.width;\n\t\t\tsh = image.height;\n\t\t\tsx = 0;\n\t\t\tsy = 0;\n\t\t}\n\n\t\tif (this.settings.subPixel === false) {\n\t\t\t// clamp to pixel grid\n\t\t\tdx |= 0;\n\t\t\tdy |= 0;\n\t\t}\n\n\t\t// same lit gate as the GL backend: only normal-mapped sprites in a\n\t\t// lit scene pay the lit path; everything else stays on \"quad\".\n\t\t// Two extra conditions this backend needs:\n\t\t// - an active customShader (the single-effect fast path) wins over\n\t\t//   the lit gate — the lit pipeline has no effect stage, so routing\n\t\t//   the quad there would silently drop the effect. A deliberate\n\t\t//   divergence from GL (which runs the effect's shader over its lit\n\t\t//   vertex stream): here the sprite draws effected-but-unlit rather\n\t\t//   than lit-but-uneffected, and never silently loses the effect.\n\t\t// - the light block must have been snapshotted THIS frame: the lit\n\t\t//   batcher's binding points into the per-frame effect-uniform arena,\n\t\t//   so a stale (previous-frame) binding would sample reused bytes.\n\t\tconst lit = this.batchers.get(\"litQuad\");\n\t\tconst useLit =\n\t\t\ttypeof lit !== \"undefined\" &&\n\t\t\tthis.customShader == null &&\n\t\t\tthis.activeLightCount > 0 &&\n\t\t\tthis.currentNormalMap !== null &&\n\t\t\tlit.hasCurrentLightBinding(this.frameId);\n\t\tthis.setBatcher(useLit ? \"litQuad\" : \"quad\");\n\n\t\tconst texture = this.cache.get(image);\n\t\t// Video sources need their GPU texture refreshed as the video\n\t\t// plays — same duck-typed version-stamp convention as the WebGL\n\t\t// backend: re-upload only when a new frame was actually presented,\n\t\t// or every draw when no stamp is available.\n\t\tlet reupload = false;\n\t\tif (typeof image.videoWidth !== \"undefined\") {\n\t\t\tif (image.version === undefined) {\n\t\t\t\treupload = true;\n\t\t\t} else if (texture._videoFrameVersion !== image.version) {\n\t\t\t\ttexture._videoFrameVersion = image.version;\n\t\t\t\treupload = true;\n\t\t\t}\n\t\t}\n\t\tconst uvs = texture.getUVs(sx, sy, sw, sh);\n\t\tthis.currentBatcher.addQuad(\n\t\t\ttexture,\n\t\t\tdx,\n\t\t\tdy,\n\t\t\tdw,\n\t\t\tdh,\n\t\t\tuvs[0],\n\t\t\tuvs[1],\n\t\t\tuvs[2],\n\t\t\tuvs[3],\n\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t\treupload,\n\t\t\tuseLit ? this.currentNormalMap : undefined,\n\t\t);\n\t}\n\n\t/**\n\t * The compressed-texture families this device supports, in the same\n\t * shape as the GL backend (one key per family; supported families hold\n\t * an object of WebGL format constants — the values the loader parsers\n\t * emit — unsupported ones are null, so the shared\n\t * hasSupportedCompressedFormats works unchanged). PVRTC has no WebGPU\n\t * equivalent and stays null.\n\t * @returns {object} one key per extension family\n\t * @override\n\t */\n\tgetSupportedCompressedTextureFormats() {\n\t\tif (typeof this.supportedCompressedFormats === \"undefined\") {\n\t\t\tconst features = this.device?.features;\n\t\t\tconst bc = features?.has(\"texture-compression-bc\") === true;\n\t\t\tconst etc2 = features?.has(\"texture-compression-etc2\") === true;\n\t\t\tconst astc = features?.has(\"texture-compression-astc\") === true;\n\t\t\tconst table = {\n\t\t\t\tastc: astc\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_4x4_KHR: 0x93b0,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_5x4_KHR: 0x93b1,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_5x5_KHR: 0x93b2,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_6x5_KHR: 0x93b3,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_6x6_KHR: 0x93b4,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_8x5_KHR: 0x93b5,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_8x6_KHR: 0x93b6,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_ASTC_8x8_KHR: 0x93b7,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: 0x93d0,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: 0x93d1,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: 0x93d2,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: 0x93d3,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: 0x93d4,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: 0x93d5,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: 0x93d6,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: 0x93d7,\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t\tbptc: bc ? { COMPRESSED_RGBA_BPTC_UNORM_EXT: 0x8e8c } : null,\n\t\t\t\ts3tc: bc\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tCOMPRESSED_RGB_S3TC_DXT1_EXT: 0x83f0,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_S3TC_DXT3_EXT: 0x83f2,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA_S3TC_DXT5_EXT: 0x83f3,\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t\ts3tc_srgb: bc\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tCOMPRESSED_SRGB_S3TC_DXT1_EXT: 0x8c4c,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: 0x8c4d,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: 0x8c4e,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: 0x8c4f,\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t\tpvrtc: null,\n\t\t\t\t// ETC1 payloads upload as ETC2 rgb8 (superset)\n\t\t\t\tetc1: etc2 ? { COMPRESSED_RGB_ETC1_WEBGL: 0x8d64 } : null,\n\t\t\t\tetc2: etc2\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tCOMPRESSED_RGB8_ETC2: 0x9274,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ETC2: 0x9275,\n\t\t\t\t\t\t\tCOMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9276,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9277,\n\t\t\t\t\t\t\tCOMPRESSED_RGBA8_ETC2_EAC: 0x9278,\n\t\t\t\t\t\t\tCOMPRESSED_SRGB8_ALPHA8_ETC2_EAC: 0x9279,\n\t\t\t\t\t\t\tCOMPRESSED_R11_EAC: 0x9270,\n\t\t\t\t\t\t\tCOMPRESSED_SIGNED_R11_EAC: 0x9271,\n\t\t\t\t\t\t\tCOMPRESSED_RG11_EAC: 0x9272,\n\t\t\t\t\t\t\tCOMPRESSED_SIGNED_RG11_EAC: 0x9273,\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t};\n\t\t\t// no device = the renegotiation window after a device loss: the\n\t\t\t// all-null table is only valid for this instant and must not be\n\t\t\t// memoized, or the replacement device's families would stay\n\t\t\t// locked to null\n\t\t\tif (typeof this.device === \"undefined\") {\n\t\t\t\treturn table;\n\t\t\t}\n\t\t\tthis.supportedCompressedFormats = table;\n\t\t}\n\t\treturn this.supportedCompressedFormats;\n\t}\n\n\t/**\n\t * Pack the active 2D lights and hand the std140 block to the lit\n\t * batcher — the WebGPU realization of the backend-neutral lighting\n\t * contract (Camera2d calls this once per camera per frame).\n\t * @param {Set<Light2d>|Array<Light2d>} lights - active lights\n\t * @param {Color} ambient - the ambient lighting floor\n\t * @param {number} [translateX=0] - camera translate x\n\t * @param {number} [translateY=0] - camera translate y\n\t * @override\n\t */\n\tsetLightUniforms(lights, ambient, translateX = 0, translateY = 0) {\n\t\tif (this.lightUniformsScratch === undefined) {\n\t\t\tthis.lightUniformsScratch = createLightUniformScratch();\n\t\t}\n\t\tconst packed = packLights(\n\t\t\tlights,\n\t\t\tambient,\n\t\t\ttranslateX,\n\t\t\ttranslateY,\n\t\t\tthis.lightUniformsScratch,\n\t\t);\n\t\tthis.activeLightCount = packed.count;\n\t\tconst lit = this.batchers.get(\"litQuad\");\n\t\tif (lit && typeof lit.setLightUniforms === \"function\") {\n\t\t\tlit.setLightUniforms(packed);\n\t\t}\n\t}\n\n\t/**\n\t * Draw a Light2d glow quad through the radial-gradient effect's\n\t * single-effect fast path — the light's color and intensity ride the\n\t * per-vertex tint, so back-to-back lights share the same pipeline.\n\t * @param {Light2d} light - the light to draw\n\t * @override\n\t */\n\tdrawLight(light) {\n\t\tif (this.lightShader === undefined) {\n\t\t\tthis.lightShader = new RadialGradientEffect(this);\n\t\t}\n\t\tconst previousShader = this.customShader;\n\t\tthis.customShader = this.lightShader;\n\t\tconst batcher = this.setBatcher(\"quad\");\n\t\tbatcher.addQuad(\n\t\t\tthis.getLightAtlas(),\n\t\t\tlight.pos.x,\n\t\t\tlight.pos.y,\n\t\t\tlight.width,\n\t\t\tlight.height,\n\t\t\t0,\n\t\t\t0,\n\t\t\t1,\n\t\t\t1,\n\t\t\t// pack the light's color (RGB) and intensity (A) into the\n\t\t\t// vertex tint — the effect reads color.rgb / color.a\n\t\t\tlight.color.toUint32(light.intensity),\n\t\t);\n\t\tthis.customShader = previousShader;\n\t}\n\n\t/**\n\t * Lazy-init a shared 1×1 white TextureAtlas used as the source texture\n\t * for drawLight's procedural effect (same rationale as the GL backend)\n\t * @returns {TextureAtlas}\n\t * @ignore\n\t * @internal\n\t */\n\tgetLightAtlas() {\n\t\tif (this.lightAtlas === undefined) {\n\t\t\tconst canvas = globalThis.document\n\t\t\t\t? globalThis.document.createElement(\"canvas\")\n\t\t\t\t: new OffscreenCanvas(1, 1);\n\t\t\tcanvas.width = 1;\n\t\t\tcanvas.height = 1;\n\t\t\tconst ctx = canvas.getContext(\"2d\");\n\t\t\tctx.fillStyle = \"#fff\";\n\t\t\tctx.fillRect(0, 0, 1, 1);\n\t\t\t// cache=false: the atlas is only ever drawn directly through\n\t\t\t// addQuad, so it needs no global TextureCache registration (and\n\t\t\t// this renderer method must not reach for the global game)\n\t\t\tthis.lightAtlas = new TextureAtlas(\n\t\t\t\tcreateAtlas(1, 1, \"lightWhite\", \"no-repeat\"),\n\t\t\t\tcanvas,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t}\n\t\treturn this.lightAtlas;\n\t}\n\n\t/**\n\t * Draw a pattern within the given rectangle.\n\t * @param {TextureAtlas} pattern - pattern object returned by {@link WebGPURenderer#createPattern}\n\t * @param {number} x - x position where to draw the pattern\n\t * @param {number} y - y position where to draw the pattern\n\t * @param {number} width - width of the pattern\n\t * @param {number} height - height of the pattern\n\t */\n\tdrawPattern(pattern, x, y, width, height) {\n\t\tconst uvs = pattern.getUVs(\"0,0,\" + width + \",\" + height);\n\t\tthis.setBatcher(\"quad\");\n\t\tthis.currentBatcher.addQuad(\n\t\t\tpattern,\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tuvs[0],\n\t\t\tuvs[1],\n\t\t\tuvs[2],\n\t\t\tuvs[3],\n\t\t\tthis.currentTint.toUint32(this.getGlobalAlpha()),\n\t\t);\n\t}\n\n\t/**\n\t * Create a pattern with the specified repetition\n\t * @param {HTMLImageElement|SVGImageElement|HTMLVideoElement|HTMLCanvasElement|ImageBitmap|OffscreenCanvas|VideoFrame} image - source image\n\t * @param {string} [repeat=\"no-repeat\"] - one of `\"repeat\"` / `\"repeat-x\"` / `\"repeat-y\"` / `\"no-repeat\"`\n\t * @returns {TextureAtlas} the patterned texture created\n\t */\n\tcreatePattern(image, repeat = \"no-repeat\") {\n\t\tconst texture = new TextureAtlas(\n\t\t\tcreateAtlas(image.width, image.height, \"pattern\", repeat),\n\t\t\timage,\n\t\t);\n\t\t// warm the resident texture + sampler pairing\n\t\tthis.textureStore.getBinding(texture);\n\t\treturn texture;\n\t}\n\n\t/**\n\t * clip the given region from the canvas — all future drawing is\n\t * limited to it. The clip is a transform-derived screen-space AABB\n\t * applied through the pass scissor (WebGPU's `setScissorRect` is\n\t * top-left-origin, so the stored coords apply without the GL y-flip;\n\t * it validates containment, so the box is clamped to the canvas).\n\t * @param {number} x - x axis of the upper-left corner of the region\n\t * @param {number} y - y axis of the upper-left corner of the region\n\t * @param {number} width - the width of the region\n\t * @param {number} height - the height of the region\n\t */\n\tclipRect(x, y, width, height) {\n\t\tconst canvas = this.getCanvas();\n\t\t// treat a non-finite transform as \"no clip\"\n\t\tconst m = this.currentTransform;\n\t\tif (!Number.isFinite(m.tx) || !Number.isFinite(m.ty)) {\n\t\t\tif (this.scissorActive) {\n\t\t\t\t// drain pending vertices under the active scissor first\n\t\t\t\tthis.currentBatcher?.flush();\n\t\t\t\tthis.scissorActive = false;\n\t\t\t\tthis.applyScissor();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// derive the screen-space AABB by feeding the rect's 4 corners\n\t\t// through `currentTransform` — any rotation collapses to the\n\t\t// rotated-rect AABB on screen, same as the GL scissor (#1349)\n\t\tconst aabb = this.clipAABB;\n\t\taabb.clear();\n\t\taabb.addFrame(x, y, x + width, y + height, m);\n\t\tlet sx = Math.floor(aabb.min.x);\n\t\tlet sy = Math.floor(aabb.min.y);\n\t\tlet sw = Math.ceil(aabb.max.x - sx);\n\t\tlet sh = Math.ceil(aabb.max.y - sy);\n\n\t\t// full-canvas coverage → caller intent is \"no clip\"\n\t\tif (\n\t\t\tsx <= 0 &&\n\t\t\tsy <= 0 &&\n\t\t\tsx + sw >= canvas.width &&\n\t\t\tsy + sh >= canvas.height\n\t\t) {\n\t\t\tif (this.scissorActive) {\n\t\t\t\tthis.currentBatcher?.flush();\n\t\t\t\tthis.scissorActive = false;\n\t\t\t\tthis.applyScissor();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setScissorRect(sx, sy, sw, sh);\n\t}\n\n\t/**\n\t * Clamp a screen-space scissor box to the canvas and make it the\n\t * active scissor (shared tail of {@link WebGPURenderer#clipRect} and\n\t * {@link WebGPURenderer#enableScissor}).\n\t * @param {number} sx - screen-space x\n\t * @param {number} sy - screen-space y\n\t * @param {number} sw - width\n\t * @param {number} sh - height\n\t * @ignore\n\t * @internal\n\t */\n\tsetScissorRect(sx, sy, sw, sh) {\n\t\tconst canvas = this.getCanvas();\n\t\t// clamp to the canvas — WebGPU raises a validation error on any\n\t\t// out-of-attachment scissor where GL silently clamps; degenerate\n\t\t// boxes become a 0-sized scissor (nothing draws), not an error\n\t\tif (sx < 0) {\n\t\t\tsw += sx;\n\t\t\tsx = 0;\n\t\t}\n\t\tif (sy < 0) {\n\t\t\tsh += sy;\n\t\t\tsy = 0;\n\t\t}\n\t\tsw = Math.max(0, Math.min(sw, canvas.width - sx));\n\t\tsh = Math.max(0, Math.min(sh, canvas.height - sy));\n\n\t\tconst cs = this.currentScissor;\n\t\tif (\n\t\t\tthis.scissorActive &&\n\t\t\tcs[0] === sx &&\n\t\t\tcs[1] === sy &&\n\t\t\tcs[2] === sw &&\n\t\t\tcs[3] === sh\n\t\t) {\n\t\t\treturn; // already at the right scissor\n\t\t}\n\t\t// drain vertices queued under the previous clip state\n\t\tthis.currentBatcher?.flush();\n\t\tthis.scissorActive = true;\n\t\tcs[0] = sx;\n\t\tcs[1] = sy;\n\t\tcs[2] = sw;\n\t\tcs[3] = sh;\n\t\tthis.applyScissor();\n\t}\n\n\t/**\n\t * Enable the scissor test with the given rectangle (transformed by the\n\t * current transform, like the WebGL renderer). Unlike\n\t * {@link WebGPURenderer#clipRect} a full-canvas rectangle still\n\t * enables the scissor rather than reading as \"no clip\".\n\t * @param {number} x - x coordinate of the scissor rectangle\n\t * @param {number} y - y coordinate of the scissor rectangle\n\t * @param {number} width - width of the scissor rectangle\n\t * @param {number} height - height of the scissor rectangle\n\t * @override\n\t */\n\tenableScissor(x, y, width, height) {\n\t\tconst aabb = this.clipAABB;\n\t\taabb.clear();\n\t\taabb.addFrame(x, y, x + width, y + height, this.currentTransform);\n\t\tconst sx = Math.floor(aabb.min.x);\n\t\tconst sy = Math.floor(aabb.min.y);\n\t\tthis.setScissorRect(\n\t\t\tsx,\n\t\t\tsy,\n\t\t\tMath.ceil(aabb.max.x - sx),\n\t\t\tMath.ceil(aabb.max.y - sy),\n\t\t);\n\t}\n\n\t/**\n\t * Enable or disable blending — blend state is pipeline state on this\n\t * backend, so this maps onto the blend-mode axis: disabling stashes\n\t * the current mode and switches to \"none\" (source replaces\n\t * destination), enabling restores the stashed mode.\n\t * @param {boolean} enable - whether blending should be enabled\n\t * @override\n\t */\n\tsetBlendEnabled(enable) {\n\t\tif (enable === false) {\n\t\t\tif (this.currentBlendMode !== \"none\") {\n\t\t\t\tthis.savedBlendMode = this.currentBlendMode;\n\t\t\t\tthis.setBlendMode(\"none\", this.premultipliedAlpha);\n\t\t\t}\n\t\t} else if (typeof this.savedBlendMode === \"string\") {\n\t\t\tthis.setBlendMode(this.savedBlendMode, this.premultipliedAlpha);\n\t\t\tthis.savedBlendMode = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Clear the current render target with transparent black — the\n\t * offscreen-target analogue of a frame clear, realized as this\n\t * backend's pass-level clear (the next pass on the target loads\n\t * cleared content instead of recording a clearing draw).\n\t * @override\n\t */\n\tclearRenderTarget() {\n\t\t// anything already recorded against the target must land first —\n\t\t// the pass restart below is what applies the clear ordering\n\t\tthis.currentBatcher?.flush();\n\t\tif (this.renderPass !== null) {\n\t\t\tthis.renderPass.end();\n\t\t\tthis.renderPass = null;\n\t\t}\n\t\tthis.pendingColorClear = true;\n\t\tthis.pendingClearValue = { r: 0, g: 0, b: 0, a: 0 };\n\t\temit(RENDER_TARGET_CHANGED, this);\n\t}\n\n\t/**\n\t * A mask limits rendering elements to the shape and position of the\n\t * given mask object — realized on the stencil half of the pass's\n\t * depth-stencil attachment, exactly like the GL backend: a write\n\t * phase increments the stencil under the mask shape (color writes\n\t * off), then the render phase only passes fragments where the stencil\n\t * equals the current mask level (or 0 when inverted).\n\t *\n\t * Entering the first mask level clears the stencil, which WebGPU can\n\t * only do at a pass boundary — the frame's pass is broken and\n\t * restarted with `stencilLoadOp: \"clear\"` (color preserved).\n\t * (Note: masks are not preserved through save/restore and need to be\n\t * manually cleared, same as the other backends.)\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse} [mask] - the shape defining the mask to be applied\n\t * @param {boolean} [invert=false] - either the given shape should define what is visible (default) or the opposite\n\t */\n\tsetMask(mask, invert = false) {\n\t\t// drain everything drawn before the mask changes\n\t\tthis.currentBatcher?.flush();\n\n\t\tif (this.maskLevel === 0) {\n\t\t\t// stencil clear = pass break (color survives via loadOp \"load\")\n\t\t\tif (this.renderPass !== null) {\n\t\t\t\tthis.renderPass.end();\n\t\t\t\tthis.renderPass = null;\n\t\t\t}\n\t\t\tthis.beginPass({ stencilLoadOp: \"clear\" });\n\t\t}\n\n\t\tthis.maskLevel++;\n\t\tif (this.maskLevel > 0xff) {\n\t\t\tthis.maskLevel = 0xff;\n\t\t\tif (this.maskDepthWarned !== true) {\n\t\t\t\tthis.maskDepthWarned = true;\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"melonJS: setMask nesting deeper than 255 — mask level clamped\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// write phase: every fragment of the mask shape increments the\n\t\t// stencil (color writes disabled by the pipeline variant)\n\t\tthis.stencilMode = \"write\";\n\t\tthis.fill(mask);\n\t\tthis.currentBatcher?.flush();\n\n\t\t// render phase: draw only where the stencil matches\n\t\tthis.stencilMode = \"test\";\n\t\tthis.maskVisibleRef = invert === true ? 0 : this.maskLevel;\n\t\tthis.ensurePass().setStencilReference(this.maskVisibleRef);\n\t}\n\n\t/**\n\t * disable the current mask\n\t * @see WebGPURenderer#setMask\n\t */\n\tclearMask() {\n\t\tif (this.maskLevel === 0) {\n\t\t\treturn;\n\t\t}\n\t\t// drain masked vertices while the stencil test still applies\n\t\tthis.currentBatcher?.flush();\n\t\tthis.maskLevel = 0;\n\t\tthis.stencilMode = \"none\";\n\t\tthis.maskVisibleRef = 0;\n\t}\n\n\t/**\n\t * Fill an arbitrary shape with the current gradient by clipping the\n\t * baked-gradient rect to the shape through the stencil — the GL\n\t * backend's #gradientMask re-expressed as pipeline stencil variants.\n\t *\n\t * Outside a mask: the stencil is cleared (a pass break), the shape's\n\t * pixels are stamped with reference 1 (\"tag\": always/replace, color\n\t * writes off), and the gradient rect draws under the \"test\" mode.\n\t * Inside a mask: the shape's VISIBLE pixels (stencil low 7 bits equal\n\t * to the mask's visible value — mask levels never use the high bit)\n\t * get a high-bit marker via \"mark\", the gradient clips to the marker,\n\t * then the marker is stripped and the mask's exact render test is\n\t * re-installed.\n\t * @ignore\n\t * @internal\n\t */\n\tgradientMask(drawShape, x, y, w, h) {\n\t\tconst grad = this.currentGradient;\n\t\tconst hasMask = this.maskLevel > 0;\n\t\t// the tag/untag phases re-enter the shape's public fill method —\n\t\t// null the gradient so they take the plain solid path\n\t\tthis.currentGradient = null;\n\n\t\tthis.currentBatcher?.flush();\n\n\t\tconst visibleRef = this.maskVisibleRef;\n\t\tlet markRef = 1;\n\n\t\tif (hasMask) {\n\t\t\tmarkRef = 0x80 | visibleRef;\n\t\t\tthis.stencilMode = \"mark\";\n\t\t\tthis.ensurePass().setStencilReference(markRef);\n\t\t} else {\n\t\t\t// stencil clear = pass break (color survives via loadOp \"load\")\n\t\t\tif (this.renderPass !== null) {\n\t\t\t\tthis.renderPass.end();\n\t\t\t\tthis.renderPass = null;\n\t\t\t}\n\t\t\tthis.beginPass({ stencilLoadOp: \"clear\" });\n\t\t\tthis.stencilMode = \"tag\";\n\t\t\tthis.ensurePass().setStencilReference(markRef);\n\t\t}\n\t\tdrawShape();\n\t\tthis.currentBatcher?.flush();\n\n\t\t// clip the gradient fill (a baked-texture quad) to the marked pixels\n\t\tthis.stencilMode = \"test\";\n\t\tthis.ensurePass().setStencilReference(markRef);\n\t\tthis.currentGradient = grad;\n\t\tthis.fillRect(x, y, w, h);\n\t\tthis.currentBatcher?.flush();\n\n\t\tif (hasMask) {\n\t\t\t// clear the marker: replace writes the reference — a no-op on\n\t\t\t// unmarked visible pixels, and it strips the high bit from the\n\t\t\t// marked ones\n\t\t\tthis.currentGradient = null;\n\t\t\tthis.stencilMode = \"mark\";\n\t\t\tthis.ensurePass().setStencilReference(visibleRef);\n\t\t\tdrawShape();\n\t\t\tthis.currentBatcher?.flush();\n\t\t\tthis.currentGradient = grad;\n\t\t\t// re-install the exact render test setMask had established\n\t\t\tthis.stencilMode = \"test\";\n\t\t} else {\n\t\t\tthis.stencilMode = \"none\";\n\t\t}\n\t\tthis.ensurePass().setStencilReference(this.maskVisibleRef);\n\t}\n\n\t/**\n\t * Stroke an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius - arc radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeArc(x, y, radius, start, end, antiClockwise = false, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillArc(x, y, radius, start, end, antiClockwise);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.arc(x, y, radius, start, end, antiClockwise);\n\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t}\n\n\t/**\n\t * Fill an arc at the specified coordinates with given radius, start and end points\n\t * @param {number} x - arc center point x-axis\n\t * @param {number} y - arc center point y-axis\n\t * @param {number} radius - arc radius\n\t * @param {number} start - start angle in radians\n\t * @param {number} end - end angle in radians\n\t * @param {boolean} [antiClockwise=false] - draw arc anti-clockwise\n\t */\n\tfillArc(x, y, radius, start, end, antiClockwise = false) {\n\t\tif (this.currentGradient) {\n\t\t\tthis.gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillArc(x, y, radius, start, end, antiClockwise);\n\t\t\t\t},\n\t\t\t\tx - radius,\n\t\t\t\ty - radius,\n\t\t\t\tradius * 2,\n\t\t\t\tradius * 2,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tlet diff = Math.abs(end - start);\n\t\tif (antiClockwise) {\n\t\t\tdiff = Math.PI * 2 - diff;\n\t\t}\n\t\tconst segments = Math.max(\n\t\t\t4,\n\t\t\tMath.round((diff * radius) / this.path2D.arcResolution),\n\t\t);\n\t\tconst startAngle = antiClockwise ? end : start;\n\t\tthis.currentBatcher.drawVertices(\n\t\t\t\"triangle-list\",\n\t\t\tgenerateTriangleFan(\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tradius,\n\t\t\t\tradius,\n\t\t\t\tstartAngle,\n\t\t\t\tstartAngle + diff,\n\t\t\t\tsegments,\n\t\t\t),\n\t\t);\n\t}\n\n\t/**\n\t * Stroke an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeEllipse(x, y, w, h, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillEllipse(x, y, w, h);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.ellipse(x, y, w, h, 0, 0, 360);\n\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t}\n\n\t/**\n\t * Fill an ellipse at the specified coordinates with given radius\n\t * @param {number} x - ellipse center point x-axis\n\t * @param {number} y - ellipse center point y-axis\n\t * @param {number} w - horizontal radius of the ellipse\n\t * @param {number} h - vertical radius of the ellipse\n\t */\n\tfillEllipse(x, y, w, h) {\n\t\tif (this.currentGradient) {\n\t\t\tthis.gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillEllipse(x, y, w, h);\n\t\t\t\t},\n\t\t\t\tx - w,\n\t\t\t\ty - h,\n\t\t\t\tw * 2,\n\t\t\t\th * 2,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst segments = Math.max(\n\t\t\t8,\n\t\t\tMath.round((Math.PI * (w + h)) / this.path2D.arcResolution),\n\t\t);\n\t\tthis.currentBatcher.drawVertices(\n\t\t\t\"triangle-list\",\n\t\t\tgenerateTriangleFan(x, y, w, h, 0, Math.PI * 2, segments),\n\t\t);\n\t}\n\n\t/**\n\t * Stroke a line between the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tstrokeLine(startX, startY, endX, endY) {\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst dash = this.renderState.lineDash;\n\t\tif (dash.length > 0) {\n\t\t\tconst segments = dashSegments(startX, startY, endX, endY, dash);\n\t\t\tif (segments.length > 0) {\n\t\t\t\tthis.currentBatcher.drawVertices(\"line-list\", segments);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.path2D.beginPath();\n\t\t\tthis.path2D.moveTo(startX, startY);\n\t\t\tthis.path2D.lineTo(endX, endY);\n\t\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t\t}\n\t}\n\n\t/**\n\t * Fill a line between the given two points\n\t * @param {number} startX - the start x coordinate\n\t * @param {number} startY - the start y coordinate\n\t * @param {number} endX - the end x coordinate\n\t * @param {number} endY - the end y coordinate\n\t */\n\tfillLine(startX, startY, endX, endY) {\n\t\tthis.strokeLine(startX, startY, endX, endY);\n\t}\n\n\t/**\n\t * Stroke a Polygon on the screen with the current color\n\t * @param {Polygon} poly - the shape to draw\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokePolygon(poly, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillPolygon(poly);\n\t\t\treturn;\n\t\t}\n\t\tconst points = poly.points;\n\t\tconst len = points.length;\n\n\t\tthis.translate(poly.pos.x, poly.pos.y);\n\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tfor (let i = 0; i < len - 1; i++) {\n\t\t\tconst curPoint = points[i];\n\t\t\tconst nextPoint = points[i + 1];\n\t\t\tthis.path2D.moveTo(curPoint.x, curPoint.y);\n\t\t\tthis.path2D.lineTo(nextPoint.x, nextPoint.y);\n\t\t}\n\t\tthis.path2D.closePath();\n\t\tconst dash = this.renderState.lineDash;\n\t\tif (dash.length > 0) {\n\t\t\tconst pts = this.path2D.points;\n\t\t\tconst dashed = dashPath(pts, dash);\n\t\t\tif (dashed.length > 0) {\n\t\t\t\tthis.currentBatcher.drawVertices(\"line-list\", dashed);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t\t}\n\t\t// add round joins at vertices for thick lines\n\t\tif (this.lineWidth > 1) {\n\t\t\tconst radius = this.lineWidth / 2;\n\t\t\tconst joinPoints = [];\n\t\t\tfor (let i = 1; i < len; i++) {\n\t\t\t\tjoinPoints.push(points[i]);\n\t\t\t}\n\t\t\tconst lastPoint = points[len - 1];\n\t\t\tconst firstPoint = points[0];\n\t\t\tif (!lastPoint.equals(firstPoint)) {\n\t\t\t\tjoinPoints.push(firstPoint);\n\t\t\t}\n\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\t\"triangle-list\",\n\t\t\t\tgenerateJoinCircles(joinPoints, radius),\n\t\t\t);\n\t\t}\n\n\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t}\n\n\t/**\n\t * Fill a Polygon on the screen\n\t * @param {Polygon} poly - the shape to draw\n\t */\n\tfillPolygon(poly) {\n\t\tif (this.currentGradient) {\n\t\t\tconst bounds = poly.getBounds();\n\t\t\t// translate to polygon's local space so gradient coords match\n\t\t\tthis.translate(poly.pos.x, poly.pos.y);\n\t\t\tthis.gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\t// draw polygon vertices directly (already translated)\n\t\t\t\t\tthis.setBatcher(\"primitive\");\n\t\t\t\t\tconst indices = poly.getIndices();\n\t\t\t\t\tconst points = poly.points;\n\t\t\t\t\tconst verts = this.polyVerts;\n\t\t\t\t\tconst len = indices.length;\n\t\t\t\t\twhile (verts.length < len) {\n\t\t\t\t\t\tverts.push({ x: 0, y: 0 });\n\t\t\t\t\t}\n\t\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\t\tconst src = points[indices[i]];\n\t\t\t\t\t\tverts[i].x = src.x;\n\t\t\t\t\t\tverts[i].y = src.y;\n\t\t\t\t\t}\n\t\t\t\t\tthis.currentBatcher.drawVertices(\"triangle-list\", verts, len);\n\t\t\t\t},\n\t\t\t\t// use local bounds (subtract pos since getBounds includes it)\n\t\t\t\tbounds.x - poly.pos.x,\n\t\t\t\tbounds.y - poly.pos.y,\n\t\t\t\tbounds.width,\n\t\t\t\tbounds.height,\n\t\t\t);\n\t\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.translate(poly.pos.x, poly.pos.y);\n\t\tconst indices = poly.getIndices();\n\t\tconst points = poly.points;\n\t\tconst verts = this.polyVerts;\n\t\tconst len = indices.length;\n\n\t\t// grow the scratch array if needed\n\t\twhile (verts.length < len) {\n\t\t\tverts.push({ x: 0, y: 0 });\n\t\t}\n\n\t\t// copy point coordinates so drawVertices won't mutate the polygon\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tconst src = points[indices[i]];\n\t\t\tverts[i].x = src.x;\n\t\t\tverts[i].y = src.y;\n\t\t}\n\n\t\tthis.currentBatcher.drawVertices(\"triangle-list\", verts, len);\n\t\tthis.translate(-poly.pos.x, -poly.pos.y);\n\t}\n\n\t/**\n\t * Draw a stroke rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point\n\t * @param {number} width - the rectangle's width\n\t * @param {number} height - the rectangle's height\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRect(x, y, width, height, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillRect(x, y, width, height);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.rect(x, y, width, height);\n\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t\t// add round joins at corners for thick lines\n\t\tif (this.lineWidth > 1) {\n\t\t\tconst radius = this.lineWidth / 2;\n\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\t\"triangle-list\",\n\t\t\t\tgenerateJoinCircles(\n\t\t\t\t\t[\n\t\t\t\t\t\t{ x, y },\n\t\t\t\t\t\t{ x: x + width, y },\n\t\t\t\t\t\t{ x: x + width, y: y + height },\n\t\t\t\t\t\t{ x, y: y + height },\n\t\t\t\t\t],\n\t\t\t\t\tradius,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Draw a filled rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rectangle starting point\n\t * @param {number} y - y axis of the coordinate for the rectangle starting point\n\t * @param {number} width - the rectangle's width\n\t * @param {number} height - the rectangle's height\n\t */\n\tfillRect(x, y, width, height) {\n\t\tif (this.currentGradient) {\n\t\t\t// toCanvas() bakes the gradient through the Canvas 2D API and\n\t\t\t// draws it as a textured quad — same path as the GL backend.\n\t\t\t// The bake is fixed-resolution: the returned width/height are\n\t\t\t// the SOURCE rect inside the shared canvas.\n\t\t\tconst baked = this.currentGradient.toCanvas(this, x, y, width, height);\n\t\t\tthis.drawImage(\n\t\t\t\tbaked.canvas,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tbaked.width,\n\t\t\t\tbaked.height,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\t// 2 triangles directly — avoids path2D + earcut overhead\n\t\tconst right = x + width;\n\t\tconst bottom = y + height;\n\t\tconst pts = this.rectTriangles;\n\t\tpts[0].x = x;\n\t\tpts[0].y = y;\n\t\tpts[1].x = right;\n\t\tpts[1].y = y;\n\t\tpts[2].x = x;\n\t\tpts[2].y = bottom;\n\t\tpts[3].x = right;\n\t\tpts[3].y = y;\n\t\tpts[4].x = right;\n\t\tpts[4].y = bottom;\n\t\tpts[5].x = x;\n\t\tpts[5].y = bottom;\n\t\tthis.currentBatcher.drawVertices(\"triangle-list\", pts);\n\t}\n\n\t/**\n\t * Stroke a rounded rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rounded rectangle starting point\n\t * @param {number} y - y axis of the coordinate for the rounded rectangle starting point\n\t * @param {number} width - the rounded rectangle's width\n\t * @param {number} height - the rounded rectangle's height\n\t * @param {number} radius - the rounded corner's radius\n\t * @param {boolean} [fill=false] - also fill the shape with the current color if true\n\t */\n\tstrokeRoundRect(x, y, width, height, radius, fill = false) {\n\t\tif (fill === true) {\n\t\t\tthis.fillRoundRect(x, y, width, height, radius);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tthis.path2D.beginPath();\n\t\tthis.path2D.roundRect(x, y, width, height, radius);\n\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t}\n\n\t/**\n\t * Draw a rounded filled rectangle at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the rounded rectangle starting point\n\t * @param {number} y - y axis of the coordinate for the rounded rectangle starting point\n\t * @param {number} width - the rounded rectangle's width\n\t * @param {number} height - the rounded rectangle's height\n\t * @param {number} radius - the rounded corner's radius\n\t */\n\tfillRoundRect(x, y, width, height, radius) {\n\t\tif (this.currentGradient) {\n\t\t\tthis.gradientMask(\n\t\t\t\t() => {\n\t\t\t\t\tthis.fillRoundRect(x, y, width, height, radius);\n\t\t\t\t},\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.setBatcher(\"primitive\");\n\t\tconst r = Math.min(radius, width / 2, height / 2);\n\t\tconst verts = [];\n\n\t\t// inner cross: 3 rects (6 triangles = 18 vertices)\n\t\tverts.push(\n\t\t\t{ x, y: y + r },\n\t\t\t{ x: x + width, y: y + r },\n\t\t\t{ x, y: y + height - r },\n\t\t\t{ x: x + width, y: y + r },\n\t\t\t{ x: x + width, y: y + height - r },\n\t\t\t{ x, y: y + height - r },\n\t\t);\n\t\tverts.push(\n\t\t\t{ x: x + r, y },\n\t\t\t{ x: x + width - r, y },\n\t\t\t{ x: x + r, y: y + r },\n\t\t\t{ x: x + width - r, y },\n\t\t\t{ x: x + width - r, y: y + r },\n\t\t\t{ x: x + r, y: y + r },\n\t\t);\n\t\tverts.push(\n\t\t\t{ x: x + r, y: y + height - r },\n\t\t\t{ x: x + width - r, y: y + height - r },\n\t\t\t{ x: x + r, y: y + height },\n\t\t\t{ x: x + width - r, y: y + height - r },\n\t\t\t{ x: x + width - r, y: y + height },\n\t\t\t{ x: x + r, y: y + height },\n\t\t);\n\n\t\t// 4 corner arcs as triangle fans\n\t\tconst cornerSegments = Math.max(\n\t\t\t4,\n\t\t\tMath.round((Math.PI * r) / 2 / this.path2D.arcResolution),\n\t\t);\n\t\tconst PI = Math.PI;\n\t\tverts.push(\n\t\t\t...generateTriangleFan(x + r, y + r, r, r, PI, PI * 1.5, cornerSegments),\n\t\t);\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + width - r,\n\t\t\t\ty + r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\tPI * 1.5,\n\t\t\t\tPI * 2,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t);\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + width - r,\n\t\t\t\ty + height - r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\t0,\n\t\t\t\tPI * 0.5,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t);\n\t\tverts.push(\n\t\t\t...generateTriangleFan(\n\t\t\t\tx + r,\n\t\t\t\ty + height - r,\n\t\t\t\tr,\n\t\t\t\tr,\n\t\t\t\tPI * 0.5,\n\t\t\t\tPI,\n\t\t\t\tcornerSegments,\n\t\t\t),\n\t\t);\n\n\t\tthis.currentBatcher.drawVertices(\"triangle-list\", verts);\n\t}\n\n\t/**\n\t * Stroke a Point at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the point\n\t * @param {number} y - y axis of the coordinate for the point\n\t */\n\tstrokePoint(x, y) {\n\t\tthis.strokeLine(x, y, x + 1, y + 1);\n\t}\n\n\t/**\n\t * Draw a point at the specified coordinates\n\t * @param {number} x - x axis of the coordinate for the point\n\t * @param {number} y - y axis of the coordinate for the point\n\t */\n\tfillPoint(x, y) {\n\t\tthis.strokePoint(x, y);\n\t}\n\n\t/**\n\t * starts a new path by emptying the list of sub-paths\n\t */\n\tbeginPath() {\n\t\tthis.path2D.beginPath();\n\t}\n\n\t/**\n\t * begins a new sub-path at the point specified by the given (x, y) coordinates\n\t * @param {number} x - the x axis of the point\n\t * @param {number} y - the y axis of the point\n\t */\n\tmoveTo(x, y) {\n\t\tthis.path2D.moveTo(x, y);\n\t}\n\n\t/**\n\t * adds a straight line to the current sub-path\n\t * @param {number} x - the x axis of the point\n\t * @param {number} y - the y axis of the point\n\t */\n\tlineTo(x, y) {\n\t\tthis.path2D.lineTo(x, y);\n\t}\n\n\t/**\n\t * Adds a quadratic Bezier curve to the current sub-path.\n\t * @param {number} cpx - the x-axis coordinate of the control point\n\t * @param {number} cpy - the y-axis coordinate of the control point\n\t * @param {number} x - the x-axis coordinate of the end point\n\t * @param {number} y - the y-axis coordinate of the end point\n\t */\n\tquadraticCurveTo(cpx, cpy, x, y) {\n\t\tthis.path2D.quadraticCurveTo(cpx, cpy, x, y);\n\t}\n\n\t/**\n\t * Adds a cubic Bezier curve to the current sub-path.\n\t * @param {number} cp1x - the x-axis coordinate of the first control point\n\t * @param {number} cp1y - the y-axis coordinate of the first control point\n\t * @param {number} cp2x - the x-axis coordinate of the second control point\n\t * @param {number} cp2y - the y-axis coordinate of the second control point\n\t * @param {number} x - the x-axis coordinate of the end point\n\t * @param {number} y - the y-axis coordinate of the end point\n\t */\n\tbezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {\n\t\tthis.path2D.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n\t}\n\n\t/**\n\t * Adds a circular arc to the current sub-path, using the given control points and radius.\n\t * @param {number} x1 - the x-axis coordinate of the first control point\n\t * @param {number} y1 - the y-axis coordinate of the first control point\n\t * @param {number} x2 - the x-axis coordinate of the second control point\n\t * @param {number} y2 - the y-axis coordinate of the second control point\n\t * @param {number} radius - the arc's radius; must be non-negative\n\t */\n\tarcTo(x1, y1, x2, y2, radius) {\n\t\tthis.path2D.arcTo(x1, y1, x2, y2, radius);\n\t}\n\n\t/**\n\t * creates a rectangular path whose starting point is at (x, y)\n\t * @param {number} x - the x axis of the coordinate for the rectangle starting point\n\t * @param {number} y - the y axis of the coordinate for the rectangle starting point\n\t * @param {number} width - the rectangle's width\n\t * @param {number} height - the rectangle's height\n\t */\n\trect(x, y, width, height) {\n\t\tthis.path2D.rect(x, y, width, height);\n\t}\n\n\t/**\n\t * adds a rounded rectangle to the current path\n\t * @param {number} x - the x axis of the coordinate for the rectangle starting point\n\t * @param {number} y - the y axis of the coordinate for the rectangle starting point\n\t * @param {number} width - the rectangle's width\n\t * @param {number} height - the rectangle's height\n\t * @param {number} radii - the corner radius\n\t */\n\troundRect(x, y, width, height, radii) {\n\t\tthis.path2D.roundRect(x, y, width, height, radii);\n\t}\n\n\t/**\n\t * add a straight line from the current point to the start of the current sub-path\n\t */\n\tclosePath() {\n\t\tthis.path2D.closePath();\n\t}\n\n\t/**\n\t * stroke the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to stroke\n\t * @param {boolean} [fill=false] - fill the shape with the current color if true\n\t */\n\tstroke(shape, fill) {\n\t\tif (typeof shape === \"undefined\") {\n\t\t\tthis.setBatcher(\"primitive\");\n\t\t\tif (fill === true) {\n\t\t\t\t// draw all triangles\n\t\t\t\tthis.currentBatcher.drawVertices(\n\t\t\t\t\t\"triangle-list\",\n\t\t\t\t\tthis.path2D.triangulatePath(),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconst dash = this.renderState.lineDash;\n\t\t\t\tif (dash.length > 0) {\n\t\t\t\t\tconst pts = this.path2D.points;\n\t\t\t\t\tconst dashed = dashPath(pts, dash);\n\t\t\t\t\tif (dashed.length > 0) {\n\t\t\t\t\t\tthis.currentBatcher.drawVertices(\"line-list\", dashed);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.currentBatcher.drawVertices(\"line-list\", this.path2D.points);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// dispatches to strokeRect/strokePolygon/etc. via the base class\n\t\t\tsuper.stroke(shape, fill);\n\t\t}\n\t}\n\n\t/**\n\t * fill the given shape or the current defined path\n\t * @param {Rect|RoundRect|Polygon|Line|Ellipse|Bounds} [shape] - a shape object to fill\n\t */\n\tfill(shape) {\n\t\tthis.stroke(shape, true);\n\t}\n\n\t/**\n\t * Enable/disable anti-aliasing — under WebGPU this only drives the\n\t * default sampler filter: textures stay resident and every\n\t * (texture, sampler) bind-group pairing is rebuilt on next draw.\n\t * @param {boolean} [enable=false] - whether to enable anti-aliasing\n\t */\n\tsetAntiAlias(enable = false) {\n\t\tsuper.setAntiAlias(enable);\n\t\tthis.currentBatcher?.flush();\n\t\tthis.textureStore?.invalidateBindGroups();\n\t\t// the lit tier caches combined color+normal bind groups and the\n\t\t// quad tier caches composed segment groups outside the store —\n\t\t// each embeds samplers resolved from the default filter\n\t\tthis.batchers.get(\"litQuad\")?.clearMaterialCache();\n\t\tthis.batchers.get(\"quad\")?.clearMaterialCache();\n\t}\n\n\t/**\n\t * Set the default texture filter at runtime, decoupled from\n\t * {@link WebGPURenderer#setAntiAlias} — same re-pairing mechanism.\n\t * @param {\"auto\"|\"nearest\"|\"linear\"} [mode=\"auto\"] - `\"auto\"` follows `antiAlias`\n\t */\n\tsetTextureFilter(mode = \"auto\") {\n\t\tsuper.setTextureFilter(mode);\n\t\tthis.currentBatcher?.flush();\n\t\tthis.textureStore?.invalidateBindGroups();\n\t\t// the lit tier caches combined color+normal bind groups and the\n\t\t// quad tier caches composed segment groups outside the store —\n\t\t// each embeds samplers resolved from the default filter\n\t\tthis.batchers.get(\"litQuad\")?.clearMaterialCache();\n\t\tthis.batchers.get(\"quad\")?.clearMaterialCache();\n\t}\n\n\t/**\n\t * rebuild every GPU-facing resource after a device loss — the WebGPU\n\t * analogue of the WebGL context-restore path\n\t * @ignore\n\t * @internal\n\t */\n\tasync restoreDevice() {\n\t\t// tear down everything tied to the dead device\n\t\tthis.abandonFrame();\n\t\tthis.textureStore?.destroy();\n\t\tthis.vertexArena?.destroy();\n\t\tthis.indexArena?.destroy();\n\t\tthis.uniformRing?.destroy();\n\t\tthis.pipelineCache?.clear();\n\t\t// device-scoped post-effect state: the shared capture, pool targets\n\t\t// and stub die with the device; effect GPU realizations self-heal\n\t\t// via the pipeline-cache epoch\n\t\tthis.captureTexture = undefined;\n\t\tthis._renderTargetPool?.destroy();\n\t\tthis._renderTargetPool = null;\n\t\tthis.effectUniformArena?.destroy();\n\t\tthis.effectUniformArena = null;\n\t\tthis.stubTexture = null;\n\t\tthis.stubTextureView = null;\n\t\tthis.currentRenderTarget = null;\n\t\t// tile-path lookup textures died with the device; the epoch check\n\t\t// in drawTileLayer rebuilds the renderer lazily\n\t\tthis.orthogonalTMXRenderer = undefined;\n\t\tthis.depthTexture = null;\n\t\tthis.msaaDepthTexture = null;\n\t\tthis.msaaColorTexture = null;\n\t\tthis.msaaColorView = null;\n\t\tthis.device = undefined;\n\t\tthis.adapter = undefined;\n\t\t// the replacement device may offer different compression families\n\t\tthis.supportedCompressedFormats = undefined;\n\t\t// renegotiate + rebuild (init also re-registers batcher layouts);\n\t\t// resident textures lazily re-upload because the cache still maps\n\t\t// sources to atlases while the unit assignments start fresh\n\t\tthis.cache.resetUnitAssignments();\n\t\tawait this.init();\n\t\t// re-init the batchers against the new device\n\t\tfor (const batcher of this.batchers.values()) {\n\t\t\tbatcher.init(this);\n\t\t}\n\t\temit(ONCONTEXT_RESTORED, this);\n\t}\n\n\t/**\n\t * Returns the WebGPU canvas context\n\t * @returns {GPUCanvasContext} the WebGPU canvas context\n\t */\n\tgetContext() {\n\t\treturn this.context;\n\t}\n\n\t/**\n\t * Reset the renderer state\n\t * @override\n\t */\n\treset() {\n\t\tthis.abandonFrame();\n\t\t// a reset can land mid-post-effect-pass — unwind the bracket state\n\t\tthis.effectPassDepth = 0;\n\t\tthis.customShader = undefined;\n\t\t// level transition: free the per-layer/per-tileset lookup textures\n\t\tthis.orthogonalTMXRenderer?.reset();\n\t\tsuper.reset();\n\t\t// re-init batchers when recovering from a device loss, plain reset\n\t\t// otherwise — the same split as the WebGL restore path. A reset\n\t\t// landing INSIDE the device renegotiation window (device gone,\n\t\t// replacement not granted yet) can only reset: init needs a device,\n\t\t// and restoreDevice re-inits every batcher once one exists\n\t\tfor (const batcher of this.batchers.values()) {\n\t\t\tif (this.isContextValid === false && typeof this.device !== \"undefined\") {\n\t\t\t\tbatcher.init(this);\n\t\t\t} else {\n\t\t\t\tbatcher.reset();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Release the GPU device, unconfigure the canvas context, and tear\n\t * down every GPU-facing cache. Called by {@link Application#destroy}.\n\t * @override\n\t */\n\tdestroy() {\n\t\t// the shared ground-shadow quads (#1515) hold retained GPU geometry\n\t\t// keyed off this renderer's batchers — released before those go\n\t\treleaseShadowQuads(this);\n\t\toff(GAME_RESET, this.onGameReset);\n\t\toff(CANVAS_ONRESIZE, this.onCanvasResize);\n\t\tthis.abandonFrame();\n\t\tif (typeof this.device !== \"undefined\") {\n\t\t\tfor (const batcher of this.batchers.values()) {\n\t\t\t\tbatcher.destroy();\n\t\t\t}\n\t\t\tthis.textureStore?.destroy();\n\t\t\tthis.vertexArena?.destroy();\n\t\t\tthis.indexArena?.destroy();\n\t\t\tthis.uniformRing?.destroy();\n\t\t\tthis.pipelineCache?.clear();\n\t\t\tthis.captureTexture?.destroy();\n\t\t\tthis.captureTexture = undefined;\n\t\t\tthis.orthogonalTMXRenderer = undefined;\n\t\t\tthis._renderTargetPool?.destroy();\n\t\t\tthis._renderTargetPool = null;\n\t\t\tthis.effectUniformArena?.destroy();\n\t\t\tthis.effectUniformArena = null;\n\t\t\tthis.stubTexture?.destroy();\n\t\t\tthis.stubTexture = null;\n\t\t\tthis.stubTextureView = null;\n\t\t\tthis.currentRenderTarget = null;\n\t\t\tthis.depthTexture?.destroy();\n\t\t\tthis.depthTexture = null;\n\t\t\tthis.msaaDepthTexture?.destroy();\n\t\t\tthis.msaaDepthTexture = null;\n\t\t\tthis.msaaColorTexture?.destroy();\n\t\t\tthis.msaaColorTexture = null;\n\t\t\tthis.msaaColorView = null;\n\t\t\tthis.context.unconfigure();\n\t\t\tthis.device.destroy();\n\t\t\tthis.device = undefined;\n\t\t\tthis.adapter = undefined;\n\t\t}\n\t\tthis.isContextValid = false;\n\t}\n}\n","export const ScaleMethods = {\n\t/**\n\t * Manual; no scaling is performed\n\t * <img src=\"images/scale-fit.png\"/>\n\t */\n\tManual: \"manual\",\n\n\t/**\n\t * Letterboxed; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-fit.png\"/>\n\t */\n\tFit: \"fit\",\n\n\t/**\n\t * Canvas is resized to fit minimum design resolution; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-fill-min.png\"/>\n\t */\n\tFillMin: \"fill-min\",\n\n\t/**\n\t * Canvas is resized to fit maximum design resolution; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-fill-max.png\"/>\n\t */\n\tFillMax: \"fill-max\",\n\n\t/**\n\t * Canvas width & height is resized to fit; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-flex.png\"/>\n\t */\n\tFlex: \"flex\",\n\n\t/**\n\t * Canvas width is resized to fit; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-flex-width.png\"/>\n\t */\n\tFlexWidth: \"flex-width\",\n\n\t/**\n\t * Canvas height is resized to fit; content is scaled to design aspect ratio\n\t * <img src=\"images/scale-flex-height.png\"/>\n\t */\n\tFlexHeight: \"flex-height\",\n\n\t/**\n\t * Canvas is resized to fit; content is scaled to screen aspect ratio\n\t * <img src=\"images/scale-stretch.png\"/>\n\t */\n\tStretch: \"stretch\",\n} as const;\n\nexport type ScaleMethod = (typeof ScaleMethods)[keyof typeof ScaleMethods];\n","import { AUTO } from \"../const\";\nimport { ScaleMethods } from \"./scaleMethods\";\nimport { ApplicationSettings } from \"./settings\";\n\nexport const defaultApplicationSettings = {\n\trenderer: AUTO,\n\tscale: 1.0,\n\tscaleMethod: ScaleMethods.Manual,\n\tpowerPreference: \"default\",\n\ttransparent: false,\n\tantiAlias: false,\n\ttextureFilter: \"auto\",\n\tmaxTextures: \"auto\",\n\tcastGroundShadow: true,\n\tconsoleHeader: true,\n\tblendMode: \"normal\",\n\tphysic: \"builtin\",\n\tgpuTilemap: true,\n\tfailIfMajorPerformanceCaveat: true,\n\thighPrecisionShader: true,\n\tsubPixel: false,\n\tverbose: false,\n\tbackgroundColor: \"#000000\",\n} satisfies Partial<ApplicationSettings>;\n","import * as device from \"../system/device\";\nimport type Application from \"./application.ts\";\n\n/**\n * display information\n * @param app - the game application instance calling this function\n */\nexport function consoleHeader(app: Application): void {\n\tconst renderType = app.renderer.type;\n\tconst gpu_renderer =\n\t\ttypeof app.renderer.GPURenderer === \"string\"\n\t\t\t? ` (${app.renderer.GPURenderer})`\n\t\t\t: \"\";\n\tconst audioType = device.hasWebAudio ? \"Web Audio\" : \"HTML5 Audio\";\n\n\t// output video information in the console\n\tconsole.log(\n\t\t`${renderType} renderer${gpu_renderer} | ${audioType} | ` +\n\t\t\t`pixel ratio ${device.devicePixelRatio} | ${\n\t\t\t\tdevice.platform.nodeJS\n\t\t\t\t\t? \"node.js\"\n\t\t\t\t\t: device.platform.isMobile\n\t\t\t\t\t\t? \"mobile\"\n\t\t\t\t\t\t: \"desktop\"\n\t\t\t} | ${device.getScreenOrientation()} | ${device.language}`,\n\t);\n\n\tconsole.log(\n\t\t`resolution: ` +\n\t\t\t`requested ${(app.settings as any).width}x${\n\t\t\t\t(app.settings as any).height\n\t\t\t}, got ${app.renderer.width}x${app.renderer.height}`,\n\t);\n}\n","import * as device from \"./../system/device\";\nimport type Application from \"./application.ts\";\n\n/**\n * scale the \"displayed\" canvas by the given scalar.\n * this will modify the size of canvas element directly.\n * Only use this if you are not using the automatic scaling feature.\n * @param game - the game application instance triggering the resize\n * @param x - x scaling multiplier\n * @param y - y scaling multiplier\n */\nfunction scale(game: Application, x: number, y: number): void {\n\tconst renderer = game.renderer;\n\tconst canvas = renderer.getCanvas();\n\tconst settings = renderer.settings as any;\n\tconst pixelRatio = device.devicePixelRatio;\n\n\tconst w = (settings.zoomX = canvas.width * x * pixelRatio);\n\tconst h = (settings.zoomY = canvas.height * y * pixelRatio);\n\n\t// update the global scale variable\n\trenderer.scaleRatio.set(x * pixelRatio, y * pixelRatio);\n\n\t// adjust CSS style based on device pixel ratio\n\tcanvas.style.width = `${w / pixelRatio}px`;\n\tcanvas.style.height = `${h / pixelRatio}px`;\n\n\t// if anti-alias and blend mode were reset (e.g. Canvas mode)\n\trenderer.setAntiAlias(settings.antiAlias);\n\trenderer.setBlendMode(settings.blendMode);\n\n\t// force repaint\n\tgame.repaint();\n}\n\n/**\n * callback for window resize event\n * @param game - the game application instance triggering the resize\n */\nexport function onresize(game: Application): void {\n\tconst renderer = game.renderer;\n\tconst settings = renderer.settings as any;\n\tlet scaleX = settings.scale,\n\t\tscaleY = settings.scale;\n\tlet nodeBounds;\n\n\tif (settings.autoScale) {\n\t\t// set max the canvas max size if CSS values are defined\n\t\tlet canvasMaxWidth = Infinity;\n\t\tlet canvasMaxHeight = Infinity;\n\n\t\tif (globalThis.getComputedStyle) {\n\t\t\tconst style = globalThis.getComputedStyle(renderer.getCanvas(), null);\n\t\t\tcanvasMaxWidth = parseInt(style.maxWidth, 10) || Infinity;\n\t\t\tcanvasMaxHeight = parseInt(style.maxHeight, 10) || Infinity;\n\t\t}\n\n\t\tlet measuredElement;\n\t\tif (typeof game.settings.scaleTarget !== \"undefined\") {\n\t\t\t// get the bounds of the given scale target\n\t\t\tmeasuredElement = game.settings.scaleTarget;\n\t\t\tnodeBounds = device.getElementBounds(measuredElement);\n\t\t} else {\n\t\t\t// get the maximum canvas size within the parent div containing the canvas container\n\t\t\tmeasuredElement = device.getParentElement(game.getParentElement());\n\t\t\tnodeBounds = device.getElementBounds(measuredElement);\n\t\t}\n\n\t\tlet availWidth = nodeBounds.width;\n\t\tlet availHeight = nodeBounds.height;\n\t\t// The space available to a child is the measured element's CONTENT\n\t\t// box; `getBoundingClientRect` returns the border box. On an\n\t\t// auto-sized container the difference (padding + border) gets baked\n\t\t// into the canvas size and re-measured on the next pass — growing\n\t\t// the canvas by that amount on every resize event (#1231). The\n\t\t// window-fallback path (string target / document.body) has no\n\t\t// padding concept and is left untouched.\n\t\tif (\n\t\t\ttypeof measuredElement === \"object\" &&\n\t\t\ttypeof globalThis.getComputedStyle === \"function\"\n\t\t) {\n\t\t\tconst style = globalThis.getComputedStyle(measuredElement);\n\t\t\tavailWidth -=\n\t\t\t\t(parseFloat(style.paddingLeft) || 0) +\n\t\t\t\t(parseFloat(style.paddingRight) || 0) +\n\t\t\t\t(parseFloat(style.borderLeftWidth) || 0) +\n\t\t\t\t(parseFloat(style.borderRightWidth) || 0);\n\t\t\tavailHeight -=\n\t\t\t\t(parseFloat(style.paddingTop) || 0) +\n\t\t\t\t(parseFloat(style.paddingBottom) || 0) +\n\t\t\t\t(parseFloat(style.borderTopWidth) || 0) +\n\t\t\t\t(parseFloat(style.borderBottomWidth) || 0);\n\t\t}\n\n\t\tconst _max_width = Math.min(canvasMaxWidth, Math.max(0, availWidth));\n\t\tconst _max_height = Math.min(canvasMaxHeight, Math.max(0, availHeight));\n\n\t\t// calculate final canvas width & height\n\t\tconst screenRatio = _max_width / _max_height;\n\n\t\tif (\n\t\t\t(settings.scaleMethod === \"fill-min\" &&\n\t\t\t\tscreenRatio > renderer.designRatio) ||\n\t\t\t(settings.scaleMethod === \"fill-max\" &&\n\t\t\t\tscreenRatio < renderer.designRatio) ||\n\t\t\tsettings.scaleMethod === \"flex-width\"\n\t\t) {\n\t\t\t// resize the display canvas to fill the parent container\n\t\t\tconst sWidth = Math.min(canvasMaxWidth, settings.height * screenRatio);\n\t\t\tscaleX = scaleY = _max_width / sWidth;\n\t\t\trenderer.resize(Math.floor(sWidth), settings.height);\n\t\t} else if (\n\t\t\t(settings.scaleMethod === \"fill-min\" &&\n\t\t\t\tscreenRatio < renderer.designRatio) ||\n\t\t\t(settings.scaleMethod === \"fill-max\" &&\n\t\t\t\tscreenRatio > renderer.designRatio) ||\n\t\t\tsettings.scaleMethod === \"flex-height\"\n\t\t) {\n\t\t\t// resize the display canvas to fill the parent container\n\t\t\tconst sHeight = Math.min(\n\t\t\t\tcanvasMaxHeight,\n\t\t\t\tsettings.width * (_max_height / _max_width),\n\t\t\t);\n\t\t\tscaleX = scaleY = _max_height / sHeight;\n\t\t\trenderer.resize(settings.width, Math.floor(sHeight));\n\t\t} else if (settings.scaleMethod === \"flex\") {\n\t\t\t// resize the display canvas to fill the parent container\n\t\t\trenderer.resize(Math.floor(_max_width), Math.floor(_max_height));\n\t\t} else if (settings.scaleMethod === \"stretch\") {\n\t\t\t// scale the display canvas to fit with the parent container\n\t\t\tscaleX = _max_width / settings.width;\n\t\t\tscaleY = _max_height / settings.height;\n\t\t} else {\n\t\t\t// scale the display canvas to fit the parent container\n\t\t\t// make sure we maintain the original aspect ratio\n\t\t\tif (screenRatio < renderer.designRatio) {\n\t\t\t\tscaleX = scaleY = _max_width / settings.width;\n\t\t\t} else {\n\t\t\t\tscaleX = scaleY = _max_height / settings.height;\n\t\t\t}\n\t\t}\n\t}\n\t// adjust scaling ratio\n\tscale(game, scaleX, scaleY);\n}\n","import type Camera2d from \"./../camera/camera2d.ts\";\nimport { AUTO, CANVAS, WEBGL, WEBGPU } from \"../const.ts\";\nimport type { PhysicsAdapter } from \"../physics/adapter.ts\";\nimport World from \"../physics/world.js\";\nimport state from \"../state/state.ts\";\nimport { boot, initialized } from \"../system/bootstrap.ts\";\nimport * as device from \"../system/device.js\";\nimport {\n\tBLUR,\n\temit,\n\tFOCUS,\n\tGAME_AFTER_DRAW,\n\tGAME_AFTER_UPDATE,\n\tGAME_BEFORE_DRAW,\n\tGAME_BEFORE_UPDATE,\n\tGAME_INIT,\n\tGAME_RESET,\n\tGAME_UPDATE,\n\toff,\n\ton,\n\tSTAGE_RESET,\n\tSTATE_CHANGE,\n\tSTATE_RESTART,\n\tSTATE_RESUME,\n\tTICK,\n\tVIDEO_INIT,\n\tWINDOW_ONORIENTATION_CHANGE,\n\tWINDOW_ONRESIZE,\n\tWINDOW_ONSCROLL,\n} from \"../system/event.ts\";\nimport timer from \"../system/timer.ts\";\nimport { getUriFragment } from \"../utils/utils.ts\";\nimport CanvasRenderer from \"../video/canvas/canvas_renderer.js\";\nimport type Renderer from \"./../video/renderer.js\";\nimport { autoDetectRenderer } from \"../video/utils/autodetect.js\";\nimport WebGLRenderer from \"../video/webgl/webgl_renderer.js\";\nimport WebGPURenderer from \"../video/webgpu/webgpu_renderer.js\";\nimport { defaultApplicationSettings } from \"./defaultApplicationSettings.ts\";\nimport { consoleHeader } from \"./header.ts\";\nimport { onresize } from \"./resize.ts\";\nimport { ScaleMethods } from \"./scaleMethods.ts\";\nimport type {\n\tApplicationSettings,\n\tResolvedApplicationSettings,\n} from \"./settings.ts\";\n\n// vendor-prefixed fullscreen entry points still ship on older WebKit /\n// Gecko / IE-derived engines; spelled out here so requestFullscreen()\n// works on any browser whose `hasFullscreenSupport` flag was set via a\n// prefix probe rather than the unprefixed standard.\ntype ElementWithLegacyFullscreen = Element & {\n\tmozRequestFullScreen?: () => void;\n\twebkitRequestFullscreen?: () => void;\n\tmsRequestFullscreen?: () => void;\n};\n// `document.exitFullscreen()` is the modern entry but older WebKit /\n// Gecko / IE-derived browsers shipped vendor-prefixed variants. Same\n// rationale as `ElementWithLegacyFullscreen`: kept here as a local\n// intersection so `app.exitFullscreen()` no-ops cleanly on every\n// browser whose `hasFullscreenSupport` was set via a prefix probe.\n// Note: Mozilla uses `mozCancelFullScreen` (not `mozExitFullScreen`).\ntype DocumentWithLegacyExitFullscreen = Document & {\n\twebkitExitFullscreen?: () => Promise<void> | void;\n\tmozCancelFullScreen?: () => Promise<void> | void;\n\tmsExitFullscreen?: () => Promise<void> | void;\n};\n\n/**\n * Resolve the user-supplied `physic` setting into the (optional) adapter\n * to pass into the {@link World} constructor plus the legacy \"builtin\"\n * / \"none\" string the rest of the engine still reads via `world.physic`.\n *\n * Accepted shapes:\n *  - `\"builtin\"` / `undefined` → World constructs its own BuiltinAdapter\n *  - `\"none\"` → World constructs its own BuiltinAdapter that we never `step()`\n *  - a `PhysicsAdapter` instance → forwarded to the World constructor\n *  - `{ adapter: PhysicsAdapter }` → explicit form, room for future fields\n *\n * \"none\" still constructs an adapter so any code touching `world.adapter`\n * keeps working; the simulation is frozen because `World.step()`\n * short-circuits on `world.physic === \"none\"`.\n *\n * We deliberately don't import `BuiltinAdapter` here — letting `World`\n * apply its own default avoids a circular import via\n * `physics/collision.js`, which imports the `game` reference back from\n * this module.\n * @ignore\n */\nfunction resolvePhysicSetting(physic: ApplicationSettings[\"physic\"]): {\n\tadapter: PhysicsAdapter | undefined;\n\tphysicLabel: string;\n} {\n\tif (physic === \"none\") {\n\t\treturn { adapter: undefined, physicLabel: \"none\" };\n\t}\n\tif (physic === undefined || physic === \"builtin\") {\n\t\treturn { adapter: undefined, physicLabel: \"builtin\" };\n\t}\n\t// instance or { adapter } object — extract and pass through. The\n\t// adapter's `physicLabel` becomes `world.physic` so user code can\n\t// branch on `world.physic === \"matter\"` (etc.) without importing the\n\t// concrete adapter class. Falls back to \"builtin\" for adapters\n\t// predating the `physicLabel` field.\n\tconst adapter =\n\t\ttypeof physic === \"object\" && \"adapter\" in physic ? physic.adapter : physic;\n\treturn { adapter, physicLabel: adapter?.physicLabel ?? \"builtin\" };\n}\n\n/**\n * The Application class is the main entry point for creating a melonJS game.\n * The constructor resolves the given settings, creates the game world and\n * registers DOM event listeners (resize, orientation, scroll); you **MUST**\n * then call (and await) {@link Application#init init}, which builds the\n * renderer, appends the canvas to the parent element, and starts the game\n * loop — without it the application has no renderer and displays nothing.\n *\n * The Application instance provides access to the core game systems:\n * - {@link Application#renderer renderer} — the active Canvas, WebGL or (experimental) WebGPU renderer\n * - {@link Application#world world} — the root container for all game objects\n * - {@link Application#viewport viewport} — the default camera / viewport\n *\n * The app instance is automatically passed to {@link Stage#onResetEvent} and\n * {@link Stage#onDestroyEvent}, and is accessible from any renderable via\n * {@link Renderable#parentApp parentApp}.\n * @category Application\n * @example\n * // create a new melonJS Application\n * const app = new Application(800, 600, {\n *     parent: \"screen\",\n *     scaleMethod: \"flex-width\",\n *     renderer: 2, // AUTO\n * });\n *\n * // build the renderer (mandatory — only suspends for WebGPU)\n * await app.init();\n *\n * // add objects to the world\n * app.world.addChild(new Sprite(0, 0, { image: \"player\" }));\n *\n * // access the viewport\n * app.viewport.follow(player, app.viewport.AXIS.BOTH);\n */\nexport default class Application {\n\t/**\n\t * the parent HTML element holding the main canvas of this application\n\t */\n\tparentElement: HTMLElement;\n\n\t/**\n\t * a reference to the active Canvas, WebGL or (experimental) WebGPU renderer\n\t */\n\trenderer!: Renderer;\n\n\t/**\n\t * the active stage \"default\" camera\n\t */\n\tviewport!: Camera2d;\n\n\t/**\n\t * a reference to the game world, <br>\n\t * a world is a virtual environment containing all the game objects\n\t */\n\tworld: World;\n\n\t/**\n\t * when true, all objects will be added under the root world container.<br>\n\t * When false, a `me.Container` object will be created for each corresponding groups\n\t * @default true\n\t */\n\tmergeGroup: boolean;\n\n\t/**\n\t * Last time the game update loop was executed. <br>\n\t * Use this value to implement frame prediction in drawing events,\n\t * for creating smooth motion while running game update logic at\n\t * a lower fps.\n\t */\n\tlastUpdate: DOMHighResTimeStamp;\n\n\t/**\n\t * true when this app instance has been initialized\n\t * @default false\n\t */\n\tisInitialized: boolean;\n\n\t/**\n\t * the given settings used when creating this application\n\t */\n\tsettings: ResolvedApplicationSettings;\n\n\t/**\n\t * Specify whether to pause this app when losing focus\n\t * @default true\n\t * @example\n\t *  // keep the default game instance running even when losing focus\n\t *  app.pauseOnBlur = false;\n\t */\n\tpauseOnBlur: boolean;\n\n\t/**\n\t * Specify whether to unpause this app when gaining back focus\n\t * @default true\n\t */\n\tresumeOnFocus: boolean;\n\n\t/**\n\t * Specify whether to stop this app when losing focus\n\t * @default false\n\t */\n\tstopOnBlur: boolean;\n\n\t// to know when we have to refresh the display\n\tisDirty: boolean;\n\n\t// always refresh the display when updatesPerSecond are lower than fps\n\tisAlwaysDirty: boolean;\n\n\t// frame counter for frameSkipping\n\tframeCounter: number;\n\tframeRate: number;\n\n\t// time accumulation for multiple update calls\n\taccumulator: number;\n\taccumulatorMax: number;\n\taccumulatorUpdateDelta: number;\n\n\t// min update step size\n\tstepSize: number;\n\n\t// DOM event handlers (stored for cleanup in destroy)\n\tprivate _onResize?: (e: Event) => void;\n\tprivate _onOrientationChange?: (e: Event) => void;\n\tprivate _onScroll?: (e: Event) => void;\n\t// melonJS-event resize subscription (stored for cleanup in destroy)\n\tprivate _doResize?: () => void;\n\t// the parent-element observer installed by init() (disconnected in destroy)\n\tprivate _resizeObserver: MutationObserver | undefined;\n\t// set by destroy(); a destroyed Application is terminal — init() refuses\n\t// to run (or, if already in flight, aborts) instead of resurrecting it\n\tprivate _destroyed = false;\n\t/**\n\t * Simulated time advanced by one logic step, in ms — what `world.update()`\n\t * receives. Fixed at `1000 / world.fps` unless `timer.interpolation` is on,\n\t * in which case it follows the real frame delta.\n\t *\n\t * Not to be confused with {@link Application#lastUpdateDelta}, which is how\n\t * long that step actually took to compute.\n\t */\n\tupdateDelta: number;\n\tlastUpdateStart: number | null;\n\n\t/**\n\t * Measured wall-clock cost of the most recent logic step, in ms\n\t * (`performance.now()` taken either side of the world/stage update).\n\t *\n\t * Used by the fixed-timestep loop to avoid a spiral of death: the\n\t * accumulator drains by at least this much, so a scene too heavy to\n\t * simulate in real time slows down instead of locking up.\n\t *\n\t * Only the *last* step of a frame is recorded, so a frame that ran several\n\t * catch-up steps reports less than its total update cost.\n\t * Renamed from `updateAverageDelta` in 20.0.0.\n\t * @since 20.0.0\n\t */\n\tlastUpdateDelta: number;\n\n\t/**\n\t * @deprecated since 20.0.0 — renamed to {@link Application#lastUpdateDelta}.\n\t * The old name claimed an average that has not existed since 2015, when the\n\t * exponential smoothing behind it was removed a day after being added.\n\t */\n\tget updateAverageDelta() {\n\t\treturn this.lastUpdateDelta;\n\t}\n\n\tset updateAverageDelta(value: number) {\n\t\tthis.lastUpdateDelta = value;\n\t}\n\n\t/**\n\t * Create a new melonJS Application.\n\t * Constructing the instance is only the first half of starting a game:\n\t * you **MUST** then call (and await) {@link Application#init init} to\n\t * build the renderer — an Application on which `init()` has not resolved\n\t * has no `renderer`, no canvas, and cannot render anything.\n\t * @param width - The width of the canvas viewport\n\t * @param height - The height of the canvas viewport\n\t * @param options - The optional parameters for the application and default renderer\n\t * @example\n\t * const app = new Application(1024, 768, {\n\t *     parent: \"game-container\",\n\t *     scale: \"auto\",\n\t *     scaleMethod: \"fit\",\n\t *     renderer: 2, // AUTO\n\t * });\n\t * await app.init();\n\t */\n\tconstructor(\n\t\twidth: number,\n\t\theight: number,\n\t\toptions: Partial<ApplicationSettings> = {},\n\t) {\n\t\tthis.mergeGroup = true;\n\t\tthis.lastUpdate = 0;\n\t\tthis.isInitialized = false;\n\t\tthis.pauseOnBlur = true;\n\t\tthis.resumeOnFocus = true;\n\t\tthis.stopOnBlur = false;\n\t\tthis.isDirty = true;\n\t\tthis.isAlwaysDirty = false;\n\t\tthis.frameCounter = 0;\n\t\tthis.frameRate = 1;\n\t\tthis.accumulator = 0.0;\n\t\tthis.accumulatorMax = 0.0;\n\t\tthis.accumulatorUpdateDelta = 0;\n\t\tthis.stepSize = 1000 / 60;\n\t\tthis.updateDelta = 0;\n\t\tthis.lastUpdateStart = null;\n\t\tthis.lastUpdateDelta = 0;\n\n\t\t// Everything renderer-independent is built here — resolved settings,\n\t\t// the parent element, the DOM event bridging, the physic world — so a\n\t\t// constructed Application already owns its world and its wiring.\n\t\t// `init()` only adds the renderer and what depends on it (canvas,\n\t\t// resize layout, console banner), which is what allows the renderer\n\t\t// to be acquired asynchronously.\n\n\t\t// ensure the engine is bootstrapped\n\t\tif (!initialized) {\n\t\t\tboot();\n\t\t}\n\n\t\tconst merged = {\n\t\t\t...defaultApplicationSettings,\n\t\t\t...(options || {}),\n\t\t};\n\n\t\tconst autoScale =\n\t\t\tmerged.scale === \"auto\" ||\n\t\t\t/^(fill-(min|max)|fit|flex(-(width|height))?|stretch)$/.test(\n\t\t\t\tmerged.scaleMethod,\n\t\t\t);\n\n\t\tconst settings = {\n\t\t\t...merged,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tautoScale,\n\t\t\tscale: autoScale ? 1.0 : +merged.scale || 1.0,\n\t\t\tzoomX: 0,\n\t\t\tzoomY: 0,\n\t\t\tscaleMethod: /^(fill-(min|max)|fit|flex(-(width|height))?|stretch)$/.test(\n\t\t\t\tmerged.scaleMethod,\n\t\t\t)\n\t\t\t\t? merged.scaleMethod\n\t\t\t\t: ScaleMethods.Fit,\n\t\t} as ResolvedApplicationSettings;\n\n\t\t// override renderer settings if &webgl or &canvas is defined in the URL\n\t\t// (the WebGL renderer is WebGL 2 only since 20.0 — the legacy\n\t\t// `#webgl1` flag is gone, `#webgl`/`#webgl2` are synonyms)\n\t\tconst uriFragment = getUriFragment();\n\t\tif (uriFragment.webgl === true || uriFragment.webgl2 === true) {\n\t\t\tsettings.renderer = WEBGL;\n\t\t} else if (uriFragment.canvas === true) {\n\t\t\tsettings.renderer = CANVAS;\n\t\t} else if (uriFragment.webgpu === true) {\n\t\t\t// exactly like passing `renderer: video.WEBGPU`: requires the\n\t\t\t// backend rather than trusting AUTO's ladder to land on it\n\t\t\tsettings.renderer = WEBGPU;\n\t\t}\n\n\t\t// computed scaled size\n\t\tsettings.zoomX = width * settings.scale;\n\t\tsettings.zoomY = height * settings.scale;\n\n\t\tthis.settings = settings;\n\n\t\t// identify parent element and/or the html target for resizing\n\t\tthis.parentElement = device.getElement(settings.parent!);\n\t\tif (typeof settings.scaleTarget !== \"undefined\") {\n\t\t\tsettings.scaleTarget = device.getElement(settings.scaleTarget);\n\t\t}\n\n\t\t// set the CSS background color on the parent element to prevent\n\t\t// a white flash before the first render frame\n\t\tif (\n\t\t\tsettings.backgroundColor !== \"transparent\" &&\n\t\t\ttypeof globalThis.getComputedStyle === \"function\"\n\t\t) {\n\t\t\tconst computedBg = globalThis.getComputedStyle(\n\t\t\t\tthis.parentElement,\n\t\t\t).backgroundColor;\n\t\t\tif (\n\t\t\t\t!computedBg ||\n\t\t\t\tcomputedBg === \"rgba(0, 0, 0, 0)\" ||\n\t\t\t\tcomputedBg === \"transparent\"\n\t\t\t) {\n\t\t\t\tthis.parentElement.style.backgroundColor = settings.backgroundColor;\n\t\t\t}\n\t\t}\n\n\t\t// bridge DOM events to the melonJS event system\n\t\tthis._onResize = (e: Event) => {\n\t\t\temit(WINDOW_ONRESIZE, e);\n\t\t};\n\t\tthis._onOrientationChange = (e: Event) => {\n\t\t\temit(WINDOW_ONORIENTATION_CHANGE, e);\n\t\t};\n\t\tthis._onScroll = (e: Event) => {\n\t\t\temit(WINDOW_ONSCROLL, e);\n\t\t};\n\t\tglobalThis.addEventListener(\"resize\", this._onResize);\n\t\tglobalThis.addEventListener(\"orientationchange\", this._onOrientationChange);\n\t\tif (device.screenOrientation) {\n\t\t\tglobalThis.screen.orientation.onchange = this._onOrientationChange;\n\t\t}\n\t\tglobalThis.addEventListener(\"scroll\", this._onScroll);\n\n\t\t// react to resize/orientation changes. These subscriptions live from\n\t\t// construction on, but `onresize` lays out the canvas through\n\t\t// `this.renderer`, which does not exist until `init()` resolves —\n\t\t// hence the `isInitialized` guard.\n\t\tthis._doResize = () => {\n\t\t\tif (this.isInitialized) {\n\t\t\t\tonresize(this);\n\t\t\t}\n\t\t};\n\t\ton(WINDOW_ONRESIZE, this._doResize);\n\t\ton(WINDOW_ONORIENTATION_CHANGE, this._doResize);\n\n\t\t// Mobile browser hacks\n\t\tif (device.platform.isMobile) {\n\t\t\t// Prevent the webview from moving on a swipe\n\t\t\tdevice.enableSwipe(false);\n\t\t}\n\n\t\t// resolve the physic setting into (adapter, legacy-string) pair.\n\t\t// Accepts a string (\"builtin\"/\"none\" — built-in adapters that ship\n\t\t// in core), a {@link PhysicsAdapter} instance (e.g.\n\t\t// `new BuiltinAdapter({ gravity })` or\n\t\t// `new MatterAdapter()` from `@melonjs/matter-adapter`), or the\n\t\t// explicit form `{ adapter: PhysicsAdapter }`.\n\t\tconst { adapter, physicLabel } = resolvePhysicSetting(settings.physic);\n\n\t\t// create a new physic world wired to the resolved adapter\n\t\tthis.world = new World(0, 0, settings.width, settings.height, adapter);\n\n\t\t// set the reference to this application instance\n\t\tthis.world.app = this;\n\t\t// `world.physic` carries the active adapter's identifier\n\t\t// (`\"builtin\"`, `\"matter\"`, third-party label, or the reserved\n\t\t// `\"none\"` when physics is disabled). User code branches on it;\n\t\t// `World.step` short-circuits the simulation only when the value\n\t\t// is `\"none\"`.\n\t\tthis.world.physic = physicLabel;\n\t\tthis.world.gpuTilemap = settings.gpuTilemap;\n\n\t\t// If a custom cameraClass is specified, seed the world's sort\n\t\t// mode from the camera's `defaultSortOn` static. Camera2d\n\t\t// declares `\"z\"` (today's default — no behavior change for\n\t\t// existing games); Camera3d declares `\"depth\"`, which switches\n\t\t// the world to the camera-distance painter's sort required by\n\t\t// perspective. User code can still override `world.sortOn` after\n\t\t// this point; we only set the initial value.\n\t\tconst cameraClass = settings.cameraClass as\n\t\t\t| { defaultSortOn?: \"x\" | \"y\" | \"z\" | \"depth\" }\n\t\t\t| undefined;\n\t\tif (cameraClass?.defaultSortOn) {\n\t\t\tthis.world.sortOn = cameraClass.defaultSortOn;\n\t\t}\n\t}\n\n\t/**\n\t * Build the renderer and everything that depends on it — the canvas\n\t * (appended to the parent element), the initial resize layout, and the\n\t * console banner. Reads {@link Application#settings}, resolved by the\n\t * constructor; it takes no arguments of its own.\n\t *\n\t * **Calling and awaiting this is mandatory** — it is the second half of\n\t * every Application's start-up, not an optional step. It resolves\n\t * synchronously for the Canvas and WebGL backends, which acquire their\n\t * context without suspending — but WebGPU cannot, and an application\n\t * whose `init()` has not resolved has no `renderer`.\n\t * @throws {Error} if it fails to instantiate the requested renderer\n\t * (e.g. `renderer: video.WEBGL` on a device with no WebGL 2 support)\n\t * @returns resolves once the application is ready to use\n\t * @example\n\t * const app = new Application(640, 480, { parent: \"screen\" });\n\t * await app.init();\n\t */\n\tasync init(): Promise<void> {\n\t\tif (this._destroyed) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Application: this instance was destroyed — construct a new \" +\n\t\t\t\t\t\"Application instead of re-initializing it\",\n\t\t\t);\n\t\t}\n\t\tif (this.isInitialized) {\n\t\t\t// a repeated call would append a second canvas and abandon the\n\t\t\t// live renderer — refuse it loudly but harmlessly\n\t\t\tconsole.warn(\"Application: init() called twice — ignoring\");\n\t\t\treturn;\n\t\t}\n\n\t\t// a previous init() attempt may have constructed a renderer before\n\t\t// rejecting (e.g. the WebGPU device negotiation failed) — release\n\t\t// it before building a new one, so a retry does not leak a backend\n\t\tthis.renderer?.destroy();\n\n\t\tif (typeof this.settings.renderer === \"number\") {\n\t\t\tswitch (this.settings.renderer) {\n\t\t\t\tcase AUTO: {\n\t\t\t\t\t// WebGPU first, then WebGL 2, then Canvas. WebGPU support\n\t\t\t\t\t// can only be proven by negotiating an adapter/device, so\n\t\t\t\t\t// the attempt is a full backend init awaited here —\n\t\t\t\t\t// rejection falls through to the synchronous candidates\n\t\t\t\t\t// (autoDetectRenderer) instead of failing the application.\n\t\t\t\t\tlet negotiated;\n\t\t\t\t\tif (typeof globalThis.navigator?.gpu !== \"undefined\") {\n\t\t\t\t\t\tconst attempt = new WebGPURenderer(this.settings as any);\n\t\t\t\t\t\tattempt.parentApplication = this;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait attempt.init();\n\t\t\t\t\t\t\tnegotiated = attempt;\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t// release the half-built backend (listeners\n\t\t\t\t\t\t\t// unhook; no device was retained on rejection)\n\t\t\t\t\t\t\tattempt.destroy();\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`AUTO: WebGPU unavailable (${\n\t\t\t\t\t\t\t\t\te instanceof Error ? e.message : String(e)\n\t\t\t\t\t\t\t\t}) — falling back to WebGL`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.renderer =\n\t\t\t\t\t\tnegotiated ?? autoDetectRenderer(this.settings as any);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase WEBGL:\n\t\t\t\t\t// \"I require WebGL\" — fail loudly if it isn't\n\t\t\t\t\t// available instead of silently falling back to\n\t\t\t\t\t// Canvas. Camera3d, mesh rendering, ShaderEffect /\n\t\t\t\t\t// post-FX, GPU tilemap and Light2d all require a\n\t\t\t\t\t// WebGL context; falling back to Canvas would\n\t\t\t\t\t// produce a silently broken scene with no signal to\n\t\t\t\t\t// the dev about why. Use `video.AUTO` to opt into\n\t\t\t\t\t// the fall-back behavior explicitly.\n\t\t\t\t\tif (!device.isWebGLSupported(this.settings)) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\"Application: `renderer: video.WEBGL` was requested \" +\n\t\t\t\t\t\t\t\t\"but a WebGL 2 context could not be created in this \" +\n\t\t\t\t\t\t\t\t\"environment (WebGL-1-only device, driver-blocklisted \" +\n\t\t\t\t\t\t\t\t\"GPU, `failIfMajorPerformanceCaveat: true` on a \" +\n\t\t\t\t\t\t\t\t\"software renderer, etc.). The WebGL renderer is \" +\n\t\t\t\t\t\t\t\t\"WebGL 2 only since 20.0. Use `video.AUTO` if Canvas \" +\n\t\t\t\t\t\t\t\t\"fallback is acceptable, or check \" +\n\t\t\t\t\t\t\t\t\"`device.isWebGLSupported()` before construction.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthis.renderer = new WebGLRenderer(this.settings as any);\n\t\t\t\t\tbreak;\n\t\t\t\tcase WEBGPU:\n\t\t\t\t\t// \"I require WebGPU\" — like WEBGL, an explicit request\n\t\t\t\t\t// either runs on WebGPU or fails; a quiet substitution\n\t\t\t\t\t// would make the request meaningless. Construction\n\t\t\t\t\t// acquires the GPUCanvasContext (throws when WebGPU is\n\t\t\t\t\t// unavailable, surfacing as a rejection of this method);\n\t\t\t\t\t// the adapter/device negotiation happens in the\n\t\t\t\t\t// `renderer.init()` await below. Use `video.AUTO` for\n\t\t\t\t\t// the WebGPU → WebGL → Canvas fallback ladder.\n\t\t\t\t\tthis.renderer = new WebGPURenderer(this.settings as any);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.renderer = new CanvasRenderer(this.settings as any);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tconst CustomRenderer = this.settings.renderer as any;\n\t\t\t// a renderer class\n\t\t\tthis.renderer = new CustomRenderer(this.settings);\n\t\t}\n\n\t\t// the renderer carries its owning application — engine code holding\n\t\t// a renderer reference must use this rather than the global game\n\t\tthis.renderer.parentApplication = this;\n\n\t\t// let the backend acquire whatever it cannot acquire synchronously —\n\t\t// an immediate resolve for Canvas and WebGL, the adapter/device\n\t\t// negotiation for WebGPU. This await is why `init()` is asynchronous.\n\t\t// (optional-chained so a duck-typed custom renderer that does not\n\t\t// extend `Renderer` keeps working without the new lifecycle hook)\n\t\tawait this.renderer.init?.();\n\n\t\t// destroy() may have run while the backend was negotiating its\n\t\t// context — finishing the bootstrap now would resurrect a torn-down\n\t\t// application (re-registered listeners, an appended canvas, a live\n\t\t// GPU device nothing will ever release, and the `game` global\n\t\t// pointing at a dead app)\n\t\tif (this._destroyed) {\n\t\t\tthis.renderer.destroy();\n\t\t\tthrow new Error(\n\t\t\t\t\"Application: destroyed while init() was awaiting the renderer — \" +\n\t\t\t\t\t\"initialization aborted\",\n\t\t\t);\n\t\t}\n\n\t\t// Cross-check the chosen camera class against the renderer the\n\t\t// engine actually instantiated. Camera classes that declare\n\t\t// `static defaultSortOn = \"depth\"` (Camera3d and any subclass)\n\t\t// need the WebGL backend — depth attachment, projection matrix\n\t\t// uniform, `drawMesh`, frustum culling — none of which the\n\t\t// Canvas renderer provides. Catches the\n\t\t// `{ renderer: video.AUTO, cameraClass: Camera3d }` combination\n\t\t// where AUTO fell back to Canvas and the user wouldn't otherwise\n\t\t// see anything go wrong until the first frame rendered a black\n\t\t// canvas. Uses `defaultSortOn === \"depth\"` as the signal so\n\t\t// Application doesn't need to import the concrete Camera3d class.\n\t\t//\n\t\t// Warn (not throw) so unit-level integration tests that bypass\n\t\t// real rendering can still verify the `cameraClass → world.sortOn`\n\t\t// bootstrap wiring under Canvas without crashing. The strong\n\t\t// failure mode for users who genuinely need WebGL is to set\n\t\t// `renderer: video.WEBGL` — that throws above when WebGL is\n\t\t// unavailable, which is the right signal at construction time.\n\t\tconst CameraClass = this.settings.cameraClass as\n\t\t\t| { defaultSortOn?: string; name?: string }\n\t\t\t| undefined;\n\t\tif (\n\t\t\tCameraClass?.defaultSortOn === \"depth\" &&\n\t\t\t!this.renderer.supportsDepthBuffer\n\t\t) {\n\t\t\tconsole.warn(\n\t\t\t\t`Application: \\`cameraClass\\` (${\n\t\t\t\t\tCameraClass.name ?? \"anonymous\"\n\t\t\t\t}) declares \\`defaultSortOn = \"depth\"\\` (Camera3d or subclass), ` +\n\t\t\t\t\t\"which requires a renderer with a depth buffer — the active \" +\n\t\t\t\t\t\"renderer has none, and so has no 3D projection path and \" +\n\t\t\t\t\t\"no `drawMesh`. The scene will not \" +\n\t\t\t\t\t\"render correctly. Use `{ renderer: video.WEBGL }` (throws \" +\n\t\t\t\t\t\"if WebGL is unavailable) instead of `video.AUTO` (silently \" +\n\t\t\t\t\t\"falls back to Canvas).\",\n\t\t\t);\n\t\t}\n\n\t\t// add our canvas (default to document.body if settings.parent is undefined)\n\t\tconst canvas = this.renderer.getCanvas();\n\t\t// the browser default `display: inline` puts the canvas on the text\n\t\t// baseline, so an auto-sized ancestor measures canvas + baseline gap —\n\t\t// and the auto-scale resize path then re-applies that few-px excess on\n\t\t// EVERY resize event (the slow canvas-growth loop of #1231). `block`\n\t\t// removes the gap. Only applied when the canvas carries no explicit\n\t\t// inline display, so user styling stays authoritative.\n\t\tif (canvas.style.display === \"\") {\n\t\t\tcanvas.style.display = \"block\";\n\t\t}\n\t\tthis.parentElement.appendChild(canvas);\n\n\t\t// trigger an initial resize();\n\t\tonresize(this);\n\n\t\t// add an observer to detect when the dom tree is modified\n\t\tif (\"MutationObserver\" in globalThis) {\n\t\t\t// Create an observer instance linked to the callback function\n\t\t\t// (stored so destroy() can disconnect it — otherwise every\n\t\t\t// discarded Application keeps re-laying-out against a parent\n\t\t\t// element that outlives it)\n\t\t\tthis._resizeObserver = new MutationObserver(() => {\n\t\t\t\tonresize(this);\n\t\t\t});\n\n\t\t\t// Start observing the target node for configured mutations\n\t\t\tthis._resizeObserver.observe(this.parentElement, {\n\t\t\t\tattributes: false,\n\t\t\t\tchildList: true,\n\t\t\t\tsubtree: true,\n\t\t\t});\n\t\t}\n\n\t\tif (this.settings.consoleHeader) {\n\t\t\tconsoleHeader(this);\n\t\t}\n\n\t\t// report the active physics adapter (wired in the constructor) —\n\t\t// useful confirmation when a third-party adapter (e.g.\n\t\t// `@melonjs/matter-adapter`) is plugged in via `settings.physic`.\n\t\t// External adapters that set `name` / `version` / `url` get logged\n\t\t// the same way as @melonjs/debug-plugin; BuiltinAdapter falls back\n\t\t// to just its class name. Skipped when `consoleHeader` is disabled.\n\t\tif (this.settings.consoleHeader) {\n\t\t\tif (this.world.physic === \"none\") {\n\t\t\t\tconsole.log(\"physics: disabled\");\n\t\t\t} else if (this.world.adapter) {\n\t\t\t\tconst a = this.world.adapter as {\n\t\t\t\t\tconstructor: { name: string };\n\t\t\t\t\tname?: string;\n\t\t\t\t\tphysicLabel?: string;\n\t\t\t\t\tversion?: string;\n\t\t\t\t\turl?: string;\n\t\t\t\t};\n\t\t\t\t// prefer the adapter's declared name, then its stable physicLabel;\n\t\t\t\t// `constructor.name` is a last resort (it's mangled in minified\n\t\t\t\t// builds, e.g. the built-in adapter would print \"physics: ry\")\n\t\t\t\tconst label = a.name ?? a.physicLabel ?? a.constructor.name;\n\t\t\t\tconst version = a.version ? ` ${a.version}` : \"\";\n\t\t\t\tconst url = a.url ? ` | ${a.url}` : \"\";\n\t\t\t\tconsole.log(`physics: ${label}${version}${url}`);\n\t\t\t} else {\n\t\t\t\tconsole.log(\"physics: enabled (no adapter)\");\n\t\t\t}\n\t\t}\n\n\t\t// The GPU tilemap path needs a WebGL 2 renderer. The previous\n\t\t// \"gpuTilemap is enabled but the active renderer is not WebGL 2\"\n\t\t// app-init warning was emitted regardless of whether a tilemap\n\t\t// was ever loaded, so apps with no TMX layers (e.g. spine demos\n\t\t// under Canvas) got a spurious noise line. TMXLayer now emits a\n\t\t// single deferred warning the first time a layer hits the\n\t\t// no-WebGL2 fallback, so the heads-up only fires when relevant.\n\n\t\t// app starting time\n\t\tthis.lastUpdate = globalThis.performance.now();\n\t\t// only register event listeners once per instance\n\t\tif (!this.isInitialized) {\n\t\t\t/* eslint-disable @typescript-eslint/unbound-method */\n\t\t\ton(STATE_CHANGE, this.repaint, this);\n\t\t\ton(STATE_RESTART, this.repaint, this);\n\t\t\ton(STATE_RESUME, this.repaint, this);\n\t\t\ton(STAGE_RESET, this.reset, this);\n\t\t\t/* eslint-enable @typescript-eslint/unbound-method */\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(TICK, this._tick, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(BLUR, this._onBlur, this);\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(FOCUS, this._onFocus, this);\n\t\t}\n\n\t\tthis.isInitialized = true;\n\n\t\t// Only publish this instance as the default `game` once it is fully\n\t\t// usable — a constructed-but-not-initialized Application has no\n\t\t// renderer, which is precisely the half-built state this split\n\t\t// removes from the global.\n\t\tsetDefaultGame(this);\n\n\t\temit(GAME_INIT, this);\n\t\temit(VIDEO_INIT, this.renderer);\n\t}\n\n\t/**\n\t * reset the game Object manager\n\t * destroy all current objects\n\t */\n\treset(): void {\n\t\t// point to the current active stage \"default\" camera\n\t\tconst current = state.get();\n\t\tif (typeof current !== \"undefined\") {\n\t\t\tthis.viewport = current.cameras.get(\"default\")!;\n\t\t}\n\n\t\t// publish reset notification\n\t\temit(GAME_RESET);\n\n\t\t// Refresh internal variables for framerate  limiting\n\t\tthis.updateFrameRate();\n\t}\n\n\t/**\n\t * Specify the property to be used when sorting renderables for this application game world.\n\t * Accepted values : \"x\", \"y\", \"z\", \"depth\"\n\t * @see {@link World.sortOn}\n\t */\n\tget sortOn(): \"x\" | \"y\" | \"z\" | \"depth\" {\n\t\treturn this.world.sortOn;\n\t}\n\tset sortOn(value: \"x\" | \"y\" | \"z\" | \"depth\") {\n\t\tthis.world.sortOn = value;\n\t}\n\n\t/**\n\t * Fired when a level is fully loaded and all renderable instantiated. <br>\n\t * Additionally the level id will also be passed to the called function.\n\t * @example\n\t * // call myFunction () everytime a level is loaded\n\t * app.onLevelLoaded = this.myFunction.bind(this);\n\t */\n\tonLevelLoaded(): void {}\n\n\t/**\n\t * Update the renderer framerate using the system config variables.\n\t * @see {@link timer.maxfps}\n\t * @see {@link World.fps}\n\t */\n\tupdateFrameRate(): void {\n\t\t// reset the frame counter\n\t\tthis.frameCounter = 0;\n\t\tthis.frameRate = ~~(0.5 + 60 / timer.maxfps);\n\n\t\t// set step size based on the updatesPerSecond\n\t\tthis.stepSize = 1000 / this.world.fps;\n\t\tthis.accumulator = 0.0;\n\t\tthis.accumulatorMax = this.stepSize * 10;\n\n\t\t// display should always re-draw when update speed doesn't match fps\n\t\t// this means the user intends to write position prediction drawing logic\n\t\tthis.isAlwaysDirty = timer.maxfps > this.world.fps;\n\t}\n\n\t/**\n\t * Returns the parent HTML Element holding the main canvas of this application\n\t * @returns the parent HTML element\n\t */\n\tgetParentElement(): HTMLElement {\n\t\treturn this.parentElement;\n\t}\n\n\t/**\n\t * Returns `true` if the browser/device is currently in fullscreen mode.\n\t * Thin convenience around {@link device.isFullscreen} so the\n\t * fullscreen trio (`isFullscreen` / `requestFullscreen` /\n\t * `exitFullscreen`) lives together on the app instance.\n\t * @category Application\n\t */\n\tisFullscreen(): boolean {\n\t\treturn device.isFullscreen();\n\t}\n\n\t/**\n\t * Trigger a fullscreen request for this application. Defaults to this\n\t * application's `parentElement` (the container the canvas was appended\n\t * into — see {@link Application#getParentElement}), so the canvas and\n\t * any sibling HUD / overlay markup inside that container go fullscreen\n\t * together.\n\t * @param element - optional element to fullscreen instead of `this.parentElement`\n\t * @example\n\t * // bind F to toggle fullscreen\n\t * me.input.bindKey(me.input.KEY.F, \"toggleFullscreen\");\n\t * me.event.on(me.event.KEYDOWN, (action) => {\n\t *   if (action === \"toggleFullscreen\") {\n\t *     if (!app.isFullscreen()) app.requestFullscreen();\n\t *     else app.exitFullscreen();\n\t *   }\n\t * });\n\t * @category Application\n\t */\n\trequestFullscreen(element?: Element): void {\n\t\tif (device.hasFullscreenSupport && !this.isFullscreen()) {\n\t\t\tconst target = (element ??\n\t\t\t\tthis.parentElement) as ElementWithLegacyFullscreen;\n\t\t\t/* eslint-disable @typescript-eslint/unbound-method -- `this` is restored explicitly via `.call(target)` below */\n\t\t\tconst request =\n\t\t\t\ttarget.requestFullscreen ||\n\t\t\t\ttarget.webkitRequestFullscreen ||\n\t\t\t\ttarget.mozRequestFullScreen ||\n\t\t\t\ttarget.msRequestFullscreen;\n\t\t\t/* eslint-enable @typescript-eslint/unbound-method */\n\t\t\tconst result = request?.call(target);\n\t\t\tif (result instanceof Promise) result.catch(console.error);\n\t\t}\n\t}\n\n\t/**\n\t * Exit fullscreen mode for this application.\n\t * @category Application\n\t */\n\texitFullscreen(): void {\n\t\tif (!device.hasFullscreenSupport || !this.isFullscreen()) return;\n\t\tconst doc = globalThis.document as DocumentWithLegacyExitFullscreen;\n\t\t/* eslint-disable @typescript-eslint/unbound-method -- `this` is restored explicitly via `.call(doc)` below */\n\t\tconst exit =\n\t\t\tdoc.exitFullscreen ||\n\t\t\tdoc.webkitExitFullscreen ||\n\t\t\tdoc.mozCancelFullScreen ||\n\t\t\tdoc.msExitFullscreen;\n\t\t/* eslint-enable @typescript-eslint/unbound-method */\n\t\tconst result = exit?.call(doc);\n\t\tif (result instanceof Promise) result.catch(console.error);\n\t}\n\n\t/**\n\t * The HTML canvas element associated with this application's renderer.\n\t * @example\n\t * // access the canvas DOM element\n\t * const canvas = app.canvas;\n\t */\n\tget canvas(): HTMLCanvasElement {\n\t\treturn this.renderer.getCanvas();\n\t}\n\n\t/**\n\t * Trigger a manual resize of the application canvas to fit the parent element.\n\t * This is automatically called on window resize/orientation change, but can\n\t * be called manually if the parent element size changes programmatically.\n\t * @example\n\t * // force a resize after changing the parent element dimensions\n\t * app.resize();\n\t */\n\tresize(): void {\n\t\tonresize(this);\n\t}\n\n\t/**\n\t * Destroy this application instance and release all associated resources.\n\t * Removes the canvas from the DOM, destroys the world, and unregisters\n\t * all event listeners.\n\t *\n\t * **Terminal**: a destroyed Application cannot be re-initialized —\n\t * `init()` rejects afterwards (and an `init()` still in flight aborts).\n\t * Construct a new Application to start again.\n\t * @param removeCanvas - if true, the canvas element is removed from the DOM (default: true)\n\t * @example\n\t * // clean up when done\n\t * app.destroy();\n\t */\n\tdestroy(removeCanvas: boolean = true): void {\n\t\t// stop the render loop and remove all event listeners\n\t\t/* eslint-disable @typescript-eslint/unbound-method */\n\t\toff(TICK, this._tick, this);\n\t\toff(BLUR, this._onBlur, this);\n\t\toff(FOCUS, this._onFocus, this);\n\t\toff(STATE_CHANGE, this.repaint, this);\n\t\toff(STATE_RESTART, this.repaint, this);\n\t\toff(STATE_RESUME, this.repaint, this);\n\t\toff(STAGE_RESET, this.reset, this);\n\t\t/* eslint-enable @typescript-eslint/unbound-method */\n\n\t\t// remove the melonJS-event resize subscriptions registered by the\n\t\t// constructor — without this every discarded Application instance\n\t\t// keeps fanning out on WINDOW_ONRESIZE for the page's lifetime\n\t\tif (this._doResize) {\n\t\t\toff(WINDOW_ONRESIZE, this._doResize);\n\t\t\toff(WINDOW_ONORIENTATION_CHANGE, this._doResize);\n\t\t}\n\n\t\t// stop watching the parent element — the observer would otherwise\n\t\t// keep re-laying-out this dead app on every DOM mutation (including\n\t\t// the canvas removal below)\n\t\tthis._resizeObserver?.disconnect();\n\t\tthis._resizeObserver = undefined;\n\n\t\t// remove DOM event listeners\n\t\tif (this._onResize) {\n\t\t\tglobalThis.removeEventListener(\"resize\", this._onResize);\n\t\t\tglobalThis.removeEventListener(\n\t\t\t\t\"orientationchange\",\n\t\t\t\tthis._onOrientationChange!,\n\t\t\t);\n\t\t\tglobalThis.removeEventListener(\"scroll\", this._onScroll!);\n\t\t\tif (device.screenOrientation) {\n\t\t\t\tglobalThis.screen.orientation.onchange = null;\n\t\t\t}\n\t\t}\n\n\t\t// destroy the world and all its children\n\t\tif (this.world) {\n\t\t\tthis.world.destroy();\n\t\t}\n\n\t\t// tear down the renderer's batchers so their `event.on(...)`\n\t\t// subscriptions (e.g. `GPU_TEXTURE_CACHE_RESET` on\n\t\t// `MaterialBatcher`) don't leak across the disposal — every\n\t\t// otherwise-discarded `Application` would otherwise keep the\n\t\t// batchers (and their renderer reference) alive for the rest\n\t\t// of the page's lifetime.\n\t\tthis.renderer?.destroy();\n\n\t\t// remove the canvas from the DOM\n\t\tif (removeCanvas && this.renderer) {\n\t\t\tconst canvas = this.renderer.getCanvas();\n\t\t\tif (canvas.parentElement) {\n\t\t\t\tcanvas.parentElement.removeChild(canvas);\n\t\t\t}\n\t\t}\n\n\t\tthis.isInitialized = false;\n\t\t// terminal: init() refuses to run on this instance from here on,\n\t\t// and an init() currently awaiting its renderer aborts instead of\n\t\t// resurrecting the app\n\t\tthis._destroyed = true;\n\t}\n\n\t/**\n\t * force the redraw (not update) of all objects\n\t */\n\trepaint(): void {\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Pause the current stage. Convenience proxy for {@link state.pause}.\n\t * @param [music=false] - also pause the current music track\n\t * @example\n\t * app.pause();        // pause game updates, keep music playing\n\t * app.pause(true);    // pause game updates and music\n\t */\n\tpause(music: boolean = false): void {\n\t\tstate.pause(music);\n\t}\n\n\t/**\n\t * Resume the current stage. Convenience proxy for {@link state.resume}.\n\t * @param [music=false] - also resume the current music track\n\t */\n\tresume(music: boolean = false): void {\n\t\tstate.resume(music);\n\t}\n\n\t/**\n\t * Freeze the current stage for a fixed duration, then automatically resume.\n\t * Useful for hit-stop / hit-pause effects on impact.\n\t *\n\t * Convenience proxy for {@link state.freeze}; see that method's\n\t * documentation for the full behaviour matrix (extend-not-stack semantics,\n\t * interaction with manual `state.pause()` / `state.resume()`, automatic\n\t * cancellation on window blur, etc.).\n\t * @param duration - duration of the freeze in milliseconds\n\t * @param [music=false] - also pause the current music track during the freeze\n\t * @returns a Promise that resolves once the freeze ends (or is cancelled)\n\t * @example\n\t * // simple hit-stop on impact\n\t * app.freeze(80);\n\t *\n\t * // chain VFX after the freeze\n\t * await app.freeze(120);\n\t * spawnImpactParticles();\n\t */\n\tfreeze(duration: number, music: boolean = false): Promise<void> {\n\t\treturn state.freeze(duration, music);\n\t}\n\n\t/** @ignore */\n\t_tick(time: number): void {\n\t\tthis.update(time);\n\t\tthis.draw();\n\t}\n\n\t/** @ignore */\n\t_onBlur(): void {\n\t\tif (this.stopOnBlur) {\n\t\t\tstate.stop(true);\n\t\t}\n\t\tif (this.pauseOnBlur) {\n\t\t\tstate.pause(true);\n\t\t}\n\t}\n\n\t/** @ignore */\n\t_onFocus(): void {\n\t\tif (this.stopOnBlur) {\n\t\t\tstate.restart(true);\n\t\t}\n\t\tif (this.resumeOnFocus) {\n\t\t\tstate.resume(true);\n\t\t}\n\t}\n\n\t/**\n\t * update all objects related to this game active scene/stage\n\t * @param time - current timestamp as provided by the RAF callback\n\t */\n\tupdate(time: number): void {\n\t\t// handle frame skipping if required\n\t\tif (++this.frameCounter % this.frameRate === 0) {\n\t\t\t// reset the frame counter\n\t\t\tthis.frameCounter = 0;\n\n\t\t\t// publish notification\n\t\t\temit(GAME_BEFORE_UPDATE, time);\n\t\t\tthis.accumulator += timer.getDelta();\n\t\t\tthis.accumulator = Math.min(this.accumulator, this.accumulatorMax);\n\n\t\t\tthis.updateDelta = timer.interpolation ? timer.getDelta() : this.stepSize;\n\t\t\tthis.accumulatorUpdateDelta = timer.interpolation\n\t\t\t\t? this.updateDelta\n\t\t\t\t: Math.max(this.updateDelta, this.lastUpdateDelta);\n\n\t\t\twhile (\n\t\t\t\tthis.accumulator >= this.accumulatorUpdateDelta ||\n\t\t\t\ttimer.interpolation\n\t\t\t) {\n\t\t\t\tthis.lastUpdateStart = globalThis.performance.now();\n\n\t\t\t\t// game update event\n\t\t\t\tif (!state.isPaused()) {\n\t\t\t\t\temit(GAME_UPDATE, time);\n\t\t\t\t}\n\n\t\t\t\t// update all objects (and pass the elapsed time since last frame)\n\t\t\t\tthis.isDirty = this.world.update(this.updateDelta);\n\t\t\t\tthis.isDirty =\n\t\t\t\t\tstate.current()!.update(this.updateDelta) || this.isDirty;\n\n\t\t\t\tthis.lastUpdate = globalThis.performance.now();\n\t\t\t\tthis.lastUpdateDelta = this.lastUpdate - this.lastUpdateStart;\n\n\t\t\t\tthis.accumulator -= this.accumulatorUpdateDelta;\n\t\t\t\tif (timer.interpolation) {\n\t\t\t\t\tthis.accumulator = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// publish notification\n\t\t\temit(GAME_AFTER_UPDATE, this.lastUpdate);\n\t\t}\n\t}\n\n\t/**\n\t * draw the active scene/stage associated to this game\n\t */\n\tdraw(): void {\n\t\tif (this.renderer.isContextValid && (this.isDirty || this.isAlwaysDirty)) {\n\t\t\t// publish notification\n\t\t\temit(GAME_BEFORE_DRAW, globalThis.performance.now());\n\n\t\t\t// prepare renderer to draw a new frame\n\t\t\tthis.renderer.clear();\n\n\t\t\t// render the stage\n\t\t\tstate.current()!.draw(this.renderer, this.world);\n\n\t\t\t// set back to flag\n\t\t\tthis.isDirty = false;\n\n\t\t\t// ground shadows are held back until every opaque mesh is down\n\t\t\t// (#1515); a scene that is nothing but meshes never switches away\n\t\t\t// from mesh mode, so the pass is closed here\n\t\t\tthis.renderer.flushGroundShadows();\n\n\t\t\t// flush/render our frame\n\t\t\tthis.renderer.flush();\n\n\t\t\t// publish notification\n\t\t\temit(GAME_AFTER_DRAW, globalThis.performance.now());\n\t\t}\n\t}\n}\n\n/**\n * The default game application instance.\n * Set via {@link setDefaultGame} during engine initialization.\n * When using {@link Application} directly, prefer using the app instance\n * (e.g. from {@link Stage#onResetEvent} or {@link Renderable#parentApp}).\n */\nexport let game: Application;\n\n/**\n * Set the default game application instance.\n * @ignore\n */\nexport function setDefaultGame(app: Application) {\n\tgame = app;\n}\n","import type ParticleEmitter from \"./emitter.ts\";\n\n/**\n * Configuration shape for {@link ParticleEmitter}.\n * Every field has a sensible default; users typically pass a `Partial<ParticleEmitterSettings>`\n * to the constructor or {@link ParticleEmitter#reset}.\n * @category Particles\n * @see {@link ParticleEmitter}\n */\nexport interface ParticleEmitterSettings {\n\t/**\n\t * Width of the particle spawn area.\n\t * @default 1\n\t */\n\twidth: number;\n\n\t/**\n\t * Height of the particle spawn area.\n\t * @default 1\n\t */\n\theight: number;\n\n\t/**\n\t * Image used for particles texture (by default melonJS will create a white\n\t * 8x8 texture image).\n\t * @default undefined\n\t * @see {@link textureSize}\n\t */\n\timage: HTMLCanvasElement | HTMLImageElement | undefined;\n\n\t/**\n\t * Default texture size used for particles if no image is specified\n\t * (by default melonJS will create a white 8x8 texture image).\n\t * @default 8\n\t * @see {@link image}\n\t */\n\ttextureSize: number;\n\n\t/**\n\t * Tint to be applied to particles.\n\t * @default \"#fff\"\n\t */\n\ttint: string;\n\n\t/**\n\t * Total number of particles in the emitter.\n\t * @default 50\n\t */\n\ttotalParticles: number;\n\n\t/**\n\t * Start angle for particle launch in Radians.\n\t * @default Math.PI / 2\n\t */\n\tangle: number;\n\n\t/**\n\t * Variation in the start angle for particle launch in Radians.\n\t * @default 0\n\t */\n\tangleVariation: number;\n\n\t/**\n\t * Minimum time each particle lives once it is emitted in ms.\n\t * If greater than `maxLife`, it is clamped to `maxLife` at reset.\n\t * @default 1000\n\t */\n\tminLife: number;\n\n\t/**\n\t * Maximum time each particle lives once it is emitted in ms.\n\t * @default 3000\n\t */\n\tmaxLife: number;\n\n\t/**\n\t * Start speed of particles.\n\t * @default 2\n\t */\n\tspeed: number;\n\n\t/**\n\t * Variation in the start speed of particles.\n\t * @default 1\n\t */\n\tspeedVariation: number;\n\n\t/**\n\t * Minimum start rotation for particle sprites in Radians.\n\t * @default 0\n\t */\n\tminRotation: number;\n\n\t/**\n\t * Maximum start rotation for particle sprites in Radians.\n\t * @default 0\n\t */\n\tmaxRotation: number;\n\n\t/**\n\t * Minimum start scale ratio for particles (1 = no scaling).\n\t * @default 1\n\t */\n\tminStartScale: number;\n\n\t/**\n\t * Maximum start scale ratio for particles (1 = no scaling).\n\t * @default 1\n\t */\n\tmaxStartScale: number;\n\n\t/**\n\t * Minimum end scale ratio for particles.\n\t * @default 0\n\t */\n\tminEndScale: number;\n\n\t/**\n\t * Maximum end scale ratio for particles.\n\t * @default 0\n\t */\n\tmaxEndScale: number;\n\n\t/**\n\t * Vertical force (Gravity) for each particle.\n\t * @default 0\n\t * @see {@link World.gravity}\n\t */\n\tgravity: number;\n\n\t/**\n\t * Horizontal force (like a Wind) for each particle.\n\t * @default 0\n\t */\n\twind: number;\n\n\t/**\n\t * Update the rotation of particle in accordance with the particle trajectory.\n\t * The particle sprite should aim at zero angle (draw from left to right).\n\t * Overrides the particle `minRotation` and `maxRotation`.\n\t * @default false\n\t */\n\tfollowTrajectory: boolean;\n\n\t/**\n\t * Enable the Texture Additive by composite operation (\"additive\" blendMode).\n\t * @default false\n\t * @see {@link blendMode}\n\t */\n\ttextureAdditive: boolean;\n\n\t/**\n\t * Blend mode applied when rendering particles. If different than \"normal\",\n\t * supersedes the `textureAdditive` setting.\n\t * @default \"normal\"\n\t * @see {@link CanvasRenderer#setBlendMode}\n\t * @see {@link WebGLRenderer#setBlendMode}\n\t */\n\tblendMode: string;\n\n\t/**\n\t * Only repaint particles while they are inside the viewport (off-screen\n\t * particles keep simulating and living out their lifetime, but do not\n\t * mark the scene dirty).\n\t * @default true\n\t */\n\tonlyInViewport: boolean;\n\n\t/**\n\t * Render particles in screen space.\n\t * @default false\n\t */\n\tfloating: boolean;\n\n\t/**\n\t * Maximum number of particles launched each tick (stream mode only).\n\t * @default 10\n\t */\n\tmaxParticles: number;\n\n\t/**\n\t * How often a particle is emitted in ms (stream mode only).\n\t * @default 100\n\t */\n\tfrequency: number;\n\n\t/**\n\t * Duration that the emitter releases particles in ms (stream mode only).\n\t * After this period, the emitter stops launching particles.\n\t * @default Infinity\n\t */\n\tduration: number;\n\n\t/**\n\t * Skip n frames after updating the particle system once.\n\t * Reduces the performance impact of emitters with many particles.\n\t * @default 0\n\t */\n\tframesToSkip: number;\n\n\t/**\n\t * When `true`, each particle refreshes its bounding box every frame so the\n\t * hitbox tracks the visual exactly (useful for debug visualization or\n\t * collision queries). When `false` (default), bounds reflect the previous\n\t * frame's transform — sufficient for viewport culling and significantly\n\t * cheaper at high particle counts.\n\t * @default false\n\t */\n\taccurateBounds: boolean;\n\n\t/**\n\t * When `true`, the emitter automatically removes itself from its parent\n\t * container once all particles have died. Useful for fire-and-forget\n\t * `burstParticles()` use cases (explosions, pickups, impact effects) where\n\t * there is no natural cleanup hook.\n\t * @default false\n\t */\n\tautoDestroyOnComplete: boolean;\n\n\t/**\n\t * Optional callback fired when the emitter completes (all particles dead\n\t * after at least one particle has been spawned, and — for stream mode —\n\t * the duration has elapsed). Fires regardless of `autoDestroyOnComplete`,\n\t * and runs *before* the emitter is removed from its parent. The callback\n\t * is invoked with the emitter as its `this` context.\n\t * @default undefined\n\t */\n\tonComplete: ((this: ParticleEmitter) => void) | undefined;\n}\n\n/**\n * Default values for every {@link ParticleEmitterSettings} field.\n * @category Particles\n */\nconst defaultParticleEmitterSettings: ParticleEmitterSettings = {\n\twidth: 1,\n\theight: 1,\n\timage: undefined,\n\ttextureSize: 8,\n\ttint: \"#fff\",\n\ttotalParticles: 50,\n\tangle: Math.PI / 2,\n\tangleVariation: 0,\n\tminLife: 1000,\n\tmaxLife: 3000,\n\tspeed: 2,\n\tspeedVariation: 1,\n\tminRotation: 0,\n\tmaxRotation: 0,\n\tminStartScale: 1,\n\tmaxStartScale: 1,\n\tminEndScale: 0,\n\tmaxEndScale: 0,\n\tgravity: 0,\n\twind: 0,\n\tfollowTrajectory: false,\n\ttextureAdditive: false,\n\tblendMode: \"normal\",\n\tonlyInViewport: true,\n\tfloating: false,\n\tmaxParticles: 10,\n\tfrequency: 100,\n\tduration: Infinity,\n\tframesToSkip: 0,\n\taccurateBounds: false,\n\tautoDestroyOnComplete: false,\n\tonComplete: undefined,\n};\n\nexport default defaultParticleEmitterSettings;\n","import { randomFloat } from \"./../math/math.ts\";\nimport Container from \"./../renderable/container.js\";\nimport timer from \"../system/timer.ts\";\nimport CanvasRenderTarget from \"../video/rendertarget/canvasrendertarget.js\";\nimport { particlePool } from \"./particle.ts\";\nimport defaultEmitterSettings, {\n\ttype ParticleEmitterSettings,\n} from \"./settings.ts\";\n\n/**\n * @ignore\n */\nfunction createDefaultParticleTexture(\n\tw: number = 8,\n\th: number = 8,\n): CanvasRenderTarget {\n\tconst defaultParticleTexture = new CanvasRenderTarget(w, h, {\n\t\toffscreenCanvas: true,\n\t});\n\n\tdefaultParticleTexture.context.fillStyle = \"#fff\";\n\tdefaultParticleTexture.context.fillRect(0, 0, w, h);\n\n\treturn defaultParticleTexture;\n}\n\n/**\n * If `settings[minKey] > settings[maxKey]`, lower `min` to `max`.\n * Guards against the partial-override footgun where a user sets only the\n * `max` half of a range-style setting and the default of `min` ends up larger.\n * @ignore\n */\nfunction clampMinToMax<K extends keyof ParticleEmitterSettings>(\n\tsettings: ParticleEmitterSettings,\n\tminKey: K,\n\tmaxKey: K,\n): void {\n\tif ((settings[minKey] as number) > (settings[maxKey] as number)) {\n\t\t(settings as Record<K, number>)[minKey] = settings[maxKey] as number;\n\t}\n}\n\n/**\n * Particle Emitter Object.\n * @category Particles\n */\nexport default class ParticleEmitter extends Container {\n\t/**\n\t * the current (active) emitter settings (with defaults merged in)\n\t */\n\tsettings: ParticleEmitterSettings;\n\n\t/** @ignore */\n\t_stream: boolean;\n\n\t/** @ignore */\n\t_frequencyTimer: number;\n\n\t/** @ignore */\n\t_durationTimer: number;\n\n\t/** @ignore */\n\t_enabled: boolean;\n\n\t/** @ignore */\n\t_updateCount: number;\n\n\t/** @ignore */\n\t_dt: number;\n\n\t/** @ignore */\n\t_defaultParticle: CanvasRenderTarget | undefined;\n\n\t/**\n\t * whether at least one particle has been spawned by this emitter — used as\n\t * the precondition for completion detection (a brand-new emitter with zero\n\t * children must not count as \"complete\")\n\t * @ignore\n\t */\n\t_hasSpawned: boolean;\n\n\t/**\n\t * cached `timer.maxfps / 1000` — particles read this directly instead of\n\t * recomputing it on every spawn.\n\t * @ignore\n\t */\n\t_deltaInv: number;\n\n\t/**\n\t * @param x - x position of the particle emitter\n\t * @param y - y position of the particle emitter\n\t * @param [settings=ParticleEmitterSettings] - the settings for the particle emitter.\n\t * @example\n\t * // Create a particle emitter at position 100, 100\n\t * let emitter = new ParticleEmitter(100, 100, {\n\t *     width: 16,\n\t *     height : 16,\n\t *     tint: \"#f00\",\n\t *     totalParticles: 32,\n\t *     angle: 0,\n\t *     angleVariation: 6.283185307179586,\n\t *     maxLife: 5,\n\t *     speed: 3\n\t * });\n\t *\n\t * // Add the emitter to the game world\n\t * app.world.addChild(emitter);\n\t *\n\t * // Launch all particles one time and stop, like an explosion\n\t * emitter.burstParticles();\n\t *\n\t * // Launch constantly the particles, like a fountain\n\t * emitter.streamParticles();\n\t *\n\t * // At the end, remove emitter from the game world\n\t * // call this in onDestroyEvent function\n\t * app.world.removeChild(emitter);\n\t */\n\tconstructor(\n\t\tx: number,\n\t\ty: number,\n\t\tsettings: Partial<ParticleEmitterSettings> = {},\n\t) {\n\t\t// call the super constructor — `??` (not `||`) so a deliberate width/height\n\t\t// of 0 (point emitter) survives, only `undefined` falls back to 1.\n\t\tsuper(x, y, settings.width ?? 1, settings.height ?? 1);\n\n\t\t// settings will be fully populated by reset() below; start with defaults\n\t\tthis.settings = { ...defaultEmitterSettings };\n\n\t\t// center the emitter around the given coordinates\n\t\tthis.centerOn(x, y);\n\n\t\t// stream mode flag\n\t\tthis._stream = false;\n\t\t// frequency timer (ms) — stream mode only\n\t\tthis._frequencyTimer = 0;\n\t\t// duration timer (ms) — stream mode only\n\t\tthis._durationTimer = 0;\n\t\t// whether the emitter is currently emitting\n\t\tthis._enabled = false;\n\t\t// emitter ticks regardless of viewport\n\t\tthis.alwaysUpdate = true;\n\t\t// preserve insertion order — particle z-sort would be wasted work\n\t\tthis.autoSort = false;\n\t\t// frame-skip bookkeeping\n\t\tthis._updateCount = 0;\n\t\tthis._dt = 0;\n\t\t// completion tracking\n\t\tthis._hasSpawned = false;\n\t\t// per-spawn delta inverse — populated by reset()\n\t\tthis._deltaInv = timer.maxfps / 1000;\n\n\t\t// Apply user overrides + clamp range pairs\n\t\tthis.reset(settings);\n\t}\n\n\t/**\n\t * Reset the emitter with particle emitter settings.\n\t * @param settings - object with emitter settings. See {@link ParticleEmitterSettings}\n\t */\n\toverride reset(settings: Partial<ParticleEmitterSettings> = {}): void {\n\t\tObject.assign(this.settings, defaultEmitterSettings, settings);\n\n\t\t// Clamp range-style settings: if `min > max`, lower `min` to `max`.\n\t\t// Catches the common footgun where a user overrides only one half of\n\t\t// a min/max pair (e.g. `maxLife: 5` while `minLife` keeps its 1000 ms\n\t\t// default), which would otherwise produce a wide unintended range.\n\t\tclampMinToMax(this.settings, \"minLife\", \"maxLife\");\n\t\tclampMinToMax(this.settings, \"minStartScale\", \"maxStartScale\");\n\t\tclampMinToMax(this.settings, \"minEndScale\", \"maxEndScale\");\n\t\tclampMinToMax(this.settings, \"minRotation\", \"maxRotation\");\n\n\t\t// refresh the cached delta inverse — `timer.maxfps` is constant after\n\t\t// boot but reset() runs after VIDEO_INIT, so this is the safest place.\n\t\tthis._deltaInv = timer.maxfps / 1000;\n\n\t\t// dispose any previously-created fallback texture. Switching to a\n\t\t// user-provided image *or* re-creating the default fallback both make\n\t\t// the old CanvasRenderTarget unreachable, so destroy it eagerly to\n\t\t// avoid a leak across repeated reset() calls.\n\t\tif (typeof this._defaultParticle !== \"undefined\") {\n\t\t\tthis._defaultParticle.destroy();\n\t\t\tthis._defaultParticle = undefined;\n\t\t}\n\t\tif (typeof this.settings.image === \"undefined\") {\n\t\t\tthis._defaultParticle = createDefaultParticleTexture(\n\t\t\t\tthis.settings.textureSize,\n\t\t\t\tthis.settings.textureSize,\n\t\t\t);\n\t\t\tthis.settings.image = this._defaultParticle.canvas;\n\t\t}\n\n\t\tthis.floating = this.settings.floating;\n\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * returns a random point on the x axis within the bounds of this emitter\n\t * @returns a random x position within the emitter bounds\n\t */\n\tgetRandomPointX(): number {\n\t\treturn randomFloat(0, this.getBounds().width);\n\t}\n\n\t/**\n\t * returns a random point on the y axis within the bounds this emitter\n\t * @returns a random y position within the emitter bounds\n\t */\n\tgetRandomPointY(): number {\n\t\treturn randomFloat(0, this.getBounds().height);\n\t}\n\n\t// Add count particles in the game world\n\t/** @ignore */\n\taddParticles(count: number): void {\n\t\t// Propagate the emitter's depth onto each new particle via\n\t\t// `Container.addChild(child, z)` so Camera3d projects them at\n\t\t// the emitter's z slice (not at the world origin, which would\n\t\t// place them on the wrong perspective plane for explosions /\n\t\t// exhaust trails attached to a moving Mesh).\n\t\t// `Renderable.depth` proxies to `pos.z` — same value, no cast.\n\t\tconst z = this.depth;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\t// Add particle to the container\n\t\t\tthis.addChild(particlePool.get(this), z);\n\t\t}\n\t\tif (count > 0) {\n\t\t\tthis._hasSpawned = true;\n\t\t}\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Emitter is of type stream and is launching particles\n\t * @returns Emitter is Stream and is launching particles\n\t */\n\tisRunning(): boolean {\n\t\treturn this._enabled && this._stream;\n\t}\n\n\t/**\n\t * Launch particles from emitter constantly (e.g. for stream)\n\t * @param [duration] - time that the emitter releases particles in ms\n\t */\n\tstreamParticles(duration?: number): void {\n\t\tthis._enabled = true;\n\t\tthis._stream = true;\n\t\tthis.settings.frequency = Math.max(1, this.settings.frequency);\n\t\tthis._durationTimer =\n\t\t\ttypeof duration === \"number\" ? duration : this.settings.duration;\n\t}\n\n\t/**\n\t * Stop the emitter from generating new particles (used only if emitter is Stream)\n\t */\n\tstopStream(): void {\n\t\tthis._enabled = false;\n\t}\n\n\t/**\n\t * Launch all particles from emitter and stop (e.g. for explosion)\n\t * @param [total] - number of particles to launch\n\t */\n\tburstParticles(total?: number): void {\n\t\tthis._enabled = true;\n\t\tthis._stream = false;\n\t\tthis.addParticles(\n\t\t\ttypeof total === \"number\" ? total : this.settings.totalParticles,\n\t\t);\n\t\tthis._enabled = false;\n\t}\n\n\t/**\n\t * @ignore\n\t */\n\toverride update(dt: number): boolean {\n\t\t// frame-skip: only do the bookkeeping when actually configured.\n\t\t// Defaults to 0 (every frame), and that path is the hot one.\n\t\tif (this.settings.framesToSkip > 0) {\n\t\t\tif (++this._updateCount > this.settings.framesToSkip) {\n\t\t\t\tthis._updateCount = 0;\n\t\t\t}\n\t\t\tif (this._updateCount > 0) {\n\t\t\t\tthis._dt += dt;\n\t\t\t\treturn this.isDirty;\n\t\t\t}\n\t\t\tdt += this._dt;\n\t\t\tthis._dt = 0;\n\t\t}\n\n\t\t// Update particles. `super.update(dt)` walks every child\n\t\t// (each Particle) through Container.update — visibility check,\n\t\t// per-particle `update(dt)`, etc. CRITICAL: assign it to a\n\t\t// local first, then OR into `this.isDirty`. Writing this as\n\t\t// `this.isDirty = this.isDirty || super.update(dt)` would\n\t\t// short-circuit when `isDirty` is already true (very common),\n\t\t// silently skipping the whole child walk — particles would\n\t\t// keep `inViewport = false` and never draw.\n\t\tconst childrenDirty = super.update(dt);\n\t\tthis.isDirty = this.isDirty || childrenDirty;\n\n\t\t// Launch new particles, if emitter is Stream\n\t\tif (this._enabled && this._stream) {\n\t\t\t// Check if the emitter has duration set\n\t\t\tif (this._durationTimer !== Infinity) {\n\t\t\t\tthis._durationTimer -= dt;\n\n\t\t\t\tif (this._durationTimer <= 0) {\n\t\t\t\t\tthis.stopStream();\n\t\t\t\t\treturn this.isDirty;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Increase the emitter launcher timer\n\t\t\tthis._frequencyTimer += dt;\n\n\t\t\t// Check for new particles launch\n\t\t\tconst particlesCount = this.getChildren().length;\n\t\t\tif (\n\t\t\t\tparticlesCount < this.settings.totalParticles &&\n\t\t\t\tthis._frequencyTimer >= this.settings.frequency\n\t\t\t) {\n\t\t\t\tthis.addParticles(\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tthis.settings.maxParticles,\n\t\t\t\t\t\tthis.settings.totalParticles - particlesCount,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tthis._frequencyTimer = 0;\n\t\t\t\tthis.isDirty = true;\n\t\t\t}\n\t\t}\n\n\t\t// completion detection — fires once after at least one particle has\n\t\t// been spawned, the emitter is no longer producing new ones, and all\n\t\t// children have died. Covers both burst (instant _enabled = false) and\n\t\t// stream (duration elapsed → stopStream()).\n\t\tif (this._hasSpawned && !this._enabled && this.getChildren().length === 0) {\n\t\t\t// guard against re-entry: clear the flag so we only fire once\n\t\t\tthis._hasSpawned = false;\n\t\t\tif (typeof this.settings.onComplete === \"function\") {\n\t\t\t\tthis.settings.onComplete.call(this);\n\t\t\t}\n\t\t\tif (this.settings.autoDestroyOnComplete && this.ancestor) {\n\t\t\t\t(this.ancestor as Container).removeChild(this);\n\t\t\t}\n\t\t}\n\n\t\treturn this.isDirty;\n\t}\n\n\t/**\n\t * Destroy function\n\t * @ignore\n\t */\n\toverride destroy(): void {\n\t\tsuper.destroy();\n\t\tif (typeof this._defaultParticle !== \"undefined\") {\n\t\t\tthis._defaultParticle.destroy();\n\t\t\tthis._defaultParticle = undefined;\n\t\t}\n\t\t// Clear the image reference so a discarded emitter doesn't pin a\n\t\t// (potentially large) image / canvas alive via `settings.image`.\n\t\t// `this.settings = undefined` was here too but offered no GC win\n\t\t// — once the emitter itself is unreferenced, `settings` follows —\n\t\t// and it required a `as unknown as ParticleEmitterSettings` cast\n\t\t// that lied about the field type. Dropped.\n\t\tthis.settings.image = undefined;\n\t}\n}\n","import type Application from \"./../application/application.ts\";\nimport { game } from \"../application/application.ts\";\nimport { checkVersion } from \"./../utils/utils.ts\";\nimport { version } from \"./../version.ts\";\n\n/**\n * Contains all registered plugins.\n */\nexport const cache: Record<string, BasePlugin> = {};\n\n/**\n * @namespace plugin\n */\n\n/**\n * a base Object class for plugin\n * (plugin must be installed using the register function)\n */\nexport class BasePlugin {\n\t/**\n\t * define the minimum required version of melonJS\n\t * this can be overridden by the plugin\n\t */\n\tversion: string;\n\n\t/**\n\t * a reference to the app/game that registered this plugin\n\t */\n\tapp: Application;\n\n\tconstructor(app: Application = game) {\n\t\tthis.version = version;\n\t\tthis.app = app;\n\t}\n}\n\n/**\n * patch a melonJS function\n * @param proto - target object\n * @param name - target function\n * @param fn - replacement function\n * @example\n * // redefine the app.update function with a new one\n * me.plugin.patch(app, \"update\", function () {\n *   // display something in the console\n *   console.log(\"duh\");\n *   // call the original app.update function\n *   this._patched();\n * });\n */\nexport function patch(\n\tproto: Record<string, any>,\n\tname: string,\n\tfn: (...args: any[]) => any,\n): void {\n\t// use the object prototype if possible\n\tif (typeof proto.prototype !== \"undefined\") {\n\t\tproto = proto.prototype;\n\t}\n\t// reuse the logic behind object extends\n\tif (typeof proto[name] === \"function\") {\n\t\t// save the original function\n\t\tconst _parent = proto[name];\n\t\t// override the function with the new one\n\t\tObject.defineProperty(proto, name, {\n\t\t\tconfigurable: true,\n\t\t\tvalue: (function (_name: string, fn: (...args: any[]) => any) {\n\t\t\t\treturn function (this: any, ...args: any[]) {\n\t\t\t\t\tthis._patched = _parent;\n\t\t\t\t\tconst ret = fn.apply(this, args);\n\t\t\t\t\tthis._patched = null;\n\t\t\t\t\treturn ret;\n\t\t\t\t};\n\t\t\t})(name, fn),\n\t\t});\n\t} else {\n\t\tthrow new Error(`${name} is not an existing function`);\n\t}\n}\n\n/**\n * Register a plugin.\n * @param pluginClass - Plugin class to instantiate and register\n * @param name - a unique name for this plugin\n * @param args - all extra parameters will be passed to the plugin constructor\n * @example\n * // register a new plugin\n * me.plugin.register(TestPlugin, \"testPlugin\");\n * // the `testPlugin` class instance can also be accessed through me.plugin.cache\n * me.plugin.cache.testPlugin.myfunction ();\n */\nexport function register(\n\tpluginClass: new (...args: any[]) => BasePlugin,\n\tname?: string,\n\t...args: any[]\n): void {\n\t// derive name from class if not provided\n\tconst pluginName =\n\t\tname || pluginClass.name || pluginClass.toString().match(/ (\\w+)/)![1];\n\n\t// ensure me.plugins[name] is not already \"used\"\n\tif (cache[pluginName]) {\n\t\tthrow new Error(`plugin ${pluginName} already registered`);\n\t}\n\n\t// try to instantiate the plugin\n\tconst instance = new pluginClass(...args);\n\n\t// inheritance check\n\tif (typeof instance === \"undefined\" || !(instance instanceof BasePlugin)) {\n\t\tthrow new Error(\"Plugin should extend the BasePlugin Class !\");\n\t}\n\n\t// compatibility testing\n\tif (checkVersion(instance.version, version) > 0) {\n\t\tthrow new Error(\n\t\t\t`Plugin version mismatch, expected: ${instance.version}, got: ${version}`,\n\t\t);\n\t}\n\n\t// create a reference to the new plugin\n\tcache[pluginName] = instance;\n}\n\n/**\n * returns the plugin instance with the specified class type or registered name\n * @param classType - the Class Object or registered name of the plugin to retrieve\n * @returns a plugin instance or undefined\n */\nexport function get(\n\tclassType: string | (new (...args: any[]) => BasePlugin),\n): BasePlugin | undefined {\n\tfor (const name in cache) {\n\t\tif (typeof classType === \"string\") {\n\t\t\tif (classType === name) {\n\t\t\t\treturn cache[name];\n\t\t\t}\n\t\t} else if (cache[name] instanceof classType) {\n\t\t\treturn cache[name];\n\t\t}\n\t}\n}\n","import * as input from \"./../input/input.ts\";\nimport { Vector2d } from \"../math/vector2d.ts\";\nimport Renderable from \"./../renderable/renderable.js\";\nimport {\n\tDRAGEND,\n\tDRAGSTART,\n\temit,\n\toff,\n\ton,\n\tPOINTERMOVE,\n} from \"../system/event.ts\";\n\n/**\n * A Draggable base object\n * @see DropTarget\n * @category Game Objects\n */\nexport class Draggable extends Renderable {\n\t/**\n\t * @param {number} x - the x coordinates of the draggable object\n\t * @param {number} y - the y coordinates of the draggable object\n\t * @param {number} width - draggable object width\n\t * @param {number} height - draggable object height\n\t */\n\tconstructor(x, y, width, height) {\n\t\tsuper(x, y, width, height);\n\t\tthis.isKinematic = false;\n\t\tthis.dragging = false;\n\t\tthis.dragId = null;\n\t\tthis.grabOffset = new Vector2d(0, 0);\n\t\tthis.initEvents();\n\t}\n\n\t/**\n\t * Initializes the events the modules needs to listen to\n\t * It translates the pointer events to me.events\n\t * in order to make them pass through the system and to make\n\t * this module testable. Then we subscribe this module to the\n\t * transformed events.\n\t * @private\n\t */\n\tinitEvents() {\n\t\tinput.registerPointerEvent(\"pointerdown\", this, (e) => {\n\t\t\temit(DRAGSTART, e, this);\n\t\t});\n\t\tinput.registerPointerEvent(\"pointerup\", this, (e) => {\n\t\t\temit(DRAGEND, e, this);\n\t\t});\n\t\tinput.registerPointerEvent(\"pointercancel\", this, (e) => {\n\t\t\temit(DRAGEND, e, this);\n\t\t});\n\n\t\tthis.handlePointerMove = (e) => {\n\t\t\tthis.dragMove(e);\n\t\t};\n\n\t\tthis.handleDragStart = (e, draggable) => {\n\t\t\tif (draggable === this) {\n\t\t\t\tthis.dragStart(e);\n\t\t\t}\n\t\t};\n\n\t\tthis.handleDragEnd = (e, draggable) => {\n\t\t\tif (draggable === this) {\n\t\t\t\tthis.dragEnd(e);\n\t\t\t}\n\t\t};\n\n\t\ton(POINTERMOVE, this.handlePointerMove);\n\t\ton(DRAGSTART, this.handleDragStart);\n\t\ton(DRAGEND, this.handleDragEnd);\n\t}\n\n\t/**\n\t * Gets called when the user starts dragging the entity\n\t * @param {object} e - the pointer event\n\t * @returns {boolean} false if the object is being dragged\n\t */\n\tdragStart(e) {\n\t\tif (this.dragging === false) {\n\t\t\tthis.dragging = true;\n\t\t\tthis.grabOffset.set(e.gameX, e.gameY);\n\t\t\tthis.grabOffset.sub(this.pos);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Gets called when the user drags this entity around\n\t * @param {object} e - the pointer event\n\t */\n\tdragMove(e) {\n\t\tif (this.dragging === true) {\n\t\t\tthis.pos.set(e.gameX, e.gameY, this.pos.z);\n\t\t\tthis.pos.sub(this.grabOffset);\n\t\t}\n\t}\n\n\t/**\n\t * Gets called when the user stops dragging the entity\n\t * @returns {boolean} false if the object stopped being dragged\n\t */\n\tdragEnd() {\n\t\tif (this.dragging === true) {\n\t\t\tthis.dragging = false;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Destructor\n\t * @ignore\n\t */\n\tdestroy() {\n\t\toff(POINTERMOVE, this.handlePointerMove);\n\t\toff(DRAGSTART, this.handleDragStart);\n\t\toff(DRAGEND, this.handleDragEnd);\n\t\tinput.releasePointerEvent(\"pointerdown\", this);\n\t\tinput.releasePointerEvent(\"pointerup\", this);\n\t\tinput.releasePointerEvent(\"pointercancel\", this);\n\t\tsuper.destroy();\n\t}\n}\n","import Renderable from \"./../renderable/renderable.js\";\nimport { DRAGEND, on } from \"../system/event.ts\";\n\n/**\n * additional import for TypeScript\n * @import { Draggable } from \"./draggable.js\";\n */\n\n/**\n * a base drop target object\n * @see Draggable\n */\nexport class DropTarget extends Renderable {\n\t/**\n\t * @param {number} x - the x coordinates of the drop target\n\t * @param {number} y - the y coordinates of the drop target\n\t * @param {number} width - drop target width\n\t * @param {number} height - drop target height\n\t */\n\tconstructor(x, y, width, height) {\n\t\tsuper(x, y, width, height);\n\n\t\tthis.isKinematic = false;\n\n\t\t/**\n\t\t * constant for the overlaps method\n\t\t * @constant\n\t\t * @type {string}\n\t\t * @name CHECKMETHOD_OVERLAP\n\t\t */\n\t\tthis.CHECKMETHOD_OVERLAP = \"overlaps\";\n\n\t\t/**\n\t\t * constant for the contains method\n\t\t * @constant\n\t\t * @type {string}\n\t\t * @name CHECKMETHOD_CONTAINS\n\t\t */\n\t\tthis.CHECKMETHOD_CONTAINS = \"contains\";\n\n\t\t/**\n\t\t * the checkmethod we want to use\n\t\t * @constant\n\t\t * @type {string}\n\t\t * @name checkMethod\n\t\t * @default \"CHECKMETHOD_OVERLAP\"\n\t\t */\n\t\tthis.checkMethod = this.CHECKMETHOD_OVERLAP;\n\n\t\tthis.removeDragEndListener = on(DRAGEND, this.checkOnMe, this);\n\t}\n\n\t/**\n\t * Sets the collision method which is going to be used to check a valid drop\n\t * @param {string} checkMethod - the checkmethod (defaults to CHECKMETHOD_OVERLAP)\n\t */\n\tsetCheckMethod(checkMethod) {\n\t\t//  We can improve this check,\n\t\t//  because now you can use every method in theory\n\t\tif (typeof this.getBounds()[checkMethod] === \"function\") {\n\t\t\tthis.checkMethod = checkMethod;\n\t\t}\n\t}\n\n\t/**\n\t * Checks if a dropped entity is dropped on the current entity\n\t * @param {object} e - the triggering event\n\t * @param {Draggable} draggable - the draggable object that is dropped\n\t */\n\tcheckOnMe(e, draggable) {\n\t\tif (\n\t\t\tdraggable &&\n\t\t\tthis.getBounds()[this.checkMethod](draggable.getBounds())\n\t\t) {\n\t\t\t// call the drop method on the current entity\n\t\t\tthis.drop(draggable);\n\t\t}\n\t}\n\n\t/**\n\t * Gets called when a draggable entity is dropped on the current entity\n\t * @param {Draggable} draggable - the draggable object that is dropped\n\t */\n\tdrop() {}\n\n\t/**\n\t * Destructor\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tthis.removeDragEndListener();\n\t\tsuper.destroy();\n\t}\n}\n","import Camera3d from \"../camera/camera3d.ts\";\nimport { getImage } from \"../loader/loader.js\";\nimport { Matrix3d } from \"../math/matrix3d.ts\";\nimport { Vector3d } from \"../math/vector3d.ts\";\nimport Texture2d from \"../video/texture/texture2d.ts\";\nimport { resolveAnchorPoint } from \"./anchorPoint.ts\";\nimport FrameAnimation from \"./frameAnimation.js\";\nimport Mesh from \"./mesh.js\";\n\n// reusable basis vectors for the billboard projection (one draw runs at a time)\nconst _right = new Vector3d();\nconst _up = new Vector3d();\nconst _fwd = new Vector3d();\n// render space is Y-down, so the visual \"up\" axis is -Y. Used to keep a\n// cylindrical billboard upright. MUST be treated as read-only — it is a shared\n// module singleton, only ever copied/crossed-from, never mutated in place.\nconst WORLD_UP = new Vector3d(0, -1, 0);\n\n// Frustum-cull side length for an anchored quad: Camera3d derives its cull\n// sphere radius as √(w²+h²)/2 from the bounds, so passing side = radius·√2\n// with radius = the farthest corner's distance from `pos` (rotation about\n// `pos` under billboarding preserves corner distances) makes the sphere\n// exactly enclose the quad in any orientation. Never smaller than the legacy\n// centered bounds (max of the quad's sides), so the default is unchanged.\nfunction cullSideForAnchor(hw, hh, offsetX, offsetY) {\n\treturn Math.max(\n\t\tMath.max(hw, hh) * 2,\n\t\tMath.SQRT2 * Math.hypot(hw + Math.abs(offsetX), hh + Math.abs(offsetY)),\n\t);\n}\n\n// Write the plain (frame-less) anchored quad into `out` (12 floats):\n// corners at ±hw/±hh shifted by the anchor offset, z = 0. Used for the\n// constructor bake and for runtime anchor changes on the no-region path.\nfunction bakeQuadVertices(hw, hh, offsetX, offsetY, out) {\n\tout[0] = -hw + offsetX;\n\tout[1] = -hh + offsetY;\n\tout[2] = 0;\n\tout[3] = hw + offsetX;\n\tout[4] = -hh + offsetY;\n\tout[5] = 0;\n\tout[6] = hw + offsetX;\n\tout[7] = hh + offsetY;\n\tout[8] = 0;\n\tout[9] = -hw + offsetX;\n\tout[10] = hh + offsetY;\n\tout[11] = 0;\n\treturn out;\n}\n\n// Resolve just the source image dimensions for sizing the quad, without\n// touching the renderer (the texture atlas itself is resolved by the Mesh\n// base class). Runs before `super()`, so it must not touch `this`.\nfunction imageSize(settings) {\n\tconst src = settings.image ?? settings.texture;\n\t// any Texture2d asset (a TextureAtlas, or a procedural NoiseTexture2d)\n\t// exposes its drawable source via getTexture()\n\tif (src instanceof Texture2d) {\n\t\tconst texture = src.getTexture();\n\t\treturn { w: texture.width, h: texture.height };\n\t}\n\tconst image = typeof src === \"object\" ? src : getImage(src);\n\tif (!image) {\n\t\tthrow new Error(\"Sprite3d: '\" + src + \"' image/texture not found!\");\n\t}\n\treturn { w: image.width, h: image.height };\n}\n\n/**\n * A textured **quad in 3D space** — the 3D counterpart of {@link Sprite},\n * for rendering sprites under a {@link Camera3d} (the 2.5D workflow: characters,\n * pickups, foliage, signs, particles in a 3D scene). It's a thin {@link Mesh}\n * subclass, so it rides the same world-space mesh pipeline (depth testing,\n * frustum culling) and supports the same material features (`lit`, `emissive`,\n * `alphaCutoff`).\n *\n * Its headline feature is **billboarding** — keeping the quad facing the camera\n * regardless of camera orientation (see {@link Sprite3d#billboard}). With\n * billboarding off it's a fixed-orientation quad (decals, posters, ground\n * markers).\n *\n * **Frame animation** is supported through the same API as {@link Sprite}\n * ({@link Sprite3d#addAnimation}, {@link Sprite3d#setCurrentAnimation},\n * {@link Sprite3d#play}/`pause`/`stop`) — pass `framewidth`/`frameheight` for a\n * spritesheet, or a packed {@link TextureAtlas}, exactly as you would for a 2D\n * `Sprite`. Both share the {@link FrameAnimation} engine, so the timing, looping\n * and chaining behavior is identical; `Sprite3d` maps the current frame onto the\n * quad each step — including packer **rotated** and **trimmed** regions, mapped\n * to full parity with the 2D `Sprite`.\n *\n * **Camera3d only.** Like {@link Mesh}, `Sprite3d` renders through the 3D\n * world-space path; under a 2D `Camera2d` it falls back to the mesh's\n * self-projection and **billboarding has no effect** (a 2D scene has no camera\n * orientation to face). Use a regular {@link Sprite} for 2D.\n *\n * **Anchoring.** Unlike its {@link Mesh} parent — where `anchorPoint` is inert\n * on the 3D path and transforms pivot at the model origin — Sprite3d's\n * `anchorPoint` is live: the anchor is baked into the quad's local vertices\n * (never applied as a renderer transform on either camera path) and can be\n * changed at runtime via `anchorPoint.set(...)`, which re-bakes the quad and\n * re-derives the cull bounds. Same key, convention and centered default as\n * the 2D {@link Sprite}, including the named presets (`\"bottom\"`, …).\n * @augments Mesh\n * @category Game Objects\n * @example\n * import { Application, Camera3d, Sprite3d } from \"melonjs\";\n *\n * // a 3D app (Camera3d is required for billboarding)\n * const app = new Application(1024, 768, { cameraClass: Camera3d });\n *\n * // a tree that always faces the camera but stays upright (2.5D).\n * // its texture has a transparent background — the mesh pass is opaque, so\n * // `alphaCutoff` (default 0.5) discards those texels for a clean silhouette.\n * const tree = new Sprite3d(0, 0, {\n *     image: \"tree\",        // a preloaded image with transparency\n *     width: 64, height: 96,\n *     z: -200,              // 3D depth (world z)\n *     billboard: true,      // = \"cylindrical\"\n *     // alphaCutoff: 0.5,  // the default — lower it to keep softer edges,\n *                           // or set 0 for a fully-opaque quad\n * });\n * app.world.addChild(tree);   // add it to the game world, like any Renderable\n *\n * // an animated, fully camera-facing pickup from a spritesheet, mirrored\n * const coin = new Sprite3d(0, 0, {\n *     image: \"coins\",\n *     framewidth: 32, frameheight: 32,\n *     width: 48, height: 48,\n *     billboard: \"spherical\",\n *     alphaCutoff: 0.5,     // cut out the transparent frame background\n * });\n * coin.addAnimation(\"spin\", [0, 1, 2, 3, 4, 5]);\n * coin.setCurrentAnimation(\"spin\");\n * coin.flipX();             // face the other way (mirrors the sprite)\n * app.world.addChild(coin);\n *\n * // a character anchored at the feet, so `pos` sits on the ground plane\n * // instead of at the sprite's geometric center\n * const hero = new Sprite3d(0, 0, {\n *     image: \"hero\",\n *     width: 48, height: 64,\n *     billboard: \"cylindrical\",\n *     anchorPoint: \"bottom\", // == { x: 0.5, y: 1 }\n * });\n */\nexport default class Sprite3d extends Mesh {\n\t/**\n\t * @param {number} x - world x position\n\t * @param {number} y - world y position\n\t * @param {object} settings - configuration\n\t * @param {HTMLImageElement|Texture2d|string} [settings.image] - the sprite texture (image name, image, or a {@link Texture2d} asset such as a {@link TextureAtlas}). Alias: `settings.texture`.\n\t * @param {number} [settings.width=settings.framewidth] - quad width in world units (pixels)\n\t * @param {number} [settings.height=settings.width] - quad height in world units\n\t * @param {number} [settings.framewidth] - width of a single frame within a spritesheet (enables frame animation)\n\t * @param {number} [settings.frameheight] - height of a single frame within a spritesheet\n\t * @param {string} [settings.region] - region name when using a texture atlas (see {@link TextureAtlas})\n\t * @param {object[]} [settings.anims] - predefined animations (same shape as {@link Sprite})\n\t * @param {number} [settings.z=0] - 3D depth (world z); also settable later via `.depth`\n\t * @param {boolean|string} [settings.billboard=false] - billboard mode: `false` (fixed orientation), `true` / `\"cylindrical\"` (faces the camera but stays upright — the 2.5D default), or `\"spherical\"` (faces the camera on all axes). Only applies under a `Camera3d`.\n\t * @param {string|Vector2d|{x:number,y:number}} [settings.anchorPoint={x:0.5,y:0.5}] - anchor of the quad relative to `pos` — same key, normalized 0..1 convention (`x`: 0 left→1 right, `y`: 0 top→1 bottom) and centered default as the 2D {@link Sprite}. Also accepts the named presets `\"center\"`, `\"top\"`, `\"bottom\"`, `\"left\"`, `\"right\"`, `\"top-left\"`, `\"top-right\"`, `\"bottom-left\"`, `\"bottom-right\"`. Baked into the quad's local vertices (composes with billboarding, flips, and trimmed/rotated atlas frames) — never applied as a renderer transform on either camera path. Mutable at runtime via `this.anchorPoint.set(...)` (re-bakes the quad and re-derives the cull bounds). Invalid values throw.\n\t * @param {boolean} [settings.flipX=false] - mirror the sprite horizontally (see {@link Sprite3d#flipX})\n\t * @param {boolean} [settings.flipY=false] - mirror the sprite vertically (see {@link Sprite3d#flipY})\n\t * @param {boolean} [settings.lit=false] - shade through the lit mesh batcher (see {@link Mesh})\n\t * @param {number[]|Float32Array} [settings.emissive] - emissive color (see {@link Mesh})\n\t * @param {number} [settings.alphaCutoff=0.5] - alpha cutout threshold (see {@link Mesh}). The mesh pass is opaque (no alpha blending), so this defaults to `0.5` to discard a sprite's transparent background (clean cutout silhouette, correct depth, no sorting). Set `0` for a fully-opaque quad, or tune the threshold.\n\t */\n\tconstructor(x, y, settings) {\n\t\t// world-space quad size (in pixels): explicit width/height first, then the\n\t\t// spritesheet frame size, then the full texture size. The texture atlas\n\t\t// itself is resolved by the Mesh base class (passing framewidth/frameheight\n\t\t// through, below) — so Sprite3d never touches the renderer directly.\n\t\tlet w;\n\t\tlet h;\n\t\tif (typeof settings.width === \"number\") {\n\t\t\tw = settings.width;\n\t\t\th = typeof settings.height === \"number\" ? settings.height : w;\n\t\t} else if (typeof settings.framewidth === \"number\") {\n\t\t\tw = settings.framewidth;\n\t\t\th = typeof settings.frameheight === \"number\" ? settings.frameheight : w;\n\t\t} else {\n\t\t\tconst size = imageSize(settings);\n\t\t\tw = size.w;\n\t\t\th = size.h;\n\t\t}\n\t\tconst hw = w / 2;\n\t\tconst hh = h / 2;\n\t\t// local-space offset shifting the quad so `anchorPoint` — not the\n\t\t// geometric center — lands at `pos`. Resolved PRE-super: the quad\n\t\t// vertices below are built before super(), and the inherited\n\t\t// ObservablePoint doesn't exist yet (it is wired up post-super).\n\t\t// X follows the image convention directly (0=left…1=right). Y is\n\t\t// inverted: local +Y is the TOP of the art (see the UV/trim mapping in\n\t\t// `_applyFrame` and `WORLD_UP` below), while `anchorPoint.y` follows\n\t\t// the image convention of 0=top…1=bottom.\n\t\tconst anchor = settings.anchorPoint\n\t\t\t? resolveAnchorPoint(settings.anchorPoint, \"Sprite3d\")\n\t\t\t: { x: 0.5, y: 0.5 };\n\t\tconst anchorOffsetX = (0.5 - anchor.x) * w;\n\t\tconst anchorOffsetY = (anchor.y - 0.5) * h;\n\t\t// a unit quad with the real pixel size baked in, in the XY plane, facing\n\t\t// +Z, shifted by the anchor offset. `normalize: false` + `scale: 1` keeps\n\t\t// these coordinates as-is so the fixed-orientation (non-billboard) case\n\t\t// renders at the right size, and the billboard path reuses the local\n\t\t// (±hw + offset, ±hh + offset) offsets directly.\n\t\tconst vertices = bakeQuadVertices(\n\t\t\thw,\n\t\t\thh,\n\t\t\tanchorOffsetX,\n\t\t\tanchorOffsetY,\n\t\t\tnew Float32Array(12),\n\t\t);\n\t\t// V flipped (1→0 top to bottom) so the texture renders upright under the\n\t\t// Y-down render space, matching Sprite. Overwritten per-frame by\n\t\t// `_applyFrame` once an animation/region is selected.\n\t\tconst uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0, 0]);\n\t\tconst indices = new Uint16Array([0, 1, 2, 0, 2, 3]);\n\t\tconst normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);\n\n\t\tsuper(x, y, {\n\t\t\tvertices,\n\t\t\tuvs,\n\t\t\tindices,\n\t\t\tnormals,\n\t\t\t// let Mesh resolve the texture; forward the spritesheet grid so the\n\t\t\t// resolved atlas carries animation frames\n\t\t\ttexture: settings.image ?? settings.texture,\n\t\t\tframewidth: settings.framewidth,\n\t\t\tframeheight: settings.frameheight,\n\t\t\t// width/height drive the frustum-cull bounds. Camera3d culls with a\n\t\t\t// sphere centered on `pos` of radius √(w²+h²)/2 = side/√2; the\n\t\t\t// billboard corners orbit `pos` at radius hypot(hw+|offX|, hh+|offY|)\n\t\t\t// (rotation about `pos` preserves corner distances, and trimmed\n\t\t\t// frames stay inside the ±hw/±hh box). Passing side = radius·√2\n\t\t\t// makes the sphere exactly enclose the quad in ANY orientation.\n\t\t\t// For the centered default this degrades to max(w, h) — unchanged\n\t\t\t// legacy bounds; a non-center anchor grows them just enough that\n\t\t\t// e.g. a bottom-anchored character can't pop out while its head is\n\t\t\t// still on screen.\n\t\t\twidth: cullSideForAnchor(hw, hh, anchorOffsetX, anchorOffsetY),\n\t\t\theight: cullSideForAnchor(hw, hh, anchorOffsetX, anchorOffsetY),\n\t\t\tscale: 1,\n\t\t\tnormalize: false,\n\t\t\trightHanded: true,\n\t\t\t// a sprite quad should be visible from both sides (a billboard can end\n\t\t\t// up wound either way, and a fixed quad is usually viewed front-on)\n\t\t\tcullBackFaces: false,\n\t\t\tlit: settings.lit === true,\n\t\t\temissive: settings.emissive,\n\t\t\t// the mesh pass is opaque (no alpha blending), so a sprite's\n\t\t\t// transparent background would otherwise render as a solid box. Use an\n\t\t\t// alpha cutout by default — fragments below the threshold are\n\t\t\t// discarded, giving a clean transparent silhouette with correct depth\n\t\t\t// testing and no back-to-front sorting. Pass `alphaCutoff: 0` to\n\t\t\t// disable (fully opaque), or tune the threshold.\n\t\t\talphaCutoff:\n\t\t\t\ttypeof settings.alphaCutoff === \"number\" ? settings.alphaCutoff : 0.5,\n\t\t\t// ground shadow (#1515) — forwarded explicitly, like everything\n\t\t\t// else here: this constructor hands `Mesh` a built settings object\n\t\t\t// rather than the caller's, so anything not named is dropped\n\t\t\t// passed through RAW, not coerced: `undefined` is meaningful here —\n\t\t\t// it means \"follow the application setting\" — and `=== true` would\n\t\t\t// flatten it to an explicit false, silently opting every sprite out\n\t\t\t// of a scene-wide default\n\t\t\tcastGroundShadow: settings.castGroundShadow,\n\t\t\tshadowGroundY: settings.shadowGroundY,\n\t\t\tshadowOpacity: settings.shadowOpacity,\n\t\t});\n\n\t\t/**\n\t\t * the quad's half-width in world units (see {@link Sprite3d#billboard}).\n\t\t * @type {number}\n\t\t * @ignore\n\t\t */\n\t\tthis._halfW = hw;\n\t\t/** @ignore */\n\t\tthis._halfH = hh;\n\t\t// local-space offset pinning `anchorPoint` to `pos` (0,0 = centered,\n\t\t// the default). Applied in `_applyFrame` after the flip, so mirroring\n\t\t// happens around the art's own center while the anchor stays fixed.\n\t\t/** @ignore */\n\t\tthis._anchorOffsetX = anchorOffsetX;\n\t\t/** @ignore */\n\t\tthis._anchorOffsetY = anchorOffsetY;\n\t\t// the anchor lives in the vertex data — tell Mesh.preDraw to suppress\n\t\t// the base transform-level anchor on BOTH camera paths (single\n\t\t// anchoring mechanism; see Mesh#_anchorBaked)\n\t\tthis._anchorBaked = true;\n\t\t// Repurpose the inherited anchorPoint as the LIVE anchor API (unlike\n\t\t// Mesh, where it stays inert on the world path): setMuted first — the\n\t\t// base updateBounds callback must not fire on a half-initialized\n\t\t// object — then swap in the re-bake callback for runtime changes.\n\t\tthis.anchorPoint.setMuted(anchor.x, anchor.y);\n\t\tthis.anchorPoint.setCallback(() => {\n\t\t\tthis._onAnchorChanged();\n\t\t});\n\t\t// reference logical (untrimmed) frame size the world quad maps to,\n\t\t// captured from the first applied frame so trimmed frames can be scaled\n\t\t// into the same world footprint (0 = not captured yet)\n\t\t/** @ignore */\n\t\tthis._refLw = 0;\n\t\t/** @ignore */\n\t\tthis._refLh = 0;\n\t\t// horizontal / vertical mirror flags. Applied by mirroring the local quad\n\t\t// in `_applyFrame` (uniform for the billboard + fixed paths, and agnostic\n\t\t// to atlas rotation/trim) rather than via the inherited transform flip,\n\t\t// which the billboard projection doesn't use.\n\t\t/** @ignore */\n\t\tthis._flipX = false;\n\t\t/** @ignore */\n\t\tthis._flipY = false;\n\t\t// the last region handed to `_applyFrame`, so a flip can re-map immediately\n\t\t/** @ignore */\n\t\tthis._region = null;\n\n\t\t// ── frame-animation: texture refs the engine reads + the engine itself ──\n\t\t// (the animation state — anim / current / dt / … — lives on the engine and\n\t\t// is surfaced through the accessors below, matching Sprite)\n\t\t/**\n\t\t * a callback fired when the current animation completes a cycle.\n\t\t * @type {Function}\n\t\t */\n\t\tthis.onended = undefined;\n\t\t/** the frame-aware atlas (== this.texture) @ignore */\n\t\tthis.source = this.texture;\n\t\t// region dictionary + name→index map. `settings.atlas` / `atlasIndices`\n\t\t// (as produced by `TextureAtlas.getAnimationSettings` /\n\t\t// `createAnimationFromName`) take precedence so atlas-by-name animation\n\t\t// works exactly as it does for a 2D Sprite; otherwise fall back to the\n\t\t// resolved atlas's own dictionary.\n\t\t/** the atlas region dictionary @ignore */\n\t\tthis.textureAtlas = settings.atlas ?? this.texture.getAtlas();\n\t\t/** name→index map for named-region atlases @ignore */\n\t\tthis.atlasIndices = settings.atlasIndices;\n\t\t/** the shared frame-animation engine @ignore */\n\t\tthis._frameAnim = new FrameAnimation(this, (region) => {\n\t\t\tthis._applyFrame(region);\n\t\t});\n\n\t\t// 3D depth (world z)\n\t\tthis.depth = typeof settings.z === \"number\" ? settings.z : 0;\n\n\t\t/**\n\t\t * Billboard mode — keeps the quad facing the active {@link Camera3d}:\n\t\t * - `false` (default) — fixed orientation (a flat quad in the XY plane;\n\t\t *   decals, posters, ground markers).\n\t\t * - `true` / `\"cylindrical\"` — faces the camera but stays upright\n\t\t *   (rotates only around the world up axis). The 2.5D default — trees,\n\t\t *   characters, items.\n\t\t * - `\"spherical\"` — faces the camera on all axes (particles, glints).\n\t\t *\n\t\t * **Only applies under a `Camera3d`**; ignored on the 2D path.\n\t\t *\n\t\t * Note: while billboarding, orientation comes from the camera, so the\n\t\t * renderable's `currentTransform` (`rotate()` / `scale()` / parent-container\n\t\t * transforms) and `meshScale` are **not** applied — only `pos` / `depth`,\n\t\t * `flipX` / `flipY`, and the quad's authored size. With billboarding `false`\n\t\t * the standard {@link Mesh} world transform applies as usual.\n\t\t * @type {boolean|string}\n\t\t * @default false\n\t\t */\n\t\tthis.billboard = settings.billboard ?? false;\n\n\t\t/** the camera captured at draw time for the billboard projection @ignore */\n\t\tthis._billboardCam = null;\n\n\t\t// select an initial frame: an explicit atlas region, then any predefined\n\t\t// animations, then a catch-all \"default\" covering every frame.\n\t\tif (typeof settings.region !== \"undefined\") {\n\t\t\tconst region = this.source.getRegion(settings.region);\n\t\t\tif (!region) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Sprite3d: region for \" + settings.region + \" not found\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.setRegion(region);\n\t\t}\n\t\tif (typeof settings.anims !== \"undefined\") {\n\t\t\tfor (const id in settings.anims) {\n\t\t\t\tthis.addAnimation(\n\t\t\t\t\tsettings.anims[id].name,\n\t\t\t\t\tsettings.anims[id].index,\n\t\t\t\t\tsettings.anims[id].speed,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// addAnimation returns 0 when there is no usable frame grid (e.g. a named\n\t\t// atlas, where the region above already selected the frame)\n\t\tif (\n\t\t\tthis.current.name === undefined &&\n\t\t\tthis.addAnimation(\"default\", null) !== 0\n\t\t) {\n\t\t\tthis.setCurrentAnimation(\"default\");\n\t\t}\n\n\t\t// optional initial flip (after the first frame is applied, so the re-map\n\t\t// in flipX/flipY has a region to work with)\n\t\tif (settings.flipX) {\n\t\t\tthis.flipX(true);\n\t\t}\n\t\tif (settings.flipY) {\n\t\t\tthis.flipY(true);\n\t\t}\n\t}\n\n\t/**\n\t * anchorPoint changed at runtime (the ObservablePoint callback wired in\n\t * the constructor): recompute the baked local offset from the live anchor\n\t * and the art size, re-bake the quad, and re-derive the frustum-cull\n\t * bounds. Preserves the base ObservablePoint contract (updateBounds +\n\t * isDirty, see Renderable's anchorPoint callback).\n\t * @ignore\n\t */\n\t_onAnchorChanged() {\n\t\t// re-bakes the quad's corner positions in place (directly below, or\n\t\t// via `_applyFrame`) — a geometry change either way\n\t\tthis._geometryVersion++;\n\t\tconst hw = this._halfW;\n\t\tconst hh = this._halfH;\n\t\tthis._anchorOffsetX = (0.5 - this.anchorPoint.x) * hw * 2;\n\t\tthis._anchorOffsetY = (this.anchorPoint.y - 0.5) * hh * 2;\n\t\tif (this._region !== null) {\n\t\t\t// re-applies the trim/rotation mapping + flips + the new offsets\n\t\t\tthis._applyFrame(this._region);\n\t\t} else {\n\t\t\t// named-atlas path where no frame region ever landed — re-bake the\n\t\t\t// plain constructor quad\n\t\t\tbakeQuadVertices(\n\t\t\t\thw,\n\t\t\t\thh,\n\t\t\t\tthis._anchorOffsetX,\n\t\t\t\tthis._anchorOffsetY,\n\t\t\t\tthis.originalVertices,\n\t\t\t);\n\t\t}\n\t\t// Resize the bounds box directly: the Rect width/height setters run\n\t\t// Polygon.recalc(), which walks `this.normals` — a field Mesh\n\t\t// repurposes for per-vertex lighting normals (a Float32Array), so the\n\t\t// setters would throw here. The polygon edge/normal machinery is\n\t\t// unused by meshes anyway; mutating the corner points + updateBounds\n\t\t// is exactly the setter minus recalc.\n\t\tconst cullSide = cullSideForAnchor(\n\t\t\thw,\n\t\t\thh,\n\t\t\tthis._anchorOffsetX,\n\t\t\tthis._anchorOffsetY,\n\t\t);\n\t\tthis.points[1].x = this.points[2].x = cullSide;\n\t\tthis.points[2].y = this.points[3].y = cullSide;\n\t\tthis.updateBounds();\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * defined animations, keyed by id (see {@link Sprite3d#addAnimation}).\n\t * @type {object}\n\t */\n\tget anim() {\n\t\treturn this._frameAnim.anim;\n\t}\n\n\t/**\n\t * current frame information (name / index / texture offset & size / trim).\n\t * @type {object}\n\t */\n\tget current() {\n\t\treturn this._frameAnim.current;\n\t}\n\n\t/**\n\t * elapsed time within the current animation frame, in milliseconds.\n\t * @type {number}\n\t */\n\tget dt() {\n\t\treturn this._frameAnim.dt;\n\t}\n\tset dt(value) {\n\t\tthis._frameAnim.dt = value;\n\t}\n\n\t/**\n\t * animation cycling speed (delay between frames in ms).\n\t * @type {number}\n\t * @default 100\n\t */\n\tget animationspeed() {\n\t\treturn this._frameAnim.animationspeed;\n\t}\n\tset animationspeed(value) {\n\t\tthis._frameAnim.animationspeed = value;\n\t}\n\n\t/**\n\t * pause the frame animation, freezing the current frame.\n\t * @type {boolean}\n\t * @default false\n\t */\n\tget animationpause() {\n\t\treturn this._frameAnim.animationpause;\n\t}\n\tset animationpause(value) {\n\t\tthis._frameAnim.animationpause = value;\n\t}\n\n\t/**\n\t * Add an animation, identical to {@link Sprite#addAnimation}.\n\t * @param {string} name - animation id\n\t * @param {number[]|string[]|object[]} index - frame indices / names (see {@link Sprite#addAnimation})\n\t * @param {number} [animationspeed] - cycling speed in ms\n\t * @returns {number} number of frames added\n\t */\n\taddAnimation(name, index, animationspeed) {\n\t\treturn this._frameAnim.addAnimation(name, index, animationspeed);\n\t}\n\n\t/**\n\t * Select the active animation, identical to {@link Sprite#setCurrentAnimation}.\n\t * @param {string} name - animation id\n\t * @param {string|Function|object} [resetAnim] - loop / chain / completion behavior\n\t * @param {boolean} [preserve_dt=false]\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tsetCurrentAnimation(name, resetAnim, preserve_dt = false) {\n\t\tthis._frameAnim.setCurrentAnimation(name, resetAnim, preserve_dt);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Reverse the given (or current) animation in place (see {@link Sprite#reverseAnimation}).\n\t * @param {string} [name] - animation id\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\treverseAnimation(name) {\n\t\tthis._frameAnim.reverseAnimation(name);\n\t\t// reversing doesn't re-apply a frame, so mark dirty here (the host owns\n\t\t// its dirty flag)\n\t\tthis.isDirty = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Mirror the sprite horizontally (e.g. flip a character to face the other\n\t * way). Unlike the 2D {@link Sprite}, the flip mirrors the quad's local\n\t * geometry so it works for every billboard mode and for rotated/trimmed atlas\n\t * regions alike. Takes effect immediately on the current frame.\n\t * @param {boolean} [flip=true]\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tflipX(flip = true) {\n\t\tthis._flipX = !!flip;\n\t\tif (this._region !== null) {\n\t\t\t// re-map the current frame with the new flip (also sets isDirty)\n\t\t\tthis._applyFrame(this._region);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Mirror the sprite vertically. See {@link Sprite3d#flipX}.\n\t * @param {boolean} [flip=true]\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tflipY(flip = true) {\n\t\tthis._flipY = !!flip;\n\t\tif (this._region !== null) {\n\t\t\tthis._applyFrame(this._region);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * @returns {boolean} true if the sprite is mirrored horizontally.\n\t */\n\tisFlippedX() {\n\t\treturn this._flipX === true;\n\t}\n\n\t/**\n\t * @returns {boolean} true if the sprite is mirrored vertically.\n\t */\n\tisFlippedY() {\n\t\treturn this._flipY === true;\n\t}\n\n\t/**\n\t * Play (and optionally switch to) an animation, identical to {@link Sprite#play}.\n\t * @param {string} [name] - animation id to play; omit to resume\n\t * @param {string|Function|object} [options] - loop / chain / completion behavior\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tplay(name, options) {\n\t\tthis.animationpause = false;\n\t\tif (name !== undefined) {\n\t\t\tthis.setCurrentAnimation(name, options);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Pause the current animation, freezing the current frame (see {@link Sprite#pause}).\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tpause() {\n\t\tthis.animationpause = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Stop and reset the current animation to its first frame (see {@link Sprite#stop}).\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tstop() {\n\t\tthis.animationpause = true;\n\t\tthis._frameAnim.rewind();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Force the current animation frame index (see {@link Sprite#setAnimationFrame}).\n\t * @param {number} [index=0] - animation frame index\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tsetAnimationFrame(index = 0) {\n\t\tthis._frameAnim.setAnimationFrame(index);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a texture region directly (see {@link Sprite#setRegion}).\n\t * @param {object} region - typically from `texture.getRegion(name)`\n\t * @returns {Sprite3d} Reference to this object for method chaining\n\t */\n\tsetRegion(region) {\n\t\tthis._frameAnim.setRegion(region);\n\t\treturn this;\n\t}\n\n\t/**\n\t * @param {string} name - animation id\n\t * @returns {boolean} true if `name` is the current animation (see {@link Sprite#isCurrentAnimation}).\n\t */\n\tisCurrentAnimation(name) {\n\t\treturn this._frameAnim.isCurrentAnimation(name);\n\t}\n\n\t/**\n\t * @returns {string[]} the names of every defined animation (see {@link Sprite#getAnimationNames}).\n\t */\n\tgetAnimationNames() {\n\t\treturn this._frameAnim.getAnimationNames();\n\t}\n\n\t/**\n\t * @returns {number} the current animation frame index (see {@link Sprite#getCurrentAnimationFrame}).\n\t */\n\tgetCurrentAnimationFrame() {\n\t\treturn this._frameAnim.getCurrentAnimationFrame();\n\t}\n\n\t/**\n\t * The frame object for the given index within the current animation.\n\t * @param {number} id - the frame id\n\t * @returns {object} the frame data\n\t * @ignore\n\t */\n\tgetAnimationFrameObjectByIndex(id) {\n\t\treturn this._frameAnim.getAnimationFrameObjectByIndex(id);\n\t}\n\n\t/**\n\t * Apply the current frame's texture region to the quad — the geometry hook\n\t * called by the shared {@link FrameAnimation} engine (the 3D counterpart of\n\t * {@link Sprite}'s sub-texture / size / anchor swap). Rewrites both the quad's\n\t * UVs (the atlas sub-rect, with a 90° corner permutation for packer-rotated\n\t * regions) and its local vertices (the trimmed art's sub-rectangle within the\n\t * logical frame, scaled into the quad's world footprint, then shifted by the\n\t * anchor offset), so trimmed and rotated `TextureAtlas` regions render at\n\t * full parity with the 2D `Sprite`.\n\t * @param {object} region - the texture region object\n\t * @ignore\n\t */\n\t_applyFrame(region) {\n\t\t// this rewrites the quad's UVs (and, for a trimmed region, its\n\t\t// vertices) in place — that IS a geometry change, so any GPU-resident\n\t\t// copy has to be refreshed\n\t\tthis._geometryVersion++;\n\t\t// remember the region so a later flipX/flipY can re-map immediately\n\t\tthis._region = region;\n\t\t// the texture backing this frame (full sheet for a spritesheet, the atlas\n\t\t// page for a packed atlas)\n\t\tconst image = this.source.getTexture(region);\n\t\tconst iw = image.width;\n\t\tconst ih = image.height;\n\n\t\t// `region.width`/`height` are the art's UNROTATED dimensions; a\n\t\t// packer-rotated region occupies a height×width box in the atlas.\n\t\tconst rotated = region.angle !== 0;\n\t\tconst aw = region.width;\n\t\tconst ah = region.height;\n\t\tconst atlasW = rotated ? ah : aw;\n\t\tconst atlasH = rotated ? aw : ah;\n\n\t\t// ── UVs: the atlas AABB, corners permuted 90° when the region is rotated\n\t\tconst ox = region.offset.x;\n\t\tconst oy = region.offset.y;\n\t\tconst uL = ox / iw;\n\t\tconst uR = (ox + atlasW) / iw;\n\t\t// texture-space top is the smaller pixel y; the baked UVs put v=0 at the\n\t\t// top corners (local +y) so the frame lands upright under Y-down.\n\t\tconst vT = oy / ih;\n\t\tconst vB = (oy + atlasH) / ih;\n\t\tconst uv = this.uvs;\n\t\t// corners: 0=(left,bottom) 1=(right,bottom) 2=(right,top) 3=(left,top)\n\t\tif (rotated) {\n\t\t\t// TexturePacker stores rotated art 90° clockwise, so undo it by\n\t\t\t// rotating the corner→UV assignment: the quad's left edge samples the\n\t\t\t// atlas top row, its top edge samples the atlas right column.\n\t\t\tuv[0] = uL;\n\t\t\tuv[1] = vT; // BL → atlas top-left\n\t\t\tuv[2] = uL;\n\t\t\tuv[3] = vB; // BR → atlas bottom-left\n\t\t\tuv[4] = uR;\n\t\t\tuv[5] = vB; // TR → atlas bottom-right\n\t\t\tuv[6] = uR;\n\t\t\tuv[7] = vT; // TL → atlas top-right\n\t\t} else {\n\t\t\tuv[0] = uL;\n\t\t\tuv[1] = vB;\n\t\t\tuv[2] = uR;\n\t\t\tuv[3] = vB;\n\t\t\tuv[4] = uR;\n\t\t\tuv[5] = vT;\n\t\t\tuv[6] = uL;\n\t\t\tuv[7] = vT;\n\t\t}\n\n\t\t// ── geometry: place the (possibly trimmed) art rect inside the logical\n\t\t// frame. Logical size is the untrimmed sourceSize; the art occupies a\n\t\t// sub-rect offset by `trim`. The world quad (±_halfW/±_halfH) maps the\n\t\t// logical frame captured from the first applied frame, so trimmed frames\n\t\t// keep their position/size relative to the full sprite footprint.\n\t\tconst trimmed = region.trimmed === true && region.sourceSize != null;\n\t\tconst logicalW = trimmed ? region.sourceSize.w : aw;\n\t\tconst logicalH = trimmed ? region.sourceSize.h : ah;\n\t\tconst tx = region.trim ? region.trim.x : 0;\n\t\tconst ty = region.trim ? region.trim.y : 0;\n\t\tif (this._refLw === 0) {\n\t\t\tthis._refLw = logicalW;\n\t\t\tthis._refLh = logicalH;\n\t\t}\n\t\t// world units per logical pixel\n\t\tconst sx = (2 * this._halfW) / this._refLw;\n\t\tconst sy = (2 * this._halfH) / this._refLh;\n\t\t// art rect in centered local space (texture top = +y, render Y-down)\n\t\tlet xl = (tx - logicalW / 2) * sx;\n\t\tlet xr = (tx + aw - logicalW / 2) * sx;\n\t\tlet yt = (logicalH / 2 - ty) * sy;\n\t\tlet yb = (logicalH / 2 - (ty + ah)) * sy;\n\t\t// mirror the local quad around its origin for flips — uniform across the\n\t\t// billboard + fixed paths and independent of atlas rotation/trim (each\n\t\t// corner's UV is unchanged, so the texture mirrors)\n\t\tif (this._flipX) {\n\t\t\txl = -xl;\n\t\t\txr = -xr;\n\t\t}\n\t\tif (this._flipY) {\n\t\t\tyt = -yt;\n\t\t\tyb = -yb;\n\t\t}\n\t\t// pin `anchorPoint` to `pos` (added last, after the flip, so mirroring\n\t\t// still happens about the art's own center rather than the shifted\n\t\t// anchor)\n\t\txl += this._anchorOffsetX;\n\t\txr += this._anchorOffsetX;\n\t\tyt += this._anchorOffsetY;\n\t\tyb += this._anchorOffsetY;\n\t\tconst v = this.originalVertices;\n\t\t// c0 BL, c1 BR, c2 TR, c3 TL (z stays 0)\n\t\tv[0] = xl;\n\t\tv[1] = yb;\n\t\tv[3] = xr;\n\t\tv[4] = yb;\n\t\tv[6] = xr;\n\t\tv[7] = yt;\n\t\tv[9] = xl;\n\t\tv[10] = yt;\n\t\t// the frame changed → this sprite needs a redraw (the host owns its dirty\n\t\t// flag; the FrameAnimation engine never sets it)\n\t\tthis.isDirty = true;\n\t}\n\n\t/**\n\t * Advance the frame animation, then defer to the standard mesh update.\n\t * @param {number} dt - time since the last update in milliseconds\n\t * @returns {boolean} true if the sprite changed and needs a redraw\n\t * @ignore\n\t */\n\tupdate(dt) {\n\t\t// advance the engine first (a frame change marks this sprite dirty via\n\t\t// `_applyFrame`), then let `super.update` report `isDirty` — same order and\n\t\t// single-source-of-truth as Sprite\n\t\tthis._frameAnim.update(dt);\n\t\treturn super.update(dt);\n\t}\n\n\t/**\n\t * Release resources (returns the engine's pooled `current.offset` to the\n\t * pool), then defer to the standard mesh/renderable teardown.\n\t * @ignore\n\t */\n\tdestroy() {\n\t\tthis._frameAnim.destroy();\n\t\tthis._region = null;\n\t\tsuper.destroy();\n\t}\n\n\t/**\n\t * Capture the rendering camera so {@link Sprite3d#_projectVerticesWorld} can\n\t * orient the billboard toward it. Read from the per-draw `viewport` (passed\n\t * by `Container.draw`), so a multi-camera stage billboards correctly against\n\t * whichever camera is drawing. When no `Camera3d` viewport is supplied the\n\t * billboard is inactive and the quad renders fixed-orientation.\n\t * @param {CanvasRenderer|WebGLRenderer} renderer\n\t * @param {Camera2d} [viewport]\n\t * @ignore\n\t */\n\tdraw(renderer, viewport) {\n\t\tthis._billboardCam = viewport instanceof Camera3d ? viewport : null;\n\t\tsuper.draw(renderer, viewport);\n\t}\n\n\t/**\n\t * Orient the quad toward the camera when billboarding; otherwise defer to the\n\t * standard {@link Mesh} world projection (a fixed-orientation quad).\n\t * @ignore\n\t */\n\t_projectVerticesWorld(offsetX, offsetY, offsetZ) {\n\t\tif (this._billboardBasis() === false) {\n\t\t\tsuper._projectVerticesWorld(offsetX, offsetY, offsetZ);\n\t\t\treturn;\n\t\t}\n\t\tthis._projectBillboardCorners(offsetX, offsetY, offsetZ);\n\t}\n\n\t/**\n\t * Resolve the camera-facing basis this billboard should use, leaving it in\n\t * the shared `_right` / `_up` scratch vectors.\n\t *\n\t * Shared by the CPU projection and the model-matrix composition so the two\n\t * can never disagree about which way the card faces.\n\t * @returns {boolean} `false` when this sprite is not billboarding\n\t * @ignore\n\t */\n\t_billboardBasis() {\n\t\tconst mode = this.billboard;\n\t\tconst cam = this._billboardCam;\n\t\tif (mode === false || mode === \"none\" || cam === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// camera basis (world space): right / up / forward. Note `getBasis`\n\t\t// returns a math basis (+Y up) while render space is Y-down — each mode\n\t\t// below resolves that sign so the card lands right-way-up.\n\t\tcam.getBasis(_right, _up, _fwd);\n\n\t\tif (mode === \"cylindrical\" || mode === true) {\n\t\t\t// stay upright: up = world up (-Y in render space), right = horizontal\n\t\t\t// axis = forward × up (this order makes the front face the camera).\n\t\t\t// Falls back to the camera right when looking straight up/down\n\t\t\t// (forward ∥ world up → degenerate cross product).\n\t\t\t_up.copy(WORLD_UP);\n\t\t\t_right.copy(_fwd).cross(WORLD_UP);\n\t\t\tif (_right.length() < 1e-4) {\n\t\t\t\tcam.getRight(_right);\n\t\t\t} else {\n\t\t\t\t_right.normalize();\n\t\t\t}\n\t\t} else {\n\t\t\t// spherical: face the camera on all axes. `getBasis` returns a math\n\t\t\t// (+Y up) basis, but render space is Y-down — so the camera's \"up\"\n\t\t\t// actually points screen-DOWN. Negate it to get the screen-up axis\n\t\t\t// (matching WORLD_UP at head-on), then right = forward × up to match\n\t\t\t// the cylindrical handedness so the front face — not the mirrored\n\t\t\t// back — points at the camera.\n\t\t\t_up.negateSelf();\n\t\t\t_right.copy(_fwd).cross(_up);\n\t\t\tif (_right.length() < 1e-4) {\n\t\t\t\tcam.getRight(_right);\n\t\t\t} else {\n\t\t\t\t_right.normalize();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * A billboard's placement is the camera-facing basis itself: the quad's\n\t * local X follows the camera's right axis and its local Y the up axis, so\n\t * the card turns to face the camera without its geometry ever changing.\n\t * Falls back to the standard mesh placement when not billboarding.\n\t * @returns {Matrix3d} the sprite's model matrix (reused instance)\n\t * @ignore\n\t */\n\t_composeModelMatrix() {\n\t\tif (this._billboardBasis() === false) {\n\t\t\treturn super._composeModelMatrix();\n\t\t}\n\t\tif (this._modelMatrix === undefined) {\n\t\t\t/** @ignore */\n\t\t\tthis._modelMatrix = new Matrix3d();\n\t\t}\n\t\tconst out = this._modelMatrix.val;\n\t\t// columns: right, up, right×up (unused by a flat quad but kept\n\t\t// well-formed), then the world position\n\t\tout[0] = _right.x;\n\t\tout[1] = _right.y;\n\t\tout[2] = _right.z;\n\t\tout[3] = 0;\n\t\tout[4] = _up.x;\n\t\tout[5] = _up.y;\n\t\tout[6] = _up.z;\n\t\tout[7] = 0;\n\t\tout[8] = _right.y * _up.z - _right.z * _up.y;\n\t\tout[9] = _right.z * _up.x - _right.x * _up.z;\n\t\tout[10] = _right.x * _up.y - _right.y * _up.x;\n\t\tout[11] = 0;\n\t\tout[12] = this.pos.x;\n\t\tout[13] = this.pos.y;\n\t\tout[14] = this.depth;\n\t\tout[15] = 1;\n\t\treturn this._modelMatrix;\n\t}\n\n\t/**\n\t * Emit the 4 corners as center ± right·localX ± up·localY (the local\n\t * offsets are the baked ±hw / ±hh quad coordinates, already shifted by the\n\t * anchor offset). Only used on renderers without retained geometry.\n\t * @ignore\n\t */\n\t_projectBillboardCorners(offsetX, offsetY, offsetZ) {\n\t\tconst out = this.vertices;\n\t\tconst src = this.originalVertices;\n\t\tconst rx = _right.x;\n\t\tconst ry = _right.y;\n\t\tconst rz = _right.z;\n\t\tconst ux = _up.x;\n\t\tconst uy = _up.y;\n\t\tconst uz = _up.z;\n\t\tfor (let i = 0; i < 4; i++) {\n\t\t\tconst i3 = i * 3;\n\t\t\tconst lx = src[i3];\n\t\t\tconst ly = src[i3 + 1];\n\t\t\tout[i3] = offsetX + rx * lx + ux * ly;\n\t\t\tout[i3 + 1] = offsetY + ry * lx + uy * ly;\n\t\t\tout[i3 + 2] = offsetZ + rz * lx + uz * ly;\n\t\t}\n\t}\n}\n","import { colorPool } from \"../math/color.ts\";\nimport { lerpArray } from \"../math/math.ts\";\nimport { computeVertexNormal } from \"../math/vertex.ts\";\nimport { Gradient } from \"../video/gradient.js\";\nimport Renderable from \"./renderable.js\";\n\n/**\n * @import {Color} from \"../math/color.ts\";\n * @import {Vector2d} from \"../math/vector2d.ts\";\n */\n\n/**\n * A Trail renderable that draws a fading, tapering ribbon behind a moving object.\n * Points are sampled from a target or added manually, and rendered as connected\n * quads with interpolated width and color.\n * @category Game Objects\n * @example\n * // simple single-color trail following a sprite\n * const trail = new Trail({ target: player, color: \"#44aaff\", width: 20 });\n * app.world.addChild(trail);\n * @example\n * // gradient trail following a sprite (fire effect)\n * const trail = new Trail({\n *     target: player,\n *     width: 30,\n *     gradient: [\"#ffff00\", \"#ff4400\", \"#ff000000\"],\n * });\n * app.world.addChild(trail);\n * @example\n * // rainbow trail with custom width curve\n * const trail = new Trail({\n *     target: player,\n *     width: 40,\n *     widthCurve: [1, 1, 0.8, 0.4, 0],\n *     gradient: [\"#ff0000\", \"#ff8800\", \"#ffff00\", \"#00ff00\", \"#0088ff\", \"#8800ff00\"],\n * });\n * app.world.addChild(trail);\n * @example\n * // manual mode (e.g. sword slash)\n * const slash = new Trail({ width: 24, lifetime: 200, color: \"#ffffff\" });\n * app.world.addChild(slash);\n * // call each frame during the attack animation\n * slash.addPoint(swordTip.x, swordTip.y);\n * @see {@link Renderable}\n */\nexport default class Trail extends Renderable {\n\t/**\n\t * @param {object} [options] - trail options\n\t * @param {Renderable|Vector2d} [options.target] - auto-follow target\n\t * @param {number} [options.length=20] - max number of points\n\t * @param {number} [options.lifetime=500] - point lifetime in ms\n\t * @param {number} [options.minDistance=4] - min distance between samples in px\n\t * @param {number} [options.width=10] - max trail width in px\n\t * @param {number[]} [options.widthCurve=[1, 0]] - width multiplier curve (0=head, 1=tail)\n\t * @param {Color|string} [options.color=\"#ffffff\"] - single trail color. Ignored if `gradient` is set.\n\t * @param {(Color|string|{pos: number, color: Color|string})[]} [options.gradient] - color gradient. Overrides `color`.\n\t * @param {number} [options.opacity=1.0] - trail opacity (0.0–1.0)\n\t * @param {string} [options.blendMode=\"normal\"] - blend mode\n\t */\n\tconstructor(options = {}) {\n\t\tsuper(0, 0, 1, 1);\n\n\t\t/** @type {Renderable|Vector2d|null} */\n\t\tthis.target = options.target ?? null;\n\t\t/** @type {number} */\n\t\tthis.maxPoints = options.length ?? 20;\n\t\t/** @type {number} */\n\t\tthis.lifetime = options.lifetime ?? 500;\n\t\t/** @type {number} */\n\t\tthis.minDistance = options.minDistance ?? 4;\n\t\t/** @type {number} */\n\t\tthis.width = options.width ?? 10;\n\t\t/** @type {number[]} */\n\t\tthis.widthCurve =\n\t\t\tArray.isArray(options.widthCurve) && options.widthCurve.length > 0\n\t\t\t\t? options.widthCurve\n\t\t\t\t: [1, 0];\n\n\t\t/** @ignore */\n\t\tthis._gradient = this._buildGradient(options);\n\t\t/** @ignore */\n\t\tthis._points = [];\n\t\t/** @ignore */\n\t\tthis._segmentColor = colorPool.get();\n\t\t/** @ignore */\n\t\tthis._n0 = { x: 0, y: 0 };\n\t\t/** @ignore */\n\t\tthis._n1 = { x: 0, y: 0 };\n\n\t\tthis.alwaysUpdate = true;\n\t\tthis.anchorPoint.set(0, 0);\n\n\t\tif (typeof options.opacity === \"number\") {\n\t\t\tthis.setOpacity(options.opacity);\n\t\t}\n\t\tif (options.blendMode) {\n\t\t\tthis.blendMode = options.blendMode;\n\t\t}\n\t}\n\n\t/**\n\t * add a point to the trail\n\t * @param {number} x - x position\n\t * @param {number} y - y position\n\t */\n\taddPoint(x, y) {\n\t\tif (this._points.length > 0) {\n\t\t\tconst last = this._points[this._points.length - 1];\n\t\t\tconst dx = x - last.x;\n\t\t\tconst dy = y - last.y;\n\t\t\tif (dx * dx + dy * dy < this.minDistance * this.minDistance) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis._points.push({ x, y, age: 0 });\n\t\tif (this._points.length > this.maxPoints) {\n\t\t\tthis._points.shift();\n\t\t}\n\t}\n\n\t/** @ignore */\n\tupdate(dt) {\n\t\tif (this.target) {\n\t\t\tconst pos = this.target.pos ?? this.target;\n\t\t\tthis.addPoint(pos.x, pos.y);\n\t\t}\n\n\t\tconst pts = this._points;\n\t\tlet expired = 0;\n\t\tfor (let i = 0; i < pts.length; i++) {\n\t\t\tpts[i].age += dt;\n\t\t\t// oldest points are at the front, count the contiguous expired block\n\t\t\tif (i === expired && pts[i].age >= this.lifetime) {\n\t\t\t\texpired++;\n\t\t\t}\n\t\t}\n\t\tif (expired > 0) {\n\t\t\tpts.splice(0, expired);\n\t\t}\n\n\t\tthis.inViewport = this._points.length > 1;\n\t\tthis.isDirty = this._points.length > 1;\n\t\treturn super.update(dt);\n\t}\n\n\t/** @ignore */\n\tdraw(renderer) {\n\t\tconst points = this._points;\n\t\tconst count = points.length;\n\t\tif (count < 2) {\n\t\t\treturn;\n\t\t}\n\n\t\t// capture base alpha set by preDraw (includes trail + parent opacity)\n\t\tconst baseAlpha = renderer.getGlobalAlpha();\n\n\t\tconst invCount = 1 / (count - 1);\n\t\tconst halfWidth = this.width * 0.5;\n\t\tconst invLifetime = 1 / this.lifetime;\n\n\t\tfor (let i = 0; i < count - 1; i++) {\n\t\t\tconst p0 = points[i];\n\t\t\tconst p1 = points[i + 1];\n\n\t\t\t// position ratio: 0 = head (newest), 1 = tail (oldest)\n\t\t\tconst t = 1 - i * invCount;\n\t\t\tconst t1 = 1 - (i + 1) * invCount;\n\n\t\t\t// width at both ends of this segment\n\t\t\tconst hw0 = lerpArray(this.widthCurve, t) * halfWidth;\n\t\t\tconst hw1 = lerpArray(this.widthCurve, t1) * halfWidth;\n\n\t\t\t// color by position, alpha by age\n\t\t\tthis._gradient.getColorAt(t, this._segmentColor);\n\t\t\tthis._segmentColor.alpha *= 1.0 - p0.age * invLifetime;\n\t\t\tif (this._segmentColor.alpha <= 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// reset renderer alpha so setColor doesn't compound across segments\n\t\t\trenderer.setGlobalAlpha(baseAlpha);\n\t\t\trenderer.setColor(this._segmentColor);\n\n\t\t\t// perpendicular normals at both endpoints\n\t\t\tconst n0 = computeVertexNormal(points, i, this._n0);\n\t\t\tconst n1 = computeVertexNormal(points, i + 1, this._n1);\n\n\t\t\t// draw quad\n\t\t\trenderer.beginPath();\n\t\t\trenderer.moveTo(p0.x - n0.x * hw0, p0.y - n0.y * hw0);\n\t\t\trenderer.lineTo(p0.x + n0.x * hw0, p0.y + n0.y * hw0);\n\t\t\trenderer.lineTo(p1.x + n1.x * hw1, p1.y + n1.y * hw1);\n\t\t\trenderer.lineTo(p1.x - n1.x * hw1, p1.y - n1.y * hw1);\n\t\t\trenderer.closePath();\n\t\t\trenderer.fill();\n\t\t}\n\t}\n\n\t/** clear all trail points */\n\tclear() {\n\t\tthis._points.length = 0;\n\t}\n\n\t/**\n\t * Build a Gradient from trail constructor options.\n\t * @param {object} options - trail constructor options\n\t * @returns {Gradient}\n\t * @ignore\n\t */\n\t_buildGradient(options) {\n\t\tconst gradient = new Gradient(\"linear\", [0, 0, 1, 0]);\n\n\t\tif (options.gradient) {\n\t\t\tconst entries = options.gradient;\n\t\t\tfor (let i = 0; i < entries.length; i++) {\n\t\t\t\tconst entry = entries[i];\n\t\t\t\tif (typeof entry === \"string\" || (entry && entry.toRGBA)) {\n\t\t\t\t\t// plain color: evenly distribute\n\t\t\t\t\tconst offset = entries.length > 1 ? i / (entries.length - 1) : 0;\n\t\t\t\t\tgradient.addColorStop(offset, entry);\n\t\t\t\t} else {\n\t\t\t\t\t// positioned stop: { pos, color }\n\t\t\t\t\tgradient.addColorStop(entry.pos, entry.color);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (options.color) {\n\t\t\t// parse into a Color to handle CSS names, rgba(), hex, or Color objects\n\t\t\tconst parsed = colorPool.get(options.color);\n\t\t\tconst hex = parsed.toHex();\n\t\t\tcolorPool.release(parsed);\n\t\t\tgradient.addColorStop(0, `${hex}ff`);\n\t\t\tgradient.addColorStop(1, `${hex}00`);\n\t\t} else {\n\t\t\tgradient.addColorStop(0, \"#ffffffff\");\n\t\t\tgradient.addColorStop(1, \"#ffffff00\");\n\t\t}\n\n\t\treturn gradient;\n\t}\n\n\t/** @ignore */\n\tdestroy() {\n\t\tthis._gradient.destroy();\n\t\tcolorPool.release(this._segmentColor);\n\t\tthis._points.length = 0;\n\t\tthis.target = null;\n\t\tsuper.destroy();\n\t}\n}\n","import {\n\tregisterPointerEvent,\n\treleasePointerEvent,\n} from \"./../../input/input.ts\";\nimport type Pointer from \"./../../input/pointer.ts\";\nimport type { Vector2d } from \"../../math/vector2d.ts\";\nimport { vector2dPool } from \"../../math/vector2d.ts\";\nimport { off, on, POINTERMOVE } from \"../../system/event.ts\";\nimport timer from \"../../system/timer.ts\";\nimport Container from \"../container.js\";\n\n/**\n * This is a basic clickable and draggable container which you can use in your game UI.\n * Use this for example if you want to display a panel that contains text, images or other UI elements.\n * @category UI\n */\nexport default class UIBaseElement extends Container {\n\t/**\n\t * UI base elements use screen coordinates by default\n\t * (Note: any child elements added to a UIBaseElement should have their floating property to false)\n\t * @see Renderable.floating\n\t * @default true\n\t */\n\toverride floating = true;\n\n\t/**\n\t * object can be clicked or not\n\t * @default true\n\t */\n\tisClickable: boolean;\n\n\t/**\n\t * object can be dragged or not\n\t * @default false\n\t */\n\tisDraggable: boolean;\n\n\t/**\n\t * Tap and hold threshold timeout in ms\n\t * @default 250\n\t */\n\tholdThreshold: number;\n\n\t/**\n\t * object can be tap and hold\n\t * @default false\n\t */\n\tisHoldable: boolean;\n\n\t/**\n\t * true if the pointer is over the object\n\t * @default false\n\t */\n\thover: boolean;\n\n\t/**\n\t * false if the pointer is down, or true when the pointer status is up\n\t * @default true\n\t */\n\treleased: boolean;\n\n\t// object has been updated (clicked,etc..)\n\tholdTimeout: number;\n\n\t// grab offset for dragging\n\tgrabOffset: Vector2d | undefined;\n\n\t/**\n\t * @param x - The x position of the container\n\t * @param y - The y position of the container\n\t * @param w - width of the container\n\t * @param h - height of the container\n\t */\n\tconstructor(x: number, y: number, w?: number, h?: number) {\n\t\tsuper(x, y, w, h);\n\n\t\tthis.isClickable = true;\n\t\tthis.isDraggable = false;\n\t\tthis.holdThreshold = 250;\n\t\tthis.isHoldable = false;\n\t\tthis.hover = false;\n\t\tthis.released = true;\n\t\tthis.holdTimeout = -1;\n\n\t\t// enable event detection\n\t\tthis.isKinematic = false;\n\n\t\t// update container and children bounds automatically\n\t\tthis.enableChildBoundsUpdate = true;\n\t}\n\n\t/**\n\t * function callback for the pointerdown event\n\t * @ignore\n\t */\n\tclicked(event: Pointer): boolean | void {\n\t\t// Check if left mouse button is pressed\n\t\tif (event.button === 0 && this.isClickable) {\n\t\t\tthis.isDirty = true;\n\t\t\tthis.released = false;\n\t\t\tif (this.isHoldable) {\n\t\t\t\ttimer.clearTimer(this.holdTimeout);\n\t\t\t\tthis.holdTimeout = timer.setTimeout(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.hold();\n\t\t\t\t\t},\n\t\t\t\t\tthis.holdThreshold,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\t\tthis.released = false;\n\t\t\t}\n\t\t\tif (this.isDraggable) {\n\t\t\t\tthis.grabOffset!.set(event.gameX, event.gameY);\n\t\t\t\tthis.grabOffset!.sub(this.pos);\n\t\t\t}\n\t\t\treturn this.onClick(event);\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed (to be extended)\n\t * @param _event - the event object\n\t * @returns return false if we need to stop propagating the event\n\t */\n\n\tonClick(_event?: Pointer): boolean {\n\t\treturn true;\n\t}\n\n\t/**\n\t * function callback for the pointerEnter event\n\t * @ignore\n\t */\n\tenter(event: Pointer): void {\n\t\tthis.hover = true;\n\t\tthis.isDirty = true;\n\t\tif (this.isDraggable) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\ton(POINTERMOVE, this.pointerMove, this);\n\t\t\t// to memorize where we grab the object\n\t\t\tthis.grabOffset = vector2dPool.get(0, 0);\n\t\t}\n\t\tthis.onOver(event);\n\t}\n\n\t/**\n\t * pointermove function\n\t * @ignore\n\t */\n\tpointerMove(event: Pointer): void {\n\t\tif (this.hover && !this.released) {\n\t\t\t// follow the pointer\n\t\t\t// pos is ObservableVector3d at runtime but typed as Vector2d in Renderable\n\t\t\t(this.pos as any).set(event.gameX, event.gameY, (this.pos as any).z);\n\t\t\t(this.pos as any).sub(this.grabOffset!);\n\t\t\t// mark the container for redraw\n\t\t\tthis.isDirty = true;\n\t\t\tthis.onMove(event);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * function called when the pointer is moved over the object\n\t * @param _event - the event object\n\t */\n\n\tonMove(_event?: Pointer): void {\n\t\t// to be extended\n\t}\n\n\t/**\n\t * function called when the pointer is over the object\n\t * @param _event - the event object\n\t */\n\n\tonOver(_event?: Pointer): void {\n\t\t// to be extended\n\t}\n\n\t/**\n\t * function callback for the pointerLeave event\n\t * @ignore\n\t */\n\tleave(event: Pointer): void {\n\t\tthis.hover = false;\n\t\tthis.isDirty = true;\n\t\tif (this.isDraggable) {\n\t\t\t// unregister on the global pointermove event\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(POINTERMOVE, this.pointerMove, this);\n\t\t\tvector2dPool.release(this.grabOffset!);\n\t\t\tthis.grabOffset = undefined;\n\t\t}\n\t\tthis.release(event);\n\t\tthis.onOut(event);\n\t}\n\n\t/**\n\t * function called when the pointer is leaving the object area\n\t * @param _event - the event object\n\t */\n\n\tonOut(_event?: Pointer): void {\n\t\t// to be extended\n\t}\n\n\t/**\n\t * function callback for the pointerup event\n\t * @ignore\n\t */\n\trelease(event: Pointer): boolean | void {\n\t\tif (!this.released) {\n\t\t\tthis.released = true;\n\t\t\tthis.isDirty = true;\n\t\t\ttimer.clearTimer(this.holdTimeout);\n\t\t\tthis.holdTimeout = -1;\n\t\t\treturn this.onRelease(event);\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed and released (to be extended)\n\t * @param _event - the event object\n\t * @returns return false if we need to stop propagating the event\n\t */\n\n\tonRelease(_event?: Pointer): boolean {\n\t\treturn true;\n\t}\n\n\t/**\n\t * function callback for the tap and hold timer event\n\t * @ignore\n\t */\n\thold(): void {\n\t\ttimer.clearTimer(this.holdTimeout);\n\t\tthis.holdTimeout = -1;\n\t\tthis.isDirty = true;\n\t\tif (!this.released) {\n\t\t\tthis.onHold();\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed and held<br>\n\t * to be extended <br>\n\t */\n\tonHold(): void {}\n\n\t/**\n\t * function called when added to the game world or a container\n\t * @ignore\n\t */\n\toverride onActivateEvent(): void {\n\t\t// register pointer events\n\t\tregisterPointerEvent(\"pointerdown\", this, (e) => {\n\t\t\treturn this.clicked(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerup\", this, (e) => {\n\t\t\treturn this.release(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointercancel\", this, (e) => {\n\t\t\treturn this.release(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerenter\", this, (e) => {\n\t\t\tthis.enter(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerleave\", this, (e) => {\n\t\t\tthis.leave(e);\n\t\t});\n\n\t\t// call the parent function\n\t\tsuper.onActivateEvent();\n\t}\n\n\t/**\n\t * function called when removed from the game world or a container\n\t * @ignore\n\t */\n\toverride onDeactivateEvent(): void {\n\t\t// release pointer events\n\t\treleasePointerEvent(\"pointerdown\", this);\n\t\treleasePointerEvent(\"pointerup\", this);\n\t\treleasePointerEvent(\"pointercancel\", this);\n\t\treleasePointerEvent(\"pointerenter\", this);\n\t\treleasePointerEvent(\"pointerleave\", this);\n\t\ttimer.clearTimer(this.holdTimeout);\n\t\tthis.holdTimeout = -1;\n\n\t\t// unregister on the global pointermove event\n\t\t// note: this is just a precaution, in case\n\t\t// the object is being remove from his parent\n\t\t// container before the leave function is called\n\t\tif (this.isDraggable) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\toff(POINTERMOVE, this.pointerMove, this);\n\t\t\tif (typeof this.grabOffset !== \"undefined\") {\n\t\t\t\tvector2dPool.release(this.grabOffset);\n\t\t\t\tthis.grabOffset = undefined;\n\t\t\t}\n\t\t}\n\n\t\t// call the parent function\n\t\tsuper.onDeactivateEvent();\n\t}\n}\n","import {\n\tregisterPointerEvent,\n\treleasePointerEvent,\n} from \"./../../input/input.ts\";\nimport type Pointer from \"./../../input/pointer.ts\";\nimport timer from \"./../../system/timer.ts\";\nimport Sprite from \"./../sprite.js\";\n\n/**\n * This is a basic sprite based button which you can use in your Game UI.\n * @category UI\n */\nexport default class UISpriteElement extends Sprite {\n\t/**\n\t * if this UISpriteElement should use screen coordinates or local coordinates\n\t * (Note: any UISpriteElement elements added to a floating parent container should have their floating property to false)\n\t * @see Renderable.floating\n\t * @default true\n\t */\n\toverride floating = true;\n\n\t/**\n\t * object can be clicked or not\n\t * @default true\n\t */\n\tisClickable: boolean;\n\n\t/**\n\t * Tap and hold threshold timeout in ms\n\t * @default 250\n\t */\n\tholdThreshold: number;\n\n\t/**\n\t * object can be tap and hold\n\t * @default false\n\t */\n\tisHoldable: boolean;\n\n\t/**\n\t * true if the pointer is over the object\n\t * @default false\n\t */\n\thover: boolean;\n\n\t// object has been updated (clicked,etc..)\n\tholdTimeout: number;\n\treleased: boolean;\n\n\t/**\n\t * @param x - the x coordinate of the UISpriteElement Object\n\t * @param y - the y coordinate of the UISpriteElement Object\n\t * @param settings - See {@link Sprite}\n\t * @param settings.image - the image to use for the sprite\n\t * @example\n\t * // create a basic GUI Object\n\t * class myButton extends UISpriteElement {\n\t *    constructor(x, y) {\n\t *       // call the UISpriteElement parent constructor\n\t *       super(x, y, {\n\t *          image: \"button\",\n\t *          framewidth: 100,\n\t *          frameheight: 50\n\t *       });\n\t *    }\n\t *\n\t *    // output something in the console\n\t *    // when the object is clicked\n\t *    onClick(event) {\n\t *       console.log(\"clicked!\");\n\t *       // don't propagate the event\n\t *       return false;\n\t *    }\n\t * });\n\t *\n\t * // add the object at pos (10,10)\n\t * world.addChild(new myButton(10,10));\n\t */\n\tconstructor(\n\t\tx: number,\n\t\ty: number,\n\t\tsettings: { image: any; [key: string]: any },\n\t) {\n\t\t// call the parent constructor\n\t\tsuper(x, y, settings);\n\n\t\tthis.isClickable = true;\n\t\tthis.holdThreshold = 250;\n\t\tthis.isHoldable = false;\n\t\tthis.hover = false;\n\t\tthis.holdTimeout = -1;\n\t\tthis.released = true;\n\n\t\t// enable event detection\n\t\tthis.isKinematic = false;\n\t}\n\n\t/**\n\t * function callback for the pointerdown event\n\t * @ignore\n\t */\n\tclicked(event: Pointer): boolean | void {\n\t\t// Check if left mouse button is pressed\n\t\tif (event.button === 0 && this.isClickable) {\n\t\t\tthis.isDirty = true;\n\t\t\tthis.released = false;\n\t\t\tif (this.isHoldable) {\n\t\t\t\ttimer.clearTimer(this.holdTimeout);\n\t\t\t\tthis.holdTimeout = timer.setTimeout(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.hold();\n\t\t\t\t\t},\n\t\t\t\t\tthis.holdThreshold,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\t\tthis.released = false;\n\t\t\t}\n\t\t\treturn this.onClick(event);\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed (to be extended)\n\t * @param _event - the event object\n\t * @returns return false if we need to stop propagating the event\n\t */\n\n\tonClick(_event?: Pointer): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * function callback for the pointerEnter event\n\t * @ignore\n\t */\n\tenter(event: Pointer): void {\n\t\tthis.hover = true;\n\t\tthis.isDirty = true;\n\t\tthis.onOver(event);\n\t}\n\n\t/**\n\t * function called when the pointer is over the object\n\t * @param _event - the event object\n\t */\n\n\tonOver(_event?: Pointer): void {\n\t\t// to be extended\n\t}\n\n\t/**\n\t * function callback for the pointerLeave event\n\t * @ignore\n\t */\n\tleave(event: Pointer): void {\n\t\tthis.hover = false;\n\t\tthis.isDirty = true;\n\t\tthis.release(event);\n\t\tthis.onOut(event);\n\t}\n\n\t/**\n\t * function called when the pointer is leaving the object area\n\t * @param _event - the event object\n\t */\n\n\tonOut(_event?: Pointer): void {\n\t\t// to be extended\n\t}\n\n\t/**\n\t * function callback for the pointerup event\n\t * @ignore\n\t */\n\trelease(event: Pointer): boolean | void {\n\t\tif (!this.released) {\n\t\t\tthis.released = true;\n\t\t\tthis.isDirty = true;\n\t\t\ttimer.clearTimer(this.holdTimeout);\n\t\t\tthis.holdTimeout = -1;\n\t\t\treturn this.onRelease(event);\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed and released (to be extended)\n\t * @param _event - the event object\n\t * @returns return false if we need to stop propagating the event\n\t */\n\n\tonRelease(_event?: Pointer): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * function callback for the tap and hold timer event\n\t * @ignore\n\t */\n\thold(): void {\n\t\ttimer.clearTimer(this.holdTimeout);\n\t\tthis.holdTimeout = -1;\n\t\tthis.isDirty = true;\n\t\tif (!this.released) {\n\t\t\tthis.onHold();\n\t\t}\n\t}\n\n\t/**\n\t * function called when the object is pressed and held<br>\n\t * to be extended <br>\n\t */\n\tonHold(): void {}\n\n\t/**\n\t * function called when added to the game world or a container\n\t * @ignore\n\t */\n\toverride onActivateEvent(): void {\n\t\t// register pointer events\n\t\tregisterPointerEvent(\"pointerdown\", this, (e) => {\n\t\t\treturn this.clicked(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerup\", this, (e) => {\n\t\t\treturn this.release(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointercancel\", this, (e) => {\n\t\t\treturn this.release(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerenter\", this, (e) => {\n\t\t\tthis.enter(e);\n\t\t});\n\t\tregisterPointerEvent(\"pointerleave\", this, (e) => {\n\t\t\tthis.leave(e);\n\t\t});\n\t}\n\n\t/**\n\t * function called when removed from the game world or a container\n\t * @ignore\n\t */\n\toverride onDeactivateEvent(): void {\n\t\t// release pointer events\n\t\treleasePointerEvent(\"pointerdown\", this);\n\t\treleasePointerEvent(\"pointerup\", this);\n\t\treleasePointerEvent(\"pointercancel\", this);\n\t\treleasePointerEvent(\"pointerenter\", this);\n\t\treleasePointerEvent(\"pointerleave\", this);\n\t\ttimer.clearTimer(this.holdTimeout);\n\t\tthis.holdTimeout = -1;\n\t}\n}\n","import type Camera2d from \"../../camera/camera2d.ts\";\nimport { RoundRect } from \"../../geometries/roundrect.ts\";\nimport type { Bounds } from \"../../physics/bounds.ts\";\nimport type Renderer from \"../../video/renderer.js\";\nimport BitmapText from \"../text/bitmaptext.js\";\nimport UIBaseElement from \"./uibaseelement.ts\";\n\ninterface UITextButtonSettings {\n\tfont?: string;\n\tsize?: number;\n\ttext?: string;\n\tbindKey?: string | number;\n\thoverOffColor?: string;\n\thoverOnColor?: string;\n\tborderStrokeColor?: string;\n\tfillStyle?: string;\n\ttextAlign?: string;\n\ttextBaseline?: string;\n\tborderWidth?: number;\n\tborderHeight?: number;\n\t[key: string]: any;\n}\n\n/**\n * This is a basic base text button which you can use in your Game UI.\n * @category UI\n */\nexport default class UITextButton extends UIBaseElement {\n\t/**\n\t * The key to bind the action to\n\t */\n\tbindKey: string | number;\n\n\t/**\n\t * The css value of a color to be used if the pointer is not hovering over the button\n\t */\n\thoverOffColor: string;\n\n\t/**\n\t * The css value of a color to be used if the pointer hovers over the button\n\t */\n\thoverOnColor: string;\n\n\t/**\n\t * The css value of a color to be used to draw the border\n\t */\n\tborderStrokeColor: string;\n\n\t/**\n\t * Set the default text alignment (or justification),<br>\n\t * possible values are \"left\", \"right\", and \"center\".\n\t * @default \"center\"\n\t */\n\ttextAlign: string;\n\n\t/**\n\t * Set the text baseline (e.g. the Y-coordinate for the draw operation), <br>\n\t * possible values are \"top\", \"hanging\", \"middle\", \"alphabetic\", \"ideographic\", \"bottom\"<br>\n\t * @default \"middle\"\n\t */\n\ttextBaseline: string;\n\n\t/**\n\t * the bitmapText used by the UITextButton class\n\t */\n\tbitmapText: BitmapText;\n\n\t/**\n\t * the measured text dimensions\n\t */\n\tdimensions: Bounds;\n\n\t/**\n\t * the round rect border\n\t */\n\tborder: RoundRect;\n\n\t/**\n\t * A Bitmap Text Button with an outlined background border, filled with background color.\n\t * It uses a RoundRect as background and changes the background color on hovering over.\n\t * The background will be drawn with 0.5 opacity, so that the background of the button is\n\t * slightly shining through.\n\t * @param x - x pos of the button\n\t * @param y - y pos of the button\n\t * @param settings - settings object\n\t * @example\n\t * // Create a new Button\n\t * class PlayButton extends UITextButton {\n\t *      constructor(x,y) {\n\t *          super(x,y, {\n\t *              font: 'my-font',\n\t *              text: 'Play',\n\t *              // if you omit the next two, size is calculated by the size of the text\n\t *              borderWidth: 200,\n\t *              borderHeight: 20,\n\t *              hoverOffColor: '#00aa0080',\n\t *              hoverOnColor: '#00ff00ff'\n\t *          });\n\t *      }\n\t *\n\t *      onClick(){\n\t *          state.change(state.PLAY);\n\t *      }\n\t * }\n\t *\n\t * world.addChild(new PlayButton(15,200));\n\t */\n\tconstructor(x: number, y: number, settings: UITextButtonSettings) {\n\t\tsuper(x, y);\n\n\t\tthis.bindKey = settings.bindKey || -1;\n\n\t\tthis.hoverOffColor = settings.hoverOffColor || \"#00aa0080\";\n\t\tthis.hoverOnColor = settings.hoverOnColor || \"#00ff00ff\";\n\n\t\tthis.borderStrokeColor = settings.borderStrokeColor || \"#000000\";\n\n\t\tthis.textAlign = settings.textAlign = settings.textAlign || \"center\";\n\n\t\tthis.textBaseline = settings.textBaseline =\n\t\t\tsettings.textBaseline || \"middle\";\n\n\t\tthis.bitmapText = new BitmapText(0, 0, settings as any);\n\n\t\t// \"detect\" the button size\n\t\tthis.dimensions = this.bitmapText.measureText();\n\t\tsettings.borderWidth = settings.borderWidth || this.dimensions.width + 16;\n\t\tsettings.borderHeight =\n\t\t\tsettings.borderHeight || this.dimensions.height + 16;\n\n\t\t// create the round rect button\n\t\tthis.border = new RoundRect(\n\t\t\tx,\n\t\t\ty,\n\t\t\tsettings.borderWidth,\n\t\t\tsettings.borderHeight,\n\t\t);\n\n\t\t// resize the container accordingly\n\t\tthis.resize(this.border.getBounds().width, this.border.getBounds().height);\n\n\t\t// adjust size position\n\t\tthis.bitmapText.pos.set(this.width / 2, this.height / 2);\n\n\t\t// add bitmapText to the UI container\n\t\tthis.addChild(this.bitmapText);\n\t}\n\n\toverride draw(renderer: Renderer, viewport?: Camera2d): void {\n\t\tconst r = renderer as any;\n\t\tif (this.hover) {\n\t\t\tr.setColor(this.hoverOnColor);\n\t\t} else {\n\t\t\tr.setColor(this.hoverOffColor);\n\t\t}\n\t\tr.fill(this.border);\n\t\tr.setColor(this.borderStrokeColor);\n\t\tr.stroke(this.border);\n\t\tsuper.draw(renderer as any, viewport as any);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct BlurUniforms {\n\tuBlurStrength : f32,\n\tuTextureSize : vec2f,\n};\n@group(3) @binding(0) var<uniform> fx : BlurUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet texel = fx.uBlurStrength / fx.uTextureSize;\n\tvar sum = vec4f(0.0);\n\t// 9-tap box blur\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(-texel.x, -texel.y));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(0.0, -texel.y));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(texel.x, -texel.y));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(-texel.x, 0.0));\n\tsum += textureSample(uTexture, uSampler, uv);\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(texel.x, 0.0));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(-texel.x, texel.y));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(0.0, texel.y));\n\tsum += textureSample(uTexture, uSampler, uv + vec2f(texel.x, texel.y));\n\treturn (sum / 9.0) * vColor;\n}\n`;\n\n/**\n * A shader effect that applies a box blur to the sprite.\n * Commonly used for background defocus, depth-of-field simulation,\n * or UI backdrop blur.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // moderate blur\n * mySprite.shader = new BlurEffect(renderer, { strength: 2.0 });\n */\nexport default class BlurEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.strength=1.0] - blur strength in texels\n\t * @param {number[]} [options.textureSize=[256, 256]] - texture dimensions [width, height]\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uBlurStrength;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tvec2 texel = uBlurStrength / uTextureSize;\n\t\t\t\tvec4 sum = vec4(0.0);\n\t\t\t\t// 9-tap box blur\n\t\t\t\tsum += texture2D(uSampler, uv + vec2(-texel.x, -texel.y));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2( 0.0,    -texel.y));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2( texel.x, -texel.y));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2(-texel.x,  0.0));\n\t\t\t\tsum += texture2D(uSampler, uv);\n\t\t\t\tsum += texture2D(uSampler, uv + vec2( texel.x,  0.0));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2(-texel.x,  texel.y));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2( 0.0,     texel.y));\n\t\t\t\tsum += texture2D(uSampler, uv + vec2( texel.x,  texel.y));\n\t\t\t\treturn (sum / 9.0) * vColor;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.strength = options.strength ?? 1.0;\n\t\tconst texSize = options.textureSize ?? [256, 256];\n\n\t\tthis.setUniform(\"uBlurStrength\", this.strength);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t}\n\n\t/**\n\t * set the blur strength\n\t * @param {number} strength - blur strength in texels\n\t */\n\tsetStrength(strength) {\n\t\tthis.strength = Math.max(0, strength);\n\t\tthis.setUniform(\"uBlurStrength\", this.strength);\n\t}\n\n\t/**\n\t * set the texture size\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct AberrationUniforms {\n\tuOffset : f32,\n\tuTextureSize : vec2f,\n};\n@group(3) @binding(0) var<uniform> fx : AberrationUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet texel = fx.uOffset / fx.uTextureSize;\n\tlet r = textureSample(uTexture, uSampler, uv + vec2f(texel.x, 0.0)).r;\n\tlet g = textureSample(uTexture, uSampler, uv).g;\n\tlet b = textureSample(uTexture, uSampler, uv - vec2f(texel.x, 0.0)).b;\n\tlet a = textureSample(uTexture, uSampler, uv).a;\n\treturn vec4f(r, g, b, a) * vColor;\n}\n`;\n\n/**\n * A shader effect that offsets the RGB color channels to create a\n * chromatic aberration (color fringe) effect. Commonly used for\n * impact feedback, glitch effects, or damage indicators.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // subtle chromatic aberration\n * mySprite.shader = new ChromaticAberrationEffect(renderer, { offset: 2.0 });\n * @example\n * // strong glitch effect\n * mySprite.shader = new ChromaticAberrationEffect(renderer, { offset: 5.0 });\n */\nexport default class ChromaticAberrationEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.offset=3.0] - channel offset in texels\n\t * @param {number[]} [options.textureSize=[256, 256]] - texture dimensions [width, height]\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uOffset;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tvec2 texel = uOffset / uTextureSize;\n\t\t\t\tfloat r = texture2D(uSampler, uv + vec2(texel.x, 0.0)).r;\n\t\t\t\tfloat g = texture2D(uSampler, uv).g;\n\t\t\t\tfloat b = texture2D(uSampler, uv - vec2(texel.x, 0.0)).b;\n\t\t\t\tfloat a = texture2D(uSampler, uv).a;\n\t\t\t\treturn vec4(r, g, b, a) * vColor;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.offset = options.offset ?? 3.0;\n\t\tconst texSize = options.textureSize ?? [256, 256];\n\n\t\tthis.setUniform(\"uOffset\", this.offset);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t}\n\n\t/**\n\t * set the channel offset\n\t * @param {number} offset - offset in texels\n\t */\n\tsetOffset(offset) {\n\t\tthis.offset = Math.max(0, offset);\n\t\tthis.setUniform(\"uOffset\", this.offset);\n\t}\n\n\t/**\n\t * set the texture size\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ColorMatrixEffect from \"./colorMatrix.js\";\n\n/**\n * A shader effect that desaturates (grayscales) the sprite.\n * Commonly used for disabled states, death effects, or petrification.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // full grayscale\n * mySprite.shader = new DesaturateEffect(renderer);\n * @example\n * // partial desaturation (50%)\n * mySprite.shader = new DesaturateEffect(renderer, { intensity: 0.5 });\n */\nexport default class DesaturateEffect extends ColorMatrixEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.intensity=1.0] - desaturation intensity (0.0 = full color, 1.0 = grayscale)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer);\n\t\tthis.intensity =\n\t\t\ttypeof options.intensity === \"number\" ? options.intensity : 1.0;\n\t\tthis.saturate(1 - this.intensity);\n\t}\n\n\t/**\n\t * set the desaturation intensity\n\t * @param {number} value - desaturation intensity (0.0 = full color, 1.0 = grayscale)\n\t */\n\tsetIntensity(value) {\n\t\tthis.intensity = Math.max(0, Math.min(1, value));\n\t\tthis.reset().saturate(1 - this.intensity);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct DissolveUniforms {\n\tuDissolveProgress : f32,\n\tuEdgeColor : vec3f,\n\tuEdgeWidth : f32,\n};\n@group(3) @binding(0) var<uniform> fx : DissolveUniforms;\n\n// pseudo-random hash\nfn dissolveHash(p : vec2f) -> f32 {\n\treturn fract(sin(dot(p, vec2f(127.1, 311.7))) * 43758.5453);\n}\n// smooth value noise\nfn dissolveNoise(p : vec2f) -> f32 {\n\tlet i = floor(p);\n\tvar f = fract(p);\n\tf = f * f * (3.0 - 2.0 * f);\n\treturn mix(mix(dissolveHash(i), dissolveHash(i + vec2f(1.0, 0.0)), f.x),\n\t\tmix(dissolveHash(i + vec2f(0.0, 1.0)), dissolveHash(i + vec2f(1.0, 1.0)), f.x), f.y);\n}\n// fractal brownian motion for organic shapes\nfn dissolveFBM(p : vec2f) -> f32 {\n\tvar v = 0.0;\n\tvar a = 0.5;\n\tvar q = p;\n\tfor (var i = 0; i < 4; i++) {\n\t\tv += a * dissolveNoise(q);\n\t\tq *= 2.0;\n\t\ta *= 0.5;\n\t}\n\treturn v;\n}\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tif (fx.uDissolveProgress <= 0.0) {\n\t\treturn color;\n\t}\n\tlet n = dissolveFBM(uv * 8.0);\n\tif (n < fx.uDissolveProgress) {\n\t\tdiscard;\n\t}\n\t// glowing burn edge\n\tlet dist = (n - fx.uDissolveProgress) / fx.uEdgeWidth;\n\tif (dist < 1.0) {\n\t\tlet t = 1.0 - dist;\n\t\t// 3-stop gradient: red edge -> orange -> bright white core\n\t\tlet red = vec3f(0.8, 0.1, 0.0);\n\t\tvar glow = select(\n\t\t\tmix(fx.uEdgeColor, vec3f(1.0, 0.95, 0.8), (t - 0.5) * 2.0),\n\t\t\tmix(red, fx.uEdgeColor, t * 2.0),\n\t\t\tt < 0.5);\n\t\tglow *= 1.0 + t * t;\n\t\treturn vec4f(mix(color.rgb, glow, t) * color.a, color.a);\n\t}\n\treturn color;\n}\n`;\n\n/**\n * A shader effect that dissolves the sprite using a noise-based threshold.\n * Pixels are discarded based on a pseudo-random noise pattern as the\n * `progress` value increases from 0 to 1.\n * Commonly used for death, spawn, or teleport effects.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // create a dissolve effect\n * const dissolve = new DissolveEffect(renderer);\n * mySprite.shader = dissolve;\n *\n * // animate the dissolve (0 = fully visible, 1 = fully dissolved)\n * dissolve.setProgress(0.5);\n */\nexport default class DissolveEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.progress=0.0] - dissolve progress (0.0 = visible, 1.0 = dissolved)\n\t * @param {number[]} [options.edgeColor=[1.0, 0.5, 0.0]] - color of the dissolve edge\n\t * @param {number} [options.edgeWidth=0.1] - width of the colored edge (0.0–1.0)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uDissolveProgress;\n\t\t\tuniform vec3 uEdgeColor;\n\t\t\tuniform float uEdgeWidth;\n\t\t\t// pseudo-random hash\n\t\t\tfloat hash(vec2 p) {\n\t\t\t\treturn fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);\n\t\t\t}\n\t\t\t// smooth value noise\n\t\t\tfloat vnoise(vec2 p) {\n\t\t\t\tvec2 i = floor(p);\n\t\t\t\tvec2 f = fract(p);\n\t\t\t\tf = f * f * (3.0 - 2.0 * f);\n\t\t\t\treturn mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x),\n\t\t\t\t           mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), f.y);\n\t\t\t}\n\t\t\t// fractal brownian motion for organic shapes\n\t\t\tfloat fbm(vec2 p) {\n\t\t\t\tfloat v = 0.0, a = 0.5;\n\t\t\t\tfor (int i = 0; i < 4; i++) { v += a * vnoise(p); p *= 2.0; a *= 0.5; }\n\t\t\t\treturn v;\n\t\t\t}\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tif (uDissolveProgress <= 0.0) {\n\t\t\t\t\treturn color;\n\t\t\t\t}\n\t\t\t\tfloat n = fbm(uv * 8.0);\n\t\t\t\tif (n < uDissolveProgress) {\n\t\t\t\t\tdiscard;\n\t\t\t\t}\n\t\t\t\t// glowing burn edge\n\t\t\t\tfloat dist = (n - uDissolveProgress) / uEdgeWidth;\n\t\t\t\tif (dist < 1.0) {\n\t\t\t\t\tfloat t = 1.0 - dist;\n\t\t\t\t\t// 3-stop gradient: red edge -> orange -> bright white core\n\t\t\t\t\tvec3 red = vec3(0.8, 0.1, 0.0);\n\t\t\t\t\tvec3 glow = (t < 0.5)\n\t\t\t\t\t\t? mix(red, uEdgeColor, t * 2.0)\n\t\t\t\t\t\t: mix(uEdgeColor, vec3(1.0, 0.95, 0.8), (t - 0.5) * 2.0);\n\t\t\t\t\tglow *= 1.0 + t * t;\n\t\t\t\t\treturn vec4(mix(color.rgb, glow, t) * color.a, color.a);\n\t\t\t\t}\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.progress = options.progress ?? 0.0;\n\t\tthis.setUniform(\"uDissolveProgress\", this.progress);\n\t\tthis.setUniform(\n\t\t\t\"uEdgeColor\",\n\t\t\tnew Float32Array(options.edgeColor ?? [1.0, 0.5, 0.0]),\n\t\t);\n\t\tthis.setUniform(\"uEdgeWidth\", options.edgeWidth ?? 0.1);\n\t}\n\n\t/**\n\t * set the dissolve progress\n\t * @param {number} value - dissolve progress (0.0 = visible, 1.0 = dissolved)\n\t */\n\tsetProgress(value) {\n\t\tthis.progress = Math.max(0, Math.min(1, value));\n\t\tthis.setUniform(\"uDissolveProgress\", this.progress);\n\t}\n\n\t/**\n\t * set the edge color\n\t * @param {number[]} color - edge color as [r, g, b] (0.0–1.0)\n\t */\n\tsetEdgeColor(color) {\n\t\tthis.setUniform(\"uEdgeColor\", new Float32Array(color));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct ShadowUniforms {\n\tuShadowOffset : vec2f,\n\tuShadowColor : vec3f,\n\tuShadowOpacity : f32,\n\tuTextureSize : vec2f,\n\tuUVYDir : f32,\n};\n@group(3) @binding(0) var<uniform> fx : ShadowUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tif (color.a > 0.0) {\n\t\treturn color;\n\t}\n\t// check if the shadow source pixel is opaque. Level-0 sample: past a\n\t// non-uniform return implicit derivatives are unavailable (sprites are\n\t// single-level textures, so identical output)\n\tvar offset = fx.uShadowOffset / fx.uTextureSize;\n\t// uUVYDir keeps \"down\" pointing down whatever the sampled space's\n\t// vertical orientation (always +1 under WebGPU; the GL pooled path\n\t// sets -1 for its bottom-up captures)\n\toffset.y = offset.y * fx.uUVYDir;\n\tlet shadowAlpha = textureSampleLevel(uTexture, uSampler, uv - offset, 0.0).a;\n\tif (shadowAlpha > 0.0) {\n\t\treturn vec4f(fx.uShadowColor, shadowAlpha * fx.uShadowOpacity) * vColor;\n\t}\n\treturn color;\n}\n`;\n\n/**\n * A shader effect that adds a drop shadow beneath the sprite.\n * Works by sampling the texture at an offset — if the offset position\n * has an opaque pixel but the current position doesn't, the shadow is drawn.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // dark shadow offset to the bottom-right\n * mySprite.shader = new DropShadowEffect(renderer, {\n *     offsetX: 3.0,\n *     offsetY: 3.0,\n *     color: [0.0, 0.0, 0.0],\n *     opacity: 0.5,\n * });\n */\nexport default class DropShadowEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.offsetX=2.0] - shadow x offset in texels\n\t * @param {number} [options.offsetY=2.0] - shadow y offset in texels\n\t * @param {number[]} [options.color=[0.0, 0.0, 0.0]] - shadow color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.opacity=0.5] - shadow opacity (0.0–1.0)\n\t * @param {number[]} [options.textureSize=[256, 256]] - texture dimensions [width, height]\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec2 uShadowOffset;\n\t\t\tuniform vec3 uShadowColor;\n\t\t\tuniform float uShadowOpacity;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tuniform float uUVYDir;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tif (color.a > 0.0) {\n\t\t\t\t\treturn color;\n\t\t\t\t}\n\t\t\t\t// check if the shadow source pixel is opaque. uUVYDir keeps\n\t\t\t\t// \"down\" pointing down whatever the sampled space's vertical\n\t\t\t\t// orientation (+1 sampling the sprite atlas directly, -1 on\n\t\t\t\t// the pooled path's bottom-up capture FBOs)\n\t\t\t\tvec2 offset = uShadowOffset / uTextureSize;\n\t\t\t\toffset.y *= uUVYDir;\n\t\t\t\tfloat shadowAlpha = texture2D(uSampler, uv - offset).a;\n\t\t\t\tif (shadowAlpha > 0.0) {\n\t\t\t\t\treturn vec4(uShadowColor, shadowAlpha * uShadowOpacity) * vColor;\n\t\t\t\t}\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst texSize = options.textureSize ?? [256, 256];\n\t\tthis.setUniform(\n\t\t\t\"uShadowOffset\",\n\t\t\tnew Float32Array([options.offsetX ?? 2.0, options.offsetY ?? 2.0]),\n\t\t);\n\t\tthis.setUniform(\n\t\t\t\"uShadowColor\",\n\t\t\tnew Float32Array(options.color ?? [0.0, 0.0, 0.0]),\n\t\t);\n\t\tthis.setUniform(\"uShadowOpacity\", options.opacity ?? 0.5);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t\t// uv.y grows downward until a renderer path says otherwise\n\t\tthis.setUniform(\"uUVYDir\", 1.0);\n\t}\n\n\t/**\n\t * set the shadow offset\n\t * @param {number} x - x offset in texels\n\t * @param {number} y - y offset in texels\n\t */\n\tsetOffset(x, y) {\n\t\tthis.setUniform(\"uShadowOffset\", new Float32Array([x, y]));\n\t}\n\n\t/**\n\t * set the texture size\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct FlashUniforms {\n\tuFlashColor : vec3f,\n\tuFlashIntensity : f32,\n};\n@group(3) @binding(0) var<uniform> fx : FlashUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\treturn vec4f(mix(color.rgb, fx.uFlashColor * color.a, fx.uFlashIntensity), color.a);\n}\n`;\n\n/**\n * A shader effect that flashes the sprite with a solid color.\n * Commonly used for hit feedback — flash white when the player takes damage.\n * The `intensity` uniform controls how much of the flash color is mixed in\n * (0.0 = original sprite, 1.0 = fully colored).\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // create a white flash effect\n * const flash = new FlashEffect(renderer);\n * mySprite.shader = flash;\n *\n * // trigger the flash (e.g. on hit)\n * flash.setIntensity(1.0);\n *\n * // fade it out over time (e.g. in update loop)\n * flash.setIntensity(flash.intensity - dt / 200);\n * @example\n * // red flash\n * const flash = new FlashEffect(renderer, { color: [1.0, 0.0, 0.0] });\n */\nexport default class FlashEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[1.0, 1.0, 1.0]] - flash color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.intensity=0.0] - initial flash intensity (0.0–1.0)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uFlashColor;\n\t\t\tuniform float uFlashIntensity;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\treturn vec4(mix(color.rgb, uFlashColor * color.a, uFlashIntensity), color.a);\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst color = options.color ?? [1.0, 1.0, 1.0];\n\t\tthis.intensity = options.intensity ?? 0.0;\n\n\t\tthis.setUniform(\"uFlashColor\", new Float32Array(color));\n\t\tthis.setUniform(\"uFlashIntensity\", this.intensity);\n\t}\n\n\t/**\n\t * set the flash intensity\n\t * @param {number} value - flash intensity (0.0 = no flash, 1.0 = fully colored)\n\t */\n\tsetIntensity(value) {\n\t\tthis.intensity = Math.max(0, Math.min(1, value));\n\t\tthis.setUniform(\"uFlashIntensity\", this.intensity);\n\t}\n\n\t/**\n\t * set the flash color\n\t * @param {number[]} color - flash color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uFlashColor\", new Float32Array(color));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct GlowUniforms {\n\tuGlowColor : vec3f,\n\tuGlowWidth : f32,\n\tuGlowIntensity : f32,\n\tuTextureSize : vec2f,\n};\n@group(3) @binding(0) var<uniform> fx : GlowUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tif (color.a > 0.0) {\n\t\treturn color;\n\t}\n\t// sample in a circle to create a soft glow (level-0: non-uniform flow)\n\tvar a = 0.0;\n\tlet texel = fx.uGlowWidth / fx.uTextureSize;\n\tfor (var angle = 0.0; angle < 6.28; angle += 0.785) {\n\t\tlet offset = vec2f(cos(angle), sin(angle)) * texel;\n\t\ta += textureSampleLevel(uTexture, uSampler, uv + offset, 0.0).a;\n\t}\n\ta = a / 8.0 * fx.uGlowIntensity;\n\tif (a > 0.0) {\n\t\treturn vec4f(fx.uGlowColor * a, a) * vColor;\n\t}\n\treturn color;\n}\n`;\n\n/**\n * A shader effect that adds a colored glow around the sprite.\n * Similar to OutlineEffect but with a soft, blurred edge instead of a hard line.\n * Commonly used for power-ups, magic items, or selection highlights.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // blue glow for a magic item\n * mySprite.shader = new GlowEffect(renderer, {\n *     color: [0.2, 0.5, 1.0],\n *     intensity: 1.5,\n * });\n */\nexport default class GlowEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[1.0, 1.0, 1.0]] - glow color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.width=3.0] - glow spread in pixels\n\t * @param {number} [options.intensity=1.0] - glow brightness multiplier\n\t * @param {number[]} [options.textureSize=[256, 256]] - texture dimensions [width, height]\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uGlowColor;\n\t\t\tuniform float uGlowWidth;\n\t\t\tuniform float uGlowIntensity;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tif (color.a > 0.0) {\n\t\t\t\t\treturn color;\n\t\t\t\t}\n\t\t\t\t// sample in a circle to create a soft glow\n\t\t\t\tfloat a = 0.0;\n\t\t\t\tvec2 texel = uGlowWidth / uTextureSize;\n\t\t\t\tfor (float angle = 0.0; angle < 6.28; angle += 0.785) {\n\t\t\t\t\tvec2 offset = vec2(cos(angle), sin(angle)) * texel;\n\t\t\t\t\ta += texture2D(uSampler, uv + offset).a;\n\t\t\t\t}\n\t\t\t\ta = a / 8.0 * uGlowIntensity;\n\t\t\t\tif (a > 0.0) {\n\t\t\t\t\treturn vec4(uGlowColor * a, a) * vColor;\n\t\t\t\t}\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst color = options.color ?? [1.0, 1.0, 1.0];\n\t\tconst width = options.width ?? 3.0;\n\t\tconst texSize = options.textureSize ?? [256, 256];\n\t\tthis.intensity = options.intensity ?? 1.0;\n\n\t\tthis.setUniform(\"uGlowColor\", new Float32Array(color));\n\t\tthis.setUniform(\"uGlowWidth\", width);\n\t\tthis.setUniform(\"uGlowIntensity\", this.intensity);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t}\n\n\t/**\n\t * set the glow color\n\t * @param {number[]} color - glow color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uGlowColor\", new Float32Array(color));\n\t}\n\n\t/**\n\t * set the glow intensity\n\t * @param {number} value - glow brightness multiplier\n\t */\n\tsetIntensity(value) {\n\t\tthis.intensity = Math.max(0, value);\n\t\tthis.setUniform(\"uGlowIntensity\", this.intensity);\n\t}\n\n\t/**\n\t * set the texture size\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct HologramUniforms {\n\tuHoloColor : vec3f,\n\tuHoloIntensity : f32,\n\tuTime : f32,\n};\n@group(3) @binding(0) var<uniform> fx : HologramUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\t// scan line\n\tlet scan = sin(uv.y * 200.0 + fx.uTime * 5.0) * 0.5 + 0.5;\n\t// flicker\n\tlet flicker = 0.95 + 0.05 * sin(fx.uTime * 30.0);\n\t// color shift\n\tvar holo = mix(color.rgb, fx.uHoloColor * color.a, fx.uHoloIntensity);\n\tholo *= (1.0 - scan * 0.15) * flicker;\n\treturn vec4f(holo, color.a * flicker);\n}\n`;\n\n/**\n * A shader effect that simulates a holographic projection with\n * horizontal scan lines, color shift, and flickering.\n * Commonly used for sci-fi UI, ghost/spirit characters, or tech displays.\n * The `time` uniform should be updated each frame for animation.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * const holo = new HologramEffect(renderer);\n * mySprite.shader = holo;\n * // update each frame\n * holo.setTime(performance.now() / 1000);\n */\nexport default class HologramEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[0.1, 0.7, 1.0]] - hologram tint color [r, g, b]\n\t * @param {number} [options.intensity=0.5] - effect intensity (0.0–1.0)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uHoloColor;\n\t\t\tuniform float uHoloIntensity;\n\t\t\tuniform float uTime;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\t// scan line\n\t\t\t\tfloat scan = sin(uv.y * 200.0 + uTime * 5.0) * 0.5 + 0.5;\n\t\t\t\t// flicker\n\t\t\t\tfloat flicker = 0.95 + 0.05 * sin(uTime * 30.0);\n\t\t\t\t// color shift\n\t\t\t\tvec3 holo = mix(color.rgb, uHoloColor * color.a, uHoloIntensity);\n\t\t\t\tholo *= (1.0 - scan * 0.15) * flicker;\n\t\t\t\treturn vec4(holo, color.a * flicker);\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst color = options.color ?? [0.1, 0.7, 1.0];\n\t\tthis.setUniform(\"uHoloColor\", new Float32Array(color));\n\t\tthis.setUniform(\"uHoloIntensity\", options.intensity ?? 0.5);\n\t\tthis.setUniform(\"uTime\", 0.0);\n\t}\n\n\t/**\n\t * set the hologram tint color\n\t * @param {number[]} color - color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uHoloColor\", new Float32Array(color));\n\t}\n}\n","import ColorMatrixEffect from \"./colorMatrix.js\";\n\n/**\n * A shader effect that inverts the colors of the sprite.\n * Commonly used for damage feedback, negative image, or X-ray effects.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * mySprite.shader = new InvertEffect(renderer);\n * @example\n * // partial inversion\n * mySprite.shader = new InvertEffect(renderer, { intensity: 0.5 });\n */\nexport default class InvertEffect extends ColorMatrixEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.intensity=1.0] - inversion intensity (0.0 = original, 1.0 = fully inverted)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer);\n\t\tthis.intensity =\n\t\t\ttypeof options.intensity === \"number\" ? options.intensity : 1.0;\n\t\tthis.invertColors(this.intensity);\n\t}\n\n\t/**\n\t * set the inversion intensity\n\t * @param {number} value - inversion intensity (0.0 = original, 1.0 = fully inverted)\n\t */\n\tsetIntensity(value) {\n\t\tthis.intensity = Math.max(0, Math.min(1, value));\n\t\tthis.reset().invertColors(this.intensity);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct OutlineUniforms {\n\tuOutlineColor : vec3f,\n\tuOutlineWidth : f32,\n\tuTextureSize : vec2f,\n};\n@group(3) @binding(0) var<uniform> fx : OutlineUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tif (color.a > 0.0) {\n\t\treturn color;\n\t}\n\t// sample neighbors to detect edges (level-0: non-uniform flow)\n\tlet texel = fx.uOutlineWidth / fx.uTextureSize;\n\tvar a = 0.0;\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(-texel.x, 0.0), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(texel.x, 0.0), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(0.0, -texel.y), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(0.0, texel.y), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(-texel.x, -texel.y), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(texel.x, -texel.y), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(-texel.x, texel.y), 0.0).a);\n\ta = max(a, textureSampleLevel(uTexture, uSampler, uv + vec2f(texel.x, texel.y), 0.0).a);\n\tif (a > 0.0) {\n\t\treturn vec4f(fx.uOutlineColor, a) * vColor;\n\t}\n\treturn color;\n}\n`;\n\n/**\n * A shader effect that draws a colored outline around the sprite.\n * Works by sampling neighboring pixels — if any neighbor is opaque but the\n * current pixel is transparent, it draws the outline color.\n * Commonly used for selection highlights, hover states, or collectible glow.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // yellow outline for selection\n * mySprite.shader = new OutlineEffect(renderer, {\n *     color: [1.0, 1.0, 0.0],\n *     width: 2.0,\n * });\n * @example\n * // remove the effect\n * mySprite.shader = undefined;\n */\nexport default class OutlineEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[1.0, 1.0, 1.0]] - outline color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.width=1.0] - outline width in pixels\n\t * @param {number[]} [options.textureSize] - texture dimensions [width, height] (defaults to renderer size)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uOutlineColor;\n\t\t\tuniform float uOutlineWidth;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tif (color.a > 0.0) {\n\t\t\t\t\treturn color;\n\t\t\t\t}\n\t\t\t\t// sample neighbors to detect edges\n\t\t\t\tvec2 texel = uOutlineWidth / uTextureSize;\n\t\t\t\tfloat a = 0.0;\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2(-texel.x, 0.0)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2( texel.x, 0.0)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2(0.0, -texel.y)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2(0.0,  texel.y)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2(-texel.x, -texel.y)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2( texel.x, -texel.y)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2(-texel.x,  texel.y)).a);\n\t\t\t\ta = max(a, texture2D(uSampler, uv + vec2( texel.x,  texel.y)).a);\n\t\t\t\tif (a > 0.0) {\n\t\t\t\t\treturn vec4(uOutlineColor, a) * vColor;\n\t\t\t\t}\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst color = options.color ?? [1.0, 1.0, 1.0];\n\t\tconst width = options.width ?? 1.0;\n\t\tconst texSize = options.textureSize ?? [renderer.width, renderer.height];\n\n\t\tthis.setUniform(\"uOutlineColor\", new Float32Array(color));\n\t\tthis.setUniform(\"uOutlineWidth\", width);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t}\n\n\t/**\n\t * set the outline color\n\t * @param {number[]} color - outline color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uOutlineColor\", new Float32Array(color));\n\t}\n\n\t/**\n\t * set the outline width\n\t * @param {number} width - outline width in pixels\n\t */\n\tsetWidth(width) {\n\t\tthis.setUniform(\"uOutlineWidth\", width);\n\t}\n\n\t/**\n\t * set the texture size for accurate outline width calculation\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct PixelateUniforms {\n\tuPixelSize : f32,\n\tuTextureSize : vec2f,\n};\n@group(3) @binding(0) var<uniform> fx : PixelateUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet texel = fx.uPixelSize / fx.uTextureSize;\n\tlet snapped = texel * floor(uv / texel) + texel * 0.5;\n\treturn textureSample(uTexture, uSampler, snapped) * vColor;\n}\n`;\n\n/**\n * A shader effect that pixelates the sprite by snapping UV coordinates\n * to a grid. Commonly used for teleport effects, transitions, or retro styling.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // moderate pixelation\n * mySprite.shader = new PixelateEffect(renderer, { size: 8.0 });\n * @example\n * // animate the pixelation (e.g. for teleport)\n * pixelate.setSize(pixelate.size + dt / 50);\n */\nexport default class PixelateEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.size=4.0] - pixel size in texels (higher = more pixelated)\n\t * @param {number[]} [options.textureSize=[256, 256]] - texture dimensions [width, height]\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uPixelSize;\n\t\t\tuniform vec2 uTextureSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tvec2 texel = uPixelSize / uTextureSize;\n\t\t\t\tvec2 snapped = texel * floor(uv / texel) + texel * 0.5;\n\t\t\t\treturn texture2D(uSampler, snapped) * vColor;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.size = options.size ?? 4.0;\n\t\tconst texSize = options.textureSize ?? [256, 256];\n\n\t\tthis.setUniform(\"uPixelSize\", this.size);\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array(texSize));\n\t}\n\n\t/**\n\t * set the pixel size\n\t * @param {number} size - pixel size in texels\n\t */\n\tsetSize(size) {\n\t\tthis.size = Math.max(1, size);\n\t\tthis.setUniform(\"uPixelSize\", this.size);\n\t}\n\n\t/**\n\t * set the texture size\n\t * @param {number} width - texture width in pixels\n\t * @param {number} height - texture height in pixels\n\t */\n\tsetTextureSize(width, height) {\n\t\tthis.setUniform(\"uTextureSize\", new Float32Array([width, height]));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct ScanlineUniforms {\n\tuScanlineOpacity : f32,\n\tuCurvature : f32,\n\tuVignetteStrength : f32,\n};\n@group(3) @binding(0) var<uniform> fx : ScanlineUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tvar c = color;\n\tvar coords = uv;\n\n\t// barrel distortion (CRT curvature)\n\tif (fx.uCurvature > 0.0) {\n\t\tvar centered = uv * 2.0 - 1.0;\n\t\tcentered *= 1.0 + fx.uCurvature * dot(centered, centered);\n\t\tcoords = centered * 0.5 + 0.5;\n\t\tif (coords.x < 0.0 || coords.x > 1.0 || coords.y < 0.0 || coords.y > 1.0) {\n\t\t\tdiscard;\n\t\t}\n\t\tc = textureSampleLevel(uTexture, uSampler, coords, 0.0) * vColor;\n\t}\n\n\t// scanlines\n\tlet line = sin(coords.y * 800.0) * 0.5 + 0.5;\n\tc = vec4f(c.rgb * (1.0 - line * fx.uScanlineOpacity), c.a);\n\n\t// vignette\n\tif (fx.uVignetteStrength > 0.0) {\n\t\tlet vig = coords * (1.0 - coords);\n\t\tlet vigFactor = vig.x * vig.y * 15.0;\n\t\tc = vec4f(c.rgb * clamp(pow(vigFactor, fx.uVignetteStrength), 0.0, 1.0), c.a);\n\t}\n\n\treturn c;\n}\n`;\n\n/**\n * A shader effect that overlays horizontal scanlines on the sprite.\n * Can optionally add barrel distortion and vignette for a full CRT monitor look.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // simple scanlines\n * mySprite.shader = new ScanlineEffect(renderer, { opacity: 0.3 });\n * @example\n * // full CRT look with curvature and vignette\n * mySprite.shader = new ScanlineEffect(renderer, {\n *     opacity: 0.3,\n *     curvature: 0.02,\n *     vignetteStrength: 0.3,\n * });\n */\nexport default class ScanlineEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.opacity=0.25] - scanline darkness (0.0 = invisible, 1.0 = fully black lines)\n\t * @param {number} [options.curvature=0.0] - barrel distortion strength (0.0 = flat, 0.02 = subtle CRT curve)\n\t * @param {number} [options.vignetteStrength=0.0] - edge darkening strength (0.0 = none, 0.3 = subtle)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uScanlineOpacity;\n\t\t\tuniform float uCurvature;\n\t\t\tuniform float uVignetteStrength;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tvec2 coords = uv;\n\n\t\t\t\t// barrel distortion (CRT curvature)\n\t\t\t\tif (uCurvature > 0.0) {\n\t\t\t\t\tvec2 centered = uv * 2.0 - 1.0;\n\t\t\t\t\tcentered *= 1.0 + uCurvature * dot(centered, centered);\n\t\t\t\t\tcoords = centered * 0.5 + 0.5;\n\t\t\t\t\tif (coords.x < 0.0 || coords.x > 1.0 || coords.y < 0.0 || coords.y > 1.0) {\n\t\t\t\t\t\tdiscard;\n\t\t\t\t\t}\n\t\t\t\t\tcolor = texture2D(uSampler, coords) * vColor;\n\t\t\t\t}\n\n\t\t\t\t// scanlines\n\t\t\t\tfloat line = sin(coords.y * 800.0) * 0.5 + 0.5;\n\t\t\t\tcolor.rgb *= 1.0 - line * uScanlineOpacity;\n\n\t\t\t\t// vignette\n\t\t\t\tif (uVignetteStrength > 0.0) {\n\t\t\t\t\tvec2 vig = coords * (1.0 - coords);\n\t\t\t\t\tfloat vigFactor = vig.x * vig.y * 15.0;\n\t\t\t\t\tcolor.rgb *= clamp(pow(vigFactor, uVignetteStrength), 0.0, 1.0);\n\t\t\t\t}\n\n\t\t\t\treturn color;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.setUniform(\"uScanlineOpacity\", options.opacity ?? 0.25);\n\t\tthis.setUniform(\"uCurvature\", options.curvature ?? 0.0);\n\t\tthis.setUniform(\"uVignetteStrength\", options.vignetteStrength ?? 0.0);\n\t}\n\n\t/**\n\t * set the scanline opacity\n\t * @param {number} opacity - scanline darkness (0.0–1.0)\n\t */\n\tsetOpacity(opacity) {\n\t\tthis.setUniform(\"uScanlineOpacity\", Math.max(0, Math.min(1, opacity)));\n\t}\n\n\t/**\n\t * set the barrel curvature strength\n\t * @param {number} curvature - distortion amount (0.0 = flat)\n\t */\n\tsetCurvature(curvature) {\n\t\tthis.setUniform(\"uCurvature\", curvature);\n\t}\n\n\t/**\n\t * set the vignette strength\n\t * @param {number} strength - edge darkening (0.0 = none)\n\t */\n\tsetVignetteStrength(strength) {\n\t\tthis.setUniform(\"uVignetteStrength\", strength);\n\t}\n}\n","import ColorMatrixEffect from \"./colorMatrix.js\";\n\n/**\n * A shader effect that applies a warm sepia (vintage photo) tone to the sprite.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * mySprite.shader = new SepiaEffect(renderer);\n * @example\n * // partial sepia\n * mySprite.shader = new SepiaEffect(renderer, { intensity: 0.5 });\n */\nexport default class SepiaEffect extends ColorMatrixEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.intensity=1.0] - sepia intensity (0.0 = original, 1.0 = full sepia)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer);\n\t\tthis.intensity =\n\t\t\ttypeof options.intensity === \"number\" ? options.intensity : 1.0;\n\t\tthis.sepia(this.intensity);\n\t}\n\n\t/**\n\t * set the sepia intensity\n\t * @param {number} value - sepia intensity (0.0 = original, 1.0 = full sepia)\n\t */\n\tsetIntensity(value) {\n\t\tthis.intensity = Math.max(0, Math.min(1, value));\n\t\tthis.reset().sepia(this.intensity);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct ShineUniforms {\n\tuShineColor : vec3f,\n\tuShineWidth : f32,\n\tuShineSpeed : f32,\n\tuShineIntensity : f32,\n\tuShineAngle : f32,\n\tuShineBands : f32,\n\tuPulseDepth : f32,\n\tuPulseSpeed : f32,\n\tuTime : f32,\n\tuUVYDir : f32,\n};\n@group(3) @binding(0) var<uniform> fx : ShineUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tif (color.a == 0.0) {\n\t\treturn color;\n\t}\n\t// Optional brightness pulse on the base color.\n\tlet pulse = (1.0 - fx.uPulseDepth) + fx.uPulseDepth * sin(fx.uTime * fx.uPulseSpeed);\n\t// Directional UV arithmetic — an ABSOLUTE coordinate, so a flipped\n\t// space mirrors the position (1 - uv.y), not merely the sign (see the\n\t// ShaderEffect uUVYDir docs; the renderer feeds the space's direction)\n\tlet y = select(uv.y, 1.0 - uv.y, fx.uUVYDir < 0.0);\n\t// Project uv along the sweep axis; tile by uShineBands (wrap-around\n\t// distance keeps sweeps gapless — see the GLSL twin's rationale).\n\tlet pos = uv.x * cos(fx.uShineAngle) + y * sin(fx.uShineAngle);\n\tlet localX = fract(pos * fx.uShineBands);\n\tlet sweep = fract(fx.uTime * fx.uShineSpeed);\n\tlet d = abs(localX - sweep);\n\tlet dist = min(d, 1.0 - d);\n\tlet glint = smoothstep(fx.uShineWidth, 0.0, dist) * fx.uShineIntensity;\n\t// premultiplied-alpha discipline mirrors the GLSL twin\n\tlet result = color.rgb * pulse + fx.uShineColor * glint * color.a;\n\treturn vec4f(result, color.a);\n}\n`;\n\n/**\n * A shader effect that sweeps a bright highlight band across the sprite —\n * the classic \"shine\" pass commonly used for coins, gems, polished metal,\n * and hover-highlighted UI elements.\n *\n * Set `bands` > 1 to tile the sweep into N parallel glints (useful for the\n * \"etched grooves\" look of a coin's rim). An optional subtle brightness\n * pulse can be layered on top via `pulseDepth` — set to 0 to disable.\n *\n * The `time` uniform must be updated each frame for the animation.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // single diagonal sweep — classic \"button shine\"\n * const shine = new ShineEffect(renderer, {\n *     color: [1.0, 0.95, 0.7],\n *     speed: 0.5,\n *     width: 0.18,\n *     angle: 0.5,\n * });\n *\n * // gold coin with ~14 parallel glints and a subtle brightness pulse\n * const coinShine = new ShineEffect(renderer, {\n *     color: [1.0, 0.95, 0.7],\n *     bands: 14.5,\n *     width: 0.15,\n *     intensity: 0.4,\n *     speed: 0.8,\n *     pulseDepth: 0.08,\n * });\n *\n * mySprite.addPostEffect(coinShine);\n *\n * // update each frame\n * coinShine.setTime(timer.getTime() / 1000);\n */\nexport default class ShineEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[1.0, 1.0, 1.0]] - shine color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.speed=0.5] - sweeps per second\n\t * @param {number} [options.width=0.15] - glint half-width as a fraction of one tile (0.0–1.0)\n\t * @param {number} [options.intensity=0.5] - maximum highlight strength\n\t * @param {number} [options.angle=0.5] - sweep direction in radians (0 = horizontal L→R, π/2 = vertical T→B)\n\t * @param {number} [options.bands=1.0] - number of parallel glints (1 = single shine; ~14.5 mimics a coin's etched-rim look)\n\t * @param {number} [options.pulseDepth=0.0] - subtle base-brightness pulse amplitude (0 disables the pulse)\n\t * @param {number} [options.pulseSpeed=3.0] - pulse oscillation rate (radians/second)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3  uShineColor;\n\t\t\tuniform float uShineWidth;\n\t\t\tuniform float uShineSpeed;\n\t\t\tuniform float uShineIntensity;\n\t\t\tuniform float uShineAngle;\n\t\t\tuniform float uShineBands;\n\t\t\tuniform float uPulseDepth;\n\t\t\tuniform float uPulseSpeed;\n\t\t\tuniform float uTime;\n\t\t\tuniform float uUVYDir;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tif (color.a == 0.0) return color;\n\t\t\t\t// Optional brightness pulse on the base color.\n\t\t\t\tfloat pulse = (1.0 - uPulseDepth) + uPulseDepth * sin(uTime * uPulseSpeed);\n\t\t\t\t// Directional UV arithmetic — an ABSOLUTE coordinate, so a\n\t\t\t\t// flipped space mirrors the position (1.0 - uv.y), not just\n\t\t\t\t// the sign (see the ShaderEffect uUVYDir docs); without it,\n\t\t\t\t// a vertical sweep runs backwards on bottom-up captures\n\t\t\t\tfloat y = uUVYDir < 0.0 ? 1.0 - uv.y : uv.y;\n\t\t\t\t// Project uv along the sweep axis (uShineAngle).\n\t\t\t\tfloat pos = uv.x * cos(uShineAngle) + y * sin(uShineAngle);\n\t\t\t\t// Tile by uShineBands so we get N parallel glints in unison.\n\t\t\t\t// localX and sweep both live in [0,1] tile-space; the\n\t\t\t\t// wrap-around distance (min(d, 1-d)) lets the glint exit\n\t\t\t\t// the right edge of a tile and immediately re-enter from\n\t\t\t\t// the left edge of the next, so there is NO perceived\n\t\t\t\t// pause between sweeps. Previous implementation padded\n\t\t\t\t// the sweep range to [-W, 1+W] which created a visible\n\t\t\t\t// off-screen gap of duration ~2W/(1+2W) per cycle.\n\t\t\t\tfloat localX = fract(pos * uShineBands);\n\t\t\t\tfloat sweep = fract(uTime * uShineSpeed);\n\t\t\t\tfloat d = abs(localX - sweep);\n\t\t\t\tfloat dist = min(d, 1.0 - d);\n\t\t\t\tfloat glint = smoothstep(uShineWidth, 0.0, dist) * uShineIntensity;\n\t\t\t\t// color.rgb is already premultiplied by alpha — multiply the\n\t\t\t\t// added glint by color.a so it stays premultiplied too, but\n\t\t\t\t// do NOT scale the whole result by color.a (that would dim\n\t\t\t\t// antialiased edges twice).\n\t\t\t\tvec3 result = color.rgb * pulse + uShineColor * glint * color.a;\n\t\t\t\treturn vec4(result, color.a);\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.setUniform(\n\t\t\t\"uShineColor\",\n\t\t\tnew Float32Array(options.color ?? [1.0, 1.0, 1.0]),\n\t\t);\n\t\tthis.setUniform(\"uShineWidth\", options.width ?? 0.15);\n\t\tthis.setUniform(\"uShineSpeed\", options.speed ?? 0.5);\n\t\tthis.setUniform(\"uShineIntensity\", options.intensity ?? 0.5);\n\t\tthis.setUniform(\"uShineAngle\", options.angle ?? 0.5);\n\t\tthis.setUniform(\"uShineBands\", options.bands ?? 1.0);\n\t\tthis.setUniform(\"uPulseDepth\", options.pulseDepth ?? 0.0);\n\t\tthis.setUniform(\"uPulseSpeed\", options.pulseSpeed ?? 3.0);\n\t\tthis.setUniform(\"uTime\", 0.0);\n\t\t// top-down default; the batchers feed the actual space direction\n\t\t// per draw through _setUVYDir (bottom-up pooled captures on GL)\n\t\tthis.setUniform(\"uUVYDir\", 1.0);\n\t}\n\n\t/**\n\t * set the shine color\n\t * @param {number[]} color - shine color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uShineColor\", new Float32Array(color));\n\t}\n\n\t/**\n\t * set the sweep speed\n\t * @param {number} value - sweeps per second\n\t */\n\tsetSpeed(value) {\n\t\tthis.setUniform(\"uShineSpeed\", value);\n\t}\n\n\t/**\n\t * set the highlight intensity\n\t * @param {number} value - maximum highlight strength\n\t */\n\tsetIntensity(value) {\n\t\tthis.setUniform(\"uShineIntensity\", value);\n\t}\n\n\t/**\n\t * set the number of parallel glints\n\t * @param {number} value - 1 for a single shine, >1 for tiled stripes\n\t */\n\tsetBands(value) {\n\t\tthis.setUniform(\"uShineBands\", value);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct TintPulseUniforms {\n\tuPulseColor : vec3f,\n\tuPulseSpeed : f32,\n\tuPulseIntensity : f32,\n\tuTime : f32,\n};\n@group(3) @binding(0) var<uniform> fx : TintPulseUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet pulse = (sin(fx.uTime * fx.uPulseSpeed * 6.2832) * 0.5 + 0.5) * fx.uPulseIntensity;\n\treturn vec4f(mix(color.rgb, fx.uPulseColor * color.a, pulse), color.a);\n}\n`;\n\n/**\n * A shader effect that pulses a color overlay on the sprite.\n * Commonly used for status effects — poison green, freeze blue, fire red.\n * The `time` uniform should be updated each frame for the pulse animation.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // poison pulse\n * const poison = new TintPulseEffect(renderer, {\n *     color: [0.0, 1.0, 0.0],\n *     speed: 3.0,\n * });\n * mySprite.shader = poison;\n *\n * // update each frame\n * poison.setTime(timer.getTime() / 1000);\n */\nexport default class TintPulseEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number[]} [options.color=[1.0, 0.0, 0.0]] - pulse color as [r, g, b] (0.0–1.0)\n\t * @param {number} [options.speed=2.0] - pulse speed (oscillations per second)\n\t * @param {number} [options.intensity=0.3] - maximum tint strength (0.0–1.0)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform vec3 uPulseColor;\n\t\t\tuniform float uPulseSpeed;\n\t\t\tuniform float uPulseIntensity;\n\t\t\tuniform float uTime;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tfloat pulse = (sin(uTime * uPulseSpeed * 6.2832) * 0.5 + 0.5) * uPulseIntensity;\n\t\t\t\treturn vec4(mix(color.rgb, uPulseColor * color.a, pulse), color.a);\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tconst color = options.color ?? [1.0, 0.0, 0.0];\n\t\tthis.setUniform(\"uPulseColor\", new Float32Array(color));\n\t\tthis.setUniform(\"uPulseSpeed\", options.speed ?? 2.0);\n\t\tthis.setUniform(\"uPulseIntensity\", options.intensity ?? 0.3);\n\t\tthis.setUniform(\"uTime\", 0.0);\n\t}\n\n\t/**\n\t * set the current time (call each frame for animation)\n\t * @param {number} time - time in seconds\n\t */\n\tsetTime(time) {\n\t\tthis.setUniform(\"uTime\", time);\n\t}\n\n\t/**\n\t * set the pulse color\n\t * @param {number[]} color - pulse color as [r, g, b] (0.0–1.0)\n\t */\n\tsetColor(color) {\n\t\tthis.setUniform(\"uPulseColor\", new Float32Array(color));\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the effect body, dual-authored: same logic, same uniform names, one body\n// per shading language — the ShaderEffect base picks the one matching the\n// active renderer (renderer.shaderLanguage)\nconst fragment = `\n\t\t\tuniform float uStrength;\n\t\t\tuniform float uSize;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tvec2 vig = uv * (1.0 - uv);\n\t\t\t\tfloat v = clamp(pow(vig.x * vig.y * uSize, uStrength), 0.0, 1.0);\n\t\t\t\treturn vec4(color.rgb * v, color.a);\n\t\t\t}\n\t\t\t`;\n\nconst wgslFragment = `\nstruct VignetteUniforms {\n\tuStrength : f32,\n\tuSize : f32,\n};\n@group(3) @binding(0) var<uniform> fx : VignetteUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet vig = uv * (1.0 - uv);\n\tlet v = clamp(pow(vig.x * vig.y * fx.uSize, fx.uStrength), 0.0, 1.0);\n\treturn vec4f(color.rgb * v, color.a);\n}\n`;\n\n/**\n * A shader effect that darkens the edges of the screen, drawing focus\n * to the center. Commonly used for atmosphere, cinematic feel, or to\n * naturally frame a camera viewport (e.g. minimap).\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * // subtle vignette on the main camera\n * app.viewport.shader = new VignetteEffect(renderer);\n * @example\n * // stronger cinematic vignette\n * app.viewport.shader = new VignetteEffect(renderer, {\n *     strength: 0.3,\n *     size: 20.0,\n * });\n */\nexport default class VignetteEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.strength=0.15] - edge darkening power (lower = stronger darkening, higher = softer falloff)\n\t * @param {number} [options.size=25.0] - vignette spread multiplier (higher = smaller dark area)\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, { glsl: fragment, wgsl: wgslFragment });\n\n\t\tthis.strength = options.strength ?? 0.15;\n\t\tthis.size = options.size ?? 25.0;\n\n\t\tthis.setUniform(\"uStrength\", this.strength);\n\t\tthis.setUniform(\"uSize\", this.size);\n\t}\n\n\t/**\n\t * set the vignette strength\n\t * @param {number} strength - edge darkening power (lower = stronger, higher = softer)\n\t */\n\tsetStrength(strength) {\n\t\tthis.strength = Math.max(0, strength);\n\t\tthis.setUniform(\"uStrength\", this.strength);\n\t}\n\n\t/**\n\t * set the vignette size\n\t * @param {number} size - spread multiplier (higher = smaller dark area)\n\t */\n\tsetSize(size) {\n\t\tthis.size = Math.max(0, size);\n\t\tthis.setUniform(\"uSize\", this.size);\n\t}\n}\n","import ShaderEffect from \"./shadereffect.js\";\n\n// the WGSL twin of the GLSL body below — same logic, same uniform\n// names, picked by the ShaderEffect base per renderer.shaderLanguage\nconst wgslFragment = `\nstruct WaveUniforms {\n\tuAmplitude : f32,\n\tuFrequency : f32,\n\tuSpeed : f32,\n\tuTime : f32,\n};\n@group(3) @binding(0) var<uniform> fx : WaveUniforms;\n\nfn apply(color : vec4f, uv : vec2f) -> vec4f {\n\tlet wave = sin(uv.y * fx.uFrequency + fx.uTime * fx.uSpeed) * fx.uAmplitude;\n\tlet distorted = vec2f(uv.x + wave, uv.y);\n\treturn textureSample(uTexture, uSampler, distorted) * vColor;\n}\n`;\n\n/**\n * A shader effect that applies a sine wave distortion to the sprite.\n * Commonly used for underwater, heat haze, or dream sequence effects.\n * The `time` uniform should be updated each frame for animation.\n * @category Effects\n * @see {@link Renderable.shader} for usage\n * @example\n * const wave = new WaveEffect(renderer, { amplitude: 0.01, frequency: 10.0 });\n * mySprite.shader = wave;\n * // update each frame\n * wave.setTime(performance.now() / 1000);\n */\nexport default class WaveEffect extends ShaderEffect {\n\t/**\n\t * @param {import(\"../webgl_renderer.js\").default} renderer - the current renderer instance\n\t * @param {object} [options] - effect options\n\t * @param {number} [options.amplitude=0.01] - wave displacement strength (in UV space, 0.01 = subtle)\n\t * @param {number} [options.frequency=10.0] - number of waves across the sprite\n\t * @param {number} [options.speed=2.0] - wave animation speed\n\t */\n\tconstructor(renderer, options = {}) {\n\t\tsuper(renderer, {\n\t\t\tglsl: `\n\t\t\tuniform float uAmplitude;\n\t\t\tuniform float uFrequency;\n\t\t\tuniform float uSpeed;\n\t\t\tuniform float uTime;\n\t\t\tvec4 apply(vec4 color, vec2 uv) {\n\t\t\t\tfloat wave = sin(uv.y * uFrequency + uTime * uSpeed) * uAmplitude;\n\t\t\t\tvec2 distorted = vec2(uv.x + wave, uv.y);\n\t\t\t\treturn texture2D(uSampler, distorted) * vColor;\n\t\t\t}\n\t\t\t`,\n\t\t\twgsl: wgslFragment,\n\t\t});\n\n\t\tthis.setUniform(\"uAmplitude\", options.amplitude ?? 0.01);\n\t\tthis.setUniform(\"uFrequency\", options.frequency ?? 10.0);\n\t\tthis.setUniform(\"uSpeed\", options.speed ?? 2.0);\n\t\tthis.setUniform(\"uTime\", 0.0);\n\t}\n\n\t/**\n\t * set the current time (call each frame for animation)\n\t * @param {number} time - time in seconds\n\t */\n\tsetTime(time) {\n\t\tthis.setUniform(\"uTime\", time);\n\t}\n\n\t/**\n\t * set the wave amplitude\n\t * @param {number} amplitude - displacement strength in UV space\n\t */\n\tsetAmplitude(amplitude) {\n\t\tthis.setUniform(\"uAmplitude\", amplitude);\n\t}\n\n\t/**\n\t * set the wave frequency\n\t * @param {number} frequency - number of waves\n\t */\n\tsetFrequency(frequency) {\n\t\tthis.setUniform(\"uFrequency\", frequency);\n\t}\n}\n","/**\n * Coherent value/gradient noise with fractal (fBm / ridged) layering.\n *\n * `Noise` is the renderer-free *algorithm*: a CPU-samplable field you can read\n * directly with {@link Noise#getNoise2d} / {@link Noise#getNoise3d} for gameplay\n * (heightmaps, terrain, spawn jitter, flow fields), or feed to a\n * {@link NoiseTexture2d} to bake it into a drawable texture.\n *\n * Each sample returns a value in the `[-1, 1]` range. The field is fully\n * deterministic for a given `seed`, so the same parameters always reproduce the\n * same field.\n * @category Math\n * @example\n * const noise = new me.Noise({ type: \"simplex\", seed: 1337, frequency: 0.02, octaves: 4 });\n * const h = noise.getNoise2d(x, y); // -1 .. 1\n */\n\nexport type NoiseType =\n\t| \"simplex\"\n\t| \"perlin\"\n\t| \"value\"\n\t| \"valueCubic\"\n\t| \"cellular\";\nexport type FractalType = \"none\" | \"fbm\" | \"ridged\" | \"pingPong\";\n\nexport interface NoiseSettings {\n\t/** the sampling algorithm (default `\"simplex\"`) */\n\ttype?: NoiseType;\n\t/** deterministic seed — same seed ⇒ same field (default `0`) */\n\tseed?: number;\n\t/** base frequency: smaller ⇒ larger, smoother features (default `0.01`) */\n\tfrequency?: number;\n\t/** convenience alias for `1 / frequency` (feature size in pixels) */\n\tscale?: number;\n\t/** fractal layering mode (default `\"fbm\"` when `octaves > 1`, else `\"none\"`) */\n\tfractalType?: FractalType;\n\t/** number of fractal octaves summed together (default `1`) */\n\toctaves?: number;\n\t/** per-octave frequency multiplier (default `2.0`) */\n\tlacunarity?: number;\n\t/** per-octave amplitude multiplier (default `0.5`) */\n\tgain?: number;\n\t/** convenience alias for `gain` */\n\tpersistence?: number;\n\t/** strength of the ping-pong fold when `fractalType === \"pingPong\"` (default `2.0`) */\n\tpingPongStrength?: number;\n\t/** feature-point jitter for `type === \"cellular\"` (0..1, default `1.0`) */\n\tcellularJitter?: number;\n\t/** perturb sample coordinates by a noise vector before sampling (default `false`) */\n\tdomainWarp?: boolean;\n\t/** displacement amplitude when `domainWarp` (in coordinate units, default `30`) */\n\tdomainWarpAmp?: number;\n\t/** frequency of the warp field when `domainWarp` (default `0.01`) */\n\tdomainWarpFrequency?: number;\n\t/** constant offset added to sample coordinates (default `0`) */\n\toffsetX?: number;\n\t/** constant offset added to sample coordinates (default `0`) */\n\toffsetY?: number;\n\t/** constant offset added to sample coordinates (default `0`) */\n\toffsetZ?: number;\n}\n\n// 12 gradient directions for 3D simplex/perlin (also used for 2D, ignoring z).\n// prettier-ignore\nconst GRAD3 = [\n\t1, 1, 0, -1, 1, 0, 1, -1, 0, -1, -1, 0, 1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0,\n\t-1, 0, 1, 1, 0, -1, 1, 0, 1, -1, 0, -1, -1,\n];\n\nconst F2 = 0.5 * (Math.sqrt(3) - 1);\nconst G2 = (3 - Math.sqrt(3)) / 6;\nconst F3 = 1 / 3;\nconst G3 = 1 / 6;\n\n// deterministic PRNG (mulberry32): maps a 32-bit seed to a [0,1) stream.\nconst mulberry32 = (seed: number) => {\n\tlet a = seed >>> 0;\n\treturn () => {\n\t\ta |= 0;\n\t\ta = (a + 0x6d2b79f5) | 0;\n\t\tlet t = Math.imul(a ^ (a >>> 15), 1 | a);\n\t\tt = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n};\n\nconst smootherstep = (t: number) => {\n\treturn t * t * t * (t * (t * 6 - 15) + 10);\n};\n\nconst lerp = (a: number, b: number, t: number) => {\n\treturn a + (b - a) * t;\n};\n\n// triangle wave in [0,1] — folds the input back and forth (ping-pong fractal)\nconst pingPong = (t: number) => {\n\tconst m = t - Math.floor(t * 0.5) * 2;\n\treturn m < 1 ? m : 2 - m;\n};\n\n// Catmull-Rom cubic interpolation through b,c at t∈[0,1] (a,d are the outer\n// control points) — used by valueCubic noise\nconst cubic = (a: number, b: number, c: number, d: number, t: number) => {\n\tconst t2 = t * t;\n\tconst t3 = t2 * t;\n\treturn (\n\t\ta * (-0.5 * t3 + t2 - 0.5 * t) +\n\t\tb * (1.5 * t3 - 2.5 * t2 + 1) +\n\t\tc * (-1.5 * t3 + 2 * t2 + 0.5 * t) +\n\t\td * (0.5 * t3 - 0.5 * t2)\n\t);\n};\n\nexport class Noise {\n\ttype: NoiseType;\n\tseed: number;\n\tfrequency: number;\n\tfractalType: FractalType;\n\toctaves: number;\n\tlacunarity: number;\n\tgain: number;\n\tpingPongStrength: number;\n\tcellularJitter: number;\n\tdomainWarp: boolean;\n\tdomainWarpAmp: number;\n\tdomainWarpFrequency: number;\n\toffsetX: number;\n\toffsetY: number;\n\toffsetZ: number;\n\n\t// doubled permutation table (512 entries), seeded from `seed`\n\t/** @ignore */\n\tprivate _perm: Uint8Array;\n\n\tconstructor(settings: NoiseSettings = {}) {\n\t\tthis.type = settings.type ?? \"simplex\";\n\t\tthis.seed = settings.seed ?? 0;\n\t\tthis.frequency =\n\t\t\tsettings.frequency ??\n\t\t\t(settings.scale ? 1 / settings.scale : undefined) ??\n\t\t\t0.01;\n\t\tthis.octaves = settings.octaves ?? 1;\n\t\tthis.fractalType =\n\t\t\tsettings.fractalType ?? (this.octaves > 1 ? \"fbm\" : \"none\");\n\t\tthis.lacunarity = settings.lacunarity ?? 2.0;\n\t\tthis.gain = settings.gain ?? settings.persistence ?? 0.5;\n\t\tthis.pingPongStrength = settings.pingPongStrength ?? 2.0;\n\t\tthis.cellularJitter = settings.cellularJitter ?? 1.0;\n\t\tthis.domainWarp = settings.domainWarp ?? false;\n\t\tthis.domainWarpAmp = settings.domainWarpAmp ?? 30;\n\t\tthis.domainWarpFrequency = settings.domainWarpFrequency ?? 0.01;\n\t\tthis.offsetX = settings.offsetX ?? 0;\n\t\tthis.offsetY = settings.offsetY ?? 0;\n\t\tthis.offsetZ = settings.offsetZ ?? 0;\n\t\tthis._perm = new Uint8Array(512);\n\t\tthis.seedNoise(this.seed);\n\t}\n\n\t/**\n\t * Re-seed the noise field. Same seed ⇒ same field.\n\t * @param seed - the new seed value\n\t * @returns this noise instance for chaining\n\t */\n\tseedNoise(seed: number) {\n\t\tthis.seed = seed;\n\t\tconst rng = mulberry32(seed);\n\t\tconst p = new Uint8Array(256);\n\t\tfor (let i = 0; i < 256; i++) {\n\t\t\tp[i] = i;\n\t\t}\n\t\t// Fisher–Yates shuffle driven by the seeded PRNG\n\t\tfor (let i = 255; i > 0; i--) {\n\t\t\tconst j = Math.floor(rng() * (i + 1));\n\t\t\tconst tmp = p[i];\n\t\t\tp[i] = p[j];\n\t\t\tp[j] = tmp;\n\t\t}\n\t\tfor (let i = 0; i < 512; i++) {\n\t\t\tthis._perm[i] = p[i & 255];\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sample the 1D noise field (a 2D slice at `y = 0`).\n\t * @param x - sample coordinate\n\t * @returns noise value in the `[-1, 1]` range\n\t */\n\tgetNoise1d(x: number) {\n\t\treturn this.getNoise2d(x, 0);\n\t}\n\n\t/**\n\t * Sample the 2D noise field at `(x, y)`.\n\t * @param x - horizontal sample coordinate\n\t * @param y - vertical sample coordinate\n\t * @returns noise value in the `[-1, 1]` range\n\t */\n\tgetNoise2d(x: number, y: number) {\n\t\tlet wx = x + this.offsetX;\n\t\tlet wy = y + this.offsetY;\n\t\tif (this.domainWarp) {\n\t\t\tconst wf = this.domainWarpFrequency;\n\t\t\tconst wa = this.domainWarpAmp;\n\t\t\tconst dx = this._simplex2d(wx * wf, wy * wf);\n\t\t\tconst dy = this._simplex2d(wx * wf + 101.7, wy * wf + 31.4);\n\t\t\twx += dx * wa;\n\t\t\twy += dy * wa;\n\t\t}\n\t\tlet px = wx * this.frequency;\n\t\tlet py = wy * this.frequency;\n\t\tif (this.fractalType === \"none\" || this.octaves <= 1) {\n\t\t\treturn this._single2d(px, py);\n\t\t}\n\t\tlet amp = 1;\n\t\tlet sum = 0;\n\t\tlet max = 0;\n\t\tfor (let o = 0; o < this.octaves; o++) {\n\t\t\tconst n = this._applyFractalShape(this._single2d(px, py));\n\t\t\tsum += n * amp;\n\t\t\tmax += amp;\n\t\t\tamp *= this.gain;\n\t\t\tpx *= this.lacunarity;\n\t\t\tpy *= this.lacunarity;\n\t\t}\n\t\treturn max > 0 ? sum / max : 0;\n\t}\n\n\t/**\n\t * Sample the 3D noise field at `(x, y, z)` — useful for animating a 2D field\n\t * by advancing `z` over time, or for volumetric/tiling effects.\n\t * @param x - sample coordinate\n\t * @param y - sample coordinate\n\t * @param z - sample coordinate\n\t * @returns noise value in the `[-1, 1]` range\n\t */\n\tgetNoise3d(x: number, y: number, z: number) {\n\t\tlet wx = x + this.offsetX;\n\t\tlet wy = y + this.offsetY;\n\t\tlet wz = z + this.offsetZ;\n\t\tif (this.domainWarp) {\n\t\t\tconst wf = this.domainWarpFrequency;\n\t\t\tconst wa = this.domainWarpAmp;\n\t\t\tconst dx = this._simplex3d(wx * wf, wy * wf, wz * wf);\n\t\t\tconst dy = this._simplex3d(wx * wf + 101.7, wy * wf + 31.4, wz * wf);\n\t\t\tconst dz = this._simplex3d(wx * wf, wy * wf + 57.1, wz * wf + 19.3);\n\t\t\twx += dx * wa;\n\t\t\twy += dy * wa;\n\t\t\twz += dz * wa;\n\t\t}\n\t\tlet px = wx * this.frequency;\n\t\tlet py = wy * this.frequency;\n\t\tlet pz = wz * this.frequency;\n\t\tif (this.fractalType === \"none\" || this.octaves <= 1) {\n\t\t\treturn this._single3d(px, py, pz);\n\t\t}\n\t\tlet amp = 1;\n\t\tlet sum = 0;\n\t\tlet max = 0;\n\t\tfor (let o = 0; o < this.octaves; o++) {\n\t\t\tconst n = this._applyFractalShape(this._single3d(px, py, pz));\n\t\t\tsum += n * amp;\n\t\t\tmax += amp;\n\t\t\tamp *= this.gain;\n\t\t\tpx *= this.lacunarity;\n\t\t\tpy *= this.lacunarity;\n\t\t\tpz *= this.lacunarity;\n\t\t}\n\t\treturn max > 0 ? sum / max : 0;\n\t}\n\n\t// per-octave fractal shaping: fbm (passthrough), ridged, or ping-pong\n\t/** @ignore */\n\tprivate _applyFractalShape(n: number) {\n\t\tif (this.fractalType === \"ridged\") {\n\t\t\tconst r = 1 - Math.abs(n);\n\t\t\treturn r * r * 2 - 1;\n\t\t}\n\t\tif (this.fractalType === \"pingPong\") {\n\t\t\treturn pingPong((n + 1) * this.pingPongStrength) * 2 - 1;\n\t\t}\n\t\treturn n;\n\t}\n\n\t// single-octave 2D sample dispatched on `type`\n\t/** @ignore */\n\tprivate _single2d(x: number, y: number) {\n\t\tif (this.type === \"value\") {\n\t\t\treturn this._value2d(x, y);\n\t\t}\n\t\tif (this.type === \"valueCubic\") {\n\t\t\treturn this._valueCubic2d(x, y);\n\t\t}\n\t\tif (this.type === \"perlin\") {\n\t\t\treturn this._perlin2d(x, y);\n\t\t}\n\t\tif (this.type === \"cellular\") {\n\t\t\treturn this._cellular2d(x, y);\n\t\t}\n\t\treturn this._simplex2d(x, y);\n\t}\n\n\t// single-octave 3D sample dispatched on `type`\n\t/** @ignore */\n\tprivate _single3d(x: number, y: number, z: number) {\n\t\tif (this.type === \"value\") {\n\t\t\treturn this._value3d(x, y, z);\n\t\t}\n\t\tif (this.type === \"valueCubic\") {\n\t\t\treturn this._valueCubic3d(x, y, z);\n\t\t}\n\t\tif (this.type === \"perlin\") {\n\t\t\treturn this._perlin3d(x, y, z);\n\t\t}\n\t\tif (this.type === \"cellular\") {\n\t\t\treturn this._cellular3d(x, y, z);\n\t\t}\n\t\treturn this._simplex3d(x, y, z);\n\t}\n\n\t// ── value noise ──────────────────────────────────────────────────────\n\n\t// hashed lattice value in [-1,1]\n\t/** @ignore */\n\tprivate _hash2d(ix: number, iy: number) {\n\t\tconst perm = this._perm;\n\t\treturn perm[(perm[ix & 255] + iy) & 255] / 127.5 - 1;\n\t}\n\n\t/** @ignore */\n\tprivate _value2d(x: number, y: number) {\n\t\tconst ix = Math.floor(x);\n\t\tconst iy = Math.floor(y);\n\t\tconst fx = smootherstep(x - ix);\n\t\tconst fy = smootherstep(y - iy);\n\t\tconst v00 = this._hash2d(ix, iy);\n\t\tconst v10 = this._hash2d(ix + 1, iy);\n\t\tconst v01 = this._hash2d(ix, iy + 1);\n\t\tconst v11 = this._hash2d(ix + 1, iy + 1);\n\t\treturn lerp(lerp(v00, v10, fx), lerp(v01, v11, fx), fy);\n\t}\n\n\t// hashed lattice value in [-1,1]\n\t/** @ignore */\n\tprivate _hash3d(ix: number, iy: number, iz: number) {\n\t\tconst perm = this._perm;\n\t\treturn perm[(perm[(perm[ix & 255] + iy) & 255] + iz) & 255] / 127.5 - 1;\n\t}\n\n\t/** @ignore */\n\tprivate _value3d(x: number, y: number, z: number) {\n\t\tconst ix = Math.floor(x);\n\t\tconst iy = Math.floor(y);\n\t\tconst iz = Math.floor(z);\n\t\tconst fx = smootherstep(x - ix);\n\t\tconst fy = smootherstep(y - iy);\n\t\tconst fz = smootherstep(z - iz);\n\t\tconst c000 = this._hash3d(ix, iy, iz);\n\t\tconst c100 = this._hash3d(ix + 1, iy, iz);\n\t\tconst c010 = this._hash3d(ix, iy + 1, iz);\n\t\tconst c110 = this._hash3d(ix + 1, iy + 1, iz);\n\t\tconst c001 = this._hash3d(ix, iy, iz + 1);\n\t\tconst c101 = this._hash3d(ix + 1, iy, iz + 1);\n\t\tconst c011 = this._hash3d(ix, iy + 1, iz + 1);\n\t\tconst c111 = this._hash3d(ix + 1, iy + 1, iz + 1);\n\t\tconst x00 = lerp(c000, c100, fx);\n\t\tconst x10 = lerp(c010, c110, fx);\n\t\tconst x01 = lerp(c001, c101, fx);\n\t\tconst x11 = lerp(c011, c111, fx);\n\t\treturn lerp(lerp(x00, x10, fy), lerp(x01, x11, fy), fz);\n\t}\n\n\t// ── value-cubic noise (Catmull-Rom interpolated lattice) ─────────────\n\n\t// cubic-interpolated row of 4 lattice values at row `cy`\n\t/** @ignore */\n\tprivate _cubicRow2d(xi: number, cy: number, fx: number) {\n\t\treturn cubic(\n\t\t\tthis._hash2d(xi - 1, cy),\n\t\t\tthis._hash2d(xi, cy),\n\t\t\tthis._hash2d(xi + 1, cy),\n\t\t\tthis._hash2d(xi + 2, cy),\n\t\t\tfx,\n\t\t);\n\t}\n\n\t/** @ignore */\n\tprivate _valueCubic2d(x: number, y: number) {\n\t\tconst xi = Math.floor(x);\n\t\tconst yi = Math.floor(y);\n\t\tconst fx = x - xi;\n\t\tconst fy = y - yi;\n\t\t// scalar locals (no per-sample array allocation)\n\t\tconst r0 = this._cubicRow2d(xi, yi - 1, fx);\n\t\tconst r1 = this._cubicRow2d(xi, yi, fx);\n\t\tconst r2 = this._cubicRow2d(xi, yi + 1, fx);\n\t\tconst r3 = this._cubicRow2d(xi, yi + 2, fx);\n\t\treturn cubic(r0, r1, r2, r3, fy);\n\t}\n\n\t// cubic-interpolated plane of 4×4 lattice values at depth `cz`\n\t/** @ignore */\n\tprivate _cubicPlane3d(\n\t\txi: number,\n\t\tyi: number,\n\t\tcz: number,\n\t\tfx: number,\n\t\tfy: number,\n\t) {\n\t\tconst r0 = cubic(\n\t\t\tthis._hash3d(xi - 1, yi - 1, cz),\n\t\t\tthis._hash3d(xi, yi - 1, cz),\n\t\t\tthis._hash3d(xi + 1, yi - 1, cz),\n\t\t\tthis._hash3d(xi + 2, yi - 1, cz),\n\t\t\tfx,\n\t\t);\n\t\tconst r1 = cubic(\n\t\t\tthis._hash3d(xi - 1, yi, cz),\n\t\t\tthis._hash3d(xi, yi, cz),\n\t\t\tthis._hash3d(xi + 1, yi, cz),\n\t\t\tthis._hash3d(xi + 2, yi, cz),\n\t\t\tfx,\n\t\t);\n\t\tconst r2 = cubic(\n\t\t\tthis._hash3d(xi - 1, yi + 1, cz),\n\t\t\tthis._hash3d(xi, yi + 1, cz),\n\t\t\tthis._hash3d(xi + 1, yi + 1, cz),\n\t\t\tthis._hash3d(xi + 2, yi + 1, cz),\n\t\t\tfx,\n\t\t);\n\t\tconst r3 = cubic(\n\t\t\tthis._hash3d(xi - 1, yi + 2, cz),\n\t\t\tthis._hash3d(xi, yi + 2, cz),\n\t\t\tthis._hash3d(xi + 1, yi + 2, cz),\n\t\t\tthis._hash3d(xi + 2, yi + 2, cz),\n\t\t\tfx,\n\t\t);\n\t\treturn cubic(r0, r1, r2, r3, fy);\n\t}\n\n\t/** @ignore */\n\tprivate _valueCubic3d(x: number, y: number, z: number) {\n\t\tconst xi = Math.floor(x);\n\t\tconst yi = Math.floor(y);\n\t\tconst zi = Math.floor(z);\n\t\tconst fx = x - xi;\n\t\tconst fy = y - yi;\n\t\tconst fz = z - zi;\n\t\t// scalar locals (no per-sample array allocation)\n\t\tconst p0 = this._cubicPlane3d(xi, yi, zi - 1, fx, fy);\n\t\tconst p1 = this._cubicPlane3d(xi, yi, zi, fx, fy);\n\t\tconst p2 = this._cubicPlane3d(xi, yi, zi + 1, fx, fy);\n\t\tconst p3 = this._cubicPlane3d(xi, yi, zi + 2, fx, fy);\n\t\treturn cubic(p0, p1, p2, p3, fz);\n\t}\n\n\t// ── cellular (Worley F1) noise ───────────────────────────────────────\n\n\t/** @ignore */\n\tprivate _cellular2d(x: number, y: number) {\n\t\tconst perm = this._perm;\n\t\tconst jitter = this.cellularJitter;\n\t\tconst xi = Math.floor(x);\n\t\tconst yi = Math.floor(y);\n\t\tlet minDist = 8;\n\t\tfor (let oy = -1; oy <= 1; oy++) {\n\t\t\tfor (let ox = -1; ox <= 1; ox++) {\n\t\t\t\tconst cx = xi + ox;\n\t\t\t\tconst cy = yi + oy;\n\t\t\t\tconst h = perm[(perm[cx & 255] + (cy & 255)) & 255];\n\t\t\t\t// two pseudo-independent jitter offsets in [0,1)\n\t\t\t\tconst fx = cx + (h / 255) * jitter;\n\t\t\t\tconst fy = cy + (perm[(h + 71) & 255] / 255) * jitter;\n\t\t\t\tconst dx = fx - x;\n\t\t\t\tconst dy = fy - y;\n\t\t\t\tconst d = dx * dx + dy * dy;\n\t\t\t\tif (d < minDist) {\n\t\t\t\t\tminDist = d;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// map F1 distance (~[0,1]) to [-1,1]: feature cores dark, edges bright\n\t\treturn Math.min(1, Math.sqrt(minDist)) * 2 - 1;\n\t}\n\n\t/** @ignore */\n\tprivate _cellular3d(x: number, y: number, z: number) {\n\t\tconst perm = this._perm;\n\t\tconst jitter = this.cellularJitter;\n\t\tconst xi = Math.floor(x);\n\t\tconst yi = Math.floor(y);\n\t\tconst zi = Math.floor(z);\n\t\tlet minDist = 16;\n\t\tfor (let oz = -1; oz <= 1; oz++) {\n\t\t\tfor (let oy = -1; oy <= 1; oy++) {\n\t\t\t\tfor (let ox = -1; ox <= 1; ox++) {\n\t\t\t\t\tconst cx = xi + ox;\n\t\t\t\t\tconst cy = yi + oy;\n\t\t\t\t\tconst cz = zi + oz;\n\t\t\t\t\tconst h =\n\t\t\t\t\t\tperm[\n\t\t\t\t\t\t\t(perm[(perm[cx & 255] + (cy & 255)) & 255] + (cz & 255)) & 255\n\t\t\t\t\t\t];\n\t\t\t\t\tconst fx = cx + (h / 255) * jitter;\n\t\t\t\t\tconst fy = cy + (perm[(h + 71) & 255] / 255) * jitter;\n\t\t\t\t\tconst fz = cz + (perm[(h + 157) & 255] / 255) * jitter;\n\t\t\t\t\tconst dx = fx - x;\n\t\t\t\t\tconst dy = fy - y;\n\t\t\t\t\tconst dz = fz - z;\n\t\t\t\t\tconst d = dx * dx + dy * dy + dz * dz;\n\t\t\t\t\tif (d < minDist) {\n\t\t\t\t\t\tminDist = d;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Math.min(1, Math.sqrt(minDist)) * 2 - 1;\n\t}\n\n\t// ── perlin (improved) noise ──────────────────────────────────────────\n\n\t/** @ignore */\n\tprivate _grad2(hash: number, x: number, y: number) {\n\t\tconst h = (hash & 7) * 3;\n\t\treturn GRAD3[h] * x + GRAD3[h + 1] * y;\n\t}\n\n\t/** @ignore */\n\tprivate _perlin2d(x: number, y: number) {\n\t\tconst perm = this._perm;\n\t\tconst xi = Math.floor(x) & 255;\n\t\tconst yi = Math.floor(y) & 255;\n\t\tconst xf = x - Math.floor(x);\n\t\tconst yf = y - Math.floor(y);\n\t\tconst u = smootherstep(xf);\n\t\tconst v = smootherstep(yf);\n\t\tconst aa = perm[perm[xi] + yi];\n\t\tconst ab = perm[perm[xi] + yi + 1];\n\t\tconst ba = perm[perm[xi + 1] + yi];\n\t\tconst bb = perm[perm[xi + 1] + yi + 1];\n\t\tconst x1 = lerp(this._grad2(aa, xf, yf), this._grad2(ba, xf - 1, yf), u);\n\t\tconst x2 = lerp(\n\t\t\tthis._grad2(ab, xf, yf - 1),\n\t\t\tthis._grad2(bb, xf - 1, yf - 1),\n\t\t\tu,\n\t\t);\n\t\t// scale ~[-1,1]\n\t\treturn lerp(x1, x2, v) * 1.4;\n\t}\n\n\t/** @ignore */\n\tprivate _grad3(hash: number, x: number, y: number, z: number) {\n\t\tconst h = (hash % 12) * 3;\n\t\treturn GRAD3[h] * x + GRAD3[h + 1] * y + GRAD3[h + 2] * z;\n\t}\n\n\t/** @ignore */\n\tprivate _perlin3d(x: number, y: number, z: number) {\n\t\tconst perm = this._perm;\n\t\tconst xi = Math.floor(x) & 255;\n\t\tconst yi = Math.floor(y) & 255;\n\t\tconst zi = Math.floor(z) & 255;\n\t\tconst xf = x - Math.floor(x);\n\t\tconst yf = y - Math.floor(y);\n\t\tconst zf = z - Math.floor(z);\n\t\tconst u = smootherstep(xf);\n\t\tconst v = smootherstep(yf);\n\t\tconst w = smootherstep(zf);\n\t\tconst aaa = perm[perm[perm[xi] + yi] + zi];\n\t\tconst aba = perm[perm[perm[xi] + yi + 1] + zi];\n\t\tconst aab = perm[perm[perm[xi] + yi] + zi + 1];\n\t\tconst abb = perm[perm[perm[xi] + yi + 1] + zi + 1];\n\t\tconst baa = perm[perm[perm[xi + 1] + yi] + zi];\n\t\tconst bba = perm[perm[perm[xi + 1] + yi + 1] + zi];\n\t\tconst bab = perm[perm[perm[xi + 1] + yi] + zi + 1];\n\t\tconst bbb = perm[perm[perm[xi + 1] + yi + 1] + zi + 1];\n\t\tconst x1 = lerp(\n\t\t\tthis._grad3(aaa, xf, yf, zf),\n\t\t\tthis._grad3(baa, xf - 1, yf, zf),\n\t\t\tu,\n\t\t);\n\t\tconst x2 = lerp(\n\t\t\tthis._grad3(aba, xf, yf - 1, zf),\n\t\t\tthis._grad3(bba, xf - 1, yf - 1, zf),\n\t\t\tu,\n\t\t);\n\t\tconst y1 = lerp(x1, x2, v);\n\t\tconst x3 = lerp(\n\t\t\tthis._grad3(aab, xf, yf, zf - 1),\n\t\t\tthis._grad3(bab, xf - 1, yf, zf - 1),\n\t\t\tu,\n\t\t);\n\t\tconst x4 = lerp(\n\t\t\tthis._grad3(abb, xf, yf - 1, zf - 1),\n\t\t\tthis._grad3(bbb, xf - 1, yf - 1, zf - 1),\n\t\t\tu,\n\t\t);\n\t\tconst y2 = lerp(x3, x4, v);\n\t\treturn lerp(y1, y2, w) * 1.4;\n\t}\n\n\t// ── simplex noise (Gustavson) ────────────────────────────────────────\n\n\t/** @ignore */\n\tprivate _simplex2d(xin: number, yin: number) {\n\t\tconst perm = this._perm;\n\t\tlet n0 = 0;\n\t\tlet n1 = 0;\n\t\tlet n2 = 0;\n\t\tconst s = (xin + yin) * F2;\n\t\tconst i = Math.floor(xin + s);\n\t\tconst j = Math.floor(yin + s);\n\t\tconst t = (i + j) * G2;\n\t\tconst x0 = xin - (i - t);\n\t\tconst y0 = yin - (j - t);\n\t\tlet i1: number;\n\t\tlet j1: number;\n\t\tif (x0 > y0) {\n\t\t\ti1 = 1;\n\t\t\tj1 = 0;\n\t\t} else {\n\t\t\ti1 = 0;\n\t\t\tj1 = 1;\n\t\t}\n\t\tconst x1 = x0 - i1 + G2;\n\t\tconst y1 = y0 - j1 + G2;\n\t\tconst x2 = x0 - 1 + 2 * G2;\n\t\tconst y2 = y0 - 1 + 2 * G2;\n\t\tconst ii = i & 255;\n\t\tconst jj = j & 255;\n\t\tlet t0 = 0.5 - x0 * x0 - y0 * y0;\n\t\tif (t0 >= 0) {\n\t\t\tt0 *= t0;\n\t\t\tconst gi0 = (perm[ii + perm[jj]] % 12) * 3;\n\t\t\tn0 = t0 * t0 * (GRAD3[gi0] * x0 + GRAD3[gi0 + 1] * y0);\n\t\t}\n\t\tlet t1 = 0.5 - x1 * x1 - y1 * y1;\n\t\tif (t1 >= 0) {\n\t\t\tt1 *= t1;\n\t\t\tconst gi1 = (perm[ii + i1 + perm[jj + j1]] % 12) * 3;\n\t\t\tn1 = t1 * t1 * (GRAD3[gi1] * x1 + GRAD3[gi1 + 1] * y1);\n\t\t}\n\t\tlet t2 = 0.5 - x2 * x2 - y2 * y2;\n\t\tif (t2 >= 0) {\n\t\t\tt2 *= t2;\n\t\t\tconst gi2 = (perm[ii + 1 + perm[jj + 1]] % 12) * 3;\n\t\t\tn2 = t2 * t2 * (GRAD3[gi2] * x2 + GRAD3[gi2 + 1] * y2);\n\t\t}\n\t\treturn 70 * (n0 + n1 + n2);\n\t}\n\n\t/** @ignore */\n\tprivate _simplex3d(xin: number, yin: number, zin: number) {\n\t\tconst perm = this._perm;\n\t\tlet n0 = 0;\n\t\tlet n1 = 0;\n\t\tlet n2 = 0;\n\t\tlet n3 = 0;\n\t\tconst s = (xin + yin + zin) * F3;\n\t\tconst i = Math.floor(xin + s);\n\t\tconst j = Math.floor(yin + s);\n\t\tconst k = Math.floor(zin + s);\n\t\tconst t = (i + j + k) * G3;\n\t\tconst x0 = xin - (i - t);\n\t\tconst y0 = yin - (j - t);\n\t\tconst z0 = zin - (k - t);\n\t\tlet i1: number;\n\t\tlet j1: number;\n\t\tlet k1: number;\n\t\tlet i2: number;\n\t\tlet j2: number;\n\t\tlet k2: number;\n\t\tif (x0 >= y0) {\n\t\t\tif (y0 >= z0) {\n\t\t\t\ti1 = 1;\n\t\t\t\tj1 = 0;\n\t\t\t\tk1 = 0;\n\t\t\t\ti2 = 1;\n\t\t\t\tj2 = 1;\n\t\t\t\tk2 = 0;\n\t\t\t} else if (x0 >= z0) {\n\t\t\t\ti1 = 1;\n\t\t\t\tj1 = 0;\n\t\t\t\tk1 = 0;\n\t\t\t\ti2 = 1;\n\t\t\t\tj2 = 0;\n\t\t\t\tk2 = 1;\n\t\t\t} else {\n\t\t\t\ti1 = 0;\n\t\t\t\tj1 = 0;\n\t\t\t\tk1 = 1;\n\t\t\t\ti2 = 1;\n\t\t\t\tj2 = 0;\n\t\t\t\tk2 = 1;\n\t\t\t}\n\t\t} else {\n\t\t\tif (y0 < z0) {\n\t\t\t\ti1 = 0;\n\t\t\t\tj1 = 0;\n\t\t\t\tk1 = 1;\n\t\t\t\ti2 = 0;\n\t\t\t\tj2 = 1;\n\t\t\t\tk2 = 1;\n\t\t\t} else if (x0 < z0) {\n\t\t\t\ti1 = 0;\n\t\t\t\tj1 = 1;\n\t\t\t\tk1 = 0;\n\t\t\t\ti2 = 0;\n\t\t\t\tj2 = 1;\n\t\t\t\tk2 = 1;\n\t\t\t} else {\n\t\t\t\ti1 = 0;\n\t\t\t\tj1 = 1;\n\t\t\t\tk1 = 0;\n\t\t\t\ti2 = 1;\n\t\t\t\tj2 = 1;\n\t\t\t\tk2 = 0;\n\t\t\t}\n\t\t}\n\t\tconst x1 = x0 - i1 + G3;\n\t\tconst y1 = y0 - j1 + G3;\n\t\tconst z1 = z0 - k1 + G3;\n\t\tconst x2 = x0 - i2 + 2 * G3;\n\t\tconst y2 = y0 - j2 + 2 * G3;\n\t\tconst z2 = z0 - k2 + 2 * G3;\n\t\tconst x3 = x0 - 1 + 3 * G3;\n\t\tconst y3 = y0 - 1 + 3 * G3;\n\t\tconst z3 = z0 - 1 + 3 * G3;\n\t\tconst ii = i & 255;\n\t\tconst jj = j & 255;\n\t\tconst kk = k & 255;\n\t\tlet t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;\n\t\tif (t0 >= 0) {\n\t\t\tt0 *= t0;\n\t\t\tconst gi0 = (perm[ii + perm[jj + perm[kk]]] % 12) * 3;\n\t\t\tn0 =\n\t\t\t\tt0 * t0 * (GRAD3[gi0] * x0 + GRAD3[gi0 + 1] * y0 + GRAD3[gi0 + 2] * z0);\n\t\t}\n\t\tlet t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;\n\t\tif (t1 >= 0) {\n\t\t\tt1 *= t1;\n\t\t\tconst gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12) * 3;\n\t\t\tn1 =\n\t\t\t\tt1 * t1 * (GRAD3[gi1] * x1 + GRAD3[gi1 + 1] * y1 + GRAD3[gi1 + 2] * z1);\n\t\t}\n\t\tlet t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;\n\t\tif (t2 >= 0) {\n\t\t\tt2 *= t2;\n\t\t\tconst gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12) * 3;\n\t\t\tn2 =\n\t\t\t\tt2 * t2 * (GRAD3[gi2] * x2 + GRAD3[gi2 + 1] * y2 + GRAD3[gi2 + 2] * z2);\n\t\t}\n\t\tlet t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;\n\t\tif (t3 >= 0) {\n\t\t\tt3 *= t3;\n\t\t\tconst gi3 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12) * 3;\n\t\t\tn3 =\n\t\t\t\tt3 * t3 * (GRAD3[gi3] * x3 + GRAD3[gi3 + 1] * y3 + GRAD3[gi3 + 2] * z3);\n\t\t}\n\t\treturn 32 * (n0 + n1 + n2 + n3);\n\t}\n}\n","import { Color } from \"../../math/color.ts\";\nimport { Noise } from \"../../math/noise.ts\";\nimport Renderer from \"../renderer.js\";\nimport Texture2d from \"./texture2d.ts\";\n\nconst smoothstep = (t) => {\n\tconst c = t < 0 ? 0 : t > 1 ? 1 : t;\n\treturn c * c * (3 - 2 * c);\n};\n\nconst lerp = (a, b, t) => {\n\treturn a + (b - a) * t;\n};\n\n/**\n * A {@link Texture2d} that bakes a {@link Noise} field into a drawable canvas —\n * usable directly as a sprite image, a normal map, an image layer, or a custom\n * shader sampler.\n *\n * The bake runs on the CPU (renderer-agnostic, works under both the Canvas and\n * WebGL backends) and produces an `HTMLCanvasElement` returned by\n * {@link NoiseTexture2d#getTexture}. Three output modes:\n * - **grayscale** (default) — the noise value mapped to `[0, 255]`.\n * - **colorRamp** — the noise value mapped through a {@link Gradient}.\n * - **asNormalMap** — the noise treated as a height field and encoded as\n *   tangent-space surface normals (RGB), for per-pixel lighting via\n *   {@link Sprite#normalMap} + {@link Light2d}.\n *\n * With `seamless: true` the texture's edges are cross-faded so it tiles with a\n * much-reduced seam (an approximate, not pixel-exact, wrap); `seamlessBlendSkirt`\n * controls how wide that edge blend band is. With `animated: true` the field is\n * sampled in 3D (`getNoise3d`) using an internal `time` as the third axis —\n * call {@link NoiseTexture2d#update} from your update loop to evolve it. Each\n * re-bake bumps a `version` the renderer reads, so the GPU texture re-uploads\n * automatically (and only when it actually changed — explicit, version-based\n * invalidation).\n *\n * Note: live re-upload is currently wired through the lit **normal-map**\n * pipeline ({@link Sprite#normalMap} + {@link Light2d}). An `animated` texture\n * used as a plain color `image` bakes a static snapshot — the color texture\n * cache does not yet honor `version` — so animate normals, not albedos, for now.\n * @augments Texture2d\n * @category Game Objects\n * @example\n * // a seamless water normal map fed to a lit sprite\n * const ripples = new me.NoiseTexture2d({\n *     width: 256, height: 256,\n *     type: \"simplex\", octaves: 4, frequency: 0.03,\n *     seamless: true, asNormalMap: true, bumpStrength: 2, animated: true, speed: 0.6,\n * });\n * const water = new me.Sprite(x, y, { image: albedo, normalMap: ripples });\n * // drive it yourself from your Stage's update(dt):\n * ripples.update(dt);\n */\nclass NoiseTexture2d extends Texture2d {\n\t/**\n\t * @param {object} [settings] - configuration; any {@link Noise} setting\n\t * (`type`, `seed`, `frequency`/`scale`, `octaves`, `gain`/`persistence`,\n\t * `lacunarity`, `fractalType`, domain-warp settings) is forwarded when no\n\t * `noise` instance is given.\n\t * @param {number} [settings.width=256] - baked texture width in pixels\n\t * @param {number} [settings.height=256] - baked texture height in pixels\n\t * @param {Noise} [settings.noise] - an existing {@link Noise} to bake; when\n\t * omitted one is built from the forwarded settings\n\t * @param {boolean} [settings.seamless=false] - tile cleanly in both axes\n\t * @param {number} [settings.seamlessBlendSkirt=0.1] - edge blend band width\n\t * as a fraction (0..1) of the smaller dimension, when `seamless`\n\t * @param {boolean} [settings.invert=false] - invert the noise value (`1 - v`)\n\t * @param {boolean} [settings.asNormalMap=false] - encode as a normal map\n\t * @param {number} [settings.bumpStrength=1] - normal steepness when `asNormalMap`\n\t * @param {Gradient} [settings.colorRamp] - map the noise value to a color\n\t * @param {boolean} [settings.animated=false] - sample in 3D (`getNoise3d`)\n\t * using an internal `time` as the third axis, advanced by {@link NoiseTexture2d#update}\n\t * @param {number} [settings.speed=1] - animation speed in noise z-units per\n\t * second (only used while `animated`)\n\t */\n\tconstructor(settings = {}) {\n\t\tsuper();\n\n\t\t/** baked texture width in pixels @type {number} */\n\t\tthis.width = settings.width ?? 256;\n\t\t/** baked texture height in pixels @type {number} */\n\t\tthis.height = settings.height ?? 256;\n\t\t/** the noise field baked by this texture @type {Noise} */\n\t\tthis.noise =\n\t\t\tsettings.noise instanceof Noise ? settings.noise : new Noise(settings);\n\t\t/** tile cleanly in both axes @type {boolean} */\n\t\tthis.seamless = settings.seamless ?? false;\n\t\t/** edge blend band width (0..1 of the smaller dimension) @type {number} */\n\t\tthis.seamlessBlendSkirt = Math.min(\n\t\t\t1,\n\t\t\tMath.max(0, settings.seamlessBlendSkirt ?? 0.1),\n\t\t);\n\t\t/** invert the noise value @type {boolean} */\n\t\tthis.invert = settings.invert ?? false;\n\t\t/** encode the field as a tangent-space normal map @type {boolean} */\n\t\tthis.asNormalMap = settings.asNormalMap ?? false;\n\t\t/** normal steepness when `asNormalMap` @type {number} */\n\t\tthis.bumpStrength = settings.bumpStrength ?? 1.0;\n\t\t/** optional color ramp applied to the noise value @type {Gradient|null} */\n\t\tthis.colorRamp = settings.colorRamp ?? null;\n\t\t/** sample the field in 3D using `time` as the third axis @type {boolean} */\n\t\tthis.animated = settings.animated ?? false;\n\t\t/** animation speed in noise z-units per second @type {number} */\n\t\tthis.speed = settings.speed ?? 1;\n\t\t/** the current animation time (third sampling axis) @type {number} */\n\t\tthis.time = 0;\n\n\t\t/** the baked output canvas (reused across re-bakes) @type {HTMLCanvasElement|null} @ignore */\n\t\tthis._canvas = null;\n\t\t/** reused ImageData scratch (avoids a per-bake allocation) @type {ImageData|null} @ignore */\n\t\tthis._imageData = null;\n\t\t/** reused height-field scratch (avoids a per-bake allocation) @type {Float32Array|null} @ignore */\n\t\tthis._heights = null;\n\t\t/** content revision, bumped on every bake and stamped on the canvas so\n\t\t * the renderer re-uploads the GPU texture when it changes @type {number} @ignore */\n\t\tthis._version = 0;\n\n\t\tthis.bake();\n\t}\n\n\t/**\n\t * The current content revision, bumped on every {@link NoiseTexture2d#bake}.\n\t * Also stamped on the baked canvas (`getTexture().version`) so the renderer\n\t * re-uploads the GPU texture only when it actually changed.\n\t * @returns {number}\n\t */\n\tget version() {\n\t\treturn this._version;\n\t}\n\n\t/**\n\t * (Re)bake the texture from the current settings into the (reused) output\n\t * canvas. Called once on construction; {@link NoiseTexture2d#update} calls it\n\t * again per frame for animated textures.\n\t * @returns {NoiseTexture2d} this texture for chaining\n\t */\n\tbake() {\n\t\tconst w = this.width;\n\t\tconst h = this.height;\n\t\t// reuse the same canvas so the texture reference stays stable across\n\t\t// re-bakes (so an in-place re-upload can replace its GPU texture)\n\t\tif (this._canvas === null) {\n\t\t\tthis._canvas = Renderer.createCanvas(w, h);\n\t\t}\n\t\tconst ctx = this._canvas.getContext(\"2d\");\n\t\t// reuse the ImageData scratch across re-bakes (animated textures re-bake\n\t\t// every frame — avoid the per-frame allocation)\n\t\tif (this._imageData === null) {\n\t\t\tthis._imageData = ctx.createImageData(w, h);\n\t\t}\n\t\tconst img = this._imageData;\n\n\t\tconst heights = this._buildHeightField();\n\t\tif (this.asNormalMap) {\n\t\t\tthis._writeNormalMap(img.data, heights);\n\t\t} else if (this.colorRamp) {\n\t\t\tthis._writeColorRamp(img.data, heights);\n\t\t} else {\n\t\t\tthis._writeGrayscale(img.data, heights);\n\t\t}\n\n\t\tctx.putImageData(img, 0, 0);\n\t\t// bump the content revision and stamp it on the canvas; the renderer\n\t\t// reads `canvas.version` and re-uploads the GPU texture when it changes.\n\t\tthis._version++;\n\t\tthis._canvas.version = this._version;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Advance an animated texture by `dt` milliseconds and re-bake it. The\n\t * re-bake bumps the texture's `version`, so the renderer re-uploads the GPU\n\t * texture automatically on the next draw — no renderer/app handle needed.\n\t *\n\t * This is NOT auto-called by the engine; drive it from your own update loop\n\t * (e.g. {@link Stage#update}) for animated textures. No-op when not `animated`.\n\t * @param {number} dt - elapsed time since the last update, in milliseconds\n\t * @returns {NoiseTexture2d} this texture for chaining\n\t */\n\tupdate(dt) {\n\t\tif (this.animated) {\n\t\t\tthis.time += (dt / 1000) * this.speed;\n\t\t\tthis.bake();\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Raw noise sample at a texture pixel (2D, or 3D when animated). The time\n\t * axis is divided by the noise frequency so that, after `getNoise3d` scales\n\t * all coords by frequency, the field evolves at `speed` noise-units/second —\n\t * decoupled from the spatial frequency (otherwise low-frequency textures\n\t * would animate imperceptibly slowly).\n\t * @ignore\n\t */\n\t_rawNoise(x, y) {\n\t\tif (!this.animated) {\n\t\t\treturn this.noise.getNoise2d(x, y);\n\t\t}\n\t\tconst z = this.time / (this.noise.frequency || 1);\n\t\treturn this.noise.getNoise3d(x, y, z);\n\t}\n\n\t/**\n\t * x-axis seamless blend: in the right skirt band, cross-fade toward the\n\t * wrapped (period-shifted) sample so the left and right edges meet.\n\t * @ignore\n\t */\n\t_blendX(x, y, w, sk) {\n\t\tconst base = this._rawNoise(x, y);\n\t\tif (x <= w - sk) {\n\t\t\treturn base;\n\t\t}\n\t\tconst t = smoothstep((x - (w - sk)) / sk);\n\t\treturn lerp(base, this._rawNoise(x - w, y), t);\n\t}\n\n\t/**\n\t * Sample the noise into a `[0, 1]` height field. When `seamless`, a skirt\n\t * cross-fade near the far edges blends opposite edges so the texture tiles\n\t * with a much-reduced seam.\n\t * @returns {Float32Array} the per-pixel height field (length `width*height`)\n\t * @ignore\n\t */\n\t_buildHeightField() {\n\t\tconst w = this.width;\n\t\tconst h = this.height;\n\t\t// reused across re-bakes (animated textures re-bake every frame)\n\t\tif (this._heights === null) {\n\t\t\tthis._heights = new Float32Array(w * h);\n\t\t}\n\t\tconst heights = this._heights;\n\t\tconst sk = Math.max(\n\t\t\t1,\n\t\t\tMath.round(this.seamlessBlendSkirt * Math.min(w, h)),\n\t\t);\n\t\tfor (let y = 0; y < h; y++) {\n\t\t\tfor (let x = 0; x < w; x++) {\n\t\t\t\tlet n;\n\t\t\t\tif (this.seamless) {\n\t\t\t\t\tconst top = this._blendX(x, y, w, sk);\n\t\t\t\t\tif (y <= h - sk) {\n\t\t\t\t\t\tn = top;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst ty = smoothstep((y - (h - sk)) / sk);\n\t\t\t\t\t\tn = lerp(top, this._blendX(x, y - h, w, sk), ty);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tn = this._rawNoise(x, y);\n\t\t\t\t}\n\t\t\t\t// map [-1,1] → [0,1], clamp, optional invert\n\t\t\t\tlet v = n * 0.5 + 0.5;\n\t\t\t\tif (v < 0) {\n\t\t\t\t\tv = 0;\n\t\t\t\t} else if (v > 1) {\n\t\t\t\t\tv = 1;\n\t\t\t\t}\n\t\t\t\tif (this.invert) {\n\t\t\t\t\tv = 1 - v;\n\t\t\t\t}\n\t\t\t\theights[y * w + x] = v;\n\t\t\t}\n\t\t}\n\t\treturn heights;\n\t}\n\n\t/** @ignore */\n\t_writeGrayscale(data, heights) {\n\t\tfor (let i = 0; i < heights.length; i++) {\n\t\t\tconst c = Math.round(heights[i] * 255);\n\t\t\tconst o = i * 4;\n\t\t\tdata[o] = c;\n\t\t\tdata[o + 1] = c;\n\t\t\tdata[o + 2] = c;\n\t\t\tdata[o + 3] = 255;\n\t\t}\n\t}\n\n\t/** @ignore */\n\t_writeColorRamp(data, heights) {\n\t\tconst ramp = this.colorRamp;\n\t\tconst color = new Color();\n\t\tfor (let i = 0; i < heights.length; i++) {\n\t\t\tramp.getColorAt(heights[i], color);\n\t\t\tconst o = i * 4;\n\t\t\tdata[o] = Math.round(color.r);\n\t\t\tdata[o + 1] = Math.round(color.g);\n\t\t\tdata[o + 2] = Math.round(color.b);\n\t\t\tdata[o + 3] = Math.round(color.alpha * 255);\n\t\t}\n\t}\n\n\t/**\n\t * Encode the height field as a tangent-space normal map (RGB), with screen-Y\n\t * flipped to normal-map Y-up so it round-trips through the lit shader's\n\t * `rgb * 2 - 1` decode. Neighbor samples wrap when `seamless`.\n\t * @ignore\n\t */\n\t_writeNormalMap(data, heights) {\n\t\tconst w = this.width;\n\t\tconst h = this.height;\n\t\tconst strength = this.bumpStrength;\n\t\tconst seamless = this.seamless;\n\t\tfor (let y = 0; y < h; y++) {\n\t\t\tfor (let x = 0; x < w; x++) {\n\t\t\t\tconst xl = seamless ? (x - 1 + w) % w : Math.max(0, x - 1);\n\t\t\t\tconst xr = seamless ? (x + 1) % w : Math.min(w - 1, x + 1);\n\t\t\t\tconst yu = seamless ? (y - 1 + h) % h : Math.max(0, y - 1);\n\t\t\t\tconst yd = seamless ? (y + 1) % h : Math.min(h - 1, y + 1);\n\t\t\t\tconst hl = heights[y * w + xl];\n\t\t\t\tconst hr = heights[y * w + xr];\n\t\t\t\tconst hu = heights[yu * w + x];\n\t\t\t\tconst hd = heights[yd * w + x];\n\t\t\t\tlet nx = (hl - hr) * strength;\n\t\t\t\t// hd is the row below (larger screen-y); (hd - hu) flips screen-down\n\t\t\t\t// into normal-map Y-up.\n\t\t\t\tlet ny = (hd - hu) * strength;\n\t\t\t\tlet nz = 1;\n\t\t\t\tconst len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;\n\t\t\t\tnx /= len;\n\t\t\t\tny /= len;\n\t\t\t\tnz /= len;\n\t\t\t\tconst o = (y * w + x) * 4;\n\t\t\t\tdata[o] = Math.round((nx * 0.5 + 0.5) * 255);\n\t\t\t\tdata[o + 1] = Math.round((ny * 0.5 + 0.5) * 255);\n\t\t\t\tdata[o + 2] = Math.round((nz * 0.5 + 0.5) * 255);\n\t\t\t\tdata[o + 3] = 255;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @returns {HTMLCanvasElement} the baked texture canvas\n\t */\n\tgetTexture() {\n\t\treturn this._canvas;\n\t}\n\n\t/**\n\t * Release the baked canvas. The texture must not be used after destroy.\n\t */\n\tdestroy() {\n\t\t// broadcasts TEXTURE2D_DESTROYED with the baked canvas so the lit\n\t\t// batcher's normal-map cache frees its GL texture — before the\n\t\t// canvas reference is dropped below\n\t\tsuper.destroy();\n\t\tthis._canvas = null;\n\t\tthis._imageData = null;\n\t\tthis._heights = null;\n\t}\n}\n\nexport default NoiseTexture2d;\n","import { TupleToUnion } from \"type-fest\";\nimport { createPool } from \"../system/pool.ts\";\nimport { damp, lerp } from \"./math.ts\";\nimport { Vector2d } from \"./vector2d.ts\";\n\nconst propertiesToObserve = [\"x\", \"y\"] as const;\ntype ObservableProperty = keyof Pick<\n\tVector2d,\n\tTupleToUnion<typeof propertiesToObserve>\n>;\nconst setOfProperties = new Set<ObservableProperty>(propertiesToObserve);\n\nconst isObservableProperty = (\n\tproperty: string | symbol,\n): property is ObservableProperty => {\n\treturn setOfProperties.has(property as any);\n};\n\n/**\n * Represents a point in a 3D coordinate vector that can be observed for changes.\n * @category Math\n */\nexport class ObservableVector2d {\n\tprivate _callback: () => void;\n\tprivate _vector2d: Vector2d;\n\tprivate _revoke: () => void;\n\n\tprivate callBackEnabled: boolean = true;\n\n\t/**\n\t * Creates a new ObservableVector3d instance.\n\t * @param x - The x-coordinate of the vector. Default is 0.\n\t * @param y - The y-coordinate of the vector. Default is 0.\n\t * @param callback - The callback function to be called when the point changes. Default is undefined.\n\t */\n\tconstructor(x: number = 0, y: number = 0, callback?: () => void) {\n\t\tconst { proxy, revoke } = Proxy.revocable(new Vector2d(x, y), {\n\t\t\tset: (target, property, value) => {\n\t\t\t\tif (isObservableProperty(property)) {\n\t\t\t\t\tReflect.set(target, property, value);\n\t\t\t\t\tif (this.callBackEnabled) {\n\t\t\t\t\t\tthis._callback?.();\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t});\n\n\t\tthis._revoke = revoke;\n\t\tthis._vector2d = proxy;\n\n\t\tif (callback) {\n\t\t\tthis.setCallback(callback);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tset(x = 0, y = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._vector2d.x = x;\n\t\tthis._vector2d.y = y;\n\t\tthis._callback?.();\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the x and y coordinates of the point without triggering the callback.\n\t * @param x - The new x-coordinate value.\n\t * @param y - The new y-coordinate value.\n\t * @returns Reference to this object for method chaining.\n\t */\n\tsetMuted(x = 0, y = 0) {\n\t\tthis.callBackEnabled = false;\n\t\tthis._vector2d.x = x;\n\t\tthis._vector2d.y = y;\n\t\tthis.callBackEnabled = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Gets the x-coordinate of the point.\n\t */\n\tget x() {\n\t\treturn this._vector2d.x;\n\t}\n\n\t/**\n\t * Sets the x-coordinate of the point.\n\t * @param value - The new x-coordinate value.\n\t */\n\tset x(value: number) {\n\t\tthis._vector2d.x = value;\n\t}\n\n\t/**\n\t * Gets the y-coordinate of the point.\n\t */\n\tget y() {\n\t\treturn this._vector2d.y;\n\t}\n\n\t/**\n\t * Sets the y-coordinate of the point.\n\t * @param value - The new y-coordinate value.\n\t */\n\tset y(value: number) {\n\t\tthis._vector2d.y = value;\n\t}\n\n\t/**\n\t * Sets the callback function to be called when the point changes.\n\t * @param callback - The callback function.\n\t */\n\tsetCallback(callback: () => void) {\n\t\tthis._callback = callback;\n\t}\n\n\t/**\n\t * set the Vector x and y properties to 0\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetZero() {\n\t\treturn this.set(0, 0);\n\t}\n\n\t/**\n\t * set the Vector x and y properties using the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsetV(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(v.x, v.y);\n\t}\n\n\t/**\n\t * Add the passed vector to this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tadd(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(this.x + v.x, this.y + v.y);\n\t}\n\n\t/**\n\t * Subtract the passed vector from this vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tsub(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(this.x - v.x, this.y - v.y);\n\t}\n\n\t/**\n\t * Multiply this vector values by the given scalar\n\t * @param x x scale value\n\t * @param [y] y scale value, if not passed, it uses the x value\n\t * @returns Reference to this object for method chaining\n\t */\n\tscale(x: number, y = x) {\n\t\treturn this.set(this.x * x, this.y * y);\n\t}\n\n\t/**\n\t * Convert this vector into isometric coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\ttoIso() {\n\t\treturn this.set(this.x - this.y, (this.x + this.y) * 0.5);\n\t}\n\n\t/**\n\t * Convert this vector into 2d coordinate space\n\t * @returns Reference to this object for method chaining\n\t */\n\tto2d() {\n\t\treturn this.set(this.y + this.x / 2, this.y - this.x / 2);\n\t}\n\n\t/**\n\t * Multiply this vector values by the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tscaleV(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(this.x * v.x, this.y * v.y);\n\t}\n\n\t/**\n\t * Divide this vector values by the passed value\n\t * @param n - the value to divide the vector by\n\t * @returns Reference to this object for method chaining\n\t */\n\tdiv(n: number) {\n\t\treturn this.set(this.x / n, this.y / n);\n\t}\n\n\t/**\n\t * Update this vector values to absolute values\n\t * @returns Reference to this object for method chaining\n\t */\n\tabs() {\n\t\treturn this.set(\n\t\t\tthis.x < 0 ? -this.x : this.x,\n\t\t\tthis.y < 0 ? -this.y : this.y,\n\t\t);\n\t}\n\n\t/**\n\t * Clamp the vector value within the specified value range\n\t * @param low minimum component value\n\t * @param high maximum component value\n\t * @returns new me.Vector2d\n\t */\n\tclamp(low: number, high: number) {\n\t\treturn this._vector2d.clamp(low, high);\n\t}\n\n\t/**\n\t * Clamp this vector value within the specified value range\n\t * @param low minimum component value\n\t * @param high maximum component value\n\t * @returns Reference to this object for method chaining\n\t */\n\tclampSelf(low: number, high: number) {\n\t\treturn this._vector2d.clampSelf(low, high);\n\t}\n\n\t/**\n\t * Update this vector with the minimum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tminV(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(this.x < v.x ? this.x : v.x, this.y < v.y ? this.y : v.y);\n\t}\n\n\t/**\n\t * Update this vector with the maximum value between this and the passed vector\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tmaxV(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(this.x > v.x ? this.x : v.x, this.y > v.y ? this.y : v.y);\n\t}\n\n\t/**\n\t * Floor the vector values\n\t * @returns new Vector2d\n\t */\n\tfloor() {\n\t\treturn this._vector2d.floor();\n\t}\n\n\t/**\n\t * Floor this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tfloorSelf() {\n\t\treturn this._vector2d.floorSelf();\n\t}\n\n\t/**\n\t * Ceil the vector values\n\t * @returns new Vector2d\n\t */\n\tceil() {\n\t\treturn this._vector2d.ceil();\n\t}\n\n\t/**\n\t * Ceil this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tceilSelf() {\n\t\treturn this._vector2d.ceilSelf();\n\t}\n\n\t/**\n\t * Negate the vector values\n\t * @returns new Vector2d\n\t */\n\tnegate() {\n\t\treturn this._vector2d.negate();\n\t}\n\n\t/**\n\t * Negate this vector values\n\t * @returns Reference to this object for method chaining\n\t */\n\tnegateSelf() {\n\t\treturn this._vector2d.negateSelf();\n\t}\n\n\t/**\n\t * Copy the x,y values of the passed vector to this one\n\t * @param v other vector\n\t * @returns Reference to this object for method chaining\n\t */\n\tcopy(v: Vector2d | ObservableVector2d) {\n\t\treturn this.set(v.x, v.y);\n\t}\n\n\t/**\n\t * Checks if this vector is equal to another vector or a pair of coordinates.\n\t * @param args - Either two numbers representing x and y coordinates or a single Vector2d object.\n\t * @returns True if the coordinates are equal, false otherwise.\n\t * @example\n\t * let v1 = new Vector2d(3, 4);\n\t * let v2 = new Vector2d(3, 4);\n\t * v1.equals(v2); // returns true\n\t * @example\n\t * let v = new Vector2d(3, 4);\n\t * v.equals(3, 4); // returns true\n\t */\n\tequals(\n\t\t...args: [number, number] | [Vector2d] | [ObservableVector2d]\n\t): boolean {\n\t\tif (args.length === 2) {\n\t\t\treturn this.x === args[0] && this.y === args[1];\n\t\t}\n\t\treturn this.x === args[0].x && this.y === args[0].y;\n\t}\n\n\t/**\n\t * normalize this vector (scale the vector so that its magnitude is 1)\n\t * @returns Reference to this object for method chaining\n\t */\n\tnormalize() {\n\t\treturn this.div(this.length() || 1);\n\t}\n\n\t/**\n\t * change this vector to be perpendicular to what it was before.<br>\n\t * (Effectively rotates it 90 degrees in a clockwise direction)\n\t * @returns Reference to this object for method chaining\n\t */\n\tperp() {\n\t\treturn this.set(this.y, -this.x);\n\t}\n\n\t/**\n\t * Rotate this vector (counter-clockwise) by the specified angle (in radians).\n\t * @param angle - The angle to rotate (in radians)\n\t * @param [v] - an optional point to rotate around\n\t * @returns Reference to this object for method chaining\n\t */\n\trotate(angle: number, v?: Vector2d | ObservableVector2d) {\n\t\tlet cx = 0;\n\t\tlet cy = 0;\n\n\t\tif (v) {\n\t\t\tcx = v.x;\n\t\t\tcy = v.y;\n\t\t}\n\n\t\tconst x = this.x - cx;\n\t\tconst y = this.y - cy;\n\n\t\tconst c = Math.cos(angle);\n\t\tconst s = Math.sin(angle);\n\n\t\treturn this.set(x * c - y * s + cx, x * s + y * c + cy);\n\t}\n\n\t/**\n\t * return the dot product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The dot product.\n\t */\n\tdot(v: Vector2d | ObservableVector2d) {\n\t\treturn this._vector2d.dot(v as Vector2d);\n\t}\n\n\t/**\n\t * return the cross product of this vector and the passed one\n\t * @param v other vector\n\t * @returns The cross product.\n\t */\n\tcross(v: Vector2d | ObservableVector2d) {\n\t\treturn this._vector2d.cross(v as Vector2d);\n\t}\n\n\t/**\n\t * return the square length of this vector\n\t * @returns The length^2 of this vector.\n\t */\n\tlength2() {\n\t\treturn this._vector2d.length2();\n\t}\n\n\t/**\n\t * return the length (magnitude) of this vector\n\t * @returns the length of this vector\n\t */\n\tlength() {\n\t\treturn this._vector2d.length();\n\t}\n\n\t/**\n\t * Linearly interpolate between this vector and the given one.\n\t * Frame-rate dependent at a fixed alpha — for smooth tracking\n\t * use {@link ObservableVector2d.damp} instead.\n\t * @param v - other vector\n\t * @param alpha - distance along the line (alpha = 0 → this vector, alpha = 1 → `v`)\n\t * @returns Reference to this object for method chaining\n\t */\n\tlerp(v: Vector2d | ObservableVector2d, alpha: number) {\n\t\tthis.set(lerp(this.x, v.x, alpha), lerp(this.y, v.y, alpha));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Frame-rate independent exponential damping toward `target`.\n\t * Per-component wrapper around {@link damp} that triggers the\n\t * change-callback exactly once via the underlying {@link set}.\n\t * @param target - vector to approach\n\t * @param lambda - decay rate in `1/seconds` (higher = snappier)\n\t * @param dt - delta time in **seconds**\n\t * @returns Reference to this object for method chaining\n\t */\n\tdamp(target: Vector2d | ObservableVector2d, lambda: number, dt: number) {\n\t\tthis.set(\n\t\t\tdamp(this.x, target.x, lambda, dt),\n\t\t\tdamp(this.y, target.y, lambda, dt),\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * interpolate the position of this vector towards the given one by the given maximum step.\n\t * @param target vector to rotate towards\n\t * @param step - the maximum step per iteration (Negative values will push the vector away from the target)\n\t * @returns Reference to this object for method chaining\n\t */\n\tmoveTowards(target: Vector2d | ObservableVector2d, step: number) {\n\t\tconst distance = this.distance(target);\n\n\t\t// within one step (and not fleeing): land exactly on the target —\n\t\t// and always mutate/return `this` (via set(), so observers fire),\n\t\t// never the caller's target\n\t\tif (distance === 0 || (step >= 0 && distance <= step)) {\n\t\t\tthis.set(target.x, target.y);\n\t\t\treturn this;\n\t\t}\n\n\t\tconst angle = Math.atan2(target.y - this.y, target.x - this.x);\n\t\tthis.set(this.x + Math.cos(angle) * step, this.y + Math.sin(angle) * step);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Calculates the Euclidean distance between this vector and another vector.\n\t * @param v - The vector to which the distance is calculated.\n\t * @returns The Euclidean distance between this vector and the given vector.\n\t * @example\n\t * let v1 = new Vector2d(3, 4);\n\t * let v2 = new Vector2d(6, 8);\n\t * v1.distance(v2); // returns 5\n\t */\n\tdistance(v: Vector2d | ObservableVector2d) {\n\t\treturn this._vector2d.distance(v as Vector2d);\n\t}\n\n\t/**\n\t * return the angle between this vector and the passed one\n\t * @param v other vector\n\t * @returns angle in radians\n\t */\n\tangle(v: Vector2d | ObservableVector2d) {\n\t\treturn this._vector2d.angle(v as Vector2d);\n\t}\n\n\t/**\n\t * project this vector on to another vector.\n\t * @param v - The vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tproject(v: Vector2d | ObservableVector2d) {\n\t\treturn this.scale(this.dot(v) / v.length2());\n\t}\n\n\t/**\n\t * Project this vector onto a vector of unit length.<br>\n\t * This is slightly more efficient than `project` when dealing with unit vectors.\n\t * @param v - The unit vector to project onto.\n\t * @returns Reference to this object for method chaining\n\t */\n\tprojectN(v: Vector2d | ObservableVector2d) {\n\t\treturn this.scale(this.dot(v));\n\t}\n\n\t/**\n\t * return a clone copy of this vector\n\t * @param [cb] callback function to override the clone values\n\t * @returns new Vector2d\n\t */\n\tclone(cb?: () => void) {\n\t\treturn observableVector2dPool.get(this.x, this.y, cb);\n\t}\n\n\t/**\n\t * convert the object to a string representation\n\t * @returns stringified representation of this vector\n\t */\n\ttoString() {\n\t\treturn this._vector2d.toString();\n\t}\n\n\t/**\n\t * Revokes the proxy object, preventing further access to the ObservablePoint instance.\n\t */\n\trevoke() {\n\t\tthis._revoke();\n\t}\n}\n\nexport const observableVector2dPool = createPool<\n\tObservableVector2d,\n\t[x?: number | undefined, y?: number | undefined, callback?: () => void]\n>((x, y, cb) => {\n\tconst vector = new ObservableVector2d(x, y, cb);\n\n\treturn {\n\t\tinstance: vector,\n\t\treset(x = 0, y = 0, cb?: () => void): void {\n\t\t\tvector.setMuted(x, y);\n\t\t\tif (cb) {\n\t\t\t\tvector.setCallback(cb);\n\t\t\t}\n\t\t},\n\t};\n});\n","/**\n * @namespace video\n */\n\n/**\n * Renderer selection constants — see {@link CANVAS}, {@link WEBGL},\n * {@link AUTO} and {@link WEBGPU}. Pass one as the `renderer` application\n * setting: `new Application(w, h, { renderer: video.WEBGL })`.\n */\nexport { AUTO, CANVAS, WEBGL, WEBGPU } from \"../const\";\n","// ES5/ES6 polyfills\nimport \"./polyfill/index.ts\";\n\nimport Application from \"./application/application.ts\";\nimport Camera2d from \"./camera/camera2d.ts\";\nimport Camera3d from \"./camera/camera3d.ts\";\nimport CameraEffect from \"./camera/effects/camera_effect.ts\";\nimport FadeEffect from \"./camera/effects/fade_effect.ts\";\nimport MaskEffect from \"./camera/effects/mask_effect.ts\";\nimport ShakeEffect from \"./camera/effects/shake_effect.ts\";\nimport Frustum from \"./camera/frustum.ts\";\nimport Pointer from \"./input/pointer.ts\";\nimport GLTFModel from \"./level/gltf/GLTFModel.js\";\nimport TMXHexagonalRenderer from \"./level/tiled/renderer/TMXHexagonalRenderer.js\";\nimport TMXIsometricRenderer from \"./level/tiled/renderer/TMXIsometricRenderer.js\";\nimport TMXOrthogonalRenderer from \"./level/tiled/renderer/TMXOrthogonalRenderer.js\";\nimport TMXRenderer from \"./level/tiled/renderer/TMXRenderer.js\";\nimport TMXStaggeredRenderer from \"./level/tiled/renderer/TMXStaggeredRenderer.js\";\nimport TMXLayer from \"./level/tiled/TMXLayer.js\";\nimport Tile from \"./level/tiled/TMXTile.js\";\nimport TMXTileMap from \"./level/tiled/TMXTileMap.js\";\nimport TMXTileset from \"./level/tiled/TMXTileset.js\";\nimport TMXTilesetGroup from \"./level/tiled/TMXTilesetGroup.js\";\nimport * as TMXUtils from \"./level/tiled/TMXUtils.js\";\nimport Light2d from \"./lighting/light2d.ts\";\nimport { ColorMatrix } from \"./math/color_matrix.ts\";\nimport ParticleEmitter from \"./particles/emitter.ts\";\nimport Particle from \"./particles/particle.ts\";\nimport ParticleEmitterSettings from \"./particles/settings.js\";\n// class definition\nimport Body from \"./physics/builtin/body.js\";\nimport World from \"./physics/world.js\";\n// utility classes\nimport { cache as plugins } from \"./plugin/plugin.ts\";\nimport Collectable from \"./renderable/collectable.js\";\nimport ColorLayer from \"./renderable/colorlayer.js\";\nimport Container from \"./renderable/container.js\";\nimport { Draggable } from \"./renderable/draggable.js\";\nimport { DropTarget } from \"./renderable/dragndrop.js\";\nimport Entity from \"./renderable/entity/entity.js\";\nimport FrameAnimation from \"./renderable/frameAnimation.js\";\nimport ImageLayer from \"./renderable/imagelayer.js\";\nimport InstancedMesh from \"./renderable/instanced_mesh.js\";\nimport Mesh from \"./renderable/mesh.js\";\nimport NineSliceSprite from \"./renderable/nineslicesprite.js\";\nimport Renderable from \"./renderable/renderable.js\";\nimport Sprite from \"./renderable/sprite.js\";\nimport Sprite3d from \"./renderable/sprite3d.js\";\nimport BitmapText from \"./renderable/text/bitmaptext.js\";\nimport BitmapTextData from \"./renderable/text/bitmaptextdata.ts\";\nimport Text from \"./renderable/text/text.js\";\nimport Trail from \"./renderable/trail.js\";\nimport Trigger from \"./renderable/trigger.js\";\nimport UIBaseElement from \"./renderable/ui/uibaseelement.ts\";\nimport UISpriteElement from \"./renderable/ui/uispriteelement.ts\";\nimport UITextButton from \"./renderable/ui/uitextbutton.ts\";\nimport Stage from \"./state/stage.ts\";\nimport state from \"./state/state.ts\";\nimport { boot } from \"./system/bootstrap.ts\";\nimport { DOMContentLoaded } from \"./system/dom.ts\";\nimport pool from \"./system/legacy_pool.js\";\nimport save from \"./system/save.ts\";\nimport timer from \"./system/timer.ts\";\nimport Tween from \"./tweens/tween.ts\";\nimport CanvasRenderer from \"./video/canvas/canvas_renderer.js\";\nimport BlurEffect from \"./video/effects/blur.js\";\nimport ChromaticAberrationEffect from \"./video/effects/chromaticAberration.js\";\nimport ColorMatrixEffect from \"./video/effects/colorMatrix.js\";\nimport DesaturateEffect from \"./video/effects/desaturate.js\";\nimport DissolveEffect from \"./video/effects/dissolve.js\";\nimport DropShadowEffect from \"./video/effects/dropShadow.js\";\nimport FlashEffect from \"./video/effects/flash.js\";\nimport GlowEffect from \"./video/effects/glow.js\";\nimport HologramEffect from \"./video/effects/hologram.js\";\nimport InvertEffect from \"./video/effects/invert.js\";\nimport OutlineEffect from \"./video/effects/outline.js\";\nimport PixelateEffect from \"./video/effects/pixelate.js\";\nimport ScanlineEffect from \"./video/effects/scanline.js\";\nimport SepiaEffect from \"./video/effects/sepia.js\";\nimport ShaderEffect from \"./video/effects/shadereffect.js\";\nimport ShineEffect from \"./video/effects/shine.js\";\nimport TintPulseEffect from \"./video/effects/tintPulse.js\";\nimport VignetteEffect from \"./video/effects/vignette.js\";\nimport WaveEffect from \"./video/effects/wave.js\";\nimport { Batcher } from \"./video/gpu/batcher.js\";\nimport {\n\tisPortableTopology,\n\tisTopology,\n\tPORTABLE_TOPOLOGIES,\n} from \"./video/gpu/topology.ts\";\nimport {\n\tisVertexFormat,\n\tresolveVertexFormat,\n} from \"./video/gpu/vertexformat.ts\";\nimport { Gradient } from \"./video/gradient.js\";\nimport Renderer from \"./video/renderer.js\";\nimport RenderState from \"./video/renderstate.js\";\nimport CanvasRenderTarget from \"./video/rendertarget/canvasrendertarget.js\";\nimport RenderTarget from \"./video/rendertarget/rendertarget.ts\";\nimport { TextureAtlas } from \"./video/texture/atlas.js\";\nimport NoiseTexture2d from \"./video/texture/noise_texture2d.js\";\nimport Texture2d from \"./video/texture/texture2d.ts\";\nimport { WebGLBatcher } from \"./video/webgl/batchers/batcher.js\";\nimport PrimitiveBatcher from \"./video/webgl/batchers/primitive_batcher.js\";\nimport QuadBatcher from \"./video/webgl/batchers/quad_batcher.js\";\nimport GLShader from \"./video/webgl/glshader.js\";\nimport WebGLRenderer from \"./video/webgl/webgl_renderer.js\";\nimport WebGPUPrimitiveBatcher from \"./video/webgpu/batchers/primitive_batcher.js\";\nimport WebGPUQuadBatcher from \"./video/webgpu/batchers/quad_batcher.js\";\nimport WebGPUBatcher from \"./video/webgpu/batchers/webgpu_batcher.js\";\nimport WebGPURenderer from \"./video/webgpu/webgpu_renderer.js\";\n\nexport * from \"./application/scaleMethods.ts\";\nexport * from \"./application/settings.ts\";\n// export all utility function\nexport * as audio from \"./audio/audio.ts\";\n// export all public constants\nexport * from \"./const.ts\";\nexport { Box3d } from \"./geometries/box3d.ts\";\nexport { Ellipse } from \"./geometries/ellipse.ts\";\nexport { Line } from \"./geometries/line.ts\";\nexport { ObservablePoint } from \"./geometries/observablePoint.ts\";\nexport { Point } from \"./geometries/point.ts\";\nexport { Polygon } from \"./geometries/polygon.ts\";\nexport { Rect } from \"./geometries/rectangle.ts\";\nexport { RoundRect } from \"./geometries/roundrect.ts\";\nexport { Sphere } from \"./geometries/sphere.ts\";\nexport * as input from \"./input/input.ts\";\n// Backward compatibility for deprecated method or properties\nexport * from \"./lang/deprecated.js\";\nexport { level } from \"./level/level.js\";\nexport {\n\tregisterTiledObjectClass,\n\tregisterTiledObjectFactory,\n} from \"./level/tiled/TMXObjectFactory.js\";\nexport { Light3d } from \"./lighting/light3d.ts\";\n\nexport * as loader from \"./loader/loader.js\";\nexport { Color } from \"./math/color.ts\";\nexport * as math from \"./math/math.ts\";\nexport { Matrix2d } from \"./math/matrix2d.ts\";\nexport { Matrix3d } from \"./math/matrix3d.ts\";\nexport type { FractalType, NoiseSettings, NoiseType } from \"./math/noise.ts\";\nexport { Noise } from \"./math/noise.ts\";\nexport { ObservableVector2d } from \"./math/observableVector2d.ts\";\nexport { ObservableVector3d } from \"./math/observableVector3d.ts\";\nexport { Vector2d } from \"./math/vector2d.ts\";\nexport { Vector3d } from \"./math/vector3d.ts\";\nexport type {\n\tAdapterCapabilities,\n\tAdapterOptions,\n\tBodyDefinition,\n\tBodyShape,\n\tBodyType,\n\tCollisionResponse,\n\tPhysicsAdapter,\n\tPhysicsBody,\n\tRaycastHit,\n\tRaycastHit3d,\n\tShapeCollisionContact,\n} from \"./physics/adapter.ts\";\nexport { Bounds } from \"./physics/bounds.ts\";\nexport { AABB3d } from \"./physics/broadphase/aabb3d.ts\";\nexport { default as BuiltinAdapter } from \"./physics/builtin/builtin-adapter.ts\";\nexport { collision } from \"./physics/collision.js\";\nexport * as plugin from \"./plugin/plugin.ts\";\nexport { getPool } from \"./pool.ts\";\nexport * as device from \"./system/device.js\";\nexport * as event from \"./system/event.ts\";\nexport * as utils from \"./utils/utils.ts\";\nexport * from \"./version.ts\";\nexport * as video from \"./video/video.js\";\n// export all class definition\nexport {\n\tApplication,\n\tBatcher,\n\tBitmapText,\n\tBitmapTextData,\n\tBlurEffect,\n\tBody,\n\tCamera2d,\n\tCamera3d,\n\tCameraEffect,\n\tCanvasRenderer,\n\tCanvasRenderTarget,\n\tChromaticAberrationEffect,\n\tCollectable,\n\tColorLayer,\n\tColorMatrix,\n\tColorMatrixEffect,\n\tContainer,\n\tDesaturateEffect,\n\tDissolveEffect,\n\tDraggable,\n\tDropShadowEffect,\n\tDropTarget,\n\tEntity, // eslint-disable-line @typescript-eslint/no-deprecated\n\tFadeEffect,\n\tFlashEffect,\n\tFrameAnimation,\n\tFrustum,\n\tGLShader,\n\tGLTFModel,\n\tGlowEffect,\n\tGradient,\n\tHologramEffect,\n\tImageLayer,\n\tInstancedMesh,\n\tInvertEffect,\n\tisPortableTopology,\n\tisTopology,\n\tisVertexFormat,\n\tLight2d,\n\tMaskEffect,\n\tMesh,\n\tNineSliceSprite,\n\tNoiseTexture2d,\n\tOutlineEffect,\n\tParticle,\n\tParticleEmitter,\n\tParticleEmitterSettings,\n\tPixelateEffect,\n\tPORTABLE_TOPOLOGIES,\n\tPointer,\n\tPrimitiveBatcher,\n\tplugins,\n\tpool,\n\tQuadBatcher,\n\tRenderable,\n\tRenderer,\n\tRenderState,\n\tRenderTarget,\n\tresolveVertexFormat,\n\tScanlineEffect,\n\tSepiaEffect,\n\tShaderEffect,\n\tShakeEffect,\n\tShineEffect,\n\tSprite,\n\tSprite3d,\n\tStage,\n\tsave,\n\tstate,\n\tText,\n\tTexture2d,\n\tTextureAtlas,\n\tTile,\n\tTintPulseEffect,\n\tTMXHexagonalRenderer,\n\tTMXIsometricRenderer,\n\tTMXLayer,\n\tTMXOrthogonalRenderer,\n\tTMXRenderer,\n\tTMXStaggeredRenderer,\n\tTMXTileMap,\n\tTMXTileset,\n\tTMXTilesetGroup,\n\tTMXUtils,\n\tTrail,\n\tTrigger,\n\tTween,\n\ttimer,\n\tUIBaseElement,\n\tUISpriteElement,\n\tUITextButton,\n\tVignetteEffect,\n\tWaveEffect,\n\tWebGLBatcher,\n\tWebGLRenderer,\n\tWebGPUBatcher,\n\tWebGPUPrimitiveBatcher,\n\tWebGPUQuadBatcher,\n\tWebGPURenderer,\n\tWorld,\n};\n\n/**\n * disable melonJS auto-initialization\n * @see {@link boot}\n */\n// eslint-disable-next-line prefer-const\nexport let skipAutoInit = false;\n\n// the default application: whichever `Application` was constructed most\n// recently registers itself here, so `game` is undefined until you make one\nexport { game } from \"./application/application.ts\";\n/**\n * initialize the melonJS library.\n * this is automatically called unless skipAutoInit is set to true,\n * to allow asynchronous loaders to work.\n * @see {@link skipAutoInit}\n */\nexport { boot, initialized } from \"./system/bootstrap.ts\";\n\n// call the library init function when ready\nDOMContentLoaded(() => {\n\tif (!skipAutoInit) {\n\t\tboot();\n\t}\n});\n\nexport type { Topology } from \"./video/gpu/topology.ts\";\nexport type { VertexFormat } from \"./video/gpu/vertexformat.ts\";\n"]}