@perspective-dev/viewer-charts 4.3.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/LICENSE.md +193 -0
- package/dist/cdn/perspective-viewer-charts.js +3 -0
- package/dist/cdn/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/axis/axis-primitives.d.ts +24 -0
- package/dist/esm/axis/bar-axis.d.ts +51 -0
- package/dist/esm/axis/canvas.d.ts +24 -0
- package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
- package/dist/esm/axis/categorical-axis.d.ts +27 -0
- package/dist/esm/axis/facet-chrome.d.ts +13 -0
- package/dist/esm/axis/label-geometry.d.ts +41 -0
- package/dist/esm/axis/legend.d.ts +44 -0
- package/dist/esm/axis/numeric-axis.d.ts +20 -0
- package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
- package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
- package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
- package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
- package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
- package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
- package/dist/esm/charts/canvas-types.d.ts +15 -0
- package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
- package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
- package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
- package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
- package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
- package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
- package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
- package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
- package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
- package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
- package/dist/esm/charts/chart-base.d.ts +402 -0
- package/dist/esm/charts/chart.d.ts +338 -0
- package/dist/esm/charts/common/band-layout.d.ts +32 -0
- package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
- package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
- package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
- package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
- package/dist/esm/charts/common/leaf-color.d.ts +33 -0
- package/dist/esm/charts/common/node-store.d.ts +81 -0
- package/dist/esm/charts/common/tree-chart.d.ts +48 -0
- package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
- package/dist/esm/charts/common/tree-data.d.ts +54 -0
- package/dist/esm/charts/common/visible-extent.d.ts +51 -0
- package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
- package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
- package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
- package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
- package/dist/esm/charts/map/map.d.ts +67 -0
- package/dist/esm/charts/registry.d.ts +14 -0
- package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
- package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
- package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
- package/dist/esm/charts/series/series-build.d.ts +228 -0
- package/dist/esm/charts/series/series-interact.d.ts +35 -0
- package/dist/esm/charts/series/series-render.d.ts +41 -0
- package/dist/esm/charts/series/series-type.d.ts +49 -0
- package/dist/esm/charts/series/series.d.ts +317 -0
- package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
- package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
- package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
- package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
- package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
- package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
- package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
- package/dist/esm/charts/treemap/treemap.d.ts +74 -0
- package/dist/esm/config.d.ts +27 -0
- package/dist/esm/data/lazy-row.d.ts +32 -0
- package/dist/esm/data/split-groups.d.ts +20 -0
- package/dist/esm/data/view-reader.d.ts +35 -0
- package/dist/esm/event-detail.d.ts +28 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/interaction/hit-test.d.ts +30 -0
- package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
- package/dist/esm/interaction/host-sink-message.d.ts +46 -0
- package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
- package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
- package/dist/esm/interaction/spatial-grid.d.ts +15 -0
- package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
- package/dist/esm/interaction/zoom-controller.d.ts +106 -0
- package/dist/esm/interaction/zoom-router.d.ts +48 -0
- package/dist/esm/layout/facet-grid.d.ts +126 -0
- package/dist/esm/layout/plot-layout.d.ts +104 -0
- package/dist/esm/layout/ticks.d.ts +17 -0
- package/dist/esm/map/mercator.d.ts +102 -0
- package/dist/esm/map/tile-cache.d.ts +38 -0
- package/dist/esm/map/tile-layer.d.ts +66 -0
- package/dist/esm/map/tile-loader.d.ts +52 -0
- package/dist/esm/map/tile-source.d.ts +66 -0
- package/dist/esm/perspective-viewer-charts.js +3 -0
- package/dist/esm/perspective-viewer-charts.js.map +7 -0
- package/dist/esm/plugin/charts.d.ts +40 -0
- package/dist/esm/plugin/plugin.d.ts +95 -0
- package/dist/esm/render/scheduler.d.ts +41 -0
- package/dist/esm/theme/gradient.d.ts +48 -0
- package/dist/esm/theme/palette.d.ts +13 -0
- package/dist/esm/theme/theme-snapshot.d.ts +7 -0
- package/dist/esm/theme/theme.d.ts +53 -0
- package/dist/esm/transport/protocol.d.ts +430 -0
- package/dist/esm/transport/renderer-transport.d.ts +201 -0
- package/dist/esm/utils/css.d.ts +1 -0
- package/dist/esm/utils/font-snapshot.d.ts +50 -0
- package/dist/esm/webgl/buffer-pool.d.ts +62 -0
- package/dist/esm/webgl/context-manager.d.ts +184 -0
- package/dist/esm/webgl/gradient-texture.d.ts +17 -0
- package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
- package/dist/esm/webgl/plot-frame.d.ts +39 -0
- package/dist/esm/webgl/program-cache.d.ts +13 -0
- package/dist/esm/webgl/shader-manifest.d.ts +53 -0
- package/dist/esm/webgl/shader-registry.d.ts +22 -0
- package/dist/esm/worker/boot.d.ts +0 -0
- package/dist/esm/worker/dispatch.d.ts +9 -0
- package/dist/esm/worker/font-loader.d.ts +2 -0
- package/dist/esm/worker/renderer.worker.d.ts +115 -0
- package/dist/esm/worker/session-host.d.ts +26 -0
- package/package.json +47 -0
- package/src/css/perspective-viewer-charts.css +95 -0
- package/src/ts/axis/axis-primitives.ts +125 -0
- package/src/ts/axis/bar-axis.ts +345 -0
- package/src/ts/axis/canvas.ts +64 -0
- package/src/ts/axis/categorical-axis-core.ts +125 -0
- package/src/ts/axis/categorical-axis.ts +716 -0
- package/src/ts/axis/facet-chrome.ts +42 -0
- package/src/ts/axis/label-geometry.ts +188 -0
- package/src/ts/axis/legend.ts +218 -0
- package/src/ts/axis/numeric-axis.ts +353 -0
- package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
- package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
- package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
- package/src/ts/charts/candlestick/candlestick.ts +367 -0
- package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
- package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
- package/src/ts/charts/canvas-types.ts +30 -0
- package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
- package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
- package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
- package/src/ts/charts/cartesian/cartesian.ts +469 -0
- package/src/ts/charts/cartesian/glyph.ts +81 -0
- package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
- package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
- package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
- package/src/ts/charts/cartesian/label-interner.ts +56 -0
- package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
- package/src/ts/charts/chart-base.ts +840 -0
- package/src/ts/charts/chart.ts +427 -0
- package/src/ts/charts/common/band-layout.ts +63 -0
- package/src/ts/charts/common/categorical-y-chart.ts +81 -0
- package/src/ts/charts/common/category-axis-resolver.ts +314 -0
- package/src/ts/charts/common/chrome-cache.ts +79 -0
- package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
- package/src/ts/charts/common/leaf-color.ts +92 -0
- package/src/ts/charts/common/node-store.ts +235 -0
- package/src/ts/charts/common/tree-chart.ts +76 -0
- package/src/ts/charts/common/tree-chrome.ts +123 -0
- package/src/ts/charts/common/tree-data.ts +623 -0
- package/src/ts/charts/common/visible-extent.ts +112 -0
- package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
- package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
- package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
- package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
- package/src/ts/charts/heatmap/heatmap.ts +368 -0
- package/src/ts/charts/map/map.ts +201 -0
- package/src/ts/charts/registry.ts +65 -0
- package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
- package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
- package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
- package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
- package/src/ts/charts/series/series-build.ts +848 -0
- package/src/ts/charts/series/series-interact.ts +604 -0
- package/src/ts/charts/series/series-render.ts +1109 -0
- package/src/ts/charts/series/series-type.ts +99 -0
- package/src/ts/charts/series/series.ts +794 -0
- package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
- package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
- package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
- package/src/ts/charts/sunburst/sunburst.ts +248 -0
- package/src/ts/charts/treemap/treemap-interact.ts +445 -0
- package/src/ts/charts/treemap/treemap-layout.ts +328 -0
- package/src/ts/charts/treemap/treemap-render.ts +886 -0
- package/src/ts/charts/treemap/treemap.ts +247 -0
- package/src/ts/config.ts +41 -0
- package/src/ts/data/lazy-row.ts +140 -0
- package/src/ts/data/split-groups.ts +97 -0
- package/src/ts/data/view-reader.ts +107 -0
- package/src/ts/event-detail.ts +44 -0
- package/src/ts/index.ts +53 -0
- package/src/ts/interaction/hit-test.ts +106 -0
- package/src/ts/interaction/host-sink-dom.ts +85 -0
- package/src/ts/interaction/host-sink-message.ts +75 -0
- package/src/ts/interaction/lazy-tooltip.ts +102 -0
- package/src/ts/interaction/raw-event-forwarder.ts +175 -0
- package/src/ts/interaction/spatial-grid.ts +100 -0
- package/src/ts/interaction/tooltip-controller.ts +407 -0
- package/src/ts/interaction/zoom-controller.ts +468 -0
- package/src/ts/interaction/zoom-router.ts +230 -0
- package/src/ts/layout/facet-grid.ts +346 -0
- package/src/ts/layout/plot-layout.ts +277 -0
- package/src/ts/layout/ticks.ts +168 -0
- package/src/ts/map/mercator.ts +204 -0
- package/src/ts/map/tile-cache.ts +96 -0
- package/src/ts/map/tile-layer.ts +382 -0
- package/src/ts/map/tile-loader.ts +143 -0
- package/src/ts/map/tile-source.ts +156 -0
- package/src/ts/plugin/charts.ts +286 -0
- package/src/ts/plugin/plugin.ts +668 -0
- package/src/ts/render/scheduler.ts +339 -0
- package/src/ts/shaders/area.frag.glsl +20 -0
- package/src/ts/shaders/area.vert.glsl +19 -0
- package/src/ts/shaders/bar.frag.glsl +25 -0
- package/src/ts/shaders/bar.vert.glsl +60 -0
- package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
- package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
- package/src/ts/shaders/density-extreme.frag.glsl +30 -0
- package/src/ts/shaders/density-mrt.frag.glsl +44 -0
- package/src/ts/shaders/density-mrt.vert.glsl +48 -0
- package/src/ts/shaders/density-resolve.frag.glsl +89 -0
- package/src/ts/shaders/density-resolve.vert.glsl +23 -0
- package/src/ts/shaders/density-splat.frag.glsl +34 -0
- package/src/ts/shaders/density-splat.vert.glsl +52 -0
- package/src/ts/shaders/gridline.frag.glsl +18 -0
- package/src/ts/shaders/gridline.vert.glsl +18 -0
- package/src/ts/shaders/heatmap.frag.glsl +23 -0
- package/src/ts/shaders/heatmap.vert.glsl +42 -0
- package/src/ts/shaders/line-uniform.frag.glsl +26 -0
- package/src/ts/shaders/line-uniform.vert.glsl +54 -0
- package/src/ts/shaders/line.frag.glsl +28 -0
- package/src/ts/shaders/line.vert.glsl +87 -0
- package/src/ts/shaders/scatter.frag.glsl +39 -0
- package/src/ts/shaders/scatter.vert.glsl +67 -0
- package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
- package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
- package/src/ts/shaders/tile.frag.glsl +27 -0
- package/src/ts/shaders/tile.vert.glsl +35 -0
- package/src/ts/shaders/treemap.frag.glsl +19 -0
- package/src/ts/shaders/treemap.vert.glsl +25 -0
- package/src/ts/shaders/y-scatter.frag.glsl +30 -0
- package/src/ts/shaders/y-scatter.vert.glsl +31 -0
- package/src/ts/theme/gradient.ts +312 -0
- package/src/ts/theme/palette.ts +64 -0
- package/src/ts/theme/theme-snapshot.ts +66 -0
- package/src/ts/theme/theme.ts +166 -0
- package/src/ts/transport/protocol.ts +497 -0
- package/src/ts/transport/renderer-transport.ts +788 -0
- package/src/ts/utils/css.ts +36 -0
- package/src/ts/utils/font-snapshot.ts +159 -0
- package/src/ts/webgl/buffer-pool.ts +163 -0
- package/src/ts/webgl/context-manager.ts +414 -0
- package/src/ts/webgl/gradient-texture.ts +84 -0
- package/src/ts/webgl/instanced-attrs.ts +139 -0
- package/src/ts/webgl/plot-frame.ts +91 -0
- package/src/ts/webgl/program-cache.ts +46 -0
- package/src/ts/webgl/shader-manifest.ts +148 -0
- package/src/ts/webgl/shader-registry.ts +97 -0
- package/src/ts/worker/boot.ts +22 -0
- package/src/ts/worker/dispatch.ts +99 -0
- package/src/ts/worker/font-loader.ts +89 -0
- package/src/ts/worker/renderer.worker.ts +734 -0
- package/src/ts/worker/session-host.ts +118 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import type { ColumnDataMap } from "../../data/view-reader";
|
|
14
|
+
import type { WebGLContextManager } from "../../webgl/context-manager";
|
|
15
|
+
import { TreeChartBase } from "../common/tree-chart";
|
|
16
|
+
import { NULL_NODE } from "../common/node-store";
|
|
17
|
+
import {
|
|
18
|
+
type BreadcrumbRegion,
|
|
19
|
+
processTreemapChunk,
|
|
20
|
+
finalizeTreemap,
|
|
21
|
+
resetTreemapState,
|
|
22
|
+
} from "./treemap-layout";
|
|
23
|
+
import { renderTreemapFrame } from "./treemap-render";
|
|
24
|
+
import {
|
|
25
|
+
handleTreemapHover,
|
|
26
|
+
handleTreemapClick,
|
|
27
|
+
handleTreemapDblClick,
|
|
28
|
+
dismissTreemapPinnedTooltip,
|
|
29
|
+
} from "./treemap-interact";
|
|
30
|
+
|
|
31
|
+
export interface TreemapLocations {
|
|
32
|
+
u_resolution: WebGLUniformLocation | null;
|
|
33
|
+
a_position: number;
|
|
34
|
+
a_color: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sentinel fallback for the Size slot when the user hasn't picked one:
|
|
39
|
+
* use the first non-metadata column in the incoming view. Treemap
|
|
40
|
+
* still needs *some* numeric-ish column to size rects.
|
|
41
|
+
*/
|
|
42
|
+
function firstNonMetadataColumn(columns: ColumnDataMap): string {
|
|
43
|
+
for (const k of columns.keys()) {
|
|
44
|
+
if (!k.startsWith("__")) {
|
|
45
|
+
return k;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Treemap chart. Shares tree storage + streaming-pipeline + color-mode
|
|
54
|
+
* state with `TreeChartBase`; adds rectangular layout + WebGL quad
|
|
55
|
+
* rendering + drill / tooltip interactions.
|
|
56
|
+
*/
|
|
57
|
+
export class TreemapChart extends TreeChartBase {
|
|
58
|
+
_program: WebGLProgram | null = null;
|
|
59
|
+
_locations: TreemapLocations | null = null;
|
|
60
|
+
_positionBuffer: WebGLBuffer | null = null;
|
|
61
|
+
_colorBuffer: WebGLBuffer | null = null;
|
|
62
|
+
_vertexCount = 0;
|
|
63
|
+
|
|
64
|
+
// Interaction
|
|
65
|
+
_hoveredNodeId: number = NULL_NODE;
|
|
66
|
+
_pinnedNodeId: number = NULL_NODE;
|
|
67
|
+
_breadcrumbRegions: BreadcrumbRegion[] = [];
|
|
68
|
+
_chromeCache: ImageBitmap | null = null;
|
|
69
|
+
_chromeCacheDirty = true;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Monotonic generation counter bumped every time the static chrome
|
|
73
|
+
* content changes (a new `drawStaticChrome` call). The async
|
|
74
|
+
* `createImageBitmap` callback captures the current gen at kickoff
|
|
75
|
+
* and only installs the resulting bitmap if its gen is still the
|
|
76
|
+
* most-recent one. Without this, out-of-order bitmap resolutions
|
|
77
|
+
* can store a stale bitmap in `_chromeCache` — any subsequent
|
|
78
|
+
* hover-only overlay call then blits that stale snapshot over the
|
|
79
|
+
* fresh chart, producing "leftover labels / cells" artefacts.
|
|
80
|
+
*/
|
|
81
|
+
_chromeCacheGen = 0;
|
|
82
|
+
|
|
83
|
+
// Faceted state
|
|
84
|
+
/**
|
|
85
|
+
* Per-facet drill roots in split_by mode. Key is the facet label
|
|
86
|
+
* (the top-level child of `_rootId`); value is the currently drilled
|
|
87
|
+
* node inside that facet's subtree. Missing keys mean the facet
|
|
88
|
+
* shows its full subtree.
|
|
89
|
+
*/
|
|
90
|
+
_facetDrillRoots: Map<string, number> = new Map();
|
|
91
|
+
_facetGrid: import("../../layout/facet-grid").FacetGrid | null = null;
|
|
92
|
+
|
|
93
|
+
/** When `false`, branch nodes at relDepth=1 render as a centered
|
|
94
|
+
* overlay (same style as relDepth=2) and no top-of-rect label
|
|
95
|
+
* reservation is made in `squarify`. Default `true` preserves the
|
|
96
|
+
* legacy title-bar look. */
|
|
97
|
+
_showBranchHeader = false;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Parallel to `_visibleNodeIds`. Each entry stores the depth of the
|
|
101
|
+
* drill root that owns the corresponding visible node, so render
|
|
102
|
+
* paths can compute `relDepth` uniformly without knowing whether
|
|
103
|
+
* faceting is active. Populated in `renderTreemapFrame` during
|
|
104
|
+
* layout.
|
|
105
|
+
*/
|
|
106
|
+
_visibleBaseDepths: Int32Array | null = null;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Parallel to `_visibleNodeIds`. The drill-root node id that owns
|
|
110
|
+
* each visible node (= `_currentRootId` in non-facet mode, per-
|
|
111
|
+
* facet drill root in facet mode). Used by hit-testing and chrome
|
|
112
|
+
* to skip the drill-root itself without a separate equality check.
|
|
113
|
+
*/
|
|
114
|
+
_visibleRootIds: Int32Array | null = null;
|
|
115
|
+
|
|
116
|
+
protected override tooltipCallbacks() {
|
|
117
|
+
return {
|
|
118
|
+
onHover: (mx: number, my: number) =>
|
|
119
|
+
handleTreemapHover(this, mx, my),
|
|
120
|
+
onLeave: () => {
|
|
121
|
+
if (
|
|
122
|
+
this._hoveredNodeId !== NULL_NODE &&
|
|
123
|
+
this._pinnedNodeId === NULL_NODE
|
|
124
|
+
) {
|
|
125
|
+
this._hoveredNodeId = NULL_NODE;
|
|
126
|
+
if (this._glManager) {
|
|
127
|
+
renderTreemapFrame(this, this._glManager);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
onClickPre: (mx: number, my: number) => {
|
|
132
|
+
handleTreemapClick(this, mx, my);
|
|
133
|
+
return true; // treemap owns all click logic
|
|
134
|
+
},
|
|
135
|
+
onDblClick: (mx: number, my: number) =>
|
|
136
|
+
handleTreemapDblClick(this, mx, my),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async uploadAndRender(
|
|
141
|
+
glManager: WebGLContextManager,
|
|
142
|
+
columns: ColumnDataMap,
|
|
143
|
+
startRow: number,
|
|
144
|
+
_endRow: number,
|
|
145
|
+
): Promise<void> {
|
|
146
|
+
this._glManager = glManager;
|
|
147
|
+
|
|
148
|
+
if (startRow === 0) {
|
|
149
|
+
const slots = this._columnSlots;
|
|
150
|
+
this._sizeName = slots[0] || firstNonMetadataColumn(columns) || "";
|
|
151
|
+
this._colorName = slots[1] || "";
|
|
152
|
+
if (!this._colorName) {
|
|
153
|
+
this._colorMode = "empty";
|
|
154
|
+
} else {
|
|
155
|
+
const t = this._columnTypes[this._colorName];
|
|
156
|
+
const isNumeric =
|
|
157
|
+
t === "float" ||
|
|
158
|
+
t === "integer" ||
|
|
159
|
+
t === "date" ||
|
|
160
|
+
t === "datetime";
|
|
161
|
+
this._colorMode = isNumeric ? "numeric" : "series";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Clear per-draw state that's tied to the OLD tree. Node
|
|
165
|
+
// IDs from the previous render don't map to anything in
|
|
166
|
+
// the fresh tree; leaving them around lets stale drill
|
|
167
|
+
// roots, hovered/pinned IDs, breadcrumb regions, the
|
|
168
|
+
// cached chrome bitmap, or an old WebGL vertex count bleed
|
|
169
|
+
// into the new render as ghost rects / labels / hit
|
|
170
|
+
// targets. See tree-data.ts's `resetTreeState` for the
|
|
171
|
+
// shared fields; everything below is treemap-specific.
|
|
172
|
+
this._hoveredNodeId = NULL_NODE;
|
|
173
|
+
this._pinnedNodeId = NULL_NODE;
|
|
174
|
+
this._breadcrumbRegions = [];
|
|
175
|
+
this._facetDrillRoots.clear();
|
|
176
|
+
this._facetGrid = null;
|
|
177
|
+
this._visibleBaseDepths = null;
|
|
178
|
+
this._visibleRootIds = null;
|
|
179
|
+
|
|
180
|
+
// Invalidate the GPU buffer contents so any render that
|
|
181
|
+
// fires before `generateAndUploadTreemap` has refilled the
|
|
182
|
+
// buffers draws zero triangles instead of the previous
|
|
183
|
+
// tree's geometry.
|
|
184
|
+
this._vertexCount = 0;
|
|
185
|
+
|
|
186
|
+
// Drop any in-flight hover tooltip promise — bumping the
|
|
187
|
+
// controller's serials makes stale resolutions no-ops
|
|
188
|
+
// rather than painting old lines on the new chart.
|
|
189
|
+
this._lazyTooltip.clearHover();
|
|
190
|
+
this._lazyTooltip.invalidatePin();
|
|
191
|
+
dismissTreemapPinnedTooltip(this);
|
|
192
|
+
this._chromeCache?.close();
|
|
193
|
+
this._chromeCache = null;
|
|
194
|
+
this._chromeCacheDirty = true;
|
|
195
|
+
this._chromeCacheGen++;
|
|
196
|
+
|
|
197
|
+
resetTreemapState(this);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
processTreemapChunk(this, columns);
|
|
201
|
+
finalizeTreemap(this);
|
|
202
|
+
if (this._rootId !== NULL_NODE) {
|
|
203
|
+
await this.requestRender(glManager);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
_fullRender(glManager: WebGLContextManager): void {
|
|
208
|
+
if (this._rootId === NULL_NODE) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this._glManager = glManager;
|
|
213
|
+
renderTreemapFrame(this, glManager);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
protected destroyInternal(): void {
|
|
217
|
+
dismissTreemapPinnedTooltip(this);
|
|
218
|
+
this._chromeCache?.close();
|
|
219
|
+
this._chromeCache = null;
|
|
220
|
+
const gl = this._glManager?.gl;
|
|
221
|
+
if (gl) {
|
|
222
|
+
if (this._positionBuffer) {
|
|
223
|
+
gl.deleteBuffer(this._positionBuffer);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this._colorBuffer) {
|
|
227
|
+
gl.deleteBuffer(this._colorBuffer);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this._positionBuffer = null;
|
|
232
|
+
this._colorBuffer = null;
|
|
233
|
+
this._program = null;
|
|
234
|
+
this._locations = null;
|
|
235
|
+
this._rootId = NULL_NODE;
|
|
236
|
+
this._currentRootId = NULL_NODE;
|
|
237
|
+
this._breadcrumbIds = [];
|
|
238
|
+
this._childLookup.clear();
|
|
239
|
+
this._visibleNodeIds = null;
|
|
240
|
+
this._visibleNodeCount = 0;
|
|
241
|
+
this._breadcrumbRegions = [];
|
|
242
|
+
this._facetDrillRoots.clear();
|
|
243
|
+
this._facetGrid = null;
|
|
244
|
+
this._visibleBaseDepths = null;
|
|
245
|
+
this._visibleRootIds = null;
|
|
246
|
+
}
|
|
247
|
+
}
|
package/src/ts/config.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Renderer mode. `"worker"` runs the chart code in a Web Worker (off
|
|
15
|
+
* the main thread, gets parallelism but pays a postMessage hop on
|
|
16
|
+
* every interaction). `"inprocess"` dynamic-imports the same worker
|
|
17
|
+
* module on the main thread so the bundle stays single-copy without
|
|
18
|
+
* the worker boundary. The two paths share a `MessageChannel`-shaped
|
|
19
|
+
* control protocol — only the handle around it differs.
|
|
20
|
+
*/
|
|
21
|
+
export const RUNTIME_MODE: "worker" | "inprocess" = "worker";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build-time toggle between the two GL-canvas display strategies.
|
|
25
|
+
*
|
|
26
|
+
* - `"direct"` — host transfers `.webgl-canvas` to the renderer via
|
|
27
|
+
* `transferControlToOffscreen`. The renderer's GL context renders
|
|
28
|
+
* straight into the visible drawing buffer.
|
|
29
|
+
*
|
|
30
|
+
* - `"blit"` — host keeps the visible canvas main-thread with a 2D
|
|
31
|
+
* context. The renderer creates its own internal `OffscreenCanvas`
|
|
32
|
+
* for GL rendering and emits each completed frame as an
|
|
33
|
+
* `ImageBitmap` over the control channel; the host blits the bitmap
|
|
34
|
+
* into the visible canvas via `drawImage`.
|
|
35
|
+
*/
|
|
36
|
+
export const RENDER_BLIT_MODE: "direct" | "blit" = "direct";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Strict-mode validation for `BufferPool.upload`.
|
|
40
|
+
*/
|
|
41
|
+
export const BUFFER_POOL_STRICT: boolean = false;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import type { View } from "@perspective-dev/client";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A single row's column values, keyed by column name. Numeric columns
|
|
17
|
+
* yield a `number`; string (dictionary) columns yield the decoded
|
|
18
|
+
* `string`; invalid (null) cells yield `null`.
|
|
19
|
+
*/
|
|
20
|
+
export type LazyRow = Map<string, string | number | null>;
|
|
21
|
+
|
|
22
|
+
const DEFAULT_CACHE_SIZE = 128;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* On-demand single-row fetcher backing lazy tooltip lookups. Given a
|
|
26
|
+
* view row index, performs `view.with_typed_arrays({start_row, end_row:
|
|
27
|
+
* start_row+1})` and projects the result into a plain `Map`. Concurrent
|
|
28
|
+
* fetches for the same index are deduped into one Promise; resolved
|
|
29
|
+
* rows are cached in a bounded LRU keyed by rowIdx.
|
|
30
|
+
*
|
|
31
|
+
* Invalidation is lifecycle-driven: the owning chart disposes and
|
|
32
|
+
* constructs a new fetcher whenever its underlying view changes (i.e.
|
|
33
|
+
* on each `draw`). In-flight fetches from the prior fetcher still
|
|
34
|
+
* resolve, but callers stamp each fetch with a serial and discard
|
|
35
|
+
* results whose serial no longer matches — so stale rows never reach
|
|
36
|
+
* the tooltip. See the per-chart hover/pin paths for that plumbing.
|
|
37
|
+
*/
|
|
38
|
+
export class LazyRowFetcher {
|
|
39
|
+
private _view: View | null;
|
|
40
|
+
private _cache: Map<number, LazyRow> = new Map();
|
|
41
|
+
private _inFlight: Map<number, Promise<LazyRow>> = new Map();
|
|
42
|
+
private readonly _maxCacheSize: number;
|
|
43
|
+
|
|
44
|
+
constructor(view: View, maxCacheSize: number = DEFAULT_CACHE_SIZE) {
|
|
45
|
+
this._view = view;
|
|
46
|
+
this._maxCacheSize = maxCacheSize;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async fetchRow(rowIdx: number): Promise<LazyRow> {
|
|
50
|
+
if (!this._view) {
|
|
51
|
+
throw new Error("LazyRowFetcher disposed");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const cached = this._cache.get(rowIdx);
|
|
55
|
+
if (cached) {
|
|
56
|
+
// LRU touch: re-insert to move to tail.
|
|
57
|
+
this._cache.delete(rowIdx);
|
|
58
|
+
this._cache.set(rowIdx, cached);
|
|
59
|
+
return cached;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const inflight = this._inFlight.get(rowIdx);
|
|
63
|
+
if (inflight) {
|
|
64
|
+
return inflight;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const p = this._fetch(rowIdx);
|
|
68
|
+
this._inFlight.set(rowIdx, p);
|
|
69
|
+
try {
|
|
70
|
+
const result = await p;
|
|
71
|
+
if (!this._view) {
|
|
72
|
+
return result;
|
|
73
|
+
} // disposed mid-flight
|
|
74
|
+
|
|
75
|
+
this._cache.set(rowIdx, result);
|
|
76
|
+
if (this._cache.size > this._maxCacheSize) {
|
|
77
|
+
const oldest = this._cache.keys().next().value;
|
|
78
|
+
if (oldest !== undefined) {
|
|
79
|
+
this._cache.delete(oldest);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
} finally {
|
|
85
|
+
this._inFlight.delete(rowIdx);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private async _fetch(rowIdx: number): Promise<LazyRow> {
|
|
90
|
+
const view = this._view;
|
|
91
|
+
if (!view) {
|
|
92
|
+
throw new Error("LazyRowFetcher disposed");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const row: LazyRow = new Map();
|
|
96
|
+
await (view as any).with_typed_arrays(
|
|
97
|
+
{
|
|
98
|
+
start_row: rowIdx,
|
|
99
|
+
end_row: rowIdx + 1,
|
|
100
|
+
float32: true,
|
|
101
|
+
},
|
|
102
|
+
(
|
|
103
|
+
names: string[],
|
|
104
|
+
values: ArrayLike<number>[],
|
|
105
|
+
validities: (Uint8Array | null)[],
|
|
106
|
+
dictionaries: (string[] | null)[],
|
|
107
|
+
) => {
|
|
108
|
+
for (let i = 0; i < names.length; i++) {
|
|
109
|
+
const name = names[i];
|
|
110
|
+
if (name.startsWith("__")) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const vals = values[i];
|
|
115
|
+
const valid = validities[i];
|
|
116
|
+
const dict = dictionaries[i];
|
|
117
|
+
const isInvalid = valid ? !((valid[0] >> 0) & 1) : false;
|
|
118
|
+
if (isInvalid) {
|
|
119
|
+
row.set(name, null);
|
|
120
|
+
} else if (dict) {
|
|
121
|
+
row.set(name, dict[vals[0] as number]);
|
|
122
|
+
} else {
|
|
123
|
+
row.set(name, vals[0] as number);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
return row;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
dispose(): void {
|
|
132
|
+
this._view = null;
|
|
133
|
+
this._cache.clear();
|
|
134
|
+
this._inFlight.clear();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get isDisposed(): boolean {
|
|
138
|
+
return this._view === null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import type { ColumnDataMap } from "./view-reader";
|
|
14
|
+
|
|
15
|
+
export interface SplitGroup {
|
|
16
|
+
/**
|
|
17
|
+
* Composite prefix (e.g., "East", "East|Enterprise" for multi-level).
|
|
18
|
+
*/
|
|
19
|
+
prefix: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Map of base column name → full Arrow column name ("prefix|base").
|
|
23
|
+
*/
|
|
24
|
+
colNames: Map<string, string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Group Arrow column names by their split prefix (everything before the
|
|
29
|
+
* last "|"). A split exists for a prefix only when every `requiredBases`
|
|
30
|
+
* entry has a non-empty column present for that prefix. `optionalBases`
|
|
31
|
+
* are included when present but do not gate group inclusion.
|
|
32
|
+
*
|
|
33
|
+
* Empty/falsy entries in either base list are skipped.
|
|
34
|
+
*/
|
|
35
|
+
export function buildSplitGroups(
|
|
36
|
+
columns: ColumnDataMap,
|
|
37
|
+
requiredBases: string[],
|
|
38
|
+
optionalBases: string[] = [],
|
|
39
|
+
): SplitGroup[] {
|
|
40
|
+
const prefixCols = new Map<string, Set<string>>();
|
|
41
|
+
for (const key of columns.keys()) {
|
|
42
|
+
if (key.startsWith("__")) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pipeIdx = key.lastIndexOf("|");
|
|
47
|
+
if (pipeIdx === -1) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const prefix = key.substring(0, pipeIdx);
|
|
52
|
+
if (!prefixCols.has(prefix)) {
|
|
53
|
+
prefixCols.set(prefix, new Set());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
prefixCols.get(prefix)!.add(key);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const out: SplitGroup[] = [];
|
|
60
|
+
for (const [prefix, keys] of prefixCols) {
|
|
61
|
+
const resolved = new Map<string, string>();
|
|
62
|
+
let ok = true;
|
|
63
|
+
for (const base of requiredBases) {
|
|
64
|
+
if (!base) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const full = `${prefix}|${base}`;
|
|
69
|
+
const col = columns.get(full);
|
|
70
|
+
if (!keys.has(full) || !col?.values) {
|
|
71
|
+
ok = false;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
resolved.set(base, full);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!ok) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const base of optionalBases) {
|
|
83
|
+
if (!base) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const full = `${prefix}|${base}`;
|
|
88
|
+
if (keys.has(full) && columns.get(full)?.values) {
|
|
89
|
+
resolved.set(base, full);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
out.push({ prefix, colNames: resolved });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import type { View } from "@perspective-dev/client";
|
|
14
|
+
|
|
15
|
+
export interface ColumnData {
|
|
16
|
+
type: "float32" | "float64" | "int32" | "string";
|
|
17
|
+
values?: Float32Array | Float64Array | Int32Array;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Dictionary key indices for string columns.
|
|
21
|
+
*/
|
|
22
|
+
indices?: Int32Array;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Dictionary values for string columns.
|
|
26
|
+
*/
|
|
27
|
+
dictionary?: string[];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Arrow validity bitfield (1 bit per row).
|
|
31
|
+
*/
|
|
32
|
+
valid?: Uint8Array;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ColumnDataMap = Map<string, ColumnData>;
|
|
36
|
+
|
|
37
|
+
export interface TypedArrayWindowOptions {
|
|
38
|
+
start_row?: number;
|
|
39
|
+
end_row?: number;
|
|
40
|
+
start_col?: number;
|
|
41
|
+
end_col?: number;
|
|
42
|
+
float32?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Fetches all columns from a View using `with_typed_arrays` and
|
|
47
|
+
* builds a `ColumnDataMap`. The `values` typed arrays and `valid`
|
|
48
|
+
* bitmaps are zero-copy views into WASM memory and remain valid only
|
|
49
|
+
* for the duration of the `render` callback — if `render` returns a
|
|
50
|
+
* `Promise`, the underlying `with_typed_arrays` call awaits it before
|
|
51
|
+
* releasing the backing Arrow buffer. Callers must not retain any
|
|
52
|
+
* `ColumnData` reference past `render`'s resolution.
|
|
53
|
+
*/
|
|
54
|
+
export async function viewToColumnDataMap(
|
|
55
|
+
view: View,
|
|
56
|
+
render: (data: ColumnDataMap) => void | Promise<void>,
|
|
57
|
+
options?: TypedArrayWindowOptions,
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
const result: ColumnDataMap = new Map();
|
|
60
|
+
|
|
61
|
+
await (view as any).with_typed_arrays(
|
|
62
|
+
options ?? {},
|
|
63
|
+
async (
|
|
64
|
+
names: string[],
|
|
65
|
+
values: ArrayLike<number>[],
|
|
66
|
+
validities: (Uint8Array | null)[],
|
|
67
|
+
dictionaries: (string[] | null)[],
|
|
68
|
+
) => {
|
|
69
|
+
for (let i = 0; i < names.length; i++) {
|
|
70
|
+
const name = names[i];
|
|
71
|
+
const vals = values[i];
|
|
72
|
+
const valid = validities[i] ?? undefined;
|
|
73
|
+
const dict = dictionaries[i];
|
|
74
|
+
|
|
75
|
+
if (dict !== null) {
|
|
76
|
+
result.set(name, {
|
|
77
|
+
type: "string",
|
|
78
|
+
indices: vals as Int32Array,
|
|
79
|
+
dictionary: dict,
|
|
80
|
+
valid,
|
|
81
|
+
});
|
|
82
|
+
} else if (vals instanceof Float32Array) {
|
|
83
|
+
result.set(name, { type: "float32", values: vals, valid });
|
|
84
|
+
} else if (vals instanceof Int32Array) {
|
|
85
|
+
result.set(name, { type: "int32", values: vals, valid });
|
|
86
|
+
} else if (vals instanceof Float64Array) {
|
|
87
|
+
// Datetime/Date columns are emitted as Float64 to keep
|
|
88
|
+
// millisecond precision; numeric Float64 also lands here
|
|
89
|
+
// when `float32` mode is off. Keep them as f64 — the
|
|
90
|
+
// chart's CPU mirrors and extents will rebase to f32 at
|
|
91
|
+
// upload time.
|
|
92
|
+
result.set(name, { type: "float64", values: vals, valid });
|
|
93
|
+
} else {
|
|
94
|
+
// Fallback: treat as float32
|
|
95
|
+
// TODO: Instance check if this needs a copy?
|
|
96
|
+
result.set(name, {
|
|
97
|
+
type: "float32",
|
|
98
|
+
values: new Float32Array(vals as any),
|
|
99
|
+
valid,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await render(result);
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import type { ViewConfig } from "@perspective-dev/client";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detail payload for the `perspective-click` CustomEvent dispatched on
|
|
17
|
+
* the `<perspective-viewer>` host when the user clicks a chart glyph.
|
|
18
|
+
* Shape matches the equivalent type in `@perspective-dev/viewer-datagrid`
|
|
19
|
+
* so consumers can listen to either plugin uniformly.
|
|
20
|
+
*/
|
|
21
|
+
export interface PerspectiveClickDetail {
|
|
22
|
+
/**
|
|
23
|
+
* Source-view row data — keys are column names, values are scalar
|
|
24
|
+
* cell values. Empty `{}` when the click hit a non-leaf aggregate
|
|
25
|
+
* (e.g. a treemap branch).
|
|
26
|
+
*/
|
|
27
|
+
row: Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Aggregate column(s) the click targeted. Single-entry for series /
|
|
31
|
+
* heatmap / tree charts; multi-entry would only appear for plugins
|
|
32
|
+
* that surface multiple measures per glyph (none today).
|
|
33
|
+
*/
|
|
34
|
+
column_names: string[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pre-built `viewer.restore({ filter })` patch — concatenates the
|
|
38
|
+
* group_by and split_by pivot values at the click target as
|
|
39
|
+
* `[<col>, "==", <value>]` clauses.
|
|
40
|
+
*/
|
|
41
|
+
config: Partial<ViewConfig>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { PerspectiveSelectDetail } from "@perspective-dev/viewer/src/ts/extensions.js";
|