UNPKG

wave-roll

Version:

JavaScript Library for Comparative MIDI Piano-Roll Visualization

1,356 lines (1,353 loc) 90.9 kB
import { E as m, U as it, T as de, a as nt, a1 as T, a2 as B, t as w, ab as ce, Z as W, ac as D, O as at, l as V, ad as ot, ae as he, _ as Te, af as I, ag as P, w as te, k as z, c as we, H as C, M as U, a4 as re, R as se, F as Se, b as O, B as F, D as ie, y as Y, ah as lt, ai as ne, K as J, aj as E, s as ae, u as ut, G as dt, m as Pe, q as Ce, a6 as Fe, a9 as Be, n as ct, o as ht, a7 as ft, a8 as pt, aa as gt, ak as mt, al as xt, am as $, e as v, an as _t } from "./index-Bn89Sr5W.js"; import { S as X, c as j, a as bt, b as yt, B as Ue } from "./colorToUniform-zJcCVLeu.js"; class Me { /** * Initialize the plugin with scope of application instance * @private * @param {object} [options] - See application options */ static init(e) { Object.defineProperty( this, "resizeTo", { set(t) { globalThis.removeEventListener("resize", this.queueResize), this._resizeTo = t, t && (globalThis.addEventListener("resize", this.queueResize), this.resize()); }, get() { return this._resizeTo; } } ), this.queueResize = () => { 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 t, r; if (this._resizeTo === globalThis.window) t = globalThis.innerWidth, r = globalThis.innerHeight; else { const { clientWidth: s, clientHeight: i } = this._resizeTo; t = s, r = i; } this.renderer.resize(t, r), this.render(); }, this._resizeId = null, this._resizeTo = null, this.resizeTo = e.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; } } Me.extension = m.Application; class Re { /** * Initialize the plugin with scope of application instance * @private * @param {object} [options] - See application options */ static init(e) { e = Object.assign({ autoStart: !0, sharedTicker: !1 }, e), Object.defineProperty( this, "ticker", { set(t) { this._ticker && this._ticker.remove(this.render, this), this._ticker = t, t && t.add(this.render, this, it.LOW); }, get() { return this._ticker; } } ), this.stop = () => { this._ticker.stop(); }, this.start = () => { this._ticker.start(); }, this._ticker = null, this.ticker = e.sharedTicker ? de.shared : new de(), e.autoStart && this.start(); } /** * Clean up the ticker, scoped to application. * @private */ static destroy() { if (this._ticker) { const e = this._ticker; this.ticker = null, e.destroy(); } } } Re.extension = m.Application; class vt extends nt { constructor() { super(...arguments), this.chars = /* @__PURE__ */ 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; } /** * The name of the font face. * @deprecated since 8.0.0 Use `fontFamily` instead. */ get font() { return T(B, "BitmapFont.font is deprecated, please use BitmapFont.fontFamily instead."), this.fontFamily; } /** * The map of base page textures (i.e., sheets of glyphs). * @deprecated since 8.0.0 Use `pages` instead. */ get pageTextures() { return T(B, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."), this.pages; } /** * The size of the font face in pixels. * @deprecated since 8.0.0 Use `fontMetrics.fontSize` instead. */ get size() { return T(B, "BitmapFont.size is deprecated, please use BitmapFont.fontMetrics.fontSize instead."), this.fontMetrics.fontSize; } /** * The kind of distance field for this font or "none". * @deprecated since 8.0.0 Use `distanceField.type` instead. */ get distanceFieldRange() { return T(B, "BitmapFont.distanceFieldRange is deprecated, please use BitmapFont.distanceField.range instead."), this.distanceField.range; } /** * The range of the distance field in pixels. * @deprecated since 8.0.0 Use `distanceField.range` instead. */ get distanceFieldType() { return T(B, "BitmapFont.distanceFieldType is deprecated, please use BitmapFont.distanceField.type instead."), this.distanceField.type; } destroy(e = !1) { this.emit("destroy", this), this.removeAllListeners(); for (const t in this.chars) this.chars[t].texture?.destroy(); this.chars = null, e && (this.pages.forEach((t) => t.texture.destroy(!0)), this.pages = null); } } const Ge = class Ae extends vt { /** * @param options - The options for the dynamic bitmap font. */ constructor(e) { super(), this.resolution = 1, 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 = !1; const t = { ...Ae.defaultOptions, ...e }; this._textureSize = t.textureSize, this._mipmap = t.mipmap; const r = t.style.clone(); t.overrideFill && (r._fill.color = 16777215, r._fill.alpha = 1, r._fill.texture = w.WHITE, r._fill.fill = null), this.applyFillAsTint = t.overrideFill; const s = r.fontSize; r.fontSize = this.baseMeasurementFontSize; const i = ce(r); t.overrideSize ? r._stroke && (r._stroke.width *= this.baseRenderedFontSize / s) : r.fontSize = this.baseRenderedFontSize = s, this._style = r, this._skipKerning = t.skipKerning ?? !1, this.resolution = t.resolution ?? 1, this._padding = t.padding ?? 4, t.textureStyle && (this._textureStyle = t.textureStyle instanceof W ? t.textureStyle : new W(t.textureStyle)), this.fontMetrics = D.measureFont(i), this.lineHeight = r.lineHeight || this.fontMetrics.fontSize || r.fontSize; } ensureCharacters(e) { const t = D.graphemeSegmenter(e).filter((p) => !this._currentChars.includes(p)).filter((p, g, _) => _.indexOf(p) === g); if (!t.length) return; this._currentChars = [...this._currentChars, ...t]; let r; this._currentPageIndex === -1 ? r = this._nextPage() : r = this.pages[this._currentPageIndex]; let { canvas: s, context: i } = r.canvasAndContext, n = r.texture.source; const a = this._style; let l = this._currentX, u = this._currentY, c = this._currentMaxCharHeight; const d = this.baseRenderedFontSize / this.baseMeasurementFontSize, h = this._padding * d; let f = !1; const b = s.width / this.resolution, x = s.height / this.resolution; for (let p = 0; p < t.length; p++) { const g = t[p], _ = D.measureText(g, a, s, !1); _.lineHeight = _.height; const y = _.width * d, S = Math.ceil((a.fontStyle === "italic" ? 2 : 1) * y), R = _.height * d, M = S + h * 2, G = R + h * 2; if (f = !1, g !== ` ` && g !== "\r" && g !== " " && g !== " " && (f = !0, c = Math.ceil(Math.max(G, c))), l + M > b && (u += c, c = G, l = 0, u + c > x)) { n.update(); const k = this._nextPage(); s = k.canvasAndContext.canvas, i = k.canvasAndContext.context, n = k.texture.source, l = 0, u = 0, c = 0; } const A = y / d - (a.dropShadow?.distance ?? 0) - (a._stroke?.width ?? 0); if (this.chars[g] = { id: g.codePointAt(0), xOffset: -this._padding, yOffset: -this._padding, xAdvance: A, kerning: {} }, f) { this._drawGlyph( i, _, l + h, u + h, d, a ); const k = n.width * d, ue = n.height * d, st = new at( l / k * n.width, u / ue * n.height, M / k * n.width, G / ue * n.height ); this.chars[g].texture = new w({ source: n, frame: st }), l += Math.ceil(M); } } n.update(), this._currentX = l, this._currentY = u, this._currentMaxCharHeight = c, this._skipKerning && this._applyKerning(t, i); } /** * @deprecated since 8.0.0 * The map of base page textures (i.e., sheets of glyphs). */ get pageTextures() { return T(B, "BitmapFont.pageTextures is deprecated, please use BitmapFont.pages instead."), this.pages; } _applyKerning(e, t) { const r = this._measureCache; for (let s = 0; s < e.length; s++) { const i = e[s]; for (let n = 0; n < this._currentChars.length; n++) { const a = this._currentChars[n]; let l = r[i]; l || (l = r[i] = t.measureText(i).width); let u = r[a]; u || (u = r[a] = t.measureText(a).width); let c = t.measureText(i + a).width, d = c - (l + u); d && (this.chars[i].kerning[a] = d), c = t.measureText(i + a).width, d = c - (l + u), d && (this.chars[a].kerning[i] = d); } } } _nextPage() { this._currentPageIndex++; const e = this.resolution, t = V.getOptimalCanvasAndContext( this._textureSize, this._textureSize, e ); this._setupContext(t.context, this._style, e); const r = e * (this.baseRenderedFontSize / this.baseMeasurementFontSize), s = new w({ source: new ot({ resource: t.canvas, resolution: r, alphaMode: "premultiply-alpha-on-upload", autoGenerateMipmaps: this._mipmap }) }); this._textureStyle && (s.source.style = this._textureStyle); const i = { canvasAndContext: t, texture: s }; return this.pages[this._currentPageIndex] = i, i; } // canvas style! _setupContext(e, t, r) { t.fontSize = this.baseRenderedFontSize, e.scale(r, r), e.font = ce(t), t.fontSize = this.baseMeasurementFontSize, e.textBaseline = t.textBaseline; const s = t._stroke, i = s?.width ?? 0; if (s && (e.lineWidth = i, e.lineJoin = s.join, e.miterLimit = s.miterLimit, e.strokeStyle = he(s, e)), t._fill && (e.fillStyle = he(t._fill, e)), t.dropShadow) { const n = t.dropShadow, a = Te.shared.setValue(n.color).toArray(), l = n.blur * r, u = n.distance * r; e.shadowColor = `rgba(${a[0] * 255},${a[1] * 255},${a[2] * 255},${n.alpha})`, e.shadowBlur = l, e.shadowOffsetX = Math.cos(n.angle) * u, e.shadowOffsetY = Math.sin(n.angle) * u; } else e.shadowColor = "black", e.shadowBlur = 0, e.shadowOffsetX = 0, e.shadowOffsetY = 0; } _drawGlyph(e, t, r, s, i, n) { const a = t.text, l = t.fontProperties, c = (n._stroke?.width ?? 0) * i, d = r + c / 2, h = s - c / 2, f = l.descent * i, b = t.lineHeight * i; let x = !1; n.stroke && c && (x = !0, e.strokeText(a, d, h + b - f)); const { shadowBlur: p, shadowOffsetX: g, shadowOffsetY: _ } = e; n._fill && (x && (e.shadowBlur = 0, e.shadowOffsetX = 0, e.shadowOffsetY = 0), e.fillText(a, d, h + b - f)), x && (e.shadowBlur = p, e.shadowOffsetX = g, e.shadowOffsetY = _); } destroy() { super.destroy(); for (let e = 0; e < this.pages.length; e++) { const { canvasAndContext: t, texture: r } = this.pages[e]; V.returnCanvasAndContext(t), r.destroy(!0); } this.pages = null; } }; Ge.defaultOptions = { textureSize: 512, style: new I(), mipmap: !0 }; let fe = Ge; function ke(o, e, t, r) { const s = { width: 0, height: 0, offsetY: 0, scale: e.fontSize / t.baseMeasurementFontSize, lines: [{ width: 0, charPositions: [], spaceWidth: 0, spacesIndex: [], chars: [] }] }; s.offsetY = t.baseLineOffset; let i = s.lines[0], n = null, a = !0; const l = { width: 0, start: 0, index: 0, // use index to not modify the array as we use it a lot! positions: [], chars: [] }, u = t.baseMeasurementFontSize / e.fontSize, c = e.letterSpacing * u, d = e.wordWrapWidth * u, h = e.lineHeight ? e.lineHeight * u : t.lineHeight, f = e.wordWrap && e.breakWords, b = (g) => { const _ = i.width; for (let y = 0; y < l.index; y++) { const S = g.positions[y]; i.chars.push(g.chars[y]), i.charPositions.push(S + _); } i.width += g.width, a = !1, l.width = 0, l.index = 0, l.chars.length = 0; }, x = () => { let g = i.chars.length - 1; if (r) { let _ = i.chars[g]; for (; _ === " "; ) i.width -= t.chars[_].xAdvance, _ = i.chars[--g]; } s.width = Math.max(s.width, i.width), i = { width: 0, charPositions: [], chars: [], spaceWidth: 0, spacesIndex: [] }, a = !0, s.lines.push(i), s.height += h; }, p = (g) => g - c > d; for (let g = 0; g < o.length + 1; g++) { let _; const y = g === o.length; y || (_ = o[g]); const S = t.chars[_] || t.chars[" "]; if (/(?:\s)/.test(_) || _ === "\r" || _ === ` ` || y) { if (!a && e.wordWrap && p(i.width + l.width) ? (x(), b(l), y || i.charPositions.push(0)) : (l.start = i.width, b(l), y || i.charPositions.push(0)), _ === "\r" || _ === ` `) i.width !== 0 && x(); else if (!y) { const A = S.xAdvance + (S.kerning[n] || 0) + c; i.width += A, i.spaceWidth = A, i.spacesIndex.push(i.charPositions.length), i.chars.push(_); } } else { const G = S.kerning[n] || 0, A = S.xAdvance + G + c; f && p(i.width + l.width + A) && (b(l), x()), l.positions[l.index++] = l.width + G, l.chars.push(_), l.width += A; } n = _; } return x(), e.align === "center" ? Tt(s) : e.align === "right" ? wt(s) : e.align === "justify" && St(s), s; } function Tt(o) { for (let e = 0; e < o.lines.length; e++) { const t = o.lines[e], r = o.width / 2 - t.width / 2; for (let s = 0; s < t.charPositions.length; s++) t.charPositions[s] += r; } } function wt(o) { for (let e = 0; e < o.lines.length; e++) { const t = o.lines[e], r = o.width - t.width; for (let s = 0; s < t.charPositions.length; s++) t.charPositions[s] += r; } } function St(o) { const e = o.width; for (let t = 0; t < o.lines.length; t++) { const r = o.lines[t]; let s = 0, i = r.spacesIndex[s++], n = 0; const a = r.spacesIndex.length, u = (e - r.width) / a; for (let c = 0; c < r.charPositions.length; c++) c === i && (i = r.spacesIndex[s++], n += u), r.charPositions[c] += n; } } function Pt(o) { if (o === "") return []; typeof o == "string" && (o = [o]); const e = []; for (let t = 0, r = o.length; t < r; t++) { const s = o[t]; if (Array.isArray(s)) { if (s.length !== 2) throw new Error(`[BitmapFont]: Invalid character range length, expecting 2 got ${s.length}.`); if (s[0].length === 0 || s[1].length === 0) throw new Error("[BitmapFont]: Invalid character delimiter."); const i = s[0].charCodeAt(0), n = s[1].charCodeAt(0); if (n < i) throw new Error("[BitmapFont]: Invalid character range."); for (let a = i, l = n; a <= l; a++) e.push(String.fromCharCode(a)); } else e.push(...Array.from(s)); } if (e.length === 0) throw new Error("[BitmapFont]: Empty set when resolving characters."); return e; } let L = 0; class Ct { 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 }; } /** * 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(e, t) { let r = `${t.fontFamily}-bitmap`, s = !0; if (t._fill.fill && !t._stroke) r += t._fill.fill.styleKey, s = !1; else if (t._stroke || t.dropShadow) { let n = t.styleKey; n = n.substring(0, n.lastIndexOf("-")), r = `${n}-bitmap`, s = !1; } if (!P.has(r)) { const n = Object.create(t); n.lineHeight = 0; const a = new fe({ style: n, overrideFill: s, overrideSize: !0, ...this.defaultOptions }); L++, L > 50 && te("BitmapText", `You have dynamically created ${L} bitmap fonts, this can be inefficient. Try pre installing your font styles using \`BitmapFont.install({name:"style1", style})\``), a.once("destroy", () => { L--, P.remove(r); }), P.set( r, a ); } const i = P.get(r); return i.ensureCharacters?.(e), i; } /** * 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(e, t, r = !0) { const s = this.getFont(e, t), i = D.graphemeSegmenter(e); return ke(i, t, s, r); } /** * 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(e, t, r = !0) { return this.getLayout(e, t, r); } // eslint-disable-next-line max-len install(...e) { let t = e[0]; typeof t == "string" && (t = { name: t, style: e[1], chars: e[2]?.chars, resolution: e[2]?.resolution, padding: e[2]?.padding, skipKerning: e[2]?.skipKerning }, T(B, "BitmapFontManager.install(name, style, options) is deprecated, use BitmapFontManager.install({name, style, ...options})")); const r = t?.name; if (!r) throw new Error("[BitmapFontManager] Property `name` is required."); t = { ...this.defaultOptions, ...t }; const s = t.style, i = s instanceof I ? s : new I(s), n = t.dynamicFill ?? this._canUseTintForStyle(i), a = new fe({ style: i, overrideFill: n, skipKerning: t.skipKerning, padding: t.padding, resolution: t.resolution, overrideSize: !1, textureStyle: t.textureStyle }), l = Pt(t.chars); return a.ensureCharacters(l.join("")), P.set(`${r}-bitmap`, a), a.once("destroy", () => P.remove(`${r}-bitmap`)), a; } /** * Uninstalls a bitmap font from the cache. * @param {string} name - The name of the bitmap font to uninstall. */ uninstall(e) { const t = `${e}-bitmap`, r = P.get(t); r && r.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(e) { return !e._stroke && (!e.dropShadow || e.dropShadow.color === 0) && !e._fill.fill && e._fill.color === 16777215; } } const Ft = new Ct(); class ze { constructor(e) { this._renderer = e; } push(e, t, r) { this._renderer.renderPipes.batch.break(r), r.add({ renderPipeId: "filter", canBundle: !1, action: "pushFilter", container: t, filterEffect: e }); } pop(e, t, r) { this._renderer.renderPipes.batch.break(r), r.add({ renderPipeId: "filter", action: "popFilter", canBundle: !1 }); } execute(e) { e.action === "pushFilter" ? this._renderer.filter.push(e) : e.action === "popFilter" && this._renderer.filter.pop(); } destroy() { this._renderer = null; } } ze.extension = { type: [ m.WebGLPipes, m.WebGPUPipes, m.CanvasPipes ], name: "filter" }; function Bt(o, e) { e.clear(); const t = e.matrix; for (let r = 0; r < o.length; r++) { const s = o[r]; s.globalDisplayStatus < 7 || (e.matrix = s.worldTransform, e.addBounds(s.bounds)); } return e.matrix = t, e; } const Ut = new re({ attributes: { aPosition: { buffer: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), format: "float32x2", stride: 8, offset: 0 } }, indexBuffer: new Uint32Array([0, 1, 2, 0, 2, 3]) }); class Mt { constructor() { this.skip = !1, this.inputTexture = null, this.backTexture = null, this.filters = null, this.bounds = new Se(), this.container = null, this.blendRequired = !1, this.outputRenderSurface = null, this.globalFrame = { x: 0, y: 0, width: 0, height: 0 }; } } class De { constructor(e) { this._filterStackIndex = 0, this._filterStack = [], this._filterGlobalUniforms = new z({ uInputSize: { value: new Float32Array(4), type: "vec4<f32>" }, uInputPixel: { value: new Float32Array(4), type: "vec4<f32>" }, uInputClamp: { value: new Float32Array(4), type: "vec4<f32>" }, uOutputFrame: { value: new Float32Array(4), type: "vec4<f32>" }, uGlobalFrame: { value: new Float32Array(4), type: "vec4<f32>" }, uOutputTexture: { value: new Float32Array(4), type: "vec4<f32>" } }), this._globalFilterBindGroup = new we({}), this.renderer = e; } /** * The back texture of the currently active filter. Requires the filter to have `blendRequired` set to true. * @readonly */ get activeBackTexture() { return this._activeFilterData?.backTexture; } /** * Pushes a filter instruction onto the filter stack. * @param instruction - The instruction containing the filter effect and container. * @internal */ push(e) { const t = this.renderer, r = e.filterEffect.filters, s = this._pushFilterData(); s.skip = !1, s.filters = r, s.container = e.container, s.outputRenderSurface = t.renderTarget.renderSurface; const i = t.renderTarget.renderTarget.colorTexture.source, n = i.resolution, a = i.antialias; if (r.length === 0) { s.skip = !0; return; } const l = s.bounds; if (this._calculateFilterArea(e, l), this._calculateFilterBounds(s, t.renderTarget.rootViewPort, a, n, 1), s.skip) return; const u = this._getPreviousFilterData(), c = this._findFilterResolution(n); let d = 0, h = 0; u && (d = u.bounds.minX, h = u.bounds.minY), this._calculateGlobalFrame( s, d, h, c, i.width, i.height ), this._setupFilterTextures(s, l, t, u); } /** * 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: e, filters: t }) { const r = this._pushFilterData(); this._activeFilterData = r, r.skip = !1, r.filters = t; const s = e.source, i = s.resolution, n = s.antialias; if (t.length === 0) return r.skip = !0, e; const a = r.bounds; if (a.addRect(e.frame), this._calculateFilterBounds(r, a.rectangle, n, i, 0), r.skip) return e; const l = i; this._calculateGlobalFrame( r, 0, 0, l, s.width, s.height ), r.outputRenderSurface = C.getOptimalTexture( a.width, a.height, r.resolution, r.antialias ), r.backTexture = w.EMPTY, r.inputTexture = e, this.renderer.renderTarget.finishRenderPass(), this._applyFiltersToTexture(r, !0); const h = r.outputRenderSurface; return h.source.alphaMode = "premultiplied-alpha", h; } /** @internal */ pop() { const e = this.renderer, t = this._popFilterData(); t.skip || (e.globalUniforms.pop(), e.renderTarget.finishRenderPass(), this._activeFilterData = t, this._applyFiltersToTexture(t, !1), t.blendRequired && C.returnTexture(t.backTexture), C.returnTexture(t.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(e, t, r) { const s = e.colorTexture.source._resolution, i = C.getOptimalTexture( t.width, t.height, s, !1 ); let n = t.minX, a = t.minY; r && (n -= r.minX, a -= r.minY), n = Math.floor(n * s), a = Math.floor(a * s); const l = Math.ceil(t.width * s), u = Math.ceil(t.height * s); return this.renderer.renderTarget.copyToTexture( e, i, { x: n, y: a }, { width: l, height: u }, { x: 0, y: 0 } ), i; } /** * 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(e, t, r, s) { const i = this.renderer, n = this._activeFilterData, l = n.outputRenderSurface === r, u = i.renderTarget.rootRenderTarget.colorTexture.source._resolution, c = this._findFilterResolution(u); let d = 0, h = 0; if (l) { const f = this._findPreviousFilterOffset(); d = f.x, h = f.y; } this._updateFilterUniforms(t, r, n, d, h, c, l, s), this._setupBindGroupsAndRender(e, t, i); } /** * 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(e, t) { const r = this._activeFilterData, s = e.set( r.inputTexture._source.width, 0, 0, r.inputTexture._source.height, r.bounds.minX, r.bounds.minY ), i = t.worldTransform.copyTo(U.shared), n = t.renderGroup || t.parentRenderGroup; return n && n.cacheToLocalTransform && i.prepend(n.cacheToLocalTransform), i.invert(), s.prepend(i), s.scale( 1 / t.texture.orig.width, 1 / t.texture.orig.height ), s.translate(t.anchor.x, t.anchor.y), s; } destroy() { } /** * 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(e, t, r) { if (r.renderPipes.uniformBatch) { const s = r.renderPipes.uniformBatch.getUboResource(this._filterGlobalUniforms); this._globalFilterBindGroup.setResource(s, 0); } else this._globalFilterBindGroup.setResource(this._filterGlobalUniforms, 0); this._globalFilterBindGroup.setResource(t.source, 1), this._globalFilterBindGroup.setResource(t.source.style, 2), e.groups[0] = this._globalFilterBindGroup, r.encoder.draw({ geometry: Ut, shader: e, state: e._state, topology: "triangle-list" }), r.type === se.WEBGL && r.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(e, t, r, s) { if (e.backTexture = w.EMPTY, e.blendRequired) { r.renderTarget.finishRenderPass(); const i = r.renderTarget.getRenderTarget(e.outputRenderSurface); e.backTexture = this.getBackTexture(i, t, s?.bounds); } e.inputTexture = C.getOptimalTexture( t.width, t.height, e.resolution, e.antialias ), r.renderTarget.bind(e.inputTexture, !0), r.globalUniforms.push({ offset: t }); } /** * 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(e, t, r, s, i, n) { const a = e.globalFrame; a.x = t * s, a.y = r * s, a.width = i * s, a.height = n * s; } /** * 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(e, t, r, s, i, n, a, l) { const u = this._filterGlobalUniforms.uniforms, c = u.uOutputFrame, d = u.uInputSize, h = u.uInputPixel, f = u.uInputClamp, b = u.uGlobalFrame, x = u.uOutputTexture; a ? (c[0] = r.bounds.minX - s, c[1] = r.bounds.minY - i) : (c[0] = 0, c[1] = 0), c[2] = e.frame.width, c[3] = e.frame.height, d[0] = e.source.width, d[1] = e.source.height, d[2] = 1 / d[0], d[3] = 1 / d[1], h[0] = e.source.pixelWidth, h[1] = e.source.pixelHeight, h[2] = 1 / h[0], h[3] = 1 / h[1], f[0] = 0.5 * h[2], f[1] = 0.5 * h[3], f[2] = e.frame.width * d[2] - 0.5 * h[2], f[3] = e.frame.height * d[3] - 0.5 * h[3]; const p = this.renderer.renderTarget.rootRenderTarget.colorTexture; b[0] = s * n, b[1] = i * n, b[2] = p.source.width * n, b[3] = p.source.height * n, t instanceof w && (t.source.resource = null); const g = this.renderer.renderTarget.getRenderTarget(t); this.renderer.renderTarget.bind(t, !!l), t instanceof w ? (x[0] = t.frame.width, x[1] = t.frame.height) : (x[0] = g.width, x[1] = g.height), x[2] = g.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(e) { let t = this._filterStackIndex - 1; for (; t > 0 && this._filterStack[t].skip; ) --t; return t > 0 && this._filterStack[t].inputTexture ? this._filterStack[t].inputTexture.source._resolution : e; } /** * Finds the offset from the previous non-skipped filter in the stack. * @returns The offset coordinates from the previous filter */ _findPreviousFilterOffset() { let e = 0, t = 0, r = this._filterStackIndex; for (; r > 0; ) { r--; const s = this._filterStack[r]; if (!s.skip) { e = s.bounds.minX, t = s.bounds.minY; break; } } return { x: e, y: t }; } /** * Calculates the filter area bounds based on the instruction type. * @param instruction - The filter instruction * @param bounds - The bounds object to populate */ _calculateFilterArea(e, t) { if (e.renderables ? Bt(e.renderables, t) : e.filterEffect.filterArea ? (t.clear(), t.addRect(e.filterEffect.filterArea), t.applyMatrix(e.container.worldTransform)) : e.container.getFastGlobalBounds(!0, t), e.container) { const s = (e.container.renderGroup || e.container.parentRenderGroup).cacheToLocalTransform; s && t.applyMatrix(s); } } _applyFiltersToTexture(e, t) { const r = e.inputTexture, s = e.bounds, i = e.filters; if (this._globalFilterBindGroup.setResource(r.source.style, 2), this._globalFilterBindGroup.setResource(e.backTexture.source, 3), i.length === 1) i[0].apply(this, r, e.outputRenderSurface, t); else { let n = e.inputTexture; const a = C.getOptimalTexture( s.width, s.height, n.source._resolution, !1 ); let l = a, u = 0; for (u = 0; u < i.length - 1; ++u) { i[u].apply(this, n, l, !0); const d = n; n = l, l = d; } i[u].apply(this, n, e.outputRenderSurface, t), C.returnTexture(a); } } _calculateFilterBounds(e, t, r, s, i) { const n = this.renderer, a = e.bounds, l = e.filters; let u = 1 / 0, c = 0, d = !0, h = !1, f = !1, b = !0; for (let x = 0; x < l.length; x++) { const p = l[x]; if (u = Math.min(u, p.resolution === "inherit" ? s : p.resolution), c += p.padding, p.antialias === "off" ? d = !1 : p.antialias === "inherit" && d && (d = r), p.clipToViewport || (b = !1), !!!(p.compatibleRenderers & n.type)) { f = !1; break; } if (p.blendRequired && !(n.backBuffer?.useBackBuffer ?? !0)) { te("Blend filter requires backBuffer on WebGL renderer to be enabled. Set `useBackBuffer: true` in the renderer options."), f = !1; break; } f = p.enabled || f, h || (h = p.blendRequired); } if (!f) { e.skip = !0; return; } if (b && a.fitBounds(0, t.width / s, 0, t.height / s), a.scale(u).ceil().scale(1 / u).pad((c | 0) * i), !a.isPositive) { e.skip = !0; return; } e.antialias = d, e.resolution = u, e.blendRequired = h; } _popFilterData() { return this._filterStackIndex--, this._filterStack[this._filterStackIndex]; } _getPreviousFilterData() { let e, t = this._filterStackIndex - 1; for (; t > 1 && (t--, e = this._filterStack[t], !!e.skip); ) ; return e; } _pushFilterData() { let e = this._filterStack[this._filterStackIndex]; return e || (e = this._filterStack[this._filterStackIndex] = new Mt()), this._filterStackIndex++, e; } } De.extension = { type: [ m.WebGLSystem, m.WebGPUSystem ], name: "filter" }; const Oe = class We extends re { constructor(...e) { let t = e[0] ?? {}; t instanceof Float32Array && (T(B, "use new MeshGeometry({ positions, uvs, indices }) instead"), t = { positions: t, uvs: e[1], indices: e[2] }), t = { ...We.defaultOptions, ...t }; const r = t.positions || new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]); let s = t.uvs; s || (t.positions ? s = new Float32Array(r.length) : s = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1])); const i = t.indices || new Uint32Array([0, 1, 2, 0, 2, 3]), n = t.shrinkBuffersToFit, a = new O({ data: r, label: "attribute-mesh-positions", shrinkToFit: n, usage: F.VERTEX | F.COPY_DST }), l = new O({ data: s, label: "attribute-mesh-uvs", shrinkToFit: n, usage: F.VERTEX | F.COPY_DST }), u = new O({ data: i, label: "index-mesh-buffer", shrinkToFit: n, usage: F.INDEX | F.COPY_DST }); super({ attributes: { aPosition: { buffer: a, format: "float32x2", stride: 8, offset: 0 }, aUV: { buffer: l, format: "float32x2", stride: 8, offset: 0 } }, indexBuffer: u, topology: t.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(e) { this.attributes.aPosition.buffer.data = e; } /** 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(e) { this.attributes.aUV.buffer.data = e; } /** The indices of the mesh. */ get indices() { return this.indexBuffer.data; } set indices(e) { this.indexBuffer.data = e; } }; Oe.defaultOptions = { topology: "triangle-list", shrinkBuffersToFit: !1 }; let oe = Oe; const pe = "http://www.w3.org/2000/svg", ge = "http://www.w3.org/1999/xhtml"; class Ie { constructor() { this.svgRoot = document.createElementNS(pe, "svg"), this.foreignObject = document.createElementNS(pe, "foreignObject"), this.domElement = document.createElementNS(ge, "div"), this.styleElement = document.createElementNS(ge, "style"); const { foreignObject: e, svgRoot: t, styleElement: r, domElement: s } = this; e.setAttribute("width", "10000"), e.setAttribute("height", "10000"), e.style.overflow = "hidden", t.appendChild(e), e.appendChild(r), e.appendChild(s), this.image = ie.get().createImage(); } } let me; function Rt(o, e, t, r) { r || (r = me || (me = new Ie())); const { domElement: s, styleElement: i, svgRoot: n } = r; s.innerHTML = `<style>${e.cssStyle};</style><div style='padding:0'>${o}</div>`, s.setAttribute("style", "transform-origin: top left; display: inline-block"), t && (i.textContent = t), document.body.appendChild(n); const a = s.getBoundingClientRect(); n.remove(); const l = e.padding * 2; return { width: a.width - l, height: a.height - l }; } class Gt { constructor() { this.batches = [], this.batched = !1; } destroy() { this.batches.forEach((e) => { Y.return(e); }), this.batches.length = 0; } } class Ee { constructor(e, t) { this.state = X.for2d(), this.renderer = e, this._adaptor = t, this.renderer.runners.contextChange.add(this); } contextChange() { this._adaptor.contextChange(this.renderer); } validateRenderable(e) { const t = e.context, r = !!e._gpuData, s = this.renderer.graphicsContext.updateGpuContext(t); return !!(s.isBatchable || r !== s.isBatchable); } addRenderable(e, t) { const r = this.renderer.graphicsContext.updateGpuContext(e.context); e.didViewUpdate && this._rebuild(e), r.isBatchable ? this._addToBatcher(e, t) : (this.renderer.renderPipes.batch.break(t), t.add(e)); } updateRenderable(e) { const r = this._getGpuDataForRenderable(e).batches; for (let s = 0; s < r.length; s++) { const i = r[s]; i._batcher.updateElement(i); } } execute(e) { if (!e.isRenderable) return; const t = this.renderer, r = e.context; if (!t.graphicsContext.getGpuContext(r).batches.length) return; const i = r.customShader || this._adaptor.shader; this.state.blendMode = e.groupBlendMode; const n = i.resources.localUniforms.uniforms; n.uTransformMatrix = e.groupTransform, n.uRound = t._roundPixels | e._roundPixels, j( e.groupColorAlpha, n.uColor, 0 ), this._adaptor.execute(this, e); } _rebuild(e) { const t = this._getGpuDataForRenderable(e), r = this.renderer.graphicsContext.updateGpuContext(e.context); t.destroy(), r.isBatchable && this._updateBatchesForRenderable(e, t); } _addToBatcher(e, t) { const r = this.renderer.renderPipes.batch, s = this._getGpuDataForRenderable(e).batches; for (let i = 0; i < s.length; i++) { const n = s[i]; r.addToBatch(n, t); } } _getGpuDataForRenderable(e) { return e._gpuData[this.renderer.uid] || this._initGpuDataForRenderable(e); } _initGpuDataForRenderable(e) { const t = new Gt(); return e._gpuData[this.renderer.uid] = t, t; } _updateBatchesForRenderable(e, t) { const r = e.context, s = this.renderer.graphicsContext.getGpuContext(r), i = this.renderer._roundPixels | e._roundPixels; t.batches = s.batches.map((n) => { const a = Y.get(lt); return n.copyTo(a), a.renderable = e, a.roundPixels = i, a; }); } destroy() { this.renderer = null, this._adaptor.destroy(), this._adaptor = null, this.state = null; } } Ee.extension = { type: [ m.WebGLPipes, m.WebGPUPipes, m.CanvasPipes ], name: "graphics" }; const Le = class He extends oe { constructor(...e) { super({}); let t = e[0] ?? {}; typeof t == "number" && (T(B, "PlaneGeometry constructor changed please use { width, height, verticesX, verticesY } instead"), t = { width: t, height: e[1], verticesX: e[2], verticesY: e[3] }), this.build(t); } /** * Refreshes plane coordinates * @param options - Options to be applied to plane geometry */ build(e) { e = { ...He.defaultOptions, ...e }, this.verticesX = this.verticesX ?? e.verticesX, this.verticesY = this.verticesY ?? e.verticesY, this.width = this.width ?? e.width, this.height = this.height ?? e.height; const t = this.verticesX * this.verticesY, r = [], s = [], i = [], n = this.verticesX - 1, a = this.verticesY - 1, l = this.width / n, u = this.height / a; for (let d = 0; d < t; d++) { const h = d % this.verticesX, f = d / this.verticesX | 0; r.push(h * l, f * u), s.push(h / n, f / a); } const c = n * a; for (let d = 0; d < c; d++) { const h = d % n, f = d / n | 0, b = f * this.verticesX + h, x = f * this.verticesX + h + 1, p = (f + 1) * this.verticesX + h, g = (f + 1) * this.verticesX + h + 1; i.push( b, x, p, x, g, p ); } this.buffers[0].data = new Float32Array(r), this.buffers[1].data = new Float32Array(s), this.indexBuffer.data = new Uint32Array(i), this.buffers[0].update(), this.buffers[1].update(), this.indexBuffer.update(); } }; Le.defaultOptions = { width: 100, height: 100, verticesX: 10, verticesY: 10 }; let At = Le; class le { 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(e) { this._topology = e; } 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(e) { this.texture !== e && (this.texture = e, this._textureMatrixUpdateId = -1); } get uvs() { const t = this.geometry.getBuffer("aUV"), r = t.data; let s = r; const i = this.texture.textureMatrix; return i.isSimple || (s = this._transformedUvs, (this._textureMatrixUpdateId !== i._updateID || this._uvUpdateId !== t._updateID) && ((!s || s.length < r.length) && (s = this._transformedUvs = new Float32Array(r.length)), this._textureMatrixUpdateId = i._updateID, this._uvUpdateId = t._updateID, i.multiplyUvs(r, s))), s; } 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; } } class xe { destroy() { } } class Ve { constructor(e, t) { this.localUniforms = new z({ uTransformMatrix: { value: new U(), type: "mat3x3<f32>" }, uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" }, uRound: { value: 0, type: "f32" } }), this.localUniformsBindGroup = new we({ 0: this.localUniforms }), this.renderer = e, this._adaptor = t, this._adaptor.init(); } validateRenderable(e) { const t = this._getMeshData(e), r = t.batched, s = e.batched; if (t.batched = s, r !== s) return !0; if (s) { const i = e._geometry; if (i.indices.length !== t.indexSize || i.positions.length !== t.vertexSize) return t.indexSize = i.indices.length, t.vertexSize = i.positions.length, !0; const n = this._getBatchableMesh(e); return n.texture.uid !== e._texture.uid && (n._textureMatrixUpdateId = -1), !n._batcher.checkAndUpdateTexture( n, e._texture ); } return !1; } addRenderable(e, t) { const r = this.renderer.renderPipes.batch, s = this._getMeshData(e); if (e.didViewUpdate && (s.indexSize = e._geometry.indices?.length, s.vertexSize = e._geometry.positions?.length), s.batched) { const i = this._getBatchableMesh(e); i.setTexture(e._texture), i.geometry = e._geometry, r.addToBatch(i, t); } else r.break(t), t.add(e); } updateRenderable(e) { if (e.batched) { const t = this._getBatchableMesh(e); t.setTexture(e._texture), t.geometry = e._geometry, t._batcher.updateElement(t); } } execute(e) { if (!e.isRenderable) return; e.state.blendMode = ne(e.groupBlendMode, e.texture._source); const t = this.localUniforms; t.uniforms.uTransformMatrix = e.groupTransform, t.uniforms.uRound = this.renderer._roundPixels | e._roundPixels, t.update(), j( e.groupColorAlpha, t.uniforms.uColor, 0 ), this._adaptor.execute(this, e); } _getMeshData(e) { var t, r; return (t = e._gpuData)[r = this.renderer.uid] || (t[r] = new xe()), e._gpuData[this.renderer.uid].meshData || this._initMeshData(e); } _initMeshData(e) { return e._gpuData[this.renderer.uid].meshData = { batched: e.batched, indexSize: 0, vertexSize: 0 }, e._gpuData[this.renderer.uid].meshData; } _getBatchableMesh(e) { var t, r; return (t = e._gpuData)[r = this.renderer.uid] || (t[r] = new xe()), e._gpuData[this.renderer.uid].batchableMesh || this._initBatchableMesh(e); } _initBatchableMesh(e) { const t = new le(); return t.renderable = e, t.setTexture(e._texture), t.transform = e.groupTransform, t.roundPixels = this.renderer._roundPixels | e._roundPixels, e._gpuData[this.renderer.uid].batchableMesh = t, t; } destroy() { this.localUniforms = null, this.localUniformsBindGroup = null, this._adaptor.destroy(), this._adaptor = null, this.renderer = null; } } Ve.extension = { type: [ m.WebGLPipes, m.WebGPUPipes, m.CanvasPipes ], name: "mesh" }; class kt { execute(e, t) { const r = e.state, s = e.renderer, i = t.shader || e.defaultShader; i.resources.uTexture = t.texture._source, i.resources.uniforms = e.localUniforms; const n = s.gl, a = e.getBuffers(t); s.shader.bind(i), s.state.set(r), s.geometry.bind(a.geometry, i.glProgram); const u = a.geometry.indexBuffer.data.BYTES_PER_ELEMENT === 2 ? n.UNSIGNED_SHORT : n.UNSIGNED_INT; n.drawElements(n.TRIANGLES, t.particleChildren.length * 6, u, 0); } } class zt { execute(e, t) { const r = e.renderer, s = t.shader || e.defaultShader; s.groups[0] = r.renderPipes.uniformBatch.getUniformBindGroup(e.localUniforms, !0), s.groups[1] = r.texture.getTextureBindGroup(t.texture); const i = e.state, n = e.getBuffers(t); r.encoder.draw({ geometry: n.geometry, shader: t.shader || e.defaultShader, state: i, size: t.particleChildren.length * 6 }); } } function _e(o, e = null) { const t = o * 6; if (t > 65535 ? e || (e = new Uint32Array(t)) : e || (e = new Uint16Array(t)), e.length !== t) throw new Error(`Out buffer length is incorrect, got ${e.length} and expected ${t}`); for (let r = 0, s = 0; r < t; r += 6, s += 4) e[r + 0] = s + 0, e[r + 1] = s + 1, e[r + 2] = s + 2, e[r + 3] = s + 0, e[r + 4] = s + 2, e[r + 5] = s + 3; return e; } function Dt(o) { return { dynamicUpdate: be(o, !0), staticUpdate: be(o, !1) }; } function be(o, e) { const t = []; t.push(` var index = 0; for (let i = 0; i < ps.length; ++i) { const p = ps[i]; `); let r = 0; for (const i in o) { const n = o[i]; if (e !== n.dynamic) continue; t.push(`offset = index + ${r}`), t.push(n.code); const a = J(n.format); r += a.stride / 4; } t.push(` index += stride * 4; } `), t.unshift(` var stride = ${r}; `); const s = t.join(` `); return new Function("ps", "f32v", "u32v", s); } class Ot { constructor(e) { this._size = 0, this._generateParticleUpdateCache = {}; const t = this._size = e.size ?? 1e3, r = e.properties; let s = 0, i = 0; for (const c in r) { const d = r[c], h = J(d.format); d.dynamic ? i += h.stride : s += h.stride; } this._dynamicStride = i / 4, this._staticStride = s / 4, this.staticAttributeBuffer = new E(t * 4 * s), this.dynamicAttributeBuffer = new E(t * 4 * i), this.indexBuffer = _e(t); const n = new re(); let a = 0, l = 0; this._staticBuffer = new O({ data: new Float32Arra