@fjader/svelte 0.2.2 → 0.2.4
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/README.md +2 -0
- package/dist/components/axes/AxisBottom.svelte +23 -6
- package/dist/components/axes/AxisBottom.svelte.d.ts +10 -0
- package/dist/components/axes/AxisLeft.svelte +77 -8
- package/dist/components/axes/AxisLeft.svelte.d.ts +17 -0
- package/dist/components/axes/BandAxisTicks.svelte +64 -25
- package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
- package/dist/components/axes/axis-title.d.ts +30 -0
- package/dist/components/axes/axis-title.js +77 -0
- package/dist/components/charts/ChartCell.svelte +1 -1
- package/dist/components/charts/ChartContainer.svelte +12 -3
- package/dist/components/charts/ChartContainer.svelte.d.ts +7 -0
- package/dist/components/guides/legend/ContinuousColorLegend.svelte +9 -2
- package/dist/components/guides/legend/OrdinalColorLegend.svelte +96 -35
- package/dist/components/marks/AreaMarkX.svelte +21 -6
- package/dist/components/marks/AreaMarkY.svelte +21 -6
- package/dist/components/marks/BarMarkX.svelte +16 -1
- package/dist/components/marks/BarMarkY.svelte +16 -1
- package/dist/components/marks/DotsMark.svelte +5 -0
- package/dist/components/marks/LineMark.svelte +8 -1
- package/dist/components/marks/ParallelCoordinatesMark.svelte +10 -0
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +5 -0
- package/dist/components/marks/RectMark.svelte +5 -0
- package/dist/components/marks/RectXMark.svelte +5 -0
- package/dist/components/marks/RectYMark.svelte +5 -0
- package/dist/components/marks/RuleMarkX.svelte +12 -2
- package/dist/components/marks/RuleMarkY.svelte +12 -2
- package/dist/components/marks/TextMark.svelte +76 -13
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +21 -2
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +300 -42
- package/dist/components/marks/lifecycle/domain-queries.d.ts +3 -1
- package/dist/components/marks/lifecycle/domain-queries.js +22 -15
- package/dist/components/marks/lifecycle/domain-registration.js +2 -6
- package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +104 -0
- package/dist/components/marks/lifecycle/other-rows.svelte.js +193 -0
- package/dist/components/marks/lifecycle/other-split.d.ts +80 -0
- package/dist/components/marks/lifecycle/other-split.js +112 -0
- package/dist/components/marks/lifecycle/query-builder.d.ts +26 -1
- package/dist/components/marks/lifecycle/query-builder.js +25 -5
- package/dist/components/marks/lifecycle/transform-scale-key.d.ts +16 -0
- package/dist/components/marks/lifecycle/transform-scale-key.js +27 -0
- package/dist/components/marks/utils/area-outline.d.ts +24 -0
- package/dist/components/marks/utils/area-outline.js +10 -0
- package/dist/components/marks/utils/reactive-config.d.ts +1 -1
- package/dist/components/marks/utils/reactive-config.js +9 -0
- package/dist/components/marks/utils/tooltip-helpers.d.ts +4 -1
- package/dist/components/marks/utils/tooltip-helpers.js +2 -1
- package/dist/components/rendering/Areas.svelte +30 -6
- package/dist/components/rendering/Areas.svelte.d.ts +2 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +3 -1
- package/dist/components/rendering/BaseGPURenderer.svelte.js +8 -3
- package/dist/components/rendering/Dots.svelte +24 -3
- package/dist/components/rendering/Dots.svelte.d.ts +2 -0
- package/dist/components/rendering/GridLine.svelte +2 -1
- package/dist/components/rendering/Lines.svelte +49 -7
- package/dist/components/rendering/Lines.svelte.d.ts +9 -0
- package/dist/components/rendering/ParallelLines.svelte +26 -7
- package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
- package/dist/components/rendering/Rects.svelte +129 -20
- package/dist/components/rendering/Rects.svelte.d.ts +9 -0
- package/dist/components/rendering/RuleX.svelte +20 -5
- package/dist/components/rendering/RuleX.svelte.d.ts +2 -0
- package/dist/components/rendering/RuleY.svelte +20 -5
- package/dist/components/rendering/RuleY.svelte.d.ts +2 -0
- package/dist/components/rendering/activity-reads.d.ts +111 -0
- package/dist/components/rendering/activity-reads.js +154 -0
- package/dist/components/rendering/uniform-sizes.d.ts +26 -0
- package/dist/components/rendering/uniform-sizes.js +28 -0
- package/dist/config/ChartConfig.svelte.d.ts +5 -1
- package/dist/gpu/ColorScaleShaderLib.d.ts +13 -1
- package/dist/gpu/ColorScaleShaderLib.js +56 -5
- package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
- package/dist/gpu/GPUCoordinator.svelte.js +68 -32
- package/dist/gpu/MorphedChannels.d.ts +6 -32
- package/dist/gpu/MorphedChannels.js +45 -3
- package/dist/gpu/ResolveShader.d.ts +2 -2
- package/dist/gpu/ResolveShader.js +2 -2
- package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
- package/dist/gpu/ScaleTemplateSystem.js +111 -11
- package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
- package/dist/gpu/SizeScaleShaderLib.js +4 -4
- package/dist/gpu/identity-slot-packing.d.ts +157 -40
- package/dist/gpu/identity-slot-packing.js +262 -62
- package/dist/gpu/tracked-scales.d.ts +14 -0
- package/dist/gpu/tracked-scales.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/interactors/BandLabelToggle.svelte +5 -9
- package/dist/interactors/BandLabelToggle.svelte.d.ts +3 -3
- package/dist/interactors/DerivedInterval1D.svelte +26 -54
- package/dist/interactors/Interval1D.svelte +8 -2
- package/dist/interactors/Interval2D.svelte +8 -2
- package/dist/interactors/ParallelAxisSource.svelte +14 -5
- package/dist/interactors/ParallelAxisSource.svelte.d.ts +2 -1
- package/dist/interactors/ParallelBrushes.svelte +8 -9
- package/dist/interactors/ParallelBrushes.svelte.d.ts +3 -3
- package/dist/interactors/Toggle.svelte +48 -38
- package/dist/interactors/Toggle.svelte.d.ts +4 -3
- package/dist/interactors/Tooltip.svelte +140 -42
- package/dist/interactors/TooltipManager.svelte.d.ts +20 -5
- package/dist/interactors/TooltipManager.svelte.js +40 -10
- package/dist/interactors/other-tooltip.d.ts +16 -0
- package/dist/interactors/other-tooltip.js +22 -0
- package/dist/interactors/relay-clauses.d.ts +23 -0
- package/dist/interactors/relay-clauses.js +60 -0
- package/dist/query-engine/CrossfilterSelection.svelte.d.ts +88 -15
- package/dist/query-engine/CrossfilterSelection.svelte.js +94 -20
- package/dist/query-engine/FieldActivity.svelte.d.ts +128 -0
- package/dist/query-engine/FieldActivity.svelte.js +378 -0
- package/dist/query-engine/FilterGroup.svelte.d.ts +76 -30
- package/dist/query-engine/FilterGroup.svelte.js +223 -124
- package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +73 -3
- package/dist/query-engine/FilterGroupRegistry.svelte.js +161 -12
- package/dist/query-engine/MarkHandle.svelte.d.ts +9 -1
- package/dist/query-engine/MarkHandle.svelte.js +12 -2
- package/dist/query-engine/QueryEngine.d.ts +52 -16
- package/dist/query-engine/QueryEngine.js +116 -34
- package/dist/query-engine/categorical-identity.d.ts +114 -27
- package/dist/query-engine/categorical-identity.js +225 -51
- package/dist/query-engine/index.d.ts +2 -1
- package/dist/query-engine/index.js +1 -0
- package/dist/query-engine/preagg/Preagg.d.ts +1 -1
- package/dist/query-engine/preagg/Preagg.js +11 -9
- package/dist/query-engine/preagg/plan.d.ts +14 -5
- package/dist/query-engine/preagg/plan.js +44 -18
- package/dist/query-engine/toggle-predicate.d.ts +21 -0
- package/dist/query-engine/toggle-predicate.js +41 -2
- package/dist/scales/ActivityTable.d.ts +103 -0
- package/dist/scales/ActivityTable.js +220 -0
- package/dist/scales/BandScaleState.svelte.d.ts +41 -15
- package/dist/scales/BandScaleState.svelte.js +78 -36
- package/dist/scales/ChartScales.svelte.d.ts +2 -2
- package/dist/scales/ColorScaleState.svelte.d.ts +36 -8
- package/dist/scales/ColorScaleState.svelte.js +118 -24
- package/dist/scales/DomainResolver.d.ts +2 -0
- package/dist/scales/DomainResolver.js +15 -8
- package/dist/scales/ScaleRegistry.svelte.d.ts +9 -0
- package/dist/scales/ScaleRegistry.svelte.js +22 -1
- package/dist/scales/TimeScaleState.svelte.js +7 -1
- package/dist/spec/index.js +1 -0
- package/dist/spec/types.d.ts +4 -0
- package/dist/theme.css +5 -4
- package/dist/transforms/TransformCompiler.d.ts +37 -2
- package/dist/transforms/TransformCompiler.js +44 -1
- package/dist/transforms/index.d.ts +27 -0
- package/dist/transforms/index.js +127 -19
- package/dist/types.d.ts +71 -0
- package/package.json +3 -2
|
@@ -12,6 +12,6 @@ import type { MarkEntry } from '../../../config/ChartConfig.svelte';
|
|
|
12
12
|
* @param entry - The mark entry (reactive props from ChartConfig)
|
|
13
13
|
* @param opts.resolveFitDomain - Getter for resolved fitDomain (deferred read)
|
|
14
14
|
*/
|
|
15
|
-
export declare function buildReactiveConfig(entry: Partial<Pick<MarkEntry, 'sort' | 'limit' | 'domain' | 'tooltip' | 'tip' | 'fitDomain'>>, opts?: {
|
|
15
|
+
export declare function buildReactiveConfig(entry: Partial<Pick<MarkEntry, 'sort' | 'limit' | 'domain' | 'tooltip' | 'tip' | 'fitDomain' | 'selectionResponse' | 'nulls' | 'contributeDomain'>>, opts?: {
|
|
16
16
|
resolveFitDomain?: () => 'x' | 'y' | boolean | undefined;
|
|
17
17
|
}): ReactiveConfig;
|
|
@@ -30,6 +30,15 @@ export function buildReactiveConfig(entry, opts) {
|
|
|
30
30
|
},
|
|
31
31
|
get tip() {
|
|
32
32
|
return entry.tip;
|
|
33
|
+
},
|
|
34
|
+
get selectionResponse() {
|
|
35
|
+
return entry.selectionResponse;
|
|
36
|
+
},
|
|
37
|
+
get nulls() {
|
|
38
|
+
return entry.nulls;
|
|
39
|
+
},
|
|
40
|
+
get contributeDomain() {
|
|
41
|
+
return entry.contributeDomain;
|
|
33
42
|
}
|
|
34
43
|
};
|
|
35
44
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OtherRowStats } from '../lifecycle/other-rows.svelte';
|
|
1
2
|
import type { ChartContext } from '../../../types';
|
|
2
3
|
import type { ColorScaleState } from '../../../scales/ColorScaleState.svelte';
|
|
3
4
|
import type { DimValue } from '../../../query-engine/categorical-identity';
|
|
@@ -34,6 +35,7 @@ interface TooltipConfigParams {
|
|
|
34
35
|
constantRadius?: number;
|
|
35
36
|
sizeScaleState?: SizeScaleState | null;
|
|
36
37
|
displayFields?: string[] | null;
|
|
38
|
+
otherStats?: TooltipConfig['otherStats'];
|
|
37
39
|
}
|
|
38
40
|
interface TooltipConfig {
|
|
39
41
|
tempTable: string;
|
|
@@ -61,7 +63,8 @@ interface TooltipConfig {
|
|
|
61
63
|
constantRadius?: number;
|
|
62
64
|
sizeScaleState?: SizeScaleState | null;
|
|
63
65
|
displayFields?: string[] | null;
|
|
66
|
+
otherStats?: (point: Record<string, unknown>) => OtherRowStats | null;
|
|
64
67
|
enabled: boolean;
|
|
65
68
|
}
|
|
66
|
-
export declare function buildTooltipConfig({ tempTable, markType, chartContext, channelFields, columns, bandAxes, colorColumn, markColor, seriesColumn, markSeries, proximityAxis, constantRadius, sizeScaleState, displayFields }: TooltipConfigParams): TooltipConfig;
|
|
69
|
+
export declare function buildTooltipConfig({ tempTable, markType, chartContext, channelFields, columns, bandAxes, colorColumn, markColor, seriesColumn, markSeries, proximityAxis, constantRadius, sizeScaleState, displayFields, otherStats }: TooltipConfigParams): TooltipConfig;
|
|
67
70
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function buildTooltipConfig({ tempTable, markType, chartContext, channelFields, columns, bandAxes, colorColumn, markColor, seriesColumn, markSeries, proximityAxis, constantRadius, sizeScaleState, displayFields }) {
|
|
1
|
+
export function buildTooltipConfig({ tempTable, markType, chartContext, channelFields, columns, bandAxes, colorColumn, markColor, seriesColumn, markSeries, proximityAxis, constantRadius, sizeScaleState, displayFields, otherStats }) {
|
|
2
2
|
const colorScaleState = markColor.scaleState;
|
|
3
3
|
const constantColor = markColor.constantCssColor;
|
|
4
4
|
return {
|
|
@@ -33,6 +33,7 @@ export function buildTooltipConfig({ tempTable, markType, chartContext, channelF
|
|
|
33
33
|
...(constantRadius != null && { constantRadius }),
|
|
34
34
|
...(sizeScaleState && { sizeScaleState }),
|
|
35
35
|
...(displayFields && { displayFields }),
|
|
36
|
+
...(otherStats && { otherStats }),
|
|
36
37
|
enabled: true
|
|
37
38
|
};
|
|
38
39
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { activityReads, colorActivityWGSL } from './activity-reads';
|
|
3
|
+
import { UNIFORM_BYTES } from './uniform-sizes';
|
|
2
4
|
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
5
|
import { RendererHost } from './RendererHost.svelte';
|
|
4
6
|
import { getGPUContext } from '../../gpu';
|
|
@@ -25,7 +27,7 @@
|
|
|
25
27
|
FACET: 6
|
|
26
28
|
};
|
|
27
29
|
|
|
28
|
-
// Uniform struct offsets (matching WGSL MarkConstants
|
|
30
|
+
// Uniform struct offsets (matching WGSL MarkConstants; size in uniform-sizes.ts)
|
|
29
31
|
const UNIFORM_OFFSETS = {
|
|
30
32
|
POINT_COUNT: 0, // u32
|
|
31
33
|
COLOR_MODE: 4, // u32
|
|
@@ -35,7 +37,8 @@
|
|
|
35
37
|
FILL_OPACITY: 32, // f32
|
|
36
38
|
DOT_RADIUS: 36, // f32
|
|
37
39
|
SHOW_BOTTOM_DOTS: 40, // u32 (0 or 1)
|
|
38
|
-
FULL_DOMAIN_SPAN: 44 // f32
|
|
40
|
+
FULL_DOMAIN_SPAN: 44, // f32
|
|
41
|
+
GROUP_INDEX: 48 // u32
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
interface Props {
|
|
@@ -55,6 +58,8 @@
|
|
|
55
58
|
orientation?: 'vertical' | 'horizontal';
|
|
56
59
|
colorMode?: number;
|
|
57
60
|
colorScaleIndex?: number;
|
|
61
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
62
|
+
groupIndex?: number;
|
|
58
63
|
constantColor?: [number, number, number, number];
|
|
59
64
|
markId?: string;
|
|
60
65
|
}
|
|
@@ -76,6 +81,7 @@
|
|
|
76
81
|
orientation = 'vertical',
|
|
77
82
|
colorMode = 0,
|
|
78
83
|
colorScaleIndex = 0,
|
|
84
|
+
groupIndex = 0,
|
|
79
85
|
constantColor = DEFAULT_FILL_COLOR,
|
|
80
86
|
markId = ''
|
|
81
87
|
}: Props = $props();
|
|
@@ -87,6 +93,9 @@
|
|
|
87
93
|
const baseMarkType = curve === 'linear' ? 'areas' : `areas-${curve}`;
|
|
88
94
|
const effectiveMarkType = faceted ? `${baseMarkType}-faceted` : baseMarkType;
|
|
89
95
|
|
|
96
|
+
/** The selection activity this shader reads; routing reads the same table. */
|
|
97
|
+
const ACTIVITY_READS = activityReads('areas');
|
|
98
|
+
|
|
90
99
|
// --- Facet compile-time specialization ---
|
|
91
100
|
// Areas uses a storage buffer (not a vertex attribute) for facet indices because
|
|
92
101
|
// the boundary check needs random access: facetIndices[nextIdx] to detect when
|
|
@@ -141,7 +150,11 @@
|
|
|
141
150
|
fillOpacity: f32,
|
|
142
151
|
dotRadius: f32,
|
|
143
152
|
showBottomDots: u32,
|
|
144
|
-
fullDomainSpan: f32
|
|
153
|
+
fullDomainSpan: f32,
|
|
154
|
+
groupIndex: u32, // Page-wide filter group index, for activity reads
|
|
155
|
+
_pad0: u32,
|
|
156
|
+
_pad1: u32,
|
|
157
|
+
_pad2: u32
|
|
145
158
|
};
|
|
146
159
|
@group(1) @binding(5) var<uniform> markConstants: MarkConstants;
|
|
147
160
|
|
|
@@ -241,7 +254,12 @@
|
|
|
241
254
|
var output: VertexOutput;
|
|
242
255
|
output.position = pixelsToClip(pos);
|
|
243
256
|
output.colorValue = colorValues[instanceIdx];
|
|
244
|
-
output.alpha =
|
|
257
|
+
output.alpha = selectionDim(
|
|
258
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
259
|
+
color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValues[instanceIdx]',
|
|
260
|
+
group: 'markConstants.groupIndex'
|
|
261
|
+
})}
|
|
262
|
+
);
|
|
245
263
|
output.circleUV = quadCorner;
|
|
246
264
|
output.circleRadius = circleR;
|
|
247
265
|
return output;
|
|
@@ -341,7 +359,12 @@
|
|
|
341
359
|
var output: VertexOutput;
|
|
342
360
|
output.position = pixelsToClip(finalPos);
|
|
343
361
|
output.colorValue = colorValues[currIdx];
|
|
344
|
-
output.alpha = markConstants.fillOpacity
|
|
362
|
+
output.alpha = markConstants.fillOpacity * selectionDim(
|
|
363
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
364
|
+
color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValues[currIdx]',
|
|
365
|
+
group: 'markConstants.groupIndex'
|
|
366
|
+
})}
|
|
367
|
+
);
|
|
345
368
|
output.circleUV = vec2<f32>(-2.0, -2.0);
|
|
346
369
|
output.circleRadius = 0.0;
|
|
347
370
|
return output;
|
|
@@ -440,13 +463,14 @@
|
|
|
440
463
|
// re-fires once data loads.
|
|
441
464
|
if (colorMode === 1 && !colorValueData) return;
|
|
442
465
|
|
|
443
|
-
const buffer = new ArrayBuffer(
|
|
466
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.areasMarkConstants);
|
|
444
467
|
const view = new DataView(buffer);
|
|
445
468
|
const LE = true;
|
|
446
469
|
|
|
447
470
|
view.setUint32(UNIFORM_OFFSETS.POINT_COUNT, dimData ? dimData.length : 0, LE);
|
|
448
471
|
view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
|
|
449
472
|
view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
|
|
473
|
+
view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
|
|
450
474
|
view.setUint32(UNIFORM_OFFSETS.ORIENTATION, orientation === 'horizontal' ? 1 : 0, LE);
|
|
451
475
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
|
|
452
476
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
|
|
@@ -16,6 +16,8 @@ interface Props {
|
|
|
16
16
|
orientation?: 'vertical' | 'horizontal';
|
|
17
17
|
colorMode?: number;
|
|
18
18
|
colorScaleIndex?: number;
|
|
19
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
20
|
+
groupIndex?: number;
|
|
19
21
|
constantColor?: [number, number, number, number];
|
|
20
22
|
markId?: string;
|
|
21
23
|
}
|
|
@@ -57,7 +57,9 @@ export declare class BaseGPURenderer {
|
|
|
57
57
|
* the band axis's scale is whose clock these channels adopt. See
|
|
58
58
|
* MorphedChannels for the fold, the snap and the clock.
|
|
59
59
|
*/
|
|
60
|
-
writeMorphed(bandAxis: 'x' | 'y', bandIds: ArrayLike<number>, channels: readonly (readonly [number, AllowSharedBufferSource])[]
|
|
60
|
+
writeMorphed(bandAxis: 'x' | 'y', bandIds: ArrayLike<number>, channels: readonly (readonly [number, AllowSharedBufferSource])[],
|
|
61
|
+
/** Bindings that land at once rather than travel (see MorphedChannels.write). */
|
|
62
|
+
land?: readonly number[]): void;
|
|
61
63
|
/**
|
|
62
64
|
* Write data to a uniform binding.
|
|
63
65
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UNIFORM_STAND_IN_BYTES } from './uniform-sizes';
|
|
1
2
|
import { MorphedChannels } from '../../gpu/MorphedChannels';
|
|
2
3
|
/** Warned once per page, not per mark. */
|
|
3
4
|
let warnedMorphUnsupported = false;
|
|
@@ -241,7 +242,9 @@ export class BaseGPURenderer {
|
|
|
241
242
|
* the band axis's scale is whose clock these channels adopt. See
|
|
242
243
|
* MorphedChannels for the fold, the snap and the clock.
|
|
243
244
|
*/
|
|
244
|
-
writeMorphed(bandAxis, bandIds, channels
|
|
245
|
+
writeMorphed(bandAxis, bandIds, channels,
|
|
246
|
+
/** Bindings that land at once rather than travel (see MorphedChannels.write). */
|
|
247
|
+
land) {
|
|
245
248
|
if (!this.#device)
|
|
246
249
|
return;
|
|
247
250
|
if (!MorphedChannels.supported) {
|
|
@@ -282,7 +285,7 @@ export class BaseGPURenderer {
|
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
287
|
this.#morphAxis = bandAxis;
|
|
285
|
-
if (this.#morphed.write(bandIds, channels) || rebind) {
|
|
288
|
+
if (this.#morphed.write(bandIds, channels, land) || rebind) {
|
|
286
289
|
this.#scheduleBindGroupUpdate();
|
|
287
290
|
}
|
|
288
291
|
else if (!this.#bindGroupUpdateScheduled) {
|
|
@@ -385,7 +388,9 @@ export class BaseGPURenderer {
|
|
|
385
388
|
get #sharedDummyBuffer() {
|
|
386
389
|
if (!this.#dummyBuffer) {
|
|
387
390
|
this.#dummyBuffer = this.#device.createBuffer({
|
|
388
|
-
|
|
391
|
+
// Bound to any binding not written yet, uniforms included: as large as
|
|
392
|
+
// the largest uniform struct, or a draw before the first write fails.
|
|
393
|
+
size: UNIFORM_STAND_IN_BYTES,
|
|
389
394
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
390
395
|
label: `${this.#markType}-dummy`
|
|
391
396
|
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { UNIFORM_BYTES } from './uniform-sizes';
|
|
2
3
|
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
4
|
import { RendererHost } from './RendererHost.svelte';
|
|
4
5
|
import { getGPUContext } from '../../gpu';
|
|
5
6
|
import { DEFAULT_STROKE_COLOR } from '../../scales/color-utils';
|
|
6
7
|
import type { ChartContext, WebGPUCanvasContext } from '../../types';
|
|
8
|
+
import { activityReads, markActivityWGSL } from './activity-reads';
|
|
7
9
|
|
|
8
10
|
const dotRendererDefinition = {
|
|
9
11
|
x: { concept: 'position', channels: ['x'] },
|
|
@@ -29,7 +31,8 @@
|
|
|
29
31
|
COLOR_SCALE_INDEX: 4,
|
|
30
32
|
SIZE_MODE: 8,
|
|
31
33
|
SIZE_SCALE_INDEX: 12,
|
|
32
|
-
CONSTANT_COLOR: 16
|
|
34
|
+
CONSTANT_COLOR: 16,
|
|
35
|
+
GROUP_INDEX: 32
|
|
33
36
|
};
|
|
34
37
|
|
|
35
38
|
interface Props {
|
|
@@ -43,6 +46,8 @@
|
|
|
43
46
|
zIndex?: number;
|
|
44
47
|
colorMode?: number;
|
|
45
48
|
colorScaleIndex?: number;
|
|
49
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
50
|
+
groupIndex?: number;
|
|
46
51
|
constantColor?: [number, number, number, number];
|
|
47
52
|
sizeMode?: number;
|
|
48
53
|
sizeScaleIndex?: number;
|
|
@@ -72,6 +77,7 @@
|
|
|
72
77
|
zIndex = 0,
|
|
73
78
|
colorMode = 0,
|
|
74
79
|
colorScaleIndex = 0,
|
|
80
|
+
groupIndex = 0,
|
|
75
81
|
constantColor = DEFAULT_STROKE_COLOR,
|
|
76
82
|
sizeMode = 0,
|
|
77
83
|
sizeScaleIndex = 0,
|
|
@@ -112,6 +118,9 @@
|
|
|
112
118
|
return degen;
|
|
113
119
|
}`;
|
|
114
120
|
|
|
121
|
+
/** The selection activities this shader reads; routing reads the same table. */
|
|
122
|
+
const ACTIVITY_READS = activityReads('dots');
|
|
123
|
+
|
|
115
124
|
const dotsShaderCode = `
|
|
116
125
|
// MarkConstants uniform for per-mark configuration
|
|
117
126
|
struct MarkConstants {
|
|
@@ -120,6 +129,10 @@
|
|
|
120
129
|
sizeMode: u32, // 0 = raw value, 1 = scale
|
|
121
130
|
sizeScaleIndex: u32, // Index into sizeConfigs
|
|
122
131
|
constantColor: vec4<f32>, // Used when colorMode = 0
|
|
132
|
+
groupIndex: u32, // Page-wide filter group index, for activity reads
|
|
133
|
+
_pad0: u32,
|
|
134
|
+
_pad1: u32,
|
|
135
|
+
_pad2: u32,
|
|
123
136
|
};
|
|
124
137
|
@group(1) @binding(5) var<uniform> markConstants: MarkConstants;
|
|
125
138
|
|
|
@@ -175,7 +188,14 @@
|
|
|
175
188
|
output.colorValue = colorValue;
|
|
176
189
|
output.uv = quadPosition;
|
|
177
190
|
output.radius = displayRadius;
|
|
178
|
-
output.alpha = alpha
|
|
191
|
+
output.alpha = alpha * selectionDim(
|
|
192
|
+
${markActivityWGSL(ACTIVITY_READS, {
|
|
193
|
+
color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
|
|
194
|
+
x: 'x',
|
|
195
|
+
y: 'y',
|
|
196
|
+
group: 'markConstants.groupIndex'
|
|
197
|
+
})}
|
|
198
|
+
);
|
|
179
199
|
return output;
|
|
180
200
|
}
|
|
181
201
|
|
|
@@ -283,12 +303,13 @@
|
|
|
283
303
|
// which re-triggers the guard and avoids stale-buffer flashes.
|
|
284
304
|
if (colorMode === 1 && !colorValueData) return;
|
|
285
305
|
|
|
286
|
-
const buffer = new ArrayBuffer(
|
|
306
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.dotsMarkConstants);
|
|
287
307
|
const view = new DataView(buffer);
|
|
288
308
|
const LE = true;
|
|
289
309
|
|
|
290
310
|
view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
|
|
291
311
|
view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
|
|
312
|
+
view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
|
|
292
313
|
view.setUint32(UNIFORM_OFFSETS.SIZE_MODE, sizeMode, LE);
|
|
293
314
|
view.setUint32(UNIFORM_OFFSETS.SIZE_SCALE_INDEX, sizeScaleIndex, LE);
|
|
294
315
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
|
|
@@ -9,6 +9,8 @@ interface Props {
|
|
|
9
9
|
zIndex?: number;
|
|
10
10
|
colorMode?: number;
|
|
11
11
|
colorScaleIndex?: number;
|
|
12
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
13
|
+
groupIndex?: number;
|
|
12
14
|
constantColor?: [number, number, number, number];
|
|
13
15
|
sizeMode?: number;
|
|
14
16
|
sizeScaleIndex?: number;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { UNIFORM_BYTES } from './uniform-sizes';
|
|
2
3
|
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
4
|
import { RendererHost } from './RendererHost.svelte';
|
|
4
5
|
import { getGPUContext } from '../../gpu';
|
|
@@ -194,7 +195,7 @@ ${placement}
|
|
|
194
195
|
// Update uniform constants
|
|
195
196
|
$effect(() => {
|
|
196
197
|
if (renderer?.resourcesCreated) {
|
|
197
|
-
const buffer = new ArrayBuffer(
|
|
198
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.gridLineRuleConstants);
|
|
198
199
|
const view = new DataView(buffer);
|
|
199
200
|
const LE = true;
|
|
200
201
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { activityReads, colorActivityWGSL } from './activity-reads';
|
|
3
|
+
import { UNIFORM_BYTES } from './uniform-sizes';
|
|
2
4
|
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
5
|
import { RendererHost } from './RendererHost.svelte';
|
|
4
6
|
import { getGPUContext } from '../../gpu';
|
|
@@ -25,7 +27,7 @@
|
|
|
25
27
|
FACET: 5
|
|
26
28
|
};
|
|
27
29
|
|
|
28
|
-
// Uniform struct offsets (matching WGSL MarkConstants
|
|
30
|
+
// Uniform struct offsets (matching WGSL MarkConstants; size in uniform-sizes.ts)
|
|
29
31
|
const UNIFORM_OFFSETS = {
|
|
30
32
|
STROKE_WIDTH: 0, // f32
|
|
31
33
|
POINT_COUNT: 4, // u32
|
|
@@ -33,7 +35,10 @@
|
|
|
33
35
|
COLOR_SCALE_INDEX: 12, // u32
|
|
34
36
|
CONSTANT_COLOR: 16, // vec4<f32>
|
|
35
37
|
DOT_RADIUS: 32, // f32
|
|
36
|
-
GLOBAL_ALPHA: 36 // f32
|
|
38
|
+
GLOBAL_ALPHA: 36, // f32
|
|
39
|
+
GROUP_INDEX: 40, // u32
|
|
40
|
+
ACTIVITY_COLOR_MODE: 44, // u32
|
|
41
|
+
ACTIVITY_COLOR_SCALE_INDEX: 48 // u32
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
interface Props {
|
|
@@ -49,6 +54,15 @@
|
|
|
49
54
|
dotRadius?: number;
|
|
50
55
|
colorMode?: number;
|
|
51
56
|
colorScaleIndex?: number;
|
|
57
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
58
|
+
groupIndex?: number;
|
|
59
|
+
/**
|
|
60
|
+
* The colour mode and scale index selection activity is read through,
|
|
61
|
+
* from `colorValueData`; null for the line's own colour. An area's
|
|
62
|
+
* outline in a constant stroke passes its fill's, with the fill's values.
|
|
63
|
+
*/
|
|
64
|
+
activityColorMode?: number | null;
|
|
65
|
+
activityColorScaleIndex?: number | null;
|
|
52
66
|
constantColor?: [number, number, number, number];
|
|
53
67
|
m4Decimator?: M4Decimator | null;
|
|
54
68
|
dotsRung?: import('../../lod/DotsRung.svelte').DotsRung | null;
|
|
@@ -69,6 +83,9 @@
|
|
|
69
83
|
dotRadius = 0,
|
|
70
84
|
colorMode = 0,
|
|
71
85
|
colorScaleIndex = 0,
|
|
86
|
+
groupIndex = 0,
|
|
87
|
+
activityColorMode = null,
|
|
88
|
+
activityColorScaleIndex = null,
|
|
72
89
|
constantColor = DEFAULT_STROKE_COLOR,
|
|
73
90
|
m4Decimator = null,
|
|
74
91
|
dotsRung = null,
|
|
@@ -83,6 +100,9 @@
|
|
|
83
100
|
const baseMarkType = curve === 'linear' ? 'lines' : `lines-${curve}`;
|
|
84
101
|
const effectiveMarkType = faceted ? `${baseMarkType}-faceted` : baseMarkType;
|
|
85
102
|
|
|
103
|
+
/** The selection activity this shader reads; routing reads the same table. */
|
|
104
|
+
const ACTIVITY_READS = activityReads('lines');
|
|
105
|
+
|
|
86
106
|
// --- Facet compile-time specialization ---
|
|
87
107
|
// Lines uses a storage buffer (not a vertex attribute) for facet indices because
|
|
88
108
|
// the boundary check needs random access: facetIndices[nextIdx] to detect when
|
|
@@ -139,7 +159,12 @@
|
|
|
139
159
|
constantColor: vec4<f32>,
|
|
140
160
|
dotRadius: f32,
|
|
141
161
|
globalAlpha: f32,
|
|
142
|
-
|
|
162
|
+
groupIndex: u32, // Page-wide filter group index, for activity reads
|
|
163
|
+
// Which colour scale this line's selection activity is read through. Its
|
|
164
|
+
// own colour by default; an area's outline, drawn in a constant stroke,
|
|
165
|
+
// reads its fill's so it dims with the fill.
|
|
166
|
+
activityColorMode: u32,
|
|
167
|
+
activityColorScaleIndex: u32
|
|
143
168
|
};
|
|
144
169
|
@group(1) @binding(4) var<uniform> markConstants: MarkConstants;
|
|
145
170
|
|
|
@@ -196,7 +221,13 @@
|
|
|
196
221
|
var output: VertexOutput;
|
|
197
222
|
output.position = pixelsToClip(pos);
|
|
198
223
|
output.colorValue = colorValues[instanceIdx];
|
|
199
|
-
output.alpha = markConstants.globalAlpha
|
|
224
|
+
output.alpha = markConstants.globalAlpha * selectionDim(
|
|
225
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
226
|
+
color:
|
|
227
|
+
'markConstants.activityColorMode, markConstants.activityColorScaleIndex, colorValues[instanceIdx]',
|
|
228
|
+
group: 'markConstants.groupIndex'
|
|
229
|
+
})}
|
|
230
|
+
);
|
|
200
231
|
output.circleUV = quadPosition;
|
|
201
232
|
output.circleRadius = circleR;
|
|
202
233
|
return output;
|
|
@@ -291,7 +322,13 @@
|
|
|
291
322
|
var output: VertexOutput;
|
|
292
323
|
output.position = pixelsToClip(finalPos);
|
|
293
324
|
output.colorValue = colorValues[currIdx];
|
|
294
|
-
output.alpha = markConstants.globalAlpha
|
|
325
|
+
output.alpha = markConstants.globalAlpha * selectionDim(
|
|
326
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
327
|
+
color:
|
|
328
|
+
'markConstants.activityColorMode, markConstants.activityColorScaleIndex, colorValues[currIdx]',
|
|
329
|
+
group: 'markConstants.groupIndex'
|
|
330
|
+
})}
|
|
331
|
+
);
|
|
295
332
|
output.circleUV = vec2<f32>(-2.0, -2.0);
|
|
296
333
|
output.circleRadius = 0.0;
|
|
297
334
|
return output;
|
|
@@ -385,9 +422,11 @@
|
|
|
385
422
|
// Guard: skip uniform write when colorMode=1 but color data hasn't
|
|
386
423
|
// arrived yet. colorValueData is tracked reactively so the effect
|
|
387
424
|
// re-fires once data loads.
|
|
388
|
-
|
|
425
|
+
const activityMode = activityColorMode ?? colorMode;
|
|
426
|
+
const activityScale = activityColorScaleIndex ?? colorScaleIndex;
|
|
427
|
+
if ((colorMode === 1 || activityMode === 1) && !colorValueData) return;
|
|
389
428
|
|
|
390
|
-
const buffer = new ArrayBuffer(
|
|
429
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.linesMarkConstants);
|
|
391
430
|
const view = new DataView(buffer);
|
|
392
431
|
const LE = true;
|
|
393
432
|
|
|
@@ -401,6 +440,9 @@
|
|
|
401
440
|
view.setUint32(UNIFORM_OFFSETS.POINT_COUNT, pointCount, LE);
|
|
402
441
|
view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
|
|
403
442
|
view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
|
|
443
|
+
view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
|
|
444
|
+
view.setUint32(UNIFORM_OFFSETS.ACTIVITY_COLOR_MODE, activityMode, LE);
|
|
445
|
+
view.setUint32(UNIFORM_OFFSETS.ACTIVITY_COLOR_SCALE_INDEX, activityScale, LE);
|
|
404
446
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
|
|
405
447
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
|
|
406
448
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 8, constantColor[2], LE);
|
|
@@ -13,6 +13,15 @@ interface Props {
|
|
|
13
13
|
dotRadius?: number;
|
|
14
14
|
colorMode?: number;
|
|
15
15
|
colorScaleIndex?: number;
|
|
16
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
17
|
+
groupIndex?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The colour mode and scale index selection activity is read through,
|
|
20
|
+
* from `colorValueData`; null for the line's own colour. An area's
|
|
21
|
+
* outline in a constant stroke passes its fill's, with the fill's values.
|
|
22
|
+
*/
|
|
23
|
+
activityColorMode?: number | null;
|
|
24
|
+
activityColorScaleIndex?: number | null;
|
|
16
25
|
constantColor?: [number, number, number, number];
|
|
17
26
|
m4Decimator?: M4Decimator | null;
|
|
18
27
|
dotsRung?: import('../../lod/DotsRung.svelte').DotsRung | null;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { activityReads, colorActivityWGSL } from './activity-reads';
|
|
3
|
+
import { PARALLEL_MAX_AXES, UNIFORM_BYTES } from './uniform-sizes';
|
|
2
4
|
import { getContext, onMount, onDestroy } from 'svelte';
|
|
3
5
|
import { RendererHost } from './RendererHost.svelte';
|
|
4
6
|
import { getGPUContext } from '../../gpu';
|
|
@@ -7,10 +9,10 @@
|
|
|
7
9
|
import { DEFAULT_STROKE_COLOR } from '../../scales/color-utils';
|
|
8
10
|
import type { ChartContext, CurveType, WebGPUCanvasContext } from '../../types';
|
|
9
11
|
|
|
10
|
-
const MAX_AXES =
|
|
11
|
-
const UNIFORM_SIZE =
|
|
12
|
+
const MAX_AXES = PARALLEL_MAX_AXES;
|
|
13
|
+
const UNIFORM_SIZE = UNIFORM_BYTES.parallelConfig;
|
|
12
14
|
// One ScaleParam (16 bytes) per axis, filled by the coordinator.
|
|
13
|
-
const AXIS_SCALES_SIZE =
|
|
15
|
+
const AXIS_SCALES_SIZE = UNIFORM_BYTES.parallelAxisScales;
|
|
14
16
|
|
|
15
17
|
// Both x and y so ScaleTemplateSystem generates scaleX(), scaleLinearY(), pixelsToClip()
|
|
16
18
|
const rendererDefinition = {
|
|
@@ -32,6 +34,8 @@
|
|
|
32
34
|
colorValueData?: Float32Array | null;
|
|
33
35
|
colorMode?: number;
|
|
34
36
|
colorScaleIndex?: number;
|
|
37
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
38
|
+
groupIndex?: number;
|
|
35
39
|
constantColor?: [number, number, number, number];
|
|
36
40
|
axisOrder: number[];
|
|
37
41
|
/** Each axis's scale id, in data (not visual) order. */
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
colorValueData = null,
|
|
52
56
|
colorMode = 0,
|
|
53
57
|
colorScaleIndex = 0,
|
|
58
|
+
groupIndex = 0,
|
|
54
59
|
constantColor = DEFAULT_STROKE_COLOR,
|
|
55
60
|
axisOrder,
|
|
56
61
|
axisScaleIds,
|
|
@@ -87,6 +92,9 @@
|
|
|
87
92
|
// (N-1) segments × resolution sub-quads × 6 verts + N circles × 6 verts
|
|
88
93
|
const vertexCount = $derived((axisCount - 1) * 6 * resolution + axisCount * 6);
|
|
89
94
|
|
|
95
|
+
/** The selection activity this shader reads; routing reads the same table. */
|
|
96
|
+
const ACTIVITY_READS = activityReads('parallel');
|
|
97
|
+
|
|
90
98
|
// evaluateCurve() and CURVE_RESOLUTION are prepended from CurveShaderLib.
|
|
91
99
|
// With linear (resolution=1): single quad per segment, identical to original.
|
|
92
100
|
// With bump-x (resolution=128): smooth S-curves between axes, adaptive.
|
|
@@ -101,7 +109,7 @@
|
|
|
101
109
|
opacity: f32,
|
|
102
110
|
colorMode: u32,
|
|
103
111
|
colorScaleIndex: u32,
|
|
104
|
-
|
|
112
|
+
groupIndex: u32,
|
|
105
113
|
_pad1: u32,
|
|
106
114
|
constantColor: vec4<f32>,
|
|
107
115
|
axisOrder: array<vec4<u32>, 4>,
|
|
@@ -191,7 +199,12 @@
|
|
|
191
199
|
var output: VertexOutput;
|
|
192
200
|
output.position = pixelsToClip(pos);
|
|
193
201
|
output.colorValue = colorValues[rowIdx];
|
|
194
|
-
output.alpha = config.opacity
|
|
202
|
+
output.alpha = config.opacity * selectionDim(
|
|
203
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
204
|
+
color: 'config.colorMode, config.colorScaleIndex, colorValues[rowIdx]',
|
|
205
|
+
group: 'config.groupIndex'
|
|
206
|
+
})}
|
|
207
|
+
);
|
|
195
208
|
output.circleUV = quadPosition;
|
|
196
209
|
output.circleRadius = halfWidth;
|
|
197
210
|
return output;
|
|
@@ -270,7 +283,12 @@
|
|
|
270
283
|
var output: VertexOutput;
|
|
271
284
|
output.position = pixelsToClip(finalPos);
|
|
272
285
|
output.colorValue = colorValues[rowIdx];
|
|
273
|
-
output.alpha = config.opacity
|
|
286
|
+
output.alpha = config.opacity * selectionDim(
|
|
287
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
288
|
+
color: 'config.colorMode, config.colorScaleIndex, colorValues[rowIdx]',
|
|
289
|
+
group: 'config.groupIndex'
|
|
290
|
+
})}
|
|
291
|
+
);
|
|
274
292
|
output.circleUV = vec2<f32>(-2.0, -2.0);
|
|
275
293
|
output.circleRadius = 0.0;
|
|
276
294
|
return output;
|
|
@@ -361,7 +379,8 @@
|
|
|
361
379
|
f32[3] = opacity;
|
|
362
380
|
u32[4] = colorMode;
|
|
363
381
|
u32[5] = colorScaleIndex;
|
|
364
|
-
|
|
382
|
+
u32[6] = groupIndex;
|
|
383
|
+
// u32[7] = padding
|
|
365
384
|
f32[8] = constantColor[0];
|
|
366
385
|
f32[9] = constantColor[1];
|
|
367
386
|
f32[10] = constantColor[2];
|
|
@@ -6,6 +6,8 @@ interface Props {
|
|
|
6
6
|
colorValueData?: Float32Array | null;
|
|
7
7
|
colorMode?: number;
|
|
8
8
|
colorScaleIndex?: number;
|
|
9
|
+
/** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
|
|
10
|
+
groupIndex?: number;
|
|
9
11
|
constantColor?: [number, number, number, number];
|
|
10
12
|
axisOrder: number[];
|
|
11
13
|
/** Each axis's scale id, in data (not visual) order. */
|