fuxi-topology 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { EventEmitter as le, Graphics as L, Sprite as gn, Texture as ne, Rectangle as Lt, Container as R, Point as Vr, MeshRope as Xr, TilingSprite as mn, TextStyle as qr, Text as vn, Assets as gi, ColorMatrixFilter as mi, Filter as G, GlProgram as U, UniformGroup as H, BlurFilter as Jr, Application as Kr } from "pixi.js";
1
+ import { EventEmitter as le, Graphics as L, Sprite as gn, Texture as ne, Rectangle as Lt, Container as R, Point as Vr, MeshRope as Xr, TilingSprite as mn, TextStyle as qr, Text as vn, Assets as gi, BlurFilter as Jr, Filter as G, GlProgram as U, UniformGroup as H, ColorMatrixFilter as mi, Application as Kr } from "pixi.js";
2
2
  import dt from "pathfinding";
3
3
  import { zoom as Zr, ZoomTransform as Ut, zoomTransform as Qr, zoomIdentity as es } from "d3-zoom";
4
4
  import { ZoomTransform as md } from "d3-zoom";
@@ -3027,712 +3027,710 @@ class Ja extends le {
3027
3027
  this._looseGraphics.clear(), this._rootOrder = [], this.removeAllListeners();
3028
3028
  }
3029
3029
  }
3030
- function Ze(t) {
3031
- return `#${t.toString(16).padStart(6, "0")}`;
3032
- }
3033
- function Ka(t) {
3034
- return "convertToBlob" in t && typeof t.convertToBlob == "function" ? t.convertToBlob({ type: "image/png" }) : new Promise((e, i) => {
3035
- t.toBlob(
3036
- (n) => n ? e(n) : i(new Error("Failed to create PNG blob")),
3037
- "image/png"
3038
- );
3039
- });
3040
- }
3041
- function Za(t) {
3042
- return typeof t.toDataURL == "function" ? t.toDataURL("image/png") : "";
3030
+ function bf(t, e) {
3031
+ const i = e?.strength ?? 4, n = e?.quality ?? 4;
3032
+ return {
3033
+ postProcessId: t,
3034
+ createFilter() {
3035
+ return new Jr({ strength: i, quality: n });
3036
+ },
3037
+ setOptions(r, s) {
3038
+ const o = r;
3039
+ s.strength !== void 0 && (o.strength = s.strength), s.quality !== void 0 && (o.quality = s.quality);
3040
+ }
3041
+ };
3043
3042
  }
3044
- function Ct(t) {
3045
- return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
3043
+ const Ka = (
3044
+ /* glsl */
3045
+ `
3046
+ in vec2 aPosition;
3047
+ out vec2 vTextureCoord;
3048
+
3049
+ uniform vec4 uInputSize;
3050
+ uniform vec4 uOutputFrame;
3051
+ uniform vec4 uOutputTexture;
3052
+
3053
+ vec4 filterVertexPosition(void)
3054
+ {
3055
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3056
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3057
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3058
+ return vec4(position, 0.0, 1.0);
3046
3059
  }
3047
- function It(t, e) {
3048
- const i = t instanceof Blob ? URL.createObjectURL(t) : `data:image/svg+xml;charset=utf-8,${encodeURIComponent(t)}`, n = document.createElement("a");
3049
- n.href = i, n.download = e, document.body.appendChild(n), n.click(), document.body.removeChild(n), t instanceof Blob && URL.revokeObjectURL(i);
3060
+
3061
+ vec2 filterTextureCoord(void)
3062
+ {
3063
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3050
3064
  }
3051
- async function Qa(t, e) {
3052
- const { pixelRatio: i = 2 } = e ?? {}, n = t.app, r = n.renderer.resolution, s = n.screen.width, o = n.screen.height;
3053
- n.renderer.resolution = i, n.renderer.resize(s, o), n.render();
3054
- const a = Ka(n.canvas);
3055
- return n.renderer.resolution = r, n.renderer.resize(s, o), n.render(), a;
3065
+
3066
+ void main(void)
3067
+ {
3068
+ gl_Position = filterVertexPosition();
3069
+ vTextureCoord = filterTextureCoord();
3056
3070
  }
3057
- async function ec(t, e) {
3058
- const i = t.app.renderer;
3059
- try {
3060
- return await i.extract.base64({ target: e, format: "png" });
3061
- } catch {
3062
- try {
3063
- const n = i.extract.canvas(e);
3064
- return Za(n);
3065
- } catch {
3066
- return "";
3071
+ `
3072
+ );
3073
+ function Za(t) {
3074
+ return (
3075
+ /* glsl */
3076
+ `
3077
+ precision highp float;
3078
+
3079
+ in vec2 vTextureCoord;
3080
+ out vec4 finalColor;
3081
+
3082
+ uniform sampler2D uTexture;
3083
+ uniform vec4 uInputSize;
3084
+ uniform float uThreshold;
3085
+ uniform float uIntensity;
3086
+ uniform float uRadius;
3087
+
3088
+ const int SAMPLES = ${t};
3089
+ const float FSAMPLES = ${t}.0;
3090
+
3091
+ void main(void)
3092
+ {
3093
+ vec4 original = texture(uTexture, vTextureCoord);
3094
+
3095
+ /* ---- 提取高亮区域并模糊 ---- */
3096
+ vec3 bloom = vec3(0.0);
3097
+ float totalWeight = 0.0;
3098
+
3099
+ for (int i = -SAMPLES; i <= SAMPLES; i++) {
3100
+ for (int j = -SAMPLES; j <= SAMPLES; j++) {
3101
+ vec2 offset = vec2(float(i), float(j)) * uRadius * uInputSize.zw;
3102
+ vec4 samp = texture(uTexture, vTextureCoord + offset);
3103
+
3104
+ // 亮度提取(Luminance)
3105
+ float lum = dot(samp.rgb, vec3(0.2126, 0.7152, 0.0722));
3106
+ float bright = max(0.0, lum - uThreshold);
3107
+
3108
+ // 高斯权重近似
3109
+ float dist = length(vec2(float(i), float(j)));
3110
+ float weight = exp(-dist * dist / (2.0 * FSAMPLES));
3111
+
3112
+ bloom += samp.rgb * bright * weight;
3113
+ totalWeight += weight;
3114
+ }
3067
3115
  }
3068
- }
3116
+
3117
+ bloom /= totalWeight;
3118
+
3119
+ /* ---- 叠加 ---- */
3120
+ vec3 result = original.rgb + bloom * uIntensity;
3121
+
3122
+ finalColor = vec4(result, original.a);
3069
3123
  }
3070
- async function tc(t) {
3071
- const e = /* @__PURE__ */ new Map();
3072
- for (const i of t.nodes.getAll()) {
3073
- const n = i.options.icon;
3074
- if (!e.has(n))
3075
- try {
3076
- const r = await t.icons.loadTexture(n), s = await ec(t, r);
3077
- e.set(n, s);
3078
- } catch {
3079
- e.set(n, "");
3124
+ `
3125
+ );
3126
+ }
3127
+ class Qa extends G {
3128
+ constructor(e = {}) {
3129
+ const i = e.threshold ?? 0.5, n = e.intensity ?? 1, r = e.radius ?? 2, s = e.samples ?? 3, o = U.from({
3130
+ vertex: Ka,
3131
+ fragment: Za(s),
3132
+ name: "bloom-filter"
3133
+ });
3134
+ super({
3135
+ glProgram: o,
3136
+ resources: {
3137
+ bloomUniforms: new H({
3138
+ uThreshold: { value: i, type: "f32" },
3139
+ uIntensity: { value: n, type: "f32" },
3140
+ uRadius: { value: r, type: "f32" }
3141
+ })
3080
3142
  }
3143
+ });
3081
3144
  }
3082
- return e;
3083
- }
3084
- async function ic(t, e) {
3085
- const { background: i = !0 } = e ?? {}, n = t.projectionContainer, r = n.skew.x, s = n.skew.y, o = n.scale.x, a = n.position.x, c = n.position.y, l = Math.ceil(t.app.screen.width), h = Math.ceil(t.app.screen.height), u = o * Math.cos(s), f = o * Math.sin(s), d = o * Math.sin(r), p = o * Math.cos(r), m = Math.cos(s), v = Math.sin(s), _ = Math.sin(r), x = Math.cos(r), w = m * x - v * _, S = x / w, b = -v / w, E = -_ / w, N = m / w, j = u * p - f * d, ee = [
3086
- { x: 0, y: 0 },
3087
- { x: l, y: 0 },
3088
- { x: 0, y: h },
3089
- { x: l, y: h }
3090
- ];
3091
- let V = 1 / 0, X = 1 / 0, ue = -1 / 0, fe = -1 / 0;
3092
- for (const { x: I, y: te } of ee) {
3093
- const pe = I - a, ge = te - c, me = (p * pe - d * ge) / j, ve = (-f * pe + u * ge) / j;
3094
- V = Math.min(V, me), X = Math.min(X, ve), ue = Math.max(ue, me), fe = Math.max(fe, ve);
3145
+ /* ---------- 便捷存取器 ---------- */
3146
+ get threshold() {
3147
+ return this.resources.bloomUniforms.uniforms.uThreshold;
3095
3148
  }
3096
- const q = g * 2, de = {
3097
- x: Math.floor((V - q) / g) * g,
3098
- y: Math.floor((X - q) / g) * g,
3099
- width: Math.ceil((ue - V + q * 2) / g) * g,
3100
- height: Math.ceil((fe - X + q * 2) / g) * g
3101
- }, $ = await tc(t), O = [];
3102
- O.push(
3103
- '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"',
3104
- ` width="${l}" height="${h}" viewBox="0 0 ${l} ${h}">`
3105
- ), O.push(" <defs>");
3106
- for (const [I, te] of $)
3107
- te && O.push(
3108
- ` <symbol id="icon-${Ct(I)}" viewBox="0 0 100 100">`,
3109
- ` <image href="${te}" width="100" height="100" />`,
3110
- " </symbol>"
3111
- );
3112
- if (O.push(" </defs>"), i) {
3113
- const I = t.grid.options, te = Ze(I.bgColor ?? 0);
3114
- O.push(` <rect width="${l}" height="${h}" fill="${te}" />`);
3149
+ set threshold(e) {
3150
+ this.resources.bloomUniforms.uniforms.uThreshold = e;
3115
3151
  }
3116
- const Gt = `matrix(${u},${f},${d},${p},${a},${c})`;
3117
- if (O.push(` <g transform="${Gt}">`), i) {
3118
- const I = await nc(t, de);
3119
- I && O.push(I);
3152
+ get intensity() {
3153
+ return this.resources.bloomUniforms.uniforms.uIntensity;
3120
3154
  }
3121
- for (const I of t.rects.getAll())
3122
- O.push(rc(I.options));
3123
- for (const I of t.lines.getAll())
3124
- O.push(sc(I));
3125
- for (const I of t.nodes.getAll())
3126
- O.push(oc(I.options, $, S, b, E, N));
3127
- for (const I of t.texts.getAll())
3128
- O.push(ac(I.options, S, b, E, N));
3129
- return O.push(" </g>"), O.push("</svg>"), O.join(`
3130
- `);
3131
- }
3132
- async function nc(t, e) {
3133
- const n = t.grid.getGraphics().children[0];
3134
- if (!n) return "";
3135
- const r = t.grid.options.tileSize ?? 100, s = n.x, o = n.y, a = n.width, c = n.height, l = n.tilePosition?.x ?? 0, h = n.tilePosition?.y ?? 0;
3136
- try {
3137
- n.x = 0, n.y = 0, n.width = e.width, n.height = e.height, n.tilePosition && n.tilePosition.set(
3138
- -e.x - r / 2,
3139
- -e.y - r / 2
3140
- );
3141
- const u = n.parent;
3142
- u && u.removeChild(n);
3143
- const f = await t.app.renderer.extract.base64({
3144
- target: n,
3145
- format: "png"
3146
- });
3147
- return u && u.addChild(n), ` <image href="${f}" x="${e.x}" y="${e.y}" width="${e.width}" height="${e.height}" />`;
3148
- } catch {
3149
- return "";
3150
- } finally {
3151
- n.x = s, n.y = o, n.width = a, n.height = c, n.tilePosition && n.tilePosition.set(l, h);
3155
+ set intensity(e) {
3156
+ this.resources.bloomUniforms.uniforms.uIntensity = e;
3152
3157
  }
3153
- }
3154
- function rc(t) {
3155
- const e = t.x * g - g / 2, i = t.y * g - g / 2, n = (t.width ?? 1) * g, r = (t.height ?? 1) * g, s = Ze(t.bgColor ?? 3447003), o = t.bgOpacity ?? 0.8, a = Ze(t.borderColor ?? 16777215), c = t.borderWidth ?? 2, l = t.borderOpacity ?? 1, h = t.borderRadius ?? 0, u = h > 0 ? ` rx="${h}" ry="${h}"` : "";
3156
- return ` <rect x="${e}" y="${i}" width="${n}" height="${r}"${u} fill="${s}" fill-opacity="${o}" stroke="${a}" stroke-width="${c}" stroke-opacity="${l}" />`;
3157
- }
3158
- function sc(t) {
3159
- const e = t.options, i = Ze(e.color ?? 4473958), n = e.width ?? 6, r = t.getPoints();
3160
- let s;
3161
- if (r && r.length >= 2)
3162
- s = r.map((a) => `${a.x},${a.y}`).join(" ");
3163
- else {
3164
- const a = [];
3165
- for (const c of e.anchor) {
3166
- const l = t.resolveAnchorPosition(c);
3167
- l && a.push({ x: l.x * g, y: l.y * g });
3168
- }
3169
- if (a.length < 2) return "";
3170
- s = a.map((c) => `${c.x},${c.y}`).join(" ");
3158
+ get radius() {
3159
+ return this.resources.bloomUniforms.uniforms.uRadius;
3171
3160
  }
3172
- const o = e.mode === "dashed" ? ` stroke-dasharray="${(e.dashPattern ?? [10, 5]).join(",")}"` : "";
3173
- return ` <polyline points="${s}" fill="none" stroke="${i}" stroke-width="${n}" stroke-linecap="round" stroke-linejoin="round"${o} />`;
3174
- }
3175
- function oc(t, e, i, n, r, s) {
3176
- const o = t.x * g, a = t.y * g, c = t.scale ?? 1, l = t.isFlat ?? !1, h = l ? 1 : t.scaleY ?? 1, u = t.offsetY ?? 0, f = t.icon;
3177
- if (!(e.has(f) && e.get(f) !== "")) return "";
3178
- const p = 100, m = -p / 2 * h + cr / 2 + u, v = [];
3179
- if (l) {
3180
- const _ = p * c, x = p * c, w = o - _ / 2, S = a + c * (m - p / 2);
3181
- v.push(
3182
- ` <use href="#icon-${Ct(f)}" x="${w}" y="${S}" width="${_}" height="${x}" />`
3183
- );
3184
- } else {
3185
- const _ = p * c, x = p * c * h, w = -_ / 2, S = c * h * m - x / 2, b = `matrix(${i},${n},${r},${s},0,0)`;
3186
- v.push(` <g transform="translate(${o},${a})">`), v.push(` <g transform="${b}">`), v.push(
3187
- ` <use href="#icon-${Ct(f)}" x="${w}" y="${S}" width="${_}" height="${x}" />`
3188
- ), v.push(" </g>"), v.push(" </g>");
3161
+ set radius(e) {
3162
+ this.resources.bloomUniforms.uniforms.uRadius = e;
3189
3163
  }
3190
- return v.join(`
3191
- `);
3192
- }
3193
- function ac(t, e, i, n, r) {
3194
- const s = t.x * g, o = t.y * g, a = t.fontSize ?? 16, c = Ze(t.color ?? 16777215), l = t.fontWeight ?? "normal", h = t.fontFamily ?? "Arial", u = t.align ?? "center", f = t.position ?? "x", d = u === "left" ? "start" : u === "right" ? "end" : "middle", p = Ct(t.text), m = `text-anchor="${d}" fill="${c}" font-size="${a}" font-weight="${l}" font-family="${h}"`;
3195
- if (f === "screen") {
3196
- const v = `matrix(${e},${i},${n},${r},0,0)`;
3197
- return [
3198
- ` <g transform="translate(${s},${o})">`,
3199
- ` <text transform="${v}" ${m}>${p}</text>`,
3200
- " </g>"
3201
- ].join(`
3202
- `);
3203
- } else return f === "y" ? ` <text x="${s}" y="${o}" transform="rotate(-90,${s},${o})" ${m}>${p}</text>` : ` <text x="${s}" y="${o}" ${m}>${p}</text>`;
3204
3164
  }
3205
- class De extends le {
3206
- /** 默认缩放级别 */
3207
- static DEFAULT_ZOOM = 0.8;
3208
- id;
3209
- app;
3210
- icons;
3211
- states;
3212
- postprocess;
3213
- projectionContainer;
3214
- layer;
3215
- grid;
3216
- nodes;
3217
- lines;
3218
- texts;
3219
- rects;
3220
- fences;
3221
- waves;
3222
- images;
3223
- sceneItems = /* @__PURE__ */ new Set();
3224
- /** 上一帧相机状态,用于 ticker 检测增量变化 */
3225
- lastCameraState = { x: 0, y: 0, scale: 0 };
3226
- _isActive = !1;
3227
- _cameraHandler;
3228
- constructor(e, i, n, r, s) {
3229
- super(), this.id = e, this.app = i, this.icons = n, this.states = r, this.postprocess = s, this.projectionContainer = new R(), this.layer = new Ja(this.projectionContainer), this.lines = new Ra(this), this.nodes = new Oa(this), this.texts = new Na(this), this.rects = new Ga(this), this.fences = new Ya(this), this.waves = new Wa(this), this.images = new Va(this), this.grid = new Da();
3230
- const { skewX: o, skewY: a } = lr();
3231
- this.projectionContainer.sortableChildren = !0, this.projectionContainer.skew.set(o, a), this.projectionContainer.scale.set(De.DEFAULT_ZOOM), this.projectionContainer.position.set(i.screen.width / 2, i.screen.height / 2), this.projectionContainer.addChild(this.grid.getGraphics()), this.grid.getGraphics().zIndex = -1, this.registerItem(this.grid);
3232
- }
3233
- // ========== 激活 / 停用 ==========
3234
- setActive(e) {
3235
- e !== this._isActive && (this._isActive = e, e ? (this.app.stage.addChild(this.projectionContainer), this.postprocess.bind(this.projectionContainer, this.app), this.app.ticker.add(this.tickerCallback, this), this.notifyCameraChange(!1, !1), this.emit("ready")) : (this.app.ticker.remove(this.tickerCallback, this), this.postprocess.unbind(), this.app.stage.removeChild(this.projectionContainer)));
3236
- }
3237
- get isActive() {
3238
- return this._isActive;
3239
- }
3240
- // ========== 序列化 ==========
3241
- toJSON() {
3242
- return {
3243
- nodes: this.nodes.toJSON(),
3244
- lines: this.lines.toJSON(),
3245
- texts: this.texts.toJSON(),
3246
- rects: this.rects.toJSON(),
3247
- fences: this.fences.toJSON(),
3248
- waves: this.waves.toJSON(),
3249
- images: this.images.toJSON(),
3250
- grid: this.grid.toJSON(),
3251
- camera: this.getCamera(),
3252
- layer: this.layer.toJSON()
3253
- };
3254
- }
3255
- fromJSON(e) {
3256
- this.clear(), e.nodes && this.nodes.fromJSON(e.nodes), e.lines && this.lines.fromJSON(e.lines), e.texts && this.texts.fromJSON(e.texts), e.rects && this.rects.fromJSON(e.rects), e.fences && this.fences.fromJSON(e.fences), e.waves && this.waves.fromJSON(e.waves), e.images && this.images.fromJSON(e.images), e.grid && this.grid.fromJSON(e.grid), e.camera && this.setCamera(e.camera), e.layer?.length && this.layer.fromJSON(e.layer);
3257
- }
3258
- /** 序列化为 YAML 字符串 */
3259
- toYAML() {
3260
- return be(this.toJSON());
3261
- }
3262
- /** 从 YAML 字符串反序列化 */
3263
- fromYAML(e) {
3264
- this.fromJSON(Ce(e));
3165
+ function Cf(t, e) {
3166
+ const i = e?.threshold ?? 0.5, n = e?.intensity ?? 1, r = e?.radius ?? 2, s = e?.samples ?? 3;
3167
+ return {
3168
+ postProcessId: t,
3169
+ createFilter() {
3170
+ return new Qa({ threshold: i, intensity: n, radius: r, samples: s });
3171
+ },
3172
+ setOptions(o, a) {
3173
+ const c = o;
3174
+ a.threshold !== void 0 && (c.threshold = a.threshold), a.intensity !== void 0 && (c.intensity = a.intensity), a.radius !== void 0 && (c.radius = a.radius);
3175
+ }
3176
+ };
3177
+ }
3178
+ function Ki(t, e) {
3179
+ const i = e.brightness ?? 1, n = e.contrast ?? 0, r = e.saturate ?? 0, s = e.hue ?? 0;
3180
+ t.reset(), i !== 1 && t.brightness(i, !0), n !== 0 && t.contrast(n, !0), r !== 0 && t.saturate(r, !0), s !== 0 && t.hue(s, !0);
3181
+ }
3182
+ function If(t, e) {
3183
+ const i = {
3184
+ brightness: e?.brightness ?? 1,
3185
+ contrast: e?.contrast ?? 0,
3186
+ saturate: e?.saturate ?? 0,
3187
+ hue: e?.hue ?? 0
3188
+ };
3189
+ return {
3190
+ postProcessId: t,
3191
+ createFilter() {
3192
+ const n = new mi();
3193
+ return Ki(n, i), n;
3194
+ },
3195
+ setOptions(n, r) {
3196
+ Object.assign(i, r), Ki(n, i);
3197
+ }
3198
+ };
3199
+ }
3200
+ const ec = (
3201
+ /* glsl */
3202
+ `
3203
+ in vec2 aPosition;
3204
+ out vec2 vTextureCoord;
3205
+
3206
+ uniform vec4 uInputSize;
3207
+ uniform vec4 uOutputFrame;
3208
+ uniform vec4 uOutputTexture;
3209
+
3210
+ vec4 filterVertexPosition(void)
3211
+ {
3212
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3213
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3214
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3215
+ return vec4(position, 0.0, 1.0);
3216
+ }
3217
+
3218
+ vec2 filterTextureCoord(void)
3219
+ {
3220
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3221
+ }
3222
+
3223
+ void main(void)
3224
+ {
3225
+ gl_Position = filterVertexPosition();
3226
+ vTextureCoord = filterTextureCoord();
3227
+ }
3228
+ `
3229
+ ), tc = (
3230
+ /* glsl */
3231
+ `
3232
+ precision highp float;
3233
+
3234
+ in vec2 vTextureCoord;
3235
+ out vec4 finalColor;
3236
+
3237
+ uniform sampler2D uTexture;
3238
+ uniform vec4 uInputSize;
3239
+ uniform vec4 uOutputFrame;
3240
+ uniform float uRadius;
3241
+ uniform float uSoftness;
3242
+ uniform float uIntensity;
3243
+ uniform vec3 uColor;
3244
+
3245
+ void main(void)
3246
+ {
3247
+ vec4 original = texture(uTexture, vTextureCoord);
3248
+
3249
+ // 将归一化 0.5 中心映射到实际纹理坐标空间
3250
+ vec2 texCenter = vec2(0.5) * (uOutputFrame.zw * uInputSize.zw);
3251
+ // 从中心到边缘的距离(归一化回 0~1 空间)
3252
+ vec2 center = (vTextureCoord - texCenter) / (uOutputFrame.zw * uInputSize.zw);
3253
+ float dist = length(center) * 2.0;
3254
+
3255
+ // smoothstep 在 radius 和 radius+softness 之间平滑过渡
3256
+ float vignetteFactor = smoothstep(uRadius, uRadius + uSoftness, dist) * uIntensity;
3257
+
3258
+ // 将边缘像素向暗角颜色混合
3259
+ finalColor = vec4(mix(original.rgb, uColor, vignetteFactor), original.a);
3260
+ }
3261
+ `
3262
+ );
3263
+ class ic extends G {
3264
+ constructor(e = {}) {
3265
+ const i = e.radius ?? 0.3, n = e.softness ?? 0.5, r = e.intensity ?? 0.8, s = e.color ?? [0, 0, 0], o = U.from({
3266
+ vertex: ec,
3267
+ fragment: tc,
3268
+ name: "vignette-filter"
3269
+ });
3270
+ super({
3271
+ glProgram: o,
3272
+ resources: {
3273
+ vignetteUniforms: new H({
3274
+ uRadius: { value: i, type: "f32" },
3275
+ uSoftness: { value: n, type: "f32" },
3276
+ uIntensity: { value: r, type: "f32" },
3277
+ uColor: { value: new Float32Array(s), type: "vec3<f32>" }
3278
+ })
3279
+ }
3280
+ });
3265
3281
  }
3266
- // ========== 导出 ==========
3267
- /**
3268
- * 将场景导出为 PNG 图片
3269
- * @param options 导出选项(padding / pixelRatio)
3270
- * @returns PNG Blob
3271
- */
3272
- async exportPNG(e) {
3273
- return Qa(this, e);
3282
+ /* ---------- 便捷存取器 ---------- */
3283
+ get radius() {
3284
+ return this.resources.vignetteUniforms.uniforms.uRadius;
3274
3285
  }
3275
- /**
3276
- * 将场景导出为 SVG 字符串
3277
- * @param options 导出选项(padding / background)
3278
- * @returns SVG 文档字符串
3279
- */
3280
- async exportSVG(e) {
3281
- return ic(this, e);
3286
+ set radius(e) {
3287
+ this.resources.vignetteUniforms.uniforms.uRadius = e;
3282
3288
  }
3283
- /**
3284
- * 导出 PNG 并自动触发浏览器下载
3285
- * @param filename 文件名,默认为 `{sceneId}.png`
3286
- * @param options 导出选项
3287
- */
3288
- async downloadPNG(e, i) {
3289
- const n = await this.exportPNG(i);
3290
- It(n, e ?? `${this.id}.png`);
3289
+ get softness() {
3290
+ return this.resources.vignetteUniforms.uniforms.uSoftness;
3291
3291
  }
3292
- /**
3293
- * 导出 SVG 并自动触发浏览器下载
3294
- * @param filename 文件名,默认为 `{sceneId}.svg`
3295
- * @param options 导出选项
3296
- */
3297
- async downloadSVG(e, i) {
3298
- const n = await this.exportSVG(i);
3299
- It(n, e ?? `${this.id}.svg`);
3292
+ set softness(e) {
3293
+ this.resources.vignetteUniforms.uniforms.uSoftness = e;
3300
3294
  }
3301
- // ========== 相机控制 ==========
3302
- /**
3303
- * 注册外部相机处理器(通常由 ViewportPlugin 调用)。
3304
- * 注册后,`setCamera` 将委托给该处理器而非直接更新 projectionContainer,
3305
- * 从而允许插件实现动画过渡等特性。
3306
- *
3307
- * @param handler 处理函数,传 `undefined` 可清除
3308
- */
3309
- setCameraHandler(e) {
3310
- this._cameraHandler = e;
3295
+ get intensity() {
3296
+ return this.resources.vignetteUniforms.uniforms.uIntensity;
3311
3297
  }
3312
- /**
3313
- * 获取当前相机状态
3314
- */
3315
- getCamera() {
3316
- const e = this.projectionContainer;
3317
- return { zoom: e.scale.x, x: e.position.x, y: e.position.y };
3298
+ set intensity(e) {
3299
+ this.resources.vignetteUniforms.uniforms.uIntensity = e;
3318
3300
  }
3319
- /**
3320
- * 设置相机状态。
3321
- * - 如果有外部处理器(ViewportPlugin 等),委托给处理器更新(可带动画过渡)。
3322
- * - 否则,直接更新 projectionContainer。
3323
- *
3324
- * 返回 Promise,在有动画过渡时可 `await` 等待完成。
3325
- *
3326
- * @param camera 部分相机参数,未传的字段保留当前值
3327
- */
3328
- setCamera(e) {
3329
- const i = this.projectionContainer, n = {
3330
- zoom: e.zoom ?? i.scale.x,
3331
- x: e.x ?? i.position.x,
3332
- y: e.y ?? i.position.y
3333
- };
3334
- return this._cameraHandler ? Promise.resolve(this._cameraHandler(n)) : (this.applyCamera(n), Promise.resolve());
3301
+ get color() {
3302
+ const e = this.resources.vignetteUniforms.uniforms.uColor;
3303
+ return [e[0], e[1], e[2]];
3335
3304
  }
3336
- /**
3337
- * 直接将相机状态应用到 projectionContainer。
3338
- * 当没有安装 ViewportPlugin 时由 `setCamera` 内部调用。
3339
- * 也可被外部直接调用以跳过插件处理。
3340
- */
3341
- applyCamera(e) {
3342
- const i = this.projectionContainer, n = this.lastCameraState, r = e.x !== n.x || e.y !== n.y, s = e.zoom !== n.scale;
3343
- i.scale.set(e.zoom), i.position.set(e.x, e.y), this._isActive && (this.lastCameraState = { x: i.x, y: i.y, scale: i.scale.x }, this.notifyCameraChange(r, s));
3305
+ set color(e) {
3306
+ const i = this.resources.vignetteUniforms.uniforms.uColor;
3307
+ i[0] = e[0], i[1] = e[1], i[2] = e[2];
3344
3308
  }
3345
- /**
3346
- * 获取默认相机状态(居中 + 默认缩放)
3347
- */
3348
- getDefaultCamera() {
3349
- return {
3350
- zoom: De.DEFAULT_ZOOM,
3351
- x: this.app.screen.width / 2,
3352
- y: this.app.screen.height / 2
3353
- };
3309
+ }
3310
+ function Af(t, e) {
3311
+ const i = e?.radius ?? 0.3, n = e?.softness ?? 0.5, r = e?.intensity ?? 0.8, s = e?.color ?? [0, 0, 0];
3312
+ return {
3313
+ postProcessId: t,
3314
+ createFilter() {
3315
+ return new ic({ radius: i, softness: n, intensity: r, color: s });
3316
+ },
3317
+ setOptions(o, a) {
3318
+ const c = o;
3319
+ a.radius !== void 0 && (c.radius = a.radius), a.softness !== void 0 && (c.softness = a.softness), a.intensity !== void 0 && (c.intensity = a.intensity), a.color !== void 0 && (c.color = a.color);
3320
+ }
3321
+ };
3322
+ }
3323
+ const nc = (
3324
+ /* glsl */
3325
+ `
3326
+ in vec2 aPosition;
3327
+ out vec2 vTextureCoord;
3328
+
3329
+ uniform vec4 uInputSize;
3330
+ uniform vec4 uOutputFrame;
3331
+ uniform vec4 uOutputTexture;
3332
+
3333
+ vec4 filterVertexPosition(void)
3334
+ {
3335
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3336
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3337
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3338
+ return vec4(position, 0.0, 1.0);
3339
+ }
3340
+
3341
+ vec2 filterTextureCoord(void)
3342
+ {
3343
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3344
+ }
3345
+
3346
+ void main(void)
3347
+ {
3348
+ gl_Position = filterVertexPosition();
3349
+ vTextureCoord = filterTextureCoord();
3350
+ }
3351
+ `
3352
+ ), rc = (
3353
+ /* glsl */
3354
+ `
3355
+ precision highp float;
3356
+
3357
+ in vec2 vTextureCoord;
3358
+ out vec4 finalColor;
3359
+
3360
+ uniform sampler2D uTexture;
3361
+ uniform vec4 uInputSize;
3362
+ uniform float uPixelSize;
3363
+
3364
+ void main(void)
3365
+ {
3366
+ // 将纹理坐标对齐到像素块中心
3367
+ vec2 pixelCoord = vTextureCoord * uInputSize.xy;
3368
+ vec2 snapped = floor(pixelCoord / uPixelSize) * uPixelSize + uPixelSize * 0.5;
3369
+ vec2 uv = snapped * uInputSize.zw;
3370
+
3371
+ finalColor = texture(uTexture, uv);
3372
+ }
3373
+ `
3374
+ );
3375
+ class sc extends G {
3376
+ constructor(e = {}) {
3377
+ const i = e.pixelSize ?? 8, n = U.from({
3378
+ vertex: nc,
3379
+ fragment: rc,
3380
+ name: "pixelate-filter"
3381
+ });
3382
+ super({
3383
+ glProgram: n,
3384
+ resources: {
3385
+ pixelateUniforms: new H({
3386
+ uPixelSize: { value: i, type: "f32" }
3387
+ })
3388
+ }
3389
+ });
3354
3390
  }
3355
- // ========== 常用操作 ==========
3356
- get stage() {
3357
- return this.app.stage;
3391
+ /* ---------- 便捷存取器 ---------- */
3392
+ get pixelSize() {
3393
+ return this.resources.pixelateUniforms.uniforms.uPixelSize;
3358
3394
  }
3359
- clear() {
3360
- this.lines.clear(), this.nodes.clear(), this.texts.clear(), this.rects.clear(), this.fences.clear(), this.waves.clear(), this.images.clear(), this.layer.clear();
3395
+ set pixelSize(e) {
3396
+ this.resources.pixelateUniforms.uniforms.uPixelSize = e;
3361
3397
  }
3362
- destroy() {
3363
- this.setActive(!1), this.emit("destroyed"), this.grid?.destroy(), this.lines?.destroy(), this.nodes?.destroy(), this.texts?.destroy(), this.rects?.destroy(), this.fences?.destroy(), this.waves?.destroy(), this.images?.destroy(), this.layer?.destroy(), this.removeAllListeners(), this.sceneItems.clear(), this.app.stage.removeChild(this.projectionContainer), this.projectionContainer.destroy();
3398
+ }
3399
+ function Tf(t, e) {
3400
+ const i = e?.pixelSize ?? 8;
3401
+ return {
3402
+ postProcessId: t,
3403
+ createFilter() {
3404
+ return new sc({ pixelSize: i });
3405
+ },
3406
+ setOptions(n, r) {
3407
+ const s = n;
3408
+ r.pixelSize !== void 0 && (s.pixelSize = r.pixelSize);
3409
+ }
3410
+ };
3411
+ }
3412
+ const oc = (
3413
+ /* glsl */
3414
+ `
3415
+ in vec2 aPosition;
3416
+ out vec2 vTextureCoord;
3417
+
3418
+ uniform vec4 uInputSize;
3419
+ uniform vec4 uOutputFrame;
3420
+ uniform vec4 uOutputTexture;
3421
+
3422
+ vec4 filterVertexPosition(void)
3423
+ {
3424
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3425
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3426
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3427
+ return vec4(position, 0.0, 1.0);
3428
+ }
3429
+
3430
+ vec2 filterTextureCoord(void)
3431
+ {
3432
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3433
+ }
3434
+
3435
+ void main(void)
3436
+ {
3437
+ gl_Position = filterVertexPosition();
3438
+ vTextureCoord = filterTextureCoord();
3439
+ }
3440
+ `
3441
+ ), ac = (
3442
+ /* glsl */
3443
+ `
3444
+ precision highp float;
3445
+
3446
+ in vec2 vTextureCoord;
3447
+ out vec4 finalColor;
3448
+
3449
+ uniform sampler2D uTexture;
3450
+ uniform float uIntensity;
3451
+ uniform float uSeed;
3452
+
3453
+ // 伪随机噪声生成
3454
+ float random(vec2 co)
3455
+ {
3456
+ return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
3457
+ }
3458
+
3459
+ void main(void)
3460
+ {
3461
+ vec4 original = texture(uTexture, vTextureCoord);
3462
+
3463
+ // 用坐标和种子生成噪声
3464
+ float noise = random(vTextureCoord + uSeed) * 2.0 - 1.0;
3465
+
3466
+ // 叠加噪声到 RGB 通道
3467
+ vec3 result = original.rgb + noise * uIntensity;
3468
+
3469
+ finalColor = vec4(clamp(result, 0.0, 1.0), original.a);
3470
+ }
3471
+ `
3472
+ );
3473
+ class cc extends G {
3474
+ constructor(e = {}) {
3475
+ const i = e.intensity ?? 0.15, n = e.seed ?? 0, r = U.from({
3476
+ vertex: oc,
3477
+ fragment: ac,
3478
+ name: "noise-filter"
3479
+ });
3480
+ super({
3481
+ glProgram: r,
3482
+ resources: {
3483
+ noiseUniforms: new H({
3484
+ uIntensity: { value: i, type: "f32" },
3485
+ uSeed: { value: n, type: "f32" }
3486
+ })
3487
+ }
3488
+ });
3364
3489
  }
3365
- // ========== SceneItem 注册和渲染循环 ==========
3366
- registerItem(e) {
3367
- this.sceneItems.add(e);
3490
+ /* ---------- 便捷存取器 ---------- */
3491
+ get intensity() {
3492
+ return this.resources.noiseUniforms.uniforms.uIntensity;
3368
3493
  }
3369
- unregisterItem(e) {
3370
- this.sceneItems.delete(e);
3494
+ set intensity(e) {
3495
+ this.resources.noiseUniforms.uniforms.uIntensity = e;
3371
3496
  }
3372
- tickerCallback = () => {
3373
- const e = this.app.ticker.deltaMS;
3374
- for (const a of this.sceneItems)
3375
- a.onRender(e), a.tickStates(e);
3376
- this.postprocess.update(e);
3377
- const { x: i, y: n } = this.projectionContainer.position, r = this.projectionContainer.scale.x, s = i !== this.lastCameraState.x || n !== this.lastCameraState.y, o = r !== this.lastCameraState.scale;
3378
- (s || o) && (this.lastCameraState = { x: i, y: n, scale: r }, this.notifyCameraChange(s, o));
3379
- };
3380
- /**
3381
- * 通知所有 SceneItem 相机已变化,并向外发射相机事件。
3382
- *
3383
- * @param positionChanged 位置(x/y)是否改变;传 `false` 时不发射 `camera:move` 事件
3384
- * @param zoomChanged 缩放是否改变;传 `false` 时不发射 `camera:zoom` 事件
3385
- *
3386
- * 两者均为 `false` 时仅通知 SceneItem(用于场景初始化),不发射任何相机事件。
3387
- */
3388
- notifyCameraChange(e = !0, i = !0) {
3389
- const n = this.getViewRect(), r = this.projectionContainer.scale.x;
3390
- for (const o of this.sceneItems) o.onCameraChange(n, r);
3391
- if (!e && !i) return;
3392
- const s = {
3393
- ...this.getCamera(),
3394
- positionChanged: e,
3395
- zoomChanged: i
3396
- };
3397
- this.emit("camera:change", s), e && this.emit("camera:move", s), i && this.emit("camera:zoom", s);
3497
+ get seed() {
3498
+ return this.resources.noiseUniforms.uniforms.uSeed;
3398
3499
  }
3399
- getViewRect() {
3400
- const { width: e, height: i } = this.app.screen, n = this.projectionContainer, r = n.skew.x, s = n.skew.y, o = n.scale.x, a = n.scale.y, c = Math.cos(s) * o, l = Math.sin(s) * o, h = Math.sin(r) * a, u = Math.cos(r) * a, f = n.x, d = n.y, p = c * u - l * h, m = u / p, v = -l / p, _ = -h / p, x = c / p, w = (ue, fe) => {
3401
- const q = ue - f, de = fe - d;
3402
- return { x: m * q + _ * de, y: v * q + x * de };
3403
- }, S = w(0, 0), b = w(e, 0), E = w(0, i), N = w(e, i), j = Math.min(S.x, b.x, E.x, N.x), ee = Math.min(S.y, b.y, E.y, N.y), V = Math.max(S.x, b.x, E.x, N.x), X = Math.max(S.y, b.y, E.y, N.y);
3404
- return { x: j, y: ee, width: V - j, height: X - ee };
3500
+ set seed(e) {
3501
+ this.resources.noiseUniforms.uniforms.uSeed = e;
3405
3502
  }
3406
- /**
3407
- * 计算场景中所有内容的包围盒(projectionContainer 本地坐标系)
3408
- * @returns 包围盒,如果场景为空则返回 null
3409
- */
3410
- getContentBounds() {
3411
- let i = 1 / 0, n = 1 / 0, r = -1 / 0, s = -1 / 0, o = !1;
3412
- const a = (c, l) => {
3413
- o = !0, c < i && (i = c), l < n && (n = l), c > r && (r = c), l > s && (s = l);
3414
- };
3415
- for (const c of this.nodes.getAll())
3416
- a(c.options.x * 100, c.options.y * 100);
3417
- for (const c of this.rects.getAll()) {
3418
- const l = c.options.x * 100, h = c.options.y * 100, u = (c.options.width ?? 1) * 100, f = (c.options.height ?? 1) * 100;
3419
- a(l, h), a(l + u, h + f);
3503
+ }
3504
+ function Of(t, e) {
3505
+ const i = e?.intensity ?? 0.15;
3506
+ let n = e?.animated ?? !0;
3507
+ return {
3508
+ postProcessId: t,
3509
+ createFilter() {
3510
+ return new cc({ intensity: i });
3511
+ },
3512
+ onUpdate(r, s) {
3513
+ n && (r.seed = Math.random());
3514
+ },
3515
+ setOptions(r, s) {
3516
+ const o = r;
3517
+ s.intensity !== void 0 && (o.intensity = s.intensity), s.animated !== void 0 && (n = s.animated);
3420
3518
  }
3421
- for (const c of this.texts.getAll())
3422
- a(c.options.x * 100, c.options.y * 100);
3423
- for (const c of this.lines.getAll())
3424
- for (const l of c.options.anchor) {
3425
- const h = c.resolveAnchorPosition(l);
3426
- h && a(h.x * 100, h.y * 100);
3427
- }
3428
- for (const c of this.fences.getAll()) {
3429
- const l = c.options.x * 100, h = c.options.y * 100, u = c.options.direction ?? "+x", f = (c.options.length ?? 4) * 100;
3430
- switch (a(l, h), u) {
3431
- case "x":
3432
- case "+x":
3433
- a(l + f, h);
3434
- break;
3435
- case "-x":
3436
- a(l - f, h);
3437
- break;
3438
- case "y":
3439
- case "+y":
3440
- a(l, h + f);
3441
- break;
3442
- case "-y":
3443
- a(l, h - f);
3444
- break;
3445
- }
3446
- }
3447
- for (const c of this.waves.getAll()) {
3448
- const l = (c.options.width ?? 4) * 100, h = (c.options.height ?? 4) * 100;
3449
- a(c.options.x * 100 - l / 2, c.options.y * 100 - h / 2), a(c.options.x * 100 + l / 2, c.options.y * 100 + h / 2);
3450
- }
3451
- for (const c of this.images.getAll())
3452
- a(c.options.x * 100, c.options.y * 100);
3453
- return o ? { x: i, y: n, width: r - i, height: s - n } : null;
3454
- }
3519
+ };
3455
3520
  }
3456
- class Oe {
3457
- topology;
3458
- sceneCleanups = /* @__PURE__ */ new Map();
3459
- _onSceneAdded;
3460
- _onSceneRemoved;
3461
- _onSceneSwitched;
3462
- _onItemPointerDown;
3463
- _onItemPointerUp;
3464
- _onItemPointerOver;
3465
- _onItemPointerOut;
3466
- install(e) {
3467
- this.topology = e;
3468
- for (const i of e.getSceneIds()) {
3469
- const n = e.getScene(i);
3470
- n && this.invokeHookScene(n);
3471
- }
3472
- this._onSceneAdded = (i) => this.invokeHookScene(i), this._onSceneRemoved = ({ id: i }) => {
3473
- this.unhookScene(i), this.onSceneRemoved(i);
3474
- }, this._onSceneSwitched = (i) => this.onSceneSwitched(i), this._onItemPointerDown = (i) => this.onItemPointerDown(i), this._onItemPointerUp = (i) => this.onItemPointerUp(i), this._onItemPointerOver = (i) => this.onItemPointerOver(i), this._onItemPointerOut = (i) => this.onItemPointerOut(i), e.on("scene:added", this._onSceneAdded), e.on("scene:removed", this._onSceneRemoved), e.on("scene:switched", this._onSceneSwitched), e.on("item:pointerdown", this._onItemPointerDown), e.on("item:pointerup", this._onItemPointerUp), e.on("item:pointerover", this._onItemPointerOver), e.on("item:pointerout", this._onItemPointerOut);
3475
- }
3476
- uninstall(e) {
3477
- for (const [i] of this.sceneCleanups) this.unhookScene(i);
3478
- this._onSceneAdded && e.off("scene:added", this._onSceneAdded), this._onSceneRemoved && e.off("scene:removed", this._onSceneRemoved), this._onSceneSwitched && e.off("scene:switched", this._onSceneSwitched), this._onItemPointerDown && e.off("item:pointerdown", this._onItemPointerDown), this._onItemPointerUp && e.off("item:pointerup", this._onItemPointerUp), this._onItemPointerOver && e.off("item:pointerover", this._onItemPointerOver), this._onItemPointerOut && e.off("item:pointerout", this._onItemPointerOut), this._onSceneAdded = void 0, this._onSceneRemoved = void 0, this._onSceneSwitched = void 0, this._onItemPointerDown = void 0, this._onItemPointerUp = void 0, this._onItemPointerOver = void 0, this._onItemPointerOut = void 0, this.topology = void 0;
3479
- }
3480
- unhookScene(e) {
3481
- const i = this.sceneCleanups.get(e);
3482
- i && (i(), this.sceneCleanups.delete(e));
3483
- }
3484
- invokeHookScene(e) {
3485
- const i = this.hookScene(e);
3486
- i && this.sceneCleanups.set(e.id, i);
3487
- }
3488
- onItemPointerDown(e) {
3489
- }
3490
- onItemPointerUp(e) {
3491
- }
3492
- onItemPointerOver(e) {
3493
- }
3494
- onItemPointerOut(e) {
3521
+ const lc = (
3522
+ /* glsl */
3523
+ `
3524
+ in vec2 aPosition;
3525
+ out vec2 vTextureCoord;
3526
+
3527
+ uniform vec4 uInputSize;
3528
+ uniform vec4 uOutputFrame;
3529
+ uniform vec4 uOutputTexture;
3530
+
3531
+ vec4 filterVertexPosition(void)
3532
+ {
3533
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3534
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3535
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3536
+ return vec4(position, 0.0, 1.0);
3537
+ }
3538
+
3539
+ vec2 filterTextureCoord(void)
3540
+ {
3541
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3542
+ }
3543
+
3544
+ void main(void)
3545
+ {
3546
+ gl_Position = filterVertexPosition();
3547
+ vTextureCoord = filterTextureCoord();
3548
+ }
3549
+ `
3550
+ ), hc = (
3551
+ /* glsl */
3552
+ `
3553
+ precision highp float;
3554
+
3555
+ in vec2 vTextureCoord;
3556
+ out vec4 finalColor;
3557
+
3558
+ uniform sampler2D uTexture;
3559
+ uniform vec4 uInputSize;
3560
+ uniform vec4 uOutputFrame;
3561
+ uniform float uOffset;
3562
+ uniform vec2 uDirection;
3563
+
3564
+ void main(void)
3565
+ {
3566
+ // 将归一化 0.5 中心映射到实际纹理坐标空间
3567
+ vec2 texCenter = vec2(0.5) * (uOutputFrame.zw * uInputSize.zw);
3568
+ // 从中心到当前像素的方向
3569
+ vec2 center = vTextureCoord - texCenter;
3570
+ vec2 dir = normalize(center) * uOffset * 0.01;
3571
+
3572
+ // 沿径向偏移采样 RGB 三通道
3573
+ float r = texture(uTexture, vTextureCoord + dir).r;
3574
+ float g = texture(uTexture, vTextureCoord).g;
3575
+ float b = texture(uTexture, vTextureCoord - dir).b;
3576
+ float a = texture(uTexture, vTextureCoord).a;
3577
+
3578
+ finalColor = vec4(r, g, b, a);
3579
+ }
3580
+ `
3581
+ );
3582
+ class uc extends G {
3583
+ constructor(e = {}) {
3584
+ const i = e.offset ?? 3, n = U.from({
3585
+ vertex: lc,
3586
+ fragment: hc,
3587
+ name: "chromatic-aberration-filter"
3588
+ });
3589
+ super({
3590
+ glProgram: n,
3591
+ resources: {
3592
+ chromaticUniforms: new H({
3593
+ uOffset: { value: i, type: "f32" },
3594
+ uDirection: { value: new Float32Array([1, 1]), type: "vec2<f32>" }
3595
+ })
3596
+ }
3597
+ });
3495
3598
  }
3496
- onSceneSwitched(e) {
3599
+ /* ---------- 便捷存取器 ---------- */
3600
+ get offset() {
3601
+ return this.resources.chromaticUniforms.uniforms.uOffset;
3497
3602
  }
3498
- onSceneRemoved(e) {
3603
+ set offset(e) {
3604
+ this.resources.chromaticUniforms.uniforms.uOffset = e;
3499
3605
  }
3500
3606
  }
3501
- class Ti extends Oe {
3502
- activeScene;
3503
- hookScene(e) {
3607
+ function Pf(t, e) {
3608
+ const i = e?.offset ?? 3;
3609
+ return {
3610
+ postProcessId: t,
3611
+ createFilter() {
3612
+ return new uc({ offset: i });
3613
+ },
3614
+ setOptions(n, r) {
3615
+ const s = n;
3616
+ r.offset !== void 0 && (s.offset = r.offset);
3617
+ }
3618
+ };
3619
+ }
3620
+ const fc = (
3621
+ /* glsl */
3622
+ `
3623
+ in vec2 aPosition;
3624
+ out vec2 vTextureCoord;
3625
+
3626
+ uniform vec4 uInputSize;
3627
+ uniform vec4 uOutputFrame;
3628
+ uniform vec4 uOutputTexture;
3629
+
3630
+ vec4 filterVertexPosition(void)
3631
+ {
3632
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3633
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3634
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3635
+ return vec4(position, 0.0, 1.0);
3636
+ }
3637
+
3638
+ vec2 filterTextureCoord(void)
3639
+ {
3640
+ return aPosition * (uOutputFrame.zw * uInputSize.zw);
3641
+ }
3642
+
3643
+ void main(void)
3644
+ {
3645
+ gl_Position = filterVertexPosition();
3646
+ vTextureCoord = filterTextureCoord();
3647
+ }
3648
+ `
3649
+ ), dc = (
3650
+ /* glsl */
3651
+ `
3652
+ precision highp float;
3653
+
3654
+ in vec2 vTextureCoord;
3655
+ out vec4 finalColor;
3656
+
3657
+ uniform sampler2D uTexture;
3658
+ uniform vec4 uInputSize;
3659
+ uniform float uLineWidth;
3660
+ uniform float uIntensity;
3661
+ uniform float uOffset;
3662
+
3663
+ void main(void)
3664
+ {
3665
+ vec4 original = texture(uTexture, vTextureCoord);
3666
+
3667
+ // 按像素行计算扫描线
3668
+ float y = vTextureCoord.y * uInputSize.y + uOffset;
3669
+ float scanline = step(0.5, fract(y / uLineWidth));
3670
+
3671
+ // 暗行降低亮度
3672
+ float factor = 1.0 - scanline * uIntensity;
3673
+
3674
+ finalColor = vec4(original.rgb * factor, original.a);
3675
+ }
3676
+ `
3677
+ );
3678
+ class pc extends G {
3679
+ constructor(e = {}) {
3680
+ const i = e.lineWidth ?? 2, n = e.intensity ?? 0.3, r = e.offset ?? 0, s = U.from({
3681
+ vertex: fc,
3682
+ fragment: dc,
3683
+ name: "scanlines-filter"
3684
+ });
3685
+ super({
3686
+ glProgram: s,
3687
+ resources: {
3688
+ scanlinesUniforms: new H({
3689
+ uLineWidth: { value: i, type: "f32" },
3690
+ uIntensity: { value: n, type: "f32" },
3691
+ uOffset: { value: r, type: "f32" }
3692
+ })
3693
+ }
3694
+ });
3504
3695
  }
3505
- install(e) {
3506
- super.install(e), e.activeScene && this.bindActive(e.activeScene);
3696
+ /* ---------- 便捷存取器 ---------- */
3697
+ get lineWidth() {
3698
+ return this.resources.scanlinesUniforms.uniforms.uLineWidth;
3507
3699
  }
3508
- onSceneSwitched({ to: e }) {
3509
- this.unbindActive(), this.bindActive(e);
3700
+ set lineWidth(e) {
3701
+ this.resources.scanlinesUniforms.uniforms.uLineWidth = e;
3510
3702
  }
3511
- onSceneRemoved(e) {
3512
- this.activeScene?.id === e && (this.onUnbindActiveScene(), this.activeScene = void 0);
3703
+ get intensity() {
3704
+ return this.resources.scanlinesUniforms.uniforms.uIntensity;
3513
3705
  }
3514
- bindActive(e) {
3515
- this.activeScene = e, this.onBindActiveScene(e);
3706
+ set intensity(e) {
3707
+ this.resources.scanlinesUniforms.uniforms.uIntensity = e;
3516
3708
  }
3517
- unbindActive() {
3518
- this.activeScene && (this.onUnbindActiveScene(), this.activeScene = void 0);
3709
+ get offset() {
3710
+ return this.resources.scanlinesUniforms.uniforms.uOffset;
3519
3711
  }
3520
- uninstall(e) {
3521
- this.unbindActive(), super.uninstall(e);
3712
+ set offset(e) {
3713
+ this.resources.scanlinesUniforms.uniforms.uOffset = e;
3522
3714
  }
3523
3715
  }
3524
- class cc extends Ti {
3525
- name = "hit-test";
3526
- _enabled = !0;
3527
- _hoveredItemId = null;
3528
- _hoveredItemType = null;
3529
- get enabled() {
3530
- return this._enabled;
3531
- }
3532
- set enabled(e) {
3533
- this._enabled = e;
3534
- }
3535
- _handlers;
3536
- onBindActiveScene(e) {
3537
- const i = this.topology, n = i.app.stage, r = (c) => {
3538
- let l = c.target;
3539
- for (; l && l !== n; ) {
3540
- const h = Ta(l);
3541
- if (h) {
3542
- const { itemType: u, itemId: f } = h;
3543
- return { itemType: u, itemId: f };
3544
- }
3545
- l = l.parent;
3546
- }
3547
- return { itemType: null, itemId: null };
3548
- }, s = (c) => {
3549
- if (!this._enabled) return;
3550
- const { itemType: l, itemId: h } = r(c);
3551
- i.emit("item:pointerdown", { itemType: l, itemId: h, sceneId: e.id, event: c });
3552
- }, o = (c) => {
3553
- if (!this._enabled) return;
3554
- const { itemType: l, itemId: h } = r(c);
3555
- i.emit("item:pointerup", { itemType: l, itemId: h, sceneId: e.id, event: c });
3556
- }, a = (c) => {
3557
- if (!this._enabled) return;
3558
- const { itemType: l, itemId: h } = r(c);
3559
- (this._hoveredItemId !== h || this._hoveredItemType !== l) && (this._hoveredItemId !== null && this._hoveredItemType !== null && i.emit("item:pointerout", {
3560
- itemType: this._hoveredItemType,
3561
- itemId: this._hoveredItemId,
3562
- sceneId: e.id,
3563
- event: c
3564
- }), h !== null && l !== null && i.emit("item:pointerover", { itemType: l, itemId: h, sceneId: e.id, event: c }), this._hoveredItemId = h, this._hoveredItemType = l);
3565
- };
3566
- n.on("pointerdown", s), n.on("pointerup", o), n.on("pointermove", a), this._handlers = { onPointerDown: s, onPointerUp: o, onPointerMove: a };
3567
- }
3568
- onUnbindActiveScene() {
3569
- if (!this._handlers) return;
3570
- const e = this.topology?.app.stage;
3571
- e && (e.off("pointerdown", this._handlers.onPointerDown), e.off("pointerup", this._handlers.onPointerUp), e.off("pointermove", this._handlers.onPointerMove)), this._handlers = void 0, this._hoveredItemId = null, this._hoveredItemType = null;
3572
- }
3573
- }
3574
- class lc extends le {
3575
- icons = /* @__PURE__ */ new Map();
3576
- textureCache = /* @__PURE__ */ new Map();
3577
- loading = /* @__PURE__ */ new Map();
3578
- /** 添加或批量添加图标 */
3579
- add(e) {
3580
- for (const i of Array.isArray(e) ? e : [e]) {
3581
- const { id: n } = i, r = this.icons.has(n);
3582
- this.icons.set(n, i), r ? (this.textureCache.delete(n), this.loading.delete(n), this.emit("icon:updated", i)) : this.emit("icon:added", i);
3583
- }
3584
- return this;
3585
- }
3586
- /** 删除图标(按 id) */
3587
- remove(e) {
3588
- return this.icons.delete(e) ? (this.textureCache.delete(e), this.loading.delete(e), this.emit("icon:removed", { id: e }), !0) : !1;
3589
- }
3590
- /** 获取图标 URL(按 id) */
3591
- getUrl(e) {
3592
- return this.icons.get(e)?.url;
3593
- }
3594
- /** 获取图标完整信息(按 id) */
3595
- get(e) {
3596
- return this.icons.get(e);
3597
- }
3598
- /** 是否已注册(按 id) */
3599
- has(e) {
3600
- return this.icons.has(e);
3601
- }
3602
- /** 获取所有图标 */
3603
- getAll() {
3604
- return [...this.icons.values()];
3605
- }
3606
- /**
3607
- * 加载图标纹理,自动缓存。
3608
- * 参数为图标 id;若 id 未注册则当作 URL 直接加载。
3609
- */
3610
- async loadTexture(e) {
3611
- const i = this.textureCache.get(e);
3612
- if (i) return i;
3613
- const n = this.loading.get(e);
3614
- if (n) return n;
3615
- const r = this.icons.get(e)?.url ?? e, s = gi.load(r).then((o) => (this.textureCache.set(e, o), this.loading.delete(e), o));
3616
- return this.loading.set(e, s), s;
3617
- }
3618
- /** 清空所有图标和缓存 */
3619
- clear() {
3620
- this.icons.clear(), this.textureCache.clear(), this.loading.clear(), this.emit("icons:cleared");
3621
- }
3622
- /** 序列化 */
3623
- toJSON() {
3624
- return this.getAll();
3625
- }
3626
- /** 反序列化(追加模式) */
3627
- fromJSON(e) {
3628
- this.add(e);
3629
- }
3630
- /** 序列化为 YAML 字符串 */
3631
- toYAML() {
3632
- return be(this.toJSON());
3633
- }
3634
- /** 从 YAML 字符串反序列化(追加模式) */
3635
- fromYAML(e) {
3636
- this.fromJSON(Ce(e));
3637
- }
3638
- destroy() {
3639
- this.clear(), this.removeAllListeners();
3640
- }
3641
- }
3642
- const Wt = /* @__PURE__ */ new WeakMap();
3643
- function bf(t, e, i) {
3644
- const n = i?.frequency ?? 2, r = i?.minAlpha ?? 0.2, s = i?.maxAlpha ?? 1;
3645
- return {
3646
- stateId: t,
3647
- type: e,
3648
- onEnter(o) {
3649
- Wt.set(o, { elapsed: 0 });
3650
- },
3651
- onLeave(o) {
3652
- Wt.delete(o), o.getGraphics().alpha = 1;
3653
- },
3654
- onUpdate(o, a) {
3655
- const c = Wt.get(o);
3656
- if (!c) return;
3657
- c.elapsed += a;
3658
- const l = (Math.sin(c.elapsed * n * Math.PI * 2 / 1e3) + 1) / 2;
3659
- o.getGraphics().alpha = r + l * (s - r);
3660
- }
3661
- };
3662
- }
3663
- const ot = /* @__PURE__ */ new WeakMap();
3664
- function Cf(t, e, i) {
3665
- const n = i?.frequency ?? 2, r = i?.height ?? 12;
3666
- return {
3667
- stateId: t,
3668
- type: e,
3669
- onEnter(s) {
3670
- const o = s.getGraphics();
3671
- ot.set(s, { elapsed: 0, origY: o.position.y, origX: o.position.x });
3672
- },
3673
- onLeave(s) {
3674
- const o = ot.get(s);
3675
- o && (s.getGraphics().position.y = o.origY, s.getGraphics().position.x = o.origX, ot.delete(s));
3676
- },
3677
- onUpdate(s, o) {
3678
- const a = ot.get(s);
3679
- if (!a) return;
3680
- a.elapsed += o;
3681
- const c = Math.abs(Math.sin(a.elapsed * n * Math.PI / 1e3));
3682
- s.getGraphics().position.y = a.origY - r * c, s.getGraphics().position.x = a.origX - r * c;
3683
- }
3684
- };
3685
- }
3686
- const Ve = /* @__PURE__ */ new WeakMap();
3687
- function Rt(t) {
3688
- const e = t.getGraphics(), i = (Ve.get(e) ?? 0) + 1;
3689
- if (Ve.set(e, i), i > 1) return;
3690
- const n = e.getLocalBounds(), r = 100, s = 80, o = Math.min(n.x, -r) - s, a = Math.min(n.y, -r) - s, c = Math.max(n.x + n.width, r) + s, l = Math.max(n.y + n.height, r) + s;
3691
- e.filterArea = new Lt(o, a, c - o, l - a);
3692
- }
3693
- function Dt(t) {
3694
- const e = t.getGraphics(), i = (Ve.get(e) ?? 1) - 1;
3695
- i <= 0 ? (Ve.delete(e), e.filterArea = void 0) : Ve.set(e, i);
3696
- }
3697
- const jt = /* @__PURE__ */ new WeakMap();
3698
- function If(t, e, i) {
3699
- const n = i?.brightness ?? 1.5;
3716
+ function Mf(t, e) {
3717
+ const i = e?.lineWidth ?? 2, n = e?.intensity ?? 0.3;
3718
+ let r = e?.animated ?? !1, s = e?.speed ?? 0.5, o = 0;
3700
3719
  return {
3701
- stateId: t,
3702
- type: e,
3703
- onEnter(r) {
3704
- const s = r.getGraphics(), o = new mi();
3705
- o.brightness(n, !1), jt.set(r, o), Rt(r), s.filters = [...s.filters ?? [], o];
3720
+ postProcessId: t,
3721
+ createFilter() {
3722
+ return new pc({ lineWidth: i, intensity: n, offset: 0 });
3706
3723
  },
3707
- onLeave(r) {
3708
- const s = jt.get(r);
3709
- if (s) {
3710
- const o = r.getGraphics();
3711
- o.filters = (o.filters ?? []).filter((a) => a !== s), Dt(r), jt.delete(r);
3712
- }
3713
- }
3714
- };
3715
- }
3716
- const Vt = /* @__PURE__ */ new WeakMap();
3717
- function Af(t, e, i) {
3718
- const n = Math.max(0, Math.min(1, i?.brightness ?? 0.5));
3719
- return {
3720
- stateId: t,
3721
- type: e,
3722
- onEnter(r) {
3723
- const s = r.getGraphics(), o = new mi();
3724
- o.brightness(n, !1), Vt.set(r, o), Rt(r), s.filters = [...s.filters ?? [], o];
3724
+ onUpdate(a, c) {
3725
+ r && (o += s * c, a.offset = o);
3725
3726
  },
3726
- onLeave(r) {
3727
- const s = Vt.get(r);
3728
- if (s) {
3729
- const o = r.getGraphics();
3730
- o.filters = (o.filters ?? []).filter((a) => a !== s), Dt(r), Vt.delete(r);
3731
- }
3727
+ setOptions(a, c) {
3728
+ const l = a;
3729
+ c.lineWidth !== void 0 && (l.lineWidth = c.lineWidth), c.intensity !== void 0 && (l.intensity = c.intensity), c.animated !== void 0 && (r = c.animated), c.speed !== void 0 && (s = c.speed);
3732
3730
  }
3733
3731
  };
3734
3732
  }
3735
- const hc = (
3733
+ const gc = (
3736
3734
  /* glsl */
3737
3735
  `
3738
3736
  in vec2 aPosition;
@@ -3761,12 +3759,9 @@ void main(void)
3761
3759
  vTextureCoord = filterTextureCoord();
3762
3760
  }
3763
3761
  `
3764
- );
3765
- function uc(t) {
3766
- const e = Math.max(8, Math.round(t * 3)), i = Math.max(2, Math.round(t)), n = e * i;
3767
- return (
3768
- /* glsl */
3769
- `
3762
+ ), mc = (
3763
+ /* glsl */
3764
+ `
3770
3765
  precision highp float;
3771
3766
 
3772
3767
  in vec2 vTextureCoord;
@@ -3774,150 +3769,110 @@ out vec4 finalColor;
3774
3769
 
3775
3770
  uniform sampler2D uTexture;
3776
3771
  uniform vec4 uInputSize;
3777
- uniform float uDistance;
3778
- uniform float uOuterStrength;
3779
- uniform vec3 uColor;
3772
+ uniform vec4 uOutputFrame;
3773
+ uniform vec2 uCenter;
3774
+ uniform float uIntensity;
3775
+ uniform float uDecay;
3776
+ uniform float uDensity;
3777
+ uniform float uWeight;
3780
3778
 
3781
- const int ANGLE_STEPS = ${e};
3782
- const int DIST_STEPS = ${i};
3783
- const float TOTAL = ${n}.0;
3784
- const float PI2 = 6.28318530718;
3779
+ const int NUM_SAMPLES = 30;
3785
3780
 
3786
3781
  void main(void)
3787
3782
  {
3788
- vec4 original = texture(uTexture, vTextureCoord);
3789
-
3790
- /* ---- 环形采样:累计周围像素的 alpha ---- */
3791
- float totalAlpha = 0.0;
3783
+ vec2 texCoord = vTextureCoord;
3784
+ // 将归一化 0~1 的光源中心映射到实际纹理坐标空间
3785
+ vec2 center = uCenter * (uOutputFrame.zw * uInputSize.zw);
3786
+ vec2 deltaTexCoord = (texCoord - center) * (1.0 / float(NUM_SAMPLES)) * uDensity;
3792
3787
 
3793
- for (int i = 0; i < ANGLE_STEPS; i++) {
3794
- float angle = float(i) * PI2 / float(ANGLE_STEPS);
3795
- vec2 dir = vec2(cos(angle), sin(angle));
3788
+ vec4 color = texture(uTexture, texCoord);
3789
+ float illuminationDecay = 1.0;
3796
3790
 
3797
- for (int j = 1; j <= DIST_STEPS; j++) {
3798
- float pct = float(j) / float(DIST_STEPS);
3799
- vec2 offset = dir * pct * uDistance * uInputSize.zw;
3800
- totalAlpha += texture(uTexture, vTextureCoord + offset).a;
3801
- }
3791
+ for (int i = 0; i < NUM_SAMPLES; i++) {
3792
+ texCoord -= deltaTexCoord;
3793
+ vec4 samp = texture(uTexture, texCoord);
3794
+ samp *= illuminationDecay * uWeight;
3795
+ color += samp;
3796
+ illuminationDecay *= uDecay;
3802
3797
  }
3803
3798
 
3804
- totalAlpha /= TOTAL;
3805
-
3806
- /* ---- 外发光:仅在原始像素透明处显示 ---- */
3807
- float glowAlpha = clamp(totalAlpha * uOuterStrength * (1.0 - original.a), 0.0, 1.0);
3808
-
3809
- /* ---- premultiplied-alpha 合成:original 在上,glow 在下 ---- */
3810
- vec4 glow = vec4(uColor * glowAlpha, glowAlpha);
3811
- finalColor = original + glow * (1.0 - original.a);
3799
+ vec4 original = texture(uTexture, vTextureCoord);
3800
+ finalColor = mix(original, color * uIntensity / float(NUM_SAMPLES), uIntensity);
3812
3801
  }
3813
3802
  `
3814
- );
3815
- }
3816
- class fc extends G {
3803
+ );
3804
+ class vc extends G {
3817
3805
  constructor(e = {}) {
3818
- const i = e.quality ?? 4, n = e.distance ?? 10, r = e.color ?? [1, 1, 0], s = e.outerStrength ?? 4, o = U.from({
3819
- vertex: hc,
3820
- fragment: uc(i),
3821
- name: "glow-filter"
3806
+ const i = e.centerX ?? 0.5, n = e.centerY ?? 0.5, r = e.intensity ?? 0.3, s = e.decay ?? 0.96, o = e.density ?? 1, a = e.weight ?? 0.5, c = U.from({
3807
+ vertex: gc,
3808
+ fragment: mc,
3809
+ name: "god-rays-filter"
3822
3810
  });
3823
3811
  super({
3824
- glProgram: o,
3812
+ glProgram: c,
3825
3813
  resources: {
3826
- glowUniforms: new H({
3827
- uDistance: { value: n, type: "f32" },
3828
- uOuterStrength: { value: s, type: "f32" },
3829
- uColor: {
3830
- value: new Float32Array(r),
3831
- type: "vec3<f32>"
3832
- }
3814
+ godRaysUniforms: new H({
3815
+ uCenter: { value: new Float32Array([i, n]), type: "vec2<f32>" },
3816
+ uIntensity: { value: r, type: "f32" },
3817
+ uDecay: { value: s, type: "f32" },
3818
+ uDensity: { value: o, type: "f32" },
3819
+ uWeight: { value: a, type: "f32" }
3833
3820
  })
3834
- },
3835
- padding: n + 5
3821
+ }
3836
3822
  });
3837
3823
  }
3838
3824
  /* ---------- 便捷存取器 ---------- */
3839
- get distance() {
3840
- return this.resources.glowUniforms.uniforms.uDistance;
3825
+ get centerX() {
3826
+ return this.resources.godRaysUniforms.uniforms.uCenter[0];
3841
3827
  }
3842
- set distance(e) {
3843
- this.resources.glowUniforms.uniforms.uDistance = e, this.padding = e + 5;
3828
+ set centerX(e) {
3829
+ this.resources.godRaysUniforms.uniforms.uCenter[0] = e;
3844
3830
  }
3845
- get outerStrength() {
3846
- return this.resources.glowUniforms.uniforms.uOuterStrength;
3831
+ get centerY() {
3832
+ return this.resources.godRaysUniforms.uniforms.uCenter[1];
3847
3833
  }
3848
- set outerStrength(e) {
3849
- this.resources.glowUniforms.uniforms.uOuterStrength = e;
3834
+ set centerY(e) {
3835
+ this.resources.godRaysUniforms.uniforms.uCenter[1] = e;
3850
3836
  }
3851
- get color() {
3852
- return this.resources.glowUniforms.uniforms.uColor;
3837
+ get intensity() {
3838
+ return this.resources.godRaysUniforms.uniforms.uIntensity;
3853
3839
  }
3854
- set color(e) {
3855
- const i = this.resources.glowUniforms.uniforms.uColor;
3856
- i[0] = e[0], i[1] = e[1], i[2] = e[2];
3840
+ set intensity(e) {
3841
+ this.resources.godRaysUniforms.uniforms.uIntensity = e;
3842
+ }
3843
+ get decay() {
3844
+ return this.resources.godRaysUniforms.uniforms.uDecay;
3845
+ }
3846
+ set decay(e) {
3847
+ this.resources.godRaysUniforms.uniforms.uDecay = e;
3848
+ }
3849
+ get density() {
3850
+ return this.resources.godRaysUniforms.uniforms.uDensity;
3851
+ }
3852
+ set density(e) {
3853
+ this.resources.godRaysUniforms.uniforms.uDensity = e;
3854
+ }
3855
+ get weight() {
3856
+ return this.resources.godRaysUniforms.uniforms.uWeight;
3857
+ }
3858
+ set weight(e) {
3859
+ this.resources.godRaysUniforms.uniforms.uWeight = e;
3857
3860
  }
3858
3861
  }
3859
- const at = /* @__PURE__ */ new WeakMap();
3860
- function dc(t) {
3861
- return [
3862
- (t >> 16 & 255) / 255,
3863
- (t >> 8 & 255) / 255,
3864
- (t & 255) / 255
3865
- ];
3866
- }
3867
- function Tf(t, e, i) {
3868
- const n = i?.color ?? 16776960, r = i?.distance ?? 10, [s, o] = i?.strength ?? [2, 6], a = i?.frequency ?? 1.5, c = i?.quality ?? 4, l = dc(n);
3869
- return {
3870
- stateId: t,
3871
- type: e,
3872
- onEnter(h) {
3873
- const u = h.getGraphics(), f = new fc({
3874
- color: l,
3875
- distance: r,
3876
- outerStrength: s,
3877
- quality: c
3878
- });
3879
- at.set(h, { elapsed: 0, filter: f }), Rt(h), u.filters = [...u.filters ?? [], f];
3880
- },
3881
- onLeave(h) {
3882
- const u = at.get(h);
3883
- if (u) {
3884
- const f = h.getGraphics();
3885
- f.filters = (f.filters ?? []).filter((d) => d !== u.filter), Dt(h), at.delete(h);
3886
- }
3887
- },
3888
- onUpdate(h, u) {
3889
- const f = at.get(h);
3890
- if (!f) return;
3891
- f.elapsed += u;
3892
- const d = (Math.sin(f.elapsed * a * Math.PI * 2 / 1e3) + 1) / 2;
3893
- f.filter.outerStrength = s + d * (o - s);
3894
- }
3895
- };
3896
- }
3897
- const ct = /* @__PURE__ */ new WeakMap();
3898
- function Of(t, e, i) {
3899
- const n = i?.frequency ?? 1.2, r = i?.minScale ?? 0.92, s = i?.maxScale ?? 1.08;
3862
+ function Ef(t, e) {
3863
+ const i = e?.centerX ?? 0.5, n = e?.centerY ?? 0.5, r = e?.intensity ?? 0.3, s = e?.decay ?? 0.96, o = e?.density ?? 1, a = e?.weight ?? 0.5;
3900
3864
  return {
3901
- stateId: t,
3902
- type: e,
3903
- onEnter(o) {
3904
- const a = o.getGraphics();
3905
- ct.set(o, { elapsed: 0, origScaleX: a.scale.x, origScaleY: a.scale.y });
3906
- },
3907
- onLeave(o) {
3908
- const a = ct.get(o);
3909
- a && (o.getGraphics().scale.set(a.origScaleX, a.origScaleY), ct.delete(o));
3865
+ postProcessId: t,
3866
+ createFilter() {
3867
+ return new vc({ centerX: i, centerY: n, intensity: r, decay: s, density: o, weight: a });
3910
3868
  },
3911
- onUpdate(o, a) {
3912
- const c = ct.get(o);
3913
- if (!c) return;
3914
- c.elapsed += a;
3915
- const l = c.elapsed * n / 1e3 % 1, h = Math.pow(Math.abs(Math.sin(l * Math.PI)), 0.6), u = r + h * (s - r);
3916
- o.getGraphics().scale.set(c.origScaleX * u, c.origScaleY * u);
3869
+ setOptions(c, l) {
3870
+ const h = c;
3871
+ l.centerX !== void 0 && (h.centerX = l.centerX), l.centerY !== void 0 && (h.centerY = l.centerY), l.intensity !== void 0 && (h.intensity = l.intensity), l.decay !== void 0 && (h.decay = l.decay), l.density !== void 0 && (h.density = l.density), l.weight !== void 0 && (h.weight = l.weight);
3917
3872
  }
3918
3873
  };
3919
3874
  }
3920
- const pc = (
3875
+ const xc = (
3921
3876
  /* glsl */
3922
3877
  `
3923
3878
  in vec2 aPosition;
@@ -3946,7 +3901,7 @@ void main(void)
3946
3901
  vTextureCoord = filterTextureCoord();
3947
3902
  }
3948
3903
  `
3949
- ), gc = (
3904
+ ), yc = (
3950
3905
  /* glsl */
3951
3906
  `
3952
3907
  precision highp float;
@@ -3955,224 +3910,76 @@ in vec2 vTextureCoord;
3955
3910
  out vec4 finalColor;
3956
3911
 
3957
3912
  uniform sampler2D uTexture;
3958
- uniform float uPhase; // 光带中心位置(投影轴坐标)
3959
- uniform float uWidth; // 光带宽度(投影轴空间)
3960
- uniform float uIntensity; // 白光峰值强度 0-1
3961
- uniform float uCosA; // cos(angle)
3962
- uniform float uSinA; // sin(angle)
3913
+ uniform float uAmount;
3963
3914
 
3964
3915
  void main(void)
3965
3916
  {
3966
3917
  vec4 original = texture(uTexture, vTextureCoord);
3967
3918
 
3968
- // 将纹理坐标投影到扫光方向上
3969
- float pos = vTextureCoord.x * uCosA + vTextureCoord.y * uSinA;
3970
-
3971
- // 计算到光带中心的距离,smooth 衰减
3972
- float dist = abs(pos - uPhase);
3973
- float halfW = uWidth * 0.5;
3974
- float t = 1.0 - smoothstep(0.0, halfW, dist);
3919
+ // Rec.709 亮度系数
3920
+ float lum = dot(original.rgb, vec3(0.2126, 0.7152, 0.0722));
3921
+ vec3 gray = vec3(lum);
3975
3922
 
3976
- // 仅在不透明区域叠加白光(premultiplied-alpha)
3977
- float add = t * uIntensity * original.a;
3978
- vec3 newRgb = min(original.rgb + vec3(add), vec3(original.a));
3923
+ // 按 amount 在原色和灰色之间插值
3924
+ vec3 result = mix(original.rgb, gray, uAmount);
3979
3925
 
3980
- finalColor = vec4(newRgb, original.a);
3926
+ finalColor = vec4(result, original.a);
3981
3927
  }
3982
3928
  `
3983
3929
  );
3984
- class mc extends G {
3985
- _angle;
3930
+ class _c extends G {
3986
3931
  constructor(e = {}) {
3987
- const i = e.width ?? 0.35, n = e.intensity ?? 0.6, r = e.angle ?? Math.PI / 6, s = U.from({
3988
- vertex: pc,
3989
- fragment: gc,
3990
- name: "shimmer-filter"
3932
+ const i = e.amount ?? 1, n = U.from({
3933
+ vertex: xc,
3934
+ fragment: yc,
3935
+ name: "grayscale-filter"
3991
3936
  });
3992
3937
  super({
3993
- glProgram: s,
3938
+ glProgram: n,
3994
3939
  resources: {
3995
- shimmerUniforms: new H({
3996
- uPhase: { value: 0, type: "f32" },
3997
- uWidth: { value: i, type: "f32" },
3998
- uIntensity: { value: n, type: "f32" },
3999
- uCosA: { value: Math.cos(r), type: "f32" },
4000
- uSinA: { value: Math.sin(r), type: "f32" }
3940
+ grayscaleUniforms: new H({
3941
+ uAmount: { value: i, type: "f32" }
4001
3942
  })
4002
3943
  }
4003
- }), this._angle = r;
3944
+ });
4004
3945
  }
4005
3946
  /* ---------- 便捷存取器 ---------- */
4006
- get phase() {
4007
- return this.resources.shimmerUniforms.uniforms.uPhase;
4008
- }
4009
- set phase(e) {
4010
- this.resources.shimmerUniforms.uniforms.uPhase = e;
4011
- }
4012
- get width() {
4013
- return this.resources.shimmerUniforms.uniforms.uWidth;
4014
- }
4015
- set width(e) {
4016
- this.resources.shimmerUniforms.uniforms.uWidth = e;
4017
- }
4018
- get intensity() {
4019
- return this.resources.shimmerUniforms.uniforms.uIntensity;
4020
- }
4021
- set intensity(e) {
4022
- this.resources.shimmerUniforms.uniforms.uIntensity = e;
4023
- }
4024
- get angle() {
4025
- return this._angle;
3947
+ get amount() {
3948
+ return this.resources.grayscaleUniforms.uniforms.uAmount;
4026
3949
  }
4027
- set angle(e) {
4028
- this._angle = e, this.resources.shimmerUniforms.uniforms.uCosA = Math.cos(e), this.resources.shimmerUniforms.uniforms.uSinA = Math.sin(e);
3950
+ set amount(e) {
3951
+ this.resources.grayscaleUniforms.uniforms.uAmount = e;
4029
3952
  }
4030
3953
  }
4031
- const lt = /* @__PURE__ */ new WeakMap();
4032
- function Pf(t, e, i) {
4033
- const n = i?.frequency ?? 0.8, r = i?.width ?? 0.35, s = i?.intensity ?? 0.6, o = i?.angle ?? Math.PI / 6;
3954
+ function Lf(t, e) {
3955
+ const i = e?.amount ?? 1;
4034
3956
  return {
4035
- stateId: t,
4036
- type: e,
4037
- onEnter(a) {
4038
- const c = new mc({ width: r, intensity: s, angle: o }), l = a.getGraphics();
4039
- Rt(a), l.filters = [...l.filters ?? [], c];
4040
- const h = Math.abs(Math.cos(o)) + Math.abs(Math.sin(o));
4041
- lt.set(a, { elapsed: 0, filter: c, maxProj: h });
4042
- },
4043
- onLeave(a) {
4044
- const c = lt.get(a);
4045
- if (c) {
4046
- const l = a.getGraphics();
4047
- l.filters = (l.filters ?? []).filter((h) => h !== c.filter), Dt(a), lt.delete(a);
4048
- }
3957
+ postProcessId: t,
3958
+ createFilter() {
3959
+ return new _c({ amount: i });
4049
3960
  },
4050
- onUpdate(a, c) {
4051
- const l = lt.get(a);
4052
- if (!l) return;
4053
- l.elapsed += c;
4054
- const h = l.elapsed * n / 1e3 % 1, u = r * 0.5, f = l.maxProj + r;
4055
- l.filter.phase = h * f - u;
3961
+ setOptions(n, r) {
3962
+ const s = n;
3963
+ r.amount !== void 0 && (s.amount = r.amount);
4056
3964
  }
4057
3965
  };
4058
3966
  }
4059
- const Xt = /* @__PURE__ */ new WeakMap();
4060
- function Mf(t, e, i) {
4061
- const n = i?.color ?? 16711680;
4062
- return {
4063
- stateId: t,
4064
- type: e,
4065
- onEnter(r) {
4066
- const s = r.getGraphics();
4067
- Xt.set(r, s.tint), s.tint = n;
4068
- },
4069
- onLeave(r) {
4070
- const s = Xt.get(r);
4071
- s !== void 0 && (r.getGraphics().tint = s, Xt.delete(r));
4072
- }
4073
- };
4074
- }
4075
- class vc extends le {
4076
- states = /* @__PURE__ */ new Map();
4077
- /** stateId → 该状态下所有的插件定义 */
4078
- plugins = /* @__PURE__ */ new Map();
4079
- /** 添加或批量添加状态 */
4080
- add(e) {
4081
- for (const i of Array.isArray(e) ? e : [e]) {
4082
- const n = this.states.has(i.id);
4083
- this.states.set(i.id, i), this.emit(n ? "state:updated" : "state:added", i);
4084
- }
4085
- return this;
4086
- }
4087
- /** 删除状态(同时清除关联的插件) */
4088
- remove(e) {
4089
- return this.states.delete(e) ? (this.plugins.delete(e), this.emit("state:removed", { id: e }), !0) : !1;
4090
- }
4091
- get(e) {
4092
- return this.states.get(e);
4093
- }
4094
- has(e) {
4095
- return this.states.has(e);
4096
- }
4097
- getAll() {
4098
- return [...this.states.values()];
4099
- }
4100
- clear() {
4101
- this.states.clear(), this.plugins.clear(), this.emit("states:cleared");
4102
- }
4103
- /** 注册状态插件 */
4104
- registerPlugin(e) {
4105
- const i = this.plugins.get(e.stateId) ?? [];
4106
- return i.push(e), this.plugins.set(e.stateId, i), this;
4107
- }
4108
- /** 注销指定 stateId + type 的所有插件 */
4109
- unregisterPlugin(e, i) {
4110
- const n = this.plugins.get(e);
4111
- if (n) {
4112
- const r = n.filter((s) => s.type !== i);
4113
- r.length ? this.plugins.set(e, r) : this.plugins.delete(e);
4114
- }
4115
- return this;
4116
- }
4117
- /** 获取匹配 stateId + type 的插件列表 */
4118
- getPlugins(e, i) {
4119
- return (this.plugins.get(e) ?? []).filter((n) => n.type === i);
4120
- }
4121
- /** 获取状态权重,未定义时为 0 */
4122
- getWeight(e) {
4123
- return this.states.get(e)?.weight ?? 0;
4124
- }
4125
- /** 按权重升序排列 stateId 列表 */
4126
- sortByWeight(e) {
4127
- return [...e].sort((i, n) => this.getWeight(i) - this.getWeight(n));
4128
- }
4129
- /** 序列化(仅元数据,插件为运行时代码不参与序列化) */
4130
- toJSON() {
4131
- return this.getAll();
4132
- }
4133
- /** 反序列化(追加模式) */
4134
- fromJSON(e) {
4135
- this.add(e);
4136
- }
4137
- toYAML() {
4138
- return be(this.toJSON());
4139
- }
4140
- fromYAML(e) {
4141
- this.fromJSON(Ce(e));
4142
- }
4143
- destroy() {
4144
- this.clear(), this.removeAllListeners();
4145
- }
4146
- }
4147
- function Ef(t, e) {
4148
- const i = e?.strength ?? 4, n = e?.quality ?? 4;
4149
- return {
4150
- postProcessId: t,
4151
- createFilter() {
4152
- return new Jr({ strength: i, quality: n });
4153
- },
4154
- setOptions(r, s) {
4155
- const o = r;
4156
- s.strength !== void 0 && (o.strength = s.strength), s.quality !== void 0 && (o.quality = s.quality);
4157
- }
4158
- };
4159
- }
4160
- const xc = (
4161
- /* glsl */
4162
- `
4163
- in vec2 aPosition;
4164
- out vec2 vTextureCoord;
4165
-
4166
- uniform vec4 uInputSize;
4167
- uniform vec4 uOutputFrame;
4168
- uniform vec4 uOutputTexture;
4169
-
4170
- vec4 filterVertexPosition(void)
4171
- {
4172
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4173
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4174
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4175
- return vec4(position, 0.0, 1.0);
3967
+ const wc = (
3968
+ /* glsl */
3969
+ `
3970
+ in vec2 aPosition;
3971
+ out vec2 vTextureCoord;
3972
+
3973
+ uniform vec4 uInputSize;
3974
+ uniform vec4 uOutputFrame;
3975
+ uniform vec4 uOutputTexture;
3976
+
3977
+ vec4 filterVertexPosition(void)
3978
+ {
3979
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
3980
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
3981
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
3982
+ return vec4(position, 0.0, 1.0);
4176
3983
  }
4177
3984
 
4178
3985
  vec2 filterTextureCoord(void)
@@ -4186,11 +3993,9 @@ void main(void)
4186
3993
  vTextureCoord = filterTextureCoord();
4187
3994
  }
4188
3995
  `
4189
- );
4190
- function yc(t) {
4191
- return (
4192
- /* glsl */
4193
- `
3996
+ ), Sc = (
3997
+ /* glsl */
3998
+ `
4194
3999
  precision highp float;
4195
4000
 
4196
4001
  in vec2 vTextureCoord;
@@ -4198,123 +4003,68 @@ out vec4 finalColor;
4198
4003
 
4199
4004
  uniform sampler2D uTexture;
4200
4005
  uniform vec4 uInputSize;
4201
- uniform float uThreshold;
4202
- uniform float uIntensity;
4203
- uniform float uRadius;
4204
-
4205
- const int SAMPLES = ${t};
4206
- const float FSAMPLES = ${t}.0;
4006
+ uniform float uStrength;
4207
4007
 
4208
4008
  void main(void)
4209
4009
  {
4210
- vec4 original = texture(uTexture, vTextureCoord);
4211
-
4212
- /* ---- 提取高亮区域并模糊 ---- */
4213
- vec3 bloom = vec3(0.0);
4214
- float totalWeight = 0.0;
4215
-
4216
- for (int i = -SAMPLES; i <= SAMPLES; i++) {
4217
- for (int j = -SAMPLES; j <= SAMPLES; j++) {
4218
- vec2 offset = vec2(float(i), float(j)) * uRadius * uInputSize.zw;
4219
- vec4 samp = texture(uTexture, vTextureCoord + offset);
4220
-
4221
- // 亮度提取(Luminance)
4222
- float lum = dot(samp.rgb, vec3(0.2126, 0.7152, 0.0722));
4223
- float bright = max(0.0, lum - uThreshold);
4224
-
4225
- // 高斯权重近似
4226
- float dist = length(vec2(float(i), float(j)));
4227
- float weight = exp(-dist * dist / (2.0 * FSAMPLES));
4010
+ vec2 step = uInputSize.zw;
4228
4011
 
4229
- bloom += samp.rgb * bright * weight;
4230
- totalWeight += weight;
4231
- }
4232
- }
4012
+ // 采样 3×3 邻域
4013
+ vec4 center = texture(uTexture, vTextureCoord);
4014
+ vec4 top = texture(uTexture, vTextureCoord + vec2( 0.0, -step.y));
4015
+ vec4 bottom = texture(uTexture, vTextureCoord + vec2( 0.0, step.y));
4016
+ vec4 left = texture(uTexture, vTextureCoord + vec2(-step.x, 0.0));
4017
+ vec4 right = texture(uTexture, vTextureCoord + vec2( step.x, 0.0));
4233
4018
 
4234
- bloom /= totalWeight;
4019
+ // 拉普拉斯卷积核: [-1, -1, -1; -1, 8, -1; -1, -1, -1] 的简化版
4020
+ // 仅取上下左右 4 邻域: [0,-1,0; -1,4,-1; 0,-1,0]
4021
+ vec4 edge = center * 4.0 - top - bottom - left - right;
4235
4022
 
4236
- /* ---- 叠加 ---- */
4237
- vec3 result = original.rgb + bloom * uIntensity;
4023
+ // 按强度叠加锐化
4024
+ vec4 result = center + edge * uStrength;
4238
4025
 
4239
- finalColor = vec4(result, original.a);
4026
+ finalColor = vec4(clamp(result.rgb, 0.0, 1.0), center.a);
4240
4027
  }
4241
4028
  `
4242
- );
4243
- }
4244
- class _c extends G {
4029
+ );
4030
+ class bc extends G {
4245
4031
  constructor(e = {}) {
4246
- const i = e.threshold ?? 0.5, n = e.intensity ?? 1, r = e.radius ?? 2, s = e.samples ?? 3, o = U.from({
4247
- vertex: xc,
4248
- fragment: yc(s),
4249
- name: "bloom-filter"
4032
+ const i = e.strength ?? 0.5, n = U.from({
4033
+ vertex: wc,
4034
+ fragment: Sc,
4035
+ name: "sharpen-filter"
4250
4036
  });
4251
4037
  super({
4252
- glProgram: o,
4038
+ glProgram: n,
4253
4039
  resources: {
4254
- bloomUniforms: new H({
4255
- uThreshold: { value: i, type: "f32" },
4256
- uIntensity: { value: n, type: "f32" },
4257
- uRadius: { value: r, type: "f32" }
4040
+ sharpenUniforms: new H({
4041
+ uStrength: { value: i, type: "f32" }
4258
4042
  })
4259
4043
  }
4260
4044
  });
4261
4045
  }
4262
4046
  /* ---------- 便捷存取器 ---------- */
4263
- get threshold() {
4264
- return this.resources.bloomUniforms.uniforms.uThreshold;
4265
- }
4266
- set threshold(e) {
4267
- this.resources.bloomUniforms.uniforms.uThreshold = e;
4268
- }
4269
- get intensity() {
4270
- return this.resources.bloomUniforms.uniforms.uIntensity;
4271
- }
4272
- set intensity(e) {
4273
- this.resources.bloomUniforms.uniforms.uIntensity = e;
4274
- }
4275
- get radius() {
4276
- return this.resources.bloomUniforms.uniforms.uRadius;
4047
+ get strength() {
4048
+ return this.resources.sharpenUniforms.uniforms.uStrength;
4277
4049
  }
4278
- set radius(e) {
4279
- this.resources.bloomUniforms.uniforms.uRadius = e;
4050
+ set strength(e) {
4051
+ this.resources.sharpenUniforms.uniforms.uStrength = e;
4280
4052
  }
4281
4053
  }
4282
- function Lf(t, e) {
4283
- const i = e?.threshold ?? 0.5, n = e?.intensity ?? 1, r = e?.radius ?? 2, s = e?.samples ?? 3;
4284
- return {
4285
- postProcessId: t,
4286
- createFilter() {
4287
- return new _c({ threshold: i, intensity: n, radius: r, samples: s });
4288
- },
4289
- setOptions(o, a) {
4290
- const c = o;
4291
- a.threshold !== void 0 && (c.threshold = a.threshold), a.intensity !== void 0 && (c.intensity = a.intensity), a.radius !== void 0 && (c.radius = a.radius);
4292
- }
4293
- };
4294
- }
4295
- function Ki(t, e) {
4296
- const i = e.brightness ?? 1, n = e.contrast ?? 0, r = e.saturate ?? 0, s = e.hue ?? 0;
4297
- t.reset(), i !== 1 && t.brightness(i, !0), n !== 0 && t.contrast(n, !0), r !== 0 && t.saturate(r, !0), s !== 0 && t.hue(s, !0);
4298
- }
4299
4054
  function kf(t, e) {
4300
- const i = {
4301
- brightness: e?.brightness ?? 1,
4302
- contrast: e?.contrast ?? 0,
4303
- saturate: e?.saturate ?? 0,
4304
- hue: e?.hue ?? 0
4305
- };
4055
+ const i = e?.strength ?? 0.5;
4306
4056
  return {
4307
4057
  postProcessId: t,
4308
4058
  createFilter() {
4309
- const n = new mi();
4310
- return Ki(n, i), n;
4059
+ return new bc({ strength: i });
4311
4060
  },
4312
4061
  setOptions(n, r) {
4313
- Object.assign(i, r), Ki(n, i);
4062
+ const s = n;
4063
+ r.strength !== void 0 && (s.strength = r.strength);
4314
4064
  }
4315
4065
  };
4316
4066
  }
4317
- const wc = (
4067
+ const Cc = (
4318
4068
  /* glsl */
4319
4069
  `
4320
4070
  in vec2 aPosition;
@@ -4343,7 +4093,7 @@ void main(void)
4343
4093
  vTextureCoord = filterTextureCoord();
4344
4094
  }
4345
4095
  `
4346
- ), Sc = (
4096
+ ), Ic = (
4347
4097
  /* glsl */
4348
4098
  `
4349
4099
  precision highp float;
@@ -4352,736 +4102,928 @@ in vec2 vTextureCoord;
4352
4102
  out vec4 finalColor;
4353
4103
 
4354
4104
  uniform sampler2D uTexture;
4355
- uniform vec4 uInputSize;
4356
- uniform vec4 uOutputFrame;
4357
- uniform float uRadius;
4358
- uniform float uSoftness;
4359
- uniform float uIntensity;
4360
- uniform vec3 uColor;
4105
+ uniform float uAmount;
4361
4106
 
4362
4107
  void main(void)
4363
4108
  {
4364
4109
  vec4 original = texture(uTexture, vTextureCoord);
4365
4110
 
4366
- // 将归一化 0.5 中心映射到实际纹理坐标空间
4367
- vec2 texCenter = vec2(0.5) * (uOutputFrame.zw * uInputSize.zw);
4368
- // 从中心到边缘的距离(归一化回 0~1 空间)
4369
- vec2 center = (vTextureCoord - texCenter) / (uOutputFrame.zw * uInputSize.zw);
4370
- float dist = length(center) * 2.0;
4111
+ // 反色
4112
+ vec3 inverted = 1.0 - original.rgb;
4371
4113
 
4372
- // smoothstep radius 和 radius+softness 之间平滑过渡
4373
- float vignetteFactor = smoothstep(uRadius, uRadius + uSoftness, dist) * uIntensity;
4114
+ // amount 在原色和反色之间插值
4115
+ vec3 result = mix(original.rgb, inverted, uAmount);
4374
4116
 
4375
- // 将边缘像素向暗角颜色混合
4376
- finalColor = vec4(mix(original.rgb, uColor, vignetteFactor), original.a);
4117
+ finalColor = vec4(result, original.a);
4377
4118
  }
4378
4119
  `
4379
4120
  );
4380
- class bc extends G {
4121
+ class Ac extends G {
4381
4122
  constructor(e = {}) {
4382
- const i = e.radius ?? 0.3, n = e.softness ?? 0.5, r = e.intensity ?? 0.8, s = e.color ?? [0, 0, 0], o = U.from({
4383
- vertex: wc,
4384
- fragment: Sc,
4385
- name: "vignette-filter"
4123
+ const i = e.amount ?? 1, n = U.from({
4124
+ vertex: Cc,
4125
+ fragment: Ic,
4126
+ name: "invert-filter"
4386
4127
  });
4387
4128
  super({
4388
- glProgram: o,
4129
+ glProgram: n,
4389
4130
  resources: {
4390
- vignetteUniforms: new H({
4391
- uRadius: { value: i, type: "f32" },
4392
- uSoftness: { value: n, type: "f32" },
4393
- uIntensity: { value: r, type: "f32" },
4394
- uColor: { value: new Float32Array(s), type: "vec3<f32>" }
4131
+ invertUniforms: new H({
4132
+ uAmount: { value: i, type: "f32" }
4395
4133
  })
4396
4134
  }
4397
4135
  });
4398
4136
  }
4399
4137
  /* ---------- 便捷存取器 ---------- */
4400
- get radius() {
4401
- return this.resources.vignetteUniforms.uniforms.uRadius;
4402
- }
4403
- set radius(e) {
4404
- this.resources.vignetteUniforms.uniforms.uRadius = e;
4405
- }
4406
- get softness() {
4407
- return this.resources.vignetteUniforms.uniforms.uSoftness;
4408
- }
4409
- set softness(e) {
4410
- this.resources.vignetteUniforms.uniforms.uSoftness = e;
4411
- }
4412
- get intensity() {
4413
- return this.resources.vignetteUniforms.uniforms.uIntensity;
4414
- }
4415
- set intensity(e) {
4416
- this.resources.vignetteUniforms.uniforms.uIntensity = e;
4417
- }
4418
- get color() {
4419
- const e = this.resources.vignetteUniforms.uniforms.uColor;
4420
- return [e[0], e[1], e[2]];
4138
+ get amount() {
4139
+ return this.resources.invertUniforms.uniforms.uAmount;
4421
4140
  }
4422
- set color(e) {
4423
- const i = this.resources.vignetteUniforms.uniforms.uColor;
4424
- i[0] = e[0], i[1] = e[1], i[2] = e[2];
4141
+ set amount(e) {
4142
+ this.resources.invertUniforms.uniforms.uAmount = e;
4425
4143
  }
4426
4144
  }
4427
4145
  function Ff(t, e) {
4428
- const i = e?.radius ?? 0.3, n = e?.softness ?? 0.5, r = e?.intensity ?? 0.8, s = e?.color ?? [0, 0, 0];
4146
+ const i = e?.amount ?? 1;
4429
4147
  return {
4430
4148
  postProcessId: t,
4431
4149
  createFilter() {
4432
- return new bc({ radius: i, softness: n, intensity: r, color: s });
4150
+ return new Ac({ amount: i });
4433
4151
  },
4434
- setOptions(o, a) {
4435
- const c = o;
4436
- a.radius !== void 0 && (c.radius = a.radius), a.softness !== void 0 && (c.softness = a.softness), a.intensity !== void 0 && (c.intensity = a.intensity), a.color !== void 0 && (c.color = a.color);
4152
+ setOptions(n, r) {
4153
+ const s = n;
4154
+ r.amount !== void 0 && (s.amount = r.amount);
4437
4155
  }
4438
4156
  };
4439
4157
  }
4440
- const Cc = (
4441
- /* glsl */
4442
- `
4443
- in vec2 aPosition;
4444
- out vec2 vTextureCoord;
4445
-
4446
- uniform vec4 uInputSize;
4447
- uniform vec4 uOutputFrame;
4448
- uniform vec4 uOutputTexture;
4449
-
4450
- vec4 filterVertexPosition(void)
4451
- {
4452
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4453
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4454
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4455
- return vec4(position, 0.0, 1.0);
4456
- }
4457
-
4458
- vec2 filterTextureCoord(void)
4459
- {
4460
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4461
- }
4462
-
4463
- void main(void)
4464
- {
4465
- gl_Position = filterVertexPosition();
4466
- vTextureCoord = filterTextureCoord();
4467
- }
4468
- `
4469
- ), Ic = (
4470
- /* glsl */
4471
- `
4472
- precision highp float;
4473
-
4474
- in vec2 vTextureCoord;
4475
- out vec4 finalColor;
4476
-
4477
- uniform sampler2D uTexture;
4478
- uniform vec4 uInputSize;
4479
- uniform float uPixelSize;
4480
-
4481
- void main(void)
4482
- {
4483
- // 将纹理坐标对齐到像素块中心
4484
- vec2 pixelCoord = vTextureCoord * uInputSize.xy;
4485
- vec2 snapped = floor(pixelCoord / uPixelSize) * uPixelSize + uPixelSize * 0.5;
4486
- vec2 uv = snapped * uInputSize.zw;
4487
-
4488
- finalColor = texture(uTexture, uv);
4489
- }
4490
- `
4491
- );
4492
- class Ac extends G {
4493
- constructor(e = {}) {
4494
- const i = e.pixelSize ?? 8, n = U.from({
4495
- vertex: Cc,
4496
- fragment: Ic,
4497
- name: "pixelate-filter"
4498
- });
4499
- super({
4500
- glProgram: n,
4501
- resources: {
4502
- pixelateUniforms: new H({
4503
- uPixelSize: { value: i, type: "f32" }
4504
- })
4505
- }
4506
- });
4158
+ class Ti extends le {
4159
+ /** 额外 padding:覆盖 BlurFilter spread + 变换舍入误差(与 states/_filter-utils 同理) */
4160
+ static FILTER_AREA_PAD = 80;
4161
+ items = /* @__PURE__ */ new Map();
4162
+ /** postProcessId → 插件定义 */
4163
+ defs = /* @__PURE__ */ new Map();
4164
+ /** postProcessId → 运行时 Filter 实例 */
4165
+ filters = /* @__PURE__ */ new Map();
4166
+ /** 当前绑定的目标容器 */
4167
+ _target = null;
4168
+ /** PixiJS Application 引用,用于获取 screen 尺寸 */
4169
+ _app = null;
4170
+ /** 绑定到目标容器(projectionContainer),启用后滤镜自动挂载 */
4171
+ bind(e, i) {
4172
+ this._target = e, this._app = i, this.rebuildFilters();
4507
4173
  }
4508
- /* ---------- 便捷存取器 ---------- */
4509
- get pixelSize() {
4510
- return this.resources.pixelateUniforms.uniforms.uPixelSize;
4174
+ /** 解绑目标容器,清除所有已挂载的滤镜 */
4175
+ unbind() {
4176
+ this._target && (this.clearFilters(), this._target.filterArea = void 0, this._target = null, this._app = null);
4511
4177
  }
4512
- set pixelSize(e) {
4513
- this.resources.pixelateUniforms.uniforms.uPixelSize = e;
4178
+ /** 添加或批量添加后期处理项 */
4179
+ add(e) {
4180
+ for (const i of Array.isArray(e) ? e : [e]) {
4181
+ i.enabled === void 0 && (i.enabled = !0);
4182
+ const n = this.items.has(i.id);
4183
+ this.items.set(i.id, i), this.emit(n ? "postprocess:updated" : "postprocess:added", i);
4184
+ }
4185
+ return this.rebuildFilters(), this;
4514
4186
  }
4515
- }
4516
- function Rf(t, e) {
4517
- const i = e?.pixelSize ?? 8;
4518
- return {
4519
- postProcessId: t,
4520
- createFilter() {
4521
- return new Ac({ pixelSize: i });
4522
- },
4523
- setOptions(n, r) {
4524
- const s = n;
4525
- r.pixelSize !== void 0 && (s.pixelSize = r.pixelSize);
4187
+ /** 删除后期处理项(同时清除关联的插件和滤镜) */
4188
+ remove(e) {
4189
+ return this.items.delete(e) ? (this.destroyFilter(e), this.defs.delete(e), this.emit("postprocess:removed", { id: e }), this.rebuildFilters(), !0) : !1;
4190
+ }
4191
+ get(e) {
4192
+ return this.items.get(e);
4193
+ }
4194
+ has(e) {
4195
+ return this.items.has(e);
4196
+ }
4197
+ getAll() {
4198
+ return [...this.items.values()];
4199
+ }
4200
+ clear() {
4201
+ this.clearFilters(), this.items.clear(), this.defs.clear(), this.emit("postprocess:cleared");
4202
+ }
4203
+ /** 启用后期处理效果 */
4204
+ enable(e) {
4205
+ const i = this.items.get(e);
4206
+ return i && !i.enabled && (i.enabled = !0, this.emit("postprocess:enabled", { id: e }), this.rebuildFilters()), this;
4207
+ }
4208
+ /** 禁用后期处理效果 */
4209
+ disable(e) {
4210
+ const i = this.items.get(e);
4211
+ return i && i.enabled !== !1 && (i.enabled = !1, this.emit("postprocess:disabled", { id: e }), this.rebuildFilters()), this;
4212
+ }
4213
+ /** 注册后期处理插件 */
4214
+ register(e) {
4215
+ return this.defs.set(e.postProcessId, e), this.rebuildFilters(), this;
4216
+ }
4217
+ /** 注销后期处理插件 */
4218
+ unregister(e) {
4219
+ return this.destroyFilter(e), this.defs.delete(e), this.rebuildFilters(), this;
4220
+ }
4221
+ /** 获取后期处理权重 */
4222
+ getWeight(e) {
4223
+ return this.items.get(e)?.weight ?? 0;
4224
+ }
4225
+ /** 获取运行时 Filter 实例(供调试用) */
4226
+ getFilter(e) {
4227
+ return this.filters.get(e);
4228
+ }
4229
+ /**
4230
+ * 热更新后期处理参数,直接修改运行时 Filter 的 uniform,无需销毁重建。
4231
+ * 仅当对应 PostProcessDef 实现了 setOptions 时生效。
4232
+ * 不支持热更新的参数(如 bloom 的 samples)需重新调用 register 重建。
4233
+ */
4234
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4235
+ updateOptions(e, i) {
4236
+ const n = this.filters.get(e), r = this.defs.get(e);
4237
+ return n && r?.setOptions && r.setOptions(n, i), this;
4238
+ }
4239
+ /** 逐帧更新(由 Scene ticker 驱动) */
4240
+ update(e) {
4241
+ this._target && this._app && this._target.filters?.length && (this._target.filterArea = this.computeFilterArea());
4242
+ for (const [i, n] of this.defs) {
4243
+ if (!n.onUpdate) continue;
4244
+ const r = this.items.get(i);
4245
+ if (!r || r.enabled === !1) continue;
4246
+ const s = this.filters.get(i);
4247
+ s && n.onUpdate(s, e);
4526
4248
  }
4527
- };
4528
- }
4529
- const Tc = (
4530
- /* glsl */
4531
- `
4532
- in vec2 aPosition;
4533
- out vec2 vTextureCoord;
4534
-
4535
- uniform vec4 uInputSize;
4536
- uniform vec4 uOutputFrame;
4537
- uniform vec4 uOutputTexture;
4538
-
4539
- vec4 filterVertexPosition(void)
4540
- {
4541
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4542
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4543
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4544
- return vec4(position, 0.0, 1.0);
4545
- }
4546
-
4547
- vec2 filterTextureCoord(void)
4548
- {
4549
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4550
- }
4551
-
4552
- void main(void)
4553
- {
4554
- gl_Position = filterVertexPosition();
4555
- vTextureCoord = filterTextureCoord();
4556
- }
4557
- `
4558
- ), Oc = (
4559
- /* glsl */
4560
- `
4561
- precision highp float;
4562
-
4563
- in vec2 vTextureCoord;
4564
- out vec4 finalColor;
4565
-
4566
- uniform sampler2D uTexture;
4567
- uniform float uIntensity;
4568
- uniform float uSeed;
4569
-
4570
- // 伪随机噪声生成
4571
- float random(vec2 co)
4572
- {
4573
- return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
4574
- }
4575
-
4576
- void main(void)
4577
- {
4578
- vec4 original = texture(uTexture, vTextureCoord);
4579
-
4580
- // 用坐标和种子生成噪声
4581
- float noise = random(vTextureCoord + uSeed) * 2.0 - 1.0;
4582
-
4583
- // 叠加噪声到 RGB 通道
4584
- vec3 result = original.rgb + noise * uIntensity;
4585
-
4586
- finalColor = vec4(clamp(result, 0.0, 1.0), original.a);
4249
+ }
4250
+ /** 序列化(仅元数据,插件为运行时代码不参与序列化) */
4251
+ toJSON() {
4252
+ return this.getAll();
4253
+ }
4254
+ /** 反序列化(追加模式) */
4255
+ fromJSON(e) {
4256
+ this.add(e);
4257
+ }
4258
+ toYAML() {
4259
+ return be(this.toJSON());
4260
+ }
4261
+ fromYAML(e) {
4262
+ this.fromJSON(Ce(e));
4263
+ }
4264
+ destroy() {
4265
+ this.clearFilters(), this.items.clear(), this.defs.clear(), this._target = null, this._app = null, this.removeAllListeners();
4266
+ }
4267
+ // ========== 内部方法 ==========
4268
+ /**
4269
+ * 计算 filterArea(projectionContainer 局部坐标空间),参考 states/_filter-utils。
4270
+ *
4271
+ * 将视口四角通过 worldTransform 逆变换到容器局部空间,
4272
+ * 加上 pad 以确保 BlurFilter 等带扩展的滤镜不被裁剪。
4273
+ *
4274
+ * **filterArea 走的是独立且正确的路径:**
4275
+ * ```
4276
+ * filterArea(local)
4277
+ * × container.worldTransform → world
4278
+ * × renderGroup.cacheToLocalTransform → render group local ✓
4279
+ * ```
4280
+ */
4281
+ computeFilterArea() {
4282
+ const e = this._app.screen, i = this._target.worldTransform.clone().invert(), n = i.apply({ x: e.x, y: e.y }), r = i.apply({ x: e.x + e.width, y: e.y }), s = i.apply({ x: e.x + e.width, y: e.y + e.height }), o = i.apply({ x: e.x, y: e.y + e.height }), a = Ti.FILTER_AREA_PAD, c = Math.min(n.x, r.x, s.x, o.x) - a, l = Math.min(n.y, r.y, s.y, o.y) - a, h = Math.max(n.x, r.x, s.x, o.x) + a, u = Math.max(n.y, r.y, s.y, o.y) + a;
4283
+ return new Lt(c, l, h - c, u - l);
4284
+ }
4285
+ /** 按权重升序排列,返回有效(已注册 + 已启用)的 ID 列表 */
4286
+ getActiveIds() {
4287
+ const e = [];
4288
+ for (const [i, n] of this.items)
4289
+ n.enabled !== !1 && (this.defs.has(i) ? e.push(i) : console.warn(
4290
+ `[PostProcess] "${i}" 后处理插件未注册(register),效果将被跳过。请确保在启用前调用 postprocess.register()。`
4291
+ ));
4292
+ return e.sort((i, n) => this.getWeight(i) - this.getWeight(n));
4293
+ }
4294
+ /** 重建 filters 数组并挂载到目标容器 */
4295
+ rebuildFilters() {
4296
+ if (!this._target) return;
4297
+ const e = this.getActiveIds();
4298
+ for (const [n, r] of this.filters)
4299
+ e.includes(n) || this.destroyFilter(n);
4300
+ for (const n of e)
4301
+ if (!this.filters.has(n)) {
4302
+ const r = this.defs.get(n);
4303
+ this.filters.set(n, r.createFilter());
4304
+ }
4305
+ const i = e.map((n) => this.filters.get(n)).filter(Boolean);
4306
+ i.length > 0 ? (this._target.filterArea = this.computeFilterArea(), this._target.filters = i) : (this._target.filterArea = void 0, this._target.filters = null);
4307
+ }
4308
+ /** 销毁单个滤镜实例 */
4309
+ destroyFilter(e) {
4310
+ const i = this.filters.get(e);
4311
+ i && (this.defs.get(e)?.onDestroy?.(i), i.destroy(), this.filters.delete(e));
4312
+ }
4313
+ /** 清除所有运行时滤镜 */
4314
+ clearFilters() {
4315
+ for (const e of [...this.filters.keys()])
4316
+ this.destroyFilter(e);
4317
+ this._target && (this._target.filters = null);
4318
+ }
4587
4319
  }
4588
- `
4589
- );
4590
- class Pc extends G {
4591
- constructor(e = {}) {
4592
- const i = e.intensity ?? 0.15, n = e.seed ?? 0, r = U.from({
4593
- vertex: Tc,
4594
- fragment: Oc,
4595
- name: "noise-filter"
4596
- });
4597
- super({
4598
- glProgram: r,
4599
- resources: {
4600
- noiseUniforms: new H({
4601
- uIntensity: { value: i, type: "f32" },
4602
- uSeed: { value: n, type: "f32" }
4603
- })
4320
+ function Ze(t) {
4321
+ return `#${t.toString(16).padStart(6, "0")}`;
4322
+ }
4323
+ function Tc(t) {
4324
+ return "convertToBlob" in t && typeof t.convertToBlob == "function" ? t.convertToBlob({ type: "image/png" }) : new Promise((e, i) => {
4325
+ t.toBlob(
4326
+ (n) => n ? e(n) : i(new Error("Failed to create PNG blob")),
4327
+ "image/png"
4328
+ );
4329
+ });
4330
+ }
4331
+ function Oc(t) {
4332
+ return typeof t.toDataURL == "function" ? t.toDataURL("image/png") : "";
4333
+ }
4334
+ function Ct(t) {
4335
+ return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4336
+ }
4337
+ function It(t, e) {
4338
+ const i = t instanceof Blob ? URL.createObjectURL(t) : `data:image/svg+xml;charset=utf-8,${encodeURIComponent(t)}`, n = document.createElement("a");
4339
+ n.href = i, n.download = e, document.body.appendChild(n), n.click(), document.body.removeChild(n), t instanceof Blob && URL.revokeObjectURL(i);
4340
+ }
4341
+ async function Pc(t, e) {
4342
+ const { pixelRatio: i = 2 } = e ?? {}, n = t.app, r = n.renderer.resolution, s = n.screen.width, o = n.screen.height;
4343
+ n.renderer.resolution = i, n.renderer.resize(s, o), n.render();
4344
+ const a = Tc(n.canvas);
4345
+ return n.renderer.resolution = r, n.renderer.resize(s, o), n.render(), a;
4346
+ }
4347
+ async function Mc(t, e) {
4348
+ const i = t.app.renderer;
4349
+ try {
4350
+ return await i.extract.base64({ target: e, format: "png" });
4351
+ } catch {
4352
+ try {
4353
+ const n = i.extract.canvas(e);
4354
+ return Oc(n);
4355
+ } catch {
4356
+ return "";
4357
+ }
4358
+ }
4359
+ }
4360
+ async function Ec(t) {
4361
+ const e = /* @__PURE__ */ new Map();
4362
+ for (const i of t.nodes.getAll()) {
4363
+ const n = i.options.icon;
4364
+ if (!e.has(n))
4365
+ try {
4366
+ const r = await t.icons.loadTexture(n), s = await Mc(t, r);
4367
+ e.set(n, s);
4368
+ } catch {
4369
+ e.set(n, "");
4604
4370
  }
4371
+ }
4372
+ return e;
4373
+ }
4374
+ async function Lc(t, e) {
4375
+ const { background: i = !0 } = e ?? {}, n = t.projectionContainer, r = n.skew.x, s = n.skew.y, o = n.scale.x, a = n.position.x, c = n.position.y, l = Math.ceil(t.app.screen.width), h = Math.ceil(t.app.screen.height), u = o * Math.cos(s), f = o * Math.sin(s), d = o * Math.sin(r), p = o * Math.cos(r), m = Math.cos(s), v = Math.sin(s), _ = Math.sin(r), x = Math.cos(r), w = m * x - v * _, S = x / w, b = -v / w, E = -_ / w, N = m / w, j = u * p - f * d, ee = [
4376
+ { x: 0, y: 0 },
4377
+ { x: l, y: 0 },
4378
+ { x: 0, y: h },
4379
+ { x: l, y: h }
4380
+ ];
4381
+ let V = 1 / 0, X = 1 / 0, ue = -1 / 0, fe = -1 / 0;
4382
+ for (const { x: I, y: te } of ee) {
4383
+ const pe = I - a, ge = te - c, me = (p * pe - d * ge) / j, ve = (-f * pe + u * ge) / j;
4384
+ V = Math.min(V, me), X = Math.min(X, ve), ue = Math.max(ue, me), fe = Math.max(fe, ve);
4385
+ }
4386
+ const q = g * 2, de = {
4387
+ x: Math.floor((V - q) / g) * g,
4388
+ y: Math.floor((X - q) / g) * g,
4389
+ width: Math.ceil((ue - V + q * 2) / g) * g,
4390
+ height: Math.ceil((fe - X + q * 2) / g) * g
4391
+ }, $ = await Ec(t), O = [];
4392
+ O.push(
4393
+ '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"',
4394
+ ` width="${l}" height="${h}" viewBox="0 0 ${l} ${h}">`
4395
+ ), O.push(" <defs>");
4396
+ for (const [I, te] of $)
4397
+ te && O.push(
4398
+ ` <symbol id="icon-${Ct(I)}" viewBox="0 0 100 100">`,
4399
+ ` <image href="${te}" width="100" height="100" />`,
4400
+ " </symbol>"
4401
+ );
4402
+ if (O.push(" </defs>"), i) {
4403
+ const I = t.grid.options, te = Ze(I.bgColor ?? 0);
4404
+ O.push(` <rect width="${l}" height="${h}" fill="${te}" />`);
4405
+ }
4406
+ const Gt = `matrix(${u},${f},${d},${p},${a},${c})`;
4407
+ if (O.push(` <g transform="${Gt}">`), i) {
4408
+ const I = await kc(t, de);
4409
+ I && O.push(I);
4410
+ }
4411
+ for (const I of t.rects.getAll())
4412
+ O.push(Fc(I.options));
4413
+ for (const I of t.lines.getAll())
4414
+ O.push(Rc(I));
4415
+ for (const I of t.nodes.getAll())
4416
+ O.push(Dc(I.options, $, S, b, E, N));
4417
+ for (const I of t.texts.getAll())
4418
+ O.push(zc(I.options, S, b, E, N));
4419
+ return O.push(" </g>"), O.push("</svg>"), O.join(`
4420
+ `);
4421
+ }
4422
+ async function kc(t, e) {
4423
+ const n = t.grid.getGraphics().children[0];
4424
+ if (!n) return "";
4425
+ const r = t.grid.options.tileSize ?? 100, s = n.x, o = n.y, a = n.width, c = n.height, l = n.tilePosition?.x ?? 0, h = n.tilePosition?.y ?? 0;
4426
+ try {
4427
+ n.x = 0, n.y = 0, n.width = e.width, n.height = e.height, n.tilePosition && n.tilePosition.set(
4428
+ -e.x - r / 2,
4429
+ -e.y - r / 2
4430
+ );
4431
+ const u = n.parent;
4432
+ u && u.removeChild(n);
4433
+ const f = await t.app.renderer.extract.base64({
4434
+ target: n,
4435
+ format: "png"
4605
4436
  });
4437
+ return u && u.addChild(n), ` <image href="${f}" x="${e.x}" y="${e.y}" width="${e.width}" height="${e.height}" />`;
4438
+ } catch {
4439
+ return "";
4440
+ } finally {
4441
+ n.x = s, n.y = o, n.width = a, n.height = c, n.tilePosition && n.tilePosition.set(l, h);
4442
+ }
4443
+ }
4444
+ function Fc(t) {
4445
+ const e = t.x * g - g / 2, i = t.y * g - g / 2, n = (t.width ?? 1) * g, r = (t.height ?? 1) * g, s = Ze(t.bgColor ?? 3447003), o = t.bgOpacity ?? 0.8, a = Ze(t.borderColor ?? 16777215), c = t.borderWidth ?? 2, l = t.borderOpacity ?? 1, h = t.borderRadius ?? 0, u = h > 0 ? ` rx="${h}" ry="${h}"` : "";
4446
+ return ` <rect x="${e}" y="${i}" width="${n}" height="${r}"${u} fill="${s}" fill-opacity="${o}" stroke="${a}" stroke-width="${c}" stroke-opacity="${l}" />`;
4447
+ }
4448
+ function Rc(t) {
4449
+ const e = t.options, i = Ze(e.color ?? 4473958), n = e.width ?? 6, r = t.getPoints();
4450
+ let s;
4451
+ if (r && r.length >= 2)
4452
+ s = r.map((a) => `${a.x},${a.y}`).join(" ");
4453
+ else {
4454
+ const a = [];
4455
+ for (const c of e.anchor) {
4456
+ const l = t.resolveAnchorPosition(c);
4457
+ l && a.push({ x: l.x * g, y: l.y * g });
4458
+ }
4459
+ if (a.length < 2) return "";
4460
+ s = a.map((c) => `${c.x},${c.y}`).join(" ");
4461
+ }
4462
+ const o = e.mode === "dashed" ? ` stroke-dasharray="${(e.dashPattern ?? [10, 5]).join(",")}"` : "";
4463
+ return ` <polyline points="${s}" fill="none" stroke="${i}" stroke-width="${n}" stroke-linecap="round" stroke-linejoin="round"${o} />`;
4464
+ }
4465
+ function Dc(t, e, i, n, r, s) {
4466
+ const o = t.x * g, a = t.y * g, c = t.scale ?? 1, l = t.isFlat ?? !1, h = l ? 1 : t.scaleY ?? 1, u = t.offsetY ?? 0, f = t.icon;
4467
+ if (!(e.has(f) && e.get(f) !== "")) return "";
4468
+ const p = 100, m = -p / 2 * h + cr / 2 + u, v = [];
4469
+ if (l) {
4470
+ const _ = p * c, x = p * c, w = o - _ / 2, S = a + c * (m - p / 2);
4471
+ v.push(
4472
+ ` <use href="#icon-${Ct(f)}" x="${w}" y="${S}" width="${_}" height="${x}" />`
4473
+ );
4474
+ } else {
4475
+ const _ = p * c, x = p * c * h, w = -_ / 2, S = c * h * m - x / 2, b = `matrix(${i},${n},${r},${s},0,0)`;
4476
+ v.push(` <g transform="translate(${o},${a})">`), v.push(` <g transform="${b}">`), v.push(
4477
+ ` <use href="#icon-${Ct(f)}" x="${w}" y="${S}" width="${_}" height="${x}" />`
4478
+ ), v.push(" </g>"), v.push(" </g>");
4479
+ }
4480
+ return v.join(`
4481
+ `);
4482
+ }
4483
+ function zc(t, e, i, n, r) {
4484
+ const s = t.x * g, o = t.y * g, a = t.fontSize ?? 16, c = Ze(t.color ?? 16777215), l = t.fontWeight ?? "normal", h = t.fontFamily ?? "Arial", u = t.align ?? "center", f = t.position ?? "x", d = u === "left" ? "start" : u === "right" ? "end" : "middle", p = Ct(t.text), m = `text-anchor="${d}" fill="${c}" font-size="${a}" font-weight="${l}" font-family="${h}"`;
4485
+ if (f === "screen") {
4486
+ const v = `matrix(${e},${i},${n},${r},0,0)`;
4487
+ return [
4488
+ ` <g transform="translate(${s},${o})">`,
4489
+ ` <text transform="${v}" ${m}>${p}</text>`,
4490
+ " </g>"
4491
+ ].join(`
4492
+ `);
4493
+ } else return f === "y" ? ` <text x="${s}" y="${o}" transform="rotate(-90,${s},${o})" ${m}>${p}</text>` : ` <text x="${s}" y="${o}" ${m}>${p}</text>`;
4494
+ }
4495
+ class De extends le {
4496
+ /** 默认缩放级别 */
4497
+ static DEFAULT_ZOOM = 0.8;
4498
+ id;
4499
+ app;
4500
+ icons;
4501
+ states;
4502
+ postprocess;
4503
+ projectionContainer;
4504
+ layer;
4505
+ grid;
4506
+ nodes;
4507
+ lines;
4508
+ texts;
4509
+ rects;
4510
+ fences;
4511
+ waves;
4512
+ images;
4513
+ sceneItems = /* @__PURE__ */ new Set();
4514
+ /** 上一帧相机状态,用于 ticker 检测增量变化 */
4515
+ lastCameraState = { x: 0, y: 0, scale: 0 };
4516
+ _isActive = !1;
4517
+ _cameraHandler;
4518
+ constructor(e, i, n, r) {
4519
+ super(), this.id = e, this.app = i, this.icons = n, this.states = r, this.postprocess = new Ti(), this.projectionContainer = new R(), this.layer = new Ja(this.projectionContainer), this.lines = new Ra(this), this.nodes = new Oa(this), this.texts = new Na(this), this.rects = new Ga(this), this.fences = new Ya(this), this.waves = new Wa(this), this.images = new Va(this), this.grid = new Da();
4520
+ const { skewX: s, skewY: o } = lr();
4521
+ this.projectionContainer.sortableChildren = !0, this.projectionContainer.skew.set(s, o), this.projectionContainer.scale.set(De.DEFAULT_ZOOM), this.projectionContainer.position.set(i.screen.width / 2, i.screen.height / 2), this.projectionContainer.addChild(this.grid.getGraphics()), this.grid.getGraphics().zIndex = -1, this.registerItem(this.grid);
4522
+ }
4523
+ // ========== 激活 / 停用 ==========
4524
+ setActive(e) {
4525
+ e !== this._isActive && (this._isActive = e, e ? (this.app.stage.addChild(this.projectionContainer), this.postprocess.bind(this.projectionContainer, this.app), this.app.ticker.add(this.tickerCallback, this), this.notifyCameraChange(!1, !1), this.emit("ready")) : (this.app.ticker.remove(this.tickerCallback, this), this.postprocess.unbind(), this.app.stage.removeChild(this.projectionContainer)));
4526
+ }
4527
+ get isActive() {
4528
+ return this._isActive;
4529
+ }
4530
+ // ========== 序列化 ==========
4531
+ toJSON() {
4532
+ return {
4533
+ nodes: this.nodes.toJSON(),
4534
+ lines: this.lines.toJSON(),
4535
+ texts: this.texts.toJSON(),
4536
+ rects: this.rects.toJSON(),
4537
+ fences: this.fences.toJSON(),
4538
+ waves: this.waves.toJSON(),
4539
+ images: this.images.toJSON(),
4540
+ grid: this.grid.toJSON(),
4541
+ camera: this.getCamera(),
4542
+ layer: this.layer.toJSON(),
4543
+ postprocess: this.postprocess.toJSON()
4544
+ };
4545
+ }
4546
+ fromJSON(e) {
4547
+ this.clear(), e.nodes && this.nodes.fromJSON(e.nodes), e.lines && this.lines.fromJSON(e.lines), e.texts && this.texts.fromJSON(e.texts), e.rects && this.rects.fromJSON(e.rects), e.fences && this.fences.fromJSON(e.fences), e.waves && this.waves.fromJSON(e.waves), e.images && this.images.fromJSON(e.images), e.grid && this.grid.fromJSON(e.grid), e.camera && this.setCamera(e.camera), e.layer?.length && this.layer.fromJSON(e.layer), e.postprocess && this.postprocess.fromJSON(e.postprocess);
4548
+ }
4549
+ /** 序列化为 YAML 字符串 */
4550
+ toYAML() {
4551
+ return be(this.toJSON());
4552
+ }
4553
+ /** 从 YAML 字符串反序列化 */
4554
+ fromYAML(e) {
4555
+ this.fromJSON(Ce(e));
4556
+ }
4557
+ // ========== 导出 ==========
4558
+ /**
4559
+ * 将场景导出为 PNG 图片
4560
+ * @param options 导出选项(padding / pixelRatio)
4561
+ * @returns PNG Blob
4562
+ */
4563
+ async exportPNG(e) {
4564
+ return Pc(this, e);
4565
+ }
4566
+ /**
4567
+ * 将场景导出为 SVG 字符串
4568
+ * @param options 导出选项(padding / background)
4569
+ * @returns SVG 文档字符串
4570
+ */
4571
+ async exportSVG(e) {
4572
+ return Lc(this, e);
4573
+ }
4574
+ /**
4575
+ * 导出 PNG 并自动触发浏览器下载
4576
+ * @param filename 文件名,默认为 `{sceneId}.png`
4577
+ * @param options 导出选项
4578
+ */
4579
+ async downloadPNG(e, i) {
4580
+ const n = await this.exportPNG(i);
4581
+ It(n, e ?? `${this.id}.png`);
4582
+ }
4583
+ /**
4584
+ * 导出 SVG 并自动触发浏览器下载
4585
+ * @param filename 文件名,默认为 `{sceneId}.svg`
4586
+ * @param options 导出选项
4587
+ */
4588
+ async downloadSVG(e, i) {
4589
+ const n = await this.exportSVG(i);
4590
+ It(n, e ?? `${this.id}.svg`);
4591
+ }
4592
+ // ========== 相机控制 ==========
4593
+ /**
4594
+ * 注册外部相机处理器(通常由 ViewportPlugin 调用)。
4595
+ * 注册后,`setCamera` 将委托给该处理器而非直接更新 projectionContainer,
4596
+ * 从而允许插件实现动画过渡等特性。
4597
+ *
4598
+ * @param handler 处理函数,传 `undefined` 可清除
4599
+ */
4600
+ setCameraHandler(e) {
4601
+ this._cameraHandler = e;
4602
+ }
4603
+ /**
4604
+ * 获取当前相机状态
4605
+ */
4606
+ getCamera() {
4607
+ const e = this.projectionContainer;
4608
+ return { zoom: e.scale.x, x: e.position.x, y: e.position.y };
4609
+ }
4610
+ /**
4611
+ * 设置相机状态。
4612
+ * - 如果有外部处理器(ViewportPlugin 等),委托给处理器更新(可带动画过渡)。
4613
+ * - 否则,直接更新 projectionContainer。
4614
+ *
4615
+ * 返回 Promise,在有动画过渡时可 `await` 等待完成。
4616
+ *
4617
+ * @param camera 部分相机参数,未传的字段保留当前值
4618
+ */
4619
+ setCamera(e) {
4620
+ const i = this.projectionContainer, n = {
4621
+ zoom: e.zoom ?? i.scale.x,
4622
+ x: e.x ?? i.position.x,
4623
+ y: e.y ?? i.position.y
4624
+ };
4625
+ return this._cameraHandler ? Promise.resolve(this._cameraHandler(n)) : (this.applyCamera(n), Promise.resolve());
4626
+ }
4627
+ /**
4628
+ * 直接将相机状态应用到 projectionContainer。
4629
+ * 当没有安装 ViewportPlugin 时由 `setCamera` 内部调用。
4630
+ * 也可被外部直接调用以跳过插件处理。
4631
+ */
4632
+ applyCamera(e) {
4633
+ const i = this.projectionContainer, n = this.lastCameraState, r = e.x !== n.x || e.y !== n.y, s = e.zoom !== n.scale;
4634
+ i.scale.set(e.zoom), i.position.set(e.x, e.y), this._isActive && (this.lastCameraState = { x: i.x, y: i.y, scale: i.scale.x }, this.notifyCameraChange(r, s));
4635
+ }
4636
+ /**
4637
+ * 获取默认相机状态(居中 + 默认缩放)
4638
+ */
4639
+ getDefaultCamera() {
4640
+ return {
4641
+ zoom: De.DEFAULT_ZOOM,
4642
+ x: this.app.screen.width / 2,
4643
+ y: this.app.screen.height / 2
4644
+ };
4645
+ }
4646
+ // ========== 常用操作 ==========
4647
+ get stage() {
4648
+ return this.app.stage;
4649
+ }
4650
+ clear() {
4651
+ this.lines.clear(), this.nodes.clear(), this.texts.clear(), this.rects.clear(), this.fences.clear(), this.waves.clear(), this.images.clear(), this.layer.clear(), this.postprocess.clear();
4652
+ }
4653
+ destroy() {
4654
+ this.setActive(!1), this.emit("destroyed"), this.grid?.destroy(), this.lines?.destroy(), this.nodes?.destroy(), this.texts?.destroy(), this.rects?.destroy(), this.fences?.destroy(), this.waves?.destroy(), this.images?.destroy(), this.layer?.destroy(), this.postprocess.destroy(), this.removeAllListeners(), this.sceneItems.clear(), this.app.stage.removeChild(this.projectionContainer), this.projectionContainer.destroy();
4655
+ }
4656
+ // ========== SceneItem 注册和渲染循环 ==========
4657
+ registerItem(e) {
4658
+ this.sceneItems.add(e);
4659
+ }
4660
+ unregisterItem(e) {
4661
+ this.sceneItems.delete(e);
4662
+ }
4663
+ tickerCallback = () => {
4664
+ const e = this.app.ticker.deltaMS;
4665
+ for (const a of this.sceneItems)
4666
+ a.onRender(e), a.tickStates(e);
4667
+ this.postprocess.update(e);
4668
+ const { x: i, y: n } = this.projectionContainer.position, r = this.projectionContainer.scale.x, s = i !== this.lastCameraState.x || n !== this.lastCameraState.y, o = r !== this.lastCameraState.scale;
4669
+ (s || o) && (this.lastCameraState = { x: i, y: n, scale: r }, this.notifyCameraChange(s, o));
4670
+ };
4671
+ /**
4672
+ * 通知所有 SceneItem 相机已变化,并向外发射相机事件。
4673
+ *
4674
+ * @param positionChanged 位置(x/y)是否改变;传 `false` 时不发射 `camera:move` 事件
4675
+ * @param zoomChanged 缩放是否改变;传 `false` 时不发射 `camera:zoom` 事件
4676
+ *
4677
+ * 两者均为 `false` 时仅通知 SceneItem(用于场景初始化),不发射任何相机事件。
4678
+ */
4679
+ notifyCameraChange(e = !0, i = !0) {
4680
+ const n = this.getViewRect(), r = this.projectionContainer.scale.x;
4681
+ for (const o of this.sceneItems) o.onCameraChange(n, r);
4682
+ if (!e && !i) return;
4683
+ const s = {
4684
+ ...this.getCamera(),
4685
+ positionChanged: e,
4686
+ zoomChanged: i
4687
+ };
4688
+ this.emit("camera:change", s), e && this.emit("camera:move", s), i && this.emit("camera:zoom", s);
4689
+ }
4690
+ getViewRect() {
4691
+ const { width: e, height: i } = this.app.screen, n = this.projectionContainer, r = n.skew.x, s = n.skew.y, o = n.scale.x, a = n.scale.y, c = Math.cos(s) * o, l = Math.sin(s) * o, h = Math.sin(r) * a, u = Math.cos(r) * a, f = n.x, d = n.y, p = c * u - l * h, m = u / p, v = -l / p, _ = -h / p, x = c / p, w = (ue, fe) => {
4692
+ const q = ue - f, de = fe - d;
4693
+ return { x: m * q + _ * de, y: v * q + x * de };
4694
+ }, S = w(0, 0), b = w(e, 0), E = w(0, i), N = w(e, i), j = Math.min(S.x, b.x, E.x, N.x), ee = Math.min(S.y, b.y, E.y, N.y), V = Math.max(S.x, b.x, E.x, N.x), X = Math.max(S.y, b.y, E.y, N.y);
4695
+ return { x: j, y: ee, width: V - j, height: X - ee };
4696
+ }
4697
+ /**
4698
+ * 计算场景中所有内容的包围盒(projectionContainer 本地坐标系)
4699
+ * @returns 包围盒,如果场景为空则返回 null
4700
+ */
4701
+ getContentBounds() {
4702
+ let i = 1 / 0, n = 1 / 0, r = -1 / 0, s = -1 / 0, o = !1;
4703
+ const a = (c, l) => {
4704
+ o = !0, c < i && (i = c), l < n && (n = l), c > r && (r = c), l > s && (s = l);
4705
+ };
4706
+ for (const c of this.nodes.getAll())
4707
+ a(c.options.x * 100, c.options.y * 100);
4708
+ for (const c of this.rects.getAll()) {
4709
+ const l = c.options.x * 100, h = c.options.y * 100, u = (c.options.width ?? 1) * 100, f = (c.options.height ?? 1) * 100;
4710
+ a(l, h), a(l + u, h + f);
4711
+ }
4712
+ for (const c of this.texts.getAll())
4713
+ a(c.options.x * 100, c.options.y * 100);
4714
+ for (const c of this.lines.getAll())
4715
+ for (const l of c.options.anchor) {
4716
+ const h = c.resolveAnchorPosition(l);
4717
+ h && a(h.x * 100, h.y * 100);
4718
+ }
4719
+ for (const c of this.fences.getAll()) {
4720
+ const l = c.options.x * 100, h = c.options.y * 100, u = c.options.direction ?? "+x", f = (c.options.length ?? 4) * 100;
4721
+ switch (a(l, h), u) {
4722
+ case "x":
4723
+ case "+x":
4724
+ a(l + f, h);
4725
+ break;
4726
+ case "-x":
4727
+ a(l - f, h);
4728
+ break;
4729
+ case "y":
4730
+ case "+y":
4731
+ a(l, h + f);
4732
+ break;
4733
+ case "-y":
4734
+ a(l, h - f);
4735
+ break;
4736
+ }
4737
+ }
4738
+ for (const c of this.waves.getAll()) {
4739
+ const l = (c.options.width ?? 4) * 100, h = (c.options.height ?? 4) * 100;
4740
+ a(c.options.x * 100 - l / 2, c.options.y * 100 - h / 2), a(c.options.x * 100 + l / 2, c.options.y * 100 + h / 2);
4741
+ }
4742
+ for (const c of this.images.getAll())
4743
+ a(c.options.x * 100, c.options.y * 100);
4744
+ return o ? { x: i, y: n, width: r - i, height: s - n } : null;
4745
+ }
4746
+ }
4747
+ class Oe {
4748
+ topology;
4749
+ sceneCleanups = /* @__PURE__ */ new Map();
4750
+ _onSceneAdded;
4751
+ _onSceneRemoved;
4752
+ _onSceneSwitched;
4753
+ _onItemPointerDown;
4754
+ _onItemPointerUp;
4755
+ _onItemPointerOver;
4756
+ _onItemPointerOut;
4757
+ install(e) {
4758
+ this.topology = e;
4759
+ for (const i of e.getSceneIds()) {
4760
+ const n = e.getScene(i);
4761
+ n && this.invokeHookScene(n);
4762
+ }
4763
+ this._onSceneAdded = (i) => this.invokeHookScene(i), this._onSceneRemoved = ({ id: i }) => {
4764
+ this.unhookScene(i), this.onSceneRemoved(i);
4765
+ }, this._onSceneSwitched = (i) => this.onSceneSwitched(i), this._onItemPointerDown = (i) => this.onItemPointerDown(i), this._onItemPointerUp = (i) => this.onItemPointerUp(i), this._onItemPointerOver = (i) => this.onItemPointerOver(i), this._onItemPointerOut = (i) => this.onItemPointerOut(i), e.on("scene:added", this._onSceneAdded), e.on("scene:removed", this._onSceneRemoved), e.on("scene:switched", this._onSceneSwitched), e.on("item:pointerdown", this._onItemPointerDown), e.on("item:pointerup", this._onItemPointerUp), e.on("item:pointerover", this._onItemPointerOver), e.on("item:pointerout", this._onItemPointerOut);
4606
4766
  }
4607
- /* ---------- 便捷存取器 ---------- */
4608
- get intensity() {
4609
- return this.resources.noiseUniforms.uniforms.uIntensity;
4767
+ uninstall(e) {
4768
+ for (const [i] of this.sceneCleanups) this.unhookScene(i);
4769
+ this._onSceneAdded && e.off("scene:added", this._onSceneAdded), this._onSceneRemoved && e.off("scene:removed", this._onSceneRemoved), this._onSceneSwitched && e.off("scene:switched", this._onSceneSwitched), this._onItemPointerDown && e.off("item:pointerdown", this._onItemPointerDown), this._onItemPointerUp && e.off("item:pointerup", this._onItemPointerUp), this._onItemPointerOver && e.off("item:pointerover", this._onItemPointerOver), this._onItemPointerOut && e.off("item:pointerout", this._onItemPointerOut), this._onSceneAdded = void 0, this._onSceneRemoved = void 0, this._onSceneSwitched = void 0, this._onItemPointerDown = void 0, this._onItemPointerUp = void 0, this._onItemPointerOver = void 0, this._onItemPointerOut = void 0, this.topology = void 0;
4610
4770
  }
4611
- set intensity(e) {
4612
- this.resources.noiseUniforms.uniforms.uIntensity = e;
4771
+ unhookScene(e) {
4772
+ const i = this.sceneCleanups.get(e);
4773
+ i && (i(), this.sceneCleanups.delete(e));
4613
4774
  }
4614
- get seed() {
4615
- return this.resources.noiseUniforms.uniforms.uSeed;
4775
+ invokeHookScene(e) {
4776
+ const i = this.hookScene(e);
4777
+ i && this.sceneCleanups.set(e.id, i);
4616
4778
  }
4617
- set seed(e) {
4618
- this.resources.noiseUniforms.uniforms.uSeed = e;
4779
+ onItemPointerDown(e) {
4619
4780
  }
4620
- }
4621
- function Df(t, e) {
4622
- const i = e?.intensity ?? 0.15;
4623
- let n = e?.animated ?? !0;
4624
- return {
4625
- postProcessId: t,
4626
- createFilter() {
4627
- return new Pc({ intensity: i });
4628
- },
4629
- onUpdate(r, s) {
4630
- n && (r.seed = Math.random());
4631
- },
4632
- setOptions(r, s) {
4633
- const o = r;
4634
- s.intensity !== void 0 && (o.intensity = s.intensity), s.animated !== void 0 && (n = s.animated);
4635
- }
4636
- };
4637
- }
4638
- const Mc = (
4639
- /* glsl */
4640
- `
4641
- in vec2 aPosition;
4642
- out vec2 vTextureCoord;
4643
-
4644
- uniform vec4 uInputSize;
4645
- uniform vec4 uOutputFrame;
4646
- uniform vec4 uOutputTexture;
4647
-
4648
- vec4 filterVertexPosition(void)
4649
- {
4650
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4651
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4652
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4653
- return vec4(position, 0.0, 1.0);
4654
- }
4655
-
4656
- vec2 filterTextureCoord(void)
4657
- {
4658
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4659
- }
4660
-
4661
- void main(void)
4662
- {
4663
- gl_Position = filterVertexPosition();
4664
- vTextureCoord = filterTextureCoord();
4665
- }
4666
- `
4667
- ), Ec = (
4668
- /* glsl */
4669
- `
4670
- precision highp float;
4671
-
4672
- in vec2 vTextureCoord;
4673
- out vec4 finalColor;
4674
-
4675
- uniform sampler2D uTexture;
4676
- uniform vec4 uInputSize;
4677
- uniform vec4 uOutputFrame;
4678
- uniform float uOffset;
4679
- uniform vec2 uDirection;
4680
-
4681
- void main(void)
4682
- {
4683
- // 将归一化 0.5 中心映射到实际纹理坐标空间
4684
- vec2 texCenter = vec2(0.5) * (uOutputFrame.zw * uInputSize.zw);
4685
- // 从中心到当前像素的方向
4686
- vec2 center = vTextureCoord - texCenter;
4687
- vec2 dir = normalize(center) * uOffset * 0.01;
4688
-
4689
- // 沿径向偏移采样 RGB 三通道
4690
- float r = texture(uTexture, vTextureCoord + dir).r;
4691
- float g = texture(uTexture, vTextureCoord).g;
4692
- float b = texture(uTexture, vTextureCoord - dir).b;
4693
- float a = texture(uTexture, vTextureCoord).a;
4694
-
4695
- finalColor = vec4(r, g, b, a);
4696
- }
4697
- `
4698
- );
4699
- class Lc extends G {
4700
- constructor(e = {}) {
4701
- const i = e.offset ?? 3, n = U.from({
4702
- vertex: Mc,
4703
- fragment: Ec,
4704
- name: "chromatic-aberration-filter"
4705
- });
4706
- super({
4707
- glProgram: n,
4708
- resources: {
4709
- chromaticUniforms: new H({
4710
- uOffset: { value: i, type: "f32" },
4711
- uDirection: { value: new Float32Array([1, 1]), type: "vec2<f32>" }
4712
- })
4713
- }
4714
- });
4781
+ onItemPointerUp(e) {
4715
4782
  }
4716
- /* ---------- 便捷存取器 ---------- */
4717
- get offset() {
4718
- return this.resources.chromaticUniforms.uniforms.uOffset;
4783
+ onItemPointerOver(e) {
4719
4784
  }
4720
- set offset(e) {
4721
- this.resources.chromaticUniforms.uniforms.uOffset = e;
4785
+ onItemPointerOut(e) {
4786
+ }
4787
+ onSceneSwitched(e) {
4788
+ }
4789
+ onSceneRemoved(e) {
4722
4790
  }
4723
4791
  }
4724
- function zf(t, e) {
4725
- const i = e?.offset ?? 3;
4726
- return {
4727
- postProcessId: t,
4728
- createFilter() {
4729
- return new Lc({ offset: i });
4730
- },
4731
- setOptions(n, r) {
4732
- const s = n;
4733
- r.offset !== void 0 && (s.offset = r.offset);
4734
- }
4735
- };
4736
- }
4737
- const kc = (
4738
- /* glsl */
4739
- `
4740
- in vec2 aPosition;
4741
- out vec2 vTextureCoord;
4742
-
4743
- uniform vec4 uInputSize;
4744
- uniform vec4 uOutputFrame;
4745
- uniform vec4 uOutputTexture;
4746
-
4747
- vec4 filterVertexPosition(void)
4748
- {
4749
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4750
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4751
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4752
- return vec4(position, 0.0, 1.0);
4753
- }
4754
-
4755
- vec2 filterTextureCoord(void)
4756
- {
4757
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4758
- }
4759
-
4760
- void main(void)
4761
- {
4762
- gl_Position = filterVertexPosition();
4763
- vTextureCoord = filterTextureCoord();
4764
- }
4765
- `
4766
- ), Fc = (
4767
- /* glsl */
4768
- `
4769
- precision highp float;
4770
-
4771
- in vec2 vTextureCoord;
4772
- out vec4 finalColor;
4773
-
4774
- uniform sampler2D uTexture;
4775
- uniform vec4 uInputSize;
4776
- uniform float uLineWidth;
4777
- uniform float uIntensity;
4778
- uniform float uOffset;
4779
-
4780
- void main(void)
4781
- {
4782
- vec4 original = texture(uTexture, vTextureCoord);
4783
-
4784
- // 按像素行计算扫描线
4785
- float y = vTextureCoord.y * uInputSize.y + uOffset;
4786
- float scanline = step(0.5, fract(y / uLineWidth));
4787
-
4788
- // 暗行降低亮度
4789
- float factor = 1.0 - scanline * uIntensity;
4790
-
4791
- finalColor = vec4(original.rgb * factor, original.a);
4792
- }
4793
- `
4794
- );
4795
- class Rc extends G {
4796
- constructor(e = {}) {
4797
- const i = e.lineWidth ?? 2, n = e.intensity ?? 0.3, r = e.offset ?? 0, s = U.from({
4798
- vertex: kc,
4799
- fragment: Fc,
4800
- name: "scanlines-filter"
4801
- });
4802
- super({
4803
- glProgram: s,
4804
- resources: {
4805
- scanlinesUniforms: new H({
4806
- uLineWidth: { value: i, type: "f32" },
4807
- uIntensity: { value: n, type: "f32" },
4808
- uOffset: { value: r, type: "f32" }
4809
- })
4810
- }
4811
- });
4792
+ class Oi extends Oe {
4793
+ activeScene;
4794
+ hookScene(e) {
4812
4795
  }
4813
- /* ---------- 便捷存取器 ---------- */
4814
- get lineWidth() {
4815
- return this.resources.scanlinesUniforms.uniforms.uLineWidth;
4796
+ install(e) {
4797
+ super.install(e), e.activeScene && this.bindActive(e.activeScene);
4816
4798
  }
4817
- set lineWidth(e) {
4818
- this.resources.scanlinesUniforms.uniforms.uLineWidth = e;
4799
+ onSceneSwitched({ to: e }) {
4800
+ this.unbindActive(), this.bindActive(e);
4819
4801
  }
4820
- get intensity() {
4821
- return this.resources.scanlinesUniforms.uniforms.uIntensity;
4802
+ onSceneRemoved(e) {
4803
+ this.activeScene?.id === e && (this.onUnbindActiveScene(), this.activeScene = void 0);
4822
4804
  }
4823
- set intensity(e) {
4824
- this.resources.scanlinesUniforms.uniforms.uIntensity = e;
4805
+ bindActive(e) {
4806
+ this.activeScene = e, this.onBindActiveScene(e);
4825
4807
  }
4826
- get offset() {
4827
- return this.resources.scanlinesUniforms.uniforms.uOffset;
4808
+ unbindActive() {
4809
+ this.activeScene && (this.onUnbindActiveScene(), this.activeScene = void 0);
4810
+ }
4811
+ uninstall(e) {
4812
+ this.unbindActive(), super.uninstall(e);
4813
+ }
4814
+ }
4815
+ class Nc extends Oi {
4816
+ name = "hit-test";
4817
+ _enabled = !0;
4818
+ _hoveredItemId = null;
4819
+ _hoveredItemType = null;
4820
+ get enabled() {
4821
+ return this._enabled;
4822
+ }
4823
+ set enabled(e) {
4824
+ this._enabled = e;
4825
+ }
4826
+ _handlers;
4827
+ onBindActiveScene(e) {
4828
+ const i = this.topology, n = i.app.stage, r = (c) => {
4829
+ let l = c.target;
4830
+ for (; l && l !== n; ) {
4831
+ const h = Ta(l);
4832
+ if (h) {
4833
+ const { itemType: u, itemId: f } = h;
4834
+ return { itemType: u, itemId: f };
4835
+ }
4836
+ l = l.parent;
4837
+ }
4838
+ return { itemType: null, itemId: null };
4839
+ }, s = (c) => {
4840
+ if (!this._enabled) return;
4841
+ const { itemType: l, itemId: h } = r(c);
4842
+ i.emit("item:pointerdown", { itemType: l, itemId: h, sceneId: e.id, event: c });
4843
+ }, o = (c) => {
4844
+ if (!this._enabled) return;
4845
+ const { itemType: l, itemId: h } = r(c);
4846
+ i.emit("item:pointerup", { itemType: l, itemId: h, sceneId: e.id, event: c });
4847
+ }, a = (c) => {
4848
+ if (!this._enabled) return;
4849
+ const { itemType: l, itemId: h } = r(c);
4850
+ (this._hoveredItemId !== h || this._hoveredItemType !== l) && (this._hoveredItemId !== null && this._hoveredItemType !== null && i.emit("item:pointerout", {
4851
+ itemType: this._hoveredItemType,
4852
+ itemId: this._hoveredItemId,
4853
+ sceneId: e.id,
4854
+ event: c
4855
+ }), h !== null && l !== null && i.emit("item:pointerover", { itemType: l, itemId: h, sceneId: e.id, event: c }), this._hoveredItemId = h, this._hoveredItemType = l);
4856
+ };
4857
+ n.on("pointerdown", s), n.on("pointerup", o), n.on("pointermove", a), this._handlers = { onPointerDown: s, onPointerUp: o, onPointerMove: a };
4828
4858
  }
4829
- set offset(e) {
4830
- this.resources.scanlinesUniforms.uniforms.uOffset = e;
4859
+ onUnbindActiveScene() {
4860
+ if (!this._handlers) return;
4861
+ const e = this.topology?.app.stage;
4862
+ e && (e.off("pointerdown", this._handlers.onPointerDown), e.off("pointerup", this._handlers.onPointerUp), e.off("pointermove", this._handlers.onPointerMove)), this._handlers = void 0, this._hoveredItemId = null, this._hoveredItemType = null;
4831
4863
  }
4832
4864
  }
4833
- function Nf(t, e) {
4834
- const i = e?.lineWidth ?? 2, n = e?.intensity ?? 0.3;
4835
- let r = e?.animated ?? !1, s = e?.speed ?? 0.5, o = 0;
4836
- return {
4837
- postProcessId: t,
4838
- createFilter() {
4839
- return new Rc({ lineWidth: i, intensity: n, offset: 0 });
4840
- },
4841
- onUpdate(a, c) {
4842
- r && (o += s * c, a.offset = o);
4843
- },
4844
- setOptions(a, c) {
4845
- const l = a;
4846
- c.lineWidth !== void 0 && (l.lineWidth = c.lineWidth), c.intensity !== void 0 && (l.intensity = c.intensity), c.animated !== void 0 && (r = c.animated), c.speed !== void 0 && (s = c.speed);
4847
- }
4848
- };
4849
- }
4850
- const Dc = (
4851
- /* glsl */
4852
- `
4853
- in vec2 aPosition;
4854
- out vec2 vTextureCoord;
4855
-
4856
- uniform vec4 uInputSize;
4857
- uniform vec4 uOutputFrame;
4858
- uniform vec4 uOutputTexture;
4859
-
4860
- vec4 filterVertexPosition(void)
4861
- {
4862
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
4863
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
4864
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
4865
- return vec4(position, 0.0, 1.0);
4866
- }
4867
-
4868
- vec2 filterTextureCoord(void)
4869
- {
4870
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4871
- }
4872
-
4873
- void main(void)
4874
- {
4875
- gl_Position = filterVertexPosition();
4876
- vTextureCoord = filterTextureCoord();
4877
- }
4878
- `
4879
- ), zc = (
4880
- /* glsl */
4881
- `
4882
- precision highp float;
4883
-
4884
- in vec2 vTextureCoord;
4885
- out vec4 finalColor;
4886
-
4887
- uniform sampler2D uTexture;
4888
- uniform vec4 uInputSize;
4889
- uniform vec4 uOutputFrame;
4890
- uniform vec2 uCenter;
4891
- uniform float uIntensity;
4892
- uniform float uDecay;
4893
- uniform float uDensity;
4894
- uniform float uWeight;
4895
-
4896
- const int NUM_SAMPLES = 30;
4897
-
4898
- void main(void)
4899
- {
4900
- vec2 texCoord = vTextureCoord;
4901
- // 将归一化 0~1 的光源中心映射到实际纹理坐标空间
4902
- vec2 center = uCenter * (uOutputFrame.zw * uInputSize.zw);
4903
- vec2 deltaTexCoord = (texCoord - center) * (1.0 / float(NUM_SAMPLES)) * uDensity;
4904
-
4905
- vec4 color = texture(uTexture, texCoord);
4906
- float illuminationDecay = 1.0;
4907
-
4908
- for (int i = 0; i < NUM_SAMPLES; i++) {
4909
- texCoord -= deltaTexCoord;
4910
- vec4 samp = texture(uTexture, texCoord);
4911
- samp *= illuminationDecay * uWeight;
4912
- color += samp;
4913
- illuminationDecay *= uDecay;
4865
+ class $c extends le {
4866
+ icons = /* @__PURE__ */ new Map();
4867
+ textureCache = /* @__PURE__ */ new Map();
4868
+ loading = /* @__PURE__ */ new Map();
4869
+ /** 添加或批量添加图标 */
4870
+ add(e) {
4871
+ for (const i of Array.isArray(e) ? e : [e]) {
4872
+ const { id: n } = i, r = this.icons.has(n);
4873
+ this.icons.set(n, i), r ? (this.textureCache.delete(n), this.loading.delete(n), this.emit("icon:updated", i)) : this.emit("icon:added", i);
4914
4874
  }
4915
-
4916
- vec4 original = texture(uTexture, vTextureCoord);
4917
- finalColor = mix(original, color * uIntensity / float(NUM_SAMPLES), uIntensity);
4918
- }
4919
- `
4920
- );
4921
- class Nc extends G {
4922
- constructor(e = {}) {
4923
- const i = e.centerX ?? 0.5, n = e.centerY ?? 0.5, r = e.intensity ?? 0.3, s = e.decay ?? 0.96, o = e.density ?? 1, a = e.weight ?? 0.5, c = U.from({
4924
- vertex: Dc,
4925
- fragment: zc,
4926
- name: "god-rays-filter"
4927
- });
4928
- super({
4929
- glProgram: c,
4930
- resources: {
4931
- godRaysUniforms: new H({
4932
- uCenter: { value: new Float32Array([i, n]), type: "vec2<f32>" },
4933
- uIntensity: { value: r, type: "f32" },
4934
- uDecay: { value: s, type: "f32" },
4935
- uDensity: { value: o, type: "f32" },
4936
- uWeight: { value: a, type: "f32" }
4937
- })
4938
- }
4939
- });
4875
+ return this;
4940
4876
  }
4941
- /* ---------- 便捷存取器 ---------- */
4942
- get centerX() {
4943
- return this.resources.godRaysUniforms.uniforms.uCenter[0];
4877
+ /** 删除图标(按 id) */
4878
+ remove(e) {
4879
+ return this.icons.delete(e) ? (this.textureCache.delete(e), this.loading.delete(e), this.emit("icon:removed", { id: e }), !0) : !1;
4944
4880
  }
4945
- set centerX(e) {
4946
- this.resources.godRaysUniforms.uniforms.uCenter[0] = e;
4881
+ /** 获取图标 URL(按 id) */
4882
+ getUrl(e) {
4883
+ return this.icons.get(e)?.url;
4947
4884
  }
4948
- get centerY() {
4949
- return this.resources.godRaysUniforms.uniforms.uCenter[1];
4885
+ /** 获取图标完整信息(按 id) */
4886
+ get(e) {
4887
+ return this.icons.get(e);
4950
4888
  }
4951
- set centerY(e) {
4952
- this.resources.godRaysUniforms.uniforms.uCenter[1] = e;
4889
+ /** 是否已注册(按 id) */
4890
+ has(e) {
4891
+ return this.icons.has(e);
4953
4892
  }
4954
- get intensity() {
4955
- return this.resources.godRaysUniforms.uniforms.uIntensity;
4893
+ /** 获取所有图标 */
4894
+ getAll() {
4895
+ return [...this.icons.values()];
4956
4896
  }
4957
- set intensity(e) {
4958
- this.resources.godRaysUniforms.uniforms.uIntensity = e;
4897
+ /**
4898
+ * 加载图标纹理,自动缓存。
4899
+ * 参数为图标 id;若 id 未注册则当作 URL 直接加载。
4900
+ */
4901
+ async loadTexture(e) {
4902
+ const i = this.textureCache.get(e);
4903
+ if (i) return i;
4904
+ const n = this.loading.get(e);
4905
+ if (n) return n;
4906
+ const r = this.icons.get(e)?.url ?? e, s = gi.load(r).then((o) => (this.textureCache.set(e, o), this.loading.delete(e), o));
4907
+ return this.loading.set(e, s), s;
4959
4908
  }
4960
- get decay() {
4961
- return this.resources.godRaysUniforms.uniforms.uDecay;
4909
+ /** 清空所有图标和缓存 */
4910
+ clear() {
4911
+ this.icons.clear(), this.textureCache.clear(), this.loading.clear(), this.emit("icons:cleared");
4962
4912
  }
4963
- set decay(e) {
4964
- this.resources.godRaysUniforms.uniforms.uDecay = e;
4913
+ /** 序列化 */
4914
+ toJSON() {
4915
+ return this.getAll();
4965
4916
  }
4966
- get density() {
4967
- return this.resources.godRaysUniforms.uniforms.uDensity;
4917
+ /** 反序列化(追加模式) */
4918
+ fromJSON(e) {
4919
+ this.add(e);
4968
4920
  }
4969
- set density(e) {
4970
- this.resources.godRaysUniforms.uniforms.uDensity = e;
4921
+ /** 序列化为 YAML 字符串 */
4922
+ toYAML() {
4923
+ return be(this.toJSON());
4971
4924
  }
4972
- get weight() {
4973
- return this.resources.godRaysUniforms.uniforms.uWeight;
4925
+ /** YAML 字符串反序列化(追加模式) */
4926
+ fromYAML(e) {
4927
+ this.fromJSON(Ce(e));
4974
4928
  }
4975
- set weight(e) {
4976
- this.resources.godRaysUniforms.uniforms.uWeight = e;
4929
+ destroy() {
4930
+ this.clear(), this.removeAllListeners();
4977
4931
  }
4978
4932
  }
4979
- function $f(t, e) {
4980
- const i = e?.centerX ?? 0.5, n = e?.centerY ?? 0.5, r = e?.intensity ?? 0.3, s = e?.decay ?? 0.96, o = e?.density ?? 1, a = e?.weight ?? 0.5;
4933
+ const Wt = /* @__PURE__ */ new WeakMap();
4934
+ function Rf(t, e, i) {
4935
+ const n = i?.frequency ?? 2, r = i?.minAlpha ?? 0.2, s = i?.maxAlpha ?? 1;
4981
4936
  return {
4982
- postProcessId: t,
4983
- createFilter() {
4984
- return new Nc({ centerX: i, centerY: n, intensity: r, decay: s, density: o, weight: a });
4937
+ stateId: t,
4938
+ type: e,
4939
+ onEnter(o) {
4940
+ Wt.set(o, { elapsed: 0 });
4985
4941
  },
4986
- setOptions(c, l) {
4987
- const h = c;
4988
- l.centerX !== void 0 && (h.centerX = l.centerX), l.centerY !== void 0 && (h.centerY = l.centerY), l.intensity !== void 0 && (h.intensity = l.intensity), l.decay !== void 0 && (h.decay = l.decay), l.density !== void 0 && (h.density = l.density), l.weight !== void 0 && (h.weight = l.weight);
4942
+ onLeave(o) {
4943
+ Wt.delete(o), o.getGraphics().alpha = 1;
4944
+ },
4945
+ onUpdate(o, a) {
4946
+ const c = Wt.get(o);
4947
+ if (!c) return;
4948
+ c.elapsed += a;
4949
+ const l = (Math.sin(c.elapsed * n * Math.PI * 2 / 1e3) + 1) / 2;
4950
+ o.getGraphics().alpha = r + l * (s - r);
4989
4951
  }
4990
4952
  };
4991
4953
  }
4992
- const $c = (
4993
- /* glsl */
4994
- `
4995
- in vec2 aPosition;
4996
- out vec2 vTextureCoord;
4997
-
4998
- uniform vec4 uInputSize;
4999
- uniform vec4 uOutputFrame;
5000
- uniform vec4 uOutputTexture;
5001
-
5002
- vec4 filterVertexPosition(void)
5003
- {
5004
- vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
5005
- position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
5006
- position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
5007
- return vec4(position, 0.0, 1.0);
5008
- }
5009
-
5010
- vec2 filterTextureCoord(void)
5011
- {
5012
- return aPosition * (uOutputFrame.zw * uInputSize.zw);
4954
+ const ot = /* @__PURE__ */ new WeakMap();
4955
+ function Df(t, e, i) {
4956
+ const n = i?.frequency ?? 2, r = i?.height ?? 12;
4957
+ return {
4958
+ stateId: t,
4959
+ type: e,
4960
+ onEnter(s) {
4961
+ const o = s.getGraphics();
4962
+ ot.set(s, { elapsed: 0, origY: o.position.y, origX: o.position.x });
4963
+ },
4964
+ onLeave(s) {
4965
+ const o = ot.get(s);
4966
+ o && (s.getGraphics().position.y = o.origY, s.getGraphics().position.x = o.origX, ot.delete(s));
4967
+ },
4968
+ onUpdate(s, o) {
4969
+ const a = ot.get(s);
4970
+ if (!a) return;
4971
+ a.elapsed += o;
4972
+ const c = Math.abs(Math.sin(a.elapsed * n * Math.PI / 1e3));
4973
+ s.getGraphics().position.y = a.origY - r * c, s.getGraphics().position.x = a.origX - r * c;
4974
+ }
4975
+ };
5013
4976
  }
5014
-
5015
- void main(void)
5016
- {
5017
- gl_Position = filterVertexPosition();
5018
- vTextureCoord = filterTextureCoord();
4977
+ const Ve = /* @__PURE__ */ new WeakMap();
4978
+ function Rt(t) {
4979
+ const e = t.getGraphics(), i = (Ve.get(e) ?? 0) + 1;
4980
+ if (Ve.set(e, i), i > 1) return;
4981
+ const n = e.getLocalBounds(), r = 100, s = 80, o = Math.min(n.x, -r) - s, a = Math.min(n.y, -r) - s, c = Math.max(n.x + n.width, r) + s, l = Math.max(n.y + n.height, r) + s;
4982
+ e.filterArea = new Lt(o, a, c - o, l - a);
5019
4983
  }
5020
- `
5021
- ), Gc = (
5022
- /* glsl */
5023
- `
5024
- precision highp float;
5025
-
5026
- in vec2 vTextureCoord;
5027
- out vec4 finalColor;
5028
-
5029
- uniform sampler2D uTexture;
5030
- uniform float uAmount;
5031
-
5032
- void main(void)
5033
- {
5034
- vec4 original = texture(uTexture, vTextureCoord);
5035
-
5036
- // Rec.709 亮度系数
5037
- float lum = dot(original.rgb, vec3(0.2126, 0.7152, 0.0722));
5038
- vec3 gray = vec3(lum);
5039
-
5040
- // 按 amount 在原色和灰色之间插值
5041
- vec3 result = mix(original.rgb, gray, uAmount);
5042
-
5043
- finalColor = vec4(result, original.a);
4984
+ function Dt(t) {
4985
+ const e = t.getGraphics(), i = (Ve.get(e) ?? 1) - 1;
4986
+ i <= 0 ? (Ve.delete(e), e.filterArea = void 0) : Ve.set(e, i);
5044
4987
  }
5045
- `
5046
- );
5047
- class Uc extends G {
5048
- constructor(e = {}) {
5049
- const i = e.amount ?? 1, n = U.from({
5050
- vertex: $c,
5051
- fragment: Gc,
5052
- name: "grayscale-filter"
5053
- });
5054
- super({
5055
- glProgram: n,
5056
- resources: {
5057
- grayscaleUniforms: new H({
5058
- uAmount: { value: i, type: "f32" }
5059
- })
4988
+ const jt = /* @__PURE__ */ new WeakMap();
4989
+ function zf(t, e, i) {
4990
+ const n = i?.brightness ?? 1.5;
4991
+ return {
4992
+ stateId: t,
4993
+ type: e,
4994
+ onEnter(r) {
4995
+ const s = r.getGraphics(), o = new mi();
4996
+ o.brightness(n, !1), jt.set(r, o), Rt(r), s.filters = [...s.filters ?? [], o];
4997
+ },
4998
+ onLeave(r) {
4999
+ const s = jt.get(r);
5000
+ if (s) {
5001
+ const o = r.getGraphics();
5002
+ o.filters = (o.filters ?? []).filter((a) => a !== s), Dt(r), jt.delete(r);
5060
5003
  }
5061
- });
5062
- }
5063
- /* ---------- 便捷存取器 ---------- */
5064
- get amount() {
5065
- return this.resources.grayscaleUniforms.uniforms.uAmount;
5066
- }
5067
- set amount(e) {
5068
- this.resources.grayscaleUniforms.uniforms.uAmount = e;
5069
- }
5004
+ }
5005
+ };
5070
5006
  }
5071
- function Gf(t, e) {
5072
- const i = e?.amount ?? 1;
5007
+ const Vt = /* @__PURE__ */ new WeakMap();
5008
+ function Nf(t, e, i) {
5009
+ const n = Math.max(0, Math.min(1, i?.brightness ?? 0.5));
5073
5010
  return {
5074
- postProcessId: t,
5075
- createFilter() {
5076
- return new Uc({ amount: i });
5011
+ stateId: t,
5012
+ type: e,
5013
+ onEnter(r) {
5014
+ const s = r.getGraphics(), o = new mi();
5015
+ o.brightness(n, !1), Vt.set(r, o), Rt(r), s.filters = [...s.filters ?? [], o];
5077
5016
  },
5078
- setOptions(n, r) {
5079
- const s = n;
5080
- r.amount !== void 0 && (s.amount = r.amount);
5017
+ onLeave(r) {
5018
+ const s = Vt.get(r);
5019
+ if (s) {
5020
+ const o = r.getGraphics();
5021
+ o.filters = (o.filters ?? []).filter((a) => a !== s), Dt(r), Vt.delete(r);
5022
+ }
5081
5023
  }
5082
5024
  };
5083
5025
  }
5084
- const Hc = (
5026
+ const Gc = (
5085
5027
  /* glsl */
5086
5028
  `
5087
5029
  in vec2 aPosition;
@@ -5110,9 +5052,12 @@ void main(void)
5110
5052
  vTextureCoord = filterTextureCoord();
5111
5053
  }
5112
5054
  `
5113
- ), Yc = (
5114
- /* glsl */
5115
- `
5055
+ );
5056
+ function Uc(t) {
5057
+ const e = Math.max(8, Math.round(t * 3)), i = Math.max(2, Math.round(t)), n = e * i;
5058
+ return (
5059
+ /* glsl */
5060
+ `
5116
5061
  precision highp float;
5117
5062
 
5118
5063
  in vec2 vTextureCoord;
@@ -5120,68 +5065,150 @@ out vec4 finalColor;
5120
5065
 
5121
5066
  uniform sampler2D uTexture;
5122
5067
  uniform vec4 uInputSize;
5123
- uniform float uStrength;
5068
+ uniform float uDistance;
5069
+ uniform float uOuterStrength;
5070
+ uniform vec3 uColor;
5071
+
5072
+ const int ANGLE_STEPS = ${e};
5073
+ const int DIST_STEPS = ${i};
5074
+ const float TOTAL = ${n}.0;
5075
+ const float PI2 = 6.28318530718;
5124
5076
 
5125
5077
  void main(void)
5126
5078
  {
5127
- vec2 step = uInputSize.zw;
5079
+ vec4 original = texture(uTexture, vTextureCoord);
5128
5080
 
5129
- // 采样 3×3 邻域
5130
- vec4 center = texture(uTexture, vTextureCoord);
5131
- vec4 top = texture(uTexture, vTextureCoord + vec2( 0.0, -step.y));
5132
- vec4 bottom = texture(uTexture, vTextureCoord + vec2( 0.0, step.y));
5133
- vec4 left = texture(uTexture, vTextureCoord + vec2(-step.x, 0.0));
5134
- vec4 right = texture(uTexture, vTextureCoord + vec2( step.x, 0.0));
5081
+ /* ---- 环形采样:累计周围像素的 alpha ---- */
5082
+ float totalAlpha = 0.0;
5135
5083
 
5136
- // 拉普拉斯卷积核: [-1, -1, -1; -1, 8, -1; -1, -1, -1] 的简化版
5137
- // 仅取上下左右 4 邻域: [0,-1,0; -1,4,-1; 0,-1,0]
5138
- vec4 edge = center * 4.0 - top - bottom - left - right;
5084
+ for (int i = 0; i < ANGLE_STEPS; i++) {
5085
+ float angle = float(i) * PI2 / float(ANGLE_STEPS);
5086
+ vec2 dir = vec2(cos(angle), sin(angle));
5139
5087
 
5140
- // 按强度叠加锐化
5141
- vec4 result = center + edge * uStrength;
5088
+ for (int j = 1; j <= DIST_STEPS; j++) {
5089
+ float pct = float(j) / float(DIST_STEPS);
5090
+ vec2 offset = dir * pct * uDistance * uInputSize.zw;
5091
+ totalAlpha += texture(uTexture, vTextureCoord + offset).a;
5092
+ }
5093
+ }
5142
5094
 
5143
- finalColor = vec4(clamp(result.rgb, 0.0, 1.0), center.a);
5095
+ totalAlpha /= TOTAL;
5096
+
5097
+ /* ---- 外发光:仅在原始像素透明处显示 ---- */
5098
+ float glowAlpha = clamp(totalAlpha * uOuterStrength * (1.0 - original.a), 0.0, 1.0);
5099
+
5100
+ /* ---- premultiplied-alpha 合成:original 在上,glow 在下 ---- */
5101
+ vec4 glow = vec4(uColor * glowAlpha, glowAlpha);
5102
+ finalColor = original + glow * (1.0 - original.a);
5144
5103
  }
5145
5104
  `
5146
- );
5147
- class Bc extends G {
5105
+ );
5106
+ }
5107
+ class Hc extends G {
5148
5108
  constructor(e = {}) {
5149
- const i = e.strength ?? 0.5, n = U.from({
5150
- vertex: Hc,
5151
- fragment: Yc,
5152
- name: "sharpen-filter"
5109
+ const i = e.quality ?? 4, n = e.distance ?? 10, r = e.color ?? [1, 1, 0], s = e.outerStrength ?? 4, o = U.from({
5110
+ vertex: Gc,
5111
+ fragment: Uc(i),
5112
+ name: "glow-filter"
5153
5113
  });
5154
5114
  super({
5155
- glProgram: n,
5115
+ glProgram: o,
5156
5116
  resources: {
5157
- sharpenUniforms: new H({
5158
- uStrength: { value: i, type: "f32" }
5117
+ glowUniforms: new H({
5118
+ uDistance: { value: n, type: "f32" },
5119
+ uOuterStrength: { value: s, type: "f32" },
5120
+ uColor: {
5121
+ value: new Float32Array(r),
5122
+ type: "vec3<f32>"
5123
+ }
5159
5124
  })
5160
- }
5125
+ },
5126
+ padding: n + 5
5161
5127
  });
5162
5128
  }
5163
5129
  /* ---------- 便捷存取器 ---------- */
5164
- get strength() {
5165
- return this.resources.sharpenUniforms.uniforms.uStrength;
5130
+ get distance() {
5131
+ return this.resources.glowUniforms.uniforms.uDistance;
5166
5132
  }
5167
- set strength(e) {
5168
- this.resources.sharpenUniforms.uniforms.uStrength = e;
5133
+ set distance(e) {
5134
+ this.resources.glowUniforms.uniforms.uDistance = e, this.padding = e + 5;
5135
+ }
5136
+ get outerStrength() {
5137
+ return this.resources.glowUniforms.uniforms.uOuterStrength;
5138
+ }
5139
+ set outerStrength(e) {
5140
+ this.resources.glowUniforms.uniforms.uOuterStrength = e;
5141
+ }
5142
+ get color() {
5143
+ return this.resources.glowUniforms.uniforms.uColor;
5144
+ }
5145
+ set color(e) {
5146
+ const i = this.resources.glowUniforms.uniforms.uColor;
5147
+ i[0] = e[0], i[1] = e[1], i[2] = e[2];
5169
5148
  }
5170
5149
  }
5171
- function Uf(t, e) {
5172
- const i = e?.strength ?? 0.5;
5150
+ const at = /* @__PURE__ */ new WeakMap();
5151
+ function Yc(t) {
5152
+ return [
5153
+ (t >> 16 & 255) / 255,
5154
+ (t >> 8 & 255) / 255,
5155
+ (t & 255) / 255
5156
+ ];
5157
+ }
5158
+ function $f(t, e, i) {
5159
+ const n = i?.color ?? 16776960, r = i?.distance ?? 10, [s, o] = i?.strength ?? [2, 6], a = i?.frequency ?? 1.5, c = i?.quality ?? 4, l = Yc(n);
5173
5160
  return {
5174
- postProcessId: t,
5175
- createFilter() {
5176
- return new Bc({ strength: i });
5161
+ stateId: t,
5162
+ type: e,
5163
+ onEnter(h) {
5164
+ const u = h.getGraphics(), f = new Hc({
5165
+ color: l,
5166
+ distance: r,
5167
+ outerStrength: s,
5168
+ quality: c
5169
+ });
5170
+ at.set(h, { elapsed: 0, filter: f }), Rt(h), u.filters = [...u.filters ?? [], f];
5177
5171
  },
5178
- setOptions(n, r) {
5179
- const s = n;
5180
- r.strength !== void 0 && (s.strength = r.strength);
5172
+ onLeave(h) {
5173
+ const u = at.get(h);
5174
+ if (u) {
5175
+ const f = h.getGraphics();
5176
+ f.filters = (f.filters ?? []).filter((d) => d !== u.filter), Dt(h), at.delete(h);
5177
+ }
5178
+ },
5179
+ onUpdate(h, u) {
5180
+ const f = at.get(h);
5181
+ if (!f) return;
5182
+ f.elapsed += u;
5183
+ const d = (Math.sin(f.elapsed * a * Math.PI * 2 / 1e3) + 1) / 2;
5184
+ f.filter.outerStrength = s + d * (o - s);
5185
+ }
5186
+ };
5187
+ }
5188
+ const ct = /* @__PURE__ */ new WeakMap();
5189
+ function Gf(t, e, i) {
5190
+ const n = i?.frequency ?? 1.2, r = i?.minScale ?? 0.92, s = i?.maxScale ?? 1.08;
5191
+ return {
5192
+ stateId: t,
5193
+ type: e,
5194
+ onEnter(o) {
5195
+ const a = o.getGraphics();
5196
+ ct.set(o, { elapsed: 0, origScaleX: a.scale.x, origScaleY: a.scale.y });
5197
+ },
5198
+ onLeave(o) {
5199
+ const a = ct.get(o);
5200
+ a && (o.getGraphics().scale.set(a.origScaleX, a.origScaleY), ct.delete(o));
5201
+ },
5202
+ onUpdate(o, a) {
5203
+ const c = ct.get(o);
5204
+ if (!c) return;
5205
+ c.elapsed += a;
5206
+ const l = c.elapsed * n / 1e3 % 1, h = Math.pow(Math.abs(Math.sin(l * Math.PI)), 0.6), u = r + h * (s - r);
5207
+ o.getGraphics().scale.set(c.origScaleX * u, c.origScaleY * u);
5181
5208
  }
5182
5209
  };
5183
5210
  }
5184
- const Wc = (
5211
+ const Bc = (
5185
5212
  /* glsl */
5186
5213
  `
5187
5214
  in vec2 aPosition;
@@ -5210,7 +5237,7 @@ void main(void)
5210
5237
  vTextureCoord = filterTextureCoord();
5211
5238
  }
5212
5239
  `
5213
- ), jc = (
5240
+ ), Wc = (
5214
5241
  /* glsl */
5215
5242
  `
5216
5243
  precision highp float;
@@ -5219,150 +5246,176 @@ in vec2 vTextureCoord;
5219
5246
  out vec4 finalColor;
5220
5247
 
5221
5248
  uniform sampler2D uTexture;
5222
- uniform float uAmount;
5249
+ uniform float uPhase; // 光带中心位置(投影轴坐标)
5250
+ uniform float uWidth; // 光带宽度(投影轴空间)
5251
+ uniform float uIntensity; // 白光峰值强度 0-1
5252
+ uniform float uCosA; // cos(angle)
5253
+ uniform float uSinA; // sin(angle)
5223
5254
 
5224
5255
  void main(void)
5225
5256
  {
5226
5257
  vec4 original = texture(uTexture, vTextureCoord);
5227
5258
 
5228
- // 反色
5229
- vec3 inverted = 1.0 - original.rgb;
5259
+ // 将纹理坐标投影到扫光方向上
5260
+ float pos = vTextureCoord.x * uCosA + vTextureCoord.y * uSinA;
5230
5261
 
5231
- // amount 在原色和反色之间插值
5232
- vec3 result = mix(original.rgb, inverted, uAmount);
5262
+ // 计算到光带中心的距离,smooth 衰减
5263
+ float dist = abs(pos - uPhase);
5264
+ float halfW = uWidth * 0.5;
5265
+ float t = 1.0 - smoothstep(0.0, halfW, dist);
5233
5266
 
5234
- finalColor = vec4(result, original.a);
5267
+ // 仅在不透明区域叠加白光(premultiplied-alpha)
5268
+ float add = t * uIntensity * original.a;
5269
+ vec3 newRgb = min(original.rgb + vec3(add), vec3(original.a));
5270
+
5271
+ finalColor = vec4(newRgb, original.a);
5235
5272
  }
5236
5273
  `
5237
5274
  );
5238
- class Vc extends G {
5275
+ class jc extends G {
5276
+ _angle;
5239
5277
  constructor(e = {}) {
5240
- const i = e.amount ?? 1, n = U.from({
5241
- vertex: Wc,
5242
- fragment: jc,
5243
- name: "invert-filter"
5278
+ const i = e.width ?? 0.35, n = e.intensity ?? 0.6, r = e.angle ?? Math.PI / 6, s = U.from({
5279
+ vertex: Bc,
5280
+ fragment: Wc,
5281
+ name: "shimmer-filter"
5244
5282
  });
5245
5283
  super({
5246
- glProgram: n,
5284
+ glProgram: s,
5247
5285
  resources: {
5248
- invertUniforms: new H({
5249
- uAmount: { value: i, type: "f32" }
5286
+ shimmerUniforms: new H({
5287
+ uPhase: { value: 0, type: "f32" },
5288
+ uWidth: { value: i, type: "f32" },
5289
+ uIntensity: { value: n, type: "f32" },
5290
+ uCosA: { value: Math.cos(r), type: "f32" },
5291
+ uSinA: { value: Math.sin(r), type: "f32" }
5250
5292
  })
5251
5293
  }
5252
- });
5294
+ }), this._angle = r;
5253
5295
  }
5254
5296
  /* ---------- 便捷存取器 ---------- */
5255
- get amount() {
5256
- return this.resources.invertUniforms.uniforms.uAmount;
5297
+ get phase() {
5298
+ return this.resources.shimmerUniforms.uniforms.uPhase;
5257
5299
  }
5258
- set amount(e) {
5259
- this.resources.invertUniforms.uniforms.uAmount = e;
5300
+ set phase(e) {
5301
+ this.resources.shimmerUniforms.uniforms.uPhase = e;
5302
+ }
5303
+ get width() {
5304
+ return this.resources.shimmerUniforms.uniforms.uWidth;
5305
+ }
5306
+ set width(e) {
5307
+ this.resources.shimmerUniforms.uniforms.uWidth = e;
5308
+ }
5309
+ get intensity() {
5310
+ return this.resources.shimmerUniforms.uniforms.uIntensity;
5311
+ }
5312
+ set intensity(e) {
5313
+ this.resources.shimmerUniforms.uniforms.uIntensity = e;
5314
+ }
5315
+ get angle() {
5316
+ return this._angle;
5317
+ }
5318
+ set angle(e) {
5319
+ this._angle = e, this.resources.shimmerUniforms.uniforms.uCosA = Math.cos(e), this.resources.shimmerUniforms.uniforms.uSinA = Math.sin(e);
5260
5320
  }
5261
5321
  }
5262
- function Hf(t, e) {
5263
- const i = e?.amount ?? 1;
5322
+ const lt = /* @__PURE__ */ new WeakMap();
5323
+ function Uf(t, e, i) {
5324
+ const n = i?.frequency ?? 0.8, r = i?.width ?? 0.35, s = i?.intensity ?? 0.6, o = i?.angle ?? Math.PI / 6;
5264
5325
  return {
5265
- postProcessId: t,
5266
- createFilter() {
5267
- return new Vc({ amount: i });
5326
+ stateId: t,
5327
+ type: e,
5328
+ onEnter(a) {
5329
+ const c = new jc({ width: r, intensity: s, angle: o }), l = a.getGraphics();
5330
+ Rt(a), l.filters = [...l.filters ?? [], c];
5331
+ const h = Math.abs(Math.cos(o)) + Math.abs(Math.sin(o));
5332
+ lt.set(a, { elapsed: 0, filter: c, maxProj: h });
5268
5333
  },
5269
- setOptions(n, r) {
5270
- const s = n;
5271
- r.amount !== void 0 && (s.amount = r.amount);
5334
+ onLeave(a) {
5335
+ const c = lt.get(a);
5336
+ if (c) {
5337
+ const l = a.getGraphics();
5338
+ l.filters = (l.filters ?? []).filter((h) => h !== c.filter), Dt(a), lt.delete(a);
5339
+ }
5340
+ },
5341
+ onUpdate(a, c) {
5342
+ const l = lt.get(a);
5343
+ if (!l) return;
5344
+ l.elapsed += c;
5345
+ const h = l.elapsed * n / 1e3 % 1, u = r * 0.5, f = l.maxProj + r;
5346
+ l.filter.phase = h * f - u;
5272
5347
  }
5273
5348
  };
5274
5349
  }
5275
- class Oi extends le {
5276
- /** 额外 padding:覆盖 BlurFilter spread + 变换舍入误差(与 states/_filter-utils 同理) */
5277
- static FILTER_AREA_PAD = 80;
5278
- items = /* @__PURE__ */ new Map();
5279
- /** postProcessId → 插件定义 */
5280
- defs = /* @__PURE__ */ new Map();
5281
- /** postProcessId → 运行时 Filter 实例 */
5282
- filters = /* @__PURE__ */ new Map();
5283
- /** 当前绑定的目标容器 */
5284
- _target = null;
5285
- /** PixiJS Application 引用,用于获取 screen 尺寸 */
5286
- _app = null;
5287
- /** 绑定到目标容器(projectionContainer),启用后滤镜自动挂载 */
5288
- bind(e, i) {
5289
- this._target = e, this._app = i, this.rebuildFilters();
5290
- }
5291
- /** 解绑目标容器,清除所有已挂载的滤镜 */
5292
- unbind() {
5293
- this._target && (this.clearFilters(), this._target.filterArea = void 0, this._target = null, this._app = null);
5294
- }
5295
- /** 添加或批量添加后期处理项 */
5350
+ const Xt = /* @__PURE__ */ new WeakMap();
5351
+ function Hf(t, e, i) {
5352
+ const n = i?.color ?? 16711680;
5353
+ return {
5354
+ stateId: t,
5355
+ type: e,
5356
+ onEnter(r) {
5357
+ const s = r.getGraphics();
5358
+ Xt.set(r, s.tint), s.tint = n;
5359
+ },
5360
+ onLeave(r) {
5361
+ const s = Xt.get(r);
5362
+ s !== void 0 && (r.getGraphics().tint = s, Xt.delete(r));
5363
+ }
5364
+ };
5365
+ }
5366
+ class Vc extends le {
5367
+ states = /* @__PURE__ */ new Map();
5368
+ /** stateId 该状态下所有的插件定义 */
5369
+ plugins = /* @__PURE__ */ new Map();
5370
+ /** 添加或批量添加状态 */
5296
5371
  add(e) {
5297
5372
  for (const i of Array.isArray(e) ? e : [e]) {
5298
- i.enabled === void 0 && (i.enabled = !0);
5299
- const n = this.items.has(i.id);
5300
- this.items.set(i.id, i), this.emit(n ? "postprocess:updated" : "postprocess:added", i);
5373
+ const n = this.states.has(i.id);
5374
+ this.states.set(i.id, i), this.emit(n ? "state:updated" : "state:added", i);
5301
5375
  }
5302
- return this.rebuildFilters(), this;
5376
+ return this;
5303
5377
  }
5304
- /** 删除后期处理项(同时清除关联的插件和滤镜) */
5378
+ /** 删除状态(同时清除关联的插件) */
5305
5379
  remove(e) {
5306
- return this.items.delete(e) ? (this.destroyFilter(e), this.defs.delete(e), this.emit("postprocess:removed", { id: e }), this.rebuildFilters(), !0) : !1;
5380
+ return this.states.delete(e) ? (this.plugins.delete(e), this.emit("state:removed", { id: e }), !0) : !1;
5307
5381
  }
5308
5382
  get(e) {
5309
- return this.items.get(e);
5383
+ return this.states.get(e);
5310
5384
  }
5311
5385
  has(e) {
5312
- return this.items.has(e);
5386
+ return this.states.has(e);
5313
5387
  }
5314
5388
  getAll() {
5315
- return [...this.items.values()];
5389
+ return [...this.states.values()];
5316
5390
  }
5317
5391
  clear() {
5318
- this.clearFilters(), this.items.clear(), this.defs.clear(), this.emit("postprocess:cleared");
5319
- }
5320
- /** 启用后期处理效果 */
5321
- enable(e) {
5322
- const i = this.items.get(e);
5323
- return i && !i.enabled && (i.enabled = !0, this.emit("postprocess:enabled", { id: e }), this.rebuildFilters()), this;
5392
+ this.states.clear(), this.plugins.clear(), this.emit("states:cleared");
5324
5393
  }
5325
- /** 禁用后期处理效果 */
5326
- disable(e) {
5327
- const i = this.items.get(e);
5328
- return i && i.enabled !== !1 && (i.enabled = !1, this.emit("postprocess:disabled", { id: e }), this.rebuildFilters()), this;
5394
+ /** 注册状态插件 */
5395
+ registerPlugin(e) {
5396
+ const i = this.plugins.get(e.stateId) ?? [];
5397
+ return i.push(e), this.plugins.set(e.stateId, i), this;
5329
5398
  }
5330
- /** 注册后期处理插件 */
5331
- register(e) {
5332
- return this.defs.set(e.postProcessId, e), this.rebuildFilters(), this;
5399
+ /** 注销指定 stateId + type 的所有插件 */
5400
+ unregisterPlugin(e, i) {
5401
+ const n = this.plugins.get(e);
5402
+ if (n) {
5403
+ const r = n.filter((s) => s.type !== i);
5404
+ r.length ? this.plugins.set(e, r) : this.plugins.delete(e);
5405
+ }
5406
+ return this;
5333
5407
  }
5334
- /** 注销后期处理插件 */
5335
- unregister(e) {
5336
- return this.destroyFilter(e), this.defs.delete(e), this.rebuildFilters(), this;
5408
+ /** 获取匹配 stateId + type 的插件列表 */
5409
+ getPlugins(e, i) {
5410
+ return (this.plugins.get(e) ?? []).filter((n) => n.type === i);
5337
5411
  }
5338
- /** 获取后期处理权重 */
5412
+ /** 获取状态权重,未定义时为 0 */
5339
5413
  getWeight(e) {
5340
- return this.items.get(e)?.weight ?? 0;
5341
- }
5342
- /** 获取运行时 Filter 实例(供调试用) */
5343
- getFilter(e) {
5344
- return this.filters.get(e);
5345
- }
5346
- /**
5347
- * 热更新后期处理参数,直接修改运行时 Filter 的 uniform,无需销毁重建。
5348
- * 仅当对应 PostProcessDef 实现了 setOptions 时生效。
5349
- * 不支持热更新的参数(如 bloom 的 samples)需重新调用 register 重建。
5350
- */
5351
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
5352
- updateOptions(e, i) {
5353
- const n = this.filters.get(e), r = this.defs.get(e);
5354
- return n && r?.setOptions && r.setOptions(n, i), this;
5414
+ return this.states.get(e)?.weight ?? 0;
5355
5415
  }
5356
- /** 逐帧更新(由 Scene ticker 驱动) */
5357
- update(e) {
5358
- this._target && this._app && this._target.filters?.length && (this._target.filterArea = this.computeFilterArea());
5359
- for (const [i, n] of this.defs) {
5360
- if (!n.onUpdate) continue;
5361
- const r = this.items.get(i);
5362
- if (!r || r.enabled === !1) continue;
5363
- const s = this.filters.get(i);
5364
- s && n.onUpdate(s, e);
5365
- }
5416
+ /** 按权重升序排列 stateId 列表 */
5417
+ sortByWeight(e) {
5418
+ return [...e].sort((i, n) => this.getWeight(i) - this.getWeight(n));
5366
5419
  }
5367
5420
  /** 序列化(仅元数据,插件为运行时代码不参与序列化) */
5368
5421
  toJSON() {
@@ -5379,64 +5432,13 @@ class Oi extends le {
5379
5432
  this.fromJSON(Ce(e));
5380
5433
  }
5381
5434
  destroy() {
5382
- this.clearFilters(), this.items.clear(), this.defs.clear(), this._target = null, this._app = null, this.removeAllListeners();
5383
- }
5384
- // ========== 内部方法 ==========
5385
- /**
5386
- * 计算 filterArea(projectionContainer 局部坐标空间),参考 states/_filter-utils。
5387
- *
5388
- * 将视口四角通过 worldTransform 逆变换到容器局部空间,
5389
- * 加上 pad 以确保 BlurFilter 等带扩展的滤镜不被裁剪。
5390
- *
5391
- * **filterArea 走的是独立且正确的路径:**
5392
- * ```
5393
- * filterArea(local)
5394
- * × container.worldTransform → world
5395
- * × renderGroup.cacheToLocalTransform → render group local ✓
5396
- * ```
5397
- */
5398
- computeFilterArea() {
5399
- const e = this._app.screen, i = this._target.worldTransform.clone().invert(), n = i.apply({ x: e.x, y: e.y }), r = i.apply({ x: e.x + e.width, y: e.y }), s = i.apply({ x: e.x + e.width, y: e.y + e.height }), o = i.apply({ x: e.x, y: e.y + e.height }), a = Oi.FILTER_AREA_PAD, c = Math.min(n.x, r.x, s.x, o.x) - a, l = Math.min(n.y, r.y, s.y, o.y) - a, h = Math.max(n.x, r.x, s.x, o.x) + a, u = Math.max(n.y, r.y, s.y, o.y) + a;
5400
- return new Lt(c, l, h - c, u - l);
5401
- }
5402
- /** 按权重升序排列,返回有效(已注册 + 已启用)的 ID 列表 */
5403
- getActiveIds() {
5404
- const e = [];
5405
- for (const [i, n] of this.items)
5406
- n.enabled !== !1 && this.defs.has(i) && e.push(i);
5407
- return e.sort((i, n) => this.getWeight(i) - this.getWeight(n));
5408
- }
5409
- /** 重建 filters 数组并挂载到目标容器 */
5410
- rebuildFilters() {
5411
- if (!this._target) return;
5412
- const e = this.getActiveIds();
5413
- for (const [n, r] of this.filters)
5414
- e.includes(n) || this.destroyFilter(n);
5415
- for (const n of e)
5416
- if (!this.filters.has(n)) {
5417
- const r = this.defs.get(n);
5418
- this.filters.set(n, r.createFilter());
5419
- }
5420
- const i = e.map((n) => this.filters.get(n)).filter(Boolean);
5421
- i.length > 0 ? (this._target.filterArea = this.computeFilterArea(), this._target.filters = i) : (this._target.filterArea = void 0, this._target.filters = null);
5422
- }
5423
- /** 销毁单个滤镜实例 */
5424
- destroyFilter(e) {
5425
- const i = this.filters.get(e);
5426
- i && (this.defs.get(e)?.onDestroy?.(i), i.destroy(), this.filters.delete(e));
5427
- }
5428
- /** 清除所有运行时滤镜 */
5429
- clearFilters() {
5430
- for (const e of [...this.filters.keys()])
5431
- this.destroyFilter(e);
5432
- this._target && (this._target.filters = null);
5435
+ this.clear(), this.removeAllListeners();
5433
5436
  }
5434
5437
  }
5435
5438
  class _r extends le {
5436
5439
  _app;
5437
- icons = new lc();
5438
- states = new vc();
5439
- postprocess = new Oi();
5440
+ icons = new $c();
5441
+ states = new Vc();
5440
5442
  scenes = /* @__PURE__ */ new Map();
5441
5443
  _activeSceneId = null;
5442
5444
  _isReady = !1;
@@ -5469,7 +5471,7 @@ class _r extends le {
5469
5471
  width: n ?? i.clientWidth,
5470
5472
  height: r ?? i.clientHeight,
5471
5473
  antialias: !0
5472
- }), i.appendChild(this._app.canvas), this._app.stage.eventMode = "static", this._app.stage.hitArea = this._app.screen, this._isReady = !0, this.use(new cc()), this.emit("ready");
5474
+ }), i.appendChild(this._app.canvas), this._app.stage.eventMode = "static", this._app.stage.hitArea = this._app.screen, this._isReady = !0, this.use(new Nc()), this.emit("ready");
5473
5475
  }
5474
5476
  /** 静态工厂方法,创建并初始化 Topology 实例 */
5475
5477
  static async create(e) {
@@ -5484,7 +5486,7 @@ class _r extends le {
5484
5486
  /** 添加场景,首个场景自动激活 */
5485
5487
  addScene(e, i) {
5486
5488
  if (this.ensureReady(), this.scenes.has(e)) throw new Error(`Scene "${e}" already exists`);
5487
- const n = new De(e, this.app, this.icons, this.states, this.postprocess);
5489
+ const n = new De(e, this.app, this.icons, this.states);
5488
5490
  return this.scenes.set(e, n), i && n.fromJSON(i), !this._isBatchLoading && this.scenes.size === 1 ? this.switchScene(e) : n.setActive(!1), this.emit("scene:added", n), n;
5489
5491
  }
5490
5492
  removeScene(e) {
@@ -5560,13 +5562,12 @@ class _r extends le {
5560
5562
  scenes: e,
5561
5563
  icons: this.icons.toJSON(),
5562
5564
  states: this.states.toJSON(),
5563
- postprocess: this.postprocess.toJSON(),
5564
5565
  _activeSceneId: this._activeSceneId ?? void 0
5565
5566
  };
5566
5567
  }
5567
5568
  fromJSON(e) {
5568
5569
  for (const n of [...this.scenes.keys()]) this.removeScene(n);
5569
- e.icons && this.icons.fromJSON(e.icons), e.states && this.states.fromJSON(e.states), e.postprocess && this.postprocess.fromJSON(e.postprocess), this._isBatchLoading = !0;
5570
+ e.icons && this.icons.fromJSON(e.icons), e.states && this.states.fromJSON(e.states), this._isBatchLoading = !0;
5570
5571
  for (const [n, r] of Object.entries(e.scenes))
5571
5572
  this.addScene(n, r);
5572
5573
  this._isBatchLoading = !1;
@@ -5665,7 +5666,7 @@ class _r extends le {
5665
5666
  e.uninstall(this);
5666
5667
  this.plugins.clear();
5667
5668
  for (const e of [...this.scenes.keys()]) this.removeScene(e);
5668
- this.icons.clear(), this.states.clear(), this.postprocess.destroy(), ka(), this.emit("destroyed"), this.removeAllListeners(), this.app?.destroy(!0), this._isReady = !1;
5669
+ this.icons.clear(), this.states.clear(), ka(), this.emit("destroyed"), this.removeAllListeners(), this.app?.destroy(!0), this._isReady = !1;
5669
5670
  }
5670
5671
  }
5671
5672
  class Pe extends Oe {
@@ -7450,7 +7451,7 @@ function ff(t) {
7450
7451
  }
7451
7452
  nt.prototype.interrupt = Gh;
7452
7453
  nt.prototype.transition = ff;
7453
- class Yf extends Ti {
7454
+ class Yf extends Oi {
7454
7455
  name = "viewport";
7455
7456
  minScale;
7456
7457
  maxScale;
@@ -8408,7 +8409,7 @@ class od extends Pe {
8408
8409
  return e.id;
8409
8410
  }
8410
8411
  }
8411
- class Me extends Ti {
8412
+ class Me extends Oi {
8412
8413
  _isActive = !1;
8413
8414
  _opts;
8414
8415
  _prevDragEnabled = null;
@@ -8905,7 +8906,7 @@ class dd extends Me {
8905
8906
  }
8906
8907
  export {
8907
8908
  Re as AZIMUTH,
8908
- Ti as ActiveScenePlugin,
8909
+ Oi as ActiveScenePlugin,
8909
8910
  Ii as BILLBOARD_SCALE_X,
8910
8911
  D as BILLBOARD_SCALE_Y,
8911
8912
  bi as BILLBOARD_SKEW_X,
@@ -8918,10 +8919,10 @@ export {
8918
8919
  Kf as FenceEndpointPlugin,
8919
8920
  sd as FenceRuntimePlugin,
8920
8921
  Xa as GroupElement,
8921
- cc as HitTestPlugin,
8922
+ Nc as HitTestPlugin,
8922
8923
  ur as ISOMETRIC_SKEW_X,
8923
8924
  fr as ISOMETRIC_SKEW_Y,
8924
- lc as IconManager,
8925
+ $c as IconManager,
8925
8926
  dd as ImageCreatorPlugin,
8926
8927
  qf as ImageEditorPlugin,
8927
8928
  rd as ImageRuntimePlugin,
@@ -8933,7 +8934,7 @@ export {
8933
8934
  hd as NodeCreatorPlugin,
8934
8935
  Bf as NodeEditorPlugin,
8935
8936
  ed as NodeRuntimePlugin,
8936
- Oi as PostProcessManager,
8937
+ Ti as PostProcessManager,
8937
8938
  cd as RectCreatorPlugin,
8938
8939
  Vf as RectDragPlugin,
8939
8940
  Xf as RectResizePlugin,
@@ -8943,7 +8944,7 @@ export {
8943
8944
  Oe as SceneAwarePlugin,
8944
8945
  Ue as SelectableEditorPlugin,
8945
8946
  qc as SelectionHighlight,
8946
- vc as StateManager,
8947
+ Vc as StateManager,
8947
8948
  xf as TILE_HDIAGONAL,
8948
8949
  vf as TILE_HEIGHT,
8949
8950
  g as TILE_SIZE,
@@ -8961,26 +8962,26 @@ export {
8961
8962
  md as ZoomTransform,
8962
8963
  lr as calculateIsometricSkew,
8963
8964
  yf as calculateIsometricTileDiagonals,
8964
- bf as createBlinkStatePlugin,
8965
- Lf as createBloomPostProcess,
8966
- Ef as createBlurPostProcess,
8967
- Cf as createBounceStatePlugin,
8968
- If as createBrightenStatePlugin,
8969
- zf as createChromaticAberrationPostProcess,
8970
- kf as createColorAdjustPostProcess,
8971
- Af as createDarkenStatePlugin,
8972
- Tf as createGlowStatePlugin,
8973
- $f as createGodRaysPostProcess,
8974
- Gf as createGrayscalePostProcess,
8975
- Of as createHeartbeatStatePlugin,
8976
- Hf as createInvertPostProcess,
8977
- Df as createNoisePostProcess,
8978
- Rf as createPixelatePostProcess,
8979
- Nf as createScanlinesPostProcess,
8980
- Uf as createSharpenPostProcess,
8981
- Pf as createShimmerStatePlugin,
8982
- Mf as createTintStatePlugin,
8983
- Ff as createVignettePostProcess,
8965
+ Rf as createBlinkStatePlugin,
8966
+ Cf as createBloomPostProcess,
8967
+ bf as createBlurPostProcess,
8968
+ Df as createBounceStatePlugin,
8969
+ zf as createBrightenStatePlugin,
8970
+ Pf as createChromaticAberrationPostProcess,
8971
+ If as createColorAdjustPostProcess,
8972
+ Nf as createDarkenStatePlugin,
8973
+ $f as createGlowStatePlugin,
8974
+ Ef as createGodRaysPostProcess,
8975
+ Lf as createGrayscalePostProcess,
8976
+ Gf as createHeartbeatStatePlugin,
8977
+ Ff as createInvertPostProcess,
8978
+ Of as createNoisePostProcess,
8979
+ Tf as createPixelatePostProcess,
8980
+ Mf as createScanlinesPostProcess,
8981
+ kf as createSharpenPostProcess,
8982
+ Uf as createShimmerStatePlugin,
8983
+ Hf as createTintStatePlugin,
8984
+ Af as createVignettePostProcess,
8984
8985
  _r as default,
8985
8986
  It as downloadFile,
8986
8987
  ba as findPath,