@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,314 @@
|
|
|
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, ColumnData } from "../../data/view-reader";
|
|
14
|
+
import type { CategoricalLevel } from "../../axis/categorical-axis";
|
|
15
|
+
import { buildGroupRuns } from "../../axis/categorical-axis-core";
|
|
16
|
+
import { formatTickValue, formatDateTickValue } from "../../layout/ticks";
|
|
17
|
+
|
|
18
|
+
export interface CategoryAxisResult {
|
|
19
|
+
/**
|
|
20
|
+
* Fully materialized hierarchical levels — labels and group runs are
|
|
21
|
+
* pre-resolved from the view's `__ROW_PATH_N__` dictionaries (or
|
|
22
|
+
* synthesized for non-string levels) so the chart can retain them
|
|
23
|
+
* past the `with_typed_arrays` callback scope. Empty when `groupBy`
|
|
24
|
+
* is empty.
|
|
25
|
+
*/
|
|
26
|
+
rowPaths: CategoricalLevel[];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Rows that actually contribute a category (post-offset).
|
|
30
|
+
*/
|
|
31
|
+
numCategories: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Leading rows skipped; callers use this to rebase per-row indices.
|
|
35
|
+
*/
|
|
36
|
+
rowOffset: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type AxisMode =
|
|
40
|
+
| { mode: "category" }
|
|
41
|
+
| {
|
|
42
|
+
mode: "numeric";
|
|
43
|
+
numericType: "date" | "datetime" | "integer" | "float";
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Numeric category-axis state. Shared across bar / candlestick / heatmap
|
|
48
|
+
* pipelines: when an axis is driven by exactly one non-string group_by /
|
|
49
|
+
* split_by level, glyphs anchor at real data values via `categoryPositions`
|
|
50
|
+
* and the chrome renders a numeric (date-aware) tick row.
|
|
51
|
+
*/
|
|
52
|
+
export interface NumericCategoryDomain {
|
|
53
|
+
min: number;
|
|
54
|
+
max: number;
|
|
55
|
+
isDate: boolean;
|
|
56
|
+
label: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Data-unit width of one category band, from min adjacent delta.
|
|
60
|
+
*/
|
|
61
|
+
bandWidth: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Compute `categoryPositions` (per-row real data values) plus a
|
|
66
|
+
* `NumericCategoryDomain` summarizing min/max/bandWidth for a numeric
|
|
67
|
+
* row-path column. `bandWidth` falls back to the full domain when there
|
|
68
|
+
* are <2 distinct positions. Pivot rows for a single group_by come ASC
|
|
69
|
+
* by default, so a forward sweep for `minDelta` is sufficient.
|
|
70
|
+
*
|
|
71
|
+
* Returns `null` when the row-path column is missing or carries no
|
|
72
|
+
* `values` array (e.g. dictionary-encoded string column).
|
|
73
|
+
*/
|
|
74
|
+
export function resolveNumericCategoryDomain(
|
|
75
|
+
rpValues: ArrayLike<number> | null | undefined,
|
|
76
|
+
numCategories: number,
|
|
77
|
+
rowOffset: number,
|
|
78
|
+
label: string,
|
|
79
|
+
isDate: boolean,
|
|
80
|
+
): {
|
|
81
|
+
categoryPositions: Float64Array;
|
|
82
|
+
numericCategoryDomain: NumericCategoryDomain;
|
|
83
|
+
} | null {
|
|
84
|
+
if (!rpValues || numCategories <= 0) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const categoryPositions = new Float64Array(numCategories);
|
|
89
|
+
let minVal = Infinity;
|
|
90
|
+
let maxVal = -Infinity;
|
|
91
|
+
for (let catI = 0; catI < numCategories; catI++) {
|
|
92
|
+
const v = rpValues[catI + rowOffset] as number;
|
|
93
|
+
categoryPositions[catI] = v;
|
|
94
|
+
if (v < minVal) {
|
|
95
|
+
minVal = v;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (v > maxVal) {
|
|
99
|
+
maxVal = v;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let minDelta = Infinity;
|
|
104
|
+
for (let i = 1; i < numCategories; i++) {
|
|
105
|
+
const d = Math.abs(categoryPositions[i] - categoryPositions[i - 1]);
|
|
106
|
+
if (d > 0 && d < minDelta) {
|
|
107
|
+
minDelta = d;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!isFinite(minDelta) || minDelta === 0) {
|
|
112
|
+
minDelta = Math.max(1, maxVal - minVal);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
categoryPositions,
|
|
117
|
+
numericCategoryDomain: {
|
|
118
|
+
min: minVal - minDelta / 2,
|
|
119
|
+
max: maxVal + minDelta / 2,
|
|
120
|
+
isDate,
|
|
121
|
+
label,
|
|
122
|
+
bandWidth: minDelta,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Decide whether the categorical axis should render as a stringified
|
|
129
|
+
* category axis or a true numeric axis. Numeric mode is only used when
|
|
130
|
+
* there is exactly one `group_by` level AND that level is a non-string,
|
|
131
|
+
* non-boolean numeric type. Boolean and any multi-level case → category.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveAxisMode(
|
|
134
|
+
groupBy: string[],
|
|
135
|
+
groupByTypes: Record<string, string>,
|
|
136
|
+
): AxisMode {
|
|
137
|
+
if (groupBy.length !== 1) {
|
|
138
|
+
return { mode: "category" };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const t = groupByTypes[groupBy[0]];
|
|
142
|
+
if (t === "date" || t === "datetime" || t === "integer" || t === "float") {
|
|
143
|
+
return { mode: "numeric", numericType: t };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { mode: "category" };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Stringify a single value from a non-string row-path column.
|
|
151
|
+
*/
|
|
152
|
+
function formatLevelValue(
|
|
153
|
+
value: number,
|
|
154
|
+
valid: boolean,
|
|
155
|
+
levelType: string,
|
|
156
|
+
): string {
|
|
157
|
+
if (!valid) {
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (levelType === "boolean") {
|
|
162
|
+
return value ? "true" : "false";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (levelType === "date" || levelType === "datetime") {
|
|
166
|
+
return formatDateTickValue(value);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (levelType === "integer") {
|
|
170
|
+
return String(value | 0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (levelType === "float") {
|
|
174
|
+
return formatTickValue(value);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return String(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Synthesize a `(indices, dictionary)` pair from a non-string row-path
|
|
182
|
+
* column so the rest of the categorical axis machinery (label
|
|
183
|
+
* pre-resolution, run-length encoding) can run unchanged. The dictionary
|
|
184
|
+
* uses `""` at index 0 as the rollup-row sentinel — this preserves the
|
|
185
|
+
* existing skip-rollup loop's `s !== ""` check.
|
|
186
|
+
*/
|
|
187
|
+
export function synthesizeStringLevel(
|
|
188
|
+
rp: ColumnData,
|
|
189
|
+
numRows: number,
|
|
190
|
+
levelType: string,
|
|
191
|
+
): { indices: Int32Array; dictionary: string[] } {
|
|
192
|
+
const values = rp.values!;
|
|
193
|
+
const valid = rp.valid;
|
|
194
|
+
const indices = new Int32Array(numRows);
|
|
195
|
+
const dictionary: string[] = [""];
|
|
196
|
+
const seen = new Map<string, number>();
|
|
197
|
+
seen.set("", 0);
|
|
198
|
+
|
|
199
|
+
for (let r = 0; r < numRows; r++) {
|
|
200
|
+
const isValid = valid ? !!((valid[r >> 3] >> (r & 7)) & 1) : true;
|
|
201
|
+
const v = values[r] as number;
|
|
202
|
+
const label = formatLevelValue(v, isValid, levelType);
|
|
203
|
+
let dictIdx = seen.get(label);
|
|
204
|
+
if (dictIdx === undefined) {
|
|
205
|
+
dictIdx = dictionary.length;
|
|
206
|
+
dictionary.push(label);
|
|
207
|
+
seen.set(label, dictIdx);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
indices[r] = dictIdx;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { indices, dictionary };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Resolve the category axis for a categorical-X chart (bar, candlestick,
|
|
218
|
+
* ohlc, …). Walks the `__ROW_PATH_N__` hierarchy columns, skips the
|
|
219
|
+
* rollup rows at the top ("Total" parent aggregates), and returns fully
|
|
220
|
+
* JS-owned level structures (precomputed labels + runs) plus the
|
|
221
|
+
* trimmed category count.
|
|
222
|
+
*
|
|
223
|
+
* Non-string row-path columns (date / datetime / integer / float /
|
|
224
|
+
* boolean group_by levels) are stringified into a synthetic dictionary
|
|
225
|
+
* so the downstream label / run-length machinery is type-agnostic.
|
|
226
|
+
*
|
|
227
|
+
* When `groupByLen === 0`, there are no row-path columns and the
|
|
228
|
+
* category axis falls back to the raw row index — callers infer that
|
|
229
|
+
* from `rowPaths.length === 0`.
|
|
230
|
+
*/
|
|
231
|
+
export function resolveCategoryAxis(
|
|
232
|
+
columns: ColumnDataMap,
|
|
233
|
+
numRows: number,
|
|
234
|
+
groupByLen: number,
|
|
235
|
+
levelTypes: string[] = [],
|
|
236
|
+
): CategoryAxisResult {
|
|
237
|
+
type RawLevel = { indices: Int32Array; dictionary: string[] };
|
|
238
|
+
const rawRowPaths: RawLevel[] = [];
|
|
239
|
+
for (let n = 0; ; n++) {
|
|
240
|
+
const rp = columns.get(`__ROW_PATH_${n}__`);
|
|
241
|
+
if (!rp) {
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (rp.type === "string" && rp.indices && rp.dictionary) {
|
|
246
|
+
rawRowPaths.push({
|
|
247
|
+
indices: rp.indices,
|
|
248
|
+
dictionary: rp.dictionary,
|
|
249
|
+
});
|
|
250
|
+
} else if (rp.values) {
|
|
251
|
+
const levelType = levelTypes[n] ?? "string";
|
|
252
|
+
rawRowPaths.push(synthesizeStringLevel(rp, numRows, levelType));
|
|
253
|
+
} else {
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let rowOffset = 0;
|
|
259
|
+
if (groupByLen > 0 && rawRowPaths.length > 0) {
|
|
260
|
+
while (rowOffset < numRows) {
|
|
261
|
+
let anyNonEmpty = false;
|
|
262
|
+
for (const rp of rawRowPaths) {
|
|
263
|
+
const s = rp.dictionary[rp.indices[rowOffset]];
|
|
264
|
+
if (s != null && s !== "") {
|
|
265
|
+
anyNonEmpty = true;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (anyNonEmpty) {
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
rowOffset++;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const numCategories = Math.max(0, numRows - rowOffset);
|
|
279
|
+
|
|
280
|
+
const L = rawRowPaths.length;
|
|
281
|
+
const rowPaths: CategoricalLevel[] =
|
|
282
|
+
groupByLen > 0 && L > 0
|
|
283
|
+
? rawRowPaths.map((rp, levelIdx) => {
|
|
284
|
+
const labels = new Array<string>(numCategories);
|
|
285
|
+
let maxLabelChars = 0;
|
|
286
|
+
for (let r = 0; r < numCategories; r++) {
|
|
287
|
+
const s = rp.dictionary[rp.indices[r + rowOffset]] ?? "";
|
|
288
|
+
labels[r] = s;
|
|
289
|
+
if (s.length > maxLabelChars) {
|
|
290
|
+
maxLabelChars = s.length;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Only outer levels need the run-length encoding for
|
|
295
|
+
// bracket rendering; leaves render per-row.
|
|
296
|
+
const runs =
|
|
297
|
+
levelIdx === L - 1
|
|
298
|
+
? []
|
|
299
|
+
: buildGroupRuns(
|
|
300
|
+
rp.indices,
|
|
301
|
+
rp.dictionary,
|
|
302
|
+
rowOffset,
|
|
303
|
+
rowOffset + numCategories,
|
|
304
|
+
).map((run) => ({
|
|
305
|
+
startIdx: run.startIdx - rowOffset,
|
|
306
|
+
endIdx: run.endIdx - rowOffset,
|
|
307
|
+
label: run.label,
|
|
308
|
+
}));
|
|
309
|
+
return { labels, runs, maxLabelChars };
|
|
310
|
+
})
|
|
311
|
+
: [];
|
|
312
|
+
|
|
313
|
+
return { rowPaths, numCategories, rowOffset };
|
|
314
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { Canvas2D, Context2D } from "../canvas-types";
|
|
14
|
+
|
|
15
|
+
export interface ChromeCacheChart {
|
|
16
|
+
_chromeCanvas: Canvas2D | null;
|
|
17
|
+
_chromeCache: ImageBitmap | null;
|
|
18
|
+
_chromeCacheDirty: boolean;
|
|
19
|
+
_chromeCacheGen: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run the static-chrome cache pattern shared by sunburst + treemap.
|
|
24
|
+
* Resizes the canvas, paints the static layer (and snapshots it as an
|
|
25
|
+
* `ImageBitmap`) when dirty, otherwise blits the cache; then runs the
|
|
26
|
+
* caller-provided overlay layer for hover/highlight state.
|
|
27
|
+
*
|
|
28
|
+
* Returns the prepared `ctx` already in DPR-scaled space so the overlay
|
|
29
|
+
* callback can paint in CSS pixels — except `null` if either the canvas
|
|
30
|
+
* is missing a 2D context or the chart has nothing to paint.
|
|
31
|
+
*/
|
|
32
|
+
export function withChromeCache(
|
|
33
|
+
chart: ChromeCacheChart,
|
|
34
|
+
canvas: Canvas2D,
|
|
35
|
+
dpr: number,
|
|
36
|
+
cssWidth: number,
|
|
37
|
+
cssHeight: number,
|
|
38
|
+
drawStatic: (ctx: Context2D) => void,
|
|
39
|
+
drawOverlay: ((ctx: Context2D) => void) | null,
|
|
40
|
+
): void {
|
|
41
|
+
const targetW = Math.round(cssWidth * dpr);
|
|
42
|
+
const targetH = Math.round(cssHeight * dpr);
|
|
43
|
+
if (canvas.width !== targetW || canvas.height !== targetH) {
|
|
44
|
+
canvas.width = targetW;
|
|
45
|
+
canvas.height = targetH;
|
|
46
|
+
chart._chromeCacheDirty = true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ctx = canvas.getContext("2d") as Context2D | null;
|
|
50
|
+
if (!ctx) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (chart._chromeCacheDirty) {
|
|
55
|
+
chart._chromeCache?.close();
|
|
56
|
+
chart._chromeCache = null;
|
|
57
|
+
chart._chromeCacheDirty = false;
|
|
58
|
+
const gen = ++chart._chromeCacheGen;
|
|
59
|
+
drawStatic(ctx);
|
|
60
|
+
createImageBitmap(canvas).then((bmp) => {
|
|
61
|
+
if (chart._chromeCacheGen === gen) {
|
|
62
|
+
chart._chromeCache?.close();
|
|
63
|
+
chart._chromeCache = bmp;
|
|
64
|
+
} else {
|
|
65
|
+
bmp.close();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} else if (chart._chromeCache) {
|
|
69
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
70
|
+
ctx.drawImage(chart._chromeCache, 0, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (drawOverlay) {
|
|
74
|
+
ctx.save();
|
|
75
|
+
ctx.scale(dpr, dpr);
|
|
76
|
+
drawOverlay(ctx);
|
|
77
|
+
ctx.restore();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { Context2D } from "../canvas-types";
|
|
14
|
+
import type { Theme } from "../../theme/theme";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Draw a freestanding tooltip box anchored near (cx, cy), measuring
|
|
18
|
+
* lines, sizing/clamping the box, painting bg/border, and laying out
|
|
19
|
+
* text rows. Shared by sunburst + treemap which need a non-PlotLayout
|
|
20
|
+
* anchor.
|
|
21
|
+
*/
|
|
22
|
+
export function drawTooltipBox(
|
|
23
|
+
ctx: Context2D,
|
|
24
|
+
theme: Theme,
|
|
25
|
+
lines: string[],
|
|
26
|
+
cx: number,
|
|
27
|
+
cy: number,
|
|
28
|
+
cssWidth: number,
|
|
29
|
+
cssHeight: number,
|
|
30
|
+
fontFamily: string,
|
|
31
|
+
): void {
|
|
32
|
+
if (lines.length === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { tooltipBg, tooltipText, tooltipBorder } = theme;
|
|
37
|
+
|
|
38
|
+
ctx.font = `11px ${fontFamily}`;
|
|
39
|
+
const lineHeight = 16;
|
|
40
|
+
const padding = 8;
|
|
41
|
+
let maxWidth = 0;
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
const w = ctx.measureText(line).width;
|
|
44
|
+
if (w > maxWidth) {
|
|
45
|
+
maxWidth = w;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const boxW = maxWidth + padding * 2;
|
|
50
|
+
const boxH = lines.length * lineHeight + padding * 2 - 4;
|
|
51
|
+
|
|
52
|
+
let tx = cx + 12;
|
|
53
|
+
let ty = cy - boxH - 8;
|
|
54
|
+
if (tx + boxW > cssWidth) {
|
|
55
|
+
tx = cx - boxW - 12;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (tx < 0) {
|
|
59
|
+
tx = 4;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (ty < 0) {
|
|
63
|
+
ty = cy + 12;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (ty + boxH > cssHeight) {
|
|
67
|
+
ty = cssHeight - boxH - 4;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ctx.fillStyle = tooltipBg;
|
|
71
|
+
ctx.strokeStyle = tooltipBorder;
|
|
72
|
+
ctx.lineWidth = 1;
|
|
73
|
+
ctx.beginPath();
|
|
74
|
+
ctx.roundRect(tx, ty, boxW, boxH, 4);
|
|
75
|
+
ctx.fill();
|
|
76
|
+
ctx.stroke();
|
|
77
|
+
|
|
78
|
+
ctx.fillStyle = tooltipText;
|
|
79
|
+
ctx.textAlign = "left";
|
|
80
|
+
ctx.textBaseline = "top";
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
ctx.fillText(lines[i], tx + padding, ty + padding + i * lineHeight);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { TreeChartBase } from "./tree-chart";
|
|
14
|
+
import type { Vec3 } from "../../theme/palette";
|
|
15
|
+
import {
|
|
16
|
+
colorValueToT,
|
|
17
|
+
sampleGradient,
|
|
18
|
+
type GradientStop,
|
|
19
|
+
} from "../../theme/gradient";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Perceptual luminance for a 0..1 RGB triple. Used by tree-chart label
|
|
23
|
+
* painters to pick a contrasting text color over each leaf's fill.
|
|
24
|
+
*/
|
|
25
|
+
export function luminance(r: number, g: number, b: number): number {
|
|
26
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sample a gradient and drop the alpha channel. Treemap / sunburst
|
|
31
|
+
* fills carry alpha separately (see {@link leafRGBA}); this is the
|
|
32
|
+
* "just give me the RGB" entry point.
|
|
33
|
+
*/
|
|
34
|
+
export function sampleRGB(
|
|
35
|
+
stops: GradientStop[],
|
|
36
|
+
t: number,
|
|
37
|
+
): [number, number, number] {
|
|
38
|
+
const c = sampleGradient(stops, t);
|
|
39
|
+
return [c[0], c[1], c[2]];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a leaf's fill color according to the chart's color mode:
|
|
44
|
+
* - `"numeric"` — sign-aware gradient sample via `colorValueToT`.
|
|
45
|
+
* - `"series"` / `"empty"` — discrete palette lookup keyed by the
|
|
46
|
+
* node's `colorLabel` (composite of group_by levels in series mode;
|
|
47
|
+
* `""` in empty mode, which maps to `palette[0]`).
|
|
48
|
+
*
|
|
49
|
+
* Returns RGB only; the alpha channel is applied separately by
|
|
50
|
+
* {@link leafRGBA} using `negativeAlpha` for leaves whose raw size was
|
|
51
|
+
* negative.
|
|
52
|
+
*/
|
|
53
|
+
export function leafColor(
|
|
54
|
+
chart: TreeChartBase,
|
|
55
|
+
nodeId: number,
|
|
56
|
+
stops: GradientStop[],
|
|
57
|
+
palette: Vec3[],
|
|
58
|
+
): [number, number, number] {
|
|
59
|
+
const store = chart._nodeStore;
|
|
60
|
+
const colorValue = store.colorValue[nodeId];
|
|
61
|
+
if (
|
|
62
|
+
chart._colorMode === "numeric" &&
|
|
63
|
+
!isNaN(colorValue) &&
|
|
64
|
+
chart._colorMax > chart._colorMin
|
|
65
|
+
) {
|
|
66
|
+
return sampleRGB(
|
|
67
|
+
stops,
|
|
68
|
+
colorValueToT(colorValue, chart._colorMin, chart._colorMax),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const idx = chart._uniqueColorLabels.get(store.colorLabel[nodeId]) ?? 0;
|
|
73
|
+
return palette[idx % palette.length] ?? [0, 0, 0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* `leafColor` + an alpha channel. Negative-size leaves receive
|
|
78
|
+
* `negativeAlpha` (mirrors `theme.areaOpacity` for area charts) so
|
|
79
|
+
* they stay visually distinguishable from positive leaves without
|
|
80
|
+
* disappearing.
|
|
81
|
+
*/
|
|
82
|
+
export function leafRGBA(
|
|
83
|
+
chart: TreeChartBase,
|
|
84
|
+
nodeId: number,
|
|
85
|
+
stops: GradientStop[],
|
|
86
|
+
palette: Vec3[],
|
|
87
|
+
negativeAlpha: number,
|
|
88
|
+
): [number, number, number, number] {
|
|
89
|
+
const rgb = leafColor(chart, nodeId, stops, palette);
|
|
90
|
+
const alpha = chart._nodeStore.sizeSign[nodeId] < 0 ? negativeAlpha : 1.0;
|
|
91
|
+
return [rgb[0], rgb[1], rgb[2], alpha];
|
|
92
|
+
}
|