@fjader/svelte 0.2.1 → 0.2.3
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/components/axes/BandAxisTicks.svelte +63 -24
- package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
- package/dist/components/charts/ChartContainer.svelte +2 -1
- package/dist/components/guides/legend/ContinuousColorLegend.svelte +2 -1
- package/dist/components/guides/legend/OrdinalColorLegend.svelte +92 -34
- package/dist/components/marks/AreaMarkX.svelte +19 -6
- package/dist/components/marks/AreaMarkY.svelte +19 -6
- package/dist/components/marks/BarMarkX.svelte +3 -0
- package/dist/components/marks/BarMarkY.svelte +3 -0
- package/dist/components/marks/DotsMark.svelte +3 -0
- package/dist/components/marks/LineMark.svelte +3 -0
- package/dist/components/marks/ParallelCoordinatesMark.svelte +6 -0
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +3 -0
- package/dist/components/marks/RectMark.svelte +3 -0
- package/dist/components/marks/RectXMark.svelte +3 -0
- package/dist/components/marks/RectYMark.svelte +3 -0
- package/dist/components/marks/RuleMarkX.svelte +10 -2
- package/dist/components/marks/RuleMarkY.svelte +10 -2
- package/dist/components/marks/TextMark.svelte +68 -4
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +15 -1
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +116 -27
- package/dist/components/marks/lifecycle/domain-queries.js +13 -11
- 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 +3 -0
- package/dist/components/rendering/Areas.svelte +32 -9
- package/dist/components/rendering/Areas.svelte.d.ts +2 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.js +4 -1
- 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 +51 -10
- package/dist/components/rendering/Lines.svelte.d.ts +9 -0
- package/dist/components/rendering/ParallelLines.svelte +28 -8
- package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
- package/dist/components/rendering/Rects.svelte +20 -5
- package/dist/components/rendering/Rects.svelte.d.ts +2 -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 +93 -0
- package/dist/components/rendering/activity-reads.js +135 -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 +2 -0
- package/dist/data/DataAccessor.js +1 -1
- package/dist/gpu/ColorScaleShaderLib.d.ts +14 -2
- package/dist/gpu/ColorScaleShaderLib.js +59 -7
- package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
- package/dist/gpu/GPUCoordinator.svelte.js +68 -32
- package/dist/gpu/NanShaderLib.d.ts +30 -0
- package/dist/gpu/NanShaderLib.js +42 -0
- package/dist/gpu/ResolveShader.d.ts +3 -3
- package/dist/gpu/ResolveShader.js +6 -5
- package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
- package/dist/gpu/ScaleTemplateSystem.js +86 -7
- package/dist/gpu/SizeScaleShaderLib.d.ts +5 -5
- package/dist/gpu/SizeScaleShaderLib.js +6 -5
- package/dist/gpu/decimation/M4Shader.d.ts +13 -7
- package/dist/gpu/decimation/M4Shader.js +79 -64
- 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.js +1 -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 +2 -1
- package/dist/interactors/Interval2D.svelte +2 -1
- package/dist/interactors/ParallelAxisSource.svelte +4 -4
- 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 +17 -36
- package/dist/interactors/Toggle.svelte.d.ts +4 -3
- package/dist/interactors/Tooltip.svelte +2 -1
- package/dist/interactors/TooltipManager.svelte.js +8 -3
- 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 +79 -0
- package/dist/query-engine/FieldActivity.svelte.js +207 -0
- package/dist/query-engine/FilterGroup.svelte.d.ts +53 -29
- package/dist/query-engine/FilterGroup.svelte.js +181 -122
- package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +42 -0
- package/dist/query-engine/FilterGroupRegistry.svelte.js +103 -1
- 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 +90 -28
- package/dist/query-engine/categorical-identity.d.ts +99 -27
- package/dist/query-engine/categorical-identity.js +210 -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 +9 -5
- package/dist/query-engine/preagg/plan.js +13 -11
- package/dist/query-engine/toggle-predicate.js +12 -1
- package/dist/scales/ActivityTable.d.ts +99 -0
- package/dist/scales/ActivityTable.js +211 -0
- package/dist/scales/BandScaleState.svelte.d.ts +28 -7
- package/dist/scales/BandScaleState.svelte.js +55 -20
- 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 +2 -0
- package/dist/transforms/index.d.ts +27 -0
- package/dist/transforms/index.js +97 -3
- package/dist/types.d.ts +30 -0
- package/package.json +1 -1
|
@@ -42,10 +42,11 @@ export async function fetchCategoricalDomains(coordinator, table, select, actual
|
|
|
42
42
|
//
|
|
43
43
|
// - Order. DISTINCT has no ORDER BY, so today's domain is whatever DuckDB
|
|
44
44
|
// returned, then "first-appearance order" in updateDomainFromExtents.
|
|
45
|
-
// Arbitrary. Dim
|
|
46
|
-
// deterministic
|
|
47
|
-
//
|
|
48
|
-
//
|
|
45
|
+
// Arbitrary. Dim values are listed in id order, so the domain is
|
|
46
|
+
// deterministic. It is the real categories only (`categoryValues`
|
|
47
|
+
// leaves out Other buckets), so position-in-domain equals the dim id
|
|
48
|
+
// only until a bucket's row: the scale encodes each value to its id
|
|
49
|
+
// rather than relying on position.
|
|
49
50
|
// - Multi-table. The dim spans every table registering the field, so two
|
|
50
51
|
// marks on one shared scale agree on the category set by construction.
|
|
51
52
|
//
|
|
@@ -63,10 +64,11 @@ export async function fetchCategoricalDomains(coordinator, table, select, actual
|
|
|
63
64
|
fetches.push((async () => {
|
|
64
65
|
// Idempotent and coalesced — the mark's own setup calls this too.
|
|
65
66
|
await coordinator.ensureColumns(table, { categorical: [identityField] });
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
//
|
|
67
|
+
// This table's categories: the dim spans every table on the field.
|
|
68
|
+
const categories = coordinator.categoryValues(identityField.key, table);
|
|
69
|
+
// A snapshot of the real categories. The scale adopts the live
|
|
70
|
+
// identity itself in MarkLifecycleV2's #ensureIdentityColumns, which
|
|
71
|
+
// also covers the explicit-domain axes that never reach this branch.
|
|
70
72
|
if (categories.length > 0)
|
|
71
73
|
scaleState.addMark(categories, markId);
|
|
72
74
|
})());
|
|
@@ -121,7 +123,7 @@ export async function fetchCategoricalDomains(coordinator, table, select, actual
|
|
|
121
123
|
fetches.push((async () => {
|
|
122
124
|
// Idempotent and coalesced — the mark's own setup calls this too.
|
|
123
125
|
await coordinator.ensureColumns(table, { categorical: [facetKey] });
|
|
124
|
-
const categories = coordinator.
|
|
126
|
+
const categories = coordinator.categoryValues(facetKey.key, table);
|
|
125
127
|
if (categories.length > 0)
|
|
126
128
|
facetBandScale.addMark(categories, markId);
|
|
127
129
|
})());
|
|
@@ -185,7 +187,7 @@ export async function fetchCategoricalDomains(coordinator, table, select, actual
|
|
|
185
187
|
fetches.push((async () => {
|
|
186
188
|
// Idempotent and coalesced — the mark's own setup calls this too.
|
|
187
189
|
await coordinator.ensureColumns(table, { categorical: [colorIdentity] });
|
|
188
|
-
const categories = coordinator.
|
|
190
|
+
const categories = coordinator.categoryValues(colorIdentity.key);
|
|
189
191
|
if (categories.length > 0) {
|
|
190
192
|
colorScaleState.setDomain(categories);
|
|
191
193
|
}
|
|
@@ -234,7 +236,7 @@ export async function fetchCategoricalDomains(coordinator, table, select, actual
|
|
|
234
236
|
if (seriesIdentity) {
|
|
235
237
|
fetches.push((async () => {
|
|
236
238
|
await coordinator.ensureColumns(table, { categorical: [seriesIdentity] });
|
|
237
|
-
const categories = coordinator.
|
|
239
|
+
const categories = coordinator.categoryValues(seriesIdentity.key);
|
|
238
240
|
if (categories.length > 0) {
|
|
239
241
|
seriesScaleState.setDomain(categories);
|
|
240
242
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `transformKey` a mark with a category-reducing transform (topN) adds to
|
|
3
|
+
* its band scale's config, which is part of the scale's registry key.
|
|
4
|
+
*
|
|
5
|
+
* It names the table, the transform (N included) and the chart. A topN axis
|
|
6
|
+
* shows the categories its own query returned, and a band scale resolves the
|
|
7
|
+
* union of every mark registered on it, so two charts may share one only if
|
|
8
|
+
* they always return the same categories. Two charts with the identical topN
|
|
9
|
+
* do not: they receive different filters (a brush skips its own chart), and a
|
|
10
|
+
* shared scale would show each the other's top N as empty slots, and share one
|
|
11
|
+
* Other bucket between two different sets of members.
|
|
12
|
+
*
|
|
13
|
+
* The chart is its ChartConfig object, which a faceted chart's cells share, so
|
|
14
|
+
* facets keep one axis; a bar and its value labels in one chart share one too.
|
|
15
|
+
*/
|
|
16
|
+
export declare function transformScaleKey(table: string, transformKey: string, chart: object | null | undefined): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `transformKey` a mark with a category-reducing transform (topN) adds to
|
|
3
|
+
* its band scale's config, which is part of the scale's registry key.
|
|
4
|
+
*
|
|
5
|
+
* It names the table, the transform (N included) and the chart. A topN axis
|
|
6
|
+
* shows the categories its own query returned, and a band scale resolves the
|
|
7
|
+
* union of every mark registered on it, so two charts may share one only if
|
|
8
|
+
* they always return the same categories. Two charts with the identical topN
|
|
9
|
+
* do not: they receive different filters (a brush skips its own chart), and a
|
|
10
|
+
* shared scale would show each the other's top N as empty slots, and share one
|
|
11
|
+
* Other bucket between two different sets of members.
|
|
12
|
+
*
|
|
13
|
+
* The chart is its ChartConfig object, which a faceted chart's cells share, so
|
|
14
|
+
* facets keep one axis; a bar and its value labels in one chart share one too.
|
|
15
|
+
*/
|
|
16
|
+
const chartKeys = new WeakMap();
|
|
17
|
+
let nextChartKey = 0;
|
|
18
|
+
/** A name for a chart, stable while its ChartConfig lives, shared by its cells. */
|
|
19
|
+
function chartKeyOf(chart) {
|
|
20
|
+
let key = chartKeys.get(chart);
|
|
21
|
+
if (!key)
|
|
22
|
+
chartKeys.set(chart, (key = `chart${nextChartKey++}`));
|
|
23
|
+
return key;
|
|
24
|
+
}
|
|
25
|
+
export function transformScaleKey(table, transformKey, chart) {
|
|
26
|
+
return chart ? `${table}:${transformKey}@${chartKeyOf(chart)}` : `${table}:${transformKey}`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How an area's outline is coloured, and which colour its selection activity
|
|
3
|
+
* is read through.
|
|
4
|
+
*
|
|
5
|
+
* The outline is drawn in the fill's colour, or in a constant `stroke`. Either
|
|
6
|
+
* way its activity is the fill's: the area dims as one shape when a selection
|
|
7
|
+
* on its colour field dims the fill, outline included. Before activity was per
|
|
8
|
+
* field the unselected areas were filtered away, outlines with them; drawn in a
|
|
9
|
+
* constant colour, an outline reading its own colour would read no activity and
|
|
10
|
+
* stay opaque over its dimmed fill.
|
|
11
|
+
*/
|
|
12
|
+
export interface OutlineColor {
|
|
13
|
+
/** The values the outline's colour buffer holds: the fill's, always. */
|
|
14
|
+
colorValueData: Float32Array | null;
|
|
15
|
+
colorMode: number;
|
|
16
|
+
colorScaleIndex: number;
|
|
17
|
+
activityColorMode: number;
|
|
18
|
+
activityColorScaleIndex: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function outlineColor(stroke: unknown, fill: {
|
|
21
|
+
data: Float32Array | null;
|
|
22
|
+
colorMode: number;
|
|
23
|
+
colorScaleIndex: number;
|
|
24
|
+
}): OutlineColor;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function outlineColor(stroke, fill) {
|
|
2
|
+
const constant = !!stroke;
|
|
3
|
+
return {
|
|
4
|
+
colorValueData: fill.data,
|
|
5
|
+
colorMode: constant ? 0 : fill.colorMode,
|
|
6
|
+
colorScaleIndex: constant ? 0 : fill.colorScaleIndex,
|
|
7
|
+
activityColorMode: fill.colorMode,
|
|
8
|
+
activityColorScaleIndex: fill.colorScaleIndex
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -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'>>, opts?: {
|
|
16
16
|
resolveFitDomain?: () => 'x' | 'y' | boolean | undefined;
|
|
17
17
|
}): ReactiveConfig;
|
|
@@ -1,8 +1,11 @@
|
|
|
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';
|
|
5
7
|
import { getCurveWGSL, getCurveResolution } from '../../gpu/CurveShaderLib';
|
|
8
|
+
import { NAN_WGSL } from '../../gpu/NanShaderLib';
|
|
6
9
|
import { DEFAULT_FILL_COLOR } from '../../scales/color-utils';
|
|
7
10
|
import type { ChartContext, WebGPUCanvasContext, CurveType } from '../../types';
|
|
8
11
|
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
FACET: 6
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
// Uniform struct offsets (matching WGSL MarkConstants
|
|
30
|
+
// Uniform struct offsets (matching WGSL MarkConstants; size in uniform-sizes.ts)
|
|
28
31
|
const UNIFORM_OFFSETS = {
|
|
29
32
|
POINT_COUNT: 0, // u32
|
|
30
33
|
COLOR_MODE: 4, // u32
|
|
@@ -34,7 +37,8 @@
|
|
|
34
37
|
FILL_OPACITY: 32, // f32
|
|
35
38
|
DOT_RADIUS: 36, // f32
|
|
36
39
|
SHOW_BOTTOM_DOTS: 40, // u32 (0 or 1)
|
|
37
|
-
FULL_DOMAIN_SPAN: 44 // f32
|
|
40
|
+
FULL_DOMAIN_SPAN: 44, // f32
|
|
41
|
+
GROUP_INDEX: 48 // u32
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
interface Props {
|
|
@@ -54,6 +58,8 @@
|
|
|
54
58
|
orientation?: 'vertical' | 'horizontal';
|
|
55
59
|
colorMode?: number;
|
|
56
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;
|
|
57
63
|
constantColor?: [number, number, number, number];
|
|
58
64
|
markId?: string;
|
|
59
65
|
}
|
|
@@ -75,6 +81,7 @@
|
|
|
75
81
|
orientation = 'vertical',
|
|
76
82
|
colorMode = 0,
|
|
77
83
|
colorScaleIndex = 0,
|
|
84
|
+
groupIndex = 0,
|
|
78
85
|
constantColor = DEFAULT_FILL_COLOR,
|
|
79
86
|
markId = ''
|
|
80
87
|
}: Props = $props();
|
|
@@ -86,6 +93,9 @@
|
|
|
86
93
|
const baseMarkType = curve === 'linear' ? 'areas' : `areas-${curve}`;
|
|
87
94
|
const effectiveMarkType = faceted ? `${baseMarkType}-faceted` : baseMarkType;
|
|
88
95
|
|
|
96
|
+
/** The selection activity this shader reads; routing reads the same table. */
|
|
97
|
+
const ACTIVITY_READS = activityReads('areas');
|
|
98
|
+
|
|
89
99
|
// --- Facet compile-time specialization ---
|
|
90
100
|
// Areas uses a storage buffer (not a vertex attribute) for facet indices because
|
|
91
101
|
// the boundary check needs random access: facetIndices[nextIdx] to detect when
|
|
@@ -140,7 +150,11 @@
|
|
|
140
150
|
fillOpacity: f32,
|
|
141
151
|
dotRadius: f32,
|
|
142
152
|
showBottomDots: u32,
|
|
143
|
-
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
|
|
144
158
|
};
|
|
145
159
|
@group(1) @binding(5) var<uniform> markConstants: MarkConstants;
|
|
146
160
|
|
|
@@ -159,9 +173,7 @@
|
|
|
159
173
|
@group(1) @binding(4) var<storage, read> colorValues: array<f32>;
|
|
160
174
|
{{FACET_BINDING}}
|
|
161
175
|
|
|
162
|
-
|
|
163
|
-
return v != v;
|
|
164
|
-
}
|
|
176
|
+
${NAN_WGSL}
|
|
165
177
|
|
|
166
178
|
// Map (dimension, boundary) to (x, y) based on orientation
|
|
167
179
|
fn makePoint(dimVal: f32, boundaryVal: f32) -> vec2<f32> {
|
|
@@ -242,7 +254,12 @@
|
|
|
242
254
|
var output: VertexOutput;
|
|
243
255
|
output.position = pixelsToClip(pos);
|
|
244
256
|
output.colorValue = colorValues[instanceIdx];
|
|
245
|
-
output.alpha =
|
|
257
|
+
output.alpha = selectionDim(
|
|
258
|
+
${colorActivityWGSL(ACTIVITY_READS, {
|
|
259
|
+
color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValues[instanceIdx]',
|
|
260
|
+
group: 'markConstants.groupIndex'
|
|
261
|
+
})}
|
|
262
|
+
);
|
|
246
263
|
output.circleUV = quadCorner;
|
|
247
264
|
output.circleRadius = circleR;
|
|
248
265
|
return output;
|
|
@@ -342,7 +359,12 @@
|
|
|
342
359
|
var output: VertexOutput;
|
|
343
360
|
output.position = pixelsToClip(finalPos);
|
|
344
361
|
output.colorValue = colorValues[currIdx];
|
|
345
|
-
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
|
+
);
|
|
346
368
|
output.circleUV = vec2<f32>(-2.0, -2.0);
|
|
347
369
|
output.circleRadius = 0.0;
|
|
348
370
|
return output;
|
|
@@ -441,13 +463,14 @@
|
|
|
441
463
|
// re-fires once data loads.
|
|
442
464
|
if (colorMode === 1 && !colorValueData) return;
|
|
443
465
|
|
|
444
|
-
const buffer = new ArrayBuffer(
|
|
466
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.areasMarkConstants);
|
|
445
467
|
const view = new DataView(buffer);
|
|
446
468
|
const LE = true;
|
|
447
469
|
|
|
448
470
|
view.setUint32(UNIFORM_OFFSETS.POINT_COUNT, dimData ? dimData.length : 0, LE);
|
|
449
471
|
view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
|
|
450
472
|
view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
|
|
473
|
+
view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
|
|
451
474
|
view.setUint32(UNIFORM_OFFSETS.ORIENTATION, orientation === 'horizontal' ? 1 : 0, LE);
|
|
452
475
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
|
|
453
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
|
}
|
|
@@ -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;
|
|
@@ -385,7 +386,9 @@ export class BaseGPURenderer {
|
|
|
385
386
|
get #sharedDummyBuffer() {
|
|
386
387
|
if (!this.#dummyBuffer) {
|
|
387
388
|
this.#dummyBuffer = this.#device.createBuffer({
|
|
388
|
-
|
|
389
|
+
// Bound to any binding not written yet, uniforms included: as large as
|
|
390
|
+
// the largest uniform struct, or a draw before the first write fails.
|
|
391
|
+
size: UNIFORM_STAND_IN_BYTES,
|
|
389
392
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
390
393
|
label: `${this.#markType}-dummy`
|
|
391
394
|
});
|
|
@@ -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,8 +1,11 @@
|
|
|
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';
|
|
5
7
|
import { getCurveWGSL, getCurveResolution } from '../../gpu/CurveShaderLib';
|
|
8
|
+
import { NAN_WGSL } from '../../gpu/NanShaderLib';
|
|
6
9
|
import { DEFAULT_STROKE_COLOR } from '../../scales/color-utils';
|
|
7
10
|
import type { ChartContext, WebGPUCanvasContext, CurveType } from '../../types';
|
|
8
11
|
import type { M4Decimator } from '../../gpu/decimation/M4Decimator.svelte';
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
FACET: 5
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
// Uniform struct offsets (matching WGSL MarkConstants
|
|
30
|
+
// Uniform struct offsets (matching WGSL MarkConstants; size in uniform-sizes.ts)
|
|
28
31
|
const UNIFORM_OFFSETS = {
|
|
29
32
|
STROKE_WIDTH: 0, // f32
|
|
30
33
|
POINT_COUNT: 4, // u32
|
|
@@ -32,7 +35,10 @@
|
|
|
32
35
|
COLOR_SCALE_INDEX: 12, // u32
|
|
33
36
|
CONSTANT_COLOR: 16, // vec4<f32>
|
|
34
37
|
DOT_RADIUS: 32, // f32
|
|
35
|
-
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
|
|
36
42
|
};
|
|
37
43
|
|
|
38
44
|
interface Props {
|
|
@@ -48,6 +54,15 @@
|
|
|
48
54
|
dotRadius?: number;
|
|
49
55
|
colorMode?: number;
|
|
50
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;
|
|
51
66
|
constantColor?: [number, number, number, number];
|
|
52
67
|
m4Decimator?: M4Decimator | null;
|
|
53
68
|
dotsRung?: import('../../lod/DotsRung.svelte').DotsRung | null;
|
|
@@ -68,6 +83,9 @@
|
|
|
68
83
|
dotRadius = 0,
|
|
69
84
|
colorMode = 0,
|
|
70
85
|
colorScaleIndex = 0,
|
|
86
|
+
groupIndex = 0,
|
|
87
|
+
activityColorMode = null,
|
|
88
|
+
activityColorScaleIndex = null,
|
|
71
89
|
constantColor = DEFAULT_STROKE_COLOR,
|
|
72
90
|
m4Decimator = null,
|
|
73
91
|
dotsRung = null,
|
|
@@ -82,6 +100,9 @@
|
|
|
82
100
|
const baseMarkType = curve === 'linear' ? 'lines' : `lines-${curve}`;
|
|
83
101
|
const effectiveMarkType = faceted ? `${baseMarkType}-faceted` : baseMarkType;
|
|
84
102
|
|
|
103
|
+
/** The selection activity this shader reads; routing reads the same table. */
|
|
104
|
+
const ACTIVITY_READS = activityReads('lines');
|
|
105
|
+
|
|
85
106
|
// --- Facet compile-time specialization ---
|
|
86
107
|
// Lines uses a storage buffer (not a vertex attribute) for facet indices because
|
|
87
108
|
// the boundary check needs random access: facetIndices[nextIdx] to detect when
|
|
@@ -138,7 +159,12 @@
|
|
|
138
159
|
constantColor: vec4<f32>,
|
|
139
160
|
dotRadius: f32,
|
|
140
161
|
globalAlpha: f32,
|
|
141
|
-
|
|
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
|
|
142
168
|
};
|
|
143
169
|
@group(1) @binding(4) var<uniform> markConstants: MarkConstants;
|
|
144
170
|
|
|
@@ -156,9 +182,7 @@
|
|
|
156
182
|
@group(1) @binding(3) var<storage, read> colorValues: array<f32>;
|
|
157
183
|
{{FACET_BINDING}}
|
|
158
184
|
|
|
159
|
-
|
|
160
|
-
return v != v;
|
|
161
|
-
}
|
|
185
|
+
${NAN_WGSL}
|
|
162
186
|
|
|
163
187
|
const DISCARD_POS = vec4<f32>(2.0, 2.0, 2.0, 1.0);
|
|
164
188
|
|
|
@@ -197,7 +221,13 @@
|
|
|
197
221
|
var output: VertexOutput;
|
|
198
222
|
output.position = pixelsToClip(pos);
|
|
199
223
|
output.colorValue = colorValues[instanceIdx];
|
|
200
|
-
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
|
+
);
|
|
201
231
|
output.circleUV = quadPosition;
|
|
202
232
|
output.circleRadius = circleR;
|
|
203
233
|
return output;
|
|
@@ -292,7 +322,13 @@
|
|
|
292
322
|
var output: VertexOutput;
|
|
293
323
|
output.position = pixelsToClip(finalPos);
|
|
294
324
|
output.colorValue = colorValues[currIdx];
|
|
295
|
-
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
|
+
);
|
|
296
332
|
output.circleUV = vec2<f32>(-2.0, -2.0);
|
|
297
333
|
output.circleRadius = 0.0;
|
|
298
334
|
return output;
|
|
@@ -386,9 +422,11 @@
|
|
|
386
422
|
// Guard: skip uniform write when colorMode=1 but color data hasn't
|
|
387
423
|
// arrived yet. colorValueData is tracked reactively so the effect
|
|
388
424
|
// re-fires once data loads.
|
|
389
|
-
|
|
425
|
+
const activityMode = activityColorMode ?? colorMode;
|
|
426
|
+
const activityScale = activityColorScaleIndex ?? colorScaleIndex;
|
|
427
|
+
if ((colorMode === 1 || activityMode === 1) && !colorValueData) return;
|
|
390
428
|
|
|
391
|
-
const buffer = new ArrayBuffer(
|
|
429
|
+
const buffer = new ArrayBuffer(UNIFORM_BYTES.linesMarkConstants);
|
|
392
430
|
const view = new DataView(buffer);
|
|
393
431
|
const LE = true;
|
|
394
432
|
|
|
@@ -402,6 +440,9 @@
|
|
|
402
440
|
view.setUint32(UNIFORM_OFFSETS.POINT_COUNT, pointCount, LE);
|
|
403
441
|
view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
|
|
404
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);
|
|
405
446
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
|
|
406
447
|
view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
|
|
407
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;
|