diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 0d702f3..c1da5d2 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -80,9 +80,48 @@ jobs: - name: Run Forge tests run: forge test -vvv + lint: + name: Lint Rust + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v4 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo build + uses: actions/cache@v4 + with: + path: target + key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock') }} + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + build: name: Build on ${{ matrix.os }} - needs: [test, test-solidity] + needs: [test, test-solidity, lint] runs-on: ${{ matrix.os }} strategy: matrix: @@ -123,7 +162,7 @@ jobs: if: matrix.target == 'aarch64-unknown-linux-gnu' run: | sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu + sudo apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu - name: Cache cargo registry uses: actions/cache@v4 @@ -154,10 +193,14 @@ jobs: env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - - name: Strip binary (Linux/macOS) - if: matrix.os != 'windows-latest' + - name: Strip binary (native builds) + if: matrix.os != 'windows-latest' && matrix.target != 'aarch64-unknown-linux-gnu' run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }} + - name: Strip binary (Linux aarch64 cross-compile) + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: aarch64-linux-gnu-strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }} + - name: Upload binary artifact uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/tagging.yml b/.github/workflows/tagging.yml new file mode 100644 index 0000000..bbd9db8 --- /dev/null +++ b/.github/workflows/tagging.yml @@ -0,0 +1,55 @@ +name: Bump Version + +on: + push: + branches: ["main"] + +jobs: + bump-version: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 + with: + persist-credentials: false + - name: Bump version and push tag + id: tag_version + uses: mathieudutour/github-tag-action@a22cf08638b34d5badda920f9daf6e72c477b07b #v6.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Cargo.toml version to match github tag for docs + shell: bash + env: + RELEASE_TAG: ${{ steps.tag_version.outputs.new_tag }} + run: | + mv docs/python/src/conf.py docs/python/src/conf.py.orig + sed "s/0\\.0\\.0/${RELEASE_TAG//v}/" docs/python/src/conf.py.orig >docs/python/src/conf.py + rm docs/python/src/conf.py.orig + mv docs/python/requirements-docs.txt docs/python/requirements-docs.txt.orig + sed "s/0\\.0\\.0/${RELEASE_TAG//v}/" docs/python/requirements-docs.txt.orig >docs/python/requirements-docs.txt + rm docs/python/requirements-docs.txt.orig + + - name: Commit files and create tag + env: + RELEASE_TAG: ${{ steps.tag_version.outputs.new_tag }} + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git fetch --tags + git checkout -b release-$RELEASE_TAG + git add . + git commit -m "ci: update version string in docs" + git tag -d $RELEASE_TAG + git tag $RELEASE_TAG + + - name: Push changes + uses: ad-m/github-push-action@77c5b412c50b723d2a4fbc6d71fb5723bcd439aa #master + env: + RELEASE_TAG: ${{ steps.tag_version.outputs.new_tag }} + with: + branch: release-${{ steps.tag_version.outputs.new_tag }} + force: true + tags: true \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 64e00f8..888a2ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,6 +352,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -453,6 +459,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -631,6 +643,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "mio" version = "1.1.1" @@ -741,6 +763,40 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustix" version = "1.1.4" @@ -777,6 +833,8 @@ dependencies = [ "clap", "hex", "logging", + "mime_guess", + "rust-embed", "serde", "serde_json", "sha2", @@ -784,11 +842,21 @@ dependencies = [ "thiserror", "tokio", "tower", + "tower-http", "tracing", "tracing-subscriber", "uuid", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "semver" version = "1.0.27" @@ -1011,6 +1079,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.3" @@ -1027,6 +1108,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1107,6 +1214,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1149,6 +1262,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1252,6 +1375,15 @@ dependencies = [ "semver", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-core" version = "0.62.2" diff --git a/Cargo.toml b/Cargo.toml index 8c5a2ba..a7e7b14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ path = "src/main.rs" anyhow = "1.0" async-trait = "0.1" axum = { version = "0.8", features = ["macros"] } +tower-http = { version = "0.6", features = ["fs", "trace", "cors"] } +rust-embed = "8.5" +mime_guess = "2.0" chrono = { version = "0.4", features = ["clock", "serde"] } clap = "4.5.60" hex = "0.4" diff --git a/Makefile b/Makefile index cdc27a6..dab5a91 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ install: test: test-rust test-solidity test-rust: + cargo fmt + cargo clippy cargo nextest run test-solidity: diff --git a/README.md b/README.md index 39dd144..6006512 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ sudo snap install multipass 2. Install safepaw ```bash -make test +make ``` ## Testing @@ -58,4 +58,7 @@ make test-rust # Run solidity tests make test-solidity -``` \ No newline at end of file +``` + +# Credits +1. [WebUI Assets -From : Sprout Lands -By : Cup Nooble](https://cupnooble.itch.io/sprout-lands-ui-pack) \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 7d118b1..bd2a4e2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,6 @@ [profile.default] src = "contracts" +test = "solidity-tests" out = "out" libs = ["lib"] solc_version = "0.8.20" diff --git a/test/AgentLog.t.sol b/solidity-tests/AgentLog.t.sol similarity index 100% rename from test/AgentLog.t.sol rename to solidity-tests/AgentLog.t.sol diff --git a/src/cli.rs b/src/cli.rs index 7d11eba..a834ed9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,11 +1,7 @@ -use std::sync::Arc; - use anyhow::{Context, Result, bail}; -use async_trait::async_trait; use clap::{Arg, ArgMatches, Command}; -use tracing::info; -use crate::vm::{Multipass, VmStatusResponse, VmSummary}; +use crate::vm::{VmApi, VmStatusResponse, VmSummary}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VmMode { @@ -13,101 +9,38 @@ pub enum VmMode { Network, } -#[async_trait] -pub trait VmApi: Send + Sync { - async fn launch(&self, name: &str) -> Result<()>; - async fn start(&self, name: &str) -> Result<()>; - async fn stop(&self, name: &str) -> Result<()>; - async fn restart(&self, name: &str) -> Result<()>; - async fn delete(&self, name: &str) -> Result<()>; - async fn info(&self, name: &str) -> Result; - async fn list(&self) -> Result>; -} - -#[derive(Clone)] -pub struct LocalVmApi { - multipass: Arc, -} - -impl LocalVmApi { - pub fn new(multipass: Arc) -> Self { - Self { multipass } - } -} - -#[async_trait] -impl VmApi for LocalVmApi { - async fn launch(&self, name: &str) -> Result<()> { - info!(vm_name = name, "launching VM. This may take a couple of minutes."); - self.multipass - .launch(name) - .await - .with_context(|| format!("failed to launch VM {name}"))?; - info!(vm_name = name, "VM launched successfully"); - Ok(()) - } - - async fn start(&self, name: &str) -> Result<()> { - info!(vm_name = name, "starting VM"); - self.multipass - .start(name) - .await - .with_context(|| format!("failed to start VM {name}"))?; - info!(vm_name = name, "VM started successfully"); - Ok(()) - } - - async fn stop(&self, name: &str) -> Result<()> { - info!(vm_name = name, "stopping VM"); - self.multipass - .stop(name) - .await - .with_context(|| format!("failed to stop VM {name}"))?; - info!(vm_name = name, "VM stopped successfully"); - Ok(()) - } - - async fn restart(&self, name: &str) -> Result<()> { - info!(vm_name = name, "restarting VM"); - self.multipass - .restart(name) - .await - .with_context(|| format!("failed to restart VM {name}"))?; - info!(vm_name = name, "VM restarted successfully"); - Ok(()) - } - - async fn delete(&self, name: &str) -> Result<()> { - info!(vm_name = name, "deleting VM"); - self.multipass - .delete(name) - .await - .with_context(|| format!("failed to delete VM {name}"))?; - info!(vm_name = name, "VM deleted successfully"); - Ok(()) - } - - async fn info(&self, name: &str) -> Result { - info!(vm_name = name, "getting VM info"); - self.multipass - .info(name) - .await - .with_context(|| format!("failed to get info for VM {name}")) - } - - async fn list(&self) -> Result> { - info!("listing VMs"); - self.multipass - .list() - .await - .context("failed to list VMs from multipass") - } -} - pub fn build_cli() -> Command { Command::new("safepaw") .about("Agents for the paranoid.") .long_about("SafePaw orchestrates isolated agent runtimes backed by Multipass VMs.") + .subcommand( + Command::new("start") + .about("Start SafePaw server daemon") + .long_about("Starts the SafePaw UI server and REST API daemon") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .default_value("0.0.0.0") + .help("Host address to bind servers (e.g., 0.0.0.0, 127.0.0.1, localhost)"), + ) + .arg( + Arg::new("ui-port") + .long("ui-port") + .value_name("PORT") + .default_value("8888") + .value_parser(clap::value_parser!(u16)) + .help("Port for the UI server"), + ) + .arg( + Arg::new("api-port") + .long("api-port") + .value_name("PORT") + .default_value("8889") + .value_parser(clap::value_parser!(u16)) + .help("Port for the REST API server"), + ), + ) .subcommand( Command::new("vm") .about("Manage VM lifecycle through multipass") @@ -172,10 +105,10 @@ pub fn resolve_vm_mode(matches: &ArgMatches) -> Result { fn format_vm_summary(vm: &VmSummary) -> String { let mut parts = vec![vm.name.clone(), vm.state.clone()]; - if let Some(ref ipv4_addrs) = vm.ipv4 { - if !ipv4_addrs.is_empty() { - parts.push(ipv4_addrs.join(",")); - } + if let Some(ref ipv4_addrs) = vm.ipv4 + && !ipv4_addrs.is_empty() + { + parts.push(ipv4_addrs.join(",")); } if let Some(ref release) = vm.release { @@ -191,10 +124,10 @@ fn format_vm_info(info: &VmStatusResponse) -> Vec { format!("State: {}", info.state), ]; - if let Some(ref ipv4_addrs) = info.ipv4 { - if !ipv4_addrs.is_empty() { - lines.push(format!("IPv4: {}", ipv4_addrs.join(", "))); - } + if let Some(ref ipv4_addrs) = info.ipv4 + && !ipv4_addrs.is_empty() + { + lines.push(format!("IPv4: {}", ipv4_addrs.join(", "))); } if let Some(ref release) = info.release { @@ -213,14 +146,20 @@ fn format_vm_info(info: &VmStatusResponse) -> Vec { let total_mb = total / 1024 / 1024; let used_mb = used / 1024 / 1024; let percent = (used as f64 / total as f64 * 100.0) as u64; - lines.push(format!("Memory: {} MiB / {} MiB ({}%)", used_mb, total_mb, percent)); + lines.push(format!( + "Memory: {} MiB / {} MiB ({}%)", + used_mb, total_mb, percent + )); } if let (Some(total), Some(used)) = (info.disk_total, info.disk_used) { let total_gb = total / 1024 / 1024 / 1024; let used_gb = used / 1024 / 1024 / 1024; let percent = (used as f64 / total as f64 * 100.0) as u64; - lines.push(format!("Disk: {} GiB / {} GiB ({}%)", used_gb, total_gb, percent)); + lines.push(format!( + "Disk: {} GiB / {} GiB ({}%)", + used_gb, total_gb, percent + )); } lines @@ -263,10 +202,7 @@ pub async fn run_vm_subcommand(matches: &ArgMatches, api: &dyn VmApi) -> Result< if vms.is_empty() { Ok(vec!["No VMs found".to_string()]) } else { - Ok(vms - .into_iter() - .map(|vm| format_vm_summary(&vm)) - .collect()) + Ok(vms.into_iter().map(|vm| format_vm_summary(&vm)).collect()) } } _ => Ok(Vec::new()), diff --git a/src/lib.rs b/src/lib.rs index 6a0ffd3..849d2fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod cli; +pub mod server; pub mod vm; diff --git a/src/main.rs b/src/main.rs index 129f675..0653726 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,8 @@ use std::env; use std::sync::Arc; use anyhow::bail; -use safepaw::cli::{LocalVmApi, VmMode, build_cli, resolve_vm_mode, run_vm_subcommand}; -use safepaw::vm::{MultipassCli, TokioCommandExecutor}; +use safepaw::cli::{VmMode, build_cli, resolve_vm_mode, run_vm_subcommand}; +use safepaw::vm::{LocalVmApi, MultipassCli, TokioCommandExecutor}; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; #[tokio::main] @@ -12,10 +12,7 @@ async fn main() { // Can be controlled via RUST_LOG env var (e.g., RUST_LOG=debug) tracing_subscriber::registry() .with(fmt::layer()) - .with( - EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("safepaw=info")) - ) + .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("safepaw=info"))) .init(); if let Err(err) = run().await { @@ -36,8 +33,22 @@ async fn run() -> anyhow::Result<()> { } let matches = build_cli().get_matches(); - if let Some(("vm", vm_matches)) = matches.subcommand() { - match resolve_vm_mode(vm_matches)? { + + match matches.subcommand() { + Some(("start", start_matches)) => { + let host = start_matches + .get_one::("host") + .map(String::as_str) + .unwrap_or("0.0.0.0"); + let ui_port = *start_matches.get_one::("ui-port").unwrap_or(&8888); + let api_port = *start_matches.get_one::("api-port").unwrap_or(&8889); + + let multipass = Arc::new(MultipassCli::new(TokioCommandExecutor)); + let api = Arc::new(LocalVmApi::new(multipass)) as Arc; + + safepaw::server::run_server(api, host, ui_port, api_port).await?; + } + Some(("vm", vm_matches)) => match resolve_vm_mode(vm_matches)? { VmMode::Local => { let multipass = Arc::new(MultipassCli::new(TokioCommandExecutor)); let api = LocalVmApi::new(multipass); @@ -49,7 +60,8 @@ async fn run() -> anyhow::Result<()> { VmMode::Network => { bail!("network mode is planned but not implemented yet"); } - } + }, + _ => {} } Ok(()) diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..46134ca --- /dev/null +++ b/src/server.rs @@ -0,0 +1,241 @@ +use std::net::SocketAddr; +use std::sync::Arc; + +use anyhow::{Context, Result}; +use axum::{ + Json, Router, + body::Body, + extract::State, + http::{HeaderValue, Response, StatusCode, Uri, header}, + response::IntoResponse, + routing::get, +}; +use rust_embed::RustEmbed; +use serde::{Deserialize, Serialize}; +use tokio::signal; +use tower_http::cors::CorsLayer; +use tracing::{info, warn}; + +use crate::vm::VmApi; + +// Embed the UI assets directly into the binary +#[derive(RustEmbed)] +#[folder = "ui/"] +struct UiAssets; + +#[derive(Clone)] +pub struct AppState { + pub(crate) vm_api: Arc, +} + +impl AppState { + pub fn new(vm_api: Arc) -> Self { + Self { vm_api } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VmStatusDto { + pub name: String, + pub state: String, + pub ipv4: Option>, + pub release: Option, + pub memory_total: Option, + pub memory_used: Option, + pub disk_total: Option, + pub disk_used: Option, +} + +// REST API handlers +async fn health_check() -> impl IntoResponse { + (StatusCode::OK, Json(serde_json::json!({"status": "ok"}))) +} + +async fn list_vms(State(state): State) -> impl IntoResponse { + match state.vm_api.list().await { + Ok(vms) => { + let dtos: Vec = vms + .into_iter() + .map(|vm| VmStatusDto { + name: vm.name, + state: vm.state, + ipv4: vm.ipv4, + release: vm.release, + memory_total: None, + memory_used: None, + disk_total: None, + disk_used: None, + }) + .collect(); + (StatusCode::OK, Json(dtos)).into_response() + } + Err(e) => { + warn!("failed to list VMs: {}", e); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({"error": format!("{}", e)})), + ) + .into_response() + } + } +} + +async fn get_vm_info( + State(state): State, + axum::extract::Path(name): axum::extract::Path, +) -> impl IntoResponse { + match state.vm_api.info(&name).await { + Ok(info) => { + let dto = VmStatusDto { + name: info.name, + state: info.state, + ipv4: info.ipv4, + release: info.release, + memory_total: info.memory_total, + memory_used: info.memory_used, + disk_total: info.disk_total, + disk_used: info.disk_used, + }; + (StatusCode::OK, Json(dto)).into_response() + } + Err(e) => { + warn!("failed to get VM info for {}: {}", name, e); + ( + StatusCode::NOT_FOUND, + Json(serde_json::json!({"error": format!("{}", e)})), + ) + .into_response() + } + } +} + +pub fn create_api_router(state: AppState) -> Router { + Router::new() + .route("/health", get(health_check)) + .route("/vms", get(list_vms)) + .route("/vms/{name}", get(get_vm_info)) + .layer(CorsLayer::permissive()) + .with_state(state) +} + +pub fn create_ui_router() -> Router { + Router::new().fallback(serve_embedded_file) +} + +async fn serve_embedded_file(uri: Uri) -> impl IntoResponse { + let mut path = uri.path().trim_start_matches('/').to_string(); + + // Default to index.html if path is empty or ends with / + if path.is_empty() || path.ends_with('/') { + path = "index.html".to_string(); + } + + match UiAssets::get(&path) { + Some(content) => { + let mime = mime_guess::from_path(&path).first_or_octet_stream(); + let body = Body::from(content.data.into_owned()); + + Response::builder() + .status(StatusCode::OK) + .header( + header::CONTENT_TYPE, + HeaderValue::from_str(mime.as_ref()).unwrap(), + ) + .body(body) + .unwrap() + } + None => Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::from("404 Not Found")) + .unwrap(), + } +} + +pub async fn run_server( + vm_api: Arc, + host: &str, + ui_port: u16, + api_port: u16, +) -> Result<()> { + let state = AppState::new(vm_api); + + // Parse host address + let host_addr: std::net::IpAddr = host + .parse() + .context(format!("invalid host address: {}", host))?; + + // API server + let api_router = create_api_router(state.clone()); + let api_addr = SocketAddr::from((host_addr, api_port)); + + // UI server (using embedded assets) + let ui_router = create_ui_router(); + let ui_addr = SocketAddr::from((host_addr, ui_port)); + + info!( + "🏡 Starting SafePaw village UI on http://{}:{}", + host, ui_port + ); + info!( + "📡 Starting REST API server on http://{}:{}", + host, api_port + ); + info!("🌐 Visit the UI to access the SafePaw village"); + info!("🔌 API health check: http://{}:{}/health", host, api_port); + + // Spawn both servers concurrently + let api_server = async { + let listener = tokio::net::TcpListener::bind(api_addr) + .await + .context(format!( + "failed to bind API server to {}:{}", + host, api_port + ))?; + axum::serve(listener, api_router) + .with_graceful_shutdown(shutdown_signal()) + .await + .context("API server failed") + }; + + let ui_server = async { + let listener = tokio::net::TcpListener::bind(ui_addr) + .await + .context(format!("failed to bind UI server to {}:{}", host, ui_port))?; + axum::serve(listener, ui_router) + .with_graceful_shutdown(shutdown_signal()) + .await + .context("UI server failed") + }; + + tokio::try_join!(api_server, ui_server)?; + + Ok(()) +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => { + info!("Received Ctrl+C, shutting down gracefully"); + } + _ = terminate => { + info!("Received terminate signal, shutting down gracefully"); + } + } +} diff --git a/src/vm.rs b/src/vm.rs index c073845..be1367f 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use anyhow::Result; use async_trait::async_trait; use axum::{ Json, Router, @@ -97,6 +98,19 @@ pub enum VmError { }, } +// High-level VM API trait (used by CLI and server) +#[async_trait] +pub trait VmApi: Send + Sync { + async fn launch(&self, name: &str) -> Result<()>; + async fn start(&self, name: &str) -> Result<()>; + async fn stop(&self, name: &str) -> Result<()>; + async fn restart(&self, name: &str) -> Result<()>; + async fn delete(&self, name: &str) -> Result<()>; + async fn info(&self, name: &str) -> Result; + async fn list(&self) -> Result>; +} + +// Low-level Multipass CLI trait #[async_trait] pub trait Multipass: Send + Sync { async fn launch(&self, name: &str) -> Result<(), VmError>; @@ -219,28 +233,31 @@ where reason: format!("missing VM entry for {name}"), })?; - let state = vm - .get("state") - .and_then(Value::as_str) - .ok_or_else(|| VmError::InvalidOutput { - action: "status", - reason: "missing VM state".to_owned(), - })?; + let state = + vm.get("state") + .and_then(Value::as_str) + .ok_or_else(|| VmError::InvalidOutput { + action: "status", + reason: "missing VM state".to_owned(), + })?; // Extract optional fields - let ipv4 = vm - .get("ipv4") - .and_then(Value::as_array) - .map(|arr| { - arr.iter() - .filter_map(Value::as_str) - .map(String::from) - .collect() - }); + let ipv4 = vm.get("ipv4").and_then(Value::as_array).map(|arr| { + arr.iter() + .filter_map(Value::as_str) + .map(String::from) + .collect() + }); let release = vm.get("release").and_then(Value::as_str).map(String::from); - let image_release = vm.get("image_release").and_then(Value::as_str).map(String::from); - let cpu_count = vm.get("cpu_count").and_then(Value::as_str).map(String::from); + let image_release = vm + .get("image_release") + .and_then(Value::as_str) + .map(String::from); + let cpu_count = vm + .get("cpu_count") + .and_then(Value::as_str) + .map(String::from); let memory_total = vm .get("memory") @@ -257,8 +274,14 @@ where .and_then(Value::as_object) .and_then(|disks| disks.values().next()) .map(|disk| { - let total = disk.get("total").and_then(|v| v.as_str()).and_then(|s| s.parse::().ok()); - let used = disk.get("used").and_then(|v| v.as_str()).and_then(|s| s.parse::().ok()); + let total = disk + .get("total") + .and_then(|v| v.as_str()) + .and_then(|s| s.parse::().ok()); + let used = disk + .get("used") + .and_then(|v| v.as_str()) + .and_then(|s| s.parse::().ok()); (total, used) }) .unwrap_or((None, None)); @@ -283,42 +306,42 @@ where reason: err.to_string(), })?; - let list = value - .get("list") - .and_then(Value::as_array) - .ok_or_else(|| VmError::InvalidOutput { - action: "list", - reason: "missing list array".to_owned(), - })?; - - let mut vms = Vec::with_capacity(list.len()); - for item in list { - let name = item - .get("name") - .and_then(Value::as_str) + let list = + value + .get("list") + .and_then(Value::as_array) .ok_or_else(|| VmError::InvalidOutput { action: "list", - reason: "missing VM name".to_owned(), + reason: "missing list array".to_owned(), })?; - let state = item - .get("state") - .and_then(Value::as_str) - .ok_or_else(|| VmError::InvalidOutput { + + let mut vms = Vec::with_capacity(list.len()); + for item in list { + let name = + item.get("name") + .and_then(Value::as_str) + .ok_or_else(|| VmError::InvalidOutput { + action: "list", + reason: "missing VM name".to_owned(), + })?; + let state = item.get("state").and_then(Value::as_str).ok_or_else(|| { + VmError::InvalidOutput { action: "list", reason: "missing VM state".to_owned(), - })?; + } + })?; - let ipv4 = item - .get("ipv4") - .and_then(Value::as_array) - .map(|arr| { - arr.iter() - .filter_map(Value::as_str) - .map(String::from) - .collect() - }); + let ipv4 = item.get("ipv4").and_then(Value::as_array).map(|arr| { + arr.iter() + .filter_map(Value::as_str) + .map(String::from) + .collect() + }); - let release = item.get("release").and_then(Value::as_str).map(String::from); + let release = item + .get("release") + .and_then(Value::as_str) + .map(String::from); vms.push(VmSummary { name: name.to_owned(), @@ -365,8 +388,11 @@ where } async fn delete(&self, name: &str) -> Result<(), VmError> { - self.run_command("delete", vec!["delete".to_owned(), name.to_owned(), "--purge".to_owned()]) - .await?; + self.run_command( + "delete", + vec!["delete".to_owned(), name.to_owned(), "--purge".to_owned()], + ) + .await?; Ok(()) } @@ -388,12 +414,99 @@ where async fn list(&self) -> Result, VmError> { let output = self - .run_command("list", vec!["list".to_owned(), "--format".to_owned(), "json".to_owned()]) + .run_command( + "list", + vec!["list".to_owned(), "--format".to_owned(), "json".to_owned()], + ) .await?; self.parse_list_output(&output.stdout) } } +// LocalVmApi: High-level API implementation using Multipass +#[derive(Clone)] +pub struct LocalVmApi { + multipass: Arc, +} + +impl LocalVmApi { + pub fn new(multipass: Arc) -> Self { + Self { multipass } + } +} + +#[async_trait] +impl VmApi for LocalVmApi { + async fn launch(&self, name: &str) -> Result<()> { + info!( + vm_name = name, + "launching VM. This may take a couple of minutes." + ); + self.multipass + .launch(name) + .await + .map_err(|e| anyhow::anyhow!("failed to launch VM {}: {}", name, e))?; + info!(vm_name = name, "VM launched successfully"); + Ok(()) + } + + async fn start(&self, name: &str) -> Result<()> { + info!(vm_name = name, "starting VM"); + self.multipass + .start(name) + .await + .map_err(|e| anyhow::anyhow!("failed to start VM {}: {}", name, e))?; + info!(vm_name = name, "VM started successfully"); + Ok(()) + } + + async fn stop(&self, name: &str) -> Result<()> { + info!(vm_name = name, "stopping VM"); + self.multipass + .stop(name) + .await + .map_err(|e| anyhow::anyhow!("failed to stop VM {}: {}", name, e))?; + info!(vm_name = name, "VM stopped successfully"); + Ok(()) + } + + async fn restart(&self, name: &str) -> Result<()> { + info!(vm_name = name, "restarting VM"); + self.multipass + .restart(name) + .await + .map_err(|e| anyhow::anyhow!("failed to restart VM {}: {}", name, e))?; + info!(vm_name = name, "VM restarted successfully"); + Ok(()) + } + + async fn delete(&self, name: &str) -> Result<()> { + info!(vm_name = name, "deleting VM"); + self.multipass + .delete(name) + .await + .map_err(|e| anyhow::anyhow!("failed to delete VM {}: {}", name, e))?; + info!(vm_name = name, "VM deleted successfully"); + Ok(()) + } + + async fn info(&self, name: &str) -> Result { + info!(vm_name = name, "getting VM info"); + self.multipass + .info(name) + .await + .map_err(|e| anyhow::anyhow!("failed to get info for VM {}: {}", name, e)) + } + + async fn list(&self) -> Result> { + info!("listing VMs"); + self.multipass + .list() + .await + .map_err(|e| anyhow::anyhow!("failed to list VMs from multipass: {}", e)) + } +} + #[derive(Clone)] struct VmApiState { multipass: Arc, diff --git a/tests/cli_vm_commands.rs b/tests/cli_vm_commands.rs index ece9b50..b1e6d91 100644 --- a/tests/cli_vm_commands.rs +++ b/tests/cli_vm_commands.rs @@ -2,8 +2,8 @@ use std::sync::{Arc, Mutex}; use async_trait::async_trait; use safepaw::{ - cli::{VmApi, build_cli, run_vm_subcommand}, - vm::{VmStatusResponse, VmSummary}, + cli::{build_cli, run_vm_subcommand}, + vm::{VmApi, VmStatusResponse, VmSummary}, }; #[derive(Default)] @@ -26,7 +26,11 @@ impl Default for FakeVmApi { impl FakeVmApi { fn calls(&self) -> Vec { - self.state.lock().expect("poisoned fake state").calls.clone() + self.state + .lock() + .expect("poisoned fake state") + .calls + .clone() } } diff --git a/tests/embedded_assets.rs b/tests/embedded_assets.rs new file mode 100644 index 0000000..962ee1e --- /dev/null +++ b/tests/embedded_assets.rs @@ -0,0 +1,101 @@ +use axum::{body::Body, http::Request}; +use tower::ServiceExt; + +#[tokio::test] +async fn test_index_html_is_embedded() { + let app = safepaw::server::create_ui_router(); + + let response = app + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), 200); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + let html = String::from_utf8_lossy(&body); + + // Verify it's the actual index.html content + assert!(html.contains("SafePaw Village")); +} + +#[tokio::test] +async fn test_assets_are_embedded() { + let app = safepaw::server::create_ui_router(); + + // Test JavaScript file + let response = app + .clone() + .oneshot( + Request::builder() + .uri("/app.js") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), 200); + assert_eq!( + response.headers().get("content-type").unwrap(), + "text/javascript" + ); + + // Test grass tile asset + let response = app + .clone() + .oneshot( + Request::builder() + .uri("/assets/tiles/grass.png") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), 200); + assert_eq!(response.headers().get("content-type").unwrap(), "image/png"); + + // Test 404 for non-existent file + let response = app + .oneshot( + Request::builder() + .uri("/nonexistent.txt") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), 404); +} + +#[tokio::test] +async fn test_pixi_library_is_embedded() { + let app = safepaw::server::create_ui_router(); + + let response = app + .oneshot( + Request::builder() + .uri("/pixi.min@v8.16.0.js") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), 200); + assert_eq!( + response.headers().get("content-type").unwrap(), + "text/javascript" + ); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + + // Verify it's the minified PixiJS library (should be substantial in size) + assert!(body.len() > 100_000, "PixiJS library should be embedded"); +} diff --git a/tests/local_vm_api_state.rs b/tests/local_vm_api_state.rs index 8f35c9f..aee5d8e 100644 --- a/tests/local_vm_api_state.rs +++ b/tests/local_vm_api_state.rs @@ -4,10 +4,7 @@ use std::{ }; use async_trait::async_trait; -use safepaw::{ - cli::{LocalVmApi, VmApi}, - vm::{Multipass, VmError, VmStatusResponse, VmSummary}, -}; +use safepaw::vm::{LocalVmApi, Multipass, VmApi, VmError, VmStatusResponse, VmSummary}; #[derive(Default)] struct FakeState { @@ -27,23 +24,21 @@ impl FakeMultipass { .lock() .expect("poisoned fake state") .statuses - .insert( - name.to_owned(), - VmStatusResponse::minimal(name, state), - ); + .insert(name.to_owned(), VmStatusResponse::minimal(name, state)); self } fn with_list(self, listed_vms: Vec) -> Self { - self.state - .lock() - .expect("poisoned fake state") - .listed_vms = listed_vms; + self.state.lock().expect("poisoned fake state").listed_vms = listed_vms; self } fn calls(&self) -> Vec { - self.state.lock().expect("poisoned fake state").calls.clone() + self.state + .lock() + .expect("poisoned fake state") + .calls + .clone() } } @@ -156,9 +151,7 @@ async fn stop_stops_vm() { let fake = FakeMultipass::default(); let api = LocalVmApi::new(Arc::new(fake.clone())); - api.stop("agent-1") - .await - .expect("stop should succeed"); + api.stop("agent-1").await.expect("stop should succeed"); assert_eq!(fake.calls(), vec!["stop:agent-1"]); } diff --git a/tests/multipass_adapter.rs b/tests/multipass_adapter.rs index 9162470..0a78203 100644 --- a/tests/multipass_adapter.rs +++ b/tests/multipass_adapter.rs @@ -32,10 +32,7 @@ impl CommandExecutor for FakeExecutor { call.push(program.to_owned()); call.extend(args.iter().cloned()); - self.calls - .lock() - .expect("poisoned calls mutex") - .push(call); + self.calls.lock().expect("poisoned calls mutex").push(call); self.outputs .lock() @@ -59,11 +56,11 @@ async fn launch_info_list_and_stop_flow_maps_to_multipass_commands() { ]); let multipass = MultipassCli::new(fake.clone()); - multipass.launch("agent-1").await.expect("launch should work"); - let info = multipass - .info("agent-1") + multipass + .launch("agent-1") .await - .expect("info should work"); + .expect("launch should work"); + let info = multipass.info("agent-1").await.expect("info should work"); let listed = multipass.list().await.expect("list should work"); multipass.stop("agent-1").await.expect("stop should work"); diff --git a/tests/server_integration.rs b/tests/server_integration.rs new file mode 100644 index 0000000..4cdc933 --- /dev/null +++ b/tests/server_integration.rs @@ -0,0 +1,187 @@ +use std::sync::{Arc, Mutex}; + +use async_trait::async_trait; +use axum::{ + body::Body, + http::{Request, StatusCode}, +}; +use safepaw::{ + server::create_api_router, + vm::{VmApi, VmStatusResponse, VmSummary}, +}; +use tower::ServiceExt; + +#[derive(Default)] +struct FakeState { + vms: Vec, +} + +#[derive(Clone, Default)] +struct FakeVmApi { + state: Arc>, +} + +impl FakeVmApi { + fn with_vms(self, vms: Vec) -> Self { + self.state.lock().expect("poisoned fake state").vms = vms; + self + } +} + +#[async_trait] +impl VmApi for FakeVmApi { + async fn launch(&self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + + async fn start(&self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + + async fn stop(&self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + + async fn restart(&self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + + async fn delete(&self, _name: &str) -> anyhow::Result<()> { + Ok(()) + } + + async fn info(&self, name: &str) -> anyhow::Result { + Ok(VmStatusResponse { + name: name.to_owned(), + state: "Running".to_owned(), + ipv4: Some(vec!["192.168.1.100".to_owned()]), + release: Some("Ubuntu 22.04".to_owned()), + image_release: Some("Ubuntu 22.04 LTS".to_owned()), + cpu_count: Some("2".to_owned()), + memory_total: Some(2 * 1024 * 1024 * 1024), // 2 GiB + memory_used: Some(1024 * 1024 * 1024), // 1 GiB + disk_total: Some(10 * 1024 * 1024 * 1024), // 10 GiB + disk_used: Some(5 * 1024 * 1024 * 1024), // 5 GiB + }) + } + + async fn list(&self) -> anyhow::Result> { + Ok(self.state.lock().expect("poisoned fake state").vms.clone()) + } +} + +#[tokio::test] +async fn health_check_returns_ok() { + let fake_api = FakeVmApi::default(); + let app_state = safepaw::server::AppState::new(Arc::new(fake_api)); + let app = create_api_router(app_state); + + let response = app + .oneshot( + Request::builder() + .uri("/health") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); + + assert_eq!(json["status"], "ok"); +} + +#[tokio::test] +async fn list_vms_returns_empty_array_when_no_vms() { + let fake_api = FakeVmApi::default(); + let app_state = safepaw::server::AppState::new(Arc::new(fake_api)); + let app = create_api_router(app_state); + + let response = app + .oneshot(Request::builder().uri("/vms").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); + + assert_eq!(json.as_array().unwrap().len(), 0); +} + +#[tokio::test] +async fn list_vms_returns_vms() { + let fake_api = FakeVmApi::default().with_vms(vec![ + VmSummary { + name: "agent-1".to_owned(), + state: "Running".to_owned(), + ipv4: Some(vec!["192.168.1.100".to_owned()]), + release: Some("Ubuntu 22.04".to_owned()), + }, + VmSummary { + name: "agent-2".to_owned(), + state: "Stopped".to_owned(), + ipv4: None, + release: Some("Ubuntu 22.04".to_owned()), + }, + ]); + let app_state = safepaw::server::AppState::new(Arc::new(fake_api)); + let app = create_api_router(app_state); + + let response = app + .oneshot(Request::builder().uri("/vms").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + let vms: Vec = serde_json::from_slice(&body).unwrap(); + + assert_eq!(vms.len(), 2); + assert_eq!(vms[0].name, "agent-1"); + assert_eq!(vms[0].state, "Running"); + assert_eq!(vms[1].name, "agent-2"); + assert_eq!(vms[1].state, "Stopped"); +} + +#[tokio::test] +async fn get_vm_info_returns_vm_details() { + let fake_api = FakeVmApi::default(); + let app_state = safepaw::server::AppState::new(Arc::new(fake_api)); + let app = create_api_router(app_state); + + let response = app + .oneshot( + Request::builder() + .uri("/vms/agent-1") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + + let body = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + let vm: safepaw::server::VmStatusDto = serde_json::from_slice(&body).unwrap(); + + assert_eq!(vm.name, "agent-1"); + assert_eq!(vm.state, "Running"); + assert_eq!(vm.memory_total, Some(2 * 1024 * 1024 * 1024)); + assert_eq!(vm.memory_used, Some(1024 * 1024 * 1024)); + assert_eq!(vm.disk_total, Some(10 * 1024 * 1024 * 1024)); + assert_eq!(vm.disk_used, Some(5 * 1024 * 1024 * 1024)); +} diff --git a/tests/vm_lifecycle.rs b/tests/vm_lifecycle.rs index 2fec533..f303c9b 100644 --- a/tests/vm_lifecycle.rs +++ b/tests/vm_lifecycle.rs @@ -30,23 +30,21 @@ impl FakeMultipass { .lock() .expect("poisoned fake state") .status_by_name - .insert( - name.to_owned(), - VmStatusResponse::minimal(name, state), - ); + .insert(name.to_owned(), VmStatusResponse::minimal(name, state)); self } fn with_list(self, listed_vms: Vec) -> Self { - self.state - .lock() - .expect("poisoned fake state") - .listed_vms = listed_vms; + self.state.lock().expect("poisoned fake state").listed_vms = listed_vms; self } fn calls(&self) -> Vec { - self.state.lock().expect("poisoned fake state").calls.clone() + self.state + .lock() + .expect("poisoned fake state") + .calls + .clone() } } @@ -131,10 +129,7 @@ async fn spawn_vm_returns_created_and_launches_vm() { )) .expect("failed to build request"); - let response = app - .oneshot(request) - .await - .expect("failed to call vm app"); + let response = app.oneshot(request).await.expect("failed to call vm app"); assert_eq!(response.status(), StatusCode::CREATED); assert_eq!(fake.calls(), vec!["launch:agent-1"]); @@ -151,10 +146,7 @@ async fn get_vm_status_returns_current_vm_state() { .body(Body::empty()) .expect("failed to build request"); - let response = app - .oneshot(request) - .await - .expect("failed to call vm app"); + let response = app.oneshot(request).await.expect("failed to call vm app"); assert_eq!(response.status(), StatusCode::OK); @@ -182,10 +174,7 @@ async fn list_vms_returns_known_vms() { .body(Body::empty()) .expect("failed to build request"); - let response = app - .oneshot(request) - .await - .expect("failed to call vm app"); + let response = app.oneshot(request).await.expect("failed to call vm app"); assert_eq!(response.status(), StatusCode::OK); @@ -213,10 +202,7 @@ async fn terminate_vm_returns_no_content_and_stops_vm() { .body(Body::empty()) .expect("failed to build request"); - let response = app - .oneshot(request) - .await - .expect("failed to call vm app"); + let response = app.oneshot(request).await.expect("failed to call vm app"); assert_eq!(response.status(), StatusCode::NO_CONTENT); assert_eq!(fake.calls(), vec!["stop:agent-1"]); diff --git a/ui/app.js b/ui/app.js new file mode 100644 index 0000000..5e58820 --- /dev/null +++ b/ui/app.js @@ -0,0 +1,1373 @@ +// SafePaw Village - A calming, RPG-style VM visualization +// Neuroscience-backed features: +// 1. Soft, natural colors (green/blue reduce cortisol) +// 2. Gentle animations (smooth movements calm the nervous system) +// 3. Predictable patterns (reduces cognitive load & anxiety) +// 4. Nature elements (proven to reduce stress) +// 5. Positive visual feedback (dopamine release) + +// DEBUG MODE: Comprehensive logging is enabled to debug drag responsiveness +// To analyze the logs when drag becomes unresponsive: +// +// NORMAL DRAG SEQUENCE should look like: +// 1. NATIVE MOUSEDOWN +// 2. [N] POINTER DOWN +// 3. [N] Drag started +// 4. NATIVE MOUSEMOVE (dragging) - multiple times +// 5. [N] DRAG ACTIVATED +// 6. NATIVE MOUSEUP +// 7. [N] POINTER UP +// 8. [N] Drag ended +// +// PROBLEM PATTERNS TO LOOK FOR: +// A. Missing POINTER events but NATIVE events present = PixiJS event system blocked +// B. "POINTER UP called but no dragData exists" = drag state lost prematurely +// C. POINTER DOWN but no Drag started = onPointerDown not executing +// D. [STATE] or [UI] events happening during drag = state update interfering +// E. Event mode changes during drag = layer configuration changed +// F. Multiple POINTER DOWN without POINTER UP = drag state not clearing +// +// Check the timestamp correlation between events to find timing issues + +const API_BASE = window.location.protocol + '//' + window.location.hostname + ':8889'; + +// State Management Layer - Handles all data fetching and state +class VMStateManager extends EventTarget { + constructor() { + super(); + this.vms = []; + this.isPolling = false; + this.pollInterval = null; + } + + startPolling(intervalMs = 8000) { + if (this.isPolling) return; + + this.isPolling = true; + this.fetchVMs(); // Initial fetch + + this.pollInterval = setInterval(() => { + this.fetchVMs(); + }, intervalMs); + } + + stopPolling() { + if (this.pollInterval) { + clearInterval(this.pollInterval); + this.pollInterval = null; + } + this.isPolling = false; + } + + async fetchVMs() { + const DEBUG = true; + const fetchStartTime = Date.now(); + + if (DEBUG) { + console.log('[STATE] Fetching VMs...', { timestamp: fetchStartTime }); + } + + try { + const response = await fetch(`${API_BASE}/vms`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const vmList = await response.json(); + + // Fetch detailed info for each VM + const detailedVMs = await Promise.all( + vmList.map(async (vm) => { + try { + const infoResponse = await fetch(`${API_BASE}/vms/${vm.name}`); + if (infoResponse.ok) { + return await infoResponse.json(); + } + return vm; + } catch (e) { + return vm; + } + }) + ); + + const fetchDuration = Date.now() - fetchStartTime; + if (DEBUG) { + console.log('[STATE] Fetch complete:', { + duration: `${fetchDuration}ms`, + vmCount: detailedVMs.length + }); + } + + // Calculate diff and update state + this.updateState(detailedVMs); + } catch (error) { + console.error('[STATE] Error fetching VMs:', error); + this.updateState([]); + } + } + + updateState(newVMs) { + const changes = this.calculateDiff(this.vms, newVMs); + + if (changes.added.length > 0 || changes.removed.length > 0 || changes.updated.length > 0) { + this.vms = newVMs; + + // Emit granular events + if (changes.added.length > 0) { + this.dispatchEvent(new CustomEvent('vmsAdded', { detail: changes.added })); + } + if (changes.removed.length > 0) { + this.dispatchEvent(new CustomEvent('vmsRemoved', { detail: changes.removed })); + } + if (changes.updated.length > 0) { + this.dispatchEvent(new CustomEvent('vmsUpdated', { detail: changes.updated })); + } + + // Emit general state change event + this.dispatchEvent(new CustomEvent('stateChanged', { detail: { vms: this.vms, changes } })); + } + } + + calculateDiff(oldVMs, newVMs) { + const added = []; + const removed = []; + const updated = []; + + const oldMap = new Map(oldVMs.map(vm => [vm.name, vm])); + const newMap = new Map(newVMs.map(vm => [vm.name, vm])); + + // Find added and updated VMs + for (const [name, newVM] of newMap) { + if (!oldMap.has(name)) { + added.push(newVM); + } else { + const oldVM = oldMap.get(name); + if (this.hasVMChanged(oldVM, newVM)) { + updated.push(newVM); + } + } + } + + // Find removed VMs + for (const [name, oldVM] of oldMap) { + if (!newMap.has(name)) { + removed.push(oldVM); + } + } + + return { added, removed, updated }; + } + + hasVMChanged(oldVM, newVM) { + // Check relevant fields that would affect rendering + return oldVM.state !== newVM.state || + oldVM.memory_used !== newVM.memory_used || + oldVM.memory_total !== newVM.memory_total || + oldVM.disk_used !== newVM.disk_used || + oldVM.disk_total !== newVM.disk_total; + } + + getVMs() { + return this.vms; + } +} + +// Presentation Layer - Handles rendering only +class SafePawVillage { + constructor(stateManager) { + this.app = null; + this.stateManager = stateManager; + this.houses = new Map(); // Map of VM name -> house data + this.tickerCallbacks = new Map(); // Track ticker callbacks for cleanup + this.isInitialized = false; + + // Subscribe to state changes + this.stateManager.addEventListener('vmsAdded', (e) => this.onVMsAdded(e.detail)); + this.stateManager.addEventListener('vmsRemoved', (e) => this.onVMsRemoved(e.detail)); + this.stateManager.addEventListener('vmsUpdated', (e) => this.onVMsUpdated(e.detail)); + this.stateManager.addEventListener('stateChanged', (e) => this.onStateChanged(e.detail)); + } + + async init() { + // Create PixiJS application (full screen) + this.app = new PIXI.Application(); + await this.app.init({ + width: window.innerWidth, + height: window.innerHeight, + backgroundColor: 0x87CEEB, // Sky blue - calming color + antialias: true, + resolution: window.devicePixelRatio || 1, + resizeTo: window, + eventMode: 'static', + eventFeatures: { + move: true, + globalMove: true, + click: true, + wheel: true, + }, + }); + + const canvas = this.app.canvas; + canvas.style.cursor = 'grab'; + canvas.style.touchAction = 'none'; // Prevent default touch behaviors + document.getElementById('game-container').appendChild(canvas); + + // Create world container + this.world = new PIXI.Container(); + this.app.stage.addChild(this.world); + + // Create layers FIRST (before drag controls that reference them) + this.groundLayer = new PIXI.Container(); + this.buildingLayer = new PIXI.Container(); + this.animalLayer = new PIXI.Container(); + this.effectsLayer = new PIXI.Container(); + + // Make ground layer non-interactive so it doesn't block drag events + this.groundLayer.eventMode = 'none'; + this.groundLayer.interactiveChildren = false; + + // Buildings and animals should also not block dragging + this.buildingLayer.eventMode = 'none'; + this.buildingLayer.interactiveChildren = false; + this.animalLayer.eventMode = 'none'; + this.animalLayer.interactiveChildren = false; + + this.world.addChild(this.groundLayer); + this.world.addChild(this.buildingLayer); + this.world.addChild(this.animalLayer); + this.world.addChild(this.effectsLayer); + + // Add drag/pan controls (AFTER layers are created) + this.setupDragControls(); + + // Load assets + await this.loadAssets(); + + // Create the village + this.createGround(); + + // Initialize village with current state + const currentVMs = this.stateManager.getVMs(); + this.initializeVillage(currentVMs); + + // Hide loading screen + document.getElementById('loading').style.display = 'none'; + document.getElementById('info-panel').style.display = 'block'; + + // Start animation loop + this.app.ticker.add(() => this.update()); + + // Start state polling (state manager handles this now) + this.stateManager.startPolling(5000); + + this.isInitialized = true; + } + + async loadAssets() { + // Load textures from the assets folder + const assetsPath = window.location.protocol + '//' + window.location.hostname + ':8888/assets'; + + try { + // Load tiles + console.log('Loading assets...'); + + const grassPath = `${assetsPath}/tiles/grass.png`; + console.log('Grass path:', grassPath); + PIXI.Assets.add({ alias: 'grass', src: grassPath }); + + const woodHousePath = `${assetsPath}/tiles/wood_house_walls.png`; + console.log('Wood house path:', woodHousePath); + PIXI.Assets.add({ alias: 'wood_house', src: woodHousePath }); + + const doorPath = `${assetsPath}/tiles/door.png`; + console.log('Door path:', doorPath); + PIXI.Assets.add({ alias: 'door', src: doorPath }); + + const chickenPath = `${assetsPath}/character/chicken.png`; + console.log('Chicken path:', chickenPath); + PIXI.Assets.add({ alias: 'chicken', src: chickenPath }); + + const cowPath = `${assetsPath}/character/cow.png`; + console.log('Cow path:', cowPath); + PIXI.Assets.add({ alias: 'cow', src: cowPath }); + + const plantsPath = `${assetsPath}/tiles/plants.png`; + console.log('Plants path:', plantsPath); + PIXI.Assets.add({ alias: 'plants', src: plantsPath }); + + const waterPath = `${assetsPath}/tiles/water.png`; + console.log('Water path:', waterPath); + PIXI.Assets.add({ alias: 'water', src: waterPath }); + + await PIXI.Assets.load(['grass', 'wood_house', 'chicken', 'cow', 'plants', 'water']); + } catch (error) { + console.error('Error loading assets:', error); + } + } + + setupDragControls() { + // Debug flag - set to true to enable logging + const DEBUG = false; + + // Enable interactive mode on the world container + this.world.eventMode = 'passive'; + this.world.interactiveChildren = false; // Prevent children from blocking events + + let dragData = null; + let currentPointer = null; + let eventCounter = 0; + + // Use the app.stage for global event handling to avoid missing events + this.app.stage.eventMode = 'static'; + this.app.stage.hitArea = this.app.screen; + + const onPointerDown = (event) => { + eventCounter++; + if (DEBUG) { + console.log(`[${eventCounter}] POINTER DOWN:`, { + timestamp: Date.now(), + position: { x: event.global.x, y: event.global.y }, + target: event.target?.constructor?.name || 'unknown', + targetEventMode: event.target?.eventMode, + button: event.button, + pointerType: event.pointerType, + existingDragData: !!dragData + }); + } + + // Start drag on any pointer down since all children are non-interactive + dragData = { + startX: event.global.x, + startY: event.global.y, + worldStartX: this.world.x, + worldStartY: this.world.y, + isDragging: false, + eventId: eventCounter + }; + currentPointer = event.global.clone(); + this.app.renderer.canvas.style.cursor = 'grabbing'; + + if (DEBUG) { + console.log(`[${eventCounter}] Drag started:`, { + startPos: { x: dragData.startX, y: dragData.startY }, + worldPos: { x: dragData.worldStartX, y: dragData.worldStartY } + }); + } + }; + + const onPointerMove = (event) => { + if (dragData) { + currentPointer = event.global.clone(); + const dx = currentPointer.x - dragData.startX; + const dy = currentPointer.y - dragData.startY; + + // Consider it a drag if moved more than 3 pixels + if (!dragData.isDragging && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) { + dragData.isDragging = true; + if (DEBUG) { + console.log(`[${dragData.eventId}] DRAG ACTIVATED:`, { + delta: { dx, dy }, + distance: Math.sqrt(dx*dx + dy*dy) + }); + } + } + + if (dragData.isDragging) { + // Apply position immediately for responsiveness + this.world.x = dragData.worldStartX + dx; + this.world.y = dragData.worldStartY + dy; + } + } + // Removed excessive "no drag" logging + }; + + const endDrag = (event, eventType) => { + if (DEBUG) { + console.log(`[${dragData?.eventId || '?'}] POINTER ${eventType}:`, { + timestamp: Date.now(), + hadDragData: !!dragData, + wasDragging: dragData?.isDragging, + position: event ? { x: event.global.x, y: event.global.y } : 'no event' + }); + } + + if (dragData) { + if (DEBUG) { + console.log(`[${dragData.eventId}] Drag ended - Distance moved:`, { + dx: currentPointer ? currentPointer.x - dragData.startX : 0, + dy: currentPointer ? currentPointer.y - dragData.startY : 0 + }); + } + dragData = null; + currentPointer = null; + this.app.renderer.canvas.style.cursor = 'grab'; + } else if (DEBUG) { + console.warn(`${eventType} called but no dragData exists - potential issue!`); + } + }; + + // Attach to stage for reliable event capture + this.app.stage.on('pointerdown', onPointerDown); + this.app.stage.on('pointermove', onPointerMove); + this.app.stage.on('pointerup', (e) => endDrag(e, 'UP')); + this.app.stage.on('pointerupoutside', (e) => endDrag(e, 'UPOUTSIDE')); + this.app.stage.on('pointercancel', (e) => endDrag(e, 'CANCEL')); + + // Also add to canvas for fallback + const canvas = this.app.renderer.canvas; + + // Prevent context menu on right-click (helps with dragging) + canvas.addEventListener('contextmenu', (e) => e.preventDefault()); + + // Add native event listeners for debugging + if (DEBUG) { + canvas.addEventListener('mousedown', (e) => { + console.log('NATIVE MOUSEDOWN:', { + x: e.clientX, + y: e.clientY, + button: e.button, + buttons: e.buttons + }); + }); + + canvas.addEventListener('mouseup', (e) => { + console.log('NATIVE MOUSEUP:', { + x: e.clientX, + y: e.clientY, + button: e.button, + buttons: e.buttons + }); + }); + + // Track native mousemove only to detect if dragging happens without dragData + let lastMoveWithButtonPressed = 0; + canvas.addEventListener('mousemove', (e) => { + if (e.buttons > 0 && !dragData) { + // Only log if we're getting native drag events but PixiJS dragData is missing + const now = Date.now(); + if (now - lastMoveWithButtonPressed > 100) { // Throttle to every 100ms + console.warn('⚠️ NATIVE MOUSEMOVE with button pressed but NO dragData!', { + x: e.clientX, + y: e.clientY, + buttons: e.buttons + }); + lastMoveWithButtonPressed = now; + } + } + }); + + // Log when stage event mode changes + console.log('Stage setup:', { + stageEventMode: this.app.stage.eventMode, + worldEventMode: this.world.eventMode, + worldInteractiveChildren: this.world.interactiveChildren, + groundLayerEventMode: this.groundLayer.eventMode, + buildingLayerEventMode: this.buildingLayer.eventMode + }); + } + } + + createGround() { + // ============================================================================ + // TILE SIZE CONFIGURATION - Change these values to test different sizes + // ============================================================================ + const SPRITESHEET_TILE_SIZE = 16; // The actual tile size in the grass.png spritesheet (try 16, 32, or 64) + const RENDER_TILE_SIZE = 32; // The size to render tiles on screen (scaled up for visibility) + + // Store these for use in createGrassTile + this.spritesheetTileSize = SPRITESHEET_TILE_SIZE; + this.tileSize = RENDER_TILE_SIZE; + + console.log('=== TILE CONFIG ===', { + spritesheetTileSize: SPRITESHEET_TILE_SIZE, + renderTileSize: RENDER_TILE_SIZE + }); + + // Define grass tile variations from the spritesheet + // Border tiles: Plain grass for clean edges (3x3 grid from rows 0-2, cols 0-2) + // These are organized by position for proper border rendering + this.borderGrassTiles = { + topLeft: { x: 0 * SPRITESHEET_TILE_SIZE, y: 0 * SPRITESHEET_TILE_SIZE }, // [0,0] + topMiddle: { x: 1 * SPRITESHEET_TILE_SIZE, y: 0 * SPRITESHEET_TILE_SIZE }, // [1,0] + topRight: { x: 2 * SPRITESHEET_TILE_SIZE, y: 0 * SPRITESHEET_TILE_SIZE }, // [2,0] + middleLeft: { x: 0 * SPRITESHEET_TILE_SIZE, y: 1 * SPRITESHEET_TILE_SIZE }, // [0,1] + middleRight: { x: 2 * SPRITESHEET_TILE_SIZE, y: 1 * SPRITESHEET_TILE_SIZE }, // [2,1] + bottomLeft: { x: 0 * SPRITESHEET_TILE_SIZE, y: 2 * SPRITESHEET_TILE_SIZE }, // [0,2] + bottomMiddle:{ x: 1 * SPRITESHEET_TILE_SIZE, y: 2 * SPRITESHEET_TILE_SIZE }, // [1,2] + bottomRight: { x: 2 * SPRITESHEET_TILE_SIZE, y: 2 * SPRITESHEET_TILE_SIZE }, // [2,2] + }; + + // Center tiles: Textured grass for visual interest + this.centerGrassVariations = [ + { x: 1 * SPRITESHEET_TILE_SIZE, y: 1 * SPRITESHEET_TILE_SIZE }, // Middle plain grass [1,1] + + // Dark texture variations (row 6, assuming rows 0-5 are other tiles) + { x: 0 * SPRITESHEET_TILE_SIZE, y: 6 * SPRITESHEET_TILE_SIZE }, // Dark texture [0,6] + { x: 1 * SPRITESHEET_TILE_SIZE, y: 6 * SPRITESHEET_TILE_SIZE }, // Dark texture [1,6] + { x: 2 * SPRITESHEET_TILE_SIZE, y: 6 * SPRITESHEET_TILE_SIZE }, // Dark texture [2,6] + { x: 3 * SPRITESHEET_TILE_SIZE, y: 6 * SPRITESHEET_TILE_SIZE }, // Dark texture [3,6] + { x: 4 * SPRITESHEET_TILE_SIZE, y: 6 * SPRITESHEET_TILE_SIZE }, // Dark texture [4,6] + + // Light texture variations (row 7) + { x: 0 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [0,7] + { x: 1 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [1,7] + { x: 2 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [2,7] + { x: 3 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [3,7] + { x: 4 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [4,7] + { x: 5 * SPRITESHEET_TILE_SIZE, y: 7 * SPRITESHEET_TILE_SIZE }, // Light texture [5,7] + ]; + + // Create a large area (3x screen size for smooth scrolling) + const tilesX = Math.ceil(this.app.screen.width / RENDER_TILE_SIZE) * 3; + const tilesY = Math.ceil(this.app.screen.height / RENDER_TILE_SIZE) * 3; + + const startX = -Math.ceil(this.app.screen.width / RENDER_TILE_SIZE); + const startY = -Math.ceil(this.app.screen.height / RENDER_TILE_SIZE); + + console.log('=== Creating ground tiles ===', { + renderTileSize: RENDER_TILE_SIZE, + spritesheetTileSize: SPRITESHEET_TILE_SIZE, + tilesX, + tilesY, + totalTiles: tilesX * tilesY + }); + + for (let y = 0; y < tilesY; y++) { + for (let x = 0; x < tilesX; x++) { + // Determine border position + const isTopRow = (y === 0); + const isBottomRow = (y === tilesY - 1); + const isLeftCol = (x === 0); + const isRightCol = (x === tilesX - 1); + const isBorder = isTopRow || isBottomRow || isLeftCol || isRightCol; + + // Calculate border position type + let borderPosition = null; + if (isBorder) { + if (isTopRow && isLeftCol) borderPosition = 'topLeft'; + else if (isTopRow && isRightCol) borderPosition = 'topRight'; + else if (isBottomRow && isLeftCol) borderPosition = 'bottomLeft'; + else if (isBottomRow && isRightCol) borderPosition = 'bottomRight'; + else if (isTopRow) borderPosition = 'topMiddle'; + else if (isBottomRow) borderPosition = 'bottomMiddle'; + else if (isLeftCol) borderPosition = 'middleLeft'; + else if (isRightCol) borderPosition = 'middleRight'; + } + + const grass = this.createGrassTile( + (startX + x) * RENDER_TILE_SIZE, + (startY + y) * RENDER_TILE_SIZE, + isBorder, + borderPosition + ); + this.groundLayer.addChild(grass); + } + } + + // Store tile dimensions for infinite scrolling + this.tilesX = tilesX; + this.tilesY = tilesY; + + } + + createGrassTile(x, y, isBorder = false, borderPosition = null) { + const grassTexture = PIXI.Assets.get('grass'); + const grass = new PIXI.Sprite(grassTexture); + + let variation; + if (isBorder && borderPosition) { + // Use specific border tile based on position + variation = this.borderGrassTiles[borderPosition]; + } else if (isBorder) { + // Fallback: if no position specified, use a middle tile + variation = this.borderGrassTiles.topMiddle; + } else { + // Center tiles: randomly select from textured variations + variation = this.centerGrassVariations[Math.floor(Math.random() * this.centerGrassVariations.length)]; + } + + // Create texture from specific region of spritesheet using the spritesheet tile size + const rect = new PIXI.Rectangle( + variation.x, + variation.y, + this.spritesheetTileSize, + this.spritesheetTileSize + ); + grass.texture = new PIXI.Texture({ + source: grassTexture.source, + frame: rect, + }); + + grass.x = x; + grass.y = y; + // Set render size (can be different from spritesheet size for scaling) + grass.width = this.tileSize; + grass.height = this.tileSize; + + return grass; + } + + // addWaterFeature(x, y, width, height) { + // const water = new PIXI.Graphics(); + // water.beginFill(0x4DB8FF, 0.6); + // water.drawRoundedRect(x, y, width, height, 10); + // water.endFill(); + + // // Gentle water animation + // this.app.ticker.add(() => { + // water.alpha = 0.5 + Math.sin(Date.now() * 0.001) * 0.1; + // }); + + // this.groundLayer.addChild(water); + // } + + // Initialize village with VMs (called once during init) + initializeVillage(vms) { + if (vms.length === 0) { + this.showWelcomeMessage(); + this.updateInfoPanel(); + return; + } + + // Layout and create all VMs + vms.forEach((vm, index) => { + const position = this.calculateVMPosition(index); + this.addVMToVillage(vm, position); + }); + + this.updateInfoPanel(); + } + + // Event handlers for state changes + onVMsAdded(vms) { + const DEBUG = true; + if (DEBUG) { + console.log('[UI] VMs added:', { + count: vms.length, + names: vms.map(vm => vm.name), + timestamp: Date.now() + }); + } + + this.hideWelcomeMessage(); + + // Calculate starting index for layout + const startIndex = this.houses.size; + + vms.forEach((vm, i) => { + const position = this.calculateVMPosition(startIndex + i); + this.addVMToVillage(vm, position); + }); + + this.updateInfoPanel(); + } + + onVMsRemoved(vms) { + const DEBUG = true; + if (DEBUG) { + console.log('[UI] VMs removed:', { + count: vms.length, + names: vms.map(vm => vm.name), + timestamp: Date.now() + }); + } + + vms.forEach(vm => { + this.removeVMFromVillage(vm.name); + }); + + // Reorganize remaining VMs + this.reorganizeVillage(); + + if (this.houses.size === 0) { + this.showWelcomeMessage(); + } + + this.updateInfoPanel(); + } + + onVMsUpdated(vms) { + const DEBUG = true; + if (DEBUG) { + console.log('[UI] VMs updated:', { + count: vms.length, + names: vms.map(vm => vm.name), + timestamp: Date.now() + }); + } + + vms.forEach(vm => { + this.updateVMInVillage(vm); + }); + + this.updateInfoPanel(); + } + + onStateChanged() { + // General state change handler (if needed) + // Individual handlers above are more granular + } + + calculateVMPosition(index) { + const padding = 100; + const spacing = 150; + const startX = padding; + const startY = padding; + + const col = index % 4; + const row = Math.floor(index / 4); + + return { + x: startX + col * spacing, + y: startY + row * spacing + }; + } + + addVMToVillage(vm, position) { + if (this.houses.has(vm.name)) { + console.warn(`VM ${vm.name} already exists in village`); + return; + } + + const houseData = this.createHouse(vm, position.x, position.y); + this.houses.set(vm.name, houseData); + } + + removeVMFromVillage(vmName) { + const houseData = this.houses.get(vmName); + if (!houseData) return; + + // Clean up ticker callbacks + this.cleanupHouseCallbacks(vmName); + + // Remove sprites + if (houseData.house) { + this.buildingLayer.removeChild(houseData.house); + } + if (houseData.nameText) { + this.buildingLayer.removeChild(houseData.nameText); + } + if (houseData.animals) { + houseData.animals.forEach(animal => { + this.animalLayer.removeChild(animal); + }); + } + + this.houses.delete(vmName); + } + + updateVMInVillage(vm) { + const houseData = this.houses.get(vm.name); + if (!houseData) { + console.warn(`VM ${vm.name} not found in village for update`); + return; + } + + // Update house appearance based on state + let tintColor; + if (vm.state === 'Running') { + tintColor = 0xFFFFFF; + } else if (vm.state === 'Stopped') { + tintColor = 0xCCCCCC; + } else { + tintColor = 0xFFEEAA; + } + + // Apply tint to all tiles in the house container + if (houseData.house.children) { + houseData.house.children.forEach(tile => { + tile.tint = tintColor; + }); + } else { + // Fallback for old single-sprite houses + houseData.house.tint = tintColor; + } + + // Clean up old animal callbacks (keep house breathing callback) + const callbacks = this.tickerCallbacks.get(vm.name) || []; + const houseCallback = callbacks[0]; // First callback is always the house breathing + + // Remove all animal callbacks (everything except first) + for (let i = 1; i < callbacks.length; i++) { + this.app.ticker.remove(callbacks[i]); + } + + // Reset to just house callback + this.tickerCallbacks.set(vm.name, [houseCallback]); + + // Update animals (remove old ones, add new ones) + if (houseData.animals) { + houseData.animals.forEach(animal => { + this.animalLayer.removeChild(animal); + }); + } + + houseData.animals = []; + + if (vm.state === 'Running') { + const animals = this.createAnimals( + vm, + houseData.house.x, + houseData.house.y, + houseData.houseSize, + houseData.tileSize, + houseData.houseScale + ); + houseData.animals = animals; + } + + // Store updated VM data + houseData.vm = vm; + } + + reorganizeVillage() { + const DEBUG = true; + if (DEBUG) { + console.log('[UI] Reorganizing village:', { + houseCount: this.houses.size, + timestamp: Date.now() + }); + } + + // Recalculate positions for all remaining VMs + const vmNames = Array.from(this.houses.keys()); + + vmNames.forEach((vmName, index) => { + const houseData = this.houses.get(vmName); + const newPosition = this.calculateVMPosition(index); + + // Smooth position transition + if (houseData.house) { + houseData.house.x = newPosition.x; + houseData.house.y = newPosition.y; + } + if (houseData.nameText) { + houseData.nameText.x = newPosition.x; + houseData.nameText.y = newPosition.y + 40; + } + + // Reposition animals + if (houseData.animals && houseData.vm.state === 'Running') { + // Remove old animals + houseData.animals.forEach(animal => this.animalLayer.removeChild(animal)); + + // Create new animals at new position with house parameters + houseData.animals = this.createAnimals( + houseData.vm, + newPosition.x, + newPosition.y, + houseData.houseSize, + houseData.tileSize, + houseData.houseScale + ); + } + }); + + // Verify event modes haven't been changed + if (DEBUG) { + console.log('[UI] After reorganize - event modes:', { + groundLayerEventMode: this.groundLayer.eventMode, + buildingLayerEventMode: this.buildingLayer.eventMode, + animalLayerEventMode: this.animalLayer.eventMode, + worldEventMode: this.world.eventMode, + stageEventMode: this.app.stage.eventMode + }); + } + } + + showWelcomeMessage() { + if (this.welcomeText) return; // Already showing + + this.welcomeText = new PIXI.Text('No VMs yet! Create one with:\nsafepaw vm launch ', { + fontFamily: 'Arial', + fontSize: 24, + fill: 0x333333, + align: 'center', + }); + this.welcomeText.x = this.app.screen.width / 2; + this.welcomeText.y = this.app.screen.height / 2; + this.welcomeText.anchor.set(0.5); + this.buildingLayer.addChild(this.welcomeText); + } + + hideWelcomeMessage() { + if (this.welcomeText) { + this.buildingLayer.removeChild(this.welcomeText); + this.welcomeText = null; + } + } + + cleanupHouseCallbacks(vmName) { + const callbacks = this.tickerCallbacks.get(vmName); + if (callbacks) { + callbacks.forEach(callback => { + this.app.ticker.remove(callback); + }); + this.tickerCallbacks.delete(vmName); + } + } + + // Generate house tile layout dynamically based on size + // Size is the number of tiles per side (e.g., 5 = 5x5 house, 8 = 8x8 house) + generateHouseLayout(size) { + const layout = []; + + // Tile mapping from spritesheet: + // [0,0] = Left back corner + // [1,0] = Back wall + // [2,0] = Right back corner + // [0,1] = Left wall + // [1,1] = Floor/interior + // [2,1] = Right wall + // [0,2] = Left front corner + // [1,2] = Front wall + // [2,2] = Right front corner + // [3,1] = Door (closed) + + for (let row = 0; row < size; row++) { + for (let col = 0; col < size; col++) { + let tile; + + // Determine tile type based on position + if (row === 0) { + // Top row (back wall) + if (col === 0) { + tile = { col: 0, row: 0 }; // Left back corner + } else if (col === size - 1) { + tile = { col: 2, row: 0 }; // Right back corner + } else { + tile = { col: 1, row: 0 }; // Back wall + } + } else if (row === size - 1) { + // Bottom row (front wall) + if (col === 0) { + tile = { col: 0, row: 2 }; // Left front corner + } else if (col === size - 1) { + tile = { col: 2, row: 2 }; // Right front corner + } else if (col === Math.floor(size / 2)) { + tile = { col: 3, row: 1 }; // Door in the center + } else { + tile = { col: 1, row: 2 }; // Front wall + } + } else { + // Middle rows + if (col === 0) { + tile = { col: 0, row: 1 }; // Left wall + } else if (col === size - 1) { + tile = { col: 2, row: 1 }; // Right wall + } else { + tile = { col: 1, row: 1 }; // Floor/interior + } + } + + layout.push(tile); + } + } + + return layout; + } + + createHouse(vm, x, y) { + // ============================================================================ + // HOUSE TILE CONFIGURATION + // ============================================================================ + const HOUSE_TILE_SIZE = 16; // The actual tile size in wood_house.png spritesheet + const HOUSE_RENDER_TILE_SIZE = 32; // The size to render each house tile + + // House size based on VM resources (not animal count anymore) + const memoryGB = (vm.memory_total || 0) / (1024 * 1024 * 1024); + const diskGB = (vm.disk_total || 0) / (1024 * 1024 * 1024); + const totalResources = memoryGB + diskGB * 0.1; // Weigh memory more than disk + + // House size scales with total resources + // Small VMs: 7x7 + // Medium VMs: 10x10 + // Large VMs: 13x13 + let houseSize; + if (totalResources < 2) { + houseSize = 7; + } else if (totalResources < 8) { + houseSize = 10; + } else { + houseSize = 13; + } + + console.log('Creating house:', { + vm: vm.name, + resources: { memoryGB: memoryGB.toFixed(2), diskGB: diskGB.toFixed(2), total: totalResources.toFixed(2) }, + houseSize: `${houseSize}x${houseSize}`, + totalTiles: houseSize * houseSize + }); + + // Generate house layout dynamically (which sprite tiles to use) + const houseTileLayout = this.generateHouseLayout(houseSize); + + // Create a container for the house + const houseContainer = new PIXI.Container(); + + // Build the house from tiles + const houseTexture = PIXI.Assets.get('wood_house'); + + // Render all tiles + for (let i = 0; i < houseTileLayout.length; i++) { + const tileInfo = houseTileLayout[i]; + + // Calculate position on screen (row and column from index) + const col = i % houseSize; + const row = Math.floor(i / houseSize); + + const tileSprite = new PIXI.Sprite(houseTexture); + + // Extract tile from spritesheet (which tile design to use) + const rect = new PIXI.Rectangle( + tileInfo.col * HOUSE_TILE_SIZE, + tileInfo.row * HOUSE_TILE_SIZE, + HOUSE_TILE_SIZE, + HOUSE_TILE_SIZE + ); + tileSprite.texture = new PIXI.Texture({ + source: houseTexture.source, + frame: rect, + }); + + // Position on screen (where to place it) + tileSprite.x = col * HOUSE_RENDER_TILE_SIZE; + tileSprite.y = row * HOUSE_RENDER_TILE_SIZE; + tileSprite.width = HOUSE_RENDER_TILE_SIZE; + tileSprite.height = HOUSE_RENDER_TILE_SIZE; + + houseContainer.addChild(tileSprite); + } + + // Position the house container + houseContainer.x = x; + houseContainer.y = y; + + // Center the container dynamically based on house size + houseContainer.pivot.set( + (houseSize * HOUSE_RENDER_TILE_SIZE) / 2, + (houseSize * HOUSE_RENDER_TILE_SIZE) / 2 + ); + + // Scale based on resources (visual metaphor) - reuse already calculated values + const baseScale = 1.5; + const resourceScale = 1 + Math.min(totalResources * 0.1, 1); + houseContainer.scale.set(baseScale * resourceScale); + + // Subtle breathing animation (calming) + const breathingSpeed = 0.0005; + const breathingAmount = 0.02; + houseContainer.userData = { + baseScale: baseScale * resourceScale, + time: Math.random() * Math.PI * 2 + }; + + // Track ticker callback for cleanup + const callbacks = []; + const breathingCallback = () => { + houseContainer.userData.time += breathingSpeed; + const breath = Math.sin(houseContainer.userData.time) * breathingAmount; + houseContainer.scale.set(houseContainer.userData.baseScale * (1 + breath)); + }; + this.app.ticker.add(breathingCallback); + callbacks.push(breathingCallback); + + // VM state color tint - apply to all tiles in the container + let tintColor; + if (vm.state === 'Running') { + tintColor = 0xFFFFFF; // Normal + } else if (vm.state === 'Stopped') { + tintColor = 0xCCCCCC; // Grayed out + } else { + tintColor = 0xFFEEAA; // Yellowish for other states + } + + // Apply tint to all child sprites + houseContainer.children.forEach(tile => { + tile.tint = tintColor; + }); + + this.buildingLayer.addChild(houseContainer); + + // Add VM name label - positioned ABOVE the house + const nameText = new PIXI.Text(vm.name, { + fontFamily: 'Arial', + fontSize: 24, // Increased from 14 to 24 + fill: 0x333333, + fontWeight: 'bold', + }); + nameText.x = x; + // Position above the house (negative Y offset) + // House height depends on houseSize, offset by half that height plus padding + const houseHeight = (houseSize * HOUSE_RENDER_TILE_SIZE * baseScale * resourceScale); + nameText.y = y - houseHeight / 2 - 40; // Above the house + nameText.anchor.set(0.5); + this.buildingLayer.addChild(nameText); + + // Add animals representing usage (fixed 5 each, positioned at bottom-right of house) + let animals = []; + if (vm.state === 'Running') { + animals = this.createAnimals(vm, x, y, houseSize, HOUSE_RENDER_TILE_SIZE, baseScale * resourceScale); + } + + // Store ticker callbacks for this VM + this.tickerCallbacks.set(vm.name, callbacks); + + // Return house data object (using houseContainer instead of house) + return { + house: houseContainer, + nameText, + animals, + vm, + // Store house parameters for later use (when updating/reorganizing) + houseSize, + tileSize: HOUSE_RENDER_TILE_SIZE, + houseScale: baseScale * resourceScale + }; + } + + createAnimals(vm, houseX, houseY, houseSize, tileSize, houseScale) { + // Animals represent resource usage + // Chickens = memory usage (0-5 based on percentage) + // Cows = disk usage (0-5 based on percentage) + // Positioned to the right of the house + const animals = []; + const callbacks = this.tickerCallbacks.get(vm.name) || []; + + // Calculate number based on usage percentage + const memoryPercent = (vm.memory_used || 0) / (vm.memory_total || 1); + const diskPercent = (vm.disk_used || 0) / (vm.disk_total || 1); + + // Map percentage to 0-5 animals + // 0-10% = 0, 10-30% = 1, 30-50% = 2, 50-70% = 3, 70-90% = 4, 90-100% = 5 + const numChickens = Math.min(Math.floor(memoryPercent * 5 + 0.5), 5); + const numCows = Math.min(Math.floor(diskPercent * 5 + 0.5), 5); + + console.log('Creating animals:', { + vm: vm.name, + memory: `${(memoryPercent * 100).toFixed(1)}%`, + disk: `${(diskPercent * 100).toFixed(1)}%`, + chickens: numChickens, + cows: numCows + }); + + // Calculate right side position + // House is centered, so we need to offset from center + const houseWidth = houseSize * tileSize * houseScale; + const houseHeight = houseSize * tileSize * houseScale; + + // Right side of house, centered vertically + const rightX = houseX + houseWidth / 2 + 40; // 40px padding from house + const centerY = houseY; + + // Add chickens vertically stacked on the right side + for (let i = 0; i < numChickens; i++) { + const offsetY = -80 + i * 40; // Increased vertical spacing for bigger sprites + + const { chicken, callback } = this.createChicken(rightX, centerY + offsetY, i); + animals.push(chicken); + callbacks.push(callback); + } + + // Add cows vertically stacked on the right side (to the right of chickens) + for (let i = 0; i < numCows; i++) { + const offsetX = 80; // More space to the right of chickens + const offsetY = -100 + i * 50; // Increased vertical spacing (cows are bigger) + + const { cow, callback } = this.createCow(rightX + offsetX, centerY + offsetY, i); + animals.push(cow); + callbacks.push(callback); + } + + // Update callbacks for this VM + this.tickerCallbacks.set(vm.name, callbacks); + + return animals; + } + + createChicken(x, y, index) { + const chickenTexture = PIXI.Assets.get('chicken'); + const CHICKEN_FRAME_SIZE = 16; + + // Chicken spritesheet layout: + // Row 0: [idle] [blink] [blank] [blank] + // Row 1: [walk1] [walk2] [walk3] [walk4] + + // Create idle frame as default + const idleFrame = new PIXI.Rectangle(0, 0, CHICKEN_FRAME_SIZE, CHICKEN_FRAME_SIZE); + const chicken = new PIXI.Sprite(new PIXI.Texture({ + source: chickenTexture.source, + frame: idleFrame, + })); + + chicken.scale.set(3.0); // Bigger scale for visibility (3x from 16x16 = 48px) + chicken.x = x; + chicken.y = y; + chicken.anchor.set(0.5); + + // Animation state + chicken.userData = { + baseY: y, + time: Math.random() * Math.PI * 2, + speed: 0.002 + Math.random() * 0.001, + animFrame: 0, + animTime: Math.random() * 100 + }; + + // Callback handles both bobbing and sprite animation + const callback = () => { + chicken.userData.time += chicken.userData.speed; + chicken.y = chicken.userData.baseY + Math.sin(chicken.userData.time) * 2; + + // Occasionally blink or animate + chicken.userData.animTime++; + if (chicken.userData.animTime > 120) { // Every ~2 seconds + // Blink frame + const blinkFrame = new PIXI.Rectangle( + 1 * CHICKEN_FRAME_SIZE, + 0, + CHICKEN_FRAME_SIZE, + CHICKEN_FRAME_SIZE + ); + chicken.texture = new PIXI.Texture({ + source: chickenTexture.source, + frame: blinkFrame, + }); + + // Reset after a short time + setTimeout(() => { + const idleFrame = new PIXI.Rectangle(0, 0, CHICKEN_FRAME_SIZE, CHICKEN_FRAME_SIZE); + chicken.texture = new PIXI.Texture({ + source: chickenTexture.source, + frame: idleFrame, + }); + }, 100); + + chicken.userData.animTime = 0; + } + }; + + this.app.ticker.add(callback); + this.animalLayer.addChild(chicken); + + return { chicken, callback }; + } + + createCow(x, y, index) { + const cowTexture = PIXI.Assets.get('cow'); + const COW_FRAME_SIZE = 32; // Cows are 32x32 pixels (larger than chickens) + + // Cow spritesheet layout: + // Row 0: [idle1] [blink] [idle2] + // Row 1: [walk1] [walk2] [blank] [blank] + + // Alternate between idle1 and idle2 for variety + const idleCol = index % 2 === 0 ? 0 : 2; + const idleFrame = new PIXI.Rectangle( + idleCol * COW_FRAME_SIZE, + 0, + COW_FRAME_SIZE, + COW_FRAME_SIZE + ); + const cow = new PIXI.Sprite(new PIXI.Texture({ + source: cowTexture.source, + frame: idleFrame, + })); + + cow.scale.set(2.0); // Bigger scale for visibility (2x from 32x32 = 64px) + cow.x = x; + cow.y = y; + cow.anchor.set(0.5); + + // Animation state + cow.userData = { + baseX: x, + time: Math.random() * Math.PI * 2, + speed: 0.001 + Math.random() * 0.0005, + idleCol: idleCol, // Remember which idle frame we're using + animTime: Math.random() * 150 + }; + + // Callback handles both swaying and sprite animation + const callback = () => { + cow.userData.time += cow.userData.speed; + cow.x = cow.userData.baseX + Math.sin(cow.userData.time) * 2; + + // Occasionally blink + cow.userData.animTime++; + if (cow.userData.animTime > 180) { // Every ~3 seconds + // Blink frame (col 1, row 0) + const blinkFrame = new PIXI.Rectangle( + 1 * COW_FRAME_SIZE, + 0, + COW_FRAME_SIZE, + COW_FRAME_SIZE + ); + cow.texture = new PIXI.Texture({ + source: cowTexture.source, + frame: blinkFrame, + }); + + // Reset back to original idle frame after a short time + setTimeout(() => { + const idleFrame = new PIXI.Rectangle( + cow.userData.idleCol * COW_FRAME_SIZE, + 0, + COW_FRAME_SIZE, + COW_FRAME_SIZE + ); + cow.texture = new PIXI.Texture({ + source: cowTexture.source, + frame: idleFrame, + }); + }, 150); + + cow.userData.animTime = 0; + } + }; + + this.app.ticker.add(callback); + this.animalLayer.addChild(cow); + + return { cow, callback }; + } + + updateInfoPanel() { + const statsDiv = document.getElementById('vm-stats'); + const vms = this.stateManager.getVMs(); + + if (vms.length === 0) { + statsDiv.innerHTML = '

No VMs running. Launch one to see your village grow!

'; + return; + } + + let html = '
'; + html += `
Total VMs:${vms.length}
`; + + const runningVMs = vms.filter(vm => vm.state === 'Running').length; + html += `
Running:${runningVMs}
`; + + html += '
'; + html += '

🐔 Chickens = Memory Usage

'; + html += '

🐄 Cows = Disk Usage

'; + html += '
'; + + html += '
'; + statsDiv.innerHTML = html; + } + + update() { + // Smooth update loop + // Additional animations and effects can be added here + } +} + +// Initialize the village when the page loads +window.addEventListener('load', async () => { + // Create state manager + const stateManager = new VMStateManager(); + + // Create village with state manager + const village = new SafePawVillage(stateManager); + await village.init(); +}); diff --git a/ui/assets/character/actions.png b/ui/assets/character/actions.png new file mode 100644 index 0000000..4c2c182 Binary files /dev/null and b/ui/assets/character/actions.png differ diff --git a/ui/assets/character/chicken.png b/ui/assets/character/chicken.png new file mode 100644 index 0000000..21b3c28 Binary files /dev/null and b/ui/assets/character/chicken.png differ diff --git a/ui/assets/character/cow.png b/ui/assets/character/cow.png new file mode 100644 index 0000000..de23d8a Binary files /dev/null and b/ui/assets/character/cow.png differ diff --git a/ui/assets/character/default_spritesheet.png b/ui/assets/character/default_spritesheet.png new file mode 100644 index 0000000..4776cb4 Binary files /dev/null and b/ui/assets/character/default_spritesheet.png differ diff --git a/ui/assets/tiles/door.png b/ui/assets/tiles/door.png new file mode 100644 index 0000000..8342d98 Binary files /dev/null and b/ui/assets/tiles/door.png differ diff --git a/ui/assets/tiles/fences.png b/ui/assets/tiles/fences.png new file mode 100644 index 0000000..0b2825b Binary files /dev/null and b/ui/assets/tiles/fences.png differ diff --git a/ui/assets/tiles/grass.png b/ui/assets/tiles/grass.png new file mode 100644 index 0000000..baea0eb Binary files /dev/null and b/ui/assets/tiles/grass.png differ diff --git a/ui/assets/tiles/hills.png b/ui/assets/tiles/hills.png new file mode 100644 index 0000000..c578c06 Binary files /dev/null and b/ui/assets/tiles/hills.png differ diff --git a/ui/assets/tiles/paths.png b/ui/assets/tiles/paths.png new file mode 100644 index 0000000..67533ec Binary files /dev/null and b/ui/assets/tiles/paths.png differ diff --git a/ui/assets/tiles/plants.png b/ui/assets/tiles/plants.png new file mode 100644 index 0000000..ae0bb83 Binary files /dev/null and b/ui/assets/tiles/plants.png differ diff --git a/ui/assets/tiles/plants2.png b/ui/assets/tiles/plants2.png new file mode 100644 index 0000000..86375eb Binary files /dev/null and b/ui/assets/tiles/plants2.png differ diff --git a/ui/assets/tiles/water.png b/ui/assets/tiles/water.png new file mode 100644 index 0000000..7969ff3 Binary files /dev/null and b/ui/assets/tiles/water.png differ diff --git a/ui/assets/tiles/wood_bridge.png b/ui/assets/tiles/wood_bridge.png new file mode 100644 index 0000000..453e80d Binary files /dev/null and b/ui/assets/tiles/wood_bridge.png differ diff --git a/ui/assets/tiles/wood_house_full.png b/ui/assets/tiles/wood_house_full.png new file mode 100644 index 0000000..e4799bc Binary files /dev/null and b/ui/assets/tiles/wood_house_full.png differ diff --git a/ui/assets/tiles/wood_house_walls.png b/ui/assets/tiles/wood_house_walls.png new file mode 100644 index 0000000..a8c31bc Binary files /dev/null and b/ui/assets/tiles/wood_house_walls.png differ diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 0000000..051b826 --- /dev/null +++ b/ui/index.html @@ -0,0 +1,136 @@ + + + + + + SafePaw Village + + + +
+
+

Loading SafePaw Village...

+
+ + + +
+ + + + + + diff --git a/ui/pixi.min@v8.16.0.js b/ui/pixi.min@v8.16.0.js new file mode 100644 index 0000000..ad3348f --- /dev/null +++ b/ui/pixi.min@v8.16.0.js @@ -0,0 +1,2314 @@ +var hI=Object.defineProperty;var zT=Object.getOwnPropertySymbols;var dI=Object.prototype.hasOwnProperty,pI=Object.prototype.propertyIsEnumerable;var WT=(d,te,ee)=>te in d?hI(d,te,{enumerable:!0,configurable:!0,writable:!0,value:ee}):d[te]=ee,VT=(d,te)=>{for(var ee in te||(te={}))dI.call(te,ee)&&WT(d,ee,te[ee]);if(zT)for(var ee of zT(te))pI.call(te,ee)&&WT(d,ee,te[ee]);return d};/*! + * PixiJS - v8.16.0 + * Compiled Tue, 03 Feb 2026 16:55:39 UTC + * + * PixiJS is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */var PIXI=(function(d){"use strict";"use strict";var te=Object.defineProperty,ee=Object.defineProperties,E1=Object.getOwnPropertyDescriptors,vh=Object.getOwnPropertySymbols,P1=Object.prototype.hasOwnProperty,A1=Object.prototype.propertyIsEnumerable,yh=(r,t,e)=>t in r?te(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,xh=(r,t)=>{for(var e in t||(t={}))P1.call(t,e)&&yh(r,e,t[e]);if(vh)for(var e of vh(t))A1.call(t,e)&&yh(r,e,t[e]);return r},C1=(r,t)=>ee(r,E1(t)),T=(r=>(r.Application="application",r.WebGLPipes="webgl-pipes",r.WebGLPipesAdaptor="webgl-pipes-adaptor",r.WebGLSystem="webgl-system",r.WebGPUPipes="webgpu-pipes",r.WebGPUPipesAdaptor="webgpu-pipes-adaptor",r.WebGPUSystem="webgpu-system",r.CanvasSystem="canvas-system",r.CanvasPipesAdaptor="canvas-pipes-adaptor",r.CanvasPipes="canvas-pipes",r.Asset="asset",r.LoadParser="load-parser",r.ResolveParser="resolve-parser",r.CacheParser="cache-parser",r.DetectionParser="detection-parser",r.MaskEffect="mask-effect",r.BlendMode="blend-mode",r.TextureSource="texture-source",r.Environment="environment",r.ShapeBuilder="shape-builder",r.Batcher="batcher",r))(T||{});const ma=r=>{if(typeof r=="function"||typeof r=="object"&&r.extension){const t=typeof r.extension!="object"?{type:r.extension}:r.extension;r=C1(xh({},t),{ref:r})}if(typeof r=="object")r=xh({},r);else throw new Error("Invalid extension type");return typeof r.type=="string"&&(r.type=[r.type]),r},qr=(r,t)=>{var e;return(e=ma(r).priority)!=null?e:t},$={_addHandlers:{},_removeHandlers:{},_queue:{},remove(...r){return r.map(ma).forEach(t=>{t.type.forEach(e=>{var i,s;return(s=(i=this._removeHandlers)[e])==null?void 0:s.call(i,t)})}),this},add(...r){return r.map(ma).forEach(t=>{t.type.forEach(e=>{var i,s;const n=this._addHandlers,a=this._queue;n[e]?(s=n[e])==null||s.call(n,t):(a[e]=a[e]||[],(i=a[e])==null||i.push(t))})}),this},handle(r,t,e){var i;const s=this._addHandlers,n=this._removeHandlers;s[r]=t,n[r]=e;const a=this._queue;return a[r]&&((i=a[r])==null||i.forEach(o=>t(o)),delete a[r]),this},handleByMap(r,t){return this.handle(r,e=>{e.name&&(t[e.name]=e.ref)},e=>{e.name&&delete t[e.name]})},handleByNamedList(r,t,e=-1){return this.handle(r,i=>{t.findIndex(s=>s.name===i.name)>=0||(t.push({name:i.name,value:i.ref}),t.sort((s,n)=>qr(n.value,e)-qr(s.value,e)))},i=>{const s=t.findIndex(n=>n.name===i.name);s!==-1&&t.splice(s,1)})},handleByList(r,t,e=-1){return this.handle(r,i=>{t.includes(i.ref)||(t.push(i.ref),t.sort((s,n)=>qr(n,e)-qr(s,e)))},i=>{const s=t.indexOf(i.ref);s!==-1&&t.splice(s,1)})},mixin(r,...t){for(const e of t)Object.defineProperties(r.prototype,Object.getOwnPropertyDescriptors(e))}};var fI=typeof globalThis!="undefined"?globalThis:typeof window!="undefined"?window:typeof global!="undefined"?global:typeof self!="undefined"?self:{};function Th(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}function mI(r){return r&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}function gI(r){return r&&Object.prototype.hasOwnProperty.call(r,"default")&&Object.keys(r).length===1?r.default:r}function _I(r){if(Object.prototype.hasOwnProperty.call(r,"__esModule"))return r;var t=r.default;if(typeof t=="function"){var e=function i(){var s=!1;try{s=this instanceof i}catch(n){}return s?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};e.prototype=t.prototype}else e={};return Object.defineProperty(e,"__esModule",{value:!0}),Object.keys(r).forEach(function(i){var s=Object.getOwnPropertyDescriptor(r,i);Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:function(){return r[i]}})}),e}var Vi={exports:{}},bI=Vi.exports,Sh;function R1(){return Sh||(Sh=1,(function(r){"use strict";var t=Object.prototype.hasOwnProperty,e="~";function i(){}Object.create&&(i.prototype=Object.create(null),new i().__proto__||(e=!1));function s(l,u,c){this.fn=l,this.context=u,this.once=c||!1}function n(l,u,c,h,p){if(typeof c!="function")throw new TypeError("The listener must be a function");var f=new s(c,h||l,p),m=e?e+u:u;return l._events[m]?l._events[m].fn?l._events[m]=[l._events[m],f]:l._events[m].push(f):(l._events[m]=f,l._eventsCount++),l}function a(l,u){--l._eventsCount===0?l._events=new i:delete l._events[u]}function o(){this._events=new i,this._eventsCount=0}o.prototype.eventNames=function(){var u=[],c,h;if(this._eventsCount===0)return u;for(h in c=this._events)t.call(c,h)&&u.push(e?h.slice(1):h);return Object.getOwnPropertySymbols?u.concat(Object.getOwnPropertySymbols(c)):u},o.prototype.listeners=function(u){var c=e?e+u:u,h=this._events[c];if(!h)return[];if(h.fn)return[h.fn];for(var p=0,f=h.length,m=new Array(f);p0:typeof r=="number"},Rt=function(r,t,e){return t===void 0&&(t=0),e===void 0&&(e=Math.pow(10,t)),Math.round(e*r)/e+0},re=function(r,t,e){return t===void 0&&(t=0),e===void 0&&(e=1),r>e?e:r>t?r:t},wh=function(r){return(r=isFinite(r)?r%360:0)>0?r:r+360},Eh=function(r){return{r:re(r.r,0,255),g:re(r.g,0,255),b:re(r.b,0,255),a:re(r.a)}},ga=function(r){return{r:Rt(r.r),g:Rt(r.g),b:Rt(r.b),a:Rt(r.a,3)}},G1=/^#([0-9a-f]{3,8})$/i,Yi=function(r){var t=r.toString(16);return t.length<2?"0"+t:t},Ph=function(r){var t=r.r,e=r.g,i=r.b,s=r.a,n=Math.max(t,e,i),a=n-Math.min(t,e,i),o=a?n===t?(e-i)/a:n===e?2+(i-t)/a:4+(t-e)/a:0;return{h:60*(o<0?o+6:o),s:n?a/n*100:0,v:n/255*100,a:s}},Ah=function(r){var t=r.h,e=r.s,i=r.v,s=r.a;t=t/360*6,e/=100,i/=100;var n=Math.floor(t),a=i*(1-e),o=i*(1-(t-n)*e),l=i*(1-(1-t+n)*e),u=n%6;return{r:255*[i,o,a,a,l,i][u],g:255*[l,i,i,o,a,a][u],b:255*[a,a,l,i,i,o][u],a:s}},Ch=function(r){return{h:wh(r.h),s:re(r.s,0,100),l:re(r.l,0,100),a:re(r.a)}},Rh=function(r){return{h:Rt(r.h),s:Rt(r.s),l:Rt(r.l),a:Rt(r.a,3)}},Mh=function(r){return Ah((e=(t=r).s,{h:t.h,s:(e*=((i=t.l)<50?i:100-i)/100)>0?2*e/(i+e)*100:0,v:i+e,a:t.a}));var t,e,i},Zr=function(r){return{h:(t=Ph(r)).h,s:(s=(200-(e=t.s))*(i=t.v)/100)>0&&s<200?e*i/100/(s<=100?s:200-s)*100:0,l:s/2,a:t.a};var t,e,i,s},I1=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,B1=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,F1=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,D1=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,_a={string:[[function(r){var t=G1.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:r.length===4?Rt(parseInt(r[3]+r[3],16)/255,2):1}:r.length===6||r.length===8?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:r.length===8?Rt(parseInt(r.substr(6,2),16)/255,2):1}:null:null},"hex"],[function(r){var t=F1.exec(r)||D1.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:Eh({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:t[7]===void 0?1:Number(t[7])/(t[8]?100:1)}):null},"rgb"],[function(r){var t=I1.exec(r)||B1.exec(r);if(!t)return null;var e,i,s=Ch({h:(e=t[1],i=t[2],i===void 0&&(i="deg"),Number(e)*(O1[i]||1)),s:Number(t[3]),l:Number(t[4]),a:t[5]===void 0?1:Number(t[5])/(t[6]?100:1)});return Mh(s)},"hsl"]],object:[[function(r){var t=r.r,e=r.g,i=r.b,s=r.a,n=s===void 0?1:s;return Ae(t)&&Ae(e)&&Ae(i)?Eh({r:Number(t),g:Number(e),b:Number(i),a:Number(n)}):null},"rgb"],[function(r){var t=r.h,e=r.s,i=r.l,s=r.a,n=s===void 0?1:s;if(!Ae(t)||!Ae(e)||!Ae(i))return null;var a=Ch({h:Number(t),s:Number(e),l:Number(i),a:Number(n)});return Mh(a)},"hsl"],[function(r){var t=r.h,e=r.s,i=r.v,s=r.a,n=s===void 0?1:s;if(!Ae(t)||!Ae(e)||!Ae(i))return null;var a=(function(o){return{h:wh(o.h),s:re(o.s,0,100),v:re(o.v,0,100),a:re(o.a)}})({h:Number(t),s:Number(e),v:Number(i),a:Number(n)});return Ah(a)},"hsv"]]},Oh=function(r,t){for(var e=0;e=.5},r.prototype.toHex=function(){return t=ga(this.rgba),e=t.r,i=t.g,s=t.b,a=(n=t.a)<1?Yi(Rt(255*n)):"","#"+Yi(e)+Yi(i)+Yi(s)+a;var t,e,i,s,n,a},r.prototype.toRgb=function(){return ga(this.rgba)},r.prototype.toRgbString=function(){return t=ga(this.rgba),e=t.r,i=t.g,s=t.b,(n=t.a)<1?"rgba("+e+", "+i+", "+s+", "+n+")":"rgb("+e+", "+i+", "+s+")";var t,e,i,s,n},r.prototype.toHsl=function(){return Rh(Zr(this.rgba))},r.prototype.toHslString=function(){return t=Rh(Zr(this.rgba)),e=t.h,i=t.s,s=t.l,(n=t.a)<1?"hsla("+e+", "+i+"%, "+s+"%, "+n+")":"hsl("+e+", "+i+"%, "+s+"%)";var t,e,i,s,n},r.prototype.toHsv=function(){return t=Ph(this.rgba),{h:Rt(t.h),s:Rt(t.s),v:Rt(t.v),a:Rt(t.a,3)};var t},r.prototype.invert=function(){return ge({r:255-(t=this.rgba).r,g:255-t.g,b:255-t.b,a:t.a});var t},r.prototype.saturate=function(t){return t===void 0&&(t=.1),ge(ba(this.rgba,t))},r.prototype.desaturate=function(t){return t===void 0&&(t=.1),ge(ba(this.rgba,-t))},r.prototype.grayscale=function(){return ge(ba(this.rgba,-1))},r.prototype.lighten=function(t){return t===void 0&&(t=.1),ge(Ih(this.rgba,t))},r.prototype.darken=function(t){return t===void 0&&(t=.1),ge(Ih(this.rgba,-t))},r.prototype.rotate=function(t){return t===void 0&&(t=15),this.hue(this.hue()+t)},r.prototype.alpha=function(t){return typeof t=="number"?ge({r:(e=this.rgba).r,g:e.g,b:e.b,a:t}):Rt(this.rgba.a,3);var e},r.prototype.hue=function(t){var e=Zr(this.rgba);return typeof t=="number"?ge({h:t,s:e.s,l:e.l,a:e.a}):Rt(e.h)},r.prototype.isEqual=function(t){return this.toHex()===ge(t).toHex()},r})(),ge=function(r){return r instanceof Ki?r:new Ki(r)},Bh=[],U1=function(r){r.forEach(function(t){Bh.indexOf(t)<0&&(t(Ki,_a),Bh.push(t))})},yI=function(){return new Ki({r:255*Math.random(),g:255*Math.random(),b:255*Math.random()})};function k1(r,t){var e={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"},i={};for(var s in e)i[e[s]]=s;var n={};r.prototype.toName=function(a){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return"transparent";var o,l,u=i[this.toHex()];if(u)return u;if(a!=null&&a.closest){var c=this.toRgb(),h=1/0,p="black";if(!n.length)for(var f in e)n[f]=new r(e[f]).toRgb();for(var m in e){var g=(o=c,l=n[m],Math.pow(o.r-l.r,2)+Math.pow(o.g-l.g,2)+Math.pow(o.b-l.b,2));gt in r?$1(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,X1=(r,t)=>{for(var e in t||(t={}))L1.call(t,e)&&Dh(r,e,t[e]);if(Fh)for(var e of Fh(t))N1.call(t,e)&&Dh(r,e,t[e]);return r};U1([k1]);const fr=class zi{constructor(t=16777215){this._value=null,this._components=new Float32Array(4),this._components.fill(1),this._int=16777215,this.value=t}get red(){return this._components[0]}get green(){return this._components[1]}get blue(){return this._components[2]}get alpha(){return this._components[3]}setValue(t){return this.value=t,this}set value(t){if(t instanceof zi)this._value=this._cloneSource(t._value),this._int=t._int,this._components.set(t._components);else{if(t===null)throw new Error("Cannot set Color#value to null");(this._value===null||!this._isSourceEqual(this._value,t))&&(this._value=this._cloneSource(t),this._normalize(this._value))}}get value(){return this._value}_cloneSource(t){return typeof t=="string"||typeof t=="number"||t instanceof Number||t===null?t:Array.isArray(t)||ArrayBuffer.isView(t)?t.slice(0):typeof t=="object"&&t!==null?X1({},t):t}_isSourceEqual(t,e){const i=typeof t;if(i!==typeof e)return!1;if(i==="number"||i==="string"||t instanceof Number)return t===e;if(Array.isArray(t)&&Array.isArray(e)||ArrayBuffer.isView(t)&&ArrayBuffer.isView(e))return t.length!==e.length?!1:t.every((s,n)=>s===e[n]);if(t!==null&&e!==null){const s=Object.keys(t),n=Object.keys(e);return s.length!==n.length?!1:s.every(a=>t[a]===e[a])}return t===e}toRgba(){const[t,e,i,s]=this._components;return{r:t,g:e,b:i,a:s}}toRgb(){const[t,e,i]=this._components;return{r:t,g:e,b:i}}toRgbaString(){const[t,e,i]=this.toUint8RgbArray();return`rgba(${t},${e},${i},${this.alpha})`}toUint8RgbArray(t){const[e,i,s]=this._components;return this._arrayRgb||(this._arrayRgb=[]),t||(t=this._arrayRgb),t[0]=Math.round(e*255),t[1]=Math.round(i*255),t[2]=Math.round(s*255),t}toArray(t){this._arrayRgba||(this._arrayRgba=[]),t||(t=this._arrayRgba);const[e,i,s,n]=this._components;return t[0]=e,t[1]=i,t[2]=s,t[3]=n,t}toRgbArray(t){this._arrayRgb||(this._arrayRgb=[]),t||(t=this._arrayRgb);const[e,i,s]=this._components;return t[0]=e,t[1]=i,t[2]=s,t}toNumber(){return this._int}toBgrNumber(){const[t,e,i]=this.toUint8RgbArray();return(i<<16)+(e<<8)+t}toLittleEndianNumber(){const t=this._int;return(t>>16)+(t&65280)+((t&255)<<16)}multiply(t){const[e,i,s,n]=zi._temp.setValue(t)._components;return this._components[0]*=e,this._components[1]*=i,this._components[2]*=s,this._components[3]*=n,this._refreshInt(),this._value=null,this}premultiply(t,e=!0){return e&&(this._components[0]*=t,this._components[1]*=t,this._components[2]*=t),this._components[3]=t,this._refreshInt(),this._value=null,this}toPremultiplied(t,e=!0){if(t===1)return(255<<24)+this._int;if(t===0)return e?0:this._int;let i=this._int>>16&255,s=this._int>>8&255,n=this._int&255;return e&&(i=i*t+.5|0,s=s*t+.5|0,n=n*t+.5|0),(t*255<<24)+(i<<16)+(s<<8)+n}toHex(){const t=this._int.toString(16);return`#${"000000".substring(0,6-t.length)+t}`}toHexa(){const t=Math.round(this._components[3]*255).toString(16);return this.toHex()+"00".substring(0,2-t.length)+t}setAlpha(t){return this._components[3]=this._clamp(t),this}_normalize(t){let e,i,s,n;if((typeof t=="number"||t instanceof Number)&&t>=0&&t<=16777215){const a=t;e=(a>>16&255)/255,i=(a>>8&255)/255,s=(a&255)/255,n=1}else if((Array.isArray(t)||t instanceof Float32Array)&&t.length>=3&&t.length<=4)t=this._clamp(t),[e,i,s,n=1]=t;else if((t instanceof Uint8Array||t instanceof Uint8ClampedArray)&&t.length>=3&&t.length<=4)t=this._clamp(t,0,255),[e,i,s,n=255]=t,e/=255,i/=255,s/=255,n/=255;else if(typeof t=="string"||typeof t=="object"){if(typeof t=="string"){const o=zi.HEX_PATTERN.exec(t);o&&(t=`#${o[2]}`)}const a=ge(t);a.isValid()&&({r:e,g:i,b:s,a:n}=a.rgba,e/=255,i/=255,s/=255)}if(e!==void 0)this._components[0]=e,this._components[1]=i,this._components[2]=s,this._components[3]=n,this._refreshInt();else throw new Error(`Unable to convert color ${t}`)}_refreshInt(){this._clamp(this._components);const[t,e,i]=this._components;this._int=(t*255<<16)+(e*255<<8)+(i*255|0)}_clamp(t,e=0,i=1){return typeof t=="number"?Math.min(Math.max(t,e),i):(t.forEach((s,n)=>{t[n]=Math.min(Math.max(s,e),i)}),t)}static isColorLike(t){return typeof t=="number"||typeof t=="string"||t instanceof Number||t instanceof zi||Array.isArray(t)||t instanceof Uint8Array||t instanceof Uint8ClampedArray||t instanceof Float32Array||t.r!==void 0&&t.g!==void 0&&t.b!==void 0||t.r!==void 0&&t.g!==void 0&&t.b!==void 0&&t.a!==void 0||t.h!==void 0&&t.s!==void 0&&t.l!==void 0||t.h!==void 0&&t.s!==void 0&&t.l!==void 0&&t.a!==void 0||t.h!==void 0&&t.s!==void 0&&t.v!==void 0||t.h!==void 0&&t.s!==void 0&&t.v!==void 0&&t.a!==void 0}};fr.shared=new fr,fr._temp=new fr,fr.HEX_PATTERN=/^(#|0x)?(([a-f0-9]{3}){1,2}([a-f0-9]{2})?)$/i;let J=fr;const Uh={cullArea:null,cullable:!1,cullableChildren:!0},kh=Math.PI*2,$h=180/Math.PI,Lh=Math.PI/180;class rt{constructor(t=0,e=0){this.x=0,this.y=0,this.x=t,this.y=e}clone(){return new rt(this.x,this.y)}copyFrom(t){return this.set(t.x,t.y),this}copyTo(t){return t.set(this.x,this.y),t}equals(t){return t.x===this.x&&t.y===this.y}set(t=0,e=t){return this.x=t,this.y=e,this}static get shared(){return ya.x=0,ya.y=0,ya}}const ya=new rt;class D{constructor(t=1,e=0,i=0,s=1,n=0,a=0){this.array=null,this.a=t,this.b=e,this.c=i,this.d=s,this.tx=n,this.ty=a}fromArray(t){this.a=t[0],this.b=t[1],this.c=t[3],this.d=t[4],this.tx=t[2],this.ty=t[5]}set(t,e,i,s,n,a){return this.a=t,this.b=e,this.c=i,this.d=s,this.tx=n,this.ty=a,this}toArray(t,e){this.array||(this.array=new Float32Array(9));const i=e||this.array;return t?(i[0]=this.a,i[1]=this.b,i[2]=0,i[3]=this.c,i[4]=this.d,i[5]=0,i[6]=this.tx,i[7]=this.ty,i[8]=1):(i[0]=this.a,i[1]=this.c,i[2]=this.tx,i[3]=this.b,i[4]=this.d,i[5]=this.ty,i[6]=0,i[7]=0,i[8]=1),i}apply(t,e){e=e||new rt;const i=t.x,s=t.y;return e.x=this.a*i+this.c*s+this.tx,e.y=this.b*i+this.d*s+this.ty,e}applyInverse(t,e){e=e||new rt;const i=this.a,s=this.b,n=this.c,a=this.d,o=this.tx,l=this.ty,u=1/(i*a+n*-s),c=t.x,h=t.y;return e.x=a*u*c+-n*u*h+(l*n-o*a)*u,e.y=i*u*h+-s*u*c+(-l*i+o*s)*u,e}translate(t,e){return this.tx+=t,this.ty+=e,this}scale(t,e){return this.a*=t,this.d*=e,this.c*=t,this.b*=e,this.tx*=t,this.ty*=e,this}rotate(t){const e=Math.cos(t),i=Math.sin(t),s=this.a,n=this.c,a=this.tx;return this.a=s*e-this.b*i,this.b=s*i+this.b*e,this.c=n*e-this.d*i,this.d=n*i+this.d*e,this.tx=a*e-this.ty*i,this.ty=a*i+this.ty*e,this}append(t){const e=this.a,i=this.b,s=this.c,n=this.d;return this.a=t.a*e+t.b*s,this.b=t.a*i+t.b*n,this.c=t.c*e+t.d*s,this.d=t.c*i+t.d*n,this.tx=t.tx*e+t.ty*s+this.tx,this.ty=t.tx*i+t.ty*n+this.ty,this}appendFrom(t,e){const i=t.a,s=t.b,n=t.c,a=t.d,o=t.tx,l=t.ty,u=e.a,c=e.b,h=e.c,p=e.d;return this.a=i*u+s*h,this.b=i*c+s*p,this.c=n*u+a*h,this.d=n*c+a*p,this.tx=o*u+l*h+e.tx,this.ty=o*c+l*p+e.ty,this}setTransform(t,e,i,s,n,a,o,l,u){return this.a=Math.cos(o+u)*n,this.b=Math.sin(o+u)*n,this.c=-Math.sin(o-l)*a,this.d=Math.cos(o-l)*a,this.tx=t-(i*this.a+s*this.c),this.ty=e-(i*this.b+s*this.d),this}prepend(t){const e=this.tx;if(t.a!==1||t.b!==0||t.c!==0||t.d!==1){const i=this.a,s=this.c;this.a=i*t.a+this.b*t.c,this.b=i*t.b+this.b*t.d,this.c=s*t.a+this.d*t.c,this.d=s*t.b+this.d*t.d}return this.tx=e*t.a+this.ty*t.c+t.tx,this.ty=e*t.b+this.ty*t.d+t.ty,this}decompose(t){const e=this.a,i=this.b,s=this.c,n=this.d,a=t.pivot,o=-Math.atan2(-s,n),l=Math.atan2(i,e),u=Math.abs(o+l);return u<1e-5||Math.abs(kh-u)<1e-5?(t.rotation=l,t.skew.x=t.skew.y=0):(t.rotation=0,t.skew.x=o,t.skew.y=l),t.scale.x=Math.sqrt(e*e+i*i),t.scale.y=Math.sqrt(s*s+n*n),t.position.x=this.tx+(a.x*e+a.y*s),t.position.y=this.ty+(a.x*i+a.y*n),t}invert(){const t=this.a,e=this.b,i=this.c,s=this.d,n=this.tx,a=t*s-e*i;return this.a=s/a,this.b=-e/a,this.c=-i/a,this.d=t/a,this.tx=(i*this.ty-s*n)/a,this.ty=-(t*this.ty-e*n)/a,this}isIdentity(){return this.a===1&&this.b===0&&this.c===0&&this.d===1&&this.tx===0&&this.ty===0}identity(){return this.a=1,this.b=0,this.c=0,this.d=1,this.tx=0,this.ty=0,this}clone(){const t=new D;return t.a=this.a,t.b=this.b,t.c=this.c,t.d=this.d,t.tx=this.tx,t.ty=this.ty,t}copyTo(t){return t.a=this.a,t.b=this.b,t.c=this.c,t.d=this.d,t.tx=this.tx,t.ty=this.ty,t}copyFrom(t){return this.a=t.a,this.b=t.b,this.c=t.c,this.d=t.d,this.tx=t.tx,this.ty=t.ty,this}equals(t){return t.a===this.a&&t.b===this.b&&t.c===this.c&&t.d===this.d&&t.tx===this.tx&&t.ty===this.ty}static get IDENTITY(){return j1.identity()}static get shared(){return H1.identity()}}const H1=new D,j1=new D;class gt{constructor(t,e,i){this._x=e||0,this._y=i||0,this._observer=t}clone(t){return new gt(t!=null?t:this._observer,this._x,this._y)}set(t=0,e=t){return(this._x!==t||this._y!==e)&&(this._x=t,this._y=e,this._observer._onUpdate(this)),this}copyFrom(t){return(this._x!==t.x||this._y!==t.y)&&(this._x=t.x,this._y=t.y,this._observer._onUpdate(this)),this}copyTo(t){return t.set(this._x,this._y),t}equals(t){return t.x===this._x&&t.y===this._y}get x(){return this._x}set x(t){this._x!==t&&(this._x=t,this._observer._onUpdate(this))}get y(){return this._y}set y(t){this._y!==t&&(this._y=t,this._observer._onUpdate(this))}}const Qr={default:-1};function nt(r="default"){return Qr[r]===void 0&&(Qr[r]=-1),++Qr[r]}function z1(){for(const r in Qr)delete Qr[r]}const je={_registeredResources:new Set,register(r){this._registeredResources.add(r)},unregister(r){this._registeredResources.delete(r)},release(){this._registeredResources.forEach(r=>r.clear())},get registeredCount(){return this._registeredResources.size},isRegistered(r){return this._registeredResources.has(r)},reset(){this._registeredResources.clear()}};class Nh{constructor(t,e){this._pool=[],this._count=0,this._index=0,this._classType=t,e&&this.prepopulate(e)}prepopulate(t){for(let e=0;e0?i=this._pool[--this._index]:(i=new this._classType,this._count++),(e=i.init)==null||e.call(i,t),i}return(t){var e;(e=t.reset)==null||e.call(t),this._pool[this._index++]=t}get totalSize(){return this._count}get totalFree(){return this._index}get totalUsed(){return this._count-this._index}clear(){if(this._pool.length>0&&this._pool[0].destroy)for(let t=0;t{const i=t[e._classType.name]?e._classType.name+e._classType.ID:e._classType.name;t[i]={free:e.totalFree,used:e.totalUsed,size:e.totalSize}}),t}clear(){this._poolsByClass.forEach(t=>t.clear()),this._poolsByClass.clear()}}const St=new Xh;je.register(St);const Hh={get isCachedAsTexture(){var r;return!!((r=this.renderGroup)!=null&&r.isCachedAsTexture)},cacheAsTexture(r){typeof r=="boolean"&&r===!1?this.disableRenderGroup():(this.enableRenderGroup(),this.renderGroup.enableCacheAsTexture(r===!0?{}:r))},updateCacheTexture(){var r;(r=this.renderGroup)==null||r.updateCacheTexture()},get cacheAsBitmap(){return this.isCachedAsTexture},set cacheAsBitmap(r){this.cacheAsTexture(r)}};function xa(r,t,e){const i=r.length;let s;if(t>=i||e===0)return;e=t+e>i?i-t:e;const n=i-e;for(s=t;s0&&s<=i){for(let o=i-1;o>=r;o--){const l=this.children[o];l&&(n.push(l),l.parent=null)}xa(this.children,r,i);const a=this.renderGroup||this.parentRenderGroup;a&&a.removeChildren(n);for(let o=0;o0&&this._didViewChangeTick++,n}else if(s===0&&this.children.length===0)return n;throw new RangeError("removeChildren: numeric values are outside the acceptable range.")},removeChildAt(r){const t=this.getChildAt(r);return this.removeChild(t)},getChildAt(r){if(r<0||r>=this.children.length)throw new Error(`getChildAt: Index (${r}) does not exist.`);return this.children[r]},setChildIndex(r,t){if(t<0||t>=this.children.length)throw new Error(`The index ${t} supplied is out of bounds ${this.children.length}`);this.getChildIndex(r),this.addChildAt(r,t)},getChildIndex(r){const t=this.children.indexOf(r);if(t===-1)throw new Error("The supplied Container must be a child of the caller");return t},addChildAt(r,t){const{children:e}=this;if(t<0||t>e.length)throw new Error(`${r}addChildAt: The index ${t} supplied is out of bounds ${e.length}`);if(r.parent){const s=r.parent.children.indexOf(r);if(r.parent===this&&s===t)return r;s!==-1&&r.parent.children.splice(s,1)}t===e.length?e.push(r):e.splice(t,0,r),r.parent=this,r.didChange=!0,r._updateFlags=15;const i=this.renderGroup||this.parentRenderGroup;return i&&i.addChild(r),this.sortableChildren&&(this.sortDirty=!0),this.emit("childAdded",r,this,t),r.emit("added",this),r},swapChildren(r,t){if(r===t)return;const e=this.getChildIndex(r),i=this.getChildIndex(t);this.children[e]=t,this.children[i]=r;const s=this.renderGroup||this.parentRenderGroup;s&&(s.structureDidChange=!0),this._didContainerChangeTick++},removeFromParent(){var r;(r=this.parent)==null||r.removeChild(this)},reparentChild(...r){return r.length===1?this.reparentChildAt(r[0],this.children.length):(r.forEach(t=>this.reparentChildAt(t,this.children.length)),r[0])},reparentChildAt(r,t){if(r.parent===this)return this.setChildIndex(r,t),r;const e=r.worldTransform.clone();r.removeFromParent(),this.addChildAt(r,t);const i=this.worldTransform.clone();return i.invert(),e.prepend(i),r.setFromMatrix(e),r},replaceChild(r,t){r.updateLocalTransform(),this.addChildAt(t,this.getChildIndex(r)),t.setFromMatrix(r.localTransform),t.updateLocalTransform(),this.removeChild(r)}},zh={collectRenderables(r,t,e){this.parentRenderLayer&&this.parentRenderLayer!==e||this.globalDisplayStatus<7||!this.includeInBuild||(this.sortableChildren&&this.sortChildren(),this.isSimple?this.collectRenderablesSimple(r,t,e):this.renderGroup?t.renderPipes.renderGroup.addRenderGroup(this.renderGroup,r):this.collectRenderablesWithEffects(r,t,e))},collectRenderablesSimple(r,t,e){const i=this.children,s=i.length;for(let n=0;n=0;s--){const n=this.effects[s];i[n.pipe].pop(n,this,r)}}};class Jr{constructor(){this.pipe="filter",this.priority=1}destroy(){for(let t=0;t{this.add({test:t.test,maskClass:t})}))}add(t){this._tests.push(t)}getMaskEffect(t){this._initialized||this.init();for(let e=0;et in r?W1(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Kh=(r,t)=>{for(var e in t||(t={}))V1.call(t,e)&&Yh(r,e,t[e]);if(Vh)for(var e of Vh(t))Y1.call(t,e)&&Yh(r,e,t[e]);return r};const qh={_maskEffect:null,_maskOptions:{inverse:!1},_filterEffect:null,effects:[],_markStructureAsChanged(){const r=this.renderGroup||this.parentRenderGroup;r&&(r.structureDidChange=!0)},addEffect(r){this.effects.indexOf(r)===-1&&(this.effects.push(r),this.effects.sort((t,e)=>t.priority-e.priority),this._markStructureAsChanged(),this._updateIsSimple())},removeEffect(r){const t=this.effects.indexOf(r);t!==-1&&(this.effects.splice(t,1),this._markStructureAsChanged(),this._updateIsSimple())},set mask(r){const t=this._maskEffect;(t==null?void 0:t.mask)!==r&&(t&&(this.removeEffect(t),qi.returnMaskEffect(t),this._maskEffect=null),r!=null&&(this._maskEffect=qi.getMaskEffect(r),this.addEffect(this._maskEffect)))},get mask(){var r;return(r=this._maskEffect)==null?void 0:r.mask},setMask(r){this._maskOptions=Kh(Kh({},this._maskOptions),r),r.mask&&(this.mask=r.mask),this._markStructureAsChanged()},set filters(r){var t;!Array.isArray(r)&&r&&(r=[r]);const e=this._filterEffect||(this._filterEffect=new Jr);r=r;const i=(r==null?void 0:r.length)>0,s=((t=e.filters)==null?void 0:t.length)>0,n=i!==s;r=Array.isArray(r)?r.slice(0):r,e.filters=Object.freeze(r),n&&(i?this.addEffect(e):(this.removeEffect(e),e.filters=r!=null?r:null))},get filters(){var r;return(r=this._filterEffect)==null?void 0:r.filters},set filterArea(r){this._filterEffect||(this._filterEffect=new Jr),this._filterEffect.filterArea=r},get filterArea(){var r;return(r=this._filterEffect)==null?void 0:r.filterArea}},Zh={label:null,get name(){return this.label},set name(r){this.label=r},getChildByName(r,t=!1){return this.getChildByLabel(r,t)},getChildByLabel(r,t=!1){const e=this.children;for(let i=0;i=this.x&&t=this.y&&e=h&&t<=p&&e>=f&&e<=m&&!(t>g&&t<_&&e>b&&et.right?t.right:this.right)<=O)return!1;const M=this.yt.bottom?t.bottom:this.bottom)>M}const i=this.left,s=this.right,n=this.top,a=this.bottom;if(s<=i||a<=n)return!1;const o=Zi[0].set(t.left,t.top),l=Zi[1].set(t.left,t.bottom),u=Zi[2].set(t.right,t.top),c=Zi[3].set(t.right,t.bottom);if(u.x<=o.x||l.y<=o.y)return!1;const h=Math.sign(e.a*e.d-e.b*e.c);if(h===0||(e.apply(o,o),e.apply(l,l),e.apply(u,u),e.apply(c,c),Math.max(o.x,l.x,u.x,c.x)<=i||Math.min(o.x,l.x,u.x,c.x)>=s||Math.max(o.y,l.y,u.y,c.y)<=n||Math.min(o.y,l.y,u.y,c.y)>=a))return!1;const p=h*(l.y-o.y),f=h*(o.x-l.x),m=p*i+f*n,g=p*s+f*n,_=p*i+f*a,b=p*s+f*a;if(Math.max(m,g,_,b)<=p*o.x+f*o.y||Math.min(m,g,_,b)>=p*c.x+f*c.y)return!1;const y=h*(o.y-u.y),x=h*(u.x-o.x),v=y*i+x*n,w=y*s+x*n,S=y*i+x*a,E=y*s+x*a;return!(Math.max(v,w,S,E)<=y*o.x+x*o.y||Math.min(v,w,S,E)>=y*c.x+x*c.y)}pad(t=0,e=t){return this.x-=t,this.y-=e,this.width+=t*2,this.height+=e*2,this}fit(t){const e=Math.max(this.x,t.x),i=Math.min(this.x+this.width,t.x+t.width),s=Math.max(this.y,t.y),n=Math.min(this.y+this.height,t.y+t.height);return this.x=e,this.width=Math.max(i-e,0),this.y=s,this.height=Math.max(n-s,0),this}ceil(t=1,e=.001){const i=Math.ceil((this.x+this.width-e)*t)/t,s=Math.ceil((this.y+this.height-e)*t)/t;return this.x=Math.floor((this.x+e)*t)/t,this.y=Math.floor((this.y+e)*t)/t,this.width=i-this.x,this.height=s-this.y,this}scale(t,e=t){return this.x*=t,this.y*=e,this.width*=t,this.height*=e,this}enlarge(t){const e=Math.min(this.x,t.x),i=Math.max(this.x+this.width,t.x+t.width),s=Math.min(this.y,t.y),n=Math.max(this.y+this.height,t.y+t.height);return this.x=e,this.width=i-e,this.y=s,this.height=n-s,this}getBounds(t){return t||(t=new it),t.copyFrom(this),t}containsRect(t){if(this.width<=0||this.height<=0)return!1;const e=t.x,i=t.y,s=t.x+t.width,n=t.y+t.height;return e>=this.x&&e=this.y&&i=this.x&&s=this.y&&nthis.maxX||this.minY>this.maxY}get rectangle(){this._rectangle||(this._rectangle=new it);const t=this._rectangle;return this.minX>this.maxX||this.minY>this.maxY?(t.x=0,t.y=0,t.width=0,t.height=0):t.copyFromBounds(this),t}clear(){return this.minX=1/0,this.minY=1/0,this.maxX=-1/0,this.maxY=-1/0,this.matrix=Qh,this}set(t,e,i,s){this.minX=t,this.minY=e,this.maxX=i,this.maxY=s}addFrame(t,e,i,s,n){n||(n=this.matrix);const a=n.a,o=n.b,l=n.c,u=n.d,c=n.tx,h=n.ty;let p=this.minX,f=this.minY,m=this.maxX,g=this.maxY,_=a*t+l*e+c,b=o*t+u*e+h;_m&&(m=_),b>g&&(g=b),_=a*i+l*e+c,b=o*i+u*e+h,_m&&(m=_),b>g&&(g=b),_=a*t+l*s+c,b=o*t+u*s+h,_m&&(m=_),b>g&&(g=b),_=a*i+l*s+c,b=o*i+u*s+h,_m&&(m=_),b>g&&(g=b),this.minX=p,this.minY=f,this.maxX=m,this.maxY=g}addRect(t,e){this.addFrame(t.x,t.y,t.x+t.width,t.y+t.height,e)}addBounds(t,e){this.addFrame(t.minX,t.minY,t.maxX,t.maxY,e)}addBoundsMask(t){this.minX=this.minX>t.minX?this.minX:t.minX,this.minY=this.minY>t.minY?this.minY:t.minY,this.maxX=this.maxXthis.maxX?p:this.maxX,this.maxY=f>this.maxY?f:this.maxY,p=a*e+l*n+c,f=o*e+u*n+h,this.minX=pthis.maxX?p:this.maxX,this.maxY=f>this.maxY?f:this.maxY,p=a*s+l*n+c,f=o*s+u*n+h,this.minX=pthis.maxX?p:this.maxX,this.maxY=f>this.maxY?f:this.maxY}fit(t){return this.minXt.right&&(this.maxX=t.right),this.minYt.bottom&&(this.maxY=t.bottom),this}fitBounds(t,e,i,s){return this.minXe&&(this.maxX=e),this.minYs&&(this.maxY=s),this}pad(t,e=t){return this.minX-=t,this.maxX+=t,this.minY-=e,this.maxY+=e,this}ceil(){return this.minX=Math.floor(this.minX),this.minY=Math.floor(this.minY),this.maxX=Math.ceil(this.maxX),this.maxY=Math.ceil(this.maxY),this}clone(){return new Pt(this.minX,this.minY,this.maxX,this.maxY)}scale(t,e=t){return this.minX*=t,this.minY*=e,this.maxX*=t,this.maxY*=e,this}get x(){return this.minX}set x(t){const e=this.maxX-this.minX;this.minX=t,this.maxX=t+e}get y(){return this.minY}set y(t){const e=this.maxY-this.minY;this.minY=t,this.maxY=t+e}get width(){return this.maxX-this.minX}set width(t){this.maxX=this.minX+t}get height(){return this.maxY-this.minY}set height(t){this.maxY=this.minY+t}get left(){return this.minX}get right(){return this.maxX}get top(){return this.minY}get bottom(){return this.maxY}get isPositive(){return this.maxX-this.minX>0&&this.maxY-this.minY>0}get isValid(){return this.minX+this.minY!==1/0}addVertexData(t,e,i,s){let n=this.minX,a=this.minY,o=this.maxX,l=this.maxY;s||(s=this.matrix);const u=s.a,c=s.b,h=s.c,p=s.d,f=s.tx,m=s.ty;for(let g=e;go?y:o,l=x>l?x:l}this.minX=n,this.minY=a,this.maxX=o,this.maxY=l}containsPoint(t,e){return this.minX<=t&&this.minY<=e&&this.maxX>=t&&this.maxY>=e}toString(){return`[pixi.js:Bounds minX=${this.minX} minY=${this.minY} maxX=${this.maxX} maxY=${this.maxY} width=${this.width} height=${this.height}]`}copyFrom(t){return this.minX=t.minX,this.minY=t.minY,this.maxX=t.maxX,this.maxY=t.maxY,this}}const Bt=St.getPool(D),_e=St.getPool(Pt),K1=new D,Jh={getFastGlobalBounds(r,t){t||(t=new Pt),t.clear(),this._getGlobalBoundsRecursive(!!r,t,this.parentRenderLayer),t.isValid||t.set(0,0,0,0);const e=this.renderGroup||this.parentRenderGroup;return t.applyMatrix(e.worldTransform),t},_getGlobalBoundsRecursive(r,t,e){let i=t;if(r&&this.parentRenderLayer&&this.parentRenderLayer!==e||this.localDisplayStatus!==7||!this.measurable)return;const s=!!this.effects.length;if((this.renderGroup||s)&&(i=_e.get().clear()),this.boundsArea)t.addRect(this.boundsArea,this.worldTransform);else{if(this.renderPipeId){const a=this.bounds;i.addFrame(a.minX,a.minY,a.maxX,a.maxY,this.groupTransform)}const n=this.children;for(let a=0;a>16&255,i=r>>8&255,s=r&255,n=t>>16&255,a=t>>8&255,o=t&255,l=e*n/255|0,u=i*a/255|0,c=s*o/255|0;return(l<<16)+(u<<8)+c}const ed=16777215;function ei(r,t){return r===ed?t:t===ed?r:Ce(r,t)}function be(r){return((r&255)<<16)+(r&65280)+(r>>16&255)}const rd={getGlobalAlpha(r){if(r)return this.renderGroup?this.renderGroup.worldAlpha:this.parentRenderGroup?this.parentRenderGroup.worldAlpha*this.alpha:this.alpha;let t=this.alpha,e=this.parent;for(;e;)t*=e.alpha,e=e.parent;return t},getGlobalTransform(r=new D,t){if(t)return r.copyFrom(this.worldTransform);this.updateLocalTransform();const e=Qi(this,Bt.get().identity());return r.appendFrom(this.localTransform,e),Bt.return(e),r},getGlobalTint(r){if(r)return this.renderGroup?be(this.renderGroup.worldColor):this.parentRenderGroup?be(ei(this.localColor,this.parentRenderGroup.worldColor)):this.tint;let t=this.localColor,e=this.parent;for(;e;)t=ei(t,e.localColor),e=e.parent;return be(t)}};function Ji(r,t,e){return t.clear(),e||(e=D.IDENTITY),id(r,t,e,r,!0),t.isValid||t.set(0,0,0,0),t}function id(r,t,e,i,s){var n,a;let o;if(s)o=Bt.get(),o=e.copyTo(o);else{if(!r.visible||!r.measurable)return;r.updateLocalTransform();const c=r.localTransform;o=Bt.get(),o.appendFrom(c,e)}const l=t,u=!!r.effects.length;if(u&&(t=_e.get().clear()),r.boundsArea)t.addRect(r.boundsArea,o);else{r.renderPipeId&&(t.matrix=o,t.addBounds(r.bounds));const c=r.children;for(let h=0;h>>1,r|=r>>>2,r|=r>>>4,r|=r>>>8,r|=r>>>16,r+1}function Sa(r){return!(r&r-1)&&!!r}function Q1(r){let t=(r>65535?1:0)<<4;r>>>=t;let e=(r>255?1:0)<<3;return r>>>=e,t|=e,e=(r>15?1:0)<<2,r>>>=e,t|=e,e=(r>3?1:0)<<1,r>>>=e,t|=e,t|r>>1}function ue(r){const t={};for(const e in r)r[e]!==void 0&&(t[e]=r[e]);return t}var J1=Object.defineProperty,ld=Object.getOwnPropertySymbols,tS=Object.prototype.hasOwnProperty,eS=Object.prototype.propertyIsEnumerable,ud=(r,t,e)=>t in r?J1(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,cd=(r,t)=>{for(var e in t||(t={}))tS.call(t,e)&&ud(r,e,t[e]);if(ld)for(var e of ld(t))eS.call(t,e)&&ud(r,e,t[e]);return r};const hd=Object.create(null);function rS(r){const t=hd[r];return t===void 0&&(hd[r]=nt("resource")),t}const dd=class YT extends Ut{constructor(t={}){var e,i,s,n,a,o,l;super(),this._resourceType="textureSampler",this._touched=0,this._maxAnisotropy=1,this.destroyed=!1,t=cd(cd({},YT.defaultOptions),t),this.addressMode=t.addressMode,this.addressModeU=(e=t.addressModeU)!=null?e:this.addressModeU,this.addressModeV=(i=t.addressModeV)!=null?i:this.addressModeV,this.addressModeW=(s=t.addressModeW)!=null?s:this.addressModeW,this.scaleMode=t.scaleMode,this.magFilter=(n=t.magFilter)!=null?n:this.magFilter,this.minFilter=(a=t.minFilter)!=null?a:this.minFilter,this.mipmapFilter=(o=t.mipmapFilter)!=null?o:this.mipmapFilter,this.lodMinClamp=t.lodMinClamp,this.lodMaxClamp=t.lodMaxClamp,this.compare=t.compare,this.maxAnisotropy=(l=t.maxAnisotropy)!=null?l:1}set addressMode(t){this.addressModeU=t,this.addressModeV=t,this.addressModeW=t}get addressMode(){return this.addressModeU}set wrapMode(t){this.addressMode=t}get wrapMode(){return this.addressMode}set scaleMode(t){this.magFilter=t,this.minFilter=t,this.mipmapFilter=t}get scaleMode(){return this.magFilter}set maxAnisotropy(t){this._maxAnisotropy=Math.min(t,16),this._maxAnisotropy>1&&(this.scaleMode="linear")}get maxAnisotropy(){return this._maxAnisotropy}get _resourceId(){return this._sharedResourceId||this._generateResourceId()}update(){this._sharedResourceId=null,this.emit("change",this)}_generateResourceId(){const t=`${this.addressModeU}-${this.addressModeV}-${this.addressModeW}-${this.magFilter}-${this.minFilter}-${this.mipmapFilter}-${this.lodMinClamp}-${this.lodMaxClamp}-${this.compare}-${this._maxAnisotropy}`;return this._sharedResourceId=rS(t),this._resourceId}destroy(){this.destroyed=!0,this.emit("destroy",this),this.emit("change",this),this.removeAllListeners()}};dd.defaultOptions={addressMode:"clamp-to-edge",scaleMode:"linear"};let Kt=dd;var iS=Object.defineProperty,pd=Object.getOwnPropertySymbols,sS=Object.prototype.hasOwnProperty,nS=Object.prototype.propertyIsEnumerable,fd=(r,t,e)=>t in r?iS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,md=(r,t)=>{for(var e in t||(t={}))sS.call(t,e)&&fd(r,e,t[e]);if(pd)for(var e of pd(t))nS.call(t,e)&&fd(r,e,t[e]);return r};const gd=class KT extends Ut{constructor(t={}){var e,i,s,n;super(),this.options=t,this._gpuData=Object.create(null),this._gcLastUsed=-1,this.uid=nt("textureSource"),this._resourceType="textureSource",this._resourceId=nt("resource"),this.uploadMethodId="unknown",this._resolution=1,this.pixelWidth=1,this.pixelHeight=1,this.width=1,this.height=1,this.sampleCount=1,this.mipLevelCount=1,this.autoGenerateMipmaps=!1,this.format="rgba8unorm",this.dimension="2d",this.viewDimension="2d",this.arrayLayerCount=1,this.antialias=!1,this._touched=0,this._batchTick=-1,this._textureBindLocation=-1,t=md(md({},KT.defaultOptions),t),this.label=(e=t.label)!=null?e:"",this.resource=t.resource,this.autoGarbageCollect=t.autoGarbageCollect,this._resolution=t.resolution,t.width?this.pixelWidth=t.width*this._resolution:this.pixelWidth=this.resource&&(i=this.resourceWidth)!=null?i:1,t.height?this.pixelHeight=t.height*this._resolution:this.pixelHeight=this.resource&&(s=this.resourceHeight)!=null?s:1,this.width=this.pixelWidth/this._resolution,this.height=this.pixelHeight/this._resolution,this.format=t.format,this.dimension=t.dimensions,this.viewDimension=(n=t.viewDimension)!=null?n:t.dimensions,this.arrayLayerCount=t.arrayLayerCount,this.mipLevelCount=t.mipLevelCount,this.autoGenerateMipmaps=t.autoGenerateMipmaps,this.sampleCount=t.sampleCount,this.antialias=t.antialias,this.alphaMode=t.alphaMode,this.style=new Kt(ue(t)),this.destroyed=!1,this._refreshPOT()}get source(){return this}get style(){return this._style}set style(t){var e,i;this.style!==t&&((e=this._style)==null||e.off("change",this._onStyleChange,this),this._style=t,(i=this._style)==null||i.on("change",this._onStyleChange,this),this._onStyleChange())}set maxAnisotropy(t){this._style.maxAnisotropy=t}get maxAnisotropy(){return this._style.maxAnisotropy}get addressMode(){return this._style.addressMode}set addressMode(t){this._style.addressMode=t}get repeatMode(){return this._style.addressMode}set repeatMode(t){this._style.addressMode=t}get magFilter(){return this._style.magFilter}set magFilter(t){this._style.magFilter=t}get minFilter(){return this._style.minFilter}set minFilter(t){this._style.minFilter=t}get mipmapFilter(){return this._style.mipmapFilter}set mipmapFilter(t){this._style.mipmapFilter=t}get lodMinClamp(){return this._style.lodMinClamp}set lodMinClamp(t){this._style.lodMinClamp=t}get lodMaxClamp(){return this._style.lodMaxClamp}set lodMaxClamp(t){this._style.lodMaxClamp=t}_onStyleChange(){this.emit("styleChange",this)}update(){if(this.resource){const t=this._resolution;if(this.resize(this.resourceWidth/t,this.resourceHeight/t))return}this.emit("update",this)}destroy(){this.destroyed=!0,this.unload(),this.emit("destroy",this),this._style&&(this._style.destroy(),this._style=null),this.uploadMethodId=null,this.resource=null,this.removeAllListeners()}unload(){var t,e;this._resourceId=nt("resource"),this.emit("change",this),this.emit("unload",this);for(const i in this._gpuData)(e=(t=this._gpuData[i])==null?void 0:t.destroy)==null||e.call(t);this._gpuData=Object.create(null)}get resourceWidth(){const{resource:t}=this;return t.naturalWidth||t.videoWidth||t.displayWidth||t.width}get resourceHeight(){const{resource:t}=this;return t.naturalHeight||t.videoHeight||t.displayHeight||t.height}get resolution(){return this._resolution}set resolution(t){this._resolution!==t&&(this._resolution=t,this.width=this.pixelWidth/t,this.height=this.pixelHeight/t)}resize(t,e,i){i||(i=this._resolution),t||(t=this.width),e||(e=this.height);const s=Math.round(t*i),n=Math.round(e*i);return this.width=s/i,this.height=n/i,this._resolution=i,this.pixelWidth===s&&this.pixelHeight===n?!1:(this._refreshPOT(),this.pixelWidth=s,this.pixelHeight=n,this.emit("resize",this),this._resourceId=nt("resource"),this.emit("change",this),!0)}updateMipmaps(){this.autoGenerateMipmaps&&this.mipLevelCount>1&&this.emit("updateMipmaps",this)}set wrapMode(t){this._style.wrapMode=t}get wrapMode(){return this._style.wrapMode}set scaleMode(t){this._style.scaleMode=t}get scaleMode(){return this._style.scaleMode}_refreshPOT(){this.isPowerOfTwo=Sa(this.pixelWidth)&&Sa(this.pixelHeight)}static test(t){throw new Error("Unimplemented")}};gd.defaultOptions={resolution:1,format:"bgra8unorm",alphaMode:"premultiply-alpha-on-upload",dimensions:"2d",viewDimension:"2d",arrayLayerCount:1,mipLevelCount:1,autoGenerateMipmaps:!1,sampleCount:1,antialias:!1,autoGarbageCollect:!1};let ht=gd;const We=[1,1,0,-1,-1,-1,0,1,1,1,0,-1,-1,-1,0,1],Ve=[0,1,1,1,0,-1,-1,-1,0,1,1,1,0,-1,-1,-1],Ye=[0,-1,-1,-1,0,1,1,1,0,1,1,1,0,-1,-1,-1],Ke=[1,1,0,-1,-1,-1,0,1,-1,-1,0,1,1,1,0,-1],wa=[],_d=[],es=Math.sign;function aS(){for(let r=0;r<16;r++){const t=[];wa.push(t);for(let e=0;e<16;e++){const i=es(We[r]*We[e]+Ye[r]*Ve[e]),s=es(Ve[r]*We[e]+Ke[r]*Ve[e]),n=es(We[r]*Ye[e]+Ye[r]*Ke[e]),a=es(Ve[r]*Ye[e]+Ke[r]*Ke[e]);for(let o=0;o<16;o++)if(We[o]===i&&Ve[o]===s&&Ye[o]===n&&Ke[o]===a){t.push(o);break}}}for(let r=0;r<16;r++){const t=new D;t.set(We[r],Ve[r],Ye[r],Ke[r],0,0),_d.push(t)}}aS();const H={E:0,SE:1,S:2,SW:3,W:4,NW:5,N:6,NE:7,MIRROR_VERTICAL:8,MAIN_DIAGONAL:10,MIRROR_HORIZONTAL:12,REVERSE_DIAGONAL:14,uX:r=>We[r],uY:r=>Ve[r],vX:r=>Ye[r],vY:r=>Ke[r],inv:r=>r&8?r&15:-r&7,add:(r,t)=>wa[r][t],sub:(r,t)=>wa[r][H.inv(t)],rotate180:r=>r^4,isVertical:r=>(r&3)===2,byDirection:(r,t)=>Math.abs(r)*2<=Math.abs(t)?t>=0?H.S:H.N:Math.abs(t)*2<=Math.abs(r)?r>0?H.E:H.W:t>0?r>0?H.SE:H.SW:r>0?H.NE:H.NW,matrixAppendRotationInv:(r,t,e=0,i=0,s=0,n=0)=>{const a=_d[H.inv(t)],o=a.a,l=a.b,u=a.c,c=a.d,h=e-Math.min(0,o*s,u*n,o*s+u*n),p=i-Math.min(0,l*s,c*n,l*s+c*n),f=r.a,m=r.b,g=r.c,_=r.d;r.a=o*f+l*g,r.b=o*m+l*_,r.c=u*f+c*g,r.d=u*m+c*_,r.tx=h*f+p*g+r.tx,r.ty=h*m+p*_+r.ty},transformRectCoords:(r,t,e,i)=>{const{x:s,y:n,width:a,height:o}=r,{x:l,y:u,width:c,height:h}=t;return e===H.E?(i.set(s+l,n+u,a,o),i):e===H.S?i.set(c-n-o+l,s+u,o,a):e===H.W?i.set(c-s-a+l,h-n-o+u,a,o):e===H.N?i.set(n+l,h-s-a+u,o,a):i.set(s+l,n+u,a,o)}},Ea=()=>{};var oS=Object.defineProperty,lS=Object.defineProperties,uS=Object.getOwnPropertyDescriptors,bd=Object.getOwnPropertySymbols,cS=Object.prototype.hasOwnProperty,hS=Object.prototype.propertyIsEnumerable,vd=(r,t,e)=>t in r?oS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,dS=(r,t)=>{for(var e in t||(t={}))cS.call(t,e)&&vd(r,e,t[e]);if(bd)for(var e of bd(t))hS.call(t,e)&&vd(r,e,t[e]);return r},pS=(r,t)=>lS(r,uS(t));class rs extends ht{constructor(t){const e=t.resource||new Float32Array(t.width*t.height*4);let i=t.format;i||(e instanceof Float32Array?i="rgba32float":e instanceof Int32Array||e instanceof Uint32Array?i="rgba32uint":e instanceof Int16Array||e instanceof Uint16Array?i="rgba16uint":(e instanceof Int8Array,i="bgra8unorm")),super(pS(dS({},t),{resource:e,format:i})),this.uploadMethodId="buffer"}static test(t){return t instanceof Int8Array||t instanceof Uint8Array||t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array}}rs.extension=T.TextureSource;const yd=new D;class Pa{constructor(t,e){this.mapCoord=new D,this.uClampFrame=new Float32Array(4),this.uClampOffset=new Float32Array(2),this._textureID=-1,this._updateID=0,this.clampOffset=0,typeof e=="undefined"?this.clampMargin=t.width<10?0:.5:this.clampMargin=e,this.isSimple=!1,this.texture=t}get texture(){return this._texture}set texture(t){var e;this.texture!==t&&((e=this._texture)==null||e.removeListener("update",this.update,this),this._texture=t,this._texture.addListener("update",this.update,this),this.update())}multiplyUvs(t,e){e===void 0&&(e=t);const i=this.mapCoord;for(let s=0;st in r?fS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,vS=(r,t)=>{for(var e in t||(t={}))_S.call(t,e)&&Td(r,e,t[e]);if(xd)for(var e of xd(t))bS.call(t,e)&&Td(r,e,t[e]);return r},yS=(r,t)=>mS(r,gS(t));let xS=0;class Sd{constructor(t){this._poolKeyHash=Object.create(null),this._texturePool={},this.textureOptions=t||{},this.enableFullScreen=!1,this.textureStyle=new Kt(this.textureOptions)}createTexture(t,e,i){const s=new ht(yS(vS({},this.textureOptions),{width:t,height:e,resolution:1,antialias:i,autoGarbageCollect:!1}));return new F({source:s,label:`texturePool_${xS++}`})}getOptimalTexture(t,e,i=1,s){let n=Math.ceil(t*i-1e-6),a=Math.ceil(e*i-1e-6);n=ze(n),a=ze(a);const o=(n<<17)+(a<<1)+(s?1:0);this._texturePool[o]||(this._texturePool[o]=[]);let l=this._texturePool[o].pop();return l||(l=this.createTexture(n,a,s)),l.source._resolution=i,l.source.width=n/i,l.source.height=a/i,l.source.pixelWidth=n,l.source.pixelHeight=a,l.frame.x=0,l.frame.y=0,l.frame.width=t,l.frame.height=e,l.updateUvs(),this._poolKeyHash[l.uid]=o,l}getSameSizeTexture(t,e=!1){const i=t.source;return this.getOptimalTexture(t.width,t.height,i._resolution,e)}returnTexture(t,e=!1){const i=this._poolKeyHash[t.uid];e&&(t.source.style=this.textureStyle),this._texturePool[i].push(t)}clear(t){if(t=t!==!1,t)for(const e in this._texturePool){const i=this._texturePool[e];if(i)for(let s=0;s-1&&this.renderGroupChildren.splice(e,1),t.renderGroupParent=null}addChild(t){if(this.structureDidChange=!0,t.parentRenderGroup=this,t.updateTick=-1,t.parent===this.root?t.relativeRenderGroupDepth=1:t.relativeRenderGroupDepth=t.parent.relativeRenderGroupDepth+1,t.didChange=!0,this.onChildUpdate(t),t.renderGroup){this.addRenderGroupChild(t.renderGroup);return}t._onRender&&this.addOnRender(t);const e=t.children;for(let i=0;i0}addOnRender(t){this._onRenderContainers.push(t)}removeOnRender(t){this._onRenderContainers.splice(this._onRenderContainers.indexOf(t),1)}runOnRender(t){for(let e=0;ethis.addChild(s)),(i=t.parent)==null||i.addChild(this)}static mixin(t){$.mixin(dt,t)}set _didChangeId(t){this._didViewChangeTick=t>>12&4095,this._didContainerChangeTick=t&4095}get _didChangeId(){return this._didContainerChangeTick&4095|(this._didViewChangeTick&4095)<<12}addChild(...t){if(t.length>1){for(let s=0;s1){for(let s=0;s-1&&(this._didViewChangeTick++,this.children.splice(i,1),this.renderGroup?this.renderGroup.removeChild(e):this.parentRenderGroup&&this.parentRenderGroup.removeChild(e),e.parentRenderLayer&&e.parentRenderLayer.detach(e),e.parent=null,this.emit("childRemoved",e,this,i),e.emit("removed",this)),e}_onUpdate(t){t&&t===this._skew&&this._updateSkew(),this._didContainerChangeTick++,!this.didChange&&(this.didChange=!0,this.parentRenderGroup&&this.parentRenderGroup.onChildUpdate(this))}set isRenderGroup(t){!!this.renderGroup!==t&&(t?this.enableRenderGroup():this.disableRenderGroup())}get isRenderGroup(){return!!this.renderGroup}enableRenderGroup(){if(this.renderGroup)return;const t=this.parentRenderGroup;t==null||t.removeChild(this),this.renderGroup=St.get(is,this),this.groupTransform=D.IDENTITY,t==null||t.addChild(this),this._updateIsSimple()}disableRenderGroup(){if(!this.renderGroup)return;const t=this.parentRenderGroup;t==null||t.removeChild(this),St.return(this.renderGroup),this.renderGroup=null,this.groupTransform=this.relativeGroupTransform,t==null||t.addChild(this),this._updateIsSimple()}_updateIsSimple(){this.isSimple=!this.renderGroup&&this.effects.length===0}get worldTransform(){return this._worldTransform||(this._worldTransform=new D),this.renderGroup?this._worldTransform.copyFrom(this.renderGroup.worldTransform):this.parentRenderGroup&&this._worldTransform.appendFrom(this.relativeGroupTransform,this.parentRenderGroup.worldTransform),this._worldTransform}get x(){return this._position.x}set x(t){this._position.x=t}get y(){return this._position.y}set y(t){this._position.y=t}get position(){return this._position}set position(t){this._position.copyFrom(t)}get rotation(){return this._rotation}set rotation(t){this._rotation!==t&&(this._rotation=t,this._onUpdate(this._skew))}get angle(){return this.rotation*$h}set angle(t){this.rotation=t*Lh}get pivot(){return this._pivot===Ca&&(this._pivot=new gt(this,0,0)),this._pivot}set pivot(t){this._pivot===Ca&&(this._pivot=new gt(this,0,0)),typeof t=="number"?this._pivot.set(t):this._pivot.copyFrom(t)}get skew(){return this._skew===Aa&&(this._skew=new gt(this,0,0)),this._skew}set skew(t){this._skew===Aa&&(this._skew=new gt(this,0,0)),this._skew.copyFrom(t)}get scale(){return this._scale===Ra&&(this._scale=new gt(this,1,1)),this._scale}set scale(t){this._scale===Ra&&(this._scale=new gt(this,0,0)),typeof t=="string"&&(t=parseFloat(t)),typeof t=="number"?this._scale.set(t):this._scale.copyFrom(t)}get origin(){return this._origin===Ma&&(this._origin=new gt(this,0,0)),this._origin}set origin(t){this._origin===Ma&&(this._origin=new gt(this,0,0)),typeof t=="number"?this._origin.set(t):this._origin.copyFrom(t)}get width(){return Math.abs(this.scale.x*this.getLocalBounds().width)}set width(t){const e=this.getLocalBounds().width;this._setWidth(t,e)}get height(){return Math.abs(this.scale.y*this.getLocalBounds().height)}set height(t){const e=this.getLocalBounds().height;this._setHeight(t,e)}getSize(t){t||(t={});const e=this.getLocalBounds();return t.width=Math.abs(this.scale.x*e.width),t.height=Math.abs(this.scale.y*e.height),t}setSize(t,e){var i;const s=this.getLocalBounds();typeof t=="object"?(e=(i=t.height)!=null?i:t.width,t=t.width):e!=null||(e=t),t!==void 0&&this._setWidth(t,s.width),e!==void 0&&this._setHeight(e,s.height)}_updateSkew(){const t=this._rotation,e=this._skew;this._cx=Math.cos(t+e._y),this._sx=Math.sin(t+e._y),this._cy=-Math.sin(t-e._x),this._sy=Math.cos(t-e._x)}updateTransform(t){return this.position.set(typeof t.x=="number"?t.x:this.position.x,typeof t.y=="number"?t.y:this.position.y),this.scale.set(typeof t.scaleX=="number"?t.scaleX||1:this.scale.x,typeof t.scaleY=="number"?t.scaleY||1:this.scale.y),this.rotation=typeof t.rotation=="number"?t.rotation:this.rotation,this.skew.set(typeof t.skewX=="number"?t.skewX:this.skew.x,typeof t.skewY=="number"?t.skewY:this.skew.y),this.pivot.set(typeof t.pivotX=="number"?t.pivotX:this.pivot.x,typeof t.pivotY=="number"?t.pivotY:this.pivot.y),this.origin.set(typeof t.originX=="number"?t.originX:this.origin.x,typeof t.originY=="number"?t.originY:this.origin.y),this}setFromMatrix(t){t.decompose(this)}updateLocalTransform(){const t=this._didContainerChangeTick;if(this._didLocalTransformChangeId===t)return;this._didLocalTransformChangeId=t;const e=this.localTransform,i=this._scale,s=this._pivot,n=this._origin,a=this._position,o=i._x,l=i._y,u=s._x,c=s._y,h=-n._x,p=-n._y;e.a=this._cx*o,e.b=this._sx*o,e.c=this._cy*l,e.d=this._sy*l,e.tx=a._x-(u*e.a+c*e.c)+(h*e.a+p*e.c)-h,e.ty=a._y-(u*e.b+c*e.d)+(h*e.b+p*e.d)-p}set alpha(t){t!==this.localAlpha&&(this.localAlpha=t,this._updateFlags|=ri,this._onUpdate())}get alpha(){return this.localAlpha}set tint(t){const e=J.shared.setValue(t!=null?t:16777215).toBgrNumber();e!==this.localColor&&(this.localColor=e,this._updateFlags|=ri,this._onUpdate())}get tint(){return be(this.localColor)}set blendMode(t){this.localBlendMode!==t&&(this.parentRenderGroup&&(this.parentRenderGroup.structureDidChange=!0),this._updateFlags|=ns,this.localBlendMode=t,this._onUpdate())}get blendMode(){return this.localBlendMode}get visible(){return!!(this.localDisplayStatus&2)}set visible(t){const e=t?2:0;(this.localDisplayStatus&2)!==e&&(this.parentRenderGroup&&(this.parentRenderGroup.structureDidChange=!0),this._updateFlags|=mr,this.localDisplayStatus^=2,this._onUpdate())}get culled(){return!(this.localDisplayStatus&4)}set culled(t){const e=t?0:4;(this.localDisplayStatus&4)!==e&&(this.parentRenderGroup&&(this.parentRenderGroup.structureDidChange=!0),this._updateFlags|=mr,this.localDisplayStatus^=4,this._onUpdate())}get renderable(){return!!(this.localDisplayStatus&1)}set renderable(t){const e=t?1:0;(this.localDisplayStatus&1)!==e&&(this._updateFlags|=mr,this.localDisplayStatus^=1,this.parentRenderGroup&&(this.parentRenderGroup.structureDidChange=!0),this._onUpdate())}get isRenderable(){return this.localDisplayStatus===7&&this.groupAlpha>0}destroy(t=!1){var e;if(this.destroyed)return;this.destroyed=!0;let i;if(this.children.length&&(i=this.removeChildren(0,this.children.length)),this.removeFromParent(),this.parent=null,this._maskEffect=null,this._filterEffect=null,this.effects=null,this._position=null,this._scale=null,this._pivot=null,this._origin=null,this._skew=null,this.emit("destroyed",this),this.removeAllListeners(),(typeof t=="boolean"?t:t==null?void 0:t.children)&&i)for(let s=0;s(r[r.INTERACTION=50]="INTERACTION",r[r.HIGH=25]="HIGH",r[r.NORMAL=0]="NORMAL",r[r.LOW=-25]="LOW",r[r.UTILITY=-50]="UTILITY",r))(ve||{});class as{constructor(t,e=null,i=0,s=!1){this.next=null,this.previous=null,this._destroyed=!1,this._fn=t,this._context=e,this.priority=i,this._once=s}match(t,e=null){return this._fn===t&&this._context===e}emit(t){this._fn&&(this._context?this._fn.call(this._context,t):this._fn(t));const e=this.next;return this._once&&this.destroy(!0),this._destroyed&&(this.next=null),e}connect(t){this.previous=t,t.next&&(t.next.previous=this),this.next=t.next,t.next=this}destroy(t=!1){this._destroyed=!0,this._fn=null,this._context=null,this.previous&&(this.previous.next=this.next),this.next&&(this.next.previous=this.previous);const e=this.next;return this.next=t?null:e,this.previous=null,e}}const wd=class Jt{constructor(){this.autoStart=!1,this.deltaTime=1,this.lastTime=-1,this.speed=1,this.started=!1,this._requestId=null,this._maxElapsedMS=100,this._minElapsedMS=0,this._protected=!1,this._lastFrame=-1,this._head=new as(null,null,1/0),this.deltaMS=1/Jt.targetFPMS,this.elapsedMS=1/Jt.targetFPMS,this._tick=t=>{this._requestId=null,this.started&&(this.update(t),this.started&&this._requestId===null&&this._head.next&&(this._requestId=requestAnimationFrame(this._tick)))}}_requestIfNeeded(){this._requestId===null&&this._head.next&&(this.lastTime=performance.now(),this._lastFrame=this.lastTime,this._requestId=requestAnimationFrame(this._tick))}_cancelIfNeeded(){this._requestId!==null&&(cancelAnimationFrame(this._requestId),this._requestId=null)}_startIfPossible(){this.started?this._requestIfNeeded():this.autoStart&&this.start()}add(t,e,i=ve.NORMAL){return this._addListener(new as(t,e,i))}addOnce(t,e,i=ve.NORMAL){return this._addListener(new as(t,e,i,!0))}_addListener(t){let e=this._head.next,i=this._head;if(!e)t.connect(i);else{for(;e;){if(t.priority>e.priority){t.connect(i);break}i=e,e=e.next}t.previous||t.connect(i)}return this._startIfPossible(),this}remove(t,e){let i=this._head.next;for(;i;)i.match(t,e)?i=i.destroy():i=i.next;return this._head.next||this._cancelIfNeeded(),this}get count(){if(!this._head)return 0;let t=0,e=this._head;for(;e=e.next;)t++;return t}start(){this.started||(this.started=!0,this._requestIfNeeded())}stop(){this.started&&(this.started=!1,this._cancelIfNeeded())}destroy(){if(!this._protected){this.stop();let t=this._head.next;for(;t;)t=t.destroy(!0);this._head.destroy(),this._head=null}}update(t=performance.now()){let e;if(t>this.lastTime){if(e=this.elapsedMS=t-this.lastTime,e>this._maxElapsedMS&&(e=this._maxElapsedMS),e*=this.speed,this._minElapsedMS){const n=t-this._lastFrame|0;if(n{if(!this._canvas)return;const e=this._canvas.getBoundingClientRect(),i=this._canvas.width,s=this._canvas.height,n=e.width/i*this._renderer.resolution,a=e.height/s*this._renderer.resolution,o=e.left,l=e.top,u=`translate(${o}px, ${l}px) scale(${n}, ${a})`;u!==this._lastTransform&&(this._domElement.style.transform=u,this._lastTransform=u)},this._domElement=t.domElement,this._renderer=t.renderer,!(globalThis.OffscreenCanvas&&this._renderer.canvas instanceof OffscreenCanvas)&&(this._canvas=this._renderer.canvas,this._attachObserver())}get canvas(){return this._canvas}ensureAttached(){!this._domElement.parentNode&&this._canvas.parentNode&&(this._canvas.parentNode.appendChild(this._domElement),this.updateTranslation())}_attachObserver(){"ResizeObserver"in globalThis?(this._observer&&(this._observer.disconnect(),this._observer=null),this._observer=new ResizeObserver(t=>{for(const e of t){if(e.target!==this._canvas)continue;const i=this.canvas.width,s=this.canvas.height,n=e.contentRect.width/i*this._renderer.resolution,a=e.contentRect.height/s*this._renderer.resolution;(this._lastScaleX!==n||this._lastScaleY!==a)&&(this.updateTranslation(),this._lastScaleX=n,this._lastScaleY=a)}}),this._observer.observe(this._canvas)):this._tickerAttached||Ct.shared.add(this.updateTranslation,this,ve.HIGH)}destroy(){this._observer?(this._observer.disconnect(),this._observer=null):this._tickerAttached&&Ct.shared.remove(this.updateTranslation),this._domElement=null,this._renderer=null,this._canvas=null,this._tickerAttached=!1,this._lastTransform="",this._lastScaleX=null,this._lastScaleY=null}}class gr{constructor(t){this.bubbles=!0,this.cancelBubble=!0,this.cancelable=!1,this.composed=!1,this.defaultPrevented=!1,this.eventPhase=gr.prototype.NONE,this.propagationStopped=!1,this.propagationImmediatelyStopped=!1,this.layer=new rt,this.page=new rt,this.NONE=0,this.CAPTURING_PHASE=1,this.AT_TARGET=2,this.BUBBLING_PHASE=3,this.manager=t}get layerX(){return this.layer.x}get layerY(){return this.layer.y}get pageX(){return this.page.x}get pageY(){return this.page.y}get data(){return this}composedPath(){return this.manager&&(!this.path||this.path[this.path.length-1]!==this.target)&&(this.path=this.target?this.manager.propagationPath(this.target):[]),this.path}initEvent(t,e,i){throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}initUIEvent(t,e,i,s,n){throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.")}preventDefault(){this.nativeEvent instanceof Event&&this.nativeEvent.cancelable&&this.nativeEvent.preventDefault(),this.defaultPrevented=!0}stopImmediatePropagation(){this.propagationImmediatelyStopped=!0}stopPropagation(){this.propagationStopped=!0}}var Ga=/iPhone/i,Ed=/iPod/i,Pd=/iPad/i,Ad=/\biOS-universal(?:.+)Mac\b/i,Ia=/\bAndroid(?:.+)Mobile\b/i,Cd=/Android/i,_r=/(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i,os=/Silk/i,Re=/Windows Phone/i,Rd=/\bWindows(?:.+)ARM\b/i,Md=/BlackBerry/i,Od=/BB10/i,Gd=/Opera Mini/i,Id=/\b(CriOS|Chrome)(?:.+)Mobile/i,Bd=/Mobile(?:.+)Firefox\b/i,Fd=function(r){return typeof r!="undefined"&&r.platform==="MacIntel"&&typeof r.maxTouchPoints=="number"&&r.maxTouchPoints>1&&typeof MSStream=="undefined"};function SS(r){return function(t){return t.test(r)}}function Dd(r){var t={userAgent:"",platform:"",maxTouchPoints:0};!r&&typeof navigator!="undefined"?t={userAgent:navigator.userAgent,platform:navigator.platform,maxTouchPoints:navigator.maxTouchPoints||0}:typeof r=="string"?t.userAgent=r:r&&r.userAgent&&(t={userAgent:r.userAgent,platform:r.platform,maxTouchPoints:r.maxTouchPoints||0});var e=t.userAgent,i=e.split("[FBAN");typeof i[1]!="undefined"&&(e=i[0]),i=e.split("Twitter"),typeof i[1]!="undefined"&&(e=i[0]);var s=SS(e),n={apple:{phone:s(Ga)&&!s(Re),ipod:s(Ed),tablet:!s(Ga)&&(s(Pd)||Fd(t))&&!s(Re),universal:s(Ad),device:(s(Ga)||s(Ed)||s(Pd)||s(Ad)||Fd(t))&&!s(Re)},amazon:{phone:s(_r),tablet:!s(_r)&&s(os),device:s(_r)||s(os)},android:{phone:!s(Re)&&s(_r)||!s(Re)&&s(Ia),tablet:!s(Re)&&!s(_r)&&!s(Ia)&&(s(os)||s(Cd)),device:!s(Re)&&(s(_r)||s(os)||s(Ia)||s(Cd))||s(/\bokhttp\b/i)},windows:{phone:s(Re),tablet:s(Rd),device:s(Re)||s(Rd)},other:{blackberry:s(Md),blackberry10:s(Od),opera:s(Gd),firefox:s(Bd),chrome:s(Id),device:s(Md)||s(Od)||s(Gd)||s(Bd)||s(Id)},any:!1,phone:!1,tablet:!1};return n.any=n.apple.device||n.android.device||n.windows.device||n.other.device,n.phone=n.apple.phone||n.android.phone||n.windows.phone,n.tablet=n.apple.tablet||n.android.tablet||n.windows.tablet,n}var Ud;const kd=((Ud=Dd.default)!=null?Ud:Dd)(globalThis.navigator);var wS=Object.defineProperty,$d=Object.getOwnPropertySymbols,ES=Object.prototype.hasOwnProperty,PS=Object.prototype.propertyIsEnumerable,Ld=(r,t,e)=>t in r?wS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Nd=(r,t)=>{for(var e in t||(t={}))ES.call(t,e)&&Ld(r,e,t[e]);if($d)for(var e of $d(t))PS.call(t,e)&&Ld(r,e,t[e]);return r};const AS=9,Xd=100,CS=0,RS=0,Hd=2,jd=1,MS=-1e3,OS=-1e3,GS=2,Ba=class qT{constructor(t,e=kd){this._mobileInfo=e,this.debug=!1,this._activateOnTab=!0,this._deactivateOnMouseMove=!0,this._isActive=!1,this._isMobileAccessibility=!1,this._div=null,this._pools={},this._renderId=0,this._children=[],this._androidUpdateCount=0,this._androidUpdateFrequency=500,this._isRunningTests=!1,this._boundOnKeyDown=this._onKeyDown.bind(this),this._boundOnMouseMove=this._onMouseMove.bind(this),this._hookDiv=null,(e.tablet||e.phone)&&this._createTouchHook(),this._renderer=t}get isActive(){return this._isActive}get isMobileAccessibility(){return this._isMobileAccessibility}get hookDiv(){return this._hookDiv}get div(){return this._div}_createTouchHook(){const t=document.createElement("button");t.style.width=`${jd}px`,t.style.height=`${jd}px`,t.style.position="absolute",t.style.top=`${MS}px`,t.style.left=`${OS}px`,t.style.zIndex=GS.toString(),t.style.backgroundColor="#FF0000",t.title="select to enable accessibility for this content",t.addEventListener("focus",()=>{this._isMobileAccessibility=!0,this._activate(),this._destroyTouchHook()}),document.body.appendChild(t),this._hookDiv=t}_destroyTouchHook(){this._hookDiv&&(document.body.removeChild(this._hookDiv),this._hookDiv=null)}_activate(){if(this._isActive)return;this._isActive=!0,this._div||(this._div=document.createElement("div"),this._div.style.position="absolute",this._div.style.top=`${CS}px`,this._div.style.left=`${RS}px`,this._div.style.pointerEvents="none",this._div.style.zIndex=Hd.toString(),this._canvasObserver=new Oa({domElement:this._div,renderer:this._renderer})),this._activateOnTab&&globalThis.addEventListener("keydown",this._boundOnKeyDown,!1),this._deactivateOnMouseMove&&globalThis.document.addEventListener("mousemove",this._boundOnMouseMove,!0);const t=this._renderer.view.canvas;if(t.parentNode)this._canvasObserver.ensureAttached(),this._initAccessibilitySetup();else{const e=new MutationObserver(()=>{t.parentNode&&(e.disconnect(),this._canvasObserver.ensureAttached(),this._initAccessibilitySetup())});e.observe(document.body,{childList:!0,subtree:!0})}}_initAccessibilitySetup(){this._renderer.runners.postrender.add(this),this._renderer.lastObjectRendered&&this._updateAccessibleObjects(this._renderer.lastObjectRendered)}_deactivate(){var t,e;if(!(!this._isActive||this._isMobileAccessibility)){this._isActive=!1,globalThis.document.removeEventListener("mousemove",this._boundOnMouseMove,!0),this._activateOnTab&&globalThis.addEventListener("keydown",this._boundOnKeyDown,!1),this._renderer.runners.postrender.remove(this);for(const i of this._children)(t=i._accessibleDiv)!=null&&t.parentNode&&(i._accessibleDiv.parentNode.removeChild(i._accessibleDiv),i._accessibleDiv=null),i._accessibleActive=!1;for(const i in this._pools)this._pools[i].forEach(s=>{s.parentNode&&s.parentNode.removeChild(s)}),delete this._pools[i];(e=this._div)!=null&&e.parentNode&&this._div.parentNode.removeChild(this._div),this._pools={},this._children=[]}}_updateAccessibleObjects(t){if(!t.visible||!t.accessibleChildren)return;t.accessible&&(t._accessibleActive||this._addChild(t),t._renderId=this._renderId);const e=t.children;if(e)for(let i=0;i=0;i--){const s=this._children[i];e.has(i)||(s._accessibleDiv&&s._accessibleDiv.parentNode&&(s._accessibleDiv.parentNode.removeChild(s._accessibleDiv),this._getPool(s.accessibleType).push(s._accessibleDiv),s._accessibleDiv=null),s._accessibleActive=!1,xa(this._children,i,1))}this._renderer.renderingToScreen&&this._canvasObserver.ensureAttached();for(let i=0;i title : ${t.title}
tabIndex: ${t.tabIndex}`}_capHitArea(t){t.x<0&&(t.width+=t.x,t.x=0),t.y<0&&(t.height+=t.y,t.y=0);const{width:e,height:i}=this._renderer;t.x+t.width>e&&(t.width=e-t.x),t.y+t.height>i&&(t.height=i-t.y)}_addChild(t){let e=this._getPool(t.accessibleType).pop();e?(e.innerHTML="",e.removeAttribute("title"),e.removeAttribute("aria-label"),e.tabIndex=0):(t.accessibleType==="button"?e=document.createElement("button"):(e=document.createElement(t.accessibleType),e.style.cssText=` + color: transparent; + pointer-events: none; + padding: 0; + margin: 0; + border: 0; + outline: 0; + background: transparent; + box-sizing: border-box; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + `,t.accessibleText&&(e.innerText=t.accessibleText)),e.style.width=`${Xd}px`,e.style.height=`${Xd}px`,e.style.backgroundColor=this.debug?"rgba(255,255,255,0.5)":"transparent",e.style.position="absolute",e.style.zIndex=Hd.toString(),e.style.borderStyle="none",navigator.userAgent.toLowerCase().includes("chrome")?e.setAttribute("aria-live","off"):e.setAttribute("aria-live","polite"),navigator.userAgent.match(/rv:.*Gecko\//)?e.setAttribute("aria-relevant","additions"):e.setAttribute("aria-relevant","text"),e.addEventListener("click",this._onClick.bind(this)),e.addEventListener("focus",this._onFocus.bind(this)),e.addEventListener("focusout",this._onFocusOut.bind(this))),e.style.pointerEvents=t.accessiblePointerEvents,e.type=t.accessibleType,t.accessibleTitle&&t.accessibleTitle!==null?e.title=t.accessibleTitle:(!t.accessibleHint||t.accessibleHint===null)&&(e.title=`container ${t.tabIndex}`),t.accessibleHint&&t.accessibleHint!==null&&e.setAttribute("aria-label",t.accessibleHint),t.interactive?e.tabIndex=t.tabIndex:e.tabIndex=0,this.debug&&this._updateDebugHTML(e),t._accessibleActive=!0,t._accessibleDiv=e,e.container=t,this._children.push(t),this._div.appendChild(t._accessibleDiv)}_dispatchEvent(t,e){const{container:i}=t.target,s=this._renderer.events.rootBoundary,n=Object.assign(new gr(s),{target:i});s.rootTarget=this._renderer.lastObjectRendered,e.forEach(a=>s.dispatchEvent(n,a))}_onClick(t){this._dispatchEvent(t,["click","pointertap","tap"])}_onFocus(t){t.target.getAttribute("aria-live")||t.target.setAttribute("aria-live","assertive"),this._dispatchEvent(t,["mouseover"])}_onFocusOut(t){t.target.getAttribute("aria-live")||t.target.setAttribute("aria-live","polite"),this._dispatchEvent(t,["mouseout"])}_onKeyDown(t){t.keyCode!==AS||!this._activateOnTab||this._activate()}_onMouseMove(t){t.movementX===0&&t.movementY===0||this._deactivate()}destroy(){var t;this._deactivate(),this._destroyTouchHook(),(t=this._canvasObserver)==null||t.destroy(),this._canvasObserver=null,this._div=null,this._pools=null,this._children=null,this._renderer=null,this._hookDiv=null,globalThis.removeEventListener("keydown",this._boundOnKeyDown),this._boundOnKeyDown=null,globalThis.document.removeEventListener("mousemove",this._boundOnMouseMove,!0),this._boundOnMouseMove=null}setAccessibilityEnabled(t){t?this._activate():this._deactivate()}_getPool(t){return this._pools[t]||(this._pools[t]=[]),this._pools[t]}};Ba.extension={type:[T.WebGLSystem,T.WebGPUSystem],name:"accessibility"},Ba.defaultOptions={enabledByDefault:!1,debug:!1,activateOnTab:!0,deactivateOnMouseMove:!0};let zd=Ba;const Wd={accessible:!1,accessibleTitle:null,accessibleHint:null,tabIndex:0,accessibleType:"button",accessibleText:null,accessiblePointerEvents:"auto",accessibleChildren:!0,_accessibleActive:!1,_accessibleDiv:null,_renderId:-1};$.add(zd),$.mixin(dt,Wd);let IS=class{constructor(){this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this._tickerAdded=!1,this._pauseUpdate=!0}init(t){this.removeTickerListener(),this.events=t,this.interactionFrequency=10,this._deltaTime=0,this._didMove=!1,this._tickerAdded=!1,this._pauseUpdate=!0}get pauseUpdate(){return this._pauseUpdate}set pauseUpdate(t){this._pauseUpdate=t}addTickerListener(){this._tickerAdded||!this.domElement||(Ct.system.add(this._tickerUpdate,this,ve.INTERACTION),this._tickerAdded=!0)}removeTickerListener(){this._tickerAdded&&(Ct.system.remove(this._tickerUpdate,this),this._tickerAdded=!1)}pointerMoved(){this._didMove=!0}_update(){if(!this.domElement||this._pauseUpdate)return;if(this._didMove){this._didMove=!1;return}const t=this.events._rootPointerEvent;this.events.supportsTouchEvents&&t.pointerType==="touch"||globalThis.document.dispatchEvent(this.events.supportsPointerEvents?new PointerEvent("pointermove",{clientX:t.clientX,clientY:t.clientY,pointerType:t.pointerType,pointerId:t.pointerId}):new MouseEvent("mousemove",{clientX:t.clientX,clientY:t.clientY}))}_tickerUpdate(t){this._deltaTime+=t.deltaTime,!(this._deltaTimei.priority-s.priority)}dispatchEvent(t,e){t.propagationStopped=!1,t.propagationImmediatelyStopped=!1,this.propagate(t,e),this.dispatch.emit(e||t.type,t)}mapEvent(t){if(!this.rootTarget)return;const e=this.mappingTable[t.type];if(e)for(let i=0,s=e.length;i=0;s--)if(t.currentTarget=i[s],this.notifyTarget(t,e),t.propagationStopped||t.propagationImmediatelyStopped)return}}all(t,e,i=this._allInteractiveElements){if(i.length===0)return;t.eventPhase=t.BUBBLING_PHASE;const s=Array.isArray(e)?e:[e];for(let n=i.length-1;n>=0;n--)s.forEach(a=>{t.currentTarget=i[n],this.notifyTarget(t,a)})}propagationPath(t){const e=[t];for(let i=0;i=0;h--){const p=c[h],f=this.hitTestMoveRecursive(p,this._isInteractive(e)?e:p.eventMode,i,s,n,a||n(t,i));if(f){if(f.length>0&&!f[f.length-1].parent)continue;const m=t.isInteractive();(f.length>0||m)&&(m&&this._allInteractiveElements.push(t),f.push(t)),this._hitElements.length===0&&(this._hitElements=f),o=!0}}}const l=this._isInteractive(e),u=t.isInteractive();return u&&u&&this._allInteractiveElements.push(t),a||this._hitElements.length>0?null:o?this._hitElements:l&&!n(t,i)&&s(t,i)?u?[t]:[]:null}hitTestRecursive(t,e,i,s,n){if(this._interactivePrune(t)||n(t,i))return null;if((t.eventMode==="dynamic"||e==="dynamic")&&(ye.pauseUpdate=!1),t.interactiveChildren&&t.children){const l=t.children,u=i;for(let c=l.length-1;c>=0;c--){const h=l[c],p=this.hitTestRecursive(h,this._isInteractive(e)?e:h.eventMode,u,s,n);if(p){if(p.length>0&&!p[p.length-1].parent)continue;const f=t.isInteractive();return(p.length>0||f)&&p.push(t),p}}}const a=this._isInteractive(e),o=t.isInteractive();return a&&s(t,i)?o?[t]:[]:null}_isInteractive(t){return t==="static"||t==="dynamic"}_interactivePrune(t){return!t||!t.visible||!t.renderable||!t.measurable||t.eventMode==="none"||t.eventMode==="passive"&&!t.interactiveChildren}hitPruneFn(t,e){if(t.hitArea&&(t.worldTransform.applyInverse(e,si),!t.hitArea.contains(si.x,si.y)))return!0;if(t.effects&&t.effects.length)for(let i=0;i0&&l!==n.target){const h=t.type==="mousemove"?"mouseout":"pointerout",p=this.createPointerEvent(t,h,l);if(this.dispatchEvent(p,"pointerout"),a&&this.dispatchEvent(p,"mouseout"),!n.composedPath().includes(l)){const f=this.createPointerEvent(t,"pointerleave",l);for(f.eventPhase=f.AT_TARGET;f.target&&!n.composedPath().includes(f.target);)f.currentTarget=f.target,this.notifyTarget(f),a&&this.notifyTarget(f,"mouseleave"),f.target=f.target.parent;this.freeEvent(f)}this.freeEvent(p)}if(l!==n.target){const h=t.type==="mousemove"?"mouseover":"pointerover",p=this.clonePointerEvent(n,h);this.dispatchEvent(p,"pointerover"),a&&this.dispatchEvent(p,"mouseover");let f=l==null?void 0:l.parent;for(;f&&f!==this.rootTarget.parent&&f!==n.target;)f=f.parent;if(!f||f===this.rootTarget.parent){const m=this.clonePointerEvent(n,"pointerenter");for(m.eventPhase=m.AT_TARGET;m.target&&m.target!==l&&m.target!==this.rootTarget.parent;)m.currentTarget=m.target,this.notifyTarget(m),a&&this.notifyTarget(m,"mouseenter"),m.target=m.target.parent;this.freeEvent(m)}this.freeEvent(p)}const u=[],c=(i=this.enableGlobalMoveEvents)!=null?i:!0;this.moveOnAll?u.push("pointermove"):this.dispatchEvent(n,"pointermove"),c&&u.push("globalpointermove"),n.pointerType==="touch"&&(this.moveOnAll?u.splice(1,0,"touchmove"):this.dispatchEvent(n,"touchmove"),c&&u.push("globaltouchmove")),a&&(this.moveOnAll?u.splice(1,0,"mousemove"):this.dispatchEvent(n,"mousemove"),c&&u.push("globalmousemove"),this.cursor=(s=n.target)==null?void 0:s.cursor),u.length>0&&this.all(n,u),this._allInteractiveElements.length=0,this._hitElements.length=0,o.overTargets=n.composedPath(),this.freeEvent(n)}mapPointerOver(t){var e;if(!(t instanceof ie))return;const i=this.trackingData(t.pointerId),s=this.createPointerEvent(t),n=s.pointerType==="mouse"||s.pointerType==="pen";this.dispatchEvent(s,"pointerover"),n&&this.dispatchEvent(s,"mouseover"),s.pointerType==="mouse"&&(this.cursor=(e=s.target)==null?void 0:e.cursor);const a=this.clonePointerEvent(s,"pointerenter");for(a.eventPhase=a.AT_TARGET;a.target&&a.target!==this.rootTarget.parent;)a.currentTarget=a.target,this.notifyTarget(a),n&&this.notifyTarget(a,"mouseenter"),a.target=a.target.parent;i.overTargets=s.composedPath(),this.freeEvent(s),this.freeEvent(a)}mapPointerOut(t){if(!(t instanceof ie))return;const e=this.trackingData(t.pointerId);if(e.overTargets){const i=t.pointerType==="mouse"||t.pointerType==="pen",s=this.findMountedTarget(e.overTargets),n=this.createPointerEvent(t,"pointerout",s);this.dispatchEvent(n),i&&this.dispatchEvent(n,"mouseout");const a=this.createPointerEvent(t,"pointerleave",s);for(a.eventPhase=a.AT_TARGET;a.target&&a.target!==this.rootTarget.parent;)a.currentTarget=a.target,this.notifyTarget(a),i&&this.notifyTarget(a,"mouseleave"),a.target=a.target.parent;e.overTargets=null,this.freeEvent(n),this.freeEvent(a)}this.cursor=null}mapPointerUp(t){if(!(t instanceof ie))return;const e=performance.now(),i=this.createPointerEvent(t);if(this.dispatchEvent(i,"pointerup"),i.pointerType==="touch")this.dispatchEvent(i,"touchend");else if(i.pointerType==="mouse"||i.pointerType==="pen"){const o=i.button===2;this.dispatchEvent(i,o?"rightup":"mouseup")}const s=this.trackingData(t.pointerId),n=this.findMountedTarget(s.pressTargetsByButton[t.button]);let a=n;if(n&&!i.composedPath().includes(n)){let o=n;for(;o&&!i.composedPath().includes(o);){if(i.currentTarget=o,this.notifyTarget(i,"pointerupoutside"),i.pointerType==="touch")this.notifyTarget(i,"touchendoutside");else if(i.pointerType==="mouse"||i.pointerType==="pen"){const l=i.button===2;this.notifyTarget(i,l?"rightupoutside":"mouseupoutside")}o=o.parent}delete s.pressTargetsByButton[t.button],a=o}if(a){const o=this.clonePointerEvent(i,"click");o.target=a,o.path=null,s.clicksByButton[t.button]||(s.clicksByButton[t.button]={clickCount:0,target:o.target,timeStamp:e});const l=s.clicksByButton[t.button];if(l.target===o.target&&e-l.timeStamp<200?++l.clickCount:l.clickCount=1,l.target=o.target,l.timeStamp=e,o.detail=l.clickCount,o.pointerType==="mouse"){const u=o.button===2;this.dispatchEvent(o,u?"rightclick":"click")}else o.pointerType==="touch"&&this.dispatchEvent(o,"tap");this.dispatchEvent(o,"pointertap"),this.freeEvent(o)}this.freeEvent(i)}mapPointerUpOutside(t){if(!(t instanceof ie))return;const e=this.trackingData(t.pointerId),i=this.findMountedTarget(e.pressTargetsByButton[t.button]),s=this.createPointerEvent(t);if(i){let n=i;for(;n;)s.currentTarget=n,this.notifyTarget(s,"pointerupoutside"),s.pointerType==="touch"?this.notifyTarget(s,"touchendoutside"):(s.pointerType==="mouse"||s.pointerType==="pen")&&this.notifyTarget(s,s.button===2?"rightupoutside":"mouseupoutside"),n=n.parent;delete e.pressTargetsByButton[t.button]}this.freeEvent(s)}mapWheel(t){if(!(t instanceof qe))return;const e=this.createWheelEvent(t);this.dispatchEvent(e),this.freeEvent(e)}findMountedTarget(t){if(!t)return null;let e=t[0];for(let i=1;it in r?DS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,$S=(r,t)=>{for(var e in t||(t={}))US.call(t,e)&&Kd(r,e,t[e]);if(Yd)for(var e of Yd(t))kS.call(t,e)&&Kd(r,e,t[e]);return r};const LS=1,NS={touchstart:"pointerdown",touchend:"pointerup",touchendoutside:"pointerupoutside",touchmove:"pointermove",touchcancel:"pointercancel"},Fa=class uh{constructor(t){this.supportsTouchEvents="ontouchstart"in globalThis,this.supportsPointerEvents=!!globalThis.PointerEvent,this.domElement=null,this.resolution=1,this.renderer=t,this.rootBoundary=new Vd(null),ye.init(this),this.autoPreventDefault=!0,this._eventsAdded=!1,this._rootPointerEvent=new ie(null),this._rootWheelEvent=new qe(null),this.cursorStyles={default:"inherit",pointer:"pointer"},this.features=new Proxy($S({},uh.defaultEventFeatures),{set:(e,i,s)=>(i==="globalMove"&&(this.rootBoundary.enableGlobalMoveEvents=s),e[i]=s,!0)}),this._onPointerDown=this._onPointerDown.bind(this),this._onPointerMove=this._onPointerMove.bind(this),this._onPointerUp=this._onPointerUp.bind(this),this._onPointerOverOut=this._onPointerOverOut.bind(this),this.onWheel=this.onWheel.bind(this)}static get defaultEventMode(){return this._defaultEventMode}init(t){var e,i;const{canvas:s,resolution:n}=this.renderer;this.setTargetElement(s),this.resolution=n,uh._defaultEventMode=(e=t.eventMode)!=null?e:"passive",Object.assign(this.features,(i=t.eventFeatures)!=null?i:{}),this.rootBoundary.enableGlobalMoveEvents=this.features.globalMove}resolutionChange(t){this.resolution=t}destroy(){ye.destroy(),this.setTargetElement(null),this.renderer=null,this._currentCursor=null}setCursor(t){t||(t="default");let e=!0;if(globalThis.OffscreenCanvas&&this.domElement instanceof OffscreenCanvas&&(e=!1),this._currentCursor===t)return;this._currentCursor=t;const i=this.cursorStyles[t];if(i)switch(typeof i){case"string":e&&(this.domElement.style.cursor=i);break;case"function":i(t);break;case"object":e&&Object.assign(this.domElement.style,i);break}else e&&typeof t=="string"&&!Object.prototype.hasOwnProperty.call(this.cursorStyles,t)&&(this.domElement.style.cursor=t)}get pointer(){return this._rootPointerEvent}_onPointerDown(t){if(!this.features.click)return;this.rootBoundary.rootTarget=this.renderer.lastObjectRendered;const e=this._normalizeToPointerData(t);this.autoPreventDefault&&e[0].isNormalized&&(t.cancelable||!("cancelable"in t))&&t.preventDefault();for(let i=0,s=e.length;i0&&(e=t.composedPath()[0]);const i=e!==this.domElement?"outside":"",s=this._normalizeToPointerData(t);for(let n=0,a=s.length;n{l.off(r,o,a)}),n?l.once(r,o,a):l.on(r,o,a)},removeEventListener(r,t,e){const i=typeof e=="boolean"&&e||typeof e=="object"&&e.capture,s=typeof t=="function"?void 0:t;r=i?`${r}capture`:r,t=typeof t=="function"?t:t.handleEvent,this.off(r,t,s)},dispatchEvent(r){if(!(r instanceof gr))throw new Error("Container cannot propagate events outside of the Federated Events API");return r.defaultPrevented=!1,r.path=null,r.target=this,r.manager.dispatchEvent(r),!r.defaultPrevented}};$.add(Da),$.mixin(dt,qd);var qt=(r=>(r[r.Low=0]="Low",r[r.Normal=1]="Normal",r[r.High=2]="High",r))(qt||{});const Zd={createCanvas:(r,t)=>{const e=document.createElement("canvas");return e.width=r,e.height=t,e},createImage:()=>new Image,getCanvasRenderingContext2D:()=>CanvasRenderingContext2D,getWebGLRenderingContext:()=>WebGLRenderingContext,getNavigator:()=>navigator,getBaseUrl:()=>{var r;return(r=document.baseURI)!=null?r:window.location.href},getFontFaceSet:()=>document.fonts,fetch:(r,t)=>fetch(r,t),parseXML:r=>new DOMParser().parseFromString(r,"text/xml")};let Qd=Zd;const L={get(){return Qd},set(r){Qd=r}};function ce(r){if(typeof r!="string")throw new TypeError(`Path must be a string. Received ${JSON.stringify(r)}`)}function ni(r){return r.split("?")[0].split("#")[0]}function XS(r){return r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function HS(r,t,e){return r.replace(new RegExp(XS(t),"g"),e)}function jS(r,t){let e="",i=0,s=-1,n=0,a=-1;for(let o=0;o<=r.length;++o){if(o2){const l=e.lastIndexOf("/");if(l!==e.length-1){l===-1?(e="",i=0):(e=e.slice(0,l),i=e.length-1-e.lastIndexOf("/")),s=o,n=0;continue}}else if(e.length===2||e.length===1){e="",i=0,s=o,n=0;continue}}t&&(e.length>0?e+="/..":e="..",i=2)}else e.length>0?e+=`/${r.slice(s+1,o)}`:e=r.slice(s+1,o),i=o-s-1;s=o,n=0}else a===46&&n!==-1?++n:n=-1}return e}const Xt={toPosix(r){return HS(r,"\\","/")},isUrl(r){return/^https?:/.test(this.toPosix(r))},isDataUrl(r){return/^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i.test(r)},isBlobUrl(r){return r.startsWith("blob:")},hasProtocol(r){return/^[^/:]+:/.test(this.toPosix(r))},getProtocol(r){ce(r),r=this.toPosix(r);const t=/^file:\/\/\//.exec(r);if(t)return t[0];const e=/^[^/:]+:\/{0,2}/.exec(r);return e?e[0]:""},toAbsolute(r,t,e){if(ce(r),this.isDataUrl(r)||this.isBlobUrl(r))return r;const i=ni(this.toPosix(t!=null?t:L.get().getBaseUrl())),s=ni(this.toPosix(e!=null?e:this.rootname(i)));return r=this.toPosix(r),r.startsWith("/")?Xt.join(s,r.slice(1)):this.isAbsolute(r)?r:this.join(i,r)},normalize(r){if(ce(r),r.length===0)return".";if(this.isDataUrl(r)||this.isBlobUrl(r))return r;r=this.toPosix(r);let t="";const e=r.startsWith("/");this.hasProtocol(r)&&(t=this.rootname(r),r=r.slice(t.length));const i=r.endsWith("/");return r=jS(r,!1),r.length>0&&i&&(r+="/"),e?`/${r}`:t+r},isAbsolute(r){return ce(r),r=this.toPosix(r),this.hasProtocol(r)?!0:r.startsWith("/")},join(...r){var t;if(r.length===0)return".";let e;for(let i=0;i0)if(e===void 0)e=s;else{const n=(t=r[i-1])!=null?t:"";this.joinExtensions.includes(this.extname(n).toLowerCase())?e+=`/../${s}`:e+=`/${s}`}}return e===void 0?".":this.normalize(e)},dirname(r){if(ce(r),r.length===0)return".";r=this.toPosix(r);let t=r.charCodeAt(0);const e=t===47;let i=-1,s=!0;const n=this.getProtocol(r),a=r;r=r.slice(n.length);for(let o=r.length-1;o>=1;--o)if(t=r.charCodeAt(o),t===47){if(!s){i=o;break}}else s=!1;return i===-1?e?"/":this.isUrl(a)?n+r:n:e&&i===1?"//":n+r.slice(0,i)},rootname(r){ce(r),r=this.toPosix(r);let t="";if(r.startsWith("/")?t="/":t=this.getProtocol(r),this.isUrl(r)){const e=r.indexOf("/",t.length);e!==-1?t=r.slice(0,e):t=r,t.endsWith("/")||(t+="/")}return t},basename(r,t){ce(r),t&&ce(t),r=ni(this.toPosix(r));let e=0,i=-1,s=!0,n;if(t!==void 0&&t.length>0&&t.length<=r.length){if(t.length===r.length&&t===r)return"";let a=t.length-1,o=-1;for(n=r.length-1;n>=0;--n){const l=r.charCodeAt(n);if(l===47){if(!s){e=n+1;break}}else o===-1&&(s=!1,o=n+1),a>=0&&(l===t.charCodeAt(a)?--a===-1&&(i=n):(a=-1,i=o))}return e===i?i=o:i===-1&&(i=r.length),r.slice(e,i)}for(n=r.length-1;n>=0;--n)if(r.charCodeAt(n)===47){if(!s){e=n+1;break}}else i===-1&&(s=!1,i=n+1);return i===-1?"":r.slice(e,i)},extname(r){ce(r),r=ni(this.toPosix(r));let t=-1,e=0,i=-1,s=!0,n=0;for(let a=r.length-1;a>=0;--a){const o=r.charCodeAt(a);if(o===47){if(!s){e=a+1;break}continue}i===-1&&(s=!1,i=a+1),o===46?t===-1?t=a:n!==1&&(n=1):t!==-1&&(n=-1)}return t===-1||i===-1||n===0||n===1&&t===i-1&&t===e+1?"":r.slice(t,i)},parse(r){ce(r);const t={root:"",dir:"",base:"",ext:"",name:""};if(r.length===0)return t;r=ni(this.toPosix(r));let e=r.charCodeAt(0);const i=this.isAbsolute(r);let s;const n="";t.root=this.rootname(r),i||this.hasProtocol(r)?s=1:s=0;let a=-1,o=0,l=-1,u=!0,c=r.length-1,h=0;for(;c>=s;--c){if(e=r.charCodeAt(c),e===47){if(!u){o=c+1;break}continue}l===-1&&(u=!1,l=c+1),e===46?a===-1?a=c:h!==1&&(h=1):a!==-1&&(h=-1)}return a===-1||l===-1||h===0||h===1&&a===l-1&&a===o+1?l!==-1&&(o===0&&i?t.base=t.name=r.slice(1,l):t.base=t.name=r.slice(o,l)):(o===0&&i?(t.name=r.slice(1,a),t.base=r.slice(1,l)):(t.name=r.slice(o,a),t.base=r.slice(o,l)),t.ext=r.slice(a,l)),t.dir=this.dirname(r),n&&(t.dir=n+t.dir),t},sep:"/",delimiter:":",joinExtensions:[".html"]},se=(r,t,e=!1)=>(Array.isArray(r)||(r=[r]),t?r.map(i=>typeof i=="string"||e?t(i):i):r);function Jd(r,t,e,i,s){const n=t[e];for(let a=0;a{const a=n.substring(1,n.length-1).split(",");s.push(a)}),Jd(r,s,0,e,i)}else i.push(r);return i}const ai=r=>!Array.isArray(r);var zS=Object.defineProperty,WS=Object.defineProperties,VS=Object.getOwnPropertyDescriptors,ep=Object.getOwnPropertySymbols,YS=Object.prototype.hasOwnProperty,KS=Object.prototype.propertyIsEnumerable,rp=(r,t,e)=>t in r?zS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ze=(r,t)=>{for(var e in t||(t={}))YS.call(t,e)&&rp(r,e,t[e]);if(ep)for(var e of ep(t))KS.call(t,e)&&rp(r,e,t[e]);return r},qS=(r,t)=>WS(r,VS(t));class Fe{constructor(){this._defaultBundleIdentifierOptions={connector:"-",createBundleAssetId:(t,e)=>`${t}${this._bundleIdConnector}${e}`,extractAssetIdFromBundle:(t,e)=>e.replace(`${t}${this._bundleIdConnector}`,"")},this._bundleIdConnector=this._defaultBundleIdentifierOptions.connector,this._createBundleAssetId=this._defaultBundleIdentifierOptions.createBundleAssetId,this._extractAssetIdFromBundle=this._defaultBundleIdentifierOptions.extractAssetIdFromBundle,this._assetMap={},this._preferredOrder=[],this._parsers=[],this._resolverHash={},this._bundles={}}setBundleIdentifier(t){var e,i,s;if(this._bundleIdConnector=(e=t.connector)!=null?e:this._bundleIdConnector,this._createBundleAssetId=(i=t.createBundleAssetId)!=null?i:this._createBundleAssetId,this._extractAssetIdFromBundle=(s=t.extractAssetIdFromBundle)!=null?s:this._extractAssetIdFromBundle,this._extractAssetIdFromBundle("foo",this._createBundleAssetId("foo","bar"))!=="bar")throw new Error("[Resolver] GenerateBundleAssetId are not working correctly")}prefer(...t){t.forEach(e=>{this._preferredOrder.push(e),e.priority||(e.priority=Object.keys(e.params))}),this._resolverHash={}}set basePath(t){this._basePath=t}get basePath(){return this._basePath}set rootPath(t){this._rootPath=t}get rootPath(){return this._rootPath}get parsers(){return this._parsers}reset(){this.setBundleIdentifier(this._defaultBundleIdentifierOptions),this._assetMap={},this._preferredOrder=[],this._resolverHash={},this._rootPath=null,this._basePath=null,this._manifest=null,this._bundles={},this._defaultSearchParams=null}setDefaultSearchParams(t){if(typeof t=="string")this._defaultSearchParams=t;else{const e=t;this._defaultSearchParams=Object.keys(e).map(i=>`${encodeURIComponent(i)}=${encodeURIComponent(e[i])}`).join("&")}}getAlias(t){const{alias:e,src:i}=t;return se(e||i,s=>typeof s=="string"?s:Array.isArray(s)?s.map(n=>{var a;return(a=n==null?void 0:n.src)!=null?a:n}):s!=null&&s.src?s.src:s,!0)}addManifest(t){this._manifest,this._manifest=t,t.bundles.forEach(e=>{this.addBundle(e.name,e.assets)})}addBundle(t,e){const i=[];let s=e;Array.isArray(e)||(s=Object.entries(e).map(([n,a])=>typeof a=="string"||Array.isArray(a)?{alias:n,src:a}:Ze({alias:n},a))),s.forEach(n=>{const a=n.src,o=n.alias;let l;if(typeof o=="string"){const u=this._createBundleAssetId(t,o);i.push(u),l=[o,u]}else{const u=o.map(c=>this._createBundleAssetId(t,c));i.push(...u),l=[...o,...u]}this.add(qS(Ze({},n),{alias:l,src:a}))}),this._bundles[t]=i}add(t){const e=[];Array.isArray(t)?e.push(...t):e.push(t);let i;se(e).forEach(s=>{const{src:n}=s;let{data:a,format:o,loadParser:l,parser:u}=s;const c=se(n).map(m=>typeof m=="string"?tp(m):Array.isArray(m)?m:[m]),h=this.getAlias(s),p=[],f=m=>{const g=this._parsers.find(_=>_.test(m));return Ze({src:m},g==null?void 0:g.parse(m))};c.forEach(m=>{m.forEach(g=>{var _,b,y,x;let v={};if(typeof g!="object"?v=f(g):(a=(_=g.data)!=null?_:a,o=(b=g.format)!=null?b:o,(g.loadParser||g.parser)&&(l=(y=g.loadParser)!=null?y:l,u=(x=g.parser)!=null?x:u),v=Ze(Ze({},f(g.src)),g)),!h)throw new Error(`[Resolver] alias is undefined for this asset: ${v.src}`);v=this._buildResolvedAsset(v,{aliases:h,data:a,format:o,loadParser:l,parser:u,progressSize:s.progressSize}),p.push(v)})}),h.forEach(m=>{this._assetMap[m]=p})})}resolveBundle(t){const e=ai(t);t=se(t);const i={};return t.forEach(s=>{const n=this._bundles[s];if(n){const a=this.resolve(n),o={};for(const l in a){const u=a[l];o[this._extractAssetIdFromBundle(s,l)]=u}i[s]=o}}),e?i[t[0]]:i}resolveUrl(t){const e=this.resolve(t);if(typeof t!="string"){const i={};for(const s in e)i[s]=e[s].src;return i}return e.src}resolve(t){const e=ai(t);t=se(t);const i={};return t.forEach(s=>{if(!this._resolverHash[s])if(this._assetMap[s]){let n=this._assetMap[s];const a=this._getPreferredOrder(n);a==null||a.priority.forEach(o=>{a.params[o].forEach(l=>{const u=n.filter(c=>c[o]?c[o]===l:!1);u.length&&(n=u)})}),this._resolverHash[s]=n[0]}else this._resolverHash[s]=this._buildResolvedAsset({alias:[s],src:s},{});i[s]=this._resolverHash[s]}),e?i[t[0]]:i}hasKey(t){return!!this._assetMap[t]}hasBundle(t){return!!this._bundles[t]}_getPreferredOrder(t){for(let e=0;en.params.format.includes(i.format));if(s)return s}return this._preferredOrder[0]}_appendDefaultSearchParams(t){if(!this._defaultSearchParams)return t;const e=/\?/.test(t)?"&":"?";return`${t}${e}${this._defaultSearchParams}`}_buildResolvedAsset(t,e){var i,s;const{aliases:n,data:a,loadParser:o,parser:l,format:u,progressSize:c}=e;return(this._basePath||this._rootPath)&&(t.src=Xt.toAbsolute(t.src,this._basePath,this._rootPath)),t.alias=(i=n!=null?n:t.alias)!=null?i:[t.src],t.src=this._appendDefaultSearchParams(t.src),t.data=Ze(Ze({},a||{}),t.data),t.loadParser=o!=null?o:t.loadParser,t.parser=l!=null?l:t.parser,t.format=(s=u!=null?u:t.format)!=null?s:ip(t.src),c!==void 0&&(t.progressSize=c),t}}Fe.RETINA_PREFIX=/@([0-9\.]+)x/;function ip(r){return r.split(".").pop().split("?").shift().split("#").shift()}const ls=(r,t)=>{const e=t.split("?")[1];return e&&(r+=`?${e}`),r},sp=class Wi{constructor(t,e){this.linkedSheets=[];let i=t;(t==null?void 0:t.source)instanceof ht&&(i={texture:t,data:e});const{texture:s,data:n,cachePrefix:a=""}=i;this.cachePrefix=a,this._texture=s instanceof F?s:null,this.textureSource=s.source,this.textures={},this.animations={},this.data=n;const o=parseFloat(n.meta.scale);o?(this.resolution=o,s.source.resolution=this.resolution):this.resolution=s.source._resolution,this._frames=this.data.frames,this._frameKeys=Object.keys(this._frames),this._batchIndex=0,this._callback=null}parse(){return new Promise(t=>{this._callback=t,this._batchIndex=0,this._frameKeys.length<=Wi.BATCH_SIZE?(this._processFrames(0),this._processAnimations(),this._parseComplete()):this._nextBatch()})}parseSync(){return this._processFrames(0,!0),this._processAnimations(),this.textures}_processFrames(t,e=!1){let i=t;const s=e?1/0:Wi.BATCH_SIZE;for(;i-t{this._batchIndex*Wi.BATCH_SIZE{i[s]=t}),Object.keys(t.textures).forEach(s=>{i[`${t.cachePrefix}${s}`]=t.textures[s]}),!e){const s=Xt.dirname(r[0]);t.linkedSheets.forEach((n,a)=>{const o=np([`${s}/${t.data.meta.related_multi_packs[a]}`],n,!0);Object.assign(i,o)})}return i}const ap={extension:T.Asset,cache:{test:r=>r instanceof Ua,getCacheableAssets:(r,t)=>np(r,t,!1)},resolver:{extension:{type:T.ResolveParser,name:"resolveSpritesheet"},test:r=>{const t=r.split("?")[0].split("."),e=t.pop(),i=t.pop();return e==="json"&&ZS.includes(i)},parse:r=>{var t,e;const i=r.split(".");return{resolution:parseFloat((e=(t=Fe.RETINA_PREFIX.exec(r))==null?void 0:t[1])!=null?e:"1"),format:i[i.length-2],src:r}}},loader:{name:"spritesheetLoader",id:"spritesheet",extension:{type:T.LoadParser,priority:qt.Normal,name:"spritesheetLoader"},async testParse(r,t){return Xt.extname(t.src).toLowerCase()===".json"&&!!r.frames},async parse(r,t,e){var i,s,n;const{texture:a,imageFilename:o,textureOptions:l,cachePrefix:u}=(i=t==null?void 0:t.data)!=null?i:{};let c=Xt.dirname(t.src);c&&c.lastIndexOf("/")!==c.length-1&&(c+="/");let h;if(a instanceof F)h=a;else{const m=ls(c+(o!=null?o:r.meta.image),t.src);h=(await e.load([{src:m,data:l}]))[m]}const p=new Ua({texture:h.source,data:r,cachePrefix:u});await p.parse();const f=(s=r==null?void 0:r.meta)==null?void 0:s.related_multi_packs;if(Array.isArray(f)){const m=[];for(const _ of f){if(typeof _!="string")continue;let b=c+_;(n=t.data)!=null&&n.ignoreMultiPack||(b=ls(b,t.src),m.push(e.load({src:b,data:{textureOptions:l,ignoreMultiPack:!0}})))}const g=await Promise.all(m);p.linkedSheets=g,g.forEach(_=>{_.linkedSheets=[p].concat(p.linkedSheets.filter(b=>b!==_))})}return p},async unload(r,t,e){await e.unload(r.textureSource._sourceOrigin),r.destroy(!1)}}};$.add(ap);function ka(r,t,e){const{width:i,height:s}=e.orig,n=e.trim;if(n){const a=n.width,o=n.height;r.minX=n.x-t._x*i,r.maxX=r.minX+a,r.minY=n.y-t._y*s,r.maxY=r.minY+o}else r.minX=-t._x*i,r.maxX=r.minX+i,r.minY=-t._y*s,r.maxY=r.minY+s}class xe extends dt{constructor(t){var e;super(t),this.canBundle=!0,this.allowChildren=!1,this._roundPixels=0,this._lastUsed=-1,this._gpuData=Object.create(null),this.autoGarbageCollect=!0,this._gcLastUsed=-1,this._bounds=new Pt(0,1,0,0),this._boundsDirty=!0,this.autoGarbageCollect=(e=t.autoGarbageCollect)!=null?e:!0}get bounds(){return this._boundsDirty?(this.updateBounds(),this._boundsDirty=!1,this._bounds):this._bounds}get roundPixels(){return!!this._roundPixels}set roundPixels(t){this._roundPixels=t?1:0}containsPoint(t){const e=this.bounds,{x:i,y:s}=t;return i>=e.minX&&i<=e.maxX&&s>=e.minY&&s<=e.maxY}onViewUpdate(){if(this._didViewChangeTick++,this._boundsDirty=!0,this.didViewUpdate)return;this.didViewUpdate=!0;const t=this.renderGroup||this.parentRenderGroup;t&&t.onChildViewUpdate(this)}unload(){var t;this.emit("unload",this);for(const e in this._gpuData)(t=this._gpuData[e])==null||t.destroy();this._gpuData=Object.create(null),this.onViewUpdate()}destroy(t){this.unload(),super.destroy(t),this._bounds=null}collectRenderablesSimple(t,e,i){const{renderPipes:s}=e;s.blendMode.pushBlendMode(this,this.groupBlendMode,t);const n=s[this.renderPipeId];n!=null&&n.addRenderable&&n.addRenderable(this,t),this.didViewUpdate=!1;const a=this.children,o=a.length;for(let l=0;lt in r?QS(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,JS=(r,t)=>{for(var e in t||(t={}))op.call(t,e)&&up(r,e,t[e]);if(us)for(var e of us(t))lp.call(t,e)&&up(r,e,t[e]);return r},t2=(r,t)=>{var e={};for(var i in r)op.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&us)for(var i of us(r))t.indexOf(i)<0&&lp.call(r,i)&&(e[i]=r[i]);return e};class he extends xe{constructor(t=F.EMPTY){t instanceof F&&(t={texture:t});const e=t,{texture:i=F.EMPTY,anchor:s,roundPixels:n,width:a,height:o}=e,l=t2(e,["texture","anchor","roundPixels","width","height"]);super(JS({label:"Sprite"},l)),this.renderPipeId="sprite",this.batched=!0,this._visualBounds={minX:0,maxX:1,minY:0,maxY:0},this._anchor=new gt({_onUpdate:()=>{this.onViewUpdate()}}),s?this.anchor=s:i.defaultAnchor&&(this.anchor=i.defaultAnchor),this.texture=i,this.allowChildren=!1,this.roundPixels=n!=null?n:!1,a!==void 0&&(this.width=a),o!==void 0&&(this.height=o)}static from(t,e=!1){return t instanceof F?new he(t):new he(F.from(t,e))}set texture(t){t||(t=F.EMPTY);const e=this._texture;e!==t&&(e&&e.dynamic&&e.off("update",this.onViewUpdate,this),t.dynamic&&t.on("update",this.onViewUpdate,this),this._texture=t,this._width&&this._setWidth(this._width,this._texture.orig.width),this._height&&this._setHeight(this._height,this._texture.orig.height),this.onViewUpdate())}get texture(){return this._texture}get visualBounds(){return ka(this._visualBounds,this._anchor,this._texture),this._visualBounds}get sourceBounds(){return this.visualBounds}updateBounds(){const t=this._anchor,e=this._texture,i=this._bounds,{width:s,height:n}=e.orig;i.minX=-t._x*s,i.maxX=i.minX+s,i.minY=-t._y*n,i.maxY=i.minY+n}destroy(t=!1){if(super.destroy(t),typeof t=="boolean"?t:t==null?void 0:t.texture){const e=typeof t=="boolean"?t:t==null?void 0:t.textureSource;this._texture.destroy(e)}this._texture=null,this._visualBounds=null,this._bounds=null,this._anchor=null}get anchor(){return this._anchor}set anchor(t){typeof t=="number"?this._anchor.set(t):this._anchor.copyFrom(t)}get width(){return Math.abs(this.scale.x)*this._texture.orig.width}set width(t){this._setWidth(t,this._texture.orig.width),this._width=t}get height(){return Math.abs(this.scale.y)*this._texture.orig.height}set height(t){this._setHeight(t,this._texture.orig.height),this._height=t}getSize(t){return t||(t={}),t.width=Math.abs(this.scale.x)*this._texture.orig.width,t.height=Math.abs(this.scale.y)*this._texture.orig.height,t}setSize(t,e){var i;typeof t=="object"?(e=(i=t.height)!=null?i:t.width,t=t.width):e!=null||(e=t),t!==void 0&&this._setWidth(t,this._texture.orig.width),e!==void 0&&this._setHeight(e,this._texture.orig.height)}}const e2=new Pt;function cs(r,t,e){const i=e2;r.measurable=!0,ti(r,e,i),t.addBoundsMask(i),r.measurable=!1}function hs(r,t,e){const i=_e.get();r.measurable=!0;const s=Bt.get().identity(),n=cp(r,e,s);Ji(r,i,n),r.measurable=!1,t.addBoundsMask(i),Bt.return(s),_e.return(i)}function cp(r,t,e){return r&&r!==t&&(cp(r.parent,t,e),r.updateLocalTransform(),e.append(r.localTransform)),e}class $a{constructor(t){this.priority=0,this.inverse=!1,this.pipe="alphaMask",t!=null&&t.mask&&this.init(t.mask)}init(t){this.mask=t,this.renderMaskToTexture=!(t instanceof he),this.mask.renderable=this.renderMaskToTexture,this.mask.includeInBuild=!this.renderMaskToTexture,this.mask.measurable=!1}reset(){this.mask!==null&&(this.mask.measurable=!0,this.mask=null)}addBounds(t,e){this.inverse||cs(this.mask,t,e)}addLocalBounds(t,e){hs(this.mask,t,e)}containsPoint(t,e){const i=this.mask;return e(i,t)}destroy(){this.reset()}static test(t){return t instanceof he}}$a.extension=T.MaskEffect;class La{constructor(t){this.priority=0,this.pipe="colorMask",t!=null&&t.mask&&this.init(t.mask)}init(t){this.mask=t}destroy(){}static test(t){return typeof t=="number"}}La.extension=T.MaskEffect;class Na{constructor(t){this.priority=0,this.pipe="stencilMask",t!=null&&t.mask&&this.init(t.mask)}init(t){this.mask=t,this.mask.includeInBuild=!1,this.mask.measurable=!1}reset(){this.mask!==null&&(this.mask.measurable=!0,this.mask.includeInBuild=!0,this.mask=null)}addBounds(t,e){cs(this.mask,t,e)}addLocalBounds(t,e){hs(this.mask,t,e)}containsPoint(t,e){const i=this.mask;return e(i,t)}destroy(){this.reset()}static test(t){return t instanceof dt}}Na.extension=T.MaskEffect;class de extends ht{constructor(t){t.resource||(t.resource=L.get().createCanvas()),t.width||(t.width=t.resource.width,t.autoDensity||(t.width/=t.resolution)),t.height||(t.height=t.resource.height,t.autoDensity||(t.height/=t.resolution)),super(t),this.uploadMethodId="image",this.autoDensity=t.autoDensity,this.resizeCanvas(),this.transparent=!!t.transparent}resizeCanvas(){this.autoDensity&&"style"in this.resource&&(this.resource.style.width=`${this.width}px`,this.resource.style.height=`${this.height}px`),(this.resource.width!==this.pixelWidth||this.resource.height!==this.pixelHeight)&&(this.resource.width=this.pixelWidth,this.resource.height=this.pixelHeight)}resize(t=this.width,e=this.height,i=this._resolution){const s=super.resize(t,e,i);return s&&this.resizeCanvas(),s}static test(t){return globalThis.HTMLCanvasElement&&t instanceof HTMLCanvasElement||globalThis.OffscreenCanvas&&t instanceof OffscreenCanvas}get context2D(){return this._context2D||(this._context2D=this.resource.getContext("2d"))}}de.extension=T.TextureSource;class De extends ht{constructor(t){super(t),this.uploadMethodId="image",this.autoGarbageCollect=!0}static test(t){return globalThis.HTMLImageElement&&t instanceof HTMLImageElement||typeof ImageBitmap!="undefined"&&t instanceof ImageBitmap||globalThis.VideoFrame&&t instanceof VideoFrame}}De.extension=T.TextureSource;let Xa;async function Ha(){return Xa!=null||(Xa=(async()=>{var r;const t=L.get().createCanvas(1,1).getContext("webgl");if(!t)return"premultiply-alpha-on-upload";const e=await new Promise(a=>{const o=document.createElement("video");o.onloadeddata=()=>a(o),o.onerror=()=>a(null),o.autoplay=!1,o.crossOrigin="anonymous",o.preload="auto",o.src="data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEAAAAAAAHTEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggEXTbuMU6uEHFO7a1OsggG97AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmoCrXsYMPQkBNgIRMYXZmV0GETGF2ZkSJiEBEAAAAAAAAFlSua8yuAQAAAAAAAEPXgQFzxYgAAAAAAAAAAZyBACK1nIN1bmSIgQCGhVZfVlA5g4EBI+ODhAJiWgDglLCBArqBApqBAlPAgQFVsIRVuYEBElTDZ9Vzc9JjwItjxYgAAAAAAAAAAWfInEWjh0VOQ09ERVJEh49MYXZjIGxpYnZweC12cDlnyKJFo4hEVVJBVElPTkSHlDAwOjAwOjAwLjA0MDAwMDAwMAAAH0O2dcfngQCgwqGggQAAAIJJg0IAABAAFgA4JBwYSgAAICAAEb///4r+AAB1oZ2mm+6BAaWWgkmDQgAAEAAWADgkHBhKAAAgIABIQBxTu2uRu4+zgQC3iveBAfGCAXHwgQM=",o.load()});if(!e)return"premultiply-alpha-on-upload";const i=t.createTexture();t.bindTexture(t.TEXTURE_2D,i);const s=t.createFramebuffer();t.bindFramebuffer(t.FRAMEBUFFER,s),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,i,0),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),t.pixelStorei(t.UNPACK_COLORSPACE_CONVERSION_WEBGL,t.NONE),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e);const n=new Uint8Array(4);return t.readPixels(0,0,1,1,t.RGBA,t.UNSIGNED_BYTE,n),t.deleteFramebuffer(s),t.deleteTexture(i),(r=t.getExtension("WEBGL_lose_context"))==null||r.loseContext(),n[0]<=n[3]?"premultiplied-alpha":"premultiply-alpha-on-upload"})()),Xa}var r2=Object.defineProperty,i2=Object.defineProperties,s2=Object.getOwnPropertyDescriptors,hp=Object.getOwnPropertySymbols,n2=Object.prototype.hasOwnProperty,a2=Object.prototype.propertyIsEnumerable,dp=(r,t,e)=>t in r?r2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,ja=(r,t)=>{for(var e in t||(t={}))n2.call(t,e)&&dp(r,e,t[e]);if(hp)for(var e of hp(t))a2.call(t,e)&&dp(r,e,t[e]);return r},o2=(r,t)=>i2(r,s2(t));const ds=class ZT extends ht{constructor(t){var e;super(t),this.isReady=!1,this.uploadMethodId="video",t=ja(ja({},ZT.defaultOptions),t),this._autoUpdate=!0,this._isConnectedToTicker=!1,this._updateFPS=t.updateFPS||0,this._msToNextUpdate=0,this.autoPlay=t.autoPlay!==!1,this.alphaMode=(e=t.alphaMode)!=null?e:"premultiply-alpha-on-upload",this._videoFrameRequestCallback=this._videoFrameRequestCallback.bind(this),this._videoFrameRequestCallbackHandle=null,this._load=null,this._resolve=null,this._reject=null,this._onCanPlay=this._onCanPlay.bind(this),this._onCanPlayThrough=this._onCanPlayThrough.bind(this),this._onError=this._onError.bind(this),this._onPlayStart=this._onPlayStart.bind(this),this._onPlayStop=this._onPlayStop.bind(this),this._onSeeked=this._onSeeked.bind(this),t.autoLoad!==!1&&this.load()}updateFrame(){if(!this.destroyed){if(this._updateFPS){const t=Ct.shared.elapsedMS*this.resource.playbackRate;this._msToNextUpdate=Math.floor(this._msToNextUpdate-t)}(!this._updateFPS||this._msToNextUpdate<=0)&&(this._msToNextUpdate=this._updateFPS?Math.floor(1e3/this._updateFPS):0),this.isValid&&this.update()}}_videoFrameRequestCallback(){this.updateFrame(),this.destroyed?this._videoFrameRequestCallbackHandle=null:this._videoFrameRequestCallbackHandle=this.resource.requestVideoFrameCallback(this._videoFrameRequestCallback)}get isValid(){return!!this.resource.videoWidth&&!!this.resource.videoHeight}async load(){if(this._load)return this._load;const t=this.resource,e=this.options;return(t.readyState===t.HAVE_ENOUGH_DATA||t.readyState===t.HAVE_FUTURE_DATA)&&t.width&&t.height&&(t.complete=!0),t.addEventListener("play",this._onPlayStart),t.addEventListener("pause",this._onPlayStop),t.addEventListener("seeked",this._onSeeked),this._isSourceReady()?this._mediaReady():(e.preload||t.addEventListener("canplay",this._onCanPlay),t.addEventListener("canplaythrough",this._onCanPlayThrough),t.addEventListener("error",this._onError,!0)),this.alphaMode=await Ha(),this._load=new Promise((i,s)=>{this.isValid?i(this):(this._resolve=i,this._reject=s,e.preloadTimeoutMs!==void 0&&(this._preloadTimeout=setTimeout(()=>{this._onError(new ErrorEvent(`Preload exceeded timeout of ${e.preloadTimeoutMs}ms`))})),t.load())}),this._load}_onError(t){this.resource.removeEventListener("error",this._onError,!0),this.emit("error",t),this._reject&&(this._reject(t),this._reject=null,this._resolve=null)}_isSourcePlaying(){const t=this.resource;return!t.paused&&!t.ended}_isSourceReady(){return this.resource.readyState>2}_onPlayStart(){this.isValid||this._mediaReady(),this._configureAutoUpdate()}_onPlayStop(){this._configureAutoUpdate()}_onSeeked(){this._autoUpdate&&!this._isSourcePlaying()&&(this._msToNextUpdate=0,this.updateFrame(),this._msToNextUpdate=0)}_onCanPlay(){this.resource.removeEventListener("canplay",this._onCanPlay),this._mediaReady()}_onCanPlayThrough(){this.resource.removeEventListener("canplaythrough",this._onCanPlay),this._preloadTimeout&&(clearTimeout(this._preloadTimeout),this._preloadTimeout=void 0),this._mediaReady()}_mediaReady(){const t=this.resource;this.isValid&&(this.isReady=!0,this.resize(t.videoWidth,t.videoHeight)),this._msToNextUpdate=0,this.updateFrame(),this._msToNextUpdate=0,this._resolve&&(this._resolve(this),this._resolve=null,this._reject=null),this._isSourcePlaying()?this._onPlayStart():this.autoPlay&&this.resource.play()}destroy(){this._configureAutoUpdate();const t=this.resource;t&&(t.removeEventListener("play",this._onPlayStart),t.removeEventListener("pause",this._onPlayStop),t.removeEventListener("seeked",this._onSeeked),t.removeEventListener("canplay",this._onCanPlay),t.removeEventListener("canplaythrough",this._onCanPlayThrough),t.removeEventListener("error",this._onError,!0),t.pause(),t.src="",t.load()),super.destroy()}get autoUpdate(){return this._autoUpdate}set autoUpdate(t){t!==this._autoUpdate&&(this._autoUpdate=t,this._configureAutoUpdate())}get updateFPS(){return this._updateFPS}set updateFPS(t){t!==this._updateFPS&&(this._updateFPS=t,this._configureAutoUpdate())}_configureAutoUpdate(){this._autoUpdate&&this._isSourcePlaying()?!this._updateFPS&&this.resource.requestVideoFrameCallback?(this._isConnectedToTicker&&(Ct.shared.remove(this.updateFrame,this),this._isConnectedToTicker=!1,this._msToNextUpdate=0),this._videoFrameRequestCallbackHandle===null&&(this._videoFrameRequestCallbackHandle=this.resource.requestVideoFrameCallback(this._videoFrameRequestCallback))):(this._videoFrameRequestCallbackHandle!==null&&(this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle),this._videoFrameRequestCallbackHandle=null),this._isConnectedToTicker||(Ct.shared.add(this.updateFrame,this),this._isConnectedToTicker=!0,this._msToNextUpdate=0)):(this._videoFrameRequestCallbackHandle!==null&&(this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle),this._videoFrameRequestCallbackHandle=null),this._isConnectedToTicker&&(Ct.shared.remove(this.updateFrame,this),this._isConnectedToTicker=!1,this._msToNextUpdate=0))}static test(t){return globalThis.HTMLVideoElement&&t instanceof HTMLVideoElement}};ds.extension=T.TextureSource,ds.defaultOptions=o2(ja({},ht.defaultOptions),{autoLoad:!0,autoPlay:!0,updateFPS:0,crossorigin:!0,loop:!1,muted:!0,playsinline:!0,preload:!1}),ds.MIME_TYPES={ogv:"video/ogg",mov:"video/quicktime",m4v:"video/mp4"};let br=ds,l2=class{constructor(){this._parsers=[],this._cache=new Map,this._cacheMap=new Map}reset(){this._cacheMap.clear(),this._cache.clear()}has(t){return this._cache.has(t)}get(t){return this._cache.get(t)}set(t,e){const i=se(t);let s;for(let l=0;l{n.set(l,e)});const a=[...n.keys()],o={cacheKeys:a,keys:i};i.forEach(l=>{this._cacheMap.set(l,o)}),a.forEach(l=>{const u=s?s[l]:e;this._cache.has(l)&&this._cache.get(l),this._cache.set(l,n.get(l))})}remove(t){if(!this._cacheMap.has(t))return;const e=this._cacheMap.get(t);e.cacheKeys.forEach(i=>{this._cache.delete(i)}),e.keys.forEach(i=>{this._cacheMap.delete(i)})}get parsers(){return this._parsers}};const tt=new l2,za=[];$.handleByList(T.TextureSource,za);function u2(r={}){return Wa(r)}function Wa(r={}){const t=r&&r.resource,e=t?r.resource:r,i=t?r:{resource:r};for(let s=0;s{tt.has(i)&&tt.remove(i)}),t||tt.set(i,n),n}function fp(r,t=!1){return typeof r=="string"?tt.get(r):r instanceof ht?new F({source:r}):pp(r,t)}F.from=fp,ht.from=Wa,$.add($a,La,Na,br,De,de,rs);class Va{constructor(t){this._renderer=t}push(t,e,i){this._renderer.renderPipes.batch.break(i),i.add({renderPipeId:"filter",canBundle:!1,action:"pushFilter",container:e,filterEffect:t})}pop(t,e,i){this._renderer.renderPipes.batch.break(i),i.add({renderPipeId:"filter",action:"popFilter",canBundle:!1})}execute(t){t.action==="pushFilter"?this._renderer.filter.push(t):t.action==="popFilter"&&this._renderer.filter.pop()}destroy(){this._renderer=null}}Va.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"filter"};const Ya=Object.create(null),mp=Object.create(null);function vr(r,t){let e=mp[r];return e===void 0&&(Ya[t]===void 0&&(Ya[t]=1),mp[r]=e=Ya[t]++),e}let oi;function Ka(){return(!oi||oi!=null&&oi.isContextLost())&&(oi=L.get().createCanvas().getContext("webgl",{})),oi}let ps;function gp(){if(!ps){ps="mediump";const r=Ka();r&&r.getShaderPrecisionFormat&&(ps=r.getShaderPrecisionFormat(r.FRAGMENT_SHADER,r.HIGH_FLOAT).precision?"highp":"mediump")}return ps}function _p(r,t,e){return t?r:e?(r=r.replace("out vec4 finalColor;",""),` + + #ifdef GL_ES // This checks if it is WebGL1 + #define in varying + #define finalColor gl_FragColor + #define texture texture2D + #endif + ${r} + `):` + + #ifdef GL_ES // This checks if it is WebGL1 + #define in attribute + #define out varying + #endif + ${r} + `}function bp(r,t,e){const i=e?t.maxSupportedFragmentPrecision:t.maxSupportedVertexPrecision;if(r.substring(0,9)!=="precision"){let s=e?t.requestedFragmentPrecision:t.requestedVertexPrecision;return s==="highp"&&i!=="highp"&&(s="mediump"),`precision ${s} float; +${r}`}else if(i!=="highp"&&r.substring(0,15)==="precision highp")return r.replace("precision highp","precision mediump");return r}function vp(r,t){return t?`#version 300 es +${r}`:r}const c2={},h2={};function yp(r,{name:t="pixi-program"},e=!0){t=t.replace(/\s+/g,"-"),t+=e?"-fragment":"-vertex";const i=e?c2:h2;return i[t]?(i[t]++,t+=`-${i[t]}`):i[t]=1,r.indexOf("#define SHADER_NAME")!==-1?r:`${`#define SHADER_NAME ${t}`} +${r}`}function xp(r,t){return t?r.replace("#version 300 es",""):r}var d2=Object.defineProperty,Tp=Object.getOwnPropertySymbols,p2=Object.prototype.hasOwnProperty,f2=Object.prototype.propertyIsEnumerable,Sp=(r,t,e)=>t in r?d2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,wp=(r,t)=>{for(var e in t||(t={}))p2.call(t,e)&&Sp(r,e,t[e]);if(Tp)for(var e of Tp(t))f2.call(t,e)&&Sp(r,e,t[e]);return r};const qa={stripVersion:xp,ensurePrecision:bp,addProgramDefines:_p,setProgramName:yp,insertVersion:vp},li=Object.create(null),Ep=class ch{constructor(t){t=wp(wp({},ch.defaultOptions),t);const e=t.fragment.indexOf("#version 300 es")!==-1,i={stripVersion:e,ensurePrecision:{requestedFragmentPrecision:t.preferredFragmentPrecision,requestedVertexPrecision:t.preferredVertexPrecision,maxSupportedVertexPrecision:"highp",maxSupportedFragmentPrecision:gp()},setProgramName:{name:t.name},addProgramDefines:e,insertVersion:e};let s=t.fragment,n=t.vertex;Object.keys(qa).forEach(a=>{const o=i[a];s=qa[a](s,o,!0),n=qa[a](n,o,!1)}),this.fragment=s,this.vertex=n,this.transformFeedbackVaryings=t.transformFeedbackVaryings,this._key=vr(`${this.vertex}:${this.fragment}`,"gl-program")}destroy(){this.fragment=null,this.vertex=null,this._attributeData=null,this._uniformData=null,this._uniformBlockData=null,this.transformFeedbackVaryings=null,li[this._cacheKey]=null}static from(t){const e=`${t.vertex}:${t.fragment}`;return li[e]||(li[e]=new ch(t),li[e]._cacheKey=e),li[e]}};Ep.defaultOptions={preferredVertexPrecision:"highp",preferredFragmentPrecision:"mediump"};let Ht=Ep;const Pp={uint8x2:{size:2,stride:2,normalised:!1},uint8x4:{size:4,stride:4,normalised:!1},sint8x2:{size:2,stride:2,normalised:!1},sint8x4:{size:4,stride:4,normalised:!1},unorm8x2:{size:2,stride:2,normalised:!0},unorm8x4:{size:4,stride:4,normalised:!0},snorm8x2:{size:2,stride:2,normalised:!0},snorm8x4:{size:4,stride:4,normalised:!0},uint16x2:{size:2,stride:4,normalised:!1},uint16x4:{size:4,stride:8,normalised:!1},sint16x2:{size:2,stride:4,normalised:!1},sint16x4:{size:4,stride:8,normalised:!1},unorm16x2:{size:2,stride:4,normalised:!0},unorm16x4:{size:4,stride:8,normalised:!0},snorm16x2:{size:2,stride:4,normalised:!0},snorm16x4:{size:4,stride:8,normalised:!0},float16x2:{size:2,stride:4,normalised:!1},float16x4:{size:4,stride:8,normalised:!1},float32:{size:1,stride:4,normalised:!1},float32x2:{size:2,stride:8,normalised:!1},float32x3:{size:3,stride:12,normalised:!1},float32x4:{size:4,stride:16,normalised:!1},uint32:{size:1,stride:4,normalised:!1},uint32x2:{size:2,stride:8,normalised:!1},uint32x3:{size:3,stride:12,normalised:!1},uint32x4:{size:4,stride:16,normalised:!1},sint32:{size:1,stride:4,normalised:!1},sint32x2:{size:2,stride:8,normalised:!1},sint32x3:{size:3,stride:12,normalised:!1},sint32x4:{size:4,stride:16,normalised:!1}};function Me(r){var t;return(t=Pp[r])!=null?t:Pp.float32}const m2={f32:"float32","vec2":"float32x2","vec3":"float32x3","vec4":"float32x4",vec2f:"float32x2",vec3f:"float32x3",vec4f:"float32x4",i32:"sint32","vec2":"sint32x2","vec3":"sint32x3","vec4":"sint32x4",vec2i:"sint32x2",vec3i:"sint32x3",vec4i:"sint32x4",u32:"uint32","vec2":"uint32x2","vec3":"uint32x3","vec4":"uint32x4",vec2u:"uint32x2",vec3u:"uint32x3",vec4u:"uint32x4",bool:"uint32","vec2":"uint32x2","vec3":"uint32x3","vec4":"uint32x4"},Ap=/@location\((\d+)\)\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)(?:,|\s|\)|$)/g;function Cp(r,t){var e;let i;for(;(i=Ap.exec(r))!==null;){const s=(e=m2[i[3]])!=null?e:"float32";t[i[2]]={location:parseInt(i[1],10),format:s,stride:Me(s).stride,offset:0,instance:!1,start:0}}Ap.lastIndex=0}function g2(r){return r.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"")}function Rp({source:r,entryPoint:t}){const e={},i=g2(r),s=i.indexOf(`fn ${t}(`);if(s===-1)return e;const n=i.indexOf("->",s);if(n===-1)return e;const a=i.substring(s,n);if(Cp(a,e),Object.keys(e).length===0){const o=a.match(/\(\s*\w+\s*:\s*(\w+)/);if(o){const l=o[1],u=new RegExp(`struct\\s+${l}\\s*\\{([^}]+)\\}`,"s"),c=i.match(u);c&&Cp(c[1],e)}}return e}function fs(r){var t,e,i;const s=/(^|[^/])@(group|binding)\(\d+\)[^;]+;/g,n=/@group\((\d+)\)/,a=/@binding\((\d+)\)/,o=/var(<[^>]+>)? (\w+)/,l=/:\s*([\w<>]+)/,u=/struct\s+(\w+)\s*{([^}]+)}/g,c=/(\w+)\s*:\s*([\w\<\>]+)/g,h=/struct\s+(\w+)/,p=(t=r.match(s))==null?void 0:t.map(m=>({group:parseInt(m.match(n)[1],10),binding:parseInt(m.match(a)[1],10),name:m.match(o)[2],isUniform:m.match(o)[1]==="",type:m.match(l)[1]}));if(!p)return{groups:[],structs:[]};const f=(i=(e=r.match(u))==null?void 0:e.map(m=>{const g=m.match(h)[1],_=m.match(c).reduce((b,y)=>{const[x,v]=y.split(":");return b[x.trim()]=v.trim(),b},{});return _?{name:g,members:_}:null}).filter(({name:m})=>p.some(g=>g.type===m||g.type.includes(`<${m}>`))))!=null?i:[];return{groups:p,structs:f}}var Ue=(r=>(r[r.VERTEX=1]="VERTEX",r[r.FRAGMENT=2]="FRAGMENT",r[r.COMPUTE=4]="COMPUTE",r))(Ue||{});function Mp({groups:r}){const t=[];for(let e=0;ee.has(a.name)?!1:(e.add(a.name),!0)),n=[...r.groups,...t.groups].filter(a=>{const o=`${a.name}-${a.binding}`;return i.has(o)?!1:(i.add(o),!0)});return{structs:s,groups:n}}const ui=Object.create(null);class kt{constructor(t){this._layoutKey=0,this._attributeLocationsKey=0;var e,i;const{fragment:s,vertex:n,layout:a,gpuLayout:o,name:l}=t;if(this.name=l,this.fragment=s,this.vertex=n,s.source===n.source){const u=fs(s.source);this.structsAndGroups=u}else{const u=fs(n.source),c=fs(s.source);this.structsAndGroups=Gp(u,c)}this.layout=a!=null?a:Op(this.structsAndGroups),this.gpuLayout=o!=null?o:Mp(this.structsAndGroups),this.autoAssignGlobalUniforms=((e=this.layout[0])==null?void 0:e.globalUniforms)!==void 0,this.autoAssignLocalUniforms=((i=this.layout[1])==null?void 0:i.localUniforms)!==void 0,this._generateProgramKey()}_generateProgramKey(){const{vertex:t,fragment:e}=this,i=t.source+e.source+t.entryPoint+e.entryPoint;this._layoutKey=vr(i,"program")}get attributeData(){var t;return(t=this._attributeData)!=null||(this._attributeData=Rp(this.vertex)),this._attributeData}destroy(){this.gpuLayout=null,this.layout=null,this.structsAndGroups=null,this.fragment=null,this.vertex=null,ui[this._cacheKey]=null}static from(t){const e=`${t.vertex.source}:${t.fragment.source}:${t.fragment.entryPoint}:${t.vertex.entryPoint}`;return ui[e]||(ui[e]=new kt(t),ui[e]._cacheKey=e),ui[e]}}class Te{constructor(t){this.resources=Object.create(null),this._dirty=!0;let e=0;for(const i in t){const s=t[i];this.setResource(s,e++)}this._updateKey()}_updateKey(){if(!this._dirty)return;this._dirty=!1;const t=[];let e=0;for(const i in this.resources)t[e++]=this.resources[i]._resourceId;this._key=t.join("|")}setResource(t,e){var i,s;const n=this.resources[e];t!==n&&(n&&((i=t.off)==null||i.call(t,"change",this.onResourceChange,this)),(s=t.on)==null||s.call(t,"change",this.onResourceChange,this),this.resources[e]=t,this._dirty=!0)}getResource(t){return this.resources[t]}_touch(t,e){const i=this.resources;for(const s in i)i[s]._gcLastUsed=t,i[s]._touched=e}destroy(){var t;const e=this.resources;for(const i in e){const s=e[i];(t=s==null?void 0:s.off)==null||t.call(s,"change",this.onResourceChange,this)}this.resources=null}onResourceChange(t){if(this._dirty=!0,t.destroyed){const e=this.resources;for(const i in e)e[i]===t&&(e[i]=null)}else this._updateKey()}}var Gt=(r=>(r[r.WEBGL=1]="WEBGL",r[r.WEBGPU=2]="WEBGPU",r[r.CANVAS=4]="CANVAS",r[r.BOTH=3]="BOTH",r))(Gt||{});const Za=["f32","i32","vec2","vec3","vec4","mat2x2","mat3x3","mat4x4","mat3x2","mat4x2","mat2x3","mat4x3","mat2x4","mat3x4","vec2","vec3","vec4"],Ip=Za.reduce((r,t)=>(r[t]=!0,r),{});function Bp(r,t){switch(r){case"f32":return 0;case"vec2":return new Float32Array(2*t);case"vec3":return new Float32Array(3*t);case"vec4":return new Float32Array(4*t);case"mat2x2":return new Float32Array([1,0,0,1]);case"mat3x3":return new Float32Array([1,0,0,0,1,0,0,0,1]);case"mat4x4":return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])}return null}var _2=Object.defineProperty,Fp=Object.getOwnPropertySymbols,b2=Object.prototype.hasOwnProperty,v2=Object.prototype.propertyIsEnumerable,Dp=(r,t,e)=>t in r?_2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Up=(r,t)=>{for(var e in t||(t={}))b2.call(t,e)&&Dp(r,e,t[e]);if(Fp)for(var e of Fp(t))v2.call(t,e)&&Dp(r,e,t[e]);return r};const kp=class QT{constructor(t,e){this._touched=0,this.uid=nt("uniform"),this._resourceType="uniformGroup",this._resourceId=nt("resource"),this.isUniformGroup=!0,this._dirtyId=0,this.destroyed=!1;var i,s;e=Up(Up({},QT.defaultOptions),e),this.uniformStructures=t;const n={};for(const a in t){const o=t[a];if(o.name=a,o.size=(i=o.size)!=null?i:1,!Ip[o.type]){const l=o.type.match(/^array<(\w+(?:<\w+>)?),\s*(\d+)>$/);if(l){const[,u,c]=l;throw new Error(`Uniform type ${o.type} is not supported. Use type: '${u}', size: ${c} instead.`)}throw new Error(`Uniform type ${o.type} is not supported. Supported uniform types are: ${Za.join(", ")}`)}(s=o.value)!=null||(o.value=Bp(o.type,o.size)),n[a]=o.value}this.uniforms=n,this._dirtyId=1,this.ubo=e.ubo,this.isStatic=e.isStatic,this._signature=vr(Object.keys(n).map(a=>`${a}-${t[a].type}`).join("-"),"uniform-group")}update(){this._dirtyId++}};kp.defaultOptions={ubo:!1,isStatic:!1};let wt=kp;var y2=Object.defineProperty,ms=Object.getOwnPropertySymbols,$p=Object.prototype.hasOwnProperty,Lp=Object.prototype.propertyIsEnumerable,Np=(r,t,e)=>t in r?y2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,x2=(r,t)=>{for(var e in t||(t={}))$p.call(t,e)&&Np(r,e,t[e]);if(ms)for(var e of ms(t))Lp.call(t,e)&&Np(r,e,t[e]);return r},T2=(r,t)=>{var e={};for(var i in r)$p.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&ms)for(var i of ms(r))t.indexOf(i)<0&&Lp.call(r,i)&&(e[i]=r[i]);return e};class Zt extends Ut{constructor(t){super(),this.uid=nt("shader"),this._uniformBindMap=Object.create(null),this._ownedBindGroups=[],this._destroyed=!1;let{gpuProgram:e,glProgram:i,groups:s,resources:n,compatibleRenderers:a,groupMap:o}=t;this.gpuProgram=e,this.glProgram=i,a===void 0&&(a=0,e&&(a|=Gt.WEBGPU),i&&(a|=Gt.WEBGL)),this.compatibleRenderers=a;const l={};if(!n&&!s&&(n={}),n&&s)throw new Error("[Shader] Cannot have both resources and groups");if(!e&&s&&!o)throw new Error("[Shader] No group map or WebGPU shader provided - consider using resources instead.");if(!e&&s&&o)for(const u in o)for(const c in o[u]){const h=o[u][c];l[h]={group:u,binding:c,name:h}}else if(e&&s&&!o){const u=e.structsAndGroups.groups;o={},u.forEach(c=>{o[c.group]=o[c.group]||{},o[c.group][c.binding]=c.name,l[c.name]=c})}else if(n){s={},o={},e&&e.structsAndGroups.groups.forEach(c=>{o[c.group]=o[c.group]||{},o[c.group][c.binding]=c.name,l[c.name]=c});let u=0;for(const c in n)l[c]||(s[99]||(s[99]=new Te,this._ownedBindGroups.push(s[99])),l[c]={group:99,binding:u,name:c},o[99]=o[99]||{},o[99][u]=c,u++);for(const c in n){const h=c;let p=n[c];!p.source&&!p._resourceType&&(p=new wt(p));const f=l[h];f&&(s[f.group]||(s[f.group]=new Te,this._ownedBindGroups.push(s[f.group])),s[f.group].setResource(p,f.binding))}}this.groups=s,this._uniformBindMap=o,this.resources=this._buildResourceAccessor(s,l)}addResource(t,e,i){var s,n;(s=this._uniformBindMap)[e]||(s[e]={}),(n=this._uniformBindMap[e])[i]||(n[i]=t),this.groups[e]||(this.groups[e]=new Te,this._ownedBindGroups.push(this.groups[e]))}_buildResourceAccessor(t,e){const i={};for(const s in e){const n=e[s];Object.defineProperty(i,n.name,{get(){return t[n.group].getResource(n.binding)},set(a){t[n.group].setResource(a,n.binding)}})}return i}destroy(t=!1){var e,i;this._destroyed||(this._destroyed=!0,this.emit("destroy",this),t&&((e=this.gpuProgram)==null||e.destroy(),(i=this.glProgram)==null||i.destroy()),this.gpuProgram=null,this.glProgram=null,this.removeAllListeners(),this._uniformBindMap=null,this._ownedBindGroups.forEach(s=>{s.destroy()}),this._ownedBindGroups=null,this.resources=null,this.groups=null)}static from(t){const e=t,{gpu:i,gl:s}=e,n=T2(e,["gpu","gl"]);let a,o;return i&&(a=kt.from(i)),s&&(o=Ht.from(s)),new Zt(x2({gpuProgram:a,glProgram:o},n))}}const S2={normal:0,add:1,multiply:2,screen:3,overlay:4,erase:5,"normal-npm":6,"add-npm":7,"screen-npm":8,min:9,max:10},Qa=0,Ja=1,to=2,eo=3,ro=4,io=5,so=class JT{constructor(){this.data=0,this.blendMode="normal",this.polygonOffset=0,this.blend=!0,this.depthMask=!0}get blend(){return!!(this.data&1<t in r?w2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,no=(r,t)=>{for(var e in t||(t={}))Xp.call(t,e)&&jp(r,e,t[e]);if(gs)for(var e of gs(t))Hp.call(t,e)&&jp(r,e,t[e]);return r},E2=(r,t)=>{var e={};for(var i in r)Xp.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&gs)for(var i of gs(r))t.indexOf(i)<0&&Hp.call(r,i)&&(e[i]=r[i]);return e};const zp=class hh extends Zt{constructor(t){t=no(no({},hh.defaultOptions),t),super(t),this.enabled=!0,this._state=jt.for2d(),this.blendMode=t.blendMode,this.padding=t.padding,typeof t.antialias=="boolean"?this.antialias=t.antialias?"on":"off":this.antialias=t.antialias,this.resolution=t.resolution,this.blendRequired=t.blendRequired,this.clipToViewport=t.clipToViewport,this.addResource("uTexture",0,1),t.blendRequired&&this.addResource("uBackTexture",0,3)}apply(t,e,i,s){t.applyFilter(this,e,i,s)}get blendMode(){return this._state.blendMode}set blendMode(t){this._state.blendMode=t}static from(t){const e=t,{gpu:i,gl:s}=e,n=E2(e,["gpu","gl"]);let a,o;return i&&(a=kt.from(i)),s&&(o=Ht.from(s)),new hh(no({gpuProgram:a,glProgram:o},n))}};zp.defaultOptions={blendMode:"normal",resolution:1,padding:0,antialias:"off",blendRequired:!1,clipToViewport:!0};let Se=zp;var ci=`in vec2 aPosition; +out vec2 vTextureCoord; + +uniform vec4 uInputSize; +uniform vec4 uOutputFrame; +uniform vec4 uOutputTexture; + +vec4 filterVertexPosition( void ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +vec2 filterTextureCoord( void ) +{ + return aPosition * (uOutputFrame.zw * uInputSize.zw); +} + +void main(void) +{ + gl_Position = filterVertexPosition(); + vTextureCoord = filterTextureCoord(); +} +`,Wp=`in vec2 vTextureCoord; +out vec4 finalColor; +uniform sampler2D uTexture; +void main() { + finalColor = texture(uTexture, vTextureCoord); +} +`,ao=`struct GlobalFilterUniforms { + uInputSize: vec4, + uInputPixel: vec4, + uInputClamp: vec4, + uOutputFrame: vec4, + uGlobalFrame: vec4, + uOutputTexture: vec4, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler: sampler; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv: vec2 +}; + +fn filterVertexPosition(aPosition: vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord(aPosition: vec2) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +@vertex +fn mainVertex( + @location(0) aPosition: vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition) + ); +} + +@fragment +fn mainFragment( + @location(0) uv: vec2, +) -> @location(0) vec4 { + return textureSample(uTexture, uSampler, uv); +} +`;class Vp extends Se{constructor(){const t=kt.from({vertex:{source:ao,entryPoint:"mainVertex"},fragment:{source:ao,entryPoint:"mainFragment"},name:"passthrough-filter"}),e=Ht.from({vertex:ci,fragment:Wp,name:"passthrough-filter"});super({gpuProgram:t,glProgram:e})}}var et=(r=>(r[r.MAP_READ=1]="MAP_READ",r[r.MAP_WRITE=2]="MAP_WRITE",r[r.COPY_SRC=4]="COPY_SRC",r[r.COPY_DST=8]="COPY_DST",r[r.INDEX=16]="INDEX",r[r.VERTEX=32]="VERTEX",r[r.UNIFORM=64]="UNIFORM",r[r.STORAGE=128]="STORAGE",r[r.INDIRECT=256]="INDIRECT",r[r.QUERY_RESOLVE=512]="QUERY_RESOLVE",r[r.STATIC=1024]="STATIC",r))(et||{});class zt extends Ut{constructor(t){let{data:e,size:i}=t;const{usage:s,label:n,shrinkToFit:a}=t;super(),this._gpuData=Object.create(null),this._gcLastUsed=-1,this.autoGarbageCollect=!0,this.uid=nt("buffer"),this._resourceType="buffer",this._resourceId=nt("resource"),this._touched=0,this._updateID=1,this._dataInt32=null,this.shrinkToFit=!0,this.destroyed=!1,e instanceof Array&&(e=new Float32Array(e)),this._data=e,i!=null||(i=e==null?void 0:e.byteLength);const o=!!e;this.descriptor={size:i,usage:s,mappedAtCreation:o,label:n},this.shrinkToFit=a!=null?a:!0}get data(){return this._data}set data(t){this.setDataWithSize(t,t.length,!0)}get dataInt32(){return this._dataInt32||(this._dataInt32=new Int32Array(this.data.buffer)),this._dataInt32}get static(){return!!(this.descriptor.usage&et.STATIC)}set static(t){t?this.descriptor.usage|=et.STATIC:this.descriptor.usage&=~et.STATIC}setDataWithSize(t,e,i){if(this._updateID++,this._updateSize=e*t.BYTES_PER_ELEMENT,this._data===t){i&&this.emit("update",this);return}const s=this._data;if(this._data=t,this._dataInt32=null,!s||s.length!==t.length){!this.shrinkToFit&&s&&t.byteLengtho&&(o=f),m>l&&(l=m),fi.destroy()),this.unload(),(e=this.indexBuffer)==null||e.destroy(),this.attributes=null,this.buffers=null,this.indexBuffer=null,this._bounds=null}}function hi(r,t,e,i,s,n){const a=r-e,o=t-i,l=s-e,u=n-i,c=a*l+o*u,h=l*l+u*u;let p=-1;h!==0&&(p=c/h);let f,m;p<0?(f=e,m=i):p>1?(f=s,m=n):(f=e+p*l,m=i+p*u);const g=r-f,_=t-m;return g*g+_*_}function lo(r,t,e,i,s,n,a,o){const l=a-e,u=o-i,c=s-e,h=n-i,p=r-e,f=t-i,m=l*l+u*u,g=l*c+u*h,_=l*p+u*f,b=c*c+h*h,y=c*p+h*f,x=1/(m*b-g*g),v=(b*_-g*y)*x,w=(m*y-g*_)*x;return v>=0&&w>=0&&v+w<1}class _s{constructor(t=0,e=0,i=0){this.type="circle",this.x=t,this.y=e,this.radius=i}clone(){return new _s(this.x,this.y,this.radius)}contains(t,e){if(this.radius<=0)return!1;const i=this.radius*this.radius;let s=this.x-t,n=this.y-e;return s*=s,n*=n,s+n<=i}strokeContains(t,e,i,s=.5){if(this.radius===0)return!1;const n=this.x-t,a=this.y-e,o=this.radius,l=(1-s)*i,u=Math.sqrt(n*n+a*a);return u<=o+l&&u>o-(i-l)}getBounds(t){return t||(t=new it),t.x=this.x-this.radius,t.y=this.y-this.radius,t.width=this.radius*2,t.height=this.radius*2,t}copyFrom(t){return this.x=t.x,this.y=t.y,this.radius=t.radius,this}copyTo(t){return t.copyFrom(this),t}}class bs{constructor(t=0,e=0,i=0,s=0){this.type="ellipse",this.x=t,this.y=e,this.halfWidth=i,this.halfHeight=s}clone(){return new bs(this.x,this.y,this.halfWidth,this.halfHeight)}contains(t,e){if(this.halfWidth<=0||this.halfHeight<=0)return!1;let i=(t-this.x)/this.halfWidth,s=(e-this.y)/this.halfHeight;return i*=i,s*=s,i+s<=1}strokeContains(t,e,i,s=.5){const{halfWidth:n,halfHeight:a}=this;if(n<=0||a<=0)return!1;const o=i*(1-s),l=i-o,u=n-l,c=a-l,h=n+o,p=a+o,f=t-this.x,m=e-this.y,g=f*f/(u*u)+m*m/(c*c),_=f*f/(h*h)+m*m/(p*p);return g>1&&_<=1}getBounds(t){return t||(t=new it),t.x=this.x-this.halfWidth,t.y=this.y-this.halfHeight,t.width=this.halfWidth*2,t.height=this.halfHeight*2,t}copyFrom(t){return this.x=t.x,this.y=t.y,this.halfWidth=t.halfWidth,this.halfHeight=t.halfHeight,this}copyTo(t){return t.copyFrom(this),t}}let A2,C2;class yr{constructor(...t){this.type="polygon";let e=Array.isArray(t[0])?t[0]:t;if(typeof e[0]!="number"){const i=[];for(let s=0,n=e.length;se!=c>e&&t<(u-o)*((e-l)/(c-l))+o&&(i=!i)}return i}strokeContains(t,e,i,s=.5){const n=i*i,a=n*(1-s),o=n-a,{points:l}=this,u=l.length-(this.closePath?0:2);for(let c=0;cs?u:s,n=ca?c:a}return t.x=i,t.width=s-i,t.y=n,t.height=a-n,t}copyFrom(t){return this.points=t.points.slice(),this.closePath=t.closePath,this}copyTo(t){return t.copyFrom(this),t}get lastX(){return this.points[this.points.length-2]}get lastY(){return this.points[this.points.length-1]}get x(){return this.points[this.points.length-2]}get y(){return this.points[this.points.length-1]}get startX(){return this.points[0]}get startY(){return this.points[1]}}const vs=(r,t,e,i,s,n,a)=>{const o=r-e,l=t-i,u=Math.sqrt(o*o+l*l);return u>=s-n&&u<=s+a};class ys{constructor(t=0,e=0,i=0,s=0,n=20){this.type="roundedRectangle",this.x=t,this.y=e,this.width=i,this.height=s,this.radius=n}getBounds(t){return t||(t=new it),t.x=this.x,t.y=this.y,t.width=this.width,t.height=this.height,t}clone(){return new ys(this.x,this.y,this.width,this.height,this.radius)}copyFrom(t){return this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height,this}copyTo(t){return t.copyFrom(this),t}contains(t,e){if(this.width<=0||this.height<=0)return!1;if(t>=this.x&&t<=this.x+this.width&&e>=this.y&&e<=this.y+this.height){const i=Math.max(0,Math.min(this.radius,Math.min(this.width,this.height)/2));if(e>=this.y+i&&e<=this.y+this.height-i||t>=this.x+i&&t<=this.x+this.width-i)return!0;let s=t-(this.x+i),n=e-(this.y+i);const a=i*i;if(s*s+n*n<=a||(s=t-(this.x+this.width-i),s*s+n*n<=a)||(n=e-(this.y+this.height-i),s*s+n*n<=a)||(s=t-(this.x+i),s*s+n*n<=a))return!0}return!1}strokeContains(t,e,i,s=.5){const{x:n,y:a,width:o,height:l,radius:u}=this,c=i*(1-s),h=i-c,p=n+u,f=a+u,m=o-u*2,g=l-u*2,_=n+o,b=a+l;return(t>=n-c&&t<=n+h||t>=_-h&&t<=_+c)&&e>=f&&e<=f+g||(e>=a-c&&e<=a+h||e>=b-h&&e<=b+c)&&t>=p&&t<=p+m?!0:t_-u&&e_-u&&e>b-u&&vs(t,e,_-u,b-u,u,h,c)||tb-u&&vs(t,e,p,b-u,u,h,c)}}class uo{constructor(t=0,e=0,i=0,s=0,n=0,a=0){this.type="triangle",this.x=t,this.y=e,this.x2=i,this.y2=s,this.x3=n,this.y3=a}contains(t,e){const i=(this.x-this.x3)*(e-this.y3)-(this.y-this.y3)*(t-this.x3),s=(this.x2-this.x)*(e-this.y)-(this.y2-this.y)*(t-this.x);if(i<0!=s<0&&i!==0&&s!==0)return!1;const n=(this.x3-this.x2)*(e-this.y2)-(this.y3-this.y2)*(t-this.x2);return n===0||n<0==i+s<=0}strokeContains(t,e,i,s=.5){const n=i/2,a=n*n,{x:o,x2:l,x3:u,y:c,y2:h,y3:p}=this;return hi(t,e,o,c,l,p)<=a||hi(t,e,l,h,u,p)<=a||hi(t,e,u,p,o,c)<=a}clone(){return new uo(this.x,this.y,this.x2,this.y2,this.x3,this.y3)}copyFrom(t){return this.x=t.x,this.y=t.y,this.x2=t.x2,this.y2=t.y2,this.x3=t.x3,this.y3=t.y3,this}copyTo(t){return t.copyFrom(this),t}getBounds(t){t||(t=new it);const e=Math.min(this.x,this.x2,this.x3),i=Math.max(this.x,this.x2,this.x3),s=Math.min(this.y,this.y2,this.y3),n=Math.max(this.y,this.y2,this.y3);return t.x=e,t.y=s,t.width=i-e,t.height=n-s,t}}const Kp=new D;function co(r,t){var e;t.clear();const i=t.matrix;for(let s=0;s"},uInputPixel:{value:new Float32Array(4),type:"vec4"},uInputClamp:{value:new Float32Array(4),type:"vec4"},uOutputFrame:{value:new Float32Array(4),type:"vec4"},uGlobalFrame:{value:new Float32Array(4),type:"vec4"},uOutputTexture:{value:new Float32Array(4),type:"vec4"}}),this._globalFilterBindGroup=new Te({}),this.renderer=t}get activeBackTexture(){var t;return(t=this._activeFilterData)==null?void 0:t.backTexture}push(t){const e=this.renderer,i=t.filterEffect.filters,s=this._pushFilterData();s.skip=!1,s.filters=i,s.container=t.container,s.outputRenderSurface=e.renderTarget.renderSurface;const n=e.renderTarget.renderTarget.colorTexture.source,a=n.resolution,o=n.antialias;if(i.every(f=>!f.enabled)){s.skip=!0;return}const l=s.bounds;if(this._calculateFilterArea(t,l),this._calculateFilterBounds(s,e.renderTarget.rootViewPort,o,a,1),s.skip)return;const u=this._getPreviousFilterData(),c=this._findFilterResolution(a);let h=0,p=0;u&&(h=u.bounds.minX,p=u.bounds.minY),this._calculateGlobalFrame(s,h,p,c,n.width,n.height),this._setupFilterTextures(s,l,e,u)}generateFilteredTexture({texture:t,filters:e}){const i=this._pushFilterData();this._activeFilterData=i,i.skip=!1,i.filters=e;const s=t.source,n=s.resolution,a=s.antialias;if(e.every(c=>!c.enabled))return i.skip=!0,t;const o=i.bounds;if(o.addRect(t.frame),this._calculateFilterBounds(i,o.rectangle,a,n,0),i.skip)return t;const l=n;this._calculateGlobalFrame(i,0,0,l,s.width,s.height),i.outputRenderSurface=yt.getOptimalTexture(o.width,o.height,i.resolution,i.antialias),i.backTexture=F.EMPTY,i.inputTexture=t,this.renderer.renderTarget.finishRenderPass(),this._applyFiltersToTexture(i,!0);const u=i.outputRenderSurface;return u.source.alphaMode="premultiplied-alpha",u}pop(){const t=this.renderer,e=this._popFilterData();e.skip||(t.globalUniforms.pop(),t.renderTarget.finishRenderPass(),this._activeFilterData=e,this._applyFiltersToTexture(e,!1),e.blendRequired&&yt.returnTexture(e.backTexture),yt.returnTexture(e.inputTexture))}getBackTexture(t,e,i){const s=t.colorTexture.source._resolution,n=yt.getOptimalTexture(e.width,e.height,s,!1);let a=e.minX,o=e.minY;i&&(a-=i.minX,o-=i.minY),a=Math.floor(a*s),o=Math.floor(o*s);const l=Math.ceil(e.width*s),u=Math.ceil(e.height*s);return this.renderer.renderTarget.copyToTexture(t,n,{x:a,y:o},{width:l,height:u},{x:0,y:0}),n}applyFilter(t,e,i,s){const n=this.renderer,a=this._activeFilterData,o=a.outputRenderSurface===i,l=n.renderTarget.rootRenderTarget.colorTexture.source._resolution,u=this._findFilterResolution(l);let c=0,h=0;if(o){const f=this._findPreviousFilterOffset();c=f.x,h=f.y}this._updateFilterUniforms(e,i,a,c,h,u,o,s);const p=t.enabled?t:this._getPassthroughFilter();this._setupBindGroupsAndRender(p,e,n)}calculateSpriteMatrix(t,e){const i=this._activeFilterData,s=t.set(i.inputTexture._source.width,0,0,i.inputTexture._source.height,i.bounds.minX,i.bounds.minY),n=e.worldTransform.copyTo(D.shared),a=e.renderGroup||e.parentRenderGroup;return a&&a.cacheToLocalTransform&&n.prepend(a.cacheToLocalTransform),n.invert(),s.prepend(n),s.scale(1/e.texture.orig.width,1/e.texture.orig.height),s.translate(e.anchor.x,e.anchor.y),s}destroy(){var t;(t=this._passthroughFilter)==null||t.destroy(!0),this._passthroughFilter=null}_getPassthroughFilter(){var t;return(t=this._passthroughFilter)!=null||(this._passthroughFilter=new Vp),this._passthroughFilter}_setupBindGroupsAndRender(t,e,i){if(i.renderPipes.uniformBatch){const s=i.renderPipes.uniformBatch.getUboResource(this._filterGlobalUniforms);this._globalFilterBindGroup.setResource(s,0)}else this._globalFilterBindGroup.setResource(this._filterGlobalUniforms,0);this._globalFilterBindGroup.setResource(e.source,1),this._globalFilterBindGroup.setResource(e.source.style,2),t.groups[0]=this._globalFilterBindGroup,i.encoder.draw({geometry:R2,shader:t,state:t._state,topology:"triangle-list"}),i.type===Gt.WEBGL&&i.renderTarget.finishRenderPass()}_setupFilterTextures(t,e,i,s){if(t.backTexture=F.EMPTY,t.inputTexture=yt.getOptimalTexture(e.width,e.height,t.resolution,t.antialias),t.blendRequired){i.renderTarget.finishRenderPass();const n=i.renderTarget.getRenderTarget(t.outputRenderSurface);t.backTexture=this.getBackTexture(n,e,s==null?void 0:s.bounds)}i.renderTarget.bind(t.inputTexture,!0),i.globalUniforms.push({offset:e})}_calculateGlobalFrame(t,e,i,s,n,a){const o=t.globalFrame;o.x=e*s,o.y=i*s,o.width=n*s,o.height=a*s}_updateFilterUniforms(t,e,i,s,n,a,o,l){const u=this._filterGlobalUniforms.uniforms,c=u.uOutputFrame,h=u.uInputSize,p=u.uInputPixel,f=u.uInputClamp,m=u.uGlobalFrame,g=u.uOutputTexture;o?(c[0]=i.bounds.minX-s,c[1]=i.bounds.minY-n):(c[0]=0,c[1]=0),c[2]=t.frame.width,c[3]=t.frame.height,h[0]=t.source.width,h[1]=t.source.height,h[2]=1/h[0],h[3]=1/h[1],p[0]=t.source.pixelWidth,p[1]=t.source.pixelHeight,p[2]=1/p[0],p[3]=1/p[1],f[0]=.5*p[2],f[1]=.5*p[3],f[2]=t.frame.width*h[2]-.5*p[2],f[3]=t.frame.height*h[3]-.5*p[3];const _=this.renderer.renderTarget.rootRenderTarget.colorTexture;m[0]=s*a,m[1]=n*a,m[2]=_.source.width*a,m[3]=_.source.height*a,e instanceof F&&(e.source.resource=null);const b=this.renderer.renderTarget.getRenderTarget(e);this.renderer.renderTarget.bind(e,!!l),e instanceof F?(g[0]=e.frame.width,g[1]=e.frame.height):(g[0]=b.width,g[1]=b.height),g[2]=b.isRoot?-1:1,this._filterGlobalUniforms.update()}_findFilterResolution(t){let e=this._filterStackIndex-1;for(;e>0&&this._filterStack[e].skip;)--e;return e>0&&this._filterStack[e].inputTexture?this._filterStack[e].inputTexture.source._resolution:t}_findPreviousFilterOffset(){let t=0,e=0,i=this._filterStackIndex;for(;i>0;){i--;const s=this._filterStack[i];if(!s.skip){t=s.bounds.minX,e=s.bounds.minY;break}}return{x:t,y:e}}_calculateFilterArea(t,e){if(t.renderables?co(t.renderables,e):t.filterEffect.filterArea?(e.clear(),e.addRect(t.filterEffect.filterArea),e.applyMatrix(t.container.worldTransform)):t.container.getFastGlobalBounds(!0,e),t.container){const i=(t.container.renderGroup||t.container.parentRenderGroup).cacheToLocalTransform;i&&e.applyMatrix(i)}}_applyFiltersToTexture(t,e){const i=t.inputTexture,s=t.bounds,n=t.filters,a=t.firstEnabledIndex,o=t.lastEnabledIndex;if(this._globalFilterBindGroup.setResource(i.source.style,2),this._globalFilterBindGroup.setResource(t.backTexture.source,3),a===o)n[a].apply(this,i,t.outputRenderSurface,e);else{let l=t.inputTexture;const u=yt.getOptimalTexture(s.width,s.height,l.source._resolution,!1);let c=u;for(let h=a;h0&&(e--,t=this._filterStack[e],!!t.skip););return t}_pushFilterData(){let t=this._filterStack[this._filterStackIndex];return t||(t=this._filterStack[this._filterStackIndex]=new M2),this._filterStackIndex++,t}}ho.extension={type:[T.WebGLSystem,T.WebGPUSystem],name:"filter"},$.add(ho),$.add(Va);var O2={__proto__:null};const po=[];$.handleByNamedList(T.Environment,po);async function fo(r){if(!r)for(let t=0;t80*e){o=r[0],l=r[1];let c=o,h=l;for(let p=e;pc&&(c=f),m>h&&(h=m)}u=Math.max(c-o,h-l),u=u!==0?32767/u:0}return pi(n,a,e,o,l,u,0),a}function Zp(r,t,e,i,s){let n;if(s===bo(r,t,e,i)>0)for(let a=t;a=t;a-=i)n=ef(a/i|0,r[a],r[a+1],n);return n&&xr(n,n.next)&&(gi(n),n=n.next),n}function Je(r,t){if(!r)return r;t||(t=r);let e=r,i;do if(i=!1,!e.steiner&&(xr(e,e.next)||bt(e.prev,e,e.next)===0)){if(gi(e),e=t=e.prev,e===e.next)break;i=!0}else e=e.next;while(i||e!==t);return t}function pi(r,t,e,i,s,n,a){if(!r)return;!a&&n&&X2(r,i,s,n);let o=r;for(;r.prev!==r.next;){const l=r.prev,u=r.next;if(n?B2(r,i,s,n):I2(r)){t.push(l.i,r.i,u.i),gi(r),r=u.next,o=u.next;continue}if(r=u,r===o){a?a===1?(r=F2(Je(r),t),pi(r,t,e,i,s,n,2)):a===2&&D2(r,t,e,i,s,n):pi(Je(r),t,e,i,s,n,1);break}}}function I2(r){const t=r.prev,e=r,i=r.next;if(bt(t,e,i)>=0)return!1;const s=t.x,n=e.x,a=i.x,o=t.y,l=e.y,u=i.y,c=Math.min(s,n,a),h=Math.min(o,l,u),p=Math.max(s,n,a),f=Math.max(o,l,u);let m=i.next;for(;m!==t;){if(m.x>=c&&m.x<=p&&m.y>=h&&m.y<=f&&fi(s,o,n,l,a,u,m.x,m.y)&&bt(m.prev,m,m.next)>=0)return!1;m=m.next}return!0}function B2(r,t,e,i){const s=r.prev,n=r,a=r.next;if(bt(s,n,a)>=0)return!1;const o=s.x,l=n.x,u=a.x,c=s.y,h=n.y,p=a.y,f=Math.min(o,l,u),m=Math.min(c,h,p),g=Math.max(o,l,u),_=Math.max(c,h,p),b=go(f,m,t,e,i),y=go(g,_,t,e,i);let x=r.prevZ,v=r.nextZ;for(;x&&x.z>=b&&v&&v.z<=y;){if(x.x>=f&&x.x<=g&&x.y>=m&&x.y<=_&&x!==s&&x!==a&&fi(o,c,l,h,u,p,x.x,x.y)&&bt(x.prev,x,x.next)>=0||(x=x.prevZ,v.x>=f&&v.x<=g&&v.y>=m&&v.y<=_&&v!==s&&v!==a&&fi(o,c,l,h,u,p,v.x,v.y)&&bt(v.prev,v,v.next)>=0))return!1;v=v.nextZ}for(;x&&x.z>=b;){if(x.x>=f&&x.x<=g&&x.y>=m&&x.y<=_&&x!==s&&x!==a&&fi(o,c,l,h,u,p,x.x,x.y)&&bt(x.prev,x,x.next)>=0)return!1;x=x.prevZ}for(;v&&v.z<=y;){if(v.x>=f&&v.x<=g&&v.y>=m&&v.y<=_&&v!==s&&v!==a&&fi(o,c,l,h,u,p,v.x,v.y)&&bt(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function F2(r,t){let e=r;do{const i=e.prev,s=e.next.next;!xr(i,s)&&Jp(i,e,e.next,s)&&mi(i,s)&&mi(s,i)&&(t.push(i.i,e.i,s.i),gi(e),gi(e.next),e=r=s),e=e.next}while(e!==r);return Je(e)}function D2(r,t,e,i,s,n){let a=r;do{let o=a.next.next;for(;o!==a.prev;){if(a.i!==o.i&&z2(a,o)){let l=tf(a,o);a=Je(a,a.next),l=Je(l,l.next),pi(a,t,e,i,s,n,0),pi(l,t,e,i,s,n,0);return}o=o.next}a=a.next}while(a!==r)}function U2(r,t,e,i){const s=[];for(let n=0,a=t.length;n=e.next.y&&e.next.y!==e.y){const h=e.x+(s-e.y)*(e.next.x-e.x)/(e.next.y-e.y);if(h<=i&&h>n&&(n=h,a=e.x=e.x&&e.x>=l&&i!==e.x&&Qp(sa.x||e.x===a.x&&N2(a,e)))&&(a=e,c=h)}e=e.next}while(e!==o);return a}function N2(r,t){return bt(r.prev,r,t.prev)<0&&bt(t.next,r,r.next)<0}function X2(r,t,e,i){let s=r;do s.z===0&&(s.z=go(s.x,s.y,t,e,i)),s.prevZ=s.prev,s.nextZ=s.next,s=s.next;while(s!==r);s.prevZ.nextZ=null,s.prevZ=null,H2(s)}function H2(r){let t,e=1;do{let i=r,s;r=null;let n=null;for(t=0;i;){t++;let a=i,o=0;for(let u=0;u0||l>0&&a;)o!==0&&(l===0||!a||i.z<=a.z)?(s=i,i=i.nextZ,o--):(s=a,a=a.nextZ,l--),n?n.nextZ=s:r=s,s.prevZ=n,n=s;i=a}n.nextZ=null,e*=2}while(t>1);return r}function go(r,t,e,i,s){return r=(r-e)*s|0,t=(t-i)*s|0,r=(r|r<<8)&16711935,r=(r|r<<4)&252645135,r=(r|r<<2)&858993459,r=(r|r<<1)&1431655765,t=(t|t<<8)&16711935,t=(t|t<<4)&252645135,t=(t|t<<2)&858993459,t=(t|t<<1)&1431655765,r|t<<1}function j2(r){let t=r,e=r;do(t.x=(r-a)*(n-o)&&(r-a)*(i-o)>=(e-a)*(t-o)&&(e-a)*(n-o)>=(s-a)*(i-o)}function fi(r,t,e,i,s,n,a,o){return!(r===a&&t===o)&&Qp(r,t,e,i,s,n,a,o)}function z2(r,t){return r.next.i!==t.i&&r.prev.i!==t.i&&!W2(r,t)&&(mi(r,t)&&mi(t,r)&&V2(r,t)&&(bt(r.prev,r,t.prev)||bt(r,t.prev,t))||xr(r,t)&&bt(r.prev,r,r.next)>0&&bt(t.prev,t,t.next)>0)}function bt(r,t,e){return(t.y-r.y)*(e.x-t.x)-(t.x-r.x)*(e.y-t.y)}function xr(r,t){return r.x===t.x&&r.y===t.y}function Jp(r,t,e,i){const s=Ts(bt(r,t,e)),n=Ts(bt(r,t,i)),a=Ts(bt(e,i,r)),o=Ts(bt(e,i,t));return!!(s!==n&&a!==o||s===0&&xs(r,e,t)||n===0&&xs(r,i,t)||a===0&&xs(e,r,i)||o===0&&xs(e,t,i))}function xs(r,t,e){return t.x<=Math.max(r.x,e.x)&&t.x>=Math.min(r.x,e.x)&&t.y<=Math.max(r.y,e.y)&&t.y>=Math.min(r.y,e.y)}function Ts(r){return r>0?1:r<0?-1:0}function W2(r,t){let e=r;do{if(e.i!==r.i&&e.next.i!==r.i&&e.i!==t.i&&e.next.i!==t.i&&Jp(e,e.next,r,t))return!0;e=e.next}while(e!==r);return!1}function mi(r,t){return bt(r.prev,r,r.next)<0?bt(r,t,r.next)>=0&&bt(r,r.prev,t)>=0:bt(r,t,r.prev)<0||bt(r,r.next,t)<0}function V2(r,t){let e=r,i=!1;const s=(r.x+t.x)/2,n=(r.y+t.y)/2;do e.y>n!=e.next.y>n&&e.next.y!==e.y&&s<(e.next.x-e.x)*(n-e.y)/(e.next.y-e.y)+e.x&&(i=!i),e=e.next;while(e!==r);return i}function tf(r,t){const e=_o(r.i,r.x,r.y),i=_o(t.i,t.x,t.y),s=r.next,n=t.prev;return r.next=t,t.prev=r,e.next=s,s.prev=e,i.next=e,e.prev=i,n.next=i,i.prev=n,i}function ef(r,t,e,i){const s=_o(r,t,e);return i?(s.next=i.next,s.prev=i,i.next.prev=s,i.next=s):(s.prev=s,s.next=s),s}function gi(r){r.next.prev=r.prev,r.prev.next=r.next,r.prevZ&&(r.prevZ.nextZ=r.nextZ),r.nextZ&&(r.nextZ.prevZ=r.prevZ)}function _o(r,t,e){return{i:r,x:t,y:e,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function TI(r,t,e,i){const s=t&&t.length,n=s?t[0]*e:r.length;let a=Math.abs(bo(r,0,n,e));if(s)for(let l=0,u=t.length;l(r[r.NONE=0]="NONE",r[r.COLOR=16384]="COLOR",r[r.STENCIL=1024]="STENCIL",r[r.DEPTH=256]="DEPTH",r[r.COLOR_DEPTH=16640]="COLOR_DEPTH",r[r.COLOR_STENCIL=17408]="COLOR_STENCIL",r[r.DEPTH_STENCIL=1280]="DEPTH_STENCIL",r[r.ALL=17664]="ALL",r))(Wt||{});class vo{constructor(t){this.items=[],this._name=t}emit(t,e,i,s,n,a,o,l){const{name:u,items:c}=this;for(let h=0,p=c.length;ht in r?Y2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ss=(r,t)=>{for(var e in t||(t={}))K2.call(t,e)&&nf(r,e,t[e]);if(sf)for(var e of sf(t))q2.call(t,e)&&nf(r,e,t[e]);return r};const Z2=["init","destroy","contextChange","resolutionChange","resetState","renderEnd","renderStart","render","update","postrender","prerender"],af=class t1 extends Ut{constructor(t){var e;super(),this.tick=0,this.uid=nt("renderer"),this.runners=Object.create(null),this.renderPipes=Object.create(null),this._initOptions={},this._systemsHash=Object.create(null),this.type=t.type,this.name=t.name,this.config=t;const i=[...Z2,...(e=this.config.runners)!=null?e:[]];this._addRunners(...i),this._unsafeEvalCheck()}async init(t={}){const e=t.skipExtensionImports===!0?!0:t.manageImports===!1;await fo(e),this._addSystems(this.config.systems),this._addPipes(this.config.renderPipes,this.config.renderPipeAdaptors);for(const i in this._systemsHash){const s=this._systemsHash[i].constructor.defaultOptions;t=Ss(Ss({},s),t)}t=Ss(Ss({},t1.defaultOptions),t),this._roundPixels=t.roundPixels?1:0;for(let i=0;i{this.runners[e]=new vo(e)})}_addSystems(t){let e;for(e in t){const i=t[e];this._addSystem(i.value,i.name)}}_addSystem(t,e){const i=new t(this);if(this[e])throw new Error(`Whoops! The name "${e}" is already in use`);this[e]=i,this._systemsHash[e]=i;for(const s in this.runners)this.runners[s].add(i);return this}_addPipes(t,e){const i=e.reduce((s,n)=>(s[n.name]=n.value,s),{});t.forEach(s=>{const n=s.value,a=s.name,o=i[a];this.renderPipes[a]=new n(this,o?new o:null),this.runners.destroy.add(this.renderPipes[a])})}destroy(t=!1){this.runners.destroy.items.reverse(),this.runners.destroy.emit(t),(t===!0||typeof t=="object"&&t.releaseGlobalResources)&&je.release(),Object.values(this.runners).forEach(e=>{e.destroy()}),this._systemsHash=null,this.renderPipes=null}generateTexture(t){return this.textureGenerator.generateTexture(t)}get roundPixels(){return!!this._roundPixels}_unsafeEvalCheck(){if(!mo())throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support.")}resetState(){this.runners.resetState.emit()}};af.defaultOptions={resolution:1,failIfMajorPerformanceCaveat:!1,roundPixels:!1};let Tr=af,yo;function _i(r){return yo!==void 0||(yo=(()=>{var t;const e={stencil:!0,failIfMajorPerformanceCaveat:r!=null?r:Tr.defaultOptions.failIfMajorPerformanceCaveat};try{if(!L.get().getWebGLRenderingContext())return!1;let i=L.get().createCanvas().getContext("webgl",e);const s=!!((t=i==null?void 0:i.getContextAttributes())!=null&&t.stencil);if(i){const n=i.getExtension("WEBGL_lose_context");n&&n.loseContext()}return i=null,s}catch(i){return!1}})()),yo}let xo;async function bi(r={}){return xo!==void 0||(xo=await(async()=>{const t=L.get().getNavigator().gpu;if(!t)return!1;try{return await(await t.requestAdapter(r)).requestDevice(),!0}catch(e){return!1}})()),xo}var Q2=Object.defineProperty,of=Object.getOwnPropertySymbols,J2=Object.prototype.hasOwnProperty,tw=Object.prototype.propertyIsEnumerable,lf=(r,t,e)=>t in r?Q2(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Sr=(r,t)=>{for(var e in t||(t={}))J2.call(t,e)&&lf(r,e,t[e]);if(of)for(var e of of(t))tw.call(t,e)&&lf(r,e,t[e]);return r};const uf=["webgl","webgpu","canvas"];async function cf(r){var t;let e=[];r.preference?(e.push(r.preference),uf.forEach(a=>{a!==r.preference&&e.push(a)})):e=uf.slice();let i,s={};for(let a=0;a{this._resizeTo&&(this._cancelResize(),this._resizeId=requestAnimationFrame(()=>this.resize()))},this._cancelResize=()=>{this._resizeId&&(cancelAnimationFrame(this._resizeId),this._resizeId=null)},this.resize=()=>{if(!this._resizeTo)return;this._cancelResize();let e,i;if(this._resizeTo===globalThis.window)e=globalThis.innerWidth,i=globalThis.innerHeight;else{const{clientWidth:s,clientHeight:n}=this._resizeTo;e=s,i=n}this.renderer.resize(e,i),this.render()},this._resizeId=null,this._resizeTo=null,this.resizeTo=t.resizeTo||null}static destroy(){globalThis.removeEventListener("resize",this.queueResize),this._cancelResize(),this._cancelResize=null,this.queueResize=null,this.resizeTo=null,this.resize=null}}wo.extension=T.Application;class Eo{static init(t){t=Object.assign({autoStart:!0,sharedTicker:!1},t),Object.defineProperty(this,"ticker",{configurable:!0,set(e){this._ticker&&this._ticker.remove(this.render,this),this._ticker=e,e&&e.add(this.render,this,ve.LOW)},get(){return this._ticker}}),this.stop=()=>{this._ticker.stop()},this.start=()=>{this._ticker.start()},this._ticker=null,this.ticker=t.sharedTicker?Ct.shared:new Ct,t.autoStart&&this.start()}static destroy(){if(this._ticker){const t=this._ticker;this.ticker=null,t.destroy()}}}Eo.extension=T.Application,$.add(wo),$.add(Eo);var rw=Object.defineProperty,hf=Object.getOwnPropertySymbols,iw=Object.prototype.hasOwnProperty,sw=Object.prototype.propertyIsEnumerable,df=(r,t,e)=>t in r?rw(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,nw=(r,t)=>{for(var e in t||(t={}))iw.call(t,e)&&df(r,e,t[e]);if(hf)for(var e of hf(t))sw.call(t,e)&&df(r,e,t[e]);return r};const pf=class dh{constructor(...t){this.stage=new dt}async init(t){t=nw({},t),this.stage||(this.stage=new dt),this.renderer=await cf(t),dh._plugins.forEach(e=>{e.init.call(this,t)})}render(){this.renderer.render({container:this.stage})}get canvas(){return this.renderer.canvas}get view(){return this.renderer.canvas}get screen(){return this.renderer.screen}destroy(t=!1,e=!1){const i=dh._plugins.slice(0);i.reverse(),i.forEach(s=>{s.destroy.call(this)}),this.stage.destroy(e),this.stage=null,this.renderer.destroy(t),this.renderer=null}};pf._plugins=[];let ff=pf;$.handleByList(T.Application,ff._plugins),$.add(To);const ws={test(r){return typeof r=="string"&&r.startsWith("info face=")},parse(r){var t,e,i;const s=r.match(/^[a-z]+\s+.+$/gm),n={info:[],common:[],page:[],char:[],chars:[],kerning:[],kernings:[],distanceField:[]};for(const m in s){const g=s[m].match(/^[a-z]+/gm)[0],_=s[m].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm),b={};for(const y in _){const x=_[y].split("="),v=x[0],w=x[1].replace(/"/gm,""),S=parseFloat(w),E=isNaN(S)?w:S;b[v]=E}n[g].push(b)}const a={chars:{},pages:[],lineHeight:0,fontSize:0,fontFamily:"",distanceField:null,baseLineOffset:0},[o]=n.info,[l]=n.common,[u]=(t=n.distanceField)!=null?t:[];u&&(a.distanceField={range:parseInt(u.distanceRange,10),type:u.fieldType}),a.fontSize=parseInt(o.size,10),a.fontFamily=o.face,a.lineHeight=parseInt(l.lineHeight,10);const c=n.page;for(let m=0;m)/)?Po.test(L.get().parseXML(r)):!1},parse(r){return Po.parse(L.get().parseXML(r))}},aw=[".xml",".fnt"],mf={extension:{type:T.CacheParser,name:"cacheBitmapFont"},test:r=>!!(r!=null&&r.pages)&&!!(r!=null&&r.chars)&&typeof(r==null?void 0:r.fontFamily)=="string",getCacheableAssets(r,t){const e={};return r.forEach(i=>{e[i]=t,e[`${i}-bitmap`]=t}),e[`${t.fontFamily}-bitmap`]=t,e}},gf={extension:{type:T.LoadParser,priority:qt.Normal},name:"loadBitmapFont",id:"bitmap-font",test(r){return aw.includes(Xt.extname(r).toLowerCase())},async testParse(r){return ws.test(r)||Ao.test(r)},async parse(r,t,e){const i=ws.test(r)?ws.parse(r):Ao.parse(r),{src:s}=t,{pages:n}=i,a=[],o=i.distanceField?{scaleMode:"linear",alphaMode:"premultiply-alpha-on-upload",autoGenerateMipmaps:!1,resolution:1}:{};for(let h=0;hl[h.src]);return new u({data:i,textures:c},s)},async load(r,t){return await(await L.get().fetch(r)).text()},async unload(r,t,e){await Promise.all(r.pages.map(i=>e.unload(i.texture.source._sourceOrigin))),r.destroy()}};class _f{constructor(t,e=!1){this._loader=t,this._assetList=[],this._isLoading=!1,this._maxConcurrent=1,this.verbose=e}add(t){t.forEach(e=>{this._assetList.push(e)}),this.verbose&&console.log("[BackgroundLoader] assets: ",this._assetList),this._isActive&&!this._isLoading&&this._next()}async _next(){if(this._assetList.length&&this._isActive){this._isLoading=!0;const t=[],e=Math.min(this._assetList.length,this._maxConcurrent);for(let i=0;iArray.isArray(r)&&r.every(t=>t instanceof F),getCacheableAssets:(r,t)=>{const e={};return r.forEach(i=>{t.forEach((s,n)=>{e[i+(n===0?"":n+1)]=s})}),e}};async function Co(r){if("Image"in globalThis)return new Promise(t=>{const e=new Image;e.onload=()=>{t(!0)},e.onerror=()=>{t(!1)},e.src=r});if("createImageBitmap"in globalThis&&"fetch"in globalThis){try{const t=await(await fetch(r)).blob();await createImageBitmap(t)}catch(t){return!1}return!0}return!1}const vf={extension:{type:T.DetectionParser,priority:1},test:async()=>Co("data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="),add:async r=>[...r,"avif"],remove:async r=>r.filter(t=>t!=="avif")},yf=["png","jpg","jpeg"],xf={extension:{type:T.DetectionParser,priority:-1},test:()=>Promise.resolve(!0),add:async r=>[...r,...yf],remove:async r=>r.filter(t=>!yf.includes(t))},ow="WorkerGlobalScope"in globalThis&&globalThis instanceof globalThis.WorkerGlobalScope;function yi(r){return ow?!1:document.createElement("video").canPlayType(r)!==""}const Tf={extension:{type:T.DetectionParser,priority:0},test:async()=>yi("video/mp4"),add:async r=>[...r,"mp4","m4v"],remove:async r=>r.filter(t=>t!=="mp4"&&t!=="m4v")},Sf={extension:{type:T.DetectionParser,priority:0},test:async()=>yi("video/ogg"),add:async r=>[...r,"ogv"],remove:async r=>r.filter(t=>t!=="ogv")},wf={extension:{type:T.DetectionParser,priority:0},test:async()=>yi("video/webm"),add:async r=>[...r,"webm"],remove:async r=>r.filter(t=>t!=="webm")},Ef={extension:{type:T.DetectionParser,priority:0},test:async()=>Co("data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA="),add:async r=>[...r,"webp"],remove:async r=>r.filter(t=>t!=="webp")};var lw=Object.defineProperty,uw=Object.defineProperties,cw=Object.getOwnPropertyDescriptors,Pf=Object.getOwnPropertySymbols,hw=Object.prototype.hasOwnProperty,dw=Object.prototype.propertyIsEnumerable,Af=(r,t,e)=>t in r?lw(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,wr=(r,t)=>{for(var e in t||(t={}))hw.call(t,e)&&Af(r,e,t[e]);if(Pf)for(var e of Pf(t))dw.call(t,e)&&Af(r,e,t[e]);return r},pw=(r,t)=>uw(r,cw(t));const Cf=class ca{constructor(){this.loadOptions=wr({},ca.defaultOptions),this._parsers=[],this._parsersValidated=!1,this.parsers=new Proxy(this._parsers,{set:(t,e,i)=>(this._parsersValidated=!1,t[e]=i,!0)}),this.promiseCache={}}reset(){this._parsersValidated=!1,this.promiseCache={}}_getLoadPromiseAndParser(t,e){const i={promise:null,parser:null};return i.promise=(async()=>{var s,n;let a=null,o=null;if((e.parser||e.loadParser)&&(o=this._parserHash[e.parser||e.loadParser]),!o){for(let l=0;l({alias:[g],src:g,data:{}})),f=p.reduce((g,_)=>g+(_.progressSize||1),0),m=p.map(async g=>{const _=Xt.toAbsolute(g.src);c[g.src]||(await this._loadAssetWithRetry(_,g,{onProgress:s,onError:n,strategy:a,retryCount:o,retryDelay:l},c),u+=g.progressSize||1,s&&s(u/f))});return await Promise.all(m),h?c[p[0].src]:c}async unload(t){const e=se(t,i=>({alias:[i],src:i})).map(async i=>{var s,n;const a=Xt.toAbsolute(i.src),o=this.promiseCache[a];if(o){const l=await o.promise;delete this.promiseCache[a],await((n=(s=o.parser)==null?void 0:s.unload)==null?void 0:n.call(s,l,i,this))}});await Promise.all(e)}_validateParsers(){this._parsersValidated=!0,this._parserHash=this._parsers.filter(t=>t.name||t.id).reduce((t,e)=>(!e.name&&!e.id||t[e.name]||t[e.id],t[e.name]=e,e.id&&(t[e.id]=e),t),{})}async _loadAssetWithRetry(t,e,i,s){let n=0;const{onError:a,strategy:o,retryCount:l,retryDelay:u}=i,c=h=>new Promise(p=>setTimeout(p,h));for(;;)try{this.promiseCache[t]||(this.promiseCache[t]=this._getLoadPromiseAndParser(t,e)),s[e.src]=await this.promiseCache[t].promise;return}catch(h){delete this.promiseCache[t],delete s[e.src],n++;const p=o!=="retry"||n>l;if(o==="retry"&&!p){a&&a(h,e),await c(u);continue}if(o==="skip"){a&&a(h,e);return}a&&a(h,e);const f=new Error(`[Loader.load] Failed to load ${t}. +${h}`);throw h instanceof Error&&h.stack&&(f.stack=h.stack),f}}};Cf.defaultOptions={onProgress:void 0,onError:void 0,strategy:"throw",retryCount:3,retryDelay:250};let Rf=Cf;function tr(r,t){if(Array.isArray(t)){for(const e of t)if(r.startsWith(`data:${e}`))return!0;return!1}return r.startsWith(`data:${t}`)}function ne(r,t){const e=r.split("?")[0],i=Xt.extname(e).toLowerCase();return Array.isArray(t)?t.includes(i):i===t}const fw=".json",mw="application/json",Mf={extension:{type:T.LoadParser,priority:qt.Low},name:"loadJson",id:"json",test(r){return tr(r,mw)||ne(r,fw)},async load(r){return await(await L.get().fetch(r)).json()}},gw=".txt",_w="text/plain",Of={name:"loadTxt",id:"text",extension:{type:T.LoadParser,priority:qt.Low,name:"loadTxt"},test(r){return tr(r,_w)||ne(r,gw)},async load(r){return await(await L.get().fetch(r)).text()}};var bw=Object.defineProperty,vw=Object.defineProperties,yw=Object.getOwnPropertyDescriptors,Gf=Object.getOwnPropertySymbols,xw=Object.prototype.hasOwnProperty,Tw=Object.prototype.propertyIsEnumerable,If=(r,t,e)=>t in r?bw(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Sw=(r,t)=>{for(var e in t||(t={}))xw.call(t,e)&&If(r,e,t[e]);if(Gf)for(var e of Gf(t))Tw.call(t,e)&&If(r,e,t[e]);return r},ww=(r,t)=>vw(r,yw(t));const Ew=["normal","bold","100","200","300","400","500","600","700","800","900"],Pw=[".ttf",".otf",".woff",".woff2"],Aw=["font/ttf","font/otf","font/woff","font/woff2"],Cw=/^(--|-?[A-Z_])[0-9A-Z_-]*$/i;function Bf(r){const t=Xt.extname(r),e=Xt.basename(r,t).replace(/(-|_)/g," ").toLowerCase().split(" ").map(n=>n.charAt(0).toUpperCase()+n.slice(1));let i=e.length>0;for(const n of e)if(!n.match(Cw)){i=!1;break}let s=e.join(" ");return i||(s=`"${s.replace(/[\\"]/g,"\\$&")}"`),s}const Rw=/^[0-9A-Za-z%:/?#\[\]@!\$&'()\*\+,;=\-._~]*$/;function Mw(r){return Rw.test(r)?r:encodeURI(r)}const Ff={extension:{type:T.LoadParser,priority:qt.Low},name:"loadWebFont",id:"web-font",test(r){return tr(r,Aw)||ne(r,Pw)},async load(r,t){var e,i,s,n,a,o;const l=L.get().getFontFaceSet();if(l){const u=[],c=(i=(e=t.data)==null?void 0:e.family)!=null?i:Bf(r),h=(a=(n=(s=t.data)==null?void 0:s.weights)==null?void 0:n.filter(f=>Ew.includes(f)))!=null?a:["normal"],p=(o=t.data)!=null?o:{};for(let f=0;fn.faces.some(a=>t.indexOf(a)!==-1));s.faces=s.faces.filter(n=>t.indexOf(n)===-1),s.faces.length===0&&(i.entries=i.entries.filter(n=>n!==s)),t.forEach(n=>{L.get().getFontFaceSet().delete(n)}),i.entries.length===0&&tt.remove(`${e}-and-url`)}};var Ro,Df;function Ow(){if(Df)return Ro;Df=1,Ro=e;var r={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},t=/([astvzqmhlc])([^astvzqmhlc]*)/ig;function e(n){var a=[];return n.replace(t,function(o,l,u){var c=l.toLowerCase();for(u=s(u),c=="m"&&u.length>2&&(a.push([l].concat(u.splice(0,2))),c="l",l=l=="m"?"l":"L");;){if(u.length==r[c])return u.unshift(l),a.push(u);if(u.length0&&(s=i.pop(),s?(n=s.startX,a=s.startY):(n=0,a=0)),s=null;break;default:}u!=="Z"&&u!=="z"&&s===null&&(s={startX:n,startY:a},i.push(s))}return t}const kf={};function Es(r,t,e){let i=2166136261;for(let s=0;s>>=0;return kf[i]||Bw(r,t,i,e)}function Bw(r,t,e,i){const s={};let n=0;for(let o=0;o{if(Er.quiet||$f.has(t))return;let i=new Error().stack;const s=`${t} +Deprecated since v${r}`,n=typeof console.groupCollapsed=="function"&&!Er.noColor;typeof i=="undefined"?console.warn("PixiJS Deprecation Warning: ",s):(i=i.split(` +`).splice(e).join(` +`),n?(console.groupCollapsed("%cPixiJS Deprecation Warning: %c%s","color:#614108;background:#fffbe6","font-weight:normal;color:#614108;background:#fffbe6",s),console.warn(i),console.groupEnd()):(console.warn("PixiJS Deprecation Warning: ",s),console.warn(i))),$f.add(t)});Object.defineProperties(xi,{quiet:{get:()=>Er.quiet,set:r=>{Er.quiet=r},enumerable:!0,configurable:!1},noColor:{get:()=>Er.noColor,set:r=>{Er.noColor=r},enumerable:!0,configurable:!1}});function Ps(r,t,e,i){if(e!=null||(e=0),i!=null||(i=Math.min(r.byteLength-e,t.byteLength)),!(e&7)&&!(i&7)){const s=i/8;new Float64Array(t,0,s).set(new Float64Array(r,e,s))}else if(!(e&3)&&!(i&3)){const s=i/4;new Float32Array(t,0,s).set(new Float32Array(r,e,s))}else new Uint8Array(t).set(new Uint8Array(r,e,i))}const Lf={normal:"normal-npm",add:"add-npm",screen:"screen-npm"};var xt=(r=>(r[r.DISABLED=0]="DISABLED",r[r.RENDERING_MASK_ADD=1]="RENDERING_MASK_ADD",r[r.MASK_ACTIVE=2]="MASK_ACTIVE",r[r.INVERSE_MASK_ACTIVE=3]="INVERSE_MASK_ACTIVE",r[r.RENDERING_MASK_REMOVE=4]="RENDERING_MASK_REMOVE",r[r.NONE=5]="NONE",r))(xt||{});function Pr(r,t){return t.alphaMode==="no-premultiply-alpha"&&Lf[r]||r}const Dw=["precision mediump float;","void main(void){","float test = 0.1;","%forloop%","gl_FragColor = vec4(0.0);","}"].join(` +`);function Uw(r){let t="";for(let e=0;e0&&(t+=` +else `),et in r?kw(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,zf=(r,t)=>{for(var e in t||(t={}))$w.call(t,e)&&jf(r,e,t[e]);if(Hf)for(var e of Hf(t))Lw.call(t,e)&&jf(r,e,t[e]);return r};class Wf{constructor(){this.renderPipeId="batch",this.action="startBatch",this.start=0,this.size=0,this.textures=new Xf,this.blendMode="normal",this.topology="triangle-strip",this.canBundle=!0}destroy(){this.textures=null,this.gpuBindGroup=null,this.bindGroup=null,this.batcher=null,this.elements=null}}const Ti=[];let As=0;je.register({clear:()=>{if(Ti.length>0)for(const r of Ti)r&&r.destroy();Ti.length=0,As=0}});function Vf(){return As>0?Ti[--As]:new Wf}function Yf(r){r.elements=null,Ti[As++]=r}let Si=0;const Kf=class e1{constructor(t){this.uid=nt("batcher"),this.dirty=!0,this.batchIndex=0,this.batches=[],this._elements=[],t=zf(zf({},e1.defaultOptions),t),t.maxTextures||(xi("v8.8.0","maxTextures is a required option for Batcher now, please pass it in the options"),t.maxTextures=Nf());const{maxTextures:e,attributesInitialSize:i,indicesInitialSize:s}=t;this.attributeBuffer=new er(i*4),this.indexBuffer=new Uint16Array(s),this.maxTextures=e}begin(){this.elementSize=0,this.elementStart=0,this.indexSize=0,this.attributeSize=0;for(let t=0;tthis.attributeBuffer.size&&this._resizeAttributeBuffer(this.attributeSize*4),this.indexSize>this.indexBuffer.length&&this._resizeIndexBuffer(this.indexSize);const l=this.attributeBuffer.float32View,u=this.attributeBuffer.uint32View,c=this.indexBuffer;let h=this._batchIndexSize,p=this._batchIndexStart,f="startBatch",m=[];const g=this.maxTextures;for(let _=this.elementStart;_=g||v)&&(this._finishBatch(i,p,h-p,s,a,o,t,f,m),f="renderBatch",p=h,a=x,o=b.topology,i=Vf(),s=i.textures,s.clear(),m=[],++Si),b._textureId=y._textureBindLocation=s.count,s.ids[y.uid]=s.count,s.textures[s.count++]=y,b._batch=i,m.push(b),h+=b.indexSize,b.packAsQuad?(this.packQuadAttributes(b,l,u,b._attributeStart,b._textureId),this.packQuadIndex(c,b._indexStart,b._attributeStart/this.vertexSize)):(this.packAttributes(b,l,u,b._attributeStart,b._textureId),this.packIndex(b,c,b._indexStart,b._attributeStart/this.vertexSize))}s.count>0&&(this._finishBatch(i,p,h-p,s,a,o,t,f,m),p=h,++Si),this.elementStart=this.elementSize,this._batchIndexStart=p,this._batchIndexSize=h}_finishBatch(t,e,i,s,n,a,o,l,u){t.gpuBindGroup=null,t.bindGroup=null,t.action=l,t.batcher=this,t.textures=s,t.blendMode=n,t.topology=a,t.start=e,t.size=i,t.elements=u,++Si,this.batches[this.batchIndex++]=t,o.add(t)}finish(t){this.break(t)}ensureAttributeBuffer(t){t*4<=this.attributeBuffer.size||this._resizeAttributeBuffer(t*4)}ensureIndexBuffer(t){t<=this.indexBuffer.length||this._resizeIndexBuffer(t)}_resizeAttributeBuffer(t){const e=Math.max(t,this.attributeBuffer.size*2),i=new er(e);Ps(this.attributeBuffer.rawBinaryData,i.rawBinaryData),this.attributeBuffer=i}_resizeIndexBuffer(t){const e=this.indexBuffer;let i=Math.max(t,e.length*1.5);i+=i%2;const s=i>65535?new Uint32Array(i):new Uint16Array(i);if(s.BYTES_PER_ELEMENT!==e.BYTES_PER_ELEMENT)for(let n=0;ns.replace(/[{()}]/g,"")))!=null?e:[]).forEach(s=>{i[s]=[]}),i}function Qf(r,t){let e;const i=/@in\s+([^;]+);/g;for(;(e=i.exec(r))!==null;)t.push(e[1])}function Bo(r,t,e=!1){const i=[];Qf(t,i),r.forEach(o=>{o.header&&Qf(o.header,i)});const s=i;e&&s.sort();const n=s.map((o,l)=>` @location(${l}) ${o},`).join(` +`);let a=t.replace(/@in\s+[^;]+;\s*/g,"");return a=a.replace("{{in}}",` +${n} +`),a}function Jf(r,t){let e;const i=/@out\s+([^;]+);/g;for(;(e=i.exec(r))!==null;)t.push(e[1])}function jw(r){const t=/\b(\w+)\s*:/g.exec(r);return t?t[1]:""}function zw(r){const t=/@.*?\s+/g;return r.replace(t,"")}function tm(r,t){const e=[];Jf(t,e),r.forEach(l=>{l.header&&Jf(l.header,e)});let i=0;const s=e.sort().map(l=>l.indexOf("builtin")>-1?l:`@location(${i++}) ${l}`).join(`, +`),n=e.sort().map(l=>` var ${zw(l)};`).join(` +`),a=`return VSOutput( + ${e.sort().map(l=>` ${jw(l)}`).join(`, +`)});`;let o=t.replace(/@out\s+[^;]+;\s*/g,"");return o=o.replace("{{struct}}",` +${s} +`),o=o.replace("{{start}}",` +${n} +`),o=o.replace("{{return}}",` +${a} +`),o}function Fo(r,t){let e=r;for(const i in t){const s=t[i];s.join(` +`).length?e=e.replace(`{{${i}}}`,`//-----${i} START-----// +${s.join(` +`)} +//----${i} FINISH----//`):e=e.replace(`{{${i}}}`,"")}return e}const rr=Object.create(null),Do=new Map;let Ww=0;function em({template:r,bits:t}){const e=im(r,t);if(rr[e])return rr[e];const{vertex:i,fragment:s}=Vw(r,t);return rr[e]=sm(i,s,t),rr[e]}function rm({template:r,bits:t}){const e=im(r,t);return rr[e]||(rr[e]=sm(r.vertex,r.fragment,t)),rr[e]}function Vw(r,t){const e=t.map(a=>a.vertex).filter(a=>!!a),i=t.map(a=>a.fragment).filter(a=>!!a);let s=Bo(e,r.vertex,!0);s=tm(e,s);const n=Bo(i,r.fragment,!0);return{vertex:s,fragment:n}}function im(r,t){return t.map(e=>(Do.has(e)||Do.set(e,Ww++),Do.get(e))).sort((e,i)=>e-i).join("-")+r.vertex+r.fragment}function sm(r,t,e){const i=Io(r),s=Io(t);return e.forEach(n=>{Go(n.vertex,i,n.name),Go(n.fragment,s,n.name)}),{vertex:Fo(r,i),fragment:Fo(t,s)}}const nm=` + @in aPosition: vec2; + @in aUV: vec2; + + @out @builtin(position) vPosition: vec4; + @out vUV : vec2; + @out vColor : vec4; + + {{header}} + + struct VSOutput { + {{struct}} + }; + + @vertex + fn main( {{in}} ) -> VSOutput { + + var worldTransformMatrix = globalUniforms.uWorldTransformMatrix; + var modelMatrix = mat3x3( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + var position = aPosition; + var uv = aUV; + + {{start}} + + vColor = vec4(1., 1., 1., 1.); + + {{main}} + + vUV = uv; + + var modelViewProjectionMatrix = globalUniforms.uProjectionMatrix * worldTransformMatrix * modelMatrix; + + vPosition = vec4((modelViewProjectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0); + + vColor *= globalUniforms.uWorldColorAlpha; + + {{end}} + + {{return}} + }; +`,am=` + @in vUV : vec2; + @in vColor : vec4; + + {{header}} + + @fragment + fn main( + {{in}} + ) -> @location(0) vec4 { + + {{start}} + + var outColor:vec4; + + {{main}} + + var finalColor:vec4 = outColor * vColor; + + {{end}} + + return finalColor; + }; +`,om=` + in vec2 aPosition; + in vec2 aUV; + + out vec4 vColor; + out vec2 vUV; + + {{header}} + + void main(void){ + + mat3 worldTransformMatrix = uWorldTransformMatrix; + mat3 modelMatrix = mat3( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + vec2 position = aPosition; + vec2 uv = aUV; + + {{start}} + + vColor = vec4(1.); + + {{main}} + + vUV = uv; + + mat3 modelViewProjectionMatrix = uProjectionMatrix * worldTransformMatrix * modelMatrix; + + gl_Position = vec4((modelViewProjectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0); + + vColor *= uWorldColorAlpha; + + {{end}} + } +`,lm=` + + in vec4 vColor; + in vec2 vUV; + + out vec4 finalColor; + + {{header}} + + void main(void) { + + {{start}} + + vec4 outColor; + + {{main}} + + finalColor = outColor * vColor; + + {{end}} + } +`,um={name:"global-uniforms-bit",vertex:{header:` + struct GlobalUniforms { + uProjectionMatrix:mat3x3, + uWorldTransformMatrix:mat3x3, + uWorldColorAlpha: vec4, + uResolution: vec2, + } + + @group(0) @binding(0) var globalUniforms : GlobalUniforms; + `}},Yw={name:"global-uniforms-ubo-bit",vertex:{header:` + uniform globalUniforms { + mat3 uProjectionMatrix; + mat3 uWorldTransformMatrix; + vec4 uWorldColorAlpha; + vec2 uResolution; + }; + `}},cm={name:"global-uniforms-bit",vertex:{header:` + uniform mat3 uProjectionMatrix; + uniform mat3 uWorldTransformMatrix; + uniform vec4 uWorldColorAlpha; + uniform vec2 uResolution; + `}};var Kw=Object.defineProperty,hm=Object.getOwnPropertySymbols,qw=Object.prototype.hasOwnProperty,Zw=Object.prototype.propertyIsEnumerable,dm=(r,t,e)=>t in r?Kw(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Qw=(r,t)=>{for(var e in t||(t={}))qw.call(t,e)&&dm(r,e,t[e]);if(hm)for(var e of hm(t))Zw.call(t,e)&&dm(r,e,t[e]);return r};function Cr({bits:r,name:t}){const e=em({template:{fragment:am,vertex:nm},bits:[um,...r]});return kt.from({name:t,vertex:{source:e.vertex,entryPoint:"main"},fragment:{source:e.fragment,entryPoint:"main"}})}function Rr({bits:r,name:t}){return new Ht(Qw({name:t},rm({template:{vertex:om,fragment:lm},bits:[cm,...r]})))}const Cs={name:"color-bit",vertex:{header:` + @in aColor: vec4; + `,main:` + vColor *= vec4(aColor.rgb * aColor.a, aColor.a); + `}},Rs={name:"color-bit",vertex:{header:` + in vec4 aColor; + `,main:` + vColor *= vec4(aColor.rgb * aColor.a, aColor.a); + `}},Uo={};function Jw(r){const t=[];if(r===1)t.push("@group(1) @binding(0) var textureSource1: texture_2d;"),t.push("@group(1) @binding(1) var textureSampler1: sampler;");else{let e=0;for(let i=0;i;`),t.push(`@group(1) @binding(${e++}) var textureSampler${i+1}: sampler;`)}return t.join(` +`)}function tE(r){const t=[];if(r===1)t.push("outColor = textureSampleGrad(textureSource1, textureSampler1, vUV, uvDx, uvDy);");else{t.push("switch vTextureId {");for(let e=0;e; + @out @interpolate(flat) vTextureId : u32; + `,main:` + vTextureId = aTextureIdAndRound.y; + `,end:` + if(aTextureIdAndRound.x == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + `},fragment:{header:` + @in @interpolate(flat) vTextureId: u32; + + ${Jw(r)} + `,main:` + var uvDx = dpdx(vUV); + var uvDy = dpdy(vUV); + + ${tE(r)} + `}}),Uo[r]}const ko={};function eE(r){const t=[];for(let e=0;e0&&t.push("else"),e, targetSize: vec2) -> vec2 + { + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; + } + `}},Or={name:"round-pixels-bit",vertex:{header:` + vec2 roundPixels(vec2 position, vec2 targetSize) + { + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; + } + `}},pm={};function Gs(r){let t=pm[r];if(t)return t;const e=new Int32Array(r);for(let i=0;ie&&this.remove(e,...t))}destroy(...t){this.removeAll(...t),this.items=Object.create(null),this._renderer=null,this._onUnload=null}}function Lo(r,t,e,i,s,n,a,o=null){let l=0;e*=t,s*=n;const u=o.a,c=o.b,h=o.c,p=o.d,f=o.tx,m=o.ty;for(;l>16|t&65280|(t&255)<<16,i=this.renderable;return i?Ce(e,i.groupColor)+(this.alpha*i.groupAlpha*255<<24):e+(this.alpha*255<<24)}get transform(){var t;return((t=this.renderable)==null?void 0:t.groupTransform)||rE}copyTo(t){t.indexOffset=this.indexOffset,t.indexSize=this.indexSize,t.attributeOffset=this.attributeOffset,t.attributeSize=this.attributeSize,t.baseColor=this.baseColor,t.alpha=this.alpha,t.texture=this.texture,t.geometryData=this.geometryData,t.topology=this.topology}reset(){this.applyTransform=!0,this.renderable=null,this.topology="triangle-list"}destroy(){this.renderable=null,this.texture=null,this.geometryData=null,this._batcher=null,this._batch=null}}var iE=Object.defineProperty,sE=Object.defineProperties,nE=Object.getOwnPropertyDescriptors,mm=Object.getOwnPropertySymbols,aE=Object.prototype.hasOwnProperty,oE=Object.prototype.propertyIsEnumerable,gm=(r,t,e)=>t in r?iE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ds=(r,t)=>{for(var e in t||(t={}))aE.call(t,e)&&gm(r,e,t[e]);if(mm)for(var e of mm(t))oE.call(t,e)&&gm(r,e,t[e]);return r},Us=(r,t)=>sE(r,nE(t));const Gr={extension:{type:T.ShapeBuilder,name:"circle"},build(r,t){let e,i,s,n,a,o;if(r.type==="circle"){const v=r;if(a=o=v.radius,a<=0)return!1;e=v.x,i=v.y,s=n=0}else if(r.type==="ellipse"){const v=r;if(a=v.halfWidth,o=v.halfHeight,a<=0||o<=0)return!1;e=v.x,i=v.y,s=n=0}else{const v=r,w=v.width/2,S=v.height/2;e=v.x+w,i=v.y+S,a=o=Math.max(0,Math.min(v.radius,Math.min(w,S))),s=w-a,n=S-o}if(s<0||n<0)return!1;const l=Math.ceil(2.3*Math.sqrt(a+o)),u=l*8+(s?4:0)+(n?4:0);if(u===0)return!1;if(l===0)return t[0]=t[6]=e+s,t[1]=t[3]=i+n,t[2]=t[4]=e-s,t[5]=t[7]=i-n,!0;let c=0,h=l*4+(s?2:0)+2,p=h,f=u,m=s+a,g=n,_=e+m,b=e-m,y=i+g;if(t[c++]=_,t[c++]=y,t[--h]=y,t[--h]=b,n){const v=i-g;t[p++]=b,t[p++]=v,t[--f]=v,t[--f]=_}for(let v=1;v0&&(s[n++]=l,s[n++]=u,s[n++]=l-1),l++;s[n++]=u+1,s[n++]=u,s[n++]=l-1}},_m=Us(Ds({},Gr),{extension:Us(Ds({},Gr.extension),{name:"ellipse"})}),bm=Us(Ds({},Gr),{extension:Us(Ds({},Gr.extension),{name:"roundedRectangle"})}),Xo=1e-4,Ho=1e-4;function vm(r){const t=r.length;if(t<6)return 1;let e=0;for(let i=0,s=r[t-2],n=r[t-1];ih&&(h+=Math.PI*2);let p=c;const f=h-c,m=Math.abs(f),g=Math.sqrt(l*l+u*u),_=(15*m*Math.sqrt(g)/Math.PI>>0)+1,b=f/_;if(p+=b,o){a.push(r,t),a.push(e,i);for(let y=1,x=p;y<_;y++,x+=b)a.push(r,t),a.push(r+Math.sin(x)*g,t+Math.cos(x)*g);a.push(r,t),a.push(s,n)}else{a.push(e,i),a.push(r,t);for(let y=1,x=p;y<_;y++,x+=b)a.push(r+Math.sin(x)*g,t+Math.cos(x)*g),a.push(r,t);a.push(s,n),a.push(r,t)}return _*2}function jo(r,t,e,i,s,n){const a=Xo;if(r.length===0)return;const o=t;let l=o.alignment;if(t.alignment!==.5){let N=vm(r);e&&(N*=-1),l=(l-.5)*N+.5}const u=new rt(r[0],r[1]),c=new rt(r[r.length-2],r[r.length-1]),h=i,p=Math.abs(u.x-c.x)=0&&(o.join==="round"?g+=ir(S,E,S-C*A,E-P*A,S-R*A,E-G*A,f,!1)+4:g+=2,f.push(S-R*B,E-G*B),f.push(S+R*A,E+G*A));continue}const It=(-C+v)*(-P+E)-(-C+S)*(-P+w),at=(-R+O)*(-G+E)-(-R+S)*(-G+M),pt=(q*at-j*It)/Z,ft=(X*It-k*at)/Z,_t=(pt-S)*(pt-S)+(ft-E)*(ft-E),ct=S+(pt-S)*A,mt=E+(ft-E)*A,ot=S-(pt-S)*B,z=E-(ft-E)*B,At=Math.min(q*q+k*k,j*j+X*X),Et=ut?A:B,le=At+Et*Et*y;_t<=le?o.join==="bevel"||_t/y>x?(ut?(f.push(ct,mt),f.push(S+C*B,E+P*B),f.push(ct,mt),f.push(S+R*B,E+G*B)):(f.push(S-C*A,E-P*A),f.push(ot,z),f.push(S-R*A,E-G*A),f.push(ot,z)),g+=2):o.join==="round"?ut?(f.push(ct,mt),f.push(S+C*B,E+P*B),g+=ir(S,E,S+C*B,E+P*B,S+R*B,E+G*B,f,!0)+4,f.push(ct,mt),f.push(S+R*B,E+G*B)):(f.push(S-C*A,E-P*A),f.push(ot,z),g+=ir(S,E,S-C*A,E-P*A,S-R*A,E-G*A,f,!1)+4,f.push(S-R*A,E-G*A),f.push(ot,z)):(f.push(ct,mt),f.push(ot,z)):(f.push(S-C*A,E-P*A),f.push(S+C*B,E+P*B),o.join==="round"?ut?g+=ir(S,E,S+C*B,E+P*B,S+R*B,E+G*B,f,!0)+2:g+=ir(S,E,S-C*A,E-P*A,S-R*A,E-G*A,f,!1)+2:o.join==="miter"&&_t/y<=x&&(ut?(f.push(ot,z),f.push(ot,z)):(f.push(ct,mt),f.push(ct,mt)),g+=2),f.push(S-R*A,E-G*A),f.push(S+R*B,E+G*B),g+=2)}v=r[(m-2)*2],w=r[(m-2)*2+1],S=r[(m-1)*2],E=r[(m-1)*2+1],C=-(w-E),P=v-S,I=Math.sqrt(C*C+P*P),C/=I,P/=I,C*=b,P*=b,f.push(S-C*A,E-P*A),f.push(S+C*B,E+P*B),h||(o.cap==="round"?g+=ir(S-C*(A-B)*.5,E-P*(A-B)*.5,S-C*A,E-P*A,S+C*B,E+P*B,f,!1)+2:o.cap==="square"&&(g+=ym(S,E,C,P,A,B,!1,f)));const Q=Ho*Ho;for(let N=_;N0&&a>0?(t[0]=i,t[1]=s,t[2]=i+n,t[3]=s,t[4]=i+n,t[5]=s+a,t[6]=i,t[7]=s+a,!0):!1},triangulate(r,t,e,i,s,n){let a=0;i*=e,t[i+a]=r[0],t[i+a+1]=r[1],a+=e,t[i+a]=r[2],t[i+a+1]=r[3],a+=e,t[i+a]=r[6],t[i+a+1]=r[7],a+=e,t[i+a]=r[4],t[i+a+1]=r[5],a+=e;const o=i/e;s[n++]=o,s[n++]=o+1,s[n++]=o+2,s[n++]=o+1,s[n++]=o+3,s[n++]=o+2}},wm={extension:{type:T.ShapeBuilder,name:"triangle"},build(r,t){return t[0]=r.x,t[1]=r.y,t[2]=r.x2,t[3]=r.y2,t[4]=r.x3,t[5]=r.y3,!0},triangulate(r,t,e,i,s,n){let a=0;i*=e,t[i+a]=r[0],t[i+a+1]=r[1],a+=e,t[i+a]=r[2],t[i+a+1]=r[3],a+=e,t[i+a]=r[4],t[i+a+1]=r[5];const o=i/e;s[n++]=o,s[n++]=o+1,s[n++]=o+2}};var uE=Object.defineProperty,Em=Object.getOwnPropertySymbols,cE=Object.prototype.hasOwnProperty,hE=Object.prototype.propertyIsEnumerable,Pm=(r,t,e)=>t in r?uE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Am=(r,t)=>{for(var e in t||(t={}))cE.call(t,e)&&Pm(r,e,t[e]);if(Em)for(var e of Em(t))hE.call(t,e)&&Pm(r,e,t[e]);return r};const Cm=[{offset:0,color:"white"},{offset:1,color:"black"}],Wo=class ph{constructor(...t){this.uid=nt("fillGradient"),this._tick=0,this.type="linear",this.colorStops=[];var e;let i=dE(t);const s=i.type==="radial"?ph.defaultRadialOptions:ph.defaultLinearOptions;i=Am(Am({},s),ue(i)),this._textureSize=i.textureSize,this._wrapMode=i.wrapMode,i.type==="radial"?(this.center=i.center,this.outerCenter=(e=i.outerCenter)!=null?e:this.center,this.innerRadius=i.innerRadius,this.outerRadius=i.outerRadius,this.scale=i.scale,this.rotation=i.rotation):(this.start=i.start,this.end=i.end),this.textureSpace=i.textureSpace,this.type=i.type,i.colorStops.forEach(n=>{this.addColorStop(n.offset,n.color)})}addColorStop(t,e){return this.colorStops.push({offset:t,color:J.shared.setValue(e).toHexa()}),this}buildLinearGradient(){if(this.texture)return;let{x:t,y:e}=this.start,{x:i,y:s}=this.end,n=i-t,a=s-e;const o=n<0||a<0;if(this._wrapMode==="clamp-to-edge"){if(n<0){const _=t;t=i,i=_,n*=-1}if(a<0){const _=e;e=s,s=_,a*=-1}}const l=this.colorStops.length?this.colorStops:Cm,u=this._textureSize,{canvas:c,context:h}=Mm(u,1),p=o?h.createLinearGradient(this._textureSize,0,0,0):h.createLinearGradient(0,0,this._textureSize,0);Rm(p,l),h.fillStyle=p,h.fillRect(0,0,u,1),this.texture=new F({source:new De({resource:c,addressMode:this._wrapMode})});const f=Math.sqrt(n*n+a*a),m=Math.atan2(a,n),g=new D;g.scale(f/u,1),g.rotate(m),g.translate(t,e),this.textureSpace==="local"&&g.scale(u,u),this.transform=g}buildGradient(){this.texture||this._tick++,this.type==="linear"?this.buildLinearGradient():this.buildRadialGradient()}buildRadialGradient(){if(this.texture)return;const t=this.colorStops.length?this.colorStops:Cm,e=this._textureSize,{canvas:i,context:s}=Mm(e,e),{x:n,y:a}=this.center,{x:o,y:l}=this.outerCenter,u=this.innerRadius,c=this.outerRadius,h=o-c,p=l-c,f=e/(c*2),m=(n-h)*f,g=(a-p)*f,_=s.createRadialGradient(m,g,u*f,(o-h)*f,(l-p)*f,c*f);Rm(_,t),s.fillStyle=t[t.length-1].color,s.fillRect(0,0,e,e),s.fillStyle=_,s.translate(m,g),s.rotate(this.rotation),s.scale(1,this.scale),s.translate(-m,-g),s.fillRect(0,0,e,e),this.texture=new F({source:new De({resource:i,addressMode:this._wrapMode})});const b=new D;b.scale(1/f,1/f),b.translate(h,p),this.textureSpace==="local"&&b.scale(e,e),this.transform=b}destroy(){var t;(t=this.texture)==null||t.destroy(!0),this.texture=null,this.transform=null,this.colorStops=[],this.start=null,this.end=null,this.center=null,this.outerCenter=null}get styleKey(){return`fill-gradient-${this.uid}-${this._tick}`}};Wo.defaultLinearOptions={start:{x:0,y:0},end:{x:0,y:1},colorStops:[],textureSpace:"local",type:"linear",textureSize:256,wrapMode:"clamp-to-edge"},Wo.defaultRadialOptions={center:{x:.5,y:.5},innerRadius:0,outerRadius:.5,colorStops:[],scale:1,textureSpace:"local",type:"radial",textureSize:256,wrapMode:"clamp-to-edge"};let Qt=Wo;function Rm(r,t){for(let e=0;e{var h;const p=[],f=sr[l.type];if(!f.build(l,p))return;const m=o.length,g=n.length/2;let _="triangle-list";if(u&&Bs(p,u),e){const v=(h=l.closePath)!=null?h:!0,w=t;w.pixelLine?(xm(p,v,n,o),_="line-list"):jo(p,w,!1,v,n,o)}else if(c){const v=[],w=p.slice();bE(c).forEach(S=>{v.push(w.length/2),w.push(...S)}),zo(w,v,n,2,g,o,m)}else f.triangulate(p,n,2,g,o,m);const b=a.length/2,y=t.texture;if(y!==F.WHITE){const v=Vo(gE,t,l,u);Lo(n,2,g,a,b,2,n.length/2-g,v)}else No(a,b,2,n.length/2-g);const x=St.get(Fs);x.indexOffset=m,x.indexSize=o.length-m,x.attributeOffset=g,x.attributeSize=n.length/2-g,x.baseColor=t.color,x.alpha=t.alpha,x.texture=y,x.geometryData=s,x.topology=_,i.push(x)})}function bE(r){const t=[];for(let e=0;e{St.return(t)}),this.graphicsData&&St.return(this.graphicsData),this.isBatchable=!1,this.context=null,this.batches.length=0,this.geometryData.indices.length=0,this.geometryData.vertices.length=0,this.geometryData.uvs.length=0,this.graphicsData=null}destroy(){this.reset(),this.batches=null,this.geometryData=null}}class Bm{constructor(){this.instructions=new ts}init(t){const e=t.maxTextures;this.batcher?this.batcher._updateMaxTextures(e):this.batcher=new Is({maxTextures:e}),this.instructions.reset()}get geometry(){return this.batcher.geometry}destroy(){this.batcher.destroy(),this.instructions.destroy(),this.batcher=null,this.instructions=null}}const Yo=class fh{constructor(t){this._renderer=t,this._managedContexts=new Ft({renderer:t,type:"resource",name:"graphicsContext"})}init(t){var e;fh.defaultOptions.bezierSmoothness=(e=t==null?void 0:t.bezierSmoothness)!=null?e:fh.defaultOptions.bezierSmoothness}getContextRenderData(t){return t._gpuData[this._renderer.uid].graphicsData||this._initContextRenderData(t)}updateGpuContext(t){const e=!!t._gpuData[this._renderer.uid],i=t._gpuData[this._renderer.uid]||this._initContext(t);if(t.dirty||!e){e&&i.reset(),Om(t,i);const s=t.batchMode;t.customShader||s==="no-batch"?i.isBatchable=!1:s==="auto"?i.isBatchable=i.geometryData.vertices.length<400:i.isBatchable=!0,t.dirty=!1}return i}getGpuContext(t){return t._gpuData[this._renderer.uid]||this._initContext(t)}_initContextRenderData(t){const e=St.get(Bm,{maxTextures:this._renderer.limits.maxBatchableTextures}),i=t._gpuData[this._renderer.uid],{batches:s,geometryData:n}=i;i.graphicsData=e;const a=n.vertices.length,o=n.indices.length;for(let h=0;hvE)return;const h=Math.PI,p=(r+e)/2,f=(t+i)/2,m=(e+s)/2,g=(i+n)/2,_=(s+a)/2,b=(n+o)/2,y=(p+m)/2,x=(f+g)/2,v=(m+_)/2,w=(g+b)/2,S=(y+v)/2,E=(x+w)/2;if(c>0){let O=a-r,M=o-t;const C=Math.abs((e-a)*M-(i-o)*O),P=Math.abs((s-a)*M-(n-o)*O);let R,G;if(C>$s&&P>$s){if((C+P)*(C+P)<=u*(O*O+M*M)){if(Ir=h&&(R=2*h-R),G>=h&&(G=2*h-G),R+Gnr){l.push(e,i);return}if(G>nr){l.push(s,n);return}}}}else if(C>$s){if(C*C<=u*(O*O+M*M)){if(Ir=h&&(R=2*h-R),Rnr){l.push(e,i);return}}}else if(P>$s){if(P*P<=u*(O*O+M*M)){if(Ir=h&&(R=2*h-R),Rnr){l.push(s,n);return}}}else if(O=S-(r+a)/2,M=E-(t+o)/2,O*O+M*M<=u){l.push(S,E);return}}Zo(r,t,p,f,y,x,S,E,l,u,c+1),Zo(S,E,v,w,_,b,a,o,l,u,c+1)}const TE=8,SE=11920929e-14,wE=1,EE=.01,Fm=0;function Dm(r,t,e,i,s,n,a,o){const l=Math.min(.99,Math.max(0,o!=null?o:ks.defaultOptions.bezierSmoothness));let u=(wE-l)/1;return u*=u,PE(t,e,i,s,n,a,r,u),r}function PE(r,t,e,i,s,n,a,o){Qo(a,r,t,e,i,s,n,o,0),a.push(s,n)}function Qo(r,t,e,i,s,n,a,o,l){if(l>TE)return;const u=Math.PI,c=(t+i)/2,h=(e+s)/2,p=(i+n)/2,f=(s+a)/2,m=(c+p)/2,g=(h+f)/2;let _=n-t,b=a-e;const y=Math.abs((i-n)*b-(s-a)*_);if(y>SE){if(y*y<=o*(_*_+b*b)){if(Fm=u&&(x=2*u-x),xn||a&&n>s)&&(l=2*Math.PI-l),o||(o=Math.max(6,Math.floor(6*Math.pow(i,.3333333333333333)*(l/Math.PI)))),o=Math.max(o,3);let u=l/o,c=s;u*=a?-1:1;for(let h=0;hc*o)}const Ei=Math.PI*2,tl={centerX:0,centerY:0,ang1:0,ang2:0},el=({x:r,y:t},e,i,s,n,a,o,l)=>{r*=e,t*=i;const u=s*r-n*t,c=n*r+s*t;return l.x=u+a,l.y=c+o,l};function AE(r,t){const e=t===-1.5707963267948966?-.551915024494:1.3333333333333333*Math.tan(t/4),i=t===1.5707963267948966?.551915024494:e,s=Math.cos(r),n=Math.sin(r),a=Math.cos(r+t),o=Math.sin(r+t);return[{x:s-n*i,y:n+s*i},{x:a+o*i,y:o-a*i},{x:a,y:o}]}const km=(r,t,e,i)=>{const s=r*i-t*e<0?-1:1;let n=r*e+t*i;return n>1&&(n=1),n<-1&&(n=-1),s*Math.acos(n)},CE=(r,t,e,i,s,n,a,o,l,u,c,h,p)=>{const f=Math.pow(s,2),m=Math.pow(n,2),g=Math.pow(c,2),_=Math.pow(h,2);let b=f*m-f*_-m*g;b<0&&(b=0),b/=f*_+m*g,b=Math.sqrt(b)*(a===o?-1:1);const y=b*s/n*h,x=b*-n/s*c,v=u*y-l*x+(r+e)/2,w=l*y+u*x+(t+i)/2,S=(c-y)/s,E=(h-x)/n,O=(-c-y)/s,M=(-h-x)/n,C=km(1,0,S,E);let P=km(S,E,O,M);o===0&&P>0&&(P-=Ei),o===1&&P<0&&(P+=Ei),p.centerX=v,p.centerY=w,p.ang1=C,p.ang2=P};function $m(r,t,e,i,s,n,a,o=0,l=0,u=0){if(n===0||a===0)return;const c=Math.sin(o*Ei/360),h=Math.cos(o*Ei/360),p=h*(t-i)/2+c*(e-s)/2,f=-c*(t-i)/2+h*(e-s)/2;if(p===0&&f===0)return;n=Math.abs(n),a=Math.abs(a);const m=Math.pow(p,2)/Math.pow(n,2)+Math.pow(f,2)/Math.pow(a,2);m>1&&(n*=Math.sqrt(m),a*=Math.sqrt(m)),CE(t,e,i,s,n,a,l,u,c,h,p,f,tl);let{ang1:g,ang2:_}=tl;const{centerX:b,centerY:y}=tl;let x=Math.abs(_)/(Ei/4);Math.abs(1-x)<1e-7&&(x=1);const v=Math.max(Math.ceil(x),1);_/=v;let w=r[r.length-2],S=r[r.length-1];const E={x:0,y:0};for(let O=0;O{const u=l.x-o.x,c=l.y-o.y,h=Math.sqrt(u*u+c*c),p=u/h,f=c/h;return{len:h,nx:p,ny:f}},n=(o,l)=>{o===0?r.moveTo(l.x,l.y):r.lineTo(l.x,l.y)};let a=t[t.length-1];for(let o=0;o0&&(m=-1,g=!0);const _=f/2;let b,y=Math.abs(Math.cos(_)*u/Math.sin(_));y>Math.min(h.len/2,p.len/2)?(y=Math.min(h.len/2,p.len/2),b=Math.abs(y*Math.sin(_)/Math.cos(_))):b=u;const x=l.x+p.nx*y+-p.ny*b*m,v=l.y+p.ny*y+p.nx*b*m,w=Math.atan2(h.ny,h.nx)+Math.PI/2*m,S=Math.atan2(p.ny,p.nx)-Math.PI/2*m;o===0&&r.moveTo(x+Math.cos(w)*b,v+Math.sin(w)*b),r.arc(x,v,b,w,S,g),a=l}}function Nm(r,t,e,i){var s;const n=(l,u)=>Math.sqrt((l.x-u.x)**2+(l.y-u.y)**2),a=(l,u,c)=>({x:l.x+(u.x-l.x)*c,y:l.y+(u.y-l.y)*c}),o=t.length;for(let l=0;l1){let n=null;for(let a=s;a=2;h-=2)c[h]===c[h-2]&&c[h-1]===c[h-3]&&c.splice(h-1,2);return this.poly(c,!0,a)}ellipse(t,e,i,s,n){return this.drawShape(new bs(t,e,i,s),n),this}roundRect(t,e,i,s,n,a){return this.drawShape(new ys(t,e,i,s,n),a),this}drawShape(t,e){return this.endPoly(),this.shapePrimitives.push({shape:t,transform:e}),this}startPoly(t,e){let i=this._currentPoly;return i&&this.endPoly(),i=new yr,i.points.push(t,e),this._currentPoly=i,this}endPoly(t=!1){const e=this._currentPoly;return e&&e.points.length>2&&(e.closePath=t,this.shapePrimitives.push({shape:e})),this._currentPoly=null,this}_ensurePoly(t=!0){if(!this._currentPoly&&(this._currentPoly=new yr,t)){const e=this.shapePrimitives[this.shapePrimitives.length-1];if(e){let i=e.shape.x,s=e.shape.y;if(e.transform&&!e.transform.isIdentity()){const n=e.transform,a=i;i=n.a*i+n.c*s+n.tx,s=n.b*a+n.d*s+n.ty}this._currentPoly.points.push(i,s)}else this._currentPoly.points.push(0,0)}}buildPath(){const t=this._graphicsPath2D;this.shapePrimitives.length=0,this._currentPoly=null;for(let e=0;eo.area).sort((o,l)=>l-o),[e,i]=t,s=t[t.length-1],n=e/i,a=i/s;return!(n>3&&a<2)}function GE(r,t=0){const e=r.instructions[t];if(!e||e.action!=="fill")throw new Error(`Expected fill instruction at index ${t}, got ${(e==null?void 0:e.action)||"undefined"}`);return e.data}function Vm(r){return r.split(/(?=[Mm])/).filter(t=>t.trim().length>0)}function Ym(r){const t=r.match(/[-+]?[0-9]*\.?[0-9]+/g);if(!t||t.length<4)return 0;const e=t.map(Number),i=[],s=[];for(let u=0;ut in r?IE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ls=(r,t)=>{for(var e in t||(t={}))BE.call(t,e)&&qm(r,e,t[e]);if(Km)for(var e of Km(t))FE.call(t,e)&&qm(r,e,t[e]);return r};function Zm(r,t){if(typeof r=="string"){const a=document.createElement("div");a.innerHTML=r.trim(),r=a.querySelector("svg")}const e={context:t,defs:{},path:new pe};jm(r,e);const i=r.children,{fillStyle:s,strokeStyle:n}=nl(r,e);for(let a=0;a1;if(P&&R){const G=C.map(I=>({path:I,area:Ym(I)}));if(G.sort((I,U)=>U.area-I.area),C.length>3||!Wm(G))for(let I=0;IparseInt(M,10)),t.context.poly(x,!0),e&&t.context.fill(e),i&&t.context.stroke(i);break;case"polyline":v=r.getAttribute("points"),x=v.match(/-?\d+/g).map(M=>parseInt(M,10)),t.context.poly(x,!1),i&&t.context.stroke(i);break;case"g":case"svg":break;default:{ae(`[SVG parser] <${r.nodeName}> elements unsupported`);break}}o&&(e=null);for(let M=0;Mt in r?DE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,fe=(r,t)=>{for(var e in t||(t={}))tg.call(t,e)&&rg(r,e,t[e]);if(Ns)for(var e of Ns(t))eg.call(t,e)&&rg(r,e,t[e]);return r},UE=(r,t)=>{var e={};for(var i in r)tg.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Ns)for(var i of Ns(r))t.indexOf(i)<0&&eg.call(r,i)&&(e[i]=r[i]);return e};function kE(r){return J.isColorLike(r)}function ig(r){return r instanceof Br}function sg(r){return r instanceof Qt}function $E(r){return r instanceof F}function LE(r,t,e){const i=J.shared.setValue(t!=null?t:0);return r.color=i.toNumber(),r.alpha=i.alpha===1?e.alpha:i.alpha,r.texture=F.WHITE,fe(fe({},e),r)}function NE(r,t,e){return r.texture=t,fe(fe({},e),r)}function ng(r,t,e){return r.fill=t,r.color=16777215,r.texture=t.texture,r.matrix=t.transform,fe(fe({},e),r)}function ag(r,t,e){return t.buildGradient(),r.fill=t,r.color=16777215,r.texture=t.texture,r.matrix=t.transform,r.textureSpace=t.textureSpace,fe(fe({},e),r)}function XE(r,t){const e=fe(fe({},t),r),i=J.shared.setValue(e.color);return e.alpha*=i.alpha,e.color=i.toNumber(),e}function ke(r,t){if(r==null)return null;const e={},i=r;return kE(r)?LE(e,r,t):$E(r)?NE(e,r,t):ig(r)?ng(e,r,t):sg(r)?ag(e,r,t):i.fill&&ig(i.fill)?ng(i,i.fill,t):i.fill&&sg(i.fill)?ag(i,i.fill,t):XE(i,t)}function Ai(r,t){const e=t,{width:i,alignment:s,miterLimit:n,cap:a,join:o,pixelLine:l}=e,u=UE(e,["width","alignment","miterLimit","cap","join","pixelLine"]),c=ke(r,u);return c?fe({width:i,alignment:s,miterLimit:n,cap:a,join:o,pixelLine:l},c):null}var HE=Object.defineProperty,og=Object.getOwnPropertySymbols,jE=Object.prototype.hasOwnProperty,zE=Object.prototype.propertyIsEnumerable,lg=(r,t,e)=>t in r?HE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Fr=(r,t)=>{for(var e in t||(t={}))jE.call(t,e)&&lg(r,e,t[e]);if(og)for(var e of og(t))zE.call(t,e)&&lg(r,e,t[e]);return r};const WE=new rt,ug=new D,ol=class Pe extends Ut{constructor(){super(...arguments),this._gpuData=Object.create(null),this.autoGarbageCollect=!0,this._gcLastUsed=-1,this.uid=nt("graphicsContext"),this.dirty=!0,this.batchMode="auto",this.instructions=[],this.destroyed=!1,this._activePath=new pe,this._transform=new D,this._fillStyle=Fr({},Pe.defaultFillStyle),this._strokeStyle=Fr({},Pe.defaultStrokeStyle),this._stateStack=[],this._tick=0,this._bounds=new Pt,this._boundsDirty=!0}clone(){const t=new Pe;return t.batchMode=this.batchMode,t.instructions=this.instructions.slice(),t._activePath=this._activePath.clone(),t._transform=this._transform.clone(),t._fillStyle=Fr({},this._fillStyle),t._strokeStyle=Fr({},this._strokeStyle),t._stateStack=this._stateStack.slice(),t._bounds=this._bounds.clone(),t._boundsDirty=!0,t}get fillStyle(){return this._fillStyle}set fillStyle(t){this._fillStyle=ke(t,Pe.defaultFillStyle)}get strokeStyle(){return this._strokeStyle}set strokeStyle(t){this._strokeStyle=Ai(t,Pe.defaultStrokeStyle)}setFillStyle(t){return this._fillStyle=ke(t,Pe.defaultFillStyle),this}setStrokeStyle(t){return this._strokeStyle=ke(t,Pe.defaultStrokeStyle),this}texture(t,e,i,s,n,a){return this.instructions.push({action:"texture",data:{image:t,dx:i||0,dy:s||0,dw:n||t.frame.width,dh:a||t.frame.height,transform:this._transform.clone(),alpha:this._fillStyle.alpha,style:e||e===0?J.shared.setValue(e).toNumber():16777215}}),this.onUpdate(),this}beginPath(){return this._activePath=new pe,this}fill(t,e){let i;const s=this.instructions[this.instructions.length-1];return this._tick===0&&(s==null?void 0:s.action)==="stroke"?i=s.data.path:i=this._activePath.clone(),i?(t!=null&&(e!==void 0&&typeof t=="number"&&(t={color:t,alpha:e}),this._fillStyle=ke(t,Pe.defaultFillStyle)),this.instructions.push({action:"fill",data:{style:this.fillStyle,path:i}}),this.onUpdate(),this._initNextPathLocation(),this._tick=0,this):this}_initNextPathLocation(){const{x:t,y:e}=this._activePath.getLastPoint(rt.shared);this._activePath.clear(),this._activePath.moveTo(t,e)}stroke(t){let e;const i=this.instructions[this.instructions.length-1];return this._tick===0&&(i==null?void 0:i.action)==="fill"?e=i.data.path:e=this._activePath.clone(),e?(t!=null&&(this._strokeStyle=Ai(t,Pe.defaultStrokeStyle)),this.instructions.push({action:"stroke",data:{style:this.strokeStyle,path:e}}),this.onUpdate(),this._initNextPathLocation(),this._tick=0,this):this}cut(){for(let t=0;t<2;t++){const e=this.instructions[this.instructions.length-1-t],i=this._activePath.clone();if(e&&(e.action==="stroke"||e.action==="fill"))if(e.data.hole)e.data.hole.addPath(i);else{e.data.hole=i;break}}return this._initNextPathLocation(),this}arc(t,e,i,s,n,a){this._tick++;const o=this._transform;return this._activePath.arc(o.a*t+o.c*e+o.tx,o.b*t+o.d*e+o.ty,i,s,n,a),this}arcTo(t,e,i,s,n){this._tick++;const a=this._transform;return this._activePath.arcTo(a.a*t+a.c*e+a.tx,a.b*t+a.d*e+a.ty,a.a*i+a.c*s+a.tx,a.b*i+a.d*s+a.ty,n),this}arcToSvg(t,e,i,s,n,a,o){this._tick++;const l=this._transform;return this._activePath.arcToSvg(t,e,i,s,n,l.a*a+l.c*o+l.tx,l.b*a+l.d*o+l.ty),this}bezierCurveTo(t,e,i,s,n,a,o){this._tick++;const l=this._transform;return this._activePath.bezierCurveTo(l.a*t+l.c*e+l.tx,l.b*t+l.d*e+l.ty,l.a*i+l.c*s+l.tx,l.b*i+l.d*s+l.ty,l.a*n+l.c*a+l.tx,l.b*n+l.d*a+l.ty,o),this}closePath(){var t;return this._tick++,(t=this._activePath)==null||t.closePath(),this}ellipse(t,e,i,s){return this._tick++,this._activePath.ellipse(t,e,i,s,this._transform.clone()),this}circle(t,e,i){return this._tick++,this._activePath.circle(t,e,i,this._transform.clone()),this}path(t){return this._tick++,this._activePath.addPath(t,this._transform.clone()),this}lineTo(t,e){this._tick++;const i=this._transform;return this._activePath.lineTo(i.a*t+i.c*e+i.tx,i.b*t+i.d*e+i.ty),this}moveTo(t,e){this._tick++;const i=this._transform,s=this._activePath.instructions,n=i.a*t+i.c*e+i.tx,a=i.b*t+i.d*e+i.ty;return s.length===1&&s[0].action==="moveTo"?(s[0].data[0]=n,s[0].data[1]=a,this):(this._activePath.moveTo(n,a),this)}quadraticCurveTo(t,e,i,s,n){this._tick++;const a=this._transform;return this._activePath.quadraticCurveTo(a.a*t+a.c*e+a.tx,a.b*t+a.d*e+a.ty,a.a*i+a.c*s+a.tx,a.b*i+a.d*s+a.ty,n),this}rect(t,e,i,s){return this._tick++,this._activePath.rect(t,e,i,s,this._transform.clone()),this}roundRect(t,e,i,s,n){return this._tick++,this._activePath.roundRect(t,e,i,s,n,this._transform.clone()),this}poly(t,e){return this._tick++,this._activePath.poly(t,e,this._transform.clone()),this}regularPoly(t,e,i,s,n=0,a){return this._tick++,this._activePath.regularPoly(t,e,i,s,n,a),this}roundPoly(t,e,i,s,n,a){return this._tick++,this._activePath.roundPoly(t,e,i,s,n,a),this}roundShape(t,e,i,s){return this._tick++,this._activePath.roundShape(t,e,i,s),this}filletRect(t,e,i,s,n){return this._tick++,this._activePath.filletRect(t,e,i,s,n),this}chamferRect(t,e,i,s,n,a){return this._tick++,this._activePath.chamferRect(t,e,i,s,n,a),this}star(t,e,i,s,n=0,a=0){return this._tick++,this._activePath.star(t,e,i,s,n,a,this._transform.clone()),this}svg(t){return this._tick++,Zm(t,this),this}restore(){const t=this._stateStack.pop();return t&&(this._transform=t.transform,this._fillStyle=t.fillStyle,this._strokeStyle=t.strokeStyle),this}save(){return this._stateStack.push({transform:this._transform.clone(),fillStyle:Fr({},this._fillStyle),strokeStyle:Fr({},this._strokeStyle)}),this}getTransform(){return this._transform}resetTransform(){return this._transform.identity(),this}rotate(t){return this._transform.rotate(t),this}scale(t,e=t){return this._transform.scale(t,e),this}setTransform(t,e,i,s,n,a){return t instanceof D?(this._transform.set(t.a,t.b,t.c,t.d,t.tx,t.ty),this):(this._transform.set(t,e,i,s,n,a),this)}transform(t,e,i,s,n,a){return t instanceof D?(this._transform.append(t),this):(ug.set(t,e,i,s,n,a),this._transform.append(ug),this)}translate(t,e=t){return this._transform.translate(t,e),this}clear(){return this._activePath.clear(),this.instructions.length=0,this.resetTransform(),this.onUpdate(),this}onUpdate(){this._boundsDirty=!0,!this.dirty&&(this.emit("update",this,16),this.dirty=!0)}get bounds(){if(!this._boundsDirty)return this._bounds;this._boundsDirty=!1;const t=this._bounds;t.clear();for(let e=0;e{delete t.promiseCache[e],tt.has(e)&&tt.remove(e)};return i.source.once("destroy",()=>{t.promiseCache[e]&&s()}),i.once("destroy",()=>{r.destroyed||s()}),i}var VE=Object.defineProperty,Hs=Object.getOwnPropertySymbols,cg=Object.prototype.hasOwnProperty,hg=Object.prototype.propertyIsEnumerable,dg=(r,t,e)=>t in r?VE(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,YE=(r,t)=>{for(var e in t||(t={}))cg.call(t,e)&&dg(r,e,t[e]);if(Hs)for(var e of Hs(t))hg.call(t,e)&&dg(r,e,t[e]);return r},KE=(r,t)=>{var e={};for(var i in r)cg.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Hs)for(var i of Hs(r))t.indexOf(i)<0&&hg.call(r,i)&&(e[i]=r[i]);return e};const qE=".svg",ZE="image/svg+xml",pg={extension:{type:T.LoadParser,priority:qt.Low,name:"loadSVG"},name:"loadSVG",id:"svg",config:{crossOrigin:"anonymous",parseAsGraphicsContext:!1},test(r){return tr(r,ZE)||ne(r,qE)},async load(r,t,e){var i,s;return((s=(i=t.data)==null?void 0:i.parseAsGraphicsContext)!=null?s:this.config.parseAsGraphicsContext)?JE(r):QE(r,t,e,this.config.crossOrigin)},unload(r){r.destroy(!0)}};async function QE(r,t,e,i){var s,n,a,o,l,u;const c=await L.get().fetch(r),h=L.get().createImage();h.src=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(await c.text())}`,h.crossOrigin=i,await h.decode();const p=(n=(s=t.data)==null?void 0:s.width)!=null?n:h.width,f=(o=(a=t.data)==null?void 0:a.height)!=null?o:h.height,m=((l=t.data)==null?void 0:l.resolution)||Xs(r),g=Math.ceil(p*m),_=Math.ceil(f*m),b=L.get().createCanvas(g,_),y=b.getContext("2d");y.imageSmoothingEnabled=!0,y.imageSmoothingQuality="high",y.drawImage(h,0,0,p*m,f*m);const x=(u=t.data)!=null?u:{},{parseAsGraphicsContext:v}=x,w=KE(x,["parseAsGraphicsContext"]),S=new De(YE({resource:b,alphaMode:"premultiply-alpha-on-upload",resolution:m},w));return $e(S,e,r)}async function JE(r){const t=await(await L.get().fetch(r)).text(),e=new $t;return e.svg(t),e}const tP=`(function(){"use strict";const e="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";async function a(){try{if(typeof createImageBitmap!="function")return!1;const A=await(await fetch(e)).blob(),t=await createImageBitmap(A);return t.width===1&&t.height===1}catch(A){return!1}}a().then(A=>{self.postMessage(A)})})(); +`;let Dr=null,ll=class{constructor(){Dr||(Dr=URL.createObjectURL(new Blob([tP],{type:"application/javascript"}))),this.worker=new Worker(Dr)}};ll.revokeObjectURL=function(){Dr&&(URL.revokeObjectURL(Dr),Dr=null)};const eP='(function(){"use strict";async function s(a,t){const e=await fetch(a);if(!e.ok)throw new Error(`[WorkerManager.loadImageBitmap] Failed to fetch ${a}: ${e.status} ${e.statusText}`);const i=await e.blob();return t==="premultiplied-alpha"?createImageBitmap(i,{premultiplyAlpha:"none"}):createImageBitmap(i)}self.onmessage=async a=>{try{const t=await s(a.data.data[0],a.data.data[1]);self.postMessage({data:t,uuid:a.data.uuid,id:a.data.id},[t])}catch(t){self.postMessage({error:t,uuid:a.data.uuid,id:a.data.id})}}})();\n';let Ur=null,fg=class{constructor(){Ur||(Ur=URL.createObjectURL(new Blob([eP],{type:"application/javascript"}))),this.worker=new Worker(Ur)}};fg.revokeObjectURL=function(){Ur&&(URL.revokeObjectURL(Ur),Ur=null)};let mg=0,ul,rP=class{constructor(){this._initialized=!1,this._createdWorkers=0,this._workerPool=[],this._queue=[],this._resolveHash={}}isImageBitmapSupported(){return this._isImageBitmapSupported!==void 0?this._isImageBitmapSupported:(this._isImageBitmapSupported=new Promise(t=>{const{worker:e}=new ll;e.addEventListener("message",i=>{e.terminate(),ll.revokeObjectURL(),t(i.data)})}),this._isImageBitmapSupported)}loadImageBitmap(t,e){var i;return this._run("loadImageBitmap",[t,(i=e==null?void 0:e.data)==null?void 0:i.alphaMode])}async _initWorkers(){this._initialized||(this._initialized=!0)}_getWorker(){ul===void 0&&(ul=navigator.hardwareConcurrency||4);let t=this._workerPool.pop();return!t&&this._createdWorkers{this._complete(e.data),this._returnWorker(e.target),this._next()})),t}_returnWorker(t){this._workerPool.push(t)}_complete(t){this._resolveHash[t.uuid]&&(t.error!==void 0?this._resolveHash[t.uuid].reject(t.error):this._resolveHash[t.uuid].resolve(t.data),delete this._resolveHash[t.uuid])}async _run(t,e){await this._initWorkers();const i=new Promise((s,n)=>{this._queue.push({id:t,arguments:e,resolve:s,reject:n})});return this._next(),i}_next(){if(!this._queue.length)return;const t=this._getWorker();if(!t)return;const e=this._queue.pop(),i=e.id;this._resolveHash[mg]={resolve:e.resolve,reject:e.reject},t.postMessage({data:e.arguments,uuid:mg++,id:i})}reset(){this._workerPool.forEach(t=>t.terminate()),this._workerPool.length=0,Object.values(this._resolveHash).forEach(({reject:t})=>{t==null||t(new Error("WorkerManager has been reset before completion"))}),this._resolveHash={},this._queue.length=0,this._initialized=!1,this._createdWorkers=0}};const cl=new rP;var iP=Object.defineProperty,gg=Object.getOwnPropertySymbols,sP=Object.prototype.hasOwnProperty,nP=Object.prototype.propertyIsEnumerable,_g=(r,t,e)=>t in r?iP(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,aP=(r,t)=>{for(var e in t||(t={}))sP.call(t,e)&&_g(r,e,t[e]);if(gg)for(var e of gg(t))nP.call(t,e)&&_g(r,e,t[e]);return r};const oP=[".jpeg",".jpg",".png",".webp",".avif"],lP=["image/jpeg","image/png","image/webp","image/avif"];async function bg(r,t){var e;const i=await L.get().fetch(r);if(!i.ok)throw new Error(`[loadImageBitmap] Failed to fetch ${r}: ${i.status} ${i.statusText}`);const s=await i.blob();return((e=t==null?void 0:t.data)==null?void 0:e.alphaMode)==="premultiplied-alpha"?createImageBitmap(s,{premultiplyAlpha:"none"}):createImageBitmap(s)}const hl={name:"loadTextures",id:"texture",extension:{type:T.LoadParser,priority:qt.High,name:"loadTextures"},config:{preferWorkers:!0,preferCreateImageBitmap:!0,crossOrigin:"anonymous"},test(r){return tr(r,lP)||ne(r,oP)},async load(r,t,e){var i;let s=null;globalThis.createImageBitmap&&this.config.preferCreateImageBitmap?this.config.preferWorkers&&await cl.isImageBitmapSupported()?s=await cl.loadImageBitmap(r,t):s=await bg(r,t):s=await new Promise((a,o)=>{s=L.get().createImage(),s.crossOrigin=this.config.crossOrigin,s.src=r,s.complete?a(s):(s.onload=()=>{a(s)},s.onerror=o)});const n=new De(aP({resource:s,alphaMode:"premultiply-alpha-on-upload",resolution:((i=t.data)==null?void 0:i.resolution)||Xs(r)},t.data));return $e(n,e,r)},unload(r){r.destroy(!0)}};var uP=Object.defineProperty,cP=Object.defineProperties,hP=Object.getOwnPropertyDescriptors,vg=Object.getOwnPropertySymbols,dP=Object.prototype.hasOwnProperty,pP=Object.prototype.propertyIsEnumerable,yg=(r,t,e)=>t in r?uP(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,dl=(r,t)=>{for(var e in t||(t={}))dP.call(t,e)&&yg(r,e,t[e]);if(vg)for(var e of vg(t))pP.call(t,e)&&yg(r,e,t[e]);return r},xg=(r,t)=>cP(r,hP(t));const fP=[".mp4",".m4v",".webm",".ogg",".ogv",".h264",".avi",".mov"];let pl,fl;function Tg(r,t,e){e===void 0&&!t.startsWith("data:")?r.crossOrigin=wg(t):e!==!1&&(r.crossOrigin=typeof e=="string"?e:"anonymous")}function Sg(r){return new Promise((t,e)=>{r.addEventListener("canplaythrough",i),r.addEventListener("error",s),r.load();function i(){n(),t()}function s(a){n(),e(a)}function n(){r.removeEventListener("canplaythrough",i),r.removeEventListener("error",s)}})}function wg(r,t=globalThis.location){if(r.startsWith("data:"))return"";t||(t=globalThis.location);const e=new URL(r,document.baseURI);return e.hostname!==t.hostname||e.port!==t.port||e.protocol!==t.protocol?"anonymous":""}function mP(){const r=[],t=[];for(const e of fP){const i=br.MIME_TYPES[e.substring(1)]||`video/${e.substring(1)}`;yi(i)&&(r.push(e),t.includes(i)||t.push(i))}return{validVideoExtensions:r,validVideoMime:t}}const Eg={name:"loadVideo",id:"video",extension:{type:T.LoadParser,name:"loadVideo"},test(r){if(!pl||!fl){const{validVideoExtensions:i,validVideoMime:s}=mP();pl=i,fl=s}const t=tr(r,fl),e=ne(r,pl);return t||e},async load(r,t,e){var i,s;const n=dl(xg(dl({},br.defaultOptions),{resolution:((i=t.data)==null?void 0:i.resolution)||Xs(r),alphaMode:((s=t.data)==null?void 0:s.alphaMode)||await Ha()}),t.data),a=document.createElement("video"),o={preload:n.autoLoad!==!1?"auto":void 0,"webkit-playsinline":n.playsinline!==!1?"":void 0,playsinline:n.playsinline!==!1?"":void 0,muted:n.muted===!0?"":void 0,loop:n.loop===!0?"":void 0,autoplay:n.autoPlay!==!1?"":void 0};Object.keys(o).forEach(c=>{const h=o[c];h!==void 0&&a.setAttribute(c,h)}),n.muted===!0&&(a.muted=!0),Tg(a,r,n.crossorigin);const l=document.createElement("source");let u;if(n.mime)u=n.mime;else if(r.startsWith("data:"))u=r.slice(5,r.indexOf(";"));else if(!r.startsWith("blob:")){const c=r.split("?")[0].slice(r.lastIndexOf(".")+1).toLowerCase();u=br.MIME_TYPES[c]||`video/${c}`}return l.src=r,u&&(l.type=u),new Promise((c,h)=>{n.preload&&!n.autoPlay&&a.load(),a.addEventListener("canplay",p),a.addEventListener("error",f),l.addEventListener("error",f),a.appendChild(l);async function p(){const g=new br(xg(dl({},n),{resource:a}));m(),t.data.preload&&await Sg(a),c($e(g,e,r))}function f(g){m(),h(g)}function m(){a.removeEventListener("canplay",p),a.removeEventListener("error",f),l.removeEventListener("error",f)}})},unload(r){r.destroy(!0)}},ml={extension:{type:T.ResolveParser,name:"resolveTexture"},test:hl.test,parse:r=>{var t,e;return{resolution:parseFloat((e=(t=Fe.RETINA_PREFIX.exec(r))==null?void 0:t[1])!=null?e:"1"),format:r.split(".").pop(),src:r}}},Pg={extension:{type:T.ResolveParser,priority:-2,name:"resolveJson"},test:r=>Fe.RETINA_PREFIX.test(r)&&r.endsWith(".json"),parse:ml.parse};var gP=Object.defineProperty,Ag=Object.getOwnPropertySymbols,_P=Object.prototype.hasOwnProperty,bP=Object.prototype.propertyIsEnumerable,Cg=(r,t,e)=>t in r?gP(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Rg=(r,t)=>{for(var e in t||(t={}))_P.call(t,e)&&Cg(r,e,t[e]);if(Ag)for(var e of Ag(t))bP.call(t,e)&&Cg(r,e,t[e]);return r};class Mg{constructor(){this._detections=[],this._initialized=!1,this.resolver=new Fe,this.loader=new Rf,this.cache=tt,this._backgroundLoader=new _f(this.loader),this._backgroundLoader.active=!0,this.reset()}async init(t={}){var e,i,s;if(this._initialized)return;if(this._initialized=!0,t.defaultSearchParams&&this.resolver.setDefaultSearchParams(t.defaultSearchParams),t.basePath&&(this.resolver.basePath=t.basePath),t.bundleIdentifier&&this.resolver.setBundleIdentifier(t.bundleIdentifier),t.manifest){let l=t.manifest;typeof l=="string"&&(l=await this.load(l)),this.resolver.addManifest(l)}const n=(i=(e=t.texturePreference)==null?void 0:e.resolution)!=null?i:1,a=typeof n=="number"?[n]:n,o=await this._detectFormats({preferredFormats:(s=t.texturePreference)==null?void 0:s.format,skipDetections:t.skipDetections,detections:this._detections});this.resolver.prefer({params:{format:o,resolution:a}}),t.preferences&&this.setPreferences(t.preferences),t.loadOptions&&(this.loader.loadOptions=Rg(Rg({},this.loader.loadOptions),t.loadOptions))}add(t){this.resolver.add(t)}async load(t,e){this._initialized||await this.init();const i=ai(t),s=se(t).map(o=>{if(typeof o!="string"){const l=this.resolver.getAlias(o);return l.some(u=>!this.resolver.hasKey(u))&&this.add(o),Array.isArray(l)?l[0]:l}return this.resolver.hasKey(o)||this.add({alias:o,src:o}),o}),n=this.resolver.resolve(s),a=await this._mapLoadToResolve(n,e);return i?a[s[0]]:a}addBundle(t,e){this.resolver.addBundle(t,e)}async loadBundle(t,e){this._initialized||await this.init();let i=!1;typeof t=="string"&&(i=!0,t=[t]);const s=this.resolver.resolveBundle(t),n={},a=Object.keys(s);let o=0;const l=[],u=()=>{e==null||e(l.reduce((h,p)=>h+p,0)/o)},c=a.map((h,p)=>{const f=s[h],m=Object.values(f),g=[...new Set(m.flat())].reduce((_,b)=>_+(b.progressSize||1),0);return l.push(0),o+=g,this._mapLoadToResolve(f,_=>{l[p]=_*g,u()}).then(_=>{n[h]=_})});return await Promise.all(c),i?n[t[0]]:n}async backgroundLoad(t){this._initialized||await this.init(),typeof t=="string"&&(t=[t]);const e=this.resolver.resolve(t);this._backgroundLoader.add(Object.values(e))}async backgroundLoadBundle(t){this._initialized||await this.init(),typeof t=="string"&&(t=[t]);const e=this.resolver.resolveBundle(t);Object.values(e).forEach(i=>{this._backgroundLoader.add(Object.values(i))})}reset(){this.resolver.reset(),this.loader.reset(),this.cache.reset(),this._initialized=!1}get(t){if(typeof t=="string")return tt.get(t);const e={};for(let i=0;i{const o=s[a.src],l=[a.src];a.alias&&l.push(...a.alias),l.forEach(u=>{n[u]=o}),tt.set(l,o)}),n}async unload(t){this._initialized||await this.init();const e=se(t).map(s=>typeof s!="string"?s.src:s),i=this.resolver.resolve(e);await this._unloadFromResolved(i)}async unloadBundle(t){this._initialized||await this.init(),t=se(t);const e=this.resolver.resolveBundle(t),i=Object.keys(e).map(s=>this._unloadFromResolved(e[s]));await Promise.all(i)}async _unloadFromResolved(t){const e=Object.values(t);e.forEach(i=>{tt.remove(i.src)}),await this.loader.unload(e)}async _detectFormats(t){let e=[];t.preferredFormats&&(e=Array.isArray(t.preferredFormats)?t.preferredFormats:[t.preferredFormats]);for(const i of t.detections)t.skipDetections||await i.test()?e=await i.add(e):t.skipDetections||(e=await i.remove(e));return e=e.filter((i,s)=>e.indexOf(i)===s),e}get detections(){return this._detections}setPreferences(t){this.loader.parsers.forEach(e=>{e.config&&Object.keys(e.config).filter(i=>i in t).forEach(i=>{e.config[i]=t[i]})})}}const Ci=new Mg;$.handleByList(T.LoadParser,Ci.loader.parsers).handleByList(T.ResolveParser,Ci.resolver.parsers).handleByList(T.CacheParser,Ci.cache.parsers).handleByList(T.DetectionParser,Ci.detections),$.add(bf,xf,vf,Ef,Tf,Sf,wf,Mf,Of,Ff,pg,hl,Eg,gf,mf,ml,Pg);const Og={loader:T.LoadParser,resolver:T.ResolveParser,cache:T.CacheParser,detection:T.DetectionParser};$.handle(T.Asset,r=>{const t=r.ref;Object.entries(Og).filter(([e])=>!!t[e]).forEach(([e,i])=>{var s;return $.add(Object.assign(t[e],{extension:(s=t[e].extension)!=null?s:i}))})},r=>{const t=r.ref;Object.keys(Og).filter(e=>!!t[e]).forEach(e=>$.remove(t[e]))});const vP={extension:{type:T.DetectionParser,priority:3},test:async()=>!!(await bi()||_i()),add:async r=>[...r,"basis"],remove:async r=>r.filter(t=>t!=="basis")};var yP=Object.defineProperty,xP=Object.defineProperties,TP=Object.getOwnPropertyDescriptors,Gg=Object.getOwnPropertySymbols,SP=Object.prototype.hasOwnProperty,wP=Object.prototype.propertyIsEnumerable,Ig=(r,t,e)=>t in r?yP(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,EP=(r,t)=>{for(var e in t||(t={}))SP.call(t,e)&&Ig(r,e,t[e]);if(Gg)for(var e of Gg(t))wP.call(t,e)&&Ig(r,e,t[e]);return r},PP=(r,t)=>xP(r,TP(t));class Ri extends ht{constructor(t){super(PP(EP({},t),{mipLevelCount:t.resource.length})),this.uploadMethodId="compressed"}}let js;function gl(){if(js)return js;const r=L.get().createCanvas(1,1).getContext("webgl");return r?(js=[...r.getExtension("EXT_texture_compression_bptc")?["bc6h-rgb-ufloat","bc6h-rgb-float","bc7-rgba-unorm","bc7-rgba-unorm-srgb"]:[],...r.getExtension("WEBGL_compressed_texture_s3tc")?["bc1-rgba-unorm","bc2-rgba-unorm","bc3-rgba-unorm"]:[],...r.getExtension("WEBGL_compressed_texture_s3tc_srgb")?["bc1-rgba-unorm-srgb","bc2-rgba-unorm-srgb","bc3-rgba-unorm-srgb"]:[],...r.getExtension("EXT_texture_compression_rgtc")?["bc4-r-unorm","bc4-r-snorm","bc5-rg-unorm","bc5-rg-snorm"]:[],...r.getExtension("WEBGL_compressed_texture_etc")?["etc2-rgb8unorm","etc2-rgb8unorm-srgb","etc2-rgba8unorm","etc2-rgba8unorm-srgb","etc2-rgb8a1unorm","etc2-rgb8a1unorm-srgb","eac-r11unorm","eac-rg11unorm"]:[],...r.getExtension("WEBGL_compressed_texture_astc")?["astc-4x4-unorm","astc-4x4-unorm-srgb","astc-5x4-unorm","astc-5x4-unorm-srgb","astc-5x5-unorm","astc-5x5-unorm-srgb","astc-6x5-unorm","astc-6x5-unorm-srgb","astc-6x6-unorm","astc-6x6-unorm-srgb","astc-8x5-unorm","astc-8x5-unorm-srgb","astc-8x6-unorm","astc-8x6-unorm-srgb","astc-8x8-unorm","astc-8x8-unorm-srgb","astc-10x5-unorm","astc-10x5-unorm-srgb","astc-10x6-unorm","astc-10x6-unorm-srgb","astc-10x8-unorm","astc-10x8-unorm-srgb","astc-10x10-unorm","astc-10x10-unorm-srgb","astc-12x10-unorm","astc-12x10-unorm-srgb","astc-12x12-unorm","astc-12x12-unorm-srgb"]:[]],js):[]}let zs;async function _l(){if(zs)return zs;const r=await L.get().getNavigator().gpu.requestAdapter();return zs=[...r.features.has("texture-compression-bc")?["bc1-rgba-unorm","bc1-rgba-unorm-srgb","bc2-rgba-unorm","bc2-rgba-unorm-srgb","bc3-rgba-unorm","bc3-rgba-unorm-srgb","bc4-r-unorm","bc4-r-snorm","bc5-rg-unorm","bc5-rg-snorm","bc6h-rgb-ufloat","bc6h-rgb-float","bc7-rgba-unorm","bc7-rgba-unorm-srgb"]:[],...r.features.has("texture-compression-etc2")?["etc2-rgb8unorm","etc2-rgb8unorm-srgb","etc2-rgb8a1unorm","etc2-rgb8a1unorm-srgb","etc2-rgba8unorm","etc2-rgba8unorm-srgb","eac-r11unorm","eac-r11snorm","eac-rg11unorm","eac-rg11snorm"]:[],...r.features.has("texture-compression-astc")?["astc-4x4-unorm","astc-4x4-unorm-srgb","astc-5x4-unorm","astc-5x4-unorm-srgb","astc-5x5-unorm","astc-5x5-unorm-srgb","astc-6x5-unorm","astc-6x5-unorm-srgb","astc-6x6-unorm","astc-6x6-unorm-srgb","astc-8x5-unorm","astc-8x5-unorm-srgb","astc-8x6-unorm","astc-8x6-unorm-srgb","astc-8x8-unorm","astc-8x8-unorm-srgb","astc-10x5-unorm","astc-10x5-unorm-srgb","astc-10x6-unorm","astc-10x6-unorm-srgb","astc-10x8-unorm","astc-10x8-unorm-srgb","astc-10x10-unorm","astc-10x10-unorm-srgb","astc-12x10-unorm","astc-12x10-unorm-srgb","astc-12x12-unorm","astc-12x12-unorm-srgb"]:[]],zs}let bl;async function vl(){return bl!==void 0||(bl=await(async()=>{const r=await bi(),t=_i();if(r&&t){const e=await _l(),i=gl();return e.filter(s=>i.includes(s))}else{if(r)return await _l();if(t)return gl()}return[]})()),bl}const Bg=["r8unorm","r8snorm","r8uint","r8sint","r16uint","r16sint","r16float","rg8unorm","rg8snorm","rg8uint","rg8sint","r32uint","r32sint","r32float","rg16uint","rg16sint","rg16float","rgba8unorm","rgba8unorm-srgb","rgba8snorm","rgba8uint","rgba8sint","bgra8unorm","bgra8unorm-srgb","rgb9e5ufloat","rgb10a2unorm","rg11b10ufloat","rg32uint","rg32sint","rg32float","rgba16uint","rgba16sint","rgba16float","rgba32uint","rgba32sint","rgba32float","stencil8","depth16unorm","depth24plus","depth24plus-stencil8","depth32float","depth32float-stencil8"];let Ws;async function Mi(){if(Ws!==void 0)return Ws;const r=await vl();return Ws=[...Bg,...r],Ws}const AP='(function(){"use strict";function g(r,a){const t=r.getNumImages(),s=r.getNumLevels(0);if(!r.startTranscoding())throw new Error("startTranscoding failed");const m=[];for(let e=0;e{BASIS({locateFile:s=>a}).then(s=>{s.initializeBasis(),t(s.BasisFile)})})}return c}async function b(r,a){const t=await fetch(r);if(t.ok){const s=await t.arrayBuffer();return new a(new Uint8Array(s))}throw new Error(`Failed to load Basis texture: ${r}`)}const h=["bc7-rgba-unorm","astc-4x4-unorm","etc2-rgba8unorm","bc3-rgba-unorm","rgba8unorm"];async function p(r){const a=await l(),t=await b(r,a),s=g(t,u);return{width:t.getImageWidth(0,0),height:t.getImageHeight(0,0),format:i,resource:s,alphaMode:"no-premultiply-alpha"}}async function y(r,a,t){r&&(n.jsUrl=r),a&&(n.wasmUrl=a),i=h.filter(s=>t.includes(s))[0],u=d(i),await l()}const U={init:async r=>{const{jsUrl:a,wasmUrl:t,supportedTextures:s}=r;await y(a,t,s)},load:async r=>{var a;try{const t=await p(r.url);return{type:"load",url:r.url,success:!0,textureOptions:t,transferables:(a=t.resource)==null?void 0:a.map(s=>s.buffer)}}catch(t){throw t}}};self.onmessage=(async r=>{const a=r.data,t=await U[a.type](a);t&&self.postMessage(t,t.transferables)})})();\n';let kr=null,Fg=class{constructor(){kr||(kr=URL.createObjectURL(new Blob([AP],{type:"application/javascript"}))),this.worker=new Worker(kr)}};Fg.revokeObjectURL=function(){kr&&(URL.revokeObjectURL(kr),kr=null)};const Vs={jsUrl:"https://cdn.jsdelivr.net/npm/pixi.js/transcoders/basis/basis_transcoder.js",wasmUrl:"https://cdn.jsdelivr.net/npm/pixi.js/transcoders/basis/basis_transcoder.wasm"};function CP(r){Object.assign(Vs,r)}let Oi;const Dg={};function RP(r){return Oi||(Oi=new Fg().worker,Oi.onmessage=t=>{const{success:e,url:i,textureOptions:s}=t.data;e||console.warn("Failed to load Basis texture",i),Dg[i](s)},Oi.postMessage({type:"init",jsUrl:Vs.jsUrl,wasmUrl:Vs.wasmUrl,supportedTextures:r})),Oi}function Ug(r,t){const e=RP(t);return new Promise(i=>{Dg[r]=i,e.postMessage({type:"load",url:r})})}const MP={extension:{type:T.LoadParser,priority:qt.High,name:"loadBasis"},name:"loadBasis",id:"basis",test(r){return ne(r,[".basis"])},async load(r,t,e){const i=await Mi(),s=await Ug(r,i),n=new Ri(s);return $e(n,e,r)},unload(r){Array.isArray(r)?r.forEach(t=>t.destroy(!0)):r.destroy(!0)}};function OP(r,t){const e=r.getNumImages(),i=r.getNumLevels(0);if(!r.startTranscoding())throw new Error("startTranscoding failed");const s=[];for(let n=0;n(r[r.DXGI_FORMAT_UNKNOWN=0]="DXGI_FORMAT_UNKNOWN",r[r.DXGI_FORMAT_R32G32B32A32_TYPELESS=1]="DXGI_FORMAT_R32G32B32A32_TYPELESS",r[r.DXGI_FORMAT_R32G32B32A32_FLOAT=2]="DXGI_FORMAT_R32G32B32A32_FLOAT",r[r.DXGI_FORMAT_R32G32B32A32_UINT=3]="DXGI_FORMAT_R32G32B32A32_UINT",r[r.DXGI_FORMAT_R32G32B32A32_SINT=4]="DXGI_FORMAT_R32G32B32A32_SINT",r[r.DXGI_FORMAT_R32G32B32_TYPELESS=5]="DXGI_FORMAT_R32G32B32_TYPELESS",r[r.DXGI_FORMAT_R32G32B32_FLOAT=6]="DXGI_FORMAT_R32G32B32_FLOAT",r[r.DXGI_FORMAT_R32G32B32_UINT=7]="DXGI_FORMAT_R32G32B32_UINT",r[r.DXGI_FORMAT_R32G32B32_SINT=8]="DXGI_FORMAT_R32G32B32_SINT",r[r.DXGI_FORMAT_R16G16B16A16_TYPELESS=9]="DXGI_FORMAT_R16G16B16A16_TYPELESS",r[r.DXGI_FORMAT_R16G16B16A16_FLOAT=10]="DXGI_FORMAT_R16G16B16A16_FLOAT",r[r.DXGI_FORMAT_R16G16B16A16_UNORM=11]="DXGI_FORMAT_R16G16B16A16_UNORM",r[r.DXGI_FORMAT_R16G16B16A16_UINT=12]="DXGI_FORMAT_R16G16B16A16_UINT",r[r.DXGI_FORMAT_R16G16B16A16_SNORM=13]="DXGI_FORMAT_R16G16B16A16_SNORM",r[r.DXGI_FORMAT_R16G16B16A16_SINT=14]="DXGI_FORMAT_R16G16B16A16_SINT",r[r.DXGI_FORMAT_R32G32_TYPELESS=15]="DXGI_FORMAT_R32G32_TYPELESS",r[r.DXGI_FORMAT_R32G32_FLOAT=16]="DXGI_FORMAT_R32G32_FLOAT",r[r.DXGI_FORMAT_R32G32_UINT=17]="DXGI_FORMAT_R32G32_UINT",r[r.DXGI_FORMAT_R32G32_SINT=18]="DXGI_FORMAT_R32G32_SINT",r[r.DXGI_FORMAT_R32G8X24_TYPELESS=19]="DXGI_FORMAT_R32G8X24_TYPELESS",r[r.DXGI_FORMAT_D32_FLOAT_S8X24_UINT=20]="DXGI_FORMAT_D32_FLOAT_S8X24_UINT",r[r.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS=21]="DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS",r[r.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT=22]="DXGI_FORMAT_X32_TYPELESS_G8X24_UINT",r[r.DXGI_FORMAT_R10G10B10A2_TYPELESS=23]="DXGI_FORMAT_R10G10B10A2_TYPELESS",r[r.DXGI_FORMAT_R10G10B10A2_UNORM=24]="DXGI_FORMAT_R10G10B10A2_UNORM",r[r.DXGI_FORMAT_R10G10B10A2_UINT=25]="DXGI_FORMAT_R10G10B10A2_UINT",r[r.DXGI_FORMAT_R11G11B10_FLOAT=26]="DXGI_FORMAT_R11G11B10_FLOAT",r[r.DXGI_FORMAT_R8G8B8A8_TYPELESS=27]="DXGI_FORMAT_R8G8B8A8_TYPELESS",r[r.DXGI_FORMAT_R8G8B8A8_UNORM=28]="DXGI_FORMAT_R8G8B8A8_UNORM",r[r.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB=29]="DXGI_FORMAT_R8G8B8A8_UNORM_SRGB",r[r.DXGI_FORMAT_R8G8B8A8_UINT=30]="DXGI_FORMAT_R8G8B8A8_UINT",r[r.DXGI_FORMAT_R8G8B8A8_SNORM=31]="DXGI_FORMAT_R8G8B8A8_SNORM",r[r.DXGI_FORMAT_R8G8B8A8_SINT=32]="DXGI_FORMAT_R8G8B8A8_SINT",r[r.DXGI_FORMAT_R16G16_TYPELESS=33]="DXGI_FORMAT_R16G16_TYPELESS",r[r.DXGI_FORMAT_R16G16_FLOAT=34]="DXGI_FORMAT_R16G16_FLOAT",r[r.DXGI_FORMAT_R16G16_UNORM=35]="DXGI_FORMAT_R16G16_UNORM",r[r.DXGI_FORMAT_R16G16_UINT=36]="DXGI_FORMAT_R16G16_UINT",r[r.DXGI_FORMAT_R16G16_SNORM=37]="DXGI_FORMAT_R16G16_SNORM",r[r.DXGI_FORMAT_R16G16_SINT=38]="DXGI_FORMAT_R16G16_SINT",r[r.DXGI_FORMAT_R32_TYPELESS=39]="DXGI_FORMAT_R32_TYPELESS",r[r.DXGI_FORMAT_D32_FLOAT=40]="DXGI_FORMAT_D32_FLOAT",r[r.DXGI_FORMAT_R32_FLOAT=41]="DXGI_FORMAT_R32_FLOAT",r[r.DXGI_FORMAT_R32_UINT=42]="DXGI_FORMAT_R32_UINT",r[r.DXGI_FORMAT_R32_SINT=43]="DXGI_FORMAT_R32_SINT",r[r.DXGI_FORMAT_R24G8_TYPELESS=44]="DXGI_FORMAT_R24G8_TYPELESS",r[r.DXGI_FORMAT_D24_UNORM_S8_UINT=45]="DXGI_FORMAT_D24_UNORM_S8_UINT",r[r.DXGI_FORMAT_R24_UNORM_X8_TYPELESS=46]="DXGI_FORMAT_R24_UNORM_X8_TYPELESS",r[r.DXGI_FORMAT_X24_TYPELESS_G8_UINT=47]="DXGI_FORMAT_X24_TYPELESS_G8_UINT",r[r.DXGI_FORMAT_R8G8_TYPELESS=48]="DXGI_FORMAT_R8G8_TYPELESS",r[r.DXGI_FORMAT_R8G8_UNORM=49]="DXGI_FORMAT_R8G8_UNORM",r[r.DXGI_FORMAT_R8G8_UINT=50]="DXGI_FORMAT_R8G8_UINT",r[r.DXGI_FORMAT_R8G8_SNORM=51]="DXGI_FORMAT_R8G8_SNORM",r[r.DXGI_FORMAT_R8G8_SINT=52]="DXGI_FORMAT_R8G8_SINT",r[r.DXGI_FORMAT_R16_TYPELESS=53]="DXGI_FORMAT_R16_TYPELESS",r[r.DXGI_FORMAT_R16_FLOAT=54]="DXGI_FORMAT_R16_FLOAT",r[r.DXGI_FORMAT_D16_UNORM=55]="DXGI_FORMAT_D16_UNORM",r[r.DXGI_FORMAT_R16_UNORM=56]="DXGI_FORMAT_R16_UNORM",r[r.DXGI_FORMAT_R16_UINT=57]="DXGI_FORMAT_R16_UINT",r[r.DXGI_FORMAT_R16_SNORM=58]="DXGI_FORMAT_R16_SNORM",r[r.DXGI_FORMAT_R16_SINT=59]="DXGI_FORMAT_R16_SINT",r[r.DXGI_FORMAT_R8_TYPELESS=60]="DXGI_FORMAT_R8_TYPELESS",r[r.DXGI_FORMAT_R8_UNORM=61]="DXGI_FORMAT_R8_UNORM",r[r.DXGI_FORMAT_R8_UINT=62]="DXGI_FORMAT_R8_UINT",r[r.DXGI_FORMAT_R8_SNORM=63]="DXGI_FORMAT_R8_SNORM",r[r.DXGI_FORMAT_R8_SINT=64]="DXGI_FORMAT_R8_SINT",r[r.DXGI_FORMAT_A8_UNORM=65]="DXGI_FORMAT_A8_UNORM",r[r.DXGI_FORMAT_R1_UNORM=66]="DXGI_FORMAT_R1_UNORM",r[r.DXGI_FORMAT_R9G9B9E5_SHAREDEXP=67]="DXGI_FORMAT_R9G9B9E5_SHAREDEXP",r[r.DXGI_FORMAT_R8G8_B8G8_UNORM=68]="DXGI_FORMAT_R8G8_B8G8_UNORM",r[r.DXGI_FORMAT_G8R8_G8B8_UNORM=69]="DXGI_FORMAT_G8R8_G8B8_UNORM",r[r.DXGI_FORMAT_BC1_TYPELESS=70]="DXGI_FORMAT_BC1_TYPELESS",r[r.DXGI_FORMAT_BC1_UNORM=71]="DXGI_FORMAT_BC1_UNORM",r[r.DXGI_FORMAT_BC1_UNORM_SRGB=72]="DXGI_FORMAT_BC1_UNORM_SRGB",r[r.DXGI_FORMAT_BC2_TYPELESS=73]="DXGI_FORMAT_BC2_TYPELESS",r[r.DXGI_FORMAT_BC2_UNORM=74]="DXGI_FORMAT_BC2_UNORM",r[r.DXGI_FORMAT_BC2_UNORM_SRGB=75]="DXGI_FORMAT_BC2_UNORM_SRGB",r[r.DXGI_FORMAT_BC3_TYPELESS=76]="DXGI_FORMAT_BC3_TYPELESS",r[r.DXGI_FORMAT_BC3_UNORM=77]="DXGI_FORMAT_BC3_UNORM",r[r.DXGI_FORMAT_BC3_UNORM_SRGB=78]="DXGI_FORMAT_BC3_UNORM_SRGB",r[r.DXGI_FORMAT_BC4_TYPELESS=79]="DXGI_FORMAT_BC4_TYPELESS",r[r.DXGI_FORMAT_BC4_UNORM=80]="DXGI_FORMAT_BC4_UNORM",r[r.DXGI_FORMAT_BC4_SNORM=81]="DXGI_FORMAT_BC4_SNORM",r[r.DXGI_FORMAT_BC5_TYPELESS=82]="DXGI_FORMAT_BC5_TYPELESS",r[r.DXGI_FORMAT_BC5_UNORM=83]="DXGI_FORMAT_BC5_UNORM",r[r.DXGI_FORMAT_BC5_SNORM=84]="DXGI_FORMAT_BC5_SNORM",r[r.DXGI_FORMAT_B5G6R5_UNORM=85]="DXGI_FORMAT_B5G6R5_UNORM",r[r.DXGI_FORMAT_B5G5R5A1_UNORM=86]="DXGI_FORMAT_B5G5R5A1_UNORM",r[r.DXGI_FORMAT_B8G8R8A8_UNORM=87]="DXGI_FORMAT_B8G8R8A8_UNORM",r[r.DXGI_FORMAT_B8G8R8X8_UNORM=88]="DXGI_FORMAT_B8G8R8X8_UNORM",r[r.DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM=89]="DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM",r[r.DXGI_FORMAT_B8G8R8A8_TYPELESS=90]="DXGI_FORMAT_B8G8R8A8_TYPELESS",r[r.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB=91]="DXGI_FORMAT_B8G8R8A8_UNORM_SRGB",r[r.DXGI_FORMAT_B8G8R8X8_TYPELESS=92]="DXGI_FORMAT_B8G8R8X8_TYPELESS",r[r.DXGI_FORMAT_B8G8R8X8_UNORM_SRGB=93]="DXGI_FORMAT_B8G8R8X8_UNORM_SRGB",r[r.DXGI_FORMAT_BC6H_TYPELESS=94]="DXGI_FORMAT_BC6H_TYPELESS",r[r.DXGI_FORMAT_BC6H_UF16=95]="DXGI_FORMAT_BC6H_UF16",r[r.DXGI_FORMAT_BC6H_SF16=96]="DXGI_FORMAT_BC6H_SF16",r[r.DXGI_FORMAT_BC7_TYPELESS=97]="DXGI_FORMAT_BC7_TYPELESS",r[r.DXGI_FORMAT_BC7_UNORM=98]="DXGI_FORMAT_BC7_UNORM",r[r.DXGI_FORMAT_BC7_UNORM_SRGB=99]="DXGI_FORMAT_BC7_UNORM_SRGB",r[r.DXGI_FORMAT_AYUV=100]="DXGI_FORMAT_AYUV",r[r.DXGI_FORMAT_Y410=101]="DXGI_FORMAT_Y410",r[r.DXGI_FORMAT_Y416=102]="DXGI_FORMAT_Y416",r[r.DXGI_FORMAT_NV12=103]="DXGI_FORMAT_NV12",r[r.DXGI_FORMAT_P010=104]="DXGI_FORMAT_P010",r[r.DXGI_FORMAT_P016=105]="DXGI_FORMAT_P016",r[r.DXGI_FORMAT_420_OPAQUE=106]="DXGI_FORMAT_420_OPAQUE",r[r.DXGI_FORMAT_YUY2=107]="DXGI_FORMAT_YUY2",r[r.DXGI_FORMAT_Y210=108]="DXGI_FORMAT_Y210",r[r.DXGI_FORMAT_Y216=109]="DXGI_FORMAT_Y216",r[r.DXGI_FORMAT_NV11=110]="DXGI_FORMAT_NV11",r[r.DXGI_FORMAT_AI44=111]="DXGI_FORMAT_AI44",r[r.DXGI_FORMAT_IA44=112]="DXGI_FORMAT_IA44",r[r.DXGI_FORMAT_P8=113]="DXGI_FORMAT_P8",r[r.DXGI_FORMAT_A8P8=114]="DXGI_FORMAT_A8P8",r[r.DXGI_FORMAT_B4G4R4A4_UNORM=115]="DXGI_FORMAT_B4G4R4A4_UNORM",r[r.DXGI_FORMAT_P208=116]="DXGI_FORMAT_P208",r[r.DXGI_FORMAT_V208=117]="DXGI_FORMAT_V208",r[r.DXGI_FORMAT_V408=118]="DXGI_FORMAT_V408",r[r.DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE=119]="DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE",r[r.DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE=120]="DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE",r[r.DXGI_FORMAT_FORCE_UINT=121]="DXGI_FORMAT_FORCE_UINT",r))(yl||{}),xl=(r=>(r[r.DDS_DIMENSION_TEXTURE1D=2]="DDS_DIMENSION_TEXTURE1D",r[r.DDS_DIMENSION_TEXTURE2D=3]="DDS_DIMENSION_TEXTURE2D",r[r.DDS_DIMENSION_TEXTURE3D=6]="DDS_DIMENSION_TEXTURE3D",r))(xl||{});function Mt(r){return r.charCodeAt(0)+(r.charCodeAt(1)<<8)+(r.charCodeAt(2)<<16)+(r.charCodeAt(3)<<24)}var Vt=(r=>(r[r.UNKNOWN=0]="UNKNOWN",r[r.R8G8B8=20]="R8G8B8",r[r.A8R8G8B8=21]="A8R8G8B8",r[r.X8R8G8B8=22]="X8R8G8B8",r[r.R5G6B5=23]="R5G6B5",r[r.X1R5G5B5=24]="X1R5G5B5",r[r.A1R5G5B5=25]="A1R5G5B5",r[r.A4R4G4B4=26]="A4R4G4B4",r[r.R3G3B2=27]="R3G3B2",r[r.A8=28]="A8",r[r.A8R3G3B2=29]="A8R3G3B2",r[r.X4R4G4B4=30]="X4R4G4B4",r[r.A2B10G10R10=31]="A2B10G10R10",r[r.A8B8G8R8=32]="A8B8G8R8",r[r.X8B8G8R8=33]="X8B8G8R8",r[r.G16R16=34]="G16R16",r[r.A2R10G10B10=35]="A2R10G10B10",r[r.A16B16G16R16=36]="A16B16G16R16",r[r.A8P8=40]="A8P8",r[r.P8=41]="P8",r[r.L8=50]="L8",r[r.A8L8=51]="A8L8",r[r.A4L4=52]="A4L4",r[r.V8U8=60]="V8U8",r[r.L6V5U5=61]="L6V5U5",r[r.X8L8V8U8=62]="X8L8V8U8",r[r.Q8W8V8U8=63]="Q8W8V8U8",r[r.V16U16=64]="V16U16",r[r.A2W10V10U10=67]="A2W10V10U10",r[r.Q16W16V16U16=110]="Q16W16V16U16",r[r.R16F=111]="R16F",r[r.G16R16F=112]="G16R16F",r[r.A16B16G16R16F=113]="A16B16G16R16F",r[r.R32F=114]="R32F",r[r.G32R32F=115]="G32R32F",r[r.A32B32G32R32F=116]="A32B32G32R32F",r[r.UYVY=Mt("UYVY")]="UYVY",r[r.R8G8_B8G8=Mt("RGBG")]="R8G8_B8G8",r[r.YUY2=Mt("YUY2")]="YUY2",r[r.D3DFMT_G8R8_G8B8=Mt("GRGB")]="D3DFMT_G8R8_G8B8",r[r.DXT1=Mt("DXT1")]="DXT1",r[r.DXT2=Mt("DXT2")]="DXT2",r[r.DXT3=Mt("DXT3")]="DXT3",r[r.DXT4=Mt("DXT4")]="DXT4",r[r.DXT5=Mt("DXT5")]="DXT5",r[r.ATI1=Mt("ATI1")]="ATI1",r[r.AT1N=Mt("AT1N")]="AT1N",r[r.ATI2=Mt("ATI2")]="ATI2",r[r.AT2N=Mt("AT2N")]="AT2N",r[r.BC4U=Mt("BC4U")]="BC4U",r[r.BC4S=Mt("BC4S")]="BC4S",r[r.BC5U=Mt("BC5U")]="BC5U",r[r.BC5S=Mt("BC5S")]="BC5S",r[r.DX10=Mt("DX10")]="DX10",r))(Vt||{});const Tl={[Vt.DXT1]:"bc1-rgba-unorm",[Vt.DXT2]:"bc2-rgba-unorm",[Vt.DXT3]:"bc2-rgba-unorm",[Vt.DXT4]:"bc3-rgba-unorm",[Vt.DXT5]:"bc3-rgba-unorm",[Vt.ATI1]:"bc4-r-unorm",[Vt.BC4U]:"bc4-r-unorm",[Vt.BC4S]:"bc4-r-snorm",[Vt.ATI2]:"bc5-rg-unorm",[Vt.BC5U]:"bc5-rg-unorm",[Vt.BC5S]:"bc5-rg-snorm",36:"rgba16uint",110:"rgba16sint",111:"r16float",112:"rg16float",113:"rgba16float",114:"r32float",115:"rg32float",116:"rgba32float"},Yt={70:"bc1-rgba-unorm",71:"bc1-rgba-unorm",72:"bc1-rgba-unorm-srgb",73:"bc2-rgba-unorm",74:"bc2-rgba-unorm",75:"bc2-rgba-unorm-srgb",76:"bc3-rgba-unorm",77:"bc3-rgba-unorm",78:"bc3-rgba-unorm-srgb",79:"bc4-r-unorm",80:"bc4-r-unorm",81:"bc4-r-snorm",82:"bc5-rg-unorm",83:"bc5-rg-unorm",84:"bc5-rg-snorm",94:"bc6h-rgb-ufloat",95:"bc6h-rgb-ufloat",96:"bc6h-rgb-float",97:"bc7-rgba-unorm",98:"bc7-rgba-unorm",99:"bc7-rgba-unorm-srgb",28:"rgba8unorm",29:"rgba8unorm-srgb",87:"bgra8unorm",91:"bgra8unorm-srgb",41:"r32float",49:"rg8unorm",56:"r16uint",61:"r8unorm",24:"rgb10a2unorm",11:"rgba16uint",13:"rgba16sint",10:"rgba16float",54:"r16float",34:"rg16float",16:"rg32float",2:"rgba32float"},Y={MAGIC_VALUE:542327876,MAGIC_SIZE:4,HEADER_SIZE:124,HEADER_DX10_SIZE:20,PIXEL_FORMAT_FLAGS:{ALPHAPIXELS:1,ALPHA:2,FOURCC:4,RGB:64,RGBA:65,YUV:512,LUMINANCE:131072,LUMINANCEA:131073},RESOURCE_MISC_TEXTURECUBE:4,HEADER_FIELDS:BP,HEADER_DX10_FIELDS:FP,DXGI_FORMAT:yl,D3D10_RESOURCE_DIMENSION:xl,D3DFMT:Vt},kg={"bc1-rgba-unorm":8,"bc1-rgba-unorm-srgb":8,"bc2-rgba-unorm":16,"bc2-rgba-unorm-srgb":16,"bc3-rgba-unorm":16,"bc3-rgba-unorm-srgb":16,"bc4-r-unorm":8,"bc4-r-snorm":8,"bc5-rg-unorm":16,"bc5-rg-snorm":16,"bc6h-rgb-ufloat":16,"bc6h-rgb-float":16,"bc7-rgba-unorm":16,"bc7-rgba-unorm-srgb":16};function $g(r,t){const{format:e,fourCC:i,width:s,height:n,dataOffset:a,mipmapCount:o}=UP(r);if(!t.includes(e))throw new Error(`Unsupported texture format: ${i} ${e}, supported: ${t}`);if(o<=1)return{format:e,width:s,height:n,resource:[new Uint8Array(r,a)],alphaMode:"no-premultiply-alpha"};const l=DP(e,s,n,a,o,r);return{format:e,width:s,height:n,resource:l,alphaMode:"no-premultiply-alpha"}}function DP(r,t,e,i,s,n){const a=[],o=kg[r];let l=t,u=e,c=i;for(let h=0;h>1,1),u=Math.max(u>>1,1)}return a}function UP(r){const t=new Uint32Array(r,0,Y.HEADER_SIZE/Uint32Array.BYTES_PER_ELEMENT);if(t[Y.HEADER_FIELDS.MAGIC]!==Y.MAGIC_VALUE)throw new Error("Invalid magic number in DDS header");const e=t[Y.HEADER_FIELDS.HEIGHT],i=t[Y.HEADER_FIELDS.WIDTH],s=Math.max(1,t[Y.HEADER_FIELDS.MIPMAP_COUNT]),n=t[Y.HEADER_FIELDS.PF_FLAGS],a=t[Y.HEADER_FIELDS.FOURCC],o=kP(t,n,a,r),l=Y.MAGIC_SIZE+Y.HEADER_SIZE+(a===Y.D3DFMT.DX10?Y.HEADER_DX10_SIZE:0);return{format:o,fourCC:a,width:i,height:e,dataOffset:l,mipmapCount:s}}function kP(r,t,e,i){if(t&Y.PIXEL_FORMAT_FLAGS.FOURCC){if(e===Y.D3DFMT.DX10){const s=new Uint32Array(i,Y.MAGIC_SIZE+Y.HEADER_SIZE,Y.HEADER_DX10_SIZE/Uint32Array.BYTES_PER_ELEMENT);if(s[Y.HEADER_DX10_FIELDS.MISC_FLAG]===Y.RESOURCE_MISC_TEXTURECUBE)throw new Error("DDSParser does not support cubemap textures");if(s[Y.HEADER_DX10_FIELDS.RESOURCE_DIMENSION]===Y.D3D10_RESOURCE_DIMENSION.DDS_DIMENSION_TEXTURE3D)throw new Error("DDSParser does not supported 3D texture data");const n=s[Y.HEADER_DX10_FIELDS.DXGI_FORMAT];if(n in Yt)return Yt[n];throw new Error(`DDSParser cannot parse texture data with DXGI format ${n}`)}if(e in Tl)return Tl[e];throw new Error(`DDSParser cannot parse texture data with fourCC format ${e}`)}if(t&Y.PIXEL_FORMAT_FLAGS.RGB||t&Y.PIXEL_FORMAT_FLAGS.RGBA)return $P(r);throw t&Y.PIXEL_FORMAT_FLAGS.YUV?new Error("DDSParser does not supported YUV uncompressed texture data."):t&Y.PIXEL_FORMAT_FLAGS.LUMINANCE||t&Y.PIXEL_FORMAT_FLAGS.LUMINANCEA?new Error("DDSParser does not support single-channel (lumninance) texture data!"):t&Y.PIXEL_FORMAT_FLAGS.ALPHA||t&Y.PIXEL_FORMAT_FLAGS.ALPHAPIXELS?new Error("DDSParser does not support single-channel (alpha) texture data!"):new Error("DDSParser failed to load a texture file due to an unknown reason!")}function $P(r){const t=r[Y.HEADER_FIELDS.RGB_BITCOUNT],e=r[Y.HEADER_FIELDS.R_BIT_MASK],i=r[Y.HEADER_FIELDS.G_BIT_MASK],s=r[Y.HEADER_FIELDS.B_BIT_MASK],n=r[Y.HEADER_FIELDS.A_BIT_MASK];switch(t){case 32:if(e===255&&i===65280&&s===16711680&&n===4278190080)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM];if(e===16711680&&i===65280&&s===255&&n===4278190080)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM];if(e===1072693248&&i===1047552&&s===1023&&n===3221225472)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM];if(e===65535&&i===4294901760&&s===0&&n===0)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM];if(e===4294967295&&i===0&&s===0&&n===0)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT];break;case 24:break;case 16:if(e===31744&&i===992&&s===31&&n===32768)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM];if(e===63488&&i===2016&&s===31&&n===0)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM];if(e===3840&&i===240&&s===15&&n===61440)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM];if(e===255&&i===0&&s===0&&n===65280)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM];if(e===65535&&i===0&&s===0&&n===0)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R16_UNORM];break;case 8:if(e===255&&i===0&&s===0&&n===0)return Yt[Y.DXGI_FORMAT.DXGI_FORMAT_R8_UNORM];break}throw new Error(`DDSParser does not support uncompressed texture with configuration: + bitCount = ${t}, rBitMask = ${e}, gBitMask = ${i}, aBitMask = ${n}`)}const LP={extension:{type:T.LoadParser,priority:qt.High,name:"loadDDS"},name:"loadDDS",id:"dds",test(r){return ne(r,[".dds"])},async load(r,t,e){const i=await Mi(),s=await(await fetch(r)).arrayBuffer(),n=$g(s,i),a=new Ri(n);return $e(a,e,r)},unload(r){Array.isArray(r)?r.forEach(t=>t.destroy(!0)):r.destroy(!0)}};var Lg=(r=>(r[r.RGBA8_SNORM=36759]="RGBA8_SNORM",r[r.RGBA=6408]="RGBA",r[r.RGBA8UI=36220]="RGBA8UI",r[r.SRGB8_ALPHA8=35907]="SRGB8_ALPHA8",r[r.RGBA8I=36238]="RGBA8I",r[r.RGBA8=32856]="RGBA8",r[r.COMPRESSED_RGB_S3TC_DXT1_EXT=33776]="COMPRESSED_RGB_S3TC_DXT1_EXT",r[r.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777]="COMPRESSED_RGBA_S3TC_DXT1_EXT",r[r.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778]="COMPRESSED_RGBA_S3TC_DXT3_EXT",r[r.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779]="COMPRESSED_RGBA_S3TC_DXT5_EXT",r[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT=35917]="COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT",r[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT=35918]="COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT",r[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT=35919]="COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT",r[r.COMPRESSED_SRGB_S3TC_DXT1_EXT=35916]="COMPRESSED_SRGB_S3TC_DXT1_EXT",r[r.COMPRESSED_RED_RGTC1_EXT=36283]="COMPRESSED_RED_RGTC1_EXT",r[r.COMPRESSED_SIGNED_RED_RGTC1_EXT=36284]="COMPRESSED_SIGNED_RED_RGTC1_EXT",r[r.COMPRESSED_RED_GREEN_RGTC2_EXT=36285]="COMPRESSED_RED_GREEN_RGTC2_EXT",r[r.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT=36286]="COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT",r[r.COMPRESSED_R11_EAC=37488]="COMPRESSED_R11_EAC",r[r.COMPRESSED_SIGNED_R11_EAC=37489]="COMPRESSED_SIGNED_R11_EAC",r[r.COMPRESSED_RG11_EAC=37490]="COMPRESSED_RG11_EAC",r[r.COMPRESSED_SIGNED_RG11_EAC=37491]="COMPRESSED_SIGNED_RG11_EAC",r[r.COMPRESSED_RGB8_ETC2=37492]="COMPRESSED_RGB8_ETC2",r[r.COMPRESSED_RGBA8_ETC2_EAC=37496]="COMPRESSED_RGBA8_ETC2_EAC",r[r.COMPRESSED_SRGB8_ETC2=37493]="COMPRESSED_SRGB8_ETC2",r[r.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC=37497]="COMPRESSED_SRGB8_ALPHA8_ETC2_EAC",r[r.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2=37494]="COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2",r[r.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2=37495]="COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2",r[r.COMPRESSED_RGBA_ASTC_4x4_KHR=37808]="COMPRESSED_RGBA_ASTC_4x4_KHR",r[r.COMPRESSED_RGBA_ASTC_5x4_KHR=37809]="COMPRESSED_RGBA_ASTC_5x4_KHR",r[r.COMPRESSED_RGBA_ASTC_5x5_KHR=37810]="COMPRESSED_RGBA_ASTC_5x5_KHR",r[r.COMPRESSED_RGBA_ASTC_6x5_KHR=37811]="COMPRESSED_RGBA_ASTC_6x5_KHR",r[r.COMPRESSED_RGBA_ASTC_6x6_KHR=37812]="COMPRESSED_RGBA_ASTC_6x6_KHR",r[r.COMPRESSED_RGBA_ASTC_8x5_KHR=37813]="COMPRESSED_RGBA_ASTC_8x5_KHR",r[r.COMPRESSED_RGBA_ASTC_8x6_KHR=37814]="COMPRESSED_RGBA_ASTC_8x6_KHR",r[r.COMPRESSED_RGBA_ASTC_8x8_KHR=37815]="COMPRESSED_RGBA_ASTC_8x8_KHR",r[r.COMPRESSED_RGBA_ASTC_10x5_KHR=37816]="COMPRESSED_RGBA_ASTC_10x5_KHR",r[r.COMPRESSED_RGBA_ASTC_10x6_KHR=37817]="COMPRESSED_RGBA_ASTC_10x6_KHR",r[r.COMPRESSED_RGBA_ASTC_10x8_KHR=37818]="COMPRESSED_RGBA_ASTC_10x8_KHR",r[r.COMPRESSED_RGBA_ASTC_10x10_KHR=37819]="COMPRESSED_RGBA_ASTC_10x10_KHR",r[r.COMPRESSED_RGBA_ASTC_12x10_KHR=37820]="COMPRESSED_RGBA_ASTC_12x10_KHR",r[r.COMPRESSED_RGBA_ASTC_12x12_KHR=37821]="COMPRESSED_RGBA_ASTC_12x12_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR=37840]="COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR=37841]="COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR=37842]="COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR=37843]="COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR=37844]="COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR=37845]="COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR=37846]="COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR=37847]="COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR=37848]="COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR=37849]="COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR=37850]="COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR=37851]="COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR=37852]="COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR",r[r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR=37853]="COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR",r[r.COMPRESSED_RGBA_BPTC_UNORM_EXT=36492]="COMPRESSED_RGBA_BPTC_UNORM_EXT",r[r.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT=36493]="COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT",r[r.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT=36494]="COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT",r[r.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT=36495]="COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT",r))(Lg||{}),NP=(r=>(r[r.RGBA=6408]="RGBA",r[r.RGB=6407]="RGB",r[r.RG=33319]="RG",r[r.RED=6403]="RED",r[r.RGBA_INTEGER=36249]="RGBA_INTEGER",r[r.RGB_INTEGER=36248]="RGB_INTEGER",r[r.RG_INTEGER=33320]="RG_INTEGER",r[r.RED_INTEGER=36244]="RED_INTEGER",r[r.ALPHA=6406]="ALPHA",r[r.LUMINANCE=6409]="LUMINANCE",r[r.LUMINANCE_ALPHA=6410]="LUMINANCE_ALPHA",r[r.DEPTH_COMPONENT=6402]="DEPTH_COMPONENT",r[r.DEPTH_STENCIL=34041]="DEPTH_STENCIL",r))(NP||{}),XP=(r=>(r[r.UNSIGNED_BYTE=5121]="UNSIGNED_BYTE",r[r.UNSIGNED_SHORT=5123]="UNSIGNED_SHORT",r[r.UNSIGNED_SHORT_5_6_5=33635]="UNSIGNED_SHORT_5_6_5",r[r.UNSIGNED_SHORT_4_4_4_4=32819]="UNSIGNED_SHORT_4_4_4_4",r[r.UNSIGNED_SHORT_5_5_5_1=32820]="UNSIGNED_SHORT_5_5_5_1",r[r.UNSIGNED_INT=5125]="UNSIGNED_INT",r[r.UNSIGNED_INT_10F_11F_11F_REV=35899]="UNSIGNED_INT_10F_11F_11F_REV",r[r.UNSIGNED_INT_2_10_10_10_REV=33640]="UNSIGNED_INT_2_10_10_10_REV",r[r.UNSIGNED_INT_24_8=34042]="UNSIGNED_INT_24_8",r[r.UNSIGNED_INT_5_9_9_9_REV=35902]="UNSIGNED_INT_5_9_9_9_REV",r[r.BYTE=5120]="BYTE",r[r.SHORT=5122]="SHORT",r[r.INT=5124]="INT",r[r.FLOAT=5126]="FLOAT",r[r.FLOAT_32_UNSIGNED_INT_24_8_REV=36269]="FLOAT_32_UNSIGNED_INT_24_8_REV",r[r.HALF_FLOAT=36193]="HALF_FLOAT",r))(XP||{});const Tt={FILE_HEADER_SIZE:64,FILE_IDENTIFIER:[171,75,84,88,32,49,49,187,13,10,26,10],FORMATS_TO_COMPONENTS:{6408:4,6407:3,33319:2,6403:1,6409:1,6410:2,6406:1},INTERNAL_FORMAT_TO_BYTES_PER_PIXEL:{33776:.5,33777:.5,33778:1,33779:1,35916:.5,35917:.5,35918:1,35919:1,36283:.5,36284:.5,36285:1,36286:1,37488:.5,37489:.5,37490:1,37491:1,37492:.5,37496:1,37493:.5,37497:1,37494:.5,37495:.5,37808:1,37840:1,37809:.8,37841:.8,37810:.64,37842:.64,37811:.53375,37843:.53375,37812:.445,37844:.445,37813:.4,37845:.4,37814:.33375,37846:.33375,37815:.25,37847:.25,37816:.32,37848:.32,37817:.26625,37849:.26625,37818:.2,37850:.2,37819:.16,37851:.16,37820:.13375,37852:.13375,37821:.11125,37853:.11125,36492:1,36493:1,36494:1,36495:1},INTERNAL_FORMAT_TO_TEXTURE_FORMATS:{33776:"bc1-rgba-unorm",33777:"bc1-rgba-unorm",33778:"bc2-rgba-unorm",33779:"bc3-rgba-unorm",35916:"bc1-rgba-unorm-srgb",35917:"bc1-rgba-unorm-srgb",35918:"bc2-rgba-unorm-srgb",35919:"bc3-rgba-unorm-srgb",36283:"bc4-r-unorm",36284:"bc4-r-snorm",36285:"bc5-rg-unorm",36286:"bc5-rg-snorm",37488:"eac-r11unorm",37490:"eac-rg11snorm",37492:"etc2-rgb8unorm",37496:"etc2-rgba8unorm",37493:"etc2-rgb8unorm-srgb",37497:"etc2-rgba8unorm-srgb",37494:"etc2-rgb8a1unorm",37495:"etc2-rgb8a1unorm-srgb",37808:"astc-4x4-unorm",37840:"astc-4x4-unorm-srgb",37809:"astc-5x4-unorm",37841:"astc-5x4-unorm-srgb",37810:"astc-5x5-unorm",37842:"astc-5x5-unorm-srgb",37811:"astc-6x5-unorm",37843:"astc-6x5-unorm-srgb",37812:"astc-6x6-unorm",37844:"astc-6x6-unorm-srgb",37813:"astc-8x5-unorm",37845:"astc-8x5-unorm-srgb",37814:"astc-8x6-unorm",37846:"astc-8x6-unorm-srgb",37815:"astc-8x8-unorm",37847:"astc-8x8-unorm-srgb",37816:"astc-10x5-unorm",37848:"astc-10x5-unorm-srgb",37817:"astc-10x6-unorm",37849:"astc-10x6-unorm-srgb",37818:"astc-10x8-unorm",37850:"astc-10x8-unorm-srgb",37819:"astc-10x10-unorm",37851:"astc-10x10-unorm-srgb",37820:"astc-12x10-unorm",37852:"astc-12x10-unorm-srgb",37821:"astc-12x12-unorm",37853:"astc-12x12-unorm-srgb",36492:"bc7-rgba-unorm",36493:"bc7-rgba-unorm-srgb",36494:"bc6h-rgb-float",36495:"bc6h-rgb-ufloat",35907:"rgba8unorm-srgb",36759:"rgba8snorm",36220:"rgba8uint",36238:"rgba8sint",6408:"rgba8unorm"},FIELDS:{FILE_IDENTIFIER:0,ENDIANNESS:12,GL_TYPE:16,GL_TYPE_SIZE:20,GL_FORMAT:24,GL_INTERNAL_FORMAT:28,GL_BASE_INTERNAL_FORMAT:32,PIXEL_WIDTH:36,PIXEL_HEIGHT:40,PIXEL_DEPTH:44,NUMBER_OF_ARRAY_ELEMENTS:48,NUMBER_OF_FACES:52,NUMBER_OF_MIPMAP_LEVELS:56,BYTES_OF_KEY_VALUE_DATA:60},TYPES_TO_BYTES_PER_COMPONENT:{5121:1,5123:2,5124:4,5125:4,5126:4,36193:8},TYPES_TO_BYTES_PER_PIXEL:{32819:2,32820:2,33635:2},ENDIANNESS:67305985};function Ng(r,t){const e=new DataView(r);if(!WP(e))throw new Error("Invalid KTX identifier in header");const{littleEndian:i,glType:s,glFormat:n,glInternalFormat:a,pixelWidth:o,pixelHeight:l,numberOfMipmapLevels:u,offset:c}=zP(e),h=Tt.INTERNAL_FORMAT_TO_TEXTURE_FORMATS[a];if(!h)throw new Error(`Unknown texture format ${a}`);if(!t.includes(h))throw new Error(`Unsupported texture format: ${h}, supportedFormats: ${t}`);const p=jP(s,n,a),f=HP(e,s,p,o,l,c,u,i);return{format:h,width:o,height:l,resource:f,alphaMode:"no-premultiply-alpha"}}function HP(r,t,e,i,s,n,a,o){const l=i+3&-4,u=s+3&-4;let c=i*s;t===0&&(c=l*u);let h=c*e,p=i,f=s,m=l,g=u,_=n;const b=new Array(a);for(let y=0;y>1||1,f=f>>1||1,m=p+4-1&-4,g=f+4-1&-4,h=m*g*e}return b}function jP(r,t,e){let i=Tt.INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[e];if(r!==0&&(Tt.TYPES_TO_BYTES_PER_COMPONENT[r]?i=Tt.TYPES_TO_BYTES_PER_COMPONENT[r]*Tt.FORMATS_TO_COMPONENTS[t]:i=Tt.TYPES_TO_BYTES_PER_PIXEL[r]),i===void 0)throw new Error("Unable to resolve the pixel format stored in the *.ktx file!");return i}function zP(r){const t=r.getUint32(Tt.FIELDS.ENDIANNESS,!0)===Tt.ENDIANNESS,e=r.getUint32(Tt.FIELDS.GL_TYPE,t),i=r.getUint32(Tt.FIELDS.GL_FORMAT,t),s=r.getUint32(Tt.FIELDS.GL_INTERNAL_FORMAT,t),n=r.getUint32(Tt.FIELDS.PIXEL_WIDTH,t),a=r.getUint32(Tt.FIELDS.PIXEL_HEIGHT,t)||1,o=r.getUint32(Tt.FIELDS.PIXEL_DEPTH,t)||1,l=r.getUint32(Tt.FIELDS.NUMBER_OF_ARRAY_ELEMENTS,t)||1,u=r.getUint32(Tt.FIELDS.NUMBER_OF_FACES,t),c=r.getUint32(Tt.FIELDS.NUMBER_OF_MIPMAP_LEVELS,t),h=r.getUint32(Tt.FIELDS.BYTES_OF_KEY_VALUE_DATA,t);if(a===0||o!==1)throw new Error("Only 2D textures are supported");if(u!==1)throw new Error("CubeTextures are not supported by KTXLoader yet!");if(l!==1)throw new Error("WebGL does not support array textures");return{littleEndian:t,glType:e,glFormat:i,glInternalFormat:s,pixelWidth:n,pixelHeight:a,numberOfMipmapLevels:c,offset:Tt.FILE_HEADER_SIZE+h}}function WP(r){for(let t=0;tt.destroy(!0)):r.destroy(!0)}},YP='(function(){"use strict";const s={rgb8unorm:{convertedFormat:"rgba8unorm",convertFunction:i},"rgb8unorm-srgb":{convertedFormat:"rgba8unorm-srgb",convertFunction:i}};function f(r){const t=r.format;if(s[t]){const n=s[t].convertFunction,o=r.resource;for(let e=0;e{LIBKTX({locateFile:o=>t}).then(o=>{n(o)})})}return c}async function v(r,t){const n=await fetch(r);if(n.ok){const o=await n.arrayBuffer();return new t.ktxTexture(new Uint8Array(o))}throw new Error(`Failed to load KTX(2) texture: ${r}`)}const x=["bc7-rgba-unorm","astc-4x4-unorm","etc2-rgba8unorm","bc3-rgba-unorm","rgba8unorm"];async function B(r){const t=await g(),n=await v(r,t);let o;if(n.needsTranscoding){o=u;const R=t.TranscodeTarget[l];if(n.transcodeBasis(R,0)!==t.ErrorCode.SUCCESS)throw new Error("Unable to transcode basis texture.")}else o=U(n);const e=d(n),b={width:n.baseWidth,height:n.baseHeight,format:o,mipLevelCount:n.numLevels,resource:e,alphaMode:"no-premultiply-alpha"};return f(b),b}async function A(r,t,n){r&&(a.jsUrl=r),t&&(a.wasmUrl=t),u=x.filter(o=>n.includes(o))[0],l=T(u),await g()}const m={init:async r=>{const{jsUrl:t,wasmUrl:n,supportedTextures:o}=r;await A(t,n,o)},load:async r=>{var t;try{const n=await B(r.url);return{type:"load",url:r.url,success:!0,textureOptions:n,transferables:(t=n.resource)==null?void 0:t.map(o=>o.buffer)}}catch(n){throw n}}};self.onmessage=(async r=>{var t;const n=r.data;try{const o=await((t=m[n.type])==null?void 0:t.call(m,n));o&&self.postMessage(o,o.transferables)}catch(o){self.postMessage({type:"error",err:o,url:n.url})}})})();\n';let $r=null;class Xg{constructor(){$r||($r=URL.createObjectURL(new Blob([YP],{type:"application/javascript"}))),this.worker=new Worker($r)}}Xg.revokeObjectURL=function(){$r&&(URL.revokeObjectURL($r),$r=null)};const Ys={jsUrl:"https://cdn.jsdelivr.net/npm/pixi.js/transcoders/ktx/libktx.js",wasmUrl:"https://cdn.jsdelivr.net/npm/pixi.js/transcoders/ktx/libktx.wasm"};function KP(r){Object.assign(Ys,r)}let Gi;const Hg={},jg={};function qP(r){return Gi||(Gi=new Xg().worker,Gi.onmessage=t=>{const{err:e,success:i,url:s,textureOptions:n}=t.data;if(e){jg[s](e);return}i||console.warn("Failed to load KTX texture",s),Hg[s](n)},Gi.postMessage({type:"init",jsUrl:Ys.jsUrl,wasmUrl:Ys.wasmUrl,supportedTextures:r})),Gi}function zg(r,t){const e=qP(t);return new Promise((i,s)=>{Hg[r]=i,jg[r]=s,e.postMessage({type:"load",url:r})})}const ZP={extension:{type:T.LoadParser,priority:qt.High,name:"loadKTX2"},name:"loadKTX2",id:"ktx2",test(r){return ne(r,".ktx2")},async load(r,t,e){const i=await Mi(),s=await zg(r,i),n=new Ri(s);return $e(n,e,r)},async unload(r){Array.isArray(r)?r.forEach(t=>t.destroy(!0)):r.destroy(!0)}},Sl={rgb8unorm:{convertedFormat:"rgba8unorm",convertFunction:Wg},"rgb8unorm-srgb":{convertedFormat:"rgba8unorm-srgb",convertFunction:Wg}};function QP(r){const t=r.format;if(Sl[t]){const e=Sl[t].convertFunction,i=r.resource;for(let s=0;sne(r,[".ktx",".ktx2",".dds"]),parse:r=>{var t,e;let i;const s=r.split(".");if(s.length>2){const n=s[s.length-2];Ks.includes(n)&&(i=n)}else i=s[s.length-1];return{resolution:parseFloat((e=(t=Fe.RETINA_PREFIX.exec(r))==null?void 0:t[1])!=null?e:"1"),format:i,src:r}}};let qs;const aA={extension:{type:T.DetectionParser,priority:2},test:async()=>!!(await bi()||_i()),add:async r=>{const t=await vl();return qs=oA(t),[...qs,...r]},remove:async r=>qs?r.filter(t=>!(t in qs)):r};function oA(r){const t=["basis"],e={};return r.forEach(i=>{const s=i.split("-")[0];s&&!e[s]&&(e[s]=!0,t.push(s))}),t.sort((i,s)=>{const n=Ks.indexOf(i),a=Ks.indexOf(s);return n===-1?1:a===-1?-1:n-a}),t}const lA=new Pt,uA=new D,Ii=new it,wl=class{cull(t,e,i=!0){this._cullRecursive(t,e,i)}_cullRecursive(t,e,i=!0){if(t.cullable&&t.measurable&&t.includeInBuild)if(t.cullArea){Ii.x=e.x,Ii.y=e.y,Ii.width=e.width,Ii.height=e.height;const s=i?t.worldTransform:t.getGlobalTransform(uA,i);t.culled=!Ii.intersects(t.cullArea,s)}else{const s=ti(t,i,lA);t.culled=s.x>=e.x+e.width||s.y>=e.y+e.height||s.x+s.width<=e.x||s.y+s.height<=e.y}else t.culled=!1;if(!(!t.cullableChildren||t.culled||!t.renderable||!t.measurable||!t.includeInBuild))for(let s=0;s{var e;const i=((e=t==null?void 0:t.culler)==null?void 0:e.updateTransform)!==!0;Kg.shared.cull(this.stage,this.renderer.screen,i),this.renderer.render({container:this.stage})}}static destroy(){this.render=this._renderRef}}qg.extension={priority:10,type:T.Application,name:"culler"};class El{constructor(t){this._attachedDomElements=[],this._renderer=t,this._renderer.runners.postrender.add(this),this._renderer.runners.init.add(this),this._domElement=document.createElement("div"),this._domElement.style.position="absolute",this._domElement.style.top="0",this._domElement.style.left="0",this._domElement.style.pointerEvents="none",this._domElement.style.zIndex="1000"}init(){this._canvasObserver=new Oa({domElement:this._domElement,renderer:this._renderer})}addRenderable(t,e){this._attachedDomElements.includes(t)||this._attachedDomElements.push(t)}updateRenderable(t){}validateRenderable(t){return!0}postrender(){const t=this._attachedDomElements;if(t.length===0){this._domElement.remove();return}this._canvasObserver.ensureAttached();for(let e=0;et in r?cA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,hA=(r,t)=>{for(var e in t||(t={}))Zg.call(t,e)&&Jg(r,e,t[e]);if(Zs)for(var e of Zs(t))Qg.call(t,e)&&Jg(r,e,t[e]);return r},dA=(r,t)=>{var e={};for(var i in r)Zg.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Zs)for(var i of Zs(r))t.indexOf(i)<0&&Qg.call(r,i)&&(e[i]=r[i]);return e};$.add(El);class pA extends xe{constructor(t={}){const e=t,{element:i,anchor:s}=e,n=dA(e,["element","anchor"]);super(hA({label:"DOMContainer"},n)),this.renderPipeId="dom",this.batched=!1,this._anchor=new rt(0,0),s&&(this.anchor=s),this.element=t.element||document.createElement("div")}get anchor(){return this._anchor}set anchor(t){typeof t=="number"?this._anchor.set(t):this._anchor.copyFrom(t)}set element(t){this._element!==t&&(this._element=t,this.onViewUpdate())}get element(){return this._element}updateBounds(){const t=this._bounds,e=this._element;if(!e){t.minX=0,t.minY=0,t.maxX=0,t.maxY=0;return}const{offsetWidth:i,offsetHeight:s}=e;t.minX=0,t.maxX=i,t.minY=0,t.maxY=s}destroy(t=!1){var e,i;super.destroy(t),(i=(e=this._element)==null?void 0:e.parentNode)==null||i.removeChild(this._element),this._element=null,this._anchor=null}}const fA={extension:{type:T.Environment,name:"browser",priority:-1},test:()=>!0,load:async()=>{await Promise.resolve().then(function(){return O2})}};var t_=` +in vec2 vTextureCoord; +in vec4 vColor; + +out vec4 finalColor; + +uniform float uBlend; + +uniform sampler2D uTexture; +uniform sampler2D uBackTexture; + +{FUNCTIONS} + +void main() +{ + vec4 back = texture(uBackTexture, vTextureCoord); + vec4 front = texture(uTexture, vTextureCoord); + float blendedAlpha = front.a + back.a * (1.0 - front.a); + + {MAIN} +} +`,e_=`in vec2 aPosition; +out vec2 vTextureCoord; +out vec2 backgroundUv; + +uniform vec4 uInputSize; +uniform vec4 uOutputFrame; +uniform vec4 uOutputTexture; + +vec4 filterVertexPosition( void ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +vec2 filterTextureCoord( void ) +{ + return aPosition * (uOutputFrame.zw * uInputSize.zw); +} + +void main(void) +{ + gl_Position = filterVertexPosition(); + vTextureCoord = filterTextureCoord(); +} +`,r_=` +struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct BlendUniforms { + uBlend:f32, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; +@group(0) @binding(3) var uBackTexture: texture_2d; + +@group(1) @binding(0) var blendUniforms : BlendUniforms; + + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2 + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition) + ); +} + +{FUNCTIONS} + +@fragment +fn mainFragment( + @location(0) uv: vec2 +) -> @location(0) vec4 { + + + var back = textureSample(uBackTexture, uSampler, uv); + var front = textureSample(uTexture, uSampler, uv); + var blendedAlpha = front.a + back.a * (1.0 - front.a); + + var out = vec4(0.0,0.0,0.0,0.0); + + {MAIN} + + return out; +}`,mA=Object.defineProperty,i_=Object.getOwnPropertySymbols,gA=Object.prototype.hasOwnProperty,_A=Object.prototype.propertyIsEnumerable,s_=(r,t,e)=>t in r?mA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,n_=(r,t)=>{for(var e in t||(t={}))gA.call(t,e)&&s_(r,e,t[e]);if(i_)for(var e of i_(t))_A.call(t,e)&&s_(r,e,t[e]);return r};class bA extends Se{constructor(t){const e=t.gpu,i=a_(n_({source:r_},e)),s=kt.from({vertex:{source:i,entryPoint:"mainVertex"},fragment:{source:i,entryPoint:"mainFragment"}}),n=t.gl,a=a_(n_({source:t_},n)),o=Ht.from({vertex:e_,fragment:a}),l=new wt({uBlend:{value:1,type:"f32"}});super({gpuProgram:s,glProgram:o,blendRequired:!0,resources:{blendUniforms:l,uBackTexture:F.EMPTY}})}}function a_(r){const{source:t,functions:e,main:i}=r;return t.replace("{FUNCTIONS}",e).replace("{MAIN}",i)}const vA=` + float getLuminosity(vec3 c) { + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + } + + vec3 setLuminosity(vec3 c, float lum) { + float modLum = lum - getLuminosity(c); + vec3 color = c.rgb + vec3(modLum); + + // clip back into legal range + modLum = getLuminosity(color); + vec3 modLumVec = vec3(modLum); + + float cMin = min(color.r, min(color.g, color.b)); + float cMax = max(color.r, max(color.g, color.b)); + + if(cMin < 0.0) { + color = mix(modLumVec, color, modLum / (modLum - cMin)); + } + + if(cMax > 1.0) { + color = mix(modLumVec, color, (1.0 - modLum) / (cMax - modLum)); + } + + return color; + } + + float getSaturation(vec3 c) { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); + } + + vec3 setSaturationMinMidMax(vec3 cSorted, float s) { + vec3 colorSorted = cSorted; + + if(colorSorted.z > colorSorted.x) { + colorSorted.y = (((colorSorted.y - colorSorted.x) * s) / (colorSorted.z - colorSorted.x)); + colorSorted.z = s; + } + else { + colorSorted.y = 0.0; + colorSorted.z = 0.0; + } + + colorSorted.x = 0.0; + + return colorSorted; + } + + vec3 setSaturation(vec3 c, float s) { + vec3 color = c; + + if(color.r <= color.g && color.r <= color.b) { + if(color.g <= color.b) { + color = setSaturationMinMidMax(color.rgb, s).rgb; + } + else { + color = setSaturationMinMidMax(color.rbg, s).rbg; + } + } + else if(color.g <= color.r && color.g <= color.b) { + if(color.r <= color.b) { + color = setSaturationMinMidMax(color.grb, s).grb; + } + else { + color = setSaturationMinMidMax(color.gbr, s).gbr; + } + } + else { + // Using bgr for both fixes part of hue + if(color.r <= color.g) { + color = setSaturationMinMidMax(color.brg, s).brg; + } + else { + color = setSaturationMinMidMax(color.bgr, s).bgr; + } + } + + return color; + } + `,yA=` + fn getLuminosity(c: vec3) -> f32 + { + return 0.3*c.r + 0.59*c.g + 0.11*c.b; + } + + fn setLuminosity(c: vec3, lum: f32) -> vec3 + { + var modLum: f32 = lum - getLuminosity(c); + var color: vec3 = c.rgb + modLum; + + // clip back into legal range + modLum = getLuminosity(color); + let modLumVec = vec3(modLum); + + let cMin: f32 = min(color.r, min(color.g, color.b)); + let cMax: f32 = max(color.r, max(color.g, color.b)); + + if(cMin < 0.0) + { + color = mix(modLumVec, color, modLum / (modLum - cMin)); + } + + if(cMax > 1.0) + { + color = mix(modLumVec, color, (1 - modLum) / (cMax - modLum)); + } + + return color; + } + + fn getSaturation(c: vec3) -> f32 + { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); + } + + fn setSaturationMinMidMax(cSorted: vec3, s: f32) -> vec3 + { + var colorSorted = cSorted; + + if(colorSorted.z > colorSorted.x) + { + colorSorted.y = (((colorSorted.y - colorSorted.x) * s) / (colorSorted.z - colorSorted.x)); + colorSorted.z = s; + } + else + { + colorSorted.y = 0; + colorSorted.z = 0; + } + + colorSorted.x = 0; + + return colorSorted; + } + + fn setSaturation(c: vec3, s: f32) -> vec3 + { + var color = c; + + if (color.r <= color.g && color.r <= color.b) + { + if (color.g <= color.b) + { + color = vec3(setSaturationMinMidMax(color.rgb, s)).rgb; + } + else + { + color = vec3(setSaturationMinMidMax(color.rbg, s)).rbg; + } + } + else if (color.g <= color.r && color.g <= color.b) + { + if (color.r <= color.b) + { + color = vec3(setSaturationMinMidMax(color.grb, s)).grb; + } + else + { + color = vec3(setSaturationMinMidMax(color.gbr, s)).gbr; + } + } + else + { + // Using bgr for both fixes part of hue + if (color.r <= color.g) + { + color = vec3(setSaturationMinMidMax(color.brg, s)).brg; + } + else + { + color = vec3(setSaturationMinMidMax(color.bgr, s)).bgr; + } + } + + return color; + } + `;var o_=` +in vec2 vTextureCoord; + +out vec4 finalColor; + +uniform float uAlpha; +uniform sampler2D uTexture; + +void main() +{ + finalColor = texture(uTexture, vTextureCoord) * uAlpha; +} +`,Pl=`struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct AlphaUniforms { + uAlpha:f32, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; + +@group(1) @binding(0) var alphaUniforms : AlphaUniforms; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2 + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +fn getSize() -> vec2 +{ + return gfu.uGlobalFrame.zw; +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition) + ); +} + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @builtin(position) position: vec4 +) -> @location(0) vec4 { + + var sample = textureSample(uTexture, uSampler, uv); + + return sample * alphaUniforms.uAlpha; +}`,xA=Object.defineProperty,TA=Object.defineProperties,SA=Object.getOwnPropertyDescriptors,Qs=Object.getOwnPropertySymbols,l_=Object.prototype.hasOwnProperty,u_=Object.prototype.propertyIsEnumerable,c_=(r,t,e)=>t in r?xA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Al=(r,t)=>{for(var e in t||(t={}))l_.call(t,e)&&c_(r,e,t[e]);if(Qs)for(var e of Qs(t))u_.call(t,e)&&c_(r,e,t[e]);return r},wA=(r,t)=>TA(r,SA(t)),EA=(r,t)=>{var e={};for(var i in r)l_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Qs)for(var i of Qs(r))t.indexOf(i)<0&&u_.call(r,i)&&(e[i]=r[i]);return e};const h_=class i1 extends Se{constructor(t){t=Al(Al({},i1.defaultOptions),t);const e=kt.from({vertex:{source:Pl,entryPoint:"mainVertex"},fragment:{source:Pl,entryPoint:"mainFragment"}}),i=Ht.from({vertex:ci,fragment:o_,name:"alpha-filter"}),s=t,{alpha:n}=s,a=EA(s,["alpha"]),o=new wt({uAlpha:{value:n,type:"f32"}});super(wA(Al({},a),{gpuProgram:e,glProgram:i,resources:{alphaUniforms:o}}))}get alpha(){return this.resources.alphaUniforms.uniforms.uAlpha}set alpha(t){this.resources.alphaUniforms.uniforms.uAlpha=t}};h_.defaultOptions={alpha:1};let PA=h_;const Cl={5:[.153388,.221461,.250301],7:[.071303,.131514,.189879,.214607],9:[.028532,.067234,.124009,.179044,.20236],11:[.0093,.028002,.065984,.121703,.175713,.198596],13:[.002406,.009255,.027867,.065666,.121117,.174868,.197641],15:[489e-6,.002403,.009246,.02784,.065602,.120999,.174697,.197448]},AA=["in vec2 vBlurTexCoords[%size%];","uniform sampler2D uTexture;","out vec4 finalColor;","void main(void)","{"," finalColor = vec4(0.0);"," %blur%","}"].join(` +`);function d_(r){const t=Cl[r],e=t.length;let i=AA,s="";const n="finalColor += texture(uTexture, vBlurTexCoords[%index%]) * %value%;";let a;for(let o=0;o=e&&(a=r-o-1),l=l.replace("%value%",t[a].toString()),s+=l,s+=` +`}return i=i.replace("%blur%",s),i=i.replace("%size%",r.toString()),i}const CA=` + in vec2 aPosition; + + uniform float uStrength; + + out vec2 vBlurTexCoords[%size%]; + + uniform vec4 uInputSize; + uniform vec4 uOutputFrame; + uniform vec4 uOutputTexture; + + vec4 filterVertexPosition( void ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + + vec2 filterTextureCoord( void ) + { + return aPosition * (uOutputFrame.zw * uInputSize.zw); + } + + void main(void) + { + gl_Position = filterVertexPosition(); + + float pixelStrength = uInputSize.%dimension% * uStrength; + + vec2 textureCoord = filterTextureCoord(); + %blur% + }`;function p_(r,t){const e=Math.ceil(r/2);let i=CA,s="",n;t?n="vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * pixelStrength, 0.0);":n="vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * pixelStrength);";for(let a=0;a, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct BlurUniforms { + uStrength:f32, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; + +@group(1) @binding(0) var blurUniforms : BlurUniforms; + + +struct VSOutput { + @builtin(position) position: vec4, + %blur-struct% + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +fn getSize() -> vec2 +{ + return gfu.uGlobalFrame.zw; +} + + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + + let filteredCord = filterTextureCoord(aPosition); + + let pixelStrength = gfu.uInputSize.%dimension% * blurUniforms.uStrength; + + return VSOutput( + filterVertexPosition(aPosition), + %blur-vertex-out% + ); +} + +@fragment +fn mainFragment( + @builtin(position) position: vec4, + %blur-fragment-in% +) -> @location(0) vec4 { + + var finalColor = vec4(0.0); + + %blur-sampling% + + return finalColor; +}`;function g_(r,t){const e=Cl[t],i=e.length,s=[],n=[],a=[];for(let h=0;h,`,r?n[h]=`filteredCord + vec2(${h-i+1} * pixelStrength, 0.0),`:n[h]=`filteredCord + vec2(0.0, ${h-i+1} * pixelStrength),`;const p=ht in r?RA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Rl=(r,t)=>{for(var e in t||(t={}))MA.call(t,e)&&b_(r,e,t[e]);if(__)for(var e of __(t))OA.call(t,e)&&b_(r,e,t[e]);return r};const v_=class s1 extends Se{constructor(t){t=Rl(Rl({},s1.defaultOptions),t);const e=f_(t.horizontal,t.kernelSize),i=g_(t.horizontal,t.kernelSize);super(Rl({glProgram:e,gpuProgram:i,resources:{blurUniforms:{uStrength:{value:0,type:"f32"}}}},t)),this.horizontal=t.horizontal,this._quality=0,this.quality=t.quality,this.blur=t.strength,this._uniforms=this.resources.blurUniforms.uniforms}apply(t,e,i,s){if(this._uniforms.uStrength=this.strength/this.passes,this.passes===1)t.applyFilter(this,e,i,s);else{const n=yt.getSameSizeTexture(e);let a=e,o=n;this._state.blend=!1;const l=t.renderer.type===Gt.WEBGPU;for(let u=0;ut in r?GA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Bi=(r,t)=>{for(var e in t||(t={}))y_.call(t,e)&&T_(r,e,t[e]);if(tn)for(var e of tn(t))x_.call(t,e)&&T_(r,e,t[e]);return r},FA=(r,t)=>IA(r,BA(t)),DA=(r,t)=>{var e={};for(var i in r)y_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&tn)for(var i of tn(r))t.indexOf(i)<0&&x_.call(r,i)&&(e[i]=r[i]);return e};class S_ extends Se{constructor(...t){var e;let i=(e=t[0])!=null?e:{};typeof i=="number"&&(i={strength:i},t[1]!==void 0&&(i.quality=t[1]),t[2]!==void 0&&(i.resolution=t[2]||"inherit"),t[3]!==void 0&&(i.kernelSize=t[3])),i=Bi(Bi({},Js.defaultOptions),i);const s=i,{strength:n,strengthX:a,strengthY:o,quality:l}=s,u=DA(s,["strength","strengthX","strengthY","quality"]);super(FA(Bi({},u),{compatibleRenderers:Gt.BOTH,resources:{}})),this._repeatEdgePixels=!1,this.blurXFilter=new Js(Bi({horizontal:!0},i)),this.blurYFilter=new Js(Bi({horizontal:!1},i)),this.quality=l,this.strengthX=a!=null?a:n,this.strengthY=o!=null?o:n,this.repeatEdgePixels=!1}apply(t,e,i,s){const n=Math.abs(this.blurXFilter.strength),a=Math.abs(this.blurYFilter.strength);if(n&&a){const o=yt.getSameSizeTexture(e);this.blurXFilter.blendMode="normal",this.blurXFilter.apply(t,e,o,!0),this.blurYFilter.blendMode=this.blendMode,this.blurYFilter.apply(t,o,i,s),yt.returnTexture(o)}else a?(this.blurYFilter.blendMode=this.blendMode,this.blurYFilter.apply(t,e,i,s)):(this.blurXFilter.blendMode=this.blendMode,this.blurXFilter.apply(t,e,i,s))}updatePadding(){this._repeatEdgePixels?this.padding=0:this.padding=Math.max(Math.abs(this.blurXFilter.blur),Math.abs(this.blurYFilter.blur))*2}get strength(){if(this.strengthX!==this.strengthY)throw new Error("BlurFilter's strengthX and strengthY are different");return this.strengthX}set strength(t){this.blurXFilter.blur=this.blurYFilter.blur=t,this.updatePadding()}get quality(){return this.blurXFilter.quality}set quality(t){this.blurXFilter.quality=this.blurYFilter.quality=t}get strengthX(){return this.blurXFilter.blur}set strengthX(t){this.blurXFilter.blur=t,this.updatePadding()}get strengthY(){return this.blurYFilter.blur}set strengthY(t){this.blurYFilter.blur=t,this.updatePadding()}get blur(){return this.strength}set blur(t){this.strength=t}get blurX(){return this.strengthX}set blurX(t){this.strengthX=t}get blurY(){return this.strengthY}set blurY(t){this.strengthY=t}get repeatEdgePixels(){return this._repeatEdgePixels}set repeatEdgePixels(t){this._repeatEdgePixels=t,this.updatePadding()}}S_.defaultOptions={strength:8,quality:4,kernelSize:5};var w_=` +in vec2 vTextureCoord; +in vec4 vColor; + +out vec4 finalColor; + +uniform float uColorMatrix[20]; +uniform float uAlpha; + +uniform sampler2D uTexture; + +float rand(vec2 co) +{ + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() +{ + vec4 color = texture(uTexture, vTextureCoord); + float randomValue = rand(gl_FragCoord.xy * 0.2); + float diff = (randomValue - 0.5) * 0.5; + + if (uAlpha == 0.0) { + finalColor = color; + return; + } + + if (color.a > 0.0) { + color.rgb /= color.a; + } + + vec4 result; + + result.r = (uColorMatrix[0] * color.r); + result.r += (uColorMatrix[1] * color.g); + result.r += (uColorMatrix[2] * color.b); + result.r += (uColorMatrix[3] * color.a); + result.r += uColorMatrix[4]; + + result.g = (uColorMatrix[5] * color.r); + result.g += (uColorMatrix[6] * color.g); + result.g += (uColorMatrix[7] * color.b); + result.g += (uColorMatrix[8] * color.a); + result.g += uColorMatrix[9]; + + result.b = (uColorMatrix[10] * color.r); + result.b += (uColorMatrix[11] * color.g); + result.b += (uColorMatrix[12] * color.b); + result.b += (uColorMatrix[13] * color.a); + result.b += uColorMatrix[14]; + + result.a = (uColorMatrix[15] * color.r); + result.a += (uColorMatrix[16] * color.g); + result.a += (uColorMatrix[17] * color.b); + result.a += (uColorMatrix[18] * color.a); + result.a += uColorMatrix[19]; + + vec3 rgb = mix(color.rgb, result.rgb, uAlpha); + + // Premultiply alpha again. + rgb *= result.a; + + finalColor = vec4(rgb, result.a); +} +`,Ml=`struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct ColorMatrixUniforms { + uColorMatrix:array, 5>, + uAlpha:f32, +}; + + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; +@group(1) @binding(0) var colorMatrixUniforms : ColorMatrixUniforms; + + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2, + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition), + ); +} + + +@fragment +fn mainFragment( + @location(0) uv: vec2, +) -> @location(0) vec4 { + + + var c = textureSample(uTexture, uSampler, uv); + + if (colorMatrixUniforms.uAlpha == 0.0) { + return c; + } + + + // Un-premultiply alpha before applying the color matrix. See issue #3539. + if (c.a > 0.0) { + c.r /= c.a; + c.g /= c.a; + c.b /= c.a; + } + + var cm = colorMatrixUniforms.uColorMatrix; + + + var result = vec4(0.); + + result.r = (cm[0][0] * c.r); + result.r += (cm[0][1] * c.g); + result.r += (cm[0][2] * c.b); + result.r += (cm[0][3] * c.a); + result.r += cm[1][0]; + + result.g = (cm[1][1] * c.r); + result.g += (cm[1][2] * c.g); + result.g += (cm[1][3] * c.b); + result.g += (cm[2][0] * c.a); + result.g += cm[2][1]; + + result.b = (cm[2][2] * c.r); + result.b += (cm[2][3] * c.g); + result.b += (cm[3][0] * c.b); + result.b += (cm[3][1] * c.a); + result.b += cm[3][2]; + + result.a = (cm[3][3] * c.r); + result.a += (cm[4][0] * c.g); + result.a += (cm[4][1] * c.b); + result.a += (cm[4][2] * c.a); + result.a += cm[4][3]; + + var rgb = mix(c.rgb, result.rgb, colorMatrixUniforms.uAlpha); + + rgb.r *= result.a; + rgb.g *= result.a; + rgb.b *= result.a; + + return vec4(rgb, result.a); +}`,UA=Object.defineProperty,kA=Object.defineProperties,$A=Object.getOwnPropertyDescriptors,E_=Object.getOwnPropertySymbols,LA=Object.prototype.hasOwnProperty,NA=Object.prototype.propertyIsEnumerable,P_=(r,t,e)=>t in r?UA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,XA=(r,t)=>{for(var e in t||(t={}))LA.call(t,e)&&P_(r,e,t[e]);if(E_)for(var e of E_(t))NA.call(t,e)&&P_(r,e,t[e]);return r},HA=(r,t)=>kA(r,$A(t));class jA extends Se{constructor(t={}){const e=new wt({uColorMatrix:{value:[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],type:"f32",size:20},uAlpha:{value:1,type:"f32"}}),i=kt.from({vertex:{source:Ml,entryPoint:"mainVertex"},fragment:{source:Ml,entryPoint:"mainFragment"}}),s=Ht.from({vertex:ci,fragment:w_,name:"color-matrix-filter"});super(HA(XA({},t),{gpuProgram:i,glProgram:s,resources:{colorMatrixUniforms:e}})),this.alpha=1}_loadMatrix(t,e=!1){let i=t;e&&(this._multiply(i,this.matrix,t),i=this._colorMatrix(i)),this.resources.colorMatrixUniforms.uniforms.uColorMatrix=i,this.resources.colorMatrixUniforms.update()}_multiply(t,e,i){return t[0]=e[0]*i[0]+e[1]*i[5]+e[2]*i[10]+e[3]*i[15],t[1]=e[0]*i[1]+e[1]*i[6]+e[2]*i[11]+e[3]*i[16],t[2]=e[0]*i[2]+e[1]*i[7]+e[2]*i[12]+e[3]*i[17],t[3]=e[0]*i[3]+e[1]*i[8]+e[2]*i[13]+e[3]*i[18],t[4]=e[0]*i[4]+e[1]*i[9]+e[2]*i[14]+e[3]*i[19]+e[4],t[5]=e[5]*i[0]+e[6]*i[5]+e[7]*i[10]+e[8]*i[15],t[6]=e[5]*i[1]+e[6]*i[6]+e[7]*i[11]+e[8]*i[16],t[7]=e[5]*i[2]+e[6]*i[7]+e[7]*i[12]+e[8]*i[17],t[8]=e[5]*i[3]+e[6]*i[8]+e[7]*i[13]+e[8]*i[18],t[9]=e[5]*i[4]+e[6]*i[9]+e[7]*i[14]+e[8]*i[19]+e[9],t[10]=e[10]*i[0]+e[11]*i[5]+e[12]*i[10]+e[13]*i[15],t[11]=e[10]*i[1]+e[11]*i[6]+e[12]*i[11]+e[13]*i[16],t[12]=e[10]*i[2]+e[11]*i[7]+e[12]*i[12]+e[13]*i[17],t[13]=e[10]*i[3]+e[11]*i[8]+e[12]*i[13]+e[13]*i[18],t[14]=e[10]*i[4]+e[11]*i[9]+e[12]*i[14]+e[13]*i[19]+e[14],t[15]=e[15]*i[0]+e[16]*i[5]+e[17]*i[10]+e[18]*i[15],t[16]=e[15]*i[1]+e[16]*i[6]+e[17]*i[11]+e[18]*i[16],t[17]=e[15]*i[2]+e[16]*i[7]+e[17]*i[12]+e[18]*i[17],t[18]=e[15]*i[3]+e[16]*i[8]+e[17]*i[13]+e[18]*i[18],t[19]=e[15]*i[4]+e[16]*i[9]+e[17]*i[14]+e[18]*i[19]+e[19],t}_colorMatrix(t){const e=new Float32Array(t);return e[4]/=255,e[9]/=255,e[14]/=255,e[19]/=255,e}brightness(t,e){const i=[t,0,0,0,0,0,t,0,0,0,0,0,t,0,0,0,0,0,1,0];this._loadMatrix(i,e)}tint(t,e){const[i,s,n]=J.shared.setValue(t).toArray(),a=[i,0,0,0,0,0,s,0,0,0,0,0,n,0,0,0,0,0,1,0];this._loadMatrix(a,e)}greyscale(t,e){const i=[t,t,t,0,0,t,t,t,0,0,t,t,t,0,0,0,0,0,1,0];this._loadMatrix(i,e)}grayscale(t,e){this.greyscale(t,e)}blackAndWhite(t){const e=[.3,.6,.1,0,0,.3,.6,.1,0,0,.3,.6,.1,0,0,0,0,0,1,0];this._loadMatrix(e,t)}hue(t,e){t=(t||0)/180*Math.PI;const i=Math.cos(t),s=Math.sin(t),n=Math.sqrt,a=1/3,o=n(a),l=i+(1-i)*a,u=a*(1-i)-o*s,c=a*(1-i)+o*s,h=a*(1-i)+o*s,p=i+a*(1-i),f=a*(1-i)-o*s,m=a*(1-i)-o*s,g=a*(1-i)+o*s,_=i+a*(1-i),b=[l,u,c,0,0,h,p,f,0,0,m,g,_,0,0,0,0,0,1,0];this._loadMatrix(b,e)}contrast(t,e){const i=(t||0)+1,s=-.5*(i-1),n=[i,0,0,0,s,0,i,0,0,s,0,0,i,0,s,0,0,0,1,0];this._loadMatrix(n,e)}saturate(t=0,e){const i=t*2/3+1,s=(i-1)*-.5,n=[i,s,s,0,0,s,i,s,0,0,s,s,i,0,0,0,0,0,1,0];this._loadMatrix(n,e)}desaturate(){this.saturate(-1)}negative(t){const e=[-1,0,0,1,0,0,-1,0,1,0,0,0,-1,1,0,0,0,0,1,0];this._loadMatrix(e,t)}sepia(t){const e=[.393,.7689999,.18899999,0,0,.349,.6859999,.16799999,0,0,.272,.5339999,.13099999,0,0,0,0,0,1,0];this._loadMatrix(e,t)}technicolor(t){const e=[1.9125277891456083,-.8545344976951645,-.09155508482755585,0,11.793603434377337,-.3087833385928097,1.7658908555458428,-.10601743074722245,0,-70.35205161461398,-.231103377548616,-.7501899197440212,1.847597816108189,0,30.950940869491138,0,0,0,1,0];this._loadMatrix(e,t)}polaroid(t){const e=[1.438,-.062,-.062,0,0,-.122,1.378,-.122,0,0,-.016,-.016,1.483,0,0,0,0,0,1,0];this._loadMatrix(e,t)}toBGR(t){const e=[0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0];this._loadMatrix(e,t)}kodachrome(t){const e=[1.1285582396593525,-.3967382283601348,-.03992559172921793,0,63.72958762196502,-.16404339962244616,1.0835251566291304,-.05498805115633132,0,24.732407896706203,-.16786010706155763,-.5603416277695248,1.6014850761964943,0,35.62982807460946,0,0,0,1,0];this._loadMatrix(e,t)}browni(t){const e=[.5997023498159715,.34553243048391263,-.2708298674538042,0,47.43192855600873,-.037703249837783157,.8609577587992641,.15059552388459913,0,-36.96841498319127,.24113635128153335,-.07441037908422492,.44972182064877153,0,-7.562075277591283,0,0,0,1,0];this._loadMatrix(e,t)}vintage(t){const e=[.6279345635605994,.3202183420819367,-.03965408211312453,0,9.651285835294123,.02578397704808868,.6441188644374771,.03259127616149294,0,7.462829176470591,.0466055556782719,-.0851232987247891,.5241648018700465,0,5.159190588235296,0,0,0,1,0];this._loadMatrix(e,t)}colorTone(t,e,i,s,n){t||(t=.2),e||(e=.15),i||(i=16770432),s||(s=3375104);const a=J.shared,[o,l,u]=a.setValue(i).toArray(),[c,h,p]=a.setValue(s).toArray(),f=[.3,.59,.11,0,0,o,l,u,t,0,c,h,p,e,0,o-c,l-h,u-p,0,0];this._loadMatrix(f,n)}night(t,e){t||(t=.1);const i=[t*-2,-t,0,0,0,-t,0,t,0,0,0,t,t*2,0,0,0,0,0,1,0];this._loadMatrix(i,e)}predator(t,e){const i=[11.224130630493164*t,-4.794486999511719*t,-2.8746118545532227*t,0*t,.40342438220977783*t,-3.6330697536468506*t,9.193157196044922*t,-2.951810836791992*t,0*t,-1.316135048866272*t,-3.2184197902679443*t,-4.2375030517578125*t,7.476448059082031*t,0*t,.8044459223747253*t,0,0,0,1,0];this._loadMatrix(i,e)}lsd(t){const e=[2,-.4,.5,0,0,-.5,2,-.4,0,0,-.4,-.5,3,0,0,0,0,0,1,0];this._loadMatrix(e,t)}reset(){const t=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];this._loadMatrix(t,!1)}get matrix(){return this.resources.colorMatrixUniforms.uniforms.uColorMatrix}set matrix(t){this.resources.colorMatrixUniforms.uniforms.uColorMatrix=t}get alpha(){return this.resources.colorMatrixUniforms.uniforms.uAlpha}set alpha(t){this.resources.colorMatrixUniforms.uniforms.uAlpha=t}}var A_=` +in vec2 vTextureCoord; +in vec2 vFilterUv; + +out vec4 finalColor; + +uniform sampler2D uTexture; +uniform sampler2D uMapTexture; + +uniform vec4 uInputClamp; +uniform highp vec4 uInputSize; +uniform mat2 uRotation; +uniform vec2 uScale; + +void main() +{ + vec4 map = texture(uMapTexture, vFilterUv); + + vec2 offset = uInputSize.zw * (uRotation * (map.xy - 0.5)) * uScale; + + finalColor = texture(uTexture, clamp(vTextureCoord + offset, uInputClamp.xy, uInputClamp.zw)); +} +`,C_=`in vec2 aPosition; +out vec2 vTextureCoord; +out vec2 vFilterUv; + + +uniform vec4 uInputSize; +uniform vec4 uOutputFrame; +uniform vec4 uOutputTexture; + +uniform mat3 uFilterMatrix; + +vec4 filterVertexPosition( void ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +vec2 filterTextureCoord( void ) +{ + return aPosition * (uOutputFrame.zw * uInputSize.zw); +} + +vec2 getFilterCoord( void ) +{ + return ( uFilterMatrix * vec3( filterTextureCoord(), 1.0) ).xy; +} + + +void main(void) +{ + gl_Position = filterVertexPosition(); + vTextureCoord = filterTextureCoord(); + vFilterUv = getFilterCoord(); +} +`,Ol=` +struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct DisplacementUniforms { + uFilterMatrix:mat3x3, + uScale:vec2, + uRotation:mat2x2 +}; + + + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; + +@group(1) @binding(0) var filterUniforms : DisplacementUniforms; +@group(1) @binding(1) var uMapTexture: texture_2d; +@group(1) @binding(2) var uMapSampler : sampler; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2, + @location(1) filterUv : vec2, + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +fn getFilterCoord(aPosition:vec2 ) -> vec2 +{ + return ( filterUniforms.uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy; +} + +fn getSize() -> vec2 +{ + + + return gfu.uGlobalFrame.zw; +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition), + getFilterCoord(aPosition) + ); +} + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @location(1) filterUv: vec2, + @builtin(position) position: vec4 +) -> @location(0) vec4 { + + var map = textureSample(uMapTexture, uMapSampler, filterUv); + + var offset = gfu.uInputSize.zw * (filterUniforms.uRotation * (map.xy - 0.5)) * filterUniforms.uScale; + + return textureSample(uTexture, uSampler, clamp(uv + offset, gfu.uInputClamp.xy, gfu.uInputClamp.zw)); +}`,zA=Object.defineProperty,WA=Object.defineProperties,VA=Object.getOwnPropertyDescriptors,en=Object.getOwnPropertySymbols,R_=Object.prototype.hasOwnProperty,M_=Object.prototype.propertyIsEnumerable,O_=(r,t,e)=>t in r?zA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,YA=(r,t)=>{for(var e in t||(t={}))R_.call(t,e)&&O_(r,e,t[e]);if(en)for(var e of en(t))M_.call(t,e)&&O_(r,e,t[e]);return r},KA=(r,t)=>WA(r,VA(t)),qA=(r,t)=>{var e={};for(var i in r)R_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&en)for(var i of en(r))t.indexOf(i)<0&&M_.call(r,i)&&(e[i]=r[i]);return e};class ZA extends Se{constructor(...t){let e=t[0];e instanceof he&&(e={sprite:e,scale:t[1]});const i=e,{sprite:s,scale:n}=i,a=qA(i,["sprite","scale"]);let o=n!=null?n:20;typeof o=="number"&&(o=new rt(o,o));const l=new wt({uFilterMatrix:{value:new D,type:"mat3x3"},uScale:{value:o,type:"vec2"},uRotation:{value:new Float32Array([0,0,0,0]),type:"mat2x2"}}),u=Ht.from({vertex:C_,fragment:A_,name:"displacement-filter"}),c=kt.from({vertex:{source:Ol,entryPoint:"mainVertex"},fragment:{source:Ol,entryPoint:"mainFragment"}}),h=s.texture.source;super(KA(YA({},a),{gpuProgram:c,glProgram:u,resources:{filterUniforms:l,uMapTexture:h,uMapSampler:h.style}})),this._sprite=e.sprite,this._sprite.renderable=!1}apply(t,e,i,s){const n=this.resources.filterUniforms.uniforms;t.calculateSpriteMatrix(n.uFilterMatrix,this._sprite);const a=this._sprite.worldTransform,o=Math.sqrt(a.a*a.a+a.b*a.b),l=Math.sqrt(a.c*a.c+a.d*a.d);o!==0&&l!==0&&(n.uRotation[0]=a.a/o,n.uRotation[1]=a.b/o,n.uRotation[2]=a.c/l,n.uRotation[3]=a.d/l),this.resources.uMapTexture=this._sprite.texture.source,t.applyFilter(this,e,i,s)}get scale(){return this.resources.filterUniforms.uniforms.uScale}}var G_=` +in vec2 vTextureCoord; +in vec4 vColor; + +out vec4 finalColor; + +uniform float uNoise; +uniform float uSeed; +uniform sampler2D uTexture; + +float rand(vec2 co) +{ + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() +{ + vec4 color = texture(uTexture, vTextureCoord); + float randomValue = rand(gl_FragCoord.xy * uSeed); + float diff = (randomValue - 0.5) * uNoise; + + // Un-premultiply alpha before applying the color matrix. See issue #3539. + if (color.a > 0.0) { + color.rgb /= color.a; + } + + color.r += diff; + color.g += diff; + color.b += diff; + + // Premultiply alpha again. + color.rgb *= color.a; + + finalColor = color; +} +`,Gl=` + +struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct NoiseUniforms { + uNoise:f32, + uSeed:f32, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; + +@group(1) @binding(0) var noiseUniforms : NoiseUniforms; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2 + }; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +fn getSize() -> vec2 +{ + return gfu.uGlobalFrame.zw; +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition) + ); +} + +fn rand(co:vec2) -> f32 +{ + return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + + + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @builtin(position) position: vec4 +) -> @location(0) vec4 { + + var pixelPosition = globalTextureCoord(position.xy);// / (getSize());//- gfu.uOutputFrame.xy); + + + var sample = textureSample(uTexture, uSampler, uv); + var randomValue = rand(pixelPosition.xy * noiseUniforms.uSeed); + var diff = (randomValue - 0.5) * noiseUniforms.uNoise; + + // Un-premultiply alpha before applying the color matrix. See issue #3539. + if (sample.a > 0.0) { + sample.r /= sample.a; + sample.g /= sample.a; + sample.b /= sample.a; + } + + sample.r += diff; + sample.g += diff; + sample.b += diff; + + // Premultiply alpha again. + sample.r *= sample.a; + sample.g *= sample.a; + sample.b *= sample.a; + + return sample; +}`,QA=Object.defineProperty,JA=Object.defineProperties,tC=Object.getOwnPropertyDescriptors,rn=Object.getOwnPropertySymbols,I_=Object.prototype.hasOwnProperty,B_=Object.prototype.propertyIsEnumerable,F_=(r,t,e)=>t in r?QA(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Il=(r,t)=>{for(var e in t||(t={}))I_.call(t,e)&&F_(r,e,t[e]);if(rn)for(var e of rn(t))B_.call(t,e)&&F_(r,e,t[e]);return r},eC=(r,t)=>JA(r,tC(t)),rC=(r,t)=>{var e={};for(var i in r)I_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&rn)for(var i of rn(r))t.indexOf(i)<0&&B_.call(r,i)&&(e[i]=r[i]);return e};const D_=class n1 extends Se{constructor(t={}){t=Il(Il({},n1.defaultOptions),t);const e=kt.from({vertex:{source:Gl,entryPoint:"mainVertex"},fragment:{source:Gl,entryPoint:"mainFragment"}}),i=Ht.from({vertex:ci,fragment:G_,name:"noise-filter"}),s=t,{noise:n,seed:a}=s,o=rC(s,["noise","seed"]);super(eC(Il({},o),{gpuProgram:e,glProgram:i,resources:{noiseUniforms:new wt({uNoise:{value:1,type:"f32"},uSeed:{value:1,type:"f32"}})}})),this.noise=n,this.seed=a!=null?a:Math.random()}get noise(){return this.resources.noiseUniforms.uniforms.uNoise}set noise(t){this.resources.noiseUniforms.uniforms.uNoise=t}get seed(){return this.resources.noiseUniforms.uniforms.uSeed}set seed(t){this.resources.noiseUniforms.uniforms.uSeed=t}};D_.defaultOptions={noise:.5};let iC=D_;var U_=`in vec2 vMaskCoord; +in vec2 vTextureCoord; + +uniform sampler2D uTexture; +uniform sampler2D uMaskTexture; + +uniform float uAlpha; +uniform vec4 uMaskClamp; +uniform float uInverse; + +out vec4 finalColor; + +void main(void) +{ + float clip = step(3.5, + step(uMaskClamp.x, vMaskCoord.x) + + step(uMaskClamp.y, vMaskCoord.y) + + step(vMaskCoord.x, uMaskClamp.z) + + step(vMaskCoord.y, uMaskClamp.w)); + + // TODO look into why this is needed + float npmAlpha = uAlpha; + vec4 original = texture(uTexture, vTextureCoord); + vec4 masky = texture(uMaskTexture, vMaskCoord); + float alphaMul = 1.0 - npmAlpha * (1.0 - masky.a); + + float a = alphaMul * masky.r * npmAlpha * clip; + + if (uInverse == 1.0) { + a = 1.0 - a; + } + + finalColor = original * a; +} +`,k_=`in vec2 aPosition; + +out vec2 vTextureCoord; +out vec2 vMaskCoord; + + +uniform vec4 uInputSize; +uniform vec4 uOutputFrame; +uniform vec4 uOutputTexture; +uniform mat3 uFilterMatrix; + +vec4 filterVertexPosition( vec2 aPosition ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +vec2 filterTextureCoord( vec2 aPosition ) +{ + return aPosition * (uOutputFrame.zw * uInputSize.zw); +} + +vec2 getFilterCoord( vec2 aPosition ) +{ + return ( uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy; +} + +void main(void) +{ + gl_Position = filterVertexPosition(aPosition); + vTextureCoord = filterTextureCoord(aPosition); + vMaskCoord = getFilterCoord(aPosition); +} +`,Bl=`struct GlobalFilterUniforms { + uInputSize:vec4, + uInputPixel:vec4, + uInputClamp:vec4, + uOutputFrame:vec4, + uGlobalFrame:vec4, + uOutputTexture:vec4, +}; + +struct MaskUniforms { + uFilterMatrix:mat3x3, + uMaskClamp:vec4, + uAlpha:f32, + uInverse:f32, +}; + +@group(0) @binding(0) var gfu: GlobalFilterUniforms; +@group(0) @binding(1) var uTexture: texture_2d; +@group(0) @binding(2) var uSampler : sampler; + +@group(1) @binding(0) var filterUniforms : MaskUniforms; +@group(1) @binding(1) var uMaskTexture: texture_2d; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2, + @location(1) filterUv : vec2, +}; + +fn filterVertexPosition(aPosition:vec2) -> vec4 +{ + var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy; + + position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + +fn filterTextureCoord( aPosition:vec2 ) -> vec2 +{ + return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw); +} + +fn globalTextureCoord( aPosition:vec2 ) -> vec2 +{ + return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); +} + +fn getFilterCoord(aPosition:vec2 ) -> vec2 +{ + return ( filterUniforms.uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy; +} + +fn getSize() -> vec2 +{ + return gfu.uGlobalFrame.zw; +} + +@vertex +fn mainVertex( + @location(0) aPosition : vec2, +) -> VSOutput { + return VSOutput( + filterVertexPosition(aPosition), + filterTextureCoord(aPosition), + getFilterCoord(aPosition) + ); +} + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @location(1) filterUv: vec2, + @builtin(position) position: vec4 +) -> @location(0) vec4 { + + var maskClamp = filterUniforms.uMaskClamp; + var uAlpha = filterUniforms.uAlpha; + + var clip = step(3.5, + step(maskClamp.x, filterUv.x) + + step(maskClamp.y, filterUv.y) + + step(filterUv.x, maskClamp.z) + + step(filterUv.y, maskClamp.w)); + + var mask = textureSample(uMaskTexture, uSampler, filterUv); + var source = textureSample(uTexture, uSampler, uv); + var alphaMul = 1.0 - uAlpha * (1.0 - mask.a); + + var a: f32 = alphaMul * mask.r * uAlpha * clip; + + if (filterUniforms.uInverse == 1.0) { + a = 1.0 - a; + } + + return source * a; +} +`,sC=Object.defineProperty,nC=Object.defineProperties,aC=Object.getOwnPropertyDescriptors,sn=Object.getOwnPropertySymbols,$_=Object.prototype.hasOwnProperty,L_=Object.prototype.propertyIsEnumerable,N_=(r,t,e)=>t in r?sC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,oC=(r,t)=>{for(var e in t||(t={}))$_.call(t,e)&&N_(r,e,t[e]);if(sn)for(var e of sn(t))L_.call(t,e)&&N_(r,e,t[e]);return r},lC=(r,t)=>nC(r,aC(t)),uC=(r,t)=>{var e={};for(var i in r)$_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&sn)for(var i of sn(r))t.indexOf(i)<0&&L_.call(r,i)&&(e[i]=r[i]);return e};class X_ extends Se{constructor(t){const e=t,{sprite:i}=e,s=uC(e,["sprite"]),n=new Pa(i.texture),a=new wt({uFilterMatrix:{value:new D,type:"mat3x3"},uMaskClamp:{value:n.uClampFrame,type:"vec4"},uAlpha:{value:1,type:"f32"},uInverse:{value:t.inverse?1:0,type:"f32"}}),o=kt.from({vertex:{source:Bl,entryPoint:"mainVertex"},fragment:{source:Bl,entryPoint:"mainFragment"}}),l=Ht.from({vertex:k_,fragment:U_,name:"mask-filter"});super(lC(oC({},s),{gpuProgram:o,glProgram:l,clipToViewport:!1,resources:{filterUniforms:a,uMaskTexture:i.texture.source}})),this.sprite=i,this._textureMatrix=n}set inverse(t){this.resources.filterUniforms.uniforms.uInverse=t?1:0}get inverse(){return this.resources.filterUniforms.uniforms.uInverse===1}apply(t,e,i,s){this._textureMatrix.texture=this.sprite.texture,t.calculateSpriteMatrix(this.resources.filterUniforms.uniforms.uFilterMatrix,this.sprite).prepend(this._textureMatrix.mapCoord),this.resources.uMaskTexture=this.sprite.texture.source,t.applyFilter(this,e,i,s)}}var cC=`fn getLuminosity(c: vec3) -> f32 { + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; +} + +fn setLuminosity(c: vec3, lum: f32) -> vec3 { + let d: f32 = lum - getLuminosity(c); + let newColor: vec3 = c.rgb + vec3(d, d, d); + + // clip back into legal range + let newLum: f32 = getLuminosity(newColor); + let cMin: f32 = min(newColor.r, min(newColor.g, newColor.b)); + let cMax: f32 = max(newColor.r, max(newColor.g, newColor.b)); + + let t1: f32 = newLum / (newLum - cMin); + let t2: f32 = (1.0 - newLum) / (cMax - newLum); + + let finalColor = mix(vec3(newLum, newLum, newLum), newColor, select(select(1.0, t2, cMax > 1.0), t1, cMin < 0.0)); + + return finalColor; +} + +fn getSaturation(c: vec3) -> f32 { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); +} + +// Set saturation if color components are sorted in ascending order. +fn setSaturationMinMidMax(cSorted: vec3, s: f32) -> vec3 { + var result: vec3; + if (cSorted.z > cSorted.x) { + let newY = (((cSorted.y - cSorted.x) * s) / (cSorted.z - cSorted.x)); + result = vec3(0.0, newY, s); + } else { + result = vec3(0.0, 0.0, 0.0); + } + return vec3(result.x, result.y, result.z); +} + +fn setSaturation(c: vec3, s: f32) -> vec3 { + var result: vec3 = c; + + if (c.r <= c.g && c.r <= c.b) { + if (c.g <= c.b) { + result = setSaturationMinMidMax(result, s); + } else { + var temp: vec3 = vec3(result.r, result.b, result.g); + temp = setSaturationMinMidMax(temp, s); + result = vec3(temp.r, temp.b, temp.g); + } + } else if (c.g <= c.r && c.g <= c.b) { + if (c.r <= c.b) { + var temp: vec3 = vec3(result.g, result.r, result.b); + temp = setSaturationMinMidMax(temp, s); + result = vec3(temp.g, temp.r, temp.b); + } else { + var temp: vec3 = vec3(result.g, result.b, result.r); + temp = setSaturationMinMidMax(temp, s); + result = vec3(temp.g, temp.b, temp.r); + } + } else { + if (c.r <= c.g) { + var temp: vec3 = vec3(result.b, result.r, result.g); + temp = setSaturationMinMidMax(temp, s); + result = vec3(temp.b, temp.r, temp.g); + } else { + var temp: vec3 = vec3(result.b, result.g, result.r); + temp = setSaturationMinMidMax(temp, s); + result = vec3(temp.b, temp.g, temp.r); + } + } + + return result; +}`;const H_=class a1{constructor(t){this._tick=()=>{this._destroyed||(this.timeout=setTimeout(this._processQueue,0))},this._processQueue=()=>{if(this._destroyed)return;const{queue:e}=this;let i=0;for(;e.length&&i{this.queue.length?(this.resolves.push(e),this.dedupeQueue(),Ct.system.addOnce(this._tick,this,ve.UTILITY)):e()})}dedupeQueue(){const t=Object.create(null);let e=0;for(let i=0;i>16&255)/255,e[i++]=(r>>8&255)/255,e[i++]=(r&255)/255,e[i++]=t}function Lr(r,t,e){const i=(r>>24&255)/255;t[e++]=(r&255)/255*i,t[e++]=(r>>8&255)/255*i,t[e++]=(r>>16&255)/255*i,t[e++]=i}class W_{constructor(){this.batches=[],this.batched=!1}destroy(){this.batches.forEach(t=>{St.return(t)}),this.batches.length=0}}class Ul{constructor(t,e){this.state=jt.for2d(),this.renderer=t,this._adaptor=e,this.renderer.runners.contextChange.add(this),this._managedGraphics=new Ft({renderer:t,type:"renderable",priority:-1,name:"graphics"})}contextChange(){this._adaptor.contextChange(this.renderer)}validateRenderable(t){const e=t.context,i=!!t._gpuData,s=this.renderer.graphicsContext.updateGpuContext(e);return!!(s.isBatchable||i!==s.isBatchable)}addRenderable(t,e){const i=this.renderer.graphicsContext.updateGpuContext(t.context);t.didViewUpdate&&this._rebuild(t),i.isBatchable?this._addToBatcher(t,e):(this.renderer.renderPipes.batch.break(e),e.add(t))}updateRenderable(t){const e=this._getGpuDataForRenderable(t).batches;for(let i=0;i{const o=St.get(Fs);return a.copyTo(o),o.renderable=t,o.roundPixels=n,o})}destroy(){this._managedGraphics.destroy(),this.renderer=null,this._adaptor.destroy(),this._adaptor=null,this.state=null}}Ul.extension={type:[T.WebGLPipes,T.WebGPUPipes],name:"graphics"},$.add(Dl),$.add(Ul),$.add(z_),$.add(ks);var fC=Object.defineProperty,nn=Object.getOwnPropertySymbols,V_=Object.prototype.hasOwnProperty,Y_=Object.prototype.propertyIsEnumerable,K_=(r,t,e)=>t in r?fC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,mC=(r,t)=>{for(var e in t||(t={}))V_.call(t,e)&&K_(r,e,t[e]);if(nn)for(var e of nn(t))Y_.call(t,e)&&K_(r,e,t[e]);return r},gC=(r,t)=>{var e={};for(var i in r)V_.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&nn)for(var i of nn(r))t.indexOf(i)<0&&Y_.call(r,i)&&(e[i]=r[i]);return e};class ar extends xe{constructor(t){t instanceof $t&&(t={context:t});const e=t||{},{context:i,roundPixels:s}=e,n=gC(e,["context","roundPixels"]);super(mC({label:"Graphics"},n)),this.renderPipeId="graphics",i?this.context=i:(this.context=this._ownedContext=new $t,this.context.autoGarbageCollect=this.autoGarbageCollect),this.didViewUpdate=!0,this.allowChildren=!1,this.roundPixels=s!=null?s:!1}set context(t){t!==this._context&&(this._context&&(this._context.off("update",this.onViewUpdate,this),this._context.off("unload",this.unload,this)),this._context=t,this._context.on("update",this.onViewUpdate,this),this._context.on("unload",this.unload,this),this.onViewUpdate())}get context(){return this._context}get bounds(){return this._context.bounds}updateBounds(){}containsPoint(t){return this._context.containsPoint(t)}destroy(t){this._ownedContext&&!t?this._ownedContext.destroy(t):(t===!0||(t==null?void 0:t.context)===!0)&&this._context.destroy(t),this._ownedContext=null,this._context=null,super.destroy(t)}_onTouch(t){this._gcLastUsed=t,this._context._gcLastUsed=t}_callContextMethod(t,e){return this.context[t](...e),this}setFillStyle(...t){return this._callContextMethod("setFillStyle",t)}setStrokeStyle(...t){return this._callContextMethod("setStrokeStyle",t)}fill(...t){return this._callContextMethod("fill",t)}stroke(...t){return this._callContextMethod("stroke",t)}texture(...t){return this._callContextMethod("texture",t)}beginPath(){return this._callContextMethod("beginPath",[])}cut(){return this._callContextMethod("cut",[])}arc(...t){return this._callContextMethod("arc",t)}arcTo(...t){return this._callContextMethod("arcTo",t)}arcToSvg(...t){return this._callContextMethod("arcToSvg",t)}bezierCurveTo(...t){return this._callContextMethod("bezierCurveTo",t)}closePath(){return this._callContextMethod("closePath",[])}ellipse(...t){return this._callContextMethod("ellipse",t)}circle(...t){return this._callContextMethod("circle",t)}path(...t){return this._callContextMethod("path",t)}lineTo(...t){return this._callContextMethod("lineTo",t)}moveTo(...t){return this._callContextMethod("moveTo",t)}quadraticCurveTo(...t){return this._callContextMethod("quadraticCurveTo",t)}rect(...t){return this._callContextMethod("rect",t)}roundRect(...t){return this._callContextMethod("roundRect",t)}poly(...t){return this._callContextMethod("poly",t)}regularPoly(...t){return this._callContextMethod("regularPoly",t)}roundPoly(...t){return this._callContextMethod("roundPoly",t)}roundShape(...t){return this._callContextMethod("roundShape",t)}filletRect(...t){return this._callContextMethod("filletRect",t)}chamferRect(...t){return this._callContextMethod("chamferRect",t)}star(...t){return this._callContextMethod("star",t)}svg(...t){return this._callContextMethod("svg",t)}restore(...t){return this._callContextMethod("restore",t)}save(){return this._callContextMethod("save",[])}getTransform(){return this.context.getTransform()}resetTransform(){return this._callContextMethod("resetTransform",[])}rotateTransform(...t){return this._callContextMethod("rotate",t)}scaleTransform(...t){return this._callContextMethod("scale",t)}setTransform(...t){return this._callContextMethod("setTransform",t)}transform(...t){return this._callContextMethod("transform",t)}translateTransform(...t){return this._callContextMethod("translate",t)}clear(){return this._callContextMethod("clear",[])}get fillStyle(){return this._context.fillStyle}set fillStyle(t){this._context.fillStyle=t}get strokeStyle(){return this._context.strokeStyle}set strokeStyle(t){this._context.strokeStyle=t}clone(t=!1){return t?new ar(this._context.clone()):(this._ownedContext=null,new ar(this._context))}lineStyle(t,e,i){const s={};return t&&(s.width=t),e&&(s.color=e),i&&(s.alpha=i),this.context.strokeStyle=s,this}beginFill(t,e){const i={};return t!==void 0&&(i.color=t),e!==void 0&&(i.alpha=e),this.context.fillStyle=i,this}endFill(){this.context.fill();const t=this.context.strokeStyle;return(t.width!==$t.defaultStrokeStyle.width||t.color!==$t.defaultStrokeStyle.color||t.alpha!==$t.defaultStrokeStyle.alpha)&&this.context.stroke(),this}drawCircle(...t){return this._callContextMethod("circle",t)}drawEllipse(...t){return this._callContextMethod("ellipse",t)}drawPolygon(...t){return this._callContextMethod("poly",t)}drawRect(...t){return this._callContextMethod("rect",t)}drawRoundedRect(...t){return this._callContextMethod("roundRect",t)}drawStar(...t){return this._callContextMethod("star",t)}}var _C=Object.defineProperty,q_=Object.getOwnPropertySymbols,bC=Object.prototype.hasOwnProperty,vC=Object.prototype.propertyIsEnumerable,Z_=(r,t,e)=>t in r?_C(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Q_=(r,t)=>{for(var e in t||(t={}))bC.call(t,e)&&Z_(r,e,t[e]);if(q_)for(var e of q_(t))vC.call(t,e)&&Z_(r,e,t[e]);return r};const J_=class o1 extends Qe{constructor(...t){var e;let i=(e=t[0])!=null?e:{};i instanceof Float32Array&&(i={positions:i,uvs:t[1],indices:t[2]}),i=Q_(Q_({},o1.defaultOptions),i);const s=i.positions||new Float32Array([0,0,1,0,1,1,0,1]);let n=i.uvs;n||(i.positions?n=new Float32Array(s.length):n=new Float32Array([0,0,1,0,1,1,0,1]));const a=i.indices||new Uint32Array([0,1,2,0,2,3]),o=i.shrinkBuffersToFit,l=new zt({data:s,label:"attribute-mesh-positions",shrinkToFit:o,usage:et.VERTEX|et.COPY_DST}),u=new zt({data:n,label:"attribute-mesh-uvs",shrinkToFit:o,usage:et.VERTEX|et.COPY_DST}),c=new zt({data:a,label:"index-mesh-buffer",shrinkToFit:o,usage:et.INDEX|et.COPY_DST});super({attributes:{aPosition:{buffer:l,format:"float32x2",stride:8,offset:0},aUV:{buffer:u,format:"float32x2",stride:8,offset:0}},indexBuffer:c,topology:i.topology}),this.batchMode="auto"}get positions(){return this.attributes.aPosition.buffer.data}set positions(t){this.attributes.aPosition.buffer.data=t}get uvs(){return this.attributes.aUV.buffer.data}set uvs(t){this.attributes.aUV.buffer.data=t}get indices(){return this.indexBuffer.data}set indices(t){this.indexBuffer.data=t}};J_.defaultOptions={topology:"triangle-list",shrinkBuffersToFit:!1};let Le=J_;class an{constructor(){this.batcherName="default",this.packAsQuad=!1,this.indexOffset=0,this.attributeOffset=0,this.roundPixels=0,this._batcher=null,this._batch=null,this._textureMatrixUpdateId=-1,this._uvUpdateId=-1}get blendMode(){return this.renderable.groupBlendMode}get topology(){return this._topology||this.geometry.topology}set topology(t){this._topology=t}reset(){this.renderable=null,this.texture=null,this._batcher=null,this._batch=null,this.geometry=null,this._uvUpdateId=-1,this._textureMatrixUpdateId=-1}setTexture(t){this.texture!==t&&(this.texture=t,this._textureMatrixUpdateId=-1)}get uvs(){const t=this.geometry.getBuffer("aUV"),e=t.data;let i=e;const s=this.texture.textureMatrix;return s.isSimple||(i=this._transformedUvs,(this._textureMatrixUpdateId!==s._updateID||this._uvUpdateId!==t._updateID)&&((!i||i.length"},uColor:{value:new Float32Array([1,1,1,1]),type:"vec4"},uRound:{value:0,type:"f32"}}),this.localUniformsBindGroup=new Te({0:this.localUniforms}),this.renderer=t,this._adaptor=e,this._adaptor.init()}validateRenderable(t){const e=this._getMeshData(t),i=e.batched,s=t.batched;if(e.batched=s,i!==s)return!0;if(s){const n=t._geometry;if(n.indices.length!==e.indexSize||n.positions.length!==e.vertexSize)return e.indexSize=n.indices.length,e.vertexSize=n.positions.length,!0;const a=this._getBatchableMesh(t);return a.texture.uid!==t._texture.uid&&(a._textureMatrixUpdateId=-1),!a._batcher.checkAndUpdateTexture(a,t._texture)}return!1}addRenderable(t,e){var i,s;const n=this.renderer.renderPipes.batch,a=this._getMeshData(t);if(t.didViewUpdate&&(a.indexSize=(i=t._geometry.indices)==null?void 0:i.length,a.vertexSize=(s=t._geometry.positions)==null?void 0:s.length),a.batched){const o=this._getBatchableMesh(t);o.setTexture(t._texture),o.geometry=t._geometry,n.addToBatch(o,e)}else n.break(e),e.add(t)}updateRenderable(t){if(t.batched){const e=this._getBatchableMesh(t);e.setTexture(t._texture),e.geometry=t._geometry,e._batcher.updateElement(e)}}execute(t){if(!t.isRenderable)return;t.state.blendMode=Pr(t.groupBlendMode,t.texture._source);const e=this.localUniforms;e.uniforms.uTransformMatrix=t.groupTransform,e.uniforms.uRound=this.renderer._roundPixels|t._roundPixels,e.update(),Lr(t.groupColorAlpha,e.uniforms.uColor,0),this._adaptor.execute(this,t)}_getMeshData(t){var e,i;return(e=t._gpuData)[i=this.renderer.uid]||(e[i]=new kl),t._gpuData[this.renderer.uid].meshData||this._initMeshData(t)}_initMeshData(t){return t._gpuData[this.renderer.uid].meshData={batched:t.batched,indexSize:0,vertexSize:0},t._gpuData[this.renderer.uid].meshData}_getBatchableMesh(t){var e,i;return(e=t._gpuData)[i=this.renderer.uid]||(e[i]=new kl),t._gpuData[this.renderer.uid].batchableMesh||this._initBatchableMesh(t)}_initBatchableMesh(t){const e=new an;return e.renderable=t,e.setTexture(t._texture),e.transform=t.groupTransform,e.roundPixels=this.renderer._roundPixels|t._roundPixels,t._gpuData[this.renderer.uid].batchableMesh=e,e}destroy(){this.localUniforms=null,this.localUniformsBindGroup=null,this._adaptor.destroy(),this._adaptor=null,this.renderer=null}}$l.extension={type:[T.WebGLPipes,T.WebGPUPipes],name:"mesh"},$.add($l);var yC=Object.defineProperty,on=Object.getOwnPropertySymbols,tb=Object.prototype.hasOwnProperty,eb=Object.prototype.propertyIsEnumerable,rb=(r,t,e)=>t in r?yC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,xC=(r,t)=>{for(var e in t||(t={}))tb.call(t,e)&&rb(r,e,t[e]);if(on)for(var e of on(t))eb.call(t,e)&&rb(r,e,t[e]);return r},TC=(r,t)=>{var e={};for(var i in r)tb.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&on)for(var i of on(r))t.indexOf(i)<0&&eb.call(r,i)&&(e[i]=r[i]);return e};class Nr extends xe{constructor(...t){var e;let i=t[0];i instanceof Qe&&(i={geometry:i,shader:t[1]},t[3]&&(i.geometry.topology=t[3]));const s=i,{geometry:n,shader:a,texture:o,roundPixels:l,state:u}=s,c=TC(s,["geometry","shader","texture","roundPixels","state"]);super(xC({label:"Mesh"},c)),this.renderPipeId="mesh",this._shader=null,this.allowChildren=!1,this.shader=a!=null?a:null,this.texture=(e=o!=null?o:a==null?void 0:a.texture)!=null?e:F.WHITE,this.state=u!=null?u:jt.for2d(),this._geometry=n,this._geometry.on("update",this.onViewUpdate,this),this.roundPixels=l!=null?l:!1}get material(){return this._shader}set shader(t){this._shader!==t&&(this._shader=t,this.onViewUpdate())}get shader(){return this._shader}set geometry(t){var e;this._geometry!==t&&((e=this._geometry)==null||e.off("update",this.onViewUpdate,this),t.on("update",this.onViewUpdate,this),this._geometry=t,this.onViewUpdate())}get geometry(){return this._geometry}set texture(t){t||(t=F.EMPTY);const e=this._texture;e!==t&&(e&&e.dynamic&&e.off("update",this.onViewUpdate,this),t.dynamic&&t.on("update",this.onViewUpdate,this),this.shader&&(this.shader.texture=t),this._texture=t,this.onViewUpdate())}get texture(){return this._texture}get batched(){return this._shader||(this.state.data&12)!==0?!1:this._geometry instanceof Le?this._geometry.batchMode==="auto"?this._geometry.positions.length/2<=100:this._geometry.batchMode==="batch":!1}get bounds(){return this._geometry.bounds}updateBounds(){this._bounds=this._geometry.bounds}containsPoint(t){const{x:e,y:i}=t;if(!this.bounds.containsPoint(e,i))return!1;const s=this.geometry.getBuffer("aPosition").data,n=this.geometry.topology==="triangle-strip"?3:1;if(this.geometry.getIndex()){const a=this.geometry.getIndex().data,o=a.length;for(let l=0;l+2t in r?SC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,PC=(r,t)=>{for(var e in t||(t={}))ib.call(t,e)&&nb(r,e,t[e]);if(ln)for(var e of ln(t))sb.call(t,e)&&nb(r,e,t[e]);return r},AC=(r,t)=>wC(r,EC(t)),CC=(r,t)=>{var e={};for(var i in r)ib.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&ln)for(var i of ln(r))t.indexOf(i)<0&&sb.call(r,i)&&(e[i]=r[i]);return e};class Fi extends he{constructor(...t){let e=t[0];Array.isArray(t[0])&&(e={textures:t[0],autoUpdate:t[1]});const i=e,{animationSpeed:s=1,autoPlay:n=!1,autoUpdate:a=!0,loop:o=!0,onComplete:l=null,onFrameChange:u=null,onLoop:c=null,textures:h,updateAnchor:p=!1}=i,f=CC(i,["animationSpeed","autoPlay","autoUpdate","loop","onComplete","onFrameChange","onLoop","textures","updateAnchor"]),[m]=h;super(AC(PC({},f),{texture:m instanceof F?m:m.texture})),this._textures=null,this._durations=null,this._autoUpdate=a,this._isConnectedToTicker=!1,this.animationSpeed=s,this.loop=o,this.updateAnchor=p,this.onComplete=l,this.onFrameChange=u,this.onLoop=c,this._currentTime=0,this._playing=!1,this._previousFrame=null,this.textures=h,n&&this.play()}stop(){this._playing&&(this._playing=!1,this._autoUpdate&&this._isConnectedToTicker&&(Ct.shared.remove(this.update,this),this._isConnectedToTicker=!1))}play(){this._playing||(this._playing=!0,this._autoUpdate&&!this._isConnectedToTicker&&(Ct.shared.add(this.update,this,ve.HIGH),this._isConnectedToTicker=!0))}gotoAndStop(t){this.stop(),this.currentFrame=t}gotoAndPlay(t){this.currentFrame=t,this.play()}update(t){if(!this._playing)return;const e=t.deltaTime,i=this.animationSpeed*e,s=this.currentFrame;if(this._durations!==null){let n=this._currentTime%1*this._durations[this.currentFrame];for(n+=i/60*1e3;n<0;)this._currentTime--,n+=this._durations[this.currentFrame];const a=Math.sign(this.animationSpeed*e);for(this._currentTime=Math.floor(this._currentTime);n>=this._durations[this.currentFrame];)n-=this._durations[this.currentFrame]*a,this._currentTime+=a;this._currentTime+=n/this._durations[this.currentFrame]}else this._currentTime+=i;this._currentTime<0&&!this.loop?(this.gotoAndStop(0),this.onComplete&&this.onComplete()):this._currentTime>=this._textures.length&&!this.loop?(this.gotoAndStop(this._textures.length-1),this.onComplete&&this.onComplete()):s!==this.currentFrame&&(this.loop&&this.onLoop&&(this.animationSpeed>0&&this.currentFrames)&&this.onLoop(),this._updateTexture())}_updateTexture(){const t=this.currentFrame;this._previousFrame!==t&&(this._previousFrame=t,this.texture=this._textures[t],this.updateAnchor&&this.texture.defaultAnchor&&this.anchor.copyFrom(this.texture.defaultAnchor),this.onFrameChange&&this.onFrameChange(this.currentFrame))}destroy(t=!1){if(typeof t=="boolean"?t:t!=null&&t.texture){const e=typeof t=="boolean"?t:t==null?void 0:t.textureSource;this._textures.forEach(i=>{this.texture!==i&&i.destroy(e)})}this._textures=[],this._durations=null,this.stop(),super.destroy(t),this.onComplete=null,this.onFrameChange=null,this.onLoop=null}static fromFrames(t){const e=[];for(let i=0;ithis.totalFrames-1)throw new Error(`[AnimatedSprite]: Invalid frame index value ${t}, expected to be between 0 and totalFrames ${this.totalFrames}.`);const e=this.currentFrame;this._currentTime=t,e!==this.currentFrame&&this._updateTexture()}get playing(){return this._playing}get autoUpdate(){return this._autoUpdate}set autoUpdate(t){t!==this._autoUpdate&&(this._autoUpdate=t,!this._autoUpdate&&this._isConnectedToTicker?(Ct.shared.remove(this.update,this),this._isConnectedToTicker=!1):this._autoUpdate&&!this._isConnectedToTicker&&this._playing&&(Ct.shared.add(this.update,this),this._isConnectedToTicker=!0))}}class ab{constructor({matrix:t,observer:e}={}){this.dirty=!0,this._matrix=t!=null?t:new D,this.observer=e,this.position=new gt(this,0,0),this.scale=new gt(this,1,1),this.pivot=new gt(this,0,0),this.skew=new gt(this,0,0),this._rotation=0,this._cx=1,this._sx=0,this._cy=0,this._sy=1}get matrix(){const t=this._matrix;return this.dirty&&(t.a=this._cx*this.scale.x,t.b=this._sx*this.scale.x,t.c=this._cy*this.scale.y,t.d=this._sy*this.scale.y,t.tx=this.position.x-(this.pivot.x*t.a+this.pivot.y*t.c),t.ty=this.position.y-(this.pivot.x*t.b+this.pivot.y*t.d),this.dirty=!1),t}_onUpdate(t){var e;this.dirty=!0,t===this.skew&&this.updateSkew(),(e=this.observer)==null||e._onUpdate(this)}updateSkew(){this._cx=Math.cos(this._rotation+this.skew.y),this._sx=Math.sin(this._rotation+this.skew.y),this._cy=-Math.sin(this._rotation-this.skew.x),this._sy=Math.cos(this._rotation-this.skew.x),this.dirty=!0}setFromMatrix(t){t.decompose(this),this.dirty=!0}get rotation(){return this._rotation}set rotation(t){this._rotation!==t&&(this._rotation=t,this._onUpdate(this.skew))}}let Xr;function ob(r){const t=L.get().createCanvas(6,1),e=t.getContext("2d");return e.fillStyle=r,e.fillRect(0,0,6,1),t}function Ll(){if(Xr!==void 0)return Xr;try{const r=ob("#ff00ff"),t=ob("#ffff00"),e=L.get().createCanvas(6,1).getContext("2d");e.globalCompositeOperation="multiply",e.drawImage(r,0,0),e.drawImage(t,2,0);const i=e.getImageData(2,0,1,1);if(!i)Xr=!1;else{const s=i.data;Xr=s[0]===255&&s[1]===0&&s[2]===0}}catch(r){Xr=!1}return Xr}const K={canvas:null,convertTintToImage:!1,cacheStepsPerColorChannel:8,canUseMultiply:Ll(),tintMethod:null,_canvasSourceCache:new WeakMap,_unpremultipliedCache:new WeakMap,getCanvasSource:r=>{var t,e;const i=r.source,s=i==null?void 0:i.resource;if(!s)return null;const n=i.alphaMode==="premultiplied-alpha",a=(t=i.resourceWidth)!=null?t:i.pixelWidth,o=(e=i.resourceHeight)!=null?e:i.pixelHeight,l=a!==i.pixelWidth||o!==i.pixelHeight;if(n){if((s instanceof HTMLCanvasElement||typeof OffscreenCanvas!="undefined"&&s instanceof OffscreenCanvas)&&!l)return s;const u=K._unpremultipliedCache.get(i);if((u==null?void 0:u.resourceId)===i._resourceId)return u.canvas}if(s instanceof Uint8Array||s instanceof Uint8ClampedArray||s instanceof Int8Array||s instanceof Uint16Array||s instanceof Int16Array||s instanceof Uint32Array||s instanceof Int32Array||s instanceof Float32Array||s instanceof ArrayBuffer){const u=K._canvasSourceCache.get(i);if((u==null?void 0:u.resourceId)===i._resourceId)return u.canvas;const c=L.get().createCanvas(i.pixelWidth,i.pixelHeight),h=c.getContext("2d"),p=h.createImageData(i.pixelWidth,i.pixelHeight),f=p.data,m=s instanceof ArrayBuffer?new Uint8Array(s):new Uint8Array(s.buffer,s.byteOffset,s.byteLength);if(i.format==="bgra8unorm")for(let g=0;g0){const g=255/m;p[f]=Math.min(255,p[f]*g+.5),p[f+1]=Math.min(255,p[f+1]*g+.5),p[f+2]=Math.min(255,p[f+2]*g+.5)}}return c.putImageData(h,0,0),K._unpremultipliedCache.set(i,{canvas:u,resourceId:i._resourceId}),u}if(l){const u=K._canvasSourceCache.get(i);if((u==null?void 0:u.resourceId)===i._resourceId)return u.canvas;const c=L.get().createCanvas(i.pixelWidth,i.pixelHeight),h=c.getContext("2d");return c.width=i.pixelWidth,c.height=i.pixelHeight,h.drawImage(s,0,0),K._canvasSourceCache.set(i,{canvas:c,resourceId:i._resourceId}),c}return s},getTintedCanvas:(r,t)=>{const e=r.texture,i=J.shared.setValue(t).toHex(),s=e.tintCache||(e.tintCache={}),n=s[i],a=e.source._resourceId;if((n==null?void 0:n.tintId)===a)return n;const o=n&&"getContext"in n?n:L.get().createCanvas();if(K.tintMethod(e,t,o),o.tintId=a,K.convertTintToImage&&o.toDataURL!==void 0){const l=L.get().createImage();l.src=o.toDataURL(),l.tintId=a,s[i]=l}else s[i]=o;return s[i]},getTintedPattern:(r,t)=>{const e=J.shared.setValue(t).toHex(),i=r.patternCache||(r.patternCache={}),s=r.source._resourceId;let n=i[e];return(n==null?void 0:n.tintId)===s||(K.canvas||(K.canvas=L.get().createCanvas()),K.tintMethod(r,t,K.canvas),n=K.canvas.getContext("2d").createPattern(K.canvas,"repeat"),n.tintId=s,i[e]=n),n},applyPatternTransform:(r,t,e=!0)=>{if(!t)return;const i=r;if(!i.setTransform)return;const s=globalThis.DOMMatrix;if(!s)return;const n=new s([t.a,t.b,t.c,t.d,t.tx,t.ty]);i.setTransform(e?n.inverse():n)},tintWithMultiply:(r,t,e)=>{var i,s;const n=e.getContext("2d"),a=r.frame.clone(),o=(s=(i=r.source._resolution)!=null?i:r.source.resolution)!=null?s:1,l=r.rotate;a.x*=o,a.y*=o,a.width*=o,a.height*=o;const u=H.isVertical(l),c=u?a.height:a.width,h=u?a.width:a.height;e.width=Math.ceil(c),e.height=Math.ceil(h),n.save(),n.fillStyle=J.shared.setValue(t).toHex(),n.fillRect(0,0,c,h),n.globalCompositeOperation="multiply";const p=K.getCanvasSource(r);if(!p){n.restore();return}l&&K._applyInverseRotation(n,l,a.width,a.height),n.drawImage(p,a.x,a.y,a.width,a.height,0,0,a.width,a.height),n.globalCompositeOperation="destination-atop",n.drawImage(p,a.x,a.y,a.width,a.height,0,0,a.width,a.height),n.restore()},tintWithOverlay:(r,t,e)=>{var i,s;const n=e.getContext("2d"),a=r.frame.clone(),o=(s=(i=r.source._resolution)!=null?i:r.source.resolution)!=null?s:1,l=r.rotate;a.x*=o,a.y*=o,a.width*=o,a.height*=o;const u=H.isVertical(l),c=u?a.height:a.width,h=u?a.width:a.height;e.width=Math.ceil(c),e.height=Math.ceil(h),n.save(),n.globalCompositeOperation="copy",n.fillStyle=J.shared.setValue(t).toHex(),n.fillRect(0,0,c,h),n.globalCompositeOperation="destination-atop";const p=K.getCanvasSource(r);if(!p){n.restore();return}l&&K._applyInverseRotation(n,l,a.width,a.height),n.drawImage(p,a.x,a.y,a.width,a.height,0,0,a.width,a.height),n.restore()},tintWithPerPixel:(r,t,e)=>{var i,s;const n=e.getContext("2d"),a=r.frame.clone(),o=(s=(i=r.source._resolution)!=null?i:r.source.resolution)!=null?s:1,l=r.rotate;a.x*=o,a.y*=o,a.width*=o,a.height*=o;const u=H.isVertical(l),c=u?a.height:a.width,h=u?a.width:a.height;e.width=Math.ceil(c),e.height=Math.ceil(h),n.save(),n.globalCompositeOperation="copy";const p=K.getCanvasSource(r);if(!p){n.restore();return}l&&K._applyInverseRotation(n,l,a.width,a.height),n.drawImage(p,a.x,a.y,a.width,a.height,0,0,a.width,a.height),n.restore();const f=t>>16&255,m=t>>8&255,g=t&255,_=n.getImageData(0,0,c,h),b=_.data;for(let y=0;y{const s=H.inv(t),n=H.uX(s),a=H.uY(s),o=H.vX(s),l=H.vY(s),u=-Math.min(0,n*e,o*i,n*e+o*i),c=-Math.min(0,a*e,l*i,a*e+l*i);r.transform(n,a,o,l,u,c)}};K.tintMethod=K.canUseMultiply?K.tintWithMultiply:K.tintWithPerPixel;const un=new D,Di=new D,we=[new rt,new rt,new rt,new rt];class Nl{constructor(t){this._renderer=t}validateRenderable(t){return!1}addRenderable(t,e){this._renderer.renderPipes.batch.break(e),e.add(t)}updateRenderable(t){}execute(t){var e,i,s,n,a,o;const l=this._renderer,u=l.canvasContext,c=u.activeContext;c.save(),u.setBlendMode(t.groupBlendMode);const h=(i=(e=l.globalUniforms.globalUniformData)==null?void 0:e.worldColor)!=null?i:4294967295,p=t.groupColorAlpha,f=(h>>>24&255)/255,m=(p>>>24&255)/255,g=(n=(s=l.filter)==null?void 0:s.alphaMultiplier)!=null?n:1,_=f*m*g;if(_<=0){c.restore();return}c.globalAlpha=_;const b=h&16777215,y=p&16777215,x=be(Ce(y,b)),v=t.texture,w=K.getTintedPattern(v,x),S=t.width,E=t.height,O=t.groupTransform,M=(o=(a=v.source._resolution)!=null?a:v.source.resolution)!=null?o:1;Di.copyFrom(t._tileTransform.matrix),t.applyAnchorToTexture||Di.translate(-t.anchor.x*S,-t.anchor.y*E),Di.scale(1/M,1/M),un.identity(),un.prepend(Di),un.prepend(O);const C=l._roundPixels|t._roundPixels;u.setContextTransform(un,C===1),c.fillStyle=w;const P=t.anchor.x*-S,R=t.anchor.y*-E;we[0].set(P,R),we[1].set(P+S,R),we[2].set(P+S,R+E),we[3].set(P,R+E);for(let G=0;G<4;G++)Di.applyInverse(we[G],we[G]);c.beginPath(),c.moveTo(we[0].x,we[0].y);for(let G=1;G<4;G++)c.lineTo(we[G].x,we[G].y);c.closePath(),c.fill(),c.restore()}destroy(){this._renderer=null}}Nl.extension={type:[T.CanvasPipes],name:"tilingSprite"};var RC=Object.defineProperty,MC=Object.defineProperties,OC=Object.getOwnPropertyDescriptors,lb=Object.getOwnPropertySymbols,GC=Object.prototype.hasOwnProperty,IC=Object.prototype.propertyIsEnumerable,ub=(r,t,e)=>t in r?RC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,cb=(r,t)=>{for(var e in t||(t={}))GC.call(t,e)&&ub(r,e,t[e]);if(lb)for(var e of lb(t))IC.call(t,e)&&ub(r,e,t[e]);return r},hb=(r,t)=>MC(r,OC(t));const Hr={name:"local-uniform-bit",vertex:{header:` + + struct LocalUniforms { + uTransformMatrix:mat3x3, + uColor:vec4, + uRound:f32, + } + + @group(1) @binding(0) var localUniforms : LocalUniforms; + `,main:` + vColor *= localUniforms.uColor; + modelMatrix *= localUniforms.uTransformMatrix; + `,end:` + if(localUniforms.uRound == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + `}},db=hb(cb({},Hr),{vertex:hb(cb({},Hr.vertex),{header:Hr.vertex.header.replace("group(1)","group(2)")})}),cn={name:"local-uniform-bit",vertex:{header:` + + uniform mat3 uTransformMatrix; + uniform vec4 uColor; + uniform float uRound; + `,main:` + vColor *= uColor; + modelMatrix = uTransformMatrix; + `,end:` + if(uRound == 1.) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + `}},pb={name:"tiling-bit",vertex:{header:` + struct TilingUniforms { + uMapCoord:mat3x3, + uClampFrame:vec4, + uClampOffset:vec2, + uTextureTransform:mat3x3, + uSizeAnchor:vec4 + }; + + @group(2) @binding(0) var tilingUniforms: TilingUniforms; + @group(2) @binding(1) var uTexture: texture_2d; + @group(2) @binding(2) var uSampler: sampler; + `,main:` + uv = (tilingUniforms.uTextureTransform * vec3(uv, 1.0)).xy; + + position = (position - tilingUniforms.uSizeAnchor.zw) * tilingUniforms.uSizeAnchor.xy; + `},fragment:{header:` + struct TilingUniforms { + uMapCoord:mat3x3, + uClampFrame:vec4, + uClampOffset:vec2, + uTextureTransform:mat3x3, + uSizeAnchor:vec4 + }; + + @group(2) @binding(0) var tilingUniforms: TilingUniforms; + @group(2) @binding(1) var uTexture: texture_2d; + @group(2) @binding(2) var uSampler: sampler; + `,main:` + + var coord = vUV + ceil(tilingUniforms.uClampOffset - vUV); + coord = (tilingUniforms.uMapCoord * vec3(coord, 1.0)).xy; + var unclamped = coord; + coord = clamp(coord, tilingUniforms.uClampFrame.xy, tilingUniforms.uClampFrame.zw); + + var bias = 0.; + + if(unclamped.x == coord.x && unclamped.y == coord.y) + { + bias = -32.; + } + + outColor = textureSampleBias(uTexture, uSampler, coord, bias); + `}},fb={name:"tiling-bit",vertex:{header:` + uniform mat3 uTextureTransform; + uniform vec4 uSizeAnchor; + + `,main:` + uv = (uTextureTransform * vec3(aUV, 1.0)).xy; + + position = (position - uSizeAnchor.zw) * uSizeAnchor.xy; + `},fragment:{header:` + uniform sampler2D uTexture; + uniform mat3 uMapCoord; + uniform vec4 uClampFrame; + uniform vec2 uClampOffset; + `,main:` + + vec2 coord = vUV + ceil(uClampOffset - vUV); + coord = (uMapCoord * vec3(coord, 1.0)).xy; + vec2 unclamped = coord; + coord = clamp(coord, uClampFrame.xy, uClampFrame.zw); + + outColor = texture(uTexture, coord, unclamped == coord ? 0.0 : -32.0);// lod-bias very negative to force lod 0 + + `}};let Xl,Hl;class mb extends Zt{constructor(){Xl!=null||(Xl=Cr({name:"tiling-sprite-shader",bits:[Hr,pb,Mr]})),Hl!=null||(Hl=Rr({name:"tiling-sprite-shader",bits:[cn,fb,Or]}));const t=new wt({uMapCoord:{value:new D,type:"mat3x3"},uClampFrame:{value:new Float32Array([0,0,1,1]),type:"vec4"},uClampOffset:{value:new Float32Array([0,0]),type:"vec2"},uTextureTransform:{value:new D,type:"mat3x3"},uSizeAnchor:{value:new Float32Array([100,100,.5,.5]),type:"vec4"}});super({glProgram:Hl,gpuProgram:Xl,resources:{localUniforms:new wt({uTransformMatrix:{value:new D,type:"mat3x3"},uColor:{value:new Float32Array([1,1,1,1]),type:"vec4"},uRound:{value:0,type:"f32"}}),tilingUniforms:t,uTexture:F.EMPTY.source,uSampler:F.EMPTY.source.style}})}updateUniforms(t,e,i,s,n,a){const o=this.resources.tilingUniforms,l=a.width,u=a.height,c=a.textureMatrix,h=o.uniforms.uTextureTransform;h.set(i.a*l/t,i.b*l/e,i.c*u/t,i.d*u/e,i.tx/t,i.ty/e),h.invert(),o.uniforms.uMapCoord=c.mapCoord,o.uniforms.uClampFrame=c.uClampFrame,o.uniforms.uClampOffset=c.uClampOffset,o.uniforms.uTextureTransform=h,o.uniforms.uSizeAnchor[0]=t,o.uniforms.uSizeAnchor[1]=e,o.uniforms.uSizeAnchor[2]=s,o.uniforms.uSizeAnchor[3]=n,a&&(this.resources.uTexture=a.source,this.resources.uSampler=a.source.style)}}class gb extends Le{constructor(){super({positions:new Float32Array([0,0,1,0,1,1,0,1]),uvs:new Float32Array([0,0,1,0,1,1,0,1]),indices:new Uint32Array([0,1,2,0,2,3])})}}function _b(r,t){const e=r.anchor.x,i=r.anchor.y;t[0]=-e*r.width,t[1]=-i*r.height,t[2]=(1-e)*r.width,t[3]=-i*r.height,t[4]=(1-e)*r.width,t[5]=(1-i)*r.height,t[6]=-e*r.width,t[7]=(1-i)*r.height}function bb(r,t,e,i){let s=0;const n=r.length/(t||2),a=i.a,o=i.b,l=i.c,u=i.d,c=i.tx,h=i.ty;for(e*=t;st in r?BC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ui=(r,t)=>{for(var e in t||(t={}))xb.call(t,e)&&Sb(r,e,t[e]);if(dn)for(var e of dn(t))Tb.call(t,e)&&Sb(r,e,t[e]);return r},FC=(r,t)=>{var e={};for(var i in r)xb.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&dn)for(var i of dn(r))t.indexOf(i)<0&&Tb.call(r,i)&&(e[i]=r[i]);return e};const wb=class ha extends xe{constructor(...t){let e=t[0]||{};e instanceof F&&(e={texture:e}),t.length>1&&(e.width=t[1],e.height=t[2]),e=Ui(Ui({},ha.defaultOptions),e);const i=e!=null?e:{},{texture:s,anchor:n,tilePosition:a,tileScale:o,tileRotation:l,width:u,height:c,applyAnchorToTexture:h,roundPixels:p}=i,f=FC(i,["texture","anchor","tilePosition","tileScale","tileRotation","width","height","applyAnchorToTexture","roundPixels"]);super(Ui({label:"TilingSprite"},f)),this.renderPipeId="tilingSprite",this.batched=!0,this.allowChildren=!1,this._anchor=new gt({_onUpdate:()=>{this.onViewUpdate()}}),this.applyAnchorToTexture=h,this.texture=s,this._width=u!=null?u:s.width,this._height=c!=null?c:s.height,this._tileTransform=new ab({observer:{_onUpdate:()=>this.onViewUpdate()}}),n&&(this.anchor=n),this.tilePosition=a,this.tileScale=o,this.tileRotation=l,this.roundPixels=p!=null?p:!1}static from(t,e={}){return typeof t=="string"?new ha(Ui({texture:tt.get(t)},e)):new ha(Ui({texture:t},e))}get uvRespectAnchor(){return xi(Mo,"uvRespectAnchor is deprecated, please use applyAnchorToTexture instead"),this.applyAnchorToTexture}set uvRespectAnchor(t){xi(Mo,"uvRespectAnchor is deprecated, please use applyAnchorToTexture instead"),this.applyAnchorToTexture=t}get clampMargin(){return this._texture.textureMatrix.clampMargin}set clampMargin(t){this._texture.textureMatrix.clampMargin=t}get anchor(){return this._anchor}set anchor(t){typeof t=="number"?this._anchor.set(t):this._anchor.copyFrom(t)}get tilePosition(){return this._tileTransform.position}set tilePosition(t){this._tileTransform.position.copyFrom(t)}get tileScale(){return this._tileTransform.scale}set tileScale(t){typeof t=="number"?this._tileTransform.scale.set(t):this._tileTransform.scale.copyFrom(t)}set tileRotation(t){this._tileTransform.rotation=t}get tileRotation(){return this._tileTransform.rotation}get tileTransform(){return this._tileTransform}set texture(t){t||(t=F.EMPTY);const e=this._texture;e!==t&&(e&&e.dynamic&&e.off("update",this.onViewUpdate,this),t.dynamic&&t.on("update",this.onViewUpdate,this),this._texture=t,this.onViewUpdate())}get texture(){return this._texture}set width(t){this._width=t,this.onViewUpdate()}get width(){return this._width}set height(t){this._height=t,this.onViewUpdate()}get height(){return this._height}setSize(t,e){var i;typeof t=="object"&&(e=(i=t.height)!=null?i:t.width,t=t.width),this._width=t,this._height=e!=null?e:t,this.onViewUpdate()}getSize(t){return t||(t={}),t.width=this._width,t.height=this._height,t}updateBounds(){const t=this._bounds,e=this._anchor,i=this._width,s=this._height;t.minX=-e._x*i,t.maxX=t.minX+i,t.minY=-e._y*s,t.maxY=t.minY+s}containsPoint(t){const e=this._width,i=this._height,s=-e*this._anchor._x;let n=0;return t.x>=s&&t.x<=s+e&&(n=-i*this._anchor._y,t.y>=n&&t.y<=n+i)}destroy(t=!1){if(super.destroy(t),this._anchor=null,this._tileTransform=null,this._bounds=null,typeof t=="boolean"?t:t==null?void 0:t.texture){const e=typeof t=="boolean"?t:t==null?void 0:t.textureSource;this._texture.destroy(e)}this._texture=null}};wb.defaultOptions={texture:F.EMPTY,anchor:{x:0,y:0},tilePosition:{x:0,y:0},tileScale:{x:1,y:1},tileRotation:0,applyAnchorToTexture:!1};let Eb=wb;var DC=Object.defineProperty,pn=Object.getOwnPropertySymbols,Pb=Object.prototype.hasOwnProperty,Ab=Object.prototype.propertyIsEnumerable,Cb=(r,t,e)=>t in r?DC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,UC=(r,t)=>{for(var e in t||(t={}))Pb.call(t,e)&&Cb(r,e,t[e]);if(pn)for(var e of pn(t))Ab.call(t,e)&&Cb(r,e,t[e]);return r},kC=(r,t)=>{var e={};for(var i in r)Pb.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&pn)for(var i of pn(r))t.indexOf(i)<0&&Ab.call(r,i)&&(e[i]=r[i]);return e};class fn extends xe{constructor(t,e){const i=t,{text:s,resolution:n,style:a,anchor:o,width:l,height:u,roundPixels:c}=i,h=kC(i,["text","resolution","style","anchor","width","height","roundPixels"]);super(UC({},h)),this.batched=!0,this._resolution=null,this._autoResolution=!0,this._didTextUpdate=!0,this._styleClass=e,this.text=s!=null?s:"",this.style=a,this.resolution=n!=null?n:null,this.allowChildren=!1,this._anchor=new gt({_onUpdate:()=>{this.onViewUpdate()}}),o&&(this.anchor=o),this.roundPixels=c!=null?c:!1,l!==void 0&&(this.width=l),u!==void 0&&(this.height=u)}get anchor(){return this._anchor}set anchor(t){typeof t=="number"?this._anchor.set(t):this._anchor.copyFrom(t)}set text(t){t=t.toString(),this._text!==t&&(this._text=t,this.onViewUpdate())}get text(){return this._text}set resolution(t){this._autoResolution=t===null,this._resolution=t,this.onViewUpdate()}get resolution(){return this._resolution}get style(){return this._style}set style(t){var e;t||(t={}),(e=this._style)==null||e.off("update",this.onViewUpdate,this),t instanceof this._styleClass?this._style=t:this._style=new this._styleClass(t),this._style.on("update",this.onViewUpdate,this),this.onViewUpdate()}get width(){return Math.abs(this.scale.x)*this.bounds.width}set width(t){this._setWidth(t,this.bounds.width)}get height(){return Math.abs(this.scale.y)*this.bounds.height}set height(t){this._setHeight(t,this.bounds.height)}getSize(t){return t||(t={}),t.width=Math.abs(this.scale.x)*this.bounds.width,t.height=Math.abs(this.scale.y)*this.bounds.height,t}setSize(t,e){var i;typeof t=="object"?(e=(i=t.height)!=null?i:t.width,t=t.width):e!=null||(e=t),t!==void 0&&this._setWidth(t,this.bounds.width),e!==void 0&&this._setHeight(e,this.bounds.height)}containsPoint(t){const e=this.bounds.width,i=this.bounds.height,s=-e*this.anchor.x;let n=0;return t.x>=s&&t.x<=s+e&&(n=-i*this.anchor.y,t.y>=n&&t.y<=n+i)}onViewUpdate(){this.didViewUpdate||(this._didTextUpdate=!0),super.onViewUpdate()}destroy(t=!1){super.destroy(t),this.owner=null,this._bounds=null,this._anchor=null,(typeof t=="boolean"?t:t!=null&&t.style)&&this._style.destroy(t),this._style=null,this._text=null}get styleKey(){return`${this._text}:${this._style.styleKey}:${this._resolution}`}}function mn(r,t){var e;let i=(e=r[0])!=null?e:{};return(typeof i=="string"||r[1])&&(i={text:i,style:r[1]}),i}class Rb{constructor(t){this._canvasPool=Object.create(null),this.canvasOptions=t||{},this.enableFullScreen=!1}_createCanvasAndContext(t,e){const i=L.get().createCanvas();i.width=t,i.height=e;const s=i.getContext("2d");return{canvas:i,context:s}}getOptimalCanvasAndContext(t,e,i=1){t=Math.ceil(t*i-1e-6),e=Math.ceil(e*i-1e-6),t=ze(t),e=ze(e);const s=(t<<17)+(e<<1);this._canvasPool[s]||(this._canvasPool[s]=[]);let n=this._canvasPool[s].pop();return n||(n=this._createCanvasAndContext(t,e)),n}returnCanvasAndContext(t){const e=t.canvas,{width:i,height:s}=e,n=(i<<17)+(s<<1);t.context.resetTransform(),t.context.clearRect(0,0,i,s),this._canvasPool[n].push(t)}clear(){this._canvasPool={}}}const me=new Rb;je.register(me);let or=null,Oe=null;function $C(r,t){or||(or=L.get().createCanvas(256,128),Oe=or.getContext("2d",{willReadFrequently:!0}),Oe.globalCompositeOperation="copy",Oe.globalAlpha=1),(or.width + * @license BSD-3-Clause + * @version 11.4.7 + */class LC{constructor(t=0,e=0,i=!1){this.first=null,this.items=Object.create(null),this.last=null,this.max=t,this.resetTtl=i,this.size=0,this.ttl=e}clear(){return this.first=null,this.items=Object.create(null),this.last=null,this.size=0,this}delete(t){if(this.has(t)){const e=this.items[t];delete this.items[t],this.size--,e.prev!==null&&(e.prev.next=e.next),e.next!==null&&(e.next.prev=e.prev),this.first===e&&(this.first=e.next),this.last===e&&(this.last=e.prev)}return this}entries(t=this.keys()){const e=new Array(t.length);for(let i=0;i0){const e=this.first;delete this.items[e.key],--this.size===0?(this.first=null,this.last=null):(this.first=e.next,this.first.prev=null)}return this}expiresAt(t){let e;return this.has(t)&&(e=this.items[t].expiry),e}get(t){const e=this.items[t];if(e!==void 0){if(this.ttl>0&&e.expiry<=Date.now()){this.delete(t);return}return this.moveToEnd(e),e.value}}has(t){return t in this.items}moveToEnd(t){this.last!==t&&(t.prev!==null&&(t.prev.next=t.next),t.next!==null&&(t.next.prev=t.prev),this.first===t&&(this.first=t.next),t.prev=this.last,t.next=null,this.last!==null&&(this.last.next=t),this.last=t,this.first===null&&(this.first=t))}keys(){const t=new Array(this.size);let e=this.first,i=0;for(;e!==null;)t[i++]=e.key,e=e.next;return t}setWithEvicted(t,e,i=this.resetTtl){let s=null;if(this.has(t))this.set(t,e,!0,i);else{this.max>0&&this.size===this.max&&(s=VT({},this.first),this.evict(!0));let n=this.items[t]={expiry:this.ttl>0?Date.now()+this.ttl:this.ttl,key:t,prev:this.last,next:null,value:e};++this.size===1?this.first=n:this.last.next=n,this.last=n}return s}set(t,e,i=!1,s=this.resetTtl){let n=this.items[t];return i||n!==void 0?(n.value=e,i===!1&&s&&(n.expiry=this.ttl>0?Date.now()+this.ttl:this.ttl),this.moveToEnd(n)):(this.max>0&&this.size===this.max&&this.evict(!0),n=this.items[t]={expiry:this.ttl>0?Date.now()+this.ttl:this.ttl,key:t,prev:this.last,next:null,value:e},++this.size===1?this.first=n:this.last.next=n,this.last=n),this}values(t=this.keys()){const e=new Array(t.length);for(let i=0;i0}function _n(r){return r.includes("<")}function NC(r,t){return r.clone().assign(t)}function zl(r,t){const e=[],i=t.tagStyles;if(!gn(t)||!_n(r))return e.push({text:r,style:t}),e;const s=[t],n=[];let a="",o=0;for(;o",o);if(u===-1){a+=l,o++;continue}const c=r.slice(o+1,u);if(c.startsWith("/")){const h=c.slice(1).trim();if(n.length>0&&n[n.length-1]===h){a.length>0&&(e.push({text:a,style:s[s.length-1]}),a=""),s.pop(),n.pop(),o=u+1;continue}else{a+=r.slice(o,u+1),o=u+1;continue}}else{const h=c.trim();if(i[h]){a.length>0&&(e.push({text:a,style:s[s.length-1]}),a="");const p=s[s.length-1],f=NC(p,i[h]);s.push(f),n.push(h),o=u+1;continue}else{a+=r.slice(o,u+1),o=u+1;continue}}}else a+=l,o++}return a.length>0&&e.push({text:a,style:s[s.length-1]}),e}function XC(r,t){return!gn(t)||!_n(r)?r:zl(r,t).map(e=>e.text).join("")}const Bb=[10,13],Fb=new Set(Bb),Db=[9,32,8192,8193,8194,8195,8196,8197,8198,8200,8201,8202,8287,12288],Ub=new Set(Db),kb=/(\r\n|\r|\n)/,$b=/(?:\r\n|\r|\n)/;function bn(r){return typeof r!="string"?!1:Fb.has(r.charCodeAt(0))}function oe(r,t){return typeof r!="string"?!1:Ub.has(r.charCodeAt(0))}function Wl(r){return r==="normal"||r==="pre-line"}function Vl(r){return r==="normal"}function Ee(r){if(typeof r!="string")return"";let t=r.length-1;for(;t>=0&&oe(r[t]);)t--;return t0&&(t.push(e.join("")),e.length=0),s==="\r"&&n===` +`?(t.push(`\r +`),i++):t.push(s);continue}e.push(s)}return e.length>0&&t.push(e.join("")),t}function Kl(r,t,e,i){const s=e(r),n=[];for(let a=0;a0&&h.push({text:U,style:R.style})}}(h.length>0||c.length===0)&&c.push(h);const p=e?Nb(c,t,i,s,a,o):c,f=[],m=[],g=[],_=[],b=[];let y=0;const x=t._fontString,v=n(x);v.fontSize===0&&(v.fontSize=t.fontSize,v.ascent=t.fontSize);let w="",S=!!t.dropShadow;for(const R of p){let G=0,I=v.ascent,U=v.descent,A="";for(const Q of R){const N=Q.style._fontString,q=n(N);N!==w&&(i.font=N,w=N);const k=s(Q.text,Q.style.letterSpacing,i);G+=k,I=Math.max(I,q.ascent),U=Math.max(U,q.descent),A+=Q.text,!S&&Q.style.dropShadow&&(S=!0)}R.length===0&&(I=v.ascent,U=v.descent),f.push(G),m.push(I),g.push(U),b.push(A);const B=t.lineHeight||I+U;_.push(B+t.leading),y=Math.max(y,G)}const E=((l=t._stroke)==null?void 0:l.width)||0,O=(e&&t.align!=="left"&&t.align!=="justify"?Math.max(y,t.wordWrapWidth):y)+E+(t.dropShadow?t.dropShadow.distance:0);let M=0;for(let R=0;R<_.length;R++)M+=_[R];M=Math.max(M,_[0]+E);const C=M+(t.dropShadow?t.dropShadow.distance:0),P=t.lineHeight||v.fontSize;return{width:O,height:C,lines:b,lineWidths:f,lineHeight:P+t.leading,maxLineWidth:y,fontProperties:v,runsByLine:p,lineAscents:m,lineDescents:g,lineHeights:_,hasDropShadow:S}}function Nb(r,t,e,i,s,n){var a,o,l;const{letterSpacing:u,whiteSpace:c,wordWrapWidth:h,breakWords:p}=t,f=Wl(c),m=h+u,g={};let _="";const b=(x,v)=>{const w=`${x}|${v.styleKey}`;let S=g[w];if(S===void 0){const E=v._fontString;E!==_&&(e.font=E,_=E),S=i(x,v.letterSpacing,e)+v.letterSpacing,g[w]=S}return S},y=[];for(const x of r){const v=Xb(x),w=y.length,S=I=>{let U=0,A=I;do{const{token:B,style:Q}=v[A];U+=b(B,Q),A++}while(A{const U=[];let A=I;do U.push({token:v[A].token,style:v[A].style}),A++;while(A{P&&P.text.length>0&&O.push(P),P=null},G=()=>{if(R(),O.length>0){const I=O[O.length-1];I.text=Ee(I.text),I.text.length===0&&O.pop()}y.push(O),O=[],M=0,C=!1};for(let I=0;Im&&N)if(M>0&&G(),p){const k=E(I);for(let j=0;jm&&G(),!P||P.style!==W?(R(),P={text:ut,style:W}):P.text+=ut,M+=It}}I+=k.length-1}else{const k=E(I);R(),y.push(k.map(j=>({text:j.token,style:j.style}))),C=!1,I+=k.length-1}else if(q+M>m&&N){if(oe(U)){C=!1;continue}G(),P={text:U,style:A},M=Q}else if(B&&!p)!P||P.style!==A?(R(),P={text:U,style:A}):P.text+=U,M+=Q;else{const k=oe(U);if(M===0&&k&&!C)continue;!P||P.style!==A?(R(),P={text:U,style:A}):P.text+=U,M+=Q}}if(R(),O.length>0){const I=O[O.length-1];I.text=Ee(I.text),I.text.length===0&&O.pop()}(O.length>0||y.length===w)&&y.push(O)}return y}function Xb(r){const t=[];let e=!1;for(const i of r){const s=Yl(i.text);let n=!0;for(const a of s){const o=oe(a)||bn(a),l=n&&e&&!o;t.push({token:a,style:i.style,continuesFromPrevious:l}),e=!o,n=!1}}return t}const jC={willReadFrequently:!0};function Hb(r,t,e,i,s){let n=e[r];return typeof n!="number"&&(n=s(r,t,i)+t,e[r]=n),n}function jb(r,t,e,i,s,n,a){const o=e.getContext("2d",jC);o.font=t._fontString;let l=0,u="";const c=[],h=Object.create(null),{letterSpacing:p,whiteSpace:f}=t,m=Wl(f),g=Vl(f);let _=!m;const b=t.wordWrapWidth+p,y=Yl(r);for(let v=0;vb)if(u!==""&&(c.push(Ee(u)),u="",l=0),s(w,t.breakWords)){const E=Kl(w,t.breakWords,a,n);for(const O of E){const M=Hb(O,p,h,o,i);M+l>b&&(c.push(Ee(u)),_=!1,u="",l=0),u+=O,l+=M}}else u.length>0&&(c.push(Ee(u)),u="",l=0),c.push(Ee(w)),_=!1,u="",l=0;else S+l>b&&(_=!1,c.push(Ee(u)),u="",l=0),(u.length>0||!oe(w)||_)&&(u+=w,l+=S)}const x=Ee(u);return x.length>0&&c.push(x),c.join(` +`)}const zb={willReadFrequently:!0},Ge=class V{static get experimentalLetterSpacingSupported(){let t=V._experimentalLetterSpacingSupported;if(t===void 0){const e=L.get().getCanvasRenderingContext2D().prototype;t=V._experimentalLetterSpacingSupported="letterSpacing"in e||"textLetterSpacing"in e}return t}constructor(t,e,i,s,n,a,o,l,u,c){this.text=t,this.style=e,this.width=i,this.height=s,this.lines=n,this.lineWidths=a,this.lineHeight=o,this.maxLineWidth=l,this.fontProperties=u,c&&(this.runsByLine=c.runsByLine,this.lineAscents=c.lineAscents,this.lineDescents=c.lineDescents,this.lineHeights=c.lineHeights,this.hasDropShadow=c.hasDropShadow)}static measureText(t=" ",e,i=V._canvas,s=e.wordWrap){var n,a;const o=`${t}-${e.styleKey}-wordWrap-${s}`;if(V._measurementCache.has(o))return V._measurementCache.get(o);if(gn(e)&&_n(t)){const w=Lb(t,e,s,V._context,V._measureText,V.measureFont,V.canBreakChars,V.wordWrapSplit),S=new V(t,e,w.width,w.height,w.lines,w.lineWidths,w.lineHeight,w.maxLineWidth,w.fontProperties,{runsByLine:w.runsByLine,lineAscents:w.lineAscents,lineDescents:w.lineDescents,lineHeights:w.lineHeights,hasDropShadow:w.hasDropShadow});return V._measurementCache.set(o,S),S}const l=e._fontString,u=V.measureFont(l);u.fontSize===0&&(u.fontSize=e.fontSize,u.ascent=e.fontSize,u.descent=0);const c=V._context;c.font=l;const h=(s?V._wordWrap(t,e,i):t).split($b),p=new Array(h.length);let f=0;for(let w=0;w0)if(a)l-=e,c-=e;else{const h=(V.graphemeSegmenter(t).length-1)*e;l+=h,c+=h}return Math.max(l,c)}static _wordWrap(t,e,i=V._canvas){return jb(t,e,i,V._measureText,V.canBreakWords,V.canBreakChars,V.wordWrapSplit)}static isBreakingSpace(t,e){return oe(t,e)}static canBreakWords(t,e){return e}static canBreakChars(t,e,i,s,n){return!0}static wordWrapSplit(t){return V.graphemeSegmenter(t)}static measureFont(t){var e,i;if(V._fonts[t])return V._fonts[t];const s=V._context;s.font=t;const n=s.measureText(V.METRICS_STRING+V.BASELINE_SYMBOL),a=(e=n.actualBoundingBoxAscent)!=null?e:0,o=(i=n.actualBoundingBoxDescent)!=null?i:0,l={ascent:a,descent:o,fontSize:a+o};return V._fonts[t]=l,l}static clearMetrics(t=""){t?delete V._fonts[t]:V._fonts={}}static get _canvas(){if(!V.__canvas){let t;try{const e=new OffscreenCanvas(0,0),i=e.getContext("2d",zb);if(i!=null&&i.measureText)return V.__canvas=e,e;t=L.get().createCanvas()}catch(e){t=L.get().createCanvas()}t.width=t.height=10,V.__canvas=t}return V.__canvas}static get _context(){return V.__context||(V.__context=V._canvas.getContext("2d",zb)),V.__context}};Ge.METRICS_STRING="|\xC9q\xC5",Ge.BASELINE_SYMBOL="M",Ge.BASELINE_MULTIPLIER=1.4,Ge.HEIGHT_MULTIPLIER=2,Ge.graphemeSegmenter=(()=>{if(typeof(Intl==null?void 0:Intl.Segmenter)=="function"){const r=new Intl.Segmenter;return t=>{const e=r.segment(t),i=[];let s=0;for(const n of e)i[s++]=n.segment;return i}}return r=>[...r]})(),Ge.experimentalLetterSpacing=!1,Ge._fonts={},Ge._measurementCache=Ib(1e3);let Ot=Ge;const zC=["serif","sans-serif","monospace","cursive","fantasy","system-ui"];function jr(r){const t=typeof r.fontSize=="number"?`${r.fontSize}px`:r.fontSize;let e=r.fontFamily;Array.isArray(r.fontFamily)||(e=r.fontFamily.split(","));for(let i=e.length-1;i>=0;i--){let s=e[i].trim();!/([\"\'])[^\'\"]+\1/.test(s)&&!zC.includes(s)&&(s=`"${s}"`),e[i]=s}return`${r.fontStyle} ${r.fontVariant} ${r.fontWeight} ${t} ${e.join(",")}`}const Wb=1e5;function lr(r,t,e,i=0,s=0,n=0){var a;if(r.texture===F.WHITE&&!r.fill)return J.shared.setValue(r.color).setAlpha((a=r.alpha)!=null?a:1).toHexa();if(r.fill){if(r.fill instanceof Br){const o=r.fill,l=t.createPattern(o.texture.source.resource,"repeat"),u=o.transform.copyTo(D.shared);return u.scale(o.texture.source.pixelWidth,o.texture.source.pixelHeight),l.setTransform(u),l}else if(r.fill instanceof Qt){const o=r.fill,l=o.type==="linear",u=o.textureSpace==="local";let c=1,h=1;u&&e&&(c=e.width+i,h=e.height+i);let p,f=!1;if(l){const{start:m,end:g}=o;p=t.createLinearGradient(m.x*c+s,m.y*h+n,g.x*c+s,g.y*h+n),f=Math.abs(g.x-m.x){let y=_+b.offset*m;y=Math.max(0,Math.min(1,y)),p.addColorStop(Math.floor(y*Wb)/Wb,J.shared.setValue(b.color).toHex())})}}else o.colorStops.forEach(m=>{p.addColorStop(m.offset,J.shared.setValue(m.color).toHex())});return p}}else{const o=t.createPattern(r.texture.source.resource,"repeat"),l=r.matrix.copyTo(D.shared);return l.scale(r.texture.source.pixelWidth,r.texture.source.pixelHeight),o.setTransform(l),o}return"red"}const Vb=new it;let WC=class{getCanvasAndContext(t){const{text:e,style:i,resolution:s=1}=t,n=i._getFinalPadding(),a=Ot.measureText(e||" ",i),o=Math.ceil(Math.ceil(Math.max(1,a.width)+n*2)*s),l=Math.ceil(Math.ceil(Math.max(1,a.height)+n*2)*s),u=me.getOptimalCanvasAndContext(o,l);this._renderTextToCanvas(i,n,s,u,a);const c=i.trim?Gb({canvas:u.canvas,width:o,height:l,resolution:1,output:Vb}):Vb.set(0,0,o,l);return{canvasAndContext:u,frame:c}}returnCanvasAndContext(t){me.returnCanvasAndContext(t)}_renderTextToCanvas(t,e,i,s,n){var a,o,l,u,c,h;if(n.runsByLine&&n.runsByLine.length>0){this._renderTaggedTextToCanvas(n,t,e,i,s);return}const{canvas:p,context:f}=s,m=jr(t),g=n.lines,_=n.lineHeight,b=n.lineWidths,y=n.maxLineWidth,x=n.fontProperties,v=p.height;if(f.resetTransform(),f.scale(i,i),f.textBaseline=t.textBaseline,(a=t._stroke)!=null&&a.width){const P=t._stroke;f.lineWidth=P.width,f.miterLimit=P.miterLimit,f.lineJoin=P.join,f.lineCap=P.cap}f.font=m;let w,S;const E=t.dropShadow?2:1,O=t.wordWrap?t.wordWrapWidth:y,M=((l=(o=t._stroke)==null?void 0:o.width)!=null?l:0)/2;let C=(_-x.fontSize)/2;_-x.fontSize<0&&(C=0);for(let P=0;Pt in r?VC(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Dt=(r,t)=>{for(var e in t||(t={}))qC.call(t,e)&&Kb(r,e,t[e]);if(Yb)for(var e of Yb(t))ZC.call(t,e)&&Kb(r,e,t[e]);return r},QC=(r,t)=>YC(r,KC(t));const ql=class dr extends Ut{constructor(t={}){var e;super(),this.uid=nt("textStyle"),this._tick=0,this._cachedFontString=null,JC(t),t instanceof dr&&(t=t._toObject());const i=Dt(Dt({},dr.defaultTextStyle),t);for(const s in i){const n=s;this[n]=i[s]}this._tagStyles=(e=t.tagStyles)!=null?e:void 0,this.update(),this._tick=0}get align(){return this._align}set align(t){this._align!==t&&(this._align=t,this.update())}get breakWords(){return this._breakWords}set breakWords(t){this._breakWords!==t&&(this._breakWords=t,this.update())}get dropShadow(){return this._dropShadow}set dropShadow(t){this._dropShadow!==t&&(t!==null&&typeof t=="object"?this._dropShadow=this._createProxy(Dt(Dt({},dr.defaultDropShadow),t)):this._dropShadow=t?this._createProxy(Dt({},dr.defaultDropShadow)):null,this.update())}get fontFamily(){return this._fontFamily}set fontFamily(t){this._fontFamily!==t&&(this._fontFamily=t,this.update())}get fontSize(){return this._fontSize}set fontSize(t){this._fontSize!==t&&(typeof t=="string"?this._fontSize=parseInt(t,10):this._fontSize=t,this.update())}get fontStyle(){return this._fontStyle}set fontStyle(t){this._fontStyle!==t&&(this._fontStyle=t.toLowerCase(),this.update())}get fontVariant(){return this._fontVariant}set fontVariant(t){this._fontVariant!==t&&(this._fontVariant=t,this.update())}get fontWeight(){return this._fontWeight}set fontWeight(t){this._fontWeight!==t&&(this._fontWeight=t,this.update())}get leading(){return this._leading}set leading(t){this._leading!==t&&(this._leading=t,this.update())}get letterSpacing(){return this._letterSpacing}set letterSpacing(t){this._letterSpacing!==t&&(this._letterSpacing=t,this.update())}get lineHeight(){return this._lineHeight}set lineHeight(t){this._lineHeight!==t&&(this._lineHeight=t,this.update())}get padding(){return this._padding}set padding(t){this._padding!==t&&(this._padding=t,this.update())}get filters(){return this._filters}set filters(t){this._filters!==t&&(this._filters=Object.freeze(t),this.update())}get trim(){return this._trim}set trim(t){this._trim!==t&&(this._trim=t,this.update())}get textBaseline(){return this._textBaseline}set textBaseline(t){this._textBaseline!==t&&(this._textBaseline=t,this.update())}get whiteSpace(){return this._whiteSpace}set whiteSpace(t){this._whiteSpace!==t&&(this._whiteSpace=t,this.update())}get wordWrap(){return this._wordWrap}set wordWrap(t){this._wordWrap!==t&&(this._wordWrap=t,this.update())}get wordWrapWidth(){return this._wordWrapWidth}set wordWrapWidth(t){this._wordWrapWidth!==t&&(this._wordWrapWidth=t,this.update())}get fill(){return this._originalFill}set fill(t){t!==this._originalFill&&(this._originalFill=t,this._isFillStyle(t)&&(this._originalFill=this._createProxy(Dt(Dt({},$t.defaultFillStyle),t),()=>{this._fill=ke(Dt({},this._originalFill),$t.defaultFillStyle)})),this._fill=ke(t===0?"black":t,$t.defaultFillStyle),this.update())}get stroke(){return this._originalStroke}set stroke(t){t!==this._originalStroke&&(this._originalStroke=t,this._isFillStyle(t)&&(this._originalStroke=this._createProxy(Dt(Dt({},$t.defaultStrokeStyle),t),()=>{this._stroke=Ai(Dt({},this._originalStroke),$t.defaultStrokeStyle)})),this._stroke=Ai(t,$t.defaultStrokeStyle),this.update())}get tagStyles(){return this._tagStyles}set tagStyles(t){this._tagStyles!==t&&(this._tagStyles=t!=null?t:void 0,this.update())}update(){this._tick++,this._cachedFontString=null,this.emit("update",this)}reset(){const t=dr.defaultTextStyle;for(const e in t)this[e]=t[e]}assign(t){for(const e in t){const i=e;this[i]=t[e]}return this}get styleKey(){return`${this.uid}-${this._tick}`}get _fontString(){return this._cachedFontString===null&&(this._cachedFontString=jr(this)),this._cachedFontString}_toObject(){return{align:this.align,breakWords:this.breakWords,dropShadow:this._dropShadow?Dt({},this._dropShadow):null,fill:this._fill?Dt({},this._fill):void 0,fontFamily:this.fontFamily,fontSize:this.fontSize,fontStyle:this.fontStyle,fontVariant:this.fontVariant,fontWeight:this.fontWeight,leading:this.leading,letterSpacing:this.letterSpacing,lineHeight:this.lineHeight,padding:this.padding,stroke:this._stroke?Dt({},this._stroke):void 0,textBaseline:this.textBaseline,trim:this.trim,whiteSpace:this.whiteSpace,wordWrap:this.wordWrap,wordWrapWidth:this.wordWrapWidth,filters:this._filters?[...this._filters]:void 0,tagStyles:this._tagStyles?Dt({},this._tagStyles):void 0}}clone(){return new dr(this._toObject())}_getFinalPadding(){let t=0;if(this._filters)for(let e=0;e(i[s]===n||(i[s]=n,e==null||e(s,n),this.update()),!0)})}_isFillStyle(t){return(t!=null?t:null)!==null&&!(J.isColorLike(t)||t instanceof Qt||t instanceof Br)}};ql.defaultDropShadow={alpha:1,angle:Math.PI/6,blur:0,color:"black",distance:5},ql.defaultTextStyle={align:"left",breakWords:!1,dropShadow:null,fill:"black",fontFamily:"Arial",fontSize:26,fontStyle:"normal",fontVariant:"normal",fontWeight:"normal",leading:0,letterSpacing:0,lineHeight:0,padding:0,stroke:null,textBaseline:"alphabetic",trim:!1,whiteSpace:"pre",wordWrap:!1,wordWrapWidth:100};let Lt=ql;function JC(r){var t,e,i,s,n;const a=r;if(typeof a.dropShadow=="boolean"&&a.dropShadow){const o=Lt.defaultDropShadow;r.dropShadow={alpha:(t=a.dropShadowAlpha)!=null?t:o.alpha,angle:(e=a.dropShadowAngle)!=null?e:o.angle,blur:(i=a.dropShadowBlur)!=null?i:o.blur,color:(s=a.dropShadowColor)!=null?s:o.color,distance:(n=a.dropShadowDistance)!=null?n:o.distance}}if(a.strokeThickness!==void 0){const o=a.stroke;let l={};if(J.isColorLike(o))l.color=o;else if(o instanceof Qt||o instanceof Br)l.fill=o;else if(Object.hasOwnProperty.call(o,"color")||Object.hasOwnProperty.call(o,"fill"))l=o;else throw new Error("Invalid stroke value.");r.stroke=QC(Dt({},l),{width:a.strokeThickness})}if(Array.isArray(a.fillGradientStops)){if(!Array.isArray(a.fill)||a.fill.length===0)throw new Error("Invalid fill value. Expected an array of colors for gradient fill.");a.fill.length,a.fillGradientStops.length;const o=new Qt({start:{x:0,y:0},end:{x:0,y:1},textureSpace:"local"}),l=a.fillGradientStops.slice(),u=a.fill.map(c=>J.shared.setValue(c).toNumber());l.forEach((c,h)=>{o.addColorStop(c,u[h])}),r.fill={fill:o}}}function vn(r,t){const{texture:e,bounds:i}=r,s=t._style._getFinalPadding();ka(i,t._anchor,e);const n=t._anchor._x*s*2,a=t._anchor._y*s*2;i.minX-=s-n,i.minY-=s-a,i.maxX-=s-n,i.maxY-=s-a}class ki{constructor(){this.batcherName="default",this.topology="triangle-list",this.attributeSize=4,this.indexSize=6,this.packAsQuad=!0,this.roundPixels=0,this._attributeStart=0,this._batcher=null,this._batch=null}get blendMode(){return this.renderable.groupBlendMode}get color(){return this.renderable.groupColorAlpha}reset(){this.renderable=null,this.texture=null,this._batcher=null,this._batch=null,this.bounds=null}destroy(){this.reset()}}class qb extends ki{}class Zl{constructor(t){this._renderer=t,t.runners.resolutionChange.add(this),this._managedTexts=new Ft({renderer:t,type:"renderable",onUnload:this.onTextUnload.bind(this),name:"canvasText"})}resolutionChange(){for(const t in this._managedTexts.items){const e=this._managedTexts.items[t];e!=null&&e._autoResolution&&e.onViewUpdate()}}validateRenderable(t){const e=this._getGpuText(t),i=t.styleKey;return e.currentKey!==i?!0:t._didTextUpdate}addRenderable(t,e){const i=this._getGpuText(t);if(t._didTextUpdate){const s=t._autoResolution?this._renderer.resolution:t.resolution;(i.currentKey!==t.styleKey||t._resolution!==s)&&this._updateGpuText(t),t._didTextUpdate=!1,vn(i,t)}this._renderer.renderPipes.batch.addToBatch(i,e)}updateRenderable(t){const e=this._getGpuText(t);e._batcher.updateElement(e)}_updateGpuText(t){const e=this._getGpuText(t);e.texture&&this._renderer.canvasText.decreaseReferenceCount(e.currentKey),t._resolution=t._autoResolution?this._renderer.resolution:t.resolution,e.texture=this._renderer.canvasText.getManagedTexture(t),e.currentKey=t.styleKey}_getGpuText(t){return t._gpuData[this._renderer.uid]||this.initGpuText(t)}initGpuText(t){const e=new qb;return e.currentKey="--",e.renderable=t,e.transform=t.groupTransform,e.bounds={minX:0,maxX:1,minY:0,maxY:0},e.roundPixels=this._renderer._roundPixels|t._roundPixels,t._gpuData[this._renderer.uid]=e,this._managedTexts.add(t),e}onTextUnload(t){const e=t._gpuData[this._renderer.uid];if(!e)return;const{canvasText:i}=this._renderer;i.getReferenceCount(e.currentKey)>0?i.decreaseReferenceCount(e.currentKey):e.texture&&i.returnTexture(e.texture)}destroy(){this._managedTexts.destroy(),this._renderer=null}}Zl.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"text"};const tR=new Pt;function yn(r,t,e,i){const s=tR;s.minX=0,s.minY=0,s.maxX=r.width/i|0,s.maxY=r.height/i|0;const n=yt.getOptimalTexture(s.width,s.height,i,!1);return n.source.uploadMethodId="image",n.source.resource=r,n.source.alphaMode="premultiply-alpha-on-upload",n.frame.width=t/i,n.frame.height=e/i,n.source.emit("update",n.source),n.updateUvs(),n}class Ql{constructor(t,e){this._activeTextures={},this._renderer=t,this._retainCanvasContext=e}getTexture(t,e,i,s){var n;typeof t=="string"&&(t={text:t,style:i,resolution:e}),t.style instanceof Lt||(t.style=new Lt(t.style)),t.textureStyle instanceof Kt||(t.textureStyle=new Kt(t.textureStyle)),typeof t.text!="string"&&(t.text=t.text.toString());const{text:a,style:o,textureStyle:l}=t,u=(n=t.resolution)!=null?n:this._renderer.resolution,{frame:c,canvasAndContext:h}=Ie.getCanvasAndContext({text:a,style:o,resolution:u}),p=yn(h.canvas,c.width,c.height,u);if(l&&(p.source.style=l),o.trim&&(c.pad(o.padding),p.frame.copyFrom(c),p.frame.scale(1/u),p.updateUvs()),o.filters){const f=this._applyFilters(p,o.filters);return this.returnTexture(p),Ie.returnCanvasAndContext(h),f}return this._renderer.texture.initSource(p._source),this._retainCanvasContext||Ie.returnCanvasAndContext(h),p}returnTexture(t){const e=t.source,i=e.resource;if(this._retainCanvasContext&&i!=null&&i.getContext){const s=i.getContext("2d");s&&Ie.returnCanvasAndContext({canvas:i,context:s})}e.resource=null,e.uploadMethodId="unknown",e.alphaMode="no-premultiply-alpha",yt.returnTexture(t,!0)}renderTextToCanvas(){}getManagedTexture(t){t._resolution=t._autoResolution?this._renderer.resolution:t.resolution;const e=t.styleKey;if(this._activeTextures[e])return this._increaseReferenceCount(e),this._activeTextures[e].texture;const i=this.getTexture({text:t.text,style:t.style,resolution:t._resolution,textureStyle:t.textureStyle});return this._activeTextures[e]={texture:i,usageCount:1},i}decreaseReferenceCount(t){const e=this._activeTextures[t];e&&(e.usageCount--,e.usageCount===0&&(this.returnTexture(e.texture),this._activeTextures[t]=null))}getReferenceCount(t){var e,i;return(i=(e=this._activeTextures[t])==null?void 0:e.usageCount)!=null?i:0}_increaseReferenceCount(t){this._activeTextures[t].usageCount++}_applyFilters(t,e){const i=this._renderer.renderTarget.renderTarget,s=this._renderer.filter.generateFilteredTexture({texture:t,filters:e});return this._renderer.renderTarget.bind(i,!1),s}destroy(){this._renderer=null;for(const t in this._activeTextures)this._activeTextures[t]&&this.returnTexture(this._activeTextures[t].texture);this._activeTextures=null}}class Jl extends Ql{constructor(t){super(t,!0)}}Jl.extension={type:[T.CanvasSystem],name:"canvasText"};class tu extends Ql{constructor(t){super(t,!1)}}tu.extension={type:[T.WebGLSystem,T.WebGPUSystem],name:"canvasText"},$.add(Jl),$.add(tu),$.add(Zl);class xn extends fn{constructor(...t){const e=mn(t,"Text");super(e,Lt),this.renderPipeId="text",e.textureStyle&&(this.textureStyle=e.textureStyle instanceof Kt?e.textureStyle:new Kt(e.textureStyle))}updateBounds(){const t=this._bounds,e=this._anchor;let i=0,s=0;if(this._style.trim){const{frame:n,canvasAndContext:a}=Ie.getCanvasAndContext({text:this.text,style:this._style,resolution:1});Ie.returnCanvasAndContext(a),i=n.width,s=n.height}else{const n=Ot.measureText(this._text,this._style);i=n.width,s=n.height}t.minX=-e._x*i,t.maxX=t.minX+i,t.minY=-e._y*s,t.maxY=t.minY+s}}class Zb extends j_{resolveQueueItem(t,e){return t instanceof dt?this.resolveContainerQueueItem(t,e):t instanceof ht||t instanceof F?e.push(t.source):t instanceof $t&&e.push(t),null}resolveContainerQueueItem(t,e){t instanceof he||t instanceof Eb||t instanceof Nr?e.push(t.texture.source):t instanceof xn?e.push(t):t instanceof ar?e.push(t.context):t instanceof Fi&&t.textures.forEach(i=>{i.source?e.push(i.source):e.push(i.texture.source)})}resolveGraphicsContextQueueItem(t){this.renderer.graphicsContext.getGpuContext(t);const{instructions:e}=t;for(const i of e)if(i.action==="texture"){const{image:s}=i.data;return s.source}else if(i.action==="fill"){const{texture:s}=i.data.style;return s.source}return null}}class eu extends Ut{constructor(){super(...arguments),this.chars=Object.create(null),this.lineHeight=0,this.fontFamily="",this.fontMetrics={fontSize:0,ascent:0,descent:0},this.baseLineOffset=0,this.distanceField={type:"none",range:0},this.pages=[],this.applyFillAsTint=!0,this.baseMeasurementFontSize=100,this.baseRenderedFontSize=100}get font(){return this.fontFamily}get pageTextures(){return this.pages}get size(){return this.fontMetrics.fontSize}get distanceFieldRange(){return this.distanceField.range}get distanceFieldType(){return this.distanceField.type}destroy(t=!1){var e;this.emit("destroy",this),this.removeAllListeners();for(const i in this.chars)(e=this.chars[i].texture)==null||e.destroy();this.chars=null,t&&(this.pages.forEach(i=>i.texture.destroy(!0)),this.pages=null)}}var eR=Object.defineProperty,Qb=Object.getOwnPropertySymbols,rR=Object.prototype.hasOwnProperty,iR=Object.prototype.propertyIsEnumerable,Jb=(r,t,e)=>t in r?eR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,tv=(r,t)=>{for(var e in t||(t={}))rR.call(t,e)&&Jb(r,e,t[e]);if(Qb)for(var e of Qb(t))iR.call(t,e)&&Jb(r,e,t[e]);return r};const ev=class l1 extends eu{constructor(t){var e,i,s;super(),this.resolution=1,this.pages=[],this._padding=0,this._measureCache=Object.create(null),this._currentChars=[],this._currentX=0,this._currentY=0,this._currentMaxCharHeight=0,this._currentPageIndex=-1,this._skipKerning=!1;const n=tv(tv({},l1.defaultOptions),t);this._textureSize=n.textureSize,this._mipmap=n.mipmap;const a=n.style.clone();n.overrideFill&&(a._fill.color=16777215,a._fill.alpha=1,a._fill.texture=F.WHITE,a._fill.fill=null),this.applyFillAsTint=n.overrideFill;const o=a.fontSize;a.fontSize=this.baseMeasurementFontSize;const l=jr(a);n.overrideSize?a._stroke&&(a._stroke.width*=this.baseRenderedFontSize/o):a.fontSize=this.baseRenderedFontSize=o,this._style=a,this._skipKerning=(e=n.skipKerning)!=null?e:!1,this.resolution=(i=n.resolution)!=null?i:1,this._padding=(s=n.padding)!=null?s:4,n.textureStyle&&(this._textureStyle=n.textureStyle instanceof Kt?n.textureStyle:new Kt(n.textureStyle)),this.fontMetrics=Ot.measureFont(l),this.lineHeight=a.lineHeight||this.fontMetrics.fontSize||a.fontSize}ensureCharacters(t){var e,i,s,n;const a=Ot.graphemeSegmenter(t).filter(v=>!this._currentChars.includes(v)).filter((v,w,S)=>S.indexOf(v)===w);if(!a.length)return;this._currentChars=[...this._currentChars,...a];let o;this._currentPageIndex===-1?o=this._nextPage():o=this.pages[this._currentPageIndex];let{canvas:l,context:u}=o.canvasAndContext,c=o.texture.source;const h=this._style;let p=this._currentX,f=this._currentY,m=this._currentMaxCharHeight;const g=this.baseRenderedFontSize/this.baseMeasurementFontSize,_=this._padding*g;let b=!1;const y=l.width/this.resolution,x=l.height/this.resolution;for(let v=0;vy&&(f+=m,m=P,p=0,f+m>x)){c.update();const G=this._nextPage();l=G.canvasAndContext.canvas,u=G.canvasAndContext.context,c=G.texture.source,p=0,f=0,m=0}const R=E/g-((i=(e=h.dropShadow)==null?void 0:e.distance)!=null?i:0)-((n=(s=h._stroke)==null?void 0:s.width)!=null?n:0);if(this.chars[w]={id:w.codePointAt(0),xOffset:-this._padding,yOffset:-this._padding,xAdvance:R,kerning:{}},b){this._drawGlyph(u,S,p+_,f+_,g,h);const G=c.width*g,I=c.height*g,U=new it(p/G*c.width,f/I*c.height,C/G*c.width,P/I*c.height);this.chars[w].texture=new F({source:c,frame:U}),p+=Math.ceil(C)}}c.update(),this._currentX=p,this._currentY=f,this._currentMaxCharHeight=m,this._skipKerning&&this._applyKerning(a,u)}get pageTextures(){return this.pages}_applyKerning(t,e){const i=this._measureCache;for(let s=0;s{const y=n.width;for(let x=0;x{let b=n.chars.length-1;if(i){let y=n.chars[b];for(;y===" ";)n.width-=e.chars[y].xAdvance,y=n.chars[--b]}s.width=Math.max(s.width,n.width),n={width:0,charPositions:[],chars:[],spaceWidth:0,spacesIndex:[]},o=!0,s.lines.push(n),s.height+=p},_=b=>b-c>h;for(let b=0;bt in r?oR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,iu=(r,t)=>{for(var e in t||(t={}))lR.call(t,e)&&sv(r,e,t[e]);if(iv)for(var e of iv(t))uR.call(t,e)&&sv(r,e,t[e]);return r};let Sn=0,cR=class{constructor(){this.ALPHA=[["a","z"],["A","Z"]," "],this.NUMERIC=[["0","9"]],this.ALPHANUMERIC=[["a","z"],["A","Z"],["0","9"]," "],this.ASCII=[[" ","~"]],this.defaultOptions={chars:this.ALPHANUMERIC,resolution:1,padding:4,skipKerning:!1,textureStyle:null},this.measureCache=Ib(1e3)}getFont(t,e){var i;let s=`${e.fontFamily}-bitmap`,n=!0;if(e._fill.fill&&!e._stroke?(s+=e._fill.fill.styleKey,n=!1):(e._stroke||e.dropShadow)&&(s=`${e.styleKey}-bitmap`,n=!1),!tt.has(s)){const o=Object.create(e);o._lineHeight=0;const l=new ru(iu({style:o,overrideFill:n,overrideSize:!0},this.defaultOptions));Sn++,Sn>50&&ae("BitmapText",`You have dynamically created ${Sn} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``),l.once("destroy",()=>{Sn--,tt.remove(s)}),tt.set(s,l)}const a=tt.get(s);return(i=a.ensureCharacters)==null||i.call(a,t),a}getLayout(t,e,i=!0){const s=this.getFont(t,e),n=`${t}-${e.styleKey}-${i}`;if(this.measureCache.has(n))return this.measureCache.get(n);const a=Ot.graphemeSegmenter(t),o=Tn(a,e,s,i);return this.measureCache.set(n,o),o}measureText(t,e,i=!0){return this.getLayout(t,e,i)}install(...t){var e,i,s,n,a;let o=t[0];typeof o=="string"&&(o={name:o,style:t[1],chars:(e=t[2])==null?void 0:e.chars,resolution:(i=t[2])==null?void 0:i.resolution,padding:(s=t[2])==null?void 0:s.padding,skipKerning:(n=t[2])==null?void 0:n.skipKerning});const l=o==null?void 0:o.name;if(!l)throw new Error("[BitmapFontManager] Property `name` is required.");o=iu(iu({},this.defaultOptions),o);const u=o.style,c=u instanceof Lt?u:new Lt(u),h=(a=o.dynamicFill)!=null?a:this._canUseTintForStyle(c),p=new ru({style:c,overrideFill:h,skipKerning:o.skipKerning,padding:o.padding,resolution:o.resolution,overrideSize:!1,textureStyle:o.textureStyle}),f=rv(o.chars);return p.ensureCharacters(f.join("")),tt.set(`${l}-bitmap`,p),p.once("destroy",()=>tt.remove(`${l}-bitmap`)),p}uninstall(t){const e=`${t}-bitmap`,i=tt.get(e);i&&i.destroy()}_canUseTintForStyle(t){return!t._stroke&&(!t.dropShadow||t.dropShadow.color===0)&&!t._fill.fill&&t._fill.color===16777215}};const zr=new cR;class nv extends ar{destroy(){this.context.customShader&&this.context.customShader.destroy(),super.destroy()}}class su{constructor(t){this._renderer=t,this._managedBitmapTexts=new Ft({renderer:t,type:"renderable",priority:-2,name:"bitmapText"})}validateRenderable(t){const e=this._getGpuBitmapText(t);return this._renderer.renderPipes.graphics.validateRenderable(e)}addRenderable(t,e){const i=this._getGpuBitmapText(t);av(t,i),t._didTextUpdate&&(t._didTextUpdate=!1,this._updateContext(t,i)),this._renderer.renderPipes.graphics.addRenderable(i,e),i.context.customShader&&this._updateDistanceField(t)}updateRenderable(t){const e=this._getGpuBitmapText(t);av(t,e),this._renderer.renderPipes.graphics.updateRenderable(e),e.context.customShader&&this._updateDistanceField(t)}_updateContext(t,e){const{context:i}=e,s=zr.getFont(t.text,t._style);if(i.clear(),s.distanceField.type!=="none"){const b=this.getSdfShader();b&&(i.customShader||(i.customShader=b))}const n=Ot.graphemeSegmenter(t.text),a=t._style;let o=s.baseLineOffset;const l=Tn(n,a,s,!0),u=a.padding,c=l.scale;let h=l.width,p=l.height+l.offsetY;a._stroke&&(h+=a._stroke.width/c,p+=a._stroke.width/c),i.translate(-t._anchor._x*h-u,-t._anchor._y*p-u).scale(c,c);const f=s.applyFillAsTint?a._fill.color:16777215;let m=s.fontMetrics.fontSize,g=s.lineHeight;a.lineHeight&&(m=a.fontSize/c,g=a.lineHeight/c);let _=(g-m)/2;_-s.baseLineOffset<0&&(_=0);for(let b=0;b, + uTransformMatrix:mat3x3, + uDistance: f32, + uRound:f32, + } + + @group(2) @binding(0) var localUniforms : LocalUniforms; + `,main:` + vColor *= localUniforms.uColor; + modelMatrix *= localUniforms.uTransformMatrix; + `,end:` + if(localUniforms.uRound == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + `},fragment:{header:` + struct LocalUniforms { + uColor:vec4, + uTransformMatrix:mat3x3, + uDistance: f32 + } + + @group(2) @binding(0) var localUniforms : LocalUniforms; + `,main:` + outColor = vec4(calculateMSDFAlpha(outColor, vColor, localUniforms.uDistance)); + `}},lv={name:"local-uniform-msdf-bit",vertex:{header:` + uniform mat3 uTransformMatrix; + uniform vec4 uColor; + uniform float uRound; + `,main:` + vColor *= uColor; + modelMatrix *= uTransformMatrix; + `,end:` + if(uRound == 1.) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + `},fragment:{header:` + uniform float uDistance; + `,main:` + outColor = vec4(calculateMSDFAlpha(outColor, vColor, uDistance)); + `}},uv={name:"msdf-bit",fragment:{header:` + fn calculateMSDFAlpha(msdfColor:vec4, shapeColor:vec4, distance:f32) -> f32 { + + // MSDF + var median = msdfColor.r + msdfColor.g + msdfColor.b - + min(msdfColor.r, min(msdfColor.g, msdfColor.b)) - + max(msdfColor.r, max(msdfColor.g, msdfColor.b)); + + // SDF + median = min(median, msdfColor.a); + + var screenPxDistance = distance * (median - 0.5); + var alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0); + if (median < 0.01) { + alpha = 0.0; + } else if (median > 0.99) { + alpha = 1.0; + } + + // Gamma correction for coverage-like alpha + var luma: f32 = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114)); + var gamma: f32 = mix(1.0, 1.0 / 2.2, luma); + var coverage: f32 = pow(shapeColor.a * alpha, gamma); + + return coverage; + + } + `}},cv={name:"msdf-bit",fragment:{header:` + float calculateMSDFAlpha(vec4 msdfColor, vec4 shapeColor, float distance) { + + // MSDF + float median = msdfColor.r + msdfColor.g + msdfColor.b - + min(msdfColor.r, min(msdfColor.g, msdfColor.b)) - + max(msdfColor.r, max(msdfColor.g, msdfColor.b)); + + // SDF + median = min(median, msdfColor.a); + + float screenPxDistance = distance * (median - 0.5); + float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0); + + if (median < 0.01) { + alpha = 0.0; + } else if (median > 0.99) { + alpha = 1.0; + } + + // Gamma correction for coverage-like alpha + float luma = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114)); + float gamma = mix(1.0, 1.0 / 2.2, luma); + float coverage = pow(shapeColor.a * alpha, gamma); + + return coverage; + } + `}};let au,ou;class hv extends Zt{constructor(t){const e=new wt({uColor:{value:new Float32Array([1,1,1,1]),type:"vec4"},uTransformMatrix:{value:new D,type:"mat3x3"},uDistance:{value:4,type:"f32"},uRound:{value:0,type:"f32"}});au!=null||(au=Cr({name:"sdf-shader",bits:[Cs,Ms(t),ov,uv,Mr]})),ou!=null||(ou=Rr({name:"sdf-shader",bits:[Rs,Os(t),lv,cv,Or]})),super({glProgram:ou,gpuProgram:au,resources:{localUniforms:e,batchSamplers:Gs(t)}})}}class lu extends su{getSdfShader(){return new hv(this._renderer.limits.maxBatchableTextures)}}lu.extension={type:[T.WebGLPipes,T.WebGPUPipes],name:"bitmapText"},$.add(nu),$.add(lu);class uu extends fn{constructor(...t){var e,i,s;const n=mn(t,"BitmapText");(e=n.style)!=null||(n.style=n.style||{}),(s=(i=n.style).fill)!=null||(i.fill=16777215),super(n,Lt),this.renderPipeId="bitmapText"}_onTouch(t){var e;this._gcLastUsed=t;for(const i in this._gpuData)(e=this._gpuData[i])==null||e._onTouch(t)}updateBounds(){const t=this._bounds,e=this._anchor,i=zr.measureText(this.text,this._style),s=i.scale,n=i.offsetY*s;let a=i.width*s,o=i.height*s;const l=this._style._stroke;l&&(a+=l.width,o+=l.width),t.minX=-e._x*a,t.maxX=t.minX+a,t.minY=-e._y*(o+n),t.maxY=t.minY+o}set resolution(t){}get resolution(){return this._resolution}}var hR=Object.defineProperty,dv=Object.getOwnPropertySymbols,dR=Object.prototype.hasOwnProperty,pR=Object.prototype.propertyIsEnumerable,pv=(r,t,e)=>t in r?hR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,cu=(r,t)=>{for(var e in t||(t={}))dR.call(t,e)&&pv(r,e,t[e]);if(dv)for(var e of dv(t))pR.call(t,e)&&pv(r,e,t[e]);return r};function fv(r){var t;const e=r._stroke,i=r._fill,s=[`div { ${[`color: ${J.shared.setValue(i.color).setAlpha((t=i.alpha)!=null?t:1).toHexa()}`,`font-size: ${r.fontSize}px`,`font-family: ${r.fontFamily}`,`font-weight: ${r.fontWeight}`,`font-style: ${r.fontStyle}`,`font-variant: ${r.fontVariant}`,`letter-spacing: ${r.letterSpacing}px`,`text-align: ${r.align}`,`padding: ${r.padding}px`,`white-space: ${r.whiteSpace==="pre"&&r.wordWrap?"pre-wrap":r.whiteSpace}`,...r.lineHeight?[`line-height: ${r.lineHeight}px`]:[],...r.wordWrap?[`word-break: ${r.breakWords?"break-all":"normal"}`,`max-width: ${r.wordWrapWidth}px`]:[],...e?[mv(e)]:[],...r.dropShadow?[hu(r.dropShadow)]:[],...r.cssOverrides].join(";")} }`];return fR(r.tagStyles,s),s.join(" ")}function hu(r){var t;const e=cu({},r),i=J.shared.setValue(e.color).setAlpha((t=e.alpha)!=null?t:1).toHexa(),s=Math.round(Math.cos(e.angle)*e.distance),n=Math.round(Math.sin(e.angle)*e.distance),a=`${s}px ${n}px`;return e.blur>0?`text-shadow: ${a} ${e.blur}px ${i}`:`text-shadow: ${a} ${i}`}function mv(r){var t;const e=J.shared.setValue(r.color).setAlpha((t=r.alpha)!=null?t:1).toHexa();return[`-webkit-text-stroke-width: ${r.width}px`,`-webkit-text-stroke-color: ${e}`,`text-stroke-width: ${r.width}px`,`text-stroke-color: ${e}`,"paint-order: stroke"].join(";")}const gv={fontSize:"font-size: {{VALUE}}px",fontFamily:"font-family: {{VALUE}}",fontWeight:"font-weight: {{VALUE}}",fontStyle:"font-style: {{VALUE}}",fontVariant:"font-variant: {{VALUE}}",letterSpacing:"letter-spacing: {{VALUE}}px",align:"text-align: {{VALUE}}",padding:"padding: {{VALUE}}px",whiteSpace:"white-space: {{VALUE}}",lineHeight:"line-height: {{VALUE}}px",wordWrapWidth:"max-width: {{VALUE}}px"},_v={fill:r=>`color: ${J.shared.setValue(r).toHexa()}`,breakWords:r=>`word-break: ${r?"break-all":"normal"}`,stroke:mv,dropShadow:r=>r===!0?hu(Lt.defaultDropShadow):r&&typeof r=="object"?hu(cu(cu({},Lt.defaultDropShadow),r)):""};function fR(r,t){for(const e in r){const i=r[e],s=[];for(const n in i)_v[n]?s.push(_v[n](i[n])):gv[n]&&s.push(gv[n].replace("{{VALUE}}",i[n]));t.push(`${e} { ${s.join(";")} }`)}}var mR=Object.defineProperty,bv=Object.getOwnPropertySymbols,gR=Object.prototype.hasOwnProperty,_R=Object.prototype.propertyIsEnumerable,vv=(r,t,e)=>t in r?mR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,yv=(r,t)=>{for(var e in t||(t={}))gR.call(t,e)&&vv(r,e,t[e]);if(bv)for(var e of bv(t))_R.call(t,e)&&vv(r,e,t[e]);return r};class wn extends Lt{constructor(t={}){var e,i;super(t),this._cssOverrides=[],this.cssOverrides=(e=t.cssOverrides)!=null?e:[],this.tagStyles=(i=t.tagStyles)!=null?i:{}}get tagStyles(){return this._tagStyles}set tagStyles(t){this._tagStyles!==t&&(this._tagStyles=t!=null?t:{},this.update())}set cssOverrides(t){this._cssOverrides=t instanceof Array?t:[t],this.update()}get cssOverrides(){return this._cssOverrides}update(){this._cssStyle=null,super.update()}clone(){return new wn({align:this.align,breakWords:this.breakWords,dropShadow:this.dropShadow?yv({},this.dropShadow):null,fill:this._fill,fontFamily:this.fontFamily,fontSize:this.fontSize,fontStyle:this.fontStyle,fontVariant:this.fontVariant,fontWeight:this.fontWeight,letterSpacing:this.letterSpacing,lineHeight:this.lineHeight,padding:this.padding,stroke:this._stroke,whiteSpace:this.whiteSpace,wordWrap:this.wordWrap,wordWrapWidth:this.wordWrapWidth,cssOverrides:this.cssOverrides,tagStyles:yv({},this.tagStyles)})}get cssStyle(){return this._cssStyle||(this._cssStyle=fv(this)),this._cssStyle}addOverride(...t){const e=t.filter(i=>!this.cssOverrides.includes(i));e.length>0&&(this.cssOverrides.push(...e),this.update())}removeOverride(...t){const e=t.filter(i=>this.cssOverrides.includes(i));e.length>0&&(this.cssOverrides=this.cssOverrides.filter(i=>!e.includes(i)),this.update())}set fill(t){super.fill=t}set stroke(t){super.stroke=t}}const xv="http://www.w3.org/2000/svg",Tv="http://www.w3.org/1999/xhtml";class du{constructor(){this.svgRoot=document.createElementNS(xv,"svg"),this.foreignObject=document.createElementNS(xv,"foreignObject"),this.domElement=document.createElementNS(Tv,"div"),this.styleElement=document.createElementNS(Tv,"style");const{foreignObject:t,svgRoot:e,styleElement:i,domElement:s}=this;t.setAttribute("width","10000"),t.setAttribute("height","10000"),t.style.overflow="hidden",e.appendChild(t),t.appendChild(i),t.appendChild(s),this.image=L.get().createImage()}destroy(){this.svgRoot.remove(),this.foreignObject.remove(),this.styleElement.remove(),this.domElement.remove(),this.image.src="",this.image.remove(),this.svgRoot=null,this.foreignObject=null,this.styleElement=null,this.domElement=null,this.image=null,this.canvasAndContext=null}}let Sv;function pu(r,t,e,i){i||(i=Sv||(Sv=new du));const{domElement:s,styleElement:n,svgRoot:a}=i;s.innerHTML=`
${r}
`,s.setAttribute("style","transform-origin: top left; display: inline-block"),e&&(n.textContent=e),document.body.appendChild(a);let o=s.scrollWidth,l=s.scrollHeight;if(a.remove(),t.dropShadow){const{distance:c,angle:h,blur:p}=t.dropShadow,f=Math.abs(Math.round(Math.cos(h)*c)),m=Math.abs(Math.round(Math.sin(h)*c));o+=f+p,l+=m+p}const u=t.padding*2;return{width:o-u,height:l-u}}class wv extends ki{constructor(){super(...arguments),this.generatingTexture=!1,this.currentKey="--"}destroy(){this.texturePromise=null,this.generatingTexture=!1,this.currentKey="--",super.destroy()}}class fu{constructor(t){this._renderer=t,t.runners.resolutionChange.add(this),this._managedTexts=new Ft({renderer:t,type:"renderable",onUnload:this.onTextUnload.bind(this),name:"htmlText"})}resolutionChange(){for(const t in this._managedTexts.items){const e=this._managedTexts.items[t];e!=null&&e._autoResolution&&e.onViewUpdate()}}validateRenderable(t){const e=this._getGpuText(t),i=t.styleKey;return e.currentKey!==i}addRenderable(t,e){const i=this._getGpuText(t);if(t._didTextUpdate){const s=t._autoResolution?this._renderer.resolution:t.resolution;(i.currentKey!==t.styleKey||t.resolution!==s)&&this._updateGpuText(t).catch(n=>{console.error(n)}),t._didTextUpdate=!1,vn(i,t)}this._renderer.renderPipes.batch.addToBatch(i,e)}updateRenderable(t){const e=this._getGpuText(t);e._batcher.updateElement(e)}async _updateGpuText(t){t._didTextUpdate=!1;const e=this._getGpuText(t);if(e.generatingTexture)return;const i=e.texturePromise;e.texturePromise=null,e.generatingTexture=!0,t._resolution=t._autoResolution?this._renderer.resolution:t.resolution;let s=this._renderer.htmlText.getTexturePromise(t);i&&(s=s.finally(()=>{this._renderer.htmlText.decreaseReferenceCount(e.currentKey),this._renderer.htmlText.returnTexturePromise(i)})),e.texturePromise=s,e.currentKey=t.styleKey,e.texture=await s;const n=t.renderGroup||t.parentRenderGroup;n&&(n.structureDidChange=!0),e.generatingTexture=!1,vn(e,t)}_getGpuText(t){return t._gpuData[this._renderer.uid]||this.initGpuText(t)}initGpuText(t){const e=new wv;return e.renderable=t,e.transform=t.groupTransform,e.texture=F.EMPTY,e.bounds={minX:0,maxX:1,minY:0,maxY:0},e.roundPixels=this._renderer._roundPixels|t._roundPixels,t._resolution=t._autoResolution?this._renderer.resolution:t.resolution,t._gpuData[this._renderer.uid]=e,this._managedTexts.add(t),e}onTextUnload(t){const e=t._gpuData[this._renderer.uid];if(!e)return;const{htmlText:i}=this._renderer;i.getReferenceCount(e.currentKey)===null?i.returnTexturePromise(e.texturePromise):i.decreaseReferenceCount(e.currentKey)}destroy(){this._managedTexts.destroy(),this._renderer=null}}fu.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"htmlText"};function mu(){const{userAgent:r}=L.get().getNavigator();return/^((?!chrome|android).)*safari/i.test(r)}function Ev(r,t){const e=t.fontFamily,i=[],s={},n=/font-family:([^;"\s]+)/g,a=r.match(n);function o(l){s[l]||(i.push(l),s[l]=!0)}if(Array.isArray(e))for(let l=0;l{const u=l.split(":")[1].trim();o(u)});for(const l in t.tagStyles){const u=t.tagStyles[l].fontFamily;o(u)}return i}async function Pv(r){const t=await(await L.get().fetch(r)).blob(),e=new FileReader;return await new Promise((i,s)=>{e.onloadend=()=>i(e.result),e.onerror=s,e.readAsDataURL(t)})}async function Av(r,t){const e=await Pv(t);return`@font-face { + font-family: "${r.fontFamily}"; + font-weight: ${r.fontWeight}; + font-style: ${r.fontStyle}; + src: url('${e}'); + }`}const En=new Map;async function Cv(r){const t=r.filter(e=>tt.has(`${e}-and-url`)).map(e=>{if(!En.has(e)){const{entries:i}=tt.get(`${e}-and-url`),s=[];i.forEach(n=>{const a=n.url,o=n.faces.map(l=>({weight:l.weight,style:l.style}));s.push(...o.map(l=>Av({fontWeight:l.weight,fontStyle:l.style,fontFamily:e},a)))}),En.set(e,Promise.all(s).then(n=>n.join(` +`)))}return En.get(e)});return(await Promise.all(t)).join(` +`)}function Rv(r,t,e,i,s){const{domElement:n,styleElement:a,svgRoot:o}=s;n.innerHTML=`
${r}
`,n.setAttribute("style",`transform: scale(${e});transform-origin: top left; display: inline-block`),a.textContent=i;const{width:l,height:u}=s.image;return o.setAttribute("width",l.toString()),o.setAttribute("height",u.toString()),new XMLSerializer().serializeToString(o)}function Mv(r,t){const e=me.getOptimalCanvasAndContext(r.width,r.height,t),{context:i}=e;return i.clearRect(0,0,r.width,r.height),i.drawImage(r,0,0),e}function Ov(r,t,e){return new Promise(async i=>{e&&await new Promise(s=>setTimeout(s,100)),r.onload=()=>{i()},r.src=`data:image/svg+xml;charset=utf8,${encodeURIComponent(t)}`,r.crossOrigin="anonymous"})}class gu{constructor(t){this._activeTextures={},this._renderer=t,this._createCanvas=t.type===Gt.WEBGPU}getTexture(t){return this.getTexturePromise(t)}getManagedTexture(t){const e=t.styleKey;if(this._activeTextures[e])return this._increaseReferenceCount(e),this._activeTextures[e].promise;const i=this._buildTexturePromise(t).then(s=>(this._activeTextures[e].texture=s,s));return this._activeTextures[e]={texture:null,promise:i,usageCount:1},i}getReferenceCount(t){var e,i;return(i=(e=this._activeTextures[t])==null?void 0:e.usageCount)!=null?i:null}_increaseReferenceCount(t){this._activeTextures[t].usageCount++}decreaseReferenceCount(t){const e=this._activeTextures[t];e&&(e.usageCount--,e.usageCount===0&&(e.texture?this._cleanUp(e.texture):e.promise.then(i=>{e.texture=i,this._cleanUp(e.texture)}).catch(()=>{}),this._activeTextures[t]=null))}getTexturePromise(t){return this._buildTexturePromise(t)}async _buildTexturePromise(t){const{text:e,style:i,resolution:s,textureStyle:n}=t,a=St.get(du),o=Ev(e,i),l=await Cv(o),u=pu(e,i,l,a),c=Math.ceil(Math.ceil(Math.max(1,u.width)+i.padding*2)*s),h=Math.ceil(Math.ceil(Math.max(1,u.height)+i.padding*2)*s),p=a.image,f=2;p.width=(c|0)+f,p.height=(h|0)+f;const m=Rv(e,i,s,l,a);await Ov(p,m,mu()&&o.length>0);const g=p;let _;this._createCanvas&&(_=Mv(p,s));const b=yn(_?_.canvas:g,p.width-f,p.height-f,s);return n&&(b.source.style=n),this._createCanvas&&(this._renderer.texture.initSource(b.source),me.returnCanvasAndContext(_)),St.return(a),b}returnTexturePromise(t){t.then(e=>{this._cleanUp(e)}).catch(()=>{})}_cleanUp(t){yt.returnTexture(t,!0),t.source.resource=null,t.source.uploadMethodId="unknown"}destroy(){this._renderer=null;for(const t in this._activeTextures)this._activeTextures[t]&&this.returnTexturePromise(this._activeTextures[t].promise);this._activeTextures=null}}gu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"htmlText"},$.add(gu),$.add(fu);class Gv extends fn{constructor(...t){const e=mn(t,"HtmlText");super(e,wn),this.renderPipeId="htmlText",e.textureStyle&&(this.textureStyle=e.textureStyle instanceof Kt?e.textureStyle:new Kt(e.textureStyle))}updateBounds(){const t=this._bounds,e=this._anchor,i=pu(this.text,this._style),{width:s,height:n}=i;t.minX=-e._x*s,t.maxX=t.minX+s,t.minY=-e._y*n,t.maxY=t.minY+n}get text(){return this._text}set text(t){const e=this._sanitiseText(t.toString());super.text=e}_sanitiseText(t){return this._removeInvalidHtmlTags(t.replace(/
/gi,"
").replace(/
/gi,"
").replace(/ /gi," "))}_removeInvalidHtmlTags(t){const e=/<[^>]*?(?=<|$)/g;return t.replace(e,"")}}class Iv extends Zb{uploadQueueItem(t){t instanceof ht?this.uploadTextureSource(t):t instanceof xn?this.uploadText(t):t instanceof Gv?this.uploadHTMLText(t):t instanceof uu?this.uploadBitmapText(t):t instanceof $t&&this.uploadGraphicsContext(t)}uploadTextureSource(t){this.renderer.texture.initSource(t)}uploadText(t){this.renderer.renderPipes.text.initGpuText(t)}uploadBitmapText(t){this.renderer.renderPipes.bitmapText.initGpuText(t)}uploadHTMLText(t){this.renderer.renderPipes.htmlText.initGpuText(t)}uploadGraphicsContext(t){this.renderer.graphicsContext.getGpuContext(t);const{instructions:e}=t;for(const i of e)if(i.action==="texture"){const{image:s}=i.data;this.uploadTextureSource(s.source)}else if(i.action==="fill"){const{texture:s}=i.data.style;this.uploadTextureSource(s.source)}return null}}class Bv extends Iv{destroy(){super.destroy(),clearTimeout(this.timeout),this.renderer=null,this.queue=null,this.resolves=null}}Bv.extension={type:[T.WebGLSystem,T.WebGPUSystem],name:"prepare"};const _u=class pr{static _getPatternRepeat(t,e){const i=t&&t!=="clamp-to-edge",s=e&&e!=="clamp-to-edge";return i&&s?"repeat":i?"repeat-x":s?"repeat-y":"no-repeat"}start(t,e,i){}execute(t,e){var i,s,n,a,o,l,u,c,h,p;const f=e.elements;if(!f||!f.length)return;const m=t.renderer,g=m.canvasContext,_=g.activeContext;for(let b=0;b>>24&255)/255,R=(C>>>24&255)/255,G=(a=(n=m.filter)==null?void 0:n.alphaMultiplier)!=null?a:1,I=P*R*G;if(I<=0)continue;_.globalAlpha=I;const U=M&16777215,A=C&16777215,B=be(Ce(A,U)),Q=v.frame,N=(o=S.addressModeU)!=null?o:S.addressMode,q=(l=S.addressModeV)!=null?l:S.addressMode,k=pr._getPatternRepeat(N,q),j=(c=(u=v.source._resolution)!=null?u:v.source.resolution)!=null?c:1,X=(p=(h=x.renderable)==null?void 0:h.renderGroup)==null?void 0:p.isCachedAsTexture,W=Q.x*j,Z=Q.y*j,ut=Q.width*j,It=Q.height*j,at=x.bounds,pt=m.renderTarget.renderTarget.isRoot,ft=at.minX,_t=at.minY,ct=at.maxX-at.minX,mt=at.maxY-at.minY,ot=v.rotate,z=v.uvs,At=Math.min(z.x0,z.x1,z.x2,z.x3,z.y0,z.y1,z.y2,z.y3),Et=Math.max(z.x0,z.x1,z.x2,z.x3,z.y0,z.y1,z.y2,z.y3),le=k!=="no-repeat"&&(At<0||Et>1),ih=ot&&!(!le&&(B!==16777215||ot));ih?(pr._tempPatternMatrix.copyFrom(x.transform),H.matrixAppendRotationInv(pr._tempPatternMatrix,ot,ft,_t,ct,mt),g.setContextTransform(pr._tempPatternMatrix,x.roundPixels===1,void 0,X&&pt)):g.setContextTransform(x.transform,x.roundPixels===1,void 0,X&&pt);const la=ih?0:ft,ua=ih?0:_t,sh=ct,nh=mt;if(le){let hr=w;const Kr=B!==16777215&&!ot,lI=Q.width<=v.source.width&&Q.height<=v.source.height;Kr&&lI&&(hr=K.getTintedCanvas({texture:v},B));const ah=_.createPattern(hr,k);if(!ah)continue;const UT=sh,kT=nh;if(UT===0||kT===0)continue;const $T=1/UT,LT=1/kT,NT=(z.x1-z.x0)*$T,XT=(z.y1-z.y0)*$T,HT=(z.x3-z.x0)*LT,jT=(z.y3-z.y0)*LT,uI=z.x0-NT*la-HT*ua,cI=z.y0-XT*la-jT*ua,oh=v.source.pixelWidth,lh=v.source.pixelHeight;pr._tempPatternMatrix.set(NT*oh,XT*lh,HT*oh,jT*lh,uI*oh,cI*lh),K.applyPatternTransform(ah,pr._tempPatternMatrix),_.fillStyle=ah,_.fillRect(la,ua,sh,nh)}else{const hr=B!==16777215||ot?K.getTintedCanvas({texture:v},B):w,Kr=hr!==w;_.drawImage(hr,Kr?0:W,Kr?0:Z,Kr?hr.width:ut,Kr?hr.height:It,la,ua,sh,nh)}}}};_u._tempPatternMatrix=new D,_u.extension={type:[T.CanvasPipesAdaptor],name:"batch"};let Fv=_u;class bu{constructor(){this._tempState=jt.for2d(),this._didUploadHash={}}init(t){t.renderer.runners.contextChange.add(this)}contextChange(){this._didUploadHash={}}start(t,e,i){const s=t.renderer,n=this._didUploadHash[i.uid];s.shader.bind(i,n),n||(this._didUploadHash[i.uid]=!0),s.shader.updateUniformGroup(s.globalUniforms.uniformGroup),s.geometry.bind(e,i.glProgram)}execute(t,e){const i=t.renderer;this._tempState.blendMode=e.blendMode,i.state.set(this._tempState);const s=e.textures.textures;for(let n=0;ni.trim()).filter(i=>i.length);let e="";return t.map(i=>{let s=e+i;return i==="{"?e+=" ":i==="}"&&(e=e.substr(0,e.length-4),s=e+i),s}).join(` +`)}const Dv={name:"texture-bit",vertex:{header:` + + struct TextureUniforms { + uTextureMatrix:mat3x3, + } + + @group(2) @binding(2) var textureUniforms : TextureUniforms; + `,main:` + uv = (textureUniforms.uTextureMatrix * vec3(uv, 1.0)).xy; + `},fragment:{header:` + @group(2) @binding(0) var uTexture: texture_2d; + @group(2) @binding(1) var uSampler: sampler; + + + `,main:` + outColor = textureSample(uTexture, uSampler, vUV); + `}},Uv={name:"texture-bit",vertex:{header:` + uniform mat3 uTextureMatrix; + `,main:` + uv = (uTextureMatrix * vec3(uv, 1.0)).xy; + `},fragment:{header:` + uniform sampler2D uTexture; + + + `,main:` + outColor = texture(uTexture, vUV); + `}},xR=new Pt;let TR=class extends Jr{constructor(){super(),this.filters=[new X_({sprite:new he(F.EMPTY),inverse:!1,resolution:"inherit",antialias:"inherit"})]}get sprite(){return this.filters[0].sprite}set sprite(t){this.filters[0].sprite=t}get inverse(){return this.filters[0].inverse}set inverse(t){this.filters[0].inverse=t}};class Cn{constructor(t){this._activeMaskStage=[],this._renderer=t}push(t,e,i){const s=this._renderer;if(s.renderPipes.batch.break(i),i.add({renderPipeId:"alphaMask",action:"pushMaskBegin",mask:t,inverse:e._maskOptions.inverse,canBundle:!1,maskedContainer:e}),t.inverse=e._maskOptions.inverse,t.renderMaskToTexture){const n=t.mask;n.includeInBuild=!0,n.collectRenderables(i,s,null),n.includeInBuild=!1}s.renderPipes.batch.break(i),i.add({renderPipeId:"alphaMask",action:"pushMaskEnd",mask:t,maskedContainer:e,inverse:e._maskOptions.inverse,canBundle:!1})}pop(t,e,i){this._renderer.renderPipes.batch.break(i),i.add({renderPipeId:"alphaMask",action:"popMaskEnd",mask:t,inverse:e._maskOptions.inverse,canBundle:!1})}execute(t){const e=this._renderer,i=t.mask.renderMaskToTexture;if(t.action==="pushMaskBegin"){const s=St.get(TR);if(s.inverse=t.inverse,i){t.mask.mask.measurable=!0;const n=ti(t.mask.mask,!0,xR);t.mask.mask.measurable=!1,n.ceil();const a=e.renderTarget.renderTarget.colorTexture.source,o=yt.getOptimalTexture(n.width,n.height,a._resolution,a.antialias);e.renderTarget.push(o,!0),e.globalUniforms.push({offset:n,worldColor:4294967295});const l=s.sprite;l.texture=o,l.worldTransform.tx=n.minX,l.worldTransform.ty=n.minY,this._activeMaskStage.push({filterEffect:s,maskedContainer:t.maskedContainer,filterTexture:o})}else s.sprite=t.mask.mask,this._activeMaskStage.push({filterEffect:s,maskedContainer:t.maskedContainer})}else if(t.action==="pushMaskEnd"){const s=this._activeMaskStage[this._activeMaskStage.length-1];i&&(e.type===Gt.WEBGL&&e.renderTarget.finishRenderPass(),e.renderTarget.pop(),e.globalUniforms.pop()),e.filter.push({renderPipeId:"filter",action:"pushFilter",container:s.maskedContainer,filterEffect:s.filterEffect,canBundle:!1})}else if(t.action==="popMaskEnd"){e.filter.pop();const s=this._activeMaskStage.pop();i&&yt.returnTexture(s.filterTexture),St.return(s.filterEffect)}}destroy(){this._renderer=null,this._activeMaskStage=null}}Cn.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"alphaMask"};class xu{constructor(t){this._colorStack=[],this._colorStackIndex=0,this._currentColor=0,this._renderer=t}buildStart(){this._colorStack[0]=15,this._colorStackIndex=1,this._currentColor=15}push(t,e,i){this._renderer.renderPipes.batch.break(i);const s=this._colorStack;s[this._colorStackIndex]=s[this._colorStackIndex-1]&t.mask;const n=this._colorStack[this._colorStackIndex];n!==this._currentColor&&(this._currentColor=n,i.add({renderPipeId:"colorMask",colorMask:n,canBundle:!1})),this._colorStackIndex++}pop(t,e,i){this._renderer.renderPipes.batch.break(i);const s=this._colorStack;this._colorStackIndex--;const n=s[this._colorStackIndex-1];n!==this._currentColor&&(this._currentColor=n,i.add({renderPipeId:"colorMask",colorMask:n,canBundle:!1}))}execute(t){}destroy(){this._renderer=null,this._colorStack=null}}xu.extension={type:[T.CanvasPipes],name:"colorMask"};class Tu{constructor(t){this._colorStack=[],this._colorStackIndex=0,this._currentColor=0,this._renderer=t}buildStart(){this._colorStack[0]=15,this._colorStackIndex=1,this._currentColor=15}push(t,e,i){this._renderer.renderPipes.batch.break(i);const s=this._colorStack;s[this._colorStackIndex]=s[this._colorStackIndex-1]&t.mask;const n=this._colorStack[this._colorStackIndex];n!==this._currentColor&&(this._currentColor=n,i.add({renderPipeId:"colorMask",colorMask:n,canBundle:!1})),this._colorStackIndex++}pop(t,e,i){this._renderer.renderPipes.batch.break(i);const s=this._colorStack;this._colorStackIndex--;const n=s[this._colorStackIndex-1];n!==this._currentColor&&(this._currentColor=n,i.add({renderPipeId:"colorMask",colorMask:n,canBundle:!1}))}execute(t){this._renderer.colorMask.setMask(t.colorMask)}destroy(){this._renderer=null,this._colorStack=null}}Tu.extension={type:[T.WebGLPipes,T.WebGPUPipes],name:"colorMask"};class SR{constructor(t){this.priority=0,this.pipe="scissorMask",this.mask=t,this.mask.renderable=!1,this.mask.measurable=!1}addBounds(t,e){cs(this.mask,t,e)}addLocalBounds(t,e){hs(this.mask,t,e)}containsPoint(t,e){const i=this.mask;return e(i,t)}reset(){this.mask!==null&&(this.mask.measurable=!0,this.mask=null)}destroy(){this.reset()}}function wR(r,t,e,i,s,n){n=Math.max(0,Math.min(n,Math.min(i,s)/2)),r.moveTo(t+n,e),r.lineTo(t+i-n,e),r.quadraticCurveTo(t+i,e,t+i,e+n),r.lineTo(t+i,e+s-n),r.quadraticCurveTo(t+i,e+s,t+i-n,e+s),r.lineTo(t+n,e+s),r.quadraticCurveTo(t,e+s,t,e+s-n),r.lineTo(t,e+n),r.quadraticCurveTo(t,e,t+n,e)}function kv(r,t){switch(t.type){case"rectangle":{const e=t;r.rect(e.x,e.y,e.width,e.height);break}case"roundedRectangle":{const e=t;wR(r,e.x,e.y,e.width,e.height,e.radius);break}case"circle":{const e=t;r.moveTo(e.x+e.radius,e.y),r.arc(e.x,e.y,e.radius,0,Math.PI*2);break}case"ellipse":{const e=t;r.ellipse?(r.moveTo(e.x+e.halfWidth,e.y),r.ellipse(e.x,e.y,e.halfWidth,e.halfHeight,0,0,Math.PI*2)):(r.save(),r.translate(e.x,e.y),r.scale(e.halfWidth,e.halfHeight),r.moveTo(1,0),r.arc(0,0,1,0,Math.PI*2),r.restore());break}case"triangle":{const e=t;r.moveTo(e.x,e.y),r.lineTo(e.x2,e.y2),r.lineTo(e.x3,e.y3),r.closePath();break}default:{const e=t,i=e.points;if(!(i!=null&&i.length))break;r.moveTo(i[0],i[1]);for(let s=2;s!l.enabled)){e.skip=!0;return}const s=[],n=1;for(const l of i){if(!l.enabled)continue;if(!Pu(l)){this._warnUnsupportedFilter(l);continue}const u=l.getCanvasFilterString();if(u===null){this._warnUnsupportedFilter(l);continue}u&&s.push(u)}if(s.length===0&&n===1){e.skip=!0;return}e.cssFilterString=s.join(" "),this._calculateFilterArea(t,e.bounds),e.useClip=!!t.filterEffect.filterArea;const a=this.renderer.canvasContext.activeContext,o=a.filter||"none";if(this._savedStates.push({filter:o,alphaMultiplier:this._alphaMultiplier}),e.useClip&&Number.isFinite(e.bounds.width)&&Number.isFinite(e.bounds.height)&&e.bounds.width>0&&e.bounds.height>0){const l=this.renderer.canvasContext.activeResolution||1;a.save(),a.setTransform(1,0,0,1,0,0),a.beginPath(),a.rect(e.bounds.x*l,e.bounds.y*l,e.bounds.width*l,e.bounds.height*l),a.clip()}else e.useClip=!1;n!==1&&(this._alphaMultiplier*=n),e.cssFilterString&&(a.filter=o!=="none"?`${o} ${e.cssFilterString}`:e.cssFilterString)}pop(){const t=this._popFilterFrame();if(t.skip)return;const e=this._savedStates.pop();if(!e)return;const i=this.renderer.canvasContext.activeContext;t.useClip?i.restore():i.filter=e.filter,this._alphaMultiplier=e.alphaMultiplier}generateFilteredTexture({texture:t,filters:e}){var i,s;if(!(e!=null&&e.length)||e.every(x=>!x.enabled))return t;const n=[],a=1;for(const x of e){if(!x.enabled)continue;if(!Pu(x)){this._warnUnsupportedFilter(x);continue}const v=x.getCanvasFilterString();if(v===null){this._warnUnsupportedFilter(x);continue}v&&n.push(v)}if(n.length===0&&a===1)return t;const o=K.getCanvasSource(t);if(!o)return t;const l=t.frame,u=(s=(i=t.source._resolution)!=null?i:t.source.resolution)!=null?s:1,c=l.width,h=l.height,p=me.getOptimalCanvasAndContext(c,h,u),{canvas:f,context:m}=p;m.setTransform(1,0,0,1,0,0),m.clearRect(0,0,f.width,f.height),n.length&&(m.filter=n.join(" ")),a!==1&&(m.globalAlpha=a);const g=l.x*u,_=l.y*u,b=c*u,y=h*u;return m.drawImage(o,g,_,b,y,0,0,b,y),m.filter="none",m.globalAlpha=1,yn(f,c,h,u)}_calculateFilterArea(t,e){if(t.renderables?co(t.renderables,e):t.filterEffect.filterArea?(e.clear(),e.addRect(t.filterEffect.filterArea),e.applyMatrix(t.container.worldTransform)):t.container.getFastGlobalBounds(!0,e),t.container){const i=t.container.renderGroup||t.container.parentRenderGroup,s=i==null?void 0:i.cacheToLocalTransform;s&&e.applyMatrix(s)}}_warnUnsupportedFilter(t){var e;const i=((e=t==null?void 0:t.constructor)==null?void 0:e.name)||"Filter";this._warnedFilterTypes.has(i)||(this._warnedFilterTypes.add(i),console.warn(`CanvasRenderer: filter "${i}" is not supported in Canvas2D and will be skipped.`))}get alphaMultiplier(){return this._alphaMultiplier}_pushFilterFrame(){let t=this._filterStack[this._filterStackIndex];return t||(t=this._filterStack[this._filterStackIndex]=new AR),this._filterStackIndex++,t}_popFilterFrame(){return this._filterStackIndex<=0?this._filterStack[0]:(this._filterStackIndex--,this._filterStack[this._filterStackIndex])}destroy(){this._filterStack=null,this._savedStates=null,this._warnedFilterTypes=null,this._alphaMultiplier=1}}Au.extension={type:[T.CanvasSystem],name:"filter"},$.add(Au);class Cu{constructor(){this.maxTextures=16,this.maxBatchableTextures=16,this.maxUniformBindings=0}init(){}}Cu.extension={type:[T.CanvasSystem],name:"limits"};class Rn{constructor(t){this._renderer=t}updateRenderable(){}destroyRenderable(){}validateRenderable(){return!1}addRenderable(t,e){this._renderer.renderPipes.batch.break(e),e.add(t)}execute(t){t.isRenderable&&t.render(this._renderer)}destroy(){this._renderer=null}}Rn.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"customRender"};function Mn(r,t){const e=r.instructionSet,i=e.instructions;for(let s=0;s>>24&255)/255,S=(v>>>24&255)/255,E=(a=(n=g.filter)==null?void 0:n.alphaMultiplier)!=null?a:1,O=w*S*E;if(O<=0)return;const M=x&16777215,C=v&16777215,P=be(Ce(C,M)),R=g._roundPixels|e._roundPixels;b.save(),_.setContextTransform(y,R===1),_.setBlendMode(e.groupBlendMode);const G=e.context.instructions;for(let I=0;I{if(!r.name)throw new Error("BlendMode extension must have a name property");$i[r.name]=r.ref},r=>{delete $i[r.name]});class Fn{constructor(t){this._blendModeStack=[],this._isAdvanced=!1,this._filterHash=Object.create(null),this._renderer=t,this._renderer.runners.prerender.add(this)}prerender(){this._activeBlendMode="normal",this._isAdvanced=!1}pushBlendMode(t,e,i){this._blendModeStack.push(e),this.setBlendMode(t,e,i)}popBlendMode(t){var e;this._blendModeStack.pop();const i=(e=this._blendModeStack[this._activeBlendMode.length-1])!=null?e:"normal";this.setBlendMode(null,i,t)}setBlendMode(t,e,i){var s;const n=t instanceof is;if(this._activeBlendMode===e){this._isAdvanced&&t&&!n&&((s=this._renderableList)==null||s.push(t));return}this._isAdvanced&&this._endAdvancedBlendMode(i),this._activeBlendMode=e,t&&(this._isAdvanced=!!$i[e],this._isAdvanced&&this._beginAdvancedBlendMode(t,i))}_beginAdvancedBlendMode(t,e){this._renderer.renderPipes.batch.break(e);const i=this._activeBlendMode;if(!$i[i])return;const s=this._ensureFilterEffect(i),n=t instanceof is,a={renderPipeId:"filter",action:"pushFilter",filterEffect:s,renderables:n?null:[t],container:n?t.root:null,canBundle:!1};this._renderableList=a.renderables,e.add(a)}_ensureFilterEffect(t){let e=this._filterHash[t];return e||(e=this._filterHash[t]=new Jr,e.filters=[new $i[t]]),e}_endAdvancedBlendMode(t){this._isAdvanced=!1,this._renderableList=null,this._renderer.renderPipes.batch.break(t),t.add({renderPipeId:"filter",action:"popFilter",canBundle:!1})}buildStart(){this._isAdvanced=!1}buildEnd(t){this._isAdvanced&&this._endAdvancedBlendMode(t)}destroy(){this._renderer=null,this._renderableList=null;for(const t in this._filterHash)this._filterHash[t].destroy();this._filterHash=null}}Fn.extension={type:[T.WebGLPipes,T.WebGPUPipes,T.CanvasPipes],name:"blendMode"};function Dn(r,t){t||(t=0);for(let e=t;e1?1:e,r.worldAlpha=e,r.worldColorAlpha=r.worldColor+((e*255|0)<<24)}function Gu(r,t,e){if(t===r.updateTick)return;r.updateTick=t,r.didChange=!1;const i=r.localTransform;r.updateLocalTransform();const s=r.parent;if(s&&!s.renderGroup?(e|=r._updateFlags,r.relativeGroupTransform.appendFrom(i,s.relativeGroupTransform),e&Lv&&Xv(r,s,e)):(e=r._updateFlags,r.relativeGroupTransform.copyFrom(i),e&Lv&&Xv(r,DR,e)),!r.renderGroup){const n=r.children,a=n.length;for(let u=0;u1?1:i,r.groupAlpha=i,r.groupColorAlpha=r.groupColor+((i*255|0)<<24)}e&ns&&(r.groupBlendMode=r.localBlendMode==="inherit"?t.groupBlendMode:r.localBlendMode),e&mr&&(r.globalDisplayStatus=r.localDisplayStatus&t.globalDisplayStatus),r._updateFlags=0}function Hv(r,t){const{list:e}=r.childrenRenderablesToUpdate;let i=!1;for(let s=0;s=0;n--)this._updateCachedRenderGroups(t.renderGroupChildren[n],e);if(t.invalidateMatrices(),t.isCachedAsTexture){if(t.textureNeedsUpdate){const n=t.root.getLocalBounds(),a=this._renderer,o=t.textureOptions.resolution||a.view.resolution,l=(i=t.textureOptions.antialias)!=null?i:a.view.antialias,u=(s=t.textureOptions.scaleMode)!=null?s:"linear",c=t.texture;n.ceil(),t.texture&&yt.returnTexture(t.texture,!0);const h=yt.getOptimalTexture(n.width,n.height,o,l);h._source.style=new Kt({scaleMode:u}),t.texture=h,t._textureBounds||(t._textureBounds=new Pt),t._textureBounds.copyFrom(n),c!==t.texture&&t.renderGroupParent&&(t.renderGroupParent.structureDidChange=!0)}}else t.texture&&(yt.returnTexture(t.texture,!0),t.texture=null)}_updateRenderGroups(t){const e=this._renderer,i=e.renderPipes;if(t.runOnRender(e),t.instructionSet.renderPipes=i,t.structureDidChange?Dn(t.childrenRenderablesToUpdate.list,0):Hv(t,i),Ou(t),t.structureDidChange?(t.structureDidChange=!1,this._buildInstructions(t,e)):this._updateRenderables(t),t.childrenRenderablesToUpdate.index=0,e.renderPipes.batch.upload(t.instructionSet),!(t.isCachedAsTexture&&!t.textureNeedsUpdate))for(let s=0;st in r?kR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Wv=(r,t)=>{for(var e in t||(t={}))$R.call(t,e)&&zv(r,e,t[e]);if(jv)for(var e of jv(t))LR.call(t,e)&&zv(r,e,t[e]);return r};const Bu=class c1{constructor(){this.clearBeforeRender=!0,this._backgroundColor=new J(0),this.color=this._backgroundColor,this.alpha=1}init(t){t=Wv(Wv({},c1.defaultOptions),t),this.clearBeforeRender=t.clearBeforeRender,this.color=t.background||t.backgroundColor||this._backgroundColor,this.alpha=t.backgroundAlpha,this._backgroundColor.setAlpha(t.backgroundAlpha)}get color(){return this._backgroundColor}set color(t){this._backgroundColor.setValue(t)}get alpha(){return this._backgroundColor.alpha}set alpha(t){this._backgroundColor.setAlpha(t)}get colorRgba(){return this._backgroundColor.toArray()}destroy(){}};Bu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"background",priority:0},Bu.defaultOptions={backgroundAlpha:1,backgroundColor:0,clearBeforeRender:!0};let Vv=Bu;var NR=Object.defineProperty,Yv=Object.getOwnPropertySymbols,XR=Object.prototype.hasOwnProperty,HR=Object.prototype.propertyIsEnumerable,Kv=(r,t,e)=>t in r?NR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Fu=(r,t)=>{for(var e in t||(t={}))XR.call(t,e)&&Kv(r,e,t[e]);if(Yv)for(var e of Yv(t))HR.call(t,e)&&Kv(r,e,t[e]);return r};const Du={png:"image/png",jpg:"image/jpeg",webp:"image/webp"},Uu=class h1{constructor(t){this._renderer=t}_normalizeOptions(t,e={}){return t instanceof dt||t instanceof F?Fu({target:t},e):Fu(Fu({},e),t)}async image(t){const e=L.get().createImage();return e.src=await this.base64(t),e}async base64(t){t=this._normalizeOptions(t,h1.defaultImageOptions);const{format:e,quality:i}=t,s=this.canvas(t);if(s.toBlob!==void 0)return new Promise((n,a)=>{s.toBlob(o=>{if(!o){a(new Error("ICanvas.toBlob failed!"));return}const l=new FileReader;l.onload=()=>n(l.result),l.onerror=a,l.readAsDataURL(o)},Du[e],i)});if(s.toDataURL!==void 0)return s.toDataURL(Du[e],i);if(s.convertToBlob!==void 0){const n=await s.convertToBlob({type:Du[e],quality:i});return new Promise((a,o)=>{const l=new FileReader;l.onload=()=>a(l.result),l.onerror=o,l.readAsDataURL(n)})}throw new Error("Extract.base64() requires ICanvas.toDataURL, ICanvas.toBlob, or ICanvas.convertToBlob to be implemented")}canvas(t){t=this._normalizeOptions(t);const e=t.target,i=this._renderer;if(e instanceof F)return i.texture.generateCanvas(e);const s=i.textureGenerator.generateTexture(t),n=i.texture.generateCanvas(s);return s.destroy(!0),n}pixels(t){t=this._normalizeOptions(t);const e=t.target,i=this._renderer,s=e instanceof F?e:i.textureGenerator.generateTexture(t),n=i.texture.getPixels(s);return e instanceof dt&&s.destroy(!0),n}texture(t){return t=this._normalizeOptions(t),t.target instanceof F?t.target:this._renderer.textureGenerator.generateTexture(t)}download(t){var e;t=this._normalizeOptions(t);const i=this.canvas(t),s=document.createElement("a");s.download=(e=t.filename)!=null?e:"image.png",s.href=i.toDataURL("image/png"),document.body.appendChild(s),s.click(),document.body.removeChild(s)}log(t){var e;const i=(e=t.width)!=null?e:200;t=this._normalizeOptions(t);const s=this.canvas(t),n=s.toDataURL();console.log(`[Pixi Texture] ${s.width}px ${s.height}px`);const a=["font-size: 1px;",`padding: ${i}px 300px;`,`background: url(${n}) no-repeat;`,"background-size: contain;"].join(" ");console.log("%c ",a)}destroy(){this._renderer=null}};Uu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"extract"},Uu.defaultImageOptions={format:"png",quality:1};let qv=Uu;var Zv=Object.getOwnPropertySymbols,jR=Object.prototype.hasOwnProperty,zR=Object.prototype.propertyIsEnumerable,WR=(r,t)=>{var e={};for(var i in r)jR.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Zv)for(var i of Zv(r))t.indexOf(i)<0&&zR.call(r,i)&&(e[i]=r[i]);return e};class Un extends F{static create(t){const e=t,{dynamic:i}=e,s=WR(e,["dynamic"]);return new Un({source:new ht(s),dynamic:i!=null?i:!1})}resize(t,e,i){return this.source.resize(t,e,i),this}}var VR=Object.defineProperty,YR=Object.defineProperties,KR=Object.getOwnPropertyDescriptors,Qv=Object.getOwnPropertySymbols,qR=Object.prototype.hasOwnProperty,ZR=Object.prototype.propertyIsEnumerable,Jv=(r,t,e)=>t in r?VR(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,QR=(r,t)=>{for(var e in t||(t={}))qR.call(t,e)&&Jv(r,e,t[e]);if(Qv)for(var e of Qv(t))ZR.call(t,e)&&Jv(r,e,t[e]);return r},JR=(r,t)=>YR(r,KR(t));const tM=new it,eM=new Pt,rM=[0,0,0,0];class ku{constructor(t){this._renderer=t}generateTexture(t){var e;t instanceof dt&&(t={target:t,frame:void 0,textureSourceOptions:{},resolution:void 0});const i=t.resolution||this._renderer.resolution,s=t.antialias||this._renderer.view.antialias,n=t.target;let a=t.clearColor;a?a=Array.isArray(a)&&a.length===4?a:J.shared.setValue(a).toArray():a=rM;const o=((e=t.frame)==null?void 0:e.copyTo(tM))||Ji(n,eM).rectangle;o.width=Math.max(o.width,1/i)|0,o.height=Math.max(o.height,1/i)|0;const l=Un.create(JR(QR({},t.textureSourceOptions),{width:o.width,height:o.height,resolution:i,antialias:s})),u=D.shared.translate(-o.x,-o.y);return this._renderer.render({container:n,transform:u,target:l,clearColor:a}),l.source.updateMipmaps(),l}destroy(){this._renderer=null}}ku.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"textureGenerator"};function ty(r){let t=!1;for(const i in r)if(r[i]==null){t=!0;break}if(!t)return r;const e=Object.create(null);for(const i in r){const s=r[i];s&&(e[i]=s)}return e}function ey(r){let t=0;for(let e=0;et in r?iM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,sy=(r,t)=>{for(var e in t||(t={}))sM.call(t,e)&&iy(r,e,t[e]);if(ry)for(var e of ry(t))nM.call(t,e)&&iy(r,e,t[e]);return r};const $u=class d1{constructor(t){this._managedResources=[],this._managedResourceHashes=[],this._managedCollections=[],this._ready=!1,this._renderer=t}init(t){t=sy(sy({},d1.defaultOptions),t),this.maxUnusedTime=t.gcMaxUnusedTime,this._frequency=t.gcFrequency,this.enabled=t.gcActive,this.now=performance.now()}get enabled(){return!!this._handler}set enabled(t){this.enabled!==t&&(t?(this._handler=this._renderer.scheduler.repeat(()=>{this._ready=!0},this._frequency,!1),this._collectionsHandler=this._renderer.scheduler.repeat(()=>{for(const e of this._managedCollections){const{context:i,collection:s,type:n}=e;n==="hash"?i[s]=ty(i[s]):i[s]=ey(i[s])}},this._frequency)):(this._renderer.scheduler.cancel(this._handler),this._renderer.scheduler.cancel(this._collectionsHandler),this._handler=0,this._collectionsHandler=0))}prerender({container:t}){this.now=performance.now(),t.renderGroup.gcTick=this._renderer.tick++,this._updateInstructionGCTick(t.renderGroup,t.renderGroup.gcTick)}postrender(){!this._ready||!this.enabled||(this.run(),this._ready=!1)}_updateInstructionGCTick(t,e){t.instructionSet.gcTick=e,t.gcTick=e;for(const i of t.renderGroupChildren)this._updateInstructionGCTick(i,e)}addCollection(t,e,i){this._managedCollections.push({context:t,collection:e,type:i})}addResource(t,e){var i,s;if(t._gcLastUsed!==-1){t._gcLastUsed=this.now,(i=t._onTouch)==null||i.call(t,this.now);return}const n=this._managedResources.length;t._gcData={index:n,type:e},t._gcLastUsed=this.now,(s=t._onTouch)==null||s.call(t,this.now),t.once("unload",this.removeResource,this),this._managedResources.push(t)}removeResource(t){const e=t._gcData;if(!e)return;const i=e.index,s=this._managedResources.length-1;if(i!==s){const n=this._managedResources[s];this._managedResources[i]=n,n._gcData.index=i}this._managedResources.length--,t._gcData=null,t._gcLastUsed=-1}addResourceHash(t,e,i,s=0){this._managedResourceHashes.push({context:t,hash:e,type:i,priority:s}),this._managedResourceHashes.sort((n,a)=>n.priority-a.priority)}run(){const t=performance.now(),e=this._managedResourceHashes;for(const s of e)this.runOnHash(s,t);let i=0;for(let s=0;s{t.off("unload",this.removeResource,this)}),this._managedResources.length=0,this._managedResourceHashes.length=0,this._managedCollections.length=0,this._renderer=null}};$u.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"gc",priority:0},$u.defaultOptions={gcActive:!0,gcMaxUnusedTime:6e4,gcFrequency:3e4};let ny=$u;class Lu{constructor(t){this._stackIndex=0,this._globalUniformDataStack=[],this._uniformsPool=[],this._activeUniforms=[],this._bindGroupPool=[],this._activeBindGroups=[],this._renderer=t}reset(){this._stackIndex=0;for(let t=0;t"},uWorldTransformMatrix:{value:new D,type:"mat3x3"},uWorldColorAlpha:{value:new Float32Array(4),type:"vec4"},uResolution:{value:[0,0],type:"vec2"}},{isStatic:!0})}destroy(){this._renderer=null,this._globalUniformDataStack.length=0,this._uniformsPool.length=0,this._activeUniforms.length=0,this._bindGroupPool.length=0,this._activeBindGroups.length=0,this._currentGlobalUniformData=null}}Lu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"globalUniforms"};let aM=1;class Nu{constructor(){this._tasks=[],this._offset=0}init(){Ct.system.add(this._update,this)}repeat(t,e,i=!0){const s=aM++;let n=0;return i&&(this._offset+=1e3,n=this._offset),this._tasks.push({func:t,duration:e,start:performance.now(),offset:n,last:performance.now(),repeat:!0,id:s}),s}cancel(t){for(let e=0;e=i.duration){const s=t-i.start;i.func(s),i.last=t}}}destroy(){Ct.system.remove(this._update,this),this._tasks.length=0}}Nu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"scheduler",priority:0};let ay=!1;function oy(r){if(!ay){if(L.get().getNavigator().userAgent.toLowerCase().indexOf("chrome")>-1){const t=[`%c %c %c %c %c PixiJS %c v${vi} (${r}) http://www.pixijs.com/ + +`,"background: #E72264; padding:5px 0;","background: #6CA2EA; padding:5px 0;","background: #B5D33D; padding:5px 0;","background: #FED23F; padding:5px 0;","color: #FFFFFF; background: #E72264; padding:5px 0;","color: #E72264; background: #FFFFFF; padding:5px 0;"];globalThis.console.log(...t)}else globalThis.console&&globalThis.console.log(`PixiJS ${vi} - ${r} - http://www.pixijs.com/`);ay=!0}}class kn{constructor(t){this._renderer=t}init(t){if(t.hello){let e=this._renderer.name;this._renderer.type===Gt.WEBGL&&(e+=` ${this._renderer.context.webGLVersion}`),oy(e)}}}kn.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"hello",priority:-2},kn.defaultOptions={hello:!1};var oM=Object.defineProperty,ly=Object.getOwnPropertySymbols,lM=Object.prototype.hasOwnProperty,uM=Object.prototype.propertyIsEnumerable,uy=(r,t,e)=>t in r?oM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,cy=(r,t)=>{for(var e in t||(t={}))lM.call(t,e)&&uy(r,e,t[e]);if(ly)for(var e of ly(t))uM.call(t,e)&&uy(r,e,t[e]);return r};const Xu=class p1{constructor(t){this._renderer=t}init(t){t=cy(cy({},p1.defaultOptions),t),this.maxUnusedTime=t.renderableGCMaxUnusedTime}get enabled(){return this._renderer.gc.enabled}set enabled(t){this._renderer.gc.enabled=t}addManagedHash(t,e){this._renderer.gc.addCollection(t,e,"hash")}addManagedArray(t,e){this._renderer.gc.addCollection(t,e,"array")}addRenderable(t){this._renderer.gc.addResource(t,"renderable")}run(){this._renderer.gc.run()}destroy(){this._renderer=null}};Xu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"renderableGC",priority:0},Xu.defaultOptions={renderableGCActive:!0,renderableGCMaxUnusedTime:6e4,renderableGCFrequency:3e4};let hy=Xu;const Hu=class da{get count(){return this._renderer.tick}get checkCount(){return this._checkCount}set checkCount(t){this._checkCount=t}get maxIdle(){return this._renderer.gc.maxUnusedTime/1e3*60}set maxIdle(t){this._renderer.gc.maxUnusedTime=t/60*1e3}get checkCountMax(){return Math.floor(this._renderer.gc._frequency/1e3)}set checkCountMax(t){}get active(){return this._renderer.gc.enabled}set active(t){this._renderer.gc.enabled=t}constructor(t){this._renderer=t,this._checkCount=0}init(t){t.textureGCActive!==da.defaultOptions.textureGCActive&&(this.active=t.textureGCActive),t.textureGCMaxIdle!==da.defaultOptions.textureGCMaxIdle&&(this.maxIdle=t.textureGCMaxIdle),t.textureGCCheckCountMax!==da.defaultOptions.textureGCCheckCountMax&&(this.checkCountMax=t.textureGCCheckCountMax)}run(){this._renderer.gc.run()}destroy(){this._renderer=null}};Hu.extension={type:[T.WebGLSystem,T.WebGPUSystem],name:"textureGC"},Hu.defaultOptions={textureGCActive:!0,textureGCAMaxIdle:null,textureGCMaxIdle:3600,textureGCCheckCountMax:600};let dy=Hu;var cM=Object.defineProperty,py=Object.getOwnPropertySymbols,hM=Object.prototype.hasOwnProperty,dM=Object.prototype.propertyIsEnumerable,fy=(r,t,e)=>t in r?cM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,my=(r,t)=>{for(var e in t||(t={}))hM.call(t,e)&&fy(r,e,t[e]);if(py)for(var e of py(t))dM.call(t,e)&&fy(r,e,t[e]);return r};const gy=class f1{constructor(t={}){if(this.uid=nt("renderTarget"),this.colorTextures=[],this.dirtyId=0,this.isRoot=!1,this._size=new Float32Array(2),this._managedColorTextures=!1,t=my(my({},f1.defaultOptions),t),this.stencil=t.stencil,this.depth=t.depth,this.isRoot=t.isRoot,typeof t.colorTextures=="number"){this._managedColorTextures=!0;for(let e=0;ei.source)];const e=this.colorTexture.source;this.resize(e.width,e.height,e._resolution)}this.colorTexture.source.on("resize",this.onSourceResize,this),(t.depthStencilTexture||this.stencil)&&(t.depthStencilTexture instanceof F||t.depthStencilTexture instanceof ht?this.depthStencilTexture=t.depthStencilTexture.source:this.ensureDepthStencilTexture())}get size(){const t=this._size;return t[0]=this.pixelWidth,t[1]=this.pixelHeight,t}get width(){return this.colorTexture.source.width}get height(){return this.colorTexture.source.height}get pixelWidth(){return this.colorTexture.source.pixelWidth}get pixelHeight(){return this.colorTexture.source.pixelHeight}get resolution(){return this.colorTexture.source._resolution}get colorTexture(){return this.colorTextures[0]}onSourceResize(t){this.resize(t.width,t.height,t._resolution,!0)}ensureDepthStencilTexture(){this.depthStencilTexture||(this.depthStencilTexture=new ht({width:this.width,height:this.height,resolution:this.resolution,format:"depth24plus-stencil8",autoGenerateMipmaps:!1,antialias:!1,mipLevelCount:1}))}resize(t,e,i=this.resolution,s=!1){this.dirtyId++,this.colorTextures.forEach((n,a)=>{s&&a===0||n.source.resize(t,e,i)}),this.depthStencilTexture&&this.depthStencilTexture.source.resize(t,e,i)}destroy(){this.colorTexture.source.off("resize",this.onSourceResize,this),this._managedColorTextures&&this.colorTextures.forEach(t=>{t.destroy()}),this.depthStencilTexture&&(this.depthStencilTexture.destroy(),delete this.depthStencilTexture)}};gy.defaultOptions={width:0,height:0,resolution:1,colorTextures:1,stencil:!1,depth:!1,antialias:!1,isRoot:!1};let $n=gy;var pM=Object.defineProperty,_y=Object.getOwnPropertySymbols,fM=Object.prototype.hasOwnProperty,mM=Object.prototype.propertyIsEnumerable,by=(r,t,e)=>t in r?pM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,gM=(r,t)=>{for(var e in t||(t={}))fM.call(t,e)&&by(r,e,t[e]);if(_y)for(var e of _y(t))mM.call(t,e)&&by(r,e,t[e]);return r};const ur=new Map;je.register(ur);function ju(r,t){if(!ur.has(r)){const e=new F({source:new de(gM({resource:r},t))}),i=()=>{ur.get(r)===e&&ur.delete(r)};e.once("destroy",i),e.source.once("destroy",i),ur.set(r,e)}return ur.get(r)}function _M(r){return ur.has(r)}var bM=Object.defineProperty,vy=Object.getOwnPropertySymbols,vM=Object.prototype.hasOwnProperty,yM=Object.prototype.propertyIsEnumerable,yy=(r,t,e)=>t in r?bM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,xy=(r,t)=>{for(var e in t||(t={}))vM.call(t,e)&&yy(r,e,t[e]);if(vy)for(var e of vy(t))yM.call(t,e)&&yy(r,e,t[e]);return r};const zu=class m1{get autoDensity(){return this.texture.source.autoDensity}set autoDensity(t){this.texture.source.autoDensity=t}get resolution(){return this.texture.source._resolution}set resolution(t){this.texture.source.resize(this.texture.source.width,this.texture.source.height,t)}init(t){t=xy(xy({},m1.defaultOptions),t),t.view&&(t.canvas=t.view),this.screen=new it(0,0,t.width,t.height),this.canvas=t.canvas||L.get().createCanvas(),this.antialias=!!t.antialias,this.texture=ju(this.canvas,t),this.renderTarget=new $n({colorTextures:[this.texture],depth:!!t.depth,isRoot:!0}),this.texture.source.transparent=t.backgroundAlpha<1,this.resolution=t.resolution}resize(t,e,i){this.texture.source.resize(t,e,i),this.screen.width=this.texture.frame.width,this.screen.height=this.texture.frame.height}destroy(t=!1){(typeof t=="boolean"?t:t!=null&&t.removeView)&&this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas),this.texture.destroy()}};zu.extension={type:[T.WebGLSystem,T.WebGPUSystem,T.CanvasSystem],name:"view",priority:0},zu.defaultOptions={width:800,height:600,autoDensity:!1,antialias:!1};let Ty=zu;const Ln=[Vv,Lu,kn,Ty,Iu,ny,dy,ku,qv,So,hy,Nu],Wu=[Fn,An,Bn,On,Cn,wu,Tu,Rn];function Sy(r,t,e,i,s,n){const a=n?1:-1;return r.identity(),r.a=1/i*2,r.d=a*(1/s*2),r.tx=-1-t*r.a,r.ty=-a-e*r.d,r}function wy(r){const t=r.colorTexture.source.resource;return globalThis.HTMLCanvasElement&&t instanceof HTMLCanvasElement&&document.body.contains(t)}class Nn{constructor(t){this.rootViewPort=new it,this.viewport=new it,this.mipLevel=0,this.layer=0,this.onRenderTargetChange=new vo("onRenderTargetChange"),this.projectionMatrix=new D,this.defaultClearColor=[0,0,0,0],this._renderSurfaceToRenderTargetHash=new Map,this._gpuRenderTargetHash=Object.create(null),this._renderTargetStack=[],this._renderer=t,t.gc.addCollection(this,"_gpuRenderTargetHash","hash")}finishRenderPass(){this.adaptor.finishRenderPass(this.renderTarget)}renderStart({target:t,clear:e,clearColor:i,frame:s,mipLevel:n,layer:a}){var o,l;this._renderTargetStack.length=0,this.push(t,e,i,s,n!=null?n:0,a!=null?a:0),this.rootViewPort.copyFrom(this.viewport),this.rootRenderTarget=this.renderTarget,this.renderingToScreen=wy(this.rootRenderTarget),(l=(o=this.adaptor).prerender)==null||l.call(o,this.rootRenderTarget)}postrender(){var t,e;(e=(t=this.adaptor).postrender)==null||e.call(t,this.rootRenderTarget)}bind(t,e=!0,i,s,n=0,a=0){const o=this.getRenderTarget(t),l=this.renderTarget!==o;this.renderTarget=o,this.renderSurface=t;const u=this.getGpuRenderTarget(o);(o.pixelWidth!==u.width||o.pixelHeight!==u.height)&&(this.adaptor.resizeGpuRenderTarget(o),u.width=o.pixelWidth,u.height=o.pixelHeight);const c=o.colorTexture,h=this.viewport,p=c.arrayLayerCount||1;if((a|0)!==a&&(a|=0),a<0||a>=p)throw new Error(`[RenderTargetSystem] layer ${a} is out of bounds (arrayLayerCount=${p}).`);this.mipLevel=n|0,this.layer=a|0;const f=Math.max(c.pixelWidth>>n,1),m=Math.max(c.pixelHeight>>n,1);if(!s&&t instanceof F&&(s=t.frame),s){const g=c._resolution,_=1<{t!==e&&t.destroy()}),this._renderSurfaceToRenderTargetHash.clear(),this._gpuRenderTargetHash=Object.create(null)}_initRenderTarget(t){let e=null;return de.test(t)&&(t=ju(t).source),t instanceof $n?e=t:t instanceof ht&&(e=new $n({colorTextures:[t]}),t.source instanceof de&&(e.isRoot=!0),t.once("destroy",()=>{e.destroy(),this._renderSurfaceToRenderTargetHash.delete(t);const i=this._gpuRenderTargetHash[e.uid];i&&(this._gpuRenderTargetHash[e.uid]=null,this.adaptor.destroyGpuRenderTarget(i))})),this._renderSurfaceToRenderTargetHash.set(t,e),e}getGpuRenderTarget(t){return this._gpuRenderTargetHash[t.uid]||(this._gpuRenderTargetHash[t.uid]=this.adaptor.initGpuRenderTarget(t))}resetState(){this.renderTarget=null,this.renderSurface=null}}class Ey{init(t,e){this._renderer=t,this._renderTargetSystem=e}initGpuRenderTarget(t){const e=t.colorTexture,{canvas:i,context:s}=this._ensureCanvas(e);return{canvas:i,context:s,width:i.width,height:i.height}}resizeGpuRenderTarget(t){const e=t.colorTexture,{canvas:i}=this._ensureCanvas(e);i.width=t.pixelWidth,i.height=t.pixelHeight}startRenderPass(t,e,i,s){const n=this._renderTargetSystem.getGpuRenderTarget(t);this._renderer.canvasContext.activeContext=n.context,this._renderer.canvasContext.activeResolution=t.resolution,e&&this.clear(t,e,i,s)}clear(t,e,i,s){const n=this._renderTargetSystem.getGpuRenderTarget(t).context,a=s||{x:0,y:0,width:t.pixelWidth,height:t.pixelHeight};if(n.setTransform(1,0,0,1,0,0),n.clearRect(a.x,a.y,a.width,a.height),i){const o=J.shared.setValue(i);o.alpha>0&&(n.globalAlpha=o.alpha,n.fillStyle=o.toHex(),n.fillRect(a.x,a.y,a.width,a.height),n.globalAlpha=1)}}finishRenderPass(){}copyToTexture(t,e,i,s,n){var a,o;const l=this._renderTargetSystem.getGpuRenderTarget(t).canvas,u=e.source,{context:c}=this._ensureCanvas(u),h=(a=n==null?void 0:n.x)!=null?a:0,p=(o=n==null?void 0:n.y)!=null?o:0;return c.drawImage(l,i.x,i.y,s.width,s.height,h,p,s.width,s.height),u.update(),e}destroyGpuRenderTarget(t){}_ensureCanvas(t){let e=t.resource;(!e||!de.test(e))&&(e=L.get().createCanvas(t.pixelWidth,t.pixelHeight),t.resource=e),(e.width!==t.pixelWidth||e.height!==t.pixelHeight)&&(e.width=t.pixelWidth,e.height=t.pixelHeight);const i=e.getContext("2d");return{canvas:e,context:i}}}class Vu extends Nn{constructor(t){super(t),this.adaptor=new Ey,this.adaptor.init(t,this)}}Vu.extension={type:[T.CanvasSystem],name:"renderTarget"};class Yu{constructor(t){}init(){}initSource(t){}generateCanvas(t){var e,i;const s=L.get().createCanvas(),n=s.getContext("2d"),a=K.getCanvasSource(t);if(!a)return s;const o=t.frame,l=(i=(e=t.source._resolution)!=null?e:t.source.resolution)!=null?i:1,u=o.x*l,c=o.y*l,h=o.width*l,p=o.height*l;return s.width=Math.ceil(h),s.height=Math.ceil(p),n.drawImage(a,u,c,h,p,0,0,h,p),s}getPixels(t){const e=this.generateCanvas(t);return{pixels:e.getContext("2d",{willReadFrequently:!0}).getImageData(0,0,e.width,e.height).data,width:e.width,height:e.height}}destroy(){}}Yu.extension={type:[T.CanvasSystem],name:"texture"};const xM=[...Ln,Eu,Cu,Yu,Vu],TM=[Fn,An,Bn,On,Cn,Su,xu,Rn],SM=[Fv,Mu],Py=[],Ay=[],Cy=[];$.handleByNamedList(T.CanvasSystem,Py),$.handleByNamedList(T.CanvasPipes,Ay),$.handleByNamedList(T.CanvasPipesAdaptor,Cy),$.add(...xM,...TM,...SM);class Ry extends Tr{constructor(){const t={name:"canvas",type:Gt.CANVAS,systems:Py,renderPipes:Ay,renderPipeAdaptors:Cy};super(t)}}var wM={__proto__:null,CanvasRenderer:Ry},Li=(r=>(r[r.ELEMENT_ARRAY_BUFFER=34963]="ELEMENT_ARRAY_BUFFER",r[r.ARRAY_BUFFER=34962]="ARRAY_BUFFER",r[r.UNIFORM_BUFFER=35345]="UNIFORM_BUFFER",r))(Li||{});class My{constructor(t,e){this._lastBindBaseLocation=-1,this._lastBindCallId=-1,this.buffer=t||null,this.updateID=-1,this.byteLength=-1,this.type=e}destroy(){this.buffer=null,this.updateID=-1,this.byteLength=-1,this.type=-1,this._lastBindBaseLocation=-1,this._lastBindCallId=-1}}class Ku{constructor(t){this._boundBufferBases=Object.create(null),this._minBaseLocation=0,this._nextBindBaseIndex=this._minBaseLocation,this._bindCallId=0,this._renderer=t,this._managedBuffers=new Ft({renderer:t,type:"resource",onUnload:this.onBufferUnload.bind(this),name:"glBuffer"})}destroy(){this._managedBuffers.destroy(),this._renderer=null,this._gl=null,this._boundBufferBases={}}contextChange(){this._gl=this._renderer.gl,this.destroyAll(!0),this._maxBindings=this._renderer.limits.maxUniformBindings}getGlBuffer(t){return t._gcLastUsed=this._renderer.gc.now,t._gpuData[this._renderer.uid]||this.createGLBuffer(t)}bind(t){const{_gl:e}=this,i=this.getGlBuffer(t);e.bindBuffer(i.type,i.buffer)}bindBufferBase(t,e){const{_gl:i}=this;this._boundBufferBases[e]!==t&&(this._boundBufferBases[e]=t,t._lastBindBaseLocation=e,i.bindBufferBase(i.UNIFORM_BUFFER,e,t.buffer))}nextBindBase(t){this._bindCallId++,this._minBaseLocation=0,t&&(this._boundBufferBases[0]=null,this._minBaseLocation=1,this._nextBindBaseIndex<1&&(this._nextBindBaseIndex=1))}freeLocationForBufferBase(t){let e=this.getLastBindBaseLocation(t);if(e>=this._minBaseLocation)return t._lastBindCallId=this._bindCallId,e;let i=0,s=this._nextBindBaseIndex;for(;i<2;){s>=this._maxBindings&&(s=this._minBaseLocation,i++);const n=this._boundBufferBases[s];if(n&&n._lastBindCallId===this._bindCallId){s++;continue}break}return e=s,this._nextBindBaseIndex=s+1,i>=2?-1:(t._lastBindCallId=this._bindCallId,this._boundBufferBases[e]=null,e)}getLastBindBaseLocation(t){const e=t._lastBindBaseLocation;return this._boundBufferBases[e]===t?e:-1}bindBufferRange(t,e,i,s){const{_gl:n}=this;i||(i=0),e||(e=0),this._boundBufferBases[e]=null,n.bindBufferRange(n.UNIFORM_BUFFER,e||0,t.buffer,i*256,s||256)}updateBuffer(t){const{_gl:e}=this,i=this.getGlBuffer(t);if(t._updateID===i.updateID)return i;i.updateID=t._updateID,e.bindBuffer(i.type,i.buffer);const s=t.data,n=t.descriptor.usage&et.STATIC?e.STATIC_DRAW:e.DYNAMIC_DRAW;return s?i.byteLength>=s.byteLength?e.bufferSubData(i.type,0,s,0,t._updateSize/s.BYTES_PER_ELEMENT):(i.byteLength=s.byteLength,e.bufferData(i.type,s,n)):(i.byteLength=t.descriptor.size,e.bufferData(i.type,i.byteLength,n)),i}destroyAll(t=!1){this._managedBuffers.removeAll(t)}onBufferUnload(t,e=!1){const i=t._gpuData[this._renderer.uid];i&&(e||this._gl.deleteBuffer(i.buffer))}createGLBuffer(t){const{_gl:e}=this;let i=Li.ARRAY_BUFFER;t.descriptor.usage&et.INDEX?i=Li.ELEMENT_ARRAY_BUFFER:t.descriptor.usage&et.UNIFORM&&(i=Li.UNIFORM_BUFFER);const s=new My(e.createBuffer(),i);return t._gpuData[this._renderer.uid]=s,this._managedBuffers.add(t),s}resetState(){this._boundBufferBases=Object.create(null)}}Ku.extension={type:[T.WebGLSystem],name:"buffer"};var EM=Object.defineProperty,PM=Object.defineProperties,AM=Object.getOwnPropertyDescriptors,Oy=Object.getOwnPropertySymbols,CM=Object.prototype.hasOwnProperty,RM=Object.prototype.propertyIsEnumerable,Gy=(r,t,e)=>t in r?EM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Xn=(r,t)=>{for(var e in t||(t={}))CM.call(t,e)&&Gy(r,e,t[e]);if(Oy)for(var e of Oy(t))RM.call(t,e)&&Gy(r,e,t[e]);return r},Iy=(r,t)=>PM(r,AM(t));const qu=class g1{constructor(t){this.supports={uint32Indices:!0,uniformBufferObject:!0,vertexArrayObject:!0,srgbTextures:!0,nonPowOf2wrapping:!0,msaa:!0,nonPowOf2mipmaps:!0},this._renderer=t,this.extensions=Object.create(null),this.handleContextLost=this.handleContextLost.bind(this),this.handleContextRestored=this.handleContextRestored.bind(this)}get isLost(){return!this.gl||this.gl.isContextLost()}contextChange(t){this.gl=t,this._renderer.gl=t}init(t){var e,i;t=Xn(Xn({},g1.defaultOptions),t);let s=this.multiView=t.multiView;if(t.context&&s&&(ae("Renderer created with both a context and multiview enabled. Disabling multiView as both cannot work together."),s=!1),s?this.canvas=L.get().createCanvas(this._renderer.canvas.width,this._renderer.canvas.height):this.canvas=this._renderer.view.canvas,t.context)this.initFromContext(t.context);else{const n=this._renderer.background.alpha<1,a=(e=t.premultipliedAlpha)!=null?e:!0,o=t.antialias&&!this._renderer.backBuffer.useBackBuffer;this.createContext(t.preferWebGLVersion,{alpha:n,premultipliedAlpha:a,antialias:o,stencil:!0,preserveDrawingBuffer:t.preserveDrawingBuffer,powerPreference:(i=t.powerPreference)!=null?i:"default"})}}ensureCanvasSize(t){if(!this.multiView){t!==this.canvas&&ae("multiView is disabled, but targetCanvas is not the main canvas");return}const{canvas:e}=this;(e.width{var e;this.gl.isContextLost()&&((e=this.extensions.loseContext)==null||e.restoreContext())},0))}handleContextRestored(){this.getExtensions(),this._renderer.runners.contextChange.emit(this.gl)}destroy(){var t;const e=this._renderer.view.canvas;this._renderer=null,e.removeEventListener("webglcontextlost",this.handleContextLost),e.removeEventListener("webglcontextrestored",this.handleContextRestored),this.gl.useProgram(null),(t=this.extensions.loseContext)==null||t.loseContext()}forceContextLoss(){var t;(t=this.extensions.loseContext)==null||t.loseContext(),this._contextLossForced=!0}validateContext(t){const e=t.getContextAttributes();e&&e.stencil;const i=this.supports,s=this.webGLVersion===2,n=this.extensions;i.uint32Indices=s||!!n.uint32ElementIndex,i.uniformBufferObject=s,i.vertexArrayObject=s||!!n.vertexArrayObject,i.srgbTextures=s||!!n.srgb,i.nonPowOf2wrapping=s,i.nonPowOf2mipmaps=s,i.msaa=s,i.uint32Indices}};qu.extension={type:[T.WebGLSystem],name:"context"},qu.defaultOptions={context:null,premultipliedAlpha:!0,preserveDrawingBuffer:!1,powerPreference:void 0,preferWebGLVersion:2,multiView:!1};let By=qu;function Zu(r,t){var e,i,s;for(const n in r.attributes){const a=r.attributes[n],o=t[n];o?((e=a.format)!=null||(a.format=o.format),(i=a.offset)!=null||(a.offset=o.offset),(s=a.instance)!=null||(a.instance=o.instance)):ae(`Attribute ${n} is not present in the shader, but is present in the geometry. Unable to infer attribute details.`)}MM(r)}function MM(r){var t,e;const{buffers:i,attributes:s}=r,n={},a={};for(const o in i){const l=i[o];n[l.uid]=0,a[l.uid]=0}for(const o in s){const l=s[o];n[l.buffer.uid]+=Me(l.format).stride}for(const o in s){const l=s[o];(t=l.stride)!=null||(l.stride=n[l.buffer.uid]),(e=l.start)!=null||(l.start=a[l.buffer.uid]),a[l.buffer.uid]+=Me(l.format).stride}}var Hn=(r=>(r[r.RGBA=6408]="RGBA",r[r.RGB=6407]="RGB",r[r.RG=33319]="RG",r[r.RED=6403]="RED",r[r.RGBA_INTEGER=36249]="RGBA_INTEGER",r[r.RGB_INTEGER=36248]="RGB_INTEGER",r[r.RG_INTEGER=33320]="RG_INTEGER",r[r.RED_INTEGER=36244]="RED_INTEGER",r[r.ALPHA=6406]="ALPHA",r[r.LUMINANCE=6409]="LUMINANCE",r[r.LUMINANCE_ALPHA=6410]="LUMINANCE_ALPHA",r[r.DEPTH_COMPONENT=6402]="DEPTH_COMPONENT",r[r.DEPTH_STENCIL=34041]="DEPTH_STENCIL",r))(Hn||{}),jn=(r=>(r[r.TEXTURE_2D=3553]="TEXTURE_2D",r[r.TEXTURE_CUBE_MAP=34067]="TEXTURE_CUBE_MAP",r[r.TEXTURE_2D_ARRAY=35866]="TEXTURE_2D_ARRAY",r[r.TEXTURE_CUBE_MAP_POSITIVE_X=34069]="TEXTURE_CUBE_MAP_POSITIVE_X",r[r.TEXTURE_CUBE_MAP_NEGATIVE_X=34070]="TEXTURE_CUBE_MAP_NEGATIVE_X",r[r.TEXTURE_CUBE_MAP_POSITIVE_Y=34071]="TEXTURE_CUBE_MAP_POSITIVE_Y",r[r.TEXTURE_CUBE_MAP_NEGATIVE_Y=34072]="TEXTURE_CUBE_MAP_NEGATIVE_Y",r[r.TEXTURE_CUBE_MAP_POSITIVE_Z=34073]="TEXTURE_CUBE_MAP_POSITIVE_Z",r[r.TEXTURE_CUBE_MAP_NEGATIVE_Z=34074]="TEXTURE_CUBE_MAP_NEGATIVE_Z",r))(jn||{}),Fy=(r=>(r[r.CLAMP=33071]="CLAMP",r[r.REPEAT=10497]="REPEAT",r[r.MIRRORED_REPEAT=33648]="MIRRORED_REPEAT",r))(Fy||{}),st=(r=>(r[r.UNSIGNED_BYTE=5121]="UNSIGNED_BYTE",r[r.UNSIGNED_SHORT=5123]="UNSIGNED_SHORT",r[r.UNSIGNED_SHORT_5_6_5=33635]="UNSIGNED_SHORT_5_6_5",r[r.UNSIGNED_SHORT_4_4_4_4=32819]="UNSIGNED_SHORT_4_4_4_4",r[r.UNSIGNED_SHORT_5_5_5_1=32820]="UNSIGNED_SHORT_5_5_5_1",r[r.UNSIGNED_INT=5125]="UNSIGNED_INT",r[r.UNSIGNED_INT_10F_11F_11F_REV=35899]="UNSIGNED_INT_10F_11F_11F_REV",r[r.UNSIGNED_INT_2_10_10_10_REV=33640]="UNSIGNED_INT_2_10_10_10_REV",r[r.UNSIGNED_INT_24_8=34042]="UNSIGNED_INT_24_8",r[r.UNSIGNED_INT_5_9_9_9_REV=35902]="UNSIGNED_INT_5_9_9_9_REV",r[r.BYTE=5120]="BYTE",r[r.SHORT=5122]="SHORT",r[r.INT=5124]="INT",r[r.FLOAT=5126]="FLOAT",r[r.FLOAT_32_UNSIGNED_INT_24_8_REV=36269]="FLOAT_32_UNSIGNED_INT_24_8_REV",r[r.HALF_FLOAT=36193]="HALF_FLOAT",r))(st||{});const Dy={uint8x2:st.UNSIGNED_BYTE,uint8x4:st.UNSIGNED_BYTE,sint8x2:st.BYTE,sint8x4:st.BYTE,unorm8x2:st.UNSIGNED_BYTE,unorm8x4:st.UNSIGNED_BYTE,snorm8x2:st.BYTE,snorm8x4:st.BYTE,uint16x2:st.UNSIGNED_SHORT,uint16x4:st.UNSIGNED_SHORT,sint16x2:st.SHORT,sint16x4:st.SHORT,unorm16x2:st.UNSIGNED_SHORT,unorm16x4:st.UNSIGNED_SHORT,snorm16x2:st.SHORT,snorm16x4:st.SHORT,float16x2:st.HALF_FLOAT,float16x4:st.HALF_FLOAT,float32:st.FLOAT,float32x2:st.FLOAT,float32x3:st.FLOAT,float32x4:st.FLOAT,uint32:st.UNSIGNED_INT,uint32x2:st.UNSIGNED_INT,uint32x3:st.UNSIGNED_INT,uint32x4:st.UNSIGNED_INT,sint32:st.INT,sint32x2:st.INT,sint32x3:st.INT,sint32x4:st.INT};function Uy(r){var t;return(t=Dy[r])!=null?t:Dy.float32}const OM={"point-list":0,"line-list":1,"line-strip":3,"triangle-list":4,"triangle-strip":5};class ky{constructor(){this.vaoCache=Object.create(null)}destroy(){this.vaoCache=Object.create(null)}}class Qu{constructor(t){this._renderer=t,this._activeGeometry=null,this._activeVao=null,this.hasVao=!0,this.hasInstance=!0,this._managedGeometries=new Ft({renderer:t,type:"resource",onUnload:this.onGeometryUnload.bind(this),name:"glGeometry"})}contextChange(){const t=this.gl=this._renderer.gl;if(!this._renderer.context.supports.vertexArrayObject)throw new Error("[PixiJS] Vertex Array Objects are not supported on this device");this.destroyAll(!0);const e=this._renderer.context.extensions.vertexArrayObject;e&&(t.createVertexArray=()=>e.createVertexArrayOES(),t.bindVertexArray=s=>e.bindVertexArrayOES(s),t.deleteVertexArray=s=>e.deleteVertexArrayOES(s));const i=this._renderer.context.extensions.vertexAttribDivisorANGLE;i&&(t.drawArraysInstanced=(s,n,a,o)=>{i.drawArraysInstancedANGLE(s,n,a,o)},t.drawElementsInstanced=(s,n,a,o,l)=>{i.drawElementsInstancedANGLE(s,n,a,o,l)},t.vertexAttribDivisor=(s,n)=>i.vertexAttribDivisorANGLE(s,n)),this._activeGeometry=null,this._activeVao=null}bind(t,e){const i=this.gl;this._activeGeometry=t;const s=this.getVao(t,e);this._activeVao!==s&&(this._activeVao=s,i.bindVertexArray(s)),this.updateBuffers()}resetState(){this.unbind()}updateBuffers(){const t=this._activeGeometry,e=this._renderer.buffer;for(let i=0;it in r?GM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Ny=(r,t)=>{for(var e in t||(t={}))IM.call(t,e)&&Ly(r,e,t[e]);if($y)for(var e of $y(t))BM.call(t,e)&&Ly(r,e,t[e]);return r};const FM=new Qe({attributes:{aPosition:[-1,-1,3,-1,-1,3]}}),Ju=class _1{constructor(t){this.useBackBuffer=!1,this._useBackBufferThisRender=!1,this._renderer=t}init(t={}){const{useBackBuffer:e,antialias:i}=Ny(Ny({},_1.defaultOptions),t);this.useBackBuffer=e,this._antialias=i,this._renderer.context.supports.msaa||(ae("antialiasing, is not supported on when using the back buffer"),this._antialias=!1),this._state=jt.for2d();const s=new Ht({vertex:` + attribute vec2 aPosition; + out vec2 vUv; + + void main() { + gl_Position = vec4(aPosition, 0.0, 1.0); + + vUv = (aPosition + 1.0) / 2.0; + + // flip dem UVs + vUv.y = 1.0 - vUv.y; + }`,fragment:` + in vec2 vUv; + out vec4 finalColor; + + uniform sampler2D uTexture; + + void main() { + finalColor = texture(uTexture, vUv); + }`,name:"big-triangle"});this._bigTriangleShader=new Zt({glProgram:s,resources:{uTexture:F.WHITE.source}})}renderStart(t){const e=this._renderer.renderTarget.getRenderTarget(t.target);if(this._useBackBufferThisRender=this.useBackBuffer&&!!e.isRoot,this._useBackBufferThisRender){const i=this._renderer.renderTarget.getRenderTarget(t.target);this._targetTexture=i.colorTexture,t.target=this._getBackBufferTexture(i.colorTexture)}}renderEnd(){this._presentBackBuffer()}_presentBackBuffer(){const t=this._renderer;t.renderTarget.finishRenderPass(),this._useBackBufferThisRender&&(t.renderTarget.bind(this._targetTexture,!1),this._bigTriangleShader.resources.uTexture=this._backBufferTexture.source,t.encoder.draw({geometry:FM,shader:this._bigTriangleShader,state:this._state}))}_getBackBufferTexture(t){return this._backBufferTexture=this._backBufferTexture||new F({source:new ht({width:t.width,height:t.height,resolution:t._resolution,antialias:this._antialias})}),this._backBufferTexture.source.resize(t.width,t.height,t._resolution),this._backBufferTexture}destroy(){this._backBufferTexture&&(this._backBufferTexture.destroy(),this._backBufferTexture=null)}};Ju.extension={type:[T.WebGLSystem],name:"backBuffer",priority:1},Ju.defaultOptions={useBackBuffer:!1};let Xy=Ju;class tc{constructor(t){this._colorMaskCache=15,this._renderer=t}setMask(t){this._colorMaskCache!==t&&(this._colorMaskCache=t,this._renderer.gl.colorMask(!!(t&8),!!(t&4),!!(t&2),!!(t&1)))}}tc.extension={type:[T.WebGLSystem],name:"colorMask"};class ec{constructor(t){this.commandFinished=Promise.resolve(),this._renderer=t}setGeometry(t,e){this._renderer.geometry.bind(t,e.glProgram)}finishRenderPass(){}draw(t){const e=this._renderer,{geometry:i,shader:s,state:n,skipSync:a,topology:o,size:l,start:u,instanceCount:c}=t;e.shader.bind(s,a),e.geometry.bind(i,e.shader._activeProgram),n&&e.state.set(n),e.geometry.draw(o,l,u,c!=null?c:i.instanceCount)}destroy(){this._renderer=null}}ec.extension={type:[T.WebGLSystem],name:"encoder"};class rc{constructor(t){this._renderer=t}contextChange(){const t=this._renderer.gl;this.maxTextures=t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS),this.maxBatchableTextures=Oo(this.maxTextures,t);const e=this._renderer.context.webGLVersion===2;this.maxUniformBindings=e?t.getParameter(t.MAX_UNIFORM_BUFFER_BINDINGS):0}destroy(){}}rc.extension={type:[T.WebGLSystem],name:"limits"};class Hy{constructor(){this.width=-1,this.height=-1,this.msaa=!1,this._attachedMipLevel=0,this._attachedLayer=0,this.msaaRenderBuffer=[]}}const Be=[];Be[xt.NONE]=void 0,Be[xt.DISABLED]={stencilWriteMask:0,stencilReadMask:0},Be[xt.RENDERING_MASK_ADD]={stencilFront:{compare:"equal",passOp:"increment-clamp"},stencilBack:{compare:"equal",passOp:"increment-clamp"}},Be[xt.RENDERING_MASK_REMOVE]={stencilFront:{compare:"equal",passOp:"decrement-clamp"},stencilBack:{compare:"equal",passOp:"decrement-clamp"}},Be[xt.MASK_ACTIVE]={stencilWriteMask:0,stencilFront:{compare:"equal",passOp:"keep"},stencilBack:{compare:"equal",passOp:"keep"}},Be[xt.INVERSE_MASK_ACTIVE]={stencilWriteMask:0,stencilFront:{compare:"not-equal",passOp:"keep"},stencilBack:{compare:"not-equal",passOp:"keep"}};class ic{constructor(t){this._stencilCache={enabled:!1,stencilReference:0,stencilMode:xt.NONE},this._renderTargetStencilState=Object.create(null),t.renderTarget.onRenderTargetChange.add(this)}contextChange(t){this._gl=t,this._comparisonFuncMapping={always:t.ALWAYS,never:t.NEVER,equal:t.EQUAL,"not-equal":t.NOTEQUAL,less:t.LESS,"less-equal":t.LEQUAL,greater:t.GREATER,"greater-equal":t.GEQUAL},this._stencilOpsMapping={keep:t.KEEP,zero:t.ZERO,replace:t.REPLACE,invert:t.INVERT,"increment-clamp":t.INCR,"decrement-clamp":t.DECR,"increment-wrap":t.INCR_WRAP,"decrement-wrap":t.DECR_WRAP},this.resetState()}onRenderTargetChange(t){if(this._activeRenderTarget===t)return;this._activeRenderTarget=t;let e=this._renderTargetStencilState[t.uid];e||(e=this._renderTargetStencilState[t.uid]={stencilMode:xt.DISABLED,stencilReference:0}),this.setStencilMode(e.stencilMode,e.stencilReference)}resetState(){this._stencilCache.enabled=!1,this._stencilCache.stencilMode=xt.NONE,this._stencilCache.stencilReference=0}setStencilMode(t,e){const i=this._renderTargetStencilState[this._activeRenderTarget.uid],s=this._gl,n=Be[t],a=this._stencilCache;if(i.stencilMode=t,i.stencilReference=e,t===xt.DISABLED){this._stencilCache.enabled&&(this._stencilCache.enabled=!1,s.disable(s.STENCIL_TEST));return}this._stencilCache.enabled||(this._stencilCache.enabled=!0,s.enable(s.STENCIL_TEST)),(t!==a.stencilMode||a.stencilReference!==e)&&(a.stencilMode=t,a.stencilReference=e,s.stencilFunc(this._comparisonFuncMapping[n.stencilBack.compare],e,255),s.stencilOp(s.KEEP,s.KEEP,this._stencilOpsMapping[n.stencilBack.passOp]))}}ic.extension={type:[T.WebGLSystem],name:"stencil"};class sc{constructor(t){this._syncFunctionHash=Object.create(null),this._adaptor=t,this._systemCheck()}_systemCheck(){if(!mo())throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support.")}ensureUniformGroup(t){const e=this.getUniformGroupData(t);t.buffer||(t.buffer=new zt({data:new Float32Array(e.layout.size/4),usage:et.UNIFORM|et.COPY_DST}))}getUniformGroupData(t){return this._syncFunctionHash[t._signature]||this._initUniformGroup(t)}_initUniformGroup(t){const e=t._signature;let i=this._syncFunctionHash[e];if(!i){const s=Object.keys(t.uniformStructures).map(o=>t.uniformStructures[o]),n=this._adaptor.createUboElements(s),a=this._generateUboSync(n.uboElements);i=this._syncFunctionHash[e]={layout:n,syncFunction:a}}return this._syncFunctionHash[e]}_generateUboSync(t){return this._adaptor.generateUboSync(t)}syncUniformGroup(t,e,i){const s=this.getUniformGroupData(t);t.buffer||(t.buffer=new zt({data:new Float32Array(s.layout.size/4),usage:et.UNIFORM|et.COPY_DST}));let n=null;return e||(e=t.buffer.data,n=t.buffer.dataInt32),i||(i=0),s.syncFunction(t.uniforms,e,n,i),!0}updateUniformGroup(t){if(t.isStatic&&!t._dirtyId)return!1;t._dirtyId=0;const e=this.syncUniformGroup(t);return t.buffer.update(),e}destroy(){this._syncFunctionHash=null}}const nc={f32:4,i32:4,"vec2":8,"vec3":12,"vec4":16,"vec2":8,"vec3":12,"vec4":16,"mat2x2":32,"mat3x3":48,"mat4x4":64};function jy(r){const t=r.map(n=>({data:n,offset:0,size:0})),e=16;let i=0,s=0;for(let n=0;n1&&(i=Math.max(i,e)*a.data.size);const o=i===12?16:i;a.size=i;const l=s%e;l>0&&e-l",test:r=>r.value.a!==void 0,ubo:` + var matrix = uv[name].toArray(true); + data[offset] = matrix[0]; + data[offset + 1] = matrix[1]; + data[offset + 2] = matrix[2]; + data[offset + 4] = matrix[3]; + data[offset + 5] = matrix[4]; + data[offset + 6] = matrix[5]; + data[offset + 8] = matrix[6]; + data[offset + 9] = matrix[7]; + data[offset + 10] = matrix[8]; + `,uniform:` + gl.uniformMatrix3fv(ud[name].location, false, uv[name].toArray(true)); + `},{type:"vec4",test:r=>r.type==="vec4"&&r.size===1&&r.value.width!==void 0,ubo:` + v = uv[name]; + data[offset] = v.x; + data[offset + 1] = v.y; + data[offset + 2] = v.width; + data[offset + 3] = v.height; + `,uniform:` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height) { + cv[0] = v.x; + cv[1] = v.y; + cv[2] = v.width; + cv[3] = v.height; + gl.uniform4f(ud[name].location, v.x, v.y, v.width, v.height); + } + `},{type:"vec2",test:r=>r.type==="vec2"&&r.size===1&&r.value.x!==void 0,ubo:` + v = uv[name]; + data[offset] = v.x; + data[offset + 1] = v.y; + `,uniform:` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.x || cv[1] !== v.y) { + cv[0] = v.x; + cv[1] = v.y; + gl.uniform2f(ud[name].location, v.x, v.y); + } + `},{type:"vec4",test:r=>r.type==="vec4"&&r.size===1&&r.value.red!==void 0,ubo:` + v = uv[name]; + data[offset] = v.red; + data[offset + 1] = v.green; + data[offset + 2] = v.blue; + data[offset + 3] = v.alpha; + `,uniform:` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue || cv[3] !== v.alpha) { + cv[0] = v.red; + cv[1] = v.green; + cv[2] = v.blue; + cv[3] = v.alpha; + gl.uniform4f(ud[name].location, v.red, v.green, v.blue, v.alpha); + } + `},{type:"vec3",test:r=>r.type==="vec3"&&r.size===1&&r.value.red!==void 0,ubo:` + v = uv[name]; + data[offset] = v.red; + data[offset + 1] = v.green; + data[offset + 2] = v.blue; + `,uniform:` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue) { + cv[0] = v.red; + cv[1] = v.green; + cv[2] = v.blue; + gl.uniform3f(ud[name].location, v.red, v.green, v.blue); + } + `}];function ac(r,t,e,i){const s=[` + var v = null; + var v2 = null; + var t = 0; + var index = 0; + var name = null; + var arrayOffset = null; + `];let n=0;for(let o=0;o1)h=l.offset/4,s.push(e(l,h-n));else{const p=i[l.data.type];h=l.offset/4,s.push(` + v = uv.${u}; + offset += ${h-n}; + ${p}; + `)}n=h}const a=s.join(` +`);return new Function("uv","data","dataInt32","offset",a)}var DM=Object.defineProperty,UM=Object.defineProperties,kM=Object.getOwnPropertyDescriptors,zy=Object.getOwnPropertySymbols,$M=Object.prototype.hasOwnProperty,LM=Object.prototype.propertyIsEnumerable,Wy=(r,t,e)=>t in r?DM(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,NM=(r,t)=>{for(var e in t||(t={}))$M.call(t,e)&&Wy(r,e,t[e]);if(zy)for(var e of zy(t))LM.call(t,e)&&Wy(r,e,t[e]);return r},XM=(r,t)=>UM(r,kM(t));function Wr(r,t){return` + for (let i = 0; i < ${r*t}; i++) { + data[offset + (((i / ${r})|0) * 4) + (i % ${r})] = v[i]; + } + `}const oc={f32:` + data[offset] = v;`,i32:` + dataInt32[offset] = v;`,"vec2":` + data[offset] = v[0]; + data[offset + 1] = v[1];`,"vec3":` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2];`,"vec4":` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 3] = v[3];`,"vec2":` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1];`,"vec3":` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1]; + dataInt32[offset + 2] = v[2];`,"vec4":` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1]; + dataInt32[offset + 2] = v[2]; + dataInt32[offset + 3] = v[3];`,"mat2x2":` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 4] = v[2]; + data[offset + 5] = v[3];`,"mat3x3":` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 4] = v[3]; + data[offset + 5] = v[4]; + data[offset + 6] = v[5]; + data[offset + 8] = v[6]; + data[offset + 9] = v[7]; + data[offset + 10] = v[8];`,"mat4x4":` + for (let i = 0; i < 16; i++) { + data[offset + i] = v[i]; + }`,"mat3x2":Wr(3,2),"mat4x2":Wr(4,2),"mat2x3":Wr(2,3),"mat4x3":Wr(4,3),"mat2x4":Wr(2,4),"mat3x4":Wr(3,4)},Vy=XM(NM({},oc),{"mat2x2":` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 3] = v[3]; + `});function Yy(r,t){const e=Math.max(nc[r.data.type]/16,1),i=r.data.value.length/r.data.size,s=(4-i%4)%4,n=r.data.type.indexOf("i32")>=0?"dataInt32":"data";return` + v = uv.${r.data.name}; + offset += ${t}; + + arrayOffset = offset; + + t = 0; + + for(var i=0; i < ${r.data.size*e}; i++) + { + for(var j = 0; j < ${i}; j++) + { + ${n}[arrayOffset++] = v[t++]; + } + ${s!==0?`arrayOffset += ${s};`:""} + } + `}function Ky(r){return ac(r,"uboStd40",Yy,oc)}class lc extends sc{constructor(){super({createUboElements:jy,generateUboSync:Ky})}}lc.extension={type:[T.WebGLSystem],name:"ubo"};class qy{constructor(){this._clearColorCache=[0,0,0,0],this._viewPortCache=new it}init(t,e){this._renderer=t,this._renderTargetSystem=e,t.runners.contextChange.add(this)}contextChange(){this._clearColorCache=[0,0,0,0],this._viewPortCache=new it;const t=this._renderer.gl;this._drawBuffersCache=[];for(let e=1;e<=16;e++)this._drawBuffersCache[e]=Array.from({length:e},(i,s)=>t.COLOR_ATTACHMENT0+s)}copyToTexture(t,e,i,s,n){const a=this._renderTargetSystem,o=this._renderer,l=a.getGpuRenderTarget(t),u=o.gl;return this.finishRenderPass(t),u.bindFramebuffer(u.FRAMEBUFFER,l.resolveTargetFramebuffer),o.texture.bind(e,0),u.copyTexSubImage2D(u.TEXTURE_2D,0,n.x,n.y,i.x,i.y,s.width,s.height),e}startRenderPass(t,e=!0,i,s,n=0,a=0){const o=this._renderTargetSystem,l=t.colorTexture,u=o.getGpuRenderTarget(t);if(a!==0&&this._renderer.context.webGLVersion<2)throw new Error("[RenderTargetSystem] Rendering to array layers requires WebGL2.");if(n>0){if(u.msaa)throw new Error("[RenderTargetSystem] Rendering to mip levels is not supported with MSAA render targets.");if(this._renderer.context.webGLVersion<2)throw new Error("[RenderTargetSystem] Rendering to mip levels requires WebGL2.")}let c=s.y;t.isRoot&&(c=l.pixelHeight-s.height-s.y),t.colorTextures.forEach(f=>{this._renderer.texture.unbind(f)});const h=this._renderer.gl;h.bindFramebuffer(h.FRAMEBUFFER,u.framebuffer),!t.isRoot&&(u._attachedMipLevel!==n||u._attachedLayer!==a)&&(t.colorTextures.forEach((f,m)=>{const g=this._renderer.texture.getGlSource(f);if(g.target===h.TEXTURE_2D){if(a!==0)throw new Error("[RenderTargetSystem] layer must be 0 when rendering to 2D textures in WebGL.");h.framebufferTexture2D(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0+m,h.TEXTURE_2D,g.texture,n)}else if(g.target===h.TEXTURE_2D_ARRAY){if(this._renderer.context.webGLVersion<2)throw new Error("[RenderTargetSystem] Rendering to 2D array textures requires WebGL2.");h.framebufferTextureLayer(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0+m,g.texture,n,a)}else if(g.target===h.TEXTURE_CUBE_MAP){if(a<0||a>5)throw new Error("[RenderTargetSystem] Cube map layer must be between 0 and 5.");h.framebufferTexture2D(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0+m,h.TEXTURE_CUBE_MAP_POSITIVE_X+a,g.texture,n)}else throw new Error("[RenderTargetSystem] Unsupported texture target for render-to-layer in WebGL.")}),u._attachedMipLevel=n,u._attachedLayer=a),t.colorTextures.length>1&&this._setDrawBuffers(t,h);const p=this._viewPortCache;(p.x!==s.x||p.y!==c||p.width!==s.width||p.height!==s.height)&&(p.x=s.x,p.y=c,p.width=s.width,p.height=s.height,h.viewport(s.x,c,s.width,s.height)),!u.depthStencilRenderBuffer&&(t.stencil||t.depth)&&this._initStencil(u),this.clear(t,e,i)}finishRenderPass(t){const e=this._renderTargetSystem.getGpuRenderTarget(t);if(!e.msaa)return;const i=this._renderer.gl;i.bindFramebuffer(i.FRAMEBUFFER,e.resolveTargetFramebuffer),i.bindFramebuffer(i.READ_FRAMEBUFFER,e.framebuffer),i.blitFramebuffer(0,0,e.width,e.height,0,0,e.width,e.height,i.COLOR_BUFFER_BIT,i.NEAREST),i.bindFramebuffer(i.FRAMEBUFFER,e.framebuffer)}initGpuRenderTarget(t){const e=this._renderer.gl,i=new Hy;return i._attachedMipLevel=0,i._attachedLayer=0,t.colorTexture instanceof de?(this._renderer.context.ensureCanvasSize(t.colorTexture.resource),i.framebuffer=null,i):(this._initColor(t,i),e.bindFramebuffer(e.FRAMEBUFFER,null),i)}destroyGpuRenderTarget(t){const e=this._renderer.gl;t.framebuffer&&(e.deleteFramebuffer(t.framebuffer),t.framebuffer=null),t.resolveTargetFramebuffer&&(e.deleteFramebuffer(t.resolveTargetFramebuffer),t.resolveTargetFramebuffer=null),t.depthStencilRenderBuffer&&(e.deleteRenderbuffer(t.depthStencilRenderBuffer),t.depthStencilRenderBuffer=null),t.msaaRenderBuffer.forEach(i=>{e.deleteRenderbuffer(i)}),t.msaaRenderBuffer=null}clear(t,e,i,s,n=0,a=0){if(!e)return;if(a!==0)throw new Error("[RenderTargetSystem] Clearing array layers is not supported in WebGL renderer.");const o=this._renderTargetSystem;typeof e=="boolean"&&(e=e?Wt.ALL:Wt.NONE);const l=this._renderer.gl;if(e&Wt.COLOR){i!=null||(i=o.defaultClearColor);const u=this._clearColorCache,c=i;(u[0]!==c[0]||u[1]!==c[1]||u[2]!==c[2]||u[3]!==c[3])&&(u[0]=c[0],u[1]=c[1],u[2]=c[2],u[3]=c[3],l.clearColor(c[0],c[1],c[2],c[3]))}l.clear(e)}resizeGpuRenderTarget(t){if(t.isRoot)return;const e=this._renderTargetSystem.getGpuRenderTarget(t);this._resizeColor(t,e),(t.stencil||t.depth)&&this._resizeStencil(e)}_initColor(t,e){const i=this._renderer,s=i.gl,n=s.createFramebuffer();if(e.resolveTargetFramebuffer=n,s.bindFramebuffer(s.FRAMEBUFFER,n),e.width=t.colorTexture.source.pixelWidth,e.height=t.colorTexture.source.pixelHeight,t.colorTextures.forEach((a,o)=>{const l=a.source;l.antialias&&(i.context.supports.msaa?e.msaa=!0:ae("[RenderTexture] Antialiasing on textures is not supported in WebGL1")),i.texture.bindSource(l,0);const u=i.texture.getGlSource(l),c=u.texture;if(u.target===s.TEXTURE_2D)s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+o,s.TEXTURE_2D,c,0);else if(u.target===s.TEXTURE_2D_ARRAY){if(i.context.webGLVersion<2)throw new Error("[RenderTargetSystem] TEXTURE_2D_ARRAY requires WebGL2.");s.framebufferTextureLayer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+o,c,0,0)}else if(u.target===s.TEXTURE_CUBE_MAP)s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+o,s.TEXTURE_CUBE_MAP_POSITIVE_X,c,0);else throw new Error("[RenderTargetSystem] Unsupported texture target for framebuffer attachment.")}),e.msaa){const a=s.createFramebuffer();e.framebuffer=a,s.bindFramebuffer(s.FRAMEBUFFER,a),t.colorTextures.forEach((o,l)=>{const u=s.createRenderbuffer();e.msaaRenderBuffer[l]=u})}else e.framebuffer=n;this._resizeColor(t,e)}_resizeColor(t,e){const i=t.colorTexture.source;if(e.width=i.pixelWidth,e.height=i.pixelHeight,e._attachedMipLevel=0,e._attachedLayer=0,t.colorTextures.forEach((s,n)=>{n!==0&&s.source.resize(i.width,i.height,i._resolution)}),e.msaa){const s=this._renderer,n=s.gl,a=e.framebuffer;n.bindFramebuffer(n.FRAMEBUFFER,a),t.colorTextures.forEach((o,l)=>{const u=o.source;s.texture.bindSource(u,0);const c=s.texture.getGlSource(u).internalFormat,h=e.msaaRenderBuffer[l];n.bindRenderbuffer(n.RENDERBUFFER,h),n.renderbufferStorageMultisample(n.RENDERBUFFER,4,c,u.pixelWidth,u.pixelHeight),n.framebufferRenderbuffer(n.FRAMEBUFFER,n.COLOR_ATTACHMENT0+l,n.RENDERBUFFER,h)})}}_initStencil(t){if(t.framebuffer===null)return;const e=this._renderer.gl,i=e.createRenderbuffer();t.depthStencilRenderBuffer=i,e.bindRenderbuffer(e.RENDERBUFFER,i),e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_STENCIL_ATTACHMENT,e.RENDERBUFFER,i),this._resizeStencil(t)}_resizeStencil(t){const e=this._renderer.gl;e.bindRenderbuffer(e.RENDERBUFFER,t.depthStencilRenderBuffer),t.msaa?e.renderbufferStorageMultisample(e.RENDERBUFFER,4,e.DEPTH24_STENCIL8,t.width,t.height):e.renderbufferStorage(e.RENDERBUFFER,this._renderer.context.webGLVersion===2?e.DEPTH24_STENCIL8:e.DEPTH_STENCIL,t.width,t.height)}prerender(t){const e=t.colorTexture.resource;this._renderer.context.multiView&&de.test(e)&&this._renderer.context.ensureCanvasSize(e)}postrender(t){if(this._renderer.context.multiView&&de.test(t.colorTexture.resource)){const e=this._renderer.context.canvas,i=t.colorTexture;i.context2D.drawImage(e,0,i.pixelHeight-e.height)}}_setDrawBuffers(t,e){const i=t.colorTextures.length,s=this._drawBuffersCache[i];if(this._renderer.context.webGLVersion===1){const n=this._renderer.context.extensions.drawBuffers;n?n.drawBuffersWEBGL(s):ae("[RenderTexture] This WebGL1 context does not support rendering to multiple targets")}else e.drawBuffers(s)}}class uc extends Nn{constructor(t){super(t),this.adaptor=new qy,this.adaptor.init(t,this)}}uc.extension={type:[T.WebGLSystem],name:"renderTarget"};class zn extends Ut{constructor({buffer:t,offset:e,size:i}){super(),this.uid=nt("buffer"),this._resourceType="bufferResource",this._touched=0,this._resourceId=nt("resource"),this._bufferResource=!0,this.destroyed=!1,this.buffer=t,this.offset=e|0,this.size=i,this.buffer.on("change",this.onBufferChange,this)}onBufferChange(){this._resourceId=nt("resource"),this.emit("change",this)}destroy(t=!1){this.destroyed=!0,t&&this.buffer.destroy(),this.emit("change",this),this.buffer=null,this.removeAllListeners()}}function Zy(r,t){const e=[],i=[` + var g = s.groups; + var sS = r.shader; + var p = s.glProgram; + var ugS = r.uniformGroup; + var resources; + `];let s=!1,n=0;const a=t._getProgramData(r.glProgram);for(const l in r.groups){const u=r.groups[l];e.push(` + resources = g[${l}].resources; + `);for(const c in u.resources){const h=u.resources[c];if(h instanceof wt)if(h.ubo){const p=r._uniformBindMap[l][Number(c)];e.push(` + sS.bindUniformBlock( + resources[${c}], + '${p}', + ${r.glProgram._uniformBlockData[p].index} + ); + `)}else e.push(` + ugS.updateUniformGroup(resources[${c}], p, sD); + `);else if(h instanceof zn){const p=r._uniformBindMap[l][Number(c)];e.push(` + sS.bindUniformBlock( + resources[${c}], + '${p}', + ${r.glProgram._uniformBlockData[p].index} + ); + `)}else if(h instanceof ht){const p=r._uniformBindMap[l][c],f=a.uniformData[p];f&&(s||(s=!0,i.push(` + var tS = r.texture; + `)),t._gl.uniform1i(f.location,n),e.push(` + tS.bind(resources[${c}], ${n}); + `),n++)}}}const o=[...i,...e].join(` +`);return new Function("r","s","sD",o)}class HM{}class Qy{constructor(t,e){this.program=t,this.uniformData=e,this.uniformGroups={},this.uniformDirtyGroups={},this.uniformBlockBindings={}}destroy(){this.uniformData=null,this.uniformGroups=null,this.uniformDirtyGroups=null,this.uniformBlockBindings=null,this.program=null}}function cc(r,t,e){const i=r.createShader(t);return r.shaderSource(i,e),r.compileShader(i),i}function hc(r){const t=new Array(r);for(let e=0;ea>o?1:-1);for(let a=0;a`${c}: ${u}`),i=r.getShaderInfoLog(t),s=i.split(` +`),n={},a=s.map(u=>parseFloat(u.replace(/^ERROR\: 0\:([\d]+)\:.*$/,"$1"))).filter(u=>u&&!n[u]?(n[u]=!0,!0):!1),o=[""];a.forEach(u=>{e[u-1]=`%c${e[u-1]}%c`,o.push("background: #FF0000; color:#FFFFFF; font-size: 10px","font-size: 10px")});const l=e.join(` +`);o[0]=l,console.error(i),console.groupCollapsed("click to view full shader code"),console.warn(...o),console.groupEnd()}function nx(r,t,e,i){r.getProgramParameter(t,r.LINK_STATUS)||(r.getShaderParameter(e,r.COMPILE_STATUS)||sx(r,e),r.getShaderParameter(i,r.COMPILE_STATUS)||sx(r,i),console.error("PixiJS Error: Could not initialize shader."),r.getProgramInfoLog(t)!==""&&console.warn("PixiJS Warning: gl.getProgramInfoLog()",r.getProgramInfoLog(t)))}function ax(r,t){const e=cc(r,r.VERTEX_SHADER,t.vertex),i=cc(r,r.FRAGMENT_SHADER,t.fragment),s=r.createProgram();r.attachShader(s,e),r.attachShader(s,i);const n=t.transformFeedbackVaryings;n&&(typeof r.transformFeedbackVaryings!="function"||r.transformFeedbackVaryings(s,n.names,n.bufferMode==="separate"?r.SEPARATE_ATTRIBS:r.INTERLEAVED_ATTRIBS)),r.linkProgram(s),r.getProgramParameter(s,r.LINK_STATUS)||nx(r,s,e,i),t._attributeData=ex(s,r,!/^[ \t]*#[ \t]*version[ \t]+300[ \t]+es[ \t]*$/m.test(t.vertex)),t._uniformData=ix(s,r),t._uniformBlockData=rx(s,r),r.deleteShader(e),r.deleteShader(i);const a={};for(const o in t._uniformData){const l=t._uniformData[o];a[o]={location:r.getUniformLocation(s,o),value:dc(l.type,l.size)}}return new Qy(s,a)}const Vn={textureCount:0,blockIndex:0};class fc{constructor(t){this._activeProgram=null,this._programDataHash=Object.create(null),this._shaderSyncFunctions=Object.create(null),this._renderer=t}contextChange(t){this._gl=t,this._programDataHash=Object.create(null),this._shaderSyncFunctions=Object.create(null),this._activeProgram=null}bind(t,e){if(this._setProgram(t.glProgram),e)return;Vn.textureCount=0,Vn.blockIndex=0;let i=this._shaderSyncFunctions[t.glProgram._key];i||(i=this._shaderSyncFunctions[t.glProgram._key]=this._generateShaderSync(t,this)),this._renderer.buffer.nextBindBase(!!t.glProgram.transformFeedbackVaryings),i(this._renderer,t,Vn)}updateUniformGroup(t){this._renderer.uniformGroup.updateUniformGroup(t,this._activeProgram,Vn)}bindUniformBlock(t,e,i=0){const s=this._renderer.buffer,n=this._getProgramData(this._activeProgram),a=t._bufferResource;a||this._renderer.ubo.updateUniformGroup(t);const o=t.buffer,l=s.updateBuffer(o),u=s.freeLocationForBufferBase(l);if(a){const{offset:h,size:p}=t;h===0&&p===o.data.byteLength?s.bindBufferBase(l,u):s.bindBufferRange(l,u,h)}else s.getLastBindBaseLocation(l)!==u&&s.bindBufferBase(l,u);const c=this._activeProgram._uniformBlockData[e].index;n.uniformBlockBindings[i]!==u&&(n.uniformBlockBindings[i]=u,this._renderer.gl.uniformBlockBinding(n.program,c,u))}_setProgram(t){if(this._activeProgram===t)return;this._activeProgram=t;const e=this._getProgramData(t);this._gl.useProgram(e.program)}_getProgramData(t){return this._programDataHash[t._key]||this._createProgramData(t)}_createProgramData(t){const e=t._key;return this._programDataHash[e]=ax(this._gl,t),this._programDataHash[e]}destroy(){for(const t of Object.keys(this._programDataHash))this._programDataHash[t].destroy();this._programDataHash=null,this._shaderSyncFunctions=null,this._activeProgram=null,this._renderer=null,this._gl=null}_generateShaderSync(t,e){return Zy(t,e)}resetState(){this._activeProgram=null}}fc.extension={type:[T.WebGLSystem],name:"shader"};const ox={f32:`if (cv !== v) { + cu.value = v; + gl.uniform1f(location, v); + }`,"vec2":`if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2f(location, v[0], v[1]); + }`,"vec3":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3f(location, v[0], v[1], v[2]); + }`,"vec4":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4f(location, v[0], v[1], v[2], v[3]); + }`,i32:`if (cv !== v) { + cu.value = v; + gl.uniform1i(location, v); + }`,"vec2":`if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2i(location, v[0], v[1]); + }`,"vec3":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3i(location, v[0], v[1], v[2]); + }`,"vec4":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4i(location, v[0], v[1], v[2], v[3]); + }`,u32:`if (cv !== v) { + cu.value = v; + gl.uniform1ui(location, v); + }`,"vec2":`if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2ui(location, v[0], v[1]); + }`,"vec3":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3ui(location, v[0], v[1], v[2]); + }`,"vec4":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4ui(location, v[0], v[1], v[2], v[3]); + }`,bool:`if (cv !== v) { + cu.value = v; + gl.uniform1i(location, v); + }`,"vec2":`if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2i(location, v[0], v[1]); + }`,"vec3":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3i(location, v[0], v[1], v[2]); + }`,"vec4":`if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4i(location, v[0], v[1], v[2], v[3]); + }`,"mat2x2":"gl.uniformMatrix2fv(location, false, v);","mat3x3":"gl.uniformMatrix3fv(location, false, v);","mat4x4":"gl.uniformMatrix4fv(location, false, v);"},lx={f32:"gl.uniform1fv(location, v);","vec2":"gl.uniform2fv(location, v);","vec3":"gl.uniform3fv(location, v);","vec4":"gl.uniform4fv(location, v);","mat2x2":"gl.uniformMatrix2fv(location, false, v);","mat3x3":"gl.uniformMatrix3fv(location, false, v);","mat4x4":"gl.uniformMatrix4fv(location, false, v);",i32:"gl.uniform1iv(location, v);","vec2":"gl.uniform2iv(location, v);","vec3":"gl.uniform3iv(location, v);","vec4":"gl.uniform4iv(location, v);",u32:"gl.uniform1iv(location, v);","vec2":"gl.uniform2iv(location, v);","vec3":"gl.uniform3iv(location, v);","vec4":"gl.uniform4iv(location, v);",bool:"gl.uniform1iv(location, v);","vec2":"gl.uniform2iv(location, v);","vec3":"gl.uniform3iv(location, v);","vec4":"gl.uniform4iv(location, v);"};function ux(r,t){const e=[` + var v = null; + var cv = null; + var cu = null; + var t = 0; + var gl = renderer.gl; + var name = null; + `];for(const i in r.uniforms){if(!t[i]){r.uniforms[i]instanceof wt?r.uniforms[i].ubo?e.push(` + renderer.shader.bindUniformBlock(uv.${i}, "${i}"); + `):e.push(` + renderer.shader.updateUniformGroup(uv.${i}); + `):r.uniforms[i]instanceof zn&&e.push(` + renderer.shader.bindBufferResource(uv.${i}, "${i}"); + `);continue}const s=r.uniformStructures[i];let n=!1;for(let a=0;a>=1,i++;this.stateId=t.data}for(let e=0;e>1,1),l=Math.max(l>>1,1)}}},mx=["right","left","top","bottom","front","back"];function gx(r){return{id:"cube",upload(t,e,i,s){const n=t.faces;for(let a=0;a=o&&c>=l,m=r.resource;(h?eO:rO)(e,a,t,o,l,u,c,m,p,f),t.width=o,t.height=l}};function eO(r,t,e,i,s,n,a,o,l,u){if(!u){l&&r.texImage2D(t,0,e.internalFormat,i,s,0,e.format,e.type,null),r.texSubImage2D(t,0,0,0,n,a,e.format,e.type,o);return}if(!l){r.texSubImage2D(t,0,0,0,e.format,e.type,o);return}r.texImage2D(t,0,e.internalFormat,i,s,0,e.format,e.type,o)}function rO(r,t,e,i,s,n,a,o,l,u){if(!u){l&&r.texImage2D(t,0,e.internalFormat,i,s,0,e.format,e.type,null),r.texSubImage2D(t,0,0,0,e.format,e.type,o);return}if(!l){r.texSubImage2D(t,0,0,0,e.format,e.type,o);return}r.texImage2D(t,0,e.internalFormat,e.format,e.type,o)}const iO=mu(),_x={id:"video",upload(r,t,e,i,s,n=iO){if(!r.isValid){const a=s!=null?s:t.target;e.texImage2D(a,0,t.internalFormat,1,1,0,t.format,t.type,null);return}_c.upload(r,t,e,i,s,n)}},bc={linear:9729,nearest:9728},bx={linear:{linear:9987,nearest:9985},nearest:{linear:9986,nearest:9984}},Yn={"clamp-to-edge":33071,repeat:10497,"mirror-repeat":33648},vx={never:512,less:513,equal:514,"less-equal":515,greater:516,"not-equal":517,"greater-equal":518,always:519};function vc(r,t,e,i,s,n,a,o){const l=n;if(!o||r.addressModeU!=="repeat"||r.addressModeV!=="repeat"||r.addressModeW!=="repeat"){const u=Yn[a?"clamp-to-edge":r.addressModeU],c=Yn[a?"clamp-to-edge":r.addressModeV],h=Yn[a?"clamp-to-edge":r.addressModeW];t[s](l,t.TEXTURE_WRAP_S,u),t[s](l,t.TEXTURE_WRAP_T,c),t.TEXTURE_WRAP_R&&t[s](l,t.TEXTURE_WRAP_R,h)}if((!o||r.magFilter!=="linear")&&t[s](l,t.TEXTURE_MAG_FILTER,bc[r.magFilter]),e){if(!o||r.mipmapFilter!=="linear"){const u=bx[r.minFilter][r.mipmapFilter];t[s](l,t.TEXTURE_MIN_FILTER,u)}}else t[s](l,t.TEXTURE_MIN_FILTER,bc[r.minFilter]);if(i&&r.maxAnisotropy>1){const u=Math.min(r.maxAnisotropy,t.getParameter(i.MAX_TEXTURE_MAX_ANISOTROPY_EXT));t[s](l,i.TEXTURE_MAX_ANISOTROPY_EXT,u)}r.compare&&t[s](l,t.TEXTURE_COMPARE_FUNC,vx[r.compare])}function yx(r){return{r8unorm:r.RED,r8snorm:r.RED,r8uint:r.RED,r8sint:r.RED,r16uint:r.RED,r16sint:r.RED,r16float:r.RED,rg8unorm:r.RG,rg8snorm:r.RG,rg8uint:r.RG,rg8sint:r.RG,r32uint:r.RED,r32sint:r.RED,r32float:r.RED,rg16uint:r.RG,rg16sint:r.RG,rg16float:r.RG,rgba8unorm:r.RGBA,"rgba8unorm-srgb":r.RGBA,rgba8snorm:r.RGBA,rgba8uint:r.RGBA,rgba8sint:r.RGBA,bgra8unorm:r.RGBA,"bgra8unorm-srgb":r.RGBA,rgb9e5ufloat:r.RGB,rgb10a2unorm:r.RGBA,rg11b10ufloat:r.RGB,rg32uint:r.RG,rg32sint:r.RG,rg32float:r.RG,rgba16uint:r.RGBA,rgba16sint:r.RGBA,rgba16float:r.RGBA,rgba32uint:r.RGBA,rgba32sint:r.RGBA,rgba32float:r.RGBA,stencil8:r.STENCIL_INDEX8,depth16unorm:r.DEPTH_COMPONENT,depth24plus:r.DEPTH_COMPONENT,"depth24plus-stencil8":r.DEPTH_STENCIL,depth32float:r.DEPTH_COMPONENT,"depth32float-stencil8":r.DEPTH_STENCIL}}var sO=Object.defineProperty,nO=Object.defineProperties,aO=Object.getOwnPropertyDescriptors,xx=Object.getOwnPropertySymbols,oO=Object.prototype.hasOwnProperty,lO=Object.prototype.propertyIsEnumerable,Tx=(r,t,e)=>t in r?sO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,cr=(r,t)=>{for(var e in t||(t={}))oO.call(t,e)&&Tx(r,e,t[e]);if(xx)for(var e of xx(t))lO.call(t,e)&&Tx(r,e,t[e]);return r},uO=(r,t)=>nO(r,aO(t));function Sx(r,t){let e={},i=r.RGBA;return r instanceof L.get().getWebGLRenderingContext()?t.srgb&&(e={"rgba8unorm-srgb":t.srgb.SRGB8_ALPHA8_EXT,"bgra8unorm-srgb":t.srgb.SRGB8_ALPHA8_EXT}):(e={"rgba8unorm-srgb":r.SRGB8_ALPHA8,"bgra8unorm-srgb":r.SRGB8_ALPHA8},i=r.RGBA8),cr(cr(cr(cr(cr(cr(uO(cr({r8unorm:r.R8,r8snorm:r.R8_SNORM,r8uint:r.R8UI,r8sint:r.R8I,r16uint:r.R16UI,r16sint:r.R16I,r16float:r.R16F,rg8unorm:r.RG8,rg8snorm:r.RG8_SNORM,rg8uint:r.RG8UI,rg8sint:r.RG8I,r32uint:r.R32UI,r32sint:r.R32I,r32float:r.R32F,rg16uint:r.RG16UI,rg16sint:r.RG16I,rg16float:r.RG16F,rgba8unorm:r.RGBA},e),{rgba8snorm:r.RGBA8_SNORM,rgba8uint:r.RGBA8UI,rgba8sint:r.RGBA8I,bgra8unorm:i,rgb9e5ufloat:r.RGB9_E5,rgb10a2unorm:r.RGB10_A2,rg11b10ufloat:r.R11F_G11F_B10F,rg32uint:r.RG32UI,rg32sint:r.RG32I,rg32float:r.RG32F,rgba16uint:r.RGBA16UI,rgba16sint:r.RGBA16I,rgba16float:r.RGBA16F,rgba32uint:r.RGBA32UI,rgba32sint:r.RGBA32I,rgba32float:r.RGBA32F,stencil8:r.STENCIL_INDEX8,depth16unorm:r.DEPTH_COMPONENT16,depth24plus:r.DEPTH_COMPONENT24,"depth24plus-stencil8":r.DEPTH24_STENCIL8,depth32float:r.DEPTH_COMPONENT32F,"depth32float-stencil8":r.DEPTH32F_STENCIL8}),t.s3tc?{"bc1-rgba-unorm":t.s3tc.COMPRESSED_RGBA_S3TC_DXT1_EXT,"bc2-rgba-unorm":t.s3tc.COMPRESSED_RGBA_S3TC_DXT3_EXT,"bc3-rgba-unorm":t.s3tc.COMPRESSED_RGBA_S3TC_DXT5_EXT}:{}),t.s3tc_sRGB?{"bc1-rgba-unorm-srgb":t.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,"bc2-rgba-unorm-srgb":t.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,"bc3-rgba-unorm-srgb":t.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}:{}),t.rgtc?{"bc4-r-unorm":t.rgtc.COMPRESSED_RED_RGTC1_EXT,"bc4-r-snorm":t.rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT,"bc5-rg-unorm":t.rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT,"bc5-rg-snorm":t.rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}:{}),t.bptc?{"bc6h-rgb-float":t.bptc.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT,"bc6h-rgb-ufloat":t.bptc.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT,"bc7-rgba-unorm":t.bptc.COMPRESSED_RGBA_BPTC_UNORM_EXT,"bc7-rgba-unorm-srgb":t.bptc.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT}:{}),t.etc?{"etc2-rgb8unorm":t.etc.COMPRESSED_RGB8_ETC2,"etc2-rgb8unorm-srgb":t.etc.COMPRESSED_SRGB8_ETC2,"etc2-rgb8a1unorm":t.etc.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,"etc2-rgb8a1unorm-srgb":t.etc.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,"etc2-rgba8unorm":t.etc.COMPRESSED_RGBA8_ETC2_EAC,"etc2-rgba8unorm-srgb":t.etc.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,"eac-r11unorm":t.etc.COMPRESSED_R11_EAC,"eac-rg11unorm":t.etc.COMPRESSED_SIGNED_RG11_EAC}:{}),t.astc?{"astc-4x4-unorm":t.astc.COMPRESSED_RGBA_ASTC_4x4_KHR,"astc-4x4-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,"astc-5x4-unorm":t.astc.COMPRESSED_RGBA_ASTC_5x4_KHR,"astc-5x4-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,"astc-5x5-unorm":t.astc.COMPRESSED_RGBA_ASTC_5x5_KHR,"astc-5x5-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,"astc-6x5-unorm":t.astc.COMPRESSED_RGBA_ASTC_6x5_KHR,"astc-6x5-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,"astc-6x6-unorm":t.astc.COMPRESSED_RGBA_ASTC_6x6_KHR,"astc-6x6-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,"astc-8x5-unorm":t.astc.COMPRESSED_RGBA_ASTC_8x5_KHR,"astc-8x5-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,"astc-8x6-unorm":t.astc.COMPRESSED_RGBA_ASTC_8x6_KHR,"astc-8x6-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,"astc-8x8-unorm":t.astc.COMPRESSED_RGBA_ASTC_8x8_KHR,"astc-8x8-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,"astc-10x5-unorm":t.astc.COMPRESSED_RGBA_ASTC_10x5_KHR,"astc-10x5-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,"astc-10x6-unorm":t.astc.COMPRESSED_RGBA_ASTC_10x6_KHR,"astc-10x6-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,"astc-10x8-unorm":t.astc.COMPRESSED_RGBA_ASTC_10x8_KHR,"astc-10x8-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,"astc-10x10-unorm":t.astc.COMPRESSED_RGBA_ASTC_10x10_KHR,"astc-10x10-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,"astc-12x10-unorm":t.astc.COMPRESSED_RGBA_ASTC_12x10_KHR,"astc-12x10-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR,"astc-12x12-unorm":t.astc.COMPRESSED_RGBA_ASTC_12x12_KHR,"astc-12x12-unorm-srgb":t.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}:{})}function wx(r){return{r8unorm:r.UNSIGNED_BYTE,r8snorm:r.BYTE,r8uint:r.UNSIGNED_BYTE,r8sint:r.BYTE,r16uint:r.UNSIGNED_SHORT,r16sint:r.SHORT,r16float:r.HALF_FLOAT,rg8unorm:r.UNSIGNED_BYTE,rg8snorm:r.BYTE,rg8uint:r.UNSIGNED_BYTE,rg8sint:r.BYTE,r32uint:r.UNSIGNED_INT,r32sint:r.INT,r32float:r.FLOAT,rg16uint:r.UNSIGNED_SHORT,rg16sint:r.SHORT,rg16float:r.HALF_FLOAT,rgba8unorm:r.UNSIGNED_BYTE,"rgba8unorm-srgb":r.UNSIGNED_BYTE,rgba8snorm:r.BYTE,rgba8uint:r.UNSIGNED_BYTE,rgba8sint:r.BYTE,bgra8unorm:r.UNSIGNED_BYTE,"bgra8unorm-srgb":r.UNSIGNED_BYTE,rgb9e5ufloat:r.UNSIGNED_INT_5_9_9_9_REV,rgb10a2unorm:r.UNSIGNED_INT_2_10_10_10_REV,rg11b10ufloat:r.UNSIGNED_INT_10F_11F_11F_REV,rg32uint:r.UNSIGNED_INT,rg32sint:r.INT,rg32float:r.FLOAT,rgba16uint:r.UNSIGNED_SHORT,rgba16sint:r.SHORT,rgba16float:r.HALF_FLOAT,rgba32uint:r.UNSIGNED_INT,rgba32sint:r.INT,rgba32float:r.FLOAT,stencil8:r.UNSIGNED_BYTE,depth16unorm:r.UNSIGNED_SHORT,depth24plus:r.UNSIGNED_INT,"depth24plus-stencil8":r.UNSIGNED_INT_24_8,depth32float:r.FLOAT,"depth32float-stencil8":r.FLOAT_32_UNSIGNED_INT_24_8_REV}}function Ex(r){return{"2d":r.TEXTURE_2D,cube:r.TEXTURE_CUBE_MAP,"1d":null,"3d":(r==null?void 0:r.TEXTURE_3D)||null,"2d-array":(r==null?void 0:r.TEXTURE_2D_ARRAY)||null,"cube-array":(r==null?void 0:r.TEXTURE_CUBE_MAP_ARRAY)||null}}function cO(r){r instanceof Uint8ClampedArray&&(r=new Uint8Array(r.buffer));const t=r.length;for(let e=0;et in r?hO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,gO=(r,t)=>{for(var e in t||(t={}))fO.call(t,e)&&Ax(r,e,t[e]);if(Px)for(var e of Px(t))mO.call(t,e)&&Ax(r,e,t[e]);return r},_O=(r,t)=>dO(r,pO(t));const bO=4;class yc{constructor(t){this._glSamplers=Object.create(null),this._boundTextures=[],this._activeTextureLocation=-1,this._boundSamplers=Object.create(null),this._premultiplyAlpha=!1,this._useSeparateSamplers=!1,this._renderer=t,this._managedTextures=new Ft({renderer:t,type:"resource",onUnload:this.onSourceUnload.bind(this),name:"glTexture"});const e={image:_c,buffer:px,video:_x,compressed:fx};this._uploads=_O(gO({},e),{cube:gx(e)})}get managedTextures(){return Object.values(this._managedTextures.items)}contextChange(t){this._gl=t,this._mapFormatToInternalFormat||(this._mapFormatToInternalFormat=Sx(t,this._renderer.context.extensions),this._mapFormatToType=wx(t),this._mapFormatToFormat=yx(t),this._mapViewDimensionToGlTarget=Ex(t)),this._managedTextures.removeAll(!0),this._glSamplers=Object.create(null),this._boundSamplers=Object.create(null),this._premultiplyAlpha=!1;for(let e=0;e<16;e++)this.bind(F.EMPTY,e)}initSource(t){this.bind(t)}bind(t,e=0){const i=t.source;t?(this.bindSource(i,e),this._useSeparateSamplers&&this._bindSampler(i.style,e)):(this.bindSource(null,e),this._useSeparateSamplers&&this._bindSampler(null,e))}bindSource(t,e=0){const i=this._gl;if(t._gcLastUsed=this._renderer.gc.now,this._boundTextures[e]!==t){this._boundTextures[e]=t,this._activateLocation(e),t||(t=F.EMPTY.source);const s=this.getGlSource(t);i.bindTexture(s.target,s.texture)}}_bindSampler(t,e=0){const i=this._gl;if(!t){this._boundSamplers[e]=null,i.bindSampler(e,null);return}const s=this._getGlSampler(t);this._boundSamplers[e]!==s&&(this._boundSamplers[e]=s,i.bindSampler(e,s))}unbind(t){const e=t.source,i=this._boundTextures,s=this._gl;for(let n=0;n1,this._renderer.context.extensions.anisotropicFiltering,"texParameteri",s.target,!this._renderer.context.supports.nonPowOf2wrapping&&!t.isPowerOfTwo,e)}onSourceUnload(t,e=!1){const i=t._gpuData[this._renderer.uid];i&&(e||(this.unbind(t),this._gl.deleteTexture(i.texture)),t.off("update",this.onSourceUpdate,this),t.off("resize",this.onSourceUpdate,this),t.off("styleChange",this.onStyleChange,this),t.off("updateMipmaps",this.onUpdateMipmaps,this))}onSourceUpdate(t){const e=this._gl,i=this.getGlSource(t);e.bindTexture(i.target,i.texture),this._boundTextures[this._activeTextureLocation]=t;const s=t.alphaMode==="premultiply-alpha-on-upload";if(this._premultiplyAlpha!==s&&(this._premultiplyAlpha=s,e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,s)),this._uploads[t.uploadMethodId])this._uploads[t.uploadMethodId].upload(t,i,e,this._renderer.context.webGLVersion);else if(i.target===e.TEXTURE_2D)this._initEmptyTexture2D(i,t);else if(i.target===e.TEXTURE_2D_ARRAY)this._initEmptyTexture2DArray(i,t);else if(i.target===e.TEXTURE_CUBE_MAP)this._initEmptyTextureCube(i,t);else throw new Error("[GlTextureSystem] Unsupported texture target for empty allocation.");this._applyMipRange(i,t),t.autoGenerateMipmaps&&t.mipLevelCount>1&&this.onUpdateMipmaps(t,!1)}onUpdateMipmaps(t,e=!0){e&&this.bindSource(t,0);const i=this.getGlSource(t);this._gl.generateMipmap(i.target)}_initEmptyTexture2D(t,e){const i=this._gl;i.texImage2D(i.TEXTURE_2D,0,t.internalFormat,e.pixelWidth,e.pixelHeight,0,t.format,t.type,null);let s=Math.max(e.pixelWidth>>1,1),n=Math.max(e.pixelHeight>>1,1);for(let a=1;a>1,1),n=Math.max(n>>1,1)}_initEmptyTexture2DArray(t,e){if(this._renderer.context.webGLVersion!==2)throw new Error("[GlTextureSystem] TEXTURE_2D_ARRAY requires WebGL2.");const i=this._gl,s=Math.max(e.arrayLayerCount|0,1);i.texImage3D(i.TEXTURE_2D_ARRAY,0,t.internalFormat,e.pixelWidth,e.pixelHeight,s,0,t.format,t.type,null);let n=Math.max(e.pixelWidth>>1,1),a=Math.max(e.pixelHeight>>1,1);for(let o=1;o>1,1),a=Math.max(a>>1,1)}_initEmptyTextureCube(t,e){const i=this._gl,s=6;for(let o=0;o>1,1),a=Math.max(e.pixelHeight>>1,1);for(let o=1;o>1,1),a=Math.max(a>>1,1)}}_applyMipRange(t,e){if(this._renderer.context.webGLVersion!==2)return;const i=this._gl,s=Math.max((e.mipLevelCount|0)-1,0);i.texParameteri(t.target,i.TEXTURE_BASE_LEVEL,0),i.texParameteri(t.target,i.TEXTURE_MAX_LEVEL,s)}_initSampler(t){const e=this._gl,i=this._gl.createSampler();return this._glSamplers[t._resourceId]=i,vc(t,e,this._boundTextures[this._activeTextureLocation].mipLevelCount>1,this._renderer.context.extensions.anisotropicFiltering,"samplerParameteri",i,!1,!0),this._glSamplers[t._resourceId]}_getGlSampler(t){return this._glSamplers[t._resourceId]||this._initSampler(t)}getGlSource(t){return t._gcLastUsed=this._renderer.gc.now,t._gpuData[this._renderer.uid]||this._initSource(t)}generateCanvas(t){const{pixels:e,width:i,height:s}=this.getPixels(t),n=L.get().createCanvas();n.width=i,n.height=s;const a=n.getContext("2d");if(a){const o=a.createImageData(i,s);o.data.set(e),a.putImageData(o,0,0)}return n}getPixels(t){const e=t.source.resolution,i=t.frame,s=Math.max(Math.round(i.width*e),1),n=Math.max(Math.round(i.height*e),1),a=new Uint8Array(bO*s*n),o=this._renderer,l=o.renderTarget.getRenderTarget(t),u=o.renderTarget.getGpuRenderTarget(l),c=o.gl;return c.bindFramebuffer(c.FRAMEBUFFER,u.resolveTargetFramebuffer),c.readPixels(Math.round(i.x*e),Math.round(i.y*e),s,n,c.RGBA,c.UNSIGNED_BYTE,a),{pixels:new Uint8ClampedArray(a.buffer),width:s,height:n}}destroy(){this._managedTextures.destroy(),this._glSamplers=null,this._boundTextures=null,this._boundSamplers=null,this._mapFormatToInternalFormat=null,this._mapFormatToType=null,this._mapFormatToFormat=null,this._uploads=null,this._renderer=null}resetState(){this._activeTextureLocation=-1,this._boundTextures.fill(F.EMPTY.source),this._boundSamplers=Object.create(null);const t=this._gl;this._premultiplyAlpha=!1,t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this._premultiplyAlpha)}}yc.extension={type:[T.WebGLSystem],name:"texture"};class xc{contextChange(t){const e=new wt({uColor:{value:new Float32Array([1,1,1,1]),type:"vec4"},uTransformMatrix:{value:new D,type:"mat3x3"},uRound:{value:0,type:"f32"}}),i=t.limits.maxBatchableTextures,s=Rr({name:"graphics",bits:[Rs,Os(i),cn,Or]});this.shader=new Zt({glProgram:s,resources:{localUniforms:e,batchSamplers:Gs(i)}})}execute(t,e){const i=e.context,s=i.customShader||this.shader,n=t.renderer,a=n.graphicsContext,{batcher:o,instructions:l}=a.getContextRenderData(i);s.groups[0]=n.globalUniforms.bindGroup,n.state.set(t.state),n.shader.bind(s),n.geometry.bind(o.geometry,s.glProgram);const u=l.instructions;for(let c=0;c",value:new D}}}})}execute(t,e){const i=t.renderer;let s=e._shader;if(s){if(!s.glProgram)return}else{s=this._shader;const n=e.texture,a=n.source;s.resources.uTexture=a,s.resources.uSampler=a.style,s.resources.textureUniforms.uniforms.uTextureMatrix=n.textureMatrix.mapCoord}s.groups[100]=i.globalUniforms.bindGroup,s.groups[101]=t.localUniformsBindGroup,i.encoder.draw({geometry:e._geometry,shader:s,state:e.state})}destroy(){this._shader.destroy(!0),this._shader=null}}Tc.extension={type:[T.WebGLPipesAdaptor],name:"mesh"};const vO=[...Ln,lc,Xy,By,rc,Ku,yc,uc,Qu,mc,fc,ec,dx,ic,tc],yO=[...Wu],xO=[bu,Tc,xc],Cx=[],Rx=[],Mx=[];$.handleByNamedList(T.WebGLSystem,Cx),$.handleByNamedList(T.WebGLPipes,Rx),$.handleByNamedList(T.WebGLPipesAdaptor,Mx),$.add(...vO,...yO,...xO);class Ox extends Tr{constructor(){const t={name:"webgl",type:Gt.WEBGL,systems:Cx,renderPipes:Rx,renderPipeAdaptors:Mx};super(t)}}var TO={__proto__:null,WebGLRenderer:Ox};class Sc{constructor(t){this._hash=Object.create(null),this._renderer=t}contextChange(t){this._gpu=t}getBindGroup(t,e,i){return t._updateKey(),this._hash[t._key]||this._createBindGroup(t,e,i)}_createBindGroup(t,e,i){var s;const n=this._gpu.device,a=e.layout[i],o=[],l=this._renderer;for(const h in a){const p=(s=t.resources[h])!=null?s:t.resources[a[h]];let f;if(p._resourceType==="uniformGroup"){const m=p;l.ubo.updateUniformGroup(m);const g=m.buffer;f={buffer:l.buffer.getGPUBuffer(g),offset:0,size:g.descriptor.size}}else if(p._resourceType==="buffer"){const m=p;f={buffer:l.buffer.getGPUBuffer(m),offset:0,size:m.descriptor.size}}else if(p._resourceType==="bufferResource"){const m=p;f={buffer:l.buffer.getGPUBuffer(m.buffer),offset:m.offset,size:m.size}}else if(p._resourceType==="textureSampler"){const m=p;f=l.texture.getGpuSampler(m)}else if(p._resourceType==="textureSource"){const m=p;f=l.texture.getTextureView(m)}o.push({binding:a[h],resource:f})}const u=l.shader.getProgramData(e).bindGroups[i],c=n.createBindGroup({layout:u,entries:o});return this._hash[t._key]=c,c}destroy(){this._hash=null,this._renderer=null}}Sc.extension={type:[T.WebGPUSystem],name:"bindGroup"};class Gx{constructor(t){this.gpuBuffer=t}destroy(){this.gpuBuffer.destroy(),this.gpuBuffer=null}}class wc{constructor(t){this._renderer=t,this._managedBuffers=new Ft({renderer:t,type:"resource",onUnload:this.onBufferUnload.bind(this),name:"gpuBuffer"})}contextChange(t){this._gpu=t}getGPUBuffer(t){var e;return t._gcLastUsed=this._renderer.gc.now,((e=t._gpuData[this._renderer.uid])==null?void 0:e.gpuBuffer)||this.createGPUBuffer(t)}updateBuffer(t){const e=this.getGPUBuffer(t),i=t.data;return t._updateID&&i&&(t._updateID=0,this._gpu.device.queue.writeBuffer(e,0,i.buffer,0,(t._updateSize||i.byteLength)+3&-4)),e}destroyAll(){this._managedBuffers.removeAll()}onBufferUnload(t){t.off("update",this.updateBuffer,this),t.off("change",this.onBufferChange,this)}createGPUBuffer(t){const e=this._gpu.device.createBuffer(t.descriptor);return t._updateID=0,t._resourceId=nt("resource"),t.data&&(Ps(t.data.buffer,e.getMappedRange(),t.data.byteOffset,t.data.byteLength),e.unmap()),t._gpuData[this._renderer.uid]=new Gx(e),this._managedBuffers.add(t)&&(t.on("update",this.updateBuffer,this),t.on("change",this.onBufferChange,this)),e}onBufferChange(t){this._managedBuffers.remove(t),t._updateID=0,this.createGPUBuffer(t)}destroy(){this._managedBuffers.destroy(),this._renderer=null,this._gpu=null}}wc.extension={type:[T.WebGPUSystem],name:"buffer"};class Ix{constructor({minUniformOffsetAlignment:t}){this._minUniformOffsetAlignment=256,this.byteIndex=0,this._minUniformOffsetAlignment=t,this.data=new Float32Array(65535)}clear(){this.byteIndex=0}addEmptyGroup(t){if(t>this._minUniformOffsetAlignment/4)throw new Error(`UniformBufferBatch: array is too large: ${t*4}`);const e=this.byteIndex;let i=e+t*4;if(i=Math.ceil(i/this._minUniformOffsetAlignment)*this._minUniformOffsetAlignment,i>this.data.length*4)throw new Error("UniformBufferBatch: ubo batch got too big");return this.byteIndex=i,e}addGroup(t){const e=this.addEmptyGroup(t.length);for(let i=0;i{this.gpu=e,this._renderer.runners.contextChange.emit(this.gpu)}),this._initPromise)}contextChange(t){this._renderer.gpu=t}async _createDeviceAndAdaptor(t){const e=await L.get().getNavigator().gpu.requestAdapter({powerPreference:t.powerPreference,forceFallbackAdapter:t.forceFallbackAdapter}),i=["texture-compression-bc","texture-compression-astc","texture-compression-etc2"].filter(n=>e.features.has(n)),s=await e.requestDevice({requiredFeatures:i});return{adapter:e,device:s}}destroy(){this.gpu=null,this._renderer=null}}Kn.extension={type:[T.WebGPUSystem],name:"device"},Kn.defaultOptions={powerPreference:void 0,forceFallbackAdapter:!1};var SO=Object.defineProperty,Bx=Object.getOwnPropertySymbols,wO=Object.prototype.hasOwnProperty,EO=Object.prototype.propertyIsEnumerable,Fx=(r,t,e)=>t in r?SO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Dx=(r,t)=>{for(var e in t||(t={}))wO.call(t,e)&&Fx(r,e,t[e]);if(Bx)for(var e of Bx(t))EO.call(t,e)&&Fx(r,e,t[e]);return r};class Pc{constructor(t){this._boundBindGroup=Object.create(null),this._boundVertexBuffer=Object.create(null),this._renderer=t}renderStart(){this.commandFinished=new Promise(t=>{this._resolveCommandFinished=t}),this.commandEncoder=this._renderer.gpu.device.createCommandEncoder()}beginRenderPass(t){this.endRenderPass(),this._clearCache(),this.renderPassEncoder=this.commandEncoder.beginRenderPass(t.descriptor)}endRenderPass(){this.renderPassEncoder&&this.renderPassEncoder.end(),this.renderPassEncoder=null}setViewport(t){this.renderPassEncoder.setViewport(t.x,t.y,t.width,t.height,0,1)}setPipelineFromGeometryProgramAndState(t,e,i,s){const n=this._renderer.pipeline.getPipeline(t,e,i,s);this.setPipeline(n)}setPipeline(t){this._boundPipeline!==t&&(this._boundPipeline=t,this.renderPassEncoder.setPipeline(t))}_setVertexBuffer(t,e){this._boundVertexBuffer[t]!==e&&(this._boundVertexBuffer[t]=e,this.renderPassEncoder.setVertexBuffer(t,this._renderer.buffer.updateBuffer(e)))}_setIndexBuffer(t){if(this._boundIndexBuffer===t)return;this._boundIndexBuffer=t;const e=t.data.BYTES_PER_ELEMENT===2?"uint16":"uint32";this.renderPassEncoder.setIndexBuffer(this._renderer.buffer.updateBuffer(t),e)}resetBindGroup(t){this._boundBindGroup[t]=null}setBindGroup(t,e,i){if(this._boundBindGroup[t]===e)return;this._boundBindGroup[t]=e,e._touch(this._renderer.gc.now,this._renderer.tick);const s=this._renderer.bindGroup.getBindGroup(e,i,t);this.renderPassEncoder.setBindGroup(t,s)}setGeometry(t,e){const i=this._renderer.pipeline.getBufferNamesToBind(t,e);for(const s in i)this._setVertexBuffer(parseInt(s,10),t.attributes[i[s]].buffer);t.indexBuffer&&this._setIndexBuffer(t.indexBuffer)}_setShaderBindGroups(t,e){for(const i in t.groups){const s=t.groups[i];e||this._syncBindGroup(s),this.setBindGroup(i,s,t.gpuProgram)}}_syncBindGroup(t){for(const e in t.resources){const i=t.resources[e];i.isUniformGroup&&this._renderer.ubo.updateUniformGroup(i)}}draw(t){const{geometry:e,shader:i,state:s,topology:n,size:a,start:o,instanceCount:l,skipSync:u}=t;this.setPipelineFromGeometryProgramAndState(e,i.gpuProgram,s,n),this.setGeometry(e,i.gpuProgram),this._setShaderBindGroups(i,u),e.indexBuffer?this.renderPassEncoder.drawIndexed(a||e.indexBuffer.data.length,l!=null?l:e.instanceCount,o||0):this.renderPassEncoder.draw(a||e.getSize(),l!=null?l:e.instanceCount,o||0)}finishRenderPass(){this.renderPassEncoder&&(this.renderPassEncoder.end(),this.renderPassEncoder=null)}postrender(){this.finishRenderPass(),this._gpu.device.queue.submit([this.commandEncoder.finish()]),this._resolveCommandFinished(),this.commandEncoder=null}restoreRenderPass(){const t=this._renderer.renderTarget.adaptor.getDescriptor(this._renderer.renderTarget.renderTarget,!1,[0,0,0,1],this._renderer.renderTarget.mipLevel,this._renderer.renderTarget.layer);this.renderPassEncoder=this.commandEncoder.beginRenderPass(t);const e=this._boundPipeline,i=Dx({},this._boundVertexBuffer),s=this._boundIndexBuffer,n=Dx({},this._boundBindGroup);this._clearCache();const a=this._renderer.renderTarget.viewport;this.renderPassEncoder.setViewport(a.x,a.y,a.width,a.height,0,1),this.setPipeline(e);for(const o in i)this._setVertexBuffer(o,i[o]);for(const o in n)this.setBindGroup(o,n[o],null);this._setIndexBuffer(s)}_clearCache(){for(let t=0;t<16;t++)this._boundBindGroup[t]=null,this._boundVertexBuffer[t]=null;this._boundIndexBuffer=null,this._boundPipeline=null}destroy(){this._renderer=null,this._gpu=null,this._boundBindGroup=null,this._boundVertexBuffer=null,this._boundIndexBuffer=null,this._boundPipeline=null}contextChange(t){this._gpu=t}}Pc.extension={type:[T.WebGPUSystem],name:"encoder",priority:1};class Ac{constructor(t){this._renderer=t}contextChange(){this.maxTextures=this._renderer.device.gpu.device.limits.maxSampledTexturesPerShaderStage,this.maxBatchableTextures=this.maxTextures}destroy(){}}Ac.extension={type:[T.WebGPUSystem],name:"limits"};class Cc{constructor(t){this._renderTargetStencilState=Object.create(null),this._renderer=t,t.renderTarget.onRenderTargetChange.add(this)}onRenderTargetChange(t){let e=this._renderTargetStencilState[t.uid];e||(e=this._renderTargetStencilState[t.uid]={stencilMode:xt.DISABLED,stencilReference:0}),this._activeRenderTarget=t,this.setStencilMode(e.stencilMode,e.stencilReference)}setStencilMode(t,e){const i=this._renderTargetStencilState[this._activeRenderTarget.uid];i.stencilMode=t,i.stencilReference=e;const s=this._renderer;s.pipeline.setStencilMode(t),s.encoder.renderPassEncoder.setStencilReference(e)}destroy(){this._renderer.renderTarget.onRenderTargetChange.remove(this),this._renderer=null,this._activeRenderTarget=null,this._renderTargetStencilState=null}}Cc.extension={type:[T.WebGPUSystem],name:"stencil"};const Ni={i32:{align:4,size:4},u32:{align:4,size:4},f32:{align:4,size:4},f16:{align:2,size:2},"vec2":{align:8,size:8},"vec2":{align:8,size:8},"vec2":{align:8,size:8},"vec2":{align:4,size:4},"vec3":{align:16,size:12},"vec3":{align:16,size:12},"vec3":{align:16,size:12},"vec3":{align:8,size:6},"vec4":{align:16,size:16},"vec4":{align:16,size:16},"vec4":{align:16,size:16},"vec4":{align:8,size:8},"mat2x2":{align:8,size:16},"mat2x2":{align:4,size:8},"mat3x2":{align:8,size:24},"mat3x2":{align:4,size:12},"mat4x2":{align:8,size:32},"mat4x2":{align:4,size:16},"mat2x3":{align:16,size:32},"mat2x3":{align:8,size:16},"mat3x3":{align:16,size:48},"mat3x3":{align:8,size:24},"mat4x3":{align:16,size:64},"mat4x3":{align:8,size:32},"mat2x4":{align:16,size:32},"mat2x4":{align:8,size:16},"mat3x4":{align:16,size:48},"mat3x4":{align:8,size:24},"mat4x4":{align:16,size:64},"mat4x4":{align:8,size:32}};function Ux(r){const t=r.map(i=>({data:i,offset:0,size:0}));let e=0;for(let i=0;i1&&(n=Math.max(n,a)*s.data.size),e=Math.ceil(e/a)*a,s.size=n,s.offset=e,e+=n}return e=Math.ceil(e/16)*16,{uboElements:t,size:e}}function kx(r,t){const{size:e,align:i}=Ni[r.data.type],s=(i-e)/4,n=r.data.type.indexOf("i32")>=0?"dataInt32":"data";return` + v = uv.${r.data.name}; + ${t!==0?`offset += ${t};`:""} + + arrayOffset = offset; + + t = 0; + + for(var i=0; i < ${r.data.size*(e/4)}; i++) + { + for(var j = 0; j < ${e/4}; j++) + { + ${n}[arrayOffset++] = v[t++]; + } + ${s!==0?`arrayOffset += ${s};`:""} + } + `}function $x(r){return ac(r,"uboWgsl",kx,Vy)}class Rc extends sc{constructor(){super({createUboElements:Ux,generateUboSync:$x})}}Rc.extension={type:[T.WebGPUSystem],name:"ubo"};const Xe=128;class Mc{constructor(t){this._bindGroupHash=Object.create(null),this._buffers=[],this._bindGroups=[],this._bufferResources=[],this._renderer=t,this._batchBuffer=new Ix({minUniformOffsetAlignment:Xe});const e=256/Xe;for(let i=0;it in r?PO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,OO=(r,t)=>{for(var e in t||(t={}))RO.call(t,e)&&Nx(r,e,t[e]);if(Lx)for(var e of Lx(t))MO.call(t,e)&&Nx(r,e,t[e]);return r},GO=(r,t)=>AO(r,CO(t));const IO={"point-list":0,"line-list":1,"line-strip":2,"triangle-list":3,"triangle-strip":4};function BO(r,t,e,i,s){return r<<24|t<<16|e<<10|i<<5|s}function FO(r,t,e,i,s){return e<<8|r<<5|i<<3|s<<1|t}class Oc{constructor(t){this._moduleCache=Object.create(null),this._bufferLayoutsCache=Object.create(null),this._bindingNamesCache=Object.create(null),this._pipeCache=Object.create(null),this._pipeStateCaches=Object.create(null),this._colorMask=15,this._multisampleCount=1,this._colorTargetCount=1,this._renderer=t}contextChange(t){this._gpu=t,this.setStencilMode(xt.DISABLED),this._updatePipeHash()}setMultisampleCount(t){this._multisampleCount!==t&&(this._multisampleCount=t,this._updatePipeHash())}setRenderTarget(t){this._multisampleCount=t.msaaSamples,this._depthStencilAttachment=t.descriptor.depthStencilAttachment?1:0,this._colorTargetCount=t.colorTargetCount,this._updatePipeHash()}setColorMask(t){this._colorMask!==t&&(this._colorMask=t,this._updatePipeHash())}setStencilMode(t){this._stencilMode!==t&&(this._stencilMode=t,this._stencilState=Be[t],this._updatePipeHash())}setPipeline(t,e,i,s){const n=this.getPipeline(t,e,i);s.setPipeline(n)}getPipeline(t,e,i,s){t._layoutKey||(Zu(t,e.attributeData),this._generateBufferKey(t)),s||(s=t.topology);const n=BO(t._layoutKey,e._layoutKey,i.data,i._blendModeId,IO[s]);return this._pipeCache[n]?this._pipeCache[n]:(this._pipeCache[n]=this._createPipeline(t,e,i,s),this._pipeCache[n])}_createPipeline(t,e,i,s){const n=this._gpu.device,a=this._createVertexBufferLayouts(t,e),o=this._renderer.state.getColorTargets(i,this._colorTargetCount),l=this._stencilMode===xt.RENDERING_MASK_ADD?0:this._colorMask;for(let h=0;h{var a;const o={arrayStride:0,stepMode:"vertex",attributes:[]},l=o.attributes;for(const u in e.attributeData){const c=t.attributes[u];((a=c.divisor)!=null?a:1)!==1&&ae(`Attribute ${u} has an invalid divisor value of '${c.divisor}'. WebGPU only supports a divisor value of 1`),c.buffer===n&&(o.arrayStride=c.stride,o.stepMode=c.instance?"instance":"vertex",l.push({shaderLocation:e.attributeData[u].location,offset:c.offset,format:c.format}))}l.length&&s.push(o)}),this._bufferLayoutsCache[i]=s,s}_updatePipeHash(){const t=FO(this._stencilMode,this._multisampleCount,this._colorMask,this._depthStencilAttachment,this._colorTargetCount);this._pipeStateCaches[t]||(this._pipeStateCaches[t]=Object.create(null)),this._pipeCache=this._pipeStateCaches[t]}destroy(){this._renderer=null,this._bufferLayoutsCache=null}}Oc.extension={type:[T.WebGPUSystem],name:"pipeline"};class Xx{constructor(){this.contexts=[],this.msaaTextures=[],this.msaaSamples=1}}class Hx{init(t,e){this._renderer=t,this._renderTargetSystem=e}copyToTexture(t,e,i,s,n){const a=this._renderer,o=this._getGpuColorTexture(t),l=a.texture.getGpuSource(e.source);return a.encoder.commandEncoder.copyTextureToTexture({texture:o,origin:i},{texture:l,origin:n},s),e}startRenderPass(t,e=!0,i,s,n=0,a=0){var o,l;const u=this._renderTargetSystem.getGpuRenderTarget(t);if(a!==0&&(o=u.msaaTextures)!=null&&o.length)throw new Error("[RenderTargetSystem] Rendering to array layers is not supported with MSAA render targets.");if(n>0&&(l=u.msaaTextures)!=null&&l.length)throw new Error("[RenderTargetSystem] Rendering to mip levels is not supported with MSAA render targets.");const c=this.getDescriptor(t,e,i,n,a);u.descriptor=c,this._renderer.pipeline.setRenderTarget(u),this._renderer.encoder.beginRenderPass(u),this._renderer.encoder.setViewport(s)}finishRenderPass(){this._renderer.encoder.endRenderPass()}_getGpuColorTexture(t){const e=this._renderTargetSystem.getGpuRenderTarget(t);return e.contexts[0]?e.contexts[0].getCurrentTexture():this._renderer.texture.getGpuSource(t.colorTextures[0].source)}getDescriptor(t,e,i,s=0,n=0){typeof e=="boolean"&&(e=e?Wt.ALL:Wt.NONE);const a=this._renderTargetSystem,o=a.getGpuRenderTarget(t),l=t.colorTextures.map((c,h)=>{const p=o.contexts[h];let f,m;if(p){if(n!==0)throw new Error("[RenderTargetSystem] Rendering to array layers is not supported for canvas targets.");f=p.getCurrentTexture().createView()}else f=this._renderer.texture.getGpuSource(c).createView({dimension:"2d",baseMipLevel:s,mipLevelCount:1,baseArrayLayer:n,arrayLayerCount:1});o.msaaTextures[h]&&(m=f,f=this._renderer.texture.getTextureView(o.msaaTextures[h]));const g=e&Wt.COLOR?"clear":"load";return i!=null||(i=a.defaultClearColor),{view:f,resolveTarget:m,clearValue:i,storeOp:"store",loadOp:g}});let u;if((t.stencil||t.depth)&&!t.depthStencilTexture&&(t.ensureDepthStencilTexture(),t.depthStencilTexture.source.sampleCount=o.msaa?4:1),t.depthStencilTexture){const c=e&Wt.STENCIL?"clear":"load",h=e&Wt.DEPTH?"clear":"load";u={view:this._renderer.texture.getGpuSource(t.depthStencilTexture.source).createView({dimension:"2d",baseMipLevel:s,mipLevelCount:1,baseArrayLayer:n,arrayLayerCount:1}),stencilStoreOp:"store",stencilLoadOp:c,depthClearValue:1,depthLoadOp:h,depthStoreOp:"store"}}return{colorAttachments:l,depthStencilAttachment:u}}clear(t,e=!0,i,s,n=0,a=0){if(!e)return;const{gpu:o,encoder:l}=this._renderer,u=o.device;if(l.commandEncoder===null){const c=u.createCommandEncoder(),h=this.getDescriptor(t,e,i,n,a),p=c.beginRenderPass(h);p.setViewport(s.x,s.y,s.width,s.height,0,1),p.end();const f=c.finish();u.queue.submit([f])}else this.startRenderPass(t,e,i,s,n,a)}initGpuRenderTarget(t){t.isRoot=!0;const e=new Xx;return e.colorTargetCount=t.colorTextures.length,t.colorTextures.forEach((i,s)=>{if(i instanceof de){const n=i.resource.getContext("webgpu"),a=i.transparent?"premultiplied":"opaque";try{n.configure({device:this._renderer.gpu.device,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC,format:"bgra8unorm",alphaMode:a})}catch(o){console.error(o)}e.contexts[s]=n}if(e.msaa=i.source.antialias,i.source.antialias){const n=new ht({width:0,height:0,sampleCount:4,arrayLayerCount:i.source.arrayLayerCount});e.msaaTextures[s]=n}}),e.msaa&&(e.msaaSamples=4,t.depthStencilTexture&&(t.depthStencilTexture.source.sampleCount=4)),e}destroyGpuRenderTarget(t){t.contexts.forEach(e=>{e.unconfigure()}),t.msaaTextures.forEach(e=>{e.destroy()}),t.msaaTextures.length=0,t.contexts.length=0}ensureDepthStencilTexture(t){const e=this._renderTargetSystem.getGpuRenderTarget(t);t.depthStencilTexture&&e.msaa&&(t.depthStencilTexture.source.sampleCount=4)}resizeGpuRenderTarget(t){const e=this._renderTargetSystem.getGpuRenderTarget(t);e.width=t.width,e.height=t.height,e.msaa&&t.colorTextures.forEach((i,s)=>{const n=e.msaaTextures[s];n==null||n.resize(i.source.width,i.source.height,i.source._resolution)})}}class Gc extends Nn{constructor(t){super(t),this.adaptor=new Hx,this.adaptor.init(t,this)}}Gc.extension={type:[T.WebGPUSystem],name:"renderTarget"};class Ic{constructor(){this._gpuProgramData=Object.create(null)}contextChange(t){this._gpu=t}getProgramData(t){return this._gpuProgramData[t._layoutKey]||this._createGPUProgramData(t)}_createGPUProgramData(t){const e=this._gpu.device,i=t.gpuLayout.map(n=>e.createBindGroupLayout({entries:n})),s={bindGroupLayouts:i};return this._gpuProgramData[t._layoutKey]={bindGroups:i,pipeline:e.createPipelineLayout(s)},this._gpuProgramData[t._layoutKey]}destroy(){this._gpu=null,this._gpuProgramData=null}}Ic.extension={type:[T.WebGPUSystem],name:"shader"};const Nt={};Nt.normal={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"}},Nt.add={alpha:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"one",dstFactor:"one",operation:"add"}},Nt.multiply={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"dst",dstFactor:"one-minus-src-alpha",operation:"add"}},Nt.screen={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"one",dstFactor:"one-minus-src",operation:"add"}},Nt.overlay={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"one",dstFactor:"one-minus-src",operation:"add"}},Nt.none={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"zero",dstFactor:"zero",operation:"add"}},Nt["normal-npm"]={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha",operation:"add"}},Nt["add-npm"]={alpha:{srcFactor:"one",dstFactor:"one",operation:"add"},color:{srcFactor:"src-alpha",dstFactor:"one",operation:"add"}},Nt["screen-npm"]={alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"src-alpha",dstFactor:"one-minus-src",operation:"add"}},Nt.erase={alpha:{srcFactor:"zero",dstFactor:"one-minus-src-alpha",operation:"add"},color:{srcFactor:"zero",dstFactor:"one-minus-src",operation:"add"}},Nt.min={alpha:{srcFactor:"one",dstFactor:"one",operation:"min"},color:{srcFactor:"one",dstFactor:"one",operation:"min"}},Nt.max={alpha:{srcFactor:"one",dstFactor:"one",operation:"max"},color:{srcFactor:"one",dstFactor:"one",operation:"max"}};class Bc{constructor(){this.defaultState=new jt,this.defaultState.blend=!0}contextChange(t){this.gpu=t}getColorTargets(t,e){const i=Nt[t.blendMode]||Nt.normal,s=[],n={format:"bgra8unorm",writeMask:0,blend:i};for(let a=0;a>1,1),n=Math.max(n>>1,1)}}},Wx=["right","left","top","bottom","front","back"];function Vx(r){return{type:"cube",upload(t,e,i){const s=t.faces;for(let n=0;n pos : array, 3> = array, 3>( + vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + + struct VertexOutput { + @builtin(position) position : vec4, + @location(0) texCoord : vec2, + }; + + @vertex + fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput { + var output : VertexOutput; + output.texCoord = pos[vertexIndex] * vec2(0.5, -0.5) + vec2(0.5); + output.position = vec4(pos[vertexIndex], 0.0, 1.0); + return output; + } + + @group(0) @binding(0) var imgSampler : sampler; + @group(0) @binding(1) var img : texture_2d; + + @fragment + fn fragmentMain(@location(0) texCoord : vec2) -> @location(0) vec4 { + return textureSample(img, imgSampler, texCoord); + } + `})),e=this.device.createRenderPipeline({layout:"auto",vertex:{module:this.mipmapShaderModule,entryPoint:"vertexMain"},fragment:{module:this.mipmapShaderModule,entryPoint:"fragmentMain",targets:[{format:t}]}}),this.pipelines[t]=e),e}generateMipmap(t){const e=this._getMipmapPipeline(t.format);if(t.dimension==="3d"||t.dimension==="1d")throw new Error("Generating mipmaps for non-2d textures is currently unsupported!");let i=t;const s=t.depthOrArrayLayers||1,n=t.usage&GPUTextureUsage.RENDER_ATTACHMENT;if(!n){const l={size:{width:Math.ceil(t.width/2),height:Math.ceil(t.height/2),depthOrArrayLayers:s},format:t.format,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_SRC|GPUTextureUsage.RENDER_ATTACHMENT,mipLevelCount:t.mipLevelCount-1};i=this.device.createTexture(l)}const a=this.device.createCommandEncoder({}),o=e.getBindGroupLayout(0);for(let l=0;lt in r?UO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,XO=(r,t)=>{for(var e in t||(t={}))LO.call(t,e)&&Zx(r,e,t[e]);if(qx)for(var e of qx(t))NO.call(t,e)&&Zx(r,e,t[e]);return r},HO=(r,t)=>kO(r,$O(t));class Uc{constructor(t){this.textureView=null,this.gpuTexture=t}destroy(){this.gpuTexture.destroy(),this.textureView=null,this.gpuTexture=null}}class kc{constructor(t){this._gpuSamplers=Object.create(null),this._bindGroupHash=Object.create(null),this._renderer=t,t.gc.addCollection(this,"_bindGroupHash","hash"),this._managedTextures=new Ft({renderer:t,type:"resource",onUnload:this.onSourceUnload.bind(this),name:"gpuTextureSource"});const e={image:Dc,buffer:jx,video:Yx,compressed:zx};this._uploads=HO(XO({},e),{cube:Vx(e)})}get managedTextures(){return Object.values(this._managedTextures.items)}contextChange(t){this._gpu=t}initSource(t){var e;return((e=t._gpuData[this._renderer.uid])==null?void 0:e.gpuTexture)||this._initSource(t)}_initSource(t){if(t.autoGenerateMipmaps){const l=Math.max(t.pixelWidth,t.pixelHeight);t.mipLevelCount=Math.floor(Math.log2(l))+1}let e=GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST;t.uploadMethodId!=="compressed"&&(e|=GPUTextureUsage.RENDER_ATTACHMENT,e|=GPUTextureUsage.COPY_SRC);const i=Fc[t.format]||{blockBytes:4,blockWidth:1,blockHeight:1},s=Math.ceil(t.pixelWidth/i.blockWidth)*i.blockWidth,n=Math.ceil(t.pixelHeight/i.blockHeight)*i.blockHeight,a={label:t.label,size:{width:s,height:n,depthOrArrayLayers:t.arrayLayerCount},format:t.format,sampleCount:t.sampleCount,mipLevelCount:t.mipLevelCount,dimension:t.dimension,usage:e},o=this._gpu.device.createTexture(a);return t._gpuData[this._renderer.uid]=new Uc(o),this._managedTextures.add(t)&&(t.on("update",this.onSourceUpdate,this),t.on("resize",this.onSourceResize,this),t.on("updateMipmaps",this.onUpdateMipmaps,this)),this.onSourceUpdate(t),o}onSourceUpdate(t){const e=this.getGpuSource(t);e&&(this._uploads[t.uploadMethodId]&&this._uploads[t.uploadMethodId].upload(t,e,this._gpu),t.autoGenerateMipmaps&&t.mipLevelCount>1&&this.onUpdateMipmaps(t))}onUpdateMipmaps(t){this._mipmapGenerator||(this._mipmapGenerator=new Kx(this._gpu.device));const e=this.getGpuSource(t);this._mipmapGenerator.generateMipmap(e)}onSourceUnload(t){t.off("update",this.onSourceUpdate,this),t.off("resize",this.onSourceResize,this),t.off("updateMipmaps",this.onUpdateMipmaps,this)}onSourceResize(t){t._gcLastUsed=this._renderer.gc.now;const e=t._gpuData[this._renderer.uid],i=e==null?void 0:e.gpuTexture;i?(i.width!==t.pixelWidth||i.height!==t.pixelHeight)&&(e.destroy(),this._bindGroupHash[t.uid]=null,t._gpuData[this._renderer.uid]=null,this.initSource(t)):this.initSource(t)}_initSampler(t){return this._gpuSamplers[t._resourceId]=this._gpu.device.createSampler(t),this._gpuSamplers[t._resourceId]}getGpuSampler(t){return this._gpuSamplers[t._resourceId]||this._initSampler(t)}getGpuSource(t){var e;return t._gcLastUsed=this._renderer.gc.now,((e=t._gpuData[this._renderer.uid])==null?void 0:e.gpuTexture)||this.initSource(t)}getTextureBindGroup(t){return this._bindGroupHash[t.uid]||this._createTextureBindGroup(t)}_createTextureBindGroup(t){const e=t.source;return this._bindGroupHash[t.uid]=new Te({0:e,1:e.style,2:new wt({uTextureMatrix:{type:"mat3x3",value:t.textureMatrix.mapCoord}})}),this._bindGroupHash[t.uid]}getTextureView(t){const e=t.source;e._gcLastUsed=this._renderer.gc.now;let i=e._gpuData[this._renderer.uid];return i||(this.initSource(e),i=e._gpuData[this._renderer.uid]),i.textureView||(i.textureView=i.gpuTexture.createView({dimension:e.viewDimension})),i.textureView}generateCanvas(t){const e=this._renderer,i=e.gpu.device.createCommandEncoder(),s=L.get().createCanvas();s.width=t.source.pixelWidth,s.height=t.source.pixelHeight;const n=s.getContext("webgpu");return n.configure({device:e.gpu.device,usage:GPUTextureUsage.COPY_DST|GPUTextureUsage.COPY_SRC,format:L.get().getNavigator().gpu.getPreferredCanvasFormat(),alphaMode:"premultiplied"}),i.copyTextureToTexture({texture:e.texture.getGpuSource(t.source),origin:{x:0,y:0}},{texture:n.getCurrentTexture()},{width:s.width,height:s.height}),e.gpu.device.queue.submit([i.finish()]),s}getPixels(t){const e=this.generateCanvas(t),i=me.getOptimalCanvasAndContext(e.width,e.height),s=i.context;s.drawImage(e,0,0);const{width:n,height:a}=e,o=s.getImageData(0,0,n,a),l=new Uint8ClampedArray(o.data.buffer);return me.returnCanvasAndContext(i),{pixels:l,width:n,height:a}}destroy(){this._managedTextures.destroy();for(const t of Object.keys(this._bindGroupHash)){const e=Number(t),i=this._bindGroupHash[e];i==null||i.destroy()}this._renderer=null,this._gpu=null,this._mipmapGenerator=null,this._gpuSamplers=null,this._bindGroupHash=null}}kc.extension={type:[T.WebGPUSystem],name:"texture"};class $c{constructor(){this._maxTextures=0}contextChange(t){const e=new wt({uTransformMatrix:{value:new D,type:"mat3x3"},uColor:{value:new Float32Array([1,1,1,1]),type:"vec4"},uRound:{value:0,type:"f32"}});this._maxTextures=t.limits.maxBatchableTextures;const i=Cr({name:"graphics",bits:[Cs,Ms(this._maxTextures),db,Mr]});this.shader=new Zt({gpuProgram:i,resources:{localUniforms:e}})}execute(t,e){const i=e.context,s=i.customShader||this.shader,n=t.renderer,a=n.graphicsContext,{batcher:o,instructions:l}=a.getContextRenderData(i),u=n.encoder;u.setGeometry(o.geometry,s.gpuProgram);const c=n.globalUniforms.bindGroup;u.setBindGroup(0,c,s.gpuProgram);const h=n.renderPipes.uniformBatch.getUniformBindGroup(s.resources.localUniforms,!0);u.setBindGroup(2,h,s.gpuProgram);const p=l.instructions;let f=null;for(let m=0;m",value:new D}}}})}execute(t,e){const i=t.renderer;let s=e._shader;if(!s)s=this._shader,s.groups[2]=i.texture.getTextureBindGroup(e.texture);else if(!s.gpuProgram)return;const n=s.gpuProgram;if(n.autoAssignGlobalUniforms&&(s.groups[0]=i.globalUniforms.bindGroup),n.autoAssignLocalUniforms){const a=t.localUniforms;s.groups[1]=i.renderPipes.uniformBatch.getUniformBindGroup(a,!0)}i.encoder.draw({geometry:e._geometry,shader:s,state:e.state})}destroy(){this._shader.destroy(!0),this._shader=null}}Lc.extension={type:[T.WebGPUPipesAdaptor],name:"mesh"};const jO=[...Ln,Rc,Pc,Kn,Ac,wc,kc,Gc,Ic,Bc,Oc,Ec,Cc,Sc],zO=[...Wu,Mc],WO=[vu,Lc,$c],Qx=[],Jx=[],t0=[];$.handleByNamedList(T.WebGPUSystem,Qx),$.handleByNamedList(T.WebGPUPipes,Jx),$.handleByNamedList(T.WebGPUPipesAdaptor,t0),$.add(...jO,...zO,...WO);class e0 extends Tr{constructor(){const t={name:"webgpu",type:Gt.WEBGPU,systems:Qx,renderPipes:Jx,renderPipeAdaptors:t0};super(t)}}var VO={__proto__:null,WebGPURenderer:e0};const YO={POINTS:"point-list",LINES:"line-list",LINE_STRIP:"line-strip",TRIANGLES:"triangle-list",TRIANGLE_STRIP:"triangle-strip"},KO=new Proxy(YO,{get(r,t){return r[t]}});var Nc=(r=>(r.CLAMP="clamp-to-edge",r.REPEAT="repeat",r.MIRRORED_REPEAT="mirror-repeat",r))(Nc||{});const qO=new Proxy(Nc,{get(r,t){return r[t]}});var Xc=(r=>(r.NEAREST="nearest",r.LINEAR="linear",r))(Xc||{});const ZO=new Proxy(Xc,{get(r,t){return r[t]}});var QO=Object.defineProperty,JO=Object.defineProperties,t3=Object.getOwnPropertyDescriptors,qn=Object.getOwnPropertySymbols,r0=Object.prototype.hasOwnProperty,i0=Object.prototype.propertyIsEnumerable,s0=(r,t,e)=>t in r?QO(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,e3=(r,t)=>{for(var e in t||(t={}))r0.call(t,e)&&s0(r,e,t[e]);if(qn)for(var e of qn(t))i0.call(t,e)&&s0(r,e,t[e]);return r},r3=(r,t)=>JO(r,t3(t)),i3=(r,t)=>{var e={};for(var i in r)r0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&qn)for(var i of qn(r))t.indexOf(i)<0&&i0.call(r,i)&&(e[i]=r[i]);return e};class Vr extends ht{constructor(t){const e=t,{faces:i}=e,s=i3(e,["faces"]);Vr._validateFaces(i);const n=i.right,a=n.resolution,o=n.format,l=n.alphaMode;super(r3(e3({},s),{resource:i,width:n.width,height:n.height,dimensions:"2d",viewDimension:"cube",arrayLayerCount:6,resolution:a,format:o,alphaMode:l})),this.uploadMethodId="cube",this.faces=i;for(const u of Object.keys(i)){const c=i[u];c.on("update",this._onFaceUpdate,this),c.on("resize",this._onFaceResize,this),c.on("unload",this._onFaceUpdate,this)}}destroy(){const t=this.faces;if(t)for(const e of Object.keys(t)){const i=t[e];i.off("update",this._onFaceUpdate,this),i.off("resize",this._onFaceResize,this),i.off("unload",this._onFaceUpdate,this)}super.destroy()}_onFaceUpdate(){this.emit("update",this)}_onFaceResize(t){Vr._validateFaces(this.faces),this.resize(t.width,t.height,t.resolution)}static _validateFaces(t){if(!t.right||!t.left||!t.top||!t.bottom||!t.front||!t.back)throw new Error("[CubeTextureSource] Requires { left, right, top, bottom, front, back } faces.");const e=t.right,i=e.pixelWidth,s=e.pixelHeight,n=e.format,a=e.alphaMode,o=e.resolution;for(const l of Object.keys(t)){const u=t[l];if(u.pixelWidth!==i||u.pixelHeight!==s)throw new Error(`[CubeTextureSource] Face '${String(l)}' has a different size. All faces must match.`);if(u.format!==n)throw new Error(`[CubeTextureSource] Face '${String(l)}' has a different format. All faces must match.`);if(u.alphaMode!==a)throw new Error(`[CubeTextureSource] Face '${String(l)}' has a different alphaMode. All faces must match.`);if(u.resolution!==o)throw new Error(`[CubeTextureSource] Face '${String(l)}' has a different resolution. All faces must match.`)}}}var s3=Object.defineProperty,n3=Object.defineProperties,a3=Object.getOwnPropertyDescriptors,Zn=Object.getOwnPropertySymbols,n0=Object.prototype.hasOwnProperty,a0=Object.prototype.propertyIsEnumerable,o0=(r,t,e)=>t in r?s3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,l0=(r,t)=>{for(var e in t||(t={}))n0.call(t,e)&&o0(r,e,t[e]);if(Zn)for(var e of Zn(t))a0.call(t,e)&&o0(r,e,t[e]);return r},o3=(r,t)=>n3(r,a3(t)),l3=(r,t)=>{var e={};for(var i in r)n0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Zn)for(var i of Zn(r))t.indexOf(i)<0&&a0.call(r,i)&&(e[i]=r[i]);return e};const Hc=["left","right","top","bottom","front","back"];function u3(r,t){const e=t?l0({},t):{};delete e.label;const i=Object.keys(e).sort(),s=i.length?`|${i.map(n=>`${n}=${String(e[n])}`).join("&")}`:"";return`cube:${Hc.map(n=>r[n]).join(",")}${s}`}class Qn extends Ut{constructor(t){var e;super(),this.uid=nt("cubeTexture"),this.destroyed=!1;const{label:i,source:s}=t;this.label=i,this.source=s,this.source.label=(e=this.label)!=null?e:this.source.label}static from(t,e=!1){if(t instanceof Vr)return new Qn({source:t});const i=t,{faces:s}=i,n=l3(i,["faces"]);let a=null;const o=Hc.every(h=>typeof s[h]=="string");if(!e&&o&&(a=u3(s,n),tt.has(a)))return tt.get(a);const l=h=>h.isTexture?h:F.from(h),u={};for(const h of Hc)u[h]=l(s[h]).source;const c=new Qn({source:new Vr(o3(l0({},n),{faces:u})),label:n.label});return a&&(tt.set(a,c),c.once("destroy",()=>{tt.has(a)&&tt.remove(a)})),c}destroy(t=!1){this.destroyed||(this.destroyed=!0,t&&this.source.destroy(),this.emit("destroy",this),this.removeAllListeners())}}const jc=Object.create(null),zc=Object.create(null);function c3(r){var t;if(r.type===Gt.WEBGPU)return zc[t=r.uid]||(zc[t]=r.gpu.device.createTexture({label:"ExternalSource placeholder",size:{width:1,height:1},format:"rgba8unorm",usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST})),zc[r.uid];if(!jc[r.uid]){const e=r.gl,i=e.createTexture();e.bindTexture(e.TEXTURE_2D,i),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,null),jc[r.uid]=i}return jc[r.uid]}class h3 extends ht{constructor({resource:t,renderer:e,label:i,width:s,height:n}){var a,o;t||(t=c3(e)),s||(s=(a=s!=null?s:t==null?void 0:t.width)!=null?a:1),n||(n=(o=n!=null?n:t==null?void 0:t.height)!=null?o:1),super({resource:t,width:s,height:n,label:i,autoGarbageCollect:!1}),this._renderer=e,this._initGpuData(t)}static test(t){return globalThis.GPUTexture&&t instanceof GPUTexture||globalThis.WebGLTexture&&t instanceof WebGLTexture}_validateTexture(t){const e=this._renderer,i=!!e.gpu,s=globalThis.GPUTexture&&t instanceof GPUTexture,n=globalThis.WebGLTexture&&t instanceof WebGLTexture;if(i&&n)throw new Error("Cannot use WebGLTexture with a WebGPU renderer");if(!i&&s)throw new Error("Cannot use GPUTexture with a WebGL renderer");if(!i){const a=e.gl;if(a&&!a.isTexture(t))throw new Error("WebGLTexture does not belong to this renderer's WebGL context")}}_initGpuData(t){const e=this._renderer;this._validateTexture(t),e.gpu?this._gpuData[e.uid]=new Uc(t):this._gpuData[e.uid]=new gc(t)}updateGPUTexture(t,e,i){const s=this._renderer,n=this._gpuData[s.uid];if(this.resource=t,s.gpu){this._validateTexture(t);const a=n;if(a.gpuTexture!==t){a.gpuTexture=t,a.textureView=null;const u=s.texture;u!=null&&u._bindGroupHash&&(u._bindGroupHash[this.uid]=null)}const o=e!=null?e:t.width,l=i!=null?i:t.height;this.resize(o,l)}else{this._validateTexture(t);const a=n;a.texture=t,e!==void 0&&i!==void 0&&this.resize(e,i)}this.emit("update",this)}destroy(){const t=this._renderer;delete this._gpuData[t.uid],super.destroy()}}class d3{constructor(){this.x0=0,this.y0=0,this.x1=1,this.y1=0,this.x2=1,this.y2=1,this.x3=0,this.y3=1,this.uvsFloat32=new Float32Array(8)}set(t,e,i){const s=e.width,n=e.height;if(i){const a=t.width/2/s,o=t.height/2/n,l=t.x/s+a,u=t.y/n+o;i=H.add(i,H.NW),this.x0=l+a*H.uX(i),this.y0=u+o*H.uY(i),i=H.add(i,2),this.x1=l+a*H.uX(i),this.y1=u+o*H.uY(i),i=H.add(i,2),this.x2=l+a*H.uX(i),this.y2=u+o*H.uY(i),i=H.add(i,2),this.x3=l+a*H.uX(i),this.y3=u+o*H.uY(i)}else this.x0=t.x/s,this.y0=t.y/n,this.x1=(t.x+t.width)/s,this.y1=t.y/n,this.x2=(t.x+t.width)/s,this.y2=(t.y+t.height)/n,this.x3=t.x/s,this.y3=(t.y+t.height)/n;this.uvsFloat32[0]=this.x0,this.uvsFloat32[1]=this.y0,this.uvsFloat32[2]=this.x1,this.uvsFloat32[3]=this.y1,this.uvsFloat32[4]=this.x2,this.uvsFloat32[5]=this.y2,this.uvsFloat32[6]=this.x3,this.uvsFloat32[7]=this.y3}}function p3(r){const t=r.toString(),e=t.indexOf("{"),i=t.lastIndexOf("}");if(e===-1||i===-1)throw new Error("getFunctionBody: No body found in function definition");return t.slice(e+1,i).trim()}function f3(r,t){return r.getFastGlobalBounds(!0,t)}var m3=Object.defineProperty,Jn=Object.getOwnPropertySymbols,u0=Object.prototype.hasOwnProperty,c0=Object.prototype.propertyIsEnumerable,h0=(r,t,e)=>t in r?m3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,g3=(r,t)=>{for(var e in t||(t={}))u0.call(t,e)&&h0(r,e,t[e]);if(Jn)for(var e of Jn(t))c0.call(t,e)&&h0(r,e,t[e]);return r},_3=(r,t)=>{var e={};for(var i in r)u0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&Jn)for(var i of Jn(r))t.indexOf(i)<0&&c0.call(r,i)&&(e[i]=r[i]);return e};class b3 extends xe{constructor(t){var e,i;typeof t=="function"&&(t={render:t});const s=t,{render:n}=s,a=_3(s,["render"]);super(g3({label:"RenderContainer"},a)),this.renderPipeId="customRender",this.batched=!1,n&&(this.render=n),this.containsPoint=(e=t.containsPoint)!=null?e:(()=>!1),this.addBounds=(i=t.addBounds)!=null?i:(()=>!1)}updateBounds(){this._bounds.clear(),this.addBounds(this._bounds)}render(t){}}function v3(r,t,e){const i=e.renderPipes?e:e.batch.renderer;return r.collectRenderables(t,i,null)}function y3(r,t){const e=t._scale,i=t._pivot,s=t._position,n=e._x,a=e._y,o=i._x,l=i._y;r.a=t._cx*n,r.b=t._sx*n,r.c=t._cy*a,r.d=t._sy*a,r.tx=s._x-(o*r.a+l*r.c),r.ty=s._y-(o*r.b+l*r.d)}function x3(r,t,e){const i=r.a,s=r.b,n=r.c,a=r.d,o=r.tx,l=r.ty,u=t.a,c=t.b,h=t.c,p=t.d;e.a=i*u+s*h,e.b=i*c+s*p,e.c=n*u+a*h,e.d=n*c+a*p,e.tx=o*u+l*h+t.tx,e.ty=o*c+l*p+t.ty}function T3(r){r instanceof pe&&(r={path:r,textureMatrix:null,out:null});const t=[],e=[],i=[],s=r.path.shapePath,n=r.textureMatrix;s.shapePrimitives.forEach(({shape:o,transform:l})=>{const u=i.length,c=t.length/2,h=[],p=sr[o.type];p.build(o,h),l&&Bs(h,l),p.triangulate(h,t,2,c,i,u);const f=e.length/2;n?(l&&n.append(l.clone().invert()),Lo(t,2,c,e,f,2,t.length/2-c,n)):No(e,f,2,t.length/2-c)});const a=r.out;return a?(a.positions=new Float32Array(t),a.uvs=new Float32Array(e),a.indices=new Uint32Array(i),a):new Le({positions:new Float32Array(t),uvs:new Float32Array(e),indices:new Uint32Array(i)})}var S3=Object.defineProperty,d0=Object.getOwnPropertySymbols,w3=Object.prototype.hasOwnProperty,E3=Object.prototype.propertyIsEnumerable,p0=(r,t,e)=>t in r?S3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,f0=(r,t)=>{for(var e in t||(t={}))w3.call(t,e)&&p0(r,e,t[e]);if(d0)for(var e of d0(t))E3.call(t,e)&&p0(r,e,t[e]);return r};const m0=class b1 extends dt{constructor(t={}){t=f0(f0({},b1.defaultOptions),t),super(),this.renderLayerChildren=[],this.sortableChildren=t.sortableChildren,this.sortFunction=t.sortFunction}attach(...t){for(let e=0;er.zIndex-t.zIndex};let P3=m0;var A3=Object.defineProperty,g0=Object.getOwnPropertySymbols,C3=Object.prototype.hasOwnProperty,R3=Object.prototype.propertyIsEnumerable,_0=(r,t,e)=>t in r?A3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,b0=(r,t)=>{for(var e in t||(t={}))C3.call(t,e)&&_0(r,e,t[e]);if(g0)for(var e of g0(t))R3.call(t,e)&&_0(r,e,t[e]);return r};const v0=class v1 extends Le{constructor(...t){var e;super({});let i=(e=t[0])!=null?e:{};typeof i=="number"&&(i={width:i,height:t[1],verticesX:t[2],verticesY:t[3]}),this.build(i)}build(t){var e,i,s,n;t=b0(b0({},v1.defaultOptions),t),this.verticesX=(e=this.verticesX)!=null?e:t.verticesX,this.verticesY=(i=this.verticesY)!=null?i:t.verticesY,this.width=(s=this.width)!=null?s:t.width,this.height=(n=this.height)!=null?n:t.height;const a=this.verticesX*this.verticesY,o=[],l=[],u=[],c=this.verticesX-1,h=this.verticesY-1,p=this.width/c,f=this.height/h;for(let g=0;gt in r?F3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Wc=(r,t)=>{for(var e in t||(t={}))P0.call(t,e)&&C0(r,e,t[e]);if(ra)for(var e of ra(t))A0.call(t,e)&&C0(r,e,t[e]);return r},k3=(r,t)=>D3(r,U3(t)),$3=(r,t)=>{var e={};for(var i in r)P0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&ra)for(var i of ra(r))t.indexOf(i)<0&&A0.call(r,i)&&(e[i]=r[i]);return e};const R0=class y1 extends Nr{constructor(t){t=Wc(Wc({},y1.defaultOptions),t);const e=t,{texture:i,verticesX:s,verticesY:n}=e,a=$3(e,["texture","verticesX","verticesY"]),o=new E0(ue({width:i.width,height:i.height,verticesX:s,verticesY:n}));super(ue(k3(Wc({},a),{geometry:o}))),this._texture=i,this.geometry.setCorners(t.x0,t.y0,t.x1,t.y1,t.x2,t.y2,t.x3,t.y3)}textureUpdated(){const t=this.geometry;if(!t)return;const{width:e,height:i}=this.texture;(t.width!==e||t.height!==i)&&(t.width=e,t.height=i,t.updateProjection())}set texture(t){this._texture!==t&&(super.texture=t,this.textureUpdated())}get texture(){return this._texture}setCorners(t,e,i,s,n,a,o,l){this.geometry.setCorners(t,e,i,s,n,a,o,l)}};R0.defaultOptions={texture:F.WHITE,verticesX:10,verticesY:10,x0:0,y0:0,x1:100,y1:0,x2:100,y2:100,x3:0,y3:100};let L3=R0;var N3=Object.defineProperty,X3=Object.defineProperties,H3=Object.getOwnPropertyDescriptors,ia=Object.getOwnPropertySymbols,M0=Object.prototype.hasOwnProperty,O0=Object.prototype.propertyIsEnumerable,G0=(r,t,e)=>t in r?N3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,j3=(r,t)=>{for(var e in t||(t={}))M0.call(t,e)&&G0(r,e,t[e]);if(ia)for(var e of ia(t))O0.call(t,e)&&G0(r,e,t[e]);return r},z3=(r,t)=>X3(r,H3(t)),W3=(r,t)=>{var e={};for(var i in r)M0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&ia)for(var i of ia(r))t.indexOf(i)<0&&O0.call(r,i)&&(e[i]=r[i]);return e};class V3 extends Nr{constructor(t){const e=t,{texture:i,verticesX:s,verticesY:n}=e,a=W3(e,["texture","verticesX","verticesY"]),o=new ta(ue({width:i.width,height:i.height,verticesX:s,verticesY:n}));super(ue(z3(j3({},a),{geometry:o,texture:i}))),this.texture=i,this.autoResize=!0}textureUpdated(){const t=this.geometry,{width:e,height:i}=this.texture;this.autoResize&&(t.width!==e||t.height!==i)&&(t.width=e,t.height=i,t.build({}))}set texture(t){var e;(e=this._texture)==null||e.off("update",this.textureUpdated,this),super.texture=t,t.on("update",this.textureUpdated,this),this.textureUpdated()}get texture(){return this._texture}destroy(t){this.texture.off("update",this.textureUpdated,this),super.destroy(t)}}var Y3=Object.defineProperty,I0=Object.getOwnPropertySymbols,K3=Object.prototype.hasOwnProperty,q3=Object.prototype.propertyIsEnumerable,B0=(r,t,e)=>t in r?Y3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,F0=(r,t)=>{for(var e in t||(t={}))K3.call(t,e)&&B0(r,e,t[e]);if(I0)for(var e of I0(t))q3.call(t,e)&&B0(r,e,t[e]);return r};const D0=class x1 extends Le{constructor(t){const{width:e,points:i,textureScale:s}=F0(F0({},x1.defaultOptions),t);super({positions:new Float32Array(i.length*4),uvs:new Float32Array(i.length*4),indices:new Uint32Array((i.length-1)*6)}),this.points=i,this._width=e,this.textureScale=s,this._build()}get width(){return this._width}_build(){const t=this.points;if(!t)return;const e=this.getBuffer("aPosition"),i=this.getBuffer("aUV"),s=this.getIndex();if(t.length<1)return;e.data.length/4!==t.length&&(e.data=new Float32Array(t.length*4),i.data=new Float32Array(t.length*4),s.data=new Uint16Array((t.length-1)*6));const n=i.data,a=s.data;n[0]=0,n[1]=0,n[2]=0,n[3]=1;let o=0,l=t[0];const u=this._width*this.textureScale,c=t.length;for(let p=0;p0){const m=l.x-t[p].x,g=l.y-t[p].y,_=Math.sqrt(m*m+g*g);l=t[p],o+=_/u}else o=p/(c-1);n[f]=o,n[f+1]=0,n[f+2]=o,n[f+3]=1}let h=0;for(let p=0;p0?this.textureScale*this._width/2:this._width/2;for(let u=0;u1&&(p=1);const f=Math.sqrt(s*s+n*n);f<1e-6?(s=0,n=0):(s/=f,n/=f,s*=l,n*=l),a[h]=c.x+s,a[h+1]=c.y+n,a[h+2]=c.x-s,a[h+3]=c.y-n,e=c}this.buffers[0].update()}update(){this.textureScale>0?this._build():this.updateVertices()}};D0.defaultOptions={width:200,points:[],textureScale:0};let U0=D0;var Z3=Object.defineProperty,Q3=Object.defineProperties,J3=Object.getOwnPropertyDescriptors,sa=Object.getOwnPropertySymbols,k0=Object.prototype.hasOwnProperty,$0=Object.prototype.propertyIsEnumerable,L0=(r,t,e)=>t in r?Z3(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Vc=(r,t)=>{for(var e in t||(t={}))k0.call(t,e)&&L0(r,e,t[e]);if(sa)for(var e of sa(t))$0.call(t,e)&&L0(r,e,t[e]);return r},tG=(r,t)=>Q3(r,J3(t)),eG=(r,t)=>{var e={};for(var i in r)k0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&sa)for(var i of sa(r))t.indexOf(i)<0&&$0.call(r,i)&&(e[i]=r[i]);return e};const N0=class T1 extends Nr{constructor(t){const e=Vc(Vc({},T1.defaultOptions),t),{texture:i,points:s,textureScale:n}=e,a=eG(e,["texture","points","textureScale"]),o=new U0(ue({width:i.height,points:s,textureScale:n}));n>0&&(i.source.style.addressMode="repeat"),super(ue(tG(Vc({},a),{texture:i,geometry:o}))),this.autoUpdate=!0,this.onRender=this._render}_render(){const t=this.geometry;(this.autoUpdate||t._width!==this.texture.height)&&(t._width=this.texture.height,t.update())}};N0.defaultOptions={textureScale:0};let rG=N0;var iG=Object.defineProperty,sG=Object.defineProperties,nG=Object.getOwnPropertyDescriptors,na=Object.getOwnPropertySymbols,X0=Object.prototype.hasOwnProperty,H0=Object.prototype.propertyIsEnumerable,j0=(r,t,e)=>t in r?iG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,aG=(r,t)=>{for(var e in t||(t={}))X0.call(t,e)&&j0(r,e,t[e]);if(na)for(var e of na(t))H0.call(t,e)&&j0(r,e,t[e]);return r},oG=(r,t)=>sG(r,nG(t)),lG=(r,t)=>{var e={};for(var i in r)X0.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&na)for(var i of na(r))t.indexOf(i)<0&&H0.call(r,i)&&(e[i]=r[i]);return e};class uG extends Nr{constructor(t){const e=t,{texture:i,vertices:s,uvs:n,indices:a,topology:o}=e,l=lG(e,["texture","vertices","uvs","indices","topology"]),u=new Le(ue({positions:s,uvs:n,indices:a,topology:o}));super(ue(oG(aG({},l),{texture:i,geometry:u}))),this.autoUpdate=!0,this.onRender=this._render}get vertices(){return this.geometry.getBuffer("aPosition").data}set vertices(t){this.geometry.getBuffer("aPosition").data=t}_render(){this.autoUpdate&&this.geometry.getBuffer("aPosition").update()}}function cG(r,t){const{width:e,height:i}=r.frame;return t.scale(1/e,1/i),t}class z0{execute(t,e){var i,s,n;const a=t.renderer,o=a.canvasContext.activeContext,l=e.particleChildren,u=e.texture;o.save(),a.canvasContext.setContextTransform(e.worldTransform,e.roundPixels),a.canvasContext.setBlendMode(e.groupBlendMode);const c=e.groupColorAlpha,h=(s=(i=a.filter)==null?void 0:i.alphaMultiplier)!=null?s:1,p=(c>>>24&255)/255*h;for(let f=0;f>>24&255)/255*p;if(b<=0)continue;const y=_&16777215,x=((y&255)<<16)+(y&65280)+(y>>16&255);let v=g.source.resource;x!==16777215&&(v=K.getTintedCanvas({texture:g},x));const w=g.frame,S=g.source.resolution,E=w.x*S,O=w.y*S,M=w.width*S,C=w.height*S;o.globalAlpha=b;const P=-m.anchorX*w.width,R=-m.anchorY*w.height;m.rotation!==0||m.scaleX!==1||m.scaleY!==1?(o.save(),o.translate(m.x,m.y),o.rotate(m.rotation),o.scale(m.scaleX,m.scaleY),o.drawImage(v,E,O,M,C,P,R,w.width,w.height),o.restore()):o.drawImage(v,E,O,M,C,m.x+P,m.y+R,w.width,w.height)}o.restore()}}function Yc(r,t=null){const e=r*6;if(e>65535?t||(t=new Uint32Array(e)):t||(t=new Uint16Array(e)),t.length!==e)throw new Error(`Out buffer length is incorrect, got ${t.length} and expected ${e}`);for(let i=0,s=0;ithis._size&&(e=!0,this._size=Math.max(t.length,this._size*1.5|0),this.staticAttributeBuffer=new er(this._size*this._staticStride*4*4),this.dynamicAttributeBuffer=new er(this._size*this._dynamicStride*4*4),this.indexBuffer=Yc(this._size),this.geometry.indexBuffer.setDataWithSize(this.indexBuffer,this.indexBuffer.byteLength,!0));const i=this.dynamicAttributeBuffer;if(this._dynamicUpload(t,i.float32View,i.uint32View),this._dynamicBuffer.setDataWithSize(this.dynamicAttributeBuffer.float32View,t.length*this._dynamicStride*4,!0),e){const s=this.staticAttributeBuffer;this._staticUpload(t,s.float32View,s.uint32View),this._staticBuffer.setDataWithSize(s.float32View,t.length*this._staticStride*4,!0)}}destroy(){this._staticBuffer.destroy(),this._dynamicBuffer.destroy(),this.geometry.destroy()}}function hG(r){const t=[];for(const e in r){const i=r[e];t.push(e,i.code,i.dynamic?"d":"s")}return t.join("_")}var K0=`varying vec2 vUV; +varying vec4 vColor; + +uniform sampler2D uTexture; + +void main(void){ + vec4 color = texture2D(uTexture, vUV) * vColor; + gl_FragColor = color; +}`,q0=`attribute vec2 aVertex; +attribute vec2 aUV; +attribute vec4 aColor; + +attribute vec2 aPosition; +attribute float aRotation; + +uniform mat3 uTranslationMatrix; +uniform float uRound; +uniform vec2 uResolution; +uniform vec4 uColor; + +varying vec2 vUV; +varying vec4 vColor; + +vec2 roundPixels(vec2 position, vec2 targetSize) +{ + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; +} + +void main(void){ + float cosRotation = cos(aRotation); + float sinRotation = sin(aRotation); + float x = aVertex.x * cosRotation - aVertex.y * sinRotation; + float y = aVertex.x * sinRotation + aVertex.y * cosRotation; + + vec2 v = vec2(x, y); + v = v + aPosition; + + gl_Position = vec4((uTranslationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0); + + if(uRound == 1.0) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + + vUV = aUV; + vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uColor; +} +`,Kc=` +struct ParticleUniforms { + uTranslationMatrix:mat3x3, + uColor:vec4, + uRound:f32, + uResolution:vec2, +}; + +fn roundPixels(position: vec2, targetSize: vec2) -> vec2 +{ + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; +} + +@group(0) @binding(0) var uniforms: ParticleUniforms; + +@group(1) @binding(0) var uTexture: texture_2d; +@group(1) @binding(1) var uSampler : sampler; + +struct VSOutput { + @builtin(position) position: vec4, + @location(0) uv : vec2, + @location(1) color : vec4, + }; +@vertex +fn mainVertex( + @location(0) aVertex: vec2, + @location(1) aPosition: vec2, + @location(2) aUV: vec2, + @location(3) aColor: vec4, + @location(4) aRotation: f32, +) -> VSOutput { + + let v = vec2( + aVertex.x * cos(aRotation) - aVertex.y * sin(aRotation), + aVertex.x * sin(aRotation) + aVertex.y * cos(aRotation) + ) + aPosition; + + var position = vec4((uniforms.uTranslationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0); + + if(uniforms.uRound == 1.0) { + position = vec4(roundPixels(position.xy, uniforms.uResolution), position.zw); + } + + let vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uniforms.uColor; + + return VSOutput( + position, + aUV, + vColor, + ); +} + +@fragment +fn mainFragment( + @location(0) uv: vec2, + @location(1) color: vec4, + @builtin(position) position: vec4, +) -> @location(0) vec4 { + + var sample = textureSample(uTexture, uSampler, uv) * color; + + return sample; +}`;class Z0 extends Zt{constructor(){const t=Ht.from({vertex:q0,fragment:K0}),e=kt.from({fragment:{source:Kc,entryPoint:"mainFragment"},vertex:{source:Kc,entryPoint:"mainVertex"}});super({glProgram:t,gpuProgram:e,resources:{uTexture:F.WHITE.source,uSampler:new Kt({}),uniforms:{uTranslationMatrix:{value:new D,type:"mat3x3"},uColor:{value:new J(16777215),type:"vec4"},uRound:{value:1,type:"f32"},uResolution:{value:[0,0],type:"vec2"}}}})}}class Xi{constructor(t,e){this.state=jt.for2d(),this.localUniforms=new wt({uTranslationMatrix:{value:new D,type:"mat3x3"},uColor:{value:new Float32Array(4),type:"vec4"},uRound:{value:1,type:"f32"},uResolution:{value:[0,0],type:"vec2"}}),this.renderer=t,this.adaptor=e,this.defaultShader=new Z0,this.state=jt.for2d(),this._managedContainers=new Ft({renderer:t,type:"renderable",name:"particleContainer"})}validateRenderable(t){return!1}addRenderable(t,e){this.renderer.renderPipes.batch.break(e),e.add(t)}getBuffers(t){return t._gpuData[this.renderer.uid]||this._initBuffer(t)}_initBuffer(t){return t._gpuData[this.renderer.uid]=new Y0({size:t.particleChildren.length,properties:t._properties}),this._managedContainers.add(t),t._gpuData[this.renderer.uid]}updateRenderable(t){}execute(t){const e=t.particleChildren;if(e.length===0)return;const i=this.renderer,s=this.getBuffers(t);t.texture||(t.texture=e[0].texture);const n=this.state;s.update(e,t._childrenDirty),t._childrenDirty=!1,n.blendMode=Pr(t.blendMode,t.texture._source);const a=this.localUniforms.uniforms,o=a.uTranslationMatrix;t.worldTransform.copyTo(o),o.prepend(i.globalUniforms.globalUniformData.projectionMatrix),a.uResolution=i.globalUniforms.globalUniformData.resolution,a.uRound=i._roundPixels|t._roundPixels,Lr(t.groupColorAlpha,a.uColor,0),this.adaptor.execute(this,t)}destroy(){this._managedContainers.destroy(),this.renderer=null,this.defaultShader&&(this.defaultShader.destroy(),this.defaultShader=null)}}Xi.extension={type:[T.CanvasPipes],name:"particle"};class qc extends Xi{constructor(t){super(t,new z0)}}qc.extension={type:[T.CanvasPipes],name:"particle"};class Q0{execute(t,e){const i=t.state,s=t.renderer,n=e.shader||t.defaultShader;n.resources.uTexture=e.texture._source,n.resources.uniforms=t.localUniforms;const a=s.gl,o=t.getBuffers(e);s.shader.bind(n),s.state.set(i),s.geometry.bind(o.geometry,n.glProgram);const l=o.geometry.indexBuffer.data.BYTES_PER_ELEMENT===2?a.UNSIGNED_SHORT:a.UNSIGNED_INT;a.drawElements(a.TRIANGLES,e.particleChildren.length*6,l,0)}}class Zc extends Xi{constructor(t){super(t,new Q0)}}Zc.extension={type:[T.WebGLPipes],name:"particle"};class J0{execute(t,e){const i=t.renderer,s=e.shader||t.defaultShader;s.groups[0]=i.renderPipes.uniformBatch.getUniformBindGroup(t.localUniforms,!0),s.groups[1]=i.texture.getTextureBindGroup(e.texture);const n=t.state,a=t.getBuffers(e);i.encoder.draw({geometry:a.geometry,shader:e.shader||t.defaultShader,state:n,size:e.particleChildren.length*6})}}class Qc extends Xi{constructor(t){super(t,new J0)}}Qc.extension={type:[T.WebGPUPipes],name:"particle"};var dG=Object.defineProperty,tT=Object.getOwnPropertySymbols,pG=Object.prototype.hasOwnProperty,fG=Object.prototype.propertyIsEnumerable,eT=(r,t,e)=>t in r?dG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,rT=(r,t)=>{for(var e in t||(t={}))pG.call(t,e)&&eT(r,e,t[e]);if(tT)for(var e of tT(t))fG.call(t,e)&&eT(r,e,t[e]);return r};const iT=class _h{constructor(t){if(t instanceof F)this.texture=t,ss(this,_h.defaultOptions,{});else{const e=rT(rT({},_h.defaultOptions),t);ss(this,e,{})}}get alpha(){return this._alpha}set alpha(t){this._alpha=Math.min(Math.max(t,0),1),this._updateColor()}get tint(){return be(this._tint)}set tint(t){this._tint=J.shared.setValue(t!=null?t:16777215).toBgrNumber(),this._updateColor()}_updateColor(){this.color=this._tint+((this._alpha*255|0)<<24)}};iT.defaultOptions={anchorX:0,anchorY:0,x:0,y:0,scaleX:1,scaleY:1,rotation:0,tint:16777215,alpha:1};let mG=iT;const Jc={vertex:{attributeName:"aVertex",format:"float32x2",code:` + const texture = p.texture; + const sx = p.scaleX; + const sy = p.scaleY; + const ax = p.anchorX; + const ay = p.anchorY; + const trim = texture.trim; + const orig = texture.orig; + + if (trim) + { + w1 = trim.x - (ax * orig.width); + w0 = w1 + trim.width; + + h1 = trim.y - (ay * orig.height); + h0 = h1 + trim.height; + } + else + { + w1 = -ax * (orig.width); + w0 = w1 + orig.width; + + h1 = -ay * (orig.height); + h0 = h1 + orig.height; + } + + f32v[offset] = w1 * sx; + f32v[offset + 1] = h1 * sy; + + f32v[offset + stride] = w0 * sx; + f32v[offset + stride + 1] = h1 * sy; + + f32v[offset + (stride * 2)] = w0 * sx; + f32v[offset + (stride * 2) + 1] = h0 * sy; + + f32v[offset + (stride * 3)] = w1 * sx; + f32v[offset + (stride * 3) + 1] = h0 * sy; + `,dynamic:!1},position:{attributeName:"aPosition",format:"float32x2",code:` + var x = p.x; + var y = p.y; + + f32v[offset] = x; + f32v[offset + 1] = y; + + f32v[offset + stride] = x; + f32v[offset + stride + 1] = y; + + f32v[offset + (stride * 2)] = x; + f32v[offset + (stride * 2) + 1] = y; + + f32v[offset + (stride * 3)] = x; + f32v[offset + (stride * 3) + 1] = y; + `,dynamic:!0},rotation:{attributeName:"aRotation",format:"float32",code:` + var rotation = p.rotation; + + f32v[offset] = rotation; + f32v[offset + stride] = rotation; + f32v[offset + (stride * 2)] = rotation; + f32v[offset + (stride * 3)] = rotation; + `,dynamic:!1},uvs:{attributeName:"aUV",format:"float32x2",code:` + var uvs = p.texture.uvs; + + f32v[offset] = uvs.x0; + f32v[offset + 1] = uvs.y0; + + f32v[offset + stride] = uvs.x1; + f32v[offset + stride + 1] = uvs.y1; + + f32v[offset + (stride * 2)] = uvs.x2; + f32v[offset + (stride * 2) + 1] = uvs.y2; + + f32v[offset + (stride * 3)] = uvs.x3; + f32v[offset + (stride * 3) + 1] = uvs.y3; + `,dynamic:!1},color:{attributeName:"aColor",format:"unorm8x4",code:` + const c = p.color; + + u32v[offset] = c; + u32v[offset + stride] = c; + u32v[offset + (stride * 2)] = c; + u32v[offset + (stride * 3)] = c; + `,dynamic:!1}};$.add(Zc),$.add(Qc),$.add(qc);var gG=Object.defineProperty,_G=Object.defineProperties,bG=Object.getOwnPropertyDescriptors,aa=Object.getOwnPropertySymbols,sT=Object.prototype.hasOwnProperty,nT=Object.prototype.propertyIsEnumerable,aT=(r,t,e)=>t in r?gG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Yr=(r,t)=>{for(var e in t||(t={}))sT.call(t,e)&&aT(r,e,t[e]);if(aa)for(var e of aa(t))nT.call(t,e)&&aT(r,e,t[e]);return r},oT=(r,t)=>_G(r,bG(t)),vG=(r,t)=>{var e={};for(var i in r)sT.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&aa)for(var i of aa(r))t.indexOf(i)<0&&nT.call(r,i)&&(e[i]=r[i]);return e};const yG=new Pt(0,0,0,0),lT=class bh extends xe{constructor(t={}){t=oT(Yr(Yr({},bh.defaultOptions),t),{dynamicProperties:Yr(Yr({},bh.defaultOptions.dynamicProperties),t==null?void 0:t.dynamicProperties)});const e=t,{dynamicProperties:i,shader:s,roundPixels:n,texture:a,particles:o}=e,l=vG(e,["dynamicProperties","shader","roundPixels","texture","particles"]);super(Yr({label:"ParticleContainer"},l)),this.renderPipeId="particle",this.batched=!1,this._childrenDirty=!1,this.texture=a||null,this.shader=s,this._properties={};for(const u in Jc){const c=Jc[u],h=i[u];this._properties[u]=oT(Yr({},c),{dynamic:h})}this.allowChildren=!0,this.roundPixels=n!=null?n:!1,this.particleChildren=o!=null?o:[]}addParticle(...t){for(let e=0;e-1&&(this.particleChildren.splice(s,1),e=!0)}return e&&this.onViewUpdate(),t[0]}update(){this._childrenDirty=!0}onViewUpdate(){this._childrenDirty=!0,super.onViewUpdate()}get bounds(){return yG}updateBounds(){}destroy(t=!1){var e,i,s;if(super.destroy(t),typeof t=="boolean"?t:t==null?void 0:t.texture){const n=typeof t=="boolean"?t:t==null?void 0:t.textureSource,a=(i=this.texture)!=null?i:(e=this.particleChildren[0])==null?void 0:e.texture;a&&a.destroy(n)}this.texture=null,(s=this.shader)==null||s.destroy()}removeParticles(t,e){t!=null||(t=0),e!=null||(e=this.particleChildren.length);const i=this.particleChildren.splice(t,e-t);return this.onViewUpdate(),i}removeParticleAt(t){const e=this.particleChildren.splice(t,1);return this.onViewUpdate(),e[0]}addParticleAt(t,e){return this.particleChildren.splice(e,0,t),this.onViewUpdate(),t}addChild(...t){throw new Error("ParticleContainer.addChild() is not available. Please use ParticleContainer.addParticle()")}removeChild(...t){throw new Error("ParticleContainer.removeChild() is not available. Please use ParticleContainer.removeParticle()")}removeChildren(t,e){throw new Error("ParticleContainer.removeChildren() is not available. Please use ParticleContainer.removeParticles()")}removeChildAt(t){throw new Error("ParticleContainer.removeChildAt() is not available. Please use ParticleContainer.removeParticleAt()")}getChildAt(t){throw new Error("ParticleContainer.getChildAt() is not available. Please use ParticleContainer.getParticleAt()")}setChildIndex(t,e){throw new Error("ParticleContainer.setChildIndex() is not available. Please use ParticleContainer.setParticleIndex()")}getChildIndex(t){throw new Error("ParticleContainer.getChildIndex() is not available. Please use ParticleContainer.getParticleIndex()")}addChildAt(t,e){throw new Error("ParticleContainer.addChildAt() is not available. Please use ParticleContainer.addParticleAt()")}swapChildren(t,e){throw new Error("ParticleContainer.swapChildren() is not available. Please use ParticleContainer.swapParticles()")}reparentChild(...t){throw new Error("ParticleContainer.reparentChild() is not available with the particle container")}reparentChildAt(t,e){throw new Error("ParticleContainer.reparentChildAt() is not available with the particle container")}};lT.defaultOptions={dynamicProperties:{vertex:!1,position:!0,rotation:!1,uvs:!1,color:!1},roundPixels:!1};let xG=lT;class th{constructor(t){this._renderer=t}validateRenderable(t){return!1}addRenderable(t,e){this._renderer.renderPipes.batch.break(e),e.add(t)}updateRenderable(t){}execute(t){var e,i,s,n,a,o;const l=this._renderer,u=l.canvasContext,c=u.activeContext;c.save();const h=t.groupTransform,p=l._roundPixels|t._roundPixels;u.setContextTransform(h,p===1),u.setBlendMode(t.groupBlendMode);const f=(i=(e=l.globalUniforms.globalUniformData)==null?void 0:e.worldColor)!=null?i:4294967295,m=t.groupColorAlpha,g=(f>>>24&255)/255,_=(m>>>24&255)/255,b=(n=(s=l.filter)==null?void 0:s.alphaMultiplier)!=null?n:1,y=g*_*b;if(y<=0){c.restore();return}c.globalAlpha=y;const x=f&16777215,v=m&16777215,w=be(Ce(v,x)),S=t.texture,E=K.getCanvasSource(S);if(!E){c.restore();return}const O=u.smoothProperty,M=S.source.style.scaleMode!=="nearest";c[O]!==M&&(c[O]=M);const C=w!==16777215||S.rotate!==0,P=C?K.getTintedCanvas({texture:S},w):E,{leftWidth:R,topHeight:G,rightWidth:I,bottomHeight:U,width:A,height:B}=t,Q=R+I,N=G+U,q=Math.min(Q>A?A/Q:1,N>B?B/N:1,1),k=R*q,j=I*q,X=G*q,W=U*q,Z=Math.max(0,A-k-j),ut=Math.max(0,B-X-W),It=t.anchor,at=(o=(a=S.source._resolution)!=null?a:S.source.resolution)!=null?o:1;let pt=S.frame.x*at,ft=S.frame.y*at;const _t=-It.x*A,ct=-It.y*B,mt=R*at,ot=G*at,z=I*at,At=U*at;let Et=S.frame.width*at,le=S.frame.height*at;C&&(pt=0,ft=0,Et=P.width,le=P.height),c.drawImage(P,pt,ft,mt,ot,_t,ct,k,X),c.drawImage(P,pt+mt,ft,Et-mt-z,ot,_t+k,ct,Z,X),c.drawImage(P,pt+Et-z,ft,z,ot,_t+A-j,ct,j,X),c.drawImage(P,pt,ft+ot,mt,le-ot-At,_t,ct+X,k,ut),c.drawImage(P,pt+mt,ft+ot,Et-mt-z,le-ot-At,_t+k,ct+X,Z,ut),c.drawImage(P,pt+Et-z,ft+ot,z,le-ot-At,_t+A-j,ct+X,j,ut),c.drawImage(P,pt,ft+le-At,mt,At,_t,ct+B-W,k,W),c.drawImage(P,pt+mt,ft+le-At,Et-mt-z,At,_t+k,ct+B-W,Z,W),c.drawImage(P,pt+Et-z,ft+le-At,z,At,_t+A-j,ct+B-W,j,W),c.restore()}destroy(){this._renderer=null}}th.extension={type:[T.CanvasPipes],name:"nineSliceSprite"};var TG=Object.defineProperty,uT=Object.getOwnPropertySymbols,SG=Object.prototype.hasOwnProperty,wG=Object.prototype.propertyIsEnumerable,cT=(r,t,e)=>t in r?TG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,hT=(r,t)=>{for(var e in t||(t={}))SG.call(t,e)&&cT(r,e,t[e]);if(uT)for(var e of uT(t))wG.call(t,e)&&cT(r,e,t[e]);return r};const dT=class S1 extends ta{constructor(t={}){t=hT(hT({},S1.defaultOptions),t),super({width:t.width,height:t.height,verticesX:4,verticesY:4}),this.update(t)}update(t){var e,i,s,n,a,o,l,u,c,h;this.width=(e=t.width)!=null?e:this.width,this.height=(i=t.height)!=null?i:this.height,this._originalWidth=(s=t.originalWidth)!=null?s:this._originalWidth,this._originalHeight=(n=t.originalHeight)!=null?n:this._originalHeight,this._leftWidth=(a=t.leftWidth)!=null?a:this._leftWidth,this._rightWidth=(o=t.rightWidth)!=null?o:this._rightWidth,this._topHeight=(l=t.topHeight)!=null?l:this._topHeight,this._bottomHeight=(u=t.bottomHeight)!=null?u:this._bottomHeight,this._anchorX=(c=t.anchor)==null?void 0:c.x,this._anchorY=(h=t.anchor)==null?void 0:h.y,this.updateUvs(),this.updatePositions()}updatePositions(){const t=this.positions,{width:e,height:i,_leftWidth:s,_rightWidth:n,_topHeight:a,_bottomHeight:o,_anchorX:l,_anchorY:u}=this,c=s+n,h=e>c?1:e/c,p=a+o,f=i>p?1:i/p,m=Math.min(h,f),g=l*e,_=u*i;t[0]=t[8]=t[16]=t[24]=-g,t[2]=t[10]=t[18]=t[26]=s*m-g,t[4]=t[12]=t[20]=t[28]=e-n*m-g,t[6]=t[14]=t[22]=t[30]=e-g,t[1]=t[3]=t[5]=t[7]=-_,t[9]=t[11]=t[13]=t[15]=a*m-_,t[17]=t[19]=t[21]=t[23]=i-o*m-_,t[25]=t[27]=t[29]=t[31]=i-_,this.getBuffer("aPosition").update()}updateUvs(){const t=this.uvs;t[0]=t[8]=t[16]=t[24]=0,t[1]=t[3]=t[5]=t[7]=0,t[6]=t[14]=t[22]=t[30]=1,t[25]=t[27]=t[29]=t[31]=1;const e=1/this._originalWidth,i=1/this._originalHeight;t[2]=t[10]=t[18]=t[26]=e*this._leftWidth,t[9]=t[11]=t[13]=t[15]=i*this._topHeight,t[4]=t[12]=t[20]=t[28]=1-e*this._rightWidth,t[17]=t[19]=t[21]=t[23]=1-i*this._bottomHeight,this.getBuffer("aUV").update()}};dT.defaultOptions={width:100,height:100,leftWidth:10,topHeight:10,rightWidth:10,bottomHeight:10,originalWidth:100,originalHeight:100};let He=dT;class pT extends an{constructor(){super(),this.geometry=new He}destroy(){this.geometry.destroy()}}class eh{constructor(t){this._renderer=t,this._managedSprites=new Ft({renderer:t,type:"renderable",name:"nineSliceSprite"})}addRenderable(t,e){const i=this._getGpuSprite(t);t.didViewUpdate&&this._updateBatchableSprite(t,i),this._renderer.renderPipes.batch.addToBatch(i,e)}updateRenderable(t){const e=this._getGpuSprite(t);t.didViewUpdate&&this._updateBatchableSprite(t,e),e._batcher.updateElement(e)}validateRenderable(t){const e=this._getGpuSprite(t);return!e._batcher.checkAndUpdateTexture(e,t._texture)}_updateBatchableSprite(t,e){e.geometry.update(t),e.setTexture(t._texture)}_getGpuSprite(t){return t._gpuData[this._renderer.uid]||this._initGPUSprite(t)}_initGPUSprite(t){const e=t._gpuData[this._renderer.uid]=new pT,i=e;return i.renderable=t,i.transform=t.groupTransform,i.texture=t._texture,i.roundPixels=this._renderer._roundPixels|t._roundPixels,this._managedSprites.add(t),t.didViewUpdate||this._updateBatchableSprite(t,i),e}destroy(){this._managedSprites.destroy(),this._renderer=null}}eh.extension={type:[T.WebGLPipes,T.WebGPUPipes],name:"nineSliceSprite"},$.add(th),$.add(eh);var EG=Object.defineProperty,oa=Object.getOwnPropertySymbols,fT=Object.prototype.hasOwnProperty,mT=Object.prototype.propertyIsEnumerable,gT=(r,t,e)=>t in r?EG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,PG=(r,t)=>{for(var e in t||(t={}))fT.call(t,e)&&gT(r,e,t[e]);if(oa)for(var e of oa(t))mT.call(t,e)&&gT(r,e,t[e]);return r},AG=(r,t)=>{var e={};for(var i in r)fT.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&oa)for(var i of oa(r))t.indexOf(i)<0&&mT.call(r,i)&&(e[i]=r[i]);return e};const _T=class w1 extends xe{constructor(t){var e,i,s,n,a,o,l,u,c,h;t instanceof F&&(t={texture:t});const p=t,{width:f,height:m,anchor:g,leftWidth:_,rightWidth:b,topHeight:y,bottomHeight:x,texture:v,roundPixels:w}=p,S=AG(p,["width","height","anchor","leftWidth","rightWidth","topHeight","bottomHeight","texture","roundPixels"]);super(PG({label:"NineSliceSprite"},S)),this.renderPipeId="nineSliceSprite",this.batched=!0,this._leftWidth=(i=_!=null?_:(e=v==null?void 0:v.defaultBorders)==null?void 0:e.left)!=null?i:He.defaultOptions.leftWidth,this._topHeight=(n=y!=null?y:(s=v==null?void 0:v.defaultBorders)==null?void 0:s.top)!=null?n:He.defaultOptions.topHeight,this._rightWidth=(o=b!=null?b:(a=v==null?void 0:v.defaultBorders)==null?void 0:a.right)!=null?o:He.defaultOptions.rightWidth,this._bottomHeight=(u=x!=null?x:(l=v==null?void 0:v.defaultBorders)==null?void 0:l.bottom)!=null?u:He.defaultOptions.bottomHeight,this._width=(c=f!=null?f:v.width)!=null?c:He.defaultOptions.width,this._height=(h=m!=null?m:v.height)!=null?h:He.defaultOptions.height,this.allowChildren=!1,this.texture=v!=null?v:w1.defaultOptions.texture,this.roundPixels=w!=null?w:!1,this._anchor=new gt({_onUpdate:()=>{this.onViewUpdate()}}),g?this.anchor=g:this.texture.defaultAnchor&&(this.anchor=this.texture.defaultAnchor)}get anchor(){return this._anchor}set anchor(t){typeof t=="number"?this._anchor.set(t):this._anchor.copyFrom(t)}get width(){return this._width}set width(t){this._width=t,this.onViewUpdate()}get height(){return this._height}set height(t){this._height=t,this.onViewUpdate()}setSize(t,e){var i;typeof t=="object"&&(e=(i=t.height)!=null?i:t.width,t=t.width),this._width=t,this._height=e!=null?e:t,this.onViewUpdate()}getSize(t){return t||(t={}),t.width=this._width,t.height=this._height,t}get leftWidth(){return this._leftWidth}set leftWidth(t){this._leftWidth=t,this.onViewUpdate()}get topHeight(){return this._topHeight}set topHeight(t){this._topHeight=t,this.onViewUpdate()}get rightWidth(){return this._rightWidth}set rightWidth(t){this._rightWidth=t,this.onViewUpdate()}get bottomHeight(){return this._bottomHeight}set bottomHeight(t){this._bottomHeight=t,this.onViewUpdate()}get texture(){return this._texture}set texture(t){t||(t=F.EMPTY);const e=this._texture;e!==t&&(e&&e.dynamic&&e.off("update",this.onViewUpdate,this),t.dynamic&&t.on("update",this.onViewUpdate,this),this._texture=t,this.onViewUpdate())}get originalWidth(){return this._texture.width}get originalHeight(){return this._texture.height}destroy(t){if(super.destroy(t),typeof t=="boolean"?t:t==null?void 0:t.texture){const e=typeof t=="boolean"?t:t==null?void 0:t.textureSource;this._texture.destroy(e)}this._texture=null}updateBounds(){const t=this._bounds,e=this._anchor,i=this._width,s=this._height;t.minX=-e._x*i,t.maxX=t.minX+i,t.minY=-e._y*s,t.maxY=t.minY+s}};_T.defaultOptions={texture:F.EMPTY};let bT=_T;class CG extends bT{constructor(...t){let e=t[0];e instanceof F&&(e={texture:e,leftWidth:t[1],topHeight:t[2],rightWidth:t[3],bottomHeight:t[4]}),super(e)}}class vT extends eu{constructor(t,e){var i;super();const{textures:s,data:n}=t;Object.keys(n.pages).forEach(a=>{const o=n.pages[parseInt(a,10)],l=s[o.id];this.pages.push({texture:l})}),Object.keys(n.chars).forEach(a=>{var o;const l=n.chars[a],{frame:u,source:c,rotate:h}=s[l.page],p=H.transformRectCoords(l,u,h,new it),f=new F({frame:p,orig:new it(0,0,l.width,l.height),source:c,rotate:h});this.chars[a]={id:a.codePointAt(0),xOffset:l.xOffset,yOffset:l.yOffset,xAdvance:l.xAdvance,kerning:(o=l.kerning)!=null?o:{},texture:f}}),this.baseRenderedFontSize=n.fontSize,this.baseMeasurementFontSize=n.fontSize,this.fontMetrics={ascent:0,descent:0,fontSize:n.fontSize},this.baseLineOffset=n.baseLineOffset,this.lineHeight=n.lineHeight,this.fontFamily=n.fontFamily,this.distanceField=(i=n.distanceField)!=null?i:{type:"none",range:0},this.url=e}destroy(){super.destroy();for(let t=0;t0?(S=i.shift(),S.text=x,S.style=s,S.label=`char-${x}`,S.x=m.charPositions[y]*l-m.charPositions[b]*l):S=new uu({text:x,style:s,label:`char-${x}`,x:m.charPositions[y]*l-m.charPositions[b]*l}),v||(u.push(S),_.addChild(S)),(v||w)&&_.children.length>0&&(_.x=m.charPositions[b]*l,c.push(_),g.addChild(_),_=new dt({label:"word"}),b=y+1)}f+=p}return{chars:u,lines:h,words:c}}var xT=Object.getOwnPropertySymbols,MG=Object.prototype.hasOwnProperty,OG=Object.prototype.propertyIsEnumerable,GG=(r,t)=>{var e={};for(var i in r)MG.call(r,i)&&t.indexOf(i)<0&&(e[i]=r[i]);if(r!=null&&xT)for(var i of xT(r))t.indexOf(i)<0&&OG.call(r,i)&&(e[i]=r[i]);return e};class rh extends dt{constructor(t){const e=t,{text:i,style:s,autoSplit:n,lineAnchor:a,wordAnchor:o,charAnchor:l}=e,u=GG(e,["text","style","autoSplit","lineAnchor","wordAnchor","charAnchor"]);super(u),this._dirty=!1,this._canReuseChars=!1,this.chars=[],this.words=[],this.lines=[],this._originalText=i,this._autoSplit=n,this._lineAnchor=a,this._wordAnchor=o,this._charAnchor=l,this.style=s}split(){const t=this.splitFn();this.chars=t.chars,this.words=t.words,this.lines=t.lines,this.addChild(...this.lines),this.charAnchor=this._charAnchor,this.wordAnchor=this._wordAnchor,this.lineAnchor=this._lineAnchor,this._dirty=!1,this._canReuseChars=!0}get text(){return this._originalText}set text(t){this._originalText=t,this.lines.forEach(e=>e.destroy({children:!0})),this.lines.length=0,this.words.length=0,this.chars.length=0,this._canReuseChars=!1,this.onTextUpdate()}_setOrigin(t,e,i){let s;typeof t=="number"?s={x:t,y:t}:s={x:t.x,y:t.y},e.forEach(n=>{const a=n.getLocalBounds(),o=a.minX+a.width*s.x,l=a.minY+a.height*s.y;n.origin.set(o,l)}),this[i]=t}get lineAnchor(){return this._lineAnchor}set lineAnchor(t){this._setOrigin(t,this.lines,"_lineAnchor")}get wordAnchor(){return this._wordAnchor}set wordAnchor(t){this._setOrigin(t,this.words,"_wordAnchor")}get charAnchor(){return this._charAnchor}set charAnchor(t){this._setOrigin(t,this.chars,"_charAnchor")}get style(){return this._style}set style(t){t||(t={}),this._style=new Lt(t),this.styleChanged()}styleChanged(){this.words.forEach(t=>t.destroy()),this.words.length=0,this.lines.forEach(t=>t.destroy()),this.lines.length=0,this._canReuseChars=!0,this.onTextUpdate()}onTextUpdate(){this._dirty=!0,this._autoSplit&&this.split()}destroy(t){super.destroy(t),this.chars=[],this.words=[],this.lines=[],(typeof t=="boolean"?t:t!=null&&t.style)&&this._style.destroy(t),this._style=null,this._originalText=""}}var IG=Object.defineProperty,BG=Object.defineProperties,FG=Object.getOwnPropertyDescriptors,TT=Object.getOwnPropertySymbols,DG=Object.prototype.hasOwnProperty,UG=Object.prototype.propertyIsEnumerable,ST=(r,t,e)=>t in r?IG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,Hi=(r,t)=>{for(var e in t||(t={}))DG.call(t,e)&&ST(r,e,t[e]);if(TT)for(var e of TT(t))UG.call(t,e)&&ST(r,e,t[e]);return r},kG=(r,t)=>BG(r,FG(t));const wT=class pa extends rh{constructor(t){var e,i,s;const n=Hi(Hi({},pa.defaultOptions),t);(e=n.style)!=null||(n.style={}),(s=(i=n.style).fill)!=null||(i.fill=16777215),super(n)}static from(t,e){const i=kG(Hi(Hi({},pa.defaultOptions),e),{text:t.text,style:new Lt(t.style)}),s=new pa(Hi({},i)),n=t.anchor;return(n.x!==0||n.y!==0)&&s.pivot.set(s.width*n.x,s.height*n.y),s}splitFn(){return yT({text:this._originalText,style:this._style,chars:this._canReuseChars?this.chars:[]})}};wT.defaultOptions={autoSplit:!0,lineAnchor:0,wordAnchor:0,charAnchor:0};let $G=wT;function LG(r,t,e){switch(r){case"center":return(e-t)/2;case"right":return e-t;default:return 0}}function ET(r){return r==="\r"||r===` +`||r===`\r +`}function NG(r,t,e){const i=[];let s=t.lines[0],n="",a=[],o=0;return e.wordWrap=!1,r.forEach(l=>{const u=/^\s*$/.test(l),c=ET(l),h=n.length===0&&u;if(u&&!c&&h)return;c||(n+=l);const p=Ot.measureText(l,e);a.push({char:l,metric:p}),n.length>=s.length&&(i.push({line:n,chars:a,width:a.reduce((f,m)=>f+m.metric.width,0)}),a=[],n="",o++,s=t.lines[o])}),i}function PT(r){var t,e,i,s;const{text:n,style:a,chars:o}=r,l=a,u=Ot.measureText(n,l),c=Ot.graphemeSegmenter(n),h=NG(c,u,l.clone()),p=l.align,f=u.lineWidths.reduce((A,B)=>Math.max(A,B),0),m=u.lines.length!==1&&l.wordWrap?l.wordWrapWidth:f,g=(t=l._fill)==null?void 0:t.fill,_=(e=l._stroke)==null?void 0:e.fill,b=g instanceof Qt,y=_ instanceof Qt,x=b||y,v=b&&g.textureSpace==="local"||y&&_.textureSpace==="local",w=u.width,S=u.height,E=l.clone();E.align="left";let O=0,M=0;if(E.trim){const{frame:A,canvasAndContext:B}=Ie.getCanvasAndContext({text:n,style:l,resolution:1});Ie.returnCanvasAndContext(B),O=-A.x,M=-A.y,E.trim=!1}const C=[],P=[],R=[];let G=0;const I=((i=l._stroke)==null?void 0:i.width)||0,U=((s=l.dropShadow)==null?void 0:s.distance)||0;return h.forEach((A,B)=>{const Q=new dt({label:`line-${B}`});Q.y=G+M,P.push(Q);const N=u.lineWidths[B];let q=LG(p,N,m),k=new dt({label:"word"});k.x=q+O,A.chars.forEach((j,X)=>{if(j.metric.width!==0){if(ET(j.char)){q+=j.metric.width-I;return}if(j.char===" ")k.children.length>0&&(R.push(k),Q.addChild(k)),q+=j.metric.width+l.letterSpacing-I,k=new dt({label:"word"}),k.x=q+O;else{let W=E;x&&(W=E.clone(),W._gradientOffset={x:-q,y:-G},v&&(W._gradientBounds={width:w,height:S}));let Z;o.length>0?(Z=o.shift(),Z.text=j.char,Z.style=W,Z.setFromMatrix(D.IDENTITY),Z.x=q-k.x+O-U*X):Z=new xn({text:j.char,style:W,x:q-k.x+O-U*X}),C.push(Z),k.addChild(Z),q+=j.metric.width+l.letterSpacing-I}}}),k.children.length>0&&(R.push(k),Q.addChild(k)),G+=u.lineHeight}),{chars:C,lines:P,words:R}}var XG=Object.defineProperty,HG=Object.defineProperties,jG=Object.getOwnPropertyDescriptors,AT=Object.getOwnPropertySymbols,zG=Object.prototype.hasOwnProperty,WG=Object.prototype.propertyIsEnumerable,CT=(r,t,e)=>t in r?XG(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,ji=(r,t)=>{for(var e in t||(t={}))zG.call(t,e)&&CT(r,e,t[e]);if(AT)for(var e of AT(t))WG.call(t,e)&&CT(r,e,t[e]);return r},VG=(r,t)=>HG(r,jG(t));const RT=class fa extends rh{constructor(t){const e=ji(ji({},fa.defaultOptions),t);super(e)}static from(t,e){const i=VG(ji(ji({},fa.defaultOptions),e),{text:t.text,style:new Lt(t.style)}),s=new fa(ji({},i)),n=t.anchor;return(n.x!==0||n.y!==0)&&s.pivot.set(s.width*n.x,s.height*n.y),s}splitFn(){return PT({text:this._originalText,style:this._style,chars:this._canReuseChars?this.chars:[]})}};RT.defaultOptions={autoSplit:!0,lineAnchor:0,wordAnchor:0,charAnchor:0};let YG=RT;const MT=["align","breakWords","cssOverrides","fontVariant","fontWeight","leading","letterSpacing","lineHeight","padding","textBaseline","trim","whiteSpace","wordWrap","wordWrapWidth","fontFamily","fontStyle","fontSize"];function KG(r){const t=[];let e=0;for(let i=0;it in r?tI(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e,BT=(r,t)=>{for(var e in t||(t={}))iI.call(t,e)&&IT(r,e,t[e]);if(GT)for(var e of GT(t))sI.call(t,e)&&IT(r,e,t[e]);return r},nI=(r,t)=>eI(r,rI(t));const aI=["#000080","#228B22","#8B0000","#4169E1","#008080","#800000","#9400D3","#FF8C00","#556B2F","#8B008B"];let oI=0;function FT(r,t=0,e={color:"#000000"}){r.renderGroup&&(e.color=aI[oI++]);let i="";for(let a=0;a key in obj ? __defProp$1m(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1m = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1o.call(b, prop)) + __defNormalProp$1m(a, prop, b[prop]); + if (__getOwnPropSymbols$1o) + for (var prop of __getOwnPropSymbols$1o(b)) { + if (__propIsEnum$1o.call(b, prop)) + __defNormalProp$1m(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$y = (a, b) => __defProps$y(a, __getOwnPropDescs$y(b)); + var ExtensionType = /* @__PURE__ */ ((ExtensionType2) => { + ExtensionType2["Application"] = "application"; + ExtensionType2["WebGLPipes"] = "webgl-pipes"; + ExtensionType2["WebGLPipesAdaptor"] = "webgl-pipes-adaptor"; + ExtensionType2["WebGLSystem"] = "webgl-system"; + ExtensionType2["WebGPUPipes"] = "webgpu-pipes"; + ExtensionType2["WebGPUPipesAdaptor"] = "webgpu-pipes-adaptor"; + ExtensionType2["WebGPUSystem"] = "webgpu-system"; + ExtensionType2["CanvasSystem"] = "canvas-system"; + ExtensionType2["CanvasPipesAdaptor"] = "canvas-pipes-adaptor"; + ExtensionType2["CanvasPipes"] = "canvas-pipes"; + ExtensionType2["Asset"] = "asset"; + ExtensionType2["LoadParser"] = "load-parser"; + ExtensionType2["ResolveParser"] = "resolve-parser"; + ExtensionType2["CacheParser"] = "cache-parser"; + ExtensionType2["DetectionParser"] = "detection-parser"; + ExtensionType2["MaskEffect"] = "mask-effect"; + ExtensionType2["BlendMode"] = "blend-mode"; + ExtensionType2["TextureSource"] = "texture-source"; + ExtensionType2["Environment"] = "environment"; + ExtensionType2["ShapeBuilder"] = "shape-builder"; + ExtensionType2["Batcher"] = "batcher"; + return ExtensionType2; + })(ExtensionType || {}); + const normalizeExtension = (ext) => { + if (typeof ext === "function" || typeof ext === "object" && ext.extension) { + if (!ext.extension) { + throw new Error("Extension class must have an extension object"); + } + const metadata = typeof ext.extension !== "object" ? { type: ext.extension } : ext.extension; + ext = __spreadProps$y(__spreadValues$1m({}, metadata), { ref: ext }); + } + if (typeof ext === "object") { + ext = __spreadValues$1m({}, ext); + } else { + throw new Error("Invalid extension type"); + } + if (typeof ext.type === "string") { + ext.type = [ext.type]; + } + return ext; + }; + const normalizeExtensionPriority = (ext, defaultPriority) => { + var _a; + return (_a = normalizeExtension(ext).priority) != null ? _a : defaultPriority; + }; + const extensions = { + /** @ignore */ + _addHandlers: {}, + /** @ignore */ + _removeHandlers: {}, + /** @ignore */ + _queue: {}, + /** + * Remove extensions from PixiJS. + * @param extensions - Extensions to be removed. Can be: + * - Extension class with static `extension` property + * - Extension format object with `type` and `ref` + * - Multiple extensions as separate arguments + * @returns {extensions} this for chaining + * @example + * ```ts + * // Remove a single extension + * extensions.remove(MyRendererPlugin); + * + * // Remove multiple extensions + * extensions.remove( + * MyRendererPlugin, + * MySystemPlugin + * ); + * ``` + * @see {@link ExtensionType} For available extension types + * @see {@link ExtensionFormat} For extension format details + */ + remove(...extensions2) { + extensions2.map(normalizeExtension).forEach((ext) => { + ext.type.forEach((type) => { + var _a, _b; + return (_b = (_a = this._removeHandlers)[type]) == null ? void 0 : _b.call(_a, ext); + }); + }); + return this; + }, + /** + * Register new extensions with PixiJS. Extensions can be registered in multiple formats: + * - As a class with a static `extension` property + * - As an extension format object + * - As multiple extensions passed as separate arguments + * @param extensions - Extensions to add to PixiJS. Each can be: + * - A class with static `extension` property + * - An extension format object with `type` and `ref` + * - Multiple extensions as separate arguments + * @returns This extensions instance for chaining + * @example + * ```ts + * // Register a simple extension + * extensions.add(MyRendererPlugin); + * + * // Register multiple extensions + * extensions.add( + * MyRendererPlugin, + * MySystemPlugin, + * }); + * ``` + * @see {@link ExtensionType} For available extension types + * @see {@link ExtensionFormat} For extension format details + * @see {@link extensions.remove} For removing registered extensions + */ + add(...extensions2) { + extensions2.map(normalizeExtension).forEach((ext) => { + ext.type.forEach((type) => { + var _a, _b; + const handlers = this._addHandlers; + const queue = this._queue; + if (!handlers[type]) { + queue[type] = queue[type] || []; + (_a = queue[type]) == null ? void 0 : _a.push(ext); + } else { + (_b = handlers[type]) == null ? void 0 : _b.call(handlers, ext); + } + }); + }); + return this; + }, + /** + * Internal method to handle extensions by name. + * @param type - The extension type. + * @param onAdd - Function handler when extensions are added/registered {@link StrictExtensionFormat}. + * @param onRemove - Function handler when extensions are removed/unregistered {@link StrictExtensionFormat}. + * @returns this for chaining. + * @internal + * @ignore + */ + handle(type, onAdd, onRemove) { + var _a; + const addHandlers = this._addHandlers; + const removeHandlers = this._removeHandlers; + if (addHandlers[type] || removeHandlers[type]) { + throw new Error(`Extension type ${type} already has a handler`); + } + addHandlers[type] = onAdd; + removeHandlers[type] = onRemove; + const queue = this._queue; + if (queue[type]) { + (_a = queue[type]) == null ? void 0 : _a.forEach((ext) => onAdd(ext)); + delete queue[type]; + } + return this; + }, + /** + * Handle a type, but using a map by `name` property. + * @param type - Type of extension to handle. + * @param map - The object map of named extensions. + * @returns this for chaining. + * @ignore + */ + handleByMap(type, map) { + return this.handle( + type, + (extension) => { + if (extension.name) { + map[extension.name] = extension.ref; + } + }, + (extension) => { + if (extension.name) { + delete map[extension.name]; + } + } + ); + }, + /** + * Handle a type, but using a list of extensions with a `name` property. + * @param type - Type of extension to handle. + * @param map - The array of named extensions. + * @param defaultPriority - Fallback priority if none is defined. + * @returns this for chaining. + * @ignore + */ + handleByNamedList(type, map, defaultPriority = -1) { + return this.handle( + type, + (extension) => { + const index = map.findIndex((item) => item.name === extension.name); + if (index >= 0) return; + map.push({ name: extension.name, value: extension.ref }); + map.sort((a, b) => normalizeExtensionPriority(b.value, defaultPriority) - normalizeExtensionPriority(a.value, defaultPriority)); + }, + (extension) => { + const index = map.findIndex((item) => item.name === extension.name); + if (index !== -1) { + map.splice(index, 1); + } + } + ); + }, + /** + * Handle a type, but using a list of extensions. + * @param type - Type of extension to handle. + * @param list - The list of extensions. + * @param defaultPriority - The default priority to use if none is specified. + * @returns this for chaining. + * @ignore + */ + handleByList(type, list, defaultPriority = -1) { + return this.handle( + type, + (extension) => { + if (list.includes(extension.ref)) { + return; + } + list.push(extension.ref); + list.sort((a, b) => normalizeExtensionPriority(b, defaultPriority) - normalizeExtensionPriority(a, defaultPriority)); + }, + (extension) => { + const index = list.indexOf(extension.ref); + if (index !== -1) { + list.splice(index, 1); + } + } + ); + }, + /** + * Mixin the source object(s) properties into the target class's prototype. + * Copies all property descriptors from source objects to the target's prototype. + * @param Target - The target class to mix properties into + * @param sources - One or more source objects containing properties to mix in + * @example + * ```ts + * // Create a mixin with shared properties + * const moveable = { + * x: 0, + * y: 0, + * move(x: number, y: number) { + * this.x += x; + * this.y += y; + * } + * }; + * + * // Create a mixin with computed properties + * const scalable = { + * scale: 1, + * get scaled() { + * return this.scale > 1; + * } + * }; + * + * // Apply mixins to a class + * extensions.mixin(Sprite, moveable, scalable); + * + * // Use mixed-in properties + * const sprite = new Sprite(); + * sprite.move(10, 20); + * console.log(sprite.x, sprite.y); // 10, 20 + * ``` + * @remarks + * - Copies all properties including getters/setters + * - Does not modify source objects + * - Preserves property descriptors + * @see {@link Object.defineProperties} For details on property descriptors + * @see {@link Object.getOwnPropertyDescriptors} For details on property copying + */ + mixin(Target, ...sources) { + for (const source of sources) { + Object.defineProperties(Target.prototype, Object.getOwnPropertyDescriptors(source)); + } + } + }; + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function getDefaultExportFromNamespaceIfPresent (n) { + return n && Object.prototype.hasOwnProperty.call(n, 'default') ? n['default'] : n; + } + + function getDefaultExportFromNamespaceIfNotNamed (n) { + return n && Object.prototype.hasOwnProperty.call(n, 'default') && Object.keys(n).length === 1 ? n['default'] : n; + } + + function getAugmentedNamespace(n) { + if (Object.prototype.hasOwnProperty.call(n, '__esModule')) return n; + var f = n.default; + if (typeof f == "function") { + var a = function a () { + var isInstance = false; + try { + isInstance = this instanceof a; + } catch {} + if (isInstance) { + return Reflect.construct(f, arguments, this.constructor); + } + return f.apply(this, arguments); + }; + a.prototype = f.prototype; + } else a = {}; + Object.defineProperty(a, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; + } + + var eventemitter3$1 = {exports: {}}; + + var eventemitter3 = eventemitter3$1.exports; + + var hasRequiredEventemitter3; + + function requireEventemitter3 () { + if (hasRequiredEventemitter3) return eventemitter3$1.exports; + hasRequiredEventemitter3 = 1; + (function (module) { + 'use strict'; + + var has = Object.prototype.hasOwnProperty + , prefix = '~'; + + /** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @private + */ + function Events() {} + + // + // We try to not inherit from `Object.prototype`. In some engines creating an + // instance in this way is faster than calling `Object.create(null)` directly. + // If `Object.create(null)` is not supported we prefix the event names with a + // character to make sure that the built-in object properties are not + // overridden or used as an attack vector. + // + if (Object.create) { + Events.prototype = Object.create(null); + + // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + if (!new Events().__proto__) prefix = false; + } + + /** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @private + */ + function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; + } + + /** + * Add a listener for a given event. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} once Specify if the listener is a one-time listener. + * @returns {EventEmitter} + * @private + */ + function addListener(emitter, event, fn, context, once) { + if (typeof fn !== 'function') { + throw new TypeError('The listener must be a function'); + } + + var listener = new EE(fn, context || emitter, once) + , evt = prefix ? prefix + event : event; + + if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; + else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); + else emitter._events[evt] = [emitter._events[evt], listener]; + + return emitter; + } + + /** + * Clear event by name. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} evt The Event name. + * @private + */ + function clearEvent(emitter, evt) { + if (--emitter._eventsCount === 0) emitter._events = new Events(); + else delete emitter._events[evt]; + } + + /** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @public + */ + function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; + } + + /** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @public + */ + EventEmitter.prototype.eventNames = function eventNames() { + var names = [] + , events + , name; + + if (this._eventsCount === 0) return names; + + for (name in (events = this._events)) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); + } + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); + } + + return names; + }; + + /** + * Return the listeners registered for a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Array} The registered listeners. + * @public + */ + EventEmitter.prototype.listeners = function listeners(event) { + var evt = prefix ? prefix + event : event + , handlers = this._events[evt]; + + if (!handlers) return []; + if (handlers.fn) return [handlers.fn]; + + for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { + ee[i] = handlers[i].fn; + } + + return ee; + }; + + /** + * Return the number of listeners listening to a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Number} The number of listeners. + * @public + */ + EventEmitter.prototype.listenerCount = function listenerCount(event) { + var evt = prefix ? prefix + event : event + , listeners = this._events[evt]; + + if (!listeners) return 0; + if (listeners.fn) return 1; + return listeners.length; + }; + + /** + * Calls each of the listeners registered for a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @public + */ + EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return false; + + var listeners = this._events[evt] + , len = arguments.length + , args + , i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: return listeners.fn.call(listeners.context), true; + case 2: return listeners.fn.call(listeners.context, a1), true; + case 3: return listeners.fn.call(listeners.context, a1, a2), true; + case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; + case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len -1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length + , j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: listeners[i].fn.call(listeners[i].context); break; + case 2: listeners[i].fn.call(listeners[i].context, a1); break; + case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; + case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; + default: + if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { + args[j - 1] = arguments[j]; + } + + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; + }; + + /** + * Add a listener for a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.on = function on(event, fn, context) { + return addListener(this, event, fn, context, false); + }; + + /** + * Add a one-time listener for a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.once = function once(event, fn, context) { + return addListener(this, event, fn, context, true); + }; + + /** + * Remove the listeners of a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {*} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return this; + if (!fn) { + clearEvent(this, evt); + return this; + } + + var listeners = this._events[evt]; + + if (listeners.fn) { + if ( + listeners.fn === fn && + (!once || listeners.once) && + (!context || listeners.context === context) + ) { + clearEvent(this, evt); + } + } else { + for (var i = 0, events = [], length = listeners.length; i < length; i++) { + if ( + listeners[i].fn !== fn || + (once && !listeners[i].once) || + (context && listeners[i].context !== context) + ) { + events.push(listeners[i]); + } + } + + // + // Reset the array, or remove it completely if we have no more listeners. + // + if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; + else clearEvent(this, evt); + } + + return this; + }; + + /** + * Remove all listeners, or those of the specified event. + * + * @param {(String|Symbol)} [event] The event name. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { + var evt; + + if (event) { + evt = prefix ? prefix + event : event; + if (this._events[evt]) clearEvent(this, evt); + } else { + this._events = new Events(); + this._eventsCount = 0; + } + + return this; + }; + + // + // Alias methods names because people roll like that. + // + EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + // + // Expose the prefix. + // + EventEmitter.prefixed = prefix; + + // + // Allow `EventEmitter` to be imported as module namespace. + // + EventEmitter.EventEmitter = EventEmitter; + + // + // Expose the module. + // + if ('undefined' !== 'object') { + module.exports = EventEmitter; + } + } (eventemitter3$1)); + return eventemitter3$1.exports; + } + + var eventemitter3Exports = requireEventemitter3(); + var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports); + + 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()})}; + + function namesPlugin(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 key in obj ? __defProp$1l(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1l = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1n.call(b, prop)) + __defNormalProp$1l(a, prop, b[prop]); + if (__getOwnPropSymbols$1n) + for (var prop of __getOwnPropSymbols$1n(b)) { + if (__propIsEnum$1n.call(b, prop)) + __defNormalProp$1l(a, prop, b[prop]); + } + return a; + }; + k([namesPlugin]); + const _Color = class _Color { + /** + * @param {ColorSource} value - Optional value to use, if not provided, white is used. + */ + constructor(value = 16777215) { + this._value = null; + this._components = new Float32Array(4); + this._components.fill(1); + this._int = 16777215; + this.value = value; + } + /** + * Get the red component of the color, normalized between 0 and 1. + * @example + * ```ts + * const color = new Color('red'); + * console.log(color.red); // 1 + * + * const green = new Color('#00ff00'); + * console.log(green.red); // 0 + * ``` + */ + get red() { + return this._components[0]; + } + /** + * Get the green component of the color, normalized between 0 and 1. + * @example + * ```ts + * const color = new Color('lime'); + * console.log(color.green); // 1 + * + * const red = new Color('#ff0000'); + * console.log(red.green); // 0 + * ``` + */ + get green() { + return this._components[1]; + } + /** + * Get the blue component of the color, normalized between 0 and 1. + * @example + * ```ts + * const color = new Color('blue'); + * console.log(color.blue); // 1 + * + * const yellow = new Color('#ffff00'); + * console.log(yellow.blue); // 0 + * ``` + */ + get blue() { + return this._components[2]; + } + /** + * Get the alpha component of the color, normalized between 0 and 1. + * @example + * ```ts + * const color = new Color('red'); + * console.log(color.alpha); // 1 (fully opaque) + * + * const transparent = new Color('rgba(255, 0, 0, 0.5)'); + * console.log(transparent.alpha); // 0.5 (semi-transparent) + * ``` + */ + get alpha() { + return this._components[3]; + } + /** + * Sets the color value and returns the instance for chaining. + * + * This is a chainable version of setting the `value` property. + * @param value - The color to set. Accepts various formats: + * - Hex strings/numbers (e.g., '#ff0000', 0xff0000) + * - RGB/RGBA values (arrays, objects) + * - CSS color names + * - HSL/HSLA values + * - HSV/HSVA values + * @returns The Color instance for chaining + * @example + * ```ts + * // Basic usage + * const color = new Color(); + * color.setValue('#ff0000') + * .setAlpha(0.5) + * .premultiply(0.8); + * + * // Different formats + * color.setValue(0xff0000); // Hex number + * color.setValue('#ff0000'); // Hex string + * color.setValue([1, 0, 0]); // RGB array + * color.setValue([1, 0, 0, 0.5]); // RGBA array + * color.setValue({ r: 1, g: 0, b: 0 }); // RGB object + * + * // Copy from another color + * const red = new Color('red'); + * color.setValue(red); + * ``` + * @throws {Error} If the color value is invalid or null + * @see {@link Color.value} For the underlying value property + */ + setValue(value) { + this.value = value; + return this; + } + /** + * The current color source. This property allows getting and setting the color value + * while preserving the original format where possible. + * @remarks + * When setting: + * - Setting to a `Color` instance copies its source and components + * - Setting to other valid sources normalizes and stores the value + * - Setting to `null` throws an Error + * - The color remains unchanged if normalization fails + * + * When getting: + * - Returns `null` if color was modified by {@link Color.multiply} or {@link Color.premultiply} + * - Otherwise returns the original color source + * @example + * ```ts + * // Setting different color formats + * const color = new Color(); + * + * color.value = 0xff0000; // Hex number + * color.value = '#ff0000'; // Hex string + * color.value = [1, 0, 0]; // RGB array + * color.value = [1, 0, 0, 0.5]; // RGBA array + * color.value = { r: 1, g: 0, b: 0 }; // RGB object + * + * // Copying from another color + * const red = new Color('red'); + * color.value = red; // Copies red's components + * + * // Getting the value + * console.log(color.value); // Returns original format + * + * // After modifications + * color.multiply([0.5, 0.5, 0.5]); + * console.log(color.value); // Returns null + * ``` + * @throws {Error} When attempting to set `null` + */ + set value(value) { + if (value instanceof _Color) { + this._value = this._cloneSource(value._value); + this._int = value._int; + this._components.set(value._components); + } else if (value === null) { + throw new Error("Cannot set Color#value to null"); + } else if (this._value === null || !this._isSourceEqual(this._value, value)) { + this._value = this._cloneSource(value); + this._normalize(this._value); + } + } + get value() { + return this._value; + } + /** + * Copy a color source internally. + * @param value - Color source + */ + _cloneSource(value) { + if (typeof value === "string" || typeof value === "number" || value instanceof Number || value === null) { + return value; + } else if (Array.isArray(value) || ArrayBuffer.isView(value)) { + return value.slice(0); + } else if (typeof value === "object" && value !== null) { + return __spreadValues$1l({}, value); + } + return value; + } + /** + * Equality check for color sources. + * @param value1 - First color source + * @param value2 - Second color source + * @returns `true` if the color sources are equal, `false` otherwise. + */ + _isSourceEqual(value1, value2) { + const type1 = typeof value1; + const type2 = typeof value2; + if (type1 !== type2) { + return false; + } else if (type1 === "number" || type1 === "string" || value1 instanceof Number) { + return value1 === value2; + } else if (Array.isArray(value1) && Array.isArray(value2) || ArrayBuffer.isView(value1) && ArrayBuffer.isView(value2)) { + if (value1.length !== value2.length) { + return false; + } + return value1.every((v, i) => v === value2[i]); + } else if (value1 !== null && value2 !== null) { + const keys1 = Object.keys(value1); + const keys2 = Object.keys(value2); + if (keys1.length !== keys2.length) { + return false; + } + return keys1.every((key) => value1[key] === value2[key]); + } + return value1 === value2; + } + /** + * Convert to a RGBA color object with normalized components (0-1). + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Convert colors to RGBA objects + * new Color('white').toRgba(); // returns { r: 1, g: 1, b: 1, a: 1 } + * new Color('#ff0000').toRgba(); // returns { r: 1, g: 0, b: 0, a: 1 } + * + * // With transparency + * new Color('rgba(255,0,0,0.5)').toRgba(); // returns { r: 1, g: 0, b: 0, a: 0.5 } + * ``` + * @returns An RGBA object with normalized components + */ + toRgba() { + const [r, g, b, a] = this._components; + return { r, g, b, a }; + } + /** + * Convert to a RGB color object with normalized components (0-1). + * + * Alpha component is omitted in the output. + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Convert colors to RGB objects + * new Color('white').toRgb(); // returns { r: 1, g: 1, b: 1 } + * new Color('#ff0000').toRgb(); // returns { r: 1, g: 0, b: 0 } + * + * // Alpha is ignored + * new Color('rgba(255,0,0,0.5)').toRgb(); // returns { r: 1, g: 0, b: 0 } + * ``` + * @returns An RGB object with normalized components + */ + toRgb() { + const [r, g, b] = this._components; + return { r, g, b }; + } + /** + * Convert to a CSS-style rgba string representation. + * + * RGB components are scaled to 0-255 range, alpha remains 0-1. + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Convert colors to RGBA strings + * new Color('white').toRgbaString(); // returns "rgba(255,255,255,1)" + * new Color('#ff0000').toRgbaString(); // returns "rgba(255,0,0,1)" + * + * // With transparency + * new Color([1, 0, 0, 0.5]).toRgbaString(); // returns "rgba(255,0,0,0.5)" + * ``` + * @returns A CSS-compatible rgba string + */ + toRgbaString() { + const [r, g, b] = this.toUint8RgbArray(); + return `rgba(${r},${g},${b},${this.alpha})`; + } + /** + * Convert to an [R, G, B] array of clamped uint8 values (0 to 255). + * @param {number[]|Uint8Array|Uint8ClampedArray} [out] - Optional output array. If not provided, + * a cached array will be used and returned. + * @returns Array containing RGB components as integers between 0-255 + * @example + * ```ts + * // Basic usage + * new Color('white').toUint8RgbArray(); // returns [255, 255, 255] + * new Color('#ff0000').toUint8RgbArray(); // returns [255, 0, 0] + * + * // Using custom output array + * const rgb = new Uint8Array(3); + * new Color('blue').toUint8RgbArray(rgb); // rgb is now [0, 0, 255] + * + * // Using different array types + * new Color('red').toUint8RgbArray(new Uint8ClampedArray(3)); // [255, 0, 0] + * new Color('red').toUint8RgbArray([]); // [255, 0, 0] + * ``` + * @remarks + * - Output values are always clamped between 0-255 + * - Alpha component is not included in output + * - Reuses internal cache array if no output array provided + */ + toUint8RgbArray(out) { + const [r, g, b] = this._components; + if (!this._arrayRgb) { + this._arrayRgb = []; + } + out || (out = this._arrayRgb); + out[0] = Math.round(r * 255); + out[1] = Math.round(g * 255); + out[2] = Math.round(b * 255); + return out; + } + /** + * Convert to an [R, G, B, A] array of normalized floats (numbers from 0.0 to 1.0). + * @param {number[]|Float32Array} [out] - Optional output array. If not provided, + * a cached array will be used and returned. + * @returns Array containing RGBA components as floats between 0-1 + * @example + * ```ts + * // Basic usage + * new Color('white').toArray(); // returns [1, 1, 1, 1] + * new Color('red').toArray(); // returns [1, 0, 0, 1] + * + * // With alpha + * new Color('rgba(255,0,0,0.5)').toArray(); // returns [1, 0, 0, 0.5] + * + * // Using custom output array + * const rgba = new Float32Array(4); + * new Color('blue').toArray(rgba); // rgba is now [0, 0, 1, 1] + * ``` + * @remarks + * - Output values are normalized between 0-1 + * - Includes alpha component as the fourth value + * - Reuses internal cache array if no output array provided + */ + toArray(out) { + if (!this._arrayRgba) { + this._arrayRgba = []; + } + out || (out = this._arrayRgba); + const [r, g, b, a] = this._components; + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = a; + return out; + } + /** + * Convert to an [R, G, B] array of normalized floats (numbers from 0.0 to 1.0). + * @param {number[]|Float32Array} [out] - Optional output array. If not provided, + * a cached array will be used and returned. + * @returns Array containing RGB components as floats between 0-1 + * @example + * ```ts + * // Basic usage + * new Color('white').toRgbArray(); // returns [1, 1, 1] + * new Color('red').toRgbArray(); // returns [1, 0, 0] + * + * // Using custom output array + * const rgb = new Float32Array(3); + * new Color('blue').toRgbArray(rgb); // rgb is now [0, 0, 1] + * ``` + * @remarks + * - Output values are normalized between 0-1 + * - Alpha component is omitted from output + * - Reuses internal cache array if no output array provided + */ + toRgbArray(out) { + if (!this._arrayRgb) { + this._arrayRgb = []; + } + out || (out = this._arrayRgb); + const [r, g, b] = this._components; + out[0] = r; + out[1] = g; + out[2] = b; + return out; + } + /** + * Convert to a hexadecimal number. + * @returns The color as a 24-bit RGB integer + * @example + * ```ts + * // Basic usage + * new Color('white').toNumber(); // returns 0xffffff + * new Color('red').toNumber(); // returns 0xff0000 + * + * // Store as hex + * const color = new Color('blue'); + * const hex = color.toNumber(); // 0x0000ff + * ``` + */ + toNumber() { + return this._int; + } + /** + * Convert to a BGR number. + * + * Useful for platforms that expect colors in BGR format. + * @returns The color as a 24-bit BGR integer + * @example + * ```ts + * // Convert RGB to BGR + * new Color(0xffcc99).toBgrNumber(); // returns 0x99ccff + * + * // Common use case: platform-specific color format + * const color = new Color('orange'); + * const bgrColor = color.toBgrNumber(); // Color with swapped R/B channels + * ``` + * @remarks + * This swaps the red and blue channels compared to the normal RGB format: + * - RGB 0xRRGGBB becomes BGR 0xBBGGRR + */ + toBgrNumber() { + const [r, g, b] = this.toUint8RgbArray(); + return (b << 16) + (g << 8) + r; + } + /** + * Convert to a hexadecimal number in little endian format (e.g., BBGGRR). + * + * Useful for platforms that expect colors in little endian byte order. + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Convert RGB color to little endian format + * new Color(0xffcc99).toLittleEndianNumber(); // returns 0x99ccff + * + * // Common use cases: + * const color = new Color('orange'); + * const leColor = color.toLittleEndianNumber(); // Swaps byte order for LE systems + * + * // Multiple conversions + * const colors = { + * normal: 0xffcc99, + * littleEndian: new Color(0xffcc99).toLittleEndianNumber(), // 0x99ccff + * backToNormal: new Color(0x99ccff).toLittleEndianNumber() // 0xffcc99 + * }; + * ``` + * @remarks + * - Swaps R and B channels in the color value + * - RGB 0xRRGGBB becomes 0xBBGGRR + * - Useful for systems that use little endian byte order + * - Can be used to convert back and forth between formats + * @returns The color as a number in little endian format (BBGGRR) + * @see {@link Color.toBgrNumber} For BGR format without byte swapping + */ + toLittleEndianNumber() { + const value = this._int; + return (value >> 16) + (value & 65280) + ((value & 255) << 16); + } + /** + * Multiply with another color. + * + * This action is destructive and modifies the original color. + * @param {ColorSource} value - The color to multiply by. Accepts any valid color format: + * - Hex strings/numbers (e.g., '#ff0000', 0xff0000) + * - RGB/RGBA arrays ([1, 0, 0], [1, 0, 0, 1]) + * - Color objects ({ r: 1, g: 0, b: 0 }) + * - CSS color names ('red', 'blue') + * @returns this - The Color instance for chaining + * @example + * ```ts + * // Basic multiplication + * const color = new Color('#ff0000'); + * color.multiply(0x808080); // 50% darker red + * + * // With transparency + * color.multiply([1, 1, 1, 0.5]); // 50% transparent + * + * // Chain operations + * color + * .multiply('#808080') + * .multiply({ r: 1, g: 1, b: 1, a: 0.5 }); + * ``` + * @remarks + * - Multiplies each RGB component and alpha separately + * - Values are clamped between 0-1 + * - Original color format is lost (value becomes null) + * - Operation cannot be undone + */ + multiply(value) { + const [r, g, b, a] = _Color._temp.setValue(value)._components; + this._components[0] *= r; + this._components[1] *= g; + this._components[2] *= b; + this._components[3] *= a; + this._refreshInt(); + this._value = null; + return this; + } + /** + * Converts color to a premultiplied alpha format. + * + * This action is destructive and modifies the original color. + * @param alpha - The alpha value to multiply by (0-1) + * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels + * @returns {Color} The Color instance for chaining + * @example + * ```ts + * // Basic premultiplication + * const color = new Color('red'); + * color.premultiply(0.5); // 50% transparent red with premultiplied RGB + * + * // Alpha only (RGB unchanged) + * color.premultiply(0.5, false); // 50% transparent, original RGB + * + * // Chain with other operations + * color + * .multiply(0x808080) + * .premultiply(0.5) + * .toNumber(); + * ``` + * @remarks + * - RGB channels are multiplied by alpha when applyToRGB is true + * - Alpha is always set to the provided value + * - Values are clamped between 0-1 + * - Original color format is lost (value becomes null) + * - Operation cannot be undone + */ + premultiply(alpha, applyToRGB = true) { + if (applyToRGB) { + this._components[0] *= alpha; + this._components[1] *= alpha; + this._components[2] *= alpha; + } + this._components[3] = alpha; + this._refreshInt(); + this._value = null; + return this; + } + /** + * Returns the color as a 32-bit premultiplied alpha integer. + * + * Format: 0xAARRGGBB + * @param {number} alpha - The alpha value to multiply by (0-1) + * @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels + * @returns {number} The premultiplied color as a 32-bit integer + * @example + * ```ts + * // Convert to premultiplied format + * const color = new Color('red'); + * + * // Full opacity (0xFFRRGGBB) + * color.toPremultiplied(1.0); // 0xFFFF0000 + * + * // 50% transparency with premultiplied RGB + * color.toPremultiplied(0.5); // 0x7F7F0000 + * + * // 50% transparency without RGB premultiplication + * color.toPremultiplied(0.5, false); // 0x7FFF0000 + * ``` + * @remarks + * - Returns full opacity (0xFF000000) when alpha is 1.0 + * - Returns 0 when alpha is 0.0 and applyToRGB is true + * - RGB values are rounded during premultiplication + */ + toPremultiplied(alpha, applyToRGB = true) { + if (alpha === 1) { + return (255 << 24) + this._int; + } + if (alpha === 0) { + return applyToRGB ? 0 : this._int; + } + let r = this._int >> 16 & 255; + let g = this._int >> 8 & 255; + let b = this._int & 255; + if (applyToRGB) { + r = r * alpha + 0.5 | 0; + g = g * alpha + 0.5 | 0; + b = b * alpha + 0.5 | 0; + } + return (alpha * 255 << 24) + (r << 16) + (g << 8) + b; + } + /** + * Convert to a hexadecimal string (6 characters). + * @returns A CSS-compatible hex color string (e.g., "#ff0000") + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Basic colors + * new Color('red').toHex(); // returns "#ff0000" + * new Color('white').toHex(); // returns "#ffffff" + * new Color('black').toHex(); // returns "#000000" + * + * // From different formats + * new Color(0xff0000).toHex(); // returns "#ff0000" + * new Color([1, 0, 0]).toHex(); // returns "#ff0000" + * new Color({ r: 1, g: 0, b: 0 }).toHex(); // returns "#ff0000" + * ``` + * @remarks + * - Always returns a 6-character hex string + * - Includes leading "#" character + * - Alpha channel is ignored + * - Values are rounded to nearest hex value + */ + toHex() { + const hexString = this._int.toString(16); + return `#${"000000".substring(0, 6 - hexString.length) + hexString}`; + } + /** + * Convert to a hexadecimal string with alpha (8 characters). + * @returns A CSS-compatible hex color string with alpha (e.g., "#ff0000ff") + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Fully opaque colors + * new Color('red').toHexa(); // returns "#ff0000ff" + * new Color('white').toHexa(); // returns "#ffffffff" + * + * // With transparency + * new Color('rgba(255, 0, 0, 0.5)').toHexa(); // returns "#ff00007f" + * new Color([1, 0, 0, 0]).toHexa(); // returns "#ff000000" + * ``` + * @remarks + * - Returns an 8-character hex string + * - Includes leading "#" character + * - Alpha is encoded in last two characters + * - Values are rounded to nearest hex value + */ + toHexa() { + const alphaValue = Math.round(this._components[3] * 255); + const alphaString = alphaValue.toString(16); + return this.toHex() + "00".substring(0, 2 - alphaString.length) + alphaString; + } + /** + * Set alpha (transparency) value while preserving color components. + * + * Provides a chainable interface for setting alpha. + * @param alpha - Alpha value between 0 (fully transparent) and 1 (fully opaque) + * @returns The Color instance for chaining + * @example + * ```ts + * // Basic alpha setting + * const color = new Color('red'); + * color.setAlpha(0.5); // 50% transparent red + * + * // Chain with other operations + * color + * .setValue('#ff0000') + * .setAlpha(0.8) // 80% opaque + * .premultiply(0.5); // Further modify alpha + * + * // Reset to fully opaque + * color.setAlpha(1); + * ``` + * @remarks + * - Alpha value is clamped between 0-1 + * - Can be chained with other color operations + */ + setAlpha(alpha) { + this._components[3] = this._clamp(alpha); + return this; + } + /** + * Normalize the input value into rgba + * @param value - Input value + */ + _normalize(value) { + let r; + let g; + let b; + let a; + if ((typeof value === "number" || value instanceof Number) && value >= 0 && value <= 16777215) { + const int = value; + r = (int >> 16 & 255) / 255; + g = (int >> 8 & 255) / 255; + b = (int & 255) / 255; + a = 1; + } else if ((Array.isArray(value) || value instanceof Float32Array) && value.length >= 3 && value.length <= 4) { + value = this._clamp(value); + [r, g, b, a = 1] = value; + } else if ((value instanceof Uint8Array || value instanceof Uint8ClampedArray) && value.length >= 3 && value.length <= 4) { + value = this._clamp(value, 0, 255); + [r, g, b, a = 255] = value; + r /= 255; + g /= 255; + b /= 255; + a /= 255; + } else if (typeof value === "string" || typeof value === "object") { + if (typeof value === "string") { + const match = _Color.HEX_PATTERN.exec(value); + if (match) { + value = `#${match[2]}`; + } + } + const color = w(value); + if (color.isValid()) { + ({ r, g, b, a } = color.rgba); + r /= 255; + g /= 255; + b /= 255; + } + } + if (r !== void 0) { + this._components[0] = r; + this._components[1] = g; + this._components[2] = b; + this._components[3] = a; + this._refreshInt(); + } else { + throw new Error(`Unable to convert color ${value}`); + } + } + /** Refresh the internal color rgb number */ + _refreshInt() { + this._clamp(this._components); + const [r, g, b] = this._components; + this._int = (r * 255 << 16) + (g * 255 << 8) + (b * 255 | 0); + } + /** + * Clamps values to a range. Will override original values + * @param value - Value(s) to clamp + * @param min - Minimum value + * @param max - Maximum value + */ + _clamp(value, min = 0, max = 1) { + if (typeof value === "number") { + return Math.min(Math.max(value, min), max); + } + value.forEach((v, i) => { + value[i] = Math.min(Math.max(v, min), max); + }); + return value; + } + /** + * Check if a value can be interpreted as a valid color format. + * Supports all color formats that can be used with the Color class. + * @param value - Value to check + * @returns True if the value can be used as a color + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // CSS colors and hex values + * Color.isColorLike('red'); // true + * Color.isColorLike('#ff0000'); // true + * Color.isColorLike(0xff0000); // true + * + * // Arrays (RGB/RGBA) + * Color.isColorLike([1, 0, 0]); // true + * Color.isColorLike([1, 0, 0, 0.5]); // true + * + * // TypedArrays + * Color.isColorLike(new Float32Array([1, 0, 0])); // true + * Color.isColorLike(new Uint8Array([255, 0, 0])); // true + * Color.isColorLike(new Uint8ClampedArray([255, 0, 0])); // true + * + * // Object formats + * Color.isColorLike({ r: 1, g: 0, b: 0 }); // true (RGB) + * Color.isColorLike({ r: 1, g: 0, b: 0, a: 0.5 }); // true (RGBA) + * Color.isColorLike({ h: 0, s: 100, l: 50 }); // true (HSL) + * Color.isColorLike({ h: 0, s: 100, l: 50, a: 0.5 }); // true (HSLA) + * Color.isColorLike({ h: 0, s: 100, v: 100 }); // true (HSV) + * Color.isColorLike({ h: 0, s: 100, v: 100, a: 0.5 });// true (HSVA) + * + * // Color instances + * Color.isColorLike(new Color('red')); // true + * + * // Invalid values + * Color.isColorLike(null); // false + * Color.isColorLike(undefined); // false + * Color.isColorLike({}); // false + * Color.isColorLike([]); // false + * Color.isColorLike('not-a-color'); // false + * ``` + * @remarks + * Checks for the following formats: + * - Numbers (0x000000 to 0xffffff) + * - CSS color strings + * - RGB/RGBA arrays and objects + * - HSL/HSLA objects + * - HSV/HSVA objects + * - TypedArrays (Float32Array, Uint8Array, Uint8ClampedArray) + * - Color instances + * @see {@link ColorSource} For supported color format types + * @see {@link Color.setValue} For setting color values + * @category utility + */ + static isColorLike(value) { + return typeof value === "number" || typeof value === "string" || value instanceof Number || value instanceof _Color || Array.isArray(value) || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Float32Array || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 && value.a !== void 0; + } + }; + /** + * Static shared Color instance used for utility operations. This is a singleton color object + * that can be reused to avoid creating unnecessary Color instances. + * > [!IMPORTANT] You should be careful when using this shared instance, as it is mutable and can be + * > changed by any code that uses it. + * > + * > It is best used for one-off color operations or temporary transformations. + * > For persistent colors, create your own Color instance instead. + * @example + * ```ts + * import { Color } from 'pixi.js'; + * + * // Use shared instance for one-off color operations + * Color.shared.setValue(0xff0000); + * const redHex = Color.shared.toHex(); // "#ff0000" + * const redRgb = Color.shared.toRgbArray(); // [1, 0, 0] + * + * // Temporary color transformations + * const colorNumber = Color.shared + * .setValue('#ff0000') // Set to red + * .setAlpha(0.5) // Make semi-transparent + * .premultiply(0.8) // Apply premultiplication + * .toNumber(); // Convert to number + * + * // Chain multiple operations + * const result = Color.shared + * .setValue(someColor) + * .multiply(tintColor) + * .toPremultiplied(alpha); + * ``` + * @remarks + * - This is a shared instance - be careful about multiple code paths using it simultaneously + * - Use for temporary color operations to avoid allocating new Color instances + * - The value is preserved between operations, so reset if needed + * - For persistent colors, create your own Color instance instead + */ + _Color.shared = new _Color(); + /** + * Temporary Color object for static uses internally. + * As to not conflict with Color.shared. + * @ignore + */ + _Color._temp = new _Color(); + /** Pattern for hex strings */ + // eslint-disable-next-line @typescript-eslint/naming-convention + _Color.HEX_PATTERN = /^(#|0x)?(([a-f0-9]{3}){1,2}([a-f0-9]{2})?)$/i; + let Color = _Color; + + "use strict"; + const cullingMixin = { + cullArea: null, + cullable: false, + cullableChildren: true + }; + + "use strict"; + const PI_2 = Math.PI * 2; + const RAD_TO_DEG = 180 / Math.PI; + const DEG_TO_RAD = Math.PI / 180; + + "use strict"; + class Point { + /** + * Creates a new `Point` + * @param {number} [x=0] - position of the point on the x axis + * @param {number} [y=0] - position of the point on the y axis + */ + constructor(x = 0, y = 0) { + /** + * Position of the point on the x axis + * @example + * ```ts + * // Set x position + * const point = new Point(); + * point.x = 100; + * + * // Use in calculations + * const width = rightPoint.x - leftPoint.x; + * ``` + */ + this.x = 0; + /** + * Position of the point on the y axis + * @example + * ```ts + * // Set y position + * const point = new Point(); + * point.y = 200; + * + * // Use in calculations + * const height = bottomPoint.y - topPoint.y; + * ``` + */ + this.y = 0; + this.x = x; + this.y = y; + } + /** + * Creates a clone of this point, which is a new instance with the same `x` and `y` values. + * @example + * ```ts + * // Basic point cloning + * const original = new Point(100, 200); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.set(300, 400); + * + * // Verify independence + * console.log(original); // Point(100, 200) + * console.log(modified); // Point(300, 400) + * ``` + * @remarks + * - Creates new Point instance + * - Deep copies x and y values + * - Independent from original + * - Useful for preserving values + * @returns A clone of this point + * @see {@link Point.copyFrom} For copying into existing point + * @see {@link Point.copyTo} For copying to existing point + */ + clone() { + return new Point(this.x, this.y); + } + /** + * Copies x and y from the given point into this point. + * @example + * ```ts + * // Basic copying + * const source = new Point(100, 200); + * const target = new Point(); + * target.copyFrom(source); + * + * // Copy and chain operations + * const point = new Point() + * .copyFrom(source) + * .set(x + 50, y + 50); + * + * // Copy from any PointData + * const data = { x: 10, y: 20 }; + * point.copyFrom(data); + * ``` + * @param p - The point to copy from + * @returns The point instance itself + * @see {@link Point.copyTo} For copying to another point + * @see {@link Point.clone} For creating new point copy + */ + copyFrom(p) { + this.set(p.x, p.y); + return this; + } + /** + * Copies this point's x and y into the given point. + * @example + * ```ts + * // Basic copying + * const source = new Point(100, 200); + * const target = new Point(); + * source.copyTo(target); + * ``` + * @param p - The point to copy to. Can be any type that is or extends `PointLike` + * @returns The point (`p`) with values updated + * @see {@link Point.copyFrom} For copying from another point + * @see {@link Point.clone} For creating new point copy + */ + copyTo(p) { + p.set(this.x, this.y); + return p; + } + /** + * Checks if another point is equal to this point. + * + * Compares x and y values using strict equality. + * @example + * ```ts + * // Basic equality check + * const p1 = new Point(100, 200); + * const p2 = new Point(100, 200); + * console.log(p1.equals(p2)); // true + * + * // Compare with PointData + * const data = { x: 100, y: 200 }; + * console.log(p1.equals(data)); // true + * + * // Check different points + * const p3 = new Point(200, 300); + * console.log(p1.equals(p3)); // false + * ``` + * @param p - The point to check + * @returns `true` if both `x` and `y` are equal + * @see {@link Point.copyFrom} For making points equal + * @see {@link PointData} For point data interface + */ + equals(p) { + return p.x === this.x && p.y === this.y; + } + /** + * Sets the point to a new x and y position. + * + * If y is omitted, both x and y will be set to x. + * @example + * ```ts + * // Basic position setting + * const point = new Point(); + * point.set(100, 200); + * + * // Set both x and y to same value + * point.set(50); // x=50, y=50 + * + * // Chain with other operations + * point + * .set(10, 20) + * .copyTo(otherPoint); + * ``` + * @param x - Position on the x axis + * @param y - Position on the y axis, defaults to x + * @returns The point instance itself + * @see {@link Point.copyFrom} For copying from another point + * @see {@link Point.equals} For comparing positions + */ + set(x = 0, y = x) { + this.x = x; + this.y = y; + return this; + } + toString() { + return `[pixi.js/math:Point x=${this.x} y=${this.y}]`; + } + /** + * A static Point object with `x` and `y` values of `0`. + * + * This shared instance is reset to zero values when accessed. + * + * > [!IMPORTANT] This point is shared and temporary. Do not store references to it. + * @example + * ```ts + * // Use for temporary calculations + * const tempPoint = Point.shared; + * tempPoint.set(100, 200); + * matrix.apply(tempPoint); + * + * // Will be reset to (0,0) on next access + * const fresh = Point.shared; // x=0, y=0 + * ``` + * @readonly + * @returns A fresh zeroed point for temporary use + * @see {@link Point.constructor} For creating new points + * @see {@link PointData} For basic point interface + */ + static get shared() { + tempPoint.x = 0; + tempPoint.y = 0; + return tempPoint; + } + } + const tempPoint = new Point(); + + "use strict"; + class Matrix { + /** + * @param a - x scale + * @param b - y skew + * @param c - x skew + * @param d - y scale + * @param tx - x translation + * @param ty - y translation + */ + constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) { + /** + * Array representation of the matrix. + * Only populated when `toArray()` is called. + * @default null + * @see {@link Matrix.toArray} For filling this array + */ + this.array = null; + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tx = tx; + this.ty = ty; + } + /** + * Creates a Matrix object based on the given array. + * Populates matrix components from a flat array in column-major order. + * + * > [!NOTE] Array mapping order: + * > ``` + * > array[0] = a (x scale) + * > array[1] = b (y skew) + * > array[2] = tx (x translation) + * > array[3] = c (x skew) + * > array[4] = d (y scale) + * > array[5] = ty (y translation) + * > ``` + * @example + * ```ts + * // Create matrix from array + * const matrix = new Matrix(); + * matrix.fromArray([ + * 2, 0, 100, // a, b, tx + * 0, 2, 100 // c, d, ty + * ]); + * + * // Create matrix from typed array + * const float32Array = new Float32Array([ + * 1, 0, 0, // Scale x1, no skew + * 0, 1, 0 // No skew, scale x1 + * ]); + * matrix.fromArray(float32Array); + * ``` + * @param array - The array to populate the matrix from + * @see {@link Matrix.toArray} For converting matrix to array + * @see {@link Matrix.set} For setting values directly + */ + fromArray(array) { + this.a = array[0]; + this.b = array[1]; + this.c = array[3]; + this.d = array[4]; + this.tx = array[2]; + this.ty = array[5]; + } + /** + * Sets the matrix properties directly. + * All matrix components can be set in one call. + * @example + * ```ts + * // Set to identity matrix + * matrix.set(1, 0, 0, 1, 0, 0); + * + * // Set to scale matrix + * matrix.set(2, 0, 0, 2, 0, 0); // Scale 2x + * + * // Set to translation matrix + * matrix.set(1, 0, 0, 1, 100, 50); // Move 100,50 + * ``` + * @param a - Scale on x axis + * @param b - Shear on y axis + * @param c - Shear on x axis + * @param d - Scale on y axis + * @param tx - Translation on x axis + * @param ty - Translation on y axis + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.identity} For resetting to identity + * @see {@link Matrix.fromArray} For setting from array + */ + set(a, b, c, d, tx, ty) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tx = tx; + this.ty = ty; + return this; + } + /** + * Creates an array from the current Matrix object. + * + * > [!NOTE] The array format is: + * > ``` + * > Non-transposed: + * > [a, c, tx, + * > b, d, ty, + * > 0, 0, 1] + * > + * > Transposed: + * > [a, b, 0, + * > c, d, 0, + * > tx,ty,1] + * > ``` + * @example + * ```ts + * // Basic array conversion + * const matrix = new Matrix(2, 0, 0, 2, 100, 100); + * const array = matrix.toArray(); + * + * // Using existing array + * const float32Array = new Float32Array(9); + * matrix.toArray(false, float32Array); + * + * // Get transposed array + * const transposed = matrix.toArray(true); + * ``` + * @param transpose - Whether to transpose the matrix + * @param out - Optional Float32Array to store the result + * @returns The array containing the matrix values + * @see {@link Matrix.fromArray} For creating matrix from array + * @see {@link Matrix.array} For cached array storage + */ + toArray(transpose, out) { + if (!this.array) { + this.array = new Float32Array(9); + } + const array = out || this.array; + if (transpose) { + array[0] = this.a; + array[1] = this.b; + array[2] = 0; + array[3] = this.c; + array[4] = this.d; + array[5] = 0; + array[6] = this.tx; + array[7] = this.ty; + array[8] = 1; + } else { + array[0] = this.a; + array[1] = this.c; + array[2] = this.tx; + array[3] = this.b; + array[4] = this.d; + array[5] = this.ty; + array[6] = 0; + array[7] = 0; + array[8] = 1; + } + return array; + } + /** + * Get a new position with the current transformation applied. + * + * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) + * @example + * ```ts + * // Basic point transformation + * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4); + * const point = new Point(10, 20); + * const transformed = matrix.apply(point); + * + * // Reuse existing point + * const output = new Point(); + * matrix.apply(point, output); + * ``` + * @param pos - The origin point to transform + * @param newPos - Optional point to store the result + * @returns The transformed point + * @see {@link Matrix.applyInverse} For inverse transformation + * @see {@link Point} For point operations + */ + apply(pos, newPos) { + newPos = newPos || new Point(); + const x = pos.x; + const y = pos.y; + newPos.x = this.a * x + this.c * y + this.tx; + newPos.y = this.b * x + this.d * y + this.ty; + return newPos; + } + /** + * Get a new position with the inverse of the current transformation applied. + * + * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) + * @example + * ```ts + * // Basic inverse transformation + * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4); + * const worldPoint = new Point(150, 100); + * const localPoint = matrix.applyInverse(worldPoint); + * + * // Reuse existing point + * const output = new Point(); + * matrix.applyInverse(worldPoint, output); + * + * // Convert mouse position to local space + * const mousePoint = new Point(mouseX, mouseY); + * const localMouse = matrix.applyInverse(mousePoint); + * ``` + * @param pos - The origin point to inverse-transform + * @param newPos - Optional point to store the result + * @returns The inverse-transformed point + * @see {@link Matrix.apply} For forward transformation + * @see {@link Matrix.invert} For getting inverse matrix + */ + applyInverse(pos, newPos) { + newPos = newPos || new Point(); + const a = this.a; + const b = this.b; + const c = this.c; + const d = this.d; + const tx = this.tx; + const ty = this.ty; + const id = 1 / (a * d + c * -b); + const x = pos.x; + const y = pos.y; + newPos.x = d * id * x + -c * id * y + (ty * c - tx * d) * id; + newPos.y = a * id * y + -b * id * x + (-ty * a + tx * b) * id; + return newPos; + } + /** + * Translates the matrix on the x and y axes. + * Adds to the position values while preserving scale, rotation and skew. + * @example + * ```ts + * // Basic translation + * const matrix = new Matrix(); + * matrix.translate(100, 50); // Move right 100, down 50 + * + * // Chain with other transformations + * matrix + * .scale(2, 2) + * .translate(100, 0) + * .rotate(Math.PI / 4); + * ``` + * @param x - How much to translate on the x axis + * @param y - How much to translate on the y axis + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.set} For setting position directly + * @see {@link Matrix.setTransform} For complete transform setup + */ + translate(x, y) { + this.tx += x; + this.ty += y; + return this; + } + /** + * Applies a scale transformation to the matrix. + * Multiplies the scale values with existing matrix components. + * @example + * ```ts + * // Basic scaling + * const matrix = new Matrix(); + * matrix.scale(2, 3); // Scale 2x horizontally, 3x vertically + * + * // Chain with other transformations + * matrix + * .translate(100, 100) + * .scale(2, 2) // Scales after translation + * .rotate(Math.PI / 4); + * ``` + * @param x - The amount to scale horizontally + * @param y - The amount to scale vertically + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.setTransform} For setting scale directly + * @see {@link Matrix.append} For combining transformations + */ + scale(x, y) { + this.a *= x; + this.d *= y; + this.c *= x; + this.b *= y; + this.tx *= x; + this.ty *= y; + return this; + } + /** + * Applies a rotation transformation to the matrix. + * + * Rotates around the origin (0,0) by the given angle in radians. + * @example + * ```ts + * // Basic rotation + * const matrix = new Matrix(); + * matrix.rotate(Math.PI / 4); // Rotate 45 degrees + * + * // Chain with other transformations + * matrix + * .translate(100, 100) // Move to rotation center + * .rotate(Math.PI) // Rotate 180 degrees + * .scale(2, 2); // Scale after rotation + * + * // Common angles + * matrix.rotate(Math.PI / 2); // 90 degrees + * matrix.rotate(Math.PI); // 180 degrees + * matrix.rotate(Math.PI * 2); // 360 degrees + * ``` + * @remarks + * - Rotates around origin point (0,0) + * - Affects position if translation was set + * - Uses counter-clockwise rotation + * - Order of operations matters when chaining + * @param angle - The angle in radians + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.setTransform} For setting rotation directly + * @see {@link Matrix.append} For combining transformations + */ + rotate(angle) { + const cos = Math.cos(angle); + const sin = Math.sin(angle); + const a1 = this.a; + const c1 = this.c; + const tx1 = this.tx; + this.a = a1 * cos - this.b * sin; + this.b = a1 * sin + this.b * cos; + this.c = c1 * cos - this.d * sin; + this.d = c1 * sin + this.d * cos; + this.tx = tx1 * cos - this.ty * sin; + this.ty = tx1 * sin + this.ty * cos; + return this; + } + /** + * Appends the given Matrix to this Matrix. + * Combines two matrices by multiplying them together: this = this * matrix + * @example + * ```ts + * // Basic matrix combination + * const matrix = new Matrix(); + * const other = new Matrix().translate(100, 0).rotate(Math.PI / 4); + * matrix.append(other); + * ``` + * @remarks + * - Order matters: A.append(B) !== B.append(A) + * - Modifies current matrix + * - Preserves transformation order + * - Commonly used for combining transforms + * @param matrix - The matrix to append + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.prepend} For prepending transformations + * @see {@link Matrix.appendFrom} For appending two external matrices + */ + append(matrix) { + const a1 = this.a; + const b1 = this.b; + const c1 = this.c; + const d1 = this.d; + this.a = matrix.a * a1 + matrix.b * c1; + this.b = matrix.a * b1 + matrix.b * d1; + this.c = matrix.c * a1 + matrix.d * c1; + this.d = matrix.c * b1 + matrix.d * d1; + this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; + this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; + return this; + } + /** + * Appends two matrices and sets the result to this matrix. + * Performs matrix multiplication: this = A * B + * @example + * ```ts + * // Basic matrix multiplication + * const result = new Matrix(); + * const matrixA = new Matrix().scale(2, 2); + * const matrixB = new Matrix().rotate(Math.PI / 4); + * result.appendFrom(matrixA, matrixB); + * ``` + * @remarks + * - Order matters: A * B !== B * A + * - Creates a new transformation from two others + * - More efficient than append() for multiple operations + * - Does not modify input matrices + * @param a - The first matrix to multiply + * @param b - The second matrix to multiply + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.append} For single matrix combination + * @see {@link Matrix.prepend} For reverse order multiplication + */ + appendFrom(a, b) { + const a1 = a.a; + const b1 = a.b; + const c1 = a.c; + const d1 = a.d; + const tx = a.tx; + const ty = a.ty; + const a2 = b.a; + const b2 = b.b; + const c2 = b.c; + const d2 = b.d; + this.a = a1 * a2 + b1 * c2; + this.b = a1 * b2 + b1 * d2; + this.c = c1 * a2 + d1 * c2; + this.d = c1 * b2 + d1 * d2; + this.tx = tx * a2 + ty * c2 + b.tx; + this.ty = tx * b2 + ty * d2 + b.ty; + return this; + } + /** + * Sets the matrix based on all the available properties. + * Combines position, scale, rotation, skew and pivot in a single operation. + * @example + * ```ts + * // Basic transform setup + * const matrix = new Matrix(); + * matrix.setTransform( + * 100, 100, // position + * 0, 0, // pivot + * 2, 2, // scale + * Math.PI / 4, // rotation (45 degrees) + * 0, 0 // skew + * ); + * ``` + * @remarks + * - Updates all matrix components at once + * - More efficient than separate transform calls + * - Uses radians for rotation and skew + * - Pivot affects rotation center + * @param x - Position on the x axis + * @param y - Position on the y axis + * @param pivotX - Pivot on the x axis + * @param pivotY - Pivot on the y axis + * @param scaleX - Scale on the x axis + * @param scaleY - Scale on the y axis + * @param rotation - Rotation in radians + * @param skewX - Skew on the x axis + * @param skewY - Skew on the y axis + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.decompose} For extracting transform properties + * @see {@link TransformableObject} For transform data structure + */ + setTransform(x, y, pivotX, pivotY, scaleX, scaleY, rotation, skewX, skewY) { + this.a = Math.cos(rotation + skewY) * scaleX; + this.b = Math.sin(rotation + skewY) * scaleX; + this.c = -Math.sin(rotation - skewX) * scaleY; + this.d = Math.cos(rotation - skewX) * scaleY; + this.tx = x - (pivotX * this.a + pivotY * this.c); + this.ty = y - (pivotX * this.b + pivotY * this.d); + return this; + } + /** + * Prepends the given Matrix to this Matrix. + * Combines two matrices by multiplying them together: this = matrix * this + * @example + * ```ts + * // Basic matrix prepend + * const matrix = new Matrix().scale(2, 2); + * const other = new Matrix().translate(100, 0); + * matrix.prepend(other); // Translation happens before scaling + * ``` + * @remarks + * - Order matters: A.prepend(B) !== B.prepend(A) + * - Modifies current matrix + * - Reverses transformation order compared to append() + * @param matrix - The matrix to prepend + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.append} For appending transformations + * @see {@link Matrix.appendFrom} For combining external matrices + */ + prepend(matrix) { + const tx1 = this.tx; + if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) { + const a1 = this.a; + const c1 = this.c; + this.a = a1 * matrix.a + this.b * matrix.c; + this.b = a1 * matrix.b + this.b * matrix.d; + this.c = c1 * matrix.a + this.d * matrix.c; + this.d = c1 * matrix.b + this.d * matrix.d; + } + this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx; + this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty; + return this; + } + /** + * Decomposes the matrix into its individual transform components. + * Extracts position, scale, rotation and skew values from the matrix. + * @example + * ```ts + * // Basic decomposition + * const matrix = new Matrix() + * .translate(100, 100) + * .rotate(Math.PI / 4) + * .scale(2, 2); + * + * const transform = { + * position: new Point(), + * scale: new Point(), + * pivot: new Point(), + * skew: new Point(), + * rotation: 0 + * }; + * + * matrix.decompose(transform); + * console.log(transform.position); // Point(100, 100) + * console.log(transform.rotation); // ~0.785 (PI/4) + * console.log(transform.scale); // Point(2, 2) + * ``` + * @remarks + * - Handles combined transformations + * - Accounts for pivot points + * - Chooses between rotation/skew based on transform type + * - Uses radians for rotation and skew + * @param transform - The transform object to store the decomposed values + * @returns The transform with the newly applied properties + * @see {@link Matrix.setTransform} For composing from components + * @see {@link TransformableObject} For transform structure + */ + decompose(transform) { + const a = this.a; + const b = this.b; + const c = this.c; + const d = this.d; + const pivot = transform.pivot; + const skewX = -Math.atan2(-c, d); + const skewY = Math.atan2(b, a); + const delta = Math.abs(skewX + skewY); + if (delta < 1e-5 || Math.abs(PI_2 - delta) < 1e-5) { + transform.rotation = skewY; + transform.skew.x = transform.skew.y = 0; + } else { + transform.rotation = 0; + transform.skew.x = skewX; + transform.skew.y = skewY; + } + transform.scale.x = Math.sqrt(a * a + b * b); + transform.scale.y = Math.sqrt(c * c + d * d); + transform.position.x = this.tx + (pivot.x * a + pivot.y * c); + transform.position.y = this.ty + (pivot.x * b + pivot.y * d); + return transform; + } + /** + * Inverts this matrix. + * Creates the matrix that when multiplied with this matrix results in an identity matrix. + * @example + * ```ts + * // Basic matrix inversion + * const matrix = new Matrix() + * .translate(100, 50) + * .scale(2, 2); + * + * matrix.invert(); // Now transforms in opposite direction + * + * // Verify inversion + * const point = new Point(50, 50); + * const transformed = matrix.apply(point); + * const original = matrix.invert().apply(transformed); + * // original ≈ point + * ``` + * @remarks + * - Modifies the current matrix + * - Useful for reversing transformations + * - Cannot invert matrices with zero determinant + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.identity} For resetting to identity + * @see {@link Matrix.applyInverse} For inverse transformations + */ + invert() { + const a1 = this.a; + const b1 = this.b; + const c1 = this.c; + const d1 = this.d; + const tx1 = this.tx; + const n = a1 * d1 - b1 * c1; + this.a = d1 / n; + this.b = -b1 / n; + this.c = -c1 / n; + this.d = a1 / n; + this.tx = (c1 * this.ty - d1 * tx1) / n; + this.ty = -(a1 * this.ty - b1 * tx1) / n; + return this; + } + /** + * Checks if this matrix is an identity matrix. + * + * An identity matrix has no transformations applied (default state). + * @example + * ```ts + * // Check if matrix is identity + * const matrix = new Matrix(); + * console.log(matrix.isIdentity()); // true + * + * // Check after transformations + * matrix.translate(100, 0); + * console.log(matrix.isIdentity()); // false + * + * // Reset and verify + * matrix.identity(); + * console.log(matrix.isIdentity()); // true + * ``` + * @remarks + * - Verifies a = 1, d = 1 (no scale) + * - Verifies b = 0, c = 0 (no skew) + * - Verifies tx = 0, ty = 0 (no translation) + * @returns True if matrix has no transformations + * @see {@link Matrix.identity} For resetting to identity + * @see {@link Matrix.IDENTITY} For constant identity matrix + */ + isIdentity() { + return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.tx === 0 && this.ty === 0; + } + /** + * Resets this Matrix to an identity (default) matrix. + * Sets all components to their default values: scale=1, no skew, no translation. + * @example + * ```ts + * // Reset transformed matrix + * const matrix = new Matrix() + * .scale(2, 2) + * .rotate(Math.PI / 4); + * matrix.identity(); // Back to default state + * + * // Chain after reset + * matrix + * .identity() + * .translate(100, 100) + * .scale(2, 2); + * + * // Compare with identity constant + * const isDefault = matrix.equals(Matrix.IDENTITY); + * ``` + * @remarks + * - Sets a=1, d=1 (default scale) + * - Sets b=0, c=0 (no skew) + * - Sets tx=0, ty=0 (no translation) + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.IDENTITY} For constant identity matrix + * @see {@link Matrix.isIdentity} For checking identity state + */ + identity() { + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; + return this; + } + /** + * Creates a new Matrix object with the same values as this one. + * @returns A copy of this matrix. Good for chaining method calls. + */ + clone() { + const matrix = new Matrix(); + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + return matrix; + } + /** + * Creates a new Matrix object with the same values as this one. + * @param matrix + * @example + * ```ts + * // Basic matrix cloning + * const matrix = new Matrix() + * .translate(100, 100) + * .rotate(Math.PI / 4); + * const copy = matrix.clone(); + * + * // Clone and modify + * const modified = matrix.clone() + * .scale(2, 2); + * + * // Compare matrices + * console.log(matrix.equals(copy)); // true + * console.log(matrix.equals(modified)); // false + * ``` + * @returns A copy of this matrix. Good for chaining method calls. + * @see {@link Matrix.copyTo} For copying to existing matrix + * @see {@link Matrix.copyFrom} For copying from another matrix + */ + copyTo(matrix) { + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + return matrix; + } + /** + * Changes the values of the matrix to be the same as the ones in given matrix. + * @example + * ```ts + * // Basic matrix copying + * const source = new Matrix() + * .translate(100, 100) + * .rotate(Math.PI / 4); + * const target = new Matrix(); + * target.copyFrom(source); + * ``` + * @param matrix - The matrix to copy from + * @returns This matrix. Good for chaining method calls. + * @see {@link Matrix.clone} For creating new matrix copy + * @see {@link Matrix.copyTo} For copying to another matrix + */ + copyFrom(matrix) { + this.a = matrix.a; + this.b = matrix.b; + this.c = matrix.c; + this.d = matrix.d; + this.tx = matrix.tx; + this.ty = matrix.ty; + return this; + } + /** + * Checks if this matrix equals another matrix. + * Compares all components for exact equality. + * @example + * ```ts + * // Basic equality check + * const m1 = new Matrix(); + * const m2 = new Matrix(); + * console.log(m1.equals(m2)); // true + * + * // Compare transformed matrices + * const transform = new Matrix() + * .translate(100, 100) + * const clone = new Matrix() + * .scale(2, 2); + * console.log(transform.equals(clone)); // false + * ``` + * @param matrix - The matrix to compare to + * @returns True if matrices are identical + * @see {@link Matrix.copyFrom} For copying matrix values + * @see {@link Matrix.isIdentity} For identity comparison + */ + equals(matrix) { + return matrix.a === this.a && matrix.b === this.b && matrix.c === this.c && matrix.d === this.d && matrix.tx === this.tx && matrix.ty === this.ty; + } + toString() { + return `[pixi.js:Matrix a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`; + } + /** + * A default (identity) matrix with no transformations applied. + * + * > [!IMPORTANT] This is a shared read-only object. Create a new Matrix if you need to modify it. + * @example + * ```ts + * // Get identity matrix reference + * const identity = Matrix.IDENTITY; + * console.log(identity.isIdentity()); // true + * + * // Compare with identity + * const matrix = new Matrix(); + * console.log(matrix.equals(Matrix.IDENTITY)); // true + * + * // Create new matrix instead of modifying IDENTITY + * const transform = new Matrix() + * .copyFrom(Matrix.IDENTITY) + * .translate(100, 100); + * ``` + * @readonly + * @returns A read-only identity matrix + * @see {@link Matrix.shared} For temporary calculations + * @see {@link Matrix.identity} For resetting matrices + */ + static get IDENTITY() { + return identityMatrix$1.identity(); + } + /** + * A static Matrix that can be used to avoid creating new objects. + * Will always ensure the matrix is reset to identity when requested. + * + * > [!IMPORTANT] This matrix is shared and temporary. Do not store references to it. + * @example + * ```ts + * // Use for temporary calculations + * const tempMatrix = Matrix.shared; + * tempMatrix.translate(100, 100).rotate(Math.PI / 4); + * const point = tempMatrix.apply({ x: 10, y: 20 }); + * + * // Will be reset to identity on next access + * const fresh = Matrix.shared; // Back to identity + * ``` + * @remarks + * - Always returns identity matrix + * - Safe to modify temporarily + * - Not safe to store references + * - Useful for one-off calculations + * @readonly + * @returns A fresh identity matrix for temporary use + * @see {@link Matrix.IDENTITY} For immutable identity matrix + * @see {@link Matrix.identity} For resetting matrices + */ + static get shared() { + return tempMatrix$8.identity(); + } + } + const tempMatrix$8 = new Matrix(); + const identityMatrix$1 = new Matrix(); + + "use strict"; + class ObservablePoint { + /** + * Creates a new `ObservablePoint` + * @param observer - Observer to pass to listen for change events. + * @param {number} [x=0] - position of the point on the x axis + * @param {number} [y=0] - position of the point on the y axis + */ + constructor(observer, x, y) { + this._x = x || 0; + this._y = y || 0; + this._observer = observer; + } + /** + * Creates a clone of this point. + * @example + * ```ts + * // Basic cloning + * const point = new ObservablePoint(observer, 100, 200); + * const copy = point.clone(); + * + * // Clone with new observer + * const newObserver = { + * _onUpdate: (p) => console.log(`Clone updated: (${p.x}, ${p.y})`) + * }; + * const watched = point.clone(newObserver); + * + * // Verify independence + * watched.set(300, 400); // Only triggers new observer + * ``` + * @param observer - Optional observer to pass to the new observable point + * @returns A copy of this observable point + * @see {@link ObservablePoint.copyFrom} For copying into existing point + * @see {@link Observer} For observer interface details + */ + clone(observer) { + return new ObservablePoint(observer != null ? observer : this._observer, this._x, this._y); + } + /** + * Sets the point to a new x and y position. + * + * If y is omitted, both x and y will be set to x. + * @example + * ```ts + * // Basic position setting + * const point = new ObservablePoint(observer); + * point.set(100, 200); + * + * // Set both x and y to same value + * point.set(50); // x=50, y=50 + * ``` + * @param x - Position on the x axis + * @param y - Position on the y axis, defaults to x + * @returns The point instance itself + * @see {@link ObservablePoint.copyFrom} For copying from another point + * @see {@link ObservablePoint.equals} For comparing positions + */ + set(x = 0, y = x) { + if (this._x !== x || this._y !== y) { + this._x = x; + this._y = y; + this._observer._onUpdate(this); + } + return this; + } + /** + * Copies x and y from the given point into this point. + * @example + * ```ts + * // Basic copying + * const source = new ObservablePoint(observer, 100, 200); + * const target = new ObservablePoint(); + * target.copyFrom(source); + * + * // Copy and chain operations + * const point = new ObservablePoint() + * .copyFrom(source) + * .set(x + 50, y + 50); + * + * // Copy from any PointData + * const data = { x: 10, y: 20 }; + * point.copyFrom(data); + * ``` + * @param p - The point to copy from + * @returns The point instance itself + * @see {@link ObservablePoint.copyTo} For copying to another point + * @see {@link ObservablePoint.clone} For creating new point copy + */ + copyFrom(p) { + if (this._x !== p.x || this._y !== p.y) { + this._x = p.x; + this._y = p.y; + this._observer._onUpdate(this); + } + return this; + } + /** + * Copies this point's x and y into the given point. + * @example + * ```ts + * // Basic copying + * const source = new ObservablePoint(100, 200); + * const target = new ObservablePoint(); + * source.copyTo(target); + * ``` + * @param p - The point to copy to. Can be any type that is or extends `PointLike` + * @returns The point (`p`) with values updated + * @see {@link ObservablePoint.copyFrom} For copying from another point + * @see {@link ObservablePoint.clone} For creating new point copy + */ + copyTo(p) { + p.set(this._x, this._y); + return p; + } + /** + * Checks if another point is equal to this point. + * + * Compares x and y values using strict equality. + * @example + * ```ts + * // Basic equality check + * const p1 = new ObservablePoint(100, 200); + * const p2 = new ObservablePoint(100, 200); + * console.log(p1.equals(p2)); // true + * + * // Compare with PointData + * const data = { x: 100, y: 200 }; + * console.log(p1.equals(data)); // true + * + * // Check different points + * const p3 = new ObservablePoint(200, 300); + * console.log(p1.equals(p3)); // false + * ``` + * @param p - The point to check + * @returns `true` if both `x` and `y` are equal + * @see {@link ObservablePoint.copyFrom} For making points equal + * @see {@link PointData} For point data interface + */ + equals(p) { + return p.x === this._x && p.y === this._y; + } + toString() { + return `[pixi.js/math:ObservablePoint x=${this._x} y=${this._y} scope=${this._observer}]`; + } + /** + * Position of the observable point on the x axis. + * Triggers observer callback when value changes. + * @example + * ```ts + * // Basic x position + * const point = new ObservablePoint(observer); + * point.x = 100; // Triggers observer + * + * // Use in calculations + * const width = rightPoint.x - leftPoint.x; + * ``` + * @default 0 + */ + get x() { + return this._x; + } + set x(value) { + if (this._x !== value) { + this._x = value; + this._observer._onUpdate(this); + } + } + /** + * Position of the observable point on the y axis. + * Triggers observer callback when value changes. + * @example + * ```ts + * // Basic y position + * const point = new ObservablePoint(observer); + * point.y = 200; // Triggers observer + * + * // Use in calculations + * const height = bottomPoint.y - topPoint.y; + * ``` + * @default 0 + */ + get y() { + return this._y; + } + set y(value) { + if (this._y !== value) { + this._y = value; + this._observer._onUpdate(this); + } + } + } + + "use strict"; + const uidCache = { + default: -1 + }; + function uid$1(name = "default") { + if (uidCache[name] === void 0) { + uidCache[name] = -1; + } + return ++uidCache[name]; + } + function resetUids() { + for (const key in uidCache) { + delete uidCache[key]; + } + } + + "use strict"; + const warnings = /* @__PURE__ */ new Set(); + const v8_0_0 = "8.0.0"; + const v8_3_4 = "8.3.4"; + const deprecationState = { + quiet: false, + noColor: false + }; + const deprecation = ((version, message, ignoreDepth = 3) => { + if (deprecationState.quiet || warnings.has(message)) return; + let stack = new Error().stack; + const deprecationMessage = `${message} +Deprecated since v${version}`; + const useGroup = typeof console.groupCollapsed === "function" && !deprecationState.noColor; + if (typeof stack === "undefined") { + console.warn("PixiJS Deprecation Warning: ", deprecationMessage); + } else { + stack = stack.split("\n").splice(ignoreDepth).join("\n"); + if (useGroup) { + console.groupCollapsed( + "%cPixiJS Deprecation Warning: %c%s", + "color:#614108;background:#fffbe6", + "font-weight:normal;color:#614108;background:#fffbe6", + deprecationMessage + ); + console.warn(stack); + console.groupEnd(); + } else { + console.warn("PixiJS Deprecation Warning: ", deprecationMessage); + console.warn(stack); + } + } + warnings.add(message); + }); + Object.defineProperties(deprecation, { + quiet: { + get: () => deprecationState.quiet, + set: (value) => { + deprecationState.quiet = value; + }, + enumerable: true, + configurable: false + }, + noColor: { + get: () => deprecationState.noColor, + set: (value) => { + deprecationState.noColor = value; + }, + enumerable: true, + configurable: false + } + }); + + "use strict"; + let warnCount = 0; + const maxWarnings = 500; + function warn(...args) { + if (warnCount === maxWarnings) return; + warnCount++; + if (warnCount === maxWarnings) { + console.warn("PixiJS Warning: too many warnings, no more warnings will be reported to the console by PixiJS."); + } else { + console.warn("PixiJS Warning: ", ...args); + } + } + + "use strict"; + const GlobalResourceRegistry = { + /** + * Set of registered pools and cleanable objects. + * @private + */ + _registeredResources: /* @__PURE__ */ new Set(), + /** + * Registers a pool or cleanable object for cleanup. + * @param {Cleanable} pool - The pool or object to register. + */ + register(pool) { + this._registeredResources.add(pool); + }, + /** + * Unregisters a pool or cleanable object from cleanup. + * @param {Cleanable} pool - The pool or object to unregister. + */ + unregister(pool) { + this._registeredResources.delete(pool); + }, + /** Clears all registered pools and cleanable objects. This will call clear() on each registered item. */ + release() { + this._registeredResources.forEach((pool) => pool.clear()); + }, + /** + * Gets the number of registered pools and cleanable objects. + * @returns {number} The count of registered items. + */ + get registeredCount() { + return this._registeredResources.size; + }, + /** + * Checks if a specific pool or cleanable object is registered. + * @param {Cleanable} pool - The pool or object to check. + * @returns {boolean} True if the item is registered, false otherwise. + */ + isRegistered(pool) { + return this._registeredResources.has(pool); + }, + /** + * Removes all registrations without clearing the pools. + * Useful if you want to reset the collector without affecting the pools. + */ + reset() { + this._registeredResources.clear(); + } + }; + + "use strict"; + class Pool { + /** + * Constructs a new Pool. + * @param ClassType - The constructor of the items in the pool. + * @param {number} [initialSize] - The initial size of the pool. + */ + constructor(ClassType, initialSize) { + this._pool = []; + this._count = 0; + this._index = 0; + this._classType = ClassType; + if (initialSize) { + this.prepopulate(initialSize); + } + } + /** + * Prepopulates the pool with a given number of items. + * @param total - The number of items to add to the pool. + */ + prepopulate(total) { + for (let i = 0; i < total; i++) { + this._pool[this._index++] = new this._classType(); + } + this._count += total; + } + /** + * Gets an item from the pool. Calls the item's `init` method if it exists. + * If there are no items left in the pool, a new one will be created. + * @param {I} [data] - Optional data to pass to the item's constructor. + * @returns {T} The item from the pool. + */ + get(data) { + var _a; + let item; + if (this._index > 0) { + item = this._pool[--this._index]; + } else { + item = new this._classType(); + this._count++; + } + (_a = item.init) == null ? void 0 : _a.call(item, data); + return item; + } + /** + * Returns an item to the pool. Calls the item's `reset` method if it exists. + * @param {T} item - The item to return to the pool. + */ + return(item) { + var _a; + (_a = item.reset) == null ? void 0 : _a.call(item); + this._pool[this._index++] = item; + } + /** + * Gets the number of items in the pool. + * @readonly + */ + get totalSize() { + return this._count; + } + /** + * Gets the number of items in the pool that are free to use without needing to create more. + * @readonly + */ + get totalFree() { + return this._index; + } + /** + * Gets the number of items in the pool that are currently in use. + * @readonly + */ + get totalUsed() { + return this._count - this._index; + } + /** clears the pool */ + clear() { + if (this._pool.length > 0 && this._pool[0].destroy) { + for (let i = 0; i < this._index; i++) { + this._pool[i].destroy(); + } + } + this._pool.length = 0; + this._count = 0; + this._index = 0; + } + } + + "use strict"; + class PoolGroupClass { + constructor() { + /** + * A map to store the pools by their class type. + * @private + */ + this._poolsByClass = /* @__PURE__ */ new Map(); + } + /** + * Prepopulates a specific pool with a given number of items. + * @template T The type of items in the pool. Must extend PoolItem. + * @param {PoolItemConstructor} Class - The constructor of the items in the pool. + * @param {number} total - The number of items to add to the pool. + */ + prepopulate(Class, total) { + const classPool = this.getPool(Class); + classPool.prepopulate(total); + } + /** + * Gets an item from a specific pool. + * @template T The type of items in the pool. Must extend PoolItem. + * @param {PoolItemConstructor} Class - The constructor of the items in the pool. + * @param {unknown} [data] - Optional data to pass to the item's constructor. + * @returns {T} The item from the pool. + */ + get(Class, data) { + const pool = this.getPool(Class); + return pool.get(data); + } + /** + * Returns an item to its respective pool. + * @param {PoolItem} item - The item to return to the pool. + */ + return(item) { + const pool = this.getPool(item.constructor); + pool.return(item); + } + /** + * Gets a specific pool based on the class type. + * @template T The type of items in the pool. Must extend PoolItem. + * @param {PoolItemConstructor} ClassType - The constructor of the items in the pool. + * @returns {Pool} The pool of the given class type. + */ + getPool(ClassType) { + if (!this._poolsByClass.has(ClassType)) { + this._poolsByClass.set(ClassType, new Pool(ClassType)); + } + return this._poolsByClass.get(ClassType); + } + /** gets the usage stats of each pool in the system */ + stats() { + const stats = {}; + this._poolsByClass.forEach((pool) => { + const name = stats[pool._classType.name] ? pool._classType.name + pool._classType.ID : pool._classType.name; + stats[name] = { + free: pool.totalFree, + used: pool.totalUsed, + size: pool.totalSize + }; + }); + return stats; + } + /** Clears all pools in the group. This will reset all pools and free their resources. */ + clear() { + this._poolsByClass.forEach((pool) => pool.clear()); + this._poolsByClass.clear(); + } + } + const BigPool = new PoolGroupClass(); + GlobalResourceRegistry.register(BigPool); + + "use strict"; + const cacheAsTextureMixin = { + get isCachedAsTexture() { + var _a; + return !!((_a = this.renderGroup) == null ? void 0 : _a.isCachedAsTexture); + }, + cacheAsTexture(val) { + if (typeof val === "boolean" && val === false) { + this.disableRenderGroup(); + } else { + this.enableRenderGroup(); + this.renderGroup.enableCacheAsTexture(val === true ? {} : val); + } + }, + updateCacheTexture() { + var _a; + (_a = this.renderGroup) == null ? void 0 : _a.updateCacheTexture(); + }, + get cacheAsBitmap() { + return this.isCachedAsTexture; + }, + set cacheAsBitmap(val) { + deprecation("v8.6.0", "cacheAsBitmap is deprecated, use cacheAsTexture instead."); + this.cacheAsTexture(val); + } + }; + + "use strict"; + function removeItems(arr, startIdx, removeCount) { + const length = arr.length; + let i; + if (startIdx >= length || removeCount === 0) { + return; + } + removeCount = startIdx + removeCount > length ? length - startIdx : removeCount; + const len = length - removeCount; + for (i = startIdx; i < len; ++i) { + arr[i] = arr[i + removeCount]; + } + arr.length = len; + } + + "use strict"; + const childrenHelperMixin = { + allowChildren: true, + removeChildren(beginIndex = 0, endIndex) { + var _a; + const end = endIndex != null ? endIndex : this.children.length; + const range = end - beginIndex; + const removed = []; + if (range > 0 && range <= end) { + for (let i = end - 1; i >= beginIndex; i--) { + const child = this.children[i]; + if (!child) continue; + removed.push(child); + child.parent = null; + } + removeItems(this.children, beginIndex, end); + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.removeChildren(removed); + } + for (let i = 0; i < removed.length; ++i) { + const child = removed[i]; + (_a = child.parentRenderLayer) == null ? void 0 : _a.detach(child); + this.emit("childRemoved", child, this, i); + removed[i].emit("removed", this); + } + if (removed.length > 0) { + this._didViewChangeTick++; + } + return removed; + } else if (range === 0 && this.children.length === 0) { + return removed; + } + throw new RangeError("removeChildren: numeric values are outside the acceptable range."); + }, + removeChildAt(index) { + const child = this.getChildAt(index); + return this.removeChild(child); + }, + getChildAt(index) { + if (index < 0 || index >= this.children.length) { + throw new Error(`getChildAt: Index (${index}) does not exist.`); + } + return this.children[index]; + }, + setChildIndex(child, index) { + if (index < 0 || index >= this.children.length) { + throw new Error(`The index ${index} supplied is out of bounds ${this.children.length}`); + } + this.getChildIndex(child); + this.addChildAt(child, index); + }, + getChildIndex(child) { + const index = this.children.indexOf(child); + if (index === -1) { + throw new Error("The supplied Container must be a child of the caller"); + } + return index; + }, + addChildAt(child, index) { + if (!this.allowChildren) { + deprecation(v8_0_0, "addChildAt: Only Containers will be allowed to add children in v8.0.0"); + } + const { children } = this; + if (index < 0 || index > children.length) { + throw new Error(`${child}addChildAt: The index ${index} supplied is out of bounds ${children.length}`); + } + if (child.parent) { + const currentIndex = child.parent.children.indexOf(child); + if (child.parent === this && currentIndex === index) { + return child; + } + if (currentIndex !== -1) { + child.parent.children.splice(currentIndex, 1); + } + } + if (index === children.length) { + children.push(child); + } else { + children.splice(index, 0, child); + } + child.parent = this; + child.didChange = true; + child._updateFlags = 15; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.addChild(child); + } + if (this.sortableChildren) this.sortDirty = true; + this.emit("childAdded", child, this, index); + child.emit("added", this); + return child; + }, + swapChildren(child, child2) { + if (child === child2) { + return; + } + const index1 = this.getChildIndex(child); + const index2 = this.getChildIndex(child2); + this.children[index1] = child2; + this.children[index2] = child; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.structureDidChange = true; + } + this._didContainerChangeTick++; + }, + removeFromParent() { + var _a; + (_a = this.parent) == null ? void 0 : _a.removeChild(this); + }, + reparentChild(...child) { + if (child.length === 1) { + return this.reparentChildAt(child[0], this.children.length); + } + child.forEach((c) => this.reparentChildAt(c, this.children.length)); + return child[0]; + }, + reparentChildAt(child, index) { + if (child.parent === this) { + this.setChildIndex(child, index); + return child; + } + const childMat = child.worldTransform.clone(); + child.removeFromParent(); + this.addChildAt(child, index); + const newMatrix = this.worldTransform.clone(); + newMatrix.invert(); + childMat.prepend(newMatrix); + child.setFromMatrix(childMat); + return child; + }, + replaceChild(oldChild, newChild) { + oldChild.updateLocalTransform(); + this.addChildAt(newChild, this.getChildIndex(oldChild)); + newChild.setFromMatrix(oldChild.localTransform); + newChild.updateLocalTransform(); + this.removeChild(oldChild); + } + }; + + "use strict"; + const collectRenderablesMixin = { + collectRenderables(instructionSet, renderer, currentLayer) { + if (this.parentRenderLayer && this.parentRenderLayer !== currentLayer || this.globalDisplayStatus < 7 || !this.includeInBuild) return; + if (this.sortableChildren) { + this.sortChildren(); + } + if (this.isSimple) { + this.collectRenderablesSimple(instructionSet, renderer, currentLayer); + } else if (this.renderGroup) { + renderer.renderPipes.renderGroup.addRenderGroup(this.renderGroup, instructionSet); + } else { + this.collectRenderablesWithEffects(instructionSet, renderer, currentLayer); + } + }, + collectRenderablesSimple(instructionSet, renderer, currentLayer) { + const children = this.children; + const length = children.length; + for (let i = 0; i < length; i++) { + children[i].collectRenderables(instructionSet, renderer, currentLayer); + } + }, + collectRenderablesWithEffects(instructionSet, renderer, currentLayer) { + const { renderPipes } = renderer; + for (let i = 0; i < this.effects.length; i++) { + const effect = this.effects[i]; + const pipe = renderPipes[effect.pipe]; + pipe.push(effect, this, instructionSet); + } + this.collectRenderablesSimple(instructionSet, renderer, currentLayer); + for (let i = this.effects.length - 1; i >= 0; i--) { + const effect = this.effects[i]; + const pipe = renderPipes[effect.pipe]; + pipe.pop(effect, this, instructionSet); + } + } + }; + + "use strict"; + class FilterEffect { + constructor() { + /** the pipe that knows how to handle this effect */ + this.pipe = "filter"; + /** the priority of this effect */ + this.priority = 1; + } + destroy() { + for (let i = 0; i < this.filters.length; i++) { + this.filters[i].destroy(); + } + this.filters = null; + this.filterArea = null; + } + } + + "use strict"; + class MaskEffectManagerClass { + constructor() { + /** @private */ + this._effectClasses = []; + this._tests = []; + this._initialized = false; + } + init() { + if (this._initialized) return; + this._initialized = true; + this._effectClasses.forEach((test) => { + this.add({ + test: test.test, + maskClass: test + }); + }); + } + add(test) { + this._tests.push(test); + } + getMaskEffect(item) { + if (!this._initialized) this.init(); + for (let i = 0; i < this._tests.length; i++) { + const test = this._tests[i]; + if (test.test(item)) { + return BigPool.get(test.maskClass, item); + } + } + return item; + } + returnMaskEffect(effect) { + BigPool.return(effect); + } + } + const MaskEffectManager = new MaskEffectManagerClass(); + extensions.handleByList(ExtensionType.MaskEffect, MaskEffectManager._effectClasses); + + "use strict"; + var __defProp$1k = Object.defineProperty; + var __getOwnPropSymbols$1m = Object.getOwnPropertySymbols; + var __hasOwnProp$1m = Object.prototype.hasOwnProperty; + var __propIsEnum$1m = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1k = (obj, key, value) => key in obj ? __defProp$1k(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1k = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1m.call(b, prop)) + __defNormalProp$1k(a, prop, b[prop]); + if (__getOwnPropSymbols$1m) + for (var prop of __getOwnPropSymbols$1m(b)) { + if (__propIsEnum$1m.call(b, prop)) + __defNormalProp$1k(a, prop, b[prop]); + } + return a; + }; + const effectsMixin = { + _maskEffect: null, + _maskOptions: { + inverse: false + }, + _filterEffect: null, + effects: [], + _markStructureAsChanged() { + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.structureDidChange = true; + } + }, + addEffect(effect) { + const index = this.effects.indexOf(effect); + if (index !== -1) return; + this.effects.push(effect); + this.effects.sort((a, b) => a.priority - b.priority); + this._markStructureAsChanged(); + this._updateIsSimple(); + }, + removeEffect(effect) { + const index = this.effects.indexOf(effect); + if (index === -1) return; + this.effects.splice(index, 1); + this._markStructureAsChanged(); + this._updateIsSimple(); + }, + set mask(value) { + const effect = this._maskEffect; + if ((effect == null ? void 0 : effect.mask) === value) return; + if (effect) { + this.removeEffect(effect); + MaskEffectManager.returnMaskEffect(effect); + this._maskEffect = null; + } + if (value === null || value === void 0) return; + this._maskEffect = MaskEffectManager.getMaskEffect(value); + this.addEffect(this._maskEffect); + }, + get mask() { + var _a; + return (_a = this._maskEffect) == null ? void 0 : _a.mask; + }, + setMask(options) { + this._maskOptions = __spreadValues$1k(__spreadValues$1k({}, this._maskOptions), options); + if (options.mask) { + this.mask = options.mask; + } + this._markStructureAsChanged(); + }, + set filters(value) { + var _a; + if (!Array.isArray(value) && value) value = [value]; + const effect = this._filterEffect || (this._filterEffect = new FilterEffect()); + value = value; + const hasFilters = (value == null ? void 0 : value.length) > 0; + const hadFilters = ((_a = effect.filters) == null ? void 0 : _a.length) > 0; + const didChange = hasFilters !== hadFilters; + value = Array.isArray(value) ? value.slice(0) : value; + effect.filters = Object.freeze(value); + if (didChange) { + if (hasFilters) { + this.addEffect(effect); + } else { + this.removeEffect(effect); + effect.filters = value != null ? value : null; + } + } + }, + get filters() { + var _a; + return (_a = this._filterEffect) == null ? void 0 : _a.filters; + }, + set filterArea(value) { + this._filterEffect || (this._filterEffect = new FilterEffect()); + this._filterEffect.filterArea = value; + }, + get filterArea() { + var _a; + return (_a = this._filterEffect) == null ? void 0 : _a.filterArea; + } + }; + + "use strict"; + const findMixin = { + label: null, + get name() { + deprecation(v8_0_0, "Container.name property has been removed, use Container.label instead"); + return this.label; + }, + set name(value) { + deprecation(v8_0_0, "Container.name property has been removed, use Container.label instead"); + this.label = value; + }, + getChildByName(name, deep = false) { + return this.getChildByLabel(name, deep); + }, + getChildByLabel(label, deep = false) { + const children = this.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.label === label || label instanceof RegExp && label.test(child.label)) return child; + } + if (deep) { + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const found = child.getChildByLabel(label, true); + if (found) { + return found; + } + } + } + return null; + }, + getChildrenByLabel(label, deep = false, out = []) { + const children = this.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.label === label || label instanceof RegExp && label.test(child.label)) { + out.push(child); + } + } + if (deep) { + for (let i = 0; i < children.length; i++) { + children[i].getChildrenByLabel(label, true, out); + } + } + return out; + } + }; + + "use strict"; + const tempPoints = [new Point(), new Point(), new Point(), new Point()]; + class Rectangle { + /** + * @param x - The X coordinate of the upper-left corner of the rectangle + * @param y - The Y coordinate of the upper-left corner of the rectangle + * @param width - The overall width of the rectangle + * @param height - The overall height of the rectangle + */ + constructor(x = 0, y = 0, width = 0, height = 0) { + /** + * The type of the object, mainly used to avoid `instanceof` checks + * @example + * ```ts + * // Check shape type + * const shape = new Rectangle(0, 0, 100, 100); + * console.log(shape.type); // 'rectangle' + * + * // Use in type guards + * if (shape.type === 'rectangle') { + * console.log(shape.width, shape.height); + * } + * ``` + * @readonly + * @default 'rectangle' + * @see {@link SHAPE_PRIMITIVE} For all shape types + */ + this.type = "rectangle"; + this.x = Number(x); + this.y = Number(y); + this.width = Number(width); + this.height = Number(height); + } + /** + * Returns the left edge (x-coordinate) of the rectangle. + * @example + * ```ts + * // Get left edge position + * const rect = new Rectangle(100, 100, 200, 150); + * console.log(rect.left); // 100 + * + * // Use in alignment calculations + * sprite.x = rect.left + padding; + * + * // Compare positions + * if (point.x > rect.left) { + * console.log('Point is right of rectangle'); + * } + * ``` + * @readonly + * @returns The x-coordinate of the left edge + * @see {@link Rectangle.right} For right edge position + * @see {@link Rectangle.x} For direct x-coordinate access + */ + get left() { + return this.x; + } + /** + * Returns the right edge (x + width) of the rectangle. + * @example + * ```ts + * // Get right edge position + * const rect = new Rectangle(100, 100, 200, 150); + * console.log(rect.right); // 300 + * + * // Align to right edge + * sprite.x = rect.right - sprite.width; + * + * // Check boundaries + * if (point.x < rect.right) { + * console.log('Point is inside right bound'); + * } + * ``` + * @readonly + * @returns The x-coordinate of the right edge + * @see {@link Rectangle.left} For left edge position + * @see {@link Rectangle.width} For width value + */ + get right() { + return this.x + this.width; + } + /** + * Returns the top edge (y-coordinate) of the rectangle. + * @example + * ```ts + * // Get top edge position + * const rect = new Rectangle(100, 100, 200, 150); + * console.log(rect.top); // 100 + * + * // Position above rectangle + * sprite.y = rect.top - sprite.height; + * + * // Check vertical position + * if (point.y > rect.top) { + * console.log('Point is below top edge'); + * } + * ``` + * @readonly + * @returns The y-coordinate of the top edge + * @see {@link Rectangle.bottom} For bottom edge position + * @see {@link Rectangle.y} For direct y-coordinate access + */ + get top() { + return this.y; + } + /** + * Returns the bottom edge (y + height) of the rectangle. + * @example + * ```ts + * // Get bottom edge position + * const rect = new Rectangle(100, 100, 200, 150); + * console.log(rect.bottom); // 250 + * + * // Stack below rectangle + * sprite.y = rect.bottom + margin; + * + * // Check vertical bounds + * if (point.y < rect.bottom) { + * console.log('Point is above bottom edge'); + * } + * ``` + * @readonly + * @returns The y-coordinate of the bottom edge + * @see {@link Rectangle.top} For top edge position + * @see {@link Rectangle.height} For height value + */ + get bottom() { + return this.y + this.height; + } + /** + * Determines whether the Rectangle is empty (has no area). + * @example + * ```ts + * // Check zero dimensions + * const rect = new Rectangle(100, 100, 0, 50); + * console.log(rect.isEmpty()); // true + * ``` + * @returns True if the rectangle has no area + * @see {@link Rectangle.width} For width value + * @see {@link Rectangle.height} For height value + */ + isEmpty() { + return this.left === this.right || this.top === this.bottom; + } + /** + * A constant empty rectangle. This is a new object every time the property is accessed. + * @example + * ```ts + * // Get fresh empty rectangle + * const empty = Rectangle.EMPTY; + * console.log(empty.isEmpty()); // true + * ``` + * @returns A new empty rectangle instance + * @see {@link Rectangle.isEmpty} For empty state testing + */ + static get EMPTY() { + return new Rectangle(0, 0, 0, 0); + } + /** + * Creates a clone of this Rectangle + * @example + * ```ts + * // Basic cloning + * const original = new Rectangle(100, 100, 200, 150); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.width *= 2; + * modified.height += 50; + * + * // Verify independence + * console.log(original.width); // 200 + * console.log(modified.width); // 400 + * ``` + * @returns A copy of the rectangle + * @see {@link Rectangle.copyFrom} For copying into existing rectangle + * @see {@link Rectangle.copyTo} For copying to another rectangle + */ + clone() { + return new Rectangle(this.x, this.y, this.width, this.height); + } + /** + * Converts a Bounds object to a Rectangle object. + * @example + * ```ts + * // Convert bounds to rectangle + * const bounds = container.getBounds(); + * const rect = new Rectangle().copyFromBounds(bounds); + * ``` + * @param bounds - The bounds to copy and convert to a rectangle + * @returns Returns itself + * @see {@link Bounds} For bounds object structure + * @see {@link Rectangle.getBounds} For getting rectangle bounds + */ + copyFromBounds(bounds) { + this.x = bounds.minX; + this.y = bounds.minY; + this.width = bounds.maxX - bounds.minX; + this.height = bounds.maxY - bounds.minY; + return this; + } + /** + * Copies another rectangle to this one. + * @example + * ```ts + * // Basic copying + * const source = new Rectangle(100, 100, 200, 150); + * const target = new Rectangle(); + * target.copyFrom(source); + * + * // Chain with other operations + * const rect = new Rectangle() + * .copyFrom(source) + * .pad(10); + * ``` + * @param rectangle - The rectangle to copy from + * @returns Returns itself + * @see {@link Rectangle.copyTo} For copying to another rectangle + * @see {@link Rectangle.clone} For creating new rectangle copy + */ + copyFrom(rectangle) { + this.x = rectangle.x; + this.y = rectangle.y; + this.width = rectangle.width; + this.height = rectangle.height; + return this; + } + /** + * Copies this rectangle to another one. + * @example + * ```ts + * // Basic copying + * const source = new Rectangle(100, 100, 200, 150); + * const target = new Rectangle(); + * source.copyTo(target); + * + * // Chain with other operations + * const result = source + * .copyTo(new Rectangle()) + * .getBounds(); + * ``` + * @param rectangle - The rectangle to copy to + * @returns Returns given parameter + * @see {@link Rectangle.copyFrom} For copying from another rectangle + * @see {@link Rectangle.clone} For creating new rectangle copy + */ + copyTo(rectangle) { + rectangle.copyFrom(this); + return rectangle; + } + /** + * Checks whether the x and y coordinates given are contained within this Rectangle + * @example + * ```ts + * // Basic containment check + * const rect = new Rectangle(100, 100, 200, 150); + * const isInside = rect.contains(150, 125); // true + * // Check edge cases + * console.log(rect.contains(100, 100)); // true (on edge) + * console.log(rect.contains(300, 250)); // false (outside) + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coordinates are within this Rectangle + * @see {@link Rectangle.containsRect} For rectangle containment + * @see {@link Rectangle.strokeContains} For checking stroke intersection + */ + contains(x, y) { + if (this.width <= 0 || this.height <= 0) { + return false; + } + if (x >= this.x && x < this.x + this.width) { + if (y >= this.y && y < this.y + this.height) { + return true; + } + } + return false; + } + /** + * Checks whether the x and y coordinates given are contained within this rectangle including the stroke. + * @example + * ```ts + * // Basic stroke check + * const rect = new Rectangle(100, 100, 200, 150); + * const isOnStroke = rect.strokeContains(150, 100, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = rect.strokeContains(150, 100, 4, 1); // Inside + * const centerStroke = rect.strokeContains(150, 100, 4, 0.5); // Centered + * const outerStroke = rect.strokeContains(150, 100, 4, 0); // Outside + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @param strokeWidth - The width of the line to check + * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer) + * @returns Whether the x/y coordinates are within this rectangle's stroke + * @see {@link Rectangle.contains} For checking fill containment + * @see {@link Rectangle.getBounds} For getting stroke bounds + */ + strokeContains(x, y, strokeWidth, alignment = 0.5) { + const { width, height } = this; + if (width <= 0 || height <= 0) return false; + const _x = this.x; + const _y = this.y; + const strokeWidthOuter = strokeWidth * (1 - alignment); + const strokeWidthInner = strokeWidth - strokeWidthOuter; + const outerLeft = _x - strokeWidthOuter; + const outerRight = _x + width + strokeWidthOuter; + const outerTop = _y - strokeWidthOuter; + const outerBottom = _y + height + strokeWidthOuter; + const innerLeft = _x + strokeWidthInner; + const innerRight = _x + width - strokeWidthInner; + const innerTop = _y + strokeWidthInner; + const innerBottom = _y + height - strokeWidthInner; + return x >= outerLeft && x <= outerRight && y >= outerTop && y <= outerBottom && !(x > innerLeft && x < innerRight && y > innerTop && y < innerBottom); + } + /** + * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object. + * Returns true only if the area of the intersection is >0, this means that Rectangles + * sharing a side are not overlapping. Another side effect is that an arealess rectangle + * (width or height equal to zero) can't intersect any other rectangle. + * @param {Rectangle} other - The Rectangle to intersect with `this`. + * @param {Matrix} transform - The transformation matrix of `other`. + * @returns {boolean} A value of `true` if the transformed `other` Rectangle intersects with `this`; otherwise `false`. + */ + /** + * Determines whether the `other` Rectangle transformed by `transform` intersects with `this` Rectangle object. + * + * Returns true only if the area of the intersection is greater than 0. + * This means that rectangles sharing only a side are not considered intersecting. + * @example + * ```ts + * // Basic intersection check + * const rect1 = new Rectangle(0, 0, 100, 100); + * const rect2 = new Rectangle(50, 50, 100, 100); + * console.log(rect1.intersects(rect2)); // true + * + * // With transformation matrix + * const matrix = new Matrix(); + * matrix.rotate(Math.PI / 4); // 45 degrees + * console.log(rect1.intersects(rect2, matrix)); // Checks with rotation + * + * // Edge cases + * const zeroWidth = new Rectangle(0, 0, 0, 100); + * console.log(rect1.intersects(zeroWidth)); // false (no area) + * ``` + * @remarks + * - Returns true only if intersection area is > 0 + * - Rectangles sharing only a side are not intersecting + * - Zero-area rectangles cannot intersect anything + * - Supports optional transformation matrix + * @param other - The Rectangle to intersect with `this` + * @param transform - Optional transformation matrix of `other` + * @returns True if the transformed `other` Rectangle intersects with `this` + * @see {@link Rectangle.containsRect} For containment testing + * @see {@link Rectangle.contains} For point testing + */ + intersects(other, transform) { + if (!transform) { + const x02 = this.x < other.x ? other.x : this.x; + const x12 = this.right > other.right ? other.right : this.right; + if (x12 <= x02) { + return false; + } + const y02 = this.y < other.y ? other.y : this.y; + const y12 = this.bottom > other.bottom ? other.bottom : this.bottom; + return y12 > y02; + } + const x0 = this.left; + const x1 = this.right; + const y0 = this.top; + const y1 = this.bottom; + if (x1 <= x0 || y1 <= y0) { + return false; + } + const lt = tempPoints[0].set(other.left, other.top); + const lb = tempPoints[1].set(other.left, other.bottom); + const rt = tempPoints[2].set(other.right, other.top); + const rb = tempPoints[3].set(other.right, other.bottom); + if (rt.x <= lt.x || lb.y <= lt.y) { + return false; + } + const s = Math.sign(transform.a * transform.d - transform.b * transform.c); + if (s === 0) { + return false; + } + transform.apply(lt, lt); + transform.apply(lb, lb); + transform.apply(rt, rt); + transform.apply(rb, rb); + if (Math.max(lt.x, lb.x, rt.x, rb.x) <= x0 || Math.min(lt.x, lb.x, rt.x, rb.x) >= x1 || Math.max(lt.y, lb.y, rt.y, rb.y) <= y0 || Math.min(lt.y, lb.y, rt.y, rb.y) >= y1) { + return false; + } + const nx = s * (lb.y - lt.y); + const ny = s * (lt.x - lb.x); + const n00 = nx * x0 + ny * y0; + const n10 = nx * x1 + ny * y0; + const n01 = nx * x0 + ny * y1; + const n11 = nx * x1 + ny * y1; + if (Math.max(n00, n10, n01, n11) <= nx * lt.x + ny * lt.y || Math.min(n00, n10, n01, n11) >= nx * rb.x + ny * rb.y) { + return false; + } + const mx = s * (lt.y - rt.y); + const my = s * (rt.x - lt.x); + const m00 = mx * x0 + my * y0; + const m10 = mx * x1 + my * y0; + const m01 = mx * x0 + my * y1; + const m11 = mx * x1 + my * y1; + if (Math.max(m00, m10, m01, m11) <= mx * lt.x + my * lt.y || Math.min(m00, m10, m01, m11) >= mx * rb.x + my * rb.y) { + return false; + } + return true; + } + /** + * Pads the rectangle making it grow in all directions. + * + * If paddingY is omitted, both paddingX and paddingY will be set to paddingX. + * @example + * ```ts + * // Basic padding + * const rect = new Rectangle(100, 100, 200, 150); + * rect.pad(10); // Adds 10px padding on all sides + * + * // Different horizontal and vertical padding + * const uiRect = new Rectangle(0, 0, 100, 50); + * uiRect.pad(20, 10); // 20px horizontal, 10px vertical + * ``` + * @remarks + * - Adjusts x/y by subtracting padding + * - Increases width/height by padding * 2 + * - Common in UI layout calculations + * - Chainable with other methods + * @param paddingX - The horizontal padding amount + * @param paddingY - The vertical padding amount + * @returns Returns itself + * @see {@link Rectangle.enlarge} For growing to include another rectangle + * @see {@link Rectangle.fit} For shrinking to fit within another rectangle + */ + pad(paddingX = 0, paddingY = paddingX) { + this.x -= paddingX; + this.y -= paddingY; + this.width += paddingX * 2; + this.height += paddingY * 2; + return this; + } + /** + * Fits this rectangle around the passed one. + * @example + * ```ts + * // Basic fitting + * const container = new Rectangle(0, 0, 100, 100); + * const content = new Rectangle(25, 25, 200, 200); + * content.fit(container); // Clips to container bounds + * ``` + * @param rectangle - The rectangle to fit around + * @returns Returns itself + * @see {@link Rectangle.enlarge} For growing to include another rectangle + * @see {@link Rectangle.pad} For adding padding around the rectangle + */ + fit(rectangle) { + const x1 = Math.max(this.x, rectangle.x); + const x2 = Math.min(this.x + this.width, rectangle.x + rectangle.width); + const y1 = Math.max(this.y, rectangle.y); + const y2 = Math.min(this.y + this.height, rectangle.y + rectangle.height); + this.x = x1; + this.width = Math.max(x2 - x1, 0); + this.y = y1; + this.height = Math.max(y2 - y1, 0); + return this; + } + /** + * Enlarges rectangle so that its corners lie on a grid defined by resolution. + * @example + * ```ts + * // Basic grid alignment + * const rect = new Rectangle(10.2, 10.6, 100.8, 100.4); + * rect.ceil(); // Aligns to whole pixels + * + * // Custom resolution grid + * const uiRect = new Rectangle(5.3, 5.7, 50.2, 50.8); + * uiRect.ceil(0.5); // Aligns to half pixels + * + * // Use with precision value + * const preciseRect = new Rectangle(20.001, 20.999, 100.001, 100.999); + * preciseRect.ceil(1, 0.01); // Handles small decimal variations + * ``` + * @param resolution - The grid size to align to (1 = whole pixels) + * @param eps - Small number to prevent floating point errors + * @returns Returns itself + * @see {@link Rectangle.fit} For constraining to bounds + * @see {@link Rectangle.enlarge} For growing dimensions + */ + ceil(resolution = 1, eps = 1e-3) { + const x2 = Math.ceil((this.x + this.width - eps) * resolution) / resolution; + const y2 = Math.ceil((this.y + this.height - eps) * resolution) / resolution; + this.x = Math.floor((this.x + eps) * resolution) / resolution; + this.y = Math.floor((this.y + eps) * resolution) / resolution; + this.width = x2 - this.x; + this.height = y2 - this.y; + return this; + } + /** + * Scales the rectangle's dimensions and position by the specified factors. + * @example + * ```ts + * const rect = new Rectangle(50, 50, 100, 100); + * + * // Scale uniformly + * rect.scale(0.5, 0.5); + * // rect is now: x=25, y=25, width=50, height=50 + * + * // non-uniformly + * rect.scale(0.5, 1); + * // rect is now: x=25, y=50, width=50, height=100 + * ``` + * @param x - The factor by which to scale the horizontal properties (x, width). + * @param y - The factor by which to scale the vertical properties (y, height). + * @returns Returns itself + */ + scale(x, y = x) { + this.x *= x; + this.y *= y; + this.width *= x; + this.height *= y; + return this; + } + /** + * Enlarges this rectangle to include the passed rectangle. + * @example + * ```ts + * // Basic enlargement + * const rect = new Rectangle(50, 50, 100, 100); + * const other = new Rectangle(0, 0, 200, 75); + * rect.enlarge(other); + * // rect is now: x=0, y=0, width=200, height=150 + * + * // Use for bounding box calculation + * const bounds = new Rectangle(); + * objects.forEach((obj) => { + * bounds.enlarge(obj.getBounds()); + * }); + * ``` + * @param rectangle - The rectangle to include + * @returns Returns itself + * @see {@link Rectangle.fit} For shrinking to fit within another rectangle + * @see {@link Rectangle.pad} For adding padding around the rectangle + */ + enlarge(rectangle) { + const x1 = Math.min(this.x, rectangle.x); + const x2 = Math.max(this.x + this.width, rectangle.x + rectangle.width); + const y1 = Math.min(this.y, rectangle.y); + const y2 = Math.max(this.y + this.height, rectangle.y + rectangle.height); + this.x = x1; + this.width = x2 - x1; + this.y = y1; + this.height = y2 - y1; + return this; + } + /** + * Returns the framing rectangle of the rectangle as a Rectangle object + * @example + * ```ts + * // Basic bounds retrieval + * const rect = new Rectangle(100, 100, 200, 150); + * const bounds = rect.getBounds(); + * + * // Reuse existing rectangle + * const out = new Rectangle(); + * rect.getBounds(out); + * ``` + * @param out - Optional rectangle to store the result + * @returns The framing rectangle + * @see {@link Rectangle.copyFrom} For direct copying + * @see {@link Rectangle.clone} For creating new copy + */ + getBounds(out) { + out || (out = new Rectangle()); + out.copyFrom(this); + return out; + } + /** + * Determines whether another Rectangle is fully contained within this Rectangle. + * + * Rectangles that occupy the same space are considered to be containing each other. + * + * Rectangles without area (width or height equal to zero) can't contain anything, + * not even other arealess rectangles. + * @example + * ```ts + * // Check if one rectangle contains another + * const container = new Rectangle(0, 0, 100, 100); + * const inner = new Rectangle(25, 25, 50, 50); + * + * console.log(container.containsRect(inner)); // true + * + * // Check overlapping rectangles + * const partial = new Rectangle(75, 75, 50, 50); + * console.log(container.containsRect(partial)); // false + * + * // Zero-area rectangles + * const empty = new Rectangle(0, 0, 0, 100); + * console.log(container.containsRect(empty)); // false + * ``` + * @param other - The Rectangle to check for containment + * @returns True if other is fully contained within this Rectangle + * @see {@link Rectangle.contains} For point containment + * @see {@link Rectangle.intersects} For overlap testing + */ + containsRect(other) { + if (this.width <= 0 || this.height <= 0) return false; + const x1 = other.x; + const y1 = other.y; + const x2 = other.x + other.width; + const y2 = other.y + other.height; + return x1 >= this.x && x1 < this.x + this.width && y1 >= this.y && y1 < this.y + this.height && x2 >= this.x && x2 < this.x + this.width && y2 >= this.y && y2 < this.y + this.height; + } + /** + * Sets the position and dimensions of the rectangle. + * @example + * ```ts + * // Basic usage + * const rect = new Rectangle(); + * rect.set(100, 100, 200, 150); + * + * // Chain with other operations + * const bounds = new Rectangle() + * .set(0, 0, 100, 100) + * .pad(10); + * ``` + * @param x - The X coordinate of the upper-left corner of the rectangle + * @param y - The Y coordinate of the upper-left corner of the rectangle + * @param width - The overall width of the rectangle + * @param height - The overall height of the rectangle + * @returns Returns itself for method chaining + * @see {@link Rectangle.copyFrom} For copying from another rectangle + * @see {@link Rectangle.clone} For creating a new copy + */ + set(x, y, width, height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + return this; + } + toString() { + return `[pixi.js/math:Rectangle x=${this.x} y=${this.y} width=${this.width} height=${this.height}]`; + } + } + + "use strict"; + const defaultMatrix = new Matrix(); + class Bounds { + /** + * Creates a new Bounds object. + * @param minX - The minimum X coordinate of the bounds. + * @param minY - The minimum Y coordinate of the bounds. + * @param maxX - The maximum X coordinate of the bounds. + * @param maxY - The maximum Y coordinate of the bounds. + */ + constructor(minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity) { + /** + * The minimum X coordinate of the bounds. + * Represents the leftmost edge of the bounding box. + * @example + * ```ts + * const bounds = new Bounds(); + * // Set left edge + * bounds.minX = 100; + * ``` + * @default Infinity + */ + this.minX = Infinity; + /** + * The minimum Y coordinate of the bounds. + * Represents the topmost edge of the bounding box. + * @example + * ```ts + * const bounds = new Bounds(); + * // Set top edge + * bounds.minY = 100; + * ``` + * @default Infinity + */ + this.minY = Infinity; + /** + * The maximum X coordinate of the bounds. + * Represents the rightmost edge of the bounding box. + * @example + * ```ts + * const bounds = new Bounds(); + * // Set right edge + * bounds.maxX = 200; + * // Get width + * const width = bounds.maxX - bounds.minX; + * ``` + * @default -Infinity + */ + this.maxX = -Infinity; + /** + * The maximum Y coordinate of the bounds. + * Represents the bottommost edge of the bounding box. + * @example + * ```ts + * const bounds = new Bounds(); + * // Set bottom edge + * bounds.maxY = 200; + * // Get height + * const height = bounds.maxY - bounds.minY; + * ``` + * @default -Infinity + */ + this.maxY = -Infinity; + /** + * The transformation matrix applied to this bounds object. + * Used when calculating bounds with transforms. + * @example + * ```ts + * const bounds = new Bounds(); + * + * // Apply translation matrix + * bounds.matrix = new Matrix() + * .translate(100, 100); + * + * // Combine transformations + * bounds.matrix = new Matrix() + * .translate(50, 50) + * .rotate(Math.PI / 4) + * .scale(2, 2); + * + * // Use in bounds calculations + * bounds.addFrame(0, 0, 100, 100); // Uses current matrix + * bounds.addFrame(0, 0, 100, 100, customMatrix); // Override matrix + * ``` + * @advanced + */ + this.matrix = defaultMatrix; + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + /** + * Checks if bounds are empty, meaning either width or height is zero or negative. + * Empty bounds occur when min values exceed max values on either axis. + * @example + * ```ts + * const bounds = new Bounds(); + * + * // Check if newly created bounds are empty + * console.log(bounds.isEmpty()); // true, default bounds are empty + * + * // Add frame and check again + * bounds.addFrame(0, 0, 100, 100); + * console.log(bounds.isEmpty()); // false, bounds now have area + * + * // Clear bounds + * bounds.clear(); + * console.log(bounds.isEmpty()); // true, bounds are empty again + * ``` + * @returns True if bounds are empty (have no area) + * @see {@link Bounds#clear} For resetting bounds + * @see {@link Bounds#isValid} For checking validity + */ + isEmpty() { + return this.minX > this.maxX || this.minY > this.maxY; + } + /** + * The bounding rectangle representation of these bounds. + * Lazily creates and updates a Rectangle instance based on the current bounds. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * + * // Get rectangle representation + * const rect = bounds.rectangle; + * console.log(rect.x, rect.y, rect.width, rect.height); + * + * // Use for hit testing + * if (bounds.rectangle.contains(mouseX, mouseY)) { + * console.log('Mouse is inside bounds!'); + * } + * ``` + * @see {@link Rectangle} For rectangle methods + * @see {@link Bounds.isEmpty} For bounds validation + */ + get rectangle() { + if (!this._rectangle) { + this._rectangle = new Rectangle(); + } + const rectangle = this._rectangle; + if (this.minX > this.maxX || this.minY > this.maxY) { + rectangle.x = 0; + rectangle.y = 0; + rectangle.width = 0; + rectangle.height = 0; + } else { + rectangle.copyFromBounds(this); + } + return rectangle; + } + /** + * Clears the bounds and resets all coordinates to their default values. + * Resets the transformation matrix back to identity. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * console.log(bounds.isEmpty()); // false + * // Clear the bounds + * bounds.clear(); + * console.log(bounds.isEmpty()); // true + * ``` + * @returns This bounds object for chaining + */ + clear() { + this.minX = Infinity; + this.minY = Infinity; + this.maxX = -Infinity; + this.maxY = -Infinity; + this.matrix = defaultMatrix; + return this; + } + /** + * Sets the bounds directly using coordinate values. + * Provides a way to set all bounds values at once. + * @example + * ```ts + * const bounds = new Bounds(); + * bounds.set(0, 0, 100, 100); + * ``` + * @param x0 - Left X coordinate of frame + * @param y0 - Top Y coordinate of frame + * @param x1 - Right X coordinate of frame + * @param y1 - Bottom Y coordinate of frame + * @see {@link Bounds#addFrame} For matrix-aware bounds setting + * @see {@link Bounds#clear} For resetting bounds + */ + set(x0, y0, x1, y1) { + this.minX = x0; + this.minY = y0; + this.maxX = x1; + this.maxY = y1; + } + /** + * Adds a rectangular frame to the bounds, optionally transformed by a matrix. + * Updates the bounds to encompass the new frame coordinates. + * @example + * ```ts + * const bounds = new Bounds(); + * bounds.addFrame(0, 0, 100, 100); + * + * // Add transformed frame + * const matrix = new Matrix() + * .translate(50, 50) + * .rotate(Math.PI / 4); + * bounds.addFrame(0, 0, 100, 100, matrix); + * ``` + * @param x0 - Left X coordinate of frame + * @param y0 - Top Y coordinate of frame + * @param x1 - Right X coordinate of frame + * @param y1 - Bottom Y coordinate of frame + * @param matrix - Optional transformation matrix + * @see {@link Bounds#addRect} For adding Rectangle objects + * @see {@link Bounds#addBounds} For adding other Bounds + */ + addFrame(x0, y0, x1, y1, matrix) { + matrix || (matrix = this.matrix); + const a = matrix.a; + const b = matrix.b; + const c = matrix.c; + const d = matrix.d; + const tx = matrix.tx; + const ty = matrix.ty; + let minX = this.minX; + let minY = this.minY; + let maxX = this.maxX; + let maxY = this.maxY; + let x = a * x0 + c * y0 + tx; + let y = b * x0 + d * y0 + ty; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + x = a * x1 + c * y0 + tx; + y = b * x1 + d * y0 + ty; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + x = a * x0 + c * y1 + tx; + y = b * x0 + d * y1 + ty; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + x = a * x1 + c * y1 + tx; + y = b * x1 + d * y1 + ty; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + /** + * Adds a rectangle to the bounds, optionally transformed by a matrix. + * Updates the bounds to encompass the given rectangle. + * @example + * ```ts + * const bounds = new Bounds(); + * // Add simple rectangle + * const rect = new Rectangle(0, 0, 100, 100); + * bounds.addRect(rect); + * + * // Add transformed rectangle + * const matrix = new Matrix() + * .translate(50, 50) + * .rotate(Math.PI / 4); + * bounds.addRect(rect, matrix); + * ``` + * @param rect - The rectangle to be added + * @param matrix - Optional transformation matrix + * @see {@link Bounds#addFrame} For adding raw coordinates + * @see {@link Bounds#addBounds} For adding other bounds + */ + addRect(rect, matrix) { + this.addFrame(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, matrix); + } + /** + * Adds another bounds object to this one, optionally transformed by a matrix. + * Expands the bounds to include the given bounds' area. + * @example + * ```ts + * const bounds = new Bounds(); + * + * // Add child bounds + * const childBounds = sprite.getBounds(); + * bounds.addBounds(childBounds); + * + * // Add transformed bounds + * const matrix = new Matrix() + * .scale(2, 2); + * bounds.addBounds(childBounds, matrix); + * ``` + * @param bounds - The bounds to be added + * @param matrix - Optional transformation matrix + * @see {@link Bounds#addFrame} For adding raw coordinates + * @see {@link Bounds#addRect} For adding rectangles + */ + addBounds(bounds, matrix) { + this.addFrame(bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, matrix); + } + /** + * Adds other Bounds as a mask, creating an intersection of the two bounds. + * Only keeps the overlapping region between current bounds and mask bounds. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Create mask bounds + * const mask = new Bounds(); + * mask.addFrame(50, 50, 150, 150); + * // Apply mask - results in bounds of (50,50,100,100) + * bounds.addBoundsMask(mask); + * ``` + * @param mask - The Bounds to use as a mask + * @see {@link Bounds#addBounds} For union operation + * @see {@link Bounds#fit} For fitting to rectangle + */ + addBoundsMask(mask) { + this.minX = this.minX > mask.minX ? this.minX : mask.minX; + this.minY = this.minY > mask.minY ? this.minY : mask.minY; + this.maxX = this.maxX < mask.maxX ? this.maxX : mask.maxX; + this.maxY = this.maxY < mask.maxY ? this.maxY : mask.maxY; + } + /** + * Applies a transformation matrix to the bounds, updating its coordinates. + * Transforms all corners of the bounds using the given matrix. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Apply translation + * const translateMatrix = new Matrix() + * .translate(50, 50); + * bounds.applyMatrix(translateMatrix); + * ``` + * @param matrix - The matrix to apply to the bounds + * @see {@link Matrix} For matrix operations + * @see {@link Bounds#addFrame} For adding transformed frames + */ + applyMatrix(matrix) { + const minX = this.minX; + const minY = this.minY; + const maxX = this.maxX; + const maxY = this.maxY; + const { a, b, c, d, tx, ty } = matrix; + let x = a * minX + c * minY + tx; + let y = b * minX + d * minY + ty; + this.minX = x; + this.minY = y; + this.maxX = x; + this.maxY = y; + x = a * maxX + c * minY + tx; + y = b * maxX + d * minY + ty; + this.minX = x < this.minX ? x : this.minX; + this.minY = y < this.minY ? y : this.minY; + this.maxX = x > this.maxX ? x : this.maxX; + this.maxY = y > this.maxY ? y : this.maxY; + x = a * minX + c * maxY + tx; + y = b * minX + d * maxY + ty; + this.minX = x < this.minX ? x : this.minX; + this.minY = y < this.minY ? y : this.minY; + this.maxX = x > this.maxX ? x : this.maxX; + this.maxY = y > this.maxY ? y : this.maxY; + x = a * maxX + c * maxY + tx; + y = b * maxX + d * maxY + ty; + this.minX = x < this.minX ? x : this.minX; + this.minY = y < this.minY ? y : this.minY; + this.maxX = x > this.maxX ? x : this.maxX; + this.maxY = y > this.maxY ? y : this.maxY; + } + /** + * Resizes the bounds object to fit within the given rectangle. + * Clips the bounds if they extend beyond the rectangle's edges. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 200, 200); + * // Fit within viewport + * const viewport = new Rectangle(50, 50, 100, 100); + * bounds.fit(viewport); + * // bounds are now (50, 50, 150, 150) + * ``` + * @param rect - The rectangle to fit within + * @returns This bounds object for chaining + * @see {@link Bounds#addBoundsMask} For intersection + * @see {@link Bounds#pad} For expanding bounds + */ + fit(rect) { + if (this.minX < rect.left) this.minX = rect.left; + if (this.maxX > rect.right) this.maxX = rect.right; + if (this.minY < rect.top) this.minY = rect.top; + if (this.maxY > rect.bottom) this.maxY = rect.bottom; + return this; + } + /** + * Resizes the bounds object to include the given bounds. + * Similar to fit() but works with raw coordinate values instead of a Rectangle. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 200, 200); + * // Fit to specific coordinates + * bounds.fitBounds(50, 150, 50, 150); + * // bounds are now (50, 50, 150, 150) + * ``` + * @param left - The left value of the bounds + * @param right - The right value of the bounds + * @param top - The top value of the bounds + * @param bottom - The bottom value of the bounds + * @returns This bounds object for chaining + * @see {@link Bounds#fit} For fitting to Rectangle + * @see {@link Bounds#addBoundsMask} For intersection + */ + fitBounds(left, right, top, bottom) { + if (this.minX < left) this.minX = left; + if (this.maxX > right) this.maxX = right; + if (this.minY < top) this.minY = top; + if (this.maxY > bottom) this.maxY = bottom; + return this; + } + /** + * Pads bounds object, making it grow in all directions. + * If paddingY is omitted, both paddingX and paddingY will be set to paddingX. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * + * // Add equal padding + * bounds.pad(10); + * // bounds are now (-10, -10, 110, 110) + * + * // Add different padding for x and y + * bounds.pad(20, 10); + * // bounds are now (-30, -20, 130, 120) + * ``` + * @param paddingX - The horizontal padding amount + * @param paddingY - The vertical padding amount + * @returns This bounds object for chaining + * @see {@link Bounds#fit} For constraining bounds + * @see {@link Bounds#scale} For uniform scaling + */ + pad(paddingX, paddingY = paddingX) { + this.minX -= paddingX; + this.maxX += paddingX; + this.minY -= paddingY; + this.maxY += paddingY; + return this; + } + /** + * Ceils the bounds by rounding up max values and rounding down min values. + * Useful for pixel-perfect calculations and avoiding fractional pixels. + * @example + * ```ts + * const bounds = new Bounds(); + * bounds.set(10.2, 10.9, 50.1, 50.8); + * + * // Round to whole pixels + * bounds.ceil(); + * // bounds are now (10, 10, 51, 51) + * ``` + * @returns This bounds object for chaining + * @see {@link Bounds#scale} For size adjustments + * @see {@link Bounds#fit} For constraining bounds + */ + ceil() { + this.minX = Math.floor(this.minX); + this.minY = Math.floor(this.minY); + this.maxX = Math.ceil(this.maxX); + this.maxY = Math.ceil(this.maxY); + return this; + } + /** + * Creates a new Bounds instance with the same values. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * + * // Create a copy + * const copy = bounds.clone(); + * + * // Original and copy are independent + * bounds.pad(10); + * console.log(copy.width === bounds.width); // false + * ``` + * @returns A new Bounds instance with the same values + * @see {@link Bounds#copyFrom} For reusing existing bounds + */ + clone() { + return new Bounds(this.minX, this.minY, this.maxX, this.maxY); + } + /** + * Scales the bounds by the given values, adjusting all edges proportionally. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * + * // Scale uniformly + * bounds.scale(2); + * // bounds are now (0, 0, 200, 200) + * + * // Scale non-uniformly + * bounds.scale(0.5, 2); + * // bounds are now (0, 0, 100, 400) + * ``` + * @param x - The X value to scale by + * @param y - The Y value to scale by (defaults to x) + * @returns This bounds object for chaining + * @see {@link Bounds#pad} For adding padding + * @see {@link Bounds#fit} For constraining size + */ + scale(x, y = x) { + this.minX *= x; + this.minY *= y; + this.maxX *= x; + this.maxY *= y; + return this; + } + /** + * The x position of the bounds in local space. + * Setting this value will move the bounds while maintaining its width. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Get x position + * console.log(bounds.x); // 0 + * + * // Move bounds horizontally + * bounds.x = 50; + * console.log(bounds.minX, bounds.maxX); // 50, 150 + * + * // Width stays the same + * console.log(bounds.width); // Still 100 + * ``` + */ + get x() { + return this.minX; + } + set x(value) { + const width = this.maxX - this.minX; + this.minX = value; + this.maxX = value + width; + } + /** + * The y position of the bounds in local space. + * Setting this value will move the bounds while maintaining its height. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Get y position + * console.log(bounds.y); // 0 + * + * // Move bounds vertically + * bounds.y = 50; + * console.log(bounds.minY, bounds.maxY); // 50, 150 + * + * // Height stays the same + * console.log(bounds.height); // Still 100 + * ``` + */ + get y() { + return this.minY; + } + set y(value) { + const height = this.maxY - this.minY; + this.minY = value; + this.maxY = value + height; + } + /** + * The width value of the bounds. + * Represents the distance between minX and maxX coordinates. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Get width + * console.log(bounds.width); // 100 + * // Resize width + * bounds.width = 200; + * console.log(bounds.maxX - bounds.minX); // 200 + * ``` + */ + get width() { + return this.maxX - this.minX; + } + set width(value) { + this.maxX = this.minX + value; + } + /** + * The height value of the bounds. + * Represents the distance between minY and maxY coordinates. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Get height + * console.log(bounds.height); // 100 + * // Resize height + * bounds.height = 150; + * console.log(bounds.maxY - bounds.minY); // 150 + * ``` + */ + get height() { + return this.maxY - this.minY; + } + set height(value) { + this.maxY = this.minY + value; + } + /** + * The left edge coordinate of the bounds. + * Alias for minX. + * @example + * ```ts + * const bounds = new Bounds(50, 0, 150, 100); + * console.log(bounds.left); // 50 + * console.log(bounds.left === bounds.minX); // true + * ``` + * @readonly + */ + get left() { + return this.minX; + } + /** + * The right edge coordinate of the bounds. + * Alias for maxX. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * console.log(bounds.right); // 100 + * console.log(bounds.right === bounds.maxX); // true + * ``` + * @readonly + */ + get right() { + return this.maxX; + } + /** + * The top edge coordinate of the bounds. + * Alias for minY. + * @example + * ```ts + * const bounds = new Bounds(0, 25, 100, 125); + * console.log(bounds.top); // 25 + * console.log(bounds.top === bounds.minY); // true + * ``` + * @readonly + */ + get top() { + return this.minY; + } + /** + * The bottom edge coordinate of the bounds. + * Alias for maxY. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 200); + * console.log(bounds.bottom); // 200 + * console.log(bounds.bottom === bounds.maxY); // true + * ``` + * @readonly + */ + get bottom() { + return this.maxY; + } + /** + * Whether the bounds has positive width and height. + * Checks if both dimensions are greater than zero. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Check if bounds are positive + * console.log(bounds.isPositive); // true + * + * // Negative bounds + * bounds.maxX = bounds.minX; + * console.log(bounds.isPositive); // false, width is 0 + * ``` + * @readonly + * @see {@link Bounds#isEmpty} For checking empty state + * @see {@link Bounds#isValid} For checking validity + */ + get isPositive() { + return this.maxX - this.minX > 0 && this.maxY - this.minY > 0; + } + /** + * Whether the bounds has valid coordinates. + * Checks if the bounds has been initialized with real values. + * @example + * ```ts + * const bounds = new Bounds(); + * console.log(bounds.isValid); // false, default state + * + * // Set valid bounds + * bounds.addFrame(0, 0, 100, 100); + * console.log(bounds.isValid); // true + * ``` + * @readonly + * @see {@link Bounds#isEmpty} For checking empty state + * @see {@link Bounds#isPositive} For checking dimensions + */ + get isValid() { + return this.minX + this.minY !== Infinity; + } + /** + * Adds vertices from a Float32Array to the bounds, optionally transformed by a matrix. + * Used for efficiently updating bounds from raw vertex data. + * @example + * ```ts + * const bounds = new Bounds(); + * + * // Add vertices from geometry + * const vertices = new Float32Array([ + * 0, 0, // Vertex 1 + * 100, 0, // Vertex 2 + * 100, 100 // Vertex 3 + * ]); + * bounds.addVertexData(vertices, 0, 6); + * + * // Add transformed vertices + * const matrix = new Matrix() + * .translate(50, 50) + * .rotate(Math.PI / 4); + * bounds.addVertexData(vertices, 0, 6, matrix); + * + * // Add subset of vertices + * bounds.addVertexData(vertices, 2, 4); // Only second vertex + * ``` + * @param vertexData - The array of vertices to add + * @param beginOffset - Starting index in the vertex array + * @param endOffset - Ending index in the vertex array (excluded) + * @param matrix - Optional transformation matrix + * @see {@link Bounds#addFrame} For adding rectangular frames + * @see {@link Matrix} For transformation details + */ + addVertexData(vertexData, beginOffset, endOffset, matrix) { + let minX = this.minX; + let minY = this.minY; + let maxX = this.maxX; + let maxY = this.maxY; + matrix || (matrix = this.matrix); + const a = matrix.a; + const b = matrix.b; + const c = matrix.c; + const d = matrix.d; + const tx = matrix.tx; + const ty = matrix.ty; + for (let i = beginOffset; i < endOffset; i += 2) { + const localX = vertexData[i]; + const localY = vertexData[i + 1]; + const x = a * localX + c * localY + tx; + const y = b * localX + d * localY + ty; + minX = x < minX ? x : minX; + minY = y < minY ? y : minY; + maxX = x > maxX ? x : maxX; + maxY = y > maxY ? y : maxY; + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + /** + * Checks if a point is contained within the bounds. + * Returns true if the point's coordinates fall within the bounds' area. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * // Basic point check + * console.log(bounds.containsPoint(50, 50)); // true + * console.log(bounds.containsPoint(150, 150)); // false + * + * // Check edges + * console.log(bounds.containsPoint(0, 0)); // true, includes edges + * console.log(bounds.containsPoint(100, 100)); // true, includes edges + * ``` + * @param x - x coordinate to check + * @param y - y coordinate to check + * @returns True if the point is inside the bounds + * @see {@link Bounds#isPositive} For valid bounds check + * @see {@link Bounds#rectangle} For Rectangle representation + */ + containsPoint(x, y) { + if (this.minX <= x && this.minY <= y && this.maxX >= x && this.maxY >= y) { + return true; + } + return false; + } + /** + * Returns a string representation of the bounds. + * Useful for debugging and logging bounds information. + * @example + * ```ts + * const bounds = new Bounds(0, 0, 100, 100); + * console.log(bounds.toString()); // "[pixi.js:Bounds minX=0 minY=0 maxX=100 maxY=100 width=100 height=100]" + * ``` + * @returns A string describing the bounds + * @see {@link Bounds#copyFrom} For copying bounds + * @see {@link Bounds#clone} For creating a new instance + */ + toString() { + return `[pixi.js:Bounds minX=${this.minX} minY=${this.minY} maxX=${this.maxX} maxY=${this.maxY} width=${this.width} height=${this.height}]`; + } + /** + * Copies the bounds from another bounds object. + * Useful for reusing bounds objects and avoiding allocations. + * @example + * ```ts + * const sourceBounds = new Bounds(0, 0, 100, 100); + * // Copy bounds + * const targetBounds = new Bounds(); + * targetBounds.copyFrom(sourceBounds); + * ``` + * @param bounds - The bounds to copy from + * @returns This bounds object for chaining + * @see {@link Bounds#clone} For creating new instances + */ + copyFrom(bounds) { + this.minX = bounds.minX; + this.minY = bounds.minY; + this.maxX = bounds.maxX; + this.maxY = bounds.maxY; + return this; + } + } + + "use strict"; + const matrixPool = BigPool.getPool(Matrix); + const boundsPool = BigPool.getPool(Bounds); + + "use strict"; + const tempMatrix$7 = new Matrix(); + const getFastGlobalBoundsMixin = { + getFastGlobalBounds(factorRenderLayers, bounds) { + bounds || (bounds = new Bounds()); + bounds.clear(); + this._getGlobalBoundsRecursive(!!factorRenderLayers, bounds, this.parentRenderLayer); + if (!bounds.isValid) { + bounds.set(0, 0, 0, 0); + } + const renderGroup = this.renderGroup || this.parentRenderGroup; + bounds.applyMatrix(renderGroup.worldTransform); + return bounds; + }, + _getGlobalBoundsRecursive(factorRenderLayers, bounds, currentLayer) { + let localBounds = bounds; + if (factorRenderLayers && this.parentRenderLayer && this.parentRenderLayer !== currentLayer) return; + if (this.localDisplayStatus !== 7 || !this.measurable) { + return; + } + const manageEffects = !!this.effects.length; + if (this.renderGroup || manageEffects) { + localBounds = boundsPool.get().clear(); + } + if (this.boundsArea) { + bounds.addRect(this.boundsArea, this.worldTransform); + } else { + if (this.renderPipeId) { + const viewBounds = this.bounds; + localBounds.addFrame( + viewBounds.minX, + viewBounds.minY, + viewBounds.maxX, + viewBounds.maxY, + this.groupTransform + ); + } + const children = this.children; + for (let i = 0; i < children.length; i++) { + children[i]._getGlobalBoundsRecursive(factorRenderLayers, localBounds, currentLayer); + } + } + if (manageEffects) { + let advanced = false; + const renderGroup = this.renderGroup || this.parentRenderGroup; + for (let i = 0; i < this.effects.length; i++) { + if (this.effects[i].addBounds) { + if (!advanced) { + advanced = true; + localBounds.applyMatrix(renderGroup.worldTransform); + } + this.effects[i].addBounds(localBounds, true); + } + } + if (advanced) { + localBounds.applyMatrix(renderGroup.worldTransform.copyTo(tempMatrix$7).invert()); + } + bounds.addBounds(localBounds); + boundsPool.return(localBounds); + } else if (this.renderGroup) { + bounds.addBounds(localBounds, this.relativeGroupTransform); + boundsPool.return(localBounds); + } + } + }; + + "use strict"; + function getGlobalBounds(target, skipUpdateTransform, bounds) { + bounds.clear(); + let parentTransform; + let pooledMatrix; + if (target.parent) { + if (!skipUpdateTransform) { + pooledMatrix = matrixPool.get().identity(); + parentTransform = updateTransformBackwards(target, pooledMatrix); + } else { + parentTransform = target.parent.worldTransform; + } + } else { + parentTransform = Matrix.IDENTITY; + } + _getGlobalBounds(target, bounds, parentTransform, skipUpdateTransform); + if (pooledMatrix) { + matrixPool.return(pooledMatrix); + } + if (!bounds.isValid) { + bounds.set(0, 0, 0, 0); + } + return bounds; + } + function _getGlobalBounds(target, bounds, parentTransform, skipUpdateTransform) { + var _a, _b; + if (!target.visible || !target.measurable) return; + let worldTransform; + if (!skipUpdateTransform) { + target.updateLocalTransform(); + worldTransform = matrixPool.get(); + worldTransform.appendFrom(target.localTransform, parentTransform); + } else { + worldTransform = target.worldTransform; + } + const parentBounds = bounds; + const preserveBounds = !!target.effects.length; + if (preserveBounds) { + bounds = boundsPool.get().clear(); + } + if (target.boundsArea) { + bounds.addRect(target.boundsArea, worldTransform); + } else { + const renderableBounds = target.bounds; + if (renderableBounds && !renderableBounds.isEmpty()) { + bounds.matrix = worldTransform; + bounds.addBounds(renderableBounds); + } + for (let i = 0; i < target.children.length; i++) { + _getGlobalBounds(target.children[i], bounds, worldTransform, skipUpdateTransform); + } + } + if (preserveBounds) { + for (let i = 0; i < target.effects.length; i++) { + (_b = (_a = target.effects[i]).addBounds) == null ? void 0 : _b.call(_a, bounds); + } + parentBounds.addBounds(bounds, Matrix.IDENTITY); + boundsPool.return(bounds); + } + if (!skipUpdateTransform) { + matrixPool.return(worldTransform); + } + } + function updateTransformBackwards(target, parentTransform) { + const parent = target.parent; + if (parent) { + updateTransformBackwards(parent, parentTransform); + parent.updateLocalTransform(); + parentTransform.append(parent.localTransform); + } + return parentTransform; + } + + "use strict"; + function multiplyHexColors(color1, color2) { + if (color1 === 16777215 || !color2) return color2; + if (color2 === 16777215 || !color1) return color1; + const r1 = color1 >> 16 & 255; + const g1 = color1 >> 8 & 255; + const b1 = color1 & 255; + const r2 = color2 >> 16 & 255; + const g2 = color2 >> 8 & 255; + const b2 = color2 & 255; + const r = r1 * r2 / 255 | 0; + const g = g1 * g2 / 255 | 0; + const b = b1 * b2 / 255 | 0; + return (r << 16) + (g << 8) + b; + } + + "use strict"; + const WHITE_BGR = 16777215; + function multiplyColors(localBGRColor, parentBGRColor) { + if (localBGRColor === WHITE_BGR) { + return parentBGRColor; + } + if (parentBGRColor === WHITE_BGR) { + return localBGRColor; + } + return multiplyHexColors(localBGRColor, parentBGRColor); + } + + "use strict"; + function bgr2rgb(color) { + return ((color & 255) << 16) + (color & 65280) + (color >> 16 & 255); + } + const getGlobalMixin = { + getGlobalAlpha(skipUpdate) { + if (skipUpdate) { + if (this.renderGroup) { + return this.renderGroup.worldAlpha; + } + if (this.parentRenderGroup) { + return this.parentRenderGroup.worldAlpha * this.alpha; + } + return this.alpha; + } + let alpha = this.alpha; + let current = this.parent; + while (current) { + alpha *= current.alpha; + current = current.parent; + } + return alpha; + }, + getGlobalTransform(matrix = new Matrix(), skipUpdate) { + if (skipUpdate) { + return matrix.copyFrom(this.worldTransform); + } + this.updateLocalTransform(); + const parentTransform = updateTransformBackwards(this, matrixPool.get().identity()); + matrix.appendFrom(this.localTransform, parentTransform); + matrixPool.return(parentTransform); + return matrix; + }, + getGlobalTint(skipUpdate) { + if (skipUpdate) { + if (this.renderGroup) { + return bgr2rgb(this.renderGroup.worldColor); + } + if (this.parentRenderGroup) { + return bgr2rgb( + multiplyColors(this.localColor, this.parentRenderGroup.worldColor) + ); + } + return this.tint; + } + let color = this.localColor; + let parent = this.parent; + while (parent) { + color = multiplyColors(color, parent.localColor); + parent = parent.parent; + } + return bgr2rgb(color); + } + }; + + "use strict"; + function getLocalBounds(target, bounds, relativeMatrix) { + bounds.clear(); + relativeMatrix || (relativeMatrix = Matrix.IDENTITY); + _getLocalBounds(target, bounds, relativeMatrix, target, true); + if (!bounds.isValid) { + bounds.set(0, 0, 0, 0); + } + return bounds; + } + function _getLocalBounds(target, bounds, parentTransform, rootContainer, isRoot) { + var _a, _b; + let relativeTransform; + if (!isRoot) { + if (!target.visible || !target.measurable) return; + target.updateLocalTransform(); + const localTransform = target.localTransform; + relativeTransform = matrixPool.get(); + relativeTransform.appendFrom(localTransform, parentTransform); + } else { + relativeTransform = matrixPool.get(); + relativeTransform = parentTransform.copyTo(relativeTransform); + } + const parentBounds = bounds; + const preserveBounds = !!target.effects.length; + if (preserveBounds) { + bounds = boundsPool.get().clear(); + } + if (target.boundsArea) { + bounds.addRect(target.boundsArea, relativeTransform); + } else { + if (target.renderPipeId) { + bounds.matrix = relativeTransform; + bounds.addBounds(target.bounds); + } + const children = target.children; + for (let i = 0; i < children.length; i++) { + _getLocalBounds(children[i], bounds, relativeTransform, rootContainer, false); + } + } + if (preserveBounds) { + for (let i = 0; i < target.effects.length; i++) { + (_b = (_a = target.effects[i]).addLocalBounds) == null ? void 0 : _b.call(_a, bounds, rootContainer); + } + parentBounds.addBounds(bounds, Matrix.IDENTITY); + boundsPool.return(bounds); + } + matrixPool.return(relativeTransform); + } + + "use strict"; + function checkChildrenDidChange(container, previousData) { + const children = container.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const uid = child.uid; + const didChange = (child._didViewChangeTick & 65535) << 16 | child._didContainerChangeTick & 65535; + const index = previousData.index; + if (previousData.data[index] !== uid || previousData.data[index + 1] !== didChange) { + previousData.data[previousData.index] = uid; + previousData.data[previousData.index + 1] = didChange; + previousData.didChange = true; + } + previousData.index = index + 2; + if (child.children.length) { + checkChildrenDidChange(child, previousData); + } + } + return previousData.didChange; + } + + "use strict"; + const tempMatrix$6 = new Matrix(); + const measureMixin = { + _localBoundsCacheId: -1, + _localBoundsCacheData: null, + _setWidth(value, localWidth) { + const sign = Math.sign(this.scale.x) || 1; + if (localWidth !== 0) { + this.scale.x = value / localWidth * sign; + } else { + this.scale.x = sign; + } + }, + _setHeight(value, localHeight) { + const sign = Math.sign(this.scale.y) || 1; + if (localHeight !== 0) { + this.scale.y = value / localHeight * sign; + } else { + this.scale.y = sign; + } + }, + getLocalBounds() { + if (!this._localBoundsCacheData) { + this._localBoundsCacheData = { + data: [], + index: 1, + didChange: false, + localBounds: new Bounds() + }; + } + const localBoundsCacheData = this._localBoundsCacheData; + localBoundsCacheData.index = 1; + localBoundsCacheData.didChange = false; + if (localBoundsCacheData.data[0] !== this._didViewChangeTick) { + localBoundsCacheData.didChange = true; + localBoundsCacheData.data[0] = this._didViewChangeTick; + } + checkChildrenDidChange(this, localBoundsCacheData); + if (localBoundsCacheData.didChange) { + getLocalBounds(this, localBoundsCacheData.localBounds, tempMatrix$6); + } + return localBoundsCacheData.localBounds; + }, + getBounds(skipUpdate, bounds) { + return getGlobalBounds(this, skipUpdate, bounds || new Bounds()); + } + }; + + "use strict"; + const onRenderMixin = { + _onRender: null, + set onRender(func) { + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (!func) { + if (this._onRender) { + renderGroup == null ? void 0 : renderGroup.removeOnRender(this); + } + this._onRender = null; + return; + } + if (!this._onRender) { + renderGroup == null ? void 0 : renderGroup.addOnRender(this); + } + this._onRender = func; + }, + get onRender() { + return this._onRender; + } + }; + + "use strict"; + const sortMixin = { + _zIndex: 0, + sortDirty: false, + sortableChildren: false, + get zIndex() { + return this._zIndex; + }, + set zIndex(value) { + if (this._zIndex === value) return; + this._zIndex = value; + this.depthOfChildModified(); + }, + depthOfChildModified() { + if (this.parent) { + this.parent.sortableChildren = true; + this.parent.sortDirty = true; + } + if (this.parentRenderGroup) { + this.parentRenderGroup.structureDidChange = true; + } + }, + sortChildren() { + if (!this.sortDirty) return; + this.sortDirty = false; + this.children.sort(sortChildren); + } + }; + function sortChildren(a, b) { + return a._zIndex - b._zIndex; + } + + "use strict"; + const toLocalGlobalMixin = { + getGlobalPosition(point = new Point(), skipUpdate = false) { + if (this.parent) { + this.parent.toGlobal(this._position, point, skipUpdate); + } else { + point.x = this._position.x; + point.y = this._position.y; + } + return point; + }, + toGlobal(position, point, skipUpdate = false) { + const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate); + point = globalMatrix.apply(position, point); + matrixPool.return(globalMatrix); + return point; + }, + toLocal(position, from, point, skipUpdate) { + if (from) { + position = from.toGlobal(position, point, skipUpdate); + } + const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate); + point = globalMatrix.applyInverse(position, point); + matrixPool.return(globalMatrix); + return point; + } + }; + + "use strict"; + class InstructionSet { + constructor() { + /** a unique id for this instruction set used through the renderer */ + this.uid = uid$1("instructionSet"); + /** the array of instructions */ + this.instructions = []; + /** the actual size of the array (any instructions passed this should be ignored) */ + this.instructionSize = 0; + this.renderables = []; + /** used by the garbage collector to track when the instruction set was last used */ + this.gcTick = 0; + } + /** reset the instruction set so it can be reused set size back to 0 */ + reset() { + this.instructionSize = 0; + } + /** + * Destroy the instruction set, clearing the instructions and renderables. + * @internal + */ + destroy() { + this.instructions.length = 0; + this.renderables.length = 0; + this.renderPipes = null; + this.gcTick = 0; + } + /** + * Add an instruction to the set + * @param instruction - add an instruction to the set + */ + add(instruction) { + this.instructions[this.instructionSize++] = instruction; + } + /** + * Log the instructions to the console (for debugging) + * @internal + */ + log() { + this.instructions.length = this.instructionSize; + console.table(this.instructions, ["type", "action"]); + } + } + + "use strict"; + function nextPow2(v) { + v += v === 0 ? 1 : 0; + --v; + v |= v >>> 1; + v |= v >>> 2; + v |= v >>> 4; + v |= v >>> 8; + v |= v >>> 16; + return v + 1; + } + function isPow2(v) { + return !(v & v - 1) && !!v; + } + function log2(v) { + let r = (v > 65535 ? 1 : 0) << 4; + v >>>= r; + let shift = (v > 255 ? 1 : 0) << 3; + v >>>= shift; + r |= shift; + shift = (v > 15 ? 1 : 0) << 2; + v >>>= shift; + r |= shift; + shift = (v > 3 ? 1 : 0) << 1; + v >>>= shift; + r |= shift; + return r | v >> 1; + } + + "use strict"; + function definedProps(obj) { + const result = {}; + for (const key in obj) { + if (obj[key] !== void 0) { + result[key] = obj[key]; + } + } + return result; + } + + "use strict"; + var __defProp$1j = Object.defineProperty; + var __getOwnPropSymbols$1l = Object.getOwnPropertySymbols; + var __hasOwnProp$1l = Object.prototype.hasOwnProperty; + var __propIsEnum$1l = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1j = (obj, key, value) => key in obj ? __defProp$1j(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1j = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1l.call(b, prop)) + __defNormalProp$1j(a, prop, b[prop]); + if (__getOwnPropSymbols$1l) + for (var prop of __getOwnPropSymbols$1l(b)) { + if (__propIsEnum$1l.call(b, prop)) + __defNormalProp$1j(a, prop, b[prop]); + } + return a; + }; + const idHash$1 = /* @__PURE__ */ Object.create(null); + function createResourceIdFromString(value) { + const id = idHash$1[value]; + if (id === void 0) { + idHash$1[value] = uid$1("resource"); + } + return id; + } + const _TextureStyle = class _TextureStyle extends EventEmitter { + /** + * @param options - options for the style + */ + constructor(options = {}) { + var _a, _b, _c, _d, _e, _f, _g; + super(); + /** @internal */ + this._resourceType = "textureSampler"; + /** @internal */ + this._touched = 0; + /** + * Specifies the maximum anisotropy value clamp used by the sampler. + * Note: Most implementations support {@link TextureStyle#maxAnisotropy} values in range + * between 1 and 16, inclusive. The used value of {@link TextureStyle#maxAnisotropy} will + * be clamped to the maximum value that the platform supports. + * @internal + */ + this._maxAnisotropy = 1; + /** + * Has the style been destroyed? + * @readonly + */ + this.destroyed = false; + options = __spreadValues$1j(__spreadValues$1j({}, _TextureStyle.defaultOptions), options); + this.addressMode = options.addressMode; + this.addressModeU = (_a = options.addressModeU) != null ? _a : this.addressModeU; + this.addressModeV = (_b = options.addressModeV) != null ? _b : this.addressModeV; + this.addressModeW = (_c = options.addressModeW) != null ? _c : this.addressModeW; + this.scaleMode = options.scaleMode; + this.magFilter = (_d = options.magFilter) != null ? _d : this.magFilter; + this.minFilter = (_e = options.minFilter) != null ? _e : this.minFilter; + this.mipmapFilter = (_f = options.mipmapFilter) != null ? _f : this.mipmapFilter; + this.lodMinClamp = options.lodMinClamp; + this.lodMaxClamp = options.lodMaxClamp; + this.compare = options.compare; + this.maxAnisotropy = (_g = options.maxAnisotropy) != null ? _g : 1; + } + set addressMode(value) { + this.addressModeU = value; + this.addressModeV = value; + this.addressModeW = value; + } + /** setting this will set wrapModeU,wrapModeV and wrapModeW all at once! */ + get addressMode() { + return this.addressModeU; + } + set wrapMode(value) { + deprecation(v8_0_0, "TextureStyle.wrapMode is now TextureStyle.addressMode"); + this.addressMode = value; + } + get wrapMode() { + return this.addressMode; + } + set scaleMode(value) { + this.magFilter = value; + this.minFilter = value; + this.mipmapFilter = value; + } + /** setting this will set magFilter,minFilter and mipmapFilter all at once! */ + get scaleMode() { + return this.magFilter; + } + /** Specifies the maximum anisotropy value clamp used by the sampler. */ + set maxAnisotropy(value) { + this._maxAnisotropy = Math.min(value, 16); + if (this._maxAnisotropy > 1) { + this.scaleMode = "linear"; + } + } + get maxAnisotropy() { + return this._maxAnisotropy; + } + // TODO - move this to WebGL? + get _resourceId() { + return this._sharedResourceId || this._generateResourceId(); + } + update() { + this._sharedResourceId = null; + this.emit("change", this); + } + _generateResourceId() { + const bigKey = `${this.addressModeU}-${this.addressModeV}-${this.addressModeW}-${this.magFilter}-${this.minFilter}-${this.mipmapFilter}-${this.lodMinClamp}-${this.lodMaxClamp}-${this.compare}-${this._maxAnisotropy}`; + this._sharedResourceId = createResourceIdFromString(bigKey); + return this._resourceId; + } + /** Destroys the style */ + destroy() { + this.destroyed = true; + this.emit("destroy", this); + this.emit("change", this); + this.removeAllListeners(); + } + }; + /** default options for the style */ + _TextureStyle.defaultOptions = { + addressMode: "clamp-to-edge", + scaleMode: "linear" + }; + let TextureStyle = _TextureStyle; + + "use strict"; + var __defProp$1i = Object.defineProperty; + var __getOwnPropSymbols$1k = Object.getOwnPropertySymbols; + var __hasOwnProp$1k = Object.prototype.hasOwnProperty; + var __propIsEnum$1k = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1i = (obj, key, value) => key in obj ? __defProp$1i(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1i = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1k.call(b, prop)) + __defNormalProp$1i(a, prop, b[prop]); + if (__getOwnPropSymbols$1k) + for (var prop of __getOwnPropSymbols$1k(b)) { + if (__propIsEnum$1k.call(b, prop)) + __defNormalProp$1i(a, prop, b[prop]); + } + return a; + }; + const _TextureSource = class _TextureSource extends EventEmitter { + /** + * @param options - options for creating a new TextureSource + */ + constructor(options = {}) { + var _a, _b, _c, _d; + super(); + this.options = options; + /** @internal */ + this._gpuData = /* @__PURE__ */ Object.create(null); + /** @internal */ + this._gcLastUsed = -1; + /** unique id for this Texture source */ + this.uid = uid$1("textureSource"); + /** + * The resource type used by this TextureSource. This is used by the bind groups to determine + * how to handle this resource. + * @internal + */ + this._resourceType = "textureSource"; + /** + * i unique resource id, used by the bind group systems. + * This can change if the texture is resized or its resource changes + * @internal + */ + this._resourceId = uid$1("resource"); + /** + * this is how the backends know how to upload this texture to the GPU + * It changes depending on the resource type. Classes that extend TextureSource + * should override this property. + * @internal + */ + this.uploadMethodId = "unknown"; + /** @internal */ + this._resolution = 1; + /** the pixel width of this texture source. This is the REAL pure number, not accounting resolution */ + this.pixelWidth = 1; + /** the pixel height of this texture source. This is the REAL pure number, not accounting resolution */ + this.pixelHeight = 1; + /** + * the width of this texture source, accounting for resolution + * eg pixelWidth 200, resolution 2, then width will be 100 + */ + this.width = 1; + /** + * the height of this texture source, accounting for resolution + * eg pixelHeight 200, resolution 2, then height will be 100 + */ + this.height = 1; + /** + * The number of samples of a multisample texture. This is always 1 for non-multisample textures. + * To enable multisample for a texture, set antialias to true + * @internal + */ + this.sampleCount = 1; + /** + * The number of mip levels to generate for this texture. + * this is overridden if autoGenerateMipmaps is true. it is read only! + */ + this.mipLevelCount = 1; + /** + * Should we auto generate mipmaps for this texture? This will automatically generate mipmaps + * for this texture when uploading to the GPU. Mipmapped textures take up more memory, but + * can look better when scaled down. + * + * For performance reasons, it is recommended to NOT use this with RenderTextures, as they are often updated every frame. + * If you do, make sure to call `updateMipmaps` after you update the texture. + */ + this.autoGenerateMipmaps = false; + /** the format that the texture data has */ + this.format = "rgba8unorm"; + /** how many dimensions does this texture have? currently v8 only supports 2d */ + this.dimension = "2d"; + /** how this texture is viewed/sampled by shaders (WebGPU view dimension) */ + this.viewDimension = "2d"; + /** how many array layers this texture has (WebGPU depthOrArrayLayers) */ + this.arrayLayerCount = 1; + /** + * Only really affects RenderTextures. + * Should we use antialiasing for this texture. It will look better, but may impact performance as a + * Blit operation will be required to resolve the texture. + */ + this.antialias = false; + /** + * Used by automatic texture Garbage Collection, stores last GC tick when it was bound + * @protected + */ + this._touched = 0; + /** + * Used by the batcher to build texture batches. faster to have the variable here! + * @protected + */ + this._batchTick = -1; + /** + * A temporary batch location for the texture batching. Here for performance reasons only! + * @protected + */ + this._textureBindLocation = -1; + options = __spreadValues$1i(__spreadValues$1i({}, _TextureSource.defaultOptions), options); + this.label = (_a = options.label) != null ? _a : ""; + this.resource = options.resource; + this.autoGarbageCollect = options.autoGarbageCollect; + this._resolution = options.resolution; + if (options.width) { + this.pixelWidth = options.width * this._resolution; + } else { + this.pixelWidth = this.resource ? (_b = this.resourceWidth) != null ? _b : 1 : 1; + } + if (options.height) { + this.pixelHeight = options.height * this._resolution; + } else { + this.pixelHeight = this.resource ? (_c = this.resourceHeight) != null ? _c : 1 : 1; + } + this.width = this.pixelWidth / this._resolution; + this.height = this.pixelHeight / this._resolution; + this.format = options.format; + this.dimension = options.dimensions; + this.viewDimension = (_d = options.viewDimension) != null ? _d : options.dimensions; + this.arrayLayerCount = options.arrayLayerCount; + this.mipLevelCount = options.mipLevelCount; + this.autoGenerateMipmaps = options.autoGenerateMipmaps; + this.sampleCount = options.sampleCount; + this.antialias = options.antialias; + this.alphaMode = options.alphaMode; + this.style = new TextureStyle(definedProps(options)); + this.destroyed = false; + this._refreshPOT(); + } + /** returns itself */ + get source() { + return this; + } + /** the style of the texture */ + get style() { + return this._style; + } + set style(value) { + var _a, _b; + if (this.style === value) return; + (_a = this._style) == null ? void 0 : _a.off("change", this._onStyleChange, this); + this._style = value; + (_b = this._style) == null ? void 0 : _b.on("change", this._onStyleChange, this); + this._onStyleChange(); + } + /** Specifies the maximum anisotropy value clamp used by the sampler. */ + set maxAnisotropy(value) { + this._style.maxAnisotropy = value; + } + get maxAnisotropy() { + return this._style.maxAnisotropy; + } + /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */ + get addressMode() { + return this._style.addressMode; + } + set addressMode(value) { + this._style.addressMode = value; + } + /** setting this will set wrapModeU, wrapModeV and wrapModeW all at once! */ + get repeatMode() { + return this._style.addressMode; + } + set repeatMode(value) { + this._style.addressMode = value; + } + /** Specifies the sampling behavior when the sample footprint is smaller than or equal to one texel. */ + get magFilter() { + return this._style.magFilter; + } + set magFilter(value) { + this._style.magFilter = value; + } + /** Specifies the sampling behavior when the sample footprint is larger than one texel. */ + get minFilter() { + return this._style.minFilter; + } + set minFilter(value) { + this._style.minFilter = value; + } + /** Specifies behavior for sampling between mipmap levels. */ + get mipmapFilter() { + return this._style.mipmapFilter; + } + set mipmapFilter(value) { + this._style.mipmapFilter = value; + } + /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */ + get lodMinClamp() { + return this._style.lodMinClamp; + } + set lodMinClamp(value) { + this._style.lodMinClamp = value; + } + /** Specifies the minimum and maximum levels of detail, respectively, used internally when sampling a texture. */ + get lodMaxClamp() { + return this._style.lodMaxClamp; + } + set lodMaxClamp(value) { + this._style.lodMaxClamp = value; + } + _onStyleChange() { + this.emit("styleChange", this); + } + /** call this if you have modified the texture outside of the constructor */ + update() { + if (this.resource) { + const resolution = this._resolution; + const didResize = this.resize(this.resourceWidth / resolution, this.resourceHeight / resolution); + if (didResize) return; + } + this.emit("update", this); + } + /** Destroys this texture source */ + destroy() { + this.destroyed = true; + this.unload(); + this.emit("destroy", this); + if (this._style) { + this._style.destroy(); + this._style = null; + } + this.uploadMethodId = null; + this.resource = null; + this.removeAllListeners(); + } + /** + * This will unload the Texture source from the GPU. This will free up the GPU memory + * As soon as it is required fore rendering, it will be re-uploaded. + */ + unload() { + var _a, _b; + this._resourceId = uid$1("resource"); + this.emit("change", this); + this.emit("unload", this); + for (const key in this._gpuData) { + (_b = (_a = this._gpuData[key]) == null ? void 0 : _a.destroy) == null ? void 0 : _b.call(_a); + } + this._gpuData = /* @__PURE__ */ Object.create(null); + } + /** the width of the resource. This is the REAL pure number, not accounting resolution */ + get resourceWidth() { + const { resource } = this; + return resource.naturalWidth || resource.videoWidth || resource.displayWidth || resource.width; + } + /** the height of the resource. This is the REAL pure number, not accounting resolution */ + get resourceHeight() { + const { resource } = this; + return resource.naturalHeight || resource.videoHeight || resource.displayHeight || resource.height; + } + /** + * the resolution of the texture. Changing this number, will not change the number of pixels in the actual texture + * but will the size of the texture when rendered. + * + * changing the resolution of this texture to 2 for example will make it appear twice as small when rendered (as pixel + * density will have increased) + */ + get resolution() { + return this._resolution; + } + set resolution(resolution) { + if (this._resolution === resolution) return; + this._resolution = resolution; + this.width = this.pixelWidth / resolution; + this.height = this.pixelHeight / resolution; + } + /** + * Resize the texture, this is handy if you want to use the texture as a render texture + * @param width - the new width of the texture + * @param height - the new height of the texture + * @param resolution - the new resolution of the texture + * @returns - if the texture was resized + */ + resize(width, height, resolution) { + resolution || (resolution = this._resolution); + width || (width = this.width); + height || (height = this.height); + const newPixelWidth = Math.round(width * resolution); + const newPixelHeight = Math.round(height * resolution); + this.width = newPixelWidth / resolution; + this.height = newPixelHeight / resolution; + this._resolution = resolution; + if (this.pixelWidth === newPixelWidth && this.pixelHeight === newPixelHeight) { + return false; + } + this._refreshPOT(); + this.pixelWidth = newPixelWidth; + this.pixelHeight = newPixelHeight; + this.emit("resize", this); + this._resourceId = uid$1("resource"); + this.emit("change", this); + return true; + } + /** + * Lets the renderer know that this texture has been updated and its mipmaps should be re-generated. + * This is only important for RenderTexture instances, as standard Texture instances will have their + * mipmaps generated on upload. You should call this method after you make any change to the texture + * + * The reason for this is is can be quite expensive to update mipmaps for a texture. So by default, + * We want you, the developer to specify when this action should happen. + * + * Generally you don't want to have mipmaps generated on Render targets that are changed every frame, + */ + updateMipmaps() { + if (this.autoGenerateMipmaps && this.mipLevelCount > 1) { + this.emit("updateMipmaps", this); + } + } + set wrapMode(value) { + this._style.wrapMode = value; + } + get wrapMode() { + return this._style.wrapMode; + } + set scaleMode(value) { + this._style.scaleMode = value; + } + /** setting this will set magFilter,minFilter and mipmapFilter all at once! */ + get scaleMode() { + return this._style.scaleMode; + } + /** + * Refresh check for isPowerOfTwo texture based on size + * @private + */ + _refreshPOT() { + this.isPowerOfTwo = isPow2(this.pixelWidth) && isPow2(this.pixelHeight); + } + static test(_resource) { + throw new Error("Unimplemented"); + } + }; + /** The default options used when creating a new TextureSource. override these to add your own defaults */ + _TextureSource.defaultOptions = { + resolution: 1, + format: "bgra8unorm", + alphaMode: "premultiply-alpha-on-upload", + dimensions: "2d", + viewDimension: "2d", + arrayLayerCount: 1, + mipLevelCount: 1, + autoGenerateMipmaps: false, + sampleCount: 1, + antialias: false, + autoGarbageCollect: false + }; + let TextureSource = _TextureSource; + + "use strict"; + const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; + const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; + const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; + const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; + const rotationCayley = []; + const rotationMatrices = []; + const signum = Math.sign; + function init() { + for (let i = 0; i < 16; i++) { + const row = []; + rotationCayley.push(row); + for (let j = 0; j < 16; j++) { + const _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + const _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + const _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + const _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (let k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + for (let i = 0; i < 16; i++) { + const mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + rotationMatrices.push(mat); + } + } + init(); + const groupD8 = { + /** + * | Rotation | Direction | + * |----------|-----------| + * | 0° | East | + * @group groupD8 + * @type {GD8Symmetry} + */ + E: 0, + /** + * | Rotation | Direction | + * |----------|-----------| + * | 45°↻ | Southeast | + * @group groupD8 + * @type {GD8Symmetry} + */ + SE: 1, + /** + * | Rotation | Direction | + * |----------|-----------| + * | 90°↻ | South | + * @group groupD8 + * @type {GD8Symmetry} + */ + S: 2, + /** + * | Rotation | Direction | + * |----------|-----------| + * | 135°↻ | Southwest | + * @group groupD8 + * @type {GD8Symmetry} + */ + SW: 3, + /** + * | Rotation | Direction | + * |----------|-----------| + * | 180° | West | + * @group groupD8 + * @type {GD8Symmetry} + */ + W: 4, + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -135°/225°↻ | Northwest | + * @group groupD8 + * @type {GD8Symmetry} + */ + NW: 5, + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -90°/270°↻ | North | + * @group groupD8 + * @type {GD8Symmetry} + */ + N: 6, + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -45°/315°↻ | Northeast | + * @group groupD8 + * @type {GD8Symmetry} + */ + NE: 7, + /** + * Reflection about Y-axis. + * @group groupD8 + * @type {GD8Symmetry} + */ + MIRROR_VERTICAL: 8, + /** + * Reflection about the main diagonal. + * @group groupD8 + * @type {GD8Symmetry} + */ + MAIN_DIAGONAL: 10, + /** + * Reflection about X-axis. + * @group groupD8 + * @type {GD8Symmetry} + */ + MIRROR_HORIZONTAL: 12, + /** + * Reflection about reverse diagonal. + * @group groupD8 + * @type {GD8Symmetry} + */ + REVERSE_DIAGONAL: 14, + /** + * @group groupD8 + * @param {GD8Symmetry} ind - sprite rotation angle. + * @returns {GD8Symmetry} The X-component of the U-axis + * after rotating the axes. + */ + uX: (ind) => ux[ind], + /** + * @group groupD8 + * @param {GD8Symmetry} ind - sprite rotation angle. + * @returns {GD8Symmetry} The Y-component of the U-axis + * after rotating the axes. + */ + uY: (ind) => uy[ind], + /** + * @group groupD8 + * @param {GD8Symmetry} ind - sprite rotation angle. + * @returns {GD8Symmetry} The X-component of the V-axis + * after rotating the axes. + */ + vX: (ind) => vx[ind], + /** + * @group groupD8 + * @param {GD8Symmetry} ind - sprite rotation angle. + * @returns {GD8Symmetry} The Y-component of the V-axis + * after rotating the axes. + */ + vY: (ind) => vy[ind], + /** + * @group groupD8 + * @param {GD8Symmetry} rotation - symmetry whose opposite + * is needed. Only rotations have opposite symmetries while + * reflections don't. + * @returns {GD8Symmetry} The opposite symmetry of `rotation` + */ + inv: (rotation) => { + if (rotation & 8) { + return rotation & 15; + } + return -rotation & 7; + }, + /** + * Composes the two D8 operations. + * + * Taking `^` as reflection: + * + * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | + * |-------|-----|-----|-----|-----|------|-------|-------|-------| + * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | + * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | + * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | + * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | + * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | + * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | + * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | + * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | + * + * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} + * @group groupD8 + * @param {GD8Symmetry} rotationSecond - Second operation, which + * is the row in the above cayley table. + * @param {GD8Symmetry} rotationFirst - First operation, which + * is the column in the above cayley table. + * @returns {GD8Symmetry} Composed operation + */ + add: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][rotationFirst], + /** + * Reverse of `add`. + * @group groupD8 + * @param {GD8Symmetry} rotationSecond - Second operation + * @param {GD8Symmetry} rotationFirst - First operation + * @returns {GD8Symmetry} Result + */ + sub: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][groupD8.inv(rotationFirst)], + /** + * Adds 180 degrees to rotation, which is a commutative + * operation. + * @group groupD8 + * @param {number} rotation - The number to rotate. + * @returns {number} Rotated number + */ + rotate180: (rotation) => rotation ^ 4, + /** + * Checks if the rotation angle is vertical, i.e. south + * or north. It doesn't work for reflections. + * @group groupD8 + * @param {GD8Symmetry} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + */ + isVertical: (rotation) => (rotation & 3) === 2, + // rotation % 4 === 2 + /** + * Approximates the vector `V(dx,dy)` into one of the + * eight directions provided by `groupD8`. + * @group groupD8 + * @param {number} dx - X-component of the vector + * @param {number} dy - Y-component of the vector + * @returns {GD8Symmetry} Approximation of the vector into + * one of the eight symmetries. + */ + byDirection: (dx, dy) => { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return groupD8.S; + } + return groupD8.N; + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return groupD8.E; + } + return groupD8.W; + } else if (dy > 0) { + if (dx > 0) { + return groupD8.SE; + } + return groupD8.SW; + } else if (dx > 0) { + return groupD8.NE; + } + return groupD8.NW; + }, + /** + * Helps sprite to compensate texture packer rotation. + * @group groupD8 + * @param {Matrix} matrix - sprite world matrix + * @param {GD8Symmetry} rotation - The rotation factor to use. + * @param {number} tx - sprite anchoring + * @param {number} ty - sprite anchoring + * @param {number} dw - sprite width + * @param {number} dh - sprite height + */ + matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0, dw = 0, dh = 0) => { + const mat = rotationMatrices[groupD8.inv(rotation)]; + const a = mat.a; + const b = mat.b; + const c = mat.c; + const d = mat.d; + const finalTx = tx - Math.min(0, a * dw, c * dh, a * dw + c * dh); + const finalTy = ty - Math.min(0, b * dw, d * dh, b * dw + d * dh); + const a1 = matrix.a; + const b1 = matrix.b; + const c1 = matrix.c; + const d1 = matrix.d; + matrix.a = a * a1 + b * c1; + matrix.b = a * b1 + b * d1; + matrix.c = c * a1 + d * c1; + matrix.d = c * b1 + d * d1; + matrix.tx = finalTx * a1 + finalTy * c1 + matrix.tx; + matrix.ty = finalTx * b1 + finalTy * d1 + matrix.ty; + }, + /** + * Transforms rectangle coordinates based on texture packer rotation. + * Used when texture atlas pages are rotated and coordinates need to be adjusted. + * @group groupD8 + * @param {RectangleLike} rect - Rectangle with original coordinates to transform + * @param {RectangleLike} sourceFrame - Source texture frame (includes offset and dimensions) + * @param {GD8Symmetry} rotation - The groupD8 rotation value + * @param {Rectangle} out - Rectangle to store the result + * @returns {Rectangle} Transformed coordinates (includes source frame offset) + */ + transformRectCoords: (rect, sourceFrame, rotation, out) => { + const { x, y, width, height } = rect; + const { x: frameX, y: frameY, width: frameWidth, height: frameHeight } = sourceFrame; + if (rotation === groupD8.E) { + out.set(x + frameX, y + frameY, width, height); + return out; + } else if (rotation === groupD8.S) { + return out.set( + frameWidth - y - height + frameX, + x + frameY, + height, + width + ); + } else if (rotation === groupD8.W) { + return out.set( + frameWidth - x - width + frameX, + frameHeight - y - height + frameY, + width, + height + ); + } else if (rotation === groupD8.N) { + return out.set( + y + frameX, + frameHeight - x - width + frameY, + height, + width + ); + } + return out.set(x + frameX, y + frameY, width, height); + } + }; + + "use strict"; + const NOOP = () => { + }; + + "use strict"; + var __defProp$1h = Object.defineProperty; + var __defProps$x = Object.defineProperties; + var __getOwnPropDescs$x = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$1j = Object.getOwnPropertySymbols; + var __hasOwnProp$1j = Object.prototype.hasOwnProperty; + var __propIsEnum$1j = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1h = (obj, key, value) => key in obj ? __defProp$1h(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1h = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1j.call(b, prop)) + __defNormalProp$1h(a, prop, b[prop]); + if (__getOwnPropSymbols$1j) + for (var prop of __getOwnPropSymbols$1j(b)) { + if (__propIsEnum$1j.call(b, prop)) + __defNormalProp$1h(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$x = (a, b) => __defProps$x(a, __getOwnPropDescs$x(b)); + class BufferImageSource extends TextureSource { + constructor(options) { + const buffer = options.resource || new Float32Array(options.width * options.height * 4); + let format = options.format; + if (!format) { + if (buffer instanceof Float32Array) { + format = "rgba32float"; + } else if (buffer instanceof Int32Array) { + format = "rgba32uint"; + } else if (buffer instanceof Uint32Array) { + format = "rgba32uint"; + } else if (buffer instanceof Int16Array) { + format = "rgba16uint"; + } else if (buffer instanceof Uint16Array) { + format = "rgba16uint"; + } else if (buffer instanceof Int8Array) { + format = "bgra8unorm"; + } else { + format = "bgra8unorm"; + } + } + super(__spreadProps$x(__spreadValues$1h({}, options), { + resource: buffer, + format + })); + this.uploadMethodId = "buffer"; + } + static test(resource) { + return resource instanceof Int8Array || resource instanceof Uint8Array || resource instanceof Uint8ClampedArray || resource instanceof Int16Array || resource instanceof Uint16Array || resource instanceof Int32Array || resource instanceof Uint32Array || resource instanceof Float32Array; + } + } + BufferImageSource.extension = ExtensionType.TextureSource; + + "use strict"; + const tempMat = new Matrix(); + class TextureMatrix { + /** + * @param texture - observed texture + * @param clampMargin - Changes frame clamping, 0.5 by default. Use -0.5 for extra border. + */ + constructor(texture, clampMargin) { + this.mapCoord = new Matrix(); + this.uClampFrame = new Float32Array(4); + this.uClampOffset = new Float32Array(2); + this._textureID = -1; + this._updateID = 0; + this.clampOffset = 0; + if (typeof clampMargin === "undefined") { + this.clampMargin = texture.width < 10 ? 0 : 0.5; + } else { + this.clampMargin = clampMargin; + } + this.isSimple = false; + this.texture = texture; + } + /** Texture property. */ + get texture() { + return this._texture; + } + set texture(value) { + var _a; + if (this.texture === value) return; + (_a = this._texture) == null ? void 0 : _a.removeListener("update", this.update, this); + this._texture = value; + this._texture.addListener("update", this.update, this); + this.update(); + } + /** + * Multiplies uvs array to transform + * @param uvs - mesh uvs + * @param [out=uvs] - output + * @returns - output + */ + multiplyUvs(uvs, out) { + if (out === void 0) { + out = uvs; + } + const mat = this.mapCoord; + for (let i = 0; i < uvs.length; i += 2) { + const x = uvs[i]; + const y = uvs[i + 1]; + out[i] = x * mat.a + y * mat.c + mat.tx; + out[i + 1] = x * mat.b + y * mat.d + mat.ty; + } + return out; + } + /** + * Updates matrices if texture was changed + * @returns - whether or not it was updated + */ + update() { + const tex = this._texture; + this._updateID++; + const uvs = tex.uvs; + this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); + const orig = tex.orig; + const trim = tex.trim; + if (trim) { + tempMat.set( + orig.width / trim.width, + 0, + 0, + orig.height / trim.height, + -trim.x / trim.width, + -trim.y / trim.height + ); + this.mapCoord.append(tempMat); + } + const texBase = tex.source; + const frame = this.uClampFrame; + const margin = this.clampMargin / texBase._resolution; + const offset = this.clampOffset / texBase._resolution; + frame[0] = (tex.frame.x + margin + offset) / texBase.width; + frame[1] = (tex.frame.y + margin + offset) / texBase.height; + frame[2] = (tex.frame.x + tex.frame.width - margin + offset) / texBase.width; + frame[3] = (tex.frame.y + tex.frame.height - margin + offset) / texBase.height; + this.uClampOffset[0] = this.clampOffset / texBase.pixelWidth; + this.uClampOffset[1] = this.clampOffset / texBase.pixelHeight; + this.isSimple = tex.frame.width === texBase.width && tex.frame.height === texBase.height && tex.rotate === 0; + return true; + } + } + + "use strict"; + class Texture extends EventEmitter { + /** + * @param {TextureOptions} options - Options for the texture + */ + constructor({ + source, + label, + frame, + orig, + trim, + defaultAnchor, + defaultBorders, + rotate, + dynamic + } = {}) { + var _a; + super(); + /** unique id for this texture */ + this.uid = uid$1("texture"); + /** A uvs object based on the given frame and the texture source */ + this.uvs = { x0: 0, y0: 0, x1: 0, y1: 0, x2: 0, y2: 0, x3: 0, y3: 0 }; + /** + * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, + * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) + */ + this.frame = new Rectangle(); + /** + * Does this Texture have any frame data assigned to it? + * + * This mode is enabled automatically if no frame was passed inside constructor. + * + * In this mode texture is subscribed to baseTexture events, and fires `update` on any change. + * + * Beware, after loading or resize of baseTexture event can fired two times! + * If you want more control, subscribe on baseTexture itself. + * @example + * texture.on('update', () => {}); + */ + this.noFrame = false; + /** + * Set to true if you plan on modifying the uvs of this texture. + * When this is the case, sprites and other objects using the texture will + * make sure to listen for changes to the uvs and update their vertices accordingly. + */ + this.dynamic = false; + /** is it a texture? yes! used for type checking */ + this.isTexture = true; + this.label = label; + this.source = (_a = source == null ? void 0 : source.source) != null ? _a : new TextureSource(); + this.noFrame = !frame; + if (frame) { + this.frame.copyFrom(frame); + } else { + const { width, height } = this._source; + this.frame.width = width; + this.frame.height = height; + } + this.orig = orig || this.frame; + this.trim = trim; + this.rotate = rotate != null ? rotate : 0; + this.defaultAnchor = defaultAnchor; + this.defaultBorders = defaultBorders; + this.destroyed = false; + this.dynamic = dynamic || false; + this.updateUvs(); + } + set source(value) { + if (this._source) { + this._source.off("resize", this.update, this); + } + this._source = value; + value.on("resize", this.update, this); + this.emit("update", this); + } + /** the underlying source of the texture (equivalent of baseTexture in v7) */ + get source() { + return this._source; + } + /** returns a TextureMatrix instance for this texture. By default, that object is not created because its heavy. */ + get textureMatrix() { + if (!this._textureMatrix) { + this._textureMatrix = new TextureMatrix(this); + } + return this._textureMatrix; + } + /** The width of the Texture in pixels. */ + get width() { + return this.orig.width; + } + /** The height of the Texture in pixels. */ + get height() { + return this.orig.height; + } + /** Call this function when you have modified the frame of this texture. */ + updateUvs() { + const { uvs, frame } = this; + const { width, height } = this._source; + const nX = frame.x / width; + const nY = frame.y / height; + const nW = frame.width / width; + const nH = frame.height / height; + let rotate = this.rotate; + if (rotate) { + const w2 = nW / 2; + const h2 = nH / 2; + const cX = nX + w2; + const cY = nY + h2; + rotate = groupD8.add(rotate, groupD8.NW); + uvs.x0 = cX + w2 * groupD8.uX(rotate); + uvs.y0 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + uvs.x1 = cX + w2 * groupD8.uX(rotate); + uvs.y1 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + uvs.x2 = cX + w2 * groupD8.uX(rotate); + uvs.y2 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + uvs.x3 = cX + w2 * groupD8.uX(rotate); + uvs.y3 = cY + h2 * groupD8.uY(rotate); + } else { + uvs.x0 = nX; + uvs.y0 = nY; + uvs.x1 = nX + nW; + uvs.y1 = nY; + uvs.x2 = nX + nW; + uvs.y2 = nY + nH; + uvs.x3 = nX; + uvs.y3 = nY + nH; + } + } + /** + * Destroys this texture + * @param destroySource - Destroy the source when the texture is destroyed. + */ + destroy(destroySource = false) { + if (this._source) { + this._source.off("resize", this.update, this); + if (destroySource) { + this._source.destroy(); + this._source = null; + } + } + this._textureMatrix = null; + this.destroyed = true; + this.emit("destroy", this); + this.removeAllListeners(); + } + /** + * Call this if you have modified the `texture outside` of the constructor. + * + * If you have modified this texture's source, you must separately call `texture.source.update()` to see those changes. + */ + update() { + if (this.noFrame) { + this.frame.width = this._source.width; + this.frame.height = this._source.height; + } + this.updateUvs(); + this.emit("update", this); + } + /** @deprecated since 8.0.0 */ + get baseTexture() { + deprecation(v8_0_0, "Texture.baseTexture is now Texture.source"); + return this._source; + } + } + Texture.EMPTY = new Texture({ + label: "EMPTY", + source: new TextureSource({ + label: "EMPTY" + }) + }); + Texture.EMPTY.destroy = NOOP; + Texture.WHITE = new Texture({ + source: new BufferImageSource({ + resource: new Uint8Array([255, 255, 255, 255]), + width: 1, + height: 1, + alphaMode: "premultiply-alpha-on-upload", + label: "WHITE" + }), + label: "WHITE" + }); + Texture.WHITE.destroy = NOOP; + + "use strict"; + var __defProp$1g = Object.defineProperty; + var __defProps$w = Object.defineProperties; + var __getOwnPropDescs$w = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$1i = Object.getOwnPropertySymbols; + var __hasOwnProp$1i = Object.prototype.hasOwnProperty; + var __propIsEnum$1i = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1g = (obj, key, value) => key in obj ? __defProp$1g(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1g = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1i.call(b, prop)) + __defNormalProp$1g(a, prop, b[prop]); + if (__getOwnPropSymbols$1i) + for (var prop of __getOwnPropSymbols$1i(b)) { + if (__propIsEnum$1i.call(b, prop)) + __defNormalProp$1g(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$w = (a, b) => __defProps$w(a, __getOwnPropDescs$w(b)); + let count = 0; + class TexturePoolClass { + /** + * @param textureOptions - options that will be passed to BaseRenderTexture constructor + * @param {SCALE_MODE} [textureOptions.scaleMode] - See {@link SCALE_MODE} for possible values. + */ + constructor(textureOptions) { + this._poolKeyHash = /* @__PURE__ */ Object.create(null); + this._texturePool = {}; + this.textureOptions = textureOptions || {}; + this.enableFullScreen = false; + this.textureStyle = new TextureStyle(this.textureOptions); + } + /** + * Creates texture with params that were specified in pool constructor. + * @param pixelWidth - Width of texture in pixels. + * @param pixelHeight - Height of texture in pixels. + * @param antialias + */ + createTexture(pixelWidth, pixelHeight, antialias) { + const textureSource = new TextureSource(__spreadProps$w(__spreadValues$1g({}, this.textureOptions), { + width: pixelWidth, + height: pixelHeight, + resolution: 1, + antialias, + autoGarbageCollect: false + })); + return new Texture({ + source: textureSource, + label: `texturePool_${count++}` + }); + } + /** + * Gets a Power-of-Two render texture or fullScreen texture + * @param frameWidth - The minimum width of the render texture. + * @param frameHeight - The minimum height of the render texture. + * @param resolution - The resolution of the render texture. + * @param antialias + * @returns The new render texture. + */ + getOptimalTexture(frameWidth, frameHeight, resolution = 1, antialias) { + let po2Width = Math.ceil(frameWidth * resolution - 1e-6); + let po2Height = Math.ceil(frameHeight * resolution - 1e-6); + po2Width = nextPow2(po2Width); + po2Height = nextPow2(po2Height); + const key = (po2Width << 17) + (po2Height << 1) + (antialias ? 1 : 0); + if (!this._texturePool[key]) { + this._texturePool[key] = []; + } + let texture = this._texturePool[key].pop(); + if (!texture) { + texture = this.createTexture(po2Width, po2Height, antialias); + } + texture.source._resolution = resolution; + texture.source.width = po2Width / resolution; + texture.source.height = po2Height / resolution; + texture.source.pixelWidth = po2Width; + texture.source.pixelHeight = po2Height; + texture.frame.x = 0; + texture.frame.y = 0; + texture.frame.width = frameWidth; + texture.frame.height = frameHeight; + texture.updateUvs(); + this._poolKeyHash[texture.uid] = key; + return texture; + } + /** + * Gets extra texture of the same size as input renderTexture + * @param texture - The texture to check what size it is. + * @param antialias - Whether to use antialias. + * @returns A texture that is a power of two + */ + getSameSizeTexture(texture, antialias = false) { + const source = texture.source; + return this.getOptimalTexture(texture.width, texture.height, source._resolution, antialias); + } + /** + * Place a render texture back into the pool. Optionally reset the style of the texture to the default texture style. + * useful if you modified the style of the texture after getting it from the pool. + * @param renderTexture - The renderTexture to free + * @param resetStyle - Whether to reset the style of the texture to the default texture style + */ + returnTexture(renderTexture, resetStyle = false) { + const key = this._poolKeyHash[renderTexture.uid]; + if (resetStyle) { + renderTexture.source.style = this.textureStyle; + } + this._texturePool[key].push(renderTexture); + } + /** + * Clears the pool. + * @param destroyTextures - Destroy all stored textures. + */ + clear(destroyTextures) { + destroyTextures = destroyTextures !== false; + if (destroyTextures) { + for (const i in this._texturePool) { + const textures = this._texturePool[i]; + if (textures) { + for (let j = 0; j < textures.length; j++) { + textures[j].destroy(true); + } + } + } + } + this._texturePool = {}; + } + } + const TexturePool = new TexturePoolClass(); + GlobalResourceRegistry.register(TexturePool); + + "use strict"; + class RenderGroup { + constructor() { + this.renderPipeId = "renderGroup"; + this.root = null; + this.canBundle = false; + this.renderGroupParent = null; + this.renderGroupChildren = []; + this.worldTransform = new Matrix(); + this.worldColorAlpha = 4294967295; + this.worldColor = 16777215; + this.worldAlpha = 1; + // these updates are transform changes.. + this.childrenToUpdate = /* @__PURE__ */ Object.create(null); + this.updateTick = 0; + this.gcTick = 0; + // these update are renderable changes.. + this.childrenRenderablesToUpdate = { list: [], index: 0 }; + // other + this.structureDidChange = true; + this.instructionSet = new InstructionSet(); + this._onRenderContainers = []; + /** + * Indicates if the cached texture needs to be updated. + * @default true + */ + this.textureNeedsUpdate = true; + /** + * Indicates if the container should be cached as a texture. + * @default false + */ + this.isCachedAsTexture = false; + this._matrixDirty = 7; + } + init(root) { + this.root = root; + if (root._onRender) this.addOnRender(root); + root.didChange = true; + const children = root.children; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + child._updateFlags = 15; + this.addChild(child); + } + } + enableCacheAsTexture(options = {}) { + this.textureOptions = options; + this.isCachedAsTexture = true; + this.textureNeedsUpdate = true; + } + disableCacheAsTexture() { + this.isCachedAsTexture = false; + if (this.texture) { + TexturePool.returnTexture(this.texture, true); + this.texture = null; + } + } + updateCacheTexture() { + this.textureNeedsUpdate = true; + const cachedParent = this._parentCacheAsTextureRenderGroup; + if (cachedParent && !cachedParent.textureNeedsUpdate) { + cachedParent.updateCacheTexture(); + } + } + reset() { + this.renderGroupChildren.length = 0; + for (const i in this.childrenToUpdate) { + const childrenAtDepth = this.childrenToUpdate[i]; + childrenAtDepth.list.fill(null); + childrenAtDepth.index = 0; + } + this.childrenRenderablesToUpdate.index = 0; + this.childrenRenderablesToUpdate.list.fill(null); + this.root = null; + this.updateTick = 0; + this.structureDidChange = true; + this._onRenderContainers.length = 0; + this.renderGroupParent = null; + this.disableCacheAsTexture(); + } + get localTransform() { + return this.root.localTransform; + } + addRenderGroupChild(renderGroupChild) { + if (renderGroupChild.renderGroupParent) { + renderGroupChild.renderGroupParent._removeRenderGroupChild(renderGroupChild); + } + renderGroupChild.renderGroupParent = this; + this.renderGroupChildren.push(renderGroupChild); + } + _removeRenderGroupChild(renderGroupChild) { + const index = this.renderGroupChildren.indexOf(renderGroupChild); + if (index > -1) { + this.renderGroupChildren.splice(index, 1); + } + renderGroupChild.renderGroupParent = null; + } + addChild(child) { + this.structureDidChange = true; + child.parentRenderGroup = this; + child.updateTick = -1; + if (child.parent === this.root) { + child.relativeRenderGroupDepth = 1; + } else { + child.relativeRenderGroupDepth = child.parent.relativeRenderGroupDepth + 1; + } + child.didChange = true; + this.onChildUpdate(child); + if (child.renderGroup) { + this.addRenderGroupChild(child.renderGroup); + return; + } + if (child._onRender) this.addOnRender(child); + const children = child.children; + for (let i = 0; i < children.length; i++) { + this.addChild(children[i]); + } + } + removeChild(child) { + this.structureDidChange = true; + if (child._onRender) { + if (!child.renderGroup) { + this.removeOnRender(child); + } + } + child.parentRenderGroup = null; + if (child.renderGroup) { + this._removeRenderGroupChild(child.renderGroup); + return; + } + const children = child.children; + for (let i = 0; i < children.length; i++) { + this.removeChild(children[i]); + } + } + removeChildren(children) { + for (let i = 0; i < children.length; i++) { + this.removeChild(children[i]); + } + } + onChildUpdate(child) { + let childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth]; + if (!childrenToUpdate) { + childrenToUpdate = this.childrenToUpdate[child.relativeRenderGroupDepth] = { + index: 0, + list: [] + }; + } + childrenToUpdate.list[childrenToUpdate.index++] = child; + } + updateRenderable(renderable) { + if (renderable.globalDisplayStatus < 7) return; + this.instructionSet.renderPipes[renderable.renderPipeId].updateRenderable(renderable); + renderable.didViewUpdate = false; + } + onChildViewUpdate(child) { + this.childrenRenderablesToUpdate.list[this.childrenRenderablesToUpdate.index++] = child; + } + get isRenderable() { + return this.root.localDisplayStatus === 7 && this.worldAlpha > 0; + } + /** + * adding a container to the onRender list will make sure the user function + * passed in to the user defined 'onRender` callBack + * @param container - the container to add to the onRender list + */ + addOnRender(container) { + this._onRenderContainers.push(container); + } + removeOnRender(container) { + this._onRenderContainers.splice(this._onRenderContainers.indexOf(container), 1); + } + runOnRender(renderer) { + for (let i = 0; i < this._onRenderContainers.length; i++) { + this._onRenderContainers[i]._onRender(renderer); + } + } + destroy() { + this.disableCacheAsTexture(); + this.renderGroupParent = null; + this.root = null; + this.childrenRenderablesToUpdate = null; + this.childrenToUpdate = null; + this.renderGroupChildren = null; + this._onRenderContainers = null; + this.instructionSet = null; + } + getChildren(out = []) { + const children = this.root.children; + for (let i = 0; i < children.length; i++) { + this._getChildren(children[i], out); + } + return out; + } + _getChildren(container, out = []) { + out.push(container); + if (container.renderGroup) return out; + const children = container.children; + for (let i = 0; i < children.length; i++) { + this._getChildren(children[i], out); + } + return out; + } + invalidateMatrices() { + this._matrixDirty = 7; + } + /** + * Returns the inverse of the world transform matrix. + * @returns {Matrix} The inverse of the world transform matrix. + */ + get inverseWorldTransform() { + if ((this._matrixDirty & 1) === 0) return this._inverseWorldTransform; + this._matrixDirty &= ~1; + this._inverseWorldTransform || (this._inverseWorldTransform = new Matrix()); + return this._inverseWorldTransform.copyFrom(this.worldTransform).invert(); + } + /** + * Returns the inverse of the texture offset transform matrix. + * @returns {Matrix} The inverse of the texture offset transform matrix. + */ + get textureOffsetInverseTransform() { + if ((this._matrixDirty & 2) === 0) return this._textureOffsetInverseTransform; + this._matrixDirty &= ~2; + this._textureOffsetInverseTransform || (this._textureOffsetInverseTransform = new Matrix()); + return this._textureOffsetInverseTransform.copyFrom(this.inverseWorldTransform).translate( + -this._textureBounds.x, + -this._textureBounds.y + ); + } + /** + * Returns the inverse of the parent texture transform matrix. + * This is used to properly transform coordinates when rendering into cached textures. + * @returns {Matrix} The inverse of the parent texture transform matrix. + */ + get inverseParentTextureTransform() { + if ((this._matrixDirty & 4) === 0) return this._inverseParentTextureTransform; + this._matrixDirty &= ~4; + const parentCacheAsTexture = this._parentCacheAsTextureRenderGroup; + if (parentCacheAsTexture) { + this._inverseParentTextureTransform || (this._inverseParentTextureTransform = new Matrix()); + return this._inverseParentTextureTransform.copyFrom(this.worldTransform).prepend(parentCacheAsTexture.inverseWorldTransform).translate( + -parentCacheAsTexture._textureBounds.x, + -parentCacheAsTexture._textureBounds.y + ); + } + return this.worldTransform; + } + /** + * Returns a matrix that transforms coordinates to the correct coordinate space of the texture being rendered to. + * This is the texture offset inverse transform of the closest parent RenderGroup that is cached as a texture. + * @returns {Matrix | null} The transform matrix for the cached texture coordinate space, + * or null if no parent is cached as texture. + */ + get cacheToLocalTransform() { + if (this.isCachedAsTexture) { + return this.textureOffsetInverseTransform; + } + if (!this._parentCacheAsTextureRenderGroup) return null; + return this._parentCacheAsTextureRenderGroup.textureOffsetInverseTransform; + } + } + + "use strict"; + function assignWithIgnore(target, options, ignore = {}) { + for (const key in options) { + if (!ignore[key] && options[key] !== void 0) { + target[key] = options[key]; + } + } + } + + "use strict"; + const defaultSkew = new ObservablePoint(null); + const defaultPivot = new ObservablePoint(null); + const defaultScale = new ObservablePoint(null, 1, 1); + const defaultOrigin = new ObservablePoint(null); + const UPDATE_COLOR = 1; + const UPDATE_BLEND = 2; + const UPDATE_VISIBLE = 4; + const UPDATE_TRANSFORM = 8; + class Container extends EventEmitter { + constructor(options = {}) { + var _a, _b; + super(); + /** + * unique id for this container + * @internal + */ + this.uid = uid$1("renderable"); + /** @private */ + this._updateFlags = 15; + // the render group this container owns + /** @private */ + this.renderGroup = null; + // the render group this container belongs to + /** @private */ + this.parentRenderGroup = null; + // the index of the container in the render group + /** @private */ + this.parentRenderGroupIndex = 0; + // set to true if the container has changed. It is reset once the changes have been applied + // by the transform system + // its here to stop ensure that when things change, only one update gets registers with the transform system + /** @private */ + this.didChange = false; + // same as above, but for the renderable + /** @private */ + this.didViewUpdate = false; + // how deep is the container relative to its render group.. + // unless the element is the root render group - it will be relative to its parent + /** @private */ + this.relativeRenderGroupDepth = 0; + /** + * The array of children of this container. Each child must be a Container or extend from it. + * + * The array is read-only, but its contents can be modified using Container methods. + * @example + * ```ts + * // Access children + * const firstChild = container.children[0]; + * const lastChild = container.children[container.children.length - 1]; + * ``` + * @readonly + * @see {@link Container#addChild} For adding children + * @see {@link Container#removeChild} For removing children + */ + this.children = []; + /** + * The display object container that contains this display object. + * This represents the parent-child relationship in the display tree. + * @example + * ```ts + * // Basic parent access + * const parent = sprite.parent; + * + * // Walk up the tree + * let current = sprite; + * while (current.parent) { + * console.log('Level up:', current.parent.constructor.name); + * current = current.parent; + * } + * ``` + * @readonly + * @see {@link Container#addChild} For adding to a parent + * @see {@link Container#removeChild} For removing from parent + */ + this.parent = null; + // used internally for changing up the render order.. mainly for masks and filters + // TODO setting this should cause a rebuild?? + /** @private */ + this.includeInBuild = true; + /** @private */ + this.measurable = true; + /** @private */ + this.isSimple = true; + /** + * The RenderLayer this container belongs to, if any. + * If it belongs to a RenderLayer, it will be rendered from the RenderLayer's position in the scene. + * @readonly + * @advanced + */ + this.parentRenderLayer = null; + // / /////////////Transform related props////////////// + // used by the transform system to check if a container needs to be updated that frame + // if the tick matches the current transform system tick, it is not updated again + /** @internal */ + this.updateTick = -1; + /** + * Current transform of the object based on local factors: position, scale, other stuff. + * This matrix represents the local transformation without any parent influence. + * @example + * ```ts + * // Basic transform access + * const localMatrix = sprite.localTransform; + * console.log(localMatrix.toString()); + * ``` + * @readonly + * @see {@link Container#worldTransform} For global transform + * @see {@link Container#groupTransform} For render group transform + */ + this.localTransform = new Matrix(); + /** + * The relative group transform is a transform relative to the render group it belongs too. It will include all parent + * transforms and up to the render group (think of it as kind of like a stage - but the stage can be nested). + * If this container is is self a render group matrix will be relative to its parent render group + * @readonly + * @advanced + */ + this.relativeGroupTransform = new Matrix(); + /** + * The group transform is a transform relative to the render group it belongs too. + * If this container is render group then this will be an identity matrix. other wise it + * will be the same as the relativeGroupTransform. + * Use this value when actually rendering things to the screen + * @readonly + * @advanced + */ + this.groupTransform = this.relativeGroupTransform; + /** + * Whether this object has been destroyed. If true, the object should no longer be used. + * After an object is destroyed, all of its functionality is disabled and references are removed. + * @example + * ```ts + * // Cleanup with destroy + * sprite.destroy(); + * console.log(sprite.destroyed); // true + * ``` + * @default false + * @see {@link Container#destroy} For destroying objects + */ + this.destroyed = false; + // transform data.. + /** + * The coordinate of the object relative to the local coordinates of the parent. + * @internal + */ + this._position = new ObservablePoint(this, 0, 0); + /** + * The scale factor of the object. + * @internal + */ + this._scale = defaultScale; + /** + * The pivot point of the container that it rotates around. + * @internal + */ + this._pivot = defaultPivot; + /** + * The origin point around which the container rotates and scales. + * Unlike pivot, changing origin will not move the container's position. + * @private + */ + this._origin = defaultOrigin; + /** + * The skew amount, on the x and y axis. + * @internal + */ + this._skew = defaultSkew; + /** + * The X-coordinate value of the normalized local X axis, + * the first column of the local transformation matrix without a scale. + * @internal + */ + this._cx = 1; + /** + * The Y-coordinate value of the normalized local X axis, + * the first column of the local transformation matrix without a scale. + * @internal + */ + this._sx = 0; + /** + * The X-coordinate value of the normalized local Y axis, + * the second column of the local transformation matrix without a scale. + * @internal + */ + this._cy = 0; + /** + * The Y-coordinate value of the normalized local Y axis, + * the second column of the local transformation matrix without a scale. + * @internal + */ + this._sy = 1; + /** + * The rotation amount. + * @internal + */ + this._rotation = 0; + // / COLOR related props ////////////// + // color stored as ABGR + /** @internal */ + this.localColor = 16777215; + /** @internal */ + this.localAlpha = 1; + /** @internal */ + this.groupAlpha = 1; + // A + /** @internal */ + this.groupColor = 16777215; + // BGR + /** @internal */ + this.groupColorAlpha = 4294967295; + // ABGR + // / BLEND related props ////////////// + /** @internal */ + this.localBlendMode = "inherit"; + /** @internal */ + this.groupBlendMode = "normal"; + // / VISIBILITY related props ////////////// + // visibility + // 0b11 + // first bit is visible, second bit is renderable + /** + * This property holds three bits: culled, visible, renderable + * the third bit represents culling (0 = culled, 1 = not culled) 0b100 + * the second bit represents visibility (0 = not visible, 1 = visible) 0b010 + * the first bit represents renderable (0 = not renderable, 1 = renderable) 0b001 + * @internal + */ + this.localDisplayStatus = 7; + // 0b11 | 0b10 | 0b01 | 0b00 + /** @internal */ + this.globalDisplayStatus = 7; + /** + * A value that increments each time the containe is modified + * eg children added, removed etc + * @ignore + */ + this._didContainerChangeTick = 0; + /** + * A value that increments each time the container view is modified + * eg texture swap, geometry change etc + * @ignore + */ + this._didViewChangeTick = 0; + /** + * property that tracks if the container transform has changed + * @ignore + */ + this._didLocalTransformChangeId = -1; + this.effects = []; + assignWithIgnore(this, options, { + children: true, + parent: true, + effects: true + }); + (_a = options.children) == null ? void 0 : _a.forEach((child) => this.addChild(child)); + (_b = options.parent) == null ? void 0 : _b.addChild(this); + } + /** + * Mixes all enumerable properties and methods from a source object to Container. + * @param source - The source of properties and methods to mix in. + * @deprecated since 8.8.0 + */ + static mixin(source) { + deprecation("8.8.0", "Container.mixin is deprecated, please use extensions.mixin instead."); + extensions.mixin(Container, source); + } + // = 'default'; + /** + * We now use the _didContainerChangeTick and _didViewChangeTick to track changes + * @deprecated since 8.2.6 + * @ignore + */ + set _didChangeId(value) { + this._didViewChangeTick = value >> 12 & 4095; + this._didContainerChangeTick = value & 4095; + } + /** @ignore */ + get _didChangeId() { + return this._didContainerChangeTick & 4095 | (this._didViewChangeTick & 4095) << 12; + } + /** + * Adds one or more children to the container. + * The children will be rendered as part of this container's display list. + * @example + * ```ts + * // Add a single child + * container.addChild(sprite); + * + * // Add multiple children + * container.addChild(background, player, foreground); + * + * // Add with type checking + * const sprite = container.addChild(new Sprite(texture)); + * sprite.tint = 'red'; + * ``` + * @param children - The Container(s) to add to the container + * @returns The first child that was added + * @see {@link Container#removeChild} For removing children + * @see {@link Container#addChildAt} For adding at specific index + */ + addChild(...children) { + if (!this.allowChildren) { + deprecation(v8_0_0, "addChild: Only Containers will be allowed to add children in v8.0.0"); + } + if (children.length > 1) { + for (let i = 0; i < children.length; i++) { + this.addChild(children[i]); + } + return children[0]; + } + const child = children[0]; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (child.parent === this) { + this.children.splice(this.children.indexOf(child), 1); + this.children.push(child); + if (renderGroup) { + renderGroup.structureDidChange = true; + } + return child; + } + if (child.parent) { + child.parent.removeChild(child); + } + this.children.push(child); + if (this.sortableChildren) this.sortDirty = true; + child.parent = this; + child.didChange = true; + child._updateFlags = 15; + if (renderGroup) { + renderGroup.addChild(child); + } + this.emit("childAdded", child, this, this.children.length - 1); + child.emit("added", this); + this._didViewChangeTick++; + if (child._zIndex !== 0) { + child.depthOfChildModified(); + } + return child; + } + /** + * Removes one or more children from the container. + * When removing multiple children, events will be triggered for each child in sequence. + * @example + * ```ts + * // Remove a single child + * const removed = container.removeChild(sprite); + * + * // Remove multiple children + * const bg = container.removeChild(background, player, userInterface); + * + * // Remove with type checking + * const sprite = container.removeChild(childSprite); + * sprite.texture = newTexture; + * ``` + * @param children - The Container(s) to remove + * @returns The first child that was removed + * @see {@link Container#addChild} For adding children + * @see {@link Container#removeChildren} For removing multiple children + */ + removeChild(...children) { + if (children.length > 1) { + for (let i = 0; i < children.length; i++) { + this.removeChild(children[i]); + } + return children[0]; + } + const child = children[0]; + const index = this.children.indexOf(child); + if (index > -1) { + this._didViewChangeTick++; + this.children.splice(index, 1); + if (this.renderGroup) { + this.renderGroup.removeChild(child); + } else if (this.parentRenderGroup) { + this.parentRenderGroup.removeChild(child); + } + if (child.parentRenderLayer) { + child.parentRenderLayer.detach(child); + } + child.parent = null; + this.emit("childRemoved", child, this, index); + child.emit("removed", this); + } + return child; + } + /** @ignore */ + _onUpdate(point) { + if (point) { + if (point === this._skew) { + this._updateSkew(); + } + } + this._didContainerChangeTick++; + if (this.didChange) return; + this.didChange = true; + if (this.parentRenderGroup) { + this.parentRenderGroup.onChildUpdate(this); + } + } + set isRenderGroup(value) { + if (!!this.renderGroup === value) return; + if (value) { + this.enableRenderGroup(); + } else { + this.disableRenderGroup(); + } + } + /** + * Returns true if this container is a render group. + * This means that it will be rendered as a separate pass, with its own set of instructions + * @advanced + */ + get isRenderGroup() { + return !!this.renderGroup; + } + /** + * Calling this enables a render group for this container. + * This means it will be rendered as a separate set of instructions. + * The transform of the container will also be handled on the GPU rather than the CPU. + * @advanced + */ + enableRenderGroup() { + if (this.renderGroup) return; + const parentRenderGroup = this.parentRenderGroup; + parentRenderGroup == null ? void 0 : parentRenderGroup.removeChild(this); + this.renderGroup = BigPool.get(RenderGroup, this); + this.groupTransform = Matrix.IDENTITY; + parentRenderGroup == null ? void 0 : parentRenderGroup.addChild(this); + this._updateIsSimple(); + } + /** + * This will disable the render group for this container. + * @advanced + */ + disableRenderGroup() { + if (!this.renderGroup) return; + const parentRenderGroup = this.parentRenderGroup; + parentRenderGroup == null ? void 0 : parentRenderGroup.removeChild(this); + BigPool.return(this.renderGroup); + this.renderGroup = null; + this.groupTransform = this.relativeGroupTransform; + parentRenderGroup == null ? void 0 : parentRenderGroup.addChild(this); + this._updateIsSimple(); + } + /** @ignore */ + _updateIsSimple() { + this.isSimple = !this.renderGroup && this.effects.length === 0; + } + /** + * Current transform of the object based on world (parent) factors. + * + * This matrix represents the absolute transformation in the scene graph. + * @example + * ```ts + * // Get world position + * const worldPos = container.worldTransform; + * console.log(`World position: (${worldPos.tx}, ${worldPos.ty})`); + * ``` + * @readonly + * @see {@link Container#localTransform} For local space transform + */ + get worldTransform() { + this._worldTransform || (this._worldTransform = new Matrix()); + if (this.renderGroup) { + this._worldTransform.copyFrom(this.renderGroup.worldTransform); + } else if (this.parentRenderGroup) { + this._worldTransform.appendFrom(this.relativeGroupTransform, this.parentRenderGroup.worldTransform); + } + return this._worldTransform; + } + /** + * The position of the container on the x axis relative to the local coordinates of the parent. + * + * An alias to position.x + * @example + * ```ts + * // Basic position + * container.x = 100; + * ``` + */ + get x() { + return this._position.x; + } + set x(value) { + this._position.x = value; + } + /** + * The position of the container on the y axis relative to the local coordinates of the parent. + * + * An alias to position.y + * @example + * ```ts + * // Basic position + * container.y = 200; + * ``` + */ + get y() { + return this._position.y; + } + set y(value) { + this._position.y = value; + } + /** + * The coordinate of the object relative to the local coordinates of the parent. + * @example + * ```ts + * // Basic position setting + * container.position.set(100, 200); + * container.position.set(100); // Sets both x and y to 100 + * // Using point data + * container.position = { x: 50, y: 75 }; + * ``` + * @since 4.0.0 + */ + get position() { + return this._position; + } + set position(value) { + this._position.copyFrom(value); + } + /** + * The rotation of the object in radians. + * + * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object; + * > rotation is in radians, angle is in degrees. + * @example + * ```ts + * // Basic rotation + * container.rotation = Math.PI / 4; // 45 degrees + * + * // Convert from degrees + * const degrees = 45; + * container.rotation = degrees * Math.PI / 180; + * + * // Rotate around center + * container.pivot.set(container.width / 2, container.height / 2); + * container.rotation = Math.PI; // 180 degrees + * + * // Rotate around center with origin + * container.origin.set(container.width / 2, container.height / 2); + * container.rotation = Math.PI; // 180 degrees + * ``` + */ + get rotation() { + return this._rotation; + } + set rotation(value) { + if (this._rotation !== value) { + this._rotation = value; + this._onUpdate(this._skew); + } + } + /** + * The angle of the object in degrees. + * + * > [!NOTE] 'rotation' and 'angle' have the same effect on a display object; + * > rotation is in radians, angle is in degrees. + * @example + * ```ts + * // Basic angle rotation + * sprite.angle = 45; // 45 degrees + * + * // Rotate around center + * sprite.pivot.set(sprite.width / 2, sprite.height / 2); + * sprite.angle = 180; // Half rotation + * + * // Rotate around center with origin + * sprite.origin.set(sprite.width / 2, sprite.height / 2); + * sprite.angle = 180; // Half rotation + * + * // Reset rotation + * sprite.angle = 0; + * ``` + */ + get angle() { + return this.rotation * RAD_TO_DEG; + } + set angle(value) { + this.rotation = value * DEG_TO_RAD; + } + /** + * The center of rotation, scaling, and skewing for this display object in its local space. + * The `position` is the projection of `pivot` in the parent's local space. + * + * By default, the pivot is the origin (0, 0). + * @example + * ```ts + * // Rotate around center + * container.pivot.set(container.width / 2, container.height / 2); + * container.rotation = Math.PI; // Rotates around center + * ``` + * @since 4.0.0 + */ + get pivot() { + if (this._pivot === defaultPivot) { + this._pivot = new ObservablePoint(this, 0, 0); + } + return this._pivot; + } + set pivot(value) { + if (this._pivot === defaultPivot) { + this._pivot = new ObservablePoint(this, 0, 0); + if (this._origin !== defaultOrigin) { + warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`); + } + } + typeof value === "number" ? this._pivot.set(value) : this._pivot.copyFrom(value); + } + /** + * The skew factor for the object in radians. Skewing is a transformation that distorts + * the object by rotating it differently at each point, creating a non-uniform shape. + * @example + * ```ts + * // Basic skewing + * container.skew.set(0.5, 0); // Skew horizontally + * container.skew.set(0, 0.5); // Skew vertically + * + * // Skew with point data + * container.skew = { x: 0.3, y: 0.3 }; // Diagonal skew + * + * // Reset skew + * container.skew.set(0, 0); + * + * // Animate skew + * app.ticker.add(() => { + * // Create wave effect + * container.skew.x = Math.sin(Date.now() / 1000) * 0.3; + * }); + * + * // Combine with rotation + * container.rotation = Math.PI / 4; // 45 degrees + * container.skew.set(0.2, 0.2); // Skew the rotated object + * ``` + * @since 4.0.0 + * @type {ObservablePoint} Point-like object with x/y properties in radians + * @default {x: 0, y: 0} + */ + get skew() { + if (this._skew === defaultSkew) { + this._skew = new ObservablePoint(this, 0, 0); + } + return this._skew; + } + set skew(value) { + if (this._skew === defaultSkew) { + this._skew = new ObservablePoint(this, 0, 0); + } + this._skew.copyFrom(value); + } + /** + * The scale factors of this object along the local coordinate axes. + * + * The default scale is (1, 1). + * @example + * ```ts + * // Basic scaling + * container.scale.set(2, 2); // Scales to double size + * container.scale.set(2); // Scales uniformly to double size + * container.scale = 2; // Scales uniformly to double size + * // Scale to a specific width and height + * container.setSize(200, 100); // Sets width to 200 and height to 100 + * ``` + * @since 4.0.0 + */ + get scale() { + if (this._scale === defaultScale) { + this._scale = new ObservablePoint(this, 1, 1); + } + return this._scale; + } + set scale(value) { + if (this._scale === defaultScale) { + this._scale = new ObservablePoint(this, 0, 0); + } + if (typeof value === "string") { + value = parseFloat(value); + } + typeof value === "number" ? this._scale.set(value) : this._scale.copyFrom(value); + } + /** + * @experimental + * The origin point around which the container rotates and scales without affecting its position. + * Unlike pivot, changing the origin will not move the container's position. + * @example + * ```ts + * // Rotate around center point + * container.origin.set(container.width / 2, container.height / 2); + * container.rotation = Math.PI; // Rotates around center + * + * // Reset origin + * container.origin.set(0, 0); + * ``` + */ + get origin() { + if (this._origin === defaultOrigin) { + this._origin = new ObservablePoint(this, 0, 0); + } + return this._origin; + } + set origin(value) { + if (this._origin === defaultOrigin) { + this._origin = new ObservablePoint(this, 0, 0); + if (this._pivot !== defaultPivot) { + warn(`Setting both a pivot and origin on a Container is not recommended. This can lead to unexpected behavior if not handled carefully.`); + } + } + typeof value === "number" ? this._origin.set(value) : this._origin.copyFrom(value); + } + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set. + * > [!NOTE] Changing the width will adjust the scale.x property of the container while maintaining its aspect ratio. + * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize} + * as it is more optimized by not recalculating the local bounds twice. + * @example + * ```ts + * // Basic width setting + * container.width = 100; + * // Optimized width setting + * container.setSize(100, 100); + * ``` + */ + get width() { + return Math.abs(this.scale.x * this.getLocalBounds().width); + } + set width(value) { + const localWidth = this.getLocalBounds().width; + this._setWidth(value, localWidth); + } + /** + * The height of the Container, + * > [!NOTE] Changing the height will adjust the scale.y property of the container while maintaining its aspect ratio. + * > [!NOTE] If you want to set both width and height at the same time, use {@link Container#setSize} + * as it is more optimized by not recalculating the local bounds twice. + * @example + * ```ts + * // Basic height setting + * container.height = 200; + * // Optimized height setting + * container.setSize(100, 200); + * ``` + */ + get height() { + return Math.abs(this.scale.y * this.getLocalBounds().height); + } + set height(value) { + const localHeight = this.getLocalBounds().height; + this._setHeight(value, localHeight); + } + /** + * Retrieves the size of the container as a [Size]{@link Size} object. + * + * This is faster than get the width and height separately. + * @example + * ```ts + * // Basic size retrieval + * const size = container.getSize(); + * console.log(`Size: ${size.width}x${size.height}`); + * + * // Reuse existing size object + * const reuseSize = { width: 0, height: 0 }; + * container.getSize(reuseSize); + * ``` + * @param out - Optional object to store the size in. + * @returns The size of the container. + */ + getSize(out) { + if (!out) { + out = {}; + } + const bounds = this.getLocalBounds(); + out.width = Math.abs(this.scale.x * bounds.width); + out.height = Math.abs(this.scale.y * bounds.height); + return out; + } + /** + * Sets the size of the container to the specified width and height. + * This is more efficient than setting width and height separately as it only recalculates bounds once. + * @example + * ```ts + * // Basic size setting + * container.setSize(100, 200); + * + * // Set uniform size + * container.setSize(100); // Sets both width and height to 100 + * ``` + * @param value - This can be either a number or a [Size]{@link Size} object. + * @param height - The height to set. Defaults to the value of `width` if not provided. + */ + setSize(value, height) { + var _a; + const size = this.getLocalBounds(); + if (typeof value === "object") { + height = (_a = value.height) != null ? _a : value.width; + value = value.width; + } else { + height != null ? height : height = value; + } + value !== void 0 && this._setWidth(value, size.width); + height !== void 0 && this._setHeight(height, size.height); + } + /** Called when the skew or the rotation changes. */ + _updateSkew() { + const rotation = this._rotation; + const skew = this._skew; + this._cx = Math.cos(rotation + skew._y); + this._sx = Math.sin(rotation + skew._y); + this._cy = -Math.sin(rotation - skew._x); + this._sy = Math.cos(rotation - skew._x); + } + /** + * Updates the transform properties of the container. + * Allows partial updates of transform properties for optimized manipulation. + * @example + * ```ts + * // Basic transform update + * container.updateTransform({ + * x: 100, + * y: 200, + * rotation: Math.PI / 4 + * }); + * + * // Scale and rotate around center + * sprite.updateTransform({ + * pivotX: sprite.width / 2, + * pivotY: sprite.height / 2, + * scaleX: 2, + * scaleY: 2, + * rotation: Math.PI + * }); + * + * // Update position only + * button.updateTransform({ + * x: button.x + 10, // Move right + * y: button.y // Keep same y + * }); + * ``` + * @param opts - Transform options to update + * @param opts.x - The x position + * @param opts.y - The y position + * @param opts.scaleX - The x-axis scale factor + * @param opts.scaleY - The y-axis scale factor + * @param opts.rotation - The rotation in radians + * @param opts.skewX - The x-axis skew factor + * @param opts.skewY - The y-axis skew factor + * @param opts.pivotX - The x-axis pivot point + * @param opts.pivotY - The y-axis pivot point + * @returns This container, for chaining + * @see {@link Container#setFromMatrix} For matrix-based transforms + * @see {@link Container#position} For direct position access + */ + updateTransform(opts) { + this.position.set( + typeof opts.x === "number" ? opts.x : this.position.x, + typeof opts.y === "number" ? opts.y : this.position.y + ); + this.scale.set( + typeof opts.scaleX === "number" ? opts.scaleX || 1 : this.scale.x, + typeof opts.scaleY === "number" ? opts.scaleY || 1 : this.scale.y + ); + this.rotation = typeof opts.rotation === "number" ? opts.rotation : this.rotation; + this.skew.set( + typeof opts.skewX === "number" ? opts.skewX : this.skew.x, + typeof opts.skewY === "number" ? opts.skewY : this.skew.y + ); + this.pivot.set( + typeof opts.pivotX === "number" ? opts.pivotX : this.pivot.x, + typeof opts.pivotY === "number" ? opts.pivotY : this.pivot.y + ); + this.origin.set( + typeof opts.originX === "number" ? opts.originX : this.origin.x, + typeof opts.originY === "number" ? opts.originY : this.origin.y + ); + return this; + } + /** + * Updates the local transform properties by decomposing the given matrix. + * Extracts position, scale, rotation, and skew from a transformation matrix. + * @example + * ```ts + * // Basic matrix transform + * const matrix = new Matrix() + * .translate(100, 100) + * .rotate(Math.PI / 4) + * .scale(2, 2); + * + * container.setFromMatrix(matrix); + * + * // Copy transform from another container + * const source = new Container(); + * source.position.set(100, 100); + * source.rotation = Math.PI / 2; + * + * target.setFromMatrix(source.localTransform); + * + * // Reset transform + * container.setFromMatrix(Matrix.IDENTITY); + * ``` + * @param matrix - The matrix to use for updating the transform + * @see {@link Container#updateTransform} For property-based updates + * @see {@link Matrix#decompose} For matrix decomposition details + */ + setFromMatrix(matrix) { + matrix.decompose(this); + } + /** Updates the local transform. */ + updateLocalTransform() { + const localTransformChangeId = this._didContainerChangeTick; + if (this._didLocalTransformChangeId === localTransformChangeId) return; + this._didLocalTransformChangeId = localTransformChangeId; + const lt = this.localTransform; + const scale = this._scale; + const pivot = this._pivot; + const origin = this._origin; + const position = this._position; + const sx = scale._x; + const sy = scale._y; + const px = pivot._x; + const py = pivot._y; + const ox = -origin._x; + const oy = -origin._y; + lt.a = this._cx * sx; + lt.b = this._sx * sx; + lt.c = this._cy * sy; + lt.d = this._sy * sy; + lt.tx = position._x - (px * lt.a + py * lt.c) + (ox * lt.a + oy * lt.c) - ox; + lt.ty = position._y - (px * lt.b + py * lt.d) + (ox * lt.b + oy * lt.d) - oy; + } + // / ///// color related stuff + set alpha(value) { + if (value === this.localAlpha) return; + this.localAlpha = value; + this._updateFlags |= UPDATE_COLOR; + this._onUpdate(); + } + /** + * The opacity of the object relative to its parent's opacity. + * Value ranges from 0 (fully transparent) to 1 (fully opaque). + * @example + * ```ts + * // Basic transparency + * sprite.alpha = 0.5; // 50% opacity + * + * // Inherited opacity + * container.alpha = 0.5; + * const child = new Sprite(texture); + * child.alpha = 0.5; + * container.addChild(child); + * // child's effective opacity is 0.25 (0.5 * 0.5) + * ``` + * @default 1 + * @see {@link Container#visible} For toggling visibility + * @see {@link Container#renderable} For render control + */ + get alpha() { + return this.localAlpha; + } + set tint(value) { + const tempColor = Color.shared.setValue(value != null ? value : 16777215); + const bgr = tempColor.toBgrNumber(); + if (bgr === this.localColor) return; + this.localColor = bgr; + this._updateFlags |= UPDATE_COLOR; + this._onUpdate(); + } + /** + * The tint applied to the sprite. + * + * This can be any valid {@link ColorSource}. + * @example + * ```ts + * // Basic color tinting + * container.tint = 0xff0000; // Red tint + * container.tint = 'red'; // Same as above + * container.tint = '#00ff00'; // Green + * container.tint = 'rgb(0,0,255)'; // Blue + * + * // Remove tint + * container.tint = 0xffffff; // White = no tint + * container.tint = null; // Also removes tint + * ``` + * @default 0xFFFFFF + * @see {@link Container#alpha} For transparency + * @see {@link Container#visible} For visibility control + */ + get tint() { + return bgr2rgb(this.localColor); + } + // / //////////////// blend related stuff + set blendMode(value) { + if (this.localBlendMode === value) return; + if (this.parentRenderGroup) { + this.parentRenderGroup.structureDidChange = true; + } + this._updateFlags |= UPDATE_BLEND; + this.localBlendMode = value; + this._onUpdate(); + } + /** + * The blend mode to be applied to the sprite. Controls how pixels are blended when rendering. + * + * Setting to 'normal' will reset to default blending. + * > [!NOTE] More blend modes are available after importing the `pixi.js/advanced-blend-modes` sub-export. + * @example + * ```ts + * // Basic blend modes + * sprite.blendMode = 'add'; // Additive blending + * sprite.blendMode = 'multiply'; // Multiply colors + * sprite.blendMode = 'screen'; // Screen blend + * + * // Reset blend mode + * sprite.blendMode = 'normal'; // Normal blending + * ``` + * @default 'normal' + * @see {@link Container#alpha} For transparency + * @see {@link Container#tint} For color adjustments + */ + get blendMode() { + return this.localBlendMode; + } + // / ///////// VISIBILITY / RENDERABLE ///////////////// + /** + * The visibility of the object. If false the object will not be drawn, + * and the transform will not be updated. + * @example + * ```ts + * // Basic visibility toggle + * sprite.visible = false; // Hide sprite + * sprite.visible = true; // Show sprite + * ``` + * @default true + * @see {@link Container#renderable} For render-only control + * @see {@link Container#alpha} For transparency + */ + get visible() { + return !!(this.localDisplayStatus & 2); + } + set visible(value) { + const valueNumber = value ? 2 : 0; + if ((this.localDisplayStatus & 2) === valueNumber) return; + if (this.parentRenderGroup) { + this.parentRenderGroup.structureDidChange = true; + } + this._updateFlags |= UPDATE_VISIBLE; + this.localDisplayStatus ^= 2; + this._onUpdate(); + } + /** @ignore */ + get culled() { + return !(this.localDisplayStatus & 4); + } + /** @ignore */ + set culled(value) { + const valueNumber = value ? 0 : 4; + if ((this.localDisplayStatus & 4) === valueNumber) return; + if (this.parentRenderGroup) { + this.parentRenderGroup.structureDidChange = true; + } + this._updateFlags |= UPDATE_VISIBLE; + this.localDisplayStatus ^= 4; + this._onUpdate(); + } + /** + * Controls whether this object can be rendered. If false the object will not be drawn, + * but the transform will still be updated. This is different from visible, which skips + * transform updates. + * @example + * ```ts + * // Basic render control + * sprite.renderable = false; // Skip rendering + * sprite.renderable = true; // Enable rendering + * ``` + * @default true + * @see {@link Container#visible} For skipping transform updates + * @see {@link Container#alpha} For transparency + */ + get renderable() { + return !!(this.localDisplayStatus & 1); + } + set renderable(value) { + const valueNumber = value ? 1 : 0; + if ((this.localDisplayStatus & 1) === valueNumber) return; + this._updateFlags |= UPDATE_VISIBLE; + this.localDisplayStatus ^= 1; + if (this.parentRenderGroup) { + this.parentRenderGroup.structureDidChange = true; + } + this._onUpdate(); + } + /** + * Whether or not the object should be rendered. + * @advanced + */ + get isRenderable() { + return this.localDisplayStatus === 7 && this.groupAlpha > 0; + } + /** + * Removes all internal references and listeners as well as removes children from the display list. + * Do not use a Container after calling `destroy`. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * ```ts + * container.destroy(); + * container.destroy(true); + * container.destroy({ children: true }); + * container.destroy({ children: true, texture: true, textureSource: true }); + * ``` + */ + destroy(options = false) { + var _a; + if (this.destroyed) return; + this.destroyed = true; + let oldChildren; + if (this.children.length) { + oldChildren = this.removeChildren(0, this.children.length); + } + this.removeFromParent(); + this.parent = null; + this._maskEffect = null; + this._filterEffect = null; + this.effects = null; + this._position = null; + this._scale = null; + this._pivot = null; + this._origin = null; + this._skew = null; + this.emit("destroyed", this); + this.removeAllListeners(); + const destroyChildren = typeof options === "boolean" ? options : options == null ? void 0 : options.children; + if (destroyChildren && oldChildren) { + for (let i = 0; i < oldChildren.length; ++i) { + oldChildren[i].destroy(options); + } + } + (_a = this.renderGroup) == null ? void 0 : _a.destroy(); + this.renderGroup = null; + } + } + extensions.mixin( + Container, + childrenHelperMixin, + getFastGlobalBoundsMixin, + toLocalGlobalMixin, + onRenderMixin, + measureMixin, + effectsMixin, + findMixin, + sortMixin, + cullingMixin, + cacheAsTextureMixin, + getGlobalMixin, + collectRenderablesMixin + ); + + "use strict"; + var UPDATE_PRIORITY = /* @__PURE__ */ ((UPDATE_PRIORITY2) => { + UPDATE_PRIORITY2[UPDATE_PRIORITY2["INTERACTION"] = 50] = "INTERACTION"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["HIGH"] = 25] = "HIGH"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["NORMAL"] = 0] = "NORMAL"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["LOW"] = -25] = "LOW"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["UTILITY"] = -50] = "UTILITY"; + return UPDATE_PRIORITY2; + })(UPDATE_PRIORITY || {}); + + "use strict"; + class TickerListener { + /** + * Constructor + * @private + * @param fn - The listener function to be added for one update + * @param context - The listener context + * @param priority - The priority for emitting + * @param once - If the handler should fire once + */ + constructor(fn, context = null, priority = 0, once = false) { + /** The next item in chain. */ + this.next = null; + /** The previous item in chain. */ + this.previous = null; + /** `true` if this listener has been destroyed already. */ + this._destroyed = false; + this._fn = fn; + this._context = context; + this.priority = priority; + this._once = once; + } + /** + * Simple compare function to figure out if a function and context match. + * @param fn - The listener function to be added for one update + * @param context - The listener context + * @returns `true` if the listener match the arguments + */ + match(fn, context = null) { + return this._fn === fn && this._context === context; + } + /** + * Emit by calling the current function. + * @param ticker - The ticker emitting. + * @returns Next ticker + */ + emit(ticker) { + if (this._fn) { + if (this._context) { + this._fn.call(this._context, ticker); + } else { + this._fn(ticker); + } + } + const redirect = this.next; + if (this._once) { + this.destroy(true); + } + if (this._destroyed) { + this.next = null; + } + return redirect; + } + /** + * Connect to the list. + * @param previous - Input node, previous listener + */ + connect(previous) { + this.previous = previous; + if (previous.next) { + previous.next.previous = this; + } + this.next = previous.next; + previous.next = this; + } + /** + * Destroy and don't use after this. + * @param hard - `true` to remove the `next` reference, this + * is considered a hard destroy. Soft destroy maintains the next reference. + * @returns The listener to redirect while emitting or removing. + */ + destroy(hard = false) { + this._destroyed = true; + this._fn = null; + this._context = null; + if (this.previous) { + this.previous.next = this.next; + } + if (this.next) { + this.next.previous = this.previous; + } + const redirect = this.next; + this.next = hard ? null : redirect; + this.previous = null; + return redirect; + } + } + + "use strict"; + const _Ticker = class _Ticker { + constructor() { + /** + * Whether or not this ticker should invoke the method {@link Ticker#start|start} + * automatically when a listener is added. + * @example + * ```ts + * // Default behavior (manual start) + * const ticker = new Ticker(); + * ticker.autoStart = false; + * ticker.add(() => { + * // Won't run until ticker.start() is called + * }); + * + * // Auto-start behavior + * const autoTicker = new Ticker(); + * autoTicker.autoStart = true; + * autoTicker.add(() => { + * // Runs immediately when added + * }); + * ``` + * @default false + * @see {@link Ticker#start} For manually starting the ticker + * @see {@link Ticker#stop} For manually stopping the ticker + */ + this.autoStart = false; + /** + * Scalar representing the delta time factor. + * This is a dimensionless value representing the fraction of a frame at the target framerate. + * At 60 FPS, this value is typically around 1.0. + * + * This is NOT in milliseconds - it's a scalar multiplier for frame-independent animations. + * For actual milliseconds, use {@link Ticker#deltaMS}. + * @example + * ```ts + * // Frame-independent animation using deltaTime scalar + * ticker.add((ticker) => { + * // Rotate sprite by 0.1 radians per frame, scaled by deltaTime + * sprite.rotation += 0.1 * ticker.deltaTime; + * }); + * ``` + */ + this.deltaTime = 1; + /** + * The last time update was invoked, in milliseconds since epoch. + * Similar to performance.now() timestamp format. + * + * Used internally for calculating time deltas between frames. + * @example + * ```ts + * ticker.add((ticker) => { + * const currentTime = performance.now(); + * const timeSinceLastFrame = currentTime - ticker.lastTime; + * console.log(`Time since last frame: ${timeSinceLastFrame}ms`); + * }); + * ``` + */ + this.lastTime = -1; + /** + * Factor of current {@link Ticker#deltaTime|deltaTime}. + * Used to scale time for slow motion or fast-forward effects. + * @example + * ```ts + * // Basic speed adjustment + * ticker.speed = 0.5; // Half speed (slow motion) + * ticker.speed = 2.0; // Double speed (fast forward) + * + * // Temporary speed changes + * function slowMotion() { + * const normalSpeed = ticker.speed; + * ticker.speed = 0.2; + * setTimeout(() => { + * ticker.speed = normalSpeed; + * }, 1000); + * } + * ``` + */ + this.speed = 1; + /** + * Whether or not this ticker has been started. + * + * `true` if {@link Ticker#start|start} has been called. + * `false` if {@link Ticker#stop|Stop} has been called. + * + * While `false`, this value may change to `true` in the + * event of {@link Ticker#autoStart|autoStart} being `true` + * and a listener is added. + * @example + * ```ts + * // Check ticker state + * const ticker = new Ticker(); + * console.log(ticker.started); // false + * + * // Start and verify + * ticker.start(); + * console.log(ticker.started); // true + * ``` + */ + this.started = false; + /** Internal current frame request ID */ + this._requestId = null; + /** + * Internal value managed by minFPS property setter and getter. + * This is the maximum allowed milliseconds between updates. + */ + this._maxElapsedMS = 100; + /** + * Internal value managed by minFPS property setter and getter. + * This is the minimum allowed milliseconds between updates. + */ + this._minElapsedMS = 0; + /** If enabled, deleting is disabled.*/ + this._protected = false; + /** The last time keyframe was executed. Maintains a relatively fixed interval with the previous value. */ + this._lastFrame = -1; + this._head = new TickerListener(null, null, Infinity); + this.deltaMS = 1 / _Ticker.targetFPMS; + this.elapsedMS = 1 / _Ticker.targetFPMS; + this._tick = (time) => { + this._requestId = null; + if (this.started) { + this.update(time); + if (this.started && this._requestId === null && this._head.next) { + this._requestId = requestAnimationFrame(this._tick); + } + } + }; + } + /** + * Conditionally requests a new animation frame. + * If a frame has not already been requested, and if the internal + * emitter has listeners, a new frame is requested. + */ + _requestIfNeeded() { + if (this._requestId === null && this._head.next) { + this.lastTime = performance.now(); + this._lastFrame = this.lastTime; + this._requestId = requestAnimationFrame(this._tick); + } + } + /** Conditionally cancels a pending animation frame. */ + _cancelIfNeeded() { + if (this._requestId !== null) { + cancelAnimationFrame(this._requestId); + this._requestId = null; + } + } + /** + * Conditionally requests a new animation frame. + * If the ticker has been started it checks if a frame has not already + * been requested, and if the internal emitter has listeners. If these + * conditions are met, a new frame is requested. If the ticker has not + * been started, but autoStart is `true`, then the ticker starts now, + * and continues with the previous conditions to request a new frame. + */ + _startIfPossible() { + if (this.started) { + this._requestIfNeeded(); + } else if (this.autoStart) { + this.start(); + } + } + /** + * Register a handler for tick events. + * @param fn - The listener function to add. Receives the Ticker instance as parameter + * @param context - The context for the listener + * @param priority - The priority of the listener + * @example + * ```ts + * // Access time properties through the ticker parameter + * ticker.add((ticker) => { + * // Use deltaTime (dimensionless scalar) for frame-independent animations + * sprite.rotation += 0.1 * ticker.deltaTime; + * + * // Use deltaMS (milliseconds) for time-based calculations + * const progress = ticker.deltaMS / animationDuration; + * + * // Use elapsedMS for raw timing measurements + * console.log(`Raw frame time: ${ticker.elapsedMS}ms`); + * }); + * ``` + */ + add(fn, context, priority = UPDATE_PRIORITY.NORMAL) { + return this._addListener(new TickerListener(fn, context, priority)); + } + /** + * Add a handler for the tick event which is only executed once on the next frame. + * @example + * ```ts + * // Basic one-time update + * ticker.addOnce(() => { + * console.log('Runs next frame only'); + * }); + * + * // With specific context + * const game = { + * init(ticker) { + * this.loadResources(); + * console.log('Game initialized'); + * } + * }; + * ticker.addOnce(game.init, game); + * + * // With priority + * ticker.addOnce( + * () => { + * // High priority one-time setup + * physics.init(); + * }, + * undefined, + * UPDATE_PRIORITY.HIGH + * ); + * ``` + * @param fn - The listener function to be added for one update + * @param context - The listener context + * @param priority - The priority for emitting (default: UPDATE_PRIORITY.NORMAL) + * @returns This instance of a ticker + * @see {@link Ticker#add} For continuous updates + * @see {@link Ticker#remove} For removing handlers + */ + addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL) { + return this._addListener(new TickerListener(fn, context, priority, true)); + } + /** + * Internally adds the event handler so that it can be sorted by priority. + * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run + * before the rendering. + * @private + * @param listener - Current listener being added. + * @returns This instance of a ticker + */ + _addListener(listener) { + let current = this._head.next; + let previous = this._head; + if (!current) { + listener.connect(previous); + } else { + while (current) { + if (listener.priority > current.priority) { + listener.connect(previous); + break; + } + previous = current; + current = current.next; + } + if (!listener.previous) { + listener.connect(previous); + } + } + this._startIfPossible(); + return this; + } + /** + * Removes any handlers matching the function and context parameters. + * If no handlers are left after removing, then it cancels the animation frame. + * @example + * ```ts + * // Basic removal + * const onTick = () => { + * sprite.rotation += 0.1; + * }; + * ticker.add(onTick); + * ticker.remove(onTick); + * + * // Remove with context + * const game = { + * update(ticker) { + * this.physics.update(ticker.deltaTime); + * } + * }; + * ticker.add(game.update, game); + * ticker.remove(game.update, game); + * + * // Remove all matching handlers + * // (if same function was added multiple times) + * ticker.add(onTick); + * ticker.add(onTick); + * ticker.remove(onTick); // Removes all instances + * ``` + * @param fn - The listener function to be removed + * @param context - The listener context to be removed + * @returns This instance of a ticker + * @see {@link Ticker#add} For adding handlers + * @see {@link Ticker#addOnce} For one-time handlers + */ + remove(fn, context) { + let listener = this._head.next; + while (listener) { + if (listener.match(fn, context)) { + listener = listener.destroy(); + } else { + listener = listener.next; + } + } + if (!this._head.next) { + this._cancelIfNeeded(); + } + return this; + } + /** + * The number of listeners on this ticker, calculated by walking through linked list. + * @example + * ```ts + * // Check number of active listeners + * const ticker = new Ticker(); + * console.log(ticker.count); // 0 + * + * // Add some listeners + * ticker.add(() => {}); + * ticker.add(() => {}); + * console.log(ticker.count); // 2 + * + * // Check after cleanup + * ticker.destroy(); + * console.log(ticker.count); // 0 + * ``` + * @readonly + * @see {@link Ticker#add} For adding listeners + * @see {@link Ticker#remove} For removing listeners + */ + get count() { + if (!this._head) { + return 0; + } + let count = 0; + let current = this._head; + while (current = current.next) { + count++; + } + return count; + } + /** + * Starts the ticker. If the ticker has listeners a new animation frame is requested at this point. + * @example + * ```ts + * // Basic manual start + * const ticker = new Ticker(); + * ticker.add(() => { + * // Animation code here + * }); + * ticker.start(); + * ``` + * @see {@link Ticker#stop} For stopping the ticker + * @see {@link Ticker#autoStart} For automatic starting + * @see {@link Ticker#started} For checking ticker state + */ + start() { + if (!this.started) { + this.started = true; + this._requestIfNeeded(); + } + } + /** + * Stops the ticker. If the ticker has requested an animation frame it is canceled at this point. + * @example + * ```ts + * // Basic stop + * const ticker = new Ticker(); + * ticker.stop(); + * ``` + * @see {@link Ticker#start} For starting the ticker + * @see {@link Ticker#started} For checking ticker state + * @see {@link Ticker#destroy} For cleaning up the ticker + */ + stop() { + if (this.started) { + this.started = false; + this._cancelIfNeeded(); + } + } + /** + * Destroy the ticker and don't use after this. Calling this method removes all references to internal events. + * @example + * ```ts + * // Clean up with active listeners + * const ticker = new Ticker(); + * ticker.add(() => {}); + * ticker.destroy(); // Removes all listeners + * ``` + * @see {@link Ticker#stop} For stopping without destroying + * @see {@link Ticker#remove} For removing specific listeners + */ + destroy() { + if (!this._protected) { + this.stop(); + let listener = this._head.next; + while (listener) { + listener = listener.destroy(true); + } + this._head.destroy(); + this._head = null; + } + } + /** + * Triggers an update. + * + * An update entails setting the + * current {@link Ticker#elapsedMS|elapsedMS}, + * the current {@link Ticker#deltaTime|deltaTime}, + * invoking all listeners with current deltaTime, + * and then finally setting {@link Ticker#lastTime|lastTime} + * with the value of currentTime that was provided. + * + * This method will be called automatically by animation + * frame callbacks if the ticker instance has been started + * and listeners are added. + * @example + * ```ts + * // Basic manual update + * const ticker = new Ticker(); + * ticker.update(performance.now()); + * ``` + * @param currentTime - The current time of execution (defaults to performance.now()) + * @see {@link Ticker#deltaTime} For frame delta value + * @see {@link Ticker#elapsedMS} For raw elapsed time + */ + update(currentTime = performance.now()) { + let elapsedMS; + if (currentTime > this.lastTime) { + elapsedMS = this.elapsedMS = currentTime - this.lastTime; + if (elapsedMS > this._maxElapsedMS) { + elapsedMS = this._maxElapsedMS; + } + elapsedMS *= this.speed; + if (this._minElapsedMS) { + const delta = currentTime - this._lastFrame | 0; + if (delta < this._minElapsedMS) { + return; + } + this._lastFrame = currentTime - delta % this._minElapsedMS; + } + this.deltaMS = elapsedMS; + this.deltaTime = this.deltaMS * _Ticker.targetFPMS; + const head = this._head; + let listener = head.next; + while (listener) { + listener = listener.emit(this); + } + if (!head.next) { + this._cancelIfNeeded(); + } + } else { + this.deltaTime = this.deltaMS = this.elapsedMS = 0; + } + this.lastTime = currentTime; + } + /** + * The frames per second at which this ticker is running. + * The default is approximately 60 in most modern browsers. + * > [!NOTE] This does not factor in the value of + * > {@link Ticker#speed|speed}, which is specific + * > to scaling {@link Ticker#deltaTime|deltaTime}. + * @example + * ```ts + * // Basic FPS monitoring + * ticker.add(() => { + * console.log(`Current FPS: ${Math.round(ticker.FPS)}`); + * }); + * ``` + * @readonly + */ + get FPS() { + return 1e3 / this.elapsedMS; + } + /** + * Manages the maximum amount of milliseconds allowed to + * elapse between invoking {@link Ticker#update|update}. + * + * This value is used to cap {@link Ticker#deltaTime|deltaTime}, + * but does not effect the measured value of {@link Ticker#FPS|FPS}. + * + * When setting this property it is clamped to a value between + * `0` and `Ticker.targetFPMS * 1000`. + * @example + * ```ts + * // Set minimum acceptable frame rate + * const ticker = new Ticker(); + * ticker.minFPS = 30; // Never go below 30 FPS + * + * // Use with maxFPS for frame rate clamping + * ticker.minFPS = 30; + * ticker.maxFPS = 60; + * + * // Monitor delta capping + * ticker.add(() => { + * // Delta time will be capped based on minFPS + * console.log(`Delta time: ${ticker.deltaTime}`); + * }); + * ``` + * @default 10 + */ + get minFPS() { + return 1e3 / this._maxElapsedMS; + } + set minFPS(fps) { + const minFPS = Math.min(this.maxFPS, fps); + const minFPMS = Math.min(Math.max(0, minFPS) / 1e3, _Ticker.targetFPMS); + this._maxElapsedMS = 1 / minFPMS; + } + /** + * Manages the minimum amount of milliseconds required to + * elapse between invoking {@link Ticker#update|update}. + * + * This will effect the measured value of {@link Ticker#FPS|FPS}. + * + * If it is set to `0`, then there is no limit; PixiJS will render as many frames as it can. + * Otherwise it will be at least `minFPS` + * @example + * ```ts + * // Set minimum acceptable frame rate + * const ticker = new Ticker(); + * ticker.maxFPS = 60; // Never go above 60 FPS + * + * // Use with maxFPS for frame rate clamping + * ticker.minFPS = 30; + * ticker.maxFPS = 60; + * + * // Monitor delta capping + * ticker.add(() => { + * // Delta time will be capped based on maxFPS + * console.log(`Delta time: ${ticker.deltaTime}`); + * }); + * ``` + * @default 0 + */ + get maxFPS() { + if (this._minElapsedMS) { + return Math.round(1e3 / this._minElapsedMS); + } + return 0; + } + set maxFPS(fps) { + if (fps === 0) { + this._minElapsedMS = 0; + } else { + const maxFPS = Math.max(this.minFPS, fps); + this._minElapsedMS = 1 / (maxFPS / 1e3); + } + } + /** + * The shared ticker instance used by {@link AnimatedSprite} and by + * {@link VideoSource} to update animation frames / video textures. + * + * It may also be used by {@link Application} if created with the `sharedTicker` option property set to true. + * + * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance. + * Please follow the examples for usage, including how to opt-out of auto-starting the shared ticker. + * @example + * import { Ticker } from 'pixi.js'; + * + * const ticker = Ticker.shared; + * // Set this to prevent starting this ticker when listeners are added. + * // By default this is true only for the Ticker.shared instance. + * ticker.autoStart = false; + * + * // FYI, call this to ensure the ticker is stopped. It should be stopped + * // if you have not attempted to render anything yet. + * ticker.stop(); + * + * // Call this when you are ready for a running shared ticker. + * ticker.start(); + * @example + * import { autoDetectRenderer, Container } from 'pixi.js'; + * + * // You may use the shared ticker to render... + * const renderer = autoDetectRenderer(); + * const stage = new Container(); + * document.body.appendChild(renderer.view); + * ticker.add((time) => renderer.render(stage)); + * + * // Or you can just update it manually. + * ticker.autoStart = false; + * ticker.stop(); + * const animate = (time) => { + * ticker.update(time); + * renderer.render(stage); + * requestAnimationFrame(animate); + * }; + * animate(performance.now()); + * @type {Ticker} + * @readonly + */ + static get shared() { + if (!_Ticker._shared) { + const shared = _Ticker._shared = new _Ticker(); + shared.autoStart = true; + shared._protected = true; + } + return _Ticker._shared; + } + /** + * The system ticker instance used by {@link PrepareBase} for core timing + * functionality that shouldn't usually need to be paused, unlike the `shared` + * ticker which drives visual animations and rendering which may want to be paused. + * + * The property {@link Ticker#autoStart|autoStart} is set to `true` for this instance. + * @type {Ticker} + * @readonly + * @advanced + */ + static get system() { + if (!_Ticker._system) { + const system = _Ticker._system = new _Ticker(); + system.autoStart = true; + system._protected = true; + } + return _Ticker._system; + } + }; + /** + * Target frame rate in frames per millisecond. + * Used for converting deltaTime to a scalar time delta. + * @example + * ```ts + * // Default is 0.06 (60 FPS) + * console.log(Ticker.targetFPMS); // 0.06 + * + * // Calculate target frame duration + * const frameDuration = 1 / Ticker.targetFPMS; // ≈ 16.67ms + * + * // Use in custom timing calculations + * const deltaTime = elapsedMS * Ticker.targetFPMS; + * ``` + * @remarks + * - Default is 0.06 (equivalent to 60 FPS) + * - Used in deltaTime calculations + * - Affects all ticker instances + * @default 0.06 + * @see {@link Ticker#deltaTime} For time scaling + * @see {@link Ticker#FPS} For actual frame rate + */ + _Ticker.targetFPMS = 0.06; + let Ticker = _Ticker; + + "use strict"; + class CanvasObserver { + constructor(options) { + /** A cached value of the last transform applied to the DOM element. */ + this._lastTransform = ""; + /** A ResizeObserver instance to observe changes in the canvas size. */ + this._observer = null; + /** A flag to indicate whether the observer is attached to the Ticker for continuous updates. */ + this._tickerAttached = false; + /** + * Updates the transform of the DOM element based on the canvas size and position. + * This method calculates the scale and translation needed to keep the DOM element in sync with the canvas. + */ + this.updateTranslation = () => { + if (!this._canvas) return; + const rect = this._canvas.getBoundingClientRect(); + const contentWidth = this._canvas.width; + const contentHeight = this._canvas.height; + const sx = rect.width / contentWidth * this._renderer.resolution; + const sy = rect.height / contentHeight * this._renderer.resolution; + const tx = rect.left; + const ty = rect.top; + const newTransform = `translate(${tx}px, ${ty}px) scale(${sx}, ${sy})`; + if (newTransform !== this._lastTransform) { + this._domElement.style.transform = newTransform; + this._lastTransform = newTransform; + } + }; + this._domElement = options.domElement; + this._renderer = options.renderer; + if (globalThis.OffscreenCanvas && this._renderer.canvas instanceof OffscreenCanvas) return; + this._canvas = this._renderer.canvas; + this._attachObserver(); + } + /** The canvas element that this CanvasObserver is associated with. */ + get canvas() { + return this._canvas; + } + /** Attaches the DOM element to the canvas parent if it is not already attached. */ + ensureAttached() { + if (!this._domElement.parentNode && this._canvas.parentNode) { + this._canvas.parentNode.appendChild(this._domElement); + this.updateTranslation(); + } + } + /** Sets up a ResizeObserver if available. This ensures that the DOM element is kept in sync with the canvas size . */ + _attachObserver() { + if ("ResizeObserver" in globalThis) { + if (this._observer) { + this._observer.disconnect(); + this._observer = null; + } + this._observer = new ResizeObserver((entries) => { + for (const entry of entries) { + if (entry.target !== this._canvas) { + continue; + } + const contentWidth = this.canvas.width; + const contentHeight = this.canvas.height; + const sx = entry.contentRect.width / contentWidth * this._renderer.resolution; + const sy = entry.contentRect.height / contentHeight * this._renderer.resolution; + const needsUpdate = this._lastScaleX !== sx || this._lastScaleY !== sy; + if (needsUpdate) { + this.updateTranslation(); + this._lastScaleX = sx; + this._lastScaleY = sy; + } + } + }); + this._observer.observe(this._canvas); + } else if (!this._tickerAttached) { + Ticker.shared.add(this.updateTranslation, this, UPDATE_PRIORITY.HIGH); + } + } + /** Destroys the CanvasObserver instance, cleaning up observers and Ticker. */ + destroy() { + if (this._observer) { + this._observer.disconnect(); + this._observer = null; + } else if (this._tickerAttached) { + Ticker.shared.remove(this.updateTranslation); + } + this._domElement = null; + this._renderer = null; + this._canvas = null; + this._tickerAttached = false; + this._lastTransform = ""; + this._lastScaleX = null; + this._lastScaleY = null; + } + } + + "use strict"; + class FederatedEvent { + /** + * @param manager - The event boundary which manages this event. Propagation can only occur + * within the boundary's jurisdiction. + */ + constructor(manager) { + /** Flags whether this event bubbles. This will take effect only if it is set before propagation. */ + this.bubbles = true; + /** @deprecated since 7.0.0 */ + this.cancelBubble = true; + /** + * Flags whether this event can be canceled using {@link FederatedEvent.preventDefault}. This is always + * false (for now). + */ + this.cancelable = false; + /** + * Flag added for compatibility with DOM `Event`. It is not used in the Federated Events + * API. + * @see https://dom.spec.whatwg.org/#dom-event-composed + * @ignore + */ + this.composed = false; + /** Flags whether the default response of the user agent was prevent through this event. */ + this.defaultPrevented = false; + /** + * The propagation phase. + * @default {@link FederatedEvent.NONE} + */ + this.eventPhase = FederatedEvent.prototype.NONE; + /** Flags whether propagation was stopped. */ + this.propagationStopped = false; + /** Flags whether propagation was immediately stopped. */ + this.propagationImmediatelyStopped = false; + /** The coordinates of the event relative to the nearest DOM layer. This is a non-standard property. */ + this.layer = new Point(); + /** The coordinates of the event relative to the DOM document. This is a non-standard property. */ + this.page = new Point(); + /** + * The event propagation phase NONE that indicates that the event is not in any phase. + * @default 0 + * @advanced + */ + this.NONE = 0; + /** + * The event propagation phase CAPTURING_PHASE that indicates that the event is in the capturing phase. + * @default 1 + * @advanced + */ + this.CAPTURING_PHASE = 1; + /** + * The event propagation phase AT_TARGET that indicates that the event is at the target. + * @default 2 + * @advanced + */ + this.AT_TARGET = 2; + /** + * The event propagation phase BUBBLING_PHASE that indicates that the event is in the bubbling phase. + * @default 3 + * @advanced + */ + this.BUBBLING_PHASE = 3; + this.manager = manager; + } + /** @readonly */ + get layerX() { + return this.layer.x; + } + /** @readonly */ + get layerY() { + return this.layer.y; + } + /** @readonly */ + get pageX() { + return this.page.x; + } + /** @readonly */ + get pageY() { + return this.page.y; + } + /** + * Fallback for the deprecated `InteractionEvent.data`. + * @deprecated since 7.0.0 + */ + get data() { + return this; + } + /** + * The propagation path for this event. Alias for {@link EventBoundary.propagationPath}. + * @advanced + */ + composedPath() { + if (this.manager && (!this.path || this.path[this.path.length - 1] !== this.target)) { + this.path = this.target ? this.manager.propagationPath(this.target) : []; + } + return this.path; + } + /** + * Unimplemented method included for implementing the DOM interface `Event`. It will throw an `Error`. + * @deprecated + * @ignore + * @param _type + * @param _bubbles + * @param _cancelable + */ + initEvent(_type, _bubbles, _cancelable) { + throw new Error("initEvent() is a legacy DOM API. It is not implemented in the Federated Events API."); + } + /** + * Unimplemented method included for implementing the DOM interface `UIEvent`. It will throw an `Error`. + * @ignore + * @deprecated + * @param _typeArg + * @param _bubblesArg + * @param _cancelableArg + * @param _viewArg + * @param _detailArg + */ + initUIEvent(_typeArg, _bubblesArg, _cancelableArg, _viewArg, _detailArg) { + throw new Error("initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API."); + } + /** + * Prevent default behavior of both PixiJS and the user agent. + * @example + * ```ts + * sprite.on('click', (event) => { + * // Prevent both browser's default click behavior + * // and PixiJS's default handling + * event.preventDefault(); + * + * // Custom handling + * customClickHandler(); + * }); + * ``` + * @remarks + * - Only works if the native event is cancelable + * - Does not stop event propagation + */ + preventDefault() { + if (this.nativeEvent instanceof Event && this.nativeEvent.cancelable) { + this.nativeEvent.preventDefault(); + } + this.defaultPrevented = true; + } + /** + * Stop this event from propagating to any additional listeners, including those + * on the current target and any following targets in the propagation path. + * @example + * ```ts + * container.on('pointerdown', (event) => { + * // Stop all further event handling + * event.stopImmediatePropagation(); + * + * // These handlers won't be called: + * // - Other pointerdown listeners on this container + * // - Any pointerdown listeners on parent containers + * }); + * ``` + * @remarks + * - Immediately stops all event propagation + * - Prevents other listeners on same target from being called + * - More aggressive than stopPropagation() + */ + stopImmediatePropagation() { + this.propagationImmediatelyStopped = true; + } + /** + * Stop this event from propagating to the next target in the propagation path. + * The rest of the listeners on the current target will still be notified. + * @example + * ```ts + * child.on('pointermove', (event) => { + * // Handle event on child + * updateChild(); + * + * // Prevent parent handlers from being called + * event.stopPropagation(); + * }); + * + * // This won't be called if child handles the event + * parent.on('pointermove', (event) => { + * updateParent(); + * }); + * ``` + * @remarks + * - Stops event bubbling to parent containers + * - Does not prevent other listeners on same target + * - Less aggressive than stopImmediatePropagation() + */ + stopPropagation() { + this.propagationStopped = true; + } + } + + var appleIphone = /iPhone/i; + var appleIpod = /iPod/i; + var appleTablet = /iPad/i; + var appleUniversal = /\biOS-universal(?:.+)Mac\b/i; + var androidPhone = /\bAndroid(?:.+)Mobile\b/i; + var androidTablet = /Android/i; + var amazonPhone = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i; + var amazonTablet = /Silk/i; + var windowsPhone = /Windows Phone/i; + var windowsTablet = /\bWindows(?:.+)ARM\b/i; + var otherBlackBerry = /BlackBerry/i; + var otherBlackBerry10 = /BB10/i; + var otherOpera = /Opera Mini/i; + var otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i; + var otherFirefox = /Mobile(?:.+)Firefox\b/i; + var isAppleTabletOnIos13 = function (navigator) { + return (typeof navigator !== 'undefined' && + navigator.platform === 'MacIntel' && + typeof navigator.maxTouchPoints === 'number' && + navigator.maxTouchPoints > 1 && + typeof MSStream === 'undefined'); + }; + function createMatch(userAgent) { + return function (regex) { return regex.test(userAgent); }; + } + function isMobile$1(param) { + var nav = { + userAgent: '', + platform: '', + maxTouchPoints: 0 + }; + if (!param && typeof navigator !== 'undefined') { + nav = { + userAgent: navigator.userAgent, + platform: navigator.platform, + maxTouchPoints: navigator.maxTouchPoints || 0 + }; + } + else if (typeof param === 'string') { + nav.userAgent = param; + } + else if (param && param.userAgent) { + nav = { + userAgent: param.userAgent, + platform: param.platform, + maxTouchPoints: param.maxTouchPoints || 0 + }; + } + var userAgent = nav.userAgent; + var tmp = userAgent.split('[FBAN'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + tmp = userAgent.split('Twitter'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + var match = createMatch(userAgent); + var result = { + apple: { + phone: match(appleIphone) && !match(windowsPhone), + ipod: match(appleIpod), + tablet: !match(appleIphone) && + (match(appleTablet) || isAppleTabletOnIos13(nav)) && + !match(windowsPhone), + universal: match(appleUniversal), + device: (match(appleIphone) || + match(appleIpod) || + match(appleTablet) || + match(appleUniversal) || + isAppleTabletOnIos13(nav)) && + !match(windowsPhone) + }, + amazon: { + phone: match(amazonPhone), + tablet: !match(amazonPhone) && match(amazonTablet), + device: match(amazonPhone) || match(amazonTablet) + }, + android: { + phone: (!match(windowsPhone) && match(amazonPhone)) || + (!match(windowsPhone) && match(androidPhone)), + tablet: !match(windowsPhone) && + !match(amazonPhone) && + !match(androidPhone) && + (match(amazonTablet) || match(androidTablet)), + device: (!match(windowsPhone) && + (match(amazonPhone) || + match(amazonTablet) || + match(androidPhone) || + match(androidTablet))) || + match(/\bokhttp\b/i) + }, + windows: { + phone: match(windowsPhone), + tablet: match(windowsTablet), + device: match(windowsPhone) || match(windowsTablet) + }, + other: { + blackberry: match(otherBlackBerry), + blackberry10: match(otherBlackBerry10), + opera: match(otherOpera), + firefox: match(otherFirefox), + chrome: match(otherChrome), + device: match(otherBlackBerry) || + match(otherBlackBerry10) || + match(otherOpera) || + match(otherFirefox) || + match(otherChrome) + }, + any: false, + phone: false, + tablet: false + }; + result.any = + result.apple.device || + result.android.device || + result.windows.device || + result.other.device; + result.phone = + result.apple.phone || result.android.phone || result.windows.phone; + result.tablet = + result.apple.tablet || result.android.tablet || result.windows.tablet; + return result; + } + + "use strict"; + var _a; + const isMobileCall = (_a = isMobile$1.default) != null ? _a : isMobile$1; + const isMobile = isMobileCall(globalThis.navigator); + + "use strict"; + var __defProp$1f = Object.defineProperty; + var __getOwnPropSymbols$1h = Object.getOwnPropertySymbols; + var __hasOwnProp$1h = Object.prototype.hasOwnProperty; + var __propIsEnum$1h = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1f = (obj, key, value) => key in obj ? __defProp$1f(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1f = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1h.call(b, prop)) + __defNormalProp$1f(a, prop, b[prop]); + if (__getOwnPropSymbols$1h) + for (var prop of __getOwnPropSymbols$1h(b)) { + if (__propIsEnum$1h.call(b, prop)) + __defNormalProp$1f(a, prop, b[prop]); + } + return a; + }; + const KEY_CODE_TAB = 9; + const DIV_TOUCH_SIZE = 100; + const DIV_TOUCH_POS_X = 0; + const DIV_TOUCH_POS_Y = 0; + const DIV_TOUCH_ZINDEX = 2; + const DIV_HOOK_SIZE = 1; + const DIV_HOOK_POS_X = -1e3; + const DIV_HOOK_POS_Y = -1e3; + const DIV_HOOK_ZINDEX = 2; + const _AccessibilitySystem = class _AccessibilitySystem { + // eslint-disable-next-line jsdoc/require-param + /** + * @param {WebGLRenderer|WebGPURenderer} renderer - A reference to the current renderer + */ + constructor(renderer, _mobileInfo = isMobile) { + this._mobileInfo = _mobileInfo; + /** Whether accessibility divs are visible for debugging */ + this.debug = false; + /** Whether to activate on tab key press */ + this._activateOnTab = true; + /** Whether to deactivate accessibility when mouse moves */ + this._deactivateOnMouseMove = true; + /** Internal variable, see isActive getter. */ + this._isActive = false; + /** Internal variable, see isMobileAccessibility getter. */ + this._isMobileAccessibility = false; + /** This is the dom element that will sit over the PixiJS element. This is where the div overlays will go. */ + this._div = null; + /** A simple pool for storing divs. */ + this._pools = {}; + /** This is a tick used to check if an object is no longer being rendered. */ + this._renderId = 0; + /** The array of currently active accessible items. */ + this._children = []; + /** Count to throttle div updates on android devices. */ + this._androidUpdateCount = 0; + /** The frequency to update the div elements. */ + this._androidUpdateFrequency = 500; + // eslint-disable-next-line @typescript-eslint/prefer-readonly + this._isRunningTests = false; + /** Bound function references for proper event listener removal */ + this._boundOnKeyDown = this._onKeyDown.bind(this); + this._boundOnMouseMove = this._onMouseMove.bind(this); + this._hookDiv = null; + if (_mobileInfo.tablet || _mobileInfo.phone) { + this._createTouchHook(); + } + this._renderer = renderer; + } + /** + * Value of `true` if accessibility is currently active and accessibility layers are showing. + * @type {boolean} + * @readonly + */ + get isActive() { + return this._isActive; + } + /** + * Value of `true` if accessibility is enabled for touch devices. + * @type {boolean} + * @readonly + */ + get isMobileAccessibility() { + return this._isMobileAccessibility; + } + /** + * Button element for handling touch hooks. + * @readonly + */ + get hookDiv() { + return this._hookDiv; + } + /** + * The DOM element that will sit over the PixiJS element. This is where the div overlays will go. + * @readonly + */ + get div() { + return this._div; + } + /** + * Creates the touch hooks. + * @private + */ + _createTouchHook() { + const hookDiv = document.createElement("button"); + hookDiv.style.width = `${DIV_HOOK_SIZE}px`; + hookDiv.style.height = `${DIV_HOOK_SIZE}px`; + hookDiv.style.position = "absolute"; + hookDiv.style.top = `${DIV_HOOK_POS_X}px`; + hookDiv.style.left = `${DIV_HOOK_POS_Y}px`; + hookDiv.style.zIndex = DIV_HOOK_ZINDEX.toString(); + hookDiv.style.backgroundColor = "#FF0000"; + hookDiv.title = "select to enable accessibility for this content"; + hookDiv.addEventListener("focus", () => { + this._isMobileAccessibility = true; + this._activate(); + this._destroyTouchHook(); + }); + document.body.appendChild(hookDiv); + this._hookDiv = hookDiv; + } + /** + * Destroys the touch hooks. + * @private + */ + _destroyTouchHook() { + if (!this._hookDiv) { + return; + } + document.body.removeChild(this._hookDiv); + this._hookDiv = null; + } + /** + * Activating will cause the Accessibility layer to be shown. + * This is called when a user presses the tab key. + * @private + */ + _activate() { + if (this._isActive) { + return; + } + this._isActive = true; + if (!this._div) { + this._div = document.createElement("div"); + this._div.style.position = "absolute"; + this._div.style.top = `${DIV_TOUCH_POS_X}px`; + this._div.style.left = `${DIV_TOUCH_POS_Y}px`; + this._div.style.pointerEvents = "none"; + this._div.style.zIndex = DIV_TOUCH_ZINDEX.toString(); + this._canvasObserver = new CanvasObserver({ + domElement: this._div, + renderer: this._renderer + }); + } + if (this._activateOnTab) { + globalThis.addEventListener("keydown", this._boundOnKeyDown, false); + } + if (this._deactivateOnMouseMove) { + globalThis.document.addEventListener("mousemove", this._boundOnMouseMove, true); + } + const canvas = this._renderer.view.canvas; + if (!canvas.parentNode) { + const observer = new MutationObserver(() => { + if (canvas.parentNode) { + observer.disconnect(); + this._canvasObserver.ensureAttached(); + this._initAccessibilitySetup(); + } + }); + observer.observe(document.body, { childList: true, subtree: true }); + } else { + this._canvasObserver.ensureAttached(); + this._initAccessibilitySetup(); + } + } + // New method to handle initialization after div is ready + _initAccessibilitySetup() { + this._renderer.runners.postrender.add(this); + if (this._renderer.lastObjectRendered) { + this._updateAccessibleObjects(this._renderer.lastObjectRendered); + } + } + /** + * Deactivates the accessibility system. Removes listeners and accessibility elements. + * @private + */ + _deactivate() { + var _a, _b; + if (!this._isActive || this._isMobileAccessibility) { + return; + } + this._isActive = false; + globalThis.document.removeEventListener("mousemove", this._boundOnMouseMove, true); + if (this._activateOnTab) { + globalThis.addEventListener("keydown", this._boundOnKeyDown, false); + } + this._renderer.runners.postrender.remove(this); + for (const child of this._children) { + if ((_a = child._accessibleDiv) == null ? void 0 : _a.parentNode) { + child._accessibleDiv.parentNode.removeChild(child._accessibleDiv); + child._accessibleDiv = null; + } + child._accessibleActive = false; + } + for (const accessibleType in this._pools) { + const pool = this._pools[accessibleType]; + pool.forEach((div) => { + if (div.parentNode) { + div.parentNode.removeChild(div); + } + }); + delete this._pools[accessibleType]; + } + if ((_b = this._div) == null ? void 0 : _b.parentNode) { + this._div.parentNode.removeChild(this._div); + } + this._pools = {}; + this._children = []; + } + /** + * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer. + * @private + * @param {Container} container - The Container to check. + */ + _updateAccessibleObjects(container) { + if (!container.visible || !container.accessibleChildren) { + return; + } + if (container.accessible) { + if (!container._accessibleActive) { + this._addChild(container); + } + container._renderId = this._renderId; + } + const children = container.children; + if (children) { + for (let i = 0; i < children.length; i++) { + this._updateAccessibleObjects(children[i]); + } + } + } + /** + * Runner init called, view is available at this point. + * @ignore + */ + init(options) { + const defaultOpts = _AccessibilitySystem.defaultOptions; + const mergedOptions = { + accessibilityOptions: __spreadValues$1f(__spreadValues$1f({}, defaultOpts), (options == null ? void 0 : options.accessibilityOptions) || {}) + }; + this.debug = mergedOptions.accessibilityOptions.debug; + this._activateOnTab = mergedOptions.accessibilityOptions.activateOnTab; + this._deactivateOnMouseMove = mergedOptions.accessibilityOptions.deactivateOnMouseMove; + if (mergedOptions.accessibilityOptions.enabledByDefault) { + this._activate(); + } + this._renderer.runners.postrender.remove(this); + } + /** + * Updates the accessibility layer during rendering. + * - Removes divs for containers no longer in the scene + * - Updates the position and dimensions of the root div + * - Updates positions of active accessibility divs + * Only fires while the accessibility system is active. + * @ignore + */ + postrender() { + const now = performance.now(); + if (this._mobileInfo.android.device && now < this._androidUpdateCount) { + return; + } + this._androidUpdateCount = now + this._androidUpdateFrequency; + if ((!this._renderer.renderingToScreen || !this._renderer.view.canvas) && !this._isRunningTests) { + return; + } + const activeIds = /* @__PURE__ */ new Set(); + if (this._renderer.lastObjectRendered) { + this._updateAccessibleObjects(this._renderer.lastObjectRendered); + for (const child of this._children) { + if (child._renderId === this._renderId) { + activeIds.add(this._children.indexOf(child)); + } + } + } + for (let i = this._children.length - 1; i >= 0; i--) { + const child = this._children[i]; + if (!activeIds.has(i)) { + if (child._accessibleDiv && child._accessibleDiv.parentNode) { + child._accessibleDiv.parentNode.removeChild(child._accessibleDiv); + const pool = this._getPool(child.accessibleType); + pool.push(child._accessibleDiv); + child._accessibleDiv = null; + } + child._accessibleActive = false; + removeItems(this._children, i, 1); + } + } + if (this._renderer.renderingToScreen) { + this._canvasObserver.ensureAttached(); + } + for (let i = 0; i < this._children.length; i++) { + const child = this._children[i]; + if (!child._accessibleActive || !child._accessibleDiv) { + continue; + } + const div = child._accessibleDiv; + const hitArea = child.hitArea || child.getBounds().rectangle; + if (child.hitArea) { + const wt = child.worldTransform; + div.style.left = `${wt.tx + hitArea.x * wt.a}px`; + div.style.top = `${wt.ty + hitArea.y * wt.d}px`; + div.style.width = `${hitArea.width * wt.a}px`; + div.style.height = `${hitArea.height * wt.d}px`; + } else { + this._capHitArea(hitArea); + div.style.left = `${hitArea.x}px`; + div.style.top = `${hitArea.y}px`; + div.style.width = `${hitArea.width}px`; + div.style.height = `${hitArea.height}px`; + } + } + this._renderId++; + } + /** + * private function that will visually add the information to the + * accessibility div + * @param {HTMLElement} div - + */ + _updateDebugHTML(div) { + div.innerHTML = `type: ${div.type}
title : ${div.title}
tabIndex: ${div.tabIndex}`; + } + /** + * Adjust the hit area based on the bounds of a display object + * @param {Rectangle} hitArea - Bounds of the child + */ + _capHitArea(hitArea) { + if (hitArea.x < 0) { + hitArea.width += hitArea.x; + hitArea.x = 0; + } + if (hitArea.y < 0) { + hitArea.height += hitArea.y; + hitArea.y = 0; + } + const { width: viewWidth, height: viewHeight } = this._renderer; + if (hitArea.x + hitArea.width > viewWidth) { + hitArea.width = viewWidth - hitArea.x; + } + if (hitArea.y + hitArea.height > viewHeight) { + hitArea.height = viewHeight - hitArea.y; + } + } + /** + * Creates or reuses a div element for a Container and adds it to the accessibility layer. + * Sets up ARIA attributes, event listeners, and positioning based on the container's properties. + * @private + * @param {Container} container - The child to make accessible. + */ + _addChild(container) { + const pool = this._getPool(container.accessibleType); + let div = pool.pop(); + if (div) { + div.innerHTML = ""; + div.removeAttribute("title"); + div.removeAttribute("aria-label"); + div.tabIndex = 0; + } else { + if (container.accessibleType === "button") { + div = document.createElement("button"); + } else { + div = document.createElement(container.accessibleType); + div.style.cssText = ` + color: transparent; + pointer-events: none; + padding: 0; + margin: 0; + border: 0; + outline: 0; + background: transparent; + box-sizing: border-box; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + `; + if (container.accessibleText) { + div.innerText = container.accessibleText; + } + } + div.style.width = `${DIV_TOUCH_SIZE}px`; + div.style.height = `${DIV_TOUCH_SIZE}px`; + div.style.backgroundColor = this.debug ? "rgba(255,255,255,0.5)" : "transparent"; + div.style.position = "absolute"; + div.style.zIndex = DIV_TOUCH_ZINDEX.toString(); + div.style.borderStyle = "none"; + if (navigator.userAgent.toLowerCase().includes("chrome")) { + div.setAttribute("aria-live", "off"); + } else { + div.setAttribute("aria-live", "polite"); + } + if (navigator.userAgent.match(/rv:.*Gecko\//)) { + div.setAttribute("aria-relevant", "additions"); + } else { + div.setAttribute("aria-relevant", "text"); + } + div.addEventListener("click", this._onClick.bind(this)); + div.addEventListener("focus", this._onFocus.bind(this)); + div.addEventListener("focusout", this._onFocusOut.bind(this)); + } + div.style.pointerEvents = container.accessiblePointerEvents; + div.type = container.accessibleType; + if (container.accessibleTitle && container.accessibleTitle !== null) { + div.title = container.accessibleTitle; + } else if (!container.accessibleHint || container.accessibleHint === null) { + div.title = `container ${container.tabIndex}`; + } + if (container.accessibleHint && container.accessibleHint !== null) { + div.setAttribute("aria-label", container.accessibleHint); + } + if (container.interactive) { + div.tabIndex = container.tabIndex; + } else { + div.tabIndex = 0; + } + if (this.debug) { + this._updateDebugHTML(div); + } + container._accessibleActive = true; + container._accessibleDiv = div; + div.container = container; + this._children.push(container); + this._div.appendChild(container._accessibleDiv); + } + /** + * Dispatch events with the EventSystem. + * @param e + * @param type + * @private + */ + _dispatchEvent(e, type) { + const { container: target } = e.target; + const boundary = this._renderer.events.rootBoundary; + const event = Object.assign(new FederatedEvent(boundary), { target }); + boundary.rootTarget = this._renderer.lastObjectRendered; + type.forEach((type2) => boundary.dispatchEvent(event, type2)); + } + /** + * Maps the div button press to pixi's EventSystem (click) + * @private + * @param {MouseEvent} e - The click event. + */ + _onClick(e) { + this._dispatchEvent(e, ["click", "pointertap", "tap"]); + } + /** + * Maps the div focus events to pixi's EventSystem (mouseover) + * @private + * @param {FocusEvent} e - The focus event. + */ + _onFocus(e) { + if (!e.target.getAttribute("aria-live")) { + e.target.setAttribute("aria-live", "assertive"); + } + this._dispatchEvent(e, ["mouseover"]); + } + /** + * Maps the div focus events to pixi's EventSystem (mouseout) + * @private + * @param {FocusEvent} e - The focusout event. + */ + _onFocusOut(e) { + if (!e.target.getAttribute("aria-live")) { + e.target.setAttribute("aria-live", "polite"); + } + this._dispatchEvent(e, ["mouseout"]); + } + /** + * Is called when a key is pressed + * @private + * @param {KeyboardEvent} e - The keydown event. + */ + _onKeyDown(e) { + if (e.keyCode !== KEY_CODE_TAB || !this._activateOnTab) { + return; + } + this._activate(); + } + /** + * Is called when the mouse moves across the renderer element + * @private + * @param {MouseEvent} e - The mouse event. + */ + _onMouseMove(e) { + if (e.movementX === 0 && e.movementY === 0) { + return; + } + this._deactivate(); + } + /** + * Destroys the accessibility system. Removes all elements and listeners. + * > [!IMPORTANT] This is typically called automatically when the {@link Application} is destroyed. + * > A typically user should not need to call this method directly. + */ + destroy() { + var _a; + this._deactivate(); + this._destroyTouchHook(); + (_a = this._canvasObserver) == null ? void 0 : _a.destroy(); + this._canvasObserver = null; + this._div = null; + this._pools = null; + this._children = null; + this._renderer = null; + this._hookDiv = null; + globalThis.removeEventListener("keydown", this._boundOnKeyDown); + this._boundOnKeyDown = null; + globalThis.document.removeEventListener("mousemove", this._boundOnMouseMove, true); + this._boundOnMouseMove = null; + } + /** + * Enables or disables the accessibility system. + * @param enabled - Whether to enable or disable accessibility. + * @example + * ```js + * app.renderer.accessibility.setAccessibilityEnabled(true); // Enable accessibility + * app.renderer.accessibility.setAccessibilityEnabled(false); // Disable accessibility + * ``` + */ + setAccessibilityEnabled(enabled) { + if (enabled) { + this._activate(); + } else { + this._deactivate(); + } + } + _getPool(accessibleType) { + if (!this._pools[accessibleType]) { + this._pools[accessibleType] = []; + } + return this._pools[accessibleType]; + } + }; + /** @ignore */ + _AccessibilitySystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "accessibility" + }; + /** + * The default options used by the system. + * You can set these before initializing the {@link Application} to change the default behavior. + * @example + * ```js + * import { AccessibilitySystem } from 'pixi.js'; + * + * AccessibilitySystem.defaultOptions.enabledByDefault = true; + * + * const app = new Application() + * app.init() + * ``` + */ + _AccessibilitySystem.defaultOptions = { + /** + * Whether to enable accessibility features on initialization + * @default false + */ + enabledByDefault: false, + /** + * Whether to visually show the accessibility divs for debugging + * @default false + */ + debug: false, + /** + * Whether to activate accessibility when tab key is pressed + * @default true + */ + activateOnTab: true, + /** + * Whether to deactivate accessibility when mouse moves + * @default true + */ + deactivateOnMouseMove: true + }; + let AccessibilitySystem = _AccessibilitySystem; + + "use strict"; + const accessibilityTarget = { + accessible: false, + accessibleTitle: null, + accessibleHint: null, + tabIndex: 0, + accessibleType: "button", + accessibleText: null, + accessiblePointerEvents: "auto", + accessibleChildren: true, + _accessibleActive: false, + _accessibleDiv: null, + _renderId: -1 + }; + + "use strict"; + extensions.add(AccessibilitySystem); + extensions.mixin(Container, accessibilityTarget); + + "use strict"; + class EventsTickerClass { + constructor() { + /** The frequency that fake events will be fired. */ + this.interactionFrequency = 10; + this._deltaTime = 0; + this._didMove = false; + this._tickerAdded = false; + this._pauseUpdate = true; + } + /** + * Initializes the event ticker. + * @param events - The event system. + */ + init(events) { + this.removeTickerListener(); + this.events = events; + this.interactionFrequency = 10; + this._deltaTime = 0; + this._didMove = false; + this._tickerAdded = false; + this._pauseUpdate = true; + } + /** Whether to pause the update checks or not. */ + get pauseUpdate() { + return this._pauseUpdate; + } + set pauseUpdate(paused) { + this._pauseUpdate = paused; + } + /** Adds the ticker listener. */ + addTickerListener() { + if (this._tickerAdded || !this.domElement) { + return; + } + Ticker.system.add(this._tickerUpdate, this, UPDATE_PRIORITY.INTERACTION); + this._tickerAdded = true; + } + /** Removes the ticker listener. */ + removeTickerListener() { + if (!this._tickerAdded) { + return; + } + Ticker.system.remove(this._tickerUpdate, this); + this._tickerAdded = false; + } + /** Sets flag to not fire extra events when the user has already moved there mouse */ + pointerMoved() { + this._didMove = true; + } + /** Updates the state of interactive objects. */ + _update() { + if (!this.domElement || this._pauseUpdate) { + return; + } + if (this._didMove) { + this._didMove = false; + return; + } + const rootPointerEvent = this.events["_rootPointerEvent"]; + if (this.events.supportsTouchEvents && rootPointerEvent.pointerType === "touch") { + return; + } + globalThis.document.dispatchEvent(this.events.supportsPointerEvents ? new PointerEvent("pointermove", { + clientX: rootPointerEvent.clientX, + clientY: rootPointerEvent.clientY, + pointerType: rootPointerEvent.pointerType, + pointerId: rootPointerEvent.pointerId + }) : new MouseEvent("mousemove", { + clientX: rootPointerEvent.clientX, + clientY: rootPointerEvent.clientY + })); + } + /** + * Updates the state of interactive objects if at least {@link interactionFrequency} + * milliseconds have passed since the last invocation. + * + * Invoked by a throttled ticker update from {@link Ticker.system}. + * @param ticker - The throttled ticker. + */ + _tickerUpdate(ticker) { + this._deltaTime += ticker.deltaTime; + if (this._deltaTime < this.interactionFrequency) { + return; + } + this._deltaTime = 0; + this._update(); + } + /** Destroys the event ticker. */ + destroy() { + this.removeTickerListener(); + this.events = null; + this.domElement = null; + this._deltaTime = 0; + this._didMove = false; + this._tickerAdded = false; + this._pauseUpdate = true; + } + } + const EventsTicker = new EventsTickerClass(); + + "use strict"; + class FederatedMouseEvent extends FederatedEvent { + constructor() { + super(...arguments); + /** The coordinates of the mouse event relative to the canvas. */ + this.client = new Point(); + /** The movement in this pointer relative to the last `mousemove` event. */ + this.movement = new Point(); + /** The offset of the pointer coordinates w.r.t. target Container in world space. This is not supported at the moment. */ + this.offset = new Point(); + /** The pointer coordinates in world space. */ + this.global = new Point(); + /** + * The pointer coordinates in the renderer's {@link AbstractRenderer.screen screen}. This has slightly + * different semantics than native PointerEvent screenX/screenY. + */ + this.screen = new Point(); + } + /** @readonly */ + get clientX() { + return this.client.x; + } + /** @readonly */ + get clientY() { + return this.client.y; + } + /** + * Alias for {@link FederatedMouseEvent.clientX this.clientX}. + * @readonly + */ + get x() { + return this.clientX; + } + /** + * Alias for {@link FederatedMouseEvent.clientY this.clientY}. + * @readonly + */ + get y() { + return this.clientY; + } + /** @readonly */ + get movementX() { + return this.movement.x; + } + /** @readonly */ + get movementY() { + return this.movement.y; + } + /** @readonly */ + get offsetX() { + return this.offset.x; + } + /** @readonly */ + get offsetY() { + return this.offset.y; + } + /** @readonly */ + get globalX() { + return this.global.x; + } + /** @readonly */ + get globalY() { + return this.global.y; + } + /** + * The pointer coordinates in the renderer's screen. Alias for `screen.x`. + * @readonly + */ + get screenX() { + return this.screen.x; + } + /** + * The pointer coordinates in the renderer's screen. Alias for `screen.y`. + * @readonly + */ + get screenY() { + return this.screen.y; + } + /** + * Converts global coordinates into container-local coordinates. + * + * This method transforms coordinates from world space to a container's local space, + * useful for precise positioning and hit testing. + * @param container - The Container to get local coordinates for + * @param point - Optional Point object to store the result. If not provided, a new Point will be created + * @param globalPos - Optional custom global coordinates. If not provided, the event's global position is used + * @returns The local coordinates as a Point object + * @example + * ```ts + * // Basic usage - get local coordinates relative to a container + * sprite.on('pointermove', (event: FederatedMouseEvent) => { + * // Get position relative to the sprite + * const localPos = event.getLocalPosition(sprite); + * console.log('Local position:', localPos.x, localPos.y); + * }); + * // Using custom global coordinates + * const customGlobal = new Point(100, 100); + * sprite.on('pointermove', (event: FederatedMouseEvent) => { + * // Transform custom coordinates + * const localPos = event.getLocalPosition(sprite, undefined, customGlobal); + * console.log('Custom local position:', localPos.x, localPos.y); + * }); + * ``` + * @see {@link Container.worldTransform} For the transformation matrix + * @see {@link Point} For the point class used to store coordinates + */ + getLocalPosition(container, point, globalPos) { + return container.worldTransform.applyInverse(globalPos || this.global, point); + } + /** + * Whether the modifier key was pressed when this event natively occurred. + * @param key - The modifier key. + */ + getModifierState(key) { + return "getModifierState" in this.nativeEvent && this.nativeEvent.getModifierState(key); + } + /** + * Not supported. + * @param _typeArg + * @param _canBubbleArg + * @param _cancelableArg + * @param _viewArg + * @param _detailArg + * @param _screenXArg + * @param _screenYArg + * @param _clientXArg + * @param _clientYArg + * @param _ctrlKeyArg + * @param _altKeyArg + * @param _shiftKeyArg + * @param _metaKeyArg + * @param _buttonArg + * @param _relatedTargetArg + * @deprecated since 7.0.0 + * @ignore + */ + // eslint-disable-next-line max-params + initMouseEvent(_typeArg, _canBubbleArg, _cancelableArg, _viewArg, _detailArg, _screenXArg, _screenYArg, _clientXArg, _clientYArg, _ctrlKeyArg, _altKeyArg, _shiftKeyArg, _metaKeyArg, _buttonArg, _relatedTargetArg) { + throw new Error("Method not implemented."); + } + } + + "use strict"; + class FederatedPointerEvent extends FederatedMouseEvent { + constructor() { + super(...arguments); + /** + * The width of the pointer's contact along the x-axis, measured in CSS pixels. + * radiusX of TouchEvents will be represented by this value. + * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width + */ + this.width = 0; + /** + * The height of the pointer's contact along the y-axis, measured in CSS pixels. + * radiusY of TouchEvents will be represented by this value. + * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height + */ + this.height = 0; + /** + * Indicates whether or not the pointer device that created the event is the primary pointer. + * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary + */ + this.isPrimary = false; + } + /** + * Only included for completeness for now + * @ignore + */ + getCoalescedEvents() { + if (this.type === "pointermove" || this.type === "mousemove" || this.type === "touchmove") { + return [this]; + } + return []; + } + /** + * Only included for completeness for now + * @ignore + */ + getPredictedEvents() { + throw new Error("getPredictedEvents is not supported!"); + } + } + + "use strict"; + class FederatedWheelEvent extends FederatedMouseEvent { + constructor() { + super(...arguments); + /** + * Units specified in pixels. + * @ignore + */ + this.DOM_DELTA_PIXEL = 0; + /** + * Units specified in lines. + * @ignore + */ + this.DOM_DELTA_LINE = 1; + /** + * Units specified in pages. + * @ignore + */ + this.DOM_DELTA_PAGE = 2; + } + } + /** + * Units specified in pixels. + * @ignore + */ + FederatedWheelEvent.DOM_DELTA_PIXEL = 0; + /** + * Units specified in lines. + * @ignore + */ + FederatedWheelEvent.DOM_DELTA_LINE = 1; + /** + * Units specified in pages. + * @ignore + */ + FederatedWheelEvent.DOM_DELTA_PAGE = 2; + + "use strict"; + const PROPAGATION_LIMIT = 2048; + const tempHitLocation = new Point(); + const tempLocalMapping = new Point(); + class EventBoundary { + /** + * @param rootTarget - The holder of the event boundary. + */ + constructor(rootTarget) { + /** + * Emits events after they were dispatched into the scene graph. + * + * This can be used for global events listening, regardless of the scene graph being used. It should + * not be used by interactive libraries for normal use. + * + * Special events that do not bubble all the way to the root target are not emitted from here, + * e.g. pointerenter, pointerleave, click. + */ + this.dispatch = new EventEmitter(); + /** + * This flag would emit `pointermove`, `touchmove`, and `mousemove` events on all Containers. + * + * The `moveOnAll` semantics mirror those of earlier versions of PixiJS. This was disabled in favor of + * the Pointer Event API's approach. + */ + this.moveOnAll = false; + /** Enables the global move events. `globalpointermove`, `globaltouchmove`, and `globalmousemove` */ + this.enableGlobalMoveEvents = true; + /** + * State object for mapping methods. + * @see EventBoundary#trackingData + */ + this.mappingState = { + trackingData: {} + }; + /** + * The event pool maps event constructors to an free pool of instances of those specific events. + * @see EventBoundary#allocateEvent + * @see EventBoundary#freeEvent + */ + this.eventPool = /* @__PURE__ */ new Map(); + /** Every interactive element gathered from the scene. Only used in `pointermove` */ + this._allInteractiveElements = []; + /** Every element that passed the hit test. Only used in `pointermove` */ + this._hitElements = []; + /** Whether or not to collect all the interactive elements from the scene. Enabled in `pointermove` */ + this._isPointerMoveEvent = false; + this.rootTarget = rootTarget; + this.hitPruneFn = this.hitPruneFn.bind(this); + this.hitTestFn = this.hitTestFn.bind(this); + this.mapPointerDown = this.mapPointerDown.bind(this); + this.mapPointerMove = this.mapPointerMove.bind(this); + this.mapPointerOut = this.mapPointerOut.bind(this); + this.mapPointerOver = this.mapPointerOver.bind(this); + this.mapPointerUp = this.mapPointerUp.bind(this); + this.mapPointerUpOutside = this.mapPointerUpOutside.bind(this); + this.mapWheel = this.mapWheel.bind(this); + this.mappingTable = {}; + this.addEventMapping("pointerdown", this.mapPointerDown); + this.addEventMapping("pointermove", this.mapPointerMove); + this.addEventMapping("pointerout", this.mapPointerOut); + this.addEventMapping("pointerleave", this.mapPointerOut); + this.addEventMapping("pointerover", this.mapPointerOver); + this.addEventMapping("pointerup", this.mapPointerUp); + this.addEventMapping("pointerupoutside", this.mapPointerUpOutside); + this.addEventMapping("wheel", this.mapWheel); + } + /** + * Adds an event mapping for the event `type` handled by `fn`. + * + * Event mappings can be used to implement additional or custom events. They take an event + * coming from the upstream scene (or directly from the {@link EventSystem}) and dispatch new downstream events + * generally trickling down and bubbling up to {@link EventBoundary.rootTarget this.rootTarget}. + * + * To modify the semantics of existing events, the built-in mapping methods of EventBoundary should be overridden + * instead. + * @param type - The type of upstream event to map. + * @param fn - The mapping method. The context of this function must be bound manually, if desired. + */ + addEventMapping(type, fn) { + if (!this.mappingTable[type]) { + this.mappingTable[type] = []; + } + this.mappingTable[type].push({ + fn, + priority: 0 + }); + this.mappingTable[type].sort((a, b) => a.priority - b.priority); + } + /** + * Dispatches the given event + * @param e - The event to dispatch. + * @param type - The type of event to dispatch. Defaults to `e.type`. + */ + dispatchEvent(e, type) { + e.propagationStopped = false; + e.propagationImmediatelyStopped = false; + this.propagate(e, type); + this.dispatch.emit(type || e.type, e); + } + /** + * Maps the given upstream event through the event boundary and propagates it downstream. + * @param e - The event to map. + */ + mapEvent(e) { + if (!this.rootTarget) { + return; + } + const mappers = this.mappingTable[e.type]; + if (mappers) { + for (let i = 0, j = mappers.length; i < j; i++) { + mappers[i].fn(e); + } + } else { + warn(`[EventBoundary]: Event mapping not defined for ${e.type}`); + } + } + /** + * Finds the Container that is the target of a event at the given coordinates. + * + * The passed (x,y) coordinates are in the world space above this event boundary. + * @param x - The x coordinate of the event. + * @param y - The y coordinate of the event. + */ + hitTest(x, y) { + EventsTicker.pauseUpdate = true; + const useMove = this._isPointerMoveEvent && this.enableGlobalMoveEvents; + const fn = useMove ? "hitTestMoveRecursive" : "hitTestRecursive"; + const invertedPath = this[fn]( + this.rootTarget, + this.rootTarget.eventMode, + tempHitLocation.set(x, y), + this.hitTestFn, + this.hitPruneFn + ); + return invertedPath && invertedPath[0]; + } + /** + * Propagate the passed event from from {@link EventBoundary.rootTarget this.rootTarget} to its + * target `e.target`. + * @param e - The event to propagate. + * @param type - The type of event to propagate. Defaults to `e.type`. + */ + propagate(e, type) { + if (!e.target) { + return; + } + const composedPath = e.composedPath(); + e.eventPhase = e.CAPTURING_PHASE; + for (let i = 0, j = composedPath.length - 1; i < j; i++) { + e.currentTarget = composedPath[i]; + this.notifyTarget(e, type); + if (e.propagationStopped || e.propagationImmediatelyStopped) return; + } + e.eventPhase = e.AT_TARGET; + e.currentTarget = e.target; + this.notifyTarget(e, type); + if (e.propagationStopped || e.propagationImmediatelyStopped) return; + e.eventPhase = e.BUBBLING_PHASE; + for (let i = composedPath.length - 2; i >= 0; i--) { + e.currentTarget = composedPath[i]; + this.notifyTarget(e, type); + if (e.propagationStopped || e.propagationImmediatelyStopped) return; + } + } + /** + * Emits the event `e` to all interactive containers. The event is propagated in the bubbling phase always. + * + * This is used in the `globalpointermove` event. + * @param e - The emitted event. + * @param type - The listeners to notify. + * @param targets - The targets to notify. + */ + all(e, type, targets = this._allInteractiveElements) { + if (targets.length === 0) return; + e.eventPhase = e.BUBBLING_PHASE; + const events = Array.isArray(type) ? type : [type]; + for (let i = targets.length - 1; i >= 0; i--) { + events.forEach((event) => { + e.currentTarget = targets[i]; + this.notifyTarget(e, event); + }); + } + } + /** + * Finds the propagation path from {@link EventBoundary.rootTarget rootTarget} to the passed + * `target`. The last element in the path is `target`. + * @param target - The target to find the propagation path to. + */ + propagationPath(target) { + const propagationPath = [target]; + for (let i = 0; i < PROPAGATION_LIMIT && (target !== this.rootTarget && target.parent); i++) { + if (!target.parent) { + throw new Error("Cannot find propagation path to disconnected target"); + } + propagationPath.push(target.parent); + target = target.parent; + } + propagationPath.reverse(); + return propagationPath; + } + hitTestMoveRecursive(currentTarget, eventMode, location, testFn, pruneFn, ignore = false) { + let shouldReturn = false; + if (this._interactivePrune(currentTarget)) return null; + if (currentTarget.eventMode === "dynamic" || eventMode === "dynamic") { + EventsTicker.pauseUpdate = false; + } + if (currentTarget.interactiveChildren && currentTarget.children) { + const children = currentTarget.children; + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + const nestedHit = this.hitTestMoveRecursive( + child, + this._isInteractive(eventMode) ? eventMode : child.eventMode, + location, + testFn, + pruneFn, + ignore || pruneFn(currentTarget, location) + ); + if (nestedHit) { + if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent) { + continue; + } + const isInteractive = currentTarget.isInteractive(); + if (nestedHit.length > 0 || isInteractive) { + if (isInteractive) this._allInteractiveElements.push(currentTarget); + nestedHit.push(currentTarget); + } + if (this._hitElements.length === 0) this._hitElements = nestedHit; + shouldReturn = true; + } + } + } + const isInteractiveMode = this._isInteractive(eventMode); + const isInteractiveTarget = currentTarget.isInteractive(); + if (isInteractiveTarget && isInteractiveTarget) this._allInteractiveElements.push(currentTarget); + if (ignore || this._hitElements.length > 0) return null; + if (shouldReturn) return this._hitElements; + if (isInteractiveMode && (!pruneFn(currentTarget, location) && testFn(currentTarget, location))) { + return isInteractiveTarget ? [currentTarget] : []; + } + return null; + } + /** + * Recursive implementation for {@link EventBoundary.hitTest hitTest}. + * @param currentTarget - The Container that is to be hit tested. + * @param eventMode - The event mode for the `currentTarget` or one of its parents. + * @param location - The location that is being tested for overlap. + * @param testFn - Callback that determines whether the target passes hit testing. This callback + * can assume that `pruneFn` failed to prune the container. + * @param pruneFn - Callback that determiness whether the target and all of its children + * cannot pass the hit test. It is used as a preliminary optimization to prune entire subtrees + * of the scene graph. + * @returns An array holding the hit testing target and all its ancestors in order. The first element + * is the target itself and the last is {@link EventBoundary.rootTarget rootTarget}. This is the opposite + * order w.r.t. the propagation path. If no hit testing target is found, null is returned. + */ + hitTestRecursive(currentTarget, eventMode, location, testFn, pruneFn) { + if (this._interactivePrune(currentTarget) || pruneFn(currentTarget, location)) { + return null; + } + if (currentTarget.eventMode === "dynamic" || eventMode === "dynamic") { + EventsTicker.pauseUpdate = false; + } + if (currentTarget.interactiveChildren && currentTarget.children) { + const children = currentTarget.children; + const relativeLocation = location; + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + const nestedHit = this.hitTestRecursive( + child, + this._isInteractive(eventMode) ? eventMode : child.eventMode, + relativeLocation, + testFn, + pruneFn + ); + if (nestedHit) { + if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent) { + continue; + } + const isInteractive = currentTarget.isInteractive(); + if (nestedHit.length > 0 || isInteractive) nestedHit.push(currentTarget); + return nestedHit; + } + } + } + const isInteractiveMode = this._isInteractive(eventMode); + const isInteractiveTarget = currentTarget.isInteractive(); + if (isInteractiveMode && testFn(currentTarget, location)) { + return isInteractiveTarget ? [currentTarget] : []; + } + return null; + } + _isInteractive(int) { + return int === "static" || int === "dynamic"; + } + _interactivePrune(container) { + if (!container || !container.visible || !container.renderable || !container.measurable) { + return true; + } + if (container.eventMode === "none") { + return true; + } + if (container.eventMode === "passive" && !container.interactiveChildren) { + return true; + } + return false; + } + /** + * Checks whether the container or any of its children cannot pass the hit test at all. + * + * {@link EventBoundary}'s implementation uses the {@link Container.hitArea hitArea} + * and {@link Container._maskEffect} for pruning. + * @param container - The container to prune. + * @param location - The location to test for overlap. + */ + hitPruneFn(container, location) { + if (container.hitArea) { + container.worldTransform.applyInverse(location, tempLocalMapping); + if (!container.hitArea.contains(tempLocalMapping.x, tempLocalMapping.y)) { + return true; + } + } + if (container.effects && container.effects.length) { + for (let i = 0; i < container.effects.length; i++) { + const effect = container.effects[i]; + if (effect.containsPoint) { + const effectContainsPoint = effect.containsPoint(location, this.hitTestFn); + if (!effectContainsPoint) { + return true; + } + } + } + } + return false; + } + /** + * Checks whether the container passes hit testing for the given location. + * @param container - The container to test. + * @param location - The location to test for overlap. + * @returns - Whether `container` passes hit testing for `location`. + */ + hitTestFn(container, location) { + if (container.hitArea) { + return true; + } + if (container == null ? void 0 : container.containsPoint) { + container.worldTransform.applyInverse(location, tempLocalMapping); + return container.containsPoint(tempLocalMapping); + } + return false; + } + /** + * Notify all the listeners to the event's `currentTarget`. + * + * If the `currentTarget` contains the property `on`, then it is called here, + * simulating the behavior from version 6.x and prior. + * @param e - The event passed to the target. + * @param type - The type of event to notify. Defaults to `e.type`. + */ + notifyTarget(e, type) { + var _a, _b; + if (!e.currentTarget.isInteractive()) { + return; + } + type != null ? type : type = e.type; + const handlerKey = `on${type}`; + (_b = (_a = e.currentTarget)[handlerKey]) == null ? void 0 : _b.call(_a, e); + const key = e.eventPhase === e.CAPTURING_PHASE || e.eventPhase === e.AT_TARGET ? `${type}capture` : type; + this._notifyListeners(e, key); + if (e.eventPhase === e.AT_TARGET) { + this._notifyListeners(e, type); + } + } + /** + * Maps the upstream `pointerdown` events to a downstream `pointerdown` event. + * + * `touchstart`, `rightdown`, `mousedown` events are also dispatched for specific pointer types. + * @param from - The upstream `pointerdown` event. + */ + mapPointerDown(from) { + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + const e = this.createPointerEvent(from); + this.dispatchEvent(e, "pointerdown"); + if (e.pointerType === "touch") { + this.dispatchEvent(e, "touchstart"); + } else if (e.pointerType === "mouse" || e.pointerType === "pen") { + const isRightButton = e.button === 2; + this.dispatchEvent(e, isRightButton ? "rightdown" : "mousedown"); + } + const trackingData = this.trackingData(from.pointerId); + trackingData.pressTargetsByButton[from.button] = e.composedPath(); + this.freeEvent(e); + } + /** + * Maps the upstream `pointermove` to downstream `pointerout`, `pointerover`, and `pointermove` events, in that order. + * + * The tracking data for the specific pointer has an updated `overTarget`. `mouseout`, `mouseover`, + * `mousemove`, and `touchmove` events are fired as well for specific pointer types. + * @param from - The upstream `pointermove` event. + */ + mapPointerMove(from) { + var _a, _b, _c; + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + this._allInteractiveElements.length = 0; + this._hitElements.length = 0; + this._isPointerMoveEvent = true; + const e = this.createPointerEvent(from); + this._isPointerMoveEvent = false; + const isMouse = e.pointerType === "mouse" || e.pointerType === "pen"; + const trackingData = this.trackingData(from.pointerId); + const outTarget = this.findMountedTarget(trackingData.overTargets); + if (((_a = trackingData.overTargets) == null ? void 0 : _a.length) > 0 && outTarget !== e.target) { + const outType = from.type === "mousemove" ? "mouseout" : "pointerout"; + const outEvent = this.createPointerEvent(from, outType, outTarget); + this.dispatchEvent(outEvent, "pointerout"); + if (isMouse) this.dispatchEvent(outEvent, "mouseout"); + if (!e.composedPath().includes(outTarget)) { + const leaveEvent = this.createPointerEvent(from, "pointerleave", outTarget); + leaveEvent.eventPhase = leaveEvent.AT_TARGET; + while (leaveEvent.target && !e.composedPath().includes(leaveEvent.target)) { + leaveEvent.currentTarget = leaveEvent.target; + this.notifyTarget(leaveEvent); + if (isMouse) this.notifyTarget(leaveEvent, "mouseleave"); + leaveEvent.target = leaveEvent.target.parent; + } + this.freeEvent(leaveEvent); + } + this.freeEvent(outEvent); + } + if (outTarget !== e.target) { + const overType = from.type === "mousemove" ? "mouseover" : "pointerover"; + const overEvent = this.clonePointerEvent(e, overType); + this.dispatchEvent(overEvent, "pointerover"); + if (isMouse) this.dispatchEvent(overEvent, "mouseover"); + let overTargetAncestor = outTarget == null ? void 0 : outTarget.parent; + while (overTargetAncestor && overTargetAncestor !== this.rootTarget.parent) { + if (overTargetAncestor === e.target) break; + overTargetAncestor = overTargetAncestor.parent; + } + const didPointerEnter = !overTargetAncestor || overTargetAncestor === this.rootTarget.parent; + if (didPointerEnter) { + const enterEvent = this.clonePointerEvent(e, "pointerenter"); + enterEvent.eventPhase = enterEvent.AT_TARGET; + while (enterEvent.target && enterEvent.target !== outTarget && enterEvent.target !== this.rootTarget.parent) { + enterEvent.currentTarget = enterEvent.target; + this.notifyTarget(enterEvent); + if (isMouse) this.notifyTarget(enterEvent, "mouseenter"); + enterEvent.target = enterEvent.target.parent; + } + this.freeEvent(enterEvent); + } + this.freeEvent(overEvent); + } + const allMethods = []; + const allowGlobalPointerEvents = (_b = this.enableGlobalMoveEvents) != null ? _b : true; + this.moveOnAll ? allMethods.push("pointermove") : this.dispatchEvent(e, "pointermove"); + allowGlobalPointerEvents && allMethods.push("globalpointermove"); + if (e.pointerType === "touch") { + this.moveOnAll ? allMethods.splice(1, 0, "touchmove") : this.dispatchEvent(e, "touchmove"); + allowGlobalPointerEvents && allMethods.push("globaltouchmove"); + } + if (isMouse) { + this.moveOnAll ? allMethods.splice(1, 0, "mousemove") : this.dispatchEvent(e, "mousemove"); + allowGlobalPointerEvents && allMethods.push("globalmousemove"); + this.cursor = (_c = e.target) == null ? void 0 : _c.cursor; + } + if (allMethods.length > 0) { + this.all(e, allMethods); + } + this._allInteractiveElements.length = 0; + this._hitElements.length = 0; + trackingData.overTargets = e.composedPath(); + this.freeEvent(e); + } + /** + * Maps the upstream `pointerover` to downstream `pointerover` and `pointerenter` events, in that order. + * + * The tracking data for the specific pointer gets a new `overTarget`. + * @param from - The upstream `pointerover` event. + */ + mapPointerOver(from) { + var _a; + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + const trackingData = this.trackingData(from.pointerId); + const e = this.createPointerEvent(from); + const isMouse = e.pointerType === "mouse" || e.pointerType === "pen"; + this.dispatchEvent(e, "pointerover"); + if (isMouse) this.dispatchEvent(e, "mouseover"); + if (e.pointerType === "mouse") this.cursor = (_a = e.target) == null ? void 0 : _a.cursor; + const enterEvent = this.clonePointerEvent(e, "pointerenter"); + enterEvent.eventPhase = enterEvent.AT_TARGET; + while (enterEvent.target && enterEvent.target !== this.rootTarget.parent) { + enterEvent.currentTarget = enterEvent.target; + this.notifyTarget(enterEvent); + if (isMouse) this.notifyTarget(enterEvent, "mouseenter"); + enterEvent.target = enterEvent.target.parent; + } + trackingData.overTargets = e.composedPath(); + this.freeEvent(e); + this.freeEvent(enterEvent); + } + /** + * Maps the upstream `pointerout` to downstream `pointerout`, `pointerleave` events, in that order. + * + * The tracking data for the specific pointer is cleared of a `overTarget`. + * @param from - The upstream `pointerout` event. + */ + mapPointerOut(from) { + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + const trackingData = this.trackingData(from.pointerId); + if (trackingData.overTargets) { + const isMouse = from.pointerType === "mouse" || from.pointerType === "pen"; + const outTarget = this.findMountedTarget(trackingData.overTargets); + const outEvent = this.createPointerEvent(from, "pointerout", outTarget); + this.dispatchEvent(outEvent); + if (isMouse) this.dispatchEvent(outEvent, "mouseout"); + const leaveEvent = this.createPointerEvent(from, "pointerleave", outTarget); + leaveEvent.eventPhase = leaveEvent.AT_TARGET; + while (leaveEvent.target && leaveEvent.target !== this.rootTarget.parent) { + leaveEvent.currentTarget = leaveEvent.target; + this.notifyTarget(leaveEvent); + if (isMouse) this.notifyTarget(leaveEvent, "mouseleave"); + leaveEvent.target = leaveEvent.target.parent; + } + trackingData.overTargets = null; + this.freeEvent(outEvent); + this.freeEvent(leaveEvent); + } + this.cursor = null; + } + /** + * Maps the upstream `pointerup` event to downstream `pointerup`, `pointerupoutside`, + * and `click`/`rightclick`/`pointertap` events, in that order. + * + * The `pointerupoutside` event bubbles from the original `pointerdown` target to the most specific + * ancestor of the `pointerdown` and `pointerup` targets, which is also the `click` event's target. `touchend`, + * `rightup`, `mouseup`, `touchendoutside`, `rightupoutside`, `mouseupoutside`, and `tap` are fired as well for + * specific pointer types. + * @param from - The upstream `pointerup` event. + */ + mapPointerUp(from) { + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + const now = performance.now(); + const e = this.createPointerEvent(from); + this.dispatchEvent(e, "pointerup"); + if (e.pointerType === "touch") { + this.dispatchEvent(e, "touchend"); + } else if (e.pointerType === "mouse" || e.pointerType === "pen") { + const isRightButton = e.button === 2; + this.dispatchEvent(e, isRightButton ? "rightup" : "mouseup"); + } + const trackingData = this.trackingData(from.pointerId); + const pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]); + let clickTarget = pressTarget; + if (pressTarget && !e.composedPath().includes(pressTarget)) { + let currentTarget = pressTarget; + while (currentTarget && !e.composedPath().includes(currentTarget)) { + e.currentTarget = currentTarget; + this.notifyTarget(e, "pointerupoutside"); + if (e.pointerType === "touch") { + this.notifyTarget(e, "touchendoutside"); + } else if (e.pointerType === "mouse" || e.pointerType === "pen") { + const isRightButton = e.button === 2; + this.notifyTarget(e, isRightButton ? "rightupoutside" : "mouseupoutside"); + } + currentTarget = currentTarget.parent; + } + delete trackingData.pressTargetsByButton[from.button]; + clickTarget = currentTarget; + } + if (clickTarget) { + const clickEvent = this.clonePointerEvent(e, "click"); + clickEvent.target = clickTarget; + clickEvent.path = null; + if (!trackingData.clicksByButton[from.button]) { + trackingData.clicksByButton[from.button] = { + clickCount: 0, + target: clickEvent.target, + timeStamp: now + }; + } + const clickHistory = trackingData.clicksByButton[from.button]; + if (clickHistory.target === clickEvent.target && now - clickHistory.timeStamp < 200) { + ++clickHistory.clickCount; + } else { + clickHistory.clickCount = 1; + } + clickHistory.target = clickEvent.target; + clickHistory.timeStamp = now; + clickEvent.detail = clickHistory.clickCount; + if (clickEvent.pointerType === "mouse") { + const isRightButton = clickEvent.button === 2; + this.dispatchEvent(clickEvent, isRightButton ? "rightclick" : "click"); + } else if (clickEvent.pointerType === "touch") { + this.dispatchEvent(clickEvent, "tap"); + } + this.dispatchEvent(clickEvent, "pointertap"); + this.freeEvent(clickEvent); + } + this.freeEvent(e); + } + /** + * Maps the upstream `pointerupoutside` event to a downstream `pointerupoutside` event, bubbling from the original + * `pointerdown` target to `rootTarget`. + * + * (The most specific ancestor of the `pointerdown` event and the `pointerup` event must the + * `{@link EventBoundary}'s root because the `pointerup` event occurred outside of the boundary.) + * + * `touchendoutside`, `mouseupoutside`, and `rightupoutside` events are fired as well for specific pointer + * types. The tracking data for the specific pointer is cleared of a `pressTarget`. + * @param from - The upstream `pointerupoutside` event. + */ + mapPointerUpOutside(from) { + if (!(from instanceof FederatedPointerEvent)) { + warn("EventBoundary cannot map a non-pointer event as a pointer event"); + return; + } + const trackingData = this.trackingData(from.pointerId); + const pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]); + const e = this.createPointerEvent(from); + if (pressTarget) { + let currentTarget = pressTarget; + while (currentTarget) { + e.currentTarget = currentTarget; + this.notifyTarget(e, "pointerupoutside"); + if (e.pointerType === "touch") { + this.notifyTarget(e, "touchendoutside"); + } else if (e.pointerType === "mouse" || e.pointerType === "pen") { + this.notifyTarget(e, e.button === 2 ? "rightupoutside" : "mouseupoutside"); + } + currentTarget = currentTarget.parent; + } + delete trackingData.pressTargetsByButton[from.button]; + } + this.freeEvent(e); + } + /** + * Maps the upstream `wheel` event to a downstream `wheel` event. + * @param from - The upstream `wheel` event. + */ + mapWheel(from) { + if (!(from instanceof FederatedWheelEvent)) { + warn("EventBoundary cannot map a non-wheel event as a wheel event"); + return; + } + const wheelEvent = this.createWheelEvent(from); + this.dispatchEvent(wheelEvent); + this.freeEvent(wheelEvent); + } + /** + * Finds the most specific event-target in the given propagation path that is still mounted in the scene graph. + * + * This is used to find the correct `pointerup` and `pointerout` target in the case that the original `pointerdown` + * or `pointerover` target was unmounted from the scene graph. + * @param propagationPath - The propagation path was valid in the past. + * @returns - The most specific event-target still mounted at the same location in the scene graph. + */ + findMountedTarget(propagationPath) { + if (!propagationPath) { + return null; + } + let currentTarget = propagationPath[0]; + for (let i = 1; i < propagationPath.length; i++) { + if (propagationPath[i].parent === currentTarget) { + currentTarget = propagationPath[i]; + } else { + break; + } + } + return currentTarget; + } + /** + * Creates an event whose `originalEvent` is `from`, with an optional `type` and `target` override. + * + * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}. + * @param from - The `originalEvent` for the returned event. + * @param [type=from.type] - The type of the returned event. + * @param target - The target of the returned event. + */ + createPointerEvent(from, type, target) { + var _a; + const event = this.allocateEvent(FederatedPointerEvent); + this.copyPointerData(from, event); + this.copyMouseData(from, event); + this.copyData(from, event); + event.nativeEvent = from.nativeEvent; + event.originalEvent = from; + event.target = (_a = target != null ? target : this.hitTest(event.global.x, event.global.y)) != null ? _a : this._hitElements[0]; + if (typeof type === "string") { + event.type = type; + } + return event; + } + /** + * Creates a wheel event whose `originalEvent` is `from`. + * + * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}. + * @param from - The upstream wheel event. + */ + createWheelEvent(from) { + const event = this.allocateEvent(FederatedWheelEvent); + this.copyWheelData(from, event); + this.copyMouseData(from, event); + this.copyData(from, event); + event.nativeEvent = from.nativeEvent; + event.originalEvent = from; + event.target = this.hitTest(event.global.x, event.global.y); + return event; + } + /** + * Clones the event `from`, with an optional `type` override. + * + * The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}. + * @param from - The event to clone. + * @param [type=from.type] - The type of the returned event. + */ + clonePointerEvent(from, type) { + const event = this.allocateEvent(FederatedPointerEvent); + event.nativeEvent = from.nativeEvent; + event.originalEvent = from.originalEvent; + this.copyPointerData(from, event); + this.copyMouseData(from, event); + this.copyData(from, event); + event.target = from.target; + event.path = from.composedPath().slice(); + event.type = type != null ? type : event.type; + return event; + } + /** + * Copies wheel {@link FederatedWheelEvent} data from `from` into `to`. + * + * The following properties are copied: + * + deltaMode + * + deltaX + * + deltaY + * + deltaZ + * @param from - The event to copy data from. + * @param to - The event to copy data into. + */ + copyWheelData(from, to) { + to.deltaMode = from.deltaMode; + to.deltaX = from.deltaX; + to.deltaY = from.deltaY; + to.deltaZ = from.deltaZ; + } + /** + * Copies pointer {@link FederatedPointerEvent} data from `from` into `to`. + * + * The following properties are copied: + * + pointerId + * + width + * + height + * + isPrimary + * + pointerType + * + pressure + * + tangentialPressure + * + tiltX + * + tiltY + * @param from - The event to copy data from. + * @param to - The event to copy data into. + */ + copyPointerData(from, to) { + if (!(from instanceof FederatedPointerEvent && to instanceof FederatedPointerEvent)) return; + to.pointerId = from.pointerId; + to.width = from.width; + to.height = from.height; + to.isPrimary = from.isPrimary; + to.pointerType = from.pointerType; + to.pressure = from.pressure; + to.tangentialPressure = from.tangentialPressure; + to.tiltX = from.tiltX; + to.tiltY = from.tiltY; + to.twist = from.twist; + } + /** + * Copies mouse {@link FederatedMouseEvent} data from `from` to `to`. + * + * The following properties are copied: + * + altKey + * + button + * + buttons + * + clientX + * + clientY + * + metaKey + * + movementX + * + movementY + * + pageX + * + pageY + * + x + * + y + * + screen + * + shiftKey + * + global + * @param from - The event to copy data from. + * @param to - The event to copy data into. + */ + copyMouseData(from, to) { + if (!(from instanceof FederatedMouseEvent && to instanceof FederatedMouseEvent)) return; + to.altKey = from.altKey; + to.button = from.button; + to.buttons = from.buttons; + to.client.copyFrom(from.client); + to.ctrlKey = from.ctrlKey; + to.metaKey = from.metaKey; + to.movement.copyFrom(from.movement); + to.screen.copyFrom(from.screen); + to.shiftKey = from.shiftKey; + to.global.copyFrom(from.global); + } + /** + * Copies base {@link FederatedEvent} data from `from` into `to`. + * + * The following properties are copied: + * + isTrusted + * + srcElement + * + timeStamp + * + type + * @param from - The event to copy data from. + * @param to - The event to copy data into. + */ + copyData(from, to) { + to.isTrusted = from.isTrusted; + to.srcElement = from.srcElement; + to.timeStamp = performance.now(); + to.type = from.type; + to.detail = from.detail; + to.view = from.view; + to.which = from.which; + to.layer.copyFrom(from.layer); + to.page.copyFrom(from.page); + } + /** + * @param id - The pointer ID. + * @returns The tracking data stored for the given pointer. If no data exists, a blank + * state will be created. + */ + trackingData(id) { + if (!this.mappingState.trackingData[id]) { + this.mappingState.trackingData[id] = { + pressTargetsByButton: {}, + clicksByButton: {}, + overTarget: null + }; + } + return this.mappingState.trackingData[id]; + } + /** + * Allocate a specific type of event from {@link EventBoundary#eventPool this.eventPool}. + * + * This allocation is constructor-agnostic, as long as it only takes one argument - this event + * boundary. + * @param constructor - The event's constructor. + * @returns An event of the given type. + */ + allocateEvent(constructor) { + if (!this.eventPool.has(constructor)) { + this.eventPool.set(constructor, []); + } + const event = this.eventPool.get(constructor).pop() || new constructor(this); + event.eventPhase = event.NONE; + event.currentTarget = null; + event.defaultPrevented = false; + event.path = null; + event.target = null; + return event; + } + /** + * Frees the event and puts it back into the event pool. + * + * It is illegal to reuse the event until it is allocated again, using `this.allocateEvent`. + * + * It is also advised that events not allocated from {@link EventBoundary#allocateEvent this.allocateEvent} + * not be freed. This is because of the possibility that the same event is freed twice, which can cause + * it to be allocated twice & result in overwriting. + * @param event - The event to be freed. + * @throws Error if the event is managed by another event boundary. + */ + freeEvent(event) { + if (event.manager !== this) throw new Error("It is illegal to free an event not managed by this EventBoundary!"); + const constructor = event.constructor; + if (!this.eventPool.has(constructor)) { + this.eventPool.set(constructor, []); + } + this.eventPool.get(constructor).push(event); + } + /** + * Similar to {@link EventEmitter.emit}, except it stops if the `propagationImmediatelyStopped` flag + * is set on the event. + * @param e - The event to call each listener with. + * @param type - The event key. + */ + _notifyListeners(e, type) { + const listeners = e.currentTarget._events[type]; + if (!listeners) return; + if ("fn" in listeners) { + if (listeners.once) e.currentTarget.removeListener(type, listeners.fn, void 0, true); + listeners.fn.call(listeners.context, e); + } else { + for (let i = 0, j = listeners.length; i < j && !e.propagationImmediatelyStopped; i++) { + if (listeners[i].once) e.currentTarget.removeListener(type, listeners[i].fn, void 0, true); + listeners[i].fn.call(listeners[i].context, e); + } + } + } + } + + "use strict"; + var __defProp$1e = Object.defineProperty; + var __getOwnPropSymbols$1g = Object.getOwnPropertySymbols; + var __hasOwnProp$1g = Object.prototype.hasOwnProperty; + var __propIsEnum$1g = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1e = (obj, key, value) => key in obj ? __defProp$1e(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1e = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1g.call(b, prop)) + __defNormalProp$1e(a, prop, b[prop]); + if (__getOwnPropSymbols$1g) + for (var prop of __getOwnPropSymbols$1g(b)) { + if (__propIsEnum$1g.call(b, prop)) + __defNormalProp$1e(a, prop, b[prop]); + } + return a; + }; + const MOUSE_POINTER_ID = 1; + const TOUCH_TO_POINTER = { + touchstart: "pointerdown", + touchend: "pointerup", + touchendoutside: "pointerupoutside", + touchmove: "pointermove", + touchcancel: "pointercancel" + }; + const _EventSystem = class _EventSystem { + /** + * @param {Renderer} renderer + */ + constructor(renderer) { + /** + * Indicates whether the current device supports touch events according to the W3C Touch Events spec. + * This is used to determine the appropriate event handling strategy. + * @see {@link https://www.w3.org/TR/touch-events/} W3C Touch Events Specification + * @readonly + * @default 'ontouchstart' in globalThis + */ + this.supportsTouchEvents = "ontouchstart" in globalThis; + /** + * Indicates whether the current device supports pointer events according to the W3C Pointer Events spec. + * Used to optimize event handling and provide more consistent cross-device interaction. + * @see {@link https://www.w3.org/TR/pointerevents/} W3C Pointer Events Specification + * @readonly + * @default !!globalThis.PointerEvent + */ + this.supportsPointerEvents = !!globalThis.PointerEvent; + /** + * The DOM element to which the root event listeners are bound. This is automatically set to + * the renderer's {@link Renderer#view view}. + */ + this.domElement = null; + /** The resolution used to convert between the DOM client space into world space. */ + this.resolution = 1; + this.renderer = renderer; + this.rootBoundary = new EventBoundary(null); + EventsTicker.init(this); + this.autoPreventDefault = true; + this._eventsAdded = false; + this._rootPointerEvent = new FederatedPointerEvent(null); + this._rootWheelEvent = new FederatedWheelEvent(null); + this.cursorStyles = { + default: "inherit", + pointer: "pointer" + }; + this.features = new Proxy(__spreadValues$1e({}, _EventSystem.defaultEventFeatures), { + set: (target, key, value) => { + if (key === "globalMove") { + this.rootBoundary.enableGlobalMoveEvents = value; + } + target[key] = value; + return true; + } + }); + this._onPointerDown = this._onPointerDown.bind(this); + this._onPointerMove = this._onPointerMove.bind(this); + this._onPointerUp = this._onPointerUp.bind(this); + this._onPointerOverOut = this._onPointerOverOut.bind(this); + this.onWheel = this.onWheel.bind(this); + } + /** + * The default interaction mode for all display objects. + * @see Container.eventMode + * @type {EventMode} + * @readonly + * @since 7.2.0 + */ + static get defaultEventMode() { + return this._defaultEventMode; + } + /** + * Runner init called, view is available at this point. + * @ignore + */ + init(options) { + var _a, _b; + const { canvas, resolution } = this.renderer; + this.setTargetElement(canvas); + this.resolution = resolution; + _EventSystem._defaultEventMode = (_a = options.eventMode) != null ? _a : "passive"; + Object.assign(this.features, (_b = options.eventFeatures) != null ? _b : {}); + this.rootBoundary.enableGlobalMoveEvents = this.features.globalMove; + } + /** + * Handle changing resolution. + * @ignore + */ + resolutionChange(resolution) { + this.resolution = resolution; + } + /** Destroys all event listeners and detaches the renderer. */ + destroy() { + EventsTicker.destroy(); + this.setTargetElement(null); + this.renderer = null; + this._currentCursor = null; + } + /** + * Sets the current cursor mode, handling any callbacks or CSS style changes. + * The cursor can be a CSS cursor string, a custom callback function, or a key from the cursorStyles dictionary. + * @param mode - Cursor mode to set. Can be: + * - A CSS cursor string (e.g., 'pointer', 'grab') + * - A key from the cursorStyles dictionary + * - null/undefined to reset to default + * @example + * ```ts + * // Using predefined cursor styles + * app.renderer.events.setCursor('pointer'); // Set standard pointer cursor + * app.renderer.events.setCursor('grab'); // Set grab cursor + * app.renderer.events.setCursor(null); // Reset to default + * + * // Using custom cursor styles + * app.renderer.events.cursorStyles.custom = 'url("cursor.png"), auto'; + * app.renderer.events.setCursor('custom'); // Apply custom cursor + * + * // Using callback-based cursor + * app.renderer.events.cursorStyles.dynamic = (mode) => { + * document.body.style.cursor = mode === 'hover' ? 'pointer' : 'default'; + * }; + * app.renderer.events.setCursor('dynamic'); // Trigger cursor callback + * ``` + * @remarks + * - Has no effect on OffscreenCanvas except for callback-based cursors + * - Caches current cursor to avoid unnecessary DOM updates + * - Supports CSS cursor values, style objects, and callback functions + * @see {@link EventSystem.cursorStyles} For defining custom cursor styles + * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor} MDN Cursor Reference + */ + setCursor(mode) { + mode || (mode = "default"); + let applyStyles = true; + if (globalThis.OffscreenCanvas && this.domElement instanceof OffscreenCanvas) { + applyStyles = false; + } + if (this._currentCursor === mode) { + return; + } + this._currentCursor = mode; + const style = this.cursorStyles[mode]; + if (style) { + switch (typeof style) { + case "string": + if (applyStyles) { + this.domElement.style.cursor = style; + } + break; + case "function": + style(mode); + break; + case "object": + if (applyStyles) { + Object.assign(this.domElement.style, style); + } + break; + } + } else if (applyStyles && typeof mode === "string" && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode)) { + this.domElement.style.cursor = mode; + } + } + /** + * The global pointer event instance containing the most recent pointer state. + * This is useful for accessing pointer information without listening to events. + * @example + * ```ts + * // Access current pointer position at any time + * const eventSystem = app.renderer.events; + * const pointer = eventSystem.pointer; + * + * // Get global coordinates + * console.log('Position:', pointer.global.x, pointer.global.y); + * + * // Check button state + * console.log('Buttons pressed:', pointer.buttons); + * + * // Get pointer type and pressure + * console.log('Type:', pointer.pointerType); + * console.log('Pressure:', pointer.pressure); + * ``` + * @readonly + * @since 7.2.0 + * @see {@link FederatedPointerEvent} For all available pointer properties + */ + get pointer() { + return this._rootPointerEvent; + } + /** + * Event handler for pointer down events on {@link EventSystem#domElement this.domElement}. + * @param nativeEvent - The native mouse/pointer/touch event. + */ + _onPointerDown(nativeEvent) { + if (!this.features.click) return; + this.rootBoundary.rootTarget = this.renderer.lastObjectRendered; + const events = this._normalizeToPointerData(nativeEvent); + if (this.autoPreventDefault && events[0].isNormalized) { + const cancelable = nativeEvent.cancelable || !("cancelable" in nativeEvent); + if (cancelable) { + nativeEvent.preventDefault(); + } + } + for (let i = 0, j = events.length; i < j; i++) { + const nativeEvent2 = events[i]; + const federatedEvent = this._bootstrapEvent(this._rootPointerEvent, nativeEvent2); + this.rootBoundary.mapEvent(federatedEvent); + } + this.setCursor(this.rootBoundary.cursor); + } + /** + * Event handler for pointer move events on on {@link EventSystem#domElement this.domElement}. + * @param nativeEvent - The native mouse/pointer/touch events. + */ + _onPointerMove(nativeEvent) { + if (!this.features.move) return; + this.rootBoundary.rootTarget = this.renderer.lastObjectRendered; + EventsTicker.pointerMoved(); + const normalizedEvents = this._normalizeToPointerData(nativeEvent); + for (let i = 0, j = normalizedEvents.length; i < j; i++) { + const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]); + this.rootBoundary.mapEvent(event); + } + this.setCursor(this.rootBoundary.cursor); + } + /** + * Event handler for pointer up events on {@link EventSystem#domElement this.domElement}. + * @param nativeEvent - The native mouse/pointer/touch event. + */ + _onPointerUp(nativeEvent) { + if (!this.features.click) return; + this.rootBoundary.rootTarget = this.renderer.lastObjectRendered; + let target = nativeEvent.target; + if (nativeEvent.composedPath && nativeEvent.composedPath().length > 0) { + target = nativeEvent.composedPath()[0]; + } + const outside = target !== this.domElement ? "outside" : ""; + const normalizedEvents = this._normalizeToPointerData(nativeEvent); + for (let i = 0, j = normalizedEvents.length; i < j; i++) { + const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]); + event.type += outside; + this.rootBoundary.mapEvent(event); + } + this.setCursor(this.rootBoundary.cursor); + } + /** + * Event handler for pointer over & out events on {@link EventSystem#domElement this.domElement}. + * @param nativeEvent - The native mouse/pointer/touch event. + */ + _onPointerOverOut(nativeEvent) { + if (!this.features.click) return; + this.rootBoundary.rootTarget = this.renderer.lastObjectRendered; + const normalizedEvents = this._normalizeToPointerData(nativeEvent); + for (let i = 0, j = normalizedEvents.length; i < j; i++) { + const event = this._bootstrapEvent(this._rootPointerEvent, normalizedEvents[i]); + this.rootBoundary.mapEvent(event); + } + this.setCursor(this.rootBoundary.cursor); + } + /** + * Passive handler for `wheel` events on {@link EventSystem.domElement this.domElement}. + * @param nativeEvent - The native wheel event. + */ + onWheel(nativeEvent) { + if (!this.features.wheel) return; + const wheelEvent = this.normalizeWheelEvent(nativeEvent); + this.rootBoundary.rootTarget = this.renderer.lastObjectRendered; + this.rootBoundary.mapEvent(wheelEvent); + } + /** + * Sets the {@link EventSystem#domElement domElement} and binds event listeners. + * This method manages the DOM event bindings for the event system, allowing you to + * change or remove the target element that receives input events. + * > [!IMPORTANT] This will default to the canvas element of the renderer, so you + * > should not need to call this unless you are using a custom element. + * @param element - The new DOM element to bind events to, or null to remove all event bindings + * @example + * ```ts + * // Set a new canvas element as the target + * const canvas = document.createElement('canvas'); + * app.renderer.events.setTargetElement(canvas); + * + * // Remove all event bindings + * app.renderer.events.setTargetElement(null); + * + * // Switch to a different canvas + * const newCanvas = document.querySelector('#game-canvas'); + * app.renderer.events.setTargetElement(newCanvas); + * ``` + * @remarks + * - Automatically removes event listeners from previous element + * - Required for the event system to function + * - Safe to call multiple times + * @see {@link EventSystem#domElement} The current DOM element + * @see {@link EventsTicker} For the ticker system that tracks pointer movement + */ + setTargetElement(element) { + this._removeEvents(); + this.domElement = element; + EventsTicker.domElement = element; + this._addEvents(); + } + /** Register event listeners on {@link Renderer#domElement this.domElement}. */ + _addEvents() { + if (this._eventsAdded || !this.domElement) { + return; + } + EventsTicker.addTickerListener(); + const style = this.domElement.style; + if (style) { + if (globalThis.navigator.msPointerEnabled) { + style.msContentZooming = "none"; + style.msTouchAction = "none"; + } else if (this.supportsPointerEvents) { + style.touchAction = "none"; + } + } + if (this.supportsPointerEvents) { + globalThis.document.addEventListener("pointermove", this._onPointerMove, true); + this.domElement.addEventListener("pointerdown", this._onPointerDown, true); + this.domElement.addEventListener("pointerleave", this._onPointerOverOut, true); + this.domElement.addEventListener("pointerover", this._onPointerOverOut, true); + globalThis.addEventListener("pointerup", this._onPointerUp, true); + } else { + globalThis.document.addEventListener("mousemove", this._onPointerMove, true); + this.domElement.addEventListener("mousedown", this._onPointerDown, true); + this.domElement.addEventListener("mouseout", this._onPointerOverOut, true); + this.domElement.addEventListener("mouseover", this._onPointerOverOut, true); + globalThis.addEventListener("mouseup", this._onPointerUp, true); + if (this.supportsTouchEvents) { + this.domElement.addEventListener("touchstart", this._onPointerDown, true); + this.domElement.addEventListener("touchend", this._onPointerUp, true); + this.domElement.addEventListener("touchmove", this._onPointerMove, true); + } + } + this.domElement.addEventListener("wheel", this.onWheel, { + passive: true, + capture: true + }); + this._eventsAdded = true; + } + /** Unregister event listeners on {@link EventSystem#domElement this.domElement}. */ + _removeEvents() { + if (!this._eventsAdded || !this.domElement) { + return; + } + EventsTicker.removeTickerListener(); + const style = this.domElement.style; + if (style) { + if (globalThis.navigator.msPointerEnabled) { + style.msContentZooming = ""; + style.msTouchAction = ""; + } else if (this.supportsPointerEvents) { + style.touchAction = ""; + } + } + if (this.supportsPointerEvents) { + globalThis.document.removeEventListener("pointermove", this._onPointerMove, true); + this.domElement.removeEventListener("pointerdown", this._onPointerDown, true); + this.domElement.removeEventListener("pointerleave", this._onPointerOverOut, true); + this.domElement.removeEventListener("pointerover", this._onPointerOverOut, true); + globalThis.removeEventListener("pointerup", this._onPointerUp, true); + } else { + globalThis.document.removeEventListener("mousemove", this._onPointerMove, true); + this.domElement.removeEventListener("mousedown", this._onPointerDown, true); + this.domElement.removeEventListener("mouseout", this._onPointerOverOut, true); + this.domElement.removeEventListener("mouseover", this._onPointerOverOut, true); + globalThis.removeEventListener("mouseup", this._onPointerUp, true); + if (this.supportsTouchEvents) { + this.domElement.removeEventListener("touchstart", this._onPointerDown, true); + this.domElement.removeEventListener("touchend", this._onPointerUp, true); + this.domElement.removeEventListener("touchmove", this._onPointerMove, true); + } + } + this.domElement.removeEventListener("wheel", this.onWheel, true); + this.domElement = null; + this._eventsAdded = false; + } + /** + * Maps coordinates from DOM/screen space into PixiJS normalized coordinates. + * This takes into account the current scale, position, and resolution of the DOM element. + * @param point - The point to store the mapped coordinates in + * @param x - The x coordinate in DOM/client space + * @param y - The y coordinate in DOM/client space + * @example + * ```ts + * // Map mouse coordinates to PixiJS space + * const point = new Point(); + * app.renderer.events.mapPositionToPoint( + * point, + * event.clientX, + * event.clientY + * ); + * console.log('Mapped position:', point.x, point.y); + * + * // Using with pointer events + * sprite.on('pointermove', (event) => { + * // event.global already contains mapped coordinates + * console.log('Global:', event.global.x, event.global.y); + * + * // Map to local coordinates + * const local = event.getLocalPosition(sprite); + * console.log('Local:', local.x, local.y); + * }); + * ``` + * @remarks + * - Accounts for element scaling and positioning + * - Adjusts for device pixel ratio/resolution + */ + mapPositionToPoint(point, x, y) { + const rect = this.domElement.isConnected ? this.domElement.getBoundingClientRect() : { + x: 0, + y: 0, + width: this.domElement.width, + height: this.domElement.height, + left: 0, + top: 0 + }; + const resolutionMultiplier = 1 / this.resolution; + point.x = (x - rect.left) * (this.domElement.width / rect.width) * resolutionMultiplier; + point.y = (y - rect.top) * (this.domElement.height / rect.height) * resolutionMultiplier; + } + /** + * Ensures that the original event object contains all data that a regular pointer event would have + * @param event - The original event data from a touch or mouse event + * @returns An array containing a single normalized pointer event, in the case of a pointer + * or mouse event, or a multiple normalized pointer events if there are multiple changed touches + */ + _normalizeToPointerData(event) { + const normalizedEvents = []; + if (this.supportsTouchEvents && event instanceof TouchEvent) { + for (let i = 0, li = event.changedTouches.length; i < li; i++) { + const touch = event.changedTouches[i]; + if (typeof touch.button === "undefined") touch.button = 0; + if (typeof touch.buttons === "undefined") touch.buttons = 1; + if (typeof touch.isPrimary === "undefined") { + touch.isPrimary = event.touches.length === 1 && event.type === "touchstart"; + } + if (typeof touch.width === "undefined") touch.width = touch.radiusX || 1; + if (typeof touch.height === "undefined") touch.height = touch.radiusY || 1; + if (typeof touch.tiltX === "undefined") touch.tiltX = 0; + if (typeof touch.tiltY === "undefined") touch.tiltY = 0; + if (typeof touch.pointerType === "undefined") touch.pointerType = "touch"; + if (typeof touch.pointerId === "undefined") touch.pointerId = touch.identifier || 0; + if (typeof touch.pressure === "undefined") touch.pressure = touch.force || 0.5; + if (typeof touch.twist === "undefined") touch.twist = 0; + if (typeof touch.tangentialPressure === "undefined") touch.tangentialPressure = 0; + if (typeof touch.layerX === "undefined") touch.layerX = touch.offsetX = touch.clientX; + if (typeof touch.layerY === "undefined") touch.layerY = touch.offsetY = touch.clientY; + touch.isNormalized = true; + touch.type = event.type; + normalizedEvents.push(touch); + } + } else if (!globalThis.MouseEvent || event instanceof MouseEvent && (!this.supportsPointerEvents || !(event instanceof globalThis.PointerEvent))) { + const tempEvent = event; + if (typeof tempEvent.isPrimary === "undefined") tempEvent.isPrimary = true; + if (typeof tempEvent.width === "undefined") tempEvent.width = 1; + if (typeof tempEvent.height === "undefined") tempEvent.height = 1; + if (typeof tempEvent.tiltX === "undefined") tempEvent.tiltX = 0; + if (typeof tempEvent.tiltY === "undefined") tempEvent.tiltY = 0; + if (typeof tempEvent.pointerType === "undefined") tempEvent.pointerType = "mouse"; + if (typeof tempEvent.pointerId === "undefined") tempEvent.pointerId = MOUSE_POINTER_ID; + if (typeof tempEvent.pressure === "undefined") tempEvent.pressure = 0.5; + if (typeof tempEvent.twist === "undefined") tempEvent.twist = 0; + if (typeof tempEvent.tangentialPressure === "undefined") tempEvent.tangentialPressure = 0; + tempEvent.isNormalized = true; + normalizedEvents.push(tempEvent); + } else { + normalizedEvents.push(event); + } + return normalizedEvents; + } + /** + * Normalizes the native {@link https://w3c.github.io/uievents/#interface-wheelevent WheelEvent}. + * + * The returned {@link FederatedWheelEvent} is a shared instance. It will not persist across + * multiple native wheel events. + * @param nativeEvent - The native wheel event that occurred on the canvas. + * @returns A federated wheel event. + */ + normalizeWheelEvent(nativeEvent) { + const event = this._rootWheelEvent; + this._transferMouseData(event, nativeEvent); + event.deltaX = nativeEvent.deltaX; + event.deltaY = nativeEvent.deltaY; + event.deltaZ = nativeEvent.deltaZ; + event.deltaMode = nativeEvent.deltaMode; + this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY); + event.global.copyFrom(event.screen); + event.offset.copyFrom(event.screen); + event.nativeEvent = nativeEvent; + event.type = nativeEvent.type; + return event; + } + /** + * Normalizes the `nativeEvent` into a federateed {@link FederatedPointerEvent}. + * @param event + * @param nativeEvent + */ + _bootstrapEvent(event, nativeEvent) { + event.originalEvent = null; + event.nativeEvent = nativeEvent; + event.pointerId = nativeEvent.pointerId; + event.width = nativeEvent.width; + event.height = nativeEvent.height; + event.isPrimary = nativeEvent.isPrimary; + event.pointerType = nativeEvent.pointerType; + event.pressure = nativeEvent.pressure; + event.tangentialPressure = nativeEvent.tangentialPressure; + event.tiltX = nativeEvent.tiltX; + event.tiltY = nativeEvent.tiltY; + event.twist = nativeEvent.twist; + this._transferMouseData(event, nativeEvent); + this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY); + event.global.copyFrom(event.screen); + event.offset.copyFrom(event.screen); + event.isTrusted = nativeEvent.isTrusted; + if (event.type === "pointerleave") { + event.type = "pointerout"; + } + if (event.type.startsWith("mouse")) { + event.type = event.type.replace("mouse", "pointer"); + } + if (event.type.startsWith("touch")) { + event.type = TOUCH_TO_POINTER[event.type] || event.type; + } + return event; + } + /** + * Transfers base & mouse event data from the `nativeEvent` to the federated event. + * @param event + * @param nativeEvent + */ + _transferMouseData(event, nativeEvent) { + event.isTrusted = nativeEvent.isTrusted; + event.srcElement = nativeEvent.srcElement; + event.timeStamp = performance.now(); + event.type = nativeEvent.type; + event.altKey = nativeEvent.altKey; + event.button = nativeEvent.button; + event.buttons = nativeEvent.buttons; + event.client.x = nativeEvent.clientX; + event.client.y = nativeEvent.clientY; + event.ctrlKey = nativeEvent.ctrlKey; + event.metaKey = nativeEvent.metaKey; + event.movement.x = nativeEvent.movementX; + event.movement.y = nativeEvent.movementY; + event.page.x = nativeEvent.pageX; + event.page.y = nativeEvent.pageY; + event.relatedTarget = null; + event.shiftKey = nativeEvent.shiftKey; + } + }; + /** @ignore */ + _EventSystem.extension = { + name: "events", + type: [ + ExtensionType.WebGLSystem, + ExtensionType.CanvasSystem, + ExtensionType.WebGPUSystem + ], + priority: -1 + }; + /** + * The event features that are enabled by the EventSystem + * @since 7.2.0 + * @example + * ```ts + * import { EventSystem, EventSystemFeatures } from 'pixi.js'; + * // Access the default event features + * EventSystem.defaultEventFeatures = { + * // Enable pointer movement events + * move: true, + * // Enable global pointer move events + * globalMove: true, + * // Enable click events + * click: true, + * // Enable wheel events + * wheel: true, + * }; + * ``` + */ + _EventSystem.defaultEventFeatures = { + /** Enables pointer events associated with pointer movement. */ + move: true, + /** Enables global pointer move events. */ + globalMove: true, + /** Enables pointer events associated with clicking. */ + click: true, + /** Enables wheel events. */ + wheel: true + }; + let EventSystem = _EventSystem; + + "use strict"; + const FederatedContainer = { + onclick: null, + onmousedown: null, + onmouseenter: null, + onmouseleave: null, + onmousemove: null, + onglobalmousemove: null, + onmouseout: null, + onmouseover: null, + onmouseup: null, + onmouseupoutside: null, + onpointercancel: null, + onpointerdown: null, + onpointerenter: null, + onpointerleave: null, + onpointermove: null, + onglobalpointermove: null, + onpointerout: null, + onpointerover: null, + onpointertap: null, + onpointerup: null, + onpointerupoutside: null, + onrightclick: null, + onrightdown: null, + onrightup: null, + onrightupoutside: null, + ontap: null, + ontouchcancel: null, + ontouchend: null, + ontouchendoutside: null, + ontouchmove: null, + onglobaltouchmove: null, + ontouchstart: null, + onwheel: null, + get interactive() { + return this.eventMode === "dynamic" || this.eventMode === "static"; + }, + set interactive(value) { + this.eventMode = value ? "static" : "passive"; + }, + _internalEventMode: void 0, + get eventMode() { + var _a; + return (_a = this._internalEventMode) != null ? _a : EventSystem.defaultEventMode; + }, + set eventMode(value) { + this._internalEventMode = value; + }, + isInteractive() { + return this.eventMode === "static" || this.eventMode === "dynamic"; + }, + interactiveChildren: true, + hitArea: null, + addEventListener(type, listener, options) { + const capture = typeof options === "boolean" && options || typeof options === "object" && options.capture; + const signal = typeof options === "object" ? options.signal : void 0; + const once = typeof options === "object" ? options.once === true : false; + const context = typeof listener === "function" ? void 0 : listener; + type = capture ? `${type}capture` : type; + const listenerFn = typeof listener === "function" ? listener : listener.handleEvent; + const emitter = this; + if (signal) { + signal.addEventListener("abort", () => { + emitter.off(type, listenerFn, context); + }); + } + if (once) { + emitter.once(type, listenerFn, context); + } else { + emitter.on(type, listenerFn, context); + } + }, + removeEventListener(type, listener, options) { + const capture = typeof options === "boolean" && options || typeof options === "object" && options.capture; + const context = typeof listener === "function" ? void 0 : listener; + type = capture ? `${type}capture` : type; + listener = typeof listener === "function" ? listener : listener.handleEvent; + this.off(type, listener, context); + }, + dispatchEvent(e) { + if (!(e instanceof FederatedEvent)) { + throw new Error("Container cannot propagate events outside of the Federated Events API"); + } + e.defaultPrevented = false; + e.path = null; + e.target = this; + e.manager.dispatchEvent(e); + return !e.defaultPrevented; + } + }; + + "use strict"; + extensions.add(EventSystem); + extensions.mixin(Container, FederatedContainer); + + "use strict"; + var LoaderParserPriority = /* @__PURE__ */ ((LoaderParserPriority2) => { + LoaderParserPriority2[LoaderParserPriority2["Low"] = 0] = "Low"; + LoaderParserPriority2[LoaderParserPriority2["Normal"] = 1] = "Normal"; + LoaderParserPriority2[LoaderParserPriority2["High"] = 2] = "High"; + return LoaderParserPriority2; + })(LoaderParserPriority || {}); + + "use strict"; + const BrowserAdapter = { + createCanvas: (width, height) => { + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + return canvas; + }, + createImage: () => new Image(), + getCanvasRenderingContext2D: () => CanvasRenderingContext2D, + getWebGLRenderingContext: () => WebGLRenderingContext, + getNavigator: () => navigator, + getBaseUrl: () => { + var _a; + return (_a = document.baseURI) != null ? _a : window.location.href; + }, + getFontFaceSet: () => document.fonts, + fetch: (url, options) => fetch(url, options), + parseXML: (xml) => { + const parser = new DOMParser(); + return parser.parseFromString(xml, "text/xml"); + } + }; + + "use strict"; + let currentAdapter = BrowserAdapter; + const DOMAdapter = { + /** + * Returns the current adapter. + * @returns {environment.Adapter} The current adapter. + */ + get() { + return currentAdapter; + }, + /** + * Sets the current adapter. + * @param adapter - The new adapter. + */ + set(adapter) { + currentAdapter = adapter; + } + }; + + "use strict"; + function assertPath(path2) { + if (typeof path2 !== "string") { + throw new TypeError(`Path must be a string. Received ${JSON.stringify(path2)}`); + } + } + function removeUrlParams(url) { + const re = url.split("?")[0]; + return re.split("#")[0]; + } + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + function replaceAll(str, find, replace) { + return str.replace(new RegExp(escapeRegExp(find), "g"), replace); + } + function normalizeStringPosix(path2, allowAboveRoot) { + let res = ""; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code = -1; + for (let i = 0; i <= path2.length; ++i) { + if (i < path2.length) { + code = path2.charCodeAt(i); + } else if (code === 47) { + break; + } else { + code = 47; + } + if (code === 47) { + if (lastSlash === i - 1 || dots === 1) { + } else if (lastSlash !== i - 1 && dots === 2) { + if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) { + if (res.length > 2) { + const lastSlashIndex = res.lastIndexOf("/"); + if (lastSlashIndex !== res.length - 1) { + if (lastSlashIndex === -1) { + res = ""; + lastSegmentLength = 0; + } else { + res = res.slice(0, lastSlashIndex); + lastSegmentLength = res.length - 1 - res.lastIndexOf("/"); + } + lastSlash = i; + dots = 0; + continue; + } + } else if (res.length === 2 || res.length === 1) { + res = ""; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + if (res.length > 0) { + res += "/.."; + } else { + res = ".."; + } + lastSegmentLength = 2; + } + } else { + if (res.length > 0) { + res += `/${path2.slice(lastSlash + 1, i)}`; + } else { + res = path2.slice(lastSlash + 1, i); + } + lastSegmentLength = i - lastSlash - 1; + } + lastSlash = i; + dots = 0; + } else if (code === 46 && dots !== -1) { + ++dots; + } else { + dots = -1; + } + } + return res; + } + const path = { + /** + * Converts a path to posix format. + * @param path - The path to convert to posix + * @example + * ```ts + * // Convert a Windows path to POSIX format + * path.toPosix('C:\\Users\\User\\Documents\\file.txt'); + * // -> 'C:/Users/User/Documents/file.txt' + * ``` + */ + toPosix(path2) { + return replaceAll(path2, "\\", "/"); + }, + /** + * Checks if the path is a URL e.g. http://, https:// + * @param path - The path to check + * @example + * ```ts + * // Check if a path is a URL + * path.isUrl('http://www.example.com'); + * // -> true + * path.isUrl('C:/Users/User/Documents/file.txt'); + * // -> false + * ``` + */ + isUrl(path2) { + return /^https?:/.test(this.toPosix(path2)); + }, + /** + * Checks if the path is a data URL + * @param path - The path to check + * @example + * ```ts + * // Check if a path is a data URL + * path.isDataUrl('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...'); + * // -> true + * ``` + */ + isDataUrl(path2) { + return /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z0-9-.!#$%*+.{}|~`]+=[a-z0-9-.!#$%*+.{}()_|~`]+)*)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s<>]*?)$/i.test(path2); + }, + /** + * Checks if the path is a blob URL + * @param path - The path to check + * @example + * ```ts + * // Check if a path is a blob URL + * path.isBlobUrl('blob:http://www.example.com/12345678-1234-1234-1234-123456789012'); + * // -> true + * ``` + */ + isBlobUrl(path2) { + return path2.startsWith("blob:"); + }, + /** + * Checks if the path has a protocol e.g. http://, https://, file:///, data:, blob:, C:/ + * This will return true for windows file paths + * @param path - The path to check + * @example + * ```ts + * // Check if a path has a protocol + * path.hasProtocol('http://www.example.com'); + * // -> true + * path.hasProtocol('C:/Users/User/Documents/file.txt'); + * // -> true + * ``` + */ + hasProtocol(path2) { + return /^[^/:]+:/.test(this.toPosix(path2)); + }, + /** + * Returns the protocol of the path e.g. http://, https://, file:///, data:, blob:, C:/ + * @param path - The path to get the protocol from + * @example + * ```ts + * // Get the protocol from a URL + * path.getProtocol('http://www.example.com/path/to/resource'); + * // -> 'http://' + * // Get the protocol from a file path + * path.getProtocol('C:/Users/User/Documents/file.txt'); + * // -> 'C:/' + * ``` + */ + getProtocol(path2) { + assertPath(path2); + path2 = this.toPosix(path2); + const matchFile = /^file:\/\/\//.exec(path2); + if (matchFile) { + return matchFile[0]; + } + const matchProtocol = /^[^/:]+:\/{0,2}/.exec(path2); + if (matchProtocol) { + return matchProtocol[0]; + } + return ""; + }, + /** + * Converts URL to an absolute path. + * When loading from a Web Worker, we must use absolute paths. + * If the URL is already absolute we return it as is + * If it's not, we convert it + * @param url - The URL to test + * @param customBaseUrl - The base URL to use + * @param customRootUrl - The root URL to use + * @example + * ```ts + * // Convert a relative URL to an absolute path + * path.toAbsolute('images/texture.png', 'http://example.com/assets/'); + * // -> 'http://example.com/assets/images/texture.png' + * ``` + */ + toAbsolute(url, customBaseUrl, customRootUrl) { + assertPath(url); + if (this.isDataUrl(url) || this.isBlobUrl(url)) return url; + const baseUrl = removeUrlParams(this.toPosix(customBaseUrl != null ? customBaseUrl : DOMAdapter.get().getBaseUrl())); + const rootUrl = removeUrlParams(this.toPosix(customRootUrl != null ? customRootUrl : this.rootname(baseUrl))); + url = this.toPosix(url); + if (url.startsWith("/")) { + return path.join(rootUrl, url.slice(1)); + } + const absolutePath = this.isAbsolute(url) ? url : this.join(baseUrl, url); + return absolutePath; + }, + /** + * Normalizes the given path, resolving '..' and '.' segments + * @param path - The path to normalize + * @example + * ```ts + * // Normalize a path with relative segments + * path.normalize('http://www.example.com/foo/bar/../baz'); + * // -> 'http://www.example.com/foo/baz' + * // Normalize a file path with relative segments + * path.normalize('C:\\Users\\User\\Documents\\..\\file.txt'); + * // -> 'C:/Users/User/file.txt' + * ``` + */ + normalize(path2) { + assertPath(path2); + if (path2.length === 0) return "."; + if (this.isDataUrl(path2) || this.isBlobUrl(path2)) return path2; + path2 = this.toPosix(path2); + let protocol = ""; + const isAbsolute = path2.startsWith("/"); + if (this.hasProtocol(path2)) { + protocol = this.rootname(path2); + path2 = path2.slice(protocol.length); + } + const trailingSeparator = path2.endsWith("/"); + path2 = normalizeStringPosix(path2, false); + if (path2.length > 0 && trailingSeparator) path2 += "/"; + if (isAbsolute) return `/${path2}`; + return protocol + path2; + }, + /** + * Determines if path is an absolute path. + * Absolute paths can be urls, data urls, or paths on disk + * @param path - The path to test + * @example + * ```ts + * // Check if a path is absolute + * path.isAbsolute('http://www.example.com/foo/bar'); + * // -> true + * path.isAbsolute('C:/Users/User/Documents/file.txt'); + * // -> true + * ``` + */ + isAbsolute(path2) { + assertPath(path2); + path2 = this.toPosix(path2); + if (this.hasProtocol(path2)) return true; + return path2.startsWith("/"); + }, + /** + * Joins all given path segments together using the platform-specific separator as a delimiter, + * then normalizes the resulting path + * @param segments - The segments of the path to join + * @example + * ```ts + * // Join multiple path segments + * path.join('assets', 'images', 'sprite.png'); + * // -> 'assets/images/sprite.png' + * // Join with relative segments + * path.join('assets', 'images', '../textures', 'sprite.png'); + * // -> 'assets/textures/sprite.png' + * ``` + */ + join(...segments) { + var _a; + if (segments.length === 0) { + return "."; + } + let joined; + for (let i = 0; i < segments.length; ++i) { + const arg = segments[i]; + assertPath(arg); + if (arg.length > 0) { + if (joined === void 0) joined = arg; + else { + const prevArg = (_a = segments[i - 1]) != null ? _a : ""; + if (this.joinExtensions.includes(this.extname(prevArg).toLowerCase())) { + joined += `/../${arg}`; + } else { + joined += `/${arg}`; + } + } + } + } + if (joined === void 0) { + return "."; + } + return this.normalize(joined); + }, + /** + * Returns the directory name of a path + * @param path - The path to parse + * @example + * ```ts + * // Get the directory name of a path + * path.dirname('http://www.example.com/foo/bar/baz.png'); + * // -> 'http://www.example.com/foo/bar' + * // Get the directory name of a file path + * path.dirname('C:/Users/User/Documents/file.txt'); + * // -> 'C:/Users/User/Documents' + * ``` + */ + dirname(path2) { + assertPath(path2); + if (path2.length === 0) return "."; + path2 = this.toPosix(path2); + let code = path2.charCodeAt(0); + const hasRoot = code === 47; + let end = -1; + let matchedSlash = true; + const proto = this.getProtocol(path2); + const origpath = path2; + path2 = path2.slice(proto.length); + for (let i = path2.length - 1; i >= 1; --i) { + code = path2.charCodeAt(i); + if (code === 47) { + if (!matchedSlash) { + end = i; + break; + } + } else { + matchedSlash = false; + } + } + if (end === -1) return hasRoot ? "/" : this.isUrl(origpath) ? proto + path2 : proto; + if (hasRoot && end === 1) return "//"; + return proto + path2.slice(0, end); + }, + /** + * Returns the root of the path e.g. /, C:/, file:///, http://domain.com/ + * @param path - The path to parse + * @example + * ```ts + * // Get the root of a URL + * path.rootname('http://www.example.com/foo/bar/baz.png'); + * // -> 'http://www.example.com/' + * // Get the root of a file path + * path.rootname('C:/Users/User/Documents/file.txt'); + * // -> 'C:/' + * ``` + */ + rootname(path2) { + assertPath(path2); + path2 = this.toPosix(path2); + let root = ""; + if (path2.startsWith("/")) root = "/"; + else { + root = this.getProtocol(path2); + } + if (this.isUrl(path2)) { + const index = path2.indexOf("/", root.length); + if (index !== -1) { + root = path2.slice(0, index); + } else root = path2; + if (!root.endsWith("/")) root += "/"; + } + return root; + }, + /** + * Returns the last portion of a path + * @param path - The path to test + * @param ext - Optional extension to remove + * @example + * ```ts + * // Get the basename of a URL + * path.basename('http://www.example.com/foo/bar/baz.png'); + * // -> 'baz.png' + * // Get the basename of a file path + * path.basename('C:/Users/User/Documents/file.txt'); + * // -> 'file.txt' + * ``` + */ + basename(path2, ext) { + assertPath(path2); + if (ext) assertPath(ext); + path2 = removeUrlParams(this.toPosix(path2)); + let start = 0; + let end = -1; + let matchedSlash = true; + let i; + if (ext !== void 0 && ext.length > 0 && ext.length <= path2.length) { + if (ext.length === path2.length && ext === path2) return ""; + let extIdx = ext.length - 1; + let firstNonSlashEnd = -1; + for (i = path2.length - 1; i >= 0; --i) { + const code = path2.charCodeAt(i); + if (code === 47) { + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + end = i; + } + } else { + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + if (start === end) end = firstNonSlashEnd; + else if (end === -1) end = path2.length; + return path2.slice(start, end); + } + for (i = path2.length - 1; i >= 0; --i) { + if (path2.charCodeAt(i) === 47) { + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + matchedSlash = false; + end = i + 1; + } + } + if (end === -1) return ""; + return path2.slice(start, end); + }, + /** + * Returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last + * portion of the path. If there is no . in the last portion of the path, or if there are no . characters other than + * the first character of the basename of path, an empty string is returned. + * @param path - The path to parse + * @example + * ```ts + * // Get the extension of a URL + * path.extname('http://www.example.com/foo/bar/baz.png'); + * // -> '.png' + * // Get the extension of a file path + * path.extname('C:/Users/User/Documents/file.txt'); + * // -> '.txt' + * ``` + */ + extname(path2) { + assertPath(path2); + path2 = removeUrlParams(this.toPosix(path2)); + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + let preDotState = 0; + for (let i = path2.length - 1; i >= 0; --i) { + const code = path2.charCodeAt(i); + if (code === 47) { + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + matchedSlash = false; + end = i + 1; + } + if (code === 46) { + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + preDotState = -1; + } + } + if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { + return ""; + } + return path2.slice(startDot, end); + }, + /** + * Parses a path into an object containing the 'root', `dir`, `base`, `ext`, and `name` properties. + * @param path - The path to parse + * @example + * ```ts + * // Parse a URL + * const parsed = path.parse('http://www.example.com/foo/bar/baz.png'); + * // -> { + * // root: 'http://www.example.com/', + * // dir: 'http://www.example.com/foo/bar', + * // base: 'baz.png', + * // ext: '.png', + * // name: 'baz' + * // } + * // Parse a file path + * const parsedFile = path.parse('C:/Users/User/Documents/file.txt'); + * // -> { + * // root: 'C:/', + * // dir: 'C:/Users/User/Documents', + * // base: 'file.txt', + * // ext: '.txt', + * // name: 'file' + * // } + * ``` + */ + parse(path2) { + assertPath(path2); + const ret = { root: "", dir: "", base: "", ext: "", name: "" }; + if (path2.length === 0) return ret; + path2 = removeUrlParams(this.toPosix(path2)); + let code = path2.charCodeAt(0); + const isAbsolute = this.isAbsolute(path2); + let start; + const protocol = ""; + ret.root = this.rootname(path2); + if (isAbsolute || this.hasProtocol(path2)) { + start = 1; + } else { + start = 0; + } + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + let i = path2.length - 1; + let preDotState = 0; + for (; i >= start; --i) { + code = path2.charCodeAt(i); + if (code === 47) { + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + matchedSlash = false; + end = i + 1; + } + if (code === 46) { + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + preDotState = -1; + } + } + if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { + if (end !== -1) { + if (startPart === 0 && isAbsolute) ret.base = ret.name = path2.slice(1, end); + else ret.base = ret.name = path2.slice(startPart, end); + } + } else { + if (startPart === 0 && isAbsolute) { + ret.name = path2.slice(1, startDot); + ret.base = path2.slice(1, end); + } else { + ret.name = path2.slice(startPart, startDot); + ret.base = path2.slice(startPart, end); + } + ret.ext = path2.slice(startDot, end); + } + ret.dir = this.dirname(path2); + if (protocol) ret.dir = protocol + ret.dir; + return ret; + }, + sep: "/", + delimiter: ":", + joinExtensions: [".html"] + }; + + "use strict"; + const convertToList = (input, transform, forceTransform = false) => { + if (!Array.isArray(input)) { + input = [input]; + } + if (!transform) { + return input; + } + return input.map((item) => { + if (typeof item === "string" || forceTransform) { + return transform(item); + } + return item; + }); + }; + + "use strict"; + function processX(base, ids, depth, result, tags) { + const id = ids[depth]; + for (let i = 0; i < id.length; i++) { + const value = id[i]; + if (depth < ids.length - 1) { + processX(base.replace(result[depth], value), ids, depth + 1, result, tags); + } else { + tags.push(base.replace(result[depth], value)); + } + } + } + function createStringVariations(string) { + const regex = /\{(.*?)\}/g; + const result = string.match(regex); + const tags = []; + if (result) { + const ids = []; + result.forEach((vars) => { + const split = vars.substring(1, vars.length - 1).split(","); + ids.push(split); + }); + processX(string, ids, 0, result, tags); + } else { + tags.push(string); + } + return tags; + } + + "use strict"; + const isSingleItem = (item) => !Array.isArray(item); + + "use strict"; + var __defProp$1d = Object.defineProperty; + var __getOwnPropSymbols$1f = Object.getOwnPropertySymbols; + var __hasOwnProp$1f = Object.prototype.hasOwnProperty; + var __propIsEnum$1f = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1d = (obj, key, value) => key in obj ? __defProp$1d(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1d = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1f.call(b, prop)) + __defNormalProp$1d(a, prop, b[prop]); + if (__getOwnPropSymbols$1f) + for (var prop of __getOwnPropSymbols$1f(b)) { + if (__propIsEnum$1f.call(b, prop)) + __defNormalProp$1d(a, prop, b[prop]); + } + return a; + }; + class Resolver { + constructor() { + this._defaultBundleIdentifierOptions = { + connector: "-", + createBundleAssetId: (bundleId, assetId) => `${bundleId}${this._bundleIdConnector}${assetId}`, + extractAssetIdFromBundle: (bundleId, assetBundleId) => assetBundleId.replace(`${bundleId}${this._bundleIdConnector}`, "") + }; + /** The character that is used to connect the bundleId and the assetId when generating a bundle asset id key */ + this._bundleIdConnector = this._defaultBundleIdentifierOptions.connector; + /** + * A function that generates a bundle asset id key from a bundleId and an assetId + * @param bundleId - the bundleId + * @param assetId - the assetId + * @returns the bundle asset id key + */ + this._createBundleAssetId = this._defaultBundleIdentifierOptions.createBundleAssetId; + /** + * A function that generates an assetId from a bundle asset id key. This is the reverse of generateBundleAssetId + * @param bundleId - the bundleId + * @param assetBundleId - the bundle asset id key + * @returns the assetId + */ + this._extractAssetIdFromBundle = this._defaultBundleIdentifierOptions.extractAssetIdFromBundle; + this._assetMap = {}; + this._preferredOrder = []; + this._parsers = []; + this._resolverHash = {}; + this._bundles = {}; + } + /** + * Override how the resolver deals with generating bundle ids. + * must be called before any bundles are added + * @param bundleIdentifier - the bundle identifier options + */ + setBundleIdentifier(bundleIdentifier) { + var _a, _b, _c; + this._bundleIdConnector = (_a = bundleIdentifier.connector) != null ? _a : this._bundleIdConnector; + this._createBundleAssetId = (_b = bundleIdentifier.createBundleAssetId) != null ? _b : this._createBundleAssetId; + this._extractAssetIdFromBundle = (_c = bundleIdentifier.extractAssetIdFromBundle) != null ? _c : this._extractAssetIdFromBundle; + if (this._extractAssetIdFromBundle("foo", this._createBundleAssetId("foo", "bar")) !== "bar") { + throw new Error("[Resolver] GenerateBundleAssetId are not working correctly"); + } + } + /** + * Let the resolver know which assets you prefer to use when resolving assets. + * Multiple prefer user defined rules can be added. + * @example + * resolver.prefer({ + * // first look for something with the correct format, and then then correct resolution + * priority: ['format', 'resolution'], + * params:{ + * format:'webp', // prefer webp images + * resolution: 2, // prefer a resolution of 2 + * } + * }) + * resolver.add('foo', ['bar@2x.webp', 'bar@2x.png', 'bar.webp', 'bar.png']); + * resolver.resolveUrl('foo') // => 'bar@2x.webp' + * @param preferOrders - the prefer options + */ + prefer(...preferOrders) { + preferOrders.forEach((prefer) => { + this._preferredOrder.push(prefer); + if (!prefer.priority) { + prefer.priority = Object.keys(prefer.params); + } + }); + this._resolverHash = {}; + } + /** + * Set the base path to prepend to all urls when resolving + * @example + * resolver.basePath = 'https://home.com/'; + * resolver.add('foo', 'bar.ong'); + * resolver.resolveUrl('foo', 'bar.png'); // => 'https://home.com/bar.png' + * @param basePath - the base path to use + */ + set basePath(basePath) { + this._basePath = basePath; + } + get basePath() { + return this._basePath; + } + /** + * Set the root path for root-relative URLs. By default the `basePath`'s root is used. If no `basePath` is set, then the + * default value for browsers is `window.location.origin` + * @example + * // Application hosted on https://home.com/some-path/index.html + * resolver.basePath = 'https://home.com/some-path/'; + * resolver.rootPath = 'https://home.com/'; + * resolver.add('foo', '/bar.png'); + * resolver.resolveUrl('foo', '/bar.png'); // => 'https://home.com/bar.png' + * @param rootPath - the root path to use + */ + set rootPath(rootPath) { + this._rootPath = rootPath; + } + get rootPath() { + return this._rootPath; + } + /** + * All the active URL parsers that help the parser to extract information and create + * an asset object-based on parsing the URL itself. + * + * Can be added using the extensions API + * @example + * resolver.add('foo', [ + * { + * resolution: 2, + * format: 'png', + * src: 'image@2x.png', + * }, + * { + * resolution:1, + * format:'png', + * src: 'image.png', + * }, + * ]); + * + * // With a url parser the information such as resolution and file format could extracted from the url itself: + * extensions.add({ + * extension: ExtensionType.ResolveParser, + * test: loadTextures.test, // test if url ends in an image + * parse: (value: string) => + * ({ + * resolution: parseFloat(Resolver.RETINA_PREFIX.exec(value)?.[1] ?? '1'), + * format: value.split('.').pop(), + * src: value, + * }), + * }); + * + * // Now resolution and format can be extracted from the url + * resolver.add('foo', [ + * 'image@2x.png', + * 'image.png', + * ]); + */ + get parsers() { + return this._parsers; + } + /** Used for testing, this resets the resolver to its initial state */ + reset() { + this.setBundleIdentifier(this._defaultBundleIdentifierOptions); + this._assetMap = {}; + this._preferredOrder = []; + this._resolverHash = {}; + this._rootPath = null; + this._basePath = null; + this._manifest = null; + this._bundles = {}; + this._defaultSearchParams = null; + } + /** + * Sets the default URL search parameters for the URL resolver. The urls can be specified as a string or an object. + * @param searchParams - the default url parameters to append when resolving urls + */ + setDefaultSearchParams(searchParams) { + if (typeof searchParams === "string") { + this._defaultSearchParams = searchParams; + } else { + const queryValues = searchParams; + this._defaultSearchParams = Object.keys(queryValues).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryValues[key])}`).join("&"); + } + } + /** + * Returns the aliases for a given asset + * @param asset - the asset to get the aliases for + */ + getAlias(asset) { + const { alias, src } = asset; + const aliasesToUse = convertToList( + alias || src, + (value) => { + if (typeof value === "string") return value; + if (Array.isArray(value)) return value.map((v) => { + var _a; + return (_a = v == null ? void 0 : v.src) != null ? _a : v; + }); + if (value == null ? void 0 : value.src) return value.src; + return value; + }, + true + ); + return aliasesToUse; + } + /** + * Add a manifest to the asset resolver. This is a nice way to add all the asset information in one go. + * generally a manifest would be built using a tool. + * @param manifest - the manifest to add to the resolver + */ + addManifest(manifest) { + if (this._manifest) { + warn("[Resolver] Manifest already exists, this will be overwritten"); + } + this._manifest = manifest; + manifest.bundles.forEach((bundle) => { + this.addBundle(bundle.name, bundle.assets); + }); + } + /** + * This adds a bundle of assets in one go so that you can resolve them as a group. + * For example you could add a bundle for each screen in you pixi app + * @example + * resolver.addBundle('animals', [ + * { alias: 'bunny', src: 'bunny.png' }, + * { alias: 'chicken', src: 'chicken.png' }, + * { alias: 'thumper', src: 'thumper.png' }, + * ]); + * // or + * resolver.addBundle('animals', { + * bunny: 'bunny.png', + * chicken: 'chicken.png', + * thumper: 'thumper.png', + * }); + * + * const resolvedAssets = await resolver.resolveBundle('animals'); + * @param bundleId - The id of the bundle to add + * @param assets - A record of the asset or assets that will be chosen from when loading via the specified key + */ + addBundle(bundleId, assets) { + const assetNames = []; + let convertedAssets = assets; + if (!Array.isArray(assets)) { + convertedAssets = Object.entries(assets).map(([alias, src]) => { + if (typeof src === "string" || Array.isArray(src)) { + return { alias, src }; + } + return __spreadValues$1d({ alias }, src); + }); + } + convertedAssets.forEach((asset) => { + const srcs = asset.src; + const aliases = asset.alias; + let ids; + if (typeof aliases === "string") { + const bundleAssetId = this._createBundleAssetId(bundleId, aliases); + assetNames.push(bundleAssetId); + ids = [aliases, bundleAssetId]; + } else { + const bundleIds = aliases.map((name) => this._createBundleAssetId(bundleId, name)); + assetNames.push(...bundleIds); + ids = [...aliases, ...bundleIds]; + } + this.add(__spreadValues$1d(__spreadValues$1d({}, asset), { + alias: ids, + src: srcs + })); + }); + this._bundles[bundleId] = assetNames; + } + /** + * Tells the resolver what keys are associated with witch asset. + * The most important thing the resolver does + * @example + * // Single key, single asset: + * resolver.add({alias: 'foo', src: 'bar.png'); + * resolver.resolveUrl('foo') // => 'bar.png' + * + * // Multiple keys, single asset: + * resolver.add({alias: ['foo', 'boo'], src: 'bar.png'}); + * resolver.resolveUrl('foo') // => 'bar.png' + * resolver.resolveUrl('boo') // => 'bar.png' + * + * // Multiple keys, multiple assets: + * resolver.add({alias: ['foo', 'boo'], src: ['bar.png', 'bar.webp']}); + * resolver.resolveUrl('foo') // => 'bar.png' + * + * // Add custom data attached to the resolver + * Resolver.add({ + * alias: 'bunnyBooBooSmooth', + * src: 'bunny{png,webp}', + * data: { scaleMode:SCALE_MODES.NEAREST }, // Base texture options + * }); + * + * resolver.resolve('bunnyBooBooSmooth') // => { src: 'bunny.png', data: { scaleMode: SCALE_MODES.NEAREST } } + * @param aliases - the UnresolvedAsset or array of UnresolvedAssets to add to the resolver + */ + add(aliases) { + const assets = []; + if (Array.isArray(aliases)) { + assets.push(...aliases); + } else { + assets.push(aliases); + } + let keyCheck; + keyCheck = (key) => { + if (this.hasKey(key)) { + warn(`[Resolver] already has key: ${key} overwriting`); + } + }; + const assetArray = convertToList(assets); + assetArray.forEach((asset) => { + const { src } = asset; + let { + data, + format, + loadParser: userDefinedLoadParser, + parser: userDefinedParser + } = asset; + const srcsToUse = convertToList(src).map((src2) => { + if (typeof src2 === "string") { + return createStringVariations(src2); + } + return Array.isArray(src2) ? src2 : [src2]; + }); + const aliasesToUse = this.getAlias(asset); + Array.isArray(aliasesToUse) ? aliasesToUse.forEach(keyCheck) : keyCheck(aliasesToUse); + const resolvedAssets = []; + const parseUrl = (url) => { + const parser = this._parsers.find((p) => p.test(url)); + return __spreadValues$1d({ + src: url + }, parser == null ? void 0 : parser.parse(url)); + }; + srcsToUse.forEach((srcs) => { + srcs.forEach((src2) => { + var _a, _b, _c, _d; + let formattedAsset = {}; + if (typeof src2 !== "object") { + formattedAsset = parseUrl(src2); + } else { + data = (_a = src2.data) != null ? _a : data; + format = (_b = src2.format) != null ? _b : format; + if (src2.loadParser || src2.parser) { + userDefinedLoadParser = (_c = src2.loadParser) != null ? _c : userDefinedLoadParser; + userDefinedParser = (_d = src2.parser) != null ? _d : userDefinedParser; + } + formattedAsset = __spreadValues$1d(__spreadValues$1d({}, parseUrl(src2.src)), src2); + } + if (!aliasesToUse) { + throw new Error(`[Resolver] alias is undefined for this asset: ${formattedAsset.src}`); + } + formattedAsset = this._buildResolvedAsset(formattedAsset, { + aliases: aliasesToUse, + data, + format, + loadParser: userDefinedLoadParser, + parser: userDefinedParser, + progressSize: asset.progressSize + }); + resolvedAssets.push(formattedAsset); + }); + }); + aliasesToUse.forEach((alias) => { + this._assetMap[alias] = resolvedAssets; + }); + }); + } + // TODO: this needs an overload like load did in Assets + /** + * If the resolver has had a manifest set via setManifest, this will return the assets urls for + * a given bundleId or bundleIds. + * @example + * // Manifest Example + * const manifest = { + * bundles: [ + * { + * name: 'load-screen', + * assets: [ + * { + * alias: 'background', + * src: 'sunset.png', + * }, + * { + * alias: 'bar', + * src: 'load-bar.{png,webp}', + * }, + * ], + * }, + * { + * name: 'game-screen', + * assets: [ + * { + * alias: 'character', + * src: 'robot.png', + * }, + * { + * alias: 'enemy', + * src: 'bad-guy.png', + * }, + * ], + * }, + * ] + * }; + * + * resolver.setManifest(manifest); + * const resolved = resolver.resolveBundle('load-screen'); + * @param bundleIds - The bundle ids to resolve + * @returns All the bundles assets or a hash of assets for each bundle specified + */ + resolveBundle(bundleIds) { + const singleAsset = isSingleItem(bundleIds); + bundleIds = convertToList(bundleIds); + const out = {}; + bundleIds.forEach((bundleId) => { + const assetNames = this._bundles[bundleId]; + if (assetNames) { + const results = this.resolve(assetNames); + const assets = {}; + for (const key in results) { + const asset = results[key]; + assets[this._extractAssetIdFromBundle(bundleId, key)] = asset; + } + out[bundleId] = assets; + } + }); + return singleAsset ? out[bundleIds[0]] : out; + } + /** + * Does exactly what resolve does, but returns just the URL rather than the whole asset object + * @param key - The key or keys to resolve + * @returns - The URLs associated with the key(s) + */ + resolveUrl(key) { + const result = this.resolve(key); + if (typeof key !== "string") { + const out = {}; + for (const i in result) { + out[i] = result[i].src; + } + return out; + } + return result.src; + } + resolve(keys) { + const singleAsset = isSingleItem(keys); + keys = convertToList(keys); + const result = {}; + keys.forEach((key) => { + if (!this._resolverHash[key]) { + if (this._assetMap[key]) { + let assets = this._assetMap[key]; + const preferredOrder = this._getPreferredOrder(assets); + preferredOrder == null ? void 0 : preferredOrder.priority.forEach((priorityKey) => { + preferredOrder.params[priorityKey].forEach((value) => { + const filteredAssets = assets.filter((asset) => { + if (asset[priorityKey]) { + return asset[priorityKey] === value; + } + return false; + }); + if (filteredAssets.length) { + assets = filteredAssets; + } + }); + }); + this._resolverHash[key] = assets[0]; + } else { + this._resolverHash[key] = this._buildResolvedAsset({ + alias: [key], + src: key + }, {}); + } + } + result[key] = this._resolverHash[key]; + }); + return singleAsset ? result[keys[0]] : result; + } + /** + * Checks if an asset with a given key exists in the resolver + * @param key - The key of the asset + */ + hasKey(key) { + return !!this._assetMap[key]; + } + /** + * Checks if a bundle with the given key exists in the resolver + * @param key - The key of the bundle + */ + hasBundle(key) { + return !!this._bundles[key]; + } + /** + * Internal function for figuring out what prefer criteria an asset should use. + * @param assets + */ + _getPreferredOrder(assets) { + for (let i = 0; i < assets.length; i++) { + const asset = assets[i]; + const preferred = this._preferredOrder.find((preference) => preference.params.format.includes(asset.format)); + if (preferred) { + return preferred; + } + } + return this._preferredOrder[0]; + } + /** + * Appends the default url parameters to the url + * @param url - The url to append the default parameters to + * @returns - The url with the default parameters appended + */ + _appendDefaultSearchParams(url) { + if (!this._defaultSearchParams) return url; + const paramConnector = /\?/.test(url) ? "&" : "?"; + return `${url}${paramConnector}${this._defaultSearchParams}`; + } + _buildResolvedAsset(formattedAsset, data) { + var _a, _b; + const { aliases, data: assetData, loadParser, parser, format, progressSize } = data; + if (this._basePath || this._rootPath) { + formattedAsset.src = path.toAbsolute(formattedAsset.src, this._basePath, this._rootPath); + } + formattedAsset.alias = (_a = aliases != null ? aliases : formattedAsset.alias) != null ? _a : [formattedAsset.src]; + formattedAsset.src = this._appendDefaultSearchParams(formattedAsset.src); + formattedAsset.data = __spreadValues$1d(__spreadValues$1d({}, assetData || {}), formattedAsset.data); + formattedAsset.loadParser = loadParser != null ? loadParser : formattedAsset.loadParser; + formattedAsset.parser = parser != null ? parser : formattedAsset.parser; + formattedAsset.format = (_b = format != null ? format : formattedAsset.format) != null ? _b : getUrlExtension(formattedAsset.src); + if (progressSize !== void 0) { + formattedAsset.progressSize = progressSize; + } + return formattedAsset; + } + } + /** + * The prefix that denotes a URL is for a retina asset. + * @default /@([0-9\.]+)x/ + * @example `@2x` + */ + Resolver.RETINA_PREFIX = /@([0-9\.]+)x/; + function getUrlExtension(url) { + return url.split(".").pop().split("?").shift().split("#").shift(); + } + + "use strict"; + const copySearchParams = (targetUrl, sourceUrl) => { + const searchParams = sourceUrl.split("?")[1]; + if (searchParams) { + targetUrl += `?${searchParams}`; + } + return targetUrl; + }; + + "use strict"; + const _Spritesheet = class _Spritesheet { + constructor(optionsOrTexture, arg1) { + /** For multi-packed spritesheets, this contains a reference to all the other spritesheets it depends on. */ + this.linkedSheets = []; + let options = optionsOrTexture; + if ((optionsOrTexture == null ? void 0 : optionsOrTexture.source) instanceof TextureSource) { + options = { + texture: optionsOrTexture, + data: arg1 + }; + } + const { texture, data, cachePrefix = "" } = options; + this.cachePrefix = cachePrefix; + this._texture = texture instanceof Texture ? texture : null; + this.textureSource = texture.source; + this.textures = {}; + this.animations = {}; + this.data = data; + const metaResolution = parseFloat(data.meta.scale); + if (metaResolution) { + this.resolution = metaResolution; + texture.source.resolution = this.resolution; + } else { + this.resolution = texture.source._resolution; + } + this._frames = this.data.frames; + this._frameKeys = Object.keys(this._frames); + this._batchIndex = 0; + this._callback = null; + } + /** + * Parse spritesheet from loaded data. This is done asynchronously + * to prevent creating too many Texture within a single process. + */ + parse() { + return new Promise((resolve) => { + this._callback = resolve; + this._batchIndex = 0; + if (this._frameKeys.length <= _Spritesheet.BATCH_SIZE) { + this._processFrames(0); + this._processAnimations(); + this._parseComplete(); + } else { + this._nextBatch(); + } + }); + } + /** + * Parse spritesheet from loaded data. This is done synchronously + * and is only suitable for smaller spritesheets (less than ~1000 frames) + * or may cause too many Texture within a single process. However, synchronous parsing may be + * more convenient since the called does not need to be asynchronous and is safe for + * small-to-medium sized spritesheets. + * + * Other than being synchronous, `parseSync` is otherwise identical to `.parse()`. + */ + parseSync() { + this._processFrames(0, true); + this._processAnimations(); + return this.textures; + } + /** + * Process a batch of frames + * @param initialFrameIndex - The index of frame to start. + * @param processAll - if true will process all frames in a single batch, ignoring BATCH_SIZE - this + * is used for synchronous parsing. + */ + _processFrames(initialFrameIndex, processAll = false) { + let frameIndex = initialFrameIndex; + const maxFrames = processAll ? Infinity : _Spritesheet.BATCH_SIZE; + while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) { + const i = this._frameKeys[frameIndex]; + const data = this._frames[i]; + const rect = data.frame; + if (rect) { + let frame = null; + let trim = null; + const sourceSize = data.trimmed !== false && data.sourceSize ? data.sourceSize : data.frame; + const orig = new Rectangle( + 0, + 0, + Math.floor(sourceSize.w) / this.resolution, + Math.floor(sourceSize.h) / this.resolution + ); + if (data.rotated) { + frame = new Rectangle( + Math.floor(rect.x) / this.resolution, + Math.floor(rect.y) / this.resolution, + Math.floor(rect.h) / this.resolution, + Math.floor(rect.w) / this.resolution + ); + } else { + frame = new Rectangle( + Math.floor(rect.x) / this.resolution, + Math.floor(rect.y) / this.resolution, + Math.floor(rect.w) / this.resolution, + Math.floor(rect.h) / this.resolution + ); + } + if (data.trimmed !== false && data.spriteSourceSize) { + trim = new Rectangle( + Math.floor(data.spriteSourceSize.x) / this.resolution, + Math.floor(data.spriteSourceSize.y) / this.resolution, + Math.floor(rect.w) / this.resolution, + Math.floor(rect.h) / this.resolution + ); + } + this.textures[i] = new Texture({ + source: this.textureSource, + frame, + orig, + trim, + rotate: data.rotated ? 2 : 0, + defaultAnchor: data.anchor, + defaultBorders: data.borders, + label: i.toString() + }); + } + frameIndex++; + } + } + /** Parse animations config. */ + _processAnimations() { + const animations = this.data.animations || {}; + for (const animName in animations) { + this.animations[animName] = []; + for (let i = 0; i < animations[animName].length; i++) { + const frameName = animations[animName][i]; + this.animations[animName].push(this.textures[frameName]); + } + } + } + /** The parse has completed. */ + _parseComplete() { + const callback = this._callback; + this._callback = null; + this._batchIndex = 0; + callback.call(this, this.textures); + } + /** Begin the next batch of textures. */ + _nextBatch() { + this._processFrames(this._batchIndex * _Spritesheet.BATCH_SIZE); + this._batchIndex++; + setTimeout(() => { + if (this._batchIndex * _Spritesheet.BATCH_SIZE < this._frameKeys.length) { + this._nextBatch(); + } else { + this._processAnimations(); + this._parseComplete(); + } + }, 0); + } + /** + * Destroy Spritesheet and don't use after this. + * @param {boolean} [destroyBase=false] - Whether to destroy the base texture as well + */ + destroy(destroyBase = false) { + var _a; + for (const i in this.textures) { + this.textures[i].destroy(); + } + this._frames = null; + this._frameKeys = null; + this.data = null; + this.textures = null; + if (destroyBase) { + (_a = this._texture) == null ? void 0 : _a.destroy(); + this.textureSource.destroy(); + } + this._texture = null; + this.textureSource = null; + this.linkedSheets = []; + } + }; + /** + * The maximum number of Textures to build per process. + * @advanced + */ + _Spritesheet.BATCH_SIZE = 1e3; + let Spritesheet = _Spritesheet; + + "use strict"; + const validImages = [ + "jpg", + "png", + "jpeg", + "avif", + "webp", + "basis", + "etc2", + "bc7", + "bc6h", + "bc5", + "bc4", + "bc3", + "bc2", + "bc1", + "eac", + "astc" + ]; + function getCacheableAssets(keys, asset, ignoreMultiPack) { + const out = {}; + keys.forEach((key) => { + out[key] = asset; + }); + Object.keys(asset.textures).forEach((key) => { + out[`${asset.cachePrefix}${key}`] = asset.textures[key]; + }); + if (!ignoreMultiPack) { + const basePath = path.dirname(keys[0]); + asset.linkedSheets.forEach((item, i) => { + const out2 = getCacheableAssets([`${basePath}/${asset.data.meta.related_multi_packs[i]}`], item, true); + Object.assign(out, out2); + }); + } + return out; + } + const spritesheetAsset = { + extension: ExtensionType.Asset, + /** Handle the caching of the related Spritesheet Textures */ + cache: { + test: (asset) => asset instanceof Spritesheet, + getCacheableAssets: (keys, asset) => getCacheableAssets(keys, asset, false) + }, + /** Resolve the resolution of the asset. */ + resolver: { + extension: { + type: ExtensionType.ResolveParser, + name: "resolveSpritesheet" + }, + test: (value) => { + const tempURL = value.split("?")[0]; + const split = tempURL.split("."); + const extension = split.pop(); + const format = split.pop(); + return extension === "json" && validImages.includes(format); + }, + parse: (value) => { + var _a, _b; + const split = value.split("."); + return { + resolution: parseFloat((_b = (_a = Resolver.RETINA_PREFIX.exec(value)) == null ? void 0 : _a[1]) != null ? _b : "1"), + format: split[split.length - 2], + src: value + }; + } + }, + /** + * Loader plugin that parses sprite sheets! + * once the JSON has been loaded this checks to see if the JSON is spritesheet data. + * If it is, we load the spritesheets image and parse the data into Spritesheet + * All textures in the sprite sheet are then added to the cache + */ + loader: { + /** used for deprecation purposes */ + name: "spritesheetLoader", + id: "spritesheet", + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Normal, + name: "spritesheetLoader" + }, + async testParse(asset, options) { + return path.extname(options.src).toLowerCase() === ".json" && !!asset.frames; + }, + async parse(asset, options, loader) { + var _a, _b, _c; + const { + texture: imageTexture, + // if user need to use preloaded texture + imageFilename, + // if user need to use custom filename (not from jsonFile.meta.image) + textureOptions, + // if user need to set texture options on texture + cachePrefix + // if user need to use custom cache prefix + } = (_a = options == null ? void 0 : options.data) != null ? _a : {}; + let basePath = path.dirname(options.src); + if (basePath && basePath.lastIndexOf("/") !== basePath.length - 1) { + basePath += "/"; + } + let texture; + if (imageTexture instanceof Texture) { + texture = imageTexture; + } else { + const imagePath = copySearchParams(basePath + (imageFilename != null ? imageFilename : asset.meta.image), options.src); + const assets = await loader.load([{ src: imagePath, data: textureOptions }]); + texture = assets[imagePath]; + } + const spritesheet = new Spritesheet({ + texture: texture.source, + data: asset, + cachePrefix + }); + await spritesheet.parse(); + const multiPacks = (_b = asset == null ? void 0 : asset.meta) == null ? void 0 : _b.related_multi_packs; + if (Array.isArray(multiPacks)) { + const promises = []; + for (const item of multiPacks) { + if (typeof item !== "string") { + continue; + } + let itemUrl = basePath + item; + if ((_c = options.data) == null ? void 0 : _c.ignoreMultiPack) { + continue; + } + itemUrl = copySearchParams(itemUrl, options.src); + promises.push(loader.load({ + src: itemUrl, + data: { + textureOptions, + ignoreMultiPack: true + } + })); + } + const res = await Promise.all(promises); + spritesheet.linkedSheets = res; + res.forEach((item) => { + item.linkedSheets = [spritesheet].concat(spritesheet.linkedSheets.filter((sp) => sp !== item)); + }); + } + return spritesheet; + }, + async unload(spritesheet, _resolvedAsset, loader) { + await loader.unload(spritesheet.textureSource._sourceOrigin); + spritesheet.destroy(false); + } + } + }; + + "use strict"; + extensions.add(spritesheetAsset); + + "use strict"; + function updateQuadBounds(bounds, anchor, texture) { + const { width, height } = texture.orig; + const trim = texture.trim; + if (trim) { + const sourceWidth = trim.width; + const sourceHeight = trim.height; + bounds.minX = trim.x - anchor._x * width; + bounds.maxX = bounds.minX + sourceWidth; + bounds.minY = trim.y - anchor._y * height; + bounds.maxY = bounds.minY + sourceHeight; + } else { + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + } + + "use strict"; + class ViewContainer extends Container { + constructor(options) { + var _a; + super(options); + /** @internal */ + this.canBundle = true; + /** @internal */ + this.allowChildren = false; + /** @internal */ + this._roundPixels = 0; + /** @internal */ + this._lastUsed = -1; + /** @internal */ + this._gpuData = /* @__PURE__ */ Object.create(null); + /** If set to true, the resource will be garbage collected automatically when it is not used. */ + this.autoGarbageCollect = true; + /** @internal */ + this._gcLastUsed = -1; + this._bounds = new Bounds(0, 1, 0, 0); + this._boundsDirty = true; + this.autoGarbageCollect = (_a = options.autoGarbageCollect) != null ? _a : true; + } + /** + * The local bounds of the view in its own coordinate space. + * Bounds are automatically updated when the view's content changes. + * @example + * ```ts + * // Get bounds dimensions + * const bounds = view.bounds; + * console.log(`Width: ${bounds.maxX - bounds.minX}`); + * console.log(`Height: ${bounds.maxY - bounds.minY}`); + * ``` + * @returns The rectangular bounds of the view + * @see {@link Bounds} For bounds operations + */ + get bounds() { + if (!this._boundsDirty) return this._bounds; + this.updateBounds(); + this._boundsDirty = false; + return this._bounds; + } + /** + * Whether or not to round the x/y position of the sprite. + * @example + * ```ts + * // Enable pixel rounding for crisp rendering + * view.roundPixels = true; + * ``` + * @default false + */ + get roundPixels() { + return !!this._roundPixels; + } + set roundPixels(value) { + this._roundPixels = value ? 1 : 0; + } + /** + * Checks if the object contains the given point in local coordinates. + * Uses the view's bounds for hit testing. + * @example + * ```ts + * // Basic point check + * const localPoint = { x: 50, y: 25 }; + * const contains = view.containsPoint(localPoint); + * console.log('Point is inside:', contains); + * ``` + * @param point - The point to check in local coordinates + * @returns True if the point is within the view's bounds + * @see {@link ViewContainer#bounds} For the bounds used in hit testing + * @see {@link Container#toLocal} For converting global coordinates to local + */ + containsPoint(point) { + const bounds = this.bounds; + const { x, y } = point; + return x >= bounds.minX && x <= bounds.maxX && y >= bounds.minY && y <= bounds.maxY; + } + /** @private */ + onViewUpdate() { + this._didViewChangeTick++; + this._boundsDirty = true; + if (this.didViewUpdate) return; + this.didViewUpdate = true; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.onChildViewUpdate(this); + } + } + /** Unloads the GPU data from the view. */ + unload() { + var _a; + this.emit("unload", this); + for (const key in this._gpuData) { + (_a = this._gpuData[key]) == null ? void 0 : _a.destroy(); + } + this._gpuData = /* @__PURE__ */ Object.create(null); + this.onViewUpdate(); + } + destroy(options) { + this.unload(); + super.destroy(options); + this._bounds = null; + } + /** + * Collects renderables for the view container. + * @param instructionSet - The instruction set to collect renderables for. + * @param renderer - The renderer to collect renderables for. + * @param currentLayer - The current render layer. + * @internal + */ + collectRenderablesSimple(instructionSet, renderer, currentLayer) { + const { renderPipes } = renderer; + renderPipes.blendMode.pushBlendMode(this, this.groupBlendMode, instructionSet); + const rp = renderPipes; + const pipe = rp[this.renderPipeId]; + if (pipe == null ? void 0 : pipe.addRenderable) { + pipe.addRenderable(this, instructionSet); + } + this.didViewUpdate = false; + const children = this.children; + const length = children.length; + for (let i = 0; i < length; i++) { + children[i].collectRenderables(instructionSet, renderer, currentLayer); + } + renderPipes.blendMode.popBlendMode(instructionSet); + } + } + + "use strict"; + var __defProp$1c = Object.defineProperty; + var __getOwnPropSymbols$1e = Object.getOwnPropertySymbols; + var __hasOwnProp$1e = Object.prototype.hasOwnProperty; + var __propIsEnum$1e = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1c = (obj, key, value) => key in obj ? __defProp$1c(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1c = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1e.call(b, prop)) + __defNormalProp$1c(a, prop, b[prop]); + if (__getOwnPropSymbols$1e) + for (var prop of __getOwnPropSymbols$1e(b)) { + if (__propIsEnum$1e.call(b, prop)) + __defNormalProp$1c(a, prop, b[prop]); + } + return a; + }; + var __objRest$q = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$1e.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$1e) + for (var prop of __getOwnPropSymbols$1e(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$1e.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class Sprite extends ViewContainer { + /** + * @param options - The options for creating the sprite. + */ + constructor(options = Texture.EMPTY) { + if (options instanceof Texture) { + options = { texture: options }; + } + const _a = options, { texture = Texture.EMPTY, anchor, roundPixels, width, height } = _a, rest = __objRest$q(_a, ["texture", "anchor", "roundPixels", "width", "height"]); + super(__spreadValues$1c({ + label: "Sprite" + }, rest)); + /** @internal */ + this.renderPipeId = "sprite"; + /** @internal */ + this.batched = true; + this._visualBounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 }; + this._anchor = new ObservablePoint( + { + _onUpdate: () => { + this.onViewUpdate(); + } + } + ); + if (anchor) { + this.anchor = anchor; + } else if (texture.defaultAnchor) { + this.anchor = texture.defaultAnchor; + } + this.texture = texture; + this.allowChildren = false; + this.roundPixels = roundPixels != null ? roundPixels : false; + if (width !== void 0) this.width = width; + if (height !== void 0) this.height = height; + } + /** + * Creates a new sprite based on a source texture, image, video, or canvas element. + * This is a convenience method that automatically creates and manages textures. + * @example + * ```ts + * // Create from path or URL + * const sprite = Sprite.from('assets/image.png'); + * + * // Create from existing texture + * const sprite = Sprite.from(texture); + * + * // Create from canvas + * const canvas = document.createElement('canvas'); + * const sprite = Sprite.from(canvas, true); // Skip caching new texture + * ``` + * @param source - The source to create the sprite from. Can be a path to an image, a texture, + * or any valid texture source (canvas, video, etc.) + * @param skipCache - Whether to skip adding to the texture cache when creating a new texture + * @returns A new sprite based on the source + * @see {@link Texture.from} For texture creation details + * @see {@link Assets} For asset loading and management + */ + static from(source, skipCache = false) { + if (source instanceof Texture) { + return new Sprite(source); + } + return new Sprite(Texture.from(source, skipCache)); + } + set texture(value) { + value || (value = Texture.EMPTY); + const currentTexture = this._texture; + if (currentTexture === value) return; + if (currentTexture && currentTexture.dynamic) currentTexture.off("update", this.onViewUpdate, this); + if (value.dynamic) value.on("update", this.onViewUpdate, this); + this._texture = value; + if (this._width) { + this._setWidth(this._width, this._texture.orig.width); + } + if (this._height) { + this._setHeight(this._height, this._texture.orig.height); + } + this.onViewUpdate(); + } + /** + * The texture that is displayed by the sprite. When changed, automatically updates + * the sprite dimensions and manages texture event listeners. + * @example + * ```ts + * // Create sprite with texture + * const sprite = new Sprite({ + * texture: Texture.from('sprite.png') + * }); + * + * // Update texture + * sprite.texture = Texture.from('newSprite.png'); + * + * // Use texture from spritesheet + * const sheet = await Assets.load('spritesheet.json'); + * sprite.texture = sheet.textures['frame1.png']; + * + * // Reset to empty texture + * sprite.texture = Texture.EMPTY; + * ``` + * @see {@link Texture} For texture creation and management + * @see {@link Assets} For asset loading + */ + get texture() { + return this._texture; + } + /** + * The bounds of the sprite, taking into account the texture's trim area. + * @example + * ```ts + * const texture = new Texture({ + * source: new TextureSource({ width: 300, height: 300 }), + * frame: new Rectangle(196, 66, 58, 56), + * trim: new Rectangle(4, 4, 58, 56), + * orig: new Rectangle(0, 0, 64, 64), + * rotate: 2, + * }); + * const sprite = new Sprite(texture); + * const visualBounds = sprite.visualBounds; + * // console.log(visualBounds); // { minX: -4, maxX: 62, minY: -4, maxY: 60 } + */ + get visualBounds() { + updateQuadBounds(this._visualBounds, this._anchor, this._texture); + return this._visualBounds; + } + /** + * @deprecated + * @ignore + */ + get sourceBounds() { + deprecation("8.6.1", "Sprite.sourceBounds is deprecated, use visualBounds instead."); + return this.visualBounds; + } + /** @private */ + updateBounds() { + const anchor = this._anchor; + const texture = this._texture; + const bounds = this._bounds; + const { width, height } = texture.orig; + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * sprite.destroy(); + * sprite.destroy(true); + * sprite.destroy({ texture: true, textureSource: true }); + */ + destroy(options = false) { + super.destroy(options); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + this._texture.destroy(destroyTextureSource); + } + this._texture = null; + this._visualBounds = null; + this._bounds = null; + this._anchor = null; + } + /** + * The anchor sets the origin point of the sprite. The default value is taken from the {@link Texture} + * and passed to the constructor. + * + * - The default is `(0,0)`, this means the sprite's origin is the top left. + * - Setting the anchor to `(0.5,0.5)` means the sprite's origin is centered. + * - Setting the anchor to `(1,1)` would mean the sprite's origin point will be the bottom right corner. + * + * If you pass only single parameter, it will set both x and y to the same value as shown in the example below. + * @example + * ```ts + * // Center the anchor point + * sprite.anchor = 0.5; // Sets both x and y to 0.5 + * sprite.position.set(400, 300); // Sprite will be centered at this position + * + * // Set specific x/y anchor points + * sprite.anchor = { + * x: 1, // Right edge + * y: 0 // Top edge + * }; + * + * // Using individual coordinates + * sprite.anchor.set(0.5, 1); // Center-bottom + * + * // For rotation around center + * sprite.anchor.set(0.5); + * sprite.rotation = Math.PI / 4; // 45 degrees around center + * + * // For scaling from center + * sprite.anchor.set(0.5); + * sprite.scale.set(2); // Scales from center point + * ``` + */ + get anchor() { + return this._anchor; + } + set anchor(value) { + typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value); + } + /** + * The width of the sprite, setting this will actually modify the scale to achieve the value set. + * @example + * ```ts + * // Set width directly + * sprite.width = 200; + * console.log(sprite.scale.x); // Scale adjusted to match width + * + * // Set width while preserving aspect ratio + * const ratio = sprite.height / sprite.width; + * sprite.width = 300; + * sprite.height = 300 * ratio; + * + * // For better performance when setting both width and height + * sprite.setSize(300, 400); // Avoids recalculating bounds twice + * + * // Reset to original texture size + * sprite.width = sprite.texture.orig.width; + * ``` + */ + get width() { + return Math.abs(this.scale.x) * this._texture.orig.width; + } + set width(value) { + this._setWidth(value, this._texture.orig.width); + this._width = value; + } + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set. + * @example + * ```ts + * // Set height directly + * sprite.height = 150; + * console.log(sprite.scale.y); // Scale adjusted to match height + * + * // Set height while preserving aspect ratio + * const ratio = sprite.width / sprite.height; + * sprite.height = 200; + * sprite.width = 200 * ratio; + * + * // For better performance when setting both width and height + * sprite.setSize(300, 400); // Avoids recalculating bounds twice + * + * // Reset to original texture size + * sprite.height = sprite.texture.orig.height; + * ``` + */ + get height() { + return Math.abs(this.scale.y) * this._texture.orig.height; + } + set height(value) { + this._setHeight(value, this._texture.orig.height); + this._height = value; + } + /** + * Retrieves the size of the Sprite as a [Size]{@link Size} object based on the texture dimensions and scale. + * This is faster than getting width and height separately as it only calculates the bounds once. + * @example + * ```ts + * // Basic size retrieval + * const sprite = new Sprite(Texture.from('sprite.png')); + * const size = sprite.getSize(); + * console.log(`Size: ${size.width}x${size.height}`); + * + * // Reuse existing size object + * const reuseSize = { width: 0, height: 0 }; + * sprite.getSize(reuseSize); + * ``` + * @param out - Optional object to store the size in, to avoid allocating a new object + * @returns The size of the Sprite + * @see {@link Sprite#width} For getting just the width + * @see {@link Sprite#height} For getting just the height + * @see {@link Sprite#setSize} For setting both width and height + */ + getSize(out) { + out || (out = {}); + out.width = Math.abs(this.scale.x) * this._texture.orig.width; + out.height = Math.abs(this.scale.y) * this._texture.orig.height; + return out; + } + /** + * Sets the size of the Sprite to the specified width and height. + * This is faster than setting width and height separately as it only recalculates bounds once. + * @example + * ```ts + * // Basic size setting + * const sprite = new Sprite(Texture.from('sprite.png')); + * sprite.setSize(100, 200); // Width: 100, Height: 200 + * + * // Set uniform size + * sprite.setSize(100); // Sets both width and height to 100 + * + * // Set size with object + * sprite.setSize({ + * width: 200, + * height: 300 + * }); + * + * // Reset to texture size + * sprite.setSize( + * sprite.texture.orig.width, + * sprite.texture.orig.height + * ); + * ``` + * @param value - This can be either a number or a {@link Size} object + * @param height - The height to set. Defaults to the value of `width` if not provided + * @see {@link Sprite#width} For setting width only + * @see {@link Sprite#height} For setting height only + * @see {@link Sprite#texture} For the source dimensions + */ + setSize(value, height) { + var _a; + if (typeof value === "object") { + height = (_a = value.height) != null ? _a : value.width; + value = value.width; + } else { + height != null ? height : height = value; + } + value !== void 0 && this._setWidth(value, this._texture.orig.width); + height !== void 0 && this._setHeight(height, this._texture.orig.height); + } + } + + "use strict"; + const tempBounds$4 = new Bounds(); + function addMaskBounds(mask, bounds, skipUpdateTransform) { + const boundsToMask = tempBounds$4; + mask.measurable = true; + getGlobalBounds(mask, skipUpdateTransform, boundsToMask); + bounds.addBoundsMask(boundsToMask); + mask.measurable = false; + } + + "use strict"; + function addMaskLocalBounds(mask, bounds, localRoot) { + const boundsToMask = boundsPool.get(); + mask.measurable = true; + const tempMatrix = matrixPool.get().identity(); + const relativeMask = getMatrixRelativeToParent(mask, localRoot, tempMatrix); + getLocalBounds(mask, boundsToMask, relativeMask); + mask.measurable = false; + bounds.addBoundsMask(boundsToMask); + matrixPool.return(tempMatrix); + boundsPool.return(boundsToMask); + } + function getMatrixRelativeToParent(target, root, matrix) { + if (!target) { + warn("Mask bounds, renderable is not inside the root container"); + return matrix; + } + if (target !== root) { + getMatrixRelativeToParent(target.parent, root, matrix); + target.updateLocalTransform(); + matrix.append(target.localTransform); + } + return matrix; + } + + "use strict"; + class AlphaMask { + constructor(options) { + this.priority = 0; + this.inverse = false; + this.pipe = "alphaMask"; + if (options == null ? void 0 : options.mask) { + this.init(options.mask); + } + } + init(mask) { + this.mask = mask; + this.renderMaskToTexture = !(mask instanceof Sprite); + this.mask.renderable = this.renderMaskToTexture; + this.mask.includeInBuild = !this.renderMaskToTexture; + this.mask.measurable = false; + } + reset() { + if (this.mask === null) return; + this.mask.measurable = true; + this.mask = null; + } + addBounds(bounds, skipUpdateTransform) { + if (!this.inverse) { + addMaskBounds(this.mask, bounds, skipUpdateTransform); + } + } + addLocalBounds(bounds, localRoot) { + addMaskLocalBounds(this.mask, bounds, localRoot); + } + containsPoint(point, hitTestFn) { + const mask = this.mask; + return hitTestFn(mask, point); + } + destroy() { + this.reset(); + } + static test(mask) { + return mask instanceof Sprite; + } + } + AlphaMask.extension = ExtensionType.MaskEffect; + + "use strict"; + class ColorMask { + constructor(options) { + this.priority = 0; + this.pipe = "colorMask"; + if (options == null ? void 0 : options.mask) { + this.init(options.mask); + } + } + init(mask) { + this.mask = mask; + } + destroy() { + } + static test(mask) { + return typeof mask === "number"; + } + } + ColorMask.extension = ExtensionType.MaskEffect; + + "use strict"; + class StencilMask { + constructor(options) { + this.priority = 0; + this.pipe = "stencilMask"; + if (options == null ? void 0 : options.mask) { + this.init(options.mask); + } + } + init(mask) { + this.mask = mask; + this.mask.includeInBuild = false; + this.mask.measurable = false; + } + reset() { + if (this.mask === null) return; + this.mask.measurable = true; + this.mask.includeInBuild = true; + this.mask = null; + } + addBounds(bounds, skipUpdateTransform) { + addMaskBounds(this.mask, bounds, skipUpdateTransform); + } + addLocalBounds(bounds, localRoot) { + addMaskLocalBounds(this.mask, bounds, localRoot); + } + containsPoint(point, hitTestFn) { + const mask = this.mask; + return hitTestFn(mask, point); + } + destroy() { + this.reset(); + } + static test(mask) { + return mask instanceof Container; + } + } + StencilMask.extension = ExtensionType.MaskEffect; + + "use strict"; + class CanvasSource extends TextureSource { + constructor(options) { + if (!options.resource) { + options.resource = DOMAdapter.get().createCanvas(); + } + if (!options.width) { + options.width = options.resource.width; + if (!options.autoDensity) { + options.width /= options.resolution; + } + } + if (!options.height) { + options.height = options.resource.height; + if (!options.autoDensity) { + options.height /= options.resolution; + } + } + super(options); + this.uploadMethodId = "image"; + this.autoDensity = options.autoDensity; + this.resizeCanvas(); + this.transparent = !!options.transparent; + } + resizeCanvas() { + if (this.autoDensity && "style" in this.resource) { + this.resource.style.width = `${this.width}px`; + this.resource.style.height = `${this.height}px`; + } + if (this.resource.width !== this.pixelWidth || this.resource.height !== this.pixelHeight) { + this.resource.width = this.pixelWidth; + this.resource.height = this.pixelHeight; + } + } + resize(width = this.width, height = this.height, resolution = this._resolution) { + const didResize = super.resize(width, height, resolution); + if (didResize) { + this.resizeCanvas(); + } + return didResize; + } + static test(resource) { + return globalThis.HTMLCanvasElement && resource instanceof HTMLCanvasElement || globalThis.OffscreenCanvas && resource instanceof OffscreenCanvas; + } + /** + * Returns the 2D rendering context for the canvas. + * Caches the context after creating it. + * @returns The 2D rendering context of the canvas. + */ + get context2D() { + return this._context2D || (this._context2D = this.resource.getContext("2d")); + } + } + CanvasSource.extension = ExtensionType.TextureSource; + + "use strict"; + class ImageSource extends TextureSource { + constructor(options) { + super(options); + this.uploadMethodId = "image"; + this.autoGarbageCollect = true; + } + static test(resource) { + return globalThis.HTMLImageElement && resource instanceof HTMLImageElement || typeof ImageBitmap !== "undefined" && resource instanceof ImageBitmap || globalThis.VideoFrame && resource instanceof VideoFrame; + } + } + ImageSource.extension = ExtensionType.TextureSource; + + "use strict"; + let promise; + async function detectVideoAlphaMode() { + promise != null ? promise : promise = (async () => { + var _a; + const canvas = DOMAdapter.get().createCanvas(1, 1); + const gl = canvas.getContext("webgl"); + if (!gl) { + return "premultiply-alpha-on-upload"; + } + const video = await new Promise((resolve) => { + const video2 = document.createElement("video"); + video2.onloadeddata = () => resolve(video2); + video2.onerror = () => resolve(null); + video2.autoplay = false; + video2.crossOrigin = "anonymous"; + video2.preload = "auto"; + video2.src = "data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEAAAAAAAHTEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggEXTbuMU6uEHFO7a1OsggG97AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmoCrXsYMPQkBNgIRMYXZmV0GETGF2ZkSJiEBEAAAAAAAAFlSua8yuAQAAAAAAAEPXgQFzxYgAAAAAAAAAAZyBACK1nIN1bmSIgQCGhVZfVlA5g4EBI+ODhAJiWgDglLCBArqBApqBAlPAgQFVsIRVuYEBElTDZ9Vzc9JjwItjxYgAAAAAAAAAAWfInEWjh0VOQ09ERVJEh49MYXZjIGxpYnZweC12cDlnyKJFo4hEVVJBVElPTkSHlDAwOjAwOjAwLjA0MDAwMDAwMAAAH0O2dcfngQCgwqGggQAAAIJJg0IAABAAFgA4JBwYSgAAICAAEb///4r+AAB1oZ2mm+6BAaWWgkmDQgAAEAAWADgkHBhKAAAgIABIQBxTu2uRu4+zgQC3iveBAfGCAXHwgQM="; + video2.load(); + }); + if (!video) { + return "premultiply-alpha-on-upload"; + } + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + const framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, + 0 + ); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video); + const pixel = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); + gl.deleteFramebuffer(framebuffer); + gl.deleteTexture(texture); + (_a = gl.getExtension("WEBGL_lose_context")) == null ? void 0 : _a.loseContext(); + return pixel[0] <= pixel[3] ? "premultiplied-alpha" : "premultiply-alpha-on-upload"; + })(); + return promise; + } + + "use strict"; + var __defProp$1b = Object.defineProperty; + var __defProps$v = Object.defineProperties; + var __getOwnPropDescs$v = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$1d = Object.getOwnPropertySymbols; + var __hasOwnProp$1d = Object.prototype.hasOwnProperty; + var __propIsEnum$1d = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1b = (obj, key, value) => key in obj ? __defProp$1b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1b = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1d.call(b, prop)) + __defNormalProp$1b(a, prop, b[prop]); + if (__getOwnPropSymbols$1d) + for (var prop of __getOwnPropSymbols$1d(b)) { + if (__propIsEnum$1d.call(b, prop)) + __defNormalProp$1b(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$v = (a, b) => __defProps$v(a, __getOwnPropDescs$v(b)); + const _VideoSource = class _VideoSource extends TextureSource { + constructor(options) { + var _a; + super(options); + // Public + /** Whether or not the video is ready to play. */ + this.isReady = false; + /** The upload method for this texture. */ + this.uploadMethodId = "video"; + options = __spreadValues$1b(__spreadValues$1b({}, _VideoSource.defaultOptions), options); + this._autoUpdate = true; + this._isConnectedToTicker = false; + this._updateFPS = options.updateFPS || 0; + this._msToNextUpdate = 0; + this.autoPlay = options.autoPlay !== false; + this.alphaMode = (_a = options.alphaMode) != null ? _a : "premultiply-alpha-on-upload"; + this._videoFrameRequestCallback = this._videoFrameRequestCallback.bind(this); + this._videoFrameRequestCallbackHandle = null; + this._load = null; + this._resolve = null; + this._reject = null; + this._onCanPlay = this._onCanPlay.bind(this); + this._onCanPlayThrough = this._onCanPlayThrough.bind(this); + this._onError = this._onError.bind(this); + this._onPlayStart = this._onPlayStart.bind(this); + this._onPlayStop = this._onPlayStop.bind(this); + this._onSeeked = this._onSeeked.bind(this); + if (options.autoLoad !== false) { + void this.load(); + } + } + /** Update the video frame if the source is not destroyed and meets certain conditions. */ + updateFrame() { + if (this.destroyed) { + return; + } + if (this._updateFPS) { + const elapsedMS = Ticker.shared.elapsedMS * this.resource.playbackRate; + this._msToNextUpdate = Math.floor(this._msToNextUpdate - elapsedMS); + } + if (!this._updateFPS || this._msToNextUpdate <= 0) { + this._msToNextUpdate = this._updateFPS ? Math.floor(1e3 / this._updateFPS) : 0; + } + if (this.isValid) { + this.update(); + } + } + /** Callback to update the video frame and potentially request the next frame update. */ + _videoFrameRequestCallback() { + this.updateFrame(); + if (this.destroyed) { + this._videoFrameRequestCallbackHandle = null; + } else { + this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback( + this._videoFrameRequestCallback + ); + } + } + /** + * Checks if the resource has valid dimensions. + * @returns {boolean} True if width and height are set, otherwise false. + */ + get isValid() { + return !!this.resource.videoWidth && !!this.resource.videoHeight; + } + /** + * Start preloading the video resource. + * @returns {Promise} Handle the validate event + */ + async load() { + if (this._load) { + return this._load; + } + const source = this.resource; + const options = this.options; + if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) { + source.complete = true; + } + source.addEventListener("play", this._onPlayStart); + source.addEventListener("pause", this._onPlayStop); + source.addEventListener("seeked", this._onSeeked); + if (!this._isSourceReady()) { + if (!options.preload) { + source.addEventListener("canplay", this._onCanPlay); + } + source.addEventListener("canplaythrough", this._onCanPlayThrough); + source.addEventListener("error", this._onError, true); + } else { + this._mediaReady(); + } + this.alphaMode = await detectVideoAlphaMode(); + this._load = new Promise((resolve, reject) => { + if (this.isValid) { + resolve(this); + } else { + this._resolve = resolve; + this._reject = reject; + if (options.preloadTimeoutMs !== void 0) { + this._preloadTimeout = setTimeout(() => { + this._onError(new ErrorEvent(`Preload exceeded timeout of ${options.preloadTimeoutMs}ms`)); + }); + } + source.load(); + } + }); + return this._load; + } + /** + * Handle video error events. + * @param event - The error event + */ + _onError(event) { + this.resource.removeEventListener("error", this._onError, true); + this.emit("error", event); + if (this._reject) { + this._reject(event); + this._reject = null; + this._resolve = null; + } + } + /** + * Checks if the underlying source is playing. + * @returns True if playing. + */ + _isSourcePlaying() { + const source = this.resource; + return !source.paused && !source.ended; + } + /** + * Checks if the underlying source is ready for playing. + * @returns True if ready. + */ + _isSourceReady() { + const source = this.resource; + return source.readyState > 2; + } + /** Runs the update loop when the video is ready to play. */ + _onPlayStart() { + if (!this.isValid) { + this._mediaReady(); + } + this._configureAutoUpdate(); + } + /** Stops the update loop when a pause event is triggered. */ + _onPlayStop() { + this._configureAutoUpdate(); + } + /** Handles behavior when the video completes seeking to the current playback position. */ + _onSeeked() { + if (this._autoUpdate && !this._isSourcePlaying()) { + this._msToNextUpdate = 0; + this.updateFrame(); + this._msToNextUpdate = 0; + } + } + _onCanPlay() { + const source = this.resource; + source.removeEventListener("canplay", this._onCanPlay); + this._mediaReady(); + } + _onCanPlayThrough() { + const source = this.resource; + source.removeEventListener("canplaythrough", this._onCanPlay); + if (this._preloadTimeout) { + clearTimeout(this._preloadTimeout); + this._preloadTimeout = void 0; + } + this._mediaReady(); + } + /** Fired when the video is loaded and ready to play. */ + _mediaReady() { + const source = this.resource; + if (this.isValid) { + this.isReady = true; + this.resize(source.videoWidth, source.videoHeight); + } + this._msToNextUpdate = 0; + this.updateFrame(); + this._msToNextUpdate = 0; + if (this._resolve) { + this._resolve(this); + this._resolve = null; + this._reject = null; + } + if (this._isSourcePlaying()) { + this._onPlayStart(); + } else if (this.autoPlay) { + void this.resource.play(); + } + } + /** Cleans up resources and event listeners associated with this texture. */ + destroy() { + this._configureAutoUpdate(); + const source = this.resource; + if (source) { + source.removeEventListener("play", this._onPlayStart); + source.removeEventListener("pause", this._onPlayStop); + source.removeEventListener("seeked", this._onSeeked); + source.removeEventListener("canplay", this._onCanPlay); + source.removeEventListener("canplaythrough", this._onCanPlayThrough); + source.removeEventListener("error", this._onError, true); + source.pause(); + source.src = ""; + source.load(); + } + super.destroy(); + } + /** Should the base texture automatically update itself, set to true by default. */ + get autoUpdate() { + return this._autoUpdate; + } + set autoUpdate(value) { + if (value !== this._autoUpdate) { + this._autoUpdate = value; + this._configureAutoUpdate(); + } + } + /** + * How many times a second to update the texture from the video. + * Leave at 0 to update at every render. + * A lower fps can help performance, as updating the texture at 60fps on a 30ps video may not be efficient. + */ + get updateFPS() { + return this._updateFPS; + } + set updateFPS(value) { + if (value !== this._updateFPS) { + this._updateFPS = value; + this._configureAutoUpdate(); + } + } + /** + * Configures the updating mechanism based on the current state and settings. + * + * This method decides between using the browser's native video frame callback or a custom ticker + * for updating the video frame. It ensures optimal performance and responsiveness + * based on the video's state, playback status, and the desired frames-per-second setting. + * + * - If `_autoUpdate` is enabled and the video source is playing: + * - It will prefer the native video frame callback if available and no specific FPS is set. + * - Otherwise, it will use a custom ticker for manual updates. + * - If `_autoUpdate` is disabled or the video isn't playing, any active update mechanisms are halted. + */ + _configureAutoUpdate() { + if (this._autoUpdate && this._isSourcePlaying()) { + if (!this._updateFPS && this.resource.requestVideoFrameCallback) { + if (this._isConnectedToTicker) { + Ticker.shared.remove(this.updateFrame, this); + this._isConnectedToTicker = false; + this._msToNextUpdate = 0; + } + if (this._videoFrameRequestCallbackHandle === null) { + this._videoFrameRequestCallbackHandle = this.resource.requestVideoFrameCallback( + this._videoFrameRequestCallback + ); + } + } else { + if (this._videoFrameRequestCallbackHandle !== null) { + this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle); + this._videoFrameRequestCallbackHandle = null; + } + if (!this._isConnectedToTicker) { + Ticker.shared.add(this.updateFrame, this); + this._isConnectedToTicker = true; + this._msToNextUpdate = 0; + } + } + } else { + if (this._videoFrameRequestCallbackHandle !== null) { + this.resource.cancelVideoFrameCallback(this._videoFrameRequestCallbackHandle); + this._videoFrameRequestCallbackHandle = null; + } + if (this._isConnectedToTicker) { + Ticker.shared.remove(this.updateFrame, this); + this._isConnectedToTicker = false; + this._msToNextUpdate = 0; + } + } + } + static test(resource) { + return globalThis.HTMLVideoElement && resource instanceof HTMLVideoElement; + } + }; + _VideoSource.extension = ExtensionType.TextureSource; + /** The default options for video sources. */ + _VideoSource.defaultOptions = __spreadProps$v(__spreadValues$1b({}, TextureSource.defaultOptions), { + /** If true, the video will start loading immediately. */ + autoLoad: true, + /** If true, the video will start playing as soon as it is loaded. */ + autoPlay: true, + /** The number of times a second to update the texture from the video. Leave at 0 to update at every render. */ + updateFPS: 0, + /** If true, the video will be loaded with the `crossorigin` attribute. */ + crossorigin: true, + /** If true, the video will loop when it ends. */ + loop: false, + /** If true, the video will be muted. */ + muted: true, + /** If true, the video will play inline. */ + playsinline: true, + /** If true, the video will be preloaded. */ + preload: false + }); + /** + * Map of video MIME types that can't be directly derived from file extensions. + * @readonly + */ + _VideoSource.MIME_TYPES = { + ogv: "video/ogg", + mov: "video/quicktime", + m4v: "video/mp4" + }; + let VideoSource = _VideoSource; + + "use strict"; + class CacheClass { + constructor() { + this._parsers = []; + this._cache = /* @__PURE__ */ new Map(); + this._cacheMap = /* @__PURE__ */ new Map(); + } + /** Clear all entries. */ + reset() { + this._cacheMap.clear(); + this._cache.clear(); + } + /** + * Check if the key exists + * @param key - The key to check + */ + has(key) { + return this._cache.has(key); + } + /** + * Fetch entry by key + * @param key - The key of the entry to get + */ + get(key) { + const result = this._cache.get(key); + if (!result) { + warn(`[Assets] Asset id ${key} was not found in the Cache`); + } + return result; + } + /** + * Set a value by key or keys name + * @param key - The key or keys to set + * @param value - The value to store in the cache or from which cacheable assets will be derived. + */ + set(key, value) { + const keys = convertToList(key); + let cacheableAssets; + for (let i = 0; i < this.parsers.length; i++) { + const parser = this.parsers[i]; + if (parser.test(value)) { + cacheableAssets = parser.getCacheableAssets(keys, value); + break; + } + } + const cacheableMap = new Map(Object.entries(cacheableAssets || {})); + if (!cacheableAssets) { + keys.forEach((key2) => { + cacheableMap.set(key2, value); + }); + } + const cacheKeys = [...cacheableMap.keys()]; + const cachedAssets = { + cacheKeys, + keys + }; + keys.forEach((key2) => { + this._cacheMap.set(key2, cachedAssets); + }); + cacheKeys.forEach((key2) => { + const val = cacheableAssets ? cacheableAssets[key2] : value; + if (this._cache.has(key2) && this._cache.get(key2) !== val) { + warn("[Cache] already has key:", key2); + } + this._cache.set(key2, cacheableMap.get(key2)); + }); + } + /** + * Remove entry by key + * + * This function will also remove any associated alias from the cache also. + * @param key - The key of the entry to remove + */ + remove(key) { + if (!this._cacheMap.has(key)) { + warn(`[Assets] Asset id ${key} was not found in the Cache`); + return; + } + const cacheMap = this._cacheMap.get(key); + const cacheKeys = cacheMap.cacheKeys; + cacheKeys.forEach((key2) => { + this._cache.delete(key2); + }); + cacheMap.keys.forEach((key2) => { + this._cacheMap.delete(key2); + }); + } + /** + * All loader parsers registered + * @advanced + */ + get parsers() { + return this._parsers; + } + } + const Cache = new CacheClass(); + + "use strict"; + const sources = []; + extensions.handleByList(ExtensionType.TextureSource, sources); + function autoDetectSource(options = {}) { + return textureSourceFrom(options); + } + function textureSourceFrom(options = {}) { + const hasResource = options && options.resource; + const res = hasResource ? options.resource : options; + const opts = hasResource ? options : { resource: options }; + for (let i = 0; i < sources.length; i++) { + const Source = sources[i]; + if (Source.test(res)) { + return new Source(opts); + } + } + throw new Error(`Could not find a source type for resource: ${opts.resource}`); + } + function resourceToTexture(options = {}, skipCache = false) { + const hasResource = options && options.resource; + const resource = hasResource ? options.resource : options; + const opts = hasResource ? options : { resource: options }; + if (!skipCache && Cache.has(resource)) { + return Cache.get(resource); + } + const texture = new Texture({ source: textureSourceFrom(opts) }); + texture.on("destroy", () => { + if (Cache.has(resource)) { + Cache.remove(resource); + } + }); + if (!skipCache) { + Cache.set(resource, texture); + } + return texture; + } + function textureFrom(id, skipCache = false) { + if (typeof id === "string") { + return Cache.get(id); + } else if (id instanceof TextureSource) { + return new Texture({ source: id }); + } + return resourceToTexture(id, skipCache); + } + Texture.from = textureFrom; + TextureSource.from = textureSourceFrom; + + "use strict"; + extensions.add(AlphaMask, ColorMask, StencilMask, VideoSource, ImageSource, CanvasSource, BufferImageSource); + + "use strict"; + class FilterPipe { + constructor(renderer) { + this._renderer = renderer; + } + push(filterEffect, container, instructionSet) { + const renderPipes = this._renderer.renderPipes; + renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "filter", + canBundle: false, + action: "pushFilter", + container, + filterEffect + }); + } + pop(_filterEffect, _container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "filter", + action: "popFilter", + canBundle: false + }); + } + execute(instruction) { + if (instruction.action === "pushFilter") { + this._renderer.filter.push(instruction); + } else if (instruction.action === "popFilter") { + this._renderer.filter.pop(); + } + } + destroy() { + this._renderer = null; + } + } + FilterPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "filter" + }; + + "use strict"; + const idCounts = /* @__PURE__ */ Object.create(null); + const idHash = /* @__PURE__ */ Object.create(null); + function createIdFromString(value, groupId) { + let id = idHash[value]; + if (id === void 0) { + if (idCounts[groupId] === void 0) { + idCounts[groupId] = 1; + } + idHash[value] = id = idCounts[groupId]++; + } + return id; + } + + "use strict"; + let context; + function getTestContext() { + if (!context || (context == null ? void 0 : context.isContextLost())) { + const canvas = DOMAdapter.get().createCanvas(); + context = canvas.getContext("webgl", {}); + } + return context; + } + + "use strict"; + let maxFragmentPrecision; + function getMaxFragmentPrecision() { + if (!maxFragmentPrecision) { + maxFragmentPrecision = "mediump"; + const gl = getTestContext(); + if (gl) { + if (gl.getShaderPrecisionFormat) { + const shaderFragment = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); + maxFragmentPrecision = shaderFragment.precision ? "highp" : "mediump"; + } + } + } + return maxFragmentPrecision; + } + + "use strict"; + function addProgramDefines(src, isES300, isFragment) { + if (isES300) return src; + if (isFragment) { + src = src.replace("out vec4 finalColor;", ""); + return ` + + #ifdef GL_ES // This checks if it is WebGL1 + #define in varying + #define finalColor gl_FragColor + #define texture texture2D + #endif + ${src} + `; + } + return ` + + #ifdef GL_ES // This checks if it is WebGL1 + #define in attribute + #define out varying + #endif + ${src} + `; + } + + "use strict"; + function ensurePrecision(src, options, isFragment) { + const maxSupportedPrecision = isFragment ? options.maxSupportedFragmentPrecision : options.maxSupportedVertexPrecision; + if (src.substring(0, 9) !== "precision") { + let precision = isFragment ? options.requestedFragmentPrecision : options.requestedVertexPrecision; + if (precision === "highp" && maxSupportedPrecision !== "highp") { + precision = "mediump"; + } + return `precision ${precision} float; +${src}`; + } else if (maxSupportedPrecision !== "highp" && src.substring(0, 15) === "precision highp") { + return src.replace("precision highp", "precision mediump"); + } + return src; + } + + "use strict"; + function insertVersion(src, isES300) { + if (!isES300) return src; + return `#version 300 es +${src}`; + } + + "use strict"; + const fragmentNameCache = {}; + const VertexNameCache = {}; + function setProgramName(src, { name = `pixi-program` }, isFragment = true) { + name = name.replace(/\s+/g, "-"); + name += isFragment ? "-fragment" : "-vertex"; + const nameCache = isFragment ? fragmentNameCache : VertexNameCache; + if (nameCache[name]) { + nameCache[name]++; + name += `-${nameCache[name]}`; + } else { + nameCache[name] = 1; + } + if (src.indexOf("#define SHADER_NAME") !== -1) return src; + const shaderName = `#define SHADER_NAME ${name}`; + return `${shaderName} +${src}`; + } + + "use strict"; + function stripVersion(src, isES300) { + if (!isES300) return src; + return src.replace("#version 300 es", ""); + } + + "use strict"; + var __defProp$1a = Object.defineProperty; + var __getOwnPropSymbols$1c = Object.getOwnPropertySymbols; + var __hasOwnProp$1c = Object.prototype.hasOwnProperty; + var __propIsEnum$1c = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1a = (obj, key, value) => key in obj ? __defProp$1a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1a = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1c.call(b, prop)) + __defNormalProp$1a(a, prop, b[prop]); + if (__getOwnPropSymbols$1c) + for (var prop of __getOwnPropSymbols$1c(b)) { + if (__propIsEnum$1c.call(b, prop)) + __defNormalProp$1a(a, prop, b[prop]); + } + return a; + }; + const processes = { + // strips any version headers.. + stripVersion, + // adds precision string if not already present + ensurePrecision, + // add some defines if WebGL1 to make it more compatible with WebGL2 shaders + addProgramDefines, + // add the program name to the shader + setProgramName, + // add the version string to the shader header + insertVersion + }; + const programCache$1 = /* @__PURE__ */ Object.create(null); + const _GlProgram = class _GlProgram { + /** + * Creates a shiny new GlProgram. Used by WebGL renderer. + * @param options - The options for the program. + */ + constructor(options) { + options = __spreadValues$1a(__spreadValues$1a({}, _GlProgram.defaultOptions), options); + const isES300 = options.fragment.indexOf("#version 300 es") !== -1; + const preprocessorOptions = { + stripVersion: isES300, + ensurePrecision: { + requestedFragmentPrecision: options.preferredFragmentPrecision, + requestedVertexPrecision: options.preferredVertexPrecision, + maxSupportedVertexPrecision: "highp", + maxSupportedFragmentPrecision: getMaxFragmentPrecision() + }, + setProgramName: { + name: options.name + }, + addProgramDefines: isES300, + insertVersion: isES300 + }; + let fragment = options.fragment; + let vertex = options.vertex; + Object.keys(processes).forEach((processKey) => { + const processOptions = preprocessorOptions[processKey]; + fragment = processes[processKey](fragment, processOptions, true); + vertex = processes[processKey](vertex, processOptions, false); + }); + this.fragment = fragment; + this.vertex = vertex; + this.transformFeedbackVaryings = options.transformFeedbackVaryings; + this._key = createIdFromString(`${this.vertex}:${this.fragment}`, "gl-program"); + } + /** destroys the program */ + destroy() { + this.fragment = null; + this.vertex = null; + this._attributeData = null; + this._uniformData = null; + this._uniformBlockData = null; + this.transformFeedbackVaryings = null; + programCache$1[this._cacheKey] = null; + } + /** + * Helper function that creates a program for a given source. + * It will check the program cache if the program has already been created. + * If it has that one will be returned, if not a new one will be created and cached. + * @param options - The options for the program. + * @returns A program using the same source + */ + static from(options) { + const key = `${options.vertex}:${options.fragment}`; + if (!programCache$1[key]) { + programCache$1[key] = new _GlProgram(options); + programCache$1[key]._cacheKey = key; + } + return programCache$1[key]; + } + }; + /** The default options used by the program. */ + _GlProgram.defaultOptions = { + preferredVertexPrecision: "highp", + preferredFragmentPrecision: "mediump" + }; + let GlProgram = _GlProgram; + + "use strict"; + const attributeFormatData = { + uint8x2: { size: 2, stride: 2, normalised: false }, + uint8x4: { size: 4, stride: 4, normalised: false }, + sint8x2: { size: 2, stride: 2, normalised: false }, + sint8x4: { size: 4, stride: 4, normalised: false }, + unorm8x2: { size: 2, stride: 2, normalised: true }, + unorm8x4: { size: 4, stride: 4, normalised: true }, + snorm8x2: { size: 2, stride: 2, normalised: true }, + snorm8x4: { size: 4, stride: 4, normalised: true }, + uint16x2: { size: 2, stride: 4, normalised: false }, + uint16x4: { size: 4, stride: 8, normalised: false }, + sint16x2: { size: 2, stride: 4, normalised: false }, + sint16x4: { size: 4, stride: 8, normalised: false }, + unorm16x2: { size: 2, stride: 4, normalised: true }, + unorm16x4: { size: 4, stride: 8, normalised: true }, + snorm16x2: { size: 2, stride: 4, normalised: true }, + snorm16x4: { size: 4, stride: 8, normalised: true }, + float16x2: { size: 2, stride: 4, normalised: false }, + float16x4: { size: 4, stride: 8, normalised: false }, + float32: { size: 1, stride: 4, normalised: false }, + float32x2: { size: 2, stride: 8, normalised: false }, + float32x3: { size: 3, stride: 12, normalised: false }, + float32x4: { size: 4, stride: 16, normalised: false }, + uint32: { size: 1, stride: 4, normalised: false }, + uint32x2: { size: 2, stride: 8, normalised: false }, + uint32x3: { size: 3, stride: 12, normalised: false }, + uint32x4: { size: 4, stride: 16, normalised: false }, + sint32: { size: 1, stride: 4, normalised: false }, + sint32x2: { size: 2, stride: 8, normalised: false }, + sint32x3: { size: 3, stride: 12, normalised: false }, + sint32x4: { size: 4, stride: 16, normalised: false } + }; + function getAttributeInfoFromFormat(format) { + var _a; + return (_a = attributeFormatData[format]) != null ? _a : attributeFormatData.float32; + } + + "use strict"; + const WGSL_TO_VERTEX_TYPES = { + f32: "float32", + "vec2": "float32x2", + "vec3": "float32x3", + "vec4": "float32x4", + vec2f: "float32x2", + vec3f: "float32x3", + vec4f: "float32x4", + i32: "sint32", + "vec2": "sint32x2", + "vec3": "sint32x3", + "vec4": "sint32x4", + vec2i: "sint32x2", + vec3i: "sint32x3", + vec4i: "sint32x4", + u32: "uint32", + "vec2": "uint32x2", + "vec3": "uint32x3", + "vec4": "uint32x4", + vec2u: "uint32x2", + vec3u: "uint32x3", + vec4u: "uint32x4", + bool: "uint32", + "vec2": "uint32x2", + "vec3": "uint32x3", + "vec4": "uint32x4" + }; + const LOCATION_REGEX = /@location\((\d+)\)\s+([a-zA-Z0-9_]+)\s*:\s*([a-zA-Z0-9_<>]+)(?:,|\s|\)|$)/g; + function parseLocations(str, results) { + var _a; + let match; + while ((match = LOCATION_REGEX.exec(str)) !== null) { + const format = (_a = WGSL_TO_VERTEX_TYPES[match[3]]) != null ? _a : "float32"; + results[match[2]] = { + location: parseInt(match[1], 10), + format, + stride: getAttributeInfoFromFormat(format).stride, + offset: 0, + instance: false, + start: 0 + }; + } + LOCATION_REGEX.lastIndex = 0; + } + function stripComments(source) { + return source.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, ""); + } + function extractAttributesFromGpuProgram({ source, entryPoint }) { + const results = {}; + const cleanSource = stripComments(source); + const mainVertStart = cleanSource.indexOf(`fn ${entryPoint}(`); + if (mainVertStart === -1) { + return results; + } + const arrowFunctionStart = cleanSource.indexOf("->", mainVertStart); + if (arrowFunctionStart === -1) { + return results; + } + const functionArgsSubstring = cleanSource.substring(mainVertStart, arrowFunctionStart); + parseLocations(functionArgsSubstring, results); + if (Object.keys(results).length === 0) { + const structMatch = functionArgsSubstring.match(/\(\s*\w+\s*:\s*(\w+)/); + if (structMatch) { + const structName = structMatch[1]; + const structRegex = new RegExp(`struct\\s+${structName}\\s*\\{([^}]+)\\}`, "s"); + const structBody = cleanSource.match(structRegex); + if (structBody) { + parseLocations(structBody[1], results); + } + } + } + return results; + } + + "use strict"; + function extractStructAndGroups(wgsl) { + var _a, _b, _c; + const linePattern = /(^|[^/])@(group|binding)\(\d+\)[^;]+;/g; + const groupPattern = /@group\((\d+)\)/; + const bindingPattern = /@binding\((\d+)\)/; + const namePattern = /var(<[^>]+>)? (\w+)/; + const typePattern = /:\s*([\w<>]+)/; + const structPattern = /struct\s+(\w+)\s*{([^}]+)}/g; + const structMemberPattern = /(\w+)\s*:\s*([\w\<\>]+)/g; + const structName = /struct\s+(\w+)/; + const groups = (_a = wgsl.match(linePattern)) == null ? void 0 : _a.map((item) => ({ + group: parseInt(item.match(groupPattern)[1], 10), + binding: parseInt(item.match(bindingPattern)[1], 10), + name: item.match(namePattern)[2], + isUniform: item.match(namePattern)[1] === "", + type: item.match(typePattern)[1] + })); + if (!groups) { + return { + groups: [], + structs: [] + }; + } + const structs = (_c = (_b = wgsl.match(structPattern)) == null ? void 0 : _b.map((struct) => { + const name = struct.match(structName)[1]; + const members = struct.match(structMemberPattern).reduce((acc, member) => { + const [name2, type] = member.split(":"); + acc[name2.trim()] = type.trim(); + return acc; + }, {}); + if (!members) { + return null; + } + return { name, members }; + }).filter(({ name }) => groups.some( + (group) => ( + // Handle both direct type matches and generic types like array + group.type === name || group.type.includes(`<${name}>`) + ) + ))) != null ? _c : []; + return { + groups, + structs + }; + } + + "use strict"; + var ShaderStage = /* @__PURE__ */ ((ShaderStage2) => { + ShaderStage2[ShaderStage2["VERTEX"] = 1] = "VERTEX"; + ShaderStage2[ShaderStage2["FRAGMENT"] = 2] = "FRAGMENT"; + ShaderStage2[ShaderStage2["COMPUTE"] = 4] = "COMPUTE"; + return ShaderStage2; + })(ShaderStage || {}); + + "use strict"; + function generateGpuLayoutGroups({ groups }) { + const layout = []; + for (let i = 0; i < groups.length; i++) { + const group = groups[i]; + if (!layout[group.group]) { + layout[group.group] = []; + } + if (group.isUniform) { + layout[group.group].push({ + binding: group.binding, + visibility: ShaderStage.VERTEX | ShaderStage.FRAGMENT, + buffer: { + type: "uniform" + } + }); + } else if (group.type === "sampler") { + layout[group.group].push({ + binding: group.binding, + visibility: ShaderStage.FRAGMENT, + sampler: { + type: "filtering" + } + }); + } else if (group.type === "texture_2d" || group.type.startsWith("texture_2d<")) { + layout[group.group].push({ + binding: group.binding, + visibility: ShaderStage.FRAGMENT, + texture: { + sampleType: "float", + viewDimension: "2d", + multisampled: false + } + }); + } else if (group.type === "texture_2d_array" || group.type.startsWith("texture_2d_array<")) { + layout[group.group].push({ + binding: group.binding, + visibility: ShaderStage.FRAGMENT, + texture: { + sampleType: "float", + viewDimension: "2d-array", + multisampled: false + } + }); + } else if (group.type === "texture_cube" || group.type.startsWith("texture_cube<")) { + layout[group.group].push({ + binding: group.binding, + visibility: ShaderStage.FRAGMENT, + texture: { + sampleType: "float", + viewDimension: "cube", + multisampled: false + } + }); + } + } + for (let i = 0; i < layout.length; i++) { + layout[i] || (layout[i] = []); + } + return layout; + } + + "use strict"; + function generateLayoutHash({ groups }) { + const layout = []; + for (let i = 0; i < groups.length; i++) { + const group = groups[i]; + if (!layout[group.group]) { + layout[group.group] = {}; + } + layout[group.group][group.name] = group.binding; + } + return layout; + } + + "use strict"; + function removeStructAndGroupDuplicates(vertexStructsAndGroups, fragmentStructsAndGroups) { + const structNameSet = /* @__PURE__ */ new Set(); + const dupeGroupKeySet = /* @__PURE__ */ new Set(); + const structs = [...vertexStructsAndGroups.structs, ...fragmentStructsAndGroups.structs].filter((struct) => { + if (structNameSet.has(struct.name)) { + return false; + } + structNameSet.add(struct.name); + return true; + }); + const groups = [...vertexStructsAndGroups.groups, ...fragmentStructsAndGroups.groups].filter((group) => { + const key = `${group.name}-${group.binding}`; + if (dupeGroupKeySet.has(key)) { + return false; + } + dupeGroupKeySet.add(key); + return true; + }); + return { structs, groups }; + } + + "use strict"; + const programCache = /* @__PURE__ */ Object.create(null); + class GpuProgram { + /** + * Create a new GpuProgram + * @param options - The options for the gpu program + */ + constructor(options) { + /** @internal */ + this._layoutKey = 0; + /** @internal */ + this._attributeLocationsKey = 0; + var _a, _b; + const { fragment, vertex, layout, gpuLayout, name } = options; + this.name = name; + this.fragment = fragment; + this.vertex = vertex; + if (fragment.source === vertex.source) { + const structsAndGroups = extractStructAndGroups(fragment.source); + this.structsAndGroups = structsAndGroups; + } else { + const vertexStructsAndGroups = extractStructAndGroups(vertex.source); + const fragmentStructsAndGroups = extractStructAndGroups(fragment.source); + this.structsAndGroups = removeStructAndGroupDuplicates(vertexStructsAndGroups, fragmentStructsAndGroups); + } + this.layout = layout != null ? layout : generateLayoutHash(this.structsAndGroups); + this.gpuLayout = gpuLayout != null ? gpuLayout : generateGpuLayoutGroups(this.structsAndGroups); + this.autoAssignGlobalUniforms = !!(((_a = this.layout[0]) == null ? void 0 : _a.globalUniforms) !== void 0); + this.autoAssignLocalUniforms = !!(((_b = this.layout[1]) == null ? void 0 : _b.localUniforms) !== void 0); + this._generateProgramKey(); + } + // TODO maker this pure + _generateProgramKey() { + const { vertex, fragment } = this; + const bigKey = vertex.source + fragment.source + vertex.entryPoint + fragment.entryPoint; + this._layoutKey = createIdFromString(bigKey, "program"); + } + get attributeData() { + var _a; + (_a = this._attributeData) != null ? _a : this._attributeData = extractAttributesFromGpuProgram(this.vertex); + return this._attributeData; + } + /** destroys the program */ + destroy() { + this.gpuLayout = null; + this.layout = null; + this.structsAndGroups = null; + this.fragment = null; + this.vertex = null; + programCache[this._cacheKey] = null; + } + /** + * Helper function that creates a program for a given source. + * It will check the program cache if the program has already been created. + * If it has that one will be returned, if not a new one will be created and cached. + * @param options - The options for the program. + * @returns A program using the same source + */ + static from(options) { + const key = `${options.vertex.source}:${options.fragment.source}:${options.fragment.entryPoint}:${options.vertex.entryPoint}`; + if (!programCache[key]) { + programCache[key] = new GpuProgram(options); + programCache[key]._cacheKey = key; + } + return programCache[key]; + } + } + + "use strict"; + class BindGroup { + /** + * Create a new instance eof the Bind Group. + * @param resources - The resources that are bound together for use by a shader. + */ + constructor(resources) { + /** The resources that are bound together for use by a shader. */ + this.resources = /* @__PURE__ */ Object.create(null); + this._dirty = true; + let index = 0; + for (const i in resources) { + const resource = resources[i]; + this.setResource(resource, index++); + } + this._updateKey(); + } + /** + * Updates the key if its flagged as dirty. This is used internally to + * match this bind group to a WebGPU BindGroup. + * @internal + */ + _updateKey() { + if (!this._dirty) return; + this._dirty = false; + const keyParts = []; + let index = 0; + for (const i in this.resources) { + keyParts[index++] = this.resources[i]._resourceId; + } + this._key = keyParts.join("|"); + } + /** + * Set a resource at a given index. this function will + * ensure that listeners will be removed from the current resource + * and added to the new resource. + * @param resource - The resource to set. + * @param index - The index to set the resource at. + */ + setResource(resource, index) { + var _a, _b; + const currentResource = this.resources[index]; + if (resource === currentResource) return; + if (currentResource) { + (_a = resource.off) == null ? void 0 : _a.call(resource, "change", this.onResourceChange, this); + } + (_b = resource.on) == null ? void 0 : _b.call(resource, "change", this.onResourceChange, this); + this.resources[index] = resource; + this._dirty = true; + } + /** + * Returns the resource at the current specified index. + * @param index - The index of the resource to get. + * @returns - The resource at the specified index. + */ + getResource(index) { + return this.resources[index]; + } + /** + * Used internally to 'touch' each resource, to ensure that the GC + * knows that all resources in this bind group are still being used. + * @param now - The current time in milliseconds. + * @param tick - The current tick. + * @internal + */ + _touch(now, tick) { + const resources = this.resources; + for (const i in resources) { + resources[i]._gcLastUsed = now; + resources[i]._touched = tick; + } + } + /** Destroys this bind group and removes all listeners. */ + destroy() { + var _a; + const resources = this.resources; + for (const i in resources) { + const resource = resources[i]; + (_a = resource == null ? void 0 : resource.off) == null ? void 0 : _a.call(resource, "change", this.onResourceChange, this); + } + this.resources = null; + } + onResourceChange(resource) { + this._dirty = true; + if (resource.destroyed) { + const resources = this.resources; + for (const i in resources) { + if (resources[i] === resource) { + resources[i] = null; + } + } + } else { + this._updateKey(); + } + } + } + + "use strict"; + var RendererType = /* @__PURE__ */ ((RendererType2) => { + RendererType2[RendererType2["WEBGL"] = 1] = "WEBGL"; + RendererType2[RendererType2["WEBGPU"] = 2] = "WEBGPU"; + RendererType2[RendererType2["CANVAS"] = 4] = "CANVAS"; + RendererType2[RendererType2["BOTH"] = 3] = "BOTH"; + return RendererType2; + })(RendererType || {}); + + "use strict"; + const UNIFORM_TYPES_VALUES = [ + "f32", + "i32", + "vec2", + "vec3", + "vec4", + "mat2x2", + "mat3x3", + "mat4x4", + "mat3x2", + "mat4x2", + "mat2x3", + "mat4x3", + "mat2x4", + "mat3x4", + "vec2", + "vec3", + "vec4" + ]; + const UNIFORM_TYPES_MAP = UNIFORM_TYPES_VALUES.reduce((acc, type) => { + acc[type] = true; + return acc; + }, {}); + + "use strict"; + function getDefaultUniformValue(type, size) { + switch (type) { + case "f32": + return 0; + case "vec2": + return new Float32Array(2 * size); + case "vec3": + return new Float32Array(3 * size); + case "vec4": + return new Float32Array(4 * size); + case "mat2x2": + return new Float32Array([ + 1, + 0, + 0, + 1 + ]); + case "mat3x3": + return new Float32Array([ + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1 + ]); + case "mat4x4": + return new Float32Array([ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ]); + } + return null; + } + + "use strict"; + var __defProp$19 = Object.defineProperty; + var __getOwnPropSymbols$1b = Object.getOwnPropertySymbols; + var __hasOwnProp$1b = Object.prototype.hasOwnProperty; + var __propIsEnum$1b = Object.prototype.propertyIsEnumerable; + var __defNormalProp$19 = (obj, key, value) => key in obj ? __defProp$19(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$19 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1b.call(b, prop)) + __defNormalProp$19(a, prop, b[prop]); + if (__getOwnPropSymbols$1b) + for (var prop of __getOwnPropSymbols$1b(b)) { + if (__propIsEnum$1b.call(b, prop)) + __defNormalProp$19(a, prop, b[prop]); + } + return a; + }; + const _UniformGroup = class _UniformGroup { + /** + * Create a new Uniform group + * @param uniformStructures - The structures of the uniform group + * @param options - The optional parameters of this uniform group + */ + constructor(uniformStructures, options) { + /** + * used internally to know if a uniform group was used in the last render pass + * @internal + */ + this._touched = 0; + /** a unique id for this uniform group used through the renderer */ + this.uid = uid$1("uniform"); + /** + * a resource type, used to identify how to handle it when its in a bind group / shader resource + * @internal + */ + this._resourceType = "uniformGroup"; + /** + * the resource id used internally by the renderer to build bind group keys + * @internal + */ + this._resourceId = uid$1("resource"); + /** used ito identify if this is a uniform group */ + this.isUniformGroup = true; + /** + * used to flag if this Uniform groups data is different from what it has stored in its buffer / on the GPU + * @internal + */ + this._dirtyId = 0; + // implementing the interface - UniformGroup are not destroyed + this.destroyed = false; + var _a, _b; + options = __spreadValues$19(__spreadValues$19({}, _UniformGroup.defaultOptions), options); + this.uniformStructures = uniformStructures; + const uniforms = {}; + for (const i in uniformStructures) { + const uniformData = uniformStructures[i]; + uniformData.name = i; + uniformData.size = (_a = uniformData.size) != null ? _a : 1; + if (!UNIFORM_TYPES_MAP[uniformData.type]) { + const arrayMatch = uniformData.type.match(/^array<(\w+(?:<\w+>)?),\s*(\d+)>$/); + if (arrayMatch) { + const [, innerType, size] = arrayMatch; + throw new Error( + `Uniform type ${uniformData.type} is not supported. Use type: '${innerType}', size: ${size} instead.` + ); + } + throw new Error(`Uniform type ${uniformData.type} is not supported. Supported uniform types are: ${UNIFORM_TYPES_VALUES.join(", ")}`); + } + (_b = uniformData.value) != null ? _b : uniformData.value = getDefaultUniformValue(uniformData.type, uniformData.size); + uniforms[i] = uniformData.value; + } + this.uniforms = uniforms; + this._dirtyId = 1; + this.ubo = options.ubo; + this.isStatic = options.isStatic; + this._signature = createIdFromString(Object.keys(uniforms).map( + (i) => `${i}-${uniformStructures[i].type}` + ).join("-"), "uniform-group"); + } + /** Call this if you want the uniform groups data to be uploaded to the GPU only useful if `isStatic` is true. */ + update() { + this._dirtyId++; + } + }; + /** The default options used by the uniform group. */ + _UniformGroup.defaultOptions = { + /** if true the UniformGroup is handled as an Uniform buffer object. */ + ubo: false, + /** if true, then you are responsible for when the data is uploaded to the GPU by calling `update()` */ + isStatic: false + }; + let UniformGroup = _UniformGroup; + + "use strict"; + var __defProp$18 = Object.defineProperty; + var __getOwnPropSymbols$1a = Object.getOwnPropertySymbols; + var __hasOwnProp$1a = Object.prototype.hasOwnProperty; + var __propIsEnum$1a = Object.prototype.propertyIsEnumerable; + var __defNormalProp$18 = (obj, key, value) => key in obj ? __defProp$18(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$18 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1a.call(b, prop)) + __defNormalProp$18(a, prop, b[prop]); + if (__getOwnPropSymbols$1a) + for (var prop of __getOwnPropSymbols$1a(b)) { + if (__propIsEnum$1a.call(b, prop)) + __defNormalProp$18(a, prop, b[prop]); + } + return a; + }; + var __objRest$p = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$1a.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$1a) + for (var prop of __getOwnPropSymbols$1a(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$1a.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class Shader extends EventEmitter { + constructor(options) { + super(); + /** A unique identifier for the shader */ + this.uid = uid$1("shader"); + /** + * A record of the uniform groups and resources used by the shader. + * This is used by WebGL renderer to sync uniform data. + * @internal + */ + this._uniformBindMap = /* @__PURE__ */ Object.create(null); + this._ownedBindGroups = []; + /** @internal */ + this._destroyed = false; + let { + gpuProgram, + glProgram, + groups, + resources, + compatibleRenderers, + groupMap + } = options; + this.gpuProgram = gpuProgram; + this.glProgram = glProgram; + if (compatibleRenderers === void 0) { + compatibleRenderers = 0; + if (gpuProgram) compatibleRenderers |= RendererType.WEBGPU; + if (glProgram) compatibleRenderers |= RendererType.WEBGL; + } + this.compatibleRenderers = compatibleRenderers; + const nameHash = {}; + if (!resources && !groups) { + resources = {}; + } + if (resources && groups) { + throw new Error("[Shader] Cannot have both resources and groups"); + } else if (!gpuProgram && groups && !groupMap) { + throw new Error("[Shader] No group map or WebGPU shader provided - consider using resources instead."); + } else if (!gpuProgram && groups && groupMap) { + for (const i in groupMap) { + for (const j in groupMap[i]) { + const uniformName = groupMap[i][j]; + nameHash[uniformName] = { + group: i, + binding: j, + name: uniformName + }; + } + } + } else if (gpuProgram && groups && !groupMap) { + const groupData = gpuProgram.structsAndGroups.groups; + groupMap = {}; + groupData.forEach((data) => { + groupMap[data.group] = groupMap[data.group] || {}; + groupMap[data.group][data.binding] = data.name; + nameHash[data.name] = data; + }); + } else if (resources) { + groups = {}; + groupMap = {}; + if (gpuProgram) { + const groupData = gpuProgram.structsAndGroups.groups; + groupData.forEach((data) => { + groupMap[data.group] = groupMap[data.group] || {}; + groupMap[data.group][data.binding] = data.name; + nameHash[data.name] = data; + }); + } + let bindTick = 0; + for (const i in resources) { + if (nameHash[i]) continue; + if (!groups[99]) { + groups[99] = new BindGroup(); + this._ownedBindGroups.push(groups[99]); + } + nameHash[i] = { group: 99, binding: bindTick, name: i }; + groupMap[99] = groupMap[99] || {}; + groupMap[99][bindTick] = i; + bindTick++; + } + for (const i in resources) { + const name = i; + let value = resources[i]; + if (!value.source && !value._resourceType) { + value = new UniformGroup(value); + } + const data = nameHash[name]; + if (data) { + if (!groups[data.group]) { + groups[data.group] = new BindGroup(); + this._ownedBindGroups.push(groups[data.group]); + } + groups[data.group].setResource(value, data.binding); + } + } + } + this.groups = groups; + this._uniformBindMap = groupMap; + this.resources = this._buildResourceAccessor(groups, nameHash); + } + /** + * Sometimes a resource group will be provided later (for example global uniforms) + * In such cases, this method can be used to let the shader know about the group. + * @param name - the name of the resource group + * @param groupIndex - the index of the group (should match the webGPU shader group location) + * @param bindIndex - the index of the bind point (should match the webGPU shader bind point) + */ + addResource(name, groupIndex, bindIndex) { + var _a, _b; + (_a = this._uniformBindMap)[groupIndex] || (_a[groupIndex] = {}); + (_b = this._uniformBindMap[groupIndex])[bindIndex] || (_b[bindIndex] = name); + if (!this.groups[groupIndex]) { + this.groups[groupIndex] = new BindGroup(); + this._ownedBindGroups.push(this.groups[groupIndex]); + } + } + _buildResourceAccessor(groups, nameHash) { + const uniformsOut = {}; + for (const i in nameHash) { + const data = nameHash[i]; + Object.defineProperty(uniformsOut, data.name, { + get() { + return groups[data.group].getResource(data.binding); + }, + set(value) { + groups[data.group].setResource(value, data.binding); + } + }); + } + return uniformsOut; + } + /** + * Use to destroy the shader when its not longer needed. + * It will destroy the resources and remove listeners. + * @param destroyPrograms - if the programs should be destroyed as well. + * Make sure its not being used by other shaders! + */ + destroy(destroyPrograms = false) { + var _a, _b; + if (this._destroyed) return; + this._destroyed = true; + this.emit("destroy", this); + if (destroyPrograms) { + (_a = this.gpuProgram) == null ? void 0 : _a.destroy(); + (_b = this.glProgram) == null ? void 0 : _b.destroy(); + } + this.gpuProgram = null; + this.glProgram = null; + this.removeAllListeners(); + this._uniformBindMap = null; + this._ownedBindGroups.forEach((bindGroup) => { + bindGroup.destroy(); + }); + this._ownedBindGroups = null; + this.resources = null; + this.groups = null; + } + static from(options) { + const _a = options, { gpu, gl } = _a, rest = __objRest$p(_a, ["gpu", "gl"]); + let gpuProgram; + let glProgram; + if (gpu) { + gpuProgram = GpuProgram.from(gpu); + } + if (gl) { + glProgram = GlProgram.from(gl); + } + return new Shader(__spreadValues$18({ + gpuProgram, + glProgram + }, rest)); + } + } + + "use strict"; + const blendModeIds = { + normal: 0, + add: 1, + multiply: 2, + screen: 3, + overlay: 4, + erase: 5, + "normal-npm": 6, + "add-npm": 7, + "screen-npm": 8, + min: 9, + max: 10 + }; + const BLEND$1 = 0; + const OFFSET$1 = 1; + const CULLING$1 = 2; + const DEPTH_TEST$1 = 3; + const WINDING$1 = 4; + const DEPTH_MASK$1 = 5; + const _State = class _State { + constructor() { + this.data = 0; + this.blendMode = "normal"; + this.polygonOffset = 0; + this.blend = true; + this.depthMask = true; + } + /** + * Activates blending of the computed fragment color values. + * @default true + */ + get blend() { + return !!(this.data & 1 << BLEND$1); + } + set blend(value) { + if (!!(this.data & 1 << BLEND$1) !== value) { + this.data ^= 1 << BLEND$1; + } + } + /** + * Activates adding an offset to depth values of polygon's fragments + * @default false + */ + get offsets() { + return !!(this.data & 1 << OFFSET$1); + } + set offsets(value) { + if (!!(this.data & 1 << OFFSET$1) !== value) { + this.data ^= 1 << OFFSET$1; + } + } + /** The culling settings for this state none - No culling back - Back face culling front - Front face culling */ + set cullMode(value) { + if (value === "none") { + this.culling = false; + return; + } + this.culling = true; + this.clockwiseFrontFace = value === "front"; + } + get cullMode() { + if (!this.culling) { + return "none"; + } + return this.clockwiseFrontFace ? "front" : "back"; + } + /** + * Activates culling of polygons. + * @default false + */ + get culling() { + return !!(this.data & 1 << CULLING$1); + } + set culling(value) { + if (!!(this.data & 1 << CULLING$1) !== value) { + this.data ^= 1 << CULLING$1; + } + } + /** + * Activates depth comparisons and updates to the depth buffer. + * @default false + */ + get depthTest() { + return !!(this.data & 1 << DEPTH_TEST$1); + } + set depthTest(value) { + if (!!(this.data & 1 << DEPTH_TEST$1) !== value) { + this.data ^= 1 << DEPTH_TEST$1; + } + } + /** + * Enables or disables writing to the depth buffer. + * @default true + */ + get depthMask() { + return !!(this.data & 1 << DEPTH_MASK$1); + } + set depthMask(value) { + if (!!(this.data & 1 << DEPTH_MASK$1) !== value) { + this.data ^= 1 << DEPTH_MASK$1; + } + } + /** + * Specifies whether or not front or back-facing polygons can be culled. + * @default false + */ + get clockwiseFrontFace() { + return !!(this.data & 1 << WINDING$1); + } + set clockwiseFrontFace(value) { + if (!!(this.data & 1 << WINDING$1) !== value) { + this.data ^= 1 << WINDING$1; + } + } + /** + * The blend mode to be applied when this state is set. Apply a value of `normal` to reset the blend mode. + * Setting this mode to anything other than NO_BLEND will automatically switch blending on. + * @default 'normal' + */ + get blendMode() { + return this._blendMode; + } + set blendMode(value) { + this.blend = value !== "none"; + this._blendMode = value; + this._blendModeId = blendModeIds[value] || 0; + } + /** + * The polygon offset. Setting this property to anything other than 0 will automatically enable polygon offset fill. + * @default 0 + */ + get polygonOffset() { + return this._polygonOffset; + } + set polygonOffset(value) { + this.offsets = !!value; + this._polygonOffset = value; + } + toString() { + return `[pixi.js/core:State blendMode=${this.blendMode} clockwiseFrontFace=${this.clockwiseFrontFace} culling=${this.culling} depthMask=${this.depthMask} polygonOffset=${this.polygonOffset}]`; + } + /** + * A quickly getting an instance of a State that is configured for 2d rendering. + * @returns a new State with values set for 2d rendering + */ + static for2d() { + const state = new _State(); + state.depthTest = false; + state.blend = true; + return state; + } + }; + _State.default2d = _State.for2d(); + let State = _State; + + "use strict"; + var __defProp$17 = Object.defineProperty; + var __getOwnPropSymbols$19 = Object.getOwnPropertySymbols; + var __hasOwnProp$19 = Object.prototype.hasOwnProperty; + var __propIsEnum$19 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$17 = (obj, key, value) => key in obj ? __defProp$17(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$17 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$19.call(b, prop)) + __defNormalProp$17(a, prop, b[prop]); + if (__getOwnPropSymbols$19) + for (var prop of __getOwnPropSymbols$19(b)) { + if (__propIsEnum$19.call(b, prop)) + __defNormalProp$17(a, prop, b[prop]); + } + return a; + }; + var __objRest$o = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$19.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$19) + for (var prop of __getOwnPropSymbols$19(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$19.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const _Filter = class _Filter extends Shader { + /** + * @param options - The optional parameters of this filter. + */ + constructor(options) { + options = __spreadValues$17(__spreadValues$17({}, _Filter.defaultOptions), options); + super(options); + /** If enabled is true the filter is applied, if false it will not. */ + this.enabled = true; + /** + * The gpu state the filter requires to render. + * @internal + */ + this._state = State.for2d(); + this.blendMode = options.blendMode; + this.padding = options.padding; + if (typeof options.antialias === "boolean") { + this.antialias = options.antialias ? "on" : "off"; + } else { + this.antialias = options.antialias; + } + this.resolution = options.resolution; + this.blendRequired = options.blendRequired; + this.clipToViewport = options.clipToViewport; + this.addResource("uTexture", 0, 1); + if (options.blendRequired) { + this.addResource("uBackTexture", 0, 3); + } + } + /** + * Applies the filter + * @param filterManager - The renderer to retrieve the filter from + * @param input - The input render target. + * @param output - The target to output to. + * @param clearMode - Should the output be cleared before rendering to it + */ + apply(filterManager, input, output, clearMode) { + filterManager.applyFilter(this, input, output, clearMode); + } + /** + * Get the blend mode of the filter. + * @default "normal" + */ + get blendMode() { + return this._state.blendMode; + } + /** Sets the blend mode of the filter. */ + set blendMode(value) { + this._state.blendMode = value; + } + /** + * A short hand function to create a filter based of a vertex and fragment shader src. + * @param options + * @returns A shiny new PixiJS filter! + */ + static from(options) { + const _a = options, { gpu, gl } = _a, rest = __objRest$o(_a, ["gpu", "gl"]); + let gpuProgram; + let glProgram; + if (gpu) { + gpuProgram = GpuProgram.from(gpu); + } + if (gl) { + glProgram = GlProgram.from(gl); + } + return new _Filter(__spreadValues$17({ + gpuProgram, + glProgram + }, rest)); + } + }; + /** The default filter settings */ + _Filter.defaultOptions = { + blendMode: "normal", + resolution: 1, + padding: 0, + antialias: "off", + blendRequired: false, + clipToViewport: true + }; + let Filter = _Filter; + + var vertex$3 = "in vec2 aPosition;\nout vec2 vTextureCoord;\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n \n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n"; + + var fragment$6 = "in vec2 vTextureCoord;\nout vec4 finalColor;\nuniform sampler2D uTexture;\nvoid main() {\n finalColor = texture(uTexture, vTextureCoord);\n}\n"; + + var source$6 = "struct GlobalFilterUniforms {\n uInputSize: vec4,\n uInputPixel: vec4,\n uInputClamp: vec4,\n uOutputFrame: vec4,\n uGlobalFrame: vec4,\n uOutputTexture: vec4,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler: sampler;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv: vec2\n};\n\nfn filterVertexPosition(aPosition: vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord(aPosition: vec2) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\n@vertex\nfn mainVertex(\n @location(0) aPosition: vec2,\n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition)\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n) -> @location(0) vec4 {\n return textureSample(uTexture, uSampler, uv);\n}\n"; + + "use strict"; + class PassthroughFilter extends Filter { + constructor() { + const gpuProgram = GpuProgram.from({ + vertex: { source: source$6, entryPoint: "mainVertex" }, + fragment: { source: source$6, entryPoint: "mainFragment" }, + name: "passthrough-filter" + }); + const glProgram = GlProgram.from({ + vertex: vertex$3, + fragment: fragment$6, + name: "passthrough-filter" + }); + super({ + gpuProgram, + glProgram + }); + } + } + + "use strict"; + var BufferUsage = /* @__PURE__ */ ((BufferUsage2) => { + BufferUsage2[BufferUsage2["MAP_READ"] = 1] = "MAP_READ"; + BufferUsage2[BufferUsage2["MAP_WRITE"] = 2] = "MAP_WRITE"; + BufferUsage2[BufferUsage2["COPY_SRC"] = 4] = "COPY_SRC"; + BufferUsage2[BufferUsage2["COPY_DST"] = 8] = "COPY_DST"; + BufferUsage2[BufferUsage2["INDEX"] = 16] = "INDEX"; + BufferUsage2[BufferUsage2["VERTEX"] = 32] = "VERTEX"; + BufferUsage2[BufferUsage2["UNIFORM"] = 64] = "UNIFORM"; + BufferUsage2[BufferUsage2["STORAGE"] = 128] = "STORAGE"; + BufferUsage2[BufferUsage2["INDIRECT"] = 256] = "INDIRECT"; + BufferUsage2[BufferUsage2["QUERY_RESOLVE"] = 512] = "QUERY_RESOLVE"; + BufferUsage2[BufferUsage2["STATIC"] = 1024] = "STATIC"; + return BufferUsage2; + })(BufferUsage || {}); + + "use strict"; + class Buffer extends EventEmitter { + /** + * Creates a new Buffer with the given options + * @param options - the options for the buffer + */ + constructor(options) { + let { data, size } = options; + const { usage, label, shrinkToFit } = options; + super(); + /** + * emits when the underlying buffer has changed shape (i.e. resized) + * letting the renderer know that it needs to discard the old buffer on the GPU and create a new one + * @event change + */ + /** + * emits when the underlying buffer data has been updated. letting the renderer know + * that it needs to update the buffer on the GPU + * @event update + */ + /** + * emits when the buffer is destroyed. letting the renderer know that it needs to destroy the buffer on the GPU + * @event destroy + */ + /** @internal */ + this._gpuData = /* @__PURE__ */ Object.create(null); + /** @internal */ + this._gcLastUsed = -1; + /** If set to true, the buffer will be garbage collected automatically when it is not used. */ + this.autoGarbageCollect = true; + /** a unique id for this uniform group used through the renderer */ + this.uid = uid$1("buffer"); + /** + * a resource type, used to identify how to handle it when its in a bind group / shader resource + * @internal + */ + this._resourceType = "buffer"; + /** + * the resource id used internally by the renderer to build bind group keys + * @internal + */ + this._resourceId = uid$1("resource"); + /** + * used internally to know if a uniform group was used in the last render pass + * @internal + */ + this._touched = 0; + /** @internal */ + this._updateID = 1; + this._dataInt32 = null; + /** + * should the GPU buffer be shrunk when the data becomes smaller? + * changing this will cause the buffer to be destroyed and a new one created on the GPU + * this can be expensive, especially if the buffer is already big enough! + * setting this to false will prevent the buffer from being shrunk. This will yield better performance + * if you are constantly setting data that is changing size often. + * @default true + */ + this.shrinkToFit = true; + /** + * Has the buffer been destroyed? + * @readonly + */ + this.destroyed = false; + if (data instanceof Array) { + data = new Float32Array(data); + } + this._data = data; + size != null ? size : size = data == null ? void 0 : data.byteLength; + const mappedAtCreation = !!data; + this.descriptor = { + size, + usage, + mappedAtCreation, + label + }; + this.shrinkToFit = shrinkToFit != null ? shrinkToFit : true; + } + /** the data in the buffer */ + get data() { + return this._data; + } + set data(value) { + this.setDataWithSize(value, value.length, true); + } + get dataInt32() { + if (!this._dataInt32) { + this._dataInt32 = new Int32Array(this.data.buffer); + } + return this._dataInt32; + } + /** whether the buffer is static or not */ + get static() { + return !!(this.descriptor.usage & BufferUsage.STATIC); + } + set static(value) { + if (value) { + this.descriptor.usage |= BufferUsage.STATIC; + } else { + this.descriptor.usage &= ~BufferUsage.STATIC; + } + } + /** + * Sets the data in the buffer to the given value. This will immediately update the buffer on the GPU. + * If you only want to update a subset of the buffer, you can pass in the size of the data. + * @param value - the data to set + * @param size - the size of the data in bytes + * @param syncGPU - should the buffer be updated on the GPU immediately? + */ + setDataWithSize(value, size, syncGPU) { + this._updateID++; + this._updateSize = size * value.BYTES_PER_ELEMENT; + if (this._data === value) { + if (syncGPU) this.emit("update", this); + return; + } + const oldData = this._data; + this._data = value; + this._dataInt32 = null; + if (!oldData || oldData.length !== value.length) { + if (!this.shrinkToFit && oldData && value.byteLength < oldData.byteLength) { + if (syncGPU) this.emit("update", this); + } else { + this.descriptor.size = value.byteLength; + this._resourceId = uid$1("resource"); + this.emit("change", this); + } + return; + } + if (syncGPU) this.emit("update", this); + } + /** + * updates the buffer on the GPU to reflect the data in the buffer. + * By default it will update the entire buffer. If you only want to update a subset of the buffer, + * you can pass in the size of the buffer to update. + * @param sizeInBytes - the new size of the buffer in bytes + */ + update(sizeInBytes) { + this._updateSize = sizeInBytes != null ? sizeInBytes : this._updateSize; + this._updateID++; + this.emit("update", this); + } + /** Unloads the buffer from the GPU */ + unload() { + var _a; + this.emit("unload", this); + for (const key in this._gpuData) { + (_a = this._gpuData[key]) == null ? void 0 : _a.destroy(); + } + this._gpuData = /* @__PURE__ */ Object.create(null); + } + /** Destroys the buffer */ + destroy() { + this.destroyed = true; + this.unload(); + this.emit("destroy", this); + this.emit("change", this); + this._data = null; + this.descriptor = null; + this.removeAllListeners(); + } + } + + "use strict"; + function ensureIsBuffer(buffer, index) { + if (!(buffer instanceof Buffer)) { + let usage = index ? BufferUsage.INDEX : BufferUsage.VERTEX; + if (buffer instanceof Array) { + if (index) { + buffer = new Uint32Array(buffer); + usage = BufferUsage.INDEX | BufferUsage.COPY_DST; + } else { + buffer = new Float32Array(buffer); + usage = BufferUsage.VERTEX | BufferUsage.COPY_DST; + } + } + buffer = new Buffer({ + data: buffer, + label: index ? "index-mesh-buffer" : "vertex-mesh-buffer", + usage + }); + } + return buffer; + } + + "use strict"; + function getGeometryBounds(geometry, attributeId, bounds) { + const attribute = geometry.getAttribute(attributeId); + if (!attribute) { + bounds.minX = 0; + bounds.minY = 0; + bounds.maxX = 0; + bounds.maxY = 0; + return bounds; + } + const data = attribute.buffer.data; + let minX = Infinity; + let minY = Infinity; + let maxX = -Infinity; + let maxY = -Infinity; + const byteSize = data.BYTES_PER_ELEMENT; + const offset = (attribute.offset || 0) / byteSize; + const stride = (attribute.stride || 2 * 4) / byteSize; + for (let i = offset; i < data.length; i += stride) { + const x = data[i]; + const y = data[i + 1]; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + if (x < minX) minX = x; + if (y < minY) minY = y; + } + bounds.minX = minX; + bounds.minY = minY; + bounds.maxX = maxX; + bounds.maxY = maxY; + return bounds; + } + + "use strict"; + function ensureIsAttribute(attribute) { + if (attribute instanceof Buffer || Array.isArray(attribute) || attribute.BYTES_PER_ELEMENT) { + attribute = { + buffer: attribute + }; + } + attribute.buffer = ensureIsBuffer(attribute.buffer, false); + return attribute; + } + class Geometry extends EventEmitter { + /** + * Create a new instance of a geometry + * @param options - The options for the geometry. + */ + constructor(options = {}) { + var _a; + super(); + /** @internal */ + this._gpuData = /* @__PURE__ */ Object.create(null); + /** If set to true, the resource will be garbage collected automatically when it is not used. */ + this.autoGarbageCollect = true; + /** @internal */ + this._gcLastUsed = -1; + /** The unique id of the geometry. */ + this.uid = uid$1("geometry"); + /** + * the layout key will be generated by WebGPU all geometries that have the same structure + * will have the same layout key. This is used to cache the pipeline layout + * @internal + */ + this._layoutKey = 0; + /** the instance count of the geometry to draw */ + this.instanceCount = 1; + this._bounds = new Bounds(); + this._boundsDirty = true; + const { attributes, indexBuffer, topology } = options; + this.buffers = []; + this.attributes = {}; + if (attributes) { + for (const i in attributes) { + this.addAttribute(i, attributes[i]); + } + } + this.instanceCount = (_a = options.instanceCount) != null ? _a : 1; + if (indexBuffer) { + this.addIndex(indexBuffer); + } + this.topology = topology || "triangle-list"; + } + onBufferUpdate() { + this._boundsDirty = true; + this.emit("update", this); + } + /** + * Returns the requested attribute. + * @param id - The name of the attribute required + * @returns - The attribute requested. + */ + getAttribute(id) { + return this.attributes[id]; + } + /** + * Returns the index buffer + * @returns - The index buffer. + */ + getIndex() { + return this.indexBuffer; + } + /** + * Returns the requested buffer. + * @param id - The name of the buffer required. + * @returns - The buffer requested. + */ + getBuffer(id) { + return this.getAttribute(id).buffer; + } + /** + * Used to figure out how many vertices there are in this geometry + * @returns the number of vertices in the geometry + */ + getSize() { + for (const i in this.attributes) { + const attribute = this.attributes[i]; + const buffer = attribute.buffer; + return buffer.data.length / (attribute.stride / 4 || attribute.size); + } + return 0; + } + /** + * Adds an attribute to the geometry. + * @param name - The name of the attribute to add. + * @param attributeOption - The attribute option to add. + */ + addAttribute(name, attributeOption) { + const attribute = ensureIsAttribute(attributeOption); + const bufferIndex = this.buffers.indexOf(attribute.buffer); + if (bufferIndex === -1) { + this.buffers.push(attribute.buffer); + attribute.buffer.on("update", this.onBufferUpdate, this); + attribute.buffer.on("change", this.onBufferUpdate, this); + } + this.attributes[name] = attribute; + } + /** + * Adds an index buffer to the geometry. + * @param indexBuffer - The index buffer to add. Can be a Buffer, TypedArray, or an array of numbers. + */ + addIndex(indexBuffer) { + this.indexBuffer = ensureIsBuffer(indexBuffer, true); + this.buffers.push(this.indexBuffer); + } + /** Returns the bounds of the geometry. */ + get bounds() { + if (!this._boundsDirty) return this._bounds; + this._boundsDirty = false; + return getGeometryBounds(this, "aPosition", this._bounds); + } + /** Unloads the geometry from the GPU. */ + unload() { + var _a; + this.emit("unload", this); + for (const key in this._gpuData) { + (_a = this._gpuData[key]) == null ? void 0 : _a.destroy(); + } + this._gpuData = /* @__PURE__ */ Object.create(null); + } + /** + * destroys the geometry. + * @param destroyBuffers - destroy the buffers associated with this geometry + */ + destroy(destroyBuffers = false) { + var _a; + this.emit("destroy", this); + this.removeAllListeners(); + if (destroyBuffers) { + this.buffers.forEach((buffer) => buffer.destroy()); + } + this.unload(); + (_a = this.indexBuffer) == null ? void 0 : _a.destroy(); + this.attributes = null; + this.buffers = null; + this.indexBuffer = null; + this._bounds = null; + } + } + + "use strict"; + + "use strict"; + function squaredDistanceToLineSegment(x, y, x1, y1, x2, y2) { + const a = x - x1; + const b = y - y1; + const c = x2 - x1; + const d = y2 - y1; + const dot = a * c + b * d; + const lenSq = c * c + d * d; + let param = -1; + if (lenSq !== 0) { + param = dot / lenSq; + } + let xx; + let yy; + if (param < 0) { + xx = x1; + yy = y1; + } else if (param > 1) { + xx = x2; + yy = y2; + } else { + xx = x1 + param * c; + yy = y1 + param * d; + } + const dx = x - xx; + const dy = y - yy; + return dx * dx + dy * dy; + } + + "use strict"; + + "use strict"; + function pointInTriangle$1(px, py, x1, y1, x2, y2, x3, y3) { + const v2x = x3 - x1; + const v2y = y3 - y1; + const v1x = x2 - x1; + const v1y = y2 - y1; + const v0x = px - x1; + const v0y = py - y1; + const dot00 = v2x * v2x + v2y * v2y; + const dot01 = v2x * v1x + v2y * v1y; + const dot02 = v2x * v0x + v2y * v0y; + const dot11 = v1x * v1x + v1y * v1y; + const dot12 = v1x * v0x + v1y * v0y; + const invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + const u = (dot11 * dot02 - dot01 * dot12) * invDenom; + const v = (dot00 * dot12 - dot01 * dot02) * invDenom; + return u >= 0 && v >= 0 && u + v < 1; + } + + "use strict"; + + "use strict"; + class Circle { + /** + * @param x - The X coordinate of the center of this circle + * @param y - The Y coordinate of the center of this circle + * @param radius - The radius of the circle + */ + constructor(x = 0, y = 0, radius = 0) { + /** + * The type of the object, mainly used to avoid `instanceof` checks. + * @example + * ```ts + * // Check shape type + * const shape = new Circle(0, 0, 50); + * console.log(shape.type); // 'circle' + * + * // Use in type guards + * if (shape.type === 'circle') { + * console.log(shape.radius); + * } + * ``` + * @remarks + * - Used for shape type checking + * - More efficient than instanceof + * - Read-only property + * @readonly + * @default 'circle' + * @see {@link SHAPE_PRIMITIVE} For all shape types + * @see {@link ShapePrimitive} For shape interface + */ + this.type = "circle"; + this.x = x; + this.y = y; + this.radius = radius; + } + /** + * Creates a clone of this Circle instance. + * @example + * ```ts + * // Basic circle cloning + * const original = new Circle(100, 100, 50); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.radius = 75; + * + * // Verify independence + * console.log(original.radius); // 50 + * console.log(modified.radius); // 75 + * ``` + * @returns A copy of the Circle + * @see {@link Circle.copyFrom} For copying into existing circle + * @see {@link Circle.copyTo} For copying to another circle + */ + clone() { + return new Circle(this.x, this.y, this.radius); + } + /** + * Checks whether the x and y coordinates given are contained within this circle. + * + * Uses the distance formula to determine if a point is inside the circle's radius. + * + * Commonly used for hit testing in PixiJS events and graphics. + * @example + * ```ts + * // Basic containment check + * const circle = new Circle(100, 100, 50); + * const isInside = circle.contains(120, 120); + * + * // Check mouse position + * const circle = new Circle(0, 0, 100); + * container.hitArea = circle; + * container.on('pointermove', (e) => { + * // only called if pointer is within circle + * }); + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coordinates are within this Circle + * @see {@link Circle.strokeContains} For checking stroke intersection + * @see {@link Circle.getBounds} For getting bounding box + */ + contains(x, y) { + if (this.radius <= 0) return false; + const r2 = this.radius * this.radius; + let dx = this.x - x; + let dy = this.y - y; + dx *= dx; + dy *= dy; + return dx + dy <= r2; + } + /** + * Checks whether the x and y coordinates given are contained within this circle including the stroke. + * @example + * ```ts + * // Basic stroke check + * const circle = new Circle(100, 100, 50); + * const isOnStroke = circle.strokeContains(150, 100, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = circle.strokeContains(150, 100, 4, 1); // Inside + * const centerStroke = circle.strokeContains(150, 100, 4, 0.5); // Centered + * const outerStroke = circle.strokeContains(150, 100, 4, 0); // Outside + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @param width - The width of the line to check + * @param alignment - The alignment of the stroke, 0.5 by default + * @returns Whether the x/y coordinates are within this Circle's stroke + * @see {@link Circle.contains} For checking fill containment + * @see {@link Circle.getBounds} For getting stroke bounds + */ + strokeContains(x, y, width, alignment = 0.5) { + if (this.radius === 0) return false; + const dx = this.x - x; + const dy = this.y - y; + const radius = this.radius; + const outerWidth = (1 - alignment) * width; + const distance = Math.sqrt(dx * dx + dy * dy); + return distance <= radius + outerWidth && distance > radius - (width - outerWidth); + } + /** + * Returns the framing rectangle of the circle as a Rectangle object. + * @example + * ```ts + * // Basic bounds calculation + * const circle = new Circle(100, 100, 50); + * const bounds = circle.getBounds(); + * // bounds: x=50, y=50, width=100, height=100 + * + * // Reuse existing rectangle + * const rect = new Rectangle(); + * circle.getBounds(rect); + * ``` + * @param out - Optional Rectangle object to store the result + * @returns The framing rectangle + * @see {@link Rectangle} For rectangle properties + * @see {@link Circle.contains} For point containment + */ + getBounds(out) { + out || (out = new Rectangle()); + out.x = this.x - this.radius; + out.y = this.y - this.radius; + out.width = this.radius * 2; + out.height = this.radius * 2; + return out; + } + /** + * Copies another circle to this one. + * @example + * ```ts + * // Basic copying + * const source = new Circle(100, 100, 50); + * const target = new Circle(); + * target.copyFrom(source); + * ``` + * @param circle - The circle to copy from + * @returns Returns itself + * @see {@link Circle.copyTo} For copying to another circle + * @see {@link Circle.clone} For creating new circle copy + */ + copyFrom(circle) { + this.x = circle.x; + this.y = circle.y; + this.radius = circle.radius; + return this; + } + /** + * Copies this circle to another one. + * @example + * ```ts + * // Basic copying + * const source = new Circle(100, 100, 50); + * const target = new Circle(); + * source.copyTo(target); + * ``` + * @param circle - The circle to copy to + * @returns Returns given parameter + * @see {@link Circle.copyFrom} For copying from another circle + * @see {@link Circle.clone} For creating new circle copy + */ + copyTo(circle) { + circle.copyFrom(this); + return circle; + } + toString() { + return `[pixi.js/math:Circle x=${this.x} y=${this.y} radius=${this.radius}]`; + } + } + + "use strict"; + class Ellipse { + /** + * @param x - The X coordinate of the center of this ellipse + * @param y - The Y coordinate of the center of this ellipse + * @param halfWidth - The half width of this ellipse + * @param halfHeight - The half height of this ellipse + */ + constructor(x = 0, y = 0, halfWidth = 0, halfHeight = 0) { + /** + * The type of the object, mainly used to avoid `instanceof` checks + * @example + * ```ts + * // Check shape type + * const shape = new Ellipse(0, 0, 50, 25); + * console.log(shape.type); // 'ellipse' + * + * // Use in type guards + * if (shape.type === 'ellipse') { + * console.log(shape.halfWidth, shape.halfHeight); + * } + * ``` + * @readonly + * @default 'ellipse' + * @see {@link SHAPE_PRIMITIVE} For all shape types + */ + this.type = "ellipse"; + this.x = x; + this.y = y; + this.halfWidth = halfWidth; + this.halfHeight = halfHeight; + } + /** + * Creates a clone of this Ellipse instance. + * @example + * ```ts + * // Basic cloning + * const original = new Ellipse(100, 100, 50, 25); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.halfWidth *= 2; + * modified.halfHeight *= 2; + * + * // Verify independence + * console.log(original.halfWidth); // 50 + * console.log(modified.halfWidth); // 100 + * ``` + * @returns A copy of the ellipse + * @see {@link Ellipse.copyFrom} For copying into existing ellipse + * @see {@link Ellipse.copyTo} For copying to another ellipse + */ + clone() { + return new Ellipse(this.x, this.y, this.halfWidth, this.halfHeight); + } + /** + * Checks whether the x and y coordinates given are contained within this ellipse. + * Uses normalized coordinates and the ellipse equation to determine containment. + * @example + * ```ts + * // Basic containment check + * const ellipse = new Ellipse(100, 100, 50, 25); + * const isInside = ellipse.contains(120, 110); + * ``` + * @remarks + * - Uses ellipse equation (x²/a² + y²/b² ≤ 1) + * - Returns false if dimensions are 0 or negative + * - Normalized to center (0,0) for calculation + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coords are within this ellipse + * @see {@link Ellipse.strokeContains} For checking stroke intersection + * @see {@link Ellipse.getBounds} For getting containing rectangle + */ + contains(x, y) { + if (this.halfWidth <= 0 || this.halfHeight <= 0) { + return false; + } + let normx = (x - this.x) / this.halfWidth; + let normy = (y - this.y) / this.halfHeight; + normx *= normx; + normy *= normy; + return normx + normy <= 1; + } + /** + * Checks whether the x and y coordinates given are contained within this ellipse including stroke. + * @example + * ```ts + * // Basic stroke check + * const ellipse = new Ellipse(100, 100, 50, 25); + * const isOnStroke = ellipse.strokeContains(150, 100, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = ellipse.strokeContains(150, 100, 4, 1); // Inside + * const centerStroke = ellipse.strokeContains(150, 100, 4, 0.5); // Centered + * const outerStroke = ellipse.strokeContains(150, 100, 4, 0); // Outside + * ``` + * @remarks + * - Uses normalized ellipse equations + * - Considers stroke alignment + * - Returns false if dimensions are 0 + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @param strokeWidth - The width of the line to check + * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer) + * @returns Whether the x/y coords are within this ellipse's stroke + * @see {@link Ellipse.contains} For checking fill containment + * @see {@link Ellipse.getBounds} For getting stroke bounds + */ + strokeContains(x, y, strokeWidth, alignment = 0.5) { + const { halfWidth, halfHeight } = this; + if (halfWidth <= 0 || halfHeight <= 0) { + return false; + } + const strokeOuterWidth = strokeWidth * (1 - alignment); + const strokeInnerWidth = strokeWidth - strokeOuterWidth; + const innerHorizontal = halfWidth - strokeInnerWidth; + const innerVertical = halfHeight - strokeInnerWidth; + const outerHorizontal = halfWidth + strokeOuterWidth; + const outerVertical = halfHeight + strokeOuterWidth; + const normalizedX = x - this.x; + const normalizedY = y - this.y; + const innerEllipse = normalizedX * normalizedX / (innerHorizontal * innerHorizontal) + normalizedY * normalizedY / (innerVertical * innerVertical); + const outerEllipse = normalizedX * normalizedX / (outerHorizontal * outerHorizontal) + normalizedY * normalizedY / (outerVertical * outerVertical); + return innerEllipse > 1 && outerEllipse <= 1; + } + /** + * Returns the framing rectangle of the ellipse as a Rectangle object. + * @example + * ```ts + * // Basic bounds calculation + * const ellipse = new Ellipse(100, 100, 50, 25); + * const bounds = ellipse.getBounds(); + * // bounds: x=50, y=75, width=100, height=50 + * + * // Reuse existing rectangle + * const rect = new Rectangle(); + * ellipse.getBounds(rect); + * ``` + * @remarks + * - Creates Rectangle if none provided + * - Top-left is (x-halfWidth, y-halfHeight) + * - Width is halfWidth * 2 + * - Height is halfHeight * 2 + * @param out - Optional Rectangle object to store the result + * @returns The framing rectangle + * @see {@link Rectangle} For rectangle properties + * @see {@link Ellipse.contains} For checking if a point is inside + */ + getBounds(out) { + out || (out = new Rectangle()); + out.x = this.x - this.halfWidth; + out.y = this.y - this.halfHeight; + out.width = this.halfWidth * 2; + out.height = this.halfHeight * 2; + return out; + } + /** + * Copies another ellipse to this one. + * @example + * ```ts + * // Basic copying + * const source = new Ellipse(100, 100, 50, 25); + * const target = new Ellipse(); + * target.copyFrom(source); + * ``` + * @param ellipse - The ellipse to copy from + * @returns Returns itself + * @see {@link Ellipse.copyTo} For copying to another ellipse + * @see {@link Ellipse.clone} For creating new ellipse copy + */ + copyFrom(ellipse) { + this.x = ellipse.x; + this.y = ellipse.y; + this.halfWidth = ellipse.halfWidth; + this.halfHeight = ellipse.halfHeight; + return this; + } + /** + * Copies this ellipse to another one. + * @example + * ```ts + * // Basic copying + * const source = new Ellipse(100, 100, 50, 25); + * const target = new Ellipse(); + * source.copyTo(target); + * ``` + * @param ellipse - The ellipse to copy to + * @returns Returns given parameter + * @see {@link Ellipse.copyFrom} For copying from another ellipse + * @see {@link Ellipse.clone} For creating new ellipse copy + */ + copyTo(ellipse) { + ellipse.copyFrom(this); + return ellipse; + } + toString() { + return `[pixi.js/math:Ellipse x=${this.x} y=${this.y} halfWidth=${this.halfWidth} halfHeight=${this.halfHeight}]`; + } + } + + "use strict"; + let tempRect$4; + let tempRect2; + class Polygon { + /** + * @param points - This can be an array of Points + * that form the polygon, a flat array of numbers that will be interpreted as [x,y, x,y, ...], or + * the arguments passed can be all the points of the polygon e.g. + * `new Polygon(new Point(), new Point(), ...)`, or the arguments passed can be flat + * x,y values e.g. `new Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers. + */ + constructor(...points) { + /** + * The type of the object, mainly used to avoid `instanceof` checks + * @example + * ```ts + * // Check shape type + * const shape = new Polygon([0, 0, 100, 0, 50, 100]); + * console.log(shape.type); // 'polygon' + * + * // Use in type guards + * if (shape.type === 'polygon') { + * // TypeScript knows this is a Polygon + * console.log(shape.points.length); + * } + * ``` + * @readonly + * @default 'polygon' + * @see {@link SHAPE_PRIMITIVE} For all shape types + */ + this.type = "polygon"; + let flat = Array.isArray(points[0]) ? points[0] : points; + if (typeof flat[0] !== "number") { + const p = []; + for (let i = 0, il = flat.length; i < il; i++) { + p.push(flat[i].x, flat[i].y); + } + flat = p; + } + this.points = flat; + this.closePath = true; + } + /** + * Determines whether the polygon's points are arranged in a clockwise direction. + * Uses the shoelace formula (surveyor's formula) to calculate the signed area. + * + * A positive area indicates clockwise winding, while negative indicates counter-clockwise. + * + * The formula sums up the cross products of adjacent vertices: + * For each pair of adjacent points (x1,y1) and (x2,y2), we calculate (x1*y2 - x2*y1) + * The final sum divided by 2 gives the signed area - positive for clockwise. + * @example + * ```ts + * // Check polygon winding + * const polygon = new Polygon([0, 0, 100, 0, 50, 100]); + * console.log(polygon.isClockwise()); // Check direction + * + * // Use in path construction + * const hole = new Polygon([25, 25, 75, 25, 75, 75, 25, 75]); + * if (hole.isClockwise() === shape.isClockwise()) { + * hole.points.reverse(); // Reverse for proper hole winding + * } + * ``` + * @returns `true` if the polygon's points are arranged clockwise, `false` if counter-clockwise + */ + isClockwise() { + let area = 0; + const points = this.points; + const length = points.length; + for (let i = 0; i < length; i += 2) { + const x1 = points[i]; + const y1 = points[i + 1]; + const x2 = points[(i + 2) % length]; + const y2 = points[(i + 3) % length]; + area += (x2 - x1) * (y2 + y1); + } + return area < 0; + } + /** + * Checks if this polygon completely contains another polygon. + * Used for detecting holes in shapes, like when parsing SVG paths. + * @example + * ```ts + * // Basic containment check + * const outerSquare = new Polygon([0,0, 100,0, 100,100, 0,100]); // A square + * const innerSquare = new Polygon([25,25, 75,25, 75,75, 25,75]); // A smaller square inside + * + * outerSquare.containsPolygon(innerSquare); // Returns true + * innerSquare.containsPolygon(outerSquare); // Returns false + * ``` + * @remarks + * - Uses bounds check for quick rejection + * - Tests all points for containment + * @param polygon - The polygon to test for containment + * @returns True if this polygon completely contains the other polygon + * @see {@link Polygon.contains} For single point testing + * @see {@link Polygon.getBounds} For bounds calculation + */ + containsPolygon(polygon) { + const thisBounds = this.getBounds(tempRect$4); + const otherBounds = polygon.getBounds(tempRect2); + if (!thisBounds.containsRect(otherBounds)) { + return false; + } + const points = polygon.points; + for (let i = 0; i < points.length; i += 2) { + const x = points[i]; + const y = points[i + 1]; + if (!this.contains(x, y)) { + return false; + } + } + return true; + } + /** + * Creates a clone of this polygon. + * @example + * ```ts + * // Basic cloning + * const original = new Polygon([0, 0, 100, 0, 50, 100]); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.points[0] = 10; // Modify first x coordinate + * ``` + * @returns A copy of the polygon + * @see {@link Polygon.copyFrom} For copying into existing polygon + * @see {@link Polygon.copyTo} For copying to another polygon + */ + clone() { + const points = this.points.slice(); + const polygon = new Polygon(points); + polygon.closePath = this.closePath; + return polygon; + } + /** + * Checks whether the x and y coordinates passed to this function are contained within this polygon. + * Uses raycasting algorithm for point-in-polygon testing. + * @example + * ```ts + * // Basic containment check + * const polygon = new Polygon([0, 0, 100, 0, 50, 100]); + * const isInside = polygon.contains(25, 25); // true + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coordinates are within this polygon + * @see {@link Polygon.strokeContains} For checking stroke intersection + * @see {@link Polygon.containsPolygon} For polygon-in-polygon testing + */ + contains(x, y) { + let inside = false; + const length = this.points.length / 2; + for (let i = 0, j = length - 1; i < length; j = i++) { + const xi = this.points[i * 2]; + const yi = this.points[i * 2 + 1]; + const xj = this.points[j * 2]; + const yj = this.points[j * 2 + 1]; + const intersect = yi > y !== yj > y && x < (xj - xi) * ((y - yi) / (yj - yi)) + xi; + if (intersect) { + inside = !inside; + } + } + return inside; + } + /** + * Checks whether the x and y coordinates given are contained within this polygon including the stroke. + * @example + * ```ts + * // Basic stroke check + * const polygon = new Polygon([0, 0, 100, 0, 50, 100]); + * const isOnStroke = polygon.strokeContains(25, 25, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = polygon.strokeContains(25, 25, 4, 1); // Inside + * const centerStroke = polygon.strokeContains(25, 25, 4, 0.5); // Centered + * const outerStroke = polygon.strokeContains(25, 25, 4, 0); // Outside + * ``` + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @param strokeWidth - The width of the line to check + * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer) + * @returns Whether the x/y coordinates are within this polygon's stroke + * @see {@link Polygon.contains} For checking fill containment + * @see {@link Polygon.getBounds} For getting stroke bounds + */ + strokeContains(x, y, strokeWidth, alignment = 0.5) { + const strokeWidthSquared = strokeWidth * strokeWidth; + const rightWidthSquared = strokeWidthSquared * (1 - alignment); + const leftWidthSquared = strokeWidthSquared - rightWidthSquared; + const { points } = this; + const iterationLength = points.length - (this.closePath ? 0 : 2); + for (let i = 0; i < iterationLength; i += 2) { + const x1 = points[i]; + const y1 = points[i + 1]; + const x2 = points[(i + 2) % points.length]; + const y2 = points[(i + 3) % points.length]; + const distanceSquared = squaredDistanceToLineSegment(x, y, x1, y1, x2, y2); + const sign = Math.sign((x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)); + if (distanceSquared <= (sign < 0 ? leftWidthSquared : rightWidthSquared)) { + return true; + } + } + return false; + } + /** + * Returns the framing rectangle of the polygon as a Rectangle object. + * @example + * ```ts + * // Basic bounds calculation + * const polygon = new Polygon([0, 0, 100, 0, 50, 100]); + * const bounds = polygon.getBounds(); + * // bounds: x=0, y=0, width=100, height=100 + * + * // Reuse existing rectangle + * const rect = new Rectangle(); + * polygon.getBounds(rect); + * ``` + * @param out - Optional rectangle to store the result + * @returns The framing rectangle + * @see {@link Rectangle} For rectangle properties + * @see {@link Polygon.contains} For checking if a point is inside + */ + getBounds(out) { + out || (out = new Rectangle()); + const points = this.points; + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + for (let i = 0, n = points.length; i < n; i += 2) { + const x = points[i]; + const y = points[i + 1]; + minX = x < minX ? x : minX; + maxX = x > maxX ? x : maxX; + minY = y < minY ? y : minY; + maxY = y > maxY ? y : maxY; + } + out.x = minX; + out.width = maxX - minX; + out.y = minY; + out.height = maxY - minY; + return out; + } + /** + * Copies another polygon to this one. + * @example + * ```ts + * // Basic copying + * const source = new Polygon([0, 0, 100, 0, 50, 100]); + * const target = new Polygon(); + * target.copyFrom(source); + * ``` + * @param polygon - The polygon to copy from + * @returns Returns itself + * @see {@link Polygon.copyTo} For copying to another polygon + * @see {@link Polygon.clone} For creating new polygon copy + */ + copyFrom(polygon) { + this.points = polygon.points.slice(); + this.closePath = polygon.closePath; + return this; + } + /** + * Copies this polygon to another one. + * @example + * ```ts + * // Basic copying + * const source = new Polygon([0, 0, 100, 0, 50, 100]); + * const target = new Polygon(); + * source.copyTo(target); + * ``` + * @param polygon - The polygon to copy to + * @returns Returns given parameter + * @see {@link Polygon.copyFrom} For copying from another polygon + * @see {@link Polygon.clone} For creating new polygon copy + */ + copyTo(polygon) { + polygon.copyFrom(this); + return polygon; + } + toString() { + return `[pixi.js/math:PolygoncloseStroke=${this.closePath}points=${this.points.reduce((pointsDesc, currentPoint) => `${pointsDesc}, ${currentPoint}`, "")}]`; + } + /** + * Get the last X coordinate of the polygon. + * @example + * ```ts + * // Basic coordinate access + * const polygon = new Polygon([0, 0, 100, 200, 300, 400]); + * console.log(polygon.lastX); // 300 + * ``` + * @readonly + * @returns The x-coordinate of the last vertex + * @see {@link Polygon.lastY} For last Y coordinate + * @see {@link Polygon.points} For raw points array + */ + get lastX() { + return this.points[this.points.length - 2]; + } + /** + * Get the last Y coordinate of the polygon. + * @example + * ```ts + * // Basic coordinate access + * const polygon = new Polygon([0, 0, 100, 200, 300, 400]); + * console.log(polygon.lastY); // 400 + * ``` + * @readonly + * @returns The y-coordinate of the last vertex + * @see {@link Polygon.lastX} For last X coordinate + * @see {@link Polygon.points} For raw points array + */ + get lastY() { + return this.points[this.points.length - 1]; + } + /** + * Get the last X coordinate of the polygon. + * @readonly + * @deprecated since 8.11.0, use {@link Polygon.lastX} instead. + */ + get x() { + deprecation("8.11.0", "Polygon.lastX is deprecated, please use Polygon.lastX instead."); + return this.points[this.points.length - 2]; + } + /** + * Get the last Y coordinate of the polygon. + * @readonly + * @deprecated since 8.11.0, use {@link Polygon.lastY} instead. + */ + get y() { + deprecation("8.11.0", "Polygon.y is deprecated, please use Polygon.lastY instead."); + return this.points[this.points.length - 1]; + } + /** + * Get the first X coordinate of the polygon. + * @example + * ```ts + * // Basic coordinate access + * const polygon = new Polygon([0, 0, 100, 200, 300, 400]); + * console.log(polygon.x); // 0 + * ``` + * @readonly + * @returns The x-coordinate of the first vertex + * @see {@link Polygon.startY} For first Y coordinate + * @see {@link Polygon.points} For raw points array + */ + get startX() { + return this.points[0]; + } + /** + * Get the first Y coordinate of the polygon. + * @example + * ```ts + * // Basic coordinate access + * const polygon = new Polygon([0, 0, 100, 200, 300, 400]); + * console.log(polygon.y); // 0 + * ``` + * @readonly + * @returns The y-coordinate of the first vertex + * @see {@link Polygon.startX} For first X coordinate + * @see {@link Polygon.points} For raw points array + */ + get startY() { + return this.points[1]; + } + } + + "use strict"; + const isCornerWithinStroke = (pX, pY, cornerX, cornerY, radius, strokeWidthInner, strokeWidthOuter) => { + const dx = pX - cornerX; + const dy = pY - cornerY; + const distance = Math.sqrt(dx * dx + dy * dy); + return distance >= radius - strokeWidthInner && distance <= radius + strokeWidthOuter; + }; + class RoundedRectangle { + /** + * @param x - The X coordinate of the upper-left corner of the rounded rectangle + * @param y - The Y coordinate of the upper-left corner of the rounded rectangle + * @param width - The overall width of this rounded rectangle + * @param height - The overall height of this rounded rectangle + * @param radius - Controls the radius of the rounded corners + */ + constructor(x = 0, y = 0, width = 0, height = 0, radius = 20) { + /** + * The type of the object, mainly used to avoid `instanceof` checks + * @example + * ```ts + * // Check shape type + * const shape = new RoundedRectangle(0, 0, 100, 100, 20); + * console.log(shape.type); // 'roundedRectangle' + * + * // Use in type guards + * if (shape.type === 'roundedRectangle') { + * console.log(shape.radius); + * } + * ``` + * @readonly + * @default 'roundedRectangle' + * @see {@link SHAPE_PRIMITIVE} For all shape types + */ + this.type = "roundedRectangle"; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.radius = radius; + } + /** + * Returns the framing rectangle of the rounded rectangle as a Rectangle object + * @example + * ```ts + * // Basic bounds calculation + * const rect = new RoundedRectangle(100, 100, 200, 150, 20); + * const bounds = rect.getBounds(); + * // bounds: x=100, y=100, width=200, height=150 + * + * // Reuse existing rectangle + * const out = new Rectangle(); + * rect.getBounds(out); + * ``` + * @remarks + * - Rectangle matches outer dimensions + * - Ignores corner radius + * @param out - Optional rectangle to store the result + * @returns The framing rectangle + * @see {@link Rectangle} For rectangle properties + * @see {@link RoundedRectangle.contains} For checking if a point is inside + */ + getBounds(out) { + out || (out = new Rectangle()); + out.x = this.x; + out.y = this.y; + out.width = this.width; + out.height = this.height; + return out; + } + /** + * Creates a clone of this Rounded Rectangle. + * @example + * ```ts + * // Basic cloning + * const original = new RoundedRectangle(100, 100, 200, 150, 20); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.radius = 30; + * modified.width *= 2; + * + * // Verify independence + * console.log(original.radius); // 20 + * console.log(modified.radius); // 30 + * ``` + * @returns A copy of the rounded rectangle + * @see {@link RoundedRectangle.copyFrom} For copying into existing rectangle + * @see {@link RoundedRectangle.copyTo} For copying to another rectangle + */ + clone() { + return new RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); + } + /** + * Copies another rectangle to this one. + * @example + * ```ts + * // Basic copying + * const source = new RoundedRectangle(100, 100, 200, 150, 20); + * const target = new RoundedRectangle(); + * target.copyFrom(source); + * + * // Chain with other operations + * const rect = new RoundedRectangle() + * .copyFrom(source) + * .getBounds(rect); + * ``` + * @param rectangle - The rectangle to copy from + * @returns Returns itself + * @see {@link RoundedRectangle.copyTo} For copying to another rectangle + * @see {@link RoundedRectangle.clone} For creating new rectangle copy + */ + copyFrom(rectangle) { + this.x = rectangle.x; + this.y = rectangle.y; + this.width = rectangle.width; + this.height = rectangle.height; + return this; + } + /** + * Copies this rectangle to another one. + * @example + * ```ts + * // Basic copying + * const source = new RoundedRectangle(100, 100, 200, 150, 20); + * const target = new RoundedRectangle(); + * source.copyTo(target); + * + * // Chain with other operations + * const result = source + * .copyTo(new RoundedRectangle()) + * .getBounds(); + * ``` + * @param rectangle - The rectangle to copy to + * @returns Returns given parameter + * @see {@link RoundedRectangle.copyFrom} For copying from another rectangle + * @see {@link RoundedRectangle.clone} For creating new rectangle copy + */ + copyTo(rectangle) { + rectangle.copyFrom(this); + return rectangle; + } + /** + * Checks whether the x and y coordinates given are contained within this Rounded Rectangle + * @example + * ```ts + * // Basic containment check + * const rect = new RoundedRectangle(100, 100, 200, 150, 20); + * const isInside = rect.contains(150, 125); // true + * // Check corner radius + * const corner = rect.contains(100, 100); // false if within corner curve + * ``` + * @remarks + * - Returns false if width/height is 0 or negative + * - Handles rounded corners with radius check + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coordinates are within this Rounded Rectangle + * @see {@link RoundedRectangle.strokeContains} For checking stroke intersection + * @see {@link RoundedRectangle.getBounds} For getting containing rectangle + */ + contains(x, y) { + if (this.width <= 0 || this.height <= 0) { + return false; + } + if (x >= this.x && x <= this.x + this.width) { + if (y >= this.y && y <= this.y + this.height) { + const radius = Math.max(0, Math.min(this.radius, Math.min(this.width, this.height) / 2)); + if (y >= this.y + radius && y <= this.y + this.height - radius || x >= this.x + radius && x <= this.x + this.width - radius) { + return true; + } + let dx = x - (this.x + radius); + let dy = y - (this.y + radius); + const radius2 = radius * radius; + if (dx * dx + dy * dy <= radius2) { + return true; + } + dx = x - (this.x + this.width - radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + dy = y - (this.y + this.height - radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + dx = x - (this.x + radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + } + } + return false; + } + /** + * Checks whether the x and y coordinates given are contained within this rectangle including the stroke. + * @example + * ```ts + * // Basic stroke check + * const rect = new RoundedRectangle(100, 100, 200, 150, 20); + * const isOnStroke = rect.strokeContains(150, 100, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = rect.strokeContains(150, 100, 4, 1); // Inside + * const centerStroke = rect.strokeContains(150, 100, 4, 0.5); // Centered + * const outerStroke = rect.strokeContains(150, 100, 4, 0); // Outside + * ``` + * @param pX - The X coordinate of the point to test + * @param pY - The Y coordinate of the point to test + * @param strokeWidth - The width of the line to check + * @param alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer) + * @returns Whether the x/y coordinates are within this rectangle's stroke + * @see {@link RoundedRectangle.contains} For checking fill containment + * @see {@link RoundedRectangle.getBounds} For getting stroke bounds + */ + strokeContains(pX, pY, strokeWidth, alignment = 0.5) { + const { x, y, width, height, radius } = this; + const strokeWidthOuter = strokeWidth * (1 - alignment); + const strokeWidthInner = strokeWidth - strokeWidthOuter; + const innerX = x + radius; + const innerY = y + radius; + const innerWidth = width - radius * 2; + const innerHeight = height - radius * 2; + const rightBound = x + width; + const bottomBound = y + height; + if ((pX >= x - strokeWidthOuter && pX <= x + strokeWidthInner || pX >= rightBound - strokeWidthInner && pX <= rightBound + strokeWidthOuter) && pY >= innerY && pY <= innerY + innerHeight) { + return true; + } + if ((pY >= y - strokeWidthOuter && pY <= y + strokeWidthInner || pY >= bottomBound - strokeWidthInner && pY <= bottomBound + strokeWidthOuter) && pX >= innerX && pX <= innerX + innerWidth) { + return true; + } + return ( + // Top-left + pX < innerX && pY < innerY && isCornerWithinStroke( + pX, + pY, + innerX, + innerY, + radius, + strokeWidthInner, + strokeWidthOuter + ) || pX > rightBound - radius && pY < innerY && isCornerWithinStroke( + pX, + pY, + rightBound - radius, + innerY, + radius, + strokeWidthInner, + strokeWidthOuter + ) || pX > rightBound - radius && pY > bottomBound - radius && isCornerWithinStroke( + pX, + pY, + rightBound - radius, + bottomBound - radius, + radius, + strokeWidthInner, + strokeWidthOuter + ) || pX < innerX && pY > bottomBound - radius && isCornerWithinStroke( + pX, + pY, + innerX, + bottomBound - radius, + radius, + strokeWidthInner, + strokeWidthOuter + ) + ); + } + toString() { + return `[pixi.js/math:RoundedRectangle x=${this.x} y=${this.y}width=${this.width} height=${this.height} radius=${this.radius}]`; + } + } + + "use strict"; + + "use strict"; + class Triangle { + /** + * @param x - The X coord of the first point. + * @param y - The Y coord of the first point. + * @param x2 - The X coord of the second point. + * @param y2 - The Y coord of the second point. + * @param x3 - The X coord of the third point. + * @param y3 - The Y coord of the third point. + */ + constructor(x = 0, y = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0) { + /** + * The type of the object, mainly used to avoid `instanceof` checks + * @example + * ```ts + * // Check shape type + * const shape = new Triangle(0, 0, 100, 0, 50, 100); + * console.log(shape.type); // 'triangle' + * + * // Use in type guards + * if (shape.type === 'triangle') { + * console.log(shape.x2, shape.y2); + * } + * ``` + * @readonly + * @default 'triangle' + * @see {@link SHAPE_PRIMITIVE} For all shape types + */ + this.type = "triangle"; + this.x = x; + this.y = y; + this.x2 = x2; + this.y2 = y2; + this.x3 = x3; + this.y3 = y3; + } + /** + * Checks whether the x and y coordinates given are contained within this triangle + * @example + * ```ts + * // Basic containment check + * const triangle = new Triangle(0, 0, 100, 0, 50, 100); + * const isInside = triangle.contains(25, 25); // true + * ``` + * @remarks + * - Uses barycentric coordinate system + * - Works with any triangle shape + * @param x - The X coordinate of the point to test + * @param y - The Y coordinate of the point to test + * @returns Whether the x/y coordinates are within this Triangle + * @see {@link Triangle.strokeContains} For checking stroke intersection + * @see {@link Triangle.getBounds} For getting containing rectangle + */ + contains(x, y) { + const s = (this.x - this.x3) * (y - this.y3) - (this.y - this.y3) * (x - this.x3); + const t = (this.x2 - this.x) * (y - this.y) - (this.y2 - this.y) * (x - this.x); + if (s < 0 !== t < 0 && s !== 0 && t !== 0) { + return false; + } + const d = (this.x3 - this.x2) * (y - this.y2) - (this.y3 - this.y2) * (x - this.x2); + return d === 0 || d < 0 === s + t <= 0; + } + /** + * Checks whether the x and y coordinates given are contained within this triangle including the stroke. + * @example + * ```ts + * // Basic stroke check + * const triangle = new Triangle(0, 0, 100, 0, 50, 100); + * const isOnStroke = triangle.strokeContains(25, 25, 4); // 4px line width + * + * // Check with different alignments + * const innerStroke = triangle.strokeContains(25, 25, 4, 1); // Inside + * const centerStroke = triangle.strokeContains(25, 25, 4, 0.5); // Centered + * const outerStroke = triangle.strokeContains(25, 25, 4, 0); // Outside + * ``` + * @param pointX - The X coordinate of the point to test + * @param pointY - The Y coordinate of the point to test + * @param strokeWidth - The width of the line to check + * @param _alignment - The alignment of the stroke (1 = inner, 0.5 = centered, 0 = outer) + * @returns Whether the x/y coordinates are within this triangle's stroke + * @see {@link Triangle.contains} For checking fill containment + * @see {@link Triangle.getBounds} For getting stroke bounds + */ + strokeContains(pointX, pointY, strokeWidth, _alignment = 0.5) { + const halfStrokeWidth = strokeWidth / 2; + const halfStrokeWidthSquared = halfStrokeWidth * halfStrokeWidth; + const { x, x2, x3, y, y2, y3 } = this; + if (squaredDistanceToLineSegment(pointX, pointY, x, y, x2, y3) <= halfStrokeWidthSquared || squaredDistanceToLineSegment(pointX, pointY, x2, y2, x3, y3) <= halfStrokeWidthSquared || squaredDistanceToLineSegment(pointX, pointY, x3, y3, x, y) <= halfStrokeWidthSquared) { + return true; + } + return false; + } + /** + * Creates a clone of this Triangle + * @example + * ```ts + * // Basic cloning + * const original = new Triangle(0, 0, 100, 0, 50, 100); + * const copy = original.clone(); + * + * // Clone and modify + * const modified = original.clone(); + * modified.x3 = 75; + * modified.y3 = 150; + * + * // Verify independence + * console.log(original.y3); // 100 + * console.log(modified.y3); // 150 + * ``` + * @returns A copy of the triangle + * @see {@link Triangle.copyFrom} For copying into existing triangle + * @see {@link Triangle.copyTo} For copying to another triangle + */ + clone() { + const triangle = new Triangle( + this.x, + this.y, + this.x2, + this.y2, + this.x3, + this.y3 + ); + return triangle; + } + /** + * Copies another triangle to this one. + * @example + * ```ts + * // Basic copying + * const source = new Triangle(0, 0, 100, 0, 50, 100); + * const target = new Triangle(); + * target.copyFrom(source); + * + * // Chain with other operations + * const triangle = new Triangle() + * .copyFrom(source) + * .getBounds(rect); + * ``` + * @param triangle - The triangle to copy from + * @returns Returns itself + * @see {@link Triangle.copyTo} For copying to another triangle + * @see {@link Triangle.clone} For creating new triangle copy + */ + copyFrom(triangle) { + this.x = triangle.x; + this.y = triangle.y; + this.x2 = triangle.x2; + this.y2 = triangle.y2; + this.x3 = triangle.x3; + this.y3 = triangle.y3; + return this; + } + /** + * Copies this triangle to another one. + * @example + * ```ts + * // Basic copying + * const source = new Triangle(0, 0, 100, 0, 50, 100); + * const target = new Triangle(); + * source.copyTo(target); + * + * // Chain with other operations + * const result = source + * .copyTo(new Triangle()) + * .getBounds(); + * ``` + * @remarks + * - Updates target triangle values + * - Copies all point coordinates + * - Returns target for chaining + * - More efficient than clone() + * @param triangle - The triangle to copy to + * @returns Returns given parameter + * @see {@link Triangle.copyFrom} For copying from another triangle + * @see {@link Triangle.clone} For creating new triangle copy + */ + copyTo(triangle) { + triangle.copyFrom(this); + return triangle; + } + /** + * Returns the framing rectangle of the triangle as a Rectangle object + * @example + * ```ts + * // Basic bounds calculation + * const triangle = new Triangle(0, 0, 100, 0, 50, 100); + * const bounds = triangle.getBounds(); + * // bounds: x=0, y=0, width=100, height=100 + * + * // Reuse existing rectangle + * const rect = new Rectangle(); + * triangle.getBounds(rect); + * ``` + * @param out - Optional rectangle to store the result + * @returns The framing rectangle + * @see {@link Rectangle} For rectangle properties + * @see {@link Triangle.contains} For checking if a point is inside + */ + getBounds(out) { + out || (out = new Rectangle()); + const minX = Math.min(this.x, this.x2, this.x3); + const maxX = Math.max(this.x, this.x2, this.x3); + const minY = Math.min(this.y, this.y2, this.y3); + const maxY = Math.max(this.y, this.y2, this.y3); + out.x = minX; + out.y = minY; + out.width = maxX - minX; + out.height = maxY - minY; + return out; + } + } + + "use strict"; + + "use strict"; + const tempProjectionMatrix = new Matrix(); + function getGlobalRenderableBounds(renderables, bounds) { + var _a; + bounds.clear(); + const actualMatrix = bounds.matrix; + for (let i = 0; i < renderables.length; i++) { + const renderable = renderables[i]; + if (renderable.globalDisplayStatus < 7) { + continue; + } + const renderGroup = (_a = renderable.renderGroup) != null ? _a : renderable.parentRenderGroup; + if (renderGroup == null ? void 0 : renderGroup.isCachedAsTexture) { + bounds.matrix = tempProjectionMatrix.copyFrom(renderGroup.textureOffsetInverseTransform).append(renderable.worldTransform); + } else if (renderGroup == null ? void 0 : renderGroup._parentCacheAsTextureRenderGroup) { + bounds.matrix = tempProjectionMatrix.copyFrom(renderGroup._parentCacheAsTextureRenderGroup.inverseWorldTransform).append(renderable.groupTransform); + } else { + bounds.matrix = renderable.worldTransform; + } + bounds.addBounds(renderable.bounds); + } + bounds.matrix = actualMatrix; + return bounds; + } + + "use strict"; + const quadGeometry = new Geometry({ + attributes: { + aPosition: { + buffer: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), + format: "float32x2", + stride: 2 * 4, + offset: 0 + } + }, + indexBuffer: new Uint32Array([0, 1, 2, 0, 2, 3]) + }); + class FilterData { + constructor() { + /** + * Indicates whether the filter should be skipped. + * @type {boolean} + */ + this.skip = false; + /** + * The texture to which the filter is applied. + * @type {Texture} + */ + this.inputTexture = null; + /** + * The back texture used for blending, if required. + * @type {Texture | null} + */ + this.backTexture = null; + /** + * The list of filters to be applied. + * @type {Filter[]} + */ + this.filters = null; + /** + * The bounds of the filter area. + * @type {Bounds} + */ + this.bounds = new Bounds(); + /** + * The container to which the filter is applied. + * @type {Container} + */ + this.container = null; + /** + * Indicates whether blending is required for the filter. + * @type {boolean} + */ + this.blendRequired = false; + /** + * The render surface where the output of the filter is rendered. + * @type {RenderSurface} + */ + this.outputRenderSurface = null; + /** + * The global frame of the filter area. + * @type {{ x: number, y: number, width: number, height: number }} + */ + this.globalFrame = { x: 0, y: 0, width: 0, height: 0 }; + /** The first enabled filter index in the current filter list. */ + this.firstEnabledIndex = -1; + /** The last enabled filter index in the current filter list. */ + this.lastEnabledIndex = -1; + } + } + class FilterSystem { + constructor(renderer) { + this._filterStackIndex = 0; + this._filterStack = []; + this._filterGlobalUniforms = new UniformGroup({ + uInputSize: { value: new Float32Array(4), type: "vec4" }, + uInputPixel: { value: new Float32Array(4), type: "vec4" }, + uInputClamp: { value: new Float32Array(4), type: "vec4" }, + uOutputFrame: { value: new Float32Array(4), type: "vec4" }, + uGlobalFrame: { value: new Float32Array(4), type: "vec4" }, + uOutputTexture: { value: new Float32Array(4), type: "vec4" } + }); + this._globalFilterBindGroup = new BindGroup({}); + this.renderer = renderer; + } + /** + * The back texture of the currently active filter. Requires the filter to have `blendRequired` set to true. + * @readonly + */ + get activeBackTexture() { + var _a; + return (_a = this._activeFilterData) == null ? void 0 : _a.backTexture; + } + /** + * Pushes a filter instruction onto the filter stack. + * @param instruction - The instruction containing the filter effect and container. + * @internal + */ + push(instruction) { + const renderer = this.renderer; + const filters = instruction.filterEffect.filters; + const filterData = this._pushFilterData(); + filterData.skip = false; + filterData.filters = filters; + filterData.container = instruction.container; + filterData.outputRenderSurface = renderer.renderTarget.renderSurface; + const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source; + const rootResolution = colorTextureSource.resolution; + const rootAntialias = colorTextureSource.antialias; + if (filters.every((filter) => !filter.enabled)) { + filterData.skip = true; + return; + } + const bounds = filterData.bounds; + this._calculateFilterArea(instruction, bounds); + this._calculateFilterBounds(filterData, renderer.renderTarget.rootViewPort, rootAntialias, rootResolution, 1); + if (filterData.skip) { + return; + } + const previousFilterData = this._getPreviousFilterData(); + const globalResolution = this._findFilterResolution(rootResolution); + let offsetX = 0; + let offsetY = 0; + if (previousFilterData) { + offsetX = previousFilterData.bounds.minX; + offsetY = previousFilterData.bounds.minY; + } + this._calculateGlobalFrame( + filterData, + offsetX, + offsetY, + globalResolution, + colorTextureSource.width, + colorTextureSource.height + ); + this._setupFilterTextures(filterData, bounds, renderer, previousFilterData); + } + /** + * Applies filters to a texture. + * + * This method takes a texture and a list of filters, applies the filters to the texture, + * and returns the resulting texture. + * @param {object} params - The parameters for applying filters. + * @param {Texture} params.texture - The texture to apply filters to. + * @param {Filter[]} params.filters - The filters to apply. + * @returns {Texture} The resulting texture after all filters have been applied. + * @example + * + * ```ts + * // Create a texture and a list of filters + * const texture = new Texture(...); + * const filters = [new BlurFilter(), new ColorMatrixFilter()]; + * + * // Apply the filters to the texture + * const resultTexture = filterSystem.applyToTexture({ texture, filters }); + * + * // Use the resulting texture + * sprite.texture = resultTexture; + * ``` + * + * Key Points: + * 1. padding is not currently supported here - so clipping may occur with filters that use padding. + * 2. If all filters are disabled or skipped, the original texture is returned. + */ + generateFilteredTexture({ texture, filters }) { + const filterData = this._pushFilterData(); + this._activeFilterData = filterData; + filterData.skip = false; + filterData.filters = filters; + const colorTextureSource = texture.source; + const rootResolution = colorTextureSource.resolution; + const rootAntialias = colorTextureSource.antialias; + if (filters.every((filter) => !filter.enabled)) { + filterData.skip = true; + return texture; + } + const bounds = filterData.bounds; + bounds.addRect(texture.frame); + this._calculateFilterBounds(filterData, bounds.rectangle, rootAntialias, rootResolution, 0); + if (filterData.skip) { + return texture; + } + const globalResolution = rootResolution; + const offsetX = 0; + const offsetY = 0; + this._calculateGlobalFrame( + filterData, + offsetX, + offsetY, + globalResolution, + colorTextureSource.width, + colorTextureSource.height + ); + filterData.outputRenderSurface = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + filterData.resolution, + filterData.antialias + ); + filterData.backTexture = Texture.EMPTY; + filterData.inputTexture = texture; + const renderer = this.renderer; + renderer.renderTarget.finishRenderPass(); + this._applyFiltersToTexture(filterData, true); + const outputTexture = filterData.outputRenderSurface; + outputTexture.source.alphaMode = "premultiplied-alpha"; + return outputTexture; + } + /** @internal */ + pop() { + const renderer = this.renderer; + const filterData = this._popFilterData(); + if (filterData.skip) { + return; + } + renderer.globalUniforms.pop(); + renderer.renderTarget.finishRenderPass(); + this._activeFilterData = filterData; + this._applyFiltersToTexture(filterData, false); + if (filterData.blendRequired) { + TexturePool.returnTexture(filterData.backTexture); + } + TexturePool.returnTexture(filterData.inputTexture); + } + /** + * Copies the last render surface to a texture. + * @param lastRenderSurface - The last render surface to copy from. + * @param bounds - The bounds of the area to copy. + * @param previousBounds - The previous bounds to use for offsetting the copy. + */ + getBackTexture(lastRenderSurface, bounds, previousBounds) { + const backgroundResolution = lastRenderSurface.colorTexture.source._resolution; + const backTexture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + backgroundResolution, + false + ); + let x = bounds.minX; + let y = bounds.minY; + if (previousBounds) { + x -= previousBounds.minX; + y -= previousBounds.minY; + } + x = Math.floor(x * backgroundResolution); + y = Math.floor(y * backgroundResolution); + const width = Math.ceil(bounds.width * backgroundResolution); + const height = Math.ceil(bounds.height * backgroundResolution); + this.renderer.renderTarget.copyToTexture( + lastRenderSurface, + backTexture, + { x, y }, + { width, height }, + { x: 0, y: 0 } + ); + return backTexture; + } + /** + * Applies a filter to a texture. + * @param filter - The filter to apply. + * @param input - The input texture. + * @param output - The output render surface. + * @param clear - Whether to clear the output surface before applying the filter. + */ + applyFilter(filter, input, output, clear) { + const renderer = this.renderer; + const filterData = this._activeFilterData; + const outputRenderSurface = filterData.outputRenderSurface; + const isFinalTarget = outputRenderSurface === output; + const rootResolution = renderer.renderTarget.rootRenderTarget.colorTexture.source._resolution; + const resolution = this._findFilterResolution(rootResolution); + let offsetX = 0; + let offsetY = 0; + if (isFinalTarget) { + const offset = this._findPreviousFilterOffset(); + offsetX = offset.x; + offsetY = offset.y; + } + this._updateFilterUniforms(input, output, filterData, offsetX, offsetY, resolution, isFinalTarget, clear); + const filterToApply = filter.enabled ? filter : this._getPassthroughFilter(); + this._setupBindGroupsAndRender(filterToApply, input, renderer); + } + /** + * Multiply _input normalized coordinates_ to this matrix to get _sprite texture normalized coordinates_. + * + * Use `outputMatrix * vTextureCoord` in the shader. + * @param outputMatrix - The matrix to output to. + * @param {Sprite} sprite - The sprite to map to. + * @returns The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) { + const data = this._activeFilterData; + const mappedMatrix = outputMatrix.set( + data.inputTexture._source.width, + 0, + 0, + data.inputTexture._source.height, + data.bounds.minX, + data.bounds.minY + ); + const worldTransform = sprite.worldTransform.copyTo(Matrix.shared); + const renderGroup = sprite.renderGroup || sprite.parentRenderGroup; + if (renderGroup && renderGroup.cacheToLocalTransform) { + worldTransform.prepend(renderGroup.cacheToLocalTransform); + } + worldTransform.invert(); + mappedMatrix.prepend(worldTransform); + mappedMatrix.scale( + 1 / sprite.texture.orig.width, + 1 / sprite.texture.orig.height + ); + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + return mappedMatrix; + } + destroy() { + var _a; + (_a = this._passthroughFilter) == null ? void 0 : _a.destroy(true); + this._passthroughFilter = null; + } + _getPassthroughFilter() { + var _a; + (_a = this._passthroughFilter) != null ? _a : this._passthroughFilter = new PassthroughFilter(); + return this._passthroughFilter; + } + /** + * Sets up the bind groups and renders the filter. + * @param filter - The filter to apply + * @param input - The input texture + * @param renderer - The renderer instance + */ + _setupBindGroupsAndRender(filter, input, renderer) { + if (renderer.renderPipes.uniformBatch) { + const batchUniforms = renderer.renderPipes.uniformBatch.getUboResource(this._filterGlobalUniforms); + this._globalFilterBindGroup.setResource(batchUniforms, 0); + } else { + this._globalFilterBindGroup.setResource(this._filterGlobalUniforms, 0); + } + this._globalFilterBindGroup.setResource(input.source, 1); + this._globalFilterBindGroup.setResource(input.source.style, 2); + filter.groups[0] = this._globalFilterBindGroup; + renderer.encoder.draw({ + geometry: quadGeometry, + shader: filter, + state: filter._state, + topology: "triangle-list" + }); + if (renderer.type === RendererType.WEBGL) { + renderer.renderTarget.finishRenderPass(); + } + } + /** + * Sets up the filter textures including input texture and back texture if needed. + * @param filterData - The filter data to update + * @param bounds - The bounds for the texture + * @param renderer - The renderer instance + * @param previousFilterData - The previous filter data for back texture calculation + */ + _setupFilterTextures(filterData, bounds, renderer, previousFilterData) { + filterData.backTexture = Texture.EMPTY; + filterData.inputTexture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + filterData.resolution, + filterData.antialias + ); + if (filterData.blendRequired) { + renderer.renderTarget.finishRenderPass(); + const renderTarget = renderer.renderTarget.getRenderTarget(filterData.outputRenderSurface); + filterData.backTexture = this.getBackTexture(renderTarget, bounds, previousFilterData == null ? void 0 : previousFilterData.bounds); + } + renderer.renderTarget.bind(filterData.inputTexture, true); + renderer.globalUniforms.push({ + offset: bounds + }); + } + /** + * Calculates and sets the global frame for the filter. + * @param filterData - The filter data to update + * @param offsetX - The X offset + * @param offsetY - The Y offset + * @param globalResolution - The global resolution + * @param sourceWidth - The source texture width + * @param sourceHeight - The source texture height + */ + _calculateGlobalFrame(filterData, offsetX, offsetY, globalResolution, sourceWidth, sourceHeight) { + const globalFrame = filterData.globalFrame; + globalFrame.x = offsetX * globalResolution; + globalFrame.y = offsetY * globalResolution; + globalFrame.width = sourceWidth * globalResolution; + globalFrame.height = sourceHeight * globalResolution; + } + /** + * Updates the filter uniforms with the current filter state. + * @param input - The input texture + * @param output - The output render surface + * @param filterData - The current filter data + * @param offsetX - The X offset for positioning + * @param offsetY - The Y offset for positioning + * @param resolution - The current resolution + * @param isFinalTarget - Whether this is the final render target + * @param clear - Whether to clear the output surface + */ + _updateFilterUniforms(input, output, filterData, offsetX, offsetY, resolution, isFinalTarget, clear) { + const uniforms = this._filterGlobalUniforms.uniforms; + const outputFrame = uniforms.uOutputFrame; + const inputSize = uniforms.uInputSize; + const inputPixel = uniforms.uInputPixel; + const inputClamp = uniforms.uInputClamp; + const globalFrame = uniforms.uGlobalFrame; + const outputTexture = uniforms.uOutputTexture; + if (isFinalTarget) { + outputFrame[0] = filterData.bounds.minX - offsetX; + outputFrame[1] = filterData.bounds.minY - offsetY; + } else { + outputFrame[0] = 0; + outputFrame[1] = 0; + } + outputFrame[2] = input.frame.width; + outputFrame[3] = input.frame.height; + inputSize[0] = input.source.width; + inputSize[1] = input.source.height; + inputSize[2] = 1 / inputSize[0]; + inputSize[3] = 1 / inputSize[1]; + inputPixel[0] = input.source.pixelWidth; + inputPixel[1] = input.source.pixelHeight; + inputPixel[2] = 1 / inputPixel[0]; + inputPixel[3] = 1 / inputPixel[1]; + inputClamp[0] = 0.5 * inputPixel[2]; + inputClamp[1] = 0.5 * inputPixel[3]; + inputClamp[2] = input.frame.width * inputSize[2] - 0.5 * inputPixel[2]; + inputClamp[3] = input.frame.height * inputSize[3] - 0.5 * inputPixel[3]; + const rootTexture = this.renderer.renderTarget.rootRenderTarget.colorTexture; + globalFrame[0] = offsetX * resolution; + globalFrame[1] = offsetY * resolution; + globalFrame[2] = rootTexture.source.width * resolution; + globalFrame[3] = rootTexture.source.height * resolution; + if (output instanceof Texture) output.source.resource = null; + const renderTarget = this.renderer.renderTarget.getRenderTarget(output); + this.renderer.renderTarget.bind(output, !!clear); + if (output instanceof Texture) { + outputTexture[0] = output.frame.width; + outputTexture[1] = output.frame.height; + } else { + outputTexture[0] = renderTarget.width; + outputTexture[1] = renderTarget.height; + } + outputTexture[2] = renderTarget.isRoot ? -1 : 1; + this._filterGlobalUniforms.update(); + } + /** + * Finds the correct resolution by looking back through the filter stack. + * @param rootResolution - The fallback root resolution to use + * @returns The resolution from the previous filter or root resolution + */ + _findFilterResolution(rootResolution) { + let currentIndex = this._filterStackIndex - 1; + while (currentIndex > 0 && this._filterStack[currentIndex].skip) { + --currentIndex; + } + return currentIndex > 0 && this._filterStack[currentIndex].inputTexture ? this._filterStack[currentIndex].inputTexture.source._resolution : rootResolution; + } + /** + * Finds the offset from the previous non-skipped filter in the stack. + * @returns The offset coordinates from the previous filter + */ + _findPreviousFilterOffset() { + let offsetX = 0; + let offsetY = 0; + let lastIndex = this._filterStackIndex; + while (lastIndex > 0) { + lastIndex--; + const prevFilterData = this._filterStack[lastIndex]; + if (!prevFilterData.skip) { + offsetX = prevFilterData.bounds.minX; + offsetY = prevFilterData.bounds.minY; + break; + } + } + return { x: offsetX, y: offsetY }; + } + /** + * Calculates the filter area bounds based on the instruction type. + * @param instruction - The filter instruction + * @param bounds - The bounds object to populate + */ + _calculateFilterArea(instruction, bounds) { + if (instruction.renderables) { + getGlobalRenderableBounds(instruction.renderables, bounds); + } else if (instruction.filterEffect.filterArea) { + bounds.clear(); + bounds.addRect(instruction.filterEffect.filterArea); + bounds.applyMatrix(instruction.container.worldTransform); + } else { + instruction.container.getFastGlobalBounds(true, bounds); + } + if (instruction.container) { + const renderGroup = instruction.container.renderGroup || instruction.container.parentRenderGroup; + const filterFrameTransform = renderGroup.cacheToLocalTransform; + if (filterFrameTransform) { + bounds.applyMatrix(filterFrameTransform); + } + } + } + _applyFiltersToTexture(filterData, clear) { + const inputTexture = filterData.inputTexture; + const bounds = filterData.bounds; + const filters = filterData.filters; + const firstEnabled = filterData.firstEnabledIndex; + const lastEnabled = filterData.lastEnabledIndex; + this._globalFilterBindGroup.setResource(inputTexture.source.style, 2); + this._globalFilterBindGroup.setResource(filterData.backTexture.source, 3); + if (firstEnabled === lastEnabled) { + filters[firstEnabled].apply(this, inputTexture, filterData.outputRenderSurface, clear); + } else { + let flip = filterData.inputTexture; + const tempTexture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + flip.source._resolution, + false + ); + let flop = tempTexture; + for (let i = firstEnabled; i < lastEnabled; i++) { + const filter = filters[i]; + if (!filter.enabled) continue; + filter.apply(this, flip, flop, true); + const t = flip; + flip = flop; + flop = t; + } + filters[lastEnabled].apply(this, flip, filterData.outputRenderSurface, clear); + TexturePool.returnTexture(tempTexture); + } + } + _calculateFilterBounds(filterData, viewPort, rootAntialias, rootResolution, paddingMultiplier) { + var _a, _b; + const renderer = this.renderer; + const bounds = filterData.bounds; + const filters = filterData.filters; + let resolution = Infinity; + let padding = 0; + let antialias = true; + let blendRequired = false; + let enabled = false; + let clipToViewport = true; + let firstEnabledIndex = -1; + let lastEnabledIndex = -1; + for (let i = 0; i < filters.length; i++) { + const filter = filters[i]; + if (!filter.enabled) continue; + if (firstEnabledIndex === -1) firstEnabledIndex = i; + lastEnabledIndex = i; + resolution = Math.min(resolution, filter.resolution === "inherit" ? rootResolution : filter.resolution); + padding += filter.padding; + if (filter.antialias === "off") { + antialias = false; + } else if (filter.antialias === "inherit") { + antialias && (antialias = rootAntialias); + } + if (!filter.clipToViewport) { + clipToViewport = false; + } + const isCompatible = !!(filter.compatibleRenderers & renderer.type); + if (!isCompatible) { + enabled = false; + break; + } + if (filter.blendRequired && !((_b = (_a = renderer.backBuffer) == null ? void 0 : _a.useBackBuffer) != null ? _b : true)) { + warn("Blend filter requires backBuffer on WebGL renderer to be enabled. Set `useBackBuffer: true` in the renderer options."); + enabled = false; + break; + } + enabled = true; + blendRequired || (blendRequired = filter.blendRequired); + } + if (!enabled) { + filterData.skip = true; + return; + } + if (clipToViewport) { + bounds.fitBounds(0, viewPort.width / rootResolution, 0, viewPort.height / rootResolution); + } + bounds.scale(resolution).ceil().scale(1 / resolution).pad((padding | 0) * paddingMultiplier); + if (!bounds.isPositive) { + filterData.skip = true; + return; + } + filterData.antialias = antialias; + filterData.resolution = resolution; + filterData.blendRequired = blendRequired; + filterData.firstEnabledIndex = firstEnabledIndex; + filterData.lastEnabledIndex = lastEnabledIndex; + } + _popFilterData() { + this._filterStackIndex--; + return this._filterStack[this._filterStackIndex]; + } + _getPreviousFilterData() { + let previousFilterData; + let index = this._filterStackIndex - 1; + while (index > 0) { + index--; + previousFilterData = this._filterStack[index]; + if (!previousFilterData.skip) { + break; + } + } + return previousFilterData; + } + _pushFilterData() { + let filterData = this._filterStack[this._filterStackIndex]; + if (!filterData) { + filterData = this._filterStack[this._filterStackIndex] = new FilterData(); + } + this._filterStackIndex++; + return filterData; + } + } + /** @ignore */ + FilterSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "filter" + }; + + "use strict"; + extensions.add(FilterSystem); + extensions.add(FilterPipe); + + "use strict"; + + var browserAll = { + __proto__: null + }; + + "use strict"; + + "use strict"; + const environments = []; + extensions.handleByNamedList(ExtensionType.Environment, environments); + async function loadEnvironmentExtensions(skip) { + if (skip) return; + for (let i = 0; i < environments.length; i++) { + const env = environments[i]; + if (env.value.test()) { + await env.value.load(); + return; + } + } + } + async function autoDetectEnvironment(add) { + return loadEnvironmentExtensions(!add); + } + + "use strict"; + let unsafeEval; + function unsafeEvalSupported() { + if (typeof unsafeEval === "boolean") { + return unsafeEval; + } + try { + const func = new Function("param1", "param2", "param3", "return param1[param2] === param3;"); + unsafeEval = func({ a: "b" }, "a", "b") === true; + } catch (_e) { + unsafeEval = false; + } + return unsafeEval; + } + + function earcut$1(data, holeIndices, dim = 2) { + + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[0] * dim : data.length; + let outerNode = linkedList(data, 0, outerLen, dim, true); + const triangles = []; + + if (!outerNode || outerNode.next === outerNode.prev) return triangles; + + let minX, minY, invSize; + + if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if (data.length > 80 * dim) { + minX = data[0]; + minY = data[1]; + let maxX = minX; + let maxY = minY; + + for (let i = dim; i < outerLen; i += dim) { + const x = data[i]; + const y = data[i + 1]; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + } + + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max(maxX - minX, maxY - minY); + invSize = invSize !== 0 ? 32767 / invSize : 0; + } + + earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); + + return triangles; + } + + // create a circular doubly linked list from polygon points in the specified winding order + function linkedList(data, start, end, dim, clockwise) { + let last; + + if (clockwise === (signedArea(data, start, end, dim) > 0)) { + for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last); + } else { + for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last); + } + + if (last && equals(last, last.next)) { + removeNode(last); + last = last.next; + } + + return last; + } + + // eliminate colinear or duplicate points + function filterPoints(start, end) { + if (!start) return start; + if (!end) end = start; + + let p = start, + again; + do { + again = false; + + if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { + removeNode(p); + p = end = p.prev; + if (p === p.next) break; + again = true; + + } else { + p = p.next; + } + } while (again || p !== end); + + return end; + } + + // main ear slicing loop which triangulates a polygon (given as a linked list) + function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && invSize) indexCurve(ear, minX, minY, invSize); + + let stop = ear; + + // iterate through ears, slicing them one by one + while (ear.prev !== ear.next) { + const prev = ear.prev; + const next = ear.next; + + if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { + triangles.push(prev.i, ear.i, next.i); // cut off the triangle + + removeNode(ear); + + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear === stop) { + // try filtering points and slicing again + if (!pass) { + earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); + + // if this didn't work, try curing all small self-intersections locally + } else if (pass === 1) { + ear = cureLocalIntersections(filterPoints(ear), triangles); + earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass === 2) { + splitEarcut(ear, triangles, dim, minX, minY, invSize); + } + + break; + } + } + } + + // check whether a polygon node forms a valid ear with adjacent nodes + function isEar(ear) { + const a = ear.prev, + b = ear, + c = ear.next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox + const x0 = Math.min(ax, bx, cx), + y0 = Math.min(ay, by, cy), + x1 = Math.max(ax, bx, cx), + y1 = Math.max(ay, by, cy); + + let p = c.next; + while (p !== a) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && + pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && + area(p.prev, p, p.next) >= 0) return false; + p = p.next; + } + + return true; + } + + function isEarHashed(ear, minX, minY, invSize) { + const a = ear.prev, + b = ear, + c = ear.next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox + const x0 = Math.min(ax, bx, cx), + y0 = Math.min(ay, by, cy), + x1 = Math.max(ax, bx, cx), + y1 = Math.max(ay, by, cy); + + // z-order range for the current triangle bbox; + const minZ = zOrder(x0, y0, minX, minY, invSize), + maxZ = zOrder(x1, y1, minX, minY, invSize); + + let p = ear.prevZ, + n = ear.nextZ; + + // look for points inside the triangle in both directions + while (p && p.z >= minZ && n && n.z <= maxZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } + + // look for remaining points in decreasing z-order + while (p && p.z >= minZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + } + + // look for remaining points in increasing z-order + while (n && n.z <= maxZ) { + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } + + return true; + } + + // go through all polygon nodes and cure small local self-intersections + function cureLocalIntersections(start, triangles) { + let p = start; + do { + const a = p.prev, + b = p.next.next; + + if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { + + triangles.push(a.i, p.i, b.i); + + // remove two nodes involved + removeNode(p); + removeNode(p.next); + + p = start = b; + } + p = p.next; + } while (p !== start); + + return filterPoints(p); + } + + // try splitting polygon into two and triangulate them independently + function splitEarcut(start, triangles, dim, minX, minY, invSize) { + // look for a valid diagonal that divides the polygon into two + let a = start; + do { + let b = a.next.next; + while (b !== a.prev) { + if (a.i !== b.i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + let c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a.next); + c = filterPoints(c, c.next); + + // run earcut on each half + earcutLinked(a, triangles, dim, minX, minY, invSize, 0); + earcutLinked(c, triangles, dim, minX, minY, invSize, 0); + return; + } + b = b.next; + } + a = a.next; + } while (a !== start); + } + + // link every hole into the outer loop, producing a single-ring polygon without holes + function eliminateHoles(data, holeIndices, outerNode, dim) { + const queue = []; + + for (let i = 0, len = holeIndices.length; i < len; i++) { + const start = holeIndices[i] * dim; + const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; + const list = linkedList(data, start, end, dim, false); + if (list === list.next) list.steiner = true; + queue.push(getLeftmost(list)); + } + + queue.sort(compareXYSlope); + + // process holes from left to right + for (let i = 0; i < queue.length; i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + + return outerNode; + } + + function compareXYSlope(a, b) { + let result = a.x - b.x; + // when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find + // the bridge to the outer shell is always the point that they meet at. + if (result === 0) { + result = a.y - b.y; + if (result === 0) { + const aSlope = (a.next.y - a.y) / (a.next.x - a.x); + const bSlope = (b.next.y - b.y) / (b.next.x - b.x); + result = aSlope - bSlope; + } + } + return result; + } + + // find a bridge between vertices that connects hole with an outer ring and link it + function eliminateHole(hole, outerNode) { + const bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + + const bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + filterPoints(bridgeReverse, bridgeReverse.next); + return filterPoints(bridge, bridge.next); + } + + // David Eberly's algorithm for finding a bridge between hole and outer polygon + function findHoleBridge(hole, outerNode) { + let p = outerNode; + const hx = hole.x; + const hy = hole.y; + let qx = -Infinity; + let m; + + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + // unless they intersect at a vertex, then choose the vertex + if (equals(hole, p)) return p; + do { + if (equals(hole, p.next)) return p.next; + else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { + const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); + if (x <= hx && x > qx) { + qx = x; + m = p.x < p.next.x ? p : p.next; + if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint + } + } + p = p.next; + } while (p !== outerNode); + + if (!m) return null; + + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point + + const stop = m; + const mx = m.x; + const my = m.y; + let tanMin = Infinity; + + p = m; + + do { + if (hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { + + const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential + + if (locallyInside(p, hole) && + (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { + m = p; + tanMin = tan; + } + } + + p = p.next; + } while (p !== stop); + + return m; + } + + // whether sector in vertex m contains sector in vertex p in the same coordinates + function sectorContainsSector(m, p) { + return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; + } + + // interlink polygon nodes in z-order + function indexCurve(start, minX, minY, invSize) { + let p = start; + do { + if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + } while (p !== start); + + p.prevZ.nextZ = null; + p.prevZ = null; + + sortLinked(p); + } + + // Simon Tatham's linked list merge sort algorithm + // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + function sortLinked(list) { + let numMerges; + let inSize = 1; + + do { + let p = list; + let e; + list = null; + let tail = null; + numMerges = 0; + + while (p) { + numMerges++; + let q = p; + let pSize = 0; + for (let i = 0; i < inSize; i++) { + pSize++; + q = q.nextZ; + if (!q) break; + } + let qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { + e = p; + p = p.nextZ; + pSize--; + } else { + e = q; + q = q.nextZ; + qSize--; + } + + if (tail) tail.nextZ = e; + else list = e; + + e.prevZ = tail; + tail = e; + } + + p = q; + } + + tail.nextZ = null; + inSize *= 2; + + } while (numMerges > 1); + + return list; + } + + // z-order of a point given coords and inverse of the longer side of data bbox + function zOrder(x, y, minX, minY, invSize) { + // coords are transformed into non-negative 15-bit integer range + x = (x - minX) * invSize | 0; + y = (y - minY) * invSize | 0; + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); + } + + // find the leftmost node of a polygon ring + function getLeftmost(start) { + let p = start, + leftmost = start; + do { + if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; + p = p.next; + } while (p !== start); + + return leftmost; + } + + // check if a point lies within a convex triangle + function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); + } + + // check if a point lies within a convex triangle but false if its equal to the first point of the triangle + function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) { + return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py); + } + + // check if a diagonal between two polygon nodes is valid (lies in polygon interior) + function isValidDiagonal(a, b) { + return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges + (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors + equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case + } + + // signed area of a triangle + function area(p, q, r) { + return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + } + + // check if two points are equal + function equals(p1, p2) { + return p1.x === p2.x && p1.y === p2.y; + } + + // check if two segments intersect + function intersects(p1, q1, p2, q2) { + const o1 = sign(area(p1, q1, p2)); + const o2 = sign(area(p1, q1, q2)); + const o3 = sign(area(p2, q2, p1)); + const o4 = sign(area(p2, q2, q1)); + + if (o1 !== o2 && o3 !== o4) return true; // general case + + if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; + } + + // for collinear points p, q, r, check if point q lies on segment pr + function onSegment(p, q, r) { + return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); + } + + function sign(num) { + return num > 0 ? 1 : num < 0 ? -1 : 0; + } + + // check if a polygon diagonal intersects any polygon segments + function intersectsPolygon(a, b) { + let p = a; + do { + if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects(p, p.next, a, b)) return true; + p = p.next; + } while (p !== a); + + return false; + } + + // check if a polygon diagonal is locally inside the polygon + function locallyInside(a, b) { + return area(a.prev, a, a.next) < 0 ? + area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : + area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; + } + + // check if the middle point of a polygon diagonal is inside the polygon + function middleInside(a, b) { + let p = a; + let inside = false; + const px = (a.x + b.x) / 2; + const py = (a.y + b.y) / 2; + do { + if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && + (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) + inside = !inside; + p = p.next; + } while (p !== a); + + return inside; + } + + // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; + // if one belongs to the outer ring and another to a hole, it merges it into a single ring + function splitPolygon(a, b) { + const a2 = createNode(a.i, a.x, a.y), + b2 = createNode(b.i, b.x, b.y), + an = a.next, + bp = b.prev; + + a.next = b; + b.prev = a; + + a2.next = an; + an.prev = a2; + + b2.next = a2; + a2.prev = b2; + + bp.next = b2; + b2.prev = bp; + + return b2; + } + + // create a node and optionally link it with previous one (in a circular doubly linked list) + function insertNode(i, x, y, last) { + const p = createNode(i, x, y); + + if (!last) { + p.prev = p; + p.next = p; + + } else { + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; + } + return p; + } + + function removeNode(p) { + p.next.prev = p.prev; + p.prev.next = p.next; + + if (p.prevZ) p.prevZ.nextZ = p.nextZ; + if (p.nextZ) p.nextZ.prevZ = p.prevZ; + } + + function createNode(i, x, y) { + return { + i, // vertex index in coordinates array + x, y, // vertex coordinates + prev: null, // previous and next vertex nodes in a polygon ring + next: null, + z: 0, // z-order curve value + prevZ: null, // previous and next nodes in z-order + nextZ: null, + steiner: false // indicates whether this is a steiner point + }; + } + + // return a percentage difference between the polygon area and its triangulation area; + // used to verify correctness of triangulation + function deviation(data, holeIndices, dim, triangles) { + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[0] * dim : data.length; + + let polygonArea = Math.abs(signedArea(data, 0, outerLen, dim)); + if (hasHoles) { + for (let i = 0, len = holeIndices.length; i < len; i++) { + const start = holeIndices[i] * dim; + const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; + polygonArea -= Math.abs(signedArea(data, start, end, dim)); + } + } + + let trianglesArea = 0; + for (let i = 0; i < triangles.length; i += 3) { + const a = triangles[i] * dim; + const b = triangles[i + 1] * dim; + const c = triangles[i + 2] * dim; + trianglesArea += Math.abs( + (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - + (data[a] - data[b]) * (data[c + 1] - data[a + 1])); + } + + return polygonArea === 0 && trianglesArea === 0 ? 0 : + Math.abs((trianglesArea - polygonArea) / polygonArea); + } + + function signedArea(data, start, end, dim) { + let sum = 0; + for (let i = start, j = end - dim; i < end; i += dim) { + sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); + j = i; + } + return sum; + } + + // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts + function flatten(data) { + const vertices = []; + const holes = []; + const dimensions = data[0][0].length; + let holeIndex = 0; + let prevLen = 0; + + for (const ring of data) { + for (const p of ring) { + for (let d = 0; d < dimensions; d++) vertices.push(p[d]); + } + if (prevLen) { + holeIndex += prevLen; + holes.push(holeIndex); + } + prevLen = ring.length; + } + return {vertices, holes, dimensions}; + } + + "use strict"; + const earcut = earcut$1.default || earcut$1; + + "use strict"; + var CLEAR = /* @__PURE__ */ ((CLEAR2) => { + CLEAR2[CLEAR2["NONE"] = 0] = "NONE"; + CLEAR2[CLEAR2["COLOR"] = 16384] = "COLOR"; + CLEAR2[CLEAR2["STENCIL"] = 1024] = "STENCIL"; + CLEAR2[CLEAR2["DEPTH"] = 256] = "DEPTH"; + CLEAR2[CLEAR2["COLOR_DEPTH"] = 16640] = "COLOR_DEPTH"; + CLEAR2[CLEAR2["COLOR_STENCIL"] = 17408] = "COLOR_STENCIL"; + CLEAR2[CLEAR2["DEPTH_STENCIL"] = 1280] = "DEPTH_STENCIL"; + CLEAR2[CLEAR2["ALL"] = 17664] = "ALL"; + return CLEAR2; + })(CLEAR || {}); + + "use strict"; + class SystemRunner { + /** + * @param name - The function name that will be executed on the listeners added to this Runner. + */ + constructor(name) { + this.items = []; + this._name = name; + } + /* jsdoc/check-param-names */ + /** + * Dispatch/Broadcast Runner to all listeners added to the queue. + * @param {...any} params - (optional) parameters to pass to each listener + */ + /* jsdoc/check-param-names */ + emit(a0, a1, a2, a3, a4, a5, a6, a7) { + const { name, items } = this; + for (let i = 0, len = items.length; i < len; i++) { + items[i][name](a0, a1, a2, a3, a4, a5, a6, a7); + } + return this; + } + /** + * Add a listener to the Runner + * + * Runners do not need to have scope or functions passed to them. + * All that is required is to pass the listening object and ensure that it has contains a function that has the same name + * as the name provided to the Runner when it was created. + * + * Eg A listener passed to this Runner will require a 'complete' function. + * + * ```ts + * import { Runner } from 'pixi.js'; + * + * const complete = new Runner('complete'); + * ``` + * + * The scope used will be the object itself. + * @param {any} item - The object that will be listening. + */ + add(item) { + if (item[this._name]) { + this.remove(item); + this.items.push(item); + } + return this; + } + /** + * Remove a single listener from the dispatch queue. + * @param {any} item - The listener that you would like to remove. + */ + remove(item) { + const index = this.items.indexOf(item); + if (index !== -1) { + this.items.splice(index, 1); + } + return this; + } + /** + * Check to see if the listener is already in the Runner + * @param {any} item - The listener that you would like to check. + */ + contains(item) { + return this.items.indexOf(item) !== -1; + } + /** Remove all listeners from the Runner */ + removeAll() { + this.items.length = 0; + return this; + } + /** Remove all references, don't use after this. */ + destroy() { + this.removeAll(); + this.items = null; + this._name = null; + } + /** + * `true` if there are no this Runner contains no listeners + * @readonly + */ + get empty() { + return this.items.length === 0; + } + /** + * The name of the runner. + * @readonly + */ + get name() { + return this._name; + } + } + + "use strict"; + var __defProp$16 = Object.defineProperty; + var __getOwnPropSymbols$18 = Object.getOwnPropertySymbols; + var __hasOwnProp$18 = Object.prototype.hasOwnProperty; + var __propIsEnum$18 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$16 = (obj, key, value) => key in obj ? __defProp$16(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$16 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$18.call(b, prop)) + __defNormalProp$16(a, prop, b[prop]); + if (__getOwnPropSymbols$18) + for (var prop of __getOwnPropSymbols$18(b)) { + if (__propIsEnum$18.call(b, prop)) + __defNormalProp$16(a, prop, b[prop]); + } + return a; + }; + const defaultRunners = [ + "init", + "destroy", + "contextChange", + "resolutionChange", + "resetState", + "renderEnd", + "renderStart", + "render", + "update", + "postrender", + "prerender" + ]; + const _AbstractRenderer = class _AbstractRenderer extends EventEmitter { + /** + * Set up a system with a collection of SystemClasses and runners. + * Systems are attached dynamically to this class when added. + * @param config - the config for the system manager + */ + constructor(config) { + var _a; + super(); + /** The current tick of the renderer. */ + this.tick = 0; + /** @internal */ + this.uid = uid$1("renderer"); + /** @internal */ + this.runners = /* @__PURE__ */ Object.create(null); + /** @internal */ + this.renderPipes = /* @__PURE__ */ Object.create(null); + this._initOptions = {}; + this._systemsHash = /* @__PURE__ */ Object.create(null); + this.type = config.type; + this.name = config.name; + this.config = config; + const combinedRunners = [...defaultRunners, ...(_a = this.config.runners) != null ? _a : []]; + this._addRunners(...combinedRunners); + this._unsafeEvalCheck(); + } + /** + * Initialize the renderer. + * @param options - The options to use to create the renderer. + */ + async init(options = {}) { + const skip = options.skipExtensionImports === true ? true : options.manageImports === false; + await loadEnvironmentExtensions(skip); + this._addSystems(this.config.systems); + this._addPipes(this.config.renderPipes, this.config.renderPipeAdaptors); + for (const systemName in this._systemsHash) { + const system = this._systemsHash[systemName]; + const defaultSystemOptions = system.constructor.defaultOptions; + options = __spreadValues$16(__spreadValues$16({}, defaultSystemOptions), options); + } + options = __spreadValues$16(__spreadValues$16({}, _AbstractRenderer.defaultOptions), options); + this._roundPixels = options.roundPixels ? 1 : 0; + for (let i = 0; i < this.runners.init.items.length; i++) { + await this.runners.init.items[i].init(options); + } + this._initOptions = options; + } + render(args, deprecated) { + var _a, _b; + this.tick++; + let options = args; + if (options instanceof Container) { + options = { container: options }; + if (deprecated) { + deprecation(v8_0_0, "passing a second argument is deprecated, please use render options instead"); + options.target = deprecated.renderTexture; + } + } + options.target || (options.target = this.view.renderTarget); + if (options.target === this.view.renderTarget) { + this._lastObjectRendered = options.container; + (_a = options.clearColor) != null ? _a : options.clearColor = this.background.colorRgba; + (_b = options.clear) != null ? _b : options.clear = this.background.clearBeforeRender; + } + if (options.clearColor) { + const isRGBAArray = Array.isArray(options.clearColor) && options.clearColor.length === 4; + options.clearColor = isRGBAArray ? options.clearColor : Color.shared.setValue(options.clearColor).toArray(); + } + if (!options.transform) { + options.container.updateLocalTransform(); + options.transform = options.container.localTransform; + } + if (!options.container.visible) { + return; + } + options.container.enableRenderGroup(); + this.runners.prerender.emit(options); + this.runners.renderStart.emit(options); + this.runners.render.emit(options); + this.runners.renderEnd.emit(options); + this.runners.postrender.emit(options); + } + /** + * Resizes the WebGL view to the specified width and height. + * @param desiredScreenWidth - The desired width of the screen. + * @param desiredScreenHeight - The desired height of the screen. + * @param resolution - The resolution / device pixel ratio of the renderer. + */ + resize(desiredScreenWidth, desiredScreenHeight, resolution) { + const previousResolution = this.view.resolution; + this.view.resize(desiredScreenWidth, desiredScreenHeight, resolution); + this.emit("resize", this.view.screen.width, this.view.screen.height, this.view.resolution); + if (resolution !== void 0 && resolution !== previousResolution) { + this.runners.resolutionChange.emit(resolution); + } + } + /** + * Clears the render target. + * @param options - The options to use when clearing the render target. + * @param options.target - The render target to clear. + * @param options.clearColor - The color to clear with. + * @param options.clear - The clear mode to use. + * @advanced + */ + clear(options = {}) { + var _a; + const renderer = this; + options.target || (options.target = renderer.renderTarget.renderTarget); + options.clearColor || (options.clearColor = this.background.colorRgba); + (_a = options.clear) != null ? _a : options.clear = CLEAR.ALL; + const { clear, clearColor, target, mipLevel, layer } = options; + Color.shared.setValue(clearColor != null ? clearColor : this.background.colorRgba); + renderer.renderTarget.clear(target, clear, Color.shared.toArray(), mipLevel != null ? mipLevel : 0, layer != null ? layer : 0); + } + /** The resolution / device pixel ratio of the renderer. */ + get resolution() { + return this.view.resolution; + } + set resolution(value) { + this.view.resolution = value; + this.runners.resolutionChange.emit(value); + } + /** + * Same as view.width, actual number of pixels in the canvas by horizontal. + * @type {number} + * @readonly + * @default 800 + */ + get width() { + return this.view.texture.frame.width; + } + /** + * Same as view.height, actual number of pixels in the canvas by vertical. + * @default 600 + */ + get height() { + return this.view.texture.frame.height; + } + // NOTE: this was `view` in v7 + /** + * The canvas element that everything is drawn to. + * @type {environment.ICanvas} + */ + get canvas() { + return this.view.canvas; + } + /** + * the last object rendered by the renderer. Useful for other plugins like interaction managers + * @readonly + */ + get lastObjectRendered() { + return this._lastObjectRendered; + } + /** + * Flag if we are rendering to the screen vs renderTexture + * @readonly + * @default true + */ + get renderingToScreen() { + const renderer = this; + return renderer.renderTarget.renderingToScreen; + } + /** + * Measurements of the screen. (0, 0, screenWidth, screenHeight). + * + * Its safe to use as filterArea or hitArea for the whole stage. + */ + get screen() { + return this.view.screen; + } + /** + * Create a bunch of runners based of a collection of ids + * @param runnerIds - the runner ids to add + */ + _addRunners(...runnerIds) { + runnerIds.forEach((runnerId) => { + this.runners[runnerId] = new SystemRunner(runnerId); + }); + } + _addSystems(systems) { + let i; + for (i in systems) { + const val = systems[i]; + this._addSystem(val.value, val.name); + } + } + /** + * Add a new system to the renderer. + * @param ClassRef - Class reference + * @param name - Property name for system, if not specified + * will use a static `name` property on the class itself. This + * name will be assigned as s property on the Renderer so make + * sure it doesn't collide with properties on Renderer. + * @returns Return instance of renderer + */ + _addSystem(ClassRef, name) { + const system = new ClassRef(this); + if (this[name]) { + throw new Error(`Whoops! The name "${name}" is already in use`); + } + this[name] = system; + this._systemsHash[name] = system; + for (const i in this.runners) { + this.runners[i].add(system); + } + return this; + } + _addPipes(pipes, pipeAdaptors) { + const adaptors = pipeAdaptors.reduce((acc, adaptor) => { + acc[adaptor.name] = adaptor.value; + return acc; + }, {}); + pipes.forEach((pipe) => { + const PipeClass = pipe.value; + const name = pipe.name; + const Adaptor = adaptors[name]; + this.renderPipes[name] = new PipeClass( + this, + Adaptor ? new Adaptor() : null + ); + this.runners.destroy.add(this.renderPipes[name]); + }); + } + destroy(options = false) { + this.runners.destroy.items.reverse(); + this.runners.destroy.emit(options); + if (options === true || typeof options === "object" && options.releaseGlobalResources) { + GlobalResourceRegistry.release(); + } + Object.values(this.runners).forEach((runner) => { + runner.destroy(); + }); + this._systemsHash = null; + this.renderPipes = null; + } + /** + * Generate a texture from a container. + * @param options - options or container target to use when generating the texture + * @returns a texture + */ + generateTexture(options) { + return this.textureGenerator.generateTexture(options); + } + /** + * Whether the renderer will round coordinates to whole pixels when rendering. + * Can be overridden on a per scene item basis. + */ + get roundPixels() { + return !!this._roundPixels; + } + /** + * Overridable function by `pixi.js/unsafe-eval` to silence + * throwing an error if platform doesn't support unsafe-evals. + * @private + * @ignore + */ + _unsafeEvalCheck() { + if (!unsafeEvalSupported()) { + throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support."); + } + } + /** + * Resets the rendering state of the renderer. + * This is useful when you want to use the WebGL context directly and need to ensure PixiJS's internal state + * stays synchronized. When modifying the WebGL context state externally, calling this method before the next Pixi + * render will reset all internal caches and ensure it executes correctly. + * + * This is particularly useful when combining PixiJS with other rendering engines like Three.js: + * ```js + * // Reset Three.js state + * threeRenderer.resetState(); + * + * // Render a Three.js scene + * threeRenderer.render(threeScene, threeCamera); + * + * // Reset PixiJS state since Three.js modified the WebGL context + * pixiRenderer.resetState(); + * + * // Now render Pixi content + * pixiRenderer.render(pixiScene); + * ``` + * @advanced + */ + resetState() { + this.runners.resetState.emit(); + } + }; + /** The default options for the renderer. */ + _AbstractRenderer.defaultOptions = { + /** + * Default resolution / device pixel ratio of the renderer. + * @default 1 + */ + resolution: 1, + /** + * Should the `failIfMajorPerformanceCaveat` flag be enabled as a context option used in the `isWebGLSupported` + * function. If set to true, a WebGL renderer can fail to be created if the browser thinks there could be + * performance issues when using WebGL. + * + * In PixiJS v6 this has changed from true to false by default, to allow WebGL to work in as many + * scenarios as possible. However, some users may have a poor experience, for example, if a user has a gpu or + * driver version blacklisted by the + * browser. + * + * If your application requires high performance rendering, you may wish to set this to false. + * We recommend one of two options if you decide to set this flag to false: + * + * 1: Use the Canvas renderer as a fallback in case high performance WebGL is + * not supported. + * + * 2: Call `isWebGLSupported` (which if found in the utils package) in your code before attempting to create a + * PixiJS renderer, and show an error message to the user if the function returns false, explaining that their + * device & browser combination does not support high performance WebGL. + * This is a much better strategy than trying to create a PixiJS renderer and finding it then fails. + * @default false + */ + failIfMajorPerformanceCaveat: false, + /** + * Should round pixels be forced when rendering? + * @default false + */ + roundPixels: false + }; + let AbstractRenderer = _AbstractRenderer; + + "use strict"; + let _isWebGLSupported; + function isWebGLSupported(failIfMajorPerformanceCaveat) { + if (_isWebGLSupported !== void 0) return _isWebGLSupported; + _isWebGLSupported = (() => { + var _a; + const contextOptions = { + stencil: true, + failIfMajorPerformanceCaveat: failIfMajorPerformanceCaveat != null ? failIfMajorPerformanceCaveat : AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat + }; + try { + if (!DOMAdapter.get().getWebGLRenderingContext()) { + return false; + } + const canvas = DOMAdapter.get().createCanvas(); + let gl = canvas.getContext("webgl", contextOptions); + const success = !!((_a = gl == null ? void 0 : gl.getContextAttributes()) == null ? void 0 : _a.stencil); + if (gl) { + const loseContext = gl.getExtension("WEBGL_lose_context"); + if (loseContext) { + loseContext.loseContext(); + } + } + gl = null; + return success; + } catch (_e) { + return false; + } + })(); + return _isWebGLSupported; + } + + "use strict"; + let _isWebGPUSupported; + async function isWebGPUSupported(options = {}) { + if (_isWebGPUSupported !== void 0) return _isWebGPUSupported; + _isWebGPUSupported = await (async () => { + const gpu = DOMAdapter.get().getNavigator().gpu; + if (!gpu) { + return false; + } + try { + const adapter = await gpu.requestAdapter(options); + await adapter.requestDevice(); + return true; + } catch (_e) { + return false; + } + })(); + return _isWebGPUSupported; + } + + "use strict"; + var __defProp$15 = Object.defineProperty; + var __getOwnPropSymbols$17 = Object.getOwnPropertySymbols; + var __hasOwnProp$17 = Object.prototype.hasOwnProperty; + var __propIsEnum$17 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$15 = (obj, key, value) => key in obj ? __defProp$15(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$15 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$17.call(b, prop)) + __defNormalProp$15(a, prop, b[prop]); + if (__getOwnPropSymbols$17) + for (var prop of __getOwnPropSymbols$17(b)) { + if (__propIsEnum$17.call(b, prop)) + __defNormalProp$15(a, prop, b[prop]); + } + return a; + }; + const renderPriority = ["webgl", "webgpu", "canvas"]; + async function autoDetectRenderer(options) { + var _a; + let preferredOrder = []; + if (options.preference) { + preferredOrder.push(options.preference); + renderPriority.forEach((item) => { + if (item !== options.preference) { + preferredOrder.push(item); + } + }); + } else { + preferredOrder = renderPriority.slice(); + } + let RendererClass; + let finalOptions = {}; + for (let i = 0; i < preferredOrder.length; i++) { + const rendererType = preferredOrder[i]; + if (rendererType === "webgpu" && await isWebGPUSupported()) { + const { WebGPURenderer } = await Promise.resolve().then(function () { return WebGPURenderer$1; }); + RendererClass = WebGPURenderer; + finalOptions = __spreadValues$15(__spreadValues$15({}, options), options.webgpu); + break; + } else if (rendererType === "webgl" && isWebGLSupported( + (_a = options.failIfMajorPerformanceCaveat) != null ? _a : AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat + )) { + const { WebGLRenderer } = await Promise.resolve().then(function () { return WebGLRenderer$1; }); + RendererClass = WebGLRenderer; + finalOptions = __spreadValues$15(__spreadValues$15({}, options), options.webgl); + break; + } else if (rendererType === "canvas") { + const { CanvasRenderer } = await Promise.resolve().then(function () { return CanvasRenderer$1; }); + RendererClass = CanvasRenderer; + finalOptions = __spreadValues$15(__spreadValues$15({}, options), options.canvasOptions); + break; + } + } + delete finalOptions.webgpu; + delete finalOptions.webgl; + delete finalOptions.canvasOptions; + if (!RendererClass) { + throw new Error("No available renderer for the current environment"); + } + const renderer = new RendererClass(); + await renderer.init(finalOptions); + return renderer; + } + + "use strict"; + const DATA_URI = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i; + const VERSION = "8.16.0"; + + "use strict"; + class ApplicationInitHook { + static init() { + var _a; + (_a = globalThis.__PIXI_APP_INIT__) == null ? void 0 : _a.call(globalThis, this, VERSION); + } + static destroy() { + } + } + /** @ignore */ + ApplicationInitHook.extension = ExtensionType.Application; + class RendererInitHook { + constructor(renderer) { + this._renderer = renderer; + } + init() { + var _a; + (_a = globalThis.__PIXI_RENDERER_INIT__) == null ? void 0 : _a.call(globalThis, this._renderer, VERSION); + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + RendererInitHook.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "initHook", + priority: -10 + }; + + "use strict"; + class ResizePlugin { + /** + * Initialize the plugin with scope of application instance + * @private + * @param {object} [options] - See application options + */ + static init(options) { + Object.defineProperty( + this, + "resizeTo", + { + configurable: true, + set(dom) { + globalThis.removeEventListener("resize", this.queueResize); + this._resizeTo = dom; + if (dom) { + globalThis.addEventListener("resize", this.queueResize); + this.resize(); + } + }, + get() { + return this._resizeTo; + } + } + ); + this.queueResize = () => { + if (!this._resizeTo) { + return; + } + this._cancelResize(); + this._resizeId = requestAnimationFrame(() => this.resize()); + }; + this._cancelResize = () => { + if (this._resizeId) { + cancelAnimationFrame(this._resizeId); + this._resizeId = null; + } + }; + this.resize = () => { + if (!this._resizeTo) { + return; + } + this._cancelResize(); + let width; + let height; + if (this._resizeTo === globalThis.window) { + width = globalThis.innerWidth; + height = globalThis.innerHeight; + } else { + const { clientWidth, clientHeight } = this._resizeTo; + width = clientWidth; + height = clientHeight; + } + this.renderer.resize(width, height); + this.render(); + }; + this._resizeId = null; + this._resizeTo = null; + this.resizeTo = options.resizeTo || null; + } + /** + * Clean up the ticker, scoped to application + * @private + */ + static destroy() { + globalThis.removeEventListener("resize", this.queueResize); + this._cancelResize(); + this._cancelResize = null; + this.queueResize = null; + this.resizeTo = null; + this.resize = null; + } + } + /** @ignore */ + ResizePlugin.extension = ExtensionType.Application; + + "use strict"; + class TickerPlugin { + /** + * Initialize the plugin with scope of application instance + * @private + * @param {object} [options] - See application options + */ + static init(options) { + options = Object.assign({ + autoStart: true, + sharedTicker: false + }, options); + Object.defineProperty( + this, + "ticker", + { + configurable: true, + set(ticker) { + if (this._ticker) { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() { + return this._ticker; + } + } + ); + this.stop = () => { + this._ticker.stop(); + }; + this.start = () => { + this._ticker.start(); + }; + this._ticker = null; + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + if (options.autoStart) { + this.start(); + } + } + /** + * Clean up the ticker, scoped to application. + * @private + */ + static destroy() { + if (this._ticker) { + const oldTicker = this._ticker; + this.ticker = null; + oldTicker.destroy(); + } + } + } + /** @ignore */ + TickerPlugin.extension = ExtensionType.Application; + + "use strict"; + extensions.add(ResizePlugin); + extensions.add(TickerPlugin); + + "use strict"; + var __defProp$14 = Object.defineProperty; + var __getOwnPropSymbols$16 = Object.getOwnPropertySymbols; + var __hasOwnProp$16 = Object.prototype.hasOwnProperty; + var __propIsEnum$16 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$14 = (obj, key, value) => key in obj ? __defProp$14(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$14 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$16.call(b, prop)) + __defNormalProp$14(a, prop, b[prop]); + if (__getOwnPropSymbols$16) + for (var prop of __getOwnPropSymbols$16(b)) { + if (__propIsEnum$16.call(b, prop)) + __defNormalProp$14(a, prop, b[prop]); + } + return a; + }; + const _Application = class _Application { + constructor(...args) { + /** + * The root display container for your application. + * All visual elements should be added to this container or its children. + * @example + * ```js + * // Create a sprite and add it to the stage + * const sprite = Sprite.from('image.png'); + * app.stage.addChild(sprite); + * + * // Create a container for grouping objects + * const container = new Container(); + * app.stage.addChild(container); + * ``` + */ + this.stage = new Container(); + if (args[0] !== void 0) { + deprecation(v8_0_0, "Application constructor options are deprecated, please use Application.init() instead."); + } + } + /** + * Initializes the PixiJS application with the specified options. + * + * This method must be called after creating a new Application instance. + * @param options - Configuration options for the application and renderer + * @returns A promise that resolves when initialization is complete + * @example + * ```js + * const app = new Application(); + * + * // Initialize with custom options + * await app.init({ + * width: 800, + * height: 600, + * backgroundColor: 0x1099bb, + * preference: 'webgl', // or 'webgpu' + * }); + * ``` + */ + async init(options) { + options = __spreadValues$14({}, options); + this.stage || (this.stage = new Container()); + this.renderer = await autoDetectRenderer(options); + _Application._plugins.forEach((plugin) => { + plugin.init.call(this, options); + }); + } + /** + * Renders the current stage to the screen. + * + * When using the default setup with {@link TickerPlugin} (enabled by default), you typically don't need to call + * this method directly as rendering is handled automatically. + * + * Only use this method if you've disabled the {@link TickerPlugin} or need custom + * render timing control. + * @example + * ```js + * // Example 1: Default setup (TickerPlugin handles rendering) + * const app = new Application(); + * await app.init(); + * // No need to call render() - TickerPlugin handles it + * + * // Example 2: Custom rendering loop (if TickerPlugin is disabled) + * const app = new Application(); + * await app.init({ autoStart: false }); // Disable automatic rendering + * + * function animate() { + * app.render(); + * requestAnimationFrame(animate); + * } + * animate(); + * ``` + */ + render() { + this.renderer.render({ container: this.stage }); + } + /** + * Reference to the renderer's canvas element. This is the HTML element + * that displays your application's graphics. + * @readonly + * @type {HTMLCanvasElement} + * @example + * ```js + * // Create a new application + * const app = new Application(); + * // Initialize the application + * await app.init({...}); + * // Add canvas to the page + * document.body.appendChild(app.canvas); + * + * // Access the canvas directly + * console.log(app.canvas); // HTMLCanvasElement + * ``` + */ + get canvas() { + return this.renderer.canvas; + } + /** + * Reference to the renderer's canvas element. + * @type {HTMLCanvasElement} + * @deprecated since 8.0.0 + * @see {@link Application#canvas} + */ + get view() { + deprecation(v8_0_0, "Application.view is deprecated, please use Application.canvas instead."); + return this.renderer.canvas; + } + /** + * Reference to the renderer's screen rectangle. This represents the visible area of your application. + * + * It's commonly used for: + * - Setting filter areas for full-screen effects + * - Defining hit areas for screen-wide interaction + * - Determining the visible bounds of your application + * @readonly + * @example + * ```js + * // Use as filter area for a full-screen effect + * const blurFilter = new BlurFilter(); + * sprite.filterArea = app.screen; + * + * // Use as hit area for screen-wide interaction + * const screenSprite = new Sprite(); + * screenSprite.hitArea = app.screen; + * + * // Get screen dimensions + * console.log(app.screen.width, app.screen.height); + * ``` + * @see {@link Rectangle} For all available properties and methods + */ + get screen() { + return this.renderer.screen; + } + /** + * Destroys the application and all of its resources. + * + * This method should be called when you want to completely + * clean up the application and free all associated memory. + * @param rendererDestroyOptions - Options for destroying the renderer: + * - `false` or `undefined`: Preserves the canvas element (default) + * - `true`: Removes the canvas element + * - `{ removeView: boolean }`: Object with removeView property to control canvas removal + * @param options - Options for destroying the application: + * - `false` or `undefined`: Basic cleanup (default) + * - `true`: Complete cleanup including children + * - Detailed options object: + * - `children`: Remove children + * - `texture`: Destroy textures + * - `textureSource`: Destroy texture sources + * - `context`: Destroy WebGL context + * @example + * ```js + * // Basic cleanup + * app.destroy(); + * + * // Remove canvas and do complete cleanup + * app.destroy(true, true); + * + * // Remove canvas with explicit options + * app.destroy({ removeView: true }, true); + * + * // Detailed cleanup with specific options + * app.destroy( + * { removeView: true }, + * { + * children: true, + * texture: true, + * textureSource: true, + * context: true + * } + * ); + * ``` + * > [!WARNING] After calling destroy, the application instance should no longer be used. + * > All properties will be null and further operations will throw errors. + */ + destroy(rendererDestroyOptions = false, options = false) { + const plugins = _Application._plugins.slice(0); + plugins.reverse(); + plugins.forEach((plugin) => { + plugin.destroy.call(this); + }); + this.stage.destroy(options); + this.stage = null; + this.renderer.destroy(rendererDestroyOptions); + this.renderer = null; + } + }; + /** + * Collection of installed plugins. + * @internal + */ + _Application._plugins = []; + let Application = _Application; + extensions.handleByList(ExtensionType.Application, Application._plugins); + extensions.add(ApplicationInitHook); + + "use strict"; + + "use strict"; + + "use strict"; + const bitmapFontTextParser = { + test(data) { + return typeof data === "string" && data.startsWith("info face="); + }, + parse(txt) { + var _a, _b, _c; + const items = txt.match(/^[a-z]+\s+.+$/gm); + const rawData = { + info: [], + common: [], + page: [], + char: [], + chars: [], + kerning: [], + kernings: [], + distanceField: [] + }; + for (const i in items) { + const name = items[i].match(/^[a-z]+/gm)[0]; + const attributeList = items[i].match(/[a-zA-Z]+=([^\s"']+|"([^"]*)")/gm); + const itemData = {}; + for (const i2 in attributeList) { + const split = attributeList[i2].split("="); + const key = split[0]; + const strValue = split[1].replace(/"/gm, ""); + const floatValue = parseFloat(strValue); + const value = isNaN(floatValue) ? strValue : floatValue; + itemData[key] = value; + } + rawData[name].push(itemData); + } + const font = { + chars: {}, + pages: [], + lineHeight: 0, + fontSize: 0, + fontFamily: "", + distanceField: null, + baseLineOffset: 0 + }; + const [info] = rawData.info; + const [common] = rawData.common; + const [distanceField] = (_a = rawData.distanceField) != null ? _a : []; + if (distanceField) { + font.distanceField = { + range: parseInt(distanceField.distanceRange, 10), + type: distanceField.fieldType + }; + } + font.fontSize = parseInt(info.size, 10); + font.fontFamily = info.face; + font.lineHeight = parseInt(common.lineHeight, 10); + const page = rawData.page; + for (let i = 0; i < page.length; i++) { + font.pages.push({ + id: parseInt(page[i].id, 10) || 0, + file: page[i].file + }); + } + const map = {}; + font.baseLineOffset = font.lineHeight - parseInt(common.base, 10); + const char = rawData.char; + for (let i = 0; i < char.length; i++) { + const charNode = char[i]; + const id = parseInt(charNode.id, 10); + let letter = (_c = (_b = charNode.letter) != null ? _b : charNode.char) != null ? _c : String.fromCharCode(id); + if (letter === "space") letter = " "; + map[id] = letter; + font.chars[letter] = { + id, + // texture deets.. + page: parseInt(charNode.page, 10) || 0, + x: parseInt(charNode.x, 10), + y: parseInt(charNode.y, 10), + width: parseInt(charNode.width, 10), + height: parseInt(charNode.height, 10), + xOffset: parseInt(charNode.xoffset, 10), + yOffset: parseInt(charNode.yoffset, 10), + xAdvance: parseInt(charNode.xadvance, 10), + kerning: {} + }; + } + const kerning = rawData.kerning || []; + for (let i = 0; i < kerning.length; i++) { + const first = parseInt(kerning[i].first, 10); + const second = parseInt(kerning[i].second, 10); + const amount = parseInt(kerning[i].amount, 10); + font.chars[map[second]].kerning[map[first]] = amount; + } + return font; + } + }; + + "use strict"; + const bitmapFontXMLParser = { + test(data) { + const xml = data; + return typeof xml !== "string" && "getElementsByTagName" in xml && xml.getElementsByTagName("page").length && xml.getElementsByTagName("info")[0].getAttribute("face") !== null; + }, + parse(xml) { + var _a, _b; + const data = { + chars: {}, + pages: [], + lineHeight: 0, + fontSize: 0, + fontFamily: "", + distanceField: null, + baseLineOffset: 0 + }; + const info = xml.getElementsByTagName("info")[0]; + const common = xml.getElementsByTagName("common")[0]; + const distanceField = xml.getElementsByTagName("distanceField")[0]; + if (distanceField) { + data.distanceField = { + type: distanceField.getAttribute("fieldType"), + range: parseInt(distanceField.getAttribute("distanceRange"), 10) + }; + } + const page = xml.getElementsByTagName("page"); + const char = xml.getElementsByTagName("char"); + const kerning = xml.getElementsByTagName("kerning"); + data.fontSize = parseInt(info.getAttribute("size"), 10); + data.fontFamily = info.getAttribute("face"); + data.lineHeight = parseInt(common.getAttribute("lineHeight"), 10); + for (let i = 0; i < page.length; i++) { + data.pages.push({ + id: parseInt(page[i].getAttribute("id"), 10) || 0, + file: page[i].getAttribute("file") + }); + } + const map = {}; + data.baseLineOffset = data.lineHeight - parseInt(common.getAttribute("base"), 10); + for (let i = 0; i < char.length; i++) { + const charNode = char[i]; + const id = parseInt(charNode.getAttribute("id"), 10); + let letter = (_b = (_a = charNode.getAttribute("letter")) != null ? _a : charNode.getAttribute("char")) != null ? _b : String.fromCharCode(id); + if (letter === "space") letter = " "; + map[id] = letter; + data.chars[letter] = { + id, + // texture deets.. + page: parseInt(charNode.getAttribute("page"), 10) || 0, + x: parseInt(charNode.getAttribute("x"), 10), + y: parseInt(charNode.getAttribute("y"), 10), + width: parseInt(charNode.getAttribute("width"), 10), + height: parseInt(charNode.getAttribute("height"), 10), + // render deets.. + xOffset: parseInt(charNode.getAttribute("xoffset"), 10), + yOffset: parseInt(charNode.getAttribute("yoffset"), 10), + // + baseLineOffset, + xAdvance: parseInt(charNode.getAttribute("xadvance"), 10), + kerning: {} + }; + } + for (let i = 0; i < kerning.length; i++) { + const first = parseInt(kerning[i].getAttribute("first"), 10); + const second = parseInt(kerning[i].getAttribute("second"), 10); + const amount = parseInt(kerning[i].getAttribute("amount"), 10); + data.chars[map[second]].kerning[map[first]] = amount; + } + return data; + } + }; + + "use strict"; + const bitmapFontXMLStringParser = { + test(data) { + if (typeof data === "string" && data.match(/)/)) { + return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data)); + } + return false; + }, + parse(data) { + return bitmapFontXMLParser.parse(DOMAdapter.get().parseXML(data)); + } + }; + + "use strict"; + const validExtensions = [".xml", ".fnt"]; + const bitmapFontCachePlugin = { + extension: { + type: ExtensionType.CacheParser, + name: "cacheBitmapFont" + }, + test: (asset) => !!(asset == null ? void 0 : asset.pages) && !!(asset == null ? void 0 : asset.chars) && typeof (asset == null ? void 0 : asset.fontFamily) === "string", + getCacheableAssets(keys, asset) { + const out = {}; + keys.forEach((key) => { + out[key] = asset; + out[`${key}-bitmap`] = asset; + }); + out[`${asset.fontFamily}-bitmap`] = asset; + return out; + } + }; + const loadBitmapFont = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Normal + }, + /** used for deprecation purposes */ + name: "loadBitmapFont", + id: "bitmap-font", + test(url) { + return validExtensions.includes(path.extname(url).toLowerCase()); + }, + async testParse(data) { + return bitmapFontTextParser.test(data) || bitmapFontXMLStringParser.test(data); + }, + async parse(asset, data, loader) { + const bitmapFontData = bitmapFontTextParser.test(asset) ? bitmapFontTextParser.parse(asset) : bitmapFontXMLStringParser.parse(asset); + const { src } = data; + const { pages } = bitmapFontData; + const textureUrls = []; + const textureOptions = bitmapFontData.distanceField ? { + scaleMode: "linear", + alphaMode: "premultiply-alpha-on-upload", + autoGenerateMipmaps: false, + resolution: 1 + } : {}; + for (let i = 0; i < pages.length; ++i) { + const pageFile = pages[i].file; + let imagePath = path.join(path.dirname(src), pageFile); + imagePath = copySearchParams(imagePath, src); + textureUrls.push({ + src: imagePath, + data: textureOptions + }); + } + const [loadedTextures, { BitmapFont }] = await Promise.all([ + loader.load(textureUrls), + Promise.resolve().then(function () { return BitmapFont$1; }) + ]); + const textures = textureUrls.map((url) => loadedTextures[url.src]); + const bitmapFont = new BitmapFont({ + data: bitmapFontData, + textures + }, src); + return bitmapFont; + }, + async load(url, _options) { + const response = await DOMAdapter.get().fetch(url); + return await response.text(); + }, + async unload(bitmapFont, _resolvedAsset, loader) { + await Promise.all(bitmapFont.pages.map((page) => loader.unload(page.texture.source._sourceOrigin))); + bitmapFont.destroy(); + } + }; + + "use strict"; + class BackgroundLoader { + /** + * @param loader + * @param verbose - should the loader log to the console + */ + constructor(loader, verbose = false) { + this._loader = loader; + this._assetList = []; + this._isLoading = false; + this._maxConcurrent = 1; + this.verbose = verbose; + } + /** + * Adds assets to the background loading queue. Assets are loaded one at a time to minimize + * performance impact. + * @param assetUrls - Array of resolved assets to load in the background + * @example + * ```ts + * // Add assets to background load queue + * backgroundLoader.add([ + * { src: 'images/level1/bg.png' }, + * { src: 'images/level1/characters.json' } + * ]); + * + * // Assets will load sequentially in the background + * // The loader automatically pauses when high-priority loads occur + * // e.g. Assets.load() is called + * ``` + * @remarks + * - Assets are loaded one at a time to minimize performance impact + * - Loading automatically pauses when Assets.load() is called + * - No progress tracking is available for background loading + * - Assets are cached as they complete loading + * @internal + */ + add(assetUrls) { + assetUrls.forEach((a) => { + this._assetList.push(a); + }); + if (this.verbose) { + console.log("[BackgroundLoader] assets: ", this._assetList); + } + if (this._isActive && !this._isLoading) { + void this._next(); + } + } + /** + * Loads the next set of assets. Will try to load as many assets as it can at the same time. + * + * The max assets it will try to load at one time will be 4. + */ + async _next() { + if (this._assetList.length && this._isActive) { + this._isLoading = true; + const toLoad = []; + const toLoadAmount = Math.min(this._assetList.length, this._maxConcurrent); + for (let i = 0; i < toLoadAmount; i++) { + toLoad.push(this._assetList.pop()); + } + await this._loader.load(toLoad); + this._isLoading = false; + void this._next(); + } + } + /** + * Controls the active state of the background loader. When active, the loader will + * continue processing its queue. When inactive, loading is paused. + * @returns Whether the background loader is currently active + * @example + * ```ts + * // Pause background loading + * backgroundLoader.active = false; + * + * // Resume background loading + * backgroundLoader.active = true; + * + * // Check current state + * console.log(backgroundLoader.active); // true/false + * + * // Common use case: Pause during intensive operations + * backgroundLoader.active = false; // Pause background loading + * ... // Perform high-priority tasks + * backgroundLoader.active = true; // Resume background loading + * ``` + * @remarks + * - Setting to true resumes loading immediately + * - Setting to false pauses after current asset completes + * - Background loading is automatically paused during `Assets.load()` + * - Assets already being loaded will complete even when set to false + */ + get active() { + return this._isActive; + } + set active(value) { + if (this._isActive === value) return; + this._isActive = value; + if (value && !this._isLoading) { + void this._next(); + } + } + } + + "use strict"; + const cacheTextureArray = { + extension: { + type: ExtensionType.CacheParser, + name: "cacheTextureArray" + }, + test: (asset) => Array.isArray(asset) && asset.every((t) => t instanceof Texture), + getCacheableAssets: (keys, asset) => { + const out = {}; + keys.forEach((key) => { + asset.forEach((item, i) => { + out[key + (i === 0 ? "" : i + 1)] = item; + }); + }); + return out; + } + }; + + "use strict"; + async function testImageFormat(imageData) { + if ("Image" in globalThis) { + return new Promise((resolve) => { + const image = new Image(); + image.onload = () => { + resolve(true); + }; + image.onerror = () => { + resolve(false); + }; + image.src = imageData; + }); + } + if ("createImageBitmap" in globalThis && "fetch" in globalThis) { + try { + const blob = await (await fetch(imageData)).blob(); + await createImageBitmap(blob); + } catch (_e) { + return false; + } + return true; + } + return false; + } + + "use strict"; + const detectAvif = { + extension: { + type: ExtensionType.DetectionParser, + priority: 1 + }, + test: async () => testImageFormat( + // eslint-disable-next-line max-len + "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=" + ), + add: async (formats) => [...formats, "avif"], + remove: async (formats) => formats.filter((f) => f !== "avif") + }; + + "use strict"; + const imageFormats = ["png", "jpg", "jpeg"]; + const detectDefaults = { + extension: { + type: ExtensionType.DetectionParser, + priority: -1 + }, + test: () => Promise.resolve(true), + add: async (formats) => [...formats, ...imageFormats], + remove: async (formats) => formats.filter((f) => !imageFormats.includes(f)) + }; + + "use strict"; + const inWorker = "WorkerGlobalScope" in globalThis && globalThis instanceof globalThis.WorkerGlobalScope; + function testVideoFormat(mimeType) { + if (inWorker) { + return false; + } + const video = document.createElement("video"); + return video.canPlayType(mimeType) !== ""; + } + + "use strict"; + const detectMp4 = { + extension: { + type: ExtensionType.DetectionParser, + priority: 0 + }, + test: async () => testVideoFormat("video/mp4"), + add: async (formats) => [...formats, "mp4", "m4v"], + remove: async (formats) => formats.filter((f) => f !== "mp4" && f !== "m4v") + }; + + "use strict"; + const detectOgv = { + extension: { + type: ExtensionType.DetectionParser, + priority: 0 + }, + test: async () => testVideoFormat("video/ogg"), + add: async (formats) => [...formats, "ogv"], + remove: async (formats) => formats.filter((f) => f !== "ogv") + }; + + "use strict"; + const detectWebm = { + extension: { + type: ExtensionType.DetectionParser, + priority: 0 + }, + test: async () => testVideoFormat("video/webm"), + add: async (formats) => [...formats, "webm"], + remove: async (formats) => formats.filter((f) => f !== "webm") + }; + + "use strict"; + const detectWebp = { + extension: { + type: ExtensionType.DetectionParser, + priority: 0 + }, + test: async () => testImageFormat( + "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=" + ), + add: async (formats) => [...formats, "webp"], + remove: async (formats) => formats.filter((f) => f !== "webp") + }; + + "use strict"; + var __defProp$13 = Object.defineProperty; + var __defProps$u = Object.defineProperties; + var __getOwnPropDescs$u = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$15 = Object.getOwnPropertySymbols; + var __hasOwnProp$15 = Object.prototype.hasOwnProperty; + var __propIsEnum$15 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$13 = (obj, key, value) => key in obj ? __defProp$13(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$13 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$15.call(b, prop)) + __defNormalProp$13(a, prop, b[prop]); + if (__getOwnPropSymbols$15) + for (var prop of __getOwnPropSymbols$15(b)) { + if (__propIsEnum$15.call(b, prop)) + __defNormalProp$13(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$u = (a, b) => __defProps$u(a, __getOwnPropDescs$u(b)); + const _Loader = class _Loader { + constructor() { + /** + * Options for loading assets with the loader. + * These options will be used as defaults for all load calls made with this loader instance. + * They can be overridden by passing options directly to the load method. + * @example + * ```ts + * // Create a loader with custom default options + * const loader = new Loader(); + * loader.loadOptions = { + * strategy: 'skip', // Default strategy to 'skip' + * retryCount: 5, // Default retry count to 5 + * retryDelay: 500, // Default retry delay to 500ms + * }; + * + * // This load call will use the loader's default options + * await loader.load('image1.png'); + */ + this.loadOptions = __spreadValues$13({}, _Loader.defaultOptions); + this._parsers = []; + this._parsersValidated = false; + /** + * All loader parsers registered + * @type {assets.LoaderParser[]} + */ + this.parsers = new Proxy(this._parsers, { + set: (target, key, value) => { + this._parsersValidated = false; + target[key] = value; + return true; + } + }); + /** Cache loading promises that ae currently active */ + this.promiseCache = {}; + } + /** function used for testing */ + reset() { + this._parsersValidated = false; + this.promiseCache = {}; + } + /** + * Used internally to generate a promise for the asset to be loaded. + * @param url - The URL to be loaded + * @param data - any custom additional information relevant to the asset being loaded + * @returns - a promise that will resolve to an Asset for example a Texture of a JSON object + */ + _getLoadPromiseAndParser(url, data) { + const result = { + promise: null, + parser: null + }; + result.promise = (async () => { + var _a, _b; + let asset = null; + let parser = null; + if (data.parser || data.loadParser) { + parser = this._parserHash[data.parser || data.loadParser]; + if (data.loadParser) { + warn( + `[Assets] "loadParser" is deprecated, use "parser" instead for ${url}` + ); + } + if (!parser) { + warn( + `[Assets] specified load parser "${data.parser || data.loadParser}" not found while loading ${url}` + ); + } + } + if (!parser) { + for (let i = 0; i < this.parsers.length; i++) { + const parserX = this.parsers[i]; + if (parserX.load && ((_a = parserX.test) == null ? void 0 : _a.call(parserX, url, data, this))) { + parser = parserX; + break; + } + } + if (!parser) { + warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has been added`); + return null; + } + } + asset = await parser.load(url, data, this); + result.parser = parser; + for (let i = 0; i < this.parsers.length; i++) { + const parser2 = this.parsers[i]; + if (parser2.parse) { + if (parser2.parse && await ((_b = parser2.testParse) == null ? void 0 : _b.call(parser2, asset, data, this))) { + asset = await parser2.parse(asset, data, this) || asset; + result.parser = parser2; + } + } + } + return asset; + })(); + return result; + } + async load(assetsToLoadIn, onProgressOrOptions) { + if (!this._parsersValidated) { + this._validateParsers(); + } + const options = typeof onProgressOrOptions === "function" ? __spreadProps$u(__spreadValues$13(__spreadValues$13({}, _Loader.defaultOptions), this.loadOptions), { onProgress: onProgressOrOptions }) : __spreadValues$13(__spreadValues$13(__spreadValues$13({}, _Loader.defaultOptions), this.loadOptions), onProgressOrOptions || {}); + const { onProgress, onError, strategy, retryCount, retryDelay } = options; + let count = 0; + const assets = {}; + const singleAsset = isSingleItem(assetsToLoadIn); + const assetsToLoad = convertToList(assetsToLoadIn, (item) => ({ + alias: [item], + src: item, + data: {} + })); + const total = assetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0); + const promises = assetsToLoad.map(async (asset) => { + const url = path.toAbsolute(asset.src); + if (assets[asset.src]) return; + await this._loadAssetWithRetry(url, asset, { onProgress, onError, strategy, retryCount, retryDelay }, assets); + count += asset.progressSize || 1; + if (onProgress) onProgress(count / total); + }); + await Promise.all(promises); + return singleAsset ? assets[assetsToLoad[0].src] : assets; + } + /** + * Unloads one or more assets. Any unloaded assets will be destroyed, freeing up memory for your app. + * The parser that created the asset, will be the one that unloads it. + * @example + * // Single asset: + * const asset = await Loader.load('cool.png'); + * + * await Loader.unload('cool.png'); + * + * console.log(asset.destroyed); // true + * @param assetsToUnloadIn - urls that you want to unload, or a single one! + */ + async unload(assetsToUnloadIn) { + const assetsToUnload = convertToList(assetsToUnloadIn, (item) => ({ + alias: [item], + src: item + })); + const promises = assetsToUnload.map(async (asset) => { + var _a, _b; + const url = path.toAbsolute(asset.src); + const loadPromise = this.promiseCache[url]; + if (loadPromise) { + const loadedAsset = await loadPromise.promise; + delete this.promiseCache[url]; + await ((_b = (_a = loadPromise.parser) == null ? void 0 : _a.unload) == null ? void 0 : _b.call(_a, loadedAsset, asset, this)); + } + }); + await Promise.all(promises); + } + /** validates our parsers, right now it only checks for name conflicts but we can add more here as required! */ + _validateParsers() { + this._parsersValidated = true; + this._parserHash = this._parsers.filter((parser) => parser.name || parser.id).reduce((hash, parser) => { + if (!parser.name && !parser.id) { + warn(`[Assets] parser should have an id`); + } else if (hash[parser.name] || hash[parser.id]) { + warn(`[Assets] parser id conflict "${parser.id}"`); + } + hash[parser.name] = parser; + if (parser.id) hash[parser.id] = parser; + return hash; + }, {}); + } + async _loadAssetWithRetry(url, asset, options, assets) { + let attempt = 0; + const { onError, strategy, retryCount, retryDelay } = options; + const wait = (ms) => new Promise((r) => setTimeout(r, ms)); + while (true) { + try { + if (!this.promiseCache[url]) { + this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset); + } + assets[asset.src] = await this.promiseCache[url].promise; + return; + } catch (e) { + delete this.promiseCache[url]; + delete assets[asset.src]; + attempt++; + const isLast = strategy !== "retry" || attempt > retryCount; + if (strategy === "retry" && !isLast) { + if (onError) onError(e, asset); + await wait(retryDelay); + continue; + } + if (strategy === "skip") { + if (onError) onError(e, asset); + return; + } + if (onError) onError(e, asset); + const error = new Error(`[Loader.load] Failed to load ${url}. +${e}`); + if (e instanceof Error && e.stack) { + error.stack = e.stack; + } + throw error; + } + } + } + }; + /** + * Default options for loading assets + * @example + * ```ts + * // Change default load options globally + * Loader.defaultOptions = { + * strategy: 'skip', // Change default strategy to 'skip' + * retryCount: 5, // Change default retry count to 5 + * retryDelay: 500, // Change default retry delay to 500ms + * }; + * ``` + */ + _Loader.defaultOptions = { + onProgress: void 0, + onError: void 0, + strategy: "throw", + retryCount: 3, + retryDelay: 250 + }; + let Loader = _Loader; + + "use strict"; + function checkDataUrl(url, mimes) { + if (Array.isArray(mimes)) { + for (const mime of mimes) { + if (url.startsWith(`data:${mime}`)) return true; + } + return false; + } + return url.startsWith(`data:${mimes}`); + } + + "use strict"; + function checkExtension(url, extension) { + const tempURL = url.split("?")[0]; + const ext = path.extname(tempURL).toLowerCase(); + if (Array.isArray(extension)) { + return extension.includes(ext); + } + return ext === extension; + } + + "use strict"; + const validJSONExtension = ".json"; + const validJSONMIME = "application/json"; + const loadJson = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Low + }, + /** used for deprecation purposes */ + name: "loadJson", + id: "json", + test(url) { + return checkDataUrl(url, validJSONMIME) || checkExtension(url, validJSONExtension); + }, + async load(url) { + const response = await DOMAdapter.get().fetch(url); + const json = await response.json(); + return json; + } + }; + + "use strict"; + const validTXTExtension = ".txt"; + const validTXTMIME = "text/plain"; + const loadTxt = { + /** used for deprecation purposes */ + name: "loadTxt", + id: "text", + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Low, + name: "loadTxt" + }, + test(url) { + return checkDataUrl(url, validTXTMIME) || checkExtension(url, validTXTExtension); + }, + async load(url) { + const response = await DOMAdapter.get().fetch(url); + const txt = await response.text(); + return txt; + } + }; + + "use strict"; + var __defProp$12 = Object.defineProperty; + var __defProps$t = Object.defineProperties; + var __getOwnPropDescs$t = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$14 = Object.getOwnPropertySymbols; + var __hasOwnProp$14 = Object.prototype.hasOwnProperty; + var __propIsEnum$14 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$12 = (obj, key, value) => key in obj ? __defProp$12(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$12 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$14.call(b, prop)) + __defNormalProp$12(a, prop, b[prop]); + if (__getOwnPropSymbols$14) + for (var prop of __getOwnPropSymbols$14(b)) { + if (__propIsEnum$14.call(b, prop)) + __defNormalProp$12(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$t = (a, b) => __defProps$t(a, __getOwnPropDescs$t(b)); + const validWeights = [ + "normal", + "bold", + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900" + ]; + const validFontExtensions = [".ttf", ".otf", ".woff", ".woff2"]; + const validFontMIMEs = [ + "font/ttf", + "font/otf", + "font/woff", + "font/woff2" + ]; + const CSS_IDENT_TOKEN_REGEX = /^(--|-?[A-Z_])[0-9A-Z_-]*$/i; + function getFontFamilyName(url) { + const ext = path.extname(url); + const name = path.basename(url, ext); + const nameWithSpaces = name.replace(/(-|_)/g, " "); + const nameTokens = nameWithSpaces.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)); + let valid = nameTokens.length > 0; + for (const token of nameTokens) { + if (!token.match(CSS_IDENT_TOKEN_REGEX)) { + valid = false; + break; + } + } + let fontFamilyName = nameTokens.join(" "); + if (!valid) { + fontFamilyName = `"${fontFamilyName.replace(/[\\"]/g, "\\$&")}"`; + } + return fontFamilyName; + } + const validURICharactersRegex = /^[0-9A-Za-z%:/?#\[\]@!\$&'()\*\+,;=\-._~]*$/; + function encodeURIWhenNeeded(uri) { + if (validURICharactersRegex.test(uri)) { + return uri; + } + return encodeURI(uri); + } + const loadWebFont = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Low + }, + /** used for deprecation purposes */ + name: "loadWebFont", + id: "web-font", + test(url) { + return checkDataUrl(url, validFontMIMEs) || checkExtension(url, validFontExtensions); + }, + async load(url, options) { + var _a, _b, _c, _d, _e, _f; + const fonts = DOMAdapter.get().getFontFaceSet(); + if (fonts) { + const fontFaces = []; + const name = (_b = (_a = options.data) == null ? void 0 : _a.family) != null ? _b : getFontFamilyName(url); + const weights = (_e = (_d = (_c = options.data) == null ? void 0 : _c.weights) == null ? void 0 : _d.filter((weight) => validWeights.includes(weight))) != null ? _e : ["normal"]; + const data = (_f = options.data) != null ? _f : {}; + for (let i = 0; i < weights.length; i++) { + const weight = weights[i]; + const font = new FontFace(name, `url(${encodeURIWhenNeeded(url)})`, __spreadProps$t(__spreadValues$12({}, data), { + weight + })); + await font.load(); + fonts.add(font); + fontFaces.push(font); + } + if (Cache.has(`${name}-and-url`)) { + const cached = Cache.get(`${name}-and-url`); + cached.entries.push({ url, faces: fontFaces }); + } else { + Cache.set(`${name}-and-url`, { + entries: [{ url, faces: fontFaces }] + }); + } + return fontFaces.length === 1 ? fontFaces[0] : fontFaces; + } + warn("[loadWebFont] FontFace API is not supported. Skipping loading font"); + return null; + }, + unload(font) { + const fonts = Array.isArray(font) ? font : [font]; + const fontFamily = fonts[0].family; + const cached = Cache.get(`${fontFamily}-and-url`); + const entry = cached.entries.find((f) => f.faces.some((t) => fonts.indexOf(t) !== -1)); + entry.faces = entry.faces.filter((f) => fonts.indexOf(f) === -1); + if (entry.faces.length === 0) { + cached.entries = cached.entries.filter((f) => f !== entry); + } + fonts.forEach((t) => { + DOMAdapter.get().getFontFaceSet().delete(t); + }); + if (cached.entries.length === 0) { + Cache.remove(`${fontFamily}-and-url`); + } + } + }; + + var parseSvgPath; + var hasRequiredParseSvgPath; + + function requireParseSvgPath () { + if (hasRequiredParseSvgPath) return parseSvgPath; + hasRequiredParseSvgPath = 1; + parseSvgPath = parse; + + /** + * expected argument lengths + * @type {Object} + */ + + var length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}; + + /** + * segment pattern + * @type {RegExp} + */ + + var segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig; + + /** + * parse an svg path data string. Generates an Array + * of commands where each command is an Array of the + * form `[command, arg1, arg2, ...]` + * + * @param {String} path + * @return {Array} + */ + + function parse(path) { + var data = []; + path.replace(segment, function(_, command, args){ + var type = command.toLowerCase(); + args = parseValues(args); + + // overloaded moveTo + if (type == 'm' && args.length > 2) { + data.push([command].concat(args.splice(0, 2))); + type = 'l'; + command = command == 'm' ? 'l' : 'L'; + } + + while (true) { + if (args.length == length[type]) { + args.unshift(command); + return data.push(args) + } + if (args.length < length[type]) throw new Error('malformed path data') + data.push([command].concat(args.splice(0, length[type]))); + } + }); + return data + } + + var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig; + + function parseValues(args) { + var numbers = args.match(number); + return numbers ? numbers.map(Number) : [] + } + return parseSvgPath; + } + + var parseSvgPathExports = requireParseSvgPath(); + var parse = /*@__PURE__*/getDefaultExportFromCjs(parseSvgPathExports); + + "use strict"; + function parseSVGPath(svgPath, path) { + const commands = parse(svgPath); + const subpaths = []; + let currentSubPath = null; + let lastX = 0; + let lastY = 0; + for (let i = 0; i < commands.length; i++) { + const command = commands[i]; + const type = command[0]; + const data = command; + switch (type) { + case "M": + lastX = data[1]; + lastY = data[2]; + path.moveTo(lastX, lastY); + break; + case "m": + lastX += data[1]; + lastY += data[2]; + path.moveTo(lastX, lastY); + break; + case "H": + lastX = data[1]; + path.lineTo(lastX, lastY); + break; + case "h": + lastX += data[1]; + path.lineTo(lastX, lastY); + break; + case "V": + lastY = data[1]; + path.lineTo(lastX, lastY); + break; + case "v": + lastY += data[1]; + path.lineTo(lastX, lastY); + break; + case "L": + lastX = data[1]; + lastY = data[2]; + path.lineTo(lastX, lastY); + break; + case "l": + lastX += data[1]; + lastY += data[2]; + path.lineTo(lastX, lastY); + break; + case "C": + lastX = data[5]; + lastY = data[6]; + path.bezierCurveTo( + data[1], + data[2], + // First control point + data[3], + data[4], + // Second control point + lastX, + lastY + // End point + ); + break; + case "c": + path.bezierCurveTo( + lastX + data[1], + lastY + data[2], + // First control point + lastX + data[3], + lastY + data[4], + // Second control point + lastX + data[5], + lastY + data[6] + // End point + ); + lastX += data[5]; + lastY += data[6]; + break; + case "S": + lastX = data[3]; + lastY = data[4]; + path.bezierCurveToShort( + data[1], + data[2], + // Control point + lastX, + lastY + // End point + ); + break; + case "s": + path.bezierCurveToShort( + lastX + data[1], + lastY + data[2], + // Control point + lastX + data[3], + lastY + data[4] + // End point + ); + lastX += data[3]; + lastY += data[4]; + break; + case "Q": + lastX = data[3]; + lastY = data[4]; + path.quadraticCurveTo( + data[1], + data[2], + // Control point + lastX, + lastY + // End point + ); + break; + case "q": + path.quadraticCurveTo( + lastX + data[1], + lastY + data[2], + // Control point + lastX + data[3], + lastY + data[4] + // End point + ); + lastX += data[3]; + lastY += data[4]; + break; + case "T": + lastX = data[1]; + lastY = data[2]; + path.quadraticCurveToShort( + lastX, + lastY + // End point + ); + break; + case "t": + lastX += data[1]; + lastY += data[2]; + path.quadraticCurveToShort( + lastX, + lastY + // End point + ); + break; + case "A": + lastX = data[6]; + lastY = data[7]; + path.arcToSvg( + data[1], + // rx + data[2], + // ry + data[3], + // x-axis-rotation + data[4], + // large-arc-flag + data[5], + // sweep-flag + lastX, + lastY + // End point + ); + break; + case "a": + lastX += data[6]; + lastY += data[7]; + path.arcToSvg( + data[1], + // rx + data[2], + // ry + data[3], + // x-axis-rotation + data[4], + // large-arc-flag + data[5], + // sweep-flag + lastX, + lastY + // End point + ); + break; + case "Z": + // Close Path + case "z": + path.closePath(); + if (subpaths.length > 0) { + currentSubPath = subpaths.pop(); + if (currentSubPath) { + lastX = currentSubPath.startX; + lastY = currentSubPath.startY; + } else { + lastX = 0; + lastY = 0; + } + } + currentSubPath = null; + break; + default: + warn(`Unknown SVG path command: ${type}`); + } + if (type !== "Z" && type !== "z") { + if (currentSubPath === null) { + currentSubPath = { startX: lastX, startY: lastY }; + subpaths.push(currentSubPath); + } + } + } + return path; + } + + "use strict"; + const cachedGroups = {}; + function getTextureBatchBindGroup(textures, size, maxTextures) { + let uid = 2166136261; + for (let i = 0; i < size; i++) { + uid ^= textures[i].uid; + uid = Math.imul(uid, 16777619); + uid >>>= 0; + } + return cachedGroups[uid] || generateTextureBatchBindGroup(textures, size, uid, maxTextures); + } + function generateTextureBatchBindGroup(textures, size, key, maxTextures) { + const bindGroupResources = {}; + let bindIndex = 0; + for (let i = 0; i < maxTextures; i++) { + const texture = i < size ? textures[i] : Texture.EMPTY.source; + bindGroupResources[bindIndex++] = texture.source; + bindGroupResources[bindIndex++] = texture.style; + } + const bindGroup = new BindGroup(bindGroupResources); + cachedGroups[key] = bindGroup; + return bindGroup; + } + + "use strict"; + class ViewableBuffer { + constructor(sizeOrBuffer) { + if (typeof sizeOrBuffer === "number") { + this.rawBinaryData = new ArrayBuffer(sizeOrBuffer); + } else if (sizeOrBuffer instanceof Uint8Array) { + this.rawBinaryData = sizeOrBuffer.buffer; + } else { + this.rawBinaryData = sizeOrBuffer; + } + this.uint32View = new Uint32Array(this.rawBinaryData); + this.float32View = new Float32Array(this.rawBinaryData); + this.size = this.rawBinaryData.byteLength; + } + /** View on the raw binary data as a `Int8Array`. */ + get int8View() { + if (!this._int8View) { + this._int8View = new Int8Array(this.rawBinaryData); + } + return this._int8View; + } + /** View on the raw binary data as a `Uint8Array`. */ + get uint8View() { + if (!this._uint8View) { + this._uint8View = new Uint8Array(this.rawBinaryData); + } + return this._uint8View; + } + /** View on the raw binary data as a `Int16Array`. */ + get int16View() { + if (!this._int16View) { + this._int16View = new Int16Array(this.rawBinaryData); + } + return this._int16View; + } + /** View on the raw binary data as a `Int32Array`. */ + get int32View() { + if (!this._int32View) { + this._int32View = new Int32Array(this.rawBinaryData); + } + return this._int32View; + } + /** View on the raw binary data as a `Float64Array`. */ + get float64View() { + if (!this._float64Array) { + this._float64Array = new Float64Array(this.rawBinaryData); + } + return this._float64Array; + } + /** View on the raw binary data as a `BigUint64Array`. */ + get bigUint64View() { + if (!this._bigUint64Array) { + this._bigUint64Array = new BigUint64Array(this.rawBinaryData); + } + return this._bigUint64Array; + } + /** + * Returns the view of the given type. + * @param type - One of `int8`, `uint8`, `int16`, + * `uint16`, `int32`, `uint32`, and `float32`. + * @returns - typed array of given type + */ + view(type) { + return this[`${type}View`]; + } + /** Destroys all buffer references. Do not use after calling this. */ + destroy() { + this.rawBinaryData = null; + this.uint32View = null; + this.float32View = null; + this.uint16View = null; + this._int8View = null; + this._uint8View = null; + this._int16View = null; + this._int32View = null; + this._float64Array = null; + this._bigUint64Array = null; + } + /** + * Returns the size of the given type in bytes. + * @param type - One of `int8`, `uint8`, `int16`, + * `uint16`, `int32`, `uint32`, and `float32`. + * @returns - size of the type in bytes + */ + static sizeOf(type) { + switch (type) { + case "int8": + case "uint8": + return 1; + case "int16": + case "uint16": + return 2; + case "int32": + case "uint32": + case "float32": + return 4; + default: + throw new Error(`${type} isn't a valid view type`); + } + } + } + + "use strict"; + function fastCopy(sourceBuffer, destinationBuffer, sourceOffset, byteLength) { + sourceOffset != null ? sourceOffset : sourceOffset = 0; + byteLength != null ? byteLength : byteLength = Math.min(sourceBuffer.byteLength - sourceOffset, destinationBuffer.byteLength); + if (!(sourceOffset & 7) && !(byteLength & 7)) { + const len = byteLength / 8; + new Float64Array(destinationBuffer, 0, len).set(new Float64Array(sourceBuffer, sourceOffset, len)); + } else if (!(sourceOffset & 3) && !(byteLength & 3)) { + const len = byteLength / 4; + new Float32Array(destinationBuffer, 0, len).set(new Float32Array(sourceBuffer, sourceOffset, len)); + } else { + new Uint8Array(destinationBuffer).set(new Uint8Array(sourceBuffer, sourceOffset, byteLength)); + } + } + + "use strict"; + const BLEND_TO_NPM = { + normal: "normal-npm", + add: "add-npm", + screen: "screen-npm" + }; + var STENCIL_MODES = /* @__PURE__ */ ((STENCIL_MODES2) => { + STENCIL_MODES2[STENCIL_MODES2["DISABLED"] = 0] = "DISABLED"; + STENCIL_MODES2[STENCIL_MODES2["RENDERING_MASK_ADD"] = 1] = "RENDERING_MASK_ADD"; + STENCIL_MODES2[STENCIL_MODES2["MASK_ACTIVE"] = 2] = "MASK_ACTIVE"; + STENCIL_MODES2[STENCIL_MODES2["INVERSE_MASK_ACTIVE"] = 3] = "INVERSE_MASK_ACTIVE"; + STENCIL_MODES2[STENCIL_MODES2["RENDERING_MASK_REMOVE"] = 4] = "RENDERING_MASK_REMOVE"; + STENCIL_MODES2[STENCIL_MODES2["NONE"] = 5] = "NONE"; + return STENCIL_MODES2; + })(STENCIL_MODES || {}); + + "use strict"; + function getAdjustedBlendModeBlend(blendMode, textureSource) { + if (textureSource.alphaMode === "no-premultiply-alpha") { + return BLEND_TO_NPM[blendMode] || blendMode; + } + return blendMode; + } + + "use strict"; + const fragTemplate$1 = [ + "precision mediump float;", + "void main(void){", + "float test = 0.1;", + "%forloop%", + "gl_FragColor = vec4(0.0);", + "}" + ].join("\n"); + function generateIfTestSrc(maxIfs) { + let src = ""; + for (let i = 0; i < maxIfs; ++i) { + if (i > 0) { + src += "\nelse "; + } + if (i < maxIfs - 1) { + src += `if(test == ${i}.0){}`; + } + } + return src; + } + function checkMaxIfStatementsInShader(maxIfs, gl) { + if (maxIfs === 0) { + throw new Error("Invalid value of `0` passed to `checkMaxIfStatementsInShader`"); + } + const shader = gl.createShader(gl.FRAGMENT_SHADER); + try { + while (true) { + const fragmentSrc = fragTemplate$1.replace(/%forloop%/gi, generateIfTestSrc(maxIfs)); + gl.shaderSource(shader, fragmentSrc); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + maxIfs = maxIfs / 2 | 0; + } else { + break; + } + } + } finally { + gl.deleteShader(shader); + } + return maxIfs; + } + + "use strict"; + let maxTexturesPerBatchCache = null; + function getMaxTexturesPerBatch() { + var _a; + if (maxTexturesPerBatchCache) return maxTexturesPerBatchCache; + const gl = getTestContext(); + maxTexturesPerBatchCache = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + maxTexturesPerBatchCache = checkMaxIfStatementsInShader( + maxTexturesPerBatchCache, + gl + ); + (_a = gl.getExtension("WEBGL_lose_context")) == null ? void 0 : _a.loseContext(); + return maxTexturesPerBatchCache; + } + + "use strict"; + class BatchTextureArray { + constructor() { + /** Respective locations for textures. */ + this.ids = /* @__PURE__ */ Object.create(null); + this.textures = []; + this.count = 0; + } + /** Clear the textures and their locations. */ + clear() { + for (let i = 0; i < this.count; i++) { + const t = this.textures[i]; + this.textures[i] = null; + this.ids[t.uid] = null; + } + this.count = 0; + } + } + + "use strict"; + var __defProp$11 = Object.defineProperty; + var __getOwnPropSymbols$13 = Object.getOwnPropertySymbols; + var __hasOwnProp$13 = Object.prototype.hasOwnProperty; + var __propIsEnum$13 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$11 = (obj, key, value) => key in obj ? __defProp$11(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$11 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$13.call(b, prop)) + __defNormalProp$11(a, prop, b[prop]); + if (__getOwnPropSymbols$13) + for (var prop of __getOwnPropSymbols$13(b)) { + if (__propIsEnum$13.call(b, prop)) + __defNormalProp$11(a, prop, b[prop]); + } + return a; + }; + class Batch { + constructor() { + this.renderPipeId = "batch"; + this.action = "startBatch"; + // TODO - eventually this could be useful for flagging batches as dirty and then only rebuilding those ones + // public elementStart = 0; + // public elementSize = 0; + // for drawing.. + this.start = 0; + this.size = 0; + this.textures = new BatchTextureArray(); + this.blendMode = "normal"; + this.topology = "triangle-strip"; + this.canBundle = true; + } + destroy() { + this.textures = null; + this.gpuBindGroup = null; + this.bindGroup = null; + this.batcher = null; + this.elements = null; + } + } + const batchPool = []; + let batchPoolIndex = 0; + GlobalResourceRegistry.register({ + clear: () => { + if (batchPool.length > 0) { + for (const item of batchPool) { + if (item) item.destroy(); + } + } + batchPool.length = 0; + batchPoolIndex = 0; + } + }); + function getBatchFromPool() { + return batchPoolIndex > 0 ? batchPool[--batchPoolIndex] : new Batch(); + } + function returnBatchToPool(batch) { + batch.elements = null; + batchPool[batchPoolIndex++] = batch; + } + let BATCH_TICK = 0; + const _Batcher = class _Batcher { + constructor(options) { + /** unique id for this batcher */ + this.uid = uid$1("batcher"); + /** Indicates whether the batch data has been modified and needs updating. */ + this.dirty = true; + /** The current index of the batch being processed. */ + this.batchIndex = 0; + /** An array of all batches created during the current rendering process. */ + this.batches = []; + this._elements = []; + options = __spreadValues$11(__spreadValues$11({}, _Batcher.defaultOptions), options); + if (!options.maxTextures) { + deprecation("v8.8.0", "maxTextures is a required option for Batcher now, please pass it in the options"); + options.maxTextures = getMaxTexturesPerBatch(); + } + const { maxTextures, attributesInitialSize, indicesInitialSize } = options; + this.attributeBuffer = new ViewableBuffer(attributesInitialSize * 4); + this.indexBuffer = new Uint16Array(indicesInitialSize); + this.maxTextures = maxTextures; + } + begin() { + this.elementSize = 0; + this.elementStart = 0; + this.indexSize = 0; + this.attributeSize = 0; + for (let i = 0; i < this.batchIndex; i++) { + returnBatchToPool(this.batches[i]); + } + this.batchIndex = 0; + this._batchIndexStart = 0; + this._batchIndexSize = 0; + this.dirty = true; + } + add(batchableObject) { + this._elements[this.elementSize++] = batchableObject; + batchableObject._indexStart = this.indexSize; + batchableObject._attributeStart = this.attributeSize; + batchableObject._batcher = this; + this.indexSize += batchableObject.indexSize; + this.attributeSize += batchableObject.attributeSize * this.vertexSize; + } + checkAndUpdateTexture(batchableObject, texture) { + const textureId = batchableObject._batch.textures.ids[texture._source.uid]; + if (!textureId && textureId !== 0) return false; + batchableObject._textureId = textureId; + batchableObject.texture = texture; + return true; + } + updateElement(batchableObject) { + this.dirty = true; + const attributeBuffer = this.attributeBuffer; + if (batchableObject.packAsQuad) { + this.packQuadAttributes( + batchableObject, + attributeBuffer.float32View, + attributeBuffer.uint32View, + batchableObject._attributeStart, + batchableObject._textureId + ); + } else { + this.packAttributes( + batchableObject, + attributeBuffer.float32View, + attributeBuffer.uint32View, + batchableObject._attributeStart, + batchableObject._textureId + ); + } + } + /** + * breaks the batcher. This happens when a batch gets too big, + * or we need to switch to a different type of rendering (a filter for example) + * @param instructionSet + */ + break(instructionSet) { + const elements = this._elements; + if (!elements[this.elementStart]) return; + let batch = getBatchFromPool(); + let textureBatch = batch.textures; + textureBatch.clear(); + const firstElement = elements[this.elementStart]; + let blendMode = getAdjustedBlendModeBlend(firstElement.blendMode, firstElement.texture._source); + let topology = firstElement.topology; + if (this.attributeSize * 4 > this.attributeBuffer.size) { + this._resizeAttributeBuffer(this.attributeSize * 4); + } + if (this.indexSize > this.indexBuffer.length) { + this._resizeIndexBuffer(this.indexSize); + } + const f32 = this.attributeBuffer.float32View; + const u32 = this.attributeBuffer.uint32View; + const indexBuffer = this.indexBuffer; + let size = this._batchIndexSize; + let start = this._batchIndexStart; + let action = "startBatch"; + let batchElements = []; + const maxTextures = this.maxTextures; + for (let i = this.elementStart; i < this.elementSize; ++i) { + const element = elements[i]; + elements[i] = null; + const texture = element.texture; + const source = texture._source; + const adjustedBlendMode = getAdjustedBlendModeBlend(element.blendMode, source); + const breakRequired = blendMode !== adjustedBlendMode || topology !== element.topology; + if (source._batchTick === BATCH_TICK && !breakRequired) { + element._textureId = source._textureBindLocation; + size += element.indexSize; + if (element.packAsQuad) { + this.packQuadAttributes( + element, + f32, + u32, + element._attributeStart, + element._textureId + ); + this.packQuadIndex( + indexBuffer, + element._indexStart, + element._attributeStart / this.vertexSize + ); + } else { + this.packAttributes( + element, + f32, + u32, + element._attributeStart, + element._textureId + ); + this.packIndex( + element, + indexBuffer, + element._indexStart, + element._attributeStart / this.vertexSize + ); + } + element._batch = batch; + batchElements.push(element); + continue; + } + source._batchTick = BATCH_TICK; + if (textureBatch.count >= maxTextures || breakRequired) { + this._finishBatch( + batch, + start, + size - start, + textureBatch, + blendMode, + topology, + instructionSet, + action, + batchElements + ); + action = "renderBatch"; + start = size; + blendMode = adjustedBlendMode; + topology = element.topology; + batch = getBatchFromPool(); + textureBatch = batch.textures; + textureBatch.clear(); + batchElements = []; + ++BATCH_TICK; + } + element._textureId = source._textureBindLocation = textureBatch.count; + textureBatch.ids[source.uid] = textureBatch.count; + textureBatch.textures[textureBatch.count++] = source; + element._batch = batch; + batchElements.push(element); + size += element.indexSize; + if (element.packAsQuad) { + this.packQuadAttributes( + element, + f32, + u32, + element._attributeStart, + element._textureId + ); + this.packQuadIndex( + indexBuffer, + element._indexStart, + element._attributeStart / this.vertexSize + ); + } else { + this.packAttributes( + element, + f32, + u32, + element._attributeStart, + element._textureId + ); + this.packIndex( + element, + indexBuffer, + element._indexStart, + element._attributeStart / this.vertexSize + ); + } + } + if (textureBatch.count > 0) { + this._finishBatch( + batch, + start, + size - start, + textureBatch, + blendMode, + topology, + instructionSet, + action, + batchElements + ); + start = size; + ++BATCH_TICK; + } + this.elementStart = this.elementSize; + this._batchIndexStart = start; + this._batchIndexSize = size; + } + _finishBatch(batch, indexStart, indexSize, textureBatch, blendMode, topology, instructionSet, action, elements) { + batch.gpuBindGroup = null; + batch.bindGroup = null; + batch.action = action; + batch.batcher = this; + batch.textures = textureBatch; + batch.blendMode = blendMode; + batch.topology = topology; + batch.start = indexStart; + batch.size = indexSize; + batch.elements = elements; + ++BATCH_TICK; + this.batches[this.batchIndex++] = batch; + instructionSet.add(batch); + } + finish(instructionSet) { + this.break(instructionSet); + } + /** + * Resizes the attribute buffer to the given size (1 = 1 float32) + * @param size - the size in vertices to ensure (not bytes!) + */ + ensureAttributeBuffer(size) { + if (size * 4 <= this.attributeBuffer.size) return; + this._resizeAttributeBuffer(size * 4); + } + /** + * Resizes the index buffer to the given size (1 = 1 float32) + * @param size - the size in vertices to ensure (not bytes!) + */ + ensureIndexBuffer(size) { + if (size <= this.indexBuffer.length) return; + this._resizeIndexBuffer(size); + } + _resizeAttributeBuffer(size) { + const newSize = Math.max(size, this.attributeBuffer.size * 2); + const newArrayBuffer = new ViewableBuffer(newSize); + fastCopy(this.attributeBuffer.rawBinaryData, newArrayBuffer.rawBinaryData); + this.attributeBuffer = newArrayBuffer; + } + _resizeIndexBuffer(size) { + const indexBuffer = this.indexBuffer; + let newSize = Math.max(size, indexBuffer.length * 1.5); + newSize += newSize % 2; + const newIndexBuffer = newSize > 65535 ? new Uint32Array(newSize) : new Uint16Array(newSize); + if (newIndexBuffer.BYTES_PER_ELEMENT !== indexBuffer.BYTES_PER_ELEMENT) { + for (let i = 0; i < indexBuffer.length; i++) { + newIndexBuffer[i] = indexBuffer[i]; + } + } else { + fastCopy(indexBuffer.buffer, newIndexBuffer.buffer); + } + this.indexBuffer = newIndexBuffer; + } + packQuadIndex(indexBuffer, index, indicesOffset) { + indexBuffer[index] = indicesOffset + 0; + indexBuffer[index + 1] = indicesOffset + 1; + indexBuffer[index + 2] = indicesOffset + 2; + indexBuffer[index + 3] = indicesOffset + 0; + indexBuffer[index + 4] = indicesOffset + 2; + indexBuffer[index + 5] = indicesOffset + 3; + } + packIndex(element, indexBuffer, index, indicesOffset) { + const indices = element.indices; + const size = element.indexSize; + const indexOffset = element.indexOffset; + const attributeOffset = element.attributeOffset; + for (let i = 0; i < size; i++) { + indexBuffer[index++] = indicesOffset + indices[i + indexOffset] - attributeOffset; + } + } + /** + * Destroys the batch and its resources. + * @param options - destruction options + * @param options.shader - whether to destroy the associated shader + */ + destroy(options = {}) { + var _a; + if (this.batches === null) return; + for (let i = 0; i < this.batchIndex; i++) { + returnBatchToPool(this.batches[i]); + } + this.batches = null; + this.geometry.destroy(true); + this.geometry = null; + if (options.shader) { + (_a = this.shader) == null ? void 0 : _a.destroy(); + this.shader = null; + } + for (let i = 0; i < this._elements.length; i++) { + if (this._elements[i]) this._elements[i]._batch = null; + } + this._elements = null; + this.indexBuffer = null; + this.attributeBuffer.destroy(); + this.attributeBuffer = null; + } + }; + _Batcher.defaultOptions = { + maxTextures: null, + attributesInitialSize: 4, + indicesInitialSize: 6 + }; + let Batcher = _Batcher; + + "use strict"; + const placeHolderBufferData = new Float32Array(1); + const placeHolderIndexData = new Uint32Array(1); + class BatchGeometry extends Geometry { + constructor() { + const vertexSize = 6; + const attributeBuffer = new Buffer({ + data: placeHolderBufferData, + label: "attribute-batch-buffer", + usage: BufferUsage.VERTEX | BufferUsage.COPY_DST, + shrinkToFit: false + }); + const indexBuffer = new Buffer({ + data: placeHolderIndexData, + label: "index-batch-buffer", + usage: BufferUsage.INDEX | BufferUsage.COPY_DST, + // | BufferUsage.STATIC, + shrinkToFit: false + }); + const stride = vertexSize * 4; + super({ + attributes: { + aPosition: { + buffer: attributeBuffer, + format: "float32x2", + stride, + offset: 0 + }, + aUV: { + buffer: attributeBuffer, + format: "float32x2", + stride, + offset: 2 * 4 + }, + aColor: { + buffer: attributeBuffer, + format: "unorm8x4", + stride, + offset: 4 * 4 + }, + aTextureIdAndRound: { + buffer: attributeBuffer, + format: "uint16x2", + stride, + offset: 5 * 4 + } + }, + indexBuffer + }); + } + } + + "use strict"; + function addBits(srcParts, parts, name) { + if (srcParts) { + for (const i in srcParts) { + const id = i.toLocaleLowerCase(); + const part = parts[id]; + if (part) { + let sanitisedPart = srcParts[i]; + if (i === "header") { + sanitisedPart = sanitisedPart.replace(/@in\s+[^;]+;\s*/g, "").replace(/@out\s+[^;]+;\s*/g, ""); + } + if (name) { + part.push(`//----${name}----//`); + } + part.push(sanitisedPart); + } else { + warn(`${i} placement hook does not exist in shader`); + } + } + } + } + + "use strict"; + const findHooksRx = /\{\{(.*?)\}\}/g; + function compileHooks(programSrc) { + var _a, _b; + const parts = {}; + const partMatches = (_b = (_a = programSrc.match(findHooksRx)) == null ? void 0 : _a.map((hook) => hook.replace(/[{()}]/g, ""))) != null ? _b : []; + partMatches.forEach((hook) => { + parts[hook] = []; + }); + return parts; + } + + "use strict"; + function extractInputs(fragmentSource, out) { + let match; + const regex = /@in\s+([^;]+);/g; + while ((match = regex.exec(fragmentSource)) !== null) { + out.push(match[1]); + } + } + function compileInputs(fragments, template, sort = false) { + const results = []; + extractInputs(template, results); + fragments.forEach((fragment) => { + if (fragment.header) { + extractInputs(fragment.header, results); + } + }); + const mainInput = results; + if (sort) { + mainInput.sort(); + } + const finalString = mainInput.map((inValue, i) => ` @location(${i}) ${inValue},`).join("\n"); + let cleanedString = template.replace(/@in\s+[^;]+;\s*/g, ""); + cleanedString = cleanedString.replace("{{in}}", ` +${finalString} +`); + return cleanedString; + } + + "use strict"; + function extractOutputs(fragmentSource, out) { + let match; + const regex = /@out\s+([^;]+);/g; + while ((match = regex.exec(fragmentSource)) !== null) { + out.push(match[1]); + } + } + function extractVariableName(value) { + const regex = /\b(\w+)\s*:/g; + const match = regex.exec(value); + return match ? match[1] : ""; + } + function stripVariable(value) { + const regex = /@.*?\s+/g; + return value.replace(regex, ""); + } + function compileOutputs(fragments, template) { + const results = []; + extractOutputs(template, results); + fragments.forEach((fragment) => { + if (fragment.header) { + extractOutputs(fragment.header, results); + } + }); + let index = 0; + const mainStruct = results.sort().map((inValue) => { + if (inValue.indexOf("builtin") > -1) { + return inValue; + } + return `@location(${index++}) ${inValue}`; + }).join(",\n"); + const mainStart = results.sort().map((inValue) => ` var ${stripVariable(inValue)};`).join("\n"); + const mainEnd = `return VSOutput( + ${results.sort().map((inValue) => ` ${extractVariableName(inValue)}`).join(",\n")});`; + let compiledCode = template.replace(/@out\s+[^;]+;\s*/g, ""); + compiledCode = compiledCode.replace("{{struct}}", ` +${mainStruct} +`); + compiledCode = compiledCode.replace("{{start}}", ` +${mainStart} +`); + compiledCode = compiledCode.replace("{{return}}", ` +${mainEnd} +`); + return compiledCode; + } + + "use strict"; + function injectBits(templateSrc, fragmentParts) { + let out = templateSrc; + for (const i in fragmentParts) { + const parts = fragmentParts[i]; + const toInject = parts.join("\n"); + if (toInject.length) { + out = out.replace(`{{${i}}}`, `//-----${i} START-----// +${parts.join("\n")} +//----${i} FINISH----//`); + } else { + out = out.replace(`{{${i}}}`, ""); + } + } + return out; + } + + "use strict"; + const cacheMap = /* @__PURE__ */ Object.create(null); + const bitCacheMap = /* @__PURE__ */ new Map(); + let CACHE_UID = 0; + function compileHighShader({ + template, + bits + }) { + const cacheId = generateCacheId(template, bits); + if (cacheMap[cacheId]) return cacheMap[cacheId]; + const { vertex, fragment } = compileInputsAndOutputs(template, bits); + cacheMap[cacheId] = compileBits(vertex, fragment, bits); + return cacheMap[cacheId]; + } + function compileHighShaderGl({ + template, + bits + }) { + const cacheId = generateCacheId(template, bits); + if (cacheMap[cacheId]) return cacheMap[cacheId]; + cacheMap[cacheId] = compileBits(template.vertex, template.fragment, bits); + return cacheMap[cacheId]; + } + function compileInputsAndOutputs(template, bits) { + const vertexFragments = bits.map((shaderBit) => shaderBit.vertex).filter((v) => !!v); + const fragmentFragments = bits.map((shaderBit) => shaderBit.fragment).filter((v) => !!v); + let compiledVertex = compileInputs(vertexFragments, template.vertex, true); + compiledVertex = compileOutputs(vertexFragments, compiledVertex); + const compiledFragment = compileInputs(fragmentFragments, template.fragment, true); + return { + vertex: compiledVertex, + fragment: compiledFragment + }; + } + function generateCacheId(template, bits) { + return bits.map((highFragment) => { + if (!bitCacheMap.has(highFragment)) { + bitCacheMap.set(highFragment, CACHE_UID++); + } + return bitCacheMap.get(highFragment); + }).sort((a, b) => a - b).join("-") + template.vertex + template.fragment; + } + function compileBits(vertex, fragment, bits) { + const vertexParts = compileHooks(vertex); + const fragmentParts = compileHooks(fragment); + bits.forEach((shaderBit) => { + addBits(shaderBit.vertex, vertexParts, shaderBit.name); + addBits(shaderBit.fragment, fragmentParts, shaderBit.name); + }); + return { + vertex: injectBits(vertex, vertexParts), + fragment: injectBits(fragment, fragmentParts) + }; + } + + "use strict"; + const vertexGPUTemplate = ( + /* wgsl */ + ` + @in aPosition: vec2; + @in aUV: vec2; + + @out @builtin(position) vPosition: vec4; + @out vUV : vec2; + @out vColor : vec4; + + {{header}} + + struct VSOutput { + {{struct}} + }; + + @vertex + fn main( {{in}} ) -> VSOutput { + + var worldTransformMatrix = globalUniforms.uWorldTransformMatrix; + var modelMatrix = mat3x3( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + var position = aPosition; + var uv = aUV; + + {{start}} + + vColor = vec4(1., 1., 1., 1.); + + {{main}} + + vUV = uv; + + var modelViewProjectionMatrix = globalUniforms.uProjectionMatrix * worldTransformMatrix * modelMatrix; + + vPosition = vec4((modelViewProjectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0); + + vColor *= globalUniforms.uWorldColorAlpha; + + {{end}} + + {{return}} + }; +` + ); + const fragmentGPUTemplate = ( + /* wgsl */ + ` + @in vUV : vec2; + @in vColor : vec4; + + {{header}} + + @fragment + fn main( + {{in}} + ) -> @location(0) vec4 { + + {{start}} + + var outColor:vec4; + + {{main}} + + var finalColor:vec4 = outColor * vColor; + + {{end}} + + return finalColor; + }; +` + ); + const vertexGlTemplate = ( + /* glsl */ + ` + in vec2 aPosition; + in vec2 aUV; + + out vec4 vColor; + out vec2 vUV; + + {{header}} + + void main(void){ + + mat3 worldTransformMatrix = uWorldTransformMatrix; + mat3 modelMatrix = mat3( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + vec2 position = aPosition; + vec2 uv = aUV; + + {{start}} + + vColor = vec4(1.); + + {{main}} + + vUV = uv; + + mat3 modelViewProjectionMatrix = uProjectionMatrix * worldTransformMatrix * modelMatrix; + + gl_Position = vec4((modelViewProjectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0); + + vColor *= uWorldColorAlpha; + + {{end}} + } +` + ); + const fragmentGlTemplate = ( + /* glsl */ + ` + + in vec4 vColor; + in vec2 vUV; + + out vec4 finalColor; + + {{header}} + + void main(void) { + + {{start}} + + vec4 outColor; + + {{main}} + + finalColor = outColor * vColor; + + {{end}} + } +` + ); + + "use strict"; + const globalUniformsBit = { + name: "global-uniforms-bit", + vertex: { + header: ( + /* wgsl */ + ` + struct GlobalUniforms { + uProjectionMatrix:mat3x3, + uWorldTransformMatrix:mat3x3, + uWorldColorAlpha: vec4, + uResolution: vec2, + } + + @group(0) @binding(0) var globalUniforms : GlobalUniforms; + ` + ) + } + }; + const globalUniformsUBOBitGl = { + name: "global-uniforms-ubo-bit", + vertex: { + header: ( + /* glsl */ + ` + uniform globalUniforms { + mat3 uProjectionMatrix; + mat3 uWorldTransformMatrix; + vec4 uWorldColorAlpha; + vec2 uResolution; + }; + ` + ) + } + }; + const globalUniformsBitGl = { + name: "global-uniforms-bit", + vertex: { + header: ( + /* glsl */ + ` + uniform mat3 uProjectionMatrix; + uniform mat3 uWorldTransformMatrix; + uniform vec4 uWorldColorAlpha; + uniform vec2 uResolution; + ` + ) + } + }; + + "use strict"; + var __defProp$10 = Object.defineProperty; + var __getOwnPropSymbols$12 = Object.getOwnPropertySymbols; + var __hasOwnProp$12 = Object.prototype.hasOwnProperty; + var __propIsEnum$12 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$10 = (obj, key, value) => key in obj ? __defProp$10(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$10 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$12.call(b, prop)) + __defNormalProp$10(a, prop, b[prop]); + if (__getOwnPropSymbols$12) + for (var prop of __getOwnPropSymbols$12(b)) { + if (__propIsEnum$12.call(b, prop)) + __defNormalProp$10(a, prop, b[prop]); + } + return a; + }; + function compileHighShaderGpuProgram({ bits, name }) { + const source = compileHighShader({ + template: { + fragment: fragmentGPUTemplate, + vertex: vertexGPUTemplate + }, + bits: [ + globalUniformsBit, + ...bits + ] + }); + return GpuProgram.from({ + name, + vertex: { + source: source.vertex, + entryPoint: "main" + }, + fragment: { + source: source.fragment, + entryPoint: "main" + } + }); + } + function compileHighShaderGlProgram({ bits, name }) { + return new GlProgram(__spreadValues$10({ + name + }, compileHighShaderGl({ + template: { + vertex: vertexGlTemplate, + fragment: fragmentGlTemplate + }, + bits: [ + globalUniformsBitGl, + ...bits + ] + }))); + } + + "use strict"; + const colorBit = { + name: "color-bit", + vertex: { + header: ( + /* wgsl */ + ` + @in aColor: vec4; + ` + ), + main: ( + /* wgsl */ + ` + vColor *= vec4(aColor.rgb * aColor.a, aColor.a); + ` + ) + } + }; + const colorBitGl = { + name: "color-bit", + vertex: { + header: ( + /* glsl */ + ` + in vec4 aColor; + ` + ), + main: ( + /* glsl */ + ` + vColor *= vec4(aColor.rgb * aColor.a, aColor.a); + ` + ) + } + }; + + "use strict"; + const textureBatchBitGpuCache = {}; + function generateBindingSrc(maxTextures) { + const src = []; + if (maxTextures === 1) { + src.push("@group(1) @binding(0) var textureSource1: texture_2d;"); + src.push("@group(1) @binding(1) var textureSampler1: sampler;"); + } else { + let bindingIndex = 0; + for (let i = 0; i < maxTextures; i++) { + src.push(`@group(1) @binding(${bindingIndex++}) var textureSource${i + 1}: texture_2d;`); + src.push(`@group(1) @binding(${bindingIndex++}) var textureSampler${i + 1}: sampler;`); + } + } + return src.join("\n"); + } + function generateSampleSrc(maxTextures) { + const src = []; + if (maxTextures === 1) { + src.push("outColor = textureSampleGrad(textureSource1, textureSampler1, vUV, uvDx, uvDy);"); + } else { + src.push("switch vTextureId {"); + for (let i = 0; i < maxTextures; i++) { + if (i === maxTextures - 1) { + src.push(` default:{`); + } else { + src.push(` case ${i}:{`); + } + src.push(` outColor = textureSampleGrad(textureSource${i + 1}, textureSampler${i + 1}, vUV, uvDx, uvDy);`); + src.push(` break;}`); + } + src.push(`}`); + } + return src.join("\n"); + } + function generateTextureBatchBit(maxTextures) { + if (!textureBatchBitGpuCache[maxTextures]) { + textureBatchBitGpuCache[maxTextures] = { + name: "texture-batch-bit", + vertex: { + header: ` + @in aTextureIdAndRound: vec2; + @out @interpolate(flat) vTextureId : u32; + `, + main: ` + vTextureId = aTextureIdAndRound.y; + `, + end: ` + if(aTextureIdAndRound.x == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + ` + }, + fragment: { + header: ` + @in @interpolate(flat) vTextureId: u32; + + ${generateBindingSrc(maxTextures)} + `, + main: ` + var uvDx = dpdx(vUV); + var uvDy = dpdy(vUV); + + ${generateSampleSrc(maxTextures)} + ` + } + }; + } + return textureBatchBitGpuCache[maxTextures]; + } + const textureBatchBitGlCache = {}; + function generateSampleGlSrc(maxTextures) { + const src = []; + for (let i = 0; i < maxTextures; i++) { + if (i > 0) { + src.push("else"); + } + if (i < maxTextures - 1) { + src.push(`if(vTextureId < ${i}.5)`); + } + src.push("{"); + src.push(` outColor = texture(uTextures[${i}], vUV);`); + src.push("}"); + } + return src.join("\n"); + } + function generateTextureBatchBitGl(maxTextures) { + if (!textureBatchBitGlCache[maxTextures]) { + textureBatchBitGlCache[maxTextures] = { + name: "texture-batch-bit", + vertex: { + header: ` + in vec2 aTextureIdAndRound; + out float vTextureId; + + `, + main: ` + vTextureId = aTextureIdAndRound.y; + `, + end: ` + if(aTextureIdAndRound.x == 1.) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + ` + }, + fragment: { + header: ` + in float vTextureId; + + uniform sampler2D uTextures[${maxTextures}]; + + `, + main: ` + + ${generateSampleGlSrc(maxTextures)} + ` + } + }; + } + return textureBatchBitGlCache[maxTextures]; + } + + "use strict"; + const roundPixelsBit = { + name: "round-pixels-bit", + vertex: { + header: ( + /* wgsl */ + ` + fn roundPixels(position: vec2, targetSize: vec2) -> vec2 + { + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; + } + ` + ) + } + }; + const roundPixelsBitGl = { + name: "round-pixels-bit", + vertex: { + header: ( + /* glsl */ + ` + vec2 roundPixels(vec2 position, vec2 targetSize) + { + return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; + } + ` + ) + } + }; + + "use strict"; + const batchSamplersUniformGroupHash = {}; + function getBatchSamplersUniformGroup(maxTextures) { + let batchSamplersUniformGroup = batchSamplersUniformGroupHash[maxTextures]; + if (batchSamplersUniformGroup) return batchSamplersUniformGroup; + const sampleValues = new Int32Array(maxTextures); + for (let i = 0; i < maxTextures; i++) { + sampleValues[i] = i; + } + batchSamplersUniformGroup = batchSamplersUniformGroupHash[maxTextures] = new UniformGroup({ + uTextures: { value: sampleValues, type: `i32`, size: maxTextures } + }, { isStatic: true }); + return batchSamplersUniformGroup; + } + + "use strict"; + class DefaultShader extends Shader { + constructor(maxTextures) { + const glProgram = compileHighShaderGlProgram({ + name: "batch", + bits: [ + colorBitGl, + generateTextureBatchBitGl(maxTextures), + roundPixelsBitGl + ] + }); + const gpuProgram = compileHighShaderGpuProgram({ + name: "batch", + bits: [ + colorBit, + generateTextureBatchBit(maxTextures), + roundPixelsBit + ] + }); + super({ + glProgram, + gpuProgram, + resources: { + batchSamplers: getBatchSamplersUniformGroup(maxTextures) + } + }); + this.maxTextures = maxTextures; + } + } + + "use strict"; + let defaultShader = null; + const _DefaultBatcher = class _DefaultBatcher extends Batcher { + constructor(options) { + super(options); + this.geometry = new BatchGeometry(); + this.name = _DefaultBatcher.extension.name; + /** The size of one attribute. 1 = 32 bit. x, y, u, v, color, textureIdAndRound -> total = 6 */ + this.vertexSize = 6; + defaultShader != null ? defaultShader : defaultShader = new DefaultShader(options.maxTextures); + this.shader = defaultShader; + } + /** + * Packs the attributes of a DefaultBatchableMeshElement into the provided views. + * @param element - The DefaultBatchableMeshElement to pack. + * @param float32View - The Float32Array view to pack into. + * @param uint32View - The Uint32Array view to pack into. + * @param index - The starting index in the views. + * @param textureId - The texture ID to use. + */ + packAttributes(element, float32View, uint32View, index, textureId) { + const textureIdAndRound = textureId << 16 | element.roundPixels & 65535; + const wt = element.transform; + const a = wt.a; + const b = wt.b; + const c = wt.c; + const d = wt.d; + const tx = wt.tx; + const ty = wt.ty; + const { positions, uvs } = element; + const argb = element.color; + const offset = element.attributeOffset; + const end = offset + element.attributeSize; + for (let i = offset; i < end; i++) { + const i2 = i * 2; + const x = positions[i2]; + const y = positions[i2 + 1]; + float32View[index++] = a * x + c * y + tx; + float32View[index++] = d * y + b * x + ty; + float32View[index++] = uvs[i2]; + float32View[index++] = uvs[i2 + 1]; + uint32View[index++] = argb; + uint32View[index++] = textureIdAndRound; + } + } + /** + * Packs the attributes of a DefaultBatchableQuadElement into the provided views. + * @param element - The DefaultBatchableQuadElement to pack. + * @param float32View - The Float32Array view to pack into. + * @param uint32View - The Uint32Array view to pack into. + * @param index - The starting index in the views. + * @param textureId - The texture ID to use. + */ + packQuadAttributes(element, float32View, uint32View, index, textureId) { + const texture = element.texture; + const wt = element.transform; + const a = wt.a; + const b = wt.b; + const c = wt.c; + const d = wt.d; + const tx = wt.tx; + const ty = wt.ty; + const bounds = element.bounds; + const w0 = bounds.maxX; + const w1 = bounds.minX; + const h0 = bounds.maxY; + const h1 = bounds.minY; + const uvs = texture.uvs; + const argb = element.color; + const textureIdAndRound = textureId << 16 | element.roundPixels & 65535; + float32View[index + 0] = a * w1 + c * h1 + tx; + float32View[index + 1] = d * h1 + b * w1 + ty; + float32View[index + 2] = uvs.x0; + float32View[index + 3] = uvs.y0; + uint32View[index + 4] = argb; + uint32View[index + 5] = textureIdAndRound; + float32View[index + 6] = a * w0 + c * h1 + tx; + float32View[index + 7] = d * h1 + b * w0 + ty; + float32View[index + 8] = uvs.x1; + float32View[index + 9] = uvs.y1; + uint32View[index + 10] = argb; + uint32View[index + 11] = textureIdAndRound; + float32View[index + 12] = a * w0 + c * h0 + tx; + float32View[index + 13] = d * h0 + b * w0 + ty; + float32View[index + 14] = uvs.x2; + float32View[index + 15] = uvs.y2; + uint32View[index + 16] = argb; + uint32View[index + 17] = textureIdAndRound; + float32View[index + 18] = a * w1 + c * h0 + tx; + float32View[index + 19] = d * h0 + b * w1 + ty; + float32View[index + 20] = uvs.x3; + float32View[index + 21] = uvs.y3; + uint32View[index + 22] = argb; + uint32View[index + 23] = textureIdAndRound; + } + /** + * Updates the maximum number of textures that can be used in the shader. + * @param maxTextures - The maximum number of textures that can be used in the shader. + * @internal + */ + _updateMaxTextures(maxTextures) { + if (this.shader.maxTextures === maxTextures) return; + defaultShader = new DefaultShader(maxTextures); + this.shader = defaultShader; + } + destroy() { + this.shader = null; + super.destroy(); + } + }; + /** @ignore */ + _DefaultBatcher.extension = { + type: [ + ExtensionType.Batcher + ], + name: "default" + }; + let DefaultBatcher = _DefaultBatcher; + + "use strict"; + class GCManagedHash { + constructor(options) { + // Exposed directly for GC system access + this.items = /* @__PURE__ */ Object.create(null); + const { renderer, type, onUnload, priority, name } = options; + this._renderer = renderer; + renderer.gc.addResourceHash(this, "items", type, priority != null ? priority : 0); + this._onUnload = onUnload; + this.name = name; + } + /** + * Add an item to the hash. No-op if already added. + * @param item + * @returns true if the item was added, false if it was already in the hash + */ + add(item) { + if (this.items[item.uid]) return false; + this.items[item.uid] = item; + item.once("unload", this.remove, this); + item._gcLastUsed = this._renderer.gc.now; + return true; + } + remove(item, ...args) { + var _a; + if (!this.items[item.uid]) return; + const gpuData = item._gpuData[this._renderer.uid]; + if (!gpuData) return; + (_a = this._onUnload) == null ? void 0 : _a.call(this, item, ...args); + gpuData.destroy(); + item._gpuData[this._renderer.uid] = null; + this.items[item.uid] = null; + } + removeAll(...args) { + Object.values(this.items).forEach((item) => item && this.remove(item, ...args)); + } + destroy(...args) { + this.removeAll(...args); + this.items = /* @__PURE__ */ Object.create(null); + this._renderer = null; + this._onUnload = null; + } + } + + "use strict"; + function buildUvs(vertices, verticesStride, verticesOffset, uvs, uvsOffset, uvsStride, size, matrix = null) { + let index = 0; + verticesOffset *= verticesStride; + uvsOffset *= uvsStride; + const a = matrix.a; + const b = matrix.b; + const c = matrix.c; + const d = matrix.d; + const tx = matrix.tx; + const ty = matrix.ty; + while (index < size) { + const x = vertices[verticesOffset]; + const y = vertices[verticesOffset + 1]; + uvs[uvsOffset] = a * x + c * y + tx; + uvs[uvsOffset + 1] = b * x + d * y + ty; + uvsOffset += uvsStride; + verticesOffset += verticesStride; + index++; + } + } + function buildSimpleUvs(uvs, uvsOffset, uvsStride, size) { + let index = 0; + uvsOffset *= uvsStride; + while (index < size) { + uvs[uvsOffset] = 0; + uvs[uvsOffset + 1] = 0; + uvsOffset += uvsStride; + index++; + } + } + + "use strict"; + function transformVertices(vertices, m, offset, stride, size) { + const a = m.a; + const b = m.b; + const c = m.c; + const d = m.d; + const tx = m.tx; + const ty = m.ty; + offset || (offset = 0); + stride || (stride = 2); + size || (size = vertices.length / stride - offset); + let index = offset * stride; + for (let i = 0; i < size; i++) { + const x = vertices[index]; + const y = vertices[index + 1]; + vertices[index] = a * x + c * y + tx; + vertices[index + 1] = b * x + d * y + ty; + index += stride; + } + } + + "use strict"; + const identityMatrix = new Matrix(); + class BatchableGraphics { + constructor() { + this.packAsQuad = false; + this.batcherName = "default"; + this.topology = "triangle-list"; + this.applyTransform = true; + this.roundPixels = 0; + this._batcher = null; + this._batch = null; + } + get uvs() { + return this.geometryData.uvs; + } + get positions() { + return this.geometryData.vertices; + } + get indices() { + return this.geometryData.indices; + } + get blendMode() { + if (this.renderable && this.applyTransform) { + return this.renderable.groupBlendMode; + } + return "normal"; + } + get color() { + const rgb = this.baseColor; + const bgr = rgb >> 16 | rgb & 65280 | (rgb & 255) << 16; + const renderable = this.renderable; + if (renderable) { + return multiplyHexColors(bgr, renderable.groupColor) + (this.alpha * renderable.groupAlpha * 255 << 24); + } + return bgr + (this.alpha * 255 << 24); + } + get transform() { + var _a; + return ((_a = this.renderable) == null ? void 0 : _a.groupTransform) || identityMatrix; + } + copyTo(gpuBuffer) { + gpuBuffer.indexOffset = this.indexOffset; + gpuBuffer.indexSize = this.indexSize; + gpuBuffer.attributeOffset = this.attributeOffset; + gpuBuffer.attributeSize = this.attributeSize; + gpuBuffer.baseColor = this.baseColor; + gpuBuffer.alpha = this.alpha; + gpuBuffer.texture = this.texture; + gpuBuffer.geometryData = this.geometryData; + gpuBuffer.topology = this.topology; + } + reset() { + this.applyTransform = true; + this.renderable = null; + this.topology = "triangle-list"; + } + destroy() { + this.renderable = null; + this.texture = null; + this.geometryData = null; + this._batcher = null; + this._batch = null; + } + } + + "use strict"; + var __defProp$$ = Object.defineProperty; + var __defProps$s = Object.defineProperties; + var __getOwnPropDescs$s = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$11 = Object.getOwnPropertySymbols; + var __hasOwnProp$11 = Object.prototype.hasOwnProperty; + var __propIsEnum$11 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$$ = (obj, key, value) => key in obj ? __defProp$$(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$$ = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$11.call(b, prop)) + __defNormalProp$$(a, prop, b[prop]); + if (__getOwnPropSymbols$11) + for (var prop of __getOwnPropSymbols$11(b)) { + if (__propIsEnum$11.call(b, prop)) + __defNormalProp$$(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$s = (a, b) => __defProps$s(a, __getOwnPropDescs$s(b)); + const buildCircle = { + extension: { + type: ExtensionType.ShapeBuilder, + name: "circle" + }, + build(shape, points) { + let x; + let y; + let dx; + let dy; + let rx; + let ry; + if (shape.type === "circle") { + const circle = shape; + rx = ry = circle.radius; + if (rx <= 0) { + return false; + } + x = circle.x; + y = circle.y; + dx = dy = 0; + } else if (shape.type === "ellipse") { + const ellipse = shape; + rx = ellipse.halfWidth; + ry = ellipse.halfHeight; + if (rx <= 0 || ry <= 0) { + return false; + } + x = ellipse.x; + y = ellipse.y; + dx = dy = 0; + } else { + const roundedRect = shape; + const halfWidth = roundedRect.width / 2; + const halfHeight = roundedRect.height / 2; + x = roundedRect.x + halfWidth; + y = roundedRect.y + halfHeight; + rx = ry = Math.max(0, Math.min(roundedRect.radius, Math.min(halfWidth, halfHeight))); + dx = halfWidth - rx; + dy = halfHeight - ry; + } + if (dx < 0 || dy < 0) { + return false; + } + const n = Math.ceil(2.3 * Math.sqrt(rx + ry)); + const m = n * 8 + (dx ? 4 : 0) + (dy ? 4 : 0); + if (m === 0) { + return false; + } + if (n === 0) { + points[0] = points[6] = x + dx; + points[1] = points[3] = y + dy; + points[2] = points[4] = x - dx; + points[5] = points[7] = y - dy; + return true; + } + let j1 = 0; + let j2 = n * 4 + (dx ? 2 : 0) + 2; + let j3 = j2; + let j4 = m; + let x0 = dx + rx; + let y0 = dy; + let x1 = x + x0; + let x2 = x - x0; + let y1 = y + y0; + points[j1++] = x1; + points[j1++] = y1; + points[--j2] = y1; + points[--j2] = x2; + if (dy) { + const y22 = y - y0; + points[j3++] = x2; + points[j3++] = y22; + points[--j4] = y22; + points[--j4] = x1; + } + for (let i = 1; i < n; i++) { + const a = Math.PI / 2 * (i / n); + const x02 = dx + Math.cos(a) * rx; + const y02 = dy + Math.sin(a) * ry; + const x12 = x + x02; + const x22 = x - x02; + const y12 = y + y02; + const y22 = y - y02; + points[j1++] = x12; + points[j1++] = y12; + points[--j2] = y12; + points[--j2] = x22; + points[j3++] = x22; + points[j3++] = y22; + points[--j4] = y22; + points[--j4] = x12; + } + x0 = dx; + y0 = dy + ry; + x1 = x + x0; + x2 = x - x0; + y1 = y + y0; + const y2 = y - y0; + points[j1++] = x1; + points[j1++] = y1; + points[--j4] = y2; + points[--j4] = x1; + if (dx) { + points[j1++] = x2; + points[j1++] = y1; + points[--j4] = y2; + points[--j4] = x2; + } + return true; + }, + triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) { + if (points.length === 0) { + return; + } + let centerX = 0; + let centerY = 0; + for (let i = 0; i < points.length; i += 2) { + centerX += points[i]; + centerY += points[i + 1]; + } + centerX /= points.length / 2; + centerY /= points.length / 2; + let count = verticesOffset; + vertices[count * verticesStride] = centerX; + vertices[count * verticesStride + 1] = centerY; + const centerIndex = count++; + for (let i = 0; i < points.length; i += 2) { + vertices[count * verticesStride] = points[i]; + vertices[count * verticesStride + 1] = points[i + 1]; + if (i > 0) { + indices[indicesOffset++] = count; + indices[indicesOffset++] = centerIndex; + indices[indicesOffset++] = count - 1; + } + count++; + } + indices[indicesOffset++] = centerIndex + 1; + indices[indicesOffset++] = centerIndex; + indices[indicesOffset++] = count - 1; + } + }; + const buildEllipse = __spreadProps$s(__spreadValues$$({}, buildCircle), { extension: __spreadProps$s(__spreadValues$$({}, buildCircle.extension), { name: "ellipse" }) }); + const buildRoundedRectangle = __spreadProps$s(__spreadValues$$({}, buildCircle), { extension: __spreadProps$s(__spreadValues$$({}, buildCircle.extension), { name: "roundedRectangle" }) }); + + "use strict"; + const closePointEps = 1e-4; + const curveEps = 1e-4; + + "use strict"; + function getOrientationOfPoints(points) { + const m = points.length; + if (m < 6) { + return 1; + } + let area = 0; + for (let i = 0, x1 = points[m - 2], y1 = points[m - 1]; i < m; i += 2) { + const x2 = points[i]; + const y2 = points[i + 1]; + area += (x2 - x1) * (y2 + y1); + x1 = x2; + y1 = y2; + } + if (area < 0) { + return -1; + } + return 1; + } + + "use strict"; + function square(x, y, nx, ny, innerWeight, outerWeight, clockwise, verts) { + const ix = x - nx * innerWeight; + const iy = y - ny * innerWeight; + const ox = x + nx * outerWeight; + const oy = y + ny * outerWeight; + let exx; + let eyy; + if (clockwise) { + exx = ny; + eyy = -nx; + } else { + exx = -ny; + eyy = nx; + } + const eix = ix + exx; + const eiy = iy + eyy; + const eox = ox + exx; + const eoy = oy + eyy; + verts.push(eix, eiy); + verts.push(eox, eoy); + return 2; + } + function round(cx, cy, sx, sy, ex, ey, verts, clockwise) { + const cx2p0x = sx - cx; + const cy2p0y = sy - cy; + let angle0 = Math.atan2(cx2p0x, cy2p0y); + let angle1 = Math.atan2(ex - cx, ey - cy); + if (clockwise && angle0 < angle1) { + angle0 += Math.PI * 2; + } else if (!clockwise && angle0 > angle1) { + angle1 += Math.PI * 2; + } + let startAngle = angle0; + const angleDiff = angle1 - angle0; + const absAngleDiff = Math.abs(angleDiff); + const radius = Math.sqrt(cx2p0x * cx2p0x + cy2p0y * cy2p0y); + const segCount = (15 * absAngleDiff * Math.sqrt(radius) / Math.PI >> 0) + 1; + const angleInc = angleDiff / segCount; + startAngle += angleInc; + if (clockwise) { + verts.push(cx, cy); + verts.push(sx, sy); + for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) { + verts.push(cx, cy); + verts.push( + cx + Math.sin(angle) * radius, + cy + Math.cos(angle) * radius + ); + } + verts.push(cx, cy); + verts.push(ex, ey); + } else { + verts.push(sx, sy); + verts.push(cx, cy); + for (let i = 1, angle = startAngle; i < segCount; i++, angle += angleInc) { + verts.push( + cx + Math.sin(angle) * radius, + cy + Math.cos(angle) * radius + ); + verts.push(cx, cy); + } + verts.push(ex, ey); + verts.push(cx, cy); + } + return segCount * 2; + } + function buildLine(points, lineStyle, flipAlignment, closed, vertices, indices) { + const eps = closePointEps; + if (points.length === 0) { + return; + } + const style = lineStyle; + let alignment = style.alignment; + if (lineStyle.alignment !== 0.5) { + let orientation = getOrientationOfPoints(points); + if (flipAlignment) orientation *= -1; + alignment = (alignment - 0.5) * orientation + 0.5; + } + const firstPoint = new Point(points[0], points[1]); + const lastPoint = new Point(points[points.length - 2], points[points.length - 1]); + const closedShape = closed; + const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps; + if (closedShape) { + points = points.slice(); + if (closedPath) { + points.pop(); + points.pop(); + lastPoint.set(points[points.length - 2], points[points.length - 1]); + } + const midPointX = (firstPoint.x + lastPoint.x) * 0.5; + const midPointY = (lastPoint.y + firstPoint.y) * 0.5; + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY); + } + const verts = vertices; + const length = points.length / 2; + let indexCount = points.length; + const indexStart = verts.length / 2; + const width = style.width / 2; + const widthSquared = width * width; + const miterLimitSquared = style.miterLimit * style.miterLimit; + let x0 = points[0]; + let y0 = points[1]; + let x1 = points[2]; + let y1 = points[3]; + let x2 = 0; + let y2 = 0; + let perpX = -(y0 - y1); + let perpY = x0 - x1; + let perp1x = 0; + let perp1y = 0; + let dist = Math.sqrt(perpX * perpX + perpY * perpY); + perpX /= dist; + perpY /= dist; + perpX *= width; + perpY *= width; + const ratio = alignment; + const innerWeight = (1 - ratio) * 2; + const outerWeight = ratio * 2; + if (!closedShape) { + if (style.cap === "round") { + indexCount += round( + x0 - perpX * (innerWeight - outerWeight) * 0.5, + y0 - perpY * (innerWeight - outerWeight) * 0.5, + x0 - perpX * innerWeight, + y0 - perpY * innerWeight, + x0 + perpX * outerWeight, + y0 + perpY * outerWeight, + verts, + true + ) + 2; + } else if (style.cap === "square") { + indexCount += square(x0, y0, perpX, perpY, innerWeight, outerWeight, true, verts); + } + } + verts.push( + x0 - perpX * innerWeight, + y0 - perpY * innerWeight + ); + verts.push( + x0 + perpX * outerWeight, + y0 + perpY * outerWeight + ); + for (let i = 1; i < length - 1; ++i) { + x0 = points[(i - 1) * 2]; + y0 = points[(i - 1) * 2 + 1]; + x1 = points[i * 2]; + y1 = points[i * 2 + 1]; + x2 = points[(i + 1) * 2]; + y2 = points[(i + 1) * 2 + 1]; + perpX = -(y0 - y1); + perpY = x0 - x1; + dist = Math.sqrt(perpX * perpX + perpY * perpY); + perpX /= dist; + perpY /= dist; + perpX *= width; + perpY *= width; + perp1x = -(y1 - y2); + perp1y = x1 - x2; + dist = Math.sqrt(perp1x * perp1x + perp1y * perp1y); + perp1x /= dist; + perp1y /= dist; + perp1x *= width; + perp1y *= width; + const dx0 = x1 - x0; + const dy0 = y0 - y1; + const dx1 = x1 - x2; + const dy1 = y2 - y1; + const dot = dx0 * dx1 + dy0 * dy1; + const cross = dy0 * dx1 - dy1 * dx0; + const clockwise = cross < 0; + if (Math.abs(cross) < 1e-3 * Math.abs(dot)) { + verts.push( + x1 - perpX * innerWeight, + y1 - perpY * innerWeight + ); + verts.push( + x1 + perpX * outerWeight, + y1 + perpY * outerWeight + ); + if (dot >= 0) { + if (style.join === "round") { + indexCount += round( + x1, + y1, + x1 - perpX * innerWeight, + y1 - perpY * innerWeight, + x1 - perp1x * innerWeight, + y1 - perp1y * innerWeight, + verts, + false + ) + 4; + } else { + indexCount += 2; + } + verts.push( + x1 - perp1x * outerWeight, + y1 - perp1y * outerWeight + ); + verts.push( + x1 + perp1x * innerWeight, + y1 + perp1y * innerWeight + ); + } + continue; + } + const c1 = (-perpX + x0) * (-perpY + y1) - (-perpX + x1) * (-perpY + y0); + const c2 = (-perp1x + x2) * (-perp1y + y1) - (-perp1x + x1) * (-perp1y + y2); + const px = (dx0 * c2 - dx1 * c1) / cross; + const py = (dy1 * c1 - dy0 * c2) / cross; + const pDist = (px - x1) * (px - x1) + (py - y1) * (py - y1); + const imx = x1 + (px - x1) * innerWeight; + const imy = y1 + (py - y1) * innerWeight; + const omx = x1 - (px - x1) * outerWeight; + const omy = y1 - (py - y1) * outerWeight; + const smallerInsideSegmentSq = Math.min(dx0 * dx0 + dy0 * dy0, dx1 * dx1 + dy1 * dy1); + const insideWeight = clockwise ? innerWeight : outerWeight; + const smallerInsideDiagonalSq = smallerInsideSegmentSq + insideWeight * insideWeight * widthSquared; + const insideMiterOk = pDist <= smallerInsideDiagonalSq; + if (insideMiterOk) { + if (style.join === "bevel" || pDist / widthSquared > miterLimitSquared) { + if (clockwise) { + verts.push(imx, imy); + verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight); + verts.push(imx, imy); + verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight); + } else { + verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight); + verts.push(omx, omy); + verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight); + verts.push(omx, omy); + } + indexCount += 2; + } else if (style.join === "round") { + if (clockwise) { + verts.push(imx, imy); + verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight); + indexCount += round( + x1, + y1, + x1 + perpX * outerWeight, + y1 + perpY * outerWeight, + x1 + perp1x * outerWeight, + y1 + perp1y * outerWeight, + verts, + true + ) + 4; + verts.push(imx, imy); + verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight); + } else { + verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight); + verts.push(omx, omy); + indexCount += round( + x1, + y1, + x1 - perpX * innerWeight, + y1 - perpY * innerWeight, + x1 - perp1x * innerWeight, + y1 - perp1y * innerWeight, + verts, + false + ) + 4; + verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight); + verts.push(omx, omy); + } + } else { + verts.push(imx, imy); + verts.push(omx, omy); + } + } else { + verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight); + verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight); + if (style.join === "round") { + if (clockwise) { + indexCount += round( + x1, + y1, + x1 + perpX * outerWeight, + y1 + perpY * outerWeight, + x1 + perp1x * outerWeight, + y1 + perp1y * outerWeight, + verts, + true + ) + 2; + } else { + indexCount += round( + x1, + y1, + x1 - perpX * innerWeight, + y1 - perpY * innerWeight, + x1 - perp1x * innerWeight, + y1 - perp1y * innerWeight, + verts, + false + ) + 2; + } + } else if (style.join === "miter" && pDist / widthSquared <= miterLimitSquared) { + if (clockwise) { + verts.push(omx, omy); + verts.push(omx, omy); + } else { + verts.push(imx, imy); + verts.push(imx, imy); + } + indexCount += 2; + } + verts.push(x1 - perp1x * innerWeight, y1 - perp1y * innerWeight); + verts.push(x1 + perp1x * outerWeight, y1 + perp1y * outerWeight); + indexCount += 2; + } + } + x0 = points[(length - 2) * 2]; + y0 = points[(length - 2) * 2 + 1]; + x1 = points[(length - 1) * 2]; + y1 = points[(length - 1) * 2 + 1]; + perpX = -(y0 - y1); + perpY = x0 - x1; + dist = Math.sqrt(perpX * perpX + perpY * perpY); + perpX /= dist; + perpY /= dist; + perpX *= width; + perpY *= width; + verts.push(x1 - perpX * innerWeight, y1 - perpY * innerWeight); + verts.push(x1 + perpX * outerWeight, y1 + perpY * outerWeight); + if (!closedShape) { + if (style.cap === "round") { + indexCount += round( + x1 - perpX * (innerWeight - outerWeight) * 0.5, + y1 - perpY * (innerWeight - outerWeight) * 0.5, + x1 - perpX * innerWeight, + y1 - perpY * innerWeight, + x1 + perpX * outerWeight, + y1 + perpY * outerWeight, + verts, + false + ) + 2; + } else if (style.cap === "square") { + indexCount += square(x1, y1, perpX, perpY, innerWeight, outerWeight, false, verts); + } + } + const eps2 = curveEps * curveEps; + for (let i = indexStart; i < indexCount + indexStart - 2; ++i) { + x0 = verts[i * 2]; + y0 = verts[i * 2 + 1]; + x1 = verts[(i + 1) * 2]; + y1 = verts[(i + 1) * 2 + 1]; + x2 = verts[(i + 2) * 2]; + y2 = verts[(i + 2) * 2 + 1]; + if (Math.abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) < eps2) { + continue; + } + indices.push(i, i + 1, i + 2); + } + } + + "use strict"; + function buildPixelLine(points, closed, vertices, indices) { + const eps = closePointEps; + if (points.length === 0) { + return; + } + const fx = points[0]; + const fy = points[1]; + const lx = points[points.length - 2]; + const ly = points[points.length - 1]; + const closePath = closed || Math.abs(fx - lx) < eps && Math.abs(fy - ly) < eps; + const verts = vertices; + const length = points.length / 2; + const indexStart = verts.length / 2; + for (let i = 0; i < length; i++) { + verts.push(points[i * 2]); + verts.push(points[i * 2 + 1]); + } + for (let i = 0; i < length - 1; i++) { + indices.push(indexStart + i, indexStart + i + 1); + } + if (closePath) { + indices.push(indexStart + length - 1, indexStart); + } + } + + "use strict"; + function triangulateWithHoles(points, holes, vertices, verticesStride, verticesOffset, indices, indicesOffset) { + const triangles = earcut(points, holes, 2); + if (!triangles) { + return; + } + for (let i = 0; i < triangles.length; i += 3) { + indices[indicesOffset++] = triangles[i] + verticesOffset; + indices[indicesOffset++] = triangles[i + 1] + verticesOffset; + indices[indicesOffset++] = triangles[i + 2] + verticesOffset; + } + let index = verticesOffset * verticesStride; + for (let i = 0; i < points.length; i += 2) { + vertices[index] = points[i]; + vertices[index + 1] = points[i + 1]; + index += verticesStride; + } + } + + "use strict"; + const emptyArray = []; + const buildPolygon = { + extension: { + type: ExtensionType.ShapeBuilder, + name: "polygon" + }, + build(shape, points) { + for (let i = 0; i < shape.points.length; i++) { + points[i] = shape.points[i]; + } + return true; + }, + triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) { + triangulateWithHoles(points, emptyArray, vertices, verticesStride, verticesOffset, indices, indicesOffset); + } + }; + + "use strict"; + const buildRectangle = { + extension: { + type: ExtensionType.ShapeBuilder, + name: "rectangle" + }, + build(shape, points) { + const rectData = shape; + const x = rectData.x; + const y = rectData.y; + const width = rectData.width; + const height = rectData.height; + if (!(width > 0 && height > 0)) { + return false; + } + points[0] = x; + points[1] = y; + points[2] = x + width; + points[3] = y; + points[4] = x + width; + points[5] = y + height; + points[6] = x; + points[7] = y + height; + return true; + }, + triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) { + let count = 0; + verticesOffset *= verticesStride; + vertices[verticesOffset + count] = points[0]; + vertices[verticesOffset + count + 1] = points[1]; + count += verticesStride; + vertices[verticesOffset + count] = points[2]; + vertices[verticesOffset + count + 1] = points[3]; + count += verticesStride; + vertices[verticesOffset + count] = points[6]; + vertices[verticesOffset + count + 1] = points[7]; + count += verticesStride; + vertices[verticesOffset + count] = points[4]; + vertices[verticesOffset + count + 1] = points[5]; + count += verticesStride; + const verticesIndex = verticesOffset / verticesStride; + indices[indicesOffset++] = verticesIndex; + indices[indicesOffset++] = verticesIndex + 1; + indices[indicesOffset++] = verticesIndex + 2; + indices[indicesOffset++] = verticesIndex + 1; + indices[indicesOffset++] = verticesIndex + 3; + indices[indicesOffset++] = verticesIndex + 2; + } + }; + + "use strict"; + const buildTriangle = { + extension: { + type: ExtensionType.ShapeBuilder, + name: "triangle" + }, + build(shape, points) { + points[0] = shape.x; + points[1] = shape.y; + points[2] = shape.x2; + points[3] = shape.y2; + points[4] = shape.x3; + points[5] = shape.y3; + return true; + }, + triangulate(points, vertices, verticesStride, verticesOffset, indices, indicesOffset) { + let count = 0; + verticesOffset *= verticesStride; + vertices[verticesOffset + count] = points[0]; + vertices[verticesOffset + count + 1] = points[1]; + count += verticesStride; + vertices[verticesOffset + count] = points[2]; + vertices[verticesOffset + count + 1] = points[3]; + count += verticesStride; + vertices[verticesOffset + count] = points[4]; + vertices[verticesOffset + count + 1] = points[5]; + const verticesIndex = verticesOffset / verticesStride; + indices[indicesOffset++] = verticesIndex; + indices[indicesOffset++] = verticesIndex + 1; + indices[indicesOffset++] = verticesIndex + 2; + } + }; + + "use strict"; + var __defProp$_ = Object.defineProperty; + var __getOwnPropSymbols$10 = Object.getOwnPropertySymbols; + var __hasOwnProp$10 = Object.prototype.hasOwnProperty; + var __propIsEnum$10 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$_ = (obj, key, value) => key in obj ? __defProp$_(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$_ = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$10.call(b, prop)) + __defNormalProp$_(a, prop, b[prop]); + if (__getOwnPropSymbols$10) + for (var prop of __getOwnPropSymbols$10(b)) { + if (__propIsEnum$10.call(b, prop)) + __defNormalProp$_(a, prop, b[prop]); + } + return a; + }; + const emptyColorStops = [{ offset: 0, color: "white" }, { offset: 1, color: "black" }]; + const _FillGradient = class _FillGradient { + constructor(...args) { + /** + * Unique identifier for this gradient instance + * @internal + */ + this.uid = uid$1("fillGradient"); + /** + * Internal tick counter to track changes in the gradient. + * This is used to invalidate the gradient when the texture changes. + * @internal + */ + this._tick = 0; + /** Type of gradient - currently only supports 'linear' */ + this.type = "linear"; + /** Array of color stops defining the gradient */ + this.colorStops = []; + var _a; + let options = ensureGradientOptions(args); + const defaults = options.type === "radial" ? _FillGradient.defaultRadialOptions : _FillGradient.defaultLinearOptions; + options = __spreadValues$_(__spreadValues$_({}, defaults), definedProps(options)); + this._textureSize = options.textureSize; + this._wrapMode = options.wrapMode; + if (options.type === "radial") { + this.center = options.center; + this.outerCenter = (_a = options.outerCenter) != null ? _a : this.center; + this.innerRadius = options.innerRadius; + this.outerRadius = options.outerRadius; + this.scale = options.scale; + this.rotation = options.rotation; + } else { + this.start = options.start; + this.end = options.end; + } + this.textureSpace = options.textureSpace; + this.type = options.type; + options.colorStops.forEach((stop) => { + this.addColorStop(stop.offset, stop.color); + }); + } + /** + * Adds a color stop to the gradient + * @param offset - Position of the stop (0-1) + * @param color - Color of the stop + * @returns This gradient instance for chaining + */ + addColorStop(offset, color) { + this.colorStops.push({ offset, color: Color.shared.setValue(color).toHexa() }); + return this; + } + /** + * Builds the internal texture and transform for the gradient. + * Called automatically when the gradient is first used. + * @internal + */ + buildLinearGradient() { + if (this.texture) return; + let { x: x0, y: y0 } = this.start; + let { x: x1, y: y1 } = this.end; + let dx = x1 - x0; + let dy = y1 - y0; + const flip = dx < 0 || dy < 0; + if (this._wrapMode === "clamp-to-edge") { + if (dx < 0) { + const temp = x0; + x0 = x1; + x1 = temp; + dx *= -1; + } + if (dy < 0) { + const temp = y0; + y0 = y1; + y1 = temp; + dy *= -1; + } + } + const colorStops = this.colorStops.length ? this.colorStops : emptyColorStops; + const defaultSize = this._textureSize; + const { canvas, context } = getCanvas(defaultSize, 1); + const gradient = !flip ? context.createLinearGradient(0, 0, this._textureSize, 0) : context.createLinearGradient(this._textureSize, 0, 0, 0); + addColorStops(gradient, colorStops); + context.fillStyle = gradient; + context.fillRect(0, 0, defaultSize, 1); + this.texture = new Texture({ + source: new ImageSource({ + resource: canvas, + addressMode: this._wrapMode + }) + }); + const dist = Math.sqrt(dx * dx + dy * dy); + const angle = Math.atan2(dy, dx); + const m = new Matrix(); + m.scale(dist / defaultSize, 1); + m.rotate(angle); + m.translate(x0, y0); + if (this.textureSpace === "local") { + m.scale(defaultSize, defaultSize); + } + this.transform = m; + } + /** + * Builds the internal texture and transform for the gradient. + * Called automatically when the gradient is first used. + * @internal + */ + buildGradient() { + if (!this.texture) this._tick++; + if (this.type === "linear") { + this.buildLinearGradient(); + } else { + this.buildRadialGradient(); + } + } + /** + * Builds the internal texture and transform for the radial gradient. + * Called automatically when the gradient is first used. + * @internal + */ + buildRadialGradient() { + if (this.texture) return; + const colorStops = this.colorStops.length ? this.colorStops : emptyColorStops; + const defaultSize = this._textureSize; + const { canvas, context } = getCanvas(defaultSize, defaultSize); + const { x: x0, y: y0 } = this.center; + const { x: x1, y: y1 } = this.outerCenter; + const r0 = this.innerRadius; + const r1 = this.outerRadius; + const ox = x1 - r1; + const oy = y1 - r1; + const scale = defaultSize / (r1 * 2); + const cx = (x0 - ox) * scale; + const cy = (y0 - oy) * scale; + const gradient = context.createRadialGradient( + cx, + cy, + r0 * scale, + (x1 - ox) * scale, + (y1 - oy) * scale, + r1 * scale + ); + addColorStops(gradient, colorStops); + context.fillStyle = colorStops[colorStops.length - 1].color; + context.fillRect(0, 0, defaultSize, defaultSize); + context.fillStyle = gradient; + context.translate(cx, cy); + context.rotate(this.rotation); + context.scale(1, this.scale); + context.translate(-cx, -cy); + context.fillRect(0, 0, defaultSize, defaultSize); + this.texture = new Texture({ + source: new ImageSource({ + resource: canvas, + addressMode: this._wrapMode + }) + }); + const m = new Matrix(); + m.scale(1 / scale, 1 / scale); + m.translate(ox, oy); + if (this.textureSpace === "local") { + m.scale(defaultSize, defaultSize); + } + this.transform = m; + } + /** Destroys the gradient, releasing resources. This will also destroy the internal texture. */ + destroy() { + var _a; + (_a = this.texture) == null ? void 0 : _a.destroy(true); + this.texture = null; + this.transform = null; + this.colorStops = []; + this.start = null; + this.end = null; + this.center = null; + this.outerCenter = null; + } + /** + * Returns a unique key for this gradient instance. + * This key is used for caching and texture management. + * @returns {string} Unique key for the gradient + */ + get styleKey() { + return `fill-gradient-${this.uid}-${this._tick}`; + } + }; + /** Default options for creating a gradient fill */ + _FillGradient.defaultLinearOptions = { + start: { x: 0, y: 0 }, + end: { x: 0, y: 1 }, + colorStops: [], + textureSpace: "local", + type: "linear", + textureSize: 256, + wrapMode: "clamp-to-edge" + }; + /** Default options for creating a radial gradient fill */ + _FillGradient.defaultRadialOptions = { + center: { x: 0.5, y: 0.5 }, + innerRadius: 0, + outerRadius: 0.5, + colorStops: [], + scale: 1, + textureSpace: "local", + type: "radial", + textureSize: 256, + wrapMode: "clamp-to-edge" + }; + let FillGradient = _FillGradient; + function addColorStops(gradient, colorStops) { + for (let i = 0; i < colorStops.length; i++) { + const stop = colorStops[i]; + gradient.addColorStop(stop.offset, stop.color); + } + } + function getCanvas(width, height) { + const canvas = DOMAdapter.get().createCanvas(width, height); + const context = canvas.getContext("2d"); + return { canvas, context }; + } + function ensureGradientOptions(args) { + var _a, _b; + let options = (_a = args[0]) != null ? _a : {}; + if (typeof options === "number" || args[1]) { + deprecation("8.5.2", `use options object instead`); + options = { + type: "linear", + start: { x: args[0], y: args[1] }, + end: { x: args[2], y: args[3] }, + textureSpace: args[4], + textureSize: (_b = args[5]) != null ? _b : FillGradient.defaultLinearOptions.textureSize + }; + } + return options; + } + + "use strict"; + const tempTextureMatrix$2 = new Matrix(); + const tempRect$3 = new Rectangle(); + function generateTextureMatrix(out, style, shape, matrix) { + const textureMatrix = style.matrix ? out.copyFrom(style.matrix).invert() : out.identity(); + if (style.textureSpace === "local") { + const bounds = shape.getBounds(tempRect$3); + if (style.width) { + bounds.pad(style.width); + } + const { x: tx, y: ty } = bounds; + const sx = 1 / bounds.width; + const sy = 1 / bounds.height; + const mTx = -tx * sx; + const mTy = -ty * sy; + const a1 = textureMatrix.a; + const b1 = textureMatrix.b; + const c1 = textureMatrix.c; + const d1 = textureMatrix.d; + textureMatrix.a *= sx; + textureMatrix.b *= sx; + textureMatrix.c *= sy; + textureMatrix.d *= sy; + textureMatrix.tx = mTx * a1 + mTy * c1 + textureMatrix.tx; + textureMatrix.ty = mTx * b1 + mTy * d1 + textureMatrix.ty; + } else { + textureMatrix.translate(style.texture.frame.x, style.texture.frame.y); + textureMatrix.scale(1 / style.texture.source.width, 1 / style.texture.source.height); + } + const sourceStyle = style.texture.source.style; + if (!(style.fill instanceof FillGradient) && sourceStyle.addressMode === "clamp-to-edge") { + sourceStyle.addressMode = "repeat"; + sourceStyle.update(); + } + if (matrix) { + textureMatrix.append(tempTextureMatrix$2.copyFrom(matrix).invert()); + } + return textureMatrix; + } + + "use strict"; + const shapeBuilders = {}; + extensions.handleByMap(ExtensionType.ShapeBuilder, shapeBuilders); + extensions.add(buildRectangle, buildPolygon, buildTriangle, buildCircle, buildEllipse, buildRoundedRectangle); + const tempRect$2 = new Rectangle(); + const tempTextureMatrix$1 = new Matrix(); + function buildContextBatches(context, gpuContext) { + const { geometryData, batches } = gpuContext; + batches.length = 0; + geometryData.indices.length = 0; + geometryData.vertices.length = 0; + geometryData.uvs.length = 0; + for (let i = 0; i < context.instructions.length; i++) { + const instruction = context.instructions[i]; + if (instruction.action === "texture") { + addTextureToGeometryData(instruction.data, batches, geometryData); + } else if (instruction.action === "fill" || instruction.action === "stroke") { + const isStroke = instruction.action === "stroke"; + const shapePath = instruction.data.path.shapePath; + const style = instruction.data.style; + const hole = instruction.data.hole; + if (isStroke && hole) { + addShapePathToGeometryData(hole.shapePath, style, true, batches, geometryData); + } + if (hole) { + shapePath.shapePrimitives[shapePath.shapePrimitives.length - 1].holes = hole.shapePath.shapePrimitives; + } + addShapePathToGeometryData(shapePath, style, isStroke, batches, geometryData); + } + } + } + function addTextureToGeometryData(data, batches, geometryData) { + const points = []; + const build = shapeBuilders.rectangle; + const rect = tempRect$2; + rect.x = data.dx; + rect.y = data.dy; + rect.width = data.dw; + rect.height = data.dh; + const matrix = data.transform; + if (!build.build(rect, points)) { + return; + } + const { vertices, uvs, indices } = geometryData; + const indexOffset = indices.length; + const vertOffset = vertices.length / 2; + if (matrix) { + transformVertices(points, matrix); + } + build.triangulate(points, vertices, 2, vertOffset, indices, indexOffset); + const texture = data.image; + const textureUvs = texture.uvs; + uvs.push( + textureUvs.x0, + textureUvs.y0, + textureUvs.x1, + textureUvs.y1, + textureUvs.x3, + textureUvs.y3, + textureUvs.x2, + textureUvs.y2 + ); + const graphicsBatch = BigPool.get(BatchableGraphics); + graphicsBatch.indexOffset = indexOffset; + graphicsBatch.indexSize = indices.length - indexOffset; + graphicsBatch.attributeOffset = vertOffset; + graphicsBatch.attributeSize = vertices.length / 2 - vertOffset; + graphicsBatch.baseColor = data.style; + graphicsBatch.alpha = data.alpha; + graphicsBatch.texture = texture; + graphicsBatch.geometryData = geometryData; + batches.push(graphicsBatch); + } + function addShapePathToGeometryData(shapePath, style, isStroke, batches, geometryData) { + const { vertices, uvs, indices } = geometryData; + shapePath.shapePrimitives.forEach(({ shape, transform: matrix, holes }) => { + var _a; + const points = []; + const build = shapeBuilders[shape.type]; + if (!build.build(shape, points)) { + return; + } + const indexOffset = indices.length; + const vertOffset = vertices.length / 2; + let topology = "triangle-list"; + if (matrix) { + transformVertices(points, matrix); + } + if (!isStroke) { + if (holes) { + const holeIndices = []; + const otherPoints = points.slice(); + const holeArrays = getHoleArrays(holes); + holeArrays.forEach((holePoints) => { + holeIndices.push(otherPoints.length / 2); + otherPoints.push(...holePoints); + }); + triangulateWithHoles(otherPoints, holeIndices, vertices, 2, vertOffset, indices, indexOffset); + } else { + build.triangulate(points, vertices, 2, vertOffset, indices, indexOffset); + } + } else { + const close = (_a = shape.closePath) != null ? _a : true; + const lineStyle = style; + if (!lineStyle.pixelLine) { + buildLine(points, lineStyle, false, close, vertices, indices); + } else { + buildPixelLine(points, close, vertices, indices); + topology = "line-list"; + } + } + const uvsOffset = uvs.length / 2; + const texture = style.texture; + if (texture !== Texture.WHITE) { + const textureMatrix = generateTextureMatrix(tempTextureMatrix$1, style, shape, matrix); + buildUvs(vertices, 2, vertOffset, uvs, uvsOffset, 2, vertices.length / 2 - vertOffset, textureMatrix); + } else { + buildSimpleUvs(uvs, uvsOffset, 2, vertices.length / 2 - vertOffset); + } + const graphicsBatch = BigPool.get(BatchableGraphics); + graphicsBatch.indexOffset = indexOffset; + graphicsBatch.indexSize = indices.length - indexOffset; + graphicsBatch.attributeOffset = vertOffset; + graphicsBatch.attributeSize = vertices.length / 2 - vertOffset; + graphicsBatch.baseColor = style.color; + graphicsBatch.alpha = style.alpha; + graphicsBatch.texture = texture; + graphicsBatch.geometryData = geometryData; + graphicsBatch.topology = topology; + batches.push(graphicsBatch); + }); + } + function getHoleArrays(holePrimitives) { + const holeArrays = []; + for (let k = 0; k < holePrimitives.length; k++) { + const holePrimitive = holePrimitives[k].shape; + const holePoints = []; + const holeBuilder = shapeBuilders[holePrimitive.type]; + if (holeBuilder.build(holePrimitive, holePoints)) { + holeArrays.push(holePoints); + } + } + return holeArrays; + } + + "use strict"; + class GpuGraphicsContext { + constructor() { + this.batches = []; + this.geometryData = { + vertices: [], + uvs: [], + indices: [] + }; + } + reset() { + if (this.batches) { + this.batches.forEach((batch) => { + BigPool.return(batch); + }); + } + if (this.graphicsData) { + BigPool.return(this.graphicsData); + } + this.isBatchable = false; + this.context = null; + this.batches.length = 0; + this.geometryData.indices.length = 0; + this.geometryData.vertices.length = 0; + this.geometryData.uvs.length = 0; + this.graphicsData = null; + } + destroy() { + this.reset(); + this.batches = null; + this.geometryData = null; + } + } + class GraphicsContextRenderData { + constructor() { + this.instructions = new InstructionSet(); + } + init(options) { + const maxTextures = options.maxTextures; + this.batcher ? this.batcher._updateMaxTextures(maxTextures) : this.batcher = new DefaultBatcher({ maxTextures }); + this.instructions.reset(); + } + /** + * @deprecated since version 8.0.0 + * Use `batcher.geometry` instead. + * @see {Batcher#geometry} + */ + get geometry() { + deprecation(v8_3_4, "GraphicsContextRenderData#geometry is deprecated, please use batcher.geometry instead."); + return this.batcher.geometry; + } + destroy() { + this.batcher.destroy(); + this.instructions.destroy(); + this.batcher = null; + this.instructions = null; + } + } + const _GraphicsContextSystem = class _GraphicsContextSystem { + constructor(renderer) { + this._renderer = renderer; + this._managedContexts = new GCManagedHash({ renderer, type: "resource", name: "graphicsContext" }); + } + /** + * Runner init called, update the default options + * @ignore + */ + init(options) { + var _a; + _GraphicsContextSystem.defaultOptions.bezierSmoothness = (_a = options == null ? void 0 : options.bezierSmoothness) != null ? _a : _GraphicsContextSystem.defaultOptions.bezierSmoothness; + } + /** + * Returns the render data for a given GraphicsContext. + * @param context - The GraphicsContext to get the render data for. + * @internal + */ + getContextRenderData(context) { + return context._gpuData[this._renderer.uid].graphicsData || this._initContextRenderData(context); + } + /** + * Updates the GPU context for a given GraphicsContext. + * If the context is dirty, it will rebuild the batches and geometry data. + * @param context - The GraphicsContext to update. + * @returns The updated GpuGraphicsContext. + * @internal + */ + updateGpuContext(context) { + const hasContext = !!context._gpuData[this._renderer.uid]; + const gpuContext = context._gpuData[this._renderer.uid] || this._initContext(context); + if (context.dirty || !hasContext) { + if (hasContext) { + gpuContext.reset(); + } + buildContextBatches(context, gpuContext); + const batchMode = context.batchMode; + if (context.customShader || batchMode === "no-batch") { + gpuContext.isBatchable = false; + } else if (batchMode === "auto") { + gpuContext.isBatchable = gpuContext.geometryData.vertices.length < 400; + } else { + gpuContext.isBatchable = true; + } + context.dirty = false; + } + return gpuContext; + } + /** + * Returns the GpuGraphicsContext for a given GraphicsContext. + * If it does not exist, it will initialize a new one. + * @param context - The GraphicsContext to get the GpuGraphicsContext for. + * @returns The GpuGraphicsContext for the given GraphicsContext. + * @internal + */ + getGpuContext(context) { + return context._gpuData[this._renderer.uid] || this._initContext(context); + } + _initContextRenderData(context) { + const graphicsData = BigPool.get(GraphicsContextRenderData, { + maxTextures: this._renderer.limits.maxBatchableTextures + }); + const gpuContext = context._gpuData[this._renderer.uid]; + const { batches, geometryData } = gpuContext; + gpuContext.graphicsData = graphicsData; + const vertexSize = geometryData.vertices.length; + const indexSize = geometryData.indices.length; + for (let i = 0; i < batches.length; i++) { + batches[i].applyTransform = false; + } + const batcher = graphicsData.batcher; + batcher.ensureAttributeBuffer(vertexSize); + batcher.ensureIndexBuffer(indexSize); + batcher.begin(); + for (let i = 0; i < batches.length; i++) { + const batch = batches[i]; + batcher.add(batch); + } + batcher.finish(graphicsData.instructions); + const geometry = batcher.geometry; + geometry.indexBuffer.setDataWithSize(batcher.indexBuffer, batcher.indexSize, true); + geometry.buffers[0].setDataWithSize(batcher.attributeBuffer.float32View, batcher.attributeSize, true); + const drawBatches = batcher.batches; + for (let i = 0; i < drawBatches.length; i++) { + const batch = drawBatches[i]; + batch.bindGroup = getTextureBatchBindGroup( + batch.textures.textures, + batch.textures.count, + this._renderer.limits.maxBatchableTextures + ); + } + return graphicsData; + } + _initContext(context) { + const gpuContext = new GpuGraphicsContext(); + gpuContext.context = context; + context._gpuData[this._renderer.uid] = gpuContext; + this._managedContexts.add(context); + return gpuContext; + } + destroy() { + this._managedContexts.destroy(); + this._renderer = null; + } + }; + /** @ignore */ + _GraphicsContextSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "graphicsContext" + }; + /** The default options for the GraphicsContextSystem. */ + _GraphicsContextSystem.defaultOptions = { + /** + * A value from 0 to 1 that controls the smoothness of bezier curves (the higher the smoother) + * @default 0.5 + */ + bezierSmoothness: 0.5 + }; + let GraphicsContextSystem = _GraphicsContextSystem; + + "use strict"; + const RECURSION_LIMIT$1 = 8; + const FLT_EPSILON$1 = 11920929e-14; + const PATH_DISTANCE_EPSILON$1 = 1; + const curveAngleToleranceEpsilon$1 = 0.01; + const mAngleTolerance$1 = 0; + const mCuspLimit = 0; + function buildAdaptiveBezier(points, sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, smoothness) { + const scale = 1; + const smoothing = Math.min( + 0.99, + // a value of 1.0 actually inverts smoothing, so we cap it at 0.99 + Math.max(0, smoothness != null ? smoothness : GraphicsContextSystem.defaultOptions.bezierSmoothness) + ); + let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale; + distanceTolerance *= distanceTolerance; + begin$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance); + return points; + } + function begin$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance) { + recursive$1(sX, sY, cp1x, cp1y, cp2x, cp2y, eX, eY, points, distanceTolerance, 0); + points.push(eX, eY); + } + function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) { + if (level > RECURSION_LIMIT$1) { + return; + } + const pi = Math.PI; + const x12 = (x1 + x2) / 2; + const y12 = (y1 + y2) / 2; + const x23 = (x2 + x3) / 2; + const y23 = (y2 + y3) / 2; + const x34 = (x3 + x4) / 2; + const y34 = (y3 + y4) / 2; + const x123 = (x12 + x23) / 2; + const y123 = (y12 + y23) / 2; + const x234 = (x23 + x34) / 2; + const y234 = (y23 + y34) / 2; + const x1234 = (x123 + x234) / 2; + const y1234 = (y123 + y234) / 2; + if (level > 0) { + let dx = x4 - x1; + let dy = y4 - y1; + const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx); + const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx); + let da1; + let da2; + if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) { + if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) { + if (mAngleTolerance$1 < curveAngleToleranceEpsilon$1) { + points.push(x1234, y1234); + return; + } + const a23 = Math.atan2(y3 - y2, x3 - x2); + da1 = Math.abs(a23 - Math.atan2(y2 - y1, x2 - x1)); + da2 = Math.abs(Math.atan2(y4 - y3, x4 - x3) - a23); + if (da1 >= pi) da1 = 2 * pi - da1; + if (da2 >= pi) da2 = 2 * pi - da2; + if (da1 + da2 < mAngleTolerance$1) { + points.push(x1234, y1234); + return; + } + if (mCuspLimit !== 0) { + if (da1 > mCuspLimit) { + points.push(x2, y2); + return; + } + if (da2 > mCuspLimit) { + points.push(x3, y3); + return; + } + } + } + } else if (d2 > FLT_EPSILON$1) { + if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) { + if (mAngleTolerance$1 < curveAngleToleranceEpsilon$1) { + points.push(x1234, y1234); + return; + } + da1 = Math.abs(Math.atan2(y3 - y2, x3 - x2) - Math.atan2(y2 - y1, x2 - x1)); + if (da1 >= pi) da1 = 2 * pi - da1; + if (da1 < mAngleTolerance$1) { + points.push(x2, y2); + points.push(x3, y3); + return; + } + if (mCuspLimit !== 0) { + if (da1 > mCuspLimit) { + points.push(x2, y2); + return; + } + } + } + } else if (d3 > FLT_EPSILON$1) { + if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) { + if (mAngleTolerance$1 < curveAngleToleranceEpsilon$1) { + points.push(x1234, y1234); + return; + } + da1 = Math.abs(Math.atan2(y4 - y3, x4 - x3) - Math.atan2(y3 - y2, x3 - x2)); + if (da1 >= pi) da1 = 2 * pi - da1; + if (da1 < mAngleTolerance$1) { + points.push(x2, y2); + points.push(x3, y3); + return; + } + if (mCuspLimit !== 0) { + if (da1 > mCuspLimit) { + points.push(x3, y3); + return; + } + } + } + } else { + dx = x1234 - (x1 + x4) / 2; + dy = y1234 - (y1 + y4) / 2; + if (dx * dx + dy * dy <= distanceTolerance) { + points.push(x1234, y1234); + return; + } + } + } + recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1); + recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1); + } + + "use strict"; + const RECURSION_LIMIT = 8; + const FLT_EPSILON = 11920929e-14; + const PATH_DISTANCE_EPSILON = 1; + const curveAngleToleranceEpsilon = 0.01; + const mAngleTolerance = 0; + function buildAdaptiveQuadratic(points, sX, sY, cp1x, cp1y, eX, eY, smoothness) { + const scale = 1; + const smoothing = Math.min( + 0.99, + // a value of 1.0 actually inverts smoothing, so we cap it at 0.99 + Math.max(0, smoothness != null ? smoothness : GraphicsContextSystem.defaultOptions.bezierSmoothness) + ); + let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale; + distanceTolerance *= distanceTolerance; + begin(sX, sY, cp1x, cp1y, eX, eY, points, distanceTolerance); + return points; + } + function begin(sX, sY, cp1x, cp1y, eX, eY, points, distanceTolerance) { + recursive(points, sX, sY, cp1x, cp1y, eX, eY, distanceTolerance, 0); + points.push(eX, eY); + } + function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) { + if (level > RECURSION_LIMIT) { + return; + } + const pi = Math.PI; + const x12 = (x1 + x2) / 2; + const y12 = (y1 + y2) / 2; + const x23 = (x2 + x3) / 2; + const y23 = (y2 + y3) / 2; + const x123 = (x12 + x23) / 2; + const y123 = (y12 + y23) / 2; + let dx = x3 - x1; + let dy = y3 - y1; + const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx); + if (d > FLT_EPSILON) { + if (d * d <= distanceTolerance * (dx * dx + dy * dy)) { + if (mAngleTolerance < curveAngleToleranceEpsilon) { + points.push(x123, y123); + return; + } + let da = Math.abs(Math.atan2(y3 - y2, x3 - x2) - Math.atan2(y2 - y1, x2 - x1)); + if (da >= pi) da = 2 * pi - da; + if (da < mAngleTolerance) { + points.push(x123, y123); + return; + } + } + } else { + dx = x123 - (x1 + x3) / 2; + dy = y123 - (y1 + y3) / 2; + if (dx * dx + dy * dy <= distanceTolerance) { + points.push(x123, y123); + return; + } + } + recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1); + recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1); + } + + "use strict"; + function buildArc(points, x, y, radius, start, end, clockwise, steps) { + let dist = Math.abs(start - end); + if (!clockwise && start > end) { + dist = 2 * Math.PI - dist; + } else if (clockwise && end > start) { + dist = 2 * Math.PI - dist; + } + steps || (steps = Math.max(6, Math.floor(6 * Math.pow(radius, 1 / 3) * (dist / Math.PI)))); + steps = Math.max(steps, 3); + let f = dist / steps; + let t = start; + f *= clockwise ? -1 : 1; + for (let i = 0; i < steps + 1; i++) { + const cs = Math.cos(t); + const sn = Math.sin(t); + const nx = x + cs * radius; + const ny = y + sn * radius; + points.push(nx, ny); + t += f; + } + } + + "use strict"; + function buildArcTo(points, x1, y1, x2, y2, radius) { + const fromX = points[points.length - 2]; + const fromY = points[points.length - 1]; + const a1 = fromY - y1; + const b1 = fromX - x1; + const a2 = y2 - y1; + const b2 = x2 - x1; + const mm = Math.abs(a1 * b2 - b1 * a2); + if (mm < 1e-8 || radius === 0) { + if (points[points.length - 2] !== x1 || points[points.length - 1] !== y1) { + points.push(x1, y1); + } + return; + } + const dd = a1 * a1 + b1 * b1; + const cc = a2 * a2 + b2 * b2; + const tt = a1 * a2 + b1 * b2; + const k1 = radius * Math.sqrt(dd) / mm; + const k2 = radius * Math.sqrt(cc) / mm; + const j1 = k1 * tt / dd; + const j2 = k2 * tt / cc; + const cx = k1 * b2 + k2 * b1; + const cy = k1 * a2 + k2 * a1; + const px = b1 * (k2 + j1); + const py = a1 * (k2 + j1); + const qx = b2 * (k1 + j2); + const qy = a2 * (k1 + j2); + const startAngle = Math.atan2(py - cy, px - cx); + const endAngle = Math.atan2(qy - cy, qx - cx); + buildArc( + points, + cx + x1, + cy + y1, + radius, + startAngle, + endAngle, + b1 * a2 > b2 * a1 + ); + } + + "use strict"; + const TAU = Math.PI * 2; + const out = { + centerX: 0, + centerY: 0, + ang1: 0, + ang2: 0 + }; + const mapToEllipse = ({ x, y }, rx, ry, cosPhi, sinPhi, centerX, centerY, out2) => { + x *= rx; + y *= ry; + const xp = cosPhi * x - sinPhi * y; + const yp = sinPhi * x + cosPhi * y; + out2.x = xp + centerX; + out2.y = yp + centerY; + return out2; + }; + function approxUnitArc(ang1, ang2) { + const a1 = ang2 === -1.5707963267948966 ? -0.551915024494 : 4 / 3 * Math.tan(ang2 / 4); + const a = ang2 === 1.5707963267948966 ? 0.551915024494 : a1; + const x1 = Math.cos(ang1); + const y1 = Math.sin(ang1); + const x2 = Math.cos(ang1 + ang2); + const y2 = Math.sin(ang1 + ang2); + return [ + { + x: x1 - y1 * a, + y: y1 + x1 * a + }, + { + x: x2 + y2 * a, + y: y2 - x2 * a + }, + { + x: x2, + y: y2 + } + ]; + } + const vectorAngle = (ux, uy, vx, vy) => { + const sign = ux * vy - uy * vx < 0 ? -1 : 1; + let dot = ux * vx + uy * vy; + if (dot > 1) { + dot = 1; + } + if (dot < -1) { + dot = -1; + } + return sign * Math.acos(dot); + }; + const getArcCenter = (px, py, cx, cy, rx, ry, largeArcFlag, sweepFlag, sinPhi, cosPhi, pxp, pyp, out2) => { + const rxSq = Math.pow(rx, 2); + const rySq = Math.pow(ry, 2); + const pxpSq = Math.pow(pxp, 2); + const pypSq = Math.pow(pyp, 2); + let radicant = rxSq * rySq - rxSq * pypSq - rySq * pxpSq; + if (radicant < 0) { + radicant = 0; + } + radicant /= rxSq * pypSq + rySq * pxpSq; + radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1); + const centerXp = radicant * rx / ry * pyp; + const centerYp = radicant * -ry / rx * pxp; + const centerX = cosPhi * centerXp - sinPhi * centerYp + (px + cx) / 2; + const centerY = sinPhi * centerXp + cosPhi * centerYp + (py + cy) / 2; + const vx1 = (pxp - centerXp) / rx; + const vy1 = (pyp - centerYp) / ry; + const vx2 = (-pxp - centerXp) / rx; + const vy2 = (-pyp - centerYp) / ry; + const ang1 = vectorAngle(1, 0, vx1, vy1); + let ang2 = vectorAngle(vx1, vy1, vx2, vy2); + if (sweepFlag === 0 && ang2 > 0) { + ang2 -= TAU; + } + if (sweepFlag === 1 && ang2 < 0) { + ang2 += TAU; + } + out2.centerX = centerX; + out2.centerY = centerY; + out2.ang1 = ang1; + out2.ang2 = ang2; + }; + function buildArcToSvg(points, px, py, cx, cy, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0) { + if (rx === 0 || ry === 0) { + return; + } + const sinPhi = Math.sin(xAxisRotation * TAU / 360); + const cosPhi = Math.cos(xAxisRotation * TAU / 360); + const pxp = cosPhi * (px - cx) / 2 + sinPhi * (py - cy) / 2; + const pyp = -sinPhi * (px - cx) / 2 + cosPhi * (py - cy) / 2; + if (pxp === 0 && pyp === 0) { + return; + } + rx = Math.abs(rx); + ry = Math.abs(ry); + const lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2); + if (lambda > 1) { + rx *= Math.sqrt(lambda); + ry *= Math.sqrt(lambda); + } + getArcCenter( + px, + py, + cx, + cy, + rx, + ry, + largeArcFlag, + sweepFlag, + sinPhi, + cosPhi, + pxp, + pyp, + out + ); + let { ang1, ang2 } = out; + const { centerX, centerY } = out; + let ratio = Math.abs(ang2) / (TAU / 4); + if (Math.abs(1 - ratio) < 1e-7) { + ratio = 1; + } + const segments = Math.max(Math.ceil(ratio), 1); + ang2 /= segments; + let lastX = points[points.length - 2]; + let lastY = points[points.length - 1]; + const outCurvePoint = { x: 0, y: 0 }; + for (let i = 0; i < segments; i++) { + const curve = approxUnitArc(ang1, ang2); + const { x: x1, y: y1 } = mapToEllipse(curve[0], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint); + const { x: x2, y: y2 } = mapToEllipse(curve[1], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint); + const { x, y } = mapToEllipse(curve[2], rx, ry, cosPhi, sinPhi, centerX, centerY, outCurvePoint); + buildAdaptiveBezier( + points, + lastX, + lastY, + x1, + y1, + x2, + y2, + x, + y + ); + lastX = x; + lastY = y; + ang1 += ang2; + } + } + + "use strict"; + function roundedShapeArc(g, points, radius) { + var _a; + const vecFrom = (p, pp) => { + const x = pp.x - p.x; + const y = pp.y - p.y; + const len = Math.sqrt(x * x + y * y); + const nx = x / len; + const ny = y / len; + return { len, nx, ny }; + }; + const sharpCorner = (i, p) => { + if (i === 0) { + g.moveTo(p.x, p.y); + } else { + g.lineTo(p.x, p.y); + } + }; + let p1 = points[points.length - 1]; + for (let i = 0; i < points.length; i++) { + const p2 = points[i % points.length]; + const pRadius = (_a = p2.radius) != null ? _a : radius; + if (pRadius <= 0) { + sharpCorner(i, p2); + p1 = p2; + continue; + } + const p3 = points[(i + 1) % points.length]; + const v1 = vecFrom(p2, p1); + const v2 = vecFrom(p2, p3); + if (v1.len < 1e-4 || v2.len < 1e-4) { + sharpCorner(i, p2); + p1 = p2; + continue; + } + let angle = Math.asin(v1.nx * v2.ny - v1.ny * v2.nx); + let radDirection = 1; + let drawDirection = false; + if (v1.nx * v2.nx - v1.ny * -v2.ny < 0) { + if (angle < 0) { + angle = Math.PI + angle; + } else { + angle = Math.PI - angle; + radDirection = -1; + drawDirection = true; + } + } else if (angle > 0) { + radDirection = -1; + drawDirection = true; + } + const halfAngle = angle / 2; + let cRadius; + let lenOut = Math.abs( + Math.cos(halfAngle) * pRadius / Math.sin(halfAngle) + ); + if (lenOut > Math.min(v1.len / 2, v2.len / 2)) { + lenOut = Math.min(v1.len / 2, v2.len / 2); + cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle)); + } else { + cRadius = pRadius; + } + const cX = p2.x + v2.nx * lenOut + -v2.ny * cRadius * radDirection; + const cY = p2.y + v2.ny * lenOut + v2.nx * cRadius * radDirection; + const startAngle = Math.atan2(v1.ny, v1.nx) + Math.PI / 2 * radDirection; + const endAngle = Math.atan2(v2.ny, v2.nx) - Math.PI / 2 * radDirection; + if (i === 0) { + g.moveTo( + cX + Math.cos(startAngle) * cRadius, + cY + Math.sin(startAngle) * cRadius + ); + } + g.arc(cX, cY, cRadius, startAngle, endAngle, drawDirection); + p1 = p2; + } + } + function roundedShapeQuadraticCurve(g, points, radius, smoothness) { + var _a; + const distance = (p1, p2) => Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2); + const pointLerp = (p1, p2, t) => ({ + x: p1.x + (p2.x - p1.x) * t, + y: p1.y + (p2.y - p1.y) * t + }); + const numPoints = points.length; + for (let i = 0; i < numPoints; i++) { + const thisPoint = points[(i + 1) % numPoints]; + const pRadius = (_a = thisPoint.radius) != null ? _a : radius; + if (pRadius <= 0) { + if (i === 0) { + g.moveTo(thisPoint.x, thisPoint.y); + } else { + g.lineTo(thisPoint.x, thisPoint.y); + } + continue; + } + const lastPoint = points[i]; + const nextPoint = points[(i + 2) % numPoints]; + const lastEdgeLength = distance(lastPoint, thisPoint); + let start; + if (lastEdgeLength < 1e-4) { + start = thisPoint; + } else { + const lastOffsetDistance = Math.min(lastEdgeLength / 2, pRadius); + start = pointLerp( + thisPoint, + lastPoint, + lastOffsetDistance / lastEdgeLength + ); + } + const nextEdgeLength = distance(nextPoint, thisPoint); + let end; + if (nextEdgeLength < 1e-4) { + end = thisPoint; + } else { + const nextOffsetDistance = Math.min(nextEdgeLength / 2, pRadius); + end = pointLerp( + thisPoint, + nextPoint, + nextOffsetDistance / nextEdgeLength + ); + } + if (i === 0) { + g.moveTo(start.x, start.y); + } else { + g.lineTo(start.x, start.y); + } + g.quadraticCurveTo(thisPoint.x, thisPoint.y, end.x, end.y, smoothness); + } + } + + "use strict"; + const tempRectangle$1 = new Rectangle(); + class ShapePath { + constructor(graphicsPath2D) { + /** The list of shape primitives that make up the path. */ + this.shapePrimitives = []; + this._currentPoly = null; + this._bounds = new Bounds(); + this._graphicsPath2D = graphicsPath2D; + this.signed = graphicsPath2D.checkForHoles; + } + /** + * Sets the starting point for a new sub-path. Any subsequent drawing commands are considered part of this path. + * @param x - The x-coordinate for the starting point. + * @param y - The y-coordinate for the starting point. + * @returns The instance of the current object for chaining. + */ + moveTo(x, y) { + this.startPoly(x, y); + return this; + } + /** + * Connects the current point to a new point with a straight line. This method updates the current path. + * @param x - The x-coordinate of the new point to connect to. + * @param y - The y-coordinate of the new point to connect to. + * @returns The instance of the current object for chaining. + */ + lineTo(x, y) { + this._ensurePoly(); + const points = this._currentPoly.points; + const fromX = points[points.length - 2]; + const fromY = points[points.length - 1]; + if (fromX !== x || fromY !== y) { + points.push(x, y); + } + return this; + } + /** + * Adds an arc to the path. The arc is centered at (x, y) + * position with radius `radius` starting at `startAngle` and ending at `endAngle`. + * @param x - The x-coordinate of the arc's center. + * @param y - The y-coordinate of the arc's center. + * @param radius - The radius of the arc. + * @param startAngle - The starting angle of the arc, in radians. + * @param endAngle - The ending angle of the arc, in radians. + * @param counterclockwise - Specifies whether the arc should be drawn in the anticlockwise direction. False by default. + * @returns The instance of the current object for chaining. + */ + arc(x, y, radius, startAngle, endAngle, counterclockwise) { + this._ensurePoly(false); + const points = this._currentPoly.points; + buildArc(points, x, y, radius, startAngle, endAngle, counterclockwise); + return this; + } + /** + * Adds an arc to the path with the arc tangent to the line joining two specified points. + * The arc radius is specified by `radius`. + * @param x1 - The x-coordinate of the first point. + * @param y1 - The y-coordinate of the first point. + * @param x2 - The x-coordinate of the second point. + * @param y2 - The y-coordinate of the second point. + * @param radius - The radius of the arc. + * @returns The instance of the current object for chaining. + */ + arcTo(x1, y1, x2, y2, radius) { + this._ensurePoly(); + const points = this._currentPoly.points; + buildArcTo(points, x1, y1, x2, y2, radius); + return this; + } + /** + * Adds an SVG-style arc to the path, allowing for elliptical arcs based on the SVG spec. + * @param rx - The x-radius of the ellipse. + * @param ry - The y-radius of the ellipse. + * @param xAxisRotation - The rotation of the ellipse's x-axis relative + * to the x-axis of the coordinate system, in degrees. + * @param largeArcFlag - Determines if the arc should be greater than or less than 180 degrees. + * @param sweepFlag - Determines if the arc should be swept in a positive angle direction. + * @param x - The x-coordinate of the arc's end point. + * @param y - The y-coordinate of the arc's end point. + * @returns The instance of the current object for chaining. + */ + arcToSvg(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { + const points = this._currentPoly.points; + buildArcToSvg( + points, + this._currentPoly.lastX, + this._currentPoly.lastY, + x, + y, + rx, + ry, + xAxisRotation, + largeArcFlag, + sweepFlag + ); + return this; + } + /** + * Adds a cubic Bezier curve to the path. + * It requires three points: the first two are control points and the third one is the end point. + * The starting point is the last point in the current path. + * @param cp1x - The x-coordinate of the first control point. + * @param cp1y - The y-coordinate of the first control point. + * @param cp2x - The x-coordinate of the second control point. + * @param cp2y - The y-coordinate of the second control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothness - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, smoothness) { + this._ensurePoly(); + const currentPoly = this._currentPoly; + buildAdaptiveBezier( + this._currentPoly.points, + currentPoly.lastX, + currentPoly.lastY, + cp1x, + cp1y, + cp2x, + cp2y, + x, + y, + smoothness + ); + return this; + } + /** + * Adds a quadratic curve to the path. It requires two points: the control point and the end point. + * The starting point is the last point in the current path. + * @param cp1x - The x-coordinate of the control point. + * @param cp1y - The y-coordinate of the control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothing - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + quadraticCurveTo(cp1x, cp1y, x, y, smoothing) { + this._ensurePoly(); + const currentPoly = this._currentPoly; + buildAdaptiveQuadratic( + this._currentPoly.points, + currentPoly.lastX, + currentPoly.lastY, + cp1x, + cp1y, + x, + y, + smoothing + ); + return this; + } + /** + * Closes the current path by drawing a straight line back to the start. + * If the shape is already closed or there are no points in the path, this method does nothing. + * @returns The instance of the current object for chaining. + */ + closePath() { + this.endPoly(true); + return this; + } + /** + * Adds another path to the current path. This method allows for the combination of multiple paths into one. + * @param path - The `GraphicsPath` object representing the path to add. + * @param transform - An optional `Matrix` object to apply a transformation to the path before adding it. + * @returns The instance of the current object for chaining. + */ + addPath(path, transform) { + this.endPoly(); + if (transform && !transform.isIdentity()) { + path = path.clone(true); + path.transform(transform); + } + const shapePrimitives = this.shapePrimitives; + const start = shapePrimitives.length; + for (let i = 0; i < path.instructions.length; i++) { + const instruction = path.instructions[i]; + this[instruction.action](...instruction.data); + } + if (path.checkForHoles && shapePrimitives.length - start > 1) { + let mainShape = null; + for (let i = start; i < shapePrimitives.length; i++) { + const shapePrimitive = shapePrimitives[i]; + if (shapePrimitive.shape.type === "polygon") { + const polygon = shapePrimitive.shape; + const mainPolygon = mainShape == null ? void 0 : mainShape.shape; + if (mainPolygon && mainPolygon.containsPolygon(polygon)) { + mainShape.holes || (mainShape.holes = []); + mainShape.holes.push(shapePrimitive); + shapePrimitives.copyWithin(i, i + 1); + shapePrimitives.length--; + i--; + } else { + mainShape = shapePrimitive; + } + } + } + } + return this; + } + /** + * Finalizes the drawing of the current path. Optionally, it can close the path. + * @param closePath - A boolean indicating whether to close the path after finishing. False by default. + */ + finish(closePath = false) { + this.endPoly(closePath); + } + /** + * Draws a rectangle shape. This method adds a new rectangle path to the current drawing. + * @param x - The x-coordinate of the top-left corner of the rectangle. + * @param y - The y-coordinate of the top-left corner of the rectangle. + * @param w - The width of the rectangle. + * @param h - The height of the rectangle. + * @param transform - An optional `Matrix` object to apply a transformation to the rectangle. + * @returns The instance of the current object for chaining. + */ + rect(x, y, w, h, transform) { + this.drawShape(new Rectangle(x, y, w, h), transform); + return this; + } + /** + * Draws a circle shape. This method adds a new circle path to the current drawing. + * @param x - The x-coordinate of the center of the circle. + * @param y - The y-coordinate of the center of the circle. + * @param radius - The radius of the circle. + * @param transform - An optional `Matrix` object to apply a transformation to the circle. + * @returns The instance of the current object for chaining. + */ + circle(x, y, radius, transform) { + this.drawShape(new Circle(x, y, radius), transform); + return this; + } + /** + * Draws a polygon shape. This method allows for the creation of complex polygons by specifying a sequence of points. + * @param points - An array of numbers, or or an array of PointData objects eg [{x,y}, {x,y}, {x,y}] + * representing the x and y coordinates of the polygon's vertices, in sequence. + * @param close - A boolean indicating whether to close the polygon path. True by default. + * @param transform - An optional `Matrix` object to apply a transformation to the polygon. + * @returns The instance of the current object for chaining. + */ + poly(points, close, transform) { + const polygon = new Polygon(points); + polygon.closePath = close; + this.drawShape(polygon, transform); + return this; + } + /** + * Draws a regular polygon with a specified number of sides. All sides and angles are equal. + * @param x - The x-coordinate of the center of the polygon. + * @param y - The y-coordinate of the center of the polygon. + * @param radius - The radius of the circumscribed circle of the polygon. + * @param sides - The number of sides of the polygon. Must be 3 or more. + * @param rotation - The rotation angle of the polygon, in radians. Zero by default. + * @param transform - An optional `Matrix` object to apply a transformation to the polygon. + * @returns The instance of the current object for chaining. + */ + regularPoly(x, y, radius, sides, rotation = 0, transform) { + sides = Math.max(sides | 0, 3); + const startAngle = -1 * Math.PI / 2 + rotation; + const delta = Math.PI * 2 / sides; + const polygon = []; + for (let i = 0; i < sides; i++) { + const angle = startAngle - i * delta; + polygon.push( + x + radius * Math.cos(angle), + y + radius * Math.sin(angle) + ); + } + this.poly(polygon, true, transform); + return this; + } + /** + * Draws a polygon with rounded corners. + * Similar to `regularPoly` but with the ability to round the corners of the polygon. + * @param x - The x-coordinate of the center of the polygon. + * @param y - The y-coordinate of the center of the polygon. + * @param radius - The radius of the circumscribed circle of the polygon. + * @param sides - The number of sides of the polygon. Must be 3 or more. + * @param corner - The radius of the rounding of the corners. + * @param rotation - The rotation angle of the polygon, in radians. Zero by default. + * @param smoothness - Optional parameter to adjust the smoothness of the rounding. + * @returns The instance of the current object for chaining. + */ + roundPoly(x, y, radius, sides, corner, rotation = 0, smoothness) { + sides = Math.max(sides | 0, 3); + if (corner <= 0) { + return this.regularPoly(x, y, radius, sides, rotation); + } + const sideLength = radius * Math.sin(Math.PI / sides) - 1e-3; + corner = Math.min(corner, sideLength); + const startAngle = -1 * Math.PI / 2 + rotation; + const delta = Math.PI * 2 / sides; + const internalAngle = (sides - 2) * Math.PI / sides / 2; + for (let i = 0; i < sides; i++) { + const angle = i * delta + startAngle; + const x0 = x + radius * Math.cos(angle); + const y0 = y + radius * Math.sin(angle); + const a1 = angle + Math.PI + internalAngle; + const a2 = angle - Math.PI - internalAngle; + const x1 = x0 + corner * Math.cos(a1); + const y1 = y0 + corner * Math.sin(a1); + const x3 = x0 + corner * Math.cos(a2); + const y3 = y0 + corner * Math.sin(a2); + if (i === 0) { + this.moveTo(x1, y1); + } else { + this.lineTo(x1, y1); + } + this.quadraticCurveTo(x0, y0, x3, y3, smoothness); + } + return this.closePath(); + } + /** + * Draws a shape with rounded corners. This function supports custom radius for each corner of the shape. + * Optionally, corners can be rounded using a quadratic curve instead of an arc, providing a different aesthetic. + * @param points - An array of `RoundedPoint` representing the corners of the shape to draw. + * A minimum of 3 points is required. + * @param radius - The default radius for the corners. + * This radius is applied to all corners unless overridden in `points`. + * @param useQuadratic - If set to true, rounded corners are drawn using a quadraticCurve + * method instead of an arc method. Defaults to false. + * @param smoothness - Specifies the smoothness of the curve when `useQuadratic` is true. + * Higher values make the curve smoother. + * @returns The instance of the current object for chaining. + */ + roundShape(points, radius, useQuadratic = false, smoothness) { + if (points.length < 3) { + return this; + } + if (useQuadratic) { + roundedShapeQuadraticCurve(this, points, radius, smoothness); + } else { + roundedShapeArc(this, points, radius); + } + return this.closePath(); + } + /** + * Draw Rectangle with fillet corners. This is much like rounded rectangle + * however it support negative numbers as well for the corner radius. + * @param x - Upper left corner of rect + * @param y - Upper right corner of rect + * @param width - Width of rect + * @param height - Height of rect + * @param fillet - accept negative or positive values + */ + filletRect(x, y, width, height, fillet) { + if (fillet === 0) { + return this.rect(x, y, width, height); + } + const maxFillet = Math.min(width, height) / 2; + const inset = Math.min(maxFillet, Math.max(-maxFillet, fillet)); + const right = x + width; + const bottom = y + height; + const dir = inset < 0 ? -inset : 0; + const size = Math.abs(inset); + return this.moveTo(x, y + size).arcTo(x + dir, y + dir, x + size, y, size).lineTo(right - size, y).arcTo(right - dir, y + dir, right, y + size, size).lineTo(right, bottom - size).arcTo(right - dir, bottom - dir, x + width - size, bottom, size).lineTo(x + size, bottom).arcTo(x + dir, bottom - dir, x, bottom - size, size).closePath(); + } + /** + * Draw Rectangle with chamfer corners. These are angled corners. + * @param x - Upper left corner of rect + * @param y - Upper right corner of rect + * @param width - Width of rect + * @param height - Height of rect + * @param chamfer - non-zero real number, size of corner cutout + * @param transform + */ + chamferRect(x, y, width, height, chamfer, transform) { + if (chamfer <= 0) { + return this.rect(x, y, width, height); + } + const inset = Math.min(chamfer, Math.min(width, height) / 2); + const right = x + width; + const bottom = y + height; + const points = [ + x + inset, + y, + right - inset, + y, + right, + y + inset, + right, + bottom - inset, + right - inset, + bottom, + x + inset, + bottom, + x, + bottom - inset, + x, + y + inset + ]; + for (let i = points.length - 1; i >= 2; i -= 2) { + if (points[i] === points[i - 2] && points[i - 1] === points[i - 3]) { + points.splice(i - 1, 2); + } + } + return this.poly(points, true, transform); + } + /** + * Draws an ellipse at the specified location and with the given x and y radii. + * An optional transformation can be applied, allowing for rotation, scaling, and translation. + * @param x - The x-coordinate of the center of the ellipse. + * @param y - The y-coordinate of the center of the ellipse. + * @param radiusX - The horizontal radius of the ellipse. + * @param radiusY - The vertical radius of the ellipse. + * @param transform - An optional `Matrix` object to apply a transformation to the ellipse. This can include rotations. + * @returns The instance of the current object for chaining. + */ + ellipse(x, y, radiusX, radiusY, transform) { + this.drawShape(new Ellipse(x, y, radiusX, radiusY), transform); + return this; + } + /** + * Draws a rectangle with rounded corners. + * The corner radius can be specified to determine how rounded the corners should be. + * An optional transformation can be applied, which allows for rotation, scaling, and translation of the rectangle. + * @param x - The x-coordinate of the top-left corner of the rectangle. + * @param y - The y-coordinate of the top-left corner of the rectangle. + * @param w - The width of the rectangle. + * @param h - The height of the rectangle. + * @param radius - The radius of the rectangle's corners. If not specified, corners will be sharp. + * @param transform - An optional `Matrix` object to apply a transformation to the rectangle. + * @returns The instance of the current object for chaining. + */ + roundRect(x, y, w, h, radius, transform) { + this.drawShape(new RoundedRectangle(x, y, w, h, radius), transform); + return this; + } + /** + * Draws a given shape on the canvas. + * This is a generic method that can draw any type of shape specified by the `ShapePrimitive` parameter. + * An optional transformation matrix can be applied to the shape, allowing for complex transformations. + * @param shape - The shape to draw, defined as a `ShapePrimitive` object. + * @param matrix - An optional `Matrix` for transforming the shape. This can include rotations, + * scaling, and translations. + * @returns The instance of the current object for chaining. + */ + drawShape(shape, matrix) { + this.endPoly(); + this.shapePrimitives.push({ shape, transform: matrix }); + return this; + } + /** + * Starts a new polygon path from the specified starting point. + * This method initializes a new polygon or ends the current one if it exists. + * @param x - The x-coordinate of the starting point of the new polygon. + * @param y - The y-coordinate of the starting point of the new polygon. + * @returns The instance of the current object for chaining. + */ + startPoly(x, y) { + let currentPoly = this._currentPoly; + if (currentPoly) { + this.endPoly(); + } + currentPoly = new Polygon(); + currentPoly.points.push(x, y); + this._currentPoly = currentPoly; + return this; + } + /** + * Ends the current polygon path. If `closePath` is set to true, + * the path is closed by connecting the last point to the first one. + * This method finalizes the current polygon and prepares it for drawing or adding to the shape primitives. + * @param closePath - A boolean indicating whether to close the polygon by connecting the last point + * back to the starting point. False by default. + * @returns The instance of the current object for chaining. + */ + endPoly(closePath = false) { + const shape = this._currentPoly; + if (shape && shape.points.length > 2) { + shape.closePath = closePath; + this.shapePrimitives.push({ shape }); + } + this._currentPoly = null; + return this; + } + _ensurePoly(start = true) { + if (this._currentPoly) return; + this._currentPoly = new Polygon(); + if (start) { + const lastShape = this.shapePrimitives[this.shapePrimitives.length - 1]; + if (lastShape) { + let lx = lastShape.shape.x; + let ly = lastShape.shape.y; + if (lastShape.transform && !lastShape.transform.isIdentity()) { + const t = lastShape.transform; + const tempX = lx; + lx = t.a * lx + t.c * ly + t.tx; + ly = t.b * tempX + t.d * ly + t.ty; + } + this._currentPoly.points.push(lx, ly); + } else { + this._currentPoly.points.push(0, 0); + } + } + } + /** Builds the path. */ + buildPath() { + const path = this._graphicsPath2D; + this.shapePrimitives.length = 0; + this._currentPoly = null; + for (let i = 0; i < path.instructions.length; i++) { + const instruction = path.instructions[i]; + this[instruction.action](...instruction.data); + } + this.finish(); + } + /** Gets the bounds of the path. */ + get bounds() { + const bounds = this._bounds; + bounds.clear(); + const shapePrimitives = this.shapePrimitives; + for (let i = 0; i < shapePrimitives.length; i++) { + const shapePrimitive = shapePrimitives[i]; + const boundsRect = shapePrimitive.shape.getBounds(tempRectangle$1); + if (shapePrimitive.transform) { + bounds.addRect(boundsRect, shapePrimitive.transform); + } else { + bounds.addRect(boundsRect); + } + } + return bounds; + } + } + + "use strict"; + class GraphicsPath { + /** + * Creates a `GraphicsPath` instance optionally from an SVG path string or an array of `PathInstruction`. + * @param instructions - An SVG path string or an array of `PathInstruction` objects. + * @param signed + */ + constructor(instructions, signed = false) { + this.instructions = []; + /** unique id for this graphics path */ + this.uid = uid$1("graphicsPath"); + this._dirty = true; + var _a; + this.checkForHoles = signed; + if (typeof instructions === "string") { + parseSVGPath(instructions, this); + } else { + this.instructions = (_a = instructions == null ? void 0 : instructions.slice()) != null ? _a : []; + } + } + /** + * Provides access to the internal shape path, ensuring it is up-to-date with the current instructions. + * @returns The `ShapePath` instance associated with this `GraphicsPath`. + */ + get shapePath() { + if (!this._shapePath) { + this._shapePath = new ShapePath(this); + } + if (this._dirty) { + this._dirty = false; + this._shapePath.buildPath(); + } + return this._shapePath; + } + /** + * Adds another `GraphicsPath` to this path, optionally applying a transformation. + * @param path - The `GraphicsPath` to add. + * @param transform - An optional transformation to apply to the added path. + * @returns The instance of the current object for chaining. + */ + addPath(path, transform) { + path = path.clone(); + this.instructions.push({ action: "addPath", data: [path, transform] }); + this._dirty = true; + return this; + } + arc(...args) { + this.instructions.push({ action: "arc", data: args }); + this._dirty = true; + return this; + } + arcTo(...args) { + this.instructions.push({ action: "arcTo", data: args }); + this._dirty = true; + return this; + } + arcToSvg(...args) { + this.instructions.push({ action: "arcToSvg", data: args }); + this._dirty = true; + return this; + } + bezierCurveTo(...args) { + this.instructions.push({ action: "bezierCurveTo", data: args }); + this._dirty = true; + return this; + } + /** + * Adds a cubic Bezier curve to the path. + * It requires two points: the second control point and the end point. The first control point is assumed to be + * The starting point is the last point in the current path. + * @param cp2x - The x-coordinate of the second control point. + * @param cp2y - The y-coordinate of the second control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothness - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + bezierCurveToShort(cp2x, cp2y, x, y, smoothness) { + const last = this.instructions[this.instructions.length - 1]; + const lastPoint = this.getLastPoint(Point.shared); + let cp1x = 0; + let cp1y = 0; + if (!last || last.action !== "bezierCurveTo") { + cp1x = lastPoint.x; + cp1y = lastPoint.y; + } else { + cp1x = last.data[2]; + cp1y = last.data[3]; + const currentX = lastPoint.x; + const currentY = lastPoint.y; + cp1x = currentX + (currentX - cp1x); + cp1y = currentY + (currentY - cp1y); + } + this.instructions.push({ action: "bezierCurveTo", data: [cp1x, cp1y, cp2x, cp2y, x, y, smoothness] }); + this._dirty = true; + return this; + } + /** + * Closes the current path by drawing a straight line back to the start. + * If the shape is already closed or there are no points in the path, this method does nothing. + * @returns The instance of the current object for chaining. + */ + closePath() { + this.instructions.push({ action: "closePath", data: [] }); + this._dirty = true; + return this; + } + ellipse(...args) { + this.instructions.push({ action: "ellipse", data: args }); + this._dirty = true; + return this; + } + lineTo(...args) { + this.instructions.push({ action: "lineTo", data: args }); + this._dirty = true; + return this; + } + moveTo(...args) { + this.instructions.push({ action: "moveTo", data: args }); + return this; + } + quadraticCurveTo(...args) { + this.instructions.push({ action: "quadraticCurveTo", data: args }); + this._dirty = true; + return this; + } + /** + * Adds a quadratic curve to the path. It uses the previous point as the control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothness - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + quadraticCurveToShort(x, y, smoothness) { + const last = this.instructions[this.instructions.length - 1]; + const lastPoint = this.getLastPoint(Point.shared); + let cpx1 = 0; + let cpy1 = 0; + if (!last || last.action !== "quadraticCurveTo") { + cpx1 = lastPoint.x; + cpy1 = lastPoint.y; + } else { + cpx1 = last.data[0]; + cpy1 = last.data[1]; + const currentX = lastPoint.x; + const currentY = lastPoint.y; + cpx1 = currentX + (currentX - cpx1); + cpy1 = currentY + (currentY - cpy1); + } + this.instructions.push({ action: "quadraticCurveTo", data: [cpx1, cpy1, x, y, smoothness] }); + this._dirty = true; + return this; + } + /** + * Draws a rectangle shape. This method adds a new rectangle path to the current drawing. + * @param x - The x-coordinate of the top-left corner of the rectangle. + * @param y - The y-coordinate of the top-left corner of the rectangle. + * @param w - The width of the rectangle. + * @param h - The height of the rectangle. + * @param transform - An optional `Matrix` object to apply a transformation to the rectangle. + * @returns The instance of the current object for chaining. + */ + rect(x, y, w, h, transform) { + this.instructions.push({ action: "rect", data: [x, y, w, h, transform] }); + this._dirty = true; + return this; + } + /** + * Draws a circle shape. This method adds a new circle path to the current drawing. + * @param x - The x-coordinate of the center of the circle. + * @param y - The y-coordinate of the center of the circle. + * @param radius - The radius of the circle. + * @param transform - An optional `Matrix` object to apply a transformation to the circle. + * @returns The instance of the current object for chaining. + */ + circle(x, y, radius, transform) { + this.instructions.push({ action: "circle", data: [x, y, radius, transform] }); + this._dirty = true; + return this; + } + roundRect(...args) { + this.instructions.push({ action: "roundRect", data: args }); + this._dirty = true; + return this; + } + poly(...args) { + this.instructions.push({ action: "poly", data: args }); + this._dirty = true; + return this; + } + regularPoly(...args) { + this.instructions.push({ action: "regularPoly", data: args }); + this._dirty = true; + return this; + } + roundPoly(...args) { + this.instructions.push({ action: "roundPoly", data: args }); + this._dirty = true; + return this; + } + roundShape(...args) { + this.instructions.push({ action: "roundShape", data: args }); + this._dirty = true; + return this; + } + filletRect(...args) { + this.instructions.push({ action: "filletRect", data: args }); + this._dirty = true; + return this; + } + chamferRect(...args) { + this.instructions.push({ action: "chamferRect", data: args }); + this._dirty = true; + return this; + } + /** + * Draws a star shape centered at a specified location. This method allows for the creation + * of stars with a variable number of points, outer radius, optional inner radius, and rotation. + * The star is drawn as a closed polygon with alternating outer and inner vertices to create the star's points. + * An optional transformation can be applied to scale, rotate, or translate the star as needed. + * @param x - The x-coordinate of the center of the star. + * @param y - The y-coordinate of the center of the star. + * @param points - The number of points of the star. + * @param radius - The outer radius of the star (distance from the center to the outer points). + * @param innerRadius - Optional. The inner radius of the star + * (distance from the center to the inner points between the outer points). + * If not provided, defaults to half of the `radius`. + * @param rotation - Optional. The rotation of the star in radians, where 0 is aligned with the y-axis. + * Defaults to 0, meaning one point is directly upward. + * @param transform - An optional `Matrix` object to apply a transformation to the star. + * This can include rotations, scaling, and translations. + * @returns The instance of the current object for chaining further drawing commands. + */ + // eslint-disable-next-line max-len + star(x, y, points, radius, innerRadius, rotation, transform) { + innerRadius || (innerRadius = radius / 2); + const startAngle = -1 * Math.PI / 2 + rotation; + const len = points * 2; + const delta = Math.PI * 2 / len; + const polygon = []; + for (let i = 0; i < len; i++) { + const r = i % 2 ? innerRadius : radius; + const angle = i * delta + startAngle; + polygon.push( + x + r * Math.cos(angle), + y + r * Math.sin(angle) + ); + } + this.poly(polygon, true, transform); + return this; + } + /** + * Creates a copy of the current `GraphicsPath` instance. This method supports both shallow and deep cloning. + * A shallow clone copies the reference of the instructions array, while a deep clone creates a new array and + * copies each instruction individually, ensuring that modifications to the instructions of the cloned `GraphicsPath` + * do not affect the original `GraphicsPath` and vice versa. + * @param deep - A boolean flag indicating whether the clone should be deep. + * @returns A new `GraphicsPath` instance that is a clone of the current instance. + */ + clone(deep = false) { + const newGraphicsPath2D = new GraphicsPath(); + newGraphicsPath2D.checkForHoles = this.checkForHoles; + if (!deep) { + newGraphicsPath2D.instructions = this.instructions.slice(); + } else { + for (let i = 0; i < this.instructions.length; i++) { + const instruction = this.instructions[i]; + newGraphicsPath2D.instructions.push({ action: instruction.action, data: instruction.data.slice() }); + } + } + return newGraphicsPath2D; + } + clear() { + this.instructions.length = 0; + this._dirty = true; + return this; + } + /** + * Applies a transformation matrix to all drawing instructions within the `GraphicsPath`. + * This method enables the modification of the path's geometry according to the provided + * transformation matrix, which can include translations, rotations, scaling, and skewing. + * + * Each drawing instruction in the path is updated to reflect the transformation, + * ensuring the visual representation of the path is consistent with the applied matrix. + * + * Note: The transformation is applied directly to the coordinates and control points of the drawing instructions, + * not to the path as a whole. This means the transformation's effects are baked into the individual instructions, + * allowing for fine-grained control over the path's appearance. + * @param matrix - A `Matrix` object representing the transformation to apply. + * @returns The instance of the current object for chaining further operations. + */ + transform(matrix) { + if (matrix.isIdentity()) return this; + const a = matrix.a; + const b = matrix.b; + const c = matrix.c; + const d = matrix.d; + const tx = matrix.tx; + const ty = matrix.ty; + let x = 0; + let y = 0; + let cpx1 = 0; + let cpy1 = 0; + let cpx2 = 0; + let cpy2 = 0; + let rx = 0; + let ry = 0; + for (let i = 0; i < this.instructions.length; i++) { + const instruction = this.instructions[i]; + const data = instruction.data; + switch (instruction.action) { + case "moveTo": + case "lineTo": + x = data[0]; + y = data[1]; + data[0] = a * x + c * y + tx; + data[1] = b * x + d * y + ty; + break; + case "bezierCurveTo": + cpx1 = data[0]; + cpy1 = data[1]; + cpx2 = data[2]; + cpy2 = data[3]; + x = data[4]; + y = data[5]; + data[0] = a * cpx1 + c * cpy1 + tx; + data[1] = b * cpx1 + d * cpy1 + ty; + data[2] = a * cpx2 + c * cpy2 + tx; + data[3] = b * cpx2 + d * cpy2 + ty; + data[4] = a * x + c * y + tx; + data[5] = b * x + d * y + ty; + break; + case "quadraticCurveTo": + cpx1 = data[0]; + cpy1 = data[1]; + x = data[2]; + y = data[3]; + data[0] = a * cpx1 + c * cpy1 + tx; + data[1] = b * cpx1 + d * cpy1 + ty; + data[2] = a * x + c * y + tx; + data[3] = b * x + d * y + ty; + break; + case "arcToSvg": + x = data[5]; + y = data[6]; + rx = data[0]; + ry = data[1]; + data[0] = a * rx + c * ry; + data[1] = b * rx + d * ry; + data[5] = a * x + c * y + tx; + data[6] = b * x + d * y + ty; + break; + case "circle": + data[4] = adjustTransform(data[3], matrix); + break; + case "rect": + data[4] = adjustTransform(data[4], matrix); + break; + case "ellipse": + data[8] = adjustTransform(data[8], matrix); + break; + case "roundRect": + data[5] = adjustTransform(data[5], matrix); + break; + case "addPath": + data[0].transform(matrix); + break; + case "poly": + data[2] = adjustTransform(data[2], matrix); + break; + default: + warn("unknown transform action", instruction.action); + break; + } + } + this._dirty = true; + return this; + } + get bounds() { + return this.shapePath.bounds; + } + /** + * Retrieves the last point from the current drawing instructions in the `GraphicsPath`. + * This method is useful for operations that depend on the path's current endpoint, + * such as connecting subsequent shapes or paths. It supports various drawing instructions, + * ensuring the last point's position is accurately determined regardless of the path's complexity. + * + * If the last instruction is a `closePath`, the method iterates backward through the instructions + * until it finds an actionable instruction that defines a point (e.g., `moveTo`, `lineTo`, + * `quadraticCurveTo`, etc.). For compound paths added via `addPath`, it recursively retrieves + * the last point from the nested path. + * @param out - A `Point` object where the last point's coordinates will be stored. + * This object is modified directly to contain the result. + * @returns The `Point` object containing the last point's coordinates. + */ + getLastPoint(out) { + let index = this.instructions.length - 1; + let lastInstruction = this.instructions[index]; + if (!lastInstruction) { + out.x = 0; + out.y = 0; + return out; + } + while (lastInstruction.action === "closePath") { + index--; + if (index < 0) { + out.x = 0; + out.y = 0; + return out; + } + lastInstruction = this.instructions[index]; + } + switch (lastInstruction.action) { + case "moveTo": + case "lineTo": + out.x = lastInstruction.data[0]; + out.y = lastInstruction.data[1]; + break; + case "quadraticCurveTo": + out.x = lastInstruction.data[2]; + out.y = lastInstruction.data[3]; + break; + case "bezierCurveTo": + out.x = lastInstruction.data[4]; + out.y = lastInstruction.data[5]; + break; + case "arc": + case "arcToSvg": + out.x = lastInstruction.data[5]; + out.y = lastInstruction.data[6]; + break; + case "addPath": + lastInstruction.data[0].getLastPoint(out); + break; + } + return out; + } + } + function adjustTransform(currentMatrix, transform) { + if (currentMatrix) { + return currentMatrix.prepend(transform); + } + return transform.clone(); + } + + "use strict"; + function parseSVGFloatAttribute(svg, id, defaultValue) { + const value = svg.getAttribute(id); + return value ? Number(value) : defaultValue; + } + + "use strict"; + function parseSVGDefinitions(svg, session) { + const definitions = svg.querySelectorAll("defs"); + for (let i = 0; i < definitions.length; i++) { + const definition = definitions[i]; + for (let j = 0; j < definition.children.length; j++) { + const child = definition.children[j]; + switch (child.nodeName.toLowerCase()) { + case "lineargradient": + session.defs[child.id] = parseLinearGradient(child); + break; + case "radialgradient": + session.defs[child.id] = parseRadialGradient(child); + break; + default: + break; + } + } + } + } + function parseLinearGradient(child) { + const x0 = parseSVGFloatAttribute(child, "x1", 0); + const y0 = parseSVGFloatAttribute(child, "y1", 0); + const x1 = parseSVGFloatAttribute(child, "x2", 1); + const y1 = parseSVGFloatAttribute(child, "y2", 0); + const gradientUnit = child.getAttribute("gradientUnits") || "objectBoundingBox"; + const gradient = new FillGradient( + x0, + y0, + x1, + y1, + gradientUnit === "objectBoundingBox" ? "local" : "global" + ); + for (let k = 0; k < child.children.length; k++) { + const stop = child.children[k]; + const offset = parseSVGFloatAttribute(stop, "offset", 0); + const color = Color.shared.setValue(stop.getAttribute("stop-color")).toNumber(); + gradient.addColorStop(offset, color); + } + return gradient; + } + function parseRadialGradient(_child) { + warn("[SVG Parser] Radial gradients are not yet supported"); + return new FillGradient(0, 0, 1, 0); + } + + "use strict"; + function extractSvgUrlId(url) { + const match = url.match(/url\s*\(\s*['"]?\s*#([^'"\s)]+)\s*['"]?\s*\)/i); + return match ? match[1] : ""; + } + + "use strict"; + const styleAttributes = { + // Fill properties + fill: { type: "paint", default: 0 }, + // Fill color/gradient + "fill-opacity": { type: "number", default: 1 }, + // Fill transparency + // Stroke properties + stroke: { type: "paint", default: 0 }, + // Stroke color/gradient + "stroke-width": { type: "number", default: 1 }, + // Width of stroke + "stroke-opacity": { type: "number", default: 1 }, + // Stroke transparency + "stroke-linecap": { type: "string", default: "butt" }, + // End cap style: butt, round, square + "stroke-linejoin": { type: "string", default: "miter" }, + // Join style: miter, round, bevel + "stroke-miterlimit": { type: "number", default: 10 }, + // Limit on miter join sharpness + "stroke-dasharray": { type: "string", default: "none" }, + // Dash pattern + "stroke-dashoffset": { type: "number", default: 0 }, + // Offset for dash pattern + // Global properties + opacity: { type: "number", default: 1 } + // Overall opacity + }; + function parseSVGStyle(svg, session) { + const style = svg.getAttribute("style"); + const strokeStyle = {}; + const fillStyle = {}; + const result = { + strokeStyle, + fillStyle, + useFill: false, + useStroke: false + }; + for (const key in styleAttributes) { + const attribute = svg.getAttribute(key); + if (attribute) { + parseAttribute(session, result, key, attribute.trim()); + } + } + if (style) { + const styleParts = style.split(";"); + for (let i = 0; i < styleParts.length; i++) { + const stylePart = styleParts[i].trim(); + const [key, value] = stylePart.split(":"); + if (styleAttributes[key]) { + parseAttribute(session, result, key, value.trim()); + } + } + } + return { + strokeStyle: result.useStroke ? strokeStyle : null, + fillStyle: result.useFill ? fillStyle : null, + useFill: result.useFill, + useStroke: result.useStroke + }; + } + function parseAttribute(session, result, id, value) { + switch (id) { + case "stroke": + if (value !== "none") { + if (value.startsWith("url(")) { + const id2 = extractSvgUrlId(value); + result.strokeStyle.fill = session.defs[id2]; + } else { + result.strokeStyle.color = Color.shared.setValue(value).toNumber(); + } + result.useStroke = true; + } + break; + case "stroke-width": + result.strokeStyle.width = Number(value); + break; + case "fill": + if (value !== "none") { + if (value.startsWith("url(")) { + const id2 = extractSvgUrlId(value); + result.fillStyle.fill = session.defs[id2]; + } else { + result.fillStyle.color = Color.shared.setValue(value).toNumber(); + } + result.useFill = true; + } + break; + case "fill-opacity": + result.fillStyle.alpha = Number(value); + break; + case "stroke-opacity": + result.strokeStyle.alpha = Number(value); + break; + case "opacity": + result.fillStyle.alpha = Number(value); + result.strokeStyle.alpha = Number(value); + break; + } + } + + "use strict"; + function checkForNestedPattern(subpathsWithArea) { + if (subpathsWithArea.length <= 2) { + return true; + } + const areas = subpathsWithArea.map((s) => s.area).sort((a, b) => b - a); + const [largestArea, secondArea] = areas; + const smallestArea = areas[areas.length - 1]; + const largestToSecondRatio = largestArea / secondArea; + const secondToSmallestRatio = secondArea / smallestArea; + if (largestToSecondRatio > 3 && secondToSmallestRatio < 2) { + return false; + } + return true; + } + function getFillInstructionData(context, index = 0) { + const instruction = context.instructions[index]; + if (!instruction || instruction.action !== "fill") { + throw new Error(`Expected fill instruction at index ${index}, got ${(instruction == null ? void 0 : instruction.action) || "undefined"}`); + } + return instruction.data; + } + + "use strict"; + function extractSubpaths(pathData) { + const parts = pathData.split(/(?=[Mm])/); + const subpaths = parts.filter((part) => part.trim().length > 0); + return subpaths; + } + function calculatePathArea(pathData) { + const coords = pathData.match(/[-+]?[0-9]*\.?[0-9]+/g); + if (!coords || coords.length < 4) return 0; + const numbers = coords.map(Number); + const xs = []; + const ys = []; + for (let i = 0; i < numbers.length; i += 2) { + if (i + 1 < numbers.length) { + xs.push(numbers[i]); + ys.push(numbers[i + 1]); + } + } + if (xs.length === 0 || ys.length === 0) return 0; + const minX = Math.min(...xs); + const maxX = Math.max(...xs); + const minY = Math.min(...ys); + const maxY = Math.max(...ys); + const area = (maxX - minX) * (maxY - minY); + return area; + } + function appendSVGPath(pathData, graphicsPath) { + const tempPath = new GraphicsPath(pathData, false); + for (const instruction of tempPath.instructions) { + graphicsPath.instructions.push(instruction); + } + } + + "use strict"; + var __defProp$Z = Object.defineProperty; + var __getOwnPropSymbols$$ = Object.getOwnPropertySymbols; + var __hasOwnProp$$ = Object.prototype.hasOwnProperty; + var __propIsEnum$$ = Object.prototype.propertyIsEnumerable; + var __defNormalProp$Z = (obj, key, value) => key in obj ? __defProp$Z(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$Z = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$$.call(b, prop)) + __defNormalProp$Z(a, prop, b[prop]); + if (__getOwnPropSymbols$$) + for (var prop of __getOwnPropSymbols$$(b)) { + if (__propIsEnum$$.call(b, prop)) + __defNormalProp$Z(a, prop, b[prop]); + } + return a; + }; + function SVGParser(svg, graphicsContext) { + if (typeof svg === "string") { + const div = document.createElement("div"); + div.innerHTML = svg.trim(); + svg = div.querySelector("svg"); + } + const session = { + context: graphicsContext, + defs: {}, + path: new GraphicsPath() + }; + parseSVGDefinitions(svg, session); + const children = svg.children; + const { fillStyle, strokeStyle } = parseSVGStyle(svg, session); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.nodeName.toLowerCase() === "defs") continue; + renderChildren(child, session, fillStyle, strokeStyle); + } + return graphicsContext; + } + function renderChildren(svg, session, fillStyle, strokeStyle) { + const children = svg.children; + const { fillStyle: f1, strokeStyle: s1 } = parseSVGStyle(svg, session); + if (f1 && fillStyle) { + fillStyle = __spreadValues$Z(__spreadValues$Z({}, fillStyle), f1); + } else if (f1) { + fillStyle = f1; + } + if (s1 && strokeStyle) { + strokeStyle = __spreadValues$Z(__spreadValues$Z({}, strokeStyle), s1); + } else if (s1) { + strokeStyle = s1; + } + const noStyle = !fillStyle && !strokeStyle; + if (noStyle) { + fillStyle = { color: 0 }; + } + let x; + let y; + let x1; + let y1; + let x2; + let y2; + let cx; + let cy; + let r; + let rx; + let ry; + let points; + let pointsString; + let d; + let graphicsPath; + let width; + let height; + switch (svg.nodeName.toLowerCase()) { + case "path": { + d = svg.getAttribute("d"); + const fillRule = svg.getAttribute("fill-rule"); + const subpaths = extractSubpaths(d); + const hasExplicitEvenodd = fillRule === "evenodd"; + const hasMultipleSubpaths = subpaths.length > 1; + const shouldProcessHoles = hasExplicitEvenodd && hasMultipleSubpaths; + if (shouldProcessHoles) { + const subpathsWithArea = subpaths.map((subpath) => ({ + path: subpath, + area: calculatePathArea(subpath) + })); + subpathsWithArea.sort((a, b) => b.area - a.area); + const useMultipleHolesApproach = subpaths.length > 3 || !checkForNestedPattern(subpathsWithArea); + if (useMultipleHolesApproach) { + for (let i = 0; i < subpathsWithArea.length; i++) { + const subpath = subpathsWithArea[i]; + const isMainShape = i === 0; + session.context.beginPath(); + const newPath = new GraphicsPath(void 0, true); + appendSVGPath(subpath.path, newPath); + session.context.path(newPath); + if (isMainShape) { + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + } else { + session.context.cut(); + } + } + } else { + for (let i = 0; i < subpathsWithArea.length; i++) { + const subpath = subpathsWithArea[i]; + const isHole = i % 2 === 1; + session.context.beginPath(); + const newPath = new GraphicsPath(void 0, true); + appendSVGPath(subpath.path, newPath); + session.context.path(newPath); + if (isHole) { + session.context.cut(); + } else { + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + } + } + } + } else { + const useEvenoddForGraphicsPath = fillRule ? fillRule === "evenodd" : true; + graphicsPath = new GraphicsPath(d, useEvenoddForGraphicsPath); + session.context.path(graphicsPath); + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + } + break; + } + case "circle": + cx = parseSVGFloatAttribute(svg, "cx", 0); + cy = parseSVGFloatAttribute(svg, "cy", 0); + r = parseSVGFloatAttribute(svg, "r", 0); + session.context.ellipse(cx, cy, r, r); + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + case "rect": + x = parseSVGFloatAttribute(svg, "x", 0); + y = parseSVGFloatAttribute(svg, "y", 0); + width = parseSVGFloatAttribute(svg, "width", 0); + height = parseSVGFloatAttribute(svg, "height", 0); + rx = parseSVGFloatAttribute(svg, "rx", 0); + ry = parseSVGFloatAttribute(svg, "ry", 0); + if (rx || ry) { + session.context.roundRect(x, y, width, height, rx || ry); + } else { + session.context.rect(x, y, width, height); + } + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + case "ellipse": + cx = parseSVGFloatAttribute(svg, "cx", 0); + cy = parseSVGFloatAttribute(svg, "cy", 0); + rx = parseSVGFloatAttribute(svg, "rx", 0); + ry = parseSVGFloatAttribute(svg, "ry", 0); + session.context.beginPath(); + session.context.ellipse(cx, cy, rx, ry); + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + case "line": + x1 = parseSVGFloatAttribute(svg, "x1", 0); + y1 = parseSVGFloatAttribute(svg, "y1", 0); + x2 = parseSVGFloatAttribute(svg, "x2", 0); + y2 = parseSVGFloatAttribute(svg, "y2", 0); + session.context.beginPath(); + session.context.moveTo(x1, y1); + session.context.lineTo(x2, y2); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + case "polygon": + pointsString = svg.getAttribute("points"); + points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10)); + session.context.poly(points, true); + if (fillStyle) session.context.fill(fillStyle); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + case "polyline": + pointsString = svg.getAttribute("points"); + points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10)); + session.context.poly(points, false); + if (strokeStyle) session.context.stroke(strokeStyle); + break; + // Group elements - just process children + case "g": + case "svg": + break; + default: { + warn(`[SVG parser] <${svg.nodeName}> elements unsupported`); + break; + } + } + if (noStyle) { + fillStyle = null; + } + for (let i = 0; i < children.length; i++) { + renderChildren(children[i], session, fillStyle, strokeStyle); + } + } + + "use strict"; + const repetitionMap = { + repeat: { + addressModeU: "repeat", + addressModeV: "repeat" + }, + "repeat-x": { + addressModeU: "repeat", + addressModeV: "clamp-to-edge" + }, + "repeat-y": { + addressModeU: "clamp-to-edge", + addressModeV: "repeat" + }, + "no-repeat": { + addressModeU: "clamp-to-edge", + addressModeV: "clamp-to-edge" + } + }; + class FillPattern { + constructor(texture, repetition) { + /** + * unique id for this fill pattern + * @internal + */ + this.uid = uid$1("fillPattern"); + /** + * Internal tick counter to track changes in the pattern. + * This is used to invalidate the pattern when the texture or transform changes. + * @internal + */ + this._tick = 0; + /** The transform matrix applied to the pattern */ + this.transform = new Matrix(); + this.texture = texture; + this.transform.scale( + 1 / texture.frame.width, + 1 / texture.frame.height + ); + if (repetition) { + texture.source.style.addressModeU = repetitionMap[repetition].addressModeU; + texture.source.style.addressModeV = repetitionMap[repetition].addressModeV; + } + } + /** + * Sets the transform for the pattern + * @param transform - The transform matrix to apply to the pattern. + * If not provided, the pattern will use the default transform. + */ + setTransform(transform) { + const texture = this.texture; + this.transform.copyFrom(transform); + this.transform.invert(); + this.transform.scale( + 1 / texture.frame.width, + 1 / texture.frame.height + ); + this._tick++; + } + /** Internal texture used to render the gradient */ + get texture() { + return this._texture; + } + set texture(value) { + if (this._texture === value) return; + this._texture = value; + this._tick++; + } + /** + * Returns a unique key for this instance. + * This key is used for caching. + * @returns {string} Unique key for the instance + */ + get styleKey() { + return `fill-pattern-${this.uid}-${this._tick}`; + } + /** Destroys the fill pattern, releasing resources. This will also destroy the internal texture. */ + destroy() { + this.texture.destroy(true); + this.texture = null; + } + } + + "use strict"; + var __defProp$Y = Object.defineProperty; + var __getOwnPropSymbols$_ = Object.getOwnPropertySymbols; + var __hasOwnProp$_ = Object.prototype.hasOwnProperty; + var __propIsEnum$_ = Object.prototype.propertyIsEnumerable; + var __defNormalProp$Y = (obj, key, value) => key in obj ? __defProp$Y(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$Y = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$_.call(b, prop)) + __defNormalProp$Y(a, prop, b[prop]); + if (__getOwnPropSymbols$_) + for (var prop of __getOwnPropSymbols$_(b)) { + if (__propIsEnum$_.call(b, prop)) + __defNormalProp$Y(a, prop, b[prop]); + } + return a; + }; + var __objRest$n = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$_.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$_) + for (var prop of __getOwnPropSymbols$_(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$_.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + function isColorLike(value) { + return Color.isColorLike(value); + } + function isFillPattern(value) { + return value instanceof FillPattern; + } + function isFillGradient(value) { + return value instanceof FillGradient; + } + function isTexture(value) { + return value instanceof Texture; + } + function handleColorLike(fill, value, defaultStyle) { + const temp = Color.shared.setValue(value != null ? value : 0); + fill.color = temp.toNumber(); + fill.alpha = temp.alpha === 1 ? defaultStyle.alpha : temp.alpha; + fill.texture = Texture.WHITE; + return __spreadValues$Y(__spreadValues$Y({}, defaultStyle), fill); + } + function handleTexture(fill, value, defaultStyle) { + fill.texture = value; + return __spreadValues$Y(__spreadValues$Y({}, defaultStyle), fill); + } + function handleFillPattern(fill, value, defaultStyle) { + fill.fill = value; + fill.color = 16777215; + fill.texture = value.texture; + fill.matrix = value.transform; + return __spreadValues$Y(__spreadValues$Y({}, defaultStyle), fill); + } + function handleFillGradient(fill, value, defaultStyle) { + value.buildGradient(); + fill.fill = value; + fill.color = 16777215; + fill.texture = value.texture; + fill.matrix = value.transform; + fill.textureSpace = value.textureSpace; + return __spreadValues$Y(__spreadValues$Y({}, defaultStyle), fill); + } + function handleFillObject(value, defaultStyle) { + const style = __spreadValues$Y(__spreadValues$Y({}, defaultStyle), value); + const color = Color.shared.setValue(style.color); + style.alpha *= color.alpha; + style.color = color.toNumber(); + return style; + } + function toFillStyle(value, defaultStyle) { + if (value === void 0 || value === null) { + return null; + } + const fill = {}; + const objectStyle = value; + if (isColorLike(value)) { + return handleColorLike(fill, value, defaultStyle); + } else if (isTexture(value)) { + return handleTexture(fill, value, defaultStyle); + } else if (isFillPattern(value)) { + return handleFillPattern(fill, value, defaultStyle); + } else if (isFillGradient(value)) { + return handleFillGradient(fill, value, defaultStyle); + } else if (objectStyle.fill && isFillPattern(objectStyle.fill)) { + return handleFillPattern(objectStyle, objectStyle.fill, defaultStyle); + } else if (objectStyle.fill && isFillGradient(objectStyle.fill)) { + return handleFillGradient(objectStyle, objectStyle.fill, defaultStyle); + } + return handleFillObject(objectStyle, defaultStyle); + } + function toStrokeStyle(value, defaultStyle) { + const _a = defaultStyle, { width, alignment, miterLimit, cap, join, pixelLine } = _a, rest = __objRest$n(_a, ["width", "alignment", "miterLimit", "cap", "join", "pixelLine"]); + const fill = toFillStyle(value, rest); + if (!fill) { + return null; + } + return __spreadValues$Y({ + width, + alignment, + miterLimit, + cap, + join, + pixelLine + }, fill); + } + + "use strict"; + var __defProp$X = Object.defineProperty; + var __getOwnPropSymbols$Z = Object.getOwnPropertySymbols; + var __hasOwnProp$Z = Object.prototype.hasOwnProperty; + var __propIsEnum$Z = Object.prototype.propertyIsEnumerable; + var __defNormalProp$X = (obj, key, value) => key in obj ? __defProp$X(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$X = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$Z.call(b, prop)) + __defNormalProp$X(a, prop, b[prop]); + if (__getOwnPropSymbols$Z) + for (var prop of __getOwnPropSymbols$Z(b)) { + if (__propIsEnum$Z.call(b, prop)) + __defNormalProp$X(a, prop, b[prop]); + } + return a; + }; + const tmpPoint = new Point(); + const tempMatrix$5 = new Matrix(); + const _GraphicsContext = class _GraphicsContext extends EventEmitter { + constructor() { + super(...arguments); + /** @internal */ + this._gpuData = /* @__PURE__ */ Object.create(null); + /** If set to true, the resource will be garbage collected automatically when it is not used. */ + this.autoGarbageCollect = true; + /** @internal */ + this._gcLastUsed = -1; + /** + * unique id for this graphics context + * @internal + */ + this.uid = uid$1("graphicsContext"); + /** + * Indicates whether content is updated and have to be re-rendered. + * @internal + */ + this.dirty = true; + /** The batch mode for this graphics context. It can be 'auto', 'batch', or 'no-batch'. */ + this.batchMode = "auto"; + /** @internal */ + this.instructions = []; + /** Whether the graphics context has been destroyed. */ + this.destroyed = false; + this._activePath = new GraphicsPath(); + this._transform = new Matrix(); + this._fillStyle = __spreadValues$X({}, _GraphicsContext.defaultFillStyle); + this._strokeStyle = __spreadValues$X({}, _GraphicsContext.defaultStrokeStyle); + this._stateStack = []; + this._tick = 0; + this._bounds = new Bounds(); + this._boundsDirty = true; + } + /** + * Creates a new GraphicsContext object that is a clone of this instance, copying all properties, + * including the current drawing state, transformations, styles, and instructions. + * @returns A new GraphicsContext instance with the same properties and state as this one. + */ + clone() { + const clone = new _GraphicsContext(); + clone.batchMode = this.batchMode; + clone.instructions = this.instructions.slice(); + clone._activePath = this._activePath.clone(); + clone._transform = this._transform.clone(); + clone._fillStyle = __spreadValues$X({}, this._fillStyle); + clone._strokeStyle = __spreadValues$X({}, this._strokeStyle); + clone._stateStack = this._stateStack.slice(); + clone._bounds = this._bounds.clone(); + clone._boundsDirty = true; + return clone; + } + /** + * The current fill style of the graphics context. This can be a color, gradient, pattern, or a more complex style defined by a FillStyle object. + */ + get fillStyle() { + return this._fillStyle; + } + set fillStyle(value) { + this._fillStyle = toFillStyle(value, _GraphicsContext.defaultFillStyle); + } + /** + * The current stroke style of the graphics context. Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object. + */ + get strokeStyle() { + return this._strokeStyle; + } + set strokeStyle(value) { + this._strokeStyle = toStrokeStyle(value, _GraphicsContext.defaultStrokeStyle); + } + /** + * Sets the current fill style of the graphics context. The fill style can be a color, gradient, + * pattern, or a more complex style defined by a FillStyle object. + * @param style - The fill style to apply. This can be a simple color, a gradient or pattern object, + * or a FillStyle or ConvertedFillStyle object. + * @returns The instance of the current GraphicsContext for method chaining. + */ + setFillStyle(style) { + this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle); + return this; + } + /** + * Sets the current stroke style of the graphics context. Similar to fill styles, stroke styles can + * encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object. + * @param style - The stroke style to apply. Can be defined as a color, a gradient or pattern, + * or a StrokeStyle or ConvertedStrokeStyle object. + * @returns The instance of the current GraphicsContext for method chaining. + */ + setStrokeStyle(style) { + this._strokeStyle = toFillStyle(style, _GraphicsContext.defaultStrokeStyle); + return this; + } + texture(texture, tint, dx, dy, dw, dh) { + this.instructions.push({ + action: "texture", + data: { + image: texture, + dx: dx || 0, + dy: dy || 0, + dw: dw || texture.frame.width, + dh: dh || texture.frame.height, + transform: this._transform.clone(), + alpha: this._fillStyle.alpha, + style: tint || tint === 0 ? Color.shared.setValue(tint).toNumber() : 16777215 + } + }); + this.onUpdate(); + return this; + } + /** + * Resets the current path. Any previous path and its commands are discarded and a new path is + * started. This is typically called before beginning a new shape or series of drawing commands. + * @returns The instance of the current GraphicsContext for method chaining. + */ + beginPath() { + this._activePath = new GraphicsPath(); + return this; + } + fill(style, alpha) { + let path; + const lastInstruction = this.instructions[this.instructions.length - 1]; + if (this._tick === 0 && (lastInstruction == null ? void 0 : lastInstruction.action) === "stroke") { + path = lastInstruction.data.path; + } else { + path = this._activePath.clone(); + } + if (!path) return this; + if (style != null) { + if (alpha !== void 0 && typeof style === "number") { + deprecation(v8_0_0, "GraphicsContext.fill(color, alpha) is deprecated, use GraphicsContext.fill({ color, alpha }) instead"); + style = { color: style, alpha }; + } + this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle); + } + this.instructions.push({ + action: "fill", + // TODO copy fill style! + data: { style: this.fillStyle, path } + }); + this.onUpdate(); + this._initNextPathLocation(); + this._tick = 0; + return this; + } + _initNextPathLocation() { + const { x, y } = this._activePath.getLastPoint(Point.shared); + this._activePath.clear(); + this._activePath.moveTo(x, y); + } + /** + * Strokes the current path with the current stroke style. This method can take an optional + * FillInput parameter to define the stroke's appearance, including its color, width, and other properties. + * @param style - (Optional) The stroke style to apply. Can be defined as a simple color or a more complex style object. If omitted, uses the current stroke style. + * @returns The instance of the current GraphicsContext for method chaining. + */ + stroke(style) { + let path; + const lastInstruction = this.instructions[this.instructions.length - 1]; + if (this._tick === 0 && (lastInstruction == null ? void 0 : lastInstruction.action) === "fill") { + path = lastInstruction.data.path; + } else { + path = this._activePath.clone(); + } + if (!path) return this; + if (style != null) { + this._strokeStyle = toStrokeStyle(style, _GraphicsContext.defaultStrokeStyle); + } + this.instructions.push({ + action: "stroke", + // TODO copy fill style! + data: { style: this.strokeStyle, path } + }); + this.onUpdate(); + this._initNextPathLocation(); + this._tick = 0; + return this; + } + /** + * Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by + * subtracting a path from the previously drawn path. If a hole is not completely in a shape, it will + * fail to cut correctly! + * @returns The instance of the current GraphicsContext for method chaining. + */ + cut() { + for (let i = 0; i < 2; i++) { + const lastInstruction = this.instructions[this.instructions.length - 1 - i]; + const holePath = this._activePath.clone(); + if (lastInstruction) { + if (lastInstruction.action === "stroke" || lastInstruction.action === "fill") { + if (lastInstruction.data.hole) { + lastInstruction.data.hole.addPath(holePath); + } else { + lastInstruction.data.hole = holePath; + break; + } + } + } + } + this._initNextPathLocation(); + return this; + } + /** + * Adds an arc to the current path, which is centered at (x, y) with the specified radius, + * starting and ending angles, and direction. + * @param x - The x-coordinate of the arc's center. + * @param y - The y-coordinate of the arc's center. + * @param radius - The arc's radius. + * @param startAngle - The starting angle, in radians. + * @param endAngle - The ending angle, in radians. + * @param counterclockwise - (Optional) Specifies whether the arc is drawn counterclockwise (true) or clockwise (false). Defaults to false. + * @returns The instance of the current GraphicsContext for method chaining. + */ + arc(x, y, radius, startAngle, endAngle, counterclockwise) { + this._tick++; + const t = this._transform; + this._activePath.arc( + t.a * x + t.c * y + t.tx, + t.b * x + t.d * y + t.ty, + radius, + startAngle, + endAngle, + counterclockwise + ); + return this; + } + /** + * Adds an arc to the current path with the given control points and radius, connected to the previous point + * by a straight line if necessary. + * @param x1 - The x-coordinate of the first control point. + * @param y1 - The y-coordinate of the first control point. + * @param x2 - The x-coordinate of the second control point. + * @param y2 - The y-coordinate of the second control point. + * @param radius - The arc's radius. + * @returns The instance of the current GraphicsContext for method chaining. + */ + arcTo(x1, y1, x2, y2, radius) { + this._tick++; + const t = this._transform; + this._activePath.arcTo( + t.a * x1 + t.c * y1 + t.tx, + t.b * x1 + t.d * y1 + t.ty, + t.a * x2 + t.c * y2 + t.tx, + t.b * x2 + t.d * y2 + t.ty, + radius + ); + return this; + } + /** + * Adds an SVG-style arc to the path, allowing for elliptical arcs based on the SVG spec. + * @param rx - The x-radius of the ellipse. + * @param ry - The y-radius of the ellipse. + * @param xAxisRotation - The rotation of the ellipse's x-axis relative + * to the x-axis of the coordinate system, in degrees. + * @param largeArcFlag - Determines if the arc should be greater than or less than 180 degrees. + * @param sweepFlag - Determines if the arc should be swept in a positive angle direction. + * @param x - The x-coordinate of the arc's end point. + * @param y - The y-coordinate of the arc's end point. + * @returns The instance of the current object for chaining. + */ + arcToSvg(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { + this._tick++; + const t = this._transform; + this._activePath.arcToSvg( + rx, + ry, + xAxisRotation, + // should we rotate this with transform?? + largeArcFlag, + sweepFlag, + t.a * x + t.c * y + t.tx, + t.b * x + t.d * y + t.ty + ); + return this; + } + /** + * Adds a cubic Bezier curve to the path. + * It requires three points: the first two are control points and the third one is the end point. + * The starting point is the last point in the current path. + * @param cp1x - The x-coordinate of the first control point. + * @param cp1y - The y-coordinate of the first control point. + * @param cp2x - The x-coordinate of the second control point. + * @param cp2y - The y-coordinate of the second control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothness - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, smoothness) { + this._tick++; + const t = this._transform; + this._activePath.bezierCurveTo( + t.a * cp1x + t.c * cp1y + t.tx, + t.b * cp1x + t.d * cp1y + t.ty, + t.a * cp2x + t.c * cp2y + t.tx, + t.b * cp2x + t.d * cp2y + t.ty, + t.a * x + t.c * y + t.tx, + t.b * x + t.d * y + t.ty, + smoothness + ); + return this; + } + /** + * Closes the current path by drawing a straight line back to the start. + * If the shape is already closed or there are no points in the path, this method does nothing. + * @returns The instance of the current object for chaining. + */ + closePath() { + var _a; + this._tick++; + (_a = this._activePath) == null ? void 0 : _a.closePath(); + return this; + } + /** + * Draws an ellipse at the specified location and with the given x and y radii. + * An optional transformation can be applied, allowing for rotation, scaling, and translation. + * @param x - The x-coordinate of the center of the ellipse. + * @param y - The y-coordinate of the center of the ellipse. + * @param radiusX - The horizontal radius of the ellipse. + * @param radiusY - The vertical radius of the ellipse. + * @returns The instance of the current object for chaining. + */ + ellipse(x, y, radiusX, radiusY) { + this._tick++; + this._activePath.ellipse(x, y, radiusX, radiusY, this._transform.clone()); + return this; + } + /** + * Draws a circle shape. This method adds a new circle path to the current drawing. + * @param x - The x-coordinate of the center of the circle. + * @param y - The y-coordinate of the center of the circle. + * @param radius - The radius of the circle. + * @returns The instance of the current object for chaining. + */ + circle(x, y, radius) { + this._tick++; + this._activePath.circle(x, y, radius, this._transform.clone()); + return this; + } + /** + * Adds another `GraphicsPath` to this path, optionally applying a transformation. + * @param path - The `GraphicsPath` to add. + * @returns The instance of the current object for chaining. + */ + path(path) { + this._tick++; + this._activePath.addPath(path, this._transform.clone()); + return this; + } + /** + * Connects the current point to a new point with a straight line. This method updates the current path. + * @param x - The x-coordinate of the new point to connect to. + * @param y - The y-coordinate of the new point to connect to. + * @returns The instance of the current object for chaining. + */ + lineTo(x, y) { + this._tick++; + const t = this._transform; + this._activePath.lineTo( + t.a * x + t.c * y + t.tx, + t.b * x + t.d * y + t.ty + ); + return this; + } + /** + * Sets the starting point for a new sub-path. Any subsequent drawing commands are considered part of this path. + * @param x - The x-coordinate for the starting point. + * @param y - The y-coordinate for the starting point. + * @returns The instance of the current object for chaining. + */ + moveTo(x, y) { + this._tick++; + const t = this._transform; + const instructions = this._activePath.instructions; + const transformedX = t.a * x + t.c * y + t.tx; + const transformedY = t.b * x + t.d * y + t.ty; + if (instructions.length === 1 && instructions[0].action === "moveTo") { + instructions[0].data[0] = transformedX; + instructions[0].data[1] = transformedY; + return this; + } + this._activePath.moveTo( + transformedX, + transformedY + ); + return this; + } + /** + * Adds a quadratic curve to the path. It requires two points: the control point and the end point. + * The starting point is the last point in the current path. + * @param cpx - The x-coordinate of the control point. + * @param cpy - The y-coordinate of the control point. + * @param x - The x-coordinate of the end point. + * @param y - The y-coordinate of the end point. + * @param smoothness - Optional parameter to adjust the smoothness of the curve. + * @returns The instance of the current object for chaining. + */ + quadraticCurveTo(cpx, cpy, x, y, smoothness) { + this._tick++; + const t = this._transform; + this._activePath.quadraticCurveTo( + t.a * cpx + t.c * cpy + t.tx, + t.b * cpx + t.d * cpy + t.ty, + t.a * x + t.c * y + t.tx, + t.b * x + t.d * y + t.ty, + smoothness + ); + return this; + } + /** + * Draws a rectangle shape. This method adds a new rectangle path to the current drawing. + * @param x - The x-coordinate of the top-left corner of the rectangle. + * @param y - The y-coordinate of the top-left corner of the rectangle. + * @param w - The width of the rectangle. + * @param h - The height of the rectangle. + * @returns The instance of the current object for chaining. + */ + rect(x, y, w, h) { + this._tick++; + this._activePath.rect(x, y, w, h, this._transform.clone()); + return this; + } + /** + * Draws a rectangle with rounded corners. + * The corner radius can be specified to determine how rounded the corners should be. + * An optional transformation can be applied, which allows for rotation, scaling, and translation of the rectangle. + * @param x - The x-coordinate of the top-left corner of the rectangle. + * @param y - The y-coordinate of the top-left corner of the rectangle. + * @param w - The width of the rectangle. + * @param h - The height of the rectangle. + * @param radius - The radius of the rectangle's corners. If not specified, corners will be sharp. + * @returns The instance of the current object for chaining. + */ + roundRect(x, y, w, h, radius) { + this._tick++; + this._activePath.roundRect(x, y, w, h, radius, this._transform.clone()); + return this; + } + /** + * Draws a polygon shape by specifying a sequence of points. This method allows for the creation of complex polygons, + * which can be both open and closed. An optional transformation can be applied, enabling the polygon to be scaled, + * rotated, or translated as needed. + * @param points - An array of numbers, or an array of PointData objects eg [{x,y}, {x,y}, {x,y}] + * representing the x and y coordinates, of the polygon's vertices, in sequence. + * @param close - A boolean indicating whether to close the polygon path. True by default. + */ + poly(points, close) { + this._tick++; + this._activePath.poly(points, close, this._transform.clone()); + return this; + } + /** + * Draws a regular polygon with a specified number of sides. All sides and angles are equal. + * @param x - The x-coordinate of the center of the polygon. + * @param y - The y-coordinate of the center of the polygon. + * @param radius - The radius of the circumscribed circle of the polygon. + * @param sides - The number of sides of the polygon. Must be 3 or more. + * @param rotation - The rotation angle of the polygon, in radians. Zero by default. + * @param transform - An optional `Matrix` object to apply a transformation to the polygon. + * @returns The instance of the current object for chaining. + */ + regularPoly(x, y, radius, sides, rotation = 0, transform) { + this._tick++; + this._activePath.regularPoly(x, y, radius, sides, rotation, transform); + return this; + } + /** + * Draws a polygon with rounded corners. + * Similar to `regularPoly` but with the ability to round the corners of the polygon. + * @param x - The x-coordinate of the center of the polygon. + * @param y - The y-coordinate of the center of the polygon. + * @param radius - The radius of the circumscribed circle of the polygon. + * @param sides - The number of sides of the polygon. Must be 3 or more. + * @param corner - The radius of the rounding of the corners. + * @param rotation - The rotation angle of the polygon, in radians. Zero by default. + * @returns The instance of the current object for chaining. + */ + roundPoly(x, y, radius, sides, corner, rotation) { + this._tick++; + this._activePath.roundPoly(x, y, radius, sides, corner, rotation); + return this; + } + /** + * Draws a shape with rounded corners. This function supports custom radius for each corner of the shape. + * Optionally, corners can be rounded using a quadratic curve instead of an arc, providing a different aesthetic. + * @param points - An array of `RoundedPoint` representing the corners of the shape to draw. + * A minimum of 3 points is required. + * @param radius - The default radius for the corners. + * This radius is applied to all corners unless overridden in `points`. + * @param useQuadratic - If set to true, rounded corners are drawn using a quadraticCurve + * method instead of an arc method. Defaults to false. + * @param smoothness - Specifies the smoothness of the curve when `useQuadratic` is true. + * Higher values make the curve smoother. + * @returns The instance of the current object for chaining. + */ + roundShape(points, radius, useQuadratic, smoothness) { + this._tick++; + this._activePath.roundShape(points, radius, useQuadratic, smoothness); + return this; + } + /** + * Draw Rectangle with fillet corners. This is much like rounded rectangle + * however it support negative numbers as well for the corner radius. + * @param x - Upper left corner of rect + * @param y - Upper right corner of rect + * @param width - Width of rect + * @param height - Height of rect + * @param fillet - accept negative or positive values + */ + filletRect(x, y, width, height, fillet) { + this._tick++; + this._activePath.filletRect(x, y, width, height, fillet); + return this; + } + /** + * Draw Rectangle with chamfer corners. These are angled corners. + * @param x - Upper left corner of rect + * @param y - Upper right corner of rect + * @param width - Width of rect + * @param height - Height of rect + * @param chamfer - non-zero real number, size of corner cutout + * @param transform + */ + chamferRect(x, y, width, height, chamfer, transform) { + this._tick++; + this._activePath.chamferRect(x, y, width, height, chamfer, transform); + return this; + } + /** + * Draws a star shape centered at a specified location. This method allows for the creation + * of stars with a variable number of points, outer radius, optional inner radius, and rotation. + * The star is drawn as a closed polygon with alternating outer and inner vertices to create the star's points. + * An optional transformation can be applied to scale, rotate, or translate the star as needed. + * @param x - The x-coordinate of the center of the star. + * @param y - The y-coordinate of the center of the star. + * @param points - The number of points of the star. + * @param radius - The outer radius of the star (distance from the center to the outer points). + * @param innerRadius - Optional. The inner radius of the star + * (distance from the center to the inner points between the outer points). + * If not provided, defaults to half of the `radius`. + * @param rotation - Optional. The rotation of the star in radians, where 0 is aligned with the y-axis. + * Defaults to 0, meaning one point is directly upward. + * @returns The instance of the current object for chaining further drawing commands. + */ + star(x, y, points, radius, innerRadius = 0, rotation = 0) { + this._tick++; + this._activePath.star(x, y, points, radius, innerRadius, rotation, this._transform.clone()); + return this; + } + /** + * Parses and renders an SVG string into the graphics context. This allows for complex shapes and paths + * defined in SVG format to be drawn within the graphics context. + * @param svg - The SVG string to be parsed and rendered. + */ + svg(svg) { + this._tick++; + SVGParser(svg, this); + return this; + } + /** + * Restores the most recently saved graphics state by popping the top of the graphics state stack. + * This includes transformations, fill styles, and stroke styles. + */ + restore() { + const state = this._stateStack.pop(); + if (state) { + this._transform = state.transform; + this._fillStyle = state.fillStyle; + this._strokeStyle = state.strokeStyle; + } + return this; + } + /** Saves the current graphics state, including transformations, fill styles, and stroke styles, onto a stack. */ + save() { + this._stateStack.push({ + transform: this._transform.clone(), + fillStyle: __spreadValues$X({}, this._fillStyle), + strokeStyle: __spreadValues$X({}, this._strokeStyle) + }); + return this; + } + /** + * Returns the current transformation matrix of the graphics context. + * @returns The current transformation matrix. + */ + getTransform() { + return this._transform; + } + /** + * Resets the current transformation matrix to the identity matrix, effectively removing any transformations (rotation, scaling, translation) previously applied. + * @returns The instance of the current GraphicsContext for method chaining. + */ + resetTransform() { + this._transform.identity(); + return this; + } + /** + * Applies a rotation transformation to the graphics context around the current origin. + * @param angle - The angle of rotation in radians. + * @returns The instance of the current GraphicsContext for method chaining. + */ + rotate(angle) { + this._transform.rotate(angle); + return this; + } + /** + * Applies a scaling transformation to the graphics context, scaling drawings by x horizontally and by y vertically. + * @param x - The scale factor in the horizontal direction. + * @param y - (Optional) The scale factor in the vertical direction. If not specified, the x value is used for both directions. + * @returns The instance of the current GraphicsContext for method chaining. + */ + scale(x, y = x) { + this._transform.scale(x, y); + return this; + } + setTransform(a, b, c, d, dx, dy) { + if (a instanceof Matrix) { + this._transform.set(a.a, a.b, a.c, a.d, a.tx, a.ty); + return this; + } + this._transform.set(a, b, c, d, dx, dy); + return this; + } + transform(a, b, c, d, dx, dy) { + if (a instanceof Matrix) { + this._transform.append(a); + return this; + } + tempMatrix$5.set(a, b, c, d, dx, dy); + this._transform.append(tempMatrix$5); + return this; + } + /** + * Applies a translation transformation to the graphics context, moving the origin by the specified amounts. + * @param x - The amount to translate in the horizontal direction. + * @param y - (Optional) The amount to translate in the vertical direction. If not specified, the x value is used for both directions. + * @returns The instance of the current GraphicsContext for method chaining. + */ + translate(x, y = x) { + this._transform.translate(x, y); + return this; + } + /** + * Clears all drawing commands from the graphics context, effectively resetting it. This includes clearing the path, + * and optionally resetting transformations to the identity matrix. + * @returns The instance of the current GraphicsContext for method chaining. + */ + clear() { + this._activePath.clear(); + this.instructions.length = 0; + this.resetTransform(); + this.onUpdate(); + return this; + } + onUpdate() { + this._boundsDirty = true; + if (this.dirty) return; + this.emit("update", this, 16); + this.dirty = true; + } + /** The bounds of the graphic shape. */ + get bounds() { + if (!this._boundsDirty) return this._bounds; + this._boundsDirty = false; + const bounds = this._bounds; + bounds.clear(); + for (let i = 0; i < this.instructions.length; i++) { + const instruction = this.instructions[i]; + const action = instruction.action; + if (action === "fill") { + const data = instruction.data; + bounds.addBounds(data.path.bounds); + } else if (action === "texture") { + const data = instruction.data; + bounds.addFrame(data.dx, data.dy, data.dx + data.dw, data.dy + data.dh, data.transform); + } + if (action === "stroke") { + const data = instruction.data; + const alignment = data.style.alignment; + const outerPadding = data.style.width * (1 - alignment); + const _bounds = data.path.bounds; + bounds.addFrame( + _bounds.minX - outerPadding, + _bounds.minY - outerPadding, + _bounds.maxX + outerPadding, + _bounds.maxY + outerPadding + ); + } + } + return bounds; + } + /** + * Check to see if a point is contained within this geometry. + * @param point - Point to check if it's contained. + * @returns {boolean} `true` if the point is contained within geometry. + */ + containsPoint(point) { + var _a; + if (!this.bounds.containsPoint(point.x, point.y)) return false; + const instructions = this.instructions; + let hasHit = false; + for (let k = 0; k < instructions.length; k++) { + const instruction = instructions[k]; + const data = instruction.data; + const path = data.path; + if (!instruction.action || !path) continue; + const style = data.style; + const shapes = path.shapePath.shapePrimitives; + for (let i = 0; i < shapes.length; i++) { + const shape = shapes[i].shape; + if (!style || !shape) continue; + const transform = shapes[i].transform; + const transformedPoint = transform ? transform.applyInverse(point, tmpPoint) : point; + if (instruction.action === "fill") { + hasHit = shape.contains(transformedPoint.x, transformedPoint.y); + } else { + const strokeStyle = style; + hasHit = shape.strokeContains(transformedPoint.x, transformedPoint.y, strokeStyle.width, strokeStyle.alignment); + } + const holes = data.hole; + if (holes) { + const holeShapes = (_a = holes.shapePath) == null ? void 0 : _a.shapePrimitives; + if (holeShapes) { + for (let j = 0; j < holeShapes.length; j++) { + if (holeShapes[j].shape.contains(transformedPoint.x, transformedPoint.y)) { + hasHit = false; + } + } + } + } + if (hasHit) { + return true; + } + } + } + return hasHit; + } + /** Unloads the GPU data from the graphics context. */ + unload() { + var _a; + this.emit("unload", this); + for (const key in this._gpuData) { + (_a = this._gpuData[key]) == null ? void 0 : _a.destroy(); + } + this._gpuData = /* @__PURE__ */ Object.create(null); + } + /** + * Destroys the GraphicsData object. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * context.destroy(); + * context.destroy(true); + * context.destroy({ texture: true, textureSource: true }); + */ + destroy(options = false) { + if (this.destroyed) return; + this.destroyed = true; + this._stateStack.length = 0; + this._transform = null; + this.unload(); + this.emit("destroy", this); + this.removeAllListeners(); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + if (this._fillStyle.texture) { + this._fillStyle.fill && "uid" in this._fillStyle.fill ? this._fillStyle.fill.destroy() : this._fillStyle.texture.destroy(destroyTextureSource); + } + if (this._strokeStyle.texture) { + this._strokeStyle.fill && "uid" in this._strokeStyle.fill ? this._strokeStyle.fill.destroy() : this._strokeStyle.texture.destroy(destroyTextureSource); + } + } + this._fillStyle = null; + this._strokeStyle = null; + this.instructions = null; + this._activePath = null; + this._bounds = null; + this._stateStack = null; + this.customShader = null; + this._transform = null; + } + }; + /** The default fill style to use when none is provided. */ + _GraphicsContext.defaultFillStyle = { + /** The color to use for the fill. */ + color: 16777215, + /** The alpha value to use for the fill. */ + alpha: 1, + /** The texture to use for the fill. */ + texture: Texture.WHITE, + /** The matrix to apply. */ + matrix: null, + /** The fill pattern to use. */ + fill: null, + /** Whether coordinates are 'global' or 'local' */ + textureSpace: "local" + }; + /** The default stroke style to use when none is provided. */ + _GraphicsContext.defaultStrokeStyle = { + /** The width of the stroke. */ + width: 1, + /** The color to use for the stroke. */ + color: 16777215, + /** The alpha value to use for the stroke. */ + alpha: 1, + /** The alignment of the stroke. */ + alignment: 0.5, + /** The miter limit to use. */ + miterLimit: 10, + /** The line cap style to use. */ + cap: "butt", + /** The line join style to use. */ + join: "miter", + /** The texture to use for the fill. */ + texture: Texture.WHITE, + /** The matrix to apply. */ + matrix: null, + /** The fill pattern to use. */ + fill: null, + /** Whether coordinates are 'global' or 'local' */ + textureSpace: "local", + /** If the stroke is a pixel line. */ + pixelLine: false + }; + let GraphicsContext = _GraphicsContext; + + "use strict"; + function getResolutionOfUrl(url, defaultValue = 1) { + var _a; + const resolution = (_a = Resolver.RETINA_PREFIX) == null ? void 0 : _a.exec(url); + if (resolution) { + return parseFloat(resolution[1]); + } + return defaultValue; + } + + "use strict"; + function createTexture(source, loader, url) { + source.label = url; + source._sourceOrigin = url; + const texture = new Texture({ + source, + label: url + }); + const unload = () => { + delete loader.promiseCache[url]; + if (Cache.has(url)) { + Cache.remove(url); + } + }; + texture.source.once("destroy", () => { + if (loader.promiseCache[url]) { + warn("[Assets] A TextureSource managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the TextureSource."); + unload(); + } + }); + texture.once("destroy", () => { + if (!source.destroyed) { + warn("[Assets] A Texture managed by Assets was destroyed instead of unloaded! Use Assets.unload() instead of destroying the Texture."); + unload(); + } + }); + return texture; + } + + "use strict"; + var __defProp$W = Object.defineProperty; + var __getOwnPropSymbols$Y = Object.getOwnPropertySymbols; + var __hasOwnProp$Y = Object.prototype.hasOwnProperty; + var __propIsEnum$Y = Object.prototype.propertyIsEnumerable; + var __defNormalProp$W = (obj, key, value) => key in obj ? __defProp$W(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$W = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$Y.call(b, prop)) + __defNormalProp$W(a, prop, b[prop]); + if (__getOwnPropSymbols$Y) + for (var prop of __getOwnPropSymbols$Y(b)) { + if (__propIsEnum$Y.call(b, prop)) + __defNormalProp$W(a, prop, b[prop]); + } + return a; + }; + var __objRest$m = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$Y.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$Y) + for (var prop of __getOwnPropSymbols$Y(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$Y.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const validSVGExtension = ".svg"; + const validSVGMIME = "image/svg+xml"; + const loadSvg = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.Low, + name: "loadSVG" + }, + /** used for deprecation purposes */ + name: "loadSVG", + id: "svg", + config: { + crossOrigin: "anonymous", + parseAsGraphicsContext: false + }, + test(url) { + return checkDataUrl(url, validSVGMIME) || checkExtension(url, validSVGExtension); + }, + async load(url, asset, loader) { + var _a, _b; + if ((_b = (_a = asset.data) == null ? void 0 : _a.parseAsGraphicsContext) != null ? _b : this.config.parseAsGraphicsContext) { + return loadAsGraphics(url); + } + return loadAsTexture(url, asset, loader, this.config.crossOrigin); + }, + unload(asset) { + asset.destroy(true); + } + }; + async function loadAsTexture(url, asset, loader, crossOrigin) { + var _a, _b, _c, _d, _e, _f; + const response = await DOMAdapter.get().fetch(url); + const image = DOMAdapter.get().createImage(); + image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(await response.text())}`; + image.crossOrigin = crossOrigin; + await image.decode(); + const width = (_b = (_a = asset.data) == null ? void 0 : _a.width) != null ? _b : image.width; + const height = (_d = (_c = asset.data) == null ? void 0 : _c.height) != null ? _d : image.height; + const resolution = ((_e = asset.data) == null ? void 0 : _e.resolution) || getResolutionOfUrl(url); + const canvasWidth = Math.ceil(width * resolution); + const canvasHeight = Math.ceil(height * resolution); + const canvas = DOMAdapter.get().createCanvas(canvasWidth, canvasHeight); + const context = canvas.getContext("2d"); + context.imageSmoothingEnabled = true; + context.imageSmoothingQuality = "high"; + context.drawImage(image, 0, 0, width * resolution, height * resolution); + const _g = (_f = asset.data) != null ? _f : {}, { parseAsGraphicsContext: _p } = _g, rest = __objRest$m(_g, ["parseAsGraphicsContext"]); + const base = new ImageSource(__spreadValues$W({ + resource: canvas, + alphaMode: "premultiply-alpha-on-upload", + resolution + }, rest)); + return createTexture(base, loader, url); + } + async function loadAsGraphics(url) { + const response = await DOMAdapter.get().fetch(url); + const svgSource = await response.text(); + const context = new GraphicsContext(); + context.svg(svgSource); + return context; + } + + const WORKER_CODE$3 = "(function () {\n 'use strict';\n\n const WHITE_PNG = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=\";\n async function checkImageBitmap() {\n try {\n if (typeof createImageBitmap !== \"function\") return false;\n const response = await fetch(WHITE_PNG);\n const imageBlob = await response.blob();\n const imageBitmap = await createImageBitmap(imageBlob);\n return imageBitmap.width === 1 && imageBitmap.height === 1;\n } catch (_e) {\n return false;\n }\n }\n void checkImageBitmap().then((result) => {\n self.postMessage(result);\n });\n\n})();\n"; + let WORKER_URL$3 = null; + let WorkerInstance$3 = class WorkerInstance + { + constructor() + { + if (!WORKER_URL$3) + { + WORKER_URL$3 = URL.createObjectURL(new Blob([WORKER_CODE$3], { type: 'application/javascript' })); + } + this.worker = new Worker(WORKER_URL$3); + } + }; + WorkerInstance$3.revokeObjectURL = function revokeObjectURL() + { + if (WORKER_URL$3) + { + URL.revokeObjectURL(WORKER_URL$3); + WORKER_URL$3 = null; + } + }; + + const WORKER_CODE$2 = "(function () {\n 'use strict';\n\n async function loadImageBitmap(url, alphaMode) {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`[WorkerManager.loadImageBitmap] Failed to fetch ${url}: ${response.status} ${response.statusText}`);\n }\n const imageBlob = await response.blob();\n return alphaMode === \"premultiplied-alpha\" ? createImageBitmap(imageBlob, { premultiplyAlpha: \"none\" }) : createImageBitmap(imageBlob);\n }\n self.onmessage = async (event) => {\n try {\n const imageBitmap = await loadImageBitmap(event.data.data[0], event.data.data[1]);\n self.postMessage({\n data: imageBitmap,\n uuid: event.data.uuid,\n id: event.data.id\n }, [imageBitmap]);\n } catch (e) {\n self.postMessage({\n error: e,\n uuid: event.data.uuid,\n id: event.data.id\n });\n }\n };\n\n})();\n"; + let WORKER_URL$2 = null; + let WorkerInstance$2 = class WorkerInstance + { + constructor() + { + if (!WORKER_URL$2) + { + WORKER_URL$2 = URL.createObjectURL(new Blob([WORKER_CODE$2], { type: 'application/javascript' })); + } + this.worker = new Worker(WORKER_URL$2); + } + }; + WorkerInstance$2.revokeObjectURL = function revokeObjectURL() + { + if (WORKER_URL$2) + { + URL.revokeObjectURL(WORKER_URL$2); + WORKER_URL$2 = null; + } + }; + + "use strict"; + let UUID = 0; + let MAX_WORKERS; + class WorkerManagerClass { + constructor() { + /** Whether the worker manager has been initialized */ + this._initialized = false; + /** Current number of created workers (used to enforce MAX_WORKERS limit) */ + this._createdWorkers = 0; + this._workerPool = []; + this._queue = []; + this._resolveHash = {}; + } + /** + * Checks if ImageBitmap is supported in the current environment. + * + * This method uses a dedicated worker to test ImageBitmap support + * and caches the result for subsequent calls. + * @returns Promise that resolves to true if ImageBitmap is supported, false otherwise + */ + isImageBitmapSupported() { + if (this._isImageBitmapSupported !== void 0) return this._isImageBitmapSupported; + this._isImageBitmapSupported = new Promise((resolve) => { + const { worker } = new WorkerInstance$3(); + worker.addEventListener("message", (event) => { + worker.terminate(); + WorkerInstance$3.revokeObjectURL(); + resolve(event.data); + }); + }); + return this._isImageBitmapSupported; + } + /** + * Loads an image as an ImageBitmap using a web worker. + * @param src - The source URL or path of the image to load + * @param asset - Optional resolved asset containing additional texture source options + * @returns Promise that resolves to the loaded ImageBitmap + * @example + * ```typescript + * const bitmap = await WorkerManager.loadImageBitmap('image.png'); + * const bitmapWithOptions = await WorkerManager.loadImageBitmap('image.png', asset); + * ``` + */ + loadImageBitmap(src, asset) { + var _a; + return this._run("loadImageBitmap", [src, (_a = asset == null ? void 0 : asset.data) == null ? void 0 : _a.alphaMode]); + } + /** + * Initializes the worker pool if not already initialized. + * Currently a no-op but reserved for future initialization logic. + */ + async _initWorkers() { + if (this._initialized) return; + this._initialized = true; + } + /** + * Gets an available worker from the pool or creates a new one if needed. + * + * Workers are created up to the MAX_WORKERS limit (based on navigator.hardwareConcurrency). + * Each worker is configured with a message handler for processing results. + * @returns Available worker or undefined if pool is at capacity and no workers are free + */ + _getWorker() { + if (MAX_WORKERS === void 0) { + MAX_WORKERS = navigator.hardwareConcurrency || 4; + } + let worker = this._workerPool.pop(); + if (!worker && this._createdWorkers < MAX_WORKERS) { + this._createdWorkers++; + worker = new WorkerInstance$2().worker; + worker.addEventListener("message", (event) => { + this._complete(event.data); + this._returnWorker(event.target); + this._next(); + }); + } + return worker; + } + /** + * Returns a worker to the pool after completing a task. + * @param worker - The worker to return to the pool + */ + _returnWorker(worker) { + this._workerPool.push(worker); + } + /** + * Handles completion of a worker task by resolving or rejecting the corresponding promise. + * @param data - Result data from the worker containing uuid, data, and optional error + */ + _complete(data) { + if (!this._resolveHash[data.uuid]) { + return; + } + if (data.error !== void 0) { + this._resolveHash[data.uuid].reject(data.error); + } else { + this._resolveHash[data.uuid].resolve(data.data); + } + delete this._resolveHash[data.uuid]; + } + /** + * Executes a task using the worker pool system. + * + * Queues the task and processes it when a worker becomes available. + * @param id - Identifier for the type of task to run + * @param args - Arguments to pass to the worker + * @returns Promise that resolves with the worker's result + */ + async _run(id, args) { + await this._initWorkers(); + const promise = new Promise((resolve, reject) => { + this._queue.push({ id, arguments: args, resolve, reject }); + }); + this._next(); + return promise; + } + /** + * Processes the next item in the queue if workers are available. + * + * This method is called after worker initialization and when workers + * complete tasks to continue processing the queue. + */ + _next() { + if (!this._queue.length) return; + const worker = this._getWorker(); + if (!worker) { + return; + } + const toDo = this._queue.pop(); + const id = toDo.id; + this._resolveHash[UUID] = { resolve: toDo.resolve, reject: toDo.reject }; + worker.postMessage({ + data: toDo.arguments, + uuid: UUID++, + id + }); + } + /** + * Resets the worker manager, terminating all workers and clearing the queue. + * + * This method: + * - Terminates all active workers + * - Rejects all pending promises with an error + * - Clears all internal state + * - Resets initialization flags + * + * This should be called when the worker manager is no longer needed + * to prevent memory leaks and ensure proper cleanup. + * @example + * ```typescript + * // Clean up when shutting down + * WorkerManager.reset(); + * ``` + */ + reset() { + this._workerPool.forEach((worker) => worker.terminate()); + this._workerPool.length = 0; + Object.values(this._resolveHash).forEach(({ reject }) => { + reject == null ? void 0 : reject(new Error("WorkerManager has been reset before completion")); + }); + this._resolveHash = {}; + this._queue.length = 0; + this._initialized = false; + this._createdWorkers = 0; + } + } + const WorkerManager = new WorkerManagerClass(); + + "use strict"; + var __defProp$V = Object.defineProperty; + var __getOwnPropSymbols$X = Object.getOwnPropertySymbols; + var __hasOwnProp$X = Object.prototype.hasOwnProperty; + var __propIsEnum$X = Object.prototype.propertyIsEnumerable; + var __defNormalProp$V = (obj, key, value) => key in obj ? __defProp$V(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$V = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$X.call(b, prop)) + __defNormalProp$V(a, prop, b[prop]); + if (__getOwnPropSymbols$X) + for (var prop of __getOwnPropSymbols$X(b)) { + if (__propIsEnum$X.call(b, prop)) + __defNormalProp$V(a, prop, b[prop]); + } + return a; + }; + const validImageExtensions = [".jpeg", ".jpg", ".png", ".webp", ".avif"]; + const validImageMIMEs = [ + "image/jpeg", + "image/png", + "image/webp", + "image/avif" + ]; + async function loadImageBitmap(url, asset) { + var _a; + const response = await DOMAdapter.get().fetch(url); + if (!response.ok) { + throw new Error(`[loadImageBitmap] Failed to fetch ${url}: ${response.status} ${response.statusText}`); + } + const imageBlob = await response.blob(); + return ((_a = asset == null ? void 0 : asset.data) == null ? void 0 : _a.alphaMode) === "premultiplied-alpha" ? createImageBitmap(imageBlob, { premultiplyAlpha: "none" }) : createImageBitmap(imageBlob); + } + const loadTextures = { + /** used for deprecation purposes */ + name: "loadTextures", + id: "texture", + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.High, + name: "loadTextures" + }, + config: { + preferWorkers: true, + preferCreateImageBitmap: true, + crossOrigin: "anonymous" + }, + test(url) { + return checkDataUrl(url, validImageMIMEs) || checkExtension(url, validImageExtensions); + }, + async load(url, asset, loader) { + var _a; + let src = null; + if (globalThis.createImageBitmap && this.config.preferCreateImageBitmap) { + if (this.config.preferWorkers && await WorkerManager.isImageBitmapSupported()) { + src = await WorkerManager.loadImageBitmap(url, asset); + } else { + src = await loadImageBitmap(url, asset); + } + } else { + src = await new Promise((resolve, reject) => { + src = DOMAdapter.get().createImage(); + src.crossOrigin = this.config.crossOrigin; + src.src = url; + if (src.complete) { + resolve(src); + } else { + src.onload = () => { + resolve(src); + }; + src.onerror = reject; + } + }); + } + const base = new ImageSource(__spreadValues$V({ + resource: src, + alphaMode: "premultiply-alpha-on-upload", + resolution: ((_a = asset.data) == null ? void 0 : _a.resolution) || getResolutionOfUrl(url) + }, asset.data)); + return createTexture(base, loader, url); + }, + unload(texture) { + texture.destroy(true); + } + }; + + "use strict"; + var __defProp$U = Object.defineProperty; + var __defProps$r = Object.defineProperties; + var __getOwnPropDescs$r = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$W = Object.getOwnPropertySymbols; + var __hasOwnProp$W = Object.prototype.hasOwnProperty; + var __propIsEnum$W = Object.prototype.propertyIsEnumerable; + var __defNormalProp$U = (obj, key, value) => key in obj ? __defProp$U(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$U = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$W.call(b, prop)) + __defNormalProp$U(a, prop, b[prop]); + if (__getOwnPropSymbols$W) + for (var prop of __getOwnPropSymbols$W(b)) { + if (__propIsEnum$W.call(b, prop)) + __defNormalProp$U(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$r = (a, b) => __defProps$r(a, __getOwnPropDescs$r(b)); + const potentialVideoExtensions = [".mp4", ".m4v", ".webm", ".ogg", ".ogv", ".h264", ".avi", ".mov"]; + let validVideoExtensions; + let validVideoMIMEs; + function crossOrigin(element, url, crossorigin) { + if (crossorigin === void 0 && !url.startsWith("data:")) { + element.crossOrigin = determineCrossOrigin(url); + } else if (crossorigin !== false) { + element.crossOrigin = typeof crossorigin === "string" ? crossorigin : "anonymous"; + } + } + function preloadVideo(element) { + return new Promise((resolve, reject) => { + element.addEventListener("canplaythrough", loaded); + element.addEventListener("error", error); + element.load(); + function loaded() { + cleanup(); + resolve(); + } + function error(err) { + cleanup(); + reject(err); + } + function cleanup() { + element.removeEventListener("canplaythrough", loaded); + element.removeEventListener("error", error); + } + }); + } + function determineCrossOrigin(url, loc = globalThis.location) { + if (url.startsWith("data:")) { + return ""; + } + loc || (loc = globalThis.location); + const parsedUrl = new URL(url, document.baseURI); + if (parsedUrl.hostname !== loc.hostname || parsedUrl.port !== loc.port || parsedUrl.protocol !== loc.protocol) { + return "anonymous"; + } + return ""; + } + function getBrowserSupportedVideoExtensions() { + const supportedExtensions = []; + const supportedMimes = []; + for (const ext of potentialVideoExtensions) { + const mimeType = VideoSource.MIME_TYPES[ext.substring(1)] || `video/${ext.substring(1)}`; + if (testVideoFormat(mimeType)) { + supportedExtensions.push(ext); + if (!supportedMimes.includes(mimeType)) { + supportedMimes.push(mimeType); + } + } + } + return { + validVideoExtensions: supportedExtensions, + validVideoMime: supportedMimes + }; + } + const loadVideoTextures = { + /** used for deprecation purposes */ + name: "loadVideo", + id: "video", + extension: { + type: ExtensionType.LoadParser, + name: "loadVideo" + }, + test(url) { + if (!validVideoExtensions || !validVideoMIMEs) { + const { validVideoExtensions: ve, validVideoMime: vm } = getBrowserSupportedVideoExtensions(); + validVideoExtensions = ve; + validVideoMIMEs = vm; + } + const isValidDataUrl = checkDataUrl(url, validVideoMIMEs); + const isValidExtension = checkExtension(url, validVideoExtensions); + return isValidDataUrl || isValidExtension; + }, + async load(url, asset, loader) { + var _a, _b; + const options = __spreadValues$U(__spreadProps$r(__spreadValues$U({}, VideoSource.defaultOptions), { + resolution: ((_a = asset.data) == null ? void 0 : _a.resolution) || getResolutionOfUrl(url), + alphaMode: ((_b = asset.data) == null ? void 0 : _b.alphaMode) || await detectVideoAlphaMode() + }), asset.data); + const videoElement = document.createElement("video"); + const attributeMap = { + preload: options.autoLoad !== false ? "auto" : void 0, + "webkit-playsinline": options.playsinline !== false ? "" : void 0, + playsinline: options.playsinline !== false ? "" : void 0, + muted: options.muted === true ? "" : void 0, + loop: options.loop === true ? "" : void 0, + autoplay: options.autoPlay !== false ? "" : void 0 + }; + Object.keys(attributeMap).forEach((key) => { + const value = attributeMap[key]; + if (value !== void 0) videoElement.setAttribute(key, value); + }); + if (options.muted === true) { + videoElement.muted = true; + } + crossOrigin(videoElement, url, options.crossorigin); + const sourceElement = document.createElement("source"); + let mime; + if (options.mime) { + mime = options.mime; + } else if (url.startsWith("data:")) { + mime = url.slice(5, url.indexOf(";")); + } else if (!url.startsWith("blob:")) { + const ext = url.split("?")[0].slice(url.lastIndexOf(".") + 1).toLowerCase(); + mime = VideoSource.MIME_TYPES[ext] || `video/${ext}`; + } + sourceElement.src = url; + if (mime) { + sourceElement.type = mime; + } + return new Promise((resolve, reject) => { + if (options.preload && !options.autoPlay) { + videoElement.load(); + } + videoElement.addEventListener("canplay", onCanPlay); + videoElement.addEventListener("error", onError); + sourceElement.addEventListener("error", onError); + videoElement.appendChild(sourceElement); + async function onCanPlay() { + const base = new VideoSource(__spreadProps$r(__spreadValues$U({}, options), { resource: videoElement })); + cleanup(); + if (asset.data.preload) { + await preloadVideo(videoElement); + } + resolve(createTexture(base, loader, url)); + } + function onError(event) { + cleanup(); + reject(event); + } + function cleanup() { + videoElement.removeEventListener("canplay", onCanPlay); + videoElement.removeEventListener("error", onError); + sourceElement.removeEventListener("error", onError); + } + }); + }, + unload(texture) { + texture.destroy(true); + } + }; + + "use strict"; + const resolveTextureUrl = { + extension: { + type: ExtensionType.ResolveParser, + name: "resolveTexture" + }, + test: loadTextures.test, + parse: (value) => { + var _a, _b; + return { + resolution: parseFloat((_b = (_a = Resolver.RETINA_PREFIX.exec(value)) == null ? void 0 : _a[1]) != null ? _b : "1"), + format: value.split(".").pop(), + src: value + }; + } + }; + + "use strict"; + const resolveJsonUrl = { + extension: { + type: ExtensionType.ResolveParser, + priority: -2, + name: "resolveJson" + }, + test: (value) => Resolver.RETINA_PREFIX.test(value) && value.endsWith(".json"), + parse: resolveTextureUrl.parse + }; + + "use strict"; + var __defProp$T = Object.defineProperty; + var __getOwnPropSymbols$V = Object.getOwnPropertySymbols; + var __hasOwnProp$V = Object.prototype.hasOwnProperty; + var __propIsEnum$V = Object.prototype.propertyIsEnumerable; + var __defNormalProp$T = (obj, key, value) => key in obj ? __defProp$T(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$T = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$V.call(b, prop)) + __defNormalProp$T(a, prop, b[prop]); + if (__getOwnPropSymbols$V) + for (var prop of __getOwnPropSymbols$V(b)) { + if (__propIsEnum$V.call(b, prop)) + __defNormalProp$T(a, prop, b[prop]); + } + return a; + }; + class AssetsClass { + constructor() { + this._detections = []; + this._initialized = false; + this.resolver = new Resolver(); + this.loader = new Loader(); + this.cache = Cache; + this._backgroundLoader = new BackgroundLoader(this.loader); + this._backgroundLoader.active = true; + this.reset(); + } + /** + * Initializes the Assets class with configuration options. While not required, + * calling this before loading assets is recommended to set up default behaviors. + * @param options - Configuration options for the Assets system + * @example + * ```ts + * // Basic initialization (optional as Assets.load will call this automatically) + * await Assets.init(); + * + * // With CDN configuration + * await Assets.init({ + * basePath: 'https://my-cdn.com/assets/', + * defaultSearchParams: { version: '1.0.0' } + * }); + * + * // With manifest and preferences + * await Assets.init({ + * manifest: { + * bundles: [{ + * name: 'game-screen', + * assets: [ + * { + * alias: 'hero', + * src: 'hero.{png,webp}', + * data: { scaleMode: SCALE_MODES.NEAREST } + * }, + * { + * alias: 'map', + * src: 'map.json' + * } + * ] + * }] + * }, + * // Optimize for device capabilities + * texturePreference: { + * resolution: window.devicePixelRatio, + * format: ['webp', 'png'] + * }, + * // Set global preferences + * preferences: { + * crossOrigin: 'anonymous', + * } + * }); + * + * // Load assets after initialization + * const heroTexture = await Assets.load('hero'); + * ``` + * @remarks + * - Can be called only once; subsequent calls will be ignored with a warning + * - Format detection runs automatically unless `skipDetections` is true + * - The manifest can be a URL to a JSON file or an inline object + * @see {@link AssetInitOptions} For all available initialization options + * @see {@link AssetsManifest} For manifest format details + */ + async init(options = {}) { + var _a, _b, _c; + if (this._initialized) { + warn("[Assets]AssetManager already initialized, did you load before calling this Assets.init()?"); + return; + } + this._initialized = true; + if (options.defaultSearchParams) { + this.resolver.setDefaultSearchParams(options.defaultSearchParams); + } + if (options.basePath) { + this.resolver.basePath = options.basePath; + } + if (options.bundleIdentifier) { + this.resolver.setBundleIdentifier(options.bundleIdentifier); + } + if (options.manifest) { + let manifest = options.manifest; + if (typeof manifest === "string") { + manifest = await this.load(manifest); + } + this.resolver.addManifest(manifest); + } + const resolutionPref = (_b = (_a = options.texturePreference) == null ? void 0 : _a.resolution) != null ? _b : 1; + const resolution = typeof resolutionPref === "number" ? [resolutionPref] : resolutionPref; + const formats = await this._detectFormats({ + preferredFormats: (_c = options.texturePreference) == null ? void 0 : _c.format, + skipDetections: options.skipDetections, + detections: this._detections + }); + this.resolver.prefer({ + params: { + format: formats, + resolution + } + }); + if (options.preferences) { + this.setPreferences(options.preferences); + } + if (options.loadOptions) { + this.loader.loadOptions = __spreadValues$T(__spreadValues$T({}, this.loader.loadOptions), options.loadOptions); + } + } + /** + * Registers assets with the Assets resolver. This method maps keys (aliases) to asset sources, + * allowing you to load assets using friendly names instead of direct URLs. + * @param assets - The unresolved assets to add to the resolver + * @example + * ```ts + * // Basic usage - single asset + * Assets.add({ + * alias: 'myTexture', + * src: 'assets/texture.png' + * }); + * const texture = await Assets.load('myTexture'); + * + * // Multiple aliases for the same asset + * Assets.add({ + * alias: ['hero', 'player'], + * src: 'hero.png' + * }); + * const hero1 = await Assets.load('hero'); + * const hero2 = await Assets.load('player'); // Same texture + * + * // Multiple format support + * Assets.add({ + * alias: 'character', + * src: 'character.{webp,png}' // Will choose best format + * }); + * Assets.add({ + * alias: 'character', + * src: ['character.webp', 'character.png'], // Explicitly specify formats + * }); + * + * // With texture options + * Assets.add({ + * alias: 'sprite', + * src: 'sprite.png', + * data: { scaleMode: 'nearest' } + * }); + * + * // Multiple assets at once + * Assets.add([ + * { alias: 'bg', src: 'background.png' }, + * { alias: 'music', src: 'music.mp3' }, + * { alias: 'spritesheet', src: 'sheet.json', data: { ignoreMultiPack: false } } + * ]); + * ``` + * @remarks + * - Assets are resolved when loaded, not when added + * - Multiple formats use the best available format for the browser + * - Adding with same alias overwrites previous definition + * - The `data` property is passed to the asset loader + * @see {@link Resolver} For details on asset resolution + * @see {@link LoaderParser} For asset-specific data options + * @advanced + */ + add(assets) { + this.resolver.add(assets); + } + async load(urls, onProgress) { + if (!this._initialized) { + await this.init(); + } + const singleAsset = isSingleItem(urls); + const urlArray = convertToList(urls).map((url) => { + if (typeof url !== "string") { + const aliases = this.resolver.getAlias(url); + if (aliases.some((alias) => !this.resolver.hasKey(alias))) { + this.add(url); + } + return Array.isArray(aliases) ? aliases[0] : aliases; + } + if (!this.resolver.hasKey(url)) this.add({ alias: url, src: url }); + return url; + }); + const resolveResults = this.resolver.resolve(urlArray); + const out = await this._mapLoadToResolve(resolveResults, onProgress); + return singleAsset ? out[urlArray[0]] : out; + } + /** + * Registers a bundle of assets that can be loaded as a group. Bundles are useful for organizing + * assets into logical groups, such as game levels or UI screens. + * @param bundleId - Unique identifier for the bundle + * @param assets - Assets to include in the bundle + * @example + * ```ts + * // Add a bundle using array format + * Assets.addBundle('animals', [ + * { alias: 'bunny', src: 'bunny.png' }, + * { alias: 'chicken', src: 'chicken.png' }, + * { alias: 'thumper', src: 'thumper.png' }, + * ]); + * + * // Add a bundle using object format + * Assets.addBundle('animals', { + * bunny: 'bunny.png', + * chicken: 'chicken.png', + * thumper: 'thumper.png', + * }); + * + * // Add a bundle with advanced options + * Assets.addBundle('ui', [ + * { + * alias: 'button', + * src: 'button.{webp,png}', + * data: { scaleMode: 'nearest' } + * }, + * { + * alias: ['logo', 'brand'], // Multiple aliases + * src: 'logo.svg', + * data: { resolution: 2 } + * } + * ]); + * + * // Load the bundle + * await Assets.loadBundle('animals'); + * + * // Use the loaded assets + * const bunny = Sprite.from('bunny'); + * const chicken = Sprite.from('chicken'); + * ``` + * @remarks + * - Bundle IDs must be unique + * - Assets in bundles are not loaded until `loadBundle` is called + * - Bundles can be background loaded using `backgroundLoadBundle` + * - Assets in bundles can be loaded individually using their aliases + * @see {@link Assets.loadBundle} For loading bundles + * @see {@link Assets.backgroundLoadBundle} For background loading bundles + * @see {@link Assets.unloadBundle} For unloading bundles + * @see {@link AssetsManifest} For manifest format details + */ + addBundle(bundleId, assets) { + this.resolver.addBundle(bundleId, assets); + } + /** + * Loads a bundle or multiple bundles of assets. Bundles are collections of related assets + * that can be loaded together. + * @param bundleIds - Single bundle ID or array of bundle IDs to load + * @param onProgress - Optional callback for load progress (0.0 to 1.0) + * @returns Promise that resolves with the loaded bundle assets + * @example + * ```ts + * // Define bundles in your manifest + * const manifest = { + * bundles: [ + * { + * name: 'load-screen', + * assets: [ + * { + * alias: 'background', + * src: 'sunset.png', + * }, + * { + * alias: 'bar', + * src: 'load-bar.{png,webp}', // use an array of individual assets + * }, + * ], + * }, + * { + * name: 'game-screen', + * assets: [ + * { + * alias: 'character', + * src: 'robot.png', + * }, + * { + * alias: 'enemy', + * src: 'bad-guy.png', + * }, + * ], + * }, + * ] + * }; + * + * // Initialize with manifest + * await Assets.init({ manifest }); + * + * // Or add bundles programmatically + * Assets.addBundle('load-screen', [...]); + * Assets.loadBundle('load-screen'); + * + * // Load a single bundle + * await Assets.loadBundle('load-screen'); + * const bg = Sprite.from('background'); // Uses alias from bundle + * + * // Load multiple bundles + * await Assets.loadBundle([ + * 'load-screen', + * 'game-screen' + * ]); + * + * // Load with progress tracking + * await Assets.loadBundle('game-screen', (progress) => { + * console.log(`Loading: ${Math.round(progress * 100)}%`); + * }); + * ``` + * @remarks + * - Bundle assets are cached automatically + * - Bundles can be pre-loaded using `backgroundLoadBundle` + * - Assets in bundles can be accessed by their aliases + * - Progress callback receives values from 0.0 to 1.0 + * @throws {Error} If the bundle ID doesn't exist in the manifest + * @see {@link Assets.addBundle} For adding bundles programmatically + * @see {@link Assets.backgroundLoadBundle} For background loading bundles + * @see {@link Assets.unloadBundle} For unloading bundles + * @see {@link AssetsManifest} For manifest format details + */ + async loadBundle(bundleIds, onProgress) { + if (!this._initialized) { + await this.init(); + } + let singleAsset = false; + if (typeof bundleIds === "string") { + singleAsset = true; + bundleIds = [bundleIds]; + } + const resolveResults = this.resolver.resolveBundle(bundleIds); + const out = {}; + const keys = Object.keys(resolveResults); + let total = 0; + const counts = []; + const _onProgress = () => { + onProgress == null ? void 0 : onProgress(counts.reduce((a, b) => a + b, 0) / total); + }; + const promises = keys.map((bundleId, i) => { + const resolveResult = resolveResults[bundleId]; + const values = Object.values(resolveResult); + const totalAssetsToLoad = [...new Set(values.flat())]; + const progressSize = totalAssetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0); + counts.push(0); + total += progressSize; + return this._mapLoadToResolve(resolveResult, (e) => { + counts[i] = e * progressSize; + _onProgress(); + }).then((resolveResult2) => { + out[bundleId] = resolveResult2; + }); + }); + await Promise.all(promises); + return singleAsset ? out[bundleIds[0]] : out; + } + /** + * Initiates background loading of assets. This allows assets to be loaded passively while other operations + * continue, making them instantly available when needed later. + * + * Background loading is useful for: + * - Preloading game levels while in a menu + * - Loading non-critical assets during gameplay + * - Reducing visible loading screens + * @param urls - Single URL/alias or array of URLs/aliases to load in the background + * @example + * ```ts + * // Basic background loading + * Assets.backgroundLoad('images/level2-assets.png'); + * + * // Background load multiple assets + * Assets.backgroundLoad([ + * 'images/sprite1.png', + * 'images/sprite2.png', + * 'images/background.png' + * ]); + * + * // Later, when you need the assets + * const textures = await Assets.load([ + * 'images/sprite1.png', + * 'images/sprite2.png' + * ]); // Resolves immediately if background loading completed + * ``` + * @remarks + * - Background loading happens one asset at a time to avoid blocking the main thread + * - Loading can be interrupted safely by calling `Assets.load()` + * - Assets are cached as they complete loading + * - No progress tracking is available for background loading + */ + async backgroundLoad(urls) { + if (!this._initialized) { + await this.init(); + } + if (typeof urls === "string") { + urls = [urls]; + } + const resolveResults = this.resolver.resolve(urls); + this._backgroundLoader.add(Object.values(resolveResults)); + } + /** + * Initiates background loading of asset bundles. Similar to backgroundLoad but works with + * predefined bundles of assets. + * + * Perfect for: + * - Preloading level bundles during gameplay + * - Loading UI assets during splash screens + * - Preparing assets for upcoming game states + * @param bundleIds - Single bundle ID or array of bundle IDs to load in the background + * @example + * ```ts + * // Define bundles in your manifest + * await Assets.init({ + * manifest: { + * bundles: [ + * { + * name: 'home', + * assets: [ + * { + * alias: 'background', + * src: 'images/home-bg.png', + * }, + * { + * alias: 'logo', + * src: 'images/logo.png', + * } + * ] + * }, + * { + * name: 'level-1', + * assets: [ + * { + * alias: 'background', + * src: 'images/level1/bg.png', + * }, + * { + * alias: 'sprites', + * src: 'images/level1/sprites.json' + * } + * ] + * }] + * } + * }); + * + * // Load the home screen assets right away + * await Assets.loadBundle('home'); + * showHomeScreen(); + * + * // Start background loading while showing home screen + * Assets.backgroundLoadBundle('level-1'); + * + * // When player starts level, load completes faster + * await Assets.loadBundle('level-1'); + * hideHomeScreen(); + * startLevel(); + * ``` + * @remarks + * - Bundle assets are loaded one at a time + * - Loading can be interrupted safely by calling `Assets.loadBundle()` + * - Assets are cached as they complete loading + * - Requires bundles to be registered via manifest or `addBundle` + * @see {@link Assets.addBundle} For adding bundles programmatically + * @see {@link Assets.loadBundle} For immediate bundle loading + * @see {@link AssetsManifest} For manifest format details + */ + async backgroundLoadBundle(bundleIds) { + if (!this._initialized) { + await this.init(); + } + if (typeof bundleIds === "string") { + bundleIds = [bundleIds]; + } + const resolveResults = this.resolver.resolveBundle(bundleIds); + Object.values(resolveResults).forEach((resolveResult) => { + this._backgroundLoader.add(Object.values(resolveResult)); + }); + } + /** + * Only intended for development purposes. + * This will wipe the resolver and caches. + * You will need to reinitialize the Asset + * @internal + */ + reset() { + this.resolver.reset(); + this.loader.reset(); + this.cache.reset(); + this._initialized = false; + } + get(keys) { + if (typeof keys === "string") { + return Cache.get(keys); + } + const assets = {}; + for (let i = 0; i < keys.length; i++) { + assets[i] = Cache.get(keys[i]); + } + return assets; + } + /** + * helper function to map resolved assets back to loaded assets + * @param resolveResults - the resolve results from the resolver + * @param progressOrLoadOptions - the progress callback or load options + */ + async _mapLoadToResolve(resolveResults, progressOrLoadOptions) { + const resolveArray = [...new Set(Object.values(resolveResults))]; + this._backgroundLoader.active = false; + const loadedAssets = await this.loader.load(resolveArray, progressOrLoadOptions); + this._backgroundLoader.active = true; + const out = {}; + resolveArray.forEach((resolveResult) => { + const asset = loadedAssets[resolveResult.src]; + const keys = [resolveResult.src]; + if (resolveResult.alias) { + keys.push(...resolveResult.alias); + } + keys.forEach((key) => { + out[key] = asset; + }); + Cache.set(keys, asset); + }); + return out; + } + /** + * Unloads assets and releases them from memory. This method ensures proper cleanup of + * loaded assets when they're no longer needed. + * @param urls - Single URL/alias or array of URLs/aliases to unload + * @example + * ```ts + * // Unload a single asset + * await Assets.unload('images/sprite.png'); + * + * // Unload using an alias + * await Assets.unload('hero'); // Unloads the asset registered with 'hero' alias + * + * // Unload multiple assets + * await Assets.unload([ + * 'images/background.png', + * 'images/character.png', + * 'hero' + * ]); + * + * // Unload and handle creation of new instances + * await Assets.unload('hero'); + * const newHero = await Assets.load('hero'); // Will load fresh from source + * ``` + * @remarks + * > [!WARNING] + * > Make sure assets aren't being used before unloading: + * > - Remove sprites using the texture + * > - Clear any references to the asset + * > - Textures will be destroyed and can't be used after unloading + * @throws {Error} If the asset is not found in cache + */ + async unload(urls) { + if (!this._initialized) { + await this.init(); + } + const urlArray = convertToList(urls).map((url) => typeof url !== "string" ? url.src : url); + const resolveResults = this.resolver.resolve(urlArray); + await this._unloadFromResolved(resolveResults); + } + /** + * Unloads all assets in a bundle. Use this to free memory when a bundle's assets + * are no longer needed, such as when switching game levels. + * @param bundleIds - Single bundle ID or array of bundle IDs to unload + * @example + * ```ts + * // Define and load a bundle + * Assets.addBundle('level-1', { + * background: 'level1/bg.png', + * sprites: 'level1/sprites.json', + * music: 'level1/music.mp3' + * }); + * + * // Load the bundle + * const level1 = await Assets.loadBundle('level-1'); + * + * // Use the assets + * const background = Sprite.from(level1.background); + * + * // When done with the level, unload everything + * await Assets.unloadBundle('level-1'); + * // background sprite is now invalid! + * + * // Unload multiple bundles + * await Assets.unloadBundle([ + * 'level-1', + * 'level-2', + * 'ui-elements' + * ]); + * ``` + * @remarks + * > [!WARNING] + * > - All assets in the bundle will be destroyed + * > - Bundle needs to be reloaded to use assets again + * > - Make sure no sprites or other objects are using the assets + * @throws {Error} If the bundle is not found + * @see {@link Assets.addBundle} For adding bundles + * @see {@link Assets.loadBundle} For loading bundles + */ + async unloadBundle(bundleIds) { + if (!this._initialized) { + await this.init(); + } + bundleIds = convertToList(bundleIds); + const resolveResults = this.resolver.resolveBundle(bundleIds); + const promises = Object.keys(resolveResults).map((bundleId) => this._unloadFromResolved(resolveResults[bundleId])); + await Promise.all(promises); + } + async _unloadFromResolved(resolveResult) { + const resolveArray = Object.values(resolveResult); + resolveArray.forEach((resolveResult2) => { + Cache.remove(resolveResult2.src); + }); + await this.loader.unload(resolveArray); + } + /** + * Detects the supported formats for the browser, and returns an array of supported formats, respecting + * the users preferred formats order. + * @param options - the options to use when detecting formats + * @param options.preferredFormats - the preferred formats to use + * @param options.skipDetections - if we should skip the detections altogether + * @param options.detections - the detections to use + * @returns - the detected formats + */ + async _detectFormats(options) { + let formats = []; + if (options.preferredFormats) { + formats = Array.isArray(options.preferredFormats) ? options.preferredFormats : [options.preferredFormats]; + } + for (const detection of options.detections) { + if (options.skipDetections || await detection.test()) { + formats = await detection.add(formats); + } else if (!options.skipDetections) { + formats = await detection.remove(formats); + } + } + formats = formats.filter((format, index) => formats.indexOf(format) === index); + return formats; + } + /** + * All the detection parsers currently added to the Assets class. + * @advanced + */ + get detections() { + return this._detections; + } + /** + * Sets global preferences for asset loading behavior. This method configures how assets + * are loaded and processed across all parsers. + * @param preferences - Asset loading preferences + * @example + * ```ts + * // Basic preferences + * Assets.setPreferences({ + * crossOrigin: 'anonymous', + * parseAsGraphicsContext: false + * }); + * ``` + * @remarks + * Preferences are applied to all compatible parsers and affect future asset loading. + * Common preferences include: + * - `crossOrigin`: CORS setting for loaded assets + * - `preferWorkers`: Whether to use web workers for loading textures + * - `preferCreateImageBitmap`: Use `createImageBitmap` for texture creation. Turning this off will use the `Image` constructor instead. + * @see {@link AssetsPreferences} For all available preferences + */ + setPreferences(preferences) { + this.loader.parsers.forEach((parser) => { + if (!parser.config) return; + Object.keys(parser.config).filter((key) => key in preferences).forEach((key) => { + parser.config[key] = preferences[key]; + }); + }); + } + } + const Assets = new AssetsClass(); + extensions.handleByList(ExtensionType.LoadParser, Assets.loader.parsers).handleByList(ExtensionType.ResolveParser, Assets.resolver.parsers).handleByList(ExtensionType.CacheParser, Assets.cache.parsers).handleByList(ExtensionType.DetectionParser, Assets.detections); + extensions.add( + cacheTextureArray, + detectDefaults, + detectAvif, + detectWebp, + detectMp4, + detectOgv, + detectWebm, + loadJson, + loadTxt, + loadWebFont, + loadSvg, + loadTextures, + loadVideoTextures, + loadBitmapFont, + bitmapFontCachePlugin, + resolveTextureUrl, + resolveJsonUrl + ); + const assetKeyMap = { + loader: ExtensionType.LoadParser, + resolver: ExtensionType.ResolveParser, + cache: ExtensionType.CacheParser, + detection: ExtensionType.DetectionParser + }; + extensions.handle(ExtensionType.Asset, (extension) => { + const ref = extension.ref; + Object.entries(assetKeyMap).filter(([key]) => !!ref[key]).forEach(([key, type]) => { + var _a; + return extensions.add(Object.assign( + ref[key], + // Allow the function to optionally define it's own + // ExtensionMetadata, the use cases here is priority for LoaderParsers + { extension: (_a = ref[key].extension) != null ? _a : type } + )); + }); + }, (extension) => { + const ref = extension.ref; + Object.keys(assetKeyMap).filter((key) => !!ref[key]).forEach((key) => extensions.remove(ref[key])); + }); + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + const detectBasis = { + extension: { + type: ExtensionType.DetectionParser, + priority: 3 + }, + test: async () => { + if (await isWebGPUSupported()) return true; + if (isWebGLSupported()) return true; + return false; + }, + add: async (formats) => [...formats, "basis"], + remove: async (formats) => formats.filter((f) => f !== "basis") + }; + + "use strict"; + var __defProp$S = Object.defineProperty; + var __defProps$q = Object.defineProperties; + var __getOwnPropDescs$q = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$U = Object.getOwnPropertySymbols; + var __hasOwnProp$U = Object.prototype.hasOwnProperty; + var __propIsEnum$U = Object.prototype.propertyIsEnumerable; + var __defNormalProp$S = (obj, key, value) => key in obj ? __defProp$S(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$S = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$U.call(b, prop)) + __defNormalProp$S(a, prop, b[prop]); + if (__getOwnPropSymbols$U) + for (var prop of __getOwnPropSymbols$U(b)) { + if (__propIsEnum$U.call(b, prop)) + __defNormalProp$S(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$q = (a, b) => __defProps$q(a, __getOwnPropDescs$q(b)); + class CompressedSource extends TextureSource { + constructor(options) { + super(__spreadProps$q(__spreadValues$S({}, options), { + mipLevelCount: options.resource.length + })); + this.uploadMethodId = "compressed"; + } + } + + "use strict"; + let supportedGLCompressedTextureFormats; + function getSupportedGlCompressedTextureFormats() { + if (supportedGLCompressedTextureFormats) return supportedGLCompressedTextureFormats; + const canvas = DOMAdapter.get().createCanvas(1, 1); + const gl = canvas.getContext("webgl"); + if (!gl) { + return []; + } + supportedGLCompressedTextureFormats = [ + // BC compressed formats usable if "texture-compression-bc" is both + // supported by the device/user agent and enabled in requestDevice. + // 'bc6h-rgb-ufloat' + // 'bc6h-rgb-float' + // 'bc7-rgba-unorm', + // 'bc7-rgba-unorm-srgb', + ...gl.getExtension("EXT_texture_compression_bptc") ? [ + "bc6h-rgb-ufloat", + "bc6h-rgb-float", + "bc7-rgba-unorm", + "bc7-rgba-unorm-srgb" + ] : [], + // BC compressed formats usable if "texture-compression-bc" is both + // supported by the device/user agent and enabled in requestDevice. + // 'bc1-rgba-unorm', + // 'bc1-rgba-unorm-srgb', + // 'bc4-r-unorm' + // 'bc4-r-snorm' + // 'bc5-rg-unorm' + // 'bc5-rg-snorm' + ...gl.getExtension("WEBGL_compressed_texture_s3tc") ? [ + "bc1-rgba-unorm", + "bc2-rgba-unorm", + "bc3-rgba-unorm" + ] : [], + ...gl.getExtension("WEBGL_compressed_texture_s3tc_srgb") ? [ + "bc1-rgba-unorm-srgb", + "bc2-rgba-unorm-srgb", + "bc3-rgba-unorm-srgb" + ] : [], + ...gl.getExtension("EXT_texture_compression_rgtc") ? [ + "bc4-r-unorm", + "bc4-r-snorm", + "bc5-rg-unorm", + "bc5-rg-snorm" + ] : [], + // ETC2 compressed formats usable if "texture-compression-etc2" is both + // supported by the device/user agent and enabled in requestDevice. + ...gl.getExtension("WEBGL_compressed_texture_etc") ? [ + "etc2-rgb8unorm", + "etc2-rgb8unorm-srgb", + "etc2-rgba8unorm", + "etc2-rgba8unorm-srgb", + "etc2-rgb8a1unorm", + "etc2-rgb8a1unorm-srgb", + "eac-r11unorm", + "eac-rg11unorm" + ] : [], + // 'eac-r11snorm', + // 'eac-rg11snorm', + // ASTC compressed formats usable if "texture-compression-astc" is both + // supported by the device/user agent and enabled in requestDevice. + ...gl.getExtension("WEBGL_compressed_texture_astc") ? [ + "astc-4x4-unorm", + "astc-4x4-unorm-srgb", + "astc-5x4-unorm", + "astc-5x4-unorm-srgb", + "astc-5x5-unorm", + "astc-5x5-unorm-srgb", + "astc-6x5-unorm", + "astc-6x5-unorm-srgb", + "astc-6x6-unorm", + "astc-6x6-unorm-srgb", + "astc-8x5-unorm", + "astc-8x5-unorm-srgb", + "astc-8x6-unorm", + "astc-8x6-unorm-srgb", + "astc-8x8-unorm", + "astc-8x8-unorm-srgb", + "astc-10x5-unorm", + "astc-10x5-unorm-srgb", + "astc-10x6-unorm", + "astc-10x6-unorm-srgb", + "astc-10x8-unorm", + "astc-10x8-unorm-srgb", + "astc-10x10-unorm", + "astc-10x10-unorm-srgb", + "astc-12x10-unorm", + "astc-12x10-unorm-srgb", + "astc-12x12-unorm", + "astc-12x12-unorm-srgb" + ] : [] + ]; + return supportedGLCompressedTextureFormats; + } + + "use strict"; + let supportedGPUCompressedTextureFormats; + async function getSupportedGPUCompressedTextureFormats() { + if (supportedGPUCompressedTextureFormats) return supportedGPUCompressedTextureFormats; + const adapter = await DOMAdapter.get().getNavigator().gpu.requestAdapter(); + supportedGPUCompressedTextureFormats = [ + ...adapter.features.has("texture-compression-bc") ? [ + // BC compressed formats usable if "texture-compression-bc" is both + // supported by the device/user agent and enabled in requestDevice. + "bc1-rgba-unorm", + "bc1-rgba-unorm-srgb", + "bc2-rgba-unorm", + "bc2-rgba-unorm-srgb", + "bc3-rgba-unorm", + "bc3-rgba-unorm-srgb", + "bc4-r-unorm", + "bc4-r-snorm", + "bc5-rg-unorm", + "bc5-rg-snorm", + "bc6h-rgb-ufloat", + "bc6h-rgb-float", + "bc7-rgba-unorm", + "bc7-rgba-unorm-srgb" + ] : [], + ...adapter.features.has("texture-compression-etc2") ? [ + // ETC2 compressed formats usable if "texture-compression-etc2" is both + // supported by the device/user agent and enabled in requestDevice. + "etc2-rgb8unorm", + "etc2-rgb8unorm-srgb", + "etc2-rgb8a1unorm", + "etc2-rgb8a1unorm-srgb", + "etc2-rgba8unorm", + "etc2-rgba8unorm-srgb", + "eac-r11unorm", + "eac-r11snorm", + "eac-rg11unorm", + "eac-rg11snorm" + ] : [], + ...adapter.features.has("texture-compression-astc") ? [ + // ASTC compressed formats usable if "texture-compression-astc" is both + // supported by the device/user agent and enabled in requestDevice. + "astc-4x4-unorm", + "astc-4x4-unorm-srgb", + "astc-5x4-unorm", + "astc-5x4-unorm-srgb", + "astc-5x5-unorm", + "astc-5x5-unorm-srgb", + "astc-6x5-unorm", + "astc-6x5-unorm-srgb", + "astc-6x6-unorm", + "astc-6x6-unorm-srgb", + "astc-8x5-unorm", + "astc-8x5-unorm-srgb", + "astc-8x6-unorm", + "astc-8x6-unorm-srgb", + "astc-8x8-unorm", + "astc-8x8-unorm-srgb", + "astc-10x5-unorm", + "astc-10x5-unorm-srgb", + "astc-10x6-unorm", + "astc-10x6-unorm-srgb", + "astc-10x8-unorm", + "astc-10x8-unorm-srgb", + "astc-10x10-unorm", + "astc-10x10-unorm-srgb", + "astc-12x10-unorm", + "astc-12x10-unorm-srgb", + "astc-12x12-unorm", + "astc-12x12-unorm-srgb" + ] : [] + ]; + return supportedGPUCompressedTextureFormats; + } + + "use strict"; + let supportedCompressedTextureFormats; + async function getSupportedCompressedTextureFormats() { + if (supportedCompressedTextureFormats !== void 0) return supportedCompressedTextureFormats; + supportedCompressedTextureFormats = await (async () => { + const _isWebGPUSupported = await isWebGPUSupported(); + const _isWebGLSupported = isWebGLSupported(); + if (_isWebGPUSupported && _isWebGLSupported) { + const gpuTextureFormats = await getSupportedGPUCompressedTextureFormats(); + const glTextureFormats = getSupportedGlCompressedTextureFormats(); + return gpuTextureFormats.filter((format) => glTextureFormats.includes(format)); + } else if (_isWebGPUSupported) { + return await getSupportedGPUCompressedTextureFormats(); + } else if (_isWebGLSupported) { + return getSupportedGlCompressedTextureFormats(); + } + return []; + })(); + return supportedCompressedTextureFormats; + } + + "use strict"; + const nonCompressedFormats = [ + // 8-bit formats + "r8unorm", + "r8snorm", + "r8uint", + "r8sint", + // 16-bit formats + "r16uint", + "r16sint", + "r16float", + "rg8unorm", + "rg8snorm", + "rg8uint", + "rg8sint", + // 32-bit formats + "r32uint", + "r32sint", + "r32float", + "rg16uint", + "rg16sint", + "rg16float", + "rgba8unorm", + "rgba8unorm-srgb", + "rgba8snorm", + "rgba8uint", + "rgba8sint", + "bgra8unorm", + "bgra8unorm-srgb", + // Packed 32-bit formats + "rgb9e5ufloat", + "rgb10a2unorm", + "rg11b10ufloat", + // 64-bit formats + "rg32uint", + "rg32sint", + "rg32float", + "rgba16uint", + "rgba16sint", + "rgba16float", + // 128-bit formats + "rgba32uint", + "rgba32sint", + "rgba32float", + // Depth/stencil formats + "stencil8", + "depth16unorm", + "depth24plus", + "depth24plus-stencil8", + "depth32float", + // "depth32float-stencil8" feature + "depth32float-stencil8" + ]; + let supportedTextureFormats; + async function getSupportedTextureFormats() { + if (supportedTextureFormats !== void 0) return supportedTextureFormats; + const compressedTextureFormats = await getSupportedCompressedTextureFormats(); + supportedTextureFormats = [ + ...nonCompressedFormats, + ...compressedTextureFormats + ]; + return supportedTextureFormats; + } + + const WORKER_CODE$1 = "(function () {\n 'use strict';\n\n function createLevelBuffers(basisTexture, basisTranscoderFormat) {\n const images = basisTexture.getNumImages();\n const levels = basisTexture.getNumLevels(0);\n const success = basisTexture.startTranscoding();\n if (!success) {\n throw new Error(\"startTranscoding failed\");\n }\n const levelBuffers = [];\n for (let levelIndex = 0; levelIndex < levels; ++levelIndex) {\n for (let sliceIndex = 0; sliceIndex < images; ++sliceIndex) {\n const transcodeSize = basisTexture.getImageTranscodedSizeInBytes(sliceIndex, levelIndex, basisTranscoderFormat);\n const levelBuffer = new Uint8Array(transcodeSize);\n const success2 = basisTexture.transcodeImage(levelBuffer, sliceIndex, levelIndex, basisTranscoderFormat, 1, 0);\n if (!success2) {\n throw new Error(\"transcodeImage failed\");\n }\n levelBuffers.push(levelBuffer);\n }\n }\n return levelBuffers;\n }\n\n const gpuFormatToBasisTranscoderFormatMap = {\n \"bc3-rgba-unorm\": 3,\n // cTFBC3_RGBA\n \"bc7-rgba-unorm\": 6,\n // cTFBC7_RGBA,\n \"etc2-rgba8unorm\": 1,\n // cTFETC2_RGBA,\n \"astc-4x4-unorm\": 10,\n // cTFASTC_4x4_RGBA,\n // Uncompressed\n rgba8unorm: 13,\n // cTFRGBA32,\n rgba4unorm: 16\n // cTFRGBA4444,\n };\n function gpuFormatToBasisTranscoderFormat(transcoderFormat) {\n const format = gpuFormatToBasisTranscoderFormatMap[transcoderFormat];\n if (format) {\n return format;\n }\n throw new Error(`Unsupported transcoderFormat: ${transcoderFormat}`);\n }\n\n const settings = {\n jsUrl: \"basis/basis_transcoder.js\",\n wasmUrl: \"basis/basis_transcoder.wasm\"\n };\n let basisTranscoderFormat;\n let basisTranscodedTextureFormat;\n let basisPromise;\n async function getBasis() {\n if (!basisPromise) {\n const absoluteJsUrl = new URL(settings.jsUrl, location.origin).href;\n const absoluteWasmUrl = new URL(settings.wasmUrl, location.origin).href;\n importScripts(absoluteJsUrl);\n basisPromise = new Promise((resolve) => {\n BASIS({\n locateFile: (_file) => absoluteWasmUrl\n }).then((module) => {\n module.initializeBasis();\n resolve(module.BasisFile);\n });\n });\n }\n return basisPromise;\n }\n async function fetchBasisTexture(url, BasisTexture) {\n const basisResponse = await fetch(url);\n if (basisResponse.ok) {\n const basisArrayBuffer = await basisResponse.arrayBuffer();\n return new BasisTexture(new Uint8Array(basisArrayBuffer));\n }\n throw new Error(`Failed to load Basis texture: ${url}`);\n }\n const preferredTranscodedFormat = [\n \"bc7-rgba-unorm\",\n \"astc-4x4-unorm\",\n \"etc2-rgba8unorm\",\n \"bc3-rgba-unorm\",\n \"rgba8unorm\"\n ];\n async function load(url) {\n const BasisTexture = await getBasis();\n const basisTexture = await fetchBasisTexture(url, BasisTexture);\n const levelBuffers = createLevelBuffers(basisTexture, basisTranscoderFormat);\n return {\n width: basisTexture.getImageWidth(0, 0),\n height: basisTexture.getImageHeight(0, 0),\n format: basisTranscodedTextureFormat,\n resource: levelBuffers,\n alphaMode: \"no-premultiply-alpha\"\n };\n }\n async function init(jsUrl, wasmUrl, supportedTextures) {\n if (jsUrl) settings.jsUrl = jsUrl;\n if (wasmUrl) settings.wasmUrl = wasmUrl;\n basisTranscodedTextureFormat = preferredTranscodedFormat.filter((format) => supportedTextures.includes(format))[0];\n basisTranscoderFormat = gpuFormatToBasisTranscoderFormat(basisTranscodedTextureFormat);\n await getBasis();\n }\n const messageHandlers = {\n init: async (data) => {\n const { jsUrl, wasmUrl, supportedTextures } = data;\n await init(jsUrl, wasmUrl, supportedTextures);\n },\n load: async (data) => {\n var _a;\n try {\n const textureOptions = await load(data.url);\n return {\n type: \"load\",\n url: data.url,\n success: true,\n textureOptions,\n transferables: (_a = textureOptions.resource) == null ? void 0 : _a.map((arr) => arr.buffer)\n };\n } catch (e) {\n throw e;\n }\n }\n };\n self.onmessage = (async (messageEvent) => {\n const message = messageEvent.data;\n const response = await messageHandlers[message.type](message);\n if (response) {\n self.postMessage(response, response.transferables);\n }\n });\n\n})();\n"; + let WORKER_URL$1 = null; + let WorkerInstance$1 = class WorkerInstance + { + constructor() + { + if (!WORKER_URL$1) + { + WORKER_URL$1 = URL.createObjectURL(new Blob([WORKER_CODE$1], { type: 'application/javascript' })); + } + this.worker = new Worker(WORKER_URL$1); + } + }; + WorkerInstance$1.revokeObjectURL = function revokeObjectURL() + { + if (WORKER_URL$1) + { + URL.revokeObjectURL(WORKER_URL$1); + WORKER_URL$1 = null; + } + }; + + "use strict"; + const basisTranscoderUrls = { + jsUrl: "https://cdn.jsdelivr.net/npm/pixi.js/transcoders/basis/basis_transcoder.js", + wasmUrl: "https://cdn.jsdelivr.net/npm/pixi.js/transcoders/basis/basis_transcoder.wasm" + }; + function setBasisTranscoderPath(config) { + Object.assign(basisTranscoderUrls, config); + } + + "use strict"; + let basisWorker; + const urlHash$1 = {}; + function getBasisWorker(supportedTextures) { + if (!basisWorker) { + basisWorker = new WorkerInstance$1().worker; + basisWorker.onmessage = (messageEvent) => { + const { success, url, textureOptions } = messageEvent.data; + if (!success) { + console.warn("Failed to load Basis texture", url); + } + urlHash$1[url](textureOptions); + }; + basisWorker.postMessage({ + type: "init", + jsUrl: basisTranscoderUrls.jsUrl, + wasmUrl: basisTranscoderUrls.wasmUrl, + supportedTextures + }); + } + return basisWorker; + } + function loadBasisOnWorker(url, supportedTextures) { + const ktxWorker = getBasisWorker(supportedTextures); + return new Promise((resolve) => { + urlHash$1[url] = resolve; + ktxWorker.postMessage({ type: "load", url }); + }); + } + + "use strict"; + const loadBasis = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.High, + name: "loadBasis" + }, + /** used for deprecation purposes */ + name: "loadBasis", + id: "basis", + test(url) { + return checkExtension(url, [".basis"]); + }, + async load(url, _asset, loader) { + const supportedTextures = await getSupportedTextureFormats(); + const textureOptions = await loadBasisOnWorker(url, supportedTextures); + const compressedTextureSource = new CompressedSource(textureOptions); + return createTexture(compressedTextureSource, loader, url); + }, + unload(texture) { + if (Array.isArray(texture)) { + texture.forEach((t) => t.destroy(true)); + } else { + texture.destroy(true); + } + } + }; + + "use strict"; + + "use strict"; + function createLevelBuffers(basisTexture, basisTranscoderFormat) { + const images = basisTexture.getNumImages(); + const levels = basisTexture.getNumLevels(0); + const success = basisTexture.startTranscoding(); + if (!success) { + throw new Error("startTranscoding failed"); + } + const levelBuffers = []; + for (let levelIndex = 0; levelIndex < levels; ++levelIndex) { + for (let sliceIndex = 0; sliceIndex < images; ++sliceIndex) { + const transcodeSize = basisTexture.getImageTranscodedSizeInBytes(sliceIndex, levelIndex, basisTranscoderFormat); + const levelBuffer = new Uint8Array(transcodeSize); + const success2 = basisTexture.transcodeImage(levelBuffer, sliceIndex, levelIndex, basisTranscoderFormat, 1, 0); + if (!success2) { + throw new Error("transcodeImage failed"); + } + levelBuffers.push(levelBuffer); + } + } + return levelBuffers; + } + + "use strict"; + const gpuFormatToBasisTranscoderFormatMap$1 = { + "bc3-rgba-unorm": 3, + // cTFBC3_RGBA + "bc7-rgba-unorm": 6, + // cTFBC7_RGBA, + "etc2-rgba8unorm": 1, + // cTFETC2_RGBA, + "astc-4x4-unorm": 10, + // cTFASTC_4x4_RGBA, + // Uncompressed + rgba8unorm: 13, + // cTFRGBA32, + rgba4unorm: 16 + // cTFRGBA4444, + }; + function gpuFormatToBasisTranscoderFormat(transcoderFormat) { + const format = gpuFormatToBasisTranscoderFormatMap$1[transcoderFormat]; + if (format) { + return format; + } + throw new Error(`Unsupported transcoderFormat: ${transcoderFormat}`); + } + + "use strict"; + const DDS_HEADER_FIELDS = { + MAGIC: 0, + SIZE: 1, + FLAGS: 2, + HEIGHT: 3, + WIDTH: 4, + MIPMAP_COUNT: 7, + PIXEL_FORMAT: 19, + PF_FLAGS: 20, + FOURCC: 21, + RGB_BITCOUNT: 22, + R_BIT_MASK: 23, + G_BIT_MASK: 24, + B_BIT_MASK: 25, + A_BIT_MASK: 26 + }; + const DDS_DX10_FIELDS = { + DXGI_FORMAT: 0, + RESOURCE_DIMENSION: 1, + MISC_FLAG: 2, + ARRAY_SIZE: 3, + MISC_FLAGS2: 4 + }; + var DXGI_FORMAT = /* @__PURE__ */ ((DXGI_FORMAT2) => { + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_UNKNOWN"] = 0] = "DXGI_FORMAT_UNKNOWN"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32A32_TYPELESS"] = 1] = "DXGI_FORMAT_R32G32B32A32_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32A32_FLOAT"] = 2] = "DXGI_FORMAT_R32G32B32A32_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32A32_UINT"] = 3] = "DXGI_FORMAT_R32G32B32A32_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32A32_SINT"] = 4] = "DXGI_FORMAT_R32G32B32A32_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32_TYPELESS"] = 5] = "DXGI_FORMAT_R32G32B32_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32_FLOAT"] = 6] = "DXGI_FORMAT_R32G32B32_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32_UINT"] = 7] = "DXGI_FORMAT_R32G32B32_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32B32_SINT"] = 8] = "DXGI_FORMAT_R32G32B32_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_TYPELESS"] = 9] = "DXGI_FORMAT_R16G16B16A16_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_FLOAT"] = 10] = "DXGI_FORMAT_R16G16B16A16_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_UNORM"] = 11] = "DXGI_FORMAT_R16G16B16A16_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_UINT"] = 12] = "DXGI_FORMAT_R16G16B16A16_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_SNORM"] = 13] = "DXGI_FORMAT_R16G16B16A16_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16B16A16_SINT"] = 14] = "DXGI_FORMAT_R16G16B16A16_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32_TYPELESS"] = 15] = "DXGI_FORMAT_R32G32_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32_FLOAT"] = 16] = "DXGI_FORMAT_R32G32_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32_UINT"] = 17] = "DXGI_FORMAT_R32G32_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G32_SINT"] = 18] = "DXGI_FORMAT_R32G32_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32G8X24_TYPELESS"] = 19] = "DXGI_FORMAT_R32G8X24_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_D32_FLOAT_S8X24_UINT"] = 20] = "DXGI_FORMAT_D32_FLOAT_S8X24_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS"] = 21] = "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_X32_TYPELESS_G8X24_UINT"] = 22] = "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R10G10B10A2_TYPELESS"] = 23] = "DXGI_FORMAT_R10G10B10A2_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R10G10B10A2_UNORM"] = 24] = "DXGI_FORMAT_R10G10B10A2_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R10G10B10A2_UINT"] = 25] = "DXGI_FORMAT_R10G10B10A2_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R11G11B10_FLOAT"] = 26] = "DXGI_FORMAT_R11G11B10_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_TYPELESS"] = 27] = "DXGI_FORMAT_R8G8B8A8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_UNORM"] = 28] = "DXGI_FORMAT_R8G8B8A8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"] = 29] = "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_UINT"] = 30] = "DXGI_FORMAT_R8G8B8A8_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_SNORM"] = 31] = "DXGI_FORMAT_R8G8B8A8_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8B8A8_SINT"] = 32] = "DXGI_FORMAT_R8G8B8A8_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_TYPELESS"] = 33] = "DXGI_FORMAT_R16G16_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_FLOAT"] = 34] = "DXGI_FORMAT_R16G16_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_UNORM"] = 35] = "DXGI_FORMAT_R16G16_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_UINT"] = 36] = "DXGI_FORMAT_R16G16_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_SNORM"] = 37] = "DXGI_FORMAT_R16G16_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16G16_SINT"] = 38] = "DXGI_FORMAT_R16G16_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32_TYPELESS"] = 39] = "DXGI_FORMAT_R32_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_D32_FLOAT"] = 40] = "DXGI_FORMAT_D32_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32_FLOAT"] = 41] = "DXGI_FORMAT_R32_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32_UINT"] = 42] = "DXGI_FORMAT_R32_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R32_SINT"] = 43] = "DXGI_FORMAT_R32_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R24G8_TYPELESS"] = 44] = "DXGI_FORMAT_R24G8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_D24_UNORM_S8_UINT"] = 45] = "DXGI_FORMAT_D24_UNORM_S8_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R24_UNORM_X8_TYPELESS"] = 46] = "DXGI_FORMAT_R24_UNORM_X8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_X24_TYPELESS_G8_UINT"] = 47] = "DXGI_FORMAT_X24_TYPELESS_G8_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_TYPELESS"] = 48] = "DXGI_FORMAT_R8G8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_UNORM"] = 49] = "DXGI_FORMAT_R8G8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_UINT"] = 50] = "DXGI_FORMAT_R8G8_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_SNORM"] = 51] = "DXGI_FORMAT_R8G8_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_SINT"] = 52] = "DXGI_FORMAT_R8G8_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_TYPELESS"] = 53] = "DXGI_FORMAT_R16_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_FLOAT"] = 54] = "DXGI_FORMAT_R16_FLOAT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_D16_UNORM"] = 55] = "DXGI_FORMAT_D16_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_UNORM"] = 56] = "DXGI_FORMAT_R16_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_UINT"] = 57] = "DXGI_FORMAT_R16_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_SNORM"] = 58] = "DXGI_FORMAT_R16_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R16_SINT"] = 59] = "DXGI_FORMAT_R16_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8_TYPELESS"] = 60] = "DXGI_FORMAT_R8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8_UNORM"] = 61] = "DXGI_FORMAT_R8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8_UINT"] = 62] = "DXGI_FORMAT_R8_UINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8_SNORM"] = 63] = "DXGI_FORMAT_R8_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8_SINT"] = 64] = "DXGI_FORMAT_R8_SINT"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_A8_UNORM"] = 65] = "DXGI_FORMAT_A8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R1_UNORM"] = 66] = "DXGI_FORMAT_R1_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R9G9B9E5_SHAREDEXP"] = 67] = "DXGI_FORMAT_R9G9B9E5_SHAREDEXP"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R8G8_B8G8_UNORM"] = 68] = "DXGI_FORMAT_R8G8_B8G8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_G8R8_G8B8_UNORM"] = 69] = "DXGI_FORMAT_G8R8_G8B8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC1_TYPELESS"] = 70] = "DXGI_FORMAT_BC1_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC1_UNORM"] = 71] = "DXGI_FORMAT_BC1_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC1_UNORM_SRGB"] = 72] = "DXGI_FORMAT_BC1_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC2_TYPELESS"] = 73] = "DXGI_FORMAT_BC2_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC2_UNORM"] = 74] = "DXGI_FORMAT_BC2_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC2_UNORM_SRGB"] = 75] = "DXGI_FORMAT_BC2_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC3_TYPELESS"] = 76] = "DXGI_FORMAT_BC3_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC3_UNORM"] = 77] = "DXGI_FORMAT_BC3_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC3_UNORM_SRGB"] = 78] = "DXGI_FORMAT_BC3_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC4_TYPELESS"] = 79] = "DXGI_FORMAT_BC4_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC4_UNORM"] = 80] = "DXGI_FORMAT_BC4_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC4_SNORM"] = 81] = "DXGI_FORMAT_BC4_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC5_TYPELESS"] = 82] = "DXGI_FORMAT_BC5_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC5_UNORM"] = 83] = "DXGI_FORMAT_BC5_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC5_SNORM"] = 84] = "DXGI_FORMAT_BC5_SNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B5G6R5_UNORM"] = 85] = "DXGI_FORMAT_B5G6R5_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B5G5R5A1_UNORM"] = 86] = "DXGI_FORMAT_B5G5R5A1_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8A8_UNORM"] = 87] = "DXGI_FORMAT_B8G8R8A8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8X8_UNORM"] = 88] = "DXGI_FORMAT_B8G8R8X8_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM"] = 89] = "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8A8_TYPELESS"] = 90] = "DXGI_FORMAT_B8G8R8A8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"] = 91] = "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8X8_TYPELESS"] = 92] = "DXGI_FORMAT_B8G8R8X8_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"] = 93] = "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC6H_TYPELESS"] = 94] = "DXGI_FORMAT_BC6H_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC6H_UF16"] = 95] = "DXGI_FORMAT_BC6H_UF16"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC6H_SF16"] = 96] = "DXGI_FORMAT_BC6H_SF16"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC7_TYPELESS"] = 97] = "DXGI_FORMAT_BC7_TYPELESS"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC7_UNORM"] = 98] = "DXGI_FORMAT_BC7_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_BC7_UNORM_SRGB"] = 99] = "DXGI_FORMAT_BC7_UNORM_SRGB"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_AYUV"] = 100] = "DXGI_FORMAT_AYUV"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_Y410"] = 101] = "DXGI_FORMAT_Y410"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_Y416"] = 102] = "DXGI_FORMAT_Y416"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_NV12"] = 103] = "DXGI_FORMAT_NV12"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_P010"] = 104] = "DXGI_FORMAT_P010"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_P016"] = 105] = "DXGI_FORMAT_P016"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_420_OPAQUE"] = 106] = "DXGI_FORMAT_420_OPAQUE"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_YUY2"] = 107] = "DXGI_FORMAT_YUY2"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_Y210"] = 108] = "DXGI_FORMAT_Y210"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_Y216"] = 109] = "DXGI_FORMAT_Y216"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_NV11"] = 110] = "DXGI_FORMAT_NV11"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_AI44"] = 111] = "DXGI_FORMAT_AI44"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_IA44"] = 112] = "DXGI_FORMAT_IA44"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_P8"] = 113] = "DXGI_FORMAT_P8"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_A8P8"] = 114] = "DXGI_FORMAT_A8P8"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_B4G4R4A4_UNORM"] = 115] = "DXGI_FORMAT_B4G4R4A4_UNORM"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_P208"] = 116] = "DXGI_FORMAT_P208"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_V208"] = 117] = "DXGI_FORMAT_V208"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_V408"] = 118] = "DXGI_FORMAT_V408"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE"] = 119] = "DXGI_FORMAT_SAMPLER_FEEDBACK_MIN_MIP_OPAQUE"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE"] = 120] = "DXGI_FORMAT_SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE"; + DXGI_FORMAT2[DXGI_FORMAT2["DXGI_FORMAT_FORCE_UINT"] = 121] = "DXGI_FORMAT_FORCE_UINT"; + return DXGI_FORMAT2; + })(DXGI_FORMAT || {}); + var D3D10_RESOURCE_DIMENSION = /* @__PURE__ */ ((D3D10_RESOURCE_DIMENSION2) => { + D3D10_RESOURCE_DIMENSION2[D3D10_RESOURCE_DIMENSION2["DDS_DIMENSION_TEXTURE1D"] = 2] = "DDS_DIMENSION_TEXTURE1D"; + D3D10_RESOURCE_DIMENSION2[D3D10_RESOURCE_DIMENSION2["DDS_DIMENSION_TEXTURE2D"] = 3] = "DDS_DIMENSION_TEXTURE2D"; + D3D10_RESOURCE_DIMENSION2[D3D10_RESOURCE_DIMENSION2["DDS_DIMENSION_TEXTURE3D"] = 6] = "DDS_DIMENSION_TEXTURE3D"; + return D3D10_RESOURCE_DIMENSION2; + })(D3D10_RESOURCE_DIMENSION || {}); + function fourCCToInt32(value) { + return value.charCodeAt(0) + (value.charCodeAt(1) << 8) + (value.charCodeAt(2) << 16) + (value.charCodeAt(3) << 24); + } + var D3DFMT = ((D3DFMT2) => { + D3DFMT2[D3DFMT2["UNKNOWN"] = 0] = "UNKNOWN"; + D3DFMT2[D3DFMT2["R8G8B8"] = 20] = "R8G8B8"; + D3DFMT2[D3DFMT2["A8R8G8B8"] = 21] = "A8R8G8B8"; + D3DFMT2[D3DFMT2["X8R8G8B8"] = 22] = "X8R8G8B8"; + D3DFMT2[D3DFMT2["R5G6B5"] = 23] = "R5G6B5"; + D3DFMT2[D3DFMT2["X1R5G5B5"] = 24] = "X1R5G5B5"; + D3DFMT2[D3DFMT2["A1R5G5B5"] = 25] = "A1R5G5B5"; + D3DFMT2[D3DFMT2["A4R4G4B4"] = 26] = "A4R4G4B4"; + D3DFMT2[D3DFMT2["R3G3B2"] = 27] = "R3G3B2"; + D3DFMT2[D3DFMT2["A8"] = 28] = "A8"; + D3DFMT2[D3DFMT2["A8R3G3B2"] = 29] = "A8R3G3B2"; + D3DFMT2[D3DFMT2["X4R4G4B4"] = 30] = "X4R4G4B4"; + D3DFMT2[D3DFMT2["A2B10G10R10"] = 31] = "A2B10G10R10"; + D3DFMT2[D3DFMT2["A8B8G8R8"] = 32] = "A8B8G8R8"; + D3DFMT2[D3DFMT2["X8B8G8R8"] = 33] = "X8B8G8R8"; + D3DFMT2[D3DFMT2["G16R16"] = 34] = "G16R16"; + D3DFMT2[D3DFMT2["A2R10G10B10"] = 35] = "A2R10G10B10"; + D3DFMT2[D3DFMT2["A16B16G16R16"] = 36] = "A16B16G16R16"; + D3DFMT2[D3DFMT2["A8P8"] = 40] = "A8P8"; + D3DFMT2[D3DFMT2["P8"] = 41] = "P8"; + D3DFMT2[D3DFMT2["L8"] = 50] = "L8"; + D3DFMT2[D3DFMT2["A8L8"] = 51] = "A8L8"; + D3DFMT2[D3DFMT2["A4L4"] = 52] = "A4L4"; + D3DFMT2[D3DFMT2["V8U8"] = 60] = "V8U8"; + D3DFMT2[D3DFMT2["L6V5U5"] = 61] = "L6V5U5"; + D3DFMT2[D3DFMT2["X8L8V8U8"] = 62] = "X8L8V8U8"; + D3DFMT2[D3DFMT2["Q8W8V8U8"] = 63] = "Q8W8V8U8"; + D3DFMT2[D3DFMT2["V16U16"] = 64] = "V16U16"; + D3DFMT2[D3DFMT2["A2W10V10U10"] = 67] = "A2W10V10U10"; + D3DFMT2[D3DFMT2["Q16W16V16U16"] = 110] = "Q16W16V16U16"; + D3DFMT2[D3DFMT2["R16F"] = 111] = "R16F"; + D3DFMT2[D3DFMT2["G16R16F"] = 112] = "G16R16F"; + D3DFMT2[D3DFMT2["A16B16G16R16F"] = 113] = "A16B16G16R16F"; + D3DFMT2[D3DFMT2["R32F"] = 114] = "R32F"; + D3DFMT2[D3DFMT2["G32R32F"] = 115] = "G32R32F"; + D3DFMT2[D3DFMT2["A32B32G32R32F"] = 116] = "A32B32G32R32F"; + D3DFMT2[D3DFMT2["UYVY"] = fourCCToInt32("UYVY")] = "UYVY"; + D3DFMT2[D3DFMT2["R8G8_B8G8"] = fourCCToInt32("RGBG")] = "R8G8_B8G8"; + D3DFMT2[D3DFMT2["YUY2"] = fourCCToInt32("YUY2")] = "YUY2"; + D3DFMT2[D3DFMT2["D3DFMT_G8R8_G8B8"] = fourCCToInt32("GRGB")] = "D3DFMT_G8R8_G8B8"; + D3DFMT2[D3DFMT2["DXT1"] = fourCCToInt32("DXT1")] = "DXT1"; + D3DFMT2[D3DFMT2["DXT2"] = fourCCToInt32("DXT2")] = "DXT2"; + D3DFMT2[D3DFMT2["DXT3"] = fourCCToInt32("DXT3")] = "DXT3"; + D3DFMT2[D3DFMT2["DXT4"] = fourCCToInt32("DXT4")] = "DXT4"; + D3DFMT2[D3DFMT2["DXT5"] = fourCCToInt32("DXT5")] = "DXT5"; + D3DFMT2[D3DFMT2["ATI1"] = fourCCToInt32("ATI1")] = "ATI1"; + D3DFMT2[D3DFMT2["AT1N"] = fourCCToInt32("AT1N")] = "AT1N"; + D3DFMT2[D3DFMT2["ATI2"] = fourCCToInt32("ATI2")] = "ATI2"; + D3DFMT2[D3DFMT2["AT2N"] = fourCCToInt32("AT2N")] = "AT2N"; + D3DFMT2[D3DFMT2["BC4U"] = fourCCToInt32("BC4U")] = "BC4U"; + D3DFMT2[D3DFMT2["BC4S"] = fourCCToInt32("BC4S")] = "BC4S"; + D3DFMT2[D3DFMT2["BC5U"] = fourCCToInt32("BC5U")] = "BC5U"; + D3DFMT2[D3DFMT2["BC5S"] = fourCCToInt32("BC5S")] = "BC5S"; + D3DFMT2[D3DFMT2["DX10"] = fourCCToInt32("DX10")] = "DX10"; + return D3DFMT2; + })(D3DFMT || {}); + const FOURCC_TO_TEXTURE_FORMAT = { + [D3DFMT.DXT1]: "bc1-rgba-unorm", + [D3DFMT.DXT2]: "bc2-rgba-unorm", + [D3DFMT.DXT3]: "bc2-rgba-unorm", + [D3DFMT.DXT4]: "bc3-rgba-unorm", + [D3DFMT.DXT5]: "bc3-rgba-unorm", + [D3DFMT.ATI1]: "bc4-r-unorm", + [D3DFMT.BC4U]: "bc4-r-unorm", + [D3DFMT.BC4S]: "bc4-r-snorm", + [D3DFMT.ATI2]: "bc5-rg-unorm", + [D3DFMT.BC5U]: "bc5-rg-unorm", + [D3DFMT.BC5S]: "bc5-rg-snorm", + [36 /* A16B16G16R16 */]: "rgba16uint", + [110 /* Q16W16V16U16 */]: "rgba16sint", + [111 /* R16F */]: "r16float", + [112 /* G16R16F */]: "rg16float", + [113 /* A16B16G16R16F */]: "rgba16float", + [114 /* R32F */]: "r32float", + [115 /* G32R32F */]: "rg32float", + [116 /* A32B32G32R32F */]: "rgba32float" + }; + const DXGI_TO_TEXTURE_FORMAT = { + [70 /* DXGI_FORMAT_BC1_TYPELESS */]: "bc1-rgba-unorm", + [71 /* DXGI_FORMAT_BC1_UNORM */]: "bc1-rgba-unorm", + [72 /* DXGI_FORMAT_BC1_UNORM_SRGB */]: "bc1-rgba-unorm-srgb", + [73 /* DXGI_FORMAT_BC2_TYPELESS */]: "bc2-rgba-unorm", + [74 /* DXGI_FORMAT_BC2_UNORM */]: "bc2-rgba-unorm", + [75 /* DXGI_FORMAT_BC2_UNORM_SRGB */]: "bc2-rgba-unorm-srgb", + [76 /* DXGI_FORMAT_BC3_TYPELESS */]: "bc3-rgba-unorm", + [77 /* DXGI_FORMAT_BC3_UNORM */]: "bc3-rgba-unorm", + [78 /* DXGI_FORMAT_BC3_UNORM_SRGB */]: "bc3-rgba-unorm-srgb", + [79 /* DXGI_FORMAT_BC4_TYPELESS */]: "bc4-r-unorm", + [80 /* DXGI_FORMAT_BC4_UNORM */]: "bc4-r-unorm", + [81 /* DXGI_FORMAT_BC4_SNORM */]: "bc4-r-snorm", + [82 /* DXGI_FORMAT_BC5_TYPELESS */]: "bc5-rg-unorm", + [83 /* DXGI_FORMAT_BC5_UNORM */]: "bc5-rg-unorm", + [84 /* DXGI_FORMAT_BC5_SNORM */]: "bc5-rg-snorm", + [94 /* DXGI_FORMAT_BC6H_TYPELESS */]: "bc6h-rgb-ufloat", + [95 /* DXGI_FORMAT_BC6H_UF16 */]: "bc6h-rgb-ufloat", + [96 /* DXGI_FORMAT_BC6H_SF16 */]: "bc6h-rgb-float", + [97 /* DXGI_FORMAT_BC7_TYPELESS */]: "bc7-rgba-unorm", + [98 /* DXGI_FORMAT_BC7_UNORM */]: "bc7-rgba-unorm", + [99 /* DXGI_FORMAT_BC7_UNORM_SRGB */]: "bc7-rgba-unorm-srgb", + [28 /* DXGI_FORMAT_R8G8B8A8_UNORM */]: "rgba8unorm", + [29 /* DXGI_FORMAT_R8G8B8A8_UNORM_SRGB */]: "rgba8unorm-srgb", + [87 /* DXGI_FORMAT_B8G8R8A8_UNORM */]: "bgra8unorm", + [91 /* DXGI_FORMAT_B8G8R8A8_UNORM_SRGB */]: "bgra8unorm-srgb", + [41 /* DXGI_FORMAT_R32_FLOAT */]: "r32float", + [49 /* DXGI_FORMAT_R8G8_UNORM */]: "rg8unorm", + [56 /* DXGI_FORMAT_R16_UNORM */]: "r16uint", + [61 /* DXGI_FORMAT_R8_UNORM */]: "r8unorm", + [24 /* DXGI_FORMAT_R10G10B10A2_UNORM */]: "rgb10a2unorm", + [11 /* DXGI_FORMAT_R16G16B16A16_UNORM */]: "rgba16uint", + [13 /* DXGI_FORMAT_R16G16B16A16_SNORM */]: "rgba16sint", + [10 /* DXGI_FORMAT_R16G16B16A16_FLOAT */]: "rgba16float", + [54 /* DXGI_FORMAT_R16_FLOAT */]: "r16float", + [34 /* DXGI_FORMAT_R16G16_FLOAT */]: "rg16float", + [16 /* DXGI_FORMAT_R32G32_FLOAT */]: "rg32float", + [2 /* DXGI_FORMAT_R32G32B32A32_FLOAT */]: "rgba32float" + }; + const DDS = { + MAGIC_VALUE: 542327876, + MAGIC_SIZE: 4, + HEADER_SIZE: 124, + HEADER_DX10_SIZE: 20, + PIXEL_FORMAT_FLAGS: { + // PIXEL_FORMAT flags + // https://github.com/Microsoft/DirectXTex/blob/main/DirectXTex/DDS.h + // https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-pixelformat + ALPHAPIXELS: 1, + ALPHA: 2, + FOURCC: 4, + RGB: 64, + RGBA: 65, + YUV: 512, + LUMINANCE: 131072, + LUMINANCEA: 131073 + }, + RESOURCE_MISC_TEXTURECUBE: 4, + HEADER_FIELDS: DDS_HEADER_FIELDS, + HEADER_DX10_FIELDS: DDS_DX10_FIELDS, + DXGI_FORMAT, + D3D10_RESOURCE_DIMENSION, + D3DFMT + }; + const TEXTURE_FORMAT_BLOCK_SIZE = { + "bc1-rgba-unorm": 8, + "bc1-rgba-unorm-srgb": 8, + "bc2-rgba-unorm": 16, + "bc2-rgba-unorm-srgb": 16, + "bc3-rgba-unorm": 16, + "bc3-rgba-unorm-srgb": 16, + "bc4-r-unorm": 8, + "bc4-r-snorm": 8, + "bc5-rg-unorm": 16, + "bc5-rg-snorm": 16, + "bc6h-rgb-ufloat": 16, + "bc6h-rgb-float": 16, + "bc7-rgba-unorm": 16, + "bc7-rgba-unorm-srgb": 16 + }; + + "use strict"; + function parseDDS(arrayBuffer, supportedFormats) { + const { + format, + fourCC, + width, + height, + dataOffset, + mipmapCount + } = parseDDSHeader(arrayBuffer); + if (!supportedFormats.includes(format)) { + throw new Error(`Unsupported texture format: ${fourCC} ${format}, supported: ${supportedFormats}`); + } + if (mipmapCount <= 1) { + return { + format, + width, + height, + resource: [new Uint8Array(arrayBuffer, dataOffset)], + alphaMode: "no-premultiply-alpha" + }; + } + const levelBuffers = getMipmapLevelBuffers(format, width, height, dataOffset, mipmapCount, arrayBuffer); + const textureOptions = { + format, + width, + height, + resource: levelBuffers, + alphaMode: "no-premultiply-alpha" + }; + return textureOptions; + } + function getMipmapLevelBuffers(format, width, height, dataOffset, mipmapCount, arrayBuffer) { + const levelBuffers = []; + const blockBytes = TEXTURE_FORMAT_BLOCK_SIZE[format]; + let mipWidth = width; + let mipHeight = height; + let offset = dataOffset; + for (let level = 0; level < mipmapCount; ++level) { + const alignedWidth = Math.ceil(Math.max(4, mipWidth) / 4) * 4; + const alignedHeight = Math.ceil(Math.max(4, mipHeight) / 4) * 4; + const byteLength = blockBytes ? alignedWidth / 4 * alignedHeight / 4 * blockBytes : mipWidth * mipHeight * 4; + const levelBuffer = new Uint8Array(arrayBuffer, offset, byteLength); + levelBuffers.push(levelBuffer); + offset += byteLength; + mipWidth = Math.max(mipWidth >> 1, 1); + mipHeight = Math.max(mipHeight >> 1, 1); + } + return levelBuffers; + } + function parseDDSHeader(buffer) { + const header = new Uint32Array(buffer, 0, DDS.HEADER_SIZE / Uint32Array.BYTES_PER_ELEMENT); + if (header[DDS.HEADER_FIELDS.MAGIC] !== DDS.MAGIC_VALUE) { + throw new Error("Invalid magic number in DDS header"); + } + const height = header[DDS.HEADER_FIELDS.HEIGHT]; + const width = header[DDS.HEADER_FIELDS.WIDTH]; + const mipmapCount = Math.max(1, header[DDS.HEADER_FIELDS.MIPMAP_COUNT]); + const flags = header[DDS.HEADER_FIELDS.PF_FLAGS]; + const fourCC = header[DDS.HEADER_FIELDS.FOURCC]; + const format = getTextureFormat(header, flags, fourCC, buffer); + const dataOffset = DDS.MAGIC_SIZE + DDS.HEADER_SIZE + (fourCC === DDS.D3DFMT.DX10 ? DDS.HEADER_DX10_SIZE : 0); + return { + format, + fourCC, + width, + height, + dataOffset, + mipmapCount + }; + } + function getTextureFormat(header, flags, fourCC, buffer) { + if (flags & DDS.PIXEL_FORMAT_FLAGS.FOURCC) { + if (fourCC === DDS.D3DFMT.DX10) { + const dx10Header = new Uint32Array( + buffer, + DDS.MAGIC_SIZE + DDS.HEADER_SIZE, + // there is a 20-byte DDS_HEADER_DX10 after DDS_HEADER + DDS.HEADER_DX10_SIZE / Uint32Array.BYTES_PER_ELEMENT + ); + const miscFlag = dx10Header[DDS.HEADER_DX10_FIELDS.MISC_FLAG]; + if (miscFlag === DDS.RESOURCE_MISC_TEXTURECUBE) { + throw new Error("DDSParser does not support cubemap textures"); + } + const resourceDimension = dx10Header[DDS.HEADER_DX10_FIELDS.RESOURCE_DIMENSION]; + if (resourceDimension === DDS.D3D10_RESOURCE_DIMENSION.DDS_DIMENSION_TEXTURE3D) { + throw new Error("DDSParser does not supported 3D texture data"); + } + const dxgiFormat = dx10Header[DDS.HEADER_DX10_FIELDS.DXGI_FORMAT]; + if (dxgiFormat in DXGI_TO_TEXTURE_FORMAT) { + return DXGI_TO_TEXTURE_FORMAT[dxgiFormat]; + } + throw new Error(`DDSParser cannot parse texture data with DXGI format ${dxgiFormat}`); + } + if (fourCC in FOURCC_TO_TEXTURE_FORMAT) { + return FOURCC_TO_TEXTURE_FORMAT[fourCC]; + } + throw new Error(`DDSParser cannot parse texture data with fourCC format ${fourCC}`); + } + if (flags & DDS.PIXEL_FORMAT_FLAGS.RGB || flags & DDS.PIXEL_FORMAT_FLAGS.RGBA) { + return getUncompressedTextureFormat(header); + } + if (flags & DDS.PIXEL_FORMAT_FLAGS.YUV) { + throw new Error("DDSParser does not supported YUV uncompressed texture data."); + } + if (flags & DDS.PIXEL_FORMAT_FLAGS.LUMINANCE || flags & DDS.PIXEL_FORMAT_FLAGS.LUMINANCEA) { + throw new Error("DDSParser does not support single-channel (lumninance) texture data!"); + } + if (flags & DDS.PIXEL_FORMAT_FLAGS.ALPHA || flags & DDS.PIXEL_FORMAT_FLAGS.ALPHAPIXELS) { + throw new Error("DDSParser does not support single-channel (alpha) texture data!"); + } + throw new Error("DDSParser failed to load a texture file due to an unknown reason!"); + } + function getUncompressedTextureFormat(header) { + const bitCount = header[DDS.HEADER_FIELDS.RGB_BITCOUNT]; + const rBitMask = header[DDS.HEADER_FIELDS.R_BIT_MASK]; + const gBitMask = header[DDS.HEADER_FIELDS.G_BIT_MASK]; + const bBitMask = header[DDS.HEADER_FIELDS.B_BIT_MASK]; + const aBitMask = header[DDS.HEADER_FIELDS.A_BIT_MASK]; + switch (bitCount) { + case 32: + if (rBitMask === 255 && gBitMask === 65280 && bBitMask === 16711680 && aBitMask === 4278190080) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM]; + } + if (rBitMask === 16711680 && gBitMask === 65280 && bBitMask === 255 && aBitMask === 4278190080) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM]; + } + if (rBitMask === 1072693248 && gBitMask === 1047552 && bBitMask === 1023 && aBitMask === 3221225472) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R10G10B10A2_UNORM]; + } + if (rBitMask === 65535 && gBitMask === 4294901760 && bBitMask === 0 && aBitMask === 0) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R16G16_UNORM]; + } + if (rBitMask === 4294967295 && gBitMask === 0 && bBitMask === 0 && aBitMask === 0) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT]; + } + break; + case 24: + if (rBitMask === 16711680 && gBitMask === 65280 && bBitMask === 255 && aBitMask === 32768) { + } + break; + case 16: + if (rBitMask === 31744 && gBitMask === 992 && bBitMask === 31 && aBitMask === 32768) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM]; + } + if (rBitMask === 63488 && gBitMask === 2016 && bBitMask === 31 && aBitMask === 0) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_B5G6R5_UNORM]; + } + if (rBitMask === 3840 && gBitMask === 240 && bBitMask === 15 && aBitMask === 61440) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM]; + } + if (rBitMask === 255 && gBitMask === 0 && bBitMask === 0 && aBitMask === 65280) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM]; + } + if (rBitMask === 65535 && gBitMask === 0 && bBitMask === 0 && aBitMask === 0) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R16_UNORM]; + } + break; + case 8: + if (rBitMask === 255 && gBitMask === 0 && bBitMask === 0 && aBitMask === 0) { + return DXGI_TO_TEXTURE_FORMAT[DDS.DXGI_FORMAT.DXGI_FORMAT_R8_UNORM]; + } + break; + } + throw new Error(`DDSParser does not support uncompressed texture with configuration: + bitCount = ${bitCount}, rBitMask = ${rBitMask}, gBitMask = ${gBitMask}, aBitMask = ${aBitMask}`); + } + + "use strict"; + const loadDDS = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.High, + name: "loadDDS" + }, + /** used for deprecation purposes */ + name: "loadDDS", + id: "dds", + test(url) { + return checkExtension(url, [".dds"]); + }, + async load(url, _asset, loader) { + const supportedTextures = await getSupportedTextureFormats(); + const ddsResponse = await fetch(url); + const ddsArrayBuffer = await ddsResponse.arrayBuffer(); + const textureOptions = parseDDS(ddsArrayBuffer, supportedTextures); + const compressedTextureSource = new CompressedSource(textureOptions); + return createTexture(compressedTextureSource, loader, url); + }, + unload(texture) { + if (Array.isArray(texture)) { + texture.forEach((t) => t.destroy(true)); + } else { + texture.destroy(true); + } + } + }; + + "use strict"; + var GL_INTERNAL_FORMAT = /* @__PURE__ */ ((GL_INTERNAL_FORMAT2) => { + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["RGBA8_SNORM"] = 36759] = "RGBA8_SNORM"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["RGBA"] = 6408] = "RGBA"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["RGBA8UI"] = 36220] = "RGBA8UI"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["SRGB8_ALPHA8"] = 35907] = "SRGB8_ALPHA8"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["RGBA8I"] = 36238] = "RGBA8I"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["RGBA8"] = 32856] = "RGBA8"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGB_S3TC_DXT1_EXT"] = 33776] = "COMPRESSED_RGB_S3TC_DXT1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_S3TC_DXT1_EXT"] = 33777] = "COMPRESSED_RGBA_S3TC_DXT1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_S3TC_DXT3_EXT"] = 33778] = "COMPRESSED_RGBA_S3TC_DXT3_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_S3TC_DXT5_EXT"] = 33779] = "COMPRESSED_RGBA_S3TC_DXT5_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT"] = 35917] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT"] = 35918] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT"] = 35919] = "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB_S3TC_DXT1_EXT"] = 35916] = "COMPRESSED_SRGB_S3TC_DXT1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RED_RGTC1_EXT"] = 36283] = "COMPRESSED_RED_RGTC1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SIGNED_RED_RGTC1_EXT"] = 36284] = "COMPRESSED_SIGNED_RED_RGTC1_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RED_GREEN_RGTC2_EXT"] = 36285] = "COMPRESSED_RED_GREEN_RGTC2_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT"] = 36286] = "COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_R11_EAC"] = 37488] = "COMPRESSED_R11_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SIGNED_R11_EAC"] = 37489] = "COMPRESSED_SIGNED_R11_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RG11_EAC"] = 37490] = "COMPRESSED_RG11_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SIGNED_RG11_EAC"] = 37491] = "COMPRESSED_SIGNED_RG11_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGB8_ETC2"] = 37492] = "COMPRESSED_RGB8_ETC2"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA8_ETC2_EAC"] = 37496] = "COMPRESSED_RGBA8_ETC2_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ETC2"] = 37493] = "COMPRESSED_SRGB8_ETC2"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"] = 37497] = "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"] = 37494] = "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"] = 37495] = "COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_4x4_KHR"] = 37808] = "COMPRESSED_RGBA_ASTC_4x4_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_5x4_KHR"] = 37809] = "COMPRESSED_RGBA_ASTC_5x4_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_5x5_KHR"] = 37810] = "COMPRESSED_RGBA_ASTC_5x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_6x5_KHR"] = 37811] = "COMPRESSED_RGBA_ASTC_6x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_6x6_KHR"] = 37812] = "COMPRESSED_RGBA_ASTC_6x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_8x5_KHR"] = 37813] = "COMPRESSED_RGBA_ASTC_8x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_8x6_KHR"] = 37814] = "COMPRESSED_RGBA_ASTC_8x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_8x8_KHR"] = 37815] = "COMPRESSED_RGBA_ASTC_8x8_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_10x5_KHR"] = 37816] = "COMPRESSED_RGBA_ASTC_10x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_10x6_KHR"] = 37817] = "COMPRESSED_RGBA_ASTC_10x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_10x8_KHR"] = 37818] = "COMPRESSED_RGBA_ASTC_10x8_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_10x10_KHR"] = 37819] = "COMPRESSED_RGBA_ASTC_10x10_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_12x10_KHR"] = 37820] = "COMPRESSED_RGBA_ASTC_12x10_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_ASTC_12x12_KHR"] = 37821] = "COMPRESSED_RGBA_ASTC_12x12_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR"] = 37840] = "COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR"] = 37841] = "COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR"] = 37842] = "COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR"] = 37843] = "COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR"] = 37844] = "COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR"] = 37845] = "COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR"] = 37846] = "COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR"] = 37847] = "COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR"] = 37848] = "COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR"] = 37849] = "COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR"] = 37850] = "COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR"] = 37851] = "COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR"] = 37852] = "COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR"] = 37853] = "COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGBA_BPTC_UNORM_EXT"] = 36492] = "COMPRESSED_RGBA_BPTC_UNORM_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT"] = 36493] = "COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT"] = 36494] = "COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT"; + GL_INTERNAL_FORMAT2[GL_INTERNAL_FORMAT2["COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT"] = 36495] = "COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT"; + return GL_INTERNAL_FORMAT2; + })(GL_INTERNAL_FORMAT || {}); + var GL_FORMATS$1 = /* @__PURE__ */ ((GL_FORMATS2) => { + GL_FORMATS2[GL_FORMATS2["RGBA"] = 6408] = "RGBA"; + GL_FORMATS2[GL_FORMATS2["RGB"] = 6407] = "RGB"; + GL_FORMATS2[GL_FORMATS2["RG"] = 33319] = "RG"; + GL_FORMATS2[GL_FORMATS2["RED"] = 6403] = "RED"; + GL_FORMATS2[GL_FORMATS2["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RGB_INTEGER"] = 36248] = "RGB_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RG_INTEGER"] = 33320] = "RG_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RED_INTEGER"] = 36244] = "RED_INTEGER"; + GL_FORMATS2[GL_FORMATS2["ALPHA"] = 6406] = "ALPHA"; + GL_FORMATS2[GL_FORMATS2["LUMINANCE"] = 6409] = "LUMINANCE"; + GL_FORMATS2[GL_FORMATS2["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA"; + GL_FORMATS2[GL_FORMATS2["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT"; + GL_FORMATS2[GL_FORMATS2["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL"; + return GL_FORMATS2; + })(GL_FORMATS$1 || {}); + var GL_TYPES$1 = /* @__PURE__ */ ((GL_TYPES2) => { + GL_TYPES2[GL_TYPES2["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV"; + GL_TYPES2[GL_TYPES2["BYTE"] = 5120] = "BYTE"; + GL_TYPES2[GL_TYPES2["SHORT"] = 5122] = "SHORT"; + GL_TYPES2[GL_TYPES2["INT"] = 5124] = "INT"; + GL_TYPES2[GL_TYPES2["FLOAT"] = 5126] = "FLOAT"; + GL_TYPES2[GL_TYPES2["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV"; + GL_TYPES2[GL_TYPES2["HALF_FLOAT"] = 36193] = "HALF_FLOAT"; + return GL_TYPES2; + })(GL_TYPES$1 || {}); + const INTERNAL_FORMAT_TO_TEXTURE_FORMATS = { + [33776 /* COMPRESSED_RGB_S3TC_DXT1_EXT */]: "bc1-rgba-unorm", + // TODO: ??? + [33777 /* COMPRESSED_RGBA_S3TC_DXT1_EXT */]: "bc1-rgba-unorm", + [33778 /* COMPRESSED_RGBA_S3TC_DXT3_EXT */]: "bc2-rgba-unorm", + [33779 /* COMPRESSED_RGBA_S3TC_DXT5_EXT */]: "bc3-rgba-unorm", + [35916 /* COMPRESSED_SRGB_S3TC_DXT1_EXT */]: "bc1-rgba-unorm-srgb", + // TODO: ??? + [35917 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT */]: "bc1-rgba-unorm-srgb", + [35918 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT */]: "bc2-rgba-unorm-srgb", + [35919 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT */]: "bc3-rgba-unorm-srgb", + [36283 /* COMPRESSED_RED_RGTC1_EXT */]: "bc4-r-unorm", + [36284 /* COMPRESSED_SIGNED_RED_RGTC1_EXT */]: "bc4-r-snorm", + [36285 /* COMPRESSED_RED_GREEN_RGTC2_EXT */]: "bc5-rg-unorm", + [36286 /* COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT */]: "bc5-rg-snorm", + [37488 /* COMPRESSED_R11_EAC */]: "eac-r11unorm", + // [GL_INTERNAL_FORMAT.COMPRESSED_SIGNED_R11_EAC]: 'eac-r11snorm', + [37490 /* COMPRESSED_RG11_EAC */]: "eac-rg11snorm", + // [GL_INTERNAL_FORMAT.COMPRESSED_SIGNED_RG11_EAC]: 'eac-rg11unorm', + [37492 /* COMPRESSED_RGB8_ETC2 */]: "etc2-rgb8unorm", + [37496 /* COMPRESSED_RGBA8_ETC2_EAC */]: "etc2-rgba8unorm", + [37493 /* COMPRESSED_SRGB8_ETC2 */]: "etc2-rgb8unorm-srgb", + [37497 /* COMPRESSED_SRGB8_ALPHA8_ETC2_EAC */]: "etc2-rgba8unorm-srgb", + [37494 /* COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 */]: "etc2-rgb8a1unorm", + [37495 /* COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 */]: "etc2-rgb8a1unorm-srgb", + [37808 /* COMPRESSED_RGBA_ASTC_4x4_KHR */]: "astc-4x4-unorm", + [37840 /* COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR */]: "astc-4x4-unorm-srgb", + [37809 /* COMPRESSED_RGBA_ASTC_5x4_KHR */]: "astc-5x4-unorm", + [37841 /* COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR */]: "astc-5x4-unorm-srgb", + [37810 /* COMPRESSED_RGBA_ASTC_5x5_KHR */]: "astc-5x5-unorm", + [37842 /* COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR */]: "astc-5x5-unorm-srgb", + [37811 /* COMPRESSED_RGBA_ASTC_6x5_KHR */]: "astc-6x5-unorm", + [37843 /* COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR */]: "astc-6x5-unorm-srgb", + [37812 /* COMPRESSED_RGBA_ASTC_6x6_KHR */]: "astc-6x6-unorm", + [37844 /* COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR */]: "astc-6x6-unorm-srgb", + [37813 /* COMPRESSED_RGBA_ASTC_8x5_KHR */]: "astc-8x5-unorm", + [37845 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR */]: "astc-8x5-unorm-srgb", + [37814 /* COMPRESSED_RGBA_ASTC_8x6_KHR */]: "astc-8x6-unorm", + [37846 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR */]: "astc-8x6-unorm-srgb", + [37815 /* COMPRESSED_RGBA_ASTC_8x8_KHR */]: "astc-8x8-unorm", + [37847 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR */]: "astc-8x8-unorm-srgb", + [37816 /* COMPRESSED_RGBA_ASTC_10x5_KHR */]: "astc-10x5-unorm", + [37848 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR */]: "astc-10x5-unorm-srgb", + [37817 /* COMPRESSED_RGBA_ASTC_10x6_KHR */]: "astc-10x6-unorm", + [37849 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR */]: "astc-10x6-unorm-srgb", + [37818 /* COMPRESSED_RGBA_ASTC_10x8_KHR */]: "astc-10x8-unorm", + [37850 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR */]: "astc-10x8-unorm-srgb", + [37819 /* COMPRESSED_RGBA_ASTC_10x10_KHR */]: "astc-10x10-unorm", + [37851 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR */]: "astc-10x10-unorm-srgb", + [37820 /* COMPRESSED_RGBA_ASTC_12x10_KHR */]: "astc-12x10-unorm", + [37852 /* COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR */]: "astc-12x10-unorm-srgb", + [37821 /* COMPRESSED_RGBA_ASTC_12x12_KHR */]: "astc-12x12-unorm", + [37853 /* COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR */]: "astc-12x12-unorm-srgb", + [36492 /* COMPRESSED_RGBA_BPTC_UNORM_EXT */]: "bc7-rgba-unorm", + [36493 /* COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT */]: "bc7-rgba-unorm-srgb", + [36494 /* COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT */]: "bc6h-rgb-float", + [36495 /* COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT */]: "bc6h-rgb-ufloat", + [35907 /* SRGB8_ALPHA8 */]: "rgba8unorm-srgb", + [36759 /* RGBA8_SNORM */]: "rgba8snorm", + [36220 /* RGBA8UI */]: "rgba8uint", + [36238 /* RGBA8I */]: "rgba8sint", + [6408 /* RGBA */]: "rgba8unorm" + // [GL_INTERNAL_FORMAT.RGBA8]: 'bgra8unorm' + }; + const FILE_IDENTIFIER = [171, 75, 84, 88, 32, 49, 49, 187, 13, 10, 26, 10]; + const FIELDS = { + FILE_IDENTIFIER: 0, + ENDIANNESS: 12, + GL_TYPE: 16, + GL_TYPE_SIZE: 20, + GL_FORMAT: 24, + GL_INTERNAL_FORMAT: 28, + GL_BASE_INTERNAL_FORMAT: 32, + PIXEL_WIDTH: 36, + PIXEL_HEIGHT: 40, + PIXEL_DEPTH: 44, + NUMBER_OF_ARRAY_ELEMENTS: 48, + NUMBER_OF_FACES: 52, + NUMBER_OF_MIPMAP_LEVELS: 56, + BYTES_OF_KEY_VALUE_DATA: 60 + }; + const FILE_HEADER_SIZE = 64; + const ENDIANNESS = 67305985; + const TYPES_TO_BYTES_PER_COMPONENT = { + [5121 /* UNSIGNED_BYTE */]: 1, + [5123 /* UNSIGNED_SHORT */]: 2, + [5124 /* INT */]: 4, + [5125 /* UNSIGNED_INT */]: 4, + [5126 /* FLOAT */]: 4, + [36193 /* HALF_FLOAT */]: 8 + }; + const FORMATS_TO_COMPONENTS = { + [6408 /* RGBA */]: 4, + [6407 /* RGB */]: 3, + [33319 /* RG */]: 2, + [6403 /* RED */]: 1, + [6409 /* LUMINANCE */]: 1, + [6410 /* LUMINANCE_ALPHA */]: 2, + [6406 /* ALPHA */]: 1 + }; + const TYPES_TO_BYTES_PER_PIXEL = { + [32819 /* UNSIGNED_SHORT_4_4_4_4 */]: 2, + [32820 /* UNSIGNED_SHORT_5_5_5_1 */]: 2, + [33635 /* UNSIGNED_SHORT_5_6_5 */]: 2 + }; + const INTERNAL_FORMAT_TO_BYTES_PER_PIXEL = { + [33776 /* COMPRESSED_RGB_S3TC_DXT1_EXT */]: 0.5, + [33777 /* COMPRESSED_RGBA_S3TC_DXT1_EXT */]: 0.5, + [33778 /* COMPRESSED_RGBA_S3TC_DXT3_EXT */]: 1, + [33779 /* COMPRESSED_RGBA_S3TC_DXT5_EXT */]: 1, + [35916 /* COMPRESSED_SRGB_S3TC_DXT1_EXT */]: 0.5, + [35917 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT */]: 0.5, + [35918 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT */]: 1, + [35919 /* COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT */]: 1, + [36283 /* COMPRESSED_RED_RGTC1_EXT */]: 0.5, + [36284 /* COMPRESSED_SIGNED_RED_RGTC1_EXT */]: 0.5, + [36285 /* COMPRESSED_RED_GREEN_RGTC2_EXT */]: 1, + [36286 /* COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT */]: 1, + [37488 /* COMPRESSED_R11_EAC */]: 0.5, + [37489 /* COMPRESSED_SIGNED_R11_EAC */]: 0.5, + [37490 /* COMPRESSED_RG11_EAC */]: 1, + [37491 /* COMPRESSED_SIGNED_RG11_EAC */]: 1, + [37492 /* COMPRESSED_RGB8_ETC2 */]: 0.5, + [37496 /* COMPRESSED_RGBA8_ETC2_EAC */]: 1, + [37493 /* COMPRESSED_SRGB8_ETC2 */]: 0.5, + [37497 /* COMPRESSED_SRGB8_ALPHA8_ETC2_EAC */]: 1, + [37494 /* COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 */]: 0.5, + [37495 /* COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 */]: 0.5, + [37808 /* COMPRESSED_RGBA_ASTC_4x4_KHR */]: 1, + [37840 /* COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR */]: 1, + [37809 /* COMPRESSED_RGBA_ASTC_5x4_KHR */]: 0.8, + [37841 /* COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR */]: 0.8, + [37810 /* COMPRESSED_RGBA_ASTC_5x5_KHR */]: 0.64, + [37842 /* COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR */]: 0.64, + [37811 /* COMPRESSED_RGBA_ASTC_6x5_KHR */]: 0.53375, + [37843 /* COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR */]: 0.53375, + [37812 /* COMPRESSED_RGBA_ASTC_6x6_KHR */]: 0.445, + [37844 /* COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR */]: 0.445, + [37813 /* COMPRESSED_RGBA_ASTC_8x5_KHR */]: 0.4, + [37845 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR */]: 0.4, + [37814 /* COMPRESSED_RGBA_ASTC_8x6_KHR */]: 0.33375, + [37846 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR */]: 0.33375, + [37815 /* COMPRESSED_RGBA_ASTC_8x8_KHR */]: 0.25, + [37847 /* COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR */]: 0.25, + [37816 /* COMPRESSED_RGBA_ASTC_10x5_KHR */]: 0.32, + [37848 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR */]: 0.32, + [37817 /* COMPRESSED_RGBA_ASTC_10x6_KHR */]: 0.26625, + [37849 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR */]: 0.26625, + [37818 /* COMPRESSED_RGBA_ASTC_10x8_KHR */]: 0.2, + [37850 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR */]: 0.2, + [37819 /* COMPRESSED_RGBA_ASTC_10x10_KHR */]: 0.16, + [37851 /* COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR */]: 0.16, + [37820 /* COMPRESSED_RGBA_ASTC_12x10_KHR */]: 0.13375, + [37852 /* COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR */]: 0.13375, + [37821 /* COMPRESSED_RGBA_ASTC_12x12_KHR */]: 0.11125, + [37853 /* COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR */]: 0.11125, + [36492 /* COMPRESSED_RGBA_BPTC_UNORM_EXT */]: 1, + [36493 /* COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT */]: 1, + [36494 /* COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT */]: 1, + [36495 /* COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT */]: 1 + }; + const KTX = { + FILE_HEADER_SIZE, + FILE_IDENTIFIER, + FORMATS_TO_COMPONENTS, + INTERNAL_FORMAT_TO_BYTES_PER_PIXEL, + INTERNAL_FORMAT_TO_TEXTURE_FORMATS, + FIELDS, + TYPES_TO_BYTES_PER_COMPONENT, + TYPES_TO_BYTES_PER_PIXEL, + ENDIANNESS + }; + + "use strict"; + function parseKTX(arrayBuffer, supportedFormats) { + const dataView = new DataView(arrayBuffer); + if (!validate(dataView)) { + throw new Error("Invalid KTX identifier in header"); + } + const { + littleEndian, + glType, + glFormat, + glInternalFormat, + pixelWidth, + pixelHeight, + numberOfMipmapLevels, + offset + } = parseKTXHeader(dataView); + const textureFormat = KTX.INTERNAL_FORMAT_TO_TEXTURE_FORMATS[glInternalFormat]; + if (!textureFormat) { + throw new Error(`Unknown texture format ${glInternalFormat}`); + } + if (!supportedFormats.includes(textureFormat)) { + throw new Error(`Unsupported texture format: ${textureFormat}, supportedFormats: ${supportedFormats}`); + } + const imagePixelByteSize = getImagePixelByteSize(glType, glFormat, glInternalFormat); + const imageBuffers = getImageBuffers( + dataView, + glType, + imagePixelByteSize, + pixelWidth, + pixelHeight, + offset, + numberOfMipmapLevels, + littleEndian + ); + return { + format: textureFormat, + width: pixelWidth, + height: pixelHeight, + resource: imageBuffers, + alphaMode: "no-premultiply-alpha" + }; + } + function getImageBuffers(dataView, glType, imagePixelByteSize, pixelWidth, pixelHeight, offset, numberOfMipmapLevels, littleEndian) { + const alignedWidth = pixelWidth + 3 & ~3; + const alignedHeight = pixelHeight + 3 & ~3; + let imagePixels = pixelWidth * pixelHeight; + if (glType === 0) { + imagePixels = alignedWidth * alignedHeight; + } + let mipByteSize = imagePixels * imagePixelByteSize; + let mipWidth = pixelWidth; + let mipHeight = pixelHeight; + let alignedMipWidth = alignedWidth; + let alignedMipHeight = alignedHeight; + let imageOffset = offset; + const imageBuffers = new Array(numberOfMipmapLevels); + for (let mipmapLevel = 0; mipmapLevel < numberOfMipmapLevels; mipmapLevel++) { + const imageSize = dataView.getUint32(imageOffset, littleEndian); + let elementOffset = imageOffset + 4; + imageBuffers[mipmapLevel] = new Uint8Array(dataView.buffer, elementOffset, mipByteSize); + elementOffset += mipByteSize; + imageOffset += imageSize + 4; + imageOffset = imageOffset % 4 !== 0 ? imageOffset + 4 - imageOffset % 4 : imageOffset; + mipWidth = mipWidth >> 1 || 1; + mipHeight = mipHeight >> 1 || 1; + alignedMipWidth = mipWidth + 4 - 1 & ~(4 - 1); + alignedMipHeight = mipHeight + 4 - 1 & ~(4 - 1); + mipByteSize = alignedMipWidth * alignedMipHeight * imagePixelByteSize; + } + return imageBuffers; + } + function getImagePixelByteSize(glType, glFormat, glInternalFormat) { + let imagePixelByteSize = KTX.INTERNAL_FORMAT_TO_BYTES_PER_PIXEL[glInternalFormat]; + if (glType !== 0) { + if (KTX.TYPES_TO_BYTES_PER_COMPONENT[glType]) { + imagePixelByteSize = KTX.TYPES_TO_BYTES_PER_COMPONENT[glType] * KTX.FORMATS_TO_COMPONENTS[glFormat]; + } else { + imagePixelByteSize = KTX.TYPES_TO_BYTES_PER_PIXEL[glType]; + } + } + if (imagePixelByteSize === void 0) { + throw new Error("Unable to resolve the pixel format stored in the *.ktx file!"); + } + return imagePixelByteSize; + } + function parseKTXHeader(dataView) { + const littleEndian = dataView.getUint32(KTX.FIELDS.ENDIANNESS, true) === KTX.ENDIANNESS; + const glType = dataView.getUint32(KTX.FIELDS.GL_TYPE, littleEndian); + const glFormat = dataView.getUint32(KTX.FIELDS.GL_FORMAT, littleEndian); + const glInternalFormat = dataView.getUint32(KTX.FIELDS.GL_INTERNAL_FORMAT, littleEndian); + const pixelWidth = dataView.getUint32(KTX.FIELDS.PIXEL_WIDTH, littleEndian); + const pixelHeight = dataView.getUint32(KTX.FIELDS.PIXEL_HEIGHT, littleEndian) || 1; + const pixelDepth = dataView.getUint32(KTX.FIELDS.PIXEL_DEPTH, littleEndian) || 1; + const numberOfArrayElements = dataView.getUint32(KTX.FIELDS.NUMBER_OF_ARRAY_ELEMENTS, littleEndian) || 1; + const numberOfFaces = dataView.getUint32(KTX.FIELDS.NUMBER_OF_FACES, littleEndian); + const numberOfMipmapLevels = dataView.getUint32(KTX.FIELDS.NUMBER_OF_MIPMAP_LEVELS, littleEndian); + const bytesOfKeyValueData = dataView.getUint32(KTX.FIELDS.BYTES_OF_KEY_VALUE_DATA, littleEndian); + if (pixelHeight === 0 || pixelDepth !== 1) { + throw new Error("Only 2D textures are supported"); + } + if (numberOfFaces !== 1) { + throw new Error("CubeTextures are not supported by KTXLoader yet!"); + } + if (numberOfArrayElements !== 1) { + throw new Error("WebGL does not support array textures"); + } + return { + littleEndian, + glType, + glFormat, + glInternalFormat, + pixelWidth, + pixelHeight, + numberOfMipmapLevels, + offset: KTX.FILE_HEADER_SIZE + bytesOfKeyValueData + }; + } + function validate(dataView) { + for (let i = 0; i < KTX.FILE_IDENTIFIER.length; i++) { + if (dataView.getUint8(i) !== KTX.FILE_IDENTIFIER[i]) { + return false; + } + } + return true; + } + + "use strict"; + const loadKTX = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.High, + name: "loadKTX" + }, + /** used for deprecation purposes */ + name: "loadKTX", + id: "ktx", + test(url) { + return checkExtension(url, ".ktx"); + }, + async load(url, _asset, loader) { + const supportedTextures = await getSupportedTextureFormats(); + const ktxResponse = await fetch(url); + const ktxArrayBuffer = await ktxResponse.arrayBuffer(); + const textureOptions = parseKTX(ktxArrayBuffer, supportedTextures); + const compressedTextureSource = new CompressedSource(textureOptions); + return createTexture(compressedTextureSource, loader, url); + }, + unload(texture) { + if (Array.isArray(texture)) { + texture.forEach((t) => t.destroy(true)); + } else { + texture.destroy(true); + } + } + }; + + const WORKER_CODE = "(function () {\n 'use strict';\n\n const converters = {\n rgb8unorm: {\n convertedFormat: \"rgba8unorm\",\n convertFunction: convertRGBtoRGBA\n },\n \"rgb8unorm-srgb\": {\n convertedFormat: \"rgba8unorm-srgb\",\n convertFunction: convertRGBtoRGBA\n }\n };\n function convertFormatIfRequired(textureOptions) {\n const format = textureOptions.format;\n if (converters[format]) {\n const convertFunction = converters[format].convertFunction;\n const levelBuffers = textureOptions.resource;\n for (let i = 0; i < levelBuffers.length; i++) {\n levelBuffers[i] = convertFunction(levelBuffers[i]);\n }\n textureOptions.format = converters[format].convertedFormat;\n }\n }\n function convertRGBtoRGBA(levelBuffer) {\n const pixelCount = levelBuffer.byteLength / 3;\n const levelBufferWithAlpha = new Uint32Array(pixelCount);\n for (let i = 0; i < pixelCount; ++i) {\n levelBufferWithAlpha[i] = levelBuffer[i * 3] + (levelBuffer[i * 3 + 1] << 8) + (levelBuffer[i * 3 + 2] << 16) + 4278190080;\n }\n return new Uint8Array(levelBufferWithAlpha.buffer);\n }\n\n function createLevelBuffersFromKTX(ktxTexture) {\n const levelBuffers = [];\n for (let i = 0; i < ktxTexture.numLevels; i++) {\n const imageData = ktxTexture.getImageData(i, 0, 0);\n const levelBuffer = new Uint8Array(imageData.byteLength);\n levelBuffer.set(imageData);\n levelBuffers.push(levelBuffer);\n }\n return levelBuffers;\n }\n\n const glFormatToGPUFormatMap = {\n 6408: \"rgba8unorm\",\n 32856: \"bgra8unorm\",\n //\n 32857: \"rgb10a2unorm\",\n 33189: \"depth16unorm\",\n 33190: \"depth24plus\",\n 33321: \"r8unorm\",\n 33323: \"rg8unorm\",\n 33325: \"r16float\",\n 33326: \"r32float\",\n 33327: \"rg16float\",\n 33328: \"rg32float\",\n 33329: \"r8sint\",\n 33330: \"r8uint\",\n 33331: \"r16sint\",\n 33332: \"r16uint\",\n 33333: \"r32sint\",\n 33334: \"r32uint\",\n 33335: \"rg8sint\",\n 33336: \"rg8uint\",\n 33337: \"rg16sint\",\n 33338: \"rg16uint\",\n 33339: \"rg32sint\",\n 33340: \"rg32uint\",\n 33778: \"bc2-rgba-unorm\",\n 33779: \"bc3-rgba-unorm\",\n 34836: \"rgba32float\",\n 34842: \"rgba16float\",\n 35056: \"depth24plus-stencil8\",\n 35898: \"rg11b10ufloat\",\n 35901: \"rgb9e5ufloat\",\n 35907: \"rgba8unorm-srgb\",\n // bgra8unorm-srgb\n 36012: \"depth32float\",\n 36013: \"depth32float-stencil8\",\n 36168: \"stencil8\",\n 36208: \"rgba32uint\",\n 36214: \"rgba16uint\",\n 36220: \"rgba8uint\",\n 36226: \"rgba32sint\",\n 36232: \"rgba16sint\",\n 36238: \"rgba8sint\",\n 36492: \"bc7-rgba-unorm\",\n 36756: \"r8snorm\",\n 36757: \"rg8snorm\",\n 36759: \"rgba8snorm\",\n 37496: \"etc2-rgba8unorm\",\n 37808: \"astc-4x4-unorm\"\n };\n function glFormatToGPUFormat(glInternalFormat) {\n const format = glFormatToGPUFormatMap[glInternalFormat];\n if (format) {\n return format;\n }\n throw new Error(`Unsupported glInternalFormat: ${glInternalFormat}`);\n }\n\n const vkFormatToGPUFormatMap = {\n 23: \"rgb8unorm\",\n // VK_FORMAT_R8G8B8_UNORM\n 37: \"rgba8unorm\",\n // VK_FORMAT_R8G8B8A8_UNORM\n 43: \"rgba8unorm-srgb\"\n // VK_FORMAT_R8G8B8A8_SRGB\n // TODO add more!\n };\n function vkFormatToGPUFormat(vkFormat) {\n const format = vkFormatToGPUFormatMap[vkFormat];\n if (format) {\n return format;\n }\n throw new Error(`Unsupported VkFormat: ${vkFormat}`);\n }\n\n function getTextureFormatFromKTXTexture(ktxTexture) {\n if (ktxTexture.classId === 2) {\n return vkFormatToGPUFormat(ktxTexture.vkFormat);\n }\n return glFormatToGPUFormat(ktxTexture.glInternalformat);\n }\n\n const gpuFormatToBasisTranscoderFormatMap = {\n \"bc3-rgba-unorm\": \"BC3_RGBA\",\n \"bc7-rgba-unorm\": \"BC7_M5_RGBA\",\n \"etc2-rgba8unorm\": \"ETC2_RGBA\",\n \"astc-4x4-unorm\": \"ASTC_4x4_RGBA\",\n // Uncompressed\n rgba8unorm: \"RGBA32\",\n rg11b10ufloat: \"R11F_G11F_B10F\"\n };\n function gpuFormatToKTXBasisTranscoderFormat(transcoderFormat) {\n const format = gpuFormatToBasisTranscoderFormatMap[transcoderFormat];\n if (format) {\n return format;\n }\n throw new Error(`Unsupported transcoderFormat: ${transcoderFormat}`);\n }\n\n const settings = {\n jsUrl: \"\",\n wasmUrl: \"\"\n };\n let basisTranscoderFormat;\n let basisTranscodedTextureFormat;\n let ktxPromise;\n async function getKTX() {\n if (!ktxPromise) {\n const absoluteJsUrl = new URL(settings.jsUrl, location.origin).href;\n const absoluteWasmUrl = new URL(settings.wasmUrl, location.origin).href;\n importScripts(absoluteJsUrl);\n ktxPromise = new Promise((resolve) => {\n LIBKTX({\n locateFile: (_file) => absoluteWasmUrl\n }).then((libktx) => {\n resolve(libktx);\n });\n });\n }\n return ktxPromise;\n }\n async function fetchKTXTexture(url, ktx) {\n const ktx2Response = await fetch(url);\n if (ktx2Response.ok) {\n const ktx2ArrayBuffer = await ktx2Response.arrayBuffer();\n return new ktx.ktxTexture(new Uint8Array(ktx2ArrayBuffer));\n }\n throw new Error(`Failed to load KTX(2) texture: ${url}`);\n }\n const preferredTranscodedFormat = [\n \"bc7-rgba-unorm\",\n \"astc-4x4-unorm\",\n \"etc2-rgba8unorm\",\n \"bc3-rgba-unorm\",\n \"rgba8unorm\"\n ];\n async function load(url) {\n const ktx = await getKTX();\n const ktxTexture = await fetchKTXTexture(url, ktx);\n let format;\n if (ktxTexture.needsTranscoding) {\n format = basisTranscodedTextureFormat;\n const transcodeFormat = ktx.TranscodeTarget[basisTranscoderFormat];\n const result = ktxTexture.transcodeBasis(transcodeFormat, 0);\n if (result !== ktx.ErrorCode.SUCCESS) {\n throw new Error(\"Unable to transcode basis texture.\");\n }\n } else {\n format = getTextureFormatFromKTXTexture(ktxTexture);\n }\n const levelBuffers = createLevelBuffersFromKTX(ktxTexture);\n const textureOptions = {\n width: ktxTexture.baseWidth,\n height: ktxTexture.baseHeight,\n format,\n mipLevelCount: ktxTexture.numLevels,\n resource: levelBuffers,\n alphaMode: \"no-premultiply-alpha\"\n };\n convertFormatIfRequired(textureOptions);\n return textureOptions;\n }\n async function init(jsUrl, wasmUrl, supportedTextures) {\n if (jsUrl) settings.jsUrl = jsUrl;\n if (wasmUrl) settings.wasmUrl = wasmUrl;\n basisTranscodedTextureFormat = preferredTranscodedFormat.filter((format) => supportedTextures.includes(format))[0];\n basisTranscoderFormat = gpuFormatToKTXBasisTranscoderFormat(basisTranscodedTextureFormat);\n await getKTX();\n }\n const messageHandlers = {\n init: async (data) => {\n const { jsUrl, wasmUrl, supportedTextures } = data;\n await init(jsUrl, wasmUrl, supportedTextures);\n },\n load: async (data) => {\n var _a;\n try {\n const textureOptions = await load(data.url);\n return {\n type: \"load\",\n url: data.url,\n success: true,\n textureOptions,\n transferables: (_a = textureOptions.resource) == null ? void 0 : _a.map((arr) => arr.buffer)\n };\n } catch (e) {\n throw e;\n }\n }\n };\n self.onmessage = (async (messageEvent) => {\n var _a;\n const message = messageEvent.data;\n try {\n const response = await ((_a = messageHandlers[message.type]) == null ? void 0 : _a.call(messageHandlers, message));\n if (response) {\n self.postMessage(response, response.transferables);\n }\n } catch (err) {\n self.postMessage({\n type: \"error\",\n err,\n url: message.url\n });\n }\n });\n\n})();\n"; + let WORKER_URL = null; + class WorkerInstance + { + constructor() + { + if (!WORKER_URL) + { + WORKER_URL = URL.createObjectURL(new Blob([WORKER_CODE], { type: 'application/javascript' })); + } + this.worker = new Worker(WORKER_URL); + } + } + WorkerInstance.revokeObjectURL = function revokeObjectURL() + { + if (WORKER_URL) + { + URL.revokeObjectURL(WORKER_URL); + WORKER_URL = null; + } + }; + + "use strict"; + const ktxTranscoderUrls = { + jsUrl: "https://cdn.jsdelivr.net/npm/pixi.js/transcoders/ktx/libktx.js", + wasmUrl: "https://cdn.jsdelivr.net/npm/pixi.js/transcoders/ktx/libktx.wasm" + }; + function setKTXTranscoderPath(config) { + Object.assign(ktxTranscoderUrls, config); + } + + "use strict"; + let ktxWorker; + const urlHash = {}; + const errorHash = {}; + function getKTX2Worker(supportedTextures) { + if (!ktxWorker) { + ktxWorker = new WorkerInstance().worker; + ktxWorker.onmessage = (messageEvent) => { + const { err, success, url, textureOptions } = messageEvent.data; + if (err) { + errorHash[url](err); + return; + } + if (!success) { + console.warn("Failed to load KTX texture", url); + } + urlHash[url](textureOptions); + }; + ktxWorker.postMessage({ + type: "init", + jsUrl: ktxTranscoderUrls.jsUrl, + wasmUrl: ktxTranscoderUrls.wasmUrl, + supportedTextures + }); + } + return ktxWorker; + } + function loadKTX2onWorker(url, supportedTextures) { + const ktxWorker2 = getKTX2Worker(supportedTextures); + return new Promise((resolve, reject) => { + urlHash[url] = resolve; + errorHash[url] = reject; + ktxWorker2.postMessage({ type: "load", url }); + }); + } + + "use strict"; + const loadKTX2 = { + extension: { + type: ExtensionType.LoadParser, + priority: LoaderParserPriority.High, + name: "loadKTX2" + }, + /** used for deprecation purposes */ + name: "loadKTX2", + id: "ktx2", + test(url) { + return checkExtension(url, ".ktx2"); + }, + async load(url, _asset, loader) { + const supportedTextures = await getSupportedTextureFormats(); + const textureOptions = await loadKTX2onWorker(url, supportedTextures); + const compressedTextureSource = new CompressedSource(textureOptions); + return createTexture(compressedTextureSource, loader, url); + }, + async unload(texture) { + if (Array.isArray(texture)) { + texture.forEach((t) => t.destroy(true)); + } else { + texture.destroy(true); + } + } + }; + + "use strict"; + + "use strict"; + const converters = { + rgb8unorm: { + convertedFormat: "rgba8unorm", + convertFunction: convertRGBtoRGBA + }, + "rgb8unorm-srgb": { + convertedFormat: "rgba8unorm-srgb", + convertFunction: convertRGBtoRGBA + } + }; + function convertFormatIfRequired(textureOptions) { + const format = textureOptions.format; + if (converters[format]) { + const convertFunction = converters[format].convertFunction; + const levelBuffers = textureOptions.resource; + for (let i = 0; i < levelBuffers.length; i++) { + levelBuffers[i] = convertFunction(levelBuffers[i]); + } + textureOptions.format = converters[format].convertedFormat; + } + } + function convertRGBtoRGBA(levelBuffer) { + const pixelCount = levelBuffer.byteLength / 3; + const levelBufferWithAlpha = new Uint32Array(pixelCount); + for (let i = 0; i < pixelCount; ++i) { + levelBufferWithAlpha[i] = levelBuffer[i * 3] + (levelBuffer[i * 3 + 1] << 8) + (levelBuffer[i * 3 + 2] << 16) + 4278190080; + } + return new Uint8Array(levelBufferWithAlpha.buffer); + } + + "use strict"; + function createLevelBuffersFromKTX(ktxTexture) { + const levelBuffers = []; + for (let i = 0; i < ktxTexture.numLevels; i++) { + const imageData = ktxTexture.getImageData(i, 0, 0); + const levelBuffer = new Uint8Array(imageData.byteLength); + levelBuffer.set(imageData); + levelBuffers.push(levelBuffer); + } + return levelBuffers; + } + + "use strict"; + const glFormatToGPUFormatMap = { + 6408: "rgba8unorm", + 32856: "bgra8unorm", + // + 32857: "rgb10a2unorm", + 33189: "depth16unorm", + 33190: "depth24plus", + 33321: "r8unorm", + 33323: "rg8unorm", + 33325: "r16float", + 33326: "r32float", + 33327: "rg16float", + 33328: "rg32float", + 33329: "r8sint", + 33330: "r8uint", + 33331: "r16sint", + 33332: "r16uint", + 33333: "r32sint", + 33334: "r32uint", + 33335: "rg8sint", + 33336: "rg8uint", + 33337: "rg16sint", + 33338: "rg16uint", + 33339: "rg32sint", + 33340: "rg32uint", + 33778: "bc2-rgba-unorm", + 33779: "bc3-rgba-unorm", + 34836: "rgba32float", + 34842: "rgba16float", + 35056: "depth24plus-stencil8", + 35898: "rg11b10ufloat", + 35901: "rgb9e5ufloat", + 35907: "rgba8unorm-srgb", + // bgra8unorm-srgb + 36012: "depth32float", + 36013: "depth32float-stencil8", + 36168: "stencil8", + 36208: "rgba32uint", + 36214: "rgba16uint", + 36220: "rgba8uint", + 36226: "rgba32sint", + 36232: "rgba16sint", + 36238: "rgba8sint", + 36492: "bc7-rgba-unorm", + 36756: "r8snorm", + 36757: "rg8snorm", + 36759: "rgba8snorm", + 37496: "etc2-rgba8unorm", + 37808: "astc-4x4-unorm" + }; + function glFormatToGPUFormat(glInternalFormat) { + const format = glFormatToGPUFormatMap[glInternalFormat]; + if (format) { + return format; + } + throw new Error(`Unsupported glInternalFormat: ${glInternalFormat}`); + } + + "use strict"; + const vkFormatToGPUFormatMap = { + 23: "rgb8unorm", + // VK_FORMAT_R8G8B8_UNORM + 37: "rgba8unorm", + // VK_FORMAT_R8G8B8A8_UNORM + 43: "rgba8unorm-srgb" + // VK_FORMAT_R8G8B8A8_SRGB + // TODO add more! + }; + function vkFormatToGPUFormat(vkFormat) { + const format = vkFormatToGPUFormatMap[vkFormat]; + if (format) { + return format; + } + throw new Error(`Unsupported VkFormat: ${vkFormat}`); + } + + "use strict"; + function getTextureFormatFromKTXTexture(ktxTexture) { + if (ktxTexture.classId === 2) { + return vkFormatToGPUFormat(ktxTexture.vkFormat); + } + return glFormatToGPUFormat(ktxTexture.glInternalformat); + } + + "use strict"; + const gpuFormatToBasisTranscoderFormatMap = { + "bc3-rgba-unorm": "BC3_RGBA", + "bc7-rgba-unorm": "BC7_M5_RGBA", + "etc2-rgba8unorm": "ETC2_RGBA", + "astc-4x4-unorm": "ASTC_4x4_RGBA", + // Uncompressed + rgba8unorm: "RGBA32", + rg11b10ufloat: "R11F_G11F_B10F" + }; + function gpuFormatToKTXBasisTranscoderFormat(transcoderFormat) { + const format = gpuFormatToBasisTranscoderFormatMap[transcoderFormat]; + if (format) { + return format; + } + throw new Error(`Unsupported transcoderFormat: ${transcoderFormat}`); + } + + "use strict"; + const validFormats = ["basis", "bc7", "bc6h", "astc", "etc2", "bc5", "bc4", "bc3", "bc2", "bc1", "eac"]; + const resolveCompressedTextureUrl = { + extension: ExtensionType.ResolveParser, + test: (value) => checkExtension(value, [".ktx", ".ktx2", ".dds"]), + parse: (value) => { + var _a, _b; + let format; + const splitValue = value.split("."); + if (splitValue.length > 2) { + const newFormat = splitValue[splitValue.length - 2]; + if (validFormats.includes(newFormat)) { + format = newFormat; + } + } else { + format = splitValue[splitValue.length - 1]; + } + return { + resolution: parseFloat((_b = (_a = Resolver.RETINA_PREFIX.exec(value)) == null ? void 0 : _a[1]) != null ? _b : "1"), + format, + src: value + }; + } + }; + + "use strict"; + let compressedTextureExtensions; + const detectCompressed = { + extension: { + type: ExtensionType.DetectionParser, + priority: 2 + }, + test: async () => { + if (await isWebGPUSupported()) return true; + if (isWebGLSupported()) return true; + return false; + }, + add: async (formats) => { + const supportedCompressedTextureFormats = await getSupportedCompressedTextureFormats(); + compressedTextureExtensions = extractExtensionsForCompressedTextureFormats(supportedCompressedTextureFormats); + return [...compressedTextureExtensions, ...formats]; + }, + remove: async (formats) => { + if (compressedTextureExtensions) { + return formats.filter((f) => !(f in compressedTextureExtensions)); + } + return formats; + } + }; + function extractExtensionsForCompressedTextureFormats(formats) { + const extensions = ["basis"]; + const dupeMap = {}; + formats.forEach((format) => { + const extension = format.split("-")[0]; + if (extension && !dupeMap[extension]) { + dupeMap[extension] = true; + extensions.push(extension); + } + }); + extensions.sort((a, b) => { + const aIndex = validFormats.indexOf(a); + const bIndex = validFormats.indexOf(b); + if (aIndex === -1) { + return 1; + } + if (bIndex === -1) { + return -1; + } + return aIndex - bIndex; + }); + return extensions; + } + + "use strict"; + + "use strict"; + const tempBounds$3 = new Bounds(); + const tempMatrix$4 = new Matrix(); + const tempRectangle = new Rectangle(); + const _Culler = class _Culler { + /** + * Culls the children of a specific container based on the given view rectangle. + * This determines which objects should be rendered and which can be skipped. + * @param container - The container to cull. Must be a Container instance. + * @param view - The view rectangle that defines the visible area + * @param skipUpdateTransform - Whether to skip updating transforms for better performance + * @example + * ```ts + * // Basic culling with view bounds + * const culler = new Culler(); + * culler.cull(stage, { + * x: 0, + * y: 0, + * width: 800, + * height: 600 + * }); + * + * // Culling to renderer screen + * culler.cull(stage, renderer.screen, false); + * ``` + * @remarks + * - Recursively processes all cullable children + * - Uses cullArea if defined, otherwise calculates bounds + * - Performance depends on scene complexity + * @see {@link CullingMixinConstructor.cullable} For enabling culling on objects + * @see {@link CullingMixinConstructor.cullArea} For custom culling boundaries + */ + cull(container, view, skipUpdateTransform = true) { + this._cullRecursive(container, view, skipUpdateTransform); + } + _cullRecursive(container, view, skipUpdateTransform = true) { + if (container.cullable && container.measurable && container.includeInBuild) { + if (container.cullArea) { + tempRectangle.x = view.x; + tempRectangle.y = view.y; + tempRectangle.width = view.width; + tempRectangle.height = view.height; + const transform = skipUpdateTransform ? container.worldTransform : container.getGlobalTransform(tempMatrix$4, skipUpdateTransform); + container.culled = !tempRectangle.intersects( + container.cullArea, + transform + ); + } else { + const bounds = getGlobalBounds(container, skipUpdateTransform, tempBounds$3); + container.culled = bounds.x >= view.x + view.width || bounds.y >= view.y + view.height || bounds.x + bounds.width <= view.x || bounds.y + bounds.height <= view.y; + } + } else { + container.culled = false; + } + if (!container.cullableChildren || container.culled || !container.renderable || !container.measurable || !container.includeInBuild) return; + for (let i = 0; i < container.children.length; i++) { + this._cullRecursive(container.children[i], view, skipUpdateTransform); + } + } + }; + /** + * A shared instance of the Culler class. Provides a global culler instance for convenience. + * @example + * ```ts + * // Use the shared instance instead of creating a new one + * Culler.shared.cull(stage, { + * x: 0, + * y: 0, + * width: 800, + * height: 600 + * }); + * ``` + * @see {@link CullerPlugin} For automatic culling using this instance + */ + _Culler.shared = new _Culler(); + let Culler = _Culler; + + "use strict"; + class CullerPlugin { + /** + * Initialize the plugin with scope of application instance + * @private + * @param {object} [options] - See application options + */ + static init(options) { + this._renderRef = this.render.bind(this); + this.render = () => { + var _a; + const updateTransform = ((_a = options == null ? void 0 : options.culler) == null ? void 0 : _a.updateTransform) !== true; + Culler.shared.cull(this.stage, this.renderer.screen, updateTransform); + this.renderer.render({ container: this.stage }); + }; + } + /** @internal */ + static destroy() { + this.render = this._renderRef; + } + } + /** @ignore */ + CullerPlugin.extension = { + priority: 10, + type: ExtensionType.Application, + name: "culler" + }; + + "use strict"; + + "use strict"; + class DOMPipe { + /** + * Constructor for the DOMPipe class. + * @param renderer - The renderer instance that this DOMPipe will be associated with. + */ + constructor(renderer) { + /** Array to keep track of attached DOM elements */ + this._attachedDomElements = []; + this._renderer = renderer; + this._renderer.runners.postrender.add(this); + this._renderer.runners.init.add(this); + this._domElement = document.createElement("div"); + this._domElement.style.position = "absolute"; + this._domElement.style.top = "0"; + this._domElement.style.left = "0"; + this._domElement.style.pointerEvents = "none"; + this._domElement.style.zIndex = "1000"; + } + /** Initializes the DOMPipe, setting up the main DOM element and adding it to the document body. */ + init() { + this._canvasObserver = new CanvasObserver({ + domElement: this._domElement, + renderer: this._renderer + }); + } + /** + * Adds a renderable DOM container to the list of attached elements. + * @param domContainer - The DOM container to be added. + * @param _instructionSet - The instruction set (unused). + */ + addRenderable(domContainer, _instructionSet) { + if (!this._attachedDomElements.includes(domContainer)) { + this._attachedDomElements.push(domContainer); + } + } + /** + * Updates a renderable DOM container. + * @param _domContainer - The DOM container to be updated (unused). + */ + updateRenderable(_domContainer) { + } + /** + * Validates a renderable DOM container. + * @param _domContainer - The DOM container to be validated (unused). + * @returns Always returns true as validation is not required. + */ + validateRenderable(_domContainer) { + return true; + } + /** Handles the post-rendering process, ensuring DOM elements are correctly positioned and visible. */ + postrender() { + const attachedDomElements = this._attachedDomElements; + if (attachedDomElements.length === 0) { + this._domElement.remove(); + return; + } + this._canvasObserver.ensureAttached(); + for (let i = 0; i < attachedDomElements.length; i++) { + const domContainer = attachedDomElements[i]; + const element = domContainer.element; + if (!domContainer.parent || domContainer.globalDisplayStatus < 7) { + element == null ? void 0 : element.remove(); + attachedDomElements.splice(i, 1); + i--; + } else { + if (!this._domElement.contains(element)) { + element.style.position = "absolute"; + element.style.pointerEvents = "auto"; + this._domElement.appendChild(element); + } + const wt = domContainer.worldTransform; + const anchor = domContainer._anchor; + const ax = domContainer.width * anchor.x; + const ay = domContainer.height * anchor.y; + element.style.transformOrigin = `${ax}px ${ay}px`; + element.style.transform = `matrix(${wt.a}, ${wt.b}, ${wt.c}, ${wt.d}, ${wt.tx - ax}, ${wt.ty - ay})`; + element.style.opacity = domContainer.groupAlpha.toString(); + } + } + } + /** Destroys the DOMPipe, removing all attached DOM elements and cleaning up resources. */ + destroy() { + var _a; + this._renderer.runners.postrender.remove(this); + for (let i = 0; i < this._attachedDomElements.length; i++) { + const domContainer = this._attachedDomElements[i]; + (_a = domContainer.element) == null ? void 0 : _a.remove(); + } + this._attachedDomElements.length = 0; + this._domElement.remove(); + this._canvasObserver.destroy(); + this._renderer = null; + } + } + /** + * Static property defining the extension type and name for the DOMPipe. + * This is used to register the DOMPipe with different rendering pipelines. + */ + DOMPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "dom" + }; + + "use strict"; + var __defProp$R = Object.defineProperty; + var __getOwnPropSymbols$T = Object.getOwnPropertySymbols; + var __hasOwnProp$T = Object.prototype.hasOwnProperty; + var __propIsEnum$T = Object.prototype.propertyIsEnumerable; + var __defNormalProp$R = (obj, key, value) => key in obj ? __defProp$R(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$R = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$T.call(b, prop)) + __defNormalProp$R(a, prop, b[prop]); + if (__getOwnPropSymbols$T) + for (var prop of __getOwnPropSymbols$T(b)) { + if (__propIsEnum$T.call(b, prop)) + __defNormalProp$R(a, prop, b[prop]); + } + return a; + }; + var __objRest$l = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$T.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$T) + for (var prop of __getOwnPropSymbols$T(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$T.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + extensions.add(DOMPipe); + class DOMContainer extends ViewContainer { + /** + * @param options - The options for creating the DOM container. + */ + constructor(options = {}) { + const _a = options, { element, anchor } = _a, rest = __objRest$l(_a, ["element", "anchor"]); + super(__spreadValues$R({ + label: "DOMContainer" + }, rest)); + /** @internal */ + this.renderPipeId = "dom"; + /** @internal */ + this.batched = false; + this._anchor = new Point(0, 0); + if (anchor) { + this.anchor = anchor; + } + this.element = options.element || document.createElement("div"); + } + /** + * The anchor sets the origin point of the container. + * Controls the relative positioning of the DOM element. + * + * The default is `(0,0)`, this means the container's origin is the top left. + * Setting the anchor to `(0.5,0.5)` means the container's origin is centered. + * Setting the anchor to `(1,1)` would mean the container's origin point will be the bottom right corner. + * @example + * ```ts + * const container = new DOMContainer(); + * + * // Set anchor to center (shorthand) + * container.anchor = 0.5; + * + * // Set anchor to bottom-right + * container.anchor = { x: 1, y: 1 }; + * + * // Set anchor to custom position + * container.anchor = new Point(0.3, 0.7); + * ``` + */ + get anchor() { + return this._anchor; + } + /** + * Sets the anchor point of the container. + * @param value - New anchor value: + * - number: Sets both x and y to same value + * - PointData: Sets x and y separately + */ + set anchor(value) { + typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value); + } + /** + * Sets the DOM element for this container. + * This will replace the current element and update the view. + * @param value - The new DOM element to use + * @example + * ```ts + * const domContainer = new DOMContainer(); + * domContainer.element = document.createElement('input'); + * ``` + */ + set element(value) { + if (this._element === value) return; + this._element = value; + this.onViewUpdate(); + } + /** + * The DOM element associated with this container. + * @example + * ```ts + * const domContainer = new DOMContainer(); + * domContainer.element.innerHTML = 'Hello World!'; + * document.body.appendChild(domContainer.element); + * ``` + */ + get element() { + return this._element; + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const element = this._element; + if (!element) { + bounds.minX = 0; + bounds.minY = 0; + bounds.maxX = 0; + bounds.maxY = 0; + return; + } + const { offsetWidth, offsetHeight } = element; + bounds.minX = 0; + bounds.maxX = offsetWidth; + bounds.minY = 0; + bounds.maxY = offsetHeight; + } + /** + * Destroys this DOM container. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that + * @example + * domContainer.destroy(); + * domContainer.destroy(true); + */ + destroy(options = false) { + var _a, _b; + super.destroy(options); + (_b = (_a = this._element) == null ? void 0 : _a.parentNode) == null ? void 0 : _b.removeChild(this._element); + this._element = null; + this._anchor = null; + } + } + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + const browserExt = { + extension: { + type: ExtensionType.Environment, + name: "browser", + priority: -1 + }, + test: () => true, + load: async () => { + await Promise.resolve().then(function () { return browserAll; }); + } + }; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + var blendTemplateFrag = "\nin vec2 vTextureCoord;\nin vec4 vColor;\n\nout vec4 finalColor;\n\nuniform float uBlend;\n\nuniform sampler2D uTexture;\nuniform sampler2D uBackTexture;\n\n{FUNCTIONS}\n\nvoid main()\n{ \n vec4 back = texture(uBackTexture, vTextureCoord);\n vec4 front = texture(uTexture, vTextureCoord);\n float blendedAlpha = front.a + back.a * (1.0 - front.a);\n \n {MAIN}\n}\n"; + + var blendTemplateVert = "in vec2 aPosition;\nout vec2 vTextureCoord;\nout vec2 backgroundUv;\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n \n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n"; + + var blendTemplate = "\nstruct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct BlendUniforms {\n uBlend:f32,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n@group(0) @binding(3) var uBackTexture: texture_2d;\n\n@group(1) @binding(0) var blendUniforms : BlendUniforms;\n\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2\n };\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); \n}\n \n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition)\n );\n}\n\n{FUNCTIONS}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2\n) -> @location(0) vec4 {\n\n\n var back = textureSample(uBackTexture, uSampler, uv);\n var front = textureSample(uTexture, uSampler, uv);\n var blendedAlpha = front.a + back.a * (1.0 - front.a);\n \n var out = vec4(0.0,0.0,0.0,0.0);\n\n {MAIN}\n\n return out;\n}"; + + "use strict"; + var __defProp$Q = Object.defineProperty; + var __getOwnPropSymbols$S = Object.getOwnPropertySymbols; + var __hasOwnProp$S = Object.prototype.hasOwnProperty; + var __propIsEnum$S = Object.prototype.propertyIsEnumerable; + var __defNormalProp$Q = (obj, key, value) => key in obj ? __defProp$Q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$Q = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$S.call(b, prop)) + __defNormalProp$Q(a, prop, b[prop]); + if (__getOwnPropSymbols$S) + for (var prop of __getOwnPropSymbols$S(b)) { + if (__propIsEnum$S.call(b, prop)) + __defNormalProp$Q(a, prop, b[prop]); + } + return a; + }; + class BlendModeFilter extends Filter { + constructor(options) { + const gpuOptions = options.gpu; + const gpuSource = compileBlendModeShader(__spreadValues$Q({ source: blendTemplate }, gpuOptions)); + const gpuProgram = GpuProgram.from({ + vertex: { + source: gpuSource, + entryPoint: "mainVertex" + }, + fragment: { + source: gpuSource, + entryPoint: "mainFragment" + } + }); + const glOptions = options.gl; + const glSource = compileBlendModeShader(__spreadValues$Q({ source: blendTemplateFrag }, glOptions)); + const glProgram = GlProgram.from({ + vertex: blendTemplateVert, + fragment: glSource + }); + const uniformGroup = new UniformGroup({ + uBlend: { + value: 1, + type: "f32" + } + }); + super({ + gpuProgram, + glProgram, + blendRequired: true, + resources: { + blendUniforms: uniformGroup, + uBackTexture: Texture.EMPTY + } + }); + } + } + function compileBlendModeShader(options) { + const { source, functions, main } = options; + return source.replace("{FUNCTIONS}", functions).replace("{MAIN}", main); + } + + "use strict"; + const hslgl = ` + float getLuminosity(vec3 c) { + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; + } + + vec3 setLuminosity(vec3 c, float lum) { + float modLum = lum - getLuminosity(c); + vec3 color = c.rgb + vec3(modLum); + + // clip back into legal range + modLum = getLuminosity(color); + vec3 modLumVec = vec3(modLum); + + float cMin = min(color.r, min(color.g, color.b)); + float cMax = max(color.r, max(color.g, color.b)); + + if(cMin < 0.0) { + color = mix(modLumVec, color, modLum / (modLum - cMin)); + } + + if(cMax > 1.0) { + color = mix(modLumVec, color, (1.0 - modLum) / (cMax - modLum)); + } + + return color; + } + + float getSaturation(vec3 c) { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); + } + + vec3 setSaturationMinMidMax(vec3 cSorted, float s) { + vec3 colorSorted = cSorted; + + if(colorSorted.z > colorSorted.x) { + colorSorted.y = (((colorSorted.y - colorSorted.x) * s) / (colorSorted.z - colorSorted.x)); + colorSorted.z = s; + } + else { + colorSorted.y = 0.0; + colorSorted.z = 0.0; + } + + colorSorted.x = 0.0; + + return colorSorted; + } + + vec3 setSaturation(vec3 c, float s) { + vec3 color = c; + + if(color.r <= color.g && color.r <= color.b) { + if(color.g <= color.b) { + color = setSaturationMinMidMax(color.rgb, s).rgb; + } + else { + color = setSaturationMinMidMax(color.rbg, s).rbg; + } + } + else if(color.g <= color.r && color.g <= color.b) { + if(color.r <= color.b) { + color = setSaturationMinMidMax(color.grb, s).grb; + } + else { + color = setSaturationMinMidMax(color.gbr, s).gbr; + } + } + else { + // Using bgr for both fixes part of hue + if(color.r <= color.g) { + color = setSaturationMinMidMax(color.brg, s).brg; + } + else { + color = setSaturationMinMidMax(color.bgr, s).bgr; + } + } + + return color; + } + `; + + "use strict"; + const hslgpu = ` + fn getLuminosity(c: vec3) -> f32 + { + return 0.3*c.r + 0.59*c.g + 0.11*c.b; + } + + fn setLuminosity(c: vec3, lum: f32) -> vec3 + { + var modLum: f32 = lum - getLuminosity(c); + var color: vec3 = c.rgb + modLum; + + // clip back into legal range + modLum = getLuminosity(color); + let modLumVec = vec3(modLum); + + let cMin: f32 = min(color.r, min(color.g, color.b)); + let cMax: f32 = max(color.r, max(color.g, color.b)); + + if(cMin < 0.0) + { + color = mix(modLumVec, color, modLum / (modLum - cMin)); + } + + if(cMax > 1.0) + { + color = mix(modLumVec, color, (1 - modLum) / (cMax - modLum)); + } + + return color; + } + + fn getSaturation(c: vec3) -> f32 + { + return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); + } + + fn setSaturationMinMidMax(cSorted: vec3, s: f32) -> vec3 + { + var colorSorted = cSorted; + + if(colorSorted.z > colorSorted.x) + { + colorSorted.y = (((colorSorted.y - colorSorted.x) * s) / (colorSorted.z - colorSorted.x)); + colorSorted.z = s; + } + else + { + colorSorted.y = 0; + colorSorted.z = 0; + } + + colorSorted.x = 0; + + return colorSorted; + } + + fn setSaturation(c: vec3, s: f32) -> vec3 + { + var color = c; + + if (color.r <= color.g && color.r <= color.b) + { + if (color.g <= color.b) + { + color = vec3(setSaturationMinMidMax(color.rgb, s)).rgb; + } + else + { + color = vec3(setSaturationMinMidMax(color.rbg, s)).rbg; + } + } + else if (color.g <= color.r && color.g <= color.b) + { + if (color.r <= color.b) + { + color = vec3(setSaturationMinMidMax(color.grb, s)).grb; + } + else + { + color = vec3(setSaturationMinMidMax(color.gbr, s)).gbr; + } + } + else + { + // Using bgr for both fixes part of hue + if (color.r <= color.g) + { + color = vec3(setSaturationMinMidMax(color.brg, s)).brg; + } + else + { + color = vec3(setSaturationMinMidMax(color.bgr, s)).bgr; + } + } + + return color; + } + `; + + var fragment$5 = "\nin vec2 vTextureCoord;\n\nout vec4 finalColor;\n\nuniform float uAlpha;\nuniform sampler2D uTexture;\n\nvoid main()\n{\n finalColor = texture(uTexture, vTextureCoord) * uAlpha;\n}\n"; + + var source$5 = "struct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct AlphaUniforms {\n uAlpha:f32,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n\n@group(1) @binding(0) var alphaUniforms : AlphaUniforms;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2\n };\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); \n}\n\nfn getSize() -> vec2\n{\n return gfu.uGlobalFrame.zw;\n}\n \n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition)\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n @builtin(position) position: vec4\n) -> @location(0) vec4 {\n \n var sample = textureSample(uTexture, uSampler, uv);\n \n return sample * alphaUniforms.uAlpha;\n}"; + + "use strict"; + var __defProp$P = Object.defineProperty; + var __defProps$p = Object.defineProperties; + var __getOwnPropDescs$p = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$R = Object.getOwnPropertySymbols; + var __hasOwnProp$R = Object.prototype.hasOwnProperty; + var __propIsEnum$R = Object.prototype.propertyIsEnumerable; + var __defNormalProp$P = (obj, key, value) => key in obj ? __defProp$P(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$P = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$R.call(b, prop)) + __defNormalProp$P(a, prop, b[prop]); + if (__getOwnPropSymbols$R) + for (var prop of __getOwnPropSymbols$R(b)) { + if (__propIsEnum$R.call(b, prop)) + __defNormalProp$P(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$p = (a, b) => __defProps$p(a, __getOwnPropDescs$p(b)); + var __objRest$k = (source2, exclude) => { + var target = {}; + for (var prop in source2) + if (__hasOwnProp$R.call(source2, prop) && exclude.indexOf(prop) < 0) + target[prop] = source2[prop]; + if (source2 != null && __getOwnPropSymbols$R) + for (var prop of __getOwnPropSymbols$R(source2)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$R.call(source2, prop)) + target[prop] = source2[prop]; + } + return target; + }; + const _AlphaFilter = class _AlphaFilter extends Filter { + constructor(options) { + options = __spreadValues$P(__spreadValues$P({}, _AlphaFilter.defaultOptions), options); + const gpuProgram = GpuProgram.from({ + vertex: { + source: source$5, + entryPoint: "mainVertex" + }, + fragment: { + source: source$5, + entryPoint: "mainFragment" + } + }); + const glProgram = GlProgram.from({ + vertex: vertex$3, + fragment: fragment$5, + name: "alpha-filter" + }); + const _a = options, { alpha } = _a, rest = __objRest$k(_a, ["alpha"]); + const alphaUniforms = new UniformGroup({ + uAlpha: { value: alpha, type: "f32" } + }); + super(__spreadProps$p(__spreadValues$P({}, rest), { + gpuProgram, + glProgram, + resources: { + alphaUniforms + } + })); + } + /** + * The alpha value of the filter. + * Controls the transparency of the filtered display object. + * @example + * ```ts + * // Create filter with initial alpha + * const filter = new AlphaFilter({ alpha: 0.5 }); + * + * // Update alpha value dynamically + * filter.alpha = 0.8; + * ``` + * @default 1 + * @remarks + * - 0 = fully transparent + * - 1 = fully opaque + * - Values are clamped between 0 and 1 + */ + get alpha() { + return this.resources.alphaUniforms.uniforms.uAlpha; + } + set alpha(value) { + this.resources.alphaUniforms.uniforms.uAlpha = value; + } + }; + /** + * Default options for the AlphaFilter. + * @example + * ```ts + * AlphaFilter.defaultOptions = { + * alpha: 0.5, // Default alpha value + * }; + * // Use default options + * const filter = new AlphaFilter(); // Uses default alpha of 0.5 + * ``` + */ + _AlphaFilter.defaultOptions = { + /** + * Amount of alpha transparency to apply. + * - 0 = fully transparent + * - 1 = fully opaque (default) + * @default 1 + */ + alpha: 1 + }; + let AlphaFilter = _AlphaFilter; + + "use strict"; + const GAUSSIAN_VALUES = { + 5: [0.153388, 0.221461, 0.250301], + 7: [0.071303, 0.131514, 0.189879, 0.214607], + 9: [0.028532, 0.067234, 0.124009, 0.179044, 0.20236], + 11: [93e-4, 0.028002, 0.065984, 0.121703, 0.175713, 0.198596], + 13: [2406e-6, 9255e-6, 0.027867, 0.065666, 0.121117, 0.174868, 0.197641], + 15: [489e-6, 2403e-6, 9246e-6, 0.02784, 0.065602, 0.120999, 0.174697, 0.197448] + }; + + "use strict"; + const fragTemplate = [ + "in vec2 vBlurTexCoords[%size%];", + "uniform sampler2D uTexture;", + "out vec4 finalColor;", + "void main(void)", + "{", + " finalColor = vec4(0.0);", + " %blur%", + "}" + ].join("\n"); + function generateBlurFragSource(kernelSize) { + const kernel = GAUSSIAN_VALUES[kernelSize]; + const halfLength = kernel.length; + let fragSource = fragTemplate; + let blurLoop = ""; + const template = "finalColor += texture(uTexture, vBlurTexCoords[%index%]) * %value%;"; + let value; + for (let i = 0; i < kernelSize; i++) { + let blur = template.replace("%index%", i.toString()); + value = i; + if (i >= halfLength) { + value = kernelSize - i - 1; + } + blur = blur.replace("%value%", kernel[value].toString()); + blurLoop += blur; + blurLoop += "\n"; + } + fragSource = fragSource.replace("%blur%", blurLoop); + fragSource = fragSource.replace("%size%", kernelSize.toString()); + return fragSource; + } + + "use strict"; + const vertTemplate = ` + in vec2 aPosition; + + uniform float uStrength; + + out vec2 vBlurTexCoords[%size%]; + + uniform vec4 uInputSize; + uniform vec4 uOutputFrame; + uniform vec4 uOutputTexture; + + vec4 filterVertexPosition( void ) +{ + vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; + + position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; + position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; + + return vec4(position, 0.0, 1.0); +} + + vec2 filterTextureCoord( void ) + { + return aPosition * (uOutputFrame.zw * uInputSize.zw); + } + + void main(void) + { + gl_Position = filterVertexPosition(); + + float pixelStrength = uInputSize.%dimension% * uStrength; + + vec2 textureCoord = filterTextureCoord(); + %blur% + }`; + function generateBlurVertSource(kernelSize, x) { + const halfLength = Math.ceil(kernelSize / 2); + let vertSource = vertTemplate; + let blurLoop = ""; + let template; + if (x) { + template = "vBlurTexCoords[%index%] = textureCoord + vec2(%sampleIndex% * pixelStrength, 0.0);"; + } else { + template = "vBlurTexCoords[%index%] = textureCoord + vec2(0.0, %sampleIndex% * pixelStrength);"; + } + for (let i = 0; i < kernelSize; i++) { + let blur = template.replace("%index%", i.toString()); + blur = blur.replace("%sampleIndex%", `${i - (halfLength - 1)}.0`); + blurLoop += blur; + blurLoop += "\n"; + } + vertSource = vertSource.replace("%blur%", blurLoop); + vertSource = vertSource.replace("%size%", kernelSize.toString()); + vertSource = vertSource.replace("%dimension%", x ? "z" : "w"); + return vertSource; + } + + "use strict"; + function generateBlurGlProgram(horizontal, kernelSize) { + const vertex = generateBlurVertSource(kernelSize, horizontal); + const fragment = generateBlurFragSource(kernelSize); + return GlProgram.from({ + vertex, + fragment, + name: `blur-${horizontal ? "horizontal" : "vertical"}-pass-filter` + }); + } + + var source$4 = "\n\nstruct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct BlurUniforms {\n uStrength:f32,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n\n@group(1) @binding(0) var blurUniforms : BlurUniforms;\n\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n %blur-struct%\n };\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); \n}\n\nfn getSize() -> vec2\n{\n return gfu.uGlobalFrame.zw;\n}\n\n\n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n\n let filteredCord = filterTextureCoord(aPosition);\n\n let pixelStrength = gfu.uInputSize.%dimension% * blurUniforms.uStrength;\n\n return VSOutput(\n filterVertexPosition(aPosition),\n %blur-vertex-out%\n );\n}\n\n@fragment\nfn mainFragment(\n @builtin(position) position: vec4,\n %blur-fragment-in%\n) -> @location(0) vec4 {\n\n var finalColor = vec4(0.0);\n\n %blur-sampling%\n\n return finalColor;\n}"; + + "use strict"; + function generateBlurProgram(horizontal, kernelSize) { + const kernel = GAUSSIAN_VALUES[kernelSize]; + const halfLength = kernel.length; + const blurStructSource = []; + const blurOutSource = []; + const blurSamplingSource = []; + for (let i = 0; i < kernelSize; i++) { + blurStructSource[i] = `@location(${i}) offset${i}: vec2,`; + if (horizontal) { + blurOutSource[i] = `filteredCord + vec2(${i - halfLength + 1} * pixelStrength, 0.0),`; + } else { + blurOutSource[i] = `filteredCord + vec2(0.0, ${i - halfLength + 1} * pixelStrength),`; + } + const kernelIndex = i < halfLength ? i : kernelSize - i - 1; + const kernelValue = kernel[kernelIndex].toString(); + blurSamplingSource[i] = `finalColor += textureSample(uTexture, uSampler, offset${i}) * ${kernelValue};`; + } + const blurStruct = blurStructSource.join("\n"); + const blurOut = blurOutSource.join("\n"); + const blurSampling = blurSamplingSource.join("\n"); + const finalSource = source$4.replace("%blur-struct%", blurStruct).replace("%blur-vertex-out%", blurOut).replace("%blur-fragment-in%", blurStruct).replace("%blur-sampling%", blurSampling).replace("%dimension%", horizontal ? "z" : "w"); + return GpuProgram.from({ + vertex: { + source: finalSource, + entryPoint: "mainVertex" + }, + fragment: { + source: finalSource, + entryPoint: "mainFragment" + } + }); + } + + "use strict"; + var __defProp$O = Object.defineProperty; + var __getOwnPropSymbols$Q = Object.getOwnPropertySymbols; + var __hasOwnProp$Q = Object.prototype.hasOwnProperty; + var __propIsEnum$Q = Object.prototype.propertyIsEnumerable; + var __defNormalProp$O = (obj, key, value) => key in obj ? __defProp$O(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$O = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$Q.call(b, prop)) + __defNormalProp$O(a, prop, b[prop]); + if (__getOwnPropSymbols$Q) + for (var prop of __getOwnPropSymbols$Q(b)) { + if (__propIsEnum$Q.call(b, prop)) + __defNormalProp$O(a, prop, b[prop]); + } + return a; + }; + const _BlurFilterPass = class _BlurFilterPass extends Filter { + /** + * @param options + * @param options.horizontal - Do pass along the x-axis (`true`) or y-axis (`false`). + * @param options.strength - The strength of the blur filter. + * @param options.quality - The quality of the blur filter. + * @param options.kernelSize - The kernelSize of the blur filter.Options: 5, 7, 9, 11, 13, 15. + */ + constructor(options) { + options = __spreadValues$O(__spreadValues$O({}, _BlurFilterPass.defaultOptions), options); + const glProgram = generateBlurGlProgram(options.horizontal, options.kernelSize); + const gpuProgram = generateBlurProgram(options.horizontal, options.kernelSize); + super(__spreadValues$O({ + glProgram, + gpuProgram, + resources: { + blurUniforms: { + uStrength: { value: 0, type: "f32" } + } + } + }, options)); + this.horizontal = options.horizontal; + this._quality = 0; + this.quality = options.quality; + this.blur = options.strength; + this._uniforms = this.resources.blurUniforms.uniforms; + } + /** + * Applies the filter. + * @param filterManager - The manager. + * @param input - The input target. + * @param output - The output target. + * @param clearMode - How to clear + */ + apply(filterManager, input, output, clearMode) { + this._uniforms.uStrength = this.strength / this.passes; + if (this.passes === 1) { + filterManager.applyFilter(this, input, output, clearMode); + } else { + const tempTexture = TexturePool.getSameSizeTexture(input); + let flip = input; + let flop = tempTexture; + this._state.blend = false; + const shouldClear = filterManager.renderer.type === RendererType.WEBGPU; + for (let i = 0; i < this.passes - 1; i++) { + filterManager.applyFilter(this, flip, flop, i === 0 ? true : shouldClear); + const temp = flop; + flop = flip; + flip = temp; + } + this._state.blend = true; + filterManager.applyFilter(this, flip, output, clearMode); + TexturePool.returnTexture(tempTexture); + } + } + /** + * Sets the strength of both the blur. + * @default 16 + */ + get blur() { + return this.strength; + } + set blur(value) { + this.padding = 1 + Math.abs(value) * 2; + this.strength = value; + } + /** + * Sets the quality of the blur by modifying the number of passes. More passes means higher + * quality blurring but the lower the performance. + * @default 4 + */ + get quality() { + return this._quality; + } + set quality(value) { + this._quality = value; + this.passes = value; + } + }; + /** Default blur filter pass options */ + _BlurFilterPass.defaultOptions = { + /** The strength of the blur filter. */ + strength: 8, + /** The quality of the blur filter. */ + quality: 4, + /** The kernelSize of the blur filter.Options: 5, 7, 9, 11, 13, 15. */ + kernelSize: 5 + }; + let BlurFilterPass = _BlurFilterPass; + + "use strict"; + var __defProp$N = Object.defineProperty; + var __defProps$o = Object.defineProperties; + var __getOwnPropDescs$o = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$P = Object.getOwnPropertySymbols; + var __hasOwnProp$P = Object.prototype.hasOwnProperty; + var __propIsEnum$P = Object.prototype.propertyIsEnumerable; + var __defNormalProp$N = (obj, key, value) => key in obj ? __defProp$N(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$N = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$P.call(b, prop)) + __defNormalProp$N(a, prop, b[prop]); + if (__getOwnPropSymbols$P) + for (var prop of __getOwnPropSymbols$P(b)) { + if (__propIsEnum$P.call(b, prop)) + __defNormalProp$N(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$o = (a, b) => __defProps$o(a, __getOwnPropDescs$o(b)); + var __objRest$j = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$P.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$P) + for (var prop of __getOwnPropSymbols$P(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$P.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class BlurFilter extends Filter { + constructor(...args) { + var _a; + let options = (_a = args[0]) != null ? _a : {}; + if (typeof options === "number") { + deprecation(v8_0_0, "BlurFilter constructor params are now options object. See params: { strength, quality, resolution, kernelSize }"); + options = { strength: options }; + if (args[1] !== void 0) options.quality = args[1]; + if (args[2] !== void 0) options.resolution = args[2] || "inherit"; + if (args[3] !== void 0) options.kernelSize = args[3]; + } + options = __spreadValues$N(__spreadValues$N({}, BlurFilterPass.defaultOptions), options); + const _b = options, { strength, strengthX, strengthY, quality } = _b, rest = __objRest$j(_b, ["strength", "strengthX", "strengthY", "quality"]); + super(__spreadProps$o(__spreadValues$N({}, rest), { + compatibleRenderers: RendererType.BOTH, + resources: {} + })); + this._repeatEdgePixels = false; + this.blurXFilter = new BlurFilterPass(__spreadValues$N({ horizontal: true }, options)); + this.blurYFilter = new BlurFilterPass(__spreadValues$N({ horizontal: false }, options)); + this.quality = quality; + this.strengthX = strengthX != null ? strengthX : strength; + this.strengthY = strengthY != null ? strengthY : strength; + this.repeatEdgePixels = false; + } + /** + * Applies the filter. + * @param filterManager - The manager. + * @param input - The input target. + * @param output - The output target. + * @param clearMode - How to clear + * @advanced + */ + apply(filterManager, input, output, clearMode) { + const xStrength = Math.abs(this.blurXFilter.strength); + const yStrength = Math.abs(this.blurYFilter.strength); + if (xStrength && yStrength) { + const tempTexture = TexturePool.getSameSizeTexture(input); + this.blurXFilter.blendMode = "normal"; + this.blurXFilter.apply(filterManager, input, tempTexture, true); + this.blurYFilter.blendMode = this.blendMode; + this.blurYFilter.apply(filterManager, tempTexture, output, clearMode); + TexturePool.returnTexture(tempTexture); + } else if (yStrength) { + this.blurYFilter.blendMode = this.blendMode; + this.blurYFilter.apply(filterManager, input, output, clearMode); + } else { + this.blurXFilter.blendMode = this.blendMode; + this.blurXFilter.apply(filterManager, input, output, clearMode); + } + } + updatePadding() { + if (this._repeatEdgePixels) { + this.padding = 0; + } else { + this.padding = Math.max(Math.abs(this.blurXFilter.blur), Math.abs(this.blurYFilter.blur)) * 2; + } + } + /** + * Sets the strength of both the blurX and blurY properties simultaneously. + * Controls the overall intensity of the Gaussian blur effect. + * @example + * ```ts + * // Set equal blur strength for both axes + * filter.strength = 8; + * + * // Will throw error if X and Y are different + * filter.strengthX = 4; + * filter.strengthY = 8; + * filter.strength; // Error: BlurFilter's strengthX and strengthY are different + * ``` + * @default 8 + * @throws {Error} If strengthX and strengthY are different values + */ + get strength() { + if (this.strengthX !== this.strengthY) { + throw new Error("BlurFilter's strengthX and strengthY are different"); + } + return this.strengthX; + } + set strength(value) { + this.blurXFilter.blur = this.blurYFilter.blur = value; + this.updatePadding(); + } + /** + * Sets the number of passes for blur. More passes means higher quality blurring. + * Controls the precision and smoothness of the blur effect at the cost of performance. + * @example + * ```ts + * // High quality blur (slower) + * filter.quality = 8; + * + * // Low quality blur (faster) + * filter.quality = 2; + * ``` + * @default 4 + * @remarks Higher values produce better quality but impact performance + */ + get quality() { + return this.blurXFilter.quality; + } + set quality(value) { + this.blurXFilter.quality = this.blurYFilter.quality = value; + } + /** + * Sets the strength of horizontal blur. + * Controls the blur intensity along the x-axis independently. + * @example + * ```ts + * // Apply horizontal-only blur + * filter.strengthX = 8; + * filter.strengthY = 0; + * + * // Create motion blur effect + * filter.strengthX = 16; + * filter.strengthY = 2; + * ``` + * @default 8 + */ + get strengthX() { + return this.blurXFilter.blur; + } + set strengthX(value) { + this.blurXFilter.blur = value; + this.updatePadding(); + } + /** + * Sets the strength of the vertical blur. + * Controls the blur intensity along the y-axis independently. + * @example + * ```ts + * // Apply vertical-only blur + * filter.strengthX = 0; + * filter.strengthY = 8; + * + * // Create radial blur effect + * filter.strengthX = 8; + * filter.strengthY = 8; + * ``` + * @default 8 + */ + get strengthY() { + return this.blurYFilter.blur; + } + set strengthY(value) { + this.blurYFilter.blur = value; + this.updatePadding(); + } + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * @default 2 + * @deprecated since 8.3.0 + * @see BlurFilter.strength + */ + get blur() { + deprecation("8.3.0", "BlurFilter.blur is deprecated, please use BlurFilter.strength instead."); + return this.strength; + } + set blur(value) { + deprecation("8.3.0", "BlurFilter.blur is deprecated, please use BlurFilter.strength instead."); + this.strength = value; + } + /** + * Sets the strength of the blurX property + * @default 2 + * @deprecated since 8.3.0 + * @see BlurFilter.strengthX + */ + get blurX() { + deprecation("8.3.0", "BlurFilter.blurX is deprecated, please use BlurFilter.strengthX instead."); + return this.strengthX; + } + set blurX(value) { + deprecation("8.3.0", "BlurFilter.blurX is deprecated, please use BlurFilter.strengthX instead."); + this.strengthX = value; + } + /** + * Sets the strength of the blurY property + * @default 2 + * @deprecated since 8.3.0 + * @see BlurFilter.strengthY + */ + get blurY() { + deprecation("8.3.0", "BlurFilter.blurY is deprecated, please use BlurFilter.strengthY instead."); + return this.strengthY; + } + set blurY(value) { + deprecation("8.3.0", "BlurFilter.blurY is deprecated, please use BlurFilter.strengthY instead."); + this.strengthY = value; + } + /** + * If set to true the edge of the target will be clamped + * @default false + */ + get repeatEdgePixels() { + return this._repeatEdgePixels; + } + set repeatEdgePixels(value) { + this._repeatEdgePixels = value; + this.updatePadding(); + } + } + /** + * Default blur filter options + * @example + * ```ts + * // Set default options for all BlurFilters + * BlurFilter.defaultOptions = { + * strength: 10, // Default blur strength + * quality: 2, // Default blur quality + * kernelSize: 7 // Default kernel size + * }; + * // Create a filter with these defaults + * const filter = new BlurFilter(); // Uses default options + * ``` + * @remarks + * - These options are used when creating a new BlurFilter without specific parameters + * - Can be overridden by passing options to the constructor + * - Useful for setting global defaults for all blur filters in your application + * @see {@link BlurFilterOptions} For detailed options + * @see {@link BlurFilter} The filter that uses these options + */ + BlurFilter.defaultOptions = { + /** The strength of the blur filter. */ + strength: 8, + /** The quality of the blur filter. */ + quality: 4, + /** The kernelSize of the blur filter.Options: 5, 7, 9, 11, 13, 15. */ + kernelSize: 5 + }; + + var fragment$4 = "\nin vec2 vTextureCoord;\nin vec4 vColor;\n\nout vec4 finalColor;\n\nuniform float uColorMatrix[20];\nuniform float uAlpha;\n\nuniform sampler2D uTexture;\n\nfloat rand(vec2 co)\n{\n return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvoid main()\n{\n vec4 color = texture(uTexture, vTextureCoord);\n float randomValue = rand(gl_FragCoord.xy * 0.2);\n float diff = (randomValue - 0.5) * 0.5;\n\n if (uAlpha == 0.0) {\n finalColor = color;\n return;\n }\n\n if (color.a > 0.0) {\n color.rgb /= color.a;\n }\n\n vec4 result;\n\n result.r = (uColorMatrix[0] * color.r);\n result.r += (uColorMatrix[1] * color.g);\n result.r += (uColorMatrix[2] * color.b);\n result.r += (uColorMatrix[3] * color.a);\n result.r += uColorMatrix[4];\n\n result.g = (uColorMatrix[5] * color.r);\n result.g += (uColorMatrix[6] * color.g);\n result.g += (uColorMatrix[7] * color.b);\n result.g += (uColorMatrix[8] * color.a);\n result.g += uColorMatrix[9];\n\n result.b = (uColorMatrix[10] * color.r);\n result.b += (uColorMatrix[11] * color.g);\n result.b += (uColorMatrix[12] * color.b);\n result.b += (uColorMatrix[13] * color.a);\n result.b += uColorMatrix[14];\n\n result.a = (uColorMatrix[15] * color.r);\n result.a += (uColorMatrix[16] * color.g);\n result.a += (uColorMatrix[17] * color.b);\n result.a += (uColorMatrix[18] * color.a);\n result.a += uColorMatrix[19];\n\n vec3 rgb = mix(color.rgb, result.rgb, uAlpha);\n\n // Premultiply alpha again.\n rgb *= result.a;\n\n finalColor = vec4(rgb, result.a);\n}\n"; + + var source$3 = "struct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct ColorMatrixUniforms {\n uColorMatrix:array, 5>,\n uAlpha:f32,\n};\n\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n@group(1) @binding(0) var colorMatrixUniforms : ColorMatrixUniforms;\n\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2,\n };\n \nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition),\n );\n}\n\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n) -> @location(0) vec4 {\n\n\n var c = textureSample(uTexture, uSampler, uv);\n \n if (colorMatrixUniforms.uAlpha == 0.0) {\n return c;\n }\n\n \n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (c.a > 0.0) {\n c.r /= c.a;\n c.g /= c.a;\n c.b /= c.a;\n }\n\n var cm = colorMatrixUniforms.uColorMatrix;\n\n\n var result = vec4(0.);\n\n result.r = (cm[0][0] * c.r);\n result.r += (cm[0][1] * c.g);\n result.r += (cm[0][2] * c.b);\n result.r += (cm[0][3] * c.a);\n result.r += cm[1][0];\n\n result.g = (cm[1][1] * c.r);\n result.g += (cm[1][2] * c.g);\n result.g += (cm[1][3] * c.b);\n result.g += (cm[2][0] * c.a);\n result.g += cm[2][1];\n\n result.b = (cm[2][2] * c.r);\n result.b += (cm[2][3] * c.g);\n result.b += (cm[3][0] * c.b);\n result.b += (cm[3][1] * c.a);\n result.b += cm[3][2];\n\n result.a = (cm[3][3] * c.r);\n result.a += (cm[4][0] * c.g);\n result.a += (cm[4][1] * c.b);\n result.a += (cm[4][2] * c.a);\n result.a += cm[4][3];\n\n var rgb = mix(c.rgb, result.rgb, colorMatrixUniforms.uAlpha);\n\n rgb.r *= result.a;\n rgb.g *= result.a;\n rgb.b *= result.a;\n\n return vec4(rgb, result.a);\n}"; + + "use strict"; + var __defProp$M = Object.defineProperty; + var __defProps$n = Object.defineProperties; + var __getOwnPropDescs$n = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$O = Object.getOwnPropertySymbols; + var __hasOwnProp$O = Object.prototype.hasOwnProperty; + var __propIsEnum$O = Object.prototype.propertyIsEnumerable; + var __defNormalProp$M = (obj, key, value) => key in obj ? __defProp$M(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$M = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$O.call(b, prop)) + __defNormalProp$M(a, prop, b[prop]); + if (__getOwnPropSymbols$O) + for (var prop of __getOwnPropSymbols$O(b)) { + if (__propIsEnum$O.call(b, prop)) + __defNormalProp$M(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$n = (a, b) => __defProps$n(a, __getOwnPropDescs$n(b)); + class ColorMatrixFilter extends Filter { + constructor(options = {}) { + const colorMatrixUniforms = new UniformGroup({ + uColorMatrix: { + value: [ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ], + type: "f32", + size: 20 + }, + uAlpha: { + value: 1, + type: "f32" + } + }); + const gpuProgram = GpuProgram.from({ + vertex: { + source: source$3, + entryPoint: "mainVertex" + }, + fragment: { + source: source$3, + entryPoint: "mainFragment" + } + }); + const glProgram = GlProgram.from({ + vertex: vertex$3, + fragment: fragment$4, + name: "color-matrix-filter" + }); + super(__spreadProps$n(__spreadValues$M({}, options), { + gpuProgram, + glProgram, + resources: { + colorMatrixUniforms + } + })); + this.alpha = 1; + } + /** + * Transforms current matrix and set the new one + * @param {number[]} matrix - 5x4 matrix + * @param multiply - if true, current matrix and matrix are multiplied. If false, + * just set the current matrix with matrix + */ + _loadMatrix(matrix, multiply = false) { + let newMatrix = matrix; + if (multiply) { + this._multiply(newMatrix, this.matrix, matrix); + newMatrix = this._colorMatrix(newMatrix); + } + this.resources.colorMatrixUniforms.uniforms.uColorMatrix = newMatrix; + this.resources.colorMatrixUniforms.update(); + } + /** + * Multiplies two mat5's + * @private + * @param out - 5x4 matrix the receiving matrix + * @param a - 5x4 matrix the first operand + * @param b - 5x4 matrix the second operand + * @returns {number[]} 5x4 matrix + */ + _multiply(out, a, b) { + out[0] = a[0] * b[0] + a[1] * b[5] + a[2] * b[10] + a[3] * b[15]; + out[1] = a[0] * b[1] + a[1] * b[6] + a[2] * b[11] + a[3] * b[16]; + out[2] = a[0] * b[2] + a[1] * b[7] + a[2] * b[12] + a[3] * b[17]; + out[3] = a[0] * b[3] + a[1] * b[8] + a[2] * b[13] + a[3] * b[18]; + out[4] = a[0] * b[4] + a[1] * b[9] + a[2] * b[14] + a[3] * b[19] + a[4]; + out[5] = a[5] * b[0] + a[6] * b[5] + a[7] * b[10] + a[8] * b[15]; + out[6] = a[5] * b[1] + a[6] * b[6] + a[7] * b[11] + a[8] * b[16]; + out[7] = a[5] * b[2] + a[6] * b[7] + a[7] * b[12] + a[8] * b[17]; + out[8] = a[5] * b[3] + a[6] * b[8] + a[7] * b[13] + a[8] * b[18]; + out[9] = a[5] * b[4] + a[6] * b[9] + a[7] * b[14] + a[8] * b[19] + a[9]; + out[10] = a[10] * b[0] + a[11] * b[5] + a[12] * b[10] + a[13] * b[15]; + out[11] = a[10] * b[1] + a[11] * b[6] + a[12] * b[11] + a[13] * b[16]; + out[12] = a[10] * b[2] + a[11] * b[7] + a[12] * b[12] + a[13] * b[17]; + out[13] = a[10] * b[3] + a[11] * b[8] + a[12] * b[13] + a[13] * b[18]; + out[14] = a[10] * b[4] + a[11] * b[9] + a[12] * b[14] + a[13] * b[19] + a[14]; + out[15] = a[15] * b[0] + a[16] * b[5] + a[17] * b[10] + a[18] * b[15]; + out[16] = a[15] * b[1] + a[16] * b[6] + a[17] * b[11] + a[18] * b[16]; + out[17] = a[15] * b[2] + a[16] * b[7] + a[17] * b[12] + a[18] * b[17]; + out[18] = a[15] * b[3] + a[16] * b[8] + a[17] * b[13] + a[18] * b[18]; + out[19] = a[15] * b[4] + a[16] * b[9] + a[17] * b[14] + a[18] * b[19] + a[19]; + return out; + } + /** + * Create a Float32 Array and normalize the offset component to 0-1 + * @param {number[]} matrix - 5x4 matrix + * @returns {number[]} 5x4 matrix with all values between 0-1 + */ + _colorMatrix(matrix) { + const m = new Float32Array(matrix); + m[4] /= 255; + m[9] /= 255; + m[14] /= 255; + m[19] /= 255; + return m; + } + /** + * Adjusts the brightness of a display object. + * + * The brightness adjustment works by multiplying the RGB channels by a scalar value while keeping + * the alpha channel unchanged. Values below 1 darken the image, while values above 1 brighten it. + * @param b - The brightness multiplier to apply. Values between 0-1 darken the image (0 being black), + * while values > 1 brighten it (2.0 would make it twice as bright) + * @param multiply - When true, the new matrix is multiplied with the current one instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * // Create a new color matrix filter + * const colorMatrix = new ColorMatrixFilter(); + * + * // Darken the image to 50% brightness + * colorMatrix.brightness(0.5, false); + * + * // Chain with other effects by using multiply + * colorMatrix + * .brightness(1.2, true) // Brighten by 20% + * .saturate(1.1, true); // Increase saturation by 10% + * ``` + */ + brightness(b, multiply) { + const matrix = [ + b, + 0, + 0, + 0, + 0, + 0, + b, + 0, + 0, + 0, + 0, + 0, + b, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Sets each channel on the diagonal of the color matrix to apply a color tint. + * + * This method provides a way to tint display objects using the color matrix filter, similar to + * the tint property available on Sprites and other display objects. The tint is applied by + * scaling the RGB channels of each pixel. + * @param color - The color to use for tinting, this can be any valid color source. + * @param multiply - When true, the new tint matrix is multiplied with the current matrix instead + * of replacing it. This allows for combining tints with other color effects. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply a red tint + * colorMatrix.tint(0xff0000); + * + * // Layer a green tint on top of existing effects + * colorMatrix.tint('green', true); + * + * // Chain with other color adjustments + * colorMatrix + * .tint('blue') // Blue tint + * .brightness(1.2, true) // Increase brightness + * ``` + */ + tint(color, multiply) { + const [r, g, b] = Color.shared.setValue(color).toArray(); + const matrix = [ + r, + 0, + 0, + 0, + 0, + 0, + g, + 0, + 0, + 0, + 0, + 0, + b, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Converts the display object to greyscale by applying a weighted matrix transformation. + * + * The greyscale effect works by setting equal RGB values for each pixel based on the scale parameter, + * effectively removing color information while preserving luminance. + * @param scale - The intensity of the greyscale effect. Value between 0-1, where: + * - 0 produces black + * - 0.5 produces 50% grey + * - 1 produces white + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Convert to 50% grey + * colorMatrix.greyscale(0.5, false); + * + * // Chain with other effects + * colorMatrix + * .greyscale(0.6, true) // Add grey tint + * .brightness(1.2, true); // Brighten the result + * ``` + */ + greyscale(scale, multiply) { + const matrix = [ + scale, + scale, + scale, + 0, + 0, + scale, + scale, + scale, + 0, + 0, + scale, + scale, + scale, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Converts the display object to grayscale by applying a weighted matrix transformation. + * + * The grayscale effect works by setting equal RGB values for each pixel based on the scale parameter, + * effectively removing color information while preserving luminance. + * @param scale - The intensity of the grayscale effect. Value between 0-1, where: + * - 0 produces black + * - 0.5 produces 50% grey + * - 1 produces white + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Convert to 50% grey + * colorMatrix.grayscale(0.5, false); + * + * // Chain with other effects + * colorMatrix + * .grayscale(0.6, true) // Add grey tint + * .brightness(1.2, true); // Brighten the result + * ``` + */ + grayscale(scale, multiply) { + this.greyscale(scale, multiply); + } + /** + * Converts the display object to pure black and white using a luminance-based threshold. + * + * This method applies a matrix transformation that removes all color information and reduces + * the image to just black and white values based on the luminance of each pixel. The transformation + * uses standard luminance weightings: 30% red, 60% green, and 10% blue. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Convert to black and white + * colorMatrix.blackAndWhite(false); + * + * // Chain with other effects + * colorMatrix + * .blackAndWhite(true) // Apply B&W effect + * .brightness(1.2, true); // Then increase brightness + * ``` + */ + blackAndWhite(multiply) { + const matrix = [ + 0.3, + 0.6, + 0.1, + 0, + 0, + 0.3, + 0.6, + 0.1, + 0, + 0, + 0.3, + 0.6, + 0.1, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Adjusts the hue of the display object by rotating the color values around the color wheel. + * + * This method uses an optimized matrix transformation that accurately rotates the RGB color space + * around its luminance axis. The implementation is based on RGB cube rotation in 3D space, providing + * better results than traditional matrices with magic luminance constants. + * @param rotation - The angle of rotation in degrees around the color wheel: + * - 0 = no change + * - 90 = rotate colors 90° clockwise + * - 180 = invert all colors + * - 270 = rotate colors 90° counter-clockwise + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Rotate hue by 90 degrees + * colorMatrix.hue(90, false); + * + * // Chain multiple color adjustments + * colorMatrix + * .hue(45, true) // Rotate colors by 45° + * .saturate(1.2, true) // Increase saturation + * .brightness(1.1, true); // Slightly brighten + * ``` + */ + hue(rotation, multiply) { + rotation = (rotation || 0) / 180 * Math.PI; + const cosR = Math.cos(rotation); + const sinR = Math.sin(rotation); + const sqrt = Math.sqrt; + const w = 1 / 3; + const sqrW = sqrt(w); + const a00 = cosR + (1 - cosR) * w; + const a01 = w * (1 - cosR) - sqrW * sinR; + const a02 = w * (1 - cosR) + sqrW * sinR; + const a10 = w * (1 - cosR) + sqrW * sinR; + const a11 = cosR + w * (1 - cosR); + const a12 = w * (1 - cosR) - sqrW * sinR; + const a20 = w * (1 - cosR) - sqrW * sinR; + const a21 = w * (1 - cosR) + sqrW * sinR; + const a22 = cosR + w * (1 - cosR); + const matrix = [ + a00, + a01, + a02, + 0, + 0, + a10, + a11, + a12, + 0, + 0, + a20, + a21, + a22, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Adjusts the contrast of the display object by modifying the separation between dark and bright values. + * + * This method applies a matrix transformation that affects the difference between dark and light areas + * in the image. Increasing contrast makes shadows darker and highlights brighter, while decreasing + * contrast brings shadows up and highlights down, reducing the overall dynamic range. + * @param amount - The contrast adjustment value. Range is 0 to 1, where: + * - 0 represents minimum contrast (flat gray) + * - 0.5 represents normal contrast + * - 1 represents maximum contrast + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Increase contrast by 50% + * colorMatrix.contrast(0.75, false); + * + * // Chain with other effects + * colorMatrix + * .contrast(0.6, true) // Boost contrast + * .brightness(1.1, true) // Slightly brighten + * .saturate(1.2, true); // Increase color intensity + * ``` + */ + contrast(amount, multiply) { + const v = (amount || 0) + 1; + const o = -0.5 * (v - 1); + const matrix = [ + v, + 0, + 0, + 0, + o, + 0, + v, + 0, + 0, + o, + 0, + 0, + v, + 0, + o, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Adjusts the saturation of the display object by modifying color separation. + * + * This method applies a matrix transformation that affects the intensity of colors. + * Increasing saturation makes colors more vivid and intense, while decreasing saturation + * moves colors toward grayscale. + * @param amount - The saturation adjustment value. Range is -1 to 1, where: + * - -1 produces grayscale + * - 0 represents no change + * - 1 produces maximum saturation + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Double the saturation + * colorMatrix.saturate(1, false); + * + * // Chain with other effects + * colorMatrix + * .saturate(0.5, true) // Increase saturation by 50% + * .brightness(1.1, true) // Slightly brighten + * .contrast(0.8, true); // Reduce contrast + * ``` + */ + saturate(amount = 0, multiply) { + const x = amount * 2 / 3 + 1; + const y = (x - 1) * -0.5; + const matrix = [ + x, + y, + y, + 0, + 0, + y, + x, + y, + 0, + 0, + y, + y, + x, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Completely removes color information from the display object, creating a grayscale version. + * + * This is a convenience method that calls `saturate(-1)` internally. The transformation preserves + * the luminance of the original image while removing all color information. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Convert image to grayscale + * colorMatrix.desaturate(); + * + * // Can be chained with other effects + * colorMatrix + * .desaturate() // Remove all color + * .brightness(1.2); // Then increase brightness + * ``` + */ + desaturate() { + this.saturate(-1); + } + /** + * Creates a negative effect by inverting all colors in the display object. + * + * This method applies a matrix transformation that inverts the RGB values of each pixel + * while preserving the alpha channel. The result is similar to a photographic negative. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Create negative effect + * colorMatrix.negative(false); + * + * // Chain with other effects + * colorMatrix + * .negative(true) // Apply negative effect + * .brightness(1.2, true) // Increase brightness + * .contrast(0.8, true); // Reduce contrast + * ``` + */ + negative(multiply) { + const matrix = [ + -1, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 1, + 0, + 0, + 0, + -1, + 1, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a sepia tone effect to the display object, creating a warm brown tint reminiscent of vintage photographs. + * + * This method applies a matrix transformation that converts colors to various shades of brown while + * preserving the original luminance values. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply sepia effect + * colorMatrix.sepia(false); + * + * // Chain with other effects + * colorMatrix + * .sepia(true) // Add sepia tone + * .brightness(1.1, true) // Slightly brighten + * .contrast(0.9, true); // Reduce contrast + * ``` + */ + sepia(multiply) { + const matrix = [ + 0.393, + 0.7689999, + 0.18899999, + 0, + 0, + 0.349, + 0.6859999, + 0.16799999, + 0, + 0, + 0.272, + 0.5339999, + 0.13099999, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a Technicolor-style effect that simulates the early color motion picture process. + * + * This method applies a matrix transformation that recreates the distinctive look of the + * Technicolor process. The effect produces highly + * saturated colors with a particular emphasis on reds, greens, and blues. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply Technicolor effect + * colorMatrix.technicolor(false); + * + * // Chain with other effects + * colorMatrix + * .technicolor(true) // Add Technicolor effect + * .contrast(1.1, true) // Boost contrast + * .brightness(0.9, true); // Slightly darken + * ``` + */ + technicolor(multiply) { + const matrix = [ + 1.9125277891456083, + -0.8545344976951645, + -0.09155508482755585, + 0, + 11.793603434377337, + -0.3087833385928097, + 1.7658908555458428, + -0.10601743074722245, + 0, + -70.35205161461398, + -0.231103377548616, + -0.7501899197440212, + 1.847597816108189, + 0, + 30.950940869491138, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a vintage Polaroid camera effect to the display object. + * + * This method applies a matrix transformation that simulates the distinctive look of + * Polaroid instant photographs, characterized by slightly enhanced contrast, subtle color shifts, + * and a warm overall tone. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply Polaroid effect + * colorMatrix.polaroid(false); + * + * // Chain with other effects + * colorMatrix + * .polaroid(true) // Add Polaroid effect + * .brightness(1.1, true) // Slightly brighten + * .contrast(1.1, true); // Boost contrast + * ``` + */ + polaroid(multiply) { + const matrix = [ + 1.438, + -0.062, + -0.062, + 0, + 0, + -0.122, + 1.378, + -0.122, + 0, + 0, + -0.016, + -0.016, + 1.483, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Swaps the red and blue color channels in the display object. + * + * This method applies a matrix transformation that exchanges the red and blue color values + * while keeping the green channel and alpha unchanged. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Swap red and blue channels + * colorMatrix.toBGR(false); + * + * // Chain with other effects + * colorMatrix + * .toBGR(true) // Swap R and B channels + * .brightness(1.1, true) // Slightly brighten + * .contrast(0.9, true); // Reduce contrast + * ``` + */ + toBGR(multiply) { + const matrix = [ + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a Kodachrome color effect that simulates the iconic film stock. + * + * This method applies a matrix transformation that recreates the distinctive look of Kodachrome film, + * known for its rich, vibrant colors and excellent image preservation qualities. The effect emphasizes + * reds and blues while producing deep, true blacks. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply Kodachrome effect + * colorMatrix.kodachrome(false); + * + * // Chain with other effects + * colorMatrix + * .kodachrome(true) // Add Kodachrome effect + * .contrast(1.1, true) // Boost contrast + * .brightness(0.9, true); // Slightly darken + * ``` + */ + kodachrome(multiply) { + const matrix = [ + 1.1285582396593525, + -0.3967382283601348, + -0.03992559172921793, + 0, + 63.72958762196502, + -0.16404339962244616, + 1.0835251566291304, + -0.05498805115633132, + 0, + 24.732407896706203, + -0.16786010706155763, + -0.5603416277695248, + 1.6014850761964943, + 0, + 35.62982807460946, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a stylized brown-tinted effect to the display object. + * + * This method applies a matrix transformation that creates a rich, warm brown tone + * with enhanced contrast and subtle color shifts. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply browni effect + * colorMatrix.browni(false); + * + * // Chain with other effects + * colorMatrix + * .browni(true) // Add brown tint + * .brightness(1.1, true) // Slightly brighten + * .contrast(1.2, true); // Boost contrast + * ``` + */ + browni(multiply) { + const matrix = [ + 0.5997023498159715, + 0.34553243048391263, + -0.2708298674538042, + 0, + 47.43192855600873, + -0.037703249837783157, + 0.8609577587992641, + 0.15059552388459913, + 0, + -36.96841498319127, + 0.24113635128153335, + -0.07441037908422492, + 0.44972182064877153, + 0, + -7.562075277591283, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a vintage photo effect that simulates old photography techniques. + * + * This method applies a matrix transformation that creates a nostalgic, aged look + * with muted colors, enhanced warmth, and subtle vignetting. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply vintage effect + * colorMatrix.vintage(false); + * + * // Chain with other effects + * colorMatrix + * .vintage(true) // Add vintage look + * .brightness(0.9, true) // Slightly darken + * .contrast(1.1, true); // Boost contrast + * ``` + */ + vintage(multiply) { + const matrix = [ + 0.6279345635605994, + 0.3202183420819367, + -0.03965408211312453, + 0, + 9.651285835294123, + 0.02578397704808868, + 0.6441188644374771, + 0.03259127616149294, + 0, + 7.462829176470591, + 0.0466055556782719, + -0.0851232987247891, + 0.5241648018700465, + 0, + 5.159190588235296, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * We don't know exactly what it does, kind of gradient map, but funny to play with! + * @param desaturation - Tone values. + * @param toned - Tone values. + * @param lightColor - Tone values, example: `0xFFE580` + * @param darkColor - Tone values, example: `0xFFE580` + * @param multiply - if true, current matrix and matrix are multiplied. If false, + * just set the current matrix with matrix + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Create sepia-like effect with custom colors + * colorMatrix.colorTone( + * 0.3, // Moderate desaturation + * 0.2, // Moderate toning + * 0xFFE580, // Warm highlight color + * 0x338000, // Dark green shadows + * false + * ); + * + * // Chain with other effects + * colorMatrix + * .colorTone(0.2, 0.15, 0xFFE580, 0x338000, true) + * .brightness(1.1, true); // Slightly brighten + * ``` + */ + colorTone(desaturation, toned, lightColor, darkColor, multiply) { + desaturation || (desaturation = 0.2); + toned || (toned = 0.15); + lightColor || (lightColor = 16770432); + darkColor || (darkColor = 3375104); + const temp = Color.shared; + const [lR, lG, lB] = temp.setValue(lightColor).toArray(); + const [dR, dG, dB] = temp.setValue(darkColor).toArray(); + const matrix = [ + 0.3, + 0.59, + 0.11, + 0, + 0, + lR, + lG, + lB, + desaturation, + 0, + dR, + dG, + dB, + toned, + 0, + lR - dR, + lG - dG, + lB - dB, + 0, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a night vision effect to the display object. + * + * This method applies a matrix transformation that simulates night vision by enhancing + * certain color channels while suppressing others, creating a green-tinted effect + * similar to night vision goggles. + * @param intensity - The intensity of the night effect (0-1): + * - 0 produces no effect + * - 0.1 produces a subtle night vision effect (default) + * - 1 produces maximum night vision effect + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply night vision effect + * colorMatrix.night(0.3, false); + * + * // Chain with other effects + * colorMatrix + * .night(0.2, true) // Add night vision + * .brightness(1.1, true) // Slightly brighten + * .contrast(1.2, true); // Boost contrast + * ``` + */ + night(intensity, multiply) { + intensity || (intensity = 0.1); + const matrix = [ + intensity * -2, + -intensity, + 0, + 0, + 0, + -intensity, + 0, + intensity, + 0, + 0, + 0, + intensity, + intensity * 2, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Predator effect + * + * Erase the current matrix by setting a new independent one + * @param amount - how much the predator feels his future victim + * @param multiply - if true, current matrix and matrix are multiplied. If false, + * just set the current matrix with matrix + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply thermal vision effect + * colorMatrix.predator(0.5, false); + * + * // Chain with other effects + * colorMatrix + * .predator(0.3, true) // Add thermal effect + * .contrast(1.2, true) // Boost contrast + * .brightness(1.1, true); // Slightly brighten + * ``` + */ + predator(amount, multiply) { + const matrix = [ + // row 1 + 11.224130630493164 * amount, + -4.794486999511719 * amount, + -2.8746118545532227 * amount, + 0 * amount, + 0.40342438220977783 * amount, + // row 2 + -3.6330697536468506 * amount, + 9.193157196044922 * amount, + -2.951810836791992 * amount, + 0 * amount, + -1.316135048866272 * amount, + // row 3 + -3.2184197902679443 * amount, + -4.2375030517578125 * amount, + 7.476448059082031 * amount, + 0 * amount, + 0.8044459223747253 * amount, + // row 4 + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Applies a psychedelic color effect that creates dramatic color shifts. + * + * This method applies a matrix transformation that produces vibrant colors + * through channel mixing and amplification. Creates an effect reminiscent of + * color distortions in psychedelic art. + * @param multiply - When true, the new matrix is multiplied with the current matrix instead of replacing it. + * This allows for cumulative effects when calling multiple color adjustments. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply psychedelic effect + * colorMatrix.lsd(false); + * + * // Chain with other effects + * colorMatrix + * .lsd(true) // Add color distortion + * .brightness(0.9, true) // Slightly darken + * .contrast(1.2, true); // Boost contrast + * ``` + */ + lsd(multiply) { + const matrix = [ + 2, + -0.4, + 0.5, + 0, + 0, + -0.5, + 2, + -0.4, + 0, + 0, + -0.4, + -0.5, + 3, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, multiply); + } + /** + * Resets the color matrix filter to its default state. + * + * This method resets all color transformations by setting the matrix back to its identity state. + * The identity matrix leaves colors unchanged, effectively removing all previously applied effects. + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply some effects + * colorMatrix + * .sepia(true) + * .brightness(1.2, true); + * + * // Reset back to original colors + * colorMatrix.reset(); + * ``` + */ + reset() { + const matrix = [ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0 + ]; + this._loadMatrix(matrix, false); + } + /** + * The current color transformation matrix of the filter. + * + * This 5x4 matrix transforms RGBA color and alpha values of each pixel. The matrix is stored + * as a 20-element array in row-major order. + * @type {ColorMatrix} + * @default [ + * 1, 0, 0, 0, 0, // Red channel + * 0, 1, 0, 0, 0, // Green channel + * 0, 0, 1, 0, 0, // Blue channel + * 0, 0, 0, 1, 0 // Alpha channel + * ] + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * // Get the current color matrix + * const currentMatrix = colorMatrix.matrix; + * // Modify the matrix + * colorMatrix.matrix = [ + * 1, 0, 0, 0, 0, + * 0, 1, 0, 0, 0, + * 0, 0, 1, 0, 0, + * 0, 0, 0, 1, 0 + * ]; + */ + get matrix() { + return this.resources.colorMatrixUniforms.uniforms.uColorMatrix; + } + set matrix(value) { + this.resources.colorMatrixUniforms.uniforms.uColorMatrix = value; + } + /** + * The opacity value used to blend between the original and transformed colors. + * + * This value controls how much of the color transformation is applied: + * - 0 = Original color only (no effect) + * - 0.5 = 50% blend of original and transformed colors + * - 1 = Fully transformed color (default) + * @default 1 + * @example + * ```ts + * const colorMatrix = new ColorMatrixFilter(); + * + * // Apply sepia at 50% strength + * colorMatrix.sepia(false); + * colorMatrix.alpha = 0.5; + * + * // Fade between effects + * colorMatrix + * .saturate(1.5) // Increase saturation + * .contrast(1.2); // Boost contrast + * colorMatrix.alpha = 0.7; // Apply at 70% strength + * ``` + */ + get alpha() { + return this.resources.colorMatrixUniforms.uniforms.uAlpha; + } + set alpha(value) { + this.resources.colorMatrixUniforms.uniforms.uAlpha = value; + } + } + + var fragment$3 = "\nin vec2 vTextureCoord;\nin vec2 vFilterUv;\n\nout vec4 finalColor;\n\nuniform sampler2D uTexture;\nuniform sampler2D uMapTexture;\n\nuniform vec4 uInputClamp;\nuniform highp vec4 uInputSize;\nuniform mat2 uRotation;\nuniform vec2 uScale;\n\nvoid main()\n{\n vec4 map = texture(uMapTexture, vFilterUv);\n \n vec2 offset = uInputSize.zw * (uRotation * (map.xy - 0.5)) * uScale; \n\n finalColor = texture(uTexture, clamp(vTextureCoord + offset, uInputClamp.xy, uInputClamp.zw));\n}\n"; + + var vertex$2 = "in vec2 aPosition;\nout vec2 vTextureCoord;\nout vec2 vFilterUv;\n\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\n\nuniform mat3 uFilterMatrix;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n \n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvec2 getFilterCoord( void )\n{\n return ( uFilterMatrix * vec3( filterTextureCoord(), 1.0) ).xy;\n}\n\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n vFilterUv = getFilterCoord();\n}\n"; + + var source$2 = "\nstruct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct DisplacementUniforms {\n uFilterMatrix:mat3x3,\n uScale:vec2,\n uRotation:mat2x2\n};\n\n\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n\n@group(1) @binding(0) var filterUniforms : DisplacementUniforms;\n@group(1) @binding(1) var uMapTexture: texture_2d;\n@group(1) @binding(2) var uMapSampler : sampler;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2,\n @location(1) filterUv : vec2,\n };\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); \n}\n\nfn getFilterCoord(aPosition:vec2 ) -> vec2\n{\n return ( filterUniforms.uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy;\n}\n\nfn getSize() -> vec2\n{\n\n \n return gfu.uGlobalFrame.zw;\n}\n \n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition),\n getFilterCoord(aPosition)\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n @location(1) filterUv: vec2,\n @builtin(position) position: vec4\n) -> @location(0) vec4 {\n\n var map = textureSample(uMapTexture, uMapSampler, filterUv);\n\n var offset = gfu.uInputSize.zw * (filterUniforms.uRotation * (map.xy - 0.5)) * filterUniforms.uScale; \n \n return textureSample(uTexture, uSampler, clamp(uv + offset, gfu.uInputClamp.xy, gfu.uInputClamp.zw));\n}"; + + "use strict"; + var __defProp$L = Object.defineProperty; + var __defProps$m = Object.defineProperties; + var __getOwnPropDescs$m = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$N = Object.getOwnPropertySymbols; + var __hasOwnProp$N = Object.prototype.hasOwnProperty; + var __propIsEnum$N = Object.prototype.propertyIsEnumerable; + var __defNormalProp$L = (obj, key, value) => key in obj ? __defProp$L(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$L = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$N.call(b, prop)) + __defNormalProp$L(a, prop, b[prop]); + if (__getOwnPropSymbols$N) + for (var prop of __getOwnPropSymbols$N(b)) { + if (__propIsEnum$N.call(b, prop)) + __defNormalProp$L(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$m = (a, b) => __defProps$m(a, __getOwnPropDescs$m(b)); + var __objRest$i = (source2, exclude) => { + var target = {}; + for (var prop in source2) + if (__hasOwnProp$N.call(source2, prop) && exclude.indexOf(prop) < 0) + target[prop] = source2[prop]; + if (source2 != null && __getOwnPropSymbols$N) + for (var prop of __getOwnPropSymbols$N(source2)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$N.call(source2, prop)) + target[prop] = source2[prop]; + } + return target; + }; + class DisplacementFilter extends Filter { + constructor(...args) { + let options = args[0]; + if (options instanceof Sprite) { + if (args[1]) { + deprecation(v8_0_0, "DisplacementFilter now uses options object instead of params. {sprite, scale}"); + } + options = { sprite: options, scale: args[1] }; + } + const _a = options, { sprite, scale: scaleOption } = _a, rest = __objRest$i(_a, ["sprite", "scale"]); + let scale = scaleOption != null ? scaleOption : 20; + if (typeof scale === "number") { + scale = new Point(scale, scale); + } + const filterUniforms = new UniformGroup({ + uFilterMatrix: { value: new Matrix(), type: "mat3x3" }, + uScale: { value: scale, type: "vec2" }, + uRotation: { value: new Float32Array([0, 0, 0, 0]), type: "mat2x2" } + }); + const glProgram = GlProgram.from({ + vertex: vertex$2, + fragment: fragment$3, + name: "displacement-filter" + }); + const gpuProgram = GpuProgram.from({ + vertex: { + source: source$2, + entryPoint: "mainVertex" + }, + fragment: { + source: source$2, + entryPoint: "mainFragment" + } + }); + const textureSource = sprite.texture.source; + super(__spreadProps$m(__spreadValues$L({}, rest), { + gpuProgram, + glProgram, + resources: { + filterUniforms, + uMapTexture: textureSource, + uMapSampler: textureSource.style + } + })); + this._sprite = options.sprite; + this._sprite.renderable = false; + } + /** + * Applies the filter. + * @param filterManager - The manager. + * @param input - The input target. + * @param output - The output target. + * @param clearMode - clearMode. + * @advanced + */ + apply(filterManager, input, output, clearMode) { + const uniforms = this.resources.filterUniforms.uniforms; + filterManager.calculateSpriteMatrix( + uniforms.uFilterMatrix, + this._sprite + ); + const wt = this._sprite.worldTransform; + const lenX = Math.sqrt(wt.a * wt.a + wt.b * wt.b); + const lenY = Math.sqrt(wt.c * wt.c + wt.d * wt.d); + if (lenX !== 0 && lenY !== 0) { + uniforms.uRotation[0] = wt.a / lenX; + uniforms.uRotation[1] = wt.b / lenX; + uniforms.uRotation[2] = wt.c / lenY; + uniforms.uRotation[3] = wt.d / lenY; + } + this.resources.uMapTexture = this._sprite.texture.source; + filterManager.applyFilter(this, input, output, clearMode); + } + /** + * The scale of the displacement effect. + * + * Gets the current x and y scaling values used for the displacement mapping. + * - x: Horizontal displacement scale + * - y: Vertical displacement scale + * @returns {Point} The current scale as a Point object + * @example + * ```ts + * const filter = new DisplacementFilter({ sprite }); + * + * // Get current scale + * console.log(filter.scale.x, filter.scale.y); + * + * // Update scale + * filter.scale.x = 100; + * filter.scale.y = 50; + * ``` + */ + get scale() { + return this.resources.filterUniforms.uniforms.uScale; + } + } + + var fragment$2 = "\nin vec2 vTextureCoord;\nin vec4 vColor;\n\nout vec4 finalColor;\n\nuniform float uNoise;\nuniform float uSeed;\nuniform sampler2D uTexture;\n\nfloat rand(vec2 co)\n{\n return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\nvoid main()\n{\n vec4 color = texture(uTexture, vTextureCoord);\n float randomValue = rand(gl_FragCoord.xy * uSeed);\n float diff = (randomValue - 0.5) * uNoise;\n\n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (color.a > 0.0) {\n color.rgb /= color.a;\n }\n\n color.r += diff;\n color.g += diff;\n color.b += diff;\n\n // Premultiply alpha again.\n color.rgb *= color.a;\n\n finalColor = color;\n}\n"; + + var source$1 = "\n\nstruct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct NoiseUniforms {\n uNoise:f32,\n uSeed:f32,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n\n@group(1) @binding(0) var noiseUniforms : NoiseUniforms;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2\n };\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw); \n}\n\nfn getSize() -> vec2\n{\n return gfu.uGlobalFrame.zw;\n}\n \n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2, \n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition)\n );\n}\n\nfn rand(co:vec2) -> f32\n{\n return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);\n}\n\n\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n @builtin(position) position: vec4\n) -> @location(0) vec4 {\n\n var pixelPosition = globalTextureCoord(position.xy);// / (getSize());//- gfu.uOutputFrame.xy);\n \n \n var sample = textureSample(uTexture, uSampler, uv);\n var randomValue = rand(pixelPosition.xy * noiseUniforms.uSeed);\n var diff = (randomValue - 0.5) * noiseUniforms.uNoise;\n \n // Un-premultiply alpha before applying the color matrix. See issue #3539.\n if (sample.a > 0.0) {\n sample.r /= sample.a;\n sample.g /= sample.a;\n sample.b /= sample.a;\n }\n\n sample.r += diff;\n sample.g += diff;\n sample.b += diff;\n\n // Premultiply alpha again.\n sample.r *= sample.a;\n sample.g *= sample.a;\n sample.b *= sample.a;\n \n return sample;\n}"; + + "use strict"; + var __defProp$K = Object.defineProperty; + var __defProps$l = Object.defineProperties; + var __getOwnPropDescs$l = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$M = Object.getOwnPropertySymbols; + var __hasOwnProp$M = Object.prototype.hasOwnProperty; + var __propIsEnum$M = Object.prototype.propertyIsEnumerable; + var __defNormalProp$K = (obj, key, value) => key in obj ? __defProp$K(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$K = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$M.call(b, prop)) + __defNormalProp$K(a, prop, b[prop]); + if (__getOwnPropSymbols$M) + for (var prop of __getOwnPropSymbols$M(b)) { + if (__propIsEnum$M.call(b, prop)) + __defNormalProp$K(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$l = (a, b) => __defProps$l(a, __getOwnPropDescs$l(b)); + var __objRest$h = (source2, exclude) => { + var target = {}; + for (var prop in source2) + if (__hasOwnProp$M.call(source2, prop) && exclude.indexOf(prop) < 0) + target[prop] = source2[prop]; + if (source2 != null && __getOwnPropSymbols$M) + for (var prop of __getOwnPropSymbols$M(source2)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$M.call(source2, prop)) + target[prop] = source2[prop]; + } + return target; + }; + const _NoiseFilter = class _NoiseFilter extends Filter { + /** + * @param options - The options of the noise filter. + */ + constructor(options = {}) { + options = __spreadValues$K(__spreadValues$K({}, _NoiseFilter.defaultOptions), options); + const gpuProgram = GpuProgram.from({ + vertex: { + source: source$1, + entryPoint: "mainVertex" + }, + fragment: { + source: source$1, + entryPoint: "mainFragment" + } + }); + const glProgram = GlProgram.from({ + vertex: vertex$3, + fragment: fragment$2, + name: "noise-filter" + }); + const _a = options, { noise, seed } = _a, rest = __objRest$h(_a, ["noise", "seed"]); + super(__spreadProps$l(__spreadValues$K({}, rest), { + gpuProgram, + glProgram, + resources: { + noiseUniforms: new UniformGroup({ + uNoise: { value: 1, type: "f32" }, + uSeed: { value: 1, type: "f32" } + }) + } + })); + this.noise = noise; + this.seed = seed != null ? seed : Math.random(); + } + /** + * The amount of noise to apply to the filtered content. + * + * This value controls the intensity of the random noise effect: + * - Values close to 0 produce subtle noise + * - Values around 0.5 produce moderate noise + * - Values close to 1 produce strong noise + * @default 0.5 + * @example + * ```ts + * const noiseFilter = new NoiseFilter(); + * + * // Set to subtle noise + * noiseFilter.noise = 0.2; + * + * // Set to maximum noise + * noiseFilter.noise = 1.0; + * ``` + */ + get noise() { + return this.resources.noiseUniforms.uniforms.uNoise; + } + set noise(value) { + this.resources.noiseUniforms.uniforms.uNoise = value; + } + /** + * The seed value used for random noise generation. + * + * This value determines the noise pattern: + * - Using the same seed will generate identical noise patterns + * - Different seeds produce different but consistent patterns + * - `Math.random()` can be used for random patterns + * @default Math.random() + * @example + * ```ts + * const noiseFilter = new NoiseFilter(); + * + * // Use a fixed seed for consistent noise + * noiseFilter.seed = 12345; + * + * // Generate new random pattern + * noiseFilter.seed = Math.random(); + * ``` + */ + get seed() { + return this.resources.noiseUniforms.uniforms.uSeed; + } + set seed(value) { + this.resources.noiseUniforms.uniforms.uSeed = value; + } + }; + /** + * The default configuration options for the NoiseFilter. + * + * These values will be used when no specific options are provided to the constructor. + * You can override any of these values by passing your own options object. + * @example + * ```ts + * NoiseFilter.defaultOptions.noise = 0.7; // Change default noise to 0.7 + * const filter = new NoiseFilter(); // Will use noise 0.7 by default + * ``` + */ + _NoiseFilter.defaultOptions = { + noise: 0.5 + }; + let NoiseFilter = _NoiseFilter; + + var fragment$1 = "in vec2 vMaskCoord;\nin vec2 vTextureCoord;\n\nuniform sampler2D uTexture;\nuniform sampler2D uMaskTexture;\n\nuniform float uAlpha;\nuniform vec4 uMaskClamp;\nuniform float uInverse;\n\nout vec4 finalColor;\n\nvoid main(void)\n{\n float clip = step(3.5,\n step(uMaskClamp.x, vMaskCoord.x) +\n step(uMaskClamp.y, vMaskCoord.y) +\n step(vMaskCoord.x, uMaskClamp.z) +\n step(vMaskCoord.y, uMaskClamp.w));\n\n // TODO look into why this is needed\n float npmAlpha = uAlpha;\n vec4 original = texture(uTexture, vTextureCoord);\n vec4 masky = texture(uMaskTexture, vMaskCoord);\n float alphaMul = 1.0 - npmAlpha * (1.0 - masky.a);\n\n float a = alphaMul * masky.r * npmAlpha * clip;\n\n if (uInverse == 1.0) {\n a = 1.0 - a;\n }\n\n finalColor = original * a;\n}\n"; + + var vertex$1 = "in vec2 aPosition;\n\nout vec2 vTextureCoord;\nout vec2 vMaskCoord;\n\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\nuniform mat3 uFilterMatrix;\n\nvec4 filterVertexPosition( vec2 aPosition )\n{\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n \n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( vec2 aPosition )\n{\n return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvec2 getFilterCoord( vec2 aPosition )\n{\n return ( uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy;\n} \n\nvoid main(void)\n{\n gl_Position = filterVertexPosition(aPosition);\n vTextureCoord = filterTextureCoord(aPosition);\n vMaskCoord = getFilterCoord(aPosition);\n}\n"; + + var source = "struct GlobalFilterUniforms {\n uInputSize:vec4,\n uInputPixel:vec4,\n uInputClamp:vec4,\n uOutputFrame:vec4,\n uGlobalFrame:vec4,\n uOutputTexture:vec4,\n};\n\nstruct MaskUniforms {\n uFilterMatrix:mat3x3,\n uMaskClamp:vec4,\n uAlpha:f32,\n uInverse:f32,\n};\n\n@group(0) @binding(0) var gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d;\n@group(0) @binding(2) var uSampler : sampler;\n\n@group(1) @binding(0) var filterUniforms : MaskUniforms;\n@group(1) @binding(1) var uMaskTexture: texture_2d;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2,\n @location(1) filterUv : vec2,\n};\n\nfn filterVertexPosition(aPosition:vec2) -> vec4\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord( aPosition:vec2 ) -> vec2\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\nfn globalTextureCoord( aPosition:vec2 ) -> vec2\n{\n return (aPosition.xy / gfu.uGlobalFrame.zw) + (gfu.uGlobalFrame.xy / gfu.uGlobalFrame.zw);\n}\n\nfn getFilterCoord(aPosition:vec2 ) -> vec2\n{\n return ( filterUniforms.uFilterMatrix * vec3( filterTextureCoord(aPosition), 1.0) ).xy;\n}\n\nfn getSize() -> vec2\n{\n return gfu.uGlobalFrame.zw;\n}\n\n@vertex\nfn mainVertex(\n @location(0) aPosition : vec2,\n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition),\n getFilterCoord(aPosition)\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n @location(1) filterUv: vec2,\n @builtin(position) position: vec4\n) -> @location(0) vec4 {\n\n var maskClamp = filterUniforms.uMaskClamp;\n var uAlpha = filterUniforms.uAlpha;\n\n var clip = step(3.5,\n step(maskClamp.x, filterUv.x) +\n step(maskClamp.y, filterUv.y) +\n step(filterUv.x, maskClamp.z) +\n step(filterUv.y, maskClamp.w));\n\n var mask = textureSample(uMaskTexture, uSampler, filterUv);\n var source = textureSample(uTexture, uSampler, uv);\n var alphaMul = 1.0 - uAlpha * (1.0 - mask.a);\n\n var a: f32 = alphaMul * mask.r * uAlpha * clip;\n\n if (filterUniforms.uInverse == 1.0) {\n a = 1.0 - a;\n }\n\n return source * a;\n}\n"; + + "use strict"; + var __defProp$J = Object.defineProperty; + var __defProps$k = Object.defineProperties; + var __getOwnPropDescs$k = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$L = Object.getOwnPropertySymbols; + var __hasOwnProp$L = Object.prototype.hasOwnProperty; + var __propIsEnum$L = Object.prototype.propertyIsEnumerable; + var __defNormalProp$J = (obj, key, value) => key in obj ? __defProp$J(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$J = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$L.call(b, prop)) + __defNormalProp$J(a, prop, b[prop]); + if (__getOwnPropSymbols$L) + for (var prop of __getOwnPropSymbols$L(b)) { + if (__propIsEnum$L.call(b, prop)) + __defNormalProp$J(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$k = (a, b) => __defProps$k(a, __getOwnPropDescs$k(b)); + var __objRest$g = (source2, exclude) => { + var target = {}; + for (var prop in source2) + if (__hasOwnProp$L.call(source2, prop) && exclude.indexOf(prop) < 0) + target[prop] = source2[prop]; + if (source2 != null && __getOwnPropSymbols$L) + for (var prop of __getOwnPropSymbols$L(source2)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$L.call(source2, prop)) + target[prop] = source2[prop]; + } + return target; + }; + class MaskFilter extends Filter { + constructor(options) { + const _a = options, { sprite } = _a, rest = __objRest$g(_a, ["sprite"]); + const textureMatrix = new TextureMatrix(sprite.texture); + const filterUniforms = new UniformGroup({ + uFilterMatrix: { value: new Matrix(), type: "mat3x3" }, + uMaskClamp: { value: textureMatrix.uClampFrame, type: "vec4" }, + uAlpha: { value: 1, type: "f32" }, + uInverse: { value: options.inverse ? 1 : 0, type: "f32" } + }); + const gpuProgram = GpuProgram.from({ + vertex: { + source, + entryPoint: "mainVertex" + }, + fragment: { + source, + entryPoint: "mainFragment" + } + }); + const glProgram = GlProgram.from({ + vertex: vertex$1, + fragment: fragment$1, + name: "mask-filter" + }); + super(__spreadProps$k(__spreadValues$J({}, rest), { + gpuProgram, + glProgram, + clipToViewport: false, + resources: { + filterUniforms, + uMaskTexture: sprite.texture.source + } + })); + this.sprite = sprite; + this._textureMatrix = textureMatrix; + } + set inverse(value) { + this.resources.filterUniforms.uniforms.uInverse = value ? 1 : 0; + } + get inverse() { + return this.resources.filterUniforms.uniforms.uInverse === 1; + } + apply(filterManager, input, output, clearMode) { + this._textureMatrix.texture = this.sprite.texture; + filterManager.calculateSpriteMatrix( + this.resources.filterUniforms.uniforms.uFilterMatrix, + this.sprite + ).prepend(this._textureMatrix.mapCoord); + this.resources.uMaskTexture = this.sprite.texture.source; + filterManager.applyFilter(this, input, output, clearMode); + } + } + + var hsl = "fn getLuminosity(c: vec3) -> f32 {\n return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;\n}\n\nfn setLuminosity(c: vec3, lum: f32) -> vec3 {\n let d: f32 = lum - getLuminosity(c);\n let newColor: vec3 = c.rgb + vec3(d, d, d);\n\n // clip back into legal range\n let newLum: f32 = getLuminosity(newColor);\n let cMin: f32 = min(newColor.r, min(newColor.g, newColor.b));\n let cMax: f32 = max(newColor.r, max(newColor.g, newColor.b));\n\n let t1: f32 = newLum / (newLum - cMin);\n let t2: f32 = (1.0 - newLum) / (cMax - newLum);\n\n let finalColor = mix(vec3(newLum, newLum, newLum), newColor, select(select(1.0, t2, cMax > 1.0), t1, cMin < 0.0));\n\n return finalColor;\n}\n\nfn getSaturation(c: vec3) -> f32 {\n return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));\n}\n\n// Set saturation if color components are sorted in ascending order.\nfn setSaturationMinMidMax(cSorted: vec3, s: f32) -> vec3 {\n var result: vec3;\n if (cSorted.z > cSorted.x) {\n let newY = (((cSorted.y - cSorted.x) * s) / (cSorted.z - cSorted.x));\n result = vec3(0.0, newY, s);\n } else {\n result = vec3(0.0, 0.0, 0.0);\n }\n return vec3(result.x, result.y, result.z);\n}\n\nfn setSaturation(c: vec3, s: f32) -> vec3 {\n var result: vec3 = c;\n\n if (c.r <= c.g && c.r <= c.b) {\n if (c.g <= c.b) {\n result = setSaturationMinMidMax(result, s);\n } else {\n var temp: vec3 = vec3(result.r, result.b, result.g);\n temp = setSaturationMinMidMax(temp, s);\n result = vec3(temp.r, temp.b, temp.g);\n }\n } else if (c.g <= c.r && c.g <= c.b) {\n if (c.r <= c.b) {\n var temp: vec3 = vec3(result.g, result.r, result.b);\n temp = setSaturationMinMidMax(temp, s);\n result = vec3(temp.g, temp.r, temp.b);\n } else {\n var temp: vec3 = vec3(result.g, result.b, result.r);\n temp = setSaturationMinMidMax(temp, s);\n result = vec3(temp.g, temp.b, temp.r);\n }\n } else {\n if (c.r <= c.g) {\n var temp: vec3 = vec3(result.b, result.r, result.g);\n temp = setSaturationMinMidMax(temp, s);\n result = vec3(temp.b, temp.r, temp.g);\n } else {\n var temp: vec3 = vec3(result.b, result.g, result.r);\n temp = setSaturationMinMidMax(temp, s);\n result = vec3(temp.b, temp.g, temp.r);\n }\n }\n\n return result;\n}"; + + "use strict"; + + "use strict"; + const _PrepareBase = class _PrepareBase { + /** + * @param {Renderer} renderer - A reference to the current renderer + */ + constructor(renderer) { + /** called per frame by the ticker, defer processing to next tick */ + this._tick = () => { + if (this._destroyed) return; + this.timeout = setTimeout(this._processQueue, 0); + }; + /** process the queue up to max item limit per frame */ + this._processQueue = () => { + if (this._destroyed) return; + const { queue } = this; + let itemsProcessed = 0; + while (queue.length && itemsProcessed < _PrepareBase.uploadsPerFrame) { + const queueItem = queue.shift(); + this.uploadQueueItem(queueItem); + itemsProcessed++; + } + if (queue.length) { + Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY); + } else { + this._resolve(); + } + }; + this.renderer = renderer; + this.queue = []; + this.resolves = []; + } + /** + * Return a copy of the queue + * @returns {PrepareQueueItem[]} The queue + */ + getQueue() { + return [...this.queue]; + } + /** + * Add a textures or graphics resource to the queue + * @param {PrepareSourceItem | PrepareSourceItem[]} resource + */ + add(resource) { + const resourceArray = Array.isArray(resource) ? resource : [resource]; + for (const resourceItem of resourceArray) { + if (resourceItem instanceof Container) { + this._addContainer(resourceItem); + } else { + this.resolveQueueItem(resourceItem, this.queue); + } + } + return this; + } + /** + * Recursively add a container and its children to the queue + * @param {Container} container - The container to add to the queue + */ + _addContainer(container) { + this.resolveQueueItem(container, this.queue); + for (const child of container.children) { + this._addContainer(child); + } + } + /** + * Upload all the textures and graphics to the GPU (optionally add more resources to the queue first) + * @param {PrepareSourceItem | PrepareSourceItem[] | undefined} resource + */ + upload(resource) { + if (resource) { + this.add(resource); + } + return new Promise((resolve) => { + if (this.queue.length) { + this.resolves.push(resolve); + this.dedupeQueue(); + Ticker.system.addOnce(this._tick, this, UPDATE_PRIORITY.UTILITY); + } else { + resolve(); + } + }); + } + /** eliminate duplicates before processing */ + dedupeQueue() { + const hash = /* @__PURE__ */ Object.create(null); + let nextUnique = 0; + for (let i = 0; i < this.queue.length; i++) { + const current = this.queue[i]; + if (!hash[current.uid]) { + hash[current.uid] = true; + this.queue[nextUnique++] = current; + } + } + this.queue.length = nextUnique; + } + destroy() { + this._destroyed = true; + clearTimeout(this.timeout); + } + /** Call all the resolve callbacks */ + _resolve() { + const { resolves } = this; + const array = resolves.slice(0); + resolves.length = 0; + for (const resolve of array) { + resolve(); + } + } + }; + /** The number of uploads to process per frame */ + _PrepareBase.uploadsPerFrame = 4; + let PrepareBase = _PrepareBase; + + "use strict"; + class CanvasGraphicsContext { + constructor() { + /** + * Whether this context can be batched. + * @advanced + */ + this.isBatchable = false; + } + /** + * Reset cached canvas data. + * @advanced + */ + reset() { + this.isBatchable = false; + this.context = null; + if (this.graphicsData) { + this.graphicsData.destroy(); + this.graphicsData = null; + } + } + /** + * Destroy the cached data. + * @advanced + */ + destroy() { + this.reset(); + } + } + class CanvasGraphicsContextRenderData { + constructor() { + /** + * Instructions for canvas rendering. + * @advanced + */ + this.instructions = new InstructionSet(); + } + /** + * Initialize render data. + * @advanced + */ + init() { + this.instructions.reset(); + } + /** + * Destroy render data. + * @advanced + */ + destroy() { + this.instructions.destroy(); + this.instructions = null; + } + } + const _CanvasGraphicsContextSystem = class _CanvasGraphicsContextSystem { + constructor(renderer) { + this._renderer = renderer; + this._managedContexts = new GCManagedHash({ renderer, type: "resource", name: "graphicsContext" }); + } + /** + * Runner init called, update the default options + * @ignore + */ + init(options) { + var _a; + _CanvasGraphicsContextSystem.defaultOptions.bezierSmoothness = (_a = options == null ? void 0 : options.bezierSmoothness) != null ? _a : _CanvasGraphicsContextSystem.defaultOptions.bezierSmoothness; + } + /** + * Returns the render data for a given GraphicsContext. + * @param context - The GraphicsContext to get the render data for. + * @internal + */ + getContextRenderData(context) { + const gpuContext = this.getGpuContext(context); + return gpuContext.graphicsData || this._initContextRenderData(context); + } + /** + * Updates the GPU context for a given GraphicsContext. + * @param context - The GraphicsContext to update. + * @returns The updated CanvasGraphicsContext. + * @internal + */ + updateGpuContext(context) { + const gpuData = context._gpuData; + const hasContext = !!gpuData[this._renderer.uid]; + const gpuContext = gpuData[this._renderer.uid] || this._initContext(context); + if (context.dirty || !hasContext) { + if (hasContext) { + gpuContext.reset(); + } + gpuContext.isBatchable = false; + context.dirty = false; + } + return gpuContext; + } + /** + * Returns the CanvasGraphicsContext for a given GraphicsContext. + * If it does not exist, it will initialize a new one. + * @param context - The GraphicsContext to get the CanvasGraphicsContext for. + * @returns The CanvasGraphicsContext for the given GraphicsContext. + * @internal + */ + getGpuContext(context) { + const gpuData = context._gpuData; + return gpuData[this._renderer.uid] || this._initContext(context); + } + _initContextRenderData(context) { + const renderData = new CanvasGraphicsContextRenderData(); + const gpuContext = this.getGpuContext(context); + gpuContext.graphicsData = renderData; + renderData.init(); + return renderData; + } + _initContext(context) { + const gpuContext = new CanvasGraphicsContext(); + gpuContext.context = context; + context._gpuData[this._renderer.uid] = gpuContext; + this._managedContexts.add(context); + return gpuContext; + } + destroy() { + this._managedContexts.destroy(); + this._renderer = null; + } + }; + /** @ignore */ + _CanvasGraphicsContextSystem.extension = { + type: [ + ExtensionType.CanvasSystem + ], + name: "graphicsContext" + }; + /** The default options for the GraphicsContextSystem. */ + _CanvasGraphicsContextSystem.defaultOptions = { + /** + * A value from 0 to 1 that controls the smoothness of bezier curves (the higher the smoother) + * @default 0.5 + */ + bezierSmoothness: 0.5 + }; + let CanvasGraphicsContextSystem = _CanvasGraphicsContextSystem; + + "use strict"; + class CanvasGraphicsPipe { + constructor(renderer, adaptor) { + this.state = State.for2d(); + this.renderer = renderer; + this._adaptor = adaptor; + this.renderer.runners.contextChange.add(this); + this._managedGraphics = new GCManagedHash({ renderer, type: "renderable", priority: -1, name: "graphics" }); + } + contextChange() { + this._adaptor.contextChange(this.renderer); + } + validateRenderable(_graphics) { + return false; + } + addRenderable(graphics, instructionSet) { + this._managedGraphics.add(graphics); + this.renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(graphics); + } + updateRenderable(_graphics) { + } + execute(graphics) { + if (!graphics.isRenderable) return; + this._adaptor.execute(this, graphics); + } + destroy() { + this._managedGraphics.destroy(); + this.renderer = null; + this._adaptor.destroy(); + this._adaptor = null; + } + } + /** @ignore */ + CanvasGraphicsPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "graphics" + }; + + "use strict"; + function colorToUniform(rgb, alpha, out, offset) { + out[offset++] = (rgb >> 16 & 255) / 255; + out[offset++] = (rgb >> 8 & 255) / 255; + out[offset++] = (rgb & 255) / 255; + out[offset++] = alpha; + } + function color32BitToUniform(abgr, out, offset) { + const alpha = (abgr >> 24 & 255) / 255; + out[offset++] = (abgr & 255) / 255 * alpha; + out[offset++] = (abgr >> 8 & 255) / 255 * alpha; + out[offset++] = (abgr >> 16 & 255) / 255 * alpha; + out[offset++] = alpha; + } + + "use strict"; + class GraphicsGpuData { + constructor() { + this.batches = []; + this.batched = false; + } + destroy() { + this.batches.forEach((batch) => { + BigPool.return(batch); + }); + this.batches.length = 0; + } + } + class GraphicsPipe { + constructor(renderer, adaptor) { + this.state = State.for2d(); + this.renderer = renderer; + this._adaptor = adaptor; + this.renderer.runners.contextChange.add(this); + this._managedGraphics = new GCManagedHash({ renderer, type: "renderable", priority: -1, name: "graphics" }); + } + contextChange() { + this._adaptor.contextChange(this.renderer); + } + validateRenderable(graphics) { + const context = graphics.context; + const wasBatched = !!graphics._gpuData; + const contextSystem = this.renderer.graphicsContext; + const gpuContext = contextSystem.updateGpuContext(context); + if (gpuContext.isBatchable || wasBatched !== gpuContext.isBatchable) { + return true; + } + return false; + } + addRenderable(graphics, instructionSet) { + const contextSystem = this.renderer.graphicsContext; + const gpuContext = contextSystem.updateGpuContext(graphics.context); + if (graphics.didViewUpdate) { + this._rebuild(graphics); + } + if (gpuContext.isBatchable) { + this._addToBatcher(graphics, instructionSet); + } else { + this.renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(graphics); + } + } + updateRenderable(graphics) { + const gpuData = this._getGpuDataForRenderable(graphics); + const batches = gpuData.batches; + for (let i = 0; i < batches.length; i++) { + const batch = batches[i]; + batch._batcher.updateElement(batch); + } + } + execute(graphics) { + if (!graphics.isRenderable) return; + const renderer = this.renderer; + const context = graphics.context; + const contextSystem = renderer.graphicsContext; + if (!contextSystem.getGpuContext(context).batches.length) { + return; + } + const shader = context.customShader || this._adaptor.shader; + this.state.blendMode = graphics.groupBlendMode; + const localUniforms = shader.resources.localUniforms.uniforms; + localUniforms.uTransformMatrix = graphics.groupTransform; + localUniforms.uRound = renderer._roundPixels | graphics._roundPixels; + color32BitToUniform( + graphics.groupColorAlpha, + localUniforms.uColor, + 0 + ); + this._adaptor.execute(this, graphics); + } + _rebuild(graphics) { + const gpuData = this._getGpuDataForRenderable(graphics); + const contextSystem = this.renderer.graphicsContext; + const gpuContext = contextSystem.updateGpuContext(graphics.context); + gpuData.destroy(); + if (gpuContext.isBatchable) { + this._updateBatchesForRenderable(graphics, gpuData); + } + } + _addToBatcher(graphics, instructionSet) { + const batchPipe = this.renderer.renderPipes.batch; + const batches = this._getGpuDataForRenderable(graphics).batches; + for (let i = 0; i < batches.length; i++) { + const batch = batches[i]; + batchPipe.addToBatch(batch, instructionSet); + } + } + _getGpuDataForRenderable(graphics) { + return graphics._gpuData[this.renderer.uid] || this._initGpuDataForRenderable(graphics); + } + _initGpuDataForRenderable(graphics) { + const gpuData = new GraphicsGpuData(); + graphics._gpuData[this.renderer.uid] = gpuData; + this._managedGraphics.add(graphics); + return gpuData; + } + _updateBatchesForRenderable(graphics, gpuData) { + const context = graphics.context; + const contextSystem = this.renderer.graphicsContext; + const gpuContext = contextSystem.getGpuContext(context); + const roundPixels = this.renderer._roundPixels | graphics._roundPixels; + gpuData.batches = gpuContext.batches.map((batch) => { + const batchClone = BigPool.get(BatchableGraphics); + batch.copyTo(batchClone); + batchClone.renderable = graphics; + batchClone.roundPixels = roundPixels; + return batchClone; + }); + } + destroy() { + this._managedGraphics.destroy(); + this.renderer = null; + this._adaptor.destroy(); + this._adaptor = null; + this.state = null; + } + } + /** @ignore */ + GraphicsPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "graphics" + }; + + "use strict"; + extensions.add(CanvasGraphicsPipe); + extensions.add(GraphicsPipe); + extensions.add(CanvasGraphicsContextSystem); + extensions.add(GraphicsContextSystem); + + "use strict"; + var __defProp$I = Object.defineProperty; + var __getOwnPropSymbols$K = Object.getOwnPropertySymbols; + var __hasOwnProp$K = Object.prototype.hasOwnProperty; + var __propIsEnum$K = Object.prototype.propertyIsEnumerable; + var __defNormalProp$I = (obj, key, value) => key in obj ? __defProp$I(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$I = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$K.call(b, prop)) + __defNormalProp$I(a, prop, b[prop]); + if (__getOwnPropSymbols$K) + for (var prop of __getOwnPropSymbols$K(b)) { + if (__propIsEnum$K.call(b, prop)) + __defNormalProp$I(a, prop, b[prop]); + } + return a; + }; + var __objRest$f = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$K.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$K) + for (var prop of __getOwnPropSymbols$K(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$K.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class Graphics extends ViewContainer { + /** + * Creates a new Graphics object. + * @param options - Options for the Graphics. + */ + constructor(options) { + if (options instanceof GraphicsContext) { + options = { context: options }; + } + const _a = options || {}, { context, roundPixels } = _a, rest = __objRest$f(_a, ["context", "roundPixels"]); + super(__spreadValues$I({ + label: "Graphics" + }, rest)); + /** @internal */ + this.renderPipeId = "graphics"; + if (!context) { + this.context = this._ownedContext = new GraphicsContext(); + this.context.autoGarbageCollect = this.autoGarbageCollect; + } else { + this.context = context; + } + this.didViewUpdate = true; + this.allowChildren = false; + this.roundPixels = roundPixels != null ? roundPixels : false; + } + set context(context) { + if (context === this._context) return; + if (this._context) { + this._context.off("update", this.onViewUpdate, this); + this._context.off("unload", this.unload, this); + } + this._context = context; + this._context.on("update", this.onViewUpdate, this); + this._context.on("unload", this.unload, this); + this.onViewUpdate(); + } + /** + * The underlying graphics context used for drawing operations. + * Controls how shapes and paths are rendered. + * @example + * ```ts + * // Create a shared context + * const sharedContext = new GraphicsContext(); + * + * // Create graphics objects sharing the same context + * const graphics1 = new Graphics(); + * const graphics2 = new Graphics(); + * + * // Assign shared context + * graphics1.context = sharedContext; + * graphics2.context = sharedContext; + * + * // Both graphics will show the same shapes + * sharedContext + * .rect(0, 0, 100, 100) + * .fill({ color: 0xff0000 }); + * ``` + * @see {@link GraphicsContext} For drawing operations + * @see {@link GraphicsOptions} For context configuration + */ + get context() { + return this._context; + } + /** + * The local bounds of the graphics object. + * Returns the boundaries after all graphical operations but before any transforms. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Draw a shape + * graphics + * .rect(0, 0, 100, 100) + * .fill({ color: 0xff0000 }); + * + * // Get bounds information + * const bounds = graphics.bounds; + * console.log(bounds.width); // 100 + * console.log(bounds.height); // 100 + * ``` + * @readonly + * @see {@link Bounds} For bounds operations + * @see {@link Container#getBounds} For transformed bounds + */ + get bounds() { + return this._context.bounds; + } + /** + * Graphics objects do not need to update their bounds as the context handles this. + * @private + */ + updateBounds() { + } + /** + * Checks if the object contains the given point. + * Returns true if the point lies within the Graphics object's rendered area. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Draw a shape + * graphics + * .rect(0, 0, 100, 100) + * .fill({ color: 0xff0000 }); + * + * // Check point intersection + * if (graphics.containsPoint({ x: 50, y: 50 })) { + * console.log('Point is inside rectangle!'); + * } + * ``` + * @param point - The point to check in local coordinates + * @returns True if the point is inside the Graphics object + * @see {@link Graphics#bounds} For bounding box checks + * @see {@link PointData} For point data structure + */ + containsPoint(point) { + return this._context.containsPoint(point); + } + /** + * Destroys this graphics renderable and optionally its context. + * @param options - Options parameter. A boolean will act as if all options + * + * If the context was created by this graphics and `destroy(false)` or `destroy()` is called + * then the context will still be destroyed. + * + * If you want to explicitly not destroy this context that this graphics created, + * then you should pass destroy({ context: false }) + * + * If the context was passed in as an argument to the constructor then it will not be destroyed + * @example + * ```ts + * // Destroy the graphics and its context + * graphics.destroy(); + * graphics.destroy(true); + * graphics.destroy({ context: true, texture: true, textureSource: true }); + * ``` + */ + destroy(options) { + if (this._ownedContext && !options) { + this._ownedContext.destroy(options); + } else if (options === true || (options == null ? void 0 : options.context) === true) { + this._context.destroy(options); + } + this._ownedContext = null; + this._context = null; + super.destroy(options); + } + /** + * @param now - The current time in milliseconds. + * @internal + */ + _onTouch(now) { + this._gcLastUsed = now; + this._context._gcLastUsed = now; + } + _callContextMethod(method, args) { + this.context[method](...args); + return this; + } + // --------------------------------------- GraphicsContext methods --------------------------------------- + /** + * Sets the current fill style of the graphics context. + * The fill style can be a color, gradient, pattern, or a complex style object. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Basic color fill + * graphics + * .setFillStyle({ color: 0xff0000 }) // Red fill + * .rect(0, 0, 100, 100) + * .fill(); + * + * // Gradient fill + * const gradient = new FillGradient({ + * end: { x: 1, y: 0 }, + * colorStops: [ + * { offset: 0, color: 0xff0000 }, // Red at start + * { offset: 0.5, color: 0x00ff00 }, // Green at middle + * { offset: 1, color: 0x0000ff }, // Blue at end + * ], + * }); + * + * graphics + * .setFillStyle(gradient) + * .circle(100, 100, 50) + * .fill(); + * + * // Pattern fill + * const pattern = new FillPattern(texture); + * graphics + * .setFillStyle({ + * fill: pattern, + * alpha: 0.5 + * }) + * .rect(0, 0, 200, 200) + * .fill(); + * ``` + * @param {FillInput} args - The fill style to apply + * @returns The Graphics instance for chaining + * @see {@link FillStyle} For fill style options + * @see {@link FillGradient} For gradient fills + * @see {@link FillPattern} For pattern fills + */ + setFillStyle(...args) { + return this._callContextMethod("setFillStyle", args); + } + /** + * Sets the current stroke style of the graphics context. + * Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Basic color stroke + * graphics + * .setStrokeStyle({ + * width: 2, + * color: 0x000000 + * }) + * .rect(0, 0, 100, 100) + * .stroke(); + * + * // Complex stroke style + * graphics + * .setStrokeStyle({ + * width: 4, + * color: 0xff0000, + * alpha: 0.5, + * join: 'round', + * cap: 'round', + * alignment: 0.5 + * }) + * .circle(100, 100, 50) + * .stroke(); + * + * // Gradient stroke + * const gradient = new FillGradient({ + * end: { x: 1, y: 0 }, + * colorStops: [ + * { offset: 0, color: 0xff0000 }, // Red at start + * { offset: 0.5, color: 0x00ff00 }, // Green at middle + * { offset: 1, color: 0x0000ff }, // Blue at end + * ], + * }); + * + * graphics + * .setStrokeStyle({ + * width: 10, + * fill: gradient + * }) + * .poly([0,0, 100,50, 0,100]) + * .stroke(); + * ``` + * @param {StrokeInput} args - The stroke style to apply + * @returns The Graphics instance for chaining + * @see {@link StrokeStyle} For stroke style options + * @see {@link FillGradient} For gradient strokes + * @see {@link FillPattern} For pattern strokes + */ + setStrokeStyle(...args) { + return this._callContextMethod("setStrokeStyle", args); + } + fill(...args) { + return this._callContextMethod("fill", args); + } + /** + * Strokes the current path with the current stroke style or specified style. + * Outlines the shape using the stroke settings. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Stroke with direct color + * graphics + * .circle(50, 50, 25) + * .stroke({ + * width: 2, + * color: 0xff0000 + * }); // 2px red stroke + * + * // Fill with texture + * graphics + * .rect(0, 0, 100, 100) + * .stroke(myTexture); // Fill with texture + * + * // Stroke with gradient + * const gradient = new FillGradient({ + * end: { x: 1, y: 0 }, + * colorStops: [ + * { offset: 0, color: 0xff0000 }, + * { offset: 0.5, color: 0x00ff00 }, + * { offset: 1, color: 0x0000ff }, + * ], + * }); + * + * graphics + * .rect(0, 0, 100, 100) + * .stroke({ + * width: 4, + * fill: gradient, + * alignment: 0.5, + * join: 'round' + * }); + * ``` + * @param {StrokeStyle} args - Optional stroke style to apply. Can be: + * - A stroke style object with width, color, etc. + * - A gradient + * - A pattern + * If omitted, uses current stroke style. + * @returns The Graphics instance for chaining + * @see {@link StrokeStyle} For stroke style options + * @see {@link FillGradient} For gradient strokes + * @see {@link setStrokeStyle} For setting default stroke style + */ + stroke(...args) { + return this._callContextMethod("stroke", args); + } + texture(...args) { + return this._callContextMethod("texture", args); + } + /** + * Resets the current path. Any previous path and its commands are discarded and a new path is + * started. This is typically called before beginning a new shape or series of drawing commands. + * @example + * ```ts + * const graphics = new Graphics(); + * graphics + * .circle(150, 150, 50) + * .fill({ color: 0x00ff00 }) + * .beginPath() // Starts a new path + * .circle(250, 150, 50) + * .fill({ color: 0x0000ff }); + * ``` + * @returns The Graphics instance for chaining + * @see {@link Graphics#moveTo} For starting a new subpath + * @see {@link Graphics#closePath} For closing the current path + */ + beginPath() { + return this._callContextMethod("beginPath", []); + } + /** + * Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by + * subtracting a path from the previously drawn path. + * + * If a hole is not completely in a shape, it will fail to cut correctly. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Draw outer circle + * graphics + * .circle(100, 100, 50) + * .fill({ color: 0xff0000 }); + * .circle(100, 100, 25) // Inner circle + * .cut() // Cuts out the inner circle from the outer circle + * ``` + */ + cut() { + return this._callContextMethod("cut", []); + } + arc(...args) { + return this._callContextMethod("arc", args); + } + arcTo(...args) { + return this._callContextMethod("arcTo", args); + } + arcToSvg(...args) { + return this._callContextMethod("arcToSvg", args); + } + bezierCurveTo(...args) { + return this._callContextMethod("bezierCurveTo", args); + } + /** + * Closes the current path by drawing a straight line back to the start point. + * + * This is useful for completing shapes and ensuring they are properly closed for fills. + * @example + * ```ts + * // Create a triangle with closed path + * const graphics = new Graphics(); + * graphics + * .moveTo(50, 50) + * .lineTo(100, 100) + * .lineTo(0, 100) + * .closePath() + * ``` + * @returns The Graphics instance for method chaining + * @see {@link Graphics#beginPath} For starting a new path + * @see {@link Graphics#fill} For filling closed paths + * @see {@link Graphics#stroke} For stroking paths + */ + closePath() { + return this._callContextMethod("closePath", []); + } + ellipse(...args) { + return this._callContextMethod("ellipse", args); + } + circle(...args) { + return this._callContextMethod("circle", args); + } + path(...args) { + return this._callContextMethod("path", args); + } + lineTo(...args) { + return this._callContextMethod("lineTo", args); + } + moveTo(...args) { + return this._callContextMethod("moveTo", args); + } + quadraticCurveTo(...args) { + return this._callContextMethod("quadraticCurveTo", args); + } + rect(...args) { + return this._callContextMethod("rect", args); + } + roundRect(...args) { + return this._callContextMethod("roundRect", args); + } + poly(...args) { + return this._callContextMethod("poly", args); + } + regularPoly(...args) { + return this._callContextMethod("regularPoly", args); + } + roundPoly(...args) { + return this._callContextMethod("roundPoly", args); + } + roundShape(...args) { + return this._callContextMethod("roundShape", args); + } + filletRect(...args) { + return this._callContextMethod("filletRect", args); + } + chamferRect(...args) { + return this._callContextMethod("chamferRect", args); + } + star(...args) { + return this._callContextMethod("star", args); + } + svg(...args) { + return this._callContextMethod("svg", args); + } + restore(...args) { + return this._callContextMethod("restore", args); + } + /** + * Saves the current graphics state onto a stack. The state includes: + * - Current transformation matrix + * - Current fill style + * - Current stroke style + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Save state before complex operations + * graphics.save(); + * + * // Create transformed and styled shape + * graphics + * .translateTransform(100, 100) + * .rotateTransform(Math.PI / 4) + * .setFillStyle({ + * color: 0xff0000, + * alpha: 0.5 + * }) + * .rect(-25, -25, 50, 50) + * .fill(); + * + * // Restore to original state + * graphics.restore(); + * + * // Continue drawing with previous state + * graphics + * .circle(50, 50, 25) + * .fill(); + * ``` + * @returns The Graphics instance for method chaining + * @see {@link Graphics#restore} For restoring the saved state + * @see {@link Graphics#setTransform} For setting transformations + */ + save() { + return this._callContextMethod("save", []); + } + /** + * Returns the current transformation matrix of the graphics context. + * This matrix represents all accumulated transformations including translate, scale, and rotate. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Apply some transformations + * graphics + * .translateTransform(100, 100) + * .rotateTransform(Math.PI / 4); + * + * // Get the current transform matrix + * const matrix = graphics.getTransform(); + * console.log(matrix.tx, matrix.ty); // 100, 100 + * + * // Use the matrix for other operations + * graphics + * .setTransform(matrix) + * .circle(0, 0, 50) + * .fill({ color: 0xff0000 }); + * ``` + * @returns The current transformation matrix. + * @see {@link Graphics#setTransform} For setting the transform matrix + * @see {@link Matrix} For matrix operations + */ + getTransform() { + return this.context.getTransform(); + } + /** + * Resets the current transformation matrix to the identity matrix, effectively removing + * any transformations (rotation, scaling, translation) previously applied. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Apply transformations + * graphics + * .translateTransform(100, 100) + * .scaleTransform(2, 2) + * .circle(0, 0, 25) + * .fill({ color: 0xff0000 }); + * // Reset transform to default state + * graphics + * .resetTransform() + * .circle(50, 50, 25) // Will draw at actual coordinates + * .fill({ color: 0x00ff00 }); + * ``` + * @returns The Graphics instance for method chaining + * @see {@link Graphics#getTransform} For getting the current transform + * @see {@link Graphics#setTransform} For setting a specific transform + * @see {@link Graphics#save} For saving the current transform state + * @see {@link Graphics#restore} For restoring a previous transform state + */ + resetTransform() { + return this._callContextMethod("resetTransform", []); + } + rotateTransform(...args) { + return this._callContextMethod("rotate", args); + } + scaleTransform(...args) { + return this._callContextMethod("scale", args); + } + setTransform(...args) { + return this._callContextMethod("setTransform", args); + } + transform(...args) { + return this._callContextMethod("transform", args); + } + translateTransform(...args) { + return this._callContextMethod("translate", args); + } + /** + * Clears all drawing commands from the graphics context, effectively resetting it. + * This includes clearing the current path, fill style, stroke style, and transformations. + * + * > [!NOTE] Graphics objects are not designed to be continuously cleared and redrawn. + * > Instead, they are intended to be used for static or semi-static graphics that + * > can be redrawn as needed. Frequent clearing and redrawing may lead to performance issues. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Draw some shapes + * graphics + * .circle(100, 100, 50) + * .fill({ color: 0xff0000 }) + * .rect(200, 100, 100, 50) + * .fill({ color: 0x00ff00 }); + * + * // Clear all graphics + * graphics.clear(); + * + * // Start fresh with new shapes + * graphics + * .circle(150, 150, 30) + * .fill({ color: 0x0000ff }); + * ``` + * @returns The Graphics instance for method chaining + * @see {@link Graphics#beginPath} For starting a new path without clearing styles + * @see {@link Graphics#save} For saving the current state + * @see {@link Graphics#restore} For restoring a previous state + */ + clear() { + return this._callContextMethod("clear", []); + } + /** + * Gets or sets the current fill style for the graphics context. The fill style determines + * how shapes are filled when using the fill() method. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Basic color fill + * graphics.fillStyle = { + * color: 0xff0000, // Red + * alpha: 1 + * }; + * + * // Using gradients + * const gradient = new FillGradient({ + * end: { x: 0, y: 1 }, // Vertical gradient + * stops: [ + * { offset: 0, color: 0xff0000, alpha: 1 }, // Start color + * { offset: 1, color: 0x0000ff, alpha: 1 } // End color + * ] + * }); + * + * graphics.fillStyle = { + * fill: gradient, + * alpha: 0.8 + * }; + * + * // Using patterns + * graphics.fillStyle = { + * texture: myTexture, + * alpha: 1, + * matrix: new Matrix() + * .scale(0.5, 0.5) + * .rotate(Math.PI / 4) + * }; + * ``` + * @type {ConvertedFillStyle} + * @see {@link FillStyle} For all available fill style options + * @see {@link FillGradient} For creating gradient fills + * @see {@link Graphics#fill} For applying the fill to paths + */ + get fillStyle() { + return this._context.fillStyle; + } + set fillStyle(value) { + this._context.fillStyle = value; + } + /** + * Gets or sets the current stroke style for the graphics context. The stroke style determines + * how paths are outlined when using the stroke() method. + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Basic stroke style + * graphics.strokeStyle = { + * width: 2, + * color: 0xff0000, + * alpha: 1 + * }; + * + * // Using with gradients + * const gradient = new FillGradient({ + * end: { x: 0, y: 1 }, + * stops: [ + * { offset: 0, color: 0xff0000, alpha: 1 }, + * { offset: 1, color: 0x0000ff, alpha: 1 } + * ] + * }); + * + * graphics.strokeStyle = { + * width: 4, + * fill: gradient, + * alignment: 0.5, + * join: 'round', + * cap: 'round' + * }; + * + * // Complex stroke settings + * graphics.strokeStyle = { + * width: 6, + * color: 0x00ff00, + * alpha: 0.5, + * join: 'miter', + * miterLimit: 10, + * }; + * ``` + * @see {@link StrokeStyle} For all available stroke style options + * @see {@link Graphics#stroke} For applying the stroke to paths + */ + get strokeStyle() { + return this._context.strokeStyle; + } + set strokeStyle(value) { + this._context.strokeStyle = value; + } + /** + * Creates a new Graphics object that copies the current graphics content. + * The clone can either share the same context (shallow clone) or have its own independent + * context (deep clone). + * @example + * ```ts + * const graphics = new Graphics(); + * + * // Create original graphics content + * graphics + * .circle(100, 100, 50) + * .fill({ color: 0xff0000 }); + * + * // Create a shallow clone (shared context) + * const shallowClone = graphics.clone(); + * + * // Changes to original affect the clone + * graphics + * .circle(200, 100, 30) + * .fill({ color: 0x00ff00 }); + * + * // Create a deep clone (independent context) + * const deepClone = graphics.clone(true); + * + * // Modify deep clone independently + * deepClone + * .translateTransform(100, 100) + * .circle(0, 0, 40) + * .fill({ color: 0x0000ff }); + * ``` + * @param deep - Whether to create a deep clone of the graphics object. + * If false (default), the context will be shared between objects. + * If true, creates an independent copy of the context. + * @returns A new Graphics instance with either shared or copied context + * @see {@link Graphics#context} For accessing the underlying graphics context + * @see {@link GraphicsContext} For understanding the shared context behavior + */ + clone(deep = false) { + if (deep) { + return new Graphics(this._context.clone()); + } + this._ownedContext = null; + const clone = new Graphics(this._context); + return clone; + } + // -------- v7 deprecations --------- + /** + * @param width + * @param color + * @param alpha + * @deprecated since 8.0.0 Use {@link Graphics#setStrokeStyle} instead + */ + lineStyle(width, color, alpha) { + deprecation(v8_0_0, "Graphics#lineStyle is no longer needed. Use Graphics#setStrokeStyle to set the stroke style."); + const strokeStyle = {}; + width && (strokeStyle.width = width); + color && (strokeStyle.color = color); + alpha && (strokeStyle.alpha = alpha); + this.context.strokeStyle = strokeStyle; + return this; + } + /** + * @param color + * @param alpha + * @deprecated since 8.0.0 Use {@link Graphics#fill} instead + */ + beginFill(color, alpha) { + deprecation(v8_0_0, "Graphics#beginFill is no longer needed. Use Graphics#fill to fill the shape with the desired style."); + const fillStyle = {}; + if (color !== void 0) fillStyle.color = color; + if (alpha !== void 0) fillStyle.alpha = alpha; + this.context.fillStyle = fillStyle; + return this; + } + /** + * @deprecated since 8.0.0 Use {@link Graphics#fill} instead + */ + endFill() { + deprecation(v8_0_0, "Graphics#endFill is no longer needed. Use Graphics#fill to fill the shape with the desired style."); + this.context.fill(); + const strokeStyle = this.context.strokeStyle; + if (strokeStyle.width !== GraphicsContext.defaultStrokeStyle.width || strokeStyle.color !== GraphicsContext.defaultStrokeStyle.color || strokeStyle.alpha !== GraphicsContext.defaultStrokeStyle.alpha) { + this.context.stroke(); + } + return this; + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#circle} instead + */ + drawCircle(...args) { + deprecation(v8_0_0, "Graphics#drawCircle has been renamed to Graphics#circle"); + return this._callContextMethod("circle", args); + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#ellipse} instead + */ + drawEllipse(...args) { + deprecation(v8_0_0, "Graphics#drawEllipse has been renamed to Graphics#ellipse"); + return this._callContextMethod("ellipse", args); + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#poly} instead + */ + drawPolygon(...args) { + deprecation(v8_0_0, "Graphics#drawPolygon has been renamed to Graphics#poly"); + return this._callContextMethod("poly", args); + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#rect} instead + */ + drawRect(...args) { + deprecation(v8_0_0, "Graphics#drawRect has been renamed to Graphics#rect"); + return this._callContextMethod("rect", args); + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#roundRect} instead + */ + drawRoundedRect(...args) { + deprecation(v8_0_0, "Graphics#drawRoundedRect has been renamed to Graphics#roundRect"); + return this._callContextMethod("roundRect", args); + } + /** + * @param {...any} args + * @deprecated since 8.0.0 Use {@link Graphics#star} instead + */ + drawStar(...args) { + deprecation(v8_0_0, "Graphics#drawStar has been renamed to Graphics#star"); + return this._callContextMethod("star", args); + } + } + + "use strict"; + var __defProp$H = Object.defineProperty; + var __getOwnPropSymbols$J = Object.getOwnPropertySymbols; + var __hasOwnProp$J = Object.prototype.hasOwnProperty; + var __propIsEnum$J = Object.prototype.propertyIsEnumerable; + var __defNormalProp$H = (obj, key, value) => key in obj ? __defProp$H(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$H = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$J.call(b, prop)) + __defNormalProp$H(a, prop, b[prop]); + if (__getOwnPropSymbols$J) + for (var prop of __getOwnPropSymbols$J(b)) { + if (__propIsEnum$J.call(b, prop)) + __defNormalProp$H(a, prop, b[prop]); + } + return a; + }; + const _MeshGeometry = class _MeshGeometry extends Geometry { + constructor(...args) { + var _a; + let options = (_a = args[0]) != null ? _a : {}; + if (options instanceof Float32Array) { + deprecation(v8_0_0, "use new MeshGeometry({ positions, uvs, indices }) instead"); + options = { + positions: options, + uvs: args[1], + indices: args[2] + }; + } + options = __spreadValues$H(__spreadValues$H({}, _MeshGeometry.defaultOptions), options); + const positions = options.positions || new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]); + let uvs = options.uvs; + if (!uvs) { + if (options.positions) { + uvs = new Float32Array(positions.length); + } else { + uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]); + } + } + const indices = options.indices || new Uint32Array([0, 1, 2, 0, 2, 3]); + const shrinkToFit = options.shrinkBuffersToFit; + const positionBuffer = new Buffer({ + data: positions, + label: "attribute-mesh-positions", + shrinkToFit, + usage: BufferUsage.VERTEX | BufferUsage.COPY_DST + }); + const uvBuffer = new Buffer({ + data: uvs, + label: "attribute-mesh-uvs", + shrinkToFit, + usage: BufferUsage.VERTEX | BufferUsage.COPY_DST + }); + const indexBuffer = new Buffer({ + data: indices, + label: "index-mesh-buffer", + shrinkToFit, + usage: BufferUsage.INDEX | BufferUsage.COPY_DST + }); + super({ + attributes: { + aPosition: { + buffer: positionBuffer, + format: "float32x2", + stride: 2 * 4, + offset: 0 + }, + aUV: { + buffer: uvBuffer, + format: "float32x2", + stride: 2 * 4, + offset: 0 + } + }, + indexBuffer, + topology: options.topology + }); + this.batchMode = "auto"; + } + /** The positions of the mesh. */ + get positions() { + return this.attributes.aPosition.buffer.data; + } + /** + * Set the positions of the mesh. + * When setting the positions, its important that the uvs array is at least as long as the positions array. + * otherwise the geometry will not be valid. + * @param {Float32Array} value - The positions of the mesh. + */ + set positions(value) { + this.attributes.aPosition.buffer.data = value; + } + /** The UVs of the mesh. */ + get uvs() { + return this.attributes.aUV.buffer.data; + } + /** + * Set the UVs of the mesh. + * Its important that the uvs array you set is at least as long as the positions array. + * otherwise the geometry will not be valid. + * @param {Float32Array} value - The UVs of the mesh. + */ + set uvs(value) { + this.attributes.aUV.buffer.data = value; + } + /** The indices of the mesh. */ + get indices() { + return this.indexBuffer.data; + } + set indices(value) { + this.indexBuffer.data = value; + } + }; + _MeshGeometry.defaultOptions = { + topology: "triangle-list", + shrinkBuffersToFit: false + }; + let MeshGeometry = _MeshGeometry; + + "use strict"; + class BatchableMesh { + constructor() { + this.batcherName = "default"; + this.packAsQuad = false; + this.indexOffset = 0; + this.attributeOffset = 0; + this.roundPixels = 0; + this._batcher = null; + this._batch = null; + this._textureMatrixUpdateId = -1; + this._uvUpdateId = -1; + } + get blendMode() { + return this.renderable.groupBlendMode; + } + get topology() { + return this._topology || this.geometry.topology; + } + set topology(value) { + this._topology = value; + } + reset() { + this.renderable = null; + this.texture = null; + this._batcher = null; + this._batch = null; + this.geometry = null; + this._uvUpdateId = -1; + this._textureMatrixUpdateId = -1; + } + /** + * Sets the texture for the batchable mesh. + * As it does so, it resets the texture matrix update ID. + * this is to ensure that the texture matrix is recalculated when the uvs are referenced + * @param value - The texture to set. + */ + setTexture(value) { + if (this.texture === value) return; + this.texture = value; + this._textureMatrixUpdateId = -1; + } + get uvs() { + const geometry = this.geometry; + const uvBuffer = geometry.getBuffer("aUV"); + const uvs = uvBuffer.data; + let transformedUvs = uvs; + const textureMatrix = this.texture.textureMatrix; + if (!textureMatrix.isSimple) { + transformedUvs = this._transformedUvs; + if (this._textureMatrixUpdateId !== textureMatrix._updateID || this._uvUpdateId !== uvBuffer._updateID) { + if (!transformedUvs || transformedUvs.length < uvs.length) { + transformedUvs = this._transformedUvs = new Float32Array(uvs.length); + } + this._textureMatrixUpdateId = textureMatrix._updateID; + this._uvUpdateId = uvBuffer._updateID; + textureMatrix.multiplyUvs(uvs, transformedUvs); + } + } + return transformedUvs; + } + get positions() { + return this.geometry.positions; + } + get indices() { + return this.geometry.indices; + } + get color() { + return this.renderable.groupColorAlpha; + } + get groupTransform() { + return this.renderable.groupTransform; + } + get attributeSize() { + return this.geometry.positions.length / 2; + } + get indexSize() { + return this.geometry.indices.length; + } + } + + "use strict"; + class MeshGpuData { + destroy() { + } + } + class MeshPipe { + constructor(renderer, adaptor) { + this.localUniforms = new UniformGroup({ + uTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4" }, + uRound: { value: 0, type: "f32" } + }); + this.localUniformsBindGroup = new BindGroup({ + 0: this.localUniforms + }); + this.renderer = renderer; + this._adaptor = adaptor; + this._adaptor.init(); + } + validateRenderable(mesh) { + const meshData = this._getMeshData(mesh); + const wasBatched = meshData.batched; + const isBatched = mesh.batched; + meshData.batched = isBatched; + if (wasBatched !== isBatched) { + return true; + } else if (isBatched) { + const geometry = mesh._geometry; + if (geometry.indices.length !== meshData.indexSize || geometry.positions.length !== meshData.vertexSize) { + meshData.indexSize = geometry.indices.length; + meshData.vertexSize = geometry.positions.length; + return true; + } + const batchableMesh = this._getBatchableMesh(mesh); + if (batchableMesh.texture.uid !== mesh._texture.uid) { + batchableMesh._textureMatrixUpdateId = -1; + } + return !batchableMesh._batcher.checkAndUpdateTexture( + batchableMesh, + mesh._texture + ); + } + return false; + } + addRenderable(mesh, instructionSet) { + var _a, _b; + const batcher = this.renderer.renderPipes.batch; + const meshData = this._getMeshData(mesh); + if (mesh.didViewUpdate) { + meshData.indexSize = (_a = mesh._geometry.indices) == null ? void 0 : _a.length; + meshData.vertexSize = (_b = mesh._geometry.positions) == null ? void 0 : _b.length; + } + if (meshData.batched) { + const gpuBatchableMesh = this._getBatchableMesh(mesh); + gpuBatchableMesh.setTexture(mesh._texture); + gpuBatchableMesh.geometry = mesh._geometry; + batcher.addToBatch(gpuBatchableMesh, instructionSet); + } else { + batcher.break(instructionSet); + instructionSet.add(mesh); + } + } + updateRenderable(mesh) { + if (mesh.batched) { + const gpuBatchableMesh = this._getBatchableMesh(mesh); + gpuBatchableMesh.setTexture(mesh._texture); + gpuBatchableMesh.geometry = mesh._geometry; + gpuBatchableMesh._batcher.updateElement(gpuBatchableMesh); + } + } + execute(mesh) { + if (!mesh.isRenderable) return; + mesh.state.blendMode = getAdjustedBlendModeBlend(mesh.groupBlendMode, mesh.texture._source); + const localUniforms = this.localUniforms; + localUniforms.uniforms.uTransformMatrix = mesh.groupTransform; + localUniforms.uniforms.uRound = this.renderer._roundPixels | mesh._roundPixels; + localUniforms.update(); + color32BitToUniform( + mesh.groupColorAlpha, + localUniforms.uniforms.uColor, + 0 + ); + this._adaptor.execute(this, mesh); + } + _getMeshData(mesh) { + var _a, _b; + (_a = mesh._gpuData)[_b = this.renderer.uid] || (_a[_b] = new MeshGpuData()); + return mesh._gpuData[this.renderer.uid].meshData || this._initMeshData(mesh); + } + _initMeshData(mesh) { + mesh._gpuData[this.renderer.uid].meshData = { + batched: mesh.batched, + indexSize: 0, + vertexSize: 0 + }; + return mesh._gpuData[this.renderer.uid].meshData; + } + _getBatchableMesh(mesh) { + var _a, _b; + (_a = mesh._gpuData)[_b = this.renderer.uid] || (_a[_b] = new MeshGpuData()); + return mesh._gpuData[this.renderer.uid].batchableMesh || this._initBatchableMesh(mesh); + } + _initBatchableMesh(mesh) { + const gpuMesh = new BatchableMesh(); + gpuMesh.renderable = mesh; + gpuMesh.setTexture(mesh._texture); + gpuMesh.transform = mesh.groupTransform; + gpuMesh.roundPixels = this.renderer._roundPixels | mesh._roundPixels; + mesh._gpuData[this.renderer.uid].batchableMesh = gpuMesh; + return gpuMesh; + } + destroy() { + this.localUniforms = null; + this.localUniformsBindGroup = null; + this._adaptor.destroy(); + this._adaptor = null; + this.renderer = null; + } + } + /** @ignore */ + MeshPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "mesh" + }; + + "use strict"; + extensions.add(MeshPipe); + + "use strict"; + var __defProp$G = Object.defineProperty; + var __getOwnPropSymbols$I = Object.getOwnPropertySymbols; + var __hasOwnProp$I = Object.prototype.hasOwnProperty; + var __propIsEnum$I = Object.prototype.propertyIsEnumerable; + var __defNormalProp$G = (obj, key, value) => key in obj ? __defProp$G(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$G = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$I.call(b, prop)) + __defNormalProp$G(a, prop, b[prop]); + if (__getOwnPropSymbols$I) + for (var prop of __getOwnPropSymbols$I(b)) { + if (__propIsEnum$I.call(b, prop)) + __defNormalProp$G(a, prop, b[prop]); + } + return a; + }; + var __objRest$e = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$I.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$I) + for (var prop of __getOwnPropSymbols$I(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$I.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class Mesh extends ViewContainer { + constructor(...args) { + var _b; + let options = args[0]; + if (options instanceof Geometry) { + deprecation(v8_0_0, "Mesh: use new Mesh({ geometry, shader }) instead"); + options = { + geometry: options, + shader: args[1] + }; + if (args[3]) { + deprecation(v8_0_0, "Mesh: drawMode argument has been removed, use geometry.topology instead"); + options.geometry.topology = args[3]; + } + } + const _a = options, { geometry, shader, texture, roundPixels, state } = _a, rest = __objRest$e(_a, ["geometry", "shader", "texture", "roundPixels", "state"]); + super(__spreadValues$G({ + label: "Mesh" + }, rest)); + /** @internal */ + this.renderPipeId = "mesh"; + /** @internal */ + this._shader = null; + this.allowChildren = false; + this.shader = shader != null ? shader : null; + this.texture = (_b = texture != null ? texture : shader == null ? void 0 : shader.texture) != null ? _b : Texture.WHITE; + this.state = state != null ? state : State.for2d(); + this._geometry = geometry; + this._geometry.on("update", this.onViewUpdate, this); + this.roundPixels = roundPixels != null ? roundPixels : false; + } + /** Alias for {@link Mesh#shader}. */ + get material() { + deprecation(v8_0_0, "mesh.material property has been removed, use mesh.shader instead"); + return this._shader; + } + /** + * Represents the vertex and fragment shaders that processes the geometry and runs on the GPU. + * Can be shared between multiple Mesh objects. + */ + set shader(value) { + if (this._shader === value) return; + this._shader = value; + this.onViewUpdate(); + } + get shader() { + return this._shader; + } + /** + * Includes vertex positions, face indices, colors, UVs, and + * custom attributes within buffers, reducing the cost of passing all + * this data to the GPU. Can be shared between multiple Mesh objects. + */ + set geometry(value) { + var _a; + if (this._geometry === value) return; + (_a = this._geometry) == null ? void 0 : _a.off("update", this.onViewUpdate, this); + value.on("update", this.onViewUpdate, this); + this._geometry = value; + this.onViewUpdate(); + } + get geometry() { + return this._geometry; + } + /** The texture that the Mesh uses. Null for non-MeshMaterial shaders */ + set texture(value) { + value || (value = Texture.EMPTY); + const currentTexture = this._texture; + if (currentTexture === value) return; + if (currentTexture && currentTexture.dynamic) currentTexture.off("update", this.onViewUpdate, this); + if (value.dynamic) value.on("update", this.onViewUpdate, this); + if (this.shader) { + this.shader.texture = value; + } + this._texture = value; + this.onViewUpdate(); + } + get texture() { + return this._texture; + } + get batched() { + if (this._shader) return false; + if ((this.state.data & 12) !== 0) return false; + if (this._geometry instanceof MeshGeometry) { + if (this._geometry.batchMode === "auto") { + return this._geometry.positions.length / 2 <= 100; + } + return this._geometry.batchMode === "batch"; + } + return false; + } + /** + * The local bounds of the mesh. + * @type {Bounds} + */ + get bounds() { + return this._geometry.bounds; + } + /** + * Update local bounds of the mesh. + * @private + */ + updateBounds() { + this._bounds = this._geometry.bounds; + } + /** + * Checks if the object contains the given point. + * @param point - The point to check + */ + containsPoint(point) { + const { x, y } = point; + if (!this.bounds.containsPoint(x, y)) return false; + const vertices = this.geometry.getBuffer("aPosition").data; + const step = this.geometry.topology === "triangle-strip" ? 3 : 1; + if (this.geometry.getIndex()) { + const indices = this.geometry.getIndex().data; + const len = indices.length; + for (let i = 0; i + 2 < len; i += step) { + const ind0 = indices[i] * 2; + const ind1 = indices[i + 1] * 2; + const ind2 = indices[i + 2] * 2; + if (pointInTriangle$1( + x, + y, + vertices[ind0], + vertices[ind0 + 1], + vertices[ind1], + vertices[ind1 + 1], + vertices[ind2], + vertices[ind2 + 1] + )) { + return true; + } + } + } else { + const len = vertices.length / 2; + for (let i = 0; i + 2 < len; i += step) { + const ind0 = i * 2; + const ind1 = (i + 1) * 2; + const ind2 = (i + 2) * 2; + if (pointInTriangle$1( + x, + y, + vertices[ind0], + vertices[ind0 + 1], + vertices[ind1], + vertices[ind1 + 1], + vertices[ind2], + vertices[ind2 + 1] + )) { + return true; + } + } + } + return false; + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * mesh.destroy(); + * mesh.destroy(true); + * mesh.destroy({ texture: true, textureSource: true }); + */ + destroy(options) { + var _a; + super.destroy(options); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + this._texture.destroy(destroyTextureSource); + } + (_a = this._geometry) == null ? void 0 : _a.off("update", this.onViewUpdate, this); + this._texture = null; + this._geometry = null; + this._shader = null; + } + } + + "use strict"; + var __defProp$F = Object.defineProperty; + var __defProps$j = Object.defineProperties; + var __getOwnPropDescs$j = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$H = Object.getOwnPropertySymbols; + var __hasOwnProp$H = Object.prototype.hasOwnProperty; + var __propIsEnum$H = Object.prototype.propertyIsEnumerable; + var __defNormalProp$F = (obj, key, value) => key in obj ? __defProp$F(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$F = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$H.call(b, prop)) + __defNormalProp$F(a, prop, b[prop]); + if (__getOwnPropSymbols$H) + for (var prop of __getOwnPropSymbols$H(b)) { + if (__propIsEnum$H.call(b, prop)) + __defNormalProp$F(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$j = (a, b) => __defProps$j(a, __getOwnPropDescs$j(b)); + var __objRest$d = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$H.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$H) + for (var prop of __getOwnPropSymbols$H(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$H.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class AnimatedSprite extends Sprite { + constructor(...args) { + let options = args[0]; + if (Array.isArray(args[0])) { + options = { + textures: args[0], + autoUpdate: args[1] + }; + } + const _a = options, { + animationSpeed = 1, + autoPlay = false, + autoUpdate = true, + loop = true, + onComplete = null, + onFrameChange = null, + onLoop = null, + textures, + updateAnchor = false + } = _a, rest = __objRest$d(_a, [ + "animationSpeed", + "autoPlay", + "autoUpdate", + "loop", + "onComplete", + "onFrameChange", + "onLoop", + "textures", + "updateAnchor" + ]); + const [firstFrame] = textures; + super(__spreadProps$j(__spreadValues$F({}, rest), { + texture: firstFrame instanceof Texture ? firstFrame : firstFrame.texture + })); + this._textures = null; + this._durations = null; + this._autoUpdate = autoUpdate; + this._isConnectedToTicker = false; + this.animationSpeed = animationSpeed; + this.loop = loop; + this.updateAnchor = updateAnchor; + this.onComplete = onComplete; + this.onFrameChange = onFrameChange; + this.onLoop = onLoop; + this._currentTime = 0; + this._playing = false; + this._previousFrame = null; + this.textures = textures; + if (autoPlay) { + this.play(); + } + } + /** + * Stops the animation playback and freezes the current frame. + * Does not reset the current frame or animation progress. + * @example + * ```ts + * // Create an animated sprite + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('walk1.png'), + * Texture.from('walk2.png'), + * Texture.from('walk3.png') + * ], + * autoPlay: true + * }); + * + * // Stop at current frame + * sprite.stop(); + * + * // Stop at specific frame + * sprite.gotoAndStop(1); // Stops at second frame + * + * // Stop and reset + * sprite.stop(); + * sprite.currentFrame = 0; + * + * // Stop with completion check + * if (sprite.playing) { + * sprite.stop(); + * sprite.onComplete?.(); + * } + * ``` + * @see {@link AnimatedSprite#play} For starting playback + * @see {@link AnimatedSprite#gotoAndStop} For stopping at a specific frame + * @see {@link AnimatedSprite#playing} For checking play state + */ + stop() { + if (!this._playing) { + return; + } + this._playing = false; + if (this._autoUpdate && this._isConnectedToTicker) { + Ticker.shared.remove(this.update, this); + this._isConnectedToTicker = false; + } + } + /** + * Starts or resumes the animation playback. + * If the animation was previously stopped, it will continue from where it left off. + * @example + * ```ts + * // Basic playback + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('walk1.png'), + * Texture.from('walk2.png'), + * ], + * autoPlay: false + * }); + * sprite.play(); + * + * // Play after stopping + * sprite.stop(); + * sprite.currentFrame = 0; // Reset to start + * sprite.play(); // Play from beginning + * + * // Play with auto-update disabled + * sprite.autoUpdate = false; + * sprite.play(); + * app.ticker.add(() => { + * sprite.update(app.ticker); // Manual updates + * }); + * ``` + * @see {@link AnimatedSprite#stop} For stopping playback + * @see {@link AnimatedSprite#gotoAndPlay} For playing from a specific frame + * @see {@link AnimatedSprite#playing} For checking play state + */ + play() { + if (this._playing) { + return; + } + this._playing = true; + if (this._autoUpdate && !this._isConnectedToTicker) { + Ticker.shared.add(this.update, this, UPDATE_PRIORITY.HIGH); + this._isConnectedToTicker = true; + } + } + /** + * Stops the AnimatedSprite and sets it to a specific frame. + * @example + * ```ts + * // Create an animated sprite + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('walk1.png'), + * Texture.from('walk2.png'), + * Texture.from('walk3.png'), + * ] + * }); + * + * // Go to specific frames + * sprite.gotoAndStop(0); // First frame + * sprite.gotoAndStop(2); // Third frame + * + * // Jump to last frame + * sprite.gotoAndStop(sprite.totalFrames - 1); + * ``` + * @param frameNumber - Frame index to stop at (0-based) + * @throws {Error} If frameNumber is out of bounds + * @see {@link AnimatedSprite#gotoAndPlay} For going to a frame and playing + * @see {@link AnimatedSprite#currentFrame} For getting/setting current frame + * @see {@link AnimatedSprite#totalFrames} For total number of frames + */ + gotoAndStop(frameNumber) { + this.stop(); + this.currentFrame = frameNumber; + } + /** + * Goes to a specific frame and begins playing the AnimatedSprite from that point. + * Combines frame navigation and playback start in one operation. + * @example + * ```ts + * // Start from specific frame + * sprite.gotoAndPlay(1); // Starts playing from second frame + * ``` + * @param frameNumber - Frame index to start playing from (0-based) + * @throws {Error} If frameNumber is out of bounds + * @see {@link AnimatedSprite#gotoAndStop} For going to a frame without playing + * @see {@link AnimatedSprite#play} For playing from current frame + * @see {@link AnimatedSprite#currentFrame} For getting/setting current frame + */ + gotoAndPlay(frameNumber) { + this.currentFrame = frameNumber; + this.play(); + } + /** + * Updates the object transform for rendering. This method handles animation timing, frame updates, + * and manages looping behavior. + * @example + * ```ts + * // Create an animated sprite with manual updates + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('frame1.png'), + * Texture.from('frame2.png'), + * Texture.from('frame3.png') + * ], + * autoUpdate: false // Disable automatic updates + * }); + * + * // Manual update with app ticker + * app.ticker.add((ticker) => { + * sprite.update(ticker); + * }); + * ``` + * @param ticker - The ticker to use for updating the animation timing + * @see {@link AnimatedSprite#autoUpdate} For controlling automatic updates + * @see {@link AnimatedSprite#animationSpeed} For controlling animation speed + * @see {@link Ticker} For timing system details + */ + update(ticker) { + if (!this._playing) { + return; + } + const deltaTime = ticker.deltaTime; + const elapsed = this.animationSpeed * deltaTime; + const previousFrame = this.currentFrame; + if (this._durations !== null) { + let lag = this._currentTime % 1 * this._durations[this.currentFrame]; + lag += elapsed / 60 * 1e3; + while (lag < 0) { + this._currentTime--; + lag += this._durations[this.currentFrame]; + } + const sign = Math.sign(this.animationSpeed * deltaTime); + this._currentTime = Math.floor(this._currentTime); + while (lag >= this._durations[this.currentFrame]) { + lag -= this._durations[this.currentFrame] * sign; + this._currentTime += sign; + } + this._currentTime += lag / this._durations[this.currentFrame]; + } else { + this._currentTime += elapsed; + } + if (this._currentTime < 0 && !this.loop) { + this.gotoAndStop(0); + if (this.onComplete) { + this.onComplete(); + } + } else if (this._currentTime >= this._textures.length && !this.loop) { + this.gotoAndStop(this._textures.length - 1); + if (this.onComplete) { + this.onComplete(); + } + } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame || this.animationSpeed < 0 && this.currentFrame > previousFrame) { + this.onLoop(); + } + } + this._updateTexture(); + } + } + /** Updates the displayed texture to match the current frame index. */ + _updateTexture() { + const currentFrame = this.currentFrame; + if (this._previousFrame === currentFrame) { + return; + } + this._previousFrame = currentFrame; + this.texture = this._textures[currentFrame]; + if (this.updateAnchor && this.texture.defaultAnchor) { + this.anchor.copyFrom(this.texture.defaultAnchor); + } + if (this.onFrameChange) { + this.onFrameChange(this.currentFrame); + } + } + /** + * Stops the AnimatedSprite and destroys it. + * This method stops the animation playback, removes it from the ticker, + * and cleans up any resources associated with the sprite. + * @param options - Options for destroying the sprite, such as whether to remove from parent + * @example + * ```ts + * // Destroy the sprite when done + * sprite.destroy(); + * // Or with options + * sprite.destroy({ children: true, texture: true, textureSource: true }); + * ``` + */ + destroy(options = false) { + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + this._textures.forEach((texture) => { + if (this.texture !== texture) { + texture.destroy(destroyTextureSource); + } + }); + } + this._textures = []; + this._durations = null; + this.stop(); + super.destroy(options); + this.onComplete = null; + this.onFrameChange = null; + this.onLoop = null; + } + /** + * A short hand way of creating an AnimatedSprite from an array of frame ids. + * Uses texture frames from the cache to create an animation sequence. + * @example + * ```ts + * // Create from frame IDs + * const frameIds = [ + * 'walk_001.png', + * 'walk_002.png', + * 'walk_003.png' + * ]; + * + * const walkingAnimation = AnimatedSprite.fromFrames(frameIds); + * walkingAnimation.play(); + * ``` + * @param frames - The array of frame ids to use for the animation + * @returns A new animated sprite using the frames + * @see {@link Texture.from} For texture creation from frames + * @see {@link Spritesheet} For loading spritesheets + */ + static fromFrames(frames) { + const textures = []; + for (let i = 0; i < frames.length; ++i) { + textures.push(Texture.from(frames[i])); + } + return new AnimatedSprite(textures); + } + /** + * A short hand way of creating an AnimatedSprite from an array of image urls. + * Each image will be used as a frame in the animation. + * @example + * ```ts + * // Create from image URLs + * const images = [ + * 'assets/walk1.png', + * 'assets/walk2.png', + * 'assets/walk3.png' + * ]; + * + * const walkingSprite = AnimatedSprite.fromImages(images); + * walkingSprite.play(); + * ``` + * @param images - The array of image urls to use as frames + * @returns A new animated sprite using the images as frames + * @see {@link Assets} For asset loading and management + * @see {@link Texture.from} For texture creation from images + */ + static fromImages(images) { + const textures = []; + for (let i = 0; i < images.length; ++i) { + textures.push(Texture.from(images[i])); + } + return new AnimatedSprite(textures); + } + /** + * The total number of frames in the AnimatedSprite. This is the same as number of textures + * assigned to the AnimatedSprite. + * @example + * ```ts + * // Create an animated sprite + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('frame1.png'), + * Texture.from('frame2.png'), + * Texture.from('frame3.png') + * ] + * }); + * + * // Get total frames + * console.log(sprite.totalFrames); // Outputs: 3 + * + * // Use with frame navigation + * sprite.gotoAndStop(sprite.totalFrames - 1); // Go to last frame + * ``` + * @readonly + * @see {@link AnimatedSprite#currentFrame} For the current frame index + * @see {@link AnimatedSprite#textures} For the array of textures + * @returns {number} The total number of frames + */ + get totalFrames() { + return this._textures.length; + } + /** + * The array of textures or frame objects used for the animation sequence. + * Can be set to either an array of Textures or an array of FrameObjects with custom timing. + * @example + * ```ts + * // Update textures at runtime + * sprite.textures = [ + * Texture.from('run1.png'), + * Texture.from('run2.png') + * ]; + * + * // Use custom frame timing + * sprite.textures = [ + * { texture: Texture.from('explosion1.png'), time: 100 }, + * { texture: Texture.from('explosion2.png'), time: 200 }, + * { texture: Texture.from('explosion3.png'), time: 300 } + * ]; + * + * // Use with spritesheet + * const sheet = await Assets.load('animations.json'); + * sprite.textures = sheet.animations['walk']; + * ``` + * @type {AnimatedSpriteFrames} + * @see {@link FrameObject} For frame timing options + * @see {@link Spritesheet} For loading from spritesheets + */ + get textures() { + return this._textures; + } + set textures(value) { + if (value[0] instanceof Texture) { + this._textures = value; + this._durations = null; + } else { + this._textures = []; + this._durations = []; + for (let i = 0; i < value.length; i++) { + this._textures.push(value[i].texture); + this._durations.push(value[i].time); + } + } + this._previousFrame = null; + this.gotoAndStop(0); + this._updateTexture(); + } + /** + * Gets or sets the current frame index of the animation. + * When setting, the value will be clamped between 0 and totalFrames - 1. + * @example + * ```ts + * // Create an animated sprite + * const sprite = new AnimatedSprite({ + * textures: [ + * Texture.from('walk1.png'), + * Texture.from('walk2.png'), + * Texture.from('walk3.png') + * ] + * }); + * + * // Get current frame + * console.log(sprite.currentFrame); // 0 + * + * // Set specific frame + * sprite.currentFrame = 1; // Show second frame + * + * // Use with frame callbacks + * sprite.onFrameChange = (frame) => { + * console.log(`Now showing frame: ${frame}`); + * }; + * sprite.currentFrame = 2; + * ``` + * @throws {Error} If attempting to set a frame index out of bounds + * @see {@link AnimatedSprite#totalFrames} For the total number of frames + * @see {@link AnimatedSprite#gotoAndPlay} For playing from a specific frame + * @see {@link AnimatedSprite#gotoAndStop} For stopping at a specific frame + */ + get currentFrame() { + let currentFrame = Math.floor(this._currentTime) % this._textures.length; + if (currentFrame < 0) { + currentFrame += this._textures.length; + } + return currentFrame; + } + set currentFrame(value) { + if (value < 0 || value > this.totalFrames - 1) { + throw new Error(`[AnimatedSprite]: Invalid frame index value ${value}, expected to be between 0 and totalFrames ${this.totalFrames}.`); + } + const previousFrame = this.currentFrame; + this._currentTime = value; + if (previousFrame !== this.currentFrame) { + this._updateTexture(); + } + } + /** + * Indicates if the AnimatedSprite is currently playing. + * This is a read-only property that reflects the current playback state. + * @example + * ```ts + * // Check if animation is playing + * console.log('Playing:', sprite.playing); // true + * + * // Use with play control + * if (!sprite.playing) { + * sprite.play(); + * } + * ``` + * @readonly + * @returns {boolean} True if the animation is currently playing + * @see {@link AnimatedSprite#play} For starting playback + * @see {@link AnimatedSprite#stop} For stopping playback + * @see {@link AnimatedSprite#loop} For controlling looping behavior + */ + get playing() { + return this._playing; + } + /** + * Controls whether the animation automatically updates using the shared ticker. + * When enabled, the animation will update on each frame. When disabled, you must + * manually call update() to advance the animation. + * @example + * ```ts + * // Create sprite with auto-update disabled + * const sprite = new AnimatedSprite({ + * textures: [], + * autoUpdate: false + * }); + * + * // Manual update with app ticker + * app.ticker.add((ticker) => { + * sprite.update(ticker); + * }); + * + * // Enable auto-update later + * sprite.autoUpdate = true; + * ``` + * @default true + * @see {@link AnimatedSprite#update} For manual animation updates + * @see {@link Ticker} For the timing system + */ + get autoUpdate() { + return this._autoUpdate; + } + set autoUpdate(value) { + if (value !== this._autoUpdate) { + this._autoUpdate = value; + if (!this._autoUpdate && this._isConnectedToTicker) { + Ticker.shared.remove(this.update, this); + this._isConnectedToTicker = false; + } else if (this._autoUpdate && !this._isConnectedToTicker && this._playing) { + Ticker.shared.add(this.update, this); + this._isConnectedToTicker = true; + } + } + } + } + + "use strict"; + class Transform { + /** + * @param options - Options for the transform. + * @param options.matrix - The matrix to use. + * @param options.observer - The observer to use. + */ + constructor({ matrix, observer } = {}) { + this.dirty = true; + this._matrix = matrix != null ? matrix : new Matrix(); + this.observer = observer; + this.position = new ObservablePoint(this, 0, 0); + this.scale = new ObservablePoint(this, 1, 1); + this.pivot = new ObservablePoint(this, 0, 0); + this.skew = new ObservablePoint(this, 0, 0); + this._rotation = 0; + this._cx = 1; + this._sx = 0; + this._cy = 0; + this._sy = 1; + } + /** + * The transformation matrix computed from the transform's properties. + * Combines position, scale, rotation, skew, and pivot into a single matrix. + * @example + * ```ts + * // Get current matrix + * const matrix = transform.matrix; + * console.log(matrix.toString()); + * ``` + * @readonly + * @see {@link Matrix} For matrix operations + * @see {@link Transform.setFromMatrix} For setting transform from matrix + */ + get matrix() { + const lt = this._matrix; + if (!this.dirty) return lt; + lt.a = this._cx * this.scale.x; + lt.b = this._sx * this.scale.x; + lt.c = this._cy * this.scale.y; + lt.d = this._sy * this.scale.y; + lt.tx = this.position.x - (this.pivot.x * lt.a + this.pivot.y * lt.c); + lt.ty = this.position.y - (this.pivot.x * lt.b + this.pivot.y * lt.d); + this.dirty = false; + return lt; + } + /** + * Called when a value changes. + * @param point + * @internal + */ + _onUpdate(point) { + var _a; + this.dirty = true; + if (point === this.skew) { + this.updateSkew(); + } + (_a = this.observer) == null ? void 0 : _a._onUpdate(this); + } + /** Called when the skew or the rotation changes. */ + updateSkew() { + this._cx = Math.cos(this._rotation + this.skew.y); + this._sx = Math.sin(this._rotation + this.skew.y); + this._cy = -Math.sin(this._rotation - this.skew.x); + this._sy = Math.cos(this._rotation - this.skew.x); + this.dirty = true; + } + toString() { + return `[pixi.js/math:Transform position=(${this.position.x}, ${this.position.y}) rotation=${this.rotation} scale=(${this.scale.x}, ${this.scale.y}) skew=(${this.skew.x}, ${this.skew.y}) ]`; + } + /** + * Decomposes a matrix and sets the transforms properties based on it. + * @example + * ```ts + * // Basic matrix decomposition + * const transform = new Transform(); + * const matrix = new Matrix() + * .translate(100, 100) + * .rotate(Math.PI / 4) + * .scale(2, 2); + * + * transform.setFromMatrix(matrix); + * console.log(transform.position.x); // 100 + * console.log(transform.rotation); // ~0.785 (π/4) + * ``` + * @param matrix - The matrix to decompose + * @see {@link Matrix#decompose} For the decomposition logic + * @see {@link Transform#matrix} For getting the current matrix + */ + setFromMatrix(matrix) { + matrix.decompose(this); + this.dirty = true; + } + /** + * The rotation of the object in radians. + * @example + * ```ts + * // Basic rotation + * transform.rotation = Math.PI / 4; // 45 degrees + * + * // Rotate around pivot point + * transform.pivot.set(50, 50); + * transform.rotation = Math.PI; // 180 degrees around pivot + * + * // Animate rotation + * app.ticker.add(() => { + * transform.rotation += 0.1; + * }); + * ``` + * @see {@link Transform#pivot} For rotation point + * @see {@link Transform#skew} For skew effects + */ + get rotation() { + return this._rotation; + } + set rotation(value) { + if (this._rotation !== value) { + this._rotation = value; + this._onUpdate(this.skew); + } + } + } + + "use strict"; + let canUseNewCanvasBlendModesValue; + function createColoredCanvas(color) { + const canvas = DOMAdapter.get().createCanvas(6, 1); + const context = canvas.getContext("2d"); + context.fillStyle = color; + context.fillRect(0, 0, 6, 1); + return canvas; + } + function canUseNewCanvasBlendModes() { + if (canUseNewCanvasBlendModesValue !== void 0) { + return canUseNewCanvasBlendModesValue; + } + try { + const magenta = createColoredCanvas("#ff00ff"); + const yellow = createColoredCanvas("#ffff00"); + const canvas = DOMAdapter.get().createCanvas(6, 1); + const context = canvas.getContext("2d"); + context.globalCompositeOperation = "multiply"; + context.drawImage(magenta, 0, 0); + context.drawImage(yellow, 2, 0); + const imageData = context.getImageData(2, 0, 1, 1); + if (!imageData) { + canUseNewCanvasBlendModesValue = false; + } else { + const data = imageData.data; + canUseNewCanvasBlendModesValue = data[0] === 255 && data[1] === 0 && data[2] === 0; + } + } catch (_error) { + canUseNewCanvasBlendModesValue = false; + } + return canUseNewCanvasBlendModesValue; + } + + "use strict"; + const canvasUtils = { + canvas: null, + convertTintToImage: false, + cacheStepsPerColorChannel: 8, + canUseMultiply: canUseNewCanvasBlendModes(), + tintMethod: null, + _canvasSourceCache: /* @__PURE__ */ new WeakMap(), + _unpremultipliedCache: /* @__PURE__ */ new WeakMap(), + getCanvasSource: (texture) => { + var _a, _b; + const source = texture.source; + const resource = source == null ? void 0 : source.resource; + if (!resource) { + return null; + } + const isPMA = source.alphaMode === "premultiplied-alpha"; + const resourceWidth = (_a = source.resourceWidth) != null ? _a : source.pixelWidth; + const resourceHeight = (_b = source.resourceHeight) != null ? _b : source.pixelHeight; + const needsResize = resourceWidth !== source.pixelWidth || resourceHeight !== source.pixelHeight; + if (isPMA) { + if (resource instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && resource instanceof OffscreenCanvas) { + if (!needsResize) { + return resource; + } + } + const cached = canvasUtils._unpremultipliedCache.get(source); + if ((cached == null ? void 0 : cached.resourceId) === source._resourceId) { + return cached.canvas; + } + } + if (resource instanceof Uint8Array || resource instanceof Uint8ClampedArray || resource instanceof Int8Array || resource instanceof Uint16Array || resource instanceof Int16Array || resource instanceof Uint32Array || resource instanceof Int32Array || resource instanceof Float32Array || resource instanceof ArrayBuffer) { + const cached = canvasUtils._canvasSourceCache.get(source); + if ((cached == null ? void 0 : cached.resourceId) === source._resourceId) { + return cached.canvas; + } + const canvas = DOMAdapter.get().createCanvas(source.pixelWidth, source.pixelHeight); + const context = canvas.getContext("2d"); + const imageData = context.createImageData(source.pixelWidth, source.pixelHeight); + const data = imageData.data; + const bytes = resource instanceof ArrayBuffer ? new Uint8Array(resource) : new Uint8Array(resource.buffer, resource.byteOffset, resource.byteLength); + if (source.format === "bgra8unorm") { + for (let i = 0; i < data.length && i + 3 < bytes.length; i += 4) { + data[i] = bytes[i + 2]; + data[i + 1] = bytes[i + 1]; + data[i + 2] = bytes[i]; + data[i + 3] = bytes[i + 3]; + } + } else { + data.set(bytes.subarray(0, data.length)); + } + context.putImageData(imageData, 0, 0); + canvasUtils._canvasSourceCache.set(source, { canvas, resourceId: source._resourceId }); + return canvas; + } + if (isPMA) { + const canvas = DOMAdapter.get().createCanvas(source.pixelWidth, source.pixelHeight); + const context = canvas.getContext("2d", { willReadFrequently: true }); + canvas.width = source.pixelWidth; + canvas.height = source.pixelHeight; + context.drawImage(resource, 0, 0); + const imageData = context.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + for (let i = 0; i < data.length; i += 4) { + const a = data[i + 3]; + if (a > 0) { + const alphaInv = 255 / a; + data[i] = Math.min(255, data[i] * alphaInv + 0.5); + data[i + 1] = Math.min(255, data[i + 1] * alphaInv + 0.5); + data[i + 2] = Math.min(255, data[i + 2] * alphaInv + 0.5); + } + } + context.putImageData(imageData, 0, 0); + canvasUtils._unpremultipliedCache.set(source, { canvas, resourceId: source._resourceId }); + return canvas; + } + if (needsResize) { + const cached = canvasUtils._canvasSourceCache.get(source); + if ((cached == null ? void 0 : cached.resourceId) === source._resourceId) { + return cached.canvas; + } + const canvas = DOMAdapter.get().createCanvas(source.pixelWidth, source.pixelHeight); + const context = canvas.getContext("2d"); + canvas.width = source.pixelWidth; + canvas.height = source.pixelHeight; + context.drawImage(resource, 0, 0); + canvasUtils._canvasSourceCache.set(source, { canvas, resourceId: source._resourceId }); + return canvas; + } + return resource; + }, + getTintedCanvas: (sprite, color) => { + const texture = sprite.texture; + const stringColor = Color.shared.setValue(color).toHex(); + const cache = texture.tintCache || (texture.tintCache = {}); + const cachedCanvas = cache[stringColor]; + const resourceId = texture.source._resourceId; + if ((cachedCanvas == null ? void 0 : cachedCanvas.tintId) === resourceId) { + return cachedCanvas; + } + const canvas = cachedCanvas && "getContext" in cachedCanvas ? cachedCanvas : DOMAdapter.get().createCanvas(); + canvasUtils.tintMethod(texture, color, canvas); + canvas.tintId = resourceId; + if (canvasUtils.convertTintToImage && canvas.toDataURL !== void 0) { + const tintImage = DOMAdapter.get().createImage(); + tintImage.src = canvas.toDataURL(); + tintImage.tintId = resourceId; + cache[stringColor] = tintImage; + } else { + cache[stringColor] = canvas; + } + return cache[stringColor]; + }, + getTintedPattern: (texture, color) => { + const stringColor = Color.shared.setValue(color).toHex(); + const cache = texture.patternCache || (texture.patternCache = {}); + const resourceId = texture.source._resourceId; + let pattern = cache[stringColor]; + if ((pattern == null ? void 0 : pattern.tintId) === resourceId) { + return pattern; + } + if (!canvasUtils.canvas) { + canvasUtils.canvas = DOMAdapter.get().createCanvas(); + } + canvasUtils.tintMethod(texture, color, canvasUtils.canvas); + const context = canvasUtils.canvas.getContext("2d"); + pattern = context.createPattern(canvasUtils.canvas, "repeat"); + pattern.tintId = resourceId; + cache[stringColor] = pattern; + return pattern; + }, + /** + * Applies a transform to a CanvasPattern. + * @param pattern - The pattern to apply the transform to. + * @param matrix - The matrix to apply. + * @param matrix.a + * @param matrix.b + * @param matrix.c + * @param matrix.d + * @param matrix.tx + * @param matrix.ty + * @param invert + */ + applyPatternTransform: (pattern, matrix, invert = true) => { + if (!matrix) return; + const patternAny = pattern; + if (!patternAny.setTransform) return; + const DOMMatrixCtor = globalThis.DOMMatrix; + if (!DOMMatrixCtor) return; + const domMatrix = new DOMMatrixCtor([matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty]); + patternAny.setTransform(invert ? domMatrix.inverse() : domMatrix); + }, + tintWithMultiply: (texture, color, canvas) => { + var _a, _b; + const context = canvas.getContext("2d"); + const crop = texture.frame.clone(); + const resolution = (_b = (_a = texture.source._resolution) != null ? _a : texture.source.resolution) != null ? _b : 1; + const rotate = texture.rotate; + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + const isVertical = groupD8.isVertical(rotate); + const outWidth = isVertical ? crop.height : crop.width; + const outHeight = isVertical ? crop.width : crop.height; + canvas.width = Math.ceil(outWidth); + canvas.height = Math.ceil(outHeight); + context.save(); + context.fillStyle = Color.shared.setValue(color).toHex(); + context.fillRect(0, 0, outWidth, outHeight); + context.globalCompositeOperation = "multiply"; + const source = canvasUtils.getCanvasSource(texture); + if (!source) { + context.restore(); + return; + } + if (rotate) { + canvasUtils._applyInverseRotation(context, rotate, crop.width, crop.height); + } + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.globalCompositeOperation = "destination-atop"; + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + tintWithOverlay: (texture, color, canvas) => { + var _a, _b; + const context = canvas.getContext("2d"); + const crop = texture.frame.clone(); + const resolution = (_b = (_a = texture.source._resolution) != null ? _a : texture.source.resolution) != null ? _b : 1; + const rotate = texture.rotate; + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + const isVertical = groupD8.isVertical(rotate); + const outWidth = isVertical ? crop.height : crop.width; + const outHeight = isVertical ? crop.width : crop.height; + canvas.width = Math.ceil(outWidth); + canvas.height = Math.ceil(outHeight); + context.save(); + context.globalCompositeOperation = "copy"; + context.fillStyle = Color.shared.setValue(color).toHex(); + context.fillRect(0, 0, outWidth, outHeight); + context.globalCompositeOperation = "destination-atop"; + const source = canvasUtils.getCanvasSource(texture); + if (!source) { + context.restore(); + return; + } + if (rotate) { + canvasUtils._applyInverseRotation(context, rotate, crop.width, crop.height); + } + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + tintWithPerPixel: (texture, color, canvas) => { + var _a, _b; + const context = canvas.getContext("2d"); + const crop = texture.frame.clone(); + const resolution = (_b = (_a = texture.source._resolution) != null ? _a : texture.source.resolution) != null ? _b : 1; + const rotate = texture.rotate; + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + const isVertical = groupD8.isVertical(rotate); + const outWidth = isVertical ? crop.height : crop.width; + const outHeight = isVertical ? crop.width : crop.height; + canvas.width = Math.ceil(outWidth); + canvas.height = Math.ceil(outHeight); + context.save(); + context.globalCompositeOperation = "copy"; + const source = canvasUtils.getCanvasSource(texture); + if (!source) { + context.restore(); + return; + } + if (rotate) { + canvasUtils._applyInverseRotation(context, rotate, crop.width, crop.height); + } + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + const r = color >> 16 & 255; + const g = color >> 8 & 255; + const b = color & 255; + const imageData = context.getImageData(0, 0, outWidth, outHeight); + const data = imageData.data; + for (let i = 0; i < data.length; i += 4) { + data[i] = data[i] * r / 255; + data[i + 1] = data[i + 1] * g / 255; + data[i + 2] = data[i + 2] * b / 255; + } + context.putImageData(imageData, 0, 0); + }, + /** + * Applies inverse rotation transform to context for texture packer rotation compensation. + * Supports all 16 groupD8 symmetries (rotations and reflections). + * @param context - Canvas 2D context + * @param rotate - The groupD8 rotation value + * @param srcWidth - Source crop width (before rotation) + * @param srcHeight - Source crop height (before rotation) + */ + _applyInverseRotation: (context, rotate, srcWidth, srcHeight) => { + const inv = groupD8.inv(rotate); + const a = groupD8.uX(inv); + const b = groupD8.uY(inv); + const c = groupD8.vX(inv); + const d = groupD8.vY(inv); + const tx = -Math.min(0, a * srcWidth, c * srcHeight, a * srcWidth + c * srcHeight); + const ty = -Math.min(0, b * srcWidth, d * srcHeight, b * srcWidth + d * srcHeight); + context.transform(a, b, c, d, tx, ty); + } + }; + canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; + + "use strict"; + const worldMatrix = new Matrix(); + const patternMatrix = new Matrix(); + const patternRect = [new Point(), new Point(), new Point(), new Point()]; + class CanvasTilingSpritePipe { + constructor(renderer) { + this._renderer = renderer; + } + validateRenderable(_renderable) { + return false; + } + addRenderable(tilingSprite, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(tilingSprite); + } + updateRenderable(_tilingSprite) { + } + execute(tilingSprite) { + var _a, _b, _c, _d, _e, _f; + const renderer = this._renderer; + const contextSystem = renderer.canvasContext; + const context = contextSystem.activeContext; + context.save(); + contextSystem.setBlendMode(tilingSprite.groupBlendMode); + const globalColor = (_b = (_a = renderer.globalUniforms.globalUniformData) == null ? void 0 : _a.worldColor) != null ? _b : 4294967295; + const groupColorAlpha = tilingSprite.groupColorAlpha; + const globalAlpha = (globalColor >>> 24 & 255) / 255; + const groupAlphaValue = (groupColorAlpha >>> 24 & 255) / 255; + const filterAlpha = (_d = (_c = renderer.filter) == null ? void 0 : _c.alphaMultiplier) != null ? _d : 1; + const alpha = globalAlpha * groupAlphaValue * filterAlpha; + if (alpha <= 0) { + context.restore(); + return; + } + context.globalAlpha = alpha; + const globalTint = globalColor & 16777215; + const groupTintBGR = groupColorAlpha & 16777215; + const tint = bgr2rgb(multiplyHexColors(groupTintBGR, globalTint)); + const texture = tilingSprite.texture; + const pattern = canvasUtils.getTintedPattern(texture, tint); + const width = tilingSprite.width; + const height = tilingSprite.height; + const transform = tilingSprite.groupTransform; + const resolution = (_f = (_e = texture.source._resolution) != null ? _e : texture.source.resolution) != null ? _f : 1; + patternMatrix.copyFrom(tilingSprite._tileTransform.matrix); + if (!tilingSprite.applyAnchorToTexture) { + patternMatrix.translate(-tilingSprite.anchor.x * width, -tilingSprite.anchor.y * height); + } + patternMatrix.scale(1 / resolution, 1 / resolution); + worldMatrix.identity(); + worldMatrix.prepend(patternMatrix); + worldMatrix.prepend(transform); + const roundPixels = renderer._roundPixels | tilingSprite._roundPixels; + contextSystem.setContextTransform(worldMatrix, roundPixels === 1); + context.fillStyle = pattern; + const lx = tilingSprite.anchor.x * -width; + const ly = tilingSprite.anchor.y * -height; + patternRect[0].set(lx, ly); + patternRect[1].set(lx + width, ly); + patternRect[2].set(lx + width, ly + height); + patternRect[3].set(lx, ly + height); + for (let i = 0; i < 4; i++) { + patternMatrix.applyInverse(patternRect[i], patternRect[i]); + } + context.beginPath(); + context.moveTo(patternRect[0].x, patternRect[0].y); + for (let i = 1; i < 4; i++) { + context.lineTo(patternRect[i].x, patternRect[i].y); + } + context.closePath(); + context.fill(); + context.restore(); + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + CanvasTilingSpritePipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "tilingSprite" + }; + + "use strict"; + var __defProp$E = Object.defineProperty; + var __defProps$i = Object.defineProperties; + var __getOwnPropDescs$i = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$G = Object.getOwnPropertySymbols; + var __hasOwnProp$G = Object.prototype.hasOwnProperty; + var __propIsEnum$G = Object.prototype.propertyIsEnumerable; + var __defNormalProp$E = (obj, key, value) => key in obj ? __defProp$E(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$E = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$G.call(b, prop)) + __defNormalProp$E(a, prop, b[prop]); + if (__getOwnPropSymbols$G) + for (var prop of __getOwnPropSymbols$G(b)) { + if (__propIsEnum$G.call(b, prop)) + __defNormalProp$E(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$i = (a, b) => __defProps$i(a, __getOwnPropDescs$i(b)); + const localUniformBit = { + name: "local-uniform-bit", + vertex: { + header: ( + /* wgsl */ + ` + + struct LocalUniforms { + uTransformMatrix:mat3x3, + uColor:vec4, + uRound:f32, + } + + @group(1) @binding(0) var localUniforms : LocalUniforms; + ` + ), + main: ( + /* wgsl */ + ` + vColor *= localUniforms.uColor; + modelMatrix *= localUniforms.uTransformMatrix; + ` + ), + end: ( + /* wgsl */ + ` + if(localUniforms.uRound == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + ` + ) + } + }; + const localUniformBitGroup2 = __spreadProps$i(__spreadValues$E({}, localUniformBit), { + vertex: __spreadProps$i(__spreadValues$E({}, localUniformBit.vertex), { + // replace the group! + header: localUniformBit.vertex.header.replace("group(1)", "group(2)") + }) + }); + const localUniformBitGl = { + name: "local-uniform-bit", + vertex: { + header: ( + /* glsl */ + ` + + uniform mat3 uTransformMatrix; + uniform vec4 uColor; + uniform float uRound; + ` + ), + main: ( + /* glsl */ + ` + vColor *= uColor; + modelMatrix = uTransformMatrix; + ` + ), + end: ( + /* glsl */ + ` + if(uRound == 1.) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + ` + ) + } + }; + + "use strict"; + const tilingBit = { + name: "tiling-bit", + vertex: { + header: ( + /* wgsl */ + ` + struct TilingUniforms { + uMapCoord:mat3x3, + uClampFrame:vec4, + uClampOffset:vec2, + uTextureTransform:mat3x3, + uSizeAnchor:vec4 + }; + + @group(2) @binding(0) var tilingUniforms: TilingUniforms; + @group(2) @binding(1) var uTexture: texture_2d; + @group(2) @binding(2) var uSampler: sampler; + ` + ), + main: ( + /* wgsl */ + ` + uv = (tilingUniforms.uTextureTransform * vec3(uv, 1.0)).xy; + + position = (position - tilingUniforms.uSizeAnchor.zw) * tilingUniforms.uSizeAnchor.xy; + ` + ) + }, + fragment: { + header: ( + /* wgsl */ + ` + struct TilingUniforms { + uMapCoord:mat3x3, + uClampFrame:vec4, + uClampOffset:vec2, + uTextureTransform:mat3x3, + uSizeAnchor:vec4 + }; + + @group(2) @binding(0) var tilingUniforms: TilingUniforms; + @group(2) @binding(1) var uTexture: texture_2d; + @group(2) @binding(2) var uSampler: sampler; + ` + ), + main: ( + /* wgsl */ + ` + + var coord = vUV + ceil(tilingUniforms.uClampOffset - vUV); + coord = (tilingUniforms.uMapCoord * vec3(coord, 1.0)).xy; + var unclamped = coord; + coord = clamp(coord, tilingUniforms.uClampFrame.xy, tilingUniforms.uClampFrame.zw); + + var bias = 0.; + + if(unclamped.x == coord.x && unclamped.y == coord.y) + { + bias = -32.; + } + + outColor = textureSampleBias(uTexture, uSampler, coord, bias); + ` + ) + } + }; + const tilingBitGl = { + name: "tiling-bit", + vertex: { + header: ( + /* glsl */ + ` + uniform mat3 uTextureTransform; + uniform vec4 uSizeAnchor; + + ` + ), + main: ( + /* glsl */ + ` + uv = (uTextureTransform * vec3(aUV, 1.0)).xy; + + position = (position - uSizeAnchor.zw) * uSizeAnchor.xy; + ` + ) + }, + fragment: { + header: ( + /* glsl */ + ` + uniform sampler2D uTexture; + uniform mat3 uMapCoord; + uniform vec4 uClampFrame; + uniform vec2 uClampOffset; + ` + ), + main: ( + /* glsl */ + ` + + vec2 coord = vUV + ceil(uClampOffset - vUV); + coord = (uMapCoord * vec3(coord, 1.0)).xy; + vec2 unclamped = coord; + coord = clamp(coord, uClampFrame.xy, uClampFrame.zw); + + outColor = texture(uTexture, coord, unclamped == coord ? 0.0 : -32.0);// lod-bias very negative to force lod 0 + + ` + ) + } + }; + + "use strict"; + let gpuProgram$1; + let glProgram$1; + class TilingSpriteShader extends Shader { + constructor() { + gpuProgram$1 != null ? gpuProgram$1 : gpuProgram$1 = compileHighShaderGpuProgram({ + name: "tiling-sprite-shader", + bits: [ + localUniformBit, + tilingBit, + roundPixelsBit + ] + }); + glProgram$1 != null ? glProgram$1 : glProgram$1 = compileHighShaderGlProgram({ + name: "tiling-sprite-shader", + bits: [ + localUniformBitGl, + tilingBitGl, + roundPixelsBitGl + ] + }); + const tilingUniforms = new UniformGroup({ + uMapCoord: { value: new Matrix(), type: "mat3x3" }, + uClampFrame: { value: new Float32Array([0, 0, 1, 1]), type: "vec4" }, + uClampOffset: { value: new Float32Array([0, 0]), type: "vec2" }, + uTextureTransform: { value: new Matrix(), type: "mat3x3" }, + uSizeAnchor: { value: new Float32Array([100, 100, 0.5, 0.5]), type: "vec4" } + }); + super({ + glProgram: glProgram$1, + gpuProgram: gpuProgram$1, + resources: { + localUniforms: new UniformGroup({ + uTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4" }, + uRound: { value: 0, type: "f32" } + }), + tilingUniforms, + uTexture: Texture.EMPTY.source, + uSampler: Texture.EMPTY.source.style + } + }); + } + updateUniforms(width, height, matrix, anchorX, anchorY, texture) { + const tilingUniforms = this.resources.tilingUniforms; + const textureWidth = texture.width; + const textureHeight = texture.height; + const textureMatrix = texture.textureMatrix; + const uTextureTransform = tilingUniforms.uniforms.uTextureTransform; + uTextureTransform.set( + matrix.a * textureWidth / width, + matrix.b * textureWidth / height, + matrix.c * textureHeight / width, + matrix.d * textureHeight / height, + matrix.tx / width, + matrix.ty / height + ); + uTextureTransform.invert(); + tilingUniforms.uniforms.uMapCoord = textureMatrix.mapCoord; + tilingUniforms.uniforms.uClampFrame = textureMatrix.uClampFrame; + tilingUniforms.uniforms.uClampOffset = textureMatrix.uClampOffset; + tilingUniforms.uniforms.uTextureTransform = uTextureTransform; + tilingUniforms.uniforms.uSizeAnchor[0] = width; + tilingUniforms.uniforms.uSizeAnchor[1] = height; + tilingUniforms.uniforms.uSizeAnchor[2] = anchorX; + tilingUniforms.uniforms.uSizeAnchor[3] = anchorY; + if (texture) { + this.resources.uTexture = texture.source; + this.resources.uSampler = texture.source.style; + } + } + } + + "use strict"; + class QuadGeometry extends MeshGeometry { + constructor() { + super({ + positions: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), + uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), + indices: new Uint32Array([0, 1, 2, 0, 2, 3]) + }); + } + } + + "use strict"; + function setPositions(tilingSprite, positions) { + const anchorX = tilingSprite.anchor.x; + const anchorY = tilingSprite.anchor.y; + positions[0] = -anchorX * tilingSprite.width; + positions[1] = -anchorY * tilingSprite.height; + positions[2] = (1 - anchorX) * tilingSprite.width; + positions[3] = -anchorY * tilingSprite.height; + positions[4] = (1 - anchorX) * tilingSprite.width; + positions[5] = (1 - anchorY) * tilingSprite.height; + positions[6] = -anchorX * tilingSprite.width; + positions[7] = (1 - anchorY) * tilingSprite.height; + } + + "use strict"; + function applyMatrix(array, stride, offset, matrix) { + let index = 0; + const size = array.length / (stride || 2); + const a = matrix.a; + const b = matrix.b; + const c = matrix.c; + const d = matrix.d; + const tx = matrix.tx; + const ty = matrix.ty; + offset *= stride; + while (index < size) { + const x = array[offset]; + const y = array[offset + 1]; + array[offset] = a * x + c * y + tx; + array[offset + 1] = b * x + d * y + ty; + offset += stride; + index++; + } + } + + "use strict"; + function setUvs(tilingSprite, uvs) { + const texture = tilingSprite.texture; + const width = texture.frame.width; + const height = texture.frame.height; + let anchorX = 0; + let anchorY = 0; + if (tilingSprite.applyAnchorToTexture) { + anchorX = tilingSprite.anchor.x; + anchorY = tilingSprite.anchor.y; + } + uvs[0] = uvs[6] = -anchorX; + uvs[2] = uvs[4] = 1 - anchorX; + uvs[1] = uvs[3] = -anchorY; + uvs[5] = uvs[7] = 1 - anchorY; + const textureMatrix = Matrix.shared; + textureMatrix.copyFrom(tilingSprite._tileTransform.matrix); + textureMatrix.tx /= tilingSprite.width; + textureMatrix.ty /= tilingSprite.height; + textureMatrix.invert(); + textureMatrix.scale(tilingSprite.width / width, tilingSprite.height / height); + applyMatrix(uvs, 2, 0, textureMatrix); + } + + "use strict"; + const sharedQuad = new QuadGeometry(); + class TilingSpriteGpuData { + constructor() { + this.canBatch = true; + this.geometry = new MeshGeometry({ + indices: sharedQuad.indices.slice(), + positions: sharedQuad.positions.slice(), + uvs: sharedQuad.uvs.slice() + }); + } + destroy() { + var _a; + this.geometry.destroy(); + (_a = this.shader) == null ? void 0 : _a.destroy(); + } + } + class TilingSpritePipe { + constructor(renderer) { + this._state = State.default2d; + this._renderer = renderer; + this._managedTilingSprites = new GCManagedHash({ renderer, type: "renderable", name: "tilingSprite" }); + } + validateRenderable(renderable) { + const tilingSpriteData = this._getTilingSpriteData(renderable); + const couldBatch = tilingSpriteData.canBatch; + this._updateCanBatch(renderable); + const canBatch = tilingSpriteData.canBatch; + if (canBatch && canBatch === couldBatch) { + const { batchableMesh } = tilingSpriteData; + return !batchableMesh._batcher.checkAndUpdateTexture( + batchableMesh, + renderable.texture + ); + } + return couldBatch !== canBatch; + } + addRenderable(tilingSprite, instructionSet) { + const batcher = this._renderer.renderPipes.batch; + this._updateCanBatch(tilingSprite); + const tilingSpriteData = this._getTilingSpriteData(tilingSprite); + const { geometry, canBatch } = tilingSpriteData; + if (canBatch) { + tilingSpriteData.batchableMesh || (tilingSpriteData.batchableMesh = new BatchableMesh()); + const batchableMesh = tilingSpriteData.batchableMesh; + if (tilingSprite.didViewUpdate) { + this._updateBatchableMesh(tilingSprite); + batchableMesh.geometry = geometry; + batchableMesh.renderable = tilingSprite; + batchableMesh.transform = tilingSprite.groupTransform; + batchableMesh.setTexture(tilingSprite._texture); + } + batchableMesh.roundPixels = this._renderer._roundPixels | tilingSprite._roundPixels; + batcher.addToBatch(batchableMesh, instructionSet); + } else { + batcher.break(instructionSet); + tilingSpriteData.shader || (tilingSpriteData.shader = new TilingSpriteShader()); + this.updateRenderable(tilingSprite); + instructionSet.add(tilingSprite); + } + } + execute(tilingSprite) { + const renderer = this._renderer; + const { shader } = this._getTilingSpriteData(tilingSprite); + shader.groups[0] = renderer.globalUniforms.bindGroup; + const localUniforms = shader.resources.localUniforms.uniforms; + localUniforms.uTransformMatrix = tilingSprite.groupTransform; + localUniforms.uRound = renderer._roundPixels | tilingSprite._roundPixels; + color32BitToUniform( + tilingSprite.groupColorAlpha, + localUniforms.uColor, + 0 + ); + this._state.blendMode = getAdjustedBlendModeBlend(tilingSprite.groupBlendMode, tilingSprite.texture._source); + renderer.encoder.draw({ + geometry: sharedQuad, + shader, + state: this._state + }); + } + updateRenderable(tilingSprite) { + const tilingSpriteData = this._getTilingSpriteData(tilingSprite); + const { canBatch } = tilingSpriteData; + if (canBatch) { + const { batchableMesh } = tilingSpriteData; + if (tilingSprite.didViewUpdate) this._updateBatchableMesh(tilingSprite); + batchableMesh._batcher.updateElement(batchableMesh); + } else if (tilingSprite.didViewUpdate) { + const { shader } = tilingSpriteData; + shader.updateUniforms( + tilingSprite.width, + tilingSprite.height, + tilingSprite._tileTransform.matrix, + tilingSprite.anchor.x, + tilingSprite.anchor.y, + tilingSprite.texture + ); + } + } + _getTilingSpriteData(renderable) { + return renderable._gpuData[this._renderer.uid] || this._initTilingSpriteData(renderable); + } + _initTilingSpriteData(tilingSprite) { + const gpuData = new TilingSpriteGpuData(); + gpuData.renderable = tilingSprite; + tilingSprite._gpuData[this._renderer.uid] = gpuData; + this._managedTilingSprites.add(tilingSprite); + return gpuData; + } + _updateBatchableMesh(tilingSprite) { + const renderableData = this._getTilingSpriteData(tilingSprite); + const { geometry } = renderableData; + const style = tilingSprite.texture.source.style; + if (style.addressMode !== "repeat") { + style.addressMode = "repeat"; + style.update(); + } + setUvs(tilingSprite, geometry.uvs); + setPositions(tilingSprite, geometry.positions); + } + destroy() { + this._managedTilingSprites.destroy(); + this._renderer = null; + } + _updateCanBatch(tilingSprite) { + const renderableData = this._getTilingSpriteData(tilingSprite); + const texture = tilingSprite.texture; + let _nonPowOf2wrapping = true; + if (this._renderer.type === RendererType.WEBGL) { + _nonPowOf2wrapping = this._renderer.context.supports.nonPowOf2wrapping; + } + renderableData.canBatch = texture.textureMatrix.isSimple && (_nonPowOf2wrapping || texture.source.isPowerOfTwo); + return renderableData.canBatch; + } + } + /** @ignore */ + TilingSpritePipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "tilingSprite" + }; + + "use strict"; + extensions.add(CanvasTilingSpritePipe); + extensions.add(TilingSpritePipe); + + "use strict"; + var __defProp$D = Object.defineProperty; + var __getOwnPropSymbols$F = Object.getOwnPropertySymbols; + var __hasOwnProp$F = Object.prototype.hasOwnProperty; + var __propIsEnum$F = Object.prototype.propertyIsEnumerable; + var __defNormalProp$D = (obj, key, value) => key in obj ? __defProp$D(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$D = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$F.call(b, prop)) + __defNormalProp$D(a, prop, b[prop]); + if (__getOwnPropSymbols$F) + for (var prop of __getOwnPropSymbols$F(b)) { + if (__propIsEnum$F.call(b, prop)) + __defNormalProp$D(a, prop, b[prop]); + } + return a; + }; + var __objRest$c = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$F.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$F) + for (var prop of __getOwnPropSymbols$F(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$F.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const _TilingSprite = class _TilingSprite extends ViewContainer { + constructor(...args) { + let options = args[0] || {}; + if (options instanceof Texture) { + options = { texture: options }; + } + if (args.length > 1) { + deprecation(v8_0_0, "use new TilingSprite({ texture, width:100, height:100 }) instead"); + options.width = args[1]; + options.height = args[2]; + } + options = __spreadValues$D(__spreadValues$D({}, _TilingSprite.defaultOptions), options); + const _a = options != null ? options : {}, { + texture, + anchor, + tilePosition, + tileScale, + tileRotation, + width, + height, + applyAnchorToTexture, + roundPixels + } = _a, rest = __objRest$c(_a, [ + "texture", + "anchor", + "tilePosition", + "tileScale", + "tileRotation", + "width", + "height", + "applyAnchorToTexture", + "roundPixels" + ]); + super(__spreadValues$D({ + label: "TilingSprite" + }, rest)); + /** @internal */ + this.renderPipeId = "tilingSprite"; + /** @advanced */ + this.batched = true; + this.allowChildren = false; + this._anchor = new ObservablePoint( + { + _onUpdate: () => { + this.onViewUpdate(); + } + } + ); + this.applyAnchorToTexture = applyAnchorToTexture; + this.texture = texture; + this._width = width != null ? width : texture.width; + this._height = height != null ? height : texture.height; + this._tileTransform = new Transform({ + observer: { + _onUpdate: () => this.onViewUpdate() + } + }); + if (anchor) this.anchor = anchor; + this.tilePosition = tilePosition; + this.tileScale = tileScale; + this.tileRotation = tileRotation; + this.roundPixels = roundPixels != null ? roundPixels : false; + } + /** + * Creates a new tiling sprite based on a source texture or image path. + * This is a convenience method that automatically creates and manages textures. + * @example + * ```ts + * // Create a new tiling sprite from an image path + * const pattern = TilingSprite.from('pattern.png'); + * pattern.width = 300; // Set the width of the tiling area + * pattern.height = 200; // Set the height of the tiling area + * + * // Create from options + * const texture = Texture.from('pattern.png'); + * const pattern = TilingSprite.from(texture, { + * width: 300, + * height: 200, + * tileScale: { x: 0.5, y: 0.5 } + * }); + * ``` + * @param source - The source to create the sprite from. Can be a path to an image or a texture + * @param options - Additional options for the tiling sprite + * @returns A new tiling sprite based on the source + * @see {@link Texture.from} For texture creation details + * @see {@link Assets} For asset loading and management + */ + static from(source, options = {}) { + if (typeof source === "string") { + return new _TilingSprite(__spreadValues$D({ + texture: Cache.get(source) + }, options)); + } + return new _TilingSprite(__spreadValues$D({ + texture: source + }, options)); + } + /** + * @see {@link TilingSpriteOptions.applyAnchorToTexture} + * @deprecated since 8.0.0 + * @advanced + */ + get uvRespectAnchor() { + deprecation(v8_0_0, "uvRespectAnchor is deprecated, please use applyAnchorToTexture instead"); + return this.applyAnchorToTexture; + } + /** @advanced */ + set uvRespectAnchor(value) { + deprecation(v8_0_0, "uvRespectAnchor is deprecated, please use applyAnchorToTexture instead"); + this.applyAnchorToTexture = value; + } + /** + * Changes frame clamping in corresponding textureMatrix + * Change to -0.5 to add a pixel to the edge, recommended for transparent trimmed textures in atlas + * @default 0.5 + * @type {number} + * @advanced + */ + get clampMargin() { + return this._texture.textureMatrix.clampMargin; + } + /** @advanced */ + set clampMargin(value) { + this._texture.textureMatrix.clampMargin = value; + } + /** + * The anchor sets the origin point of the sprite. The default value is taken from the {@link Texture} + * and passed to the constructor. + * + * - The default is `(0,0)`, this means the sprite's origin is the top left. + * - Setting the anchor to `(0.5,0.5)` means the sprite's origin is centered. + * - Setting the anchor to `(1,1)` would mean the sprite's origin point will be the bottom right corner. + * + * If you pass only single parameter, it will set both x and y to the same value as shown in the example below. + * @example + * ```ts + * // Center the anchor point + * sprite.anchor = 0.5; // Sets both x and y to 0.5 + * sprite.position.set(400, 300); // Sprite will be centered at this position + * + * // Set specific x/y anchor points + * sprite.anchor = { + * x: 1, // Right edge + * y: 0 // Top edge + * }; + * + * // Using individual coordinates + * sprite.anchor.set(0.5, 1); // Center-bottom + * + * // For rotation around center + * sprite.anchor.set(0.5); + * sprite.rotation = Math.PI / 4; // 45 degrees around center + * + * // For scaling from center + * sprite.anchor.set(0.5); + * sprite.scale.set(2); // Scales from center point + * ``` + */ + get anchor() { + return this._anchor; + } + set anchor(value) { + typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value); + } + /** + * The offset of the tiling texture. + * Used to scroll or position the repeated pattern. + * @example + * ```ts + * // Offset the tiling pattern by 100 pixels in both x and y directions + * tilingSprite.tilePosition = { x: 100, y: 100 }; + * ``` + * @default {x: 0, y: 0} + */ + get tilePosition() { + return this._tileTransform.position; + } + set tilePosition(value) { + this._tileTransform.position.copyFrom(value); + } + /** + * Scale of the tiling texture. + * Affects the size of each repeated instance of the texture. + * @example + * ```ts + * // Scale the texture by 1.5 in both x and y directions + * tilingSprite.tileScale = { x: 1.5, y: 1.5 }; + * ``` + * @default {x: 1, y: 1} + */ + get tileScale() { + return this._tileTransform.scale; + } + set tileScale(value) { + typeof value === "number" ? this._tileTransform.scale.set(value) : this._tileTransform.scale.copyFrom(value); + } + set tileRotation(value) { + this._tileTransform.rotation = value; + } + /** + * Rotation of the tiling texture in radians. + * This controls the rotation applied to the texture before tiling. + * @example + * ```ts + * // Rotate the texture by 45 degrees (in radians) + * tilingSprite.tileRotation = Math.PI / 4; // 45 degrees + * ``` + * @default 0 + */ + get tileRotation() { + return this._tileTransform.rotation; + } + /** + * The transform object that controls the tiling texture's position, scale, and rotation. + * This transform is independent of the sprite's own transform properties. + * @example + * ```ts + * // Access transform properties directly + * sprite.tileTransform.position.set(100, 50); + * sprite.tileTransform.scale.set(2); + * sprite.tileTransform.rotation = Math.PI / 4; + * + * // Create smooth scrolling animation + * app.ticker.add(() => { + * sprite.tileTransform.position.x += 1; + * sprite.tileTransform.rotation += 0.01; + * }); + * + * // Reset transform + * sprite.tileTransform.position.set(0); + * sprite.tileTransform.scale.set(1); + * sprite.tileTransform.rotation = 0; + * ``` + * @returns {Transform} The transform object for the tiling texture + * @see {@link Transform} For transform operations + * @see {@link TilingSprite#tilePosition} For position control + * @see {@link TilingSprite#tileScale} For scale control + * @see {@link TilingSprite#tileRotation} For rotation control + * @advanced + */ + get tileTransform() { + return this._tileTransform; + } + set texture(value) { + value || (value = Texture.EMPTY); + const currentTexture = this._texture; + if (currentTexture === value) return; + if (currentTexture && currentTexture.dynamic) currentTexture.off("update", this.onViewUpdate, this); + if (value.dynamic) value.on("update", this.onViewUpdate, this); + this._texture = value; + this.onViewUpdate(); + } + /** + * The texture to use for tiling. + * This is the image that will be repeated across the sprite. + * @example + * ```ts + * // Use a texture from the asset cache + * tilingSprite.texture = Texture.from('assets/pattern.png'); + * ``` + * @default Texture.WHITE + */ + get texture() { + return this._texture; + } + /** + * The width of the tiling area. This defines how wide the area is that the texture will be tiled across. + * @example + * ```ts + * // Create a tiling sprite + * const sprite = new TilingSprite({ + * texture: Texture.from('pattern.png'), + * width: 500, + * height: 300 + * }); + * + * // Adjust width dynamically + * sprite.width = 800; // Expands tiling area + * + * // Update on resize + * window.addEventListener('resize', () => { + * sprite.width = app.screen.width; + * }); + * ``` + * @see {@link TilingSprite#setSize} For setting both width and height efficiently + * @see {@link TilingSprite#height} For setting height + */ + set width(value) { + this._width = value; + this.onViewUpdate(); + } + get width() { + return this._width; + } + set height(value) { + this._height = value; + this.onViewUpdate(); + } + /** + * The height of the tiling area. This defines how tall the area is that the texture will be tiled across. + * @example + * ```ts + * // Create a tiling sprite + * const sprite = new TilingSprite({ + * texture: Texture.from('pattern.png'), + * width: 500, + * height: 300 + * }); + * + * // Adjust width dynamically + * sprite.height = 800; // Expands tiling area + * + * // Update on resize + * window.addEventListener('resize', () => { + * sprite.height = app.screen.height; + * }); + * ``` + * @see {@link TilingSprite#setSize} For setting both width and height efficiently + * @see {@link TilingSprite#width} For setting width + */ + get height() { + return this._height; + } + /** + * Sets the size of the TilingSprite to the specified width and height. + * This is faster than setting width and height separately as it only triggers one update. + * @example + * ```ts + * // Set specific dimensions + * sprite.setSize(300, 200); // Width: 300, Height: 200 + * + * // Set uniform size (square) + * sprite.setSize(400); // Width: 400, Height: 400 + * + * // Set size using object + * sprite.setSize({ + * width: 500, + * height: 300 + * }); + * ``` + * @param value - This can be either a number for uniform sizing or a Size object with width/height properties + * @param height - The height to set. Defaults to the value of `width` if not provided + * @see {@link TilingSprite#width} For setting width only + * @see {@link TilingSprite#height} For setting height only + */ + setSize(value, height) { + var _a; + if (typeof value === "object") { + height = (_a = value.height) != null ? _a : value.width; + value = value.width; + } + this._width = value; + this._height = height != null ? height : value; + this.onViewUpdate(); + } + /** + * Retrieves the size of the TilingSprite as a {@link Size} object. + * This method is more efficient than getting width and height separately as it only allocates one object. + * @example + * ```ts + * // Get basic size + * const size = sprite.getSize(); + * console.log(`Size: ${size.width}x${size.height}`); + * + * // Reuse existing size object + * const reuseSize = { width: 0, height: 0 }; + * sprite.getSize(reuseSize); + * ``` + * @param out - Optional object to store the size in, to avoid allocating a new object + * @returns The size of the TilingSprite + * @see {@link TilingSprite#width} For getting just the width + * @see {@link TilingSprite#height} For getting just the height + * @see {@link TilingSprite#setSize} For setting both width and height efficiently + */ + getSize(out) { + out || (out = {}); + out.width = this._width; + out.height = this._height; + return out; + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const anchor = this._anchor; + const width = this._width; + const height = this._height; + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + /** + * Checks if the object contains the given point in local coordinates. + * Takes into account the anchor offset when determining boundaries. + * @example + * ```ts + * // Create a tiling sprite + * const sprite = new TilingSprite({ + * texture: Texture.from('pattern.png'), + * width: 200, + * height: 100, + * anchor: 0.5 // Center anchor + * }); + * + * // Basic point check + * const contains = sprite.containsPoint({ x: 50, y: 25 }); + * console.log('Point is inside:', contains); + * + * // Check with different anchors + * sprite.anchor.set(0); // Top-left anchor + * console.log('Contains point:', sprite.containsPoint({ x: 150, y: 75 })); + * ``` + * @param point - The point to check in local coordinates + * @returns True if the point is within the sprite's bounds + * @see {@link TilingSprite#toLocal} For converting global coordinates to local + * @see {@link TilingSprite#anchor} For understanding boundary calculations + */ + containsPoint(point) { + const width = this._width; + const height = this._height; + const x1 = -width * this._anchor._x; + let y1 = 0; + if (point.x >= x1 && point.x <= x1 + width) { + y1 = -height * this._anchor._y; + if (point.y >= y1 && point.y <= y1 + height) return true; + } + return false; + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * tilingSprite.destroy(); + * tilingSprite.destroy(true); + * tilingSprite.destroy({ texture: true, textureSource: true }); + */ + destroy(options = false) { + super.destroy(options); + this._anchor = null; + this._tileTransform = null; + this._bounds = null; + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + this._texture.destroy(destroyTextureSource); + } + this._texture = null; + } + }; + /** + * Default options used when creating a TilingSprite instance. + * These values are used as fallbacks when specific options are not provided. + * @example + * ```ts + * // Override default options globally + * TilingSprite.defaultOptions.texture = Texture.from('defaultPattern.png'); + * TilingSprite.defaultOptions.tileScale = { x: 2, y: 2 }; + * + * // Create sprite using default options + * const sprite = new TilingSprite(); + * // Will use defaultPattern.png and scale 2x + * ``` + * @type {TilingSpriteOptions} + * @see {@link TilingSpriteOptions} For all available options + * @see {@link TilingSprite.from} For creating sprites with custom options + * @see {@link Texture.EMPTY} For the default empty texture + */ + _TilingSprite.defaultOptions = { + /** The texture to use for the sprite. */ + texture: Texture.EMPTY, + /** The anchor point of the sprite */ + anchor: { x: 0, y: 0 }, + /** The offset of the image that is being tiled. */ + tilePosition: { x: 0, y: 0 }, + /** Scaling of the image that is being tiled. */ + tileScale: { x: 1, y: 1 }, + /** The rotation of the image that is being tiled. */ + tileRotation: 0, + /** + * Flags whether the tiling pattern should originate from the origin instead of the top-left corner in + * local space. + * + * This will make the texture coordinates assigned to each vertex dependent on the value of the anchor. Without + * this, the top-left corner always gets the (0, 0) texture coordinate. + * @default false + */ + applyAnchorToTexture: false + }; + let TilingSprite = _TilingSprite; + + "use strict"; + var __defProp$C = Object.defineProperty; + var __getOwnPropSymbols$E = Object.getOwnPropertySymbols; + var __hasOwnProp$E = Object.prototype.hasOwnProperty; + var __propIsEnum$E = Object.prototype.propertyIsEnumerable; + var __defNormalProp$C = (obj, key, value) => key in obj ? __defProp$C(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$C = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$E.call(b, prop)) + __defNormalProp$C(a, prop, b[prop]); + if (__getOwnPropSymbols$E) + for (var prop of __getOwnPropSymbols$E(b)) { + if (__propIsEnum$E.call(b, prop)) + __defNormalProp$C(a, prop, b[prop]); + } + return a; + }; + var __objRest$b = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$E.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$E) + for (var prop of __getOwnPropSymbols$E(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$E.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class AbstractText extends ViewContainer { + constructor(options, styleClass) { + const _a = options, { text, resolution, style, anchor, width, height, roundPixels } = _a, rest = __objRest$b(_a, ["text", "resolution", "style", "anchor", "width", "height", "roundPixels"]); + super(__spreadValues$C({}, rest)); + /** @internal */ + this.batched = true; + /** @internal */ + this._resolution = null; + /** @internal */ + this._autoResolution = true; + /** @internal */ + this._didTextUpdate = true; + this._styleClass = styleClass; + this.text = text != null ? text : ""; + this.style = style; + this.resolution = resolution != null ? resolution : null; + this.allowChildren = false; + this._anchor = new ObservablePoint( + { + _onUpdate: () => { + this.onViewUpdate(); + } + } + ); + if (anchor) this.anchor = anchor; + this.roundPixels = roundPixels != null ? roundPixels : false; + if (width !== void 0) this.width = width; + if (height !== void 0) this.height = height; + } + /** + * The anchor point of the text that controls the origin point for positioning and rotation. + * Can be a number (same value for x/y) or a PointData object. + * - (0,0) is top-left + * - (0.5,0.5) is center + * - (1,1) is bottom-right + * ```ts + * // Set anchor to center + * const text = new Text({ + * text: 'Hello Pixi!', + * anchor: 0.5 // Same as { x: 0.5, y: 0.5 } + * }); + * // Set anchor to top-left + * const text2 = new Text({ + * text: 'Hello Pixi!', + * anchor: { x: 0, y: 0 } // Top-left corner + * }); + * // Set anchor to bottom-right + * const text3 = new Text({ + * text: 'Hello Pixi!', + * anchor: { x: 1, y: 1 } // Bottom-right corner + * }); + * ``` + * @default { x: 0, y: 0 } + */ + get anchor() { + return this._anchor; + } + set anchor(value) { + typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value); + } + /** + * The text content to display. Use '\n' for line breaks. + * Accepts strings, numbers, or objects with toString() method. + * @example + * ```ts + * const text = new Text({ + * text: 'Hello Pixi!', + * }); + * const multilineText = new Text({ + * text: 'Line 1\nLine 2\nLine 3', + * }); + * const numberText = new Text({ + * text: 12345, // Will be converted to '12345' + * }); + * const objectText = new Text({ + * text: { toString: () => 'Object Text' }, // Custom toString + * }); + * + * // Update text dynamically + * text.text = 'Updated Text'; // Re-renders with new text + * text.text = 67890; // Updates to '67890' + * text.text = { toString: () => 'Dynamic Text' }; // Uses custom toString method + * // Clear text + * text.text = ''; // Clears the text + * ``` + * @default '' + */ + set text(value) { + value = value.toString(); + if (this._text === value) return; + this._text = value; + this.onViewUpdate(); + } + get text() { + return this._text; + } + /** + * The resolution/device pixel ratio for rendering. + * Higher values result in sharper text at the cost of performance. + * Set to null for auto-resolution based on device. + * @example + * ```ts + * const text = new Text({ + * text: 'Hello Pixi!', + * resolution: 2 // High DPI for sharper text + * }); + * const autoResText = new Text({ + * text: 'Auto Resolution', + * resolution: null // Use device's pixel ratio + * }); + * ``` + * @default null + */ + set resolution(value) { + this._autoResolution = value === null; + this._resolution = value; + this.onViewUpdate(); + } + get resolution() { + return this._resolution; + } + get style() { + return this._style; + } + /** + * The style configuration for the text. + * Can be a TextStyle instance or a configuration object. + * Supports canvas text styles, HTML text styles, and bitmap text styles. + * @example + * ```ts + * const text = new Text({ + * text: 'Styled Text', + * style: { + * fontSize: 24, + * fill: 0xff1010, // Red color + * fontFamily: 'Arial', + * align: 'center', // Center alignment + * stroke: { color: '#4a1850', width: 5 }, // Purple stroke + * dropShadow: { + * color: '#000000', // Black shadow + * blur: 4, // Shadow blur + * distance: 6 // Shadow distance + * } + * } + * }); + * const htmlText = new HTMLText({ + * text: 'HTML Styled Text', + * style: { + * fontSize: '20px', + * fill: 'blue', + * fontFamily: 'Verdana', + * } + * }); + * const bitmapText = new BitmapText({ + * text: 'Bitmap Styled Text', + * style: { + * fontName: 'Arial', + * fontSize: 32, + * } + * }) + * + * // Update style dynamically + * text.style = { + * fontSize: 30, // Change font size + * fill: 0x00ff00, // Change color to green + * align: 'right', // Change alignment to right + * stroke: { color: '#000000', width: 2 }, // Add black stroke + * } + */ + set style(style) { + var _a; + style || (style = {}); + (_a = this._style) == null ? void 0 : _a.off("update", this.onViewUpdate, this); + if (style instanceof this._styleClass) { + this._style = style; + } else { + this._style = new this._styleClass(style); + } + this._style.on("update", this.onViewUpdate, this); + this.onViewUpdate(); + } + /** + * The width of the sprite, setting this will actually modify the scale to achieve the value set. + * @example + * ```ts + * // Set width directly + * texture.width = 200; + * console.log(texture.scale.x); // Scale adjusted to match width + * + * // For better performance when setting both width and height + * texture.setSize(300, 400); // Avoids recalculating bounds twice + * ``` + */ + get width() { + return Math.abs(this.scale.x) * this.bounds.width; + } + set width(value) { + this._setWidth(value, this.bounds.width); + } + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set. + * @example + * ```ts + * // Set height directly + * texture.height = 200; + * console.log(texture.scale.y); // Scale adjusted to match height + * + * // For better performance when setting both width and height + * texture.setSize(300, 400); // Avoids recalculating bounds twice + * ``` + */ + get height() { + return Math.abs(this.scale.y) * this.bounds.height; + } + set height(value) { + this._setHeight(value, this.bounds.height); + } + /** + * Retrieves the size of the Text as a [Size]{@link Size} object based on the texture dimensions and scale. + * This is faster than getting width and height separately as it only calculates the bounds once. + * @example + * ```ts + * // Basic size retrieval + * const text = new Text({ + * text: 'Hello Pixi!', + * style: { fontSize: 24 } + * }); + * const size = text.getSize(); + * console.log(`Size: ${size.width}x${size.height}`); + * + * // Reuse existing size object + * const reuseSize = { width: 0, height: 0 }; + * text.getSize(reuseSize); + * ``` + * @param out - Optional object to store the size in, to avoid allocating a new object + * @returns The size of the Sprite + * @see {@link Text#width} For getting just the width + * @see {@link Text#height} For getting just the height + * @see {@link Text#setSize} For setting both width and height + */ + getSize(out) { + out || (out = {}); + out.width = Math.abs(this.scale.x) * this.bounds.width; + out.height = Math.abs(this.scale.y) * this.bounds.height; + return out; + } + /** + * Sets the size of the Text to the specified width and height. + * This is faster than setting width and height separately as it only recalculates bounds once. + * @example + * ```ts + * // Basic size setting + * const text = new Text({ + * text: 'Hello Pixi!', + * style: { fontSize: 24 } + * }); + * text.setSize(100, 200); // Width: 100, Height: 200 + * + * // Set uniform size + * text.setSize(100); // Sets both width and height to 100 + * + * // Set size with object + * text.setSize({ + * width: 200, + * height: 300 + * }); + * ``` + * @param value - This can be either a number or a {@link Size} object + * @param height - The height to set. Defaults to the value of `width` if not provided + * @see {@link Text#width} For setting width only + * @see {@link Text#height} For setting height only + */ + setSize(value, height) { + var _a; + if (typeof value === "object") { + height = (_a = value.height) != null ? _a : value.width; + value = value.width; + } else { + height != null ? height : height = value; + } + value !== void 0 && this._setWidth(value, this.bounds.width); + height !== void 0 && this._setHeight(height, this.bounds.height); + } + /** + * Checks if the object contains the given point in local coordinates. + * Uses the text's bounds for hit testing. + * @example + * ```ts + * // Basic point check + * const localPoint = { x: 50, y: 25 }; + * const contains = text.containsPoint(localPoint); + * console.log('Point is inside:', contains); + * ``` + * @param point - The point to check in local coordinates + * @returns True if the point is within the text's bounds + * @see {@link Container#toLocal} For converting global coordinates to local + */ + containsPoint(point) { + const width = this.bounds.width; + const height = this.bounds.height; + const x1 = -width * this.anchor.x; + let y1 = 0; + if (point.x >= x1 && point.x <= x1 + width) { + y1 = -height * this.anchor.y; + if (point.y >= y1 && point.y <= y1 + height) return true; + } + return false; + } + /** @internal */ + onViewUpdate() { + if (!this.didViewUpdate) this._didTextUpdate = true; + super.onViewUpdate(); + } + /** + * Destroys this text renderable and optionally its style texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * // Destroys the text and its style + * text.destroy({ style: true, texture: true, textureSource: true }); + * text.destroy(true); + * text.destroy() // Destroys the text, but not its style + */ + destroy(options = false) { + super.destroy(options); + this.owner = null; + this._bounds = null; + this._anchor = null; + if (typeof options === "boolean" ? options : options == null ? void 0 : options.style) { + this._style.destroy(options); + } + this._style = null; + this._text = null; + } + /** + * Returns a unique key for this instance. + * This key is used for caching. + * @returns {string} Unique key for the instance + */ + get styleKey() { + return `${this._text}:${this._style.styleKey}:${this._resolution}`; + } + } + function ensureTextOptions(args, name) { + var _a; + let options = (_a = args[0]) != null ? _a : {}; + if (typeof options === "string" || args[1]) { + deprecation(v8_0_0, `use new ${name}({ text: "hi!", style }) instead`); + options = { + text: options, + style: args[1] + }; + } + return options; + } + + "use strict"; + class CanvasPoolClass { + constructor(canvasOptions) { + this._canvasPool = /* @__PURE__ */ Object.create(null); + this.canvasOptions = canvasOptions || {}; + this.enableFullScreen = false; + } + /** + * Creates texture with params that were specified in pool constructor. + * @param pixelWidth - Width of texture in pixels. + * @param pixelHeight - Height of texture in pixels. + */ + _createCanvasAndContext(pixelWidth, pixelHeight) { + const canvas = DOMAdapter.get().createCanvas(); + canvas.width = pixelWidth; + canvas.height = pixelHeight; + const context = canvas.getContext("2d"); + return { canvas, context }; + } + /** + * Gets a Power-of-Two render texture or fullScreen texture + * @param minWidth - The minimum width of the render texture. + * @param minHeight - The minimum height of the render texture. + * @param resolution - The resolution of the render texture. + * @returns The new render texture. + */ + getOptimalCanvasAndContext(minWidth, minHeight, resolution = 1) { + minWidth = Math.ceil(minWidth * resolution - 1e-6); + minHeight = Math.ceil(minHeight * resolution - 1e-6); + minWidth = nextPow2(minWidth); + minHeight = nextPow2(minHeight); + const key = (minWidth << 17) + (minHeight << 1); + if (!this._canvasPool[key]) { + this._canvasPool[key] = []; + } + let canvasAndContext = this._canvasPool[key].pop(); + if (!canvasAndContext) { + canvasAndContext = this._createCanvasAndContext(minWidth, minHeight); + } + return canvasAndContext; + } + /** + * Place a render texture back into the pool. + * @param canvasAndContext + */ + returnCanvasAndContext(canvasAndContext) { + const canvas = canvasAndContext.canvas; + const { width, height } = canvas; + const key = (width << 17) + (height << 1); + canvasAndContext.context.resetTransform(); + canvasAndContext.context.clearRect(0, 0, width, height); + this._canvasPool[key].push(canvasAndContext); + } + clear() { + this._canvasPool = {}; + } + } + const CanvasPool = new CanvasPoolClass(); + GlobalResourceRegistry.register(CanvasPool); + + "use strict"; + let _internalCanvas = null; + let _internalContext = null; + function ensureInternalCanvas(width, height) { + if (!_internalCanvas) { + _internalCanvas = DOMAdapter.get().createCanvas(256, 128); + _internalContext = _internalCanvas.getContext("2d", { willReadFrequently: true }); + _internalContext.globalCompositeOperation = "copy"; + _internalContext.globalAlpha = 1; + } + if (_internalCanvas.width < width || _internalCanvas.height < height) { + _internalCanvas.width = nextPow2(width); + _internalCanvas.height = nextPow2(height); + } + } + function checkRow(data, width, y) { + for (let x = 0, index = 4 * y * width; x < width; ++x, index += 4) { + if (data[index + 3] !== 0) return false; + } + return true; + } + function checkColumn(data, width, x, top, bottom) { + const stride = 4 * width; + for (let y = top, index = top * stride + 4 * x; y <= bottom; ++y, index += stride) { + if (data[index + 3] !== 0) return false; + } + return true; + } + function getCanvasBoundingBox(...args) { + var _a, _b, _c; + let options = args[0]; + if (!options.canvas) { + options = { canvas: args[0], resolution: args[1] }; + } + const { canvas } = options; + const resolution = Math.min((_a = options.resolution) != null ? _a : 1, 1); + const width = (_b = options.width) != null ? _b : canvas.width; + const height = (_c = options.height) != null ? _c : canvas.height; + let output = options.output; + ensureInternalCanvas(width, height); + if (!_internalContext) { + throw new TypeError("Failed to get canvas 2D context"); + } + _internalContext.drawImage( + canvas, + 0, + 0, + width, + height, + 0, + 0, + width * resolution, + height * resolution + ); + const imageData = _internalContext.getImageData(0, 0, width, height); + const data = imageData.data; + let left = 0; + let top = 0; + let right = width - 1; + let bottom = height - 1; + while (top < height && checkRow(data, width, top)) ++top; + if (top === height) return Rectangle.EMPTY; + while (checkRow(data, width, bottom)) --bottom; + while (checkColumn(data, width, left, top, bottom)) ++left; + while (checkColumn(data, width, right, top, bottom)) --right; + ++right; + ++bottom; + _internalContext.globalCompositeOperation = "source-over"; + _internalContext.strokeRect(left, top, right - left, bottom - top); + _internalContext.globalCompositeOperation = "copy"; + output != null ? output : output = new Rectangle(); + output.set(left / resolution, top / resolution, (right - left) / resolution, (bottom - top) / resolution); + return output; + } + + /** + * tiny-lru + * + * @copyright 2026 Jason Mulligan + * @license BSD-3-Clause + * @version 11.4.7 + */ + /** + * A high-performance Least Recently Used (LRU) cache implementation with optional TTL support. + * Items are automatically evicted when the cache reaches its maximum size, + * removing the least recently used items first. All core operations (get, set, delete) are O(1). + * + * @class LRU + * @example + * // Create a cache with max 100 items + * const cache = new LRU(100); + * cache.set('key1', 'value1'); + * console.log(cache.get('key1')); // 'value1' + * + * @example + * // Create a cache with TTL + * const cache = new LRU(100, 5000); // 5 second TTL + * cache.set('key1', 'value1'); + * // After 5 seconds, key1 will be expired + */ + class LRU { + /** + * Creates a new LRU cache instance. + * Note: Constructor does not validate parameters. Use lru() factory function for parameter validation. + * + * @constructor + * @param {number} [max=0] - Maximum number of items to store. 0 means unlimited. + * @param {number} [ttl=0] - Time to live in milliseconds. 0 means no expiration. + * @param {boolean} [resetTtl=false] - Whether to reset TTL when accessing existing items via get(). + * @example + * const cache = new LRU(1000, 60000, true); // 1000 items, 1 minute TTL, reset on access + * @see {@link lru} For parameter validation + * @since 1.0.0 + */ + constructor (max = 0, ttl = 0, resetTtl = false) { + this.first = null; + this.items = Object.create(null); + this.last = null; + this.max = max; + this.resetTtl = resetTtl; + this.size = 0; + this.ttl = ttl; + } + + /** + * Removes all items from the cache. + * + * @method clear + * @memberof LRU + * @returns {LRU} The LRU instance for method chaining. + * @example + * cache.clear(); + * console.log(cache.size); // 0 + * @since 1.0.0 + */ + clear () { + this.first = null; + this.items = Object.create(null); + this.last = null; + this.size = 0; + + return this; + } + + /** + * Removes an item from the cache by key. + * + * @method delete + * @memberof LRU + * @param {string} key - The key of the item to delete. + * @returns {LRU} The LRU instance for method chaining. + * @example + * cache.set('key1', 'value1'); + * cache.delete('key1'); + * console.log(cache.has('key1')); // false + * @see {@link LRU#has} + * @see {@link LRU#clear} + * @since 1.0.0 + */ + delete (key) { + if (this.has(key)) { + const item = this.items[key]; + + delete this.items[key]; + this.size--; + + if (item.prev !== null) { + item.prev.next = item.next; + } + + if (item.next !== null) { + item.next.prev = item.prev; + } + + if (this.first === item) { + this.first = item.next; + } + + if (this.last === item) { + this.last = item.prev; + } + } + + return this; + } + + /** + * Returns an array of [key, value] pairs for the specified keys. + * Order follows LRU order (least to most recently used). + * + * @method entries + * @memberof LRU + * @param {string[]} [keys=this.keys()] - Array of keys to get entries for. Defaults to all keys. + * @returns {Array>} Array of [key, value] pairs in LRU order. + * @example + * cache.set('a', 1).set('b', 2); + * console.log(cache.entries()); // [['a', 1], ['b', 2]] + * console.log(cache.entries(['a'])); // [['a', 1]] + * @see {@link LRU#keys} + * @see {@link LRU#values} + * @since 11.1.0 + */ + entries (keys = this.keys()) { + const result = new Array(keys.length); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + result[i] = [key, this.get(key)]; + } + + return result; + } + + /** + * Removes the least recently used item from the cache. + * + * @method evict + * @memberof LRU + * @param {boolean} [bypass=false] - Whether to force eviction even when cache is empty. + * @returns {LRU} The LRU instance for method chaining. + * @example + * cache.set('old', 'value').set('new', 'value'); + * cache.evict(); // Removes 'old' item + * @see {@link LRU#setWithEvicted} + * @since 1.0.0 + */ + evict (bypass = false) { + if (bypass || this.size > 0) { + const item = this.first; + + delete this.items[item.key]; + + if (--this.size === 0) { + this.first = null; + this.last = null; + } else { + this.first = item.next; + this.first.prev = null; + } + } + + return this; + } + + /** + * Returns the expiration timestamp for a given key. + * + * @method expiresAt + * @memberof LRU + * @param {string} key - The key to check expiration for. + * @returns {number|undefined} The expiration timestamp in milliseconds, or undefined if key doesn't exist. + * @example + * const cache = new LRU(100, 5000); // 5 second TTL + * cache.set('key1', 'value1'); + * console.log(cache.expiresAt('key1')); // timestamp 5 seconds from now + * @see {@link LRU#get} + * @see {@link LRU#has} + * @since 1.0.0 + */ + expiresAt (key) { + let result; + + if (this.has(key)) { + result = this.items[key].expiry; + } + + return result; + } + + /** + * Retrieves a value from the cache by key. Updates the item's position to most recently used. + * + * @method get + * @memberof LRU + * @param {string} key - The key to retrieve. + * @returns {*} The value associated with the key, or undefined if not found or expired. + * @example + * cache.set('key1', 'value1'); + * console.log(cache.get('key1')); // 'value1' + * console.log(cache.get('nonexistent')); // undefined + * @see {@link LRU#set} + * @see {@link LRU#has} + * @since 1.0.0 + */ + get (key) { + const item = this.items[key]; + + if (item !== undefined) { + // Check TTL only if enabled to avoid unnecessary Date.now() calls + if (this.ttl > 0) { + if (item.expiry <= Date.now()) { + this.delete(key); + + return undefined; + } + } + + // Fast LRU update without full set() overhead + this.moveToEnd(item); + + return item.value; + } + + return undefined; + } + + /** + * Checks if a key exists in the cache. + * + * @method has + * @memberof LRU + * @param {string} key - The key to check for. + * @returns {boolean} True if the key exists, false otherwise. + * @example + * cache.set('key1', 'value1'); + * console.log(cache.has('key1')); // true + * console.log(cache.has('nonexistent')); // false + * @see {@link LRU#get} + * @see {@link LRU#delete} + * @since 9.0.0 + */ + has (key) { + return key in this.items; + } + + /** + * Efficiently moves an item to the end of the LRU list (most recently used position). + * This is an internal optimization method that avoids the overhead of the full set() operation + * when only LRU position needs to be updated. + * + * @method moveToEnd + * @memberof LRU + * @param {Object} item - The cache item with prev/next pointers to reposition. + * @private + * @since 11.3.5 + */ + moveToEnd (item) { + // If already at the end, nothing to do + if (this.last === item) { + return; + } + + // Remove item from current position in the list + if (item.prev !== null) { + item.prev.next = item.next; + } + + if (item.next !== null) { + item.next.prev = item.prev; + } + + // Update first pointer if this was the first item + if (this.first === item) { + this.first = item.next; + } + + // Add item to the end + item.prev = this.last; + item.next = null; + + if (this.last !== null) { + this.last.next = item; + } + + this.last = item; + + // Handle edge case: if this was the only item, it's also first + if (this.first === null) { + this.first = item; + } + } + + /** + * Returns an array of all keys in the cache, ordered from least to most recently used. + * + * @method keys + * @memberof LRU + * @returns {string[]} Array of keys in LRU order. + * @example + * cache.set('a', 1).set('b', 2); + * cache.get('a'); // Move 'a' to most recent + * console.log(cache.keys()); // ['b', 'a'] + * @see {@link LRU#values} + * @see {@link LRU#entries} + * @since 9.0.0 + */ + keys () { + const result = new Array(this.size); + let x = this.first; + let i = 0; + + while (x !== null) { + result[i++] = x.key; + x = x.next; + } + + return result; + } + + /** + * Sets a value in the cache and returns any evicted item. + * + * @method setWithEvicted + * @memberof LRU + * @param {string} key - The key to set. + * @param {*} value - The value to store. + * @param {boolean} [resetTtl=this.resetTtl] - Whether to reset the TTL for this operation. + * @returns {Object|null} The evicted item (if any) with shape {key, value, expiry, prev, next}, or null. + * @example + * const cache = new LRU(2); + * cache.set('a', 1).set('b', 2); + * const evicted = cache.setWithEvicted('c', 3); // evicted = {key: 'a', value: 1, ...} + * @see {@link LRU#set} + * @see {@link LRU#evict} + * @since 11.3.0 + */ + setWithEvicted (key, value, resetTtl = this.resetTtl) { + let evicted = null; + + if (this.has(key)) { + this.set(key, value, true, resetTtl); + } else { + if (this.max > 0 && this.size === this.max) { + evicted = {...this.first}; + this.evict(true); + } + + let item = this.items[key] = { + expiry: this.ttl > 0 ? Date.now() + this.ttl : this.ttl, + key: key, + prev: this.last, + next: null, + value + }; + + if (++this.size === 1) { + this.first = item; + } else { + this.last.next = item; + } + + this.last = item; + } + + return evicted; + } + + /** + * Sets a value in the cache. Updates the item's position to most recently used. + * + * @method set + * @memberof LRU + * @param {string} key - The key to set. + * @param {*} value - The value to store. + * @param {boolean} [bypass=false] - Internal parameter for setWithEvicted method. + * @param {boolean} [resetTtl=this.resetTtl] - Whether to reset the TTL for this operation. + * @returns {LRU} The LRU instance for method chaining. + * @example + * cache.set('key1', 'value1') + * .set('key2', 'value2') + * .set('key3', 'value3'); + * @see {@link LRU#get} + * @see {@link LRU#setWithEvicted} + * @since 1.0.0 + */ + set (key, value, bypass = false, resetTtl = this.resetTtl) { + let item = this.items[key]; + + if (bypass || item !== undefined) { + // Existing item: update value and position + item.value = value; + + if (bypass === false && resetTtl) { + item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl; + } + + // Always move to end, but the bypass parameter affects TTL reset behavior + this.moveToEnd(item); + } else { + // New item: check for eviction and create + if (this.max > 0 && this.size === this.max) { + this.evict(true); + } + + item = this.items[key] = { + expiry: this.ttl > 0 ? Date.now() + this.ttl : this.ttl, + key: key, + prev: this.last, + next: null, + value + }; + + if (++this.size === 1) { + this.first = item; + } else { + this.last.next = item; + } + + this.last = item; + } + + return this; + } + + /** + * Returns an array of all values in the cache for the specified keys. + * Order follows LRU order (least to most recently used). + * + * @method values + * @memberof LRU + * @param {string[]} [keys=this.keys()] - Array of keys to get values for. Defaults to all keys. + * @returns {Array<*>} Array of values corresponding to the keys in LRU order. + * @example + * cache.set('a', 1).set('b', 2); + * console.log(cache.values()); // [1, 2] + * console.log(cache.values(['a'])); // [1] + * @see {@link LRU#keys} + * @see {@link LRU#entries} + * @since 11.1.0 + */ + values (keys = this.keys()) { + const result = new Array(keys.length); + for (let i = 0; i < keys.length; i++) { + result[i] = this.get(keys[i]); + } + + return result; + } + } + + /** + * Factory function to create a new LRU cache instance with parameter validation. + * + * @function lru + * @param {number} [max=1000] - Maximum number of items to store. Must be >= 0. Use 0 for unlimited size. + * @param {number} [ttl=0] - Time to live in milliseconds. Must be >= 0. Use 0 for no expiration. + * @param {boolean} [resetTtl=false] - Whether to reset TTL when accessing existing items via get(). + * @returns {LRU} A new LRU cache instance. + * @throws {TypeError} When parameters are invalid (negative numbers or wrong types). + * @example + * // Create cache with factory function + * const cache = lru(100, 5000, true); + * cache.set('key', 'value'); + * + * @example + * // Error handling + * try { + * const cache = lru(-1); // Invalid max + * } catch (error) { + * console.error(error.message); // "Invalid max value" + * } + * @see {@link LRU} + * @since 1.0.0 + */ + function lru (max = 1000, ttl = 0, resetTtl = false) { + if (isNaN(max) || max < 0) { + throw new TypeError("Invalid max value"); + } + + if (isNaN(ttl) || ttl < 0) { + throw new TypeError("Invalid ttl value"); + } + + if (typeof resetTtl !== "boolean") { + throw new TypeError("Invalid resetTtl value"); + } + + return new LRU(max, ttl, resetTtl); + } + + "use strict"; + function hasTagStyles(style) { + return !!style.tagStyles && Object.keys(style.tagStyles).length > 0; + } + function hasTagMarkup(text) { + return text.includes("<"); + } + function createMergedStyle(baseStyle, overrides) { + return baseStyle.clone().assign(overrides); + } + function parseTaggedText(text, style) { + const runs = []; + const tagStyles = style.tagStyles; + if (!hasTagStyles(style) || !hasTagMarkup(text)) { + runs.push({ text, style }); + return runs; + } + const styleStack = [style]; + const tagStack = []; + let currentText = ""; + let i = 0; + while (i < text.length) { + const char = text[i]; + if (char === "<") { + const closeIndex = text.indexOf(">", i); + if (closeIndex === -1) { + currentText += char; + i++; + continue; + } + const tagContent = text.slice(i + 1, closeIndex); + if (tagContent.startsWith("/")) { + const closingTagName = tagContent.slice(1).trim(); + if (tagStack.length > 0 && tagStack[tagStack.length - 1] === closingTagName) { + if (currentText.length > 0) { + runs.push({ + text: currentText, + style: styleStack[styleStack.length - 1] + }); + currentText = ""; + } + styleStack.pop(); + tagStack.pop(); + i = closeIndex + 1; + continue; + } else { + currentText += text.slice(i, closeIndex + 1); + i = closeIndex + 1; + continue; + } + } else { + const tagName = tagContent.trim(); + if (tagStyles[tagName]) { + if (currentText.length > 0) { + runs.push({ + text: currentText, + style: styleStack[styleStack.length - 1] + }); + currentText = ""; + } + const currentStyle = styleStack[styleStack.length - 1]; + const mergedStyle = createMergedStyle(currentStyle, tagStyles[tagName]); + styleStack.push(mergedStyle); + tagStack.push(tagName); + i = closeIndex + 1; + continue; + } else { + currentText += text.slice(i, closeIndex + 1); + i = closeIndex + 1; + continue; + } + } + } else { + currentText += char; + i++; + } + } + if (currentText.length > 0) { + runs.push({ + text: currentText, + style: styleStack[styleStack.length - 1] + }); + } + return runs; + } + function getPlainText(text, style) { + if (!hasTagStyles(style) || !hasTagMarkup(text)) { + return text; + } + const runs = parseTaggedText(text, style); + return runs.map((run) => run.text).join(""); + } + + "use strict"; + const NEWLINES = [ + 10, + // line feed + 13 + // carriage return + ]; + const NEWLINES_SET = new Set(NEWLINES); + const BREAKING_SPACES = [ + 9, + // character tabulation + 32, + // space + 8192, + // en quad + 8193, + // em quad + 8194, + // en space + 8195, + // em space + 8196, + // three-per-em space + 8197, + // four-per-em space + 8198, + // six-per-em space + 8200, + // punctuation space + 8201, + // thin space + 8202, + // hair space + 8287, + // medium mathematical space + 12288 + // ideographic space + ]; + const BREAKING_SPACES_SET = new Set(BREAKING_SPACES); + const NEWLINE_SPLIT_REGEX = /(\r\n|\r|\n)/; + const NEWLINE_MATCH_REGEX = /(?:\r\n|\r|\n)/; + function isNewline(char) { + if (typeof char !== "string") { + return false; + } + return NEWLINES_SET.has(char.charCodeAt(0)); + } + function isBreakingSpace(char, _nextChar) { + if (typeof char !== "string") { + return false; + } + return BREAKING_SPACES_SET.has(char.charCodeAt(0)); + } + function collapseSpaces(whiteSpace) { + return whiteSpace === "normal" || whiteSpace === "pre-line"; + } + function collapseNewlines(whiteSpace) { + return whiteSpace === "normal"; + } + function trimRight(text) { + if (typeof text !== "string") { + return ""; + } + let i = text.length - 1; + while (i >= 0 && isBreakingSpace(text[i])) { + i--; + } + return i < text.length - 1 ? text.slice(0, i + 1) : text; + } + function tokenize(text) { + const tokens = []; + const tokenChars = []; + if (typeof text !== "string") { + return tokens; + } + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const nextChar = text[i + 1]; + if (isBreakingSpace(char, nextChar) || isNewline(char)) { + if (tokenChars.length > 0) { + tokens.push(tokenChars.join("")); + tokenChars.length = 0; + } + if (char === "\r" && nextChar === "\n") { + tokens.push("\r\n"); + i++; + } else { + tokens.push(char); + } + continue; + } + tokenChars.push(char); + } + if (tokenChars.length > 0) { + tokens.push(tokenChars.join("")); + } + return tokens; + } + function getCharacterGroups(token, breakWords, splitFn, canBreakCharsFn) { + const characters = splitFn(token); + const groups = []; + for (let j = 0; j < characters.length; j++) { + let char = characters[j]; + let lastChar = char; + let k = 1; + while (characters[j + k]) { + const nextChar = characters[j + k]; + if (!canBreakCharsFn(lastChar, nextChar, token, j, breakWords)) { + char += nextChar; + lastChar = nextChar; + k++; + } else { + break; + } + } + j += k - 1; + groups.push(char); + } + return groups; + } + + "use strict"; + const NEWLINE_TO_SPACE_REGEX = /\r\n|\r|\n/g; + function measureTaggedText(text, style, wordWrap, context, measureTextFn, measureFontFn, canBreakCharsFn, wordWrapSplitFn) { + var _a; + const runs = parseTaggedText(text, style); + const shouldCollapseNewlines = collapseNewlines(style.whiteSpace); + if (shouldCollapseNewlines) { + for (let i = 0; i < runs.length; i++) { + const run = runs[i]; + runs[i] = { text: run.text.replace(NEWLINE_TO_SPACE_REGEX, " "), style: run.style }; + } + } + const runsByLine = []; + let currentLineRuns = []; + for (const run of runs) { + const parts = run.text.split(NEWLINE_SPLIT_REGEX); + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (part === "\r\n" || part === "\r" || part === "\n") { + runsByLine.push(currentLineRuns); + currentLineRuns = []; + } else if (part.length > 0) { + currentLineRuns.push({ text: part, style: run.style }); + } + } + } + if (currentLineRuns.length > 0 || runsByLine.length === 0) { + runsByLine.push(currentLineRuns); + } + const wrappedRunsByLine = wordWrap ? wordWrapTaggedLines( + runsByLine, + style, + context, + measureTextFn, + canBreakCharsFn, + wordWrapSplitFn + ) : runsByLine; + const lineWidths = []; + const lineAscents = []; + const lineDescents = []; + const lineHeightsArr = []; + const lines = []; + let maxLineWidth = 0; + const baseFont = style._fontString; + const baseFontProps = measureFontFn(baseFont); + if (baseFontProps.fontSize === 0) { + baseFontProps.fontSize = style.fontSize; + baseFontProps.ascent = style.fontSize; + } + let lastFont = ""; + let hasDropShadow = !!style.dropShadow; + for (const lineRuns of wrappedRunsByLine) { + let lineWidth = 0; + let lineAscent = baseFontProps.ascent; + let lineDescent = baseFontProps.descent; + let lineText = ""; + for (const run of lineRuns) { + const runFont = run.style._fontString; + const runFontProps = measureFontFn(runFont); + if (runFont !== lastFont) { + context.font = runFont; + lastFont = runFont; + } + const runWidth = measureTextFn(run.text, run.style.letterSpacing, context); + lineWidth += runWidth; + lineAscent = Math.max(lineAscent, runFontProps.ascent); + lineDescent = Math.max(lineDescent, runFontProps.descent); + lineText += run.text; + if (!hasDropShadow && run.style.dropShadow) { + hasDropShadow = true; + } + } + if (lineRuns.length === 0) { + lineAscent = baseFontProps.ascent; + lineDescent = baseFontProps.descent; + } + lineWidths.push(lineWidth); + lineAscents.push(lineAscent); + lineDescents.push(lineDescent); + lines.push(lineText); + const computedLineHeight = style.lineHeight || lineAscent + lineDescent; + lineHeightsArr.push(computedLineHeight + style.leading); + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + const strokeWidth = ((_a = style._stroke) == null ? void 0 : _a.width) || 0; + const useWrapWidth = wordWrap && style.align !== "left" && style.align !== "justify"; + const alignWidth = useWrapWidth ? Math.max(maxLineWidth, style.wordWrapWidth) : maxLineWidth; + const width = alignWidth + strokeWidth + (style.dropShadow ? style.dropShadow.distance : 0); + let baseHeight = 0; + for (let i = 0; i < lineHeightsArr.length; i++) { + baseHeight += lineHeightsArr[i]; + } + baseHeight = Math.max(baseHeight, lineHeightsArr[0] + strokeWidth); + const height = baseHeight + (style.dropShadow ? style.dropShadow.distance : 0); + const baseLineHeight = style.lineHeight || baseFontProps.fontSize; + return { + width, + height, + lines, + lineWidths, + lineHeight: baseLineHeight + style.leading, + maxLineWidth, + fontProperties: baseFontProps, + runsByLine: wrappedRunsByLine, + lineAscents, + lineDescents, + lineHeights: lineHeightsArr, + hasDropShadow + }; + } + function wordWrapTaggedLines(runsByLine, style, context, measureTextFn, canBreakCharsFn, wordWrapSplitFn) { + var _a, _b, _c; + const { letterSpacing, whiteSpace, wordWrapWidth, breakWords } = style; + const shouldCollapseSpaces = collapseSpaces(whiteSpace); + const adjustedWrapWidth = wordWrapWidth + letterSpacing; + const tokenWidthCache = {}; + let lastFont = ""; + const measureTokenWidth = (token, tokenStyle) => { + const cacheKey = `${token}|${tokenStyle.styleKey}`; + let width = tokenWidthCache[cacheKey]; + if (width === void 0) { + const font = tokenStyle._fontString; + if (font !== lastFont) { + context.font = font; + lastFont = font; + } + width = measureTextFn(token, tokenStyle.letterSpacing, context) + tokenStyle.letterSpacing; + tokenWidthCache[cacheKey] = width; + } + return width; + }; + const result = []; + for (const lineRuns of runsByLine) { + const styledTokens = tokenizeTaggedRuns(lineRuns); + const resultStartLength = result.length; + const getWordGroupWidth = (startIndex) => { + let totalWidth = 0; + let j = startIndex; + do { + const { token: groupToken, style: groupStyle } = styledTokens[j]; + totalWidth += measureTokenWidth(groupToken, groupStyle); + j++; + } while (j < styledTokens.length && styledTokens[j].continuesFromPrevious); + return totalWidth; + }; + const getWordGroupTokens = (startIndex) => { + const tokens = []; + let j = startIndex; + do { + tokens.push({ token: styledTokens[j].token, style: styledTokens[j].style }); + j++; + } while (j < styledTokens.length && styledTokens[j].continuesFromPrevious); + return tokens; + }; + let currentLineRuns = []; + let currentWidth = 0; + let canPrependSpaces = !shouldCollapseSpaces; + let buildingRun = null; + const flushBuildingRun = () => { + if (buildingRun && buildingRun.text.length > 0) { + currentLineRuns.push(buildingRun); + } + buildingRun = null; + }; + const startNewLine = () => { + flushBuildingRun(); + if (currentLineRuns.length > 0) { + const lastRun = currentLineRuns[currentLineRuns.length - 1]; + lastRun.text = trimRight(lastRun.text); + if (lastRun.text.length === 0) { + currentLineRuns.pop(); + } + } + result.push(currentLineRuns); + currentLineRuns = []; + currentWidth = 0; + canPrependSpaces = false; + }; + for (let i = 0; i < styledTokens.length; i++) { + const { token, style: tokenStyle, continuesFromPrevious } = styledTokens[i]; + const tokenWidth = measureTokenWidth(token, tokenStyle); + if (shouldCollapseSpaces) { + const currIsSpace = isBreakingSpace(token); + const lastChar = (_c = (_b = buildingRun == null ? void 0 : buildingRun.text[buildingRun.text.length - 1]) != null ? _b : (_a = currentLineRuns[currentLineRuns.length - 1]) == null ? void 0 : _a.text.slice(-1)) != null ? _c : ""; + const lastIsSpace = lastChar ? isBreakingSpace(lastChar) : false; + if (currIsSpace && lastIsSpace) { + continue; + } + } + const startsWordGroup = !continuesFromPrevious; + const wordGroupWidth = startsWordGroup ? getWordGroupWidth(i) : tokenWidth; + if (wordGroupWidth > adjustedWrapWidth && startsWordGroup) { + if (currentWidth > 0) { + startNewLine(); + } + if (breakWords) { + const wordGroupTokens = getWordGroupTokens(i); + for (let g = 0; g < wordGroupTokens.length; g++) { + const groupToken = wordGroupTokens[g].token; + const groupStyle = wordGroupTokens[g].style; + const charGroups = getCharacterGroups( + groupToken, + breakWords, + wordWrapSplitFn, + canBreakCharsFn + ); + for (const char of charGroups) { + const charWidth = measureTokenWidth(char, groupStyle); + if (charWidth + currentWidth > adjustedWrapWidth) { + startNewLine(); + } + if (!buildingRun || buildingRun.style !== groupStyle) { + flushBuildingRun(); + buildingRun = { text: char, style: groupStyle }; + } else { + buildingRun.text += char; + } + currentWidth += charWidth; + } + } + i += wordGroupTokens.length - 1; + } else { + const wordGroupTokens = getWordGroupTokens(i); + flushBuildingRun(); + result.push(wordGroupTokens.map((t) => ({ text: t.token, style: t.style }))); + canPrependSpaces = false; + i += wordGroupTokens.length - 1; + } + } else if (wordGroupWidth + currentWidth > adjustedWrapWidth && startsWordGroup) { + if (isBreakingSpace(token)) { + canPrependSpaces = false; + continue; + } + startNewLine(); + buildingRun = { text: token, style: tokenStyle }; + currentWidth = tokenWidth; + } else if (continuesFromPrevious && !breakWords) { + if (!buildingRun || buildingRun.style !== tokenStyle) { + flushBuildingRun(); + buildingRun = { text: token, style: tokenStyle }; + } else { + buildingRun.text += token; + } + currentWidth += tokenWidth; + } else { + const isSpace = isBreakingSpace(token); + if (currentWidth === 0 && isSpace && !canPrependSpaces) { + continue; + } + if (!buildingRun || buildingRun.style !== tokenStyle) { + flushBuildingRun(); + buildingRun = { text: token, style: tokenStyle }; + } else { + buildingRun.text += token; + } + currentWidth += tokenWidth; + } + } + flushBuildingRun(); + if (currentLineRuns.length > 0) { + const lastRun = currentLineRuns[currentLineRuns.length - 1]; + lastRun.text = trimRight(lastRun.text); + if (lastRun.text.length === 0) { + currentLineRuns.pop(); + } + } + if (currentLineRuns.length > 0 || result.length === resultStartLength) { + result.push(currentLineRuns); + } + } + return result; + } + function tokenizeTaggedRuns(runs) { + const styledTokens = []; + let lastTokenWasWord = false; + for (const run of runs) { + const tokens = tokenize(run.text); + let isFirstTokenInRun = true; + for (const token of tokens) { + const isSpace = isBreakingSpace(token) || isNewline(token); + const continuesFromPrevious = isFirstTokenInRun && lastTokenWasWord && !isSpace; + styledTokens.push({ token, style: run.style, continuesFromPrevious }); + lastTokenWasWord = !isSpace; + isFirstTokenInRun = false; + } + } + return styledTokens; + } + + "use strict"; + const contextSettings$1 = { + // TextMetrics requires getImageData readback for measuring fonts. + willReadFrequently: true + }; + function getFromCache(key, letterSpacing, cache, context, measureTextFn) { + let width = cache[key]; + if (typeof width !== "number") { + width = measureTextFn(key, letterSpacing, context) + letterSpacing; + cache[key] = width; + } + return width; + } + function wordWrap(text, style, canvas, measureTextFn, canBreakWordsFn, canBreakCharsFn, wordWrapSplitFn) { + const context = canvas.getContext("2d", contextSettings$1); + context.font = style._fontString; + let width = 0; + let line = ""; + const linesArray = []; + const cache = /* @__PURE__ */ Object.create(null); + const { letterSpacing, whiteSpace } = style; + const shouldCollapseSpaces = collapseSpaces(whiteSpace); + const shouldCollapseNewlines = collapseNewlines(whiteSpace); + let canPrependSpaces = !shouldCollapseSpaces; + const wordWrapWidth = style.wordWrapWidth + letterSpacing; + const tokens = tokenize(text); + for (let i = 0; i < tokens.length; i++) { + let token = tokens[i]; + if (isNewline(token)) { + if (!shouldCollapseNewlines) { + linesArray.push(trimRight(line)); + canPrependSpaces = !shouldCollapseSpaces; + line = ""; + width = 0; + continue; + } + token = " "; + } + if (shouldCollapseSpaces) { + const currIsBreakingSpace = isBreakingSpace(token); + const lastIsBreakingSpace = isBreakingSpace(line[line.length - 1]); + if (currIsBreakingSpace && lastIsBreakingSpace) { + continue; + } + } + const tokenWidth = getFromCache(token, letterSpacing, cache, context, measureTextFn); + if (tokenWidth > wordWrapWidth) { + if (line !== "") { + linesArray.push(trimRight(line)); + line = ""; + width = 0; + } + if (canBreakWordsFn(token, style.breakWords)) { + const charGroups = getCharacterGroups(token, style.breakWords, wordWrapSplitFn, canBreakCharsFn); + for (const char of charGroups) { + const characterWidth = getFromCache(char, letterSpacing, cache, context, measureTextFn); + if (characterWidth + width > wordWrapWidth) { + linesArray.push(trimRight(line)); + canPrependSpaces = false; + line = ""; + width = 0; + } + line += char; + width += characterWidth; + } + } else { + if (line.length > 0) { + linesArray.push(trimRight(line)); + line = ""; + width = 0; + } + linesArray.push(trimRight(token)); + canPrependSpaces = false; + line = ""; + width = 0; + } + } else { + if (tokenWidth + width > wordWrapWidth) { + canPrependSpaces = false; + linesArray.push(trimRight(line)); + line = ""; + width = 0; + } + if (line.length > 0 || !isBreakingSpace(token) || canPrependSpaces) { + line += token; + width += tokenWidth; + } + } + } + const trimmedLine = trimRight(line); + if (trimmedLine.length > 0) { + linesArray.push(trimmedLine); + } + return linesArray.join("\n"); + } + + "use strict"; + const contextSettings = { + // TextMetrics requires getImageData readback for measuring fonts. + willReadFrequently: true + }; + const _CanvasTextMetrics = class _CanvasTextMetrics { + /** + * Checking that we can use modern canvas 2D API. + * + * Note: This is an unstable API, Chrome < 94 use `textLetterSpacing`, later versions use `letterSpacing`. + * @see CanvasTextMetrics.experimentalLetterSpacing + * @see https://developer.mozilla.org/en-US/docs/Web/API/ICanvasRenderingContext2D/letterSpacing + * @see https://developer.chrome.com/origintrials/#/view_trial/3585991203293757441 + */ + static get experimentalLetterSpacingSupported() { + let result = _CanvasTextMetrics._experimentalLetterSpacingSupported; + if (result === void 0) { + const proto = DOMAdapter.get().getCanvasRenderingContext2D().prototype; + result = _CanvasTextMetrics._experimentalLetterSpacingSupported = "letterSpacing" in proto || "textLetterSpacing" in proto; + } + return result; + } + /** + * @param text - the text that was measured + * @param style - the style that was measured + * @param width - the measured width of the text + * @param height - the measured height of the text + * @param lines - an array of the lines of text broken by new lines and wrapping if specified in style + * @param lineWidths - an array of the line widths for each line matched to `lines` + * @param lineHeight - the measured line height for this style + * @param maxLineWidth - the maximum line width for all measured lines + * @param fontProperties - the font properties object from TextMetrics.measureFont + * @param taggedData - optional object containing tagged text specific data + * @param taggedData.runsByLine - per-line style runs for tagged text + * @param taggedData.lineAscents - per-line ascent values for tagged text + * @param taggedData.lineDescents - per-line descent values for tagged text + * @param taggedData.lineHeights - per-line height values for tagged text + * @param taggedData.hasDropShadow - whether any run has a drop shadow + */ + constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties, taggedData) { + this.text = text; + this.style = style; + this.width = width; + this.height = height; + this.lines = lines; + this.lineWidths = lineWidths; + this.lineHeight = lineHeight; + this.maxLineWidth = maxLineWidth; + this.fontProperties = fontProperties; + if (taggedData) { + this.runsByLine = taggedData.runsByLine; + this.lineAscents = taggedData.lineAscents; + this.lineDescents = taggedData.lineDescents; + this.lineHeights = taggedData.lineHeights; + this.hasDropShadow = taggedData.hasDropShadow; + } + } + /** + * Measures the supplied string of text and returns a Rectangle. + * @param text - The text to measure. + * @param style - The text style to use for measuring + * @param canvas - optional specification of the canvas to use for measuring. + * @param wordWrap + * @returns Measured width and height of the text. + */ + static measureText(text = " ", style, canvas = _CanvasTextMetrics._canvas, wordWrap2 = style.wordWrap) { + var _a, _b; + const textKey = `${text}-${style.styleKey}-wordWrap-${wordWrap2}`; + if (_CanvasTextMetrics._measurementCache.has(textKey)) { + return _CanvasTextMetrics._measurementCache.get(textKey); + } + const isTagged = hasTagStyles(style) && hasTagMarkup(text); + if (isTagged) { + const result = measureTaggedText( + text, + style, + wordWrap2, + _CanvasTextMetrics._context, + _CanvasTextMetrics._measureText, + _CanvasTextMetrics.measureFont, + _CanvasTextMetrics.canBreakChars, + _CanvasTextMetrics.wordWrapSplit + ); + const measurements2 = new _CanvasTextMetrics( + text, + style, + result.width, + result.height, + result.lines, + result.lineWidths, + result.lineHeight, + result.maxLineWidth, + result.fontProperties, + { + runsByLine: result.runsByLine, + lineAscents: result.lineAscents, + lineDescents: result.lineDescents, + lineHeights: result.lineHeights, + hasDropShadow: result.hasDropShadow + } + ); + _CanvasTextMetrics._measurementCache.set(textKey, measurements2); + return measurements2; + } + const font = style._fontString; + const fontProperties = _CanvasTextMetrics.measureFont(font); + if (fontProperties.fontSize === 0) { + fontProperties.fontSize = style.fontSize; + fontProperties.ascent = style.fontSize; + fontProperties.descent = 0; + } + const context = _CanvasTextMetrics._context; + context.font = font; + const outputText = wordWrap2 ? _CanvasTextMetrics._wordWrap(text, style, canvas) : text; + const lines = outputText.split(NEWLINE_MATCH_REGEX); + const lineWidths = new Array(lines.length); + let maxLineWidth = 0; + for (let i = 0; i < lines.length; i++) { + const lineWidth = _CanvasTextMetrics._measureText(lines[i], style.letterSpacing, context); + lineWidths[i] = lineWidth; + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + const strokeWidth = (_b = (_a = style._stroke) == null ? void 0 : _a.width) != null ? _b : 0; + const lineHeight = style.lineHeight || fontProperties.fontSize; + const baseWidth = _CanvasTextMetrics._getAlignWidth(maxLineWidth, style, wordWrap2); + const width = _CanvasTextMetrics._adjustWidthForStyle(baseWidth, style); + const baseHeight = Math.max(lineHeight, fontProperties.fontSize + strokeWidth) + (lines.length - 1) * (lineHeight + style.leading); + const height = _CanvasTextMetrics._adjustHeightForStyle(baseHeight, style); + const measurements = new _CanvasTextMetrics( + text, + style, + width, + height, + lines, + lineWidths, + lineHeight + style.leading, + maxLineWidth, + fontProperties + ); + _CanvasTextMetrics._measurementCache.set(textKey, measurements); + return measurements; + } + /** + * Adjusts the measured width to account for stroke and drop shadow. + * @param baseWidth - The base content width + * @param style - The text style + * @returns The adjusted width + */ + static _adjustWidthForStyle(baseWidth, style) { + var _a; + const strokeWidth = ((_a = style._stroke) == null ? void 0 : _a.width) || 0; + let width = baseWidth + strokeWidth; + if (style.dropShadow) { + width += style.dropShadow.distance; + } + return width; + } + /** + * Adjusts the measured height to account for drop shadow. + * @param baseHeight - The base content height + * @param style - The text style + * @returns The adjusted height + */ + static _adjustHeightForStyle(baseHeight, style) { + let height = baseHeight; + if (style.dropShadow) { + height += style.dropShadow.distance; + } + return height; + } + /** + * Calculates the base width for alignment purposes. + * When word wrap is enabled with center/right alignment, uses wordWrapWidth. + * @param maxLineWidth - The maximum line width + * @param style - The text style + * @param wordWrapEnabled - Whether word wrap is enabled + * @returns The width to use for alignment calculations + */ + static _getAlignWidth(maxLineWidth, style, wordWrapEnabled) { + const useWrapWidth = wordWrapEnabled && style.align !== "left" && style.align !== "justify"; + return useWrapWidth ? Math.max(maxLineWidth, style.wordWrapWidth) : maxLineWidth; + } + /** + * Measures the rendered width of a string, accounting for letter spacing and using the provided context. + * @param text - The text to measure + * @param letterSpacing - Letter spacing in pixels + * @param context - Canvas 2D context + * @returns The measured width of the text with spacing + * @internal + */ + static _measureText(text, letterSpacing, context) { + var _a, _b; + let useExperimentalLetterSpacing = false; + if (_CanvasTextMetrics.experimentalLetterSpacingSupported) { + if (_CanvasTextMetrics.experimentalLetterSpacing) { + context.letterSpacing = `${letterSpacing}px`; + context.textLetterSpacing = `${letterSpacing}px`; + useExperimentalLetterSpacing = true; + } else { + context.letterSpacing = "0px"; + context.textLetterSpacing = "0px"; + } + } + const metrics = context.measureText(text); + let metricWidth = metrics.width; + const actualBoundingBoxLeft = -((_a = metrics.actualBoundingBoxLeft) != null ? _a : 0); + const actualBoundingBoxRight = (_b = metrics.actualBoundingBoxRight) != null ? _b : 0; + let boundsWidth = actualBoundingBoxRight - actualBoundingBoxLeft; + if (metricWidth > 0) { + if (useExperimentalLetterSpacing) { + metricWidth -= letterSpacing; + boundsWidth -= letterSpacing; + } else { + const val = (_CanvasTextMetrics.graphemeSegmenter(text).length - 1) * letterSpacing; + metricWidth += val; + boundsWidth += val; + } + } + return Math.max(metricWidth, boundsWidth); + } + /** + * Applies newlines to a string to have it optimally fit into the horizontal + * bounds set by the Text object's wordWrapWidth property. + * @param text - String to apply word wrapping to + * @param style - the style to use when wrapping + * @param canvas - optional specification of the canvas to use for measuring. + * @returns New string with new lines applied where required + */ + static _wordWrap(text, style, canvas = _CanvasTextMetrics._canvas) { + return wordWrap( + text, + style, + canvas, + _CanvasTextMetrics._measureText, + _CanvasTextMetrics.canBreakWords, + _CanvasTextMetrics.canBreakChars, + _CanvasTextMetrics.wordWrapSplit + ); + } + /** + * Determines if char is a breaking whitespace. + * + * It allows one to determine whether char should be a breaking whitespace + * For example certain characters in CJK langs or numbers. + * It must return a boolean. + * @param char - The character + * @param [_nextChar] - The next character + * @returns True if whitespace, False otherwise. + */ + static isBreakingSpace(char, _nextChar) { + return isBreakingSpace(char, _nextChar); + } + /** + * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior. + * + * It allows one to customise which words should break + * Examples are if the token is CJK or numbers. + * It must return a boolean. + * @param _token - The token + * @param breakWords - The style attr break words + * @returns Whether to break word or not + */ + static canBreakWords(_token, breakWords) { + return breakWords; + } + /** + * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior. + * + * It allows one to determine whether a pair of characters + * should be broken by newlines + * For example certain characters in CJK langs or numbers. + * It must return a boolean. + * @param _char - The character + * @param _nextChar - The next character + * @param _token - The token/word the characters are from + * @param _index - The index in the token of the char + * @param _breakWords - The style attr break words + * @returns whether to break word or not + */ + static canBreakChars(_char, _nextChar, _token, _index, _breakWords) { + return true; + } + /** + * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior. + * + * It is called when a token (usually a word) has to be split into separate pieces + * in order to determine the point to break a word. + * It must return an array of characters. + * @param token - The token to split + * @returns The characters of the token + * @see CanvasTextMetrics.graphemeSegmenter + */ + static wordWrapSplit(token) { + return _CanvasTextMetrics.graphemeSegmenter(token); + } + /** + * Calculates the ascent, descent and fontSize of a given font-style + * @param font - String representing the style of the font + * @returns Font properties object + */ + static measureFont(font) { + var _a, _b; + if (_CanvasTextMetrics._fonts[font]) { + return _CanvasTextMetrics._fonts[font]; + } + const context = _CanvasTextMetrics._context; + context.font = font; + const metrics = context.measureText(_CanvasTextMetrics.METRICS_STRING + _CanvasTextMetrics.BASELINE_SYMBOL); + const ascent = (_a = metrics.actualBoundingBoxAscent) != null ? _a : 0; + const descent = (_b = metrics.actualBoundingBoxDescent) != null ? _b : 0; + const properties = { + ascent, + descent, + fontSize: ascent + descent + }; + _CanvasTextMetrics._fonts[font] = properties; + return properties; + } + /** + * Clear font metrics in metrics cache. + * @param {string} [font] - font name. If font name not set then clear cache for all fonts. + */ + static clearMetrics(font = "") { + if (font) { + delete _CanvasTextMetrics._fonts[font]; + } else { + _CanvasTextMetrics._fonts = {}; + } + } + /** + * Cached canvas element for measuring text + * TODO: this should be private, but isn't because of backward compat, will fix later. + * @ignore + */ + static get _canvas() { + if (!_CanvasTextMetrics.__canvas) { + let canvas; + try { + const c = new OffscreenCanvas(0, 0); + const context = c.getContext("2d", contextSettings); + if (context == null ? void 0 : context.measureText) { + _CanvasTextMetrics.__canvas = c; + return c; + } + canvas = DOMAdapter.get().createCanvas(); + } catch (_cx) { + canvas = DOMAdapter.get().createCanvas(); + } + canvas.width = canvas.height = 10; + _CanvasTextMetrics.__canvas = canvas; + } + return _CanvasTextMetrics.__canvas; + } + /** + * TODO: this should be private, but isn't because of backward compat, will fix later. + * @ignore + */ + static get _context() { + if (!_CanvasTextMetrics.__context) { + _CanvasTextMetrics.__context = _CanvasTextMetrics._canvas.getContext("2d", contextSettings); + } + return _CanvasTextMetrics.__context; + } + }; + /** + * String used for calculate font metrics. + * These characters are all tall to help calculate the height required for text. + */ + _CanvasTextMetrics.METRICS_STRING = "|\xC9q\xC5"; + /** Baseline symbol for calculate font metrics. */ + _CanvasTextMetrics.BASELINE_SYMBOL = "M"; + /** Baseline multiplier for calculate font metrics. */ + _CanvasTextMetrics.BASELINE_MULTIPLIER = 1.4; + /** Height multiplier for setting height of canvas to calculate font metrics. */ + _CanvasTextMetrics.HEIGHT_MULTIPLIER = 2; + /** + * A Unicode "character", or "grapheme cluster", can be composed of multiple Unicode code points, + * such as letters with diacritical marks (e.g. `'\u0065\u0301'`, letter e with acute) + * or emojis with modifiers (e.g. `'\uD83E\uDDD1\u200D\uD83D\uDCBB'`, technologist). + * The new `Intl.Segmenter` API in ES2022 can split the string into grapheme clusters correctly. If it is not available, + * PixiJS will fallback to use the iterator of String, which can only spilt the string into code points. + * If you want to get full functionality in environments that don't support `Intl.Segmenter` (such as Firefox), + * you can use other libraries such as [grapheme-splitter]{@link https://www.npmjs.com/package/grapheme-splitter} + * or [graphemer]{@link https://www.npmjs.com/package/graphemer} to create a polyfill. Since these libraries can be + * relatively large in size to handle various Unicode grapheme clusters properly, PixiJS won't use them directly. + */ + _CanvasTextMetrics.graphemeSegmenter = (() => { + if (typeof (Intl == null ? void 0 : Intl.Segmenter) === "function") { + const segmenter = new Intl.Segmenter(); + return (s) => { + const segments = segmenter.segment(s); + const result = []; + let i = 0; + for (const segment of segments) { + result[i++] = segment.segment; + } + return result; + }; + } + return (s) => [...s]; + })(); + /** + * New rendering behavior for letter-spacing which uses Chrome's new native API. This will + * lead to more accurate letter-spacing results because it does not try to manually draw + * each character. However, this Chrome API is experimental and may not serve all cases yet. + * @see CanvasTextMetrics.experimentalLetterSpacingSupported + */ + _CanvasTextMetrics.experimentalLetterSpacing = false; + /** Cache of {@link TextMetrics.FontMetrics} objects. */ + _CanvasTextMetrics._fonts = {}; + /** Cache for measured text metrics */ + _CanvasTextMetrics._measurementCache = lru(1e3); + let CanvasTextMetrics = _CanvasTextMetrics; + + "use strict"; + const genericFontFamilies = [ + "serif", + "sans-serif", + "monospace", + "cursive", + "fantasy", + "system-ui" + ]; + function fontStringFromTextStyle(style) { + const fontSizeString = typeof style.fontSize === "number" ? `${style.fontSize}px` : style.fontSize; + let fontFamilies = style.fontFamily; + if (!Array.isArray(style.fontFamily)) { + fontFamilies = style.fontFamily.split(","); + } + for (let i = fontFamilies.length - 1; i >= 0; i--) { + let fontFamily = fontFamilies[i].trim(); + if (!/([\"\'])[^\'\"]+\1/.test(fontFamily) && !genericFontFamilies.includes(fontFamily)) { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(",")}`; + } + + "use strict"; + const PRECISION = 1e5; + function getCanvasFillStyle(fillStyle, context, textMetrics, padding = 0, offsetX = 0, offsetY = 0) { + var _a; + if (fillStyle.texture === Texture.WHITE && !fillStyle.fill) { + return Color.shared.setValue(fillStyle.color).setAlpha((_a = fillStyle.alpha) != null ? _a : 1).toHexa(); + } else if (!fillStyle.fill) { + const pattern = context.createPattern(fillStyle.texture.source.resource, "repeat"); + const tempMatrix = fillStyle.matrix.copyTo(Matrix.shared); + tempMatrix.scale(fillStyle.texture.source.pixelWidth, fillStyle.texture.source.pixelHeight); + pattern.setTransform(tempMatrix); + return pattern; + } else if (fillStyle.fill instanceof FillPattern) { + const fillPattern = fillStyle.fill; + const pattern = context.createPattern(fillPattern.texture.source.resource, "repeat"); + const tempMatrix = fillPattern.transform.copyTo(Matrix.shared); + tempMatrix.scale( + fillPattern.texture.source.pixelWidth, + fillPattern.texture.source.pixelHeight + ); + pattern.setTransform(tempMatrix); + return pattern; + } else if (fillStyle.fill instanceof FillGradient) { + const fillGradient = fillStyle.fill; + const isLinear = fillGradient.type === "linear"; + const isLocal = fillGradient.textureSpace === "local"; + let width = 1; + let height = 1; + if (isLocal && textMetrics) { + width = textMetrics.width + padding; + height = textMetrics.height + padding; + } + let gradient; + let isNearlyVertical = false; + if (isLinear) { + const { start, end } = fillGradient; + gradient = context.createLinearGradient( + start.x * width + offsetX, + start.y * height + offsetY, + end.x * width + offsetX, + end.y * height + offsetY + ); + isNearlyVertical = Math.abs(end.x - start.x) < Math.abs((end.y - start.y) * 0.1); + } else { + const { center, innerRadius, outerCenter, outerRadius } = fillGradient; + gradient = context.createRadialGradient( + center.x * width + offsetX, + center.y * height + offsetY, + innerRadius * width, + outerCenter.x * width + offsetX, + outerCenter.y * height + offsetY, + outerRadius * width + ); + } + if (isNearlyVertical && isLocal && textMetrics) { + const ratio = textMetrics.lineHeight / height; + for (let i = 0; i < textMetrics.lines.length; i++) { + const start = (i * textMetrics.lineHeight + padding / 2) / height; + fillGradient.colorStops.forEach((stop) => { + let globalStop = start + stop.offset * ratio; + globalStop = Math.max(0, Math.min(1, globalStop)); + gradient.addColorStop( + // fix to 5 decimal places to avoid floating point precision issues + Math.floor(globalStop * PRECISION) / PRECISION, + Color.shared.setValue(stop.color).toHex() + ); + }); + } + } else { + fillGradient.colorStops.forEach((stop) => { + gradient.addColorStop(stop.offset, Color.shared.setValue(stop.color).toHex()); + }); + } + return gradient; + } + warn("FillStyle not recognised", fillStyle); + return "red"; + } + + "use strict"; + const tempRect$1 = new Rectangle(); + class CanvasTextGeneratorClass { + /** + * Creates a canvas with the specified text rendered to it. + * + * Generates a canvas of appropriate size, renders the text with the provided style, + * and returns both the canvas/context and a Rectangle representing the text bounds. + * + * When trim is enabled in the style, the frame will represent the bounds of the + * non-transparent pixels, which can be smaller than the full canvas. + * @param options - The options for generating the text canvas + * @param options.text - The text to render + * @param options.style - The style to apply to the text + * @param options.resolution - The resolution of the canvas (defaults to 1) + * @param options.padding + * @returns An object containing the canvas/context and the frame (bounds) of the text + */ + getCanvasAndContext(options) { + const { text, style, resolution = 1 } = options; + const padding = style._getFinalPadding(); + const measured = CanvasTextMetrics.measureText(text || " ", style); + const width = Math.ceil(Math.ceil(Math.max(1, measured.width) + padding * 2) * resolution); + const height = Math.ceil(Math.ceil(Math.max(1, measured.height) + padding * 2) * resolution); + const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(width, height); + this._renderTextToCanvas(style, padding, resolution, canvasAndContext, measured); + const frame = style.trim ? getCanvasBoundingBox({ canvas: canvasAndContext.canvas, width, height, resolution: 1, output: tempRect$1 }) : tempRect$1.set(0, 0, width, height); + return { + canvasAndContext, + frame + }; + } + /** + * Returns a canvas and context to the pool. + * + * This should be called when you're done with the canvas to allow reuse + * and prevent memory leaks. + * @param canvasAndContext - The canvas and context to return to the pool + */ + returnCanvasAndContext(canvasAndContext) { + CanvasPool.returnCanvasAndContext(canvasAndContext); + } + /** + * Renders text to its canvas, and updates its texture. + * @param style - The style of the text + * @param padding - The padding of the text + * @param resolution - The resolution of the text + * @param canvasAndContext - The canvas and context to render the text to + * @param measured - Pre-measured text metrics to avoid duplicate measurement + */ + _renderTextToCanvas(style, padding, resolution, canvasAndContext, measured) { + var _a, _b, _c, _d, _e, _f; + if (measured.runsByLine && measured.runsByLine.length > 0) { + this._renderTaggedTextToCanvas(measured, style, padding, resolution, canvasAndContext); + return; + } + const { canvas, context } = canvasAndContext; + const font = fontStringFromTextStyle(style); + const lines = measured.lines; + const lineHeight = measured.lineHeight; + const lineWidths = measured.lineWidths; + const maxLineWidth = measured.maxLineWidth; + const fontProperties = measured.fontProperties; + const height = canvas.height; + context.resetTransform(); + context.scale(resolution, resolution); + context.textBaseline = style.textBaseline; + if ((_a = style._stroke) == null ? void 0 : _a.width) { + const strokeStyle = style._stroke; + context.lineWidth = strokeStyle.width; + context.miterLimit = strokeStyle.miterLimit; + context.lineJoin = strokeStyle.join; + context.lineCap = strokeStyle.cap; + } + context.font = font; + let linePositionX; + let linePositionY; + const passesCount = style.dropShadow ? 2 : 1; + const alignWidth = style.wordWrap ? style.wordWrapWidth : maxLineWidth; + const strokeWidth = (_c = (_b = style._stroke) == null ? void 0 : _b.width) != null ? _c : 0; + const halfStroke = strokeWidth / 2; + let linePositionYShift = (lineHeight - fontProperties.fontSize) / 2; + if (lineHeight - fontProperties.fontSize < 0) { + linePositionYShift = 0; + } + for (let i = 0; i < passesCount; ++i) { + const isShadowPass = style.dropShadow && i === 0; + const dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + padding * 2) : 0; + const dsOffsetShadow = dsOffsetText * resolution; + if (isShadowPass) { + this._setupDropShadow(context, style, resolution, dsOffsetShadow); + } else { + const gradientBounds = style._gradientBounds; + const gradientOffset = style._gradientOffset; + if (gradientBounds) { + const gradientMetrics = { + width: gradientBounds.width, + height: gradientBounds.height, + lineHeight: gradientBounds.height, + lines: measured.lines + }; + this._setFillAndStrokeStyles( + context, + style, + gradientMetrics, + padding, + halfStroke, + (_d = gradientOffset == null ? void 0 : gradientOffset.x) != null ? _d : 0, + (_e = gradientOffset == null ? void 0 : gradientOffset.y) != null ? _e : 0 + ); + } else if (gradientOffset) { + this._setFillAndStrokeStyles( + context, + style, + measured, + padding, + halfStroke, + gradientOffset.x, + gradientOffset.y + ); + } else { + this._setFillAndStrokeStyles(context, style, measured, padding, halfStroke); + } + context.shadowColor = "black"; + } + for (let j = 0; j < lines.length; j++) { + linePositionX = halfStroke; + linePositionY = halfStroke + j * lineHeight + fontProperties.ascent + linePositionYShift; + linePositionX += this._getAlignmentOffset(lineWidths[j], alignWidth, style.align); + if ((_f = style._stroke) == null ? void 0 : _f.width) { + this._drawLetterSpacing( + lines[j], + style, + canvasAndContext, + linePositionX + padding, + linePositionY + padding - dsOffsetText, + true + ); + } + if (style._fill !== void 0) { + this._drawLetterSpacing( + lines[j], + style, + canvasAndContext, + linePositionX + padding, + linePositionY + padding - dsOffsetText + ); + } + } + } + } + /** + * Renders tagged text (with per-run styles) to canvas. + * @param measured - The measured text metrics containing runsByLine + * @param style - The base text style + * @param padding - The padding of the text + * @param resolution - The resolution of the text + * @param canvasAndContext - The canvas and context to render to + */ + _renderTaggedTextToCanvas(measured, style, padding, resolution, canvasAndContext) { + var _a, _b, _c; + const { canvas, context } = canvasAndContext; + const { runsByLine, lineWidths, maxLineWidth, lineAscents, lineHeights, hasDropShadow } = measured; + const height = canvas.height; + context.resetTransform(); + context.scale(resolution, resolution); + context.textBaseline = style.textBaseline; + const passesCount = hasDropShadow ? 2 : 1; + const alignWidth = style.wordWrap ? style.wordWrapWidth : maxLineWidth; + const strokeWidth = (_b = (_a = style._stroke) == null ? void 0 : _a.width) != null ? _b : 0; + const halfStroke = strokeWidth / 2; + const runDataByLine = []; + for (let lineIndex = 0; lineIndex < runsByLine.length; lineIndex++) { + const lineRuns = runsByLine[lineIndex]; + const runData = []; + for (const run of lineRuns) { + const font = fontStringFromTextStyle(run.style); + context.font = font; + runData.push({ + width: CanvasTextMetrics._measureText(run.text, run.style.letterSpacing, context), + font + }); + } + runDataByLine.push(runData); + } + for (let pass = 0; pass < passesCount; ++pass) { + const isShadowPass = hasDropShadow && pass === 0; + const dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + padding * 2) : 0; + const dsOffsetShadow = dsOffsetText * resolution; + if (!isShadowPass) { + context.shadowColor = "black"; + } + let currentY = halfStroke; + for (let lineIndex = 0; lineIndex < runsByLine.length; lineIndex++) { + const lineRuns = runsByLine[lineIndex]; + const lineWidth = lineWidths[lineIndex]; + const lineAscent = lineAscents[lineIndex]; + const currentLineHeight = lineHeights[lineIndex]; + const lineRunData = runDataByLine[lineIndex]; + let linePositionX = halfStroke; + linePositionX += this._getAlignmentOffset(lineWidth, alignWidth, style.align); + const linePositionY = currentY + lineAscent; + let runX = linePositionX + padding; + for (let runIndex = 0; runIndex < lineRuns.length; runIndex++) { + const run = lineRuns[runIndex]; + const { width: runWidth, font: runFont } = lineRunData[runIndex]; + context.font = runFont; + context.textBaseline = run.style.textBaseline; + if ((_c = run.style._stroke) == null ? void 0 : _c.width) { + const runStroke = run.style._stroke; + context.lineWidth = runStroke.width; + context.miterLimit = runStroke.miterLimit; + context.lineJoin = runStroke.join; + context.lineCap = runStroke.cap; + if (isShadowPass) { + if (run.style.dropShadow) { + this._setupDropShadow( + context, + run.style, + resolution, + dsOffsetShadow + ); + } else { + runX += runWidth; + continue; + } + } else { + const runFontProps = CanvasTextMetrics.measureFont(runFont); + const runHeight = run.style.lineHeight || runFontProps.fontSize; + const runMetrics = { + width: runWidth, + height: runHeight, + lineHeight: runHeight, + lines: [run.text] + }; + context.strokeStyle = getCanvasFillStyle( + runStroke, + context, + runMetrics, + padding * 2, + runX - padding, + currentY + ); + } + this._drawLetterSpacing( + run.text, + run.style, + canvasAndContext, + runX, + linePositionY + padding - dsOffsetText, + true + ); + } + runX += runWidth; + } + runX = linePositionX + padding; + for (let runIndex = 0; runIndex < lineRuns.length; runIndex++) { + const run = lineRuns[runIndex]; + const { width: runWidth, font: runFont } = lineRunData[runIndex]; + context.font = runFont; + context.textBaseline = run.style.textBaseline; + if (run.style._fill !== void 0) { + if (isShadowPass) { + if (run.style.dropShadow) { + this._setupDropShadow( + context, + run.style, + resolution, + dsOffsetShadow + ); + } else { + runX += runWidth; + continue; + } + } else { + const runFontProps = CanvasTextMetrics.measureFont(runFont); + const runHeight = run.style.lineHeight || runFontProps.fontSize; + const runMetrics = { + width: runWidth, + height: runHeight, + lineHeight: runHeight, + lines: [run.text] + }; + context.fillStyle = getCanvasFillStyle( + run.style._fill, + context, + runMetrics, + padding * 2, + runX - padding, + currentY + ); + } + this._drawLetterSpacing( + run.text, + run.style, + canvasAndContext, + runX, + linePositionY + padding - dsOffsetText, + false + ); + } + runX += runWidth; + } + currentY += currentLineHeight; + } + } + } + /** + * Sets fill and stroke styles on the canvas context for text rendering. + * @param context - The canvas context + * @param style - The text style + * @param metrics - The text metrics for gradient calculation + * @param padding - The padding value + * @param halfStroke - Half the stroke width + * @param offsetX - X offset for gradient positioning + * @param offsetY - Y offset for gradient positioning + */ + _setFillAndStrokeStyles(context, style, metrics, padding, halfStroke, offsetX = 0, offsetY = 0) { + var _a; + context.fillStyle = style._fill ? getCanvasFillStyle(style._fill, context, metrics, padding * 2, offsetX, offsetY) : null; + if ((_a = style._stroke) == null ? void 0 : _a.width) { + const strokePadding = halfStroke + padding * 2; + context.strokeStyle = getCanvasFillStyle( + style._stroke, + context, + metrics, + strokePadding, + offsetX, + offsetY + ); + } + } + /** + * Sets up the canvas context for drop shadow rendering. + * @param context - The canvas context + * @param style - The text style containing drop shadow options + * @param resolution - The resolution multiplier + * @param dsOffsetShadow - The shadow Y offset + */ + _setupDropShadow(context, style, resolution, dsOffsetShadow) { + context.fillStyle = "black"; + context.strokeStyle = "black"; + const shadowOptions = style.dropShadow; + const dropShadowColor = shadowOptions.color; + const dropShadowAlpha = shadowOptions.alpha; + context.shadowColor = Color.shared.setValue(dropShadowColor).setAlpha(dropShadowAlpha).toRgbaString(); + const dropShadowBlur = shadowOptions.blur * resolution; + const dropShadowDistance = shadowOptions.distance * resolution; + context.shadowBlur = dropShadowBlur; + context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance; + context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance + dsOffsetShadow; + } + /** + * Calculates the X offset for text alignment. + * @param lineWidth - The width of the current line + * @param alignWidth - The width to align against (maxLineWidth or wordWrapWidth) + * @param align - The text alignment + * @returns The X offset for this line + */ + _getAlignmentOffset(lineWidth, alignWidth, align) { + if (align === "right") { + return alignWidth - lineWidth; + } else if (align === "center") { + return (alignWidth - lineWidth) / 2; + } + return 0; + } + /** + * Render the text with letter-spacing. + * + * This method handles rendering text with the correct letter spacing, using either: + * 1. Native letter spacing if supported by the browser + * 2. Manual letter spacing calculation if not natively supported + * + * For manual letter spacing, it calculates the position of each character + * based on its width and the desired spacing. + * @param text - The text to draw + * @param style - The text style to apply + * @param canvasAndContext - The canvas and context to draw to + * @param x - Horizontal position to draw the text + * @param y - Vertical position to draw the text + * @param isStroke - Whether to render the stroke (true) or fill (false) + * @private + */ + _drawLetterSpacing(text, style, canvasAndContext, x, y, isStroke = false) { + const { context } = canvasAndContext; + const letterSpacing = style.letterSpacing; + let useExperimentalLetterSpacing = false; + if (CanvasTextMetrics.experimentalLetterSpacingSupported) { + if (CanvasTextMetrics.experimentalLetterSpacing) { + context.letterSpacing = `${letterSpacing}px`; + context.textLetterSpacing = `${letterSpacing}px`; + useExperimentalLetterSpacing = true; + } else { + context.letterSpacing = "0px"; + context.textLetterSpacing = "0px"; + } + } + if (letterSpacing === 0 || useExperimentalLetterSpacing) { + if (isStroke) { + context.strokeText(text, x, y); + } else { + context.fillText(text, x, y); + } + return; + } + let currentPosition = x; + const stringArray = CanvasTextMetrics.graphemeSegmenter(text); + let previousWidth = context.measureText(text).width; + let currentWidth = 0; + for (let i = 0; i < stringArray.length; ++i) { + const currentChar = stringArray[i]; + if (isStroke) { + context.strokeText(currentChar, currentPosition, y); + } else { + context.fillText(currentChar, currentPosition, y); + } + let textStr = ""; + for (let j = i + 1; j < stringArray.length; ++j) { + textStr += stringArray[j]; + } + currentWidth = context.measureText(textStr).width; + currentPosition += previousWidth - currentWidth + letterSpacing; + previousWidth = currentWidth; + } + } + } + const CanvasTextGenerator = new CanvasTextGeneratorClass(); + + "use strict"; + var __defProp$B = Object.defineProperty; + var __defProps$h = Object.defineProperties; + var __getOwnPropDescs$h = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$D = Object.getOwnPropertySymbols; + var __hasOwnProp$D = Object.prototype.hasOwnProperty; + var __propIsEnum$D = Object.prototype.propertyIsEnumerable; + var __defNormalProp$B = (obj, key, value) => key in obj ? __defProp$B(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$B = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$D.call(b, prop)) + __defNormalProp$B(a, prop, b[prop]); + if (__getOwnPropSymbols$D) + for (var prop of __getOwnPropSymbols$D(b)) { + if (__propIsEnum$D.call(b, prop)) + __defNormalProp$B(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$h = (a, b) => __defProps$h(a, __getOwnPropDescs$h(b)); + const _TextStyle = class _TextStyle extends EventEmitter { + constructor(style = {}) { + var _a; + super(); + /** + * Unique identifier for the TextStyle class. + * This is used to track instances and ensure uniqueness. + * @internal + */ + this.uid = uid$1("textStyle"); + /** + * Internal tick counter used to track updates and changes. + * This is incremented whenever the style is modified, allowing for efficient change detection. + * @internal + */ + this._tick = 0; + this._cachedFontString = null; + convertV7Tov8Style(style); + const isTextStyle = style instanceof _TextStyle; + const existingStyle = style; + if (isTextStyle) { + style = existingStyle._toObject(); + } + const fullStyle = __spreadValues$B(__spreadValues$B({}, _TextStyle.defaultTextStyle), style); + for (const key in fullStyle) { + const thisKey = key; + this[thisKey] = fullStyle[key]; + } + this._tagStyles = (_a = style.tagStyles) != null ? _a : void 0; + this.update(); + this._tick = 0; + } + /** + * Alignment for multiline text, does not affect single line text. + * @type {'left'|'center'|'right'|'justify'} + */ + get align() { + return this._align; + } + set align(value) { + if (this._align === value) return; + this._align = value; + this.update(); + } + /** Indicates if lines can be wrapped within words, it needs wordWrap to be set to true. */ + get breakWords() { + return this._breakWords; + } + set breakWords(value) { + if (this._breakWords === value) return; + this._breakWords = value; + this.update(); + } + /** Set a drop shadow for the text. */ + get dropShadow() { + return this._dropShadow; + } + set dropShadow(value) { + if (this._dropShadow === value) return; + if (value !== null && typeof value === "object") { + this._dropShadow = this._createProxy(__spreadValues$B(__spreadValues$B({}, _TextStyle.defaultDropShadow), value)); + } else { + this._dropShadow = value ? this._createProxy(__spreadValues$B({}, _TextStyle.defaultDropShadow)) : null; + } + this.update(); + } + /** The font family, can be a single font name, or a list of names where the first is the preferred font. */ + get fontFamily() { + return this._fontFamily; + } + set fontFamily(value) { + if (this._fontFamily === value) return; + this._fontFamily = value; + this.update(); + } + /** The font size (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em') */ + get fontSize() { + return this._fontSize; + } + set fontSize(value) { + if (this._fontSize === value) return; + if (typeof value === "string") { + this._fontSize = parseInt(value, 10); + } else { + this._fontSize = value; + } + this.update(); + } + /** + * The font style. + * @type {'normal'|'italic'|'oblique'} + */ + get fontStyle() { + return this._fontStyle; + } + set fontStyle(value) { + if (this._fontStyle === value) return; + this._fontStyle = value.toLowerCase(); + this.update(); + } + /** + * The font variant. + * @type {'normal'|'small-caps'} + */ + get fontVariant() { + return this._fontVariant; + } + set fontVariant(value) { + if (this._fontVariant === value) return; + this._fontVariant = value; + this.update(); + } + /** + * The font weight. + * @type {'normal'|'bold'|'bolder'|'lighter'|'100'|'200'|'300'|'400'|'500'|'600'|'700'|'800'|'900'} + */ + get fontWeight() { + return this._fontWeight; + } + set fontWeight(value) { + if (this._fontWeight === value) return; + this._fontWeight = value; + this.update(); + } + /** The space between lines. */ + get leading() { + return this._leading; + } + set leading(value) { + if (this._leading === value) return; + this._leading = value; + this.update(); + } + /** The amount of spacing between letters, default is 0. */ + get letterSpacing() { + return this._letterSpacing; + } + set letterSpacing(value) { + if (this._letterSpacing === value) return; + this._letterSpacing = value; + this.update(); + } + /** The line height, a number that represents the vertical space that a letter uses. */ + get lineHeight() { + return this._lineHeight; + } + set lineHeight(value) { + if (this._lineHeight === value) return; + this._lineHeight = value; + this.update(); + } + /** + * Occasionally some fonts are cropped. Adding some padding will prevent this from happening + * by adding padding to all sides of the text. + * > [!NOTE] This will NOT affect the positioning or bounds of the text. + */ + get padding() { + return this._padding; + } + set padding(value) { + if (this._padding === value) return; + this._padding = value; + this.update(); + } + /** + * An optional filter or array of filters to apply to the text, allowing for advanced visual effects. + * These filters will be applied to the text as it is created, resulting in faster rendering for static text + * compared to applying the filter directly to the text object (which would be applied at run time). + * @default null + */ + get filters() { + return this._filters; + } + set filters(value) { + if (this._filters === value) return; + this._filters = Object.freeze(value); + this.update(); + } + /** + * Trim transparent borders from the text texture. + * > [!IMPORTANT] PERFORMANCE WARNING: + * > This is a costly operation as it requires scanning pixel alpha values. + * > Avoid using `trim: true` for dynamic text, as it could significantly impact performance. + */ + get trim() { + return this._trim; + } + set trim(value) { + if (this._trim === value) return; + this._trim = value; + this.update(); + } + /** + * The baseline of the text that is rendered. + * @type {'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom'} + */ + get textBaseline() { + return this._textBaseline; + } + set textBaseline(value) { + if (this._textBaseline === value) return; + this._textBaseline = value; + this.update(); + } + /** + * How newlines and spaces should be handled. + * Default is 'pre' (preserve, preserve). + * + * value | New lines | Spaces + * --- | --- | --- + * 'normal' | Collapse | Collapse + * 'pre' | Preserve | Preserve + * 'pre-line' | Preserve | Collapse + * @type {'normal'|'pre'|'pre-line'} + */ + get whiteSpace() { + return this._whiteSpace; + } + set whiteSpace(value) { + if (this._whiteSpace === value) return; + this._whiteSpace = value; + this.update(); + } + /** Indicates if word wrap should be used. */ + get wordWrap() { + return this._wordWrap; + } + set wordWrap(value) { + if (this._wordWrap === value) return; + this._wordWrap = value; + this.update(); + } + /** The width at which text will wrap, it needs wordWrap to be set to true. */ + get wordWrapWidth() { + return this._wordWrapWidth; + } + set wordWrapWidth(value) { + if (this._wordWrapWidth === value) return; + this._wordWrapWidth = value; + this.update(); + } + /** + * The fill style that will be used to color the text. + * This can be: + * - A color string like 'red', '#00FF00', or 'rgba(255,0,0,0.5)' + * - A hex number like 0xff0000 for red + * - A FillStyle object with properties like { color: 0xff0000, alpha: 0.5 } + * - A FillGradient for gradient fills + * - A FillPattern for pattern/texture fills + * + * When using a FillGradient, vertical gradients (angle of 90 degrees) are applied per line of text, + * while gradients at any other angle are spread across the entire text body as a whole. + * @example + * // Vertical gradient applied per line + * const verticalGradient = new FillGradient(0, 0, 0, 1) + * .addColorStop(0, 0xff0000) + * .addColorStop(1, 0x0000ff); + * + * const text = new Text({ + * text: 'Line 1\nLine 2', + * style: { fill: verticalGradient } + * }); + * + * To manage the gradient in a global scope, set the textureSpace property of the FillGradient to 'global'. + * @type {string|number|FillStyle|FillGradient|FillPattern} + */ + get fill() { + return this._originalFill; + } + set fill(value) { + if (value === this._originalFill) return; + this._originalFill = value; + if (this._isFillStyle(value)) { + this._originalFill = this._createProxy(__spreadValues$B(__spreadValues$B({}, GraphicsContext.defaultFillStyle), value), () => { + this._fill = toFillStyle( + __spreadValues$B({}, this._originalFill), + GraphicsContext.defaultFillStyle + ); + }); + } + this._fill = toFillStyle( + value === 0 ? "black" : value, + GraphicsContext.defaultFillStyle + ); + this.update(); + } + /** A fillstyle that will be used on the text stroke, e.g., 'blue', '#FCFF00'. */ + get stroke() { + return this._originalStroke; + } + set stroke(value) { + if (value === this._originalStroke) return; + this._originalStroke = value; + if (this._isFillStyle(value)) { + this._originalStroke = this._createProxy(__spreadValues$B(__spreadValues$B({}, GraphicsContext.defaultStrokeStyle), value), () => { + this._stroke = toStrokeStyle( + __spreadValues$B({}, this._originalStroke), + GraphicsContext.defaultStrokeStyle + ); + }); + } + this._stroke = toStrokeStyle(value, GraphicsContext.defaultStrokeStyle); + this.update(); + } + /** + * Custom styles to apply to specific tags within the text. + * Allows for rich text formatting using simple tag markup like `text`. + * + * Tags are only parsed when this property has entries. If `tagStyles` is undefined, + * `<` characters in text are treated as literal. + * @example + * ```ts + * const text = new Text({ + * text: 'Red, Blue', + * style: { + * fill: 'white', + * tagStyles: { + * red: { fill: 'red' }, + * blue: { fill: 'blue' } + * } + * } + * }); + * ``` + */ + get tagStyles() { + return this._tagStyles; + } + set tagStyles(value) { + if (this._tagStyles === value) return; + this._tagStyles = value != null ? value : void 0; + this.update(); + } + update() { + this._tick++; + this._cachedFontString = null; + this.emit("update", this); + } + /** Resets all properties to the default values */ + reset() { + const defaultStyle = _TextStyle.defaultTextStyle; + for (const key in defaultStyle) { + this[key] = defaultStyle[key]; + } + } + /** + * Assigns partial style options to this TextStyle instance. + * Uses public setters to ensure proper value transformation. + * @param values - Partial style options to assign + * @returns This TextStyle instance for chaining + */ + assign(values) { + for (const key in values) { + const thisKey = key; + this[thisKey] = values[key]; + } + return this; + } + /** + * Returns a unique key for this instance. + * This key is used for caching. + * @returns {string} Unique key for the instance + */ + get styleKey() { + return `${this.uid}-${this._tick}`; + } + /** + * Returns the CSS font string for this style, cached for performance. + * @internal + * @returns CSS font string + */ + get _fontString() { + if (this._cachedFontString === null) { + this._cachedFontString = fontStringFromTextStyle(this); + } + return this._cachedFontString; + } + /** + * Returns an object with the same values as this TextStyle instance. + * @returns Object with the same values as this TextStyle instance + * @example + * ```ts + * const style = new TextStyle({ + * fontSize: 24, + * fill: 0xff0000, + * stroke: { color: 0x0000ff, width: 2 } + * }); + * const object = style.toObject(); + * console.log(object); + * // { fontSize: 24, fill: 0xff0000, stroke: { color: 0x0000ff, width: 2 } } + * ``` + */ + _toObject() { + return { + align: this.align, + breakWords: this.breakWords, + dropShadow: this._dropShadow ? __spreadValues$B({}, this._dropShadow) : null, + fill: this._fill ? __spreadValues$B({}, this._fill) : void 0, + fontFamily: this.fontFamily, + fontSize: this.fontSize, + fontStyle: this.fontStyle, + fontVariant: this.fontVariant, + fontWeight: this.fontWeight, + leading: this.leading, + letterSpacing: this.letterSpacing, + lineHeight: this.lineHeight, + padding: this.padding, + stroke: this._stroke ? __spreadValues$B({}, this._stroke) : void 0, + textBaseline: this.textBaseline, + trim: this.trim, + whiteSpace: this.whiteSpace, + wordWrap: this.wordWrap, + wordWrapWidth: this.wordWrapWidth, + filters: this._filters ? [...this._filters] : void 0, + tagStyles: this._tagStyles ? __spreadValues$B({}, this._tagStyles) : void 0 + }; + } + /** + * Creates a new TextStyle object with the same values as this one. + * @returns New cloned TextStyle object + */ + clone() { + return new _TextStyle(this._toObject()); + } + /** + * Returns the final padding for the text style, taking into account any filters applied. + * Used internally for correct measurements + * @internal + * @returns {number} The final padding for the text style. + */ + _getFinalPadding() { + let filterPadding = 0; + if (this._filters) { + for (let i = 0; i < this._filters.length; i++) { + filterPadding += this._filters[i].padding; + } + } + return Math.max(this._padding, filterPadding); + } + /** + * Destroys this text style. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * // Destroy the text style and its textures + * textStyle.destroy({ texture: true, textureSource: true }); + * textStyle.destroy(true); + */ + destroy(options = false) { + var _a, _b, _c, _d; + this.removeAllListeners(); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + if ((_a = this._fill) == null ? void 0 : _a.texture) { + this._fill.texture.destroy(destroyTextureSource); + } + if ((_b = this._originalFill) == null ? void 0 : _b.texture) { + this._originalFill.texture.destroy(destroyTextureSource); + } + if ((_c = this._stroke) == null ? void 0 : _c.texture) { + this._stroke.texture.destroy(destroyTextureSource); + } + if ((_d = this._originalStroke) == null ? void 0 : _d.texture) { + this._originalStroke.texture.destroy(destroyTextureSource); + } + } + this._fill = null; + this._stroke = null; + this.dropShadow = null; + this._originalStroke = null; + this._originalFill = null; + } + _createProxy(value, cb) { + return new Proxy(value, { + set: (target, property, newValue) => { + if (target[property] === newValue) return true; + target[property] = newValue; + cb == null ? void 0 : cb(property, newValue); + this.update(); + return true; + } + }); + } + _isFillStyle(value) { + return (value != null ? value : null) !== null && !(Color.isColorLike(value) || value instanceof FillGradient || value instanceof FillPattern); + } + }; + /** + * Default drop shadow settings used when enabling drop shadows on text. + * These values are used as the base configuration when drop shadows are enabled without specific settings. + * @example + * ```ts + * // Customize default settings globally + * TextStyle.defaultDropShadow.alpha = 0.5; // 50% opacity for all shadows + * TextStyle.defaultDropShadow.blur = 2; // 2px blur for all shadows + * TextStyle.defaultDropShadow.color = 'blue'; // Blue shadows by default + * ``` + */ + _TextStyle.defaultDropShadow = { + alpha: 1, + angle: Math.PI / 6, + blur: 0, + color: "black", + distance: 5 + }; + /** + * Default text style settings used when creating new text objects. + * These values serve as the base configuration and can be customized globally. + * @example + * ```ts + * // Customize default text style globally + * TextStyle.defaultTextStyle.fontSize = 16; + * TextStyle.defaultTextStyle.fill = 0x333333; + * TextStyle.defaultTextStyle.fontFamily = ['Arial', 'Helvetica', 'sans-serif']; + * ``` + */ + _TextStyle.defaultTextStyle = { + align: "left", + breakWords: false, + dropShadow: null, + fill: "black", + fontFamily: "Arial", + fontSize: 26, + fontStyle: "normal", + fontVariant: "normal", + fontWeight: "normal", + leading: 0, + letterSpacing: 0, + lineHeight: 0, + padding: 0, + stroke: null, + textBaseline: "alphabetic", + trim: false, + whiteSpace: "pre", + wordWrap: false, + wordWrapWidth: 100 + }; + let TextStyle = _TextStyle; + function convertV7Tov8Style(style) { + var _a, _b, _c, _d, _e; + const oldStyle = style; + if (typeof oldStyle.dropShadow === "boolean" && oldStyle.dropShadow) { + const defaults = TextStyle.defaultDropShadow; + style.dropShadow = { + alpha: (_a = oldStyle.dropShadowAlpha) != null ? _a : defaults.alpha, + angle: (_b = oldStyle.dropShadowAngle) != null ? _b : defaults.angle, + blur: (_c = oldStyle.dropShadowBlur) != null ? _c : defaults.blur, + color: (_d = oldStyle.dropShadowColor) != null ? _d : defaults.color, + distance: (_e = oldStyle.dropShadowDistance) != null ? _e : defaults.distance + }; + } + if (oldStyle.strokeThickness !== void 0) { + deprecation(v8_0_0, "strokeThickness is now a part of stroke"); + const color = oldStyle.stroke; + let obj = {}; + if (Color.isColorLike(color)) { + obj.color = color; + } else if (color instanceof FillGradient || color instanceof FillPattern) { + obj.fill = color; + } else if (Object.hasOwnProperty.call(color, "color") || Object.hasOwnProperty.call(color, "fill")) { + obj = color; + } else { + throw new Error("Invalid stroke value."); + } + style.stroke = __spreadProps$h(__spreadValues$B({}, obj), { + width: oldStyle.strokeThickness + }); + } + if (Array.isArray(oldStyle.fillGradientStops)) { + deprecation(v8_0_0, "gradient fill is now a fill pattern: `new FillGradient(...)`"); + if (!Array.isArray(oldStyle.fill) || oldStyle.fill.length === 0) { + throw new Error("Invalid fill value. Expected an array of colors for gradient fill."); + } + if (oldStyle.fill.length !== oldStyle.fillGradientStops.length) { + warn("The number of fill colors must match the number of fill gradient stops."); + } + const gradientFill = new FillGradient({ + start: { x: 0, y: 0 }, + end: { x: 0, y: 1 }, + textureSpace: "local" + }); + const fillGradientStops = oldStyle.fillGradientStops.slice(); + const fills = oldStyle.fill.map((color) => Color.shared.setValue(color).toNumber()); + fillGradientStops.forEach((stop, index) => { + gradientFill.addColorStop(stop, fills[index]); + }); + style.fill = { + fill: gradientFill + }; + } + } + + "use strict"; + function updateTextBounds(batchableSprite, text) { + const { texture, bounds } = batchableSprite; + const padding = text._style._getFinalPadding(); + updateQuadBounds(bounds, text._anchor, texture); + const paddingOffset = text._anchor._x * padding * 2; + const paddingOffsetY = text._anchor._y * padding * 2; + bounds.minX -= padding - paddingOffset; + bounds.minY -= padding - paddingOffsetY; + bounds.maxX -= padding - paddingOffset; + bounds.maxY -= padding - paddingOffsetY; + } + + "use strict"; + class BatchableSprite { + constructor() { + this.batcherName = "default"; + this.topology = "triangle-list"; + // batch specific.. + this.attributeSize = 4; + this.indexSize = 6; + this.packAsQuad = true; + this.roundPixels = 0; + this._attributeStart = 0; + // location in the buffer + this._batcher = null; + this._batch = null; + } + get blendMode() { + return this.renderable.groupBlendMode; + } + get color() { + return this.renderable.groupColorAlpha; + } + reset() { + this.renderable = null; + this.texture = null; + this._batcher = null; + this._batch = null; + this.bounds = null; + } + destroy() { + this.reset(); + } + } + + "use strict"; + class BatchableText extends BatchableSprite { + } + + "use strict"; + class CanvasTextPipe { + constructor(renderer) { + this._renderer = renderer; + renderer.runners.resolutionChange.add(this); + this._managedTexts = new GCManagedHash({ + renderer, + type: "renderable", + onUnload: this.onTextUnload.bind(this), + name: "canvasText" + }); + } + resolutionChange() { + for (const key in this._managedTexts.items) { + const text = this._managedTexts.items[key]; + if (text == null ? void 0 : text._autoResolution) text.onViewUpdate(); + } + } + validateRenderable(text) { + const gpuText = this._getGpuText(text); + const newKey = text.styleKey; + if (gpuText.currentKey !== newKey) return true; + return text._didTextUpdate; + } + addRenderable(text, instructionSet) { + const batchableText = this._getGpuText(text); + if (text._didTextUpdate) { + const resolution = text._autoResolution ? this._renderer.resolution : text.resolution; + if (batchableText.currentKey !== text.styleKey || text._resolution !== resolution) { + this._updateGpuText(text); + } + text._didTextUpdate = false; + updateTextBounds(batchableText, text); + } + this._renderer.renderPipes.batch.addToBatch(batchableText, instructionSet); + } + updateRenderable(text) { + const batchableText = this._getGpuText(text); + batchableText._batcher.updateElement(batchableText); + } + _updateGpuText(text) { + const batchableText = this._getGpuText(text); + if (batchableText.texture) { + this._renderer.canvasText.decreaseReferenceCount(batchableText.currentKey); + } + text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution; + batchableText.texture = this._renderer.canvasText.getManagedTexture(text); + batchableText.currentKey = text.styleKey; + } + _getGpuText(text) { + return text._gpuData[this._renderer.uid] || this.initGpuText(text); + } + initGpuText(text) { + const batchableText = new BatchableText(); + batchableText.currentKey = "--"; + batchableText.renderable = text; + batchableText.transform = text.groupTransform; + batchableText.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 }; + batchableText.roundPixels = this._renderer._roundPixels | text._roundPixels; + text._gpuData[this._renderer.uid] = batchableText; + this._managedTexts.add(text); + return batchableText; + } + onTextUnload(text) { + const gpuData = text._gpuData[this._renderer.uid]; + if (!gpuData) return; + const { canvasText } = this._renderer; + const refCount = canvasText.getReferenceCount(gpuData.currentKey); + if (refCount > 0) { + canvasText.decreaseReferenceCount(gpuData.currentKey); + } else if (gpuData.texture) { + canvasText.returnTexture(gpuData.texture); + } + } + destroy() { + this._managedTexts.destroy(); + this._renderer = null; + } + } + /** @ignore */ + CanvasTextPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "text" + }; + + "use strict"; + const tempBounds$2 = new Bounds(); + function getPo2TextureFromSource(image, width, height, resolution) { + const bounds = tempBounds$2; + bounds.minX = 0; + bounds.minY = 0; + bounds.maxX = image.width / resolution | 0; + bounds.maxY = image.height / resolution | 0; + const texture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + resolution, + false + ); + texture.source.uploadMethodId = "image"; + texture.source.resource = image; + texture.source.alphaMode = "premultiply-alpha-on-upload"; + texture.frame.width = width / resolution; + texture.frame.height = height / resolution; + texture.source.emit("update", texture.source); + texture.updateUvs(); + return texture; + } + + "use strict"; + class AbstractTextSystem { + constructor(renderer, retainCanvasContext) { + this._activeTextures = {}; + this._renderer = renderer; + this._retainCanvasContext = retainCanvasContext; + } + getTexture(options, _resolution, _style, _textKey) { + var _a; + if (typeof options === "string") { + deprecation("8.0.0", "CanvasTextSystem.getTexture: Use object TextOptions instead of separate arguments"); + options = { + text: options, + style: _style, + resolution: _resolution + }; + } + if (!(options.style instanceof TextStyle)) { + options.style = new TextStyle(options.style); + } + if (!(options.textureStyle instanceof TextureStyle)) { + options.textureStyle = new TextureStyle(options.textureStyle); + } + if (typeof options.text !== "string") { + options.text = options.text.toString(); + } + const { text, style, textureStyle } = options; + const resolution = (_a = options.resolution) != null ? _a : this._renderer.resolution; + const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({ + text, + style, + resolution + }); + const texture = getPo2TextureFromSource(canvasAndContext.canvas, frame.width, frame.height, resolution); + if (textureStyle) texture.source.style = textureStyle; + if (style.trim) { + frame.pad(style.padding); + texture.frame.copyFrom(frame); + texture.frame.scale(1 / resolution); + texture.updateUvs(); + } + if (style.filters) { + const filteredTexture = this._applyFilters(texture, style.filters); + this.returnTexture(texture); + CanvasTextGenerator.returnCanvasAndContext(canvasAndContext); + return filteredTexture; + } + this._renderer.texture.initSource(texture._source); + if (!this._retainCanvasContext) { + CanvasTextGenerator.returnCanvasAndContext(canvasAndContext); + } + return texture; + } + /** + * Returns a texture that was created wit the above `getTexture` function. + * Handy if you are done with a texture and want to return it to the pool. + * @param texture - The texture to be returned. + */ + returnTexture(texture) { + const source = texture.source; + const resource = source.resource; + if (this._retainCanvasContext && (resource == null ? void 0 : resource.getContext)) { + const context = resource.getContext("2d"); + if (context) { + CanvasTextGenerator.returnCanvasAndContext({ canvas: resource, context }); + } + } + source.resource = null; + source.uploadMethodId = "unknown"; + source.alphaMode = "no-premultiply-alpha"; + TexturePool.returnTexture(texture, true); + } + /** + * Renders text to its canvas, and updates its texture. + * @deprecated since 8.10.0 + */ + renderTextToCanvas() { + deprecation( + "8.10.0", + "CanvasTextSystem.renderTextToCanvas: no longer supported, use CanvasTextSystem.getTexture instead" + ); + } + /** + * Gets or creates a managed texture for a Text object. This method handles texture reuse and reference counting. + * @param text - The Text object that needs a texture + * @returns A Texture instance that represents the rendered text + * @remarks + * This method performs the following: + * 1. Sets the appropriate resolution based on auto-resolution settings + * 2. Checks if a texture already exists for the text's style + * 3. Creates a new texture if needed or returns an existing one + * 4. Manages reference counting for texture reuse + */ + getManagedTexture(text) { + text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution; + const textKey = text.styleKey; + if (this._activeTextures[textKey]) { + this._increaseReferenceCount(textKey); + return this._activeTextures[textKey].texture; + } + const texture = this.getTexture({ + text: text.text, + style: text.style, + resolution: text._resolution, + textureStyle: text.textureStyle + }); + this._activeTextures[textKey] = { + texture, + usageCount: 1 + }; + return texture; + } + /** + * Decreases the reference count for a texture associated with a text key. + * When the reference count reaches zero, the texture is returned to the pool. + * @param textKey - The unique key identifying the text style configuration + * @remarks + * This method is crucial for memory management, ensuring textures are properly + * cleaned up when they are no longer needed by any Text instances. + */ + decreaseReferenceCount(textKey) { + const activeTexture = this._activeTextures[textKey]; + if (!activeTexture) return; + activeTexture.usageCount--; + if (activeTexture.usageCount === 0) { + this.returnTexture(activeTexture.texture); + this._activeTextures[textKey] = null; + } + } + /** + * Gets the current reference count for a texture associated with a text key. + * @param textKey - The unique key identifying the text style configuration + * @returns The number of Text instances currently using this texture + */ + getReferenceCount(textKey) { + var _a, _b; + return (_b = (_a = this._activeTextures[textKey]) == null ? void 0 : _a.usageCount) != null ? _b : 0; + } + _increaseReferenceCount(textKey) { + this._activeTextures[textKey].usageCount++; + } + /** + * Applies the specified filters to the given texture. + * + * This method takes a texture and a list of filters, applies the filters to the texture, + * and returns the resulting texture. It also ensures that the alpha mode of the resulting + * texture is set to 'premultiplied-alpha'. + * @param {Texture} texture - The texture to which the filters will be applied. + * @param {Filter[]} filters - The filters to apply to the texture. + * @returns {Texture} The resulting texture after all filters have been applied. + */ + _applyFilters(texture, filters) { + const currentRenderTarget = this._renderer.renderTarget.renderTarget; + const resultTexture = this._renderer.filter.generateFilteredTexture({ + texture, + filters + }); + this._renderer.renderTarget.bind(currentRenderTarget, false); + return resultTexture; + } + destroy() { + this._renderer = null; + for (const key in this._activeTextures) { + if (this._activeTextures[key]) this.returnTexture(this._activeTextures[key].texture); + } + this._activeTextures = null; + } + } + + "use strict"; + class CanvasRendererTextSystem extends AbstractTextSystem { + constructor(renderer) { + super(renderer, true); + } + } + /** @ignore */ + CanvasRendererTextSystem.extension = { + type: [ + ExtensionType.CanvasSystem + ], + name: "canvasText" + }; + + "use strict"; + class CanvasTextSystem extends AbstractTextSystem { + constructor(renderer) { + super(renderer, false); + } + } + /** @ignore */ + CanvasTextSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "canvasText" + }; + + "use strict"; + extensions.add(CanvasRendererTextSystem); + extensions.add(CanvasTextSystem); + extensions.add(CanvasTextPipe); + + "use strict"; + class Text extends AbstractText { + constructor(...args) { + const options = ensureTextOptions(args, "Text"); + super(options, TextStyle); + /** @internal */ + this.renderPipeId = "text"; + if (options.textureStyle) { + this.textureStyle = options.textureStyle instanceof TextureStyle ? options.textureStyle : new TextureStyle(options.textureStyle); + } + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const anchor = this._anchor; + let width = 0; + let height = 0; + if (this._style.trim) { + const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({ + text: this.text, + style: this._style, + resolution: 1 + }); + CanvasTextGenerator.returnCanvasAndContext(canvasAndContext); + width = frame.width; + height = frame.height; + } else { + const canvasMeasurement = CanvasTextMetrics.measureText( + this._text, + this._style + ); + width = canvasMeasurement.width; + height = canvasMeasurement.height; + } + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + } + + "use strict"; + class PrepareQueue extends PrepareBase { + /** + * Resolve the given resource type and return an item for the queue + * @param source + * @param queue + */ + resolveQueueItem(source, queue) { + if (source instanceof Container) { + this.resolveContainerQueueItem(source, queue); + } else if (source instanceof TextureSource || source instanceof Texture) { + queue.push(source.source); + } else if (source instanceof GraphicsContext) { + queue.push(source); + } + return null; + } + /** + * Resolve the given container and return an item for the queue + * @param container + * @param queue + */ + resolveContainerQueueItem(container, queue) { + if (container instanceof Sprite || container instanceof TilingSprite || container instanceof Mesh) { + queue.push(container.texture.source); + } else if (container instanceof Text) { + queue.push(container); + } else if (container instanceof Graphics) { + queue.push(container.context); + } else if (container instanceof AnimatedSprite) { + container.textures.forEach((textureOrFrame) => { + if (textureOrFrame.source) { + queue.push(textureOrFrame.source); + } else { + queue.push(textureOrFrame.texture.source); + } + }); + } + } + /** + * Resolve the given graphics context and return an item for the queue + * @param graphicsContext + */ + resolveGraphicsContextQueueItem(graphicsContext) { + this.renderer.graphicsContext.getGpuContext(graphicsContext); + const { instructions } = graphicsContext; + for (const instruction of instructions) { + if (instruction.action === "texture") { + const { image } = instruction.data; + return image.source; + } else if (instruction.action === "fill") { + const { texture } = instruction.data.style; + return texture.source; + } + } + return null; + } + } + + "use strict"; + class AbstractBitmapFont extends EventEmitter { + constructor() { + super(...arguments); + /** The map of characters by character code. */ + this.chars = /* @__PURE__ */ Object.create(null); + /** + * The line-height of the font face in pixels. + * @type {number} + */ + this.lineHeight = 0; + /** + * The name of the font face + * @type {string} + */ + this.fontFamily = ""; + /** The metrics of the font face. */ + this.fontMetrics = { fontSize: 0, ascent: 0, descent: 0 }; + /** + * The offset of the font face from the baseline. + * @type {number} + */ + this.baseLineOffset = 0; + /** The range and type of the distance field for this font. */ + this.distanceField = { type: "none", range: 0 }; + /** The map of base page textures (i.e., sheets of glyphs). */ + this.pages = []; + /** should the fill for this font be applied as a tint to the text. */ + this.applyFillAsTint = true; + /** The size of the font face in pixels. */ + this.baseMeasurementFontSize = 100; + this.baseRenderedFontSize = 100; + } + /** + * The name of the font face. + * @deprecated since 8.0.0 Use `fontFamily` instead. + */ + get font() { + deprecation(v8_0_0, "BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead."); + return this.fontFamily; + } + /** + * The map of base page textures (i.e., sheets of glyphs). + * @deprecated since 8.0.0 Use `pages` instead. + */ + get pageTextures() { + deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."); + return this.pages; + } + /** + * The size of the font face in pixels. + * @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead. + */ + get size() { + deprecation(v8_0_0, "BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead."); + return this.fontMetrics.fontSize; + } + /** + * The kind of distance field for this font or "none". + * @deprecated since 8.0.0 Use `distanceField.type` instead. + */ + get distanceFieldRange() { + deprecation(v8_0_0, "BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead."); + return this.distanceField.range; + } + /** + * The range of the distance field in pixels. + * @deprecated since 8.0.0 Use `distanceField.range` instead. + */ + get distanceFieldType() { + deprecation(v8_0_0, "BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead."); + return this.distanceField.type; + } + destroy(destroyTextures = false) { + var _a; + this.emit("destroy", this); + this.removeAllListeners(); + for (const i in this.chars) { + (_a = this.chars[i].texture) == null ? void 0 : _a.destroy(); + } + this.chars = null; + if (destroyTextures) { + this.pages.forEach((page) => page.texture.destroy(true)); + this.pages = null; + } + } + } + + "use strict"; + var __defProp$A = Object.defineProperty; + var __getOwnPropSymbols$C = Object.getOwnPropertySymbols; + var __hasOwnProp$C = Object.prototype.hasOwnProperty; + var __propIsEnum$C = Object.prototype.propertyIsEnumerable; + var __defNormalProp$A = (obj, key, value) => key in obj ? __defProp$A(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$A = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$C.call(b, prop)) + __defNormalProp$A(a, prop, b[prop]); + if (__getOwnPropSymbols$C) + for (var prop of __getOwnPropSymbols$C(b)) { + if (__propIsEnum$C.call(b, prop)) + __defNormalProp$A(a, prop, b[prop]); + } + return a; + }; + const _DynamicBitmapFont = class _DynamicBitmapFont extends AbstractBitmapFont { + /** + * @param options - The options for the dynamic bitmap font. + */ + constructor(options) { + var _a, _b, _c; + super(); + /** + * this is a resolution modifier for the font size.. + * texture resolution will also be used to scale texture according to its font size also + */ + this.resolution = 1; + /** The pages of the font. */ + this.pages = []; + this._padding = 0; + this._measureCache = /* @__PURE__ */ Object.create(null); + this._currentChars = []; + this._currentX = 0; + this._currentY = 0; + this._currentMaxCharHeight = 0; + this._currentPageIndex = -1; + this._skipKerning = false; + const dynamicOptions = __spreadValues$A(__spreadValues$A({}, _DynamicBitmapFont.defaultOptions), options); + this._textureSize = dynamicOptions.textureSize; + this._mipmap = dynamicOptions.mipmap; + const style = dynamicOptions.style.clone(); + if (dynamicOptions.overrideFill) { + style._fill.color = 16777215; + style._fill.alpha = 1; + style._fill.texture = Texture.WHITE; + style._fill.fill = null; + } + this.applyFillAsTint = dynamicOptions.overrideFill; + const requestedFontSize = style.fontSize; + style.fontSize = this.baseMeasurementFontSize; + const font = fontStringFromTextStyle(style); + if (dynamicOptions.overrideSize) { + if (style._stroke) { + style._stroke.width *= this.baseRenderedFontSize / requestedFontSize; + } + } else { + style.fontSize = this.baseRenderedFontSize = requestedFontSize; + } + this._style = style; + this._skipKerning = (_a = dynamicOptions.skipKerning) != null ? _a : false; + this.resolution = (_b = dynamicOptions.resolution) != null ? _b : 1; + this._padding = (_c = dynamicOptions.padding) != null ? _c : 4; + if (dynamicOptions.textureStyle) { + this._textureStyle = dynamicOptions.textureStyle instanceof TextureStyle ? dynamicOptions.textureStyle : new TextureStyle(dynamicOptions.textureStyle); + } + this.fontMetrics = CanvasTextMetrics.measureFont(font); + this.lineHeight = style.lineHeight || this.fontMetrics.fontSize || style.fontSize; + } + ensureCharacters(chars) { + var _a, _b, _c, _d; + const charList = CanvasTextMetrics.graphemeSegmenter(chars).filter((char) => !this._currentChars.includes(char)).filter((char, index, self) => self.indexOf(char) === index); + if (!charList.length) return; + this._currentChars = [...this._currentChars, ...charList]; + let pageData; + if (this._currentPageIndex === -1) { + pageData = this._nextPage(); + } else { + pageData = this.pages[this._currentPageIndex]; + } + let { canvas, context } = pageData.canvasAndContext; + let textureSource = pageData.texture.source; + const style = this._style; + let currentX = this._currentX; + let currentY = this._currentY; + let currentMaxCharHeight = this._currentMaxCharHeight; + const fontScale = this.baseRenderedFontSize / this.baseMeasurementFontSize; + const padding = this._padding * fontScale; + let skipTexture = false; + const maxTextureWidth = canvas.width / this.resolution; + const maxTextureHeight = canvas.height / this.resolution; + for (let i = 0; i < charList.length; i++) { + const char = charList[i]; + const metrics = CanvasTextMetrics.measureText(char, style, canvas, false); + metrics.lineHeight = metrics.height; + const width = metrics.width * fontScale; + const textureGlyphWidth = Math.ceil((style.fontStyle === "italic" ? 2 : 1) * width); + const height = metrics.height * fontScale; + const paddedWidth = textureGlyphWidth + padding * 2; + const paddedHeight = height + padding * 2; + skipTexture = false; + if (char !== "\n" && char !== "\r" && char !== " " && char !== " ") { + skipTexture = true; + currentMaxCharHeight = Math.ceil(Math.max(paddedHeight, currentMaxCharHeight)); + } + if (currentX + paddedWidth > maxTextureWidth) { + currentY += currentMaxCharHeight; + currentMaxCharHeight = paddedHeight; + currentX = 0; + if (currentY + currentMaxCharHeight > maxTextureHeight) { + textureSource.update(); + const pageData2 = this._nextPage(); + canvas = pageData2.canvasAndContext.canvas; + context = pageData2.canvasAndContext.context; + textureSource = pageData2.texture.source; + currentX = 0; + currentY = 0; + currentMaxCharHeight = 0; + } + } + const xAdvance = width / fontScale - ((_b = (_a = style.dropShadow) == null ? void 0 : _a.distance) != null ? _b : 0) - ((_d = (_c = style._stroke) == null ? void 0 : _c.width) != null ? _d : 0); + this.chars[char] = { + id: char.codePointAt(0), + xOffset: -this._padding, + yOffset: -this._padding, + xAdvance, + kerning: {} + }; + if (skipTexture) { + this._drawGlyph( + context, + metrics, + currentX + padding, + currentY + padding, + fontScale, + style + ); + const px = textureSource.width * fontScale; + const py = textureSource.height * fontScale; + const frame = new Rectangle( + currentX / px * textureSource.width, + currentY / py * textureSource.height, + paddedWidth / px * textureSource.width, + paddedHeight / py * textureSource.height + ); + this.chars[char].texture = new Texture({ + source: textureSource, + frame + }); + currentX += Math.ceil(paddedWidth); + } + } + textureSource.update(); + this._currentX = currentX; + this._currentY = currentY; + this._currentMaxCharHeight = currentMaxCharHeight; + this._skipKerning && this._applyKerning(charList, context); + } + /** + * @deprecated since 8.0.0 + * The map of base page textures (i.e., sheets of glyphs). + */ + get pageTextures() { + deprecation(v8_0_0, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."); + return this.pages; + } + _applyKerning(newChars, context) { + const measureCache = this._measureCache; + for (let i = 0; i < newChars.length; i++) { + const first = newChars[i]; + for (let j = 0; j < this._currentChars.length; j++) { + const second = this._currentChars[j]; + let c1 = measureCache[first]; + if (!c1) c1 = measureCache[first] = context.measureText(first).width; + let c2 = measureCache[second]; + if (!c2) c2 = measureCache[second] = context.measureText(second).width; + let total = context.measureText(first + second).width; + let amount = total - (c1 + c2); + if (amount) { + this.chars[first].kerning[second] = amount; + } + total = context.measureText(first + second).width; + amount = total - (c1 + c2); + if (amount) { + this.chars[second].kerning[first] = amount; + } + } + } + } + _nextPage() { + this._currentPageIndex++; + const textureResolution = this.resolution; + const canvasAndContext = CanvasPool.getOptimalCanvasAndContext( + this._textureSize, + this._textureSize, + textureResolution + ); + this._setupContext(canvasAndContext.context, this._style, textureResolution); + const resolution = textureResolution * (this.baseRenderedFontSize / this.baseMeasurementFontSize); + const texture = new Texture({ + source: new ImageSource({ + resource: canvasAndContext.canvas, + resolution, + alphaMode: "premultiply-alpha-on-upload", + autoGenerateMipmaps: this._mipmap + }) + }); + if (this._textureStyle) { + texture.source.style = this._textureStyle; + } + const pageData = { + canvasAndContext, + texture + }; + this.pages[this._currentPageIndex] = pageData; + return pageData; + } + // canvas style! + _setupContext(context, style, resolution) { + var _a; + style.fontSize = this.baseRenderedFontSize; + context.scale(resolution, resolution); + context.font = fontStringFromTextStyle(style); + style.fontSize = this.baseMeasurementFontSize; + context.textBaseline = style.textBaseline; + const stroke = style._stroke; + const strokeThickness = (_a = stroke == null ? void 0 : stroke.width) != null ? _a : 0; + if (stroke) { + context.lineWidth = strokeThickness; + context.lineJoin = stroke.join; + context.miterLimit = stroke.miterLimit; + context.strokeStyle = getCanvasFillStyle(stroke, context); + } + if (style._fill) { + context.fillStyle = getCanvasFillStyle(style._fill, context); + } + if (style.dropShadow) { + const shadowOptions = style.dropShadow; + const rgb = Color.shared.setValue(shadowOptions.color).toArray(); + const dropShadowBlur = shadowOptions.blur * resolution; + const dropShadowDistance = shadowOptions.distance * resolution; + context.shadowColor = `rgba(${rgb[0] * 255},${rgb[1] * 255},${rgb[2] * 255},${shadowOptions.alpha})`; + context.shadowBlur = dropShadowBlur; + context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance; + context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance; + } else { + context.shadowColor = "black"; + context.shadowBlur = 0; + context.shadowOffsetX = 0; + context.shadowOffsetY = 0; + } + } + _drawGlyph(context, metrics, x, y, fontScale, style) { + var _a; + const char = metrics.text; + const fontProperties = metrics.fontProperties; + const stroke = style._stroke; + const strokeThickness = ((_a = stroke == null ? void 0 : stroke.width) != null ? _a : 0) * fontScale; + const tx = x + strokeThickness / 2; + const ty = y - strokeThickness / 2; + const descent = fontProperties.descent * fontScale; + const lineHeight = metrics.lineHeight * fontScale; + let removeShadow = false; + if (style.stroke && strokeThickness) { + removeShadow = true; + context.strokeText(char, tx, ty + lineHeight - descent); + } + const { shadowBlur, shadowOffsetX, shadowOffsetY } = context; + if (style._fill) { + if (removeShadow) { + context.shadowBlur = 0; + context.shadowOffsetX = 0; + context.shadowOffsetY = 0; + } + context.fillText(char, tx, ty + lineHeight - descent); + } + if (removeShadow) { + context.shadowBlur = shadowBlur; + context.shadowOffsetX = shadowOffsetX; + context.shadowOffsetY = shadowOffsetY; + } + } + destroy() { + super.destroy(); + for (let i = 0; i < this.pages.length; i++) { + const { canvasAndContext, texture } = this.pages[i]; + CanvasPool.returnCanvasAndContext(canvasAndContext); + texture.destroy(true); + } + this.pages = null; + } + }; + _DynamicBitmapFont.defaultOptions = { + textureSize: 512, + style: new TextStyle(), + mipmap: true + }; + let DynamicBitmapFont = _DynamicBitmapFont; + + "use strict"; + function getBitmapTextLayout(chars, style, font, trimEnd) { + const layoutData = { + width: 0, + height: 0, + offsetY: 0, + scale: style.fontSize / font.baseMeasurementFontSize, + lines: [{ + width: 0, + charPositions: [], + spaceWidth: 0, + spacesIndex: [], + chars: [] + }] + }; + layoutData.offsetY = font.baseLineOffset; + let currentLine = layoutData.lines[0]; + let previousChar = null; + let firstWord = true; + const currentWord = { + spaceWord: false, + width: 0, + start: 0, + index: 0, + // use index to not modify the array as we use it a lot! + positions: [], + chars: [] + }; + const scale = font.baseMeasurementFontSize / style.fontSize; + const adjustedLetterSpacing = style.letterSpacing * scale; + const adjustedWordWrapWidth = style.wordWrapWidth * scale; + const adjustedLineHeight = style.lineHeight ? style.lineHeight * scale : font.lineHeight; + const breakWords = style.wordWrap && style.breakWords; + const nextWord = (word) => { + const start = currentLine.width; + for (let j = 0; j < currentWord.index; j++) { + const position = word.positions[j]; + currentLine.chars.push(word.chars[j]); + currentLine.charPositions.push(position + start); + } + currentLine.width += word.width; + firstWord = false; + currentWord.width = 0; + currentWord.index = 0; + currentWord.chars.length = 0; + }; + const nextLine = () => { + let index = currentLine.chars.length - 1; + if (trimEnd) { + let lastChar = currentLine.chars[index]; + while (lastChar === " ") { + currentLine.width -= font.chars[lastChar].xAdvance; + lastChar = currentLine.chars[--index]; + } + } + layoutData.width = Math.max(layoutData.width, currentLine.width); + currentLine = { + width: 0, + charPositions: [], + chars: [], + spaceWidth: 0, + spacesIndex: [] + }; + firstWord = true; + layoutData.lines.push(currentLine); + layoutData.height += adjustedLineHeight; + }; + const checkIsOverflow = (lineWidth) => lineWidth - adjustedLetterSpacing > adjustedWordWrapWidth; + for (let i = 0; i < chars.length + 1; i++) { + let char; + const isEnd = i === chars.length; + if (!isEnd) { + char = chars[i]; + } + const charData = font.chars[char] || font.chars[" "]; + const isSpace = /(?:\s)/.test(char); + const isWordBreak = isSpace || char === "\r" || char === "\n" || isEnd; + if (isWordBreak) { + const addWordToNextLine = !firstWord && style.wordWrap && checkIsOverflow(currentLine.width + currentWord.width); + if (addWordToNextLine) { + nextLine(); + nextWord(currentWord); + if (!isEnd) { + currentLine.charPositions.push(0); + } + } else { + currentWord.start = currentLine.width; + nextWord(currentWord); + if (!isEnd) { + currentLine.charPositions.push(0); + } + } + if (char === "\r" || char === "\n") { + nextLine(); + } else if (!isEnd) { + const spaceWidth = charData.xAdvance + (charData.kerning[previousChar] || 0) + adjustedLetterSpacing; + currentLine.width += spaceWidth; + currentLine.spaceWidth = spaceWidth; + currentLine.spacesIndex.push(currentLine.charPositions.length); + currentLine.chars.push(char); + } + } else { + const kerning = charData.kerning[previousChar] || 0; + const nextCharWidth = charData.xAdvance + kerning + adjustedLetterSpacing; + const addWordToNextLine = breakWords && checkIsOverflow(currentLine.width + currentWord.width + nextCharWidth); + if (addWordToNextLine) { + nextWord(currentWord); + nextLine(); + } + currentWord.positions[currentWord.index++] = currentWord.width + kerning; + currentWord.chars.push(char); + currentWord.width += nextCharWidth; + } + previousChar = char; + } + nextLine(); + if (style.align === "center") { + alignCenter(layoutData); + } else if (style.align === "right") { + alignRight(layoutData); + } else if (style.align === "justify") { + alignJustify(layoutData); + } + return layoutData; + } + function alignCenter(measurementData) { + for (let i = 0; i < measurementData.lines.length; i++) { + const line = measurementData.lines[i]; + const offset = measurementData.width / 2 - line.width / 2; + for (let j = 0; j < line.charPositions.length; j++) { + line.charPositions[j] += offset; + } + } + } + function alignRight(measurementData) { + for (let i = 0; i < measurementData.lines.length; i++) { + const line = measurementData.lines[i]; + const offset = measurementData.width - line.width; + for (let j = 0; j < line.charPositions.length; j++) { + line.charPositions[j] += offset; + } + } + } + function alignJustify(measurementData) { + const width = measurementData.width; + for (let i = 0; i < measurementData.lines.length; i++) { + const line = measurementData.lines[i]; + let indy = 0; + let spaceIndex = line.spacesIndex[indy++]; + let offset = 0; + const totalSpaces = line.spacesIndex.length; + const newSpaceWidth = (width - line.width) / totalSpaces; + const spaceWidth = newSpaceWidth; + for (let j = 0; j < line.charPositions.length; j++) { + if (j === spaceIndex) { + spaceIndex = line.spacesIndex[indy++]; + offset += spaceWidth; + } + line.charPositions[j] += offset; + } + } + } + + "use strict"; + function resolveCharacters(chars) { + if (chars === "") { + return []; + } + if (typeof chars === "string") { + chars = [chars]; + } + const result = []; + for (let i = 0, j = chars.length; i < j; i++) { + const item = chars[i]; + if (Array.isArray(item)) { + if (item.length !== 2) { + throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${item.length}.`); + } + if (item[0].length === 0 || item[1].length === 0) { + throw new Error("[BitmapFont]: Invalid character delimiter."); + } + const startCode = item[0].charCodeAt(0); + const endCode = item[1].charCodeAt(0); + if (endCode < startCode) { + throw new Error("[BitmapFont]: Invalid character range."); + } + for (let i2 = startCode, j2 = endCode; i2 <= j2; i2++) { + result.push(String.fromCharCode(i2)); + } + } else { + result.push(...Array.from(item)); + } + } + if (result.length === 0) { + throw new Error("[BitmapFont]: Empty set when resolving characters."); + } + return result; + } + + "use strict"; + var __defProp$z = Object.defineProperty; + var __getOwnPropSymbols$B = Object.getOwnPropertySymbols; + var __hasOwnProp$B = Object.prototype.hasOwnProperty; + var __propIsEnum$B = Object.prototype.propertyIsEnumerable; + var __defNormalProp$z = (obj, key, value) => key in obj ? __defProp$z(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$z = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$B.call(b, prop)) + __defNormalProp$z(a, prop, b[prop]); + if (__getOwnPropSymbols$B) + for (var prop of __getOwnPropSymbols$B(b)) { + if (__propIsEnum$B.call(b, prop)) + __defNormalProp$z(a, prop, b[prop]); + } + return a; + }; + let fontCount = 0; + class BitmapFontManagerClass { + constructor() { + /** + * This character set includes all the letters in the alphabet (both lower- and upper- case). + * @type {string[][]} + * @example + * BitmapFont.from('ExampleFont', style, { chars: BitmapFont.ALPHA }) + */ + this.ALPHA = [["a", "z"], ["A", "Z"], " "]; + /** + * This character set includes all decimal digits (from 0 to 9). + * @type {string[][]} + * @example + * BitmapFont.from('ExampleFont', style, { chars: BitmapFont.NUMERIC }) + */ + this.NUMERIC = [["0", "9"]]; + /** + * This character set is the union of `BitmapFont.ALPHA` and `BitmapFont.NUMERIC`. + * @type {string[][]} + */ + this.ALPHANUMERIC = [["a", "z"], ["A", "Z"], ["0", "9"], " "]; + /** + * This character set consists of all the ASCII table. + * @type {string[][]} + * @see http://www.asciitable.com/ + */ + this.ASCII = [[" ", "~"]]; + /** Default options for installing a new BitmapFont. */ + this.defaultOptions = { + chars: this.ALPHANUMERIC, + resolution: 1, + padding: 4, + skipKerning: false, + textureStyle: null + }; + /** Cache for measured text layouts to avoid recalculating them multiple times. */ + this.measureCache = lru(1e3); + } + /** + * Get a font for the specified text and style. + * @param text - The text to get the font for + * @param style - The style to use + */ + getFont(text, style) { + var _a; + let fontFamilyKey = `${style.fontFamily}-bitmap`; + let overrideFill = true; + if (style._fill.fill && !style._stroke) { + fontFamilyKey += style._fill.fill.styleKey; + overrideFill = false; + } else if (style._stroke || style.dropShadow) { + fontFamilyKey = `${style.styleKey}-bitmap`; + overrideFill = false; + } + if (!Cache.has(fontFamilyKey)) { + const styleCopy = Object.create(style); + styleCopy._lineHeight = 0; + const fnt = new DynamicBitmapFont(__spreadValues$z({ + style: styleCopy, + overrideFill, + overrideSize: true + }, this.defaultOptions)); + fontCount++; + if (fontCount > 50) { + warn("BitmapText", `You have dynamically created ${fontCount} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``); + } + fnt.once("destroy", () => { + fontCount--; + Cache.remove(fontFamilyKey); + }); + Cache.set( + fontFamilyKey, + fnt + ); + } + const dynamicFont = Cache.get(fontFamilyKey); + (_a = dynamicFont.ensureCharacters) == null ? void 0 : _a.call(dynamicFont, text); + return dynamicFont; + } + /** + * Get the layout of a text for the specified style. + * @param text - The text to get the layout for + * @param style - The style to use + * @param trimEnd - Whether to ignore whitespaces at the end of each line + */ + getLayout(text, style, trimEnd = true) { + const bitmapFont = this.getFont(text, style); + const id = `${text}-${style.styleKey}-${trimEnd}`; + if (this.measureCache.has(id)) { + return this.measureCache.get(id); + } + const segments = CanvasTextMetrics.graphemeSegmenter(text); + const layoutData = getBitmapTextLayout(segments, style, bitmapFont, trimEnd); + this.measureCache.set(id, layoutData); + return layoutData; + } + /** + * Measure the text using the specified style. + * @param text - The text to measure + * @param style - The style to use + * @param trimEnd - Whether to ignore whitespaces at the end of each line + */ + measureText(text, style, trimEnd = true) { + return this.getLayout(text, style, trimEnd); + } + // eslint-disable-next-line max-len + install(...args) { + var _a, _b, _c, _d, _e; + let options = args[0]; + if (typeof options === "string") { + options = { + name: options, + style: args[1], + chars: (_a = args[2]) == null ? void 0 : _a.chars, + resolution: (_b = args[2]) == null ? void 0 : _b.resolution, + padding: (_c = args[2]) == null ? void 0 : _c.padding, + skipKerning: (_d = args[2]) == null ? void 0 : _d.skipKerning + }; + deprecation(v8_0_0, "BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})"); + } + const name = options == null ? void 0 : options.name; + if (!name) { + throw new Error("[BitmapFontManager] Property `name` is required."); + } + options = __spreadValues$z(__spreadValues$z({}, this.defaultOptions), options); + const textStyle = options.style; + const style = textStyle instanceof TextStyle ? textStyle : new TextStyle(textStyle); + const overrideFill = (_e = options.dynamicFill) != null ? _e : this._canUseTintForStyle(style); + const font = new DynamicBitmapFont({ + style, + overrideFill, + skipKerning: options.skipKerning, + padding: options.padding, + resolution: options.resolution, + overrideSize: false, + textureStyle: options.textureStyle + }); + const flatChars = resolveCharacters(options.chars); + font.ensureCharacters(flatChars.join("")); + Cache.set(`${name}-bitmap`, font); + font.once("destroy", () => Cache.remove(`${name}-bitmap`)); + return font; + } + /** + * Uninstalls a bitmap font from the cache. + * @param {string} name - The name of the bitmap font to uninstall. + */ + uninstall(name) { + const cacheKey = `${name}-bitmap`; + const font = Cache.get(cacheKey); + if (font) { + font.destroy(); + } + } + /** + * Determines if a style can use tinting instead of baking colors into the bitmap. + * Tinting is more efficient as it allows reusing the same bitmap with different colors. + * @param style - The text style to evaluate + * @returns true if the style can use tinting, false if colors must be baked in + * @private + */ + _canUseTintForStyle(style) { + return !style._stroke && (!style.dropShadow || style.dropShadow.color === 0) && !style._fill.fill && style._fill.color === 16777215; + } + } + const BitmapFontManager = new BitmapFontManagerClass(); + + "use strict"; + class BitmapTextGraphics extends Graphics { + destroy() { + if (this.context.customShader) { + this.context.customShader.destroy(); + } + super.destroy(); + } + } + class AbstractBitmapTextPipe { + constructor(renderer) { + this._renderer = renderer; + this._managedBitmapTexts = new GCManagedHash({ renderer, type: "renderable", priority: -2, name: "bitmapText" }); + } + validateRenderable(bitmapText) { + const graphicsRenderable = this._getGpuBitmapText(bitmapText); + return this._renderer.renderPipes.graphics.validateRenderable(graphicsRenderable); + } + addRenderable(bitmapText, instructionSet) { + const graphicsRenderable = this._getGpuBitmapText(bitmapText); + syncWithProxy(bitmapText, graphicsRenderable); + if (bitmapText._didTextUpdate) { + bitmapText._didTextUpdate = false; + this._updateContext(bitmapText, graphicsRenderable); + } + this._renderer.renderPipes.graphics.addRenderable(graphicsRenderable, instructionSet); + if (graphicsRenderable.context.customShader) { + this._updateDistanceField(bitmapText); + } + } + updateRenderable(bitmapText) { + const graphicsRenderable = this._getGpuBitmapText(bitmapText); + syncWithProxy(bitmapText, graphicsRenderable); + this._renderer.renderPipes.graphics.updateRenderable(graphicsRenderable); + if (graphicsRenderable.context.customShader) { + this._updateDistanceField(bitmapText); + } + } + _updateContext(bitmapText, proxyGraphics) { + const { context } = proxyGraphics; + const bitmapFont = BitmapFontManager.getFont(bitmapText.text, bitmapText._style); + context.clear(); + if (bitmapFont.distanceField.type !== "none") { + const sdfShader = this.getSdfShader(); + if (sdfShader) { + if (!context.customShader) { + context.customShader = sdfShader; + } + } + } + const chars = CanvasTextMetrics.graphemeSegmenter(bitmapText.text); + const style = bitmapText._style; + let currentY = bitmapFont.baseLineOffset; + const bitmapTextLayout = getBitmapTextLayout(chars, style, bitmapFont, true); + const padding = style.padding; + const scale = bitmapTextLayout.scale; + let tx = bitmapTextLayout.width; + let ty = bitmapTextLayout.height + bitmapTextLayout.offsetY; + if (style._stroke) { + tx += style._stroke.width / scale; + ty += style._stroke.width / scale; + } + context.translate(-bitmapText._anchor._x * tx - padding, -bitmapText._anchor._y * ty - padding).scale(scale, scale); + const tint = bitmapFont.applyFillAsTint ? style._fill.color : 16777215; + let fontSize = bitmapFont.fontMetrics.fontSize; + let lineHeight = bitmapFont.lineHeight; + if (style.lineHeight) { + fontSize = style.fontSize / scale; + lineHeight = style.lineHeight / scale; + } + let linePositionYShift = (lineHeight - fontSize) / 2; + if (linePositionYShift - bitmapFont.baseLineOffset < 0) { + linePositionYShift = 0; + } + for (let i = 0; i < bitmapTextLayout.lines.length; i++) { + const line = bitmapTextLayout.lines[i]; + for (let j = 0; j < line.charPositions.length; j++) { + const char = line.chars[j]; + const charData = bitmapFont.chars[char]; + if (charData == null ? void 0 : charData.texture) { + const texture = charData.texture; + context.texture( + texture, + tint, + Math.round(line.charPositions[j] + charData.xOffset), + Math.round(currentY + charData.yOffset + linePositionYShift), + texture.orig.width, + texture.orig.height + ); + } + } + currentY += lineHeight; + } + } + _getGpuBitmapText(bitmapText) { + return bitmapText._gpuData[this._renderer.uid] || this.initGpuText(bitmapText); + } + initGpuText(bitmapText) { + const proxyRenderable = new BitmapTextGraphics(); + bitmapText._gpuData[this._renderer.uid] = proxyRenderable; + this._updateContext(bitmapText, proxyRenderable); + this._managedBitmapTexts.add(bitmapText); + return proxyRenderable; + } + _updateDistanceField(bitmapText) { + const context = this._getGpuBitmapText(bitmapText).context; + const fontFamily = bitmapText._style.fontFamily; + const dynamicFont = Cache.get(`${fontFamily}-bitmap`); + const { a, b, c, d } = bitmapText.groupTransform; + const dx = Math.sqrt(a * a + b * b); + const dy = Math.sqrt(c * c + d * d); + const worldScale = (Math.abs(dx) + Math.abs(dy)) / 2; + const fontScale = dynamicFont.baseRenderedFontSize / bitmapText._style.fontSize; + const distance = worldScale * dynamicFont.distanceField.range * (1 / fontScale); + context.customShader.resources.localUniforms.uniforms.uDistance = distance; + } + destroy() { + this._managedBitmapTexts.destroy(); + this._renderer = null; + this._managedBitmapTexts = null; + } + } + function syncWithProxy(container, proxy) { + proxy.groupTransform = container.groupTransform; + proxy.groupColorAlpha = container.groupColorAlpha; + proxy.groupColor = container.groupColor; + proxy.groupBlendMode = container.groupBlendMode; + proxy.globalDisplayStatus = container.globalDisplayStatus; + proxy.groupTransform = container.groupTransform; + proxy.localDisplayStatus = container.localDisplayStatus; + proxy.groupAlpha = container.groupAlpha; + proxy._roundPixels = container._roundPixels; + } + + "use strict"; + class CanvasBitmapTextPipe extends AbstractBitmapTextPipe { + getSdfShader() { + return null; + } + } + /** @ignore */ + CanvasBitmapTextPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "bitmapText" + }; + + "use strict"; + const localUniformMSDFBit = { + name: "local-uniform-msdf-bit", + vertex: { + header: ( + /* wgsl */ + ` + struct LocalUniforms { + uColor:vec4, + uTransformMatrix:mat3x3, + uDistance: f32, + uRound:f32, + } + + @group(2) @binding(0) var localUniforms : LocalUniforms; + ` + ), + main: ( + /* wgsl */ + ` + vColor *= localUniforms.uColor; + modelMatrix *= localUniforms.uTransformMatrix; + ` + ), + end: ( + /* wgsl */ + ` + if(localUniforms.uRound == 1) + { + vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); + } + ` + ) + }, + fragment: { + header: ( + /* wgsl */ + ` + struct LocalUniforms { + uColor:vec4, + uTransformMatrix:mat3x3, + uDistance: f32 + } + + @group(2) @binding(0) var localUniforms : LocalUniforms; + ` + ), + main: ( + /* wgsl */ + ` + outColor = vec4(calculateMSDFAlpha(outColor, vColor, localUniforms.uDistance)); + ` + ) + } + }; + const localUniformMSDFBitGl = { + name: "local-uniform-msdf-bit", + vertex: { + header: ( + /* glsl */ + ` + uniform mat3 uTransformMatrix; + uniform vec4 uColor; + uniform float uRound; + ` + ), + main: ( + /* glsl */ + ` + vColor *= uColor; + modelMatrix *= uTransformMatrix; + ` + ), + end: ( + /* glsl */ + ` + if(uRound == 1.) + { + gl_Position.xy = roundPixels(gl_Position.xy, uResolution); + } + ` + ) + }, + fragment: { + header: ( + /* glsl */ + ` + uniform float uDistance; + ` + ), + main: ( + /* glsl */ + ` + outColor = vec4(calculateMSDFAlpha(outColor, vColor, uDistance)); + ` + ) + } + }; + + "use strict"; + const mSDFBit = { + name: "msdf-bit", + fragment: { + header: ( + /* wgsl */ + ` + fn calculateMSDFAlpha(msdfColor:vec4, shapeColor:vec4, distance:f32) -> f32 { + + // MSDF + var median = msdfColor.r + msdfColor.g + msdfColor.b - + min(msdfColor.r, min(msdfColor.g, msdfColor.b)) - + max(msdfColor.r, max(msdfColor.g, msdfColor.b)); + + // SDF + median = min(median, msdfColor.a); + + var screenPxDistance = distance * (median - 0.5); + var alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0); + if (median < 0.01) { + alpha = 0.0; + } else if (median > 0.99) { + alpha = 1.0; + } + + // Gamma correction for coverage-like alpha + var luma: f32 = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114)); + var gamma: f32 = mix(1.0, 1.0 / 2.2, luma); + var coverage: f32 = pow(shapeColor.a * alpha, gamma); + + return coverage; + + } + ` + ) + } + }; + const mSDFBitGl = { + name: "msdf-bit", + fragment: { + header: ( + /* glsl */ + ` + float calculateMSDFAlpha(vec4 msdfColor, vec4 shapeColor, float distance) { + + // MSDF + float median = msdfColor.r + msdfColor.g + msdfColor.b - + min(msdfColor.r, min(msdfColor.g, msdfColor.b)) - + max(msdfColor.r, max(msdfColor.g, msdfColor.b)); + + // SDF + median = min(median, msdfColor.a); + + float screenPxDistance = distance * (median - 0.5); + float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0); + + if (median < 0.01) { + alpha = 0.0; + } else if (median > 0.99) { + alpha = 1.0; + } + + // Gamma correction for coverage-like alpha + float luma = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114)); + float gamma = mix(1.0, 1.0 / 2.2, luma); + float coverage = pow(shapeColor.a * alpha, gamma); + + return coverage; + } + ` + ) + } + }; + + "use strict"; + let gpuProgram; + let glProgram; + class SdfShader extends Shader { + constructor(maxTextures) { + const uniforms = new UniformGroup({ + uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4" }, + uTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + uDistance: { value: 4, type: "f32" }, + uRound: { value: 0, type: "f32" } + }); + gpuProgram != null ? gpuProgram : gpuProgram = compileHighShaderGpuProgram({ + name: "sdf-shader", + bits: [ + colorBit, + generateTextureBatchBit(maxTextures), + localUniformMSDFBit, + mSDFBit, + roundPixelsBit + ] + }); + glProgram != null ? glProgram : glProgram = compileHighShaderGlProgram({ + name: "sdf-shader", + bits: [ + colorBitGl, + generateTextureBatchBitGl(maxTextures), + localUniformMSDFBitGl, + mSDFBitGl, + roundPixelsBitGl + ] + }); + super({ + glProgram, + gpuProgram, + resources: { + localUniforms: uniforms, + batchSamplers: getBatchSamplersUniformGroup(maxTextures) + } + }); + } + } + + "use strict"; + class BitmapTextPipe extends AbstractBitmapTextPipe { + getSdfShader() { + return new SdfShader(this._renderer.limits.maxBatchableTextures); + } + } + /** @ignore */ + BitmapTextPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "bitmapText" + }; + + "use strict"; + extensions.add(CanvasBitmapTextPipe); + extensions.add(BitmapTextPipe); + + "use strict"; + class BitmapText extends AbstractText { + constructor(...args) { + var _a, _b, _c; + const options = ensureTextOptions(args, "BitmapText"); + (_a = options.style) != null ? _a : options.style = options.style || {}; + (_c = (_b = options.style).fill) != null ? _c : _b.fill = 16777215; + super(options, TextStyle); + /** @internal */ + this.renderPipeId = "bitmapText"; + } + /** + * @param now - The current time in milliseconds. + * @internal + */ + _onTouch(now) { + var _a; + this._gcLastUsed = now; + for (const key in this._gpuData) { + (_a = this._gpuData[key]) == null ? void 0 : _a._onTouch(now); + } + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const anchor = this._anchor; + const bitmapMeasurement = BitmapFontManager.measureText(this.text, this._style); + const scale = bitmapMeasurement.scale; + const offset = bitmapMeasurement.offsetY * scale; + let width = bitmapMeasurement.width * scale; + let height = bitmapMeasurement.height * scale; + const stroke = this._style._stroke; + if (stroke) { + width += stroke.width; + height += stroke.width; + } + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * (height + offset); + bounds.maxY = bounds.minY + height; + } + /** + * The resolution / device pixel ratio for text rendering. + * Unlike other text types, BitmapText resolution is managed by the BitmapFont. + * Individual resolution changes are not supported. + * @example + * ```ts + * // ❌ Incorrect: Setting resolution directly (will trigger warning) + * const text = new BitmapText({ + * text: 'Hello', + * resolution: 2 // This will be ignored + * }); + * + * // ✅ Correct: Set resolution when installing the font + * BitmapFont.install({ + * name: 'MyFont', + * style: { + * fontFamily: 'Arial', + * }, + * resolution: 2 // Resolution is set here + * }); + * + * const text = new BitmapText({ + * text: 'Hello', + * style: { + * fontFamily: 'MyFont' // Uses font's resolution + * } + * }); + * ``` + * @default 1 + * @see {@link BitmapFont.install} For setting font resolution + * @throws {Warning} When attempting to change resolution directly + * @readonly + */ + set resolution(value) { + if (value !== null) { + warn( + // eslint-disable-next-line max-len + "[BitmapText] dynamically updating the resolution is not supported. Resolution should be managed by the BitmapFont." + ); + } + } + get resolution() { + return this._resolution; + } + } + + "use strict"; + var __defProp$y = Object.defineProperty; + var __getOwnPropSymbols$A = Object.getOwnPropertySymbols; + var __hasOwnProp$A = Object.prototype.hasOwnProperty; + var __propIsEnum$A = Object.prototype.propertyIsEnumerable; + var __defNormalProp$y = (obj, key, value) => key in obj ? __defProp$y(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$y = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$A.call(b, prop)) + __defNormalProp$y(a, prop, b[prop]); + if (__getOwnPropSymbols$A) + for (var prop of __getOwnPropSymbols$A(b)) { + if (__propIsEnum$A.call(b, prop)) + __defNormalProp$y(a, prop, b[prop]); + } + return a; + }; + function textStyleToCSS(style) { + var _a; + const stroke = style._stroke; + const fill = style._fill; + const color = Color.shared.setValue(fill.color).setAlpha((_a = fill.alpha) != null ? _a : 1).toHexa(); + const cssStyleString = [ + `color: ${color}`, + `font-size: ${style.fontSize}px`, + `font-family: ${style.fontFamily}`, + `font-weight: ${style.fontWeight}`, + `font-style: ${style.fontStyle}`, + `font-variant: ${style.fontVariant}`, + `letter-spacing: ${style.letterSpacing}px`, + `text-align: ${style.align}`, + `padding: ${style.padding}px`, + `white-space: ${style.whiteSpace === "pre" && style.wordWrap ? "pre-wrap" : style.whiteSpace}`, + ...style.lineHeight ? [`line-height: ${style.lineHeight}px`] : [], + ...style.wordWrap ? [ + `word-break: ${style.breakWords ? "break-all" : "normal"}`, + `max-width: ${style.wordWrapWidth}px` + ] : [], + ...stroke ? [strokeToCSS(stroke)] : [], + ...style.dropShadow ? [dropShadowToCSS(style.dropShadow)] : [], + ...style.cssOverrides + ].join(";"); + const cssStyles = [`div { ${cssStyleString} }`]; + tagStyleToCSS(style.tagStyles, cssStyles); + return cssStyles.join(" "); + } + function dropShadowToCSS(dropShadowStyle) { + var _a; + const dropshadowStyle = __spreadValues$y({}, dropShadowStyle); + const color = Color.shared.setValue(dropshadowStyle.color).setAlpha((_a = dropshadowStyle.alpha) != null ? _a : 1).toHexa(); + const x = Math.round(Math.cos(dropshadowStyle.angle) * dropshadowStyle.distance); + const y = Math.round(Math.sin(dropshadowStyle.angle) * dropshadowStyle.distance); + const position = `${x}px ${y}px`; + if (dropshadowStyle.blur > 0) { + return `text-shadow: ${position} ${dropshadowStyle.blur}px ${color}`; + } + return `text-shadow: ${position} ${color}`; + } + function strokeToCSS(stroke) { + var _a; + const color = Color.shared.setValue(stroke.color).setAlpha((_a = stroke.alpha) != null ? _a : 1).toHexa(); + return [ + `-webkit-text-stroke-width: ${stroke.width}px`, + `-webkit-text-stroke-color: ${color}`, + `text-stroke-width: ${stroke.width}px`, + `text-stroke-color: ${color}`, + "paint-order: stroke" + ].join(";"); + } + const templates = { + fontSize: `font-size: {{VALUE}}px`, + fontFamily: `font-family: {{VALUE}}`, + fontWeight: `font-weight: {{VALUE}}`, + fontStyle: `font-style: {{VALUE}}`, + fontVariant: `font-variant: {{VALUE}}`, + letterSpacing: `letter-spacing: {{VALUE}}px`, + align: `text-align: {{VALUE}}`, + padding: `padding: {{VALUE}}px`, + whiteSpace: `white-space: {{VALUE}}`, + lineHeight: `line-height: {{VALUE}}px`, + wordWrapWidth: `max-width: {{VALUE}}px` + }; + const transform = { + fill: (value) => `color: ${Color.shared.setValue(value).toHexa()}`, + breakWords: (value) => `word-break: ${value ? "break-all" : "normal"}`, + stroke: strokeToCSS, + dropShadow: (value) => { + if (value === true) { + return dropShadowToCSS(TextStyle.defaultDropShadow); + } + if (value && typeof value === "object") { + return dropShadowToCSS(__spreadValues$y(__spreadValues$y({}, TextStyle.defaultDropShadow), value)); + } + return ""; + } + }; + function tagStyleToCSS(tagStyles, out) { + for (const i in tagStyles) { + const tagStyle = tagStyles[i]; + const cssTagStyle = []; + for (const j in tagStyle) { + if (transform[j]) { + cssTagStyle.push(transform[j](tagStyle[j])); + } else if (templates[j]) { + cssTagStyle.push(templates[j].replace("{{VALUE}}", tagStyle[j])); + } + } + out.push(`${i} { ${cssTagStyle.join(";")} }`); + } + } + + "use strict"; + var __defProp$x = Object.defineProperty; + var __getOwnPropSymbols$z = Object.getOwnPropertySymbols; + var __hasOwnProp$z = Object.prototype.hasOwnProperty; + var __propIsEnum$z = Object.prototype.propertyIsEnumerable; + var __defNormalProp$x = (obj, key, value) => key in obj ? __defProp$x(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$x = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$z.call(b, prop)) + __defNormalProp$x(a, prop, b[prop]); + if (__getOwnPropSymbols$z) + for (var prop of __getOwnPropSymbols$z(b)) { + if (__propIsEnum$z.call(b, prop)) + __defNormalProp$x(a, prop, b[prop]); + } + return a; + }; + class HTMLTextStyle extends TextStyle { + constructor(options = {}) { + var _a, _b; + super(options); + this._cssOverrides = []; + this.cssOverrides = (_a = options.cssOverrides) != null ? _a : []; + this.tagStyles = (_b = options.tagStyles) != null ? _b : {}; + } + /** + * Custom styles to apply to specific HTML tags. + * Allows for consistent styling of custom elements without CSS overrides. + * @example + * ```ts + * const text = new HTMLText({ + * text: 'Red, Blue, Green', + * style: { + * tagStyles: { + * red: { fill: 'red' }, + * blue: { fill: 'blue' }, + * green: { fill: 'green' }, + * } + * } + * }); + * ``` + * @standard + */ + get tagStyles() { + return this._tagStyles; + } + /** @standard */ + set tagStyles(value) { + if (this._tagStyles === value) return; + this._tagStyles = value != null ? value : {}; + this.update(); + } + /** + * List of CSS style overrides to apply to the HTML text. + * These styles are added after the built-in styles and can override any default styling. + * @advanced + */ + set cssOverrides(value) { + this._cssOverrides = value instanceof Array ? value : [value]; + this.update(); + } + /** @advanced */ + get cssOverrides() { + return this._cssOverrides; + } + /** + * Updates the text style and triggers a refresh of the CSS style cache. + * This method is called automatically when style properties are changed. + * @example + * ```ts + * // Update after multiple changes + * const text = new HTMLText({ + * text: 'Hello World', + * style + * }); + * + * style.fontSize = 32; + * style.fill = '#00ff00'; + * style.fontFamily = 'Arial'; + * style.update(); // Apply all changes at once + * ``` + * @advanced + * @see {@link HTMLTextStyle#cssStyle} For accessing the generated CSS + * @see {@link HTMLTextStyle#cssOverrides} For managing CSS overrides + */ + update() { + this._cssStyle = null; + super.update(); + } + /** + * Creates a new HTMLTextStyle object with the same values as this one. + * This creates a deep copy of all style properties, including dropShadow and tag styles. + * @example + * ```ts + * // Create original style + * const originalStyle = new HTMLTextStyle({ + * fontSize: 24, + * fill: '#ff0000', + * tagStyles: { + * header: { fontSize: 32, fill: '#00ff00' } + * } + * }); + * + * // Clone the style + * const clonedStyle = originalStyle.clone(); + * + * // Modify cloned style independently + * clonedStyle.fontSize = 36; + * clonedStyle.fill = '#0000ff'; + * + * // Original style remains unchanged + * console.log(originalStyle.fontSize); // Still 24 + * console.log(originalStyle.fill); // Still '#ff0000' + * ``` + * + * Properties that are cloned: + * - Basic text properties (fontSize, fontFamily, etc.) + * - Fill and stroke styles + * - Drop shadow configuration + * - CSS overrides + * - Tag styles (deep copied) + * - Word wrap settings + * - Alignment and spacing + * @returns {HTMLTextStyle} A new HTMLTextStyle instance with the same properties + * @see {@link HTMLTextStyle} For available style properties + * @see {@link HTMLTextStyle#cssOverrides} For CSS override handling + * @see {@link HTMLTextStyle#tagStyles} For tag style configuration + * @standard + */ + clone() { + return new HTMLTextStyle({ + align: this.align, + breakWords: this.breakWords, + dropShadow: this.dropShadow ? __spreadValues$x({}, this.dropShadow) : null, + fill: this._fill, + fontFamily: this.fontFamily, + fontSize: this.fontSize, + fontStyle: this.fontStyle, + fontVariant: this.fontVariant, + fontWeight: this.fontWeight, + letterSpacing: this.letterSpacing, + lineHeight: this.lineHeight, + padding: this.padding, + stroke: this._stroke, + whiteSpace: this.whiteSpace, + wordWrap: this.wordWrap, + wordWrapWidth: this.wordWrapWidth, + cssOverrides: this.cssOverrides, + tagStyles: __spreadValues$x({}, this.tagStyles) + }); + } + /** + * The CSS style string that will be applied to the HTML text. + * @advanced + */ + get cssStyle() { + if (!this._cssStyle) { + this._cssStyle = textStyleToCSS(this); + } + return this._cssStyle; + } + /** + * Add a style override, this can be any CSS property + * it will override any built-in style. This is the + * property and the value as a string (e.g., `color: red`). + * This will override any other internal style. + * @param {string} value - CSS style(s) to add. + * @example + * style.addOverride('background-color: red'); + * @advanced + */ + addOverride(...value) { + const toAdd = value.filter((v) => !this.cssOverrides.includes(v)); + if (toAdd.length > 0) { + this.cssOverrides.push(...toAdd); + this.update(); + } + } + /** + * Remove any overrides that match the value. + * @param {string} value - CSS style to remove. + * @example + * style.removeOverride('background-color: red'); + * @advanced + */ + removeOverride(...value) { + const toRemove = value.filter((v) => this.cssOverrides.includes(v)); + if (toRemove.length > 0) { + this.cssOverrides = this.cssOverrides.filter((v) => !toRemove.includes(v)); + this.update(); + } + } + /** + * Sets the fill style for the text. HTML text only supports color fills (string or number values). + * Texture fills are not supported and will trigger a warning in debug mode. + * @example + * ```ts + * // Using hex colors + * const text = new HTMLText({ + * text: 'Colored Text', + * style: { + * fill: 0xff0000 // Red color + * } + * }); + * + * // Using CSS color strings + * text.style.fill = '#00ff00'; // Hex string (Green) + * text.style.fill = 'blue'; // Named color + * text.style.fill = 'rgb(255,0,0)' // RGB + * text.style.fill = '#f0f'; // Short hex + * + * // Invalid usage (will trigger warning in debug) + * text.style.fill = { + * type: 'pattern', + * texture: Texture.from('pattern.png') + * }; // Not supported, falls back to default + * ``` + * @param value - The fill color to use. Must be a string or number. + * @throws {Warning} In debug mode when attempting to use unsupported fill types + * @see {@link TextStyle#fill} For full fill options in canvas text + * @standard + */ + set fill(value) { + if (typeof value !== "string" && typeof value !== "number") { + warn("[HTMLTextStyle] only color fill is not supported by HTMLText"); + } + super.fill = value; + } + /** + * Sets the stroke style for the text. HTML text only supports color strokes (string or number values). + * Texture strokes are not supported and will trigger a warning in debug mode. + * @example + * ```ts + * // Using hex colors + * const text = new HTMLText({ + * text: 'Outlined Text', + * style: { + * stroke: 0xff0000 // Red outline + * } + * }); + * + * // Using CSS color strings + * text.style.stroke = '#00ff00'; // Hex string (Green) + * text.style.stroke = 'blue'; // Named color + * text.style.stroke = 'rgb(255,0,0)' // RGB + * text.style.stroke = '#f0f'; // Short hex + * + * // Using stroke width + * text.style = { + * stroke: { + * color: '#ff0000', + * width: 2 + * } + * }; + * + * // Remove stroke + * text.style.stroke = null; + * + * // Invalid usage (will trigger warning in debug) + * text.style.stroke = { + * type: 'pattern', + * texture: Texture.from('pattern.png') + * }; // Not supported, falls back to default + * ``` + * @param value - The stroke style to use. Must be a string, number, or stroke configuration object + * @throws {Warning} In debug mode when attempting to use unsupported stroke types + * @see {@link TextStyle#stroke} For full stroke options in canvas text + * @standard + */ + set stroke(value) { + if (value && typeof value !== "string" && typeof value !== "number") { + warn("[HTMLTextStyle] only color stroke is not supported by HTMLText"); + } + super.stroke = value; + } + } + + "use strict"; + const nssvg = "http://www.w3.org/2000/svg"; + const nsxhtml = "http://www.w3.org/1999/xhtml"; + class HTMLTextRenderData { + constructor() { + this.svgRoot = document.createElementNS(nssvg, "svg"); + this.foreignObject = document.createElementNS(nssvg, "foreignObject"); + this.domElement = document.createElementNS(nsxhtml, "div"); + this.styleElement = document.createElementNS(nsxhtml, "style"); + const { foreignObject, svgRoot, styleElement, domElement } = this; + foreignObject.setAttribute("width", "10000"); + foreignObject.setAttribute("height", "10000"); + foreignObject.style.overflow = "hidden"; + svgRoot.appendChild(foreignObject); + foreignObject.appendChild(styleElement); + foreignObject.appendChild(domElement); + this.image = DOMAdapter.get().createImage(); + } + destroy() { + this.svgRoot.remove(); + this.foreignObject.remove(); + this.styleElement.remove(); + this.domElement.remove(); + this.image.src = ""; + this.image.remove(); + this.svgRoot = null; + this.foreignObject = null; + this.styleElement = null; + this.domElement = null; + this.image = null; + this.canvasAndContext = null; + } + } + + "use strict"; + let tempHTMLTextRenderData; + function measureHtmlText(text, style, fontStyleCSS, htmlTextRenderData) { + htmlTextRenderData || (htmlTextRenderData = tempHTMLTextRenderData || (tempHTMLTextRenderData = new HTMLTextRenderData())); + const { domElement, styleElement, svgRoot } = htmlTextRenderData; + domElement.innerHTML = `
${text}
`; + domElement.setAttribute("style", "transform-origin: top left; display: inline-block"); + if (fontStyleCSS) { + styleElement.textContent = fontStyleCSS; + } + document.body.appendChild(svgRoot); + let contentWidth = domElement.scrollWidth; + let contentHeight = domElement.scrollHeight; + svgRoot.remove(); + if (style.dropShadow) { + const { distance, angle, blur } = style.dropShadow; + const shadowOffsetX = Math.abs(Math.round(Math.cos(angle) * distance)); + const shadowOffsetY = Math.abs(Math.round(Math.sin(angle) * distance)); + contentWidth += shadowOffsetX + blur; + contentHeight += shadowOffsetY + blur; + } + const doublePadding = style.padding * 2; + return { + width: contentWidth - doublePadding, + height: contentHeight - doublePadding + }; + } + + "use strict"; + class BatchableHTMLText extends BatchableSprite { + constructor() { + super(...arguments); + this.generatingTexture = false; + this.currentKey = "--"; + } + /** Destroys the BatchableHTMLText instance. Returns the texture promise to the renderer and cleans up references. */ + destroy() { + this.texturePromise = null; + this.generatingTexture = false; + this.currentKey = "--"; + super.destroy(); + } + } + + "use strict"; + class HTMLTextPipe { + constructor(renderer) { + this._renderer = renderer; + renderer.runners.resolutionChange.add(this); + this._managedTexts = new GCManagedHash({ + renderer, + type: "renderable", + onUnload: this.onTextUnload.bind(this), + name: "htmlText" + }); + } + resolutionChange() { + for (const key in this._managedTexts.items) { + const text = this._managedTexts.items[key]; + if (text == null ? void 0 : text._autoResolution) { + text.onViewUpdate(); + } + } + } + validateRenderable(htmlText) { + const gpuText = this._getGpuText(htmlText); + const newKey = htmlText.styleKey; + if (gpuText.currentKey !== newKey) { + return true; + } + return false; + } + addRenderable(htmlText, instructionSet) { + const batchableHTMLText = this._getGpuText(htmlText); + if (htmlText._didTextUpdate) { + const resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution; + if (batchableHTMLText.currentKey !== htmlText.styleKey || htmlText.resolution !== resolution) { + this._updateGpuText(htmlText).catch((e) => { + console.error(e); + }); + } + htmlText._didTextUpdate = false; + updateTextBounds(batchableHTMLText, htmlText); + } + this._renderer.renderPipes.batch.addToBatch(batchableHTMLText, instructionSet); + } + updateRenderable(htmlText) { + const batchableHTMLText = this._getGpuText(htmlText); + batchableHTMLText._batcher.updateElement(batchableHTMLText); + } + async _updateGpuText(htmlText) { + htmlText._didTextUpdate = false; + const batchableHTMLText = this._getGpuText(htmlText); + if (batchableHTMLText.generatingTexture) return; + const oldTexturePromise = batchableHTMLText.texturePromise; + batchableHTMLText.texturePromise = null; + batchableHTMLText.generatingTexture = true; + htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution; + let texturePromise = this._renderer.htmlText.getTexturePromise(htmlText); + if (oldTexturePromise) { + texturePromise = texturePromise.finally(() => { + this._renderer.htmlText.decreaseReferenceCount(batchableHTMLText.currentKey); + this._renderer.htmlText.returnTexturePromise(oldTexturePromise); + }); + } + batchableHTMLText.texturePromise = texturePromise; + batchableHTMLText.currentKey = htmlText.styleKey; + batchableHTMLText.texture = await texturePromise; + const renderGroup = htmlText.renderGroup || htmlText.parentRenderGroup; + if (renderGroup) { + renderGroup.structureDidChange = true; + } + batchableHTMLText.generatingTexture = false; + updateTextBounds(batchableHTMLText, htmlText); + } + _getGpuText(htmlText) { + return htmlText._gpuData[this._renderer.uid] || this.initGpuText(htmlText); + } + initGpuText(htmlText) { + const batchableHTMLText = new BatchableHTMLText(); + batchableHTMLText.renderable = htmlText; + batchableHTMLText.transform = htmlText.groupTransform; + batchableHTMLText.texture = Texture.EMPTY; + batchableHTMLText.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 }; + batchableHTMLText.roundPixels = this._renderer._roundPixels | htmlText._roundPixels; + htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution; + htmlText._gpuData[this._renderer.uid] = batchableHTMLText; + this._managedTexts.add(htmlText); + return batchableHTMLText; + } + onTextUnload(text) { + const gpuData = text._gpuData[this._renderer.uid]; + if (!gpuData) return; + const { htmlText } = this._renderer; + htmlText.getReferenceCount(gpuData.currentKey) === null ? htmlText.returnTexturePromise(gpuData.texturePromise) : htmlText.decreaseReferenceCount(gpuData.currentKey); + } + destroy() { + this._managedTexts.destroy(); + this._renderer = null; + } + } + /** @ignore */ + HTMLTextPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "htmlText" + }; + + "use strict"; + function isSafari() { + const { userAgent } = DOMAdapter.get().getNavigator(); + return /^((?!chrome|android).)*safari/i.test(userAgent); + } + + "use strict"; + function extractFontFamilies(text, style) { + const fontFamily = style.fontFamily; + const fontFamilies = []; + const dedupe = {}; + const regex = /font-family:([^;"\s]+)/g; + const matches = text.match(regex); + function addFontFamily(fontFamily2) { + if (!dedupe[fontFamily2]) { + fontFamilies.push(fontFamily2); + dedupe[fontFamily2] = true; + } + } + if (Array.isArray(fontFamily)) { + for (let i = 0; i < fontFamily.length; i++) { + addFontFamily(fontFamily[i]); + } + } else { + addFontFamily(fontFamily); + } + if (matches) { + matches.forEach((match) => { + const fontFamily2 = match.split(":")[1].trim(); + addFontFamily(fontFamily2); + }); + } + for (const i in style.tagStyles) { + const fontFamily2 = style.tagStyles[i].fontFamily; + addFontFamily(fontFamily2); + } + return fontFamilies; + } + + "use strict"; + async function loadFontAsBase64(url) { + const response = await DOMAdapter.get().fetch(url); + const blob = await response.blob(); + const reader = new FileReader(); + const dataSrc = await new Promise((resolve, reject) => { + reader.onloadend = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + return dataSrc; + } + + "use strict"; + async function loadFontCSS(style, url) { + const dataSrc = await loadFontAsBase64(url); + return `@font-face { + font-family: "${style.fontFamily}"; + font-weight: ${style.fontWeight}; + font-style: ${style.fontStyle}; + src: url('${dataSrc}'); + }`; + } + + "use strict"; + const FontStylePromiseCache = /* @__PURE__ */ new Map(); + async function getFontCss(fontFamilies) { + const fontPromises = fontFamilies.filter((fontFamily) => Cache.has(`${fontFamily}-and-url`)).map((fontFamily) => { + if (!FontStylePromiseCache.has(fontFamily)) { + const { entries } = Cache.get(`${fontFamily}-and-url`); + const promises = []; + entries.forEach((entry) => { + const url = entry.url; + const faces = entry.faces; + const out = faces.map((face) => ({ weight: face.weight, style: face.style })); + promises.push( + ...out.map( + (style) => loadFontCSS( + { + fontWeight: style.weight, + fontStyle: style.style, + fontFamily + }, + url + ) + ) + ); + }); + FontStylePromiseCache.set( + fontFamily, + Promise.all(promises).then((css) => css.join("\n")) + ); + } + return FontStylePromiseCache.get(fontFamily); + }); + return (await Promise.all(fontPromises)).join("\n"); + } + + "use strict"; + function getSVGUrl(text, style, resolution, fontCSS, htmlTextData) { + const { domElement, styleElement, svgRoot } = htmlTextData; + domElement.innerHTML = `
${text}
`; + domElement.setAttribute("style", `transform: scale(${resolution});transform-origin: top left; display: inline-block`); + styleElement.textContent = fontCSS; + const { width, height } = htmlTextData.image; + svgRoot.setAttribute("width", width.toString()); + svgRoot.setAttribute("height", height.toString()); + return new XMLSerializer().serializeToString(svgRoot); + } + + "use strict"; + function getTemporaryCanvasFromImage(image, resolution) { + const canvasAndContext = CanvasPool.getOptimalCanvasAndContext( + image.width, + image.height, + resolution + ); + const { context } = canvasAndContext; + context.clearRect(0, 0, image.width, image.height); + context.drawImage(image, 0, 0); + return canvasAndContext; + } + + "use strict"; + function loadSVGImage(image, url, delay) { + return new Promise(async (resolve) => { + if (delay) { + await new Promise((resolve2) => setTimeout(resolve2, 100)); + } + image.onload = () => { + resolve(); + }; + image.src = `data:image/svg+xml;charset=utf8,${encodeURIComponent(url)}`; + image.crossOrigin = "anonymous"; + }); + } + + "use strict"; + class HTMLTextSystem { + constructor(renderer) { + this._activeTextures = {}; + this._renderer = renderer; + this._createCanvas = renderer.type === RendererType.WEBGPU; + } + /** + * @param options + * @deprecated Use getTexturePromise instead + */ + getTexture(options) { + return this.getTexturePromise(options); + } + /** + * Increases the reference count for a texture. + * @param text - The HTMLText instance associated with the texture. + */ + getManagedTexture(text) { + const textKey = text.styleKey; + if (this._activeTextures[textKey]) { + this._increaseReferenceCount(textKey); + return this._activeTextures[textKey].promise; + } + const promise = this._buildTexturePromise(text).then((texture) => { + this._activeTextures[textKey].texture = texture; + return texture; + }); + this._activeTextures[textKey] = { + texture: null, + promise, + usageCount: 1 + }; + return promise; + } + /** + * Gets the current reference count for a texture associated with a text key. + * @param textKey - The unique key identifying the text style configuration + * @returns The number of Text instances currently using this texture + */ + getReferenceCount(textKey) { + var _a, _b; + return (_b = (_a = this._activeTextures[textKey]) == null ? void 0 : _a.usageCount) != null ? _b : null; + } + _increaseReferenceCount(textKey) { + this._activeTextures[textKey].usageCount++; + } + /** + * Decreases the reference count for a texture. + * If the count reaches zero, the texture is cleaned up. + * @param textKey - The key associated with the HTMLText instance. + */ + decreaseReferenceCount(textKey) { + const activeTexture = this._activeTextures[textKey]; + if (!activeTexture) return; + activeTexture.usageCount--; + if (activeTexture.usageCount === 0) { + if (activeTexture.texture) { + this._cleanUp(activeTexture.texture); + } else { + activeTexture.promise.then((texture) => { + activeTexture.texture = texture; + this._cleanUp(activeTexture.texture); + }).catch(() => { + warn("HTMLTextSystem: Failed to clean texture"); + }); + } + this._activeTextures[textKey] = null; + } + } + /** + * Returns a promise that resolves to a texture for the given HTMLText options. + * @param options - The options for the HTMLText. + * @returns A promise that resolves to a Texture. + */ + getTexturePromise(options) { + return this._buildTexturePromise(options); + } + async _buildTexturePromise(options) { + const { text, style, resolution, textureStyle } = options; + const htmlTextData = BigPool.get(HTMLTextRenderData); + const fontFamilies = extractFontFamilies(text, style); + const fontCSS = await getFontCss(fontFamilies); + const measured = measureHtmlText(text, style, fontCSS, htmlTextData); + const width = Math.ceil(Math.ceil(Math.max(1, measured.width) + style.padding * 2) * resolution); + const height = Math.ceil(Math.ceil(Math.max(1, measured.height) + style.padding * 2) * resolution); + const image = htmlTextData.image; + const uvSafeOffset = 2; + image.width = (width | 0) + uvSafeOffset; + image.height = (height | 0) + uvSafeOffset; + const svgURL = getSVGUrl(text, style, resolution, fontCSS, htmlTextData); + await loadSVGImage(image, svgURL, isSafari() && fontFamilies.length > 0); + const resource = image; + let canvasAndContext; + if (this._createCanvas) { + canvasAndContext = getTemporaryCanvasFromImage(image, resolution); + } + const texture = getPo2TextureFromSource( + canvasAndContext ? canvasAndContext.canvas : resource, + image.width - uvSafeOffset, + image.height - uvSafeOffset, + resolution + ); + if (textureStyle) texture.source.style = textureStyle; + if (this._createCanvas) { + this._renderer.texture.initSource(texture.source); + CanvasPool.returnCanvasAndContext(canvasAndContext); + } + BigPool.return(htmlTextData); + return texture; + } + returnTexturePromise(texturePromise) { + texturePromise.then((texture) => { + this._cleanUp(texture); + }).catch(() => { + warn("HTMLTextSystem: Failed to clean texture"); + }); + } + _cleanUp(texture) { + TexturePool.returnTexture(texture, true); + texture.source.resource = null; + texture.source.uploadMethodId = "unknown"; + } + destroy() { + this._renderer = null; + for (const key in this._activeTextures) { + if (this._activeTextures[key]) this.returnTexturePromise(this._activeTextures[key].promise); + } + this._activeTextures = null; + } + } + /** @ignore */ + HTMLTextSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "htmlText" + }; + + "use strict"; + extensions.add(HTMLTextSystem); + extensions.add(HTMLTextPipe); + + "use strict"; + class HTMLText extends AbstractText { + constructor(...args) { + const options = ensureTextOptions(args, "HtmlText"); + super(options, HTMLTextStyle); + /** @internal */ + this.renderPipeId = "htmlText"; + if (options.textureStyle) { + this.textureStyle = options.textureStyle instanceof TextureStyle ? options.textureStyle : new TextureStyle(options.textureStyle); + } + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const anchor = this._anchor; + const htmlMeasurement = measureHtmlText(this.text, this._style); + const { width, height } = htmlMeasurement; + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + get text() { + return this._text; + } + /** + * The text content to display. Use '\n' for line breaks. + * Accepts strings, numbers, or objects with toString() method. + * @example + * ```ts + * const text = new HTMLText({ + * text: 'Hello Pixi!', + * }); + * const multilineText = new HTMLText({ + * text: 'Line 1\nLine 2\nLine 3', + * }); + * const numberText = new HTMLText({ + * text: 12345, // Will be converted to '12345' + * }); + * const objectText = new HTMLText({ + * text: { toString: () => 'Object Text' }, // Custom toString + * }); + * + * // Update text dynamically + * text.text = 'Updated Text'; // Re-renders with new text + * text.text = 67890; // Updates to '67890' + * text.text = { toString: () => 'Dynamic Text' }; // Uses custom toString method + * // Clear text + * text.text = ''; // Clears the text + * ``` + * @default '' + */ + set text(text) { + const sanitisedText = this._sanitiseText(text.toString()); + super.text = sanitisedText; + } + /** + * Sanitise text - replace `
` with `
`, ` ` with ` ` + * @param text + * @see https://www.sitepoint.com/community/t/xhtml-1-0-transitional-xml-parsing-error-entity-nbsp-not-defined/3392/3 + */ + _sanitiseText(text) { + return this._removeInvalidHtmlTags(text.replace(/
/gi, "
").replace(/
/gi, "
").replace(/ /gi, " ")); + } + _removeInvalidHtmlTags(input) { + const brokenTagPattern = /<[^>]*?(?=<|$)/g; + return input.replace(brokenTagPattern, ""); + } + } + + "use strict"; + class PrepareUpload extends PrepareQueue { + /** + * Upload the given queue item + * @param item + */ + uploadQueueItem(item) { + if (item instanceof TextureSource) { + this.uploadTextureSource(item); + } else if (item instanceof Text) { + this.uploadText(item); + } else if (item instanceof HTMLText) { + this.uploadHTMLText(item); + } else if (item instanceof BitmapText) { + this.uploadBitmapText(item); + } else if (item instanceof GraphicsContext) { + this.uploadGraphicsContext(item); + } + } + uploadTextureSource(textureSource) { + this.renderer.texture.initSource(textureSource); + } + uploadText(_text) { + this.renderer.renderPipes.text.initGpuText(_text); + } + uploadBitmapText(_text) { + this.renderer.renderPipes.bitmapText.initGpuText(_text); + } + uploadHTMLText(_text) { + this.renderer.renderPipes.htmlText.initGpuText(_text); + } + /** + * Resolve the given graphics context and return an item for the queue + * @param graphicsContext + */ + uploadGraphicsContext(graphicsContext) { + this.renderer.graphicsContext.getGpuContext(graphicsContext); + const { instructions } = graphicsContext; + for (const instruction of instructions) { + if (instruction.action === "texture") { + const { image } = instruction.data; + this.uploadTextureSource(image.source); + } else if (instruction.action === "fill") { + const { texture } = instruction.data.style; + this.uploadTextureSource(texture.source); + } + } + return null; + } + } + + "use strict"; + class PrepareSystem extends PrepareUpload { + /** Destroys the plugin, don't use after this. */ + destroy() { + super.destroy(); + clearTimeout(this.timeout); + this.renderer = null; + this.queue = null; + this.resolves = null; + } + } + /** @ignore */ + PrepareSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "prepare" + }; + + "use strict"; + + "use strict"; + const _CanvasBatchAdaptor = class _CanvasBatchAdaptor { + static _getPatternRepeat(addressModeU, addressModeV) { + const repeatU = addressModeU && addressModeU !== "clamp-to-edge"; + const repeatV = addressModeV && addressModeV !== "clamp-to-edge"; + if (repeatU && repeatV) return "repeat"; + if (repeatU) return "repeat-x"; + if (repeatV) return "repeat-y"; + return "no-repeat"; + } + start(batchPipe, geometry, shader) { + void batchPipe; + void geometry; + void shader; + } + execute(batchPipe, batch) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + const elements = batch.elements; + if (!elements || !elements.length) return; + const renderer = batchPipe.renderer; + const contextSystem = renderer.canvasContext; + const context = contextSystem.activeContext; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + if (!element.packAsQuad) continue; + const quad = element; + const texture = quad.texture; + const source = texture ? canvasUtils.getCanvasSource(texture) : null; + if (!source) continue; + const textureStyle = texture.source.style; + const smoothProperty = contextSystem.smoothProperty; + const shouldSmooth = textureStyle.scaleMode !== "nearest"; + if (context[smoothProperty] !== shouldSmooth) { + context[smoothProperty] = shouldSmooth; + } + contextSystem.setBlendMode(batch.blendMode); + const globalColor = (_b = (_a = renderer.globalUniforms.globalUniformData) == null ? void 0 : _a.worldColor) != null ? _b : 4294967295; + const argb = quad.color; + const globalAlpha = (globalColor >>> 24 & 255) / 255; + const quadAlpha = (argb >>> 24 & 255) / 255; + const filterAlpha = (_d = (_c = renderer.filter) == null ? void 0 : _c.alphaMultiplier) != null ? _d : 1; + const alpha = globalAlpha * quadAlpha * filterAlpha; + if (alpha <= 0) continue; + context.globalAlpha = alpha; + const globalTint = globalColor & 16777215; + const quadTint = argb & 16777215; + const tint = bgr2rgb(multiplyHexColors(quadTint, globalTint)); + const frame = texture.frame; + const repeatU = (_e = textureStyle.addressModeU) != null ? _e : textureStyle.addressMode; + const repeatV = (_f = textureStyle.addressModeV) != null ? _f : textureStyle.addressMode; + const repeat = _CanvasBatchAdaptor._getPatternRepeat(repeatU, repeatV); + const resolution = (_h = (_g = texture.source._resolution) != null ? _g : texture.source.resolution) != null ? _h : 1; + const isFromCachedRenderGroup = (_j = (_i = quad.renderable) == null ? void 0 : _i.renderGroup) == null ? void 0 : _j.isCachedAsTexture; + const sx = frame.x * resolution; + const sy = frame.y * resolution; + const sw = frame.width * resolution; + const sh = frame.height * resolution; + const bounds = quad.bounds; + const isRootTarget = renderer.renderTarget.renderTarget.isRoot; + const dx = bounds.minX; + const dy = bounds.minY; + const dw = bounds.maxX - bounds.minX; + const dh = bounds.maxY - bounds.minY; + const rotate = texture.rotate; + const uvs = texture.uvs; + const uvMin = Math.min(uvs.x0, uvs.x1, uvs.x2, uvs.x3, uvs.y0, uvs.y1, uvs.y2, uvs.y3); + const uvMax = Math.max(uvs.x0, uvs.x1, uvs.x2, uvs.x3, uvs.y0, uvs.y1, uvs.y2, uvs.y3); + const needsRepeat = repeat !== "no-repeat" && (uvMin < 0 || uvMax > 1); + const willUseProcessedCanvas = !needsRepeat && (tint !== 16777215 || rotate); + const applyRotateTransform = rotate && !willUseProcessedCanvas; + if (applyRotateTransform) { + _CanvasBatchAdaptor._tempPatternMatrix.copyFrom(quad.transform); + groupD8.matrixAppendRotationInv( + _CanvasBatchAdaptor._tempPatternMatrix, + rotate, + dx, + dy, + dw, + dh + ); + contextSystem.setContextTransform( + _CanvasBatchAdaptor._tempPatternMatrix, + quad.roundPixels === 1, + void 0, + isFromCachedRenderGroup && isRootTarget + ); + } else { + contextSystem.setContextTransform( + quad.transform, + quad.roundPixels === 1, + void 0, + isFromCachedRenderGroup && isRootTarget + ); + } + const drawX = applyRotateTransform ? 0 : dx; + const drawY = applyRotateTransform ? 0 : dy; + const drawW = dw; + const drawH = dh; + if (needsRepeat) { + let patternSource = source; + const canTint = tint !== 16777215 && !rotate; + const fitsFrame = frame.width <= texture.source.width && frame.height <= texture.source.height; + if (canTint && fitsFrame) { + patternSource = canvasUtils.getTintedCanvas({ texture }, tint); + } + const pattern = context.createPattern(patternSource, repeat); + if (!pattern) continue; + const denomX = drawW; + const denomY = drawH; + if (denomX === 0 || denomY === 0) continue; + const invDx = 1 / denomX; + const invDy = 1 / denomY; + const a = (uvs.x1 - uvs.x0) * invDx; + const b = (uvs.y1 - uvs.y0) * invDx; + const c = (uvs.x3 - uvs.x0) * invDy; + const d = (uvs.y3 - uvs.y0) * invDy; + const tx = uvs.x0 - a * drawX - c * drawY; + const ty = uvs.y0 - b * drawX - d * drawY; + const pixelWidth = texture.source.pixelWidth; + const pixelHeight = texture.source.pixelHeight; + _CanvasBatchAdaptor._tempPatternMatrix.set( + a * pixelWidth, + b * pixelHeight, + c * pixelWidth, + d * pixelHeight, + tx * pixelWidth, + ty * pixelHeight + ); + canvasUtils.applyPatternTransform(pattern, _CanvasBatchAdaptor._tempPatternMatrix); + context.fillStyle = pattern; + context.fillRect(drawX, drawY, drawW, drawH); + } else { + const needsProcessing = tint !== 16777215 || rotate; + const processedSource = needsProcessing ? canvasUtils.getTintedCanvas({ texture }, tint) : source; + const isProcessed = processedSource !== source; + context.drawImage( + processedSource, + isProcessed ? 0 : sx, + isProcessed ? 0 : sy, + isProcessed ? processedSource.width : sw, + isProcessed ? processedSource.height : sh, + drawX, + drawY, + drawW, + drawH + ); + } + } + } + }; + _CanvasBatchAdaptor._tempPatternMatrix = new Matrix(); + /** @ignore */ + _CanvasBatchAdaptor.extension = { + type: [ + ExtensionType.CanvasPipesAdaptor + ], + name: "batch" + }; + let CanvasBatchAdaptor = _CanvasBatchAdaptor; + + "use strict"; + class GlBatchAdaptor { + constructor() { + this._tempState = State.for2d(); + /** + * We only want to sync the a batched shaders uniforms once on first use + * this is a hash of shader uids to a boolean value. When the shader is first bound + * we set the value to true. When the shader is bound again we check the value and + * if it is true we know that the uniforms have already been synced and we skip it. + */ + this._didUploadHash = {}; + } + init(batcherPipe) { + batcherPipe.renderer.runners.contextChange.add(this); + } + contextChange() { + this._didUploadHash = {}; + } + start(batchPipe, geometry, shader) { + const renderer = batchPipe.renderer; + const didUpload = this._didUploadHash[shader.uid]; + renderer.shader.bind(shader, didUpload); + if (!didUpload) { + this._didUploadHash[shader.uid] = true; + } + renderer.shader.updateUniformGroup(renderer.globalUniforms.uniformGroup); + renderer.geometry.bind(geometry, shader.glProgram); + } + execute(batchPipe, batch) { + const renderer = batchPipe.renderer; + this._tempState.blendMode = batch.blendMode; + renderer.state.set(this._tempState); + const textures = batch.textures.textures; + for (let i = 0; i < batch.textures.count; i++) { + renderer.texture.bind(textures[i], i); + } + renderer.geometry.draw(batch.topology, batch.size, batch.start); + } + } + /** @ignore */ + GlBatchAdaptor.extension = { + type: [ + ExtensionType.WebGLPipesAdaptor + ], + name: "batch" + }; + + "use strict"; + function generateGPULayout(maxTextures) { + const gpuLayout = []; + let bindIndex = 0; + for (let i = 0; i < maxTextures; i++) { + gpuLayout[bindIndex] = { + texture: { + sampleType: "float", + viewDimension: "2d", + multisampled: false + }, + binding: bindIndex, + visibility: GPUShaderStage.FRAGMENT + }; + bindIndex++; + gpuLayout[bindIndex] = { + sampler: { + type: "filtering" + }, + binding: bindIndex, + visibility: GPUShaderStage.FRAGMENT + }; + bindIndex++; + } + return gpuLayout; + } + + "use strict"; + function generateLayout(maxTextures) { + const layout = {}; + let bindIndex = 0; + for (let i = 0; i < maxTextures; i++) { + layout[`textureSource${i + 1}`] = bindIndex++; + layout[`textureSampler${i + 1}`] = bindIndex++; + } + return layout; + } + + "use strict"; + const tempState = State.for2d(); + class GpuBatchAdaptor { + start(batchPipe, geometry, shader) { + const renderer = batchPipe.renderer; + const encoder = renderer.encoder; + const program = shader.gpuProgram; + this._shader = shader; + this._geometry = geometry; + encoder.setGeometry(geometry, program); + tempState.blendMode = "normal"; + renderer.pipeline.getPipeline( + geometry, + program, + tempState + ); + const globalUniformsBindGroup = renderer.globalUniforms.bindGroup; + encoder.resetBindGroup(1); + encoder.setBindGroup(0, globalUniformsBindGroup, program); + } + execute(batchPipe, batch) { + const program = this._shader.gpuProgram; + const renderer = batchPipe.renderer; + const encoder = renderer.encoder; + if (!batch.bindGroup) { + const textureBatch = batch.textures; + batch.bindGroup = getTextureBatchBindGroup( + textureBatch.textures, + textureBatch.count, + renderer.limits.maxBatchableTextures + ); + } + tempState.blendMode = batch.blendMode; + const gpuBindGroup = renderer.bindGroup.getBindGroup( + batch.bindGroup, + program, + 1 + ); + const pipeline = renderer.pipeline.getPipeline( + this._geometry, + program, + tempState, + batch.topology + ); + batch.bindGroup._touch(renderer.gc.now, renderer.tick); + encoder.setPipeline(pipeline); + encoder.renderPassEncoder.setBindGroup(1, gpuBindGroup); + encoder.renderPassEncoder.drawIndexed(batch.size, 1, batch.start); + } + } + /** @ignore */ + GpuBatchAdaptor.extension = { + type: [ + ExtensionType.WebGPUPipesAdaptor + ], + name: "batch" + }; + + "use strict"; + const _BatcherPipe = class _BatcherPipe { + constructor(renderer, adaptor) { + this.state = State.for2d(); + this._batchersByInstructionSet = /* @__PURE__ */ Object.create(null); + /** A record of all active batchers, keyed by their names */ + this._activeBatches = /* @__PURE__ */ Object.create(null); + var _a, _b; + this.renderer = renderer; + this._adaptor = adaptor; + (_b = (_a = this._adaptor).init) == null ? void 0 : _b.call(_a, this); + } + static getBatcher(name) { + return new this._availableBatchers[name](); + } + buildStart(instructionSet) { + let batchers = this._batchersByInstructionSet[instructionSet.uid]; + if (!batchers) { + batchers = this._batchersByInstructionSet[instructionSet.uid] = /* @__PURE__ */ Object.create(null); + batchers.default || (batchers.default = new DefaultBatcher({ + maxTextures: this.renderer.limits.maxBatchableTextures + })); + } + this._activeBatches = batchers; + this._activeBatch = this._activeBatches.default; + for (const i in this._activeBatches) { + this._activeBatches[i].begin(); + } + } + addToBatch(batchableObject, instructionSet) { + if (this._activeBatch.name !== batchableObject.batcherName) { + this._activeBatch.break(instructionSet); + let batch = this._activeBatches[batchableObject.batcherName]; + if (!batch) { + batch = this._activeBatches[batchableObject.batcherName] = _BatcherPipe.getBatcher(batchableObject.batcherName); + batch.begin(); + } + this._activeBatch = batch; + } + this._activeBatch.add(batchableObject); + } + break(instructionSet) { + this._activeBatch.break(instructionSet); + } + buildEnd(instructionSet) { + this._activeBatch.break(instructionSet); + const batches = this._activeBatches; + for (const i in batches) { + const batch = batches[i]; + const geometry = batch.geometry; + geometry.indexBuffer.setDataWithSize(batch.indexBuffer, batch.indexSize, true); + geometry.buffers[0].setDataWithSize(batch.attributeBuffer.float32View, batch.attributeSize, false); + } + } + upload(instructionSet) { + const batchers = this._batchersByInstructionSet[instructionSet.uid]; + for (const i in batchers) { + const batcher = batchers[i]; + const geometry = batcher.geometry; + if (batcher.dirty) { + batcher.dirty = false; + geometry.buffers[0].update(batcher.attributeSize * 4); + } + } + } + execute(batch) { + if (batch.action === "startBatch") { + const batcher = batch.batcher; + const geometry = batcher.geometry; + const shader = batcher.shader; + this._adaptor.start(this, geometry, shader); + } + this._adaptor.execute(this, batch); + } + destroy() { + this.state = null; + this.renderer = null; + this._adaptor = null; + for (const i in this._activeBatches) { + this._activeBatches[i].destroy(); + } + this._activeBatches = null; + } + }; + /** @ignore */ + _BatcherPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "batch" + }; + _BatcherPipe._availableBatchers = /* @__PURE__ */ Object.create(null); + let BatcherPipe = _BatcherPipe; + extensions.handleByMap(ExtensionType.Batcher, BatcherPipe._availableBatchers); + extensions.add(DefaultBatcher); + + "use strict"; + + "use strict"; + function formatShader(shader) { + const spl = shader.split(/([\n{}])/g).map((a) => a.trim()).filter((a) => a.length); + let indent = ""; + const formatted = spl.map((a) => { + let indentedLine = indent + a; + if (a === "{") { + indent += " "; + } else if (a === "}") { + indent = indent.substr(0, indent.length - 4); + indentedLine = indent + a; + } + return indentedLine; + }).join("\n"); + return formatted; + } + + "use strict"; + const textureBit = { + name: "texture-bit", + vertex: { + header: ( + /* wgsl */ + ` + + struct TextureUniforms { + uTextureMatrix:mat3x3, + } + + @group(2) @binding(2) var textureUniforms : TextureUniforms; + ` + ), + main: ( + /* wgsl */ + ` + uv = (textureUniforms.uTextureMatrix * vec3(uv, 1.0)).xy; + ` + ) + }, + fragment: { + header: ( + /* wgsl */ + ` + @group(2) @binding(0) var uTexture: texture_2d; + @group(2) @binding(1) var uSampler: sampler; + + + ` + ), + main: ( + /* wgsl */ + ` + outColor = textureSample(uTexture, uSampler, vUV); + ` + ) + } + }; + const textureBitGl = { + name: "texture-bit", + vertex: { + header: ( + /* glsl */ + ` + uniform mat3 uTextureMatrix; + ` + ), + main: ( + /* glsl */ + ` + uv = (uTextureMatrix * vec3(uv, 1.0)).xy; + ` + ) + }, + fragment: { + header: ( + /* glsl */ + ` + uniform sampler2D uTexture; + + + ` + ), + main: ( + /* glsl */ + ` + outColor = texture(uTexture, vUV); + ` + ) + } + }; + + "use strict"; + const tempBounds$1 = new Bounds(); + class AlphaMaskEffect extends FilterEffect { + constructor() { + super(); + this.filters = [new MaskFilter({ + sprite: new Sprite(Texture.EMPTY), + inverse: false, + resolution: "inherit", + antialias: "inherit" + })]; + } + get sprite() { + return this.filters[0].sprite; + } + set sprite(value) { + this.filters[0].sprite = value; + } + get inverse() { + return this.filters[0].inverse; + } + set inverse(value) { + this.filters[0].inverse = value; + } + } + class AlphaMaskPipe { + constructor(renderer) { + this._activeMaskStage = []; + this._renderer = renderer; + } + push(mask, maskedContainer, instructionSet) { + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "alphaMask", + action: "pushMaskBegin", + mask, + inverse: maskedContainer._maskOptions.inverse, + canBundle: false, + maskedContainer + }); + mask.inverse = maskedContainer._maskOptions.inverse; + if (mask.renderMaskToTexture) { + const maskContainer = mask.mask; + maskContainer.includeInBuild = true; + maskContainer.collectRenderables( + instructionSet, + renderer, + null + ); + maskContainer.includeInBuild = false; + } + renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "alphaMask", + action: "pushMaskEnd", + mask, + maskedContainer, + inverse: maskedContainer._maskOptions.inverse, + canBundle: false + }); + } + pop(mask, _maskedContainer, instructionSet) { + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "alphaMask", + action: "popMaskEnd", + mask, + inverse: _maskedContainer._maskOptions.inverse, + canBundle: false + }); + } + execute(instruction) { + const renderer = this._renderer; + const renderMask = instruction.mask.renderMaskToTexture; + if (instruction.action === "pushMaskBegin") { + const filterEffect = BigPool.get(AlphaMaskEffect); + filterEffect.inverse = instruction.inverse; + if (renderMask) { + instruction.mask.mask.measurable = true; + const bounds = getGlobalBounds(instruction.mask.mask, true, tempBounds$1); + instruction.mask.mask.measurable = false; + bounds.ceil(); + const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source; + const filterTexture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + colorTextureSource._resolution, + colorTextureSource.antialias + ); + renderer.renderTarget.push(filterTexture, true); + renderer.globalUniforms.push({ + offset: bounds, + worldColor: 4294967295 + }); + const sprite = filterEffect.sprite; + sprite.texture = filterTexture; + sprite.worldTransform.tx = bounds.minX; + sprite.worldTransform.ty = bounds.minY; + this._activeMaskStage.push({ + filterEffect, + maskedContainer: instruction.maskedContainer, + filterTexture + }); + } else { + filterEffect.sprite = instruction.mask.mask; + this._activeMaskStage.push({ + filterEffect, + maskedContainer: instruction.maskedContainer + }); + } + } else if (instruction.action === "pushMaskEnd") { + const maskData = this._activeMaskStage[this._activeMaskStage.length - 1]; + if (renderMask) { + if (renderer.type === RendererType.WEBGL) { + renderer.renderTarget.finishRenderPass(); + } + renderer.renderTarget.pop(); + renderer.globalUniforms.pop(); + } + renderer.filter.push({ + renderPipeId: "filter", + action: "pushFilter", + container: maskData.maskedContainer, + filterEffect: maskData.filterEffect, + canBundle: false + }); + } else if (instruction.action === "popMaskEnd") { + renderer.filter.pop(); + const maskData = this._activeMaskStage.pop(); + if (renderMask) { + TexturePool.returnTexture(maskData.filterTexture); + } + BigPool.return(maskData.filterEffect); + } + } + destroy() { + this._renderer = null; + this._activeMaskStage = null; + } + } + /** @ignore */ + AlphaMaskPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "alphaMask" + }; + + "use strict"; + class CanvasColorMaskPipe { + constructor(renderer) { + this._colorStack = []; + this._colorStackIndex = 0; + this._currentColor = 0; + this._renderer = renderer; + } + buildStart() { + this._colorStack[0] = 15; + this._colorStackIndex = 1; + this._currentColor = 15; + } + push(mask, _container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + const colorStack = this._colorStack; + colorStack[this._colorStackIndex] = colorStack[this._colorStackIndex - 1] & mask.mask; + const currentColor = this._colorStack[this._colorStackIndex]; + if (currentColor !== this._currentColor) { + this._currentColor = currentColor; + instructionSet.add({ + renderPipeId: "colorMask", + colorMask: currentColor, + canBundle: false + }); + } + this._colorStackIndex++; + } + pop(_mask, _container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + const colorStack = this._colorStack; + this._colorStackIndex--; + const currentColor = colorStack[this._colorStackIndex - 1]; + if (currentColor !== this._currentColor) { + this._currentColor = currentColor; + instructionSet.add({ + renderPipeId: "colorMask", + colorMask: currentColor, + canBundle: false + }); + } + } + execute(_instruction) { + } + destroy() { + this._renderer = null; + this._colorStack = null; + } + } + /** @ignore */ + CanvasColorMaskPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "colorMask" + }; + + "use strict"; + class ColorMaskPipe { + constructor(renderer) { + this._colorStack = []; + this._colorStackIndex = 0; + this._currentColor = 0; + this._renderer = renderer; + } + buildStart() { + this._colorStack[0] = 15; + this._colorStackIndex = 1; + this._currentColor = 15; + } + push(mask, _container, instructionSet) { + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + const colorStack = this._colorStack; + colorStack[this._colorStackIndex] = colorStack[this._colorStackIndex - 1] & mask.mask; + const currentColor = this._colorStack[this._colorStackIndex]; + if (currentColor !== this._currentColor) { + this._currentColor = currentColor; + instructionSet.add({ + renderPipeId: "colorMask", + colorMask: currentColor, + canBundle: false + }); + } + this._colorStackIndex++; + } + pop(_mask, _container, instructionSet) { + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + const colorStack = this._colorStack; + this._colorStackIndex--; + const currentColor = colorStack[this._colorStackIndex - 1]; + if (currentColor !== this._currentColor) { + this._currentColor = currentColor; + instructionSet.add({ + renderPipeId: "colorMask", + colorMask: currentColor, + canBundle: false + }); + } + } + execute(instruction) { + const renderer = this._renderer; + renderer.colorMask.setMask(instruction.colorMask); + } + destroy() { + this._renderer = null; + this._colorStack = null; + } + } + /** @ignore */ + ColorMaskPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "colorMask" + }; + + "use strict"; + + "use strict"; + class ScissorMask { + constructor(mask) { + this.priority = 0; + this.pipe = "scissorMask"; + this.mask = mask; + this.mask.renderable = false; + this.mask.measurable = false; + } + addBounds(bounds, skipUpdateTransform) { + addMaskBounds(this.mask, bounds, skipUpdateTransform); + } + addLocalBounds(bounds, localRoot) { + addMaskLocalBounds(this.mask, bounds, localRoot); + } + containsPoint(point, hitTestFn) { + const mask = this.mask; + return hitTestFn(mask, point); + } + reset() { + if (this.mask === null) return; + this.mask.measurable = true; + this.mask = null; + } + destroy() { + this.reset(); + } + } + + "use strict"; + function buildRoundedRectPath$1(context, x, y, width, height, radius) { + radius = Math.max(0, Math.min(radius, Math.min(width, height) / 2)); + context.moveTo(x + radius, y); + context.lineTo(x + width - radius, y); + context.quadraticCurveTo(x + width, y, x + width, y + radius); + context.lineTo(x + width, y + height - radius); + context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + context.lineTo(x + radius, y + height); + context.quadraticCurveTo(x, y + height, x, y + height - radius); + context.lineTo(x, y + radius); + context.quadraticCurveTo(x, y, x + radius, y); + } + function buildShapePath$1(context, shape) { + switch (shape.type) { + case "rectangle": { + const rect = shape; + context.rect(rect.x, rect.y, rect.width, rect.height); + break; + } + case "roundedRectangle": { + const rect = shape; + buildRoundedRectPath$1(context, rect.x, rect.y, rect.width, rect.height, rect.radius); + break; + } + case "circle": { + const circle = shape; + context.moveTo(circle.x + circle.radius, circle.y); + context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2); + break; + } + case "ellipse": { + const ellipse = shape; + if (context.ellipse) { + context.moveTo(ellipse.x + ellipse.halfWidth, ellipse.y); + context.ellipse(ellipse.x, ellipse.y, ellipse.halfWidth, ellipse.halfHeight, 0, 0, Math.PI * 2); + } else { + context.save(); + context.translate(ellipse.x, ellipse.y); + context.scale(ellipse.halfWidth, ellipse.halfHeight); + context.moveTo(1, 0); + context.arc(0, 0, 1, 0, Math.PI * 2); + context.restore(); + } + break; + } + case "triangle": { + const tri = shape; + context.moveTo(tri.x, tri.y); + context.lineTo(tri.x2, tri.y2); + context.lineTo(tri.x3, tri.y3); + context.closePath(); + break; + } + case "polygon": + default: { + const poly = shape; + const points = poly.points; + if (!(points == null ? void 0 : points.length)) break; + context.moveTo(points[0], points[1]); + for (let i = 2; i < points.length; i += 2) { + context.lineTo(points[i], points[i + 1]); + } + if (poly.closePath) { + context.closePath(); + } + break; + } + } + } + function addHolePaths$1(context, holes) { + if (!(holes == null ? void 0 : holes.length)) return false; + for (let i = 0; i < holes.length; i++) { + const hole = holes[i]; + if (!(hole == null ? void 0 : hole.shape)) continue; + const transform = hole.transform; + const hasTransform = transform && !transform.isIdentity(); + if (hasTransform) { + context.save(); + context.transform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + buildShapePath$1(context, hole.shape); + if (hasTransform) { + context.restore(); + } + } + return true; + } + class CanvasStencilMaskPipe { + constructor(renderer) { + this._warnedMaskTypes = /* @__PURE__ */ new Set(); + this._canvasMaskStack = []; + this._renderer = renderer; + } + push(mask, _container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "stencilMask", + action: "pushMaskBegin", + mask, + inverse: _container._maskOptions.inverse, + canBundle: false + }); + } + pop(_mask, _container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "stencilMask", + action: "popMaskEnd", + mask: _mask, + inverse: _container._maskOptions.inverse, + canBundle: false + }); + } + execute(instruction) { + var _a, _b, _c; + if (instruction.action !== "pushMaskBegin" && instruction.action !== "popMaskEnd") { + return; + } + const canvasRenderer = this._renderer; + const contextSystem = canvasRenderer.canvasContext; + const context = contextSystem == null ? void 0 : contextSystem.activeContext; + if (!context) return; + if (instruction.action === "popMaskEnd") { + const didClip = this._canvasMaskStack.pop(); + if (didClip) { + context.restore(); + } + return; + } + if (instruction.inverse) { + this._warnOnce( + "inverse", + "CanvasRenderer: inverse masks are not supported on Canvas2D; ignoring inverse flag." + ); + } + const maskContainer = instruction.mask.mask; + if (!(maskContainer instanceof Graphics)) { + this._warnOnce( + "nonGraphics", + "CanvasRenderer: only Graphics masks are supported in Canvas2D; skipping mask." + ); + this._canvasMaskStack.push(false); + return; + } + const graphics = maskContainer; + const instructions = (_a = graphics.context) == null ? void 0 : _a.instructions; + if (!(instructions == null ? void 0 : instructions.length)) { + this._canvasMaskStack.push(false); + return; + } + context.save(); + contextSystem.setContextTransform( + graphics.groupTransform, + (canvasRenderer._roundPixels | graphics._roundPixels) === 1 + ); + context.beginPath(); + let drewPath = false; + let hasHoles = false; + for (let i = 0; i < instructions.length; i++) { + const instructionData = instructions[i]; + const action = instructionData.action; + if (action !== "fill" && action !== "stroke") continue; + const data = instructionData.data; + const shapePath = (_b = data == null ? void 0 : data.path) == null ? void 0 : _b.shapePath; + if (!((_c = shapePath == null ? void 0 : shapePath.shapePrimitives) == null ? void 0 : _c.length)) continue; + const shapePrimitives = shapePath.shapePrimitives; + for (let j = 0; j < shapePrimitives.length; j++) { + const primitive = shapePrimitives[j]; + if (!(primitive == null ? void 0 : primitive.shape)) continue; + const transform = primitive.transform; + const hasTransform = transform && !transform.isIdentity(); + if (hasTransform) { + context.save(); + context.transform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + buildShapePath$1(context, primitive.shape); + hasHoles = addHolePaths$1(context, primitive.holes) || hasHoles; + drewPath = true; + if (hasTransform) { + context.restore(); + } + } + } + if (!drewPath) { + context.restore(); + this._canvasMaskStack.push(false); + return; + } + if (hasHoles) { + context.clip("evenodd"); + } else { + context.clip(); + } + this._canvasMaskStack.push(true); + } + destroy() { + this._renderer = null; + this._warnedMaskTypes = null; + this._canvasMaskStack = null; + } + _warnOnce(key, message) { + if (this._warnedMaskTypes.has(key)) return; + this._warnedMaskTypes.add(key); + warn(message); + } + } + CanvasStencilMaskPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "stencilMask" + }; + + "use strict"; + class StencilMaskPipe { + constructor(renderer) { + // used when building and also when executing.. + this._maskStackHash = {}; + this._maskHash = /* @__PURE__ */ new WeakMap(); + this._renderer = renderer; + } + push(mask, _container, instructionSet) { + var _a, _b; + const effect = mask; + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + renderer.renderPipes.blendMode.setBlendMode(effect.mask, "none", instructionSet); + instructionSet.add({ + renderPipeId: "stencilMask", + action: "pushMaskBegin", + mask, + inverse: _container._maskOptions.inverse, + canBundle: false + }); + const maskContainer = effect.mask; + maskContainer.includeInBuild = true; + if (!this._maskHash.has(effect)) { + this._maskHash.set(effect, { + instructionsStart: 0, + instructionsLength: 0 + }); + } + const maskData = this._maskHash.get(effect); + maskData.instructionsStart = instructionSet.instructionSize; + maskContainer.collectRenderables( + instructionSet, + renderer, + null + ); + maskContainer.includeInBuild = false; + renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "stencilMask", + action: "pushMaskEnd", + mask, + inverse: _container._maskOptions.inverse, + canBundle: false + }); + const instructionsLength = instructionSet.instructionSize - maskData.instructionsStart - 1; + maskData.instructionsLength = instructionsLength; + const renderTargetUid = renderer.renderTarget.renderTarget.uid; + (_b = (_a = this._maskStackHash)[renderTargetUid]) != null ? _b : _a[renderTargetUid] = 0; + } + pop(mask, _container, instructionSet) { + const effect = mask; + const renderer = this._renderer; + renderer.renderPipes.batch.break(instructionSet); + renderer.renderPipes.blendMode.setBlendMode(effect.mask, "none", instructionSet); + instructionSet.add({ + renderPipeId: "stencilMask", + action: "popMaskBegin", + inverse: _container._maskOptions.inverse, + canBundle: false + }); + const maskData = this._maskHash.get(mask); + for (let i = 0; i < maskData.instructionsLength; i++) { + instructionSet.instructions[instructionSet.instructionSize++] = instructionSet.instructions[maskData.instructionsStart++]; + } + instructionSet.add({ + renderPipeId: "stencilMask", + action: "popMaskEnd", + canBundle: false + }); + } + execute(instruction) { + var _a, _b; + const renderer = this._renderer; + const gpuRenderer = renderer; + const renderTargetUid = renderer.renderTarget.renderTarget.uid; + let maskStackIndex = (_b = (_a = this._maskStackHash)[renderTargetUid]) != null ? _b : _a[renderTargetUid] = 0; + if (instruction.action === "pushMaskBegin") { + gpuRenderer.renderTarget.ensureDepthStencil(); + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.RENDERING_MASK_ADD, maskStackIndex); + maskStackIndex++; + gpuRenderer.colorMask.setMask(0); + } else if (instruction.action === "pushMaskEnd") { + if (instruction.inverse) { + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.INVERSE_MASK_ACTIVE, maskStackIndex); + } else { + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.MASK_ACTIVE, maskStackIndex); + } + gpuRenderer.colorMask.setMask(15); + } else if (instruction.action === "popMaskBegin") { + gpuRenderer.colorMask.setMask(0); + if (maskStackIndex !== 0) { + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.RENDERING_MASK_REMOVE, maskStackIndex); + } else { + gpuRenderer.renderTarget.clear(null, CLEAR.STENCIL); + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.DISABLED, maskStackIndex); + } + maskStackIndex--; + } else if (instruction.action === "popMaskEnd") { + if (instruction.inverse) { + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.INVERSE_MASK_ACTIVE, maskStackIndex); + } else { + gpuRenderer.stencil.setStencilMode(STENCIL_MODES.MASK_ACTIVE, maskStackIndex); + } + gpuRenderer.colorMask.setMask(15); + } + this._maskStackHash[renderTargetUid] = maskStackIndex; + } + destroy() { + this._renderer = null; + this._maskStackHash = null; + this._maskHash = null; + } + } + StencilMaskPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "stencilMask" + }; + + "use strict"; + + "use strict"; + const FALLBACK_BLEND = "source-over"; + function mapCanvasBlendModesToPixi() { + const supportsAdvanced = canUseNewCanvasBlendModes(); + const map = /* @__PURE__ */ Object.create(null); + map.inherit = FALLBACK_BLEND; + map.none = FALLBACK_BLEND; + map.normal = "source-over"; + map.add = "lighter"; + map.multiply = supportsAdvanced ? "multiply" : FALLBACK_BLEND; + map.screen = supportsAdvanced ? "screen" : FALLBACK_BLEND; + map.overlay = supportsAdvanced ? "overlay" : FALLBACK_BLEND; + map.darken = supportsAdvanced ? "darken" : FALLBACK_BLEND; + map.lighten = supportsAdvanced ? "lighten" : FALLBACK_BLEND; + map["color-dodge"] = supportsAdvanced ? "color-dodge" : FALLBACK_BLEND; + map["color-burn"] = supportsAdvanced ? "color-burn" : FALLBACK_BLEND; + map["hard-light"] = supportsAdvanced ? "hard-light" : FALLBACK_BLEND; + map["soft-light"] = supportsAdvanced ? "soft-light" : FALLBACK_BLEND; + map.difference = supportsAdvanced ? "difference" : FALLBACK_BLEND; + map.exclusion = supportsAdvanced ? "exclusion" : FALLBACK_BLEND; + map.saturation = supportsAdvanced ? "saturation" : FALLBACK_BLEND; + map.color = supportsAdvanced ? "color" : FALLBACK_BLEND; + map.luminosity = supportsAdvanced ? "luminosity" : FALLBACK_BLEND; + map["linear-burn"] = supportsAdvanced ? "color-burn" : FALLBACK_BLEND; + map["linear-dodge"] = supportsAdvanced ? "color-dodge" : FALLBACK_BLEND; + map["linear-light"] = supportsAdvanced ? "hard-light" : FALLBACK_BLEND; + map["pin-light"] = supportsAdvanced ? "hard-light" : FALLBACK_BLEND; + map["vivid-light"] = supportsAdvanced ? "hard-light" : FALLBACK_BLEND; + map["hard-mix"] = FALLBACK_BLEND; + map.negation = supportsAdvanced ? "difference" : FALLBACK_BLEND; + map["normal-npm"] = map.normal; + map["add-npm"] = map.add; + map["screen-npm"] = map.screen; + map.erase = "destination-out"; + map.subtract = FALLBACK_BLEND; + map.divide = FALLBACK_BLEND; + map.min = FALLBACK_BLEND; + map.max = FALLBACK_BLEND; + return map; + } + + "use strict"; + const tempMatrix$3 = new Matrix(); + class CanvasContextSystem { + /** + * @param renderer - The owning CanvasRenderer. + */ + constructor(renderer) { + /** Resolution of the active context. */ + this.activeResolution = 1; + /** The image smoothing property to toggle for this browser. */ + this.smoothProperty = "imageSmoothingEnabled"; + /** Map of Pixi blend modes to canvas composite operations. */ + this.blendModes = mapCanvasBlendModesToPixi(); + /** Current canvas blend mode. */ + this._activeBlendMode = "normal"; + /** Optional projection transform for render targets. */ + this._projTransform = null; + /** True when external blend mode control is in use. */ + this._outerBlend = false; + /** Tracks unsupported blend mode warnings to avoid spam. */ + this._warnedBlendModes = /* @__PURE__ */ new Set(); + this._renderer = renderer; + } + resolutionChange(resolution) { + this.activeResolution = resolution; + } + /** Initializes the root context and smoothing flag selection. */ + init() { + const alpha = this._renderer.background.alpha < 1; + this.rootContext = this._renderer.canvas.getContext( + "2d", + { alpha } + ); + this.activeContext = this.rootContext; + this.activeResolution = this._renderer.resolution; + if (!this.rootContext.imageSmoothingEnabled) { + const rc = this.rootContext; + if (rc.webkitImageSmoothingEnabled) { + this.smoothProperty = "webkitImageSmoothingEnabled"; + } else if (rc.mozImageSmoothingEnabled) { + this.smoothProperty = "mozImageSmoothingEnabled"; + } else if (rc.oImageSmoothingEnabled) { + this.smoothProperty = "oImageSmoothingEnabled"; + } else if (rc.msImageSmoothingEnabled) { + this.smoothProperty = "msImageSmoothingEnabled"; + } + } + } + /** + * Sets the current transform on the active context. + * @param transform - Transform to apply. + * @param roundPixels - Whether to round translation to integers. + * @param localResolution - Optional local resolution multiplier. + * @param skipGlobalTransform - If true, skip applying the global world transform matrix. + */ + setContextTransform(transform, roundPixels, localResolution, skipGlobalTransform) { + var _a; + const globalTransform = skipGlobalTransform ? Matrix.IDENTITY : ((_a = this._renderer.globalUniforms.globalUniformData) == null ? void 0 : _a.worldTransformMatrix) || Matrix.IDENTITY; + let mat = tempMatrix$3; + mat.copyFrom(globalTransform); + mat.append(transform); + const proj = this._projTransform; + const contextResolution = this.activeResolution; + localResolution = localResolution || contextResolution; + if (proj) { + const finalMat = Matrix.shared; + finalMat.copyFrom(mat); + finalMat.prepend(proj); + mat = finalMat; + } + if (roundPixels) { + this.activeContext.setTransform( + mat.a * localResolution, + mat.b * localResolution, + mat.c * localResolution, + mat.d * localResolution, + mat.tx * contextResolution | 0, + mat.ty * contextResolution | 0 + ); + } else { + this.activeContext.setTransform( + mat.a * localResolution, + mat.b * localResolution, + mat.c * localResolution, + mat.d * localResolution, + mat.tx * contextResolution, + mat.ty * contextResolution + ); + } + } + /** + * Clears the current render target, optionally filling with a color. + * @param clearColor - Color to fill after clearing. + * @param alpha - Alpha override for the clear color. + */ + clear(clearColor, alpha) { + const context = this.activeContext; + const renderer = this._renderer; + context.clearRect(0, 0, renderer.width, renderer.height); + if (clearColor) { + const color = Color.shared.setValue(clearColor); + context.globalAlpha = alpha != null ? alpha : color.alpha; + context.fillStyle = color.toHex(); + context.fillRect(0, 0, renderer.width, renderer.height); + context.globalAlpha = 1; + } + } + /** + * Sets the active blend mode. + * @param blendMode - Pixi blend mode. + */ + setBlendMode(blendMode) { + if (this._activeBlendMode === blendMode) return; + this._activeBlendMode = blendMode; + this._outerBlend = false; + const mappedBlend = this.blendModes[blendMode]; + if (!mappedBlend) { + if (!this._warnedBlendModes.has(blendMode)) { + console.warn( + `CanvasRenderer: blend mode "${blendMode}" is not supported in Canvas2D; falling back to "source-over".` + ); + this._warnedBlendModes.add(blendMode); + } + this.activeContext.globalCompositeOperation = "source-over"; + return; + } + this.activeContext.globalCompositeOperation = mappedBlend; + } + /** Releases context references. */ + destroy() { + this.rootContext = null; + this.activeContext = null; + this._warnedBlendModes.clear(); + } + } + /** @ignore */ + CanvasContextSystem.extension = { + type: [ + ExtensionType.CanvasSystem + ], + name: "canvasContext" + }; + + "use strict"; + function isCanvasFilterCapable(filter) { + return typeof filter.getCanvasFilterString === "function"; + } + class CanvasFilterFrame { + constructor() { + this.skip = false; + this.useClip = false; + this.filters = null; + this.container = null; + this.bounds = new Bounds(); + this.cssFilterString = ""; + } + } + class CanvasFilterSystem { + /** + * @param renderer - The Canvas renderer + * @param renderer.canvasContext + * @param renderer.canvasContext.activeContext + * @param renderer.canvasContext.activeResolution + */ + constructor(renderer) { + this._filterStack = []; + this._filterStackIndex = 0; + this._savedStates = []; + this._alphaMultiplier = 1; + this._warnedFilterTypes = /* @__PURE__ */ new Set(); + this.renderer = renderer; + } + /** + * Push a filter instruction onto the stack. + * Called when entering a filtered container. + * @param instruction - The filter instruction from FilterPipe + */ + push(instruction) { + const filterFrame = this._pushFilterFrame(); + const filters = instruction.filterEffect.filters; + filterFrame.skip = false; + filterFrame.useClip = false; + filterFrame.filters = filters; + filterFrame.container = instruction.container; + filterFrame.cssFilterString = ""; + if (filters.every((filter) => !filter.enabled)) { + filterFrame.skip = true; + return; + } + const cssFilters = []; + const alphaMultiplier = 1; + for (const filter of filters) { + if (!filter.enabled) continue; + if (!isCanvasFilterCapable(filter)) { + this._warnUnsupportedFilter(filter); + continue; + } + const cssString = filter.getCanvasFilterString(); + if (cssString === null) { + this._warnUnsupportedFilter(filter); + continue; + } + if (cssString) { + cssFilters.push(cssString); + } + } + if (cssFilters.length === 0 && alphaMultiplier === 1) { + filterFrame.skip = true; + return; + } + filterFrame.cssFilterString = cssFilters.join(" "); + this._calculateFilterArea(instruction, filterFrame.bounds); + filterFrame.useClip = !!instruction.filterEffect.filterArea; + const context = this.renderer.canvasContext.activeContext; + const previousFilter = context.filter || "none"; + this._savedStates.push({ filter: previousFilter, alphaMultiplier: this._alphaMultiplier }); + if (filterFrame.useClip && Number.isFinite(filterFrame.bounds.width) && Number.isFinite(filterFrame.bounds.height) && filterFrame.bounds.width > 0 && filterFrame.bounds.height > 0) { + const resolution = this.renderer.canvasContext.activeResolution || 1; + context.save(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.beginPath(); + context.rect( + filterFrame.bounds.x * resolution, + filterFrame.bounds.y * resolution, + filterFrame.bounds.width * resolution, + filterFrame.bounds.height * resolution + ); + context.clip(); + } else { + filterFrame.useClip = false; + } + if (alphaMultiplier !== 1) { + this._alphaMultiplier *= alphaMultiplier; + } + if (filterFrame.cssFilterString) { + context.filter = previousFilter !== "none" ? `${previousFilter} ${filterFrame.cssFilterString}` : filterFrame.cssFilterString; + } + } + /** Pop a filter from the stack. Called when exiting a filtered container. */ + pop() { + const filterFrame = this._popFilterFrame(); + if (filterFrame.skip) { + return; + } + const savedState = this._savedStates.pop(); + if (!savedState) { + return; + } + const context = this.renderer.canvasContext.activeContext; + if (filterFrame.useClip) { + context.restore(); + } else { + context.filter = savedState.filter; + } + this._alphaMultiplier = savedState.alphaMultiplier; + } + /** + * Applies supported filters to a texture and returns a new texture. + * Unsupported filters are skipped with a warn-once message. + * @param params - The parameters for applying filters. + * @param params.texture + * @param params.filters + * @returns The resulting texture after filters are applied. + */ + generateFilteredTexture({ texture, filters }) { + var _a, _b; + if (!(filters == null ? void 0 : filters.length) || filters.every((filter) => !filter.enabled)) { + return texture; + } + const cssFilters = []; + const alphaMultiplier = 1; + for (const filter of filters) { + if (!filter.enabled) continue; + if (!isCanvasFilterCapable(filter)) { + this._warnUnsupportedFilter(filter); + continue; + } + const cssString = filter.getCanvasFilterString(); + if (cssString === null) { + this._warnUnsupportedFilter(filter); + continue; + } + if (cssString) { + cssFilters.push(cssString); + } + } + if (cssFilters.length === 0 && alphaMultiplier === 1) { + return texture; + } + const source = canvasUtils.getCanvasSource(texture); + if (!source) { + return texture; + } + const frame = texture.frame; + const resolution = (_b = (_a = texture.source._resolution) != null ? _a : texture.source.resolution) != null ? _b : 1; + const width = frame.width; + const height = frame.height; + const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(width, height, resolution); + const { canvas, context } = canvasAndContext; + context.setTransform(1, 0, 0, 1, 0, 0); + context.clearRect(0, 0, canvas.width, canvas.height); + if (cssFilters.length) { + context.filter = cssFilters.join(" "); + } + if (alphaMultiplier !== 1) { + context.globalAlpha = alphaMultiplier; + } + const sx = frame.x * resolution; + const sy = frame.y * resolution; + const sw = width * resolution; + const sh = height * resolution; + context.drawImage( + source, + sx, + sy, + sw, + sh, + 0, + 0, + sw, + sh + ); + context.filter = "none"; + context.globalAlpha = 1; + return getPo2TextureFromSource(canvas, width, height, resolution); + } + /** + * Calculate the filter area bounds. + * @param instruction - Filter instruction + * @param bounds - Bounds object to populate + */ + _calculateFilterArea(instruction, bounds) { + if (instruction.renderables) { + getGlobalRenderableBounds(instruction.renderables, bounds); + } else if (instruction.filterEffect.filterArea) { + bounds.clear(); + bounds.addRect(instruction.filterEffect.filterArea); + bounds.applyMatrix(instruction.container.worldTransform); + } else { + instruction.container.getFastGlobalBounds(true, bounds); + } + if (instruction.container) { + const renderGroup = instruction.container.renderGroup || instruction.container.parentRenderGroup; + const filterFrameTransform = renderGroup == null ? void 0 : renderGroup.cacheToLocalTransform; + if (filterFrameTransform) { + bounds.applyMatrix(filterFrameTransform); + } + } + } + _warnUnsupportedFilter(filter) { + var _a; + const filterName = ((_a = filter == null ? void 0 : filter.constructor) == null ? void 0 : _a.name) || "Filter"; + if (this._warnedFilterTypes.has(filterName)) { + return; + } + this._warnedFilterTypes.add(filterName); + console.warn( + `CanvasRenderer: filter "${filterName}" is not supported in Canvas2D and will be skipped.` + ); + } + get alphaMultiplier() { + return this._alphaMultiplier; + } + _pushFilterFrame() { + let filterFrame = this._filterStack[this._filterStackIndex]; + if (!filterFrame) { + filterFrame = this._filterStack[this._filterStackIndex] = new CanvasFilterFrame(); + } + this._filterStackIndex++; + return filterFrame; + } + _popFilterFrame() { + if (this._filterStackIndex <= 0) { + return this._filterStack[0]; + } + this._filterStackIndex--; + return this._filterStack[this._filterStackIndex]; + } + /** Destroys the system */ + destroy() { + this._filterStack = null; + this._savedStates = null; + this._warnedFilterTypes = null; + this._alphaMultiplier = 1; + } + } + /** @ignore */ + CanvasFilterSystem.extension = { + type: [ExtensionType.CanvasSystem], + name: "filter" + }; + extensions.add(CanvasFilterSystem); + + "use strict"; + class CanvasLimitsSystem { + constructor() { + this.maxTextures = 16; + this.maxBatchableTextures = 16; + this.maxUniformBindings = 0; + } + init() { + } + } + /** @ignore */ + CanvasLimitsSystem.extension = { + type: [ + ExtensionType.CanvasSystem + ], + name: "limits" + }; + + "use strict"; + class CustomRenderPipe { + constructor(renderer) { + this._renderer = renderer; + } + updateRenderable() { + } + destroyRenderable() { + } + validateRenderable() { + return false; + } + addRenderable(container, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(container); + } + execute(container) { + if (!container.isRenderable) return; + container.render(this._renderer); + } + destroy() { + this._renderer = null; + } + } + CustomRenderPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "customRender" + }; + + "use strict"; + function executeInstructions(renderGroup, renderer) { + const instructionSet = renderGroup.instructionSet; + const instructions = instructionSet.instructions; + for (let i = 0; i < instructionSet.instructionSize; i++) { + const instruction = instructions[i]; + renderer[instruction.renderPipeId].execute(instruction); + } + } + + "use strict"; + class RenderGroupPipe { + constructor(renderer) { + this._renderer = renderer; + } + addRenderGroup(renderGroup, instructionSet) { + if (renderGroup.isCachedAsTexture) { + this._addRenderableCacheAsTexture(renderGroup, instructionSet); + } else { + this._addRenderableDirect(renderGroup, instructionSet); + } + } + execute(renderGroup) { + if (!renderGroup.isRenderable) return; + if (renderGroup.isCachedAsTexture) { + this._executeCacheAsTexture(renderGroup); + } else { + this._executeDirect(renderGroup); + } + } + destroy() { + this._renderer = null; + } + _addRenderableDirect(renderGroup, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + if (renderGroup._batchableRenderGroup) { + BigPool.return(renderGroup._batchableRenderGroup); + renderGroup._batchableRenderGroup = null; + } + instructionSet.add(renderGroup); + } + _addRenderableCacheAsTexture(renderGroup, instructionSet) { + var _a; + const batchableRenderGroup = (_a = renderGroup._batchableRenderGroup) != null ? _a : renderGroup._batchableRenderGroup = BigPool.get(BatchableSprite); + batchableRenderGroup.renderable = renderGroup.root; + batchableRenderGroup.transform = renderGroup.root.relativeGroupTransform; + batchableRenderGroup.texture = renderGroup.texture; + batchableRenderGroup.bounds = renderGroup._textureBounds; + instructionSet.add(renderGroup); + this._renderer.renderPipes.blendMode.pushBlendMode(renderGroup, renderGroup.root.groupBlendMode, instructionSet); + this._renderer.renderPipes.batch.addToBatch(batchableRenderGroup, instructionSet); + this._renderer.renderPipes.blendMode.popBlendMode(instructionSet); + } + _executeCacheAsTexture(renderGroup) { + if (renderGroup.textureNeedsUpdate) { + renderGroup.textureNeedsUpdate = false; + const worldTransformMatrix = new Matrix().translate( + -renderGroup._textureBounds.x, + -renderGroup._textureBounds.y + ); + this._renderer.renderTarget.push(renderGroup.texture, true, null, renderGroup.texture.frame); + this._renderer.globalUniforms.push({ + worldTransformMatrix, + worldColor: 4294967295, + offset: { x: 0, y: 0 } + }); + executeInstructions(renderGroup, this._renderer.renderPipes); + this._renderer.renderTarget.finishRenderPass(); + this._renderer.renderTarget.pop(); + this._renderer.globalUniforms.pop(); + } + renderGroup._batchableRenderGroup._batcher.updateElement(renderGroup._batchableRenderGroup); + renderGroup._batchableRenderGroup._batcher.geometry.buffers[0].update(); + } + _executeDirect(renderGroup) { + this._renderer.globalUniforms.push({ + worldTransformMatrix: renderGroup.inverseParentTextureTransform, + worldColor: renderGroup.worldColorAlpha + }); + executeInstructions(renderGroup, this._renderer.renderPipes); + this._renderer.globalUniforms.pop(); + } + } + RenderGroupPipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "renderGroup" + }; + + "use strict"; + const emptyCanvasStyle = "#808080"; + const tempMatrix$2 = new Matrix(); + const tempTextureMatrix = new Matrix(); + const tempGradientMatrix = new Matrix(); + const tempPatternMatrix = new Matrix(); + function fillTriangles(context, vertices, indices) { + context.beginPath(); + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 2; + const i1 = indices[i + 1] * 2; + const i2 = indices[i + 2] * 2; + context.moveTo(vertices[i0], vertices[i0 + 1]); + context.lineTo(vertices[i1], vertices[i1 + 1]); + context.lineTo(vertices[i2], vertices[i2 + 1]); + context.closePath(); + } + context.fill(); + } + function colorToHex(color) { + const clamped = color & 16777215; + return `#${clamped.toString(16).padStart(6, "0")}`; + } + function buildRoundedRectPath(context, x, y, width, height, radius) { + radius = Math.max(0, Math.min(radius, Math.min(width, height) / 2)); + context.moveTo(x + radius, y); + context.lineTo(x + width - radius, y); + context.quadraticCurveTo(x + width, y, x + width, y + radius); + context.lineTo(x + width, y + height - radius); + context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + context.lineTo(x + radius, y + height); + context.quadraticCurveTo(x, y + height, x, y + height - radius); + context.lineTo(x, y + radius); + context.quadraticCurveTo(x, y, x + radius, y); + } + function buildShapePath(context, shape) { + switch (shape.type) { + case "rectangle": { + const rect = shape; + context.rect(rect.x, rect.y, rect.width, rect.height); + break; + } + case "roundedRectangle": { + const rect = shape; + buildRoundedRectPath(context, rect.x, rect.y, rect.width, rect.height, rect.radius); + break; + } + case "circle": { + const circle = shape; + context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2); + break; + } + case "ellipse": { + const ellipse = shape; + if (context.ellipse) { + context.ellipse(ellipse.x, ellipse.y, ellipse.halfWidth, ellipse.halfHeight, 0, 0, Math.PI * 2); + } else { + context.save(); + context.translate(ellipse.x, ellipse.y); + context.scale(ellipse.halfWidth, ellipse.halfHeight); + context.arc(0, 0, 1, 0, Math.PI * 2); + context.restore(); + } + break; + } + case "triangle": { + const tri = shape; + context.moveTo(tri.x, tri.y); + context.lineTo(tri.x2, tri.y2); + context.lineTo(tri.x3, tri.y3); + context.closePath(); + break; + } + case "polygon": + default: { + const poly = shape; + const points = poly.points; + if (!(points == null ? void 0 : points.length)) break; + context.moveTo(points[0], points[1]); + for (let i = 2; i < points.length; i += 2) { + context.lineTo(points[i], points[i + 1]); + } + if (poly.closePath) { + context.closePath(); + } + break; + } + } + } + function addHolePaths(context, holes) { + if (!(holes == null ? void 0 : holes.length)) return false; + for (let i = 0; i < holes.length; i++) { + const hole = holes[i]; + if (!(hole == null ? void 0 : hole.shape)) continue; + const transform = hole.transform; + const hasTransform = transform && !transform.isIdentity(); + if (hasTransform) { + context.save(); + context.transform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + buildShapePath(context, hole.shape); + if (hasTransform) { + context.restore(); + } + } + return true; + } + function getCanvasStyle(style, tint, textureMatrix, currentTransform) { + const fill = style.fill; + if (fill instanceof FillGradient) { + fill.buildGradient(); + const gradientTexture = fill.texture; + if (gradientTexture) { + const pattern = canvasUtils.getTintedPattern(gradientTexture, tint); + const patternMatrix = textureMatrix ? tempPatternMatrix.copyFrom(textureMatrix).scale(gradientTexture.source.pixelWidth, gradientTexture.source.pixelHeight) : tempPatternMatrix.copyFrom(fill.transform); + if (currentTransform && !style.textureSpace) { + patternMatrix.append(currentTransform); + } + canvasUtils.applyPatternTransform(pattern, patternMatrix); + return pattern; + } + } + if (fill instanceof FillPattern) { + const pattern = canvasUtils.getTintedPattern(fill.texture, tint); + canvasUtils.applyPatternTransform(pattern, fill.transform); + return pattern; + } + const texture = style.texture; + if (texture && texture !== Texture.WHITE) { + if (!texture.source.resource) { + return emptyCanvasStyle; + } + const pattern = canvasUtils.getTintedPattern(texture, tint); + const patternMatrix = textureMatrix ? tempPatternMatrix.copyFrom(textureMatrix).scale(texture.source.pixelWidth, texture.source.pixelHeight) : style.matrix; + canvasUtils.applyPatternTransform(pattern, patternMatrix); + return pattern; + } + return colorToHex(tint); + } + class CanvasGraphicsAdaptor { + constructor() { + this.shader = null; + } + contextChange(renderer) { + void renderer; + } + execute(graphicsPipe, renderable) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l; + const renderer = graphicsPipe.renderer; + const contextSystem = renderer.canvasContext; + const context = contextSystem.activeContext; + const baseTransform = renderable.groupTransform; + const globalColor = (_b = (_a = renderer.globalUniforms.globalUniformData) == null ? void 0 : _a.worldColor) != null ? _b : 4294967295; + const groupColorAlpha = renderable.groupColorAlpha; + const globalAlpha = (globalColor >>> 24 & 255) / 255; + const groupAlphaValue = (groupColorAlpha >>> 24 & 255) / 255; + const filterAlpha = (_d = (_c = renderer.filter) == null ? void 0 : _c.alphaMultiplier) != null ? _d : 1; + const groupAlpha = globalAlpha * groupAlphaValue * filterAlpha; + if (groupAlpha <= 0) return; + const globalTint = globalColor & 16777215; + const groupTintBGR = groupColorAlpha & 16777215; + const groupTint = bgr2rgb(multiplyHexColors(groupTintBGR, globalTint)); + const roundPixels = renderer._roundPixels | renderable._roundPixels; + context.save(); + contextSystem.setContextTransform(baseTransform, roundPixels === 1); + contextSystem.setBlendMode(renderable.groupBlendMode); + const instructions = renderable.context.instructions; + for (let i = 0; i < instructions.length; i++) { + const instruction = instructions[i]; + if (instruction.action === "texture") { + const data2 = instruction.data; + const texture = data2.image; + const source = texture ? canvasUtils.getCanvasSource(texture) : null; + if (!source) continue; + const alpha2 = data2.alpha * groupAlpha; + if (alpha2 <= 0) continue; + const tint2 = multiplyHexColors(data2.style, groupTint); + context.globalAlpha = alpha2; + let drawSource = source; + if (tint2 !== 16777215) { + drawSource = canvasUtils.getTintedCanvas({ texture }, tint2); + } + const frame = texture.frame; + const resolution = (_f = (_e = texture.source._resolution) != null ? _e : texture.source.resolution) != null ? _f : 1; + let sx = frame.x * resolution; + let sy = frame.y * resolution; + const sw = frame.width * resolution; + const sh = frame.height * resolution; + if (drawSource !== source) { + sx = 0; + sy = 0; + } + const transform = data2.transform; + const hasTransform = transform && !transform.isIdentity(); + const rotate = texture.rotate; + if (hasTransform || rotate) { + tempMatrix$2.copyFrom(baseTransform); + if (hasTransform) { + tempMatrix$2.append(transform); + } + if (rotate) { + groupD8.matrixAppendRotationInv(tempMatrix$2, rotate, data2.dx, data2.dy, data2.dw, data2.dh); + } + contextSystem.setContextTransform(tempMatrix$2, roundPixels === 1); + } else { + contextSystem.setContextTransform(baseTransform, roundPixels === 1); + } + context.drawImage( + drawSource, + sx, + sy, + drawSource === source ? sw : drawSource.width, + drawSource === source ? sh : drawSource.height, + rotate ? 0 : data2.dx, + rotate ? 0 : data2.dy, + data2.dw, + data2.dh + ); + if (hasTransform || rotate) { + contextSystem.setContextTransform(baseTransform, roundPixels === 1); + } + continue; + } + const data = instruction.data; + const shapePath = (_g = data == null ? void 0 : data.path) == null ? void 0 : _g.shapePath; + if (!((_h = shapePath == null ? void 0 : shapePath.shapePrimitives) == null ? void 0 : _h.length)) continue; + const style = data.style; + const tint = multiplyHexColors(style.color, groupTint); + const alpha = style.alpha * groupAlpha; + if (alpha <= 0) continue; + const isStroke = instruction.action === "stroke"; + context.globalAlpha = alpha; + if (isStroke) { + const strokeStyle = style; + context.lineWidth = strokeStyle.width; + context.lineCap = strokeStyle.cap; + context.lineJoin = strokeStyle.join; + context.miterLimit = strokeStyle.miterLimit; + } + const shapePrimitives = shapePath.shapePrimitives; + if (!isStroke && ((_k = (_j = (_i = data.hole) == null ? void 0 : _i.shapePath) == null ? void 0 : _j.shapePrimitives) == null ? void 0 : _k.length)) { + const lastShape = shapePrimitives[shapePrimitives.length - 1]; + lastShape.holes = data.hole.shapePath.shapePrimitives; + } + for (let j = 0; j < shapePrimitives.length; j++) { + const primitive = shapePrimitives[j]; + if (!(primitive == null ? void 0 : primitive.shape)) continue; + const transform = primitive.transform; + const hasTransform = transform && !transform.isIdentity(); + const hasTexture = style.texture && style.texture !== Texture.WHITE; + const textureTransform = style.textureSpace === "global" ? transform : null; + const textureMatrix = hasTexture ? generateTextureMatrix(tempTextureMatrix, style, primitive.shape, textureTransform) : null; + const currentTransform = hasTransform ? tempGradientMatrix.copyFrom(baseTransform).append(transform) : baseTransform; + const canvasStyle = getCanvasStyle( + style, + tint, + textureMatrix, + currentTransform + ); + if (hasTransform) { + context.save(); + context.transform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + if (isStroke) { + const strokeStyle = style; + const useStrokeGeometry = strokeStyle.alignment !== 0.5 && !strokeStyle.pixelLine; + if (useStrokeGeometry) { + const points = []; + const vertices = []; + const indices = []; + const shapeBuilder = shapeBuilders[primitive.shape.type]; + if (shapeBuilder == null ? void 0 : shapeBuilder.build(primitive.shape, points)) { + const close = (_l = primitive.shape.closePath) != null ? _l : true; + buildLine(points, strokeStyle, false, close, vertices, indices); + context.fillStyle = canvasStyle; + fillTriangles(context, vertices, indices); + } else { + context.strokeStyle = canvasStyle; + context.beginPath(); + buildShapePath(context, primitive.shape); + context.stroke(); + } + } else { + context.strokeStyle = canvasStyle; + context.beginPath(); + buildShapePath(context, primitive.shape); + context.stroke(); + } + } else { + context.fillStyle = canvasStyle; + context.beginPath(); + buildShapePath(context, primitive.shape); + const hasHoles = addHolePaths(context, primitive.holes); + if (hasHoles) { + context.fill("evenodd"); + } else { + context.fill(); + } + } + if (hasTransform) { + context.restore(); + } + } + } + context.restore(); + } + destroy() { + this.shader = null; + } + } + /** @ignore */ + CanvasGraphicsAdaptor.extension = { + type: [ + ExtensionType.CanvasPipesAdaptor + ], + name: "graphics" + }; + + "use strict"; + class SpritePipe { + constructor(renderer) { + this._renderer = renderer; + } + addRenderable(sprite, instructionSet) { + const gpuSprite = this._getGpuSprite(sprite); + if (sprite.didViewUpdate) this._updateBatchableSprite(sprite, gpuSprite); + this._renderer.renderPipes.batch.addToBatch(gpuSprite, instructionSet); + } + updateRenderable(sprite) { + const gpuSprite = this._getGpuSprite(sprite); + if (sprite.didViewUpdate) this._updateBatchableSprite(sprite, gpuSprite); + gpuSprite._batcher.updateElement(gpuSprite); + } + validateRenderable(sprite) { + const gpuSprite = this._getGpuSprite(sprite); + return !gpuSprite._batcher.checkAndUpdateTexture( + gpuSprite, + sprite._texture + ); + } + _updateBatchableSprite(sprite, batchableSprite) { + batchableSprite.bounds = sprite.visualBounds; + batchableSprite.texture = sprite._texture; + } + _getGpuSprite(sprite) { + return sprite._gpuData[this._renderer.uid] || this._initGPUSprite(sprite); + } + _initGPUSprite(sprite) { + const batchableSprite = new BatchableSprite(); + batchableSprite.renderable = sprite; + batchableSprite.transform = sprite.groupTransform; + batchableSprite.texture = sprite._texture; + batchableSprite.bounds = sprite.visualBounds; + batchableSprite.roundPixels = this._renderer._roundPixels | sprite._roundPixels; + sprite._gpuData[this._renderer.uid] = batchableSprite; + return batchableSprite; + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + SpritePipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "sprite" + }; + + "use strict"; + const BLEND_MODE_FILTERS = {}; + extensions.handle(ExtensionType.BlendMode, (value) => { + if (!value.name) { + throw new Error("BlendMode extension must have a name property"); + } + BLEND_MODE_FILTERS[value.name] = value.ref; + }, (value) => { + delete BLEND_MODE_FILTERS[value.name]; + }); + class BlendModePipe { + constructor(renderer) { + this._blendModeStack = []; + this._isAdvanced = false; + this._filterHash = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + this._renderer.runners.prerender.add(this); + } + prerender() { + this._activeBlendMode = "normal"; + this._isAdvanced = false; + } + /** + * Push a blend mode onto the internal stack and apply it to the instruction set if needed. + * @param renderable - The renderable or {@link RenderGroup} associated with the change. + * @param blendMode - The blend mode to activate. + * @param instructionSet - The instruction set being built. + */ + pushBlendMode(renderable, blendMode, instructionSet) { + this._blendModeStack.push(blendMode); + this.setBlendMode(renderable, blendMode, instructionSet); + } + /** + * Pop the last blend mode from the stack and apply the new top-of-stack mode. + * @param instructionSet - The instruction set being built. + */ + popBlendMode(instructionSet) { + var _a; + this._blendModeStack.pop(); + const blendMode = (_a = this._blendModeStack[this._activeBlendMode.length - 1]) != null ? _a : "normal"; + this.setBlendMode(null, blendMode, instructionSet); + } + /** + * Ensure a blend mode switch is added to the instruction set when the mode changes. + * If an advanced blend mode is active, subsequent renderables will be collected so they can be + * rendered within a single filter pass. + * @param renderable - The renderable or {@link RenderGroup} to associate with the change, or null when unwinding. + * @param blendMode - The target blend mode. + * @param instructionSet - The instruction set being built. + */ + setBlendMode(renderable, blendMode, instructionSet) { + var _a; + const isRenderGroup = renderable instanceof RenderGroup; + if (this._activeBlendMode === blendMode) { + if (this._isAdvanced && renderable && !isRenderGroup) { + (_a = this._renderableList) == null ? void 0 : _a.push(renderable); + } + return; + } + if (this._isAdvanced) this._endAdvancedBlendMode(instructionSet); + this._activeBlendMode = blendMode; + if (!renderable) return; + this._isAdvanced = !!BLEND_MODE_FILTERS[blendMode]; + if (this._isAdvanced) this._beginAdvancedBlendMode(renderable, instructionSet); + } + _beginAdvancedBlendMode(renderable, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + const blendMode = this._activeBlendMode; + if (!BLEND_MODE_FILTERS[blendMode]) { + warn(`Unable to assign BlendMode: '${blendMode}'. You may want to include: import 'pixi.js/advanced-blend-modes'`); + return; + } + const filterEffect = this._ensureFilterEffect(blendMode); + const isRenderGroup = renderable instanceof RenderGroup; + const instruction = { + renderPipeId: "filter", + action: "pushFilter", + filterEffect, + renderables: isRenderGroup ? null : [renderable], + container: isRenderGroup ? renderable.root : null, + canBundle: false + }; + this._renderableList = instruction.renderables; + instructionSet.add(instruction); + } + _ensureFilterEffect(blendMode) { + let filterEffect = this._filterHash[blendMode]; + if (!filterEffect) { + filterEffect = this._filterHash[blendMode] = new FilterEffect(); + filterEffect.filters = [new BLEND_MODE_FILTERS[blendMode]()]; + } + return filterEffect; + } + _endAdvancedBlendMode(instructionSet) { + this._isAdvanced = false; + this._renderableList = null; + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add({ + renderPipeId: "filter", + action: "popFilter", + canBundle: false + }); + } + /** + * called when the instruction build process is starting this will reset internally to the default blend mode + * @internal + */ + buildStart() { + this._isAdvanced = false; + } + /** + * called when the instruction build process is finished, ensuring that if there is an advanced blend mode + * active, we add the final render instructions added to the instruction set + * @param instructionSet - The instruction set we are adding to + * @internal + */ + buildEnd(instructionSet) { + if (!this._isAdvanced) return; + this._endAdvancedBlendMode(instructionSet); + } + /** @internal */ + destroy() { + this._renderer = null; + this._renderableList = null; + for (const i in this._filterHash) { + this._filterHash[i].destroy(); + } + this._filterHash = null; + } + } + /** @ignore */ + BlendModePipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes, + ExtensionType.CanvasPipes + ], + name: "blendMode" + }; + + "use strict"; + function clearList(list, index) { + index || (index = 0); + for (let j = index; j < list.length; j++) { + if (list[j]) { + list[j] = null; + } else { + break; + } + } + } + + "use strict"; + const tempContainer = new Container(); + const UPDATE_BLEND_COLOR_VISIBLE = UPDATE_VISIBLE | UPDATE_COLOR | UPDATE_BLEND; + function updateRenderGroupTransforms(renderGroup, updateChildRenderGroups = false) { + updateRenderGroupTransform(renderGroup); + const childrenToUpdate = renderGroup.childrenToUpdate; + const updateTick = renderGroup.updateTick++; + for (const j in childrenToUpdate) { + const renderGroupDepth = Number(j); + const childrenAtDepth = childrenToUpdate[j]; + const list = childrenAtDepth.list; + const index = childrenAtDepth.index; + for (let i = 0; i < index; i++) { + const child = list[i]; + if (child.parentRenderGroup === renderGroup && child.relativeRenderGroupDepth === renderGroupDepth) { + updateTransformAndChildren(child, updateTick, 0); + } + } + clearList(list, index); + childrenAtDepth.index = 0; + } + if (updateChildRenderGroups) { + for (let i = 0; i < renderGroup.renderGroupChildren.length; i++) { + updateRenderGroupTransforms(renderGroup.renderGroupChildren[i], updateChildRenderGroups); + } + } + } + function updateRenderGroupTransform(renderGroup) { + const root = renderGroup.root; + let worldAlpha; + if (renderGroup.renderGroupParent) { + const renderGroupParent = renderGroup.renderGroupParent; + renderGroup.worldTransform.appendFrom( + root.relativeGroupTransform, + renderGroupParent.worldTransform + ); + renderGroup.worldColor = multiplyColors( + root.groupColor, + renderGroupParent.worldColor + ); + worldAlpha = root.groupAlpha * renderGroupParent.worldAlpha; + } else { + renderGroup.worldTransform.copyFrom(root.localTransform); + renderGroup.worldColor = root.localColor; + worldAlpha = root.localAlpha; + } + worldAlpha = worldAlpha < 0 ? 0 : worldAlpha > 1 ? 1 : worldAlpha; + renderGroup.worldAlpha = worldAlpha; + renderGroup.worldColorAlpha = renderGroup.worldColor + ((worldAlpha * 255 | 0) << 24); + } + function updateTransformAndChildren(container, updateTick, updateFlags) { + if (updateTick === container.updateTick) return; + container.updateTick = updateTick; + container.didChange = false; + const localTransform = container.localTransform; + container.updateLocalTransform(); + const parent = container.parent; + if (parent && !parent.renderGroup) { + updateFlags |= container._updateFlags; + container.relativeGroupTransform.appendFrom( + localTransform, + parent.relativeGroupTransform + ); + if (updateFlags & UPDATE_BLEND_COLOR_VISIBLE) { + updateColorBlendVisibility(container, parent, updateFlags); + } + } else { + updateFlags = container._updateFlags; + container.relativeGroupTransform.copyFrom(localTransform); + if (updateFlags & UPDATE_BLEND_COLOR_VISIBLE) { + updateColorBlendVisibility(container, tempContainer, updateFlags); + } + } + if (!container.renderGroup) { + const children = container.children; + const length = children.length; + for (let i = 0; i < length; i++) { + updateTransformAndChildren(children[i], updateTick, updateFlags); + } + const renderGroup = container.parentRenderGroup; + const renderable = container; + if (renderable.renderPipeId && !renderGroup.structureDidChange) { + renderGroup.updateRenderable(renderable); + } + } + } + function updateColorBlendVisibility(container, parent, updateFlags) { + if (updateFlags & UPDATE_COLOR) { + container.groupColor = multiplyColors( + container.localColor, + parent.groupColor + ); + let groupAlpha = container.localAlpha * parent.groupAlpha; + groupAlpha = groupAlpha < 0 ? 0 : groupAlpha > 1 ? 1 : groupAlpha; + container.groupAlpha = groupAlpha; + container.groupColorAlpha = container.groupColor + ((groupAlpha * 255 | 0) << 24); + } + if (updateFlags & UPDATE_BLEND) { + container.groupBlendMode = container.localBlendMode === "inherit" ? parent.groupBlendMode : container.localBlendMode; + } + if (updateFlags & UPDATE_VISIBLE) { + container.globalDisplayStatus = container.localDisplayStatus & parent.globalDisplayStatus; + } + container._updateFlags = 0; + } + + "use strict"; + function validateRenderables(renderGroup, renderPipes) { + const { list } = renderGroup.childrenRenderablesToUpdate; + let rebuildRequired = false; + for (let i = 0; i < renderGroup.childrenRenderablesToUpdate.index; i++) { + const container = list[i]; + const renderable = container; + const pipe = renderPipes[renderable.renderPipeId]; + rebuildRequired = pipe.validateRenderable(container); + if (rebuildRequired) { + break; + } + } + renderGroup.structureDidChange = rebuildRequired; + return rebuildRequired; + } + + "use strict"; + const tempMatrix$1 = new Matrix(); + class RenderGroupSystem { + constructor(renderer) { + this._renderer = renderer; + } + render({ container, transform }) { + const parent = container.parent; + const renderGroupParent = container.renderGroup.renderGroupParent; + container.parent = null; + container.renderGroup.renderGroupParent = null; + const renderer = this._renderer; + const originalLocalTransform = tempMatrix$1; + if (transform) { + originalLocalTransform.copyFrom(container.renderGroup.localTransform); + container.renderGroup.localTransform.copyFrom(transform); + } + const renderPipes = renderer.renderPipes; + this._updateCachedRenderGroups(container.renderGroup, null); + this._updateRenderGroups(container.renderGroup); + renderer.globalUniforms.start({ + worldTransformMatrix: transform ? container.renderGroup.localTransform : container.renderGroup.worldTransform, + worldColor: container.renderGroup.worldColorAlpha + }); + executeInstructions(container.renderGroup, renderPipes); + if (renderPipes.uniformBatch) { + renderPipes.uniformBatch.renderEnd(); + } + if (transform) { + container.renderGroup.localTransform.copyFrom(originalLocalTransform); + } + container.parent = parent; + container.renderGroup.renderGroupParent = renderGroupParent; + } + destroy() { + this._renderer = null; + } + _updateCachedRenderGroups(renderGroup, closestCacheAsTexture) { + var _a, _b; + renderGroup._parentCacheAsTextureRenderGroup = closestCacheAsTexture; + if (renderGroup.isCachedAsTexture) { + if (!renderGroup.textureNeedsUpdate) return; + closestCacheAsTexture = renderGroup; + } + for (let i = renderGroup.renderGroupChildren.length - 1; i >= 0; i--) { + this._updateCachedRenderGroups(renderGroup.renderGroupChildren[i], closestCacheAsTexture); + } + renderGroup.invalidateMatrices(); + if (renderGroup.isCachedAsTexture) { + if (renderGroup.textureNeedsUpdate) { + const bounds = renderGroup.root.getLocalBounds(); + const renderer = this._renderer; + const resolution = renderGroup.textureOptions.resolution || renderer.view.resolution; + const antialias = (_a = renderGroup.textureOptions.antialias) != null ? _a : renderer.view.antialias; + const scaleMode = (_b = renderGroup.textureOptions.scaleMode) != null ? _b : "linear"; + const lastTexture = renderGroup.texture; + bounds.ceil(); + if (renderGroup.texture) { + TexturePool.returnTexture(renderGroup.texture, true); + } + const texture = TexturePool.getOptimalTexture( + bounds.width, + bounds.height, + resolution, + antialias + ); + texture._source.style = new TextureStyle({ scaleMode }); + renderGroup.texture = texture; + renderGroup._textureBounds || (renderGroup._textureBounds = new Bounds()); + renderGroup._textureBounds.copyFrom(bounds); + if (lastTexture !== renderGroup.texture) { + if (renderGroup.renderGroupParent) { + renderGroup.renderGroupParent.structureDidChange = true; + } + } + } + } else if (renderGroup.texture) { + TexturePool.returnTexture(renderGroup.texture, true); + renderGroup.texture = null; + } + } + _updateRenderGroups(renderGroup) { + const renderer = this._renderer; + const renderPipes = renderer.renderPipes; + renderGroup.runOnRender(renderer); + renderGroup.instructionSet.renderPipes = renderPipes; + if (!renderGroup.structureDidChange) { + validateRenderables(renderGroup, renderPipes); + } else { + clearList(renderGroup.childrenRenderablesToUpdate.list, 0); + } + updateRenderGroupTransforms(renderGroup); + if (renderGroup.structureDidChange) { + renderGroup.structureDidChange = false; + this._buildInstructions(renderGroup, renderer); + } else { + this._updateRenderables(renderGroup); + } + renderGroup.childrenRenderablesToUpdate.index = 0; + renderer.renderPipes.batch.upload(renderGroup.instructionSet); + if (renderGroup.isCachedAsTexture && !renderGroup.textureNeedsUpdate) return; + for (let i = 0; i < renderGroup.renderGroupChildren.length; i++) { + this._updateRenderGroups(renderGroup.renderGroupChildren[i]); + } + } + _updateRenderables(renderGroup) { + const { list, index } = renderGroup.childrenRenderablesToUpdate; + for (let i = 0; i < index; i++) { + const container = list[i]; + if (container.didViewUpdate) { + renderGroup.updateRenderable(container); + } + } + clearList(list, index); + } + _buildInstructions(renderGroup, rendererOrPipes) { + const root = renderGroup.root; + const instructionSet = renderGroup.instructionSet; + instructionSet.reset(); + const renderer = rendererOrPipes.renderPipes ? rendererOrPipes : rendererOrPipes.batch.renderer; + const renderPipes = renderer.renderPipes; + renderPipes.batch.buildStart(instructionSet); + renderPipes.blendMode.buildStart(); + renderPipes.colorMask.buildStart(); + if (root.sortableChildren) { + root.sortChildren(); + } + root.collectRenderablesWithEffects(instructionSet, renderer, null); + renderPipes.batch.buildEnd(instructionSet); + renderPipes.blendMode.buildEnd(instructionSet); + } + } + /** @ignore */ + RenderGroupSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "renderGroup" + }; + + "use strict"; + var __defProp$w = Object.defineProperty; + var __getOwnPropSymbols$y = Object.getOwnPropertySymbols; + var __hasOwnProp$y = Object.prototype.hasOwnProperty; + var __propIsEnum$y = Object.prototype.propertyIsEnumerable; + var __defNormalProp$w = (obj, key, value) => key in obj ? __defProp$w(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$w = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$y.call(b, prop)) + __defNormalProp$w(a, prop, b[prop]); + if (__getOwnPropSymbols$y) + for (var prop of __getOwnPropSymbols$y(b)) { + if (__propIsEnum$y.call(b, prop)) + __defNormalProp$w(a, prop, b[prop]); + } + return a; + }; + const _BackgroundSystem = class _BackgroundSystem { + constructor() { + this.clearBeforeRender = true; + this._backgroundColor = new Color(0); + this.color = this._backgroundColor; + this.alpha = 1; + } + /** + * initiates the background system + * @param options - the options for the background colors + */ + init(options) { + options = __spreadValues$w(__spreadValues$w({}, _BackgroundSystem.defaultOptions), options); + this.clearBeforeRender = options.clearBeforeRender; + this.color = options.background || options.backgroundColor || this._backgroundColor; + this.alpha = options.backgroundAlpha; + this._backgroundColor.setAlpha(options.backgroundAlpha); + } + /** The background color to fill if not transparent */ + get color() { + return this._backgroundColor; + } + set color(value) { + const incoming = Color.shared.setValue(value); + if (incoming.alpha < 1 && this._backgroundColor.alpha === 1) { + warn( + "Cannot set a transparent background on an opaque canvas. To enable transparency, set backgroundAlpha < 1 when initializing your Application." + ); + } + this._backgroundColor.setValue(value); + } + /** The background color alpha. Setting this to 0 will make the canvas transparent. */ + get alpha() { + return this._backgroundColor.alpha; + } + set alpha(value) { + this._backgroundColor.setAlpha(value); + } + /** The background color as an [R, G, B, A] array. */ + get colorRgba() { + return this._backgroundColor.toArray(); + } + /** + * destroys the background system + * @internal + */ + destroy() { + } + }; + /** @ignore */ + _BackgroundSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "background", + priority: 0 + }; + /** default options used by the system */ + _BackgroundSystem.defaultOptions = { + /** + * {@link WebGLOptions.backgroundAlpha} + * @default 1 + */ + backgroundAlpha: 1, + /** + * {@link WebGLOptions.backgroundColor} + * @default 0x000000 + */ + backgroundColor: 0, + /** + * {@link WebGLOptions.clearBeforeRender} + * @default true + */ + clearBeforeRender: true + }; + let BackgroundSystem = _BackgroundSystem; + + "use strict"; + var __defProp$v = Object.defineProperty; + var __getOwnPropSymbols$x = Object.getOwnPropertySymbols; + var __hasOwnProp$x = Object.prototype.hasOwnProperty; + var __propIsEnum$x = Object.prototype.propertyIsEnumerable; + var __defNormalProp$v = (obj, key, value) => key in obj ? __defProp$v(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$v = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$x.call(b, prop)) + __defNormalProp$v(a, prop, b[prop]); + if (__getOwnPropSymbols$x) + for (var prop of __getOwnPropSymbols$x(b)) { + if (__propIsEnum$x.call(b, prop)) + __defNormalProp$v(a, prop, b[prop]); + } + return a; + }; + const imageTypes = { + png: "image/png", + jpg: "image/jpeg", + webp: "image/webp" + }; + const _ExtractSystem = class _ExtractSystem { + /** @param renderer - The renderer this System works for. */ + constructor(renderer) { + this._renderer = renderer; + } + _normalizeOptions(options, defaults = {}) { + if (options instanceof Container || options instanceof Texture) { + return __spreadValues$v({ + target: options + }, defaults); + } + return __spreadValues$v(__spreadValues$v({}, defaults), options); + } + /** + * Creates an IImage from a display object or texture. + * @param options - Options for creating the image, or the target to extract + * @returns Promise that resolves with the generated IImage + * @example + * ```ts + * // Basic usage with a sprite + * const sprite = new Sprite(texture); + * const image = await renderer.extract.image(sprite); + * document.body.appendChild(image); + * + * // Advanced usage with options + * const image = await renderer.extract.image({ + * target: container, + * format: 'webp', + * quality: 0.8, + * frame: new Rectangle(0, 0, 100, 100), + * resolution: 2, + * clearColor: '#ff0000', + * antialias: true + * }); + * + * // Extract directly from a texture + * const texture = Texture.from('myTexture.png'); + * const image = await renderer.extract.image(texture); + * ``` + * @see {@link ExtractImageOptions} For detailed options + * @see {@link ExtractSystem.base64} For base64 string output + * @see {@link ExtractSystem.canvas} For canvas output + * @see {@link ImageLike} For the image interface + * @category rendering + */ + async image(options) { + const image = DOMAdapter.get().createImage(); + image.src = await this.base64(options); + return image; + } + /** + * Converts the target into a base64 encoded string. + * + * This method works by first creating + * a canvas using `Extract.canvas` and then converting it to a base64 string. + * @param options - The options for creating the base64 string, or the target to extract + * @returns Promise that resolves with the base64 encoded string + * @example + * ```ts + * // Basic usage with a sprite + * const sprite = new Sprite(texture); + * const base64 = await renderer.extract.base64(sprite); + * console.log(base64); // data:image/png;base64,... + * + * // Advanced usage with options + * const base64 = await renderer.extract.base64({ + * target: container, + * format: 'webp', + * quality: 0.8, + * frame: new Rectangle(0, 0, 100, 100), + * resolution: 2 + * }); + * ``` + * @throws Will throw an error if the platform doesn't support any of: + * - ICanvas.toDataURL + * - ICanvas.toBlob + * - ICanvas.convertToBlob + * @see {@link ExtractImageOptions} For detailed options + * @see {@link ExtractSystem.canvas} For canvas output + * @see {@link ExtractSystem.image} For HTMLImage output + * @category rendering + */ + async base64(options) { + options = this._normalizeOptions( + options, + _ExtractSystem.defaultImageOptions + ); + const { format, quality } = options; + const canvas = this.canvas(options); + if (canvas.toBlob !== void 0) { + return new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (!blob) { + reject(new Error("ICanvas.toBlob failed!")); + return; + } + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }, imageTypes[format], quality); + }); + } + if (canvas.toDataURL !== void 0) { + return canvas.toDataURL(imageTypes[format], quality); + } + if (canvas.convertToBlob !== void 0) { + const blob = await canvas.convertToBlob({ type: imageTypes[format], quality }); + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + } + throw new Error("Extract.base64() requires ICanvas.toDataURL, ICanvas.toBlob, or ICanvas.convertToBlob to be implemented"); + } + /** + * Creates a Canvas element, renders the target to it and returns it. + * This method is useful for creating static images or when you need direct canvas access. + * @param options - The options for creating the canvas, or the target to extract + * @returns A Canvas element with the texture rendered on + * @example + * ```ts + * // Basic canvas extraction from a sprite + * const sprite = new Sprite(texture); + * const canvas = renderer.extract.canvas(sprite); + * document.body.appendChild(canvas); + * + * // Extract with custom region + * const canvas = renderer.extract.canvas({ + * target: container, + * frame: new Rectangle(0, 0, 100, 100) + * }); + * + * // Extract with high resolution + * const canvas = renderer.extract.canvas({ + * target: sprite, + * resolution: 2, + * clearColor: '#ff0000' + * }); + * + * // Extract directly from a texture + * const texture = Texture.from('myTexture.png'); + * const canvas = renderer.extract.canvas(texture); + * + * // Extract with anti-aliasing + * const canvas = renderer.extract.canvas({ + * target: graphics, + * antialias: true + * }); + * ``` + * @see {@link ExtractOptions} For detailed options + * @see {@link ExtractSystem.image} For HTMLImage output + * @see {@link ExtractSystem.pixels} For raw pixel data + * @category rendering + */ + canvas(options) { + options = this._normalizeOptions(options); + const target = options.target; + const renderer = this._renderer; + if (target instanceof Texture) { + return renderer.texture.generateCanvas(target); + } + const texture = renderer.textureGenerator.generateTexture(options); + const canvas = renderer.texture.generateCanvas(texture); + texture.destroy(true); + return canvas; + } + /** + * Returns a one-dimensional array containing the pixel data of the entire texture in RGBA order, + * with integer values between 0 and 255 (inclusive). + * > [!NOE] The returned array is a flat Uint8Array where every 4 values represent RGBA + * @param options - The options for extracting the image, or the target to extract + * @returns One-dimensional Uint8Array containing the pixel data in RGBA format + * @example + * ```ts + * // Basic pixel extraction + * const sprite = new Sprite(texture); + * const pixels = renderer.extract.pixels(sprite); + * console.log(pixels[0], pixels[1], pixels[2], pixels[3]); // R,G,B,A values + * + * // Extract with custom region + * const pixels = renderer.extract.pixels({ + * target: sprite, + * frame: new Rectangle(0, 0, 100, 100) + * }); + * + * // Extract with high resolution + * const pixels = renderer.extract.pixels({ + * target: sprite, + * resolution: 2 + * }); + * ``` + * @see {@link ExtractOptions} For detailed options + * @see {@link ExtractSystem.canvas} For canvas output + * @see {@link ExtractSystem.image} For image output + * @category rendering + */ + pixels(options) { + options = this._normalizeOptions(options); + const target = options.target; + const renderer = this._renderer; + const texture = target instanceof Texture ? target : renderer.textureGenerator.generateTexture(options); + const pixelInfo = renderer.texture.getPixels(texture); + if (target instanceof Container) { + texture.destroy(true); + } + return pixelInfo; + } + /** + * Creates a texture from a display object or existing texture. + * + * This is useful for creating + * reusable textures from rendered content or making copies of existing textures. + * > [!NOTE] The returned texture should be destroyed when no longer needed + * @param options - The options for creating the texture, or the target to extract + * @returns A new texture containing the extracted content + * @example + * ```ts + * // Basic texture extraction from a sprite + * const sprite = new Sprite(texture); + * const extractedTexture = renderer.extract.texture(sprite); + * + * // Extract with custom region + * const regionTexture = renderer.extract.texture({ + * target: container, + * frame: new Rectangle(0, 0, 100, 100) + * }); + * + * // Extract with high resolution + * const hiResTexture = renderer.extract.texture({ + * target: sprite, + * resolution: 2, + * clearColor: '#ff0000' + * }); + * + * // Create a new sprite from extracted texture + * const newSprite = new Sprite( + * renderer.extract.texture({ + * target: graphics, + * antialias: true + * }) + * ); + * + * // Clean up when done + * extractedTexture.destroy(true); + * ``` + * @see {@link ExtractOptions} For detailed options + * @see {@link Texture} For texture management + * @see {@link GenerateTextureSystem} For texture generation + * @category rendering + */ + texture(options) { + options = this._normalizeOptions(options); + if (options.target instanceof Texture) return options.target; + return this._renderer.textureGenerator.generateTexture(options); + } + /** + * Extracts and downloads content from the renderer as an image file. + * This is a convenient way to save screenshots or export rendered content. + * > [!NOTE] The download will use PNG format regardless of the filename extension + * @param options - The options for downloading and extracting the image, or the target to extract + * @example + * ```ts + * // Basic download with default filename + * const sprite = new Sprite(texture); + * renderer.extract.download(sprite); // Downloads as 'image.png' + * + * // Download with custom filename + * renderer.extract.download({ + * target: sprite, + * filename: 'screenshot.png' + * }); + * + * // Download with custom region + * renderer.extract.download({ + * target: container, + * filename: 'region.png', + * frame: new Rectangle(0, 0, 100, 100) + * }); + * + * // Download with high resolution and background + * renderer.extract.download({ + * target: stage, + * filename: 'hd-screenshot.png', + * resolution: 2, + * clearColor: '#ff0000' + * }); + * + * // Download with anti-aliasing + * renderer.extract.download({ + * target: graphics, + * filename: 'smooth.png', + * antialias: true + * }); + * ``` + * @see {@link ExtractDownloadOptions} For detailed options + * @see {@link ExtractSystem.image} For creating images without download + * @see {@link ExtractSystem.canvas} For canvas output + * @category rendering + */ + download(options) { + var _a; + options = this._normalizeOptions(options); + const canvas = this.canvas(options); + const link = document.createElement("a"); + link.download = (_a = options.filename) != null ? _a : "image.png"; + link.href = canvas.toDataURL("image/png"); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + /** + * Logs the target to the console as an image. This is a useful way to debug what's happening in the renderer. + * The image will be displayed in the browser's console using CSS background images. + * @param options - The options for logging the image, or the target to log + * @param options.width - The width of the logged image preview in the console (in pixels) + * @example + * ```ts + * // Basic usage + * const sprite = new Sprite(texture); + * renderer.extract.log(sprite); + * ``` + * @see {@link ExtractSystem.canvas} For getting raw canvas output + * @see {@link ExtractSystem.pixels} For raw pixel data + * @category rendering + * @advanced + */ + log(options) { + var _a; + const width = (_a = options.width) != null ? _a : 200; + options = this._normalizeOptions(options); + const canvas = this.canvas(options); + const base64 = canvas.toDataURL(); + console.log(`[Pixi Texture] ${canvas.width}px ${canvas.height}px`); + const style = [ + "font-size: 1px;", + `padding: ${width}px ${300}px;`, + `background: url(${base64}) no-repeat;`, + "background-size: contain;" + ].join(" "); + console.log("%c ", style); + } + destroy() { + this._renderer = null; + } + }; + /** @ignore */ + _ExtractSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "extract" + }; + /** + * Default options for image extraction. + * @example + * ```ts + * // Customize default options + * ExtractSystem.defaultImageOptions.format = 'webp'; + * ExtractSystem.defaultImageOptions.quality = 0.8; + * + * // Use defaults + * const image = await renderer.extract.image(sprite); + * ``` + */ + _ExtractSystem.defaultImageOptions = { + format: "png", + quality: 1 + }; + let ExtractSystem = _ExtractSystem; + + "use strict"; + var __getOwnPropSymbols$w = Object.getOwnPropertySymbols; + var __hasOwnProp$w = Object.prototype.hasOwnProperty; + var __propIsEnum$w = Object.prototype.propertyIsEnumerable; + var __objRest$a = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$w.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$w) + for (var prop of __getOwnPropSymbols$w(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$w.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class RenderTexture extends Texture { + /** + * Creates a RenderTexture. Pass `dynamic: true` in options to allow resizing after creation. + * @param options - Options for the RenderTexture, including width, height, and dynamic. + * @returns A new RenderTexture instance. + * @example + * const rt = RenderTexture.create({ width: 100, height: 100, dynamic: true }); + * rt.resize(500, 500); + */ + static create(options) { + const _a = options, { dynamic } = _a, rest = __objRest$a(_a, ["dynamic"]); + return new RenderTexture({ + source: new TextureSource(rest), + dynamic: dynamic != null ? dynamic : false + }); + } + /** + * Resizes the render texture. + * @param width - The new width of the render texture. + * @param height - The new height of the render texture. + * @param resolution - The new resolution of the render texture. + * @returns This texture. + */ + resize(width, height, resolution) { + this.source.resize(width, height, resolution); + return this; + } + } + + "use strict"; + var __defProp$u = Object.defineProperty; + var __defProps$g = Object.defineProperties; + var __getOwnPropDescs$g = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$v = Object.getOwnPropertySymbols; + var __hasOwnProp$v = Object.prototype.hasOwnProperty; + var __propIsEnum$v = Object.prototype.propertyIsEnumerable; + var __defNormalProp$u = (obj, key, value) => key in obj ? __defProp$u(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$u = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$v.call(b, prop)) + __defNormalProp$u(a, prop, b[prop]); + if (__getOwnPropSymbols$v) + for (var prop of __getOwnPropSymbols$v(b)) { + if (__propIsEnum$v.call(b, prop)) + __defNormalProp$u(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$g = (a, b) => __defProps$g(a, __getOwnPropDescs$g(b)); + const tempRect = new Rectangle(); + const tempBounds = new Bounds(); + const noColor = [0, 0, 0, 0]; + class GenerateTextureSystem { + constructor(renderer) { + this._renderer = renderer; + } + /** + * Creates a texture from a display object that can be used for creating sprites and other textures. + * This is particularly useful for optimizing performance when a complex container needs to be reused. + * @param options - Generate texture options or a container to convert to texture + * @returns A new RenderTexture containing the rendered display object + * @example + * ```ts + * // Basic usage with a container + * const container = new Container(); + * container.addChild( + * new Graphics() + * .circle(0, 0, 50) + * .fill('red') + * ); + * + * const texture = renderer.textureGenerator.generateTexture(container); + * + * // Advanced usage with options + * const texture = renderer.textureGenerator.generateTexture({ + * target: container, + * frame: new Rectangle(0, 0, 100, 100), // Specific region + * resolution: 2, // High DPI + * clearColor: '#ff0000', // Red background + * antialias: true // Smooth edges + * }); + * + * // Create a sprite from the generated texture + * const sprite = new Sprite(texture); + * + * // Clean up when done + * texture.destroy(true); + * ``` + * @see {@link GenerateTextureOptions} For detailed texture generation options + * @see {@link RenderTexture} For the type of texture created + * @category rendering + */ + generateTexture(options) { + var _a; + if (options instanceof Container) { + options = { + target: options, + frame: void 0, + textureSourceOptions: {}, + resolution: void 0 + }; + } + const resolution = options.resolution || this._renderer.resolution; + const antialias = options.antialias || this._renderer.view.antialias; + const container = options.target; + let clearColor = options.clearColor; + if (clearColor) { + const isRGBAArray = Array.isArray(clearColor) && clearColor.length === 4; + clearColor = isRGBAArray ? clearColor : Color.shared.setValue(clearColor).toArray(); + } else { + clearColor = noColor; + } + const region = ((_a = options.frame) == null ? void 0 : _a.copyTo(tempRect)) || getLocalBounds(container, tempBounds).rectangle; + region.width = Math.max(region.width, 1 / resolution) | 0; + region.height = Math.max(region.height, 1 / resolution) | 0; + const target = RenderTexture.create(__spreadProps$g(__spreadValues$u({}, options.textureSourceOptions), { + width: region.width, + height: region.height, + resolution, + antialias + })); + const transform = Matrix.shared.translate(-region.x, -region.y); + this._renderer.render({ + container, + transform, + target, + clearColor + }); + target.source.updateMipmaps(); + return target; + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + GenerateTextureSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "textureGenerator" + }; + + "use strict"; + function cleanHash(hash) { + let clean = false; + for (const i in hash) { + if (hash[i] == void 0) { + clean = true; + break; + } + } + if (!clean) return hash; + const cleanHash2 = /* @__PURE__ */ Object.create(null); + for (const i in hash) { + const value = hash[i]; + if (value) { + cleanHash2[i] = value; + } + } + return cleanHash2; + } + function cleanArray(arr) { + let offset = 0; + for (let i = 0; i < arr.length; i++) { + if (arr[i] == void 0) { + offset++; + } else { + arr[i - offset] = arr[i]; + } + } + arr.length -= offset; + return arr; + } + + "use strict"; + var __defProp$t = Object.defineProperty; + var __getOwnPropSymbols$u = Object.getOwnPropertySymbols; + var __hasOwnProp$u = Object.prototype.hasOwnProperty; + var __propIsEnum$u = Object.prototype.propertyIsEnumerable; + var __defNormalProp$t = (obj, key, value) => key in obj ? __defProp$t(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$t = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$u.call(b, prop)) + __defNormalProp$t(a, prop, b[prop]); + if (__getOwnPropSymbols$u) + for (var prop of __getOwnPropSymbols$u(b)) { + if (__propIsEnum$u.call(b, prop)) + __defNormalProp$t(a, prop, b[prop]); + } + return a; + }; + const _GCSystem = class _GCSystem { + /** + * Creates a new GCSystem instance. + * @param renderer - The renderer this garbage collection system works for + */ + constructor(renderer) { + /** Array of resources being tracked for garbage collection */ + this._managedResources = []; + this._managedResourceHashes = []; + this._managedCollections = []; + this._ready = false; + this._renderer = renderer; + } + /** + * Initializes the garbage collection system with the provided options. + * @param options - Configuration options + */ + init(options) { + options = __spreadValues$t(__spreadValues$t({}, _GCSystem.defaultOptions), options); + this.maxUnusedTime = options.gcMaxUnusedTime; + this._frequency = options.gcFrequency; + this.enabled = options.gcActive; + this.now = performance.now(); + } + /** + * Gets whether the garbage collection system is currently enabled. + * @returns True if GC is enabled, false otherwise + */ + get enabled() { + return !!this._handler; + } + /** + * Enables or disables the garbage collection system. + * When enabled, schedules periodic cleanup of resources. + * When disabled, cancels all scheduled cleanups. + */ + set enabled(value) { + if (this.enabled === value) return; + if (value) { + this._handler = this._renderer.scheduler.repeat( + () => { + this._ready = true; + }, + this._frequency, + false + ); + this._collectionsHandler = this._renderer.scheduler.repeat( + () => { + for (const hash of this._managedCollections) { + const { context, collection, type } = hash; + if (type === "hash") { + context[collection] = cleanHash(context[collection]); + } else { + context[collection] = cleanArray(context[collection]); + } + } + }, + this._frequency + ); + } else { + this._renderer.scheduler.cancel(this._handler); + this._renderer.scheduler.cancel(this._collectionsHandler); + this._handler = 0; + this._collectionsHandler = 0; + } + } + /** + * Called before rendering. Updates the current timestamp. + * @param options - The render options + * @param options.container - The container to render + */ + prerender({ container }) { + this.now = performance.now(); + container.renderGroup.gcTick = this._renderer.tick++; + this._updateInstructionGCTick(container.renderGroup, container.renderGroup.gcTick); + } + /** Performs garbage collection after rendering. */ + postrender() { + if (!this._ready || !this.enabled) return; + this.run(); + this._ready = false; + } + /** + * Updates the GC tick counter for a render group and its children. + * @param renderGroup - The render group to update + * @param gcTick - The new tick value + */ + _updateInstructionGCTick(renderGroup, gcTick) { + renderGroup.instructionSet.gcTick = gcTick; + renderGroup.gcTick = gcTick; + for (const child of renderGroup.renderGroupChildren) { + this._updateInstructionGCTick(child, gcTick); + } + } + /** + * Registers a collection for garbage collection tracking. + * @param context - The object containing the collection + * @param collection - The property name on context that holds the collection + * @param type - The type of collection to track ('hash' or 'array') + */ + addCollection(context, collection, type) { + this._managedCollections.push({ + context, + collection, + type + }); + } + /** + * Registers a resource for garbage collection tracking. + * @param resource - The resource to track + * @param type - The type of resource to track + */ + addResource(resource, type) { + var _a, _b; + if (resource._gcLastUsed !== -1) { + resource._gcLastUsed = this.now; + (_a = resource._onTouch) == null ? void 0 : _a.call(resource, this.now); + return; + } + const index = this._managedResources.length; + resource._gcData = { + index, + type + }; + resource._gcLastUsed = this.now; + (_b = resource._onTouch) == null ? void 0 : _b.call(resource, this.now); + resource.once("unload", this.removeResource, this); + this._managedResources.push(resource); + } + /** + * Removes a resource from garbage collection tracking. + * Call this when manually destroying a resource. + * @param resource - The resource to stop tracking + */ + removeResource(resource) { + const gcData = resource._gcData; + if (!gcData) return; + const index = gcData.index; + const last = this._managedResources.length - 1; + if (index !== last) { + const lastResource = this._managedResources[last]; + this._managedResources[index] = lastResource; + lastResource._gcData.index = index; + } + this._managedResources.length--; + resource._gcData = null; + resource._gcLastUsed = -1; + } + /** + * Registers a hash-based resource collection for garbage collection tracking. + * Resources in the hash will be automatically tracked and cleaned up when unused. + * @param context - The object containing the hash property + * @param hash - The property name on context that holds the resource hash + * @param type - The type of resources in the hash ('resource' or 'renderable') + * @param priority - Processing priority (lower values are processed first) + */ + addResourceHash(context, hash, type, priority = 0) { + this._managedResourceHashes.push({ + context, + hash, + type, + priority + }); + this._managedResourceHashes.sort((a, b) => a.priority - b.priority); + } + /** + * Performs garbage collection by cleaning up unused resources. + * Removes resources that haven't been used for longer than maxUnusedTime. + */ + run() { + const now = performance.now(); + const managedResourceHashes = this._managedResourceHashes; + for (const hashEntry of managedResourceHashes) { + this.runOnHash(hashEntry, now); + } + let writeIndex = 0; + for (let i = 0; i < this._managedResources.length; i++) { + const resource = this._managedResources[i]; + writeIndex = this.runOnResource(resource, now, writeIndex); + } + this._managedResources.length = writeIndex; + } + updateRenderableGCTick(renderable, now) { + var _a, _b, _c, _d, _e; + const renderGroup = (_a = renderable.renderGroup) != null ? _a : renderable.parentRenderGroup; + const currentTick = (_c = (_b = renderGroup == null ? void 0 : renderGroup.instructionSet) == null ? void 0 : _b.gcTick) != null ? _c : -1; + if (((_d = renderGroup == null ? void 0 : renderGroup.gcTick) != null ? _d : 0) === currentTick) { + renderable._gcLastUsed = now; + (_e = renderable._onTouch) == null ? void 0 : _e.call(renderable, now); + } + } + runOnResource(resource, now, writeIndex) { + const gcData = resource._gcData; + if (gcData.type === "renderable") { + this.updateRenderableGCTick(resource, now); + } + const isRecentlyUsed = now - resource._gcLastUsed < this.maxUnusedTime; + if (isRecentlyUsed || !resource.autoGarbageCollect) { + this._managedResources[writeIndex] = resource; + gcData.index = writeIndex; + writeIndex++; + } else { + resource.unload(); + resource._gcData = null; + resource._gcLastUsed = -1; + resource.off("unload", this.removeResource, this); + } + return writeIndex; + } + /** + * Creates a clone of the hash, copying all non-null entries up to (but not including) the stop key. + * @param hashValue - The original hash to clone from + * @param stopKey - The key to stop at (exclusive) + * @returns A new hash object with copied entries + */ + _createHashClone(hashValue, stopKey) { + const hashClone = /* @__PURE__ */ Object.create(null); + for (const k in hashValue) { + if (k === stopKey) break; + if (hashValue[k] !== null) hashClone[k] = hashValue[k]; + } + return hashClone; + } + runOnHash(hashEntry, now) { + var _a, _b; + const { context, hash, type } = hashEntry; + const hashValue = context[hash]; + let hashClone = null; + let nullCount = 0; + for (const key in hashValue) { + const resource = hashValue[key]; + if (resource === null) { + nullCount++; + if (nullCount === 1e4 && !hashClone) { + hashClone = this._createHashClone(hashValue, key); + } + continue; + } + if (resource._gcLastUsed === -1) { + resource._gcLastUsed = now; + (_a = resource._onTouch) == null ? void 0 : _a.call(resource, now); + if (hashClone) hashClone[key] = resource; + continue; + } + if (type === "renderable") { + this.updateRenderableGCTick(resource, now); + } + const isRecentlyUsed = now - resource._gcLastUsed < this.maxUnusedTime; + if (!isRecentlyUsed && resource.autoGarbageCollect) { + if (!hashClone) { + if (nullCount + 1 !== 1e4) { + hashValue[key] = null; + nullCount++; + } else { + hashClone = this._createHashClone(hashValue, key); + } + } + if (type === "renderable") { + const res = resource; + const renderGroup = (_b = res.renderGroup) != null ? _b : res.parentRenderGroup; + if (renderGroup) renderGroup.structureDidChange = true; + } + resource.unload(); + resource._gcData = null; + resource._gcLastUsed = -1; + } else if (hashClone) { + hashClone[key] = resource; + } + } + if (hashClone) { + context[hash] = hashClone; + } + } + /** Cleans up the garbage collection system. Disables GC and removes all tracked resources. */ + destroy() { + this.enabled = false; + this._managedResources.forEach((resource) => { + resource.off("unload", this.removeResource, this); + }); + this._managedResources.length = 0; + this._managedResourceHashes.length = 0; + this._managedCollections.length = 0; + this._renderer = null; + } + }; + /** @ignore */ + _GCSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "gc", + priority: 0 + }; + /** Default options for the GCSystem */ + _GCSystem.defaultOptions = { + /** Enable/disable the garbage collector */ + gcActive: true, + /** Time in ms before an unused resource is collected (default 1 minute) */ + gcMaxUnusedTime: 6e4, + /** How often to run garbage collection in ms (default 30 seconds) */ + gcFrequency: 3e4 + }; + let GCSystem = _GCSystem; + + "use strict"; + class GlobalUniformSystem { + constructor(renderer) { + this._stackIndex = 0; + this._globalUniformDataStack = []; + this._uniformsPool = []; + this._activeUniforms = []; + this._bindGroupPool = []; + this._activeBindGroups = []; + this._renderer = renderer; + } + reset() { + this._stackIndex = 0; + for (let i = 0; i < this._activeUniforms.length; i++) { + this._uniformsPool.push(this._activeUniforms[i]); + } + for (let i = 0; i < this._activeBindGroups.length; i++) { + this._bindGroupPool.push(this._activeBindGroups[i]); + } + this._activeUniforms.length = 0; + this._activeBindGroups.length = 0; + } + start(options) { + this.reset(); + this.push(options); + } + bind({ + size, + projectionMatrix, + worldTransformMatrix, + worldColor, + offset + }) { + const renderTarget = this._renderer.renderTarget.renderTarget; + const currentGlobalUniformData = this._stackIndex ? this._globalUniformDataStack[this._stackIndex - 1] : { + projectionData: renderTarget, + worldTransformMatrix: new Matrix(), + worldColor: 4294967295, + offset: new Point() + }; + const globalUniformData = { + projectionMatrix: projectionMatrix || this._renderer.renderTarget.projectionMatrix, + resolution: size || renderTarget.size, + worldTransformMatrix: worldTransformMatrix || currentGlobalUniformData.worldTransformMatrix, + worldColor: worldColor || currentGlobalUniformData.worldColor, + offset: offset || currentGlobalUniformData.offset, + bindGroup: null + }; + const uniformGroup = this._uniformsPool.pop() || this._createUniforms(); + this._activeUniforms.push(uniformGroup); + const uniforms = uniformGroup.uniforms; + uniforms.uProjectionMatrix = globalUniformData.projectionMatrix; + uniforms.uResolution = globalUniformData.resolution; + uniforms.uWorldTransformMatrix.copyFrom(globalUniformData.worldTransformMatrix); + uniforms.uWorldTransformMatrix.tx -= globalUniformData.offset.x; + uniforms.uWorldTransformMatrix.ty -= globalUniformData.offset.y; + color32BitToUniform( + globalUniformData.worldColor, + uniforms.uWorldColorAlpha, + 0 + ); + uniformGroup.update(); + let bindGroup; + if (this._renderer.renderPipes.uniformBatch) { + bindGroup = this._renderer.renderPipes.uniformBatch.getUniformBindGroup(uniformGroup, false); + } else { + bindGroup = this._bindGroupPool.pop() || new BindGroup(); + this._activeBindGroups.push(bindGroup); + bindGroup.setResource(uniformGroup, 0); + } + globalUniformData.bindGroup = bindGroup; + this._currentGlobalUniformData = globalUniformData; + } + push(options) { + this.bind(options); + this._globalUniformDataStack[this._stackIndex++] = this._currentGlobalUniformData; + } + pop() { + this._currentGlobalUniformData = this._globalUniformDataStack[--this._stackIndex - 1]; + if (this._renderer.type === RendererType.WEBGL) { + this._currentGlobalUniformData.bindGroup.resources[0].update(); + } + } + get bindGroup() { + return this._currentGlobalUniformData.bindGroup; + } + get globalUniformData() { + return this._currentGlobalUniformData; + } + get uniformGroup() { + return this._currentGlobalUniformData.bindGroup.resources[0]; + } + _createUniforms() { + const globalUniforms = new UniformGroup({ + uProjectionMatrix: { value: new Matrix(), type: "mat3x3" }, + uWorldTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + // TODO - someone smart - set this to be a unorm8x4 rather than a vec4 + uWorldColorAlpha: { value: new Float32Array(4), type: "vec4" }, + uResolution: { value: [0, 0], type: "vec2" } + }, { + isStatic: true + }); + return globalUniforms; + } + destroy() { + this._renderer = null; + this._globalUniformDataStack.length = 0; + this._uniformsPool.length = 0; + this._activeUniforms.length = 0; + this._bindGroupPool.length = 0; + this._activeBindGroups.length = 0; + this._currentGlobalUniformData = null; + } + } + /** @ignore */ + GlobalUniformSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "globalUniforms" + }; + + "use strict"; + let uid = 1; + class SchedulerSystem { + constructor() { + this._tasks = []; + /** a small off set to apply to the repeat schedules. This is just to make sure they run at slightly different times */ + this._offset = 0; + } + /** Initializes the scheduler system and starts the ticker. */ + init() { + Ticker.system.add(this._update, this); + } + /** + * Schedules a repeating task. + * @param func - The function to execute. + * @param duration - The interval duration in milliseconds. + * @param useOffset - this will spread out tasks so that they do not all run at the same time + * @returns The unique identifier for the scheduled task. + */ + repeat(func, duration, useOffset = true) { + const id = uid++; + let offset = 0; + if (useOffset) { + this._offset += 1e3; + offset = this._offset; + } + this._tasks.push({ + func, + duration, + start: performance.now(), + offset, + last: performance.now(), + repeat: true, + id + }); + return id; + } + /** + * Cancels a scheduled task. + * @param id - The unique identifier of the task to cancel. + */ + cancel(id) { + for (let i = 0; i < this._tasks.length; i++) { + if (this._tasks[i].id === id) { + this._tasks.splice(i, 1); + return; + } + } + } + /** + * Updates and executes the scheduled tasks. + * @private + */ + _update() { + const now = performance.now(); + for (let i = 0; i < this._tasks.length; i++) { + const task = this._tasks[i]; + if (now - task.offset - task.last >= task.duration) { + const elapsed = now - task.start; + task.func(elapsed); + task.last = now; + } + } + } + /** + * Destroys the scheduler system and removes all tasks. + * @internal + */ + destroy() { + Ticker.system.remove(this._update, this); + this._tasks.length = 0; + } + } + /** @ignore */ + SchedulerSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "scheduler", + priority: 0 + }; + + "use strict"; + let saidHello = false; + function sayHello(type) { + if (saidHello) { + return; + } + if (DOMAdapter.get().getNavigator().userAgent.toLowerCase().indexOf("chrome") > -1) { + const args = [ + `%c %c %c %c %c PixiJS %c v${VERSION} (${type}) http://www.pixijs.com/ + +`, + "background: #E72264; padding:5px 0;", + "background: #6CA2EA; padding:5px 0;", + "background: #B5D33D; padding:5px 0;", + "background: #FED23F; padding:5px 0;", + "color: #FFFFFF; background: #E72264; padding:5px 0;", + "color: #E72264; background: #FFFFFF; padding:5px 0;" + ]; + globalThis.console.log(...args); + } else if (globalThis.console) { + globalThis.console.log(`PixiJS ${VERSION} - ${type} - http://www.pixijs.com/`); + } + saidHello = true; + } + + "use strict"; + class HelloSystem { + constructor(renderer) { + this._renderer = renderer; + } + /** + * It all starts here! This initiates every system, passing in the options for any system by name. + * @param options - the config for the renderer and all its systems + */ + init(options) { + if (options.hello) { + let name = this._renderer.name; + if (this._renderer.type === RendererType.WEBGL) { + name += ` ${this._renderer.context.webGLVersion}`; + } + sayHello(name); + } + } + } + /** @ignore */ + HelloSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "hello", + priority: -2 + }; + /** The default options for the system. */ + HelloSystem.defaultOptions = { + /** {@link WebGLOptions.hello} */ + hello: false + }; + + "use strict"; + var __defProp$s = Object.defineProperty; + var __getOwnPropSymbols$t = Object.getOwnPropertySymbols; + var __hasOwnProp$t = Object.prototype.hasOwnProperty; + var __propIsEnum$t = Object.prototype.propertyIsEnumerable; + var __defNormalProp$s = (obj, key, value) => key in obj ? __defProp$s(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$s = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$t.call(b, prop)) + __defNormalProp$s(a, prop, b[prop]); + if (__getOwnPropSymbols$t) + for (var prop of __getOwnPropSymbols$t(b)) { + if (__propIsEnum$t.call(b, prop)) + __defNormalProp$s(a, prop, b[prop]); + } + return a; + }; + const _RenderableGCSystem = class _RenderableGCSystem { + /** + * Creates a new RenderableGCSystem instance. + * @param renderer - The renderer this garbage collection system works for + */ + constructor(renderer) { + this._renderer = renderer; + } + /** + * Initializes the garbage collection system with the provided options. + * @param options - Configuration options for the renderer + */ + init(options) { + options = __spreadValues$s(__spreadValues$s({}, _RenderableGCSystem.defaultOptions), options); + this.maxUnusedTime = options.renderableGCMaxUnusedTime; + } + /** + * Gets whether the garbage collection system is currently enabled. + * @returns True if GC is enabled, false otherwise + */ + get enabled() { + deprecation("8.15.0", "RenderableGCSystem.enabled is deprecated, please use the GCSystem.enabled instead."); + return this._renderer.gc.enabled; + } + /** + * Enables or disables the garbage collection system. + * When enabled, schedules periodic cleanup of resources. + * When disabled, cancels all scheduled cleanups. + */ + set enabled(value) { + deprecation("8.15.0", "RenderableGCSystem.enabled is deprecated, please use the GCSystem.enabled instead."); + this._renderer.gc.enabled = value; + } + /** + * Adds a hash table to be managed by the garbage collector. + * @param context - The object containing the hash table + * @param hash - The property name of the hash table + */ + addManagedHash(context, hash) { + deprecation("8.15.0", "RenderableGCSystem.addManagedHash is deprecated, please use the GCSystem.addCollection instead."); + this._renderer.gc.addCollection(context, hash, "hash"); + } + /** + * Adds an array to be managed by the garbage collector. + * @param context - The object containing the array + * @param hash - The property name of the array + */ + addManagedArray(context, hash) { + deprecation("8.15.0", "RenderableGCSystem.addManagedArray is deprecated, please use the GCSystem.addCollection instead."); + this._renderer.gc.addCollection(context, hash, "array"); + } + /** + * Starts tracking a renderable for garbage collection. + * @param _renderable - The renderable to track + * @deprecated since 8.15.0 + */ + addRenderable(_renderable) { + deprecation("8.15.0", "RenderableGCSystem.addRenderable is deprecated, please use the GCSystem instead."); + this._renderer.gc.addResource(_renderable, "renderable"); + } + /** + * Performs garbage collection by cleaning up unused renderables. + * Removes renderables that haven't been used for longer than maxUnusedTime. + */ + run() { + deprecation("8.15.0", "RenderableGCSystem.run is deprecated, please use the GCSystem instead."); + this._renderer.gc.run(); + } + /** Cleans up the garbage collection system. Disables GC and removes all tracked resources. */ + destroy() { + this._renderer = null; + } + }; + /** + * Extension metadata for registering this system with the renderer. + * @ignore + */ + _RenderableGCSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "renderableGC", + priority: 0 + }; + /** + * Default configuration options for the garbage collection system. + * These can be overridden when initializing the renderer. + * @deprecated since 8.15.0 + */ + _RenderableGCSystem.defaultOptions = { + /** Enable/disable the garbage collector */ + renderableGCActive: true, + /** Time in ms before an unused resource is collected (default 1 minute) */ + renderableGCMaxUnusedTime: 6e4, + /** How often to run garbage collection in ms (default 30 seconds) */ + renderableGCFrequency: 3e4 + }; + let RenderableGCSystem = _RenderableGCSystem; + + "use strict"; + const _TextureGCSystem = class _TextureGCSystem { + /** + * Frame count since started. + * @readonly + * @deprecated since 8.15.0 + */ + get count() { + return this._renderer.tick; + } + /** + * Frame count since last garbage collection. + * @readonly + * @deprecated since 8.15.0 + */ + get checkCount() { + return this._checkCount; + } + set checkCount(value) { + deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead."); + this._checkCount = value; + } + /** + * Maximum idle frames before a texture is destroyed by garbage collection. + * @see TextureGCSystem.defaultMaxIdle + * @deprecated since 8.15.0 + */ + get maxIdle() { + return this._renderer.gc.maxUnusedTime / 1e3 * 60; + } + set maxIdle(value) { + deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead."); + this._renderer.gc.maxUnusedTime = value / 60 * 1e3; + } + /** + * Frames between two garbage collections. + * @see TextureGCSystem.defaultCheckCountMax + * @deprecated since 8.15.0 + */ + // eslint-disable-next-line dot-notation + get checkCountMax() { + return Math.floor(this._renderer.gc["_frequency"] / 1e3); + } + set checkCountMax(_value) { + deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead."); + } + /** + * Current garbage collection mode. + * @see TextureGCSystem.defaultMode + * @deprecated since 8.15.0 + */ + get active() { + return this._renderer.gc.enabled; + } + set active(value) { + deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead."); + this._renderer.gc.enabled = value; + } + /** @param renderer - The renderer this System works for. */ + constructor(renderer) { + this._renderer = renderer; + this._checkCount = 0; + } + init(options) { + if (options.textureGCActive !== _TextureGCSystem.defaultOptions.textureGCActive) { + this.active = options.textureGCActive; + } + if (options.textureGCMaxIdle !== _TextureGCSystem.defaultOptions.textureGCMaxIdle) { + this.maxIdle = options.textureGCMaxIdle; + } + if (options.textureGCCheckCountMax !== _TextureGCSystem.defaultOptions.textureGCCheckCountMax) { + this.checkCountMax = options.textureGCCheckCountMax; + } + } + /** + * Checks to see when the last time a texture was used. + * If the texture has not been used for a specified amount of time, it will be removed from the GPU. + * @deprecated since 8.15.0 + */ + run() { + deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead."); + this._renderer.gc.run(); + } + destroy() { + this._renderer = null; + } + }; + /** @ignore */ + _TextureGCSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem + ], + name: "textureGC" + }; + /** + * Default options for the TextureGCSystem + * @deprecated since 8.15.0 + */ + _TextureGCSystem.defaultOptions = { + /** + * If set to true, this will enable the garbage collector on the GPU. + * @default true + */ + textureGCActive: true, + /** + * @deprecated since 8.3.0 + * @see {@link TextureGCSystemOptions.textureGCMaxIdle} + */ + textureGCAMaxIdle: null, + /** + * The maximum idle frames before a texture is destroyed by garbage collection. + * @default 60 * 60 + */ + textureGCMaxIdle: 60 * 60, + /** + * Frames between two garbage collections. + * @default 600 + */ + textureGCCheckCountMax: 600 + }; + let TextureGCSystem = _TextureGCSystem; + + "use strict"; + var __defProp$r = Object.defineProperty; + var __getOwnPropSymbols$s = Object.getOwnPropertySymbols; + var __hasOwnProp$s = Object.prototype.hasOwnProperty; + var __propIsEnum$s = Object.prototype.propertyIsEnumerable; + var __defNormalProp$r = (obj, key, value) => key in obj ? __defProp$r(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$r = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$s.call(b, prop)) + __defNormalProp$r(a, prop, b[prop]); + if (__getOwnPropSymbols$s) + for (var prop of __getOwnPropSymbols$s(b)) { + if (__propIsEnum$s.call(b, prop)) + __defNormalProp$r(a, prop, b[prop]); + } + return a; + }; + const _RenderTarget = class _RenderTarget { + /** + * @param [descriptor] - Options for creating a render target. + */ + constructor(descriptor = {}) { + /** unique id for this render target */ + this.uid = uid$1("renderTarget"); + /** + * An array of textures that can be written to by the GPU - mostly this has one texture in Pixi, but you could + * write to multiple if required! (eg deferred lighting) + */ + this.colorTextures = []; + this.dirtyId = 0; + this.isRoot = false; + this._size = new Float32Array(2); + /** if true, then when the render target is destroyed, it will destroy all the textures that were created for it. */ + this._managedColorTextures = false; + descriptor = __spreadValues$r(__spreadValues$r({}, _RenderTarget.defaultOptions), descriptor); + this.stencil = descriptor.stencil; + this.depth = descriptor.depth; + this.isRoot = descriptor.isRoot; + if (typeof descriptor.colorTextures === "number") { + this._managedColorTextures = true; + for (let i = 0; i < descriptor.colorTextures; i++) { + this.colorTextures.push( + new TextureSource({ + width: descriptor.width, + height: descriptor.height, + resolution: descriptor.resolution, + antialias: descriptor.antialias + }) + ); + } + } else { + this.colorTextures = [...descriptor.colorTextures.map((texture) => texture.source)]; + const colorSource = this.colorTexture.source; + this.resize(colorSource.width, colorSource.height, colorSource._resolution); + } + this.colorTexture.source.on("resize", this.onSourceResize, this); + if (descriptor.depthStencilTexture || this.stencil) { + if (descriptor.depthStencilTexture instanceof Texture || descriptor.depthStencilTexture instanceof TextureSource) { + this.depthStencilTexture = descriptor.depthStencilTexture.source; + } else { + this.ensureDepthStencilTexture(); + } + } + } + get size() { + const _size = this._size; + _size[0] = this.pixelWidth; + _size[1] = this.pixelHeight; + return _size; + } + get width() { + return this.colorTexture.source.width; + } + get height() { + return this.colorTexture.source.height; + } + get pixelWidth() { + return this.colorTexture.source.pixelWidth; + } + get pixelHeight() { + return this.colorTexture.source.pixelHeight; + } + get resolution() { + return this.colorTexture.source._resolution; + } + get colorTexture() { + return this.colorTextures[0]; + } + onSourceResize(source) { + this.resize(source.width, source.height, source._resolution, true); + } + /** + * This will ensure a depthStencil texture is created for this render target. + * Most likely called by the mask system to make sure we have stencil buffer added. + * @internal + */ + ensureDepthStencilTexture() { + if (!this.depthStencilTexture) { + this.depthStencilTexture = new TextureSource({ + width: this.width, + height: this.height, + resolution: this.resolution, + format: "depth24plus-stencil8", + autoGenerateMipmaps: false, + antialias: false, + mipLevelCount: 1 + // sampleCount: handled by the render target system.. + }); + } + } + resize(width, height, resolution = this.resolution, skipColorTexture = false) { + this.dirtyId++; + this.colorTextures.forEach((colorTexture, i) => { + if (skipColorTexture && i === 0) return; + colorTexture.source.resize(width, height, resolution); + }); + if (this.depthStencilTexture) { + this.depthStencilTexture.source.resize(width, height, resolution); + } + } + destroy() { + this.colorTexture.source.off("resize", this.onSourceResize, this); + if (this._managedColorTextures) { + this.colorTextures.forEach((texture) => { + texture.destroy(); + }); + } + if (this.depthStencilTexture) { + this.depthStencilTexture.destroy(); + delete this.depthStencilTexture; + } + } + }; + /** The default options for a render target */ + _RenderTarget.defaultOptions = { + /** the width of the RenderTarget */ + width: 0, + /** the height of the RenderTarget */ + height: 0, + /** the resolution of the RenderTarget */ + resolution: 1, + /** an array of textures, or a number indicating how many color textures there should be */ + colorTextures: 1, + /** should this render target have a stencil buffer? */ + stencil: false, + /** should this render target have a depth buffer? */ + depth: false, + /** should this render target be antialiased? */ + antialias: false, + // save on perf by default! + /** is this a root element, true if this is gl context owners render target */ + isRoot: false + }; + let RenderTarget = _RenderTarget; + + "use strict"; + var __defProp$q = Object.defineProperty; + var __getOwnPropSymbols$r = Object.getOwnPropertySymbols; + var __hasOwnProp$r = Object.prototype.hasOwnProperty; + var __propIsEnum$r = Object.prototype.propertyIsEnumerable; + var __defNormalProp$q = (obj, key, value) => key in obj ? __defProp$q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$q = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$r.call(b, prop)) + __defNormalProp$q(a, prop, b[prop]); + if (__getOwnPropSymbols$r) + for (var prop of __getOwnPropSymbols$r(b)) { + if (__propIsEnum$r.call(b, prop)) + __defNormalProp$q(a, prop, b[prop]); + } + return a; + }; + const canvasCache = /* @__PURE__ */ new Map(); + GlobalResourceRegistry.register(canvasCache); + function getCanvasTexture(canvas, options) { + if (!canvasCache.has(canvas)) { + const texture = new Texture({ + source: new CanvasSource(__spreadValues$q({ + resource: canvas + }, options)) + }); + const onDestroy = () => { + if (canvasCache.get(canvas) === texture) { + canvasCache.delete(canvas); + } + }; + texture.once("destroy", onDestroy); + texture.source.once("destroy", onDestroy); + canvasCache.set(canvas, texture); + } + return canvasCache.get(canvas); + } + function hasCachedCanvasTexture(canvas) { + return canvasCache.has(canvas); + } + + "use strict"; + var __defProp$p = Object.defineProperty; + var __getOwnPropSymbols$q = Object.getOwnPropertySymbols; + var __hasOwnProp$q = Object.prototype.hasOwnProperty; + var __propIsEnum$q = Object.prototype.propertyIsEnumerable; + var __defNormalProp$p = (obj, key, value) => key in obj ? __defProp$p(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$p = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$q.call(b, prop)) + __defNormalProp$p(a, prop, b[prop]); + if (__getOwnPropSymbols$q) + for (var prop of __getOwnPropSymbols$q(b)) { + if (__propIsEnum$q.call(b, prop)) + __defNormalProp$p(a, prop, b[prop]); + } + return a; + }; + const _ViewSystem = class _ViewSystem { + /** + * Whether CSS dimensions of canvas view should be resized to screen dimensions automatically. + * This is only supported for HTMLCanvasElement and will be ignored if the canvas is an OffscreenCanvas. + * @type {boolean} + */ + get autoDensity() { + return this.texture.source.autoDensity; + } + set autoDensity(value) { + this.texture.source.autoDensity = value; + } + /** The resolution / device pixel ratio of the renderer. */ + get resolution() { + return this.texture.source._resolution; + } + set resolution(value) { + this.texture.source.resize( + this.texture.source.width, + this.texture.source.height, + value + ); + } + /** + * initiates the view system + * @param options - the options for the view + */ + init(options) { + options = __spreadValues$p(__spreadValues$p({}, _ViewSystem.defaultOptions), options); + if (options.view) { + deprecation(v8_0_0, "ViewSystem.view has been renamed to ViewSystem.canvas"); + options.canvas = options.view; + } + this.screen = new Rectangle(0, 0, options.width, options.height); + this.canvas = options.canvas || DOMAdapter.get().createCanvas(); + this.antialias = !!options.antialias; + this.texture = getCanvasTexture(this.canvas, options); + this.renderTarget = new RenderTarget({ + colorTextures: [this.texture], + depth: !!options.depth, + isRoot: true + }); + this.texture.source.transparent = options.backgroundAlpha < 1; + this.resolution = options.resolution; + } + /** + * Resizes the screen and canvas to the specified dimensions. + * @param desiredScreenWidth - The new width of the screen. + * @param desiredScreenHeight - The new height of the screen. + * @param resolution + */ + resize(desiredScreenWidth, desiredScreenHeight, resolution) { + this.texture.source.resize(desiredScreenWidth, desiredScreenHeight, resolution); + this.screen.width = this.texture.frame.width; + this.screen.height = this.texture.frame.height; + } + /** + * Destroys this System and optionally removes the canvas from the dom. + * @param {options | false} options - The options for destroying the view, or "false". + * @example + * viewSystem.destroy(); + * viewSystem.destroy(true); + * viewSystem.destroy({ removeView: true }); + */ + destroy(options = false) { + const removeView = typeof options === "boolean" ? options : !!(options == null ? void 0 : options.removeView); + if (removeView && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + this.texture.destroy(); + } + }; + /** @ignore */ + _ViewSystem.extension = { + type: [ + ExtensionType.WebGLSystem, + ExtensionType.WebGPUSystem, + ExtensionType.CanvasSystem + ], + name: "view", + priority: 0 + }; + /** The default options for the view system. */ + _ViewSystem.defaultOptions = { + /** + * {@link WebGLOptions.width} + * @default 800 + */ + width: 800, + /** + * {@link WebGLOptions.height} + * @default 600 + */ + height: 600, + /** + * {@link WebGLOptions.autoDensity} + * @default false + */ + autoDensity: false, + /** + * {@link WebGLOptions.antialias} + * @default false + */ + antialias: false + }; + let ViewSystem = _ViewSystem; + + "use strict"; + const SharedSystems = [ + BackgroundSystem, + GlobalUniformSystem, + HelloSystem, + ViewSystem, + RenderGroupSystem, + GCSystem, + TextureGCSystem, + GenerateTextureSystem, + ExtractSystem, + RendererInitHook, + RenderableGCSystem, + SchedulerSystem + ]; + const SharedRenderPipes = [ + BlendModePipe, + BatcherPipe, + SpritePipe, + RenderGroupPipe, + AlphaMaskPipe, + StencilMaskPipe, + ColorMaskPipe, + CustomRenderPipe + ]; + + "use strict"; + function calculateProjection(pm, x, y, width, height, flipY) { + const sign = flipY ? 1 : -1; + pm.identity(); + pm.a = 1 / width * 2; + pm.d = sign * (1 / height * 2); + pm.tx = -1 - x * pm.a; + pm.ty = -sign - y * pm.d; + return pm; + } + + "use strict"; + function isRenderingToScreen(renderTarget) { + const resource = renderTarget.colorTexture.source.resource; + return globalThis.HTMLCanvasElement && resource instanceof HTMLCanvasElement && document.body.contains(resource); + } + + "use strict"; + class RenderTargetSystem { + constructor(renderer) { + /** This is the root viewport for the render pass */ + this.rootViewPort = new Rectangle(); + /** the current viewport that the gpu is using */ + this.viewport = new Rectangle(); + /** the current mip level being rendered to (for texture subresources) */ + this.mipLevel = 0; + /** the current array layer being rendered to (for array-backed targets) */ + this.layer = 0; + /** + * a runner that lets systems know if the active render target has changed. + * Eg the Stencil System needs to know so it can manage the stencil buffer + */ + this.onRenderTargetChange = new SystemRunner("onRenderTargetChange"); + /** the projection matrix that is used by the shaders based on the active render target and the viewport */ + this.projectionMatrix = new Matrix(); + /** the default clear color for render targets */ + this.defaultClearColor = [0, 0, 0, 0]; + /** + * a hash that stores the render target for a given render surface. When you pass in a texture source, + * a render target is created for it. This map stores and makes it easy to retrieve the render target + */ + this._renderSurfaceToRenderTargetHash = /* @__PURE__ */ new Map(); + /** A hash that stores a gpu render target for a given render target. */ + this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null); + /** + * A stack that stores the render target and frame that is currently being rendered to. + * When push is called, the current render target is stored in this stack. + * When pop is called, the previous render target is restored. + */ + this._renderTargetStack = []; + this._renderer = renderer; + renderer.gc.addCollection(this, "_gpuRenderTargetHash", "hash"); + } + /** called when dev wants to finish a render pass */ + finishRenderPass() { + this.adaptor.finishRenderPass(this.renderTarget); + } + /** + * called when the renderer starts to render a scene. + * @param options + * @param options.target - the render target to render to + * @param options.clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111 + * @param options.clearColor - the color to clear to + * @param options.frame - the frame to render to + * @param options.mipLevel - the mip level to render to + * @param options.layer - The layer of the render target to render to. Used for array or 3D textures, or when rendering + * to a specific layer of a layered render target. Optional. + */ + renderStart({ + target, + clear, + clearColor, + frame, + mipLevel, + layer + }) { + var _a, _b; + this._renderTargetStack.length = 0; + this.push( + target, + clear, + clearColor, + frame, + mipLevel != null ? mipLevel : 0, + layer != null ? layer : 0 + ); + this.rootViewPort.copyFrom(this.viewport); + this.rootRenderTarget = this.renderTarget; + this.renderingToScreen = isRenderingToScreen(this.rootRenderTarget); + (_b = (_a = this.adaptor).prerender) == null ? void 0 : _b.call(_a, this.rootRenderTarget); + } + postrender() { + var _a, _b; + (_b = (_a = this.adaptor).postrender) == null ? void 0 : _b.call(_a, this.rootRenderTarget); + } + /** + * Binding a render surface! This is the main function of the render target system. + * It will take the RenderSurface (which can be a texture, canvas, or render target) and bind it to the renderer. + * Once bound all draw calls will be rendered to the render surface. + * + * If a frame is not provided and the render surface is a {@link Texture}, the frame of the texture will be used. + * + * IMPORTANT: + * - `frame` is treated as **base mip (mip 0) pixel space**. + * - When `mipLevel > 0`, the viewport derived from `frame` is scaled by \(2^{mipLevel}\) and clamped to the + * mip dimensions. This keeps "render the same region" semantics consistent across mip levels. + * - When `renderSurface` is a {@link Texture}, `renderer.render({ container, target: texture, mipLevel })` will + * render into + * the underlying {@link TextureSource} (Pixi will create/use a {@link RenderTarget} for the source) using the + * texture's frame to define the region (in mip 0 space). + * @param renderSurface - the render surface to bind + * @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111 + * @param clearColor - the color to clear to + * @param frame - the frame to render to + * @param mipLevel - the mip level to render to + * @param layer - the layer (or slice) of the render surface to render to. For array textures, + * 3D textures, or cubemaps, this specifies the target layer or face. Defaults to 0 (the first layer/face). + * Ignored for surfaces that do not support layers. + * @returns the render target that was bound + */ + bind(renderSurface, clear = true, clearColor, frame, mipLevel = 0, layer = 0) { + const renderTarget = this.getRenderTarget(renderSurface); + const didChange = this.renderTarget !== renderTarget; + this.renderTarget = renderTarget; + this.renderSurface = renderSurface; + const gpuRenderTarget = this.getGpuRenderTarget(renderTarget); + if (renderTarget.pixelWidth !== gpuRenderTarget.width || renderTarget.pixelHeight !== gpuRenderTarget.height) { + this.adaptor.resizeGpuRenderTarget(renderTarget); + gpuRenderTarget.width = renderTarget.pixelWidth; + gpuRenderTarget.height = renderTarget.pixelHeight; + } + const source = renderTarget.colorTexture; + const viewport = this.viewport; + const arrayLayerCount = source.arrayLayerCount || 1; + if ((layer | 0) !== layer) { + layer |= 0; + } + if (layer < 0 || layer >= arrayLayerCount) { + throw new Error(`[RenderTargetSystem] layer ${layer} is out of bounds (arrayLayerCount=${arrayLayerCount}).`); + } + this.mipLevel = mipLevel | 0; + this.layer = layer | 0; + const pixelWidth = Math.max(source.pixelWidth >> mipLevel, 1); + const pixelHeight = Math.max(source.pixelHeight >> mipLevel, 1); + if (!frame && renderSurface instanceof Texture) { + frame = renderSurface.frame; + } + if (frame) { + const resolution = source._resolution; + const scale = 1 << Math.max(mipLevel | 0, 0); + const baseX = frame.x * resolution + 0.5 | 0; + const baseY = frame.y * resolution + 0.5 | 0; + const baseW = frame.width * resolution + 0.5 | 0; + const baseH = frame.height * resolution + 0.5 | 0; + let x = Math.floor(baseX / scale); + let y = Math.floor(baseY / scale); + let w = Math.ceil(baseW / scale); + let h = Math.ceil(baseH / scale); + x = Math.min(Math.max(x, 0), pixelWidth - 1); + y = Math.min(Math.max(y, 0), pixelHeight - 1); + w = Math.min(Math.max(w, 1), pixelWidth - x); + h = Math.min(Math.max(h, 1), pixelHeight - y); + viewport.x = x; + viewport.y = y; + viewport.width = w; + viewport.height = h; + } else { + viewport.x = 0; + viewport.y = 0; + viewport.width = pixelWidth; + viewport.height = pixelHeight; + } + calculateProjection( + this.projectionMatrix, + 0, + 0, + viewport.width / source.resolution, + viewport.height / source.resolution, + !renderTarget.isRoot + ); + this.adaptor.startRenderPass(renderTarget, clear, clearColor, viewport, mipLevel, layer); + if (didChange) { + this.onRenderTargetChange.emit(renderTarget); + } + return renderTarget; + } + clear(target, clear = CLEAR.ALL, clearColor, mipLevel = this.mipLevel, layer = this.layer) { + if (!clear) return; + if (target) { + target = this.getRenderTarget(target); + } + this.adaptor.clear( + target || this.renderTarget, + clear, + clearColor, + this.viewport, + mipLevel, + layer + ); + } + contextChange() { + this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null); + } + /** + * Push a render surface to the renderer. This will bind the render surface to the renderer, + * @param renderSurface - the render surface to push + * @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111 + * @param clearColor - the color to clear to + * @param frame - the frame to use when rendering to the render surface + * @param mipLevel - the mip level to render to + * @param layer - The layer of the render surface to render to. For array textures or cube maps, this specifies + * which layer or face to target. Defaults to 0 (the first layer). + */ + push(renderSurface, clear = CLEAR.ALL, clearColor, frame, mipLevel = 0, layer = 0) { + const renderTarget = this.bind(renderSurface, clear, clearColor, frame, mipLevel, layer); + this._renderTargetStack.push({ + renderTarget, + frame, + mipLevel, + layer + }); + return renderTarget; + } + /** Pops the current render target from the renderer and restores the previous render target. */ + pop() { + this._renderTargetStack.pop(); + const currentRenderTargetData = this._renderTargetStack[this._renderTargetStack.length - 1]; + this.bind( + currentRenderTargetData.renderTarget, + false, + null, + currentRenderTargetData.frame, + currentRenderTargetData.mipLevel, + currentRenderTargetData.layer + ); + } + /** + * Gets the render target from the provide render surface. Eg if its a texture, + * it will return the render target for the texture. + * If its a render target, it will return the same render target. + * @param renderSurface - the render surface to get the render target for + * @returns the render target for the render surface + */ + getRenderTarget(renderSurface) { + var _a; + if (renderSurface.isTexture) { + renderSurface = renderSurface.source; + } + return (_a = this._renderSurfaceToRenderTargetHash.get(renderSurface)) != null ? _a : this._initRenderTarget(renderSurface); + } + /** + * Copies a render surface to another texture. + * + * NOTE: + * for sourceRenderSurfaceTexture, The render target must be something that is written too by the renderer + * + * The following is not valid: + * @example + * const canvas = document.createElement('canvas') + * canvas.width = 200; + * canvas.height = 200; + * + * const ctx = canvas2.getContext('2d')! + * ctx.fillStyle = 'red' + * ctx.fillRect(0, 0, 200, 200); + * + * const texture = RenderTexture.create({ + * width: 200, + * height: 200, + * }) + * const renderTarget = renderer.renderTarget.getRenderTarget(canvas2); + * + * renderer.renderTarget.copyToTexture(renderTarget,texture, {x:0,y:0},{width:200,height:200},{x:0,y:0}); + * + * The best way to copy a canvas is to create a texture from it. Then render with that. + * + * Parsing in a RenderTarget canvas context (with a 2d context) + * @param sourceRenderSurfaceTexture - the render surface to copy from + * @param {Texture} destinationTexture - the texture to copy to + * @param {object} originSrc - the origin of the copy + * @param {number} originSrc.x - the x origin of the copy + * @param {number} originSrc.y - the y origin of the copy + * @param {object} size - the size of the copy + * @param {number} size.width - the width of the copy + * @param {number} size.height - the height of the copy + * @param {object} originDest - the destination origin (top left to paste from!) + * @param {number} originDest.x - the x origin of the paste + * @param {number} originDest.y - the y origin of the paste + */ + copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) { + if (originSrc.x < 0) { + size.width += originSrc.x; + originDest.x -= originSrc.x; + originSrc.x = 0; + } + if (originSrc.y < 0) { + size.height += originSrc.y; + originDest.y -= originSrc.y; + originSrc.y = 0; + } + const { pixelWidth, pixelHeight } = sourceRenderSurfaceTexture; + size.width = Math.min(size.width, pixelWidth - originSrc.x); + size.height = Math.min(size.height, pixelHeight - originSrc.y); + return this.adaptor.copyToTexture( + sourceRenderSurfaceTexture, + destinationTexture, + originSrc, + size, + originDest + ); + } + /** + * ensures that we have a depth stencil buffer available to render to + * This is used by the mask system to make sure we have a stencil buffer. + */ + ensureDepthStencil() { + if (!this.renderTarget.stencil) { + this.renderTarget.stencil = true; + this.adaptor.startRenderPass(this.renderTarget, false, null, this.viewport, 0, this.layer); + } + } + /** nukes the render target system */ + destroy() { + this._renderer = null; + this._renderSurfaceToRenderTargetHash.forEach((renderTarget, key) => { + if (renderTarget !== key) { + renderTarget.destroy(); + } + }); + this._renderSurfaceToRenderTargetHash.clear(); + this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null); + } + _initRenderTarget(renderSurface) { + let renderTarget = null; + if (CanvasSource.test(renderSurface)) { + renderSurface = getCanvasTexture(renderSurface).source; + } + if (renderSurface instanceof RenderTarget) { + renderTarget = renderSurface; + } else if (renderSurface instanceof TextureSource) { + renderTarget = new RenderTarget({ + colorTextures: [renderSurface] + }); + if (renderSurface.source instanceof CanvasSource) { + renderTarget.isRoot = true; + } + renderSurface.once("destroy", () => { + renderTarget.destroy(); + this._renderSurfaceToRenderTargetHash.delete(renderSurface); + const gpuRenderTarget = this._gpuRenderTargetHash[renderTarget.uid]; + if (gpuRenderTarget) { + this._gpuRenderTargetHash[renderTarget.uid] = null; + this.adaptor.destroyGpuRenderTarget(gpuRenderTarget); + } + }); + } + this._renderSurfaceToRenderTargetHash.set(renderSurface, renderTarget); + return renderTarget; + } + getGpuRenderTarget(renderTarget) { + return this._gpuRenderTargetHash[renderTarget.uid] || (this._gpuRenderTargetHash[renderTarget.uid] = this.adaptor.initGpuRenderTarget(renderTarget)); + } + resetState() { + this.renderTarget = null; + this.renderSurface = null; + } + } + + "use strict"; + class CanvasRenderTargetAdaptor { + /** + * Initializes the adaptor. + * @param renderer - Canvas renderer instance. + * @param renderTargetSystem - The render target system. + * @advanced + */ + init(renderer, renderTargetSystem) { + this._renderer = renderer; + this._renderTargetSystem = renderTargetSystem; + } + /** + * Creates a GPU render target for canvas. + * @param renderTarget - Render target to initialize. + * @advanced + */ + initGpuRenderTarget(renderTarget) { + const colorTexture = renderTarget.colorTexture; + const { canvas, context } = this._ensureCanvas(colorTexture); + return { + canvas, + context, + width: canvas.width, + height: canvas.height + }; + } + /** + * Resizes the backing canvas for a render target. + * @param renderTarget - Render target to resize. + * @advanced + */ + resizeGpuRenderTarget(renderTarget) { + const colorTexture = renderTarget.colorTexture; + const { canvas } = this._ensureCanvas(colorTexture); + canvas.width = renderTarget.pixelWidth; + canvas.height = renderTarget.pixelHeight; + } + /** + * Starts a render pass on the canvas target. + * @param renderTarget - Target to render to. + * @param clear - Clear mode. + * @param clearColor - Optional clear color. + * @param viewport - Optional viewport. + * @advanced + */ + startRenderPass(renderTarget, clear, clearColor, viewport) { + const gpuRenderTarget = this._renderTargetSystem.getGpuRenderTarget(renderTarget); + this._renderer.canvasContext.activeContext = gpuRenderTarget.context; + this._renderer.canvasContext.activeResolution = renderTarget.resolution; + if (clear) { + this.clear(renderTarget, clear, clearColor, viewport); + } + } + /** + * Clears the render target. + * @param renderTarget - Target to clear. + * @param _clear - Clear mode (unused). + * @param clearColor - Optional clear color. + * @param viewport - Optional viewport rectangle. + * @advanced + */ + clear(renderTarget, _clear, clearColor, viewport) { + const gpuRenderTarget = this._renderTargetSystem.getGpuRenderTarget(renderTarget); + const context = gpuRenderTarget.context; + const bounds = viewport || { x: 0, y: 0, width: renderTarget.pixelWidth, height: renderTarget.pixelHeight }; + context.setTransform(1, 0, 0, 1, 0, 0); + context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height); + if (clearColor) { + const color = Color.shared.setValue(clearColor); + if (color.alpha > 0) { + context.globalAlpha = color.alpha; + context.fillStyle = color.toHex(); + context.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + context.globalAlpha = 1; + } + } + } + /** + * Finishes the render pass (no-op for canvas). + * @advanced + */ + finishRenderPass() { + } + /** + * Copies a render target into a texture source. + * @param {RenderTarget} sourceRenderSurfaceTexture - Source render target. + * @param {Texture} destinationTexture - Destination texture. + * @param {object} originSrc - Source origin. + * @param {number} originSrc.x - Source x origin. + * @param {number} originSrc.y - Source y origin. + * @param {object} size - Copy size. + * @param {number} size.width - Copy width. + * @param {number} size.height - Copy height. + * @param {object} [originDest] - Destination origin. + * @param {number} originDest.x - Destination x origin. + * @param {number} originDest.y - Destination y origin. + * @advanced + */ + copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) { + var _a, _b; + const sourceGpuTarget = this._renderTargetSystem.getGpuRenderTarget(sourceRenderSurfaceTexture); + const sourceCanvas = sourceGpuTarget.canvas; + const destSource = destinationTexture.source; + const { context } = this._ensureCanvas(destSource); + const dx = (_a = originDest == null ? void 0 : originDest.x) != null ? _a : 0; + const dy = (_b = originDest == null ? void 0 : originDest.y) != null ? _b : 0; + context.drawImage( + sourceCanvas, + originSrc.x, + originSrc.y, + size.width, + size.height, + dx, + dy, + size.width, + size.height + ); + destSource.update(); + return destinationTexture; + } + /** + * Destroys a GPU render target (no-op for canvas). + * @param _gpuRenderTarget - Target to destroy. + * @advanced + */ + destroyGpuRenderTarget(_gpuRenderTarget) { + } + _ensureCanvas(source) { + let canvas = source.resource; + if (!canvas || !CanvasSource.test(canvas)) { + canvas = DOMAdapter.get().createCanvas(source.pixelWidth, source.pixelHeight); + source.resource = canvas; + } + if (canvas.width !== source.pixelWidth || canvas.height !== source.pixelHeight) { + canvas.width = source.pixelWidth; + canvas.height = source.pixelHeight; + } + const context = canvas.getContext("2d"); + return { canvas, context }; + } + } + + "use strict"; + class CanvasRenderTargetSystem extends RenderTargetSystem { + constructor(renderer) { + super(renderer); + this.adaptor = new CanvasRenderTargetAdaptor(); + this.adaptor.init(renderer, this); + } + } + /** @ignore */ + CanvasRenderTargetSystem.extension = { + type: [ExtensionType.CanvasSystem], + name: "renderTarget" + }; + + "use strict"; + class CanvasTextureSystem { + /** + * @param renderer - The owning CanvasRenderer. + */ + constructor(renderer) { + void renderer; + } + /** Initializes the system (no-op for canvas). */ + init() { + } + /** + * Initializes a texture source (no-op for canvas). + * @param _source - Texture source. + */ + initSource(_source) { + } + /** + * Creates a canvas containing the texture's frame. + * @param texture - Texture to render. + */ + generateCanvas(texture) { + var _a, _b; + const canvas = DOMAdapter.get().createCanvas(); + const context = canvas.getContext("2d"); + const source = canvasUtils.getCanvasSource(texture); + if (!source) { + return canvas; + } + const frame = texture.frame; + const resolution = (_b = (_a = texture.source._resolution) != null ? _a : texture.source.resolution) != null ? _b : 1; + const sx = frame.x * resolution; + const sy = frame.y * resolution; + const sw = frame.width * resolution; + const sh = frame.height * resolution; + canvas.width = Math.ceil(sw); + canvas.height = Math.ceil(sh); + context.drawImage( + source, + sx, + sy, + sw, + sh, + 0, + 0, + sw, + sh + ); + return canvas; + } + /** + * Reads pixel data from a texture. + * @param texture - Texture to read. + */ + getPixels(texture) { + const canvas = this.generateCanvas(texture); + const context = canvas.getContext("2d", { willReadFrequently: true }); + const imageData = context.getImageData(0, 0, canvas.width, canvas.height); + return { + pixels: imageData.data, + width: canvas.width, + height: canvas.height + }; + } + /** Destroys the system (no-op for canvas). */ + destroy() { + } + } + /** @ignore */ + CanvasTextureSystem.extension = { + type: [ + ExtensionType.CanvasSystem + ], + name: "texture" + }; + + "use strict"; + const DefaultCanvasSystems = [ + ...SharedSystems, + CanvasContextSystem, + CanvasLimitsSystem, + CanvasTextureSystem, + CanvasRenderTargetSystem + ]; + const DefaultCanvasPipes = [ + BlendModePipe, + BatcherPipe, + SpritePipe, + RenderGroupPipe, + AlphaMaskPipe, + CanvasStencilMaskPipe, + CanvasColorMaskPipe, + CustomRenderPipe + ]; + const DefaultCanvasAdapters = [ + CanvasBatchAdaptor, + CanvasGraphicsAdaptor + ]; + const systems$2 = []; + const renderPipes$2 = []; + const renderPipeAdaptors$2 = []; + extensions.handleByNamedList(ExtensionType.CanvasSystem, systems$2); + extensions.handleByNamedList(ExtensionType.CanvasPipes, renderPipes$2); + extensions.handleByNamedList(ExtensionType.CanvasPipesAdaptor, renderPipeAdaptors$2); + extensions.add(...DefaultCanvasSystems, ...DefaultCanvasPipes, ...DefaultCanvasAdapters); + class CanvasRenderer extends AbstractRenderer { + constructor() { + const systemConfig = { + name: "canvas", + type: RendererType.CANVAS, + systems: systems$2, + renderPipes: renderPipes$2, + renderPipeAdaptors: renderPipeAdaptors$2 + }; + super(systemConfig); + } + } + + var CanvasRenderer$1 = { + __proto__: null, + CanvasRenderer: CanvasRenderer + }; + + "use strict"; + var BUFFER_TYPE = /* @__PURE__ */ ((BUFFER_TYPE2) => { + BUFFER_TYPE2[BUFFER_TYPE2["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER"; + BUFFER_TYPE2[BUFFER_TYPE2["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER"; + BUFFER_TYPE2[BUFFER_TYPE2["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER"; + return BUFFER_TYPE2; + })(BUFFER_TYPE || {}); + + "use strict"; + class GlBuffer { + constructor(buffer, type) { + this._lastBindBaseLocation = -1; + this._lastBindCallId = -1; + this.buffer = buffer || null; + this.updateID = -1; + this.byteLength = -1; + this.type = type; + } + destroy() { + this.buffer = null; + this.updateID = -1; + this.byteLength = -1; + this.type = -1; + this._lastBindBaseLocation = -1; + this._lastBindCallId = -1; + } + } + + "use strict"; + class GlBufferSystem { + /** + * @param {Renderer} renderer - The renderer this System works for. + */ + constructor(renderer) { + /** Cache keeping track of the base bound buffer bases */ + this._boundBufferBases = /* @__PURE__ */ Object.create(null); + this._minBaseLocation = 0; + this._nextBindBaseIndex = this._minBaseLocation; + this._bindCallId = 0; + this._renderer = renderer; + this._managedBuffers = new GCManagedHash({ + renderer, + type: "resource", + onUnload: this.onBufferUnload.bind(this), + name: "glBuffer" + }); + } + /** @ignore */ + destroy() { + this._managedBuffers.destroy(); + this._renderer = null; + this._gl = null; + this._boundBufferBases = {}; + } + /** Sets up the renderer context and necessary buffers. */ + contextChange() { + this._gl = this._renderer.gl; + this.destroyAll(true); + this._maxBindings = this._renderer.limits.maxUniformBindings; + } + getGlBuffer(buffer) { + buffer._gcLastUsed = this._renderer.gc.now; + return buffer._gpuData[this._renderer.uid] || this.createGLBuffer(buffer); + } + /** + * This binds specified buffer. On first run, it will create the webGL buffers for the context too + * @param buffer - the buffer to bind to the renderer + */ + bind(buffer) { + const { _gl: gl } = this; + const glBuffer = this.getGlBuffer(buffer); + gl.bindBuffer(glBuffer.type, glBuffer.buffer); + } + /** + * Binds an uniform buffer to at the given index. + * + * A cache is used so a buffer will not be bound again if already bound. + * @param glBuffer - the buffer to bind + * @param index - the base index to bind it to. + */ + bindBufferBase(glBuffer, index) { + const { _gl: gl } = this; + if (this._boundBufferBases[index] !== glBuffer) { + this._boundBufferBases[index] = glBuffer; + glBuffer._lastBindBaseLocation = index; + gl.bindBufferBase(gl.UNIFORM_BUFFER, index, glBuffer.buffer); + } + } + nextBindBase(hasTransformFeedback) { + this._bindCallId++; + this._minBaseLocation = 0; + if (hasTransformFeedback) { + this._boundBufferBases[0] = null; + this._minBaseLocation = 1; + if (this._nextBindBaseIndex < 1) { + this._nextBindBaseIndex = 1; + } + } + } + freeLocationForBufferBase(glBuffer) { + let freeIndex = this.getLastBindBaseLocation(glBuffer); + if (freeIndex >= this._minBaseLocation) { + glBuffer._lastBindCallId = this._bindCallId; + return freeIndex; + } + let loop = 0; + let nextIndex = this._nextBindBaseIndex; + while (loop < 2) { + if (nextIndex >= this._maxBindings) { + nextIndex = this._minBaseLocation; + loop++; + } + const curBuf = this._boundBufferBases[nextIndex]; + if (curBuf && curBuf._lastBindCallId === this._bindCallId) { + nextIndex++; + continue; + } + break; + } + freeIndex = nextIndex; + this._nextBindBaseIndex = nextIndex + 1; + if (loop >= 2) { + return -1; + } + glBuffer._lastBindCallId = this._bindCallId; + this._boundBufferBases[freeIndex] = null; + return freeIndex; + } + getLastBindBaseLocation(glBuffer) { + const index = glBuffer._lastBindBaseLocation; + if (this._boundBufferBases[index] === glBuffer) { + return index; + } + return -1; + } + /** + * Binds a buffer whilst also binding its range. + * This will make the buffer start from the offset supplied rather than 0 when it is read. + * @param glBuffer - the buffer to bind + * @param index - the base index to bind at, defaults to 0 + * @param offset - the offset to bind at (this is blocks of 256). 0 = 0, 1 = 256, 2 = 512 etc + * @param size - the size to bind at (this is blocks of 256). + */ + bindBufferRange(glBuffer, index, offset, size) { + const { _gl: gl } = this; + offset || (offset = 0); + index || (index = 0); + this._boundBufferBases[index] = null; + gl.bindBufferRange(gl.UNIFORM_BUFFER, index || 0, glBuffer.buffer, offset * 256, size || 256); + } + /** + * Will ensure the data in the buffer is uploaded to the GPU. + * @param {Buffer} buffer - the buffer to update + */ + updateBuffer(buffer) { + const { _gl: gl } = this; + const glBuffer = this.getGlBuffer(buffer); + if (buffer._updateID === glBuffer.updateID) { + return glBuffer; + } + glBuffer.updateID = buffer._updateID; + gl.bindBuffer(glBuffer.type, glBuffer.buffer); + const data = buffer.data; + const drawType = buffer.descriptor.usage & BufferUsage.STATIC ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW; + if (data) { + if (glBuffer.byteLength >= data.byteLength) { + gl.bufferSubData(glBuffer.type, 0, data, 0, buffer._updateSize / data.BYTES_PER_ELEMENT); + } else { + glBuffer.byteLength = data.byteLength; + gl.bufferData(glBuffer.type, data, drawType); + } + } else { + glBuffer.byteLength = buffer.descriptor.size; + gl.bufferData(glBuffer.type, glBuffer.byteLength, drawType); + } + return glBuffer; + } + /** + * dispose all WebGL resources of all managed buffers + * @param contextLost + */ + destroyAll(contextLost = false) { + this._managedBuffers.removeAll(contextLost); + } + onBufferUnload(buffer, contextLost = false) { + const glBuffer = buffer._gpuData[this._renderer.uid]; + if (!glBuffer) return; + if (!contextLost) this._gl.deleteBuffer(glBuffer.buffer); + } + /** + * creates and attaches a GLBuffer object tied to the current context. + * @param buffer + * @protected + */ + createGLBuffer(buffer) { + const { _gl: gl } = this; + let type = BUFFER_TYPE.ARRAY_BUFFER; + if (buffer.descriptor.usage & BufferUsage.INDEX) { + type = BUFFER_TYPE.ELEMENT_ARRAY_BUFFER; + } else if (buffer.descriptor.usage & BufferUsage.UNIFORM) { + type = BUFFER_TYPE.UNIFORM_BUFFER; + } + const glBuffer = new GlBuffer(gl.createBuffer(), type); + buffer._gpuData[this._renderer.uid] = glBuffer; + this._managedBuffers.add(buffer); + return glBuffer; + } + resetState() { + this._boundBufferBases = /* @__PURE__ */ Object.create(null); + } + } + /** @ignore */ + GlBufferSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "buffer" + }; + + "use strict"; + var __defProp$o = Object.defineProperty; + var __defProps$f = Object.defineProperties; + var __getOwnPropDescs$f = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$p = Object.getOwnPropertySymbols; + var __hasOwnProp$p = Object.prototype.hasOwnProperty; + var __propIsEnum$p = Object.prototype.propertyIsEnumerable; + var __defNormalProp$o = (obj, key, value) => key in obj ? __defProp$o(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$o = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$p.call(b, prop)) + __defNormalProp$o(a, prop, b[prop]); + if (__getOwnPropSymbols$p) + for (var prop of __getOwnPropSymbols$p(b)) { + if (__propIsEnum$p.call(b, prop)) + __defNormalProp$o(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$f = (a, b) => __defProps$f(a, __getOwnPropDescs$f(b)); + const _GlContextSystem = class _GlContextSystem { + /** @param renderer - The renderer this System works for. */ + constructor(renderer) { + /** + * Features supported by current renderer. + * @type {object} + * @readonly + */ + this.supports = { + /** Support for 32-bit indices buffer. */ + uint32Indices: true, + /** Support for UniformBufferObjects */ + uniformBufferObject: true, + /** Support for VertexArrayObjects */ + vertexArrayObject: true, + /** Support for SRGB texture format */ + srgbTextures: true, + /** Support for wrapping modes if a texture is non-power of two */ + nonPowOf2wrapping: true, + /** Support for MSAA (antialiasing of dynamic textures) */ + msaa: true, + /** Support for mipmaps if a texture is non-power of two */ + nonPowOf2mipmaps: true + }; + this._renderer = renderer; + this.extensions = /* @__PURE__ */ Object.create(null); + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + } + /** + * `true` if the context is lost + * @readonly + */ + get isLost() { + return !this.gl || this.gl.isContextLost(); + } + /** + * Handles the context change event. + * @param {WebGLRenderingContext} gl - New WebGL context. + */ + contextChange(gl) { + this.gl = gl; + this._renderer.gl = gl; + } + init(options) { + var _a, _b; + options = __spreadValues$o(__spreadValues$o({}, _GlContextSystem.defaultOptions), options); + let multiView = this.multiView = options.multiView; + if (options.context && multiView) { + warn("Renderer created with both a context and multiview enabled. Disabling multiView as both cannot work together."); + multiView = false; + } + if (multiView) { + this.canvas = DOMAdapter.get().createCanvas(this._renderer.canvas.width, this._renderer.canvas.height); + } else { + this.canvas = this._renderer.view.canvas; + } + if (options.context) { + this.initFromContext(options.context); + } else { + const alpha = this._renderer.background.alpha < 1; + const premultipliedAlpha = (_a = options.premultipliedAlpha) != null ? _a : true; + const antialias = options.antialias && !this._renderer.backBuffer.useBackBuffer; + this.createContext(options.preferWebGLVersion, { + alpha, + premultipliedAlpha, + antialias, + stencil: true, + preserveDrawingBuffer: options.preserveDrawingBuffer, + powerPreference: (_b = options.powerPreference) != null ? _b : "default" + }); + } + } + ensureCanvasSize(targetCanvas) { + if (!this.multiView) { + if (targetCanvas !== this.canvas) { + warn("multiView is disabled, but targetCanvas is not the main canvas"); + } + return; + } + const { canvas } = this; + if (canvas.width < targetCanvas.width || canvas.height < targetCanvas.height) { + canvas.width = Math.max(targetCanvas.width, targetCanvas.width); + canvas.height = Math.max(targetCanvas.height, targetCanvas.height); + } + } + /** + * Initializes the context. + * @protected + * @param {WebGLRenderingContext} gl - WebGL context + */ + initFromContext(gl) { + this.gl = gl; + this.webGLVersion = gl instanceof DOMAdapter.get().getWebGLRenderingContext() ? 1 : 2; + this.getExtensions(); + this.validateContext(gl); + this._renderer.runners.contextChange.emit(gl); + const element = this._renderer.view.canvas; + element.addEventListener("webglcontextlost", this.handleContextLost, false); + element.addEventListener("webglcontextrestored", this.handleContextRestored, false); + } + /** + * Initialize from context options + * @protected + * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext + * @param preferWebGLVersion + * @param {object} options - context attributes + */ + createContext(preferWebGLVersion, options) { + let gl; + const canvas = this.canvas; + if (preferWebGLVersion === 2) { + gl = canvas.getContext("webgl2", options); + } + if (!gl) { + gl = canvas.getContext("webgl", options); + if (!gl) { + throw new Error("This browser does not support WebGL. Try using the canvas renderer"); + } + } + this.gl = gl; + this.initFromContext(this.gl); + } + /** Auto-populate the {@link GlContextSystem.extensions extensions}. */ + getExtensions() { + const { gl } = this; + const common = { + anisotropicFiltering: gl.getExtension("EXT_texture_filter_anisotropic"), + floatTextureLinear: gl.getExtension("OES_texture_float_linear"), + s3tc: gl.getExtension("WEBGL_compressed_texture_s3tc"), + s3tc_sRGB: gl.getExtension("WEBGL_compressed_texture_s3tc_srgb"), + // eslint-disable-line camelcase + etc: gl.getExtension("WEBGL_compressed_texture_etc"), + etc1: gl.getExtension("WEBGL_compressed_texture_etc1"), + pvrtc: gl.getExtension("WEBGL_compressed_texture_pvrtc") || gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"), + atc: gl.getExtension("WEBGL_compressed_texture_atc"), + astc: gl.getExtension("WEBGL_compressed_texture_astc"), + bptc: gl.getExtension("EXT_texture_compression_bptc"), + rgtc: gl.getExtension("EXT_texture_compression_rgtc"), + loseContext: gl.getExtension("WEBGL_lose_context") + }; + if (this.webGLVersion === 1) { + this.extensions = __spreadProps$f(__spreadValues$o({}, common), { + drawBuffers: gl.getExtension("WEBGL_draw_buffers"), + depthTexture: gl.getExtension("WEBGL_depth_texture"), + vertexArrayObject: gl.getExtension("OES_vertex_array_object") || gl.getExtension("MOZ_OES_vertex_array_object") || gl.getExtension("WEBKIT_OES_vertex_array_object"), + uint32ElementIndex: gl.getExtension("OES_element_index_uint"), + // Floats and half-floats + floatTexture: gl.getExtension("OES_texture_float"), + floatTextureLinear: gl.getExtension("OES_texture_float_linear"), + textureHalfFloat: gl.getExtension("OES_texture_half_float"), + textureHalfFloatLinear: gl.getExtension("OES_texture_half_float_linear"), + vertexAttribDivisorANGLE: gl.getExtension("ANGLE_instanced_arrays"), + srgb: gl.getExtension("EXT_sRGB") + }); + } else { + this.extensions = __spreadProps$f(__spreadValues$o({}, common), { + colorBufferFloat: gl.getExtension("EXT_color_buffer_float") + }); + const provokeExt = gl.getExtension("WEBGL_provoking_vertex"); + if (provokeExt) { + provokeExt.provokingVertexWEBGL(provokeExt.FIRST_VERTEX_CONVENTION_WEBGL); + } + } + } + /** + * Handles a lost webgl context + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) { + event.preventDefault(); + if (this._contextLossForced) { + this._contextLossForced = false; + setTimeout(() => { + var _a; + if (this.gl.isContextLost()) { + (_a = this.extensions.loseContext) == null ? void 0 : _a.restoreContext(); + } + }, 0); + } + } + /** Handles a restored webgl context. */ + handleContextRestored() { + this.getExtensions(); + this._renderer.runners.contextChange.emit(this.gl); + } + destroy() { + var _a; + const element = this._renderer.view.canvas; + this._renderer = null; + element.removeEventListener("webglcontextlost", this.handleContextLost); + element.removeEventListener("webglcontextrestored", this.handleContextRestored); + this.gl.useProgram(null); + (_a = this.extensions.loseContext) == null ? void 0 : _a.loseContext(); + } + /** + * this function can be called to force a webGL context loss + * this will release all resources on the GPU. + * Useful if you need to put Pixi to sleep, and save some GPU memory + * + * As soon as render is called - all resources will be created again. + */ + forceContextLoss() { + var _a; + (_a = this.extensions.loseContext) == null ? void 0 : _a.loseContext(); + this._contextLossForced = true; + } + /** + * Validate context. + * @param {WebGLRenderingContext} gl - Render context. + */ + validateContext(gl) { + const attributes = gl.getContextAttributes(); + if (attributes && !attributes.stencil) { + warn("Provided WebGL context does not have a stencil buffer, masks may not render correctly"); + } + const supports = this.supports; + const isWebGl2 = this.webGLVersion === 2; + const extensions = this.extensions; + supports.uint32Indices = isWebGl2 || !!extensions.uint32ElementIndex; + supports.uniformBufferObject = isWebGl2; + supports.vertexArrayObject = isWebGl2 || !!extensions.vertexArrayObject; + supports.srgbTextures = isWebGl2 || !!extensions.srgb; + supports.nonPowOf2wrapping = isWebGl2; + supports.nonPowOf2mipmaps = isWebGl2; + supports.msaa = isWebGl2; + if (!supports.uint32Indices) { + warn("Provided WebGL context does not support 32 index buffer, large scenes may not render correctly"); + } + } + }; + /** @ignore */ + _GlContextSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "context" + }; + /** The default options for the system. */ + _GlContextSystem.defaultOptions = { + /** + * {@link WebGLOptions.context} + * @default null + */ + context: null, + /** + * {@link WebGLOptions.premultipliedAlpha} + * @default true + */ + premultipliedAlpha: true, + /** + * {@link WebGLOptions.preserveDrawingBuffer} + * @default false + */ + preserveDrawingBuffer: false, + /** + * {@link WebGLOptions.powerPreference} + * @default default + */ + powerPreference: void 0, + /** + * {@link WebGLOptions.webGLVersion} + * @default 2 + */ + preferWebGLVersion: 2, + /** + * {@link WebGLOptions.multiView} + * @default false + */ + multiView: false + }; + let GlContextSystem = _GlContextSystem; + + "use strict"; + + "use strict"; + + "use strict"; + function ensureAttributes(geometry, extractedData) { + var _a, _b, _c; + for (const i in geometry.attributes) { + const attribute = geometry.attributes[i]; + const attributeData = extractedData[i]; + if (attributeData) { + (_a = attribute.format) != null ? _a : attribute.format = attributeData.format; + (_b = attribute.offset) != null ? _b : attribute.offset = attributeData.offset; + (_c = attribute.instance) != null ? _c : attribute.instance = attributeData.instance; + } else { + warn(`Attribute ${i} is not present in the shader, but is present in the geometry. Unable to infer attribute details.`); + } + } + ensureStartAndStride(geometry); + } + function ensureStartAndStride(geometry) { + var _a, _b; + const { buffers, attributes } = geometry; + const tempStride = {}; + const tempStart = {}; + for (const j in buffers) { + const buffer = buffers[j]; + tempStride[buffer.uid] = 0; + tempStart[buffer.uid] = 0; + } + for (const j in attributes) { + const attribute = attributes[j]; + tempStride[attribute.buffer.uid] += getAttributeInfoFromFormat(attribute.format).stride; + } + for (const j in attributes) { + const attribute = attributes[j]; + (_a = attribute.stride) != null ? _a : attribute.stride = tempStride[attribute.buffer.uid]; + (_b = attribute.start) != null ? _b : attribute.start = tempStart[attribute.buffer.uid]; + tempStart[attribute.buffer.uid] += getAttributeInfoFromFormat(attribute.format).stride; + } + } + + "use strict"; + var GL_FORMATS = /* @__PURE__ */ ((GL_FORMATS2) => { + GL_FORMATS2[GL_FORMATS2["RGBA"] = 6408] = "RGBA"; + GL_FORMATS2[GL_FORMATS2["RGB"] = 6407] = "RGB"; + GL_FORMATS2[GL_FORMATS2["RG"] = 33319] = "RG"; + GL_FORMATS2[GL_FORMATS2["RED"] = 6403] = "RED"; + GL_FORMATS2[GL_FORMATS2["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RGB_INTEGER"] = 36248] = "RGB_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RG_INTEGER"] = 33320] = "RG_INTEGER"; + GL_FORMATS2[GL_FORMATS2["RED_INTEGER"] = 36244] = "RED_INTEGER"; + GL_FORMATS2[GL_FORMATS2["ALPHA"] = 6406] = "ALPHA"; + GL_FORMATS2[GL_FORMATS2["LUMINANCE"] = 6409] = "LUMINANCE"; + GL_FORMATS2[GL_FORMATS2["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA"; + GL_FORMATS2[GL_FORMATS2["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT"; + GL_FORMATS2[GL_FORMATS2["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL"; + return GL_FORMATS2; + })(GL_FORMATS || {}); + var GL_TARGETS = /* @__PURE__ */ ((GL_TARGETS2) => { + GL_TARGETS2[GL_TARGETS2["TEXTURE_2D"] = 3553] = "TEXTURE_2D"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z"; + GL_TARGETS2[GL_TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z"; + return GL_TARGETS2; + })(GL_TARGETS || {}); + var GL_WRAP_MODES = /* @__PURE__ */ ((GL_WRAP_MODES2) => { + GL_WRAP_MODES2[GL_WRAP_MODES2["CLAMP"] = 33071] = "CLAMP"; + GL_WRAP_MODES2[GL_WRAP_MODES2["REPEAT"] = 10497] = "REPEAT"; + GL_WRAP_MODES2[GL_WRAP_MODES2["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT"; + return GL_WRAP_MODES2; + })(GL_WRAP_MODES || {}); + var GL_TYPES = /* @__PURE__ */ ((GL_TYPES2) => { + GL_TYPES2[GL_TYPES2["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4"; + GL_TYPES2[GL_TYPES2["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8"; + GL_TYPES2[GL_TYPES2["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV"; + GL_TYPES2[GL_TYPES2["BYTE"] = 5120] = "BYTE"; + GL_TYPES2[GL_TYPES2["SHORT"] = 5122] = "SHORT"; + GL_TYPES2[GL_TYPES2["INT"] = 5124] = "INT"; + GL_TYPES2[GL_TYPES2["FLOAT"] = 5126] = "FLOAT"; + GL_TYPES2[GL_TYPES2["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV"; + GL_TYPES2[GL_TYPES2["HALF_FLOAT"] = 36193] = "HALF_FLOAT"; + return GL_TYPES2; + })(GL_TYPES || {}); + + "use strict"; + const infoMap = { + uint8x2: GL_TYPES.UNSIGNED_BYTE, + uint8x4: GL_TYPES.UNSIGNED_BYTE, + sint8x2: GL_TYPES.BYTE, + sint8x4: GL_TYPES.BYTE, + unorm8x2: GL_TYPES.UNSIGNED_BYTE, + unorm8x4: GL_TYPES.UNSIGNED_BYTE, + snorm8x2: GL_TYPES.BYTE, + snorm8x4: GL_TYPES.BYTE, + uint16x2: GL_TYPES.UNSIGNED_SHORT, + uint16x4: GL_TYPES.UNSIGNED_SHORT, + sint16x2: GL_TYPES.SHORT, + sint16x4: GL_TYPES.SHORT, + unorm16x2: GL_TYPES.UNSIGNED_SHORT, + unorm16x4: GL_TYPES.UNSIGNED_SHORT, + snorm16x2: GL_TYPES.SHORT, + snorm16x4: GL_TYPES.SHORT, + float16x2: GL_TYPES.HALF_FLOAT, + float16x4: GL_TYPES.HALF_FLOAT, + float32: GL_TYPES.FLOAT, + float32x2: GL_TYPES.FLOAT, + float32x3: GL_TYPES.FLOAT, + float32x4: GL_TYPES.FLOAT, + uint32: GL_TYPES.UNSIGNED_INT, + uint32x2: GL_TYPES.UNSIGNED_INT, + uint32x3: GL_TYPES.UNSIGNED_INT, + uint32x4: GL_TYPES.UNSIGNED_INT, + sint32: GL_TYPES.INT, + sint32x2: GL_TYPES.INT, + sint32x3: GL_TYPES.INT, + sint32x4: GL_TYPES.INT + }; + function getGlTypeFromFormat(format) { + var _a; + return (_a = infoMap[format]) != null ? _a : infoMap.float32; + } + + "use strict"; + const topologyToGlMap = { + "point-list": 0, + "line-list": 1, + "line-strip": 3, + "triangle-list": 4, + "triangle-strip": 5 + }; + class GlGeometryGpuData { + constructor() { + this.vaoCache = /* @__PURE__ */ Object.create(null); + } + destroy() { + this.vaoCache = /* @__PURE__ */ Object.create(null); + } + } + class GlGeometrySystem { + /** @param renderer - The renderer this System works for. */ + constructor(renderer) { + this._renderer = renderer; + this._activeGeometry = null; + this._activeVao = null; + this.hasVao = true; + this.hasInstance = true; + this._managedGeometries = new GCManagedHash({ + renderer, + type: "resource", + onUnload: this.onGeometryUnload.bind(this), + name: "glGeometry" + }); + } + /** Sets up the renderer context and necessary buffers. */ + contextChange() { + const gl = this.gl = this._renderer.gl; + if (!this._renderer.context.supports.vertexArrayObject) { + throw new Error("[PixiJS] Vertex Array Objects are not supported on this device"); + } + this.destroyAll(true); + const nativeVaoExtension = this._renderer.context.extensions.vertexArrayObject; + if (nativeVaoExtension) { + gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES(); + gl.bindVertexArray = (vao) => nativeVaoExtension.bindVertexArrayOES(vao); + gl.deleteVertexArray = (vao) => nativeVaoExtension.deleteVertexArrayOES(vao); + } + const nativeInstancedExtension = this._renderer.context.extensions.vertexAttribDivisorANGLE; + if (nativeInstancedExtension) { + gl.drawArraysInstanced = (a, b, c, d) => { + nativeInstancedExtension.drawArraysInstancedANGLE(a, b, c, d); + }; + gl.drawElementsInstanced = (a, b, c, d, e) => { + nativeInstancedExtension.drawElementsInstancedANGLE(a, b, c, d, e); + }; + gl.vertexAttribDivisor = (a, b) => nativeInstancedExtension.vertexAttribDivisorANGLE(a, b); + } + this._activeGeometry = null; + this._activeVao = null; + } + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @param geometry - Instance of geometry to bind. + * @param program - Instance of program to use vao for. + */ + bind(geometry, program) { + const gl = this.gl; + this._activeGeometry = geometry; + const vao = this.getVao(geometry, program); + if (this._activeVao !== vao) { + this._activeVao = vao; + gl.bindVertexArray(vao); + } + this.updateBuffers(); + } + /** Reset and unbind any active VAO and geometry. */ + resetState() { + this.unbind(); + } + /** Update buffers of the currently bound geometry. */ + updateBuffers() { + const geometry = this._activeGeometry; + const bufferSystem = this._renderer.buffer; + for (let i = 0; i < geometry.buffers.length; i++) { + const buffer = geometry.buffers[i]; + bufferSystem.updateBuffer(buffer); + } + geometry._gcLastUsed = this._renderer.gc.now; + } + /** + * Check compatibility between a geometry and a program + * @param geometry - Geometry instance. + * @param program - Program instance. + */ + checkCompatibility(geometry, program) { + const geometryAttributes = geometry.attributes; + const shaderAttributes = program._attributeData; + for (const j in shaderAttributes) { + if (!geometryAttributes[j]) { + throw new Error(`shader and geometry incompatible, geometry missing the "${j}" attribute`); + } + } + } + /** + * Takes a geometry and program and generates a unique signature for them. + * @param geometry - To get signature from. + * @param program - To test geometry against. + * @returns - Unique signature of the geometry and program + */ + getSignature(geometry, program) { + const attribs = geometry.attributes; + const shaderAttributes = program._attributeData; + const strings = ["g", geometry.uid]; + for (const i in attribs) { + if (shaderAttributes[i]) { + strings.push(i, shaderAttributes[i].location); + } + } + return strings.join("-"); + } + getVao(geometry, program) { + var _a; + return ((_a = geometry._gpuData[this._renderer.uid]) == null ? void 0 : _a.vaoCache[program._key]) || this.initGeometryVao(geometry, program); + } + /** + * Creates or gets Vao with the same structure as the geometry and stores it on the geometry. + * If vao is created, it is bound automatically. We use a shader to infer what and how to set up the + * attribute locations. + * @param geometry - Instance of geometry to to generate Vao for. + * @param program + * @param _incRefCount - Increment refCount of all geometry buffers. + */ + initGeometryVao(geometry, program, _incRefCount = true) { + const gl = this._renderer.gl; + const bufferSystem = this._renderer.buffer; + this._renderer.shader._getProgramData(program); + this.checkCompatibility(geometry, program); + const signature = this.getSignature(geometry, program); + let gpuData = geometry._gpuData[this._renderer.uid]; + if (!gpuData) { + gpuData = new GlGeometryGpuData(); + geometry._gpuData[this._renderer.uid] = gpuData; + this._managedGeometries.add(geometry); + } + const vaoObjectHash = gpuData.vaoCache; + let vao = vaoObjectHash[signature]; + if (vao) { + vaoObjectHash[program._key] = vao; + return vao; + } + ensureAttributes(geometry, program._attributeData); + const buffers = geometry.buffers; + vao = gl.createVertexArray(); + gl.bindVertexArray(vao); + for (let i = 0; i < buffers.length; i++) { + const buffer = buffers[i]; + bufferSystem.bind(buffer); + } + this.activateVao(geometry, program); + vaoObjectHash[program._key] = vao; + vaoObjectHash[signature] = vao; + gl.bindVertexArray(null); + return vao; + } + onGeometryUnload(geometry, contextLost = false) { + const gpuData = geometry._gpuData[this._renderer.uid]; + if (!gpuData) return; + const vaoCache = gpuData.vaoCache; + if (!contextLost) { + for (const i in vaoCache) { + if (this._activeVao !== vaoCache[i]) { + this.resetState(); + } + this.gl.deleteVertexArray(vaoCache[i]); + } + } + } + /** + * Dispose all WebGL resources of all managed geometries. + * @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls + */ + destroyAll(contextLost = false) { + this._managedGeometries.removeAll(contextLost); + } + /** + * Activate vertex array object. + * @param geometry - Geometry instance. + * @param program - Shader program instance. + */ + activateVao(geometry, program) { + var _a, _b; + const gl = this._renderer.gl; + const bufferSystem = this._renderer.buffer; + const attributes = geometry.attributes; + if (geometry.indexBuffer) { + bufferSystem.bind(geometry.indexBuffer); + } + let lastBuffer = null; + for (const j in attributes) { + const attribute = attributes[j]; + const buffer = attribute.buffer; + const glBuffer = bufferSystem.getGlBuffer(buffer); + const programAttrib = program._attributeData[j]; + if (programAttrib) { + if (lastBuffer !== glBuffer) { + bufferSystem.bind(buffer); + lastBuffer = glBuffer; + } + const location = programAttrib.location; + gl.enableVertexAttribArray(location); + const attributeInfo = getAttributeInfoFromFormat(attribute.format); + const type = getGlTypeFromFormat(attribute.format); + if (((_a = programAttrib.format) == null ? void 0 : _a.substring(1, 4)) === "int") { + gl.vertexAttribIPointer( + location, + attributeInfo.size, + type, + attribute.stride, + attribute.offset + ); + } else { + gl.vertexAttribPointer( + location, + attributeInfo.size, + type, + attributeInfo.normalised, + attribute.stride, + attribute.offset + ); + } + if (attribute.instance) { + if (this.hasInstance) { + const divisor = (_b = attribute.divisor) != null ? _b : 1; + gl.vertexAttribDivisor(location, divisor); + } else { + throw new Error("geometry error, GPU Instancing is not supported on this device"); + } + } + } + } + } + /** + * Draws the currently bound geometry. + * @param topology - The type primitive to render. + * @param size - The number of elements to be rendered. If not specified, all vertices after the + * starting vertex will be drawn. + * @param start - The starting vertex in the geometry to start drawing from. If not specified, + * drawing will start from the first vertex. + * @param instanceCount - The number of instances of the set of elements to execute. If not specified, + * all instances will be drawn. + * @returns This instance of the geometry system. + */ + draw(topology, size, start, instanceCount) { + const { gl } = this._renderer; + const geometry = this._activeGeometry; + const glTopology = topologyToGlMap[topology || geometry.topology]; + instanceCount != null ? instanceCount : instanceCount = geometry.instanceCount; + if (geometry.indexBuffer) { + const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT; + const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT; + if (instanceCount !== 1) { + gl.drawElementsInstanced(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount); + } else { + gl.drawElements(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize); + } + } else if (instanceCount !== 1) { + gl.drawArraysInstanced(glTopology, start || 0, size || geometry.getSize(), instanceCount); + } else { + gl.drawArrays(glTopology, start || 0, size || geometry.getSize()); + } + return this; + } + /** Unbind/reset everything. */ + unbind() { + this.gl.bindVertexArray(null); + this._activeVao = null; + this._activeGeometry = null; + } + destroy() { + this._managedGeometries.destroy(); + this._renderer = null; + this.gl = null; + this._activeVao = null; + this._activeGeometry = null; + } + } + /** @ignore */ + GlGeometrySystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "geometry" + }; + + "use strict"; + var __defProp$n = Object.defineProperty; + var __getOwnPropSymbols$o = Object.getOwnPropertySymbols; + var __hasOwnProp$o = Object.prototype.hasOwnProperty; + var __propIsEnum$o = Object.prototype.propertyIsEnumerable; + var __defNormalProp$n = (obj, key, value) => key in obj ? __defProp$n(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$n = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$o.call(b, prop)) + __defNormalProp$n(a, prop, b[prop]); + if (__getOwnPropSymbols$o) + for (var prop of __getOwnPropSymbols$o(b)) { + if (__propIsEnum$o.call(b, prop)) + __defNormalProp$n(a, prop, b[prop]); + } + return a; + }; + const bigTriangleGeometry = new Geometry({ + attributes: { + aPosition: [ + -1, + -1, + // Bottom left corner + 3, + -1, + // Bottom right corner, extending beyond right edge + -1, + 3 + // Top left corner, extending beyond top edge + ] + } + }); + const _GlBackBufferSystem = class _GlBackBufferSystem { + constructor(renderer) { + /** if true, the back buffer is used */ + this.useBackBuffer = false; + this._useBackBufferThisRender = false; + this._renderer = renderer; + } + init(options = {}) { + const { useBackBuffer, antialias } = __spreadValues$n(__spreadValues$n({}, _GlBackBufferSystem.defaultOptions), options); + this.useBackBuffer = useBackBuffer; + this._antialias = antialias; + if (!this._renderer.context.supports.msaa) { + warn("antialiasing, is not supported on when using the back buffer"); + this._antialias = false; + } + this._state = State.for2d(); + const bigTriangleProgram = new GlProgram({ + vertex: ` + attribute vec2 aPosition; + out vec2 vUv; + + void main() { + gl_Position = vec4(aPosition, 0.0, 1.0); + + vUv = (aPosition + 1.0) / 2.0; + + // flip dem UVs + vUv.y = 1.0 - vUv.y; + }`, + fragment: ` + in vec2 vUv; + out vec4 finalColor; + + uniform sampler2D uTexture; + + void main() { + finalColor = texture(uTexture, vUv); + }`, + name: "big-triangle" + }); + this._bigTriangleShader = new Shader({ + glProgram: bigTriangleProgram, + resources: { + uTexture: Texture.WHITE.source + } + }); + } + /** + * This is called before the RenderTargetSystem is started. This is where + * we replace the target with the back buffer if required. + * @param options - The options for this render. + */ + renderStart(options) { + const renderTarget = this._renderer.renderTarget.getRenderTarget(options.target); + this._useBackBufferThisRender = this.useBackBuffer && !!renderTarget.isRoot; + if (this._useBackBufferThisRender) { + const renderTarget2 = this._renderer.renderTarget.getRenderTarget(options.target); + this._targetTexture = renderTarget2.colorTexture; + options.target = this._getBackBufferTexture(renderTarget2.colorTexture); + } + } + renderEnd() { + this._presentBackBuffer(); + } + _presentBackBuffer() { + const renderer = this._renderer; + renderer.renderTarget.finishRenderPass(); + if (!this._useBackBufferThisRender) return; + renderer.renderTarget.bind(this._targetTexture, false); + this._bigTriangleShader.resources.uTexture = this._backBufferTexture.source; + renderer.encoder.draw({ + geometry: bigTriangleGeometry, + shader: this._bigTriangleShader, + state: this._state + }); + } + _getBackBufferTexture(targetSourceTexture) { + this._backBufferTexture = this._backBufferTexture || new Texture({ + source: new TextureSource({ + width: targetSourceTexture.width, + height: targetSourceTexture.height, + resolution: targetSourceTexture._resolution, + antialias: this._antialias + }) + }); + this._backBufferTexture.source.resize( + targetSourceTexture.width, + targetSourceTexture.height, + targetSourceTexture._resolution + ); + return this._backBufferTexture; + } + /** destroys the back buffer */ + destroy() { + if (this._backBufferTexture) { + this._backBufferTexture.destroy(); + this._backBufferTexture = null; + } + } + }; + /** @ignore */ + _GlBackBufferSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "backBuffer", + priority: 1 + }; + /** default options for the back buffer system */ + _GlBackBufferSystem.defaultOptions = { + /** if true will use the back buffer where required */ + useBackBuffer: false + }; + let GlBackBufferSystem = _GlBackBufferSystem; + + "use strict"; + class GlColorMaskSystem { + constructor(renderer) { + this._colorMaskCache = 15; + this._renderer = renderer; + } + setMask(colorMask) { + if (this._colorMaskCache === colorMask) return; + this._colorMaskCache = colorMask; + this._renderer.gl.colorMask( + !!(colorMask & 8), + !!(colorMask & 4), + !!(colorMask & 2), + !!(colorMask & 1) + ); + } + } + /** @ignore */ + GlColorMaskSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "colorMask" + }; + + "use strict"; + class GlEncoderSystem { + constructor(renderer) { + this.commandFinished = Promise.resolve(); + this._renderer = renderer; + } + setGeometry(geometry, shader) { + this._renderer.geometry.bind(geometry, shader.glProgram); + } + finishRenderPass() { + } + draw(options) { + const renderer = this._renderer; + const { geometry, shader, state, skipSync, topology: type, size, start, instanceCount } = options; + renderer.shader.bind(shader, skipSync); + renderer.geometry.bind(geometry, renderer.shader._activeProgram); + if (state) { + renderer.state.set(state); + } + renderer.geometry.draw(type, size, start, instanceCount != null ? instanceCount : geometry.instanceCount); + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + GlEncoderSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "encoder" + }; + + "use strict"; + class GlLimitsSystem { + constructor(renderer) { + this._renderer = renderer; + } + contextChange() { + const gl = this._renderer.gl; + this.maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); + this.maxBatchableTextures = checkMaxIfStatementsInShader(this.maxTextures, gl); + const isWebGl2 = this._renderer.context.webGLVersion === 2; + this.maxUniformBindings = isWebGl2 ? gl.getParameter(gl.MAX_UNIFORM_BUFFER_BINDINGS) : 0; + } + destroy() { + } + } + /** @ignore */ + GlLimitsSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "limits" + }; + + "use strict"; + class GlRenderTarget { + constructor() { + this.width = -1; + this.height = -1; + this.msaa = false; + /** + * Tracks which mip level is currently attached to this render target's framebuffer. + * This lets us skip redundant framebufferTexture2D calls on the common path. + * @internal + */ + this._attachedMipLevel = 0; + /** + * Tracks which array layer (or cube face index) is currently attached to this render target's framebuffer. + * For non-array 2D textures this will always be 0. + * @internal + */ + this._attachedLayer = 0; + this.msaaRenderBuffer = []; + } + } + + "use strict"; + const GpuStencilModesToPixi = []; + GpuStencilModesToPixi[STENCIL_MODES.NONE] = void 0; + GpuStencilModesToPixi[STENCIL_MODES.DISABLED] = { + stencilWriteMask: 0, + stencilReadMask: 0 + }; + GpuStencilModesToPixi[STENCIL_MODES.RENDERING_MASK_ADD] = { + stencilFront: { + compare: "equal", + passOp: "increment-clamp" + }, + stencilBack: { + compare: "equal", + passOp: "increment-clamp" + } + }; + GpuStencilModesToPixi[STENCIL_MODES.RENDERING_MASK_REMOVE] = { + stencilFront: { + compare: "equal", + passOp: "decrement-clamp" + }, + stencilBack: { + compare: "equal", + passOp: "decrement-clamp" + } + }; + GpuStencilModesToPixi[STENCIL_MODES.MASK_ACTIVE] = { + stencilWriteMask: 0, + stencilFront: { + compare: "equal", + passOp: "keep" + }, + stencilBack: { + compare: "equal", + passOp: "keep" + } + }; + GpuStencilModesToPixi[STENCIL_MODES.INVERSE_MASK_ACTIVE] = { + stencilWriteMask: 0, + stencilFront: { + compare: "not-equal", + passOp: "keep" + }, + stencilBack: { + compare: "not-equal", + passOp: "keep" + } + }; + + "use strict"; + class GlStencilSystem { + constructor(renderer) { + this._stencilCache = { + enabled: false, + stencilReference: 0, + stencilMode: STENCIL_MODES.NONE + }; + this._renderTargetStencilState = /* @__PURE__ */ Object.create(null); + renderer.renderTarget.onRenderTargetChange.add(this); + } + contextChange(gl) { + this._gl = gl; + this._comparisonFuncMapping = { + always: gl.ALWAYS, + never: gl.NEVER, + equal: gl.EQUAL, + "not-equal": gl.NOTEQUAL, + less: gl.LESS, + "less-equal": gl.LEQUAL, + greater: gl.GREATER, + "greater-equal": gl.GEQUAL + }; + this._stencilOpsMapping = { + keep: gl.KEEP, + zero: gl.ZERO, + replace: gl.REPLACE, + invert: gl.INVERT, + "increment-clamp": gl.INCR, + "decrement-clamp": gl.DECR, + "increment-wrap": gl.INCR_WRAP, + "decrement-wrap": gl.DECR_WRAP + }; + this.resetState(); + } + onRenderTargetChange(renderTarget) { + if (this._activeRenderTarget === renderTarget) return; + this._activeRenderTarget = renderTarget; + let stencilState = this._renderTargetStencilState[renderTarget.uid]; + if (!stencilState) { + stencilState = this._renderTargetStencilState[renderTarget.uid] = { + stencilMode: STENCIL_MODES.DISABLED, + stencilReference: 0 + }; + } + this.setStencilMode(stencilState.stencilMode, stencilState.stencilReference); + } + resetState() { + this._stencilCache.enabled = false; + this._stencilCache.stencilMode = STENCIL_MODES.NONE; + this._stencilCache.stencilReference = 0; + } + setStencilMode(stencilMode, stencilReference) { + const stencilState = this._renderTargetStencilState[this._activeRenderTarget.uid]; + const gl = this._gl; + const mode = GpuStencilModesToPixi[stencilMode]; + const _stencilCache = this._stencilCache; + stencilState.stencilMode = stencilMode; + stencilState.stencilReference = stencilReference; + if (stencilMode === STENCIL_MODES.DISABLED) { + if (this._stencilCache.enabled) { + this._stencilCache.enabled = false; + gl.disable(gl.STENCIL_TEST); + } + return; + } + if (!this._stencilCache.enabled) { + this._stencilCache.enabled = true; + gl.enable(gl.STENCIL_TEST); + } + if (stencilMode !== _stencilCache.stencilMode || _stencilCache.stencilReference !== stencilReference) { + _stencilCache.stencilMode = stencilMode; + _stencilCache.stencilReference = stencilReference; + gl.stencilFunc(this._comparisonFuncMapping[mode.stencilBack.compare], stencilReference, 255); + gl.stencilOp(gl.KEEP, gl.KEEP, this._stencilOpsMapping[mode.stencilBack.passOp]); + } + } + } + /** @ignore */ + GlStencilSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "stencil" + }; + + "use strict"; + class UboSystem { + constructor(adaptor) { + /** Cache of uniform buffer layouts and sync functions, so we don't have to re-create them */ + this._syncFunctionHash = /* @__PURE__ */ Object.create(null); + this._adaptor = adaptor; + this._systemCheck(); + } + /** + * Overridable function by `pixi.js/unsafe-eval` to silence + * throwing an error if platform doesn't support unsafe-evals. + * @private + */ + _systemCheck() { + if (!unsafeEvalSupported()) { + throw new Error("Current environment does not allow unsafe-eval, please use pixi.js/unsafe-eval module to enable support."); + } + } + ensureUniformGroup(uniformGroup) { + const uniformData = this.getUniformGroupData(uniformGroup); + uniformGroup.buffer || (uniformGroup.buffer = new Buffer({ + data: new Float32Array(uniformData.layout.size / 4), + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST + })); + } + getUniformGroupData(uniformGroup) { + return this._syncFunctionHash[uniformGroup._signature] || this._initUniformGroup(uniformGroup); + } + _initUniformGroup(uniformGroup) { + const uniformGroupSignature = uniformGroup._signature; + let uniformData = this._syncFunctionHash[uniformGroupSignature]; + if (!uniformData) { + const elements = Object.keys(uniformGroup.uniformStructures).map((i) => uniformGroup.uniformStructures[i]); + const layout = this._adaptor.createUboElements(elements); + const syncFunction = this._generateUboSync(layout.uboElements); + uniformData = this._syncFunctionHash[uniformGroupSignature] = { + layout, + syncFunction + }; + } + return this._syncFunctionHash[uniformGroupSignature]; + } + _generateUboSync(uboElements) { + return this._adaptor.generateUboSync(uboElements); + } + syncUniformGroup(uniformGroup, data, offset) { + const uniformGroupData = this.getUniformGroupData(uniformGroup); + uniformGroup.buffer || (uniformGroup.buffer = new Buffer({ + data: new Float32Array(uniformGroupData.layout.size / 4), + usage: BufferUsage.UNIFORM | BufferUsage.COPY_DST + })); + let dataInt32 = null; + if (!data) { + data = uniformGroup.buffer.data; + dataInt32 = uniformGroup.buffer.dataInt32; + } + offset || (offset = 0); + uniformGroupData.syncFunction(uniformGroup.uniforms, data, dataInt32, offset); + return true; + } + updateUniformGroup(uniformGroup) { + if (uniformGroup.isStatic && !uniformGroup._dirtyId) return false; + uniformGroup._dirtyId = 0; + const synced = this.syncUniformGroup(uniformGroup); + uniformGroup.buffer.update(); + return synced; + } + destroy() { + this._syncFunctionHash = null; + } + } + + "use strict"; + const WGSL_TO_STD40_SIZE = { + f32: 4, + i32: 4, + "vec2": 8, + "vec3": 12, + "vec4": 16, + "vec2": 8, + "vec3": 12, + "vec4": 16, + "mat2x2": 16 * 2, + "mat3x3": 16 * 3, + "mat4x4": 16 * 4 + // TODO - not essential for now but support these in the future + // int: 4, + // ivec2: 8, + // ivec3: 12, + // ivec4: 16, + // uint: 4, + // uvec2: 8, + // uvec3: 12, + // uvec4: 16, + // bool: 4, + // bvec2: 8, + // bvec3: 12, + // bvec4: 16, + // mat2: 16 * 2, + // mat3: 16 * 3, + // mat4: 16 * 4, + }; + function createUboElementsSTD40(uniformData) { + const uboElements = uniformData.map((data) => ({ + data, + offset: 0, + size: 0 + })); + const chunkSize = 16; + let size = 0; + let offset = 0; + for (let i = 0; i < uboElements.length; i++) { + const uboElement = uboElements[i]; + size = WGSL_TO_STD40_SIZE[uboElement.data.type]; + if (!size) { + throw new Error(`Unknown type ${uboElement.data.type}`); + } + if (uboElement.data.size > 1) { + size = Math.max(size, chunkSize) * uboElement.data.size; + } + const boundary = size === 12 ? 16 : size; + uboElement.size = size; + const curOffset = offset % chunkSize; + if (curOffset > 0 && chunkSize - curOffset < boundary) { + offset += (chunkSize - curOffset) % 16; + } else { + offset += (size - curOffset % size) % size; + } + uboElement.offset = offset; + offset += size; + } + offset = Math.ceil(offset / 16) * 16; + return { uboElements, size: offset }; + } + + "use strict"; + const uniformParsers = [ + // uploading pixi matrix object to mat3 + { + type: "mat3x3", + test: (data) => { + const value = data.value; + return value.a !== void 0; + }, + ubo: ` + var matrix = uv[name].toArray(true); + data[offset] = matrix[0]; + data[offset + 1] = matrix[1]; + data[offset + 2] = matrix[2]; + data[offset + 4] = matrix[3]; + data[offset + 5] = matrix[4]; + data[offset + 6] = matrix[5]; + data[offset + 8] = matrix[6]; + data[offset + 9] = matrix[7]; + data[offset + 10] = matrix[8]; + `, + uniform: ` + gl.uniformMatrix3fv(ud[name].location, false, uv[name].toArray(true)); + ` + }, + // uploading a pixi rectangle as a vec4 + { + type: "vec4", + test: (data) => data.type === "vec4" && data.size === 1 && data.value.width !== void 0, + ubo: ` + v = uv[name]; + data[offset] = v.x; + data[offset + 1] = v.y; + data[offset + 2] = v.width; + data[offset + 3] = v.height; + `, + uniform: ` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height) { + cv[0] = v.x; + cv[1] = v.y; + cv[2] = v.width; + cv[3] = v.height; + gl.uniform4f(ud[name].location, v.x, v.y, v.width, v.height); + } + ` + }, + // uploading a pixi point as a vec2 + { + type: "vec2", + test: (data) => data.type === "vec2" && data.size === 1 && data.value.x !== void 0, + ubo: ` + v = uv[name]; + data[offset] = v.x; + data[offset + 1] = v.y; + `, + uniform: ` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.x || cv[1] !== v.y) { + cv[0] = v.x; + cv[1] = v.y; + gl.uniform2f(ud[name].location, v.x, v.y); + } + ` + }, + // uploading a pixi color as a vec4 + { + type: "vec4", + test: (data) => data.type === "vec4" && data.size === 1 && data.value.red !== void 0, + ubo: ` + v = uv[name]; + data[offset] = v.red; + data[offset + 1] = v.green; + data[offset + 2] = v.blue; + data[offset + 3] = v.alpha; + `, + uniform: ` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue || cv[3] !== v.alpha) { + cv[0] = v.red; + cv[1] = v.green; + cv[2] = v.blue; + cv[3] = v.alpha; + gl.uniform4f(ud[name].location, v.red, v.green, v.blue, v.alpha); + } + ` + }, + // uploading a pixi color as a vec3 + { + type: "vec3", + test: (data) => data.type === "vec3" && data.size === 1 && data.value.red !== void 0, + ubo: ` + v = uv[name]; + data[offset] = v.red; + data[offset + 1] = v.green; + data[offset + 2] = v.blue; + `, + uniform: ` + cv = ud[name].value; + v = uv[name]; + if (cv[0] !== v.red || cv[1] !== v.green || cv[2] !== v.blue) { + cv[0] = v.red; + cv[1] = v.green; + cv[2] = v.blue; + gl.uniform3f(ud[name].location, v.red, v.green, v.blue); + } + ` + } + ]; + + "use strict"; + function createUboSyncFunction(uboElements, parserCode, arrayGenerationFunction, singleSettersMap) { + const funcFragments = [` + var v = null; + var v2 = null; + var t = 0; + var index = 0; + var name = null; + var arrayOffset = null; + `]; + let prev = 0; + for (let i = 0; i < uboElements.length; i++) { + const uboElement = uboElements[i]; + const name = uboElement.data.name; + let parsed = false; + let offset = 0; + for (let j = 0; j < uniformParsers.length; j++) { + const uniformParser = uniformParsers[j]; + if (uniformParser.test(uboElement.data)) { + offset = uboElement.offset / 4; + funcFragments.push( + `name = "${name}";`, + `offset += ${offset - prev};`, + uniformParsers[j][parserCode] || uniformParsers[j].ubo + ); + parsed = true; + break; + } + } + if (!parsed) { + if (uboElement.data.size > 1) { + offset = uboElement.offset / 4; + funcFragments.push(arrayGenerationFunction(uboElement, offset - prev)); + } else { + const template = singleSettersMap[uboElement.data.type]; + offset = uboElement.offset / 4; + funcFragments.push( + /* wgsl */ + ` + v = uv.${name}; + offset += ${offset - prev}; + ${template}; + ` + ); + } + } + prev = offset; + } + const fragmentSrc = funcFragments.join("\n"); + return new Function( + "uv", + "data", + "dataInt32", + "offset", + fragmentSrc + ); + } + + "use strict"; + var __defProp$m = Object.defineProperty; + var __defProps$e = Object.defineProperties; + var __getOwnPropDescs$e = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$n = Object.getOwnPropertySymbols; + var __hasOwnProp$n = Object.prototype.hasOwnProperty; + var __propIsEnum$n = Object.prototype.propertyIsEnumerable; + var __defNormalProp$m = (obj, key, value) => key in obj ? __defProp$m(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$m = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$n.call(b, prop)) + __defNormalProp$m(a, prop, b[prop]); + if (__getOwnPropSymbols$n) + for (var prop of __getOwnPropSymbols$n(b)) { + if (__propIsEnum$n.call(b, prop)) + __defNormalProp$m(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$e = (a, b) => __defProps$e(a, __getOwnPropDescs$e(b)); + function loopMatrix(col, row) { + const total = col * row; + return ` + for (let i = 0; i < ${total}; i++) { + data[offset + (((i / ${col})|0) * 4) + (i % ${col})] = v[i]; + } + `; + } + const uboSyncFunctionsSTD40 = { + f32: ` + data[offset] = v;`, + i32: ` + dataInt32[offset] = v;`, + "vec2": ` + data[offset] = v[0]; + data[offset + 1] = v[1];`, + "vec3": ` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2];`, + "vec4": ` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 3] = v[3];`, + "vec2": ` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1];`, + "vec3": ` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1]; + dataInt32[offset + 2] = v[2];`, + "vec4": ` + dataInt32[offset] = v[0]; + dataInt32[offset + 1] = v[1]; + dataInt32[offset + 2] = v[2]; + dataInt32[offset + 3] = v[3];`, + "mat2x2": ` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 4] = v[2]; + data[offset + 5] = v[3];`, + "mat3x3": ` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 4] = v[3]; + data[offset + 5] = v[4]; + data[offset + 6] = v[5]; + data[offset + 8] = v[6]; + data[offset + 9] = v[7]; + data[offset + 10] = v[8];`, + "mat4x4": ` + for (let i = 0; i < 16; i++) { + data[offset + i] = v[i]; + }`, + "mat3x2": loopMatrix(3, 2), + "mat4x2": loopMatrix(4, 2), + "mat2x3": loopMatrix(2, 3), + "mat4x3": loopMatrix(4, 3), + "mat2x4": loopMatrix(2, 4), + "mat3x4": loopMatrix(3, 4) + }; + const uboSyncFunctionsWGSL = __spreadProps$e(__spreadValues$m({}, uboSyncFunctionsSTD40), { + "mat2x2": ` + data[offset] = v[0]; + data[offset + 1] = v[1]; + data[offset + 2] = v[2]; + data[offset + 3] = v[3]; + ` + }); + + "use strict"; + function generateArraySyncSTD40(uboElement, offsetToAdd) { + const rowSize = Math.max(WGSL_TO_STD40_SIZE[uboElement.data.type] / 16, 1); + const elementSize = uboElement.data.value.length / uboElement.data.size; + const remainder = (4 - elementSize % 4) % 4; + const data = uboElement.data.type.indexOf("i32") >= 0 ? "dataInt32" : "data"; + return ` + v = uv.${uboElement.data.name}; + offset += ${offsetToAdd}; + + arrayOffset = offset; + + t = 0; + + for(var i=0; i < ${uboElement.data.size * rowSize}; i++) + { + for(var j = 0; j < ${elementSize}; j++) + { + ${data}[arrayOffset++] = v[t++]; + } + ${remainder !== 0 ? `arrayOffset += ${remainder};` : ""} + } + `; + } + + "use strict"; + function createUboSyncFunctionSTD40(uboElements) { + return createUboSyncFunction( + uboElements, + "uboStd40", + generateArraySyncSTD40, + uboSyncFunctionsSTD40 + ); + } + + "use strict"; + class GlUboSystem extends UboSystem { + constructor() { + super({ + createUboElements: createUboElementsSTD40, + generateUboSync: createUboSyncFunctionSTD40 + }); + } + } + /** @ignore */ + GlUboSystem.extension = { + type: [ExtensionType.WebGLSystem], + name: "ubo" + }; + + "use strict"; + class GlRenderTargetAdaptor { + constructor() { + this._clearColorCache = [0, 0, 0, 0]; + this._viewPortCache = new Rectangle(); + } + init(renderer, renderTargetSystem) { + this._renderer = renderer; + this._renderTargetSystem = renderTargetSystem; + renderer.runners.contextChange.add(this); + } + contextChange() { + this._clearColorCache = [0, 0, 0, 0]; + this._viewPortCache = new Rectangle(); + const gl = this._renderer.gl; + this._drawBuffersCache = []; + for (let i = 1; i <= 16; i++) { + this._drawBuffersCache[i] = Array.from({ length: i }, (_, j) => gl.COLOR_ATTACHMENT0 + j); + } + } + copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) { + const renderTargetSystem = this._renderTargetSystem; + const renderer = this._renderer; + const glRenderTarget = renderTargetSystem.getGpuRenderTarget(sourceRenderSurfaceTexture); + const gl = renderer.gl; + this.finishRenderPass(sourceRenderSurfaceTexture); + gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.resolveTargetFramebuffer); + renderer.texture.bind(destinationTexture, 0); + gl.copyTexSubImage2D( + gl.TEXTURE_2D, + 0, + originDest.x, + originDest.y, + originSrc.x, + originSrc.y, + size.width, + size.height + ); + return destinationTexture; + } + startRenderPass(renderTarget, clear = true, clearColor, viewport, mipLevel = 0, layer = 0) { + const renderTargetSystem = this._renderTargetSystem; + const source = renderTarget.colorTexture; + const gpuRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget); + if (layer !== 0 && this._renderer.context.webGLVersion < 2) { + throw new Error("[RenderTargetSystem] Rendering to array layers requires WebGL2."); + } + if (mipLevel > 0) { + if (gpuRenderTarget.msaa) { + throw new Error("[RenderTargetSystem] Rendering to mip levels is not supported with MSAA render targets."); + } + if (this._renderer.context.webGLVersion < 2) { + throw new Error("[RenderTargetSystem] Rendering to mip levels requires WebGL2."); + } + } + let viewPortY = viewport.y; + if (renderTarget.isRoot) { + viewPortY = source.pixelHeight - viewport.height - viewport.y; + } + renderTarget.colorTextures.forEach((texture) => { + this._renderer.texture.unbind(texture); + }); + const gl = this._renderer.gl; + gl.bindFramebuffer(gl.FRAMEBUFFER, gpuRenderTarget.framebuffer); + if (!renderTarget.isRoot && (gpuRenderTarget._attachedMipLevel !== mipLevel || gpuRenderTarget._attachedLayer !== layer)) { + renderTarget.colorTextures.forEach((colorTexture, i) => { + const glSource = this._renderer.texture.getGlSource(colorTexture); + if (glSource.target === gl.TEXTURE_2D) { + if (layer !== 0) { + throw new Error("[RenderTargetSystem] layer must be 0 when rendering to 2D textures in WebGL."); + } + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + glSource.texture, + mipLevel + ); + } else if (glSource.target === gl.TEXTURE_2D_ARRAY) { + if (this._renderer.context.webGLVersion < 2) { + throw new Error("[RenderTargetSystem] Rendering to 2D array textures requires WebGL2."); + } + gl.framebufferTextureLayer( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + glSource.texture, + mipLevel, + layer + ); + } else if (glSource.target === gl.TEXTURE_CUBE_MAP) { + if (layer < 0 || layer > 5) { + throw new Error("[RenderTargetSystem] Cube map layer must be between 0 and 5."); + } + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_POSITIVE_X + layer, + glSource.texture, + mipLevel + ); + } else { + throw new Error("[RenderTargetSystem] Unsupported texture target for render-to-layer in WebGL."); + } + }); + gpuRenderTarget._attachedMipLevel = mipLevel; + gpuRenderTarget._attachedLayer = layer; + } + if (renderTarget.colorTextures.length > 1) { + this._setDrawBuffers(renderTarget, gl); + } + const viewPortCache = this._viewPortCache; + if (viewPortCache.x !== viewport.x || viewPortCache.y !== viewPortY || viewPortCache.width !== viewport.width || viewPortCache.height !== viewport.height) { + viewPortCache.x = viewport.x; + viewPortCache.y = viewPortY; + viewPortCache.width = viewport.width; + viewPortCache.height = viewport.height; + gl.viewport( + viewport.x, + viewPortY, + viewport.width, + viewport.height + ); + } + if (!gpuRenderTarget.depthStencilRenderBuffer && (renderTarget.stencil || renderTarget.depth)) { + this._initStencil(gpuRenderTarget); + } + this.clear(renderTarget, clear, clearColor); + } + finishRenderPass(renderTarget) { + const renderTargetSystem = this._renderTargetSystem; + const glRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget); + if (!glRenderTarget.msaa) return; + const gl = this._renderer.gl; + gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.resolveTargetFramebuffer); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, glRenderTarget.framebuffer); + gl.blitFramebuffer( + 0, + 0, + glRenderTarget.width, + glRenderTarget.height, + 0, + 0, + glRenderTarget.width, + glRenderTarget.height, + gl.COLOR_BUFFER_BIT, + gl.NEAREST + ); + gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.framebuffer); + } + initGpuRenderTarget(renderTarget) { + const renderer = this._renderer; + const gl = renderer.gl; + const glRenderTarget = new GlRenderTarget(); + glRenderTarget._attachedMipLevel = 0; + glRenderTarget._attachedLayer = 0; + const colorTexture = renderTarget.colorTexture; + if (colorTexture instanceof CanvasSource) { + this._renderer.context.ensureCanvasSize(renderTarget.colorTexture.resource); + glRenderTarget.framebuffer = null; + return glRenderTarget; + } + this._initColor(renderTarget, glRenderTarget); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + return glRenderTarget; + } + destroyGpuRenderTarget(gpuRenderTarget) { + const gl = this._renderer.gl; + if (gpuRenderTarget.framebuffer) { + gl.deleteFramebuffer(gpuRenderTarget.framebuffer); + gpuRenderTarget.framebuffer = null; + } + if (gpuRenderTarget.resolveTargetFramebuffer) { + gl.deleteFramebuffer(gpuRenderTarget.resolveTargetFramebuffer); + gpuRenderTarget.resolveTargetFramebuffer = null; + } + if (gpuRenderTarget.depthStencilRenderBuffer) { + gl.deleteRenderbuffer(gpuRenderTarget.depthStencilRenderBuffer); + gpuRenderTarget.depthStencilRenderBuffer = null; + } + gpuRenderTarget.msaaRenderBuffer.forEach((renderBuffer) => { + gl.deleteRenderbuffer(renderBuffer); + }); + gpuRenderTarget.msaaRenderBuffer = null; + } + clear(_renderTarget, clear, clearColor, _viewport, _mipLevel = 0, layer = 0) { + if (!clear) return; + if (layer !== 0) { + throw new Error("[RenderTargetSystem] Clearing array layers is not supported in WebGL renderer."); + } + const renderTargetSystem = this._renderTargetSystem; + if (typeof clear === "boolean") { + clear = clear ? CLEAR.ALL : CLEAR.NONE; + } + const gl = this._renderer.gl; + if (clear & CLEAR.COLOR) { + clearColor != null ? clearColor : clearColor = renderTargetSystem.defaultClearColor; + const clearColorCache = this._clearColorCache; + const clearColorArray = clearColor; + if (clearColorCache[0] !== clearColorArray[0] || clearColorCache[1] !== clearColorArray[1] || clearColorCache[2] !== clearColorArray[2] || clearColorCache[3] !== clearColorArray[3]) { + clearColorCache[0] = clearColorArray[0]; + clearColorCache[1] = clearColorArray[1]; + clearColorCache[2] = clearColorArray[2]; + clearColorCache[3] = clearColorArray[3]; + gl.clearColor(clearColorArray[0], clearColorArray[1], clearColorArray[2], clearColorArray[3]); + } + } + gl.clear(clear); + } + resizeGpuRenderTarget(renderTarget) { + if (renderTarget.isRoot) return; + const renderTargetSystem = this._renderTargetSystem; + const glRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget); + this._resizeColor(renderTarget, glRenderTarget); + if (renderTarget.stencil || renderTarget.depth) { + this._resizeStencil(glRenderTarget); + } + } + _initColor(renderTarget, glRenderTarget) { + const renderer = this._renderer; + const gl = renderer.gl; + const resolveTargetFramebuffer = gl.createFramebuffer(); + glRenderTarget.resolveTargetFramebuffer = resolveTargetFramebuffer; + gl.bindFramebuffer(gl.FRAMEBUFFER, resolveTargetFramebuffer); + glRenderTarget.width = renderTarget.colorTexture.source.pixelWidth; + glRenderTarget.height = renderTarget.colorTexture.source.pixelHeight; + const colorTextures = renderTarget.colorTextures; + colorTextures.forEach((colorTexture, i) => { + const source = colorTexture.source; + if (source.antialias) { + if (renderer.context.supports.msaa) { + glRenderTarget.msaa = true; + } else { + warn("[RenderTexture] Antialiasing on textures is not supported in WebGL1"); + } + } + renderer.texture.bindSource(source, 0); + const glSource = renderer.texture.getGlSource(source); + const glTexture = glSource.texture; + if (glSource.target === gl.TEXTURE_2D) { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + glTexture, + 0 + ); + } else if (glSource.target === gl.TEXTURE_2D_ARRAY) { + if (renderer.context.webGLVersion < 2) { + throw new Error("[RenderTargetSystem] TEXTURE_2D_ARRAY requires WebGL2."); + } + gl.framebufferTextureLayer( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + glTexture, + 0, + 0 + ); + } else if (glSource.target === gl.TEXTURE_CUBE_MAP) { + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + glTexture, + 0 + ); + } else { + throw new Error("[RenderTargetSystem] Unsupported texture target for framebuffer attachment."); + } + }); + if (glRenderTarget.msaa) { + const viewFramebuffer = gl.createFramebuffer(); + glRenderTarget.framebuffer = viewFramebuffer; + gl.bindFramebuffer(gl.FRAMEBUFFER, viewFramebuffer); + renderTarget.colorTextures.forEach((_, i) => { + const msaaRenderBuffer = gl.createRenderbuffer(); + glRenderTarget.msaaRenderBuffer[i] = msaaRenderBuffer; + }); + } else { + glRenderTarget.framebuffer = resolveTargetFramebuffer; + } + this._resizeColor(renderTarget, glRenderTarget); + } + _resizeColor(renderTarget, glRenderTarget) { + const source = renderTarget.colorTexture.source; + glRenderTarget.width = source.pixelWidth; + glRenderTarget.height = source.pixelHeight; + glRenderTarget._attachedMipLevel = 0; + glRenderTarget._attachedLayer = 0; + renderTarget.colorTextures.forEach((colorTexture, i) => { + if (i === 0) return; + colorTexture.source.resize(source.width, source.height, source._resolution); + }); + if (glRenderTarget.msaa) { + const renderer = this._renderer; + const gl = renderer.gl; + const viewFramebuffer = glRenderTarget.framebuffer; + gl.bindFramebuffer(gl.FRAMEBUFFER, viewFramebuffer); + renderTarget.colorTextures.forEach((colorTexture, i) => { + const source2 = colorTexture.source; + renderer.texture.bindSource(source2, 0); + const glSource = renderer.texture.getGlSource(source2); + const glInternalFormat = glSource.internalFormat; + const msaaRenderBuffer = glRenderTarget.msaaRenderBuffer[i]; + gl.bindRenderbuffer( + gl.RENDERBUFFER, + msaaRenderBuffer + ); + gl.renderbufferStorageMultisample( + gl.RENDERBUFFER, + 4, + glInternalFormat, + source2.pixelWidth, + source2.pixelHeight + ); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.RENDERBUFFER, + msaaRenderBuffer + ); + }); + } + } + _initStencil(glRenderTarget) { + if (glRenderTarget.framebuffer === null) return; + const gl = this._renderer.gl; + const depthStencilRenderBuffer = gl.createRenderbuffer(); + glRenderTarget.depthStencilRenderBuffer = depthStencilRenderBuffer; + gl.bindRenderbuffer( + gl.RENDERBUFFER, + depthStencilRenderBuffer + ); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.DEPTH_STENCIL_ATTACHMENT, + gl.RENDERBUFFER, + depthStencilRenderBuffer + ); + this._resizeStencil(glRenderTarget); + } + _resizeStencil(glRenderTarget) { + const gl = this._renderer.gl; + gl.bindRenderbuffer( + gl.RENDERBUFFER, + glRenderTarget.depthStencilRenderBuffer + ); + if (glRenderTarget.msaa) { + gl.renderbufferStorageMultisample( + gl.RENDERBUFFER, + 4, + gl.DEPTH24_STENCIL8, + glRenderTarget.width, + glRenderTarget.height + ); + } else { + gl.renderbufferStorage( + gl.RENDERBUFFER, + this._renderer.context.webGLVersion === 2 ? gl.DEPTH24_STENCIL8 : gl.DEPTH_STENCIL, + glRenderTarget.width, + glRenderTarget.height + ); + } + } + prerender(renderTarget) { + const resource = renderTarget.colorTexture.resource; + if (this._renderer.context.multiView && CanvasSource.test(resource)) { + this._renderer.context.ensureCanvasSize(resource); + } + } + postrender(renderTarget) { + if (!this._renderer.context.multiView) return; + if (CanvasSource.test(renderTarget.colorTexture.resource)) { + const contextCanvas = this._renderer.context.canvas; + const canvasSource = renderTarget.colorTexture; + canvasSource.context2D.drawImage( + contextCanvas, + 0, + canvasSource.pixelHeight - contextCanvas.height + ); + } + } + _setDrawBuffers(renderTarget, gl) { + const count = renderTarget.colorTextures.length; + const bufferArray = this._drawBuffersCache[count]; + if (this._renderer.context.webGLVersion === 1) { + const ext = this._renderer.context.extensions.drawBuffers; + if (!ext) { + warn("[RenderTexture] This WebGL1 context does not support rendering to multiple targets"); + } else { + ext.drawBuffersWEBGL(bufferArray); + } + } else { + gl.drawBuffers(bufferArray); + } + } + } + + "use strict"; + class GlRenderTargetSystem extends RenderTargetSystem { + constructor(renderer) { + super(renderer); + this.adaptor = new GlRenderTargetAdaptor(); + this.adaptor.init(renderer, this); + } + } + /** @ignore */ + GlRenderTargetSystem.extension = { + type: [ExtensionType.WebGLSystem], + name: "renderTarget" + }; + + "use strict"; + + "use strict"; + class BufferResource extends EventEmitter { + /** + * Create a new Buffer Resource. + * @param options - The options for the buffer resource + * @param options.buffer - The underlying buffer that this resource is using + * @param options.offset - The offset of the buffer this resource is using. + * If not provided, then it will use the offset of the buffer. + * @param options.size - The size of the buffer this resource is using. + * If not provided, then it will use the size of the buffer. + */ + constructor({ buffer, offset, size }) { + super(); + /** + * emits when the underlying buffer has changed shape (i.e. resized) + * letting the renderer know that it needs to discard the old buffer on the GPU and create a new one + * @event change + */ + /** a unique id for this uniform group used through the renderer */ + this.uid = uid$1("buffer"); + /** + * a resource type, used to identify how to handle it when its in a bind group / shader resource + * @internal + */ + this._resourceType = "bufferResource"; + /** + * used internally to know if a uniform group was used in the last render pass + * @internal + */ + this._touched = 0; + /** + * the resource id used internally by the renderer to build bind group keys + * @internal + */ + this._resourceId = uid$1("resource"); + /** + * A cheeky hint to the GL renderer to let it know this is a BufferResource + * @internal + */ + this._bufferResource = true; + /** + * Has the Buffer resource been destroyed? + * @readonly + */ + this.destroyed = false; + this.buffer = buffer; + this.offset = offset | 0; + this.size = size; + this.buffer.on("change", this.onBufferChange, this); + } + onBufferChange() { + this._resourceId = uid$1("resource"); + this.emit("change", this); + } + /** + * Destroys this resource. Make sure the underlying buffer is not used anywhere else + * if you want to destroy it as well, or code will explode + * @param destroyBuffer - Should the underlying buffer be destroyed as well? + */ + destroy(destroyBuffer = false) { + this.destroyed = true; + if (destroyBuffer) { + this.buffer.destroy(); + } + this.emit("change", this); + this.buffer = null; + this.removeAllListeners(); + } + } + + "use strict"; + function generateShaderSyncCode(shader, shaderSystem) { + const funcFragments = []; + const headerFragments = [` + var g = s.groups; + var sS = r.shader; + var p = s.glProgram; + var ugS = r.uniformGroup; + var resources; + `]; + let addedTextreSystem = false; + let textureCount = 0; + const programData = shaderSystem._getProgramData(shader.glProgram); + for (const i in shader.groups) { + const group = shader.groups[i]; + funcFragments.push(` + resources = g[${i}].resources; + `); + for (const j in group.resources) { + const resource = group.resources[j]; + if (resource instanceof UniformGroup) { + if (resource.ubo) { + const resName = shader._uniformBindMap[i][Number(j)]; + funcFragments.push(` + sS.bindUniformBlock( + resources[${j}], + '${resName}', + ${shader.glProgram._uniformBlockData[resName].index} + ); + `); + } else { + funcFragments.push(` + ugS.updateUniformGroup(resources[${j}], p, sD); + `); + } + } else if (resource instanceof BufferResource) { + const resName = shader._uniformBindMap[i][Number(j)]; + funcFragments.push(` + sS.bindUniformBlock( + resources[${j}], + '${resName}', + ${shader.glProgram._uniformBlockData[resName].index} + ); + `); + } else if (resource instanceof TextureSource) { + const uniformName = shader._uniformBindMap[i][j]; + const uniformData = programData.uniformData[uniformName]; + if (uniformData) { + if (!addedTextreSystem) { + addedTextreSystem = true; + headerFragments.push(` + var tS = r.texture; + `); + } + shaderSystem._gl.uniform1i(uniformData.location, textureCount); + funcFragments.push(` + tS.bind(resources[${j}], ${textureCount}); + `); + textureCount++; + } + } + } + } + const functionSource = [...headerFragments, ...funcFragments].join("\n"); + return new Function("r", "s", "sD", functionSource); + } + + "use strict"; + class IGLUniformData { + } + class GlProgramData { + /** + * Makes a new Pixi program. + * @param program - webgl program + * @param uniformData - uniforms + */ + constructor(program, uniformData) { + this.program = program; + this.uniformData = uniformData; + this.uniformGroups = {}; + this.uniformDirtyGroups = {}; + this.uniformBlockBindings = {}; + } + /** Destroys this program. */ + destroy() { + this.uniformData = null; + this.uniformGroups = null; + this.uniformDirtyGroups = null; + this.uniformBlockBindings = null; + this.program = null; + } + } + + "use strict"; + function compileShader(gl, type, src) { + const shader = gl.createShader(type); + gl.shaderSource(shader, src); + gl.compileShader(shader); + return shader; + } + + "use strict"; + function booleanArray(size) { + const array = new Array(size); + for (let i = 0; i < array.length; i++) { + array[i] = false; + } + return array; + } + function defaultValue(type, size) { + switch (type) { + case "float": + return 0; + case "vec2": + return new Float32Array(2 * size); + case "vec3": + return new Float32Array(3 * size); + case "vec4": + return new Float32Array(4 * size); + case "int": + case "uint": + case "sampler2D": + case "sampler2DArray": + return 0; + case "ivec2": + return new Int32Array(2 * size); + case "ivec3": + return new Int32Array(3 * size); + case "ivec4": + return new Int32Array(4 * size); + case "uvec2": + return new Uint32Array(2 * size); + case "uvec3": + return new Uint32Array(3 * size); + case "uvec4": + return new Uint32Array(4 * size); + case "bool": + return false; + case "bvec2": + return booleanArray(2 * size); + case "bvec3": + return booleanArray(3 * size); + case "bvec4": + return booleanArray(4 * size); + case "mat2": + return new Float32Array([ + 1, + 0, + 0, + 1 + ]); + case "mat3": + return new Float32Array([ + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1 + ]); + case "mat4": + return new Float32Array([ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ]); + } + return null; + } + + "use strict"; + let GL_TABLE = null; + const GL_TO_GLSL_TYPES = { + FLOAT: "float", + FLOAT_VEC2: "vec2", + FLOAT_VEC3: "vec3", + FLOAT_VEC4: "vec4", + INT: "int", + INT_VEC2: "ivec2", + INT_VEC3: "ivec3", + INT_VEC4: "ivec4", + UNSIGNED_INT: "uint", + UNSIGNED_INT_VEC2: "uvec2", + UNSIGNED_INT_VEC3: "uvec3", + UNSIGNED_INT_VEC4: "uvec4", + BOOL: "bool", + BOOL_VEC2: "bvec2", + BOOL_VEC3: "bvec3", + BOOL_VEC4: "bvec4", + FLOAT_MAT2: "mat2", + FLOAT_MAT3: "mat3", + FLOAT_MAT4: "mat4", + SAMPLER_2D: "sampler2D", + INT_SAMPLER_2D: "sampler2D", + UNSIGNED_INT_SAMPLER_2D: "sampler2D", + SAMPLER_CUBE: "samplerCube", + INT_SAMPLER_CUBE: "samplerCube", + UNSIGNED_INT_SAMPLER_CUBE: "samplerCube", + SAMPLER_2D_ARRAY: "sampler2DArray", + INT_SAMPLER_2D_ARRAY: "sampler2DArray", + UNSIGNED_INT_SAMPLER_2D_ARRAY: "sampler2DArray" + }; + const GLSL_TO_VERTEX_TYPES = { + float: "float32", + vec2: "float32x2", + vec3: "float32x3", + vec4: "float32x4", + int: "sint32", + ivec2: "sint32x2", + ivec3: "sint32x3", + ivec4: "sint32x4", + uint: "uint32", + uvec2: "uint32x2", + uvec3: "uint32x3", + uvec4: "uint32x4", + bool: "uint32", + bvec2: "uint32x2", + bvec3: "uint32x3", + bvec4: "uint32x4" + }; + function mapType(gl, type) { + if (!GL_TABLE) { + const typeNames = Object.keys(GL_TO_GLSL_TYPES); + GL_TABLE = {}; + for (let i = 0; i < typeNames.length; ++i) { + const tn = typeNames[i]; + GL_TABLE[gl[tn]] = GL_TO_GLSL_TYPES[tn]; + } + } + return GL_TABLE[type]; + } + function mapGlToVertexFormat(gl, type) { + const typeValue = mapType(gl, type); + return GLSL_TO_VERTEX_TYPES[typeValue] || "float32"; + } + + "use strict"; + function extractAttributesFromGlProgram(program, gl, sortAttributes = false) { + const attributes = {}; + const totalAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + for (let i = 0; i < totalAttributes; i++) { + const attribData = gl.getActiveAttrib(program, i); + if (attribData.name.startsWith("gl_")) { + continue; + } + const format = mapGlToVertexFormat(gl, attribData.type); + attributes[attribData.name] = { + location: 0, + // set further down.. + format, + stride: getAttributeInfoFromFormat(format).stride, + offset: 0, + instance: false, + start: 0 + }; + } + const keys = Object.keys(attributes); + if (sortAttributes) { + keys.sort((a, b) => a > b ? 1 : -1); + for (let i = 0; i < keys.length; i++) { + attributes[keys[i]].location = i; + gl.bindAttribLocation(program, i, keys[i]); + } + gl.linkProgram(program); + } else { + for (let i = 0; i < keys.length; i++) { + attributes[keys[i]].location = gl.getAttribLocation(program, keys[i]); + } + } + return attributes; + } + + "use strict"; + function getUboData(program, gl) { + if (!gl.ACTIVE_UNIFORM_BLOCKS) return {}; + const uniformBlocks = {}; + const totalUniformsBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS); + for (let i = 0; i < totalUniformsBlocks; i++) { + const name = gl.getActiveUniformBlockName(program, i); + const uniformBlockIndex = gl.getUniformBlockIndex(program, name); + const size = gl.getActiveUniformBlockParameter(program, i, gl.UNIFORM_BLOCK_DATA_SIZE); + uniformBlocks[name] = { + name, + index: uniformBlockIndex, + size + }; + } + return uniformBlocks; + } + + "use strict"; + function getUniformData(program, gl) { + const uniforms = {}; + const totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + for (let i = 0; i < totalUniforms; i++) { + const uniformData = gl.getActiveUniform(program, i); + const name = uniformData.name.replace(/\[.*?\]$/, ""); + const isArray = !!uniformData.name.match(/\[.*?\]$/); + const type = mapType(gl, uniformData.type); + uniforms[name] = { + name, + index: i, + type, + size: uniformData.size, + isArray, + value: defaultValue(type, uniformData.size) + }; + } + return uniforms; + } + + "use strict"; + function logPrettyShaderError(gl, shader) { + const shaderSrc = gl.getShaderSource(shader).split("\n").map((line, index) => `${index}: ${line}`); + const shaderLog = gl.getShaderInfoLog(shader); + const splitShader = shaderLog.split("\n"); + const dedupe = {}; + const lineNumbers = splitShader.map((line) => parseFloat(line.replace(/^ERROR\: 0\:([\d]+)\:.*$/, "$1"))).filter((n) => { + if (n && !dedupe[n]) { + dedupe[n] = true; + return true; + } + return false; + }); + const logArgs = [""]; + lineNumbers.forEach((number) => { + shaderSrc[number - 1] = `%c${shaderSrc[number - 1]}%c`; + logArgs.push("background: #FF0000; color:#FFFFFF; font-size: 10px", "font-size: 10px"); + }); + const fragmentSourceToLog = shaderSrc.join("\n"); + logArgs[0] = fragmentSourceToLog; + console.error(shaderLog); + console.groupCollapsed("click to view full shader code"); + console.warn(...logArgs); + console.groupEnd(); + } + function logProgramError(gl, program, vertexShader, fragmentShader) { + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { + logPrettyShaderError(gl, vertexShader); + } + if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { + logPrettyShaderError(gl, fragmentShader); + } + console.error("PixiJS Error: Could not initialize shader."); + if (gl.getProgramInfoLog(program) !== "") { + console.warn("PixiJS Warning: gl.getProgramInfoLog()", gl.getProgramInfoLog(program)); + } + } + } + + "use strict"; + function generateProgram(gl, program) { + const glVertShader = compileShader(gl, gl.VERTEX_SHADER, program.vertex); + const glFragShader = compileShader(gl, gl.FRAGMENT_SHADER, program.fragment); + const webGLProgram = gl.createProgram(); + gl.attachShader(webGLProgram, glVertShader); + gl.attachShader(webGLProgram, glFragShader); + const transformFeedbackVaryings = program.transformFeedbackVaryings; + if (transformFeedbackVaryings) { + if (typeof gl.transformFeedbackVaryings !== "function") { + warn(`TransformFeedback is not supported but TransformFeedbackVaryings are given.`); + } else { + gl.transformFeedbackVaryings( + webGLProgram, + transformFeedbackVaryings.names, + transformFeedbackVaryings.bufferMode === "separate" ? gl.SEPARATE_ATTRIBS : gl.INTERLEAVED_ATTRIBS + ); + } + } + gl.linkProgram(webGLProgram); + if (!gl.getProgramParameter(webGLProgram, gl.LINK_STATUS)) { + logProgramError(gl, webGLProgram, glVertShader, glFragShader); + } + program._attributeData = extractAttributesFromGlProgram( + webGLProgram, + gl, + !/^[ \t]*#[ \t]*version[ \t]+300[ \t]+es[ \t]*$/m.test(program.vertex) + ); + program._uniformData = getUniformData(webGLProgram, gl); + program._uniformBlockData = getUboData(webGLProgram, gl); + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + const uniformData = {}; + for (const i in program._uniformData) { + const data = program._uniformData[i]; + uniformData[i] = { + location: gl.getUniformLocation(webGLProgram, i), + value: defaultValue(data.type, data.size) + }; + } + const glProgram = new GlProgramData(webGLProgram, uniformData); + return glProgram; + } + + "use strict"; + const defaultSyncData = { + textureCount: 0, + blockIndex: 0 + }; + class GlShaderSystem { + constructor(renderer) { + /** @internal */ + this._activeProgram = null; + this._programDataHash = /* @__PURE__ */ Object.create(null); + this._shaderSyncFunctions = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + } + contextChange(gl) { + this._gl = gl; + this._programDataHash = /* @__PURE__ */ Object.create(null); + this._shaderSyncFunctions = /* @__PURE__ */ Object.create(null); + this._activeProgram = null; + } + /** + * Changes the current shader to the one given in parameter. + * @param shader - the new shader + * @param skipSync - false if the shader should automatically sync its uniforms. + * @returns the glProgram that belongs to the shader. + */ + bind(shader, skipSync) { + this._setProgram(shader.glProgram); + if (skipSync) return; + defaultSyncData.textureCount = 0; + defaultSyncData.blockIndex = 0; + let syncFunction = this._shaderSyncFunctions[shader.glProgram._key]; + if (!syncFunction) { + syncFunction = this._shaderSyncFunctions[shader.glProgram._key] = this._generateShaderSync(shader, this); + } + this._renderer.buffer.nextBindBase(!!shader.glProgram.transformFeedbackVaryings); + syncFunction(this._renderer, shader, defaultSyncData); + } + /** + * Updates the uniform group. + * @param uniformGroup - the uniform group to update + */ + updateUniformGroup(uniformGroup) { + this._renderer.uniformGroup.updateUniformGroup(uniformGroup, this._activeProgram, defaultSyncData); + } + /** + * Binds a uniform block to the shader. + * @param uniformGroup - the uniform group to bind + * @param name - the name of the uniform block + * @param index - the index of the uniform block + */ + bindUniformBlock(uniformGroup, name, index = 0) { + const bufferSystem = this._renderer.buffer; + const programData = this._getProgramData(this._activeProgram); + const isBufferResource = uniformGroup._bufferResource; + if (!isBufferResource) { + this._renderer.ubo.updateUniformGroup(uniformGroup); + } + const buffer = uniformGroup.buffer; + const glBuffer = bufferSystem.updateBuffer(buffer); + const boundLocation = bufferSystem.freeLocationForBufferBase(glBuffer); + if (isBufferResource) { + const { offset, size } = uniformGroup; + if (offset === 0 && size === buffer.data.byteLength) { + bufferSystem.bindBufferBase(glBuffer, boundLocation); + } else { + bufferSystem.bindBufferRange(glBuffer, boundLocation, offset); + } + } else if (bufferSystem.getLastBindBaseLocation(glBuffer) !== boundLocation) { + bufferSystem.bindBufferBase(glBuffer, boundLocation); + } + const uniformBlockIndex = this._activeProgram._uniformBlockData[name].index; + if (programData.uniformBlockBindings[index] === boundLocation) return; + programData.uniformBlockBindings[index] = boundLocation; + this._renderer.gl.uniformBlockBinding(programData.program, uniformBlockIndex, boundLocation); + } + _setProgram(program) { + if (this._activeProgram === program) return; + this._activeProgram = program; + const programData = this._getProgramData(program); + this._gl.useProgram(programData.program); + } + /** + * @param program - the program to get the data for + * @internal + */ + _getProgramData(program) { + return this._programDataHash[program._key] || this._createProgramData(program); + } + _createProgramData(program) { + const key = program._key; + this._programDataHash[key] = generateProgram(this._gl, program); + return this._programDataHash[key]; + } + destroy() { + for (const key of Object.keys(this._programDataHash)) { + this._programDataHash[key].destroy(); + } + this._programDataHash = null; + this._shaderSyncFunctions = null; + this._activeProgram = null; + this._renderer = null; + this._gl = null; + } + /** + * Creates a function that can be executed that will sync the shader as efficiently as possible. + * Overridden by the unsafe eval package if you don't want eval used in your project. + * @param shader - the shader to generate the sync function for + * @param shaderSystem - the shader system to use + * @returns - the generated sync function + * @ignore + */ + _generateShaderSync(shader, shaderSystem) { + return generateShaderSyncCode(shader, shaderSystem); + } + resetState() { + this._activeProgram = null; + } + } + /** @ignore */ + GlShaderSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "shader" + }; + + "use strict"; + const UNIFORM_TO_SINGLE_SETTERS = { + f32: `if (cv !== v) { + cu.value = v; + gl.uniform1f(location, v); + }`, + "vec2": `if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2f(location, v[0], v[1]); + }`, + "vec3": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3f(location, v[0], v[1], v[2]); + }`, + "vec4": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4f(location, v[0], v[1], v[2], v[3]); + }`, + i32: `if (cv !== v) { + cu.value = v; + gl.uniform1i(location, v); + }`, + "vec2": `if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2i(location, v[0], v[1]); + }`, + "vec3": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3i(location, v[0], v[1], v[2]); + }`, + "vec4": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4i(location, v[0], v[1], v[2], v[3]); + }`, + u32: `if (cv !== v) { + cu.value = v; + gl.uniform1ui(location, v); + }`, + "vec2": `if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2ui(location, v[0], v[1]); + }`, + "vec3": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3ui(location, v[0], v[1], v[2]); + }`, + "vec4": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4ui(location, v[0], v[1], v[2], v[3]); + }`, + bool: `if (cv !== v) { + cu.value = v; + gl.uniform1i(location, v); + }`, + "vec2": `if (cv[0] !== v[0] || cv[1] !== v[1]) { + cv[0] = v[0]; + cv[1] = v[1]; + gl.uniform2i(location, v[0], v[1]); + }`, + "vec3": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + gl.uniform3i(location, v[0], v[1], v[2]); + }`, + "vec4": `if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3]) { + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = v[3]; + gl.uniform4i(location, v[0], v[1], v[2], v[3]); + }`, + "mat2x2": `gl.uniformMatrix2fv(location, false, v);`, + "mat3x3": `gl.uniformMatrix3fv(location, false, v);`, + "mat4x4": `gl.uniformMatrix4fv(location, false, v);` + }; + const UNIFORM_TO_ARRAY_SETTERS = { + f32: `gl.uniform1fv(location, v);`, + "vec2": `gl.uniform2fv(location, v);`, + "vec3": `gl.uniform3fv(location, v);`, + "vec4": `gl.uniform4fv(location, v);`, + "mat2x2": `gl.uniformMatrix2fv(location, false, v);`, + "mat3x3": `gl.uniformMatrix3fv(location, false, v);`, + "mat4x4": `gl.uniformMatrix4fv(location, false, v);`, + i32: `gl.uniform1iv(location, v);`, + "vec2": `gl.uniform2iv(location, v);`, + "vec3": `gl.uniform3iv(location, v);`, + "vec4": `gl.uniform4iv(location, v);`, + u32: `gl.uniform1iv(location, v);`, + "vec2": `gl.uniform2iv(location, v);`, + "vec3": `gl.uniform3iv(location, v);`, + "vec4": `gl.uniform4iv(location, v);`, + bool: `gl.uniform1iv(location, v);`, + "vec2": `gl.uniform2iv(location, v);`, + "vec3": `gl.uniform3iv(location, v);`, + "vec4": `gl.uniform4iv(location, v);` + }; + + "use strict"; + function generateUniformsSync(group, uniformData) { + const funcFragments = [` + var v = null; + var cv = null; + var cu = null; + var t = 0; + var gl = renderer.gl; + var name = null; + `]; + for (const i in group.uniforms) { + if (!uniformData[i]) { + if (group.uniforms[i] instanceof UniformGroup) { + if (group.uniforms[i].ubo) { + funcFragments.push(` + renderer.shader.bindUniformBlock(uv.${i}, "${i}"); + `); + } else { + funcFragments.push(` + renderer.shader.updateUniformGroup(uv.${i}); + `); + } + } else if (group.uniforms[i] instanceof BufferResource) { + funcFragments.push(` + renderer.shader.bindBufferResource(uv.${i}, "${i}"); + `); + } + continue; + } + const uniform = group.uniformStructures[i]; + let parsed = false; + for (let j = 0; j < uniformParsers.length; j++) { + const parser = uniformParsers[j]; + if (uniform.type === parser.type && parser.test(uniform)) { + funcFragments.push(`name = "${i}";`, uniformParsers[j].uniform); + parsed = true; + break; + } + } + if (!parsed) { + const templateType = uniform.size === 1 ? UNIFORM_TO_SINGLE_SETTERS : UNIFORM_TO_ARRAY_SETTERS; + const template = templateType[uniform.type].replace("location", `ud["${i}"].location`); + funcFragments.push(` + cu = ud["${i}"]; + cv = cu.value; + v = uv["${i}"]; + ${template};`); + } + } + return new Function("ud", "uv", "renderer", "syncData", funcFragments.join("\n")); + } + + "use strict"; + class GlUniformGroupSystem { + /** @param renderer - The renderer this System works for. */ + constructor(renderer) { + /** Cache to holds the generated functions. Stored against UniformObjects unique signature. */ + this._cache = {}; + this._uniformGroupSyncHash = {}; + this._renderer = renderer; + this.gl = null; + this._cache = {}; + } + contextChange(gl) { + this.gl = gl; + } + /** + * Uploads the uniforms values to the currently bound shader. + * @param group - the uniforms values that be applied to the current shader + * @param program + * @param syncData + * @param syncData.textureCount + */ + updateUniformGroup(group, program, syncData) { + const programData = this._renderer.shader._getProgramData(program); + if (!group.isStatic || group._dirtyId !== programData.uniformDirtyGroups[group.uid]) { + programData.uniformDirtyGroups[group.uid] = group._dirtyId; + const syncFunc = this._getUniformSyncFunction(group, program); + syncFunc(programData.uniformData, group.uniforms, this._renderer, syncData); + } + } + /** + * Overridable by the pixi.js/unsafe-eval package to use static syncUniforms instead. + * @param group + * @param program + */ + _getUniformSyncFunction(group, program) { + var _a; + return ((_a = this._uniformGroupSyncHash[group._signature]) == null ? void 0 : _a[program._key]) || this._createUniformSyncFunction(group, program); + } + _createUniformSyncFunction(group, program) { + const uniformGroupSyncHash = this._uniformGroupSyncHash[group._signature] || (this._uniformGroupSyncHash[group._signature] = {}); + const id = this._getSignature(group, program._uniformData, "u"); + if (!this._cache[id]) { + this._cache[id] = this._generateUniformsSync(group, program._uniformData); + } + uniformGroupSyncHash[program._key] = this._cache[id]; + return uniformGroupSyncHash[program._key]; + } + _generateUniformsSync(group, uniformData) { + return generateUniformsSync(group, uniformData); + } + /** + * Takes a uniform group and data and generates a unique signature for them. + * @param group - The uniform group to get signature of + * @param group.uniforms + * @param uniformData - Uniform information generated by the shader + * @param preFix + * @returns Unique signature of the uniform group + */ + _getSignature(group, uniformData, preFix) { + const uniforms = group.uniforms; + const strings = [`${preFix}-`]; + for (const i in uniforms) { + strings.push(i); + if (uniformData[i]) { + strings.push(uniformData[i].type); + } + } + return strings.join("-"); + } + /** Destroys this System and removes all its textures. */ + destroy() { + this._renderer = null; + this._cache = null; + } + } + /** @ignore */ + GlUniformGroupSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "uniformGroup" + }; + + "use strict"; + function migrateFragmentFromV7toV8(fragmentShader) { + fragmentShader = fragmentShader.replaceAll("texture2D", "texture").replaceAll("gl_FragColor", "finalColor").replaceAll("varying", "in"); + fragmentShader = ` + out vec4 finalColor; + ${fragmentShader} + `; + return fragmentShader; + } + + "use strict"; + const GLSL_TO_SIZE = { + float: 1, + vec2: 2, + vec3: 3, + vec4: 4, + int: 1, + ivec2: 2, + ivec3: 3, + ivec4: 4, + uint: 1, + uvec2: 2, + uvec3: 3, + uvec4: 4, + bool: 1, + bvec2: 2, + bvec3: 3, + bvec4: 4, + mat2: 4, + mat3: 9, + mat4: 16, + sampler2D: 1 + }; + function mapSize(type) { + return GLSL_TO_SIZE[type]; + } + + "use strict"; + function mapWebGLBlendModesToPixi(gl) { + const blendMap = {}; + blendMap.normal = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + blendMap.add = [gl.ONE, gl.ONE]; + blendMap.multiply = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + blendMap.screen = [gl.ONE, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + blendMap.none = [0, 0]; + blendMap["normal-npm"] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + blendMap["add-npm"] = [gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE]; + blendMap["screen-npm"] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + blendMap.erase = [gl.ZERO, gl.ONE_MINUS_SRC_ALPHA]; + const isWebGl2 = !(gl instanceof DOMAdapter.get().getWebGLRenderingContext()); + if (isWebGl2) { + blendMap.min = [gl.ONE, gl.ONE, gl.ONE, gl.ONE, gl.MIN, gl.MIN]; + blendMap.max = [gl.ONE, gl.ONE, gl.ONE, gl.ONE, gl.MAX, gl.MAX]; + } else { + const ext = gl.getExtension("EXT_blend_minmax"); + if (ext) { + blendMap.min = [gl.ONE, gl.ONE, gl.ONE, gl.ONE, ext.MIN_EXT, ext.MIN_EXT]; + blendMap.max = [gl.ONE, gl.ONE, gl.ONE, gl.ONE, ext.MAX_EXT, ext.MAX_EXT]; + } + } + return blendMap; + } + + "use strict"; + const BLEND = 0; + const OFFSET = 1; + const CULLING = 2; + const DEPTH_TEST = 3; + const WINDING = 4; + const DEPTH_MASK = 5; + const _GlStateSystem = class _GlStateSystem { + constructor(renderer) { + /** + * Whether to invert the front face when rendering + * This is used for render textures where the Y-coordinate is flipped + * @default false + */ + this._invertFrontFace = false; + this.gl = null; + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = "none"; + this._blendEq = false; + this.map = []; + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + this.map[DEPTH_MASK] = this.setDepthMask; + this.checks = []; + this.defaultState = State.for2d(); + renderer.renderTarget.onRenderTargetChange.add(this); + } + onRenderTargetChange(renderTarget) { + this._invertFrontFace = !renderTarget.isRoot; + if (this._cullFace) { + this.setFrontFace(this._frontFace); + } else { + this._frontFaceDirty = true; + } + } + contextChange(gl) { + this.gl = gl; + this.blendModesMap = mapWebGLBlendModesToPixi(gl); + this.resetState(); + } + /** + * Sets the current state + * @param {*} state - The state to set. + */ + set(state) { + state || (state = this.defaultState); + if (this.stateId !== state.data) { + let diff = this.stateId ^ state.data; + let i = 0; + while (diff) { + if (diff & 1) { + this.map[i].call(this, !!(state.data & 1 << i)); + } + diff >>= 1; + i++; + } + this.stateId = state.data; + } + for (let i = 0; i < this.checks.length; i++) { + this.checks[i](this, state); + } + } + /** + * Sets the state, when previous state is unknown. + * @param {*} state - The state to set + */ + forceState(state) { + state || (state = this.defaultState); + for (let i = 0; i < this.map.length; i++) { + this.map[i].call(this, !!(state.data & 1 << i)); + } + for (let i = 0; i < this.checks.length; i++) { + this.checks[i](this, state); + } + this.stateId = state.data; + } + /** + * Sets whether to enable or disable blending. + * @param value - Turn on or off WebGl blending. + */ + setBlend(value) { + this._updateCheck(_GlStateSystem._checkBlendMode, value); + this.gl[value ? "enable" : "disable"](this.gl.BLEND); + } + /** + * Sets whether to enable or disable polygon offset fill. + * @param value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) { + this._updateCheck(_GlStateSystem._checkPolygonOffset, value); + this.gl[value ? "enable" : "disable"](this.gl.POLYGON_OFFSET_FILL); + } + /** + * Sets whether to enable or disable depth test. + * @param value - Turn on or off webgl depth testing. + */ + setDepthTest(value) { + this.gl[value ? "enable" : "disable"](this.gl.DEPTH_TEST); + } + /** + * Sets whether to enable or disable depth mask. + * @param value - Turn on or off webgl depth mask. + */ + setDepthMask(value) { + this.gl.depthMask(value); + } + /** + * Sets whether to enable or disable cull face. + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) { + this._cullFace = value; + this.gl[value ? "enable" : "disable"](this.gl.CULL_FACE); + if (this._cullFace && this._frontFaceDirty) { + this.setFrontFace(this._frontFace); + } + } + /** + * Sets the gl front face. + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) { + this._frontFace = value; + this._frontFaceDirty = false; + const faceMode = this._invertFrontFace ? !value : value; + if (this._glFrontFace !== faceMode) { + this._glFrontFace = faceMode; + this.gl.frontFace(this.gl[faceMode ? "CW" : "CCW"]); + } + } + /** + * Sets the blend mode. + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) { + if (!this.blendModesMap[value]) { + value = "normal"; + } + if (value === this.blendMode) { + return; + } + this.blendMode = value; + const mode = this.blendModesMap[value]; + const gl = this.gl; + if (mode.length === 2) { + gl.blendFunc(mode[0], mode[1]); + } else { + gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } + if (mode.length === 6) { + this._blendEq = true; + gl.blendEquationSeparate(mode[4], mode[5]); + } else if (this._blendEq) { + this._blendEq = false; + gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD); + } + } + /** + * Sets the polygon offset. + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) { + this.gl.polygonOffset(value, scale); + } + /** Resets all the logic and disables the VAOs. */ + resetState() { + this._glFrontFace = false; + this._frontFace = false; + this._cullFace = false; + this._frontFaceDirty = false; + this._invertFrontFace = false; + this.gl.frontFace(this.gl.CCW); + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + this.forceState(this.defaultState); + this._blendEq = true; + this.blendMode = ""; + this.setBlendMode("normal"); + } + /** + * Checks to see which updates should be checked based on which settings have been activated. + * + * For example, if blend is enabled then we should check the blend modes each time the state is changed + * or if polygon fill is activated then we need to check if the polygon offset changes. + * The idea is that we only check what we have too. + * @param func - the checking function to add or remove + * @param value - should the check function be added or removed. + */ + _updateCheck(func, value) { + const index = this.checks.indexOf(func); + if (value && index === -1) { + this.checks.push(func); + } else if (!value && index !== -1) { + this.checks.splice(index, 1); + } + } + /** + * A private little wrapper function that we call to check the blend mode. + * @param system - the System to perform the state check on + * @param state - the state that the blendMode will pulled from + */ + static _checkBlendMode(system, state) { + system.setBlendMode(state.blendMode); + } + /** + * A private little wrapper function that we call to check the polygon offset. + * @param system - the System to perform the state check on + * @param state - the state that the blendMode will pulled from + */ + static _checkPolygonOffset(system, state) { + system.setPolygonOffset(1, state.polygonOffset); + } + /** @ignore */ + destroy() { + this.gl = null; + this.checks.length = 0; + } + }; + /** @ignore */ + _GlStateSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "state" + }; + let GlStateSystem = _GlStateSystem; + + "use strict"; + class GlTexture { + constructor(texture) { + this.target = GL_TARGETS.TEXTURE_2D; + /** + * Bitmask tracking which array layers / sub-targets have been initialized at mip level 0. + * Used by uploaders that need per-layer allocation semantics (e.g. cube faces). + * @internal + */ + this._layerInitMask = 0; + this.texture = texture; + this.width = -1; + this.height = -1; + this.type = GL_TYPES.UNSIGNED_BYTE; + this.internalFormat = GL_FORMATS.RGBA; + this.format = GL_FORMATS.RGBA; + this.samplerType = 0; + } + destroy() { + } + } + + "use strict"; + const glUploadBufferImageResource = { + id: "buffer", + upload(source, glTexture, gl, _webGLVersion, targetOverride, forceAllocation = false) { + const target = targetOverride || glTexture.target; + if (!forceAllocation && (glTexture.width === source.width && glTexture.height === source.height)) { + gl.texSubImage2D( + target, + 0, + 0, + 0, + source.width, + source.height, + glTexture.format, + glTexture.type, + source.resource + ); + } else { + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + source.width, + source.height, + 0, + glTexture.format, + glTexture.type, + source.resource + ); + } + glTexture.width = source.width; + glTexture.height = source.height; + } + }; + + "use strict"; + const compressedFormatMap = { + "bc1-rgba-unorm": true, + "bc1-rgba-unorm-srgb": true, + "bc2-rgba-unorm": true, + "bc2-rgba-unorm-srgb": true, + "bc3-rgba-unorm": true, + "bc3-rgba-unorm-srgb": true, + "bc4-r-unorm": true, + "bc4-r-snorm": true, + "bc5-rg-unorm": true, + "bc5-rg-snorm": true, + "bc6h-rgb-ufloat": true, + "bc6h-rgb-float": true, + "bc7-rgba-unorm": true, + "bc7-rgba-unorm-srgb": true, + // ETC2 compressed formats usable if "texture-compression-etc2" is both + // supported by the device/user agent and enabled in requestDevice. + "etc2-rgb8unorm": true, + "etc2-rgb8unorm-srgb": true, + "etc2-rgb8a1unorm": true, + "etc2-rgb8a1unorm-srgb": true, + "etc2-rgba8unorm": true, + "etc2-rgba8unorm-srgb": true, + "eac-r11unorm": true, + "eac-r11snorm": true, + "eac-rg11unorm": true, + "eac-rg11snorm": true, + // ASTC compressed formats usable if "texture-compression-astc" is both + // supported by the device/user agent and enabled in requestDevice. + "astc-4x4-unorm": true, + "astc-4x4-unorm-srgb": true, + "astc-5x4-unorm": true, + "astc-5x4-unorm-srgb": true, + "astc-5x5-unorm": true, + "astc-5x5-unorm-srgb": true, + "astc-6x5-unorm": true, + "astc-6x5-unorm-srgb": true, + "astc-6x6-unorm": true, + "astc-6x6-unorm-srgb": true, + "astc-8x5-unorm": true, + "astc-8x5-unorm-srgb": true, + "astc-8x6-unorm": true, + "astc-8x6-unorm-srgb": true, + "astc-8x8-unorm": true, + "astc-8x8-unorm-srgb": true, + "astc-10x5-unorm": true, + "astc-10x5-unorm-srgb": true, + "astc-10x6-unorm": true, + "astc-10x6-unorm-srgb": true, + "astc-10x8-unorm": true, + "astc-10x8-unorm-srgb": true, + "astc-10x10-unorm": true, + "astc-10x10-unorm-srgb": true, + "astc-12x10-unorm": true, + "astc-12x10-unorm-srgb": true, + "astc-12x12-unorm": true, + "astc-12x12-unorm-srgb": true + }; + const glUploadCompressedTextureResource = { + id: "compressed", + upload(source, glTexture, gl, _webGLVersion, targetOverride, _forceAllocation) { + const target = targetOverride != null ? targetOverride : glTexture.target; + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); + let mipWidth = source.pixelWidth; + let mipHeight = source.pixelHeight; + const compressed = !!compressedFormatMap[source.format]; + for (let i = 0; i < source.resource.length; i++) { + const levelBuffer = source.resource[i]; + if (compressed) { + gl.compressedTexImage2D( + target, + i, + glTexture.internalFormat, + mipWidth, + mipHeight, + 0, + levelBuffer + ); + } else { + gl.texImage2D( + target, + i, + glTexture.internalFormat, + mipWidth, + mipHeight, + 0, + glTexture.format, + glTexture.type, + levelBuffer + ); + } + mipWidth = Math.max(mipWidth >> 1, 1); + mipHeight = Math.max(mipHeight >> 1, 1); + } + } + }; + + "use strict"; + const FACE_ORDER$1 = ["right", "left", "top", "bottom", "front", "back"]; + function createGlUploadCubeTextureResource(uploaders) { + return { + id: "cube", + upload(source, glTexture, gl, webGLVersion) { + const faces = source.faces; + for (let faceIndex = 0; faceIndex < FACE_ORDER$1.length; faceIndex++) { + const key = FACE_ORDER$1[faceIndex]; + const face = faces[key]; + const uploader = uploaders[face.uploadMethodId] || uploaders.image; + uploader.upload( + face, + glTexture, + gl, + webGLVersion, + // Use the face target for the current face. cube faces ids go up 1 so + // GL_TARGETS.TEXTURE_CUBE_MAP_POSITIVE_X + i addresses the i'th face target. + GL_TARGETS.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, + // Force allocation for the first upload of each face. + (glTexture._layerInitMask & 1 << faceIndex) === 0 + ); + glTexture._layerInitMask |= 1 << faceIndex; + } + glTexture.width = source.pixelWidth; + glTexture.height = source.pixelHeight; + } + }; + } + + "use strict"; + const glUploadImageResource = { + id: "image", + upload(source, glTexture, gl, webGLVersion, targetOverride, forceAllocation = false) { + const target = targetOverride || glTexture.target; + const textureWidth = source.pixelWidth; + const textureHeight = source.pixelHeight; + const resourceWidth = source.resourceWidth; + const resourceHeight = source.resourceHeight; + const isWebGL2 = webGLVersion === 2; + const needsAllocation = forceAllocation || glTexture.width !== textureWidth || glTexture.height !== textureHeight; + const resourceFitsTexture = resourceWidth >= textureWidth && resourceHeight >= textureHeight; + const resource = source.resource; + const uploadFunction = isWebGL2 ? uploadImageWebGL2 : uploadImageWebGL1; + uploadFunction( + gl, + target, + glTexture, + textureWidth, + textureHeight, + resourceWidth, + resourceHeight, + resource, + needsAllocation, + resourceFitsTexture + ); + glTexture.width = textureWidth; + glTexture.height = textureHeight; + } + }; + function uploadImageWebGL2(gl, target, glTexture, textureWidth, textureHeight, resourceWidth, resourceHeight, resource, needsAllocation, resourceFitsTexture) { + if (!resourceFitsTexture) { + if (needsAllocation) { + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + textureWidth, + textureHeight, + 0, + glTexture.format, + glTexture.type, + null + ); + } + gl.texSubImage2D( + target, + 0, + 0, + 0, + resourceWidth, + resourceHeight, + glTexture.format, + glTexture.type, + resource + ); + return; + } + if (!needsAllocation) { + gl.texSubImage2D( + target, + 0, + 0, + 0, + glTexture.format, + glTexture.type, + resource + ); + return; + } + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + textureWidth, + textureHeight, + 0, + glTexture.format, + glTexture.type, + resource + ); + } + function uploadImageWebGL1(gl, target, glTexture, textureWidth, textureHeight, _resourceWidth, _resourceHeight, resource, needsAllocation, resourceFitsTexture) { + if (!resourceFitsTexture) { + if (needsAllocation) { + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + textureWidth, + textureHeight, + 0, + glTexture.format, + glTexture.type, + null + ); + } + gl.texSubImage2D( + target, + 0, + 0, + 0, + glTexture.format, + glTexture.type, + resource + ); + return; + } + if (!needsAllocation) { + gl.texSubImage2D( + target, + 0, + 0, + 0, + glTexture.format, + glTexture.type, + resource + ); + return; + } + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + glTexture.format, + glTexture.type, + resource + ); + } + + "use strict"; + const defaultForceAllocation = isSafari(); + const glUploadVideoResource = { + id: "video", + upload(source, glTexture, gl, webGLVersion, targetOverride, forceAllocation = defaultForceAllocation) { + if (!source.isValid) { + const target = targetOverride != null ? targetOverride : glTexture.target; + gl.texImage2D( + target, + 0, + glTexture.internalFormat, + 1, + 1, + 0, + glTexture.format, + glTexture.type, + null + ); + return; + } + glUploadImageResource.upload(source, glTexture, gl, webGLVersion, targetOverride, forceAllocation); + } + }; + + "use strict"; + const scaleModeToGlFilter = { + linear: 9729, + nearest: 9728 + }; + const mipmapScaleModeToGlFilter = { + linear: { + linear: 9987, + nearest: 9985 + }, + nearest: { + linear: 9986, + nearest: 9984 + } + }; + const wrapModeToGlAddress = { + "clamp-to-edge": 33071, + repeat: 10497, + "mirror-repeat": 33648 + }; + const compareModeToGlCompare = { + never: 512, + less: 513, + equal: 514, + "less-equal": 515, + greater: 516, + "not-equal": 517, + "greater-equal": 518, + always: 519 + }; + + "use strict"; + function applyStyleParams(style, gl, mipmaps, anisotropicExt, glFunctionName, firstParam, forceClamp, firstCreation) { + const castParam = firstParam; + if (!firstCreation || style.addressModeU !== "repeat" || style.addressModeV !== "repeat" || style.addressModeW !== "repeat") { + const wrapModeS = wrapModeToGlAddress[forceClamp ? "clamp-to-edge" : style.addressModeU]; + const wrapModeT = wrapModeToGlAddress[forceClamp ? "clamp-to-edge" : style.addressModeV]; + const wrapModeR = wrapModeToGlAddress[forceClamp ? "clamp-to-edge" : style.addressModeW]; + gl[glFunctionName](castParam, gl.TEXTURE_WRAP_S, wrapModeS); + gl[glFunctionName](castParam, gl.TEXTURE_WRAP_T, wrapModeT); + if (gl.TEXTURE_WRAP_R) gl[glFunctionName](castParam, gl.TEXTURE_WRAP_R, wrapModeR); + } + if (!firstCreation || style.magFilter !== "linear") { + gl[glFunctionName](castParam, gl.TEXTURE_MAG_FILTER, scaleModeToGlFilter[style.magFilter]); + } + if (mipmaps) { + if (!firstCreation || style.mipmapFilter !== "linear") { + const glFilterMode = mipmapScaleModeToGlFilter[style.minFilter][style.mipmapFilter]; + gl[glFunctionName](castParam, gl.TEXTURE_MIN_FILTER, glFilterMode); + } + } else { + gl[glFunctionName](castParam, gl.TEXTURE_MIN_FILTER, scaleModeToGlFilter[style.minFilter]); + } + if (anisotropicExt && style.maxAnisotropy > 1) { + const level = Math.min(style.maxAnisotropy, gl.getParameter(anisotropicExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT)); + gl[glFunctionName](castParam, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, level); + } + if (style.compare) { + gl[glFunctionName](castParam, gl.TEXTURE_COMPARE_FUNC, compareModeToGlCompare[style.compare]); + } + } + + "use strict"; + function mapFormatToGlFormat(gl) { + return { + // 8-bit formats + r8unorm: gl.RED, + r8snorm: gl.RED, + r8uint: gl.RED, + r8sint: gl.RED, + // 16-bit formats + r16uint: gl.RED, + r16sint: gl.RED, + r16float: gl.RED, + rg8unorm: gl.RG, + rg8snorm: gl.RG, + rg8uint: gl.RG, + rg8sint: gl.RG, + // 32-bit formats + r32uint: gl.RED, + r32sint: gl.RED, + r32float: gl.RED, + rg16uint: gl.RG, + rg16sint: gl.RG, + rg16float: gl.RG, + rgba8unorm: gl.RGBA, + "rgba8unorm-srgb": gl.RGBA, + // Packed 32-bit formats + rgba8snorm: gl.RGBA, + rgba8uint: gl.RGBA, + rgba8sint: gl.RGBA, + bgra8unorm: gl.RGBA, + "bgra8unorm-srgb": gl.RGBA, + rgb9e5ufloat: gl.RGB, + rgb10a2unorm: gl.RGBA, + rg11b10ufloat: gl.RGB, + // 64-bit formats + rg32uint: gl.RG, + rg32sint: gl.RG, + rg32float: gl.RG, + rgba16uint: gl.RGBA, + rgba16sint: gl.RGBA, + rgba16float: gl.RGBA, + // 128-bit formats + rgba32uint: gl.RGBA, + rgba32sint: gl.RGBA, + rgba32float: gl.RGBA, + // Depth/stencil formats + stencil8: gl.STENCIL_INDEX8, + depth16unorm: gl.DEPTH_COMPONENT, + depth24plus: gl.DEPTH_COMPONENT, + "depth24plus-stencil8": gl.DEPTH_STENCIL, + depth32float: gl.DEPTH_COMPONENT, + "depth32float-stencil8": gl.DEPTH_STENCIL + }; + } + + "use strict"; + var __defProp$l = Object.defineProperty; + var __defProps$d = Object.defineProperties; + var __getOwnPropDescs$d = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$m = Object.getOwnPropertySymbols; + var __hasOwnProp$m = Object.prototype.hasOwnProperty; + var __propIsEnum$m = Object.prototype.propertyIsEnumerable; + var __defNormalProp$l = (obj, key, value) => key in obj ? __defProp$l(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$l = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$m.call(b, prop)) + __defNormalProp$l(a, prop, b[prop]); + if (__getOwnPropSymbols$m) + for (var prop of __getOwnPropSymbols$m(b)) { + if (__propIsEnum$m.call(b, prop)) + __defNormalProp$l(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$d = (a, b) => __defProps$d(a, __getOwnPropDescs$d(b)); + function mapFormatToGlInternalFormat(gl, extensions) { + let srgb = {}; + let bgra8unorm = gl.RGBA; + if (!(gl instanceof DOMAdapter.get().getWebGLRenderingContext())) { + srgb = { + "rgba8unorm-srgb": gl.SRGB8_ALPHA8, + "bgra8unorm-srgb": gl.SRGB8_ALPHA8 + }; + bgra8unorm = gl.RGBA8; + } else if (extensions.srgb) { + srgb = { + "rgba8unorm-srgb": extensions.srgb.SRGB8_ALPHA8_EXT, + "bgra8unorm-srgb": extensions.srgb.SRGB8_ALPHA8_EXT + }; + } + return __spreadValues$l(__spreadValues$l(__spreadValues$l(__spreadValues$l(__spreadValues$l(__spreadValues$l(__spreadProps$d(__spreadValues$l({ + // 8-bit formats + r8unorm: gl.R8, + r8snorm: gl.R8_SNORM, + r8uint: gl.R8UI, + r8sint: gl.R8I, + // 16-bit formats + r16uint: gl.R16UI, + r16sint: gl.R16I, + r16float: gl.R16F, + rg8unorm: gl.RG8, + rg8snorm: gl.RG8_SNORM, + rg8uint: gl.RG8UI, + rg8sint: gl.RG8I, + // 32-bit formats + r32uint: gl.R32UI, + r32sint: gl.R32I, + r32float: gl.R32F, + rg16uint: gl.RG16UI, + rg16sint: gl.RG16I, + rg16float: gl.RG16F, + rgba8unorm: gl.RGBA + }, srgb), { + // Packed 32-bit formats + rgba8snorm: gl.RGBA8_SNORM, + rgba8uint: gl.RGBA8UI, + rgba8sint: gl.RGBA8I, + bgra8unorm, + rgb9e5ufloat: gl.RGB9_E5, + rgb10a2unorm: gl.RGB10_A2, + rg11b10ufloat: gl.R11F_G11F_B10F, + // 64-bit formats + rg32uint: gl.RG32UI, + rg32sint: gl.RG32I, + rg32float: gl.RG32F, + rgba16uint: gl.RGBA16UI, + rgba16sint: gl.RGBA16I, + rgba16float: gl.RGBA16F, + // 128-bit formats + rgba32uint: gl.RGBA32UI, + rgba32sint: gl.RGBA32I, + rgba32float: gl.RGBA32F, + // Depth/stencil formats + stencil8: gl.STENCIL_INDEX8, + depth16unorm: gl.DEPTH_COMPONENT16, + depth24plus: gl.DEPTH_COMPONENT24, + "depth24plus-stencil8": gl.DEPTH24_STENCIL8, + depth32float: gl.DEPTH_COMPONENT32F, + "depth32float-stencil8": gl.DEPTH32F_STENCIL8 + }), extensions.s3tc ? { + "bc1-rgba-unorm": extensions.s3tc.COMPRESSED_RGBA_S3TC_DXT1_EXT, + "bc2-rgba-unorm": extensions.s3tc.COMPRESSED_RGBA_S3TC_DXT3_EXT, + "bc3-rgba-unorm": extensions.s3tc.COMPRESSED_RGBA_S3TC_DXT5_EXT + } : {}), extensions.s3tc_sRGB ? { + "bc1-rgba-unorm-srgb": extensions.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + "bc2-rgba-unorm-srgb": extensions.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, + "bc3-rgba-unorm-srgb": extensions.s3tc_sRGB.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + } : {}), extensions.rgtc ? { + "bc4-r-unorm": extensions.rgtc.COMPRESSED_RED_RGTC1_EXT, + "bc4-r-snorm": extensions.rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT, + "bc5-rg-unorm": extensions.rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT, + "bc5-rg-snorm": extensions.rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT + } : {}), extensions.bptc ? { + "bc6h-rgb-float": extensions.bptc.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, + "bc6h-rgb-ufloat": extensions.bptc.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, + "bc7-rgba-unorm": extensions.bptc.COMPRESSED_RGBA_BPTC_UNORM_EXT, + "bc7-rgba-unorm-srgb": extensions.bptc.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT + } : {}), extensions.etc ? { + "etc2-rgb8unorm": extensions.etc.COMPRESSED_RGB8_ETC2, + "etc2-rgb8unorm-srgb": extensions.etc.COMPRESSED_SRGB8_ETC2, + "etc2-rgb8a1unorm": extensions.etc.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, + "etc2-rgb8a1unorm-srgb": extensions.etc.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, + "etc2-rgba8unorm": extensions.etc.COMPRESSED_RGBA8_ETC2_EAC, + "etc2-rgba8unorm-srgb": extensions.etc.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, + "eac-r11unorm": extensions.etc.COMPRESSED_R11_EAC, + // 'eac-r11snorm' + "eac-rg11unorm": extensions.etc.COMPRESSED_SIGNED_RG11_EAC + // 'eac-rg11snorm' + } : {}), extensions.astc ? { + "astc-4x4-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_4x4_KHR, + "astc-4x4-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, + "astc-5x4-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_5x4_KHR, + "astc-5x4-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, + "astc-5x5-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_5x5_KHR, + "astc-5x5-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, + "astc-6x5-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_6x5_KHR, + "astc-6x5-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, + "astc-6x6-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_6x6_KHR, + "astc-6x6-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, + "astc-8x5-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_8x5_KHR, + "astc-8x5-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, + "astc-8x6-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_8x6_KHR, + "astc-8x6-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, + "astc-8x8-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_8x8_KHR, + "astc-8x8-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, + "astc-10x5-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_10x5_KHR, + "astc-10x5-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, + "astc-10x6-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_10x6_KHR, + "astc-10x6-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, + "astc-10x8-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_10x8_KHR, + "astc-10x8-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, + "astc-10x10-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_10x10_KHR, + "astc-10x10-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, + "astc-12x10-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_12x10_KHR, + "astc-12x10-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, + "astc-12x12-unorm": extensions.astc.COMPRESSED_RGBA_ASTC_12x12_KHR, + "astc-12x12-unorm-srgb": extensions.astc.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR + } : {}); + } + + "use strict"; + function mapFormatToGlType(gl) { + return { + // 8-bit formats + r8unorm: gl.UNSIGNED_BYTE, + r8snorm: gl.BYTE, + r8uint: gl.UNSIGNED_BYTE, + r8sint: gl.BYTE, + // 16-bit formats + r16uint: gl.UNSIGNED_SHORT, + r16sint: gl.SHORT, + r16float: gl.HALF_FLOAT, + rg8unorm: gl.UNSIGNED_BYTE, + rg8snorm: gl.BYTE, + rg8uint: gl.UNSIGNED_BYTE, + rg8sint: gl.BYTE, + // 32-bit formats + r32uint: gl.UNSIGNED_INT, + r32sint: gl.INT, + r32float: gl.FLOAT, + rg16uint: gl.UNSIGNED_SHORT, + rg16sint: gl.SHORT, + rg16float: gl.HALF_FLOAT, + rgba8unorm: gl.UNSIGNED_BYTE, + "rgba8unorm-srgb": gl.UNSIGNED_BYTE, + // Packed 32-bit formats + rgba8snorm: gl.BYTE, + rgba8uint: gl.UNSIGNED_BYTE, + rgba8sint: gl.BYTE, + bgra8unorm: gl.UNSIGNED_BYTE, + "bgra8unorm-srgb": gl.UNSIGNED_BYTE, + rgb9e5ufloat: gl.UNSIGNED_INT_5_9_9_9_REV, + rgb10a2unorm: gl.UNSIGNED_INT_2_10_10_10_REV, + rg11b10ufloat: gl.UNSIGNED_INT_10F_11F_11F_REV, + // 64-bit formats + rg32uint: gl.UNSIGNED_INT, + rg32sint: gl.INT, + rg32float: gl.FLOAT, + rgba16uint: gl.UNSIGNED_SHORT, + rgba16sint: gl.SHORT, + rgba16float: gl.HALF_FLOAT, + // 128-bit formats + rgba32uint: gl.UNSIGNED_INT, + rgba32sint: gl.INT, + rgba32float: gl.FLOAT, + // Depth/stencil formats + stencil8: gl.UNSIGNED_BYTE, + depth16unorm: gl.UNSIGNED_SHORT, + depth24plus: gl.UNSIGNED_INT, + "depth24plus-stencil8": gl.UNSIGNED_INT_24_8, + depth32float: gl.FLOAT, + "depth32float-stencil8": gl.FLOAT_32_UNSIGNED_INT_24_8_REV + }; + } + + "use strict"; + function mapViewDimensionToGlTarget(gl) { + return { + "2d": gl.TEXTURE_2D, + cube: gl.TEXTURE_CUBE_MAP, + "1d": null, + // WebGL2 only + "3d": (gl == null ? void 0 : gl.TEXTURE_3D) || null, + "2d-array": (gl == null ? void 0 : gl.TEXTURE_2D_ARRAY) || null, + "cube-array": (gl == null ? void 0 : gl.TEXTURE_CUBE_MAP_ARRAY) || null + }; + } + + "use strict"; + function unpremultiplyAlpha$1(pixels) { + if (pixels instanceof Uint8ClampedArray) { + pixels = new Uint8Array(pixels.buffer); + } + const n = pixels.length; + for (let i = 0; i < n; i += 4) { + const alpha = pixels[i + 3]; + if (alpha !== 0) { + const a = 255.001 / alpha; + pixels[i] = pixels[i] * a + 0.5; + pixels[i + 1] = pixels[i + 1] * a + 0.5; + pixels[i + 2] = pixels[i + 2] * a + 0.5; + } + } + } + + "use strict"; + var __defProp$k = Object.defineProperty; + var __defProps$c = Object.defineProperties; + var __getOwnPropDescs$c = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$l = Object.getOwnPropertySymbols; + var __hasOwnProp$l = Object.prototype.hasOwnProperty; + var __propIsEnum$l = Object.prototype.propertyIsEnumerable; + var __defNormalProp$k = (obj, key, value) => key in obj ? __defProp$k(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$k = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$l.call(b, prop)) + __defNormalProp$k(a, prop, b[prop]); + if (__getOwnPropSymbols$l) + for (var prop of __getOwnPropSymbols$l(b)) { + if (__propIsEnum$l.call(b, prop)) + __defNormalProp$k(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$c = (a, b) => __defProps$c(a, __getOwnPropDescs$c(b)); + const BYTES_PER_PIXEL = 4; + class GlTextureSystem { + constructor(renderer) { + this._glSamplers = /* @__PURE__ */ Object.create(null); + this._boundTextures = []; + this._activeTextureLocation = -1; + this._boundSamplers = /* @__PURE__ */ Object.create(null); + this._premultiplyAlpha = false; + // TODO - separate samplers will be a cool thing to add, but not right now! + this._useSeparateSamplers = false; + this._renderer = renderer; + this._managedTextures = new GCManagedHash({ + renderer, + type: "resource", + onUnload: this.onSourceUnload.bind(this), + name: "glTexture" + }); + const baseUploaders = { + image: glUploadImageResource, + buffer: glUploadBufferImageResource, + video: glUploadVideoResource, + compressed: glUploadCompressedTextureResource + }; + this._uploads = __spreadProps$c(__spreadValues$k({}, baseUploaders), { + cube: createGlUploadCubeTextureResource(baseUploaders) + }); + } + /** + * @deprecated since 8.15.0 + */ + get managedTextures() { + return Object.values(this._managedTextures.items); + } + contextChange(gl) { + this._gl = gl; + if (!this._mapFormatToInternalFormat) { + this._mapFormatToInternalFormat = mapFormatToGlInternalFormat(gl, this._renderer.context.extensions); + this._mapFormatToType = mapFormatToGlType(gl); + this._mapFormatToFormat = mapFormatToGlFormat(gl); + this._mapViewDimensionToGlTarget = mapViewDimensionToGlTarget(gl); + } + this._managedTextures.removeAll(true); + this._glSamplers = /* @__PURE__ */ Object.create(null); + this._boundSamplers = /* @__PURE__ */ Object.create(null); + this._premultiplyAlpha = false; + for (let i = 0; i < 16; i++) { + this.bind(Texture.EMPTY, i); + } + } + /** + * Initializes a texture source, if it has already been initialized nothing will happen. + * @param source - The texture source to initialize. + * @returns The initialized texture source. + */ + initSource(source) { + this.bind(source); + } + bind(texture, location = 0) { + const source = texture.source; + if (texture) { + this.bindSource(source, location); + if (this._useSeparateSamplers) { + this._bindSampler(source.style, location); + } + } else { + this.bindSource(null, location); + if (this._useSeparateSamplers) { + this._bindSampler(null, location); + } + } + } + bindSource(source, location = 0) { + const gl = this._gl; + source._gcLastUsed = this._renderer.gc.now; + if (this._boundTextures[location] !== source) { + this._boundTextures[location] = source; + this._activateLocation(location); + source || (source = Texture.EMPTY.source); + const glTexture = this.getGlSource(source); + gl.bindTexture(glTexture.target, glTexture.texture); + } + } + _bindSampler(style, location = 0) { + const gl = this._gl; + if (!style) { + this._boundSamplers[location] = null; + gl.bindSampler(location, null); + return; + } + const sampler = this._getGlSampler(style); + if (this._boundSamplers[location] !== sampler) { + this._boundSamplers[location] = sampler; + gl.bindSampler(location, sampler); + } + } + unbind(texture) { + const source = texture.source; + const boundTextures = this._boundTextures; + const gl = this._gl; + for (let i = 0; i < boundTextures.length; i++) { + if (boundTextures[i] === source) { + this._activateLocation(i); + const glTexture = this.getGlSource(source); + gl.bindTexture(glTexture.target, null); + boundTextures[i] = null; + } + } + } + _activateLocation(location) { + if (this._activeTextureLocation !== location) { + this._activeTextureLocation = location; + this._gl.activeTexture(this._gl.TEXTURE0 + location); + } + } + _initSource(source) { + const gl = this._gl; + const glTexture = new GlTexture(gl.createTexture()); + glTexture.type = this._mapFormatToType[source.format]; + glTexture.internalFormat = this._mapFormatToInternalFormat[source.format]; + glTexture.format = this._mapFormatToFormat[source.format]; + glTexture.target = this._mapViewDimensionToGlTarget[source.viewDimension]; + if (glTexture.target === null) { + throw new Error(`Unsupported view dimension: ${source.viewDimension} with this webgl version: ${this._renderer.context.webGLVersion}`); + } + if (source.uploadMethodId === "cube") { + glTexture.target = gl.TEXTURE_CUBE_MAP; + } + if (source.autoGenerateMipmaps && (this._renderer.context.supports.nonPowOf2mipmaps || source.isPowerOfTwo)) { + const biggestDimension = Math.max(source.width, source.height); + source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1; + } + source._gpuData[this._renderer.uid] = glTexture; + const added = this._managedTextures.add(source); + if (added) { + source.on("update", this.onSourceUpdate, this); + source.on("resize", this.onSourceUpdate, this); + source.on("styleChange", this.onStyleChange, this); + source.on("updateMipmaps", this.onUpdateMipmaps, this); + } + this.onSourceUpdate(source); + this.updateStyle(source, false); + return glTexture; + } + onStyleChange(source) { + this.updateStyle(source, false); + } + updateStyle(source, firstCreation) { + const gl = this._gl; + const glTexture = this.getGlSource(source); + gl.bindTexture(glTexture.target, glTexture.texture); + this._boundTextures[this._activeTextureLocation] = source; + applyStyleParams( + source.style, + gl, + source.mipLevelCount > 1, + this._renderer.context.extensions.anisotropicFiltering, + "texParameteri", + glTexture.target, + // will force a clamp to edge if the texture is not a power of two + !this._renderer.context.supports.nonPowOf2wrapping && !source.isPowerOfTwo, + firstCreation + ); + } + onSourceUnload(source, contextLost = false) { + const glTexture = source._gpuData[this._renderer.uid]; + if (!glTexture) return; + if (!contextLost) { + this.unbind(source); + this._gl.deleteTexture(glTexture.texture); + } + source.off("update", this.onSourceUpdate, this); + source.off("resize", this.onSourceUpdate, this); + source.off("styleChange", this.onStyleChange, this); + source.off("updateMipmaps", this.onUpdateMipmaps, this); + } + onSourceUpdate(source) { + const gl = this._gl; + const glTexture = this.getGlSource(source); + gl.bindTexture(glTexture.target, glTexture.texture); + this._boundTextures[this._activeTextureLocation] = source; + const premultipliedAlpha = source.alphaMode === "premultiply-alpha-on-upload"; + if (this._premultiplyAlpha !== premultipliedAlpha) { + this._premultiplyAlpha = premultipliedAlpha; + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultipliedAlpha); + } + if (this._uploads[source.uploadMethodId]) { + this._uploads[source.uploadMethodId].upload(source, glTexture, gl, this._renderer.context.webGLVersion); + } else if (glTexture.target === gl.TEXTURE_2D) { + this._initEmptyTexture2D(glTexture, source); + } else if (glTexture.target === gl.TEXTURE_2D_ARRAY) { + this._initEmptyTexture2DArray(glTexture, source); + } else if (glTexture.target === gl.TEXTURE_CUBE_MAP) { + this._initEmptyTextureCube(glTexture, source); + } else { + throw new Error("[GlTextureSystem] Unsupported texture target for empty allocation."); + } + this._applyMipRange(glTexture, source); + if (source.autoGenerateMipmaps && source.mipLevelCount > 1) { + this.onUpdateMipmaps(source, false); + } + } + onUpdateMipmaps(source, bind = true) { + if (bind) this.bindSource(source, 0); + const glTexture = this.getGlSource(source); + this._gl.generateMipmap(glTexture.target); + } + _initEmptyTexture2D(glTexture, source) { + const gl = this._gl; + gl.texImage2D( + gl.TEXTURE_2D, + 0, + glTexture.internalFormat, + source.pixelWidth, + source.pixelHeight, + 0, + glTexture.format, + glTexture.type, + null + ); + let w = Math.max(source.pixelWidth >> 1, 1); + let h = Math.max(source.pixelHeight >> 1, 1); + for (let level = 1; level < source.mipLevelCount; level++) { + gl.texImage2D( + gl.TEXTURE_2D, + level, + glTexture.internalFormat, + w, + h, + 0, + glTexture.format, + glTexture.type, + null + ); + w = Math.max(w >> 1, 1); + h = Math.max(h >> 1, 1); + } + } + _initEmptyTexture2DArray(glTexture, source) { + if (this._renderer.context.webGLVersion !== 2) { + throw new Error("[GlTextureSystem] TEXTURE_2D_ARRAY requires WebGL2."); + } + const gl2 = this._gl; + const depth = Math.max(source.arrayLayerCount | 0, 1); + gl2.texImage3D( + gl2.TEXTURE_2D_ARRAY, + 0, + glTexture.internalFormat, + source.pixelWidth, + source.pixelHeight, + depth, + 0, + glTexture.format, + glTexture.type, + null + ); + let w = Math.max(source.pixelWidth >> 1, 1); + let h = Math.max(source.pixelHeight >> 1, 1); + for (let level = 1; level < source.mipLevelCount; level++) { + gl2.texImage3D( + gl2.TEXTURE_2D_ARRAY, + level, + glTexture.internalFormat, + w, + h, + depth, + 0, + glTexture.format, + glTexture.type, + null + ); + w = Math.max(w >> 1, 1); + h = Math.max(h >> 1, 1); + } + } + _initEmptyTextureCube(glTexture, source) { + const gl = this._gl; + const totalCubeFaces = 6; + for (let face = 0; face < totalCubeFaces; face++) { + gl.texImage2D( + gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, + 0, + glTexture.internalFormat, + source.pixelWidth, + source.pixelHeight, + 0, + glTexture.format, + glTexture.type, + null + ); + } + let w = Math.max(source.pixelWidth >> 1, 1); + let h = Math.max(source.pixelHeight >> 1, 1); + for (let level = 1; level < source.mipLevelCount; level++) { + for (let face = 0; face < totalCubeFaces; face++) { + gl.texImage2D( + gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, + level, + glTexture.internalFormat, + w, + h, + 0, + glTexture.format, + glTexture.type, + null + ); + } + w = Math.max(w >> 1, 1); + h = Math.max(h >> 1, 1); + } + } + /** + * Applies a mip range to the currently-bound texture so WebGL2 considers the texture "mipmap complete" + * for the declared `mipLevelCount` (especially important for partial mip chains rendered via FBO). + * @param glTexture - The GL texture wrapper. + * @param source - The texture source describing mipLevelCount. + */ + _applyMipRange(glTexture, source) { + if (this._renderer.context.webGLVersion !== 2) return; + const gl = this._gl; + const maxLevel = Math.max((source.mipLevelCount | 0) - 1, 0); + gl.texParameteri(glTexture.target, gl.TEXTURE_BASE_LEVEL, 0); + gl.texParameteri(glTexture.target, gl.TEXTURE_MAX_LEVEL, maxLevel); + } + _initSampler(style) { + const gl = this._gl; + const glSampler = this._gl.createSampler(); + this._glSamplers[style._resourceId] = glSampler; + applyStyleParams( + style, + gl, + this._boundTextures[this._activeTextureLocation].mipLevelCount > 1, + this._renderer.context.extensions.anisotropicFiltering, + "samplerParameteri", + glSampler, + false, + true + ); + return this._glSamplers[style._resourceId]; + } + _getGlSampler(sampler) { + return this._glSamplers[sampler._resourceId] || this._initSampler(sampler); + } + getGlSource(source) { + source._gcLastUsed = this._renderer.gc.now; + return source._gpuData[this._renderer.uid] || this._initSource(source); + } + generateCanvas(texture) { + const { pixels, width, height } = this.getPixels(texture); + const canvas = DOMAdapter.get().createCanvas(); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext("2d"); + if (ctx) { + const imageData = ctx.createImageData(width, height); + imageData.data.set(pixels); + ctx.putImageData(imageData, 0, 0); + } + return canvas; + } + getPixels(texture) { + const resolution = texture.source.resolution; + const frame = texture.frame; + const width = Math.max(Math.round(frame.width * resolution), 1); + const height = Math.max(Math.round(frame.height * resolution), 1); + const pixels = new Uint8Array(BYTES_PER_PIXEL * width * height); + const renderer = this._renderer; + const renderTarget = renderer.renderTarget.getRenderTarget(texture); + const glRenterTarget = renderer.renderTarget.getGpuRenderTarget(renderTarget); + const gl = renderer.gl; + gl.bindFramebuffer(gl.FRAMEBUFFER, glRenterTarget.resolveTargetFramebuffer); + gl.readPixels( + Math.round(frame.x * resolution), + Math.round(frame.y * resolution), + width, + height, + gl.RGBA, + gl.UNSIGNED_BYTE, + pixels + ); + if (false) { + unpremultiplyAlpha(pixels); + } + return { pixels: new Uint8ClampedArray(pixels.buffer), width, height }; + } + destroy() { + this._managedTextures.destroy(); + this._glSamplers = null; + this._boundTextures = null; + this._boundSamplers = null; + this._mapFormatToInternalFormat = null; + this._mapFormatToType = null; + this._mapFormatToFormat = null; + this._uploads = null; + this._renderer = null; + } + resetState() { + this._activeTextureLocation = -1; + this._boundTextures.fill(Texture.EMPTY.source); + this._boundSamplers = /* @__PURE__ */ Object.create(null); + const gl = this._gl; + this._premultiplyAlpha = false; + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._premultiplyAlpha); + } + } + /** @ignore */ + GlTextureSystem.extension = { + type: [ + ExtensionType.WebGLSystem + ], + name: "texture" + }; + + "use strict"; + + "use strict"; + class GlGraphicsAdaptor { + contextChange(renderer) { + const uniforms = new UniformGroup({ + uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4" }, + uTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + uRound: { value: 0, type: "f32" } + }); + const maxTextures = renderer.limits.maxBatchableTextures; + const glProgram = compileHighShaderGlProgram({ + name: "graphics", + bits: [ + colorBitGl, + generateTextureBatchBitGl(maxTextures), + localUniformBitGl, + roundPixelsBitGl + ] + }); + this.shader = new Shader({ + glProgram, + resources: { + localUniforms: uniforms, + batchSamplers: getBatchSamplersUniformGroup(maxTextures) + } + }); + } + execute(graphicsPipe, renderable) { + const context = renderable.context; + const shader = context.customShader || this.shader; + const renderer = graphicsPipe.renderer; + const contextSystem = renderer.graphicsContext; + const { + batcher, + instructions + } = contextSystem.getContextRenderData(context); + shader.groups[0] = renderer.globalUniforms.bindGroup; + renderer.state.set(graphicsPipe.state); + renderer.shader.bind(shader); + renderer.geometry.bind(batcher.geometry, shader.glProgram); + const batches = instructions.instructions; + for (let i = 0; i < instructions.instructionSize; i++) { + const batch = batches[i]; + if (batch.size) { + for (let j = 0; j < batch.textures.count; j++) { + renderer.texture.bind(batch.textures.textures[j], j); + } + renderer.geometry.draw(batch.topology, batch.size, batch.start); + } + } + } + destroy() { + this.shader.destroy(true); + this.shader = null; + } + } + /** @ignore */ + GlGraphicsAdaptor.extension = { + type: [ + ExtensionType.WebGLPipesAdaptor + ], + name: "graphics" + }; + + "use strict"; + class GlMeshAdaptor { + init() { + const glProgram = compileHighShaderGlProgram({ + name: "mesh", + bits: [ + localUniformBitGl, + textureBitGl, + roundPixelsBitGl + ] + }); + this._shader = new Shader({ + glProgram, + resources: { + uTexture: Texture.EMPTY.source, + textureUniforms: { + uTextureMatrix: { type: "mat3x3", value: new Matrix() } + } + } + }); + } + execute(meshPipe, mesh) { + const renderer = meshPipe.renderer; + let shader = mesh._shader; + if (!shader) { + shader = this._shader; + const texture = mesh.texture; + const source = texture.source; + shader.resources.uTexture = source; + shader.resources.uSampler = source.style; + shader.resources.textureUniforms.uniforms.uTextureMatrix = texture.textureMatrix.mapCoord; + } else if (!shader.glProgram) { + warn("Mesh shader has no glProgram", mesh.shader); + return; + } + shader.groups[100] = renderer.globalUniforms.bindGroup; + shader.groups[101] = meshPipe.localUniformsBindGroup; + renderer.encoder.draw({ + geometry: mesh._geometry, + shader, + state: mesh.state + }); + } + destroy() { + this._shader.destroy(true); + this._shader = null; + } + } + GlMeshAdaptor.extension = { + type: [ + ExtensionType.WebGLPipesAdaptor + ], + name: "mesh" + }; + + "use strict"; + const DefaultWebGLSystems = [ + ...SharedSystems, + GlUboSystem, + GlBackBufferSystem, + GlContextSystem, + GlLimitsSystem, + GlBufferSystem, + GlTextureSystem, + GlRenderTargetSystem, + GlGeometrySystem, + GlUniformGroupSystem, + GlShaderSystem, + GlEncoderSystem, + GlStateSystem, + GlStencilSystem, + GlColorMaskSystem + ]; + const DefaultWebGLPipes = [...SharedRenderPipes]; + const DefaultWebGLAdapters = [GlBatchAdaptor, GlMeshAdaptor, GlGraphicsAdaptor]; + const systems$1 = []; + const renderPipes$1 = []; + const renderPipeAdaptors$1 = []; + extensions.handleByNamedList(ExtensionType.WebGLSystem, systems$1); + extensions.handleByNamedList(ExtensionType.WebGLPipes, renderPipes$1); + extensions.handleByNamedList(ExtensionType.WebGLPipesAdaptor, renderPipeAdaptors$1); + extensions.add(...DefaultWebGLSystems, ...DefaultWebGLPipes, ...DefaultWebGLAdapters); + class WebGLRenderer extends AbstractRenderer { + constructor() { + const systemConfig = { + name: "webgl", + type: RendererType.WEBGL, + systems: systems$1, + renderPipes: renderPipes$1, + renderPipeAdaptors: renderPipeAdaptors$1 + }; + super(systemConfig); + } + } + + var WebGLRenderer$1 = { + __proto__: null, + WebGLRenderer: WebGLRenderer + }; + + "use strict"; + class BindGroupSystem { + constructor(renderer) { + this._hash = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + } + contextChange(gpu) { + this._gpu = gpu; + } + getBindGroup(bindGroup, program, groupIndex) { + bindGroup._updateKey(); + const gpuBindGroup = this._hash[bindGroup._key] || this._createBindGroup(bindGroup, program, groupIndex); + return gpuBindGroup; + } + _createBindGroup(group, program, groupIndex) { + var _a; + const device = this._gpu.device; + const groupLayout = program.layout[groupIndex]; + const entries = []; + const renderer = this._renderer; + for (const j in groupLayout) { + const resource = (_a = group.resources[j]) != null ? _a : group.resources[groupLayout[j]]; + let gpuResource; + if (resource._resourceType === "uniformGroup") { + const uniformGroup = resource; + renderer.ubo.updateUniformGroup(uniformGroup); + const buffer = uniformGroup.buffer; + gpuResource = { + buffer: renderer.buffer.getGPUBuffer(buffer), + offset: 0, + size: buffer.descriptor.size + }; + } else if (resource._resourceType === "buffer") { + const buffer = resource; + gpuResource = { + buffer: renderer.buffer.getGPUBuffer(buffer), + offset: 0, + size: buffer.descriptor.size + }; + } else if (resource._resourceType === "bufferResource") { + const bufferResource = resource; + gpuResource = { + buffer: renderer.buffer.getGPUBuffer(bufferResource.buffer), + offset: bufferResource.offset, + size: bufferResource.size + }; + } else if (resource._resourceType === "textureSampler") { + const sampler = resource; + gpuResource = renderer.texture.getGpuSampler(sampler); + } else if (resource._resourceType === "textureSource") { + const texture = resource; + gpuResource = renderer.texture.getTextureView(texture); + } + entries.push({ + binding: groupLayout[j], + resource: gpuResource + }); + } + const layout = renderer.shader.getProgramData(program).bindGroups[groupIndex]; + const gpuBindGroup = device.createBindGroup({ + layout, + entries + }); + this._hash[group._key] = gpuBindGroup; + return gpuBindGroup; + } + destroy() { + this._hash = null; + this._renderer = null; + } + } + /** @ignore */ + BindGroupSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "bindGroup" + }; + + "use strict"; + class GpuBufferData { + constructor(gpuBuffer) { + this.gpuBuffer = gpuBuffer; + } + destroy() { + this.gpuBuffer.destroy(); + this.gpuBuffer = null; + } + } + class GpuBufferSystem { + constructor(renderer) { + this._renderer = renderer; + this._managedBuffers = new GCManagedHash({ + renderer, + type: "resource", + onUnload: this.onBufferUnload.bind(this), + name: "gpuBuffer" + }); + } + contextChange(gpu) { + this._gpu = gpu; + } + getGPUBuffer(buffer) { + var _a; + buffer._gcLastUsed = this._renderer.gc.now; + return ((_a = buffer._gpuData[this._renderer.uid]) == null ? void 0 : _a.gpuBuffer) || this.createGPUBuffer(buffer); + } + updateBuffer(buffer) { + const gpuBuffer = this.getGPUBuffer(buffer); + const data = buffer.data; + if (buffer._updateID && data) { + buffer._updateID = 0; + this._gpu.device.queue.writeBuffer( + gpuBuffer, + 0, + data.buffer, + 0, + // round to the nearest 4 bytes + (buffer._updateSize || data.byteLength) + 3 & ~3 + ); + } + return gpuBuffer; + } + /** dispose all WebGL resources of all managed buffers */ + destroyAll() { + this._managedBuffers.removeAll(); + } + onBufferUnload(buffer) { + buffer.off("update", this.updateBuffer, this); + buffer.off("change", this.onBufferChange, this); + } + createGPUBuffer(buffer) { + const gpuBuffer = this._gpu.device.createBuffer(buffer.descriptor); + buffer._updateID = 0; + buffer._resourceId = uid$1("resource"); + if (buffer.data) { + fastCopy( + buffer.data.buffer, + gpuBuffer.getMappedRange(), + buffer.data.byteOffset, + buffer.data.byteLength + ); + gpuBuffer.unmap(); + } + buffer._gpuData[this._renderer.uid] = new GpuBufferData(gpuBuffer); + if (this._managedBuffers.add(buffer)) { + buffer.on("update", this.updateBuffer, this); + buffer.on("change", this.onBufferChange, this); + } + return gpuBuffer; + } + onBufferChange(buffer) { + this._managedBuffers.remove(buffer); + buffer._updateID = 0; + this.createGPUBuffer(buffer); + } + destroy() { + this._managedBuffers.destroy(); + this._renderer = null; + this._gpu = null; + } + } + /** @ignore */ + GpuBufferSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "buffer" + }; + + "use strict"; + class UboBatch { + constructor({ minUniformOffsetAlignment }) { + this._minUniformOffsetAlignment = 256; + this.byteIndex = 0; + this._minUniformOffsetAlignment = minUniformOffsetAlignment; + this.data = new Float32Array(65535); + } + clear() { + this.byteIndex = 0; + } + addEmptyGroup(size) { + if (size > this._minUniformOffsetAlignment / 4) { + throw new Error(`UniformBufferBatch: array is too large: ${size * 4}`); + } + const start = this.byteIndex; + let newSize = start + size * 4; + newSize = Math.ceil(newSize / this._minUniformOffsetAlignment) * this._minUniformOffsetAlignment; + if (newSize > this.data.length * 4) { + throw new Error("UniformBufferBatch: ubo batch got too big"); + } + this.byteIndex = newSize; + return start; + } + addGroup(array) { + const offset = this.addEmptyGroup(array.length); + for (let i = 0; i < array.length; i++) { + this.data[offset / 4 + i] = array[i]; + } + return offset; + } + destroy() { + this.data = null; + } + } + + "use strict"; + class GpuColorMaskSystem { + constructor(renderer) { + this._colorMaskCache = 15; + this._renderer = renderer; + } + setMask(colorMask) { + if (this._colorMaskCache === colorMask) return; + this._colorMaskCache = colorMask; + this._renderer.pipeline.setColorMask(colorMask); + } + destroy() { + this._renderer = null; + this._colorMaskCache = null; + } + } + /** @ignore */ + GpuColorMaskSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "colorMask" + }; + + "use strict"; + class GpuDeviceSystem { + /** + * @param {WebGPURenderer} renderer - The renderer this System works for. + */ + constructor(renderer) { + this._renderer = renderer; + } + async init(options) { + if (this._initPromise) return this._initPromise; + this._initPromise = (options.gpu ? Promise.resolve(options.gpu) : this._createDeviceAndAdaptor(options)).then((gpu) => { + this.gpu = gpu; + this._renderer.runners.contextChange.emit(this.gpu); + }); + return this._initPromise; + } + /** + * Handle the context change event + * @param gpu + */ + contextChange(gpu) { + this._renderer.gpu = gpu; + } + /** + * Helper class to create a WebGL Context + * @param {object} options - An options object that gets passed in to the canvas element containing the + * context attributes + * @see https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/getContext + * @returns {WebGLRenderingContext} the WebGL context + */ + async _createDeviceAndAdaptor(options) { + const adapter = await DOMAdapter.get().getNavigator().gpu.requestAdapter({ + powerPreference: options.powerPreference, + forceFallbackAdapter: options.forceFallbackAdapter + }); + const requiredFeatures = [ + "texture-compression-bc", + "texture-compression-astc", + "texture-compression-etc2" + ].filter((feature) => adapter.features.has(feature)); + const device = await adapter.requestDevice({ + requiredFeatures + }); + return { adapter, device }; + } + destroy() { + this.gpu = null; + this._renderer = null; + } + } + /** @ignore */ + GpuDeviceSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "device" + }; + /** The default options for the GpuDeviceSystem. */ + GpuDeviceSystem.defaultOptions = { + /** + * {@link WebGPUOptions.powerPreference} + * @default default + */ + powerPreference: void 0, + /** + * Force the use of the fallback adapter + * @default false + */ + forceFallbackAdapter: false + }; + + "use strict"; + var __defProp$j = Object.defineProperty; + var __getOwnPropSymbols$k = Object.getOwnPropertySymbols; + var __hasOwnProp$k = Object.prototype.hasOwnProperty; + var __propIsEnum$k = Object.prototype.propertyIsEnumerable; + var __defNormalProp$j = (obj, key, value) => key in obj ? __defProp$j(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$j = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$k.call(b, prop)) + __defNormalProp$j(a, prop, b[prop]); + if (__getOwnPropSymbols$k) + for (var prop of __getOwnPropSymbols$k(b)) { + if (__propIsEnum$k.call(b, prop)) + __defNormalProp$j(a, prop, b[prop]); + } + return a; + }; + class GpuEncoderSystem { + constructor(renderer) { + this._boundBindGroup = /* @__PURE__ */ Object.create(null); + this._boundVertexBuffer = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + } + renderStart() { + this.commandFinished = new Promise((resolve) => { + this._resolveCommandFinished = resolve; + }); + this.commandEncoder = this._renderer.gpu.device.createCommandEncoder(); + } + beginRenderPass(gpuRenderTarget) { + this.endRenderPass(); + this._clearCache(); + this.renderPassEncoder = this.commandEncoder.beginRenderPass(gpuRenderTarget.descriptor); + } + endRenderPass() { + if (this.renderPassEncoder) { + this.renderPassEncoder.end(); + } + this.renderPassEncoder = null; + } + setViewport(viewport) { + this.renderPassEncoder.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1); + } + setPipelineFromGeometryProgramAndState(geometry, program, state, topology) { + const pipeline = this._renderer.pipeline.getPipeline(geometry, program, state, topology); + this.setPipeline(pipeline); + } + setPipeline(pipeline) { + if (this._boundPipeline === pipeline) return; + this._boundPipeline = pipeline; + this.renderPassEncoder.setPipeline(pipeline); + } + _setVertexBuffer(index, buffer) { + if (this._boundVertexBuffer[index] === buffer) return; + this._boundVertexBuffer[index] = buffer; + this.renderPassEncoder.setVertexBuffer(index, this._renderer.buffer.updateBuffer(buffer)); + } + _setIndexBuffer(buffer) { + if (this._boundIndexBuffer === buffer) return; + this._boundIndexBuffer = buffer; + const indexFormat = buffer.data.BYTES_PER_ELEMENT === 2 ? "uint16" : "uint32"; + this.renderPassEncoder.setIndexBuffer(this._renderer.buffer.updateBuffer(buffer), indexFormat); + } + resetBindGroup(index) { + this._boundBindGroup[index] = null; + } + setBindGroup(index, bindGroup, program) { + if (this._boundBindGroup[index] === bindGroup) return; + this._boundBindGroup[index] = bindGroup; + bindGroup._touch(this._renderer.gc.now, this._renderer.tick); + const gpuBindGroup = this._renderer.bindGroup.getBindGroup(bindGroup, program, index); + this.renderPassEncoder.setBindGroup(index, gpuBindGroup); + } + setGeometry(geometry, program) { + const buffersToBind = this._renderer.pipeline.getBufferNamesToBind(geometry, program); + for (const i in buffersToBind) { + this._setVertexBuffer(parseInt(i, 10), geometry.attributes[buffersToBind[i]].buffer); + } + if (geometry.indexBuffer) { + this._setIndexBuffer(geometry.indexBuffer); + } + } + _setShaderBindGroups(shader, skipSync) { + for (const i in shader.groups) { + const bindGroup = shader.groups[i]; + if (!skipSync) { + this._syncBindGroup(bindGroup); + } + this.setBindGroup(i, bindGroup, shader.gpuProgram); + } + } + _syncBindGroup(bindGroup) { + for (const j in bindGroup.resources) { + const resource = bindGroup.resources[j]; + if (resource.isUniformGroup) { + this._renderer.ubo.updateUniformGroup(resource); + } + } + } + draw(options) { + const { geometry, shader, state, topology, size, start, instanceCount, skipSync } = options; + this.setPipelineFromGeometryProgramAndState(geometry, shader.gpuProgram, state, topology); + this.setGeometry(geometry, shader.gpuProgram); + this._setShaderBindGroups(shader, skipSync); + if (geometry.indexBuffer) { + this.renderPassEncoder.drawIndexed( + size || geometry.indexBuffer.data.length, + instanceCount != null ? instanceCount : geometry.instanceCount, + start || 0 + ); + } else { + this.renderPassEncoder.draw(size || geometry.getSize(), instanceCount != null ? instanceCount : geometry.instanceCount, start || 0); + } + } + finishRenderPass() { + if (this.renderPassEncoder) { + this.renderPassEncoder.end(); + this.renderPassEncoder = null; + } + } + postrender() { + this.finishRenderPass(); + this._gpu.device.queue.submit([this.commandEncoder.finish()]); + this._resolveCommandFinished(); + this.commandEncoder = null; + } + // restores a render pass if finishRenderPass was called + // not optimised as really used for debugging! + // used when we want to stop drawing and log a texture.. + restoreRenderPass() { + const descriptor = this._renderer.renderTarget.adaptor.getDescriptor( + this._renderer.renderTarget.renderTarget, + false, + [0, 0, 0, 1], + this._renderer.renderTarget.mipLevel, + this._renderer.renderTarget.layer + ); + this.renderPassEncoder = this.commandEncoder.beginRenderPass(descriptor); + const boundPipeline = this._boundPipeline; + const boundVertexBuffer = __spreadValues$j({}, this._boundVertexBuffer); + const boundIndexBuffer = this._boundIndexBuffer; + const boundBindGroup = __spreadValues$j({}, this._boundBindGroup); + this._clearCache(); + const viewport = this._renderer.renderTarget.viewport; + this.renderPassEncoder.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1); + this.setPipeline(boundPipeline); + for (const i in boundVertexBuffer) { + this._setVertexBuffer(i, boundVertexBuffer[i]); + } + for (const i in boundBindGroup) { + this.setBindGroup(i, boundBindGroup[i], null); + } + this._setIndexBuffer(boundIndexBuffer); + } + _clearCache() { + for (let i = 0; i < 16; i++) { + this._boundBindGroup[i] = null; + this._boundVertexBuffer[i] = null; + } + this._boundIndexBuffer = null; + this._boundPipeline = null; + } + destroy() { + this._renderer = null; + this._gpu = null; + this._boundBindGroup = null; + this._boundVertexBuffer = null; + this._boundIndexBuffer = null; + this._boundPipeline = null; + } + contextChange(gpu) { + this._gpu = gpu; + } + } + /** @ignore */ + GpuEncoderSystem.extension = { + type: [ExtensionType.WebGPUSystem], + name: "encoder", + priority: 1 + }; + + "use strict"; + class GpuLimitsSystem { + constructor(renderer) { + this._renderer = renderer; + } + contextChange() { + this.maxTextures = this._renderer.device.gpu.device.limits.maxSampledTexturesPerShaderStage; + this.maxBatchableTextures = this.maxTextures; + } + destroy() { + } + } + /** @ignore */ + GpuLimitsSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "limits" + }; + + "use strict"; + class GpuStencilSystem { + constructor(renderer) { + this._renderTargetStencilState = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + renderer.renderTarget.onRenderTargetChange.add(this); + } + onRenderTargetChange(renderTarget) { + let stencilState = this._renderTargetStencilState[renderTarget.uid]; + if (!stencilState) { + stencilState = this._renderTargetStencilState[renderTarget.uid] = { + stencilMode: STENCIL_MODES.DISABLED, + stencilReference: 0 + }; + } + this._activeRenderTarget = renderTarget; + this.setStencilMode(stencilState.stencilMode, stencilState.stencilReference); + } + setStencilMode(stencilMode, stencilReference) { + const stencilState = this._renderTargetStencilState[this._activeRenderTarget.uid]; + stencilState.stencilMode = stencilMode; + stencilState.stencilReference = stencilReference; + const renderer = this._renderer; + renderer.pipeline.setStencilMode(stencilMode); + renderer.encoder.renderPassEncoder.setStencilReference(stencilReference); + } + destroy() { + this._renderer.renderTarget.onRenderTargetChange.remove(this); + this._renderer = null; + this._activeRenderTarget = null; + this._renderTargetStencilState = null; + } + } + /** @ignore */ + GpuStencilSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "stencil" + }; + + "use strict"; + const WGSL_ALIGN_SIZE_DATA = { + i32: { align: 4, size: 4 }, + u32: { align: 4, size: 4 }, + f32: { align: 4, size: 4 }, + f16: { align: 2, size: 2 }, + "vec2": { align: 8, size: 8 }, + "vec2": { align: 8, size: 8 }, + "vec2": { align: 8, size: 8 }, + "vec2": { align: 4, size: 4 }, + "vec3": { align: 16, size: 12 }, + "vec3": { align: 16, size: 12 }, + "vec3": { align: 16, size: 12 }, + "vec3": { align: 8, size: 6 }, + "vec4": { align: 16, size: 16 }, + "vec4": { align: 16, size: 16 }, + "vec4": { align: 16, size: 16 }, + "vec4": { align: 8, size: 8 }, + "mat2x2": { align: 8, size: 16 }, + "mat2x2": { align: 4, size: 8 }, + "mat3x2": { align: 8, size: 24 }, + "mat3x2": { align: 4, size: 12 }, + "mat4x2": { align: 8, size: 32 }, + "mat4x2": { align: 4, size: 16 }, + "mat2x3": { align: 16, size: 32 }, + "mat2x3": { align: 8, size: 16 }, + "mat3x3": { align: 16, size: 48 }, + "mat3x3": { align: 8, size: 24 }, + "mat4x3": { align: 16, size: 64 }, + "mat4x3": { align: 8, size: 32 }, + "mat2x4": { align: 16, size: 32 }, + "mat2x4": { align: 8, size: 16 }, + "mat3x4": { align: 16, size: 48 }, + "mat3x4": { align: 8, size: 24 }, + "mat4x4": { align: 16, size: 64 }, + "mat4x4": { align: 8, size: 32 } + }; + function createUboElementsWGSL(uniformData) { + const uboElements = uniformData.map((data) => ({ + data, + offset: 0, + size: 0 + })); + let offset = 0; + for (let i = 0; i < uboElements.length; i++) { + const uboElement = uboElements[i]; + let size = WGSL_ALIGN_SIZE_DATA[uboElement.data.type].size; + const align = WGSL_ALIGN_SIZE_DATA[uboElement.data.type].align; + if (!WGSL_ALIGN_SIZE_DATA[uboElement.data.type]) { + throw new Error(`[Pixi.js] WebGPU UniformBuffer: Unknown type ${uboElement.data.type}`); + } + if (uboElement.data.size > 1) { + size = Math.max(size, align) * uboElement.data.size; + } + offset = Math.ceil(offset / align) * align; + uboElement.size = size; + uboElement.offset = offset; + offset += size; + } + offset = Math.ceil(offset / 16) * 16; + return { uboElements, size: offset }; + } + + "use strict"; + function generateArraySyncWGSL(uboElement, offsetToAdd) { + const { size, align } = WGSL_ALIGN_SIZE_DATA[uboElement.data.type]; + const remainder = (align - size) / 4; + const data = uboElement.data.type.indexOf("i32") >= 0 ? "dataInt32" : "data"; + return ` + v = uv.${uboElement.data.name}; + ${offsetToAdd !== 0 ? `offset += ${offsetToAdd};` : ""} + + arrayOffset = offset; + + t = 0; + + for(var i=0; i < ${uboElement.data.size * (size / 4)}; i++) + { + for(var j = 0; j < ${size / 4}; j++) + { + ${data}[arrayOffset++] = v[t++]; + } + ${remainder !== 0 ? `arrayOffset += ${remainder};` : ""} + } + `; + } + + "use strict"; + function createUboSyncFunctionWGSL(uboElements) { + return createUboSyncFunction( + uboElements, + "uboWgsl", + generateArraySyncWGSL, + uboSyncFunctionsWGSL + ); + } + + "use strict"; + class GpuUboSystem extends UboSystem { + constructor() { + super({ + createUboElements: createUboElementsWGSL, + generateUboSync: createUboSyncFunctionWGSL + }); + } + } + /** @ignore */ + GpuUboSystem.extension = { + type: [ExtensionType.WebGPUSystem], + name: "ubo" + }; + + "use strict"; + const minUniformOffsetAlignment = 128; + class GpuUniformBatchPipe { + constructor(renderer) { + this._bindGroupHash = /* @__PURE__ */ Object.create(null); + // number of buffers.. + this._buffers = []; + this._bindGroups = []; + this._bufferResources = []; + this._renderer = renderer; + this._batchBuffer = new UboBatch({ minUniformOffsetAlignment }); + const totalBuffers = 256 / minUniformOffsetAlignment; + for (let i = 0; i < totalBuffers; i++) { + let usage = BufferUsage.UNIFORM | BufferUsage.COPY_DST; + if (i === 0) usage |= BufferUsage.COPY_SRC; + this._buffers.push(new Buffer({ + data: this._batchBuffer.data, + usage + })); + } + } + renderEnd() { + this._uploadBindGroups(); + this._resetBindGroups(); + } + _resetBindGroups() { + this._bindGroupHash = /* @__PURE__ */ Object.create(null); + this._batchBuffer.clear(); + } + // just works for single bind groups for now + getUniformBindGroup(group, duplicate) { + if (!duplicate && this._bindGroupHash[group.uid]) { + return this._bindGroupHash[group.uid]; + } + this._renderer.ubo.ensureUniformGroup(group); + const data = group.buffer.data; + const offset = this._batchBuffer.addEmptyGroup(data.length); + this._renderer.ubo.syncUniformGroup(group, this._batchBuffer.data, offset / 4); + this._bindGroupHash[group.uid] = this._getBindGroup(offset / minUniformOffsetAlignment); + return this._bindGroupHash[group.uid]; + } + getUboResource(group) { + this._renderer.ubo.updateUniformGroup(group); + const data = group.buffer.data; + const offset = this._batchBuffer.addGroup(data); + return this._getBufferResource(offset / minUniformOffsetAlignment); + } + getArrayBindGroup(data) { + const offset = this._batchBuffer.addGroup(data); + return this._getBindGroup(offset / minUniformOffsetAlignment); + } + getArrayBufferResource(data) { + const offset = this._batchBuffer.addGroup(data); + const index = offset / minUniformOffsetAlignment; + return this._getBufferResource(index); + } + _getBufferResource(index) { + if (!this._bufferResources[index]) { + const buffer = this._buffers[index % 2]; + this._bufferResources[index] = new BufferResource({ + buffer, + offset: (index / 2 | 0) * 256, + size: minUniformOffsetAlignment + }); + } + return this._bufferResources[index]; + } + _getBindGroup(index) { + if (!this._bindGroups[index]) { + const bindGroup = new BindGroup({ + 0: this._getBufferResource(index) + }); + this._bindGroups[index] = bindGroup; + } + return this._bindGroups[index]; + } + _uploadBindGroups() { + const bufferSystem = this._renderer.buffer; + const firstBuffer = this._buffers[0]; + firstBuffer.update(this._batchBuffer.byteIndex); + bufferSystem.updateBuffer(firstBuffer); + const commandEncoder = this._renderer.gpu.device.createCommandEncoder(); + for (let i = 1; i < this._buffers.length; i++) { + const buffer = this._buffers[i]; + commandEncoder.copyBufferToBuffer( + bufferSystem.getGPUBuffer(firstBuffer), + minUniformOffsetAlignment, + bufferSystem.getGPUBuffer(buffer), + 0, + this._batchBuffer.byteIndex + ); + } + this._renderer.gpu.device.queue.submit([commandEncoder.finish()]); + } + destroy() { + var _a; + for (let i = 0; i < this._bindGroups.length; i++) { + (_a = this._bindGroups[i]) == null ? void 0 : _a.destroy(); + } + this._bindGroups = null; + this._bindGroupHash = null; + for (let i = 0; i < this._buffers.length; i++) { + this._buffers[i].destroy(); + } + this._buffers = null; + for (let i = 0; i < this._bufferResources.length; i++) { + this._bufferResources[i].destroy(); + } + this._bufferResources = null; + this._batchBuffer.destroy(); + this._renderer = null; + } + } + /** @ignore */ + GpuUniformBatchPipe.extension = { + type: [ + ExtensionType.WebGPUPipes + ], + name: "uniformBatch" + }; + + "use strict"; + var __defProp$i = Object.defineProperty; + var __defProps$b = Object.defineProperties; + var __getOwnPropDescs$b = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$j = Object.getOwnPropertySymbols; + var __hasOwnProp$j = Object.prototype.hasOwnProperty; + var __propIsEnum$j = Object.prototype.propertyIsEnumerable; + var __defNormalProp$i = (obj, key, value) => key in obj ? __defProp$i(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$i = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$j.call(b, prop)) + __defNormalProp$i(a, prop, b[prop]); + if (__getOwnPropSymbols$j) + for (var prop of __getOwnPropSymbols$j(b)) { + if (__propIsEnum$j.call(b, prop)) + __defNormalProp$i(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$b = (a, b) => __defProps$b(a, __getOwnPropDescs$b(b)); + const topologyStringToId = { + "point-list": 0, + "line-list": 1, + "line-strip": 2, + "triangle-list": 3, + "triangle-strip": 4 + }; + function getGraphicsStateKey(geometryLayout, shaderKey, state, blendMode, topology) { + return geometryLayout << 24 | shaderKey << 16 | state << 10 | blendMode << 5 | topology; + } + function getGlobalStateKey(stencilStateId, multiSampleCount, colorMask, renderTarget, colorTargetCount) { + return colorMask << 8 | stencilStateId << 5 | renderTarget << 3 | colorTargetCount << 1 | multiSampleCount; + } + class PipelineSystem { + constructor(renderer) { + this._moduleCache = /* @__PURE__ */ Object.create(null); + this._bufferLayoutsCache = /* @__PURE__ */ Object.create(null); + this._bindingNamesCache = /* @__PURE__ */ Object.create(null); + this._pipeCache = /* @__PURE__ */ Object.create(null); + this._pipeStateCaches = /* @__PURE__ */ Object.create(null); + this._colorMask = 15; + this._multisampleCount = 1; + this._colorTargetCount = 1; + this._renderer = renderer; + } + contextChange(gpu) { + this._gpu = gpu; + this.setStencilMode(STENCIL_MODES.DISABLED); + this._updatePipeHash(); + } + setMultisampleCount(multisampleCount) { + if (this._multisampleCount === multisampleCount) return; + this._multisampleCount = multisampleCount; + this._updatePipeHash(); + } + setRenderTarget(renderTarget) { + this._multisampleCount = renderTarget.msaaSamples; + this._depthStencilAttachment = renderTarget.descriptor.depthStencilAttachment ? 1 : 0; + this._colorTargetCount = renderTarget.colorTargetCount; + this._updatePipeHash(); + } + setColorMask(colorMask) { + if (this._colorMask === colorMask) return; + this._colorMask = colorMask; + this._updatePipeHash(); + } + setStencilMode(stencilMode) { + if (this._stencilMode === stencilMode) return; + this._stencilMode = stencilMode; + this._stencilState = GpuStencilModesToPixi[stencilMode]; + this._updatePipeHash(); + } + setPipeline(geometry, program, state, passEncoder) { + const pipeline = this.getPipeline(geometry, program, state); + passEncoder.setPipeline(pipeline); + } + getPipeline(geometry, program, state, topology) { + if (!geometry._layoutKey) { + ensureAttributes(geometry, program.attributeData); + this._generateBufferKey(geometry); + } + topology || (topology = geometry.topology); + const key = getGraphicsStateKey( + geometry._layoutKey, + program._layoutKey, + state.data, + state._blendModeId, + topologyStringToId[topology] + ); + if (this._pipeCache[key]) return this._pipeCache[key]; + this._pipeCache[key] = this._createPipeline(geometry, program, state, topology); + return this._pipeCache[key]; + } + _createPipeline(geometry, program, state, topology) { + const device = this._gpu.device; + const buffers = this._createVertexBufferLayouts(geometry, program); + const blendModes = this._renderer.state.getColorTargets(state, this._colorTargetCount); + const writeMask = this._stencilMode === STENCIL_MODES.RENDERING_MASK_ADD ? 0 : this._colorMask; + for (let i = 0; i < blendModes.length; i++) { + blendModes[i].writeMask = writeMask; + } + const layout = this._renderer.shader.getProgramData(program).pipeline; + const descriptor = { + // TODO later check if its helpful to create.. + // layout, + vertex: { + module: this._getModule(program.vertex.source), + entryPoint: program.vertex.entryPoint, + // geometry.. + buffers + }, + fragment: { + module: this._getModule(program.fragment.source), + entryPoint: program.fragment.entryPoint, + targets: blendModes + }, + primitive: { + topology, + cullMode: state.cullMode + }, + layout, + multisample: { + count: this._multisampleCount + }, + // depthStencil, + label: `PIXI Pipeline` + }; + if (this._depthStencilAttachment) { + descriptor.depthStencil = __spreadProps$b(__spreadValues$i({}, this._stencilState), { + format: "depth24plus-stencil8", + depthWriteEnabled: state.depthTest, + depthCompare: state.depthTest ? "less" : "always" + }); + } + const pipeline = device.createRenderPipeline(descriptor); + return pipeline; + } + _getModule(code) { + return this._moduleCache[code] || this._createModule(code); + } + _createModule(code) { + const device = this._gpu.device; + this._moduleCache[code] = device.createShaderModule({ + code + }); + return this._moduleCache[code]; + } + _generateBufferKey(geometry) { + const keyGen = []; + let index = 0; + const attributeKeys = Object.keys(geometry.attributes).sort(); + for (let i = 0; i < attributeKeys.length; i++) { + const attribute = geometry.attributes[attributeKeys[i]]; + keyGen[index++] = attribute.offset; + keyGen[index++] = attribute.format; + keyGen[index++] = attribute.stride; + keyGen[index++] = attribute.instance; + } + const stringKey = keyGen.join("|"); + geometry._layoutKey = createIdFromString(stringKey, "geometry"); + return geometry._layoutKey; + } + _generateAttributeLocationsKey(program) { + const keyGen = []; + let index = 0; + const attributeKeys = Object.keys(program.attributeData).sort(); + for (let i = 0; i < attributeKeys.length; i++) { + const attribute = program.attributeData[attributeKeys[i]]; + keyGen[index++] = attribute.location; + } + const stringKey = keyGen.join("|"); + program._attributeLocationsKey = createIdFromString(stringKey, "programAttributes"); + return program._attributeLocationsKey; + } + /** + * Returns a hash of buffer names mapped to bind locations. + * This is used to bind the correct buffer to the correct location in the shader. + * @param geometry - The geometry where to get the buffer names + * @param program - The program where to get the buffer names + * @returns An object of buffer names mapped to the bind location. + */ + getBufferNamesToBind(geometry, program) { + const key = geometry._layoutKey << 16 | program._attributeLocationsKey; + if (this._bindingNamesCache[key]) return this._bindingNamesCache[key]; + const data = this._createVertexBufferLayouts(geometry, program); + const bufferNamesToBind = /* @__PURE__ */ Object.create(null); + const attributeData = program.attributeData; + for (let i = 0; i < data.length; i++) { + const attributes = Object.values(data[i].attributes); + const shaderLocation = attributes[0].shaderLocation; + for (const j in attributeData) { + if (attributeData[j].location === shaderLocation) { + bufferNamesToBind[i] = j; + break; + } + } + } + this._bindingNamesCache[key] = bufferNamesToBind; + return bufferNamesToBind; + } + _createVertexBufferLayouts(geometry, program) { + if (!program._attributeLocationsKey) this._generateAttributeLocationsKey(program); + const key = geometry._layoutKey << 16 | program._attributeLocationsKey; + if (this._bufferLayoutsCache[key]) { + return this._bufferLayoutsCache[key]; + } + const vertexBuffersLayout = []; + geometry.buffers.forEach((buffer) => { + var _a; + const bufferEntry = { + arrayStride: 0, + stepMode: "vertex", + attributes: [] + }; + const bufferEntryAttributes = bufferEntry.attributes; + for (const i in program.attributeData) { + const attribute = geometry.attributes[i]; + if (((_a = attribute.divisor) != null ? _a : 1) !== 1) { + warn(`Attribute ${i} has an invalid divisor value of '${attribute.divisor}'. WebGPU only supports a divisor value of 1`); + } + if (attribute.buffer === buffer) { + bufferEntry.arrayStride = attribute.stride; + bufferEntry.stepMode = attribute.instance ? "instance" : "vertex"; + bufferEntryAttributes.push({ + shaderLocation: program.attributeData[i].location, + offset: attribute.offset, + format: attribute.format + }); + } + } + if (bufferEntryAttributes.length) { + vertexBuffersLayout.push(bufferEntry); + } + }); + this._bufferLayoutsCache[key] = vertexBuffersLayout; + return vertexBuffersLayout; + } + _updatePipeHash() { + const key = getGlobalStateKey( + this._stencilMode, + this._multisampleCount, + this._colorMask, + this._depthStencilAttachment, + this._colorTargetCount + ); + if (!this._pipeStateCaches[key]) { + this._pipeStateCaches[key] = /* @__PURE__ */ Object.create(null); + } + this._pipeCache = this._pipeStateCaches[key]; + } + destroy() { + this._renderer = null; + this._bufferLayoutsCache = null; + } + } + /** @ignore */ + PipelineSystem.extension = { + type: [ExtensionType.WebGPUSystem], + name: "pipeline" + }; + + "use strict"; + class GpuRenderTarget { + constructor() { + this.contexts = []; + this.msaaTextures = []; + this.msaaSamples = 1; + } + } + + "use strict"; + class GpuRenderTargetAdaptor { + init(renderer, renderTargetSystem) { + this._renderer = renderer; + this._renderTargetSystem = renderTargetSystem; + } + copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) { + const renderer = this._renderer; + const baseGpuTexture = this._getGpuColorTexture( + sourceRenderSurfaceTexture + ); + const backGpuTexture = renderer.texture.getGpuSource( + destinationTexture.source + ); + renderer.encoder.commandEncoder.copyTextureToTexture( + { + texture: baseGpuTexture, + origin: originSrc + }, + { + texture: backGpuTexture, + origin: originDest + }, + size + ); + return destinationTexture; + } + startRenderPass(renderTarget, clear = true, clearColor, viewport, mipLevel = 0, layer = 0) { + var _a, _b; + const renderTargetSystem = this._renderTargetSystem; + const gpuRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget); + if (layer !== 0 && ((_a = gpuRenderTarget.msaaTextures) == null ? void 0 : _a.length)) { + throw new Error("[RenderTargetSystem] Rendering to array layers is not supported with MSAA render targets."); + } + if (mipLevel > 0 && ((_b = gpuRenderTarget.msaaTextures) == null ? void 0 : _b.length)) { + throw new Error("[RenderTargetSystem] Rendering to mip levels is not supported with MSAA render targets."); + } + const descriptor = this.getDescriptor(renderTarget, clear, clearColor, mipLevel, layer); + gpuRenderTarget.descriptor = descriptor; + this._renderer.pipeline.setRenderTarget(gpuRenderTarget); + this._renderer.encoder.beginRenderPass(gpuRenderTarget); + this._renderer.encoder.setViewport(viewport); + } + finishRenderPass() { + this._renderer.encoder.endRenderPass(); + } + /** + * returns the gpu texture for the first color texture in the render target + * mainly used by the filter manager to get copy the texture for blending + * @param renderTarget + * @returns a gpu texture + */ + _getGpuColorTexture(renderTarget) { + const gpuRenderTarget = this._renderTargetSystem.getGpuRenderTarget(renderTarget); + if (gpuRenderTarget.contexts[0]) { + return gpuRenderTarget.contexts[0].getCurrentTexture(); + } + return this._renderer.texture.getGpuSource( + renderTarget.colorTextures[0].source + ); + } + getDescriptor(renderTarget, clear, clearValue, mipLevel = 0, layer = 0) { + if (typeof clear === "boolean") { + clear = clear ? CLEAR.ALL : CLEAR.NONE; + } + const renderTargetSystem = this._renderTargetSystem; + const gpuRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget); + const colorAttachments = renderTarget.colorTextures.map( + (texture, i) => { + const context = gpuRenderTarget.contexts[i]; + let view; + let resolveTarget; + if (context) { + if (layer !== 0) { + throw new Error("[RenderTargetSystem] Rendering to array layers is not supported for canvas targets."); + } + const currentTexture = context.getCurrentTexture(); + const canvasTextureView = currentTexture.createView(); + view = canvasTextureView; + } else { + view = this._renderer.texture.getGpuSource(texture).createView({ + // Render attachments must be 2d views; for array/cube textures we select a single layer. + dimension: "2d", + baseMipLevel: mipLevel, + mipLevelCount: 1, + baseArrayLayer: layer, + arrayLayerCount: 1 + }); + } + if (gpuRenderTarget.msaaTextures[i]) { + resolveTarget = view; + view = this._renderer.texture.getTextureView( + gpuRenderTarget.msaaTextures[i] + ); + } + const loadOp = clear & CLEAR.COLOR ? "clear" : "load"; + clearValue != null ? clearValue : clearValue = renderTargetSystem.defaultClearColor; + return { + view, + resolveTarget, + clearValue, + storeOp: "store", + loadOp + }; + } + ); + let depthStencilAttachment; + if ((renderTarget.stencil || renderTarget.depth) && !renderTarget.depthStencilTexture) { + renderTarget.ensureDepthStencilTexture(); + renderTarget.depthStencilTexture.source.sampleCount = gpuRenderTarget.msaa ? 4 : 1; + } + if (renderTarget.depthStencilTexture) { + const stencilLoadOp = clear & CLEAR.STENCIL ? "clear" : "load"; + const depthLoadOp = clear & CLEAR.DEPTH ? "clear" : "load"; + depthStencilAttachment = { + view: this._renderer.texture.getGpuSource(renderTarget.depthStencilTexture.source).createView({ + dimension: "2d", + baseMipLevel: mipLevel, + mipLevelCount: 1, + baseArrayLayer: layer, + arrayLayerCount: 1 + }), + stencilStoreOp: "store", + stencilLoadOp, + depthClearValue: 1, + depthLoadOp, + depthStoreOp: "store" + }; + } + const descriptor = { + colorAttachments, + depthStencilAttachment + }; + return descriptor; + } + clear(renderTarget, clear = true, clearColor, viewport, mipLevel = 0, layer = 0) { + if (!clear) return; + const { gpu, encoder } = this._renderer; + const device = gpu.device; + const standAlone = encoder.commandEncoder === null; + if (standAlone) { + const commandEncoder = device.createCommandEncoder(); + const renderPassDescriptor = this.getDescriptor(renderTarget, clear, clearColor, mipLevel, layer); + const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); + passEncoder.setViewport(viewport.x, viewport.y, viewport.width, viewport.height, 0, 1); + passEncoder.end(); + const gpuCommands = commandEncoder.finish(); + device.queue.submit([gpuCommands]); + } else { + this.startRenderPass(renderTarget, clear, clearColor, viewport, mipLevel, layer); + } + } + initGpuRenderTarget(renderTarget) { + renderTarget.isRoot = true; + const gpuRenderTarget = new GpuRenderTarget(); + gpuRenderTarget.colorTargetCount = renderTarget.colorTextures.length; + renderTarget.colorTextures.forEach((colorTexture, i) => { + if (colorTexture instanceof CanvasSource) { + const context = colorTexture.resource.getContext( + "webgpu" + ); + const alphaMode = colorTexture.transparent ? "premultiplied" : "opaque"; + try { + context.configure({ + device: this._renderer.gpu.device, + usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + format: "bgra8unorm", + alphaMode + }); + } catch (e) { + console.error(e); + } + gpuRenderTarget.contexts[i] = context; + } + gpuRenderTarget.msaa = colorTexture.source.antialias; + if (colorTexture.source.antialias) { + const msaaTexture = new TextureSource({ + width: 0, + height: 0, + sampleCount: 4, + arrayLayerCount: colorTexture.source.arrayLayerCount + }); + gpuRenderTarget.msaaTextures[i] = msaaTexture; + } + }); + if (gpuRenderTarget.msaa) { + gpuRenderTarget.msaaSamples = 4; + if (renderTarget.depthStencilTexture) { + renderTarget.depthStencilTexture.source.sampleCount = 4; + } + } + return gpuRenderTarget; + } + destroyGpuRenderTarget(gpuRenderTarget) { + gpuRenderTarget.contexts.forEach((context) => { + context.unconfigure(); + }); + gpuRenderTarget.msaaTextures.forEach((texture) => { + texture.destroy(); + }); + gpuRenderTarget.msaaTextures.length = 0; + gpuRenderTarget.contexts.length = 0; + } + ensureDepthStencilTexture(renderTarget) { + const gpuRenderTarget = this._renderTargetSystem.getGpuRenderTarget(renderTarget); + if (renderTarget.depthStencilTexture && gpuRenderTarget.msaa) { + renderTarget.depthStencilTexture.source.sampleCount = 4; + } + } + resizeGpuRenderTarget(renderTarget) { + const gpuRenderTarget = this._renderTargetSystem.getGpuRenderTarget(renderTarget); + gpuRenderTarget.width = renderTarget.width; + gpuRenderTarget.height = renderTarget.height; + if (gpuRenderTarget.msaa) { + renderTarget.colorTextures.forEach((colorTexture, i) => { + const msaaTexture = gpuRenderTarget.msaaTextures[i]; + msaaTexture == null ? void 0 : msaaTexture.resize( + colorTexture.source.width, + colorTexture.source.height, + colorTexture.source._resolution + ); + }); + } + } + } + + "use strict"; + class GpuRenderTargetSystem extends RenderTargetSystem { + constructor(renderer) { + super(renderer); + this.adaptor = new GpuRenderTargetAdaptor(); + this.adaptor.init(renderer, this); + } + } + /** @ignore */ + GpuRenderTargetSystem.extension = { + type: [ExtensionType.WebGPUSystem], + name: "renderTarget" + }; + + "use strict"; + + "use strict"; + class GpuShaderSystem { + constructor() { + this._gpuProgramData = /* @__PURE__ */ Object.create(null); + } + contextChange(gpu) { + this._gpu = gpu; + } + getProgramData(program) { + return this._gpuProgramData[program._layoutKey] || this._createGPUProgramData(program); + } + _createGPUProgramData(program) { + const device = this._gpu.device; + const bindGroups = program.gpuLayout.map((group) => device.createBindGroupLayout({ entries: group })); + const pipelineLayoutDesc = { bindGroupLayouts: bindGroups }; + this._gpuProgramData[program._layoutKey] = { + bindGroups, + pipeline: device.createPipelineLayout(pipelineLayoutDesc) + }; + return this._gpuProgramData[program._layoutKey]; + } + destroy() { + this._gpu = null; + this._gpuProgramData = null; + } + } + /** @ignore */ + GpuShaderSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "shader" + }; + + "use strict"; + const GpuBlendModesToPixi = {}; + GpuBlendModesToPixi.normal = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + } + }; + GpuBlendModesToPixi.add = { + alpha: { + srcFactor: "src-alpha", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "one", + dstFactor: "one", + operation: "add" + } + }; + GpuBlendModesToPixi.multiply = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "dst", + dstFactor: "one-minus-src-alpha", + operation: "add" + } + }; + GpuBlendModesToPixi.screen = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "one", + dstFactor: "one-minus-src", + operation: "add" + } + }; + GpuBlendModesToPixi.overlay = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "one", + dstFactor: "one-minus-src", + operation: "add" + } + }; + GpuBlendModesToPixi.none = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "zero", + dstFactor: "zero", + operation: "add" + } + }; + GpuBlendModesToPixi["normal-npm"] = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "src-alpha", + dstFactor: "one-minus-src-alpha", + operation: "add" + } + }; + GpuBlendModesToPixi["add-npm"] = { + alpha: { + srcFactor: "one", + dstFactor: "one", + operation: "add" + }, + color: { + srcFactor: "src-alpha", + dstFactor: "one", + operation: "add" + } + }; + GpuBlendModesToPixi["screen-npm"] = { + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "src-alpha", + dstFactor: "one-minus-src", + operation: "add" + } + }; + GpuBlendModesToPixi.erase = { + alpha: { + srcFactor: "zero", + dstFactor: "one-minus-src-alpha", + operation: "add" + }, + color: { + srcFactor: "zero", + dstFactor: "one-minus-src", + operation: "add" + } + }; + GpuBlendModesToPixi.min = { + alpha: { + srcFactor: "one", + dstFactor: "one", + operation: "min" + }, + color: { + srcFactor: "one", + dstFactor: "one", + operation: "min" + } + }; + GpuBlendModesToPixi.max = { + alpha: { + srcFactor: "one", + dstFactor: "one", + operation: "max" + }, + color: { + srcFactor: "one", + dstFactor: "one", + operation: "max" + } + }; + + "use strict"; + class GpuStateSystem { + constructor() { + this.defaultState = new State(); + this.defaultState.blend = true; + } + contextChange(gpu) { + this.gpu = gpu; + } + /** + * Gets the blend mode data for the current state + * @param state - The state to get the blend mode from + * @param count - The number of color targets to create + */ + getColorTargets(state, count) { + const blend = GpuBlendModesToPixi[state.blendMode] || GpuBlendModesToPixi.normal; + const targets = []; + const target = { + format: "bgra8unorm", + writeMask: 0, + blend + }; + for (let i = 0; i < count; i++) { + targets[i] = target; + } + return targets; + } + destroy() { + this.gpu = null; + } + } + /** @ignore */ + GpuStateSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "state" + }; + + "use strict"; + const gpuUploadBufferImageResource = { + type: "image", + upload(source, gpuTexture, gpu, originZOverride = 0) { + const resource = source.resource; + const total = (source.pixelWidth | 0) * (source.pixelHeight | 0); + const bytesPerPixel = resource.byteLength / total; + gpu.device.queue.writeTexture( + { texture: gpuTexture, origin: { x: 0, y: 0, z: originZOverride } }, + resource, + { + offset: 0, + rowsPerImage: source.pixelHeight, + bytesPerRow: source.pixelWidth * bytesPerPixel + }, + { + width: source.pixelWidth, + height: source.pixelHeight, + depthOrArrayLayers: 1 + } + ); + } + }; + + "use strict"; + const blockDataMap = { + "bc1-rgba-unorm": { blockBytes: 8, blockWidth: 4, blockHeight: 4 }, + "bc2-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 }, + "bc3-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 }, + "bc7-rgba-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 }, + "etc1-rgb-unorm": { blockBytes: 8, blockWidth: 4, blockHeight: 4 }, + "etc2-rgba8unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 }, + "astc-4x4-unorm": { blockBytes: 16, blockWidth: 4, blockHeight: 4 } + }; + const defaultBlockData = { blockBytes: 4, blockWidth: 1, blockHeight: 1 }; + const gpuUploadCompressedTextureResource = { + type: "compressed", + upload(source, gpuTexture, gpu, originZOverride = 0) { + let mipWidth = source.pixelWidth; + let mipHeight = source.pixelHeight; + const blockData = blockDataMap[source.format] || defaultBlockData; + for (let i = 0; i < source.resource.length; i++) { + const levelBuffer = source.resource[i]; + const bytesPerRow = Math.ceil(mipWidth / blockData.blockWidth) * blockData.blockBytes; + gpu.device.queue.writeTexture( + { + texture: gpuTexture, + mipLevel: i, + origin: { x: 0, y: 0, z: originZOverride } + }, + levelBuffer, + { + offset: 0, + bytesPerRow + }, + { + width: Math.ceil(mipWidth / blockData.blockWidth) * blockData.blockWidth, + height: Math.ceil(mipHeight / blockData.blockHeight) * blockData.blockHeight, + depthOrArrayLayers: 1 + } + ); + mipWidth = Math.max(mipWidth >> 1, 1); + mipHeight = Math.max(mipHeight >> 1, 1); + } + } + }; + + "use strict"; + const FACE_ORDER = ["right", "left", "top", "bottom", "front", "back"]; + function createGpuUploadCubeTextureResource(uploaders) { + return { + type: "cube", + upload(source, gpuTexture, gpu) { + const faces = source.faces; + for (let i = 0; i < FACE_ORDER.length; i++) { + const key = FACE_ORDER[i]; + const face = faces[key]; + const uploader = uploaders[face.uploadMethodId] || uploaders.image; + uploader.upload(face, gpuTexture, gpu, i); + } + } + }; + } + + "use strict"; + const gpuUploadImageResource = { + type: "image", + upload(source, gpuTexture, gpu, originZOverride = 0) { + const resource = source.resource; + if (!resource) return; + if (globalThis.HTMLImageElement && resource instanceof HTMLImageElement) { + const canvas = DOMAdapter.get().createCanvas(resource.width, resource.height); + const context = canvas.getContext("2d"); + context.drawImage(resource, 0, 0, resource.width, resource.height); + source.resource = canvas; + warn("ImageSource: Image element passed, converting to canvas and replacing resource."); + } + const width = Math.min(gpuTexture.width, source.resourceWidth || source.pixelWidth); + const height = Math.min(gpuTexture.height, source.resourceHeight || source.pixelHeight); + const premultipliedAlpha = source.alphaMode === "premultiply-alpha-on-upload"; + gpu.device.queue.copyExternalImageToTexture( + { source: resource }, + { texture: gpuTexture, origin: { x: 0, y: 0, z: originZOverride }, premultipliedAlpha }, + { + width, + height + } + ); + } + }; + + "use strict"; + const gpuUploadVideoResource = { + type: "video", + upload(source, gpuTexture, gpu, originZOverride) { + gpuUploadImageResource.upload(source, gpuTexture, gpu, originZOverride); + } + }; + + "use strict"; + class GpuMipmapGenerator { + constructor(device) { + this.device = device; + this.sampler = device.createSampler({ minFilter: "linear" }); + this.pipelines = {}; + } + _getMipmapPipeline(format) { + let pipeline = this.pipelines[format]; + if (!pipeline) { + if (!this.mipmapShaderModule) { + this.mipmapShaderModule = this.device.createShaderModule({ + code: ( + /* wgsl */ + ` + var pos : array, 3> = array, 3>( + vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + + struct VertexOutput { + @builtin(position) position : vec4, + @location(0) texCoord : vec2, + }; + + @vertex + fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput { + var output : VertexOutput; + output.texCoord = pos[vertexIndex] * vec2(0.5, -0.5) + vec2(0.5); + output.position = vec4(pos[vertexIndex], 0.0, 1.0); + return output; + } + + @group(0) @binding(0) var imgSampler : sampler; + @group(0) @binding(1) var img : texture_2d; + + @fragment + fn fragmentMain(@location(0) texCoord : vec2) -> @location(0) vec4 { + return textureSample(img, imgSampler, texCoord); + } + ` + ) + }); + } + pipeline = this.device.createRenderPipeline({ + layout: "auto", + vertex: { + module: this.mipmapShaderModule, + entryPoint: "vertexMain" + }, + fragment: { + module: this.mipmapShaderModule, + entryPoint: "fragmentMain", + targets: [{ format }] + } + }); + this.pipelines[format] = pipeline; + } + return pipeline; + } + /** + * Generates mipmaps for the given GPUTexture from the data in level 0. + * @param {module:External.GPUTexture} texture - Texture to generate mipmaps for. + * @returns {module:External.GPUTexture} - The originally passed texture + */ + generateMipmap(texture) { + const pipeline = this._getMipmapPipeline(texture.format); + if (texture.dimension === "3d" || texture.dimension === "1d") { + throw new Error("Generating mipmaps for non-2d textures is currently unsupported!"); + } + let mipTexture = texture; + const arrayLayerCount = texture.depthOrArrayLayers || 1; + const renderToSource = texture.usage & GPUTextureUsage.RENDER_ATTACHMENT; + if (!renderToSource) { + const mipTextureDescriptor = { + size: { + width: Math.ceil(texture.width / 2), + height: Math.ceil(texture.height / 2), + depthOrArrayLayers: arrayLayerCount + }, + format: texture.format, + usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT, + mipLevelCount: texture.mipLevelCount - 1 + }; + mipTexture = this.device.createTexture(mipTextureDescriptor); + } + const commandEncoder = this.device.createCommandEncoder({}); + const bindGroupLayout = pipeline.getBindGroupLayout(0); + for (let arrayLayer = 0; arrayLayer < arrayLayerCount; ++arrayLayer) { + let srcView = texture.createView({ + baseMipLevel: 0, + mipLevelCount: 1, + dimension: "2d", + baseArrayLayer: arrayLayer, + arrayLayerCount: 1 + }); + let dstMipLevel = renderToSource ? 1 : 0; + for (let i = 1; i < texture.mipLevelCount; ++i) { + const dstView = mipTexture.createView({ + baseMipLevel: dstMipLevel++, + mipLevelCount: 1, + dimension: "2d", + baseArrayLayer: arrayLayer, + arrayLayerCount: 1 + }); + const passEncoder = commandEncoder.beginRenderPass({ + colorAttachments: [{ + view: dstView, + storeOp: "store", + loadOp: "clear", + clearValue: { r: 0, g: 0, b: 0, a: 0 } + }] + }); + const bindGroup = this.device.createBindGroup({ + layout: bindGroupLayout, + entries: [{ + binding: 0, + resource: this.sampler + }, { + binding: 1, + resource: srcView + }] + }); + passEncoder.setPipeline(pipeline); + passEncoder.setBindGroup(0, bindGroup); + passEncoder.draw(3, 1, 0, 0); + passEncoder.end(); + srcView = dstView; + } + } + if (!renderToSource) { + const mipLevelSize = { + width: Math.ceil(texture.width / 2), + height: Math.ceil(texture.height / 2), + depthOrArrayLayers: arrayLayerCount + }; + for (let i = 1; i < texture.mipLevelCount; ++i) { + commandEncoder.copyTextureToTexture({ + texture: mipTexture, + mipLevel: i - 1 + }, { + texture, + mipLevel: i + }, mipLevelSize); + mipLevelSize.width = Math.ceil(mipLevelSize.width / 2); + mipLevelSize.height = Math.ceil(mipLevelSize.height / 2); + } + } + this.device.queue.submit([commandEncoder.finish()]); + if (!renderToSource) { + mipTexture.destroy(); + } + return texture; + } + } + + "use strict"; + var __defProp$h = Object.defineProperty; + var __defProps$a = Object.defineProperties; + var __getOwnPropDescs$a = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$i = Object.getOwnPropertySymbols; + var __hasOwnProp$i = Object.prototype.hasOwnProperty; + var __propIsEnum$i = Object.prototype.propertyIsEnumerable; + var __defNormalProp$h = (obj, key, value) => key in obj ? __defProp$h(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$h = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$i.call(b, prop)) + __defNormalProp$h(a, prop, b[prop]); + if (__getOwnPropSymbols$i) + for (var prop of __getOwnPropSymbols$i(b)) { + if (__propIsEnum$i.call(b, prop)) + __defNormalProp$h(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$a = (a, b) => __defProps$a(a, __getOwnPropDescs$a(b)); + class GPUTextureGpuData { + constructor(gpuTexture) { + this.textureView = null; + this.gpuTexture = gpuTexture; + } + /** Destroys this GPU data instance. */ + destroy() { + this.gpuTexture.destroy(); + this.textureView = null; + this.gpuTexture = null; + } + } + class GpuTextureSystem { + constructor(renderer) { + this._gpuSamplers = /* @__PURE__ */ Object.create(null); + this._bindGroupHash = /* @__PURE__ */ Object.create(null); + this._renderer = renderer; + renderer.gc.addCollection(this, "_bindGroupHash", "hash"); + this._managedTextures = new GCManagedHash({ + renderer, + type: "resource", + onUnload: this.onSourceUnload.bind(this), + name: "gpuTextureSource" + }); + const baseUploaders = { + image: gpuUploadImageResource, + buffer: gpuUploadBufferImageResource, + video: gpuUploadVideoResource, + compressed: gpuUploadCompressedTextureResource + }; + this._uploads = __spreadProps$a(__spreadValues$h({}, baseUploaders), { + cube: createGpuUploadCubeTextureResource(baseUploaders) + }); + } + /** + * @deprecated since 8.15.0 + */ + get managedTextures() { + return Object.values(this._managedTextures.items); + } + contextChange(gpu) { + this._gpu = gpu; + } + /** + * Initializes a texture source, if it has already been initialized nothing will happen. + * @param source - The texture source to initialize. + * @returns The initialized texture source. + */ + initSource(source) { + var _a; + return ((_a = source._gpuData[this._renderer.uid]) == null ? void 0 : _a.gpuTexture) || this._initSource(source); + } + _initSource(source) { + if (source.autoGenerateMipmaps) { + const biggestDimension = Math.max(source.pixelWidth, source.pixelHeight); + source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1; + } + let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST; + if (source.uploadMethodId !== "compressed") { + usage |= GPUTextureUsage.RENDER_ATTACHMENT; + usage |= GPUTextureUsage.COPY_SRC; + } + const blockData = blockDataMap[source.format] || { blockBytes: 4, blockWidth: 1, blockHeight: 1 }; + const width = Math.ceil(source.pixelWidth / blockData.blockWidth) * blockData.blockWidth; + const height = Math.ceil(source.pixelHeight / blockData.blockHeight) * blockData.blockHeight; + const textureDescriptor = { + label: source.label, + size: { width, height, depthOrArrayLayers: source.arrayLayerCount }, + format: source.format, + sampleCount: source.sampleCount, + mipLevelCount: source.mipLevelCount, + dimension: source.dimension, + usage + }; + const gpuTexture = this._gpu.device.createTexture(textureDescriptor); + source._gpuData[this._renderer.uid] = new GPUTextureGpuData(gpuTexture); + const added = this._managedTextures.add(source); + if (added) { + source.on("update", this.onSourceUpdate, this); + source.on("resize", this.onSourceResize, this); + source.on("updateMipmaps", this.onUpdateMipmaps, this); + } + this.onSourceUpdate(source); + return gpuTexture; + } + onSourceUpdate(source) { + const gpuTexture = this.getGpuSource(source); + if (!gpuTexture) return; + if (this._uploads[source.uploadMethodId]) { + this._uploads[source.uploadMethodId].upload(source, gpuTexture, this._gpu); + } + if (source.autoGenerateMipmaps && source.mipLevelCount > 1) { + this.onUpdateMipmaps(source); + } + } + onUpdateMipmaps(source) { + if (!this._mipmapGenerator) { + this._mipmapGenerator = new GpuMipmapGenerator(this._gpu.device); + } + const gpuTexture = this.getGpuSource(source); + this._mipmapGenerator.generateMipmap(gpuTexture); + } + onSourceUnload(source) { + source.off("update", this.onSourceUpdate, this); + source.off("resize", this.onSourceResize, this); + source.off("updateMipmaps", this.onUpdateMipmaps, this); + } + onSourceResize(source) { + source._gcLastUsed = this._renderer.gc.now; + const gpuData = source._gpuData[this._renderer.uid]; + const gpuTexture = gpuData == null ? void 0 : gpuData.gpuTexture; + if (!gpuTexture) { + this.initSource(source); + } else if (gpuTexture.width !== source.pixelWidth || gpuTexture.height !== source.pixelHeight) { + gpuData.destroy(); + this._bindGroupHash[source.uid] = null; + source._gpuData[this._renderer.uid] = null; + this.initSource(source); + } + } + _initSampler(sampler) { + this._gpuSamplers[sampler._resourceId] = this._gpu.device.createSampler(sampler); + return this._gpuSamplers[sampler._resourceId]; + } + getGpuSampler(sampler) { + return this._gpuSamplers[sampler._resourceId] || this._initSampler(sampler); + } + getGpuSource(source) { + var _a; + source._gcLastUsed = this._renderer.gc.now; + return ((_a = source._gpuData[this._renderer.uid]) == null ? void 0 : _a.gpuTexture) || this.initSource(source); + } + /** + * this returns s bind group for a specific texture, the bind group contains + * - the texture source + * - the texture style + * - the texture matrix + * This is cached so the bind group should only be created once per texture + * @param texture - the texture you want the bindgroup for + * @returns the bind group for the texture + */ + getTextureBindGroup(texture) { + return this._bindGroupHash[texture.uid] || this._createTextureBindGroup(texture); + } + _createTextureBindGroup(texture) { + const source = texture.source; + this._bindGroupHash[texture.uid] = new BindGroup({ + 0: source, + 1: source.style, + 2: new UniformGroup({ + uTextureMatrix: { type: "mat3x3", value: texture.textureMatrix.mapCoord } + }) + }); + return this._bindGroupHash[texture.uid]; + } + getTextureView(texture) { + const source = texture.source; + source._gcLastUsed = this._renderer.gc.now; + let gpuData = source._gpuData[this._renderer.uid]; + if (!gpuData) { + this.initSource(source); + gpuData = source._gpuData[this._renderer.uid]; + } + gpuData.textureView || (gpuData.textureView = gpuData.gpuTexture.createView({ dimension: source.viewDimension })); + return gpuData.textureView; + } + generateCanvas(texture) { + const renderer = this._renderer; + const commandEncoder = renderer.gpu.device.createCommandEncoder(); + const canvas = DOMAdapter.get().createCanvas(); + canvas.width = texture.source.pixelWidth; + canvas.height = texture.source.pixelHeight; + const context = canvas.getContext("webgpu"); + context.configure({ + device: renderer.gpu.device, + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC, + format: DOMAdapter.get().getNavigator().gpu.getPreferredCanvasFormat(), + alphaMode: "premultiplied" + }); + commandEncoder.copyTextureToTexture({ + texture: renderer.texture.getGpuSource(texture.source), + origin: { + x: 0, + y: 0 + } + }, { + texture: context.getCurrentTexture() + }, { + width: canvas.width, + height: canvas.height + }); + renderer.gpu.device.queue.submit([commandEncoder.finish()]); + return canvas; + } + getPixels(texture) { + const webGPUCanvas = this.generateCanvas(texture); + const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(webGPUCanvas.width, webGPUCanvas.height); + const context = canvasAndContext.context; + context.drawImage(webGPUCanvas, 0, 0); + const { width, height } = webGPUCanvas; + const imageData = context.getImageData(0, 0, width, height); + const pixels = new Uint8ClampedArray(imageData.data.buffer); + CanvasPool.returnCanvasAndContext(canvasAndContext); + return { pixels, width, height }; + } + destroy() { + this._managedTextures.destroy(); + for (const k of Object.keys(this._bindGroupHash)) { + const key = Number(k); + const bindGroup = this._bindGroupHash[key]; + bindGroup == null ? void 0 : bindGroup.destroy(); + } + this._renderer = null; + this._gpu = null; + this._mipmapGenerator = null; + this._gpuSamplers = null; + this._bindGroupHash = null; + } + } + /** @ignore */ + GpuTextureSystem.extension = { + type: [ + ExtensionType.WebGPUSystem + ], + name: "texture" + }; + + "use strict"; + + "use strict"; + class GpuGraphicsAdaptor { + constructor() { + this._maxTextures = 0; + } + contextChange(renderer) { + const localUniforms = new UniformGroup({ + uTransformMatrix: { value: new Matrix(), type: "mat3x3" }, + uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4" }, + uRound: { value: 0, type: "f32" } + }); + this._maxTextures = renderer.limits.maxBatchableTextures; + const gpuProgram = compileHighShaderGpuProgram({ + name: "graphics", + bits: [ + colorBit, + generateTextureBatchBit(this._maxTextures), + localUniformBitGroup2, + roundPixelsBit + ] + }); + this.shader = new Shader({ + gpuProgram, + resources: { + // added on the fly! + localUniforms + } + }); + } + execute(graphicsPipe, renderable) { + const context = renderable.context; + const shader = context.customShader || this.shader; + const renderer = graphicsPipe.renderer; + const contextSystem = renderer.graphicsContext; + const { + batcher, + instructions + } = contextSystem.getContextRenderData(context); + const encoder = renderer.encoder; + encoder.setGeometry(batcher.geometry, shader.gpuProgram); + const globalUniformsBindGroup = renderer.globalUniforms.bindGroup; + encoder.setBindGroup(0, globalUniformsBindGroup, shader.gpuProgram); + const localBindGroup = renderer.renderPipes.uniformBatch.getUniformBindGroup(shader.resources.localUniforms, true); + encoder.setBindGroup(2, localBindGroup, shader.gpuProgram); + const batches = instructions.instructions; + let topology = null; + for (let i = 0; i < instructions.instructionSize; i++) { + const batch = batches[i]; + if (batch.topology !== topology) { + topology = batch.topology; + encoder.setPipelineFromGeometryProgramAndState( + batcher.geometry, + shader.gpuProgram, + graphicsPipe.state, + batch.topology + ); + } + shader.groups[1] = batch.bindGroup; + if (!batch.gpuBindGroup) { + const textureBatch = batch.textures; + batch.bindGroup = getTextureBatchBindGroup( + textureBatch.textures, + textureBatch.count, + this._maxTextures + ); + batch.gpuBindGroup = renderer.bindGroup.getBindGroup( + batch.bindGroup, + shader.gpuProgram, + 1 + ); + } + encoder.setBindGroup(1, batch.bindGroup, shader.gpuProgram); + encoder.renderPassEncoder.drawIndexed(batch.size, 1, batch.start); + } + } + destroy() { + this.shader.destroy(true); + this.shader = null; + } + } + /** @ignore */ + GpuGraphicsAdaptor.extension = { + type: [ + ExtensionType.WebGPUPipesAdaptor + ], + name: "graphics" + }; + + "use strict"; + class GpuMeshAdapter { + init() { + const gpuProgram = compileHighShaderGpuProgram({ + name: "mesh", + bits: [ + localUniformBit, + textureBit, + roundPixelsBit + ] + }); + this._shader = new Shader({ + gpuProgram, + resources: { + uTexture: Texture.EMPTY._source, + uSampler: Texture.EMPTY._source.style, + textureUniforms: { + uTextureMatrix: { type: "mat3x3", value: new Matrix() } + } + } + }); + } + execute(meshPipe, mesh) { + const renderer = meshPipe.renderer; + let shader = mesh._shader; + if (!shader) { + shader = this._shader; + shader.groups[2] = renderer.texture.getTextureBindGroup(mesh.texture); + } else if (!shader.gpuProgram) { + warn("Mesh shader has no gpuProgram", mesh.shader); + return; + } + const gpuProgram = shader.gpuProgram; + if (gpuProgram.autoAssignGlobalUniforms) { + shader.groups[0] = renderer.globalUniforms.bindGroup; + } + if (gpuProgram.autoAssignLocalUniforms) { + const localUniforms = meshPipe.localUniforms; + shader.groups[1] = renderer.renderPipes.uniformBatch.getUniformBindGroup(localUniforms, true); + } + renderer.encoder.draw({ + geometry: mesh._geometry, + shader, + state: mesh.state + }); + } + destroy() { + this._shader.destroy(true); + this._shader = null; + } + } + /** @ignore */ + GpuMeshAdapter.extension = { + type: [ + ExtensionType.WebGPUPipesAdaptor + ], + name: "mesh" + }; + + "use strict"; + const DefaultWebGPUSystems = [ + ...SharedSystems, + GpuUboSystem, + GpuEncoderSystem, + GpuDeviceSystem, + GpuLimitsSystem, + GpuBufferSystem, + GpuTextureSystem, + GpuRenderTargetSystem, + GpuShaderSystem, + GpuStateSystem, + PipelineSystem, + GpuColorMaskSystem, + GpuStencilSystem, + BindGroupSystem + ]; + const DefaultWebGPUPipes = [...SharedRenderPipes, GpuUniformBatchPipe]; + const DefaultWebGPUAdapters = [GpuBatchAdaptor, GpuMeshAdapter, GpuGraphicsAdaptor]; + const systems = []; + const renderPipes = []; + const renderPipeAdaptors = []; + extensions.handleByNamedList(ExtensionType.WebGPUSystem, systems); + extensions.handleByNamedList(ExtensionType.WebGPUPipes, renderPipes); + extensions.handleByNamedList(ExtensionType.WebGPUPipesAdaptor, renderPipeAdaptors); + extensions.add(...DefaultWebGPUSystems, ...DefaultWebGPUPipes, ...DefaultWebGPUAdapters); + class WebGPURenderer extends AbstractRenderer { + constructor() { + const systemConfig = { + name: "webgpu", + type: RendererType.WEBGPU, + systems, + renderPipes, + renderPipeAdaptors + }; + super(systemConfig); + } + } + + var WebGPURenderer$1 = { + __proto__: null, + WebGPURenderer: WebGPURenderer + }; + + "use strict"; + const DEPRECATED_DRAW_MODES = { + POINTS: "point-list", + LINES: "line-list", + LINE_STRIP: "line-strip", + TRIANGLES: "triangle-list", + TRIANGLE_STRIP: "triangle-strip" + }; + const DRAW_MODES = new Proxy(DEPRECATED_DRAW_MODES, { + get(target, prop) { + deprecation(v8_0_0, `DRAW_MODES.${prop} is deprecated, use '${DEPRECATED_DRAW_MODES[prop]}' instead`); + return target[prop]; + } + }); + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + var DEPRECATED_WRAP_MODES = /* @__PURE__ */ ((DEPRECATED_WRAP_MODES2) => { + DEPRECATED_WRAP_MODES2["CLAMP"] = "clamp-to-edge"; + DEPRECATED_WRAP_MODES2["REPEAT"] = "repeat"; + DEPRECATED_WRAP_MODES2["MIRRORED_REPEAT"] = "mirror-repeat"; + return DEPRECATED_WRAP_MODES2; + })(DEPRECATED_WRAP_MODES || {}); + const WRAP_MODES = new Proxy(DEPRECATED_WRAP_MODES, { + get(target, prop) { + deprecation(v8_0_0, `DRAW_MODES.${prop} is deprecated, use '${DEPRECATED_WRAP_MODES[prop]}' instead`); + return target[prop]; + } + }); + var DEPRECATED_SCALE_MODES = /* @__PURE__ */ ((DEPRECATED_SCALE_MODES2) => { + DEPRECATED_SCALE_MODES2["NEAREST"] = "nearest"; + DEPRECATED_SCALE_MODES2["LINEAR"] = "linear"; + return DEPRECATED_SCALE_MODES2; + })(DEPRECATED_SCALE_MODES || {}); + const SCALE_MODES = new Proxy(DEPRECATED_SCALE_MODES, { + get(target, prop) { + deprecation(v8_0_0, `DRAW_MODES.${prop} is deprecated, use '${DEPRECATED_SCALE_MODES[prop]}' instead`); + return target[prop]; + } + }); + + "use strict"; + var __defProp$g = Object.defineProperty; + var __defProps$9 = Object.defineProperties; + var __getOwnPropDescs$9 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$h = Object.getOwnPropertySymbols; + var __hasOwnProp$h = Object.prototype.hasOwnProperty; + var __propIsEnum$h = Object.prototype.propertyIsEnumerable; + var __defNormalProp$g = (obj, key, value) => key in obj ? __defProp$g(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$g = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$h.call(b, prop)) + __defNormalProp$g(a, prop, b[prop]); + if (__getOwnPropSymbols$h) + for (var prop of __getOwnPropSymbols$h(b)) { + if (__propIsEnum$h.call(b, prop)) + __defNormalProp$g(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$9 = (a, b) => __defProps$9(a, __getOwnPropDescs$9(b)); + var __objRest$9 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$h.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$h) + for (var prop of __getOwnPropSymbols$h(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$h.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class CubeTextureSource extends TextureSource { + constructor(options) { + const _a = options, { faces } = _a, rest = __objRest$9(_a, ["faces"]); + CubeTextureSource._validateFaces(faces); + const first = faces.right; + const derivedResolution = first.resolution; + const derivedFormat = first.format; + const derivedAlphaMode = first.alphaMode; + const ignoredKeys = [ + "resolution", + "format", + "alphaMode", + "dimensions", + "viewDimension" + ].filter((key) => rest[key] !== void 0); + if (ignoredKeys.length) { + warn( + `[CubeTextureSource] Ignoring option(s) [${ignoredKeys.join(", ")}]; these are derived from face sources.` + ); + } + super(__spreadProps$9(__spreadValues$g({}, rest), { + resource: faces, + // Keep these aligned with the face sources so any code that reads width/height works. + width: first.width, + height: first.height, + dimensions: "2d", + viewDimension: "cube", + arrayLayerCount: 6, + resolution: derivedResolution, + format: derivedFormat, + alphaMode: derivedAlphaMode + })); + /** @internal */ + this.uploadMethodId = "cube"; + this.faces = faces; + for (const key of Object.keys(faces)) { + const face = faces[key]; + face.on("update", this._onFaceUpdate, this); + face.on("resize", this._onFaceResize, this); + face.on("unload", this._onFaceUpdate, this); + } + } + destroy() { + const faces = this.faces; + if (faces) { + for (const key of Object.keys(faces)) { + const face = faces[key]; + face.off("update", this._onFaceUpdate, this); + face.off("resize", this._onFaceResize, this); + face.off("unload", this._onFaceUpdate, this); + } + } + super.destroy(); + } + _onFaceUpdate() { + this.emit("update", this); + } + _onFaceResize(face) { + CubeTextureSource._validateFaces(this.faces); + this.resize(face.width, face.height, face.resolution); + } + static _validateFaces(faces) { + if (!faces.right || !faces.left || !faces.top || !faces.bottom || !faces.front || !faces.back) { + throw new Error("[CubeTextureSource] Requires { left, right, top, bottom, front, back } faces."); + } + const first = faces.right; + const expectedPixelWidth = first.pixelWidth; + const expectedPixelHeight = first.pixelHeight; + const expectedFormat = first.format; + const expectedAlphaMode = first.alphaMode; + const expectedResolution = first.resolution; + for (const key of Object.keys(faces)) { + const face = faces[key]; + if (face.pixelWidth !== expectedPixelWidth || face.pixelHeight !== expectedPixelHeight) { + throw new Error(`[CubeTextureSource] Face '${String(key)}' has a different size. All faces must match.`); + } + if (face.format !== expectedFormat) { + throw new Error(`[CubeTextureSource] Face '${String(key)}' has a different format. All faces must match.`); + } + if (face.alphaMode !== expectedAlphaMode) { + throw new Error( + `[CubeTextureSource] Face '${String(key)}' has a different alphaMode. All faces must match.` + ); + } + if (face.resolution !== expectedResolution) { + throw new Error( + `[CubeTextureSource] Face '${String(key)}' has a different resolution. All faces must match.` + ); + } + } + } + } + + "use strict"; + var __defProp$f = Object.defineProperty; + var __defProps$8 = Object.defineProperties; + var __getOwnPropDescs$8 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$g = Object.getOwnPropertySymbols; + var __hasOwnProp$g = Object.prototype.hasOwnProperty; + var __propIsEnum$g = Object.prototype.propertyIsEnumerable; + var __defNormalProp$f = (obj, key, value) => key in obj ? __defProp$f(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$f = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$g.call(b, prop)) + __defNormalProp$f(a, prop, b[prop]); + if (__getOwnPropSymbols$g) + for (var prop of __getOwnPropSymbols$g(b)) { + if (__propIsEnum$g.call(b, prop)) + __defNormalProp$f(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$8 = (a, b) => __defProps$8(a, __getOwnPropDescs$8(b)); + var __objRest$8 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$g.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$g) + for (var prop of __getOwnPropSymbols$g(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$g.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const faceKeys = ["left", "right", "top", "bottom", "front", "back"]; + function getCubeCacheKey(faceIds, options) { + const opts = options ? __spreadValues$f({}, options) : {}; + delete opts.label; + const optKeys = Object.keys(opts).sort(); + const optPart = optKeys.length ? `|${optKeys.map((k) => `${k}=${String(opts[k])}`).join("&")}` : ""; + const facesPart = faceKeys.map((k) => faceIds[k]).join(","); + return `cube:${facesPart}${optPart}`; + } + class CubeTexture extends EventEmitter { + constructor(options) { + var _a; + super(); + /** unique id for this cube texture */ + this.uid = uid$1("cubeTexture"); + /** Has the texture been destroyed? */ + this.destroyed = false; + const { label, source } = options; + this.label = label; + this.source = source; + this.source.label = (_a = this.label) != null ? _a : this.source.label; + } + static from(options, skipCache = false) { + if (options instanceof CubeTextureSource) { + return new CubeTexture({ source: options }); + } + const _a = options, { faces } = _a, sourceOptions = __objRest$8(_a, ["faces"]); + let cacheKey = null; + const isFaceIds = faceKeys.every((key) => typeof faces[key] === "string"); + if (!skipCache && isFaceIds) { + cacheKey = getCubeCacheKey(faces, sourceOptions); + if (Cache.has(cacheKey)) { + return Cache.get(cacheKey); + } + } + const toTexture = (input) => { + if (input.isTexture) return input; + return Texture.from(input); + }; + const faceSources = {}; + for (const key of faceKeys) { + faceSources[key] = toTexture(faces[key]).source; + } + const cubeTexture = new CubeTexture({ + source: new CubeTextureSource(__spreadProps$8(__spreadValues$f({}, sourceOptions), { + faces: faceSources + })), + label: sourceOptions.label + }); + if (cacheKey) { + Cache.set(cacheKey, cubeTexture); + cubeTexture.once("destroy", () => { + if (Cache.has(cacheKey)) { + Cache.remove(cacheKey); + } + }); + } + return cubeTexture; + } + /** + * Destroy this CubeTexture. + * @param destroySource - If true, destroys the underlying {@link CubeTextureSource}. + */ + destroy(destroySource = false) { + if (this.destroyed) return; + this.destroyed = true; + if (destroySource) { + this.source.destroy(); + } + this.emit("destroy", this); + this.removeAllListeners(); + } + } + + "use strict"; + + "use strict"; + const placeholderGl = /* @__PURE__ */ Object.create(null); + const placeholderGpu = /* @__PURE__ */ Object.create(null); + function getPlaceholder(renderer) { + var _a; + if (renderer.type === RendererType.WEBGPU) { + placeholderGpu[_a = renderer.uid] || (placeholderGpu[_a] = renderer.gpu.device.createTexture({ + label: "ExternalSource placeholder", + size: { width: 1, height: 1 }, + format: "rgba8unorm", + usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST + })); + return placeholderGpu[renderer.uid]; + } + if (!placeholderGl[renderer.uid]) { + const gl = renderer.gl; + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + placeholderGl[renderer.uid] = texture; + } + return placeholderGl[renderer.uid]; + } + class ExternalSource extends TextureSource { + constructor({ resource, renderer, label, width, height }) { + var _a, _b; + resource || (resource = getPlaceholder(renderer)); + width || (width = (_a = width != null ? width : resource == null ? void 0 : resource.width) != null ? _a : 1); + height || (height = (_b = height != null ? height : resource == null ? void 0 : resource.height) != null ? _b : 1); + super({ + resource, + width, + height, + label, + // External textures shouldn't be garbage collected - the external library owns them + autoGarbageCollect: false + }); + this._renderer = renderer; + this._initGpuData(resource); + } + /** + * Test if a resource is a valid external GPU texture. + * @param resource - The resource to test + * @returns True if the resource is a GPUTexture or WebGLTexture + */ + static test(resource) { + return globalThis.GPUTexture && resource instanceof GPUTexture || globalThis.WebGLTexture && resource instanceof WebGLTexture; + } + _validateTexture(resource) { + const renderer = this._renderer; + const isWebGPU = !!renderer.gpu; + const isGPUTexture = globalThis.GPUTexture && resource instanceof GPUTexture; + const isWebGLTexture = globalThis.WebGLTexture && resource instanceof WebGLTexture; + if (isWebGPU && isWebGLTexture) { + throw new Error("Cannot use WebGLTexture with a WebGPU renderer"); + } + if (!isWebGPU && isGPUTexture) { + throw new Error("Cannot use GPUTexture with a WebGL renderer"); + } + if (!isWebGPU) { + const gl = renderer.gl; + if (gl && !gl.isTexture(resource)) { + throw new Error("WebGLTexture does not belong to this renderer's WebGL context"); + } + } + } + _initGpuData(resource) { + const renderer = this._renderer; + this._validateTexture(resource); + if (renderer.gpu) { + this._gpuData[renderer.uid] = new GPUTextureGpuData(resource); + } else { + this._gpuData[renderer.uid] = new GlTexture(resource); + } + } + /** + * Update the external GPU texture reference. + * Call this when the external library provides a new texture. + * @param gpuTexture - The new GPU texture + * @param width - New width (required for WebGLTexture, auto-detected for GPUTexture) + * @param height - New height (required for WebGLTexture, auto-detected for GPUTexture) + */ + updateGPUTexture(gpuTexture, width, height) { + const renderer = this._renderer; + const gpuData = this._gpuData[renderer.uid]; + this.resource = gpuTexture; + if (renderer.gpu) { + this._validateTexture(gpuTexture); + const data = gpuData; + if (data.gpuTexture !== gpuTexture) { + data.gpuTexture = gpuTexture; + data.textureView = null; + const textureSystem = renderer.texture; + if (textureSystem == null ? void 0 : textureSystem._bindGroupHash) { + textureSystem._bindGroupHash[this.uid] = null; + } + } + const newWidth = width != null ? width : gpuTexture.width; + const newHeight = height != null ? height : gpuTexture.height; + this.resize(newWidth, newHeight); + } else { + this._validateTexture(gpuTexture); + const data = gpuData; + data.texture = gpuTexture; + if (width !== void 0 && height !== void 0) { + this.resize(width, height); + } + } + this.emit("update", this); + } + destroy() { + const renderer = this._renderer; + delete this._gpuData[renderer.uid]; + super.destroy(); + } + } + + "use strict"; + class TextureUvs { + constructor() { + this.x0 = 0; + this.y0 = 0; + this.x1 = 1; + this.y1 = 0; + this.x2 = 1; + this.y2 = 1; + this.x3 = 0; + this.y3 = 1; + this.uvsFloat32 = new Float32Array(8); + } + /** + * Sets the texture Uvs based on the given frame information. + * @protected + * @param frame - The frame of the texture + * @param baseFrame - The base frame of the texture + * @param rotate - Rotation of frame, see {@link groupD8} + */ + set(frame, baseFrame, rotate) { + const tw = baseFrame.width; + const th = baseFrame.height; + if (rotate) { + const w2 = frame.width / 2 / tw; + const h2 = frame.height / 2 / th; + const cX = frame.x / tw + w2; + const cY = frame.y / th + h2; + rotate = groupD8.add(rotate, groupD8.NW); + this.x0 = cX + w2 * groupD8.uX(rotate); + this.y0 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + this.x1 = cX + w2 * groupD8.uX(rotate); + this.y1 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + this.x2 = cX + w2 * groupD8.uX(rotate); + this.y2 = cY + h2 * groupD8.uY(rotate); + rotate = groupD8.add(rotate, 2); + this.x3 = cX + w2 * groupD8.uX(rotate); + this.y3 = cY + h2 * groupD8.uY(rotate); + } else { + this.x0 = frame.x / tw; + this.y0 = frame.y / th; + this.x1 = (frame.x + frame.width) / tw; + this.y1 = frame.y / th; + this.x2 = (frame.x + frame.width) / tw; + this.y2 = (frame.y + frame.height) / th; + this.x3 = frame.x / tw; + this.y3 = (frame.y + frame.height) / th; + } + this.uvsFloat32[0] = this.x0; + this.uvsFloat32[1] = this.y0; + this.uvsFloat32[2] = this.x1; + this.uvsFloat32[3] = this.y1; + this.uvsFloat32[4] = this.x2; + this.uvsFloat32[5] = this.y2; + this.uvsFloat32[6] = this.x3; + this.uvsFloat32[7] = this.y3; + } + toString() { + return `[pixi.js/core:TextureUvs x0=${this.x0} y0=${this.y0} x1=${this.x1} y1=${this.y1} x2=${this.x2} y2=${this.y2} x3=${this.x3} y3=${this.y3}]`; + } + } + + "use strict"; + function parseFunctionBody(fn) { + const fnStr = fn.toString(); + const bodyStart = fnStr.indexOf("{"); + const bodyEnd = fnStr.lastIndexOf("}"); + if (bodyStart === -1 || bodyEnd === -1) { + throw new Error("getFunctionBody: No body found in function definition"); + } + return fnStr.slice(bodyStart + 1, bodyEnd).trim(); + } + + "use strict"; + + "use strict"; + + "use strict"; + function getFastGlobalBounds(target, bounds) { + deprecation("8.7.0", "Use container.getFastGlobalBounds() instead"); + return target.getFastGlobalBounds(true, bounds); + } + + "use strict"; + + "use strict"; + + "use strict"; + var __defProp$e = Object.defineProperty; + var __getOwnPropSymbols$f = Object.getOwnPropertySymbols; + var __hasOwnProp$f = Object.prototype.hasOwnProperty; + var __propIsEnum$f = Object.prototype.propertyIsEnumerable; + var __defNormalProp$e = (obj, key, value) => key in obj ? __defProp$e(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$e = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$f.call(b, prop)) + __defNormalProp$e(a, prop, b[prop]); + if (__getOwnPropSymbols$f) + for (var prop of __getOwnPropSymbols$f(b)) { + if (__propIsEnum$f.call(b, prop)) + __defNormalProp$e(a, prop, b[prop]); + } + return a; + }; + var __objRest$7 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$f.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$f) + for (var prop of __getOwnPropSymbols$f(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$f.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class RenderContainer extends ViewContainer { + /** + * @param options - The options for the container. + */ + constructor(options) { + var _b, _c; + if (typeof options === "function") { + options = { render: options }; + } + const _a = options, { render } = _a, rest = __objRest$7(_a, ["render"]); + super(__spreadValues$e({ + label: "RenderContainer" + }, rest)); + /** @internal */ + this.renderPipeId = "customRender"; + /** @internal */ + this.batched = false; + if (render) this.render = render; + this.containsPoint = (_b = options.containsPoint) != null ? _b : (() => false); + this.addBounds = (_c = options.addBounds) != null ? _c : (() => false); + } + /** @private */ + updateBounds() { + this._bounds.clear(); + this.addBounds(this._bounds); + } + /** + * An overridable function that can be used to render the object using the current renderer. + * @param _renderer - The current renderer + */ + render(_renderer) { + } + } + + "use strict"; + function collectAllRenderables(container, instructionSet, rendererOrPipes) { + deprecation("8.7.0", "Please use container.collectRenderables instead."); + const renderer = rendererOrPipes.renderPipes ? rendererOrPipes : rendererOrPipes.batch.renderer; + return container.collectRenderables(instructionSet, renderer, null); + } + + "use strict"; + function updateLocalTransform(lt, container) { + const scale = container._scale; + const pivot = container._pivot; + const position = container._position; + const sx = scale._x; + const sy = scale._y; + const px = pivot._x; + const py = pivot._y; + lt.a = container._cx * sx; + lt.b = container._sx * sx; + lt.c = container._cy * sy; + lt.d = container._sy * sy; + lt.tx = position._x - (px * lt.a + py * lt.c); + lt.ty = position._y - (px * lt.b + py * lt.d); + } + + "use strict"; + function updateWorldTransform(local, parent, world) { + const lta = local.a; + const ltb = local.b; + const ltc = local.c; + const ltd = local.d; + const lttx = local.tx; + const ltty = local.ty; + const pta = parent.a; + const ptb = parent.b; + const ptc = parent.c; + const ptd = parent.d; + world.a = lta * pta + ltb * ptc; + world.b = lta * ptb + ltb * ptd; + world.c = ltc * pta + ltd * ptc; + world.d = ltc * ptb + ltd * ptd; + world.tx = lttx * pta + ltty * ptc + parent.tx; + world.ty = lttx * ptb + ltty * ptd + parent.ty; + } + + "use strict"; + + "use strict"; + + "use strict"; + function buildGeometryFromPath(options) { + if (options instanceof GraphicsPath) { + options = { + path: options, + textureMatrix: null, + out: null + }; + } + const vertices = []; + const uvs = []; + const indices = []; + const shapePath = options.path.shapePath; + const textureMatrix = options.textureMatrix; + shapePath.shapePrimitives.forEach(({ shape, transform: matrix }) => { + const indexOffset = indices.length; + const vertOffset = vertices.length / 2; + const points = []; + const build = shapeBuilders[shape.type]; + build.build(shape, points); + if (matrix) { + transformVertices(points, matrix); + } + build.triangulate(points, vertices, 2, vertOffset, indices, indexOffset); + const uvsOffset = uvs.length / 2; + if (textureMatrix) { + if (matrix) { + textureMatrix.append(matrix.clone().invert()); + } + buildUvs(vertices, 2, vertOffset, uvs, uvsOffset, 2, vertices.length / 2 - vertOffset, textureMatrix); + } else { + buildSimpleUvs(uvs, uvsOffset, 2, vertices.length / 2 - vertOffset); + } + }); + const out = options.out; + if (out) { + out.positions = new Float32Array(vertices); + out.uvs = new Float32Array(uvs); + out.indices = new Uint32Array(indices); + return out; + } + const geometry = new MeshGeometry({ + positions: new Float32Array(vertices), + uvs: new Float32Array(uvs), + indices: new Uint32Array(indices) + }); + return geometry; + } + + "use strict"; + var __defProp$d = Object.defineProperty; + var __getOwnPropSymbols$e = Object.getOwnPropertySymbols; + var __hasOwnProp$e = Object.prototype.hasOwnProperty; + var __propIsEnum$e = Object.prototype.propertyIsEnumerable; + var __defNormalProp$d = (obj, key, value) => key in obj ? __defProp$d(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$d = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$e.call(b, prop)) + __defNormalProp$d(a, prop, b[prop]); + if (__getOwnPropSymbols$e) + for (var prop of __getOwnPropSymbols$e(b)) { + if (__propIsEnum$e.call(b, prop)) + __defNormalProp$d(a, prop, b[prop]); + } + return a; + }; + const _RenderLayer = class _RenderLayer extends Container { + /** + * Creates a new RenderLayer instance + * @param options - Configuration options for the RenderLayer + * @param {boolean} [options.sortableChildren=false] - If true, layer children will be automatically sorted each render + * @param {Function} [options.sortFunction] - Custom function to sort layer children. Default sorts by zIndex + */ + constructor(options = {}) { + options = __spreadValues$d(__spreadValues$d({}, _RenderLayer.defaultOptions), options); + super(); + /** + * The list of objects that this layer is responsible for rendering. Objects in this list maintain + * their original parent in the scene graph but are rendered as part of this layer. + * @example + * ```ts + * const layer = new RenderLayer(); + * const sprite = new Sprite(texture); + * + * // Add sprite to scene graph for transforms + * container.addChild(sprite); + * + * // Add to layer for render order control + * layer.attach(sprite); + * console.log(layer.renderLayerChildren.length); // 1 + * + * // Access objects in the layer + * layer.renderLayerChildren.forEach(child => { + * console.log('Layer child:', child); + * }); + * + * // Check if object is in layer + * const isInLayer = layer.renderLayerChildren.includes(sprite); + * + * // Clear all objects from layer + * layer.detachAll(); + * console.log(layer.renderLayerChildren.length); // 0 + * ``` + * @readonly + * @see {@link RenderLayer#attach} For adding objects to the layer + * @see {@link RenderLayer#detach} For removing objects from the layer + * @see {@link RenderLayer#detachAll} For removing all objects from the layer + */ + this.renderLayerChildren = []; + this.sortableChildren = options.sortableChildren; + this.sortFunction = options.sortFunction; + } + /** + * Adds one or more Containers to this render layer. The Containers will be rendered as part of this layer + * while maintaining their original parent in the scene graph. + * + * If the Container already belongs to a layer, it will be removed from the old layer before being added to this one. + * @example + * ```ts + * const layer = new RenderLayer(); + * const container = new Container(); + * const sprite1 = new Sprite(texture1); + * const sprite2 = new Sprite(texture2); + * + * // Add sprites to scene graph for transforms + * container.addChild(sprite1, sprite2); + * + * // Add sprites to layer for render order control + * layer.attach(sprite1, sprite2); + * + * // Add single sprite with type checking + * const typedSprite = layer.attach(new Sprite(texture3)); + * typedSprite.tint = 'red'; + * + * // Automatically removes from previous layer if needed + * const otherLayer = new RenderLayer(); + * otherLayer.attach(sprite1); // Removes from previous layer + * ``` + * @param children - The Container(s) to add to this layer. Can be any Container or array of Containers. + * @returns The first child that was added, for method chaining + * @see {@link RenderLayer#detach} For removing objects from the layer + * @see {@link RenderLayer#detachAll} For removing all objects from the layer + * @see {@link Container#addChild} For adding to scene graph hierarchy + */ + attach(...children) { + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.parentRenderLayer) { + if (child.parentRenderLayer === this) continue; + child.parentRenderLayer.detach(child); + } + this.renderLayerChildren.push(child); + child.parentRenderLayer = this; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.structureDidChange = true; + } + } + return children[0]; + } + /** + * Removes one or more Containers from this render layer. The Containers will maintain their + * original parent in the scene graph but will no longer be rendered as part of this layer. + * @example + * ```ts + * const layer = new RenderLayer(); + * const container = new Container(); + * const sprite1 = new Sprite(texture1); + * const sprite2 = new Sprite(texture2); + * + * // Add sprites to scene graph and layer + * container.addChild(sprite1, sprite2); + * layer.attach(sprite1, sprite2); + * + * // Remove single sprite from layer + * layer.detach(sprite1); + * // sprite1 is still child of container but not rendered in layer + * + * // Remove multiple sprites at once + * const otherLayer = new RenderLayer(); + * otherLayer.attach(sprite3, sprite4); + * otherLayer.detach(sprite3, sprite4); + * + * // Type-safe detachment + * const typedSprite = layer.detach(spriteInLayer); + * typedSprite.texture = newTexture; // TypeScript knows this is a Sprite + * ``` + * @param children - The Container(s) to remove from this layer + * @returns The first child that was removed, for method chaining + * @see {@link RenderLayer#attach} For adding objects to the layer + * @see {@link RenderLayer#detachAll} For removing all objects from the layer + * @see {@link Container#removeChild} For removing from scene graph hierarchy + */ + detach(...children) { + for (let i = 0; i < children.length; i++) { + const child = children[i]; + const index = this.renderLayerChildren.indexOf(child); + if (index !== -1) { + this.renderLayerChildren.splice(index, 1); + } + child.parentRenderLayer = null; + const renderGroup = this.renderGroup || this.parentRenderGroup; + if (renderGroup) { + renderGroup.structureDidChange = true; + } + } + return children[0]; + } + /** + * Removes all objects from this render layer. Objects will maintain their + * original parent in the scene graph but will no longer be rendered as part of this layer. + * @example + * ```ts + * const layer = new RenderLayer(); + * const container = new Container(); + * + * // Add multiple sprites to scene graph and layer + * const sprites = [ + * new Sprite(texture1), + * new Sprite(texture2), + * new Sprite(texture3) + * ]; + * + * container.addChild(...sprites); // Add to scene graph + * layer.attach(...sprites); // Add to render layer + * + * // Later, remove all sprites from layer at once + * layer.detachAll(); + * console.log(layer.renderLayerChildren.length); // 0 + * console.log(container.children.length); // 3 (still in scene graph) + * ``` + * @returns The RenderLayer instance for method chaining + * @see {@link RenderLayer#attach} For adding objects to the layer + * @see {@link RenderLayer#detach} For removing individual objects + * @see {@link Container#removeChildren} For removing from scene graph + */ + detachAll() { + const layerChildren = this.renderLayerChildren; + for (let i = 0; i < layerChildren.length; i++) { + layerChildren[i].parentRenderLayer = null; + } + this.renderLayerChildren.length = 0; + } + /** + * Collects renderables for this layer and its children. + * This method is called by the renderer to gather all objects that should be rendered in this layer. + * @param instructionSet - The set of instructions to collect renderables into. + * @param renderer - The renderer that is collecting renderables. + * @param _currentLayer - The current render layer being processed. + * @internal + */ + collectRenderables(instructionSet, renderer, _currentLayer) { + const layerChildren = this.renderLayerChildren; + const length = layerChildren.length; + if (this.sortableChildren) { + this.sortRenderLayerChildren(); + } + for (let i = 0; i < length; i++) { + if (!layerChildren[i].parent) { + warn( + "Container must be added to both layer and scene graph. Layers only handle render order - the scene graph is required for transforms (addChild)", + layerChildren[i] + ); + } + layerChildren[i].collectRenderables(instructionSet, renderer, this); + } + } + /** + * Sort the layer's children using the defined sort function. This method allows manual sorting + * of layer children and is automatically called during rendering if sortableChildren is true. + * @example + * ```ts + * const layer = new RenderLayer(); + * + * // Add multiple sprites at different depths + * const sprite1 = new Sprite(texture); + * const sprite2 = new Sprite(texture); + * const sprite3 = new Sprite(texture); + * + * sprite1.zIndex = 3; + * sprite2.zIndex = 1; + * sprite3.zIndex = 2; + * + * layer.attach(sprite1, sprite2, sprite3); + * + * // Manual sorting with default zIndex sort + * layer.sortRenderLayerChildren(); + * // Order is now: sprite2 (1), sprite3 (2), sprite1 (3) + * + * // Custom sort by y position + * layer.sortFunction = (a, b) => a.y - b.y; + * layer.sortRenderLayerChildren(); + * + * // Automatic sorting + * layer.sortableChildren = true; // Will sort each render + * ``` + * @returns The RenderLayer instance for method chaining + * @see {@link RenderLayer#sortableChildren} For enabling automatic sorting + * @see {@link RenderLayer#sortFunction} For customizing the sort logic + */ + sortRenderLayerChildren() { + this.renderLayerChildren.sort(this.sortFunction); + } + /** + * Recursively calculates the global bounds of this RenderLayer and its children. + * @param factorRenderLayers + * @param bounds + * @param _currentLayer + * @internal + */ + _getGlobalBoundsRecursive(factorRenderLayers, bounds, _currentLayer) { + if (!factorRenderLayers) return; + const children = this.renderLayerChildren; + for (let i = 0; i < children.length; i++) { + children[i]._getGlobalBoundsRecursive(true, bounds, this); + } + } + /** + * @inheritdoc + * @internal + */ + getFastGlobalBounds(factorRenderLayers, bounds) { + return super.getFastGlobalBounds(factorRenderLayers, bounds); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. Please use `RenderLayer.attach()` instead. + * @param {...any} _children + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + addChild(..._children) { + throw new Error( + "RenderLayer.addChild() is not available. Please use RenderLayer.attach()" + ); + } + /** + * This method is not available in RenderLayer. + * Calling this method will throw an error. Please use `RenderLayer.detach()` instead. + * @param {...any} _children + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChild(..._children) { + throw new Error( + "RenderLayer.removeChild() is not available. Please use RenderLayer.detach()" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. Please use `RenderLayer.detach()` instead. + * @param {number} [_beginIndex] + * @param {number} [_endIndex] + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChildren(_beginIndex, _endIndex) { + throw new Error( + "RenderLayer.removeChildren() is not available. Please use RenderLayer.detach()" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChildAt(_index) { + throw new Error( + "RenderLayer.removeChildAt() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + getChildAt(_index) { + throw new Error( + "RenderLayer.getChildAt() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {Container} _child + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + setChildIndex(_child, _index) { + throw new Error( + "RenderLayer.setChildIndex() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {Container} _child + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + getChildIndex(_child) { + throw new Error( + "RenderLayer.getChildIndex() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {Container} _child + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + addChildAt(_child, _index) { + throw new Error( + "RenderLayer.addChildAt() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param {Container} _child + * @param {Container} _child2 + * @ignore + */ + swapChildren(_child, _child2) { + throw new Error( + "RenderLayer.swapChildren() is not available" + ); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param _child - The child to reparent + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + reparentChild(..._child) { + throw new Error("RenderLayer.reparentChild() is not available with the render layer"); + } + /** + * This method is not available in RenderLayer. + * + * Calling this method will throw an error. + * @param _child - The child to reparent + * @param _index - The index to reparent the child to + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + reparentChildAt(_child, _index) { + throw new Error("RenderLayer.reparentChildAt() is not available with the render layer"); + } + }; + /** + * Default options for RenderLayer instances. These options control the sorting behavior + * of objects within the render layer. + * @example + * ```ts + * // Create a custom render layer with modified default options + * RenderLayer.defaultOptions = { + * sortableChildren: true, + * sortFunction: (a, b) => a.y - b.y // Sort by vertical position + * }; + * + * // All new render layers will use these defaults + * const layer1 = new RenderLayer(); + * // layer1 will have sortableChildren = true + * ``` + * @property {boolean} sortableChildren - + * @property {Function} sortFunction - + * @see {@link RenderLayer} For the main render layer class + * @see {@link Container#zIndex} For the default sort property + * @see {@link RenderLayer#sortRenderLayerChildren} For manual sorting + */ + _RenderLayer.defaultOptions = { + /** If true, layer children will be automatically sorted each render. Default is false. */ + sortableChildren: false, + /** + * Function used to sort layer children. + * Default sorts by zIndex. Accepts two Container objects and returns + * a number indicating their relative order. + * @param a - First container to compare + * @param b - Second container to compare + * @returns Negative if a should render before b, positive if b should render before a + */ + sortFunction: (a, b) => a.zIndex - b.zIndex + }; + let RenderLayer = _RenderLayer; + + "use strict"; + var __defProp$c = Object.defineProperty; + var __getOwnPropSymbols$d = Object.getOwnPropertySymbols; + var __hasOwnProp$d = Object.prototype.hasOwnProperty; + var __propIsEnum$d = Object.prototype.propertyIsEnumerable; + var __defNormalProp$c = (obj, key, value) => key in obj ? __defProp$c(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$c = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$d.call(b, prop)) + __defNormalProp$c(a, prop, b[prop]); + if (__getOwnPropSymbols$d) + for (var prop of __getOwnPropSymbols$d(b)) { + if (__propIsEnum$d.call(b, prop)) + __defNormalProp$c(a, prop, b[prop]); + } + return a; + }; + const _PlaneGeometry = class _PlaneGeometry extends MeshGeometry { + constructor(...args) { + var _a; + super({}); + let options = (_a = args[0]) != null ? _a : {}; + if (typeof options === "number") { + deprecation(v8_0_0, "PlaneGeometry constructor changed please use { width, height, verticesX, verticesY } instead"); + options = { + width: options, + height: args[1], + verticesX: args[2], + verticesY: args[3] + }; + } + this.build(options); + } + /** + * Refreshes plane coordinates + * @param options - Options to be applied to plane geometry + */ + build(options) { + var _a, _b, _c, _d; + options = __spreadValues$c(__spreadValues$c({}, _PlaneGeometry.defaultOptions), options); + this.verticesX = (_a = this.verticesX) != null ? _a : options.verticesX; + this.verticesY = (_b = this.verticesY) != null ? _b : options.verticesY; + this.width = (_c = this.width) != null ? _c : options.width; + this.height = (_d = this.height) != null ? _d : options.height; + const total = this.verticesX * this.verticesY; + const verts = []; + const uvs = []; + const indices = []; + const verticesX = this.verticesX - 1; + const verticesY = this.verticesY - 1; + const sizeX = this.width / verticesX; + const sizeY = this.height / verticesY; + for (let i = 0; i < total; i++) { + const x = i % this.verticesX; + const y = i / this.verticesX | 0; + verts.push(x * sizeX, y * sizeY); + uvs.push(x / verticesX, y / verticesY); + } + const totalSub = verticesX * verticesY; + for (let i = 0; i < totalSub; i++) { + const xpos = i % verticesX; + const ypos = i / verticesX | 0; + const value = ypos * this.verticesX + xpos; + const value2 = ypos * this.verticesX + xpos + 1; + const value3 = (ypos + 1) * this.verticesX + xpos; + const value4 = (ypos + 1) * this.verticesX + xpos + 1; + indices.push( + value, + value2, + value3, + value2, + value4, + value3 + ); + } + this.buffers[0].data = new Float32Array(verts); + this.buffers[1].data = new Float32Array(uvs); + this.indexBuffer.data = new Uint32Array(indices); + this.buffers[0].update(); + this.buffers[1].update(); + this.indexBuffer.update(); + } + }; + _PlaneGeometry.defaultOptions = { + width: 100, + height: 100, + verticesX: 10, + verticesY: 10 + }; + let PlaneGeometry = _PlaneGeometry; + + "use strict"; + function applyProjectiveTransformationToPlane(width, height, geometry, transformationMatrix) { + const buffer = geometry.buffers[0]; + const vertices = buffer.data; + const { verticesX, verticesY } = geometry; + const sizeX = width / (verticesX - 1); + const sizeY = height / (verticesY - 1); + let index = 0; + const a00 = transformationMatrix[0]; + const a01 = transformationMatrix[1]; + const a02 = transformationMatrix[2]; + const a10 = transformationMatrix[3]; + const a11 = transformationMatrix[4]; + const a12 = transformationMatrix[5]; + const a20 = transformationMatrix[6]; + const a21 = transformationMatrix[7]; + const a22 = transformationMatrix[8]; + for (let i = 0; i < vertices.length; i += 2) { + const x = index % verticesX * sizeX; + const y = (index / verticesX | 0) * sizeY; + const newX = a00 * x + a01 * y + a02; + const newY = a10 * x + a11 * y + a12; + const w = a20 * x + a21 * y + a22; + vertices[i] = newX / w; + vertices[i + 1] = newY / w; + index++; + } + buffer.update(); + } + + "use strict"; + function computeAdjugate(out, matrix) { + const a00 = matrix[0]; + const a01 = matrix[1]; + const a02 = matrix[2]; + const a10 = matrix[3]; + const a11 = matrix[4]; + const a12 = matrix[5]; + const a20 = matrix[6]; + const a21 = matrix[7]; + const a22 = matrix[8]; + out[0] = a11 * a22 - a12 * a21; + out[1] = a02 * a21 - a01 * a22; + out[2] = a01 * a12 - a02 * a11; + out[3] = a12 * a20 - a10 * a22; + out[4] = a00 * a22 - a02 * a20; + out[5] = a02 * a10 - a00 * a12; + out[6] = a10 * a21 - a11 * a20; + out[7] = a01 * a20 - a00 * a21; + out[8] = a00 * a11 - a01 * a10; + return out; + } + function multiplyMatrix3x3(out, a, b) { + const a00 = a[0]; + const a01 = a[1]; + const a02 = a[2]; + const a10 = a[3]; + const a11 = a[4]; + const a12 = a[5]; + const a20 = a[6]; + const a21 = a[7]; + const a22 = a[8]; + const b00 = b[0]; + const b01 = b[1]; + const b02 = b[2]; + const b10 = b[3]; + const b11 = b[4]; + const b12 = b[5]; + const b20 = b[6]; + const b21 = b[7]; + const b22 = b[8]; + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; + } + function multiplyMatrixAndVector(out, m, v) { + const x = v[0]; + const y = v[1]; + const z = v[2]; + out[0] = m[0] * x + m[1] * y + m[2] * z; + out[1] = m[3] * x + m[4] * y + m[5] * z; + out[2] = m[6] * x + m[7] * y + m[8] * z; + return out; + } + const tempMatrix = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const tempVec = [0, 0, 0]; + const tempVec2 = [0, 0, 0]; + function generateBasisToPointsMatrix(out, x1, y1, x2, y2, x3, y3, x4, y4) { + const m = tempMatrix; + m[0] = x1; + m[1] = x2; + m[2] = x3; + m[3] = y1; + m[4] = y2; + m[5] = y3; + m[6] = 1; + m[7] = 1; + m[8] = 1; + const adjugateM = computeAdjugate( + out, + // reusing out as adjugateM is only used once + m + ); + tempVec2[0] = x4; + tempVec2[1] = y4; + tempVec2[2] = 1; + const v = multiplyMatrixAndVector( + tempVec, + adjugateM, + tempVec2 + ); + const diagonalMatrix = out; + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = v[1]; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = v[2]; + return multiplyMatrix3x3(out, diagonalMatrix, m); + } + const tempSourceMatrix = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const tempDestinationMatrix = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + function compute2DProjection(out, x1s, y1s, x1d, y1d, x2s, y2s, x2d, y2d, x3s, y3s, x3d, y3d, x4s, y4s, x4d, y4d) { + const sourceMatrix = generateBasisToPointsMatrix( + tempSourceMatrix, + x1s, + y1s, + x2s, + y2s, + x3s, + y3s, + x4s, + y4s + ); + const destinationMatrix = generateBasisToPointsMatrix( + tempDestinationMatrix, + x1d, + y1d, + x2d, + y2d, + x3d, + y3d, + x4d, + y4d + ); + return multiplyMatrix3x3( + out, + computeAdjugate(sourceMatrix, sourceMatrix), + destinationMatrix + ); + } + + "use strict"; + class PerspectivePlaneGeometry extends PlaneGeometry { + /** + * @param options - Options to be applied to MeshPlane + * @param options.width - The width of the plane + * @param options.height - The height of the plane + * @param options.verticesX - The amount of vertices on the x axis + * @param options.verticesY - The amount of vertices on the y axis + */ + constructor(options) { + super(options); + this._projectionMatrix = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + const { width, height } = options; + this.corners = [0, 0, width, 0, width, height, 0, height]; + } + /** + * Will set the corners of the quad to the given coordinates + * Calculating the perspective so it looks correct! + * @param x0 - x coordinate of the first corner + * @param y0 - y coordinate of the first corner + * @param x1 - x coordinate of the second corner + * @param y1 - y coordinate of the second corner + * @param x2 - x coordinate of the third corner + * @param y2 - y coordinate of the third corner + * @param x3 - x coordinate of the fourth corner + * @param y3 - y coordinate of the fourth corner + */ + setCorners(x0, y0, x1, y1, x2, y2, x3, y3) { + const corners = this.corners; + corners[0] = x0; + corners[1] = y0; + corners[2] = x1; + corners[3] = y1; + corners[4] = x2; + corners[5] = y2; + corners[6] = x3; + corners[7] = y3; + this.updateProjection(); + } + /** Update the projection matrix based on the corners */ + updateProjection() { + const { width, height } = this; + const corners = this.corners; + const projectionMatrix = compute2DProjection( + this._projectionMatrix, + 0, + 0, + // top-left source + corners[0], + corners[1], + // top-left dest + width, + 0, + // top-right source + corners[2], + corners[3], + // top-right dest + width, + height, + // bottom-right source + corners[4], + corners[5], + // bottom-right dest + 0, + height, + // bottom-left source + corners[6], + corners[7] + // bottom-left dest + ); + applyProjectiveTransformationToPlane( + width, + height, + this, + projectionMatrix + ); + } + } + + "use strict"; + var __defProp$b = Object.defineProperty; + var __defProps$7 = Object.defineProperties; + var __getOwnPropDescs$7 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$c = Object.getOwnPropertySymbols; + var __hasOwnProp$c = Object.prototype.hasOwnProperty; + var __propIsEnum$c = Object.prototype.propertyIsEnumerable; + var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$b = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$c.call(b, prop)) + __defNormalProp$b(a, prop, b[prop]); + if (__getOwnPropSymbols$c) + for (var prop of __getOwnPropSymbols$c(b)) { + if (__propIsEnum$c.call(b, prop)) + __defNormalProp$b(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$7 = (a, b) => __defProps$7(a, __getOwnPropDescs$7(b)); + var __objRest$6 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$c.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$c) + for (var prop of __getOwnPropSymbols$c(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$c.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const _PerspectiveMesh = class _PerspectiveMesh extends Mesh { + /** + * @param options - Options to be applied to PerspectiveMesh + */ + constructor(options) { + options = __spreadValues$b(__spreadValues$b({}, _PerspectiveMesh.defaultOptions), options); + const _a = options, { texture, verticesX, verticesY } = _a, rest = __objRest$6(_a, ["texture", "verticesX", "verticesY"]); + const planeGeometry = new PerspectivePlaneGeometry(definedProps({ + width: texture.width, + height: texture.height, + verticesX, + verticesY + })); + super(definedProps(__spreadProps$7(__spreadValues$b({}, rest), { geometry: planeGeometry }))); + this._texture = texture; + this.geometry.setCorners( + options.x0, + options.y0, + options.x1, + options.y1, + options.x2, + options.y2, + options.x3, + options.y3 + ); + } + /** Update the geometry when the texture is updated */ + textureUpdated() { + const geometry = this.geometry; + if (!geometry) return; + const { width, height } = this.texture; + if (geometry.width !== width || geometry.height !== height) { + geometry.width = width; + geometry.height = height; + geometry.updateProjection(); + } + } + set texture(value) { + if (this._texture === value) return; + super.texture = value; + this.textureUpdated(); + } + /** + * The texture that the mesh uses for rendering. When changed, automatically updates + * the geometry to match the new texture dimensions. + * @example + * ```ts + * const mesh = new PerspectiveMesh({ + * texture: Texture.from('initial.png'), + * }); + * + * // Update texture and maintain perspective + * mesh.texture = Texture.from('newImage.png'); + * ``` + * @see {@link Texture} For texture creation and management + * @see {@link PerspectiveMesh#setCorners} For adjusting the mesh perspective + */ + get texture() { + return this._texture; + } + /** + * Sets the corners of the mesh to create a perspective transformation. The corners should be + * specified in clockwise order starting from the top-left. + * + * The mesh automatically recalculates the UV coordinates to create the perspective effect. + * @example + * ```ts + * const mesh = new PerspectiveMesh({ + * texture: Texture.from('myImage.png'), + * }); + * + * // Create a basic perspective tilt + * mesh.setCorners( + * 0, 0, // Top-left + * 100, 20, // Top-right (raised) + * 100, 100, // Bottom-right + * 0, 80 // Bottom-left + * ); + * + * // Create a skewed billboard effect + * mesh.setCorners( + * 0, 30, // Top-left (shifted down) + * 128, 0, // Top-right (raised) + * 128, 128, // Bottom-right + * 0, 98 // Bottom-left (shifted up) + * ); + * + * // Animate perspective + * app.ticker.add((delta) => { + * const time = performance.now() / 1000; + * const wave = Math.sin(time) * 20; + * + * mesh.setCorners( + * 0, wave, // Top-left + * 100, -wave, // Top-right + * 100, 100, // Bottom-right + * 0, 100 // Bottom-left + * ); + * }); + * ``` + * @param x0 - x-coordinate of the top-left corner + * @param y0 - y-coordinate of the top-left corner + * @param x1 - x-coordinate of the top-right corner + * @param y1 - y-coordinate of the top-right corner + * @param x2 - x-coordinate of the bottom-right corner + * @param y2 - y-coordinate of the bottom-right corner + * @param x3 - x-coordinate of the bottom-left corner + * @param y3 - y-coordinate of the bottom-left corner + * @returns The PerspectiveMesh instance for method chaining + * @see {@link PerspectivePlaneGeometry} For the underlying geometry calculations + */ + setCorners(x0, y0, x1, y1, x2, y2, x3, y3) { + this.geometry.setCorners(x0, y0, x1, y1, x2, y2, x3, y3); + } + }; + /** + * Default options for creating a PerspectiveMesh instance. + * + * Creates a 100x100 pixel square mesh + * with a white texture and 10x10 vertex grid for the perspective calculations. + * @example + * ```ts + * // Change defaults globally + * PerspectiveMesh.defaultOptions = { + * ...PerspectiveMesh.defaultOptions, + * verticesX: 15, + * verticesY: 15, + * // Move top edge up for default skew + * y0: -20, + * y1: -20 + * }; + * ``` + * @see {@link PerspectivePlaneOptions} For all available options + * @see {@link PerspectivePlaneGeometry} For how vertices affect perspective quality + */ + _PerspectiveMesh.defaultOptions = { + texture: Texture.WHITE, + verticesX: 10, + verticesY: 10, + x0: 0, + y0: 0, + x1: 100, + y1: 0, + x2: 100, + y2: 100, + x3: 0, + y3: 100 + }; + let PerspectiveMesh = _PerspectiveMesh; + + "use strict"; + var __defProp$a = Object.defineProperty; + var __defProps$6 = Object.defineProperties; + var __getOwnPropDescs$6 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$b = Object.getOwnPropertySymbols; + var __hasOwnProp$b = Object.prototype.hasOwnProperty; + var __propIsEnum$b = Object.prototype.propertyIsEnumerable; + var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$a = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$b.call(b, prop)) + __defNormalProp$a(a, prop, b[prop]); + if (__getOwnPropSymbols$b) + for (var prop of __getOwnPropSymbols$b(b)) { + if (__propIsEnum$b.call(b, prop)) + __defNormalProp$a(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$6 = (a, b) => __defProps$6(a, __getOwnPropDescs$6(b)); + var __objRest$5 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$b.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$b) + for (var prop of __getOwnPropSymbols$b(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$b.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class MeshPlane extends Mesh { + /** + * @param options - Options to be applied to MeshPlane + */ + constructor(options) { + const _a = options, { texture, verticesX, verticesY } = _a, rest = __objRest$5(_a, ["texture", "verticesX", "verticesY"]); + const planeGeometry = new PlaneGeometry(definedProps({ + width: texture.width, + height: texture.height, + verticesX, + verticesY + })); + super(definedProps(__spreadProps$6(__spreadValues$a({}, rest), { geometry: planeGeometry, texture }))); + this.texture = texture; + this.autoResize = true; + } + /** + * Method used for overrides, to do something in case texture frame was changed. + * Meshes based on plane can override it and change more details based on texture. + * @internal + */ + textureUpdated() { + const geometry = this.geometry; + const { width, height } = this.texture; + if (this.autoResize && (geometry.width !== width || geometry.height !== height)) { + geometry.width = width; + geometry.height = height; + geometry.build({}); + } + } + set texture(value) { + var _a; + (_a = this._texture) == null ? void 0 : _a.off("update", this.textureUpdated, this); + super.texture = value; + value.on("update", this.textureUpdated, this); + this.textureUpdated(); + } + /** + * The texture that the mesh plane uses for rendering. When changed, automatically updates + * geometry dimensions if autoResize is true and manages texture update event listeners. + * @example + * ```ts + * const plane = new MeshPlane({ + * texture: Assets.get('initial.png'), + * verticesX: 10, + * verticesY: 10 + * }); + * + * // Update texture and auto-resize geometry + * plane.texture = Assets.get('larger.png'); + * ``` + * @see {@link MeshPlane#autoResize} For controlling automatic geometry updates + * @see {@link PlaneGeometry} For manual geometry updates + * @see {@link Texture} For texture creation and management + */ + get texture() { + return this._texture; + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * meshPlane.destroy(); + * meshPlane.destroy(true); + * meshPlane.destroy({ texture: true, textureSource: true }); + */ + destroy(options) { + this.texture.off("update", this.textureUpdated, this); + super.destroy(options); + } + } + + "use strict"; + var __defProp$9 = Object.defineProperty; + var __getOwnPropSymbols$a = Object.getOwnPropertySymbols; + var __hasOwnProp$a = Object.prototype.hasOwnProperty; + var __propIsEnum$a = Object.prototype.propertyIsEnumerable; + var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$9 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$a.call(b, prop)) + __defNormalProp$9(a, prop, b[prop]); + if (__getOwnPropSymbols$a) + for (var prop of __getOwnPropSymbols$a(b)) { + if (__propIsEnum$a.call(b, prop)) + __defNormalProp$9(a, prop, b[prop]); + } + return a; + }; + const _RopeGeometry = class _RopeGeometry extends MeshGeometry { + /** + * @param options - Options to be applied to rope geometry + */ + constructor(options) { + const { width, points, textureScale } = __spreadValues$9(__spreadValues$9({}, _RopeGeometry.defaultOptions), options); + super({ + positions: new Float32Array(points.length * 4), + uvs: new Float32Array(points.length * 4), + indices: new Uint32Array((points.length - 1) * 6) + }); + this.points = points; + this._width = width; + this.textureScale = textureScale; + this._build(); + } + /** + * The width (i.e., thickness) of the rope. + * @readonly + */ + get width() { + return this._width; + } + /** Refreshes Rope indices and uvs */ + _build() { + const points = this.points; + if (!points) return; + const vertexBuffer = this.getBuffer("aPosition"); + const uvBuffer = this.getBuffer("aUV"); + const indexBuffer = this.getIndex(); + if (points.length < 1) { + return; + } + if (vertexBuffer.data.length / 4 !== points.length) { + vertexBuffer.data = new Float32Array(points.length * 4); + uvBuffer.data = new Float32Array(points.length * 4); + indexBuffer.data = new Uint16Array((points.length - 1) * 6); + } + const uvs = uvBuffer.data; + const indices = indexBuffer.data; + uvs[0] = 0; + uvs[1] = 0; + uvs[2] = 0; + uvs[3] = 1; + let amount = 0; + let prev = points[0]; + const textureWidth = this._width * this.textureScale; + const total = points.length; + for (let i = 0; i < total; i++) { + const index = i * 4; + if (this.textureScale > 0) { + const dx = prev.x - points[i].x; + const dy = prev.y - points[i].y; + const distance = Math.sqrt(dx * dx + dy * dy); + prev = points[i]; + amount += distance / textureWidth; + } else { + amount = i / (total - 1); + } + uvs[index] = amount; + uvs[index + 1] = 0; + uvs[index + 2] = amount; + uvs[index + 3] = 1; + } + let indexCount = 0; + for (let i = 0; i < total - 1; i++) { + const index = i * 2; + indices[indexCount++] = index; + indices[indexCount++] = index + 1; + indices[indexCount++] = index + 2; + indices[indexCount++] = index + 2; + indices[indexCount++] = index + 1; + indices[indexCount++] = index + 3; + } + uvBuffer.update(); + indexBuffer.update(); + this.updateVertices(); + } + /** refreshes vertices of Rope mesh */ + updateVertices() { + const points = this.points; + if (points.length < 1) { + return; + } + let lastPoint = points[0]; + let nextPoint; + let perpX = 0; + let perpY = 0; + const vertices = this.buffers[0].data; + const total = points.length; + const halfWidth = this.textureScale > 0 ? this.textureScale * this._width / 2 : this._width / 2; + for (let i = 0; i < total; i++) { + const point = points[i]; + const index = i * 4; + if (i < points.length - 1) { + nextPoint = points[i + 1]; + } else { + nextPoint = point; + } + perpY = -(nextPoint.x - lastPoint.x); + perpX = nextPoint.y - lastPoint.y; + let ratio = (1 - i / (total - 1)) * 10; + if (ratio > 1) { + ratio = 1; + } + const perpLength = Math.sqrt(perpX * perpX + perpY * perpY); + if (perpLength < 1e-6) { + perpX = 0; + perpY = 0; + } else { + perpX /= perpLength; + perpY /= perpLength; + perpX *= halfWidth; + perpY *= halfWidth; + } + vertices[index] = point.x + perpX; + vertices[index + 1] = point.y + perpY; + vertices[index + 2] = point.x - perpX; + vertices[index + 3] = point.y - perpY; + lastPoint = point; + } + this.buffers[0].update(); + } + /** Refreshes Rope indices and uvs */ + update() { + if (this.textureScale > 0) { + this._build(); + } else { + this.updateVertices(); + } + } + }; + /** Default options for RopeGeometry constructor. */ + _RopeGeometry.defaultOptions = { + /** The width (i.e., thickness) of the rope. */ + width: 200, + /** An array of points that determine the rope. */ + points: [], + /** Rope texture scale, if zero then the rope texture is stretched. */ + textureScale: 0 + }; + let RopeGeometry = _RopeGeometry; + + "use strict"; + var __defProp$8 = Object.defineProperty; + var __defProps$5 = Object.defineProperties; + var __getOwnPropDescs$5 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$9 = Object.getOwnPropertySymbols; + var __hasOwnProp$9 = Object.prototype.hasOwnProperty; + var __propIsEnum$9 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$8 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$9.call(b, prop)) + __defNormalProp$8(a, prop, b[prop]); + if (__getOwnPropSymbols$9) + for (var prop of __getOwnPropSymbols$9(b)) { + if (__propIsEnum$9.call(b, prop)) + __defNormalProp$8(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$5 = (a, b) => __defProps$5(a, __getOwnPropDescs$5(b)); + var __objRest$4 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$9.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$9) + for (var prop of __getOwnPropSymbols$9(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$9.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const _MeshRope = class _MeshRope extends Mesh { + /** + * Note: The wrap mode of the texture is set to REPEAT if `textureScale` is positive. + * @param options + * @param options.texture - The texture to use on the rope. + * @param options.points - An array of {@link math.Point} objects to construct this rope. + * @param {number} options.textureScale - Optional. Positive values scale rope texture + * keeping its aspect ratio. You can reduce alpha channel artifacts by providing a larger texture + * and downsampling here. If set to zero, texture will be stretched instead. + */ + constructor(options) { + const _a = __spreadValues$8(__spreadValues$8({}, _MeshRope.defaultOptions), options), { texture, points, textureScale } = _a, rest = __objRest$4(_a, ["texture", "points", "textureScale"]); + const ropeGeometry = new RopeGeometry(definedProps({ width: texture.height, points, textureScale })); + if (textureScale > 0) { + texture.source.style.addressMode = "repeat"; + } + super(definedProps(__spreadProps$5(__spreadValues$8({}, rest), { + texture, + geometry: ropeGeometry + }))); + this.autoUpdate = true; + this.onRender = this._render; + } + _render() { + const geometry = this.geometry; + if (this.autoUpdate || geometry._width !== this.texture.height) { + geometry._width = this.texture.height; + geometry.update(); + } + } + }; + /** + * Default options for creating a MeshRope instance. These values are used when specific + * options aren't provided in the constructor. + * @example + * ```ts + * // Use default options globally + * MeshRope.defaultOptions = { + * textureScale: 0.5 // Set higher quality texture scaling + * }; + * + * // Create rope with modified defaults + * const rope = new MeshRope({ + * texture: Texture.from('rope.png'), + * points: [ + * new Point(0, 0), + * new Point(100, 0) + * ] + * }); // Will use textureScale: 0.5 + * ``` + * @property {number} textureScale - Controls texture scaling along the rope (0 = stretch) + * @see {@link MeshRopeOptions} For all available options + */ + _MeshRope.defaultOptions = { + textureScale: 0 + }; + let MeshRope = _MeshRope; + + "use strict"; + var __defProp$7 = Object.defineProperty; + var __defProps$4 = Object.defineProperties; + var __getOwnPropDescs$4 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$8 = Object.getOwnPropertySymbols; + var __hasOwnProp$8 = Object.prototype.hasOwnProperty; + var __propIsEnum$8 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$7 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$8.call(b, prop)) + __defNormalProp$7(a, prop, b[prop]); + if (__getOwnPropSymbols$8) + for (var prop of __getOwnPropSymbols$8(b)) { + if (__propIsEnum$8.call(b, prop)) + __defNormalProp$7(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$4 = (a, b) => __defProps$4(a, __getOwnPropDescs$4(b)); + var __objRest$3 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$8.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$8) + for (var prop of __getOwnPropSymbols$8(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$8.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class MeshSimple extends Mesh { + /** + * @param options - Options to be used for construction + */ + constructor(options) { + const _a = options, { texture, vertices, uvs, indices, topology } = _a, rest = __objRest$3(_a, ["texture", "vertices", "uvs", "indices", "topology"]); + const geometry = new MeshGeometry(definedProps({ + positions: vertices, + uvs, + indices, + topology + })); + super(definedProps(__spreadProps$4(__spreadValues$7({}, rest), { + texture, + geometry + }))); + this.autoUpdate = true; + this.onRender = this._render; + } + /** + * The vertex positions of the mesh as a TypedArray. Each vertex is represented by two + * consecutive values (x, y) in the array. Changes to these values will update the mesh's shape. + * @example + * ```ts + * // Read vertex positions + * const vertices = mesh.vertices; + * console.log('First vertex:', vertices[0], vertices[1]); + * + * // Modify vertices directly + * vertices[0] += 10; // Move first vertex right + * vertices[1] -= 20; // Move first vertex up + * + * // Animate vertices + * app.ticker.add(() => { + * const time = performance.now() / 1000; + * const vertices = mesh.vertices; + * + * // Wave motion + * for (let i = 0; i < vertices.length; i += 2) { + * vertices[i + 1] = Math.sin(time + i * 0.5) * 20; + * } + * }); + * ``` + * @see {@link MeshSimple#autoUpdate} For controlling vertex buffer updates + * @see {@link MeshGeometry#getBuffer} For direct buffer access + */ + get vertices() { + return this.geometry.getBuffer("aPosition").data; + } + set vertices(value) { + this.geometry.getBuffer("aPosition").data = value; + } + _render() { + if (this.autoUpdate) { + this.geometry.getBuffer("aPosition").update(); + } + } + } + + "use strict"; + function getTextureDefaultMatrix(texture, out) { + const { width, height } = texture.frame; + out.scale(1 / width, 1 / height); + return out; + } + + "use strict"; + class CanvasParticleContainerAdaptor { + execute(particleContainerPipe, container) { + var _a, _b, _c; + const renderer = particleContainerPipe.renderer; + const context = renderer.canvasContext.activeContext; + const children = container.particleChildren; + const texture = container.texture; + context.save(); + renderer.canvasContext.setContextTransform(container.worldTransform, container.roundPixels); + renderer.canvasContext.setBlendMode(container.groupBlendMode); + const groupColorAlpha = container.groupColorAlpha; + const filterAlpha = (_b = (_a = renderer.filter) == null ? void 0 : _a.alphaMultiplier) != null ? _b : 1; + const groupAlpha = (groupColorAlpha >>> 24 & 255) / 255 * filterAlpha; + for (let i = 0; i < children.length; i++) { + const particle = children[i]; + const pTexture = particle.texture || texture; + if (!((_c = pTexture == null ? void 0 : pTexture.source) == null ? void 0 : _c.resource)) continue; + const color = particle.color; + const alpha = (color >>> 24 & 255) / 255 * groupAlpha; + if (alpha <= 0) continue; + const bgr = color & 16777215; + const tint = ((bgr & 255) << 16) + (bgr & 65280) + (bgr >> 16 & 255); + let drawSource = pTexture.source.resource; + if (tint !== 16777215) { + drawSource = canvasUtils.getTintedCanvas({ texture: pTexture }, tint); + } + const frame = pTexture.frame; + const resolution = pTexture.source.resolution; + const sx = frame.x * resolution; + const sy = frame.y * resolution; + const sw = frame.width * resolution; + const sh = frame.height * resolution; + context.globalAlpha = alpha; + const dx = -particle.anchorX * frame.width; + const dy = -particle.anchorY * frame.height; + if (particle.rotation !== 0 || particle.scaleX !== 1 || particle.scaleY !== 1) { + context.save(); + context.translate(particle.x, particle.y); + context.rotate(particle.rotation); + context.scale(particle.scaleX, particle.scaleY); + context.drawImage(drawSource, sx, sy, sw, sh, dx, dy, frame.width, frame.height); + context.restore(); + } else { + context.drawImage(drawSource, sx, sy, sw, sh, particle.x + dx, particle.y + dy, frame.width, frame.height); + } + } + context.restore(); + } + } + + "use strict"; + function createIndicesForQuads(size, outBuffer = null) { + const totalIndices = size * 6; + if (totalIndices > 65535) { + outBuffer || (outBuffer = new Uint32Array(totalIndices)); + } else { + outBuffer || (outBuffer = new Uint16Array(totalIndices)); + } + if (outBuffer.length !== totalIndices) { + throw new Error(`Out buffer length is incorrect, got ${outBuffer.length} and expected ${totalIndices}`); + } + for (let i = 0, j = 0; i < totalIndices; i += 6, j += 4) { + outBuffer[i + 0] = j + 0; + outBuffer[i + 1] = j + 1; + outBuffer[i + 2] = j + 2; + outBuffer[i + 3] = j + 0; + outBuffer[i + 4] = j + 2; + outBuffer[i + 5] = j + 3; + } + return outBuffer; + } + + "use strict"; + function generateParticleUpdateFunction(properties) { + return { + dynamicUpdate: generateUpdateFunction(properties, true), + staticUpdate: generateUpdateFunction(properties, false) + }; + } + function generateUpdateFunction(properties, dynamic) { + const funcFragments = []; + funcFragments.push(` + + var index = 0; + + for (let i = 0; i < ps.length; ++i) + { + const p = ps[i]; + + `); + let offset = 0; + for (const i in properties) { + const property = properties[i]; + if (dynamic !== property.dynamic) continue; + funcFragments.push(`offset = index + ${offset}`); + funcFragments.push(property.code); + const attributeInfo = getAttributeInfoFromFormat(property.format); + offset += attributeInfo.stride / 4; + } + funcFragments.push(` + index += stride * 4; + } + `); + funcFragments.unshift(` + var stride = ${offset}; + `); + const functionSource = funcFragments.join("\n"); + return new Function("ps", "f32v", "u32v", functionSource); + } + + "use strict"; + class ParticleBuffer { + constructor(options) { + this._size = 0; + this._generateParticleUpdateCache = {}; + var _a; + const size = this._size = (_a = options.size) != null ? _a : 1e3; + const properties = options.properties; + let staticVertexSize = 0; + let dynamicVertexSize = 0; + for (const i in properties) { + const property = properties[i]; + const attributeInfo = getAttributeInfoFromFormat(property.format); + if (property.dynamic) { + dynamicVertexSize += attributeInfo.stride; + } else { + staticVertexSize += attributeInfo.stride; + } + } + this._dynamicStride = dynamicVertexSize / 4; + this._staticStride = staticVertexSize / 4; + this.staticAttributeBuffer = new ViewableBuffer(size * 4 * staticVertexSize); + this.dynamicAttributeBuffer = new ViewableBuffer(size * 4 * dynamicVertexSize); + this.indexBuffer = createIndicesForQuads(size); + const geometry = new Geometry(); + let dynamicOffset = 0; + let staticOffset = 0; + this._staticBuffer = new Buffer({ + data: new Float32Array(1), + label: "static-particle-buffer", + shrinkToFit: false, + usage: BufferUsage.VERTEX | BufferUsage.COPY_DST + }); + this._dynamicBuffer = new Buffer({ + data: new Float32Array(1), + label: "dynamic-particle-buffer", + shrinkToFit: false, + usage: BufferUsage.VERTEX | BufferUsage.COPY_DST + }); + for (const i in properties) { + const property = properties[i]; + const attributeInfo = getAttributeInfoFromFormat(property.format); + if (property.dynamic) { + geometry.addAttribute(property.attributeName, { + buffer: this._dynamicBuffer, + stride: this._dynamicStride * 4, + offset: dynamicOffset * 4, + format: property.format + }); + dynamicOffset += attributeInfo.size; + } else { + geometry.addAttribute(property.attributeName, { + buffer: this._staticBuffer, + stride: this._staticStride * 4, + offset: staticOffset * 4, + format: property.format + }); + staticOffset += attributeInfo.size; + } + } + geometry.addIndex(this.indexBuffer); + const uploadFunction = this.getParticleUpdate(properties); + this._dynamicUpload = uploadFunction.dynamicUpdate; + this._staticUpload = uploadFunction.staticUpdate; + this.geometry = geometry; + } + getParticleUpdate(properties) { + const key = getParticleSyncKey(properties); + if (this._generateParticleUpdateCache[key]) { + return this._generateParticleUpdateCache[key]; + } + this._generateParticleUpdateCache[key] = this.generateParticleUpdate(properties); + return this._generateParticleUpdateCache[key]; + } + generateParticleUpdate(properties) { + return generateParticleUpdateFunction(properties); + } + update(particles, uploadStatic) { + if (particles.length > this._size) { + uploadStatic = true; + this._size = Math.max(particles.length, this._size * 1.5 | 0); + this.staticAttributeBuffer = new ViewableBuffer(this._size * this._staticStride * 4 * 4); + this.dynamicAttributeBuffer = new ViewableBuffer(this._size * this._dynamicStride * 4 * 4); + this.indexBuffer = createIndicesForQuads(this._size); + this.geometry.indexBuffer.setDataWithSize( + this.indexBuffer, + this.indexBuffer.byteLength, + true + ); + } + const dynamicAttributeBuffer = this.dynamicAttributeBuffer; + this._dynamicUpload(particles, dynamicAttributeBuffer.float32View, dynamicAttributeBuffer.uint32View); + this._dynamicBuffer.setDataWithSize( + this.dynamicAttributeBuffer.float32View, + particles.length * this._dynamicStride * 4, + true + ); + if (uploadStatic) { + const staticAttributeBuffer = this.staticAttributeBuffer; + this._staticUpload(particles, staticAttributeBuffer.float32View, staticAttributeBuffer.uint32View); + this._staticBuffer.setDataWithSize( + staticAttributeBuffer.float32View, + particles.length * this._staticStride * 4, + true + ); + } + } + destroy() { + this._staticBuffer.destroy(); + this._dynamicBuffer.destroy(); + this.geometry.destroy(); + } + } + function getParticleSyncKey(properties) { + const keyGen = []; + for (const key in properties) { + const property = properties[key]; + keyGen.push(key, property.code, property.dynamic ? "d" : "s"); + } + return keyGen.join("_"); + } + + var fragment = "varying vec2 vUV;\nvarying vec4 vColor;\n\nuniform sampler2D uTexture;\n\nvoid main(void){\n vec4 color = texture2D(uTexture, vUV) * vColor;\n gl_FragColor = color;\n}"; + + var vertex = "attribute vec2 aVertex;\nattribute vec2 aUV;\nattribute vec4 aColor;\n\nattribute vec2 aPosition;\nattribute float aRotation;\n\nuniform mat3 uTranslationMatrix;\nuniform float uRound;\nuniform vec2 uResolution;\nuniform vec4 uColor;\n\nvarying vec2 vUV;\nvarying vec4 vColor;\n\nvec2 roundPixels(vec2 position, vec2 targetSize)\n{ \n return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0;\n}\n\nvoid main(void){\n float cosRotation = cos(aRotation);\n float sinRotation = sin(aRotation);\n float x = aVertex.x * cosRotation - aVertex.y * sinRotation;\n float y = aVertex.x * sinRotation + aVertex.y * cosRotation;\n\n vec2 v = vec2(x, y);\n v = v + aPosition;\n\n gl_Position = vec4((uTranslationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n if(uRound == 1.0)\n {\n gl_Position.xy = roundPixels(gl_Position.xy, uResolution);\n }\n\n vUV = aUV;\n vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uColor;\n}\n"; + + var wgsl = "\nstruct ParticleUniforms {\n uTranslationMatrix:mat3x3,\n uColor:vec4,\n uRound:f32,\n uResolution:vec2,\n};\n\nfn roundPixels(position: vec2, targetSize: vec2) -> vec2\n{\n return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0;\n}\n\n@group(0) @binding(0) var uniforms: ParticleUniforms;\n\n@group(1) @binding(0) var uTexture: texture_2d;\n@group(1) @binding(1) var uSampler : sampler;\n\nstruct VSOutput {\n @builtin(position) position: vec4,\n @location(0) uv : vec2,\n @location(1) color : vec4,\n };\n@vertex\nfn mainVertex(\n @location(0) aVertex: vec2,\n @location(1) aPosition: vec2,\n @location(2) aUV: vec2,\n @location(3) aColor: vec4,\n @location(4) aRotation: f32,\n) -> VSOutput {\n \n let v = vec2(\n aVertex.x * cos(aRotation) - aVertex.y * sin(aRotation),\n aVertex.x * sin(aRotation) + aVertex.y * cos(aRotation)\n ) + aPosition;\n\n var position = vec4((uniforms.uTranslationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n if(uniforms.uRound == 1.0) {\n position = vec4(roundPixels(position.xy, uniforms.uResolution), position.zw);\n }\n\n let vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uniforms.uColor;\n\n return VSOutput(\n position,\n aUV,\n vColor,\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2,\n @location(1) color: vec4,\n @builtin(position) position: vec4,\n) -> @location(0) vec4 {\n\n var sample = textureSample(uTexture, uSampler, uv) * color;\n \n return sample;\n}"; + + "use strict"; + class ParticleShader extends Shader { + constructor() { + const glProgram = GlProgram.from({ + vertex, + fragment + }); + const gpuProgram = GpuProgram.from({ + fragment: { + source: wgsl, + entryPoint: "mainFragment" + }, + vertex: { + source: wgsl, + entryPoint: "mainVertex" + } + }); + super({ + glProgram, + gpuProgram, + resources: { + // this will be replaced with the texture from the particle container + uTexture: Texture.WHITE.source, + // this will be replaced with the texture style from the particle container + uSampler: new TextureStyle({}), + // this will be replaced with the local uniforms from the particle container + uniforms: { + uTranslationMatrix: { value: new Matrix(), type: "mat3x3" }, + uColor: { value: new Color(16777215), type: "vec4" }, + uRound: { value: 1, type: "f32" }, + uResolution: { value: [0, 0], type: "vec2" } + } + } + }); + } + } + + "use strict"; + class ParticleContainerPipe { + /** + * @param renderer - The renderer this sprite batch works for. + * @param adaptor + */ + constructor(renderer, adaptor) { + /** @internal */ + this.state = State.for2d(); + /** Local uniforms that are used for rendering particles. */ + this.localUniforms = new UniformGroup({ + uTranslationMatrix: { value: new Matrix(), type: "mat3x3" }, + uColor: { value: new Float32Array(4), type: "vec4" }, + uRound: { value: 1, type: "f32" }, + uResolution: { value: [0, 0], type: "vec2" } + }); + this.renderer = renderer; + this.adaptor = adaptor; + this.defaultShader = new ParticleShader(); + this.state = State.for2d(); + this._managedContainers = new GCManagedHash({ renderer, type: "renderable", name: "particleContainer" }); + } + validateRenderable(_renderable) { + return false; + } + addRenderable(renderable, instructionSet) { + this.renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(renderable); + } + getBuffers(renderable) { + return renderable._gpuData[this.renderer.uid] || this._initBuffer(renderable); + } + _initBuffer(renderable) { + renderable._gpuData[this.renderer.uid] = new ParticleBuffer({ + size: renderable.particleChildren.length, + properties: renderable._properties + }); + this._managedContainers.add(renderable); + return renderable._gpuData[this.renderer.uid]; + } + updateRenderable(_renderable) { + } + execute(container) { + const children = container.particleChildren; + if (children.length === 0) { + return; + } + const renderer = this.renderer; + const buffer = this.getBuffers(container); + container.texture || (container.texture = children[0].texture); + const state = this.state; + buffer.update(children, container._childrenDirty); + container._childrenDirty = false; + state.blendMode = getAdjustedBlendModeBlend(container.blendMode, container.texture._source); + const uniforms = this.localUniforms.uniforms; + const transformationMatrix = uniforms.uTranslationMatrix; + container.worldTransform.copyTo(transformationMatrix); + transformationMatrix.prepend(renderer.globalUniforms.globalUniformData.projectionMatrix); + uniforms.uResolution = renderer.globalUniforms.globalUniformData.resolution; + uniforms.uRound = renderer._roundPixels | container._roundPixels; + color32BitToUniform( + container.groupColorAlpha, + uniforms.uColor, + 0 + ); + this.adaptor.execute(this, container); + } + /** Destroys the ParticleRenderer. */ + destroy() { + this._managedContainers.destroy(); + this.renderer = null; + if (this.defaultShader) { + this.defaultShader.destroy(); + this.defaultShader = null; + } + } + } + /** @ignore */ + ParticleContainerPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "particle" + }; + + "use strict"; + class CanvasParticleContainerPipe extends ParticleContainerPipe { + constructor(renderer) { + super(renderer, new CanvasParticleContainerAdaptor()); + } + } + /** @ignore */ + CanvasParticleContainerPipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "particle" + }; + + "use strict"; + class GlParticleContainerAdaptor { + execute(particleContainerPipe, container) { + const state = particleContainerPipe.state; + const renderer = particleContainerPipe.renderer; + const shader = container.shader || particleContainerPipe.defaultShader; + shader.resources.uTexture = container.texture._source; + shader.resources.uniforms = particleContainerPipe.localUniforms; + const gl = renderer.gl; + const buffer = particleContainerPipe.getBuffers(container); + renderer.shader.bind(shader); + renderer.state.set(state); + renderer.geometry.bind(buffer.geometry, shader.glProgram); + const byteSize = buffer.geometry.indexBuffer.data.BYTES_PER_ELEMENT; + const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT; + gl.drawElements(gl.TRIANGLES, container.particleChildren.length * 6, glType, 0); + } + } + + "use strict"; + class GlParticleContainerPipe extends ParticleContainerPipe { + constructor(renderer) { + super(renderer, new GlParticleContainerAdaptor()); + } + } + /** @ignore */ + GlParticleContainerPipe.extension = { + type: [ + ExtensionType.WebGLPipes + ], + name: "particle" + }; + + "use strict"; + class GpuParticleContainerAdaptor { + execute(particleContainerPipe, container) { + const renderer = particleContainerPipe.renderer; + const shader = container.shader || particleContainerPipe.defaultShader; + shader.groups[0] = renderer.renderPipes.uniformBatch.getUniformBindGroup(particleContainerPipe.localUniforms, true); + shader.groups[1] = renderer.texture.getTextureBindGroup(container.texture); + const state = particleContainerPipe.state; + const buffer = particleContainerPipe.getBuffers(container); + renderer.encoder.draw({ + geometry: buffer.geometry, + shader: container.shader || particleContainerPipe.defaultShader, + state, + size: container.particleChildren.length * 6 + }); + } + } + + "use strict"; + class GpuParticleContainerPipe extends ParticleContainerPipe { + constructor(renderer) { + super(renderer, new GpuParticleContainerAdaptor()); + } + } + /** @ignore */ + GpuParticleContainerPipe.extension = { + type: [ + ExtensionType.WebGPUPipes + ], + name: "particle" + }; + + "use strict"; + var __defProp$6 = Object.defineProperty; + var __getOwnPropSymbols$7 = Object.getOwnPropertySymbols; + var __hasOwnProp$7 = Object.prototype.hasOwnProperty; + var __propIsEnum$7 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$6 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$7.call(b, prop)) + __defNormalProp$6(a, prop, b[prop]); + if (__getOwnPropSymbols$7) + for (var prop of __getOwnPropSymbols$7(b)) { + if (__propIsEnum$7.call(b, prop)) + __defNormalProp$6(a, prop, b[prop]); + } + return a; + }; + const _Particle = class _Particle { + constructor(options) { + if (options instanceof Texture) { + this.texture = options; + assignWithIgnore(this, _Particle.defaultOptions, {}); + } else { + const combined = __spreadValues$6(__spreadValues$6({}, _Particle.defaultOptions), options); + assignWithIgnore(this, combined, {}); + } + } + /** + * The transparency of the particle. Values range from 0 (fully transparent) + * to 1 (fully opaque). Values outside this range are clamped. + * @example + * ```ts + * // Create a semi-transparent particle + * const particle = new Particle({ + * texture: Texture.from('particle.png'), + * alpha: 0.5 + * }); + * + * // Fade out + * particle.alpha *= 0.9; + * + * // Fade in + * particle.alpha = Math.min(particle.alpha + 0.1, 1); + * + * // Values are clamped to valid range + * particle.alpha = 1.5; // Becomes 1.0 + * particle.alpha = -0.5; // Becomes 0.0 + * + * // Animate transparency + * app.ticker.add((delta) => { + * const time = performance.now() / 1000; + * particle.alpha = 0.5 + Math.sin(time) * 0.5; // Pulse between 0-1 + * }); + * ``` + * @default 1 + * @see {@link Particle#tint} For controlling particle color + * @see {@link Particle#color} For the combined color and alpha value + */ + get alpha() { + return this._alpha; + } + set alpha(value) { + this._alpha = Math.min(Math.max(value, 0), 1); + this._updateColor(); + } + /** + * The tint color of the particle. Can be set using hex numbers or CSS color strings. + * The tint is multiplied with the texture color to create the final particle color. + * @example + * ```ts + * // Create a red particle + * const particle = new Particle({ + * texture: Texture.from('particle.png'), + * tint: 0xff0000 + * }); + * + * // Use CSS color strings + * particle.tint = '#00ff00'; // Green + * particle.tint = 'blue'; // Blue + * + * // Animate tint color + * app.ticker.add(() => { + * const time = performance.now() / 1000; + * + * // Cycle through hues + * const hue = (time * 50) % 360; + * particle.tint = `hsl(${hue}, 100%, 50%)`; + * }); + * + * // Reset to white (no tint) + * particle.tint = 0xffffff; + * ``` + * @type {ColorSource} Hex number or CSS color string + * @default 0xffffff + * @see {@link Particle#alpha} For controlling transparency + * @see {@link Particle#color} For the combined color and alpha value + * @see {@link Color} For supported color formats + */ + get tint() { + return bgr2rgb(this._tint); + } + set tint(value) { + this._tint = Color.shared.setValue(value != null ? value : 16777215).toBgrNumber(); + this._updateColor(); + } + _updateColor() { + this.color = this._tint + ((this._alpha * 255 | 0) << 24); + } + }; + /** + * Default options used when creating new particles. These values are applied when specific + * options aren't provided in the constructor. + * @example + * ```ts + * // Override defaults globally + * Particle.defaultOptions = { + * ...Particle.defaultOptions, + * anchorX: 0.5, + * anchorY: 0.5, + * alpha: 0.8 + * }; + * + * // New particles use modified defaults + * const centeredParticle = new Particle(texture); + * console.log(centeredParticle.anchorX); // 0.5 + * console.log(centeredParticle.alpha); // 0.8 + * ``` + * @see {@link ParticleOptions} For all available options + * @see {@link Particle} For the particle implementation + */ + _Particle.defaultOptions = { + anchorX: 0, + anchorY: 0, + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, + tint: 16777215, + alpha: 1 + }; + let Particle = _Particle; + + "use strict"; + const particleData = { + vertex: { + attributeName: "aVertex", + format: "float32x2", + code: ` + const texture = p.texture; + const sx = p.scaleX; + const sy = p.scaleY; + const ax = p.anchorX; + const ay = p.anchorY; + const trim = texture.trim; + const orig = texture.orig; + + if (trim) + { + w1 = trim.x - (ax * orig.width); + w0 = w1 + trim.width; + + h1 = trim.y - (ay * orig.height); + h0 = h1 + trim.height; + } + else + { + w1 = -ax * (orig.width); + w0 = w1 + orig.width; + + h1 = -ay * (orig.height); + h0 = h1 + orig.height; + } + + f32v[offset] = w1 * sx; + f32v[offset + 1] = h1 * sy; + + f32v[offset + stride] = w0 * sx; + f32v[offset + stride + 1] = h1 * sy; + + f32v[offset + (stride * 2)] = w0 * sx; + f32v[offset + (stride * 2) + 1] = h0 * sy; + + f32v[offset + (stride * 3)] = w1 * sx; + f32v[offset + (stride * 3) + 1] = h0 * sy; + `, + dynamic: false + }, + // positionData + position: { + attributeName: "aPosition", + format: "float32x2", + code: ` + var x = p.x; + var y = p.y; + + f32v[offset] = x; + f32v[offset + 1] = y; + + f32v[offset + stride] = x; + f32v[offset + stride + 1] = y; + + f32v[offset + (stride * 2)] = x; + f32v[offset + (stride * 2) + 1] = y; + + f32v[offset + (stride * 3)] = x; + f32v[offset + (stride * 3) + 1] = y; + `, + dynamic: true + }, + // rotationData + rotation: { + attributeName: "aRotation", + format: "float32", + code: ` + var rotation = p.rotation; + + f32v[offset] = rotation; + f32v[offset + stride] = rotation; + f32v[offset + (stride * 2)] = rotation; + f32v[offset + (stride * 3)] = rotation; + `, + dynamic: false + }, + // uvsData + uvs: { + attributeName: "aUV", + format: "float32x2", + code: ` + var uvs = p.texture.uvs; + + f32v[offset] = uvs.x0; + f32v[offset + 1] = uvs.y0; + + f32v[offset + stride] = uvs.x1; + f32v[offset + stride + 1] = uvs.y1; + + f32v[offset + (stride * 2)] = uvs.x2; + f32v[offset + (stride * 2) + 1] = uvs.y2; + + f32v[offset + (stride * 3)] = uvs.x3; + f32v[offset + (stride * 3) + 1] = uvs.y3; + `, + dynamic: false + }, + // tintData + color: { + attributeName: "aColor", + format: "unorm8x4", + code: ` + const c = p.color; + + u32v[offset] = c; + u32v[offset + stride] = c; + u32v[offset + (stride * 2)] = c; + u32v[offset + (stride * 3)] = c; + `, + dynamic: false + } + }; + + "use strict"; + extensions.add(GlParticleContainerPipe); + extensions.add(GpuParticleContainerPipe); + extensions.add(CanvasParticleContainerPipe); + + "use strict"; + var __defProp$5 = Object.defineProperty; + var __defProps$3 = Object.defineProperties; + var __getOwnPropDescs$3 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$6 = Object.getOwnPropertySymbols; + var __hasOwnProp$6 = Object.prototype.hasOwnProperty; + var __propIsEnum$6 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$5 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$6.call(b, prop)) + __defNormalProp$5(a, prop, b[prop]); + if (__getOwnPropSymbols$6) + for (var prop of __getOwnPropSymbols$6(b)) { + if (__propIsEnum$6.call(b, prop)) + __defNormalProp$5(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$3 = (a, b) => __defProps$3(a, __getOwnPropDescs$3(b)); + var __objRest$2 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$6.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$6) + for (var prop of __getOwnPropSymbols$6(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$6.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const emptyBounds = new Bounds(0, 0, 0, 0); + const _ParticleContainer = class _ParticleContainer extends ViewContainer { + /** + * @param options - The options for creating the sprite. + */ + constructor(options = {}) { + options = __spreadProps$3(__spreadValues$5(__spreadValues$5({}, _ParticleContainer.defaultOptions), options), { + dynamicProperties: __spreadValues$5(__spreadValues$5({}, _ParticleContainer.defaultOptions.dynamicProperties), options == null ? void 0 : options.dynamicProperties) + }); + const _a = options, { dynamicProperties, shader, roundPixels, texture, particles } = _a, rest = __objRest$2(_a, ["dynamicProperties", "shader", "roundPixels", "texture", "particles"]); + super(__spreadValues$5({ + label: "ParticleContainer" + }, rest)); + /** + * The unique identifier for the render pipe of this ParticleContainer. + * @internal + */ + this.renderPipeId = "particle"; + /** @internal */ + this.batched = false; + /** + * Indicates if the children of this ParticleContainer have changed and need to be updated. + * @internal + */ + this._childrenDirty = false; + this.texture = texture || null; + this.shader = shader; + this._properties = {}; + for (const key in particleData) { + const property = particleData[key]; + const dynamic = dynamicProperties[key]; + this._properties[key] = __spreadProps$3(__spreadValues$5({}, property), { + dynamic + }); + } + this.allowChildren = true; + this.roundPixels = roundPixels != null ? roundPixels : false; + this.particleChildren = particles != null ? particles : []; + } + /** + * Adds one or more particles to the container. The particles will be rendered using the container's shared texture + * and properties. When adding multiple particles, they must all share the same base texture. + * @example + * ```ts + * const container = new ParticleContainer(); + * + * // Add a single particle + * const particle = new Particle(Assets.get('particleTexture')); + * container.addParticle(particle); + * + * // Add multiple particles at once + * const particles = [ + * new Particle(Assets.get('particleTexture')), + * new Particle(Assets.get('particleTexture')), + * new Particle(Assets.get('particleTexture')) + * ]; + * + * container.addParticle(...particles); + * ``` + * @param children - The Particle(s) to add to the container + * @returns The first particle that was added, for method chaining + * @see {@link ParticleContainer#texture} For setting the shared texture + * @see {@link ParticleContainer#update} For updating after modifications + */ + addParticle(...children) { + for (let i = 0; i < children.length; i++) { + this.particleChildren.push(children[i]); + } + this.onViewUpdate(); + return children[0]; + } + /** + * Removes one or more particles from the container. The particles must already be children + * of this container to be removed. + * @example + * ```ts + * // Remove a single particle + * container.removeParticle(particle1); + * + * // Remove multiple particles at once + * container.removeParticle(particle2, particle3); + * ``` + * @param children - The Particle(s) to remove from the container + * @returns The first particle that was removed, for method chaining + * @see {@link ParticleContainer#particleChildren} For accessing all particles + * @see {@link ParticleContainer#removeParticles} For removing particles by index + * @see {@link ParticleContainer#removeParticleAt} For removing a particle at a specific index + */ + removeParticle(...children) { + let didRemove = false; + for (let i = 0; i < children.length; i++) { + const index = this.particleChildren.indexOf(children[i]); + if (index > -1) { + this.particleChildren.splice(index, 1); + didRemove = true; + } + } + if (didRemove) this.onViewUpdate(); + return children[0]; + } + /** + * Updates the particle container's internal state. Call this method after manually modifying + * the particleChildren array or when changing static properties of particles. + * @example + * ```ts + * // Batch modify particles + * container.particleChildren.push(...particles); + * container.update(); // Required after direct array modification + * + * // Update static properties + * container.particleChildren.forEach(particle => { + * particle.position.set( + * Math.random() * 800, + * Math.random() * 600 + * ); + * }); + * container.update(); // Required after changing static positions + * ``` + * @see {@link ParticleProperties} For configuring dynamic vs static properties + * @see {@link ParticleContainer#particleChildren} For direct array access + */ + update() { + this._childrenDirty = true; + } + onViewUpdate() { + this._childrenDirty = true; + super.onViewUpdate(); + } + /** + * Returns a static empty bounds object since ParticleContainer does not calculate bounds automatically + * for performance reasons. Use the `boundsArea` property to manually set container bounds. + * @example + * ```ts + * const container = new ParticleContainer({ + * texture: Texture.from('particle.png') + * }); + * + * // Default bounds are empty + * console.log(container.bounds); // Bounds(0, 0, 0, 0) + * + * // Set manual bounds for the particle area + * container.boundsArea = { + * minX: 0, + * minY: 0, + * maxX: 800, + * maxY: 600 + * }; + * ``` + * @readonly + * @returns {Bounds} An empty bounds object (0,0,0,0) + * @see {@link Container#boundsArea} For manually setting container bounds + * @see {@link Bounds} For bounds object structure + */ + get bounds() { + return emptyBounds; + } + /** @private */ + updateBounds() { + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * particleContainer.destroy(); + * particleContainer.destroy(true); + * particleContainer.destroy({ texture: true, textureSource: true, children: true }); + */ + destroy(options = false) { + var _a, _b, _c; + super.destroy(options); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + const texture = (_b = this.texture) != null ? _b : (_a = this.particleChildren[0]) == null ? void 0 : _a.texture; + if (texture) { + texture.destroy(destroyTextureSource); + } + } + this.texture = null; + (_c = this.shader) == null ? void 0 : _c.destroy(); + } + /** + * Removes all particles from this container that are within the begin and end indexes. + * @param beginIndex - The beginning position. + * @param endIndex - The ending position. Default value is size of the container. + * @returns - List of removed particles + */ + removeParticles(beginIndex, endIndex) { + beginIndex != null ? beginIndex : beginIndex = 0; + endIndex != null ? endIndex : endIndex = this.particleChildren.length; + const children = this.particleChildren.splice( + beginIndex, + endIndex - beginIndex + ); + this.onViewUpdate(); + return children; + } + /** + * Removes a particle from the specified index position. + * @param index - The index to get the particle from + * @returns The particle that was removed. + */ + removeParticleAt(index) { + const child = this.particleChildren.splice(index, 1); + this.onViewUpdate(); + return child[0]; + } + /** + * Adds a particle to the container at a specified index. If the index is out of bounds an error will be thrown. + * If the particle is already in this container, it will be moved to the specified index. + * @param {Container} child - The particle to add. + * @param {number} index - The absolute index where the particle will be positioned at the end of the operation. + * @returns {Container} The particle that was added. + */ + addParticleAt(child, index) { + this.particleChildren.splice(index, 0, child); + this.onViewUpdate(); + return child; + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.addParticle()` instead. + * @param {...any} _children + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + addChild(..._children) { + throw new Error( + "ParticleContainer.addChild() is not available. Please use ParticleContainer.addParticle()" + ); + } + /** + * This method is not available in ParticleContainer. + * Calling this method will throw an error. Please use `ParticleContainer.removeParticle()` instead. + * @param {...any} _children + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChild(..._children) { + throw new Error( + "ParticleContainer.removeChild() is not available. Please use ParticleContainer.removeParticle()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.removeParticles()` instead. + * @param {number} [_beginIndex] + * @param {number} [_endIndex] + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChildren(_beginIndex, _endIndex) { + throw new Error( + "ParticleContainer.removeChildren() is not available. Please use ParticleContainer.removeParticles()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.removeParticleAt()` instead. + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + removeChildAt(_index) { + throw new Error( + "ParticleContainer.removeChildAt() is not available. Please use ParticleContainer.removeParticleAt()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.getParticleAt()` instead. + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + getChildAt(_index) { + throw new Error( + "ParticleContainer.getChildAt() is not available. Please use ParticleContainer.getParticleAt()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.setParticleIndex()` instead. + * @param {ContainerChild} _child + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + setChildIndex(_child, _index) { + throw new Error( + "ParticleContainer.setChildIndex() is not available. Please use ParticleContainer.setParticleIndex()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.getParticleIndex()` instead. + * @param {ContainerChild} _child + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + getChildIndex(_child) { + throw new Error( + "ParticleContainer.getChildIndex() is not available. Please use ParticleContainer.getParticleIndex()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.addParticleAt()` instead. + * @param {ContainerChild} _child + * @param {number} _index + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + addChildAt(_child, _index) { + throw new Error( + "ParticleContainer.addChildAt() is not available. Please use ParticleContainer.addParticleAt()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. Please use `ParticleContainer.swapParticles()` instead. + * @param {ContainerChild} _child + * @param {ContainerChild} _child2 + * @ignore + */ + swapChildren(_child, _child2) { + throw new Error( + "ParticleContainer.swapChildren() is not available. Please use ParticleContainer.swapParticles()" + ); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. + * @param _child - The child to reparent + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + reparentChild(..._child) { + throw new Error("ParticleContainer.reparentChild() is not available with the particle container"); + } + /** + * This method is not available in ParticleContainer. + * + * Calling this method will throw an error. + * @param _child - The child to reparent + * @param _index - The index to reparent the child to + * @throws {Error} Always throws an error as this method is not available. + * @ignore + */ + reparentChildAt(_child, _index) { + throw new Error("ParticleContainer.reparentChildAt() is not available with the particle container"); + } + }; + /** + * Defines the default options for creating a ParticleContainer. + * @example + * ```ts + * // Change defaults globally + * ParticleContainer.defaultOptions = { + * dynamicProperties: { + * position: true, // Update positions each frame + * rotation: true, // Update rotations each frame + * vertex: false, // Static vertices + * uvs: false, // Static texture coordinates + * color: false // Static colors + * }, + * roundPixels: true // Enable pixel rounding for crisp rendering + * }; + * ``` + * @property {Record} dynamicProperties - Specifies which properties are dynamic. + * @property {boolean} roundPixels - Indicates if pixels should be rounded. + */ + _ParticleContainer.defaultOptions = { + /** Specifies which properties are dynamic. */ + dynamicProperties: { + /** Indicates if vertex positions are dynamic. */ + vertex: false, + /** Indicates if particle positions are dynamic. */ + position: true, + /** Indicates if particle rotations are dynamic. */ + rotation: false, + /** Indicates if UV coordinates are dynamic. */ + uvs: false, + /** Indicates if particle colors are dynamic. */ + color: false + }, + /** Indicates if pixels should be rounded for rendering. */ + roundPixels: false + }; + let ParticleContainer = _ParticleContainer; + + "use strict"; + class CanvasNineSliceSpritePipe { + constructor(renderer) { + this._renderer = renderer; + } + validateRenderable(_sprite) { + return false; + } + addRenderable(sprite, instructionSet) { + this._renderer.renderPipes.batch.break(instructionSet); + instructionSet.add(sprite); + } + updateRenderable(_sprite) { + } + execute(sprite) { + var _a, _b, _c, _d, _e, _f; + const renderer = this._renderer; + const contextSystem = renderer.canvasContext; + const context = contextSystem.activeContext; + context.save(); + const transform = sprite.groupTransform; + const roundPixels = renderer._roundPixels | sprite._roundPixels; + contextSystem.setContextTransform(transform, roundPixels === 1); + contextSystem.setBlendMode(sprite.groupBlendMode); + const globalColor = (_b = (_a = renderer.globalUniforms.globalUniformData) == null ? void 0 : _a.worldColor) != null ? _b : 4294967295; + const groupColorAlpha = sprite.groupColorAlpha; + const globalAlpha = (globalColor >>> 24 & 255) / 255; + const groupAlphaValue = (groupColorAlpha >>> 24 & 255) / 255; + const filterAlpha = (_d = (_c = renderer.filter) == null ? void 0 : _c.alphaMultiplier) != null ? _d : 1; + const alpha = globalAlpha * groupAlphaValue * filterAlpha; + if (alpha <= 0) { + context.restore(); + return; + } + context.globalAlpha = alpha; + const globalTint = globalColor & 16777215; + const groupTintBGR = groupColorAlpha & 16777215; + const tint = bgr2rgb(multiplyHexColors(groupTintBGR, globalTint)); + const texture = sprite.texture; + const drawSource = canvasUtils.getCanvasSource(texture); + if (!drawSource) { + context.restore(); + return; + } + const smoothProperty = contextSystem.smoothProperty; + const shouldSmooth = texture.source.style.scaleMode !== "nearest"; + if (context[smoothProperty] !== shouldSmooth) { + context[smoothProperty] = shouldSmooth; + } + const needsProcessing = tint !== 16777215 || texture.rotate !== 0; + const finalSource = needsProcessing ? canvasUtils.getTintedCanvas({ texture }, tint) : drawSource; + const { + leftWidth, + topHeight, + rightWidth, + bottomHeight, + width, + height + } = sprite; + const totalBorderWidth = leftWidth + rightWidth; + const totalBorderHeight = topHeight + bottomHeight; + const scale = Math.min( + totalBorderWidth > width ? width / totalBorderWidth : 1, + totalBorderHeight > height ? height / totalBorderHeight : 1, + 1 + ); + const destLeftWidth = leftWidth * scale; + const destRightWidth = rightWidth * scale; + const destTopHeight = topHeight * scale; + const destBottomHeight = bottomHeight * scale; + const destCenterWidth = Math.max(0, width - destLeftWidth - destRightWidth); + const destCenterHeight = Math.max(0, height - destTopHeight - destBottomHeight); + const anchor = sprite.anchor; + const resolution = (_f = (_e = texture.source._resolution) != null ? _e : texture.source.resolution) != null ? _f : 1; + let sx = texture.frame.x * resolution; + let sy = texture.frame.y * resolution; + const dx = -anchor.x * width; + const dy = -anchor.y * height; + const lw = leftWidth * resolution; + const tw = topHeight * resolution; + const rw = rightWidth * resolution; + const bw = bottomHeight * resolution; + let sw = texture.frame.width * resolution; + let sh = texture.frame.height * resolution; + if (needsProcessing) { + sx = 0; + sy = 0; + sw = finalSource.width; + sh = finalSource.height; + } + context.drawImage(finalSource, sx, sy, lw, tw, dx, dy, destLeftWidth, destTopHeight); + context.drawImage( + finalSource, + sx + lw, + sy, + sw - lw - rw, + tw, + dx + destLeftWidth, + dy, + destCenterWidth, + destTopHeight + ); + context.drawImage( + finalSource, + sx + sw - rw, + sy, + rw, + tw, + dx + width - destRightWidth, + dy, + destRightWidth, + destTopHeight + ); + context.drawImage( + finalSource, + sx, + sy + tw, + lw, + sh - tw - bw, + dx, + dy + destTopHeight, + destLeftWidth, + destCenterHeight + ); + context.drawImage( + finalSource, + sx + lw, + sy + tw, + sw - lw - rw, + sh - tw - bw, + dx + destLeftWidth, + dy + destTopHeight, + destCenterWidth, + destCenterHeight + ); + context.drawImage( + finalSource, + sx + sw - rw, + sy + tw, + rw, + sh - tw - bw, + dx + width - destRightWidth, + dy + destTopHeight, + destRightWidth, + destCenterHeight + ); + context.drawImage( + finalSource, + sx, + sy + sh - bw, + lw, + bw, + dx, + dy + height - destBottomHeight, + destLeftWidth, + destBottomHeight + ); + context.drawImage( + finalSource, + sx + lw, + sy + sh - bw, + sw - lw - rw, + bw, + dx + destLeftWidth, + dy + height - destBottomHeight, + destCenterWidth, + destBottomHeight + ); + context.drawImage( + finalSource, + sx + sw - rw, + sy + sh - bw, + rw, + bw, + dx + width - destRightWidth, + dy + height - destBottomHeight, + destRightWidth, + destBottomHeight + ); + context.restore(); + } + destroy() { + this._renderer = null; + } + } + /** @ignore */ + CanvasNineSliceSpritePipe.extension = { + type: [ + ExtensionType.CanvasPipes + ], + name: "nineSliceSprite" + }; + + "use strict"; + var __defProp$4 = Object.defineProperty; + var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols; + var __hasOwnProp$5 = Object.prototype.hasOwnProperty; + var __propIsEnum$5 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$4 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$5.call(b, prop)) + __defNormalProp$4(a, prop, b[prop]); + if (__getOwnPropSymbols$5) + for (var prop of __getOwnPropSymbols$5(b)) { + if (__propIsEnum$5.call(b, prop)) + __defNormalProp$4(a, prop, b[prop]); + } + return a; + }; + const _NineSliceGeometry = class _NineSliceGeometry extends PlaneGeometry { + constructor(options = {}) { + options = __spreadValues$4(__spreadValues$4({}, _NineSliceGeometry.defaultOptions), options); + super({ + width: options.width, + height: options.height, + verticesX: 4, + verticesY: 4 + }); + this.update(options); + } + /** + * Updates the NineSliceGeometry with the options. + * @param options - The options of the NineSliceGeometry. + */ + update(options) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + this.width = (_a = options.width) != null ? _a : this.width; + this.height = (_b = options.height) != null ? _b : this.height; + this._originalWidth = (_c = options.originalWidth) != null ? _c : this._originalWidth; + this._originalHeight = (_d = options.originalHeight) != null ? _d : this._originalHeight; + this._leftWidth = (_e = options.leftWidth) != null ? _e : this._leftWidth; + this._rightWidth = (_f = options.rightWidth) != null ? _f : this._rightWidth; + this._topHeight = (_g = options.topHeight) != null ? _g : this._topHeight; + this._bottomHeight = (_h = options.bottomHeight) != null ? _h : this._bottomHeight; + this._anchorX = (_i = options.anchor) == null ? void 0 : _i.x; + this._anchorY = (_j = options.anchor) == null ? void 0 : _j.y; + this.updateUvs(); + this.updatePositions(); + } + /** Updates the positions of the vertices. */ + updatePositions() { + const p = this.positions; + const { + width, + height, + _leftWidth, + _rightWidth, + _topHeight, + _bottomHeight, + _anchorX, + _anchorY + } = this; + const w = _leftWidth + _rightWidth; + const scaleW = width > w ? 1 : width / w; + const h = _topHeight + _bottomHeight; + const scaleH = height > h ? 1 : height / h; + const scale = Math.min(scaleW, scaleH); + const anchorOffsetX = _anchorX * width; + const anchorOffsetY = _anchorY * height; + p[0] = p[8] = p[16] = p[24] = -anchorOffsetX; + p[2] = p[10] = p[18] = p[26] = _leftWidth * scale - anchorOffsetX; + p[4] = p[12] = p[20] = p[28] = width - _rightWidth * scale - anchorOffsetX; + p[6] = p[14] = p[22] = p[30] = width - anchorOffsetX; + p[1] = p[3] = p[5] = p[7] = -anchorOffsetY; + p[9] = p[11] = p[13] = p[15] = _topHeight * scale - anchorOffsetY; + p[17] = p[19] = p[21] = p[23] = height - _bottomHeight * scale - anchorOffsetY; + p[25] = p[27] = p[29] = p[31] = height - anchorOffsetY; + this.getBuffer("aPosition").update(); + } + /** Updates the UVs of the vertices. */ + updateUvs() { + const uvs = this.uvs; + uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0; + uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0; + uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1; + uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1; + const _uvw = 1 / this._originalWidth; + const _uvh = 1 / this._originalHeight; + uvs[2] = uvs[10] = uvs[18] = uvs[26] = _uvw * this._leftWidth; + uvs[9] = uvs[11] = uvs[13] = uvs[15] = _uvh * this._topHeight; + uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - _uvw * this._rightWidth; + uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - _uvh * this._bottomHeight; + this.getBuffer("aUV").update(); + } + }; + /** The default options for the NineSliceGeometry. */ + _NineSliceGeometry.defaultOptions = { + /** The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */ + width: 100, + /** The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */ + height: 100, + /** The width of the left column. */ + leftWidth: 10, + /** The height of the top row. */ + topHeight: 10, + /** The width of the right column. */ + rightWidth: 10, + /** The height of the bottom row. */ + bottomHeight: 10, + /** The original width of the texture */ + originalWidth: 100, + /** The original height of the texture */ + originalHeight: 100 + }; + let NineSliceGeometry = _NineSliceGeometry; + + "use strict"; + class NineSliceSpriteGpuData extends BatchableMesh { + constructor() { + super(); + this.geometry = new NineSliceGeometry(); + } + destroy() { + this.geometry.destroy(); + } + } + class NineSliceSpritePipe { + constructor(renderer) { + this._renderer = renderer; + this._managedSprites = new GCManagedHash({ renderer, type: "renderable", name: "nineSliceSprite" }); + } + addRenderable(sprite, instructionSet) { + const gpuSprite = this._getGpuSprite(sprite); + if (sprite.didViewUpdate) this._updateBatchableSprite(sprite, gpuSprite); + this._renderer.renderPipes.batch.addToBatch(gpuSprite, instructionSet); + } + updateRenderable(sprite) { + const gpuSprite = this._getGpuSprite(sprite); + if (sprite.didViewUpdate) this._updateBatchableSprite(sprite, gpuSprite); + gpuSprite._batcher.updateElement(gpuSprite); + } + validateRenderable(sprite) { + const gpuSprite = this._getGpuSprite(sprite); + return !gpuSprite._batcher.checkAndUpdateTexture( + gpuSprite, + sprite._texture + ); + } + _updateBatchableSprite(sprite, batchableSprite) { + batchableSprite.geometry.update(sprite); + batchableSprite.setTexture(sprite._texture); + } + _getGpuSprite(sprite) { + return sprite._gpuData[this._renderer.uid] || this._initGPUSprite(sprite); + } + _initGPUSprite(sprite) { + const gpuData = sprite._gpuData[this._renderer.uid] = new NineSliceSpriteGpuData(); + const batchableMesh = gpuData; + batchableMesh.renderable = sprite; + batchableMesh.transform = sprite.groupTransform; + batchableMesh.texture = sprite._texture; + batchableMesh.roundPixels = this._renderer._roundPixels | sprite._roundPixels; + this._managedSprites.add(sprite); + if (!sprite.didViewUpdate) { + this._updateBatchableSprite(sprite, batchableMesh); + } + return gpuData; + } + destroy() { + this._managedSprites.destroy(); + this._renderer = null; + } + } + /** @ignore */ + NineSliceSpritePipe.extension = { + type: [ + ExtensionType.WebGLPipes, + ExtensionType.WebGPUPipes + ], + name: "nineSliceSprite" + }; + + "use strict"; + extensions.add(CanvasNineSliceSpritePipe); + extensions.add(NineSliceSpritePipe); + + "use strict"; + var __defProp$3 = Object.defineProperty; + var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols; + var __hasOwnProp$4 = Object.prototype.hasOwnProperty; + var __propIsEnum$4 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$3 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$4.call(b, prop)) + __defNormalProp$3(a, prop, b[prop]); + if (__getOwnPropSymbols$4) + for (var prop of __getOwnPropSymbols$4(b)) { + if (__propIsEnum$4.call(b, prop)) + __defNormalProp$3(a, prop, b[prop]); + } + return a; + }; + var __objRest$1 = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$4.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$4) + for (var prop of __getOwnPropSymbols$4(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$4.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + const _NineSliceSprite = class _NineSliceSprite extends ViewContainer { + constructor(options) { + var _b, _c, _d, _e, _f, _g, _h, _i, _j, _k; + if (options instanceof Texture) { + options = { texture: options }; + } + const _a = options, { + width, + height, + anchor, + leftWidth, + rightWidth, + topHeight, + bottomHeight, + texture, + roundPixels + } = _a, rest = __objRest$1(_a, [ + "width", + "height", + "anchor", + "leftWidth", + "rightWidth", + "topHeight", + "bottomHeight", + "texture", + "roundPixels" + ]); + super(__spreadValues$3({ + label: "NineSliceSprite" + }, rest)); + /** @internal */ + this.renderPipeId = "nineSliceSprite"; + /** @internal */ + this.batched = true; + this._leftWidth = (_c = leftWidth != null ? leftWidth : (_b = texture == null ? void 0 : texture.defaultBorders) == null ? void 0 : _b.left) != null ? _c : NineSliceGeometry.defaultOptions.leftWidth; + this._topHeight = (_e = topHeight != null ? topHeight : (_d = texture == null ? void 0 : texture.defaultBorders) == null ? void 0 : _d.top) != null ? _e : NineSliceGeometry.defaultOptions.topHeight; + this._rightWidth = (_g = rightWidth != null ? rightWidth : (_f = texture == null ? void 0 : texture.defaultBorders) == null ? void 0 : _f.right) != null ? _g : NineSliceGeometry.defaultOptions.rightWidth; + this._bottomHeight = (_i = bottomHeight != null ? bottomHeight : (_h = texture == null ? void 0 : texture.defaultBorders) == null ? void 0 : _h.bottom) != null ? _i : NineSliceGeometry.defaultOptions.bottomHeight; + this._width = (_j = width != null ? width : texture.width) != null ? _j : NineSliceGeometry.defaultOptions.width; + this._height = (_k = height != null ? height : texture.height) != null ? _k : NineSliceGeometry.defaultOptions.height; + this.allowChildren = false; + this.texture = texture != null ? texture : _NineSliceSprite.defaultOptions.texture; + this.roundPixels = roundPixels != null ? roundPixels : false; + this._anchor = new ObservablePoint( + { + _onUpdate: () => { + this.onViewUpdate(); + } + } + ); + if (anchor) { + this.anchor = anchor; + } else if (this.texture.defaultAnchor) { + this.anchor = this.texture.defaultAnchor; + } + } + /** + * The anchor sets the origin point of the sprite. The default value is taken from the {@link Texture} + * and passed to the constructor. + * + * - The default is `(0,0)`, this means the sprite's origin is the top left. + * - Setting the anchor to `(0.5,0.5)` means the sprite's origin is centered. + * - Setting the anchor to `(1,1)` would mean the sprite's origin point will be the bottom right corner. + * + * If you pass only single parameter, it will set both x and y to the same value as shown in the example below. + * @example + * ```ts + * // Center the anchor point + * sprite.anchor = 0.5; // Sets both x and y to 0.5 + * sprite.position.set(400, 300); // Sprite will be centered at this position + * + * // Set specific x/y anchor points + * sprite.anchor = { + * x: 1, // Right edge + * y: 0 // Top edge + * }; + * + * // Using individual coordinates + * sprite.anchor.set(0.5, 1); // Center-bottom + * + * // For rotation around center + * sprite.anchor.set(0.5); + * sprite.rotation = Math.PI / 4; // 45 degrees around center + * + * // For scaling from center + * sprite.anchor.set(0.5); + * sprite.scale.set(2); // Scales from center point + * ``` + */ + get anchor() { + return this._anchor; + } + set anchor(value) { + typeof value === "number" ? this._anchor.set(value) : this._anchor.copyFrom(value); + } + /** + * The width of the NineSliceSprite, setting this will actually modify the vertices and UV's of this plane. + * The width affects how the middle sections are scaled. + * @example + * ```ts + * // Create a nine-slice sprite with fixed width + * const panel = new NineSliceSprite({ + * texture: Texture.from('panel.png'), + * width: 200 // Sets initial width + * }); + * + * // Adjust width dynamically + * panel.width = 300; // Stretches middle sections + * ``` + * @see {@link NineSliceSprite#setSize} For setting both width and height efficiently + * @see {@link NineSliceSprite#height} For setting height + */ + get width() { + return this._width; + } + set width(value) { + this._width = value; + this.onViewUpdate(); + } + /** + * The height of the NineSliceSprite, setting this will actually modify the vertices and UV's of this plane. + * The height affects how the middle sections are scaled. + * @example + * ```ts + * // Create a nine-slice sprite with fixed height + * const panel = new NineSliceSprite({ + * texture: Texture.from('panel.png'), + * height: 150 // Sets initial height + * }); + * + * // Adjust height dynamically + * panel.height = 200; // Stretches middle sections + * + * // Create responsive UI element + * const dialog = new NineSliceSprite({ + * texture: Texture.from('dialog.png'), + * topHeight: 30, + * bottomHeight: 30, + * height: parent.height * 0.5 // 50% of parent height + * }); + * ``` + * @see {@link NineSliceSprite#setSize} For setting both width and height efficiently + * @see {@link NineSliceSprite#width} For setting width + */ + get height() { + return this._height; + } + set height(value) { + this._height = value; + this.onViewUpdate(); + } + /** + * Sets the size of the NineSliceSprite to the specified width and height. + * This method directly modifies the vertices and UV coordinates of the sprite. + * + * Using this is more efficient than setting width and height separately as it only triggers one update. + * @example + * ```ts + * // Set to specific dimensions + * panel.setSize(300, 200); // Width: 300, Height: 200 + * + * // Set uniform size + * panel.setSize(200); // Makes a square 200x200 + * + * // Set size using object + * panel.setSize({ + * width: 400, + * height: 300 + * }); + * ``` + * @param value - This can be either a number or a Size object with width/height properties + * @param height - The height to set. Defaults to the value of `width` if not provided + * @see {@link NineSliceSprite#width} For setting width only + * @see {@link NineSliceSprite#height} For setting height only + */ + setSize(value, height) { + var _a; + if (typeof value === "object") { + height = (_a = value.height) != null ? _a : value.width; + value = value.width; + } + this._width = value; + this._height = height != null ? height : value; + this.onViewUpdate(); + } + /** + * Retrieves the size of the NineSliceSprite as a [Size]{@link Size} object. + * This method is more efficient than getting width and height separately. + * @example + * ```ts + * // Get basic size + * const size = panel.getSize(); + * console.log(`Size: ${size.width}x${size.height}`); + * + * // Reuse existing size object + * const reuseSize = { width: 0, height: 0 }; + * panel.getSize(reuseSize); + * ``` + * @param out - Optional object to store the size in, to avoid allocating a new object + * @returns The size of the NineSliceSprite + * @see {@link NineSliceSprite#width} For getting just the width + * @see {@link NineSliceSprite#height} For getting just the height + * @see {@link NineSliceSprite#setSize} For setting both width and height efficiently + */ + getSize(out) { + out || (out = {}); + out.width = this._width; + out.height = this._height; + return out; + } + /** + * Width of the left vertical bar (A). + * Controls the size of the left edge that remains unscaled + * @example + * ```ts + * const sprite = new NineSliceSprite({ ..., leftWidth: 20 }); + * sprite.leftWidth = 20; // Set left border width + * ``` + * @default 10 + */ + get leftWidth() { + return this._leftWidth; + } + set leftWidth(value) { + this._leftWidth = value; + this.onViewUpdate(); + } + /** + * Height of the top horizontal bar (C). + * Controls the size of the top edge that remains unscaled + * @example + * ```ts + * const sprite = new NineSliceSprite({ ..., topHeight: 20 }); + * sprite.topHeight = 20; // Set top border height + * ``` + * @default 10 + */ + get topHeight() { + return this._topHeight; + } + set topHeight(value) { + this._topHeight = value; + this.onViewUpdate(); + } + /** + * Width of the right vertical bar (B). + * Controls the size of the right edge that remains unscaled + * @example + * ```ts + * const sprite = new NineSliceSprite({ ..., rightWidth: 20 }); + * sprite.rightWidth = 20; // Set right border width + * ``` + * @default 10 + */ + get rightWidth() { + return this._rightWidth; + } + set rightWidth(value) { + this._rightWidth = value; + this.onViewUpdate(); + } + /** + * Height of the bottom horizontal bar (D). + * Controls the size of the bottom edge that remains unscaled + * @example + * ```ts + * const sprite = new NineSliceSprite({ ..., bottomHeight: 20 }); + * sprite.bottomHeight = 20; // Set bottom border height + * ``` + * @default 10 + */ + get bottomHeight() { + return this._bottomHeight; + } + set bottomHeight(value) { + this._bottomHeight = value; + this.onViewUpdate(); + } + /** + * The texture to use on the NineSliceSprite. + * ```ts + * // Create a sprite with a texture + * const sprite = new NineSliceSprite({ + * texture: Texture.from('path/to/image.png') + * }); + * // Update the texture later + * sprite.texture = Texture.from('path/to/another-image.png'); + * ``` + * @default Texture.EMPTY + */ + get texture() { + return this._texture; + } + set texture(value) { + value || (value = Texture.EMPTY); + const currentTexture = this._texture; + if (currentTexture === value) return; + if (currentTexture && currentTexture.dynamic) currentTexture.off("update", this.onViewUpdate, this); + if (value.dynamic) value.on("update", this.onViewUpdate, this); + this._texture = value; + this.onViewUpdate(); + } + /** + * The original width of the texture before any nine-slice scaling. + * This is the width of the source texture used to create the nine-slice sprite. + * @example + * ```ts + * // Get original dimensions + * console.log(`Original size: ${sprite.originalWidth}x${sprite.originalHeight}`); + * + * // Use for relative scaling + * sprite.width = sprite.originalWidth * 2; // Double the original width + * + * // Reset to original size + * sprite.setSize(sprite.originalWidth, sprite.originalHeight); + * ``` + * @readonly + * @see {@link NineSliceSprite#width} For the current displayed width + * @see {@link Texture#width} For direct texture width access + * @returns The original width of the texture + */ + get originalWidth() { + return this._texture.width; + } + /** + * The original height of the texture before any nine-slice scaling. + * This is the height of the source texture used to create the nine-slice sprite. + * @example + * ```ts + * // Get original dimensions + * console.log(`Original size: ${sprite.originalWidth}x${sprite.originalHeight}`); + * + * // Use for relative scaling + * sprite.height = sprite.originalHeight * 2; // Double the original height + * + * // Reset to original size + * sprite.setSize(sprite.originalWidth, sprite.originalHeight); + * ``` + * @readonly + * @see {@link NineSliceSprite#height} For the current displayed height + * @see {@link Texture#height} For direct texture height access + * @returns The original height of the texture + */ + get originalHeight() { + return this._texture.height; + } + /** + * Destroys this sprite renderable and optionally its texture. + * @param options - Options parameter. A boolean will act as if all options + * have been set to that value + * @example + * nineSliceSprite.destroy(); + * nineSliceSprite.destroy(true); + * nineSliceSprite.destroy({ texture: true, textureSource: true }); + */ + destroy(options) { + super.destroy(options); + const destroyTexture = typeof options === "boolean" ? options : options == null ? void 0 : options.texture; + if (destroyTexture) { + const destroyTextureSource = typeof options === "boolean" ? options : options == null ? void 0 : options.textureSource; + this._texture.destroy(destroyTextureSource); + } + this._texture = null; + } + /** @private */ + updateBounds() { + const bounds = this._bounds; + const anchor = this._anchor; + const width = this._width; + const height = this._height; + bounds.minX = -anchor._x * width; + bounds.maxX = bounds.minX + width; + bounds.minY = -anchor._y * height; + bounds.maxY = bounds.minY + height; + } + }; + /** + * The default options used to override initial values of any options passed in the constructor. + * These values are used as fallbacks when specific options are not provided. + * @example + * ```ts + * // Override default options globally + * NineSliceSprite.defaultOptions.texture = Texture.from('defaultButton.png'); + * // Create sprite with default texture + * const sprite = new NineSliceSprite({...}); + * // sprite will use 'defaultButton.png' as its texture + * + * // Reset to empty texture + * NineSliceSprite.defaultOptions.texture = Texture.EMPTY; + * ``` + * @type {NineSliceSpriteOptions} + * @see {@link NineSliceSpriteOptions} For all available options + * @see {@link Texture#defaultBorders} For texture-level border settings + */ + _NineSliceSprite.defaultOptions = { + /** @default Texture.EMPTY */ + texture: Texture.EMPTY + }; + let NineSliceSprite = _NineSliceSprite; + class NineSlicePlane extends NineSliceSprite { + constructor(...args) { + let options = args[0]; + if (options instanceof Texture) { + deprecation(v8_0_0, "NineSlicePlane now uses the options object {texture, leftWidth, rightWidth, topHeight, bottomHeight}"); + options = { + texture: options, + leftWidth: args[1], + topHeight: args[2], + rightWidth: args[3], + bottomHeight: args[4] + }; + } + deprecation(v8_0_0, "NineSlicePlane is deprecated. Use NineSliceSprite instead."); + super(options); + } + } + + "use strict"; + class BitmapFont extends AbstractBitmapFont { + constructor(options, url) { + var _a; + super(); + const { textures, data } = options; + Object.keys(data.pages).forEach((key) => { + const pageData = data.pages[parseInt(key, 10)]; + const texture = textures[pageData.id]; + this.pages.push({ texture }); + }); + Object.keys(data.chars).forEach((key) => { + var _a2; + const charData = data.chars[key]; + const { + frame: textureFrame, + source: textureSource, + rotate: textureRotate + } = textures[charData.page]; + const frame = groupD8.transformRectCoords( + charData, + textureFrame, + textureRotate, + new Rectangle() + ); + const texture = new Texture({ + frame, + orig: new Rectangle(0, 0, charData.width, charData.height), + source: textureSource, + rotate: textureRotate + }); + this.chars[key] = { + id: key.codePointAt(0), + xOffset: charData.xOffset, + yOffset: charData.yOffset, + xAdvance: charData.xAdvance, + kerning: (_a2 = charData.kerning) != null ? _a2 : {}, + texture + }; + }); + this.baseRenderedFontSize = data.fontSize; + this.baseMeasurementFontSize = data.fontSize; + this.fontMetrics = { + ascent: 0, + descent: 0, + fontSize: data.fontSize + }; + this.baseLineOffset = data.baseLineOffset; + this.lineHeight = data.lineHeight; + this.fontFamily = data.fontFamily; + this.distanceField = (_a = data.distanceField) != null ? _a : { + type: "none", + range: 0 + }; + this.url = url; + } + /** Destroys the BitmapFont object. */ + destroy() { + super.destroy(); + for (let i = 0; i < this.pages.length; i++) { + const { texture } = this.pages[i]; + texture.destroy(true); + } + this.pages = null; + } + /** + * Generates and installs a bitmap font with the specified options. + * The font will be cached and available for use in BitmapText objects. + * @param options - Setup options for font generation + * @returns Installed font instance + * @example + * ```ts + * // Install a basic font + * BitmapFont.install({ + * name: 'Title', + * style: { + * fontFamily: 'Arial', + * fontSize: 32, + * fill: '#ffffff' + * } + * }); + * + * // Install with advanced options + * BitmapFont.install({ + * name: 'Custom', + * style: { + * fontFamily: 'Arial', + * fontSize: 24, + * fill: '#00ff00', + * stroke: { color: '#000000', width: 2 } + * }, + * chars: [['a', 'z'], ['A', 'Z'], ['0', '9']], + * resolution: 2, + * padding: 4, + * textureStyle: { + * scaleMode: 'nearest' + * } + * }); + * ``` + */ + static install(options) { + BitmapFontManager.install(options); + } + /** + * Uninstalls a bitmap font from the cache. + * This frees up memory and resources associated with the font. + * @param name - The name of the bitmap font to uninstall + * @example + * ```ts + * // Remove a font when it's no longer needed + * BitmapFont.uninstall('MyCustomFont'); + * + * // Clear multiple fonts + * ['Title', 'Heading', 'Body'].forEach(BitmapFont.uninstall); + * ``` + */ + static uninstall(name) { + BitmapFontManager.uninstall(name); + } + } + + var BitmapFont$1 = { + __proto__: null, + BitmapFont: BitmapFont + }; + + "use strict"; + function bitmapTextSplit(options) { + const { text, style, chars: existingChars } = options; + const textStyle = style; + const font = BitmapFontManager.getFont(text, textStyle); + const segments = CanvasTextMetrics.graphemeSegmenter(text); + const layout = getBitmapTextLayout(segments, textStyle, font, true); + const scale = layout.scale; + const chars = []; + const words = []; + const lines = []; + const lineHeight = style.lineHeight ? style.lineHeight : font.lineHeight * scale; + let yOffset = 0; + for (const line of layout.lines) { + if (line.chars.length === 0) continue; + const lineContainer = new Container({ label: "line" }); + lineContainer.y = yOffset; + lines.push(lineContainer); + let currentWordContainer = new Container({ label: "word" }); + let currentWordStartIndex = 0; + for (let i = 0; i < line.chars.length; i++) { + const char = line.chars[i]; + if (!char) continue; + const charData = font.chars[char]; + if (!charData) continue; + const isSpace = char === " "; + const isLastChar = i === line.chars.length - 1; + let charInstance; + if (existingChars.length > 0) { + charInstance = existingChars.shift(); + charInstance.text = char; + charInstance.style = textStyle; + charInstance.label = `char-${char}`; + charInstance.x = line.charPositions[i] * scale - line.charPositions[currentWordStartIndex] * scale; + } else { + charInstance = new BitmapText({ + text: char, + style: textStyle, + label: `char-${char}`, + x: line.charPositions[i] * scale - line.charPositions[currentWordStartIndex] * scale + }); + } + if (!isSpace) { + chars.push(charInstance); + currentWordContainer.addChild(charInstance); + } + if (isSpace || isLastChar) { + if (currentWordContainer.children.length > 0) { + currentWordContainer.x = line.charPositions[currentWordStartIndex] * scale; + words.push(currentWordContainer); + lineContainer.addChild(currentWordContainer); + currentWordContainer = new Container({ label: "word" }); + currentWordStartIndex = i + 1; + } + } + } + yOffset += lineHeight; + } + return { chars, lines, words }; + } + + "use strict"; + var __getOwnPropSymbols$3 = Object.getOwnPropertySymbols; + var __hasOwnProp$3 = Object.prototype.hasOwnProperty; + var __propIsEnum$3 = Object.prototype.propertyIsEnumerable; + var __objRest = (source, exclude) => { + var target = {}; + for (var prop in source) + if (__hasOwnProp$3.call(source, prop) && exclude.indexOf(prop) < 0) + target[prop] = source[prop]; + if (source != null && __getOwnPropSymbols$3) + for (var prop of __getOwnPropSymbols$3(source)) { + if (exclude.indexOf(prop) < 0 && __propIsEnum$3.call(source, prop)) + target[prop] = source[prop]; + } + return target; + }; + class AbstractSplitText extends Container { + constructor(config) { + const _a = config, { + text, + style, + autoSplit, + lineAnchor, + wordAnchor, + charAnchor + } = _a, options = __objRest(_a, [ + "text", + "style", + "autoSplit", + "lineAnchor", + "wordAnchor", + "charAnchor" + ]); + super(options); + this._dirty = false; + this._canReuseChars = false; + this.chars = []; + this.words = []; + this.lines = []; + this._originalText = text; + this._autoSplit = autoSplit; + this._lineAnchor = lineAnchor; + this._wordAnchor = wordAnchor; + this._charAnchor = charAnchor; + this.style = style; + } + /** + * Splits the text into lines, words, and characters. + * Call this manually when autoSplit is false. + * @example Manual Splitting + * ```ts + * const text = new SplitText({ + * text: 'Manual Update', + * autoSplit: false + * }); + * + * text.text = 'New Content'; + * text.style = { fontSize: 32 }; + * text.split(); // Apply changes + * ``` + */ + split() { + const res = this.splitFn(); + this.chars = res.chars; + this.words = res.words; + this.lines = res.lines; + this.addChild(...this.lines); + this.charAnchor = this._charAnchor; + this.wordAnchor = this._wordAnchor; + this.lineAnchor = this._lineAnchor; + this._dirty = false; + this._canReuseChars = true; + } + get text() { + return this._originalText; + } + /** + * Gets or sets the text content. + * Setting new text triggers splitting if autoSplit is true. + * > [!NOTE] Setting this frequently can have a performance impact, especially with large texts and canvas text. + * @example Dynamic Text Updates + * ```ts + * const text = new SplitText({ + * text: 'Original', + * autoSplit: true + * }); + * + * // Auto-splits on change + * text.text = 'Updated Content'; + * + * // Manual update + * text.autoSplit = false; + * text.text = 'Manual Update'; + * text.split(); + * ``` + */ + set text(value) { + this._originalText = value; + this.lines.forEach((line) => line.destroy({ children: true })); + this.lines.length = 0; + this.words.length = 0; + this.chars.length = 0; + this._canReuseChars = false; + this.onTextUpdate(); + } + _setOrigin(value, elements, property) { + let originPoint; + if (typeof value === "number") { + originPoint = { x: value, y: value }; + } else { + originPoint = { x: value.x, y: value.y }; + } + elements.forEach((element) => { + const localBounds = element.getLocalBounds(); + const originX = localBounds.minX + localBounds.width * originPoint.x; + const originY = localBounds.minY + localBounds.height * originPoint.y; + element.origin.set(originX, originY); + }); + this[property] = value; + } + /** + * Gets or sets the transform anchor for line segments. + * The anchor point determines the center of rotation and scaling for each line. + * @example Setting Line Anchors + * ```ts + * // Center rotation/scaling + * text.lineAnchor = 0.5; + * + * // Rotate/scale from top-right corner + * text.lineAnchor = { x: 1, y: 0 }; + * + * // Custom anchor point + * text.lineAnchor = { + * x: 0.2, // 20% from left + * y: 0.8 // 80% from top + * }; + * ``` + */ + get lineAnchor() { + return this._lineAnchor; + } + set lineAnchor(value) { + this._setOrigin(value, this.lines, "_lineAnchor"); + } + /** + * Gets or sets the transform anchor for word segments. + * The anchor point determines the center of rotation and scaling for each word. + * @example + * ```ts + * // Center each word + * text.wordAnchor = 0.5; + * + * // Scale from bottom-left + * text.wordAnchor = { x: 0, y: 1 }; + * + * // Rotate around custom point + * text.wordAnchor = { + * x: 0.75, // 75% from left + * y: 0.5 // Middle vertically + * }; + * ``` + */ + get wordAnchor() { + return this._wordAnchor; + } + set wordAnchor(value) { + this._setOrigin(value, this.words, "_wordAnchor"); + } + /** + * Gets or sets the transform anchor for character segments. + * The anchor point determines the center of rotation and scaling for each character. + * @example Setting Character Anchors + * ```ts + * // Center each character + * text.charAnchor = 0.5; + * + * // Rotate from top-center + * text.charAnchor = { x: 0.5, y: 0 }; + * + * // Scale from bottom-right + * text.charAnchor = { x: 1, y: 1 }; + * ``` + * @example Animation with Anchors + * ```ts + * // Rotate characters around their centers + * text.charAnchor = 0.5; + * text.chars.forEach((char, i) => { + * gsap.to(char, { + * rotation: Math.PI * 2, + * duration: 1, + * delay: i * 0.1, + * repeat: -1 + * }); + * }); + * ``` + */ + get charAnchor() { + return this._charAnchor; + } + set charAnchor(value) { + this._setOrigin(value, this.chars, "_charAnchor"); + } + get style() { + return this._style; + } + /** + * The style configuration for the text. + * Can be a TextStyle instance or a configuration object. + * @example + * ```ts + * const text = new Text({ + * text: 'Styled Text', + * style: { + * fontSize: 24, + * fill: 0xff1010, // Red color + * fontFamily: 'Arial', + * align: 'center', // Center alignment + * stroke: { color: '#4a1850', width: 5 }, // Purple stroke + * dropShadow: { + * color: '#000000', // Black shadow + * blur: 4, // Shadow blur + * distance: 6 // Shadow distance + * } + * } + * }); + * // Update style dynamically + * text.style = { + * fontSize: 30, // Change font size + * fill: 0x00ff00, // Change color to green + * align: 'right', // Change alignment to right + * stroke: { color: '#000000', width: 2 }, // Add black stroke + * } + */ + set style(style) { + style || (style = {}); + this._style = new TextStyle(style); + this.styleChanged(); + } + /** + * Used to notify the text that the style has changed. + * This will re-split the text and re-apply the style. + * @example + * ```ts + * text.style.fontSize = 32; + * text.styleChanged(); + * ``` + */ + styleChanged() { + this.words.forEach((word) => word.destroy()); + this.words.length = 0; + this.lines.forEach((line) => line.destroy()); + this.lines.length = 0; + this._canReuseChars = true; + this.onTextUpdate(); + } + onTextUpdate() { + this._dirty = true; + if (this._autoSplit) { + this.split(); + } + } + /** + * Destroys the SplitText instance and all its resources. + * Cleans up all segment arrays, event listeners, and optionally the text style. + * @param options - Destroy configuration options + * @example + * ```ts + * // Clean up everything + * text.destroy({ children: true, texture: true, style: true }); + * + * // Remove from parent but keep style + * text.destroy({ children: true, style: false }); + * ``` + */ + destroy(options) { + super.destroy(options); + this.chars = []; + this.words = []; + this.lines = []; + if (typeof options === "boolean" ? options : options == null ? void 0 : options.style) { + this._style.destroy(options); + } + this._style = null; + this._originalText = ""; + } + } + + "use strict"; + var __defProp$2 = Object.defineProperty; + var __defProps$2 = Object.defineProperties; + var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$2 = Object.getOwnPropertySymbols; + var __hasOwnProp$2 = Object.prototype.hasOwnProperty; + var __propIsEnum$2 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$2 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$2.call(b, prop)) + __defNormalProp$2(a, prop, b[prop]); + if (__getOwnPropSymbols$2) + for (var prop of __getOwnPropSymbols$2(b)) { + if (__propIsEnum$2.call(b, prop)) + __defNormalProp$2(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b)); + const _SplitBitmapText = class _SplitBitmapText extends AbstractSplitText { + constructor(config) { + var _a, _b, _c; + const completeOptions = __spreadValues$2(__spreadValues$2({}, _SplitBitmapText.defaultOptions), config); + (_a = completeOptions.style) != null ? _a : completeOptions.style = {}; + (_c = (_b = completeOptions.style).fill) != null ? _c : _b.fill = 16777215; + super(completeOptions); + } + /** + * Creates a SplitBitmapText instance from an existing text object. + * Useful for converting standard Text or BitmapText objects into segmented versions. + * @param text - The source text object to convert + * @param options - Additional splitting options + * @returns A new SplitBitmapText instance + * @example + * ```ts + * const bitmapText = new BitmapText({ + * text: 'Bitmap Text', + * style: { fontFamily: 'Arial' } + * }); + * + * const segmented = SplitBitmapText.from(bitmapText); + * + * // with additional options + * const segmentedWithOptions = SplitBitmapText.from(bitmapText, { + * autoSplit: false, + * lineAnchor: 0.5, + * wordAnchor: { x: 0, y: 0.5 }, + * }) + * ``` + */ + static from(text, options) { + const completeOptions = __spreadProps$2(__spreadValues$2(__spreadValues$2({}, _SplitBitmapText.defaultOptions), options), { + text: text.text, + style: new TextStyle(text.style) + }); + const splitText = new _SplitBitmapText(__spreadValues$2({}, completeOptions)); + const anchor = text.anchor; + if (anchor.x !== 0 || anchor.y !== 0) { + splitText.pivot.set( + splitText.width * anchor.x, + splitText.height * anchor.y + ); + } + return splitText; + } + splitFn() { + return bitmapTextSplit({ + text: this._originalText, + style: this._style, + chars: this._canReuseChars ? this.chars : [] + }); + } + }; + /** + * Default configuration options for SplitBitmapText instances. + * @example + * ```ts + * // Override defaults globally + * SplitBitmapText.defaultOptions = { + * autoSplit: false, + * lineAnchor: 0.5, // Center alignment + * wordAnchor: { x: 0, y: 0.5 }, // Left-center + * charAnchor: { x: 0.5, y: 1 } // Bottom-center + * }; + * ``` + */ + _SplitBitmapText.defaultOptions = { + autoSplit: true, + // Auto-update on text/style changes + lineAnchor: 0, + // Top-left alignment + wordAnchor: 0, + // Top-left alignment + charAnchor: 0 + // Top-left alignment + }; + let SplitBitmapText = _SplitBitmapText; + + "use strict"; + function getAlignmentOffset(alignment, lineWidth, largestLine) { + switch (alignment) { + case "center": + return (largestLine - lineWidth) / 2; + case "right": + return largestLine - lineWidth; + case "left": + default: + return 0; + } + } + function isNewlineCharacter(char) { + return char === "\r" || char === "\n" || char === "\r\n"; + } + function groupTextSegments(segments, measuredText, textStyle) { + const groupedSegments = []; + let currentLine = measuredText.lines[0]; + let matchedLine = ""; + let chars = []; + let lineCount = 0; + textStyle.wordWrap = false; + segments.forEach((segment) => { + const isWhitespace = /^\s*$/.test(segment); + const isNewline = isNewlineCharacter(segment); + const isSpaceAtStart = matchedLine.length === 0 && isWhitespace; + if (isWhitespace && !isNewline && isSpaceAtStart) { + return; + } + if (!isNewline) matchedLine += segment; + const metric = CanvasTextMetrics.measureText(segment, textStyle); + chars.push({ char: segment, metric }); + if (matchedLine.length >= currentLine.length) { + groupedSegments.push({ + line: matchedLine, + chars, + width: chars.reduce((acc, seg) => acc + seg.metric.width, 0) + }); + chars = []; + matchedLine = ""; + lineCount++; + currentLine = measuredText.lines[lineCount]; + } + }); + return groupedSegments; + } + function canvasTextSplit(options) { + var _a, _b, _c, _d; + const { text, style, chars: existingChars } = options; + const textStyle = style; + const measuredText = CanvasTextMetrics.measureText(text, textStyle); + const segments = CanvasTextMetrics.graphemeSegmenter(text); + const groupedSegments = groupTextSegments(segments, measuredText, textStyle.clone()); + const alignment = textStyle.align; + const maxLineWidth = measuredText.lineWidths.reduce((max, line) => Math.max(max, line), 0); + const isSingleLine = measuredText.lines.length === 1; + const useWordWrapWidth = !isSingleLine && textStyle.wordWrap; + const alignWidth = useWordWrapWidth ? textStyle.wordWrapWidth : maxLineWidth; + const fillGradient = (_a = textStyle._fill) == null ? void 0 : _a.fill; + const strokeGradient = (_b = textStyle._stroke) == null ? void 0 : _b.fill; + const hasFillGradient = fillGradient instanceof FillGradient; + const hasStrokeGradient = strokeGradient instanceof FillGradient; + const hasGradient = hasFillGradient || hasStrokeGradient; + const hasLocalGradient = hasFillGradient && fillGradient.textureSpace === "local" || hasStrokeGradient && strokeGradient.textureSpace === "local"; + const fullTextWidth = measuredText.width; + const fullTextHeight = measuredText.height; + const baseCharStyle = textStyle.clone(); + baseCharStyle.align = "left"; + let trimOffsetX = 0; + let trimOffsetY = 0; + if (baseCharStyle.trim) { + const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({ + text, + style: textStyle, + resolution: 1 + }); + CanvasTextGenerator.returnCanvasAndContext(canvasAndContext); + trimOffsetX = -frame.x; + trimOffsetY = -frame.y; + baseCharStyle.trim = false; + } + const chars = []; + const lineContainers = []; + const wordContainers = []; + let yOffset = 0; + const strokeWidth = ((_c = textStyle._stroke) == null ? void 0 : _c.width) || 0; + const dropShadowDistance = ((_d = textStyle.dropShadow) == null ? void 0 : _d.distance) || 0; + groupedSegments.forEach((group, lineIndex) => { + const lineContainer = new Container({ label: `line-${lineIndex}` }); + lineContainer.y = yOffset + trimOffsetY; + lineContainers.push(lineContainer); + const lineWidth = measuredText.lineWidths[lineIndex]; + let xOffset = getAlignmentOffset(alignment, lineWidth, alignWidth); + let currentWordContainer = new Container({ label: "word" }); + currentWordContainer.x = xOffset + trimOffsetX; + group.chars.forEach((segment, charIndex) => { + if (segment.metric.width === 0) { + return; + } + if (isNewlineCharacter(segment.char)) { + xOffset += segment.metric.width - strokeWidth; + return; + } + if (segment.char === " ") { + if (currentWordContainer.children.length > 0) { + wordContainers.push(currentWordContainer); + lineContainer.addChild(currentWordContainer); + } + xOffset += segment.metric.width + textStyle.letterSpacing - strokeWidth; + currentWordContainer = new Container({ label: "word" }); + currentWordContainer.x = xOffset + trimOffsetX; + } else { + let charStyle = baseCharStyle; + if (hasGradient) { + charStyle = baseCharStyle.clone(); + charStyle._gradientOffset = { x: -xOffset, y: -yOffset }; + if (hasLocalGradient) { + charStyle._gradientBounds = { width: fullTextWidth, height: fullTextHeight }; + } + } + let char; + if (existingChars.length > 0) { + char = existingChars.shift(); + char.text = segment.char; + char.style = charStyle; + char.setFromMatrix(Matrix.IDENTITY); + char.x = xOffset - currentWordContainer.x + trimOffsetX - dropShadowDistance * charIndex; + } else { + char = new Text({ + text: segment.char, + style: charStyle, + x: xOffset - currentWordContainer.x + trimOffsetX - dropShadowDistance * charIndex + }); + } + chars.push(char); + currentWordContainer.addChild(char); + xOffset += segment.metric.width + textStyle.letterSpacing - strokeWidth; + } + }); + if (currentWordContainer.children.length > 0) { + wordContainers.push(currentWordContainer); + lineContainer.addChild(currentWordContainer); + } + yOffset += measuredText.lineHeight; + }); + return { chars, lines: lineContainers, words: wordContainers }; + } + + "use strict"; + var __defProp$1 = Object.defineProperty; + var __defProps$1 = Object.defineProperties; + var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; + var __hasOwnProp$1 = Object.prototype.hasOwnProperty; + var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; + var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues$1 = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp$1.call(b, prop)) + __defNormalProp$1(a, prop, b[prop]); + if (__getOwnPropSymbols$1) + for (var prop of __getOwnPropSymbols$1(b)) { + if (__propIsEnum$1.call(b, prop)) + __defNormalProp$1(a, prop, b[prop]); + } + return a; + }; + var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)); + const _SplitText = class _SplitText extends AbstractSplitText { + constructor(config) { + const completeOptions = __spreadValues$1(__spreadValues$1({}, _SplitText.defaultOptions), config); + super(completeOptions); + } + /** + * Creates a SplitText instance from an existing text object. + * Useful for converting standard Text or Text objects into segmented versions. + * @param text - The source text object to convert + * @param options - Additional splitting options + * @returns A new SplitText instance + * @example + * ```ts + * const text = new Text({ + * text: 'Bitmap Text', + * style: { fontFamily: 'Arial' } + * }); + * + * const segmented = SplitText.from(text); + * + * // with additional options + * const segmentedWithOptions = SplitText.from(text, { + * autoSplit: false, + * lineAnchor: 0.5, + * wordAnchor: { x: 0, y: 0.5 }, + * }) + * ``` + */ + static from(text, options) { + const completeOptions = __spreadProps$1(__spreadValues$1(__spreadValues$1({}, _SplitText.defaultOptions), options), { + text: text.text, + style: new TextStyle(text.style) + }); + const splitText = new _SplitText(__spreadValues$1({}, completeOptions)); + const anchor = text.anchor; + if (anchor.x !== 0 || anchor.y !== 0) { + splitText.pivot.set( + splitText.width * anchor.x, + splitText.height * anchor.y + ); + } + return splitText; + } + splitFn() { + return canvasTextSplit({ + text: this._originalText, + style: this._style, + chars: this._canReuseChars ? this.chars : [] + }); + } + }; + /** + * Default configuration options for SplitText instances. + * @example + * ```ts + * // Override defaults globally + * SplitText.defaultOptions = { + * autoSplit: false, + * lineAnchor: 0.5, // Center alignment + * wordAnchor: { x: 0, y: 0.5 }, // Left-center + * charAnchor: { x: 0.5, y: 1 } // Bottom-center + * }; + * ``` + */ + _SplitText.defaultOptions = { + autoSplit: true, + // Auto-update on text/style changes + lineAnchor: 0, + // Top-left alignment + wordAnchor: 0, + // Top-left alignment + charAnchor: 0 + // Top-left alignment + }; + let SplitText = _SplitText; + + "use strict"; + + "use strict"; + + "use strict"; + const valuesToIterateForKeys = [ + "align", + "breakWords", + "cssOverrides", + "fontVariant", + "fontWeight", + "leading", + "letterSpacing", + "lineHeight", + "padding", + "textBaseline", + "trim", + "whiteSpace", + "wordWrap", + "wordWrapWidth", + "fontFamily", + "fontStyle", + "fontSize" + ]; + function generateTextStyleKey(style) { + const key = []; + let index = 0; + for (let i = 0; i < valuesToIterateForKeys.length; i++) { + const prop = `_${valuesToIterateForKeys[i]}`; + key[index++] = style[prop]; + } + index = addFillStyleKey(style._fill, key, index); + index = addStokeStyleKey(style._stroke, key, index); + index = addDropShadowKey(style.dropShadow, key, index); + index = addFiltersKey(style.filters, key, index); + return key.join("-"); + } + function addFiltersKey(filters, key, index) { + if (!filters) return index; + for (const filter of filters) { + key[index++] = filter.uid; + } + return index; + } + function addFillStyleKey(fillStyle, key, index) { + var _a; + if (!fillStyle) return index; + key[index++] = fillStyle.color; + key[index++] = fillStyle.alpha; + key[index++] = (_a = fillStyle.fill) == null ? void 0 : _a.styleKey; + return index; + } + function addStokeStyleKey(strokeStyle, key, index) { + if (!strokeStyle) return index; + index = addFillStyleKey(strokeStyle, key, index); + key[index++] = strokeStyle.width; + key[index++] = strokeStyle.alignment; + key[index++] = strokeStyle.cap; + key[index++] = strokeStyle.join; + key[index++] = strokeStyle.miterLimit; + return index; + } + function addDropShadowKey(dropShadow, key, index) { + if (!dropShadow) return index; + key[index++] = dropShadow.alpha; + key[index++] = dropShadow.angle; + key[index++] = dropShadow.blur; + key[index++] = dropShadow.distance; + key[index++] = Color.shared.setValue(dropShadow.color).toNumber(); + return index; + } + + "use strict"; + + "use strict"; + + "use strict"; + + "use strict"; + async function logDebugTexture(texture, renderer, size = 200) { + const base64 = await renderer.extract.base64(texture); + if (renderer.type !== RendererType.CANVAS) { + await renderer.encoder.commandFinished; + } + const width = size; + console.log(`logging texture ${texture.source.width}px ${texture.source.height}px`); + const style = [ + "font-size: 1px;", + `padding: ${width}px ${300}px;`, + `background: url(${base64}) no-repeat;`, + "background-size: contain;" + ].join(" "); + console.log("%c ", style); + } + + "use strict"; + var __defProp = Object.defineProperty; + var __defProps = Object.defineProperties; + var __getOwnPropDescs = Object.getOwnPropertyDescriptors; + var __getOwnPropSymbols = Object.getOwnPropertySymbols; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __propIsEnum = Object.prototype.propertyIsEnumerable; + var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; + }; + var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); + const colors = [ + "#000080", + // Navy Blue + "#228B22", + // Forest Green + "#8B0000", + // Dark Red + "#4169E1", + // Royal Blue + "#008080", + // Teal + "#800000", + // Maroon + "#9400D3", + // Dark Violet + "#FF8C00", + // Dark Orange + "#556B2F", + // Olive Green + "#8B008B" + // Dark Magenta + ]; + let colorTick = 0; + function logScene(container, depth = 0, data = { color: "#000000" }) { + if (container.renderGroup) { + data.color = colors[colorTick++]; + } + let spaces = ""; + for (let i = 0; i < depth; i++) { + spaces += " "; + } + let label = container.label; + if (!label && container instanceof Sprite) { + label = `sprite:${container.texture.label}`; + } + let output = `%c ${spaces}|- ${label} (worldX:${container.worldTransform.tx}, relativeRenderX:${container.relativeGroupTransform.tx}, renderX:${container.groupTransform.tx}, localX:${container.x})`; + if (container.renderGroup) { + output += " (RenderGroup)"; + } + if (container.filters) { + output += "(*filters)"; + } + console.log(output, `color:${data.color}; font-weight:bold;`); + depth++; + for (let i = 0; i < container.children.length; i++) { + const child = container.children[i]; + logScene(child, depth, __spreadValues({}, data)); + } + } + function logRenderGroupScene(renderGroup, depth = 0, data = { index: 0, color: "#000000" }) { + let spaces = ""; + for (let i = 0; i < depth; i++) { + spaces += " "; + } + const output = `%c ${spaces}- ${data.index}: ${renderGroup.root.label} worldX:${renderGroup.worldTransform.tx}`; + console.log(output, `color:${data.color}; font-weight:bold;`); + depth++; + for (let i = 0; i < renderGroup.renderGroupChildren.length; i++) { + const child = renderGroup.renderGroupChildren[i]; + logRenderGroupScene(child, depth, __spreadProps(__spreadValues({}, data), { index: i })); + } + } + + "use strict"; + + "use strict"; + + "use strict"; + + exports.AbstractBitmapFont = AbstractBitmapFont; + exports.AbstractBitmapTextPipe = AbstractBitmapTextPipe; + exports.AbstractRenderer = AbstractRenderer; + exports.AbstractSplitText = AbstractSplitText; + exports.AbstractText = AbstractText; + exports.AbstractTextSystem = AbstractTextSystem; + exports.AccessibilitySystem = AccessibilitySystem; + exports.AlphaFilter = AlphaFilter; + exports.AlphaMask = AlphaMask; + exports.AlphaMaskPipe = AlphaMaskPipe; + exports.AnimatedSprite = AnimatedSprite; + exports.Application = Application; + exports.ApplicationInitHook = ApplicationInitHook; + exports.Assets = Assets; + exports.AssetsClass = AssetsClass; + exports.BLEND_TO_NPM = BLEND_TO_NPM; + exports.BREAKING_SPACES = BREAKING_SPACES; + exports.BREAKING_SPACES_SET = BREAKING_SPACES_SET; + exports.BUFFER_TYPE = BUFFER_TYPE; + exports.BackgroundLoader = BackgroundLoader; + exports.BackgroundSystem = BackgroundSystem; + exports.Batch = Batch; + exports.BatchGeometry = BatchGeometry; + exports.BatchTextureArray = BatchTextureArray; + exports.BatchableGraphics = BatchableGraphics; + exports.BatchableHTMLText = BatchableHTMLText; + exports.BatchableMesh = BatchableMesh; + exports.BatchableSprite = BatchableSprite; + exports.BatchableText = BatchableText; + exports.Batcher = Batcher; + exports.BatcherPipe = BatcherPipe; + exports.BigPool = BigPool; + exports.BindGroup = BindGroup; + exports.BindGroupSystem = BindGroupSystem; + exports.BitmapFont = BitmapFont; + exports.BitmapFontManager = BitmapFontManager; + exports.BitmapText = BitmapText; + exports.BitmapTextGraphics = BitmapTextGraphics; + exports.BitmapTextPipe = BitmapTextPipe; + exports.BlendModeFilter = BlendModeFilter; + exports.BlendModePipe = BlendModePipe; + exports.BlurFilter = BlurFilter; + exports.BlurFilterPass = BlurFilterPass; + exports.Bounds = Bounds; + exports.BrowserAdapter = BrowserAdapter; + exports.Buffer = Buffer; + exports.BufferImageSource = BufferImageSource; + exports.BufferResource = BufferResource; + exports.BufferUsage = BufferUsage; + exports.CLEAR = CLEAR; + exports.Cache = Cache; + exports.CanvasBatchAdaptor = CanvasBatchAdaptor; + exports.CanvasBitmapTextPipe = CanvasBitmapTextPipe; + exports.CanvasColorMaskPipe = CanvasColorMaskPipe; + exports.CanvasContextSystem = CanvasContextSystem; + exports.CanvasFilterSystem = CanvasFilterSystem; + exports.CanvasGraphicsAdaptor = CanvasGraphicsAdaptor; + exports.CanvasGraphicsContextSystem = CanvasGraphicsContextSystem; + exports.CanvasGraphicsPipe = CanvasGraphicsPipe; + exports.CanvasLimitsSystem = CanvasLimitsSystem; + exports.CanvasNineSliceSpritePipe = CanvasNineSliceSpritePipe; + exports.CanvasObserver = CanvasObserver; + exports.CanvasParticleContainerAdaptor = CanvasParticleContainerAdaptor; + exports.CanvasParticleContainerPipe = CanvasParticleContainerPipe; + exports.CanvasPool = CanvasPool; + exports.CanvasPoolClass = CanvasPoolClass; + exports.CanvasRenderTargetAdaptor = CanvasRenderTargetAdaptor; + exports.CanvasRenderTargetSystem = CanvasRenderTargetSystem; + exports.CanvasRenderer = CanvasRenderer; + exports.CanvasRendererTextSystem = CanvasRendererTextSystem; + exports.CanvasSource = CanvasSource; + exports.CanvasStencilMaskPipe = CanvasStencilMaskPipe; + exports.CanvasTextGenerator = CanvasTextGenerator; + exports.CanvasTextMetrics = CanvasTextMetrics; + exports.CanvasTextPipe = CanvasTextPipe; + exports.CanvasTextSystem = CanvasTextSystem; + exports.CanvasTextureSystem = CanvasTextureSystem; + exports.CanvasTilingSpritePipe = CanvasTilingSpritePipe; + exports.Circle = Circle; + exports.Color = Color; + exports.ColorMask = ColorMask; + exports.ColorMaskPipe = ColorMaskPipe; + exports.ColorMatrixFilter = ColorMatrixFilter; + exports.CompressedSource = CompressedSource; + exports.Container = Container; + exports.CubeTexture = CubeTexture; + exports.CubeTextureSource = CubeTextureSource; + exports.Culler = Culler; + exports.CullerPlugin = CullerPlugin; + exports.CustomRenderPipe = CustomRenderPipe; + exports.D3D10_RESOURCE_DIMENSION = D3D10_RESOURCE_DIMENSION; + exports.D3DFMT = D3DFMT; + exports.DATA_URI = DATA_URI; + exports.DDS = DDS; + exports.DEG_TO_RAD = DEG_TO_RAD; + exports.DEPRECATED_SCALE_MODES = DEPRECATED_SCALE_MODES; + exports.DEPRECATED_WRAP_MODES = DEPRECATED_WRAP_MODES; + exports.DOMAdapter = DOMAdapter; + exports.DOMContainer = DOMContainer; + exports.DOMPipe = DOMPipe; + exports.DRAW_MODES = DRAW_MODES; + exports.DXGI_FORMAT = DXGI_FORMAT; + exports.DXGI_TO_TEXTURE_FORMAT = DXGI_TO_TEXTURE_FORMAT; + exports.DefaultBatcher = DefaultBatcher; + exports.DefaultShader = DefaultShader; + exports.DisplacementFilter = DisplacementFilter; + exports.DynamicBitmapFont = DynamicBitmapFont; + exports.Ellipse = Ellipse; + exports.EventBoundary = EventBoundary; + exports.EventEmitter = EventEmitter; + exports.EventSystem = EventSystem; + exports.EventsTicker = EventsTicker; + exports.ExtensionType = ExtensionType; + exports.ExternalSource = ExternalSource; + exports.ExtractSystem = ExtractSystem; + exports.FOURCC_TO_TEXTURE_FORMAT = FOURCC_TO_TEXTURE_FORMAT; + exports.FederatedContainer = FederatedContainer; + exports.FederatedEvent = FederatedEvent; + exports.FederatedMouseEvent = FederatedMouseEvent; + exports.FederatedPointerEvent = FederatedPointerEvent; + exports.FederatedWheelEvent = FederatedWheelEvent; + exports.FillGradient = FillGradient; + exports.FillPattern = FillPattern; + exports.Filter = Filter; + exports.FilterEffect = FilterEffect; + exports.FilterPipe = FilterPipe; + exports.FilterSystem = FilterSystem; + exports.FontStylePromiseCache = FontStylePromiseCache; + exports.GAUSSIAN_VALUES = GAUSSIAN_VALUES; + exports.GCManagedHash = GCManagedHash; + exports.GCSystem = GCSystem; + exports.GL_FORMATS = GL_FORMATS; + exports.GL_INTERNAL_FORMAT = GL_INTERNAL_FORMAT; + exports.GL_TARGETS = GL_TARGETS; + exports.GL_TYPES = GL_TYPES; + exports.GL_WRAP_MODES = GL_WRAP_MODES; + exports.GPUTextureGpuData = GPUTextureGpuData; + exports.GenerateTextureSystem = GenerateTextureSystem; + exports.Geometry = Geometry; + exports.GlBackBufferSystem = GlBackBufferSystem; + exports.GlBatchAdaptor = GlBatchAdaptor; + exports.GlBuffer = GlBuffer; + exports.GlBufferSystem = GlBufferSystem; + exports.GlColorMaskSystem = GlColorMaskSystem; + exports.GlContextSystem = GlContextSystem; + exports.GlEncoderSystem = GlEncoderSystem; + exports.GlGeometryGpuData = GlGeometryGpuData; + exports.GlGeometrySystem = GlGeometrySystem; + exports.GlGraphicsAdaptor = GlGraphicsAdaptor; + exports.GlLimitsSystem = GlLimitsSystem; + exports.GlMeshAdaptor = GlMeshAdaptor; + exports.GlParticleContainerAdaptor = GlParticleContainerAdaptor; + exports.GlParticleContainerPipe = GlParticleContainerPipe; + exports.GlProgram = GlProgram; + exports.GlProgramData = GlProgramData; + exports.GlRenderTarget = GlRenderTarget; + exports.GlRenderTargetAdaptor = GlRenderTargetAdaptor; + exports.GlRenderTargetSystem = GlRenderTargetSystem; + exports.GlShaderSystem = GlShaderSystem; + exports.GlStateSystem = GlStateSystem; + exports.GlStencilSystem = GlStencilSystem; + exports.GlTexture = GlTexture; + exports.GlTextureSystem = GlTextureSystem; + exports.GlUboSystem = GlUboSystem; + exports.GlUniformGroupSystem = GlUniformGroupSystem; + exports.GlobalResourceRegistry = GlobalResourceRegistry; + exports.GlobalUniformSystem = GlobalUniformSystem; + exports.GpuBatchAdaptor = GpuBatchAdaptor; + exports.GpuBlendModesToPixi = GpuBlendModesToPixi; + exports.GpuBufferData = GpuBufferData; + exports.GpuBufferSystem = GpuBufferSystem; + exports.GpuColorMaskSystem = GpuColorMaskSystem; + exports.GpuDeviceSystem = GpuDeviceSystem; + exports.GpuEncoderSystem = GpuEncoderSystem; + exports.GpuGraphicsAdaptor = GpuGraphicsAdaptor; + exports.GpuGraphicsContext = GpuGraphicsContext; + exports.GpuLimitsSystem = GpuLimitsSystem; + exports.GpuMeshAdapter = GpuMeshAdapter; + exports.GpuMipmapGenerator = GpuMipmapGenerator; + exports.GpuParticleContainerAdaptor = GpuParticleContainerAdaptor; + exports.GpuParticleContainerPipe = GpuParticleContainerPipe; + exports.GpuProgram = GpuProgram; + exports.GpuRenderTarget = GpuRenderTarget; + exports.GpuRenderTargetAdaptor = GpuRenderTargetAdaptor; + exports.GpuRenderTargetSystem = GpuRenderTargetSystem; + exports.GpuShaderSystem = GpuShaderSystem; + exports.GpuStateSystem = GpuStateSystem; + exports.GpuStencilModesToPixi = GpuStencilModesToPixi; + exports.GpuStencilSystem = GpuStencilSystem; + exports.GpuTextureSystem = GpuTextureSystem; + exports.GpuUboSystem = GpuUboSystem; + exports.GpuUniformBatchPipe = GpuUniformBatchPipe; + exports.Graphics = Graphics; + exports.GraphicsContext = GraphicsContext; + exports.GraphicsContextRenderData = GraphicsContextRenderData; + exports.GraphicsContextSystem = GraphicsContextSystem; + exports.GraphicsGpuData = GraphicsGpuData; + exports.GraphicsPath = GraphicsPath; + exports.GraphicsPipe = GraphicsPipe; + exports.HTMLText = HTMLText; + exports.HTMLTextPipe = HTMLTextPipe; + exports.HTMLTextRenderData = HTMLTextRenderData; + exports.HTMLTextStyle = HTMLTextStyle; + exports.HTMLTextSystem = HTMLTextSystem; + exports.HelloSystem = HelloSystem; + exports.IGLUniformData = IGLUniformData; + exports.ImageSource = ImageSource; + exports.InstructionSet = InstructionSet; + exports.KTX = KTX; + exports.Loader = Loader; + exports.LoaderParserPriority = LoaderParserPriority; + exports.MaskEffectManager = MaskEffectManager; + exports.MaskEffectManagerClass = MaskEffectManagerClass; + exports.MaskFilter = MaskFilter; + exports.Matrix = Matrix; + exports.Mesh = Mesh; + exports.MeshGeometry = MeshGeometry; + exports.MeshGpuData = MeshGpuData; + exports.MeshPipe = MeshPipe; + exports.MeshPlane = MeshPlane; + exports.MeshRope = MeshRope; + exports.MeshSimple = MeshSimple; + exports.NEWLINES = NEWLINES; + exports.NEWLINES_SET = NEWLINES_SET; + exports.NEWLINE_MATCH_REGEX = NEWLINE_MATCH_REGEX; + exports.NEWLINE_SPLIT_REGEX = NEWLINE_SPLIT_REGEX; + exports.NOOP = NOOP; + exports.NineSliceGeometry = NineSliceGeometry; + exports.NineSlicePlane = NineSlicePlane; + exports.NineSliceSprite = NineSliceSprite; + exports.NineSliceSpriteGpuData = NineSliceSpriteGpuData; + exports.NineSliceSpritePipe = NineSliceSpritePipe; + exports.NoiseFilter = NoiseFilter; + exports.ObservablePoint = ObservablePoint; + exports.PI_2 = PI_2; + exports.Particle = Particle; + exports.ParticleBuffer = ParticleBuffer; + exports.ParticleContainer = ParticleContainer; + exports.ParticleContainerPipe = ParticleContainerPipe; + exports.ParticleShader = ParticleShader; + exports.PassthroughFilter = PassthroughFilter; + exports.PerspectiveMesh = PerspectiveMesh; + exports.PerspectivePlaneGeometry = PerspectivePlaneGeometry; + exports.PipelineSystem = PipelineSystem; + exports.PlaneGeometry = PlaneGeometry; + exports.Point = Point; + exports.Polygon = Polygon; + exports.Pool = Pool; + exports.PoolGroupClass = PoolGroupClass; + exports.PrepareBase = PrepareBase; + exports.PrepareQueue = PrepareQueue; + exports.PrepareSystem = PrepareSystem; + exports.PrepareUpload = PrepareUpload; + exports.QuadGeometry = QuadGeometry; + exports.RAD_TO_DEG = RAD_TO_DEG; + exports.Rectangle = Rectangle; + exports.RenderContainer = RenderContainer; + exports.RenderGroup = RenderGroup; + exports.RenderGroupPipe = RenderGroupPipe; + exports.RenderGroupSystem = RenderGroupSystem; + exports.RenderLayer = RenderLayer; + exports.RenderTarget = RenderTarget; + exports.RenderTargetSystem = RenderTargetSystem; + exports.RenderTexture = RenderTexture; + exports.RenderableGCSystem = RenderableGCSystem; + exports.RendererInitHook = RendererInitHook; + exports.RendererType = RendererType; + exports.ResizePlugin = ResizePlugin; + exports.Resolver = Resolver; + exports.RopeGeometry = RopeGeometry; + exports.RoundedRectangle = RoundedRectangle; + exports.SCALE_MODES = SCALE_MODES; + exports.STENCIL_MODES = STENCIL_MODES; + exports.SVGParser = SVGParser; + exports.SchedulerSystem = SchedulerSystem; + exports.ScissorMask = ScissorMask; + exports.SdfShader = SdfShader; + exports.Shader = Shader; + exports.ShaderStage = ShaderStage; + exports.ShapePath = ShapePath; + exports.SharedRenderPipes = SharedRenderPipes; + exports.SharedSystems = SharedSystems; + exports.SplitBitmapText = SplitBitmapText; + exports.SplitText = SplitText; + exports.Sprite = Sprite; + exports.SpritePipe = SpritePipe; + exports.Spritesheet = Spritesheet; + exports.State = State; + exports.StencilMask = StencilMask; + exports.StencilMaskPipe = StencilMaskPipe; + exports.SystemRunner = SystemRunner; + exports.TEXTURE_FORMAT_BLOCK_SIZE = TEXTURE_FORMAT_BLOCK_SIZE; + exports.Text = Text; + exports.TextStyle = TextStyle; + exports.Texture = Texture; + exports.TextureGCSystem = TextureGCSystem; + exports.TextureMatrix = TextureMatrix; + exports.TexturePool = TexturePool; + exports.TexturePoolClass = TexturePoolClass; + exports.TextureSource = TextureSource; + exports.TextureStyle = TextureStyle; + exports.TextureUvs = TextureUvs; + exports.Ticker = Ticker; + exports.TickerListener = TickerListener; + exports.TickerPlugin = TickerPlugin; + exports.TilingSprite = TilingSprite; + exports.TilingSpriteGpuData = TilingSpriteGpuData; + exports.TilingSpritePipe = TilingSpritePipe; + exports.TilingSpriteShader = TilingSpriteShader; + exports.Transform = Transform; + exports.Triangle = Triangle; + exports.UNIFORM_TO_ARRAY_SETTERS = UNIFORM_TO_ARRAY_SETTERS; + exports.UNIFORM_TO_SINGLE_SETTERS = UNIFORM_TO_SINGLE_SETTERS; + exports.UNIFORM_TYPES_MAP = UNIFORM_TYPES_MAP; + exports.UNIFORM_TYPES_VALUES = UNIFORM_TYPES_VALUES; + exports.UPDATE_BLEND = UPDATE_BLEND; + exports.UPDATE_COLOR = UPDATE_COLOR; + exports.UPDATE_PRIORITY = UPDATE_PRIORITY; + exports.UPDATE_TRANSFORM = UPDATE_TRANSFORM; + exports.UPDATE_VISIBLE = UPDATE_VISIBLE; + exports.UboBatch = UboBatch; + exports.UboSystem = UboSystem; + exports.UniformGroup = UniformGroup; + exports.VERSION = VERSION; + exports.VideoSource = VideoSource; + exports.ViewContainer = ViewContainer; + exports.ViewSystem = ViewSystem; + exports.ViewableBuffer = ViewableBuffer; + exports.WGSL_ALIGN_SIZE_DATA = WGSL_ALIGN_SIZE_DATA; + exports.WGSL_TO_STD40_SIZE = WGSL_TO_STD40_SIZE; + exports.WRAP_MODES = WRAP_MODES; + exports.WebGLRenderer = WebGLRenderer; + exports.WebGPURenderer = WebGPURenderer; + exports.WorkerManager = WorkerManager; + exports.accessibilityTarget = accessibilityTarget; + exports.addBits = addBits; + exports.addMaskBounds = addMaskBounds; + exports.addMaskLocalBounds = addMaskLocalBounds; + exports.addProgramDefines = addProgramDefines; + exports.alphaFrag = fragment$5; + exports.alphaWgsl = source$5; + exports.appendSVGPath = appendSVGPath; + exports.applyMatrix = applyMatrix; + exports.applyProjectiveTransformationToPlane = applyProjectiveTransformationToPlane; + exports.applyStyleParams = applyStyleParams; + exports.assignWithIgnore = assignWithIgnore; + exports.autoDetectEnvironment = autoDetectEnvironment; + exports.autoDetectRenderer = autoDetectRenderer; + exports.autoDetectSource = autoDetectSource; + exports.basisTranscoderUrls = basisTranscoderUrls; + exports.bgr2rgb = bgr2rgb; + exports.bitmapFontCachePlugin = bitmapFontCachePlugin; + exports.bitmapFontTextParser = bitmapFontTextParser; + exports.bitmapFontXMLParser = bitmapFontXMLParser; + exports.bitmapFontXMLStringParser = bitmapFontXMLStringParser; + exports.bitmapTextSplit = bitmapTextSplit; + exports.blendTemplateFrag = blendTemplateFrag; + exports.blendTemplateVert = blendTemplateVert; + exports.blendTemplateWgsl = blendTemplate; + exports.blockDataMap = blockDataMap; + exports.blurTemplateWgsl = source$4; + exports.boundsPool = boundsPool; + exports.browserExt = browserExt; + exports.buildAdaptiveBezier = buildAdaptiveBezier; + exports.buildAdaptiveQuadratic = buildAdaptiveQuadratic; + exports.buildArc = buildArc; + exports.buildArcTo = buildArcTo; + exports.buildArcToSvg = buildArcToSvg; + exports.buildCircle = buildCircle; + exports.buildContextBatches = buildContextBatches; + exports.buildEllipse = buildEllipse; + exports.buildGeometryFromPath = buildGeometryFromPath; + exports.buildLine = buildLine; + exports.buildPixelLine = buildPixelLine; + exports.buildPolygon = buildPolygon; + exports.buildRectangle = buildRectangle; + exports.buildRoundedRectangle = buildRoundedRectangle; + exports.buildSimpleUvs = buildSimpleUvs; + exports.buildTriangle = buildTriangle; + exports.buildUvs = buildUvs; + exports.cacheAsTextureMixin = cacheAsTextureMixin; + exports.cacheTextureArray = cacheTextureArray; + exports.calculatePathArea = calculatePathArea; + exports.calculateProjection = calculateProjection; + exports.canUseNewCanvasBlendModes = canUseNewCanvasBlendModes; + exports.canvasTextSplit = canvasTextSplit; + exports.canvasUtils = canvasUtils; + exports.checkChildrenDidChange = checkChildrenDidChange; + exports.checkDataUrl = checkDataUrl; + exports.checkExtension = checkExtension; + exports.checkForNestedPattern = checkForNestedPattern; + exports.checkMaxIfStatementsInShader = checkMaxIfStatementsInShader; + exports.childrenHelperMixin = childrenHelperMixin; + exports.cleanArray = cleanArray; + exports.cleanHash = cleanHash; + exports.clearList = clearList; + exports.closePointEps = closePointEps; + exports.collapseNewlines = collapseNewlines; + exports.collapseSpaces = collapseSpaces; + exports.collectAllRenderables = collectAllRenderables; + exports.collectRenderablesMixin = collectRenderablesMixin; + exports.color32BitToUniform = color32BitToUniform; + exports.colorBit = colorBit; + exports.colorBitGl = colorBitGl; + exports.colorMatrixFilterFrag = fragment$4; + exports.colorMatrixFilterWgsl = source$3; + exports.colorToUniform = colorToUniform; + exports.compareModeToGlCompare = compareModeToGlCompare; + exports.compileHighShader = compileHighShader; + exports.compileHighShaderGl = compileHighShaderGl; + exports.compileHighShaderGlProgram = compileHighShaderGlProgram; + exports.compileHighShaderGpuProgram = compileHighShaderGpuProgram; + exports.compileHooks = compileHooks; + exports.compileInputs = compileInputs; + exports.compileOutputs = compileOutputs; + exports.compileShader = compileShader; + exports.compute2DProjection = compute2DProjection; + exports.convertFormatIfRequired = convertFormatIfRequired; + exports.convertToList = convertToList; + exports.copySearchParams = copySearchParams; + exports.createGlUploadCubeTextureResource = createGlUploadCubeTextureResource; + exports.createGpuUploadCubeTextureResource = createGpuUploadCubeTextureResource; + exports.createIdFromString = createIdFromString; + exports.createIndicesForQuads = createIndicesForQuads; + exports.createLevelBuffers = createLevelBuffers; + exports.createLevelBuffersFromKTX = createLevelBuffersFromKTX; + exports.createStringVariations = createStringVariations; + exports.createTexture = createTexture; + exports.createUboElementsSTD40 = createUboElementsSTD40; + exports.createUboElementsWGSL = createUboElementsWGSL; + exports.createUboSyncFunction = createUboSyncFunction; + exports.createUboSyncFunctionSTD40 = createUboSyncFunctionSTD40; + exports.createUboSyncFunctionWGSL = createUboSyncFunctionWGSL; + exports.crossOrigin = crossOrigin; + exports.cullingMixin = cullingMixin; + exports.curveEps = curveEps; + exports.defaultFilterVert = vertex$3; + exports.defaultValue = defaultValue; + exports.definedProps = definedProps; + exports.deprecation = deprecation; + exports.detectAvif = detectAvif; + exports.detectBasis = detectBasis; + exports.detectCompressed = detectCompressed; + exports.detectDefaults = detectDefaults; + exports.detectMp4 = detectMp4; + exports.detectOgv = detectOgv; + exports.detectVideoAlphaMode = detectVideoAlphaMode; + exports.detectWebm = detectWebm; + exports.detectWebp = detectWebp; + exports.determineCrossOrigin = determineCrossOrigin; + exports.displacementFrag = fragment$3; + exports.displacementVert = vertex$2; + exports.displacementWgsl = source$2; + exports.earcut = earcut; + exports.effectsMixin = effectsMixin; + exports.ensureAttributes = ensureAttributes; + exports.ensureIsBuffer = ensureIsBuffer; + exports.ensurePrecision = ensurePrecision; + exports.ensureTextOptions = ensureTextOptions; + exports.executeInstructions = executeInstructions; + exports.extensions = extensions; + exports.extractAttributesFromGlProgram = extractAttributesFromGlProgram; + exports.extractAttributesFromGpuProgram = extractAttributesFromGpuProgram; + exports.extractFontFamilies = extractFontFamilies; + exports.extractStructAndGroups = extractStructAndGroups; + exports.extractSubpaths = extractSubpaths; + exports.extractSvgUrlId = extractSvgUrlId; + exports.fastCopy = fastCopy; + exports.findMixin = findMixin; + exports.fontStringFromTextStyle = fontStringFromTextStyle; + exports.formatShader = formatShader; + exports.fragmentGPUTemplate = fragmentGPUTemplate; + exports.fragmentGlTemplate = fragmentGlTemplate; + exports.generateArraySyncSTD40 = generateArraySyncSTD40; + exports.generateArraySyncWGSL = generateArraySyncWGSL; + exports.generateBlurFragSource = generateBlurFragSource; + exports.generateBlurGlProgram = generateBlurGlProgram; + exports.generateBlurProgram = generateBlurProgram; + exports.generateBlurVertSource = generateBlurVertSource; + exports.generateGPULayout = generateGPULayout; + exports.generateGpuLayoutGroups = generateGpuLayoutGroups; + exports.generateLayout = generateLayout; + exports.generateLayoutHash = generateLayoutHash; + exports.generateParticleUpdateFunction = generateParticleUpdateFunction; + exports.generateProgram = generateProgram; + exports.generateShaderSyncCode = generateShaderSyncCode; + exports.generateTextStyleKey = generateTextStyleKey; + exports.generateTextureBatchBit = generateTextureBatchBit; + exports.generateTextureBatchBitGl = generateTextureBatchBitGl; + exports.generateTextureMatrix = generateTextureMatrix; + exports.generateUniformsSync = generateUniformsSync; + exports.getAdjustedBlendModeBlend = getAdjustedBlendModeBlend; + exports.getAttributeInfoFromFormat = getAttributeInfoFromFormat; + exports.getBatchSamplersUniformGroup = getBatchSamplersUniformGroup; + exports.getBitmapTextLayout = getBitmapTextLayout; + exports.getCanvasBoundingBox = getCanvasBoundingBox; + exports.getCanvasFillStyle = getCanvasFillStyle; + exports.getCanvasTexture = getCanvasTexture; + exports.getCharacterGroups = getCharacterGroups; + exports.getDefaultUniformValue = getDefaultUniformValue; + exports.getFastGlobalBounds = getFastGlobalBounds; + exports.getFastGlobalBoundsMixin = getFastGlobalBoundsMixin; + exports.getFillInstructionData = getFillInstructionData; + exports.getFontCss = getFontCss; + exports.getFontFamilyName = getFontFamilyName; + exports.getGeometryBounds = getGeometryBounds; + exports.getGlTypeFromFormat = getGlTypeFromFormat; + exports.getGlobalBounds = getGlobalBounds; + exports.getGlobalMixin = getGlobalMixin; + exports.getGlobalRenderableBounds = getGlobalRenderableBounds; + exports.getLocalBounds = getLocalBounds; + exports.getMaxFragmentPrecision = getMaxFragmentPrecision; + exports.getMaxTexturesPerBatch = getMaxTexturesPerBatch; + exports.getOrientationOfPoints = getOrientationOfPoints; + exports.getPlainText = getPlainText; + exports.getPo2TextureFromSource = getPo2TextureFromSource; + exports.getResolutionOfUrl = getResolutionOfUrl; + exports.getSVGUrl = getSVGUrl; + exports.getSupportedCompressedTextureFormats = getSupportedCompressedTextureFormats; + exports.getSupportedGPUCompressedTextureFormats = getSupportedGPUCompressedTextureFormats; + exports.getSupportedGlCompressedTextureFormats = getSupportedGlCompressedTextureFormats; + exports.getSupportedTextureFormats = getSupportedTextureFormats; + exports.getTemporaryCanvasFromImage = getTemporaryCanvasFromImage; + exports.getTestContext = getTestContext; + exports.getTextureBatchBindGroup = getTextureBatchBindGroup; + exports.getTextureDefaultMatrix = getTextureDefaultMatrix; + exports.getTextureFormatFromKTXTexture = getTextureFormatFromKTXTexture; + exports.getUboData = getUboData; + exports.getUniformData = getUniformData; + exports.getUrlExtension = getUrlExtension; + exports.glFormatToGPUFormat = glFormatToGPUFormat; + exports.glUploadBufferImageResource = glUploadBufferImageResource; + exports.glUploadCompressedTextureResource = glUploadCompressedTextureResource; + exports.glUploadImageResource = glUploadImageResource; + exports.glUploadVideoResource = glUploadVideoResource; + exports.globalUniformsBit = globalUniformsBit; + exports.globalUniformsBitGl = globalUniformsBitGl; + exports.globalUniformsUBOBitGl = globalUniformsUBOBitGl; + exports.gpuFormatToBasisTranscoderFormat = gpuFormatToBasisTranscoderFormat; + exports.gpuFormatToKTXBasisTranscoderFormat = gpuFormatToKTXBasisTranscoderFormat; + exports.gpuUploadBufferImageResource = gpuUploadBufferImageResource; + exports.gpuUploadCompressedTextureResource = gpuUploadCompressedTextureResource; + exports.gpuUploadImageResource = gpuUploadImageResource; + exports.gpuUploadVideoResource = gpuUploadVideoResource; + exports.groupD8 = groupD8; + exports.hasCachedCanvasTexture = hasCachedCanvasTexture; + exports.hasTagMarkup = hasTagMarkup; + exports.hasTagStyles = hasTagStyles; + exports.hslWgsl = hsl; + exports.hslgl = hslgl; + exports.hslgpu = hslgpu; + exports.injectBits = injectBits; + exports.insertVersion = insertVersion; + exports.isBreakingSpace = isBreakingSpace; + exports.isCanvasFilterCapable = isCanvasFilterCapable; + exports.isMobile = isMobile; + exports.isNewline = isNewline; + exports.isPow2 = isPow2; + exports.isRenderingToScreen = isRenderingToScreen; + exports.isSafari = isSafari; + exports.isSingleItem = isSingleItem; + exports.isWebGLSupported = isWebGLSupported; + exports.isWebGPUSupported = isWebGPUSupported; + exports.ktxTranscoderUrls = ktxTranscoderUrls; + exports.loadBasis = loadBasis; + exports.loadBasisOnWorker = loadBasisOnWorker; + exports.loadBitmapFont = loadBitmapFont; + exports.loadDDS = loadDDS; + exports.loadEnvironmentExtensions = loadEnvironmentExtensions; + exports.loadFontAsBase64 = loadFontAsBase64; + exports.loadFontCSS = loadFontCSS; + exports.loadImageBitmap = loadImageBitmap; + exports.loadJson = loadJson; + exports.loadKTX = loadKTX; + exports.loadKTX2 = loadKTX2; + exports.loadKTX2onWorker = loadKTX2onWorker; + exports.loadSVGImage = loadSVGImage; + exports.loadSvg = loadSvg; + exports.loadTextures = loadTextures; + exports.loadTxt = loadTxt; + exports.loadVideoTextures = loadVideoTextures; + exports.loadWebFont = loadWebFont; + exports.localUniformBit = localUniformBit; + exports.localUniformBitGl = localUniformBitGl; + exports.localUniformBitGroup2 = localUniformBitGroup2; + exports.localUniformMSDFBit = localUniformMSDFBit; + exports.localUniformMSDFBitGl = localUniformMSDFBitGl; + exports.log2 = log2; + exports.logDebugTexture = logDebugTexture; + exports.logProgramError = logProgramError; + exports.logRenderGroupScene = logRenderGroupScene; + exports.logScene = logScene; + exports.mSDFBit = mSDFBit; + exports.mSDFBitGl = mSDFBitGl; + exports.mapCanvasBlendModesToPixi = mapCanvasBlendModesToPixi; + exports.mapFormatToGlFormat = mapFormatToGlFormat; + exports.mapFormatToGlInternalFormat = mapFormatToGlInternalFormat; + exports.mapFormatToGlType = mapFormatToGlType; + exports.mapGlToVertexFormat = mapGlToVertexFormat; + exports.mapSize = mapSize; + exports.mapType = mapType; + exports.mapViewDimensionToGlTarget = mapViewDimensionToGlTarget; + exports.mapWebGLBlendModesToPixi = mapWebGLBlendModesToPixi; + exports.maskFrag = fragment$1; + exports.maskVert = vertex$1; + exports.maskWgsl = source; + exports.matrixPool = matrixPool; + exports.measureHtmlText = measureHtmlText; + exports.measureMixin = measureMixin; + exports.measureTaggedText = measureTaggedText; + exports.migrateFragmentFromV7toV8 = migrateFragmentFromV7toV8; + exports.mipmapScaleModeToGlFilter = mipmapScaleModeToGlFilter; + exports.multiplyColors = multiplyColors; + exports.multiplyHexColors = multiplyHexColors; + exports.nextPow2 = nextPow2; + exports.noiseFrag = fragment$2; + exports.noiseWgsl = source$1; + exports.nonCompressedFormats = nonCompressedFormats; + exports.normalizeExtensionPriority = normalizeExtensionPriority; + exports.onRenderMixin = onRenderMixin; + exports.parseDDS = parseDDS; + exports.parseFunctionBody = parseFunctionBody; + exports.parseKTX = parseKTX; + exports.parseSVGDefinitions = parseSVGDefinitions; + exports.parseSVGFloatAttribute = parseSVGFloatAttribute; + exports.parseSVGPath = parseSVGPath; + exports.parseSVGStyle = parseSVGStyle; + exports.parseTaggedText = parseTaggedText; + exports.particleData = particleData; + exports.particlesFrag = fragment; + exports.particlesVert = vertex; + exports.particlesWgsl = wgsl; + exports.passthroughFrag = fragment$6; + exports.passthroughWgsl = source$6; + exports.path = path; + exports.pointInTriangle = pointInTriangle$1; + exports.preloadVideo = preloadVideo; + exports.removeItems = removeItems; + exports.removeStructAndGroupDuplicates = removeStructAndGroupDuplicates; + exports.resetUids = resetUids; + exports.resolveCharacters = resolveCharacters; + exports.resolveCompressedTextureUrl = resolveCompressedTextureUrl; + exports.resolveJsonUrl = resolveJsonUrl; + exports.resolveTextureUrl = resolveTextureUrl; + exports.resourceToTexture = resourceToTexture; + exports.roundPixelsBit = roundPixelsBit; + exports.roundPixelsBitGl = roundPixelsBitGl; + exports.roundedShapeArc = roundedShapeArc; + exports.roundedShapeQuadraticCurve = roundedShapeQuadraticCurve; + exports.sayHello = sayHello; + exports.scaleModeToGlFilter = scaleModeToGlFilter; + exports.setBasisTranscoderPath = setBasisTranscoderPath; + exports.setKTXTranscoderPath = setKTXTranscoderPath; + exports.setPositions = setPositions; + exports.setProgramName = setProgramName; + exports.setUvs = setUvs; + exports.shapeBuilders = shapeBuilders; + exports.sortMixin = sortMixin; + exports.spritesheetAsset = spritesheetAsset; + exports.squaredDistanceToLineSegment = squaredDistanceToLineSegment; + exports.stripVersion = stripVersion; + exports.styleAttributes = styleAttributes; + exports.testImageFormat = testImageFormat; + exports.testVideoFormat = testVideoFormat; + exports.textStyleToCSS = textStyleToCSS; + exports.textureBit = textureBit; + exports.textureBitGl = textureBitGl; + exports.textureFrom = textureFrom; + exports.tilingBit = tilingBit; + exports.tilingBitGl = tilingBitGl; + exports.toFillStyle = toFillStyle; + exports.toLocalGlobalMixin = toLocalGlobalMixin; + exports.toStrokeStyle = toStrokeStyle; + exports.tokenize = tokenize; + exports.tokenizeTaggedRuns = tokenizeTaggedRuns; + exports.transformVertices = transformVertices; + exports.triangulateWithHoles = triangulateWithHoles; + exports.trimRight = trimRight; + exports.uboSyncFunctionsSTD40 = uboSyncFunctionsSTD40; + exports.uboSyncFunctionsWGSL = uboSyncFunctionsWGSL; + exports.uid = uid$1; + exports.uniformParsers = uniformParsers; + exports.unpremultiplyAlpha = unpremultiplyAlpha$1; + exports.unsafeEvalSupported = unsafeEvalSupported; + exports.updateLocalTransform = updateLocalTransform; + exports.updateQuadBounds = updateQuadBounds; + exports.updateRenderGroupTransform = updateRenderGroupTransform; + exports.updateRenderGroupTransforms = updateRenderGroupTransforms; + exports.updateTextBounds = updateTextBounds; + exports.updateTransformAndChildren = updateTransformAndChildren; + exports.updateTransformBackwards = updateTransformBackwards; + exports.updateWorldTransform = updateWorldTransform; + exports.v8_0_0 = v8_0_0; + exports.v8_3_4 = v8_3_4; + exports.validFormats = validFormats; + exports.validateRenderables = validateRenderables; + exports.vertexGPUTemplate = vertexGPUTemplate; + exports.vertexGlTemplate = vertexGlTemplate; + exports.vkFormatToGPUFormat = vkFormatToGPUFormat; + exports.warn = warn; + exports.wordWrap = wordWrap; + exports.wordWrapTaggedLines = wordWrapTaggedLines; + exports.wrapModeToGlAddress = wrapModeToGlAddress; + + return exports; + +})({}); +//# sourceMappingURL=pixi.js.map \ No newline at end of file