{"version":3,"file":"pixi.mjs","sources":["../src/extensions/Extensions.ts","../node_modules/eventemitter3/index.js","../node_modules/@pixi/colord/index.mjs","../node_modules/@pixi/colord/plugins/names.mjs","../src/color/Color.ts","../src/culling/cullingMixin.ts","../src/maths/misc/const.ts","../src/maths/point/Point.ts","../src/maths/matrix/Matrix.ts","../src/maths/point/ObservablePoint.ts","../src/utils/data/uid.ts","../src/utils/logging/deprecation.ts","../src/utils/logging/warn.ts","../src/utils/pool/Pool.ts","../src/utils/pool/PoolGroup.ts","../src/scene/container/container-mixins/cacheAsTextureMixin.ts","../src/utils/data/removeItems.ts","../src/scene/container/container-mixins/childrenHelperMixin.ts","../src/scene/container/container-mixins/collectRenderablesMixin.ts","../src/filters/FilterEffect.ts","../src/rendering/mask/MaskEffectManager.ts","../src/scene/container/container-mixins/effectsMixin.ts","../src/scene/container/container-mixins/findMixin.ts","../src/maths/shapes/Rectangle.ts","../src/scene/container/bounds/Bounds.ts","../src/scene/container/bounds/utils/matrixAndBoundsPool.ts","../src/scene/container/container-mixins/getFastGlobalBoundsMixin.ts","../src/scene/container/bounds/getGlobalBounds.ts","../src/scene/container/utils/multiplyHexColors.ts","../src/scene/container/utils/multiplyColors.ts","../src/scene/container/container-mixins/getGlobalMixin.ts","../src/scene/container/bounds/getLocalBounds.ts","../src/scene/container/utils/checkChildrenDidChange.ts","../src/scene/container/container-mixins/measureMixin.ts","../src/scene/container/container-mixins/onRenderMixin.ts","../src/scene/container/container-mixins/sortMixin.ts","../src/scene/container/container-mixins/toLocalGlobalMixin.ts","../src/rendering/renderers/shared/instructions/InstructionSet.ts","../src/maths/misc/pow2.ts","../src/scene/container/utils/definedProps.ts","../src/rendering/renderers/shared/texture/TextureStyle.ts","../src/rendering/renderers/shared/texture/sources/TextureSource.ts","../src/maths/matrix/groupD8.ts","../src/utils/misc/NOOP.ts","../src/rendering/renderers/shared/texture/sources/BufferImageSource.ts","../src/rendering/renderers/shared/texture/TextureMatrix.ts","../src/rendering/renderers/shared/texture/Texture.ts","../src/rendering/renderers/shared/texture/TexturePool.ts","../src/scene/container/RenderGroup.ts","../src/scene/container/utils/assignWithIgnore.ts","../src/scene/container/Container.ts","../src/ticker/const.ts","../src/ticker/TickerListener.ts","../src/ticker/Ticker.ts","../src/dom/CanvasObserver.ts","../src/events/FederatedEvent.ts","../node_modules/ismobilejs/esm/isMobile.js","../src/utils/browser/isMobile.ts","../src/accessibility/AccessibilitySystem.ts","../src/accessibility/accessibilityTarget.ts","../src/accessibility/init.ts","../src/app/ResizePlugin.ts","../src/app/TickerPlugin.ts","../src/app/init.ts","../src/events/EventTicker.ts","../src/events/FederatedMouseEvent.ts","../src/events/FederatedPointerEvent.ts","../src/events/FederatedWheelEvent.ts","../src/events/EventBoundary.ts","../src/events/EventSystem.ts","../src/events/FederatedEventTarget.ts","../src/events/init.ts","../src/dom/DOMPipe.ts","../src/scene/view/ViewContainer.ts","../src/dom/DOMContainer.ts","../src/dom/init.ts","../src/assets/loader/parsers/LoaderParser.ts","../src/environment-browser/BrowserAdapter.ts","../src/environment/adapter.ts","../src/utils/path.ts","../src/assets/utils/convertToList.ts","../src/assets/utils/createStringVariations.ts","../src/assets/utils/isSingleItem.ts","../src/assets/resolver/Resolver.ts","../src/assets/utils/copySearchParams.ts","../src/spritesheet/Spritesheet.ts","../src/spritesheet/spritesheetAsset.ts","../src/spritesheet/init.ts","../src/utils/data/updateQuadBounds.ts","../src/scene/sprite/Sprite.ts","../src/rendering/mask/utils/addMaskBounds.ts","../src/rendering/mask/utils/addMaskLocalBounds.ts","../src/rendering/mask/alpha/AlphaMask.ts","../src/rendering/mask/color/ColorMask.ts","../src/rendering/mask/stencil/StencilMask.ts","../src/rendering/renderers/shared/texture/sources/CanvasSource.ts","../src/rendering/renderers/shared/texture/sources/ImageSource.ts","../src/utils/browser/detectVideoAlphaMode.ts","../src/rendering/renderers/shared/texture/sources/VideoSource.ts","../src/assets/cache/Cache.ts","../src/rendering/renderers/shared/texture/utils/textureFrom.ts","../src/rendering/init.ts","../src/rendering/renderers/gpu/shader/BindGroup.ts","../src/rendering/batcher/gpu/getTextureBatchBindGroup.ts","../src/utils/data/ViewableBuffer.ts","../src/rendering/renderers/shared/buffer/utils/fastCopy.ts","../src/rendering/renderers/shared/state/const.ts","../src/rendering/renderers/shared/state/getAdjustedBlendModeBlend.ts","../src/rendering/renderers/gl/shader/program/getTestContext.ts","../src/rendering/batcher/gl/utils/checkMaxIfStatementsInShader.ts","../src/rendering/batcher/gl/utils/maxRecommendedTextures.ts","../src/rendering/batcher/shared/BatchTextureArray.ts","../src/rendering/batcher/shared/Batcher.ts","../src/rendering/renderers/shared/buffer/const.ts","../src/rendering/renderers/shared/buffer/Buffer.ts","../src/rendering/renderers/shared/geometry/utils/ensureIsBuffer.ts","../src/rendering/renderers/shared/geometry/utils/getGeometryBounds.ts","../src/rendering/renderers/shared/geometry/Geometry.ts","../src/rendering/batcher/shared/BatchGeometry.ts","../src/rendering/renderers/shared/utils/createIdFromString.ts","../src/rendering/renderers/gl/shader/program/getMaxFragmentPrecision.ts","../src/rendering/renderers/gl/shader/program/preprocessors/addProgramDefines.ts","../src/rendering/renderers/gl/shader/program/preprocessors/ensurePrecision.ts","../src/rendering/renderers/gl/shader/program/preprocessors/insertVersion.ts","../src/rendering/renderers/gl/shader/program/preprocessors/setProgramName.ts","../src/rendering/renderers/gl/shader/program/preprocessors/stripVersion.ts","../src/rendering/renderers/gl/shader/GlProgram.ts","../src/rendering/renderers/shared/geometry/utils/getAttributeInfoFromFormat.ts","../src/rendering/renderers/gpu/shader/utils/extractAttributesFromGpuProgram.ts","../src/rendering/renderers/gpu/shader/utils/extractStructAndGroups.ts","../src/rendering/renderers/shared/shader/const.ts","../src/rendering/renderers/gpu/shader/utils/generateGpuLayoutGroups.ts","../src/rendering/renderers/gpu/shader/utils/generateLayoutHash.ts","../src/rendering/renderers/gpu/shader/utils/removeStructAndGroupDuplicates.ts","../src/rendering/renderers/gpu/shader/GpuProgram.ts","../src/rendering/high-shader/compiler/utils/addBits.ts","../src/rendering/high-shader/compiler/utils/compileHooks.ts","../src/rendering/high-shader/compiler/utils/compileInputs.ts","../src/rendering/high-shader/compiler/utils/compileOutputs.ts","../src/rendering/high-shader/compiler/utils/injectBits.ts","../src/rendering/high-shader/compiler/compileHighShader.ts","../src/rendering/high-shader/defaultProgramTemplate.ts","../src/rendering/high-shader/shader-bits/globalUniformsBit.ts","../src/rendering/high-shader/compileHighShaderToProgram.ts","../src/rendering/high-shader/shader-bits/colorBit.ts","../src/rendering/high-shader/shader-bits/generateTextureBatchBit.ts","../src/rendering/high-shader/shader-bits/roundPixelsBit.ts","../src/rendering/renderers/shared/shader/types.ts","../src/rendering/renderers/shared/shader/utils/getDefaultUniformValue.ts","../src/rendering/renderers/shared/shader/UniformGroup.ts","../src/rendering/renderers/gl/shader/getBatchSamplersUniformGroup.ts","../src/rendering/renderers/types.ts","../src/rendering/renderers/shared/shader/Shader.ts","../src/rendering/batcher/shared/DefaultShader.ts","../src/rendering/batcher/shared/DefaultBatcher.ts","../src/rendering/renderers/shared/geometry/utils/buildUvs.ts","../src/rendering/renderers/shared/geometry/utils/transformVertices.ts","../src/scene/graphics/shared/BatchableGraphics.ts","../src/scene/graphics/shared/buildCommands/buildCircle.ts","../src/scene/graphics/shared/const.ts","../src/scene/graphics/shared/utils/getOrientationOfPoints.ts","../src/scene/graphics/shared/buildCommands/buildLine.ts","../src/scene/graphics/shared/buildCommands/buildPixelLine.ts","../node_modules/earcut/src/earcut.js","../src/utils/utils.ts","../src/scene/graphics/shared/utils/triangulateWithHoles.ts","../src/scene/graphics/shared/buildCommands/buildPolygon.ts","../src/scene/graphics/shared/buildCommands/buildRectangle.ts","../src/scene/graphics/shared/buildCommands/buildTriangle.ts","../src/scene/graphics/shared/fill/FillGradient.ts","../src/scene/graphics/shared/utils/generateTextureFillMatrix.ts","../src/scene/graphics/shared/utils/buildContextBatches.ts","../src/scene/graphics/shared/GraphicsContextSystem.ts","../src/rendering/renderers/shared/state/State.ts","../src/scene/graphics/gpu/colorToUniform.ts","../src/scene/graphics/shared/GraphicsPipe.ts","../src/scene/graphics/init.ts","../src/scene/mesh/shared/BatchableMesh.ts","../src/scene/mesh/shared/MeshPipe.ts","../src/scene/mesh/init.ts","../src/scene/particle-container/gl/GlParticleContainerAdaptor.ts","../src/scene/particle-container/shared/utils/createIndicesForQuads.ts","../src/scene/particle-container/shared/utils/generateParticleUpdateFunction.ts","../src/scene/particle-container/shared/ParticleBuffer.ts","../src/scene/particle-container/shared/shader/ParticleShader.ts","../src/scene/particle-container/shared/ParticleContainerPipe.ts","../src/scene/particle-container/shared/GlParticleContainerPipe.ts","../src/scene/particle-container/gpu/GpuParticleContainerAdaptor.ts","../src/scene/particle-container/shared/GpuParticleContainerPipe.ts","../src/scene/particle-container/init.ts","../src/scene/text/utils/updateTextBounds.ts","../src/scene/sprite/BatchableSprite.ts","../src/scene/text/canvas/BatchableText.ts","../src/scene/text/canvas/CanvasTextPipe.ts","../src/scene/graphics/shared/fill/FillPattern.ts","../node_modules/parse-svg-path/index.js","../src/scene/graphics/shared/svg/parseSVGPath.ts","../src/maths/shapes/Circle.ts","../src/maths/shapes/Ellipse.ts","../src/maths/misc/squaredDistanceToLineSegment.ts","../src/maths/shapes/Polygon.ts","../src/maths/shapes/RoundedRectangle.ts","../src/scene/graphics/shared/buildCommands/buildAdaptiveBezier.ts","../src/scene/graphics/shared/buildCommands/buildAdaptiveQuadratic.ts","../src/scene/graphics/shared/buildCommands/buildArc.ts","../src/scene/graphics/shared/buildCommands/buildArcTo.ts","../src/scene/graphics/shared/buildCommands/buildArcToSvg.ts","../src/scene/graphics/shared/path/roundShape.ts","../src/scene/graphics/shared/path/ShapePath.ts","../src/scene/graphics/shared/path/GraphicsPath.ts","../src/scene/graphics/shared/svg/parseSVGFloatAttribute.ts","../src/scene/graphics/shared/svg/parseSVGDefinitions.ts","../src/scene/graphics/shared/svg/utils/extractSvgUrlId.ts","../src/scene/graphics/shared/svg/parseSVGStyle.ts","../src/scene/graphics/shared/svg/SVGParser.ts","../src/scene/graphics/shared/utils/convertFillInputToFillStyle.ts","../src/scene/graphics/shared/GraphicsContext.ts","../src/scene/text/utils/generateTextStyleKey.ts","../src/scene/text/TextStyle.ts","../src/scene/text/utils/getPo2TextureFromSource.ts","../src/rendering/renderers/shared/texture/CanvasPool.ts","../src/utils/canvas/getCanvasBoundingBox.ts","../src/scene/text/canvas/utils/fontStringFromTextStyle.ts","../src/scene/text/canvas/CanvasTextMetrics.ts","../src/scene/text/canvas/utils/getCanvasFillStyle.ts","../src/scene/text/canvas/CanvasTextGenerator.ts","../src/scene/text/canvas/CanvasTextSystem.ts","../src/scene/text/init.ts","../src/scene/graphics/shared/Graphics.ts","../src/scene/text/sdfShader/shader-bits/localUniformMSDFBit.ts","../src/scene/text/sdfShader/shader-bits/mSDFBit.ts","../src/scene/text/sdfShader/SdfShader.ts","../src/scene/text-bitmap/AbstractBitmapFont.ts","../src/scene/text-bitmap/DynamicBitmapFont.ts","../src/scene/text-bitmap/utils/getBitmapTextLayout.ts","../src/scene/text-bitmap/utils/resolveCharacters.ts","../src/scene/text-bitmap/BitmapFontManager.ts","../src/scene/text-bitmap/BitmapTextPipe.ts","../src/scene/text-bitmap/init.ts","../src/scene/text-html/BatchableHTMLText.ts","../src/scene/text-html/HTMLTextPipe.ts","../src/utils/browser/isSafari.ts","../src/scene/text-html/HTMLTextRenderData.ts","../src/scene/text-html/utils/extractFontFamilies.ts","../src/scene/text-html/utils/loadFontAsBase64.ts","../src/scene/text-html/utils/loadFontCSS.ts","../src/scene/text-html/utils/getFontCss.ts","../src/scene/text-html/utils/getSVGUrl.ts","../src/scene/text-html/utils/getTemporaryCanvasFromImage.ts","../src/scene/text-html/utils/loadSVGImage.ts","../src/scene/text-html/utils/measureHtmlText.ts","../src/scene/text-html/HTMLTextSystem.ts","../src/scene/text-html/init.ts","../src/scene/mesh/shared/MeshGeometry.ts","../src/rendering/high-shader/shader-bits/localUniformBit.ts","../src/scene/sprite-tiling/shader/tilingBit.ts","../src/scene/sprite-tiling/shader/TilingSpriteShader.ts","../src/scene/sprite-tiling/utils/QuadGeometry.ts","../src/scene/sprite-tiling/utils/setPositions.ts","../src/scene/sprite-tiling/utils/applyMatrix.ts","../src/scene/sprite-tiling/utils/setUvs.ts","../src/scene/sprite-tiling/TilingSpritePipe.ts","../src/scene/sprite-tiling/init.ts","../src/scene/mesh-plane/PlaneGeometry.ts","../src/scene/sprite-nine-slice/NineSliceGeometry.ts","../src/scene/sprite-nine-slice/NineSliceSpritePipe.ts","../src/scene/sprite-nine-slice/init.ts","../src/filters/FilterPipe.ts","../src/scene/container/bounds/getRenderableBounds.ts","../src/filters/FilterSystem.ts","../src/filters/init.ts","../src/environment/autoDetectEnvironment.ts","../src/utils/browser/unsafeEvalSupported.ts","../src/rendering/renderers/gl/const.ts","../src/rendering/renderers/shared/system/SystemRunner.ts","../src/rendering/renderers/shared/system/AbstractRenderer.ts","../src/utils/browser/isWebGLSupported.ts","../src/utils/browser/isWebGPUSupported.ts","../src/rendering/renderers/autoDetectRenderer.ts","../src/utils/const.ts","../src/utils/global/globalHooks.ts","../src/app/Application.ts","../src/scene/text-bitmap/BitmapFont.ts","../src/scene/text-bitmap/asset/bitmapFontTextParser.ts","../src/scene/text-bitmap/asset/bitmapFontXMLParser.ts","../src/scene/text-bitmap/asset/bitmapFontXMLStringParser.ts","../src/scene/text-bitmap/asset/loadBitmapFont.ts","../src/assets/BackgroundLoader.ts","../src/assets/cache/parsers/cacheTextureArray.ts","../src/assets/detections/utils/testImageFormat.ts","../src/assets/detections/parsers/detectAvif.ts","../src/assets/detections/parsers/detectDefaults.ts","../src/assets/detections/utils/testVideoFormat.ts","../src/assets/detections/parsers/detectMp4.ts","../src/assets/detections/parsers/detectOgv.ts","../src/assets/detections/parsers/detectWebm.ts","../src/assets/detections/parsers/detectWebp.ts","../src/assets/loader/Loader.ts","../src/assets/utils/checkDataUrl.ts","../src/assets/utils/checkExtension.ts","../src/assets/loader/parsers/loadJson.ts","../src/assets/loader/parsers/loadTxt.ts","../src/assets/loader/parsers/loadWebFont.ts","../src/utils/network/getResolutionOfUrl.ts","../src/assets/loader/parsers/textures/utils/createTexture.ts","../src/assets/loader/parsers/textures/loadSVG.ts","../src/assets/loader/workers/WorkerManager.ts","../src/assets/loader/parsers/textures/loadTextures.ts","../src/assets/loader/parsers/textures/loadVideoTextures.ts","../src/assets/resolver/parsers/resolveTextureUrl.ts","../src/assets/resolver/parsers/resolveJsonUrl.ts","../src/assets/Assets.ts","../src/compressed-textures/basis/detectBasis.ts","../src/rendering/renderers/shared/texture/sources/CompressedSource.ts","../src/rendering/renderers/gl/texture/utils/getSupportedGlCompressedTextureFormats.ts","../src/rendering/renderers/gpu/texture/utils/getSupportedGPUCompressedTextureFormats.ts","../src/rendering/renderers/shared/texture/utils/getSupportedCompressedTextureFormats.ts","../src/rendering/renderers/shared/texture/utils/getSupportedTextureFormats.ts","../src/compressed-textures/basis/utils/setBasisTranscoderPath.ts","../src/compressed-textures/basis/worker/loadBasisOnWorker.ts","../src/compressed-textures/basis/loadBasis.ts","../src/compressed-textures/basis/utils/createLevelBuffers.ts","../src/compressed-textures/basis/utils/gpuFormatToBasisTranscoderFormat.ts","../src/compressed-textures/dds/const.ts","../src/compressed-textures/dds/parseDDS.ts","../src/compressed-textures/dds/loadDDS.ts","../src/compressed-textures/ktx2/const.ts","../src/compressed-textures/ktx/parseKTX.ts","../src/compressed-textures/ktx/loadKTX.ts","../src/compressed-textures/ktx2/utils/setKTXTranscoderPath.ts","../src/compressed-textures/ktx2/worker/loadKTX2onWorker.ts","../src/compressed-textures/ktx2/loadKTX2.ts","../src/compressed-textures/ktx2/utils/convertFormatIfRequired.ts","../src/compressed-textures/ktx2/utils/createLevelBuffersFromKTX.ts","../src/compressed-textures/ktx2/utils/glFormatToGPUFormat.ts","../src/compressed-textures/ktx2/utils/vkFormatToGPUFormat.ts","../src/compressed-textures/ktx2/utils/getTextureFormatFromKTXTexture.ts","../src/compressed-textures/ktx2/utils/gpuFormatToKTXBasisTranscoderFormat.ts","../src/compressed-textures/shared/resolveCompressedTextureUrl.ts","../src/compressed-textures/shared/detectCompressed.ts","../src/culling/Culler.ts","../src/culling/CullerPlugin.ts","../src/environment-browser/browserExt.ts","../src/filters/Filter.ts","../src/filters/blend-modes/BlendModeFilter.ts","../src/filters/blend-modes/hls/GLhls.ts","../src/filters/blend-modes/hls/GPUhls.ts","../src/filters/defaults/alpha/AlphaFilter.ts","../src/filters/defaults/blur/const.ts","../src/filters/defaults/blur/gl/generateBlurFragSource.ts","../src/filters/defaults/blur/gl/generateBlurVertSource.ts","../src/filters/defaults/blur/gl/generateBlurGlProgram.ts","../src/filters/defaults/blur/gpu/generateBlurProgram.ts","../src/filters/defaults/blur/BlurFilterPass.ts","../src/filters/defaults/blur/BlurFilter.ts","../src/filters/defaults/color-matrix/ColorMatrixFilter.ts","../src/filters/defaults/displacement/DisplacementFilter.ts","../src/filters/defaults/noise/NoiseFilter.ts","../src/filters/mask/MaskFilter.ts","../src/maths/point/pointInTriangle.ts","../src/maths/shapes/Triangle.ts","../src/prepare/PrepareBase.ts","../src/scene/mesh/shared/Mesh.ts","../src/scene/sprite-animated/AnimatedSprite.ts","../src/utils/misc/Transform.ts","../src/scene/sprite-tiling/TilingSprite.ts","../src/scene/text/AbstractText.ts","../src/scene/text/Text.ts","../src/prepare/PrepareQueue.ts","../src/scene/text-bitmap/BitmapText.ts","../src/scene/text-html/utils/textStyleToCSS.ts","../src/scene/text-html/HTMLTextStyle.ts","../src/scene/text-html/HTMLText.ts","../src/prepare/PrepareUpload.ts","../src/prepare/PrepareSystem.ts","../src/rendering/batcher/gl/GlBatchAdaptor.ts","../src/rendering/batcher/gpu/generateGPULayout.ts","../src/rendering/batcher/gpu/generateLayout.ts","../src/rendering/batcher/gpu/GpuBatchAdaptor.ts","../src/rendering/batcher/shared/BatcherPipe.ts","../src/rendering/high-shader/compiler/utils/formatShader.ts","../src/rendering/high-shader/shader-bits/textureBit.ts","../src/rendering/mask/alpha/AlphaMaskPipe.ts","../src/rendering/mask/color/ColorMaskPipe.ts","../src/rendering/mask/scissor/ScissorMask.ts","../src/rendering/mask/stencil/StencilMaskPipe.ts","../src/rendering/renderers/gl/buffer/const.ts","../src/rendering/renderers/gl/buffer/GlBuffer.ts","../src/rendering/renderers/gl/buffer/GlBufferSystem.ts","../src/rendering/renderers/gl/context/GlContextSystem.ts","../src/rendering/renderers/gl/shader/program/ensureAttributes.ts","../src/rendering/renderers/gl/texture/const.ts","../src/rendering/renderers/gl/geometry/utils/getGlTypeFromFormat.ts","../src/rendering/renderers/gl/geometry/GlGeometrySystem.ts","../src/rendering/renderers/gl/GlBackBufferSystem.ts","../src/rendering/renderers/gl/GlColorMaskSystem.ts","../src/rendering/renderers/gl/GlEncoderSystem.ts","../src/rendering/renderers/gl/GlLimitsSystem.ts","../src/rendering/renderers/gl/GlRenderTarget.ts","../src/rendering/renderers/gpu/state/GpuStencilModesToPixi.ts","../src/rendering/renderers/gl/GlStencilSystem.ts","../src/rendering/renderers/shared/shader/UboSystem.ts","../src/rendering/renderers/gl/shader/utils/createUboElementsSTD40.ts","../src/rendering/renderers/shared/shader/utils/uniformParsers.ts","../src/rendering/renderers/shared/shader/utils/createUboSyncFunction.ts","../src/rendering/renderers/shared/shader/utils/uboSyncFunctions.ts","../src/rendering/renderers/gl/shader/utils/generateArraySyncSTD40.ts","../src/rendering/renderers/gl/shader/utils/createUboSyncSTD40.ts","../src/rendering/renderers/gl/GlUboSystem.ts","../src/rendering/renderers/gl/renderTarget/GlRenderTargetAdaptor.ts","../src/rendering/renderers/gpu/renderTarget/calculateProjection.ts","../src/rendering/renderers/shared/texture/utils/getCanvasTexture.ts","../src/rendering/renderers/shared/renderTarget/isRenderingToScreen.ts","../src/rendering/renderers/shared/renderTarget/RenderTarget.ts","../src/rendering/renderers/shared/renderTarget/RenderTargetSystem.ts","../src/rendering/renderers/gl/renderTarget/GlRenderTargetSystem.ts","../src/rendering/renderers/shared/buffer/BufferResource.ts","../src/rendering/renderers/gl/shader/GenerateShaderSyncCode.ts","../src/rendering/renderers/gl/shader/GlProgramData.ts","../src/rendering/renderers/gl/shader/program/compileShader.ts","../src/rendering/renderers/gl/shader/program/defaultValue.ts","../src/rendering/renderers/gl/shader/program/mapType.ts","../src/rendering/renderers/gl/shader/program/extractAttributesFromGlProgram.ts","../src/rendering/renderers/gl/shader/program/getUboData.ts","../src/rendering/renderers/gl/shader/program/getUniformData.ts","../src/rendering/renderers/gl/shader/program/logProgramError.ts","../src/rendering/renderers/gl/shader/program/generateProgram.ts","../src/rendering/renderers/gl/shader/GlShaderSystem.ts","../src/rendering/renderers/gl/shader/utils/generateUniformsSyncTypes.ts","../src/rendering/renderers/gl/shader/utils/generateUniformsSync.ts","../src/rendering/renderers/gl/shader/GlUniformGroupSystem.ts","../src/rendering/renderers/gl/shader/migrateFragmentFromV7toV8.ts","../src/rendering/renderers/gl/shader/program/mapSize.ts","../src/rendering/renderers/gl/state/mapWebGLBlendModesToPixi.ts","../src/rendering/renderers/gl/state/GlStateSystem.ts","../src/rendering/renderers/gl/texture/GlTexture.ts","../src/rendering/renderers/gl/texture/uploaders/glUploadBufferImageResource.ts","../src/rendering/renderers/gl/texture/uploaders/glUploadCompressedTextureResource.ts","../src/rendering/renderers/gl/texture/uploaders/glUploadImageResource.ts","../src/rendering/renderers/gl/texture/uploaders/glUploadVideoResource.ts","../src/rendering/renderers/gl/texture/utils/pixiToGlMaps.ts","../src/rendering/renderers/gl/texture/utils/applyStyleParams.ts","../src/rendering/renderers/gl/texture/utils/mapFormatToGlFormat.ts","../src/rendering/renderers/gl/texture/utils/mapFormatToGlInternalFormat.ts","../src/rendering/renderers/gl/texture/utils/mapFormatToGlType.ts","../src/rendering/renderers/gl/texture/utils/unpremultiplyAlpha.ts","../src/rendering/renderers/gl/texture/GlTextureSystem.ts","../src/scene/graphics/gl/GlGraphicsAdaptor.ts","../src/scene/mesh/gl/GlMeshAdaptor.ts","../src/scene/container/CustomRenderPipe.ts","../src/scene/container/utils/executeInstructions.ts","../src/scene/container/RenderGroupPipe.ts","../src/scene/container/utils/clearList.ts","../src/scene/container/utils/updateRenderGroupTransforms.ts","../src/scene/container/utils/validateRenderables.ts","../src/scene/container/RenderGroupSystem.ts","../src/scene/sprite/SpritePipe.ts","../src/rendering/renderers/shared/background/BackgroundSystem.ts","../src/rendering/renderers/shared/blendModes/BlendModePipe.ts","../src/rendering/renderers/shared/extract/ExtractSystem.ts","../src/rendering/renderers/shared/texture/RenderTexture.ts","../src/rendering/renderers/shared/extract/GenerateTextureSystem.ts","../src/rendering/renderers/shared/renderTarget/GlobalUniformSystem.ts","../src/rendering/renderers/shared/SchedulerSystem.ts","../src/utils/sayHello.ts","../src/rendering/renderers/shared/startup/HelloSystem.ts","../src/utils/data/clean.ts","../src/rendering/renderers/shared/texture/RenderableGCSystem.ts","../src/rendering/renderers/shared/texture/TextureGCSystem.ts","../src/rendering/renderers/shared/view/ViewSystem.ts","../src/rendering/renderers/shared/system/SharedSystems.ts","../src/rendering/renderers/gl/WebGLRenderer.ts","../src/rendering/renderers/gpu/BindGroupSystem.ts","../src/rendering/renderers/gpu/buffer/GpuBufferSystem.ts","../src/rendering/renderers/gpu/buffer/UboBatch.ts","../src/rendering/renderers/gpu/GpuColorMaskSystem.ts","../src/rendering/renderers/gpu/GpuDeviceSystem.ts","../src/rendering/renderers/gpu/GpuEncoderSystem.ts","../src/rendering/renderers/gpu/GpuLimitsSystem.ts","../src/rendering/renderers/gpu/GpuStencilSystem.ts","../src/rendering/renderers/gpu/shader/utils/createUboElementsWGSL.ts","../src/rendering/renderers/gpu/shader/utils/generateArraySyncWGSL.ts","../src/rendering/renderers/gpu/shader/utils/createUboSyncFunctionWGSL.ts","../src/rendering/renderers/gpu/GpuUboSystem.ts","../src/rendering/renderers/gpu/GpuUniformBatchPipe.ts","../src/rendering/renderers/gpu/pipeline/PipelineSystem.ts","../src/rendering/renderers/gpu/renderTarget/GpuRenderTarget.ts","../src/rendering/renderers/gpu/renderTarget/GpuRenderTargetAdaptor.ts","../src/rendering/renderers/gpu/renderTarget/GpuRenderTargetSystem.ts","../src/rendering/renderers/gpu/shader/GpuShaderSystem.ts","../src/rendering/renderers/gpu/state/GpuBlendModesToPixi.ts","../src/rendering/renderers/gpu/state/GpuStateSystem.ts","../src/rendering/renderers/gpu/texture/uploaders/gpuUploadBufferImageResource.ts","../src/rendering/renderers/gpu/texture/uploaders/gpuUploadCompressedTextureResource.ts","../src/rendering/renderers/gpu/texture/uploaders/gpuUploadImageSource.ts","../src/rendering/renderers/gpu/texture/uploaders/gpuUploadVideoSource.ts","../src/rendering/renderers/gpu/texture/utils/GpuMipmapGenerator.ts","../src/rendering/renderers/gpu/texture/GpuTextureSystem.ts","../src/scene/graphics/gpu/GpuGraphicsAdaptor.ts","../src/scene/mesh/gpu/GpuMeshAdapter.ts","../src/rendering/renderers/gpu/WebGPURenderer.ts","../src/rendering/renderers/shared/geometry/const.ts","../src/rendering/renderers/shared/texture/const.ts","../src/rendering/renderers/shared/texture/TextureUvs.ts","../src/rendering/renderers/shared/utils/parseFunctionBody.ts","../src/scene/container/bounds/getFastGlobalBounds.ts","../src/scene/container/RenderContainer.ts","../src/scene/container/utils/collectAllRenderables.ts","../src/scene/container/utils/updateLocalTransform.ts","../src/scene/container/utils/updateWorldTransform.ts","../src/scene/layers/RenderLayer.ts","../src/scene/mesh-perspective/utils/applyProjectiveTransformationToPlane.ts","../src/scene/mesh-perspective/utils/compute2DProjections.ts","../src/scene/mesh-perspective/PerspectivePlaneGeometry.ts","../src/scene/mesh-perspective/PerspectiveMesh.ts","../src/scene/mesh-plane/MeshPlane.ts","../src/scene/mesh-simple/RopeGeometry.ts","../src/scene/mesh-simple/MeshRope.ts","../src/scene/mesh-simple/MeshSimple.ts","../src/scene/mesh/shared/getTextureDefaultMatrix.ts","../src/scene/particle-container/shared/Particle.ts","../src/scene/particle-container/shared/particleData.ts","../src/scene/particle-container/shared/ParticleContainer.ts","../src/scene/sprite-nine-slice/NineSliceSprite.ts","../src/scene/text-bitmap/utils/bitmapTextSplit.ts","../src/scene/text-split/AbstractSplitText.ts","../src/scene/text-split/SplitBitmapText.ts","../src/scene/text/utils/canvasTextSplit.ts","../src/scene/text-split/SplitText.ts","../src/utils/logging/logDebugTexture.ts","../src/utils/logging/logScene.ts"],"sourcesContent":["/**\n * Collection of valid extension types.\n * @category extensions\n * @advanced\n */\nenum ExtensionType\n{\n /** extensions that are registered as Application plugins */\n Application = 'application',\n\n /** extensions that are registered as WebGL render pipes */\n WebGLPipes = 'webgl-pipes',\n /** extensions that are registered as WebGL render pipes adaptors */\n WebGLPipesAdaptor = 'webgl-pipes-adaptor',\n /** extensions that are registered as WebGL render systems */\n WebGLSystem = 'webgl-system',\n\n /** extensions that are registered as WebGPU render pipes */\n WebGPUPipes = 'webgpu-pipes',\n /** extensions that are registered as WebGPU render pipes adaptors */\n WebGPUPipesAdaptor = 'webgpu-pipes-adaptor',\n /** extensions that are registered as WebGPU render systems */\n WebGPUSystem = 'webgpu-system',\n\n /** extensions that are registered as Canvas render pipes */\n CanvasSystem = 'canvas-system',\n /** extensions that are registered as Canvas render pipes adaptors */\n CanvasPipesAdaptor = 'canvas-pipes-adaptor',\n /** extensions that are registered as Canvas render systems */\n CanvasPipes = 'canvas-pipes',\n\n /** extensions that combine the other Asset extensions */\n Asset = 'asset',\n /** extensions that are used to load assets through Assets */\n LoadParser = 'load-parser',\n /** extensions that are used to resolve asset urls through Assets */\n ResolveParser = 'resolve-parser',\n /** extensions that are used to handle how urls are cached by Assets */\n CacheParser = 'cache-parser',\n /** extensions that are used to add/remove available resources from Assets */\n DetectionParser = 'detection-parser',\n\n /** extensions that are registered with the MaskEffectManager */\n MaskEffect = 'mask-effect',\n\n /** A type of extension for creating a new advanced blend mode */\n BlendMode = 'blend-mode',\n\n /** A type of extension that will be used to auto detect a resource type */\n TextureSource = 'texture-source',\n\n /** A type of extension that will be used to auto detect an environment */\n Environment = 'environment',\n\n /** A type of extension for building and triangulating custom shapes used in graphics. */\n ShapeBuilder = 'shape-builder',\n\n /** A type of extension for creating custom batchers used in rendering. */\n Batcher = 'batcher',\n}\n\n/**\n * The metadata for an extension.\n * @category extensions\n * @ignore\n */\ninterface ExtensionMetadataDetails\n{\n /** The extension type, can be multiple types */\n type: ExtensionType | ExtensionType[];\n /** Optional. Some plugins provide an API name/property, to make them more easily accessible */\n name?: string;\n /** Optional, used for sorting the plugins in a particular order */\n priority?: number;\n}\n\n/**\n * The metadata for an extension.\n * @category extensions\n * @advanced\n */\ntype ExtensionMetadata = ExtensionType | ExtensionMetadataDetails;\n\n/**\n * Format when registering an extension. Generally, the extension\n * should have these values as `extension` static property,\n * but you can override name or type by providing an object.\n * @category extensions\n * @advanced\n */\ninterface ExtensionFormat\n{\n /** The extension type, can be multiple types */\n type: ExtensionType | ExtensionType[];\n /** Optional. Some plugins provide an API name/property, such as Renderer plugins */\n name?: string;\n /** Optional, used for sorting the plugins in a particular order */\n priority?: number;\n /** Reference to the plugin object/class */\n ref: any;\n}\n\n/**\n * Extension format that is used internally for registrations.\n * @category extensions\n * @ignore\n */\ninterface StrictExtensionFormat extends ExtensionFormat\n{\n /** The extension type, always expressed as multiple, even if a single */\n type: ExtensionType[];\n}\n\n/**\n * The function that is called when an extension is added or removed.\n * @category extensions\n * @ignore\n */\ntype ExtensionHandler = (extension: StrictExtensionFormat) => void;\n\n/**\n * Convert input into extension format data.\n * @ignore\n */\nconst normalizeExtension = (ext: ExtensionFormat | any): StrictExtensionFormat =>\n{\n // Class/Object submission, use extension object\n if (typeof ext === 'function' || (typeof ext === 'object' && ext.extension))\n {\n // #if _DEBUG\n if (!ext.extension)\n {\n throw new Error('Extension class must have an extension object');\n }\n // #endif\n const metadata: ExtensionMetadataDetails = (typeof ext.extension !== 'object')\n ? { type: ext.extension }\n : ext.extension;\n\n ext = { ...metadata, ref: ext };\n }\n if (typeof ext === 'object')\n {\n ext = { ...ext };\n }\n else\n {\n throw new Error('Invalid extension type');\n }\n\n if (typeof ext.type === 'string')\n {\n ext.type = [ext.type];\n }\n\n return ext;\n};\n\n/**\n * Get the priority for an extension.\n * @ignore\n * @param ext - Any extension\n * @param defaultPriority - Fallback priority if none is defined.\n * @returns The priority for the extension.\n * @category extensions\n */\nexport const normalizeExtensionPriority = (ext: ExtensionFormat | any, defaultPriority: number): number =>\n normalizeExtension(ext).priority ?? defaultPriority;\n\n/**\n * Global registration system for all PixiJS extensions. Provides a centralized way to add, remove,\n * and manage functionality across the engine.\n *\n * Features:\n * - Register custom extensions and plugins\n * - Handle multiple extension types\n * - Priority-based ordering\n * @example\n * ```ts\n * import { extensions, ExtensionType } from 'pixi.js';\n *\n * // Register a simple object extension\n * extensions.add({\n * extension: {\n * type: ExtensionType.LoadParser,\n * name: 'my-loader',\n * priority: 100, // Optional priority for ordering\n * },\n * // add load parser functions\n * });\n *\n * // Register a class-based extension\n * class MyRendererPlugin {\n * static extension = {\n * type: [ExtensionType.WebGLSystem, ExtensionType.WebGPUSystem],\n * name: 'myRendererPlugin'\n * };\n *\n * // add renderer plugin methods\n * }\n * extensions.add(MyRendererPlugin);\n *\n * // Remove extensions\n * extensions.remove(MyRendererPlugin);\n * ```\n * @remarks\n * - Extensions must have a type from {@link ExtensionType}\n * - Can be registered before or after their handlers\n * - Supports priority-based ordering\n * - Automatically normalizes extension formats\n * @see {@link ExtensionType} For all available extension types\n * @see {@link ExtensionFormat} For extension registration format\n * @see {@link Application} For application plugin system\n * @see {@link LoaderParser} For asset loading extensions\n * @category extensions\n * @standard\n * @class\n */\nconst extensions = {\n\n /** @ignore */\n _addHandlers: {} as Partial>,\n\n /** @ignore */\n _removeHandlers: {} as Partial>,\n\n /** @ignore */\n _queue: {} as Partial>,\n\n /**\n * Remove extensions from PixiJS.\n * @param extensions - Extensions to be removed. Can be:\n * - Extension class with static `extension` property\n * - Extension format object with `type` and `ref`\n * - Multiple extensions as separate arguments\n * @returns {extensions} this for chaining\n * @example\n * ```ts\n * // Remove a single extension\n * extensions.remove(MyRendererPlugin);\n *\n * // Remove multiple extensions\n * extensions.remove(\n * MyRendererPlugin,\n * MySystemPlugin\n * );\n * ```\n * @see {@link ExtensionType} For available extension types\n * @see {@link ExtensionFormat} For extension format details\n */\n remove(...extensions: Array)\n {\n extensions.map(normalizeExtension).forEach((ext) =>\n {\n ext.type.forEach((type) => this._removeHandlers[type]?.(ext));\n });\n\n return this;\n },\n\n /**\n * Register new extensions with PixiJS. Extensions can be registered in multiple formats:\n * - As a class with a static `extension` property\n * - As an extension format object\n * - As multiple extensions passed as separate arguments\n * @param extensions - Extensions to add to PixiJS. Each can be:\n * - A class with static `extension` property\n * - An extension format object with `type` and `ref`\n * - Multiple extensions as separate arguments\n * @returns This extensions instance for chaining\n * @example\n * ```ts\n * // Register a simple extension\n * extensions.add(MyRendererPlugin);\n *\n * // Register multiple extensions\n * extensions.add(\n * MyRendererPlugin,\n * MySystemPlugin,\n * });\n * ```\n * @see {@link ExtensionType} For available extension types\n * @see {@link ExtensionFormat} For extension format details\n * @see {@link extensions.remove} For removing registered extensions\n */\n add(...extensions: Array)\n {\n // Handle any extensions either passed as class w/ data or as data\n extensions.map(normalizeExtension).forEach((ext) =>\n {\n ext.type.forEach((type) =>\n {\n const handlers = this._addHandlers;\n const queue = this._queue;\n\n if (!handlers[type])\n {\n queue[type] = queue[type] || [];\n queue[type]?.push(ext);\n }\n else\n {\n handlers[type]?.(ext);\n }\n });\n });\n\n return this;\n },\n\n /**\n * Internal method to handle extensions by name.\n * @param type - The extension type.\n * @param onAdd - Function handler when extensions are added/registered {@link StrictExtensionFormat}.\n * @param onRemove - Function handler when extensions are removed/unregistered {@link StrictExtensionFormat}.\n * @returns this for chaining.\n * @internal\n * @ignore\n */\n handle(type: ExtensionType, onAdd: ExtensionHandler, onRemove: ExtensionHandler)\n {\n const addHandlers = this._addHandlers;\n const removeHandlers = this._removeHandlers;\n\n // #if _DEBUG\n if (addHandlers[type] || removeHandlers[type])\n {\n throw new Error(`Extension type ${type} already has a handler`);\n }\n // #endif\n\n addHandlers[type] = onAdd;\n removeHandlers[type] = onRemove;\n\n // Process the queue\n const queue = this._queue;\n\n // Process any plugins that have been registered before the handler\n if (queue[type])\n {\n queue[type]?.forEach((ext) => onAdd(ext));\n delete queue[type];\n }\n\n return this;\n },\n\n /**\n * Handle a type, but using a map by `name` property.\n * @param type - Type of extension to handle.\n * @param map - The object map of named extensions.\n * @returns this for chaining.\n * @ignore\n */\n handleByMap(type: ExtensionType, map: Record)\n {\n return this.handle(type,\n (extension) =>\n {\n if (extension.name)\n {\n map[extension.name] = extension.ref;\n }\n },\n (extension) =>\n {\n if (extension.name)\n {\n delete map[extension.name];\n }\n }\n );\n },\n\n /**\n * Handle a type, but using a list of extensions with a `name` property.\n * @param type - Type of extension to handle.\n * @param map - The array of named extensions.\n * @param defaultPriority - Fallback priority if none is defined.\n * @returns this for chaining.\n * @ignore\n */\n handleByNamedList(type: ExtensionType, map: {name: string, value: any}[], defaultPriority = -1)\n {\n return this.handle(\n type,\n (extension) =>\n {\n const index = map.findIndex((item) => item.name === extension.name);\n\n if (index >= 0) return;\n\n map.push({ name: extension.name, value: extension.ref });\n map.sort((a, b) =>\n normalizeExtensionPriority(b.value, defaultPriority)\n - normalizeExtensionPriority(a.value, defaultPriority));\n },\n (extension) =>\n {\n const index = map.findIndex((item) => item.name === extension.name);\n\n if (index !== -1)\n {\n map.splice(index, 1);\n }\n }\n );\n },\n\n /**\n * Handle a type, but using a list of extensions.\n * @param type - Type of extension to handle.\n * @param list - The list of extensions.\n * @param defaultPriority - The default priority to use if none is specified.\n * @returns this for chaining.\n * @ignore\n */\n handleByList(type: ExtensionType, list: any[], defaultPriority = -1)\n {\n return this.handle(\n type,\n (extension) =>\n {\n if (list.includes(extension.ref))\n {\n return;\n }\n\n list.push(extension.ref);\n list.sort((a, b) =>\n normalizeExtensionPriority(b, defaultPriority) - normalizeExtensionPriority(a, defaultPriority));\n },\n (extension) =>\n {\n const index = list.indexOf(extension.ref);\n\n if (index !== -1)\n {\n list.splice(index, 1);\n }\n }\n );\n },\n\n /**\n * Mixin the source object(s) properties into the target class's prototype.\n * Copies all property descriptors from source objects to the target's prototype.\n * @param Target - The target class to mix properties into\n * @param sources - One or more source objects containing properties to mix in\n * @example\n * ```ts\n * // Create a mixin with shared properties\n * const moveable = {\n * x: 0,\n * y: 0,\n * move(x: number, y: number) {\n * this.x += x;\n * this.y += y;\n * }\n * };\n *\n * // Create a mixin with computed properties\n * const scalable = {\n * scale: 1,\n * get scaled() {\n * return this.scale > 1;\n * }\n * };\n *\n * // Apply mixins to a class\n * extensions.mixin(Sprite, moveable, scalable);\n *\n * // Use mixed-in properties\n * const sprite = new Sprite();\n * sprite.move(10, 20);\n * console.log(sprite.x, sprite.y); // 10, 20\n * ```\n * @remarks\n * - Copies all properties including getters/setters\n * - Does not modify source objects\n * - Preserves property descriptors\n * @see {@link Object.defineProperties} For details on property descriptors\n * @see {@link Object.getOwnPropertyDescriptors} For details on property copying\n */\n mixin(Target: any, ...sources: Parameters[0][])\n {\n // Apply each source's properties to the target prototype\n for (const source of sources)\n {\n Object.defineProperties(Target.prototype, Object.getOwnPropertyDescriptors(source));\n }\n }\n};\n\nexport {\n extensions,\n ExtensionType,\n};\nexport type {\n StrictExtensionFormat as ExtensionFormat,\n ExtensionFormat as ExtensionFormatLoose,\n ExtensionHandler,\n ExtensionMetadata,\n ExtensionMetadataDetails\n};\n","'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n Events.prototype = Object.create(null);\n\n //\n // This hack is needed because the `__proto__` property is still inherited in\n // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n //\n if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @private\n */\nfunction EE(fn, context, once) {\n this.fn = fn;\n this.context = context;\n this.once = once || false;\n}\n\n/**\n * Add a listener for a given event.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} context The context to invoke the listener with.\n * @param {Boolean} once Specify if the listener is a one-time listener.\n * @returns {EventEmitter}\n * @private\n */\nfunction addListener(emitter, event, fn, context, once) {\n if (typeof fn !== 'function') {\n throw new TypeError('The listener must be a function');\n }\n\n var listener = new EE(fn, context || emitter, once)\n , evt = prefix ? prefix + event : event;\n\n if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;\n else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);\n else emitter._events[evt] = [emitter._events[evt], listener];\n\n return emitter;\n}\n\n/**\n * Clear event by name.\n *\n * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.\n * @param {(String|Symbol)} evt The Event name.\n * @private\n */\nfunction clearEvent(emitter, evt) {\n if (--emitter._eventsCount === 0) emitter._events = new Events();\n else delete emitter._events[evt];\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @public\n */\nfunction EventEmitter() {\n this._events = new Events();\n this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n var names = []\n , events\n , name;\n\n if (this._eventsCount === 0) return names;\n\n for (name in (events = this._events)) {\n if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n }\n\n if (Object.getOwnPropertySymbols) {\n return names.concat(Object.getOwnPropertySymbols(events));\n }\n\n return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Array} The registered listeners.\n * @public\n */\nEventEmitter.prototype.listeners = function listeners(event) {\n var evt = prefix ? prefix + event : event\n , handlers = this._events[evt];\n\n if (!handlers) return [];\n if (handlers.fn) return [handlers.fn];\n\n for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {\n ee[i] = handlers[i].fn;\n }\n\n return ee;\n};\n\n/**\n * Return the number of listeners listening to a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Number} The number of listeners.\n * @public\n */\nEventEmitter.prototype.listenerCount = function listenerCount(event) {\n var evt = prefix ? prefix + event : event\n , listeners = this._events[evt];\n\n if (!listeners) return 0;\n if (listeners.fn) return 1;\n return listeners.length;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return false;\n\n var listeners = this._events[evt]\n , len = arguments.length\n , args\n , i;\n\n if (listeners.fn) {\n if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n switch (len) {\n case 1: return listeners.fn.call(listeners.context), true;\n case 2: return listeners.fn.call(listeners.context, a1), true;\n case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n }\n\n for (i = 1, args = new Array(len -1); i < len; i++) {\n args[i - 1] = arguments[i];\n }\n\n listeners.fn.apply(listeners.context, args);\n } else {\n var length = listeners.length\n , j;\n\n for (i = 0; i < length; i++) {\n if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n switch (len) {\n case 1: listeners[i].fn.call(listeners[i].context); break;\n case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n default:\n if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n args[j - 1] = arguments[j];\n }\n\n listeners[i].fn.apply(listeners[i].context, args);\n }\n }\n }\n\n return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n return addListener(this, event, fn, context, false);\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn The listener function.\n * @param {*} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n return addListener(this, event, fn, context, true);\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {(String|Symbol)} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {*} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return this;\n if (!fn) {\n clearEvent(this, evt);\n return this;\n }\n\n var listeners = this._events[evt];\n\n if (listeners.fn) {\n if (\n listeners.fn === fn &&\n (!once || listeners.once) &&\n (!context || listeners.context === context)\n ) {\n clearEvent(this, evt);\n }\n } else {\n for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n if (\n listeners[i].fn !== fn ||\n (once && !listeners[i].once) ||\n (context && listeners[i].context !== context)\n ) {\n events.push(listeners[i]);\n }\n }\n\n //\n // Reset the array, or remove it completely if we have no more listeners.\n //\n if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n else clearEvent(this, evt);\n }\n\n return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {(String|Symbol)} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n var evt;\n\n if (event) {\n evt = prefix ? prefix + event : event;\n if (this._events[evt]) clearEvent(this, evt);\n } else {\n this._events = new Events();\n this._eventsCount = 0;\n }\n\n return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n module.exports = EventEmitter;\n}\n","var r={grad:.9,turn:360,rad:360/(2*Math.PI)},t=function(r){return\"string\"==typeof r?r.length>0:\"number\"==typeof r},n=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=Math.pow(10,t)),Math.round(n*r)/n+0},e=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=1),r>n?n:r>t?r:t},u=function(r){return(r=isFinite(r)?r%360:0)>0?r:r+360},a=function(r){return{r:e(r.r,0,255),g:e(r.g,0,255),b:e(r.b,0,255),a:e(r.a)}},o=function(r){return{r:n(r.r),g:n(r.g),b:n(r.b),a:n(r.a,3)}},i=/^#([0-9a-f]{3,8})$/i,s=function(r){var t=r.toString(16);return t.length<2?\"0\"+t:t},h=function(r){var t=r.r,n=r.g,e=r.b,u=r.a,a=Math.max(t,n,e),o=a-Math.min(t,n,e),i=o?a===t?(n-e)/o:a===n?2+(e-t)/o:4+(t-n)/o:0;return{h:60*(i<0?i+6:i),s:a?o/a*100:0,v:a/255*100,a:u}},b=function(r){var t=r.h,n=r.s,e=r.v,u=r.a;t=t/360*6,n/=100,e/=100;var a=Math.floor(t),o=e*(1-n),i=e*(1-(t-a)*n),s=e*(1-(1-t+a)*n),h=a%6;return{r:255*[e,i,o,o,s,e][h],g:255*[s,e,e,i,o,o][h],b:255*[o,o,s,e,e,i][h],a:u}},g=function(r){return{h:u(r.h),s:e(r.s,0,100),l:e(r.l,0,100),a:e(r.a)}},d=function(r){return{h:n(r.h),s:n(r.s),l:n(r.l),a:n(r.a,3)}},f=function(r){return b((n=(t=r).s,{h:t.h,s:(n*=((e=t.l)<50?e:100-e)/100)>0?2*n/(e+n)*100:0,v:e+n,a:t.a}));var t,n,e},c=function(r){return{h:(t=h(r)).h,s:(u=(200-(n=t.s))*(e=t.v)/100)>0&&u<200?n*e/100/(u<=100?u:200-u)*100:0,l:u/2,a:t.a};var t,n,e,u},l=/^hsla?\\(\\s*([+-]?\\d*\\.?\\d+)(deg|rad|grad|turn)?\\s*,\\s*([+-]?\\d*\\.?\\d+)%\\s*,\\s*([+-]?\\d*\\.?\\d+)%\\s*(?:,\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*)?\\)$/i,p=/^hsla?\\(\\s*([+-]?\\d*\\.?\\d+)(deg|rad|grad|turn)?\\s+([+-]?\\d*\\.?\\d+)%\\s+([+-]?\\d*\\.?\\d+)%\\s*(?:\\/\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*)?\\)$/i,v=/^rgba?\\(\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*,\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*,\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*(?:,\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*)?\\)$/i,m=/^rgba?\\(\\s*([+-]?\\d*\\.?\\d+)(%)?\\s+([+-]?\\d*\\.?\\d+)(%)?\\s+([+-]?\\d*\\.?\\d+)(%)?\\s*(?:\\/\\s*([+-]?\\d*\\.?\\d+)(%)?\\s*)?\\)$/i,y={string:[[function(r){var t=i.exec(r);return t?(r=t[1]).length<=4?{r:parseInt(r[0]+r[0],16),g:parseInt(r[1]+r[1],16),b:parseInt(r[2]+r[2],16),a:4===r.length?n(parseInt(r[3]+r[3],16)/255,2):1}:6===r.length||8===r.length?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:8===r.length?n(parseInt(r.substr(6,2),16)/255,2):1}:null:null},\"hex\"],[function(r){var t=v.exec(r)||m.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:a({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:void 0===t[7]?1:Number(t[7])/(t[8]?100:1)}):null},\"rgb\"],[function(t){var n=l.exec(t)||p.exec(t);if(!n)return null;var e,u,a=g({h:(e=n[1],u=n[2],void 0===u&&(u=\"deg\"),Number(e)*(r[u]||1)),s:Number(n[3]),l:Number(n[4]),a:void 0===n[5]?1:Number(n[5])/(n[6]?100:1)});return f(a)},\"hsl\"]],object:[[function(r){var n=r.r,e=r.g,u=r.b,o=r.a,i=void 0===o?1:o;return t(n)&&t(e)&&t(u)?a({r:Number(n),g:Number(e),b:Number(u),a:Number(i)}):null},\"rgb\"],[function(r){var n=r.h,e=r.s,u=r.l,a=r.a,o=void 0===a?1:a;if(!t(n)||!t(e)||!t(u))return null;var i=g({h:Number(n),s:Number(e),l:Number(u),a:Number(o)});return f(i)},\"hsl\"],[function(r){var n=r.h,a=r.s,o=r.v,i=r.a,s=void 0===i?1:i;if(!t(n)||!t(a)||!t(o))return null;var h=function(r){return{h:u(r.h),s:e(r.s,0,100),v:e(r.v,0,100),a:e(r.a)}}({h:Number(n),s:Number(a),v:Number(o),a:Number(s)});return b(h)},\"hsv\"]]},N=function(r,t){for(var n=0;n=.5},r.prototype.toHex=function(){return r=o(this.rgba),t=r.r,e=r.g,u=r.b,i=(a=r.a)<1?s(n(255*a)):\"\",\"#\"+s(t)+s(e)+s(u)+i;var r,t,e,u,a,i},r.prototype.toRgb=function(){return o(this.rgba)},r.prototype.toRgbString=function(){return r=o(this.rgba),t=r.r,n=r.g,e=r.b,(u=r.a)<1?\"rgba(\"+t+\", \"+n+\", \"+e+\", \"+u+\")\":\"rgb(\"+t+\", \"+n+\", \"+e+\")\";var r,t,n,e,u},r.prototype.toHsl=function(){return d(c(this.rgba))},r.prototype.toHslString=function(){return r=d(c(this.rgba)),t=r.h,n=r.s,e=r.l,(u=r.a)<1?\"hsla(\"+t+\", \"+n+\"%, \"+e+\"%, \"+u+\")\":\"hsl(\"+t+\", \"+n+\"%, \"+e+\"%)\";var r,t,n,e,u},r.prototype.toHsv=function(){return r=h(this.rgba),{h:n(r.h),s:n(r.s),v:n(r.v),a:n(r.a,3)};var r},r.prototype.invert=function(){return w({r:255-(r=this.rgba).r,g:255-r.g,b:255-r.b,a:r.a});var r},r.prototype.saturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,r))},r.prototype.desaturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,-r))},r.prototype.grayscale=function(){return w(M(this.rgba,-1))},r.prototype.lighten=function(r){return void 0===r&&(r=.1),w($(this.rgba,r))},r.prototype.darken=function(r){return void 0===r&&(r=.1),w($(this.rgba,-r))},r.prototype.rotate=function(r){return void 0===r&&(r=15),this.hue(this.hue()+r)},r.prototype.alpha=function(r){return\"number\"==typeof r?w({r:(t=this.rgba).r,g:t.g,b:t.b,a:r}):n(this.rgba.a,3);var t},r.prototype.hue=function(r){var t=c(this.rgba);return\"number\"==typeof r?w({h:r,s:t.s,l:t.l,a:t.a}):n(t.h)},r.prototype.isEqual=function(r){return this.toHex()===w(r).toHex()},r}(),w=function(r){return r instanceof j?r:new j(r)},S=[],k=function(r){r.forEach(function(r){S.indexOf(r)<0&&(r(j,y),S.push(r))})},E=function(){return new j({r:255*Math.random(),g:255*Math.random(),b:255*Math.random()})};export{j as Colord,w as colord,k as extend,I as getFormat,E as random};\n","export default function(e,f){var a={white:\"#ffffff\",bisque:\"#ffe4c4\",blue:\"#0000ff\",cadetblue:\"#5f9ea0\",chartreuse:\"#7fff00\",chocolate:\"#d2691e\",coral:\"#ff7f50\",antiquewhite:\"#faebd7\",aqua:\"#00ffff\",azure:\"#f0ffff\",whitesmoke:\"#f5f5f5\",papayawhip:\"#ffefd5\",plum:\"#dda0dd\",blanchedalmond:\"#ffebcd\",black:\"#000000\",gold:\"#ffd700\",goldenrod:\"#daa520\",gainsboro:\"#dcdcdc\",cornsilk:\"#fff8dc\",cornflowerblue:\"#6495ed\",burlywood:\"#deb887\",aquamarine:\"#7fffd4\",beige:\"#f5f5dc\",crimson:\"#dc143c\",cyan:\"#00ffff\",darkblue:\"#00008b\",darkcyan:\"#008b8b\",darkgoldenrod:\"#b8860b\",darkkhaki:\"#bdb76b\",darkgray:\"#a9a9a9\",darkgreen:\"#006400\",darkgrey:\"#a9a9a9\",peachpuff:\"#ffdab9\",darkmagenta:\"#8b008b\",darkred:\"#8b0000\",darkorchid:\"#9932cc\",darkorange:\"#ff8c00\",darkslateblue:\"#483d8b\",gray:\"#808080\",darkslategray:\"#2f4f4f\",darkslategrey:\"#2f4f4f\",deeppink:\"#ff1493\",deepskyblue:\"#00bfff\",wheat:\"#f5deb3\",firebrick:\"#b22222\",floralwhite:\"#fffaf0\",ghostwhite:\"#f8f8ff\",darkviolet:\"#9400d3\",magenta:\"#ff00ff\",green:\"#008000\",dodgerblue:\"#1e90ff\",grey:\"#808080\",honeydew:\"#f0fff0\",hotpink:\"#ff69b4\",blueviolet:\"#8a2be2\",forestgreen:\"#228b22\",lawngreen:\"#7cfc00\",indianred:\"#cd5c5c\",indigo:\"#4b0082\",fuchsia:\"#ff00ff\",brown:\"#a52a2a\",maroon:\"#800000\",mediumblue:\"#0000cd\",lightcoral:\"#f08080\",darkturquoise:\"#00ced1\",lightcyan:\"#e0ffff\",ivory:\"#fffff0\",lightyellow:\"#ffffe0\",lightsalmon:\"#ffa07a\",lightseagreen:\"#20b2aa\",linen:\"#faf0e6\",mediumaquamarine:\"#66cdaa\",lemonchiffon:\"#fffacd\",lime:\"#00ff00\",khaki:\"#f0e68c\",mediumseagreen:\"#3cb371\",limegreen:\"#32cd32\",mediumspringgreen:\"#00fa9a\",lightskyblue:\"#87cefa\",lightblue:\"#add8e6\",midnightblue:\"#191970\",lightpink:\"#ffb6c1\",mistyrose:\"#ffe4e1\",moccasin:\"#ffe4b5\",mintcream:\"#f5fffa\",lightslategray:\"#778899\",lightslategrey:\"#778899\",navajowhite:\"#ffdead\",navy:\"#000080\",mediumvioletred:\"#c71585\",powderblue:\"#b0e0e6\",palegoldenrod:\"#eee8aa\",oldlace:\"#fdf5e6\",paleturquoise:\"#afeeee\",mediumturquoise:\"#48d1cc\",mediumorchid:\"#ba55d3\",rebeccapurple:\"#663399\",lightsteelblue:\"#b0c4de\",mediumslateblue:\"#7b68ee\",thistle:\"#d8bfd8\",tan:\"#d2b48c\",orchid:\"#da70d6\",mediumpurple:\"#9370db\",purple:\"#800080\",pink:\"#ffc0cb\",skyblue:\"#87ceeb\",springgreen:\"#00ff7f\",palegreen:\"#98fb98\",red:\"#ff0000\",yellow:\"#ffff00\",slateblue:\"#6a5acd\",lavenderblush:\"#fff0f5\",peru:\"#cd853f\",palevioletred:\"#db7093\",violet:\"#ee82ee\",teal:\"#008080\",slategray:\"#708090\",slategrey:\"#708090\",aliceblue:\"#f0f8ff\",darkseagreen:\"#8fbc8f\",darkolivegreen:\"#556b2f\",greenyellow:\"#adff2f\",seagreen:\"#2e8b57\",seashell:\"#fff5ee\",tomato:\"#ff6347\",silver:\"#c0c0c0\",sienna:\"#a0522d\",lavender:\"#e6e6fa\",lightgreen:\"#90ee90\",orange:\"#ffa500\",orangered:\"#ff4500\",steelblue:\"#4682b4\",royalblue:\"#4169e1\",turquoise:\"#40e0d0\",yellowgreen:\"#9acd32\",salmon:\"#fa8072\",saddlebrown:\"#8b4513\",sandybrown:\"#f4a460\",rosybrown:\"#bc8f8f\",darksalmon:\"#e9967a\",lightgoldenrodyellow:\"#fafad2\",snow:\"#fffafa\",lightgrey:\"#d3d3d3\",lightgray:\"#d3d3d3\",dimgray:\"#696969\",dimgrey:\"#696969\",olivedrab:\"#6b8e23\",olive:\"#808000\"},r={};for(var d in a)r[a[d]]=d;var l={};e.prototype.toName=function(f){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return\"transparent\";var d,i,n=r[this.toHex()];if(n)return n;if(null==f?void 0:f.closest){var o=this.toRgb(),t=1/0,b=\"black\";if(!l.length)for(var c in a)l[c]=new e(a[c]).toRgb();for(var g in a){var u=(d=o,i=l[g],Math.pow(d.r-i.r,2)+Math.pow(d.g-i.g,2)+Math.pow(d.b-i.b,2));u [!IMPORTANT] You should be careful when using this shared instance, as it is mutable and can be\n * > changed by any code that uses it.\n * >\n * > It is best used for one-off color operations or temporary transformations.\n * > For persistent colors, create your own Color instance instead.\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Use shared instance for one-off color operations\n * Color.shared.setValue(0xff0000);\n * const redHex = Color.shared.toHex(); // \"#ff0000\"\n * const redRgb = Color.shared.toRgbArray(); // [1, 0, 0]\n *\n * // Temporary color transformations\n * const colorNumber = Color.shared\n * .setValue('#ff0000') // Set to red\n * .setAlpha(0.5) // Make semi-transparent\n * .premultiply(0.8) // Apply premultiplication\n * .toNumber(); // Convert to number\n *\n * // Chain multiple operations\n * const result = Color.shared\n * .setValue(someColor)\n * .multiply(tintColor)\n * .toPremultiplied(alpha);\n * ```\n * @remarks\n * - This is a shared instance - be careful about multiple code paths using it simultaneously\n * - Use for temporary color operations to avoid allocating new Color instances\n * - The value is preserved between operations, so reset if needed\n * - For persistent colors, create your own Color instance instead\n */\n public static readonly shared = new Color();\n\n /**\n * Temporary Color object for static uses internally.\n * As to not conflict with Color.shared.\n * @ignore\n */\n private static readonly _temp = new Color();\n\n /** Pattern for hex strings */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n private static readonly HEX_PATTERN = /^(#|0x)?(([a-f0-9]{3}){1,2}([a-f0-9]{2})?)$/i;\n\n /** Internal color source, from constructor or set value */\n private _value: Exclude | null;\n\n /** Normalized rgba component, floats from 0-1 */\n private _components: Float32Array;\n\n /** Cache color as number */\n private _int: number;\n\n /** An array of the current Color. Only populated when `toArray` functions are called */\n private _arrayRgba: number[] | null;\n private _arrayRgb: number[] | null;\n\n /**\n * @param {ColorSource} value - Optional value to use, if not provided, white is used.\n */\n constructor(value: ColorSource = 0xffffff)\n {\n this._value = null;\n this._components = new Float32Array(4);\n this._components.fill(1);\n this._int = 0xffffff;\n this.value = value;\n }\n\n /**\n * Get the red component of the color, normalized between 0 and 1.\n * @example\n * ```ts\n * const color = new Color('red');\n * console.log(color.red); // 1\n *\n * const green = new Color('#00ff00');\n * console.log(green.red); // 0\n * ```\n */\n get red(): number\n {\n return this._components[0];\n }\n\n /**\n * Get the green component of the color, normalized between 0 and 1.\n * @example\n * ```ts\n * const color = new Color('lime');\n * console.log(color.green); // 1\n *\n * const red = new Color('#ff0000');\n * console.log(red.green); // 0\n * ```\n */\n get green(): number\n {\n return this._components[1];\n }\n\n /**\n * Get the blue component of the color, normalized between 0 and 1.\n * @example\n * ```ts\n * const color = new Color('blue');\n * console.log(color.blue); // 1\n *\n * const yellow = new Color('#ffff00');\n * console.log(yellow.blue); // 0\n * ```\n */\n get blue(): number\n {\n return this._components[2];\n }\n\n /**\n * Get the alpha component of the color, normalized between 0 and 1.\n * @example\n * ```ts\n * const color = new Color('red');\n * console.log(color.alpha); // 1 (fully opaque)\n *\n * const transparent = new Color('rgba(255, 0, 0, 0.5)');\n * console.log(transparent.alpha); // 0.5 (semi-transparent)\n * ```\n */\n get alpha(): number\n {\n return this._components[3];\n }\n\n /**\n * Sets the color value and returns the instance for chaining.\n *\n * This is a chainable version of setting the `value` property.\n * @param value - The color to set. Accepts various formats:\n * - Hex strings/numbers (e.g., '#ff0000', 0xff0000)\n * - RGB/RGBA values (arrays, objects)\n * - CSS color names\n * - HSL/HSLA values\n * - HSV/HSVA values\n * @returns The Color instance for chaining\n * @example\n * ```ts\n * // Basic usage\n * const color = new Color();\n * color.setValue('#ff0000')\n * .setAlpha(0.5)\n * .premultiply(0.8);\n *\n * // Different formats\n * color.setValue(0xff0000); // Hex number\n * color.setValue('#ff0000'); // Hex string\n * color.setValue([1, 0, 0]); // RGB array\n * color.setValue([1, 0, 0, 0.5]); // RGBA array\n * color.setValue({ r: 1, g: 0, b: 0 }); // RGB object\n *\n * // Copy from another color\n * const red = new Color('red');\n * color.setValue(red);\n * ```\n * @throws {Error} If the color value is invalid or null\n * @see {@link Color.value} For the underlying value property\n */\n public setValue(value: ColorSource): this\n {\n this.value = value;\n\n return this;\n }\n\n /**\n * The current color source. This property allows getting and setting the color value\n * while preserving the original format where possible.\n * @remarks\n * When setting:\n * - Setting to a `Color` instance copies its source and components\n * - Setting to other valid sources normalizes and stores the value\n * - Setting to `null` throws an Error\n * - The color remains unchanged if normalization fails\n *\n * When getting:\n * - Returns `null` if color was modified by {@link Color.multiply} or {@link Color.premultiply}\n * - Otherwise returns the original color source\n * @example\n * ```ts\n * // Setting different color formats\n * const color = new Color();\n *\n * color.value = 0xff0000; // Hex number\n * color.value = '#ff0000'; // Hex string\n * color.value = [1, 0, 0]; // RGB array\n * color.value = [1, 0, 0, 0.5]; // RGBA array\n * color.value = { r: 1, g: 0, b: 0 }; // RGB object\n *\n * // Copying from another color\n * const red = new Color('red');\n * color.value = red; // Copies red's components\n *\n * // Getting the value\n * console.log(color.value); // Returns original format\n *\n * // After modifications\n * color.multiply([0.5, 0.5, 0.5]);\n * console.log(color.value); // Returns null\n * ```\n * @throws {Error} When attempting to set `null`\n */\n set value(value: ColorSource | null)\n {\n // Support copying from other Color objects\n if (value instanceof Color)\n {\n this._value = this._cloneSource(value._value);\n this._int = value._int;\n this._components.set(value._components);\n }\n else if (value === null)\n {\n throw new Error('Cannot set Color#value to null');\n }\n else if (this._value === null || !this._isSourceEqual(this._value, value))\n {\n this._value = this._cloneSource(value);\n this._normalize(this._value);\n }\n }\n get value(): Exclude | null\n {\n return this._value;\n }\n\n /**\n * Copy a color source internally.\n * @param value - Color source\n */\n private _cloneSource(value: Exclude | null): Exclude | null\n {\n if (typeof value === 'string' || typeof value === 'number' || value instanceof Number || value === null)\n {\n return value;\n }\n else if (Array.isArray(value) || ArrayBuffer.isView(value))\n {\n return value.slice(0);\n }\n else if (typeof value === 'object' && value !== null)\n {\n return { ...value };\n }\n\n return value;\n }\n\n /**\n * Equality check for color sources.\n * @param value1 - First color source\n * @param value2 - Second color source\n * @returns `true` if the color sources are equal, `false` otherwise.\n */\n private _isSourceEqual(value1: Exclude, value2: Exclude): boolean\n {\n const type1 = typeof value1;\n const type2 = typeof value2;\n\n // Mismatched types\n if (type1 !== type2)\n {\n return false;\n }\n // Handle numbers/strings and things that extend Number\n // important to do the instanceof Number first, as this is \"object\" type\n else if (type1 === 'number' || type1 === 'string' || value1 instanceof Number)\n {\n return value1 === value2;\n }\n // Handle Arrays and TypedArrays\n else if (\n (Array.isArray(value1) && Array.isArray(value2))\n || (ArrayBuffer.isView(value1) && ArrayBuffer.isView(value2))\n )\n {\n if (value1.length !== value2.length)\n {\n return false;\n }\n\n return value1.every((v, i) => v === value2[i]);\n }\n // Handle Objects\n else if (value1 !== null && value2 !== null)\n {\n const keys1 = Object.keys(value1) as (keyof typeof value1)[];\n const keys2 = Object.keys(value2) as (keyof typeof value2)[];\n\n if (keys1.length !== keys2.length)\n {\n return false;\n }\n\n return keys1.every((key) => value1[key] === value2[key]);\n }\n\n return value1 === value2;\n }\n\n /**\n * Convert to a RGBA color object with normalized components (0-1).\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Convert colors to RGBA objects\n * new Color('white').toRgba(); // returns { r: 1, g: 1, b: 1, a: 1 }\n * new Color('#ff0000').toRgba(); // returns { r: 1, g: 0, b: 0, a: 1 }\n *\n * // With transparency\n * new Color('rgba(255,0,0,0.5)').toRgba(); // returns { r: 1, g: 0, b: 0, a: 0.5 }\n * ```\n * @returns An RGBA object with normalized components\n */\n public toRgba(): RgbaColor\n {\n const [r, g, b, a] = this._components;\n\n return { r, g, b, a };\n }\n\n /**\n * Convert to a RGB color object with normalized components (0-1).\n *\n * Alpha component is omitted in the output.\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Convert colors to RGB objects\n * new Color('white').toRgb(); // returns { r: 1, g: 1, b: 1 }\n * new Color('#ff0000').toRgb(); // returns { r: 1, g: 0, b: 0 }\n *\n * // Alpha is ignored\n * new Color('rgba(255,0,0,0.5)').toRgb(); // returns { r: 1, g: 0, b: 0 }\n * ```\n * @returns An RGB object with normalized components\n */\n public toRgb(): RgbColor\n {\n const [r, g, b] = this._components;\n\n return { r, g, b };\n }\n\n /**\n * Convert to a CSS-style rgba string representation.\n *\n * RGB components are scaled to 0-255 range, alpha remains 0-1.\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Convert colors to RGBA strings\n * new Color('white').toRgbaString(); // returns \"rgba(255,255,255,1)\"\n * new Color('#ff0000').toRgbaString(); // returns \"rgba(255,0,0,1)\"\n *\n * // With transparency\n * new Color([1, 0, 0, 0.5]).toRgbaString(); // returns \"rgba(255,0,0,0.5)\"\n * ```\n * @returns A CSS-compatible rgba string\n */\n public toRgbaString(): string\n {\n const [r, g, b] = this.toUint8RgbArray();\n\n return `rgba(${r},${g},${b},${this.alpha})`;\n }\n\n /**\n * Convert to an [R, G, B] array of clamped uint8 values (0 to 255).\n * @param {number[]|Uint8Array|Uint8ClampedArray} [out] - Optional output array. If not provided,\n * a cached array will be used and returned.\n * @returns Array containing RGB components as integers between 0-255\n * @example\n * ```ts\n * // Basic usage\n * new Color('white').toUint8RgbArray(); // returns [255, 255, 255]\n * new Color('#ff0000').toUint8RgbArray(); // returns [255, 0, 0]\n *\n * // Using custom output array\n * const rgb = new Uint8Array(3);\n * new Color('blue').toUint8RgbArray(rgb); // rgb is now [0, 0, 255]\n *\n * // Using different array types\n * new Color('red').toUint8RgbArray(new Uint8ClampedArray(3)); // [255, 0, 0]\n * new Color('red').toUint8RgbArray([]); // [255, 0, 0]\n * ```\n * @remarks\n * - Output values are always clamped between 0-255\n * - Alpha component is not included in output\n * - Reuses internal cache array if no output array provided\n */\n public toUint8RgbArray(out?: T): T\n {\n const [r, g, b] = this._components;\n\n if (!this._arrayRgb)\n {\n this._arrayRgb = [];\n }\n\n out ||= this._arrayRgb as T;\n\n out[0] = Math.round(r * 255);\n out[1] = Math.round(g * 255);\n out[2] = Math.round(b * 255);\n\n return out;\n }\n\n /**\n * Convert to an [R, G, B, A] array of normalized floats (numbers from 0.0 to 1.0).\n * @param {number[]|Float32Array} [out] - Optional output array. If not provided,\n * a cached array will be used and returned.\n * @returns Array containing RGBA components as floats between 0-1\n * @example\n * ```ts\n * // Basic usage\n * new Color('white').toArray(); // returns [1, 1, 1, 1]\n * new Color('red').toArray(); // returns [1, 0, 0, 1]\n *\n * // With alpha\n * new Color('rgba(255,0,0,0.5)').toArray(); // returns [1, 0, 0, 0.5]\n *\n * // Using custom output array\n * const rgba = new Float32Array(4);\n * new Color('blue').toArray(rgba); // rgba is now [0, 0, 1, 1]\n * ```\n * @remarks\n * - Output values are normalized between 0-1\n * - Includes alpha component as the fourth value\n * - Reuses internal cache array if no output array provided\n */\n public toArray(out?: T): T\n {\n if (!this._arrayRgba)\n {\n this._arrayRgba = [];\n }\n\n out ||= this._arrayRgba as T;\n const [r, g, b, a] = this._components;\n\n out[0] = r;\n out[1] = g;\n out[2] = b;\n out[3] = a;\n\n return out;\n }\n\n /**\n * Convert to an [R, G, B] array of normalized floats (numbers from 0.0 to 1.0).\n * @param {number[]|Float32Array} [out] - Optional output array. If not provided,\n * a cached array will be used and returned.\n * @returns Array containing RGB components as floats between 0-1\n * @example\n * ```ts\n * // Basic usage\n * new Color('white').toRgbArray(); // returns [1, 1, 1]\n * new Color('red').toRgbArray(); // returns [1, 0, 0]\n *\n * // Using custom output array\n * const rgb = new Float32Array(3);\n * new Color('blue').toRgbArray(rgb); // rgb is now [0, 0, 1]\n * ```\n * @remarks\n * - Output values are normalized between 0-1\n * - Alpha component is omitted from output\n * - Reuses internal cache array if no output array provided\n */\n public toRgbArray(out?: T): T\n {\n if (!this._arrayRgb)\n {\n this._arrayRgb = [];\n }\n\n out ||= this._arrayRgb as T;\n const [r, g, b] = this._components;\n\n out[0] = r;\n out[1] = g;\n out[2] = b;\n\n return out;\n }\n\n /**\n * Convert to a hexadecimal number.\n * @returns The color as a 24-bit RGB integer\n * @example\n * ```ts\n * // Basic usage\n * new Color('white').toNumber(); // returns 0xffffff\n * new Color('red').toNumber(); // returns 0xff0000\n *\n * // Store as hex\n * const color = new Color('blue');\n * const hex = color.toNumber(); // 0x0000ff\n * ```\n */\n public toNumber(): number\n {\n return this._int;\n }\n\n /**\n * Convert to a BGR number.\n *\n * Useful for platforms that expect colors in BGR format.\n * @returns The color as a 24-bit BGR integer\n * @example\n * ```ts\n * // Convert RGB to BGR\n * new Color(0xffcc99).toBgrNumber(); // returns 0x99ccff\n *\n * // Common use case: platform-specific color format\n * const color = new Color('orange');\n * const bgrColor = color.toBgrNumber(); // Color with swapped R/B channels\n * ```\n * @remarks\n * This swaps the red and blue channels compared to the normal RGB format:\n * - RGB 0xRRGGBB becomes BGR 0xBBGGRR\n */\n public toBgrNumber(): number\n {\n const [r, g, b] = this.toUint8RgbArray();\n\n return (b << 16) + (g << 8) + r;\n }\n\n /**\n * Convert to a hexadecimal number in little endian format (e.g., BBGGRR).\n *\n * Useful for platforms that expect colors in little endian byte order.\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Convert RGB color to little endian format\n * new Color(0xffcc99).toLittleEndianNumber(); // returns 0x99ccff\n *\n * // Common use cases:\n * const color = new Color('orange');\n * const leColor = color.toLittleEndianNumber(); // Swaps byte order for LE systems\n *\n * // Multiple conversions\n * const colors = {\n * normal: 0xffcc99,\n * littleEndian: new Color(0xffcc99).toLittleEndianNumber(), // 0x99ccff\n * backToNormal: new Color(0x99ccff).toLittleEndianNumber() // 0xffcc99\n * };\n * ```\n * @remarks\n * - Swaps R and B channels in the color value\n * - RGB 0xRRGGBB becomes 0xBBGGRR\n * - Useful for systems that use little endian byte order\n * - Can be used to convert back and forth between formats\n * @returns The color as a number in little endian format (BBGGRR)\n * @see {@link Color.toBgrNumber} For BGR format without byte swapping\n */\n public toLittleEndianNumber(): number\n {\n const value = this._int;\n\n return (value >> 16) + (value & 0xff00) + ((value & 0xff) << 16);\n }\n\n /**\n * Multiply with another color.\n *\n * This action is destructive and modifies the original color.\n * @param {ColorSource} value - The color to multiply by. Accepts any valid color format:\n * - Hex strings/numbers (e.g., '#ff0000', 0xff0000)\n * - RGB/RGBA arrays ([1, 0, 0], [1, 0, 0, 1])\n * - Color objects ({ r: 1, g: 0, b: 0 })\n * - CSS color names ('red', 'blue')\n * @returns this - The Color instance for chaining\n * @example\n * ```ts\n * // Basic multiplication\n * const color = new Color('#ff0000');\n * color.multiply(0x808080); // 50% darker red\n *\n * // With transparency\n * color.multiply([1, 1, 1, 0.5]); // 50% transparent\n *\n * // Chain operations\n * color\n * .multiply('#808080')\n * .multiply({ r: 1, g: 1, b: 1, a: 0.5 });\n * ```\n * @remarks\n * - Multiplies each RGB component and alpha separately\n * - Values are clamped between 0-1\n * - Original color format is lost (value becomes null)\n * - Operation cannot be undone\n */\n public multiply(value: ColorSource): this\n {\n const [r, g, b, a] = Color._temp.setValue(value)._components;\n\n this._components[0] *= r;\n this._components[1] *= g;\n this._components[2] *= b;\n this._components[3] *= a;\n\n this._refreshInt();\n this._value = null;\n\n return this;\n }\n\n /**\n * Converts color to a premultiplied alpha format.\n *\n * This action is destructive and modifies the original color.\n * @param alpha - The alpha value to multiply by (0-1)\n * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels\n * @returns {Color} The Color instance for chaining\n * @example\n * ```ts\n * // Basic premultiplication\n * const color = new Color('red');\n * color.premultiply(0.5); // 50% transparent red with premultiplied RGB\n *\n * // Alpha only (RGB unchanged)\n * color.premultiply(0.5, false); // 50% transparent, original RGB\n *\n * // Chain with other operations\n * color\n * .multiply(0x808080)\n * .premultiply(0.5)\n * .toNumber();\n * ```\n * @remarks\n * - RGB channels are multiplied by alpha when applyToRGB is true\n * - Alpha is always set to the provided value\n * - Values are clamped between 0-1\n * - Original color format is lost (value becomes null)\n * - Operation cannot be undone\n */\n public premultiply(alpha: number, applyToRGB = true): this\n {\n if (applyToRGB)\n {\n this._components[0] *= alpha;\n this._components[1] *= alpha;\n this._components[2] *= alpha;\n }\n this._components[3] = alpha;\n\n this._refreshInt();\n this._value = null;\n\n return this;\n }\n\n /**\n * Returns the color as a 32-bit premultiplied alpha integer.\n *\n * Format: 0xAARRGGBB\n * @param {number} alpha - The alpha value to multiply by (0-1)\n * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels\n * @returns {number} The premultiplied color as a 32-bit integer\n * @example\n * ```ts\n * // Convert to premultiplied format\n * const color = new Color('red');\n *\n * // Full opacity (0xFFRRGGBB)\n * color.toPremultiplied(1.0); // 0xFFFF0000\n *\n * // 50% transparency with premultiplied RGB\n * color.toPremultiplied(0.5); // 0x7F7F0000\n *\n * // 50% transparency without RGB premultiplication\n * color.toPremultiplied(0.5, false); // 0x7FFF0000\n * ```\n * @remarks\n * - Returns full opacity (0xFF000000) when alpha is 1.0\n * - Returns 0 when alpha is 0.0 and applyToRGB is true\n * - RGB values are rounded during premultiplication\n */\n public toPremultiplied(alpha: number, applyToRGB = true): number\n {\n if (alpha === 1.0)\n {\n return (0xff << 24) + this._int;\n }\n if (alpha === 0.0)\n {\n return applyToRGB ? 0 : this._int;\n }\n let r = (this._int >> 16) & 0xff;\n let g = (this._int >> 8) & 0xff;\n let b = this._int & 0xff;\n\n if (applyToRGB)\n {\n r = ((r * alpha) + 0.5) | 0;\n g = ((g * alpha) + 0.5) | 0;\n b = ((b * alpha) + 0.5) | 0;\n }\n\n return ((alpha * 255) << 24) + (r << 16) + (g << 8) + b;\n }\n\n /**\n * Convert to a hexadecimal string (6 characters).\n * @returns A CSS-compatible hex color string (e.g., \"#ff0000\")\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Basic colors\n * new Color('red').toHex(); // returns \"#ff0000\"\n * new Color('white').toHex(); // returns \"#ffffff\"\n * new Color('black').toHex(); // returns \"#000000\"\n *\n * // From different formats\n * new Color(0xff0000).toHex(); // returns \"#ff0000\"\n * new Color([1, 0, 0]).toHex(); // returns \"#ff0000\"\n * new Color({ r: 1, g: 0, b: 0 }).toHex(); // returns \"#ff0000\"\n * ```\n * @remarks\n * - Always returns a 6-character hex string\n * - Includes leading \"#\" character\n * - Alpha channel is ignored\n * - Values are rounded to nearest hex value\n */\n public toHex(): string\n {\n const hexString = this._int.toString(16);\n\n return `#${'000000'.substring(0, 6 - hexString.length) + hexString}`;\n }\n\n /**\n * Convert to a hexadecimal string with alpha (8 characters).\n * @returns A CSS-compatible hex color string with alpha (e.g., \"#ff0000ff\")\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // Fully opaque colors\n * new Color('red').toHexa(); // returns \"#ff0000ff\"\n * new Color('white').toHexa(); // returns \"#ffffffff\"\n *\n * // With transparency\n * new Color('rgba(255, 0, 0, 0.5)').toHexa(); // returns \"#ff00007f\"\n * new Color([1, 0, 0, 0]).toHexa(); // returns \"#ff000000\"\n * ```\n * @remarks\n * - Returns an 8-character hex string\n * - Includes leading \"#\" character\n * - Alpha is encoded in last two characters\n * - Values are rounded to nearest hex value\n */\n public toHexa(): string\n {\n const alphaValue = Math.round(this._components[3] * 255);\n const alphaString = alphaValue.toString(16);\n\n return this.toHex() + '00'.substring(0, 2 - alphaString.length) + alphaString;\n }\n\n /**\n * Set alpha (transparency) value while preserving color components.\n *\n * Provides a chainable interface for setting alpha.\n * @param alpha - Alpha value between 0 (fully transparent) and 1 (fully opaque)\n * @returns The Color instance for chaining\n * @example\n * ```ts\n * // Basic alpha setting\n * const color = new Color('red');\n * color.setAlpha(0.5); // 50% transparent red\n *\n * // Chain with other operations\n * color\n * .setValue('#ff0000')\n * .setAlpha(0.8) // 80% opaque\n * .premultiply(0.5); // Further modify alpha\n *\n * // Reset to fully opaque\n * color.setAlpha(1);\n * ```\n * @remarks\n * - Alpha value is clamped between 0-1\n * - Can be chained with other color operations\n */\n public setAlpha(alpha: number): this\n {\n this._components[3] = this._clamp(alpha);\n\n return this;\n }\n\n /**\n * Normalize the input value into rgba\n * @param value - Input value\n */\n private _normalize(value: Exclude): void\n {\n let r: number | undefined;\n let g: number | undefined;\n let b: number | undefined;\n let a: number | undefined;\n\n // Number is a primitive so typeof works fine, but in the case\n // that someone creates a class that extends Number, we also\n // need to check for instanceof Number\n if (\n (typeof value === 'number' || value instanceof Number)\n && (value as number) >= 0\n && (value as number) <= 0xffffff\n )\n {\n const int = value as number; // cast required because instanceof Number is ambiguous for TS\n\n r = ((int >> 16) & 0xff) / 255;\n g = ((int >> 8) & 0xff) / 255;\n b = (int & 0xff) / 255;\n a = 1.0;\n }\n else if (\n (Array.isArray(value) || value instanceof Float32Array)\n // Can be rgb or rgba\n && value.length >= 3\n && value.length <= 4\n )\n {\n // make sure all values are 0 - 1\n value = this._clamp(value);\n [r, g, b, a = 1.0] = value;\n }\n else if (\n (value instanceof Uint8Array || value instanceof Uint8ClampedArray)\n // Can be rgb or rgba\n && value.length >= 3\n && value.length <= 4\n )\n {\n // make sure all values are 0 - 255\n value = this._clamp(value, 0, 255);\n [r, g, b, a = 255] = value;\n r /= 255;\n g /= 255;\n b /= 255;\n a /= 255;\n }\n else if (typeof value === 'string' || typeof value === 'object')\n {\n if (typeof value === 'string')\n {\n const match = Color.HEX_PATTERN.exec(value);\n\n if (match)\n {\n // Normalize hex string, remove 0x or # prefix\n value = `#${match[2]}`;\n }\n }\n\n const color = colord(value as AnyColor);\n\n if (color.isValid())\n {\n ({ r, g, b, a } = color.rgba);\n r /= 255;\n g /= 255;\n b /= 255;\n }\n }\n\n // Cache normalized values for rgba and hex integer\n if (r !== undefined)\n {\n this._components[0] = r as number;\n this._components[1] = g as number;\n this._components[2] = b as number;\n this._components[3] = a as number;\n this._refreshInt();\n }\n else\n {\n throw new Error(`Unable to convert color ${value}`);\n }\n }\n\n /** Refresh the internal color rgb number */\n private _refreshInt(): void\n {\n // Clamp values to 0 - 1\n this._clamp(this._components);\n\n const [r, g, b] = this._components;\n\n this._int = ((r * 255) << 16) + ((g * 255) << 8) + ((b * 255) | 0);\n }\n\n /**\n * Clamps values to a range. Will override original values\n * @param value - Value(s) to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n */\n private _clamp(value: T, min = 0, max = 1): T\n {\n if (typeof value === 'number')\n {\n return Math.min(Math.max(value, min), max) as T;\n }\n\n value.forEach((v, i) =>\n {\n value[i] = Math.min(Math.max(v, min), max);\n });\n\n return value;\n }\n\n /**\n * Check if a value can be interpreted as a valid color format.\n * Supports all color formats that can be used with the Color class.\n * @param value - Value to check\n * @returns True if the value can be used as a color\n * @example\n * ```ts\n * import { Color } from 'pixi.js';\n *\n * // CSS colors and hex values\n * Color.isColorLike('red'); // true\n * Color.isColorLike('#ff0000'); // true\n * Color.isColorLike(0xff0000); // true\n *\n * // Arrays (RGB/RGBA)\n * Color.isColorLike([1, 0, 0]); // true\n * Color.isColorLike([1, 0, 0, 0.5]); // true\n *\n * // TypedArrays\n * Color.isColorLike(new Float32Array([1, 0, 0])); // true\n * Color.isColorLike(new Uint8Array([255, 0, 0])); // true\n * Color.isColorLike(new Uint8ClampedArray([255, 0, 0])); // true\n *\n * // Object formats\n * Color.isColorLike({ r: 1, g: 0, b: 0 }); // true (RGB)\n * Color.isColorLike({ r: 1, g: 0, b: 0, a: 0.5 }); // true (RGBA)\n * Color.isColorLike({ h: 0, s: 100, l: 50 }); // true (HSL)\n * Color.isColorLike({ h: 0, s: 100, l: 50, a: 0.5 }); // true (HSLA)\n * Color.isColorLike({ h: 0, s: 100, v: 100 }); // true (HSV)\n * Color.isColorLike({ h: 0, s: 100, v: 100, a: 0.5 });// true (HSVA)\n *\n * // Color instances\n * Color.isColorLike(new Color('red')); // true\n *\n * // Invalid values\n * Color.isColorLike(null); // false\n * Color.isColorLike(undefined); // false\n * Color.isColorLike({}); // false\n * Color.isColorLike([]); // false\n * Color.isColorLike('not-a-color'); // false\n * ```\n * @remarks\n * Checks for the following formats:\n * - Numbers (0x000000 to 0xffffff)\n * - CSS color strings\n * - RGB/RGBA arrays and objects\n * - HSL/HSLA objects\n * - HSV/HSVA objects\n * - TypedArrays (Float32Array, Uint8Array, Uint8ClampedArray)\n * - Color instances\n * @see {@link ColorSource} For supported color format types\n * @see {@link Color.setValue} For setting color values\n * @category utility\n */\n public static isColorLike(value: unknown): value is ColorSource\n {\n return (\n typeof value === 'number'\n || typeof value === 'string'\n || value instanceof Number\n || value instanceof Color\n || Array.isArray(value)\n || value instanceof Uint8Array\n || value instanceof Uint8ClampedArray\n || value instanceof Float32Array\n || ((value as RgbColor).r !== undefined\n && (value as RgbColor).g !== undefined\n && (value as RgbColor).b !== undefined)\n || ((value as RgbaColor).r !== undefined\n && (value as RgbaColor).g !== undefined\n && (value as RgbaColor).b !== undefined\n && (value as RgbaColor).a !== undefined)\n || ((value as HslColor).h !== undefined\n && (value as HslColor).s !== undefined\n && (value as HslColor).l !== undefined)\n || ((value as HslaColor).h !== undefined\n && (value as HslaColor).s !== undefined\n && (value as HslaColor).l !== undefined\n && (value as HslaColor).a !== undefined)\n || ((value as HsvColor).h !== undefined\n && (value as HsvColor).s !== undefined\n && (value as HsvColor).v !== undefined)\n || ((value as HsvaColor).h !== undefined\n && (value as HsvaColor).s !== undefined\n && (value as HsvaColor).v !== undefined\n && (value as HsvaColor).a !== undefined)\n );\n }\n}\n","import type { Rectangle } from '../maths/shapes/Rectangle';\n\n/**\n * The CullingMixin interface provides properties and methods for managing culling behavior\n * of a display object. Culling is the process of determining whether an object should be rendered\n * based on its visibility within the current view or frame.\n *\n * Key Features:\n * - Custom culling areas for better performance\n * - Per-object culling control\n * - Child culling management\n * @example\n * ```ts\n * // Enable culling for a container\n * const container = new Container();\n * container.cullable = true;\n *\n * // Set custom cull area for better performance\n * container.cullArea = new Rectangle(0, 0, 800, 600);\n *\n * // Disable child culling for static scenes\n * container.cullableChildren = false;\n * ```\n * @category scene\n * @standard\n */\nexport interface CullingMixinConstructor\n{\n /**\n * Custom shape used for culling calculations instead of object bounds.\n * Defined in local space coordinates relative to the object.\n * > [!NOTE]\n * > Setting this to a custom Rectangle allows you to define a specific area for culling,\n * > which can improve performance by avoiding expensive bounds calculations.\n * @example\n * ```ts\n * const container = new Container();\n *\n * // Define custom culling boundary\n * container.cullArea = new Rectangle(0, 0, 800, 600);\n *\n * // Reset to use object bounds\n * container.cullArea = null;\n * ```\n * @remarks\n * - Improves performance by avoiding bounds calculations\n * - Useful for containers with many children\n * - Set to null to use object bounds\n * @default null\n */\n cullArea: Rectangle;\n\n /**\n * Controls whether this object should be culled when out of view.\n * When true, the object will not be rendered if its bounds are outside the visible area.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n *\n * // Enable culling\n * sprite.cullable = true;\n *\n * // Force object to always render\n * sprite.cullable = false;\n * ```\n * @remarks\n * - Does not affect transform updates\n * - Applies to this object only\n * - Children follow their own cullable setting\n * @default false\n */\n cullable: boolean;\n\n /**\n * Controls whether children of this container can be culled.\n * When false, skips recursive culling checks for better performance.\n * @example\n * ```ts\n * const container = new Container();\n *\n * // Enable container culling\n * container.cullable = true;\n *\n * // Disable child culling for performance\n * container.cullableChildren = false;\n *\n * // Children will always render if container is visible\n * container.addChild(sprite1, sprite2, sprite3);\n * ```\n * @remarks\n * - Improves performance for static scenes\n * - Useful when children are always within container bounds\n * - Parent culling still applies\n * @default true\n */\n cullableChildren: boolean;\n}\n\n/** @internal */\nexport const cullingMixin: CullingMixinConstructor = {\n cullArea: null,\n cullable: false,\n cullableChildren: true,\n};\n","/**\n * Two Pi.\n * @type {number}\n * @category maths\n * @standard\n */\nexport const PI_2 = Math.PI * 2;\n\n/**\n * Conversion factor for converting radians to degrees.\n * @type {number} RAD_TO_DEG\n * @category maths\n * @standard\n */\nexport const RAD_TO_DEG = 180 / Math.PI;\n\n/**\n * Conversion factor for converting degrees to radians.\n * @type {number}\n * @category maths\n * @standard\n */\nexport const DEG_TO_RAD = Math.PI / 180;\n\n/**\n * Constants that identify shapes, mainly to prevent `instanceof` calls.\n * @category maths\n * @advanced\n */\nexport type SHAPE_PRIMITIVE =\n | 'polygon'\n | 'rectangle'\n | 'circle'\n | 'ellipse'\n | 'triangle'\n | 'roundedRectangle';\n","/* eslint-disable @typescript-eslint/no-use-before-define */\nimport type { PointData } from './PointData';\nimport type { PointLike } from './PointLike';\n\n// eslint-disable-next-line max-len\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type, requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface Point extends PixiMixins.Point { }\n\n/**\n * The Point object represents a location in a two-dimensional coordinate system, where `x` represents\n * the position on the horizontal axis and `y` represents the position on the vertical axis.\n *\n * Many Pixi functions accept the `PointData` type as an alternative to `Point`,\n * which only requires `x` and `y` properties.\n * @example\n * ```ts\n * // Basic point creation\n * const point = new Point(100, 200);\n *\n * // Using with transformations\n * const matrix = new Matrix();\n * matrix.translate(50, 50).apply(point);\n *\n * // Point arithmetic\n * const start = new Point(0, 0);\n * const end = new Point(100, 100);\n * const middle = new Point(\n * (start.x + end.x) / 2,\n * (start.y + end.y) / 2\n * );\n * ```\n * @see {@link PointData} For basic x,y interface\n * @see {@link PointLike} For point manipulation interface\n * @see {@link ObservablePoint} For observable version\n * @category maths\n * @standard\n */\nexport class Point implements PointLike\n{\n /**\n * Position of the point on the x axis\n * @example\n * ```ts\n * // Set x position\n * const point = new Point();\n * point.x = 100;\n *\n * // Use in calculations\n * const width = rightPoint.x - leftPoint.x;\n * ```\n */\n public x = 0;\n /**\n * Position of the point on the y axis\n * @example\n * ```ts\n * // Set y position\n * const point = new Point();\n * point.y = 200;\n *\n * // Use in calculations\n * const height = bottomPoint.y - topPoint.y;\n * ```\n */\n public y = 0;\n\n /**\n * Creates a new `Point`\n * @param {number} [x=0] - position of the point on the x axis\n * @param {number} [y=0] - position of the point on the y axis\n */\n constructor(x = 0, y = 0)\n {\n this.x = x;\n this.y = y;\n }\n\n /**\n * Creates a clone of this point, which is a new instance with the same `x` and `y` values.\n * @example\n * ```ts\n * // Basic point cloning\n * const original = new Point(100, 200);\n * const copy = original.clone();\n *\n * // Clone and modify\n * const modified = original.clone();\n * modified.set(300, 400);\n *\n * // Verify independence\n * console.log(original); // Point(100, 200)\n * console.log(modified); // Point(300, 400)\n * ```\n * @remarks\n * - Creates new Point instance\n * - Deep copies x and y values\n * - Independent from original\n * - Useful for preserving values\n * @returns A clone of this point\n * @see {@link Point.copyFrom} For copying into existing point\n * @see {@link Point.copyTo} For copying to existing point\n */\n public clone(): Point\n {\n return new Point(this.x, this.y);\n }\n\n /**\n * Copies x and y from the given point into this point.\n * @example\n * ```ts\n * // Basic copying\n * const source = new Point(100, 200);\n * const target = new Point();\n * target.copyFrom(source);\n *\n * // Copy and chain operations\n * const point = new Point()\n * .copyFrom(source)\n * .set(x + 50, y + 50);\n *\n * // Copy from any PointData\n * const data = { x: 10, y: 20 };\n * point.copyFrom(data);\n * ```\n * @param p - The point to copy from\n * @returns The point instance itself\n * @see {@link Point.copyTo} For copying to another point\n * @see {@link Point.clone} For creating new point copy\n */\n public copyFrom(p: PointData): this\n {\n this.set(p.x, p.y);\n\n return this;\n }\n\n /**\n * Copies this point's x and y into the given point.\n * @example\n * ```ts\n * // Basic copying\n * const source = new Point(100, 200);\n * const target = new Point();\n * source.copyTo(target);\n * ```\n * @param p - The point to copy to. Can be any type that is or extends `PointLike`\n * @returns The point (`p`) with values updated\n * @see {@link Point.copyFrom} For copying from another point\n * @see {@link Point.clone} For creating new point copy\n */\n public copyTo(p: T): T\n {\n p.set(this.x, this.y);\n\n return p;\n }\n\n /**\n * Checks if another point is equal to this point.\n *\n * Compares x and y values using strict equality.\n * @example\n * ```ts\n * // Basic equality check\n * const p1 = new Point(100, 200);\n * const p2 = new Point(100, 200);\n * console.log(p1.equals(p2)); // true\n *\n * // Compare with PointData\n * const data = { x: 100, y: 200 };\n * console.log(p1.equals(data)); // true\n *\n * // Check different points\n * const p3 = new Point(200, 300);\n * console.log(p1.equals(p3)); // false\n * ```\n * @param p - The point to check\n * @returns `true` if both `x` and `y` are equal\n * @see {@link Point.copyFrom} For making points equal\n * @see {@link PointData} For point data interface\n */\n public equals(p: PointData): boolean\n {\n return (p.x === this.x) && (p.y === this.y);\n }\n\n /**\n * Sets the point to a new x and y position.\n *\n * If y is omitted, both x and y will be set to x.\n * @example\n * ```ts\n * // Basic position setting\n * const point = new Point();\n * point.set(100, 200);\n *\n * // Set both x and y to same value\n * point.set(50); // x=50, y=50\n *\n * // Chain with other operations\n * point\n * .set(10, 20)\n * .copyTo(otherPoint);\n * ```\n * @param x - Position on the x axis\n * @param y - Position on the y axis, defaults to x\n * @returns The point instance itself\n * @see {@link Point.copyFrom} For copying from another point\n * @see {@link Point.equals} For comparing positions\n */\n public set(x = 0, y: number = x): this\n {\n this.x = x;\n this.y = y;\n\n return this;\n }\n\n // #if _DEBUG\n public toString(): string\n {\n return `[pixi.js/math:Point x=${this.x} y=${this.y}]`;\n }\n // #endif\n\n /**\n * A static Point object with `x` and `y` values of `0`.\n *\n * This shared instance is reset to zero values when accessed.\n *\n * > [!IMPORTANT] This point is shared and temporary. Do not store references to it.\n * @example\n * ```ts\n * // Use for temporary calculations\n * const tempPoint = Point.shared;\n * tempPoint.set(100, 200);\n * matrix.apply(tempPoint);\n *\n * // Will be reset to (0,0) on next access\n * const fresh = Point.shared; // x=0, y=0\n * ```\n * @readonly\n * @returns A fresh zeroed point for temporary use\n * @see {@link Point.constructor} For creating new points\n * @see {@link PointData} For basic point interface\n */\n static get shared(): Point\n {\n tempPoint.x = 0;\n tempPoint.y = 0;\n\n return tempPoint;\n }\n}\n\nconst tempPoint = new Point();\n","/* eslint-disable @typescript-eslint/no-use-before-define */\nimport { PI_2 } from '../misc/const';\nimport { Point } from '../point/Point';\n\nimport type { PointData } from '../point/PointData';\n\n/**\n * The data structure that contains the position, scale, pivot, skew and rotation of an object.\n * This is used by the {@link Matrix} class to decompose the matrix into its components.\n * @category maths\n * @advanced\n */\nexport interface TransformableObject\n{\n /** The position of the object */\n position: PointData;\n /** The scale of the object */\n scale: PointData;\n /** The pivot of the object */\n pivot: PointData;\n /** The skew of the object */\n skew: PointData;\n /** The rotation of the object */\n rotation: number;\n}\n\n/**\n * A fast matrix for 2D transformations.\n * Represents a 3x3 transformation matrix:\n *\n * ```js\n * | a c tx |\n * | b d ty |\n * | 0 0 1 |\n * ```\n * @example\n * ```ts\n * // Create identity matrix\n * const matrix = new Matrix();\n *\n * // Create matrix with custom values\n * const transform = new Matrix(2, 0, 0, 2, 100, 100); // Scale 2x, translate 100,100\n *\n * // Transform a point\n * const point = { x: 10, y: 20 };\n * const transformed = transform.apply(point);\n *\n * // Chain transformations\n * matrix\n * .translate(100, 50)\n * .rotate(Math.PI / 4)\n * .scale(2, 2);\n * ```\n * @remarks\n * - Used for transform hierarchies\n * - Supports scale, rotation, position\n * - Can be concatenated with append/prepend\n * - Efficient for batched transformations\n * @category maths\n * @standard\n */\nexport class Matrix\n{\n /**\n * Scale on the x axis.\n * @default 1\n */\n public a: number;\n\n /**\n * Shear on the y axis.\n * @default 0\n */\n public b: number;\n\n /**\n * Shear on the x axis.\n * @default 0\n */\n public c: number;\n\n /**\n * Scale on the y axis.\n * @default 1\n */\n public d: number;\n\n /**\n * Translation on the x axis.\n * @default 0\n */\n public tx: number;\n\n /**\n * Translation on the y axis.\n * @default 0\n */\n public ty: number;\n\n /**\n * Array representation of the matrix.\n * Only populated when `toArray()` is called.\n * @default null\n * @see {@link Matrix.toArray} For filling this array\n */\n public array: Float32Array | null = null;\n\n /**\n * @param a - x scale\n * @param b - y skew\n * @param c - x skew\n * @param d - y scale\n * @param tx - x translation\n * @param ty - y translation\n */\n constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0)\n {\n this.a = a;\n this.b = b;\n this.c = c;\n this.d = d;\n this.tx = tx;\n this.ty = ty;\n }\n\n /**\n * Creates a Matrix object based on the given array.\n * Populates matrix components from a flat array in column-major order.\n *\n * > [!NOTE] Array mapping order:\n * > ```\n * > array[0] = a (x scale)\n * > array[1] = b (y skew)\n * > array[2] = tx (x translation)\n * > array[3] = c (x skew)\n * > array[4] = d (y scale)\n * > array[5] = ty (y translation)\n * > ```\n * @example\n * ```ts\n * // Create matrix from array\n * const matrix = new Matrix();\n * matrix.fromArray([\n * 2, 0, 100, // a, b, tx\n * 0, 2, 100 // c, d, ty\n * ]);\n *\n * // Create matrix from typed array\n * const float32Array = new Float32Array([\n * 1, 0, 0, // Scale x1, no skew\n * 0, 1, 0 // No skew, scale x1\n * ]);\n * matrix.fromArray(float32Array);\n * ```\n * @param array - The array to populate the matrix from\n * @see {@link Matrix.toArray} For converting matrix to array\n * @see {@link Matrix.set} For setting values directly\n */\n public fromArray(array: number[]): void\n {\n this.a = array[0];\n this.b = array[1];\n this.c = array[3];\n this.d = array[4];\n this.tx = array[2];\n this.ty = array[5];\n }\n\n /**\n * Sets the matrix properties directly.\n * All matrix components can be set in one call.\n * @example\n * ```ts\n * // Set to identity matrix\n * matrix.set(1, 0, 0, 1, 0, 0);\n *\n * // Set to scale matrix\n * matrix.set(2, 0, 0, 2, 0, 0); // Scale 2x\n *\n * // Set to translation matrix\n * matrix.set(1, 0, 0, 1, 100, 50); // Move 100,50\n * ```\n * @param a - Scale on x axis\n * @param b - Shear on y axis\n * @param c - Shear on x axis\n * @param d - Scale on y axis\n * @param tx - Translation on x axis\n * @param ty - Translation on y axis\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.identity} For resetting to identity\n * @see {@link Matrix.fromArray} For setting from array\n */\n public set(a: number, b: number, c: number, d: number, tx: number, ty: number): this\n {\n this.a = a;\n this.b = b;\n this.c = c;\n this.d = d;\n this.tx = tx;\n this.ty = ty;\n\n return this;\n }\n\n /**\n * Creates an array from the current Matrix object.\n *\n * > [!NOTE] The array format is:\n * > ```\n * > Non-transposed:\n * > [a, c, tx,\n * > b, d, ty,\n * > 0, 0, 1]\n * >\n * > Transposed:\n * > [a, b, 0,\n * > c, d, 0,\n * > tx,ty,1]\n * > ```\n * @example\n * ```ts\n * // Basic array conversion\n * const matrix = new Matrix(2, 0, 0, 2, 100, 100);\n * const array = matrix.toArray();\n *\n * // Using existing array\n * const float32Array = new Float32Array(9);\n * matrix.toArray(false, float32Array);\n *\n * // Get transposed array\n * const transposed = matrix.toArray(true);\n * ```\n * @param transpose - Whether to transpose the matrix\n * @param out - Optional Float32Array to store the result\n * @returns The array containing the matrix values\n * @see {@link Matrix.fromArray} For creating matrix from array\n * @see {@link Matrix.array} For cached array storage\n */\n public toArray(transpose?: boolean, out?: Float32Array): Float32Array\n {\n if (!this.array)\n {\n this.array = new Float32Array(9);\n }\n\n const array = out || this.array;\n\n if (transpose)\n {\n array[0] = this.a;\n array[1] = this.b;\n array[2] = 0;\n array[3] = this.c;\n array[4] = this.d;\n array[5] = 0;\n array[6] = this.tx;\n array[7] = this.ty;\n array[8] = 1;\n }\n else\n {\n array[0] = this.a;\n array[1] = this.c;\n array[2] = this.tx;\n array[3] = this.b;\n array[4] = this.d;\n array[5] = this.ty;\n array[6] = 0;\n array[7] = 0;\n array[8] = 1;\n }\n\n return array;\n }\n\n /**\n * Get a new position with the current transformation applied.\n *\n * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering)\n * @example\n * ```ts\n * // Basic point transformation\n * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4);\n * const point = new Point(10, 20);\n * const transformed = matrix.apply(point);\n *\n * // Reuse existing point\n * const output = new Point();\n * matrix.apply(point, output);\n * ```\n * @param pos - The origin point to transform\n * @param newPos - Optional point to store the result\n * @returns The transformed point\n * @see {@link Matrix.applyInverse} For inverse transformation\n * @see {@link Point} For point operations\n */\n public apply

(pos: PointData, newPos?: P): P\n {\n newPos = (newPos || new Point()) as P;\n\n const x = pos.x;\n const y = pos.y;\n\n newPos.x = (this.a * x) + (this.c * y) + this.tx;\n newPos.y = (this.b * x) + (this.d * y) + this.ty;\n\n return newPos;\n }\n\n /**\n * Get a new position with the inverse of the current transformation applied.\n *\n * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input)\n * @example\n * ```ts\n * // Basic inverse transformation\n * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4);\n * const worldPoint = new Point(150, 100);\n * const localPoint = matrix.applyInverse(worldPoint);\n *\n * // Reuse existing point\n * const output = new Point();\n * matrix.applyInverse(worldPoint, output);\n *\n * // Convert mouse position to local space\n * const mousePoint = new Point(mouseX, mouseY);\n * const localMouse = matrix.applyInverse(mousePoint);\n * ```\n * @param pos - The origin point to inverse-transform\n * @param newPos - Optional point to store the result\n * @returns The inverse-transformed point\n * @see {@link Matrix.apply} For forward transformation\n * @see {@link Matrix.invert} For getting inverse matrix\n */\n public applyInverse

(pos: PointData, newPos?: P): P\n {\n newPos = (newPos || new Point()) as P;\n\n const a = this.a;\n const b = this.b;\n const c = this.c;\n const d = this.d;\n const tx = this.tx;\n const ty = this.ty;\n\n const id = 1 / ((a * d) + (c * -b));\n\n const x = pos.x;\n const y = pos.y;\n\n newPos.x = (d * id * x) + (-c * id * y) + (((ty * c) - (tx * d)) * id);\n newPos.y = (a * id * y) + (-b * id * x) + (((-ty * a) + (tx * b)) * id);\n\n return newPos;\n }\n\n /**\n * Translates the matrix on the x and y axes.\n * Adds to the position values while preserving scale, rotation and skew.\n * @example\n * ```ts\n * // Basic translation\n * const matrix = new Matrix();\n * matrix.translate(100, 50); // Move right 100, down 50\n *\n * // Chain with other transformations\n * matrix\n * .scale(2, 2)\n * .translate(100, 0)\n * .rotate(Math.PI / 4);\n * ```\n * @param x - How much to translate on the x axis\n * @param y - How much to translate on the y axis\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.set} For setting position directly\n * @see {@link Matrix.setTransform} For complete transform setup\n */\n public translate(x: number, y: number): this\n {\n this.tx += x;\n this.ty += y;\n\n return this;\n }\n\n /**\n * Applies a scale transformation to the matrix.\n * Multiplies the scale values with existing matrix components.\n * @example\n * ```ts\n * // Basic scaling\n * const matrix = new Matrix();\n * matrix.scale(2, 3); // Scale 2x horizontally, 3x vertically\n *\n * // Chain with other transformations\n * matrix\n * .translate(100, 100)\n * .scale(2, 2) // Scales after translation\n * .rotate(Math.PI / 4);\n * ```\n * @param x - The amount to scale horizontally\n * @param y - The amount to scale vertically\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.setTransform} For setting scale directly\n * @see {@link Matrix.append} For combining transformations\n */\n public scale(x: number, y: number): this\n {\n this.a *= x;\n this.d *= y;\n this.c *= x;\n this.b *= y;\n this.tx *= x;\n this.ty *= y;\n\n return this;\n }\n\n /**\n * Applies a rotation transformation to the matrix.\n *\n * Rotates around the origin (0,0) by the given angle in radians.\n * @example\n * ```ts\n * // Basic rotation\n * const matrix = new Matrix();\n * matrix.rotate(Math.PI / 4); // Rotate 45 degrees\n *\n * // Chain with other transformations\n * matrix\n * .translate(100, 100) // Move to rotation center\n * .rotate(Math.PI) // Rotate 180 degrees\n * .scale(2, 2); // Scale after rotation\n *\n * // Common angles\n * matrix.rotate(Math.PI / 2); // 90 degrees\n * matrix.rotate(Math.PI); // 180 degrees\n * matrix.rotate(Math.PI * 2); // 360 degrees\n * ```\n * @remarks\n * - Rotates around origin point (0,0)\n * - Affects position if translation was set\n * - Uses counter-clockwise rotation\n * - Order of operations matters when chaining\n * @param angle - The angle in radians\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.setTransform} For setting rotation directly\n * @see {@link Matrix.append} For combining transformations\n */\n public rotate(angle: number): this\n {\n const cos = Math.cos(angle);\n const sin = Math.sin(angle);\n\n const a1 = this.a;\n const c1 = this.c;\n const tx1 = this.tx;\n\n this.a = (a1 * cos) - (this.b * sin);\n this.b = (a1 * sin) + (this.b * cos);\n this.c = (c1 * cos) - (this.d * sin);\n this.d = (c1 * sin) + (this.d * cos);\n this.tx = (tx1 * cos) - (this.ty * sin);\n this.ty = (tx1 * sin) + (this.ty * cos);\n\n return this;\n }\n\n /**\n * Appends the given Matrix to this Matrix.\n * Combines two matrices by multiplying them together: this = this * matrix\n * @example\n * ```ts\n * // Basic matrix combination\n * const matrix = new Matrix();\n * const other = new Matrix().translate(100, 0).rotate(Math.PI / 4);\n * matrix.append(other);\n * ```\n * @remarks\n * - Order matters: A.append(B) !== B.append(A)\n * - Modifies current matrix\n * - Preserves transformation order\n * - Commonly used for combining transforms\n * @param matrix - The matrix to append\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.prepend} For prepending transformations\n * @see {@link Matrix.appendFrom} For appending two external matrices\n */\n public append(matrix: Matrix): this\n {\n const a1 = this.a;\n const b1 = this.b;\n const c1 = this.c;\n const d1 = this.d;\n\n this.a = (matrix.a * a1) + (matrix.b * c1);\n this.b = (matrix.a * b1) + (matrix.b * d1);\n this.c = (matrix.c * a1) + (matrix.d * c1);\n this.d = (matrix.c * b1) + (matrix.d * d1);\n\n this.tx = (matrix.tx * a1) + (matrix.ty * c1) + this.tx;\n this.ty = (matrix.tx * b1) + (matrix.ty * d1) + this.ty;\n\n return this;\n }\n\n /**\n * Appends two matrices and sets the result to this matrix.\n * Performs matrix multiplication: this = A * B\n * @example\n * ```ts\n * // Basic matrix multiplication\n * const result = new Matrix();\n * const matrixA = new Matrix().scale(2, 2);\n * const matrixB = new Matrix().rotate(Math.PI / 4);\n * result.appendFrom(matrixA, matrixB);\n * ```\n * @remarks\n * - Order matters: A * B !== B * A\n * - Creates a new transformation from two others\n * - More efficient than append() for multiple operations\n * - Does not modify input matrices\n * @param a - The first matrix to multiply\n * @param b - The second matrix to multiply\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.append} For single matrix combination\n * @see {@link Matrix.prepend} For reverse order multiplication\n */\n public appendFrom(a: Matrix, b: Matrix): this\n {\n const a1 = a.a;\n const b1 = a.b;\n const c1 = a.c;\n const d1 = a.d;\n const tx = a.tx;\n const ty = a.ty;\n\n const a2 = b.a;\n const b2 = b.b;\n const c2 = b.c;\n const d2 = b.d;\n\n this.a = (a1 * a2) + (b1 * c2);\n this.b = (a1 * b2) + (b1 * d2);\n this.c = (c1 * a2) + (d1 * c2);\n this.d = (c1 * b2) + (d1 * d2);\n this.tx = (tx * a2) + (ty * c2) + b.tx;\n this.ty = (tx * b2) + (ty * d2) + b.ty;\n\n return this;\n }\n\n /**\n * Sets the matrix based on all the available properties.\n * Combines position, scale, rotation, skew and pivot in a single operation.\n * @example\n * ```ts\n * // Basic transform setup\n * const matrix = new Matrix();\n * matrix.setTransform(\n * 100, 100, // position\n * 0, 0, // pivot\n * 2, 2, // scale\n * Math.PI / 4, // rotation (45 degrees)\n * 0, 0 // skew\n * );\n * ```\n * @remarks\n * - Updates all matrix components at once\n * - More efficient than separate transform calls\n * - Uses radians for rotation and skew\n * - Pivot affects rotation center\n * @param x - Position on the x axis\n * @param y - Position on the y axis\n * @param pivotX - Pivot on the x axis\n * @param pivotY - Pivot on the y axis\n * @param scaleX - Scale on the x axis\n * @param scaleY - Scale on the y axis\n * @param rotation - Rotation in radians\n * @param skewX - Skew on the x axis\n * @param skewY - Skew on the y axis\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.decompose} For extracting transform properties\n * @see {@link TransformableObject} For transform data structure\n */\n public setTransform(x: number, y: number, pivotX: number, pivotY: number, scaleX: number,\n scaleY: number, rotation: number, skewX: number, skewY: number): this\n {\n this.a = Math.cos(rotation + skewY) * scaleX;\n this.b = Math.sin(rotation + skewY) * scaleX;\n this.c = -Math.sin(rotation - skewX) * scaleY;\n this.d = Math.cos(rotation - skewX) * scaleY;\n\n this.tx = x - ((pivotX * this.a) + (pivotY * this.c));\n this.ty = y - ((pivotX * this.b) + (pivotY * this.d));\n\n return this;\n }\n\n /**\n * Prepends the given Matrix to this Matrix.\n * Combines two matrices by multiplying them together: this = matrix * this\n * @example\n * ```ts\n * // Basic matrix prepend\n * const matrix = new Matrix().scale(2, 2);\n * const other = new Matrix().translate(100, 0);\n * matrix.prepend(other); // Translation happens before scaling\n * ```\n * @remarks\n * - Order matters: A.prepend(B) !== B.prepend(A)\n * - Modifies current matrix\n * - Reverses transformation order compared to append()\n * @param matrix - The matrix to prepend\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.append} For appending transformations\n * @see {@link Matrix.appendFrom} For combining external matrices\n */\n public prepend(matrix: Matrix): this\n {\n const tx1 = this.tx;\n\n if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1)\n {\n const a1 = this.a;\n const c1 = this.c;\n\n this.a = (a1 * matrix.a) + (this.b * matrix.c);\n this.b = (a1 * matrix.b) + (this.b * matrix.d);\n this.c = (c1 * matrix.a) + (this.d * matrix.c);\n this.d = (c1 * matrix.b) + (this.d * matrix.d);\n }\n\n this.tx = (tx1 * matrix.a) + (this.ty * matrix.c) + matrix.tx;\n this.ty = (tx1 * matrix.b) + (this.ty * matrix.d) + matrix.ty;\n\n return this;\n }\n\n /**\n * Decomposes the matrix into its individual transform components.\n * Extracts position, scale, rotation and skew values from the matrix.\n * @example\n * ```ts\n * // Basic decomposition\n * const matrix = new Matrix()\n * .translate(100, 100)\n * .rotate(Math.PI / 4)\n * .scale(2, 2);\n *\n * const transform = {\n * position: new Point(),\n * scale: new Point(),\n * pivot: new Point(),\n * skew: new Point(),\n * rotation: 0\n * };\n *\n * matrix.decompose(transform);\n * console.log(transform.position); // Point(100, 100)\n * console.log(transform.rotation); // ~0.785 (PI/4)\n * console.log(transform.scale); // Point(2, 2)\n * ```\n * @remarks\n * - Handles combined transformations\n * - Accounts for pivot points\n * - Chooses between rotation/skew based on transform type\n * - Uses radians for rotation and skew\n * @param transform - The transform object to store the decomposed values\n * @returns The transform with the newly applied properties\n * @see {@link Matrix.setTransform} For composing from components\n * @see {@link TransformableObject} For transform structure\n */\n public decompose(transform: TransformableObject): TransformableObject\n {\n // sort out rotation / skew..\n const a = this.a;\n const b = this.b;\n const c = this.c;\n const d = this.d;\n const pivot = transform.pivot;\n\n const skewX = -Math.atan2(-c, d);\n const skewY = Math.atan2(b, a);\n\n const delta = Math.abs(skewX + skewY);\n\n if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001)\n {\n transform.rotation = skewY;\n transform.skew.x = transform.skew.y = 0;\n }\n else\n {\n transform.rotation = 0;\n transform.skew.x = skewX;\n transform.skew.y = skewY;\n }\n\n // next set scale\n transform.scale.x = Math.sqrt((a * a) + (b * b));\n transform.scale.y = Math.sqrt((c * c) + (d * d));\n\n // next set position\n transform.position.x = this.tx + ((pivot.x * a) + (pivot.y * c));\n transform.position.y = this.ty + ((pivot.x * b) + (pivot.y * d));\n\n return transform;\n }\n\n /**\n * Inverts this matrix.\n * Creates the matrix that when multiplied with this matrix results in an identity matrix.\n * @example\n * ```ts\n * // Basic matrix inversion\n * const matrix = new Matrix()\n * .translate(100, 50)\n * .scale(2, 2);\n *\n * matrix.invert(); // Now transforms in opposite direction\n *\n * // Verify inversion\n * const point = new Point(50, 50);\n * const transformed = matrix.apply(point);\n * const original = matrix.invert().apply(transformed);\n * // original ≈ point\n * ```\n * @remarks\n * - Modifies the current matrix\n * - Useful for reversing transformations\n * - Cannot invert matrices with zero determinant\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.identity} For resetting to identity\n * @see {@link Matrix.applyInverse} For inverse transformations\n */\n public invert(): this\n {\n const a1 = this.a;\n const b1 = this.b;\n const c1 = this.c;\n const d1 = this.d;\n const tx1 = this.tx;\n const n = (a1 * d1) - (b1 * c1);\n\n this.a = d1 / n;\n this.b = -b1 / n;\n this.c = -c1 / n;\n this.d = a1 / n;\n this.tx = ((c1 * this.ty) - (d1 * tx1)) / n;\n this.ty = -((a1 * this.ty) - (b1 * tx1)) / n;\n\n return this;\n }\n\n /**\n * Checks if this matrix is an identity matrix.\n *\n * An identity matrix has no transformations applied (default state).\n * @example\n * ```ts\n * // Check if matrix is identity\n * const matrix = new Matrix();\n * console.log(matrix.isIdentity()); // true\n *\n * // Check after transformations\n * matrix.translate(100, 0);\n * console.log(matrix.isIdentity()); // false\n *\n * // Reset and verify\n * matrix.identity();\n * console.log(matrix.isIdentity()); // true\n * ```\n * @remarks\n * - Verifies a = 1, d = 1 (no scale)\n * - Verifies b = 0, c = 0 (no skew)\n * - Verifies tx = 0, ty = 0 (no translation)\n * @returns True if matrix has no transformations\n * @see {@link Matrix.identity} For resetting to identity\n * @see {@link Matrix.IDENTITY} For constant identity matrix\n */\n public isIdentity(): boolean\n {\n return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.tx === 0 && this.ty === 0;\n }\n\n /**\n * Resets this Matrix to an identity (default) matrix.\n * Sets all components to their default values: scale=1, no skew, no translation.\n * @example\n * ```ts\n * // Reset transformed matrix\n * const matrix = new Matrix()\n * .scale(2, 2)\n * .rotate(Math.PI / 4);\n * matrix.identity(); // Back to default state\n *\n * // Chain after reset\n * matrix\n * .identity()\n * .translate(100, 100)\n * .scale(2, 2);\n *\n * // Compare with identity constant\n * const isDefault = matrix.equals(Matrix.IDENTITY);\n * ```\n * @remarks\n * - Sets a=1, d=1 (default scale)\n * - Sets b=0, c=0 (no skew)\n * - Sets tx=0, ty=0 (no translation)\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.IDENTITY} For constant identity matrix\n * @see {@link Matrix.isIdentity} For checking identity state\n */\n public identity(): this\n {\n this.a = 1;\n this.b = 0;\n this.c = 0;\n this.d = 1;\n this.tx = 0;\n this.ty = 0;\n\n return this;\n }\n\n /**\n * Creates a new Matrix object with the same values as this one.\n * @returns A copy of this matrix. Good for chaining method calls.\n */\n public clone(): Matrix\n {\n const matrix = new Matrix();\n\n matrix.a = this.a;\n matrix.b = this.b;\n matrix.c = this.c;\n matrix.d = this.d;\n matrix.tx = this.tx;\n matrix.ty = this.ty;\n\n return matrix;\n }\n\n /**\n * Creates a new Matrix object with the same values as this one.\n * @param matrix\n * @example\n * ```ts\n * // Basic matrix cloning\n * const matrix = new Matrix()\n * .translate(100, 100)\n * .rotate(Math.PI / 4);\n * const copy = matrix.clone();\n *\n * // Clone and modify\n * const modified = matrix.clone()\n * .scale(2, 2);\n *\n * // Compare matrices\n * console.log(matrix.equals(copy)); // true\n * console.log(matrix.equals(modified)); // false\n * ```\n * @returns A copy of this matrix. Good for chaining method calls.\n * @see {@link Matrix.copyTo} For copying to existing matrix\n * @see {@link Matrix.copyFrom} For copying from another matrix\n */\n public copyTo(matrix: Matrix): Matrix\n {\n matrix.a = this.a;\n matrix.b = this.b;\n matrix.c = this.c;\n matrix.d = this.d;\n matrix.tx = this.tx;\n matrix.ty = this.ty;\n\n return matrix;\n }\n\n /**\n * Changes the values of the matrix to be the same as the ones in given matrix.\n * @example\n * ```ts\n * // Basic matrix copying\n * const source = new Matrix()\n * .translate(100, 100)\n * .rotate(Math.PI / 4);\n * const target = new Matrix();\n * target.copyFrom(source);\n * ```\n * @param matrix - The matrix to copy from\n * @returns This matrix. Good for chaining method calls.\n * @see {@link Matrix.clone} For creating new matrix copy\n * @see {@link Matrix.copyTo} For copying to another matrix\n */\n public copyFrom(matrix: Matrix): this\n {\n this.a = matrix.a;\n this.b = matrix.b;\n this.c = matrix.c;\n this.d = matrix.d;\n this.tx = matrix.tx;\n this.ty = matrix.ty;\n\n return this;\n }\n\n /**\n * Checks if this matrix equals another matrix.\n * Compares all components for exact equality.\n * @example\n * ```ts\n * // Basic equality check\n * const m1 = new Matrix();\n * const m2 = new Matrix();\n * console.log(m1.equals(m2)); // true\n *\n * // Compare transformed matrices\n * const transform = new Matrix()\n * .translate(100, 100)\n * const clone = new Matrix()\n * .scale(2, 2);\n * console.log(transform.equals(clone)); // false\n * ```\n * @param matrix - The matrix to compare to\n * @returns True if matrices are identical\n * @see {@link Matrix.copyFrom} For copying matrix values\n * @see {@link Matrix.isIdentity} For identity comparison\n */\n public equals(matrix: Matrix)\n {\n return matrix.a === this.a && matrix.b === this.b\n && matrix.c === this.c && matrix.d === this.d\n && matrix.tx === this.tx && matrix.ty === this.ty;\n }\n\n // #if _DEBUG\n public toString(): string\n {\n return `[pixi.js:Matrix a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`;\n }\n // #endif\n\n /**\n * A default (identity) matrix with no transformations applied.\n *\n * > [!IMPORTANT] This is a shared read-only object. Create a new Matrix if you need to modify it.\n * @example\n * ```ts\n * // Get identity matrix reference\n * const identity = Matrix.IDENTITY;\n * console.log(identity.isIdentity()); // true\n *\n * // Compare with identity\n * const matrix = new Matrix();\n * console.log(matrix.equals(Matrix.IDENTITY)); // true\n *\n * // Create new matrix instead of modifying IDENTITY\n * const transform = new Matrix()\n * .copyFrom(Matrix.IDENTITY)\n * .translate(100, 100);\n * ```\n * @readonly\n * @returns A read-only identity matrix\n * @see {@link Matrix.shared} For temporary calculations\n * @see {@link Matrix.identity} For resetting matrices\n */\n static get IDENTITY(): Readonly\n {\n return identityMatrix.identity();\n }\n\n /**\n * A static Matrix that can be used to avoid creating new objects.\n * Will always ensure the matrix is reset to identity when requested.\n *\n * > [!IMPORTANT] This matrix is shared and temporary. Do not store references to it.\n * @example\n * ```ts\n * // Use for temporary calculations\n * const tempMatrix = Matrix.shared;\n * tempMatrix.translate(100, 100).rotate(Math.PI / 4);\n * const point = tempMatrix.apply({ x: 10, y: 20 });\n *\n * // Will be reset to identity on next access\n * const fresh = Matrix.shared; // Back to identity\n * ```\n * @remarks\n * - Always returns identity matrix\n * - Safe to modify temporarily\n * - Not safe to store references\n * - Useful for one-off calculations\n * @readonly\n * @returns A fresh identity matrix for temporary use\n * @see {@link Matrix.IDENTITY} For immutable identity matrix\n * @see {@link Matrix.identity} For resetting matrices\n */\n static get shared(): Matrix\n {\n return tempMatrix.identity();\n }\n}\n\nconst tempMatrix = new Matrix();\nconst identityMatrix = new Matrix();\n","import type { PointData } from './PointData';\nimport type { PointLike } from './PointLike';\n\n// eslint-disable-next-line max-len\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type, requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface ObservablePoint extends PixiMixins.ObservablePoint { }\n\n/**\n * Observer used to listen for observable point changes.\n * Provides callback mechanism for point value updates.\n * @example\n * ```ts\n * // Basic observer implementation\n * const observer: Observer = {\n * _onUpdate: (point) => {\n * console.log(`Point updated to (${point.x}, ${point.y})`);\n * }\n * };\n *\n * // Create observable point with observer\n * const point = new ObservablePoint(observer, 100, 100);\n *\n * // Observer will be notified on changes\n * point.x = 200; // Logs: Point updated to (200, 100)\n * ```\n * @remarks\n * - Used internally by ObservablePoint\n * - Triggered on x/y changes\n * - Can track multiple points\n * - Useful for change detection\n * @typeParam T - The type of point being observed\n * @see {@link ObservablePoint} The observable point class\n * @see {@link PointLike} For point interface\n * @category maths\n * @standard\n */\nexport interface Observer\n{\n /**\n * Callback to call when the point has updated.\n * Triggered whenever x or y coordinates change.\n * @param point - The point that was updated\n */\n _onUpdate: (point?: T) => void;\n}\n\n/**\n * The ObservablePoint object represents a location in a two-dimensional coordinate system.\n * Triggers a callback when its position changes.\n *\n * The x and y properties represent the position on the horizontal and vertical axes, respectively.\n * @example\n * ```ts\n * // Basic observable point usage\n * const point = new ObservablePoint(\n * { _onUpdate: (p) => console.log(`Updated to (${p.x}, ${p.y})`) },\n * 100, 100\n * );\n *\n * // Update triggers callback\n * point.x = 200; // Logs: Updated to (200, 100)\n * point.y = 300; // Logs: Updated to (200, 300)\n *\n * // Set both coordinates\n * point.set(50, 50); // Logs: Updated to (50, 50)\n * ```\n * @see {@link Point} For non-observable version\n * @see {@link Observer} For observer interface\n * @see {@link PointLike} For point interface\n * @category maths\n * @standard\n */\nexport class ObservablePoint implements PointLike\n{\n /** @ignore */\n public _x: number;\n /** @ignore */\n public _y: number;\n\n /** This object used to call the `onUpdate` callback when the point changes. */\n private readonly _observer: Observer;\n\n /**\n * Creates a new `ObservablePoint`\n * @param observer - Observer to pass to listen for change events.\n * @param {number} [x=0] - position of the point on the x axis\n * @param {number} [y=0] - position of the point on the y axis\n */\n constructor(observer: Observer, x?: number, y?: number)\n {\n this._x = x || 0;\n this._y = y || 0;\n\n this._observer = observer;\n }\n\n /**\n * Creates a clone of this point.\n * @example\n * ```ts\n * // Basic cloning\n * const point = new ObservablePoint(observer, 100, 200);\n * const copy = point.clone();\n *\n * // Clone with new observer\n * const newObserver = {\n * _onUpdate: (p) => console.log(`Clone updated: (${p.x}, ${p.y})`)\n * };\n * const watched = point.clone(newObserver);\n *\n * // Verify independence\n * watched.set(300, 400); // Only triggers new observer\n * ```\n * @param observer - Optional observer to pass to the new observable point\n * @returns A copy of this observable point\n * @see {@link ObservablePoint.copyFrom} For copying into existing point\n * @see {@link Observer} For observer interface details\n */\n public clone(observer?: Observer): ObservablePoint\n {\n return new ObservablePoint(observer ?? this._observer, this._x, this._y);\n }\n\n /**\n * Sets the point to a new x and y position.\n *\n * If y is omitted, both x and y will be set to x.\n * @example\n * ```ts\n * // Basic position setting\n * const point = new ObservablePoint(observer);\n * point.set(100, 200);\n *\n * // Set both x and y to same value\n * point.set(50); // x=50, y=50\n * ```\n * @param x - Position on the x axis\n * @param y - Position on the y axis, defaults to x\n * @returns The point instance itself\n * @see {@link ObservablePoint.copyFrom} For copying from another point\n * @see {@link ObservablePoint.equals} For comparing positions\n */\n public set(x = 0, y = x): this\n {\n if (this._x !== x || this._y !== y)\n {\n this._x = x;\n this._y = y;\n this._observer._onUpdate(this);\n }\n\n return this;\n }\n\n /**\n * Copies x and y from the given point into this point.\n * @example\n * ```ts\n * // Basic copying\n * const source = new ObservablePoint(observer, 100, 200);\n * const target = new ObservablePoint();\n * target.copyFrom(source);\n *\n * // Copy and chain operations\n * const point = new ObservablePoint()\n * .copyFrom(source)\n * .set(x + 50, y + 50);\n *\n * // Copy from any PointData\n * const data = { x: 10, y: 20 };\n * point.copyFrom(data);\n * ```\n * @param p - The point to copy from\n * @returns The point instance itself\n * @see {@link ObservablePoint.copyTo} For copying to another point\n * @see {@link ObservablePoint.clone} For creating new point copy\n */\n public copyFrom(p: PointData): this\n {\n if (this._x !== p.x || this._y !== p.y)\n {\n this._x = p.x;\n this._y = p.y;\n this._observer._onUpdate(this);\n }\n\n return this;\n }\n\n /**\n * Copies this point's x and y into the given point.\n * @example\n * ```ts\n * // Basic copying\n * const source = new ObservablePoint(100, 200);\n * const target = new ObservablePoint();\n * source.copyTo(target);\n * ```\n * @param p - The point to copy to. Can be any type that is or extends `PointLike`\n * @returns The point (`p`) with values updated\n * @see {@link ObservablePoint.copyFrom} For copying from another point\n * @see {@link ObservablePoint.clone} For creating new point copy\n */\n public copyTo(p: T): T\n {\n p.set(this._x, this._y);\n\n return p;\n }\n\n /**\n * Checks if another point is equal to this point.\n *\n * Compares x and y values using strict equality.\n * @example\n * ```ts\n * // Basic equality check\n * const p1 = new ObservablePoint(100, 200);\n * const p2 = new ObservablePoint(100, 200);\n * console.log(p1.equals(p2)); // true\n *\n * // Compare with PointData\n * const data = { x: 100, y: 200 };\n * console.log(p1.equals(data)); // true\n *\n * // Check different points\n * const p3 = new ObservablePoint(200, 300);\n * console.log(p1.equals(p3)); // false\n * ```\n * @param p - The point to check\n * @returns `true` if both `x` and `y` are equal\n * @see {@link ObservablePoint.copyFrom} For making points equal\n * @see {@link PointData} For point data interface\n */\n public equals(p: PointData): boolean\n {\n return (p.x === this._x) && (p.y === this._y);\n }\n\n // #if _DEBUG\n public toString(): string\n {\n return `[pixi.js/math:ObservablePoint x=${this._x} y=${this._y} scope=${this._observer}]`;\n }\n // #endif\n\n /**\n * Position of the observable point on the x axis.\n * Triggers observer callback when value changes.\n * @example\n * ```ts\n * // Basic x position\n * const point = new ObservablePoint(observer);\n * point.x = 100; // Triggers observer\n *\n * // Use in calculations\n * const width = rightPoint.x - leftPoint.x;\n * ```\n * @default 0\n */\n get x(): number\n {\n return this._x;\n }\n\n set x(value: number)\n {\n if (this._x !== value)\n {\n this._x = value;\n this._observer._onUpdate(this);\n }\n }\n\n /**\n * Position of the observable point on the y axis.\n * Triggers observer callback when value changes.\n * @example\n * ```ts\n * // Basic y position\n * const point = new ObservablePoint(observer);\n * point.y = 200; // Triggers observer\n *\n * // Use in calculations\n * const height = bottomPoint.y - topPoint.y;\n * ```\n * @default 0\n */\n get y(): number\n {\n return this._y;\n }\n\n set y(value: number)\n {\n if (this._y !== value)\n {\n this._y = value;\n this._observer._onUpdate(this);\n }\n }\n}\n","const uidCache: Record = {\n default: -1,\n};\n\n/**\n * The names of the unique identifiers. These are used to create unique identifiers for different types of objects.\n * @category utils\n * @internal\n */\nexport type UIDNames =\n | 'default'\n | 'resource'\n | 'texture'\n | 'textureSource'\n | 'textureResource'\n | 'batcher' //\n | 'graphicsContext' //\n | 'graphicsView' //\n | 'graphicsPath' //\n | 'fillGradient' //\n | 'fillPattern' //\n | 'meshView' //\n | 'renderable' //\n | 'buffer' //\n | 'bufferResource' //\n | 'geometry'\n | 'instructionSet' //\n | 'renderTarget' //\n | 'uniform' //\n | 'spriteView' //\n | 'textView' //\n | 'tilingSpriteView' //\n | 'shader' //\n | 'renderer';\n\n/**\n * Gets the next unique identifier\n * @param name - The name of the identifier.\n * @returns {number} The next unique identifier to use.\n * @category utils\n * @internal\n */\nexport function uid(name: UIDNames = 'default'): number\n{\n if (uidCache[name] === undefined)\n {\n uidCache[name] = -1;\n }\n\n return ++uidCache[name];\n}\n\n/**\n * Resets the next unique identifier to 0. This is used for some tests, dont touch or things WILL explode :)\n * @internal\n */\nexport function resetUids(): void\n{\n for (const key in uidCache)\n {\n delete uidCache[key];\n }\n}\n","import type { Dict } from '../types';\n\n// A map of warning messages already fired\nconst warnings: Dict = {};\n\n/**\n * deprecation name for version 8.0.0\n * @ignore\n * @internal\n */\nexport const v8_0_0 = '8.0.0';\n/**\n * deprecation name for version 8.1.0\n * @ignore\n * @internal\n */\nexport const v8_3_4 = '8.3.4';\n\n/**\n * Helper for warning developers about deprecated features & settings.\n * A stack track for warnings is given; useful for tracking-down where\n * deprecated methods/properties/classes are being used within the code.\n * @category utils\n * @ignore\n * @function deprecation\n * @param {string} version - The version where the feature became deprecated\n * @param {string} message - Message should include what is deprecated, where, and the new solution\n * @param {number} [ignoreDepth=3] - The number of steps to ignore at the top of the error stack\n * this is mostly to ignore internal deprecation calls.\n */\nexport function deprecation(version: string, message: string, ignoreDepth = 3): void\n{\n // Ignore duplicate\n if (warnings[message])\n {\n return;\n }\n\n /* eslint-disable no-console */\n let stack = new Error().stack;\n\n // Handle IE < 10 and Safari < 6\n if (typeof stack === 'undefined')\n {\n console.warn('PixiJS Deprecation Warning: ', `${message}\\nDeprecated since v${version}`);\n }\n else\n {\n // chop off the stack trace which includes PixiJS internal calls\n stack = stack.split('\\n').splice(ignoreDepth).join('\\n');\n\n if (console.groupCollapsed)\n {\n console.groupCollapsed(\n '%cPixiJS Deprecation Warning: %c%s',\n 'color:#614108;background:#fffbe6',\n 'font-weight:normal;color:#614108;background:#fffbe6',\n `${message}\\nDeprecated since v${version}`\n );\n console.warn(stack);\n console.groupEnd();\n }\n else\n {\n console.warn('PixiJS Deprecation Warning: ', `${message}\\nDeprecated since v${version}`);\n console.warn(stack);\n }\n }\n /* eslint-enable no-console */\n\n warnings[message] = true;\n}\n","let warnCount = 0;\nconst maxWarnings = 500;\n\n/**\n * Logs a PixiJS warning message to the console. Stops logging after 500 warnings have been logged.\n * @param args - The warning message(s) to log\n * @returns {void}\n * @category utils\n * @ignore\n */\nexport function warn(...args: any[])\n{\n if (warnCount === maxWarnings) return;\n\n warnCount++;\n\n if (warnCount === maxWarnings)\n {\n console.warn('PixiJS Warning: too many warnings, no more warnings will be reported to the console by PixiJS.');\n }\n else\n {\n console.warn('PixiJS Warning: ', ...args);\n }\n}\n","/**\n * A generic class for managing a pool of items.\n * @template T The type of items in the pool. Must implement {@link PoolItem}.\n * @category utils\n * @advanced\n */\nexport class Pool\n{\n /** @internal */\n public readonly _classType: PoolItemConstructor;\n private readonly _pool: T[] = [];\n private _count = 0;\n private _index = 0;\n\n /**\n * Constructs a new Pool.\n * @param ClassType - The constructor of the items in the pool.\n * @param {number} [initialSize] - The initial size of the pool.\n */\n constructor(ClassType: PoolItemConstructor, initialSize?: number)\n {\n this._classType = ClassType;\n\n if (initialSize)\n {\n this.prepopulate(initialSize);\n }\n }\n\n /**\n * Prepopulates the pool with a given number of items.\n * @param total - The number of items to add to the pool.\n */\n public prepopulate(total: number): void\n {\n for (let i = 0; i < total; i++)\n {\n this._pool[this._index++] = new this._classType();\n }\n\n this._count += total;\n }\n\n /**\n * Gets an item from the pool. Calls the item's `init` method if it exists.\n * If there are no items left in the pool, a new one will be created.\n * @param {unknown} [data] - Optional data to pass to the item's constructor.\n * @returns {T} The item from the pool.\n */\n public get(data?: unknown): T\n {\n let item;\n\n if (this._index > 0)\n {\n item = this._pool[--this._index];\n }\n else\n {\n item = new this._classType();\n }\n\n item.init?.(data);\n\n return item;\n }\n\n /**\n * Returns an item to the pool. Calls the item's `reset` method if it exists.\n * @param {T} item - The item to return to the pool.\n */\n public return(item: T): void\n {\n item.reset?.();\n\n this._pool[this._index++] = item;\n }\n\n /**\n * Gets the number of items in the pool.\n * @readonly\n */\n get totalSize(): number\n {\n return this._count;\n }\n\n /**\n * Gets the number of items in the pool that are free to use without needing to create more.\n * @readonly\n */\n get totalFree(): number\n {\n return this._index;\n }\n\n /**\n * Gets the number of items in the pool that are currently in use.\n * @readonly\n */\n get totalUsed(): number\n {\n return this._count - this._index;\n }\n\n /** clears the pool - mainly used for debugging! */\n public clear()\n {\n this._pool.length = 0;\n this._index = 0;\n }\n}\n\n/**\n * An object that can be stored in a {@link Pool}.\n * @category utils\n * @advanced\n */\nexport type PoolItem = {\n init?: (data?: any) => void;\n reset?: () => void;\n [key: string]: any;\n};\n\n/**\n * The constructor of an object that can be stored in a {@link Pool}.\n * @typeParam K - The type of the object that can be stored in a {@link Pool}.\n * @category utils\n * @advanced\n */\nexport type PoolItemConstructor = new () => K;\n","import { Pool } from './Pool';\n\nimport type { PoolItem, PoolItemConstructor } from './Pool';\n\n/**\n * A type alias for a constructor of a Pool.\n * @template T The type of items in the pool. Must extend PoolItem.\n * @category utils\n * @advanced\n */\nexport type PoolConstructor = new () => Pool;\n\n/**\n * A group of pools that can be used to store objects of different types.\n * @category utils\n * @advanced\n */\nexport class PoolGroupClass\n{\n /**\n * A map to store the pools by their class type.\n * @private\n */\n private readonly _poolsByClass: Map, Pool> = new Map();\n\n /**\n * Prepopulates a specific pool with a given number of items.\n * @template T The type of items in the pool. Must extend PoolItem.\n * @param {PoolItemConstructor} Class - The constructor of the items in the pool.\n * @param {number} total - The number of items to add to the pool.\n */\n public prepopulate(Class: PoolItemConstructor, total: number): void\n {\n const classPool = this.getPool(Class);\n\n classPool.prepopulate(total);\n }\n\n /**\n * Gets an item from a specific pool.\n * @template T The type of items in the pool. Must extend PoolItem.\n * @param {PoolItemConstructor} Class - The constructor of the items in the pool.\n * @param {unknown} [data] - Optional data to pass to the item's constructor.\n * @returns {T} The item from the pool.\n */\n public get(Class: PoolItemConstructor, data?: unknown): T\n {\n const pool = this.getPool(Class);\n\n return pool.get(data) as T;\n }\n\n /**\n * Returns an item to its respective pool.\n * @param {PoolItem} item - The item to return to the pool.\n */\n public return(item: PoolItem): void\n {\n const pool = this.getPool(item.constructor as PoolItemConstructor);\n\n pool.return(item);\n }\n\n /**\n * Gets a specific pool based on the class type.\n * @template T The type of items in the pool. Must extend PoolItem.\n * @param {PoolItemConstructor} ClassType - The constructor of the items in the pool.\n * @returns {Pool} The pool of the given class type.\n */\n public getPool(ClassType: PoolItemConstructor): Pool\n {\n if (!this._poolsByClass.has(ClassType))\n {\n this._poolsByClass.set(ClassType, new Pool(ClassType));\n }\n\n return this._poolsByClass.get(ClassType) as Pool;\n }\n\n /** gets the usage stats of each pool in the system */\n public stats(): Record\n {\n const stats = {} as Record;\n\n this._poolsByClass.forEach((pool) =>\n {\n // TODO: maybe we should allow the name to be set when `createEntity` is called\n const name = stats[pool._classType.name]\n ? pool._classType.name + (pool._classType as any).ID : pool._classType.name;\n\n stats[name] = {\n free: pool.totalFree,\n used: pool.totalUsed,\n size: pool.totalSize,\n };\n });\n\n return stats;\n }\n}\n\n/**\n * A singleton instance of the PoolGroupClass that can be used throughout the application.\n * @internal\n */\nexport const BigPool = new PoolGroupClass();\n","import { deprecation } from '../../../utils/logging/deprecation';\n\nimport type { Container } from '../Container';\nimport type { CacheAsTextureOptions } from '../RenderGroup';\n\n/** @ignore */\nexport interface CacheAsTextureMixinConstructor\n{\n cacheAsTexture?: (val: boolean | CacheAsTextureOptions) => void;\n}\n\n/**\n * The CacheAsTextureMixin interface provides methods and properties for caching a container as a texture.\n * This can improve rendering performance for complex static containers by allowing them to be rendered as a single texture.\n * It includes methods to enable or disable caching, update the cached texture, and check\n * 1if the container is currently cached.\n * @category scene\n * @advanced\n */\nexport interface CacheAsTextureMixin extends Required\n{\n /**\n * Caches this container as a texture. This allows the container to be rendered as a single texture,\n * which can improve performance for complex static containers.\n * @example\n * ```ts\n * // Basic caching\n * container.cacheAsTexture(true);\n *\n * // With custom options\n * container.cacheAsTexture({\n * resolution: 2,\n * antialias: true,\n * });\n *\n * // Disable caching\n * container.cacheAsTexture(false);\n *\n * // Cache a complex UI\n * const ui = new Container();\n * // Add multiple children...\n * ui.cacheAsTexture(true);\n * ui.updateCacheTexture(); // Update if contents change\n * ```\n * @param val - If true, enables caching with default options.\n * If false, disables caching.\n * Can also pass options object to configure caching behavior.\n * @see {@link Container#updateCacheTexture} For updating cached content\n * @see {@link Container#isCachedAsTexture} For checking cache state\n */\n cacheAsTexture: (val: boolean | CacheAsTextureOptions) => void;\n\n /**\n * Updates the cached texture of this container. This will flag the container's cached texture\n * to be redrawn on the next render.\n * @example\n * ```ts\n * // Basic update after changes\n * container.updateCacheTexture();\n * ```\n */\n updateCacheTexture: () => void;\n\n /**\n * Legacy property for backwards compatibility with PixiJS v7 and below.\n * Use `cacheAsTexture` instead.\n * @deprecated since 8.0.0\n */\n cacheAsBitmap: boolean;\n\n /**\n * Whether this container is currently cached as a texture.\n * @example\n * ```ts\n * // Check cache state\n * if (container.isCachedAsTexture) {\n * console.log('Container is cached');\n * }\n * ```\n * @readonly\n * @see {@link Container#cacheAsTexture} For enabling caching\n * @see {@link Container#updateCacheTexture} For updating cache\n */\n readonly isCachedAsTexture: boolean;\n}\n\n/** @internal */\nexport const cacheAsTextureMixin: Partial = {\n get isCachedAsTexture(): boolean\n {\n return !!this.renderGroup?.isCachedAsTexture;\n },\n\n cacheAsTexture(val: boolean | CacheAsTextureOptions): void\n {\n if (typeof val === 'boolean' && val === false)\n {\n this.disableRenderGroup();\n }\n else\n {\n this.enableRenderGroup();\n this.renderGroup.enableCacheAsTexture(val === true ? {} : val);\n }\n },\n\n updateCacheTexture(): void\n {\n this.renderGroup?.updateCacheTexture();\n },\n\n get cacheAsBitmap(): boolean\n {\n return this.isCachedAsTexture;\n },\n\n set cacheAsBitmap(val: boolean)\n {\n // #if _DEBUG\n deprecation('v8.6.0', 'cacheAsBitmap is deprecated, use cacheAsTexture instead.');\n // #endif\n this.cacheAsTexture(val);\n },\n} as Container;\n","/**\n * Remove items from a javascript array without generating garbage\n * @function removeItems\n * @category utils\n * @internal\n * @param {Array} arr - Array to remove elements from\n * @param {number} startIdx - starting index\n * @param {number} removeCount - how many to remove\n */\nexport function removeItems(arr: any[], startIdx: number, removeCount: number): void\n{\n const length = arr.length;\n let i;\n\n if (startIdx >= length || removeCount === 0)\n {\n return;\n }\n\n removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);\n\n const len = length - removeCount;\n\n for (i = startIdx; i < len; ++i)\n {\n arr[i] = arr[i + removeCount];\n }\n\n arr.length = len;\n}\n","import { removeItems } from '../../../utils/data/removeItems';\nimport { deprecation, v8_0_0 } from '../../../utils/logging/deprecation';\n\nimport type { IRenderLayer } from '../../layers/RenderLayer';\nimport type { Container, ContainerChild } from '../Container';\n\n/**\n * Mixin interface for containers that allows them to manage children.\n * It provides methods for adding, removing, and manipulating child containers.\n * @category scene\n * @advanced\n */\nexport interface ChildrenHelperMixin\n{\n /** @internal */\n allowChildren: boolean;\n addChild(...children: U): U[0];\n removeChild(...children: U): U[0];\n /**\n * Removes all children from this container that are within the begin and end indexes.\n * @example\n * ```ts\n * // Remove all children\n * container.removeChildren();\n *\n * // Remove first 3 children\n * const removed = container.removeChildren(0, 3);\n * console.log('Removed:', removed.length); // 3\n *\n * // Remove children from index 2 onwards\n * container.removeChildren(2);\n *\n * // Remove specific range\n * const middle = container.removeChildren(1, 4);\n * ```\n * @param {number} beginIndex - The beginning position\n * @param {number} endIndex - The ending position. Default is container size\n * @returns List of removed children\n * @throws {RangeError} If begin/end indexes are invalid\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChild} For removing specific children\n */\n removeChildren(beginIndex?: number, endIndex?: number): C[];\n /**\n * Removes a child from the specified index position.\n * @example\n * ```ts\n * // Remove first child\n * const removed = container.removeChildAt(0);\n *\n * // type safe access\n * const sprite = container.removeChildAt(1);\n *\n * // With error handling\n * try {\n * const child = container.removeChildAt(10);\n * } catch (e) {\n * console.warn('Index out of bounds');\n * }\n * ```\n * @param {number} index - The index to remove the child from\n * @returns The child that was removed\n * @throws {Error} If index is out of bounds\n * @see {@link Container#removeChild} For removing specific children\n * @see {@link Container#removeChildren} For removing multiple children\n */\n removeChildAt(index: number): U;\n /**\n * Returns the child at the specified index.\n * @example\n * ```ts\n * // Get first child\n * const first = container.getChildAt(0);\n *\n * // Type-safe access\n * const sprite = container.getChildAt(1);\n *\n * // With error handling\n * try {\n * const child = container.getChildAt(10);\n * } catch (e) {\n * console.warn('Index out of bounds');\n * }\n * ```\n * @param {number} index - The index to get the child from\n * @returns The child at the given index\n * @throws {Error} If index is out of bounds\n * @see {@link Container#children} For direct array access\n * @see {@link Container#getChildByLabel} For name-based lookup\n */\n getChildAt(index: number): U;\n /**\n * Changes the position of an existing child in the container.\n * @example\n * ```ts\n * // Basic index change\n * container.setChildIndex(sprite, 0); // Move to front\n * container.setChildIndex(sprite, container.children.length - 1); // Move to back\n *\n * // With error handling\n * try {\n * container.setChildIndex(sprite, 5);\n * } catch (e) {\n * console.warn('Invalid index or child not found');\n * }\n * ```\n * @param {Container}child - The child Container instance to reposition\n * @param {number}index - The resulting index number for the child\n * @throws {Error} If index is out of bounds\n * @throws {Error} If child is not in container\n * @see {@link Container#getChildIndex} For getting current index\n * @see {@link Container#swapChildren} For swapping positions\n */\n setChildIndex(child: C | IRenderLayer, index: number): void;\n /**\n * Returns the index position of a child Container instance.\n * @example\n * ```ts\n * // Basic index lookup\n * const index = container.getChildIndex(sprite);\n * console.log(`Sprite is at index ${index}`);\n *\n * // With error handling\n * try {\n * const index = container.getChildIndex(sprite);\n * } catch (e) {\n * console.warn('Child not found in container');\n * }\n * ```\n * @param {Container} child - The Container instance to identify\n * @returns The index position of the child container\n * @throws {Error} If child is not in this container\n * @see {@link Container#setChildIndex} For changing index\n * @see {@link Container#children} For direct array access\n */\n getChildIndex(child: C | IRenderLayer): number;\n /**\n * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown.\n * If the child is already in this container, it will be moved to the specified index.\n * @example\n * ```ts\n * // Add at specific index\n * container.addChildAt(sprite, 0); // Add to front\n *\n * // Move existing child\n * const index = container.children.length - 1;\n * container.addChildAt(existingChild, index); // Move to back\n *\n * // With error handling\n * try {\n * container.addChildAt(sprite, 1000);\n * } catch (e) {\n * console.warn('Index out of bounds');\n * }\n * ```\n * @param {Container} child - The child to add\n * @param {number} index - The index where the child will be placed\n * @returns The child that was added\n * @throws {Error} If index is out of bounds\n * @see {@link Container#addChild} For adding to the end\n * @see {@link Container#setChildIndex} For moving existing children\n */\n addChildAt(child: U, index: number): U;\n /**\n * Swaps the position of 2 Containers within this container.\n * @example\n * ```ts\n * // Basic swap\n * container.swapChildren(sprite1, sprite2);\n *\n * // With error handling\n * try {\n * container.swapChildren(sprite1, sprite2);\n * } catch (e) {\n * console.warn('One or both children not found in container');\n * }\n * ```\n * @remarks\n * - Updates render groups\n * - No effect if same child\n * - Triggers container changes\n * - Common in z-ordering\n * @param {Container} child - First container to swap\n * @param {Container} child2 - Second container to swap\n * @throws {Error} If either child is not in container\n * @see {@link Container#setChildIndex} For direct index placement\n * @see {@link Container#getChildIndex} For getting current positions\n */\n swapChildren(child: U, child2: U): void;\n /**\n * Remove the Container from its parent Container. If the Container has no parent, do nothing.\n * @example\n * ```ts\n * // Basic removal\n * sprite.removeFromParent();\n *\n * // With validation\n * if (sprite.parent) {\n * sprite.removeFromParent();\n * }\n * ```\n * @see {@link Container#addChild} For adding to a new parent\n * @see {@link Container#removeChild} For parent removing children\n */\n removeFromParent(): void;\n /**\n * Reparent a child or multiple children to this container while preserving their world transform.\n * This ensures that the visual position and rotation of the children remain the same even when changing parents.\n * @example\n * ```ts\n * // Basic reparenting\n * const sprite = new Sprite(texture);\n * oldContainer.addChild(sprite);\n * // Move to new parent, keeping visual position\n * newContainer.reparentChild(sprite);\n *\n * // Reparent multiple children\n * const batch = [sprite1, sprite2, sprite3];\n * newContainer.reparentChild(...batch);\n * ```\n * @param {Container} child - The child or children to reparent\n * @returns The first child that was reparented\n * @see {@link Container#reparentChildAt} For index-specific reparenting\n * @see {@link Container#addChild} For simple parenting\n */\n reparentChild(...child: U): U[0];\n /**\n * Reparent the child to this container at the specified index while preserving its world transform.\n * This ensures that the visual position and rotation of the child remain the same even when changing parents.\n * @example\n * ```ts\n * // Basic index-specific reparenting\n * const sprite = new Sprite(texture);\n * oldContainer.addChild(sprite);\n * // Move to new parent at index 0 (front)\n * newContainer.reparentChildAt(sprite, 0);\n * ```\n * @param {Container} child - The child to reparent\n * @param {number} index - The index to reparent the child to\n * @returns The reparented child\n * @throws {Error} If index is out of bounds\n * @see {@link Container#reparentChild} For appending reparented children\n * @see {@link Container#addChildAt} For simple indexed parenting\n */\n reparentChildAt(child: U, index: number): U;\n /**\n * Replace a child in the container with a new child. Copying the local transform from the old child to the new one.\n * @param {Container} oldChild - The child to replace.\n * @param {Container} newChild - The new child to add.\n */\n replaceChild(oldChild: U, newChild: T): void;\n}\n\n/** @internal */\nexport const childrenHelperMixin: ChildrenHelperMixin = {\n\n allowChildren: true,\n\n removeChildren(beginIndex = 0, endIndex?: number): ContainerChild[]\n {\n const end = endIndex ?? this.children.length;\n const range = end - beginIndex;\n const removed: ContainerChild[] = [];\n\n if (range > 0 && range <= end)\n {\n for (let i = end - 1; i >= beginIndex; i--)\n {\n const child = this.children[i];\n\n if (!child) continue;\n removed.push(child);\n child.parent = null;\n }\n\n removeItems(this.children, beginIndex, end);\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (renderGroup)\n {\n renderGroup.removeChildren(removed);\n }\n\n for (let i = 0; i < removed.length; ++i)\n {\n const child = removed[i];\n\n child.parentRenderLayer?.detach(child);\n\n this.emit('childRemoved', child, this, i);\n removed[i].emit('removed', this);\n }\n\n if (removed.length > 0)\n {\n this._didViewChangeTick++;\n }\n\n return removed;\n }\n else if (range === 0 && this.children.length === 0)\n {\n return removed;\n }\n\n throw new RangeError('removeChildren: numeric values are outside the acceptable range.');\n },\n\n removeChildAt(index: number): U\n {\n const child = this.getChildAt(index);\n\n return this.removeChild(child);\n },\n\n getChildAt(index: number): U\n {\n if (index < 0 || index >= this.children.length)\n {\n throw new Error(`getChildAt: Index (${index}) does not exist.`);\n }\n\n return this.children[index] as U;\n },\n\n setChildIndex(child: ContainerChild | IRenderLayer, index: number): void\n {\n if (index < 0 || index >= this.children.length)\n {\n throw new Error(`The index ${index} supplied is out of bounds ${this.children.length}`);\n }\n\n this.getChildIndex(child); // check if child exists\n this.addChildAt(child, index);\n },\n\n getChildIndex(child: ContainerChild | IRenderLayer): number\n {\n const index = this.children.indexOf(child as ContainerChild);\n\n if (index === -1)\n {\n throw new Error('The supplied Container must be a child of the caller');\n }\n\n return index;\n },\n\n addChildAt(child: U, index: number): U\n {\n // #if _DEBUG\n if (!this.allowChildren)\n {\n deprecation(v8_0_0, 'addChildAt: Only Containers will be allowed to add children in v8.0.0');\n }\n // #endif\n\n const { children } = this;\n\n if (index < 0 || index > children.length)\n {\n throw new Error(`${child}addChildAt: The index ${index} supplied is out of bounds ${children.length}`);\n }\n\n // TODO - check if child is already in the list?\n // we should be able to optimise this!\n\n if (child.parent)\n {\n const currentIndex = child.parent.children.indexOf(child as ContainerChild);\n\n // If this child is in the container and in the same position, do nothing\n if (child.parent === this && currentIndex === index)\n {\n return child;\n }\n\n if (currentIndex !== -1)\n {\n child.parent.children.splice(currentIndex, 1);\n }\n }\n\n if (index === children.length)\n {\n children.push(child as ContainerChild);\n }\n else\n {\n children.splice(index, 0, child as ContainerChild);\n }\n\n child.parent = this;\n child.didChange = true;\n child._updateFlags = 0b1111;\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (renderGroup)\n {\n renderGroup.addChild(child as ContainerChild);\n }\n\n if (this.sortableChildren) this.sortDirty = true;\n\n this.emit('childAdded', child as ContainerChild, this, index);\n child.emit('added', this);\n\n return child;\n },\n\n swapChildren(child: U, child2: U): void\n {\n if (child === child2)\n {\n return;\n }\n\n const index1 = this.getChildIndex(child);\n const index2 = this.getChildIndex(child2);\n\n this.children[index1] = child2 as ContainerChild;\n this.children[index2] = child as ContainerChild;\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (renderGroup)\n {\n renderGroup.structureDidChange = true;\n }\n\n this._didContainerChangeTick++;\n },\n\n removeFromParent()\n {\n this.parent?.removeChild(this);\n },\n\n reparentChild(...child: U): U[0]\n {\n if (child.length === 1)\n {\n return this.reparentChildAt(child[0], this.children.length);\n }\n\n child.forEach((c) => this.reparentChildAt(c, this.children.length));\n\n return child[0];\n },\n\n reparentChildAt(child: U, index: number): U\n {\n if (child.parent === this)\n {\n this.setChildIndex(child, index);\n\n return child;\n }\n\n const childMat = child.worldTransform.clone();\n\n child.removeFromParent();\n this.addChildAt(child, index);\n\n const newMatrix = this.worldTransform.clone();\n\n newMatrix.invert();\n childMat.prepend(newMatrix);\n\n child.setFromMatrix(childMat);\n\n return child;\n },\n\n replaceChild(oldChild: U, newChild: T)\n {\n oldChild.updateLocalTransform();\n this.addChildAt(newChild, this.getChildIndex(oldChild));\n\n newChild.setFromMatrix(oldChild.localTransform);\n newChild.updateLocalTransform();\n this.removeChild(oldChild);\n },\n} as Container;\n","import { type InstructionSet } from '../../../rendering/renderers/shared/instructions/InstructionSet';\nimport { type InstructionPipe } from '../../../rendering/renderers/shared/instructions/RenderPipe';\nimport { type Renderer, type RenderPipes } from '../../../rendering/renderers/types';\nimport { type IRenderLayer } from '../../layers/RenderLayer';\n\nimport type { Container } from '../Container';\n\n/**\n * The CollectRenderablesMixin interface defines methods for collecting renderable objects\n * from a container and its children. These methods add the renderables to an instruction set,\n * which is used by the renderer to process and display the scene.\n * @category scene\n * @internal\n */\nexport interface CollectRenderablesMixin\n{\n /**\n * Collects all renderables from the container and its children, adding them to the instruction set.\n * This method decides whether to use a simple or advanced collection method based on the container's properties.\n * @param {InstructionSet} instructionSet - The set of instructions to which the renderables will be added.\n * @param {Renderer} renderer - The renderer responsible for rendering the scene.\n * @param {IRenderLayer} currentLayer - The current render layer being processed.\n * @internal\n */\n collectRenderables(instructionSet: InstructionSet, renderer: Renderer, currentLayer: IRenderLayer): void;\n\n /**\n * Collects renderables using a simple method, suitable for containers marked as simple.\n * This method iterates over the container's children and adds their renderables to the instruction set.\n * @param {InstructionSet} instructionSet - The set of instructions to which the renderables will be added.\n * @param {Renderer} renderer - The renderer responsible for rendering the scene.\n * @param {IRenderLayer} currentLayer - The current render layer being processed.\n * @internal\n */\n collectRenderablesSimple(instructionSet: InstructionSet, renderer: Renderer, currentLayer: IRenderLayer): void;\n\n /**\n * Collects renderables using an advanced method, suitable for containers with complex processing needs.\n * This method handles additional effects and transformations that may be applied to the renderables.\n * @param {InstructionSet} instructionSet - The set of instructions to which the renderables will be added.\n * @param {Renderer} renderer - The renderer responsible for rendering the scene.\n * @param {IRenderLayer} currentLayer - The current render layer being processed.\n * @internal\n */\n collectRenderablesWithEffects(\n instructionSet: InstructionSet,\n renderer: Renderer,\n currentLayer: IRenderLayer,\n ): void;\n}\n\n/**\n * The collectRenderablesMixin provides implementations for the methods defined in the CollectRenderablesMixin interface.\n * It includes logic to determine the appropriate method for collecting renderables based on the container's properties.\n * @internal\n */\nexport const collectRenderablesMixin: Partial = {\n collectRenderables(instructionSet: InstructionSet, renderer: Renderer, currentLayer: IRenderLayer): void\n {\n // Skip processing if the container is not in the current render layer or is not fully visible.\n if ((this.parentRenderLayer && this.parentRenderLayer !== currentLayer)\n || this.globalDisplayStatus < 0b111 || !this.includeInBuild) return;\n\n // Sort children if the container has sortable children.\n if (this.sortableChildren)\n {\n this.sortChildren();\n }\n\n // Choose the appropriate method for collecting renderables based on the container's properties.\n if (this.isSimple)\n {\n this.collectRenderablesSimple(instructionSet, renderer, currentLayer);\n }\n else if (this.renderGroup)\n {\n renderer.renderPipes.renderGroup.addRenderGroup(this.renderGroup, instructionSet);\n }\n else\n {\n this.collectRenderablesWithEffects(instructionSet, renderer, currentLayer);\n }\n },\n collectRenderablesSimple(\n instructionSet: InstructionSet,\n renderer: Renderer,\n currentLayer: IRenderLayer,\n ): void\n {\n const children = this.children;\n const length = children.length;\n\n // Iterate over each child and collect their renderables.\n for (let i = 0; i < length; i++)\n {\n children[i].collectRenderables(instructionSet, renderer, currentLayer);\n }\n },\n collectRenderablesWithEffects(\n instructionSet: InstructionSet,\n renderer: Renderer,\n currentLayer: IRenderLayer,\n ): void\n {\n const { renderPipes } = renderer;\n\n // Apply each effect to the renderables before collecting them.\n for (let i = 0; i < this.effects.length; i++)\n {\n const effect = this.effects[i];\n const pipe = renderPipes[effect.pipe as keyof RenderPipes] as InstructionPipe;\n\n pipe.push(effect, this, instructionSet);\n }\n\n // Collect renderables using the simple method after applying effects.\n this.collectRenderablesSimple(instructionSet, renderer, currentLayer);\n\n // Remove effects from the renderables after collection, processing in reverse order.\n for (let i = this.effects.length - 1; i >= 0; i--)\n {\n const effect = this.effects[i];\n const pipe = renderPipes[effect.pipe as keyof RenderPipes] as InstructionPipe;\n\n pipe.pop(effect, this, instructionSet);\n }\n }\n} as Container;\n","import type { Rectangle } from '../maths/shapes/Rectangle';\nimport type { Effect } from '../scene/container/Effect';\nimport type { Filter } from './Filter';\n\n/**\n * A filter effect is an effect that can be applied to a container that involves applying special pixel effects\n * to that container as it is rendered. Used internally when the filters property is modified on a container.\n * @internal\n */\nexport class FilterEffect implements Effect\n{\n /** read only filters array - to modify, set it again! */\n public filters: readonly Filter[];\n /**\n * If specified, rather than calculating the bounds of the container that the filter\n * will apply to, we use this rect instead. This is a local rect - so will have the containers transform\n * applied to it\n */\n public filterArea?: Rectangle;\n\n /** the pipe that knows how to handle this effect */\n public pipe = 'filter';\n /** the priority of this effect */\n public priority = 1;\n\n public destroy(): void\n {\n for (let i = 0; i < this.filters.length; i++)\n {\n this.filters[i].destroy();\n }\n\n this.filters = null;\n this.filterArea = null;\n }\n}\n","import { extensions, ExtensionType } from '../../extensions/Extensions';\nimport { BigPool } from '../../utils/pool/PoolGroup';\n\nimport type { Effect, EffectConstructor } from '../../scene/container/Effect';\nimport type { PoolItem, PoolItemConstructor } from '../../utils/pool/Pool';\n\ninterface MaskConversionTest\n{\n test: (item: any) => boolean;\n maskClass: new (item: any) => Effect & PoolItem;\n}\n\n/**\n * Represents a mask effect that can be applied to a container.\n * @category rendering\n * @advanced\n */\nexport type MaskEffect = {mask: unknown} & Effect;\n\n/**\n * A class that manages the conversion of masks to mask effects.\n * @category rendering\n * @ignore\n */\nexport class MaskEffectManagerClass\n{\n /** @private */\n public readonly _effectClasses: EffectConstructor[] = [];\n private readonly _tests: MaskConversionTest[] = [];\n private _initialized = false;\n\n public init()\n {\n if (this._initialized) return;\n\n this._initialized = true;\n\n this._effectClasses.forEach((test) =>\n {\n this.add({\n test: test.test,\n maskClass: test\n });\n });\n }\n\n public add(test: MaskConversionTest)\n {\n this._tests.push(test);\n }\n\n public getMaskEffect(item: any): MaskEffect\n {\n if (!this._initialized) this.init();\n\n for (let i = 0; i < this._tests.length; i++)\n {\n const test = this._tests[i];\n\n if (test.test(item))\n {\n return BigPool.get(test.maskClass as PoolItemConstructor, item);\n }\n }\n\n return item;\n }\n\n public returnMaskEffect(effect: Effect & PoolItem)\n {\n BigPool.return(effect);\n }\n}\n\n/**\n * A class that manages the conversion of masks to mask effects.\n * @class\n * @category rendering\n * @advanced\n */\nexport const MaskEffectManager = new MaskEffectManagerClass();\n\n// Handle registration of extensions\nextensions\n .handleByList(ExtensionType.MaskEffect, MaskEffectManager._effectClasses);\n","import { FilterEffect } from '../../../filters/FilterEffect';\nimport { MaskEffectManager } from '../../../rendering/mask/MaskEffectManager';\n\nimport type { Filter } from '../../../filters/Filter';\nimport type { Rectangle } from '../../../maths/shapes/Rectangle';\nimport type { MaskEffect } from '../../../rendering/mask/MaskEffectManager';\nimport type { Container } from '../Container';\nimport type { Effect } from '../Effect';\n\n/** @ignore */\nexport interface EffectsMixinConstructor\n{\n /**\n * The mask to apply, which can be a Container or null.\n *\n * If null, it clears the existing mask.\n * @example\n * ```ts\n * // Set a mask\n * sprite.setMask({\n * mask: graphics,\n * inverse: false,\n * });\n */\n mask?: Mask;\n setMask?: (options: Partial) => void;\n /**\n * Sets the filters for the displayObject.\n * Filters are visual effects that can be applied to any display object and its children.\n *\n * > [!IMPORTANT] This is a WebGL/WebGPU only feature and will be ignored by the canvas renderer.\n * @example\n * ```ts\n * new Container({\n * filters: [new BlurFilter(2), new ColorMatrixFilter()],\n * });\n * ```\n * @see {@link Filter} For filter base class\n */\n filters?: Filter | readonly Filter[];\n}\n\n/**\n * The Mask type represents different ways to mask a display object.\n * - A number represents a mask ID.\n * - A Container represents a mask object, such as a Graphics or Sprite.\n * - null indicates that no mask is applied.\n * @example\n * ```ts\n * // Using a Container as a mask\n * const maskContainer: Mask = new Graphics();\n * // Using a mask ID\n * const maskId: Mask = 123;\n * // No mask applied\n * const noMask: Mask = null;\n * ```\n * @category scene\n * @standard\n */\nexport type Mask = number | Container | null;\n\n/**\n * Options for configuring mask behavior on a display object.\n * @example\n * ```ts\n * // Basic mask inversion\n * sprite.setMask({\n * mask: graphics,\n * inverse: true\n * });\n * ```\n * @see {@link Container#setMask} For applying masks with options\n * @see {@link Container#mask} For basic masking\n * @category scene\n * @standard\n */\nexport interface MaskOptions\n{\n /**\n * Whether the mask should be inverted.\n * When true, the masked area becomes transparent and the unmasked area becomes visible.\n * @default false\n * @example\n * ```ts\n * // Invert the mask\n * sprite.setMask({\n * mask: graphics,\n * inverse: true\n * });\n * ```\n */\n inverse: boolean;\n}\n\n/**\n * MaskOptionsAndMask combines MaskOptions with a Mask for configuring masking behavior.\n * Used when setting up complex masking effects with additional options.\n * @example\n * ```ts\n * sprite.setMask({\n * mask: graphics,\n * inverse: true,\n * });\n *\n * // Clear existing mask\n * sprite.setMask({\n * mask: null,\n * inverse: false,\n * });\n * ```\n * @category scene\n * @standard\n * @see {@link Container#setMask} For applying masks\n * @see {@link MaskOptions} For base options\n */\nexport interface MaskOptionsAndMask extends MaskOptions\n{\n /**\n * The mask to apply, which can be a Container or null.\n *\n * If null, it clears the existing mask.\n * @example\n * ```ts\n * // Set a mask\n * sprite.setMask({\n * mask: graphics,\n * inverse: false,\n * });\n */\n mask: Mask;\n}\n\n/**\n * The EffectsMixin interface provides methods and properties for managing effects\n * such as masks and filters on a display object.\n * It allows for adding, removing, and configuring effects, as well as setting a mask for the display object.\n * @category scene\n * @advanced\n */\nexport interface EffectsMixin extends Required\n{\n /** @private */\n _maskEffect?: MaskEffect;\n /** @private */\n _maskOptions?: MaskOptions;\n /** @private */\n _filterEffect?: FilterEffect,\n /** @private */\n _markStructureAsChanged(): void;\n\n /**\n * The area the filter is applied to. This is used as an optimization to define a specific region\n * for filter effects instead of calculating the display object bounds each frame.\n *\n * > [!NOTE]\n * > Setting this to a custom Rectangle allows you to define a specific area for filter effects,\n * > which can improve performance by avoiding expensive bounds calculations.\n * @example\n * ```ts\n * // Set specific filter area\n * container.filterArea = new Rectangle(0, 0, 100, 100);\n *\n * // Optimize filter region\n * const screen = app.screen;\n * container.filterArea = new Rectangle(\n * screen.x,\n * screen.y,\n * screen.width,\n * screen.height\n * );\n * ```\n * @see {@link Container#filters} For applying filters\n * @see {@link Rectangle} For area definition\n */\n filterArea?: Rectangle,\n /**\n * todo Needs docs\n * @advanced\n */\n effects?: Effect[];\n /**\n * todo Needs docs.\n * @param {Effect} effect - The effect to add.\n * @ignore\n */\n addEffect(effect: Effect): void;\n /**\n * todo Needs docs.\n * @param {Effect} effect - The effect to remove.\n * @ignore\n */\n removeEffect(effect: Effect): void;\n /**\n * Used to set mask and control mask options on a display object.\n * Allows for more detailed control over masking behavior compared to the mask property.\n * @example\n * ```ts\n * import { Graphics, Sprite } from 'pixi.js';\n *\n * // Create a circular mask\n * const graphics = new Graphics()\n * .beginFill(0xFF3300)\n * .drawCircle(100, 100, 50)\n * .endFill();\n *\n * // Apply mask with options\n * sprite.setMask({\n * mask: graphics,\n * inverse: true, // Create a hole effect\n * });\n *\n * // Clear existing mask\n * sprite.setMask({ mask: null });\n * ```\n * @param {Partial} options - Configuration options for the mask\n * @see {@link Container#mask} For simple masking\n * @see {@link MaskOptionsAndMask} For full options API\n */\n setMask(options: Partial): void;\n /**\n * Sets a mask for the displayObject. A mask is an object that limits the visibility of an\n * object to the shape of the mask applied to it.\n *\n * > [!IMPORTANT] In PixiJS a regular mask must be a {@link Graphics} or a {@link Sprite} object.\n * > This allows for much faster masking in canvas as it utilities shape clipping.\n * > Furthermore, a mask of an object must be in the subtree of its parent.\n * > Otherwise, `getLocalBounds` may calculate incorrect bounds, which makes the container's width and height wrong.\n *\n * For sprite mask both alpha and red channel are used. Black mask is the same as transparent mask.\n * @example\n * ```ts\n * // Apply mask to sprite\n * const sprite = new Sprite(texture);\n * sprite.mask = graphics;\n *\n * // Remove mask\n * sprite.mask = null;\n * ```\n * @see {@link Graphics} For creating mask shapes\n * @see {@link Sprite} For texture-based masks\n * @see {@link Container#setMask} For advanced mask options\n */\n mask: Mask;\n /**\n * Sets the filters for the displayObject.\n * Filters are visual effects that can be applied to any display object and its children.\n *\n * > [!IMPORTANT] This is a WebGL/WebGPU only feature and will be ignored by the canvas renderer.\n * @example\n * ```ts\n * // Add a single filter\n * sprite.filters = new BlurFilter(2);\n *\n * // Apply multiple filters\n * container.filters = [\n * new BlurFilter(2),\n * new ColorMatrixFilter(),\n * ];\n *\n * // Remove filters\n * sprite.filters = null;\n * ```\n * @see {@link Filter} For filter base class\n */\n set filters(value: Filter | Filter[] | null | undefined);\n get filters(): readonly Filter[];\n}\n\n/** @internal */\nexport const effectsMixin: Partial = {\n _maskEffect: null,\n _maskOptions: {\n inverse: false,\n },\n _filterEffect: null,\n\n effects: [],\n\n _markStructureAsChanged()\n {\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (renderGroup)\n {\n renderGroup.structureDidChange = true;\n }\n },\n\n addEffect(effect: Effect)\n {\n const index = this.effects.indexOf(effect);\n\n if (index !== -1) return; // already exists!\n\n this.effects.push(effect);\n\n this.effects.sort((a, b) => a.priority - b.priority);\n\n this._markStructureAsChanged();\n\n // if (this.renderGroup)\n // {\n // this.renderGroup.structureDidChange = true;\n // }\n\n this._updateIsSimple();\n },\n\n removeEffect(effect: Effect)\n {\n const index = this.effects.indexOf(effect);\n\n if (index === -1) return; // already exists!\n\n this.effects.splice(index, 1);\n\n this._markStructureAsChanged();\n\n this._updateIsSimple();\n },\n\n set mask(value: Mask)\n {\n const effect = this._maskEffect;\n\n if (effect?.mask === value) return;\n\n if (effect)\n {\n this.removeEffect(effect);\n\n MaskEffectManager.returnMaskEffect(effect);\n\n this._maskEffect = null;\n }\n\n if (value === null || value === undefined) return;\n\n this._maskEffect = MaskEffectManager.getMaskEffect(value);\n\n this.addEffect(this._maskEffect);\n },\n get mask(): unknown\n {\n return this._maskEffect?.mask;\n },\n\n setMask(options: Partial)\n {\n this._maskOptions = {\n ...this._maskOptions,\n ...options,\n };\n\n if (options.mask)\n {\n this.mask = options.mask;\n }\n\n this._markStructureAsChanged();\n },\n\n set filters(value: Filter | Filter[] | null | undefined)\n {\n if (!Array.isArray(value) && value) value = [value];\n\n const effect = this._filterEffect ||= new FilterEffect();\n\n // Ignore the Filter type\n value = value as Filter[] | null | undefined;\n\n const hasFilters = value?.length > 0;\n const hadFilters = effect.filters?.length > 0;\n\n const didChange = hasFilters !== hadFilters;\n\n // Clone the filters array so we don't freeze the user-input\n value = Array.isArray(value) ? value.slice(0) : value;\n\n // Ensure filters are immutable via filters getter\n effect.filters = Object.freeze(value);\n\n if (didChange)\n {\n if (hasFilters)\n {\n this.addEffect(effect);\n }\n else\n {\n this.removeEffect(effect);\n\n // sets the empty array...\n effect.filters = value ?? null;\n }\n }\n },\n get filters(): readonly Filter[]\n {\n return this._filterEffect?.filters;\n },\n\n set filterArea(value: Rectangle)\n {\n this._filterEffect ||= new FilterEffect();\n\n this._filterEffect.filterArea = value;\n },\n get filterArea(): Rectangle\n {\n return this._filterEffect?.filterArea;\n },\n\n} as Container;\n","import { deprecation, v8_0_0 } from '../../../utils/logging/deprecation';\n\nimport type { Container } from '../Container';\n\n/** @ignore */\nexport interface FindMixinConstructor\n{\n /**\n * The instance label of the object.\n * @default null\n */\n label?: string;\n}\n\n/**\n * The FindMixin interface provides methods for finding children within a container by their label.\n * It allows for searching for a single child or multiple children with a specific label,\n * either directly or recursively through the container's hierarchy.\n * @category scene\n * @advanced\n */\nexport interface FindMixin extends Required\n{\n /**\n * The instance name of the object.\n * @deprecated since 8.0.0\n * @see Container#label\n * @default null\n */\n name: string;\n /**\n * @deprecated since 8.0.0\n * @param {string} label - Instance name.\n * @param {boolean}[deep=false] - Whether to search recursively\n * @returns {Container} The child with the specified name.\n * @see Container#getChildByLabel\n */\n getChildByName(label: RegExp | string, deep?: boolean): Container | null;\n /**\n * Returns the first child in the container with the specified label.\n * Recursive searches are done in a pre-order traversal.\n * @example\n * ```ts\n * // Basic label search\n * const child = container.getChildByLabel('player');\n *\n * // Search with regular expression\n * const enemy = container.getChildByLabel(/enemy-\\d+/);\n *\n * // Deep search through children\n * const deepChild = container.getChildByLabel('powerup', true);\n * ```\n * @param {RegExp|string} label - Instance label to search for\n * @param {boolean} deep - Whether to search recursively through children\n * @returns The first child with the specified label, or null if none found\n * @see {@link Container#getChildrenByLabel} For finding all matches\n * @see {@link Container#label} For setting labels\n */\n getChildByLabel(label: RegExp | string, deep?: boolean): Container | null;\n /**\n * Returns all children in the container with the specified label.\n * Recursive searches are done in a pre-order traversal.\n * @example\n * ```ts\n * // Basic label search\n * const enemies = container.getChildrenByLabel('enemy');\n * // Search with regular expression\n * const powerups = container.getChildrenByLabel(/powerup-\\d+/);\n * // Deep search with collection\n * const buttons = [];\n * container.getChildrenByLabel('button', true, buttons);\n * ```\n * @param {string|RegExp} label - Instance label to search for\n * @param {boolean}[deep=false] - Whether to search recursively through children\n * @param {Container[]} [out=[]] - Optional array to store matching children in\n * @returns An array of children with the specified label\n * @see {@link Container#getChildByLabel} For finding first match\n * @see {@link Container#label} For setting labels\n */\n getChildrenByLabel(label: RegExp | string, deep?: boolean, out?: Container[]): Container[];\n}\n\n/** @internal */\nexport const findMixin: Partial = {\n label: null,\n\n get name(): string\n {\n // #if _DEBUG\n deprecation(v8_0_0, 'Container.name property has been removed, use Container.label instead');\n // #endif\n\n return this.label;\n },\n set name(value: string)\n {\n // #if _DEBUG\n deprecation(v8_0_0, 'Container.name property has been removed, use Container.label instead');\n // #endif\n\n this.label = value;\n },\n\n getChildByName(name: string, deep = false): Container | null\n {\n return this.getChildByLabel(name, deep);\n },\n\n getChildByLabel(label: string | RegExp, deep = false): Container | null\n {\n const children = this.children;\n\n for (let i = 0; i < children.length; i++)\n {\n const child = children[i];\n\n if (child.label === label || (label instanceof RegExp && label.test(child.label))) return child;\n }\n\n if (deep)\n {\n for (let i = 0; i < children.length; i++)\n {\n const child = children[i];\n const found = child.getChildByLabel(label, true);\n\n if (found)\n {\n return found;\n }\n }\n }\n\n return null;\n },\n\n getChildrenByLabel(label: string | RegExp, deep = false, out = []): Container[]\n {\n const children = this.children;\n\n for (let i = 0; i < children.length; i++)\n {\n const child = children[i];\n\n if (child.label === label || (label instanceof RegExp && label.test(child.label)))\n {\n out.push(child);\n }\n }\n\n if (deep)\n {\n for (let i = 0; i < children.length; i++)\n {\n children[i].getChildrenByLabel(label, true, out);\n }\n }\n\n return out;\n },\n} as Container;\n","// import { SHAPES } from '../const';\nimport { Point } from '../point/Point';\n\nimport type { Bounds } from '../../scene/container/bounds/Bounds';\nimport type { Matrix } from '../matrix/Matrix';\nimport type { SHAPE_PRIMITIVE } from '../misc/const';\nimport type { ShapePrimitive } from './ShapePrimitive';\n\nconst tempPoints = [new Point(), new Point(), new Point(), new Point()];\n\n// eslint-disable-next-line max-len\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type, requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface Rectangle extends PixiMixins.Rectangle { }\n\n/**\n * The `Rectangle` object represents a rectangular area defined by its position and dimensions.\n * Used for hit testing, bounds calculation, and general geometric operations.\n * @example\n * ```ts\n * // Basic rectangle creation\n * const rect = new Rectangle(100, 100, 200, 150);\n *\n * // Use as container bounds\n * container.hitArea = new Rectangle(0, 0, 100, 100);\n *\n * // Check point containment\n * const isInside = rect.contains(mouseX, mouseY);\n *\n * // Manipulate dimensions\n * rect.width *= 2;\n * rect.height += 50;\n * ```\n * @remarks\n * - Position defined by top-left corner (x,y)\n * - Dimensions defined by width and height\n * - Supports point and rectangle containment\n * - Common in UI and layout calculations\n * @see {@link Circle} For circular shapes\n * @see {@link Polygon} For complex shapes\n * @see {@link RoundedRectangle} For rounded corners\n * @category maths\n * @standard\n */\nexport class Rectangle implements ShapePrimitive\n{\n /**\n * The type of the object, mainly used to avoid `instanceof` checks\n * @example\n * ```ts\n * // Check shape type\n * const shape = new Rectangle(0, 0, 100, 100);\n * console.log(shape.type); // 'rectangle'\n *\n * // Use in type guards\n * if (shape.type === 'rectangle') {\n * console.log(shape.width, shape.height);\n * }\n * ```\n * @readonly\n * @default 'rectangle'\n * @see {@link SHAPE_PRIMITIVE} For all shape types\n */\n public readonly type: SHAPE_PRIMITIVE = 'rectangle';\n\n /**\n * The X coordinate of the upper-left corner of the rectangle\n * @example\n * ```ts\n * // Basic x position\n * const rect = new Rectangle();\n * rect.x = 100;\n * ```\n * @default 0\n */\n public x: number;\n\n /**\n * The Y coordinate of the upper-left corner of the rectangle\n * @example\n * ```ts\n * // Basic y position\n * const rect = new Rectangle();\n * rect.y = 100;\n * ```\n * @default 0\n */\n public y: number;\n\n /**\n * The overall width of this rectangle\n * @example\n * ```ts\n * // Basic width setting\n * const rect = new Rectangle();\n * rect.width = 200;\n * ```\n * @default 0\n */\n public width: number;\n\n /**\n * The overall height of this rectangle\n * @example\n * ```ts\n * // Basic height setting\n * const rect = new Rectangle();\n * rect.height = 150;\n * ```\n * @default 0\n */\n public height: number;\n\n /**\n * @param x - The X coordinate of the upper-left corner of the rectangle\n * @param y - The Y coordinate of the upper-left corner of the rectangle\n * @param width - The overall width of the rectangle\n * @param height - The overall height of the rectangle\n */\n constructor(x: string | number = 0, y: string | number = 0, width: string | number = 0, height: string | number = 0)\n {\n this.x = Number(x);\n this.y = Number(y);\n this.width = Number(width);\n this.height = Number(height);\n }\n\n /**\n * Returns the left edge (x-coordinate) of the rectangle.\n * @example\n * ```ts\n * // Get left edge position\n * const rect = new Rectangle(100, 100, 200, 150);\n * console.log(rect.left); // 100\n *\n * // Use in alignment calculations\n * sprite.x = rect.left + padding;\n *\n * // Compare positions\n * if (point.x > rect.left) {\n * console.log('Point is right of rectangle');\n * }\n * ```\n * @readonly\n * @returns The x-coordinate of the left edge\n * @see {@link Rectangle.right} For right edge position\n * @see {@link Rectangle.x} For direct x-coordinate access\n */\n get left(): number\n {\n return this.x;\n }\n\n /**\n * Returns the right edge (x + width) of the rectangle.\n * @example\n * ```ts\n * // Get right edge position\n * const rect = new Rectangle(100, 100, 200, 150);\n * console.log(rect.right); // 300\n *\n * // Align to right edge\n * sprite.x = rect.right - sprite.width;\n *\n * // Check boundaries\n * if (point.x < rect.right) {\n * console.log('Point is inside right bound');\n * }\n * ```\n * @readonly\n * @returns The x-coordinate of the right edge\n * @see {@link Rectangle.left} For left edge position\n * @see {@link Rectangle.width} For width value\n */\n get right(): number\n {\n return this.x + this.width;\n }\n\n /**\n * Returns the top edge (y-coordinate) of the rectangle.\n * @example\n * ```ts\n * // Get top edge position\n * const rect = new Rectangle(100, 100, 200, 150);\n * console.log(rect.top); // 100\n *\n * // Position above rectangle\n * sprite.y = rect.top - sprite.height;\n *\n * // Check vertical position\n * if (point.y > rect.top) {\n * console.log('Point is below top edge');\n * }\n * ```\n * @readonly\n * @returns The y-coordinate of the top edge\n * @see {@link Rectangle.bottom} For bottom edge position\n * @see {@link Rectangle.y} For direct y-coordinate access\n */\n get top(): number\n {\n return this.y;\n }\n\n /**\n * Returns the bottom edge (y + height) of the rectangle.\n * @example\n * ```ts\n * // Get bottom edge position\n * const rect = new Rectangle(100, 100, 200, 150);\n * console.log(rect.bottom); // 250\n *\n * // Stack below rectangle\n * sprite.y = rect.bottom + margin;\n *\n * // Check vertical bounds\n * if (point.y < rect.bottom) {\n * console.log('Point is above bottom edge');\n * }\n * ```\n * @readonly\n * @returns The y-coordinate of the bottom edge\n * @see {@link Rectangle.top} For top edge position\n * @see {@link Rectangle.height} For height value\n */\n get bottom(): number\n {\n return this.y + this.height;\n }\n\n /**\n * Determines whether the Rectangle is empty (has no area).\n * @example\n * ```ts\n * // Check zero dimensions\n * const rect = new Rectangle(100, 100, 0, 50);\n * console.log(rect.isEmpty()); // true\n * ```\n * @returns True if the rectangle has no area\n * @see {@link Rectangle.width} For width value\n * @see {@link Rectangle.height} For height value\n */\n public isEmpty(): boolean\n {\n return this.left === this.right || this.top === this.bottom;\n }\n\n /**\n * A constant empty rectangle. This is a new object every time the property is accessed.\n * @example\n * ```ts\n * // Get fresh empty rectangle\n * const empty = Rectangle.EMPTY;\n * console.log(empty.isEmpty()); // true\n * ```\n * @returns A new empty rectangle instance\n * @see {@link Rectangle.isEmpty} For empty state testing\n */\n static get EMPTY(): Rectangle\n {\n return new Rectangle(0, 0, 0, 0);\n }\n\n /**\n * Creates a clone of this Rectangle\n * @example\n * ```ts\n * // Basic cloning\n * const original = new Rectangle(100, 100, 200, 150);\n * const copy = original.clone();\n *\n * // Clone and modify\n * const modified = original.clone();\n * modified.width *= 2;\n * modified.height += 50;\n *\n * // Verify independence\n * console.log(original.width); // 200\n * console.log(modified.width); // 400\n * ```\n * @returns A copy of the rectangle\n * @see {@link Rectangle.copyFrom} For copying into existing rectangle\n * @see {@link Rectangle.copyTo} For copying to another rectangle\n */\n public clone(): Rectangle\n {\n return new Rectangle(this.x, this.y, this.width, this.height);\n }\n\n /**\n * Converts a Bounds object to a Rectangle object.\n * @example\n * ```ts\n * // Convert bounds to rectangle\n * const bounds = container.getBounds();\n * const rect = new Rectangle().copyFromBounds(bounds);\n * ```\n * @param bounds - The bounds to copy and convert to a rectangle\n * @returns Returns itself\n * @see {@link Bounds} For bounds object structure\n * @see {@link Rectangle.getBounds} For getting rectangle bounds\n */\n public copyFromBounds(bounds: Bounds): this\n {\n this.x = bounds.minX;\n this.y = bounds.minY;\n this.width = bounds.maxX - bounds.minX;\n this.height = bounds.maxY - bounds.minY;\n\n return this;\n }\n\n /**\n * Copies another rectangle to this one.\n * @example\n * ```ts\n * // Basic copying\n * const source = new Rectangle(100, 100, 200, 150);\n * const target = new Rectangle();\n * target.copyFrom(source);\n *\n * // Chain with other operations\n * const rect = new Rectangle()\n * .copyFrom(source)\n * .pad(10);\n * ```\n * @param rectangle - The rectangle to copy from\n * @returns Returns itself\n * @see {@link Rectangle.copyTo} For copying to another rectangle\n * @see {@link Rectangle.clone} For creating new rectangle copy\n */\n public copyFrom(rectangle: Rectangle): Rectangle\n {\n this.x = rectangle.x;\n this.y = rectangle.y;\n this.width = rectangle.width;\n this.height = rectangle.height;\n\n return this;\n }\n\n /**\n * Copies this rectangle to another one.\n * @example\n * ```ts\n * // Basic copying\n * const source = new Rectangle(100, 100, 200, 150);\n * const target = new Rectangle();\n * source.copyTo(target);\n *\n * // Chain with other operations\n * const result = source\n * .copyTo(new Rectangle())\n * .getBounds();\n * ```\n * @param rectangle - The rectangle to copy to\n * @returns Returns given parameter\n * @see {@link Rectangle.copyFrom} For copying from another rectangle\n * @see {@link Rectangle.clone} For creating new rectangle copy\n */\n public copyTo(rectangle: Rectangle): Rectangle\n {\n rectangle.copyFrom(this);\n\n return rectangle;\n }\n\n /**\n * Checks whether the x and y coordinates given are contained within this Rectangle\n * @example\n * ```ts\n * // Basic containment check\n * const rect = new Rectangle(100, 100, 200, 150);\n * const isInside = rect.contains(150, 125); // true\n * // Check edge cases\n * console.log(rect.contains(100, 100)); // true (on edge)\n * console.log(rect.contains(300, 250)); // false (outside)\n * ```\n * @param x - The X coordinate of the point to test\n * @param y - The Y coordinate of the point to test\n * @returns Whether the x/y coordinates are within this Rectangle\n * @see {@link Rectangle.containsRect} For rectangle containment\n * @see {@link Rectangle.strokeContains} For checking stroke intersection\n */\n public contains(x: number, y: number): boolean\n {\n if (this.width <= 0 || this.height <= 0)\n {\n return false;\n }\n\n if (x >= this.x && x < this.x + this.width)\n {\n if (y >= this.y && y < this.y + this.height)\n {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Checks whether the x and y coordinates given are contained within this rectangle including the stroke.\n * @example\n * ```ts\n * // Basic stroke check\n * const rect = new Rectangle(100, 100, 200, 150);\n * const isOnStroke = rect.strokeContains(150, 100, 4); // 4px line width\n *\n * // Check with different alignments\n * const innerStroke = rect.strokeContains(150, 100, 4, 1); // Inside\n * const centerStroke = rect.strokeContains(150, 100, 4, 0.5); // Centered\n * const outerStroke = rect.strokeContains(150, 100, 4, 0); // Outside\n * ```\n * @param x - The X coordinate of the point to test\n * @param y - The Y coordinate of the point to test\n * @param strokeWidth - The width of the line to check\n * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer)\n * @returns Whether the x/y coordinates are within this rectangle's stroke\n * @see {@link Rectangle.contains} For checking fill containment\n * @see {@link Rectangle.getBounds} For getting stroke bounds\n */\n public strokeContains(x: number, y: number, strokeWidth: number, alignment: number = 0.5): boolean\n {\n const { width, height } = this;\n\n if (width <= 0 || height <= 0) return false;\n\n const _x = this.x;\n const _y = this.y;\n\n const strokeWidthOuter = strokeWidth * (1 - alignment);\n const strokeWidthInner = strokeWidth - strokeWidthOuter;\n\n const outerLeft = _x - strokeWidthOuter;\n const outerRight = _x + width + strokeWidthOuter;\n const outerTop = _y - strokeWidthOuter;\n const outerBottom = _y + height + strokeWidthOuter;\n\n const innerLeft = _x + strokeWidthInner;\n const innerRight = _x + width - strokeWidthInner;\n const innerTop = _y + strokeWidthInner;\n const innerBottom = _y + height - strokeWidthInner;\n\n return (x >= outerLeft && x <= outerRight && y >= outerTop && y <= outerBottom)\n && !(x > innerLeft && x < innerRight && y > innerTop && y < innerBottom);\n }\n /**\n * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object.\n * Returns true only if the area of the intersection is >0, this means that Rectangles\n * sharing a side are not overlapping. Another side effect is that an arealess rectangle\n * (width or height equal to zero) can't intersect any other rectangle.\n * @param {Rectangle} other - The Rectangle to intersect with `this`.\n * @param {Matrix} transform - The transformation matrix of `other`.\n * @returns {boolean} A value of `true` if the transformed `other` Rectangle intersects with `this`; otherwise `false`.\n */\n /**\n * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object.\n *\n * Returns true only if the area of the intersection is greater than 0.\n * This means that rectangles sharing only a side are not considered intersecting.\n * @example\n * ```ts\n * // Basic intersection check\n * const rect1 = new Rectangle(0, 0, 100, 100);\n * const rect2 = new Rectangle(50, 50, 100, 100);\n * console.log(rect1.intersects(rect2)); // true\n *\n * // With transformation matrix\n * const matrix = new Matrix();\n * matrix.rotate(Math.PI / 4); // 45 degrees\n * console.log(rect1.intersects(rect2, matrix)); // Checks with rotation\n *\n * // Edge cases\n * const zeroWidth = new Rectangle(0, 0, 0, 100);\n * console.log(rect1.intersects(zeroWidth)); // false (no area)\n * ```\n * @remarks\n * - Returns true only if intersection area is > 0\n * - Rectangles sharing only a side are not intersecting\n * - Zero-area rectangles cannot intersect anything\n * - Supports optional transformation matrix\n * @param other - The Rectangle to intersect with `this`\n * @param transform - Optional transformation matrix of `other`\n * @returns True if the transformed `other` Rectangle intersects with `this`\n * @see {@link Rectangle.containsRect} For containment testing\n * @see {@link Rectangle.contains} For point testing\n */\n public intersects(other: Rectangle, transform?: Matrix): boolean\n {\n if (!transform)\n {\n const x0 = this.x < other.x ? other.x : this.x;\n const x1 = this.right > other.right ? other.right : this.right;\n\n if (x1 <= x0)\n {\n return false;\n }\n\n const y0 = this.y < other.y ? other.y : this.y;\n const y1 = this.bottom > other.bottom ? other.bottom : this.bottom;\n\n return y1 > y0;\n }\n\n const x0 = this.left;\n const x1 = this.right;\n const y0 = this.top;\n const y1 = this.bottom;\n\n if (x1 <= x0 || y1 <= y0)\n {\n return false;\n }\n\n const lt = tempPoints[0].set(other.left, other.top);\n const lb = tempPoints[1].set(other.left, other.bottom);\n const rt = tempPoints[2].set(other.right, other.top);\n const rb = tempPoints[3].set(other.right, other.bottom);\n\n if (rt.x <= lt.x || lb.y <= lt.y)\n {\n return false;\n }\n\n const s = Math.sign((transform.a * transform.d) - (transform.b * transform.c));\n\n if (s === 0)\n {\n return false;\n }\n\n transform.apply(lt, lt);\n transform.apply(lb, lb);\n transform.apply(rt, rt);\n transform.apply(rb, rb);\n\n if (Math.max(lt.x, lb.x, rt.x, rb.x) <= x0\n || Math.min(lt.x, lb.x, rt.x, rb.x) >= x1\n || Math.max(lt.y, lb.y, rt.y, rb.y) <= y0\n || Math.min(lt.y, lb.y, rt.y, rb.y) >= y1)\n {\n return false;\n }\n\n const nx = s * (lb.y - lt.y);\n const ny = s * (lt.x - lb.x);\n const n00 = (nx * x0) + (ny * y0);\n const n10 = (nx * x1) + (ny * y0);\n const n01 = (nx * x0) + (ny * y1);\n const n11 = (nx * x1) + (ny * y1);\n\n if (Math.max(n00, n10, n01, n11) <= (nx * lt.x) + (ny * lt.y)\n || Math.min(n00, n10, n01, n11) >= (nx * rb.x) + (ny * rb.y))\n {\n return false;\n }\n\n const mx = s * (lt.y - rt.y);\n const my = s * (rt.x - lt.x);\n const m00 = (mx * x0) + (my * y0);\n const m10 = (mx * x1) + (my * y0);\n const m01 = (mx * x0) + (my * y1);\n const m11 = (mx * x1) + (my * y1);\n\n if (Math.max(m00, m10, m01, m11) <= (mx * lt.x) + (my * lt.y)\n || Math.min(m00, m10, m01, m11) >= (mx * rb.x) + (my * rb.y))\n {\n return false;\n }\n\n return true;\n }\n\n /**\n * Pads the rectangle making it grow in all directions.\n *\n * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.\n * @example\n * ```ts\n * // Basic padding\n * const rect = new Rectangle(100, 100, 200, 150);\n * rect.pad(10); // Adds 10px padding on all sides\n *\n * // Different horizontal and vertical padding\n * const uiRect = new Rectangle(0, 0, 100, 50);\n * uiRect.pad(20, 10); // 20px horizontal, 10px vertical\n * ```\n * @remarks\n * - Adjusts x/y by subtracting padding\n * - Increases width/height by padding * 2\n * - Common in UI layout calculations\n * - Chainable with other methods\n * @param paddingX - The horizontal padding amount\n * @param paddingY - The vertical padding amount\n * @returns Returns itself\n * @see {@link Rectangle.enlarge} For growing to include another rectangle\n * @see {@link Rectangle.fit} For shrinking to fit within another rectangle\n */\n public pad(paddingX = 0, paddingY = paddingX): this\n {\n this.x -= paddingX;\n this.y -= paddingY;\n\n this.width += paddingX * 2;\n this.height += paddingY * 2;\n\n return this;\n }\n\n /**\n * Fits this rectangle around the passed one.\n * @example\n * ```ts\n * // Basic fitting\n * const container = new Rectangle(0, 0, 100, 100);\n * const content = new Rectangle(25, 25, 200, 200);\n * content.fit(container); // Clips to container bounds\n * ```\n * @param rectangle - The rectangle to fit around\n * @returns Returns itself\n * @see {@link Rectangle.enlarge} For growing to include another rectangle\n * @see {@link Rectangle.pad} For adding padding around the rectangle\n */\n public fit(rectangle: Rectangle): this\n {\n const x1 = Math.max(this.x, rectangle.x);\n const x2 = Math.min(this.x + this.width, rectangle.x + rectangle.width);\n const y1 = Math.max(this.y, rectangle.y);\n const y2 = Math.min(this.y + this.height, rectangle.y + rectangle.height);\n\n this.x = x1;\n this.width = Math.max(x2 - x1, 0);\n this.y = y1;\n this.height = Math.max(y2 - y1, 0);\n\n return this;\n }\n\n /**\n * Enlarges rectangle so that its corners lie on a grid defined by resolution.\n * @example\n * ```ts\n * // Basic grid alignment\n * const rect = new Rectangle(10.2, 10.6, 100.8, 100.4);\n * rect.ceil(); // Aligns to whole pixels\n *\n * // Custom resolution grid\n * const uiRect = new Rectangle(5.3, 5.7, 50.2, 50.8);\n * uiRect.ceil(0.5); // Aligns to half pixels\n *\n * // Use with precision value\n * const preciseRect = new Rectangle(20.001, 20.999, 100.001, 100.999);\n * preciseRect.ceil(1, 0.01); // Handles small decimal variations\n * ```\n * @param resolution - The grid size to align to (1 = whole pixels)\n * @param eps - Small number to prevent floating point errors\n * @returns Returns itself\n * @see {@link Rectangle.fit} For constraining to bounds\n * @see {@link Rectangle.enlarge} For growing dimensions\n */\n public ceil(resolution = 1, eps = 0.001): this\n {\n const x2 = Math.ceil((this.x + this.width - eps) * resolution) / resolution;\n const y2 = Math.ceil((this.y + this.height - eps) * resolution) / resolution;\n\n this.x = Math.floor((this.x + eps) * resolution) / resolution;\n this.y = Math.floor((this.y + eps) * resolution) / resolution;\n\n this.width = x2 - this.x;\n this.height = y2 - this.y;\n\n return this;\n }\n\n /**\n * Scales the rectangle's dimensions and position by the specified factors.\n * @example\n * ```ts\n * const rect = new Rectangle(50, 50, 100, 100);\n *\n * // Scale uniformly\n * rect.scale(0.5, 0.5);\n * // rect is now: x=25, y=25, width=50, height=50\n *\n * // non-uniformly\n * rect.scale(0.5, 1);\n * // rect is now: x=25, y=50, width=50, height=100\n * ```\n * @param x - The factor by which to scale the horizontal properties (x, width).\n * @param y - The factor by which to scale the vertical properties (y, height).\n * @returns Returns itself\n */\n public scale(x: number, y: number = x): this\n {\n this.x *= x;\n this.y *= y;\n this.width *= x;\n this.height *= y;\n\n return this;\n }\n\n /**\n * Enlarges this rectangle to include the passed rectangle.\n * @example\n * ```ts\n * // Basic enlargement\n * const rect = new Rectangle(50, 50, 100, 100);\n * const other = new Rectangle(0, 0, 200, 75);\n * rect.enlarge(other);\n * // rect is now: x=0, y=0, width=200, height=150\n *\n * // Use for bounding box calculation\n * const bounds = new Rectangle();\n * objects.forEach((obj) => {\n * bounds.enlarge(obj.getBounds());\n * });\n * ```\n * @param rectangle - The rectangle to include\n * @returns Returns itself\n * @see {@link Rectangle.fit} For shrinking to fit within another rectangle\n * @see {@link Rectangle.pad} For adding padding around the rectangle\n */\n public enlarge(rectangle: Rectangle): this\n {\n const x1 = Math.min(this.x, rectangle.x);\n const x2 = Math.max(this.x + this.width, rectangle.x + rectangle.width);\n const y1 = Math.min(this.y, rectangle.y);\n const y2 = Math.max(this.y + this.height, rectangle.y + rectangle.height);\n\n this.x = x1;\n this.width = x2 - x1;\n this.y = y1;\n this.height = y2 - y1;\n\n return this;\n }\n\n /**\n * Returns the framing rectangle of the rectangle as a Rectangle object\n * @example\n * ```ts\n * // Basic bounds retrieval\n * const rect = new Rectangle(100, 100, 200, 150);\n * const bounds = rect.getBounds();\n *\n * // Reuse existing rectangle\n * const out = new Rectangle();\n * rect.getBounds(out);\n * ```\n * @param out - Optional rectangle to store the result\n * @returns The framing rectangle\n * @see {@link Rectangle.copyFrom} For direct copying\n * @see {@link Rectangle.clone} For creating new copy\n */\n public getBounds(out?: Rectangle): Rectangle\n {\n out ||= new Rectangle();\n out.copyFrom(this);\n\n return out;\n }\n\n /**\n * Determines whether another Rectangle is fully contained within this Rectangle.\n *\n * Rectangles that occupy the same space are considered to be containing each other.\n *\n * Rectangles without area (width or height equal to zero) can't contain anything,\n * not even other arealess rectangles.\n * @example\n * ```ts\n * // Check if one rectangle contains another\n * const container = new Rectangle(0, 0, 100, 100);\n * const inner = new Rectangle(25, 25, 50, 50);\n *\n * console.log(container.containsRect(inner)); // true\n *\n * // Check overlapping rectangles\n * const partial = new Rectangle(75, 75, 50, 50);\n * console.log(container.containsRect(partial)); // false\n *\n * // Zero-area rectangles\n * const empty = new Rectangle(0, 0, 0, 100);\n * console.log(container.containsRect(empty)); // false\n * ```\n * @param other - The Rectangle to check for containment\n * @returns True if other is fully contained within this Rectangle\n * @see {@link Rectangle.contains} For point containment\n * @see {@link Rectangle.intersects} For overlap testing\n */\n public containsRect(other: Rectangle): boolean\n {\n if (this.width <= 0 || this.height <= 0) return false;\n\n const x1 = other.x;\n const y1 = other.y;\n const x2 = other.x + other.width;\n const y2 = other.y + other.height;\n\n return x1 >= this.x && x1 < this.x + this.width\n && y1 >= this.y && y1 < this.y + this.height\n && x2 >= this.x && x2 < this.x + this.width\n && y2 >= this.y && y2 < this.y + this.height;\n }\n\n /**\n * Sets the position and dimensions of the rectangle.\n * @example\n * ```ts\n * // Basic usage\n * const rect = new Rectangle();\n * rect.set(100, 100, 200, 150);\n *\n * // Chain with other operations\n * const bounds = new Rectangle()\n * .set(0, 0, 100, 100)\n * .pad(10);\n * ```\n * @param x - The X coordinate of the upper-left corner of the rectangle\n * @param y - The Y coordinate of the upper-left corner of the rectangle\n * @param width - The overall width of the rectangle\n * @param height - The overall height of the rectangle\n * @returns Returns itself for method chaining\n * @see {@link Rectangle.copyFrom} For copying from another rectangle\n * @see {@link Rectangle.clone} For creating a new copy\n */\n public set(x: number, y: number, width: number, height: number): this\n {\n this.x = x;\n this.y = y;\n this.width = width;\n this.height = height;\n\n return this;\n }\n\n // #if _DEBUG\n public toString(): string\n {\n return `[pixi.js/math:Rectangle x=${this.x} y=${this.y} width=${this.width} height=${this.height}]`;\n }\n // #endif\n}\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { Rectangle } from '../../../maths/shapes/Rectangle';\n\n/**\n * A simple axis-aligned bounding box (AABB) data structure used to define rectangular boundaries.\n * Provides a clearer alternative to array-based bounds representation [minX, minY, maxX, maxY].\n * @example\n * ```ts\n * // Create bounds data\n * const bounds: BoundsData = {\n * minX: 0,\n * minY: 0,\n * maxX: 100,\n * maxY: 100\n * };\n *\n * // Calculate dimensions\n * const width = bounds.maxX - bounds.minX;\n * const height = bounds.maxY - bounds.minY;\n *\n * // Check if point is inside\n * const isInside = (x: number, y: number) =>\n * x >= bounds.minX && x <= bounds.maxX &&\n * y >= bounds.minY && y <= bounds.maxY;\n * ```\n * @see {@link Bounds} For full bounds implementation\n * @see {@link Container#getBounds} For getting bounds\n * @category rendering\n * @standard\n */\nexport interface BoundsData\n{\n /** The minimum X coordinate of the bounds */\n minX: number;\n /** The minimum Y coordinate of the bounds */\n minY: number;\n /** The maximum X coordinate of the bounds */\n maxX: number;\n /** The maximum Y coordinate of the bounds */\n maxY: number;\n}\n\nconst defaultMatrix = new Matrix();\n\n// TODO optimisations\n// 1 - get rectangle could use a dirty flag, rather than setting the data each time is called\n// 2- getFrame ALWAYS assumes a matrix, could be optimised to avoid the matrix calculation if not needed\n\n/**\n * A representation of an axis-aligned bounding box (AABB) used for efficient collision detection and culling.\n * Stores minimum and maximum coordinates to define a rectangular boundary.\n * @example\n * ```ts\n * // Create bounds\n * const bounds = new Bounds();\n *\n * // Add a rectangular frame\n * bounds.addFrame(0, 0, 100, 100);\n * console.log(bounds.width, bounds.height); // 100, 100\n *\n * // Transform bounds\n * const matrix = new Matrix()\n * .translate(50, 50)\n * .rotate(Math.PI / 4);\n * bounds.applyMatrix(matrix);\n *\n * // Check point intersection\n * if (bounds.containsPoint(75, 75)) {\n * console.log('Point is inside bounds!');\n * }\n * ```\n * @category rendering\n * @standard\n */\nexport class Bounds\n{\n /**\n * The minimum X coordinate of the bounds.\n * Represents the leftmost edge of the bounding box.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * // Set left edge\n * bounds.minX = 100;\n * ```\n * @default Infinity\n */\n public minX = Infinity;\n\n /**\n * The minimum Y coordinate of the bounds.\n * Represents the topmost edge of the bounding box.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * // Set top edge\n * bounds.minY = 100;\n * ```\n * @default Infinity\n */\n public minY = Infinity;\n\n /**\n * The maximum X coordinate of the bounds.\n * Represents the rightmost edge of the bounding box.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * // Set right edge\n * bounds.maxX = 200;\n * // Get width\n * const width = bounds.maxX - bounds.minX;\n * ```\n * @default -Infinity\n */\n public maxX = -Infinity;\n\n /**\n * The maximum Y coordinate of the bounds.\n * Represents the bottommost edge of the bounding box.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * // Set bottom edge\n * bounds.maxY = 200;\n * // Get height\n * const height = bounds.maxY - bounds.minY;\n * ```\n * @default -Infinity\n */\n public maxY = -Infinity;\n\n /**\n * The transformation matrix applied to this bounds object.\n * Used when calculating bounds with transforms.\n * @example\n * ```ts\n * const bounds = new Bounds();\n *\n * // Apply translation matrix\n * bounds.matrix = new Matrix()\n * .translate(100, 100);\n *\n * // Combine transformations\n * bounds.matrix = new Matrix()\n * .translate(50, 50)\n * .rotate(Math.PI / 4)\n * .scale(2, 2);\n *\n * // Use in bounds calculations\n * bounds.addFrame(0, 0, 100, 100); // Uses current matrix\n * bounds.addFrame(0, 0, 100, 100, customMatrix); // Override matrix\n * ```\n * @advanced\n */\n public matrix = defaultMatrix;\n\n private _rectangle: Rectangle;\n\n /**\n * Creates a new Bounds object.\n * @param minX - The minimum X coordinate of the bounds.\n * @param minY - The minimum Y coordinate of the bounds.\n * @param maxX - The maximum X coordinate of the bounds.\n * @param maxY - The maximum Y coordinate of the bounds.\n */\n constructor(minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity)\n {\n this.minX = minX;\n this.minY = minY;\n this.maxX = maxX;\n this.maxY = maxY;\n }\n\n /**\n * Checks if bounds are empty, meaning either width or height is zero or negative.\n * Empty bounds occur when min values exceed max values on either axis.\n * @example\n * ```ts\n * const bounds = new Bounds();\n *\n * // Check if newly created bounds are empty\n * console.log(bounds.isEmpty()); // true, default bounds are empty\n *\n * // Add frame and check again\n * bounds.addFrame(0, 0, 100, 100);\n * console.log(bounds.isEmpty()); // false, bounds now have area\n *\n * // Clear bounds\n * bounds.clear();\n * console.log(bounds.isEmpty()); // true, bounds are empty again\n * ```\n * @returns True if bounds are empty (have no area)\n * @see {@link Bounds#clear} For resetting bounds\n * @see {@link Bounds#isValid} For checking validity\n */\n public isEmpty(): boolean\n {\n return this.minX > this.maxX || this.minY > this.maxY;\n }\n\n /**\n * The bounding rectangle representation of these bounds.\n * Lazily creates and updates a Rectangle instance based on the current bounds.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n *\n * // Get rectangle representation\n * const rect = bounds.rectangle;\n * console.log(rect.x, rect.y, rect.width, rect.height);\n *\n * // Use for hit testing\n * if (bounds.rectangle.contains(mouseX, mouseY)) {\n * console.log('Mouse is inside bounds!');\n * }\n * ```\n * @see {@link Rectangle} For rectangle methods\n * @see {@link Bounds.isEmpty} For bounds validation\n */\n get rectangle(): Rectangle\n {\n if (!this._rectangle)\n {\n this._rectangle = new Rectangle();\n }\n\n const rectangle = this._rectangle;\n\n if (this.minX > this.maxX || this.minY > this.maxY)\n {\n rectangle.x = 0;\n rectangle.y = 0;\n rectangle.width = 0;\n rectangle.height = 0;\n }\n else\n {\n rectangle.copyFromBounds(this);\n }\n\n return rectangle;\n }\n\n /**\n * Clears the bounds and resets all coordinates to their default values.\n * Resets the transformation matrix back to identity.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * console.log(bounds.isEmpty()); // false\n * // Clear the bounds\n * bounds.clear();\n * console.log(bounds.isEmpty()); // true\n * ```\n * @returns This bounds object for chaining\n */\n public clear(): this\n {\n this.minX = Infinity;\n this.minY = Infinity;\n this.maxX = -Infinity;\n this.maxY = -Infinity;\n\n this.matrix = defaultMatrix;\n\n return this;\n }\n\n /**\n * Sets the bounds directly using coordinate values.\n * Provides a way to set all bounds values at once.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * bounds.set(0, 0, 100, 100);\n * ```\n * @param x0 - Left X coordinate of frame\n * @param y0 - Top Y coordinate of frame\n * @param x1 - Right X coordinate of frame\n * @param y1 - Bottom Y coordinate of frame\n * @see {@link Bounds#addFrame} For matrix-aware bounds setting\n * @see {@link Bounds#clear} For resetting bounds\n */\n public set(x0: number, y0: number, x1: number, y1: number)\n {\n this.minX = x0;\n this.minY = y0;\n this.maxX = x1;\n this.maxY = y1;\n }\n\n /**\n * Adds a rectangular frame to the bounds, optionally transformed by a matrix.\n * Updates the bounds to encompass the new frame coordinates.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * bounds.addFrame(0, 0, 100, 100);\n *\n * // Add transformed frame\n * const matrix = new Matrix()\n * .translate(50, 50)\n * .rotate(Math.PI / 4);\n * bounds.addFrame(0, 0, 100, 100, matrix);\n * ```\n * @param x0 - Left X coordinate of frame\n * @param y0 - Top Y coordinate of frame\n * @param x1 - Right X coordinate of frame\n * @param y1 - Bottom Y coordinate of frame\n * @param matrix - Optional transformation matrix\n * @see {@link Bounds#addRect} For adding Rectangle objects\n * @see {@link Bounds#addBounds} For adding other Bounds\n */\n public addFrame(x0: number, y0: number, x1: number, y1: number, matrix?: Matrix): void\n {\n matrix ||= this.matrix;\n\n const a = matrix.a;\n const b = matrix.b;\n const c = matrix.c;\n const d = matrix.d;\n const tx = matrix.tx;\n const ty = matrix.ty;\n\n let minX = this.minX;\n let minY = this.minY;\n let maxX = this.maxX;\n let maxY = this.maxY;\n\n let x = (a * x0) + (c * y0) + tx;\n let y = (b * x0) + (d * y0) + ty;\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n\n x = (a * x1) + (c * y0) + tx;\n y = (b * x1) + (d * y0) + ty;\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n\n x = (a * x0) + (c * y1) + tx;\n y = (b * x0) + (d * y1) + ty;\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n\n x = (a * x1) + (c * y1) + tx;\n y = (b * x1) + (d * y1) + ty;\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n\n this.minX = minX;\n this.minY = minY;\n this.maxX = maxX;\n this.maxY = maxY;\n }\n\n /**\n * Adds a rectangle to the bounds, optionally transformed by a matrix.\n * Updates the bounds to encompass the given rectangle.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * // Add simple rectangle\n * const rect = new Rectangle(0, 0, 100, 100);\n * bounds.addRect(rect);\n *\n * // Add transformed rectangle\n * const matrix = new Matrix()\n * .translate(50, 50)\n * .rotate(Math.PI / 4);\n * bounds.addRect(rect, matrix);\n * ```\n * @param rect - The rectangle to be added\n * @param matrix - Optional transformation matrix\n * @see {@link Bounds#addFrame} For adding raw coordinates\n * @see {@link Bounds#addBounds} For adding other bounds\n */\n public addRect(rect: Rectangle, matrix?: Matrix)\n {\n this.addFrame(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, matrix);\n }\n\n /**\n * Adds another bounds object to this one, optionally transformed by a matrix.\n * Expands the bounds to include the given bounds' area.\n * @example\n * ```ts\n * const bounds = new Bounds();\n *\n * // Add child bounds\n * const childBounds = sprite.getBounds();\n * bounds.addBounds(childBounds);\n *\n * // Add transformed bounds\n * const matrix = new Matrix()\n * .scale(2, 2);\n * bounds.addBounds(childBounds, matrix);\n * ```\n * @param bounds - The bounds to be added\n * @param matrix - Optional transformation matrix\n * @see {@link Bounds#addFrame} For adding raw coordinates\n * @see {@link Bounds#addRect} For adding rectangles\n */\n public addBounds(bounds: BoundsData, matrix?: Matrix)\n {\n this.addFrame(bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, matrix);\n }\n\n /**\n * Adds other Bounds as a mask, creating an intersection of the two bounds.\n * Only keeps the overlapping region between current bounds and mask bounds.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Create mask bounds\n * const mask = new Bounds();\n * mask.addFrame(50, 50, 150, 150);\n * // Apply mask - results in bounds of (50,50,100,100)\n * bounds.addBoundsMask(mask);\n * ```\n * @param mask - The Bounds to use as a mask\n * @see {@link Bounds#addBounds} For union operation\n * @see {@link Bounds#fit} For fitting to rectangle\n */\n public addBoundsMask(mask: Bounds): void\n {\n this.minX = this.minX > mask.minX ? this.minX : mask.minX;\n this.minY = this.minY > mask.minY ? this.minY : mask.minY;\n this.maxX = this.maxX < mask.maxX ? this.maxX : mask.maxX;\n this.maxY = this.maxY < mask.maxY ? this.maxY : mask.maxY;\n }\n\n /**\n * Applies a transformation matrix to the bounds, updating its coordinates.\n * Transforms all corners of the bounds using the given matrix.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Apply translation\n * const translateMatrix = new Matrix()\n * .translate(50, 50);\n * bounds.applyMatrix(translateMatrix);\n * ```\n * @param matrix - The matrix to apply to the bounds\n * @see {@link Matrix} For matrix operations\n * @see {@link Bounds#addFrame} For adding transformed frames\n */\n public applyMatrix(matrix: Matrix): void\n {\n const minX = this.minX;\n const minY = this.minY;\n const maxX = this.maxX;\n const maxY = this.maxY;\n\n // multiple bounds by matrix\n const { a, b, c, d, tx, ty } = matrix;\n\n let x = (a * minX) + (c * minY) + tx;\n let y = (b * minX) + (d * minY) + ty;\n\n this.minX = x;\n this.minY = y;\n this.maxX = x;\n this.maxY = y;\n\n x = (a * maxX) + (c * minY) + tx;\n y = (b * maxX) + (d * minY) + ty;\n this.minX = x < this.minX ? x : this.minX;\n this.minY = y < this.minY ? y : this.minY;\n this.maxX = x > this.maxX ? x : this.maxX;\n this.maxY = y > this.maxY ? y : this.maxY;\n\n x = (a * minX) + (c * maxY) + tx;\n y = (b * minX) + (d * maxY) + ty;\n this.minX = x < this.minX ? x : this.minX;\n this.minY = y < this.minY ? y : this.minY;\n this.maxX = x > this.maxX ? x : this.maxX;\n this.maxY = y > this.maxY ? y : this.maxY;\n\n x = (a * maxX) + (c * maxY) + tx;\n y = (b * maxX) + (d * maxY) + ty;\n this.minX = x < this.minX ? x : this.minX;\n this.minY = y < this.minY ? y : this.minY;\n this.maxX = x > this.maxX ? x : this.maxX;\n this.maxY = y > this.maxY ? y : this.maxY;\n }\n\n /**\n * Resizes the bounds object to fit within the given rectangle.\n * Clips the bounds if they extend beyond the rectangle's edges.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 200, 200);\n * // Fit within viewport\n * const viewport = new Rectangle(50, 50, 100, 100);\n * bounds.fit(viewport);\n * // bounds are now (50, 50, 150, 150)\n * ```\n * @param rect - The rectangle to fit within\n * @returns This bounds object for chaining\n * @see {@link Bounds#addBoundsMask} For intersection\n * @see {@link Bounds#pad} For expanding bounds\n */\n public fit(rect: Rectangle): this\n {\n if (this.minX < rect.left) this.minX = rect.left;\n if (this.maxX > rect.right) this.maxX = rect.right;\n\n if (this.minY < rect.top) this.minY = rect.top;\n if (this.maxY > rect.bottom) this.maxY = rect.bottom;\n\n return this;\n }\n\n /**\n * Resizes the bounds object to include the given bounds.\n * Similar to fit() but works with raw coordinate values instead of a Rectangle.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 200, 200);\n * // Fit to specific coordinates\n * bounds.fitBounds(50, 150, 50, 150);\n * // bounds are now (50, 50, 150, 150)\n * ```\n * @param left - The left value of the bounds\n * @param right - The right value of the bounds\n * @param top - The top value of the bounds\n * @param bottom - The bottom value of the bounds\n * @returns This bounds object for chaining\n * @see {@link Bounds#fit} For fitting to Rectangle\n * @see {@link Bounds#addBoundsMask} For intersection\n */\n public fitBounds(left: number, right: number, top: number, bottom: number): this\n {\n if (this.minX < left) this.minX = left;\n if (this.maxX > right) this.maxX = right;\n\n if (this.minY < top) this.minY = top;\n if (this.maxY > bottom) this.maxY = bottom;\n\n return this;\n }\n\n /**\n * Pads bounds object, making it grow in all directions.\n * If paddingY is omitted, both paddingX and paddingY will be set to paddingX.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n *\n * // Add equal padding\n * bounds.pad(10);\n * // bounds are now (-10, -10, 110, 110)\n *\n * // Add different padding for x and y\n * bounds.pad(20, 10);\n * // bounds are now (-30, -20, 130, 120)\n * ```\n * @param paddingX - The horizontal padding amount\n * @param paddingY - The vertical padding amount\n * @returns This bounds object for chaining\n * @see {@link Bounds#fit} For constraining bounds\n * @see {@link Bounds#scale} For uniform scaling\n */\n public pad(paddingX: number, paddingY: number = paddingX): this\n {\n this.minX -= paddingX;\n this.maxX += paddingX;\n\n this.minY -= paddingY;\n this.maxY += paddingY;\n\n return this;\n }\n\n /**\n * Ceils the bounds by rounding up max values and rounding down min values.\n * Useful for pixel-perfect calculations and avoiding fractional pixels.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * bounds.set(10.2, 10.9, 50.1, 50.8);\n *\n * // Round to whole pixels\n * bounds.ceil();\n * // bounds are now (10, 10, 51, 51)\n * ```\n * @returns This bounds object for chaining\n * @see {@link Bounds#scale} For size adjustments\n * @see {@link Bounds#fit} For constraining bounds\n */\n public ceil(): this\n {\n this.minX = Math.floor(this.minX);\n this.minY = Math.floor(this.minY);\n this.maxX = Math.ceil(this.maxX);\n this.maxY = Math.ceil(this.maxY);\n\n return this;\n }\n\n /**\n * Creates a new Bounds instance with the same values.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n *\n * // Create a copy\n * const copy = bounds.clone();\n *\n * // Original and copy are independent\n * bounds.pad(10);\n * console.log(copy.width === bounds.width); // false\n * ```\n * @returns A new Bounds instance with the same values\n * @see {@link Bounds#copyFrom} For reusing existing bounds\n */\n public clone(): Bounds\n {\n return new Bounds(this.minX, this.minY, this.maxX, this.maxY);\n }\n\n /**\n * Scales the bounds by the given values, adjusting all edges proportionally.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n *\n * // Scale uniformly\n * bounds.scale(2);\n * // bounds are now (0, 0, 200, 200)\n *\n * // Scale non-uniformly\n * bounds.scale(0.5, 2);\n * // bounds are now (0, 0, 100, 400)\n * ```\n * @param x - The X value to scale by\n * @param y - The Y value to scale by (defaults to x)\n * @returns This bounds object for chaining\n * @see {@link Bounds#pad} For adding padding\n * @see {@link Bounds#fit} For constraining size\n */\n public scale(x: number, y: number = x): this\n {\n this.minX *= x;\n this.minY *= y;\n this.maxX *= x;\n this.maxY *= y;\n\n return this;\n }\n\n /**\n * The x position of the bounds in local space.\n * Setting this value will move the bounds while maintaining its width.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Get x position\n * console.log(bounds.x); // 0\n *\n * // Move bounds horizontally\n * bounds.x = 50;\n * console.log(bounds.minX, bounds.maxX); // 50, 150\n *\n * // Width stays the same\n * console.log(bounds.width); // Still 100\n * ```\n */\n get x(): number\n {\n return this.minX;\n }\n set x(value: number)\n {\n const width = this.maxX - this.minX;\n\n this.minX = value;\n this.maxX = value + width;\n }\n\n /**\n * The y position of the bounds in local space.\n * Setting this value will move the bounds while maintaining its height.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Get y position\n * console.log(bounds.y); // 0\n *\n * // Move bounds vertically\n * bounds.y = 50;\n * console.log(bounds.minY, bounds.maxY); // 50, 150\n *\n * // Height stays the same\n * console.log(bounds.height); // Still 100\n * ```\n */\n get y(): number\n {\n return this.minY;\n }\n\n set y(value: number)\n {\n const height = this.maxY - this.minY;\n\n this.minY = value;\n this.maxY = value + height;\n }\n\n /**\n * The width value of the bounds.\n * Represents the distance between minX and maxX coordinates.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Get width\n * console.log(bounds.width); // 100\n * // Resize width\n * bounds.width = 200;\n * console.log(bounds.maxX - bounds.minX); // 200\n * ```\n */\n get width(): number\n {\n return this.maxX - this.minX;\n }\n\n set width(value: number)\n {\n this.maxX = this.minX + value;\n }\n\n /**\n * The height value of the bounds.\n * Represents the distance between minY and maxY coordinates.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Get height\n * console.log(bounds.height); // 100\n * // Resize height\n * bounds.height = 150;\n * console.log(bounds.maxY - bounds.minY); // 150\n * ```\n */\n get height(): number\n {\n return this.maxY - this.minY;\n }\n\n set height(value: number)\n {\n this.maxY = this.minY + value;\n }\n\n /**\n * The left edge coordinate of the bounds.\n * Alias for minX.\n * @example\n * ```ts\n * const bounds = new Bounds(50, 0, 150, 100);\n * console.log(bounds.left); // 50\n * console.log(bounds.left === bounds.minX); // true\n * ```\n * @readonly\n */\n get left(): number\n {\n return this.minX;\n }\n\n /**\n * The right edge coordinate of the bounds.\n * Alias for maxX.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * console.log(bounds.right); // 100\n * console.log(bounds.right === bounds.maxX); // true\n * ```\n * @readonly\n */\n get right(): number\n {\n return this.maxX;\n }\n\n /**\n * The top edge coordinate of the bounds.\n * Alias for minY.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 25, 100, 125);\n * console.log(bounds.top); // 25\n * console.log(bounds.top === bounds.minY); // true\n * ```\n * @readonly\n */\n get top(): number\n {\n return this.minY;\n }\n\n /**\n * The bottom edge coordinate of the bounds.\n * Alias for maxY.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 200);\n * console.log(bounds.bottom); // 200\n * console.log(bounds.bottom === bounds.maxY); // true\n * ```\n * @readonly\n */\n get bottom(): number\n {\n return this.maxY;\n }\n\n /**\n * Whether the bounds has positive width and height.\n * Checks if both dimensions are greater than zero.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Check if bounds are positive\n * console.log(bounds.isPositive); // true\n *\n * // Negative bounds\n * bounds.maxX = bounds.minX;\n * console.log(bounds.isPositive); // false, width is 0\n * ```\n * @readonly\n * @see {@link Bounds#isEmpty} For checking empty state\n * @see {@link Bounds#isValid} For checking validity\n */\n get isPositive(): boolean\n {\n return (this.maxX - this.minX > 0) && (this.maxY - this.minY > 0);\n }\n\n /**\n * Whether the bounds has valid coordinates.\n * Checks if the bounds has been initialized with real values.\n * @example\n * ```ts\n * const bounds = new Bounds();\n * console.log(bounds.isValid); // false, default state\n *\n * // Set valid bounds\n * bounds.addFrame(0, 0, 100, 100);\n * console.log(bounds.isValid); // true\n * ```\n * @readonly\n * @see {@link Bounds#isEmpty} For checking empty state\n * @see {@link Bounds#isPositive} For checking dimensions\n */\n get isValid(): boolean\n {\n return (this.minX + this.minY !== Infinity);\n }\n\n /**\n * Adds vertices from a Float32Array to the bounds, optionally transformed by a matrix.\n * Used for efficiently updating bounds from raw vertex data.\n * @example\n * ```ts\n * const bounds = new Bounds();\n *\n * // Add vertices from geometry\n * const vertices = new Float32Array([\n * 0, 0, // Vertex 1\n * 100, 0, // Vertex 2\n * 100, 100 // Vertex 3\n * ]);\n * bounds.addVertexData(vertices, 0, 6);\n *\n * // Add transformed vertices\n * const matrix = new Matrix()\n * .translate(50, 50)\n * .rotate(Math.PI / 4);\n * bounds.addVertexData(vertices, 0, 6, matrix);\n *\n * // Add subset of vertices\n * bounds.addVertexData(vertices, 2, 4); // Only second vertex\n * ```\n * @param vertexData - The array of vertices to add\n * @param beginOffset - Starting index in the vertex array\n * @param endOffset - Ending index in the vertex array (excluded)\n * @param matrix - Optional transformation matrix\n * @see {@link Bounds#addFrame} For adding rectangular frames\n * @see {@link Matrix} For transformation details\n */\n public addVertexData(vertexData: Float32Array, beginOffset: number, endOffset: number, matrix?: Matrix): void\n {\n let minX = this.minX;\n let minY = this.minY;\n let maxX = this.maxX;\n let maxY = this.maxY;\n\n matrix ||= this.matrix;\n\n const a = matrix.a;\n const b = matrix.b;\n const c = matrix.c;\n const d = matrix.d;\n const tx = matrix.tx;\n const ty = matrix.ty;\n\n for (let i = beginOffset; i < endOffset; i += 2)\n {\n const localX = vertexData[i];\n const localY = vertexData[i + 1];\n\n const x = (a * localX) + (c * localY) + tx;\n const y = (b * localX) + (d * localY) + ty;\n\n minX = x < minX ? x : minX;\n minY = y < minY ? y : minY;\n maxX = x > maxX ? x : maxX;\n maxY = y > maxY ? y : maxY;\n }\n\n this.minX = minX;\n this.minY = minY;\n this.maxX = maxX;\n this.maxY = maxY;\n }\n\n /**\n * Checks if a point is contained within the bounds.\n * Returns true if the point's coordinates fall within the bounds' area.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * // Basic point check\n * console.log(bounds.containsPoint(50, 50)); // true\n * console.log(bounds.containsPoint(150, 150)); // false\n *\n * // Check edges\n * console.log(bounds.containsPoint(0, 0)); // true, includes edges\n * console.log(bounds.containsPoint(100, 100)); // true, includes edges\n * ```\n * @param x - x coordinate to check\n * @param y - y coordinate to check\n * @returns True if the point is inside the bounds\n * @see {@link Bounds#isPositive} For valid bounds check\n * @see {@link Bounds#rectangle} For Rectangle representation\n */\n public containsPoint(x: number, y: number): boolean\n {\n if (this.minX <= x && this.minY <= y && this.maxX >= x && this.maxY >= y)\n {\n return true;\n }\n\n return false;\n }\n\n /**\n * Returns a string representation of the bounds.\n * Useful for debugging and logging bounds information.\n * @example\n * ```ts\n * const bounds = new Bounds(0, 0, 100, 100);\n * console.log(bounds.toString()); // \"[pixi.js:Bounds minX=0 minY=0 maxX=100 maxY=100 width=100 height=100]\"\n * ```\n * @returns A string describing the bounds\n * @see {@link Bounds#copyFrom} For copying bounds\n * @see {@link Bounds#clone} For creating a new instance\n */\n public toString(): string\n {\n // eslint-disable-next-line max-len\n return `[pixi.js:Bounds minX=${this.minX} minY=${this.minY} maxX=${this.maxX} maxY=${this.maxY} width=${this.width} height=${this.height}]`;\n }\n\n /**\n * Copies the bounds from another bounds object.\n * Useful for reusing bounds objects and avoiding allocations.\n * @example\n * ```ts\n * const sourceBounds = new Bounds(0, 0, 100, 100);\n * // Copy bounds\n * const targetBounds = new Bounds();\n * targetBounds.copyFrom(sourceBounds);\n * ```\n * @param bounds - The bounds to copy from\n * @returns This bounds object for chaining\n * @see {@link Bounds#clone} For creating new instances\n */\n public copyFrom(bounds: Bounds): this\n {\n this.minX = bounds.minX;\n this.minY = bounds.minY;\n this.maxX = bounds.maxX;\n this.maxY = bounds.maxY;\n\n return this;\n }\n}\n\n","import { Matrix } from '../../../../maths/matrix/Matrix';\nimport { Pool } from '../../../../utils/pool/Pool';\nimport { Bounds } from '../Bounds';\n\nimport type { PoolItem } from '../../../../utils/pool/Pool';\n\ntype MatrixPoolItem = Matrix & PoolItem;\ntype BoundsPoolItem = Bounds & PoolItem;\n/** @internal */\nexport const matrixPool = new Pool(Matrix);\n/** @internal */\nexport const boundsPool = new Pool(Bounds);\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { type Renderable } from '../../../rendering/renderers/shared/Renderable';\nimport { type IRenderLayer } from '../../layers/RenderLayer';\nimport { Bounds } from '../bounds/Bounds';\nimport { boundsPool } from '../bounds/utils/matrixAndBoundsPool';\n\nimport type { Container } from '../Container';\n\nconst tempMatrix = new Matrix();\n\n/**\n * Interface for the GetFastGlobalBoundsMixin, which provides methods to compute\n * an approximate global bounding box for a container and its children.\n * @category scene\n * @advanced\n */\nexport interface GetFastGlobalBoundsMixin\n{\n /**\n * Computes an approximate global bounding box for the container and its children.\n * This method is optimized for speed by using axis-aligned bounding boxes (AABBs),\n * and uses the last render results from when it updated the transforms. This function does not update them.\n * which may result in slightly larger bounds but never smaller than the actual bounds.\n *\n * for accurate (but less performant) results use `container.getGlobalBounds`\n * @param {boolean} [factorRenderLayers] - A flag indicating whether to consider render layers in the calculation.\n * @param {Bounds} [bounds] - The output bounds object to store the result. If not provided, a new one is created.\n * @returns {Bounds} The computed bounds.\n * @advanced\n */\n getFastGlobalBounds(factorRenderLayers?: boolean, bounds?: Bounds): Bounds;\n\n /**\n * Recursively calculates the global bounds for the container and its children.\n * This method is used internally by getFastGlobalBounds to traverse the scene graph.\n * @param {boolean} factorRenderLayers - A flag indicating whether to consider render layers in the calculation.\n * @param {Bounds} bounds - The bounds object to update with the calculated values.\n * @param {IRenderLayer} currentLayer - The current render layer being processed.\n * @internal\n */\n _getGlobalBoundsRecursive(\n factorRenderLayers: boolean,\n bounds: Bounds,\n currentLayer: IRenderLayer,\n ): void;\n}\n\n/**\n * Mixin providing the implementation of the GetFastGlobalBoundsMixin interface.\n * It includes methods to compute and recursively calculate global bounds for containers.\n * @internal\n */\nexport const getFastGlobalBoundsMixin: Partial = {\n getFastGlobalBounds(factorRenderLayers?: boolean, bounds?: Bounds): Bounds\n {\n bounds ||= new Bounds();\n\n // Initialize the bounds for fresh calculations.\n bounds.clear();\n\n // Calculate bounds recursively, starting from the current container.\n this._getGlobalBoundsRecursive(!!factorRenderLayers, bounds, this.parentRenderLayer);\n\n // Validate the calculated bounds, resetting if invalid.\n if (!bounds.isValid)\n {\n bounds.set(0, 0, 0, 0);\n }\n\n // Apply the world transformation to the bounds.\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n bounds.applyMatrix(renderGroup.worldTransform);\n\n return bounds;\n },\n\n _getGlobalBoundsRecursive(\n factorRenderLayers: boolean,\n bounds: Bounds,\n currentLayer: IRenderLayer,\n )\n {\n let localBounds = bounds;\n\n // Skip if the container is not in the current render layer when factoring render layers.\n if (factorRenderLayers && this.parentRenderLayer && this.parentRenderLayer !== currentLayer) return;\n\n // Skip if the container is not fully visible or not measurable.\n if (this.localDisplayStatus !== 0b111 || (!this.measurable))\n {\n return;\n }\n\n // Determine if effects need to be managed, requiring separate bounds handling.\n const manageEffects = !!this.effects.length;\n\n // Use a temporary bounds object if the container is a render group or has effects.\n if (this.renderGroup || manageEffects)\n {\n localBounds = boundsPool.get().clear();\n }\n\n // Add the container's own bounds area to the bounds if it exists.\n if (this.boundsArea)\n {\n bounds.addRect(this.boundsArea, this.worldTransform);\n }\n else\n {\n // If the container is renderable, add its bounds to the local bounds.\n if (this.renderPipeId)\n {\n const viewBounds = (this as Renderable).bounds;\n\n localBounds.addFrame(\n viewBounds.minX,\n viewBounds.minY,\n viewBounds.maxX,\n viewBounds.maxY,\n this.groupTransform\n );\n }\n\n // Recursively process each child to include their bounds.\n const children = this.children;\n\n for (let i = 0; i < children.length; i++)\n {\n children[i]._getGlobalBoundsRecursive(factorRenderLayers, localBounds, currentLayer);\n }\n }\n\n // If effects are managed, apply them to the bounds.\n if (manageEffects)\n {\n let advanced = false;\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n // Apply each effect that modifies bounds.\n for (let i = 0; i < this.effects.length; i++)\n {\n if (this.effects[i].addBounds)\n {\n if (!advanced)\n {\n advanced = true;\n localBounds.applyMatrix(renderGroup.worldTransform);\n }\n this.effects[i].addBounds(localBounds, true);\n }\n }\n\n // Adjust bounds back to the local coordinate space if advanced bounds were calculated.\n if (advanced)\n {\n localBounds.applyMatrix(renderGroup.worldTransform.copyTo(tempMatrix).invert());\n }\n\n // Add the local bounds to the final bounds and return the temporary bounds object.\n bounds.addBounds(localBounds);\n boundsPool.return(localBounds);\n }\n else if (this.renderGroup)\n {\n // If the container is a render group, add its local bounds to the final bounds.\n bounds.addBounds(localBounds, this.relativeGroupTransform);\n boundsPool.return(localBounds);\n }\n }\n\n} as Container;\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { boundsPool, matrixPool } from './utils/matrixAndBoundsPool';\n\nimport type { Renderable } from '../../../rendering/renderers/shared/Renderable';\nimport type { Container } from '../Container';\nimport type { Bounds } from './Bounds';\n\n/**\n * Gets the global bounds of a container, including all its children\n * @param target - The target container to get the bounds from\n * @param skipUpdateTransform - If true, the transform will not be updated before calculating bounds.\n * @param bounds - The output bounds object.\n * @returns The bounds.\n * @internal\n */\nexport function getGlobalBounds(target: Container, skipUpdateTransform: boolean, bounds: Bounds): Bounds\n{\n bounds.clear();\n\n let parentTransform;\n let pooledMatrix;\n\n if (target.parent)\n {\n if (!skipUpdateTransform)\n {\n pooledMatrix = matrixPool.get().identity();\n parentTransform = updateTransformBackwards(target, pooledMatrix);\n }\n else\n {\n parentTransform = target.parent.worldTransform;\n }\n }\n else\n {\n parentTransform = Matrix.IDENTITY;\n }\n\n // then collect them...\n\n _getGlobalBounds(target, bounds, parentTransform, skipUpdateTransform);\n\n if (pooledMatrix)\n {\n matrixPool.return(pooledMatrix);\n }\n\n if (!bounds.isValid)\n {\n bounds.set(0, 0, 0, 0);\n }\n\n return bounds;\n}\n\nfunction _getGlobalBounds(\n target: Container,\n bounds: Bounds,\n parentTransform: Matrix,\n skipUpdateTransform: boolean,\n): void\n{\n if (!target.visible || !target.measurable) return;\n\n let worldTransform: Matrix;\n\n if (!skipUpdateTransform)\n {\n target.updateLocalTransform();\n\n worldTransform = matrixPool.get();\n\n worldTransform.appendFrom(target.localTransform, parentTransform);\n }\n else\n {\n worldTransform = target.worldTransform;\n }\n\n const parentBounds = bounds;\n const preserveBounds = !!target.effects.length;\n\n if (preserveBounds)\n {\n bounds = boundsPool.get().clear();\n }\n\n if (target.boundsArea)\n {\n bounds.addRect(target.boundsArea, worldTransform);\n }\n else\n {\n if ((target as Renderable).bounds)\n {\n // save a copy\n bounds.matrix = worldTransform;\n bounds.addBounds((target as Renderable).bounds);\n }\n\n for (let i = 0; i < target.children.length; i++)\n {\n _getGlobalBounds(target.children[i], bounds, worldTransform, skipUpdateTransform);\n }\n }\n\n if (preserveBounds)\n {\n for (let i = 0; i < target.effects.length; i++)\n {\n target.effects[i].addBounds?.(bounds);\n }\n\n parentBounds.addBounds(bounds, Matrix.IDENTITY);\n\n boundsPool.return(bounds);\n }\n\n if (!skipUpdateTransform)\n {\n matrixPool.return(worldTransform);\n }\n}\n\n/**\n * @param target\n * @param parentTransform\n * @internal\n */\nexport function updateTransformBackwards(target: Container, parentTransform: Matrix)\n{\n const parent = target.parent;\n\n if (parent)\n {\n updateTransformBackwards(parent, parentTransform);\n\n parent.updateLocalTransform();\n\n parentTransform.append(parent.localTransform);\n }\n\n return parentTransform;\n}\n","/**\n * @param color1\n * @param color2\n * @internal\n */\nexport function multiplyHexColors(color1: number, color2: number): number\n{\n if (color1 === 0xFFFFFF || !color2) return color2;\n if (color2 === 0xFFFFFF || !color1) return color1;\n\n const r1 = (color1 >> 16) & 0xFF;\n const g1 = (color1 >> 8) & 0xFF;\n const b1 = color1 & 0xFF;\n\n const r2 = (color2 >> 16) & 0xFF;\n const g2 = (color2 >> 8) & 0xFF;\n const b2 = color2 & 0xFF;\n\n const r = ((r1 * r2) / 255) | 0;\n const g = ((g1 * g2) / 255) | 0;\n const b = ((b1 * b2) / 255) | 0;\n\n return (r << 16) + (g << 8) + b;\n}\n","import { multiplyHexColors } from './multiplyHexColors';\n\nconst WHITE_BGR = 0xFFFFFF;\n\n/**\n * @param localBGRColor\n * @param parentBGRColor\n * @internal\n */\nexport function multiplyColors(localBGRColor: number, parentBGRColor: number)\n{\n if (localBGRColor === WHITE_BGR)\n {\n return parentBGRColor;\n }\n\n if (parentBGRColor === WHITE_BGR)\n {\n return localBGRColor;\n }\n\n return multiplyHexColors(localBGRColor, parentBGRColor);\n}\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { updateTransformBackwards } from '../bounds/getGlobalBounds';\nimport { matrixPool } from '../bounds/utils/matrixAndBoundsPool';\nimport { multiplyColors } from '../utils/multiplyColors';\n\nimport type { Container } from '../Container';\n\n/**\n * Converts a color from BGR format to RGB format.\n * @param color - The color in BGR format (0xBBGGRR).\n * @returns The color in RGB format (0xRRGGBB).\n * @category utils\n * @internal\n */\nexport function bgr2rgb(color: number): number\n{\n return ((color & 0xFF) << 16) + (color & 0xFF00) + ((color >> 16) & 0xFF);\n}\n\n/**\n * Interface for a mixin that provides methods to retrieve global properties of a container.\n * This mixin allows you to get the global alpha, transform matrix, and tint color of a container,\n * taking into account its parent containers and render groups.\n * It includes methods to optimize performance by using cached values when available.\n * @category scene\n * @advanced\n */\nexport interface GetGlobalMixin\n{\n /**\n * Returns the global (compound) alpha of the container within the scene.\n * @param {boolean} skipUpdate - Performance optimization flag:\n * - If false (default): Recalculates the entire alpha chain through parents for accuracy\n * - If true: Uses cached worldAlpha from the last render pass for better performance\n * @returns The resulting alpha value (between 0 and 1)\n * @example\n * ```ts\n * // Accurate but slower - recalculates entire alpha chain\n * const preciseAlpha = container.getGlobalAlpha();\n *\n * // Faster but may be outdated - uses cached alpha\n * const cachedAlpha = container.getGlobalAlpha(true);\n * ```\n */\n getGlobalAlpha(skipUpdate?: boolean): number;\n /**\n * Returns the global transform matrix of the container within the scene.\n * @param {Matrix} matrix - Optional matrix to store the result. If not provided, a new Matrix will be created.\n * @param {boolean} skipUpdate - Performance optimization flag:\n * - If false (default): Recalculates the entire transform chain for accuracy\n * - If true: Uses cached worldTransform from the last render pass for better performance\n * @returns The resulting transformation matrix (either the input matrix or a new one)\n * @example\n * ```ts\n * // Accurate but slower - recalculates entire transform chain\n * const preciseTransform = container.getGlobalTransform();\n *\n * // Faster but may be outdated - uses cached transform\n * const cachedTransform = container.getGlobalTransform(undefined, true);\n *\n * // Reuse existing matrix\n * const existingMatrix = new Matrix();\n * container.getGlobalTransform(existingMatrix);\n * ```\n */\n getGlobalTransform(matrix?: Matrix, skipUpdate?: boolean): Matrix;\n /**\n * Returns the global (compound) tint color of the container within the scene.\n * @param {boolean} skipUpdate - Performance optimization flag:\n * - If false (default): Recalculates the entire tint chain through parents for accuracy\n * - If true: Uses cached worldColor from the last render pass for better performance\n * @returns The resulting tint color as a 24-bit RGB number (0xRRGGBB)\n * @example\n * ```ts\n * // Accurate but slower - recalculates entire tint chain\n * const preciseTint = container.getGlobalTint();\n *\n * // Faster but may be outdated - uses cached tint\n * const cachedTint = container.getGlobalTint(true);\n * ```\n */\n getGlobalTint(skipUpdate?: boolean): number;\n}\n\n/** @internal */\nexport const getGlobalMixin: Partial = {\n getGlobalAlpha(skipUpdate?: boolean): number\n {\n if (skipUpdate)\n {\n if (this.renderGroup)\n {\n return this.renderGroup.worldAlpha;\n }\n\n if (this.parentRenderGroup)\n {\n return this.parentRenderGroup.worldAlpha * this.alpha;\n }\n\n return this.alpha;\n }\n\n let alpha = this.alpha;\n let current = this.parent;\n\n while (current)\n {\n alpha *= current.alpha;\n current = current.parent;\n }\n\n return alpha;\n },\n getGlobalTransform(matrix = new Matrix(), skipUpdate?: boolean): Matrix\n {\n if (skipUpdate)\n {\n return matrix.copyFrom(this.worldTransform);\n }\n\n this.updateLocalTransform();\n\n const parentTransform = updateTransformBackwards(this, matrixPool.get().identity());\n\n matrix.appendFrom(this.localTransform, parentTransform);\n matrixPool.return(parentTransform);\n\n return matrix;\n },\n getGlobalTint(skipUpdate?: boolean): number\n {\n if (skipUpdate)\n {\n if (this.renderGroup)\n {\n return bgr2rgb(this.renderGroup.worldColor);\n }\n\n if (this.parentRenderGroup)\n {\n return bgr2rgb(\n multiplyColors(this.localColor, this.parentRenderGroup.worldColor)\n );\n }\n\n return this.tint;\n }\n\n let color = this.localColor;\n let parent = this.parent;\n\n while (parent)\n {\n color = multiplyColors(color, parent.localColor);\n parent = parent.parent;\n }\n\n return bgr2rgb(color);\n }\n\n} as Container;\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { boundsPool, matrixPool } from './utils/matrixAndBoundsPool';\n\nimport type { Renderable } from '../../../rendering/renderers/shared/Renderable';\nimport type { Container } from '../Container';\nimport type { Bounds } from './Bounds';\n\n/**\n * @param target\n * @param bounds\n * @param relativeMatrix\n * @internal\n */\nexport function getLocalBounds(target: Container, bounds: Bounds, relativeMatrix?: Matrix): Bounds\n{\n bounds.clear();\n\n relativeMatrix ||= Matrix.IDENTITY;\n\n _getLocalBounds(target, bounds, relativeMatrix, target, true);\n\n if (!bounds.isValid)\n {\n bounds.set(0, 0, 0, 0);\n }\n\n return bounds;\n}\n\nfunction _getLocalBounds(\n target: Container,\n bounds: Bounds,\n parentTransform: Matrix,\n rootContainer: Container,\n isRoot: boolean\n): void\n{\n let relativeTransform: Matrix;\n\n if (!isRoot)\n {\n if (!target.visible || !target.measurable) return;\n\n target.updateLocalTransform();\n\n const localTransform = target.localTransform;\n\n relativeTransform = matrixPool.get();\n relativeTransform.appendFrom(localTransform, parentTransform);\n }\n else\n {\n relativeTransform = matrixPool.get();\n relativeTransform = parentTransform.copyTo(relativeTransform);\n }\n\n const parentBounds = bounds;\n const preserveBounds = !!target.effects.length;\n\n if (preserveBounds)\n {\n bounds = boundsPool.get().clear();\n }\n\n if (target.boundsArea)\n {\n bounds.addRect(target.boundsArea, relativeTransform);\n }\n else\n {\n if (target.renderPipeId)\n {\n bounds.matrix = relativeTransform;\n bounds.addBounds((target as Renderable).bounds);\n }\n\n const children = target.children;\n\n for (let i = 0; i < children.length; i++)\n {\n _getLocalBounds(children[i], bounds, relativeTransform, rootContainer, false);\n }\n }\n\n if (preserveBounds)\n {\n for (let i = 0; i < target.effects.length; i++)\n {\n target.effects[i].addLocalBounds?.(bounds, rootContainer);\n }\n\n // TODO - make a add transformed bounds?\n parentBounds.addBounds(bounds, Matrix.IDENTITY);\n\n boundsPool.return(bounds);\n }\n\n matrixPool.return(relativeTransform);\n}\n\n","import type { Container } from '../Container';\n\n/**\n * This function will crawl through the container essentially check if the children have changed.\n *\n * This function checkChildrenDidChange recursively checks if any child in a Container\n * or its children has changed. It does this by comparing a generated changeId for each\n * child against a stored value in previousData.\n * The changeId is a combination of the child's uid and _didChangeId, bitwise manipulated for uniqueness.\n * If a change is detected, it updates previousData and sets didChange to true.\n * The function returns a boolean indicating if any change was detected in the entire hierarchy of children.\n * @param container - the container to check for changes\n * @param previousData - the previous data from the last check made\n * @param previousData.data - the data array\n * @param previousData.index - the index of the data array\n * @param previousData.didChange - did the data change\n * @internal\n */\nexport function checkChildrenDidChange(\n container: Container,\n previousData: {\n data: number[];\n index: number;\n didChange: boolean;\n })\n{\n const children = container.children;\n\n for (let i = 0; i < children.length; i++)\n {\n const child = children[i];\n\n const uid = child.uid;\n const didChange = ((child._didViewChangeTick & 0xffff) << 16) | (child._didContainerChangeTick & 0xffff);\n\n const index = previousData.index;\n\n if (previousData.data[index] !== uid || previousData.data[index + 1] !== didChange)\n {\n previousData.data[previousData.index] = uid;\n previousData.data[previousData.index + 1] = didChange;\n\n previousData.didChange = true;\n }\n\n previousData.index = index + 2;\n\n if (child.children.length)\n {\n checkChildrenDidChange(child, previousData);\n }\n }\n\n return previousData.didChange;\n}\n","import { Matrix } from '../../../maths/matrix/Matrix';\nimport { Bounds } from '../bounds/Bounds';\nimport { getGlobalBounds } from '../bounds/getGlobalBounds';\nimport { getLocalBounds } from '../bounds/getLocalBounds';\nimport { checkChildrenDidChange } from '../utils/checkChildrenDidChange';\n\nimport type { Size } from '../../../maths/misc/Size';\nimport type { Container } from '../Container';\n\n/**\n * A utility type that makes all properties of T optional except for the specified keys K.\n * @category utils\n * @internal\n */\nexport type Optional = Omit & Partial>;\n\n/** @ignore */\nexport interface MeasureMixinConstructor\n{\n /**\n * The width of the display object, in pixels.\n * @example\n * ```ts\n * new Container({ width: 100});\n * ```\n * @default 0\n */\n width?: number;\n /**\n * The height of the display object, in pixels.\n * @example\n * ```ts\n * new Container({ height: 100});\n * ```\n * @default 0\n */\n height?: number;\n}\n/**\n * The MeasureMixin interface provides methods for measuring and manipulating the size and bounds of a display object.\n * It includes methods to get and set the size of the object, retrieve its local bounds,\n * and calculate its global bounds.\n * @category scene\n * @advanced\n */\nexport interface MeasureMixin extends Required\n{\n getSize(out?: Size): Size;\n setSize(width: number, height?: number): void;\n setSize(value: Optional): void;\n /**\n * Retrieves the local bounds of the container as a Bounds object.\n * Uses cached values when possible for better performance.\n * @example\n * ```ts\n * // Basic bounds check\n * const bounds = container.getLocalBounds();\n * console.log(`Width: ${bounds.width}, Height: ${bounds.height}`);\n * // subsequent calls will reuse the cached bounds\n * const cachedBounds = container.getLocalBounds();\n * console.log(bounds === cachedBounds); // true\n * ```\n * @returns The bounding area\n * @see {@link Container#getBounds} For world space bounds\n * @see {@link Bounds} For bounds properties\n */\n getLocalBounds(): Bounds;\n /**\n * Calculates and returns the (world) bounds of the display object as a Rectangle.\n * Takes into account transforms and child bounds.\n * @example\n * ```ts\n * // Basic bounds calculation\n * const bounds = sprite.getBounds();\n * console.log(`World bounds: ${bounds.x}, ${bounds.y}, ${bounds.width}, ${bounds.height}`);\n *\n * // Reuse bounds object for performance\n * const recycleBounds = new Bounds();\n * sprite.getBounds(false, recycleBounds);\n *\n * // Skip update for performance\n * const fastBounds = sprite.getBounds(true);\n * ```\n * @remarks\n * - Includes transform calculations\n * - Updates scene graph by default\n * - Can reuse bounds objects\n * - Common in hit testing\n * @param {boolean} skipUpdate - Setting to `true` will stop the transforms of the scene graph from\n * being updated. This means the calculation returned MAY be out of date BUT will give you a\n * nice performance boost.\n * @param {Bounds} bounds - Optional bounds to store the result of the bounds calculation\n * @returns The minimum axis-aligned rectangle in world space that fits around this object\n * @see {@link Container#getLocalBounds} For untransformed bounds\n * @see {@link Bounds} For bounds properties\n */\n getBounds(skipUpdate?: boolean, bounds?: Bounds): Bounds;\n /** @private */\n _localBoundsCacheData: LocalBoundsCacheData;\n /** @private */\n _localBoundsCacheId: number;\n /** @private */\n _setWidth(width: number, localWidth: number): void;\n /** @private */\n _setHeight(height: number, localHeight: number): void;\n}\n\ninterface LocalBoundsCacheData\n{\n data: number[];\n index: number;\n didChange: boolean;\n localBounds: Bounds;\n}\n\nconst tempMatrix = new Matrix();\n\n/** @internal */\nexport const measureMixin: Partial = {\n\n _localBoundsCacheId: -1,\n _localBoundsCacheData: null,\n\n _setWidth(value: number, localWidth: number)\n {\n const sign = Math.sign(this.scale.x) || 1;\n\n if (localWidth !== 0)\n {\n this.scale.x = (value / localWidth) * sign;\n }\n else\n {\n this.scale.x = sign;\n }\n },\n\n _setHeight(value: number, localHeight: number)\n {\n const sign = Math.sign(this.scale.y) || 1;\n\n if (localHeight !== 0)\n {\n this.scale.y = (value / localHeight) * sign;\n }\n else\n {\n this.scale.y = sign;\n }\n },\n\n getLocalBounds(): Bounds\n {\n if (!this._localBoundsCacheData)\n {\n this._localBoundsCacheData = {\n data: [],\n index: 1,\n didChange: false,\n localBounds: new Bounds()\n };\n }\n\n const localBoundsCacheData = this._localBoundsCacheData;\n\n localBoundsCacheData.index = 1;\n localBoundsCacheData.didChange = false;\n\n if (localBoundsCacheData.data[0] !== this._didViewChangeTick)\n {\n localBoundsCacheData.didChange = true;\n localBoundsCacheData.data[0] = this._didViewChangeTick;\n }\n\n checkChildrenDidChange(this, localBoundsCacheData);\n\n if (localBoundsCacheData.didChange)\n {\n getLocalBounds(this, localBoundsCacheData.localBounds, tempMatrix);\n }\n\n return localBoundsCacheData.localBounds;\n },\n\n getBounds(skipUpdate?: boolean, bounds?: Bounds): Bounds\n {\n return getGlobalBounds(this, skipUpdate, bounds || new Bounds());\n },\n} as Container;\n","import type { Renderer } from '../../../rendering/renderers/types';\nimport type { Container } from '../Container';\n\n/** @internal */\nexport interface OnRenderMixinConstructor\n{\n /**\n * This callback is used when the container is rendered. It runs every frame during the render process,\n * making it ideal for per-frame updates and animations.\n *\n * > [!NOTE] In v7 many users used `updateTransform` for this, however the way v8 renders objects is different\n * > and \"updateTransform\" is no longer called every frame\n * @example\n * ```ts\n * // Basic rotation animation\n * const container = new Container();\n * container.onRender = () => {\n * container.rotation += 0.01;\n * };\n *\n * // Cleanup when done\n * container.onRender = null; // Removes callback\n * ```\n * @param renderer - The renderer instance\n * @see {@link Renderer} For renderer capabilities\n */\n onRender?: ((renderer: Renderer) => void | null);\n}\n\n/**\n * The OnRenderMixin interface provides a way to define a callback that is executed\n * every time the container is rendered. This is useful for adding custom rendering logic\n * or animations that need to be updated each frame.\n * @category scene\n * @advanced\n */\nexport interface OnRenderMixin extends Required\n{\n /** @private */\n _onRender: ((renderer: Renderer) => void) | null;\n}\n\n/** @internal */\nexport const onRenderMixin: Partial = {\n _onRender: null,\n\n set onRender(func: (renderer: Renderer) => void)\n {\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (!func)\n {\n if (this._onRender)\n {\n renderGroup?.removeOnRender(this);\n }\n\n this._onRender = null;\n\n return;\n }\n\n if (!this._onRender)\n {\n renderGroup?.addOnRender(this);\n }\n\n this._onRender = func;\n },\n\n get onRender(): (renderer: Renderer) => void\n {\n return this._onRender;\n }\n} as Container;\n","import type { Container } from '../Container';\n\n/** @ignore */\nexport interface SortMixinConstructor\n{\n /**\n * The zIndex of the container.\n *\n * Controls the rendering order of children within their parent container.\n *\n * A higher value will mean it will be moved towards the front of the rendering order.\n * @example\n * ```ts\n * // Add in any order\n * container.addChild(character, background, foreground);\n *\n * // Adjust rendering order\n * background.zIndex = 0;\n * character.zIndex = 1;\n * foreground.zIndex = 2;\n * ```\n * @see {@link Container#sortableChildren} For enabling sorting\n * @see {@link Container#sortChildren} For manual sorting\n * @default 0\n */\n zIndex?: number;\n /**\n * Should children be sorted by zIndex at the next render call.\n *\n * Will get automatically set to true if a new child is added, or if a child's zIndex changes.\n * @default false\n * @internal\n */\n sortDirty?: boolean;\n /**\n * If set to true, the container will sort its children by `zIndex` value\n * when the next render is called, or manually if `sortChildren()` is called.\n *\n * This actually changes the order of elements in the array of children,\n * so it will affect the rendering order.\n *\n * > [!NOTE] Also be aware of that this may not work nicely with the `addChildAt()` function,\n * > as the `zIndex` sorting may cause the child to automatically sorted to another position.\n * @example\n * ```ts\n * container.sortableChildren = true;\n * ```\n * @default false\n */\n sortableChildren?: boolean;\n}\n\n/**\n * The SortMixin interface provides methods and properties for sorting children of a container\n * based on their `zIndex` values. It allows for automatic sorting of children when their `zIndex`\n * changes or when new children are added. The mixin includes properties to manage sorting state\n * and methods to sort children explicitly.\n * @category scene\n * @advanced\n */\nexport interface SortMixin extends Required\n{\n /** @internal */\n _zIndex: number;\n /**\n * Sorts children by zIndex value. Only sorts if container is marked as dirty.\n * @example\n * ```ts\n * // Basic sorting\n * particles.zIndex = 2; // Will mark as dirty\n * container.sortChildren();\n * ```\n * @see {@link Container#sortableChildren} For enabling automatic sorting\n * @see {@link Container#zIndex} For setting child order\n */\n sortChildren: () => void;\n /** @internal */\n depthOfChildModified: () => void;\n}\n\n/** @internal */\nexport const sortMixin: Partial = {\n _zIndex: 0,\n sortDirty: false,\n sortableChildren: false,\n\n get zIndex()\n {\n return this._zIndex;\n },\n\n set zIndex(value: number)\n {\n if (this._zIndex === value) return;\n\n this._zIndex = value;\n\n this.depthOfChildModified();\n },\n\n depthOfChildModified()\n {\n if (this.parent)\n {\n this.parent.sortableChildren = true;\n this.parent.sortDirty = true;\n }\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.structureDidChange = true;\n }\n },\n\n sortChildren()\n {\n if (!this.sortDirty) return;\n\n this.sortDirty = false;\n\n this.children.sort(sortChildren);\n },\n} as Container;\n\nfunction sortChildren(a: Container, b: Container): number\n{\n return a._zIndex - b._zIndex;\n}\n","import { Point } from '../../../maths/point/Point';\nimport { matrixPool } from '../bounds/utils/matrixAndBoundsPool';\n\nimport type { PointData } from '../../../maths/point/PointData';\nimport type { Container } from '../Container';\n\n/**\n * Interface for a mixin that provides methods to convert between local and global coordinates.\n * This mixin allows you to get the global position of a container,\n * convert a point from local to global coordinates,\n * and convert a point from global to local coordinates.\n *\n * It includes methods to optimize performance by using cached matrices when available.\n * @category scene\n * @advanced\n */\nexport interface ToLocalGlobalMixin\n{\n /**\n * Returns the global position of the container, taking into account the container hierarchy.\n * @example\n * ```ts\n * // Basic position check\n * const globalPos = sprite.getGlobalPosition();\n * console.log(`Global: (${globalPos.x}, ${globalPos.y})`);\n *\n * // Reuse point object\n * const point = new Point();\n * sprite.getGlobalPosition(point);\n *\n * // Skip transform update for performance\n * const fastPos = container.getGlobalPosition(undefined, true);\n * ```\n * @param {Point} point - The optional point to write the global value to\n * @param {boolean} skipUpdate - Should we skip the update transform\n * @returns The updated point\n * @see {@link Container#toGlobal} For converting specific points\n * @see {@link Container#toLocal} For converting to local space\n */\n getGlobalPosition(point?: Point, skipUpdate?: boolean): Point;\n /**\n * Calculates the global position of a point relative to this container.\n * Takes into account the container hierarchy and transforms.\n * @example\n * ```ts\n * // Basic point conversion\n * const localPoint = { x: 10, y: 20 };\n * const globalPoint = container.toGlobal(localPoint);\n *\n * // With point reuse\n * const reusePoint = new Point();\n * container.toGlobal(localPoint, reusePoint);\n *\n * // Performance optimization\n * const fastPoint = container.toGlobal(\n * { x: 50, y: 50 },\n * undefined,\n * true // Skip transform update\n * );\n * ```\n * @param {PointData} position - The local point to convert\n * @param {P} point - Optional point to store the result\n * @param {boolean} skipUpdate - Whether to skip transform updates\n * @returns The global position\n * @see {@link Container#toLocal} For reverse conversion\n * @see {@link Container#getGlobalPosition} For container position\n */\n toGlobal

(position: PointData, point?: P, skipUpdate?: boolean): P;\n /**\n * Calculates the local position of the container relative to another point.\n * Converts coordinates from any coordinate space to this container's local coordinate space.\n * @example\n * ```ts\n * // Basic coordinate conversion\n * const worldPoint = { x: 100, y: 100 };\n * const localPos = container.toLocal(worldPoint);\n *\n * // Convert from another container\n * const fromSprite = new Sprite(texture);\n * fromSprite.position.set(50, 50);\n * const pointInSprite = { x: 10, y: 10 };\n * const localPoint = container.toLocal(pointInSprite, fromSprite);\n *\n * // With point reuse for performance\n * const reusePoint = new Point();\n * container.toLocal(worldPoint, undefined, reusePoint);\n *\n * // Skip transform update for static objects\n * const fastLocal = container.toLocal(\n * worldPoint,\n * undefined,\n * undefined,\n * true\n * );\n * ```\n * @param {PointData} position - The world origin to calculate from\n * @param {Container} from - The Container to calculate the global position from\n * @param {P} point - A Point object in which to store the value\n * @param {boolean} skipUpdate - Should we skip the update transform\n * @returns A point object representing the position in local space\n * @see {@link Container#toGlobal} For reverse conversion\n * @see {@link Container#getGlobalPosition} For container position\n */\n toLocal

(position: PointData, from?: Container, point?: P, skipUpdate?: boolean): P;\n}\n\n/** @internal */\nexport const toLocalGlobalMixin: Partial = {\n getGlobalPosition(point: Point = new Point(), skipUpdate = false): Point\n {\n if (this.parent)\n {\n this.parent.toGlobal(this._position, point, skipUpdate);\n }\n else\n {\n point.x = this._position.x;\n point.y = this._position.y;\n }\n\n return point;\n },\n\n toGlobal

(position: PointData, point?: P, skipUpdate = false): P\n {\n const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);\n\n // simply apply the matrix..\n point = globalMatrix.apply(position, point);\n\n matrixPool.return(globalMatrix);\n\n return point;\n },\n\n toLocal

(position: PointData, from?: Container, point?: P, skipUpdate?: boolean): P\n {\n if (from)\n {\n position = from.toGlobal(position, point, skipUpdate);\n }\n\n const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);\n\n // simply apply the matrix..\n point = globalMatrix.applyInverse(position, point);\n\n matrixPool.return(globalMatrix);\n\n return point;\n }\n} as Container;\n","import { uid } from '../../../../utils/data/uid';\n\nimport type { Renderable } from '../Renderable';\nimport type { Instruction } from './Instruction';\n\n/**\n * A set of instructions that can be executed by the renderer.\n * Basically wraps an array, but with some extra properties that help the renderer\n * to keep things nice and optimised.\n *\n * Note:\n * InstructionSet.instructions contains all the instructions, but does not resize (for performance).\n * So for the true length of the instructions you need to use InstructionSet.instructionSize\n * @category rendering\n * @advanced\n */\nexport class InstructionSet\n{\n /** a unique id for this instruction set used through the renderer */\n public readonly uid: number = uid('instructionSet');\n /** the array of instructions */\n public readonly instructions: Instruction[] = [];\n /** the actual size of the array (any instructions passed this should be ignored) */\n public instructionSize = 0;\n /** allows for access to the render pipes of the renderer */\n public renderPipes: any;\n\n public renderables: Renderable[] = [];\n /** used by the garbage collector to track when the instruction set was last used */\n public gcTick = 0;\n\n /** reset the instruction set so it can be reused set size back to 0 */\n public reset()\n {\n this.instructionSize = 0;\n }\n\n /**\n * Add an instruction to the set\n * @param instruction - add an instruction to the set\n */\n public add(instruction: Instruction)\n {\n this.instructions[this.instructionSize++] = instruction;\n }\n\n /**\n * Log the instructions to the console (for debugging)\n * @internal\n */\n public log()\n {\n this.instructions.length = this.instructionSize;\n // eslint-disable-next-line no-console\n console.table(this.instructions, ['type', 'action']);\n }\n}\n","// Taken from the bit-twiddle package\n\n/**\n * Rounds to next power of two.\n * @function nextPow2\n * @param {number} v - input value\n * @returns {number} - next rounded power of two\n * @category maths\n * @advanced\n */\nexport function nextPow2(v: number): number\n{\n v += v === 0 ? 1 : 0;\n --v;\n v |= v >>> 1;\n v |= v >>> 2;\n v |= v >>> 4;\n v |= v >>> 8;\n v |= v >>> 16;\n\n return v + 1;\n}\n\n/**\n * Checks if a number is a power of two.\n * @function isPow2\n * @param {number} v - input value\n * @returns {boolean} `true` if value is power of two\n * @category maths\n * @advanced\n */\nexport function isPow2(v: number): boolean\n{\n return !(v & (v - 1)) && (!!v);\n}\n\n/**\n * Computes ceil of log base 2\n * @function log2\n * @param {number} v - input value\n * @returns {number} logarithm base 2\n * @category maths\n * @advanced\n */\nexport function log2(v: number): number\n{\n let r = (v > 0xFFFF ? 1 : 0) << 4;\n\n v >>>= r;\n\n let shift = (v > 0xFF ? 1 : 0) << 3;\n\n v >>>= shift; r |= shift;\n shift = (v > 0xF ? 1 : 0) << 2;\n v >>>= shift; r |= shift;\n shift = (v > 0x3 ? 1 : 0) << 1;\n v >>>= shift; r |= shift;\n\n return r | (v >> 1);\n}\n","/**\n * Returns a new object with all properties from the input object that have defined values.\n * @template T - The type of the input object.\n * @param {T} obj - The input object.\n * @returns {T} - A new object with only the defined properties from the input object.\n * @category utils\n * @ignore\n */\nexport function definedProps>(obj: T): T\n{\n const result: Partial = {};\n\n for (const key in obj)\n {\n if (obj[key] !== undefined)\n {\n result[key] = obj[key];\n }\n }\n\n return result as T;\n}\n","import EventEmitter from 'eventemitter3';\nimport { uid } from '../../../../utils/data/uid';\nimport { deprecation, v8_0_0 } from '../../../../utils/logging/deprecation';\n\nimport type { BindResource } from '../../gpu/shader/BindResource';\nimport type { COMPARE_FUNCTION, SCALE_MODE, WRAP_MODE } from './const';\n\nconst idHash: Record = Object.create(null);\n\n/**\n * This takes a shader string and maps it to a resource id.\n * This is a little different than regular resource ids as these ids\n * are not unique to the resource. But must not overlap with other (non sampler) resources Ids.\n * @param value - the string to turn into a resource id\n * @returns a unique resource id\n */\nfunction createResourceIdFromString(value: string): number\n{\n const id = idHash[value];\n\n if (id === undefined)\n {\n idHash[value] = uid('resource');\n }\n\n return id;\n}\n\n/**\n * The options for the texture style.\n * @category rendering\n * @advanced\n */\nexport interface TextureStyleOptions extends Partial\n{\n /** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */\n addressMode?: WRAP_MODE;\n /** specifies the {{GPUAddressMode|address modes}} for the texture width, height, and depth coordinates, respectively. */\n addressModeU?: WRAP_MODE;\n /** specifies the {{GPUAddressMode|address modes}} for the texture width, height, and depth coordinates, respectively. */\n addressModeV?: WRAP_MODE;\n /** Specifies the {{GPUAddressMode|address modes}} for the texture width, height, and depth coordinates, respectively. */\n addressModeW?: WRAP_MODE;\n\n /** setting this will set magFilter,minFilter and mipmapFilter all at once! */\n scaleMode?: SCALE_MODE;\n\n /** specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */\n magFilter?: SCALE_MODE;\n /** specifies the sampling behavior when the sample footprint is larger than one texel. */\n minFilter?: SCALE_MODE;\n /** specifies behavior for sampling between mipmap levels. */\n mipmapFilter?: SCALE_MODE;\n\n /** specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */\n lodMinClamp?: number;\n /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */\n lodMaxClamp?: number;\n /**\n * When provided the sampler will be a comparison sampler with the specified\n * {@link COMPARE_FUNCTION}.\n * Note: Comparison samplers may use filtering, but the sampling results will be\n * implementation-dependent and may differ from the normal filtering rules.\n */\n compare?: COMPARE_FUNCTION;\n /**\n * Specifies the maximum anisotropy value clamp used by the sampler.\n * Note: Most implementations support {@link TextureStyle#maxAnisotropy} values in range\n * between 1 and 16, inclusive. The used value of {@link TextureStyle#maxAnisotropy} will\n * be clamped to the maximum value that the platform supports.\n *\n * setting this to anything higher than 1 will set scale modes to 'linear'\n */\n maxAnisotropy?: number;\n}\n\n/**\n * A texture style describes how a texture should be sampled by a shader.\n * @category rendering\n * @advanced\n */\nexport class TextureStyle extends EventEmitter<{\n change: TextureStyle,\n destroy: TextureStyle,\n}> implements BindResource\n{\n /** @internal */\n public _resourceType = 'textureSampler';\n /** @internal */\n public _touched = 0;\n private _sharedResourceId: number;\n\n /** default options for the style */\n public static readonly defaultOptions: TextureStyleOptions = {\n addressMode: 'clamp-to-edge',\n scaleMode: 'linear'\n };\n\n /** */\n public addressModeU?: WRAP_MODE;\n /** */\n public addressModeV?: WRAP_MODE;\n /** Specifies the {{GPUAddressMode|address modes}} for the texture width, height, and depth coordinates, respectively. */\n public addressModeW?: WRAP_MODE;\n /** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */\n public magFilter?: SCALE_MODE;\n /** Specifies the sampling behavior when the sample footprint is larger than one texel. */\n public minFilter?: SCALE_MODE;\n /** Specifies behavior for sampling between mipmap levels. */\n public mipmapFilter?: SCALE_MODE;\n /** */\n public lodMinClamp?: number;\n /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */\n public lodMaxClamp?: number;\n /**\n * When provided the sampler will be a comparison sampler with the specified\n * {@link COMPARE_FUNCTION}.\n * Note: Comparison samplers may use filtering, but the sampling results will be\n * implementation-dependent and may differ from the normal filtering rules.\n */\n public compare?: COMPARE_FUNCTION;\n /**\n * Specifies the maximum anisotropy value clamp used by the sampler.\n * Note: Most implementations support {@link TextureStyle#maxAnisotropy} values in range\n * between 1 and 16, inclusive. The used value of {@link TextureStyle#maxAnisotropy} will\n * be clamped to the maximum value that the platform supports.\n * @internal\n */\n public _maxAnisotropy?: number = 1;\n\n /**\n * Has the style been destroyed?\n * @readonly\n */\n public destroyed = false;\n\n /**\n * @param options - options for the style\n */\n constructor(options: TextureStyleOptions = {})\n {\n super();\n\n options = { ...TextureStyle.defaultOptions, ...options };\n\n this.addressMode = options.addressMode;\n\n this.addressModeU = options.addressModeU ?? this.addressModeU;\n this.addressModeV = options.addressModeV ?? this.addressModeV;\n this.addressModeW = options.addressModeW ?? this.addressModeW;\n\n this.scaleMode = options.scaleMode;\n\n this.magFilter = options.magFilter ?? this.magFilter;\n this.minFilter = options.minFilter ?? this.minFilter;\n this.mipmapFilter = options.mipmapFilter ?? this.mipmapFilter;\n\n this.lodMinClamp = options.lodMinClamp;\n this.lodMaxClamp = options.lodMaxClamp;\n\n this.compare = options.compare;\n\n this.maxAnisotropy = options.maxAnisotropy ?? 1;\n }\n\n set addressMode(value: WRAP_MODE)\n {\n this.addressModeU = value;\n this.addressModeV = value;\n this.addressModeW = value;\n }\n\n /** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */\n get addressMode(): WRAP_MODE\n {\n return this.addressModeU;\n }\n\n set wrapMode(value: WRAP_MODE)\n {\n // #if _DEBUG\n deprecation(v8_0_0, 'TextureStyle.wrapMode is now TextureStyle.addressMode');\n // #endif\n\n this.addressMode = value;\n }\n\n get wrapMode(): WRAP_MODE\n {\n return this.addressMode;\n }\n\n set scaleMode(value: SCALE_MODE)\n {\n this.magFilter = value;\n this.minFilter = value;\n this.mipmapFilter = value;\n }\n\n /** setting this will set magFilter,minFilter and mipmapFilter all at once! */\n get scaleMode(): SCALE_MODE\n {\n return this.magFilter;\n }\n\n /** Specifies the maximum anisotropy value clamp used by the sampler. */\n set maxAnisotropy(value: number)\n {\n this._maxAnisotropy = Math.min(value, 16);\n\n if (this._maxAnisotropy > 1)\n {\n this.scaleMode = 'linear';\n }\n }\n\n get maxAnisotropy(): number\n {\n return this._maxAnisotropy;\n }\n\n // TODO - move this to WebGL?\n get _resourceId(): number\n {\n return this._sharedResourceId || this._generateResourceId();\n }\n\n public update()\n {\n // manage the resource..\n this.emit('change', this);\n this._sharedResourceId = null;\n }\n\n private _generateResourceId(): number\n {\n // eslint-disable-next-line max-len\n const bigKey = `${this.addressModeU}-${this.addressModeV}-${this.addressModeW}-${this.magFilter}-${this.minFilter}-${this.mipmapFilter}-${this.lodMinClamp}-${this.lodMaxClamp}-${this.compare}-${this._maxAnisotropy}`;\n\n this._sharedResourceId = createResourceIdFromString(bigKey);\n\n return this._resourceId;\n }\n\n /** Destroys the style */\n public destroy()\n {\n this.destroyed = true;\n\n this.emit('destroy', this);\n this.emit('change', this);\n\n this.removeAllListeners();\n }\n}\n","import EventEmitter from 'eventemitter3';\nimport { isPow2 } from '../../../../../maths/misc/pow2';\nimport { definedProps } from '../../../../../scene/container/utils/definedProps';\nimport { uid } from '../../../../../utils/data/uid';\nimport { TextureStyle } from '../TextureStyle';\n\nimport type { BindResource } from '../../../gpu/shader/BindResource';\nimport type { ALPHA_MODES, SCALE_MODE, TEXTURE_DIMENSIONS, TEXTURE_FORMATS, WRAP_MODE } from '../const';\nimport type { TextureStyleOptions } from '../TextureStyle';\nimport type { TextureResourceOrOptions } from '../utils/textureFrom';\n\n/**\n * options for creating a new TextureSource\n * @category rendering\n * @advanced\n */\nexport interface TextureSourceOptions = any> extends TextureStyleOptions\n{\n /**\n * the resource that will be uploaded to the GPU. This is where we get our pixels from\n * eg an ImageBimt / Canvas / Video etc\n */\n resource?: T;\n /** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */\n width?: number;\n /** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */\n height?: number;\n /** the resolution of the texture. */\n resolution?: number;\n /** the format that the texture data has */\n format?: TEXTURE_FORMATS;\n /**\n * Used by internal textures\n * @ignore\n */\n sampleCount?: number;\n /**\n * Only really affects RenderTextures.\n * Should we use antialiasing for this texture. It will look better, but may impact performance as a\n * Blit operation will be required to resolve the texture.\n */\n antialias?: boolean;\n /** how many dimensions does this texture have? currently v8 only supports 2d */\n dimensions?: TEXTURE_DIMENSIONS;\n /** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */\n mipLevelCount?: number;\n /**\n * Should we auto generate mipmaps for this texture? This will automatically generate mipmaps\n * for this texture when uploading to the GPU. Mipmapped textures take up more memory, but\n * can look better when scaled down.\n *\n * For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.\n * If you do, make sure to call `updateMipmaps` after you update the texture.\n */\n autoGenerateMipmaps?: boolean;\n /** the alpha mode of the texture */\n alphaMode?: ALPHA_MODES;\n /** optional label, can be used for debugging */\n label?: string;\n /** If true, the Garbage Collector will unload this texture if it is not used after a period of time */\n autoGarbageCollect?: boolean;\n}\n\n/**\n * A TextureSource stores the information that represents an image.\n * All textures have require TextureSource, which contains information about the source.\n * Therefore you can have many textures all using a single TextureSource (eg a sprite sheet)\n *\n * This is an class is extended depending on the source of the texture.\n * Eg if you are using an an image as your resource, then an ImageSource is used.\n * @category rendering\n * @advanced\n */\nexport class TextureSource = any> extends EventEmitter<{\n change: BindResource;\n update: TextureSource;\n unload: TextureSource;\n destroy: TextureSource;\n resize: TextureSource;\n styleChange: TextureSource;\n updateMipmaps: TextureSource;\n error: Error;\n}> implements BindResource\n{\n /** The default options used when creating a new TextureSource. override these to add your own defaults */\n public static defaultOptions: TextureSourceOptions = {\n resolution: 1,\n format: 'bgra8unorm',\n alphaMode: 'premultiply-alpha-on-upload',\n dimensions: '2d',\n mipLevelCount: 1,\n autoGenerateMipmaps: false,\n sampleCount: 1,\n antialias: false,\n autoGarbageCollect: false,\n };\n\n /** unique id for this Texture source */\n public readonly uid: number = uid('textureSource');\n /** optional label, can be used for debugging */\n public label: string;\n\n /**\n * The resource type used by this TextureSource. This is used by the bind groups to determine\n * how to handle this resource.\n * @internal\n */\n public readonly _resourceType = 'textureSource';\n /**\n * i unique resource id, used by the bind group systems.\n * This can change if the texture is resized or its resource changes\n * @internal\n */\n public _resourceId = uid('resource');\n /**\n * this is how the backends know how to upload this texture to the GPU\n * It changes depending on the resource type. Classes that extend TextureSource\n * should override this property.\n * @internal\n */\n public uploadMethodId = 'unknown';\n\n /** @internal */\n public _resolution = 1;\n\n /** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */\n public pixelWidth = 1;\n /** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */\n public pixelHeight = 1;\n\n /**\n * the width of this texture source, accounting for resolution\n * eg pixelWidth 200, resolution 2, then width will be 100\n */\n public width = 1;\n /**\n * the height of this texture source, accounting for resolution\n * eg pixelHeight 200, resolution 2, then height will be 100\n */\n public height = 1;\n\n /**\n * the resource that will be uploaded to the GPU. This is where we get our pixels from\n * eg an ImageBimt / Canvas / Video etc\n */\n public resource: T;\n\n /**\n * The number of samples of a multisample texture. This is always 1 for non-multisample textures.\n * To enable multisample for a texture, set antialias to true\n * @internal\n */\n public sampleCount = 1;\n\n /** The number of mip levels to generate for this texture. this is overridden if autoGenerateMipmaps is true */\n public mipLevelCount = 1;\n /**\n * Should we auto generate mipmaps for this texture? This will automatically generate mipmaps\n * for this texture when uploading to the GPU. Mipmapped textures take up more memory, but\n * can look better when scaled down.\n *\n * For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame.\n * If you do, make sure to call `updateMipmaps` after you update the texture.\n */\n public autoGenerateMipmaps = false;\n /** the format that the texture data has */\n public format: TEXTURE_FORMATS = 'rgba8unorm';\n /** how many dimensions does this texture have? currently v8 only supports 2d */\n public dimension: TEXTURE_DIMENSIONS = '2d';\n /** the alpha mode of the texture */\n public alphaMode: ALPHA_MODES;\n private _style: TextureStyle;\n\n /**\n * Only really affects RenderTextures.\n * Should we use antialiasing for this texture. It will look better, but may impact performance as a\n * Blit operation will be required to resolve the texture.\n */\n public antialias = false;\n\n /**\n * Has the source been destroyed?\n * @readonly\n */\n public destroyed: boolean;\n\n /**\n * Used by automatic texture Garbage Collection, stores last GC tick when it was bound\n * @protected\n */\n public _touched = 0;\n\n /**\n * Used by the batcher to build texture batches. faster to have the variable here!\n * @protected\n */\n public _batchTick = -1;\n /**\n * A temporary batch location for the texture batching. Here for performance reasons only!\n * @protected\n */\n public _textureBindLocation = -1;\n\n public isPowerOfTwo: boolean;\n\n /** If true, the Garbage Collector will unload this texture if it is not used after a period of time */\n public autoGarbageCollect: boolean;\n\n /**\n * used internally to know where a texture came from. Usually assigned by the asset loader!\n * @ignore\n */\n public _sourceOrigin: string;\n\n /**\n * @param options - options for creating a new TextureSource\n */\n constructor(protected readonly options: TextureSourceOptions = {})\n {\n super();\n\n options = { ...TextureSource.defaultOptions, ...options };\n\n this.label = options.label ?? '';\n this.resource = options.resource;\n this.autoGarbageCollect = options.autoGarbageCollect;\n this._resolution = options.resolution;\n\n if (options.width)\n {\n this.pixelWidth = options.width * this._resolution;\n }\n else\n {\n this.pixelWidth = this.resource ? (this.resourceWidth ?? 1) : 1;\n }\n\n if (options.height)\n {\n this.pixelHeight = options.height * this._resolution;\n }\n else\n {\n this.pixelHeight = this.resource ? (this.resourceHeight ?? 1) : 1;\n }\n\n this.width = this.pixelWidth / this._resolution;\n this.height = this.pixelHeight / this._resolution;\n\n this.format = options.format;\n this.dimension = options.dimensions;\n this.mipLevelCount = options.mipLevelCount;\n this.autoGenerateMipmaps = options.autoGenerateMipmaps;\n this.sampleCount = options.sampleCount;\n this.antialias = options.antialias;\n this.alphaMode = options.alphaMode;\n\n this.style = new TextureStyle(definedProps(options));\n\n this.destroyed = false;\n\n this._refreshPOT();\n }\n\n /** returns itself */\n get source(): TextureSource\n {\n return this;\n }\n\n /** the style of the texture */\n get style(): TextureStyle\n {\n return this._style;\n }\n\n set style(value: TextureStyle)\n {\n if (this.style === value) return;\n\n this._style?.off('change', this._onStyleChange, this);\n this._style = value;\n this._style?.on('change', this._onStyleChange, this);\n\n this._onStyleChange();\n }\n\n /** Specifies the maximum anisotropy value clamp used by the sampler. */\n set maxAnisotropy(value: number)\n {\n this._style.maxAnisotropy = value;\n }\n\n get maxAnisotropy(): number\n {\n return this._style.maxAnisotropy;\n }\n\n /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */\n get addressMode(): WRAP_MODE\n {\n return this._style.addressMode;\n }\n\n set addressMode(value: WRAP_MODE)\n {\n this._style.addressMode = value;\n }\n\n /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */\n get repeatMode(): WRAP_MODE\n {\n return this._style.addressMode;\n }\n\n set repeatMode(value: WRAP_MODE)\n {\n this._style.addressMode = value;\n }\n\n /** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */\n get magFilter(): SCALE_MODE\n {\n return this._style.magFilter;\n }\n\n set magFilter(value: SCALE_MODE)\n {\n this._style.magFilter = value;\n }\n\n /** Specifies the sampling behavior when the sample footprint is larger than one texel. */\n get minFilter(): SCALE_MODE\n {\n return this._style.minFilter;\n }\n\n set minFilter(value: SCALE_MODE)\n {\n this._style.minFilter = value;\n }\n\n /** Specifies behavior for sampling between mipmap levels. */\n get mipmapFilter(): SCALE_MODE\n {\n return this._style.mipmapFilter;\n }\n\n set mipmapFilter(value: SCALE_MODE)\n {\n this._style.mipmapFilter = value;\n }\n\n /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */\n get lodMinClamp(): number\n {\n return this._style.lodMinClamp;\n }\n\n set lodMinClamp(value: number)\n {\n this._style.lodMinClamp = value;\n }\n\n /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */\n get lodMaxClamp(): number\n {\n return this._style.lodMaxClamp;\n }\n\n set lodMaxClamp(value: number)\n {\n this._style.lodMaxClamp = value;\n }\n\n private _onStyleChange()\n {\n this.emit('styleChange', this);\n }\n\n /** call this if you have modified the texture outside of the constructor */\n public update()\n {\n // update resource...\n if (this.resource)\n {\n const resolution = this._resolution;\n\n const didResize = this.resize(this.resourceWidth / resolution, this.resourceHeight / resolution);\n\n // no need to dispatch the update we resized as that will\n // notify the texture systems anyway\n if (didResize) return;\n }\n\n this.emit('update', this);\n }\n\n /** Destroys this texture source */\n public destroy()\n {\n this.destroyed = true;\n this.emit('destroy', this);\n this.emit('change', this);\n\n if (this._style)\n {\n this._style.destroy();\n this._style = null;\n }\n\n this.uploadMethodId = null;\n this.resource = null;\n this.removeAllListeners();\n }\n\n /**\n * This will unload the Texture source from the GPU. This will free up the GPU memory\n * As soon as it is required fore rendering, it will be re-uploaded.\n */\n public unload()\n {\n this._resourceId = uid('resource');\n this.emit('change', this);\n this.emit('unload', this);\n }\n\n /** the width of the resource. This is the REAL pure number, not accounting resolution */\n public get resourceWidth(): number\n {\n const { resource } = this;\n\n return resource.naturalWidth || resource.videoWidth || resource.displayWidth || resource.width;\n }\n\n /** the height of the resource. This is the REAL pure number, not accounting resolution */\n public get resourceHeight(): number\n {\n const { resource } = this;\n\n return resource.naturalHeight || resource.videoHeight || resource.displayHeight || resource.height;\n }\n\n /**\n * the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture\n * but will the size of the texture when rendered.\n *\n * changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel\n * density will have increased)\n */\n get resolution(): number\n {\n return this._resolution;\n }\n\n set resolution(resolution: number)\n {\n if (this._resolution === resolution) return;\n\n this._resolution = resolution;\n\n this.width = this.pixelWidth / resolution;\n this.height = this.pixelHeight / resolution;\n }\n\n /**\n * Resize the texture, this is handy if you want to use the texture as a render texture\n * @param width - the new width of the texture\n * @param height - the new height of the texture\n * @param resolution - the new resolution of the texture\n * @returns - if the texture was resized\n */\n public resize(width?: number, height?: number, resolution?: number): boolean\n {\n resolution ||= this._resolution;\n width ||= this.width;\n height ||= this.height;\n\n // make sure we work with rounded pixels\n const newPixelWidth = Math.round(width * resolution);\n const newPixelHeight = Math.round(height * resolution);\n\n this.width = newPixelWidth / resolution;\n this.height = newPixelHeight / resolution;\n\n this._resolution = resolution;\n\n if (this.pixelWidth === newPixelWidth && this.pixelHeight === newPixelHeight)\n {\n return false;\n }\n\n this._refreshPOT();\n\n this.pixelWidth = newPixelWidth;\n this.pixelHeight = newPixelHeight;\n\n this.emit('resize', this);\n\n this._resourceId = uid('resource');\n this.emit('change', this);\n\n return true;\n }\n\n /**\n * Lets the renderer know that this texture has been updated and its mipmaps should be re-generated.\n * This is only important for RenderTexture instances, as standard Texture instances will have their\n * mipmaps generated on upload. You should call this method after you make any change to the texture\n *\n * The reason for this is is can be quite expensive to update mipmaps for a texture. So by default,\n * We want you, the developer to specify when this action should happen.\n *\n * Generally you don't want to have mipmaps generated on Render targets that are changed every frame,\n */\n public updateMipmaps()\n {\n if (this.autoGenerateMipmaps && this.mipLevelCount > 1)\n {\n this.emit('updateMipmaps', this);\n }\n }\n\n set wrapMode(value: WRAP_MODE)\n {\n this._style.wrapMode = value;\n }\n\n get wrapMode(): WRAP_MODE\n {\n return this._style.wrapMode;\n }\n\n set scaleMode(value: SCALE_MODE)\n {\n this._style.scaleMode = value;\n }\n\n /** setting this will set magFilter,minFilter and mipmapFilter all at once! */\n get scaleMode(): SCALE_MODE\n {\n return this._style.scaleMode;\n }\n\n /**\n * Refresh check for isPowerOfTwo texture based on size\n * @private\n */\n protected _refreshPOT(): void\n {\n this.isPowerOfTwo = isPow2(this.pixelWidth) && isPow2(this.pixelHeight);\n }\n\n public static test(_resource: any): any\n {\n // this should be overridden by other sources..\n throw new Error('Unimplemented');\n }\n\n /**\n * A helper function that creates a new TextureSource based on the resource you provide.\n * @param resource - The resource to create the texture source from.\n */\n public static from: (resource: TextureResourceOrOptions) => TextureSource;\n}\n","// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group\n//\n// This file implements the dihedral group of order 16, also called\n// of degree 8. That's why its called groupD8.\n\nimport { type RectangleLike } from '../../culling/Culler';\nimport { type Rectangle } from '../shapes/Rectangle';\nimport { Matrix } from './Matrix';\n\n/*\n * Transform matrix for operation n is:\n * | ux | vx |\n * | uy | vy |\n */\n\nconst ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1];\nconst uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1];\nconst vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1];\nconst vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1];\n\n/**\n * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table}\n * for the composition of each rotation in the dihederal group D8.\n * @type {number[][]}\n * @private\n */\nconst rotationCayley: number[][] = [];\n\n/**\n * Matrices for each `GD8Symmetry` rotation.\n * @type {Matrix[]}\n * @private\n */\nconst rotationMatrices: Matrix[] = [];\n\n/** Alias for `Math.sign`. */\nconst signum = Math.sign;\n\n/*\n * Initializes `rotationCayley` and `rotationMatrices`. It is called\n * only once below.\n */\nfunction init(): void\n{\n for (let i = 0; i < 16; i++)\n {\n const row: number[] = [];\n\n rotationCayley.push(row);\n\n for (let j = 0; j < 16; j++)\n {\n /* Multiplies rotation matrices i and j. */\n const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j]));\n const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j]));\n const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j]));\n const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j]));\n\n /* Finds rotation matrix matching the product and pushes it. */\n for (let k = 0; k < 16; k++)\n {\n if (ux[k] === _ux && uy[k] === _uy\n && vx[k] === _vx && vy[k] === _vy)\n {\n row.push(k);\n break;\n }\n }\n }\n }\n\n for (let i = 0; i < 16; i++)\n {\n const mat = new Matrix();\n\n mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0);\n rotationMatrices.push(mat);\n }\n}\n\ninit();\n\ntype GD8Symmetry = number;\n/**\n * @typedef {number} GD8Symmetry\n * @see groupD8\n */\n\n/**\n * Implements the dihedral group D8, which is similar to\n * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html};\n * D8 is the same but with diagonals, and it is used for texture\n * rotations.\n *\n * The directions the U- and V- axes after rotation\n * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))`\n * and `(vX(a), vY(a))`. These aren't necessarily unit vectors.\n * @author Ivan: ivanpopelyshev\n * @groupDescription groupD8\n * @category maths\n * @advanced\n */\nexport const groupD8 = {\n /**\n * | Rotation | Direction |\n * |----------|-----------|\n * | 0° | East |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n E: 0,\n\n /**\n * | Rotation | Direction |\n * |----------|-----------|\n * | 45°↻ | Southeast |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n SE: 1,\n\n /**\n * | Rotation | Direction |\n * |----------|-----------|\n * | 90°↻ | South |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n S: 2,\n\n /**\n * | Rotation | Direction |\n * |----------|-----------|\n * | 135°↻ | Southwest |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n SW: 3,\n\n /**\n * | Rotation | Direction |\n * |----------|-----------|\n * | 180° | West |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n W: 4,\n\n /**\n * | Rotation | Direction |\n * |-------------|--------------|\n * | -135°/225°↻ | Northwest |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n NW: 5,\n\n /**\n * | Rotation | Direction |\n * |-------------|--------------|\n * | -90°/270°↻ | North |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n N: 6,\n\n /**\n * | Rotation | Direction |\n * |-------------|--------------|\n * | -45°/315°↻ | Northeast |\n * @group groupD8\n * @type {GD8Symmetry}\n */\n NE: 7,\n\n /**\n * Reflection about Y-axis.\n * @group groupD8\n * @type {GD8Symmetry}\n */\n MIRROR_VERTICAL: 8,\n\n /**\n * Reflection about the main diagonal.\n * @group groupD8\n * @type {GD8Symmetry}\n */\n MAIN_DIAGONAL: 10,\n\n /**\n * Reflection about X-axis.\n * @group groupD8\n * @type {GD8Symmetry}\n */\n MIRROR_HORIZONTAL: 12,\n\n /**\n * Reflection about reverse diagonal.\n * @group groupD8\n * @type {GD8Symmetry}\n */\n REVERSE_DIAGONAL: 14,\n\n /**\n * @group groupD8\n * @param {GD8Symmetry} ind - sprite rotation angle.\n * @returns {GD8Symmetry} The X-component of the U-axis\n * after rotating the axes.\n */\n uX: (ind: GD8Symmetry): GD8Symmetry => ux[ind],\n\n /**\n * @group groupD8\n * @param {GD8Symmetry} ind - sprite rotation angle.\n * @returns {GD8Symmetry} The Y-component of the U-axis\n * after rotating the axes.\n */\n uY: (ind: GD8Symmetry): GD8Symmetry => uy[ind],\n\n /**\n * @group groupD8\n * @param {GD8Symmetry} ind - sprite rotation angle.\n * @returns {GD8Symmetry} The X-component of the V-axis\n * after rotating the axes.\n */\n vX: (ind: GD8Symmetry): GD8Symmetry => vx[ind],\n\n /**\n * @group groupD8\n * @param {GD8Symmetry} ind - sprite rotation angle.\n * @returns {GD8Symmetry} The Y-component of the V-axis\n * after rotating the axes.\n */\n vY: (ind: GD8Symmetry): GD8Symmetry => vy[ind],\n\n /**\n * @group groupD8\n * @param {GD8Symmetry} rotation - symmetry whose opposite\n * is needed. Only rotations have opposite symmetries while\n * reflections don't.\n * @returns {GD8Symmetry} The opposite symmetry of `rotation`\n */\n inv: (rotation: GD8Symmetry): GD8Symmetry =>\n {\n if (rotation & 8)// true only if between 8 & 15 (reflections)\n {\n return rotation & 15;// or rotation % 16\n }\n\n return (-rotation) & 7;// or (8 - rotation) % 8\n },\n\n /**\n * Composes the two D8 operations.\n *\n * Taking `^` as reflection:\n *\n * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 |\n * |-------|-----|-----|-----|-----|------|-------|-------|-------|\n * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ |\n * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ |\n * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ |\n * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ |\n * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S |\n * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W |\n * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N |\n * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E |\n *\n * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table}\n * @group groupD8\n * @param {GD8Symmetry} rotationSecond - Second operation, which\n * is the row in the above cayley table.\n * @param {GD8Symmetry} rotationFirst - First operation, which\n * is the column in the above cayley table.\n * @returns {GD8Symmetry} Composed operation\n */\n add: (rotationSecond: GD8Symmetry, rotationFirst: GD8Symmetry): GD8Symmetry => (\n rotationCayley[rotationSecond][rotationFirst]\n ),\n\n /**\n * Reverse of `add`.\n * @group groupD8\n * @param {GD8Symmetry} rotationSecond - Second operation\n * @param {GD8Symmetry} rotationFirst - First operation\n * @returns {GD8Symmetry} Result\n */\n sub: (rotationSecond: GD8Symmetry, rotationFirst: GD8Symmetry): GD8Symmetry => (\n rotationCayley[rotationSecond][groupD8.inv(rotationFirst)]\n ),\n\n /**\n * Adds 180 degrees to rotation, which is a commutative\n * operation.\n * @group groupD8\n * @param {number} rotation - The number to rotate.\n * @returns {number} Rotated number\n */\n rotate180: (rotation: number): number => rotation ^ 4,\n\n /**\n * Checks if the rotation angle is vertical, i.e. south\n * or north. It doesn't work for reflections.\n * @group groupD8\n * @param {GD8Symmetry} rotation - The number to check.\n * @returns {boolean} Whether or not the direction is vertical\n */\n isVertical: (rotation: GD8Symmetry): boolean => (rotation & 3) === 2, // rotation % 4 === 2\n\n /**\n * Approximates the vector `V(dx,dy)` into one of the\n * eight directions provided by `groupD8`.\n * @group groupD8\n * @param {number} dx - X-component of the vector\n * @param {number} dy - Y-component of the vector\n * @returns {GD8Symmetry} Approximation of the vector into\n * one of the eight symmetries.\n */\n byDirection: (dx: number, dy: number): GD8Symmetry =>\n {\n if (Math.abs(dx) * 2 <= Math.abs(dy))\n {\n if (dy >= 0)\n {\n return groupD8.S;\n }\n\n return groupD8.N;\n }\n else if (Math.abs(dy) * 2 <= Math.abs(dx))\n {\n if (dx > 0)\n {\n return groupD8.E;\n }\n\n return groupD8.W;\n }\n else if (dy > 0)\n {\n if (dx > 0)\n {\n return groupD8.SE;\n }\n\n return groupD8.SW;\n }\n else if (dx > 0)\n {\n return groupD8.NE;\n }\n\n return groupD8.NW;\n },\n\n /**\n * Helps sprite to compensate texture packer rotation.\n * @group groupD8\n * @param {Matrix} matrix - sprite world matrix\n * @param {GD8Symmetry} rotation - The rotation factor to use.\n * @param {number} tx - sprite anchoring\n * @param {number} ty - sprite anchoring\n */\n matrixAppendRotationInv: (matrix: Matrix, rotation: GD8Symmetry, tx = 0, ty = 0): void =>\n {\n // Packer used \"rotation\", we use \"inv(rotation)\"\n const mat: Matrix = rotationMatrices[groupD8.inv(rotation)];\n\n mat.tx = tx;\n mat.ty = ty;\n matrix.append(mat);\n },\n\n /**\n * Transforms rectangle coordinates based on texture packer rotation.\n * Used when texture atlas pages are rotated and coordinates need to be adjusted.\n * @group groupD8\n * @param {RectangleLike} rect - Rectangle with original coordinates to transform\n * @param {RectangleLike} sourceFrame - Source texture frame (includes offset and dimensions)\n * @param {GD8Symmetry} rotation - The groupD8 rotation value\n * @param {Rectangle} out - Rectangle to store the result\n * @returns {Rectangle} Transformed coordinates (includes source frame offset)\n */\n transformRectCoords: (\n rect: RectangleLike,\n sourceFrame: RectangleLike,\n rotation: GD8Symmetry,\n out: Rectangle\n ): Rectangle =>\n {\n const { x, y, width, height } = rect;\n const { x: frameX, y: frameY, width: frameWidth, height: frameHeight } = sourceFrame;\n\n if (rotation === groupD8.E)\n {\n // No rotation\n out.set(x + frameX, y + frameY, width, height);\n\n return out;\n }\n else if (rotation === groupD8.S)\n {\n // 90° clockwise rotation\n return out.set(\n (frameWidth - y - height) + frameX,\n x + frameY,\n height,\n width\n );\n }\n else if (rotation === groupD8.W)\n {\n // 180° rotation\n return out.set(\n (frameWidth - x - width) + frameX,\n (frameHeight - y - height) + frameY,\n width,\n height\n );\n }\n else if (rotation === groupD8.N)\n {\n // 270° clockwise rotation (90° counter-clockwise)\n return out.set(\n y + frameX,\n (frameHeight - x - width) + frameY,\n height,\n width\n );\n }\n\n // For other rotations (diagonal and reflections), fall back to no rotation\n // These are less common in typical texture atlases\n return out.set(x + frameX, y + frameY, width, height);\n },\n};\n","/** @internal */\nexport const NOOP = () =>\n{\n // empty!\n};\n","import { ExtensionType } from '../../../../../extensions/Extensions';\nimport { TextureSource } from './TextureSource';\n\nimport type { ExtensionMetadata } from '../../../../../extensions/Extensions';\nimport type { TypedArray } from '../../buffer/Buffer';\nimport type { TextureSourceOptions } from './TextureSource';\n\n/**\n * Options for creating a BufferImageSource.\n * @category rendering\n * @advanced\n */\nexport interface BufferSourceOptions extends TextureSourceOptions\n{\n width: number;\n height: number;\n}\n\n/**\n * A texture source that uses a TypedArray or ArrayBuffer as its resource.\n * It automatically determines the format based on the type of TypedArray provided.\n * @category rendering\n * @advanced\n */\nexport class BufferImageSource extends TextureSource\n{\n public static extension: ExtensionMetadata = ExtensionType.TextureSource;\n\n public uploadMethodId = 'buffer';\n\n constructor(options: BufferSourceOptions)\n {\n const buffer = options.resource || new Float32Array(options.width * options.height * 4);\n let format = options.format;\n\n if (!format)\n {\n if (buffer instanceof Float32Array)\n {\n format = 'rgba32float';\n }\n else if (buffer instanceof Int32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Uint32Array)\n {\n format = 'rgba32uint';\n }\n else if (buffer instanceof Int16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Uint16Array)\n {\n format = 'rgba16uint';\n }\n else if (buffer instanceof Int8Array)\n {\n format = 'bgra8unorm';\n }\n else\n {\n format = 'bgra8unorm';\n }\n }\n\n super({\n ...options,\n resource: buffer,\n format,\n });\n }\n\n public static test(resource: any): resource is TypedArray | ArrayBuffer\n {\n return resource instanceof Int8Array\n || resource instanceof Uint8Array\n || resource instanceof Uint8ClampedArray\n || resource instanceof Int16Array\n || resource instanceof Uint16Array\n || resource instanceof Int32Array\n || resource instanceof Uint32Array\n || resource instanceof Float32Array;\n }\n}\n","import { Matrix } from '../../../../maths/matrix/Matrix';\n\nimport type { Texture } from './Texture';\n\nconst tempMat = new Matrix();\n\n/**\n * Class controls uv mapping from Texture normal space to BaseTexture normal space.\n *\n * Takes `trim` and `rotate` into account. May contain clamp settings for Meshes and TilingSprite.\n *\n * Can be used in Texture `uvMatrix` field, or separately, you can use different clamp settings on the same texture.\n * If you want to add support for texture region of certain feature or filter, that's what you're looking for.\n *\n * Takes track of Texture changes through `_lastTextureID` private field.\n * Use `update()` method call to track it from outside.\n * @see Texture\n * @see Mesh\n * @see TilingSprite\n * @category rendering\n * @advanced\n */\nexport class TextureMatrix\n{\n /**\n * Matrix operation that converts texture region coords to texture coords\n * @readonly\n */\n public mapCoord: Matrix;\n\n /**\n * Changes frame clamping\n * Works with TilingSprite and Mesh\n * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders\n * @default 0\n */\n public clampOffset: number;\n\n /**\n * Changes frame clamping\n * Works with TilingSprite and Mesh\n * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas\n * @default 0.5\n */\n public clampMargin: number;\n\n /**\n * Clamp region for normalized coords, left-top pixel center in xy , bottom-right in zw.\n * Calculated based on clampOffset.\n */\n public readonly uClampFrame: Float32Array;\n\n /** Normalized clamp offset. Calculated based on clampOffset. */\n public readonly uClampOffset: Float32Array;\n\n /**\n * Tracks Texture frame changes.\n * @ignore\n */\n public _updateID: number;\n\n /**\n * Tracks Texture frame changes.\n * @protected\n */\n protected _textureID: number;\n\n protected _texture: Texture;\n\n /**\n * If texture size is the same as baseTexture.\n * @default false\n * @readonly\n */\n public isSimple: boolean;\n\n /**\n * @param texture - observed texture\n * @param clampMargin - Changes frame clamping, 0.5 by default. Use -0.5 for extra border.\n */\n constructor(texture: Texture, clampMargin?: number)\n {\n this.mapCoord = new Matrix();\n this.uClampFrame = new Float32Array(4);\n this.uClampOffset = new Float32Array(2);\n this._textureID = -1;\n this._updateID = 0;\n\n this.clampOffset = 0;\n\n if ((typeof clampMargin === 'undefined'))\n {\n this.clampMargin = (texture.width < 10) ? 0 : 0.5;\n }\n else\n {\n this.clampMargin = clampMargin;\n }\n\n this.isSimple = false;\n\n this.texture = texture;\n }\n\n /** Texture property. */\n get texture(): Texture\n {\n return this._texture;\n }\n\n set texture(value: Texture)\n {\n if (this.texture === value) return;\n\n this._texture?.removeListener('update', this.update, this);\n this._texture = value;\n this._texture.addListener('update', this.update, this);\n\n this.update();\n }\n\n /**\n * Multiplies uvs array to transform\n * @param uvs - mesh uvs\n * @param [out=uvs] - output\n * @returns - output\n */\n public multiplyUvs(uvs: Float32Array, out?: Float32Array): Float32Array\n {\n if (out === undefined)\n {\n out = uvs;\n }\n\n const mat = this.mapCoord;\n\n for (let i = 0; i < uvs.length; i += 2)\n {\n const x = uvs[i];\n const y = uvs[i + 1];\n\n out[i] = (x * mat.a) + (y * mat.c) + mat.tx;\n out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;\n }\n\n return out;\n }\n\n /**\n * Updates matrices if texture was changed\n * @returns - whether or not it was updated\n */\n public update(): boolean\n {\n const tex = this._texture;\n\n this._updateID++;\n\n const uvs = tex.uvs;\n\n this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0);\n\n const orig = tex.orig;\n const trim = tex.trim;\n\n if (trim)\n {\n tempMat.set(\n orig.width / trim.width,\n 0, 0, orig.height / trim.height,\n -trim.x / trim.width,\n -trim.y / trim.height\n );\n\n this.mapCoord.append(tempMat);\n }\n\n const texBase = tex.source;\n const frame = this.uClampFrame;\n const margin = this.clampMargin / texBase._resolution;\n const offset = this.clampOffset / texBase._resolution;\n\n frame[0] = (tex.frame.x + margin + offset) / texBase.width;\n frame[1] = (tex.frame.y + margin + offset) / texBase.height;\n frame[2] = (tex.frame.x + tex.frame.width - margin + offset) / texBase.width;\n frame[3] = (tex.frame.y + tex.frame.height - margin + offset) / texBase.height;\n\n this.uClampOffset[0] = this.clampOffset / texBase.pixelWidth;\n this.uClampOffset[1] = this.clampOffset / texBase.pixelHeight;\n\n this.isSimple = tex.frame.width === texBase.width\n && tex.frame.height === texBase.height\n && tex.rotate === 0;\n\n return true;\n }\n}\n","import EventEmitter from 'eventemitter3';\nimport { groupD8 } from '../../../../maths/matrix/groupD8';\nimport { Rectangle } from '../../../../maths/shapes/Rectangle';\nimport { uid } from '../../../../utils/data/uid';\nimport { deprecation, v8_0_0 } from '../../../../utils/logging/deprecation';\nimport { NOOP } from '../../../../utils/misc/NOOP';\nimport { BufferImageSource } from './sources/BufferImageSource';\nimport { TextureSource } from './sources/TextureSource';\nimport { TextureMatrix } from './TextureMatrix';\n\nimport type { TextureResourceOrOptions } from './utils/textureFrom';\n\n/**\n * Stores the width of the non-scalable borders, for example when used with {@link NineSlicePlane} texture.\n * @category rendering\n * @advanced\n */\nexport interface TextureBorders\n{\n /** left border in pixels */\n left: number;\n /** top border in pixels */\n top: number;\n /** right border in pixels */\n right: number;\n /** bottom border in pixels */\n bottom: number;\n}\n\n/**\n * The UVs data structure for a texture.\n * @category rendering\n * @advanced\n */\nexport type UVs = {\n x0: number;\n y0: number;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n x3: number;\n y3: number;\n};\n\n/**\n * The options that can be passed to a new Texture\n * @category rendering\n * @standard\n */\nexport interface TextureOptions\n{\n /** the underlying texture data that this texture will use */\n source?: TextureSourceType;\n /** optional label, for debugging */\n label?: string;\n /** The rectangle frame of the texture to show */\n frame?: Rectangle;\n /** The area of original texture */\n orig?: Rectangle;\n /** Trimmed rectangle of original texture */\n trim?: Rectangle;\n /** Default anchor point used for sprite placement / rotation */\n defaultAnchor?: { x: number; y: number };\n /** Default borders used for 9-slice scaling {@link NineSlicePlane}*/\n defaultBorders?: TextureBorders;\n /** indicates how the texture was rotated by texture packer. See {@link groupD8} */\n rotate?: number;\n /**\n * Set to true if you plan on modifying this texture's frame, UVs, or swapping its source at runtime.\n * This is false by default as it improves performance. Generally, it's recommended to create new\n * textures and swap those rather than modifying an existing texture's properties unless you are\n * working with a dynamic frames.\n * Not setting this to true when modifying the texture can lead to visual artifacts.\n *\n * If this is false and you modify the texture, you can manually update the sprite's texture by calling\n * `sprite.onViewUpdate()`.\n */\n dynamic?: boolean;\n}\n\n/**\n * A texture that can be bound to a shader as it has a texture source.\n * @category rendering\n * @advanced\n */\nexport interface BindableTexture\n{\n source: TextureSource;\n}\n\n/**\n * A texture source can be a string, an image, a video, a canvas, or a texture resource.\n * @category rendering\n * @advanced\n * @see {@link TextureSource}\n * @see {@link TextureResourceOrOptions}\n * @see {@link Texture.from}\n */\nexport type TextureSourceLike = TextureSource | TextureResourceOrOptions | string;\n\n/**\n * A texture stores the information that represents an image or part of an image.\n *\n * A texture must have a loaded resource passed to it to work. It does not contain any\n * loading mechanisms.\n *\n * The Assets class can be used to load a texture from a file. This is the recommended\n * way as it will handle the loading and caching for you.\n *\n * ```js\n *\n * const texture = await Assets.load('assets/image.png');\n *\n * // once Assets has loaded the image it will be available via the from method\n * const sameTexture = Texture.from('assets/image.png');\n * // another way to access the texture once loaded\n * const sameAgainTexture = Assets.get('assets/image.png');\n *\n * const sprite1 = new Sprite(texture);\n *\n * ```\n *\n * It cannot be added to the display list directly; instead use it as the texture for a Sprite.\n * If no frame is provided for a texture, then the whole image is used.\n *\n * You can directly create a texture from an image and then reuse it multiple times like this :\n *\n * ```js\n * import { Sprite, Texture } from 'pixi.js';\n *\n * const texture = await Assets.load('assets/image.png');\n * const sprite1 = new Sprite(texture);\n * const sprite2 = new Sprite(texture);\n * ```\n *\n * If you didn't pass the texture frame to constructor, it enables `noFrame` mode:\n * it subscribes on baseTexture events, it automatically resizes at the same time as baseTexture.\n * @category rendering\n * @class\n * @standard\n */\nexport class Texture extends EventEmitter<{\n update: Texture\n destroy: Texture\n}> implements BindableTexture\n{\n /**\n * Helper function that creates a returns Texture based on the source you provide.\n * The source should be loaded and ready to go. If not its best to grab the asset using Assets.\n * @param id - String or Source to create texture from\n * @param skipCache - Skip adding the texture to the cache\n * @returns The texture based on the Id provided\n */\n public static from: (id: TextureSourceLike, skipCache?: boolean) => Texture;\n\n /** label used for debugging */\n public label?: string;\n /** unique id for this texture */\n public readonly uid: number = uid('texture');\n /**\n * Has the texture been destroyed?\n * @readonly\n */\n public destroyed: boolean;\n\n /** @internal */\n public _source: TextureSourceType;\n\n /**\n * Indicates whether the texture is rotated inside the atlas\n * set to 2 to compensate for texture packer rotation\n * set to 6 to compensate for spine packer rotation\n * can be used to rotate or mirror sprites\n * See {@link groupD8} for explanation\n */\n public readonly rotate: number;\n /** A uvs object based on the given frame and the texture source */\n public readonly uvs: UVs = { x0: 0, y0: 0, x1: 0, y1: 0, x2: 0, y2: 0, x3: 0, y3: 0 };\n /**\n * Anchor point that is used as default if sprite is created with this texture.\n * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point.\n * @default {0,0}\n */\n public readonly defaultAnchor?: { x: number; y: number };\n /**\n * Default width of the non-scalable border that is used if 9-slice plane is created with this texture.\n * @since 7.2.0\n * @see NineSliceSprite\n */\n public readonly defaultBorders?: TextureBorders;\n /**\n * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering,\n * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases)\n */\n public readonly frame = new Rectangle();\n /** This is the area of original texture, before it was put in atlas. */\n public readonly orig: Rectangle;\n /**\n * This is the trimmed area of original texture, before it was put in atlas\n * Please call `updateUvs()` after you change coordinates of `trim` manually.\n */\n public readonly trim: Rectangle;\n\n /**\n * Does this Texture have any frame data assigned to it?\n *\n * This mode is enabled automatically if no frame was passed inside constructor.\n *\n * In this mode texture is subscribed to baseTexture events, and fires `update` on any change.\n *\n * Beware, after loading or resize of baseTexture event can fired two times!\n * If you want more control, subscribe on baseTexture itself.\n * @example\n * texture.on('update', () => {});\n */\n public noFrame = false;\n\n /**\n * Set to true if you plan on modifying the uvs of this texture.\n * When this is the case, sprites and other objects using the texture will\n * make sure to listen for changes to the uvs and update their vertices accordingly.\n */\n public dynamic = false;\n\n private _textureMatrix: TextureMatrix;\n\n /** is it a texture? yes! used for type checking */\n public readonly isTexture = true;\n\n /**\n * @param {TextureOptions} options - Options for the texture\n */\n constructor({\n source,\n label,\n frame,\n orig,\n trim,\n defaultAnchor,\n defaultBorders,\n rotate,\n dynamic\n }: TextureOptions = {})\n {\n super();\n\n this.label = label;\n this.source = (source?.source ?? new TextureSource()) as TextureSourceType;\n\n this.noFrame = !frame;\n\n if (frame)\n {\n this.frame.copyFrom(frame);\n }\n else\n {\n const { width, height } = this._source;\n\n this.frame.width = width;\n this.frame.height = height;\n }\n\n this.orig = orig || this.frame;\n this.trim = trim;\n\n this.rotate = rotate ?? 0;\n this.defaultAnchor = defaultAnchor;\n this.defaultBorders = defaultBorders;\n\n this.destroyed = false;\n this.dynamic = dynamic || false;\n\n this.updateUvs();\n }\n\n set source(value: TextureSourceType)\n {\n if (this._source)\n {\n this._source.off('resize', this.update, this);\n }\n\n this._source = value;\n\n value.on('resize', this.update, this);\n\n this.emit('update', this);\n }\n\n /** the underlying source of the texture (equivalent of baseTexture in v7) */\n get source(): TextureSourceType\n {\n return this._source;\n }\n\n /** returns a TextureMatrix instance for this texture. By default, that object is not created because its heavy. */\n get textureMatrix()\n {\n if (!this._textureMatrix)\n {\n this._textureMatrix = new TextureMatrix(this);\n }\n\n return this._textureMatrix;\n }\n\n /** The width of the Texture in pixels. */\n get width(): number\n {\n return this.orig.width;\n }\n\n /** The height of the Texture in pixels. */\n get height(): number\n {\n return this.orig.height;\n }\n\n /** Call this function when you have modified the frame of this texture. */\n public updateUvs()\n {\n const { uvs, frame } = this;\n const { width, height } = this._source;\n\n const nX = frame.x / width;\n const nY = frame.y / height;\n\n const nW = frame.width / width;\n const nH = frame.height / height;\n\n let rotate = this.rotate;\n\n if (rotate)\n {\n // width and height div 2 div baseFrame size\n const w2 = nW / 2;\n const h2 = nH / 2;\n\n // coordinates of center\n const cX = nX + w2;\n const cY = nY + h2;\n\n rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner\n uvs.x0 = cX + (w2 * groupD8.uX(rotate));\n uvs.y0 = cY + (h2 * groupD8.uY(rotate));\n\n rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise\n uvs.x1 = cX + (w2 * groupD8.uX(rotate));\n uvs.y1 = cY + (h2 * groupD8.uY(rotate));\n\n rotate = groupD8.add(rotate, 2);\n uvs.x2 = cX + (w2 * groupD8.uX(rotate));\n uvs.y2 = cY + (h2 * groupD8.uY(rotate));\n\n rotate = groupD8.add(rotate, 2);\n uvs.x3 = cX + (w2 * groupD8.uX(rotate));\n uvs.y3 = cY + (h2 * groupD8.uY(rotate));\n }\n\n else\n {\n uvs.x0 = nX;\n uvs.y0 = nY;\n uvs.x1 = nX + nW;\n uvs.y1 = nY;\n uvs.x2 = nX + nW;\n uvs.y2 = nY + nH;\n uvs.x3 = nX;\n uvs.y3 = nY + nH;\n }\n }\n\n /**\n * Destroys this texture\n * @param destroySource - Destroy the source when the texture is destroyed.\n */\n public destroy(destroySource = false)\n {\n if (this._source)\n {\n if (destroySource)\n {\n this._source.destroy();\n this._source = null;\n }\n }\n\n this._textureMatrix = null;\n this.destroyed = true;\n this.emit('destroy', this);\n this.removeAllListeners();\n }\n\n /**\n * Call this if you have modified the `texture outside` of the constructor.\n *\n * If you have modified this texture's source, you must separately call `texture.source.update()` to see those changes.\n */\n public update(): void\n {\n if (this.noFrame)\n {\n this.frame.width = this._source.width;\n this.frame.height = this._source.height;\n }\n\n this.updateUvs();\n this.emit('update', this);\n }\n\n /** @deprecated since 8.0.0 */\n get baseTexture(): TextureSource\n {\n // #if _DEBUG\n deprecation(v8_0_0, 'Texture.baseTexture is now Texture.source');\n // #endif\n\n return this._source;\n }\n\n /** an Empty Texture used internally by the engine */\n public static EMPTY: Texture;\n /** a White texture used internally by the engine */\n public static WHITE: Texture;\n}\n\nTexture.EMPTY = new Texture({\n label: 'EMPTY',\n source: new TextureSource({\n label: 'EMPTY',\n })\n});\n\nTexture.EMPTY.destroy = NOOP;\n\nTexture.WHITE = new Texture({\n source: new BufferImageSource({\n resource: new Uint8Array([255, 255, 255, 255]),\n width: 1,\n height: 1,\n alphaMode: 'premultiply-alpha-on-upload',\n label: 'WHITE',\n }),\n label: 'WHITE',\n});\n\nTexture.WHITE.destroy = NOOP;\n","import { nextPow2 } from '../../../../maths/misc/pow2';\nimport { TextureSource } from './sources/TextureSource';\nimport { Texture } from './Texture';\nimport { TextureStyle } from './TextureStyle';\n\nimport type { TextureSourceOptions } from './sources/TextureSource';\n\nlet count = 0;\n\n/**\n * Texture pool, used by FilterSystem and plugins.\n *\n * Stores collection of temporary pow2 or screen-sized renderTextures\n *\n * If you use custom RenderTexturePool for your filters, you can use methods\n * `getFilterTexture` and `returnFilterTexture` same as in default pool\n * @category rendering\n * @advanced\n */\nexport class TexturePoolClass\n{\n /** The default options for texture pool */\n public textureOptions: TextureSourceOptions;\n\n /** The default texture style for the pool */\n public textureStyle: TextureStyle;\n\n /**\n * Allow renderTextures of the same size as screen, not just pow2\n *\n * Automatically sets to true after `setScreenSize`\n * @default false\n */\n public enableFullScreen: boolean;\n\n private _texturePool: {[x in string | number]: Texture[]};\n private _poolKeyHash: Record = Object.create(null);\n\n /**\n * @param textureOptions - options that will be passed to BaseRenderTexture constructor\n * @param {SCALE_MODE} [textureOptions.scaleMode] - See {@link SCALE_MODE} for possible values.\n */\n constructor(textureOptions?: TextureSourceOptions)\n {\n this._texturePool = {};\n this.textureOptions = textureOptions || {};\n this.enableFullScreen = false;\n this.textureStyle = new TextureStyle(this.textureOptions);\n }\n\n /**\n * Creates texture with params that were specified in pool constructor.\n * @param pixelWidth - Width of texture in pixels.\n * @param pixelHeight - Height of texture in pixels.\n * @param antialias\n */\n public createTexture(pixelWidth: number, pixelHeight: number, antialias: boolean): Texture\n {\n const textureSource = new TextureSource({\n ...this.textureOptions,\n\n width: pixelWidth,\n height: pixelHeight,\n resolution: 1,\n antialias,\n autoGarbageCollect: false,\n });\n\n return new Texture({\n source: textureSource,\n label: `texturePool_${count++}`,\n });\n }\n\n /**\n * Gets a Power-of-Two render texture or fullScreen texture\n * @param frameWidth - The minimum width of the render texture.\n * @param frameHeight - The minimum height of the render texture.\n * @param resolution - The resolution of the render texture.\n * @param antialias\n * @returns The new render texture.\n */\n public getOptimalTexture(frameWidth: number, frameHeight: number, resolution = 1, antialias: boolean): Texture\n {\n let po2Width = Math.ceil((frameWidth * resolution) - 1e-6);\n let po2Height = Math.ceil((frameHeight * resolution) - 1e-6);\n\n po2Width = nextPow2(po2Width);\n po2Height = nextPow2(po2Height);\n\n const key = (po2Width << 17) + (po2Height << 1) + (antialias ? 1 : 0);\n\n if (!this._texturePool[key])\n {\n this._texturePool[key] = [];\n }\n\n let texture = this._texturePool[key].pop();\n\n if (!texture)\n {\n texture = this.createTexture(po2Width, po2Height, antialias);\n }\n\n texture.source._resolution = resolution;\n texture.source.width = po2Width / resolution;\n texture.source.height = po2Height / resolution;\n texture.source.pixelWidth = po2Width;\n texture.source.pixelHeight = po2Height;\n\n // fit the layout to the requested original size\n texture.frame.x = 0;\n texture.frame.y = 0;\n texture.frame.width = frameWidth;\n texture.frame.height = frameHeight;\n\n texture.updateUvs();\n\n this._poolKeyHash[texture.uid] = key;\n\n return texture;\n }\n\n /**\n * Gets extra texture of the same size as input renderTexture\n * @param texture - The texture to check what size it is.\n * @param antialias - Whether to use antialias.\n * @returns A texture that is a power of two\n */\n public getSameSizeTexture(texture: Texture, antialias = false)\n {\n const source = texture.source;\n\n return this.getOptimalTexture(texture.width, texture.height, source._resolution, antialias);\n }\n\n /**\n * Place a render texture back into the pool. Optionally reset the style of the texture to the default texture style.\n * useful if you modified the style of the texture after getting it from the pool.\n * @param renderTexture - The renderTexture to free\n * @param resetStyle - Whether to reset the style of the texture to the default texture style\n */\n public returnTexture(renderTexture: Texture, resetStyle = false): void\n {\n const key = this._poolKeyHash[renderTexture.uid];\n\n // we can skip the copy if we don't need to reset the style\n if (resetStyle)\n {\n renderTexture.source.style = this.textureStyle;\n }\n\n this._texturePool[key].push(renderTexture);\n }\n\n /**\n * Clears the pool.\n * @param destroyTextures - Destroy all stored textures.\n */\n public clear(destroyTextures?: boolean): void\n {\n destroyTextures = destroyTextures !== false;\n if (destroyTextures)\n {\n for (const i in this._texturePool)\n {\n const textures = this._texturePool[i];\n\n if (textures)\n {\n for (let j = 0; j < textures.length; j++)\n {\n textures[j].destroy(true);\n }\n }\n }\n }\n\n this._texturePool = {};\n }\n}\n\n/**\n * The default texture pool instance.\n * @category rendering\n * @advanced\n */\nexport const TexturePool = new TexturePoolClass();\n","import { Matrix } from '../../maths/matrix/Matrix';\nimport { InstructionSet } from '../../rendering/renderers/shared/instructions/InstructionSet';\nimport { type SCALE_MODE } from '../../rendering/renderers/shared/texture/const';\nimport { TexturePool } from '../../rendering/renderers/shared/texture/TexturePool';\nimport { type Renderer } from '../../rendering/renderers/types';\n\nimport type { Instruction } from '../../rendering/renderers/shared/instructions/Instruction';\nimport type { Texture } from '../../rendering/renderers/shared/texture/Texture';\nimport type { BatchableSprite } from '../sprite/BatchableSprite';\nimport type { ViewContainer } from '../view/ViewContainer';\nimport type { Bounds } from './bounds/Bounds';\nimport type { Container } from './Container';\n\n/**\n * Options for caching a container as a texture.\n * @category rendering\n * @advanced\n */\nexport interface CacheAsTextureOptions\n{\n /**\n * If true, the texture will be antialiased. This smooths out the edges of the texture.\n * @default false\n */\n antialias?: boolean;\n /**\n * The resolution of the texture. A higher resolution means a sharper texture but uses more memory.\n * By default the resolution is 1 which is the same as the rendererers resolution.\n */\n resolution?: number;\n /**\n * Scale Mode to use for the cached texture\n * @type {SCALE_MODE}\n * @default 'linear'\n * @example\n * ```ts\n * const container = new Container();\n * container.cacheAsTexture({ scaleMode: 'nearest' });\n * ```\n * @see {@link SCALE_MODE}\n */\n scaleMode?: SCALE_MODE;\n}\n\n/**\n * A RenderGroup is a class that is responsible for I generating a set of instructions that are used to render the\n * root container and its children. It also watches for any changes in that container or its children,\n * these changes are analysed and either the instruction set is rebuild or the instructions data is updated.\n * @category rendering\n * @advanced\n */\nexport class RenderGroup implements Instruction\n{\n public renderPipeId = 'renderGroup';\n public root: Container = null;\n\n public canBundle = false;\n\n public renderGroupParent: RenderGroup = null;\n public renderGroupChildren: RenderGroup[] = [];\n\n public worldTransform: Matrix = new Matrix();\n public worldColorAlpha = 0xffffffff;\n public worldColor = 0xffffff;\n public worldAlpha = 1;\n\n // these updates are transform changes..\n public readonly childrenToUpdate: Record = Object.create(null);\n public updateTick = 0;\n public gcTick = 0;\n\n // these update are renderable changes..\n public readonly childrenRenderablesToUpdate: { list: Container[]; index: number; } = { list: [], index: 0 };\n\n // other\n public structureDidChange = true;\n\n public instructionSet: InstructionSet = new InstructionSet();\n\n private readonly _onRenderContainers: Container[] = [];\n\n /**\n * Indicates if the cached texture needs to be updated.\n * @default true\n */\n public textureNeedsUpdate = true;\n\n /**\n * Indicates if the container should be cached as a texture.\n * @default false\n */\n public isCachedAsTexture = false;\n\n /**\n * The texture used for caching the container. this is only set if isCachedAsTexture is true.\n * It can only be accessed after a render pass.\n * @type {Texture | undefined}\n */\n public texture?: Texture;\n\n /**\n * The bounds of the cached texture.\n * @type {Bounds | undefined}\n * @ignore\n */\n public _textureBounds?: Bounds;\n\n /**\n * The options for caching the container as a texture.\n * @type {CacheAsTextureOptions}\n */\n public textureOptions: CacheAsTextureOptions;\n\n /**\n * holds a reference to the batchable render sprite\n * @ignore\n */\n public _batchableRenderGroup: BatchableSprite;\n\n /**\n * Holds a reference to the closest parent RenderGroup that has isCachedAsTexture enabled.\n * This is used to properly transform coordinates when rendering into cached textures.\n * @type {RenderGroup | null}\n * @ignore\n */\n public _parentCacheAsTextureRenderGroup: RenderGroup;\n\n private _inverseWorldTransform: Matrix;\n private _textureOffsetInverseTransform: Matrix;\n private _inverseParentTextureTransform: Matrix;\n\n private _matrixDirty = 0b111;\n\n public init(root: Container)\n {\n this.root = root;\n\n if (root._onRender) this.addOnRender(root);\n\n root.didChange = true;\n\n const children = root.children;\n\n for (let i = 0; i < children.length; i++)\n {\n const child = children[i];\n\n // make sure the children are all updated on the first pass..\n child._updateFlags = 0b1111;\n\n this.addChild(child);\n }\n }\n\n public enableCacheAsTexture(options: CacheAsTextureOptions = {}): void\n {\n this.textureOptions = options;\n this.isCachedAsTexture = true;\n this.textureNeedsUpdate = true;\n }\n\n public disableCacheAsTexture(): void\n {\n this.isCachedAsTexture = false;\n if (this.texture)\n {\n TexturePool.returnTexture(this.texture, true);\n this.texture = null;\n }\n }\n\n public updateCacheTexture(): void\n {\n this.textureNeedsUpdate = true;\n }\n\n public reset()\n {\n this.renderGroupChildren.length = 0;\n\n for (const i in this.childrenToUpdate)\n {\n const childrenAtDepth = this.childrenToUpdate[i];\n\n childrenAtDepth.list.fill(null);\n childrenAtDepth.index = 0;\n }\n\n this.childrenRenderablesToUpdate.index = 0;\n this.childrenRenderablesToUpdate.list.fill(null);\n\n this.root = null;\n this.updateTick = 0;\n this.structureDidChange = true;\n\n this._onRenderContainers.length = 0;\n this.renderGroupParent = null;\n\n this.disableCacheAsTexture();\n }\n\n get localTransform()\n {\n return this.root.localTransform;\n }\n\n public addRenderGroupChild(renderGroupChild: RenderGroup)\n {\n if (renderGroupChild.renderGroupParent)\n {\n renderGroupChild.renderGroupParent._removeRenderGroupChild(renderGroupChild);\n }\n\n renderGroupChild.renderGroupParent = this;\n\n this.renderGroupChildren.push(renderGroupChild);\n }\n\n private _removeRenderGroupChild(renderGroupChild: RenderGroup)\n {\n const index = this.renderGroupChildren.indexOf(renderGroupChild);\n\n if (index > -1)\n {\n this.renderGroupChildren.splice(index, 1);\n }\n\n renderGroupChild.renderGroupParent = null;\n }\n\n public addChild(child: Container)\n {\n this.structureDidChange = true;\n\n child.parentRenderGroup = this;\n\n child.updateTick = -1;\n\n if (child.parent === this.root)\n {\n child.relativeRenderGroupDepth = 1;\n }\n else\n {\n child.relativeRenderGroupDepth = child.parent.relativeRenderGroupDepth + 1;\n }\n\n child.didChange = true;\n this.onChildUpdate(child);\n\n if (child.renderGroup)\n {\n this.addRenderGroupChild(child.renderGroup);\n\n return;\n }\n\n if (child._onRender) this.addOnRender(child);\n\n const children = child.children;\n\n for (let i = 0; i < children.length; i++)\n {\n this.addChild(children[i]);\n }\n }\n\n public removeChild(child: Container)\n {\n // remove all the children...\n this.structureDidChange = true;\n\n if (child._onRender)\n {\n // Remove the child to the onRender list under the following conditions:\n // 1. If the child is not a render group.\n // 2. If the child is a render group root of this render group - which it can't be removed from in this case.\n if (!child.renderGroup)\n {\n this.removeOnRender(child);\n }\n }\n\n child.parentRenderGroup = null;\n\n if (child.renderGroup)\n {\n this._removeRenderGroupChild(child.renderGroup);\n\n return;\n }\n\n const children = child.children;\n\n for (let i = 0; i < children.length; i++)\n {\n this.removeChild(children[i]);\n }\n }\n\n public removeChildren(children: Container[])\n {\n for (let i = 0; i < children.length; i++)\n {\n this.removeChild(children[i]);\n }\n }\n\n public onChildUpdate(child: Container)\n {\n let childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth];\n\n if (!childrenToUpdate)\n {\n childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth] = {\n index: 0,\n list: [],\n };\n }\n\n childrenToUpdate.list[childrenToUpdate.index++] = child;\n }\n\n public updateRenderable(renderable: ViewContainer)\n {\n if (renderable.globalDisplayStatus < 0b111) return;\n this.instructionSet.renderPipes[renderable.renderPipeId].updateRenderable(renderable);\n renderable.didViewUpdate = false;\n }\n\n public onChildViewUpdate(child: Container)\n {\n this.childrenRenderablesToUpdate.list[this.childrenRenderablesToUpdate.index++] = child;\n }\n\n get isRenderable(): boolean\n {\n return (this.root.localDisplayStatus === 0b111 && this.worldAlpha > 0);\n }\n\n /**\n * adding a container to the onRender list will make sure the user function\n * passed in to the user defined 'onRender` callBack\n * @param container - the container to add to the onRender list\n */\n public addOnRender(container: Container)\n {\n this._onRenderContainers.push(container);\n }\n\n public removeOnRender(container: Container)\n {\n this._onRenderContainers.splice(this._onRenderContainers.indexOf(container), 1);\n }\n\n public runOnRender(renderer: Renderer)\n {\n for (let i = 0; i < this._onRenderContainers.length; i++)\n {\n this._onRenderContainers[i]._onRender(renderer);\n }\n }\n\n public destroy()\n {\n this.disableCacheAsTexture();\n\n this.renderGroupParent = null;\n this.root = null;\n (this.childrenRenderablesToUpdate as any) = null;\n (this.childrenToUpdate as any) = null;\n (this.renderGroupChildren as any) = null;\n (this._onRenderContainers as any) = null;\n this.instructionSet = null;\n }\n\n public getChildren(out: Container[] = []): Container[]\n {\n const children = this.root.children;\n\n for (let i = 0; i < children.length; i++)\n {\n this._getChildren(children[i], out);\n }\n\n return out;\n }\n\n private _getChildren(container: Container, out: Container[] = []): Container[]\n {\n out.push(container);\n\n if (container.renderGroup) return out;\n\n const children = container.children;\n\n for (let i = 0; i < children.length; i++)\n {\n this._getChildren(children[i], out);\n }\n\n return out;\n }\n\n public invalidateMatrices()\n {\n this._matrixDirty = 0b111;\n }\n\n /**\n * Returns the inverse of the world transform matrix.\n * @returns {Matrix} The inverse of the world transform matrix.\n */\n public get inverseWorldTransform()\n {\n if ((this._matrixDirty & 0b001) === 0) return this._inverseWorldTransform;\n\n this._matrixDirty &= ~0b001;\n\n // TODO - add dirty flag\n this._inverseWorldTransform ||= new Matrix();\n\n return this._inverseWorldTransform\n .copyFrom(this.worldTransform)\n .invert();\n }\n\n /**\n * Returns the inverse of the texture offset transform matrix.\n * @returns {Matrix} The inverse of the texture offset transform matrix.\n */\n public get textureOffsetInverseTransform()\n {\n if ((this._matrixDirty & 0b010) === 0) return this._textureOffsetInverseTransform;\n\n this._matrixDirty &= ~0b010;\n\n this._textureOffsetInverseTransform ||= new Matrix();\n\n // TODO shared.. bad!\n return this._textureOffsetInverseTransform\n .copyFrom(this.inverseWorldTransform)\n .translate(\n -this._textureBounds.x,\n -this._textureBounds.y\n );\n }\n\n /**\n * Returns the inverse of the parent texture transform matrix.\n * This is used to properly transform coordinates when rendering into cached textures.\n * @returns {Matrix} The inverse of the parent texture transform matrix.\n */\n public get inverseParentTextureTransform()\n {\n if ((this._matrixDirty & 0b100) === 0) return this._inverseParentTextureTransform;\n\n this._matrixDirty &= ~0b100;\n\n const parentCacheAsTexture = this._parentCacheAsTextureRenderGroup;\n\n if (parentCacheAsTexture)\n {\n this._inverseParentTextureTransform ||= new Matrix();\n\n // Get relative transform by removing parent's world transform\n return this._inverseParentTextureTransform\n .copyFrom(this.worldTransform)\n .prepend(parentCacheAsTexture.inverseWorldTransform)\n // Offset by texture bounds\n .translate(\n -parentCacheAsTexture._textureBounds.x,\n -parentCacheAsTexture._textureBounds.y\n );\n }\n\n return this.worldTransform;\n }\n\n /**\n * Returns a matrix that transforms coordinates to the correct coordinate space of the texture being rendered to.\n * This is the texture offset inverse transform of the closest parent RenderGroup that is cached as a texture.\n * @returns {Matrix | null} The transform matrix for the cached texture coordinate space,\n * or null if no parent is cached as texture.\n */\n public get cacheToLocalTransform()\n {\n if (!this._parentCacheAsTextureRenderGroup) return null;\n\n return this._parentCacheAsTextureRenderGroup.textureOffsetInverseTransform;\n }\n}\n","/**\n * Assigns properties from one object to another, using an optional array of property names to ignore.\n * @param target - The target object to assign properties to.\n * @param options - The object to assign properties from.\n * @param ignore - An object of property names to ignore ({ propToIgnore: true }).\n * @category utils\n * @internal\n */\nexport function assignWithIgnore>(\n target: T,\n options: T,\n ignore: Record = {}\n)\n{\n for (const key in options)\n {\n if (!ignore[key] && options[key] !== undefined)\n {\n target[key] = options[key];\n }\n }\n}\n","import EventEmitter from 'eventemitter3';\nimport { Color, type ColorSource } from '../../color/Color';\nimport { cullingMixin } from '../../culling/cullingMixin';\nimport { extensions } from '../../extensions/Extensions';\nimport { Matrix } from '../../maths/matrix/Matrix';\nimport { DEG_TO_RAD, RAD_TO_DEG } from '../../maths/misc/const';\nimport { ObservablePoint } from '../../maths/point/ObservablePoint';\nimport { uid } from '../../utils/data/uid';\nimport { deprecation, v8_0_0 } from '../../utils/logging/deprecation';\nimport { warn } from '../../utils/logging/warn';\nimport { BigPool } from '../../utils/pool/PoolGroup';\nimport { type IRenderLayer } from '../layers/RenderLayer';\nimport { cacheAsTextureMixin } from './container-mixins/cacheAsTextureMixin';\nimport { childrenHelperMixin } from './container-mixins/childrenHelperMixin';\nimport { collectRenderablesMixin } from './container-mixins/collectRenderablesMixin';\nimport { effectsMixin } from './container-mixins/effectsMixin';\nimport { findMixin } from './container-mixins/findMixin';\nimport { getFastGlobalBoundsMixin } from './container-mixins/getFastGlobalBoundsMixin';\nimport { bgr2rgb, getGlobalMixin } from './container-mixins/getGlobalMixin';\nimport { measureMixin } from './container-mixins/measureMixin';\nimport { onRenderMixin } from './container-mixins/onRenderMixin';\nimport { sortMixin } from './container-mixins/sortMixin';\nimport { toLocalGlobalMixin } from './container-mixins/toLocalGlobalMixin';\nimport { RenderGroup } from './RenderGroup';\nimport { assignWithIgnore } from './utils/assignWithIgnore';\n\nimport type { Size } from '../../maths/misc/Size';\nimport type { PointData } from '../../maths/point/PointData';\nimport type { Rectangle } from '../../maths/shapes/Rectangle';\nimport type { BLEND_MODES } from '../../rendering/renderers/shared/state/const';\nimport type { Dict } from '../../utils/types';\nimport type { Optional } from './container-mixins/measureMixin';\nimport type { DestroyOptions } from './destroyTypes';\n\n/**\n * The type of child that can be added to a {@link Container}.\n * This is a generic type that extends the {@link Container} class.\n * @category scene\n * @standard\n */\nexport type ContainerChild = Container;\n\n// as pivot and skew are the least used properties of a container, we can use this optimisation\n// to avoid allocating lots of unnecessary objects for them.\nconst defaultSkew = new ObservablePoint(null);\nconst defaultPivot = new ObservablePoint(null);\nconst defaultScale = new ObservablePoint(null, 1, 1);\nconst defaultOrigin = new ObservablePoint(null);\n\n/**\n * Events that can be emitted by a Container. These events provide lifecycle hooks and notifications\n * for container state changes.\n * @example\n * ```ts\n * import { Container, Sprite } from 'pixi.js';\n *\n * // Setup container with event listeners\n * const container = new Container();\n *\n * // Listen for child additions\n * container.on('childAdded', (child, container, index) => {\n * console.log(`Child added at index ${index}:`, child);\n * });\n *\n * // Listen for child removals\n * container.on('childRemoved', (child, container, index) => {\n * console.log(`Child removed from index ${index}:`, child);\n * });\n *\n * // Listen for when container is added to parent\n * container.on('added', (parent) => {\n * console.log('Added to parent:', parent);\n * });\n *\n * // Listen for when container is removed from parent\n * container.on('removed', (parent) => {\n * console.log('Removed from parent:', parent);\n * });\n *\n * // Listen for container destruction\n * container.on('destroyed', (container) => {\n * console.log('Container destroyed:', container);\n * });\n * ```\n * @category scene\n * @standard\n */\nexport interface ContainerEvents extends PixiMixins.ContainerEvents\n{\n /**\n * Emitted when this container is added to a new container.\n * Useful for setting up parent-specific behaviors.\n * @param container - The parent container this was added to\n * @example\n * ```ts\n * const child = new Container();\n * child.on('added', (parent) => {\n * console.log('Child added to parent:', parent.label);\n * });\n * parentContainer.addChild(child);\n * ```\n */\n added: [container: Container];\n\n /**\n * Emitted when a child is added to this container.\n * Useful for tracking container composition changes.\n * @param child - The child that was added\n * @param container - The container the child was added to (this container)\n * @param index - The index at which the child was added\n * @example\n * ```ts\n * const parent = new Container();\n * parent.on('childAdded', (child, container, index) => {\n * console.log(`New child at index ${index}:`, child);\n * });\n * ```\n */\n childAdded: [child: C, container: Container, index: number];\n\n /**\n * Emitted when this container is removed from its parent.\n * Useful for cleanup and state management.\n * @param container - The parent container this was removed from\n * @example\n * ```ts\n * const child = new Container();\n * child.on('removed', (oldParent) => {\n * console.log('Child removed from parent:', oldParent.label);\n * });\n * ```\n */\n removed: [container: Container];\n\n /**\n * Emitted when a child is removed from this container.\n * Useful for cleanup and maintaining container state.\n * @param child - The child that was removed\n * @param container - The container the child was removed from (this container)\n * @param index - The index from which the child was removed\n * @example\n * ```ts\n * const parent = new Container();\n * parent.on('childRemoved', (child, container, index) => {\n * console.log(`Child removed from index ${index}:`, child);\n * });\n * ```\n */\n childRemoved: [child: C, container: Container, index: number];\n\n /**\n * Emitted when the container is destroyed.\n * Useful for final cleanup and resource management.\n * @param container - The container that was destroyed\n * @example\n * ```ts\n * const container = new Container();\n * container.on('destroyed', (container) => {\n * console.log('Container destroyed:', container.label);\n * });\n * ```\n */\n destroyed: [container: Container];\n}\n\ntype AnyEvent = {\n // The following is a hack to allow any custom event while maintaining type safety.\n // For some reason, the tsc compiler gets angry about error TS1023\n // \"An index signature parameter type must be either 'string' or 'number'.\"\n // This is really odd since ({}&string) should interpret as string, but then again\n // there is some black magic behind why this works in the first place.\n // Closest thing to an explanation:\n // https://stackoverflow.com/questions/70144348/why-does-a-union-of-type-literals-and-string-cause-ide-code-completion-wh\n //\n // Side note, we disable @typescript-eslint/ban-types since {}&string is the only syntax that works.\n // Nor of the Record/unknown/never alternatives work.\n [K: ({} & string) | ({} & symbol)]: any;\n};\n\n/** @internal */\nexport const UPDATE_COLOR = 0b0001;\n/** @internal */\nexport const UPDATE_BLEND = 0b0010;\n/** @internal */\nexport const UPDATE_VISIBLE = 0b0100;\n/** @internal */\nexport const UPDATE_TRANSFORM = 0b1000;\n\n/**\n * Options for updating the transform of a container.\n * @category scene\n * @standard\n */\nexport interface UpdateTransformOptions\n{\n x: number;\n y: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n skewX: number;\n skewY: number;\n pivotX: number;\n pivotY: number;\n originX: number;\n originY: number;\n}\n\n/**\n * Constructor options used for `Container` instances.\n * ```js\n * const container = new Container({\n * position: new Point(100, 200),\n * scale: new Point(2, 2),\n * rotation: Math.PI / 2,\n * });\n * ```\n * @category scene\n * @standard\n * @see Container\n */\nexport interface ContainerOptions extends PixiMixins.ContainerOptions\n{\n /** @see Container#isRenderGroup */\n isRenderGroup?: boolean;\n\n /**\n * The blend mode to be applied to the sprite. Controls how pixels are blended when rendering.\n *\n * Setting to 'normal' will reset to default blending.\n * > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export.\n * @example\n * ```ts\n * // Basic blend modes\n * new Container({ blendMode: 'normal' }); // Default blending\n * new Container({ blendMode: 'add' }); // Additive blending\n * new Container({ blendMode: 'multiply' }); // Multiply colors\n * new Container({ blendMode: 'screen' }); // Screen blend\n * ```\n * @default 'normal'\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#tint} For color adjustments\n */\n blendMode?: BLEND_MODES;\n /**\n * The tint applied to the sprite.\n *\n * This can be any valid {@link ColorSource}.\n * @example\n * ```ts\n * new Container({ tint: 0xff0000 }); // Red tint\n * new Container({ tint: 'blue' }); // Blue tint\n * new Container({ tint: '#00ff00' }); // Green tint\n * new Container({ tint: 'rgb(0,0,255)' }); // Blue tint\n * ```\n * @default 0xFFFFFF\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#visible} For visibility control\n */\n tint?: ColorSource;\n\n /**\n * The opacity of the object relative to its parent's opacity.\n * Value ranges from 0 (fully transparent) to 1 (fully opaque).\n * @example\n * ```ts\n * new Container({ alpha: 0.5 }); // 50% opacity\n * new Container({ alpha: 1 }); // Fully opaque\n * ```\n * @default 1\n * @see {@link Container#visible} For toggling visibility\n * @see {@link Container#renderable} For render control\n */\n alpha?: number;\n /**\n * The angle of the object in degrees.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * new Container({ angle: 45 }); // Rotate 45 degrees\n * new Container({ angle: 90 }); // Rotate 90 degrees\n * ```\n */\n angle?: number;\n /**\n * The array of children of this container. Each child must be a Container or extend from it.\n *\n * The array is read-only, but its contents can be modified using Container methods.\n * @example\n * ```ts\n * new Container({\n * children: [\n * new Container(), // First child\n * new Container(), // Second child\n * ],\n * });\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChild} For removing children\n */\n children?: C[];\n /**\n * The display object container that contains this display object.\n * This represents the parent-child relationship in the display tree.\n * @readonly\n * @see {@link Container#addChild} For adding to a parent\n * @see {@link Container#removeChild} For removing from parent\n */\n parent?: Container;\n /**\n * Controls whether this object can be rendered. If false the object will not be drawn,\n * but the transform will still be updated. This is different from visible, which skips\n * transform updates.\n * @example\n * ```ts\n * new Container({ renderable: false }); // Will not be drawn, but transforms will update\n * ```\n * @default true\n * @see {@link Container#visible} For skipping transform updates\n * @see {@link Container#alpha} For transparency\n */\n renderable?: boolean;\n /**\n * The rotation of the object in radians.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * new Container({ rotation: Math.PI / 4 }); // Rotate 45 degrees\n * new Container({ rotation: Math.PI / 2 }); // Rotate 90 degrees\n * ```\n */\n rotation?: number;\n /**\n * The scale factors of this object along the local coordinate axes.\n *\n * The default scale is (1, 1).\n * @example\n * ```ts\n * new Container({ scale: new Point(2, 2) }); // Scale by 2x\n * new Container({ scale: 0.5 }); // Scale by 0.5x\n * new Container({ scale: { x: 1.5, y: 1.5 } }); // Scale by 1.5x\n * ```\n */\n scale?: PointData | number;\n /**\n * The center of rotation, scaling, and skewing for this display object in its local space.\n * The `position` is the projection of `pivot` in the parent's local space.\n *\n * By default, the pivot is the origin (0, 0).\n * @example\n * ```ts\n * new Container({ pivot: new Point(100, 200) }); // Set pivot to (100, 200)\n * new Container({ pivot: 50 }); // Set pivot to (50, 50)\n * new Container({ pivot: { x: 150, y: 150 } }); // Set pivot to (150, 150)\n * ```\n */\n pivot?: PointData | number;\n /**\n * The origin point around which the container rotates and scales.\n * Unlike pivot, changing origin will not move the container's position.\n * @example\n * ```ts\n * new Container({ origin: new Point(100, 100) }); // Rotate around point (100,100)\n * new Container({ origin: 50 }); // Rotate around point (50, 50)\n * new Container({ origin: { x: 150, y: 150 } }); // Rotate around point (150, 150)\n * ```\n */\n origin?: PointData | number;\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @example\n * ```ts\n * new Container({ position: new Point(100, 200) }); // Set position to (100, 200)\n * new Container({ position: { x: 150, y: 150 } }); // Set position to (150, 150)\n * ```\n */\n position?: PointData;\n /**\n * The skew factor for the object in radians. Skewing is a transformation that distorts\n * the object by rotating it differently at each point, creating a non-uniform shape.\n * @example\n * ```ts\n * new Container({ skew: new Point(0.1, 0.2) }); // Skew by 0.1 radians on x and 0.2 radians on y\n * new Container({ skew: { x: 0.1, y: 0.2 } }); // Skew by 0.1 radians on x and 0.2 radians on y\n * ```\n * @default { x: 0, y: 0 }\n */\n skew?: PointData;\n /**\n * The visibility of the object. If false the object will not be drawn,\n * and the transform will not be updated.\n * @example\n * ```ts\n * new Container({ visible: false }); // Will not be drawn and transforms will not update\n * new Container({ visible: true }); // Will be drawn and transforms will update\n * ```\n * @default true\n * @see {@link Container#renderable} For render-only control\n * @see {@link Container#alpha} For transparency\n */\n visible?: boolean;\n /**\n * The position of the container on the x axis relative to the local coordinates of the parent.\n *\n * An alias to position.x\n * @example\n * ```ts\n * new Container({ x: 100 }); // Set x position to 100\n * ```\n */\n x?: number;\n /**\n * The position of the container on the y axis relative to the local coordinates of the parent.\n *\n * An alias to position.y\n * @example\n * ```ts\n * new Container({ y: 200 }); // Set y position to 200\n * ```\n */\n y?: number;\n /**\n * An optional bounds area for this container. Setting this rectangle will stop the renderer\n * from recursively measuring the bounds of each children and instead use this single boundArea.\n *\n * > [!IMPORTANT] This is great for optimisation! If for example you have a\n * > 1000 spinning particles and you know they all sit within a specific bounds,\n * > then setting it will mean the renderer will not need to measure the\n * > 1000 children to find the bounds. Instead it will just use the bounds you set.\n * @example\n * ```ts\n * const container = new Container({\n * boundsArea: new Rectangle(0, 0, 500, 500) // Set a fixed bounds area\n * });\n * ```\n */\n boundsArea?: Rectangle;\n}\n\n// eslint-disable-next-line requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface Container\n extends PixiMixins.Container, EventEmitter & AnyEvent> {}\n\n/**\n * Container is a general-purpose display object that holds children. It also adds built-in support for advanced\n * rendering features like masking and filtering.\n *\n * It is the base class of all display objects that act as a container for other objects, including Graphics\n * and Sprite.\n *\n *

\n *\n * Transforms\n *\n * The [transform]{@link Container#localTransform} of a display object describes the projection from its\n * local coordinate space to its parent's local coordinate space. The following properties are derived\n * from the transform:\n *\n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n *
PropertyDescription
[pivot]{@link Container#pivot}\n * Invariant under rotation, scaling, and skewing. The projection of into the parent's space of the pivot\n * is equal to position, regardless of the other three transformations. In other words, It is the center of\n * rotation, scaling, and skewing.\n *
[position]{@link Container#position}\n * Translation. This is the position of the [pivot]{@link Container#pivot} in the parent's local\n * space. The default value of the pivot is the origin (0,0). If the top-left corner of your display object\n * is (0,0) in its local space, then the position will be its top-left corner in the parent's local space.\n *
[scale]{@link Container#scale}\n * Scaling. This will stretch (or compress) the display object's projection. The scale factors are along the\n * local coordinate axes. In other words, the display object is scaled before rotated or skewed. The center\n * of scaling is the [pivot]{@link Container#pivot}.\n *
[rotation]{@link Container#rotation}\n * Rotation. This will rotate the display object's projection by this angle (in radians).\n *
[skew]{@link Container#skew}\n *

Skewing. This can be used to deform a rectangular display object into a parallelogram.

\n *

\n * In PixiJS, skew has a slightly different behaviour than the conventional meaning. It can be\n * thought of the net rotation applied to the coordinate axes (separately). For example, if \"skew.x\" is\n * ⍺ and \"skew.y\" is β, then the line x = 0 will be rotated by ⍺ (y = -x*cot⍺) and the line y = 0 will be\n * rotated by β (y = x*tanβ). A line y = x*tanϴ (i.e. a line at angle ϴ to the x-axis in local-space) will\n * be rotated by an angle between ⍺ and β.\n *

\n *

\n * It can be observed that if skew is applied equally to both axes, then it will be equivalent to applying\n * a rotation. Indeed, if \"skew.x\" = -ϴ and \"skew.y\" = ϴ, it will produce an equivalent of \"rotation\" = ϴ.\n *

\n *

\n * Another quite interesting observation is that \"skew.x\", \"skew.y\", rotation are commutative operations. Indeed,\n * because rotation is essentially a careful combination of the two.\n *

\n *
[angle]{@link Container#angle}Rotation. This is an alias for [rotation]{@link Container#rotation}, but in degrees.
[x]{@link Container#x}Translation. This is an alias for position.x!
[y]{@link Container#y}Translation. This is an alias for position.y!
[width]{@link Container#width}\n * Implemented in [Container]{@link Container}. Scaling. The width property calculates scale.x by dividing\n * the \"requested\" width by the local bounding box width. It is indirectly an abstraction over scale.x, and there\n * is no concept of user-defined width.\n *
[height]{@link Container#height}\n * Implemented in [Container]{@link Container}. Scaling. The height property calculates scale.y by dividing\n * the \"requested\" height by the local bounding box height. It is indirectly an abstraction over scale.y, and there\n * is no concept of user-defined height.\n *
\n *
\n *\n *
\n * Alpha\n *\n * This alpha sets a display object's **relative opacity** w.r.t its parent. For example, if the alpha of a display\n * object is 0.5 and its parent's alpha is 0.5, then it will be rendered with 25% opacity (assuming alpha is not\n * applied on any ancestor further up the chain).\n *
\n *\n *
\n * Renderable vs Visible\n *\n * The `renderable` and `visible` properties can be used to prevent a display object from being rendered to the\n * screen. However, there is a subtle difference between the two. When using `renderable`, the transforms of the display\n * object (and its children subtree) will continue to be calculated. When using `visible`, the transforms will not\n * be calculated.\n * ```ts\n * import { BlurFilter, Container, Graphics, Sprite } from 'pixi.js';\n *\n * const container = new Container();\n * const sprite = Sprite.from('https://s3-us-west-2.amazonaws.com/s.cdpn.io/693612/IaUrttj.png');\n *\n * sprite.width = 512;\n * sprite.height = 512;\n *\n * // Adds a sprite as a child to this container. As a result, the sprite will be rendered whenever the container\n * // is rendered.\n * container.addChild(sprite);\n *\n * // Blurs whatever is rendered by the container\n * container.filters = [new BlurFilter()];\n *\n * // Only the contents within a circle at the center should be rendered onto the screen.\n * container.mask = new Graphics()\n * .beginFill(0xffffff)\n * .drawCircle(sprite.width / 2, sprite.height / 2, Math.min(sprite.width, sprite.height) / 2)\n * .endFill();\n * ```\n *\n *
\n *\n *
\n * RenderGroup\n *\n * In PixiJS v8, containers can be set to operate in 'render group mode',\n * transforming them into entities akin to a stage in traditional rendering paradigms.\n * A render group is a root renderable entity, similar to a container,\n * but it's rendered in a separate pass with its own unique set of rendering instructions.\n * This approach enhances rendering efficiency and organization, particularly in complex scenes.\n *\n * You can enable render group mode on any container using container.enableRenderGroup()\n * or by initializing a new container with the render group property set to true (new Container({isRenderGroup: true})).\n * The method you choose depends on your specific use case and setup requirements.\n *\n * An important aspect of PixiJS’s rendering process is the automatic treatment of rendered scenes as render groups.\n * This conversion streamlines the rendering process, but understanding when and how this happens is crucial\n * to fully leverage its benefits.\n *\n * One of the key advantages of using render groups is the performance efficiency in moving them. Since transformations\n * are applied at the GPU level, moving a render group, even one with complex and numerous children,\n * doesn't require recalculating the rendering instructions or performing transformations on each child.\n * This makes operations like panning a large game world incredibly efficient.\n *\n * However, it's crucial to note that render groups do not batch together.\n * This means that turning every container into a render group could actually slow things down,\n * as each render group is processed separately. It's best to use render groups judiciously, at a broader level,\n * rather than on a per-child basis.\n * This approach ensures you get the performance benefits without overburdening the rendering process.\n *\n * RenderGroups maintain their own set of rendering instructions,\n * ensuring that changes or updates within a render group don't affect the rendering\n * instructions of its parent or other render groups.\n * This isolation ensures more stable and predictable rendering behavior.\n *\n * Additionally, renderGroups can be nested, allowing for powerful options in organizing different aspects of your scene.\n * This feature is particularly beneficial for separating complex game graphics from UI elements,\n * enabling intricate and efficient scene management in complex applications.\n *\n * This means that Containers have 3 levels of matrix to be mindful of:\n *\n * 1. localTransform, this is the transform of the container based on its own properties\n * 2. groupTransform, this it the transform of the container relative to the renderGroup it belongs too\n * 3. worldTransform, this is the transform of the container relative to the Scene being rendered\n *
\n * @category scene\n * @standard\n */\nexport class Container extends EventEmitter & AnyEvent>\n{\n /**\n * Mixes all enumerable properties and methods from a source object to Container.\n * @param source - The source of properties and methods to mix in.\n * @deprecated since 8.8.0\n */\n public static mixin(source: Dict): void\n {\n // #if _DEBUG\n deprecation('8.8.0', 'Container.mixin is deprecated, please use extensions.mixin instead.');\n // #endif\n extensions.mixin(Container, source);\n }\n\n /**\n * unique id for this container\n * @internal\n */\n public readonly uid: number = uid('renderable');\n\n /** @private */\n public _updateFlags = 0b1111;\n\n // the render group this container owns\n /** @private */\n public renderGroup: RenderGroup = null;\n // the render group this container belongs to\n /** @private */\n public parentRenderGroup: RenderGroup = null;\n // the index of the container in the render group\n /** @private */\n public parentRenderGroupIndex: number = 0;\n\n // set to true if the container has changed. It is reset once the changes have been applied\n // by the transform system\n // its here to stop ensure that when things change, only one update gets registers with the transform system\n /** @private */\n public didChange = false;\n // same as above, but for the renderable\n /** @private */\n public didViewUpdate = false;\n\n // how deep is the container relative to its render group..\n // unless the element is the root render group - it will be relative to its parent\n /** @private */\n public relativeRenderGroupDepth = 0;\n\n /**\n * The array of children of this container. Each child must be a Container or extend from it.\n *\n * The array is read-only, but its contents can be modified using Container methods.\n * @example\n * ```ts\n * // Access children\n * const firstChild = container.children[0];\n * const lastChild = container.children[container.children.length - 1];\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChild} For removing children\n */\n public children: C[] = [];\n /**\n * The display object container that contains this display object.\n * This represents the parent-child relationship in the display tree.\n * @example\n * ```ts\n * // Basic parent access\n * const parent = sprite.parent;\n *\n * // Walk up the tree\n * let current = sprite;\n * while (current.parent) {\n * console.log('Level up:', current.parent.constructor.name);\n * current = current.parent;\n * }\n * ```\n * @readonly\n * @see {@link Container#addChild} For adding to a parent\n * @see {@link Container#removeChild} For removing from parent\n */\n public parent: Container | null = null;\n\n // used internally for changing up the render order.. mainly for masks and filters\n // TODO setting this should cause a rebuild??\n /** @private */\n public includeInBuild = true;\n /** @private */\n public measurable = true;\n /** @private */\n public isSimple = true;\n\n /**\n * The RenderLayer this container belongs to, if any.\n * If it belongs to a RenderLayer, it will be rendered from the RenderLayer's position in the scene.\n * @readonly\n * @advanced\n */\n public parentRenderLayer: IRenderLayer;\n\n // / /////////////Transform related props//////////////\n\n // used by the transform system to check if a container needs to be updated that frame\n // if the tick matches the current transform system tick, it is not updated again\n /** @internal */\n public updateTick = -1;\n\n /**\n * Current transform of the object based on local factors: position, scale, other stuff.\n * This matrix represents the local transformation without any parent influence.\n * @example\n * ```ts\n * // Basic transform access\n * const localMatrix = sprite.localTransform;\n * console.log(localMatrix.toString());\n * ```\n * @readonly\n * @see {@link Container#worldTransform} For global transform\n * @see {@link Container#groupTransform} For render group transform\n */\n public localTransform: Matrix = new Matrix();\n /**\n * The relative group transform is a transform relative to the render group it belongs too. It will include all parent\n * transforms and up to the render group (think of it as kind of like a stage - but the stage can be nested).\n * If this container is is self a render group matrix will be relative to its parent render group\n * @readonly\n * @advanced\n */\n public relativeGroupTransform: Matrix = new Matrix();\n /**\n * The group transform is a transform relative to the render group it belongs too.\n * If this container is render group then this will be an identity matrix. other wise it\n * will be the same as the relativeGroupTransform.\n * Use this value when actually rendering things to the screen\n * @readonly\n * @advanced\n */\n public groupTransform: Matrix = this.relativeGroupTransform;\n\n // the global transform taking into account the render group and all parents\n private _worldTransform: Matrix;\n\n /**\n * Whether this object has been destroyed. If true, the object should no longer be used.\n * After an object is destroyed, all of its functionality is disabled and references are removed.\n * @example\n * ```ts\n * // Cleanup with destroy\n * sprite.destroy();\n * console.log(sprite.destroyed); // true\n * ```\n * @default false\n * @see {@link Container#destroy} For destroying objects\n */\n public destroyed = false;\n\n // transform data..\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @internal\n */\n public _position: ObservablePoint = new ObservablePoint(this, 0, 0);\n\n /**\n * The scale factor of the object.\n * @internal\n */\n public _scale: ObservablePoint = defaultScale;\n\n /**\n * The pivot point of the container that it rotates around.\n * @internal\n */\n public _pivot: ObservablePoint = defaultPivot;\n\n /**\n * The origin point around which the container rotates and scales.\n * Unlike pivot, changing origin will not move the container's position.\n * @private\n */\n public _origin: ObservablePoint = defaultOrigin;\n\n /**\n * The skew amount, on the x and y axis.\n * @internal\n */\n public _skew: ObservablePoint = defaultSkew;\n\n /**\n * The X-coordinate value of the normalized local X axis,\n * the first column of the local transformation matrix without a scale.\n * @internal\n */\n public _cx = 1;\n\n /**\n * The Y-coordinate value of the normalized local X axis,\n * the first column of the local transformation matrix without a scale.\n * @internal\n */\n public _sx = 0;\n\n /**\n * The X-coordinate value of the normalized local Y axis,\n * the second column of the local transformation matrix without a scale.\n * @internal\n */\n public _cy = 0;\n\n /**\n * The Y-coordinate value of the normalized local Y axis,\n * the second column of the local transformation matrix without a scale.\n * @internal\n */\n public _sy = 1;\n\n /**\n * The rotation amount.\n * @internal\n */\n private _rotation = 0;\n\n // / COLOR related props //////////////\n\n // color stored as ABGR\n /** @internal */\n public localColor = 0xFFFFFF;\n /** @internal */\n public localAlpha = 1;\n\n /** @internal */\n public groupAlpha = 1; // A\n /** @internal */\n public groupColor = 0xFFFFFF; // BGR\n /** @internal */\n public groupColorAlpha = 0xFFFFFFFF; // ABGR\n\n // / BLEND related props //////////////\n\n /** @internal */\n public localBlendMode: BLEND_MODES = 'inherit';\n /** @internal */\n public groupBlendMode: BLEND_MODES = 'normal';\n\n // / VISIBILITY related props //////////////\n\n // visibility\n // 0b11\n // first bit is visible, second bit is renderable\n /**\n * This property holds three bits: culled, visible, renderable\n * the third bit represents culling (0 = culled, 1 = not culled) 0b100\n * the second bit represents visibility (0 = not visible, 1 = visible) 0b010\n * the first bit represents renderable (0 = not renderable, 1 = renderable) 0b001\n * @internal\n */\n public localDisplayStatus = 0b111; // 0b11 | 0b10 | 0b01 | 0b00\n /** @internal */\n public globalDisplayStatus = 0b111; // 0b11 | 0b10 | 0b01 | 0b00\n\n /** @internal */\n public readonly renderPipeId: string;\n\n /**\n * An optional bounds area for this container. Setting this rectangle will stop the renderer\n * from recursively measuring the bounds of each children and instead use this single boundArea.\n *\n * > [!IMPORTANT] This is great for optimisation! If for example you have a\n * > 1000 spinning particles and you know they all sit within a specific bounds,\n * > then setting it will mean the renderer will not need to measure the\n * > 1000 children to find the bounds. Instead it will just use the bounds you set.\n * @example\n * ```ts\n * const container = new Container();\n * container.boundsArea = new Rectangle(0, 0, 500, 500);\n * ```\n */\n public boundsArea: Rectangle;\n\n /**\n * A value that increments each time the containe is modified\n * eg children added, removed etc\n * @ignore\n */\n public _didContainerChangeTick = 0;\n /**\n * A value that increments each time the container view is modified\n * eg texture swap, geometry change etc\n * @ignore\n */\n public _didViewChangeTick = 0;\n\n /** @internal */\n public layerParentId: string;// = 'default';\n /**\n * We now use the _didContainerChangeTick and _didViewChangeTick to track changes\n * @deprecated since 8.2.6\n * @ignore\n */\n set _didChangeId(value: number)\n {\n this._didViewChangeTick = (value >> 12) & 0xFFF; // Extract the upper 12 bits\n this._didContainerChangeTick = value & 0xFFF; // Extract the lower 12 bits\n }\n /** @ignore */\n get _didChangeId(): number\n {\n return (this._didContainerChangeTick & 0xfff) | ((this._didViewChangeTick & 0xfff) << 12);\n }\n\n /**\n * property that tracks if the container transform has changed\n * @ignore\n */\n private _didLocalTransformChangeId = -1;\n\n constructor(options: ContainerOptions = {})\n {\n super();\n\n this.effects = [];\n assignWithIgnore(this, options, {\n children: true,\n parent: true,\n effects: true,\n });\n\n options.children?.forEach((child) => this.addChild(child));\n options.parent?.addChild(this);\n }\n\n /**\n * Adds one or more children to the container.\n * The children will be rendered as part of this container's display list.\n * @example\n * ```ts\n * // Add a single child\n * container.addChild(sprite);\n *\n * // Add multiple children\n * container.addChild(background, player, foreground);\n *\n * // Add with type checking\n * const sprite = container.addChild(new Sprite(texture));\n * sprite.tint = 'red';\n * ```\n * @param children - The Container(s) to add to the container\n * @returns The first child that was added\n * @see {@link Container#removeChild} For removing children\n * @see {@link Container#addChildAt} For adding at specific index\n */\n public addChild(...children: U): U[0]\n {\n // #if _DEBUG\n if (!this.allowChildren)\n {\n deprecation(v8_0_0, 'addChild: Only Containers will be allowed to add children in v8.0.0');\n }\n // #endif\n\n if (children.length > 1)\n {\n // loop through the array and add all children\n for (let i = 0; i < children.length; i++)\n {\n this.addChild(children[i]);\n }\n\n return children[0];\n }\n\n const child = children[0] as C;\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (child.parent === this)\n {\n this.children.splice(this.children.indexOf(child), 1);\n this.children.push(child);\n\n if (renderGroup)\n {\n renderGroup.structureDidChange = true;\n }\n\n return child;\n }\n\n if (child.parent)\n {\n // TODO Optimisation...if the parent has the same render group, this does not need to change!\n child.parent.removeChild(child);\n }\n\n this.children.push(child);\n\n if (this.sortableChildren) this.sortDirty = true;\n\n child.parent = this;\n\n child.didChange = true;\n\n // TODO - Optimise this? could check what the parent has set?\n child._updateFlags = 0b1111;\n\n if (renderGroup)\n {\n renderGroup.addChild(child);\n }\n\n this.emit('childAdded', child, this, this.children.length - 1);\n child.emit('added', this);\n\n this._didViewChangeTick++;\n\n if (child._zIndex !== 0)\n {\n child.depthOfChildModified();\n }\n\n return child;\n }\n\n /**\n * Removes one or more children from the container.\n * When removing multiple children, events will be triggered for each child in sequence.\n * @example\n * ```ts\n * // Remove a single child\n * const removed = container.removeChild(sprite);\n *\n * // Remove multiple children\n * const bg = container.removeChild(background, player, userInterface);\n *\n * // Remove with type checking\n * const sprite = container.removeChild(childSprite);\n * sprite.texture = newTexture;\n * ```\n * @param children - The Container(s) to remove\n * @returns The first child that was removed\n * @see {@link Container#addChild} For adding children\n * @see {@link Container#removeChildren} For removing multiple children\n */\n public removeChild(...children: U): U[0]\n {\n // if there is only one argument we can bypass looping through the them\n if (children.length > 1)\n {\n // loop through the arguments property and remove all children\n for (let i = 0; i < children.length; i++)\n {\n this.removeChild(children[i]);\n }\n\n return children[0];\n }\n\n const child = children[0] as C;\n\n const index = this.children.indexOf(child);\n\n if (index > -1)\n {\n this._didViewChangeTick++;\n\n this.children.splice(index, 1);\n\n if (this.renderGroup)\n {\n this.renderGroup.removeChild(child);\n }\n else if (this.parentRenderGroup)\n {\n this.parentRenderGroup.removeChild(child);\n }\n\n if (child.parentRenderLayer)\n {\n child.parentRenderLayer.detach(child);\n }\n\n child.parent = null;\n this.emit('childRemoved', child, this, index);\n child.emit('removed', this);\n }\n\n return child;\n }\n\n /** @ignore */\n public _onUpdate(point?: ObservablePoint)\n {\n if (point)\n {\n // this.updateFlags |= UPDATE_TRANSFORM;\n\n if (point === this._skew)\n {\n this._updateSkew();\n }\n }\n\n this._didContainerChangeTick++;\n\n if (this.didChange) return;\n this.didChange = true;\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.onChildUpdate(this);\n }\n }\n\n set isRenderGroup(value: boolean)\n {\n if (!!this.renderGroup === value) return;\n\n if (value)\n {\n this.enableRenderGroup();\n }\n else\n {\n this.disableRenderGroup();\n }\n }\n\n /**\n * Returns true if this container is a render group.\n * This means that it will be rendered as a separate pass, with its own set of instructions\n * @advanced\n */\n get isRenderGroup(): boolean\n {\n return !!this.renderGroup;\n }\n\n /**\n * Calling this enables a render group for this container.\n * This means it will be rendered as a separate set of instructions.\n * The transform of the container will also be handled on the GPU rather than the CPU.\n * @advanced\n */\n public enableRenderGroup(): void\n {\n if (this.renderGroup) return;\n\n const parentRenderGroup = this.parentRenderGroup;\n\n parentRenderGroup?.removeChild(this);\n\n this.renderGroup = BigPool.get(RenderGroup, this);\n\n // this group matrix will now be an identity matrix,\n // as its own transform will be passed to the GPU\n this.groupTransform = Matrix.IDENTITY;\n\n parentRenderGroup?.addChild(this);\n\n this._updateIsSimple();\n }\n\n /**\n * This will disable the render group for this container.\n * @advanced\n */\n public disableRenderGroup(): void\n {\n if (!this.renderGroup) return;\n\n const parentRenderGroup = this.parentRenderGroup;\n\n parentRenderGroup?.removeChild(this);\n\n BigPool.return(this.renderGroup);\n\n this.renderGroup = null;\n this.groupTransform = this.relativeGroupTransform;\n\n parentRenderGroup?.addChild(this);\n\n this._updateIsSimple();\n }\n\n /** @ignore */\n public _updateIsSimple()\n {\n this.isSimple = !(this.renderGroup) && (this.effects.length === 0);\n }\n\n /**\n * Current transform of the object based on world (parent) factors.\n *\n * This matrix represents the absolute transformation in the scene graph.\n * @example\n * ```ts\n * // Get world position\n * const worldPos = container.worldTransform;\n * console.log(`World position: (${worldPos.tx}, ${worldPos.ty})`);\n * ```\n * @readonly\n * @see {@link Container#localTransform} For local space transform\n */\n get worldTransform()\n {\n this._worldTransform ||= new Matrix();\n\n if (this.renderGroup)\n {\n this._worldTransform.copyFrom(this.renderGroup.worldTransform);\n }\n else if (this.parentRenderGroup)\n {\n this._worldTransform.appendFrom(this.relativeGroupTransform, this.parentRenderGroup.worldTransform);\n }\n\n return this._worldTransform;\n }\n\n /**\n * The position of the container on the x axis relative to the local coordinates of the parent.\n *\n * An alias to position.x\n * @example\n * ```ts\n * // Basic position\n * container.x = 100;\n * ```\n */\n get x(): number\n {\n return this._position.x;\n }\n\n set x(value: number)\n {\n this._position.x = value;\n }\n\n /**\n * The position of the container on the y axis relative to the local coordinates of the parent.\n *\n * An alias to position.y\n * @example\n * ```ts\n * // Basic position\n * container.y = 200;\n * ```\n */\n get y(): number\n {\n return this._position.y;\n }\n\n set y(value: number)\n {\n this._position.y = value;\n }\n\n /**\n * The coordinate of the object relative to the local coordinates of the parent.\n * @example\n * ```ts\n * // Basic position setting\n * container.position.set(100, 200);\n * container.position.set(100); // Sets both x and y to 100\n * // Using point data\n * container.position = { x: 50, y: 75 };\n * ```\n * @since 4.0.0\n */\n get position(): ObservablePoint\n {\n return this._position;\n }\n\n set position(value: PointData)\n {\n this._position.copyFrom(value);\n }\n\n /**\n * The rotation of the object in radians.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * // Basic rotation\n * container.rotation = Math.PI / 4; // 45 degrees\n *\n * // Convert from degrees\n * const degrees = 45;\n * container.rotation = degrees * Math.PI / 180;\n *\n * // Rotate around center\n * container.pivot.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // 180 degrees\n *\n * // Rotate around center with origin\n * container.origin.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // 180 degrees\n * ```\n */\n get rotation(): number\n {\n return this._rotation;\n }\n\n set rotation(value: number)\n {\n if (this._rotation !== value)\n {\n this._rotation = value;\n this._onUpdate(this._skew);\n }\n }\n\n /**\n * The angle of the object in degrees.\n *\n * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object;\n * > rotation is in radians, angle is in degrees.\n * @example\n * ```ts\n * // Basic angle rotation\n * sprite.angle = 45; // 45 degrees\n *\n * // Rotate around center\n * sprite.pivot.set(sprite.width / 2, sprite.height / 2);\n * sprite.angle = 180; // Half rotation\n *\n * // Rotate around center with origin\n * sprite.origin.set(sprite.width / 2, sprite.height / 2);\n * sprite.angle = 180; // Half rotation\n *\n * // Reset rotation\n * sprite.angle = 0;\n * ```\n */\n get angle(): number\n {\n return this.rotation * RAD_TO_DEG;\n }\n\n set angle(value: number)\n {\n this.rotation = value * DEG_TO_RAD;\n }\n\n /**\n * The center of rotation, scaling, and skewing for this display object in its local space.\n * The `position` is the projection of `pivot` in the parent's local space.\n *\n * By default, the pivot is the origin (0, 0).\n * @example\n * ```ts\n * // Rotate around center\n * container.pivot.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // Rotates around center\n * ```\n * @since 4.0.0\n */\n get pivot(): ObservablePoint\n {\n if (this._pivot === defaultPivot)\n {\n this._pivot = new ObservablePoint(this, 0, 0);\n }\n\n return this._pivot;\n }\n\n set pivot(value: PointData | number)\n {\n if (this._pivot === defaultPivot)\n {\n this._pivot = new ObservablePoint(this, 0, 0);\n\n // #if _DEBUG\n if (this._origin !== defaultOrigin)\n {\n // eslint-disable-next-line max-len\n warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`);\n }\n // #endif\n }\n\n typeof value === 'number' ? this._pivot.set(value) : this._pivot.copyFrom(value);\n }\n\n /**\n * The skew factor for the object in radians. Skewing is a transformation that distorts\n * the object by rotating it differently at each point, creating a non-uniform shape.\n * @example\n * ```ts\n * // Basic skewing\n * container.skew.set(0.5, 0); // Skew horizontally\n * container.skew.set(0, 0.5); // Skew vertically\n *\n * // Skew with point data\n * container.skew = { x: 0.3, y: 0.3 }; // Diagonal skew\n *\n * // Reset skew\n * container.skew.set(0, 0);\n *\n * // Animate skew\n * app.ticker.add(() => {\n * // Create wave effect\n * container.skew.x = Math.sin(Date.now() / 1000) * 0.3;\n * });\n *\n * // Combine with rotation\n * container.rotation = Math.PI / 4; // 45 degrees\n * container.skew.set(0.2, 0.2); // Skew the rotated object\n * ```\n * @since 4.0.0\n * @type {ObservablePoint} Point-like object with x/y properties in radians\n * @default {x: 0, y: 0}\n */\n get skew(): ObservablePoint\n {\n if (this._skew === defaultSkew)\n {\n this._skew = new ObservablePoint(this, 0, 0);\n }\n\n return this._skew;\n }\n\n set skew(value: PointData)\n {\n if (this._skew === defaultSkew)\n {\n this._skew = new ObservablePoint(this, 0, 0);\n }\n\n this._skew.copyFrom(value);\n }\n\n /**\n * The scale factors of this object along the local coordinate axes.\n *\n * The default scale is (1, 1).\n * @example\n * ```ts\n * // Basic scaling\n * container.scale.set(2, 2); // Scales to double size\n * container.scale.set(2); // Scales uniformly to double size\n * container.scale = 2; // Scales uniformly to double size\n * // Scale to a specific width and height\n * container.setSize(200, 100); // Sets width to 200 and height to 100\n * ```\n * @since 4.0.0\n */\n get scale(): ObservablePoint\n {\n if (this._scale === defaultScale)\n {\n this._scale = new ObservablePoint(this, 1, 1);\n }\n\n return this._scale;\n }\n\n set scale(value: PointData | number | string)\n {\n if (this._scale === defaultScale)\n {\n this._scale = new ObservablePoint(this, 0, 0);\n }\n\n if (typeof value === 'string')\n {\n value = parseFloat(value);\n }\n\n typeof value === 'number' ? this._scale.set(value) : this._scale.copyFrom(value);\n }\n\n /**\n * @experimental\n * The origin point around which the container rotates and scales without affecting its position.\n * Unlike pivot, changing the origin will not move the container's position.\n * @example\n * ```ts\n * // Rotate around center point\n * container.origin.set(container.width / 2, container.height / 2);\n * container.rotation = Math.PI; // Rotates around center\n *\n * // Reset origin\n * container.origin.set(0, 0);\n * ```\n */\n get origin(): ObservablePoint\n {\n if (this._origin === defaultOrigin)\n {\n this._origin = new ObservablePoint(this, 0, 0);\n }\n\n return this._origin;\n }\n\n set origin(value: PointData | number)\n {\n if (this._origin === defaultOrigin)\n {\n this._origin = new ObservablePoint(this, 0, 0);\n\n // #if _DEBUG\n if (this._pivot !== defaultPivot)\n {\n // eslint-disable-next-line max-len\n warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`);\n }\n // #endif\n }\n\n typeof value === 'number' ? this._origin.set(value) : this._origin.copyFrom(value);\n }\n\n /**\n * The width of the Container, setting this will actually modify the scale to achieve the value set.\n * > [!NOTE] Changing the width will adjust the scale.x property of the container while maintaining its aspect ratio.\n * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}\n * as it is more optimized by not recalculating the local bounds twice.\n * @example\n * ```ts\n * // Basic width setting\n * container.width = 100;\n * // Optimized width setting\n * container.setSize(100, 100);\n * ```\n */\n get width(): number\n {\n return Math.abs(this.scale.x * this.getLocalBounds().width);\n }\n\n set width(value: number)\n {\n const localWidth = this.getLocalBounds().width;\n\n this._setWidth(value, localWidth);\n }\n\n /**\n * The height of the Container,\n * > [!NOTE] Changing the height will adjust the scale.y property of the container while maintaining its aspect ratio.\n * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize}\n * as it is more optimized by not recalculating the local bounds twice.\n * @example\n * ```ts\n * // Basic height setting\n * container.height = 200;\n * // Optimized height setting\n * container.setSize(100, 200);\n * ```\n */\n get height(): number\n {\n return Math.abs(this.scale.y * this.getLocalBounds().height);\n }\n\n set height(value: number)\n {\n const localHeight = this.getLocalBounds().height;\n\n this._setHeight(value, localHeight);\n }\n\n /**\n * Retrieves the size of the container as a [Size]{@link Size} object.\n *\n * This is faster than get the width and height separately.\n * @example\n * ```ts\n * // Basic size retrieval\n * const size = container.getSize();\n * console.log(`Size: ${size.width}x${size.height}`);\n *\n * // Reuse existing size object\n * const reuseSize = { width: 0, height: 0 };\n * container.getSize(reuseSize);\n * ```\n * @param out - Optional object to store the size in.\n * @returns The size of the container.\n */\n public getSize(out?: Size): Size\n {\n if (!out)\n {\n out = {} as Size;\n }\n\n const bounds = this.getLocalBounds();\n\n out.width = Math.abs(this.scale.x * bounds.width);\n out.height = Math.abs(this.scale.y * bounds.height);\n\n return out;\n }\n\n /**\n * Sets the size of the container to the specified width and height.\n * This is more efficient than setting width and height separately as it only recalculates bounds once.\n * @example\n * ```ts\n * // Basic size setting\n * container.setSize(100, 200);\n *\n * // Set uniform size\n * container.setSize(100); // Sets both width and height to 100\n * ```\n * @param value - This can be either a number or a [Size]{@link Size} object.\n * @param height - The height to set. Defaults to the value of `width` if not provided.\n */\n public setSize(value: number | Optional, height?: number)\n {\n const size = this.getLocalBounds();\n\n if (typeof value === 'object')\n {\n height = value.height ?? value.width;\n value = value.width;\n }\n else\n {\n height ??= value;\n }\n\n value !== undefined && this._setWidth(value, size.width);\n height !== undefined && this._setHeight(height, size.height);\n }\n\n /** Called when the skew or the rotation changes. */\n private _updateSkew(): void\n {\n const rotation = this._rotation;\n const skew = this._skew;\n\n this._cx = Math.cos(rotation + skew._y);\n this._sx = Math.sin(rotation + skew._y);\n this._cy = -Math.sin(rotation - skew._x); // cos, added PI/2\n this._sy = Math.cos(rotation - skew._x); // sin, added PI/2\n }\n\n /**\n * Updates the transform properties of the container.\n * Allows partial updates of transform properties for optimized manipulation.\n * @example\n * ```ts\n * // Basic transform update\n * container.updateTransform({\n * x: 100,\n * y: 200,\n * rotation: Math.PI / 4\n * });\n *\n * // Scale and rotate around center\n * sprite.updateTransform({\n * pivotX: sprite.width / 2,\n * pivotY: sprite.height / 2,\n * scaleX: 2,\n * scaleY: 2,\n * rotation: Math.PI\n * });\n *\n * // Update position only\n * button.updateTransform({\n * x: button.x + 10, // Move right\n * y: button.y // Keep same y\n * });\n * ```\n * @param opts - Transform options to update\n * @param opts.x - The x position\n * @param opts.y - The y position\n * @param opts.scaleX - The x-axis scale factor\n * @param opts.scaleY - The y-axis scale factor\n * @param opts.rotation - The rotation in radians\n * @param opts.skewX - The x-axis skew factor\n * @param opts.skewY - The y-axis skew factor\n * @param opts.pivotX - The x-axis pivot point\n * @param opts.pivotY - The y-axis pivot point\n * @returns This container, for chaining\n * @see {@link Container#setFromMatrix} For matrix-based transforms\n * @see {@link Container#position} For direct position access\n */\n public updateTransform(opts: Partial): this\n {\n this.position.set(\n typeof opts.x === 'number' ? opts.x : this.position.x,\n typeof opts.y === 'number' ? opts.y : this.position.y\n );\n this.scale.set(\n typeof opts.scaleX === 'number' ? opts.scaleX || 1 : this.scale.x,\n typeof opts.scaleY === 'number' ? opts.scaleY || 1 : this.scale.y\n );\n this.rotation = typeof opts.rotation === 'number' ? opts.rotation : this.rotation;\n this.skew.set(\n typeof opts.skewX === 'number' ? opts.skewX : this.skew.x,\n typeof opts.skewY === 'number' ? opts.skewY : this.skew.y\n );\n this.pivot.set(\n typeof opts.pivotX === 'number' ? opts.pivotX : this.pivot.x,\n typeof opts.pivotY === 'number' ? opts.pivotY : this.pivot.y\n );\n this.origin.set(\n typeof opts.originX === 'number' ? opts.originX : this.origin.x,\n typeof opts.originY === 'number' ? opts.originY : this.origin.y\n );\n\n return this;\n }\n\n /**\n * Updates the local transform properties by decomposing the given matrix.\n * Extracts position, scale, rotation, and skew from a transformation matrix.\n * @example\n * ```ts\n * // Basic matrix transform\n * const matrix = new Matrix()\n * .translate(100, 100)\n * .rotate(Math.PI / 4)\n * .scale(2, 2);\n *\n * container.setFromMatrix(matrix);\n *\n * // Copy transform from another container\n * const source = new Container();\n * source.position.set(100, 100);\n * source.rotation = Math.PI / 2;\n *\n * target.setFromMatrix(source.localTransform);\n *\n * // Reset transform\n * container.setFromMatrix(Matrix.IDENTITY);\n * ```\n * @param matrix - The matrix to use for updating the transform\n * @see {@link Container#updateTransform} For property-based updates\n * @see {@link Matrix#decompose} For matrix decomposition details\n */\n public setFromMatrix(matrix: Matrix): void\n {\n matrix.decompose(this);\n }\n\n /** Updates the local transform. */\n public updateLocalTransform(): void\n {\n const localTransformChangeId = this._didContainerChangeTick;\n\n if (this._didLocalTransformChangeId === localTransformChangeId) return;\n\n this._didLocalTransformChangeId = localTransformChangeId;\n\n const lt = this.localTransform;\n const scale = this._scale;\n const pivot = this._pivot;\n const origin = this._origin;\n const position = this._position;\n\n const sx = scale._x;\n const sy = scale._y;\n\n const px = pivot._x;\n const py = pivot._y;\n\n const ox = -origin._x;\n const oy = -origin._y;\n\n // get the matrix values of the container based on its this properties..\n lt.a = this._cx * sx;\n lt.b = this._sx * sx;\n lt.c = this._cy * sy;\n lt.d = this._sy * sy;\n\n lt.tx = position._x - ((px * lt.a) + (py * lt.c)) // Pivot offset\n + ((ox * lt.a) + (oy * lt.c)) // Origin offset for rotation and scaling\n - ox; // Remove origin to maintain position\n lt.ty = position._y - ((px * lt.b) + (py * lt.d)) // Pivot offset\n + ((ox * lt.b) + (oy * lt.d)) // Origin offset for rotation and scaling\n - oy; // Remove origin to maintain position\n }\n\n // / ///// color related stuff\n\n set alpha(value: number)\n {\n if (value === this.localAlpha) return;\n\n this.localAlpha = value;\n\n this._updateFlags |= UPDATE_COLOR;\n\n this._onUpdate();\n }\n\n /**\n * The opacity of the object relative to its parent's opacity.\n * Value ranges from 0 (fully transparent) to 1 (fully opaque).\n * @example\n * ```ts\n * // Basic transparency\n * sprite.alpha = 0.5; // 50% opacity\n *\n * // Inherited opacity\n * container.alpha = 0.5;\n * const child = new Sprite(texture);\n * child.alpha = 0.5;\n * container.addChild(child);\n * // child's effective opacity is 0.25 (0.5 * 0.5)\n * ```\n * @default 1\n * @see {@link Container#visible} For toggling visibility\n * @see {@link Container#renderable} For render control\n */\n get alpha(): number\n {\n return this.localAlpha;\n }\n\n set tint(value: ColorSource)\n {\n const tempColor = Color.shared.setValue(value ?? 0xFFFFFF);\n const bgr = tempColor.toBgrNumber();\n\n if (bgr === this.localColor) return;\n\n this.localColor = bgr;\n\n this._updateFlags |= UPDATE_COLOR;\n\n this._onUpdate();\n }\n\n /**\n * The tint applied to the sprite.\n *\n * This can be any valid {@link ColorSource}.\n * @example\n * ```ts\n * // Basic color tinting\n * container.tint = 0xff0000; // Red tint\n * container.tint = 'red'; // Same as above\n * container.tint = '#00ff00'; // Green\n * container.tint = 'rgb(0,0,255)'; // Blue\n *\n * // Remove tint\n * container.tint = 0xffffff; // White = no tint\n * container.tint = null; // Also removes tint\n * ```\n * @default 0xFFFFFF\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#visible} For visibility control\n */\n get tint(): number\n {\n // convert bgr to rgb..\n return bgr2rgb(this.localColor);\n }\n\n // / //////////////// blend related stuff\n\n set blendMode(value: BLEND_MODES)\n {\n if (this.localBlendMode === value) return;\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.structureDidChange = true;\n }\n\n this._updateFlags |= UPDATE_BLEND;\n\n this.localBlendMode = value;\n\n this._onUpdate();\n }\n\n /**\n * The blend mode to be applied to the sprite. Controls how pixels are blended when rendering.\n *\n * Setting to 'normal' will reset to default blending.\n * > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export.\n * @example\n * ```ts\n * // Basic blend modes\n * sprite.blendMode = 'add'; // Additive blending\n * sprite.blendMode = 'multiply'; // Multiply colors\n * sprite.blendMode = 'screen'; // Screen blend\n *\n * // Reset blend mode\n * sprite.blendMode = 'normal'; // Normal blending\n * ```\n * @default 'normal'\n * @see {@link Container#alpha} For transparency\n * @see {@link Container#tint} For color adjustments\n */\n get blendMode(): BLEND_MODES\n {\n return this.localBlendMode;\n }\n\n // / ///////// VISIBILITY / RENDERABLE /////////////////\n\n /**\n * The visibility of the object. If false the object will not be drawn,\n * and the transform will not be updated.\n * @example\n * ```ts\n * // Basic visibility toggle\n * sprite.visible = false; // Hide sprite\n * sprite.visible = true; // Show sprite\n * ```\n * @default true\n * @see {@link Container#renderable} For render-only control\n * @see {@link Container#alpha} For transparency\n */\n get visible()\n {\n return !!(this.localDisplayStatus & 0b010);\n }\n\n set visible(value: boolean)\n {\n const valueNumber = value ? 0b010 : 0;\n\n if ((this.localDisplayStatus & 0b010) === valueNumber) return;\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.structureDidChange = true;\n }\n\n this._updateFlags |= UPDATE_VISIBLE;\n\n this.localDisplayStatus ^= 0b010;\n\n this._onUpdate();\n }\n\n /** @ignore */\n get culled()\n {\n return !(this.localDisplayStatus & 0b100);\n }\n\n /** @ignore */\n set culled(value: boolean)\n {\n const valueNumber = value ? 0 : 0b100;\n\n if ((this.localDisplayStatus & 0b100) === valueNumber) return;\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.structureDidChange = true;\n }\n\n this._updateFlags |= UPDATE_VISIBLE;\n this.localDisplayStatus ^= 0b100;\n\n this._onUpdate();\n }\n\n /**\n * Controls whether this object can be rendered. If false the object will not be drawn,\n * but the transform will still be updated. This is different from visible, which skips\n * transform updates.\n * @example\n * ```ts\n * // Basic render control\n * sprite.renderable = false; // Skip rendering\n * sprite.renderable = true; // Enable rendering\n * ```\n * @default true\n * @see {@link Container#visible} For skipping transform updates\n * @see {@link Container#alpha} For transparency\n */\n get renderable()\n {\n return !!(this.localDisplayStatus & 0b001);\n }\n\n set renderable(value: boolean)\n {\n const valueNumber = value ? 0b001 : 0;\n\n if ((this.localDisplayStatus & 0b001) === valueNumber) return;\n\n this._updateFlags |= UPDATE_VISIBLE;\n this.localDisplayStatus ^= 0b001;\n\n if (this.parentRenderGroup)\n {\n this.parentRenderGroup.structureDidChange = true;\n }\n\n this._onUpdate();\n }\n\n /**\n * Whether or not the object should be rendered.\n * @advanced\n */\n get isRenderable(): boolean\n {\n return (this.localDisplayStatus === 0b111 && this.groupAlpha > 0);\n }\n\n /**\n * Removes all internal references and listeners as well as removes children from the display list.\n * Do not use a Container after calling `destroy`.\n * @param options - Options parameter. A boolean will act as if all options\n * have been set to that value\n * @example\n * ```ts\n * container.destroy();\n * container.destroy(true);\n * container.destroy({ children: true });\n * container.destroy({ children: true, texture: true, textureSource: true });\n * ```\n */\n public destroy(options: DestroyOptions = false): void\n {\n if (this.destroyed) return;\n this.destroyed = true;\n\n // remove children is faster than removeChild..\n\n let oldChildren: ContainerChild[];\n\n // we add this check as calling removeChildren on particle container will throw an error\n // As we know it does cannot have any children, check before calling the function.\n if (this.children.length)\n {\n oldChildren = this.removeChildren(0, this.children.length);\n }\n\n this.removeFromParent();\n this.parent = null;\n this._maskEffect = null;\n this._filterEffect = null;\n this.effects = null;\n this._position = null;\n this._scale = null;\n this._pivot = null;\n this._origin = null;\n this._skew = null;\n\n this.emit('destroyed', this);\n\n this.removeAllListeners();\n\n const destroyChildren = typeof options === 'boolean' ? options : options?.children;\n\n if (destroyChildren && oldChildren)\n {\n for (let i = 0; i < oldChildren.length; ++i)\n {\n oldChildren[i].destroy(options);\n }\n }\n\n this.renderGroup?.destroy();\n this.renderGroup = null;\n }\n}\n\nextensions.mixin(\n Container,\n childrenHelperMixin,\n getFastGlobalBoundsMixin,\n toLocalGlobalMixin,\n onRenderMixin,\n measureMixin,\n effectsMixin,\n findMixin,\n sortMixin,\n cullingMixin,\n cacheAsTextureMixin,\n getGlobalMixin,\n collectRenderablesMixin,\n);\n","/**\n * Represents the update priorities used by internal Pixi classes when registered with\n * the {@link Ticker} object. Higher priority items are updated first and lower\n * priority items, such as render, should go later.\n * @enum {number}\n * @category ticker\n * @standard\n */\nexport enum UPDATE_PRIORITY\n{\n /**\n * Highest priority used for interaction events in {@link EventSystem}\n * @default 50\n */\n INTERACTION = 50,\n /**\n * High priority updating, used by {@link AnimatedSprite}\n * @default 25\n */\n HIGH = 25,\n /**\n * Default priority for ticker events, see {@link Ticker#add}.\n * @default 0\n */\n NORMAL = 0,\n /**\n * Low priority used for {@link Application} rendering.\n * @default -25\n */\n LOW = -25,\n /**\n * Lowest priority used for {@link PrepareBase} utility.\n * @default -50\n */\n UTILITY = -50,\n}\n","import type { Ticker, TickerCallback } from './Ticker';\n\n/**\n * Internal class for handling the priority sorting of ticker handlers.\n * @private\n * @class\n */\nexport class TickerListener\n{\n /** The current priority. */\n public priority: number;\n /** The next item in chain. */\n public next: TickerListener = null;\n /** The previous item in chain. */\n public previous: TickerListener = null;\n\n /** The handler function to execute. */\n private _fn: TickerCallback;\n /** The calling to execute. */\n private _context: T;\n /** If this should only execute once. */\n private readonly _once: boolean;\n /** `true` if this listener has been destroyed already. */\n private _destroyed = false;\n\n /**\n * Constructor\n * @private\n * @param fn - The listener function to be added for one update\n * @param context - The listener context\n * @param priority - The priority for emitting\n * @param once - If the handler should fire once\n */\n constructor(fn: TickerCallback, context: T = null, priority = 0, once = false)\n {\n this._fn = fn;\n this._context = context;\n this.priority = priority;\n this._once = once;\n }\n\n /**\n * Simple compare function to figure out if a function and context match.\n * @param fn - The listener function to be added for one update\n * @param context - The listener context\n * @returns `true` if the listener match the arguments\n */\n public match(fn: TickerCallback, context: any = null): boolean\n {\n return this._fn === fn && this._context === context;\n }\n\n /**\n * Emit by calling the current function.\n * @param ticker - The ticker emitting.\n * @returns Next ticker\n */\n public emit(ticker: Ticker): TickerListener\n {\n if (this._fn)\n {\n if (this._context)\n {\n this._fn.call(this._context, ticker);\n }\n else\n {\n (this as TickerListener)._fn(ticker);\n }\n }\n\n const redirect = this.next;\n\n if (this._once)\n {\n this.destroy(true);\n }\n\n // Soft-destroying should remove\n // the next reference\n if (this._destroyed)\n {\n this.next = null;\n }\n\n return redirect;\n }\n\n /**\n * Connect to the list.\n * @param previous - Input node, previous listener\n */\n public connect(previous: TickerListener): void\n {\n this.previous = previous;\n if (previous.next)\n {\n previous.next.previous = this;\n }\n this.next = previous.next;\n previous.next = this;\n }\n\n /**\n * Destroy and don't use after this.\n * @param hard - `true` to remove the `next` reference, this\n * is considered a hard destroy. Soft destroy maintains the next reference.\n * @returns The listener to redirect while emitting or removing.\n */\n public destroy(hard = false): TickerListener\n {\n this._destroyed = true;\n this._fn = null;\n this._context = null;\n\n // Disconnect, hook up next and previous\n if (this.previous)\n {\n this.previous.next = this.next;\n }\n\n if (this.next)\n {\n this.next.previous = this.previous;\n }\n\n // Redirect to the next item\n const redirect = this.next;\n\n // Remove references\n this.next = hard ? null : redirect;\n this.previous = null;\n\n return redirect;\n }\n}\n","import { UPDATE_PRIORITY } from './const';\nimport { TickerListener } from './TickerListener';\n\n/**\n * A callback which can be added to a ticker.\n * ```js\n * ticker.add(() => {\n * // do something every frame\n * });\n * ```\n * @category ticker\n * @standard\n */\nexport type TickerCallback = (this: T, ticker: Ticker) => any;\n\n/**\n * A Ticker class that runs an update loop that other objects listen to.\n * Used for managing animation frames and timing in a PixiJS application.\n *\n * It provides a way to add listeners that will be called on each frame,\n * allowing for smooth animations and updates.\n *\n * Animation frames are requested\n * only when necessary, e.g., when the ticker is started and the emitter has listeners.\n * @example\n * ```ts\n * // Basic ticker usage\n * const ticker = new Ticker();\n * ticker.add((ticker) => {\n * // Update every frame\n * sprite.rotation += 0.1 * ticker.deltaTime;\n * });\n * ticker.start();\n *\n * // Control update priority\n * ticker.add(\n * (ticker) => {\n * // High priority updates run first\n * physics.update(ticker.deltaTime);\n * },\n * undefined,\n * UPDATE_PRIORITY.HIGH\n * );\n *\n * // One-time updates\n * ticker.addOnce(() => {\n * console.log('Runs on next frame only');\n * });\n * ```\n * @see {@link TickerPlugin} For use with Application\n * @see {@link UPDATE_PRIORITY} For priority constants\n * @see {@link TickerCallback} For listener function type\n * @category ticker\n * @standard\n */\nexport class Ticker\n{\n /**\n * Target frame rate in frames per millisecond.\n * Used for converting deltaTime to a scalar time delta.\n * @example\n * ```ts\n * // Default is 0.06 (60 FPS)\n * console.log(Ticker.targetFPMS); // 0.06\n *\n * // Calculate target frame duration\n * const frameDuration = 1 / Ticker.targetFPMS; // ≈ 16.67ms\n *\n * // Use in custom timing calculations\n * const deltaTime = elapsedMS * Ticker.targetFPMS;\n * ```\n * @remarks\n * - Default is 0.06 (equivalent to 60 FPS)\n * - Used in deltaTime calculations\n * - Affects all ticker instances\n * @default 0.06\n * @see {@link Ticker#deltaTime} For time scaling\n * @see {@link Ticker#FPS} For actual frame rate\n */\n public static targetFPMS = 0.06;\n\n /** The private shared ticker instance */\n private static _shared: Ticker;\n /** The private system ticker instance */\n private static _system: Ticker;\n\n /**\n * Whether or not this ticker should invoke the method {@link Ticker#start|start}\n * automatically when a listener is added.\n * @example\n * ```ts\n * // Default behavior (manual start)\n * const ticker = new Ticker();\n * ticker.autoStart = false;\n * ticker.add(() => {\n * // Won't run until ticker.start() is called\n * });\n *\n * // Auto-start behavior\n * const autoTicker = new Ticker();\n * autoTicker.autoStart = true;\n * autoTicker.add(() => {\n * // Runs immediately when added\n * });\n * ```\n * @default false\n * @see {@link Ticker#start} For manually starting the ticker\n * @see {@link Ticker#stop} For manually stopping the ticker\n */\n public autoStart = false;\n /**\n * Scalar time value from last frame to this frame.\n * Used for frame-based animations and updates.\n *\n * This value is capped by setting {@link Ticker#minFPS|minFPS}\n * and is scaled with {@link Ticker#speed|speed}.\n * > [!NOTE] The cap may be exceeded by scaling.\n * @example\n * ```ts\n * // Basic animation\n * ticker.add((ticker) => {\n * // Rotate sprite by 0.1 radians per frame, scaled by deltaTime\n * sprite.rotation += 0.1 * ticker.deltaTime;\n * });\n * ```\n */\n public deltaTime = 1;\n /**\n * Scalar time elapsed in milliseconds from last frame to this frame.\n * Provides precise timing for animations and updates.\n *\n * This value is capped by setting {@link Ticker#minFPS|minFPS}\n * and is scaled with {@link Ticker#speed|speed}.\n *\n * If the platform supports DOMHighResTimeStamp,\n * this value will have a precision of 1 µs.\n *\n * Defaults to target frame time\n *\n * > [!NOTE] The cap may be exceeded by scaling.\n * @example\n * ```ts\n * // Animation timing\n * ticker.add((ticker) => {\n * // Use millisecond timing for precise animations\n * const progress = (ticker.deltaMS / animationDuration);\n * sprite.alpha = Math.min(1, progress);\n * });\n * ```\n * @default 16.66\n */\n public deltaMS: number;\n /**\n * Time elapsed in milliseconds from last frame to this frame.\n * Provides raw timing information without modifications.\n *\n * Opposed to what the scalar {@link Ticker#deltaTime|deltaTime}\n * is based, this value is neither capped nor scaled.\n *\n * If the platform supports DOMHighResTimeStamp,\n * this value will have a precision of 1 µs.\n *\n * Defaults to target frame time\n * @example\n * ```ts\n * // Basic timing information\n * ticker.add((ticker) => {\n * console.log(`Raw frame time: ${ticker.elapsedMS}ms`);\n * });\n * ```\n * @default 16.66\n */\n public elapsedMS: number;\n /**\n * The last time {@link Ticker#update|update} was invoked.\n * Used for calculating time deltas between frames.\n *\n * This value is also reset internally outside of invoking\n * update, but only when a new animation frame is requested.\n *\n * If the platform supports DOMHighResTimeStamp,\n * this value will have a precision of 1 µs.\n * @example\n * ```ts\n * // Basic timing check\n * ticker.add(() => {\n * const timeSinceStart = performance.now() - ticker.lastTime;\n * console.log(`Time running: ${timeSinceStart}ms`);\n * });\n * ```\n */\n public lastTime = -1;\n /**\n * Factor of current {@link Ticker#deltaTime|deltaTime}.\n * Used to scale time for slow motion or fast-forward effects.\n * @example\n * ```ts\n * // Basic speed adjustment\n * ticker.speed = 0.5; // Half speed (slow motion)\n * ticker.speed = 2.0; // Double speed (fast forward)\n *\n * // Temporary speed changes\n * function slowMotion() {\n * const normalSpeed = ticker.speed;\n * ticker.speed = 0.2;\n * setTimeout(() => {\n * ticker.speed = normalSpeed;\n * }, 1000);\n * }\n * ```\n */\n public speed = 1;\n /**\n * Whether or not this ticker has been started.\n *\n * `true` if {@link Ticker#start|start} has been called.\n * `false` if {@link Ticker#stop|Stop} has been called.\n *\n * While `false`, this value may change to `true` in the\n * event of {@link Ticker#autoStart|autoStart} being `true`\n * and a listener is added.\n * @example\n * ```ts\n * // Check ticker state\n * const ticker = new Ticker();\n * console.log(ticker.started); // false\n *\n * // Start and verify\n * ticker.start();\n * console.log(ticker.started); // true\n * ```\n */\n public started = false;\n\n /** The first listener. All new listeners added are chained on this. */\n private _head: TickerListener;\n /** Internal current frame request ID */\n private _requestId: number = null;\n /**\n * Internal value managed by minFPS property setter and getter.\n * This is the maximum allowed milliseconds between updates.\n */\n private _maxElapsedMS = 100;\n /**\n * Internal value managed by minFPS property setter and getter.\n * This is the minimum allowed milliseconds between updates.\n */\n private _minElapsedMS = 0;\n /** If enabled, deleting is disabled.*/\n private _protected = false;\n /** The last time keyframe was executed. Maintains a relatively fixed interval with the previous value. */\n private _lastFrame = -1;\n /**\n * Internal tick method bound to ticker instance.\n * This is because in early 2015, Function.bind\n * is still 60% slower in high performance scenarios.\n * Also separating frame requests from update method\n * so listeners may be called at any time and with\n * any animation API, just invoke ticker.update(time).\n * @param time - Time since last tick.\n */\n private readonly _tick: (time: number) => any;\n\n constructor()\n {\n this._head = new TickerListener(null, null, Infinity);\n this.deltaMS = 1 / Ticker.targetFPMS;\n this.elapsedMS = 1 / Ticker.targetFPMS;\n\n this._tick = (time: number): void =>\n {\n this._requestId = null;\n\n if (this.started)\n {\n // Invoke listeners now\n this.update(time);\n // Listener side effects may have modified ticker state.\n if (this.started && this._requestId === null && this._head.next)\n {\n this._requestId = requestAnimationFrame(this._tick);\n }\n }\n };\n }\n\n /**\n * Conditionally requests a new animation frame.\n * If a frame has not already been requested, and if the internal\n * emitter has listeners, a new frame is requested.\n */\n private _requestIfNeeded(): void\n {\n if (this._requestId === null && this._head.next)\n {\n // ensure callbacks get correct delta\n this.lastTime = performance.now();\n this._lastFrame = this.lastTime;\n this._requestId = requestAnimationFrame(this._tick);\n }\n }\n\n /** Conditionally cancels a pending animation frame. */\n private _cancelIfNeeded(): void\n {\n if (this._requestId !== null)\n {\n cancelAnimationFrame(this._requestId);\n this._requestId = null;\n }\n }\n\n /**\n * Conditionally requests a new animation frame.\n * If the ticker has been started it checks if a frame has not already\n * been requested, and if the internal emitter has listeners. If these\n * conditions are met, a new frame is requested. If the ticker has not\n * been started, but autoStart is `true`, then the ticker starts now,\n * and continues with the previous conditions to request a new frame.\n */\n private _startIfPossible(): void\n {\n if (this.started)\n {\n this._requestIfNeeded();\n }\n else if (this.autoStart)\n {\n this.start();\n }\n }\n\n /**\n * Register a handler for tick events. Calls continuously unless\n * it is removed or the ticker is stopped.\n * @example\n * ```ts\n * // Basic update handler\n * ticker.add((ticker) => {\n * // Update every frame\n * sprite.rotation += 0.1 * ticker.deltaTime;\n * });\n *\n * // With specific context\n * const game = {\n * update(ticker) {\n * this.physics.update(ticker.deltaTime);\n * }\n * };\n * ticker.add(game.update, game);\n *\n * // With priority\n * ticker.add(\n * (ticker) => {\n * // Runs before normal priority updates\n * physics.update(ticker.deltaTime);\n * },\n * undefined,\n * UPDATE_PRIORITY.HIGH\n * );\n * ```\n * @param fn - The listener function to be added for updates\n * @param context - The listener context\n * @param priority - The priority for emitting (default: UPDATE_PRIORITY.NORMAL)\n * @returns This instance of a ticker\n * @see {@link Ticker#addOnce} For one-time handlers\n * @see {@link Ticker#remove} For removing handlers\n */\n public add(fn: TickerCallback, context?: T, priority: number = UPDATE_PRIORITY.NORMAL): this\n {\n return this._addListener(new TickerListener(fn, context, priority));\n }\n\n /**\n * Add a handler for the tick event which is only executed once on the next frame.\n * @example\n * ```ts\n * // Basic one-time update\n * ticker.addOnce(() => {\n * console.log('Runs next frame only');\n * });\n *\n * // With specific context\n * const game = {\n * init(ticker) {\n * this.loadResources();\n * console.log('Game initialized');\n * }\n * };\n * ticker.addOnce(game.init, game);\n *\n * // With priority\n * ticker.addOnce(\n * () => {\n * // High priority one-time setup\n * physics.init();\n * },\n * undefined,\n * UPDATE_PRIORITY.HIGH\n * );\n * ```\n * @param fn - The listener function to be added for one update\n * @param context - The listener context\n * @param priority - The priority for emitting (default: UPDATE_PRIORITY.NORMAL)\n * @returns This instance of a ticker\n * @see {@link Ticker#add} For continuous updates\n * @see {@link Ticker#remove} For removing handlers\n */\n public addOnce(fn: TickerCallback, context?: T, priority: number = UPDATE_PRIORITY.NORMAL): this\n {\n return this._addListener(new TickerListener(fn, context, priority, true));\n }\n\n /**\n * Internally adds the event handler so that it can be sorted by priority.\n * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run\n * before the rendering.\n * @private\n * @param listener - Current listener being added.\n * @returns This instance of a ticker\n */\n private _addListener(listener: TickerListener): this\n {\n // For attaching to head\n let current = this._head.next;\n let previous = this._head;\n\n // Add the first item\n if (!current)\n {\n listener.connect(previous);\n }\n else\n {\n // Go from highest to lowest priority\n while (current)\n {\n if (listener.priority > current.priority)\n {\n listener.connect(previous);\n break;\n }\n previous = current;\n current = current.next;\n }\n\n // Not yet connected\n if (!listener.previous)\n {\n listener.connect(previous);\n }\n }\n\n this._startIfPossible();\n\n return this;\n }\n\n /**\n * Removes any handlers matching the function and context parameters.\n * If no handlers are left after removing, then it cancels the animation frame.\n * @example\n * ```ts\n * // Basic removal\n * const onTick = () => {\n * sprite.rotation += 0.1;\n * };\n * ticker.add(onTick);\n * ticker.remove(onTick);\n *\n * // Remove with context\n * const game = {\n * update(ticker) {\n * this.physics.update(ticker.deltaTime);\n * }\n * };\n * ticker.add(game.update, game);\n * ticker.remove(game.update, game);\n *\n * // Remove all matching handlers\n * // (if same function was added multiple times)\n * ticker.add(onTick);\n * ticker.add(onTick);\n * ticker.remove(onTick); // Removes all instances\n * ```\n * @param fn - The listener function to be removed\n * @param context - The listener context to be removed\n * @returns This instance of a ticker\n * @see {@link Ticker#add} For adding handlers\n * @see {@link Ticker#addOnce} For one-time handlers\n */\n public remove(fn: TickerCallback, context?: T): this\n {\n let listener = this._head.next;\n\n while (listener)\n {\n // We found a match, lets remove it\n // no break to delete all possible matches\n // incase a listener was added 2+ times\n if (listener.match(fn, context))\n {\n listener = listener.destroy();\n }\n else\n {\n listener = listener.next;\n }\n }\n\n if (!this._head.next)\n {\n this._cancelIfNeeded();\n }\n\n return this;\n }\n\n /**\n * The number of listeners on this ticker, calculated by walking through linked list.\n * @example\n * ```ts\n * // Check number of active listeners\n * const ticker = new Ticker();\n * console.log(ticker.count); // 0\n *\n * // Add some listeners\n * ticker.add(() => {});\n * ticker.add(() => {});\n * console.log(ticker.count); // 2\n *\n * // Check after cleanup\n * ticker.destroy();\n * console.log(ticker.count); // 0\n * ```\n * @readonly\n * @see {@link Ticker#add} For adding listeners\n * @see {@link Ticker#remove} For removing listeners\n */\n get count(): number\n {\n if (!this._head)\n {\n return 0;\n }\n\n let count = 0;\n let current = this._head;\n\n while ((current = current.next))\n {\n count++;\n }\n\n return count;\n }\n\n /**\n * Starts the ticker. If the ticker has listeners a new animation frame is requested at this point.\n * @example\n * ```ts\n * // Basic manual start\n * const ticker = new Ticker();\n * ticker.add(() => {\n * // Animation code here\n * });\n * ticker.start();\n * ```\n * @see {@link Ticker#stop} For stopping the ticker\n * @see {@link Ticker#autoStart} For automatic starting\n * @see {@link Ticker#started} For checking ticker state\n */\n public start(): void\n {\n if (!this.started)\n {\n this.started = true;\n this._requestIfNeeded();\n }\n }\n\n /**\n * Stops the ticker. If the ticker has requested an animation frame it is canceled at this point.\n * @example\n * ```ts\n * // Basic stop\n * const ticker = new Ticker();\n * ticker.stop();\n * ```\n * @see {@link Ticker#start} For starting the ticker\n * @see {@link Ticker#started} For checking ticker state\n * @see {@link Ticker#destroy} For cleaning up the ticker\n */\n public stop(): void\n {\n if (this.started)\n {\n this.started = false;\n this._cancelIfNeeded();\n }\n }\n\n /**\n * Destroy the ticker and don't use after this. Calling this method removes all references to internal events.\n * @example\n * ```ts\n * // Clean up with active listeners\n * const ticker = new Ticker();\n * ticker.add(() => {});\n * ticker.destroy(); // Removes all listeners\n * ```\n * @see {@link Ticker#stop} For stopping without destroying\n * @see {@link Ticker#remove} For removing specific listeners\n */\n public destroy(): void\n {\n if (!this._protected)\n {\n this.stop();\n\n let listener = this._head.next;\n\n while (listener)\n {\n listener = listener.destroy(true);\n }\n\n this._head.destroy();\n this._head = null;\n }\n }\n\n /**\n * Triggers an update.\n *\n * An update entails setting the\n * current {@link Ticker#elapsedMS|elapsedMS},\n * the current {@link Ticker#deltaTime|deltaTime},\n * invoking all listeners with current deltaTime,\n * and then finally setting {@link Ticker#lastTime|lastTime}\n * with the value of currentTime that was provided.\n *\n * This method will be called automatically by animation\n * frame callbacks if the ticker instance has been started\n * and listeners are added.\n * @example\n * ```ts\n * // Basic manual update\n * const ticker = new Ticker();\n * ticker.update(performance.now());\n * ```\n * @param currentTime - The current time of execution (defaults to performance.now())\n * @see {@link Ticker#deltaTime} For frame delta value\n * @see {@link Ticker#elapsedMS} For raw elapsed time\n */\n public update(currentTime: number = performance.now()): void\n {\n let elapsedMS;\n\n // If the difference in time is zero or negative, we ignore most of the work done here.\n // If there is no valid difference, then should be no reason to let anyone know about it.\n // A zero delta, is exactly that, nothing should update.\n //\n // The difference in time can be negative, and no this does not mean time traveling.\n // This can be the result of a race condition between when an animation frame is requested\n // on the current JavaScript engine event loop, and when the ticker's start method is invoked\n // (which invokes the internal _requestIfNeeded method). If a frame is requested before\n // _requestIfNeeded is invoked, then the callback for the animation frame the ticker requests,\n // can receive a time argument that can be less than the lastTime value that was set within\n // _requestIfNeeded. This difference is in microseconds, but this is enough to cause problems.\n //\n // This check covers this browser engine timing issue, as well as if consumers pass an invalid\n // currentTime value. This may happen if consumers opt-out of the autoStart, and update themselves.\n\n if (currentTime > this.lastTime)\n {\n // Save uncapped elapsedMS for measurement\n elapsedMS = this.elapsedMS = currentTime - this.lastTime;\n\n // cap the milliseconds elapsed used for deltaTime\n if (elapsedMS > this._maxElapsedMS)\n {\n elapsedMS = this._maxElapsedMS;\n }\n\n elapsedMS *= this.speed;\n\n // If not enough time has passed, exit the function.\n // Get ready for next frame by setting _lastFrame, but based on _minElapsedMS\n // adjustment to ensure a relatively stable interval.\n if (this._minElapsedMS)\n {\n const delta = currentTime - this._lastFrame | 0;\n\n if (delta < this._minElapsedMS)\n {\n return;\n }\n\n this._lastFrame = currentTime - (delta % this._minElapsedMS);\n }\n\n this.deltaMS = elapsedMS;\n this.deltaTime = this.deltaMS * Ticker.targetFPMS;\n\n // Cache a local reference, in-case ticker is destroyed\n // during the emit, we can still check for head.next\n const head = this._head;\n\n // Invoke listeners added to internal emitter\n let listener = head.next;\n\n while (listener)\n {\n listener = listener.emit(this);\n }\n\n if (!head.next)\n {\n this._cancelIfNeeded();\n }\n }\n else\n {\n this.deltaTime = this.deltaMS = this.elapsedMS = 0;\n }\n\n this.lastTime = currentTime;\n }\n\n /**\n * The frames per second at which this ticker is running.\n * The default is approximately 60 in most modern browsers.\n * > [!NOTE] This does not factor in the value of\n * > {@link Ticker#speed|speed}, which is specific\n * > to scaling {@link Ticker#deltaTime|deltaTime}.\n * @example\n * ```ts\n * // Basic FPS monitoring\n * ticker.add(() => {\n * console.log(`Current FPS: ${Math.round(ticker.FPS)}`);\n * });\n * ```\n * @readonly\n */\n get FPS(): number\n {\n return 1000 / this.elapsedMS;\n }\n\n /**\n * Manages the maximum amount of milliseconds allowed to\n * elapse between invoking {@link Ticker#update|update}.\n *\n * This value is used to cap {@link Ticker#deltaTime|deltaTime},\n * but does not effect the measured value of {@link Ticker#FPS|FPS}.\n *\n * When setting this property it is clamped to a value between\n * `0` and `Ticker.targetFPMS * 1000`.\n * @example\n * ```ts\n * // Set minimum acceptable frame rate\n * const ticker = new Ticker();\n * ticker.minFPS = 30; // Never go below 30 FPS\n *\n * // Use with maxFPS for frame rate clamping\n * ticker.minFPS = 30;\n * ticker.maxFPS = 60;\n *\n * // Monitor delta capping\n * ticker.add(() => {\n * // Delta time will be capped based on minFPS\n * console.log(`Delta time: ${ticker.deltaTime}`);\n * });\n * ```\n * @default 10\n */\n get minFPS(): number\n {\n return 1000 / this._maxElapsedMS;\n }\n\n set minFPS(fps: number)\n {\n // Minimum must be below the maxFPS\n const minFPS = Math.min(this.maxFPS, fps);\n\n // Must be at least 0, but below 1 / Ticker.targetFPMS\n const minFPMS = Math.min(Math.max(0, minFPS) / 1000, Ticker.targetFPMS);\n\n this._maxElapsedMS = 1 / minFPMS;\n }\n\n /**\n * Manages the minimum amount of milliseconds required to\n * elapse between invoking {@link Ticker#update|update}.\n *\n * This will effect the measured value of {@link Ticker#FPS|FPS}.\n *\n * If it is set to `0`, then there is no limit; PixiJS will render as many frames as it can.\n * Otherwise it will be at least `minFPS`\n * @example\n * ```ts\n * // Set minimum acceptable frame rate\n * const ticker = new Ticker();\n * ticker.maxFPS = 60; // Never go above 60 FPS\n *\n * // Use with maxFPS for frame rate clamping\n * ticker.minFPS = 30;\n * ticker.maxFPS = 60;\n *\n * // Monitor delta capping\n * ticker.add(() => {\n * // Delta time will be capped based on maxFPS\n * console.log(`Delta time: ${ticker.deltaTime}`);\n * });\n * ```\n * @default 0\n */\n get maxFPS(): number\n {\n if (this._minElapsedMS)\n {\n return Math.round(1000 / this._minElapsedMS);\n }\n\n return 0;\n }\n\n set maxFPS(fps: number)\n {\n if (fps === 0)\n {\n this._minElapsedMS = 0;\n }\n else\n {\n // Max must be at least the minFPS\n const maxFPS = Math.max(this.minFPS, fps);\n\n this._minElapsedMS = 1 / (maxFPS / 1000);\n }\n }\n\n /**\n * The shared ticker instance used by {@link AnimatedSprite} and by\n * {@link VideoSource} to update animation frames / video textures.\n *\n * It may also be used by {@link Application} if created with the `sharedTicker` option property set to true.\n *\n * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance.\n * Please follow the examples for usage, including how to opt-out of auto-starting the shared ticker.\n * @example\n * import { Ticker } from 'pixi.js';\n *\n * const ticker = Ticker.shared;\n * // Set this to prevent starting this ticker when listeners are added.\n * // By default this is true only for the Ticker.shared instance.\n * ticker.autoStart = false;\n *\n * // FYI, call this to ensure the ticker is stopped. It should be stopped\n * // if you have not attempted to render anything yet.\n * ticker.stop();\n *\n * // Call this when you are ready for a running shared ticker.\n * ticker.start();\n * @example\n * import { autoDetectRenderer, Container } from 'pixi.js';\n *\n * // You may use the shared ticker to render...\n * const renderer = autoDetectRenderer();\n * const stage = new Container();\n * document.body.appendChild(renderer.view);\n * ticker.add((time) => renderer.render(stage));\n *\n * // Or you can just update it manually.\n * ticker.autoStart = false;\n * ticker.stop();\n * const animate = (time) => {\n * ticker.update(time);\n * renderer.render(stage);\n * requestAnimationFrame(animate);\n * };\n * animate(performance.now());\n * @type {Ticker}\n * @readonly\n */\n static get shared(): Ticker\n {\n if (!Ticker._shared)\n {\n const shared = Ticker._shared = new Ticker();\n\n shared.autoStart = true;\n shared._protected = true;\n }\n\n return Ticker._shared;\n }\n\n /**\n * The system ticker instance used by {@link PrepareBase} for core timing\n * functionality that shouldn't usually need to be paused, unlike the `shared`\n * ticker which drives visual animations and rendering which may want to be paused.\n *\n * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance.\n * @type {Ticker}\n * @readonly\n * @advanced\n */\n static get system(): Ticker\n {\n if (!Ticker._system)\n {\n const system = Ticker._system = new Ticker();\n\n system.autoStart = true;\n system._protected = true;\n }\n\n return Ticker._system;\n }\n}\n","import { type Renderer } from '../rendering/renderers/types';\nimport { UPDATE_PRIORITY } from '../ticker/const';\nimport { Ticker } from '../ticker/Ticker';\n\n/**\n * CanvasObserver class synchronizes the DOM element's transform with the canvas size and position.\n * It uses ResizeObserver for efficient updates and requestAnimationFrame for fallback.\n * This ensures that the DOM element is always correctly positioned and scaled relative to the canvas.\n * @internal\n */\nexport class CanvasObserver\n{\n /** A cached value of the last transform applied to the DOM element. */\n private _lastTransform = '';\n /** A ResizeObserver instance to observe changes in the canvas size. */\n private _observer: ResizeObserver | null = null;\n /** The canvas element that this observer is associated with. */\n private _canvas: HTMLCanvasElement;\n /** The DOM element that will be transformed based on the canvas size and position. */\n private readonly _domElement: HTMLElement;\n /** The renderer instance that this observer is associated with. */\n private readonly _renderer: Renderer;\n /** The last scale values applied to the DOM element, used to avoid unnecessary updates. */\n private _lastScaleX: number;\n /** The last scale values applied to the DOM element, used to avoid unnecessary updates. */\n private _lastScaleY: number;\n /** A flag to indicate whether the observer is attached to the Ticker for continuous updates. */\n private _tickerAttached = false;\n\n constructor(options: { domElement: HTMLElement; renderer: Renderer })\n {\n this._domElement = options.domElement;\n this._renderer = options.renderer;\n\n // We need to ensure that the canvas is not an OffscreenCanvas\n if (globalThis.OffscreenCanvas && this._renderer.canvas instanceof OffscreenCanvas) return;\n this._canvas = this._renderer.canvas;\n this._attachObserver();\n }\n\n /** The canvas element that this CanvasObserver is associated with. */\n public get canvas(): HTMLCanvasElement\n {\n return this._canvas;\n }\n\n /** Attaches the DOM element to the canvas parent if it is not already attached. */\n public ensureAttached()\n {\n if (!this._domElement.parentNode && this._canvas.parentNode)\n {\n this._canvas.parentNode.appendChild(this._domElement);\n this.updateTranslation();\n }\n }\n\n /**\n * Updates the transform of the DOM element based on the canvas size and position.\n * This method calculates the scale and translation needed to keep the DOM element in sync with the canvas.\n */\n public readonly updateTranslation = () =>\n {\n if (!this._canvas) return;\n\n const rect = this._canvas.getBoundingClientRect(); // still needed for left/top\n const contentWidth = this._canvas.width;\n const contentHeight = this._canvas.height;\n\n const sx = (rect.width / contentWidth) * this._renderer.resolution;\n const sy = (rect.height / contentHeight) * this._renderer.resolution;\n const tx = rect.left;\n const ty = rect.top;\n\n const newTransform = `translate(${tx}px, ${ty}px) scale(${sx}, ${sy})`;\n\n if (newTransform !== this._lastTransform)\n {\n this._domElement.style.transform = newTransform;\n this._lastTransform = newTransform;\n }\n };\n\n /** Sets up a ResizeObserver if available. This ensures that the DOM element is kept in sync with the canvas size . */\n private _attachObserver()\n {\n if ('ResizeObserver' in globalThis)\n {\n if (this._observer)\n {\n this._observer.disconnect();\n this._observer = null;\n }\n\n this._observer = new ResizeObserver((entries) =>\n {\n for (const entry of entries)\n {\n if (entry.target !== this._canvas)\n {\n continue;\n }\n\n const contentWidth = this.canvas.width;\n const contentHeight = this.canvas.height;\n const sx = (entry.contentRect.width / contentWidth) * this._renderer.resolution;\n const sy = (entry.contentRect.height / contentHeight) * this._renderer.resolution;\n\n // Only refetch position if scale actually changed\n const needsUpdate = this._lastScaleX !== sx || this._lastScaleY !== sy;\n\n if (needsUpdate)\n {\n this.updateTranslation(); // safely fetch `left` and `top` only when needed\n this._lastScaleX = sx;\n this._lastScaleY = sy;\n }\n }\n });\n this._observer.observe(this._canvas);\n }\n else if (!this._tickerAttached)\n {\n Ticker.shared.add(this.updateTranslation, this, UPDATE_PRIORITY.HIGH);\n }\n }\n\n /** Destroys the CanvasObserver instance, cleaning up observers and Ticker. */\n public destroy()\n {\n if (this._observer)\n {\n this._observer.disconnect();\n this._observer = null;\n }\n else if (this._tickerAttached)\n {\n Ticker.shared.remove(this.updateTranslation);\n }\n\n (this._domElement as null) = null;\n (this._renderer as null) = null;\n this._canvas = null;\n this._tickerAttached = false;\n this._lastTransform = '';\n this._lastScaleX = null;\n this._lastScaleY = null;\n }\n}\n","import { Point } from '../maths/point/Point';\n\nimport type { Container } from '../scene/container/Container';\nimport type { EventBoundary } from './EventBoundary';\n\n/**\n * A PixiJS compatible touch event interface that extends the standard DOM Touch interface.\n * Provides additional properties to normalize touch input with mouse/pointer events.\n * @example\n * ```ts\n * // Access touch information\n * sprite.on('touchstart', (event) => {\n * // Standard touch properties\n * console.log('Touch position:', event.clientX, event.clientY);\n * console.log('Touch ID:', event.pointerId);\n *\n * // Additional PixiJS properties\n * console.log('Pressure:', event.pressure);\n * console.log('Size:', event.width, event.height);\n * console.log('Tilt:', event.tiltX, event.tiltY);\n * });\n * ```\n * @category events\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Touch} DOM Touch Interface\n * @standard\n */\nexport interface PixiTouch extends Touch\n{\n /** The button being pressed (0: left, 1: middle, 2: right) */\n button: number;\n\n /** Bitmap of currently pressed buttons */\n buttons: number;\n\n /** Whether this is the primary touch point */\n isPrimary: boolean;\n\n /** The width of the touch contact area */\n width: number;\n\n /** The height of the touch contact area */\n height: number;\n\n /** The angle of tilt along the x-axis (in degrees) */\n tiltX: number;\n\n /** The angle of tilt along the y-axis (in degrees) */\n tiltY: number;\n\n /** The type of pointer that triggered this event */\n pointerType: string;\n\n /** Unique identifier for this touch point */\n pointerId: number;\n\n /** The normalized pressure of the pointer (0 to 1) */\n pressure: number;\n\n /** The rotation angle of the pointer (e.g., pen) */\n twist: number;\n\n /** The normalized tangential pressure of the pointer */\n tangentialPressure: number;\n\n /** The x coordinate relative to the current layer */\n layerX: number;\n\n /** The y coordinate relative to the current layer */\n layerY: number;\n\n /** The x coordinate relative to the target's offset parent */\n offsetX: number;\n\n /** The y coordinate relative to the target's offset parent */\n offsetY: number;\n\n /** Whether the event was normalized by PixiJS */\n isNormalized: boolean;\n\n /** The type of touch event */\n type: string;\n}\n\n/**\n * A DOM-compatible synthetic event implementation for PixiJS's event system.\n * This class implements the standard DOM Event interface while providing additional\n * functionality specific to PixiJS events.\n * > [!NOTE] You wont receive an instance of this class directly, but rather a subclass\n * > of this class, such as {@link FederatedPointerEvent}, {@link FederatedMouseEvent}, or\n * > {@link FederatedWheelEvent}. This class is the base for all federated events.\n * @example\n * ```ts\n * // Basic event handling\n * sprite.on('pointerdown', (event: FederatedEvent) => {\n * // Access standard DOM event properties\n * console.log('Target:', event.target);\n * console.log('Phase:', event.eventPhase);\n * console.log('Type:', event.type);\n *\n * // Control propagation\n * event.stopPropagation();\n * });\n * ```\n * @typeParam N - The type of native event held. Can be either a UIEvent or PixiTouch.\n * @remarks\n * - Implements the standard DOM UIEvent interface\n * - Provides event bubbling and capturing phases\n * - Supports propagation control\n * - Manages event paths through display tree\n * - Normalizes native browser events\n * @see {@link https://dom.spec.whatwg.org/#event} DOM Event Specification\n * @see {@link FederatedPointerEvent} For pointer-specific events\n * @see {@link FederatedMouseEvent} For mouse-specific events\n * @see {@link FederatedWheelEvent} For wheel-specific events\n * @category events\n * @standard\n */\nexport class FederatedEvent implements UIEvent\n{\n /** Flags whether this event bubbles. This will take effect only if it is set before propagation. */\n public bubbles = true;\n\n /** @deprecated since 7.0.0 */\n public cancelBubble = true;\n\n /**\n * Flags whether this event can be canceled using {@link FederatedEvent.preventDefault}. This is always\n * false (for now).\n */\n public readonly cancelable = false;\n\n /**\n * Flag added for compatibility with DOM `Event`. It is not used in the Federated Events\n * API.\n * @see https://dom.spec.whatwg.org/#dom-event-composed\n * @ignore\n */\n public readonly composed = false;\n\n /** The listeners of the event target that are being notified. */\n public currentTarget: Container;\n\n /** Flags whether the default response of the user agent was prevent through this event. */\n public defaultPrevented = false;\n\n /**\n * The propagation phase.\n * @default {@link FederatedEvent.NONE}\n */\n public eventPhase = FederatedEvent.prototype.NONE;\n\n /** Flags whether this is a user-trusted event */\n public isTrusted: boolean;\n\n /** @deprecated since 7.0.0 */\n public returnValue: boolean;\n\n /** @deprecated since 7.0.0 */\n public srcElement: EventTarget;\n\n /** The event target that this will be dispatched to. */\n public target: Container;\n\n /** The timestamp of when the event was created. */\n public timeStamp: number;\n\n /** The type of event, e.g. `\"mouseup\"`. */\n public type: string;\n\n /** The native event that caused the foremost original event. */\n public nativeEvent: N;\n\n /** The original event that caused this event, if any. */\n public originalEvent: FederatedEvent;\n\n /** Flags whether propagation was stopped. */\n public propagationStopped = false;\n\n /** Flags whether propagation was immediately stopped. */\n public propagationImmediatelyStopped = false;\n\n /** The composed path of the event's propagation. The `target` is at the end. */\n public path: Container[];\n\n /** The {@link EventBoundary} that manages this event. Null for root events. */\n public readonly manager: EventBoundary;\n\n /** Event-specific detail */\n public detail: number;\n\n /** The global Window object. */\n public view: WindowProxy;\n\n /**\n * Not supported.\n * @deprecated since 7.0.0\n * @ignore\n */\n public which: number;\n\n /** The coordinates of the event relative to the nearest DOM layer. This is a non-standard property. */\n public layer: Point = new Point();\n\n /** @readonly */\n get layerX(): number { return this.layer.x; }\n\n /** @readonly */\n get layerY(): number { return this.layer.y; }\n\n /** The coordinates of the event relative to the DOM document. This is a non-standard property. */\n public page: Point = new Point();\n\n /** @readonly */\n get pageX(): number { return this.page.x; }\n\n /** @readonly */\n get pageY(): number { return this.page.y; }\n\n /**\n * @param manager - The event boundary which manages this event. Propagation can only occur\n * within the boundary's jurisdiction.\n */\n constructor(manager: EventBoundary)\n {\n this.manager = manager;\n }\n\n /**\n * Fallback for the deprecated `InteractionEvent.data`.\n * @deprecated since 7.0.0\n */\n get data(): this\n {\n return this;\n }\n\n /**\n * The propagation path for this event. Alias for {@link EventBoundary.propagationPath}.\n * @advanced\n */\n public composedPath(): Container[]\n {\n // Find the propagation path if it isn't cached or if the target has changed since since\n // the last evaluation.\n if (this.manager && (!this.path || this.path[this.path.length - 1] !== this.target))\n {\n this.path = this.target ? this.manager.propagationPath(this.target) : [];\n }\n\n return this.path;\n }\n\n /**\n * Unimplemented method included for implementing the DOM interface `Event`. It will throw an `Error`.\n * @deprecated\n * @ignore\n * @param _type\n * @param _bubbles\n * @param _cancelable\n */\n public initEvent(_type: string, _bubbles?: boolean, _cancelable?: boolean): void\n {\n throw new Error('initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.');\n }\n\n /**\n * Unimplemented method included for implementing the DOM interface `UIEvent`. It will throw an `Error`.\n * @ignore\n * @deprecated\n * @param _typeArg\n * @param _bubblesArg\n * @param _cancelableArg\n * @param _viewArg\n * @param _detailArg\n */\n public initUIEvent(_typeArg: string, _bubblesArg?: boolean, _cancelableArg?: boolean, _viewArg?: Window | null,\n _detailArg?: number): void\n {\n throw new Error('initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.');\n }\n\n /**\n * Prevent default behavior of both PixiJS and the user agent.\n * @example\n * ```ts\n * sprite.on('click', (event) => {\n * // Prevent both browser's default click behavior\n * // and PixiJS's default handling\n * event.preventDefault();\n *\n * // Custom handling\n * customClickHandler();\n * });\n * ```\n * @remarks\n * - Only works if the native event is cancelable\n * - Does not stop event propagation\n */\n public preventDefault(): void\n {\n if (this.nativeEvent instanceof Event && this.nativeEvent.cancelable)\n {\n this.nativeEvent.preventDefault();\n }\n\n this.defaultPrevented = true;\n }\n\n /**\n * Stop this event from propagating to any additional listeners, including those\n * on the current target and any following targets in the propagation path.\n * @example\n * ```ts\n * container.on('pointerdown', (event) => {\n * // Stop all further event handling\n * event.stopImmediatePropagation();\n *\n * // These handlers won't be called:\n * // - Other pointerdown listeners on this container\n * // - Any pointerdown listeners on parent containers\n * });\n * ```\n * @remarks\n * - Immediately stops all event propagation\n * - Prevents other listeners on same target from being called\n * - More aggressive than stopPropagation()\n */\n public stopImmediatePropagation(): void\n {\n this.propagationImmediatelyStopped = true;\n }\n\n /**\n * Stop this event from propagating to the next target in the propagation path.\n * The rest of the listeners on the current target will still be notified.\n * @example\n * ```ts\n * child.on('pointermove', (event) => {\n * // Handle event on child\n * updateChild();\n *\n * // Prevent parent handlers from being called\n * event.stopPropagation();\n * });\n *\n * // This won't be called if child handles the event\n * parent.on('pointermove', (event) => {\n * updateParent();\n * });\n * ```\n * @remarks\n * - Stops event bubbling to parent containers\n * - Does not prevent other listeners on same target\n * - Less aggressive than stopImmediatePropagation()\n */\n public stopPropagation(): void\n {\n this.propagationStopped = true;\n }\n\n /**\n * The event propagation phase NONE that indicates that the event is not in any phase.\n * @default 0\n * @advanced\n */\n public readonly NONE = 0;\n /**\n * The event propagation phase CAPTURING_PHASE that indicates that the event is in the capturing phase.\n * @default 1\n * @advanced\n */\n public readonly CAPTURING_PHASE = 1;\n /**\n * The event propagation phase AT_TARGET that indicates that the event is at the target.\n * @default 2\n * @advanced\n */\n public readonly AT_TARGET = 2;\n /**\n * The event propagation phase BUBBLING_PHASE that indicates that the event is in the bubbling phase.\n * @default 3\n * @advanced\n */\n public readonly BUBBLING_PHASE = 3;\n}\n","var appleIphone = /iPhone/i;\nvar appleIpod = /iPod/i;\nvar appleTablet = /iPad/i;\nvar appleUniversal = /\\biOS-universal(?:.+)Mac\\b/i;\nvar androidPhone = /\\bAndroid(?:.+)Mobile\\b/i;\nvar androidTablet = /Android/i;\nvar amazonPhone = /(?:SD4930UR|\\bSilk(?:.+)Mobile\\b)/i;\nvar amazonTablet = /Silk/i;\nvar windowsPhone = /Windows Phone/i;\nvar windowsTablet = /\\bWindows(?:.+)ARM\\b/i;\nvar otherBlackBerry = /BlackBerry/i;\nvar otherBlackBerry10 = /BB10/i;\nvar otherOpera = /Opera Mini/i;\nvar otherChrome = /\\b(CriOS|Chrome)(?:.+)Mobile/i;\nvar otherFirefox = /Mobile(?:.+)Firefox\\b/i;\nvar isAppleTabletOnIos13 = function (navigator) {\n return (typeof navigator !== 'undefined' &&\n navigator.platform === 'MacIntel' &&\n typeof navigator.maxTouchPoints === 'number' &&\n navigator.maxTouchPoints > 1 &&\n typeof MSStream === 'undefined');\n};\nfunction createMatch(userAgent) {\n return function (regex) { return regex.test(userAgent); };\n}\nexport default function isMobile(param) {\n var nav = {\n userAgent: '',\n platform: '',\n maxTouchPoints: 0\n };\n if (!param && typeof navigator !== 'undefined') {\n nav = {\n userAgent: navigator.userAgent,\n platform: navigator.platform,\n maxTouchPoints: navigator.maxTouchPoints || 0\n };\n }\n else if (typeof param === 'string') {\n nav.userAgent = param;\n }\n else if (param && param.userAgent) {\n nav = {\n userAgent: param.userAgent,\n platform: param.platform,\n maxTouchPoints: param.maxTouchPoints || 0\n };\n }\n var userAgent = nav.userAgent;\n var tmp = userAgent.split('[FBAN');\n if (typeof tmp[1] !== 'undefined') {\n userAgent = tmp[0];\n }\n tmp = userAgent.split('Twitter');\n if (typeof tmp[1] !== 'undefined') {\n userAgent = tmp[0];\n }\n var match = createMatch(userAgent);\n var result = {\n apple: {\n phone: match(appleIphone) && !match(windowsPhone),\n ipod: match(appleIpod),\n tablet: !match(appleIphone) &&\n (match(appleTablet) || isAppleTabletOnIos13(nav)) &&\n !match(windowsPhone),\n universal: match(appleUniversal),\n device: (match(appleIphone) ||\n match(appleIpod) ||\n match(appleTablet) ||\n match(appleUniversal) ||\n isAppleTabletOnIos13(nav)) &&\n !match(windowsPhone)\n },\n amazon: {\n phone: match(amazonPhone),\n tablet: !match(amazonPhone) && match(amazonTablet),\n device: match(amazonPhone) || match(amazonTablet)\n },\n android: {\n phone: (!match(windowsPhone) && match(amazonPhone)) ||\n (!match(windowsPhone) && match(androidPhone)),\n tablet: !match(windowsPhone) &&\n !match(amazonPhone) &&\n !match(androidPhone) &&\n (match(amazonTablet) || match(androidTablet)),\n device: (!match(windowsPhone) &&\n (match(amazonPhone) ||\n match(amazonTablet) ||\n match(androidPhone) ||\n match(androidTablet))) ||\n match(/\\bokhttp\\b/i)\n },\n windows: {\n phone: match(windowsPhone),\n tablet: match(windowsTablet),\n device: match(windowsPhone) || match(windowsTablet)\n },\n other: {\n blackberry: match(otherBlackBerry),\n blackberry10: match(otherBlackBerry10),\n opera: match(otherOpera),\n firefox: match(otherFirefox),\n chrome: match(otherChrome),\n device: match(otherBlackBerry) ||\n match(otherBlackBerry10) ||\n match(otherOpera) ||\n match(otherFirefox) ||\n match(otherChrome)\n },\n any: false,\n phone: false,\n tablet: false\n };\n result.any =\n result.apple.device ||\n result.android.device ||\n result.windows.device ||\n result.other.device;\n result.phone =\n result.apple.phone || result.android.phone || result.windows.phone;\n result.tablet =\n result.apple.tablet || result.android.tablet || result.windows.tablet;\n return result;\n}\n//# sourceMappingURL=isMobile.js.map","import isMobileJs from 'ismobilejs';\n\n// ismobilejs have different import behavior for CJS and ESM, so here is the hack\ntype isMobileJsType = typeof isMobileJs & { default?: typeof isMobileJs };\nconst isMobileCall = (isMobileJs as isMobileJsType).default ?? isMobileJs;\n\n/**\n * The result of the mobile device detection system.\n * Provides detailed information about device type and platform.\n * @example\n * ```ts\n * // Type usage with isMobile\n * const deviceInfo: isMobileResult = isMobile;\n *\n * // Check device categories\n * if (deviceInfo.apple.device) {\n * console.log('iOS Device Details:', {\n * isPhone: deviceInfo.apple.phone,\n * isTablet: deviceInfo.apple.tablet,\n * isUniversal: deviceInfo.apple.universal\n * });\n * }\n *\n * // Platform-specific checks\n * const platformInfo = {\n * isApple: deviceInfo.apple.device,\n * isAndroid: deviceInfo.android.device,\n * isAmazon: deviceInfo.amazon.device,\n * isWindows: deviceInfo.windows.device\n * };\n * ```\n * @category utils\n * @standard\n */\nexport type isMobileResult = {\n /**\n * Apple device detection information.\n * Provides detailed iOS device categorization.\n * @example\n * ```ts\n * // iOS device checks\n * if (isMobile.apple.device) {\n * if (isMobile.apple.tablet) {\n * // iPad-specific code\n * useTabletLayout();\n * } else if (isMobile.apple.phone) {\n * // iPhone-specific code\n * usePhoneLayout();\n * }\n * }\n * ```\n */\n apple: {\n /** Whether the device is an iPhone */\n phone: boolean;\n /** Whether the device is an iPod Touch */\n ipod: boolean;\n /** Whether the device is an iPad */\n tablet: boolean;\n /** Whether app is running in iOS universal mode */\n universal: boolean;\n /** Whether device is any Apple mobile device */\n device: boolean;\n };\n\n /**\n * Amazon device detection information.\n * Identifies Amazon Fire tablets and phones.\n * @example\n * ```ts\n * // Amazon Fire tablet detection\n * if (isMobile.amazon.tablet) {\n * // Fire tablet optimizations\n * optimizeForFireTablet();\n * }\n * ```\n */\n amazon: {\n /** Whether device is a Fire Phone */\n phone: boolean;\n /** Whether device is a Fire Tablet */\n tablet: boolean;\n /** Whether device is any Amazon mobile device */\n device: boolean;\n };\n\n /**\n * Android device detection information.\n * Categorizes Android phones and tablets.\n * @example\n * ```ts\n * // Android device handling\n * if (isMobile.android.device) {\n * // Check specific type\n * const deviceType = isMobile.android.tablet ?\n * 'tablet' : 'phone';\n * console.log(`Android ${deviceType} detected`);\n * }\n * ```\n */\n android: {\n /** Whether device is an Android phone */\n phone: boolean;\n /** Whether device is an Android tablet */\n tablet: boolean;\n /** Whether device is any Android device */\n device: boolean;\n };\n\n /**\n * Windows device detection information.\n * Identifies Windows phones and tablets.\n * @example\n * ```ts\n * // Windows device checks\n * if (isMobile.windows.tablet) {\n * // Surface tablet optimizations\n * enableTouchFeatures();\n * }\n * ```\n */\n windows: {\n /** Whether device is a Windows Phone */\n phone: boolean;\n /** Whether device is a Windows tablet */\n tablet: boolean;\n /** Whether device is any Windows mobile device */\n device: boolean;\n };\n\n /**\n * Other device detection information.\n * Covers additional platforms and browsers.\n * @example\n * ```ts\n * // Check other platforms\n * if (isMobile.other.blackberry10) {\n * // BlackBerry 10 specific code\n * } else if (isMobile.other.chrome) {\n * // Chrome mobile specific code\n * }\n * ```\n */\n other: {\n /** Whether device is a BlackBerry */\n blackberry: boolean;\n /** Whether device is a BlackBerry 10 */\n blackberry10: boolean;\n /** Whether browser is Opera Mobile */\n opera: boolean;\n /** Whether browser is Firefox Mobile */\n firefox: boolean;\n /** Whether browser is Chrome Mobile */\n chrome: boolean;\n /** Whether device is any other mobile device */\n device: boolean;\n };\n\n /**\n * Whether the device is any type of phone.\n * Combines detection across all platforms.\n * @example\n * ```ts\n * // Check if device is a phone\n * if (isMobile.phone) {\n * console.log('Running on a mobile phone');\n * }\n * ```\n */\n phone: boolean;\n\n /**\n * Whether the device is any type of tablet.\n * Combines detection across all platforms.\n * @example\n * ```ts\n * // Check if device is a tablet\n * if (isMobile.tablet) {\n * console.log('Running on a mobile tablet');\n * }\n * ```\n */\n tablet: boolean;\n\n /**\n * Whether the device is any type of mobile device.\n * True if any mobile platform is detected.\n * @example\n * ```ts\n * // Check if device is mobile\n * if (isMobile.any) {\n * console.log('Running on a mobile device');\n * }\n * ```\n */\n any: boolean;\n};\n\n/**\n * Detects whether the device is mobile and what type of mobile device it is.\n * Provides a comprehensive detection system for mobile platforms and devices.\n * @example\n * ```ts\n * import { isMobile } from 'pixi.js';\n *\n * // Check specific device types\n * if (isMobile.apple.tablet) {\n * console.log('Running on iPad');\n * }\n *\n * // Check platform categories\n * if (isMobile.android.any) {\n * console.log('Running on Android');\n * }\n *\n * // Conditional rendering\n * if (isMobile.phone) {\n * renderer.resolution = 2;\n * view.style.width = '100vw';\n * }\n * ```\n * @remarks\n * - Detects all major mobile platforms\n * - Distinguishes between phones and tablets\n * - Updates when navigator changes\n * - Common in responsive design\n * @category utils\n * @standard\n * @see {@link isMobileResult} For full type definition\n */\nexport const isMobile: isMobileResult = isMobileCall(globalThis.navigator);\n","import { CanvasObserver } from '../dom/CanvasObserver';\nimport { FederatedEvent } from '../events/FederatedEvent';\nimport { ExtensionType } from '../extensions/Extensions';\nimport { isMobile } from '../utils/browser/isMobile';\nimport { removeItems } from '../utils/data/removeItems';\nimport { type AccessibleHTMLElement } from './accessibilityTarget';\n\nimport type { Rectangle } from '../maths/shapes/Rectangle';\nimport type { System } from '../rendering/renderers/shared/system/System';\nimport type { Renderer } from '../rendering/renderers/types';\nimport type { Container } from '../scene/container/Container';\nimport type { isMobileResult } from '../utils/browser/isMobile';\n\n/** @ignore */\nconst KEY_CODE_TAB = 9;\n\nconst DIV_TOUCH_SIZE = 100;\nconst DIV_TOUCH_POS_X = 0;\nconst DIV_TOUCH_POS_Y = 0;\nconst DIV_TOUCH_ZINDEX = 2;\n\nconst DIV_HOOK_SIZE = 1;\nconst DIV_HOOK_POS_X = -1000;\nconst DIV_HOOK_POS_Y = -1000;\nconst DIV_HOOK_ZINDEX = 2;\n\n/**\n * Initialisation options for the accessibility system when used with an Application.\n * @category accessibility\n * @advanced\n */\nexport interface AccessibilitySystemOptions\n{\n /** Options for the accessibility system */\n accessibilityOptions?: AccessibilityOptions;\n}\n\n/**\n * The options for the accessibility system.\n * @category accessibility\n * @advanced\n */\nexport interface AccessibilityOptions\n{\n /** Whether to enable accessibility features on initialization instead of waiting for tab key */\n enabledByDefault?: boolean;\n /** Whether to visually show the accessibility divs for debugging */\n debug?: boolean;\n /** Whether to allow tab key press to activate accessibility features */\n activateOnTab?: boolean;\n /** Whether to deactivate accessibility when mouse moves */\n deactivateOnMouseMove?: boolean;\n}\n\n/**\n * The Accessibility system provides screen reader and keyboard navigation support for PixiJS content.\n * It creates an accessible DOM layer over the canvas that can be controlled programmatically or through user interaction.\n *\n * By default, the system activates when users press the tab key. This behavior can be customized through options:\n * ```js\n * const app = new Application({\n * accessibilityOptions: {\n * // Enable immediately instead of waiting for tab\n * enabledByDefault: true,\n * // Disable tab key activation\n * activateOnTab: false,\n * // Show/hide accessibility divs\n * debug: false,\n * // Prevent accessibility from being deactivated when mouse moves\n * deactivateOnMouseMove: false,\n * }\n * });\n * ```\n *\n * The system can also be controlled programmatically by accessing the `renderer.accessibility` property:\n * ```js\n * app.renderer.accessibility.setAccessibilityEnabled(true);\n * ```\n *\n * To make individual containers accessible:\n * ```js\n * container.accessible = true;\n * ```\n * There are several properties that can be set on a Container to control its accessibility which can\n * be found here: {@link AccessibleOptions}.\n * @category accessibility\n * @standard\n */\nexport class AccessibilitySystem implements System\n{\n /** @ignore */\n public static extension = {\n type: [\n ExtensionType.WebGLSystem,\n ExtensionType.WebGPUSystem,\n ],\n name: 'accessibility',\n } as const;\n\n /**\n * The default options used by the system.\n * You can set these before initializing the {@link Application} to change the default behavior.\n * @example\n * ```js\n * import { AccessibilitySystem } from 'pixi.js';\n *\n * AccessibilitySystem.defaultOptions.enabledByDefault = true;\n *\n * const app = new Application()\n * app.init()\n * ```\n */\n public static defaultOptions: AccessibilityOptions = {\n /**\n * Whether to enable accessibility features on initialization\n * @default false\n */\n enabledByDefault: false,\n /**\n * Whether to visually show the accessibility divs for debugging\n * @default false\n */\n debug: false,\n /**\n * Whether to activate accessibility when tab key is pressed\n * @default true\n */\n activateOnTab: true,\n /**\n * Whether to deactivate accessibility when mouse moves\n * @default true\n */\n deactivateOnMouseMove: true,\n };\n\n /** Whether accessibility divs are visible for debugging */\n public debug = false;\n\n /** Whether to activate on tab key press */\n private _activateOnTab = true;\n\n /** Whether to deactivate accessibility when mouse moves */\n private _deactivateOnMouseMove = true;\n\n /**\n * The renderer this accessibility manager works for.\n * @type {WebGLRenderer|WebGPURenderer}\n */\n private _renderer: Renderer;\n\n /** Internal variable, see isActive getter. */\n private _isActive = false;\n\n /** Internal variable, see isMobileAccessibility getter. */\n private _isMobileAccessibility = false;\n\n /** Button element for handling touch hooks. */\n private _hookDiv: HTMLElement | null;\n\n /** This is the dom element that will sit over the PixiJS element. This is where the div overlays will go. */\n private _div: HTMLElement | null = null;\n\n /** A simple pool for storing divs. */\n private _pool: AccessibleHTMLElement[] = [];\n\n /** This is a tick used to check if an object is no longer being rendered. */\n private _renderId = 0;\n\n /** The array of currently active accessible items. */\n private _children: Container[] = [];\n\n /** Count to throttle div updates on android devices. */\n private _androidUpdateCount = 0;\n\n /** The frequency to update the div elements. */\n private readonly _androidUpdateFrequency = 500; // 2fps\n private _canvasObserver: CanvasObserver;\n\n // eslint-disable-next-line jsdoc/require-param\n /**\n * @param {WebGLRenderer|WebGPURenderer} renderer - A reference to the current renderer\n */\n constructor(renderer: Renderer, private readonly _mobileInfo: isMobileResult = isMobile)\n {\n this._hookDiv = null;\n\n if (_mobileInfo.tablet || _mobileInfo.phone)\n {\n this._createTouchHook();\n }\n\n this._renderer = renderer;\n }\n\n /**\n * Value of `true` if accessibility is currently active and accessibility layers are showing.\n * @type {boolean}\n * @readonly\n */\n get isActive(): boolean\n {\n return this._isActive;\n }\n\n /**\n * Value of `true` if accessibility is enabled for touch devices.\n * @type {boolean}\n * @readonly\n */\n get isMobileAccessibility(): boolean\n {\n return this._isMobileAccessibility;\n }\n\n /**\n * The DOM element that will sit over the PixiJS element. This is where the div overlays will go.\n * @readonly\n */\n get hookDiv()\n {\n return this._hookDiv;\n }\n\n /**\n * Creates the touch hooks.\n * @private\n */\n private _createTouchHook(): void\n {\n const hookDiv = document.createElement('button');\n\n hookDiv.style.width = `${DIV_HOOK_SIZE}px`;\n hookDiv.style.height = `${DIV_HOOK_SIZE}px`;\n hookDiv.style.position = 'absolute';\n hookDiv.style.top = `${DIV_HOOK_POS_X}px`;\n hookDiv.style.left = `${DIV_HOOK_POS_Y}px`;\n hookDiv.style.zIndex = DIV_HOOK_ZINDEX.toString();\n hookDiv.style.backgroundColor = '#FF0000';\n hookDiv.title = 'select to enable accessibility for this content';\n\n hookDiv.addEventListener('focus', () =>\n {\n this._isMobileAccessibility = true;\n this._activate();\n this._destroyTouchHook();\n });\n\n document.body.appendChild(hookDiv);\n this._hookDiv = hookDiv;\n }\n\n /**\n * Destroys the touch hooks.\n * @private\n */\n private _destroyTouchHook(): void\n {\n if (!this._hookDiv)\n {\n return;\n }\n document.body.removeChild(this._hookDiv);\n this._hookDiv = null;\n }\n\n /**\n * Activating will cause the Accessibility layer to be shown.\n * This is called when a user presses the tab key.\n * @private\n */\n private _activate(): void\n {\n if (this._isActive)\n {\n return;\n }\n\n this._isActive = true;\n\n // Create and add div if needed\n if (!this._div)\n {\n this._div = document.createElement('div');\n this._div.style.position = 'absolute';\n this._div.style.top = `${DIV_TOUCH_POS_X}px`;\n this._div.style.left = `${DIV_TOUCH_POS_Y}px`;\n this._div.style.pointerEvents = 'none';\n this._div.style.zIndex = DIV_TOUCH_ZINDEX.toString();\n\n // Initialize the CanvasTransformSync to keep the DOM element in sync with the canvas\n this._canvasObserver = new CanvasObserver({\n domElement: this._div,\n renderer: this._renderer,\n });\n }\n\n // Bind event handlers and add listeners when activating\n if (this._activateOnTab)\n {\n this._onKeyDown = this._onKeyDown.bind(this);\n globalThis.addEventListener('keydown', this._onKeyDown, false);\n }\n\n if (this._deactivateOnMouseMove)\n {\n this._onMouseMove = this._onMouseMove.bind(this);\n globalThis.document.addEventListener('mousemove', this._onMouseMove, true);\n }\n\n // Check if canvas is in DOM\n const canvas = this._renderer.view.canvas;\n\n if (!canvas.parentNode)\n {\n const observer = new MutationObserver(() =>\n {\n if (canvas.parentNode)\n {\n observer.disconnect();\n\n // Add to DOM\n this._canvasObserver.ensureAttached();\n // Only start the postrender runner after div is ready\n this._initAccessibilitySetup();\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true });\n }\n else\n {\n // Add to DOM\n this._canvasObserver.ensureAttached();\n // Div is ready, initialize accessibility\n this._initAccessibilitySetup();\n }\n }\n\n // New method to handle initialization after div is ready\n private _initAccessibilitySetup(): void\n {\n // Add the postrender runner to start processing accessible objects\n this._renderer.runners.postrender.add(this);\n\n // Force an initial update of accessible objects\n if (this._renderer.lastObjectRendered)\n {\n this._updateAccessibleObjects(this._renderer.lastObjectRendered as Container);\n }\n }\n\n /**\n * Deactivates the accessibility system. Removes listeners and accessibility elements.\n * @private\n */\n private _deactivate(): void\n {\n if (!this._isActive || this._isMobileAccessibility)\n {\n return;\n }\n\n this._isActive = false;\n\n // Switch listeners\n globalThis.document.removeEventListener('mousemove', this._onMouseMove, true);\n if (this._activateOnTab)\n {\n globalThis.addEventListener('keydown', this._onKeyDown, false);\n }\n\n this._renderer.runners.postrender.remove(this);\n\n // Remove all active accessibility elements\n for (const child of this._children)\n {\n if (child._accessibleDiv && child._accessibleDiv.parentNode)\n {\n child._accessibleDiv.parentNode.removeChild(child._accessibleDiv);\n child._accessibleDiv = null;\n }\n child._accessibleActive = false;\n }\n\n // Clear the pool of divs\n this._pool.forEach((div) =>\n {\n if (div.parentNode)\n {\n div.parentNode.removeChild(div);\n }\n });\n\n // Remove parent div from DOM\n if (this._div && this._div.parentNode)\n {\n this._div.parentNode.removeChild(this._div);\n }\n\n this._pool = [];\n this._children = [];\n }\n\n /**\n * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.\n * @private\n * @param {Container} container - The Container to check.\n */\n private _updateAccessibleObjects(container: Container): void\n {\n if (!container.visible || !container.accessibleChildren)\n {\n return;\n }\n\n // Separate check for accessibility without requiring interactivity\n if (container.accessible)\n {\n if (!container._accessibleActive)\n {\n this._addChild(container);\n }\n\n container._renderId = this._renderId;\n }\n\n const children = container.children;\n\n if (children)\n {\n for (let i = 0; i < children.length; i++)\n {\n this._updateAccessibleObjects(children[i] as Container);\n }\n }\n }\n\n /**\n * Runner init called, view is available at this point.\n * @ignore\n */\n public init(options?: AccessibilitySystemOptions): void\n {\n // Ensure we have the accessibilityOptions object\n const defaultOpts = AccessibilitySystem.defaultOptions;\n const mergedOptions = {\n accessibilityOptions: {\n ...defaultOpts,\n ...(options?.accessibilityOptions || {})\n }\n };\n\n this.debug = mergedOptions.accessibilityOptions.debug;\n this._activateOnTab = mergedOptions.accessibilityOptions.activateOnTab;\n this._deactivateOnMouseMove = mergedOptions.accessibilityOptions.deactivateOnMouseMove;\n\n if (mergedOptions.accessibilityOptions.enabledByDefault)\n {\n this._activate();\n }\n else if (this._activateOnTab)\n {\n this._onKeyDown = this._onKeyDown.bind(this);\n globalThis.addEventListener('keydown', this._onKeyDown, false);\n }\n\n this._renderer.runners.postrender.remove(this);\n }\n\n /**\n * Updates the accessibility layer during rendering.\n * - Removes divs for containers no longer in the scene\n * - Updates the position and dimensions of the root div\n * - Updates positions of active accessibility divs\n * Only fires while the accessibility system is active.\n * @ignore\n */\n public postrender(): void\n {\n /* On Android default web browser, tab order seems to be calculated by position rather than tabIndex,\n * moving buttons can cause focus to flicker between two buttons making it hard/impossible to navigate,\n * so I am just running update every half a second, seems to fix it.\n */\n const now = performance.now();\n\n if (this._mobileInfo.android.device && now < this._androidUpdateCount)\n {\n return;\n }\n\n this._androidUpdateCount = now + this._androidUpdateFrequency;\n\n if (!this._renderer.renderingToScreen || !this._renderer.view.canvas)\n {\n return;\n }\n\n // Track which containers are still active this frame\n const activeIds = new Set();\n\n if (this._renderer.lastObjectRendered)\n {\n this._updateAccessibleObjects(this._renderer.lastObjectRendered as Container);\n\n // Mark all updated containers as active\n for (const child of this._children)\n {\n if (child._renderId === this._renderId)\n {\n activeIds.add(this._children.indexOf(child));\n }\n }\n }\n\n // Remove any containers that weren't updated this frame\n for (let i = this._children.length - 1; i >= 0; i--)\n {\n const child = this._children[i];\n\n if (!activeIds.has(i))\n {\n // Container was removed, clean up its accessibility div\n if (child._accessibleDiv && child._accessibleDiv.parentNode)\n {\n child._accessibleDiv.parentNode.removeChild(child._accessibleDiv);\n\n this._pool.push(child._accessibleDiv);\n child._accessibleDiv = null;\n }\n child._accessibleActive = false;\n removeItems(this._children, i, 1);\n }\n }\n\n // Update root div dimensions if needed\n if (this._renderer.renderingToScreen)\n {\n // Ensure the main DOM element is attached to the same parent as the canvas\n this._canvasObserver.ensureAttached();\n }\n\n // Update positions of existing divs\n for (let i = 0; i < this._children.length; i++)\n {\n const child = this._children[i];\n\n if (!child._accessibleActive || !child._accessibleDiv)\n {\n continue;\n }\n\n // Only update position-related properties\n const div = child._accessibleDiv;\n const hitArea = (child.hitArea || child.getBounds().rectangle) as Rectangle;\n\n if (child.hitArea)\n {\n const wt = child.worldTransform;\n\n div.style.left = `${(wt.tx + (hitArea.x * wt.a))}px`;\n div.style.top = `${(wt.ty + (hitArea.y * wt.d))}px`;\n div.style.width = `${hitArea.width * wt.a}px`;\n div.style.height = `${hitArea.height * wt.d}px`;\n }\n else\n {\n this._capHitArea(hitArea);\n div.style.left = `${hitArea.x}px`;\n div.style.top = `${hitArea.y}px`;\n div.style.width = `${hitArea.width}px`;\n div.style.height = `${hitArea.height}px`;\n }\n }\n\n // increment the render id..\n this._renderId++;\n }\n\n /**\n * private function that will visually add the information to the\n * accessibility div\n * @param {HTMLElement} div -\n */\n private _updateDebugHTML(div: AccessibleHTMLElement): void\n {\n div.innerHTML = `type: ${div.type}
title : ${div.title}
tabIndex: ${div.tabIndex}`;\n }\n\n /**\n * Adjust the hit area based on the bounds of a display object\n * @param {Rectangle} hitArea - Bounds of the child\n */\n private _capHitArea(hitArea: Rectangle): void\n {\n if (hitArea.x < 0)\n {\n hitArea.width += hitArea.x;\n hitArea.x = 0;\n }\n\n if (hitArea.y < 0)\n {\n hitArea.height += hitArea.y;\n hitArea.y = 0;\n }\n\n const { width: viewWidth, height: viewHeight } = this._renderer;\n\n if (hitArea.x + hitArea.width > viewWidth)\n {\n hitArea.width = viewWidth - hitArea.x;\n }\n\n if (hitArea.y + hitArea.height > viewHeight)\n {\n hitArea.height = viewHeight - hitArea.y;\n }\n }\n\n /**\n * Creates or reuses a div element for a Container and adds it to the accessibility layer.\n * Sets up ARIA attributes, event listeners, and positioning based on the container's properties.\n * @private\n * @param {Container} container - The child to make accessible.\n */\n private _addChild(container: T): void\n {\n let div = this._pool.pop();\n\n if (!div)\n {\n if (container.accessibleType === 'button')\n {\n div = document.createElement('button');\n }\n else\n {\n div = document.createElement(container.accessibleType);\n div.style.cssText = `\n color: transparent;\n pointer-events: none;\n padding: 0;\n margin: 0;\n border: 0;\n outline: 0;\n background: transparent;\n box-sizing: border-box;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n `;\n if (container.accessibleText)\n {\n div.innerText = container.accessibleText;\n }\n }\n div.style.width = `${DIV_TOUCH_SIZE}px`;\n div.style.height = `${DIV_TOUCH_SIZE}px`;\n div.style.backgroundColor = this.debug ? 'rgba(255,255,255,0.5)' : 'transparent';\n div.style.position = 'absolute';\n div.style.zIndex = DIV_TOUCH_ZINDEX.toString();\n div.style.borderStyle = 'none';\n\n // ARIA attributes ensure that button title and hint updates are announced properly\n if (navigator.userAgent.toLowerCase().includes('chrome'))\n {\n // Chrome doesn't need aria-live to work as intended; in fact it just gets more confused.\n div.setAttribute('aria-live', 'off');\n }\n else\n {\n div.setAttribute('aria-live', 'polite');\n }\n\n if (navigator.userAgent.match(/rv:.*Gecko\\//))\n {\n // FireFox needs this to announce only the new button name\n div.setAttribute('aria-relevant', 'additions');\n }\n else\n {\n // required by IE, other browsers don't much care\n div.setAttribute('aria-relevant', 'text');\n }\n\n div.addEventListener('click', this._onClick.bind(this));\n div.addEventListener('focus', this._onFocus.bind(this));\n div.addEventListener('focusout', this._onFocusOut.bind(this));\n }\n\n // set pointer events\n div.style.pointerEvents = container.accessiblePointerEvents;\n // set the type, this defaults to button!\n div.type = container.accessibleType;\n\n if (container.accessibleTitle && container.accessibleTitle !== null)\n {\n div.title = container.accessibleTitle;\n }\n else if (!container.accessibleHint\n || container.accessibleHint === null)\n {\n div.title = `container ${container.tabIndex}`;\n }\n\n if (container.accessibleHint\n && container.accessibleHint !== null)\n {\n div.setAttribute('aria-label', container.accessibleHint);\n }\n\n if (this.debug)\n {\n this._updateDebugHTML(div);\n }\n\n container._accessibleActive = true;\n container._accessibleDiv = div;\n div.container = container;\n\n this._children.push(container);\n this._div.appendChild(container._accessibleDiv);\n if (container.interactive)\n {\n container._accessibleDiv.tabIndex = container.tabIndex;\n }\n }\n\n /**\n * Dispatch events with the EventSystem.\n * @param e\n * @param type\n * @private\n */\n private _dispatchEvent(e: UIEvent, type: string[]): void\n {\n const { container: target } = e.target as AccessibleHTMLElement;\n const boundary = this._renderer.events.rootBoundary;\n const event: FederatedEvent = Object.assign(new FederatedEvent(boundary), { target });\n\n boundary.rootTarget = this._renderer.lastObjectRendered as Container;\n type.forEach((type) => boundary.dispatchEvent(event, type));\n }\n\n /**\n * Maps the div button press to pixi's EventSystem (click)\n * @private\n * @param {MouseEvent} e - The click event.\n */\n private _onClick(e: MouseEvent): void\n {\n this._dispatchEvent(e, ['click', 'pointertap', 'tap']);\n }\n\n /**\n * Maps the div focus events to pixi's EventSystem (mouseover)\n * @private\n * @param {FocusEvent} e - The focus event.\n */\n private _onFocus(e: FocusEvent): void\n {\n if (!(e.target as Element).getAttribute('aria-live'))\n {\n (e.target as Element).setAttribute('aria-live', 'assertive');\n }\n\n this._dispatchEvent(e, ['mouseover']);\n }\n\n /**\n * Maps the div focus events to pixi's EventSystem (mouseout)\n * @private\n * @param {FocusEvent} e - The focusout event.\n */\n private _onFocusOut(e: FocusEvent): void\n {\n if (!(e.target as Element).getAttribute('aria-live'))\n {\n (e.target as Element).setAttribute('aria-live', 'polite');\n }\n\n this._dispatchEvent(e, ['mouseout']);\n }\n\n /**\n * Is called when a key is pressed\n * @private\n * @param {KeyboardEvent} e - The keydown event.\n */\n private _onKeyDown(e: KeyboardEvent): void\n {\n if (e.keyCode !== KEY_CODE_TAB || !this._activateOnTab)\n {\n return;\n }\n\n this._activate();\n }\n\n /**\n * Is called when the mouse moves across the renderer element\n * @private\n * @param {MouseEvent} e - The mouse event.\n */\n private _onMouseMove(e: MouseEvent): void\n {\n if (e.movementX === 0 && e.movementY === 0)\n {\n return;\n }\n\n this._deactivate();\n }\n\n /**\n * Destroys the accessibility system. Removes all elements and listeners.\n * > [!IMPORTANT] This is typically called automatically when the {@link Application} is destroyed.\n * > A typically user should not need to call this method directly.\n */\n public destroy(): void\n {\n this._deactivate();\n this._destroyTouchHook();\n\n this._canvasObserver?.destroy();\n this._canvasObserver = null;\n\n this._div = null;\n this._pool = null;\n this._children = null;\n this._renderer = null;\n\n if (this._activateOnTab)\n {\n globalThis.removeEventListener('keydown', this._onKeyDown);\n }\n }\n\n /**\n * Enables or disables the accessibility system.\n * @param enabled - Whether to enable or disable accessibility.\n * @example\n * ```js\n * app.renderer.accessibility.setAccessibilityEnabled(true); // Enable accessibility\n * app.renderer.accessibility.setAccessibilityEnabled(false); // Disable accessibility\n * ```\n */\n public setAccessibilityEnabled(enabled: boolean): void\n {\n if (enabled)\n {\n this._activate();\n }\n else\n {\n this._deactivate();\n }\n }\n}\n","import type { Container } from '../scene/container/Container';\n\n/**\n * The type of the pointer event to listen for.\n * @category accessibility\n * @standard\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events\n */\nexport type PointerEvents = 'auto'\n| 'none'\n| 'visiblePainted'\n| 'visibleFill'\n| 'visibleStroke'\n| 'visible'\n| 'painted'\n| 'fill'\n| 'stroke'\n| 'all'\n| 'inherit';\n\n/**\n * When `accessible` is enabled on any display object, these properties will affect its accessibility.\n * @example\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleTitle = 'My Container';\n * container.accessibleHint = 'This is a container';\n * container.tabIndex = 0;\n * @category accessibility\n * @standard\n */\nexport interface AccessibleOptions\n{\n /**\n * Flag for if the object is accessible. If true AccessibilityManager will overlay a\n * shadow div with attributes set\n * @default false\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * ```\n */\n accessible: boolean;\n /**\n * Sets the title attribute of the shadow div\n * If accessibleTitle AND accessibleHint has not been this will default to 'container [tabIndex]'\n * @type {string}\n * @default null\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleTitle = 'My Container';\n * ```\n */\n accessibleTitle: string | null;\n /**\n * Sets the aria-label attribute of the shadow div\n * @default null\n * @advanced\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleHint = 'This is a container';\n * ```\n */\n accessibleHint: string | null;\n /**\n * Sets the tabIndex of the shadow div. You can use this to set the order of the\n * elements when using the tab key to navigate.\n * @default 0\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.tabIndex = 0;\n *\n * const sprite = new Sprite(texture);\n * sprite.accessible = true;\n * sprite.tabIndex = 1;\n * ```\n */\n tabIndex: number;\n /**\n * Specify the type of div the accessible layer is. Screen readers treat the element differently\n * depending on this type. Defaults to button.\n * @default 'button'\n * @type {string}\n * @advanced\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleType = 'button'; // or 'link', 'checkbox', etc.\n * ```\n */\n accessibleType: keyof HTMLElementTagNameMap;\n /**\n * Specify the pointer-events the accessible div will use\n * Defaults to auto.\n * @default 'auto'\n * @type {PointerEvents}\n * @advanced\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessiblePointerEvents = 'none'; // or 'auto', 'visiblePainted', etc.\n * ```\n */\n accessiblePointerEvents: PointerEvents;\n\n /**\n * Sets the text content of the shadow\n * @default null\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleText = 'This is a container';\n * ```\n */\n accessibleText: string | null;\n\n /**\n * Setting to false will prevent any children inside this container to\n * be accessible. Defaults to true.\n * @default true\n * @example\n * ```js\n * const container = new Container();\n * container.accessible = true;\n * container.accessibleChildren = false; // This will prevent any children from being accessible\n *\n * const sprite = new Sprite(texture);\n * sprite.accessible = true; // This will not work since accessibleChildren is false\n * ```\n */\n accessibleChildren: boolean;\n}\n\n/**\n * The Accessibility object is attached to the {@link Container}.\n * @private\n */\nexport interface AccessibleTarget extends AccessibleOptions\n{\n /** @private */\n _accessibleActive: boolean;\n /** @private */\n _accessibleDiv: AccessibleHTMLElement | null;\n /** @private */\n _renderId: number;\n}\n\n/** @internal */\nexport interface AccessibleHTMLElement extends HTMLElement\n{\n type?: string;\n container?: Container;\n}\n\n/**\n * Default property values of accessible objects\n * used by {@link AccessibilitySystem}.\n * @internal\n * @example\n * import { accessibleTarget } from 'pixi.js';\n *\n * function MyObject() {}\n * Object.assign(MyObject.prototype, accessibleTarget);\n */\nexport const accessibilityTarget: AccessibleTarget = {\n accessible: false,\n accessibleTitle: null,\n accessibleHint: null,\n tabIndex: 0,\n accessibleType: 'button',\n accessibleText: null,\n accessiblePointerEvents: 'auto',\n accessibleChildren: true,\n _accessibleActive: false,\n _accessibleDiv: null,\n _renderId: -1,\n};\n","import { extensions } from '../extensions/Extensions';\nimport { Container } from '../scene/container/Container';\nimport { AccessibilitySystem } from './AccessibilitySystem';\nimport { accessibilityTarget } from './accessibilityTarget';\n\nextensions.add(AccessibilitySystem);\nextensions.mixin(Container, accessibilityTarget);\n","import { ExtensionType } from '../extensions/Extensions';\n\nimport type { ExtensionMetadata } from '../extensions/Extensions';\nimport type { Renderer } from '../rendering/renderers/types';\n\ntype ResizeableRenderer = Pick;\n\n/**\n * Application options for the {@link ResizePlugin}.\n * These options control how your application handles window and element resizing.\n * @example\n * ```ts\n * // Auto-resize to window\n * await app.init({ resizeTo: window });\n *\n * // Auto-resize to container element\n * await app.init({ resizeTo: document.querySelector('#game') });\n * ```\n * @category app\n * @standard\n */\nexport interface ResizePluginOptions\n{\n /**\n * Element to automatically resize the renderer to.\n * @example\n * ```ts\n * const app = new Application();\n * await app.init({\n * resizeTo: window, // Resize to the entire window\n * // or\n * resizeTo: document.querySelector('#game-container'), // Resize to a specific element\n * // or\n * resizeTo: null, // Disable auto-resize\n * });\n * ```\n * @default null\n */\n resizeTo?: Window | HTMLElement;\n}\n\n/**\n * Middleware for Application's resize functionality. This plugin handles automatic\n * and manual resizing of your PixiJS application.\n *\n * Adds the following features to {@link Application}:\n * - `resizeTo`: Set an element to automatically resize to\n * - `resize`: Manually trigger a resize\n * - `queueResize`: Queue a resize for the next animation frame\n * - `cancelResize`: Cancel a queued resize\n * @example\n * ```ts\n * import { Application, ResizePlugin } from 'pixi.js';\n *\n * // Create application\n * const app = new Application();\n *\n * // Example 1: Auto-resize to window\n * await app.init({ resizeTo: window });\n *\n * // Example 2: Auto-resize to specific element\n * const container = document.querySelector('#game-container');\n * await app.init({ resizeTo: container });\n *\n * // Example 3: Change resize target at runtime\n * app.resizeTo = window; // Enable auto-resize to window\n * app.resizeTo = null; // Disable auto-resize\n * ```\n * @category app\n * @standard\n */\nexport class ResizePlugin\n{\n /** @ignore */\n public static extension: ExtensionMetadata = ExtensionType.Application;\n /** @internal */\n public static resizeTo: Window | HTMLElement;\n /** @internal */\n public static resize: () => void;\n /** @internal */\n public static renderer: ResizeableRenderer;\n /** @internal */\n public static queueResize: () => void;\n /** @internal */\n public static render: () => void;\n /** @internal */\n private static _resizeId: number;\n /** @internal */\n private static _resizeTo: Window | HTMLElement;\n /** @internal */\n private static _cancelResize: () => void;\n\n /**\n * Initialize the plugin with scope of application instance\n * @private\n * @param {object} [options] - See application options\n */\n public static init(options: ResizePluginOptions): void\n {\n Object.defineProperty(this, 'resizeTo',\n {\n set(dom: Window | HTMLElement)\n {\n globalThis.removeEventListener('resize', this.queueResize);\n this._resizeTo = dom;\n if (dom)\n {\n globalThis.addEventListener('resize', this.queueResize);\n this.resize();\n }\n },\n get()\n {\n return this._resizeTo;\n },\n });\n\n this.queueResize = (): void =>\n {\n if (!this._resizeTo)\n {\n return;\n }\n\n this._cancelResize();\n\n // // Throttle resize events per raf\n this._resizeId = requestAnimationFrame(() => this.resize());\n };\n\n this._cancelResize = (): void =>\n {\n if (this._resizeId)\n {\n cancelAnimationFrame(this._resizeId);\n this._resizeId = null;\n }\n };\n\n this.resize = (): void =>\n {\n if (!this._resizeTo)\n {\n return;\n }\n\n // clear queue resize\n this._cancelResize();\n\n let width: number;\n let height: number;\n\n // Resize to the window\n if (this._resizeTo === globalThis.window)\n {\n width = globalThis.innerWidth;\n height = globalThis.innerHeight;\n }\n // Resize to other HTML entities\n else\n {\n const { clientWidth, clientHeight } = this._resizeTo as HTMLElement;\n\n width = clientWidth;\n height = clientHeight;\n }\n\n this.renderer.resize(width, height);\n this.render();\n };\n\n // On resize\n this._resizeId = null;\n this._resizeTo = null;\n this.resizeTo = options.resizeTo || null;\n }\n\n /**\n * Clean up the ticker, scoped to application\n * @private\n */\n public static destroy(): void\n {\n globalThis.removeEventListener('resize', this.queueResize);\n this._cancelResize();\n this._cancelResize = null;\n this.queueResize = null;\n this.resizeTo = null;\n this.resize = null;\n }\n}\n","import { ExtensionType } from '../extensions/Extensions';\nimport { UPDATE_PRIORITY } from '../ticker/const';\nimport { Ticker } from '../ticker/Ticker';\n\nimport type { ExtensionMetadata } from '../extensions/Extensions';\n\n/**\n * Application options for the {@link TickerPlugin}.\n * These options control the animation loop and update cycle of your PixiJS application.\n * @example\n * ```ts\n * import { Application } from 'pixi.js';\n *\n * // Basic setup with default options\n * const app = new Application();\n * await app.init({\n * autoStart: true, // Start animation loop automatically\n * sharedTicker: false // Use dedicated ticker instance\n * });\n *\n * // Advanced setup with shared ticker\n * const app2 = new Application();\n * await app2.init({\n * autoStart: false, // Don't start automatically\n * sharedTicker: true // Use global shared ticker\n * });\n *\n * // Start animation when ready\n * app2.start();\n * ```\n * @remarks\n * The ticker is the heart of your application's animation system. It:\n * - Manages the render loop\n * - Provides accurate timing information\n * - Handles frame-based updates\n * - Supports priority-based execution order\n * @see {@link Ticker} For detailed ticker functionality\n * @see {@link UPDATE_PRIORITY} For update priority constants\n * @category app\n * @standard\n */\nexport interface TickerPluginOptions\n{\n /**\n * Controls whether the animation loop starts automatically after initialization.\n * > [!IMPORTANT]\n * > Setting this to `false` does NOT stop the shared ticker even if `sharedTicker` is `true`.\n * > You must stop the shared ticker manually if needed.\n * @example\n * ```ts\n * // Auto-start (default behavior)\n * await app.init({ autoStart: true });\n *\n * // Manual start\n * await app.init({ autoStart: false });\n * app.start(); // Start when ready\n * ```\n * @default true\n */\n autoStart?: boolean;\n\n /**\n * Controls whether to use the shared global ticker or create a new instance.\n *\n * The shared ticker is useful when you have multiple instances that should sync their updates.\n * However, it has some limitations regarding update order control.\n *\n * Update Order:\n * 1. System ticker (always runs first)\n * 2. Shared ticker (if enabled)\n * 3. App ticker (if using own ticker)\n * @example\n * ```ts\n * // Use shared ticker (global instance)\n * await app.init({ sharedTicker: true });\n *\n * // Use dedicated ticker (default)\n * await app.init({ sharedTicker: false });\n *\n * // Access ticker properties\n * console.log(app.ticker.FPS); // Current FPS\n * console.log(app.ticker.deltaMS); // MS since last update\n * ```\n * @default false\n */\n sharedTicker?: boolean;\n}\n\n/**\n * Middleware for Application's {@link Ticker} functionality. This plugin manages the\n * animation loop and update cycle of your PixiJS application.\n *\n * Adds the following features to {@link Application}:\n * - `ticker`: Access to the application's ticker\n * - `start`: Start the animation loop\n * - `stop`: Stop the animation loop\n * @example\n * ```ts\n * import { Application, TickerPlugin, extensions } from 'pixi.js';\n *\n * // Create application\n * const app = new Application();\n *\n * // Example 1: Basic ticker usage (default autoStart)\n * await app.init({ autoStart: true }); // Starts ticker automatically\n *\n * // Example 2: Manual ticker control\n * await app.init({ autoStart: false }); // Don't start automatically\n * app.start(); // Start manually\n * app.stop(); // Stop manually\n *\n * // Example 3: Add custom update logic\n * app.ticker.add((ticker) => {\n * // Run every frame, delta is the time since last update\n * sprite.rotation += 0.1 * ticker.deltaTime;\n * });\n *\n * // Example 4: Control update priority\n * import { UPDATE_PRIORITY } from 'pixi.js';\n *\n * app.ticker.add(\n * (ticker) => {\n * // Run before normal priority updates\n * },\n * null,\n * UPDATE_PRIORITY.HIGH\n * );\n *\n * // Example 5: One-time update\n * app.ticker.addOnce(() => {\n * console.log('Runs next frame only');\n * });\n * ```\n * @see {@link Ticker} For detailed ticker functionality\n * @see {@link UPDATE_PRIORITY} For priority constants\n * @category app\n * @standard\n */\nexport class TickerPlugin\n{\n /** @ignore */\n public static extension: ExtensionMetadata = ExtensionType.Application;\n\n /** @internal */\n public static start: () => void;\n /** @internal */\n public static stop: () => void;\n /** @internal */\n private static _ticker: Ticker;\n /** @internal */\n public static ticker: Ticker;\n\n /**\n * Initialize the plugin with scope of application instance\n * @private\n * @param {object} [options] - See application options\n */\n public static init(options?: PixiMixins.ApplicationOptions): void\n {\n // Set default\n options = Object.assign({\n autoStart: true,\n sharedTicker: false,\n }, options);\n\n // Create ticker setter\n Object.defineProperty(this, 'ticker',\n {\n set(ticker)\n {\n if (this._ticker)\n {\n this._ticker.remove(this.render, this);\n }\n this._ticker = ticker;\n if (ticker)\n {\n ticker.add(this.render, this, UPDATE_PRIORITY.LOW);\n }\n },\n get()\n {\n return this._ticker;\n },\n });\n\n this.stop = (): void =>\n {\n this._ticker.stop();\n };\n\n this.start = (): void =>\n {\n this._ticker.start();\n };\n\n this._ticker = null;\n this.ticker = options.sharedTicker ? Ticker.shared : new Ticker();\n\n // Start the rendering\n if (options.autoStart)\n {\n this.start();\n }\n }\n\n /**\n * Clean up the ticker, scoped to application.\n * @private\n */\n public static destroy(): void\n {\n if (this._ticker)\n {\n const oldTicker = this._ticker;\n\n this.ticker = null;\n oldTicker.destroy();\n }\n }\n}\n","import { extensions } from '../extensions/Extensions';\nimport { ResizePlugin } from './ResizePlugin';\nimport { TickerPlugin } from './TickerPlugin';\n\nextensions.add(ResizePlugin);\nextensions.add(TickerPlugin);\n","import { UPDATE_PRIORITY } from '../ticker/const';\nimport { Ticker } from '../ticker/Ticker';\n\nimport type { EventSystem } from './EventSystem';\n\n/** @advanced */\nclass EventsTickerClass\n{\n /** The event system. */\n public events: EventSystem;\n /** The DOM element to listen to events on. */\n public domElement: HTMLElement;\n /** The frequency that fake events will be fired. */\n public interactionFrequency = 10;\n\n private _deltaTime = 0;\n private _didMove = false;\n private _tickerAdded = false;\n private _pauseUpdate = true;\n\n /**\n * Initializes the event ticker.\n * @param events - The event system.\n */\n public init(events: EventSystem): void\n {\n this.removeTickerListener();\n this.events = events;\n this.interactionFrequency = 10;\n this._deltaTime = 0;\n this._didMove = false;\n this._tickerAdded = false;\n this._pauseUpdate = true;\n }\n\n /** Whether to pause the update checks or not. */\n get pauseUpdate(): boolean\n {\n return this._pauseUpdate;\n }\n\n set pauseUpdate(paused: boolean)\n {\n this._pauseUpdate = paused;\n }\n\n /** Adds the ticker listener. */\n public addTickerListener(): void\n {\n if (this._tickerAdded || !this.domElement)\n {\n return;\n }\n\n Ticker.system.add(this._tickerUpdate, this, UPDATE_PRIORITY.INTERACTION);\n\n this._tickerAdded = true;\n }\n\n /** Removes the ticker listener. */\n public removeTickerListener(): void\n {\n if (!this._tickerAdded)\n {\n return;\n }\n\n Ticker.system.remove(this._tickerUpdate, this);\n\n this._tickerAdded = false;\n }\n\n /** Sets flag to not fire extra events when the user has already moved there mouse */\n public pointerMoved(): void\n {\n this._didMove = true;\n }\n\n /** Updates the state of interactive objects. */\n private _update(): void\n {\n if (!this.domElement || this._pauseUpdate)\n {\n return;\n }\n\n // if the user move the mouse this check has already been done using the mouse move!\n if (this._didMove)\n {\n this._didMove = false;\n\n return;\n }\n\n // eslint-disable-next-line dot-notation\n const rootPointerEvent = this.events['_rootPointerEvent'];\n\n if (this.events.supportsTouchEvents && (rootPointerEvent as PointerEvent).pointerType === 'touch')\n {\n return;\n }\n\n globalThis.document.dispatchEvent(this.events.supportsPointerEvents ? new PointerEvent('pointermove', {\n clientX: rootPointerEvent.clientX,\n clientY: rootPointerEvent.clientY,\n pointerType: rootPointerEvent.pointerType,\n pointerId: rootPointerEvent.pointerId,\n }) : new MouseEvent('mousemove', {\n clientX: rootPointerEvent.clientX,\n clientY: rootPointerEvent.clientY,\n }));\n }\n\n /**\n * Updates the state of interactive objects if at least {@link interactionFrequency}\n * milliseconds have passed since the last invocation.\n *\n * Invoked by a throttled ticker update from {@link Ticker.system}.\n * @param ticker - The throttled ticker.\n */\n private _tickerUpdate(ticker: Ticker): void\n {\n this._deltaTime += ticker.deltaTime;\n\n if (this._deltaTime < this.interactionFrequency)\n {\n return;\n }\n\n this._deltaTime = 0;\n\n this._update();\n }\n}\n\n/**\n * This class handles automatic firing of PointerEvents\n * in the case where the pointer is stationary for too long.\n * This is to ensure that hit-tests are still run on moving objects.\n * @since 7.2.0\n * @category events\n * @class\n * @advanced\n */\nexport const EventsTicker = new EventsTickerClass();\n","import { Point } from '../maths/point/Point';\nimport { FederatedEvent } from './FederatedEvent';\n\nimport type { PointData } from '../maths/point/PointData';\nimport type { Container } from '../scene/container/Container';\nimport type { PixiTouch } from './FederatedEvent';\n\n/**\n * A specialized event class for mouse interactions in PixiJS applications.\n * Extends {@link FederatedEvent} to provide mouse-specific properties and methods\n * while maintaining compatibility with the DOM MouseEvent interface.\n *\n * Key features:\n * - Tracks mouse button states\n * - Provides modifier key states\n * - Supports coordinate systems (client, screen, global)\n * - Enables precise position tracking\n * @example\n * ```ts\n * // Basic mouse event handling\n * sprite.on('mousemove', (event: FederatedMouseEvent) => {\n * // Get coordinates in different spaces\n * console.log('Global position:', event.global.x, event.global.y);\n * console.log('Client position:', event.client.x, event.client.y);\n * console.log('Screen position:', event.screen.x, event.screen.y);\n *\n * // Check button and modifier states\n * if (event.buttons === 1 && event.ctrlKey) {\n * console.log('Left click + Control key');\n * }\n *\n * // Get local coordinates relative to any container\n * const localPos = event.getLocalPosition(container);\n * console.log('Local position:', localPos.x, localPos.y);\n * });\n *\n * // Handle mouse button states\n * sprite.on('mousedown', (event: FederatedMouseEvent) => {\n * console.log('Mouse button:', event.button); // 0=left, 1=middle, 2=right\n * console.log('Active buttons:', event.buttons);\n * });\n * ```\n * @category events\n * @see {@link FederatedEvent} For base event functionality\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent} DOM MouseEvent Interface\n * @standard\n */\nexport class FederatedMouseEvent extends FederatedEvent<\nMouseEvent | PointerEvent | PixiTouch\n> implements MouseEvent\n{\n /** Whether the \"alt\" key was pressed when this mouse event occurred. */\n public altKey: boolean;\n\n /** The specific button that was pressed in this mouse event. */\n public button: number;\n\n /** The button depressed when this event occurred. */\n public buttons: number;\n\n /** Whether the \"control\" key was pressed when this mouse event occurred. */\n public ctrlKey: boolean;\n\n /** Whether the \"meta\" key was pressed when this mouse event occurred. */\n public metaKey: boolean;\n\n /** This is currently not implemented in the Federated Events API. */\n public relatedTarget: EventTarget;\n\n /** Whether the \"shift\" key was pressed when this mouse event occurred. */\n public shiftKey: boolean;\n\n /** The coordinates of the mouse event relative to the canvas. */\n public client: Point = new Point();\n\n /** @readonly */\n public get clientX(): number { return this.client.x; }\n\n /** @readonly */\n public get clientY(): number { return this.client.y; }\n\n /**\n * Alias for {@link FederatedMouseEvent.clientX this.clientX}.\n * @readonly\n */\n get x(): number { return this.clientX; }\n\n /**\n * Alias for {@link FederatedMouseEvent.clientY this.clientY}.\n * @readonly\n */\n get y(): number { return this.clientY; }\n\n /** This is the number of clicks that occurs in 200ms/click of each other. */\n public detail: number;\n\n /** The movement in this pointer relative to the last `mousemove` event. */\n public movement: Point = new Point();\n\n /** @readonly */\n get movementX(): number { return this.movement.x; }\n\n /** @readonly */\n get movementY(): number { return this.movement.y; }\n\n /** The offset of the pointer coordinates w.r.t. target Container in world space. This is not supported at the moment. */\n public offset: Point = new Point();\n\n /** @readonly */\n get offsetX(): number { return this.offset.x; }\n\n /** @readonly */\n get offsetY(): number { return this.offset.y; }\n\n /** The pointer coordinates in world space. */\n public global: Point = new Point();\n\n /** @readonly */\n get globalX(): number { return this.global.x; }\n\n /** @readonly */\n get globalY(): number { return this.global.y; }\n\n /**\n * The pointer coordinates in the renderer's {@link AbstractRenderer.screen screen}. This has slightly\n * different semantics than native PointerEvent screenX/screenY.\n */\n public screen: Point = new Point();\n\n /**\n * The pointer coordinates in the renderer's screen. Alias for `screen.x`.\n * @readonly\n */\n get screenX(): number { return this.screen.x; }\n\n /**\n * The pointer coordinates in the renderer's screen. Alias for `screen.y`.\n * @readonly\n */\n get screenY(): number { return this.screen.y; }\n\n /**\n * Converts global coordinates into container-local coordinates.\n *\n * This method transforms coordinates from world space to a container's local space,\n * useful for precise positioning and hit testing.\n * @param container - The Container to get local coordinates for\n * @param point - Optional Point object to store the result. If not provided, a new Point will be created\n * @param globalPos - Optional custom global coordinates. If not provided, the event's global position is used\n * @returns The local coordinates as a Point object\n * @example\n * ```ts\n * // Basic usage - get local coordinates relative to a container\n * sprite.on('pointermove', (event: FederatedMouseEvent) => {\n * // Get position relative to the sprite\n * const localPos = event.getLocalPosition(sprite);\n * console.log('Local position:', localPos.x, localPos.y);\n * });\n * // Using custom global coordinates\n * const customGlobal = new Point(100, 100);\n * sprite.on('pointermove', (event: FederatedMouseEvent) => {\n * // Transform custom coordinates\n * const localPos = event.getLocalPosition(sprite, undefined, customGlobal);\n * console.log('Custom local position:', localPos.x, localPos.y);\n * });\n * ```\n * @see {@link Container.worldTransform} For the transformation matrix\n * @see {@link Point} For the point class used to store coordinates\n */\n public getLocalPosition

(container: Container, point?: P, globalPos?: PointData): P\n {\n return container.worldTransform.applyInverse

(globalPos || this.global, point);\n }\n\n /**\n * Whether the modifier key was pressed when this event natively occurred.\n * @param key - The modifier key.\n */\n public getModifierState(key: string): boolean\n {\n return 'getModifierState' in this.nativeEvent && this.nativeEvent.getModifierState(key);\n }\n\n /**\n * Not supported.\n * @param _typeArg\n * @param _canBubbleArg\n * @param _cancelableArg\n * @param _viewArg\n * @param _detailArg\n * @param _screenXArg\n * @param _screenYArg\n * @param _clientXArg\n * @param _clientYArg\n * @param _ctrlKeyArg\n * @param _altKeyArg\n * @param _shiftKeyArg\n * @param _metaKeyArg\n * @param _buttonArg\n * @param _relatedTargetArg\n * @deprecated since 7.0.0\n * @ignore\n */\n // eslint-disable-next-line max-params\n public initMouseEvent(\n _typeArg: string,\n _canBubbleArg: boolean,\n _cancelableArg: boolean,\n _viewArg: Window,\n _detailArg: number,\n _screenXArg: number,\n _screenYArg: number,\n _clientXArg: number,\n _clientYArg: number,\n _ctrlKeyArg: boolean,\n _altKeyArg: boolean,\n _shiftKeyArg: boolean,\n _metaKeyArg: boolean,\n _buttonArg: number,\n _relatedTargetArg: EventTarget\n ): void\n {\n throw new Error('Method not implemented.');\n }\n}\n","import { FederatedMouseEvent } from './FederatedMouseEvent';\n\n/**\n * A specialized event class for pointer interactions in PixiJS applications.\n * Extends {@link FederatedMouseEvent} to provide advanced pointer-specific features\n * while maintaining compatibility with the DOM PointerEvent interface.\n *\n * Key features:\n * - Supports multi-touch interactions\n * - Provides pressure sensitivity\n * - Handles stylus input\n * - Tracks pointer dimensions\n * - Supports tilt detection\n * @example\n * ```ts\n * // Basic pointer event handling\n * sprite.on('pointerdown', (event: FederatedPointerEvent) => {\n * // Access pointer information\n * console.log('Pointer ID:', event.pointerId);\n * console.log('Pointer Type:', event.pointerType);\n * console.log('Is Primary:', event.isPrimary);\n *\n * // Get pressure and tilt data\n * console.log('Pressure:', event.pressure);\n * console.log('Tilt:', event.tiltX, event.tiltY);\n *\n * // Access contact geometry\n * console.log('Size:', event.width, event.height);\n * });\n *\n * // Handle stylus-specific features\n * sprite.on('pointermove', (event: FederatedPointerEvent) => {\n * if (event.pointerType === 'pen') {\n * // Handle stylus tilt\n * const tiltAngle = Math.atan2(event.tiltY, event.tiltX);\n * console.log('Tilt angle:', tiltAngle);\n *\n * // Use barrel button pressure\n * console.log('Tangential pressure:', event.tangentialPressure);\n * }\n * });\n * ```\n * @see {@link FederatedMouseEvent} For base mouse event functionality\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent} DOM PointerEvent Interface\n * @see {@link EventSystem} For the event management system\n * @category events\n * @standard\n */\nexport class FederatedPointerEvent extends FederatedMouseEvent implements PointerEvent\n{\n /**\n * The unique identifier of the pointer.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId}\n */\n public pointerId: number;\n\n /**\n * The width of the pointer's contact along the x-axis, measured in CSS pixels.\n * radiusX of TouchEvents will be represented by this value.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width\n */\n public width = 0;\n\n /**\n * The angle in radians of a pointer or stylus measuring the vertical angle between\n * the device's surface to the pointer or stylus.\n * A stylus at 0 degrees would be directly parallel whereas at π/2 degrees it would be perpendicular.\n * @see https://developer.mozilla.org/docs/Web/API/PointerEvent/altitudeAngle)\n */\n public altitudeAngle: number;\n\n /**\n * The angle in radians of a pointer or stylus measuring an arc from the X axis of the device to\n * the pointer or stylus projected onto the screen's plane.\n * A stylus at 0 degrees would be pointing to the \"0 o'clock\" whereas at π/2 degrees it would be pointing at \"6 o'clock\".\n * @see https://developer.mozilla.org/docs/Web/API/PointerEvent/azimuthAngle)\n */\n public azimuthAngle: number;\n\n /**\n * The height of the pointer's contact along the y-axis, measured in CSS pixels.\n * radiusY of TouchEvents will be represented by this value.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height\n */\n public height = 0;\n\n /**\n * Indicates whether or not the pointer device that created the event is the primary pointer.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary\n */\n public isPrimary = false;\n\n /**\n * The type of pointer that triggered the event.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType\n */\n public pointerType: string;\n\n /**\n * Pressure applied by the pointing device during the event.\n *s\n * A Touch's force property will be represented by this value.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure\n */\n public pressure: number;\n\n /**\n * Barrel pressure on a stylus pointer.\n * @see https://w3c.github.io/pointerevents/#pointerevent-interface\n */\n public tangentialPressure: number;\n\n /**\n * The angle, in degrees, between the pointer device and the screen.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX\n */\n public tiltX: number;\n\n /**\n * The angle, in degrees, between the pointer device and the screen.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY\n */\n public tiltY: number;\n\n /**\n * Twist of a stylus pointer.\n * @see https://w3c.github.io/pointerevents/#pointerevent-interface\n */\n public twist: number;\n\n /** This is the number of clicks that occurs in 200ms/click of each other. */\n public detail: number;\n\n /**\n * Only included for completeness for now\n * @ignore\n */\n public getCoalescedEvents(): PointerEvent[]\n {\n if (this.type === 'pointermove' || this.type === 'mousemove' || this.type === 'touchmove')\n {\n return [this];\n }\n\n return [];\n }\n\n /**\n * Only included for completeness for now\n * @ignore\n */\n public getPredictedEvents(): PointerEvent[]\n {\n throw new Error('getPredictedEvents is not supported!');\n }\n}\n","import { FederatedMouseEvent } from './FederatedMouseEvent';\n\n/**\n * A specialized event class for wheel/scroll interactions in PixiJS applications.\n * Extends {@link FederatedMouseEvent} to provide wheel-specific properties while\n * maintaining compatibility with the DOM WheelEvent interface.\n *\n * Key features:\n * - Provides scroll delta information\n * - Supports different scroll modes (pixel, line, page)\n * - Inherits mouse event properties\n * - Normalizes cross-browser wheel events\n * @example\n * ```ts\n * // Basic wheel event handling\n * sprite.on('wheel', (event: FederatedWheelEvent) => {\n * // Get scroll amount\n * console.log('Vertical scroll:', event.deltaY);\n * console.log('Horizontal scroll:', event.deltaX);\n *\n * // Check scroll mode\n * if (event.deltaMode === FederatedWheelEvent.DOM_DELTA_LINE) {\n * console.log('Scrolling by lines');\n * } else if (event.deltaMode === FederatedWheelEvent.DOM_DELTA_PAGE) {\n * console.log('Scrolling by pages');\n * } else {\n * console.log('Scrolling by pixels');\n * }\n *\n * // Get scroll position\n * console.log('Scroll at:', event.global.x, event.global.y);\n * });\n *\n * // Common use case: Zoom control\n * container.on('wheel', (event: FederatedWheelEvent) => {\n * // Prevent page scrolling\n * event.preventDefault();\n *\n * // Zoom in/out based on scroll direction\n * const zoomFactor = 1 + (event.deltaY / 1000);\n * container.scale.set(container.scale.x * zoomFactor);\n * });\n * ```\n * @see {@link FederatedMouseEvent} For base mouse event functionality\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent} DOM WheelEvent Interface\n * @see {@link EventSystem} For the event management system\n * @category events\n * @standard\n */\nexport class FederatedWheelEvent extends FederatedMouseEvent implements WheelEvent\n{\n /**\n * The units of `deltaX`, `deltaY`, and `deltaZ`. This is one of `DOM_DELTA_LINE`,\n * `DOM_DELTA_PAGE`, `DOM_DELTA_PIXEL`.\n */\n public deltaMode: number;\n\n /** Horizontal scroll amount */\n public deltaX: number;\n\n /** Vertical scroll amount */\n public deltaY: number;\n\n /** z-axis scroll amount. */\n public deltaZ: number;\n\n /**\n * Units specified in pixels.\n * @ignore\n */\n public static readonly DOM_DELTA_PIXEL = 0;\n\n /**\n * Units specified in pixels.\n * @ignore\n */\n public readonly DOM_DELTA_PIXEL = 0;\n\n /**\n * Units specified in lines.\n * @ignore\n */\n public static readonly DOM_DELTA_LINE = 1;\n\n /**\n * Units specified in lines.\n * @ignore\n */\n public readonly DOM_DELTA_LINE = 1;\n\n /**\n * Units specified in pages.\n * @ignore\n */\n public static readonly DOM_DELTA_PAGE = 2;\n\n /**\n * Units specified in pages.\n * @ignore\n */\n public readonly DOM_DELTA_PAGE = 2;\n}\n","import EventEmitter from 'eventemitter3';\nimport { Point } from '../maths/point/Point';\nimport { warn } from '../utils/logging/warn';\nimport { EventsTicker } from './EventTicker';\nimport { FederatedMouseEvent } from './FederatedMouseEvent';\nimport { FederatedPointerEvent } from './FederatedPointerEvent';\nimport { FederatedWheelEvent } from './FederatedWheelEvent';\n\nimport type { Renderable } from '../rendering/renderers/shared/Renderable';\nimport type { Container } from '../scene/container/Container';\nimport type { EmitterListeners, TrackingData } from './EventBoundaryTypes';\nimport type { FederatedEvent } from './FederatedEvent';\nimport type {\n Cursor, EventMode, FederatedEventHandler,\n} from './FederatedEventTarget';\n\n// The maximum iterations used in propagation. This prevent infinite loops.\nconst PROPAGATION_LIMIT = 2048;\n\nconst tempHitLocation = new Point();\nconst tempLocalMapping = new Point();\n\n/**\n * Event boundaries are \"barriers\" where events coming from an upstream scene are modified before downstream propagation.\n *\n * ## Root event boundary\n *\n * The {@link EventSystem#rootBoundary rootBoundary} handles events coming from the <canvas />.\n * {@link EventSystem} handles the normalization from native {@link https://dom.spec.whatwg.org/#event Events}\n * into {@link FederatedEvent FederatedEvents}. The rootBoundary then does the hit-testing and event dispatch\n * for the upstream normalized event.\n *\n * ## Additional event boundaries\n *\n * An additional event boundary may be desired within an application's scene graph. For example, if a portion of the scene is\n * is flat with many children at one level - a spatial hash maybe needed to accelerate hit testing. In this scenario, the\n * container can be detached from the scene and glued using a custom event boundary.\n *\n * ```ts\n * import { Container } from 'pixi.js';\n * import { EventBoundary } from 'pixi.js';\n * import { SpatialHash } from 'pixi-spatial-hash';\n *\n * class HashedHitTestingEventBoundary\n * {\n * private spatialHash: SpatialHash;\n *\n * constructor(scene: Container, spatialHash: SpatialHash)\n * {\n * super(scene);\n * this.spatialHash = spatialHash;\n * }\n *\n * hitTestRecursive(...)\n * {\n * // TODO: If target === this.rootTarget, then use spatial hash to get a\n * // list of possible children that match the given (x,y) coordinates.\n * }\n * }\n *\n * class VastScene extends Container\n * {\n * protected eventBoundary: EventBoundary;\n * protected scene: Container;\n * protected spatialHash: SpatialHash;\n *\n * constructor()\n * {\n * this.scene = new Container();\n * this.spatialHash = new SpatialHash();\n * this.eventBoundary = new HashedHitTestingEventBoundary(this.scene, this.spatialHash);\n *\n * // Populate this.scene with a ton of children, while updating this.spatialHash\n * }\n * }\n * ```\n * @category events\n * @advanced\n */\nexport class EventBoundary\n{\n /**\n * The root event-target residing below the event boundary.\n * All events are dispatched trickling down and bubbling up to this `rootTarget`.\n */\n public rootTarget: Container;\n\n /**\n * Emits events after they were dispatched into the scene graph.\n *\n * This can be used for global events listening, regardless of the scene graph being used. It should\n * not be used by interactive libraries for normal use.\n *\n * Special events that do not bubble all the way to the root target are not emitted from here,\n * e.g. pointerenter, pointerleave, click.\n */\n public dispatch: EventEmitter = new EventEmitter();\n\n /** The cursor preferred by the event targets underneath this boundary. */\n public cursor: Cursor | (string & {});\n\n /**\n * This flag would emit `pointermove`, `touchmove`, and `mousemove` events on all Containers.\n *\n * The `moveOnAll` semantics mirror those of earlier versions of PixiJS. This was disabled in favor of\n * the Pointer Event API's approach.\n */\n public moveOnAll = false;\n\n /** Enables the global move events. `globalpointermove`, `globaltouchmove`, and `globalmousemove` */\n public enableGlobalMoveEvents = true;\n\n /**\n * Maps event types to forwarding handles for them.\n *\n * {@link EventBoundary EventBoundary} provides mapping for \"pointerdown\", \"pointermove\",\n * \"pointerout\", \"pointerleave\", \"pointerover\", \"pointerup\", and \"pointerupoutside\" by default.\n * @see EventBoundary#addEventMapping\n */\n protected mappingTable: Record void,\n priority: number\n }>>;\n\n /**\n * State object for mapping methods.\n * @see EventBoundary#trackingData\n */\n protected mappingState: Record = {\n trackingData: {}\n };\n\n /**\n * The event pool maps event constructors to an free pool of instances of those specific events.\n * @see EventBoundary#allocateEvent\n * @see EventBoundary#freeEvent\n */\n protected eventPool: Map = new Map();\n\n /** Every interactive element gathered from the scene. Only used in `pointermove` */\n private readonly _allInteractiveElements: Container[] = [];\n /** Every element that passed the hit test. Only used in `pointermove` */\n private _hitElements: Container[] = [];\n /** Whether or not to collect all the interactive elements from the scene. Enabled in `pointermove` */\n private _isPointerMoveEvent = false;\n\n /**\n * @param rootTarget - The holder of the event boundary.\n */\n constructor(rootTarget?: Container)\n {\n this.rootTarget = rootTarget;\n\n this.hitPruneFn = this.hitPruneFn.bind(this);\n this.hitTestFn = this.hitTestFn.bind(this);\n this.mapPointerDown = this.mapPointerDown.bind(this);\n this.mapPointerMove = this.mapPointerMove.bind(this);\n this.mapPointerOut = this.mapPointerOut.bind(this);\n this.mapPointerOver = this.mapPointerOver.bind(this);\n this.mapPointerUp = this.mapPointerUp.bind(this);\n this.mapPointerUpOutside = this.mapPointerUpOutside.bind(this);\n this.mapWheel = this.mapWheel.bind(this);\n\n this.mappingTable = {};\n this.addEventMapping('pointerdown', this.mapPointerDown);\n this.addEventMapping('pointermove', this.mapPointerMove);\n this.addEventMapping('pointerout', this.mapPointerOut);\n this.addEventMapping('pointerleave', this.mapPointerOut);\n this.addEventMapping('pointerover', this.mapPointerOver);\n this.addEventMapping('pointerup', this.mapPointerUp);\n this.addEventMapping('pointerupoutside', this.mapPointerUpOutside);\n this.addEventMapping('wheel', this.mapWheel);\n }\n\n /**\n * Adds an event mapping for the event `type` handled by `fn`.\n *\n * Event mappings can be used to implement additional or custom events. They take an event\n * coming from the upstream scene (or directly from the {@link EventSystem}) and dispatch new downstream events\n * generally trickling down and bubbling up to {@link EventBoundary.rootTarget this.rootTarget}.\n *\n * To modify the semantics of existing events, the built-in mapping methods of EventBoundary should be overridden\n * instead.\n * @param type - The type of upstream event to map.\n * @param fn - The mapping method. The context of this function must be bound manually, if desired.\n */\n public addEventMapping(type: string, fn: (e: FederatedEvent) => void): void\n {\n if (!this.mappingTable[type])\n {\n this.mappingTable[type] = [];\n }\n\n this.mappingTable[type].push({\n fn,\n priority: 0,\n });\n this.mappingTable[type].sort((a, b) => a.priority - b.priority);\n }\n\n /**\n * Dispatches the given event\n * @param e - The event to dispatch.\n * @param type - The type of event to dispatch. Defaults to `e.type`.\n */\n public dispatchEvent(e: FederatedEvent, type?: string): void\n {\n e.propagationStopped = false;\n e.propagationImmediatelyStopped = false;\n\n this.propagate(e, type);\n this.dispatch.emit(type || e.type, e);\n }\n\n /**\n * Maps the given upstream event through the event boundary and propagates it downstream.\n * @param e - The event to map.\n */\n public mapEvent(e: FederatedEvent): void\n {\n if (!this.rootTarget)\n {\n return;\n }\n\n const mappers = this.mappingTable[e.type];\n\n if (mappers)\n {\n for (let i = 0, j = mappers.length; i < j; i++)\n {\n mappers[i].fn(e);\n }\n }\n else\n {\n // #if _DEBUG\n warn(`[EventBoundary]: Event mapping not defined for ${e.type}`);\n // #endif\n }\n }\n\n /**\n * Finds the Container that is the target of a event at the given coordinates.\n *\n * The passed (x,y) coordinates are in the world space above this event boundary.\n * @param x - The x coordinate of the event.\n * @param y - The y coordinate of the event.\n */\n public hitTest(\n x: number,\n y: number,\n ): Container\n {\n EventsTicker.pauseUpdate = true;\n // if we are using global move events, we need to hit test the whole scene graph\n const useMove = this._isPointerMoveEvent && this.enableGlobalMoveEvents;\n const fn = useMove ? 'hitTestMoveRecursive' : 'hitTestRecursive';\n const invertedPath = this[fn](\n this.rootTarget,\n this.rootTarget.eventMode,\n tempHitLocation.set(x, y),\n this.hitTestFn,\n this.hitPruneFn,\n );\n\n return invertedPath && invertedPath[0];\n }\n\n /**\n * Propagate the passed event from from {@link EventBoundary.rootTarget this.rootTarget} to its\n * target `e.target`.\n * @param e - The event to propagate.\n * @param type - The type of event to propagate. Defaults to `e.type`.\n */\n public propagate(e: FederatedEvent, type?: string): void\n {\n if (!e.target)\n {\n // This usually occurs when the scene graph is not interactive.\n return;\n }\n\n const composedPath = e.composedPath();\n\n // Capturing phase\n e.eventPhase = e.CAPTURING_PHASE;\n\n for (let i = 0, j = composedPath.length - 1; i < j; i++)\n {\n e.currentTarget = composedPath[i];\n\n this.notifyTarget(e, type);\n\n if (e.propagationStopped || e.propagationImmediatelyStopped) return;\n }\n\n // At target phase\n e.eventPhase = e.AT_TARGET;\n e.currentTarget = e.target;\n\n this.notifyTarget(e, type);\n\n if (e.propagationStopped || e.propagationImmediatelyStopped) return;\n\n // Bubbling phase\n e.eventPhase = e.BUBBLING_PHASE;\n\n for (let i = composedPath.length - 2; i >= 0; i--)\n {\n e.currentTarget = composedPath[i];\n\n this.notifyTarget(e, type);\n\n if (e.propagationStopped || e.propagationImmediatelyStopped) return;\n }\n }\n\n /**\n * Emits the event `e` to all interactive containers. The event is propagated in the bubbling phase always.\n *\n * This is used in the `globalpointermove` event.\n * @param e - The emitted event.\n * @param type - The listeners to notify.\n * @param targets - The targets to notify.\n */\n public all(e: FederatedEvent, type?: string | string[], targets = this._allInteractiveElements): void\n {\n if (targets.length === 0) return;\n\n e.eventPhase = e.BUBBLING_PHASE;\n\n const events = Array.isArray(type) ? type : [type];\n\n // loop through all interactive elements and notify them of the event\n // loop through targets backwards\n for (let i = targets.length - 1; i >= 0; i--)\n {\n events.forEach((event) =>\n {\n e.currentTarget = targets[i];\n this.notifyTarget(e, event);\n });\n }\n }\n\n /**\n * Finds the propagation path from {@link EventBoundary.rootTarget rootTarget} to the passed\n * `target`. The last element in the path is `target`.\n * @param target - The target to find the propagation path to.\n */\n public propagationPath(target: Container): Container[]\n {\n const propagationPath = [target];\n\n for (let i = 0; i < PROPAGATION_LIMIT && (target !== this.rootTarget && target.parent); i++)\n {\n if (!target.parent)\n {\n throw new Error('Cannot find propagation path to disconnected target');\n }\n\n propagationPath.push(target.parent);\n\n target = target.parent;\n }\n\n propagationPath.reverse();\n\n return propagationPath;\n }\n\n protected hitTestMoveRecursive(\n currentTarget: Container,\n eventMode: EventMode,\n location: Point,\n testFn: (object: Container, pt: Point) => boolean,\n pruneFn: (object: Container, pt: Point) => boolean,\n ignore = false\n ): Container[]\n {\n let shouldReturn = false;\n\n // only bail out early if it is not interactive\n if (this._interactivePrune(currentTarget)) return null;\n\n if (currentTarget.eventMode === 'dynamic' || eventMode === 'dynamic')\n {\n EventsTicker.pauseUpdate = false;\n }\n\n if (currentTarget.interactiveChildren && currentTarget.children)\n {\n const children = currentTarget.children;\n\n for (let i = children.length - 1; i >= 0; i--)\n {\n const child = children[i] as Container;\n\n const nestedHit = this.hitTestMoveRecursive(\n child,\n this._isInteractive(eventMode) ? eventMode : child.eventMode,\n location,\n testFn,\n pruneFn,\n ignore || pruneFn(currentTarget, location)\n );\n\n if (nestedHit)\n {\n // Its a good idea to check if a child has lost its parent.\n // this means it has been removed whilst looping so its best\n if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent)\n {\n continue;\n }\n\n // Only add the current hit-test target to the hit-test chain if the chain\n // has already started (i.e. the event target has been found) or if the current\n // target is interactive (i.e. it becomes the event target).\n const isInteractive = currentTarget.isInteractive();\n\n if (nestedHit.length > 0 || isInteractive)\n {\n if (isInteractive) this._allInteractiveElements.push(currentTarget);\n nestedHit.push(currentTarget);\n }\n\n // store all hit elements to be returned once we have traversed the whole tree\n if (this._hitElements.length === 0) this._hitElements = nestedHit;\n\n shouldReturn = true;\n }\n }\n }\n\n const isInteractiveMode = this._isInteractive(eventMode);\n const isInteractiveTarget = currentTarget.isInteractive();\n\n if (isInteractiveTarget && isInteractiveTarget) this._allInteractiveElements.push(currentTarget);\n\n // we don't carry on hit testing something once we have found a hit,\n // now only care about gathering the interactive elements\n if (ignore || this._hitElements.length > 0) return null;\n\n if (shouldReturn) return this._hitElements as Container[];\n\n // Finally, hit test this Container itself.\n if (isInteractiveMode && (!pruneFn(currentTarget, location) && testFn(currentTarget, location)))\n {\n // The current hit-test target is the event's target only if it is interactive. Otherwise,\n // the first interactive ancestor will be the event's target.\n return isInteractiveTarget ? [currentTarget] : [];\n }\n\n return null;\n }\n\n /**\n * Recursive implementation for {@link EventBoundary.hitTest hitTest}.\n * @param currentTarget - The Container that is to be hit tested.\n * @param eventMode - The event mode for the `currentTarget` or one of its parents.\n * @param location - The location that is being tested for overlap.\n * @param testFn - Callback that determines whether the target passes hit testing. This callback\n * can assume that `pruneFn` failed to prune the container.\n * @param pruneFn - Callback that determiness whether the target and all of its children\n * cannot pass the hit test. It is used as a preliminary optimization to prune entire subtrees\n * of the scene graph.\n * @returns An array holding the hit testing target and all its ancestors in order. The first element\n * is the target itself and the last is {@link EventBoundary.rootTarget rootTarget}. This is the opposite\n * order w.r.t. the propagation path. If no hit testing target is found, null is returned.\n */\n protected hitTestRecursive(\n currentTarget: Container,\n eventMode: EventMode,\n location: Point,\n testFn: (object: Container, pt: Point) => boolean,\n pruneFn: (object: Container, pt: Point) => boolean\n ): Container[]\n {\n // Attempt to prune this Container and its subtree as an optimization.\n if (this._interactivePrune(currentTarget) || pruneFn(currentTarget, location))\n {\n return null;\n }\n if (currentTarget.eventMode === 'dynamic' || eventMode === 'dynamic')\n {\n EventsTicker.pauseUpdate = false;\n }\n\n // Find a child that passes the hit testing and return one, if any.\n if (currentTarget.interactiveChildren && currentTarget.children)\n {\n const children = currentTarget.children;\n const relativeLocation = location;\n\n for (let i = children.length - 1; i >= 0; i--)\n {\n const child = children[i] as Container;\n\n const nestedHit = this.hitTestRecursive(\n child,\n this._isInteractive(eventMode) ? eventMode : child.eventMode,\n relativeLocation,\n testFn,\n pruneFn\n );\n\n if (nestedHit)\n {\n // Its a good idea to check if a child has lost its parent.\n // this means it has been removed whilst looping so its best\n if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent)\n {\n continue;\n }\n\n // Only add the current hit-test target to the hit-test chain if the chain\n // has already started (i.e. the event target has been found) or if the current\n // target is interactive (i.e. it becomes the event target).\n const isInteractive = currentTarget.isInteractive();\n\n if (nestedHit.length > 0 || isInteractive) nestedHit.push(currentTarget);\n\n return nestedHit;\n }\n }\n }\n\n const isInteractiveMode = this._isInteractive(eventMode);\n const isInteractiveTarget = currentTarget.isInteractive();\n\n // Finally, hit test this Container itself.\n if (isInteractiveMode && testFn(currentTarget, location))\n {\n // The current hit-test target is the event's target only if it is interactive. Otherwise,\n // the first interactive ancestor will be the event's target.\n return isInteractiveTarget ? [currentTarget] : [];\n }\n\n return null;\n }\n\n private _isInteractive(int: EventMode): int is 'static' | 'dynamic'\n {\n return int === 'static' || int === 'dynamic';\n }\n\n private _interactivePrune(container: Container): boolean\n {\n // If container is a mask, invisible, or not renderable then it cannot be hit directly.\n if (!container || !container.visible || !container.renderable || !container.measurable)\n {\n return true;\n }\n\n // If this Container is none then it cannot be hit by anything.\n if (container.eventMode === 'none')\n {\n return true;\n }\n\n // If this Container is passive and it has no interactive children then it cannot be hit\n if (container.eventMode === 'passive' && !container.interactiveChildren)\n {\n return true;\n }\n\n return false;\n }\n\n /**\n * Checks whether the container or any of its children cannot pass the hit test at all.\n *\n * {@link EventBoundary}'s implementation uses the {@link Container.hitArea hitArea}\n * and {@link Container._maskEffect} for pruning.\n * @param container - The container to prune.\n * @param location - The location to test for overlap.\n */\n protected hitPruneFn(container: Container, location: Point): boolean\n {\n if (container.hitArea)\n {\n container.worldTransform.applyInverse(location, tempLocalMapping);\n\n if (!container.hitArea.contains(tempLocalMapping.x, tempLocalMapping.y))\n {\n return true;\n }\n }\n\n if (container.effects && container.effects.length)\n {\n for (let i = 0; i < container.effects.length; i++)\n {\n const effect = container.effects[i];\n\n if (effect.containsPoint)\n {\n const effectContainsPoint = effect.containsPoint(location, this.hitTestFn);\n\n if (!effectContainsPoint)\n {\n return true;\n }\n }\n }\n }\n\n return false;\n }\n\n /**\n * Checks whether the container passes hit testing for the given location.\n * @param container - The container to test.\n * @param location - The location to test for overlap.\n * @returns - Whether `container` passes hit testing for `location`.\n */\n protected hitTestFn(container: Container, location: Point): boolean\n {\n // If the container failed pruning with a hitArea, then it must pass it.\n if (container.hitArea)\n {\n return true;\n }\n\n if ((container as Renderable)?.containsPoint)\n {\n container.worldTransform.applyInverse(location, tempLocalMapping);\n\n return (container as Renderable).containsPoint(tempLocalMapping) as boolean;\n }\n\n // TODO: Should we hit test based on bounds?\n\n return false;\n }\n\n /**\n * Notify all the listeners to the event's `currentTarget`.\n *\n * If the `currentTarget` contains the property `on`, then it is called here,\n * simulating the behavior from version 6.x and prior.\n * @param e - The event passed to the target.\n * @param type - The type of event to notify. Defaults to `e.type`.\n */\n protected notifyTarget(e: FederatedEvent, type?: string): void\n {\n if (!e.currentTarget.isInteractive())\n {\n return;\n }\n\n type ??= e.type;\n\n // call the `on${type}` for the current target if it exists\n const handlerKey = `on${type}` as keyof Container;\n\n (e.currentTarget[handlerKey] as FederatedEventHandler)?.(e);\n\n const key = e.eventPhase === e.CAPTURING_PHASE || e.eventPhase === e.AT_TARGET ? `${type}capture` : type;\n\n this._notifyListeners(e, key);\n\n if (e.eventPhase === e.AT_TARGET)\n {\n this._notifyListeners(e, type);\n }\n }\n\n /**\n * Maps the upstream `pointerdown` events to a downstream `pointerdown` event.\n *\n * `touchstart`, `rightdown`, `mousedown` events are also dispatched for specific pointer types.\n * @param from - The upstream `pointerdown` event.\n */\n protected mapPointerDown(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n const e = this.createPointerEvent(from);\n\n this.dispatchEvent(e, 'pointerdown');\n\n if (e.pointerType === 'touch')\n {\n this.dispatchEvent(e, 'touchstart');\n }\n else if (e.pointerType === 'mouse' || e.pointerType === 'pen')\n {\n const isRightButton = e.button === 2;\n\n this.dispatchEvent(e, isRightButton ? 'rightdown' : 'mousedown');\n }\n\n const trackingData = this.trackingData(from.pointerId);\n\n trackingData.pressTargetsByButton[from.button] = e.composedPath();\n\n this.freeEvent(e);\n }\n\n /**\n * Maps the upstream `pointermove` to downstream `pointerout`, `pointerover`, and `pointermove` events, in that order.\n *\n * The tracking data for the specific pointer has an updated `overTarget`. `mouseout`, `mouseover`,\n * `mousemove`, and `touchmove` events are fired as well for specific pointer types.\n * @param from - The upstream `pointermove` event.\n */\n protected mapPointerMove(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n this._allInteractiveElements.length = 0;\n this._hitElements.length = 0;\n this._isPointerMoveEvent = true;\n const e = this.createPointerEvent(from);\n\n this._isPointerMoveEvent = false;\n const isMouse = e.pointerType === 'mouse' || e.pointerType === 'pen';\n const trackingData = this.trackingData(from.pointerId);\n const outTarget = this.findMountedTarget(trackingData.overTargets);\n\n // First pointerout/pointerleave\n if (trackingData.overTargets?.length > 0 && outTarget !== e.target)\n {\n // pointerout always occurs on the overTarget when the pointer hovers over another element.\n const outType = from.type === 'mousemove' ? 'mouseout' : 'pointerout';\n const outEvent = this.createPointerEvent(from, outType, outTarget);\n\n this.dispatchEvent(outEvent, 'pointerout');\n if (isMouse) this.dispatchEvent(outEvent, 'mouseout');\n\n // If the pointer exits overTarget and its descendants, then a pointerleave event is also fired. This event\n // is dispatched to all ancestors that no longer capture the pointer.\n if (!e.composedPath().includes(outTarget))\n {\n const leaveEvent = this.createPointerEvent(from, 'pointerleave', outTarget);\n\n leaveEvent.eventPhase = leaveEvent.AT_TARGET;\n\n while (leaveEvent.target && !e.composedPath().includes(leaveEvent.target))\n {\n leaveEvent.currentTarget = leaveEvent.target;\n\n this.notifyTarget(leaveEvent);\n if (isMouse) this.notifyTarget(leaveEvent, 'mouseleave');\n\n leaveEvent.target = leaveEvent.target.parent;\n }\n\n this.freeEvent(leaveEvent);\n }\n\n this.freeEvent(outEvent);\n }\n\n // Then pointerover\n if (outTarget !== e.target)\n {\n // pointerover always occurs on the new overTarget\n const overType = from.type === 'mousemove' ? 'mouseover' : 'pointerover';\n const overEvent = this.clonePointerEvent(e, overType);// clone faster\n\n this.dispatchEvent(overEvent, 'pointerover');\n if (isMouse) this.dispatchEvent(overEvent, 'mouseover');\n\n // Probe whether the newly hovered Container is an ancestor of the original overTarget.\n let overTargetAncestor = outTarget?.parent;\n\n while (overTargetAncestor && overTargetAncestor !== this.rootTarget.parent)\n {\n if (overTargetAncestor === e.target) break;\n\n overTargetAncestor = overTargetAncestor.parent;\n }\n\n // The pointer has entered a non-ancestor of the original overTarget. This means we need a pointerentered\n // event.\n const didPointerEnter = !overTargetAncestor || overTargetAncestor === this.rootTarget.parent;\n\n if (didPointerEnter)\n {\n const enterEvent = this.clonePointerEvent(e, 'pointerenter');\n\n enterEvent.eventPhase = enterEvent.AT_TARGET;\n\n while (enterEvent.target\n && enterEvent.target !== outTarget\n && enterEvent.target !== this.rootTarget.parent)\n {\n enterEvent.currentTarget = enterEvent.target;\n\n this.notifyTarget(enterEvent);\n if (isMouse) this.notifyTarget(enterEvent, 'mouseenter');\n\n enterEvent.target = enterEvent.target.parent;\n }\n\n this.freeEvent(enterEvent);\n }\n\n this.freeEvent(overEvent);\n }\n\n const allMethods: string[] = [];\n const allowGlobalPointerEvents = this.enableGlobalMoveEvents ?? true;\n\n this.moveOnAll ? allMethods.push('pointermove') : this.dispatchEvent(e, 'pointermove');\n allowGlobalPointerEvents && allMethods.push('globalpointermove');\n\n // Then pointermove\n if (e.pointerType === 'touch')\n {\n this.moveOnAll ? allMethods.splice(1, 0, 'touchmove') : this.dispatchEvent(e, 'touchmove');\n allowGlobalPointerEvents && allMethods.push('globaltouchmove');\n }\n\n if (isMouse)\n {\n this.moveOnAll ? allMethods.splice(1, 0, 'mousemove') : this.dispatchEvent(e, 'mousemove');\n allowGlobalPointerEvents && allMethods.push('globalmousemove');\n this.cursor = e.target?.cursor;\n }\n\n if (allMethods.length > 0)\n {\n this.all(e, allMethods);\n }\n this._allInteractiveElements.length = 0;\n this._hitElements.length = 0;\n\n trackingData.overTargets = e.composedPath();\n\n this.freeEvent(e);\n }\n\n /**\n * Maps the upstream `pointerover` to downstream `pointerover` and `pointerenter` events, in that order.\n *\n * The tracking data for the specific pointer gets a new `overTarget`.\n * @param from - The upstream `pointerover` event.\n */\n protected mapPointerOver(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n const trackingData = this.trackingData(from.pointerId);\n const e = this.createPointerEvent(from);\n const isMouse = e.pointerType === 'mouse' || e.pointerType === 'pen';\n\n this.dispatchEvent(e, 'pointerover');\n if (isMouse) this.dispatchEvent(e, 'mouseover');\n if (e.pointerType === 'mouse') this.cursor = e.target?.cursor;\n\n // pointerenter events must be fired since the pointer entered from upstream.\n const enterEvent = this.clonePointerEvent(e, 'pointerenter');\n\n enterEvent.eventPhase = enterEvent.AT_TARGET;\n\n while (enterEvent.target && enterEvent.target !== this.rootTarget.parent)\n {\n enterEvent.currentTarget = enterEvent.target;\n\n this.notifyTarget(enterEvent);\n if (isMouse) this.notifyTarget(enterEvent, 'mouseenter');\n\n enterEvent.target = enterEvent.target.parent;\n }\n\n trackingData.overTargets = e.composedPath();\n\n this.freeEvent(e);\n this.freeEvent(enterEvent);\n }\n\n /**\n * Maps the upstream `pointerout` to downstream `pointerout`, `pointerleave` events, in that order.\n *\n * The tracking data for the specific pointer is cleared of a `overTarget`.\n * @param from - The upstream `pointerout` event.\n */\n protected mapPointerOut(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n const trackingData = this.trackingData(from.pointerId);\n\n if (trackingData.overTargets)\n {\n const isMouse = from.pointerType === 'mouse' || from.pointerType === 'pen';\n const outTarget = this.findMountedTarget(trackingData.overTargets);\n\n // pointerout first\n const outEvent = this.createPointerEvent(from, 'pointerout', outTarget);\n\n this.dispatchEvent(outEvent);\n if (isMouse) this.dispatchEvent(outEvent, 'mouseout');\n\n // pointerleave(s) are also dispatched b/c the pointer must've left rootTarget and its descendants to\n // get an upstream pointerout event (upstream events do not know rootTarget has descendants).\n const leaveEvent = this.createPointerEvent(from, 'pointerleave', outTarget);\n\n leaveEvent.eventPhase = leaveEvent.AT_TARGET;\n\n while (leaveEvent.target && leaveEvent.target !== this.rootTarget.parent)\n {\n leaveEvent.currentTarget = leaveEvent.target;\n\n this.notifyTarget(leaveEvent);\n if (isMouse) this.notifyTarget(leaveEvent, 'mouseleave');\n\n leaveEvent.target = leaveEvent.target.parent;\n }\n\n trackingData.overTargets = null;\n\n this.freeEvent(outEvent);\n this.freeEvent(leaveEvent);\n }\n\n this.cursor = null;\n }\n\n /**\n * Maps the upstream `pointerup` event to downstream `pointerup`, `pointerupoutside`,\n * and `click`/`rightclick`/`pointertap` events, in that order.\n *\n * The `pointerupoutside` event bubbles from the original `pointerdown` target to the most specific\n * ancestor of the `pointerdown` and `pointerup` targets, which is also the `click` event's target. `touchend`,\n * `rightup`, `mouseup`, `touchendoutside`, `rightupoutside`, `mouseupoutside`, and `tap` are fired as well for\n * specific pointer types.\n * @param from - The upstream `pointerup` event.\n */\n protected mapPointerUp(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n const now = performance.now();\n const e = this.createPointerEvent(from);\n\n this.dispatchEvent(e, 'pointerup');\n\n if (e.pointerType === 'touch')\n {\n this.dispatchEvent(e, 'touchend');\n }\n else if (e.pointerType === 'mouse' || e.pointerType === 'pen')\n {\n const isRightButton = e.button === 2;\n\n this.dispatchEvent(e, isRightButton ? 'rightup' : 'mouseup');\n }\n\n const trackingData = this.trackingData(from.pointerId);\n const pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]);\n\n let clickTarget = pressTarget;\n\n // pointerupoutside only bubbles. It only bubbles upto the parent that doesn't contain\n // the pointerup location.\n if (pressTarget && !e.composedPath().includes(pressTarget))\n {\n let currentTarget = pressTarget;\n\n while (currentTarget && !e.composedPath().includes(currentTarget))\n {\n e.currentTarget = currentTarget;\n\n this.notifyTarget(e, 'pointerupoutside');\n\n if (e.pointerType === 'touch')\n {\n this.notifyTarget(e, 'touchendoutside');\n }\n else if (e.pointerType === 'mouse' || e.pointerType === 'pen')\n {\n const isRightButton = e.button === 2;\n\n this.notifyTarget(e, isRightButton ? 'rightupoutside' : 'mouseupoutside');\n }\n\n currentTarget = currentTarget.parent;\n }\n\n delete trackingData.pressTargetsByButton[from.button];\n\n // currentTarget is the most specific ancestor holding both the pointerdown and pointerup\n // targets. That is - it's our click target!\n clickTarget = currentTarget;\n }\n\n // click!\n if (clickTarget)\n {\n const clickEvent = this.clonePointerEvent(e, 'click');\n\n clickEvent.target = clickTarget;\n clickEvent.path = null;\n\n if (!trackingData.clicksByButton[from.button])\n {\n trackingData.clicksByButton[from.button] = {\n clickCount: 0,\n target: clickEvent.target,\n timeStamp: now,\n };\n }\n\n const clickHistory = trackingData.clicksByButton[from.button];\n\n if (clickHistory.target === clickEvent.target\n && now - clickHistory.timeStamp < 200)\n {\n ++clickHistory.clickCount;\n }\n else\n {\n clickHistory.clickCount = 1;\n }\n\n clickHistory.target = clickEvent.target;\n clickHistory.timeStamp = now;\n\n clickEvent.detail = clickHistory.clickCount;\n\n if (clickEvent.pointerType === 'mouse')\n {\n const isRightButton = clickEvent.button === 2;\n\n this.dispatchEvent(clickEvent, isRightButton ? 'rightclick' : 'click');\n }\n else if (clickEvent.pointerType === 'touch')\n {\n this.dispatchEvent(clickEvent, 'tap');\n }\n\n this.dispatchEvent(clickEvent, 'pointertap');\n\n this.freeEvent(clickEvent);\n }\n\n this.freeEvent(e);\n }\n\n /**\n * Maps the upstream `pointerupoutside` event to a downstream `pointerupoutside` event, bubbling from the original\n * `pointerdown` target to `rootTarget`.\n *\n * (The most specific ancestor of the `pointerdown` event and the `pointerup` event must the\n * `{@link EventBoundary}'s root because the `pointerup` event occurred outside of the boundary.)\n *\n * `touchendoutside`, `mouseupoutside`, and `rightupoutside` events are fired as well for specific pointer\n * types. The tracking data for the specific pointer is cleared of a `pressTarget`.\n * @param from - The upstream `pointerupoutside` event.\n */\n protected mapPointerUpOutside(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-pointer event as a pointer event');\n // #endif\n\n return;\n }\n\n const trackingData = this.trackingData(from.pointerId);\n const pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]);\n const e = this.createPointerEvent(from);\n\n if (pressTarget)\n {\n let currentTarget = pressTarget;\n\n while (currentTarget)\n {\n e.currentTarget = currentTarget;\n\n this.notifyTarget(e, 'pointerupoutside');\n\n if (e.pointerType === 'touch')\n {\n this.notifyTarget(e, 'touchendoutside');\n }\n else if (e.pointerType === 'mouse' || e.pointerType === 'pen')\n {\n this.notifyTarget(e, e.button === 2 ? 'rightupoutside' : 'mouseupoutside');\n }\n\n currentTarget = currentTarget.parent;\n }\n\n delete trackingData.pressTargetsByButton[from.button];\n }\n\n this.freeEvent(e);\n }\n\n /**\n * Maps the upstream `wheel` event to a downstream `wheel` event.\n * @param from - The upstream `wheel` event.\n */\n protected mapWheel(from: FederatedEvent): void\n {\n if (!(from instanceof FederatedWheelEvent))\n {\n // #if _DEBUG\n warn('EventBoundary cannot map a non-wheel event as a wheel event');\n // #endif\n\n return;\n }\n\n const wheelEvent = this.createWheelEvent(from);\n\n this.dispatchEvent(wheelEvent);\n this.freeEvent(wheelEvent);\n }\n\n /**\n * Finds the most specific event-target in the given propagation path that is still mounted in the scene graph.\n *\n * This is used to find the correct `pointerup` and `pointerout` target in the case that the original `pointerdown`\n * or `pointerover` target was unmounted from the scene graph.\n * @param propagationPath - The propagation path was valid in the past.\n * @returns - The most specific event-target still mounted at the same location in the scene graph.\n */\n protected findMountedTarget(propagationPath: Container[]): Container\n {\n if (!propagationPath)\n {\n return null;\n }\n\n let currentTarget = propagationPath[0];\n\n for (let i = 1; i < propagationPath.length; i++)\n {\n // Set currentTarget to the next target in the path only if it is still attached to the\n // scene graph (i.e. parent still points to the expected ancestor).\n if (propagationPath[i].parent === currentTarget)\n {\n currentTarget = propagationPath[i];\n }\n else\n {\n break;\n }\n }\n\n return currentTarget;\n }\n\n /**\n * Creates an event whose `originalEvent` is `from`, with an optional `type` and `target` override.\n *\n * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.\n * @param from - The `originalEvent` for the returned event.\n * @param [type=from.type] - The type of the returned event.\n * @param target - The target of the returned event.\n */\n protected createPointerEvent(\n from: FederatedPointerEvent,\n type?: string,\n target?: Container\n ): FederatedPointerEvent\n {\n const event = this.allocateEvent(FederatedPointerEvent);\n\n this.copyPointerData(from, event);\n this.copyMouseData(from, event);\n this.copyData(from, event);\n\n event.nativeEvent = from.nativeEvent;\n event.originalEvent = from;\n event.target = target\n ?? this.hitTest(event.global.x, event.global.y) as Container\n ?? this._hitElements[0];\n\n if (typeof type === 'string')\n {\n event.type = type;\n }\n\n return event;\n }\n\n /**\n * Creates a wheel event whose `originalEvent` is `from`.\n *\n * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.\n * @param from - The upstream wheel event.\n */\n protected createWheelEvent(from: FederatedWheelEvent): FederatedWheelEvent\n {\n const event = this.allocateEvent(FederatedWheelEvent);\n\n this.copyWheelData(from, event);\n this.copyMouseData(from, event);\n this.copyData(from, event);\n\n event.nativeEvent = from.nativeEvent;\n event.originalEvent = from;\n event.target = this.hitTest(event.global.x, event.global.y);\n\n return event;\n }\n\n /**\n * Clones the event `from`, with an optional `type` override.\n *\n * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.\n * @param from - The event to clone.\n * @param [type=from.type] - The type of the returned event.\n */\n protected clonePointerEvent(from: FederatedPointerEvent, type?: string): FederatedPointerEvent\n {\n const event = this.allocateEvent(FederatedPointerEvent);\n\n event.nativeEvent = from.nativeEvent;\n event.originalEvent = from.originalEvent;\n\n this.copyPointerData(from, event);\n this.copyMouseData(from, event);\n this.copyData(from, event);\n\n // copy propagation path for perf\n event.target = from.target;\n event.path = from.composedPath().slice();\n event.type = type ?? event.type;\n\n return event;\n }\n\n /**\n * Copies wheel {@link FederatedWheelEvent} data from `from` into `to`.\n *\n * The following properties are copied:\n * + deltaMode\n * + deltaX\n * + deltaY\n * + deltaZ\n * @param from - The event to copy data from.\n * @param to - The event to copy data into.\n */\n protected copyWheelData(from: FederatedWheelEvent, to: FederatedWheelEvent): void\n {\n to.deltaMode = from.deltaMode;\n to.deltaX = from.deltaX;\n to.deltaY = from.deltaY;\n to.deltaZ = from.deltaZ;\n }\n\n /**\n * Copies pointer {@link FederatedPointerEvent} data from `from` into `to`.\n *\n * The following properties are copied:\n * + pointerId\n * + width\n * + height\n * + isPrimary\n * + pointerType\n * + pressure\n * + tangentialPressure\n * + tiltX\n * + tiltY\n * @param from - The event to copy data from.\n * @param to - The event to copy data into.\n */\n protected copyPointerData(from: FederatedEvent, to: FederatedEvent): void\n {\n if (!(from instanceof FederatedPointerEvent && to instanceof FederatedPointerEvent)) return;\n\n to.pointerId = from.pointerId;\n to.width = from.width;\n to.height = from.height;\n to.isPrimary = from.isPrimary;\n to.pointerType = from.pointerType;\n to.pressure = from.pressure;\n to.tangentialPressure = from.tangentialPressure;\n to.tiltX = from.tiltX;\n to.tiltY = from.tiltY;\n to.twist = from.twist;\n }\n\n /**\n * Copies mouse {@link FederatedMouseEvent} data from `from` to `to`.\n *\n * The following properties are copied:\n * + altKey\n * + button\n * + buttons\n * + clientX\n * + clientY\n * + metaKey\n * + movementX\n * + movementY\n * + pageX\n * + pageY\n * + x\n * + y\n * + screen\n * + shiftKey\n * + global\n * @param from - The event to copy data from.\n * @param to - The event to copy data into.\n */\n protected copyMouseData(from: FederatedEvent, to: FederatedEvent): void\n {\n if (!(from instanceof FederatedMouseEvent && to instanceof FederatedMouseEvent)) return;\n\n to.altKey = from.altKey;\n to.button = from.button;\n to.buttons = from.buttons;\n to.client.copyFrom(from.client);\n to.ctrlKey = from.ctrlKey;\n to.metaKey = from.metaKey;\n to.movement.copyFrom(from.movement);\n to.screen.copyFrom(from.screen);\n to.shiftKey = from.shiftKey;\n to.global.copyFrom(from.global);\n }\n\n /**\n * Copies base {@link FederatedEvent} data from `from` into `to`.\n *\n * The following properties are copied:\n * + isTrusted\n * + srcElement\n * + timeStamp\n * + type\n * @param from - The event to copy data from.\n * @param to - The event to copy data into.\n */\n protected copyData(from: FederatedEvent, to: FederatedEvent): void\n {\n to.isTrusted = from.isTrusted;\n to.srcElement = from.srcElement;\n to.timeStamp = performance.now();\n to.type = from.type;\n to.detail = from.detail;\n to.view = from.view;\n to.which = from.which;\n to.layer.copyFrom(from.layer);\n to.page.copyFrom(from.page);\n }\n\n /**\n * @param id - The pointer ID.\n * @returns The tracking data stored for the given pointer. If no data exists, a blank\n * state will be created.\n */\n protected trackingData(id: number): TrackingData\n {\n if (!this.mappingState.trackingData[id])\n {\n this.mappingState.trackingData[id] = {\n pressTargetsByButton: {},\n clicksByButton: {},\n overTarget: null\n };\n }\n\n return this.mappingState.trackingData[id];\n }\n\n /**\n * Allocate a specific type of event from {@link EventBoundary#eventPool this.eventPool}.\n *\n * This allocation is constructor-agnostic, as long as it only takes one argument - this event\n * boundary.\n * @param constructor - The event's constructor.\n * @returns An event of the given type.\n */\n protected allocateEvent(\n constructor: { new(boundary: EventBoundary): T }\n ): T\n {\n if (!this.eventPool.has(constructor as any))\n {\n this.eventPool.set(constructor as any, []);\n }\n\n const event = this.eventPool.get(constructor as any).pop() as T\n || new constructor(this);\n\n event.eventPhase = event.NONE;\n event.currentTarget = null;\n event.defaultPrevented = false;\n event.path = null;\n event.target = null;\n\n return event;\n }\n\n /**\n * Frees the event and puts it back into the event pool.\n *\n * It is illegal to reuse the event until it is allocated again, using `this.allocateEvent`.\n *\n * It is also advised that events not allocated from {@link EventBoundary#allocateEvent this.allocateEvent}\n * not be freed. This is because of the possibility that the same event is freed twice, which can cause\n * it to be allocated twice & result in overwriting.\n * @param event - The event to be freed.\n * @throws Error if the event is managed by another event boundary.\n */\n protected freeEvent(event: T): void\n {\n if (event.manager !== this) throw new Error('It is illegal to free an event not managed by this EventBoundary!');\n\n const constructor = event.constructor;\n\n if (!this.eventPool.has(constructor as any))\n {\n this.eventPool.set(constructor as any, []);\n }\n\n this.eventPool.get(constructor as any).push(event);\n }\n\n /**\n * Similar to {@link EventEmitter.emit}, except it stops if the `propagationImmediatelyStopped` flag\n * is set on the event.\n * @param e - The event to call each listener with.\n * @param type - The event key.\n */\n private _notifyListeners(e: FederatedEvent, type: string): void\n {\n const listeners = ((e.currentTarget as any)._events as EmitterListeners)[type];\n\n if (!listeners) return;\n\n if ('fn' in listeners)\n {\n if (listeners.once) e.currentTarget.removeListener(type, listeners.fn, undefined, true);\n listeners.fn.call(listeners.context, e);\n }\n else\n {\n for (\n let i = 0, j = listeners.length;\n i < j && !e.propagationImmediatelyStopped;\n i++)\n {\n if (listeners[i].once) e.currentTarget.removeListener(type, listeners[i].fn, undefined, true);\n listeners[i].fn.call(listeners[i].context, e);\n }\n }\n }\n}\n","import { ExtensionType } from '../extensions/Extensions';\nimport { EventBoundary } from './EventBoundary';\nimport { EventsTicker } from './EventTicker';\nimport { FederatedPointerEvent } from './FederatedPointerEvent';\nimport { FederatedWheelEvent } from './FederatedWheelEvent';\n\nimport type { ExtensionMetadata } from '../extensions/Extensions';\nimport type { PointData } from '../maths/point/PointData';\nimport type { System } from '../rendering/renderers/shared/system/System';\nimport type { Renderer } from '../rendering/renderers/types';\nimport type { PixiTouch } from './FederatedEvent';\nimport type { EventMode } from './FederatedEventTarget';\nimport type { FederatedMouseEvent } from './FederatedMouseEvent';\n\nconst MOUSE_POINTER_ID = 1;\nconst TOUCH_TO_POINTER: Record = {\n touchstart: 'pointerdown',\n touchend: 'pointerup',\n touchendoutside: 'pointerupoutside',\n touchmove: 'pointermove',\n touchcancel: 'pointercancel',\n};\n\n/**\n * Options for configuring the PixiJS event system. These options control how the event system\n * handles different types of interactions and event propagation.\n * @example\n * ```ts\n * // Basic event system configuration\n * const app = new Application();\n * await app.init({\n * // Configure default interaction mode\n * eventMode: 'static',\n *\n * // Configure event features\n * eventFeatures: {\n * move: true, // Enable pointer movement events\n * globalMove: false, // Disable global move events\n * click: true, // Enable click events\n * wheel: true // Enable wheel/scroll events\n * }\n * });\n *\n * // Access event system after initialization\n * const eventSystem = app.renderer.events;\n * console.log(eventSystem.features); // Check enabled features\n * ```\n * @see {@link EventSystem} For the main event system implementation\n * @see {@link EventMode} For interaction mode details\n * @see {@link EventSystemFeatures} For all available feature options\n * @advanced\n * @category events\n */\nexport interface EventSystemOptions\n{\n /**\n * The default event mode for all display objects.\n * Controls how objects respond to interaction events.\n *\n * Possible values:\n * - `'none'`: No interaction events\n * - `'passive'`: Only container's children receive events (default)\n * - `'auto'`: Receives events when parent is interactive\n * - `'static'`: Standard interaction events\n * - `'dynamic'`: Like static but with additional synthetic events\n * @default 'passive'\n */\n eventMode?: EventMode;\n\n /**\n * Configuration for enabling/disabling specific event features.\n * Use this to optimize performance by turning off unused functionality.\n * @example\n * ```ts\n * const app = new Application();\n * await app.init({\n * eventFeatures: {\n * // Core interaction events\n * move: true, // Pointer/mouse/touch movement\n * click: true, // Click/tap events\n * wheel: true, // Mouse wheel/scroll events\n * // Global tracking\n * globalMove: false // Global pointer movement\n * }\n * });\n * ```\n */\n eventFeatures?: Partial;\n}\n\n/**\n * The event features that are enabled by the EventSystem. These features control\n * different types of interaction events in your PixiJS application.\n * @example\n * ```ts\n * // Configure features during application initialization\n * const app = new Application();\n * await app.init({\n * eventFeatures: {\n * // Basic interaction events\n * move: true, // Enable pointer movement tracking\n * click: true, // Enable click/tap events\n * wheel: true, // Enable mouse wheel/scroll events\n * // Advanced features\n * globalMove: false // Disable global move tracking for performance\n * }\n * });\n *\n * // Or configure after initialization\n * app.renderer.events.features.move = false; // Disable movement events\n * app.renderer.events.features.globalMove = true; // Enable global tracking\n * ```\n * @since 7.2.0\n * @category events\n * @advanced\n */\nexport interface EventSystemFeatures\n{\n /**\n * Enables pointer events associated with pointer movement.\n *\n * When enabled, these events will fire:\n * - `pointermove` / `mousemove` / `touchmove`\n * - `pointerout` / `mouseout`\n * - `pointerover` / `mouseover`\n * @example\n * ```ts\n * // Enable movement events\n * app.renderer.events.features.move = true;\n *\n * // Listen for movement\n * sprite.on('pointermove', (event) => {\n * console.log('Pointer position:', event.global.x, event.global.y);\n * });\n * ```\n * @default true\n */\n move: boolean;\n\n /**\n * Enables global pointer move events that fire regardless of target.\n *\n * When enabled, these events will fire:\n * - `globalpointermove`\n * - `globalmousemove`\n * - `globaltouchmove`\n * @example\n * ```ts\n * // Enable global tracking\n * app.renderer.events.features.globalMove = true;\n *\n * // Track pointer globally\n * sprite.on('globalpointermove', (event) => {\n * // Fires even when pointer is not over sprite\n * console.log('Global position:', event.global.x, event.global.y);\n * });\n * ```\n * @default true\n */\n globalMove: boolean;\n /**\n * Enables pointer events associated with clicking/tapping.\n *\n * When enabled, these events will fire:\n * - `pointerdown` / `mousedown` / `touchstart` / `rightdown`\n * - `pointerup` / `mouseup` / `touchend` / `rightup`\n * - `pointerupoutside` / `mouseupoutside` / `touchendoutside` / `rightupoutside`\n * - `click` / `tap`\n * @example\n * ```ts\n * // Enable click events\n * app.renderer.events.features.click = true;\n *\n * // Handle clicks\n * sprite.on('click', (event) => {\n * console.log('Clicked at:', event.global.x, event.global.y);\n * });\n * ```\n * @default true\n */\n click: boolean;\n /**\n * Enables mouse wheel/scroll events.\n * @example\n * ```ts\n * // Enable wheel events\n * app.renderer.events.features.wheel = true;\n *\n * // Handle scrolling\n * sprite.on('wheel', (event) => {\n * // Zoom based on scroll direction\n * const scale = 1 + (event.deltaY / 1000);\n * sprite.scale.set(sprite.scale.x * scale);\n * });\n * ```\n * @default true\n */\n wheel: boolean;\n}\n\n/**\n * The system for handling UI events in PixiJS applications. This class manages mouse, touch, and pointer events,\n * normalizing them into a consistent event model.\n * @example\n * ```ts\n * // Access event system through renderer\n * const eventSystem = app.renderer.events;\n *\n * // Configure event features\n * eventSystem.features.globalMove = false; // Disable global move events\n * eventSystem.features.click = true; // Enable click events\n *\n * // Set custom cursor styles\n * eventSystem.cursorStyles.default = 'pointer';\n * eventSystem.cursorStyles.grab = 'grab';\n *\n * // Get current pointer position\n * const pointer = eventSystem.pointer;\n * console.log(pointer.global.x, pointer.global.y);\n * ```\n *\n * Features:\n * - Normalizes browser events into consistent format\n * - Supports mouse, touch, and pointer events\n * - Handles event delegation and bubbling\n * - Provides cursor management\n * - Configurable event features\n * @see {@link EventBoundary} For event propagation and handling\n * @see {@link FederatedEvent} For the base event class\n * @see {@link EventMode} For interaction modes\n * @category events\n * @standard\n */\nexport class EventSystem implements System\n{\n /** @ignore */\n public static extension: ExtensionMetadata = {\n name: 'events',\n type: [\n ExtensionType.WebGLSystem,\n ExtensionType.CanvasSystem,\n ExtensionType.WebGPUSystem,\n ],\n priority: -1,\n };\n\n /**\n * The event features that are enabled by the EventSystem\n * @since 7.2.0\n * @example\n * ```ts\n * import { EventSystem, EventSystemFeatures } from 'pixi.js';\n * // Access the default event features\n * EventSystem.defaultEventFeatures = {\n * // Enable pointer movement events\n * move: true,\n * // Enable global pointer move events\n * globalMove: true,\n * // Enable click events\n * click: true,\n * // Enable wheel events\n * wheel: true,\n * };\n * ```\n */\n public static defaultEventFeatures: EventSystemFeatures = {\n /** Enables pointer events associated with pointer movement. */\n move: true,\n /** Enables global pointer move events. */\n globalMove: true,\n /** Enables pointer events associated with clicking. */\n click: true,\n /** Enables wheel events. */\n wheel: true,\n };\n\n private static _defaultEventMode: EventMode;\n\n /**\n * The default interaction mode for all display objects.\n * @see Container.eventMode\n * @type {EventMode}\n * @readonly\n * @since 7.2.0\n */\n public static get defaultEventMode()\n {\n return this._defaultEventMode;\n }\n\n /**\n * The {@link EventBoundary} for the stage.\n *\n * The {@link EventBoundary#rootTarget rootTarget} of this root boundary is automatically set to\n * the last rendered object before any event processing is initiated. This means the main scene\n * needs to be rendered atleast once before UI events will start propagating.\n *\n * The root boundary should only be changed during initialization. Otherwise, any state held by the\n * event boundary may be lost (like hovered & pressed Containers).\n * @advanced\n */\n public readonly rootBoundary: EventBoundary;\n\n /**\n * Indicates whether the current device supports touch events according to the W3C Touch Events spec.\n * This is used to determine the appropriate event handling strategy.\n * @see {@link https://www.w3.org/TR/touch-events/} W3C Touch Events Specification\n * @readonly\n * @default 'ontouchstart' in globalThis\n */\n public readonly supportsTouchEvents = 'ontouchstart' in globalThis;\n\n /**\n * Indicates whether the current device supports pointer events according to the W3C Pointer Events spec.\n * Used to optimize event handling and provide more consistent cross-device interaction.\n * @see {@link https://www.w3.org/TR/pointerevents/} W3C Pointer Events Specification\n * @readonly\n * @default !!globalThis.PointerEvent\n */\n public readonly supportsPointerEvents = !!globalThis.PointerEvent;\n\n /**\n * Controls whether default browser actions are automatically prevented on pointer events.\n * When true, prevents default browser actions from occurring on pointer events.\n * @remarks\n * - Does not apply to pointer events for backwards compatibility\n * - preventDefault on pointer events stops mouse events from firing\n * - For every pointer event, there will always be either a mouse or touch event alongside it\n * - Setting this to false allows default browser actions (text selection, dragging images, etc.)\n * @example\n * ```ts\n * // Allow default browser actions\n * app.renderer.events.autoPreventDefault = false;\n *\n * // Block default actions (default)\n * app.renderer.events.autoPreventDefault = true;\n *\n * // Example with text selection\n * const text = new Text('Selectable text');\n * text.eventMode = 'static';\n * app.renderer.events.autoPreventDefault = false; // Allow text selection\n * ```\n * @default true\n */\n public autoPreventDefault: boolean;\n\n /**\n * Dictionary of custom cursor styles that can be used across the application.\n * Used to define how different cursor modes are handled when interacting with display objects.\n * @example\n * ```ts\n * // Access event system through renderer\n * const eventSystem = app.renderer.events;\n *\n * // Set string-based cursor styles\n * eventSystem.cursorStyles.default = 'pointer';\n * eventSystem.cursorStyles.hover = 'grab';\n * eventSystem.cursorStyles.drag = 'grabbing';\n *\n * // Use CSS object for complex styling\n * eventSystem.cursorStyles.custom = {\n * cursor: 'url(\"custom.png\") 2 2, auto',\n * userSelect: 'none'\n * };\n *\n * // Use a url for custom cursors\n * const defaultIcon = 'url(\\'https://pixijs.com/assets/bunny.png\\'),auto';\n * eventSystem.cursorStyles.icon = defaultIcon;\n *\n * // Use callback function for dynamic cursors\n * eventSystem.cursorStyles.dynamic = (mode) => {\n * // Update cursor based on mode\n * document.body.style.cursor = mode === 'hover'\n * ? 'pointer'\n * : 'default';\n * };\n *\n * // Apply cursor style to a sprite\n * sprite.cursor = 'hover'; // Will use the hover style defined above\n * sprite.cursor = 'icon'; // Will apply the icon cursor\n * sprite.cursor = 'custom'; // Will apply the custom CSS styles\n * sprite.cursor = 'drag'; // Will apply the grabbing cursor\n * sprite.cursor = 'default'; // Will apply the default pointer cursor\n * sprite.cursor = 'dynamic'; // Will call the dynamic function\n * ```\n * @remarks\n * - Strings are treated as CSS cursor values\n * - Objects are applied as CSS styles to the DOM element\n * - Functions are called directly for custom cursor handling\n * - Default styles for 'default' and 'pointer' are provided\n * @default\n * ```ts\n * {\n * default: 'inherit',\n * pointer: 'pointer' // Default cursor styles\n * }\n * ```\n */\n public cursorStyles: Record void) | CSSStyleDeclaration>;\n\n /**\n * The DOM element to which the root event listeners are bound. This is automatically set to\n * the renderer's {@link Renderer#view view}.\n */\n public domElement: HTMLElement = null;\n\n /** The resolution used to convert between the DOM client space into world space. */\n public resolution = 1;\n\n /** The renderer managing this {@link EventSystem}. */\n public renderer: Renderer;\n\n /**\n * The event features that are enabled by the EventSystem\n * @since 7.2.0\n * @example\n * const app = new Application()\n * app.renderer.events.features.globalMove = false\n *\n * // to override all features use Object.assign\n * Object.assign(app.renderer.events.features, {\n * move: false,\n * globalMove: false,\n * click: false,\n * wheel: false,\n * })\n */\n public readonly features: EventSystemFeatures;\n\n private _currentCursor: string;\n private readonly _rootPointerEvent: FederatedPointerEvent;\n private readonly _rootWheelEvent: FederatedWheelEvent;\n private _eventsAdded: boolean;\n\n /**\n * @param {Renderer} renderer\n */\n constructor(renderer: Renderer)\n {\n this.renderer = renderer;\n this.rootBoundary = new EventBoundary(null);\n EventsTicker.init(this);\n\n this.autoPreventDefault = true;\n this._eventsAdded = false;\n\n this._rootPointerEvent = new FederatedPointerEvent(null);\n this._rootWheelEvent = new FederatedWheelEvent(null);\n\n this.cursorStyles = {\n default: 'inherit',\n pointer: 'pointer',\n };\n\n this.features = new Proxy({ ...EventSystem.defaultEventFeatures }, {\n set: (target, key, value) =>\n {\n if (key === 'globalMove')\n {\n this.rootBoundary.enableGlobalMoveEvents = value;\n }\n target[key as keyof EventSystemFeatures] = value;\n\n return true;\n }\n });\n\n this._onPointerDown = this._onPointerDown.bind(this);\n this._onPointerMove = this._onPointerMove.bind(this);\n this._onPointerUp = this._onPointerUp.bind(this);\n this._onPointerOverOut = this._onPointerOverOut.bind(this);\n this.onWheel = this.onWheel.bind(this);\n }\n\n /**\n * Runner init called, view is available at this point.\n * @ignore\n */\n public init(options: EventSystemOptions): void\n {\n const { canvas, resolution } = this.renderer;\n\n this.setTargetElement(canvas as HTMLCanvasElement);\n this.resolution = resolution;\n EventSystem._defaultEventMode = options.eventMode ?? 'passive';\n Object.assign(this.features, options.eventFeatures ?? {});\n this.rootBoundary.enableGlobalMoveEvents = this.features.globalMove;\n }\n\n /**\n * Handle changing resolution.\n * @ignore\n */\n public resolutionChange(resolution: number): void\n {\n this.resolution = resolution;\n }\n\n /** Destroys all event listeners and detaches the renderer. */\n public destroy(): void\n {\n this.setTargetElement(null);\n this.renderer = null;\n this._currentCursor = null;\n }\n\n /**\n * Sets the current cursor mode, handling any callbacks or CSS style changes.\n * The cursor can be a CSS cursor string, a custom callback function, or a key from the cursorStyles dictionary.\n * @param mode - Cursor mode to set. Can be:\n * - A CSS cursor string (e.g., 'pointer', 'grab')\n * - A key from the cursorStyles dictionary\n * - null/undefined to reset to default\n * @example\n * ```ts\n * // Using predefined cursor styles\n * app.renderer.events.setCursor('pointer'); // Set standard pointer cursor\n * app.renderer.events.setCursor('grab'); // Set grab cursor\n * app.renderer.events.setCursor(null); // Reset to default\n *\n * // Using custom cursor styles\n * app.renderer.events.cursorStyles.custom = 'url(\"cursor.png\"), auto';\n * app.renderer.events.setCursor('custom'); // Apply custom cursor\n *\n * // Using callback-based cursor\n * app.renderer.events.cursorStyles.dynamic = (mode) => {\n * document.body.style.cursor = mode === 'hover' ? 'pointer' : 'default';\n * };\n * app.renderer.events.setCursor('dynamic'); // Trigger cursor callback\n * ```\n * @remarks\n * - Has no effect on OffscreenCanvas except for callback-based cursors\n * - Caches current cursor to avoid unnecessary DOM updates\n * - Supports CSS cursor values, style objects, and callback functions\n * @see {@link EventSystem.cursorStyles} For defining custom cursor styles\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} MDN Cursor Reference\n */\n public setCursor(mode: string): void\n {\n mode ||= 'default';\n let applyStyles = true;\n\n // offscreen canvas does not support setting styles, but cursor modes can be functions,\n // in order to handle pixi rendered cursors, so we can't bail\n if (globalThis.OffscreenCanvas && this.domElement instanceof OffscreenCanvas)\n {\n applyStyles = false;\n }\n // if the mode didn't actually change, bail early\n if (this._currentCursor === mode)\n {\n return;\n }\n this._currentCursor = mode;\n const style = this.cursorStyles[mode];\n\n // only do things if there is a cursor style for it\n if (style)\n {\n switch (typeof style)\n {\n case 'string':\n // string styles are handled as cursor CSS\n if (applyStyles)\n {\n this.domElement.style.cursor = style;\n }\n break;\n case 'function':\n // functions are just called, and passed the cursor mode\n style(mode);\n break;\n case 'object':\n // if it is an object, assume that it is a dictionary of CSS styles,\n // apply it to the interactionDOMElement\n if (applyStyles)\n {\n Object.assign(this.domElement.style, style);\n }\n break;\n }\n }\n else if (applyStyles && typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))\n {\n // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry\n // for the mode, then assume that the dev wants it to be CSS for the cursor.\n this.domElement.style.cursor = mode;\n }\n }\n\n /**\n * The global pointer event instance containing the most recent pointer state.\n * This is useful for accessing pointer information without listening to events.\n * @example\n * ```ts\n * // Access current pointer position at any time\n * const eventSystem = app.renderer.events;\n * const pointer = eventSystem.pointer;\n *\n * // Get global coordinates\n * console.log('Position:', pointer.global.x, pointer.global.y);\n *\n * // Check button state\n * console.log('Buttons pressed:', pointer.buttons);\n *\n * // Get pointer type and pressure\n * console.log('Type:', pointer.pointerType);\n * console.log('Pressure:', pointer.pressure);\n * ```\n * @readonly\n * @since 7.2.0\n * @see {@link FederatedPointerEvent} For all available pointer properties\n */\n public get pointer(): Readonly\n {\n return this._rootPointerEvent;\n }\n\n /**\n * Event handler for pointer down events on {@link EventSystem#domElement this.domElement}.\n * @param nativeEvent - The native mouse/pointer/touch event.\n */\n private _onPointerDown(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void\n {\n if (!this.features.click) return;\n this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;\n\n const events = this._normalizeToPointerData(nativeEvent);\n\n /*\n * No need to prevent default on natural pointer events, as there are no side effects\n * Normalized events, however, may have the double mousedown/touchstart issue on the native android browser,\n * so still need to be prevented.\n */\n\n // Guaranteed that there will be at least one event in events, and all events must have the same pointer type\n\n if (this.autoPreventDefault && (events[0] as any).isNormalized)\n {\n const cancelable = nativeEvent.cancelable || !('cancelable' in nativeEvent);\n\n if (cancelable)\n {\n nativeEvent.preventDefault();\n }\n }\n\n for (let i = 0, j = events.length; i < j; i++)\n {\n const nativeEvent = events[i];\n const federatedEvent = this._bootstrapEvent(this._rootPointerEvent, nativeEvent);\n\n this.rootBoundary.mapEvent(federatedEvent);\n }\n\n this.setCursor(this.rootBoundary.cursor);\n }\n\n /**\n * Event handler for pointer move events on on {@link EventSystem#domElement this.domElement}.\n * @param nativeEvent - The native mouse/pointer/touch events.\n */\n private _onPointerMove(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void\n {\n if (!this.features.move) return;\n this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;\n\n EventsTicker.pointerMoved();\n\n const normalizedEvents = this._normalizeToPointerData(nativeEvent);\n\n for (let i = 0, j = normalizedEvents.length; i < j; i++)\n {\n const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]);\n\n this.rootBoundary.mapEvent(event);\n }\n\n this.setCursor(this.rootBoundary.cursor);\n }\n\n /**\n * Event handler for pointer up events on {@link EventSystem#domElement this.domElement}.\n * @param nativeEvent - The native mouse/pointer/touch event.\n */\n private _onPointerUp(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void\n {\n if (!this.features.click) return;\n this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;\n\n let target = nativeEvent.target;\n\n // if in shadow DOM use composedPath to access target\n if (nativeEvent.composedPath && nativeEvent.composedPath().length > 0)\n {\n target = nativeEvent.composedPath()[0];\n }\n\n const outside = target !== this.domElement ? 'outside' : '';\n const normalizedEvents = this._normalizeToPointerData(nativeEvent);\n\n for (let i = 0, j = normalizedEvents.length; i < j; i++)\n {\n const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]);\n\n event.type += outside;\n\n this.rootBoundary.mapEvent(event);\n }\n\n this.setCursor(this.rootBoundary.cursor);\n }\n\n /**\n * Event handler for pointer over & out events on {@link EventSystem#domElement this.domElement}.\n * @param nativeEvent - The native mouse/pointer/touch event.\n */\n private _onPointerOverOut(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void\n {\n if (!this.features.click) return;\n this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;\n\n const normalizedEvents = this._normalizeToPointerData(nativeEvent);\n\n for (let i = 0, j = normalizedEvents.length; i < j; i++)\n {\n const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]);\n\n this.rootBoundary.mapEvent(event);\n }\n\n this.setCursor(this.rootBoundary.cursor);\n }\n\n /**\n * Passive handler for `wheel` events on {@link EventSystem.domElement this.domElement}.\n * @param nativeEvent - The native wheel event.\n */\n protected onWheel(nativeEvent: WheelEvent): void\n {\n if (!this.features.wheel) return;\n const wheelEvent = this.normalizeWheelEvent(nativeEvent);\n\n this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;\n this.rootBoundary.mapEvent(wheelEvent);\n }\n\n /**\n * Sets the {@link EventSystem#domElement domElement} and binds event listeners.\n * This method manages the DOM event bindings for the event system, allowing you to\n * change or remove the target element that receives input events.\n * > [!IMPORTANT] This will default to the canvas element of the renderer, so you\n * > should not need to call this unless you are using a custom element.\n * @param element - The new DOM element to bind events to, or null to remove all event bindings\n * @example\n * ```ts\n * // Set a new canvas element as the target\n * const canvas = document.createElement('canvas');\n * app.renderer.events.setTargetElement(canvas);\n *\n * // Remove all event bindings\n * app.renderer.events.setTargetElement(null);\n *\n * // Switch to a different canvas\n * const newCanvas = document.querySelector('#game-canvas');\n * app.renderer.events.setTargetElement(newCanvas);\n * ```\n * @remarks\n * - Automatically removes event listeners from previous element\n * - Required for the event system to function\n * - Safe to call multiple times\n * @see {@link EventSystem#domElement} The current DOM element\n * @see {@link EventsTicker} For the ticker system that tracks pointer movement\n */\n public setTargetElement(element: HTMLElement): void\n {\n this._removeEvents();\n this.domElement = element;\n EventsTicker.domElement = element;\n this._addEvents();\n }\n\n /** Register event listeners on {@link Renderer#domElement this.domElement}. */\n private _addEvents(): void\n {\n if (this._eventsAdded || !this.domElement)\n {\n return;\n }\n\n EventsTicker.addTickerListener();\n\n const style = this.domElement.style as CrossCSSStyleDeclaration;\n\n if (style)\n {\n if ((globalThis.navigator as any).msPointerEnabled)\n {\n style.msContentZooming = 'none';\n style.msTouchAction = 'none';\n }\n else if (this.supportsPointerEvents)\n {\n style.touchAction = 'none';\n }\n }\n\n /*\n * These events are added first, so that if pointer events are normalized, they are fired\n * in the same order as non-normalized events. ie. pointer event 1st, mouse / touch 2nd\n */\n if (this.supportsPointerEvents)\n {\n globalThis.document.addEventListener('pointermove', this._onPointerMove, true);\n this.domElement.addEventListener('pointerdown', this._onPointerDown, true);\n // pointerout is fired in addition to pointerup (for touch events) and pointercancel\n // we already handle those, so for the purposes of what we do in onPointerOut, we only\n // care about the pointerleave event\n this.domElement.addEventListener('pointerleave', this._onPointerOverOut, true);\n this.domElement.addEventListener('pointerover', this._onPointerOverOut, true);\n // globalThis.addEventListener('pointercancel', this.onPointerCancel, true);\n globalThis.addEventListener('pointerup', this._onPointerUp, true);\n }\n else\n {\n globalThis.document.addEventListener('mousemove', this._onPointerMove, true);\n this.domElement.addEventListener('mousedown', this._onPointerDown, true);\n this.domElement.addEventListener('mouseout', this._onPointerOverOut, true);\n this.domElement.addEventListener('mouseover', this._onPointerOverOut, true);\n globalThis.addEventListener('mouseup', this._onPointerUp, true);\n\n if (this.supportsTouchEvents)\n {\n this.domElement.addEventListener('touchstart', this._onPointerDown, true);\n // this.domElement.addEventListener('touchcancel', this.onPointerCancel, true);\n this.domElement.addEventListener('touchend', this._onPointerUp, true);\n this.domElement.addEventListener('touchmove', this._onPointerMove, true);\n }\n }\n\n this.domElement.addEventListener('wheel', this.onWheel, {\n passive: true,\n capture: true,\n });\n\n this._eventsAdded = true;\n }\n\n /** Unregister event listeners on {@link EventSystem#domElement this.domElement}. */\n private _removeEvents(): void\n {\n if (!this._eventsAdded || !this.domElement)\n {\n return;\n }\n\n EventsTicker.removeTickerListener();\n\n const style = this.domElement.style as CrossCSSStyleDeclaration;\n\n // offscreen canvas does not have style, so check first\n if (style)\n {\n if ((globalThis.navigator as any).msPointerEnabled)\n {\n style.msContentZooming = '';\n style.msTouchAction = '';\n }\n else if (this.supportsPointerEvents)\n {\n style.touchAction = '';\n }\n }\n\n if (this.supportsPointerEvents)\n {\n globalThis.document.removeEventListener('pointermove', this._onPointerMove, true);\n this.domElement.removeEventListener('pointerdown', this._onPointerDown, true);\n this.domElement.removeEventListener('pointerleave', this._onPointerOverOut, true);\n this.domElement.removeEventListener('pointerover', this._onPointerOverOut, true);\n // globalThis.removeEventListener('pointercancel', this.onPointerCancel, true);\n globalThis.removeEventListener('pointerup', this._onPointerUp, true);\n }\n else\n {\n globalThis.document.removeEventListener('mousemove', this._onPointerMove, true);\n this.domElement.removeEventListener('mousedown', this._onPointerDown, true);\n this.domElement.removeEventListener('mouseout', this._onPointerOverOut, true);\n this.domElement.removeEventListener('mouseover', this._onPointerOverOut, true);\n globalThis.removeEventListener('mouseup', this._onPointerUp, true);\n\n if (this.supportsTouchEvents)\n {\n this.domElement.removeEventListener('touchstart', this._onPointerDown, true);\n // this.domElement.removeEventListener('touchcancel', this.onPointerCancel, true);\n this.domElement.removeEventListener('touchend', this._onPointerUp, true);\n this.domElement.removeEventListener('touchmove', this._onPointerMove, true);\n }\n }\n\n this.domElement.removeEventListener('wheel', this.onWheel, true);\n\n this.domElement = null;\n this._eventsAdded = false;\n }\n\n /**\n * Maps coordinates from DOM/screen space into PixiJS normalized coordinates.\n * This takes into account the current scale, position, and resolution of the DOM element.\n * @param point - The point to store the mapped coordinates in\n * @param x - The x coordinate in DOM/client space\n * @param y - The y coordinate in DOM/client space\n * @example\n * ```ts\n * // Map mouse coordinates to PixiJS space\n * const point = new Point();\n * app.renderer.events.mapPositionToPoint(\n * point,\n * event.clientX,\n * event.clientY\n * );\n * console.log('Mapped position:', point.x, point.y);\n *\n * // Using with pointer events\n * sprite.on('pointermove', (event) => {\n * // event.global already contains mapped coordinates\n * console.log('Global:', event.global.x, event.global.y);\n *\n * // Map to local coordinates\n * const local = event.getLocalPosition(sprite);\n * console.log('Local:', local.x, local.y);\n * });\n * ```\n * @remarks\n * - Accounts for element scaling and positioning\n * - Adjusts for device pixel ratio/resolution\n */\n public mapPositionToPoint(point: PointData, x: number, y: number): void\n {\n const rect = this.domElement.isConnected\n ? this.domElement.getBoundingClientRect()\n : {\n x: 0,\n y: 0,\n width: (this.domElement as any).width,\n height: (this.domElement as any).height,\n left: 0,\n top: 0\n };\n\n const resolutionMultiplier = 1.0 / this.resolution;\n\n point.x = ((x - rect.left) * ((this.domElement as any).width / rect.width)) * resolutionMultiplier;\n point.y = ((y - rect.top) * ((this.domElement as any).height / rect.height)) * resolutionMultiplier;\n }\n\n /**\n * Ensures that the original event object contains all data that a regular pointer event would have\n * @param event - The original event data from a touch or mouse event\n * @returns An array containing a single normalized pointer event, in the case of a pointer\n * or mouse event, or a multiple normalized pointer events if there are multiple changed touches\n */\n private _normalizeToPointerData(event: TouchEvent | MouseEvent | PointerEvent): PointerEvent[]\n {\n const normalizedEvents = [];\n\n if (this.supportsTouchEvents && event instanceof TouchEvent)\n {\n for (let i = 0, li = event.changedTouches.length; i < li; i++)\n {\n const touch = event.changedTouches[i] as PixiTouch;\n\n if (typeof touch.button === 'undefined') touch.button = 0;\n if (typeof touch.buttons === 'undefined') touch.buttons = 1;\n if (typeof touch.isPrimary === 'undefined')\n {\n touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';\n }\n if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;\n if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;\n if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;\n if (typeof touch.tiltY === 'undefined') touch.tiltY = 0;\n if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';\n if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;\n if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;\n if (typeof touch.twist === 'undefined') touch.twist = 0;\n if (typeof touch.tangentialPressure === 'undefined') touch.tangentialPressure = 0;\n // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven\n // support, and the fill ins are not quite the same\n // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top\n // left is not 0,0 on the page\n if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;\n if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;\n\n // mark the touch as normalized, just so that we know we did it\n touch.isNormalized = true;\n touch.type = event.type;\n\n normalizedEvents.push(touch);\n }\n }\n // apparently PointerEvent subclasses MouseEvent, so yay\n else if (!globalThis.MouseEvent\n || (event instanceof MouseEvent && (!this.supportsPointerEvents || !(event instanceof globalThis.PointerEvent))))\n {\n const tempEvent = event as PixiPointerEvent;\n\n if (typeof tempEvent.isPrimary === 'undefined') tempEvent.isPrimary = true;\n if (typeof tempEvent.width === 'undefined') tempEvent.width = 1;\n if (typeof tempEvent.height === 'undefined') tempEvent.height = 1;\n if (typeof tempEvent.tiltX === 'undefined') tempEvent.tiltX = 0;\n if (typeof tempEvent.tiltY === 'undefined') tempEvent.tiltY = 0;\n if (typeof tempEvent.pointerType === 'undefined') tempEvent.pointerType = 'mouse';\n if (typeof tempEvent.pointerId === 'undefined') tempEvent.pointerId = MOUSE_POINTER_ID;\n if (typeof tempEvent.pressure === 'undefined') tempEvent.pressure = 0.5;\n if (typeof tempEvent.twist === 'undefined') tempEvent.twist = 0;\n if (typeof tempEvent.tangentialPressure === 'undefined') tempEvent.tangentialPressure = 0;\n\n // mark the mouse event as normalized, just so that we know we did it\n tempEvent.isNormalized = true;\n\n normalizedEvents.push(tempEvent);\n }\n else\n {\n normalizedEvents.push(event);\n }\n\n return normalizedEvents as PointerEvent[];\n }\n\n /**\n * Normalizes the native {@link https://w3c.github.io/uievents/#interface-wheelevent WheelEvent}.\n *\n * The returned {@link FederatedWheelEvent} is a shared instance. It will not persist across\n * multiple native wheel events.\n * @param nativeEvent - The native wheel event that occurred on the canvas.\n * @returns A federated wheel event.\n */\n protected normalizeWheelEvent(nativeEvent: WheelEvent): FederatedWheelEvent\n {\n const event = this._rootWheelEvent;\n\n this._transferMouseData(event, nativeEvent);\n\n // When WheelEvent is triggered by scrolling with mouse wheel, reading WheelEvent.deltaMode\n // before deltaX/deltaY/deltaZ on Firefox will result in WheelEvent.DOM_DELTA_LINE (1),\n // while reading WheelEvent.deltaMode after deltaX/deltaY/deltaZ on Firefox or reading\n // in any order on other browsers will result in WheelEvent.DOM_DELTA_PIXEL (0).\n // Therefore, we need to read WheelEvent.deltaMode after deltaX/deltaY/deltaZ in order to\n // make its behavior more consistent across browsers.\n // @see https://github.com/pixijs/pixijs/issues/8970\n event.deltaX = nativeEvent.deltaX;\n event.deltaY = nativeEvent.deltaY;\n event.deltaZ = nativeEvent.deltaZ;\n event.deltaMode = nativeEvent.deltaMode;\n\n this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY);\n event.global.copyFrom(event.screen);\n event.offset.copyFrom(event.screen);\n\n event.nativeEvent = nativeEvent;\n event.type = nativeEvent.type;\n\n return event;\n }\n\n /**\n * Normalizes the `nativeEvent` into a federateed {@link FederatedPointerEvent}.\n * @param event\n * @param nativeEvent\n */\n private _bootstrapEvent(event: FederatedPointerEvent, nativeEvent: PointerEvent): FederatedPointerEvent\n {\n event.originalEvent = null;\n event.nativeEvent = nativeEvent;\n\n event.pointerId = nativeEvent.pointerId;\n event.width = nativeEvent.width;\n event.height = nativeEvent.height;\n event.isPrimary = nativeEvent.isPrimary;\n event.pointerType = nativeEvent.pointerType;\n event.pressure = nativeEvent.pressure;\n event.tangentialPressure = nativeEvent.tangentialPressure;\n event.tiltX = nativeEvent.tiltX;\n event.tiltY = nativeEvent.tiltY;\n event.twist = nativeEvent.twist;\n this._transferMouseData(event, nativeEvent);\n\n this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY);\n event.global.copyFrom(event.screen);// global = screen for top-level\n event.offset.copyFrom(event.screen);// EventBoundary recalculates using its rootTarget\n\n event.isTrusted = nativeEvent.isTrusted;\n if (event.type === 'pointerleave')\n {\n event.type = 'pointerout';\n }\n if (event.type.startsWith('mouse'))\n {\n event.type = event.type.replace('mouse', 'pointer');\n }\n if (event.type.startsWith('touch'))\n {\n event.type = TOUCH_TO_POINTER[event.type] || event.type;\n }\n\n return event;\n }\n\n /**\n * Transfers base & mouse event data from the `nativeEvent` to the federated event.\n * @param event\n * @param nativeEvent\n */\n private _transferMouseData(event: FederatedMouseEvent, nativeEvent: MouseEvent): void\n {\n event.isTrusted = nativeEvent.isTrusted;\n event.srcElement = nativeEvent.srcElement;\n event.timeStamp = performance.now();\n event.type = nativeEvent.type;\n\n event.altKey = nativeEvent.altKey;\n event.button = nativeEvent.button;\n event.buttons = nativeEvent.buttons;\n event.client.x = nativeEvent.clientX;\n event.client.y = nativeEvent.clientY;\n event.ctrlKey = nativeEvent.ctrlKey;\n event.metaKey = nativeEvent.metaKey;\n event.movement.x = nativeEvent.movementX;\n event.movement.y = nativeEvent.movementY;\n event.page.x = nativeEvent.pageX;\n event.page.y = nativeEvent.pageY;\n event.relatedTarget = null;\n event.shiftKey = nativeEvent.shiftKey;\n }\n}\n\ninterface CrossCSSStyleDeclaration extends CSSStyleDeclaration\n{\n msContentZooming: string;\n msTouchAction: string;\n}\n\ninterface PixiPointerEvent extends PointerEvent\n{\n isPrimary: boolean;\n width: number;\n height: number;\n tiltX: number;\n tiltY: number;\n pointerType: string;\n pointerId: number;\n pressure: number;\n twist: number;\n tangentialPressure: number;\n isNormalized: boolean;\n type: string;\n}\n","import { EventSystem } from './EventSystem';\nimport { FederatedEvent } from './FederatedEvent';\n\nimport type EventEmitter from 'eventemitter3';\nimport type { Container } from '../scene/container/Container';\nimport type { AllFederatedEventMap } from './FederatedEventMap';\nimport type { FederatedPointerEvent } from './FederatedPointerEvent';\nimport type { FederatedWheelEvent } from './FederatedWheelEvent';\n\n/**\n * The type of cursor to use when the mouse pointer is hovering over an interactive element.\n * Accepts any valid CSS cursor value.\n * @example\n * ```ts\n * // Basic cursor types\n * sprite.cursor = 'pointer'; // Hand cursor for clickable elements\n * sprite.cursor = 'grab'; // Grab cursor for draggable elements\n * sprite.cursor = 'crosshair'; // Precise cursor for selection\n *\n * // Direction cursors\n * sprite.cursor = 'n-resize'; // North resize\n * sprite.cursor = 'ew-resize'; // East-west resize\n * sprite.cursor = 'nesw-resize';// Northeast-southwest resize\n *\n * // Custom cursor with fallback\n * sprite.cursor = 'url(\"custom.png\"), auto';\n * ```\n *\n * Common cursor values:\n * - Basic: `auto`, `default`, `none`, `pointer`, `wait`\n * - Text: `text`, `vertical-text`\n * - Links: `alias`, `copy`, `move`\n * - Selection: `cell`, `crosshair`\n * - Drag: `grab`, `grabbing`\n * - Disabled: `not-allowed`, `no-drop`\n * - Resize: `n-resize`, `e-resize`, `s-resize`, `w-resize`\n * - Bidirectional: `ns-resize`, `ew-resize`, `nesw-resize`, `nwse-resize`\n * - Other: `help`, `progress`\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} MDN Cursor Documentation\n * @category events\n * @standard\n */\nexport type Cursor = 'auto'\n| 'default'\n| 'none'\n| 'context-menu'\n| 'help'\n| 'pointer'\n| 'progress'\n| 'wait'\n| 'cell'\n| 'crosshair'\n| 'text'\n| 'vertical-text'\n| 'alias'\n| 'copy'\n| 'move'\n| 'no-drop'\n| 'not-allowed'\n| 'e-resize'\n| 'n-resize'\n| 'ne-resize'\n| 'nw-resize'\n| 's-resize'\n| 'se-resize'\n| 'sw-resize'\n| 'w-resize'\n| 'ns-resize'\n| 'ew-resize'\n| 'nesw-resize'\n| 'col-resize'\n| 'nwse-resize'\n| 'row-resize'\n| 'all-scroll'\n| 'zoom-in'\n| 'zoom-out'\n| 'grab'\n| 'grabbing';\n\n/**\n * Interface defining a hit area for pointer interaction. The hit area specifies\n * the region in which pointer events should be captured by a display object.\n * @example\n * ```ts\n * // Create a rectangular hit area\n * sprite.hitArea = new Rectangle(0, 0, 100, 100);\n *\n * // Create a circular hit area\n * sprite.hitArea = new Circle(50, 50, 50);\n *\n * // Custom hit area implementation\n * sprite.hitArea = {\n * contains(x: number, y: number) {\n * // Custom hit testing logic\n * return x >= 0 && x <= 100 && y >= 0 && y <= 100;\n * }\n * };\n * ```\n * @remarks\n * - Hit areas override the default bounds-based hit testing\n * - Can improve performance by simplifying hit tests\n * - Useful for irregular shapes or precise interaction areas\n * - Common implementations include Rectangle, Circle, Polygon\n * @see {@link Container.eventMode} For enabling interactivity\n * @see {@link Container.interactive} For backwards compatibility\n * @category events\n * @standard\n */\nexport interface IHitArea\n{\n /**\n * Checks if the given coordinates are inside this hit area.\n * @param {number} x - The x coordinate to check\n * @param {number} y - The y coordinate to check\n * @returns True if the coordinates are inside the hit area\n */\n contains(x: number, y: number): boolean;\n}\n\n/**\n * Function type for handlers, e.g., onclick\n * @category events\n * @advanced\n */\nexport type FederatedEventHandler = (event: T) => void;\n\n/**\n * The type of interaction behavior for a Container. This is set via the {@link Container#eventMode} property.\n * @example\n * ```ts\n * // Basic event mode setup\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static'; // Enable standard interaction\n * sprite.on('pointerdown', () => { console.log('clicked!'); });\n *\n * // Different event modes\n * sprite.eventMode = 'none'; // Disable all interaction\n * sprite.eventMode = 'passive'; // Only allow interaction on children\n * sprite.eventMode = 'auto'; // Like DOM pointer-events: auto\n * sprite.eventMode = 'dynamic'; // For moving/animated objects\n * ```\n *\n * Available modes:\n * - `'none'`: Ignores all interaction events, even on its children\n * - `'passive'`: **(default)** Does not emit events and ignores hit testing on itself and non-interactive children.\n * Interactive children will still emit events.\n * - `'auto'`: Does not emit events but is hit tested if parent is interactive. Same as `interactive = false` in v7\n * - `'static'`: Emit events and is hit tested. Same as `interactive = true` in v7\n * - `'dynamic'`: Emits events and is hit tested but will also receive mock interaction events fired from\n * a ticker to allow for interaction when the mouse isn't moving\n *\n * Performance tips:\n * - Use `'none'` for pure visual elements\n * - Use `'passive'` for containers with some interactive children\n * - Use `'static'` for standard buttons/controls\n * - Use `'dynamic'` only for moving/animated interactive elements\n * @since 7.2.0\n * @category events\n * @standard\n */\nexport type EventMode = 'none' | 'passive' | 'auto' | 'static' | 'dynamic';\n\n/**\n * The properties available for any interactive object. This interface defines the core interaction\n * properties and event handlers that can be set on any Container in PixiJS.\n * @example\n * ```ts\n * // Basic interactive setup\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n * sprite.cursor = 'pointer';\n *\n * // Using event handlers\n * sprite.on('click', (event) => console.log('Sprite clicked!', event));\n * sprite.on('pointerdown', (event) => console.log('Pointer down!', event));\n *\n * // Using property-based event handlers\n * sprite.onclick = (event) => console.log('Clicked!');\n * sprite.onpointerenter = () => sprite.alpha = 0.7;\n * sprite.onpointerleave = () => sprite.alpha = 1.0;\n *\n * // Custom hit area\n * sprite.hitArea = new Rectangle(0, 0, 100, 100);\n * ```\n *\n * Core Properties:\n * - `eventMode`: Controls how the object handles interaction events\n * - `cursor`: Sets the mouse cursor when hovering\n * - `hitArea`: Defines custom hit testing area\n * - `interactive`: Alias for `eventMode` to enable interaction with \"static\" or \"passive\" modes\n * - `interactiveChildren`: Controls hit testing on children\n *\n * Event Handlers:\n * - Mouse: click, mousedown, mouseup, mousemove, mouseenter, mouseleave\n * - Touch: touchstart, touchend, touchmove, tap\n * - Pointer: pointerdown, pointerup, pointermove, pointerover\n * - Global: globalpointermove, globalmousemove, globaltouchmove\n * > [!IMPORTANT] Global events are fired when the pointer moves even if it is outside the bounds of the Container.\n * @see {@link EventMode} For interaction mode details\n * @see {@link Cursor} For cursor style options\n * @see {@link IHitArea} For hit area implementation\n * @category events\n * @standard\n */\nexport interface FederatedOptions\n{\n /**\n * The cursor style to display when the mouse pointer is hovering over the object.\n * Accepts any valid CSS cursor value or custom cursor URL.\n * @example\n * ```ts\n * // Common cursor types\n * sprite.cursor = 'pointer'; // Hand cursor for clickable elements\n * sprite.cursor = 'grab'; // Grab cursor for draggable elements\n * sprite.cursor = 'crosshair'; // Precise cursor for selection\n * sprite.cursor = 'not-allowed'; // Indicate disabled state\n *\n * // Direction cursors\n * sprite.cursor = 'n-resize'; // North resize\n * sprite.cursor = 'ew-resize'; // East-west resize\n * sprite.cursor = 'nesw-resize'; // Northeast-southwest resize\n *\n * // Custom cursor with fallback\n * sprite.cursor = 'url(\"custom.png\"), auto';\n * sprite.cursor = 'url(\"cursor.cur\") 2 2, pointer'; // With hotspot offset\n * ```\n * @type {Cursor | string}\n * @default undefined\n * @see {@link EventSystem.cursorStyles} For setting global cursor styles\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} MDN Cursor Documentation\n */\n cursor?: Cursor | (string & {});\n /**\n * Enable interaction events for the Container. Touch, pointer and mouse events are supported.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n *\n * // Enable standard interaction (like buttons)\n * sprite.eventMode = 'static';\n * sprite.on('pointerdown', () => console.log('clicked!'));\n *\n * // Enable for moving objects\n * sprite.eventMode = 'dynamic';\n * sprite.on('pointermove', () => updatePosition());\n *\n * // Disable all interaction\n * sprite.eventMode = 'none';\n *\n * // Only allow child interactions\n * sprite.eventMode = 'passive';\n * ```\n *\n * Available modes:\n *\n * - `'none'`: Ignores all interaction events, even on its children. Best for pure visuals.\n * - `'passive'`: **(default)** Does not emit events and ignores hit testing on itself and non-interactive\n * children. Interactive children will still emit events.\n * - `'auto'`: Does not emit events but is hit tested if parent is interactive. Same as `interactive = false` in v7.\n * - `'static'`: Emit events and is hit tested. Same as `interactive = true` in v7. Best for buttons/UI.\n * - `'dynamic'`: Like static but also receives synthetic events when pointer is idle. Best for moving objects.\n *\n * Performance tips:\n * - Use `'none'` for pure visual elements\n * - Use `'passive'` for containers with some interactive children\n * - Use `'static'` for standard UI elements\n * - Use `'dynamic'` only when needed for moving/animated elements\n * @since 7.2.0\n */\n eventMode?: EventMode;\n /**\n * Whether this object should fire UI events. This is an alias for `eventMode` set to `'static'` or `'passive'`.\n * Setting this to true will enable interaction events like `pointerdown`, `click`, etc.\n * Setting it to false will disable all interaction events on this object.\n * @see {@link Container.eventMode}\n * @example\n * ```ts\n * // Enable interaction events\n * sprite.interactive = true; // Sets eventMode = 'static'\n * sprite.interactive = false; // Sets eventMode = 'passive'\n * ```\n */\n interactive?: boolean\n /**\n * Controls whether children of this container can receive pointer events.\n *\n * Setting this to false allows PixiJS to skip hit testing on all children,\n * improving performance for containers with many non-interactive children.\n * @default true\n * @example\n * ```ts\n * // Container with many visual-only children\n * const container = new Container();\n * container.interactiveChildren = false; // Skip hit testing children\n *\n * // Menu with interactive buttons\n * const menu = new Container();\n * menu.interactiveChildren = true; // Test all children\n * menu.addChild(button1, button2, button3);\n *\n * // Performance optimization\n * background.interactiveChildren = false;\n * foreground.interactiveChildren = true;\n * ```\n */\n interactiveChildren?: boolean;\n /**\n * Defines a custom hit area for pointer interaction testing. When set, this shape will be used\n * for hit testing instead of the container's standard bounds.\n * @example\n * ```ts\n * import { Rectangle, Circle, Sprite } from 'pixi.js';\n *\n * // Rectangular hit area\n * const button = new Sprite(texture);\n * button.eventMode = 'static';\n * button.hitArea = new Rectangle(0, 0, 100, 50);\n *\n * // Circular hit area\n * const icon = new Sprite(texture);\n * icon.eventMode = 'static';\n * icon.hitArea = new Circle(32, 32, 32);\n *\n * // Custom hit area with polygon\n * const custom = new Sprite(texture);\n * custom.eventMode = 'static';\n * custom.hitArea = new Polygon([0,0, 100,0, 100,100, 0,100]);\n *\n * // Custom hit testing logic\n * sprite.hitArea = {\n * contains(x: number, y: number) {\n * // Custom collision detection\n * return x >= 0 && x <= width && y >= 0 && y <= height;\n * }\n * };\n * ```\n * @remarks\n * - Takes precedence over the container's bounds for hit testing\n * - Can improve performance by simplifying collision checks\n * - Useful for irregular shapes or precise click areas\n */\n hitArea?: IHitArea | null;\n\n /**\n * Property-based event handler for the `click` event.\n * Fired when a pointer device (mouse, touch, etc.) completes a click action.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('click', (event) => {\n * console.log('Sprite clicked at:', event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onclick = (event) => {\n * console.log('Clicked at:', event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n onclick?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mousedown` event.\n * Fired when a mouse button is pressed while the pointer is over the object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mousedown', (event) => {\n * sprite.alpha = 0.5; // Visual feedback\n * console.log('Mouse button:', event.button);\n * });\n * // Using property-based handler\n * sprite.onmousedown = (event) => {\n * sprite.alpha = 0.5; // Visual feedback\n * console.log('Mouse button:', event.button);\n * };\n * ```\n * @default null\n */\n onmousedown?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseenter` event.\n * Fired when the mouse pointer enters the bounds of the object. Does not bubble.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseenter', (event) => {\n * sprite.scale.set(1.1);\n * });\n * // Using property-based handler\n * sprite.onmouseenter = (event) => {\n * sprite.scale.set(1.1);\n * };\n * ```\n * @default null\n */\n onmouseenter?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseleave` event.\n * Fired when the pointer leaves the bounds of the display object. Does not bubble.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseleave', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onmouseleave = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onmouseleave?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mousemove` event.\n * Fired when the pointer moves while over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mousemove', (event) => {\n * // Get coordinates relative to the sprite\n * console.log('Local:', event.getLocalPosition(sprite));\n * });\n * // Using property-based handler\n * sprite.onmousemove = (event) => {\n * // Get coordinates relative to the sprite\n * console.log('Local:', event.getLocalPosition(sprite));\n * };\n * ```\n * @default null\n */\n onmousemove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `globalmousemove` event.\n *\n * Fired when the mouse moves anywhere, regardless of whether the pointer is over this object.\n * The object must have `eventMode` set to 'static' or 'dynamic' to receive this event.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('globalmousemove', (event) => {\n * // Move sprite to mouse position\n * sprite.position.copyFrom(event.global);\n * });\n * // Using property-based handler\n * sprite.onglobalmousemove = (event) => {\n * // Move sprite to mouse position\n * sprite.position.copyFrom(event.global);\n * };\n * ```\n * @default null\n * @remarks\n * - Fires even when the mouse is outside the object's bounds\n * - Useful for drag operations or global mouse tracking\n * - Must have `eventMode` set appropriately to receive events\n * - Part of the global move events family along with `globalpointermove` and `globaltouchmove`\n */\n onglobalmousemove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseout` event.\n * Fired when the pointer moves out of the bounds of the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseout', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onmouseout = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onmouseout?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseover` event.\n * Fired when the pointer moves onto the bounds of the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseover', (event) => {\n * sprite.scale.set(1.1);\n * });\n * // Using property-based handler\n * sprite.onmouseover = (event) => {\n * sprite.scale.set(1.1);\n * };\n * ```\n * @default null\n */\n onmouseover?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseup` event.\n * Fired when a mouse button is released over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseup', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onmouseup = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onmouseup?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `mouseupoutside` event.\n * Fired when a mouse button is released outside the display object that initially\n * registered a mousedown.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('mouseupoutside', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onmouseupoutside = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onmouseupoutside?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointercancel` event.\n * Fired when a pointer device interaction is canceled or lost.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointercancel', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onpointercancel = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onpointercancel?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerdown` event.\n * Fired when a pointer device button (mouse, touch, pen, etc.) is pressed.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerdown', (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onpointerdown = (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n onpointerdown?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerenter` event.\n * Fired when a pointer device enters the bounds of the display object. Does not bubble.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerenter', (event) => {\n * sprite.scale.set(1.2);\n * });\n * // Using property-based handler\n * sprite.onpointerenter = (event) => {\n * sprite.scale.set(1.2);\n * };\n * ```\n * @default null\n */\n onpointerenter?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerleave` event.\n * Fired when a pointer device leaves the bounds of the display object. Does not bubble.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n * // Using emitter handler\n * sprite.on('pointerleave', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onpointerleave = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onpointerleave?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointermove` event.\n * Fired when a pointer device moves while over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointermove', (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onpointermove = (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n onpointermove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `globalpointermove` event.\n *\n * Fired when the pointer moves anywhere, regardless of whether the pointer is over this object.\n * The object must have `eventMode` set to 'static' or 'dynamic' to receive this event.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('globalpointermove', (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onglobalpointermove = (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * };\n * ```\n * @default null\n * @remarks\n * - Fires even when the mouse is outside the object's bounds\n * - Useful for drag operations or global mouse tracking\n * - Must have `eventMode` set appropriately to receive events\n * - Part of the global move events family along with `globalpointermove` and `globaltouchmove`\n */\n onglobalpointermove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerout` event.\n * Fired when the pointer moves out of the bounds of the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerout', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onpointerout = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onpointerout?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerover` event.\n * Fired when the pointer moves over the bounds of the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerover', (event) => {\n * sprite.scale.set(1.2);\n * });\n * // Using property-based handler\n * sprite.onpointerover = (event) => {\n * sprite.scale.set(1.2);\n * };\n * ```\n * @default null\n */\n onpointerover?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointertap` event.\n * Fired when a pointer device completes a tap action (e.g., touch or mouse click).\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointertap', (event) => {\n * console.log('Sprite tapped at:', event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onpointertap = (event) => {\n * console.log('Sprite tapped at:', event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n onpointertap?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerup` event.\n * Fired when a pointer device button (mouse, touch, pen, etc.) is released.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerup', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onpointerup = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onpointerup?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `pointerupoutside` event.\n * Fired when a pointer device button is released outside the bounds of the display object\n * that initially registered a pointerdown.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('pointerupoutside', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onpointerupoutside = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onpointerupoutside?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `rightclick` event.\n * Fired when a right-click (context menu) action is performed on the object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('rightclick', (event) => {\n * console.log('Right-clicked at:', event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onrightclick = (event) => {\n * console.log('Right-clicked at:', event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n onrightclick?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `rightdown` event.\n * Fired when a right mouse button is pressed down over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('rightdown', (event) => {\n * sprite.scale.set(0.9);\n * });\n * // Using property-based handler\n * sprite.onrightdown = (event) => {\n * sprite.scale.set(0.9);\n * };\n * ```\n * @default null\n */\n onrightdown?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `rightup` event.\n * Fired when a right mouse button is released over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('rightup', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onrightup = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onrightup?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `rightupoutside` event.\n * Fired when a right mouse button is released outside the bounds of the display object\n * that initially registered a rightdown.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('rightupoutside', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.onrightupoutside = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n onrightupoutside?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `tap` event.\n * Fired when a tap action (touch) is completed on the object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('tap', (event) => {\n * console.log('Sprite tapped at:', event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.ontap = (event) => {\n * console.log('Sprite tapped at:', event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n ontap?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `touchcancel` event.\n * Fired when a touch interaction is canceled, such as when the touch is interrupted.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('touchcancel', (event) => {\n * console.log('Touch canceled at:', event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.ontouchcancel = (event) => {\n * console.log('Touch canceled at:', event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n ontouchcancel?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `touchend` event.\n * Fired when a touch interaction ends, such as when the finger is lifted from the screen.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('touchend', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.ontouchend = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n ontouchend?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `touchendoutside` event.\n * Fired when a touch interaction ends outside the bounds of the display object\n * that initially registered a touchstart.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('touchendoutside', (event) => {\n * sprite.scale.set(1.0);\n * });\n * // Using property-based handler\n * sprite.ontouchendoutside = (event) => {\n * sprite.scale.set(1.0);\n * };\n * ```\n * @default null\n */\n ontouchendoutside?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `touchmove` event.\n * Fired when a touch interaction moves while over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('touchmove', (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.ontouchmove = (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * };\n * ```\n * @default null\n */\n ontouchmove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `globaltouchmove` event.\n *\n * Fired when a touch interaction moves anywhere, regardless of whether the pointer is over this object.\n * The object must have `eventMode` set to 'static' or 'dynamic' to receive this event.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('globaltouchmove', (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * });\n * // Using property-based handler\n * sprite.onglobaltouchmove = (event) => {\n * sprite.position.set(event.global.x, event.global.y);\n * };\n * ```\n * @default null\n * @remarks\n * - Fires even when the touch is outside the object's bounds\n * - Useful for drag operations or global touch tracking\n * - Must have `eventMode` set appropriately to receive events\n * - Part of the global move events family along with `globalpointermove` and `globalmousemove`\n */\n onglobaltouchmove?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `touchstart` event.\n * Fired when a touch interaction starts, such as when a finger touches the screen.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('touchstart', (event) => {\n * sprite.scale.set(0.9);\n * });\n * // Using property-based handler\n * sprite.ontouchstart = (event) => {\n * sprite.scale.set(0.9);\n * };\n * ```\n * @default null\n */\n ontouchstart?: FederatedEventHandler | null;\n\n /**\n * Property-based event handler for the `wheel` event.\n * Fired when the mouse wheel is scrolled while over the display object.\n * @example\n * ```ts\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n *\n * // Using emitter handler\n * sprite.on('wheel', (event) => {\n * sprite.scale.x += event.deltaY * 0.01; // Zoom in/out\n * sprite.scale.y += event.deltaY * 0.01; // Zoom in/out\n * });\n * // Using property-based handler\n * sprite.onwheel = (event) => {\n * sprite.scale.x += event.deltaY * 0.01; // Zoom in/out\n * sprite.scale.y += event.deltaY * 0.01; // Zoom in/out\n * };\n * ```\n * @default null\n */\n onwheel?: FederatedEventHandler | null;\n}\n\n/**\n * The options for the `addEventListener` method.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener}\n * @category events\n * @advanced\n */\nexport type AddListenerOptions = boolean | AddEventListenerOptions;\n/**\n * The options for the `removeEventListener` method.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener}\n * @category events\n * @advanced\n */\nexport type RemoveListenerOptions = boolean | EventListenerOptions;\n\n/**\n * Additional properties for a Container that is used for interaction events.\n * @category events\n * @advanced\n */\nexport interface IFederatedContainer extends FederatedOptions\n{\n /** The parent of this event target. */\n readonly parent?: Container;\n\n /** The children of this event target. */\n readonly children?: ReadonlyArray;\n\n /** @private */\n _internalEventMode: EventMode;\n\n /**\n * Determines if the container is interactive or not\n * @returns {boolean} Whether the container is interactive or not\n * @since 7.2.0\n * @example\n * import { Sprite } from 'pixi.js';\n *\n * const sprite = new Sprite(texture);\n * sprite.eventMode = 'static';\n * sprite.isInteractive(); // true\n *\n * sprite.eventMode = 'dynamic';\n * sprite.isInteractive(); // true\n *\n * sprite.eventMode = 'none';\n * sprite.isInteractive(); // false\n *\n * sprite.eventMode = 'passive';\n * sprite.isInteractive(); // false\n *\n * sprite.eventMode = 'auto';\n * sprite.isInteractive(); // false\n */\n isInteractive: () => boolean;\n /**\n * Unlike `on` or `addListener` which are methods from EventEmitter, `addEventListener`\n * seeks to be compatible with the DOM's `addEventListener` with support for options.\n * @param {any} type - The type of event to listen to.\n * @param {any} listener - The listener callback or object.\n * @param {any} options - Listener options, used for capture phase.\n * @example\n * // Tell the user whether they did a single, double, triple, or nth click.\n * button.addEventListener('click', {\n * handleEvent(e): {\n * let prefix;\n *\n * switch (e.detail) {\n * case 1: prefix = 'single'; break;\n * case 2: prefix = 'double'; break;\n * case 3: prefix = 'triple'; break;\n * default: prefix = e.detail + 'th'; break;\n * }\n *\n * console.log('That was a ' + prefix + 'click');\n * }\n * });\n *\n * // But skip the first click!\n * button.parent.addEventListener('click', function blockClickOnce(e) {\n * e.stopImmediatePropagation();\n * button.parent.removeEventListener('click', blockClickOnce, true);\n * }, {\n * capture: true,\n * });\n */\n addEventListener(\n type: K,\n listener: (e: AllFederatedEventMap[K]) => any,\n options?: AddListenerOptions\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddListenerOptions\n ): void;\n /**\n * Unlike `off` or `removeListener` which are methods from EventEmitter, `removeEventListener`\n * seeks to be compatible with the DOM's `removeEventListener` with support for options.\n * @param {K} type - The type of event the listener is bound to.\n * @param {any} listener - The listener callback or object.\n * @param {RemoveListenerOptions} options - The original listener options.\n * This is required to deregister a capture phase listener.\n */\n removeEventListener(\n type: K,\n listener: (e: AllFederatedEventMap[K]) => any,\n options?: RemoveListenerOptions\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: RemoveListenerOptions\n ): void;\n /**\n * Dispatch the event on this {@link Container} using the event's {@link EventBoundary}.\n *\n * The target of the event is set to `this` and the `defaultPrevented` flag is cleared before dispatch.\n * @param {FederatedEvent} e - The event to dispatch.\n * @returns Whether the {@link FederatedEvent.preventDefault preventDefault}() method was not invoked.\n * @example\n * // Reuse a click event!\n * button.dispatchEvent(clickEvent);\n */\n dispatchEvent(e: FederatedEvent): boolean;\n}\n\n/** @internal */\nexport const FederatedContainer: IFederatedContainer = {\n onclick: null,\n onmousedown: null,\n onmouseenter: null,\n onmouseleave: null,\n onmousemove: null,\n onglobalmousemove: null,\n onmouseout: null,\n onmouseover: null,\n onmouseup: null,\n onmouseupoutside: null,\n onpointercancel: null,\n onpointerdown: null,\n onpointerenter: null,\n onpointerleave: null,\n onpointermove: null,\n onglobalpointermove: null,\n onpointerout: null,\n onpointerover: null,\n onpointertap: null,\n onpointerup: null,\n onpointerupoutside: null,\n onrightclick: null,\n onrightdown: null,\n onrightup: null,\n onrightupoutside: null,\n ontap: null,\n ontouchcancel: null,\n ontouchend: null,\n ontouchendoutside: null,\n ontouchmove: null,\n onglobaltouchmove: null,\n ontouchstart: null,\n onwheel: null,\n get interactive()\n {\n return this.eventMode === 'dynamic' || this.eventMode === 'static';\n },\n set interactive(value: boolean)\n {\n this.eventMode = value ? 'static' : 'passive';\n },\n _internalEventMode: undefined,\n get eventMode()\n {\n return this._internalEventMode ?? EventSystem.defaultEventMode;\n },\n set eventMode(value)\n {\n this._internalEventMode = value;\n },\n isInteractive(): boolean\n {\n return this.eventMode === 'static' || this.eventMode === 'dynamic';\n },\n interactiveChildren: true,\n hitArea: null,\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: AddListenerOptions\n )\n {\n const capture = (typeof options === 'boolean' && options)\n || (typeof options === 'object' && options.capture);\n const signal = typeof options === 'object' ? options.signal : undefined;\n const once = typeof options === 'object' ? (options.once === true) : false;\n const context = typeof listener === 'function' ? undefined : listener;\n\n type = capture ? `${type}capture` : type;\n const listenerFn = typeof listener === 'function' ? listener : listener.handleEvent;\n\n const emitter = (this as unknown as EventEmitter);\n\n if (signal)\n {\n signal.addEventListener('abort', () =>\n {\n emitter.off(type, listenerFn, context);\n });\n }\n\n if (once)\n {\n emitter.once(type, listenerFn, context);\n }\n else\n {\n emitter.on(type, listenerFn, context);\n }\n },\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: RemoveListenerOptions\n )\n {\n const capture = (typeof options === 'boolean' && options)\n || (typeof options === 'object' && options.capture);\n const context = typeof listener === 'function' ? undefined : listener;\n\n type = capture ? `${type}capture` : type;\n listener = typeof listener === 'function' ? listener : listener.handleEvent;\n\n (this as unknown as EventEmitter).off(type, listener, context);\n },\n dispatchEvent(e: Event): boolean\n {\n if (!(e instanceof FederatedEvent))\n {\n throw new Error('Container cannot propagate events outside of the Federated Events API');\n }\n\n e.defaultPrevented = false;\n e.path = null;\n e.target = this as Container;\n e.manager.dispatchEvent(e);\n\n return !e.defaultPrevented;\n }\n};\n","import { extensions } from '../extensions/Extensions';\nimport { Container } from '../scene/container/Container';\nimport { EventSystem } from './EventSystem';\nimport { FederatedContainer } from './FederatedEventTarget';\n\nextensions.add(EventSystem);\nextensions.mixin(Container, FederatedContainer);\n","import { ExtensionType } from '../extensions/Extensions';\nimport { CanvasObserver } from './CanvasObserver';\nimport { type DOMContainer } from './DOMContainer';\n\nimport type { InstructionSet } from '../rendering/renderers/shared/instructions/InstructionSet';\nimport type { RenderPipe } from '../rendering/renderers/shared/instructions/RenderPipe';\nimport type { Renderer } from '../rendering/renderers/types';\n\n/**\n * The DOMPipe class is responsible for managing and rendering DOM elements within a PixiJS scene.\n * It maps dom elements to the canvas and ensures they are correctly positioned and visible.\n * @internal\n */\nexport class DOMPipe implements RenderPipe\n{\n /**\n * Static property defining the extension type and name for the DOMPipe.\n * This is used to register the DOMPipe with different rendering pipelines.\n */\n public static extension = {\n type: [\n ExtensionType.WebGLPipes,\n ExtensionType.WebGPUPipes,\n ExtensionType.CanvasPipes,\n ],\n name: 'dom',\n } as const;\n\n private _renderer: Renderer;\n\n /** Array to keep track of attached DOM elements */\n private readonly _attachedDomElements: DOMContainer[] = [];\n /** The main DOM element that acts as a container for other DOM elements */\n private readonly _domElement: HTMLDivElement;\n /** The CanvasTransformSync instance that keeps the DOM element in sync with the canvas */\n private _canvasObserver: CanvasObserver;\n\n /**\n * Constructor for the DOMPipe class.\n * @param renderer - The renderer instance that this DOMPipe will be associated with.\n */\n constructor(renderer: Renderer)\n {\n this._renderer = renderer;\n\n // Add this DOMPipe to the postrender runner of the renderer\n // we want to dom elements are calculated after all things have been rendered\n this._renderer.runners.postrender.add(this);\n\n // add DOMPipe to init runners\n this._renderer.runners.init.add(this);\n\n // Create a main DOM element to contain other DOM elements\n this._domElement = document.createElement('div');\n this._domElement.style.position = 'absolute';\n this._domElement.style.top = '0';\n this._domElement.style.left = '0';\n this._domElement.style.pointerEvents = 'none';\n this._domElement.style.zIndex = '1000';\n }\n\n /** Initializes the DOMPipe, setting up the main DOM element and adding it to the document body. */\n public init(): void\n {\n // Initialize the CanvasTransformSync to keep the DOM element in sync with the canvas\n this._canvasObserver = new CanvasObserver({\n domElement: this._domElement,\n renderer: this._renderer,\n });\n }\n\n /**\n * Adds a renderable DOM container to the list of attached elements.\n * @param domContainer - The DOM container to be added.\n * @param _instructionSet - The instruction set (unused).\n */\n public addRenderable(domContainer: DOMContainer, _instructionSet: InstructionSet): void\n {\n if (!this._attachedDomElements.includes(domContainer))\n {\n this._attachedDomElements.push(domContainer);\n }\n }\n\n /**\n * Updates a renderable DOM container.\n * @param _domContainer - The DOM container to be updated (unused).\n */\n public updateRenderable(_domContainer: DOMContainer): void\n {\n // Updates happen in postrender\n }\n\n /**\n * Validates a renderable DOM container.\n * @param _domContainer - The DOM container to be validated (unused).\n * @returns Always returns true as validation is not required.\n */\n public validateRenderable(_domContainer: DOMContainer): boolean\n {\n return true;\n }\n\n /** Handles the post-rendering process, ensuring DOM elements are correctly positioned and visible. */\n public postrender(): void\n {\n const attachedDomElements = this._attachedDomElements;\n\n if (attachedDomElements.length === 0)\n {\n this._domElement.remove();\n\n return;\n }\n\n // Ensure the main DOM element is attached to the same parent as the canvas\n this._canvasObserver.ensureAttached();\n\n for (let i = 0; i < attachedDomElements.length; i++)\n {\n const domContainer = attachedDomElements[i];\n const element = domContainer.element;\n\n if (!domContainer.parent || domContainer.globalDisplayStatus < 0b111)\n {\n element?.remove();\n attachedDomElements.splice(i, 1);\n i--;\n }\n else\n {\n if (!this._domElement.contains(element))\n {\n element.style.position = 'absolute';\n element.style.pointerEvents = 'auto';\n this._domElement.appendChild(element);\n }\n\n const wt = domContainer.worldTransform;\n const anchor = domContainer._anchor;\n const ax = domContainer.width * anchor.x;\n const ay = domContainer.height * anchor.y;\n\n element.style.transformOrigin = `${ax}px ${ay}px`;\n element.style.transform = `matrix(${wt.a}, ${wt.b}, ${wt.c}, ${wt.d}, ${wt.tx - ax}, ${wt.ty - ay})`;\n element.style.opacity = domContainer.groupAlpha.toString();\n }\n }\n }\n\n /** Destroys the DOMPipe, removing all attached DOM elements and cleaning up resources. */\n public destroy(): void\n {\n this._renderer.runners.postrender.remove(this);\n\n for (let i = 0; i < this._attachedDomElements.length; i++)\n {\n const domContainer = this._attachedDomElements[i];\n\n domContainer.element?.remove();\n }\n\n this._attachedDomElements.length = 0;\n this._domElement.remove();\n this._canvasObserver.destroy();\n this._renderer = null;\n }\n}\n","import { type InstructionSet } from '../../rendering/renderers/shared/instructions/InstructionSet';\nimport { type RenderPipe } from '../../rendering/renderers/shared/instructions/RenderPipe';\nimport { type Renderer } from '../../rendering/renderers/types';\nimport { Bounds } from '../container/bounds/Bounds';\nimport { Container, type ContainerOptions } from '../container/Container';\nimport { type IRenderLayer } from '../layers/RenderLayer';\n\nimport type { PointData } from '../../maths/point/PointData';\nimport type { View } from '../../rendering/renderers/shared/view/View';\nimport type { DestroyOptions } from '../container/destroyTypes';\n\n/** @internal */\nexport interface GPUData\n{\n destroy: () => void;\n}\n\n/**\n * Options for the construction of a ViewContainer.\n * @category scene\n * @advanced\n */\nexport interface ViewContainerOptions extends ContainerOptions, PixiMixins.ViewContainerOptions {}\n// eslint-disable-next-line requireExport/require-export-jsdoc, requireMemberAPI/require-member-api-doc\nexport interface ViewContainer extends PixiMixins.ViewContainer, Container\n{\n // eslint-disable-next-line requireMemberAPI/require-member-api-doc\n _gpuData: Record;\n}\n\n/**\n * A ViewContainer is a type of container that represents a view.\n * This view can be a Sprite, a Graphics object, or any other object that can be rendered.\n * This class is abstract and should not be used directly.\n * @category scene\n * @advanced\n */\nexport abstract class ViewContainer extends Container implements View\n{\n /** @internal */\n public override readonly renderPipeId: string;\n /** @internal */\n public readonly canBundle = true;\n /** @internal */\n public override allowChildren = false;\n\n /** @internal */\n public _roundPixels: 0 | 1 = 0;\n /** @internal */\n public _lastUsed = -1;\n\n /** @internal */\n public _gpuData: Record = Object.create(null);\n\n protected _bounds: Bounds = new Bounds(0, 1, 0, 0);\n protected _boundsDirty = true;\n\n /**\n * The local bounds of the view in its own coordinate space.\n * Bounds are automatically updated when the view's content changes.\n * @example\n * ```ts\n * // Get bounds dimensions\n * const bounds = view.bounds;\n * console.log(`Width: ${bounds.maxX - bounds.minX}`);\n * console.log(`Height: ${bounds.maxY - bounds.minY}`);\n * ```\n * @returns The rectangular bounds of the view\n * @see {@link Bounds} For bounds operations\n */\n public get bounds()\n {\n if (!this._boundsDirty) return this._bounds;\n\n this.updateBounds();\n\n this._boundsDirty = false;\n\n return this._bounds;\n }\n\n /** @private */\n protected abstract updateBounds(): void;\n\n /**\n * Whether or not to round the x/y position of the sprite.\n * @example\n * ```ts\n * // Enable pixel rounding for crisp rendering\n * view.roundPixels = true;\n * ```\n * @default false\n */\n get roundPixels()\n {\n return !!this._roundPixels;\n }\n\n set roundPixels(value: boolean)\n {\n this._roundPixels = value ? 1 : 0;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-useless-constructor\n constructor(options: ViewContainerOptions)\n {\n super(options);\n }\n\n /**\n * Checks if the object contains the given point in local coordinates.\n * Uses the view's bounds for hit testing.\n * @example\n * ```ts\n * // Basic point check\n * const localPoint = { x: 50, y: 25 };\n * const contains = view.containsPoint(localPoint);\n * console.log('Point is inside:', contains);\n * ```\n * @param point - The point to check in local coordinates\n * @returns True if the point is within the view's bounds\n * @see {@link ViewContainer#bounds} For the bounds used in hit testing\n * @see {@link Container#toLocal} For converting global coordinates to local\n */\n public containsPoint(point: PointData)\n {\n const bounds = this.bounds;\n const { x, y } = point;\n\n return (x >= bounds.minX\n && x <= bounds.maxX\n && y >= bounds.minY\n && y <= bounds.maxY);\n }\n\n /** @private */\n public abstract batched: boolean;\n\n /** @private */\n protected onViewUpdate()\n {\n this._didViewChangeTick++;\n\n this._boundsDirty = true;\n\n if (this.didViewUpdate) return;\n this.didViewUpdate = true;\n\n const renderGroup = this.renderGroup || this.parentRenderGroup;\n\n if (renderGroup)\n {\n renderGroup.onChildViewUpdate(this);\n }\n }\n\n public override destroy(options?: DestroyOptions): void\n {\n super.destroy(options);\n\n this._bounds = null;\n\n for (const key in this._gpuData)\n {\n (this._gpuData[key] as GPU_DATA).destroy?.();\n }\n\n this._gpuData = null;\n }\n\n /**\n * Collects renderables for the view container.\n * @param instructionSet - The instruction set to collect renderables for.\n * @param renderer - The renderer to collect renderables for.\n * @param currentLayer - The current render layer.\n * @internal\n */\n public override collectRenderablesSimple(\n instructionSet: InstructionSet,\n renderer: Renderer,\n currentLayer: IRenderLayer,\n ): void\n {\n const { renderPipes } = renderer;\n\n // TODO add blends in\n renderPipes.blendMode.setBlendMode(this, this.groupBlendMode, instructionSet);\n\n const rp = renderPipes as unknown as Record;\n\n rp[this.renderPipeId].addRenderable(this, instructionSet);\n\n this.didViewUpdate = false;\n\n const children = this.children;\n const length = children.length;\n\n for (let i = 0; i < length; i++)\n {\n children[i].collectRenderables(instructionSet, renderer, currentLayer);\n }\n }\n}\n","import { Point } from '../maths/point/Point';\nimport { ViewContainer, type ViewContainerOptions } from '../scene/view/ViewContainer';\n\nimport type { PointData } from '../maths/point/PointData';\n\n/**\n * Options for configuring a {@link DOMContainer}.\n * Controls how DOM elements are integrated into the PixiJS scene graph.\n * @example\n * ```ts\n * // Create with a custom element\n * const domContainer = new DOMContainer({\n * element: document.createElement('input'),\n * anchor: { x: 0.5, y: 0.5 } // or anchor: 0.5 to center both x and y\n * });\n * ```\n * @category scene\n * @standard\n * @noInheritDoc\n */\nexport interface DOMContainerOptions extends ViewContainerOptions\n{\n /**\n * The DOM element to use for the container.\n * Can be any HTML element like div, input, textarea, etc.\n *\n * If not provided, creates a new div element.\n * @default document.createElement('div')\n */\n element?: HTMLElement;\n\n /**\n * The anchor point of the container.\n * - Can be a single number to set both x and y\n * - Can be a point-like object with x,y coordinates\n * - (0,0) is top-left\n * - (1,1) is bottom-right\n * - (0.5,0.5) is center\n * @default 0\n */\n anchor?: PointData | number;\n}\n\n/**\n * The DOMContainer object is used to render DOM elements within the PixiJS scene graph.\n * It allows you to integrate HTML elements into your PixiJS application while maintaining\n * proper transform hierarchy and visibility.\n *\n * DOMContainer is especially useful for rendering standard DOM elements\n * that handle user input, such as `` or `