@perspective-dev/viewer-charts 4.5.2 → 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/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 +31 -2
- package/dist/esm/render/scheduler.d.ts +11 -8
- package/dist/esm/transport/protocol.d.ts +31 -2
- package/dist/esm/transport/renderer-transport.d.ts +74 -3
- 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/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 +86 -16
- package/src/ts/render/scheduler.ts +39 -13
- package/src/ts/transport/protocol.ts +33 -0
- package/src/ts/transport/renderer-transport.ts +185 -9
- package/src/ts/webgl/instanced-attrs.ts +15 -1
- package/src/ts/worker/dispatch.ts +4 -1
- package/src/ts/worker/renderer.worker.ts +32 -3
|
@@ -30,9 +30,12 @@ export interface ChartTypeConfig {
|
|
|
30
30
|
* Per-chart-type overrides for `DEFAULT_PLUGIN_CONFIG`. Used when a
|
|
31
31
|
* field's sensible default differs by chart family — currently
|
|
32
32
|
* `include_zero` (true for Y Bar / Y Area / X Bar, false for line
|
|
33
|
-
* / scatter / cartesian / financial)
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* / scatter / cartesian / financial) and `facet_mode` ("overlay"
|
|
34
|
+
* for the band-pipeline families — series / financial — whose
|
|
35
|
+
* historical split_by rendering is the single stacked/colored
|
|
36
|
+
* plot; "grid" elsewhere). Applied at schema generation and at
|
|
37
|
+
* `restore({})` so the effective default matches the surfaced UI
|
|
38
|
+
* default.
|
|
36
39
|
*/
|
|
37
40
|
plugin_field_defaults?: Partial<PluginConfig>;
|
|
38
41
|
}
|
|
@@ -10,7 +10,6 @@ export declare class HTMLPerspectiveViewerWebGLPluginElement extends HTMLElement
|
|
|
10
10
|
private _renderer;
|
|
11
11
|
private _rendererPromise;
|
|
12
12
|
private _rawEventForwarder;
|
|
13
|
-
private _generation;
|
|
14
13
|
private _resetClickAbort;
|
|
15
14
|
/**
|
|
16
15
|
* Plugin-scoped global config. Seeded lazily from
|
|
@@ -97,10 +96,40 @@ export declare class HTMLPerspectiveViewerWebGLPluginElement extends HTMLElement
|
|
|
97
96
|
};
|
|
98
97
|
draw(view: View): Promise<void>;
|
|
99
98
|
update(view: View): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Shared body of `draw` / `update`. No re-entrancy guard: the host
|
|
101
|
+
* serializes every rendering call (`draw`, `update`, `render`,
|
|
102
|
+
* `resize`) and `delete` on its per-renderer draw lock, so this
|
|
103
|
+
* method never overlaps itself or a teardown. The one exception —
|
|
104
|
+
* an external DOM disconnect `delete()`ing the element mid-draw —
|
|
105
|
+
* is absorbed by `RendererTransport`, whose post-`destroy()`
|
|
106
|
+
* requests settle immediately instead of pending forever.
|
|
107
|
+
*/
|
|
100
108
|
private _drawImpl;
|
|
101
109
|
clear(): Promise<void>;
|
|
102
110
|
resize(): Promise<void>;
|
|
103
|
-
|
|
111
|
+
/**
|
|
112
|
+
* OPTIONAL host presize protocol: render at the TARGET element box
|
|
113
|
+
* `(width, height)` — the box the host's pending layout commit will
|
|
114
|
+
* produce — holding the resulting frame offscreen, so nothing on
|
|
115
|
+
* screen changes during the round-trip. Resolves, once the resized
|
|
116
|
+
* frame is staged, to a present closure; the host calls it in the
|
|
117
|
+
* same task as the layout commit, landing geometry and pixels in
|
|
118
|
+
* one paint. Plugins without this method get the host's held
|
|
119
|
+
* style-override presize path instead.
|
|
120
|
+
*/
|
|
121
|
+
presize(width: number, height: number): Promise<(() => void) | void>;
|
|
122
|
+
restyle(): void;
|
|
123
|
+
/**
|
|
124
|
+
* Clear any active selection state (pinned tooltip) WITHOUT emitting
|
|
125
|
+
* selection events — the host calls this when a global filter
|
|
126
|
+
* contributed by this panel's selection is removed from the global
|
|
127
|
+
* filter bar, so the pin can't outlive the filter it produced. The
|
|
128
|
+
* transport's `_lastInsertConfig` (remove-set memory) is deliberately
|
|
129
|
+
* RETAINED so a subsequent selection still replaces any leftover
|
|
130
|
+
* clauses rather than accumulating.
|
|
131
|
+
*/
|
|
132
|
+
deselect(): Promise<void>;
|
|
104
133
|
render(view: View): Promise<Blob>;
|
|
105
134
|
restore(config: any, columns_config?: Record<string, any>): void;
|
|
106
135
|
delete(): void;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { WebGLContextManager } from "../webgl/context-manager";
|
|
2
2
|
/**
|
|
3
|
-
* Request a coalesced render of `glManager`
|
|
4
|
-
*
|
|
5
|
-
* Phase 2
|
|
3
|
+
* Request a coalesced render of `glManager`. `fullRender` submits the
|
|
4
|
+
* GL commands (Phase 1, synchronous); `render2D` draws the 2D layers
|
|
5
|
+
* (gridlines + chrome) and runs in Phase 2 *after* `awaitGpuFence`, so
|
|
6
|
+
* the 2D canvases don't present ahead of the GL frame. Returns a
|
|
7
|
+
* promise that resolves when this entry's Phase 2 (`awaitGpuFence` +
|
|
8
|
+
* `render2D` + `endFrame`) completes.
|
|
6
9
|
*
|
|
7
10
|
* If a request is already pending for the same glManager, the new
|
|
8
|
-
* call's `fullRender`
|
|
9
|
-
* wins; closures read chart state lazily so this is
|
|
10
|
-
* no-op, but keeps the
|
|
11
|
-
* resolves alongside the existing waiters.
|
|
11
|
+
* call's `fullRender` / `render2D` closures replace the prior ones
|
|
12
|
+
* (latest call wins; closures read chart state lazily so this is
|
|
13
|
+
* functionally a no-op, but keeps the closures fresh) and the returned
|
|
14
|
+
* promise resolves alongside the existing waiters.
|
|
12
15
|
*/
|
|
13
|
-
export declare function requestRender(glManager: WebGLContextManager, fullRender: () => void): Promise<void>;
|
|
16
|
+
export declare function requestRender(glManager: WebGLContextManager, fullRender: () => void, render2D: () => void): Promise<void>;
|
|
14
17
|
/**
|
|
15
18
|
* Run `op` synchronously if no drain `present()` is currently active
|
|
16
19
|
* for `glManager`. Otherwise queue `op` to run as soon as that
|
|
@@ -9,8 +9,8 @@ export type { ThemeSnapshot };
|
|
|
9
9
|
* host's real `Client` — that's pure protobuf bytes; this one is the
|
|
10
10
|
* chart's own renderer control plane.
|
|
11
11
|
*/
|
|
12
|
-
export type ControlMsg = InitMsg | SetViewByNameMsg | SetColumnsConfigMsg | SetPluginConfigMsg | SetBufferMaxCapacityMsg | LoadAndRenderMsg | RedrawMsg | ResizeMsg | ClearMsg | InvalidateThemeMsg | RestoreZoomMsg | ResetAllZoomsMsg | ResetExpandedDomainMsg | SaveZoomReqMsg | SnapshotPngReqMsg | InteractionMsg | DestroyMsg;
|
|
13
|
-
export type WorkerMsg = ReadyMsg | ZoomChangedMsg | SaveZoomReplyMsg | SnapshotPngReplyMsg | PinTooltipMsg | DismissTooltipMsg | SetCursorMsg | UserClickMsg | UserSelectMsg | LoadAndRenderAckMsg | FrameBitmapMsg | ErrorMsg;
|
|
12
|
+
export type ControlMsg = InitMsg | SetViewByNameMsg | SetColumnsConfigMsg | SetPluginConfigMsg | SetBufferMaxCapacityMsg | LoadAndRenderMsg | RedrawMsg | DeselectMsg | ResizeMsg | ClearMsg | InvalidateThemeMsg | RestoreZoomMsg | ResetAllZoomsMsg | ResetExpandedDomainMsg | SaveZoomReqMsg | SnapshotPngReqMsg | InteractionMsg | DestroyMsg;
|
|
13
|
+
export type WorkerMsg = ReadyMsg | ZoomChangedMsg | SaveZoomReplyMsg | SnapshotPngReplyMsg | PinTooltipMsg | DismissTooltipMsg | SetCursorMsg | UserClickMsg | UserSelectMsg | LoadAndRenderAckMsg | ResizeAckMsg | FrameBitmapMsg | ErrorMsg;
|
|
14
14
|
/**
|
|
15
15
|
* Session-tagged envelopes for the shared-worker transport. Every
|
|
16
16
|
* message between the host and the shared `Worker` carries a numeric
|
|
@@ -230,12 +230,41 @@ export interface LoadAndRenderAckMsg {
|
|
|
230
230
|
export interface RedrawMsg {
|
|
231
231
|
kind: "redraw";
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Host → worker: silently clear the chart's selection state (pinned
|
|
235
|
+
* tooltip) — no `userSelect` reply, so the host filter-bar removal that
|
|
236
|
+
* triggered it can't echo back into the host's filter set. The worker's
|
|
237
|
+
* `TooltipController.dismiss()` posts `dismissTooltip` to tear down the
|
|
238
|
+
* host-side pinned artifact.
|
|
239
|
+
*/
|
|
240
|
+
export interface DeselectMsg {
|
|
241
|
+
kind: "deselect";
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Host → worker: resize the chart to a new CSS box. Replied with
|
|
245
|
+
* {@link ResizeAckMsg} AFTER the resized frame PRESENTS — the host's
|
|
246
|
+
* `plugin.resize()` promise is the viewer's presize contract ("the
|
|
247
|
+
* plugin has repainted at the target box when this resolves"), so an
|
|
248
|
+
* ack at message-receipt would let the settings-pane layout commit
|
|
249
|
+
* against the old-dimensions bitmap (the aspect-ratio warp).
|
|
250
|
+
*/
|
|
233
251
|
export interface ResizeMsg {
|
|
234
252
|
kind: "resize";
|
|
253
|
+
msgId: number;
|
|
235
254
|
cssWidth: number;
|
|
236
255
|
cssHeight: number;
|
|
237
256
|
dpr: number;
|
|
238
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Worker → host reply to a `ResizeMsg`, posted after the resized
|
|
260
|
+
* frame's present completes. Always sent — present failures and
|
|
261
|
+
* torn-down charts included — so the host's awaited promise resolves
|
|
262
|
+
* deterministically.
|
|
263
|
+
*/
|
|
264
|
+
export interface ResizeAckMsg {
|
|
265
|
+
kind: "resizeAck";
|
|
266
|
+
msgId: number;
|
|
267
|
+
}
|
|
239
268
|
export interface ClearMsg {
|
|
240
269
|
kind: "clear";
|
|
241
270
|
}
|
|
@@ -20,11 +20,21 @@ import type { InteractionEvent } from "./protocol";
|
|
|
20
20
|
*/
|
|
21
21
|
export declare class RendererTransport {
|
|
22
22
|
private _handle;
|
|
23
|
+
/**
|
|
24
|
+
* Set by {@link destroy}. Requests allocated after teardown settle
|
|
25
|
+
* immediately in `_allocPending` — `_post` is already a no-op once
|
|
26
|
+
* `_handle` is null, so a late request would otherwise never
|
|
27
|
+
* receive its reply and pend forever.
|
|
28
|
+
*/
|
|
29
|
+
private _destroyed;
|
|
23
30
|
private _proxyChannel;
|
|
24
31
|
private _proxySession;
|
|
25
32
|
private _client;
|
|
26
33
|
private _view;
|
|
27
34
|
private _tableName;
|
|
35
|
+
/** Source panel `slot`, tagged onto dispatched interaction events so the
|
|
36
|
+
* host can route them (master/detail, global filter) to the right panel. */
|
|
37
|
+
private _panel;
|
|
28
38
|
private _clientWorkerURL;
|
|
29
39
|
private _clientWasm;
|
|
30
40
|
private _chartTag;
|
|
@@ -63,6 +73,30 @@ export declare class RendererTransport {
|
|
|
63
73
|
* canvas's drawing buffer is the worker's transferred GL canvas).
|
|
64
74
|
*/
|
|
65
75
|
private _displayCtx;
|
|
76
|
+
/**
|
|
77
|
+
* Present-hold state for the staged-present presize protocol
|
|
78
|
+
* ({@link presize}). While holding, inbound `frameBitmap`s are
|
|
79
|
+
* STAGED (latest wins) instead of blitted, so the on-screen frame
|
|
80
|
+
* — old dimensions in the old, unchanged box — stays put until the
|
|
81
|
+
* host invokes the present closure `presize` resolved to, in the
|
|
82
|
+
* same task as its layout commit. Entered only by `presize`;
|
|
83
|
+
* exited by the present closure, by `presize`'s rejection path, or
|
|
84
|
+
* by `destroy` — every settle path releases the hold or hands the
|
|
85
|
+
* caller the release.
|
|
86
|
+
*/
|
|
87
|
+
/**
|
|
88
|
+
* The last `(cssWidth, cssHeight, dpr)` posted to the worker — at init
|
|
89
|
+
* or any resize — i.e. the dimensions the worker has already rendered
|
|
90
|
+
* (or has an in-flight render for). {@link resize} short-circuits when
|
|
91
|
+
* its measurement matches (±0.5px, the presize protocol's own
|
|
92
|
+
* threshold): reactive resizes fan in from several host paths per
|
|
93
|
+
* layout transition (the post-commit fan-out, activation nudges, the
|
|
94
|
+
* fresh-restore tail), and every worker resize is an unconditional
|
|
95
|
+
* full re-render.
|
|
96
|
+
*/
|
|
97
|
+
private _lastPostedSize;
|
|
98
|
+
private _holdPresent;
|
|
99
|
+
private _stagedFrame;
|
|
66
100
|
/**
|
|
67
101
|
* Host-side sink for tooltip + cursor side-effects. The chart
|
|
68
102
|
* inside the renderer calls into a `MessageHostSink` that posts
|
|
@@ -85,6 +119,7 @@ export declare class RendererTransport {
|
|
|
85
119
|
client: Client;
|
|
86
120
|
view: View;
|
|
87
121
|
tableName?: string;
|
|
122
|
+
panel?: string;
|
|
88
123
|
clientWasm: WebAssembly.Module;
|
|
89
124
|
clientWorkerURL: URL;
|
|
90
125
|
chartTag: string;
|
|
@@ -125,7 +160,8 @@ export declare class RendererTransport {
|
|
|
125
160
|
* `loadAndRenderAck`. Per the worker's "resolve on stale"
|
|
126
161
|
* contract, a mid-flight cancellation (a newer `loadAndRender`
|
|
127
162
|
* superseding this one) still acks — the host's awaiter just
|
|
128
|
-
* resolves quietly.
|
|
163
|
+
* resolves quietly. Calls issued after `destroy()` resolve
|
|
164
|
+
* immediately (see `_allocPending`).
|
|
129
165
|
*/
|
|
130
166
|
loadAndRender(opts: {
|
|
131
167
|
viewerConfig: {
|
|
@@ -138,7 +174,41 @@ export declare class RendererTransport {
|
|
|
138
174
|
};
|
|
139
175
|
}): Promise<void>;
|
|
140
176
|
redraw(): void;
|
|
141
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Silently clear the worker chart's selection state (pinned tooltip) —
|
|
179
|
+
* no selection events are emitted; the worker echoes `dismissTooltip`
|
|
180
|
+
* to drop the host-side pinned artifact. See `DeselectMsg`.
|
|
181
|
+
*/
|
|
182
|
+
deselect(): void;
|
|
183
|
+
/**
|
|
184
|
+
* Resize the chart to the GL canvas's CURRENT CSS box and resolve
|
|
185
|
+
* once the resized frame has PRESENTED (the worker's `resizeAck`) —
|
|
186
|
+
* not at message-post, which would let callers proceed while the
|
|
187
|
+
* old-dimensions bitmap is still on screen. Reactive geometry
|
|
188
|
+
* changes only; anticipated ones go through {@link presize}.
|
|
189
|
+
*
|
|
190
|
+
* A measurement matching {@link _lastPostedSize} resolves immediately
|
|
191
|
+
* WITHOUT a worker round trip: the worker has already rendered (or is
|
|
192
|
+
* presenting) that exact frame, and re-posting would be a full
|
|
193
|
+
* re-render of an identical bitmap.
|
|
194
|
+
*/
|
|
195
|
+
resize(): Promise<void>;
|
|
196
|
+
/**
|
|
197
|
+
* Staged-present variant of {@link resize}: render at the given
|
|
198
|
+
* TARGET canvas CSS box (no measurement — the caller supplies the
|
|
199
|
+
* box the pending layout commit will produce), HOLDING the
|
|
200
|
+
* resulting frame (and any others that arrive meanwhile) offscreen
|
|
201
|
+
* instead of blitting, so nothing on screen changes. Resolves,
|
|
202
|
+
* once the resized frame is staged, to a present closure: calling
|
|
203
|
+
* it blits the latest staged frame and exits the hold — the host
|
|
204
|
+
* runs it in the same task as its layout commit so geometry and
|
|
205
|
+
* pixels land in one paint. On rejection (teardown) the hold is
|
|
206
|
+
* released here, so every settle path either frees the display or
|
|
207
|
+
* hands the caller the release.
|
|
208
|
+
*/
|
|
209
|
+
presize(cssWidth: number, cssHeight: number): Promise<(() => void) | void>;
|
|
210
|
+
private _postResize;
|
|
211
|
+
private _presentStaged;
|
|
142
212
|
clear(): void;
|
|
143
213
|
invalidateTheme(): void;
|
|
144
214
|
saveZoom(): Promise<void>;
|
|
@@ -146,7 +216,8 @@ export declare class RendererTransport {
|
|
|
146
216
|
* Allocate a pending request slot of the given `kind`. Returns the
|
|
147
217
|
* id (encoded into the outgoing `ControlMsg`) and a promise that
|
|
148
218
|
* resolves / rejects when the matching reply arrives or
|
|
149
|
-
* `destroy()` drains the table.
|
|
219
|
+
* `destroy()` drains the table. Requests issued *after* `destroy()`
|
|
220
|
+
* settle immediately with the same per-kind semantics as the drain.
|
|
150
221
|
*/
|
|
151
222
|
private _allocPending;
|
|
152
223
|
restoreZoom(state: any): void;
|
|
@@ -34,11 +34,17 @@ export declare function createQuadCornerBuffer(gl: GL): WebGLBuffer;
|
|
|
34
34
|
* paint zero data). No-op return `true` when `attr` is negative
|
|
35
35
|
* (optimized-out attribute — drawing the rest is still valid).
|
|
36
36
|
*
|
|
37
|
+
* `firstInstance` byte-offsets the attribute pointer so the draw's
|
|
38
|
+
* instance 0 reads element `firstInstance` of the buffer — the WebGL
|
|
39
|
+
* substitute for a `baseInstance` draw parameter. Faceted renderers
|
|
40
|
+
* use it to draw a contiguous per-facet sub-range of a shared
|
|
41
|
+
* instance buffer.
|
|
42
|
+
*
|
|
37
43
|
* Render-path uses `peek`, never `getOrCreate`: the latter recreates
|
|
38
44
|
* with zero-initialized contents when `_totalCapacity` has grown past
|
|
39
45
|
* the current buffer, which would wipe the previous draw's vertex
|
|
40
46
|
* data and leave `drawArraysInstanced` to render zeros. See
|
|
41
47
|
* {@link BufferPool#peek} for the full rationale.
|
|
42
48
|
*/
|
|
43
|
-
export declare function bindInstancedFloatAttr(glManager: WebGLContextManager, instancing: Instancing, attr: number, name: string, components: number): boolean;
|
|
49
|
+
export declare function bindInstancedFloatAttr(glManager: WebGLContextManager, instancing: Instancing, attr: number, name: string, components: number, firstInstance?: number): boolean;
|
|
44
50
|
export {};
|
|
@@ -72,6 +72,18 @@ export declare class WorkerRenderer {
|
|
|
72
72
|
*/
|
|
73
73
|
loadAndRender(msg: LoadAndRenderMsg): Promise<void>;
|
|
74
74
|
redraw(): void;
|
|
75
|
+
/**
|
|
76
|
+
* `redraw`, acked to the host AFTER the frame's present completes —
|
|
77
|
+
* the reply backing the viewer's presize contract (`ResizeAckMsg`):
|
|
78
|
+
* `plugin.resize()` must not resolve until the resized frame is
|
|
79
|
+
* actually on screen. A resize landing mid-present defers its
|
|
80
|
+
* canvas mutation (`deferIfDraining`), but the redraw's own frame
|
|
81
|
+
* applies pending dimensions in `beginFrame`, so the present this
|
|
82
|
+
* awaits IS at the new size. Always acks — a failed present or a
|
|
83
|
+
* torn-down chart resolves the host's await rather than stranding
|
|
84
|
+
* it (completion, not success, is the contract).
|
|
85
|
+
*/
|
|
86
|
+
redrawAck(msgId: number): void;
|
|
75
87
|
resize(cssWidth: number, cssHeight: number, dpr: number): void;
|
|
76
88
|
clear(): void;
|
|
77
89
|
saveZoom(): any;
|
package/package.json
CHANGED
|
@@ -224,7 +224,10 @@ export function renderCandlestickFrame(
|
|
|
224
224
|
chart._lastCatTicks = numericCat
|
|
225
225
|
? computeNiceTicks(vis.xMin, vis.xMax, 6)
|
|
226
226
|
: null;
|
|
227
|
-
|
|
227
|
+
// Deferred past the GPU fence (see `_defer2D`) so the chrome canvas
|
|
228
|
+
// doesn't present ahead of the GL glyphs on resize. Reads the
|
|
229
|
+
// `_last*` frame state set above.
|
|
230
|
+
chart._defer2D(() => renderCandlestickChromeOverlay(chart));
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
export function renderCandlestickChromeOverlay(chart: CandlestickChart): void {
|
|
@@ -92,15 +92,16 @@ export class CandlestickChart extends CategoricalYChart {
|
|
|
92
92
|
_yDomain: { min: number; max: number } = { min: 0, max: 1 };
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* `domain_mode: "expand"`
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
95
|
+
* `domain_mode: "expand"` accumulator — the VALUE (Y) axis only.
|
|
96
|
+
* The category axis has NO accumulator on band charts (see
|
|
97
|
+
* `SeriesChart._expandedLeftDomain`): a numeric/datetime `group_by`
|
|
98
|
+
* axis always fits the current data. Cleared in
|
|
99
|
+
* `resetExpandedDomain` — wired from the worker's `resetAllZooms`
|
|
100
|
+
* and from view-config mutations on `AbstractChart`. `null`
|
|
101
|
+
* whenever the option is `"fit"` or the accumulator has just been
|
|
102
|
+
* cleared.
|
|
101
103
|
*/
|
|
102
104
|
_expandedYDomain: { min: number; max: number } | null = null;
|
|
103
|
-
_expandedCategoryDomain: { min: number; max: number } | null = null;
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
107
|
* Numeric category-axis state (single non-string group_by).
|
|
@@ -267,22 +268,17 @@ export class CandlestickChart extends CategoricalYChart {
|
|
|
267
268
|
scratchCandles: this._candles,
|
|
268
269
|
});
|
|
269
270
|
// `domain_mode: "expand"` post-build union — mirrors the series
|
|
270
|
-
// pipeline
|
|
271
|
-
//
|
|
271
|
+
// pipeline: VALUE (Y) axis only, never the category axis (see
|
|
272
|
+
// `SeriesChart.uploadAndRender`). `expandDomainInPlace` mutates
|
|
273
|
+
// `result.yDomain` so the assignment below picks up the grown
|
|
274
|
+
// extent automatically.
|
|
272
275
|
if (this._pluginConfig.domain_mode === "expand") {
|
|
273
276
|
this._expandedYDomain = expandDomainInPlace(
|
|
274
277
|
this._expandedYDomain,
|
|
275
278
|
result.yDomain,
|
|
276
279
|
);
|
|
277
|
-
if (result.numericCategoryDomain) {
|
|
278
|
-
this._expandedCategoryDomain = expandDomainInPlace(
|
|
279
|
-
this._expandedCategoryDomain,
|
|
280
|
-
result.numericCategoryDomain,
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
280
|
} else {
|
|
284
281
|
this._expandedYDomain = null;
|
|
285
|
-
this._expandedCategoryDomain = null;
|
|
286
282
|
}
|
|
287
283
|
|
|
288
284
|
this._rowPaths = result.rowPaths;
|
|
@@ -320,7 +316,6 @@ export class CandlestickChart extends CategoricalYChart {
|
|
|
320
316
|
|
|
321
317
|
override resetExpandedDomain(): void {
|
|
322
318
|
this._expandedYDomain = null;
|
|
323
|
-
this._expandedCategoryDomain = null;
|
|
324
319
|
}
|
|
325
320
|
|
|
326
321
|
protected destroyInternal(): void {
|
|
@@ -204,7 +204,11 @@ export function renderCartesianFrame(
|
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
// Defer the chrome (axes/legend/tooltip) draw past the GPU fence so
|
|
208
|
+
// the chrome canvas doesn't present ahead of the GL glyphs on resize.
|
|
209
|
+
// `renderCartesianChromeOverlay` reads the `_last*` frame state set
|
|
210
|
+
// above, which stays stable until the next `_fullRender`.
|
|
211
|
+
chart._defer2D(() => renderCartesianChromeOverlay(chart));
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
interface RenderFrameCtx {
|
|
@@ -406,26 +410,25 @@ function renderSinglePlotFrame(
|
|
|
406
410
|
|
|
407
411
|
const isMap = chart._renderMode === "map";
|
|
408
412
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
theme,
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
} else if (chart._gridlineCanvas && isMap) {
|
|
413
|
+
// Defer the gridline draw past the GPU fence (see `_defer2D`) so the
|
|
414
|
+
// gridline canvas doesn't present ahead of the GL glyphs on resize.
|
|
415
|
+
// The closure captures this frame's `layout` / ticks / `theme`.
|
|
416
|
+
const dpr = glManager.dpr;
|
|
417
|
+
const gridlineCanvas = chart._gridlineCanvas;
|
|
418
|
+
if (gridlineCanvas && !isMap) {
|
|
419
|
+
chart._defer2D(() => {
|
|
420
|
+
// One-shot destructive prep (resizes + clears + scales to
|
|
421
|
+
// DPR). `renderGridlines` itself is non-destructive.
|
|
422
|
+
initCanvas(gridlineCanvas, layout, dpr);
|
|
423
|
+
renderGridlines(gridlineCanvas, layout, xTicks, yTicks, theme, dpr);
|
|
424
|
+
});
|
|
425
|
+
} else if (gridlineCanvas && isMap) {
|
|
423
426
|
// Map mode draws no cartesian gridlines, but the gridline
|
|
424
427
|
// canvas may carry stale ink from a prior cartesian chart
|
|
425
428
|
// type. Reset it to a clean transparent surface so the
|
|
426
429
|
// basemap (rendered into the GL canvas below) reads as the
|
|
427
430
|
// only background layer.
|
|
428
|
-
initCanvas(
|
|
431
|
+
chart._defer2D(() => initCanvas(gridlineCanvas, layout, dpr));
|
|
429
432
|
}
|
|
430
433
|
|
|
431
434
|
renderInPlotFrame(gl, layout, glManager.dpr, () => {
|
|
@@ -571,10 +574,17 @@ function renderFacetedFrame(
|
|
|
571
574
|
// One-shot destructive prep for the gridline + WebGL canvases.
|
|
572
575
|
// Both phases below are per-facet; calling their destructive
|
|
573
576
|
// helpers (initCanvas / renderInPlotFrame) in the loop would wipe
|
|
574
|
-
// every previously-drawn facet, leaving only the last cell
|
|
575
|
-
//
|
|
576
|
-
|
|
577
|
-
|
|
577
|
+
// every previously-drawn facet, leaving only the last cell visible.
|
|
578
|
+
//
|
|
579
|
+
// The gridline draws are deferred past the GPU fence (see
|
|
580
|
+
// `_defer2D`) so the gridline canvas doesn't present ahead of the GL
|
|
581
|
+
// glyphs on resize. `initCanvas` is deferred first so it runs before
|
|
582
|
+
// the per-facet `renderGridlines` closures below (FIFO flush order);
|
|
583
|
+
// the GL prep (`clearAndSetupFrame`) and draws stay in this pass.
|
|
584
|
+
const dpr = glManager.dpr;
|
|
585
|
+
const gridlineCanvas = chart._gridlineCanvas;
|
|
586
|
+
if (gridlineCanvas && sampleLayout) {
|
|
587
|
+
chart._defer2D(() => initCanvas(gridlineCanvas, sampleLayout, dpr));
|
|
578
588
|
}
|
|
579
589
|
|
|
580
590
|
clearAndSetupFrame(gl);
|
|
@@ -609,49 +619,56 @@ function renderFacetedFrame(
|
|
|
609
619
|
// basemap layer is rendered into the GL canvas inside the
|
|
610
620
|
// facet's scissor below.
|
|
611
621
|
const isMap = chart._renderMode === "map";
|
|
612
|
-
if (
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
622
|
+
if (gridlineCanvas && !isMap) {
|
|
623
|
+
// Deferred to the post-fence 2D flush. The closure captures
|
|
624
|
+
// this facet's `cell` (whose `cell.layout` already carries
|
|
625
|
+
// the padded domain from `buildProjectionMatrix` above) plus
|
|
626
|
+
// the frame-local domain/ticks, so it stays correct without
|
|
627
|
+
// recomputing at flush time.
|
|
628
|
+
chart._defer2D(() => {
|
|
629
|
+
const localXTicks = independent
|
|
630
|
+
? computeTicks(
|
|
631
|
+
buildXDomain(
|
|
632
|
+
chart,
|
|
633
|
+
facetDomain.xMin,
|
|
634
|
+
facetDomain.xMax,
|
|
635
|
+
xIsDate,
|
|
636
|
+
),
|
|
637
|
+
buildYDomain(
|
|
638
|
+
chart,
|
|
639
|
+
facetDomain.yMin,
|
|
640
|
+
facetDomain.yMax,
|
|
641
|
+
yIsDate,
|
|
642
|
+
),
|
|
643
|
+
cell.layout,
|
|
644
|
+
).xTicks
|
|
645
|
+
: xTicks;
|
|
646
|
+
const localYTicks = independent
|
|
647
|
+
? computeTicks(
|
|
648
|
+
buildXDomain(
|
|
649
|
+
chart,
|
|
650
|
+
facetDomain.xMin,
|
|
651
|
+
facetDomain.xMax,
|
|
652
|
+
xIsDate,
|
|
653
|
+
),
|
|
654
|
+
buildYDomain(
|
|
655
|
+
chart,
|
|
656
|
+
facetDomain.yMin,
|
|
657
|
+
facetDomain.yMax,
|
|
658
|
+
yIsDate,
|
|
659
|
+
),
|
|
660
|
+
cell.layout,
|
|
661
|
+
).yTicks
|
|
662
|
+
: yTicks;
|
|
663
|
+
renderGridlines(
|
|
664
|
+
gridlineCanvas,
|
|
665
|
+
cell.layout,
|
|
666
|
+
localXTicks,
|
|
667
|
+
localYTicks,
|
|
668
|
+
theme,
|
|
669
|
+
dpr,
|
|
670
|
+
);
|
|
671
|
+
});
|
|
655
672
|
}
|
|
656
673
|
|
|
657
674
|
withScissor(gl, cell.layout, glManager.dpr, () => {
|