@perspective-dev/viewer-charts 4.3.0 → 4.5.1
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/axis/bar-axis.d.ts +9 -1
- package/dist/esm/axis/categorical-axis.d.ts +0 -2
- package/dist/esm/charts/cartesian/cartesian.d.ts +26 -0
- package/dist/esm/charts/common/category-axis-resolver.d.ts +43 -1
- package/dist/esm/charts/common/expand-domain.d.ts +20 -0
- package/dist/esm/charts/common/tree-chart.d.ts +7 -0
- package/dist/esm/charts/common/tree-chrome.d.ts +23 -1
- package/dist/esm/charts/common/tree-interact.d.ts +46 -0
- package/dist/esm/charts/series/glyphs/draw-lines.d.ts +11 -4
- package/dist/esm/charts/series/series-build.d.ts +38 -2
- package/dist/esm/charts/series/series-render.d.ts +1 -4
- package/dist/esm/charts/series/series-type.d.ts +19 -17
- package/dist/esm/charts/series/series.d.ts +16 -0
- package/dist/esm/charts/sunburst/sunburst-interact.d.ts +1 -1
- package/dist/esm/charts/treemap/treemap-interact.d.ts +1 -6
- package/dist/esm/interaction/host-sink-message.d.ts +10 -28
- package/dist/esm/interaction/raw-event-forwarder.d.ts +6 -7
- package/dist/esm/interaction/zoom-controller.d.ts +31 -20
- package/dist/esm/interaction/zoom-router.d.ts +3 -26
- package/dist/esm/perspective-viewer-charts.js +2 -2
- package/dist/esm/perspective-viewer-charts.js.map +3 -3
- package/dist/esm/plugin/plugin.d.ts +0 -1
- package/dist/esm/theme/palette.d.ts +0 -5
- package/dist/esm/transport/protocol.d.ts +2 -7
- package/dist/esm/worker/renderer.worker.d.ts +2 -4
- package/package.json +1 -1
- package/src/ts/axis/bar-axis.ts +74 -45
- package/src/ts/axis/categorical-axis.ts +0 -2
- package/src/ts/charts/candlestick/candlestick-render.ts +10 -7
- package/src/ts/charts/candlestick/candlestick.ts +10 -29
- package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +36 -2
- package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +36 -2
- package/src/ts/charts/cartesian/cartesian-build.ts +143 -9
- package/src/ts/charts/cartesian/cartesian-render.ts +205 -30
- package/src/ts/charts/cartesian/cartesian.ts +43 -4
- package/src/ts/charts/cartesian/glyphs/density.ts +36 -41
- package/src/ts/charts/cartesian/glyphs/lines.ts +13 -15
- package/src/ts/charts/cartesian/glyphs/points.ts +12 -17
- package/src/ts/charts/chart-base.ts +20 -6
- package/src/ts/charts/chart.ts +1 -1
- package/src/ts/charts/common/category-axis-resolver.ts +135 -1
- package/src/ts/charts/common/expand-domain.ts +40 -0
- package/src/ts/charts/common/tree-chart.ts +16 -0
- package/src/ts/charts/common/tree-chrome.ts +86 -1
- package/src/ts/charts/common/tree-interact.ts +209 -0
- package/src/ts/charts/heatmap/heatmap-render.ts +9 -11
- package/src/ts/charts/series/glyphs/draw-areas.ts +30 -1
- package/src/ts/charts/series/glyphs/draw-lines.ts +151 -76
- package/src/ts/charts/series/series-build.ts +394 -21
- package/src/ts/charts/series/series-render.ts +159 -38
- package/src/ts/charts/series/series-type.ts +37 -17
- package/src/ts/charts/series/series.ts +63 -68
- package/src/ts/charts/sunburst/sunburst-interact.ts +18 -162
- package/src/ts/charts/sunburst/sunburst-render.ts +24 -89
- package/src/ts/charts/sunburst/sunburst.ts +1 -15
- package/src/ts/charts/treemap/treemap-interact.ts +22 -189
- package/src/ts/charts/treemap/treemap-render.ts +19 -46
- package/src/ts/charts/treemap/treemap.ts +1 -16
- package/src/ts/interaction/host-sink-message.ts +33 -22
- package/src/ts/interaction/raw-event-forwarder.ts +10 -12
- package/src/ts/interaction/zoom-controller.ts +120 -83
- package/src/ts/interaction/zoom-router.ts +3 -126
- package/src/ts/map/tile-layer.ts +13 -13
- package/src/ts/plugin/plugin.ts +100 -184
- package/src/ts/shaders/line-uniform.frag.glsl +2 -1
- package/src/ts/shaders/line-uniform.vert.glsl +19 -0
- package/src/ts/theme/palette.ts +1 -4
- package/src/ts/transport/protocol.ts +3 -8
- package/src/ts/worker/dispatch.ts +0 -1
- package/src/ts/worker/renderer.worker.ts +10 -46
|
@@ -24,134 +24,11 @@ export interface ZoomTarget {
|
|
|
24
24
|
controller: ZoomController;
|
|
25
25
|
layout: PlotLayout;
|
|
26
26
|
}
|
|
27
|
-
export type ZoomTargetResolver = (mx: number, my: number) => ZoomTarget | null;
|
|
28
27
|
|
|
29
28
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* controllers (one per facet) can coexist on a single canvas.
|
|
34
|
-
*/
|
|
35
|
-
export class ZoomRouter {
|
|
36
|
-
private _element: HTMLElement | null = null;
|
|
37
|
-
private _resolve: ZoomTargetResolver | null = null;
|
|
38
|
-
private _onUpdate: (() => void) | null = null;
|
|
39
|
-
|
|
40
|
-
private _pointerDown = false;
|
|
41
|
-
private _pointerTarget: ZoomTarget | null = null;
|
|
42
|
-
private _lastPointerX = 0;
|
|
43
|
-
private _lastPointerY = 0;
|
|
44
|
-
|
|
45
|
-
private _onWheel: ((e: WheelEvent) => void) | null = null;
|
|
46
|
-
private _onPointerDown: ((e: PointerEvent) => void) | null = null;
|
|
47
|
-
private _onPointerMove: ((e: PointerEvent) => void) | null = null;
|
|
48
|
-
private _onPointerUp: ((e: PointerEvent) => void) | null = null;
|
|
49
|
-
|
|
50
|
-
attach(
|
|
51
|
-
element: HTMLElement,
|
|
52
|
-
resolve: ZoomTargetResolver,
|
|
53
|
-
onUpdate: () => void,
|
|
54
|
-
): void {
|
|
55
|
-
this.detach();
|
|
56
|
-
this._element = element;
|
|
57
|
-
this._resolve = resolve;
|
|
58
|
-
this._onUpdate = onUpdate;
|
|
59
|
-
|
|
60
|
-
this._onWheel = (e: WheelEvent) => {
|
|
61
|
-
const rect = element.getBoundingClientRect();
|
|
62
|
-
const mouseX = e.clientX - rect.left;
|
|
63
|
-
const mouseY = e.clientY - rect.top;
|
|
64
|
-
const target = resolve(mouseX, mouseY);
|
|
65
|
-
if (!target) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
applyWheel(target, mouseX, mouseY, e.deltaY);
|
|
71
|
-
onUpdate();
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
this._onPointerDown = (e: PointerEvent) => {
|
|
75
|
-
const rect = element.getBoundingClientRect();
|
|
76
|
-
const mouseX = e.clientX - rect.left;
|
|
77
|
-
const mouseY = e.clientY - rect.top;
|
|
78
|
-
const target = resolve(mouseX, mouseY);
|
|
79
|
-
if (!target) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this._pointerDown = true;
|
|
84
|
-
this._pointerTarget = target;
|
|
85
|
-
this._lastPointerX = e.clientX;
|
|
86
|
-
this._lastPointerY = e.clientY;
|
|
87
|
-
element.setPointerCapture(e.pointerId);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
this._onPointerMove = (e: PointerEvent) => {
|
|
91
|
-
if (!this._pointerDown || !this._pointerTarget) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const dx = e.clientX - this._lastPointerX;
|
|
96
|
-
const dy = e.clientY - this._lastPointerY;
|
|
97
|
-
this._lastPointerX = e.clientX;
|
|
98
|
-
this._lastPointerY = e.clientY;
|
|
99
|
-
applyPan(this._pointerTarget, dx, dy);
|
|
100
|
-
onUpdate();
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
this._onPointerUp = () => {
|
|
104
|
-
this._pointerDown = false;
|
|
105
|
-
this._pointerTarget = null;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
element.addEventListener("wheel", this._onWheel, { passive: false });
|
|
109
|
-
element.addEventListener("pointerdown", this._onPointerDown);
|
|
110
|
-
element.addEventListener("pointermove", this._onPointerMove);
|
|
111
|
-
element.addEventListener("pointerup", this._onPointerUp);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
detach(): void {
|
|
115
|
-
if (this._element) {
|
|
116
|
-
if (this._onWheel) {
|
|
117
|
-
this._element.removeEventListener("wheel", this._onWheel);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (this._onPointerDown) {
|
|
121
|
-
this._element.removeEventListener(
|
|
122
|
-
"pointerdown",
|
|
123
|
-
this._onPointerDown,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (this._onPointerMove) {
|
|
128
|
-
this._element.removeEventListener(
|
|
129
|
-
"pointermove",
|
|
130
|
-
this._onPointerMove,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (this._onPointerUp) {
|
|
135
|
-
this._element.removeEventListener(
|
|
136
|
-
"pointerup",
|
|
137
|
-
this._onPointerUp,
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this._element = null;
|
|
143
|
-
this._resolve = null;
|
|
144
|
-
this._onUpdate = null;
|
|
145
|
-
this._pointerDown = false;
|
|
146
|
-
this._pointerTarget = null;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Apply a wheel-zoom delta to the resolved target. Exported for
|
|
152
|
-
* re-use inside the worker, where the renderer dispatches interaction
|
|
153
|
-
* events forwarded from the host's `RawEventForwarder` instead of
|
|
154
|
-
* receiving DOM events directly.
|
|
29
|
+
* Apply a wheel-zoom delta to the resolved target. The worker
|
|
30
|
+
* dispatches interaction events forwarded from the host's
|
|
31
|
+
* `RawEventForwarder` and calls this directly.
|
|
155
32
|
*/
|
|
156
33
|
export function applyWheel(
|
|
157
34
|
target: ZoomTarget,
|
package/src/ts/map/tile-layer.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { TileLoader, tileKey } from "./tile-loader";
|
|
|
18
18
|
import type { TileSource } from "./tile-source";
|
|
19
19
|
import tileVert from "../shaders/tile.vert.glsl";
|
|
20
20
|
import tileFrag from "../shaders/tile.frag.glsl";
|
|
21
|
+
import { compileProgram } from "../webgl/program-cache";
|
|
21
22
|
|
|
22
23
|
type GL = WebGL2RenderingContext | WebGLRenderingContext;
|
|
23
24
|
|
|
@@ -344,24 +345,23 @@ export class TileLayer {
|
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
const gl = glManager.gl;
|
|
347
|
-
|
|
348
|
+
this._program = compileProgram<TileProgramCache>(
|
|
349
|
+
glManager,
|
|
348
350
|
"map-tile",
|
|
349
351
|
tileVert,
|
|
350
352
|
tileFrag,
|
|
353
|
+
[
|
|
354
|
+
"u_projection",
|
|
355
|
+
"u_extent_min",
|
|
356
|
+
"u_extent_max",
|
|
357
|
+
"u_uv_min",
|
|
358
|
+
"u_uv_max",
|
|
359
|
+
"u_tile",
|
|
360
|
+
"u_alpha",
|
|
361
|
+
],
|
|
362
|
+
["a_corner"],
|
|
351
363
|
);
|
|
352
364
|
|
|
353
|
-
this._program = {
|
|
354
|
-
program,
|
|
355
|
-
u_projection: gl.getUniformLocation(program, "u_projection"),
|
|
356
|
-
u_extent_min: gl.getUniformLocation(program, "u_extent_min"),
|
|
357
|
-
u_extent_max: gl.getUniformLocation(program, "u_extent_max"),
|
|
358
|
-
u_uv_min: gl.getUniformLocation(program, "u_uv_min"),
|
|
359
|
-
u_uv_max: gl.getUniformLocation(program, "u_uv_max"),
|
|
360
|
-
u_tile: gl.getUniformLocation(program, "u_tile"),
|
|
361
|
-
u_alpha: gl.getUniformLocation(program, "u_alpha"),
|
|
362
|
-
a_corner: gl.getAttribLocation(program, "a_corner"),
|
|
363
|
-
};
|
|
364
|
-
|
|
365
365
|
const buf = gl.createBuffer();
|
|
366
366
|
if (!buf) {
|
|
367
367
|
return;
|
package/src/ts/plugin/plugin.ts
CHANGED
|
@@ -38,173 +38,85 @@ import { RENDER_BLIT_MODE } from "../config";
|
|
|
38
38
|
const FACET_CONFIG_DEFAULTS: FacetConfig = { ...DEFAULT_FACET_CONFIG };
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* Static UI-control spec per `plugin_config` field. Mirrors the shape
|
|
42
|
+
* `column_config_schema` already returns (datagrid). The runtime default
|
|
43
|
+
* is sourced separately from the chart-type-effective defaults at
|
|
44
|
+
* `fieldSpec` call time so per-chart overrides like
|
|
45
|
+
* `include_zero=true` for Y Bar / Y Area / X Bar surface in the UI.
|
|
45
46
|
*/
|
|
47
|
+
type FieldSpec =
|
|
48
|
+
| { kind: "Bool" }
|
|
49
|
+
| {
|
|
50
|
+
kind: "Enum";
|
|
51
|
+
variants: ReadonlyArray<{ value: string; label: string }>;
|
|
52
|
+
}
|
|
53
|
+
| { kind: "Number"; min: number; max: number; step?: number };
|
|
54
|
+
|
|
55
|
+
const FIELD_SCHEMAS: Record<PluginConfigField, FieldSpec> = {
|
|
56
|
+
auto_alt_y_axis: { kind: "Bool" },
|
|
57
|
+
include_zero: { kind: "Bool" },
|
|
58
|
+
domain_mode: {
|
|
59
|
+
kind: "Enum",
|
|
60
|
+
variants: [
|
|
61
|
+
{ value: "fit", label: "Fit" },
|
|
62
|
+
{ value: "expand", label: "Expand" },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
facet_mode: {
|
|
66
|
+
kind: "Enum",
|
|
67
|
+
variants: [
|
|
68
|
+
{ value: "grid", label: "Grid" },
|
|
69
|
+
{ value: "overlay", label: "Overlay" },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
facet_zoom_mode: {
|
|
73
|
+
kind: "Enum",
|
|
74
|
+
variants: [
|
|
75
|
+
{ value: "shared", label: "Shared" },
|
|
76
|
+
{ value: "independent", label: "Independent" },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
series_zoom_mode: {
|
|
80
|
+
kind: "Enum",
|
|
81
|
+
variants: [
|
|
82
|
+
{ value: "dynamic", label: "Dynamic" },
|
|
83
|
+
{ value: "fixed", label: "Fixed" },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
line_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 16 },
|
|
87
|
+
point_size_px: { kind: "Number", min: 1, max: 32 },
|
|
88
|
+
band_inner_frac: { kind: "Number", min: 0.1, max: 1, step: 0.01 },
|
|
89
|
+
bar_inner_pad: { kind: "Number", min: 0, max: 0.9, step: 0.01 },
|
|
90
|
+
wick_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 8 },
|
|
91
|
+
ohlc_line_width_px: { kind: "Number", min: 0.5, step: 0.5, max: 8 },
|
|
92
|
+
gradient_radius_px: { kind: "Number", min: 2, step: 1, max: 256 },
|
|
93
|
+
gradient_intensity: { kind: "Number", min: 0.05, step: 0.05, max: 4 },
|
|
94
|
+
gradient_heat_max: { kind: "Number", min: 0.1, step: 0.1, max: 64 },
|
|
95
|
+
gradient_color_mode: {
|
|
96
|
+
kind: "Enum",
|
|
97
|
+
variants: [
|
|
98
|
+
{ value: "mean", label: "Mean (density-weighted)" },
|
|
99
|
+
{ value: "density", label: "Density only" },
|
|
100
|
+
{ value: "extreme", label: "Extremes" },
|
|
101
|
+
{ value: "signed", label: "Signed sum" },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
map_tile_provider: {
|
|
105
|
+
kind: "Enum",
|
|
106
|
+
variants: [
|
|
107
|
+
{ value: "carto-positron", label: "Light (Positron)" },
|
|
108
|
+
{ value: "carto-dark-matter", label: "Dark Matter" },
|
|
109
|
+
{ value: "carto-voyager", label: "Voyager" },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
map_tile_alpha: { kind: "Number", min: 0, max: 1, step: 0.05 },
|
|
113
|
+
};
|
|
114
|
+
|
|
46
115
|
function fieldSpec(
|
|
47
116
|
key: PluginConfigField,
|
|
48
117
|
defaults: PluginConfig,
|
|
49
118
|
): Record<string, unknown> & { kind: string } {
|
|
50
|
-
|
|
51
|
-
case "auto_alt_y_axis":
|
|
52
|
-
return { kind: "Bool", key, default: defaults.auto_alt_y_axis };
|
|
53
|
-
case "include_zero":
|
|
54
|
-
return { kind: "Bool", key, default: defaults.include_zero };
|
|
55
|
-
case "domain_mode":
|
|
56
|
-
return {
|
|
57
|
-
kind: "Enum",
|
|
58
|
-
key,
|
|
59
|
-
default: defaults.domain_mode,
|
|
60
|
-
variants: [
|
|
61
|
-
{ value: "fit", label: "Fit" },
|
|
62
|
-
{ value: "expand", label: "Expand" },
|
|
63
|
-
],
|
|
64
|
-
};
|
|
65
|
-
case "facet_mode":
|
|
66
|
-
return {
|
|
67
|
-
kind: "Enum",
|
|
68
|
-
key,
|
|
69
|
-
default: DEFAULT_PLUGIN_CONFIG.facet_mode,
|
|
70
|
-
variants: [
|
|
71
|
-
{ value: "grid", label: "Grid" },
|
|
72
|
-
{ value: "overlay", label: "Overlay" },
|
|
73
|
-
],
|
|
74
|
-
};
|
|
75
|
-
case "facet_zoom_mode":
|
|
76
|
-
return {
|
|
77
|
-
kind: "Enum",
|
|
78
|
-
key,
|
|
79
|
-
default: DEFAULT_PLUGIN_CONFIG.facet_zoom_mode,
|
|
80
|
-
variants: [
|
|
81
|
-
{ value: "shared", label: "Shared" },
|
|
82
|
-
{ value: "independent", label: "Independent" },
|
|
83
|
-
],
|
|
84
|
-
};
|
|
85
|
-
case "series_zoom_mode":
|
|
86
|
-
return {
|
|
87
|
-
kind: "Enum",
|
|
88
|
-
key,
|
|
89
|
-
default: DEFAULT_PLUGIN_CONFIG.series_zoom_mode,
|
|
90
|
-
variants: [
|
|
91
|
-
{ value: "dynamic", label: "Dynamic" },
|
|
92
|
-
{ value: "fixed", label: "Fixed" },
|
|
93
|
-
],
|
|
94
|
-
};
|
|
95
|
-
case "line_width_px":
|
|
96
|
-
return {
|
|
97
|
-
kind: "Number",
|
|
98
|
-
key,
|
|
99
|
-
default: DEFAULT_PLUGIN_CONFIG.line_width_px,
|
|
100
|
-
min: 0.5,
|
|
101
|
-
step: 0.5,
|
|
102
|
-
max: 16,
|
|
103
|
-
};
|
|
104
|
-
case "point_size_px":
|
|
105
|
-
return {
|
|
106
|
-
kind: "Number",
|
|
107
|
-
key,
|
|
108
|
-
default: DEFAULT_PLUGIN_CONFIG.point_size_px,
|
|
109
|
-
min: 1,
|
|
110
|
-
max: 32,
|
|
111
|
-
};
|
|
112
|
-
case "band_inner_frac":
|
|
113
|
-
return {
|
|
114
|
-
kind: "Number",
|
|
115
|
-
key,
|
|
116
|
-
default: DEFAULT_PLUGIN_CONFIG.band_inner_frac,
|
|
117
|
-
min: 0.1,
|
|
118
|
-
max: 1,
|
|
119
|
-
step: 0.01,
|
|
120
|
-
};
|
|
121
|
-
case "bar_inner_pad":
|
|
122
|
-
return {
|
|
123
|
-
kind: "Number",
|
|
124
|
-
key,
|
|
125
|
-
default: DEFAULT_PLUGIN_CONFIG.bar_inner_pad,
|
|
126
|
-
min: 0,
|
|
127
|
-
max: 0.9,
|
|
128
|
-
step: 0.01,
|
|
129
|
-
};
|
|
130
|
-
case "wick_width_px":
|
|
131
|
-
return {
|
|
132
|
-
kind: "Number",
|
|
133
|
-
key,
|
|
134
|
-
default: DEFAULT_PLUGIN_CONFIG.wick_width_px,
|
|
135
|
-
min: 0.5,
|
|
136
|
-
step: 0.5,
|
|
137
|
-
max: 8,
|
|
138
|
-
};
|
|
139
|
-
case "ohlc_line_width_px":
|
|
140
|
-
return {
|
|
141
|
-
kind: "Number",
|
|
142
|
-
key,
|
|
143
|
-
default: DEFAULT_PLUGIN_CONFIG.ohlc_line_width_px,
|
|
144
|
-
min: 0.5,
|
|
145
|
-
step: 0.5,
|
|
146
|
-
max: 8,
|
|
147
|
-
};
|
|
148
|
-
case "gradient_radius_px":
|
|
149
|
-
return {
|
|
150
|
-
kind: "Number",
|
|
151
|
-
key,
|
|
152
|
-
default: DEFAULT_PLUGIN_CONFIG.gradient_radius_px,
|
|
153
|
-
min: 2,
|
|
154
|
-
step: 1,
|
|
155
|
-
max: 256,
|
|
156
|
-
};
|
|
157
|
-
case "gradient_intensity":
|
|
158
|
-
return {
|
|
159
|
-
kind: "Number",
|
|
160
|
-
key,
|
|
161
|
-
default: DEFAULT_PLUGIN_CONFIG.gradient_intensity,
|
|
162
|
-
min: 0.05,
|
|
163
|
-
step: 0.05,
|
|
164
|
-
max: 4,
|
|
165
|
-
};
|
|
166
|
-
case "gradient_heat_max":
|
|
167
|
-
return {
|
|
168
|
-
kind: "Number",
|
|
169
|
-
key,
|
|
170
|
-
default: DEFAULT_PLUGIN_CONFIG.gradient_heat_max,
|
|
171
|
-
min: 0.1,
|
|
172
|
-
step: 0.1,
|
|
173
|
-
max: 64,
|
|
174
|
-
};
|
|
175
|
-
case "gradient_color_mode":
|
|
176
|
-
return {
|
|
177
|
-
kind: "Enum",
|
|
178
|
-
key,
|
|
179
|
-
default: DEFAULT_PLUGIN_CONFIG.gradient_color_mode,
|
|
180
|
-
variants: [
|
|
181
|
-
{ value: "mean", label: "Mean (density-weighted)" },
|
|
182
|
-
{ value: "density", label: "Density only" },
|
|
183
|
-
{ value: "extreme", label: "Extremes" },
|
|
184
|
-
{ value: "signed", label: "Signed sum" },
|
|
185
|
-
],
|
|
186
|
-
};
|
|
187
|
-
case "map_tile_provider":
|
|
188
|
-
return {
|
|
189
|
-
kind: "Enum",
|
|
190
|
-
key,
|
|
191
|
-
default: DEFAULT_PLUGIN_CONFIG.map_tile_provider,
|
|
192
|
-
variants: [
|
|
193
|
-
{ value: "carto-positron", label: "Light (Positron)" },
|
|
194
|
-
{ value: "carto-dark-matter", label: "Dark Matter" },
|
|
195
|
-
{ value: "carto-voyager", label: "Voyager" },
|
|
196
|
-
],
|
|
197
|
-
};
|
|
198
|
-
case "map_tile_alpha":
|
|
199
|
-
return {
|
|
200
|
-
kind: "Number",
|
|
201
|
-
key,
|
|
202
|
-
default: DEFAULT_PLUGIN_CONFIG.map_tile_alpha,
|
|
203
|
-
min: 0,
|
|
204
|
-
max: 1,
|
|
205
|
-
step: 0.05,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
119
|
+
return { ...FIELD_SCHEMAS[key], key, default: defaults[key] };
|
|
208
120
|
}
|
|
209
121
|
|
|
210
122
|
const GLOBAL_STYLES = (() => {
|
|
@@ -519,6 +431,32 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
519
431
|
default: false,
|
|
520
432
|
});
|
|
521
433
|
}
|
|
434
|
+
|
|
435
|
+
// Line / area glyphs can bridge interior nulls by linear
|
|
436
|
+
// interpolation. Bar / scatter ignore the flag.
|
|
437
|
+
const supports_interpolate =
|
|
438
|
+
effective_chart_type === "line" ||
|
|
439
|
+
effective_chart_type === "area";
|
|
440
|
+
|
|
441
|
+
if (supports_interpolate) {
|
|
442
|
+
const variants =
|
|
443
|
+
effective_chart_type === "area"
|
|
444
|
+
? [
|
|
445
|
+
{ value: "skip", label: "Skip" },
|
|
446
|
+
{ value: "solid", label: "Solid" },
|
|
447
|
+
]
|
|
448
|
+
: [
|
|
449
|
+
{ value: "skip", label: "Skip" },
|
|
450
|
+
{ value: "solid", label: "Solid" },
|
|
451
|
+
{ value: "transparent", label: "Transparent" },
|
|
452
|
+
];
|
|
453
|
+
fields.push({
|
|
454
|
+
kind: "Enum",
|
|
455
|
+
key: "interpolate",
|
|
456
|
+
default: "solid",
|
|
457
|
+
variants,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
522
460
|
}
|
|
523
461
|
|
|
524
462
|
// Per-column formatter widgets. Surfaced for every chart type so
|
|
@@ -599,28 +537,6 @@ export class HTMLPerspectiveViewerWebGLPluginElement
|
|
|
599
537
|
return 5;
|
|
600
538
|
}
|
|
601
539
|
|
|
602
|
-
save() {
|
|
603
|
-
const state: any = {};
|
|
604
|
-
const zoom = this._renderer?.saveZoom();
|
|
605
|
-
if (zoom) {
|
|
606
|
-
state.zoom = zoom;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Only emit the keys this chart actually consumes.
|
|
610
|
-
const cfg: Partial<PluginConfig> = {};
|
|
611
|
-
for (const key of this._chartType.applicable_plugin_fields) {
|
|
612
|
-
// `key` is `PluginConfigField` = `keyof PluginConfig`, so this
|
|
613
|
-
// indexed assignment is type-safe without a cast.
|
|
614
|
-
(cfg[key] as PluginConfig[typeof key]) = this._pluginConfig[key];
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (Object.keys(cfg).length > 0) {
|
|
618
|
-
state.plugin_config = cfg;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return state;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
540
|
async render(view: View): Promise<Blob> {
|
|
625
541
|
await this._ensureRenderer(view);
|
|
626
542
|
await this.draw(view);
|
|
@@ -16,11 +16,12 @@ uniform vec4 u_color;
|
|
|
16
16
|
uniform float u_line_width;
|
|
17
17
|
|
|
18
18
|
varying float v_edge_dist;
|
|
19
|
+
varying float v_seg_alpha;
|
|
19
20
|
|
|
20
21
|
void main() {
|
|
21
22
|
float dist = abs(v_edge_dist);
|
|
22
23
|
float coreEdge = u_line_width / (u_line_width + 1.5);
|
|
23
24
|
float alpha = 1.0 - smoothstep(coreEdge, 1.0, dist);
|
|
24
25
|
|
|
25
|
-
gl_FragColor = vec4(u_color.rgb, u_color.a * alpha);
|
|
26
|
+
gl_FragColor = vec4(u_color.rgb, u_color.a * alpha * v_seg_alpha);
|
|
26
27
|
}
|
|
@@ -22,11 +22,27 @@ attribute vec2 a_end;
|
|
|
22
22
|
// 0 = start+left, 1 = start+right, 2 = end+left, 3 = end+right
|
|
23
23
|
attribute float a_corner;
|
|
24
24
|
|
|
25
|
+
// Per-segment "is endpoint a real source-data cell" flags. Both vertices
|
|
26
|
+
// of a segment's quad see the same instance values, so `v_seg_alpha`
|
|
27
|
+
// below is constant across the quad — no gradient fade. Read from the
|
|
28
|
+
// per-cell real-flag buffer with offset 0 / 1 (same overlap trick as
|
|
29
|
+
// the bar-line glyph's segment-position attributes).
|
|
30
|
+
attribute float a_real_start;
|
|
31
|
+
attribute float a_real_end;
|
|
32
|
+
|
|
25
33
|
uniform mat4 u_projection;
|
|
26
34
|
uniform vec2 u_resolution;
|
|
27
35
|
uniform float u_line_width;
|
|
28
36
|
|
|
37
|
+
// Alpha multiplier applied to any segment whose endpoints are not both
|
|
38
|
+
// real. Set per draw based on the series' interpolate mode:
|
|
39
|
+
// 0.0 = skip (gaps at synthesized cells)
|
|
40
|
+
// 1.0 = solid (no visible difference; default for non-line)
|
|
41
|
+
// 0.5 = transparent (50% opacity on segments touching synthesized cells)
|
|
42
|
+
uniform float u_interp_alpha;
|
|
43
|
+
|
|
29
44
|
varying float v_edge_dist;
|
|
45
|
+
varying float v_seg_alpha;
|
|
30
46
|
|
|
31
47
|
void main() {
|
|
32
48
|
vec4 clipStart = u_projection * vec4(a_start, 0.0, 1.0);
|
|
@@ -51,4 +67,7 @@ void main() {
|
|
|
51
67
|
|
|
52
68
|
gl_Position = clipPos + vec4(clipOffset, 0.0, 0.0);
|
|
53
69
|
v_edge_dist = side;
|
|
70
|
+
|
|
71
|
+
float bothReal = a_real_start * a_real_end;
|
|
72
|
+
v_seg_alpha = mix(u_interp_alpha, 1.0, step(0.5, bothReal));
|
|
54
73
|
}
|
package/src/ts/theme/palette.ts
CHANGED
|
@@ -18,10 +18,7 @@ export type Vec3 = [number, number, number];
|
|
|
18
18
|
* Build a series palette of length `count` by sampling the theme gradient
|
|
19
19
|
* at evenly-spaced offsets. For count == 1 returns the 50% stop.
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
stops: GradientStop[],
|
|
23
|
-
count: number,
|
|
24
|
-
): Vec3[] {
|
|
21
|
+
function interpolatePalette(stops: GradientStop[], count: number): Vec3[] {
|
|
25
22
|
if (count <= 0) {
|
|
26
23
|
return [];
|
|
27
24
|
}
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
import type { FacetConfig, PluginConfig } from "../charts/chart";
|
|
14
14
|
import type { PerspectiveClickDetail } from "../event-detail";
|
|
15
|
+
import type { ThemeSnapshot } from "../theme/theme";
|
|
15
16
|
import type { ViewConfig } from "@perspective-dev/client";
|
|
16
17
|
|
|
18
|
+
export type { ThemeSnapshot };
|
|
19
|
+
|
|
17
20
|
/**
|
|
18
21
|
* Worker-mode control-channel messages. Distinct from the perspective
|
|
19
22
|
* `ProxySession` channel that the worker's `Client` uses to talk to the
|
|
@@ -222,14 +225,6 @@ export interface FontFaceDescriptor {
|
|
|
222
225
|
display?: string;
|
|
223
226
|
}
|
|
224
227
|
|
|
225
|
-
/**
|
|
226
|
-
* Theme values resolved on the host via `getComputedStyle` and shipped
|
|
227
|
-
* to the renderer scope, which has no DOM. Decoded by the chart via
|
|
228
|
-
* `theme/theme.ts::resolveThemeFromVars`. Plain map for
|
|
229
|
-
* structured-clone.
|
|
230
|
-
*/
|
|
231
|
-
export type ThemeSnapshot = Record<string, string>;
|
|
232
|
-
|
|
233
228
|
export interface SetViewByNameMsg {
|
|
234
229
|
kind: "setViewByName";
|
|
235
230
|
name: string;
|
|
@@ -177,41 +177,11 @@ export class WorkerRenderer {
|
|
|
177
177
|
this.chartImpl.setView?.(view);
|
|
178
178
|
this.glManager.bufferPool.maxCapacity = msg.bufferMaxCapacity;
|
|
179
179
|
this.glManager.resize(msg.cssWidth, msg.cssHeight, msg.dpr);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
lines: envelope.payload.lines,
|
|
186
|
-
pos: envelope.payload.pos,
|
|
187
|
-
bounds: envelope.payload.bounds,
|
|
188
|
-
});
|
|
189
|
-
break;
|
|
190
|
-
case "dismiss":
|
|
191
|
-
this.post({ kind: "dismissTooltip" });
|
|
192
|
-
break;
|
|
193
|
-
case "setCursor":
|
|
194
|
-
this.post({ kind: "setCursor", cursor: envelope.cursor });
|
|
195
|
-
break;
|
|
196
|
-
case "userClick":
|
|
197
|
-
this.post({
|
|
198
|
-
kind: "userClick",
|
|
199
|
-
detail: envelope.payload as any,
|
|
200
|
-
});
|
|
201
|
-
break;
|
|
202
|
-
case "userSelect":
|
|
203
|
-
this.post({
|
|
204
|
-
kind: "userSelect",
|
|
205
|
-
selected: envelope.payload.selected,
|
|
206
|
-
row: envelope.payload.row,
|
|
207
|
-
column_names: envelope.payload.column_names,
|
|
208
|
-
insertConfig: envelope.payload.insertConfig as any,
|
|
209
|
-
});
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
this.chartImpl.attachTooltip?.(hostSink);
|
|
180
|
+
// `MessageHostSink` emits `WorkerMsg`s directly — no translation
|
|
181
|
+
// needed here.
|
|
182
|
+
this.chartImpl.attachTooltip?.(
|
|
183
|
+
new MessageHostSink((msg) => this.post(msg)),
|
|
184
|
+
);
|
|
215
185
|
}
|
|
216
186
|
|
|
217
187
|
setViewByName(name: string): void {
|
|
@@ -309,13 +279,9 @@ export class WorkerRenderer {
|
|
|
309
279
|
}
|
|
310
280
|
}
|
|
311
281
|
} catch (err) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
// this method). Surface to the worker console; the host's
|
|
316
|
-
// pending promise still gets resolved via the `finally`
|
|
317
|
-
// ack below so `draw()` can't deadlock on a renderer error.
|
|
318
|
-
console.error("loadAndRender failed", err);
|
|
282
|
+
if ((err + "").indexOf("View not found") === -1) {
|
|
283
|
+
console.error("loadAndRender failed", err);
|
|
284
|
+
}
|
|
319
285
|
} finally {
|
|
320
286
|
this.post({ kind: "loadAndRenderAck", msgId: msg.msgId });
|
|
321
287
|
}
|
|
@@ -417,10 +383,8 @@ export class WorkerRenderer {
|
|
|
417
383
|
|
|
418
384
|
/**
|
|
419
385
|
* Hit-test the cursor against the chart's facet grid (in faceted
|
|
420
|
-
* mode) or its current layout (single-plot).
|
|
421
|
-
*
|
|
422
|
-
* worker owns the facet grid and controllers, so the resolution
|
|
423
|
-
* runs here.
|
|
386
|
+
* mode) or its current layout (single-plot). The worker owns the
|
|
387
|
+
* facet grid and controllers, so the resolution runs here.
|
|
424
388
|
*/
|
|
425
389
|
private _resolveTarget(mx: number, my: number): ZoomTarget | null {
|
|
426
390
|
const chart = this.chartImpl as any;
|