@perspective-dev/viewer-charts 4.5.1 → 5.0.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/cdn/perspective-viewer-charts.js +2 -2
- package/dist/cdn/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/charts/candlestick/candlestick.d.ts +8 -10
- package/dist/esm/charts/chart-base.d.ts +58 -4
- package/dist/esm/charts/chart.d.ts +45 -9
- package/dist/esm/charts/series/glyphs/draw-areas.d.ts +3 -2
- package/dist/esm/charts/series/glyphs/draw-bars.d.ts +8 -1
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +3 -2
- package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +5 -2
- package/dist/esm/charts/series/series-build.d.ts +11 -0
- package/dist/esm/charts/series/series-interact.d.ts +10 -0
- package/dist/esm/charts/series/series-render.d.ts +17 -1
- package/dist/esm/charts/series/series.d.ts +53 -10
- package/dist/esm/config.d.ts +19 -0
- package/dist/esm/data/view-reader.d.ts +10 -1
- package/dist/esm/event-detail.d.ts +7 -0
- package/dist/esm/layout/ticks.d.ts +5 -0
- package/dist/esm/perspective-viewer-charts.js +2 -2
- package/dist/esm/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/plugin/charts.d.ts +6 -3
- package/dist/esm/plugin/plugin.d.ts +46 -4
- package/dist/esm/render/scheduler.d.ts +34 -8
- package/dist/esm/transport/protocol.d.ts +31 -2
- package/dist/esm/transport/renderer-transport.d.ts +74 -3
- package/dist/esm/webgl/context-manager.d.ts +100 -117
- package/dist/esm/webgl/context-pool.d.ts +40 -0
- package/dist/esm/webgl/gl-context.d.ts +54 -0
- package/dist/esm/webgl/instanced-attrs.d.ts +7 -1
- package/dist/esm/worker/renderer.worker.d.ts +12 -0
- package/package.json +1 -1
- package/src/ts/charts/candlestick/candlestick-render.ts +4 -1
- package/src/ts/charts/candlestick/candlestick.ts +12 -17
- package/src/ts/charts/cartesian/cartesian-render.ts +80 -63
- package/src/ts/charts/chart-base.ts +85 -5
- package/src/ts/charts/chart.ts +47 -9
- package/src/ts/charts/common/category-axis-resolver.ts +5 -1
- package/src/ts/charts/common/tree-data.ts +61 -16
- package/src/ts/charts/heatmap/heatmap-render.ts +6 -2
- package/src/ts/charts/series/glyphs/draw-areas.ts +10 -1
- package/src/ts/charts/series/glyphs/draw-bars.ts +30 -9
- package/src/ts/charts/series/glyphs/draw-lines.ts +10 -1
- package/src/ts/charts/series/glyphs/draw-scatter.ts +56 -3
- package/src/ts/charts/series/series-build.ts +21 -3
- package/src/ts/charts/series/series-interact.ts +86 -13
- package/src/ts/charts/series/series-render.ts +660 -17
- package/src/ts/charts/series/series.ts +96 -26
- package/src/ts/charts/sunburst/sunburst-interact.ts +2 -3
- package/src/ts/charts/sunburst/sunburst-render.ts +3 -1
- package/src/ts/charts/treemap/treemap-interact.ts +2 -5
- package/src/ts/charts/treemap/treemap-render.ts +3 -1
- package/src/ts/charts/treemap/treemap.ts +6 -1
- package/src/ts/config.ts +21 -1
- package/src/ts/data/view-reader.ts +22 -2
- package/src/ts/event-detail.ts +8 -0
- package/src/ts/index.ts +13 -7
- package/src/ts/interaction/raw-event-forwarder.ts +7 -0
- package/src/ts/interaction/zoom-controller.ts +8 -0
- package/src/ts/layout/ticks.ts +9 -0
- package/src/ts/plugin/charts.ts +20 -4
- package/src/ts/plugin/plugin.ts +157 -23
- package/src/ts/render/scheduler.ts +164 -55
- package/src/ts/transport/protocol.ts +33 -0
- package/src/ts/transport/renderer-transport.ts +185 -9
- package/src/ts/webgl/context-manager.ts +195 -189
- package/src/ts/webgl/context-pool.ts +78 -0
- package/src/ts/webgl/gl-context.ts +191 -0
- package/src/ts/webgl/instanced-attrs.ts +15 -1
- package/src/ts/worker/dispatch.ts +4 -1
- package/src/ts/worker/renderer.worker.ts +94 -22
- package/src/ts/worker/session-host.ts +29 -0
|
@@ -84,7 +84,11 @@ interface RendererHandle {
|
|
|
84
84
|
terminate(): void;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
type PendingRenderType =
|
|
87
|
+
type PendingRenderType =
|
|
88
|
+
| "saveZoom"
|
|
89
|
+
| "loadAndRender"
|
|
90
|
+
| "snapshotPng"
|
|
91
|
+
| "resize";
|
|
88
92
|
interface PendingRenderRequest {
|
|
89
93
|
kind: PendingRenderType;
|
|
90
94
|
resolve: (v: any) => void;
|
|
@@ -110,11 +114,22 @@ interface PendingRenderRequest {
|
|
|
110
114
|
*/
|
|
111
115
|
export class RendererTransport {
|
|
112
116
|
private _handle: RendererHandle | null = null;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Set by {@link destroy}. Requests allocated after teardown settle
|
|
120
|
+
* immediately in `_allocPending` — `_post` is already a no-op once
|
|
121
|
+
* `_handle` is null, so a late request would otherwise never
|
|
122
|
+
* receive its reply and pend forever.
|
|
123
|
+
*/
|
|
124
|
+
private _destroyed = false;
|
|
113
125
|
private _proxyChannel: MessageChannel | null = null;
|
|
114
126
|
private _proxySession: any = null;
|
|
115
127
|
private _client: Client;
|
|
116
128
|
private _view: View;
|
|
117
129
|
private _tableName: string | undefined;
|
|
130
|
+
/** Source panel `slot`, tagged onto dispatched interaction events so the
|
|
131
|
+
* host can route them (master/detail, global filter) to the right panel. */
|
|
132
|
+
private _panel: string | undefined;
|
|
118
133
|
private _clientWorkerURL: URL;
|
|
119
134
|
private _clientWasm: WebAssembly.Module;
|
|
120
135
|
private _chartTag: string;
|
|
@@ -158,6 +173,37 @@ export class RendererTransport {
|
|
|
158
173
|
*/
|
|
159
174
|
private _displayCtx: CanvasRenderingContext2D | null = null;
|
|
160
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Present-hold state for the staged-present presize protocol
|
|
178
|
+
* ({@link presize}). While holding, inbound `frameBitmap`s are
|
|
179
|
+
* STAGED (latest wins) instead of blitted, so the on-screen frame
|
|
180
|
+
* — old dimensions in the old, unchanged box — stays put until the
|
|
181
|
+
* host invokes the present closure `presize` resolved to, in the
|
|
182
|
+
* same task as its layout commit. Entered only by `presize`;
|
|
183
|
+
* exited by the present closure, by `presize`'s rejection path, or
|
|
184
|
+
* by `destroy` — every settle path releases the hold or hands the
|
|
185
|
+
* caller the release.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* The last `(cssWidth, cssHeight, dpr)` posted to the worker — at init
|
|
189
|
+
* or any resize — i.e. the dimensions the worker has already rendered
|
|
190
|
+
* (or has an in-flight render for). {@link resize} short-circuits when
|
|
191
|
+
* its measurement matches (±0.5px, the presize protocol's own
|
|
192
|
+
* threshold): reactive resizes fan in from several host paths per
|
|
193
|
+
* layout transition (the post-commit fan-out, activation nudges, the
|
|
194
|
+
* fresh-restore tail), and every worker resize is an unconditional
|
|
195
|
+
* full re-render.
|
|
196
|
+
*/
|
|
197
|
+
private _lastPostedSize: {
|
|
198
|
+
cssWidth: number;
|
|
199
|
+
cssHeight: number;
|
|
200
|
+
dpr: number;
|
|
201
|
+
} | null = null;
|
|
202
|
+
|
|
203
|
+
private _holdPresent = false;
|
|
204
|
+
|
|
205
|
+
private _stagedFrame: ImageBitmap | null = null;
|
|
206
|
+
|
|
161
207
|
/**
|
|
162
208
|
* Host-side sink for tooltip + cursor side-effects. The chart
|
|
163
209
|
* inside the renderer calls into a `MessageHostSink` that posts
|
|
@@ -182,6 +228,7 @@ export class RendererTransport {
|
|
|
182
228
|
client: Client;
|
|
183
229
|
view: View;
|
|
184
230
|
tableName?: string;
|
|
231
|
+
panel?: string;
|
|
185
232
|
clientWasm: WebAssembly.Module;
|
|
186
233
|
clientWorkerURL: URL;
|
|
187
234
|
chartTag: string;
|
|
@@ -192,6 +239,7 @@ export class RendererTransport {
|
|
|
192
239
|
this._client = opts.client;
|
|
193
240
|
this._view = opts.view;
|
|
194
241
|
this._tableName = opts.tableName;
|
|
242
|
+
this._panel = opts.panel;
|
|
195
243
|
this._clientWorkerURL = opts.clientWorkerURL;
|
|
196
244
|
this._clientWasm = opts.clientWasm;
|
|
197
245
|
this._chartTag = opts.chartTag;
|
|
@@ -301,6 +349,11 @@ export class RendererTransport {
|
|
|
301
349
|
precompileShaders: this._precompileShaders,
|
|
302
350
|
};
|
|
303
351
|
|
|
352
|
+
this._lastPostedSize = {
|
|
353
|
+
cssWidth: rect.width,
|
|
354
|
+
cssHeight: rect.height,
|
|
355
|
+
dpr,
|
|
356
|
+
};
|
|
304
357
|
this._handle = await this._createHandle(workerURL, initMsg);
|
|
305
358
|
this._handle.addMessageListener((msg) =>
|
|
306
359
|
this._handleRendererMsg(msg as WorkerMsg),
|
|
@@ -438,7 +491,8 @@ export class RendererTransport {
|
|
|
438
491
|
* `loadAndRenderAck`. Per the worker's "resolve on stale"
|
|
439
492
|
* contract, a mid-flight cancellation (a newer `loadAndRender`
|
|
440
493
|
* superseding this one) still acks — the host's awaiter just
|
|
441
|
-
* resolves quietly.
|
|
494
|
+
* resolves quietly. Calls issued after `destroy()` resolve
|
|
495
|
+
* immediately (see `_allocPending`).
|
|
442
496
|
*/
|
|
443
497
|
loadAndRender(opts: {
|
|
444
498
|
viewerConfig: {
|
|
@@ -464,19 +518,96 @@ export class RendererTransport {
|
|
|
464
518
|
this._post({ kind: "redraw" });
|
|
465
519
|
}
|
|
466
520
|
|
|
467
|
-
|
|
521
|
+
/**
|
|
522
|
+
* Silently clear the worker chart's selection state (pinned tooltip) —
|
|
523
|
+
* no selection events are emitted; the worker echoes `dismissTooltip`
|
|
524
|
+
* to drop the host-side pinned artifact. See `DeselectMsg`.
|
|
525
|
+
*/
|
|
526
|
+
deselect(): void {
|
|
527
|
+
this._post({ kind: "deselect" });
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Resize the chart to the GL canvas's CURRENT CSS box and resolve
|
|
532
|
+
* once the resized frame has PRESENTED (the worker's `resizeAck`) —
|
|
533
|
+
* not at message-post, which would let callers proceed while the
|
|
534
|
+
* old-dimensions bitmap is still on screen. Reactive geometry
|
|
535
|
+
* changes only; anticipated ones go through {@link presize}.
|
|
536
|
+
*
|
|
537
|
+
* A measurement matching {@link _lastPostedSize} resolves immediately
|
|
538
|
+
* WITHOUT a worker round trip: the worker has already rendered (or is
|
|
539
|
+
* presenting) that exact frame, and re-posting would be a full
|
|
540
|
+
* re-render of an identical bitmap.
|
|
541
|
+
*/
|
|
542
|
+
resize(): Promise<void> {
|
|
468
543
|
if (!this._hostGlCanvas) {
|
|
469
|
-
return;
|
|
544
|
+
return Promise.resolve();
|
|
470
545
|
}
|
|
471
546
|
|
|
472
547
|
const rect = this._hostGlCanvas.getBoundingClientRect();
|
|
473
548
|
const dpr = window.devicePixelRatio || 1;
|
|
549
|
+
const last = this._lastPostedSize;
|
|
550
|
+
if (
|
|
551
|
+
last &&
|
|
552
|
+
Math.abs(last.cssWidth - rect.width) <= 0.5 &&
|
|
553
|
+
Math.abs(last.cssHeight - rect.height) <= 0.5 &&
|
|
554
|
+
last.dpr === dpr
|
|
555
|
+
) {
|
|
556
|
+
return Promise.resolve();
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return this._postResize(rect.width, rect.height);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Staged-present variant of {@link resize}: render at the given
|
|
564
|
+
* TARGET canvas CSS box (no measurement — the caller supplies the
|
|
565
|
+
* box the pending layout commit will produce), HOLDING the
|
|
566
|
+
* resulting frame (and any others that arrive meanwhile) offscreen
|
|
567
|
+
* instead of blitting, so nothing on screen changes. Resolves,
|
|
568
|
+
* once the resized frame is staged, to a present closure: calling
|
|
569
|
+
* it blits the latest staged frame and exits the hold — the host
|
|
570
|
+
* runs it in the same task as its layout commit so geometry and
|
|
571
|
+
* pixels land in one paint. On rejection (teardown) the hold is
|
|
572
|
+
* released here, so every settle path either frees the display or
|
|
573
|
+
* hands the caller the release.
|
|
574
|
+
*/
|
|
575
|
+
presize(cssWidth: number, cssHeight: number): Promise<(() => void) | void> {
|
|
576
|
+
if (!this._hostGlCanvas) {
|
|
577
|
+
return Promise.resolve();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
this._holdPresent = true;
|
|
581
|
+
return this._postResize(cssWidth, cssHeight).then(
|
|
582
|
+
() => () => this._presentStaged(),
|
|
583
|
+
(err) => {
|
|
584
|
+
this._presentStaged();
|
|
585
|
+
throw err;
|
|
586
|
+
},
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private _postResize(cssWidth: number, cssHeight: number): Promise<void> {
|
|
591
|
+
const dpr = window.devicePixelRatio || 1;
|
|
592
|
+
const { id, promise } = this._allocPending<void>("resize");
|
|
593
|
+
this._lastPostedSize = { cssWidth, cssHeight, dpr };
|
|
474
594
|
this._post({
|
|
475
595
|
kind: "resize",
|
|
476
|
-
|
|
477
|
-
|
|
596
|
+
msgId: id,
|
|
597
|
+
cssWidth,
|
|
598
|
+
cssHeight,
|
|
478
599
|
dpr,
|
|
479
600
|
});
|
|
601
|
+
return promise;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private _presentStaged(): void {
|
|
605
|
+
this._holdPresent = false;
|
|
606
|
+
const staged = this._stagedFrame;
|
|
607
|
+
this._stagedFrame = null;
|
|
608
|
+
if (staged) {
|
|
609
|
+
this._drawFrameBitmap(staged);
|
|
610
|
+
}
|
|
480
611
|
}
|
|
481
612
|
|
|
482
613
|
clear() {
|
|
@@ -501,12 +632,39 @@ export class RendererTransport {
|
|
|
501
632
|
* Allocate a pending request slot of the given `kind`. Returns the
|
|
502
633
|
* id (encoded into the outgoing `ControlMsg`) and a promise that
|
|
503
634
|
* resolves / rejects when the matching reply arrives or
|
|
504
|
-
* `destroy()` drains the table.
|
|
635
|
+
* `destroy()` drains the table. Requests issued *after* `destroy()`
|
|
636
|
+
* settle immediately with the same per-kind semantics as the drain.
|
|
505
637
|
*/
|
|
506
638
|
private _allocPending<T>(kind: PendingRenderType): {
|
|
507
639
|
id: number;
|
|
508
640
|
promise: Promise<T>;
|
|
509
641
|
} {
|
|
642
|
+
// A request issued after `destroy()` would never settle: `_post`
|
|
643
|
+
// no-ops with no handle, so no reply ever lands — and the host
|
|
644
|
+
// awaits `loadAndRender` while holding its per-renderer draw
|
|
645
|
+
// lock, so a pending-forever promise wedges that panel's lock
|
|
646
|
+
// permanently. (Reachable when an external DOM disconnect
|
|
647
|
+
// `delete()`s the element mid-draw; the host's own teardown
|
|
648
|
+
// paths are serialized behind the same lock.) Mirror the
|
|
649
|
+
// `destroy()` drain: `loadAndRender` resolves silently,
|
|
650
|
+
// reply-bearing kinds reject. The pre-attached no-op `catch`
|
|
651
|
+
// marks the rejection handled for fire-and-forget callers
|
|
652
|
+
// (`saveZoom`) without affecting real awaiters.
|
|
653
|
+
if (this._destroyed) {
|
|
654
|
+
if (kind === "loadAndRender") {
|
|
655
|
+
return {
|
|
656
|
+
id: -1,
|
|
657
|
+
promise: Promise.resolve(undefined as unknown as T),
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const promise = Promise.reject<T>(
|
|
662
|
+
new Error("RendererTransport destroyed"),
|
|
663
|
+
);
|
|
664
|
+
promise.catch(() => {});
|
|
665
|
+
return { id: -1, promise };
|
|
666
|
+
}
|
|
667
|
+
|
|
510
668
|
const id = ++this._pendingCounter;
|
|
511
669
|
const promise = new Promise<T>((resolve, reject) => {
|
|
512
670
|
this._pending.set(id, { kind, resolve, reject });
|
|
@@ -548,6 +706,7 @@ export class RendererTransport {
|
|
|
548
706
|
}
|
|
549
707
|
|
|
550
708
|
destroy(): void {
|
|
709
|
+
this._destroyed = true;
|
|
551
710
|
this._post({ kind: "destroy" });
|
|
552
711
|
if (this._proxySession) {
|
|
553
712
|
this._proxySession.close().catch(() => {});
|
|
@@ -572,6 +731,9 @@ export class RendererTransport {
|
|
|
572
731
|
// them, and so the GPU-backed 2D context can release earlier.
|
|
573
732
|
this._hostGlCanvas = null;
|
|
574
733
|
this._displayCtx = null;
|
|
734
|
+
this._holdPresent = false;
|
|
735
|
+
this._stagedFrame?.close();
|
|
736
|
+
this._stagedFrame = null;
|
|
575
737
|
|
|
576
738
|
// Drain pending request promises with kind-aware semantics:
|
|
577
739
|
// - `loadAndRender` resolves silently (the host's awaited draw
|
|
@@ -633,7 +795,9 @@ export class RendererTransport {
|
|
|
633
795
|
{
|
|
634
796
|
bubbles: true,
|
|
635
797
|
composed: true,
|
|
636
|
-
|
|
798
|
+
// Tag with the source panel so a multi-panel host
|
|
799
|
+
// can attribute the click to the right panel.
|
|
800
|
+
detail: { ...msg.detail, panel: this._panel },
|
|
637
801
|
},
|
|
638
802
|
),
|
|
639
803
|
);
|
|
@@ -658,6 +822,9 @@ export class RendererTransport {
|
|
|
658
822
|
removeConfigs as any,
|
|
659
823
|
insertConfigs as any,
|
|
660
824
|
);
|
|
825
|
+
// Tag with the source panel so the host's master/detail +
|
|
826
|
+
// global-filter routing can identify the originating panel.
|
|
827
|
+
detail.panel = this._panel;
|
|
661
828
|
this._dispatchOnViewer(
|
|
662
829
|
new CustomEvent<PerspectiveSelectDetail>(
|
|
663
830
|
"perspective-global-filter",
|
|
@@ -672,7 +839,13 @@ export class RendererTransport {
|
|
|
672
839
|
}
|
|
673
840
|
|
|
674
841
|
case "frameBitmap":
|
|
675
|
-
this.
|
|
842
|
+
if (this._holdPresent) {
|
|
843
|
+
this._stagedFrame?.close();
|
|
844
|
+
this._stagedFrame = msg.bitmap;
|
|
845
|
+
} else {
|
|
846
|
+
this._drawFrameBitmap(msg.bitmap);
|
|
847
|
+
}
|
|
848
|
+
|
|
676
849
|
break;
|
|
677
850
|
case "error":
|
|
678
851
|
this._rejectReady(new Error(msg.message));
|
|
@@ -680,6 +853,9 @@ export class RendererTransport {
|
|
|
680
853
|
case "loadAndRenderAck":
|
|
681
854
|
this._resolvePending(msg.msgId, "loadAndRender", undefined);
|
|
682
855
|
break;
|
|
856
|
+
case "resizeAck":
|
|
857
|
+
this._resolvePending(msg.msgId, "resize", undefined);
|
|
858
|
+
break;
|
|
683
859
|
case "snapshotPngReply":
|
|
684
860
|
this._resolvePending(msg.requestId, "snapshotPng", msg.blob);
|
|
685
861
|
break;
|