@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
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
type SeriesChartRecord,
|
|
19
19
|
} from "./series-build";
|
|
20
20
|
import {
|
|
21
|
-
renderBarFrame,
|
|
22
21
|
uploadBarInstances,
|
|
23
22
|
rebuildGlyphBuffers,
|
|
24
23
|
rightAxisDataToPixel,
|
|
24
|
+
layoutForRecord,
|
|
25
25
|
} from "./series-render";
|
|
26
26
|
|
|
27
27
|
const POINT_HIT_RADIUS_PX = 10;
|
|
@@ -50,6 +50,12 @@ export function getHoveredBar(chart: SeriesChart): SeriesChartRecord | null {
|
|
|
50
50
|
* Handle mouse-move across all glyph types. Tests (in reverse paint order
|
|
51
51
|
* so top glyphs win): scatter points → line points → bars → areas.
|
|
52
52
|
* Updates `_hoveredBarIdx` or `_hoveredSample` and re-renders on change.
|
|
53
|
+
*
|
|
54
|
+
* Faceted frames first resolve the cell under the cursor: that cell's
|
|
55
|
+
* layout supplies the pixel→data mapping and its index becomes the
|
|
56
|
+
* `splitFilter` — records of other splits share the same band-slot
|
|
57
|
+
* coordinates by construction, so an unfiltered hit-test would match
|
|
58
|
+
* a bar from a different facet.
|
|
53
59
|
*/
|
|
54
60
|
export function handleBarHover(
|
|
55
61
|
chart: SeriesChart,
|
|
@@ -60,7 +66,33 @@ export function handleBarHover(
|
|
|
60
66
|
return;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
let layout = chart._lastLayout;
|
|
70
|
+
let splitFilter: number | undefined;
|
|
71
|
+
if (chart._facetGrid) {
|
|
72
|
+
const cells = chart._facetGrid.cells;
|
|
73
|
+
let found = -1;
|
|
74
|
+
for (let i = 0; i < cells.length; i++) {
|
|
75
|
+
const p = cells[i].layout.plotRect;
|
|
76
|
+
if (
|
|
77
|
+
mx >= p.x &&
|
|
78
|
+
mx <= p.x + p.width &&
|
|
79
|
+
my >= p.y &&
|
|
80
|
+
my <= p.y + p.height
|
|
81
|
+
) {
|
|
82
|
+
found = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (found < 0) {
|
|
88
|
+
clearHover(chart);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
layout = cells[found].layout;
|
|
93
|
+
splitFilter = found;
|
|
94
|
+
}
|
|
95
|
+
|
|
64
96
|
const plot = layout.plotRect;
|
|
65
97
|
|
|
66
98
|
if (
|
|
@@ -131,6 +163,7 @@ export function handleBarHover(
|
|
|
131
163
|
pxPerDataX,
|
|
132
164
|
pxPerDataYLeft,
|
|
133
165
|
pxPerDataYRight,
|
|
166
|
+
splitFilter,
|
|
134
167
|
);
|
|
135
168
|
|
|
136
169
|
// 2. Line points (still above bars; treat as point hits).
|
|
@@ -144,6 +177,7 @@ export function handleBarHover(
|
|
|
144
177
|
pxPerDataX,
|
|
145
178
|
pxPerDataYLeft,
|
|
146
179
|
pxPerDataYRight,
|
|
180
|
+
splitFilter,
|
|
147
181
|
);
|
|
148
182
|
}
|
|
149
183
|
|
|
@@ -158,6 +192,7 @@ export function handleBarHover(
|
|
|
158
192
|
const by1 = bars.y1;
|
|
159
193
|
const ax = bars.axis;
|
|
160
194
|
const hidden = chart._hiddenSeries;
|
|
195
|
+
const P = chart._splitPrefixes.length;
|
|
161
196
|
for (let i = 0; i < bars.count; i++) {
|
|
162
197
|
if (ct[i] !== BAR_TYPE_BAR) {
|
|
163
198
|
continue;
|
|
@@ -167,6 +202,10 @@ export function handleBarHover(
|
|
|
167
202
|
continue;
|
|
168
203
|
}
|
|
169
204
|
|
|
205
|
+
if (splitFilter !== undefined && sid[i] % P !== splitFilter) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
170
209
|
const xc = xC[i];
|
|
171
210
|
const halfW = hw[i];
|
|
172
211
|
if (dataX < xc - halfW || dataX > xc + halfW) {
|
|
@@ -187,7 +226,13 @@ export function handleBarHover(
|
|
|
187
226
|
|
|
188
227
|
// 4. Areas (strip hit — stacked records via `_bars`, unstacked via samples).
|
|
189
228
|
if (nextBarIdx < 0 && !nextSample) {
|
|
190
|
-
const areaHit = hitTestAreas(
|
|
229
|
+
const areaHit = hitTestAreas(
|
|
230
|
+
chart,
|
|
231
|
+
dataX,
|
|
232
|
+
dataYLeft,
|
|
233
|
+
dataYRight,
|
|
234
|
+
splitFilter,
|
|
235
|
+
);
|
|
191
236
|
if (areaHit) {
|
|
192
237
|
if (areaHit.idx >= 0) {
|
|
193
238
|
nextBarIdx = areaHit.idx;
|
|
@@ -209,6 +254,7 @@ function hitTestPoints(
|
|
|
209
254
|
pxPerDataX: number,
|
|
210
255
|
pxPerDataYLeft: number,
|
|
211
256
|
pxPerDataYRight: number,
|
|
257
|
+
splitFilter?: number,
|
|
212
258
|
): SeriesChartRecord | null {
|
|
213
259
|
const N = chart._numCategories;
|
|
214
260
|
const S = chart._series.length;
|
|
@@ -233,6 +279,10 @@ function hitTestPoints(
|
|
|
233
279
|
continue;
|
|
234
280
|
}
|
|
235
281
|
|
|
282
|
+
if (splitFilter !== undefined && s.splitIdx !== splitFilter) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
236
286
|
const dataY = s.axis === 1 ? dataYRight : dataYLeft;
|
|
237
287
|
const pyPerData = s.axis === 1 ? pxPerDataYRight : pxPerDataYLeft;
|
|
238
288
|
|
|
@@ -289,6 +339,7 @@ function hitTestAreas(
|
|
|
289
339
|
dataX: number,
|
|
290
340
|
dataYLeft: number,
|
|
291
341
|
dataYRight: number,
|
|
342
|
+
splitFilter?: number,
|
|
292
343
|
): { idx: number; bar: SeriesChartRecord | null } | null {
|
|
293
344
|
// Closest category to the mouse; an area covers every [cat - 0.5, cat + 0.5]
|
|
294
345
|
// slot, so use `round(dataX)` as the candidate index.
|
|
@@ -313,6 +364,7 @@ function hitTestAreas(
|
|
|
313
364
|
const ax = bars.axis;
|
|
314
365
|
const by0 = bars.y0;
|
|
315
366
|
const by1 = bars.y1;
|
|
367
|
+
const P = chart._splitPrefixes.length;
|
|
316
368
|
for (let i = 0; i < bars.count; i++) {
|
|
317
369
|
if (ct[i] !== BAR_TYPE_AREA) {
|
|
318
370
|
continue;
|
|
@@ -326,6 +378,10 @@ function hitTestAreas(
|
|
|
326
378
|
continue;
|
|
327
379
|
}
|
|
328
380
|
|
|
381
|
+
if (splitFilter !== undefined && sid[i] % P !== splitFilter) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
|
|
329
385
|
const dy = ax[i] === 0 ? dataYLeft : dataYRight;
|
|
330
386
|
const y0 = by0[i];
|
|
331
387
|
const y1 = by1[i];
|
|
@@ -346,6 +402,10 @@ function hitTestAreas(
|
|
|
346
402
|
continue;
|
|
347
403
|
}
|
|
348
404
|
|
|
405
|
+
if (splitFilter !== undefined && s.splitIdx !== splitFilter) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
349
409
|
const idx = cat * S + s.seriesId;
|
|
350
410
|
if (!((valid[idx >> 3] >> (idx & 7)) & 1)) {
|
|
351
411
|
continue;
|
|
@@ -383,7 +443,7 @@ function clearHover(chart: SeriesChart): void {
|
|
|
383
443
|
chart._hoveredBarIdx = -1;
|
|
384
444
|
chart._hoveredSample = null;
|
|
385
445
|
if (chart._glManager) {
|
|
386
|
-
|
|
446
|
+
chart.requestRender(chart._glManager);
|
|
387
447
|
}
|
|
388
448
|
}
|
|
389
449
|
}
|
|
@@ -405,13 +465,17 @@ function applyHover(
|
|
|
405
465
|
chart._hoveredBarIdx = nextBarIdx;
|
|
406
466
|
chart._hoveredSample = nextSample;
|
|
407
467
|
if (chart._glManager) {
|
|
408
|
-
|
|
468
|
+
chart.requestRender(chart._glManager);
|
|
409
469
|
}
|
|
410
470
|
}
|
|
411
471
|
|
|
412
472
|
/**
|
|
413
473
|
* Handle a click on the legend area. Returns true when the click hit a
|
|
414
474
|
* legend entry (the caller should then treat the event as consumed).
|
|
475
|
+
*
|
|
476
|
+
* An entry may own several series (facet-grid mode groups the legend
|
|
477
|
+
* by aggregate): the toggle is all-or-nothing — any visible member
|
|
478
|
+
* hides the whole group, a fully-hidden group shows every member.
|
|
415
479
|
*/
|
|
416
480
|
export function handleBarLegendClick(
|
|
417
481
|
chart: SeriesChart,
|
|
@@ -430,10 +494,15 @@ export function handleBarLegendClick(
|
|
|
430
494
|
my >= r.y &&
|
|
431
495
|
my <= r.y + r.height
|
|
432
496
|
) {
|
|
433
|
-
|
|
434
|
-
chart._hiddenSeries.
|
|
435
|
-
|
|
436
|
-
|
|
497
|
+
const anyVisible = entry.seriesIds.some(
|
|
498
|
+
(sid) => !chart._hiddenSeries.has(sid),
|
|
499
|
+
);
|
|
500
|
+
for (const sid of entry.seriesIds) {
|
|
501
|
+
if (anyVisible) {
|
|
502
|
+
chart._hiddenSeries.add(sid);
|
|
503
|
+
} else {
|
|
504
|
+
chart._hiddenSeries.delete(sid);
|
|
505
|
+
}
|
|
437
506
|
}
|
|
438
507
|
|
|
439
508
|
// Hidden-series change affects which bars contribute to
|
|
@@ -454,7 +523,7 @@ export function handleBarLegendClick(
|
|
|
454
523
|
if (chart._glManager) {
|
|
455
524
|
uploadBarInstances(chart, chart._glManager);
|
|
456
525
|
rebuildGlyphBuffers(chart, chart._glManager);
|
|
457
|
-
|
|
526
|
+
chart.requestRender(chart._glManager);
|
|
458
527
|
}
|
|
459
528
|
|
|
460
529
|
return true;
|
|
@@ -571,7 +640,11 @@ function pinTooltip(chart: SeriesChart, b: SeriesChartRecord): void {
|
|
|
571
640
|
return;
|
|
572
641
|
}
|
|
573
642
|
|
|
574
|
-
|
|
643
|
+
// Faceted frames anchor in the record's own cell.
|
|
644
|
+
const layout = layoutForRecord(chart, b);
|
|
645
|
+
if (!layout) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
575
648
|
|
|
576
649
|
// Anchor at the bar midpoint for bar glyphs (tooltip reads against
|
|
577
650
|
// the body); at the point itself (`y1`) for line / scatter / area.
|
|
@@ -582,7 +655,7 @@ function pinTooltip(chart: SeriesChart, b: SeriesChartRecord): void {
|
|
|
582
655
|
? chart._isHorizontal
|
|
583
656
|
? layout.dataToPixel(anchorV, b.xCenter)
|
|
584
657
|
: layout.dataToPixel(b.xCenter, anchorV)
|
|
585
|
-
: rightAxisDataToPixel(chart, b.xCenter, anchorV);
|
|
658
|
+
: rightAxisDataToPixel(chart, b.xCenter, anchorV, layout);
|
|
586
659
|
|
|
587
660
|
const lines = buildBarTooltipLines(chart, b);
|
|
588
661
|
if (lines.length === 0) {
|
|
@@ -594,7 +667,7 @@ function pinTooltip(chart: SeriesChart, b: SeriesChartRecord): void {
|
|
|
594
667
|
chart._hoveredBarIdx = -1;
|
|
595
668
|
chart._hoveredSample = null;
|
|
596
669
|
if (chart._glManager) {
|
|
597
|
-
|
|
670
|
+
chart.requestRender(chart._glManager);
|
|
598
671
|
}
|
|
599
672
|
}
|
|
600
673
|
|