@nubisco/ui 1.43.0 → 1.45.0
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.
- package/dist/components/Blueprint.renderer.d.ts +12 -0
- package/dist/components/Blueprint.renderer.d.ts.map +1 -1
- package/dist/components/Blueprint.types.d.ts +9 -0
- package/dist/components/Blueprint.types.d.ts.map +1 -1
- package/dist/components/Blueprint.vue.d.ts +1 -1
- package/dist/components/Blueprint.vue.d.ts.map +1 -1
- package/dist/components/BlueprintCard.vue.d.ts.map +1 -1
- package/dist/components/BlueprintPixiRenderer.vue.d.ts.map +1 -1
- package/dist/components/Grid.vue.d.ts +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/blueprint-pixi/pixi-scene.d.ts +31 -2
- package/dist/components/blueprint-pixi/pixi-scene.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +38 -25
- package/dist/index.mjs.map +1 -1
- package/dist/{pixi-scene-BKcn6pWT.js → pixi-scene-CK6-CaUQ.js} +65 -10
- package/dist/pixi-scene-CK6-CaUQ.js.map +1 -0
- package/dist/pixi-scene-CmsiMPkU.cjs +2 -0
- package/dist/pixi-scene-CmsiMPkU.cjs.map +1 -0
- package/dist/ui.css +1 -1
- package/package.json +1 -1
- package/dist/pixi-scene-BKcn6pWT.js.map +0 -1
- package/dist/pixi-scene-D8kAPyID.cjs +0 -2
- package/dist/pixi-scene-D8kAPyID.cjs.map +0 -1
|
@@ -62,7 +62,7 @@ function a(e) {
|
|
|
62
62
|
}
|
|
63
63
|
//#endregion
|
|
64
64
|
//#region src/components/blueprint-pixi/pixi-scene.ts
|
|
65
|
-
var o = 24, s = .45, c = 2.5, l = .
|
|
65
|
+
var o = 24, s = .45, c = 2.5, l = .004, u = .4, d = 40, f = class e {
|
|
66
66
|
app;
|
|
67
67
|
world;
|
|
68
68
|
grid;
|
|
@@ -84,6 +84,8 @@ var o = 24, s = .45, c = 2.5, l = .4, u = 40, d = class e {
|
|
|
84
84
|
needsRender = !1;
|
|
85
85
|
rafId = 0;
|
|
86
86
|
lastAnimMs = 0;
|
|
87
|
+
colorByLevel = !1;
|
|
88
|
+
activityStyle = "flow";
|
|
87
89
|
liveData;
|
|
88
90
|
levelsDirty = !1;
|
|
89
91
|
pendingWires = [];
|
|
@@ -160,13 +162,14 @@ var o = 24, s = .45, c = 2.5, l = .4, u = 40, d = class e {
|
|
|
160
162
|
updateGridAlpha(e) {
|
|
161
163
|
if (this.background === "none") return;
|
|
162
164
|
let t = o * e, n = Math.max(0, Math.min(1, (t - 6) / 10));
|
|
163
|
-
this.grid.alpha =
|
|
165
|
+
this.grid.alpha = u * n;
|
|
164
166
|
}
|
|
165
167
|
setWires(e, t) {
|
|
166
168
|
this.destroyed || (this.pendingWires = e, this.pendingShouldFlow = t, this.wiresDirty = !0, this.ensureRaf());
|
|
167
169
|
}
|
|
168
170
|
reconcileWireGeometry() {
|
|
169
171
|
this.wiresDirty = !1, this.wiresDrawnOnce = !0;
|
|
172
|
+
for (let e of this.wireNodes.values()) e.vibrating &&= (this.strokeBase(e), !1);
|
|
170
173
|
let e = /* @__PURE__ */ new Set(), t = [];
|
|
171
174
|
for (let n of this.pendingWires) {
|
|
172
175
|
let i = this.bezierCache.get(n.path);
|
|
@@ -200,12 +203,18 @@ var o = 24, s = .45, c = 2.5, l = .4, u = 40, d = class e {
|
|
|
200
203
|
}
|
|
201
204
|
}
|
|
202
205
|
wireColorFor(e) {
|
|
203
|
-
if (this.liveData && !e.isMidi && !e.dim) {
|
|
206
|
+
if (this.colorByLevel && this.liveData && !e.isMidi && !e.dim) {
|
|
204
207
|
let t = this.liveData.get(e.key);
|
|
205
208
|
if (t > 0) return a(t);
|
|
206
209
|
}
|
|
207
210
|
return e.baseColor;
|
|
208
211
|
}
|
|
212
|
+
setLevelColoring(e) {
|
|
213
|
+
this.destroyed || this.colorByLevel === e || (this.colorByLevel = e, this.levelsDirty = !0, this.ensureRaf());
|
|
214
|
+
}
|
|
215
|
+
setActivityStyle(e) {
|
|
216
|
+
this.destroyed || this.activityStyle === e || (this.activityStyle = e, this.clearFlow(), this.wiresDrawnOnce = !1, this.wiresDirty = !0, this.ensureRaf());
|
|
217
|
+
}
|
|
209
218
|
applyWireColors() {
|
|
210
219
|
for (let e of this.wireNodes.values()) e.gfx.tint = this.wireColorFor(e);
|
|
211
220
|
}
|
|
@@ -269,21 +278,67 @@ var o = 24, s = .45, c = 2.5, l = .4, u = 40, d = class e {
|
|
|
269
278
|
if (this.rafId = 0, this.destroyed) return;
|
|
270
279
|
let t = this.needsRender;
|
|
271
280
|
this.needsRender = !1;
|
|
272
|
-
let n = e - this.lastAnimMs >=
|
|
281
|
+
let n = e - this.lastAnimMs >= d, r = this.wiresDirty && (!this.wiresDrawnOnce || n), i = r || this.levelsDirty && n;
|
|
273
282
|
if (r && this.reconcileWireGeometry(), this.metersDirty && (this.reconcileMeters(), this.updateMeterValues(), t = !0), i && (this.levelsDirty = !1, this.applyWireColors(), this.updateMeterValues(), t = !0), this.flowNodes.length && n) {
|
|
274
|
-
let n = this.lastAnimMs ? e - this.lastAnimMs :
|
|
275
|
-
this.flowPhase = (this.flowPhase + n / 1e3 * s) % 1, this.drawFlow(), t = !0;
|
|
283
|
+
let n = this.lastAnimMs ? e - this.lastAnimMs : d;
|
|
284
|
+
this.flowPhase = (this.flowPhase + n / 1e3 * s) % 1, this.activityStyle === "pulse" ? this.drawPulse() : this.activityStyle === "vibrate" ? this.drawVibrate() : this.drawFlow(), t = !0;
|
|
276
285
|
}
|
|
277
286
|
n && (this.lastAnimMs = e), t && this.app.render(), (this.needsRender || this.wiresDirty || this.levelsDirty || this.metersDirty || this.flowNodes.length) && this.ensureRaf();
|
|
278
287
|
};
|
|
279
288
|
drawFlow() {
|
|
280
289
|
let e = c / Math.max(this.zoom, .01) / 8, t = 0;
|
|
281
290
|
for (let n of this.flowNodes) {
|
|
282
|
-
|
|
283
|
-
|
|
291
|
+
if ((this.liveData ? this.liveData.get(n.key) : 0) <= l) continue;
|
|
292
|
+
let r = this.wireColorFor(n);
|
|
293
|
+
for (let a = 0; a < 5; a++) {
|
|
294
|
+
let o = this.flowPhase - a * .06;
|
|
295
|
+
o < 0 && (o += 1);
|
|
296
|
+
let [s, c] = i(n.bezier, o), l = this.flowSprites[t];
|
|
297
|
+
l || (l = new this.Pixi.Sprite(this.dotTexture), l.anchor.set(.5), this.flowLayer.addChild(l), this.flowSprites[t] = l), l.visible = !0, l.position.set(s, c), l.scale.set(e * (1 - a * .12)), l.alpha = .9 * (1 - a / 5), l.tint = r, t++;
|
|
298
|
+
}
|
|
284
299
|
}
|
|
285
300
|
for (; t < this.flowSprites.length; t++) this.flowSprites[t].visible = !1;
|
|
286
301
|
}
|
|
302
|
+
drawPulse() {
|
|
303
|
+
let e = .4 + .6 * Math.abs(Math.sin(this.flowPhase * Math.PI * 2));
|
|
304
|
+
for (let t of this.flowNodes) {
|
|
305
|
+
let n = this.liveData ? this.liveData.get(t.key) : 0;
|
|
306
|
+
t.gfx.alpha = n > l ? e : t.dim ? .25 : .55;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
drawVibrate() {
|
|
310
|
+
let e = 2.5 / Math.max(this.zoom, .01);
|
|
311
|
+
for (let t of this.flowNodes) {
|
|
312
|
+
if ((this.liveData ? this.liveData.get(t.key) : 0) <= l) {
|
|
313
|
+
t.vibrating &&= (this.strokeBase(t), !1);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
t.vibrating = !0, this.strokeVibrated(t, e);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
strokeBase(e) {
|
|
320
|
+
let t = e.bezier;
|
|
321
|
+
e.gfx.clear().moveTo(t.p0[0], t.p0[1]).bezierCurveTo(t.c1[0], t.c1[1], t.c2[0], t.c2[1], t.p3[0], t.p3[1]).stroke({
|
|
322
|
+
width: 1,
|
|
323
|
+
pixelLine: !0,
|
|
324
|
+
color: 16777215
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
strokeVibrated(e, t) {
|
|
328
|
+
let n = e.bezier, r = this.flowPhase * Math.PI * 2 * 6, a = e.gfx;
|
|
329
|
+
a.clear();
|
|
330
|
+
for (let e = 0; e <= 48; e++) {
|
|
331
|
+
let o = e / 48, [s, c] = i(n, o), [l, u] = i(n, Math.min(1, o + .005)), d = l - s, f = u - c, p = Math.hypot(d, f) || 1;
|
|
332
|
+
d /= p, f /= p;
|
|
333
|
+
let m = Math.sin(o * Math.PI), h = Math.sin(o * Math.PI * 12 + r) * t * m, g = s + -f * h, _ = c + d * h;
|
|
334
|
+
e === 0 ? a.moveTo(g, _) : a.lineTo(g, _);
|
|
335
|
+
}
|
|
336
|
+
a.stroke({
|
|
337
|
+
width: 1,
|
|
338
|
+
pixelLine: !0,
|
|
339
|
+
color: 16777215
|
|
340
|
+
});
|
|
341
|
+
}
|
|
287
342
|
clearFlow() {
|
|
288
343
|
for (let e of this.flowSprites) e.visible = !1;
|
|
289
344
|
}
|
|
@@ -295,6 +350,6 @@ var o = 24, s = .45, c = 2.5, l = .4, u = 40, d = class e {
|
|
|
295
350
|
}
|
|
296
351
|
};
|
|
297
352
|
//#endregion
|
|
298
|
-
export {
|
|
353
|
+
export { f as PixiScene };
|
|
299
354
|
|
|
300
|
-
//# sourceMappingURL=pixi-scene-
|
|
355
|
+
//# sourceMappingURL=pixi-scene-CK6-CaUQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixi-scene-CK6-CaUQ.js","names":[],"sources":["../src/components/blueprint-pixi/pixi-color.ts","../src/components/blueprint-pixi/wire-path.ts","../src/components/blueprint-pixi/level-color.ts","../src/components/blueprint-pixi/pixi-scene.ts"],"sourcesContent":["// CSS-color → PixiJS numeric color, with CSS custom-property resolution.\n//\n// Wire and card colors arrive as CSS color strings: concrete (`#38bdf8`,\n// `rgb(...)`) for resolved wires, but possibly a `var(--token)` for card\n// accents or the grid. PixiJS `Color` parses concrete strings but not\n// `var(...)`, so we resolve custom properties against a reference element's\n// computed style first, then hand the result to `Color`. Lookups are cached\n// because getComputedStyle is not free and the same tokens recur across\n// thousands of wires.\n\nimport type * as PIXI from 'pixi.js'\n\nexport type TColorResolver = (\n css: string | undefined,\n fallback: number,\n) => number\n\nconst VAR_RE = /var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^)]+))?\\)/\n\n/**\n * Build a resolver bound to a PixiJS module and a reference element (used to\n * resolve CSS custom properties). The returned function converts a CSS color\n * string to a 24-bit RGB number, returning `fallback` for empty/unparseable\n * input so a bad token never throws mid-render.\n */\nexport function makeColorResolver(\n Pixi: typeof PIXI,\n el: HTMLElement,\n): TColorResolver {\n const cache = new Map<string, number>()\n\n function resolveVar(css: string): string {\n const m = css.match(VAR_RE)\n if (!m) return css\n const token = m[1]!\n const fallbackExpr = m[2]?.trim()\n const styles = getComputedStyle(el)\n const value = styles.getPropertyValue(token).trim()\n if (value) return value\n // Nested var fallback (e.g. var(--a, var(--b, #fff))): recurse.\n if (fallbackExpr) return resolveVar(fallbackExpr)\n return css\n }\n\n return (css, fallback) => {\n if (!css) return fallback\n const hit = cache.get(css)\n if (hit !== undefined) return hit\n let out: number\n try {\n const resolved = css.includes('var(') ? resolveVar(css) : css\n out = new Pixi.Color(resolved).toNumber()\n } catch {\n out = fallback\n }\n cache.set(css, out)\n return out\n }\n}\n","// Parsing and sampling for the cubic-bezier wire paths NbBlueprint produces.\n//\n// NbBlueprint's `computedWires` emits each wire as an SVG path string of the\n// exact form `M fx fy C c1x c1y, c2x c2y, tx ty` (see Blueprint.vue). The\n// PixiJS renderer re-draws those same curves with `Graphics.bezierCurveTo`,\n// and the flow animation samples them into points. This module turns the\n// path string into structured control points and sampled polylines. It has\n// no PixiJS dependency so it stays in the always-loaded bundle and is unit\n// testable without a WebGL context.\n\nexport interface ICubicBezier {\n /** Start point [x, y]. */\n p0: [number, number]\n /** First control point. */\n c1: [number, number]\n /** Second control point. */\n c2: [number, number]\n /** End point. */\n p3: [number, number]\n}\n\nconst NUMBER_RE = /-?\\d*\\.?\\d+(?:e[-+]?\\d+)?/gi\n\n/**\n * Parse a `M x y C x y x y x y` cubic path string into control points.\n * Returns null if the string does not contain the expected 8 numbers (so a\n * malformed or non-cubic path is skipped rather than throwing).\n */\nexport function parseCubicPath(d: string): ICubicBezier | null {\n const m = d.match(NUMBER_RE)\n if (!m || m.length < 8) return null\n const n = m.slice(0, 8).map(Number)\n if (n.some((v) => Number.isNaN(v))) return null\n return {\n p0: [n[0]!, n[1]!],\n c1: [n[2]!, n[3]!],\n c2: [n[4]!, n[5]!],\n p3: [n[6]!, n[7]!],\n }\n}\n\n/** Evaluate a cubic bezier at parameter t in [0, 1]. */\nexport function cubicAt(b: ICubicBezier, t: number): [number, number] {\n const mt = 1 - t\n const a = mt * mt * mt\n const c = 3 * mt * mt * t\n const e = 3 * mt * t * t\n const f = t * t * t\n return [\n a * b.p0[0] + c * b.c1[0] + e * b.c2[0] + f * b.p3[0],\n a * b.p0[1] + c * b.c1[1] + e * b.c2[1] + f * b.p3[1],\n ]\n}\n\n/**\n * Sample a cubic bezier into `segments + 1` points. Used by the flow\n * animation to walk evenly-ish along the wire. `segments` is a fixed count\n * (not arc-length parameterised) which is plenty for a short connector.\n */\nexport function sampleCubic(\n b: ICubicBezier,\n segments: number,\n): [number, number][] {\n const pts: [number, number][] = []\n for (let i = 0; i <= segments; i++) pts.push(cubicAt(b, i / segments))\n return pts\n}\n","// Map an audio level (0..1) to a green -> yellow -> red colour as a PixiJS\n// numeric colour (0xRRGGBB). The same ramp the DOM path uses for wire levels,\n// but returned as a number so the GPU layer never parses a CSS string per\n// frame. Level <= 0.5 interpolates green -> yellow; above, yellow -> red.\nexport function levelToColorNumber(level: number): number {\n const l = Math.max(0, Math.min(1, level))\n const green = [34, 197, 94]\n const yellow = [250, 204, 21]\n const red = [239, 68, 68]\n let a: number[], b: number[], t: number\n if (l <= 0.5) {\n a = green\n b = yellow\n t = l * 2\n } else {\n a = yellow\n b = red\n t = (l - 0.5) * 2\n }\n const mix = (x: number, y: number): number => Math.round(x + (y - x) * t)\n return (\n (mix(a[0]!, b[0]!) << 16) | (mix(a[1]!, b[1]!) << 8) | mix(a[2]!, b[2]!)\n )\n}\n","// PixiJS scene manager for the Blueprint renderer.\n//\n// Owns the Application, a single panned/zoomed `world` Container (the camera),\n// an infinite dot-grid background, a batched wire layer, and an animated flow\n// layer. Cards are NOT drawn here: they stay real interactive DOM in the\n// overlay above this canvas at all times (no painted/snapshot substitute). This\n// scene only accelerates the grid + wires + flow on the GPU. The Vue component\n// (BlueprintPixiRenderer.vue) drives it with the culled scene NbBlueprint feeds\n// every renderer; this module has all the PixiJS specifics.\n//\n// PixiJS is passed in (not imported) so it stays a dynamically-loaded,\n// externalized optional peer dependency; only `import type` references reach\n// this module statically, and those are erased at build time.\n\nimport type * as PIXI from 'pixi.js'\nimport type { IBlueprintCard, TBlueprintBackground } from '../Blueprint.types'\nimport type { IBlueprintWire } from '../Blueprint.renderer'\nimport { makeColorResolver } from './pixi-color'\nimport { parseCubicPath, cubicAt, type ICubicBezier } from './wire-path'\nimport { levelToColorNumber } from './level-color'\nimport type { BlueprintLiveData } from './live-data'\n\nconst GRID_TILE = 24\nconst FLOW_SPEED = 0.45 // fraction of a wire traversed per second\nconst FLOW_DOT_R = 2.5\n// A flow candidate only animates when its live signal exceeds this (linear\n// peak, ~-48 dBFS): low enough to trace inaudible-but-present signal, high\n// enough to ignore the noise floor, so activity shows the real signal path and\n// visibly stops where the signal stops (muted / no-input / silent wires).\nconst ACTIVITY_THRESHOLD = 0.004\n// Wire/flow geometry lives in the zoomed `world`. A normal world-space stroke\n// shrinks on screen as you zoom out (wires vanish at far zoom); `pixelLine`\n// renders a crisp screen-space line instead, constant at every zoom, so the\n// wire layer is drawn once and never re-stroked as the camera moves.\nconst GRID_BASE_ALPHA = 0.4\n// This scene renders ON DEMAND: there is no free-running 60fps loop. A state\n// change (camera, wires, background) requests a single repaint, and when\n// nothing changes the scene renders nothing and costs ~0 CPU, which matters in\n// an audio host where the GPU/CPU budget belongs to DSP. Animated modes\n// (\"levels\"/flow) push updates as fast as ~60Hz; we coalesce those and repaint\n// at most this often, so an always-animating mode stays cheap. ~25fps reads as\n// smooth for level/flow motion at a fraction of the cost of 60.\nconst ANIM_FRAME_MS = 40\n\nexport interface IPixiSceneOptions {\n canvas: HTMLCanvasElement\n /** Reference element for resolving CSS custom properties (the container). */\n el: HTMLElement\n width: number\n height: number\n resolution: number\n background: TBlueprintBackground\n /** Non-reactive live values (wire levels, ...) the host writes at audio rate;\n * the scene reads them on its throttled render tick. */\n liveData?: BlueprintLiveData\n}\n\n// One meter's GPU geometry in world coords (resolved from a card + its local\n// meter box). Recomputed only when a card moves or the visible set changes.\ninterface IMeterGeom {\n id: string\n x: number\n y: number\n w: number\n h: number\n}\n\n// A drawn meter: a static dark track and an animated fill, plus a signature so\n// geometry is only re-laid-out when it actually changes.\ninterface IMeterNode {\n track: PIXI.Sprite\n fill: PIXI.Sprite\n sig: string\n}\n\n// One wire drawn once (geometry baked, stroked white) and recoloured by `tint`.\n// Animating the level colour is then a uniform change, NOT a re-tessellation,\n// which is what eliminates the per-frame GPU-geometry churn (and its GC spikes).\ninterface IWireNode {\n gfx: PIXI.Graphics\n path: string\n bezier: ICubicBezier\n baseColor: number\n isMidi: boolean\n dim: boolean\n flows: boolean\n key: string\n /** True while this wire's geometry is being re-stroked by the 'vibrate'\n * activity style, so it can be restored to the straight base stroke when it\n * stops carrying signal. */\n vibrating?: boolean\n}\n\n/** How the 'activity' wire mode animates a live wire. */\nexport type TWireActivityStyle = 'flow' | 'pulse' | 'vibrate'\n\nexport class PixiScene {\n private app: PIXI.Application\n private world!: PIXI.Container\n private grid!: PIXI.TilingSprite\n // Wire layer: a container of per-wire Graphics (see IWireNode). Flow layer: a\n // pool of reused dot sprites repositioned each frame (no re-tessellation).\n private wireLayer!: PIXI.Container\n private wireNodes = new Map<string, IWireNode>()\n private flowLayer!: PIXI.Container\n private flowSprites: PIXI.Sprite[] = []\n private dotTexture!: PIXI.Texture\n // Meter layer: each declared card meter is a static dark track + an animated\n // fill sprite (scale.y + tint from the live channel). Drawn over wires, on the\n // back canvas; the DOM card leaves a transparent hole so it shows through.\n private meterLayer!: PIXI.Container\n private meterNodes = new Map<string, IMeterNode>()\n private pendingMeters: IMeterGeom[] = []\n private metersDirty = false\n private trackColor = 0x16161e\n private resolveColor!: ReturnType<typeof makeColorResolver>\n // Fallback colour for a wire whose own colour does not resolve.\n private connectedColor = 0x6366f1\n private flowNodes: IWireNode[] = []\n private flowPhase = 0\n private zoom = 1\n // Render-on-demand scheduler. `needsRender` is the immediate path (camera,\n // background, resize). `rafId` holds the single in-flight frame; the loop\n // only stays alive while there is pending or ongoing (animation) work, then\n // stops so the scene costs nothing at rest. `lastAnimMs` paces the throttled\n // animation/wire-rebuild path.\n private needsRender = false\n private rafId = 0\n private lastAnimMs = 0\n // Colour wires by live level ('levels' mode) vs keep base colour. Distinct\n // from \"is live data present\", because 'activity' mode also streams levels\n // (to gate flow) but must NOT colour wires. See setLevelColoring.\n private colorByLevel = false\n // How 'activity' mode animates a live wire (flow comet / pulse / vibrate).\n private activityStyle: TWireActivityStyle = 'flow'\n // Live wire levels (host writes at audio rate, bypassing Vue). `levelsDirty`\n // is set when a write lands; the frame loop recolours wires on the throttled\n // animation cadence, then idles when the host stops writing.\n private liveData: BlueprintLiveData | undefined\n private levelsDirty = false\n // Coalesced wire state: setWires only stashes the latest set + marks dirty;\n // the frame loop rebuilds it (immediately the first time, then throttled to\n // ANIM_FRAME_MS). Parsed beziers are cached by path string so unchanged\n // geometry (levels mode only shifts colour) never pays the regex parse again.\n private pendingWires: IBlueprintWire[] = []\n private pendingShouldFlow: (conn: IBlueprintWire['conn']) => boolean = () =>\n false\n private wiresDirty = false\n private wiresDrawnOnce = false\n private bezierCache = new Map<string, ICubicBezier | null>()\n private destroyed = false\n private background: TBlueprintBackground = 'dots'\n private gridColor = 0x2a2a38\n private gridTexture!: PIXI.Texture\n\n private constructor(private readonly Pixi: typeof PIXI) {\n this.app = new Pixi.Application()\n }\n\n /** Create and initialise a scene. Async because PixiJS v8 `app.init` is. */\n static async create(\n Pixi: typeof PIXI,\n opts: IPixiSceneOptions,\n ): Promise<PixiScene> {\n const scene = new PixiScene(Pixi)\n await scene.init(opts)\n return scene\n }\n\n private async init(opts: IPixiSceneOptions): Promise<void> {\n await this.app.init({\n canvas: opts.canvas,\n width: opts.width,\n height: opts.height,\n backgroundAlpha: 0,\n antialias: true,\n resolution: opts.resolution,\n autoDensity: true,\n preference: 'webgl',\n // No free-running render loop: we drive app.render() on demand so an idle\n // blueprint costs ~0 CPU (the audio engine gets the budget).\n autoStart: false,\n })\n if (this.destroyed) {\n this.app.destroy({ removeView: false }, { children: true })\n return\n }\n\n this.resolveColor = makeColorResolver(this.Pixi, opts.el)\n this.connectedColor = this.resolveColor('var(--nb-c-primary)', 0x6366f1)\n\n this.liveData = opts.liveData\n if (this.liveData) {\n // A host write wakes the loop; the actual recolour is throttled in frame().\n this.liveData.onChange = () => {\n if (this.destroyed) return\n this.levelsDirty = true\n this.ensureRaf()\n }\n }\n\n this.background = opts.background\n this.gridColor = this.resolveColor(\n 'var(--nb-blueprint-grid-color, var(--nb-c-border))',\n 0x2a2a38,\n )\n this.grid = this.buildGrid(opts.width, opts.height)\n this.app.stage.addChild(this.grid)\n\n this.world = new this.Pixi.Container()\n this.app.stage.addChild(this.world)\n\n this.wireLayer = new this.Pixi.Container()\n this.meterLayer = new this.Pixi.Container()\n this.flowLayer = new this.Pixi.Container()\n // Order: wires (back) -> meters (over the card area) -> flow dots (top).\n this.world.addChild(this.wireLayer, this.meterLayer, this.flowLayer)\n this.dotTexture = this.makeDotTexture()\n this.trackColor = this.resolveColor('var(--nb-c-layer-2)', 0x16161e)\n // Stop Pixi's shared ticker for good measure; rendering is fully on-demand.\n this.app.ticker.stop()\n }\n\n private buildGrid(width: number, height: number): PIXI.TilingSprite {\n // A GRID_TILE-sized pattern tile, tiled across the screen. The grid lives\n // in stage (screen) space; panning/zooming maps to the tiling sprite's\n // tilePosition/tileScale (= zoom), which is far cheaper than redrawing a\n // world-space pattern every camera change.\n this.gridTexture = this.makeGridTexture(this.background)\n const sprite = new this.Pixi.TilingSprite({\n texture: this.gridTexture,\n width,\n height,\n })\n sprite.alpha = 0.4\n sprite.visible = this.background !== 'none'\n return sprite\n }\n\n private makeGridTexture(bg: TBlueprintBackground): PIXI.Texture {\n // A transparent full-tile rect fixes the texture size to GRID_TILE; the\n // pattern (dot, or top+left edge lines) is drawn over it so tiling yields\n // an evenly spaced grid.\n const g = new this.Pixi.Graphics()\n g.rect(0, 0, GRID_TILE, GRID_TILE).fill({ color: 0xffffff, alpha: 0 })\n if (bg === 'dots') {\n g.circle(1, 1, 1).fill({ color: this.gridColor, alpha: 1 })\n } else if (bg === 'lines') {\n g.rect(0, 0, GRID_TILE, 1).fill({ color: this.gridColor, alpha: 1 })\n g.rect(0, 0, 1, GRID_TILE).fill({ color: this.gridColor, alpha: 1 })\n }\n const texture = this.app.renderer.generateTexture(g)\n // Linear sampling softens the pattern when it is minified (zoomed out),\n // which together with the far-zoom alpha fade (see updateGridAlpha) avoids\n // the moiré a nearest-sampled 1px dot/line grid produces at fractional\n // tile scales.\n texture.source.scaleMode = 'linear'\n g.destroy()\n return texture\n }\n\n /** Switch the background pattern at runtime. */\n setBackground(bg: TBlueprintBackground): void {\n if (this.destroyed || bg === this.background) return\n this.background = bg\n const old = this.gridTexture\n this.gridTexture = this.makeGridTexture(bg)\n this.grid.texture = this.gridTexture\n this.grid.visible = bg !== 'none'\n old?.destroy(true)\n this.requestRender()\n }\n\n setCamera(panX: number, panY: number, zoom: number): void {\n if (this.destroyed) return\n this.zoom = zoom\n this.world.position.set(panX, panY)\n this.world.scale.set(zoom)\n // Grid follows the camera: the GRID_TILE-sized pattern scales with zoom\n // and slides with pan.\n this.grid.tilePosition.set(panX, panY)\n this.grid.tileScale.set(zoom)\n this.updateGridAlpha(zoom)\n // Wires use pixelLine (constant screen width), so the camera never needs to\n // re-stroke them: panning and zooming are a pure GPU transform of the layer.\n this.requestRender()\n }\n\n /** Fade the grid out as tiles approach sub-pixel density (far zoom): the\n * pattern is meaningless when that dense and is the source of moiré. */\n private updateGridAlpha(zoom: number): void {\n if (this.background === 'none') return\n const tilePx = GRID_TILE * zoom\n const fade = Math.max(0, Math.min(1, (tilePx - 6) / 10))\n this.grid.alpha = GRID_BASE_ALPHA * fade\n }\n\n /** Stash the latest wire set; the frame loop reconciles geometry + colours it\n * (the first set at once, later ones coalesced to ANIM_FRAME_MS). */\n setWires(\n wires: IBlueprintWire[],\n shouldFlow: (conn: IBlueprintWire['conn']) => boolean,\n ): void {\n if (this.destroyed) return\n this.pendingWires = wires\n this.pendingShouldFlow = shouldFlow\n this.wiresDirty = true\n this.ensureRaf()\n }\n\n /** Reconcile the per-wire Graphics against the pending set. Geometry is\n * stroked (white, pixelLine) only when a wire is new or its path changed;\n * colour is NOT baked here (see applyWireColors). This runs on a\n * topology/geometry change, never per animation frame. */\n private reconcileWireGeometry(): void {\n this.wiresDirty = false\n this.wiresDrawnOnce = true\n // A style / mode change forces a reconcile: drop any 'vibrate' geometry back\n // to the straight base first, so switching to another style (flow / pulse)\n // or mode (levels / simple) resets the wire shape. If still in 'vibrate' the\n // next frame re-applies it to the wires actually carrying signal.\n for (const node of this.wireNodes.values()) {\n if (node.vibrating) {\n this.strokeBase(node)\n node.vibrating = false\n }\n }\n const seen = new Set<string>()\n const flow: IWireNode[] = []\n for (const wire of this.pendingWires) {\n let b = this.bezierCache.get(wire.path)\n if (b === undefined) {\n b = parseCubicPath(wire.path)\n this.bezierCache.set(wire.path, b)\n }\n if (!b) continue\n const c = wire.conn\n const key = `${c.fromNode}|${c.fromPort}|${c.toNode}|${c.toPort}`\n seen.add(key)\n const dim = c.active === false\n let node = this.wireNodes.get(key)\n if (!node) {\n const gfx = new this.Pixi.Graphics()\n node = {\n gfx,\n path: '',\n bezier: b,\n baseColor: 0,\n isMidi: wire.isMidi,\n dim,\n flows: false,\n key,\n }\n this.wireNodes.set(key, node)\n this.wireLayer.addChild(gfx)\n }\n node.bezier = b\n node.baseColor = this.resolveColor(wire.color, this.connectedColor)\n node.isMidi = wire.isMidi\n node.dim = dim\n node.flows = !dim && this.pendingShouldFlow(c)\n // Re-stroke only when the geometry actually changed.\n if (node.path !== wire.path) {\n node.path = wire.path\n node.gfx.clear()\n node.gfx\n .moveTo(b.p0[0], b.p0[1])\n .bezierCurveTo(b.c1[0], b.c1[1], b.c2[0], b.c2[1], b.p3[0], b.p3[1])\n .stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n node.gfx.alpha = dim ? 0.25 : 0.55\n if (node.flows) flow.push(node)\n }\n for (const [key, node] of this.wireNodes) {\n if (seen.has(key)) continue\n node.gfx.destroy()\n this.wireNodes.delete(key)\n }\n this.flowNodes = flow\n if (!flow.length) this.clearFlow()\n if (this.bezierCache.size > seen.size * 2 + 64) {\n const live = new Set([...this.wireNodes.values()].map((n) => n.path))\n for (const path of this.bezierCache.keys()) {\n if (!live.has(path)) this.bezierCache.delete(path)\n }\n }\n }\n\n /** The current colour of a wire: its live level (only in level-colouring /\n * 'levels' mode) or its base accent. In 'activity' mode wires keep their base\n * colour (activity is motion, not colour — the two are separate). */\n private wireColorFor(node: IWireNode): number {\n if (this.colorByLevel && this.liveData && !node.isMidi && !node.dim) {\n const level = this.liveData.get(node.key)\n if (level > 0) return levelToColorNumber(level)\n }\n return node.baseColor\n }\n\n /** Colour wires by their live level ('levels' mode) vs keep base colour\n * ('activity'/'simple'). Live data may still be streaming in 'activity' for\n * the flow gate, so this flag, not the presence of data, decides colouring. */\n setLevelColoring(on: boolean): void {\n if (this.destroyed || this.colorByLevel === on) return\n this.colorByLevel = on\n this.levelsDirty = true\n this.ensureRaf()\n }\n\n /** Choose how 'activity' animates a live wire. Forces a geometry + colour\n * reconcile so any per-frame mutation from the previous style (vibrate's\n * re-stroked path, pulse's alpha) is reset to the clean base first. */\n setActivityStyle(style: TWireActivityStyle): void {\n if (this.destroyed || this.activityStyle === style) return\n this.activityStyle = style\n this.clearFlow()\n this.wiresDrawnOnce = false\n this.wiresDirty = true\n this.ensureRaf()\n }\n\n /** Recolour every wire by tint only (no re-tessellation). Cheap enough to run\n * on every level tick. */\n private applyWireColors(): void {\n for (const node of this.wireNodes.values()) {\n node.gfx.tint = this.wireColorFor(node)\n }\n }\n\n /** Stash the visible cards' meter geometry (world coords). Runs when the\n * visible set or a card position changes, NOT per animation frame. */\n setMeters(cards: IBlueprintCard[]): void {\n if (this.destroyed) return\n const geoms: IMeterGeom[] = []\n for (const card of cards) {\n if (!card.meters) continue\n for (const m of card.meters) {\n geoms.push({\n id: m.id,\n x: card.x + m.x,\n y: card.y + m.y,\n w: m.w,\n h: m.h,\n })\n }\n }\n this.pendingMeters = geoms\n this.metersDirty = true\n this.ensureRaf()\n }\n\n /** Reconcile meter sprites against the pending geometry. Lays out a track +\n * fill per meter only when its box changes; values are applied separately. */\n private reconcileMeters(): void {\n this.metersDirty = false\n const seen = new Set<string>()\n for (const g of this.pendingMeters) {\n seen.add(g.id)\n const sig = `${Math.round(g.x)},${Math.round(g.y)},${Math.round(g.w)},${Math.round(g.h)}`\n let node = this.meterNodes.get(g.id)\n if (!node) {\n const track = new this.Pixi.Sprite(this.Pixi.Texture.WHITE)\n const fill = new this.Pixi.Sprite(this.Pixi.Texture.WHITE)\n track.tint = this.trackColor\n fill.anchor.set(0, 1) // grow up from the bottom\n this.meterLayer.addChild(track, fill)\n node = { track, fill, sig: '' }\n this.meterNodes.set(g.id, node)\n }\n if (node.sig !== sig) {\n node.sig = sig\n node.track.position.set(g.x, g.y)\n node.track.width = g.w\n node.track.height = g.h\n node.fill.position.set(g.x, g.y + g.h)\n node.fill.width = g.w\n }\n }\n for (const [id, node] of this.meterNodes) {\n if (seen.has(id)) continue\n node.track.destroy()\n node.fill.destroy()\n this.meterNodes.delete(id)\n }\n }\n\n /** Set each meter's fill height + colour from the live channel. Cheap (a\n * sprite scale + tint), no geometry rebuild; runs on the live tick. */\n private updateMeterValues(): void {\n if (!this.meterNodes.size) return\n const live = this.liveData\n for (const [id, node] of this.meterNodes) {\n const v = live ? Math.max(0, Math.min(1, live.get(id))) : 0\n const h = Math.round(Number(node.sig.split(',')[3]))\n node.fill.height = v * h\n node.fill.tint = levelToColorNumber(v)\n }\n }\n\n /** A small white dot, tinted per flow wire. Built once. */\n private makeDotTexture(): PIXI.Texture {\n const g = new this.Pixi.Graphics()\n g.circle(8, 8, 8).fill({ color: 0xffffff, alpha: 1 })\n const tex = this.app.renderer.generateTexture(g)\n tex.source.scaleMode = 'linear'\n g.destroy()\n return tex\n }\n\n resize(width: number, height: number, resolution: number): void {\n if (this.destroyed) return\n this.app.renderer.resolution = resolution\n this.app.renderer.resize(width, height)\n this.grid.width = width\n this.grid.height = height\n this.requestRender()\n }\n\n // ── Render-on-demand scheduler ────────────────────────────────────────\n\n /** Request a single repaint for an immediate change (camera/background/resize). */\n private requestRender(): void {\n this.needsRender = true\n this.ensureRaf()\n }\n\n /** Ensure exactly one frame is scheduled. */\n private ensureRaf(): void {\n if (this.rafId || this.destroyed) return\n this.rafId = requestAnimationFrame(this.frame)\n }\n\n /** The only place app.render() is called. Runs a frame, then reschedules\n * itself ONLY while there is pending or ongoing (animation) work, so an idle\n * scene drops to zero scheduled frames and zero CPU. */\n private frame = (ts: number): void => {\n this.rafId = 0\n if (this.destroyed) return\n let render = this.needsRender\n this.needsRender = false\n\n const animWindow = ts - this.lastAnimMs >= ANIM_FRAME_MS\n // Geometry reconcile (topology/path change) runs at once the first time then\n // throttled; colour (base or live level) is a cheap tint pass, no\n // re-tessellation, so a 60Hz level stream costs almost nothing.\n const doGeometry = this.wiresDirty && (!this.wiresDrawnOnce || animWindow)\n const doColors = doGeometry || (this.levelsDirty && animWindow)\n if (doGeometry) this.reconcileWireGeometry()\n // Meter geometry reconciles when the visible set / a card moves (infrequent,\n // not throttled); values animate from the live channel on the same throttled\n // cadence as wire colours.\n if (this.metersDirty) {\n this.reconcileMeters()\n this.updateMeterValues()\n render = true\n }\n if (doColors) {\n this.levelsDirty = false\n this.applyWireColors()\n this.updateMeterValues()\n render = true\n }\n // Activity animation: advance the shared phase + render the chosen style on\n // the anim cadence. Only wires actually carrying signal animate (gated per\n // style on the live level); the rest stay static.\n if (this.flowNodes.length && animWindow) {\n const dt = this.lastAnimMs ? ts - this.lastAnimMs : ANIM_FRAME_MS\n this.flowPhase = (this.flowPhase + FLOW_SPEED * (dt / 1000)) % 1\n if (this.activityStyle === 'pulse') this.drawPulse()\n else if (this.activityStyle === 'vibrate') this.drawVibrate()\n else this.drawFlow()\n render = true\n }\n if (animWindow) this.lastAnimMs = ts\n\n if (render) this.app.render()\n\n // Keep looping only while work remains; otherwise stop (0 CPU at rest).\n if (\n this.needsRender ||\n this.wiresDirty ||\n this.levelsDirty ||\n this.metersDirty ||\n this.flowNodes.length\n ) {\n this.ensureRaf()\n }\n }\n\n /** Position a pooled, tinted dot sprite on each flowing wire. Reuses sprites\n * (no per-frame geometry/allocation), so flow animation does not churn. */\n /** 'flow': a directional comet (a short fading trail of dots) travels each\n * active wire source -> destination. Cheap: pooled sprites, no geometry. */\n private drawFlow(): void {\n const scale = FLOW_DOT_R / Math.max(this.zoom, 0.01) / 8\n const COMET = 5 // dots per wire\n const GAP = 0.06 // phase spacing between trail dots\n let i = 0\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n if (level <= ACTIVITY_THRESHOLD) continue\n const color = this.wireColorFor(node)\n for (let k = 0; k < COMET; k++) {\n let ph = this.flowPhase - k * GAP\n if (ph < 0) ph += 1\n const [x, y] = cubicAt(node.bezier, ph)\n let sprite = this.flowSprites[i]\n if (!sprite) {\n sprite = new this.Pixi.Sprite(this.dotTexture)\n sprite.anchor.set(0.5)\n this.flowLayer.addChild(sprite)\n this.flowSprites[i] = sprite\n }\n sprite.visible = true\n sprite.position.set(x, y)\n sprite.scale.set(scale * (1 - k * 0.12))\n sprite.alpha = 0.9 * (1 - k / COMET) // lead bright, trail fades\n sprite.tint = color\n i++\n }\n }\n for (; i < this.flowSprites.length; i++)\n this.flowSprites[i]!.visible = false\n }\n\n /** 'pulse': the whole active wire brightens and dims (shared phase). No\n * sprites, no geometry — just a per-wire alpha, so it is essentially free. */\n private drawPulse(): void {\n const pulse = 0.4 + 0.6 * Math.abs(Math.sin(this.flowPhase * Math.PI * 2))\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n node.gfx.alpha =\n level > ACTIVITY_THRESHOLD ? pulse : node.dim ? 0.25 : 0.55\n }\n }\n\n /** 'vibrate': the active wire oscillates like a plucked string. Re-strokes the\n * wire's geometry each frame with a perpendicular sine (enveloped to zero at\n * the endpoints), only for wires carrying signal. Restores the straight base\n * stroke when a wire goes silent. Costs a per-frame re-tessellation of the\n * ACTIVE wires only. */\n private drawVibrate(): void {\n const amp = 2.5 / Math.max(this.zoom, 0.01) // ~constant screen amplitude\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n if (level <= ACTIVITY_THRESHOLD) {\n if (node.vibrating) {\n this.strokeBase(node)\n node.vibrating = false\n }\n continue\n }\n node.vibrating = true\n this.strokeVibrated(node, amp)\n }\n }\n\n /** Re-stroke a wire's straight base bezier (undoes a vibrate). */\n private strokeBase(node: IWireNode): void {\n const b = node.bezier\n node.gfx\n .clear()\n .moveTo(b.p0[0], b.p0[1])\n .bezierCurveTo(b.c1[0], b.c1[1], b.c2[0], b.c2[1], b.p3[0], b.p3[1])\n .stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n\n /** Re-stroke a wire as a vibrating string: sample the bezier and offset each\n * sample perpendicular to the local tangent by an enveloped travelling sine. */\n private strokeVibrated(node: IWireNode, amp: number): void {\n const b = node.bezier\n // Tight, fast string: many short waves along the wire (WAVES) and a quick\n // temporal shimmer (TEMPORAL, integer multiple so the phase wraps\n // seamlessly). M is kept at ~4 samples per wave so the high spatial\n // frequency stays smooth without aliasing.\n const WAVES = 12\n const TEMPORAL = 6\n const M = 48\n const temporal = this.flowPhase * Math.PI * 2 * TEMPORAL\n const g = node.gfx\n g.clear()\n for (let s = 0; s <= M; s++) {\n const t = s / M\n const [x, y] = cubicAt(b, t)\n const [x2, y2] = cubicAt(b, Math.min(1, t + 0.005))\n let dx = x2 - x\n let dy = y2 - y\n const len = Math.hypot(dx, dy) || 1\n dx /= len\n dy /= len\n const env = Math.sin(t * Math.PI) // 0 at ends, 1 at middle\n const off = Math.sin(t * Math.PI * WAVES + temporal) * amp * env\n const ox = x + -dy * off\n const oy = y + dx * off\n if (s === 0) g.moveTo(ox, oy)\n else g.lineTo(ox, oy)\n }\n g.stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n\n private clearFlow(): void {\n for (const s of this.flowSprites) s.visible = false\n }\n\n destroy(): void {\n if (this.destroyed) return\n this.destroyed = true\n if (this.rafId) {\n cancelAnimationFrame(this.rafId)\n this.rafId = 0\n }\n this.wireNodes.clear()\n this.flowSprites = []\n this.flowNodes = []\n this.meterNodes.clear()\n // removeView:false — the <canvas> is Vue-owned and unmounts with the\n // component; we only tear down the GPU scene + textures.\n this.app.destroy({ removeView: false }, { children: true, texture: true })\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=/var\(\s*(--[\w-]+)\s*(?:,\s*([^)]+))?\)/;function t(t,n){let r=new Map;function i(t){let r=t.match(e);if(!r)return t;let a=r[1],o=r[2]?.trim();return getComputedStyle(n).getPropertyValue(a).trim()||(o?i(o):t)}return(e,n)=>{if(!e)return n;let a=r.get(e);if(a!==void 0)return a;let o;try{let n=e.includes(`var(`)?i(e):e;o=new t.Color(n).toNumber()}catch{o=n}return r.set(e,o),o}}var n=/-?\d*\.?\d+(?:e[-+]?\d+)?/gi;function r(e){let t=e.match(n);if(!t||t.length<8)return null;let r=t.slice(0,8).map(Number);return r.some(e=>Number.isNaN(e))?null:{p0:[r[0],r[1]],c1:[r[2],r[3]],c2:[r[4],r[5]],p3:[r[6],r[7]]}}function i(e,t){let n=1-t,r=n*n*n,i=3*n*n*t,a=3*n*t*t,o=t*t*t;return[r*e.p0[0]+i*e.c1[0]+a*e.c2[0]+o*e.p3[0],r*e.p0[1]+i*e.c1[1]+a*e.c2[1]+o*e.p3[1]]}function a(e){let t=Math.max(0,Math.min(1,e)),n=[34,197,94],r=[250,204,21],i=[239,68,68],a,o,s;t<=.5?(a=n,o=r,s=t*2):(a=r,o=i,s=(t-.5)*2);let c=(e,t)=>Math.round(e+(t-e)*s);return c(a[0],o[0])<<16|c(a[1],o[1])<<8|c(a[2],o[2])}var o=24,s=.45,c=2.5,l=.004,u=.4,d=40,f=class e{app;world;grid;wireLayer;wireNodes=new Map;flowLayer;flowSprites=[];dotTexture;meterLayer;meterNodes=new Map;pendingMeters=[];metersDirty=!1;trackColor=1447454;resolveColor;connectedColor=6514417;flowNodes=[];flowPhase=0;zoom=1;needsRender=!1;rafId=0;lastAnimMs=0;colorByLevel=!1;activityStyle=`flow`;liveData;levelsDirty=!1;pendingWires=[];pendingShouldFlow=()=>!1;wiresDirty=!1;wiresDrawnOnce=!1;bezierCache=new Map;destroyed=!1;background=`dots`;gridColor=2763320;gridTexture;constructor(e){this.Pixi=e,this.app=new e.Application}static async create(t,n){let r=new e(t);return await r.init(n),r}async init(e){if(await this.app.init({canvas:e.canvas,width:e.width,height:e.height,backgroundAlpha:0,antialias:!0,resolution:e.resolution,autoDensity:!0,preference:`webgl`,autoStart:!1}),this.destroyed){this.app.destroy({removeView:!1},{children:!0});return}this.resolveColor=t(this.Pixi,e.el),this.connectedColor=this.resolveColor(`var(--nb-c-primary)`,6514417),this.liveData=e.liveData,this.liveData&&(this.liveData.onChange=()=>{this.destroyed||(this.levelsDirty=!0,this.ensureRaf())}),this.background=e.background,this.gridColor=this.resolveColor(`var(--nb-blueprint-grid-color, var(--nb-c-border))`,2763320),this.grid=this.buildGrid(e.width,e.height),this.app.stage.addChild(this.grid),this.world=new this.Pixi.Container,this.app.stage.addChild(this.world),this.wireLayer=new this.Pixi.Container,this.meterLayer=new this.Pixi.Container,this.flowLayer=new this.Pixi.Container,this.world.addChild(this.wireLayer,this.meterLayer,this.flowLayer),this.dotTexture=this.makeDotTexture(),this.trackColor=this.resolveColor(`var(--nb-c-layer-2)`,1447454),this.app.ticker.stop()}buildGrid(e,t){this.gridTexture=this.makeGridTexture(this.background);let n=new this.Pixi.TilingSprite({texture:this.gridTexture,width:e,height:t});return n.alpha=.4,n.visible=this.background!==`none`,n}makeGridTexture(e){let t=new this.Pixi.Graphics;t.rect(0,0,o,o).fill({color:16777215,alpha:0}),e===`dots`?t.circle(1,1,1).fill({color:this.gridColor,alpha:1}):e===`lines`&&(t.rect(0,0,o,1).fill({color:this.gridColor,alpha:1}),t.rect(0,0,1,o).fill({color:this.gridColor,alpha:1}));let n=this.app.renderer.generateTexture(t);return n.source.scaleMode=`linear`,t.destroy(),n}setBackground(e){if(this.destroyed||e===this.background)return;this.background=e;let t=this.gridTexture;this.gridTexture=this.makeGridTexture(e),this.grid.texture=this.gridTexture,this.grid.visible=e!==`none`,t?.destroy(!0),this.requestRender()}setCamera(e,t,n){this.destroyed||(this.zoom=n,this.world.position.set(e,t),this.world.scale.set(n),this.grid.tilePosition.set(e,t),this.grid.tileScale.set(n),this.updateGridAlpha(n),this.requestRender())}updateGridAlpha(e){if(this.background===`none`)return;let t=o*e,n=Math.max(0,Math.min(1,(t-6)/10));this.grid.alpha=u*n}setWires(e,t){this.destroyed||(this.pendingWires=e,this.pendingShouldFlow=t,this.wiresDirty=!0,this.ensureRaf())}reconcileWireGeometry(){this.wiresDirty=!1,this.wiresDrawnOnce=!0;for(let e of this.wireNodes.values())e.vibrating&&=(this.strokeBase(e),!1);let e=new Set,t=[];for(let n of this.pendingWires){let i=this.bezierCache.get(n.path);if(i===void 0&&(i=r(n.path),this.bezierCache.set(n.path,i)),!i)continue;let a=n.conn,o=`${a.fromNode}|${a.fromPort}|${a.toNode}|${a.toPort}`;e.add(o);let s=a.active===!1,c=this.wireNodes.get(o);if(!c){let e=new this.Pixi.Graphics;c={gfx:e,path:``,bezier:i,baseColor:0,isMidi:n.isMidi,dim:s,flows:!1,key:o},this.wireNodes.set(o,c),this.wireLayer.addChild(e)}c.bezier=i,c.baseColor=this.resolveColor(n.color,this.connectedColor),c.isMidi=n.isMidi,c.dim=s,c.flows=!s&&this.pendingShouldFlow(a),c.path!==n.path&&(c.path=n.path,c.gfx.clear(),c.gfx.moveTo(i.p0[0],i.p0[1]).bezierCurveTo(i.c1[0],i.c1[1],i.c2[0],i.c2[1],i.p3[0],i.p3[1]).stroke({width:1,pixelLine:!0,color:16777215})),c.gfx.alpha=s?.25:.55,c.flows&&t.push(c)}for(let[t,n]of this.wireNodes)e.has(t)||(n.gfx.destroy(),this.wireNodes.delete(t));if(this.flowNodes=t,t.length||this.clearFlow(),this.bezierCache.size>e.size*2+64){let e=new Set([...this.wireNodes.values()].map(e=>e.path));for(let t of this.bezierCache.keys())e.has(t)||this.bezierCache.delete(t)}}wireColorFor(e){if(this.colorByLevel&&this.liveData&&!e.isMidi&&!e.dim){let t=this.liveData.get(e.key);if(t>0)return a(t)}return e.baseColor}setLevelColoring(e){this.destroyed||this.colorByLevel===e||(this.colorByLevel=e,this.levelsDirty=!0,this.ensureRaf())}setActivityStyle(e){this.destroyed||this.activityStyle===e||(this.activityStyle=e,this.clearFlow(),this.wiresDrawnOnce=!1,this.wiresDirty=!0,this.ensureRaf())}applyWireColors(){for(let e of this.wireNodes.values())e.gfx.tint=this.wireColorFor(e)}setMeters(e){if(this.destroyed)return;let t=[];for(let n of e)if(n.meters)for(let e of n.meters)t.push({id:e.id,x:n.x+e.x,y:n.y+e.y,w:e.w,h:e.h});this.pendingMeters=t,this.metersDirty=!0,this.ensureRaf()}reconcileMeters(){this.metersDirty=!1;let e=new Set;for(let t of this.pendingMeters){e.add(t.id);let n=`${Math.round(t.x)},${Math.round(t.y)},${Math.round(t.w)},${Math.round(t.h)}`,r=this.meterNodes.get(t.id);if(!r){let e=new this.Pixi.Sprite(this.Pixi.Texture.WHITE),n=new this.Pixi.Sprite(this.Pixi.Texture.WHITE);e.tint=this.trackColor,n.anchor.set(0,1),this.meterLayer.addChild(e,n),r={track:e,fill:n,sig:``},this.meterNodes.set(t.id,r)}r.sig!==n&&(r.sig=n,r.track.position.set(t.x,t.y),r.track.width=t.w,r.track.height=t.h,r.fill.position.set(t.x,t.y+t.h),r.fill.width=t.w)}for(let[t,n]of this.meterNodes)e.has(t)||(n.track.destroy(),n.fill.destroy(),this.meterNodes.delete(t))}updateMeterValues(){if(!this.meterNodes.size)return;let e=this.liveData;for(let[t,n]of this.meterNodes){let r=e?Math.max(0,Math.min(1,e.get(t))):0,i=Math.round(Number(n.sig.split(`,`)[3]));n.fill.height=r*i,n.fill.tint=a(r)}}makeDotTexture(){let e=new this.Pixi.Graphics;e.circle(8,8,8).fill({color:16777215,alpha:1});let t=this.app.renderer.generateTexture(e);return t.source.scaleMode=`linear`,e.destroy(),t}resize(e,t,n){this.destroyed||(this.app.renderer.resolution=n,this.app.renderer.resize(e,t),this.grid.width=e,this.grid.height=t,this.requestRender())}requestRender(){this.needsRender=!0,this.ensureRaf()}ensureRaf(){this.rafId||this.destroyed||(this.rafId=requestAnimationFrame(this.frame))}frame=e=>{if(this.rafId=0,this.destroyed)return;let t=this.needsRender;this.needsRender=!1;let n=e-this.lastAnimMs>=d,r=this.wiresDirty&&(!this.wiresDrawnOnce||n),i=r||this.levelsDirty&&n;if(r&&this.reconcileWireGeometry(),this.metersDirty&&(this.reconcileMeters(),this.updateMeterValues(),t=!0),i&&(this.levelsDirty=!1,this.applyWireColors(),this.updateMeterValues(),t=!0),this.flowNodes.length&&n){let n=this.lastAnimMs?e-this.lastAnimMs:d;this.flowPhase=(this.flowPhase+n/1e3*s)%1,this.activityStyle===`pulse`?this.drawPulse():this.activityStyle===`vibrate`?this.drawVibrate():this.drawFlow(),t=!0}n&&(this.lastAnimMs=e),t&&this.app.render(),(this.needsRender||this.wiresDirty||this.levelsDirty||this.metersDirty||this.flowNodes.length)&&this.ensureRaf()};drawFlow(){let e=c/Math.max(this.zoom,.01)/8,t=0;for(let n of this.flowNodes){if((this.liveData?this.liveData.get(n.key):0)<=l)continue;let r=this.wireColorFor(n);for(let a=0;a<5;a++){let o=this.flowPhase-a*.06;o<0&&(o+=1);let[s,c]=i(n.bezier,o),l=this.flowSprites[t];l||(l=new this.Pixi.Sprite(this.dotTexture),l.anchor.set(.5),this.flowLayer.addChild(l),this.flowSprites[t]=l),l.visible=!0,l.position.set(s,c),l.scale.set(e*(1-a*.12)),l.alpha=.9*(1-a/5),l.tint=r,t++}}for(;t<this.flowSprites.length;t++)this.flowSprites[t].visible=!1}drawPulse(){let e=.4+.6*Math.abs(Math.sin(this.flowPhase*Math.PI*2));for(let t of this.flowNodes){let n=this.liveData?this.liveData.get(t.key):0;t.gfx.alpha=n>l?e:t.dim?.25:.55}}drawVibrate(){let e=2.5/Math.max(this.zoom,.01);for(let t of this.flowNodes){if((this.liveData?this.liveData.get(t.key):0)<=l){t.vibrating&&=(this.strokeBase(t),!1);continue}t.vibrating=!0,this.strokeVibrated(t,e)}}strokeBase(e){let t=e.bezier;e.gfx.clear().moveTo(t.p0[0],t.p0[1]).bezierCurveTo(t.c1[0],t.c1[1],t.c2[0],t.c2[1],t.p3[0],t.p3[1]).stroke({width:1,pixelLine:!0,color:16777215})}strokeVibrated(e,t){let n=e.bezier,r=this.flowPhase*Math.PI*2*6,a=e.gfx;a.clear();for(let e=0;e<=48;e++){let o=e/48,[s,c]=i(n,o),[l,u]=i(n,Math.min(1,o+.005)),d=l-s,f=u-c,p=Math.hypot(d,f)||1;d/=p,f/=p;let m=Math.sin(o*Math.PI),h=Math.sin(o*Math.PI*12+r)*t*m,g=s+-f*h,_=c+d*h;e===0?a.moveTo(g,_):a.lineTo(g,_)}a.stroke({width:1,pixelLine:!0,color:16777215})}clearFlow(){for(let e of this.flowSprites)e.visible=!1}destroy(){this.destroyed||(this.destroyed=!0,this.rafId&&=(cancelAnimationFrame(this.rafId),0),this.wireNodes.clear(),this.flowSprites=[],this.flowNodes=[],this.meterNodes.clear(),this.app.destroy({removeView:!1},{children:!0,texture:!0}))}};exports.PixiScene=f;
|
|
2
|
+
//# sourceMappingURL=pixi-scene-CmsiMPkU.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixi-scene-CmsiMPkU.cjs","names":[],"sources":["../src/components/blueprint-pixi/pixi-color.ts","../src/components/blueprint-pixi/wire-path.ts","../src/components/blueprint-pixi/level-color.ts","../src/components/blueprint-pixi/pixi-scene.ts"],"sourcesContent":["// CSS-color → PixiJS numeric color, with CSS custom-property resolution.\n//\n// Wire and card colors arrive as CSS color strings: concrete (`#38bdf8`,\n// `rgb(...)`) for resolved wires, but possibly a `var(--token)` for card\n// accents or the grid. PixiJS `Color` parses concrete strings but not\n// `var(...)`, so we resolve custom properties against a reference element's\n// computed style first, then hand the result to `Color`. Lookups are cached\n// because getComputedStyle is not free and the same tokens recur across\n// thousands of wires.\n\nimport type * as PIXI from 'pixi.js'\n\nexport type TColorResolver = (\n css: string | undefined,\n fallback: number,\n) => number\n\nconst VAR_RE = /var\\(\\s*(--[\\w-]+)\\s*(?:,\\s*([^)]+))?\\)/\n\n/**\n * Build a resolver bound to a PixiJS module and a reference element (used to\n * resolve CSS custom properties). The returned function converts a CSS color\n * string to a 24-bit RGB number, returning `fallback` for empty/unparseable\n * input so a bad token never throws mid-render.\n */\nexport function makeColorResolver(\n Pixi: typeof PIXI,\n el: HTMLElement,\n): TColorResolver {\n const cache = new Map<string, number>()\n\n function resolveVar(css: string): string {\n const m = css.match(VAR_RE)\n if (!m) return css\n const token = m[1]!\n const fallbackExpr = m[2]?.trim()\n const styles = getComputedStyle(el)\n const value = styles.getPropertyValue(token).trim()\n if (value) return value\n // Nested var fallback (e.g. var(--a, var(--b, #fff))): recurse.\n if (fallbackExpr) return resolveVar(fallbackExpr)\n return css\n }\n\n return (css, fallback) => {\n if (!css) return fallback\n const hit = cache.get(css)\n if (hit !== undefined) return hit\n let out: number\n try {\n const resolved = css.includes('var(') ? resolveVar(css) : css\n out = new Pixi.Color(resolved).toNumber()\n } catch {\n out = fallback\n }\n cache.set(css, out)\n return out\n }\n}\n","// Parsing and sampling for the cubic-bezier wire paths NbBlueprint produces.\n//\n// NbBlueprint's `computedWires` emits each wire as an SVG path string of the\n// exact form `M fx fy C c1x c1y, c2x c2y, tx ty` (see Blueprint.vue). The\n// PixiJS renderer re-draws those same curves with `Graphics.bezierCurveTo`,\n// and the flow animation samples them into points. This module turns the\n// path string into structured control points and sampled polylines. It has\n// no PixiJS dependency so it stays in the always-loaded bundle and is unit\n// testable without a WebGL context.\n\nexport interface ICubicBezier {\n /** Start point [x, y]. */\n p0: [number, number]\n /** First control point. */\n c1: [number, number]\n /** Second control point. */\n c2: [number, number]\n /** End point. */\n p3: [number, number]\n}\n\nconst NUMBER_RE = /-?\\d*\\.?\\d+(?:e[-+]?\\d+)?/gi\n\n/**\n * Parse a `M x y C x y x y x y` cubic path string into control points.\n * Returns null if the string does not contain the expected 8 numbers (so a\n * malformed or non-cubic path is skipped rather than throwing).\n */\nexport function parseCubicPath(d: string): ICubicBezier | null {\n const m = d.match(NUMBER_RE)\n if (!m || m.length < 8) return null\n const n = m.slice(0, 8).map(Number)\n if (n.some((v) => Number.isNaN(v))) return null\n return {\n p0: [n[0]!, n[1]!],\n c1: [n[2]!, n[3]!],\n c2: [n[4]!, n[5]!],\n p3: [n[6]!, n[7]!],\n }\n}\n\n/** Evaluate a cubic bezier at parameter t in [0, 1]. */\nexport function cubicAt(b: ICubicBezier, t: number): [number, number] {\n const mt = 1 - t\n const a = mt * mt * mt\n const c = 3 * mt * mt * t\n const e = 3 * mt * t * t\n const f = t * t * t\n return [\n a * b.p0[0] + c * b.c1[0] + e * b.c2[0] + f * b.p3[0],\n a * b.p0[1] + c * b.c1[1] + e * b.c2[1] + f * b.p3[1],\n ]\n}\n\n/**\n * Sample a cubic bezier into `segments + 1` points. Used by the flow\n * animation to walk evenly-ish along the wire. `segments` is a fixed count\n * (not arc-length parameterised) which is plenty for a short connector.\n */\nexport function sampleCubic(\n b: ICubicBezier,\n segments: number,\n): [number, number][] {\n const pts: [number, number][] = []\n for (let i = 0; i <= segments; i++) pts.push(cubicAt(b, i / segments))\n return pts\n}\n","// Map an audio level (0..1) to a green -> yellow -> red colour as a PixiJS\n// numeric colour (0xRRGGBB). The same ramp the DOM path uses for wire levels,\n// but returned as a number so the GPU layer never parses a CSS string per\n// frame. Level <= 0.5 interpolates green -> yellow; above, yellow -> red.\nexport function levelToColorNumber(level: number): number {\n const l = Math.max(0, Math.min(1, level))\n const green = [34, 197, 94]\n const yellow = [250, 204, 21]\n const red = [239, 68, 68]\n let a: number[], b: number[], t: number\n if (l <= 0.5) {\n a = green\n b = yellow\n t = l * 2\n } else {\n a = yellow\n b = red\n t = (l - 0.5) * 2\n }\n const mix = (x: number, y: number): number => Math.round(x + (y - x) * t)\n return (\n (mix(a[0]!, b[0]!) << 16) | (mix(a[1]!, b[1]!) << 8) | mix(a[2]!, b[2]!)\n )\n}\n","// PixiJS scene manager for the Blueprint renderer.\n//\n// Owns the Application, a single panned/zoomed `world` Container (the camera),\n// an infinite dot-grid background, a batched wire layer, and an animated flow\n// layer. Cards are NOT drawn here: they stay real interactive DOM in the\n// overlay above this canvas at all times (no painted/snapshot substitute). This\n// scene only accelerates the grid + wires + flow on the GPU. The Vue component\n// (BlueprintPixiRenderer.vue) drives it with the culled scene NbBlueprint feeds\n// every renderer; this module has all the PixiJS specifics.\n//\n// PixiJS is passed in (not imported) so it stays a dynamically-loaded,\n// externalized optional peer dependency; only `import type` references reach\n// this module statically, and those are erased at build time.\n\nimport type * as PIXI from 'pixi.js'\nimport type { IBlueprintCard, TBlueprintBackground } from '../Blueprint.types'\nimport type { IBlueprintWire } from '../Blueprint.renderer'\nimport { makeColorResolver } from './pixi-color'\nimport { parseCubicPath, cubicAt, type ICubicBezier } from './wire-path'\nimport { levelToColorNumber } from './level-color'\nimport type { BlueprintLiveData } from './live-data'\n\nconst GRID_TILE = 24\nconst FLOW_SPEED = 0.45 // fraction of a wire traversed per second\nconst FLOW_DOT_R = 2.5\n// A flow candidate only animates when its live signal exceeds this (linear\n// peak, ~-48 dBFS): low enough to trace inaudible-but-present signal, high\n// enough to ignore the noise floor, so activity shows the real signal path and\n// visibly stops where the signal stops (muted / no-input / silent wires).\nconst ACTIVITY_THRESHOLD = 0.004\n// Wire/flow geometry lives in the zoomed `world`. A normal world-space stroke\n// shrinks on screen as you zoom out (wires vanish at far zoom); `pixelLine`\n// renders a crisp screen-space line instead, constant at every zoom, so the\n// wire layer is drawn once and never re-stroked as the camera moves.\nconst GRID_BASE_ALPHA = 0.4\n// This scene renders ON DEMAND: there is no free-running 60fps loop. A state\n// change (camera, wires, background) requests a single repaint, and when\n// nothing changes the scene renders nothing and costs ~0 CPU, which matters in\n// an audio host where the GPU/CPU budget belongs to DSP. Animated modes\n// (\"levels\"/flow) push updates as fast as ~60Hz; we coalesce those and repaint\n// at most this often, so an always-animating mode stays cheap. ~25fps reads as\n// smooth for level/flow motion at a fraction of the cost of 60.\nconst ANIM_FRAME_MS = 40\n\nexport interface IPixiSceneOptions {\n canvas: HTMLCanvasElement\n /** Reference element for resolving CSS custom properties (the container). */\n el: HTMLElement\n width: number\n height: number\n resolution: number\n background: TBlueprintBackground\n /** Non-reactive live values (wire levels, ...) the host writes at audio rate;\n * the scene reads them on its throttled render tick. */\n liveData?: BlueprintLiveData\n}\n\n// One meter's GPU geometry in world coords (resolved from a card + its local\n// meter box). Recomputed only when a card moves or the visible set changes.\ninterface IMeterGeom {\n id: string\n x: number\n y: number\n w: number\n h: number\n}\n\n// A drawn meter: a static dark track and an animated fill, plus a signature so\n// geometry is only re-laid-out when it actually changes.\ninterface IMeterNode {\n track: PIXI.Sprite\n fill: PIXI.Sprite\n sig: string\n}\n\n// One wire drawn once (geometry baked, stroked white) and recoloured by `tint`.\n// Animating the level colour is then a uniform change, NOT a re-tessellation,\n// which is what eliminates the per-frame GPU-geometry churn (and its GC spikes).\ninterface IWireNode {\n gfx: PIXI.Graphics\n path: string\n bezier: ICubicBezier\n baseColor: number\n isMidi: boolean\n dim: boolean\n flows: boolean\n key: string\n /** True while this wire's geometry is being re-stroked by the 'vibrate'\n * activity style, so it can be restored to the straight base stroke when it\n * stops carrying signal. */\n vibrating?: boolean\n}\n\n/** How the 'activity' wire mode animates a live wire. */\nexport type TWireActivityStyle = 'flow' | 'pulse' | 'vibrate'\n\nexport class PixiScene {\n private app: PIXI.Application\n private world!: PIXI.Container\n private grid!: PIXI.TilingSprite\n // Wire layer: a container of per-wire Graphics (see IWireNode). Flow layer: a\n // pool of reused dot sprites repositioned each frame (no re-tessellation).\n private wireLayer!: PIXI.Container\n private wireNodes = new Map<string, IWireNode>()\n private flowLayer!: PIXI.Container\n private flowSprites: PIXI.Sprite[] = []\n private dotTexture!: PIXI.Texture\n // Meter layer: each declared card meter is a static dark track + an animated\n // fill sprite (scale.y + tint from the live channel). Drawn over wires, on the\n // back canvas; the DOM card leaves a transparent hole so it shows through.\n private meterLayer!: PIXI.Container\n private meterNodes = new Map<string, IMeterNode>()\n private pendingMeters: IMeterGeom[] = []\n private metersDirty = false\n private trackColor = 0x16161e\n private resolveColor!: ReturnType<typeof makeColorResolver>\n // Fallback colour for a wire whose own colour does not resolve.\n private connectedColor = 0x6366f1\n private flowNodes: IWireNode[] = []\n private flowPhase = 0\n private zoom = 1\n // Render-on-demand scheduler. `needsRender` is the immediate path (camera,\n // background, resize). `rafId` holds the single in-flight frame; the loop\n // only stays alive while there is pending or ongoing (animation) work, then\n // stops so the scene costs nothing at rest. `lastAnimMs` paces the throttled\n // animation/wire-rebuild path.\n private needsRender = false\n private rafId = 0\n private lastAnimMs = 0\n // Colour wires by live level ('levels' mode) vs keep base colour. Distinct\n // from \"is live data present\", because 'activity' mode also streams levels\n // (to gate flow) but must NOT colour wires. See setLevelColoring.\n private colorByLevel = false\n // How 'activity' mode animates a live wire (flow comet / pulse / vibrate).\n private activityStyle: TWireActivityStyle = 'flow'\n // Live wire levels (host writes at audio rate, bypassing Vue). `levelsDirty`\n // is set when a write lands; the frame loop recolours wires on the throttled\n // animation cadence, then idles when the host stops writing.\n private liveData: BlueprintLiveData | undefined\n private levelsDirty = false\n // Coalesced wire state: setWires only stashes the latest set + marks dirty;\n // the frame loop rebuilds it (immediately the first time, then throttled to\n // ANIM_FRAME_MS). Parsed beziers are cached by path string so unchanged\n // geometry (levels mode only shifts colour) never pays the regex parse again.\n private pendingWires: IBlueprintWire[] = []\n private pendingShouldFlow: (conn: IBlueprintWire['conn']) => boolean = () =>\n false\n private wiresDirty = false\n private wiresDrawnOnce = false\n private bezierCache = new Map<string, ICubicBezier | null>()\n private destroyed = false\n private background: TBlueprintBackground = 'dots'\n private gridColor = 0x2a2a38\n private gridTexture!: PIXI.Texture\n\n private constructor(private readonly Pixi: typeof PIXI) {\n this.app = new Pixi.Application()\n }\n\n /** Create and initialise a scene. Async because PixiJS v8 `app.init` is. */\n static async create(\n Pixi: typeof PIXI,\n opts: IPixiSceneOptions,\n ): Promise<PixiScene> {\n const scene = new PixiScene(Pixi)\n await scene.init(opts)\n return scene\n }\n\n private async init(opts: IPixiSceneOptions): Promise<void> {\n await this.app.init({\n canvas: opts.canvas,\n width: opts.width,\n height: opts.height,\n backgroundAlpha: 0,\n antialias: true,\n resolution: opts.resolution,\n autoDensity: true,\n preference: 'webgl',\n // No free-running render loop: we drive app.render() on demand so an idle\n // blueprint costs ~0 CPU (the audio engine gets the budget).\n autoStart: false,\n })\n if (this.destroyed) {\n this.app.destroy({ removeView: false }, { children: true })\n return\n }\n\n this.resolveColor = makeColorResolver(this.Pixi, opts.el)\n this.connectedColor = this.resolveColor('var(--nb-c-primary)', 0x6366f1)\n\n this.liveData = opts.liveData\n if (this.liveData) {\n // A host write wakes the loop; the actual recolour is throttled in frame().\n this.liveData.onChange = () => {\n if (this.destroyed) return\n this.levelsDirty = true\n this.ensureRaf()\n }\n }\n\n this.background = opts.background\n this.gridColor = this.resolveColor(\n 'var(--nb-blueprint-grid-color, var(--nb-c-border))',\n 0x2a2a38,\n )\n this.grid = this.buildGrid(opts.width, opts.height)\n this.app.stage.addChild(this.grid)\n\n this.world = new this.Pixi.Container()\n this.app.stage.addChild(this.world)\n\n this.wireLayer = new this.Pixi.Container()\n this.meterLayer = new this.Pixi.Container()\n this.flowLayer = new this.Pixi.Container()\n // Order: wires (back) -> meters (over the card area) -> flow dots (top).\n this.world.addChild(this.wireLayer, this.meterLayer, this.flowLayer)\n this.dotTexture = this.makeDotTexture()\n this.trackColor = this.resolveColor('var(--nb-c-layer-2)', 0x16161e)\n // Stop Pixi's shared ticker for good measure; rendering is fully on-demand.\n this.app.ticker.stop()\n }\n\n private buildGrid(width: number, height: number): PIXI.TilingSprite {\n // A GRID_TILE-sized pattern tile, tiled across the screen. The grid lives\n // in stage (screen) space; panning/zooming maps to the tiling sprite's\n // tilePosition/tileScale (= zoom), which is far cheaper than redrawing a\n // world-space pattern every camera change.\n this.gridTexture = this.makeGridTexture(this.background)\n const sprite = new this.Pixi.TilingSprite({\n texture: this.gridTexture,\n width,\n height,\n })\n sprite.alpha = 0.4\n sprite.visible = this.background !== 'none'\n return sprite\n }\n\n private makeGridTexture(bg: TBlueprintBackground): PIXI.Texture {\n // A transparent full-tile rect fixes the texture size to GRID_TILE; the\n // pattern (dot, or top+left edge lines) is drawn over it so tiling yields\n // an evenly spaced grid.\n const g = new this.Pixi.Graphics()\n g.rect(0, 0, GRID_TILE, GRID_TILE).fill({ color: 0xffffff, alpha: 0 })\n if (bg === 'dots') {\n g.circle(1, 1, 1).fill({ color: this.gridColor, alpha: 1 })\n } else if (bg === 'lines') {\n g.rect(0, 0, GRID_TILE, 1).fill({ color: this.gridColor, alpha: 1 })\n g.rect(0, 0, 1, GRID_TILE).fill({ color: this.gridColor, alpha: 1 })\n }\n const texture = this.app.renderer.generateTexture(g)\n // Linear sampling softens the pattern when it is minified (zoomed out),\n // which together with the far-zoom alpha fade (see updateGridAlpha) avoids\n // the moiré a nearest-sampled 1px dot/line grid produces at fractional\n // tile scales.\n texture.source.scaleMode = 'linear'\n g.destroy()\n return texture\n }\n\n /** Switch the background pattern at runtime. */\n setBackground(bg: TBlueprintBackground): void {\n if (this.destroyed || bg === this.background) return\n this.background = bg\n const old = this.gridTexture\n this.gridTexture = this.makeGridTexture(bg)\n this.grid.texture = this.gridTexture\n this.grid.visible = bg !== 'none'\n old?.destroy(true)\n this.requestRender()\n }\n\n setCamera(panX: number, panY: number, zoom: number): void {\n if (this.destroyed) return\n this.zoom = zoom\n this.world.position.set(panX, panY)\n this.world.scale.set(zoom)\n // Grid follows the camera: the GRID_TILE-sized pattern scales with zoom\n // and slides with pan.\n this.grid.tilePosition.set(panX, panY)\n this.grid.tileScale.set(zoom)\n this.updateGridAlpha(zoom)\n // Wires use pixelLine (constant screen width), so the camera never needs to\n // re-stroke them: panning and zooming are a pure GPU transform of the layer.\n this.requestRender()\n }\n\n /** Fade the grid out as tiles approach sub-pixel density (far zoom): the\n * pattern is meaningless when that dense and is the source of moiré. */\n private updateGridAlpha(zoom: number): void {\n if (this.background === 'none') return\n const tilePx = GRID_TILE * zoom\n const fade = Math.max(0, Math.min(1, (tilePx - 6) / 10))\n this.grid.alpha = GRID_BASE_ALPHA * fade\n }\n\n /** Stash the latest wire set; the frame loop reconciles geometry + colours it\n * (the first set at once, later ones coalesced to ANIM_FRAME_MS). */\n setWires(\n wires: IBlueprintWire[],\n shouldFlow: (conn: IBlueprintWire['conn']) => boolean,\n ): void {\n if (this.destroyed) return\n this.pendingWires = wires\n this.pendingShouldFlow = shouldFlow\n this.wiresDirty = true\n this.ensureRaf()\n }\n\n /** Reconcile the per-wire Graphics against the pending set. Geometry is\n * stroked (white, pixelLine) only when a wire is new or its path changed;\n * colour is NOT baked here (see applyWireColors). This runs on a\n * topology/geometry change, never per animation frame. */\n private reconcileWireGeometry(): void {\n this.wiresDirty = false\n this.wiresDrawnOnce = true\n // A style / mode change forces a reconcile: drop any 'vibrate' geometry back\n // to the straight base first, so switching to another style (flow / pulse)\n // or mode (levels / simple) resets the wire shape. If still in 'vibrate' the\n // next frame re-applies it to the wires actually carrying signal.\n for (const node of this.wireNodes.values()) {\n if (node.vibrating) {\n this.strokeBase(node)\n node.vibrating = false\n }\n }\n const seen = new Set<string>()\n const flow: IWireNode[] = []\n for (const wire of this.pendingWires) {\n let b = this.bezierCache.get(wire.path)\n if (b === undefined) {\n b = parseCubicPath(wire.path)\n this.bezierCache.set(wire.path, b)\n }\n if (!b) continue\n const c = wire.conn\n const key = `${c.fromNode}|${c.fromPort}|${c.toNode}|${c.toPort}`\n seen.add(key)\n const dim = c.active === false\n let node = this.wireNodes.get(key)\n if (!node) {\n const gfx = new this.Pixi.Graphics()\n node = {\n gfx,\n path: '',\n bezier: b,\n baseColor: 0,\n isMidi: wire.isMidi,\n dim,\n flows: false,\n key,\n }\n this.wireNodes.set(key, node)\n this.wireLayer.addChild(gfx)\n }\n node.bezier = b\n node.baseColor = this.resolveColor(wire.color, this.connectedColor)\n node.isMidi = wire.isMidi\n node.dim = dim\n node.flows = !dim && this.pendingShouldFlow(c)\n // Re-stroke only when the geometry actually changed.\n if (node.path !== wire.path) {\n node.path = wire.path\n node.gfx.clear()\n node.gfx\n .moveTo(b.p0[0], b.p0[1])\n .bezierCurveTo(b.c1[0], b.c1[1], b.c2[0], b.c2[1], b.p3[0], b.p3[1])\n .stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n node.gfx.alpha = dim ? 0.25 : 0.55\n if (node.flows) flow.push(node)\n }\n for (const [key, node] of this.wireNodes) {\n if (seen.has(key)) continue\n node.gfx.destroy()\n this.wireNodes.delete(key)\n }\n this.flowNodes = flow\n if (!flow.length) this.clearFlow()\n if (this.bezierCache.size > seen.size * 2 + 64) {\n const live = new Set([...this.wireNodes.values()].map((n) => n.path))\n for (const path of this.bezierCache.keys()) {\n if (!live.has(path)) this.bezierCache.delete(path)\n }\n }\n }\n\n /** The current colour of a wire: its live level (only in level-colouring /\n * 'levels' mode) or its base accent. In 'activity' mode wires keep their base\n * colour (activity is motion, not colour — the two are separate). */\n private wireColorFor(node: IWireNode): number {\n if (this.colorByLevel && this.liveData && !node.isMidi && !node.dim) {\n const level = this.liveData.get(node.key)\n if (level > 0) return levelToColorNumber(level)\n }\n return node.baseColor\n }\n\n /** Colour wires by their live level ('levels' mode) vs keep base colour\n * ('activity'/'simple'). Live data may still be streaming in 'activity' for\n * the flow gate, so this flag, not the presence of data, decides colouring. */\n setLevelColoring(on: boolean): void {\n if (this.destroyed || this.colorByLevel === on) return\n this.colorByLevel = on\n this.levelsDirty = true\n this.ensureRaf()\n }\n\n /** Choose how 'activity' animates a live wire. Forces a geometry + colour\n * reconcile so any per-frame mutation from the previous style (vibrate's\n * re-stroked path, pulse's alpha) is reset to the clean base first. */\n setActivityStyle(style: TWireActivityStyle): void {\n if (this.destroyed || this.activityStyle === style) return\n this.activityStyle = style\n this.clearFlow()\n this.wiresDrawnOnce = false\n this.wiresDirty = true\n this.ensureRaf()\n }\n\n /** Recolour every wire by tint only (no re-tessellation). Cheap enough to run\n * on every level tick. */\n private applyWireColors(): void {\n for (const node of this.wireNodes.values()) {\n node.gfx.tint = this.wireColorFor(node)\n }\n }\n\n /** Stash the visible cards' meter geometry (world coords). Runs when the\n * visible set or a card position changes, NOT per animation frame. */\n setMeters(cards: IBlueprintCard[]): void {\n if (this.destroyed) return\n const geoms: IMeterGeom[] = []\n for (const card of cards) {\n if (!card.meters) continue\n for (const m of card.meters) {\n geoms.push({\n id: m.id,\n x: card.x + m.x,\n y: card.y + m.y,\n w: m.w,\n h: m.h,\n })\n }\n }\n this.pendingMeters = geoms\n this.metersDirty = true\n this.ensureRaf()\n }\n\n /** Reconcile meter sprites against the pending geometry. Lays out a track +\n * fill per meter only when its box changes; values are applied separately. */\n private reconcileMeters(): void {\n this.metersDirty = false\n const seen = new Set<string>()\n for (const g of this.pendingMeters) {\n seen.add(g.id)\n const sig = `${Math.round(g.x)},${Math.round(g.y)},${Math.round(g.w)},${Math.round(g.h)}`\n let node = this.meterNodes.get(g.id)\n if (!node) {\n const track = new this.Pixi.Sprite(this.Pixi.Texture.WHITE)\n const fill = new this.Pixi.Sprite(this.Pixi.Texture.WHITE)\n track.tint = this.trackColor\n fill.anchor.set(0, 1) // grow up from the bottom\n this.meterLayer.addChild(track, fill)\n node = { track, fill, sig: '' }\n this.meterNodes.set(g.id, node)\n }\n if (node.sig !== sig) {\n node.sig = sig\n node.track.position.set(g.x, g.y)\n node.track.width = g.w\n node.track.height = g.h\n node.fill.position.set(g.x, g.y + g.h)\n node.fill.width = g.w\n }\n }\n for (const [id, node] of this.meterNodes) {\n if (seen.has(id)) continue\n node.track.destroy()\n node.fill.destroy()\n this.meterNodes.delete(id)\n }\n }\n\n /** Set each meter's fill height + colour from the live channel. Cheap (a\n * sprite scale + tint), no geometry rebuild; runs on the live tick. */\n private updateMeterValues(): void {\n if (!this.meterNodes.size) return\n const live = this.liveData\n for (const [id, node] of this.meterNodes) {\n const v = live ? Math.max(0, Math.min(1, live.get(id))) : 0\n const h = Math.round(Number(node.sig.split(',')[3]))\n node.fill.height = v * h\n node.fill.tint = levelToColorNumber(v)\n }\n }\n\n /** A small white dot, tinted per flow wire. Built once. */\n private makeDotTexture(): PIXI.Texture {\n const g = new this.Pixi.Graphics()\n g.circle(8, 8, 8).fill({ color: 0xffffff, alpha: 1 })\n const tex = this.app.renderer.generateTexture(g)\n tex.source.scaleMode = 'linear'\n g.destroy()\n return tex\n }\n\n resize(width: number, height: number, resolution: number): void {\n if (this.destroyed) return\n this.app.renderer.resolution = resolution\n this.app.renderer.resize(width, height)\n this.grid.width = width\n this.grid.height = height\n this.requestRender()\n }\n\n // ── Render-on-demand scheduler ────────────────────────────────────────\n\n /** Request a single repaint for an immediate change (camera/background/resize). */\n private requestRender(): void {\n this.needsRender = true\n this.ensureRaf()\n }\n\n /** Ensure exactly one frame is scheduled. */\n private ensureRaf(): void {\n if (this.rafId || this.destroyed) return\n this.rafId = requestAnimationFrame(this.frame)\n }\n\n /** The only place app.render() is called. Runs a frame, then reschedules\n * itself ONLY while there is pending or ongoing (animation) work, so an idle\n * scene drops to zero scheduled frames and zero CPU. */\n private frame = (ts: number): void => {\n this.rafId = 0\n if (this.destroyed) return\n let render = this.needsRender\n this.needsRender = false\n\n const animWindow = ts - this.lastAnimMs >= ANIM_FRAME_MS\n // Geometry reconcile (topology/path change) runs at once the first time then\n // throttled; colour (base or live level) is a cheap tint pass, no\n // re-tessellation, so a 60Hz level stream costs almost nothing.\n const doGeometry = this.wiresDirty && (!this.wiresDrawnOnce || animWindow)\n const doColors = doGeometry || (this.levelsDirty && animWindow)\n if (doGeometry) this.reconcileWireGeometry()\n // Meter geometry reconciles when the visible set / a card moves (infrequent,\n // not throttled); values animate from the live channel on the same throttled\n // cadence as wire colours.\n if (this.metersDirty) {\n this.reconcileMeters()\n this.updateMeterValues()\n render = true\n }\n if (doColors) {\n this.levelsDirty = false\n this.applyWireColors()\n this.updateMeterValues()\n render = true\n }\n // Activity animation: advance the shared phase + render the chosen style on\n // the anim cadence. Only wires actually carrying signal animate (gated per\n // style on the live level); the rest stay static.\n if (this.flowNodes.length && animWindow) {\n const dt = this.lastAnimMs ? ts - this.lastAnimMs : ANIM_FRAME_MS\n this.flowPhase = (this.flowPhase + FLOW_SPEED * (dt / 1000)) % 1\n if (this.activityStyle === 'pulse') this.drawPulse()\n else if (this.activityStyle === 'vibrate') this.drawVibrate()\n else this.drawFlow()\n render = true\n }\n if (animWindow) this.lastAnimMs = ts\n\n if (render) this.app.render()\n\n // Keep looping only while work remains; otherwise stop (0 CPU at rest).\n if (\n this.needsRender ||\n this.wiresDirty ||\n this.levelsDirty ||\n this.metersDirty ||\n this.flowNodes.length\n ) {\n this.ensureRaf()\n }\n }\n\n /** Position a pooled, tinted dot sprite on each flowing wire. Reuses sprites\n * (no per-frame geometry/allocation), so flow animation does not churn. */\n /** 'flow': a directional comet (a short fading trail of dots) travels each\n * active wire source -> destination. Cheap: pooled sprites, no geometry. */\n private drawFlow(): void {\n const scale = FLOW_DOT_R / Math.max(this.zoom, 0.01) / 8\n const COMET = 5 // dots per wire\n const GAP = 0.06 // phase spacing between trail dots\n let i = 0\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n if (level <= ACTIVITY_THRESHOLD) continue\n const color = this.wireColorFor(node)\n for (let k = 0; k < COMET; k++) {\n let ph = this.flowPhase - k * GAP\n if (ph < 0) ph += 1\n const [x, y] = cubicAt(node.bezier, ph)\n let sprite = this.flowSprites[i]\n if (!sprite) {\n sprite = new this.Pixi.Sprite(this.dotTexture)\n sprite.anchor.set(0.5)\n this.flowLayer.addChild(sprite)\n this.flowSprites[i] = sprite\n }\n sprite.visible = true\n sprite.position.set(x, y)\n sprite.scale.set(scale * (1 - k * 0.12))\n sprite.alpha = 0.9 * (1 - k / COMET) // lead bright, trail fades\n sprite.tint = color\n i++\n }\n }\n for (; i < this.flowSprites.length; i++)\n this.flowSprites[i]!.visible = false\n }\n\n /** 'pulse': the whole active wire brightens and dims (shared phase). No\n * sprites, no geometry — just a per-wire alpha, so it is essentially free. */\n private drawPulse(): void {\n const pulse = 0.4 + 0.6 * Math.abs(Math.sin(this.flowPhase * Math.PI * 2))\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n node.gfx.alpha =\n level > ACTIVITY_THRESHOLD ? pulse : node.dim ? 0.25 : 0.55\n }\n }\n\n /** 'vibrate': the active wire oscillates like a plucked string. Re-strokes the\n * wire's geometry each frame with a perpendicular sine (enveloped to zero at\n * the endpoints), only for wires carrying signal. Restores the straight base\n * stroke when a wire goes silent. Costs a per-frame re-tessellation of the\n * ACTIVE wires only. */\n private drawVibrate(): void {\n const amp = 2.5 / Math.max(this.zoom, 0.01) // ~constant screen amplitude\n for (const node of this.flowNodes) {\n const level = this.liveData ? this.liveData.get(node.key) : 0\n if (level <= ACTIVITY_THRESHOLD) {\n if (node.vibrating) {\n this.strokeBase(node)\n node.vibrating = false\n }\n continue\n }\n node.vibrating = true\n this.strokeVibrated(node, amp)\n }\n }\n\n /** Re-stroke a wire's straight base bezier (undoes a vibrate). */\n private strokeBase(node: IWireNode): void {\n const b = node.bezier\n node.gfx\n .clear()\n .moveTo(b.p0[0], b.p0[1])\n .bezierCurveTo(b.c1[0], b.c1[1], b.c2[0], b.c2[1], b.p3[0], b.p3[1])\n .stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n\n /** Re-stroke a wire as a vibrating string: sample the bezier and offset each\n * sample perpendicular to the local tangent by an enveloped travelling sine. */\n private strokeVibrated(node: IWireNode, amp: number): void {\n const b = node.bezier\n // Tight, fast string: many short waves along the wire (WAVES) and a quick\n // temporal shimmer (TEMPORAL, integer multiple so the phase wraps\n // seamlessly). M is kept at ~4 samples per wave so the high spatial\n // frequency stays smooth without aliasing.\n const WAVES = 12\n const TEMPORAL = 6\n const M = 48\n const temporal = this.flowPhase * Math.PI * 2 * TEMPORAL\n const g = node.gfx\n g.clear()\n for (let s = 0; s <= M; s++) {\n const t = s / M\n const [x, y] = cubicAt(b, t)\n const [x2, y2] = cubicAt(b, Math.min(1, t + 0.005))\n let dx = x2 - x\n let dy = y2 - y\n const len = Math.hypot(dx, dy) || 1\n dx /= len\n dy /= len\n const env = Math.sin(t * Math.PI) // 0 at ends, 1 at middle\n const off = Math.sin(t * Math.PI * WAVES + temporal) * amp * env\n const ox = x + -dy * off\n const oy = y + dx * off\n if (s === 0) g.moveTo(ox, oy)\n else g.lineTo(ox, oy)\n }\n g.stroke({ width: 1, pixelLine: true, color: 0xffffff })\n }\n\n private clearFlow(): void {\n for (const s of this.flowSprites) s.visible = false\n }\n\n destroy(): void {\n if (this.destroyed) return\n this.destroyed = true\n if (this.rafId) {\n cancelAnimationFrame(this.rafId)\n this.rafId = 0\n }\n this.wireNodes.clear()\n this.flowSprites = []\n this.flowNodes = []\n this.meterNodes.clear()\n // removeView:false — the <canvas> is Vue-owned and unmounts with the\n // component; we only tear down the GPU scene + textures.\n this.app.destroy({ removeView: false }, { children: true, texture: true })\n }\n}\n"],"mappings":"AAiBA,IAAM,EAAS,0CAQf,SAAgB,EACd,EACA,EACgB,CAChB,IAAM,EAAQ,IAAI,IAElB,SAAS,EAAW,EAAqB,CACvC,IAAM,EAAI,EAAI,MAAM,EAAO,CAC3B,GAAI,CAAC,EAAG,OAAO,EACf,IAAM,EAAQ,EAAE,GACV,EAAe,EAAE,IAAI,MAAM,CAMjC,OALe,iBAAiB,EAAG,CACd,iBAAiB,EAAM,CAAC,MAAM,GAG/C,EAAqB,EAAW,EAAa,CAC1C,GAGT,OAAQ,EAAK,IAAa,CACxB,GAAI,CAAC,EAAK,OAAO,EACjB,IAAM,EAAM,EAAM,IAAI,EAAI,CAC1B,GAAI,IAAQ,IAAA,GAAW,OAAO,EAC9B,IAAI,EACJ,GAAI,CACF,IAAM,EAAW,EAAI,SAAS,OAAO,CAAG,EAAW,EAAI,CAAG,EAC1D,EAAM,IAAI,EAAK,MAAM,EAAS,CAAC,UAAU,MACnC,CACN,EAAM,EAGR,OADA,EAAM,IAAI,EAAK,EAAI,CACZ,GCnCX,IAAM,EAAY,8BAOlB,SAAgB,EAAe,EAAgC,CAC7D,IAAM,EAAI,EAAE,MAAM,EAAU,CAC5B,GAAI,CAAC,GAAK,EAAE,OAAS,EAAG,OAAO,KAC/B,IAAM,EAAI,EAAE,MAAM,EAAG,EAAE,CAAC,IAAI,OAAO,CAEnC,OADI,EAAE,KAAM,GAAM,OAAO,MAAM,EAAE,CAAC,CAAS,KACpC,CACL,GAAI,CAAC,EAAE,GAAK,EAAE,GAAI,CAClB,GAAI,CAAC,EAAE,GAAK,EAAE,GAAI,CAClB,GAAI,CAAC,EAAE,GAAK,EAAE,GAAI,CAClB,GAAI,CAAC,EAAE,GAAK,EAAE,GAAI,CACnB,CAIH,SAAgB,EAAQ,EAAiB,EAA6B,CACpE,IAAM,EAAK,EAAI,EACT,EAAI,EAAK,EAAK,EACd,EAAI,EAAI,EAAK,EAAK,EAClB,EAAI,EAAI,EAAK,EAAI,EACjB,EAAI,EAAI,EAAI,EAClB,MAAO,CACL,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GACnD,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GAAK,EAAI,EAAE,GAAG,GACpD,CC/CH,SAAgB,EAAmB,EAAuB,CACxD,IAAM,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAM,CAAC,CACnC,EAAQ,CAAC,GAAI,IAAK,GAAG,CACrB,EAAS,CAAC,IAAK,IAAK,GAAG,CACvB,EAAM,CAAC,IAAK,GAAI,GAAG,CACrB,EAAa,EAAa,EAC1B,GAAK,IACP,EAAI,EACJ,EAAI,EACJ,EAAI,EAAI,IAER,EAAI,EACJ,EAAI,EACJ,GAAK,EAAI,IAAO,GAElB,IAAM,GAAO,EAAW,IAAsB,KAAK,MAAM,GAAK,EAAI,GAAK,EAAE,CACzE,OACG,EAAI,EAAE,GAAK,EAAE,GAAI,EAAI,GAAO,EAAI,EAAE,GAAK,EAAE,GAAI,EAAI,EAAK,EAAI,EAAE,GAAK,EAAE,GAAI,CCC5E,IAAM,EAAY,GACZ,EAAa,IACb,EAAa,IAKb,EAAqB,KAKrB,EAAkB,GAQlB,EAAgB,GAsDT,EAAb,MAAa,CAAU,CACrB,IACA,MACA,KAGA,UACA,UAAoB,IAAI,IACxB,UACA,YAAqC,EAAE,CACvC,WAIA,WACA,WAAqB,IAAI,IACzB,cAAsC,EAAE,CACxC,YAAsB,GACtB,WAAqB,QACrB,aAEA,eAAyB,QACzB,UAAiC,EAAE,CACnC,UAAoB,EACpB,KAAe,EAMf,YAAsB,GACtB,MAAgB,EAChB,WAAqB,EAIrB,aAAuB,GAEvB,cAA4C,OAI5C,SACA,YAAsB,GAKtB,aAAyC,EAAE,CAC3C,sBACE,GACF,WAAqB,GACrB,eAAyB,GACzB,YAAsB,IAAI,IAC1B,UAAoB,GACpB,WAA2C,OAC3C,UAAoB,QACpB,YAEA,YAAoB,EAAoC,CAAnB,KAAA,KAAA,EACnC,KAAK,IAAM,IAAI,EAAK,YAItB,aAAa,OACX,EACA,EACoB,CACpB,IAAM,EAAQ,IAAI,EAAU,EAAK,CAEjC,OADA,MAAM,EAAM,KAAK,EAAK,CACf,EAGT,MAAc,KAAK,EAAwC,CAczD,GAbA,MAAM,KAAK,IAAI,KAAK,CAClB,OAAQ,EAAK,OACb,MAAO,EAAK,MACZ,OAAQ,EAAK,OACb,gBAAiB,EACjB,UAAW,GACX,WAAY,EAAK,WACjB,YAAa,GACb,WAAY,QAGZ,UAAW,GACZ,CAAC,CACE,KAAK,UAAW,CAClB,KAAK,IAAI,QAAQ,CAAE,WAAY,GAAO,CAAE,CAAE,SAAU,GAAM,CAAC,CAC3D,OAGF,KAAK,aAAe,EAAkB,KAAK,KAAM,EAAK,GAAG,CACzD,KAAK,eAAiB,KAAK,aAAa,sBAAuB,QAAS,CAExE,KAAK,SAAW,EAAK,SACjB,KAAK,WAEP,KAAK,SAAS,aAAiB,CACzB,KAAK,YACT,KAAK,YAAc,GACnB,KAAK,WAAW,IAIpB,KAAK,WAAa,EAAK,WACvB,KAAK,UAAY,KAAK,aACpB,qDACA,QACD,CACD,KAAK,KAAO,KAAK,UAAU,EAAK,MAAO,EAAK,OAAO,CACnD,KAAK,IAAI,MAAM,SAAS,KAAK,KAAK,CAElC,KAAK,MAAQ,IAAI,KAAK,KAAK,UAC3B,KAAK,IAAI,MAAM,SAAS,KAAK,MAAM,CAEnC,KAAK,UAAY,IAAI,KAAK,KAAK,UAC/B,KAAK,WAAa,IAAI,KAAK,KAAK,UAChC,KAAK,UAAY,IAAI,KAAK,KAAK,UAE/B,KAAK,MAAM,SAAS,KAAK,UAAW,KAAK,WAAY,KAAK,UAAU,CACpE,KAAK,WAAa,KAAK,gBAAgB,CACvC,KAAK,WAAa,KAAK,aAAa,sBAAuB,QAAS,CAEpE,KAAK,IAAI,OAAO,MAAM,CAGxB,UAAkB,EAAe,EAAmC,CAKlE,KAAK,YAAc,KAAK,gBAAgB,KAAK,WAAW,CACxD,IAAM,EAAS,IAAI,KAAK,KAAK,aAAa,CACxC,QAAS,KAAK,YACd,QACA,SACD,CAAC,CAGF,MAFA,GAAO,MAAQ,GACf,EAAO,QAAU,KAAK,aAAe,OAC9B,EAGT,gBAAwB,EAAwC,CAI9D,IAAM,EAAI,IAAI,KAAK,KAAK,SACxB,EAAE,KAAK,EAAG,EAAG,EAAW,EAAU,CAAC,KAAK,CAAE,MAAO,SAAU,MAAO,EAAG,CAAC,CAClE,IAAO,OACT,EAAE,OAAO,EAAG,EAAG,EAAE,CAAC,KAAK,CAAE,MAAO,KAAK,UAAW,MAAO,EAAG,CAAC,CAClD,IAAO,UAChB,EAAE,KAAK,EAAG,EAAG,EAAW,EAAE,CAAC,KAAK,CAAE,MAAO,KAAK,UAAW,MAAO,EAAG,CAAC,CACpE,EAAE,KAAK,EAAG,EAAG,EAAG,EAAU,CAAC,KAAK,CAAE,MAAO,KAAK,UAAW,MAAO,EAAG,CAAC,EAEtE,IAAM,EAAU,KAAK,IAAI,SAAS,gBAAgB,EAAE,CAOpD,MAFA,GAAQ,OAAO,UAAY,SAC3B,EAAE,SAAS,CACJ,EAIT,cAAc,EAAgC,CAC5C,GAAI,KAAK,WAAa,IAAO,KAAK,WAAY,OAC9C,KAAK,WAAa,EAClB,IAAM,EAAM,KAAK,YACjB,KAAK,YAAc,KAAK,gBAAgB,EAAG,CAC3C,KAAK,KAAK,QAAU,KAAK,YACzB,KAAK,KAAK,QAAU,IAAO,OAC3B,GAAK,QAAQ,GAAK,CAClB,KAAK,eAAe,CAGtB,UAAU,EAAc,EAAc,EAAoB,CACpD,KAAK,YACT,KAAK,KAAO,EACZ,KAAK,MAAM,SAAS,IAAI,EAAM,EAAK,CACnC,KAAK,MAAM,MAAM,IAAI,EAAK,CAG1B,KAAK,KAAK,aAAa,IAAI,EAAM,EAAK,CACtC,KAAK,KAAK,UAAU,IAAI,EAAK,CAC7B,KAAK,gBAAgB,EAAK,CAG1B,KAAK,eAAe,EAKtB,gBAAwB,EAAoB,CAC1C,GAAI,KAAK,aAAe,OAAQ,OAChC,IAAM,EAAS,EAAY,EACrB,EAAO,KAAK,IAAI,EAAG,KAAK,IAAI,GAAI,EAAS,GAAK,GAAG,CAAC,CACxD,KAAK,KAAK,MAAQ,EAAkB,EAKtC,SACE,EACA,EACM,CACF,KAAK,YACT,KAAK,aAAe,EACpB,KAAK,kBAAoB,EACzB,KAAK,WAAa,GAClB,KAAK,WAAW,EAOlB,uBAAsC,CACpC,KAAK,WAAa,GAClB,KAAK,eAAiB,GAKtB,IAAK,IAAM,KAAQ,KAAK,UAAU,QAAQ,CACxC,AAEE,EAAK,aADL,KAAK,WAAW,EAAK,CACJ,IAGrB,IAAM,EAAO,IAAI,IACX,EAAoB,EAAE,CAC5B,IAAK,IAAM,KAAQ,KAAK,aAAc,CACpC,IAAI,EAAI,KAAK,YAAY,IAAI,EAAK,KAAK,CAKvC,GAJI,IAAM,IAAA,KACR,EAAI,EAAe,EAAK,KAAK,CAC7B,KAAK,YAAY,IAAI,EAAK,KAAM,EAAE,EAEhC,CAAC,EAAG,SACR,IAAM,EAAI,EAAK,KACT,EAAM,GAAG,EAAE,SAAS,GAAG,EAAE,SAAS,GAAG,EAAE,OAAO,GAAG,EAAE,SACzD,EAAK,IAAI,EAAI,CACb,IAAM,EAAM,EAAE,SAAW,GACrB,EAAO,KAAK,UAAU,IAAI,EAAI,CAClC,GAAI,CAAC,EAAM,CACT,IAAM,EAAM,IAAI,KAAK,KAAK,SAC1B,EAAO,CACL,MACA,KAAM,GACN,OAAQ,EACR,UAAW,EACX,OAAQ,EAAK,OACb,MACA,MAAO,GACP,MACD,CACD,KAAK,UAAU,IAAI,EAAK,EAAK,CAC7B,KAAK,UAAU,SAAS,EAAI,CAE9B,EAAK,OAAS,EACd,EAAK,UAAY,KAAK,aAAa,EAAK,MAAO,KAAK,eAAe,CACnE,EAAK,OAAS,EAAK,OACnB,EAAK,IAAM,EACX,EAAK,MAAQ,CAAC,GAAO,KAAK,kBAAkB,EAAE,CAE1C,EAAK,OAAS,EAAK,OACrB,EAAK,KAAO,EAAK,KACjB,EAAK,IAAI,OAAO,CAChB,EAAK,IACF,OAAO,EAAE,GAAG,GAAI,EAAE,GAAG,GAAG,CACxB,cAAc,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAG,CACnE,OAAO,CAAE,MAAO,EAAG,UAAW,GAAM,MAAO,SAAU,CAAC,EAE3D,EAAK,IAAI,MAAQ,EAAM,IAAO,IAC1B,EAAK,OAAO,EAAK,KAAK,EAAK,CAEjC,IAAK,GAAM,CAAC,EAAK,KAAS,KAAK,UACzB,EAAK,IAAI,EAAI,GACjB,EAAK,IAAI,SAAS,CAClB,KAAK,UAAU,OAAO,EAAI,EAI5B,GAFA,KAAK,UAAY,EACZ,EAAK,QAAQ,KAAK,WAAW,CAC9B,KAAK,YAAY,KAAO,EAAK,KAAO,EAAI,GAAI,CAC9C,IAAM,EAAO,IAAI,IAAI,CAAC,GAAG,KAAK,UAAU,QAAQ,CAAC,CAAC,IAAK,GAAM,EAAE,KAAK,CAAC,CACrE,IAAK,IAAM,KAAQ,KAAK,YAAY,MAAM,CACnC,EAAK,IAAI,EAAK,EAAE,KAAK,YAAY,OAAO,EAAK,EAQxD,aAAqB,EAAyB,CAC5C,GAAI,KAAK,cAAgB,KAAK,UAAY,CAAC,EAAK,QAAU,CAAC,EAAK,IAAK,CACnE,IAAM,EAAQ,KAAK,SAAS,IAAI,EAAK,IAAI,CACzC,GAAI,EAAQ,EAAG,OAAO,EAAmB,EAAM,CAEjD,OAAO,EAAK,UAMd,iBAAiB,EAAmB,CAC9B,KAAK,WAAa,KAAK,eAAiB,IAC5C,KAAK,aAAe,EACpB,KAAK,YAAc,GACnB,KAAK,WAAW,EAMlB,iBAAiB,EAAiC,CAC5C,KAAK,WAAa,KAAK,gBAAkB,IAC7C,KAAK,cAAgB,EACrB,KAAK,WAAW,CAChB,KAAK,eAAiB,GACtB,KAAK,WAAa,GAClB,KAAK,WAAW,EAKlB,iBAAgC,CAC9B,IAAK,IAAM,KAAQ,KAAK,UAAU,QAAQ,CACxC,EAAK,IAAI,KAAO,KAAK,aAAa,EAAK,CAM3C,UAAU,EAA+B,CACvC,GAAI,KAAK,UAAW,OACpB,IAAM,EAAsB,EAAE,CAC9B,IAAK,IAAM,KAAQ,EACZ,KAAK,OACV,IAAK,IAAM,KAAK,EAAK,OACnB,EAAM,KAAK,CACT,GAAI,EAAE,GACN,EAAG,EAAK,EAAI,EAAE,EACd,EAAG,EAAK,EAAI,EAAE,EACd,EAAG,EAAE,EACL,EAAG,EAAE,EACN,CAAC,CAGN,KAAK,cAAgB,EACrB,KAAK,YAAc,GACnB,KAAK,WAAW,CAKlB,iBAAgC,CAC9B,KAAK,YAAc,GACnB,IAAM,EAAO,IAAI,IACjB,IAAK,IAAM,KAAK,KAAK,cAAe,CAClC,EAAK,IAAI,EAAE,GAAG,CACd,IAAM,EAAM,GAAG,KAAK,MAAM,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,EAAE,EAAE,GACnF,EAAO,KAAK,WAAW,IAAI,EAAE,GAAG,CACpC,GAAI,CAAC,EAAM,CACT,IAAM,EAAQ,IAAI,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,CACrD,EAAO,IAAI,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,CAC1D,EAAM,KAAO,KAAK,WAClB,EAAK,OAAO,IAAI,EAAG,EAAE,CACrB,KAAK,WAAW,SAAS,EAAO,EAAK,CACrC,EAAO,CAAE,QAAO,OAAM,IAAK,GAAI,CAC/B,KAAK,WAAW,IAAI,EAAE,GAAI,EAAK,CAE7B,EAAK,MAAQ,IACf,EAAK,IAAM,EACX,EAAK,MAAM,SAAS,IAAI,EAAE,EAAG,EAAE,EAAE,CACjC,EAAK,MAAM,MAAQ,EAAE,EACrB,EAAK,MAAM,OAAS,EAAE,EACtB,EAAK,KAAK,SAAS,IAAI,EAAE,EAAG,EAAE,EAAI,EAAE,EAAE,CACtC,EAAK,KAAK,MAAQ,EAAE,GAGxB,IAAK,GAAM,CAAC,EAAI,KAAS,KAAK,WACxB,EAAK,IAAI,EAAG,GAChB,EAAK,MAAM,SAAS,CACpB,EAAK,KAAK,SAAS,CACnB,KAAK,WAAW,OAAO,EAAG,EAM9B,mBAAkC,CAChC,GAAI,CAAC,KAAK,WAAW,KAAM,OAC3B,IAAM,EAAO,KAAK,SAClB,IAAK,GAAM,CAAC,EAAI,KAAS,KAAK,WAAY,CACxC,IAAM,EAAI,EAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAK,IAAI,EAAG,CAAC,CAAC,CAAG,EACpD,EAAI,KAAK,MAAM,OAAO,EAAK,IAAI,MAAM,IAAI,CAAC,GAAG,CAAC,CACpD,EAAK,KAAK,OAAS,EAAI,EACvB,EAAK,KAAK,KAAO,EAAmB,EAAE,EAK1C,gBAAuC,CACrC,IAAM,EAAI,IAAI,KAAK,KAAK,SACxB,EAAE,OAAO,EAAG,EAAG,EAAE,CAAC,KAAK,CAAE,MAAO,SAAU,MAAO,EAAG,CAAC,CACrD,IAAM,EAAM,KAAK,IAAI,SAAS,gBAAgB,EAAE,CAGhD,MAFA,GAAI,OAAO,UAAY,SACvB,EAAE,SAAS,CACJ,EAGT,OAAO,EAAe,EAAgB,EAA0B,CAC1D,KAAK,YACT,KAAK,IAAI,SAAS,WAAa,EAC/B,KAAK,IAAI,SAAS,OAAO,EAAO,EAAO,CACvC,KAAK,KAAK,MAAQ,EAClB,KAAK,KAAK,OAAS,EACnB,KAAK,eAAe,EAMtB,eAA8B,CAC5B,KAAK,YAAc,GACnB,KAAK,WAAW,CAIlB,WAA0B,CACpB,KAAK,OAAS,KAAK,YACvB,KAAK,MAAQ,sBAAsB,KAAK,MAAM,EAMhD,MAAiB,GAAqB,CAEpC,GADA,KAAK,MAAQ,EACT,KAAK,UAAW,OACpB,IAAI,EAAS,KAAK,YAClB,KAAK,YAAc,GAEnB,IAAM,EAAa,EAAK,KAAK,YAAc,EAIrC,EAAa,KAAK,aAAe,CAAC,KAAK,gBAAkB,GACzD,EAAW,GAAe,KAAK,aAAe,EAmBpD,GAlBI,GAAY,KAAK,uBAAuB,CAIxC,KAAK,cACP,KAAK,iBAAiB,CACtB,KAAK,mBAAmB,CACxB,EAAS,IAEP,IACF,KAAK,YAAc,GACnB,KAAK,iBAAiB,CACtB,KAAK,mBAAmB,CACxB,EAAS,IAKP,KAAK,UAAU,QAAU,EAAY,CACvC,IAAM,EAAK,KAAK,WAAa,EAAK,KAAK,WAAa,EACpD,KAAK,WAAa,KAAK,UAA0B,EAAK,IAAnB,GAA4B,EAC3D,KAAK,gBAAkB,QAAS,KAAK,WAAW,CAC3C,KAAK,gBAAkB,UAAW,KAAK,aAAa,CACxD,KAAK,UAAU,CACpB,EAAS,GAEP,IAAY,KAAK,WAAa,GAE9B,GAAQ,KAAK,IAAI,QAAQ,EAI3B,KAAK,aACL,KAAK,YACL,KAAK,aACL,KAAK,aACL,KAAK,UAAU,SAEf,KAAK,WAAW,EAQpB,UAAyB,CACvB,IAAM,EAAQ,EAAa,KAAK,IAAI,KAAK,KAAM,IAAK,CAAG,EAGnD,EAAI,EACR,IAAK,IAAM,KAAQ,KAAK,UAAW,CAEjC,IADc,KAAK,SAAW,KAAK,SAAS,IAAI,EAAK,IAAI,CAAG,IAC/C,EAAoB,SACjC,IAAM,EAAQ,KAAK,aAAa,EAAK,CACrC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC9B,IAAI,EAAK,KAAK,UAAY,EAAI,IAC1B,EAAK,IAAG,GAAM,GAClB,GAAM,CAAC,EAAG,GAAK,EAAQ,EAAK,OAAQ,EAAG,CACnC,EAAS,KAAK,YAAY,GACzB,IACH,EAAS,IAAI,KAAK,KAAK,OAAO,KAAK,WAAW,CAC9C,EAAO,OAAO,IAAI,GAAI,CACtB,KAAK,UAAU,SAAS,EAAO,CAC/B,KAAK,YAAY,GAAK,GAExB,EAAO,QAAU,GACjB,EAAO,SAAS,IAAI,EAAG,EAAE,CACzB,EAAO,MAAM,IAAI,GAAS,EAAI,EAAI,KAAM,CACxC,EAAO,MAAQ,IAAO,EAAI,EAAI,GAC9B,EAAO,KAAO,EACd,KAGJ,KAAO,EAAI,KAAK,YAAY,OAAQ,IAClC,KAAK,YAAY,GAAI,QAAU,GAKnC,WAA0B,CACxB,IAAM,EAAQ,GAAM,GAAM,KAAK,IAAI,KAAK,IAAI,KAAK,UAAY,KAAK,GAAK,EAAE,CAAC,CAC1E,IAAK,IAAM,KAAQ,KAAK,UAAW,CACjC,IAAM,EAAQ,KAAK,SAAW,KAAK,SAAS,IAAI,EAAK,IAAI,CAAG,EAC5D,EAAK,IAAI,MACP,EAAQ,EAAqB,EAAQ,EAAK,IAAM,IAAO,KAS7D,aAA4B,CAC1B,IAAM,EAAM,IAAM,KAAK,IAAI,KAAK,KAAM,IAAK,CAC3C,IAAK,IAAM,KAAQ,KAAK,UAAW,CAEjC,IADc,KAAK,SAAW,KAAK,SAAS,IAAI,EAAK,IAAI,CAAG,IAC/C,EAAoB,CAC/B,AAEE,EAAK,aADL,KAAK,WAAW,EAAK,CACJ,IAEnB,SAEF,EAAK,UAAY,GACjB,KAAK,eAAe,EAAM,EAAI,EAKlC,WAAmB,EAAuB,CACxC,IAAM,EAAI,EAAK,OACf,EAAK,IACF,OAAO,CACP,OAAO,EAAE,GAAG,GAAI,EAAE,GAAG,GAAG,CACxB,cAAc,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAI,EAAE,GAAG,GAAG,CACnE,OAAO,CAAE,MAAO,EAAG,UAAW,GAAM,MAAO,SAAU,CAAC,CAK3D,eAAuB,EAAiB,EAAmB,CACzD,IAAM,EAAI,EAAK,OAQT,EAAW,KAAK,UAAY,KAAK,GAAK,EAAI,EAC1C,EAAI,EAAK,IACf,EAAE,OAAO,CACT,IAAK,IAAI,EAAI,EAAG,GAAK,GAAG,IAAK,CAC3B,IAAM,EAAI,EAAI,GACR,CAAC,EAAG,GAAK,EAAQ,EAAG,EAAE,CACtB,CAAC,EAAI,GAAM,EAAQ,EAAG,KAAK,IAAI,EAAG,EAAI,KAAM,CAAC,CAC/C,EAAK,EAAK,EACV,EAAK,EAAK,EACR,EAAM,KAAK,MAAM,EAAI,EAAG,EAAI,EAClC,GAAM,EACN,GAAM,EACN,IAAM,EAAM,KAAK,IAAI,EAAI,KAAK,GAAG,CAC3B,EAAM,KAAK,IAAI,EAAI,KAAK,GAAK,GAAQ,EAAS,CAAG,EAAM,EACvD,EAAK,EAAI,CAAC,EAAK,EACf,EAAK,EAAI,EAAK,EAChB,IAAM,EAAG,EAAE,OAAO,EAAI,EAAG,CACxB,EAAE,OAAO,EAAI,EAAG,CAEvB,EAAE,OAAO,CAAE,MAAO,EAAG,UAAW,GAAM,MAAO,SAAU,CAAC,CAG1D,WAA0B,CACxB,IAAK,IAAM,KAAK,KAAK,YAAa,EAAE,QAAU,GAGhD,SAAgB,CACV,KAAK,YACT,KAAK,UAAY,GACjB,AAEE,KAAK,SADL,qBAAqB,KAAK,MAAM,CACnB,GAEf,KAAK,UAAU,OAAO,CACtB,KAAK,YAAc,EAAE,CACrB,KAAK,UAAY,EAAE,CACnB,KAAK,WAAW,OAAO,CAGvB,KAAK,IAAI,QAAQ,CAAE,WAAY,GAAO,CAAE,CAAE,SAAU,GAAM,QAAS,GAAM,CAAC"}
|